Translate

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

2019年12月5日木曜日

Apache commons math の Levenberg-Marquardt法で、パラメータに制約条件を付ける

1か月ぶりの更新。
かなりマニアックなネタ。

Apache commons mathは、数値計算用のJavaのライブラリで、Levenberg-Marqurdt法によるフィッティングや、機械学習など、いろんなメソッドが用意されている。Fijiにも外部ライブラリとして利用されている。
2次元のガウス分布でのフィッティング(例えば蛍光輝点のフィッティングなど)には、
optimizationライブラリの、LevenbergarquardtOptimizerを使う。
詳しいやり方は省くが、

  1. モデル関数とそのモデル関数のjacobianを、それぞれMultivariateVectorFuncion, MultivariateMatrixFunctionとして作る
  2. LevenbergMarquardtOptimizerのオブジェクトを作る
    LevenbergMarquardtOptimizer lmo = new LevenbergMarquardtOptimizer();
  3. LeastSquaresProblemを作り、lmo.optimizeのパラメータとして渡す。返り値として、最適化されたパラメータを受け取る
    LeastSquaresOptimizer.Optimun lsoo = lmo.optimize(lsp); // lsp -> LeastSquaresProblemのオブジェクト
ここで、lspはLeastSquaresBuilderで作ることができる。
LeastSquaresBuilder lsb = new LeastSquaresBuilder();
lsb.model(modelfunction(), jacobianfunction()); // モデル関数とそのjacobian
lsb.target(data); // 入力データ
lsb.start(newStart); // パラメータの初期値
lsb.maxIterations(1000); // 最大itterationの回数

例えば有名なグラフ・統計解析ソフトのOriginでは、非線形フィッティングの際に、パラメータに制約条件を付けることができる。Apache commons mathでもできるが、詳しいやり方はちゃんと書いてない。一応、LeastSquaresのParameters Validationにそれっぽい記述がある。

In some cases, the model function requires parameters to lie within a specific domain. For example a parameter may be used in a square root and needs to be positive, or another parameter represents the sine of an angle and should be within -1 and +1, or several parameters may need to remain in the unit circle and the sum of their squares must be smaller than 1. The least square solvers available in Apache Commons Math currently don't allow to set up constraints on the parameters. This is a known missing feature. There are two ways to circumvent this.

Both ways are achieved by setting up a ParameterValidator instance. The input of the value and jacobian model functions will always be the output of the parameter validator if one exists.
どうやらこの、ParameterValidatorを使えばできそうな雰囲気だが、Javadocを見てもほとんど情報がない。ParameterValidatorはinterfaceで、validate(RealVector params)というメソッドを実装するインターフェースである。なんとなくこのparamsというのが怪しそうだが、RealVector型もよくわからん。

LeastSquaresBuilderで作ったlsbのメソッドで、parameterValidatorがあるのでなんとなく使ってみる。
lsb.parameterValidator(new ParameterValidator() { ... }
ここで、parametervValidatorの引数はParameterValidatorを実装するクラスだが、いちいちクラスを作るのは面倒臭いので内部クラスとして作る。
lsb.parameterValidator(new ParameterValidator() {
    @Override
    public RealVector validate(RealVector params) { ... }
});
Eclipseを使えば、インターフェースで実装するべきメソッドを自動で作ってくれる。ここで、先ほどの記述に戻ると、
The input of the value and jacobian model functions will always be the output of the parameter validator if one exists.
なんとなく訳すと、「値とjacobian モデル関数の入力は、Parameter Validatorが存在するときには常にその出力となります」というようなことが書いてあり、多分iterationのたんびにparameterValidatorが呼び出されることになりそう。あとは、パラメータにアクセスできればよい。 RealVector型であるparamsからパラメータにアクセスするには、
getEntry(int index)
setEntry(int index, double value)
メソッドを使えばよい。indexはパラメータを示すインデックスである。 例えば、
public RealVector validate(RealVector params) {
 System.out.println("parameter[0]: " + params.getEntry(0));
 return params;
}
としてフィッティングを行うと(ここではとあるモデル関数によるフィッティングを行っている)、
v0: 255.0
v0: 76.64496657297977
v0: 215.79832218070536
...
v0: 258.1698480422253
というように、iterationごとでv0がどのような値になったかを出力してくれるようになる。
従って、例えばv0は必ず0より大きな値をとる必要があるのであれば、
if (params.getEntry(0) < 0) params.setEntry(0, 0.00001);
とすれば、もしiterationの途中で0以下となったとしても、0.00001に強制的に引き戻すことができる(ここで0にすると、パラメータが0に張り付いてしまうことがあるので、微妙に0より大きな値にしている)。
これで、フィッティングのパラメータに制約を与えることができるようになった。

ちなみにこの方法をネット上で検索したが、英語のサイトでもまず見つからない。かなりレアな情報だと思う。

2019年10月11日金曜日

public static void mainとresourceへのアクセス

Public static void main

Eclipseでmavenを使ってjarファイルのpluginを作る場合、動作確認はjarファイルをImageJのpluginsフォルダに移して実行、ということが多い。作ってはImageJを立ち上げなおして、というのは結構面倒臭い。
そこで、public static void main(String args[])を作っておけば、EclipseのRunからJava applicationとして簡単にテストすることができる。
ijをインスタンスしなくても、ImageJのクラスは普通に使えるので、特にplugin filterを作る場合などに便利。
import java.net.URI;
import java.net.URISyntaxException;

import ij.IJ; import ij.ImagePlus; import ij.plugin.filter.PlugInFilter; import ij.process.ImageProcessor;
public class Hoge implements PlugInFilter {
@Override public int setup(String arg, ImagePlus imp) { // TODO Auto-generated method stub return DOES_ALL; }
@Override public void run(ImageProcessor ip) { // TODO Auto-generated method stub
}
public static void main(String[] args) { // TODO Auto-generated method stub URI img; try { img = ClassLoader.getSystemResource("test.tif").toURI(); ImagePlus imp = IJ.openImage(img.getPath()); Hoge hogehoge = new Hoge(); hogehoge.setup("", imp); hogehoge.run(imp.getProcessor()); } catch (URISyntaxException e) { // TODO Auto-generated catch block e.printStackTrace(); }
}

}

Hogeのオブジェクトを作り、setupでImagePlusを、runでImageProcessorを送ることができる。

Resourceフォルダへのアクセス

テスト用の画像データは、resourceフォルダに入れておく。resourceフォルダに入れたファイルは、以下のようにしてアドレスを取り出す。
URI img = ClassLoader.getSystemResource("test.tif").toURI();
ImagePlus imp = IJ.openImage(img.getPath());

これで開発はかなり楽になる。

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を最小限利用するのが良い。

2019年9月12日木曜日

findMaxima (MaximumFinder)で解析範囲を指定する-3

繰り返しになるが、MaximumFinderクラスは、コンストラクタはMaximumFinder()なので、
MaximumFinder mf = new MaximumFinder()
となる。メソッドである、getMaximaもfindMaximaも引数としては
java.awt.Polygon getMaxima(ImageProcessor ip, double tolerance, boolean excludeOnEdges)

ByteProcessor findMaxima(ImageProcessor ip, double tolerance, int outputType, boolean excludeOnEdges)
であり、impはない。しかし、findMaximaの中ではimpを参照していることから、mfにimpを入れないといけない。 そこで、改めてメソッドを眺めてみると、
int setup(java.lang.String arg, ImagePlus imp)
あった!ありました!
たまに見るsetupメソッドだが、MaximumFinderで唯一impをパラメータとしている。ソースを見てみると、
public int setup(String arg, ImagePlus imp) {
        this.imp = imp;
        return flags;
    }
だけ。impをメンバ変数のimpに代入するだけのメソッドである。これを使えばよい。 まとめると、non-rectangularなRoiを解析範囲として指定するためには、
imp.setRoi(ovalRaoi);
MaximumFinder mf = new MaximumFinder()
mf.setup("", imp);
poly = mf.getMaxima(ip, tolerance, false);
とすればよい。imp.setRoi(ovalRoi)の時点で、ipにもovalRoiがセットされている。 以上がMaximumFinderでnon-rectangularな解析範囲をする方法だ(と思う)。 ここまでたどり着くの相当疲れた。。

2019年9月11日水曜日

findMaxima (MaximumFinder)で解析範囲を指定する-2

findMaxima(...)の中身を見てみる。
まず、
Rectangle roi = ip.getRoi();
とあり、その後の処理(local maximaの探すところ)では、
for (int y=roi.y; y<roi.y+roi.height; y++) {         //find local minimum/maximum now

            for (int x=roi.x; x<roi.x+roi.width; x++) {      //ImageStatistics won't work if we have no ImagePlus

...

となっている。明らかに矩形のROIとしてしか取り出していない。
次に呼び出されるメソッドは、
getSortedMaxPoints(ip, typeP, excludeEdgesNow, isEDM, globalMin, globalMax, threshold)
であるが、このメソッドの中も
Rectangle roi = ip.getRoi();
となっていて、やはり矩形のROIとしてしか取り出していない。

さて、Maximaの探索には、さらに次のメソッド
analyzeAndMarkMaxima(ip, typeP, maxPoints, excludeEdgesNow, isEDM, globalMin, tolerance, strict, outputType, maxSortingError);
で行われている。このメソッドの中をみると、
        if (imp!=null)

            roi = imp.getRoi();

なんと、ipではなくimp (ImagePlus)からROIを取り出している。マジか!
さらにソースを斜め読みしていくと、
                            if (roi==null || roi.contains(x, y))

                                xyCoordinates.addPoint(x, y);
となっていた。これは、検出したMaximaがroiの中に含まれるかどうか判定している個所と考えられる。

findMaxima (MaximumFinder)で解析範囲を指定する-1

とても久しぶりに更新。
輝点検出に便利なfindMaxima。JavaではMaximumFinderクラスを使う。
コンストラクタは、シンプルにMaximumFinderのみ。
MaximumFinder mf = new MaximumFinder()
輝点検出には、主にgetMaximaか、findMaximaを使う。
java.awt.Polygon getMaxima(ImageProcessor ip, double tolerance, boolean excludeOnEdges)

ByteProcessor findMaxima(ImageProcessor ip, double tolerance, int outputType, boolean excludeOnEdges)
findMaximaの方は、outputTypeにMaximumFinder.Maskなどとしておけば、Mask画像を返してくれる。getMaximaの方は、輝点をPointRoiとして利用したい場合に便利。Polygonとしてもらっておけば、例えば、
poly = mf.getMaxima(ip, tolerance, false);

PointRoi pr = new PointRoi(poly.xpoints, poly.ypoints, poly.npoints);

imp.setRoi(pr);
とすれば、検出した輝点をPointRoiとして表示してくれる。

さて、画面全体に対して輝点検出する場合はこれでよいが、例えば画面の一部分ROIで区切った個所だけを検出することができると、MaximumFinderの説明文に書いてある。

Except for segmentation, this plugin works with area ROIs, including non-rectangular ROIs, which define the area where maxima are reported.

円形のROIを設定することにする。getMaximaの引数がImageProcessor ipなので、
ip.setRoi(ovalRoi);
poly = mf.getMaxima(ip, tolerance, false);
とでもすれば、 指定できるのかと思いきやうまくいかない。円なのに矩形領域になってしまう。
じゃあ、ImagePlusに指定すればいいのかと思い、
imp.setRoi(ovalRaoi);
としてもダメ。ImageJの掲示板に似たような議論があったが、ip.setRoiでうまくいくよ、と書いてあるけどうまくいかない。
discrepancy between APi and IJ.run in Find maxima

こういう時は、MaximumFinderのソースコードを見る必要がある。
MaximumFinder.java
例えば、上記のgetMaximaを見てみると、
public Polygon getMaxima(ImageProcessor ip, double tolerance, boolean excludeOnEdges) {
return getMaxima(ip, tolerance, excludeOnEdges, excludeOnEdges);}
public Polygon getMaxima(ImageProcessor ip, double tolerance, boolean strict, boolean excludeOnEdges) {
 findMaxima(ip, tolerance, strict, ImageProcessor.NO_THRESHOLD,
  MaximumFinder.POINT_SELECTION, excludeOnEdges, false);
 if (xyCoordinates==null)
  return new Polygon();
 else
return xyCoordinates;}
となっており、結局findMaximaを呼んでいることがわかる。
2へ続く

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と違い、すべてのメソッドを実装する必要はない。
以上