GIMPを画像データ処理ツールとして使いこなす(その1) -プラグイン作成の備忘録-

前回[ その0 ](https://qiita.com/mCvA/items/5deff37e1a4b33601b70) の続き

GIMPでPythonを使ってスクリプトを書くためのアレコレをまとめていくシリーズです。

まずはテンプレを貼ります。基本的にこのテンプレを適切に変えていけばGIMPのスクリプトになるはずです。
ただいくつか注意点があるので、今日はこれを使ってスクリプトファイルの構成を解説していきます。

```python:templatePython-fu.py
#!/usr/bin/env python
# -*- coding: utf-8 -*-

from gimpfu import *

def plugin_main(image, layer):# 関数の設定 変数にimageとlayerは必須
    #関数の処理 何かしらの処理がないとエラーになって登録されない。
    gimp.message("Hello GIMP")
    
register(
        "python_fu_sample", #プラグイン名
        "blurb: 推薦文", #簡単な説明
        "help: もう少し詳しい説明", #詳細な説明
        "author", #作者
        "copyright", #ライセンス
        "2011/11/11", #ライセンスの日付
        "<Image>/sample", # メニュー項目　アンダーバーは消える。
        "*", # imagetypes: "RGB*, GRAY*" など
        [ # 引数  (type, name, description, default [, extra])　初期値の無しは None と指定する。空欄は不可
            # (PF_INT8, "変数名", "説明", "初期値"),
            # (PF_INT16, "変数名", "説明", "初期値"),
            # (PF_INT32, "変数名", "説明", "初期値"),
            # (PF_INT, "変数名", "説明", "初期値"),
            # (PF_FLOAT, "変数名", "説明", "初期値"),
            # (PF_STRING, "変数名", "説明", "初期値"),
            # (PF_VALUE, "変数名", "説明", "初期値"),
            # (PF_COLOR, "変数名", "説明", "初期値"),
            # (PF_COLOUR, "変数名", "説明", "初期値"),
            # (PF_REGION, "変数名", "説明", "初期値"),
            # (PF_IMAGE, "変数名", "説明", "初期値"),  #通常は自動で取得するので設定不要
            # (PF_LAYER, "変数名", "説明", "初期値"),
            # (PF_CHANNEL, "変数名", "説明", "初期値"),
            # (PF_DRAWABLE, "変数名", "説明", "初期値"), #通常は自動で取得するので設定不要
            # (PF_TOGGLE, "変数名", "説明", "初期値"),
            # (PF_BOOL, "変数名", "説明", "初期値"),
            # (PF_RADIO, "変数名", "説明", "初期値"),
            # (PF_SLIDER, "変数名", "説明", "初期値"),
            # (PF_SPINNER, "変数名", "説明", "初期値"),
            # (PF_ADJUSTMENT, "変数名", "説明", "初期値"),
            # (PF_FONT, "変数名", "説明", "初期値"),
            # (PF_FILE, "変数名", "説明", "初期値"), # 初期値が/で終わるとディレクトリを指定する
            # (PF_BRUSH, "変数名", "説明", "初期値"),
            # (PF_PATTERN, "変数名", "説明", "初期値"),
            # (PF_GRADIENT, "変数名", "説明", "初期値"),
            # (PF_PALETTE, "変数名", "説明", "初期値") # パレット名をstring型で返す 
            
        ],
        [], # 戻り値
        plugin_main) # 関数名

main()

'''
内容に文法エラーがあるとメニューに登録できない。
plugin_mainの内容のチェックはコンソロールから直接実行することで確認が可能。
register関数の引数をすべてコメントアウトしてみてメニューに登録できれば、
plugin_mainの内容に文法エラーはないと言える。（思い通りに動くとは言っていない）
引数を指定する各タプルにはそれぞれ文法がある模様…（現在確認中）
register関数の中の引数とplugin_main()の順番を揃えておかないとエラーになる場合がある。
Linux、Macの場合は実行権限を与えておくこと
'''
```

## GIMPスクリプトの基本構成
GIMPではpython2.7を使ったスクリプトファイルのことを**Python-fu**と言うようです。
これは普通のpythonの".py"形式のファイルなのですが、書式と言うか構成が決まっています。必須なものがなかったりするとスクリプトとしてメニュー登録できません。

1. ヘッダー:冒頭のコメント2行のこと。無いとスクリプトとして認識されないことがあるのでおまじない的にも必須。
2. import: ``` from gimpfu import * ``` は必須です。他必要な``` import os``` とかはpython標準のものはimportして使えます。ただしtkinterなど使えない標準ライブラリもある。
3. ``` def plugin_main(image, layer): ``` :スクリプト本体。image,layerは引数として必須な模様。他に必要な引数は追加できるが、後述のregister関数との連携が必須。連携が崩れているとダメ。
4. register関数: ある意味最大の難所がココ。register関数の引数の内容を使ってGIMPがスクリプトを立ち上げた時のダイアログやメニューの登録、ヘルプや概要の紹介をします。一つの関数に盛り込み過ぎで把握するのが大変だった。
5. main(): おそらく実行時に使われる。：変更する必要はないけれど必須。

ヘッダーとmain()は解説不要かと思いますので2のimportのところから解説していきます。
## GIMP上のpythonコンソロール
GIMPのメニューから
　*フィルター > Python-fu > コンソロール*
と辿っていくとpython2.7のコンソロールが立ち上がります。スクリプトを書く前の検証やデバックにも使えます。

## import できるもの出来ないもの。
基本的にはpyhton2.7で使える標準ライブラリはimportできます。ただし、os環境によって少し挙動が違うので現状把握はしきれていません。
よく使われるライブラリを列挙してみます。
### 可
* ファイル操作や時刻関係 ：os, sys, time, datetime
* 特定のファイル形式を扱うもの：csv, json, xml
* 数学や乱数: math, random
* URL関連？: urllib (使ったこと無いのでよく分からない。importは出来た)

※標準じゃないけれどなぜかimport出来たのが ```numpy```。ちなみにwindows版では出来なかった気がする。未検証。

### 不可
* PIL : 画像関係のライブラリ。　画像関係は全てGIMPの機能を使いましょう、ということか。
* tkinter ：GUIのライブラリ。　ダイアログとか気軽に使えてよかったのだけれどGIMPでは使えない。register関数で全てやる感じになる。

### 何もしてなくても既にimportされているライブラリ
* gimp ： GIMPの本体？オブジェクト指向的に書くにはこちらのライブラリを使うのが書きやすい。次回以降で解説予定。
* pdb ：　GIMPの機能をまとめたプロシージャデーターベース。ここにGIMPの全てが詰まっている。次回以降で解説予定。

## スクリプト本体　（plugin_main）
ここにスクリプトの本体を書き込みます。pythonと使うライブラリの構文に沿って書き込めばOK。外部やスクリプト内に別の関数を作って使うことも出来ます。そこら辺はpythonのルールと同じです。

## register関数
### register関数はなんのため？
* ヘルプに表示する情報を取得する
* メニューのどこに配置するか指示する
* スクリプトを立ち上げたときのダイアログを作って、plugin_mainに引き渡す

※機能や目的が多岐にわたっているので非常に分かりにくい。

### ヘルプ部分
*ヘルプ > プロシージャブラウザ* とメニューを辿っていくとこんな画面が開きます。これは前回、試しで入れたHellow worldのプラグインです。

![プロシージャブラウザ.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/245455/4aeef4d4-2d72-37bd-74c4-471b556155ac.png)

ここに書く内容を記述するregister関数は以下の部分です。

```python
register(
        "python_fu_sample", #プラグイン名
        "blurb: 推薦文", #簡単な説明
        "help: もう少し詳しい説明", #詳細な説明
        "author", #作者
        "copyright", #ライセンス
        "2011/11/11", #ライセンスの日付
```

パラメーターのところは後述するプラグイン本体への引数の部分で記述します。
### メニューの配置場所
```python

        "<Image>/sample", # メニュー項目　アンダーバーは消える。
        "*", # imagetypes: "RGB*, GRAY*" など
```
メニューのどこに作ったプラグインをおくのかを記述します。上の書き方だとメニューバーの一番上にsampleというボタンができてそれをクリックすることでプラグインを呼び出すことができるようになります。

``"<Image>/File/sample"`` と書くと *ファイル > sample*という形でファイルの項目にボタンが作成されることになります。
メニューの階層構造が決まっているようなのですが、情報が少なくてよくわかりません。
ちなみにアンダーバー”_”は使えません。消えてしまいます。

特定のイメージタイプのときだけ使えるようにも出来ます。そのときには二行目の``"*"``の部分を``"RGB"``などと記述します。そうするとRGB以外の画像のときはグレーアウトして使えなくなります。
### 本体への引数指定
```python
        [ # 引数  (type, name, description, default [, extra])　初期値の無しは None と指定する。空欄は不可
            # (PF_INT8, "変数名", "説明", "初期値"),
            # (PF_INT16, "変数名", "説明", "初期値"),
            # (PF_INT32, "変数名", "説明", "初期値"),
            # (PF_INT, "変数名", "説明", "初期値"),
            # (PF_FLOAT, "変数名", "説明", "初期値"),
            # (PF_STRING, "変数名", "説明", "初期値"),
            # (PF_VALUE, "変数名", "説明", "初期値"),
            # (PF_COLOR, "変数名", "説明", "初期値"),
            # (PF_COLOUR, "変数名", "説明", "初期値"),
            # (PF_REGION, "変数名", "説明", "初期値"),
            # (PF_IMAGE, "変数名", "説明", "初期値"),  #通常は自動で取得するので設定不要
            # (PF_LAYER, "変数名", "説明", "初期値"),
            # (PF_CHANNEL, "変数名", "説明", "初期値"),
            # (PF_DRAWABLE, "変数名", "説明", "初期値"), #通常は自動で取得するので設定不要
            # (PF_TOGGLE, "変数名", "説明", "初期値"),
            # (PF_BOOL, "変数名", "説明", "初期値"),
            # (PF_RADIO, "変数名", "説明", "初期値"),
            # (PF_SLIDER, "変数名", "説明", "初期値"),
            # (PF_SPINNER, "変数名", "説明", "初期値"),
            # (PF_ADJUSTMENT, "変数名", "説明", "初期値"),
            # (PF_FONT, "変数名", "説明", "初期値"),
            # (PF_FILE, "変数名", "説明", "初期値"), # 初期値が/で終わるとディレクトリを指定する
            # (PF_BRUSH, "変数名", "説明", "初期値"),
            # (PF_PATTERN, "変数名", "説明", "初期値"),
            # (PF_GRADIENT, "変数名", "説明", "初期値"),
            # (PF_PALETTE, "変数名", "説明", "初期値") # パレット名をstring型で返す 
            
        ],
```
この状態ではここの部分は全てコメントアウトしてますので、空欄のリストと同じです。コメントアウトを外して指定の文字列を入れるとダイアログメニューに反映されます。
例えば

```python
(PF_STRING, "message", "メッセージ", "Hello World"),
```
というタプルをこのリストに入れると立ち上がったダイアログに文字入力欄が現れて初期値として *Hello World* が入力されている、という具合です。そしてそこに入力された値を``message``という名前でplugin_main()に渡します。ただし、ここには落とし穴があって***plugin_main()で引数を指定する順番とリスト内の順番とが揃えておく必要があります***。これがズレていると正しい名前をつけていても引き渡しがうまく行かない時があります。

PF_〜で始まるのはダイアログでのウィジットで数値や文字入力欄(PF_INTやPF_STRINGなど)の他にラジオボタン(PF_RADIO)、スライダー(PF_SLIDER)、ファイルダイアログ(PF_FILE)などがあります。PF_PALETTE、PF_BRUSH、PF_PATTERNなどはそれぞれパレット、ブラシ、塗りつぶしパターンでstring型でplugin_main()に渡します。それぞれをのオブジェクトを使って何かさせたいときはその名前を使ってオブジェクトを呼び出すコードを仕込む必要があります。

##参考リンク
* [5 分で始める GIMP Python-Fu](https://qiita.com/dhomma/items/f33ecd1c4d4c6ece7685)
* [GIMPで画像を一括でレベル補正（Python-Fu）](https://qiita.com/otoshita/items/88f1ac857fd86346db94)
* [Gimp PDB Documentation](http://oldhome.schmorp.de/marc/pdb/index.html) ※英語 ただしプロシージャの内容の記述は貴重