省型旧形国電の残影を求めて

戦前型旧形国電および鉄道と変褪色フィルム写真を中心とした写真補正編集の話題を扱います。他のサイトでは得られない、筆者独自開発の写真補正ツールや補正技法についても情報提供しています。写真補正技法への質問はコメント欄へどうぞ

マスク画像に一挙にガンマ補正を掛けるプラグイン - GIMPを便利に! プロジェクト

f:id:yasuo_ssi:20210309093756j:plain

 久しぶりに、「GIMPを便利に! プロジェクト」の自作プラグインを追加します。先日指摘したように、リニアワークフローを採用したGIMP2.10.x では、レイヤーマスクの動きがどうやらGIMP2.8以前と異なるようです。

 ただ、マスクの挙動がはっきり解明できていないので、何とも言えないのですが、ひょっとすると画像処理エンジンにGEGL採用以降はマスク画像が一旦sRGBガンマデコードされてから不透明度に転換されているのではないかという疑問がわいています。

 もちろん、明度や色調の補正の場合、一旦ガンマデコードすることは理にかなっていると思いますが、マスク画像の場合不透明度に転換されます。この場合、仮にガンマデコードされているとしたら、ガンマデコードされると却って不都合な場合もあるのでは(→つまり、中域を中心に、補正レイヤーの効果が弱まる)ないかと考えました。

 そこで、その対策としてはマスク画像をsRGB TRCの近似値である、2.2(もしくは4.4)でガンマ補正をしてみる、というのもありではないかと思いました。ただ、マスクがかかったレイヤーが複数あると、いちいちマスク編集画面に入ってレベル補正でガンマ値をいじっていると面倒です。そこで、一挙に複数のマスクのガンマ補正を行うGIMPプラグインを書いてみました。さらにマスクを一挙に反転するオプションも付けました。

 ガンマ補正値を1より上げると、マスク自体の透過度(明るさ)が上がり、マスクを掛けた補正レイヤー自体の不透過度が上がります(補正レイヤーの効果は上昇)。逆に、ガンマ補正値を1より下げると、マスク自体の透過度(明るさ)が下がり、マスクを掛けた補正レイヤー自体の不透過度も下がります(補正レイヤーの効果は下降)。

 補正レイヤーの不透過度をGIMP上のレイヤーダイアログで調整することで補正レイヤーの効果を調節できますが、しかしこれは補正レイヤーの効果を下げる方向しか調節できません。本ツールを使うと、補正レイヤーの効果を上げる方向にも調節できます(但し、レイヤーにマスクを掛けなければいけないという前提です。当然マスクを掛ける必要がないなら、掛けないのが補正レイヤーの効果を最大に発揮します→補正レイヤーで下のレイヤーを塗りつぶしてしまうのですから)

 

 コード自体は短いのですが、Pythonとはいえ、GIMP用のプラグインを1年ぐらい書いていないので、勘が鈍り結構時間がかかってしまいました。結局 Pythonそれ自体より、API 理解が重要です。そのあたりどういう構成になっていたのかを思い出すところから始めなければなりませんでしたので...

 プラグインファイルはこちらからダウンロードをお願いします。

このファイルをダウンロードしたら、そのファイルを解凍してGimpプラグインフォルダにおきます。Windowsの場合は通常、

C:\Program Files\GIMP 2\lib\gimp\2.0\plug-ins (全ユーザに対し有効)

もしくは、

C:\Users\%USERNAME%\AppData\Roaming\GIMP\2.10\plug-ins (個別ユーザに対し有効)

Linuxの場合は、

各ユーザのホームディレクトリの下の、

.config/GIMP/2.10/plug-ins (個別ユーザに対し有効)

になります。なお、Linuxの場合、インストールの方法によって、全ユーザに対し有効なプラグインフォルダの位置が異なりますので、省略します。

 

 なお、プラグインフォルダに本ファイルを置くと、メニュー上の位置は、レイヤーの一番下の方になります。

メニュー表示位置

 

 因みに実行前のレイヤーとマスクは下記です。事例として分かりやすいように、マスクに明度一律50%の画像を貼り付けてあります。

実行前のレイヤーとマスク

 メニューをクリックするとガンマ補正値の選択と、反転の指定ダイアログが出るので、1/2.2, 1/1.1, 1.0, 1.1, 2.2 か 4.4のいずれかを選んでください。1.0以下の値を選ぶとマスク画像が暗くなり(補正レイヤーの透明度が高まり効果が弱まる)、1.0を超える値を選ぶとマスク画像が明るくなり、補正レイヤーの不透明度が高まって効果が強まります。なお、1.0は一切ガンマ補正を行いません。反転のみ行いたいときに選びます。

プラグインのパラメータ設定ダイアログ

 すると下記のように実行されます。適用されたマスクは、明るく補正されているのが分かると思います。なお、レイヤーを不可視に設定していると、そのレイヤーのマスクの補正はスキップします。適用を除外したいマスクがある場合、そのマスクのあるレイヤーを不可視にしておいてください

プラグイン実行中

 なお、現在のバージョンでは、レイヤーグループの下のレイヤーにあるマスクには対応していません。いずれ時間を見てバージョンアップしたいと思います。

 なお、既に述べたように、補正レイヤーの効果が弱まること自体は必ずしも悪くありません。例えば補正レイヤーを重ねることで強めることもできますし、より細かい調整を可能にします。また今までもそれなりの補正効果が得られていました。このプラグインを使うべきかどうかは、個々の画像の編集状況に応じてご判断ください。

 

 GIMP上でのPythonプラグインの開発が難しいことを鑑みて、他のプログラマーの方の参考までにソースコードを注釈付きで掲載します。

--------------------

#!/usr/bin/env python
# -*- coding: utf-8 -*-

# ライブラリの読込
from gimpfu import *
import os, glob

# プログラムの本体
def mask_gamma_encoding(image, option):
    # option: ガンマ値の選択肢: 0 > 2.2, 1>4.4
    pdb.gimp_message_get_handler(ERROR_CONSOLE)
        # gimp.messageの出力先をエラーコンソールに設定
    # gimp.message(str(option))
    if option == 0:
        value = 1 / 2.2
    elif option == 1:
        value = 1 / 1.1
    elif option == 3:
        value = 1.1
    elif option == 4:
        value = 2.2
    elif option == 5:
        value = 4.4
    else:
        value = 1.0
        # 以上オプション値に応じてガンマ補正値valueを設定
    pdb.gimp_image_set_active_layer(image, image.layers[0])
        # imageオブジェクトの一番上のレイヤーをアクティブに
    pdb.gimp_image_undo_group_start(image)
        # Ctrl+ zで取消す undo範囲をここから設定
    for i in range(len(image.layers)):
        # レイヤー数の分、以下の処理を反復
        pdb.gimp_image_set_active_layer(image, image.layers[i])
            # i 番目のレイヤーをアクティブに
        mask = pdb.gimp_layer_get_mask(pdb.gimp_image_get_active_layer(image))
            # アクティブレイヤーにマスクがあるかどうか確認
        if mask != None:
            # アクティブレイヤーにマスクがある場合以下を実行
            #注意! プロシジャーブラウザで -1 とされている部分は、Python上では、None (" "はつけない) に変更する*1
            try:
                # 以下エラーが出たら exceptへ飛ぶ
                pdb.gimp_layer_set_edit_mask(pdb.gimp_image_get_active_layer(image), FALSE)
                     # 一旦アクティブレイヤーのマスクの編集モードを解除
               pdb.gimp_layer_set_edit_mask(pdb.gimp_image_get_active_layer(image), TRUE)
                    # アクティブレイヤーのマスクを編集モードを有効に
                pdb.gimp_drawable_levels(pdb.gimp_image_get_active_drawable(image) ,0 ,0,1 ,FALSE ,value ,0 ,1 ,FALSE)
                    # value値でマスクのガンマ値を補正
                if invert == True:
                    pdb.gimp_drawable_invert(pdb.gimp_image_get_active_drawable(image) ,FALSE)
                    # もし反転指定があれば反転
            except Exception, error:
                print error
                    # エラーが出たら error値を出力して次のループへ
    pdb.gimp_image_undo_group_end(image)
        # undo 範囲指定の終了
    return
# 以下 register 指定データ
register(
    "mask_gamma_encoding",
        # プログラム本体の関数名指定 (def 以下)
    "Mask gamma encoding",
        # プログラムのタイトル
    "Mask gamma encoding",
        # プログラムの説明
    "Ohnishi, Yasuo",
        # 作者名
    "Ohnishi, Yasuo",
        # 著作権者名
    "2022 Ver 0.20",
        # 作成年
    "マスクにガンマ補正適用...",
        # メニュー表示
    "*",      # Alternately use RGB, RGB*, GRAY*, INDEXED etc.
        # プラグインが有効な時の条件設定 (ファイル形式等)
    [
        (PF_IMAGE, "image", "input image", None),
        # 現在アクティブなイメージオブジェクト読込の設定
        (PF_OPTION, "option", "ガンマ補正値", 2, ("1/2.2", "1/1.1", "1.0", "1.1", "2.2", "4.4"))
        # オプションダイアログの表示設定
        (PF_BOOL,   "invert", "反転", False),
        # マスクの反転指定
    ],
    [],
    mask_gamma_encoding, menu="<Image>/Layer/" )
        # プログラム名とそのメニュー上の表示位置の指定
main()

-------------------

 プログラムを書くにあたって、どのようにマスクを取得するか、アクティブにするかにちょっと困りました。imageオブジェクトには、layersというプロパティはあるようですが、drawables というプロパティはないようでした。さらに、gimp_image_set_active_layer というAPIはありますが、gimp_image_set_active_drawable というAPIもありません。

 結局、image.layers でレイヤー数を取得し、forループで1枚1枚のレイヤーに対し反復処理で、マスクがあるかどうか確認、あれば、pdb.gimp_layer_set_edit_mask で、そのレイヤーのマスクを編集モードにしてアクティブにすることにして、さらにアクティブなdrawable (つまり今アクティブであるマスク) に対して、 pdb.gimp_drawable_levels を実行することでマスクに対するガンマ補正を掛けることにしました。

 なお、

pdb.gimp_layer_set_edit_mask(pdb.gimp_image_get_active_layer(image), TRUE)

で、レイヤーマスクの編集モードをオンにする直前に、

pdb.gimp_layer_set_edit_mask(pdb.gimp_image_get_active_layer(image), FALSE)

というコマンドを入れています。なぜこんな馬鹿なことをやるのかとお思いの方もいらしゃるかと思います。

 実は、このプログラムを実行してみたところ、なぜか正しく動いたり、動かなかったりしました。結局その理由を探ってみると、ファイルの中に、一つのレイヤーでもマスク編集モードがオンのままになっていると、なぜか実行がスキップされてしまうのです(しかもPythonインタープリタのエラーメッセージは出ないので、正確な理由が分かりません)。そこで、各レイヤー毎に処理する際に、一旦レイヤー編集モードを解除して、それからレイヤー編集モードをオンにし直すと、うまく実行されるようになりました。GIMPの場合、実際に動かしてみると、このような予想外の動きをすることが結構多いので、頭の中で考えているだけでは、うまくプラグインを書くことができません。この辺りがGIMPにおけるPythonプログラミングの難しさかと思います。

 なお、Windows上でデバッグを行う場合、

pdb.gimp_message_get_handler(ERROR_CONSOLE)

を使うと、

gimp.message の出力先がエラーコンソールになり、中間過程を確認することができます。

*1:以下の拙稿参照。

yasuo-ssi.hatenablog.com