Translate

ラベル macro の投稿を表示しています。 すべての投稿を表示
ラベル macro の投稿を表示しています。 すべての投稿を表示

2019年10月28日月曜日

Logウィンドウ以外にprintの出力を出す

printは普通に使うとLogウィンドウに結果を出力する。
Logウィンドウとは違うウィンドウに結果を出力するには、
print(textTitle, string);
を使う。
textTitleは
run("Text Window...", "name=textTitle width=80 height=20 menu");
のようにして作成したテキストウィンドウを指す。このとき、textTitleは"[]"で囲まれている必要がある。
textTitle = "[test text]";
run("Text Window...", "name=" + textTitle + " width=80 height=20 menu");
print(textTitle, "Hello mac\n");
print(textTitle, "DomDom burger");

とかいう感じ。printはそのままでは改行してくれないので、複数行にするときは改行コードを入れておく。


2019年10月21日月曜日

ImageJのデバッグ機能

ImageJのMacro editorは普通のテキストエディタで、予約語や変数をハイライトしてくれないし、インデントも自動で設定はしてくれない。
マクロを書くならScript EditorがあるFijiを使うのが断然楽だ。だが、Fiji使いでも普通のImageJは別途入れておいた方が良い。
ImageJのMacroEditorには、Debug機能がついている。

プログラムは常にバグを含んでいる。構文ミスなどのエラーは修正が容易だが、エラーは出ずにでもなんか動作がおかしい、というバグは厄介である。
バグを修正することをデバッグというが、ImageJのMacroEditorにはその機能がある。EditorのDebugメニューから使うことができる。

  • Debug Macro ... デバッグモードに入る
  • Step ... 1行ずつ実行
  • Trace ... ゆっくり実行
  • Fast Trace ...速度をあげて実行
  • Run ... 普通に実行
  • Run to Insertion Point ... カーソル位置まで実行(カーソル位置の文は実行しない)
  • Abort ... やめる
となっている。デバッグモードになれば、変数の中身も別ウィンドウで確認することができる。

作成はFijiでDebugはImageJで、という感じで使いたい(Fijiでできれば話は早いのだが、デバッグ機能の実装が難しい、というような話を聞いた)。

2019年10月16日水曜日

輝点の検出を楽に行う -macro-

以前、輝点の検出を楽に行うとしてpluginからMaximumFinderを使う方法を記述したが、Macroからは、FindMaximaを使う。
run("Find Maxima...", "prominence=10 output=[Point Selection]");
(最新のImageJバージョンは、prominenceだが、ちょっと前のバージョンまではtoleranceというパラメータだった)。
これだけでPointRoiを作ってくれる。
このPointRoi位置の輝度の情報は、Measure...をすればResults Tableとして得ることもできるが、Macro内でアクセスする場合は、
getStatistics(area, mean, min, max, std, histogram);
で簡単に取得できる。(このコマンドを実行すれば、各要素が変数area, mean, min, max, histogramに格納される。histogramは配列)

さらに、PointRoiの個々の座標は、
Roi.getCoordinates(xpoints, ypoints);
とすれば、各座標はxpoints、ypointsに配列として格納できるので、

for (i = 0; i < lengthOf(xpoints); i++) {

 print(xpoints[i], ypoints[i]);

}
とでもすれば、各座標を取り出すことができる。

2019年10月2日水曜日

(小ネタ)pluginからmacroに値を渡す

PluginからMacroに値を渡す方法がImageJのメーリングリストにあった。
How to return a value from a plugin to a macro?
ここのMicheal schmidさんの投稿にあるように、
plugin側では、
public class My_Plugin extends ... {

     static double myResult;

     void run(ImageProcessor ip) {

        ...

         myResult = ...;

     }

     static String getResult() {

         return myResult.toString();

     }
}
としておき、macro側では
myResult = parseFloat(call("My_Plugin.getResult"));
とすれば渡せると思いきや、これを実行すると、getResultなんてありませんよ、とエラーがでると思う。
static String getResult() {...}
を、
public static String getResult() {...}

にしないと、外部からアクセスすることができないから。ちなみにテキスト以外は無理ぽいので、複数の値を渡すときは、カンマ区切りなどにしておき、macro側で、
items = split(myResult, ",");

として要素を取り出せばよい。 Pluginにしかできないことも多々あるが、macroの方が手軽なので、できるだけmacroで作りつつ、外部ライブラリを使いたい場合などはpluginを最小限利用するのが良い。

2018年12月13日木曜日

誰も教えてくれないImageJの使い方(というかマクロの使い方)その2 マクロからマクロを呼ぶ

マクロから他のマクロを呼ぶ場合は二通りある。

  1. pluginsフォルダーにいれて、runコマンドで呼び出す
  2. macrosフォルダーにいれて、runMacroコマンドで呼び出す

簡単なのはマクロをmacrosフォルダーではなくpluginsフォルダーに置いて、
run(macroName, dialogParameter);

で呼ぶ。この時、呼ばれる側のマクロがDialogを開く設定にしておけば、dialogParameterに必要な引数を渡せば、呼び出される側にパラメータを渡すことができる。dialogParameterの記述方法を知るには、Recorederを使って記録しておけばよい。
例えば例として以下のようなマクロを考えてみる。
/*
 * add_Test.ijm
 */
Dialog.create("add test");
Dialog.addNumber("a", 1);
Dialog.addNumber("b", 1);
Dialog.show()
a = Dialog.getNumber();
b = Dialog.getNumber();
print("a + b = ", a + b);

このマクロをpluginsフォルダーに保存して、pluginメニューから実行すると、Recorderには以下のように記録される。
run("add Test", "a=1 b=1");

このようにDialogを開くようにしておけば、パラメータとして引数を渡すことができる。ちなみにPluginを作る際にもこのテクニックは利用できる。

しかし、この方法ではパラメータを与えることはできても受け取ることはできない (returnが使えない)。従って、例えば呼び出す側で
ret = run("add Test", "a=1 b=1");
と書いてもエラーがでる。

引数を受け取ることのできるようにするには、runではなくrunMacro(macroName, parameter)で呼び出す。ただし、上記のようにDialogのパラメータとして直接渡すことはできないし、呼び出すとDialogが開いてしまう。そこで、呼び出される側のマクロ (ここではadd_Test.ijm)を次のようにする。
/*
 * add_Test.ijm
 */

arg = getArgument();
if (arg == "") {
 Dialog.create("add test");
 Dialog.addNumber("a", 1);
 Dialog.addNumber("b", 1);
 Dialog.show()
 a = Dialog.getNumber();
 b = Dialog.getNumber();
} else {
 items = split(arg, ",");
 a = parseInt(items[0]);
 b = parseInt(items[1]);
}
return toString(a+b);
getArgument()は、このマクロが別のマクロから呼び出された場合、parameterで指定した文字列が入る。そこで、もしこの文字列が空の場合はDialogを開くように設定することで、このマクロ単体でも実行チェックを行うことができる。
argにparameterが入っていれば、そこから要素を取り出す。渡すことのできるのは一つの文字列なので、例えば","で各パラメータを区切ってやる。そこから各要素をとりだすために、
items = split(arg, ",");

を行うと、各要素をitems配列に返してくれる。あとは、各要素を取り出してやればよい(数値ならparseIntや parseFloatを使う)。
さらに、結果を
return toString(a+b);

で文字列として返してやることができる(マクロの場合のパラメータのやりとりは常に文字列のみ)。こうすれば、呼び出す側で例えば
ret = runMacro("add_Test.ijm", "1,1");

としてやれば、retに結果を文字列として返すことができる。

まとめると、お手軽なのはpluginsフォルダーに入れる方法だが、後々のことを考えると、runMacroで呼び出せるようにしておいた方がよいと思う。

2018年12月5日水曜日

誰も教えてくれないImageJの使い方 -ツールバーへの登録-

久しぶりの更新。あんまり知らないだろうな、という方法をこそっと紹介してみる。

ImageJのツールバーへの登録方法。Built-in-Macroにある
newMenu(macroName, stringArray)
を使う。macroNameにはメニュー処理するMacro、stringArrayにはメニューの項目を登録する。実際の例として、ToolBar Menuに記載がある。一部抜粋すると、

var dCmds = newMenu("Developer Menu Tool",
      newArray("ImageJ Website","News", "ImageJ Wiki", "Resources", "Macro Language", "Macros", "Macro Functions", "Startup Macros...", "Plugins", "Source Code", "List Archives", "-", "Record...", "Capture Screen ", "Monitor Memory...", "Find Commands... ", "Control Panel...", "Search...", "Debug Mode"));
      
macro "Developer Menu Tool - C037T0b11DT7b09eTcb09v" {
       cmd = getArgument();
       if (cmd=="ImageJ Website")
           run("URL...", "url=http://rsbweb.nih.gov/ij/");

ルールとして、Macro名(macroName)の後ろには"Menu Tool"を付ける。stringArray(直接newArrayで登録しているが)にメニューの項目を設定する。ここまではいいのだが、では例えば以下のようにしても、アイコンがでずうまく登録できない。

var dCmds = newMenu("TestMenu Tool", newArray("Test1macro", "Test2macro");

macro "Test Menu Tool" {
    cmd = getArgument();
    runMacro(cmd);
}

なぜなら、登録すべきアイコンを設定していないから!
ここでサンプルをみると、
macro "Developer Menu Tool - C037T0b11DT7b09eTcb09v" {
とあり、Menu Toolの後ろになにやらよくわからない文字列が並んでいる。しかしサンプルのどこをみてもこれの説明はないし、ググっても見つからない。最初、アイコンの画像を参照するIDか何かかと思って、ij.jarを解凍してみたけど、それっぽいリソースは見つからなかった。
うーん、困った。困ったときにはImageJのソースコードを見るのが良い。メニューを登録しているクラスは、ij.gui.Toolbarっぽい。さらに探すと、void drawIconなるメソッドが見つかる。ちょっと長いけど、メソッドを張り付けてみる。

void drawIcon(Graphics g, int tool, int x, int y) {
        if (null==g) return;
        icon = icons[tool];
        if (icon==null) return;
        this.icon = icon;
        int x1, y1, x2, y2;
        pc = 0;
        while (true) {
            char command = icon.charAt(pc++);
            if (pc>=icon.length()) break;
            switch (command) {
                case 'B': x+=v(); y+=v(); break;  // reset base
                case 'R': g.drawRect(x+v(), y+v(), v(), v()); break;  // rectangle
                case 'F': g.fillRect(x+v(), y+v(), v(), v()); break;  // filled rectangle
                case 'O': g.drawOval(x+v(), y+v(), v(), v()); break;  // oval
                case 'V': case 'o': g.fillOval(x+v(), y+v(), v(), v()); break;  // filled oval
                case 'C': // set color
                    int v1=v(), v2=v(), v3=v();
                    int red=v1*16, green=v2*16, blue=v3*16;
                    if (red>255) red=255; if (green>255) green=255; if (blue>255) blue=255;
                    Color color = v1==1&&v2==2&&v3==3?foregroundColor:new Color(red,green,blue);
                    g.setColor(color);
                    break; 
                case 'L': g.drawLine(x+v(), y+v(), x+v(), y+v()); break; // line
                case 'D': g.fillRect(x+v(), y+v(), 1, 1); break; // dot
                case 'P': // polyline
                    Polygon p = new Polygon();
                    p.addPoint(x+v(), y+v());
                    while (true) {
                        x2=v(); if (x2==0) break;
                        y2=v(); if (y2==0) break;
                        p.addPoint(x+x2, y+y2);
                    }
                    g.drawPolyline(p.xpoints, p.ypoints, p.npoints);
                    break;
                case 'G': case 'H':// polygon or filled polygon
                    p = new Polygon();
                    p.addPoint(x+v(), y+v());
                    while (true) {
                        x2=v(); y2=v();
                        if (x2==0 && y2==0 && p.npoints>2)
                            break;
                        p.addPoint(x+x2, y+y2);
                    }
                    if (command=='G')
                        g.drawPolygon(p.xpoints, p.ypoints, p.npoints);
                    else
                        g.fillPolygon(p.xpoints, p.ypoints, p.npoints);
                    break;
                case 'T': // text (one character)
                    x2 = x+v()-1;
                    y2 = y+v();
                    int size = v()*10+v()+1;
                    char[] c = new char[1];
                    c[0] = pc<icon.length()?icon.charAt(pc++):'e';
                    g.setFont(new Font("SansSerif", Font.PLAIN, size));
                    g.drawString(new String(c), x2, y2);
                    break;
                default: break;
            }
            if (pc>=icon.length()) break;
        }
        if (menus[tool]!=null && menus[tool].getItemCount()>0) { 
            xOffset = x; yOffset = y;
            drawTriangle(15, 15);
        }
    }
なにやらSwich文でBやらFやらOやらCやらを場合分けしている。。実は先ほどの意味のよくわからない文字列
C037T0b11DT7b09eTcb09v
これには、アイコンを描画するための命令が記述されていた!
例えば最初の
C037
の”C”は描画色を決めている。そのあとの3桁の数字はそれぞれRGBに対応しており、この値に16を乗じたものになる。したがってここでは(R,G,B) = (0, 3*16, 7*16)という色を指定している。
次の
T0b11D
の”T"は、文字を指定する。最初の0bはXY座標を示しており、X=0, Y=b (=11, 16進数)を表す。次の"11"はフォントサイズ。最後の"D"は実際に表示する文字を示す。
こんな感じでアイコンを直接描画することができる。
利用できるコマンドをソースコードから類推すると
  1. Bxy: ベース(開始XY座標)をxyでリセットする?
  2. Rxywh: Rectangleの描画 (x, y, w, h)
  3. Fxywh: FillRectangleの描画 (x, y, w, h)
  4. Oxywh: Ovalの描画 (x, y, w, h)
  5. Vxywh: FillOvalの描画 (x, y, w, h) ("o"でもよいみたい)
  6. Crgb: 描画色の決定 (r*16, g*16, b*16)が実際の色
  7. Lxyx'y': Lineの描画 (x, y, x', y')
  8. Dxy: Dotの描画 (x, y)
  9. Pxyxyxyxyxy... : PolyLineの描画
  10. Gxyxyxyxyxy... : Polygonの描画 ("H"でもよいみたい)
  11. Txysc : Text cの描画 (x, y), fontsize = s
これを使えば好きなアイコンを作ることができる。座標の指定は0~fの16なので、実質16x16ピクセルと思えばよさそう。


2017年5月10日水曜日

writeとprint

writeとprint

ImageJでマクロを書くときに、Logウィンドウへの書き出しはいつもwrite関数で行っていた。しかし、一般的にはprint関数が使われている。
ImageJ のBuilt-in Macro Functionsには, 実はwrite関数は明示されてない.現在では,logウィンドウへの書き出しはwrite関数ではなく,print関数が一般的である.では,write関数とprint関数では動作は異なるのだろうか?ImageJのソースコードを見ると、ij.macro.Functionsパッケージに,void doFunction(int type)という関数があり、この中に、
switch (type) {
…
    case PRINT: case WRITE: print(); break;
…
と書かれた場所がある.switch関数はtypeの中身に応じて処理を振り分けるJava言語の命令であり,処理の数だけcase が用意されてある.ここで示されたcase行の意味は,「typeにPRINTやWRITEが格納されていれば,print()関数を実行せよ」である.従って,マクロでprintと書こうが,writeと書こうが同じ処理となる.このような仕組みはおそらく互換性を維持するためのものと思われる