EclipseでImageJのPlugin作成 -拡散シミュレーション(ランダムウォーク)-
今回は粒子の拡散シミュレーションを行うImageJのPluginを書いてみた。
コードは下記。
import java.awt.BorderLayout; import java.awt.FileDialog; import java.awt.Frame; import java.awt.event.ActionEvent; import java.awt.event.ActionListener; import javax.swing.JButton; import javax.swing.JFrame; import javax.swing.JPanel; import ij.IJ; import ij.ImagePlus; import ij.plugin.PlugIn; import ij.plugin.ZProjector; import ij.process.ImageProcessor; public class Simulator_ implements PlugIn, ActionListener{ ImagePlus imp, stackImp; ImageProcessor ip; int h, w; int sliceN = 500; //シミュレーションする時間 (フレーム数) int number = 1000; //粒子の数 //シミュレーションするためのキャンバスの作成 public void makeCanvas () { imp = IJ.openImage("何かしらの画像ファイルのディレクトリと名前"); imp.setTitle("Simulation"); w = imp.getWidth(); h = imp.getHeight(); imp.setRoi(0, 0, w, h); IJ.setBackgroundColor(0, 0, 0); IJ.run(imp, "Clear", "slice"); imp.killRoi(); //シミュレーションするためにstack画像にする for (int i = 0; i < sliceN; i++) { IJ.run(imp, "Add Slice", ""); } ip = imp.getProcessor(); } //拡散のシミュレーション public void simulate () { //粒子を作る int x[] = new int [number]; int y [] = new int [number]; //全ての粒子を中心に置く for (int i = 0; i < number; i++) { x[i] = w/2; y[i] = h/2; } imp.setSlice(1); ip.putPixelValue(w/2, h/2, 65535); //粒子の動きを描く for (int i = 2; i < sliceN; i++) { imp.setSlice(i); for (int j = 1; j < number; j++) { double th = 2.0*Math.PI*Math.random(); x[j] = x[j] + (int)(Math.cos(th)*10); y[j] = y[j] + (int)(Math.sin(th)*10); ip.putPixelValue(x[j], y[j], 65535 - 10*j); } } //シミュレーション結果を見やすくする IJ.run(imp, "Fire", ""); IJ.run("Brightness/Contrast..."); IJ.setMinAndMax(imp, 55000, 65535); imp.show(); } //シミュレーションのオーバーレイ画像を作る public void zStack () { stackImp = ZProjector.run(imp, "max"); IJ.setMinAndMax(stackImp, 55000, 65535); stackImp.show(); } //////シミュレーション結果を保存する//////// //保存する名前を付ける String addName () { FileDialog fd = new FileDialog(new Frame(), "名前を付ける. 拡張子は付けないで下さい.", FileDialog.SAVE); fd.setVisible(true); String path = fd.getDirectory() + fd.getFile(); fd.dispose(); return path; } //保存する public void saveImage () { String name = addName(); IJ.saveAs(imp, "TIFF", name); IJ.saveAs(stackImp, "TIFF", name + " stack"); } //コントローラーの作成 public void run (String arg) { JFrame frame = new JFrame ("Simulator"); frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE); frame.setBounds(10, 10, 180, 120); JButton doneButton = new JButton ("Done"); JButton saveButton = new JButton ("Save"); doneButton.addActionListener(this); saveButton.addActionListener(this); doneButton.setActionCommand("Done"); saveButton.setActionCommand("Save"); doneButton.setBounds(40, 10, 90, 30); saveButton.setBounds(40, 50, 90, 30); JPanel pane = new JPanel (); pane.setLayout(null); pane.add(doneButton); pane.add(saveButton); frame.getContentPane().add(pane, BorderLayout.CENTER); frame.setVisible(true); } //ボタンを押した時の反応 public void actionPerformed(ActionEvent e) { String cmd = e.getActionCommand(); if (cmd.equals("Done")) { makeCanvas (); simulate(); zStack(); } else if (cmd.equals("Save")) { saveImage(); } } }
このPluginを使うと、まず下のコントローラーが立ち上がる。
そして、Doneボタンを押すと、粒子の拡散シミュレーションを行い、
拡散シミュレーションの結果のstack画像と、それをZ-Projectionした下記の画像が作成される。
そしてSaveボタンを押せば、これらのシミュレーション結果を保存できる。
このシミュレーション方法はかなり簡便で、しかも実験データと同じ方法で解析できるので、かなり汎用性が高そうだ。
いくつかコードを見ていく。
今回は、拡散シミュレーションの結果を動画として取り扱うために、拡散シミュレーション結果をMuliti-tiffにすることにした。
そのため、まず下記のコードで、
//シミュレーションするためのキャンバスの作成 public void makeCanvas () { imp = IJ.openImage("何かしらの画像ファイルのディレクトリと名前"); imp.setTitle("Simulation"); w = imp.getWidth(); h = imp.getHeight(); imp.setRoi(0, 0, w, h); IJ.setBackgroundColor(0, 0, 0); IJ.run(imp, "Clear", "slice"); imp.killRoi(); //シミュレーションするためにstack画像にする for (int i = 0; i < sliceN; i++) { IJ.run(imp, "Add Slice", ""); } ip = imp.getProcessor(); }
適当な画像(ここでは512x512 pixelの16bit画像を使用)を開き、その画像の数値をクリアし、値を持たないstack画像を作成。このstack画像がシミュレーション結果を書き出すためのキャンバスになる。(stackの枚数がシミュレーション時間に相当)
そして、下記の部分で、
//全ての粒子を中心に置く for (int i = 0; i < number; i++) { x[i] = w/2; y[i] = h/2; } imp.setSlice(1); ip.putPixelValue(w/2, h/2, 65535);
全ての粒子を中心に配置 (初期状態)。
そして、下記の部分で個々の粒子の位置を各sliceに記述することで、粒子の経時変化をシミュレーション出来るようにした。
(今回は輝度値も時間と共に減少するようにし、粒子位置の経時変化をより見やすくした)
//粒子の動きを描く for (int i = 2; i < sliceN; i++) { imp.setSlice(i); for (int j = 1; j < number; j++) { double th = 2.0*Math.PI*Math.random(); x[j] = x[j] + (int)(Math.cos(th)*10); y[j] = y[j] + (int)(Math.sin(th)*10); ip.putPixelValue(x[j], y[j], 65535 - 10*j); //時間と共に粒子の輝度値が減少 } }