いきなり日曜大工

好きなことを好きなだけやる

マイ地図タイルを作ろう #2 GIMP-Python プラグイン入門

GIMP のプラグイン開発事情

というわけで画像からマップタイルを自動生成してくれる GIMP のプラグインを書くべく、GIMP のプラグイン開発事情を調べます。

GIMP + Windows10 の環境でマクロ程度のプラグインを手軽に作るなら現在のところ

  • Script-Fu (Scheme)
  • Python-Fu (Python)

の2択になるようです。
Scheme って知りませんでしたが Lisp なんですね。懐かしの too many ()s.
Python はやったことありませんが興味はありました。こちらは no more {}s.

今からやるなら no more {}s かなと思うので、またもや未経験の方を選択(適当)。

Python-Fu

Python-Fu とは、GIMP が提供している機能拡張用のライブラリ libgimp に Python からアクセスできるようにしてくれるラッパーモジュールのことで、これを用いれば Python から GIMP を操作したり、作ったスクリプトをプラグイン登録することができる、そうです。

GIMP にはこのモジュールが利用できる状態になっている対話型の Python インタプリタ Python-Fu コンソール が用意されていて、ここで実際にその機能を使ってのお試しプログラミングができます。

ところで Python-Fu という呼称なんですが、これは Script-Fu と対になる便宜上の呼び方(最近の呼び方?)といった感じで、実際にドキュメント類を見た限りでは GIMP-Python と呼ぶ方が圧倒的に多かったです。公式サイトの ドキュメント の冒頭にも

“GIMP-Python, which is a set of Python modules that act as a wrapper to libgimp allowing the writing of plug-ins for GIMP”

とありましたんで、自分も今後 GIMP-Python で進めたいと思います。

GIMP-Python でプラグイン開発

GIMP 公式手引きの GIMP スクリプト には Script-Fu しかないっていうね・・・。
というわけで、GIMP-Python について調べたことをまとめておきます。一応公式サイトのドキュメント(他参考サイト)に目を通して書いていますが、あくまで自分の理解あるいは経験に基づくメモですので怪しいと思った方は原文を直接ご覧ください。

GIMP Python について参考になったサイト

  • GIMP Python Documentation
    公式サイトの文書です。1ページが長すぎて読みにくいですが基本的かつとても重要なことが書かれています。
  • Writing GIMP Scripts and Plug-Ins
    Beginning Gimp という本の著者 Akkana Peck さんというの方のサイトですが、説明スライドやチートシート、サンプルスクリプトまで至れり尽くせり。今回のプラグイン作りの最後までお世話になることになる GIMP 先生です。

プラグインスクリプトの構造

以下はサンプルスクリプト。上記の Akkana さんのサイトの スライド に最小構成の見本があったので借用させて頂きまして、少々手直しの上(書式がちょっと古かったみたいなのと文字コード対策を追加)日本語の注釈をつけています。

プラグインスクリプトの置き場所は Windows10 + GIMP-2.10 の場合

C:\Users\{Username}\AppData\Roaming\GIMP\2.10\plug-ins\

になります。ファイルを保存したら GIMP を再起動すればプラグインが読み込まれます。

#!/usr/bin/python
# -*- coding: utf-8 -*-                    # 文字コード宣言(2行目までに)

from gimpfu import *                       # gimpfu モジュールのインポート

# プラグイン関数
def do_stuff(img, layer, howmuch) :
    pdb.gimp_invert(layer)

register(
    "python_fu_do_stuff",                  # プロシージャとしてユニークな名前
    "Do stuff",                            # 簡単な説明
    "Do some neat stuff",                  # Procedure Browser のヘルプ
    "Your Name",                           # 著者
    "Your Name",                           # 著作権者
    "2008",                                # 日付
    "Do Stuff...",                         # メニュー追加時のラベル
    "RGB*",                                # 扱う画像タイプ
    [                                      # プラグイン関数の引数
      (PF_IMAGE, "img", "Image", None),
      (PF_DRAWABLE, "drawable", "Drawable", None),
      (PF_INT, "amt", "How much?", 50)
    ],
    [],                                    # 戻り値 (まず使わない)
    do_stuff,                              # プラグイン関数
    menu="<Image>/Filters/Enhance"         # GIMP のメニューパス
)

main()                                     # 必要です

枠組みとして重要なのは、4行目の gimpfu モジュールの呼び出し、7行目のプラグイン関数、10行目の register 関数、末尾行の main 関数です。
プラグインを実行すると、まず末尾行にある main が呼び出されます。次にユーザからの入力を受け取る必要があれば入力用の GUI が表示され(このケースでは “How much?” と聞かれて整数を入力)、GUI とのやりとりが終了すると入力が引き渡されプラグイン関数が実行されます。

register 関数は GIMP にスクリプトを登録するときに使われる関数です。プラグインの情報やメニュー内での位置、プラグイン実行時の入力(プラグイン関数に渡される引数)などをここで定義します。

その他メモ

  • 2行目: ソースコードに ascii 以外の文字を使うときは2行目までに使用する文字コードの宣言が必要。これは Python の問題 (2.1.4. Encoding declarations)

register 関数

サンプルプラグインの register 関数部分の抜粋です。

register(
    "python_fu_do_stuff",                  # プロシージャとしてユニークな名前
    "Do stuff",                            # 簡単な説明
    "Do some neat stuff",                  # Procedure Browser のヘルプ
    "Your Name",                           # 著者
    "Your Name",                           # 著作権者
    "2008",                                # 日付
    "Do Stuff...",                         # メニュー追加時のラベル
    "RGB*",                                # 扱う画像タイプ
    [                                      # プラグイン関数の引数
      (PF_IMAGE, "img", "Image", None),
      (PF_DRAWABLE, "drawable", "Drawable", None),
      (PF_INT, "amt", "How much?", 50)
    ],
    [],                                    # 戻り値 (まず使わない)
    do_stuff,                              # プラグイン関数
    menu="<Image>/Filters/Enhance"         # GIMP のメニューパス
)

プラグインが扱う画像タイプの指定

18行目、プラグインが処理対象として受け入れ可能な画像のタイプを指定します。画像のタイプとは、GIMP の3つの基本モードとされている RGB, Grayscale, Indexed を軸に

  • RGB, GRAY, INDEXED (アルファチャンネルなし)
  • RGBA, GRAYA, INDEXEDA (アルファチャンネルあり)

また RGB* でアルファチャンネル有り無しどちらでもという指定ができます。まとめて “RGB*, GRAY*” と指定すれば RGB と Grayscale ならアルファチャンネルの有無を問わず受け付ける、という意味になり、”*” なら何でも来いになります。
この指定は入力画像が必要でないなら空 “” にすべきです。むしろいらないのに設定してるとこの指定条件と合致する(無関係な)画像を開いている状態じゃないとプラグインを起動できなくなります。

プラグイン関数の引数と入力 GUI

19行目からの配列、これはプラグイン関数に渡される引数を定義する配列です。

    [                                      # プラグイン関数の引数
      (PF_IMAGE, "img", "Image", None),
      (PF_DRAWABLE, "drawable", "Drawable", None),
      (PF_INT, "amt", "How much?", 50)
    ],

このサンプルスクリプトではプラグインを実行するとまずダイアログが出て How much? と整数の入力を促す PF_INT 型 の UI が表示されます。このようにパラメータの入力に見合った UI 型を引数定義用の配列に指定すると、プラグイン起動時にその UI が表示されてユーザからの入力を受け取り、それがプラグイン関数の引数として渡されます。

PF_INT の UI

ちなみにサンプルスクリプトの How much? な整数はプラグイン関数に引き渡されますが、実際にプラグイン関数の中では使われてないです(まあサンプルだということで)。

今開いている画像に処理を加えたい場合もここで画像やレイヤーを引数に指定することになり、サンプルスクリプトでもそうしているのですが、これについてはちょっと特殊で

[
    (PF_IMAGE, "image", "Input image", None),       # 画像
    (PF_LAYER, "layer", "Input layer", None),       # レイヤー (PF_DRAWABLE も可)
    ...                                             # あとはお好みで
]

のように配列の先頭にこの順番で書くと、プラグイン関数に開いている画像とレイヤーが自動で渡され、この2つの入力を促す GUI は省略されます。このときプラグイン関数側は

def do_stuff(image, layer, ...):

として受け取ります。

使える UI 型についてはドキュメントに リスト がのっていますが、具体的な型のイメージがさっぱりわからかなかったので(画像処理ソフトそのものに疎いもので) Akkana さんの GitHub にある UI サンプル表示プラグイン を参考にさせていただきました。GIMP に入れてみると様々な UI のサンプルを見ることができます。

プラグインを登録できるメニューのパス

26行目はプラグインを登録するときに GIMP のメニューのどこに追加するかの指定です。

    menu="<Image>/Filters/Enhance"         # GIMP のメニューパス

このメニューに追加できる場所 (placeholder) のリストは公式のドキュメント類に見つけられなかったので、気合でググり出したところ結局そこは Akkana さんのブログでした。

GIMP menu placeholders (Shallow Thoughts)

記事によると、placeholders のリストは GIMP ソースの menus/image-menu.xml に定義があるので(自分でもソースを取り寄せて実際に確認) 、そこから見やすいようにメニューまわりを抜き出してツリー風に整形して載せてくださったそうです。ありがたや GIMP 先生。

その他メモ

  • プラグイン関数に引き渡す配列の要素で、2番目にこれは変数名じゃないのというのがあるんだけど別にプラグイン関数側の変数名と同じじゃなくても動く模様。
  • GUI が省略されて自動渡しになる順番の条件: 第1引数が PF_IMAGE|PF_DRAWABLE|PF_LAYER|PF_CHANNEL のとき。第1引数が PF_IMAGE かつ 第2引数が PF_DRAWTABLE|PF_LAYER|PF_CHANNEL なら第2引数まで省略(独自調査、他にもあるかもしれない)。

つづく。