Gimp3のPython-fuで外部コマンドをフィルターとして実行「適応的閾値による二値化処理 adaptiveThreshold」

玉洲習画帖 第5巻 コンピュータ
出典:国立国会図書館「NDLイメージバンク」(https://ndlsearch.ndl.go.jp/imagebank)

GIMPには適応的閾値による二値化処理が無いようなので、OpenCVSharpのCv2.adaptiveThresholdで外部コマンド(ToLine.exe)を作り、GIMPのフィルターとして呼び出す実装になっています。

外部コマンドをフィルターとして実行

プラグインディレクトリに保存
ファイル名:my-toline\my-toline.py

#!/usr/bin/env python3
# GIMP 3 (Python-Fu) : toLine.exeを使って線画抽出
import sys, gi, subprocess
import datetime, os, time
gi.require_version('Gimp', '3.0')
from gi.repository import Gimp, GObject, Gio
PROC = "python-fu-toline"
def run(proc, run_mode, image, drawables, config, data):
    start = time.time()
    now = datetime.datetime.now()
    filename = now.strftime('%Y%m%d%H%M%S') + ".bmp"
    filename2 = now.strftime('%Y%m%d%H%M%S') + "_line.png"
    temp_dir = 'j:/temp'
    temp_in_file = os.path.join(temp_dir, filename)
    temp_out_file = os.path.join(temp_dir, filename2)

    try:
        # 出力先の画像ファイルのパスオブジェクトの生成
        out = Gio.File.new_for_path(temp_in_file)
        # 画像の保存(エクスポート)
        Gimp.file_save(Gimp.RunMode.NONINTERACTIVE, image, out, None)
        subprocess.run(["c:/Users/karet/bin/ToLine.exe", "-i", temp_in_file, "-o", temp_out_file])
        # 読み込みたいファイル
        f = Gio.File.new_for_path(temp_out_file)
        # レイヤーとして読み込む
        new_layer = Gimp.file_load_layer(Gimp.RunMode.NONINTERACTIVE, image, f)
        new_layer.set_name("toLine")
        # 画像に挿入(インデックス0で一番上に追加)
        image.insert_layer(new_layer, None, 0)
        os.remove(temp_in_file)
        os.remove(temp_out_file)
        end = time.time()
        Gimp.message(f"処理時間: {end - start:.3f} 秒")
        return proc.new_return_values(Gimp.PDBStatusType.SUCCESS, None)
    except Exception as e:
        Gimp.message(f"起動失敗: {e}")
        return proc.new_return_values(Gimp.PDBStatusType.EXECUTION_ERROR, None)
class RunNotepad(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("Run ToLine")
        p.add_menu_path("<Image>/Filters/My")   # 画像を開いている時に表示
        p.set_documentation(
            "Launch notepad.exe via subprocess",
            "外部の toLine.exe ",
            "my-toline.py"
        )
        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(RunNotepad.__gtype__, sys.argv)

感想

外部コマンドに画像を渡すためにテンポラリディレクトリに画像ファイルをエクスポートしています。
劣化させないためPNG形式でエクスポートしていましたが、GIMPのPNGエクスポートは遅いので、BMP形式でエクスポートするようにしてみました。
手元の環境で4000pxx3000pxぐらいの画像の処理で2秒弱といった感じです。速くは無いですが個人的に許容範囲内の処理時間だと思います。

問題点として、テンポラリディレクトリにエクスポートをしている関係で、次回のエクスポートの出力先のディレクトリがテンポラリディレクトリに変更されてしまうこと。

問題点はありますが、この外部コマンドの呼び出す方式は、C#などのプログラミング言語でGIMPのフィルターを拡張出来るので結構使える場面が多いのでは無いかと思います。

コメント