生物屋さんのためのゼロからのプログラミング

―忘れないための覚書 (たま~に更新)―

EclipseでImageJのPlugin作成 -ROI付き画像を名前をつけて保存する (Save images with ROI) -

今回は、ROIを置いた位置が分かるように、ROI付き画像に名前をつけて保存する方法を書いた。(ROIの位置確認用)
ImagePlus上のROIをBufferedImage上に移してBufferedImageを保存するという、かなりトリッキーなことをしたが、自分の目的に適っているので良しとした。

ソースコードは下記。

import java.awt.BasicStroke;
import java.awt.BorderLayout;
import java.awt.Color;
import java.awt.FileDialog;
import java.awt.Frame;
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.awt.event.MouseEvent;
import java.awt.event.MouseListener;
import java.awt.event.MouseMotionListener;
import java.awt.geom.Ellipse2D;
import java.awt.image.BufferedImage;
import java.io.FileOutputStream;
import java.io.IOException;

import javax.imageio.ImageIO;
import javax.swing.JButton;
import javax.swing.JFrame;
import javax.swing.JPanel;

import ij.IJ;
import ij.ImagePlus;
import ij.gui.ImageCanvas;
import ij.gui.OvalRoi;
import ij.io.OpenDialog;
import ij.plugin.PlugIn;
import ij.plugin.frame.RoiManager;
import ij.process.ImageProcessor;

public class ROI_Save implements PlugIn, MouseListener, MouseMotionListener, ActionListener {

	BufferedImage bim;
	JFrame imgFrame;
	MyPanel mypane; //BufferedImageを貼り付ける用のJPanel
	ImagePlus imp;
	ImageCanvas ic;
	ImageProcessor ip;
	Graphics2D g2;
	RoiManager rm;
	int x, y, h, w;
	int counter = 1;  //ROIの個数をカウントする用
	BasicStroke bs;

	//画像を開く
	public void openFile () {
		OpenDialog od = new OpenDialog("Select");
		String directory = od.getDirectory();
		String name = od.getFileName();
		String path = directory + name;
		imp = IJ.openImage(path);
		imp.show();
		h = imp.getHeight();
		w = imp.getWidth();
        ic = imp.getCanvas();  //ImageCanvasにImageを渡す
        ic.addMouseListener(this);
        ic.addMouseMotionListener(this);

        //ROIを表示できる様にRoiManagerを立ち上げる
        rm = new RoiManager();
        rm.runCommand(imp, "Show All");
        rm.runCommand(imp, "Show All with labels");

        //ImagePlusをBufferedImageに渡す
        bim = imp.getBufferedImage();

        //BufferedImageの画像を添付する用のJFrameの作成
        imgFrame = new JFrame ("Pic Test");
		imgFrame.setBounds(300, 300, w, h);
		imgFrame.setVisible(true);
		mypane = new MyPanel(w, h);
		imgFrame.getContentPane().add(mypane, BorderLayout.CENTER);
	}

	//BufferedImageの方にROIを置く
	public void drawRoi () {
		g2.draw(new Ellipse2D.Double(x-10, y-10, 20, 20));

		//ROIに番号をつける
		g2.drawString(String.valueOf(counter), x-3, y+3);
	}

	//出力画像の名前書き
	String writeFile () {
		FileDialog fd = new FileDialog (new Frame (), "保存", FileDialog.SAVE);
		fd.setVisible(true);
		String fullpath = fd.getDirectory() + fd.getFile();
		fd.dispose();
		return fullpath;
	}

	//保存ダイアログを使用して保存
	public void saveFile () {
		try {
			FileOutputStream fo = new FileOutputStream (this.writeFile());
			ImageIO.write(bim, "png", fo);
		} catch (IOException ex) {
			System.out.println("Miss");
		}
	}

	public class MyPanel extends JPanel {
		public MyPanel (int width, int height) {
			setSize (width, height);
		}

		public void paintComponent (Graphics g) {
			g.drawImage(bim, 0, 0, this);
			g2 = (Graphics2D)g;
			g2 = (Graphics2D) mypane.getGraphics();
			g2 = bim.createGraphics();
			bs = new BasicStroke(1.0f); //線の太さを決める
			g2.setPaint(Color.red);
			g2.setStroke(bs);
		}
	}

	@Override
	public void actionPerformed(ActionEvent e) {
		String cmd = e.getActionCommand();
		if (cmd.equals("Open")) {
			openFile();
		} else if (cmd.equals("Save")) {
			saveFile();
		}
	}

	public void run (String arg) {
		JFrame frame = new JFrame ("Controller");
		frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
		frame.setBounds(10, 10, 200, 100);
		frame.setVisible(true);

		JButton openButton = new JButton ("Open");
		JButton saveButton = new JButton ("Save");
		openButton.addActionListener(this);
		saveButton.addActionListener(this);
		openButton.setActionCommand("Open");
		saveButton.setActionCommand("Save");
		openButton.setBounds(40, 10, 90, 30);
		saveButton.setBounds(40, 50, 90, 30);

		JPanel pane = new JPanel();
		pane.setLayout(null);
		pane.add(openButton);
		pane.add(saveButton);
		frame.getContentPane().add(pane, BorderLayout.CENTER);
	}

	@Override
	public void mouseClicked(MouseEvent e) {

		//ImagePlus上でマウスがクリックされた場所の座標の取得
		x = ic.offScreenX(e.getX());
		y = ic.offScreenY(e.getY());

		//クリックされた場所を中心とした円を描き、RoiManagerに加える
		imp.setRoi(new OvalRoi(x-10, y-10, 20, 20));
		rm.addRoi(imp.getRoi());

		//BufferedImageにROIをつける
		drawRoi ();
		counter++;
	}

	@Override
	public void mousePressed(MouseEvent e) {
	}

	@Override
	public void mouseReleased(MouseEvent e) {
	}

	@Override
	public void mouseEntered(MouseEvent e) {
	}

	@Override
	public void mouseExited(MouseEvent e) {
	}

	@Override
	public void mouseDragged(MouseEvent e) {
	}

	@Override
	public void mouseMoved(MouseEvent e) {
	}

}

これを実行すると、
f:id:Aki-Miya:20180116083113p:plainのウインドウが立ち上がり、
"Open"ボタンを押すと、ダイアログが現れ、画像ファイルを選択できる。ここでは、またImageJにある"Clown"の画像を選択した。
f:id:Aki-Miya:20180116083341p:plain
画像を選択すると、上のように同じ画像が2つ現れる。1つはImagePlus、もう1つがBufferedImage(Pic Testのウインドウのもの)。また、ROIの位置を画像に表示させたままにするために、RoiManagerも立ち上がるようにした。

ImagePlus上に、下のように適当にROIを置き、(注:この段階ではBufferedImageの方には何故かROIは付かない。原因不明)
f:id:Aki-Miya:20180116083807p:plain

"Controller"ウインドウの"Save"ボタンを押すと、下のように保存用のダイアログが立ち上がり、BufferedImageの画像を保存できる (注:拡張子もつけないとダメ)。
f:id:Aki-Miya:20180116083653p:plain

保存した画像が下。
f:id:Aki-Miya:20180116084011p:plain
これで、とりあえずROI付きの画像を保存でき、解析したDataのどこにROIを置いたのかを後で確認できるようになった。


いくつかコードを見ていく。
本当はROIを置いた状態のImagePlusをそのまま保存したかったのだが、色々調べても分からなかったため、以前の記事ウインドウサイズを変えても、描画が消えない方法 - 生物屋さんのためのゼロからのプログラミングでGraphics2Dで描画した画像を保存する方法を書いたので、今回はそれを利用することにした。

  //ImagePlusをBufferedImageに渡す
        bim = imp.getBufferedImage();

そのため、上の部分でImagePlusをBufferedImageに渡して、BufferedImage上にROIを転写することにした。
下記の部分で、BufferedImage上にGraphics2Dを使ってROIを描けるようにした。

public class MyPanel extends JPanel {
		public MyPanel (int width, int height) {
			setSize (width, height);
		}

		public void paintComponent (Graphics g) {
			g.drawImage(bim, 0, 0, this);
			g2 = (Graphics2D)g;
			g2 = (Graphics2D) mypane.getGraphics();
			g2 = bim.createGraphics();
			bs = new BasicStroke(1.0f); //線の太さを決める
			g2.setPaint(Color.red);
			g2.setStroke(bs);
		}
	}

そして、ImageCanvas上のマウスをクリックした場所と同じBufferedImage上の場所にROIを置くのが下記。(ついでに、ImageCanvas上と同様にラベルをつけるようにもした)

//BufferedImageの方にROIを
	public void drawRoi () {
		g2.draw(new Ellipse2D.Double(x-10, y-10, 20, 20));

		//ROIに番号をつける
		g2.drawString(String.valueOf(counter), x-3, y+3);
	}

この

   g2.draw(new Ellipse2D.Double(x-10, y-10, 20, 20));

の使い方は、ImagePlus上にROIを置く下記の使い方

   imp.setRoi(new OvalRoi(x-10, y-10, 20, 20));

と同じである。