Difference between revisions of "Example Watershed with Seeds"
From BoofCV
Jump to navigationJump to searchm |
m |
||
Line 4: | Line 4: | ||
Example Code: | Example Code: | ||
* [https://github.com/lessthanoptimal/BoofCV/blob/v0. | * [https://github.com/lessthanoptimal/BoofCV/blob/v0.37/examples/src/main/java/boofcv/examples/segmentation/ExampleWatershedWithSeeds.java ExampleWatershedWithSeeds.java] | ||
Concepts: | Concepts: | ||
Line 29: | Line 29: | ||
*/ | */ | ||
public class ExampleWatershedWithSeeds { | public class ExampleWatershedWithSeeds { | ||
public static void main(String[] args) { | public static void main( String[] args ) { | ||
BufferedImage image = UtilImageIO.loadImage(UtilIO.pathExample("particles01.jpg")); | BufferedImage image = UtilImageIO.loadImage(UtilIO.pathExample("particles01.jpg")); | ||
GrayU8 input = ConvertBufferedImage.convertFromSingle(image, null, GrayU8.class); | GrayU8 input = ConvertBufferedImage.convertFromSingle(image, null, GrayU8.class); | ||
// declare working data | // declare working data | ||
GrayU8 binary = new GrayU8(input.width,input.height); | GrayU8 binary = new GrayU8(input.width, input.height); | ||
GrayS32 label = new GrayS32(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 | ||
// each other | // each other | ||
double mean = ImageStatistics.mean(input); | double mean = ImageStatistics.mean(input); | ||
ThresholdImageOps.threshold(input, binary, (int) mean, true); | ThresholdImageOps.threshold(input, binary, (int)mean, true); | ||
GrayU8 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; | ||
Line 50: | Line 49: | ||
WatershedVincentSoille1991 watershed = FactorySegmentationAlg.watershed(ConnectRule.FOUR); | WatershedVincentSoille1991 watershed = FactorySegmentationAlg.watershed(ConnectRule.FOUR); | ||
watershed.process(input,label); | watershed.process(input, label); | ||
GrayS32 output = watershed.getOutput(); | GrayS32 output = watershed.getOutput(); | ||
BufferedImage outLabeled = VisualizeBinaryData.renderLabeledBG(label, numRegions, null); | BufferedImage outLabeled = VisualizeBinaryData.renderLabeledBG(label, numRegions, null); | ||
VisualizeRegions.watersheds(output,image,1); | VisualizeRegions.watersheds(output, image, 1); | ||
// Removing the watersheds and update the region count | // Removing the watersheds and update the region count | ||
Line 61: | Line 60: | ||
watershed.removeWatersheds(); | watershed.removeWatersheds(); | ||
numRegions -= 1; | numRegions -= 1; | ||
BufferedImage outRegions = VisualizeRegions.regions(output,numRegions,null); | BufferedImage outRegions = VisualizeRegions.regions(output, numRegions, null); | ||
ListDisplayPanel gui = new ListDisplayPanel(); | ListDisplayPanel gui = new ListDisplayPanel(); |
Revision as of 18:40, 21 December 2020
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
}
}