Example Watershed with Seeds

From BoofCV
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
	}
}