GIMP Python-Fuで不透明部分を選択してマスクを作成する

#1.はじめに

不透明部分からのマスクの作成を行いました。
GIMP 2.10で動作確認をしています。

#2.実行結果

透明で塗りつぶしたレイヤーに緑円と青円を描画し、不透明部分(緑円＋青円)からマスクを作成し、赤円を描画しています。その結果、赤円は緑円と青円と重なる部分のみ描画されます。

![20191212.PNG](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/534247/b1f7d687-e4b4-3f38-9fc7-d61818a44898.png)

#3.メイン部分説明

gimp_image_select_item()にADD_SELECTION_MASKを指定して不透明部分を選択しています。
gimp_layer_create_mask()とgimp_layer_set_edit_mask()でマスクの作成とレイヤーへの追加を行っています。
gimp_layer_set_edit_mask()にFALSEを指定して、マスクでなく通常部分を編集するようにしています。

```Python
    # 緑円
    set_color(0, 255, 0, 1.0)
    make_path_relative2(image, 320, 300, points, 3, TRUE)
    # 青円
    set_color(0, 0, 255, 1.0)
    make_path_relative2(image, 480, 300, points, 3, TRUE)
    #
    # 不透明部分(緑円＋青円)を選択する
    pdb.gimp_image_select_item(image, 0, transparent_layer)
    # 選択範囲からマスクを作成する
    mask = pdb.gimp_layer_create_mask(transparent_layer, ADD_SELECTION_MASK)
    # マスクをレイヤーに追加する
    pdb.gimp_layer_add_mask(transparent_layer, mask)
    # マスク編集を無効にする(マスクでない部分を編集する)
    pdb.gimp_layer_set_edit_mask(transparent_layer, FALSE)
    set_color(255, 0, 0, 1.0)
    # 赤円
    make_path_relative2(image, 400, 230, points, 3, TRUE)
```


#4.スクリプト全体

```Python
# Python-Fu のサンプル・スクリプト
# GIMP の Python-Fu コンソールにコピペして実行してください

# 画像データの作成
## 指定したサイズで画像データを作成する
### width : 画像データの幅 (px)
### height : 画像データの高さ (px)
def create_image(width, height):
  # 画像データを生成
  return gimp.Image(width, height, RGB)

# レイヤーの追加
## 指定した名前のレイヤーを新規に作成し、画像データに挿入する
### image : レイヤーを追加する画像データ
### name : 新規に作成するレイヤーの名前（文字列）
def add_layer(image, name):
  # レイヤーの作成に必要なパラメータ
  width   = image.width
  height  = image.height
  type    = RGB_IMAGE
  opacity = 100
  mode    = NORMAL_MODE
  #
  # パラメータをもとにレイヤーを作成
  layer = gimp.Layer(image, name, width, height, type, opacity, mode)
  #
  # レイヤーを背景色で塗りつぶす（GIMP のデフォルトの挙動に合わせています）
  layer.fill(1)
  #
  # 画像データの 0 番目の位置にレイヤーを挿入する
  position = 0
  image.add_layer(layer, position)
  #
  return layer

def add_layer_transparent(image, name):
  # レイヤーの作成に必要なパラメータ
  width   = image.width
  height  = image.height
  type    = RGBA_IMAGE
  opacity = 100
  mode    = NORMAL_MODE
  #
  # パラメータをもとにレイヤーを作成
  layer = gimp.Layer(image, name, width, height, type, opacity, mode)
  #
  # レイヤーを透明色で塗りつぶす
  layer.fill(TRANSPARENT_FILL)
  #
  # 画像データの 0 番目の位置にレイヤーを挿入する
  position = 0
  image.add_layer(layer, position)
  #
  return layer

# 描画する色を変更する
## パレットの前景色を変更して描画色を設定する
### r : 赤要素 (0-255)
### g : 緑要素 (0-255)
### b : 青要素 (0-255)
### a : 透明度 (0-1.0)
def set_color(r, g, b, a):
  color = (r, g, b, a)
  pdb.gimp_context_set_foreground(color)

def set_bg_color(r, g, b, a):
  color = (r, g, b, a)
  pdb.gimp_context_set_background(color)

# 描画する線の太さを変える
## ブラシのサイズを変更して線の太さを設定する
### width : 線の太さ
def set_line_width(width):
  pdb.gimp_context_set_brush_size(width)

# アンチエイリアスを設定する
## アンチエイリアスを設定する
### antialias : TRUE:有効、FALSE:無効
def set_antialias(antialias):
  pdb.gimp_context_set_antialias(antialias)

# 画像の表示
## 新しいウィンドウを作成し、画像データを表示する
### image : 表示する画像データ
def display_image(image):
  gimp.Display(image)

# パス作成
## パスを作成
### image : レイヤーを追加する画像データ
### points : パス座標
### mode : 0:描画しない、1:閉じないストローク(初期値)、2:閉じるストローク、3:塗りつぶし
### delete_path : TRUE:パスを削除する(初期値)、FALSE:パスを残す
def make_path(
    image,
    points,
    mode=1,
    delete_path=TRUE,
    ):
    """ パスを作成する。 """
    #
    # フラグ設定
    #
    is_draw   = FALSE
    is_closed = FALSE
    is_fill   = FALSE
    is_stroke = FALSE
    if mode == 1:           # 閉じないストローク
        is_draw   = TRUE
        is_closed = FALSE
        is_fill   = FALSE
        is_stroke = FALSE
    elif mode == 2:         # 閉じるストローク
        is_draw   = TRUE
        is_closed = TRUE
        is_fill   = FALSE
        is_stroke = FALSE
    elif mode == 3:         # 塗りつぶし
        is_draw   = TRUE
        is_closed = TRUE        # TRUE/FALSEどちらでも変わらない
        is_fill   = TRUE
        is_stroke = FALSE
    elif mode == 4:         # 閉じないストローク(アンチエイリアスが効かない)
        is_draw   = TRUE
        is_closed = FALSE       # 効かない
        is_fill   = FALSE
        is_stroke = TRUE
    elif mode == 5:         # 閉じるストローク(アンチエイリアスが効かない)
        is_draw   = TRUE
        is_closed = TRUE
        is_fill   = FALSE
        is_stroke = TRUE
    #
    vectors = pdb.gimp_vectors_new(image, 'path')  # パス新規作成
    pdb.gimp_image_add_vectors(image, vectors, 0)  # パスを画像に追加
    #
    # パスとしてストロークを追加
    #
    stroke_id = pdb.gimp_vectors_stroke_new_from_points(vectors, 0,
            len(points), points, is_closed)
    #
    # パスを表示
    #
    pdb.gimp_vectors_set_visible(vectors, TRUE)
    #
    # 描画するよう指定されてたら、パスから選択範囲を作って描画する。
    #
    if is_draw:
        #
        # 選択範囲解除
        #
        pdb.gimp_selection_none(image)
        #
        # パスから選択範囲作成
        #
        pdb.gimp_vectors_to_selection(
            vectors,
            CHANNEL_OP_REPLACE,
            TRUE,
            FALSE,
            0,
            0,
            )
        #
        # 前景色で描画
        #
        drawable = pdb.gimp_image_active_drawable(image)
        if is_fill:
            pdb.gimp_edit_fill(drawable, FOREGROUND_FILL)
        elif is_stroke:
            pdb.gimp_edit_stroke(drawable)
        else:
            pdb.gimp_drawable_edit_stroke_item(drawable, vectors)
        #
        # 選択範囲解除
        #
        pdb.gimp_selection_none(image)
    #
    # パス削除が指定されていたらパスを削除する。
    #
    if delete_path:
        # パス削除
        #
        pdb.gimp_image_remove_vectors(image, vectors)
    #
    return

def get_absolute_point(list):
    """ """ 
    rlist = []
    for pos in list:
        if len(pos) == 6:
            (x, y, rx1, ry1, rx2, ry2) = pos
            x1 = x + rx1
            y1 = y + ry1
            x2 = x + rx2
            y2 = y + ry2
        else:
            (x, y) = pos
            x1 = x
            y1 = y
            x2 = x
            y2 = y
        rlist.extend([
            x1,
            y1,
            x,
            y,
            x2,
            y2,
            ])
    return rlist

# 絶対座標取得
def get_absolute_point2(x, y, list):
    """ """
    rlist = []
    bx = x
    by = y
    for pos in list:
        if len(pos) == 6:
            (rx0, ry0, rx1, ry1, rx2, ry2) = pos
            bx = bx + rx0
            by = by + ry0
            x1 = bx + rx1
            y1 = by + ry1
            x2 = bx + rx2
            y2 = by + ry2
        else:
            (rx0, ry0) = pos
            bx = bx + rx0
            by = by + ry0
            x1 = bx
            y1 = by
            x2 = bx
            y2 = by
        rlist.extend([
            x1,
            y1,
            bx,
            by,
            x2,
            y2,
            ])
    return rlist

# パス作成(相対座標指定)
## パスを作成
### image : レイヤーを追加する画像データ
### points : パス座標
### mode : 0:描画しない、1:最後を閉じないストローク(初期値)、2:最後を閉じるストローク、3:塗りつぶし
### delete_path : TRUE:パスを削除する(初期値)、FALSE:パスを残す
def make_path_relative(
    image,
    points,
    mode=1,
    delete_path=TRUE,
    ):
    """ パスを作成する。 """
    new_points = get_absolute_point(points)
    make_path(image, new_points, mode, delete_path)

# パス作成(座標・アンカー・制御点相対座標指定)
## パスを作成
### image : レイヤーを追加する画像データ
### x : 開始X座標
### y : 開始Y座標
### points : パス座標
### mode : 0:描画しない、1:最後を閉じないストローク(初期値)、2:最後を閉じるストローク、3:塗りつぶし
### delete_path : TRUE:パスを削除する(初期値)、FALSE:パスを残す
def make_path_relative2(
    image,
    x,
    y,
    points,
    mode=1,
    delete_path=TRUE,
    ):
    """ パスを作成する。 """
    new_points = get_absolute_point2(x, y, points)
    make_path(image, new_points, mode, delete_path)

# メイン
def main():
    set_bg_color(255, 255, 255, 1.0)
    image = create_image(800, 600)
    background_layer = add_layer(image, "背景")
    transparent_layer = add_layer_transparent(image, "透明")
    set_line_width(10)
    set_antialias(TRUE)
    # 値は(アンカーX, Y[, 制御開始点増分X, Y, 制御終了点増分X, Y])
    #
    # ほぼ円
    points = [
        (+100,    0,   0, -50,   0, +50),
        (-100, +100, +50,   0, -50,   0),
        (-100, -100,   0, +50,   0, -50),
        (+100, -100, -50,   0, +50,   0),
        ]
    # 緑円
    set_color(0, 255, 0, 1.0)
    make_path_relative2(image, 320, 300, points, 3, TRUE)
    # 青円
    set_color(0, 0, 255, 1.0)
    make_path_relative2(image, 480, 300, points, 3, TRUE)
    #
    # 不透明部分(緑円＋青円)を選択する
    pdb.gimp_image_select_item(image, 0, transparent_layer)
    # 選択範囲からマスクを作成する
    mask = pdb.gimp_layer_create_mask(transparent_layer, ADD_SELECTION_MASK)
    # マスクをレイヤーに追加する
    pdb.gimp_layer_add_mask(transparent_layer, mask)
    # マスク編集を無効にする(マスクでない部分を編集する)
    pdb.gimp_layer_set_edit_mask(transparent_layer, FALSE)
    set_color(255, 0, 0, 1.0)
    # 赤円
    make_path_relative2(image, 400, 230, points, 3, TRUE)
    #
    display_image(image)

main()
```

#5.参考

[python-fu 自動操作おさらい - Qiita](https://qiita.com/otoshita/items/88f1ac857fd86346db94)
[Gimp Automating Image Processing with Python Fu](https://www.vastorigins.com/2019/08/gimp-automating-image-processing-with.html)

#6.その他

マスクの削除も行いました。
→[GIMP Python-Fuでマスクを削除する - Qiita](https://qiita.com/mayo00/items/3c90d2b38a15a5b0dc7c)