Gimp2

 フリーのグラフィック・レタッチ・ソフト、Gimp2 について気が付いたことをまとめます。

Gimp2についてのランダムメモ

 スクリプト以外の操作上のことなど、ここにメモしておきます。


どこで入手する?

 インターネットでダウンロードできます。
  本家 で入手できます。
 現在の最新版(安定版)は、2.8.14です。


スクリプトについてのランダムメモ

 ここには、gimp2.6で調べたものが書かれています。従って、スクリプトの言語はschemeを対称にしています。新しい版では、スクリプトはschemeだけでなく、Pythonでも書けるのですが、残念ながらここでは触れていません。

スクリプトの置き場所
 メニューから「編集」→「環境設定」で環境設定を選び、ダイアログの左から「フォルダ」を開いて「スクリプト」を選びます。
 右側にスクリプトとしてサーチされるパスが登録されているので、新たに自分のスクリプトを置いておくパスを登録できます。
スクリプト変更を反映する
 Gimp起動中にスクリプト動作検査をする場合のことです。
 Gimpのメニューから「フィルター」→「Script-Fu」→「スクリプトを再読み込み」を選ぶと、スクリプトを読み直してくれます。
 ここで注意が必要なのは、Scriput-Fuコンソールの扱い。再読み込みをしても開いているコンソールには反映してくれません。ですから、スクリプトの再読み込みをする前にコンソールを閉じておくこと。
レジスターしない関数は動くか
 作成した関数を使用するために、関数の登録、メニューへの登録をするようになっています。
 が、しかし、登録しなくてもdefineで定義した関数などは、呼び出し可能です。  ちょっとテストする場合などは、エディタでプログラムを修正して、再読み込みさせた後、コンソールから呼び出してやればいいわけですね。
 また、この方法で処理用のライブラリなども作成できます。
Windows版のパス表記 - file-glob -
 パスの区切り文字、「/」と「\」。Windows自体は両方とも受け付けるようですが、それぞれの処理のライブラリはその限りではありません。
 Linux起源のソフトだと区切りが「/」でないと動かないことが多いのですが、Gimp2.6.8で少し試したところ、大抵のところで、「/」、「\」の両方が使えるようです。ならば、「/」で書きたいところ。というのは、「\」自体は「"文字列"」の中のエスケープに使われているので、例えば「c:\datafolder\text.txt」ならば"c:\\datafolder\\text.txt"と書かなければなりません。(\\が、「\」を表すわけですね。

 ところが、ワイルドカード展開(古い言葉ですね。ファイルマッチングかな)をするための関数file-globは区切りが「\」でなければうまく動かないようです。
 「e:\graphics」にある拡張子「jpg」のファイルの一覧は、次のように書いて求めます。
(file-glob "e:\\graphics\\*.jpg" 1)
 戻り値は、(ファイルの数 ファイル名のリスト)の形(ドキュメントでは文字列の配列のはずなんだけど!!)。単純にファイル名のリストを取り出すには、おまじないの関数を書きます。
(cadr (file-glob "e:\\graphics\\*.jpg" 1))
file-jpeg-save

  jpeg形式の保存命令ですが、オプションが沢山あるけれど、ドキュメントを読んでも良くわからなかった。手探りで見当をつけたのが、下のとおり。
 いずれ、きちんとした資料が出たときには、はっきりするのでしょうが…

run-modeINT32 RUN-INTERACTIVE (0), RUN-NONINTERACTIVE (1)が選択できるのだけれど…。
imageIMAGE 保存対象のイメージ
drawableDRAWABLE 保存対象のドローワブル。ドローワブルはレイヤーとか、チャンネルのこと。
filenameSTRING ファイル名
raw-filenameSTRING ファイル名。
qualityFLOAT 品質(0.0〜1.0)。0.85ぐらいが適当。0.95以上はあまり意味が無いとされる。
smoothingFLOAT 画像を滑らかにする処理の度合い(0.0〜1.0)。画像は滑らかになるけれど、少しボケます。
optimizeINT32 最適化(0:しない 1:する)。エントロピー化パラメータの最適化を行うかどうかを決める。最適化をすると処理速度が遅くなる。
progressiveINT32 プログレッシブ(0:しない 1:する)。インタレース方式にするかどうかを決める。通常は必要無い。
commentSTRING コメント文字列。
subsmpINT32 サブサンプリング方式を指定。マニュアルに書いてないので結果のファイルサイズから類推しました。
0(4:1:1) :ファイル容量最小。 境界のはっきりしない画像に向いているが、 不自然な色が出る傾向があり。
1(4:2:2):ほとんどの画像で品質とファイル容量の絶妙なバランスがとれる標準的なサブサンプリング法。しかしサブサンプリングを使わない方法 (4:4:4) の方が目に見えて画像品質が高い場合もある。例えば均一な地を背景に文字が書かれた画像や色変化が乏しい画像には不適。
2(4:4:4):最高品位。境界部や対照色は保持されるが、圧縮能力は下がる。
3 (4:2:2):に似ているが、縦方向ではなく横方向の微妙な色変化をよくサンプリングできる。
baselineINT32 JPEGベースラインを採用(0:しない 1:する)。デコーダーによっては、JPEGベースラインでないと扱えないものもある。
restartINT32 リスタート(0:使用する 1:使用しない)。画像の途中に分割読み込みを可能にするマーカーを挿入します。
dctINT32 DCT変換方式を指定する。マニュアルに書かれていないので類推です。
0高速整数:もっとも正確さが劣る。
1整数:「浮動小数」より高速だが、 正確さは劣る。
2浮動小数:正確。演算する機種によって僅かに違う結果が出る。
スクリプト関数登録 - script-fu-register -

ここへ移動しました

Gimp2Scheme の Enum

 ここへ移動しました。

gimp-drawable ドローワブル
 draw - able からの造語でしょうから、drawできるもの、「描画体」とでも訳せばいいのかもしれません。
具体的には、「レイヤー」、「レイヤーマスク」、「チャンネル」、「テキスト・レイヤー」がドローワブルです。「レイヤー」などは、ドローワブルのサブクラスのようなものです。
gimp-buffer バッファー

 カットアンドペースト用の名前つきのバッファです。中には、ドローワブルが格納されます。名前がつけられているので、画像の一時記憶など、便利に使えます。
 コピー、カットは次の関数で行います。

gimp-edit-named-copy
gimp-edit-named-copy-visible
gimp-edit-named-cut

 ペーストは次の関数で行います。

gimp-edit-named-paste

 ペーストするとフローティング選択(floating-sel)になるので、レイヤーに変換する(gimp-floating-sel-to-layer)か、上書きします(gimp-floating-sel-relax)。

 使用し終わったバッファは、gimp-buffer-delete で削除します。

gimp の API 関数はリストを返す

 API関数は、Scriput-Fuプロシージャブラウザで説明を見ることが出来ますが、そこに書いていない、重要なポイント。API関数は、必ず、リストの形で値を返します。戻り値が一つしかなくても、要素一つのリストを返します。
これは、ブラウザに書かれているうちの、内部GIMPプロシージャと、GIMPプロシージャについて、当てはまります。

 また、後から組み込んだスクリプト関数は、「一時プロシージャ」と区別されますが、このとき、引数表示の先頭に「run-mode」が勝手に追加されていますが、この引数は不要です。たぶん、ブラウザの誤りだと思うのですが…。

ダイナミック・スコープか、レキシカル・スコープか

 大抵のLispはダイナミック・スコープですが、Common Lisp とか、Scheme は、レキシカル・スコープを持ちます。では、gimpのSchemeはどちらのスコープでしょうか?

 その前に…
 ある関数を定義したとき、その関数のローカル変数を、その関数の中で呼び出した別の関数から見えるような仕組みがダイナミック・スコープ、見えない仕組みがレキシカル・スコープです。(大雑把ですが…)
 C 言語などは、レキシカル・スコープ。Perlでは、myで宣言するとレキシカル・スコープ。local で宣言するとダイナミック・スコープになります。

 手っ取り早く、一番簡単なローカル変数、つまり、関数の引数で試してみます。

> (define (func_a x) (+ x y))
func_a
> (define (func_b x y) (func_a x))
func_b
> (define y 0)
y
> (func_b 1 2)
1

 (func_b 1 2) を実行すると、func_b の中では x=1、y=2。この状態で func_a を呼び出すと、func_a は引数に y を加えて返します。この y が、何れの y かというのがテーマ。戻り値が 1 であることから、func_b の引数の y ではなく、(define y 0 )で設定されたグローバルな y が参照されていることが推察できます。引数はダイナミック・スコープではなく、レキシカル・スコープを持ちます(ダイナミック・スコープでは、func_a の y は func_b の y を参照します)。

define は、どこに定義するか

 define で変数や、関数を定義しますが、定義されたものは、どのスコープに定義されるでしょうか。
define はいろいろな場所にかけるので、いろいろ、試しました。

define を関数の外に書いたとき
> (define myvar 45)
myvar
> myvar
45

 一番、普通のケース。グローバルなスコープ、つまり、どこからでも参照したり変更したりできます。

define を関数の中に書いたとき

関数の中で define を使って変数を定義してみます。

> (define (myfunc x)
  (define y 15)
  (+ y x)
)
myfunc
> (define y 0)
y
> (myfunc 10)
25
> y
0

 myfunc の中で define を使って y を定義しています。myfunc の中で値を 15 にしているにもかかわらず、グローバルのyは変更されていないことがわかります。つまり、関数の中で define を使って定義した変数はローカル変数になります

define を if の中に書いたとき
> (define a 6)
a
> (if nil (define a 1) (define a2))
a
> a
1

 当たり前ですが、if の外側のスコープに定義されます。

define を let の中に書いたとき

 let は、内部ブロック内にだけ通用するローカル変数を定義するもの。このブロックの中でdefineを使ってみた。

> (define c 15)
c
> (let
  ( (a 1) (b 2) )
  (define c 3)
  c
)
3
> c
15    

 define で定義した変数のスコープは、let のブロック(body)中だけであることがわかる。

 もう少し、変な状況

> (define c 15)
c
> (let ( ( a (define c 5) ) (b c) )
    ( list a b c)
)
(c 5 5)
> c
5

 let の変数定義部分では、let の define は外側のスコープに変数を定義します。当然ですね。

define を let *の中に書いたとき

 let と同様とは思いますが、ブロック内に書いてみます。

> (define (myfunc x)
   (list 
     (let* ( (a 1) (b 2) )
         (define x 10)
         (list a b x)
     )
    x
  )
)
myfunc
> (myfunc 4)
((1 2 10) 4)

 define で定義した変数のスコープは、let* のブロック(body)中だけであることがわかる。

 let* はローカル変数のバインディングを登場順に行うので、ここに define を書くと変なことが起きそう。

> (define c 0)
c
> (let* ( ( a (define c 5) ) (b c) )
    ( list a b c)
)
(c 5 5)
> c
5
> (set! c 0)
0
> (let* ( ( a 1 ) (b (define c 10)) )
    ( list a b c)
)
(1 c 10)
> c
0

 let* のローカル変数定義の最初にdefine を使った場合は、外側のスコープに定義されるけれど、二番目の変数に定義したものは、ローカルなスコープ内に定義されます。まさに、変なことが起きています。(たぶん、Schemeではこういう書き方自体が許されないと思いますが…)

define を letrec の中に書いたとき

 let / let* と同様とは思いますが、ブロック内に書いてみます。

> (define (myfunc x)
   (list (letrec ( (a 1) (b 2) )
                 (define c 5)
                 (list a b c)
         )
        x
  )
)
myfunc
> (define c 0)
c
> (myfunc 10)
((1 2 5) 10)
> c
0

 define で定義した変数のスコープは、letrec のブロック(body)中だけであることがわかる。

 lettrec は、ローカル変数の値を決める前に変数をまとめて定義するので、let や let* と動作が異なるようです。

> (define c 0)
c
> (letrec ( ( a (define c 5) ) (b c) )
    ( list a b c)
)

(c 5 5)
> c
0
> (letrec ( ( a 1 ) (b (define c 10)) )
    ( list a b c)
)

(1 c 10)
> c
0

 最初の変数定義に書いた define、二番目に書いた define 共にローカル変数になっていることがわかる。

define を begin の中に書いたとき
> (define c 0)
c
> (begin (define c 100) )
c
> c
100

 begin では、特にローカル変数化されないことがわかった。

define を whenの中に書いたとき
> (define flag #t)
flag
> (define c 0)
c
> (while flag (define c 15) (print c) (set! flag #f))
15
()
> c
0

 while のブロックの中で define で定義した変数はブロックのローカル変数になる。(MzSchemeでは書けない)

define を unless の中に書いたとき
> (define flag #f)
flag
> (define c 0)
c
> (unless flag (define c 15) (print c) (set! flag #t))
15
#t
> c
15

 なんと、while とは違って、unless のブロック内で define で定義した変数はブロックのローカル変数にはならない。調べてみないとわからないものですね。

define を do の中に書いたとき
> (define c 0)
c
> (do ( (flag #f) )
     (flag (list flag c) ) 
     (define c 15) (print c) (set! flag #t)
)
15
(#t 0)
> c
0

 do のブロック内で define で定義した変数はブロックのローカル変数になることがわかる。
 また、値の返却 (list flag c) 時にはスコープが閉じてしまって、ブロックのローカル変数を参照できないことも、興味深い。

 色々試しましたが、define で定義した変数は、そのときのスコープに定義されるということのようです。
 ローカル死うコープを持つブロック構造を備えた特殊関数はどれか、ということになりますが、if cond case などは、除外してよいようです。
 意味合いから、明確にスコープを持っているのは、let let* letrec do 。
 begin は、スコープを持っていませんでした。また、while はスコープを持つのに、unlessは持たないという不思議な結果です。


アルゴ算法堂へ戻る