皆さまこんにちは。
『サイバーパンク2077 』から抜け出せません、yu3xx(ゆーさんちょめちょめ)です。
このブログではこれまで、ImageJマクロの様々な使い方について実例を提示しながら紹介してきました。
ImageJマクロはIJ1 Macroという独自の言語で書く必要がありますが、FijiというImageJの上位互換版を利用することで、人気のプログラミング言語であるPython(Jython)でプラグインを書くことが可能となります。
私自身は今までFijiやJythonは使用していませんでしたが、ちょこちょこ勉強していくためのきっかけとしてJythonを用いて漫画自炊用Pluginを作成してみました。
今回はこのJython Pluginを紹介しつつ、同じくこれからPlugin開発を始めてみたい人向けのかんたんな解説や、マクロと比較した処理時間の違いを書いていきたいと思います。
2024.02.20追記. Jythonを用いたPluginを本格的に作ってみました
もくじ
Jythonコードはどこに書けばいいの?
① まずはFijiをダウンロード・インストールします。
② Fiji起動後、上部タブから
[File] → [New] → [Script...] でスクリプトエディタが起動します。
③ 言語設定を変更したいので、上部タブから
[Language] → [Python (Jython)] を選択します。
④ これで準備はOKです。 このスクリプトエディタにコードを書いていきます。
作成したJythonコード
from ij import IJ
from ij.io import DirectoryChooser
from ij.process import ImageProcessor
import os
import time
openDir = DirectoryChooser("").getDirectory()
IJ.log("openDir : " + openDir)
saveDir = DirectoryChooser("").getDirectory()
IJ.log("saveDir : " + saveDir)
startTime = time.time()
for root, dirs, files in os.walk(openDir):
files.sort()
for name in files:
if name.endswith(".jpg"):
path = os.path.join(root,name)
scale = 0.5
min = 20
max = 230
IJ.log(path)
imp = IJ.openImage(path)
w = int(imp.getWidth() * scale)
h = int(imp.getHeight() * scale)
imp = imp.resize(w,h,"bicubic")
ip = imp.getProcessor()
ip.setMinAndMax(min,max)
IJ.run(imp,"Apply LUT", "")
savepath = saveDir + name
IJ.saveAs(imp,"JPEG", savepath)
finishTime = time.time()
processingTime = finishTime - startTime
print processingTime
IJ.log("Processing time: " + str(processingTime))
Jythonの基礎的な解説
マクロでは「ListFiles」というユーザー定義関数を使ってやっていたものですが、Jythonではosというモジュールをインポートすることで、初期装備として使うことができます。
② クラス、オブジェクト、インスタンス、メソッド
このあたりはImageJマクロでは使わなかった概念です。
諸説あるところも含みますので、もしかすると練達の先輩方から怒られてしまうかもしれませんが、ふわっとゆるや〜かに書いていきます。
◇ クラスについて
ImagePlusとかImagePricessorとか、なぞのワードが出て来ますが、これらは「クラス」と呼ばれる設計図のようなものです。
ImagePlusは画像ファイルそのものの設計図で、ImageProcessorは画像から取り出した画素データの設計図です。
◇ オブジェクト、インスタンスについて
この設計図から作り出した実体のあるものを「オブジェクト」とか「インスタンス」と呼びます。
なので変数impは、クラスImagePlusという設計図から作り出したオブジェクト(インスタンス)となります。
変数ipは、設計図ImageProcessorから作ったオブジェクト(インスタンス)です。
◇ インスタンス化について
通常、Jythonのようなオブジェクト指向なプログラミングでは、「クラス(ほにゃらら)」の文法でインスタンスを召喚します。これをインスタンス化と呼びます。
例えばMonsterというクラスがあるとして、「Monster("スライム")」と唱えると、スライムという名前の実体のあるモンスターがインスタンスとして召喚されます。この召喚手順をコンストラクタと呼びます。
Jythonではこの正式なインスタンス化の召喚手順を踏まずにオブジェクトを生成することもあります。
上記コード内の「imp = IJ.openImage(path)」の部分は正式な手順を省略してimpというオブジェクトを生成している一例です。
「openImage(ほにゃらら)」という命令は「ImagePlusクラスのものを出力する」ということをあらかじめ定められている命令なので、結果としてimpというImagePlusクラスのオブジェクトが生成されています。
この「命令」は次に解説するメソッドのことになります。
◇ メソッドについて
それぞれのクラスには「メソッド」という処理や機能が用意されています。例えば「車」というクラスに、「前に進む」というメソッドが用意されているようなものです。
メソッドは基本的には、オブジェクトとして実体を持ってはじめて実行出来ます。メソッドを実行するときには「オブジェクト.メソッド」という文法で書くことになります。上記コード内の「ip.setMinAndMax」のような書き方です。
ところが、クラスのままメソッドを実行するという特別な場合もあります。これを「staticなメソッド」といい、「クラス.メソッド」の文法で書きます。
上記コードによく出てくる「IJ」はクラスなのですが、「IJ.log(ほにゃらら)」はこの「staticなメソッド」を使っています。
◇ API Documentationという虎の巻について
どのクラスにどんなメソッドがあるのか、そしてそのメソッドの出力はどんなタイプなのか、staticメソッドなのかどうか、についてはImageJの公式サイトに掲載されている「API Documentation」を見るとわかります。
例えば「ijパッケージ」の中にある「ImagePlusクラス」の中を見てみます。
「Method Summary」という項目がメソッドについて記載された部分なのですが、1列目「Modifier and Type」が出力されるタイプ、2列目「Method」がメソッド名、3列目「Description」がメソッドの内容となっています。
書き方がややこしいのでわかりにくく感じるかもしれませんが、2列目「Method」を見ながらいろいろ探すことになると思います。
『書き方に迷ったら、この虎の巻を参照する』というのが、Jython Plugin開発の一番のキモになりそうです。
staticメソッドは1列目「Modifier and Type」の欄にstaticと書かれています。
この項をまとめると
クラス : 設計図。
インスタンス、オブジェクト : クラスをもとに実際に作った物。モノ。
メソッド : オブジェクトやクラスが使うことの出来る呪文。
となります。
保存場所と起動方法
① スクリプトエディタに入力して作成した「.pyファイル」は、「Fiji.appフォルダ内のPluginsフォルダ」の中に保存します。
Pluginsフォルダ直下に適当な名前のサブフォルダを作成して、その中に.pyファイルを入れることも出来ます。
ただしサブフォルダ名に日本語文字を含んでしまうとPlugin起動時にエラーとなるため、英数字のみで命名してください。
② ファイルを保存したら、Fijiを一度閉じてからアプリを再起動すればインストール完了です。
!!!注意!!!
このときファイル名に _(アンダーバー)を「最低でも1つ」入れて下さい!
アンダーバーを名前に含む.pyファイルのみが自動インストールされます!
③ 起動するときは、上部タブ[Plugins]から使いたいPluginを選択してください。
Macroとの処理時間の比較
//Macro Code
openDir = getDirectory("");
list = getFileList(openDir);
print(openDir);
saveDir = getDirectory("");
print(saveDir);
setBatchMode(true);
startTime = getTime();
listFiles(openDir);
finishTime = getTime();
processingTime = (finishTime - startTime) / 1000;
print("Processing Time : ",processingTime);
//-----------------------------------------------------------------------------
//Define listFiles
function listFiles(dir){
list = getFileList(dir);
for(i=0; i<list.length; i++){
if(endsWith(list[i],"/")){
listFiles(dir+list[i]);
}else{
open(dir+list[i]);
operation();
}
}
}
//-----------------------------------------------------------------------------
//-----------------------------------------------------------------------------
//Define operation
function operation(){
name = getTitle();
print(dir + list[i]);
w = getWidth();
scale = 0.5;
targetW = scale * w;
run("Size...", "width=targetW constrain average interpolation=Bicubic");
min = 20;
max = 230;
setMinAndMax(min, max);
run("Apply LUT");
saveAs("JPEG", saveDir+name);
//bytes = parseInt(IJ.currentMemory());
//bytes = bytes / 1000000;
//print(bytes);
close();
}
//-----------------------------------------------------------------------------
Jython Pluginと同様な処理をするマクロです。自炊画像処理で最もよく使う「Almighty_Processing.ijm」を基本とした処理内容にしています。
処理時間は手動のディレクトリ選択が済んでからの、処理完了までの時間を測定しています。
処理対象の資料は『地獄のアリス』第一巻(松本次郎、集英社)の400dpiスキャンJPEG画像計217ファイルです。
結果は
Jython Plugin ... 63.3 sec
Macro ... 66.4 sec
「Jython Pluginのほうがちょび〜っとだけ速い」という結果でした。
FijiやJythonをはじめて使ってみた感想
Fijiはスクリプトエディタが高機能で、
・お気に入り表示的なウィンドウ追加
・入力領域の色彩テーマを変更可能
・対応するカッコのハイライト表示
・検索と置換機能の改良
・ユーザー定義関数を含む関数のサジェスト機能
などなど、良いことがたくさんあって結構気に入っています。
ImageJのStartUp Macrosもそのまま使用出来るので、ImageJのカスタマイズをそのまま引き継ぐことが出来ます。
今後は今までに作成したMacro「.ijmファイル」をFijiに移して、MacroとJython Pluginを共存させつつ、Fijiをベースに使っていこうと思っています。
JythonによるPlugin開発
おしまい
・公開したマクロのまとめ