简体   繁体   中英

How to watershed(segment) an image in Java with BoofCV?

I am trying to segment a simple image using watershed function provided by BoofCV in Java. So I have writen (copied, edited and adjusted) the following code :

package alltestshere;

import boofcv.alg.filter.binary.BinaryImageOps;
import boofcv.alg.filter.binary.Contour;
import boofcv.alg.filter.binary.GThresholdImageOps;
import boofcv.gui.ListDisplayPanel;
import boofcv.gui.binary.VisualizeBinaryData;
import boofcv.gui.image.ShowImages;
import boofcv.io.UtilIO;
import boofcv.io.image.ConvertBufferedImage;
import boofcv.io.image.UtilImageIO;
import boofcv.struct.ConnectRule;
import boofcv.struct.image.GrayS32;
import boofcv.struct.image.GrayU8;
import java.awt.image.BufferedImage;
import java.util.List;
import boofcv.alg.segmentation.watershed.WatershedVincentSoille1991;
import boofcv.factory.segmentation.FactorySegmentationAlg;
import boofcv.gui.feature.VisualizeRegions;



public class examp {

   public static void main( String args[] ) {
    // load and convert the image into a usable format
    BufferedImage image = UtilImageIO.loadImage(UtilIO.pathExample("C:\\\\Users\\\\Caterina\\\\Downloads\\\\boofcv\\\\data\\\\example\\\\shapes\\\\shapes02.png"));
    // convert into a usable format
    GrayU8 input = ConvertBufferedImage.convertFromSingle(image, null, GrayU8.class);

//declare some of my working data
    GrayU8 binary = new GrayU8(input.width,input.height);
    GrayS32 markers = new GrayS32(input.width,input.height);

    // Select a global threshold using Otsu's method.
    GThresholdImageOps.threshold(input, binary, GThresholdImageOps.computeOtsu(input, 0, 255),true);

    //through multiple erosion you can obtain the sure foreground and use it as marker in order to segment the image
    GrayU8 filtered = new GrayU8 (input.width, input.height);
    GrayU8 filtered2 = new GrayU8 (input.width, input.height);
    GrayU8 filtered3 = new GrayU8 (input.width, input.height);
    BinaryImageOps.erode8(binary, 1, filtered);
    BinaryImageOps.erode8(filtered, 1, filtered2);
    BinaryImageOps.erode8(filtered2, 1, filtered3);

//count how many markers you have (one for every foreground part +1 for the background
    int numRegions = BinaryImageOps.contour(filtered3, ConnectRule.EIGHT, markers).size()+1 ;


    // Detect foreground imagea using an 8-connect rule
    List<Contour> contours = BinaryImageOps.contour(binary, ConnectRule.EIGHT, markers);

    //Watershed function which takes the original b&w image as input and the markers 
    WatershedVincentSoille1991 watershed = FactorySegmentationAlg.watershed(ConnectRule.FOUR);
    watershed.process(input, markers);

    //get the results of the watershed as output
    GrayS32 output = watershed.getOutput();


    // display the results
    BufferedImage visualBinary = VisualizeBinaryData.renderBinary(input, false, null);
    BufferedImage visualFiltered = VisualizeBinaryData.renderBinary(filtered3, false, null);
    BufferedImage visualLabel = VisualizeBinaryData.renderLabeledBG(markers , contours.size(), null);
    BufferedImage outLabeled = VisualizeBinaryData.renderLabeledBG(output, numRegions, null);


    ListDisplayPanel panel = new ListDisplayPanel();
    panel.addImage(visualBinary, "Binary Original");
    panel.addImage(visualFiltered, "Binary Filtered");
    panel.addImage(visualLabel, "Markers");
    panel.addImage(outLabeled, "Watershed");
    ShowImages.showWindow(panel,"Watershed");
    }

}

This code, however does not work well. Specifically, instead of colouring with different colours the foreground objects and leave the background as it may, it just splits all the image into region while each regions consists of only one foreground object and some part of the background and paints all this part with the same colour (picture 3). So, what do I do wrong?

I am uploading the Original Picture Markers Picture and Watershed Picture

Thanks in advance, Katerina

You get this result because you are not processing the background as a region. The markers you provide to watershed are only the contour of your shapes. Since the background isn't a region, the watershed algorithm splits it equally to each region. It is done equally because the distance in your original image of each shape to the background is the same (binary image).

If you want to get the background as another region, then provide to the watershed algorithm some points of the background as markers such as the corners for example.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM