GIMP3のPython-Fuでピクセル単位のアクセスで高速塗りつぶし

コンピュータ

GIMPでピクセル単位でアクセスする方法としてget_pixel / set_pixelがありますが、全ピクセルにアクセスすると動作が非常に遅いことに気が付きます。
全体に対するフィルター処理を行うには不向きなので、高速に動作する方法を探してみました。

サンプルコード

# 1.先頭の画像を取得
image = Gimp.get_images()[0]
# 2.選択レイヤー      
layer = image.get_selected_layers()[0]

# 3.レイヤーのピクセルデータをバッファとして取得
buf = layer.get_buffer()
# 幅、高さ取得
w, h = layer.get_width(), layer.get_height()
# GEGLの矩形指定でレイヤー全領域を対象に。
rect = Gegl.Rectangle.new(0, 0, w, h)

# 4.RGBA 前提で bytearray にコピー
bpp = 4         # ピクセルのバイト数
scale = 1.0     # スケーリング 1.0 等倍
fmt = "RGBA u8" # 出力フォーマット
abyss_policy = Gegl.AbyssPolicy.NONE # 範囲外の扱い:NONE
# byte配列(読み取り専用)を取得
pixel_bytes = buf.get(rect, scale, fmt, abyss_policy)
# 書き換え可能なコピー
raw = bytearray(pixel_bytes) 

# 5.ピクセルを一括書き換え(赤で塗りつぶし)
for i in range(0, len(raw), bpp):
    raw[i]   = 255  # R
    raw[i+1] = 0    # G
    raw[i+2] = 0    # B
    raw[i+3] = 255  # A

# 6.書き換えたデータをバッファへ戻し
buf.set(rect, fmt, bytes(raw))
# レイヤーを更新
layer.update(rect.x, rect.y, rect.width, rect.height)

機能

アルファチャンネルありのカラー画像を表示した状態で、Python-Fuのコンソールで以下のコードを貼り付けると、画像が赤で塗りつぶしされます。

概要

get_pixel / set_pixelでは1ピクセル単位でアクセスしますが、こちらのコードでは比較的高速にアクセスすることができるPythonの配列にピクセルを変換し、一括処理することで、短時間で塗りつぶし処理が実現します。

  • 手順1.2.→「画像とレイヤーの取得」

    レイヤーオブジェクトを取得しています。
    フィルターを作るためには、ほぼ必須のオブジェクトになります。
    Python-Fu向けの取得方法で、メニューから呼び出すプラグインの場合は異なります。(必要ない)

  • 手順3.4 → 「ピクセルデータをPython配列にコピー」

    buf.get(rect, scale, fmt, abyss_policy)はバッファからピクセルを取り出す関数で、RGBAの生データを Python 側にコピーします。
    レイヤーオブジェクトからピクセルをbyte配列に変換することで、比較的高速にアクセスすることができるようになります。

  • 手順5 → 「配列を編集」

    byte配列に変換されたピクセルを一括書き換えを行います。

  • 手順6 → 「バッファに戻して反映」

    手順5.で書き換えたbyte配列をレイヤーオブジェクトへ戻します。

最後に

この手法を抑えておくと、畳み込み演算などのフィルターの定番アルゴリズムを実装することができるようになります。
オリジナルフィルター作成が捗ると思います。

コメント