Translate

2017年2月11日土曜日

輝点の検出を楽に行う

FindMaximaを使う

[Process -> FindMaxima]を使うと、画面上の輝点などを一瞬で検出してくれるため、輝点追跡をする際の初期パラメータを与えるためなどに重宝する。Pluginから当然利用することが可能。

MaximumFinderクラス

FindMaximaを使うには、MaximumFinderクラスを利用する。
import ij.plugin.filter.MaximumFinder;
import java.awt.Polygon;

...

ImageProcessor ip = imp.getProcessor();
double tol = 10;

MaximumFinder mf = new MaximumFinder();
Polygon mp = mf.getMaxima(ip, tol, true);

...
  1. まず、MaximumFidnerオブジェクトを作成する。
  2. 輝点の検出には、MaximumFinderのメソッドgetMaxima(ImageProcessor ip, double tolerance, boolean excludeOnEdges)を使う。ipで現在開いている画像のImageProcessorを渡す。toleranceは輝点を検出する際の閾値を決める。excludeOnEdgesは、画面のエッジ上の輝点を検出するかどうか決める。
  3. 検出された輝点の位置情報を、java.awt.Polygon型として返す。
    • 格納される順番は、輝度値が高い順番となる
  4. 輝点の座標情報には以下のようにアクセスすればよい
int ind = 0;
double x = mp.xpoints[ind];
doulbe y = mp.ypoints[ind];
Polgonのメンバ変数であるxpoints, ypointsを使えば、座標データに直接アクセスすることが可能である。これを利用すれば、輝点の自動検出や輝点のトラッキングなどに応用することが可能となる。
以上

2017年2月6日月曜日

ToDo List

ToDo List

ブログで書く予定の内容。随時更新。

Basic

  • [ ] ImagePlusとImageProcessor
  • [ ] ImageStack
  • [ ] Java
  • [ ] ImageStatistics

Listener系

  • [x] MouseListener
  • [x] ImageListener

ImageProcessor系

  • [x] Threshold
  • [x] MaximumFinder

ImageCanvas系

  • [ ] Overlay

ImagePlus系

  • [ ] Calibration

GUI系

  • [ ] Table
  • [ ] GenericDialog

外部ライブラリ

  • [ ] JFreeChart
  • [ ] JNI
  • ApacheCommonsMath
    • [ ] Curve Fitting
    • [ ] Matrix

Call from Macro

Environment

  • IDE
    • [ ] Eclipse
    • [ ] IntelliJ IDEA
  • [ ] Git
  • [ ] Maven
  • Mindmap
    • [ ] Xmind
    • [ ] mindmup
  • TextEditor
    • [ ] Vim
    • [ ] SublimeText3

2017年2月4日土曜日

画像に閾値が設定されているかどうか知る

Thresholdについて

ThresholdはImageProcessorのメソッドを使う。よく使うのは以下の二つ。
public void setThreshold(double minThreshold,
                         double maxThreshold,
                         int lutUpdate)

public double getMinThreshold()
lutUpdateにはRED_LUTやBLACK_AND_WHITE_LUT, OVER_UNDER_LUT, NO_LUT_UPDATEがあり、ImageProcessorの静的なメンバとして値が設定されている。なので、
ImageProcessor ip = imp.getProcessor();
ip.setThreshold(min, max, ImageProcessor.RED_LUT);
などのように使う。ここで、minの値を-808080.0DにセットするとThresholdを解除することができる。あるいは、ip.resetThreshold()というメソッドも使える。
また、getMinThoreshold()で返ってきた値が-808080.0Dあるいは、ImageProcessor.NO_THRESHOLDと同値であれば、その画像にはThresholdがかかっていないことを知ることができる。
これらメソッドは、例えばオブジェクトの自動検出のために閾値を利用する場合、閾値が設定されているかどうか事前に調べるために使うことができる。
以上

2017年2月3日金曜日

マウスのイベントを取得する

MouseListnerの実装

マウスのイベントを取得するためにはMouseListenerを利用する。MouseListenerはImageCanvasで実装する
public class hoge extends PlugInFrame {
    public static icMouseAdapter ima;
    public static ImagePlus imp;

    ...

    public hoge() { 
        imp = WindowManager.getCurrentImage();
        ImageCanvas ic = imp.getCanvas();
        ic.addMouseListener(ima = new icMouseAdapter(imp));

        ...

    }
}
ij.WindowManager.getCurrentImage()を使うと、現在アクティブな画像のImagePlusオブジェクトを取得することができる。

MouseAdapterクラス

ImageCanvasオブジェクトを取得したのち、addMouseListenerメソッドで実装する。ここでインスタンスしているicMouseAdapterは以下のようなクラスになる。
public class icMouseAdapter extends MouseAdapter implements Measurements {

    ... 
    public ImagePlus imp;

    public icMouseAdapter(ImagePlus imp) {
        this.imp = imp;
    }

    public void mouseClicked(MouseEvent e) {
        // マウスがクリックされたときの処理
    }
}
MouseAdapterクラスは、マウスイベントを受け取るための抽象アダプタクラスである。マウスのイベントには当然ほかにもいろいろあるが(ドラッグとかホイールの回転とか)、この場合継承しているのでinterfaceと違い、すべてのメソッドを実装する必要はない。
メソッドのパラメータMouseEvent eを使えば、マウスの座標情報を得ることができる。
int x = e.getX();
int y = e.getY();
ただし、もし画像が拡大・縮小されていた場合、マウスをクリックした場所の座標データを取得すると、本当に欲しい画像上の座標ではなく、拡大・縮小された画像での座標を取得することになってしまう。そこで、ImageCanvas.offScreenX(int x)を使う。
int x = ic.offScreenX(e.getX());
int y = ic.offScreenY(e.getY());
これで、マウスがクリックされた場所の座標データを取得することができる。

MouseListenerインターフェース

一方、Interfaceを使ってマウスイベントの取得方法を使うこともできる。

public class icMouseAdapter implements MouseListener, Measurements {

    ... 
    public ImagePlus imp;

    public icMouseAdapter(ImagePlus imp) {
        this.imp = imp;
    }

    public void mouseClicked(MouseEvent e) {
        // マウスがクリックされたときの処理
    }
    public void mouseEntered(MouseEvent e) {
        // マウスがコンポーネントに入ったときの処理
    }
    public void mouseExited(MouseEvent e) {
        // マウスがコンポーネントから出たときの処理
    }
    public void mousePressed(MouseEvent e) {
        // マウスのボタンが押されたとき
    }
    public void mouseReleased(MouseEvent e) {
        // マウスのボタンが離れたとき
    }
}
MouseListenerはInterfaceなので、使わないメソッドもすべて実装する必要があるが、もしほかのクラスを継承したいためにMouseAdapterが使えない(多重継承できないため)場合には、このInterfaceが使える。
プラグインが終わるなどしてMouseListenerの取り除く場合は
ic.removeMouseListener(ima);
としてやればよい(ImageListenerの場合と同じようにする)。
以上

2017年2月2日木曜日

画像が更新されたことを知る

ImageListenerの実装

画像が更新されたときにOverlayを書き直したり、画像を新しく開いたり閉じたりした際に応答するためには ImageListenerを実装する。
Interface ImageListener
ImageListenerには3つのabstract methodsがある。それぞれ、
void    imageClosed(ImagePlus imp) 
void    imageOpened(ImagePlus imp) 
void    imageUpdated(ImagePlus imp) 
で、文字通り、imageClosedは画像が閉じたとき、imageOpenedは画像が開いたとき、imageUpdatedは画像が更新されたとき(例えばimp.updateAndDraw()が呼び出されたとき)に実行される。
このinterfaceはPluginのサブクラスやPluginFilterを実装するクラス(要はPluginのクラス)に実装するのではなく、
static void addImageListener(ImageListener listener) 
と、ImagePlusのクラスメソッドであるaddImageListenerで実装する。手っ取り早いのは、内部クラスとしてPluginの中で実装してしまう方法だ。
... Pluginのなんらかの処理

ImagePlus.addImageListener(new ImageListener() {

    @override
    public void imageClosed(ImagePlus imp) {
    //画像を閉じたときの処理
    }

    @override
    public void imageOpened(ImagePlus imp) {
    //画像を開いたときの処理
    }

    @override
    public void imageUpdated(ImagePlus imp) {
    //画像が更新されたときの処理
    }
}
ただ、こうすると、例えばPlugInFrameを閉じてもlistenerが残ったままになるので具合が悪い。
ImagePlusからlistenerを取り除くには、
static void ImagePlus.removeImageListener(ImageListener listener)
を実行すればよい。なので、ImageListnerを実装するときはこうしている。
public class hoge extends PlugInFrame {
    ...
    public static ImageListener listener;
    ...

    public hoge() {
        ...
        ImagePlus.addImageListener(new listener = ImageListener() {
            ... 
        }

        addWindowListener(new WindowAdapter() {
            @override
            public void windowClosing(WindowEvent e) {
                //フレームが閉じる時の処理
            }

            @override
            public void windowClosed(WindowEvent e) {
                //フレームが閉じたときの処理
                ImagePlus.removeImageListener(listener);
            }
        }
    }
}
まず、staticなメンバ変数としてlistenerを宣言している。そして
ImagePlus.addImageListener(new listner = ImageListener() {
とすることで、listenerをnewしている。別にちゃんとクラスを用意してもいいが、面倒くさいのでいつもこうしている。
WindowAdapterは、フレームが閉じたりするときの動作を記述することができるサブクラスで、addWindowListenerにそのサブクラスを継承するクラスを渡せばよい。これもいつも内部クラスで済ます。interfaceと違い、すべてのメソッドを実装する必要はない。
以上

2017年1月31日火曜日

kotlinでplugin5

KotlinでPlugin開発-5

前回まででコードはできたので、ビルドしてPluginの形式にする。

plugins.config

ImageJのPluginには.classと.jarの2種類がある。.classファイルをPluginsフォルダーに入れれば、そのままファイル名がPlugin名となる。一方、複数のclassを含むような場合は、.jarファイルにまとめてしまう。その際、メインとなるクラスがどれかを指定する必要がある。plugins.configファイルを作れば、どのクラスがメインか、さらにはどのメニューに表示されるかを設定することができる。
plugins.configは、src\main\resourcesに作る。resourcesを右クリックして、[New -> File]を選ぶ。ファイル名はplugins.configとする。



中身はこんな感じ。
Plugins, "test1", jp.yo4.main.test
“,”でパラメータが区切られている。
  • 最初のパラメータはメニューを示す。ここを変えれば(例えばAnalyzeやProcessなど)、そこにPluginが表示されるようになる。
  • 次のパラメータは、メニューに表示される項目名を指定する。”“で囲む。
  • 最後のパラメータは、Pluginとして呼び出されるクラスを指定する。この際、パッケージ名をフルで指摘しないと実行されない。

jarファイルの作成

jarファイルはmavenのlifecycleで指定する。[View -> Tool Windows -> Maven Projects]を選ぶと、右側にMaven Projectsというウィンドウが現れる。ここで、プロジェクト名を展開し、Lifecycleも展開する。複数項目が現れるが、packageが目的のlifecycleになる。


packageを選ぶと、あらかじめpom.xmlに設定したdependencyなどの項目に従ってビルドが始まり、必要なファイルをすべて包含したjarファイルを作成してくれる。作成されたjarファイルは、Projectのtargetフォルダに作られる。あとは、このファイルをImageJのPluginsフォルダに放り込めば完成する。


おわり

KotlinでのPlugin開発は思ったより簡単にできる。今回はnull安全など、Kotlinらしい機能はあまり目立たなかったが、今後は、もっと活用したより大きなPluginを作ってみたい。

(2017/02/01: 少し修正)

2017年1月30日月曜日

kotlinでplugin4

KolinでPlugin開発-4

メソッドの中身を書く-2

run

IntelliJ IDEAが自動生成したrunメソッドは次の通り。
    override fun run(p0: ImageProcessor?) {
        throw UnsupportedOperationException("not implemented") //To change body of created functions use File | Settings | File Templates.
    }
例によってImageProcessorの引数p0はipに変えておく。
ここでは、単純にinvert処理を行う。Javaではシンプルに、
    ip.invert();
とすればよいが、もしipがnullの場合(画像が開いていない場合)はNullPointerExceptionが発生しうる。
実際にはsetupメソッドで画像の有無を確認していることから、もし画像が開いていない場合はrunメソッドまで処理が行くことはないが
したがって、kotlinで同じようにip.invert()とすると
Only safe (?.) or non-null asserted (!!.) calls are allowed on a nullable receiver of type ImageProcessor?
とIntelliJ IDEAに怒られるため、それを回避するようにコードを記述する必要がある。

if文による確認

一番わかりやすいのは、ipがnullかどうか確認することだ。これは単純に
        if(ip != null) {
            ip.invert()
        }
とすればよい。

安全呼出し

null許容型のオブジェクトのプロパティにアクセスする方法として、安全呼出し (safe calls)がある。これは、プロパティの呼出しに.ではなく、?.を使う
    ip?.invert()
もしipの中身がnullの場合、nullを返すがビルドはエラーとならない。kotlinのreferenceによると、この呼び出し方法は連鎖(chain)呼出しの際に有効であると述べられている。
null-safetySave Callsを参照
例えば、もしip1がnullだった場合、
ip1: ImageProcessor? = null
IJ.log("ip1 is " + ip1?.toString())
このコードは、ip1 is nullと返す。

!!演算子

もう一つの方法は、NullPointerExceptionが好きな人(NPE Lovers)用らしいです。null共用型のプロパティを呼び出す際に!!.を使います。
    ip!!.invert()
これはJavaの時と同じような動作をする。もしipがnulの場合、nullPointerExceptionを返す。

どれを使うべきか

状況に応じて、とするべきだろう。!!演算子は、javaと同じよう挙動を示すので、kotlinを使うメリットがあまり感じられない気がする。固いのは条件文でnullかどうかを検証する方法だが、コードが長くなる。でも確実。安全呼出しはchainで使うと有用だよと書かれているが、単発の呼出しは単にnullが来てもスルーする方法にしか思えない。このあたり理解不足か。

runメソッドの中身

こんな感じにしてみた。
    override fun run(ip: ImageProcessor?) {
        if(ip != null) {
            IJ.log("if-check")
            ip.invert()
            imp?.updateAndDraw()
        }
        IJ.wait(1000)

        IJ.log("safe call")
        ip?.invert()
        imp?.updateAndDraw()

        IJ.wait(1000)
        IJ.log("!!")
        ip!!.invert()
        imp?.updateAndDraw()
    }