Difference between revisions of "Example Watershed with Seeds"

From BoofCV
Jump to navigationJump to search
m
m
(2 intermediate revisions by the same user not shown)
Line 4: Line 4:


Example Code:
Example Code:
* [https://github.com/lessthanoptimal/BoofCV/blob/v0.18/examples/src/boofcv/examples/segmentation/ExampleWatershedWithSeeds.java ExampleWatershedWithSeeds.java]
* [https://github.com/lessthanoptimal/BoofCV/blob/v0.23/examples/src/boofcv/examples/segmentation/ExampleWatershedWithSeeds.java ExampleWatershedWithSeeds.java]


Concepts:
Concepts:
Line 31: Line 31:
public static void main(String[] args) {
public static void main(String[] args) {


BufferedImage image = UtilImageIO.loadImage("../data/applet/particles01.jpg");
BufferedImage image = UtilImageIO.loadImage(UtilIO.pathExample("particles01.jpg"));
ImageUInt8 input = ConvertBufferedImage.convertFromSingle(image, null, ImageUInt8.class);
GrayU8 input = ConvertBufferedImage.convertFromSingle(image, null, GrayU8.class);


// declare working data
// declare working data
ImageUInt8 binary = new ImageUInt8(input.width,input.height);
GrayU8 binary = new GrayU8(input.width,input.height);
ImageSInt32 label = new ImageSInt32(input.width,input.height);
GrayS32 label = new GrayS32(input.width,input.height);


// Try using the mean pixel value to create a binary image then erode it to separate the particles from
// Try using the mean pixel value to create a binary image then erode it to separate the particles from
Line 42: Line 42:
double mean = ImageStatistics.mean(input);
double mean = ImageStatistics.mean(input);
ThresholdImageOps.threshold(input, binary, (int) mean, true);
ThresholdImageOps.threshold(input, binary, (int) mean, true);
ImageUInt8 filtered = BinaryImageOps.erode8(binary, 2, null);
GrayU8 filtered = BinaryImageOps.erode8(binary, 2, null);
int numRegions = BinaryImageOps.contour(filtered, ConnectRule.EIGHT, label).size() + 1;
int numRegions = BinaryImageOps.contour(filtered, ConnectRule.EIGHT, label).size() + 1;
// +1 to regions because contour only counts blobs and not the background
// +1 to regions because contour only counts blobs and not the background
Line 52: Line 52:
watershed.process(input,label);
watershed.process(input,label);


ImageSInt32 output = watershed.getOutput();
GrayS32 output = watershed.getOutput();


BufferedImage outLabeled = VisualizeBinaryData.renderLabeledBG(label, numRegions, null);
BufferedImage outLabeled = VisualizeBinaryData.renderLabeledBG(label, numRegions, null);
Line 63: Line 63:
BufferedImage outRegions = VisualizeRegions.regions(output,numRegions,null);
BufferedImage outRegions = VisualizeRegions.regions(output,numRegions,null);


ShowImages.showWindow(outLabeled,"Seeds");
ListDisplayPanel gui = new ListDisplayPanel();
ShowImages.showWindow(outRegions,"Regions");
gui.addImage(image, "Watersheds");
ShowImages.showWindow(image,"Watersheds");
gui.addImage(outRegions, "Regions");
gui.addImage(outLabeled, "Seeds");
ShowImages.showWindow(gui, "Watershed", true);


// Additional processing would be needed for this example to be really useful.
// Additional processing would be needed for this example to be really useful.

Revision as of 22:36, 27 March 2016

Found watershed around particles drawn in red.

Watershed is one of the earlier and still more popular image segmentation routines. While very fast, one major problem it suffers from is that it generate an excessive number of regions. In certain applications you can overcome this problem by providing initial seeds to watershed.

Example Code:

Concepts:

  • Image Segmentation
  • Super Pixels

Related Examples:

Relevant Applets:

Example Code

/**
 * Watershed image segmentation will often produce an excessive number of regions since each local minimum is the
 * seed which creates a new region.  To get around this problem you can provide the seeds manually.  The down side
 * to providing manual seeds is that it is no longer a general purpose algorithm and requires knowledge of the image
 * structure to provide the seeds.  This example demonstrates how to do this.
 *
 * @author Peter Abeles
 */
public class ExampleWatershedWithSeeds {
	public static void main(String[] args) {

		BufferedImage image = UtilImageIO.loadImage(UtilIO.pathExample("particles01.jpg"));
		GrayU8 input = ConvertBufferedImage.convertFromSingle(image, null, GrayU8.class);

		// declare working data
		GrayU8 binary = new GrayU8(input.width,input.height);
		GrayS32 label = new GrayS32(input.width,input.height);

		// Try using the mean pixel value to create a binary image then erode it to separate the particles from
		// each other
		double mean = ImageStatistics.mean(input);
		ThresholdImageOps.threshold(input, binary, (int) mean, true);
		GrayU8 filtered = BinaryImageOps.erode8(binary, 2, null);
		int numRegions = BinaryImageOps.contour(filtered, ConnectRule.EIGHT, label).size() + 1;
		// +1 to regions because contour only counts blobs and not the background

		// The labeled image can be used as is.  A precondition for seeded watershed is that all seeds have an
		// ID > 0.  Luckily, a value of 0 was used for background pixels in the contour algorithm.
		WatershedVincentSoille1991 watershed = FactorySegmentationAlg.watershed(ConnectRule.FOUR);

		watershed.process(input,label);

		GrayS32 output = watershed.getOutput();

		BufferedImage outLabeled = VisualizeBinaryData.renderLabeledBG(label, numRegions, null);
		VisualizeRegions.watersheds(output,image,1);

		// Removing the watersheds and update the region count
		// NOTE: watershed.getTotalRegions() does not return correct results if seeds are used!
		watershed.removeWatersheds();
		numRegions -= 1;
		BufferedImage outRegions = VisualizeRegions.regions(output,numRegions,null);

		ListDisplayPanel gui = new ListDisplayPanel();
		gui.addImage(image, "Watersheds");
		gui.addImage(outRegions, "Regions");
		gui.addImage(outLabeled, "Seeds");
		ShowImages.showWindow(gui, "Watershed", true);

		// Additional processing would be needed for this example to be really useful.
		// The watersheds can be used to characterize the background while the seed binary image the particles
		// From this the particles could be more accurately classified by assigning each pixel one of the two
		// just mentioned groups based distance
	}
}