I am currently analyzing images from potato tubers, to obtain dimentions of those tubers. As some tubers are touching in the pictures I need to watershed the image as to split them. However there are some tubers that are roughly shaped like a figure 8, although indentations in the middle are fairly small, which are splitted into two tubers. Would there be any way to differentiate between these and tubers touching. Perhaps some sort of threshold as to how deep an indentation needs to be before it is split?
Any tips or tricks are welcome, as I’m just a beginner.
Edit: Here is an example of what happends. I changed the image to 8 bit, and set an appropriate threshold. Then I used Erode and Dilate, from the BioVoxxel Toolbox, as to remove some background noise. After that I used watershed, which seperated several potatoes that were touching. However the one in this picture is also splitted, about were I drew the red line. As you can clearly see this is a single tuber, and therefore I would prefer if it doesn’t get split.
Since you are running the toolbox already, you can actually give the watershed irregular features a try. In its newest version it allows 3 different parameters to tweak the standard watershed towards fewer separations.
Setting the seperator size to 0 - 50 worked for me. However I tested only on 1 image. More will follow in the future.
@biovoxxel; why is there a erode cycle in your version. I already performed erode and dilate cycles before to get rid of back ground noize. And to what percentage does this erode cycle influence my final results in terms of area measurements?
@Luuklag There is also the possibility to perform the watershed on an 8-bit grayscale image (instead of binary) if you want, but it doesn’t appear to be available through the ImageJ UI. I do this regularly for my segmentation, it has worked better for me than watershed on binary but I had to utilize existing ImageJ code in my own Java code to do so, so it may be beyond what you are willing to do but thought I would throw it out there as an option.
The relevant class in ImageJ is MaximaFinder.
Here is how I would have used it on your images…
I blur the image of the tubers so that there is a “maxima” in each tuber. I call the MaximaFinder.findMaxima(…) method on the blurred image. This essentially sets the “seed” points for the watershed algorithm within the MaximaFinder object I have instantiated. However, now that the seed pionts are set, you can apply the watershed algorithm to the original tuber image (I find a slightly blurred version to produce smoother segments). The code thankfullly does clever things to avoid segmenting local maxima and minima besides the seed points. The watershed algorithm called this way is guaranteed to produce only one segmented region per maxima. Thus, if there is a 1-to-1 mapping between tubers and maxima, your segmented regions will have a 1-to-1 mapping as well. Blurring just prior to watershedding can cause a little inaccuracy at boundaries (i.e., your tuber boundary might overlap an adjacent tuber a tiny bit but won’t cut directly through your tuber as you have shown.) Here is the code for applying it to a grayscale image. (“this” refers to the MaximaFinder class that I copied into my own code. This code was added to that class.)
public ByteProcessor segmentImageUsingMaxima(ImageProcessor ip, boolean excludeOnEdges)
ImageStatistics stats = ImageStatistics.getStatistics(ip, ImageStatistics.MIN_MAX, null);
ByteProcessor outIp = this.make8bit(ip, this.savedTypeP, false, (long) stats.min, (long) stats.max, 0);
// if (IJ.debugMode) new ImagePlus("pixel types precleanup",
this.cleanupMaxima(outIp, this.savedTypeP, this.savedMaxPoints); // eliminate all the small maxima (i.e. those outside MAX_AREA)
// if (IJ.debugMode) new ImagePlus("pixel types postcleanup",
// (new ImagePlus("pre-watershed", outIp.duplicate())).show();
return null; // if user-cancelled, return
// (new ImagePlus("watershed segment", outIp)).show();
// if(!isEDM) // Assume ip is not an EDM
this.cleanupExtraLines(outIp); // eliminate lines due to local
// (none in EDM)
this.watershedPostProcess(outIp); // levels to binary image
The watershed algorithm applied to a binary image essentially turns your binary image into grayscale image of distances from boundaries. Here I’m using the intensity as the “distance” metric from the boundary. The particular location of the calculated boundaries using the approach above will be rather “wild” in the large regions between tubers, but that really doesn’t matter, you only care where the boundary is when you get close to the tuber, where the intensity is a good metric of distance and the boundary becomes more predictable. So taking images of your light colored tubers on a dark background would be important. Also, technically this code produces a segmented image. You would have to overlay the segmented lines on your thresholded image of your tubers to come up with a final product.