Seeds.java
package com.github.celldynamics.quimp.plugin.randomwalk;
import java.awt.Color;
import java.util.ArrayList;
import java.util.List;
import org.apache.commons.math3.linear.RealMatrix;
import com.github.celldynamics.quimp.plugin.randomwalk.RandomWalkSegmentation.SeedTypes;
import ij.ImagePlus;
import ij.ImageStack;
import ij.process.ImageProcessor;
/**
* Keep information about seeds for different Seed types.
*
* <p>Seed type specifies type of object scribbled by user ({@link SeedTypes}, seed map is an binary
* image of size of scribbled (original) image where pixels selected by user are white.
*
* <p>Each seed map is stored as binary ImageProcessor but it is assumed that for one key
* ({@link SeedTypes}) there can be several separate seeds (e.g. for different objects that should
* be treated separately). This is common case for e.g. {@link SeedTypes#FOREGROUNDS}.
*
* <p>Internally each seed image is stored in {@link ImageStack} structure, note that <i>put</i>
* method does not verify is there is the same slice in stack already. Seed types other than
* {@link SeedTypes#FOREGROUNDS} usually have only one seed map associated, but it is also stored
* in {@link ImageStack}. One needs to take care about proper unwrapping data if standard
* {@link #get(Object)} is used.
*
* <p>Seed maps can be converted from various sources like RGB images or grayscale images but maps
* are always binary.
*
* @author p.baniukiewicz
* @see SeedProcessor
*/
@SuppressWarnings("serial")
public class Seeds extends ListMap<ImageProcessor> {
/**
* Default constructor.
*/
public Seeds() {
super();
}
/**
* Allow to set initial capacity.
*
* @param initialCapacity initial capacity
*/
public Seeds(int initialCapacity) {
super(initialCapacity);
}
/**
* Retrieve specified seed map from specified key.
*
* @param key key to retrieve
* @param slice seed map number (starts from 1)
* @return specified map or null if not available (neither key nor slice number)
*/
public ImageProcessor get(SeedTypes key, int slice) {
slice--;
List<ImageProcessor> tmp = super.get(key);
if (tmp == null) {
return null;
}
if (slice < 0 || slice >= tmp.size()) {
return null;
}
return tmp.get(slice);
}
/**
* Convert binary maps representing foregrounds and background seeds to lists of coordinates
* (format accepted by {@link RandomWalkSegmentation#solver(Seeds, RealMatrix[])}).
*
* <p>Assumes that there can be more labels for each key (e.g. few objects labelled as foreground
* that will be treated separately). Each label for specified key is converted to list of points
* separately as well.
*
* @param key which map to convert
* @return List of point coordinates accepted by RW algorithm for each label within specified key.
* If there is more labels for e.g. FOREGROUND key, each is converted to list of points
* separately. If seed map is empty (black) or key does not
* exist an empty list is returned.
* @see SeedProcessor#decodeSeedsfromRgb(ImagePlus, List, Color)
* @see SeedProcessor#decodeSeedsfromRgb(ImageProcessor, List, Color)
*/
public List<List<Point>> convertToList(SeedTypes key) {
List<ImageProcessor> seeds = get(key);
// output map integrating two lists of points
List<List<Point>> out = new ArrayList<>();
// Can be empty if points not found
if (seeds == null || seeds.isEmpty()) {
return out;
}
for (int s = 0; s < seeds.size(); s++) {
ImageProcessor slice = seeds.get(s);
List<Point> points = new ArrayList<>();
for (int x = 0; x < slice.getWidth(); x++) {
for (int y = 0; y < slice.getHeight(); y++) {
if (slice.get(x, y) > 0) {
points.add(new Point(x, y)); // remember foreground coords
}
}
}
out.add(points);
}
return out;
}
/**
* Convert seeds to ImageStack.
*
* @param key which map to convert
* @return Stack of seeds for selected key or null if key does not exist or there aren't maps
* under it.
*/
public ImageStack convertToStack(SeedTypes key) {
List<ImageProcessor> seeds = get(key);
if (seeds == null || seeds.isEmpty()) {
return null;
}
ImageProcessor tmp = seeds.get(0);
ImageStack ret = new ImageStack(tmp.getWidth(), tmp.getHeight());
for (ImageProcessor ip : seeds) {
ret.addSlice(ip);
}
return ret;
}
/*
* (non-Javadoc)
*
* @see java.lang.Object#toString()
*/
@Override
public String toString() {
return super.toString();
}
}