GIMPでPython-Fuを使った画像処理③

画像処理シリーズ

GIMPでPython-Fuを使った画像処理① - かすブログ

GIMPでPython-Fuを使った画像処理② - かすブログ

前回のコードから、実際の写真に使えるようにプラグインをアップデート。

今回のフィルタを適用した画像

目次

スクリプト呼び出し時に設定値を変更出来るようにする

設定値をGIMPスクリプト呼び出し画面から調整できるように変更する。 呼び出し画面を実装するような手間は必要なく、スクリプト処理のメソッドを登録する、register関数に引数情報を入力すると 自動的に設定画面が作成された。 ソースコードのprmsの配列要素を追加する。

####################
# スクリプト登録
####################
# メニューバーの My にcross_filterを追加(画像を開くまで実行不可)

prms = [
    (PF_IMAGE, "image",         "Input image", None),
    (PF_INT8,  "kobo_num",      "[filter setting] Kobo Num (half)", 2),
    (PF_INT8,  "length1",       "[filter setting] Motion Blur Length 1", 5),
    (PF_INT8,  "length2",       "[filter setting] Motion Blur Length 2", 10),
    (PF_INT8,  "angle_offset",  "[filter setting] Motion Blur angle offset", 45),
    (PF_INT8,  "clip_color",    "[pre processing] under the value is clip to 0", 230),
    (PF_INT8,  "strength",      "[post processing] cross value alpha blend strength", 200),
    (PF_BOOL,  "merge_off",     "[check] cross Merge OFF Mode", False)
    ]
register(
    "cross_filter",             # name
    "my cross filter",          # blurb (宣伝文句),
    "help: this is my filter",  # help
    "skattun",                  # author
    "(C) 2023 skattun",         # copyright
    "2023/5/5",                 # date
    "cross_filter",             # menupath(not use)
    "RGB*",                     # imagetypes
    prms,                       # params
    [],                         # results
    plugin_main,                # function
    menu = "<Image>/My")        # menu path設定

モーションぼかし前に一定輝度以下の画素を削除する処理を追加

前回の処理のように、自分で指定した高輝度画素だけモーションぼかしする場合は不要だったが、 通常の画像を処理する場合はモーションぼかしをかける必要のない画素はalphaにしておく必要があった。 そのため、一定輝度(clip_color)以下の画素をalphaにする処理を追加する。 crosss_filterのメソッドを前処理・フィルタ本体・後処理ブロックに分けた。

def cross_filter(image, layer, kobo_num, length1, length2, angle_offset, clip_color, strength, merge_off):

    try:
        # undoグループ開始
        pdb.gimp_image_undo_group_start(image)

        ##########
        # 前処理
        ##########

        # レイヤーをコピー
        work_layer = pdb.gimp_layer_copy(layer, 1)  # alphaチャンネルを追加
        pdb.gimp_image_insert_layer(image, work_layer, None, 0)

        # トーンカーブ でclip_color値を0.0に変換
        clip_color_d = clip_color / 255.
        points = spline_to_points([0.0, 0.0, clip_color_d, 0.0])
        print(points)
        pdb.gimp_drawable_curves_explicit(work_layer, HISTOGRAM_VALUE, 256, points)

        # 色を透明度に で黒をalphaに変換
        color = (0.0, 0.0, 0.0)
        pdb.plug_in_colortoalpha(image, work_layer, color)

        ##########
        # main
        ##########
        # クロスフィルタの実体
        cross_filter_core(image, work_layer, kobo_num, length1, length2, angle_offset, strength, merge_off)

        ##########
        # 後処理
        ##########

        # レイヤーを削除
        if not merge_off:
            image.remove_layer(work_layer)

    finally:
        # undoグループ終了
        pdb.gimp_image_undo_group_end(image)

        # 処理終了
        pdb.gimp_progress_end()
トーンカーブで一定輝度(clip_color)以下の値を0にクリップする

まずは不要な画素を0にクリップする。

       clip_color_d = clip_color / 255.
        points = spline_to_points([0.0, 0.0, clip_color_d, 0.0])
        print(points)
        pdb.gimp_drawable_curves_explicit(work_layer, HISTOGRAM_VALUE, 256, points)

前回使ったspline_to_points()メソッドを再利用。(x,y)の順でポイントを引数で与える。 x(input): clip_color_d -> y(output): 0.0 という設定。 トーンカーブVALUEで指定する。名前的にはHISTOGRAM_LUMINANCEがよさそうだが、こちらはRuntimeErrorとなって実行できなかった。

0をalphaに変換

先ほど不要な画素を0にしたので、0を画素値指定でalphaに変換する

        # 色を透明度に で黒をalphaに変換
        color = (0.0, 0.0, 0.0)
        pdb.plug_in_colortoalpha(image, work_layer, color)

ソースコード

以下がプラグインソースコード全体

# -*- coding: utf-8 -*-

####################
# import
####################
# gimpfu用
from gimpfu import *

# ファイル入出力用
import sys

####################
# parameters
####################

# --verboseなら%USER_PROFILE%のパス、
# 通常起動なら.xcfファイルがあるパスに出力される
OUTTXT_PATH = "stdout.txt"
PW = 10
PH = 10

####################
# method
####################


# spline曲線を256点のポイントにして返すメソッド
def spline_to_points(spline):
    points = [ k/255.0 for k in range(256) ]
    x0 = 0.0
    y0 = 0.0
    ix = 0
    x0 = spline.pop(0) # remove initial (0.0,0.0)
    y0 = spline.pop(0)
    while spline:
        x = spline.pop(0)
        y = spline.pop(0)
        while ix < 256:
            xi = ix / 255.
            if xi > x:
                break
            points[ix] = ((x-xi) * y0 + (xi-x0) * y) / (x-x0)
            ix += 1

        x0 = x
        y0 = y
    return points


def cross_filter_core(image, layer, kobo_num, length1, length2, angle_offset, strength, merge_off):

    kobo = kobo_num # 光芒の数の半分、光芒は偶数のみ可能

    mblur_type = 0      # リニアモーション
    mblur_cx = 0        # center x
    mblur_cy = 0        # center y
    # gauss_h = 3         # gauss h
    # gauss_v = 3         # gauss v

    # レイヤーグループを追加
    layer_group = pdb.gimp_layer_group_new(image)
    pdb.gimp_image_insert_layer(image, layer_group, None, 0)

    angle = angle_offset
    angle_dx = 360 / (kobo * 2)
    for n in range(kobo):

        # レイヤーをコピー
        work_layer = pdb.gimp_layer_copy(layer, 1)  # alphaチャンネルを追加
        pdb.gimp_image_insert_layer(image, work_layer, layer_group, 0) # レイヤーグループに追加, positon:0 (top)

        # 角度を更新
        angle += angle_dx

        # ガウスフィルタIIR処理実行
        # pdb.plug_in_gauss(image, work_layer, gauss_h, gauss_v, 0) # 0: IIR

        # モーションぼかし処理実行
        pdb.plug_in_mblur(image, work_layer, mblur_type, length1, angle, mblur_cx, mblur_cy)
        pdb.plug_in_mblur(image, work_layer, mblur_type, length2, angle, mblur_cx, mblur_cy)

        # アルファチャンネル調整
        strength_d = strength / 255.
        points = spline_to_points([0.0, 0.0, 0.2, strength_d])
        pdb.gimp_drawable_curves_explicit(work_layer, HISTOGRAM_ALPHA, 256, points)

    # レイヤーのマージ
    if not merge_off:
        dst_layer = pdb.gimp_image_merge_layer_group(image, layer_group)
        dst_layer.name = "cross_filter"

def cross_filter(image, layer, kobo_num, length1, length2, angle_offset, clip_color, strength, merge_off):

    try:
        # undoグループ開始
        pdb.gimp_image_undo_group_start(image)

        ##########
        # 前処理
        ##########

        # レイヤーをコピー
        work_layer = pdb.gimp_layer_copy(layer, 1)  # alphaチャンネルを追加
        pdb.gimp_image_insert_layer(image, work_layer, None, 0)

        # トーンカーブ でclip_color値を1.0に変換
        clip_color_d = clip_color / 255.
        points = spline_to_points([0.0, 0.0, clip_color_d, 0.0])
        print(points)
        pdb.gimp_drawable_curves_explicit(work_layer, HISTOGRAM_VALUE, 256, points)

        # 色を透明度に で黒をalphaに変換
        color = (0.0, 0.0, 0.0)
        pdb.plug_in_colortoalpha(image, work_layer, color)

        ##########
        # main
        ##########
        # クロスフィルタの実体
        cross_filter_core(image, work_layer, kobo_num, length1, length2, angle_offset, strength, merge_off)

        ##########
        # 後処理
        ##########

        # レイヤーを削除
        if not merge_off:
            image.remove_layer(work_layer)

    finally:
        # undoグループ終了
        pdb.gimp_image_undo_group_end(image)

        # 処理終了
        pdb.gimp_progress_end()


# 引数はregisterで登録するときに指定する。
def plugin_main(image, kobo_num, length1, length2, angle_offset, clip_color, strength, merge_off):

    layer = pdb.gimp_image_get_active_layer(image)

    # cross_filter実行
    cross_filter(image, layer, kobo_num, length1, length2, angle_offset, clip_color, strength, merge_off)



####################
# スクリプト登録
####################
# メニューバーの My にcross_filterを追加(画像を開くまで実行不可)

prms = [
    (PF_IMAGE, "image",         "Input image", None),
    (PF_INT8,  "kobo_num",      "[filter setting] Kobo Num (half)", 2),
    (PF_INT8,  "length1",       "[filter setting] Motion Blur Length 1", 5),
    (PF_INT8,  "length2",       "[filter setting] Motion Blur Length 2", 10),
    (PF_INT8,  "angle_offset",  "[filter setting] Motion Blur angle offset", 45),
    (PF_INT8,  "clip_color",    "[pre processing] under the value is clip to 0", 230),
    (PF_INT8,  "strength",      "[post processing] cross value alpha blend strength", 200),
    (PF_BOOL,  "merge_off",     "[check] cross Merge OFF Mode", False)
    ]
register(
    "cross_filter",             # name
    "my cross filter",          # blurb (宣伝文句),
    "help: this is my filter",  # help
    "skattun",                  # author
    "(C) 2023 skattun",         # copyright
    "2023/5/5",                 # date
    "cross_filter",             # menupath(not use)
    "RGB*",                     # imagetypes
    prms,                       # params
    [],                         # results
    plugin_main,                # function
    menu = "<Image>/My")        # menu path設定

####################
# main
####################
main()

####################
# 100m pic filter setting memo
# length1: 80
# length2: 100
# angle: 45
# clip: 230
# strength: 80

####################

フィルタ実行結果と考察

元画像 / フィルタ適用後

strengthパラメタでalphaブレンド調整できるようにした。 多少弱めても大量のクロスがつく。多すぎる。もちろんフィルタ用の元画像を手動で修正すれば調整できるが、 スクリプトに何かしら対策を追加したいところ。 また、光学的なクロスフィルタで撮った写真と比べるとデジタル処理感が出てしまっている。

光学的なクロスフィルタで撮った写真

手動でブラシで黒塗りをして光源を間引いてフィルタ適用してみた画像がこちら。

元画像 / フィルタ用編集画像 / フィルタ適用後

光線をつけたいところだけ簡単につけれるのはデジタル処理の利点かも。

今日でGWが終わるので、次回以降改善したい内容をメモ。

  • クロスが密集しすぎるので、間引きたい。また太めの光源にもクロスがついていて太くなるのを細くしたい、もっと先細りしたクロスにしたい。
    • 前処理にハイパスフィルタを入れてみる?
    • ランダムで光源をマスクする処理を追加?
  • 光線が単色になってしまう。色むらを付けれないか?
    • クリップするときにLEVELだけでなくR/G/B chそれぞれの値でクリップしたレイヤを作成してフィルタ処理してランダムに合成?