EclipseでImageJのPlugin作成 -stack画像にROIを描き、その数値データをグラフ化する-
今回は、数値データをグラフ化するプラグインを書いた。
コードは下記。
import java.awt.BorderLayout; import java.awt.Dimension; import java.awt.Graphics; import java.awt.Graphics2D; import java.awt.Toolkit; import java.awt.event.ActionEvent; import java.awt.event.ActionListener; import java.awt.event.MouseEvent; import java.awt.geom.Line2D; import javax.swing.JButton; import javax.swing.JComponent; import javax.swing.JFrame; import javax.swing.JLabel; import javax.swing.JPanel; import javax.swing.event.MouseInputAdapter; import ij.IJ; import ij.ImagePlus; import ij.io.OpenDialog; import ij.measure.ResultsTable; import ij.plugin.PlugIn; import ij.plugin.frame.RoiManager; public class Graph_Plot implements PlugIn, ActionListener { ImagePlus imp; //解析する画像 Graphics2D g2; MyCanvas mycan; RoiManager rm; JFrame frame; JButton openButton; JButton doneButton; JLabel xLabel; JLabel yLabel; JLabel x; //グラフ上でのデータの位置の表記用 JLabel val; //データの表記用 int number; //スライスの枚数 double time []; //時間の入れ物 double data []; //データの入れ物 double modiTime []; //補正後の時間の入れ物 double modiData[]; //補正後のデータの入れ物 /////////////////////////////////////////////////// //ファイルを開く public void openFile() { //画像の二重オープンを防ぐ openButton.setEnabled(false); //オープンダイアログで画像を開く OpenDialog od = new OpenDialog("Open"); String path = od.getDirectory() + od.getFileName(); imp = IJ.openImage(path); number = imp.getNSlices(); imp.show(); //Dataの入れ物の作成 data = new double[number]; time = new double [number]; modiData = new double [number]; modiTime = new double [number]; //グラフを作れるようにする doneButton.setEnabled(true); } /////////////////////////////////////////////////// //数値の入手 public void getValue() { //RoiManagerのリセット if (rm != null) rm.close(); //数値データの取得 rm = new RoiManager(); IJ.run("Set Measurements...", "area mean redirect=None decimal=3"); rm.addRoi(imp.getRoi()); ResultsTable rt = rm.multiMeasure(imp); for (int i = 0; i < number; i++) { time [i] = i; data[i] = rt.getValue(1, i); } rt = null; } /////////////////////////////////////////////////// //グラフに合わせるためにデータを拡大・縮小する public void modify () { //Min & Maxの検出 double min = data[0]; double max = data[0]; for (int i = 0; i < number; i++) { if (data[i] < min) min = data[i]; if (data[i] > max) max = data[i]; } double minValue = min; double maxValue = max; //グラフの拡大・縮小 double zx = 400.0/number; double zy = 300.0/(maxValue - minValue); for (int i = 0; i < number ; i++) { modiTime[i] = zx*time[i]; modiData[i] = 300 - ((data[i] - minValue)*zy); } } /////////////////////////////////////////////////// //グラフの作成 public void makeLine() { JFrame drawFrame = new JFrame ("Draw"); drawFrame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE); drawFrame.setBounds(0, 0, 460, 360); mycan = new MyCanvas(); drawFrame.getContentPane().add(mycan); mycan.addMouseListener (new MouseCheck()); mycan.addMouseMotionListener(new MouseCheck()); drawFrame.setVisible(true); } /////////////////////////////////////////////////// public class MyCanvas extends JComponent { public void paintComponent(Graphics g) { g2 = (Graphics2D)g; g2.draw(new Line2D.Double(50, 320, 460, 320)); g2.draw(new Line2D.Double(50, 10, 50, 320)); for (int i = 0; i < number - 1; i++) { g2.draw(new Line2D.Double(50 + modiTime[i], modiData[i] + 10, 50 + modiTime[i+1], modiData[i+1] + 10)); } } } /////////////////////////////////////////////////// //輝度値の表示 class MouseCheck extends MouseInputAdapter { public void mouseMoved(MouseEvent e) { int x0 = e.getX(); //JFrame上での座標をDataの位置になおす int xtrue = (x0 - 50) * number/400; //グラフ上でのみ数値を表記する if (x0 >= 50 && x0 < 400) { x.setText(String.valueOf(xtrue)); val.setText(String.valueOf(data[xtrue])); } else { x.setText(""); val.setText(""); } } } /////////////////////////////////////////////////// //コントローラーの作成 public void run (String arg) { Dimension screenSize = Toolkit.getDefaultToolkit().getScreenSize(); int sw = screenSize.width; frame = new JFrame("Graph"); frame.setBounds(sw-200, 10, 160, 200); openButton = new JButton ("Open"); doneButton = new JButton ("Done"); xLabel = new JLabel ("x = "); x = new JLabel (""); yLabel = new JLabel ("val = "); val = new JLabel (""); openButton.addActionListener(this); doneButton.addActionListener(this); openButton.setActionCommand("Open"); doneButton.setActionCommand("Done"); openButton.setBounds(35, 10 , 90, 30); doneButton.setBounds(35, 50, 90, 30); xLabel.setBounds(35, 90, 50, 30); x.setBounds(60, 90, 60, 30); yLabel.setBounds(25, 130, 50, 30); val.setBounds(60, 130, 90, 30); JPanel pane = new JPanel(); pane.setLayout(null); pane.add(openButton); pane.add(doneButton); pane.add(xLabel); pane.add(x); pane.add(val); pane.add(yLabel); doneButton.setEnabled(false); frame.getContentPane().add(pane, BorderLayout.CENTER); frame.setVisible(true); } /////////////////////////////////////////////////// //ボタンを押した時の反応 public void actionPerformed(ActionEvent e) { String cmd = e.getActionCommand(); if (cmd.equals("Open")) { openFile (); } else if (cmd.equals("Done")) { getValue(); modify(); makeLine(); } } }
このプラグインを実行すると、下記のコントローラーが立ち上がり、
Openボタンで画像を開き(今回も)、下記のようにROIを置いて Doneボタンを押すと、
stack画像をMultiMeasureして、その結果を下記のようなグラフとして表示する。
このグラフ上をマウスでなぞると、コントローラーの下の部分に、データの数値が表示される。
(テストサンプルとして、stack画像には以前の記事EclipseでImageJのPlugin作成 -マウスでクリックした場所にOval ROIを描き、Excelファイルにデータを書き出す。- - 生物屋さんのためのゼロからのプログラミングと同様に、三浦耕太先生著のImageJではじめる生物画像解析 | 学研メディカル秀潤社の第5章−1 (p197)のサンプル画像"ER-flow.tif"を使用した。)
いくつかコードを見ていく。
PC上では左上が(0, 0)の座標となっているが、我々がグラフを描くときには左下を(0, 0)の座標とすることが多い。
そのため、下記の部分でデータをグラフ化するために補正を行っている。
(ここでの、"300"や"400"はグラフのサイズ)
//グラフに合わせるためにデータを拡大・縮小する public void modify () { //Min & Maxの検出 double min = data[0]; double max = data[0]; for (int i = 0; i < number; i++) { if (data[i] < min) min = data[i]; if (data[i] > max) max = data[i]; } double minValue = min; double maxValue = max; //グラフの拡大・縮小 double zx = 400.0/number; double zy = 300.0/(maxValue - minValue); for (int i = 0; i < number ; i++) { modiTime[i] = zx*time[i]; modiData[i] = 300 - ((data[i] - minValue)*zy); } }
そして、拡大・縮小するためにDataの数値をDoubleに変更したため、Doubleのデータを使って線を描くために、下記の部分でGraphics2DのLine2D.Doubleを使用している。
public class MyCanvas extends JComponent { public void paintComponent(Graphics g) { g2 = (Graphics2D)g; g2.draw(new Line2D.Double(50, 320, 460, 320)); g2.draw(new Line2D.Double(50, 10, 50, 320)); for (int i = 0; i < number - 1; i++) { g2.draw(new Line2D.Double(50 + modiTime[i], modiData[i] + 10, 50 + modiTime[i+1], modiData[i+1] + 10)); } } }
また、グラフ上での数値データを読み込むために、下記でMouseInputAdapterを使用している。
//輝度値の表示 class MouseCheck extends MouseInputAdapter { public void mouseMoved(MouseEvent e) { int x0 = e.getX(); //JFrame上での座標をDataの位置になおす int xtrue = (x0 - 50) * number/400; //グラフ上でのみ数値を表記する if (x0 >= 50 && x0 < 400) { x.setText(String.valueOf(xtrue)); val.setText(String.valueOf(data[xtrue])); } else { x.setText(""); val.setText(""); } } }
とりあえず、数値データをグラフ化できる方法が分かったので、他のプラグイン開発に応用できそう。