Windows版GIMP3でPython-Fuを試す2。

コンピュータ

レイヤーの幅と高さを取得

w, h = layer.get_width(), layer.get_height()

Colorオブジェクトを新規作成

c = Gegl.Color.new("#00FF00") # オブジェクトを緑(00FF00)で作成
c.get_rgba()                  # RGBAを取得
# (red=0.0, green=1.0, blue=0.0, alpha=1.0)

指定座標のピクセルの色情報を読み込み

x, y = 10, 20             # 取得する座標
c = layer.get_pixel(x, y) # ピクセルの色情報を取得
c.get_rgba()              # Colorオブジェクトからrgbaを取得
# (red=1.0, green=0.0, blue=0.0, alpha=1.0)

指定座標のピクセルに色情報を書き込み

c = Gegl.Color.new("0000FF") # 青
layer.set_pixel(0, 0, c) # x.0 y.0に色をセット
# True
layer.update(0, 0, 1, 1) # 更新
# True

image
(なんか青じゃ無いような?)

レイヤーの塗りつぶし

from gi.repository import Gimp, Gegl, Babl
import time

# 画像と“先頭の選択レイヤー”を取得
img = Gimp.get_images()[0]
sel = img.get_selected_layers()
layer = sel[0] if sel else None
if not layer:
    raise RuntimeError("レイヤー未選択")

# RGB系以外だと見た目が想定と異なる可能性あり(GRAY/INDEXEDは内部で変換されます)
w, h = layer.get_width(), layer.get_height()

# ループ開始 
t0 = time.perf_counter()

# 全ピクセル走査:R を 1.0(=255)に、他は維持
for y in range(h):
    for x in range(w):
        col = layer.get_pixel(x, y) # -> Gegl.Color
        r, g, b, a = col.get_rgba()   # 0.0〜1.0 の sRGB
        col.set_rgba(1.0, g, b, a)  # R=最大、G/B/Aはそのまま
        _ = layer.set_pixel(x, y, col)

# ループ終了
t9 = time.perf_counter()

# まとめて画面更新
layer.update(0, 0, w, h)

# 処理時間
loop_ms   = (t9 - t0) * 1000
print(f"Loop: {loop_ms:.1f} ms")

# Loop: 14837.0 ms

256×256ピクセルのレイヤーの書き込みで約15秒ほど

レイヤーの塗りつぶし(高速版)

from gi.repository import Gimp, Gegl
import time

# --- 対象レイヤー ---
img = Gimp.get_images()[0]
sel = img.get_selected_layers()
layer = sel[0] if sel else None
if not layer:
    raise RuntimeError("レイヤー未選択")

w, h = layer.get_width(), layer.get_height()
if w == 0 or h == 0:
    raise RuntimeError("レイヤーサイズが 0")

buf  = layer.get_buffer()
rect = Gegl.Rectangle.new(0, 0, w, h)

# 1) 読み出しフォーマットを決める(まず RGBA8 を試し、無理なら RGB8)
FMT_CANDIDATES = ["R'G'B'A u8", "RGBA u8", "R'G'B' u8", "RGB u8"]
for FMT in FMT_CANDIDATES:
    raw = bytearray(buf.get(rect, 1.0, FMT, Gegl.AbyssPolicy.NONE))
    px = w * h
    if len(raw) in (px*4, px*3):
        break
else:
    raise RuntimeError(f"想定外の配列長 len={len(raw)} for {w}x{h}")

BPP = len(raw) // (w*h)   # 3 or 4

# -- loop: 読み込み済み raw を編集 → 書き戻し --
t0 = time.perf_counter()

for y in range(h):
    row_base = y * w * BPP
    for x in range(w):
        i = row_base + x * BPP
        raw[i + 0] = 255  # R
        raw[i + 1] = 0    # G
        raw[i + 2] = 0    # B
        if BPP == 4:
            raw[i + 3] = 255  # A

buf.set(rect, FMT, bytes(raw))

t1 = time.perf_counter()
# -- end loop --

layer.update(0, 0, w, h)

print(f"Loop: {(t1 - t0)*1000:.1f} ms")

# Loop: 61.6 ms

1秒以下になりました。ちょっと速すぎる気がする。

選択範囲バウンディングボックスを取得する。

Gimp.Selection.bounds(img)
# (True, non_empty=True, x1=862, y1=44, x2=1076, y2=342)

True … 関数の成功
non_empy…Ttrue 範囲選択されている。False 範囲選択されていない
x1…862 バウンディングボックスの左上隅のx座標
y1…44 バウンディングボックスの左上隅のy座標
x2…1076 バウンディングボックスの右下隅のx座標
y2…342 バウンディングボックスの右下隅のy座標

座標が選択範囲内か判定

Gimp.Selection.value(img, x, y)
# 0 ... 範囲外
# 255 ... 範囲内

レイヤーのアルファチャンネル有無判定

new_layer.has_alpha()

レイヤーのアルファチャンネル追加

new_layer.add_alpha()

レイヤーをクリア

レイヤーがアルファチャンネル有りだと、レイヤーが透明になるはず。

Gimp.Drawable.edit_clear(new_layer)

イメージ内のレイヤー数を取得

レイヤーの一覧を要素数をlen()で取得していますが、もっと良い方法があるかもしれません。

layer_max = len(image.get_layers())

選択中のレイヤーの順番(index)を取得

img = Gimp.get_images()[0]
layer = img.get_selected_layers()[0]
layers = img.get_layers()
index = layers.index(layer)
print(index)
# 4 ...(0から数えて5番目のレイヤーが選択)

選択範囲を2px拡張します

my-growselect\my-growselect.py

#!/usr/bin/env python3
# GIMP 3 (Python-Fu) : 選択範囲を2px拡張します
import sys, gi, subprocess
import datetime, os, time
gi.require_version('Gimp', '3.0')
from gi.repository import Gimp, GObject, Gio
PROC = "python-fu-growselect"
def run(proc, run_mode, image, drawables, config, data):
    Gimp.Selection.grow(image, 2)
    
    #Gimp.message(f"Start Grow Select")
    return proc.new_return_values(Gimp.PDBStatusType.SUCCESS, None)
class RunGrowSelect(Gimp.PlugIn):
    def do_query_procedures(self):
        return [PROC]
    def do_create_procedure(self, name):
        if name != PROC:
            return None
        p = Gimp.ImageProcedure.new(self, name, Gimp.PDBProcType.PLUGIN, run, None)
        p.set_menu_label("Grow Select")
        p.add_menu_path("<Image>/Filters/My")   # 画像を開いている時に表示
        p.set_documentation(
            "選択範囲を2px拡張します",
        )
        p.set_attribution("Your Name", "Public Domain", "2025")
        # 画像が必要なメニュー配下なので image types を指定
        p.set_image_types("*")
        # 描画対象が選べる状態で有効化(最低限の感度)
        p.set_sensitivity_mask(Gimp.ProcedureSensitivityMask.DRAWABLE)
        return p
Gimp.main(RunGrowSelect.__gtype__, sys.argv)

選択範囲を4px拡張し穴を削除

my-floodselect\my-floodselect.py

#!/usr/bin/env python3
# GIMP 3 (Python-Fu) : 選択範囲を4px拡張し穴を削除
import sys, gi, subprocess
import datetime, os, time
gi.require_version('Gimp', '3.0')
from gi.repository import Gimp, GObject, Gio
PROC = "python-fu-floodselect"
def run(proc, run_mode, image, drawables, config, data):
    Gimp.Selection.grow(image, 4)
    Gimp.Selection.flood(image)
    
    #Gimp.message(f"Start Flood Select")
    return proc.new_return_values(Gimp.PDBStatusType.SUCCESS, None)
class RunFloodSelect(Gimp.PlugIn):
    def do_query_procedures(self):
        return [PROC]
    def do_create_procedure(self, name):
        if name != PROC:
            return None
        p = Gimp.ImageProcedure.new(self, name, Gimp.PDBProcType.PLUGIN, run, None)
        p.set_menu_label("Floor Select")
        p.add_menu_path("<Image>/Filters/My")   # 画像を開いている時に表示
        p.set_documentation(
            "選択範囲を4px拡張し穴を削除",
        )
        p.set_attribution("Your Name", "Public Domain", "2025")
        # 画像が必要なメニュー配下なので image types を指定
        p.set_image_types("*")
        # 描画対象が選べる状態で有効化(最低限の感度)
        p.set_sensitivity_mask(Gimp.ProcedureSensitivityMask.DRAWABLE)
        return p
Gimp.main(RunFloodSelect.__gtype__, sys.argv)

コメント