プログラミングGIMP

自由研究にGIMPプログラミングをやってみた(2.6.10を使用)。夏休みはないけど。

GIMPを使って、複数の操作を順番に実行する必要のある、ある程度手間のかかる画像処理をしたいのだけど、手作業で繰り返すのはちょっと辛い。また、できればバッチ的にたくさんのファイルを処理したい。というわけで、ごく具体的な目的があってのチャレンジである。参考にしたのは以下。

GIMPでのプログラミングはScript-Fuと呼ばれていて、Schemeで書く。かっこを多用しなければならないのでついつい腰がひけてしまう。ただ、ここでやりたいのは操作手順を書き起こすことなので、webサービスを作るような場面でのプログラミングよりはずっと単純な内容で済む。「;」のかわりに「)」を書く、という程度の気持ちでもなんとかなる。はずだ。

作業の流れ

今回行ったいくつかのプログラム作成ではどれもこんな手順を繰り返した。

  1. まず、プログラムの最初と最後に書く決まり文句をコピー・ペーストする。最初の一つは上に挙げたページやファイルからコピー・ペーストした。
  2. 次に、決まり文句の間に必要な命令(GIMPでの操作に対応する)を書き加えいく。
  3. 終わったらセーブして、GIMPに読み込ませる。
  4. そして、メニューからプログラムを実行して動作を確認する。
  5. うまく動けば完了。そうでなければファイルの編集→セーブ→読み込み→実行→動作確認を必要なだけ繰り返す。

プログラムの実体

GIMPにプログラムを実行させるときの単位は関数である。だから、プログラミングでは関数を書く。

関数を書いたファイルは編集→環境設定→フォルダ→スクリプトで表示される場所に置く。拡張子はscm。手元では~/.gimp-2.6/scriptsとなっていたので、~/.gimp-2.6/scripts/foobar.scmといった名前のファイルとした。

一つのファイルに複数の関数を書くことができる。手順が長かったり、似た手順を何通りかで作りたい場合など、適度なサイズの関数に分けておくと、全体的な記述量を減らすことができる。このあたりは一般的なプログラミングと同じ。

決まり文句

作成したプログラムはGIMPのメニューに登録することができる。というよりも、メニューから実行できるようにするために、前述した「決まり文句」を書いておく。

メニューから実行しないのであればこの決まり文句がなくたって構わない。ただ、動作確認の際にはメニューから実行して、画像がどう変わっていくかをいちいち見たほうがわかりやすい。なので、そういう必要がないとはっきりしている場合以外はとりあえず決まり文句から書き始める。(そのうち必要あるかどうかはわかるようになる。)

(define (script-fu-foo-bar-baz image layer ...)
  ... GIMPに実行させたい手順を書くところ ...
)

(script-fu-register "script-fu-foo-bar-baz"
  "メニューに表示させる見出し..."
  "関数の説明文"
  "作者の名前"
  "copyright表示"
  "作成日付"
  ""
  SF-IMAGE       "Input Image"   0
  SF-DRAWABLE    "Input Layer"   0
  ...
)

(script-fu-menu-register "script-fu-foo-bar-baz" "<Image>/Filters/メニュー階層の小見出し")

「define 〜」というのが関数を書く部分。最初のかっこの中身が関数名と引数の指定になる。Script-Fuのための関数(メニューから実行する関数)は「script-fu-」で始める決まりだそうなので従っておく。

「script-fu-register 〜」と「script-fu-menu-register 〜」は作成した関数をGIMPに登録するためのもの。前者はメニューから実行された際、処理対象となる画像(キャンバス)やファイル名の指定、入力されたパラメータ値などを関数に渡すための定義となる。最後の「...」の部分にいろいろな記述をすることで、実行時にファイル選択させることなどが可能となる。

後者は関数をメニューのどこにいれるかを指定するもの。どこであっても構わないだろうが、たいていは画像→フィルターあたりが適当だろう。

script-fu-registerとscript-fu-menu-registerの最初の引数で関数を指定するので、defineした関数名にそろえておく。

関数の探し方

defineの中身には、GIMPのGUIで行っている操作を書くことになる。これらの操作もやはり関数として提供されている。関数の呼び出しは「(関数名 引数...)」という形式で行う。前項の「(script-fu-register 〜)」も関数呼び出しだし、関数を定義するための「(define 〜)」もそう。

手順が一つだけならScript-Fuにするまでもないわけで、関数の中ではいくつもの処理を実行することになる。複数の操作を順番に実行するにはこの関数呼び出しを並べてやればよい。

(define (script-fu-foo-bar-baz image layer)
  (function-a)
  (function-b 123 "456")
  ...
)

と、こんな具合い。では、実行したい操作に対応する関数をどうやって探すのか。

一つのルートはフィルタ→Script-Fu→Script-Fuコンソール→参照で表示されるScript-Fuプロシージャブラウザである。検索という部分にそれらしいキーワードを入力する。うまく見付かると関数のリストが表示される。おれおれScript-Fu入門に詳しい説明がある。

キーワードがわからないときにはメニューの項目が二つめのルートになる。……が、メニューは日本語で表示されているので、英語の関数名とはなかなか結び付きにくい。また、省略されていることもある。こういうときはヘルプを参照する。そこで出てくる英語の見出しが参考になることがある。

あとは勘。あるいはシンプルに検索する。似たようなScript-Fuがあればそこから類推できることもある。標準添付のScript-Fuもあるのでgrepしてみるのもよい。

動作確認とデバッグ

プロシージャブラウザを見るために起動してScript-FuコンソールではScript-Fuで使う関数の実行ができる。これを使って動作確認すればよさそうであるが…… 使い勝手はあまりよくない、と思う。(一行しか書けないし、補完もきかない。Rubyでいうinspect的な表示がちょっと弱い、など。)

しょうがないので、ごくごく簡単な動作確認以外は、ファイルを編集してセーブしてフィルタ→Script-Fu→スクリプトを再読み込みとした上で、メニューから実行するというのを繰り返した。

プログラムに間違いがあって動作させられないときにはエラーメッセージが表示される。ただ、それではよくわからないことも少なくない。

ウィンドウ→ドッキング可能なダイアログ→エラーコンソールと選択すると動作中のメッセージが記録されていくウィンドウが開く。プログラム中に「(gimp-message "メッセージ〜")」と書いておくと、その内容はエラーコンソールに表示させることができる。(エラーコンソールがないときにはキャンバスの下端に表示されては消えていく。)

関数の効果を知る

手順が長くなると、エラーは起きないけども求めた結果にならないということもある。Script-Fuのステップ実行ができるといいのだけど……。

前項ではファイル書き換え→再読み込み→実行を繰り返すとはいったものの、いくつかあるなかのどの関数を使えばいいかわからないときや引数に何を値えれば適正なのかわからないときなど、ファイル編集を繰り返すのが辛いこともある。コンソールから現在開いているキャンバスを指定して関数を実行すれば、ある程度の助けになるかもしれない。

多くの関数ではキャンバスと、キャンバス上のレイヤを引数にとる。したがってこれらをコンソール上で得る方法が必要となる。キャンバスを得るにはこんな風にすればよいようだ。

(aref (car (cdr (gimp-image-list))) 0)

これを使って次のようにしておくと、コンソール上ではimageによって最初のキャンバスを参照できるようになる。(ここに限っていえば変数設定のようなもの。)

(define image (aref (car (cdr (gimp-image-list))) 0))

次にレイヤを得る方法。これは行いたい操作によって何が必要なのか変わってくるのだけども、とりあえずキャンバスでアクティブになっているレイヤを得るなら次のようにする。

(car (gimp-image-get-active-layer image))

やはり次のようにdefineしておくことでレイヤをいつでも参照できるようになる。

(define layer (car (gimp-image-get-active-layer image)))

これらを用いて、次のようなことができるようになる。

> (gimp-image-get-filename image) ; 元ファイルの名前を得る
("/tmp/xyz.jpg")
> (gimp-histogram layer HISTOGRAM-VALUE 0 255) ; レイヤのヒストグラムを得る
(215.6229328 76.42384291 255 1732800 1732800 1)
> (gimp-invert layer) ; レイヤの明暗を反転させる
(#t)

実はもっとスマートな方法があるのかもしれないが、よくわからない。(とりあえず手元でやりたかったことはできたので追求していない。)

実際に使った関数

実際に作成した関数たちの中で使った関数を挙げてみる。適当にコピー・ペーストしているのでこれが全部ではないかもしれない。

  • number->string - 数値を文字列に変換
  • string-append - 文字列の列結
  • gimp-message - デバッグプリント
  • gimp-histogram - ヒストグラムを得る
  • gimp-levels - 色→レベル
  • gimp-displays-flush - キャンバスの再描画
  • gimp-image-convert-grayscale - 画像→モード→グレースケール
  • gimp-layer-copy - レイヤの複製
  • gimp-image-add-layer - キャンバスにレイヤを加えて操作できるようにする
  • gimp-image-set-active-layer - 指定したレイヤをアクティブにする
  • plug-in-unsharp-mask - フィルタ→強調→アンシャープマスク
  • gimp-by-color-select - 色域を選択
  • gimp-image-remove-layer - レイヤの削除
  • gimp-selection-shrink - 選択→選択範囲を縮小
  • gimp-selection-grow - 選択→選択範囲を拡大
  • gimp-selection-feather - 選択→境界をぼかす
  • gimp-selection-clear - 選択→選択を解除
  • plug-in-gauss - フィルタ→ぼかし→ガウシアンぼかし
  • plug-in-sel-gauss - フィルタ→ぼかし→選択的ガウシアンぼかし
  • gimp-edit-fill → 塗りつぶし
  • gimp-context-get-background - 背景色を得る
  • gimp-context-set-background - 背景色を設定する
  • plug-in-edge - フィルタ→輪郭抽出→輪郭
  • gimp-invert - 色→階調を反転
  • gimp-edit-copy → 編集→コピー
  • gimp-selection-combine → チャンネルを選択範囲に…
  • gimp-drawable-width → レイヤの幅を得る
  • gimp-drawable-height → レイヤの高さを得る
  • gimp-layer-new - 新しくレイヤを作る
  • gimp-image-remove-channel - チャンネルを削除する
  • gimp-image-merge-visible-layers - レイヤを統合する
  • gimp-floating-sel-anchor - フローティングレイヤを固定
  • gimp-edit-paste - 編集→貼り付け
  • gimp-image-scale-full - 画像→画像の拡大・縮小

まとめ?

処理結果をファイル出力するには前項に挙げていないgimp-file-saveやfile-jpg-saveなどを使わなければならないが、file-jpg-saveの引数を指定するのは大変そうであるとか未解決(?)の部分もある。またバッチ処理をするためにはファイルリストの取得やコマンドラインからの実行(gimp -b)など、もう少し調べておくべきところもある。

が、当初の目的をかなえるための目処はたったので、これ以上の追求はいったん止めておく。というよりも、画像処理上の知識や用語のほうが足りなくなってきて、プログラミングの追求よりもむしろそちらの勉強が必要になってきたのだった。