#@ File (label="Path to image") impath #@ int (label="delta (relative difference between threshold levels)", min=0, value=1) delta #@ int (label="Minimum region area", min=0, value=500) minarea #@ int (label="Maximum region area", min=0, value=2000) maxarea #@ float (label="Maximum variation of stability for the region", min=0, value=0.5) maxvar #@ String (label = "Identify bright/dark regions", choices={"Both (Default)", "Bright", "Dark"}, value = "Both (Default)") method #@ Boolean (Label = "Add regions to ROI manager", value=true) as_roi #@ Boolean (Label = "Generate mask", value=true) make_mask ''' MSER : Maximally Stable Extremal Region Detector This detector performs successive thresholding with different thresholds : from bright to dark AND dark to bright by default (but one can optionnally choose only one direction too) The Bright to Dark option rather identify bright regions and vice-versa For each threshold it performs a connected component analysis Then it compare the evolution of a connected region over the level of thresholds : the more variation in the size of the region over the threshold levels levels, the less stable is the region. Finally the script retruns a set of stable regions that fits in the criterium of area and stability defined by the user It retruns this region as a cloud of point, as well as a bounding box for the region. In this macro, the Hull is returned as a ROI stored into the roi manager A nice use can be to combine with the mask of the object using "AND" arithmetic so that we only retain the MSER region on the object ! TO DO : to prevent inclusion of regions within others, just paint all of them (directly in opencv for better performance), the ones inside others are just overwritten/covered by the largest (like in the Knime worklow) Will also fix the cases for which the convexHull do not work However it will cut regions that might overlap NB : image kept not with capital I to stick to the IMage input version for which variable image is a script parameters and therefore should be small ''' from org.bytedeco.javacpp.opencv_features2d import MSER from org.bytedeco.javacpp.opencv_core import RectVector, PointVectorVector #from org.bytedeco.javacpp.opencv_imgproc import convexHull, drawContours from ij import IJ, ImagePlus from ij.gui import Roi, PolygonRoi from ij.process import ByteProcessor from ij.plugin import LutLoader from ij.plugin.frame import RoiManager from ImageConverter import ImProcToMat from ijopencv.opencv import PointVectorPointRoiConverter from java.lang.System import getProperty # to get the path to a LUT import os if as_roi: # Initialise ROI manager RM = RoiManager() rm = RM.getRoiManager() # Define defaut paramters for detector - according to doc those are used only for color images so lets use the default only min_diversity = 1 max_evolution = 10 area_threshold = 1.01 min_margin = 0.003 edge_blur_size = 5 # Initialise detector detector = MSER.create(delta,minarea,maxarea,maxvar,min_diversity,max_evolution,area_threshold,min_margin,edge_blur_size) # Set unilateral threshold if selected if method != "Both (Default)": print "Set to unidirectionnal thresholding" detector.setPass2Only(True) # Open ImagePLus image = IJ.openImage(impath.getPath()) # Get ImageProcessor from ImagePlus ImProc = image.getProcessor() # Detect stable regions if method == "Dark": ImProc = ImProc.duplicate() # detach fron the original image to prevent in place change # invert the image before doing the unidirectionnal detection print "Invert image before unidirectionnal thresholding" ImProc.invert() # Convert image to 8-bit (scaling) opencv matrix ImCV = ImProcToMat(ImProc, Bit=8) # Initialise result vectors Regions = PointVectorVector() # Regions contains n cloud of points (one/region). Each cloud of points is a PointVector, hence Regions is a Vector of PointVector BBox = RectVector() # detect MSER regions detector.detectRegions(ImCV, Regions, BBox) # Initialise ROI converter ROIconverter = PointVectorPointRoiConverter() # Report the number of detected BBoxes (rather report it in a result table ?) N = BBox.size() message = "Found {} stable regions".format(N) print message IJ.log(message) if make_mask: # intialise a mask of same size than the image Width, Height = image.width, image.height Mask = ByteProcessor(Width, Height) # Loop over regions color = 1 # later used for mask to fill ROI for i in range(N): # HULL # not done in opencv because the input of the function should be a Mat while we have a PointVector # Recover the Point cloud as a PointVector PointVec = Regions.get(i) # Test to draw contour #drawContours(ImCV, PointVec, -1, 255, -1, 0, Mat(),0,Point(0,0)) # Convert to a IJ PointROI PointRoi = ROIconverter.convert(PointVec, float) # Do a convex Hull to prevent hollow shapes and reduce the number of points. Hull = PointRoi.getConvexHull() # Returned object is a Java Polygon (not a ROI) # Fix whem Hull fails if not Hull: # getConvexHull returns None sometime # Create a line connecting the points (allow to fit spline) LinePolygon = PointRoi.getContainedFloatPoints() # Convert this line back to a ROI (to be able to apply the spline) LineRoi = PolygonRoi(LinePolygon, 2) LineRoi.fitSpline() # Apply then the convexHull on this spline (directly on the Hull = LineRoi.getConvexHull() # Convert Polygon object back to a ROI (in both cases) HullRoi = PolygonRoi(Hull, 2) if as_roi: rm.addRoi(HullRoi) ''' if showBox: # Bounding box can be obtained by "Selection to bounding box" # Bounding Boxes box = BBox.get(i) BoxRoi = Roi(box.x(), box.y(), box.width(), box.height()) rm.addRoi(BoxRoi) ''' if make_mask: # Set fill color Mask.setColor(color) # Set current Roi on the image and fill #Mask.setRoi(roi) Mask.fill(HullRoi) # Update color for next ROI color+=1 if color==256: color=1 # reset color counter to stay in a 8-bit range # once looping over ROI is done if make_mask: # Set LUT to Glasbey PathLut = os.path.join(getProperty('fiji.dir'),'luts','glasbey_on_dark.lut') Lut = LutLoader.openLut(PathLut) Mask.setLut(Lut) # Display mask ImMask = ImagePlus("Mask", Mask) ImMask.show() if as_roi: # Bring image back in focus image.show() rm.runCommand("Show All with labels")