SeedPicker.java
package com.github.celldynamics.quimp.plugin.randomwalk;
import java.awt.FlowLayout;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.awt.event.ComponentAdapter;
import java.awt.event.ComponentEvent;
import java.awt.event.WindowAdapter;
import java.awt.event.WindowEvent;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import javax.swing.JButton;
import javax.swing.JFrame;
import javax.swing.JOptionPane;
import javax.swing.JPanel;
import javax.swing.SwingUtilities;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import com.github.celldynamics.quimp.QuimpException.MessageSinkTypes;
import com.github.celldynamics.quimp.utils.UiTools;
import ij.IJ;
import ij.ImagePlus;
import ij.gui.Line;
import ij.gui.Roi;
import ij.plugin.frame.RoiManager;
/**
* Simple seed picker by means of ROIs. Allow to select multiple foreground objects and one
* background. Produces {@link Seeds} structure at output.
*
* <p>Add current ROI to ROI list, if nothing selected look through ROI manager and renames
* un-renamed ROIs according to requested type (FG or BG).
*
* @author p.baniukiewicz
*
*/
@SuppressWarnings("serial")
public class SeedPicker extends JFrame {
/**
* The Constant LOGGER.
*/
static final Logger LOGGER = LoggerFactory.getLogger(SeedPicker.class.getName());
/**
* Prefix for foreground ROIs.
*/
public final String fgName = "fg";
/**
* Prefix for background ROI.
*/
public final String bgName = "bg";
/** The content pane. */
private JPanel contentPane;
/**
* Image used for seeding. It is not modified, only its dimensions are used.
*/
public ImagePlus image;
/** The rm. */
RoiManager rm;
/** The last tool. */
private String lastTool;
/** The last line width. */
private int lastLineWidth;
/** The last fg num. */
private int lastFgNum = 0; // cell number
/** The last bg num. */
private int lastBgNum = 0; // but this is index, cell is always 0 for BG
/** The bn finish listeners. */
// For Finish button we need specific order of ActionListeners
private ActionListener[] bnFinishListeners = new ActionListener[2];
/**
* Converted seeds available after Finish.
*/
public List<Seeds> seedsRoi = new ArrayList<>();
/** The btn finish. */
private JButton btnFinish;
/**
* Default constructor. Does not show window. Require {@link #image} to be set.
*/
public SeedPicker() {
this(false);
}
/**
* Allow to provide image to process and disable UI. Recommended to API calls.
*
* @param image image to seed, actually only size of it is required
* @param show true to show the UI
*/
public SeedPicker(ImagePlus image, boolean show) {
this(show);
this.image = image;
}
/**
* Allow to provide image to work with.
*
* @param image image to seed, actually only size of it is required
*/
public SeedPicker(ImagePlus image) {
this(true);
this.image = image;
}
/**
* Create the frame.
*
* @param show true to show the UI
*/
public SeedPicker(boolean show) {
setDefaultCloseOperation(JFrame.HIDE_ON_CLOSE);
setResizable(false);
setAlwaysOnTop(true);
setType(Type.NORMAL);
setTitle("Select seeds");
setVisible(show);
// setPreferredSize(new Dimension(300, 100));
// setBounds(100, 100, 295, 66);
contentPane = new JPanel();
setContentPane(contentPane);
contentPane.setLayout(new FlowLayout(FlowLayout.LEFT, 5, 5));
JButton tglbtnNewFg = new JButton("New FG");
UiTools.setToolTip(tglbtnNewFg,
"Create next FG label from current selection or from unassigned ROIs from Roi Manager"
+ ". For creating one label from separated ROIs,"
+ " add them first to Roi Manager and then use this button.");
tglbtnNewFg.addActionListener(new ActionListener() {
public void actionPerformed(ActionEvent e) {
if (!increaseRois(fgName)) {
// nothing added - add current selection
Roi current = image.getRoi();
if (current != null) {
rm.add(image, current, 0);
increaseRois(fgName);
}
}
}
});
contentPane.add(tglbtnNewFg);
JButton btnBackground = new JButton("New BG");
UiTools.setToolTip(btnBackground,
"Create next BG label from current selection or from unassigned ROIs from Roi Manager"
+ ". For creating one label from separated ROIs,"
+ " add them first to Roi Manager and then use this button.");
btnBackground.addActionListener(new ActionListener() {
public void actionPerformed(ActionEvent e) {
if (!increaseRois(bgName)) {
// nothing added - add current selection
Roi current = image.getRoi();
if (current != null) {
rm.add(image, current, 0);
increaseRois(bgName);
}
}
}
});
contentPane.add(btnBackground);
btnFinish = new JButton("Finish");
// this listener should go first, before that from RandomWalkView
bnFinishListeners[0] = new ActionListener() {
@Override
public void actionPerformed(ActionEvent e) {
try {
if (image == null) {
throw new RandomWalkException("No image opened with SeedPicker.");
}
seedsRoi = SeedProcessor.decodeSeedsfromRoiStack(Arrays.asList(rm.getRoisAsArray()),
fgName, bgName, image.getWidth(), image.getHeight(), image.getImageStackSize());
dispose();
} catch (RandomWalkException ex) {
ex.setMessageSinkType(MessageSinkTypes.GUI);
ex.handleException((JFrame) SwingUtilities.getRoot(contentPane),
"Problem with converting seeds from ROI");
}
}
};
btnFinish.addActionListener(new ActionListener() {
@Override
public void actionPerformed(ActionEvent e) {
bnFinishListeners[0].actionPerformed(e);
bnFinishListeners[1].actionPerformed(e);
}
});
contentPane.add(btnFinish);
addComponentListener(new ComponentAdapter() {
@Override
public void componentShown(ComponentEvent arg0) {
openRoiManager();
selectTools();
}
@Override
public void componentHidden(ComponentEvent e) {
IJ.setTool(lastTool);
Line.setWidth(lastLineWidth);
}
});
addWindowListener(new WindowAdapter() {
@Override
public void windowClosing(WindowEvent arg0) {
IJ.setTool(lastTool);
Line.setWidth(lastLineWidth);
}
@Override
public void windowClosed(WindowEvent e) {
IJ.setTool(lastTool);
Line.setWidth(lastLineWidth);
}
});
pack();
}
/**
* Add Listener to Finish button that is executed as second.
*
* <p>First listener prepares {@link SeedPicker#seedsRoi} structure, so second one can use it.
*
* @param list listener to add as second
*/
void addFinishController(ActionListener list) {
bnFinishListeners[1] = list;
}
/**
* Rename ROIs in RoiManager and increases counters.
*
* @param coreName ROI to rename
* @return true if something added
*/
private boolean increaseRois(String coreName) {
boolean ret = false;
if (coreName.equals(fgName)) {
ret = renameRois(fgName);
if (ret) { // rename FG
lastFgNum++; // and increase counter if anything renamed
}
}
if (coreName.equals(bgName)) {
ret = renameRois(bgName);
if (ret) {
lastBgNum++;
}
}
return ret;
}
/**
* Open roi manager.
*/
private void openRoiManager() {
rm = RoiManager.getRoiManager();
// rm.reset();
}
/**
* Select tools.
*/
private void selectTools() {
lastTool = IJ.getToolName();
lastLineWidth = Line.getWidth();
IJ.setTool("freeline");
Line.setWidth(10);
}
/**
* Rename ROIs in RoiManager.
*
* <p>Any nonrenamed ROIS will be renamed to format corenameCell_no, where Cell is cell number and
* no
* is ROI number for the cell. There can be many ROIs for one cell. For background there is only
* one object but again it cn be scribbled by many ROIs.
*
* @param coreName corename ROI will be renamed to
* @return true if anything was added.
*/
private boolean renameRois(String coreName) {
boolean added = false; // nothing renamed in manager
int localIndex; // for FG will progress and match global Index, for BG always 0
int localNum;
if (rm == null) {
return false;
}
if (coreName.equals(bgName)) {
localIndex = 0; // do not count backgrounds ROIs
localNum = lastBgNum;
} else {
localIndex = lastFgNum; // but count foreground
localNum = 0;
}
List<Roi> rois = Arrays.asList(rm.getRoisAsArray());
for (Roi roi : rois) {
String n = roi.getName();
if (n.startsWith(fgName) || n.startsWith(bgName)) {
continue;
}
int roiIndex = rm.getRoiIndex(roi);
rm.rename(roiIndex, coreName + localIndex + "_" + localNum);
roi.setName(coreName + localIndex + "_" + localNum);
localNum++;
added = true;
}
if (coreName.equals(bgName)) {
lastBgNum = localNum;
}
return added;
}
/**
* Prepare for new ROI selection. Clears all counters, roi manager and Seed list.
*/
public void reset() {
lastFgNum = 0;
lastBgNum = 0;
seedsRoi.clear();
if (rm != null) {
if (rm.getRoisAsArray().length != 0) {
int dialogButton = JOptionPane.YES_NO_OPTION;
int dialogResult =
JOptionPane.showConfirmDialog(null, "Clear ROI manager??", "Warning", dialogButton);
if (dialogResult == JOptionPane.YES_OPTION) {
rm.reset();
}
}
}
}
}