All Downloads are FREE. Search and download functionalities are using the official Maven repository.

com.github.celldynamics.quimp.geom.TrackOutline Maven / Gradle / Ivy

Go to download

QuimP software, a set of plugins for ImageJ to quantify spatio-temporal patterns of fluorescently labeled proteins in the cortex of moving cells.

The newest version!
package com.github.celldynamics.quimp.geom;

import java.awt.Color;
import java.util.ArrayList;
import java.util.Iterator;
import java.util.List;

import org.apache.commons.lang3.tuple.ImmutablePair;
import org.apache.commons.lang3.tuple.Pair;
import org.scijava.vecmath.Point2d;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import com.github.celldynamics.quimp.Outline;
import com.github.celldynamics.quimp.plugin.utils.QuimpDataConverter;

import ij.ImagePlus;
import ij.gui.PolygonRoi;
import ij.gui.Roi;
import ij.gui.Wand;
import ij.process.ImageProcessor;

/*
 * //!>
 * @startuml doc-files/TrackOutline_1_UML.png
 * User->(Create object)
 * User->(Convert Outlines to Point2d)
 * User->(get deep copy of Outlines)
 * @enduml
 * 
 * @startuml doc-files/TrackOutline_2_UML.png
 * actor User
 * User-->TrackOutline : <>\n""image"",""frame""
 * TrackOutline->prepare : ""image""
 * note left 
 * Filtering and BW
 * operations
 * endnote
 * prepare -> prepare : //open//
 * prepare->prepare : //close//
 * prepare->TrackOutline : ""prepared""
 * TrackOutline -> getOutlines
 * loop every pixel
 * getOutlines->getOutline : not background pixel [x,y]
 * getOutline->Wand : [x,y]
 * Wand->getOutline : ""xpoints"",""ypoints""
 * getOutline->SegmentedShapeRoi : <>
 * SegmentedShapeRoi-->getOutline
 * getOutline->getOutline : clear ROI on image
 * getOutline->SegmentedShapeRoi : set ""frame""
 * SegmentedShapeRoi-->getOutline
 * getOutline->getOutlines : ""SegmentedShapeRoi""
 * getOutlines->getOutlines : store ""SegmentedShapeRoi""
 * getOutlines->getOutlines : store ""Color""
 * end
 * getOutlines->TrackOutline
 * @enduml
 * 
 * //!<
 */
/**
 * Convert grayscale masks into list of vertices in correct order. Stand as ROI holder.
 * 
 * 

The algorithm uses IJ tools for tracking and filling (deleting) objects It goes through all * points of the image and for every visited point it checks whether the value is different than * defined background. If it is, the Wand tool is used to select object given by the pixel value * inside it. The ROI (outline) is then stored in this object and served as reference The ROI is * then used to delete selected object from image (using background value). Next, the algorithm * moves to next pixel (of the same image the object has been deleted from, so it is not possible to * detect the same object twice). * *

It assigns also frame number to outline
*
* Creating object runs also outline detection and tracking. Detected outlines are stored in object * and can be accessed by reference directly from outlines array or as copies from * getCopyofShapes().
*
* * @author p.baniukiewicz * @see com.github.celldynamics.quimp.geom.SegmentedShapeRoi * */ public class TrackOutline { /** * The Constant LOGGER. */ static final Logger LOGGER = LoggerFactory.getLogger(TrackOutline.class.getName()); /** * ROIs below this size (width or height) will be removed. * * @see #getOutlines(double, boolean) * @see #getOutlinesasPoints(double, boolean) * @see #getOutlinesColors(double, boolean) * @see #getOutlineasRawPoints() */ static final int SIZE_LIMIT = 10; /** * Original image. It is not modified. */ protected ImageProcessor imp; /** * Image under process. It is modified by Outline methods. */ private ImageProcessor prepared; /** * List of found outlines as ROIs. */ public ArrayList outlines; /** * List of colors of objects that were used to produce SegmentedShapeRoi. * *

This list is related to {@link TrackOutline#outlines}. Colors are encoded as rgb * {@link Color#Color(int)} */ public ArrayList colors; /** * The background color. */ protected int background; /** * Maximal number of searched objects, all objects if negative. */ private int maxNumObj = -1; /** * Frame for which imp has been got. */ private int frame; /** * Constructor from ImageProcessor. * * @param imp Image to process (not modified) * @param background Color value for background * @param frame Frame of stack that \a imp belongs to * @throws IllegalArgumentException when wrong image format is provided */ public TrackOutline(ImageProcessor imp, int background, int frame) { if (imp.getBitDepth() != 8 && imp.getBitDepth() != 16) { throw new IllegalArgumentException("Only 8-bit or 16-bit images are supported"); } outlines = new ArrayList<>(); colors = new ArrayList<>(); this.imp = imp; this.background = background; this.prepared = prepare(); this.frame = frame; getOutlines(); } /** * Constructor from ImageProcessor for single images. * * @param imp Image to process (not modified) * @param background Color value for background * @throws IllegalArgumentException when wrong image format is provided */ public TrackOutline(ImageProcessor imp, int background) { this(imp, background, 1); } /** * Default constructor. * * @param im Image to process (not modified), 8-bit, one slice * @param background Background color */ public TrackOutline(ImagePlus im, int background) { this(im.getProcessor(), background, 1); } /** * Filter input image to remove single pixels. * *

Implement closing followed by opening. * *

TODO If input image is grayscale this method may not work as expected, e.g. small pixels * with one color sticked to larger objects with another color will not be removed * * @return Filtered processor */ public ImageProcessor prepare() { ImageProcessor filtered = imp.duplicate(); // closing filtered.dilate(); filtered.erode(); // opening filtered.erode(); filtered.dilate(); return filtered; } /** * Get outline using Wand tool. * * @param col Any point inside region * @param row Any point inside region * @param color Color of object * @return ShapeRoi that contains ROI for given object with assigned frame to it * @throws IllegalArgumentException when wand was not able to find point */ SegmentedShapeRoi getOutline(int col, int row, int color) { Wand wand = new Wand(prepared); wand.autoOutline(col, row, color, color, Wand.EIGHT_CONNECTED); if (wand.npoints == 0) { throw new IllegalArgumentException("Wand: Points not found"); } Roi roi = new PolygonRoi(wand.xpoints, wand.ypoints, wand.npoints, Roi.FREEROI); clearRoi(roi, background); SegmentedShapeRoi ret = new SegmentedShapeRoi(roi); // create segmentation object ret.setFrame(frame); // set current frame to this object return ret; } /** * Try to find all outlines on image. * *

It is possible to limit number of searched outlines setting maxNumObj > 0 The algorithm goes * through every pixel on image and if this pixel is different than background (defined in * constructor) it uses it as source of Wand. Wand should outline found object, which is then * erased from image. then next pixel is analyzed. * *

Fills outlines field that contains list of all ROIs obtained for this image together with * frame number assigned to TrackOutline. * *

Skip very small object. * * @see #SIZE_LIMIT * */ private void getOutlines() { // go through the image and look for non background pixels outer: for (int r = 0; r < prepared.getHeight(); r++) { for (int c = 0; c < prepared.getWidth(); c++) { int pixel = prepared.getPixel(c, r); if (pixel != background) { // non background pixel // remember outline and delete it from input image SegmentedShapeRoi sr = getOutline(c, r, pixel); if (sr.getFloatWidth() < SIZE_LIMIT || sr.getFloatHeight() < SIZE_LIMIT) { continue; } outlines.add(sr); colors.add(new Color(pixel)); // store source color as rgb if (maxNumObj > -1) { if (outlines.size() >= maxNumObj) { LOGGER.warn("Reached maximal number of outlines"); break outer; } } } } } } /** * Convert found outlines to Outline. * * @param step resolution step * @param smooth true to use IJ polygon smoothing (running average). * @return List of Outline object that represents all * @see SegmentedShapeRoi#getOutlineasPoints() * @see #getOutlinesasPoints(double, boolean) * @see #getOutlinesColors(double, boolean) */ public List getOutlines(double step, boolean smooth) { Pair, List> ret = getOutlinesColors(step, smooth); return ret.getLeft(); } /** * Convert found outlines to Outline. * * @param step resolution step * @param smooth true to use IJ polygon smoothing (running average). * @return List of Outline object and colors of foreground pixels used to produce them coded as * rgb by {@link Color#Color(int)} * @see SegmentedShapeRoi#getOutlineasPoints() * @see #getOutlinesasPoints(double, boolean) */ public Pair, List> getOutlinesColors(double step, boolean smooth) { List rois = getCopyofShapes(); // convert to Outlines from ROIs ArrayList outlines = new ArrayList<>(); for (SegmentedShapeRoi sr : rois) { // interpolate and reduce number of points sr.setInterpolationParameters(step, false, smooth); Outline o; o = new QuimpDataConverter(sr.getOutlineasPoints()).getOutline(); outlines.add(o); } return new ImmutablePair, List>(outlines, colors); } /** * Reformat collected outlines and Colors to list of pairs. * * @param step resolution step * @param smooth true to use IJ polygon smoothing (running average). * @return List of pairs, outlines and colors of pixels they were created from */ public List> getPairs(double step, boolean smooth) { List out = getOutlinesColors(step, smooth).getLeft(); List> ret = new ArrayList<>(); Iterator ito = out.iterator(); Iterator itc = colors.iterator(); while (ito.hasNext() && itc.hasNext()) { Pair p = new ImmutablePair(ito.next(), itc.next()); ret.add(p); } return ret; } /** * Erase roi on image stored in object with color bckColor. * * @param roi roi on this image * @param bckColor color for erasing */ private void clearRoi(Roi roi, int bckColor) { prepared.setColor(bckColor); prepared.fill(roi); } /** * Convert found outlines to List. * * @param step step - step during conversion outline to points. For 1 every point from outline * is included in output list * @param smooth true for using running average during interpolation * @return List of List of ROIs * @see SegmentedShapeRoi#getOutlineasPoints() */ public List> getOutlinesasPoints(double step, boolean smooth) { List> ret = new ArrayList<>(); for (SegmentedShapeRoi sr : outlines) { sr.setInterpolationParameters(step, false, smooth); ret.add(sr.getOutlineasPoints()); } return ret; } /** * Convert found outlines to List without any interpolation. * * @return List of List of ROIs * * @see SegmentedShapeRoi#getOutlineasPoints() */ public List> getOutlineasRawPoints() { List> ret = new ArrayList<>(); for (SegmentedShapeRoi sr : outlines) { ret.add(sr.getOutlineasRawPoints()); } return ret; } /** * Return deep copy of Shapes. * * @return deep copy of Rois. */ public List getCopyofShapes() { ArrayList clon = new ArrayList<>(); for (SegmentedShapeRoi sr : outlines) { clon.add((SegmentedShapeRoi) sr.clone()); } return clon; } /** * Return shallow copy of Shapes. * * @return Rois found on image. * * @see #getColors() */ public List getShapes() { return outlines; } /** * Get colors of pixels that outlines were produced from. * *

Size of this array and order of elements correspond to {@link #getShapes()} and all * get methods in this class. * * @return the colors as RGB, created by {@link Color#Color(int)}. Integer can be retrieved by * summing up three RGB components. * * @see #getShapes() */ public ArrayList getColors() { return colors; } /** * Set {@link Roi#setStrokeColor(Color)} of each found Roi to color of pixels it was produced * from. * *

Modified are {@link SegmentedShapeRoi} stored in {@link #outlines} */ public void setColors() { Iterator ito = outlines.iterator(); Iterator itc = colors.iterator(); while (ito.hasNext() && itc.hasNext()) { ito.next().setStrokeColor(itc.next()); } } /* * (non-Javadoc) * * @see java.lang.Object#toString() */ @Override public String toString() { return "\nTrackOutline [outlines=" + outlines + "]"; } }





© 2015 - 2025 Weber Informatics LLC | Privacy Policy