インフィニットループ 技術ブログ

2015年06月11日 (木)

著者 : mizuno_as

GIMP(とあの言語)でお手軽にフォトレタッチしよう

こんにちは、mizuno_asです。みなさん、写真を撮ってますか?
デジタルカメラやカメラつきケータイのコモディティ化によって、フィルムに比べてはるかにカジュアルに、誰もが写真を楽しむことができるようになりました。最近ではほぼすべての携帯電話やスマートフォンにカメラが搭載されていますから、日本人はほぼ全員、常にデジタルカメラを持ち歩いている状態だと言ってもよいでしょう。
しかし、撮影した後のデータはカメラのメモリーに入れっぱなしという人も多いのではないでしょうか。フォトレタッチソフトでほんのひと手間かけるだけで、写真の見栄えはぐっと良くなります。せっかく撮影した写真ですから、どうせなら綺麗に仕上げたいですよね。とはいえ、トーンカーブをいじったり、フィルタをかけたり、トリミングしたり……そんな手間が面倒くさいのもまた事実。そこで今回は、オープンソースのフォトレタッチソフトGIMPを使った、レタッチのサポートを考えてみました。


さて、画像の自動処理といえば、みんな大好きImageMagickがまず思い浮かびます。Webアプリのバックエンドや、シェルスクリプトの中から実行するのであればImageMagick一択なのですが、こと写真のレタッチとして考えると、少々使い勝手に難があります。たとえばconvertコマンドでトーンカーブを調整するにはlevelオプションを使ってこのようにするらしいのですが……
なるほどわからん。
やはり非対話的なプログラムは直感的ではなく、扱いが難しいですね。あくまで今回は「手作業のレタッチをスクリプトでサポートする」というスタンスですので、フォトレタッチアプリケーションのGIMP + マクロ言語で行きたいと思います ((「いざとなったら手作業で直せばいいや」と開き直ることも重要です 🙂 )) 。
GIMPはマクロとして、Script-Fuと呼ばれるSchemeベースのLISPインタプリタを搭載しています。GIMPで行える様々な機能、つまりレイヤーの作成、明るさの補正、トーンカーブの調整、画像のスケール……といったことを、LISPの関数を呼び出すことで自動実行させることができるのです。組込みマクロ言語自体はそれほど珍しい存在ではありませんが、みなさんが普段使っているテキストエディタの設定ファイルと同じLISPで書けるというのがいい所ですよね ((Python-Fuという機能もありますが、ここでは触れないことにします。)) 。それでは実際にスクリプトを作成していきましょう。

スクリプトの作成と配置

Ubuntu 15.04のGIMP 2.8の場合、スクリプトはホームディレクトリの~/.gimp-2.8/scriptsに配置します。Macの場合は~/Library/Application Support/GIMP/2.8/scriptsがそれに該当します。ここに拡張子がscmの適当なファイルを作成してください。
スクリプト内には必ず、script-fu-registerという関数がなくてはなりません。これはGIMPがプロシージャデータベースにスクリプトを登録する際に実行する関数です。スクリプト中の任意の場所に書くことができますが、ファイル末尾に書くのが一般的のようです。script-fu-registerには引数として、そのスクリプトの名前やメニュー内の登録位置、作者名、スクリプトが取るべき引数などを与えます。
例として、画像の明るさとコントラストを調整するスクリプトを考えてみましょう ((「色」→「明るさ – コントラスト」と同じことをするスクリプトです。自分で作成する意味はまったくありませんが、練習ということで。)) 。このスクリプトのscript-fu-register関数は以下のようになります。実際の処理を行う関数名がscript-fu-brightness-contrastで、メニューの「フィルター」の直下に「Brightness and Contrast」というメニュー項目を作成し、そこからスクリプトを実行できるようにします。その後はスクリプトの説明、作者名などなど、見たまんまです。SF-*となっているのが、script-fu-brightness-contrast関数に渡される引数です。SF-IMAGEは現在開いている画像が、SF-DRAWABLEには現在のアクティブレイヤーが暗黙的に渡されます。開いている画像に対して適用するスクリプトの場合、かならず引数の最初はこの2つである必要があります。SF-ADJUSTMENTはスライダーを作成します。ここではスライダーの初期値を0、最小値を-127、最大値を127、小ステップを1、大ステップを10に設定しています。スクリプトを実行すると、ダイアログ内にスライダーが表示され、変数に格納される値をユーザーが入力できるという仕掛けです。

(script-fu-register
"script-fu-brightness-contrast"
"<image>/Filters/Brightness and Contrast"
"Adjust brightness and contrast"
"Hajime Mizuno"
"copyright 2015, Hajime Mizuno"
"June 09, 2015"
""
SF-IMAGE "Image" 0
SF-DRAWABLE "Drawable" 0
SF-ADJUSTMENT "Brightness" '(0 -127 127 1 10 0 0)
SF-ADJUSTMENT "Contrast" '(0 -127 127 1 10 0 0))

次に、実際に処理を行うscript-fu-brightness-contrast関数を実装します。先ほどのscript-fu-registerで設定したふたつのスライダーで設定された値が、それぞれinBrightness、inContrastに格納されています。
gimp-brightness-contrastは文字通りの関数で、引数に受け取ったレイヤーの明るさとコントラストを変更します。明るさとコントラストは-127〜127の範囲の整数である必要があるため、先ほどスライダーの取りうる範囲をこのように指定したわけです。gimp-displays-flushは、画像への変更をすべてフラッシュする関数です。処理の最後に呼んでおきましょう。

(define (script-fu-brightness-contrast inImage inLayer inBrightness inContrast)
(gimp-brightness-contrast inLayer inBrightness inContrast)
(gimp-displays-flush))

ここまで書けたらコードを保存して、GIMPのメニューから「フィルター」→「Script-Fu」→「スクリプトを再読み込み」を実行してください。うまくいけば、「フィルター」メニューの末尾に「Brightness and Contrast」が追加されているはずです。

「ヘルプ」→「プラグインブラウザー」を開いてみましょう。自作スクリプトが登録されているのがわかります。

実際にスクリプトを動かしてみましょう。何か画像を開いたら、メニューからスクリプトを起動します。ダイアログに明るさとコントラストを入力して「OK」をクリックしましょう。

使用前と使用後を比較してみました。ちゃんと動いているようですね。

複数の処理を実行してみる

スクリプトの仕組みがわかったところで、もう少し処理を追加してスクリプトに意味を持たせてみましょう。先ほどの明るさ、コントラストの修正スクリプトに、スケールとアンシャープマスクの機能を追加してみます ((関数名がそのままなのはご愛嬌です。)) 。まずはscript-fu-registerに引数を増やします。SF-TOGGLEを使い、画像のスケールとアンシャープマスクの処理を実行するかどうかのフラグを用意しました。スケールする縦横のサイズはSF-VALUEで数値を入力する仕様とします。アンシャープマスクの半径、量、しきい値はSF-ADJUSTMENTを使うのですが、6番目の引数を1、ないしは2としておきます。これで小数点以下の桁が有効になります ((最後の引数はタイプの指定です。1にするとスライダーではなくスピナーになります。 )) 。

(script-fu-register
"script-fu-brightness-contrast"
"<Image>/Filters/Brightness and Contrast"
"Adjust brightness and contrast"
"Hajime Mizuno"
"copyright 2015, Hajime Mizuno"
"June 09, 2015"
""
SF-IMAGE "Image" 0
SF-DRAWABLE "Drawable" 0
SF-ADJUSTMENT "Brightness" '(0 -127 127 1 10 0 0)
SF-ADJUSTMENT "Contrast" '(0 -127 127 1 10 0 0)
SF-TOGGLE "Scale Image" TRUE
SF-VALUE "Width" "800"
SF-VALUE "Height" "600"
SF-TOGGLE "Unsharp Mask" TRUE
SF-ADJUSTMENT "Radius" '(5.0 0.1 500 0.1 1 1 0)
SF-ADJUSTMENT "Amount" '(0.50 0.00 50.0 0.01 0.1 2 0)
SF-ADJUSTMENT "Threshold" '(0 0 255 1 10 0 0))

実装はこのような感じになります。引数と実行する処理が増えただけで、特に難しいところはないでしょう。gimp-image-undo-group-startとgimp-image-undo-group-endは、この間に行われた処理を、ひとつのアンドゥの単位としてまとめる関数です。複数の処理を順次実行する場合は、処理全体をこれで囲っておきましょう。こうすることで、一度のアンドゥでスクリプトの処理すべてを一気に取り消すことができるようになります。

(define (script-fu-brightness-contrast inImage inLayer inBrightness inContrast inScale inWidth inHeight inUnsharp inRadius inAmount inThreshold)
(gimp-image-undo-group-start inImage)
(gimp-brightness-contrast inLayer inBrightness inContrast)
(if (= inScale TRUE) (gimp-image-scale inImage inWidth inHeight))
(if (= inUnsharp TRUE) (plug-in-unsharp-mask RUN-NONINTERACTIVE inImage inLayer inRadius inAmount inThreshold))
(gimp-image-undo-group-end inImage)
(gimp-displays-flush))

スクリプトを再読み込みして実行するとこんな感じになります。

もっと複雑な処理を実行する

ここまでできれば、実行したい処理を増やしていくだけで、どんな作業も自動化できそうですね。例として、画像をミニチュア写真風に加工するスクリプトを作ってみました。手順としては

  1. 画像の中央以外をボカす ((この方法だと、ボカす部分とボカさない部分の境界がはっきりしすぎるため、画像によってものすごく不自然な仕上りになってしまいます。手作業ならクイックマスクとグラデーションツールを使ってなめらかにボカすのですが、実装方法がわからなかったのでこんなことになっています。))
  2. 新しいレイヤーをオーバーレイモードで作成して、透明色で塗り潰す
  3. 新しいレイヤーの四隅を黒く塗り潰した上でボカす
  4. 画像の明るさとコントラストを上げる
  5. 画像の彩度を上げる
  6. トーンカーブをS字に微調整する

です。具体的なコードを以下に示します。

(define (script-fu-diorama inImage inLayer)
(gimp-image-undo-group-start inImage)
(let* (
(width (car (gimp-image-width inImage)))
(height (car (gimp-image-height inImage)))
(layer (car (gimp-layer-new inImage width height RGBA-IMAGE "layer1" 100 OVERLAY-MODE))))
(gimp-image-select-rectangle inImage CHANNEL-OP-ADD 0 (* (/ height 12) 5) width (* (/ height 12) 2))
(gimp-selection-invert inImage)
(plug-in-gauss-rle2 RUN-NONINTERACTIVE inImage inLayer 8.0 8.0)
(gimp-selection-none inImage)

(gimp-drawable-fill layer TRANS-IMAGE-FILL)
(gimp-image-add-layer inImage layer -1)
(gimp-image-select-ellipse inImage CHANNEL-OP-ADD 0 0 width height)
(gimp-selection-invert inImage)
(gimp-context-set-foreground ‘(0 0 0))
(gimp-edit-fill layer FOREGROUND-FILL)
(gimp-selection-none inImage)
(plug-in-gauss-rle2 RUN-NONINTERACTIVE inImage layer 200.0 200.0)
(gimp-brightness-contrast inLayer 20 30)
(gimp-hue-saturation inLayer ALL-HUES 0 0 25)
(gimp-curves-spline inLayer HISTOGRAM-VALUE 8 #(0 0 65 55 160 170 255 255)))
(gimp-image-undo-group-end inImage)
(gimp-displays-flush))
(script-fu-register
“script-fu-diorama”
“/Filters/Diorama”
“Creates a miniature style effect.”
“Hajime Mizuno”
“copyright 2015, Hajime Mizuno”
“June 10, 2015”
“”
SF-IMAGE “Image” 0
SF-DRAWABLE “Drawable” 0
)

実行してみると、この画像が

こんな感じに加工されます!

その他にも

この例では開いている画像に対して処理を行いましたが、「あるディレクトリの中にあるすべてのファイルに処理を適用するスクリプト」なども作れます ((このような、画像に依存しないスクリプトを独立系スクリプトと呼びます。)) 。連続撮影したすべての画像に同じパラメーターで処理を適用したいような場合、一枚ずつ手でトーンカーブの調整などしていたら今世紀が終わってしまいますよね。そのような時にも、Script-Fuは威力を発揮しそうです。プロシージャーブラウザーを眺めながら、色々な関数を試してみるのも面白いかもしれませんね。

ブログ記事検索

このブログについて

このブログは、札幌市・仙台市の「株式会社インフィニットループ」が運営する技術ブログです。 お仕事で使えるITネタを社員たちが発信します!