ImageJ2のパラメータ記法を使ってスクリプト書きをちょっとだけ楽する

忙しいんですけど、現実逃避のためのエントリ。

さて、みんな大好きImageJですが、実は数年前からImageJ2というものが開発されています。2014年にRC版が公開されました。

ImageJ2はSciJavaと呼ばれるフレームワークをベースにして完全に新しく作り直されたもので、Plugin作成の際に使用するライブラリも従来のImageJ1.xのものとは全く異なっています。ただし、後方互換性に非常に気を使って作られているので、これまでのImageJ1.x用のPluginやScirpt、Macroも問題なく動きますし、ユーザーレベルだと今のところあまり大きな違いはありません。実際、現在のFijiにはすでにImageJ2が同梱されています。


ImageJ2の細かい説明はこのへん


で、ImageJ2は従来のImageJでは限界があった部分がいろいろと改良されており、いずれは移行するべきなのでしょうけど、正直、一からScriptの書き方を勉強し直すのも面倒くさい一部機能がベータ版だったりと発展途上ではあるので、とりあえずまだ様子見でいいんじゃないかと思います。ただ、ImageJ2の機能を使うとScript書きが楽になる場面があったりするので今回はそれの紹介です。

これまでのImageJ1.xにおいて、ScriptやMacroの実行時にファイルやディレクトリを指定する、またはパラメータを入力する、などといった場合にはOpenDialogやDirectoryChooser、GenericDialogといったものが使われていました。しかし、これをScriptの中に実装しようとすると微妙に面倒くさい。


たとえば、とあるディレクトリを指定するためのダイアログを表示させるためのJython Scriptだと

from ij.io import DirectoryChooser  
  
dc = DirectoryChooser("Choose a folder")  
folder = dc.getDirectory()  
  
if folder is None:  
  print "User canceled the dialog!"  
else  
  print "Selected folder:", folder

こんな感じ(ここから参照しました)。

パラメータを入力するときは、

from ij.gui import GenericDialog

def getName():
	gd = GenericDialog("What's your name?")
	gd.addStringField("name", "Masuda")
	gd.showDialog()

	if gd.wasCanceled():  
		print("User canceled dialog!")
		return
		
	input_name = gd.getNextString()

	return input_name

name = getName()

if name is not None:
	print("Your name is " + name + "!")

パラメータが増えると、

from ij.gui import GenericDialog

def getPersonalData():
	gd = GenericDialog("enter your personal data")
	gd.addStringField("name", "Masuda")
	gd.addNumericField("age", 17, 0)
	bloodtypes = ["A", "B", "O", "AB"]
	gd.addChoice("blood type", bloodtypes, bloodtypes[0])
	gd.showDialog()

	if gd.wasCanceled():  
		print("User canceled dialog!")
		return
		
	name   = gd.getNextString()
	age    = gd.getNextNumber()
	b_type = gd.getNextChoice()
	
	return name, age, b_type

data = getPersonalData()

if data is not None:
	name, age, b_type = data
	print name, age, b_type

面倒くさい。

そこで、#@parameterという記法を使います。たとえば、

#@String name
print("Your name is " + name)

として実行すると、文字列を入力するためのダイアログが出てくる。入力したダイアログは変数nameに格納される。


#@output <変数型> <変数名>で出力もできる。

#@String name
#@output String result

result = "Your name is " + name


いろいろできる。

#@String(label = "What's your name?", value = "Masuda") name
#@output String result

result = "Your name is " + name

GenericDialogを用いて作った上の例は

#@String(label = "name", value = "Masuda") name
#@Integer(label = "age", value = 17) age
#@String(label = "blood type", choices = {"A", "B", "O", "AB"}, style = "listBox") b_type

print name, age, b_type

と書ける。


ディレクトリを指定したいときは

#@File(label = "select a directory", style = "directory") my_dir

print(my_dir)


また、表示している画像オブジェクトを取得したいときは

#@ImagePlus img

print(img.getTitle())

とするとactive windowのImagePlusオブジェクトが取得できる。

今回のScriptはJythonだけど、他のScript言語やMacroでも同じように使える(らしい)。また、従来のImageJ1.xのAPIを用いた記述と混ぜて使うこともできる。

細かいことは以下のリンクを参考に。

https://imagej.net/Script_Parameters
http://imagej.github.io/presentations/2015-09-04-imagej2-scripting/#/

今のところ、まだお試し程度にしか使っていないので自己責任で。

(追記)

これ書きながら、Scriptを繰り返し実行したときにデフォルト値がvalueで指定した値にならずに前の入力を引きずっているのが気になっていたんだけど、Forumを検索していて原因がわかった。parameterにはpersistというpropertyがあってデフォルトではtrueになっている。これがtrueになっていると前回の入力がvalueで指定した値を上書きするらしい。なので、

#@String(label = "What's your name?", value = "Masuda", persist = false) name
#@output String result

result = "Your name is " + name

のように書くと正しくvalueの値がデフォルト値として参照される。