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

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

ImageJ / Python プログラミング Tips: Jython ではリスト変数と配列は異なる

 ImageJ における Python は厳密に言うと、Jython と言って、Python のインターフェースから Java を操作する言語を使っています。

 元々 Python は、インタープリターとして C言語で書かれたコードを実行する、言わば C 言語の複雑さをオブラートで包むような言語 (CPython) として考えられていたようで、その Java 版として Jython が考えられていたようです。ただ、今日では Python (= CPython) から Java の機能を呼び出すライブラリもできていますので、今は Jython を使う意味が薄れています。そのため、Jython も 2015 年以降あまり進化していないようです。おそらく今日 Jython を使う意味は、ほぼ ImageJ を Python で操作するためと言って良いでしょう。ただ、開発が完全に終了したのではなく、メンテナンスリリースは続いているようです*1

www.jython.org

 従って、Jython は、ちょっと古い Python 2.7 準拠になっていますが、それ以外にも通常の Python とは異なります。それで今回ちょっと躓きました。

 今回つまづいたのは、imageProcessor にある applyTable メソッドです。このメソッドは、imageProcessor に LUT を適用するメソッドです。このメソッドは LUT を引数として取りますが、その LUT は整数の 256 (8bit 画像の場合) もしくは 65536 (16bit 画像の場合) の要素を持つ配列である必要があります。

 ところが Jython 上で正しく整数の配列を読ませているはずなのに、以下のエラーが出ます。

TypeError: applyTable(): 1st arg can't be coerced to int[]

 余談ですが、Chat GPT にどうして間違っているのですか、と聞いてみたところ、「コードは正しい様です。他のところが間違っているかもしれません」と答えます。ちなみに Chat GPT はプログラミングのアルゴリズムに関して、メジャーな言語、皆がよく使うようなアルゴリズムに関しては、かなりよく答えてくれますが、マイナーでニッチな言語のアルゴリズムに関してはあまり当てになりません。その原理を考えれば当然です。つまり、学習するための元ネタが少ないので、答えも怪しくなる、ということです (もちろん元ネタがいっぱいあっても、皆が間違っていれば間違った答えが出ます)。Jython でプログラミングをするなんてその最たるものでしょう。GIMP 用のスクリプトPython で書くのも、Chat GPT に正しい答えを期待できません。「本当にそれで合っていますか?」と問いかけると「すみません。間違いがありました」と言って、別のコードを提案してきます。さらに、「本当にこれで大丈夫ですか?」と追加で問いかけるとまた「すみません。間違いがありました」という具合です。Chat GPT が提案してきたコードが正しいかどうかを判断するには、自分がその言語に対してちゃんと知識が必要で、うっかりそのまま鵜呑みにできません。特にマイナーであればあるほど怪しいです。

 まぁ、Chat GPT はネット上の多数決によって決まった回答を出してくれるサービスだと思えば良いのではないでしょうか。ただ、多数決によって決まった回答が常に正しいとは限りません。プーチンだってロシア国民の圧倒的多数から支持されているのですから。

 それはともかく、その理由を探るために、Java 関連の情報を探っていると、Python では配列=リスト変数ですが、Java ではそうではなく、配列 (arrey) とリスト変数は別だということが分かりました。配列は要素数が固定されているのに対し、リスト変数は Python と同じく、要素を増やしたり減らしたりできるというのです。つまり Python のリスト変数で LUT を作ると、それを Java の配列として認識してくれないため、型がおかしいと文句を言ってくるのではないかと推測しました。

 では、Jython 上で、Java の配列が扱えないかと調べてみると、以下のような情報が見つかりました。

docs.oracle.com

 結局、この移行ガイドにあるように、

import jarray

を頭につけ、その後、

lut = jarray.array([0]*256, 'i')

で lut を作成すると、その lut を引数として取った applyTable メソッドがちゃんと動くことが分かりました。

 なお、試していませんが、以下のようなコードでも良いようです。

import java
java.type("int[]")(10)

 これ以外にも、PythonJava の型の違いから、漫然とコードを書くとうまく ImageJ の API が動いてくれないということが色々ありそうです。ImageJ の API は基本 Java 準拠ですので、この点は要注意かと思われます。

*1:なお、Java を操作する Python 3.x 準拠の Python インタープリターとして、GraalPy というのも開発されているようです。

medium.com ただ、この記事は2020年以降、Jython はメンテナンスされていないと書かれていますが、2022年にメンテナンスリリースが出ています。おそらく ImageJ で使われている限り、2年に一度程度メンテナンスが入るのではないでしょうか。