先日ご紹介した AI で背景を削除するプラグインですが、どのようなアルゴリズムを使っているのが、自分の勉強のために解読してみました。個人的には外部の AI ライブラリやユーティリティを使った GIMP プラグインを開発したいと思っているので、その予備的勉強ということです。
以下ぐうてるブログ版を基にしたアルゴリズムの概略です。


基本、Gimp.main でスタートします。そして、class AIRemoveBG を呼び出しますが、このクラスの役割は、主として GIMP へのプラグインとしての登録情報を指定することです。ただこのプラグインでは併せて、パラメータ取得のダイアログ表示や取得するパラメータの種類に関しても、ここで指定しています。このため、
procedure.add_boolean_argument
procedure.add_int_argument
というようなメソッドを使っています。ちょうど、GIMP 2.10 のレジスター関数で指定したのと同じような使い方ですが、これは後述する GIMP 独自のダイアログウィジェットとセットで使うようです。
因みに私が書いているダイアログ関係は、現在大半で Gtk の標準ウィジェットを使っているので、このような指定は使っていません。
そして、Gimp.ImageProcedure.new で実質的な本体である def RemoveBG を呼び出します。
■ def RemoveBG
最初に、ダイアログの定義と実行を行っています。
if run_mode == Gimp.RunMode.INTERACTIVE:
GimpUi.init('plug-in-RemoveBG-python')
dialog = GimpUi.ProcedureDialog(procedure=procedure, config=config)
dialog.fill(["asMask"])
dialog.fill(["AlphaMatting"])
dialog.fill(["aeValue"])
dialog.get_int_combo("selModel", GimpUi.IntStore.new (tupleModel))
dialog.fill(["selModel"])
if not dialog.run():
return procedure.new_return_values(Gimp.PDBStatusType.CANCEL, GLib.Error())
ダイアログの作成には、Gimp 独自の GimpUi.ProcedureDialog を使っており、dialog.run というような記述は見当たりませんが、これで実行まで行っているようです。ダイアログにおけるウィジェットの配置は、
dialog.fill() というメソッドで行っていますが、引数を先の procedure.add_boolean_argument 等で指定した引数を取得することで、配置できてしまうようです。このため、Gtk の標準的ウィジェットだけを使っていると、ボックスを配置して、その中に更にウィジェットを配置して... と 7 〜 8 行のコードを書かなければいけないところを、1 行で済ませることが可能になっています。
その後は rembg で使う一時ファイル関係の指定が続きます。rembg で読み込ませるのは jpeg 形式のファイル、出力は png 形式のファイルを指定しています。
125行目以下は、remgb を実行するためのコマンド行の編集です。
config.get_property メソッドで、ダイアログからパラメータを取得して、それに基づいてコマンド行を作成しています。command_args というリスト変数がコマンド行に相当します。
159行目以下の image.undo_group_start() から本格的な実行プロセスに入ります。
最初に行うのはオリジナルレイヤー drawable に基づいて、rembg に読ませる一時 jpeg ファイルを作成します。入力ファイルの作成が所定時間に終了しなければエラーメッセージを出し終了、作成に成功すれば、次の rembg の実行に移ります。
GIMP プラグインから外部コマンド rembg を実行するわけですが、これは以下のコマンドを使っています。
result = subprocess.run(
command_args,
check=True,
capture_output=True,
text=True,
shell=False
)
この subprocess.run というメソッドが、外部コマンドを実行するメソッドで、具体的な実行コマンドは先に作成しておいた command_args を引数として与えることで実行しています。
因みに rembg はインストールするときに python を使いますが、インストールが終わると、実行バイナリーファイルを作るようなので、その作成された実行バイナリーファイルをちゃんと所定の位置に配置していれば、依存関係や仮想環境の問題はクリアされるのではないかと思われます。
この実行が成功すれば、背景が切り抜かれたファイルが png ファイルとして作成されます。これを一旦、既存の image オブジェクトの、既存レイヤーの上に挿入します。しかし、切り抜き部分をマスクとして保存するオプションを指定していると、挿入したレイヤーを一旦削除して、オリジナルレイヤーを複写して挿入し直し、結果ファイルから切り抜き部分を選択範囲として読み込んだあと、その選択範囲に基づくマスクを今挿入し直したレイヤーに掛けます。
これで結果ファイルは完成したので、rembg に読み込ませる一時ファイルを削除して、全プロセスを終了します。
■ 補助ユーザ定義関数
これ以外に以下のユーザ定義関数があります。
def pdbCall
def store_layer_png
def store_layer_jpg
def pdbCall は従来 (2.10) のように pdb プロシジャーを呼び出すための関数です。2.10 ではダイレクトに pdb プロシジャーを呼び出せましたが、3.x では直接呼び出せなくなったので、この関数を通して呼び出します。
本プログラムでは以下の3つの pdb プロシジャーを呼び出しています。
file-png-export
file-jpeg-export
gimp-file-lodad-layer
def store_layer_png と def store_layer_jpg は文字通り rembg の入出力ファイルを定義、作成するためのユーザ定義関数です。
■ プログレスバー
GImp.progress_init(string) stringは、表示メッセージ
Gimp.progress_update(float) float は進行状況のパーセンテージで 0.0-1.0 の値
で画面の下にプログレスバーを表示します。
https://lazka.github.io/pgi-docs/#Gimp-3.0/functions.html#Gimp.progress_init
■ 学習ポイント
本プログラムのキーポイントとしては、外部コマンドを実行するために、subprocess.run メソッドを使っているというあたりでしょうか。なお、これはファンさんのオリジナルプログラムと比べてみると、ぐうてるブログでの工夫のようで、ファンさんのオリジナルプログラムでは、os.system(コマンド) を使っていました。
■ Flatpak 版のプラグインの開発について
またここでは詳細は記してはいませんが、通常はサンドボックス内で動く Flatpak バージョンは、通常の起動ではこのメソッドを使うと外部コマンドと正常な通信ができないので、特殊な起動方法が必要になる、という点が注意点になるかと思います。
まず、外部コマンドを入れる際は、コマンドラインの頭に、
flatpak-spawn --host
を入れ、それに引き続き、コマンドを続けます。
さらに flatpak 起動を行う際に、
flatpak run --socket=session-bus app/org.gimp.GIMP/x86_64/stable
--verbose
と、 --socket=session-bus というオプションを入れます。しかしサンドボックスに大きな穴を開けることになるのでセキュリティ面では低下します。とはいえ、自分でビルドした GIMP や AppImage 版はサンドボックスは使わないのでそんなに気にする必要はないと思います。