#!/usr/bin/env python # -*- coding: utf-8 -*- # -*- mode: python; Encoding: utf8n -*- # Last updated: <2026/03/27 00:10:17 +0900> """ GIMPからG'MIC CLI(gmic.exe)を呼び出して処理ができるか実験。 GIMPのレイヤー内容をベタデータにして、 gmic.exe の標準入力にパイプで送り、 処理結果を標準出力に出させてパイプで受け取り、 新規レイヤーに書き出すPython-fuスクリプト。 外部のexeにフィルタ処理をさせられるかどうかの実験。 by mieki256 * Menu : Filters > Misc > G'MIC CLI Pipe... * Windows11 x64 25H2 + GIMP 2.10.38 Portable * Ver. 0.0.1 2026.03.25 初版。 """ from gimpfu import * import subprocess import os import array # GIMP(Interleaved) -> G'MIC(Planar) def convert_to_planar(interleaved_raw, width, height, bpp): """GIMPのRGBRGB...形式をG'MICのRRR...GGG...形式に変換する""" interleaved_data = array.array("B", interleaved_raw) planar_list = [] for c in range(bpp): # 各チャンネルのデータを抽出 (スライス [開始:終了:ステップ] を利用) planar_list.append(interleaved_data[c::bpp]) # 全チャンネルのデータを結合してバイナリ文字列で返す return "".join([c_data.tostring() for c_data in planar_list]) # G'MIC(Planar) -> GIMP(Interleaved) def convert_to_interleaved(planar_raw, width, height, bpp): """G'MICのRRR...GGG...形式をGIMPのRGBRGB...形式に復元する""" num_pixels = width * height received_planar = array.array("B", planar_raw) # 空の配列を用意して、チャンネルごとに値を埋め戻す output_interleaved = array.array("B", [0] * (num_pixels * bpp)) for c in range(bpp): # c番目のチャンネルデータを抽出 channel_data = received_planar[c * num_pixels: (c + 1) * num_pixels] # インターリーブの位置 [c, c+bpp, c+2bpp...] に代入 output_interleaved[c::bpp] = channel_data return output_interleaved.tostring() def python_fu_gmic_cli_pipe(image, drawable, gmic_dir, gmic_command): """メイン処理""" # G'MIC CLIの場所を一時的に環境変数PATHの先頭に追加する if gmic_dir and os.path.isdir(gmic_dir): current_path = os.environ.get("PATH", "") os.environ["PATH"] = gmic_dir + os.pathsep + current_path else: gimp.message("Invalid directory.") return # ユーザー入力をスペースで分割してリスト化 user_cmds = gmic_command.split() if gmic_command else ["blur", "2"] image.undo_group_start() # undoできるようにしておく # レイヤーの横幅、縦幅、チャンネル数を取得 width = drawable.width height = drawable.height bpp = drawable.bpp # RGBなら3, RGBAなら4 # GIMP(Interleaved) -> Planar 変換 gimp.progress_init("Preparing Planar data...") pr = drawable.get_pixel_rgn(0, 0, width, height, False, False) planar_data = convert_to_planar(pr[0:width, 0:height], width, height, bpp) # G'MICの引数構築 # 入力: -.raw,uint8,幅,高さ,1,チャンネル数 (G'MICのraw指定は w,h,d,s) # 出力: -.raw,uint8 # "-" は標準入力/標準出力を示す gmic_exe = "gmic.exe" if os.name == "nt" else "gmic" cmd = [ gmic_exe, "i", "-.raw,uint8,{w},{h},1,{c}".format(w=width, h=height, c=bpp), ] cmd.extend(user_cmds) # ユーザーが入力したコマンドを挿入 cmd.extend(["cut", "0,255", "o", "-.raw,uint8"]) # 出力設定を追加 # パイプの構築 try: proc = subprocess.Popen( cmd, stdin=subprocess.PIPE, stdout=subprocess.PIPE, stderr=subprocess.PIPE, env=os.environ, shell=(os.name == "nt"), ) # パイプへ送信してgmic側で受信 pdb.gimp_progress_set_text("G'MIC processing...") stdout_data, stderr_data = proc.communicate(input=planar_data) if proc.returncode != 0: gimp.message("G'MIC Error: " + stderr_data) return # Planar -> GIMP(Interleaved) 復元 pdb.gimp_progress_set_text("Restoring Interleaved data...") interleaved_bytes = convert_to_interleaved(stdout_data, width, height, bpp) # レイヤーへの書き出し new_layer = gimp.Layer( image, "G'MIC Output", width, height, drawable.type, 100, NORMAL_MODE ) image.add_layer(new_layer, 0) pr_new = new_layer.get_pixel_rgn(0, 0, width, height, True, True) pr_new[0:width, 0:height] = interleaved_bytes new_layer.flush() new_layer.merge_shadow(True) new_layer.update(0, 0, width, height) except Exception as e: gimp.message("Error: " + str(e)) image.undo_group_end() gimp.displays_flush() # 画像の表示を更新 return # ---------------------------------------- # GIMPへのメニュー登録、ダイアログの指定 register( "python-fu-gmic-cli-pipe", # プロシジャの名前 "Process layer with G'MIC CLI via pipe", # プロシジャの説明文 "Sends raw pixel data to gmic.exe and receives processed data.", # PDBに登録する追加情報 "mieki256", # 作者名 "mieki256", # Copyright "2026.03.25", # 作成日 "G'MIC CLI Pipe...", # メニュー名 "RGB*", # 対応する画像タイプ # ダイアログの指定 [ # (型, 変数名, 説明文, デフォルト値) (PF_IMAGE, "image", "Input image", None), (PF_DRAWABLE, "drawable", "Input drawable", None), (PF_DIRNAME, "gmic_dir", "G'MIC CLI location", "D:\\home2\\bin\\gmic_dir\\"), (PF_STRING, "gmic_command", "G'MIC Command", "blur 10"), ], [], # 戻り値の定義 python_fu_gmic_cli_pipe, # 処理を埋け持つ関数名 menu="/Filters/Misc", # メニューの登録場所 ) main() # プラグインを駆動させるための関数