Skip to content

Instantly share code, notes, and snippets.

@ctrueden
Last active March 25, 2019 21:21
Show Gist options
  • Star 2 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save ctrueden/ae89963150448ced6ef44f16be33c48a to your computer and use it in GitHub Desktop.
Save ctrueden/ae89963150448ced6ef44f16be33c48a to your computer and use it in GitHub Desktop.
Seeded watershed in ImageJ using BoofCV
#@dependency(group="org.boofcv", module="boofcv-core", version="0.33")
#@dependency(group="org.boofcv", module="boofcv-swing", version="0.32")
#@both ImagePlus imp
#@output ImagePlus (label="Watersheds") watersheds
#@output ImagePlus (label="Regions") regions
#@output ImagePlus (label="Seeds") seeds
import boofcv.alg.filter.binary.BinaryImageOps
import boofcv.alg.filter.binary.ThresholdImageOps
import boofcv.alg.misc.ImageStatistics
import boofcv.alg.segmentation.watershed.WatershedVincentSoille1991
import boofcv.factory.segmentation.FactorySegmentationAlg
import boofcv.gui.binary.VisualizeBinaryData
import boofcv.gui.feature.VisualizeRegions
import boofcv.io.image.ConvertBufferedImage
import boofcv.struct.ConnectRule
import boofcv.struct.image.GrayS32
import boofcv.struct.image.GrayU8
// Call BoofCV to perform an in-place seeded watershed segmentation.
// Adapted from:
// https://github.com/lessthanoptimal/BoofCV/blob/v0.33.1/examples/src/main/java/boofcv/examples/segmentation/ExampleWatershedWithSeeds.java
// - Accepts a BufferedImage as input and mutates it.
// - Returns [regions, seeds] as a two-element list.
def seededWatershed = { image ->
input = ConvertBufferedImage.convertFromSingle(image, null, GrayU8.class)
// declare working data
binary = new GrayU8(input.width,input.height)
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
mean = ImageStatistics.mean(input)
ThresholdImageOps.threshold(input, binary, (int) mean, true)
filtered = BinaryImageOps.erode8(binary, 2, null)
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.
watershed = FactorySegmentationAlg.watershed(ConnectRule.FOUR)
watershed.process(input,label)
output = watershed.getOutput()
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--
outRegions = VisualizeRegions.regions(output,numRegions,null)
return [outRegions, outLabeled]
}
result = seededWatershed(imp.getImage())
regions = new ij.ImagePlus("Regions", result[0])
seeds = new ij.ImagePlus("Seeds", result[1])
@ctrueden
Copy link
Author

Requires scijava-grab, not currently included with Fiji. Download it here and drop it into Fiji.app/jars.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment