class Slic(Segmenter, SkimageSegmenter): """Run SLIC (Simple Linear Iterative Clustering) segmentation.""" def __init__(self, n_segments=35, sigma=2.0, compactness=10.0, border_color='Yellow', border_outline='No'): """Constructor. Parameters ---------- n_segments : integer, optional, default = 250 The (approximate) number of labels in the segmented output image. sigma : float, optional, default = 5.0 Width of Gaussian smoothing kernel for pre-processing. compactness : float, optional, default = 10.0 Higher values give more weight to space proximity, making superpixel shapes more square/cubic. border_color : string X11Color name of segment border color. border_outline : string If 'yes' double the size of segment border. """ super(self.__class__, self).__init__(border_color, border_outline) self.n_segments = Config("Segments", n_segments, int) self.sigma = Config("Sigma", sigma, float) self.compactness = Config("Compactness", compactness, float) def get_config(self): """Return configuration of segmenter. Returns ------- config : OrderedDict Current configs of segmenter. """ slic_config = OrderedDict() slic_config["n_segments"] = self.n_segments slic_config["sigma"] = self.sigma slic_config["compactness"] = self.compactness slic_config["border_color"] = self.border_color slic_config["border_outline"] = self.border_outline return slic_config def set_config(self, configs): """Update configuration of segmenter. Parameters ---------- configs : OrderedDict New configs of segmenter. """ self.n_segments = Config.nvl_config(configs["n_segments"], self.n_segments) self.sigma = Config.nvl_config(configs["sigma"], self.sigma) self.compactness = Config.nvl_config(configs["compactness"], self.compactness) self.border_color = Config.nvl_config(configs["border_color"], self.border_color) self.border_outline = Config.nvl_config(configs["border_outline"], self.border_outline) self.border_outline.value = self.border_outline.value if self.border_outline.value == 'Yes' else 'No' def get_summary_config(self): """Return fomatted summary of configuration. Returns ------- summary : string Formatted string with summary of configuration. """ slic_config = OrderedDict() slic_config[self.n_segments.label] = self.n_segments.value slic_config[self.sigma.label] = self.sigma.value slic_config[self.compactness.label] = self.compactness.value slic_config[self.border_color.label] = self.border_color.value slic_config[self.border_outline.label] = self.border_outline.value summary = '' for config in slic_config: summary += "%s: %s\n" % (config, str(slic_config[config])) return summary def get_list_segments(self): """Return a list with segments after apply segmentation. Returns ------- segments : list List of segments of segmented image. """ return self.get_list_segments_skimage() def get_segment(self, px=0, py=0, idx_segment=None, path_to_mask=None): """Return a specified segment using a index or position in image. Parameters ---------- px : integer, optional, default = 0 Segment point inside the image in x-axis. py : integer, optional, default = 0 Segment point inside the image in y-axis. idx_segment : integer, optional, default = None Index of segment returned by previous call of this method. Returns ------- segment : opencv 3-channel color image. Rectangle encompassing the segment image. size_segment : integer Number of pixels of segment. idx_segment : integer Index of segment if found, -1 otherwise. run_time : integer Running time spent in milliseconds. """ return self.get_segment_skimage(px, py, idx_segment, path_to_mask) def paint_segment(self, image, color, px=0, py=0, idx_segment=[], border=True, clear=False): """Paint a list of segments using a index or position in image. Parameters ---------- image : opencv 3-channel color image. Segmented image. color : string X11Color name. px : integer, optional, default = 0 Segment point inside the image in x-axis. py : integer, optional, default = 0 Segment point inside the image in y-axis. idx_segment : list, optional, default = [] List of segments. border : boolean, optional, default = True If true paint the border of segments with default color. clear : boolean, optional, default = False If true clear previous painting in the image. Returns ------- new_image : opencv 3-channel color image. Painted image. run_time : integer Running time spent in milliseconds. """ return self.paint_segment_skimage(image, color, px, py, idx_segment, border, clear) def run(self, image): """Perform the segmentation Parameters ---------- image : opencv 3-channel color image. Original image. Returns ------- new_image : opencv 3-channel color image. Segmented image. run_time : integer Running time spent in milliseconds. """ args = { "n_segments": self.n_segments.get_cast_val(), "sigma": self.sigma.get_cast_val(), "compactness": self.compactness.get_cast_val() } return self.run_skimage(image, slic, **args) def reset(self): """Clean all data of segmentation. """ return self.reset_skimage()
class Felzenszwalb(Segmenter, SkimageSegmenter): """Run Felzenszwalb's method segmentation.""" def __init__(self, scale=100.0, sigma=1.0, min_size=20, border_color='Yellow', border_outline='No'): """Constructor. Parameters ---------- scale : integer, float, default = 100.0 Free parameter. Higher means larger clusters. sigma : float, optional, default = 1.0 Width of Gaussian kernel used in preprocessing. min_size : integer, optional, default = 20 Minimum component size. Enforced using postprocessing. border_color : string X11Color name of segment border color. border_outline : string If 'yes' double the size of segment border. """ super(self.__class__, self).__init__(border_color, border_outline) self.scale = Config("Scale", scale, float) self.sigma = Config("Sigma", sigma, float) self.min_size = Config("Min Size", min_size, int) def get_config(self): """Return configuration of segmenter. Returns ------- config : OrderedDict Current configs of segmenter. """ felzenszwalb_config = OrderedDict() felzenszwalb_config["scale"] = self.scale felzenszwalb_config["sigma"] = self.sigma felzenszwalb_config["min_size"] = self.min_size felzenszwalb_config["border_color"] = self.border_color felzenszwalb_config["border_outline"] = self.border_outline return felzenszwalb_config def set_config(self, configs): """Update configuration of segmenter. Parameters ---------- configs : OrderedDict New configs of segmenter. """ self.scale = Config.nvl_config(configs["scale"], self.scale) self.sigma = Config.nvl_config(configs["sigma"], self.sigma) self.min_size = Config.nvl_config(configs["min_size"], self.min_size) self.border_color = Config.nvl_config(configs["border_color"], self.border_color) self.border_outline = Config.nvl_config(configs["border_outline"], self.border_outline) self.border_outline.value = self.border_outline.value if self.border_outline.value == 'Yes' else 'No' def get_summary_config(self): """Return fomatted summary of configuration. Returns ------- summary : string Formatted string with summary of configuration. """ felzenszwalb_config = OrderedDict() felzenszwalb_config[self.scale.label] = self.scale.value felzenszwalb_config[self.sigma.label] = self.sigma.value felzenszwalb_config[self.min_size.label] = self.min_size.value felzenszwalb_config[self.border_color.label] = self.border_color.value felzenszwalb_config[ self.border_outline.label] = self.border_outline.value summary = '' for config in felzenszwalb_config: summary += "%s: %s\n" % (config, str(felzenszwalb_config[config])) return summary def get_list_segments(self): """Return a list with segments after apply segmentation. Returns ------- segments : list List of segments of segmented image. """ return self.get_list_segments_skimage() def get_segment(self, px=0, py=0, idx_segment=None, path_to_mask=None): """Return a specified segment using a index or position in image. Parameters ---------- px : integer, optional, default = 0 Segment point inside the image in x-axis. py : integer, optional, default = 0 Segment point inside the image in y-axis. idx_segment : integer, optional, default = None Index of segment returned by previous call of this method. Returns ------- segment : opencv 3-channel color image. Rectangle encompassing the segment image. size_segment : integer Number of pixels of segment. idx_segment : integer Index of segment if found, -1 otherwise. run_time : integer Running time spent in milliseconds. """ return self.get_segment_skimage(px, py, idx_segment, path_to_mask) def paint_segment(self, image, color, px=0, py=0, idx_segment=[], border=True, clear=False): """Paint a list of segments using a index or position in image. Parameters ---------- image : opencv 3-channel color image. Segmented image. color : string X11Color name. px : integer, optional, default = 0 Segment point inside the image in x-axis. py : integer, optional, default = 0 Segment point inside the image in y-axis. idx_segment : list, optional, default = [] List of segments. border : boolean, optional, default = True If true paint the border of segments with default color. clear : boolean, optional, default = False If true clear previous painting in the image. Returns ------- new_image : opencv 3-channel color image. Painted image. run_time : integer Running time spent in milliseconds. """ return self.paint_segment_skimage(image, color, px, py, idx_segment, border, clear) def run(self, image): """Perform the segmentation Parameters ---------- image : opencv 3-channel color image. Original image. Returns ------- new_image : opencv 3-channel color image. Segmented image. run_time : integer Running time spent in milliseconds. """ args = { "scale": self.scale.get_cast_val(), "sigma": self.sigma.get_cast_val(), "min_size": self.min_size.get_cast_val() } return self.run_skimage(image, felzenszwalb, **args) def reset(self): """Clean all data of segmentation. """ return self.reset_skimage()
class Quickshift(Segmenter, SkimageSegmenter): """Run Quickshift segmentation.""" def __init__(self, ratio=0.5, kernel_size=2.0, max_dist=10.0, border_color='Yellow', border_outline='No'): """Constructor. Parameters ---------- ratio : float, optional, between 0 and 1, default = 0.5 Balances color-space proximity and image-space proximity. Higher values give more weight to color-space. kernel_size : float, optional, default = 2.0 Width of Gaussian kernel used in smoothing the sample density. Higher means fewer clusters. max_dist : float, optional, default = 10.0 Cut-off point for data distances. Higher means fewer clusters. border_color : string X11Color name of segment border color. border_outline : string If 'yes' double the size of segment border. """ super(self.__class__, self).__init__(border_color, border_outline) self.ratio = Config("Ratio [0-1]", ratio, float) self.kernel_size = Config("Kernel Size", kernel_size, float) #self.sigma = Config("Sigma", sigma, float) self.max_dist = Config("Max Dist", max_dist, float) def get_config(self): """Return configuration of segmenter. Returns ------- config : OrderedDict Current configs of segmenter. """ quickshift_config = OrderedDict() quickshift_config["ratio"] = self.ratio quickshift_config["kernel_size"] = self.kernel_size #quickshift_config["sigma"] = self.sigma quickshift_config["max_dist"] = self.max_dist quickshift_config["border_color"] = self.border_color quickshift_config["border_outline"] = self.border_outline return quickshift_config def set_config(self, configs): """Update configuration of segmenter. Parameters ---------- configs : OrderedDict New configs of segmenter. """ self.ratio = Config.nvl_config(configs["ratio"], self.ratio) self.kernel_size = Config.nvl_config(configs["kernel_size"], self.kernel_size) #self.sigma = Config.nvl_config(configs["sigma"], self.sigma) self.max_dist = Config.nvl_config(configs["max_dist"], self.max_dist) self.border_color = Config.nvl_config(configs["border_color"], self.border_color) self.border_outline = Config.nvl_config(configs["border_outline"], self.border_outline) self.ratio.value = self.ratio.value if self.ratio.value <= 1 else 1 self.border_outline.value = self.border_outline.value if self.border_outline.value == 'Yes' else 'No' def get_summary_config(self): """Return fomatted summary of configuration. Returns ------- summary : string Formatted string with summary of configuration. """ quickshift_config = OrderedDict() quickshift_config[self.ratio.label] = self.ratio.value quickshift_config[self.kernel_size.label] = self.kernel_size.value #quickshift_config[self.sigma.label] = self.sigma.value quickshift_config[self.max_dist.label] = self.max_dist.value quickshift_config[self.border_color.label] = self.border_color.value quickshift_config[ self.border_outline.label] = self.border_outline.value summary = '' for config in quickshift_config: summary += "%s: %s\n" % (config, str(quickshift_config[config])) return summary def get_list_segments(self): """Return a list with segments after apply segmentation. Returns ------- segments : list List of segments of segmented image. """ return self.get_list_segments_skimage() def get_segment(self, px=0, py=0, idx_segment=None, path_to_mask=None): """Return a specified segment using a index or position in image. Parameters ---------- px : integer, optional, default = 0 Segment point inside the image in x-axis. py : integer, optional, default = 0 Segment point inside the image in y-axis. idx_segment : integer, optional, default = None Index of segment returned by previous call of this method. Returns ------- segment : opencv 3-channel color image. Rectangle encompassing the segment image. size_segment : integer Number of pixels of segment. idx_segment : integer Index of segment if found, -1 otherwise. run_time : integer Running time spent in milliseconds. """ return self.get_segment_skimage(px, py, idx_segment, path_to_mask) def paint_segment(self, image, color, px=0, py=0, idx_segment=[], border=True, clear=False): """Paint a list of segments using a index or position in image. Parameters ---------- image : opencv 3-channel color image. Segmented image. color : string X11Color name. px : integer, optional, default = 0 Segment point inside the image in x-axis. py : integer, optional, default = 0 Segment point inside the image in y-axis. idx_segment : list, optional, default = [] List of segments. border : boolean, optional, default = True If true paint the border of segments with default color. clear : boolean, optional, default = False If true clear previous painting in the image. Returns ------- new_image : opencv 3-channel color image. Painted image. run_time : integer Running time spent in milliseconds. """ return self.paint_segment_skimage(image, color, px, py, idx_segment, border, clear) def run(self, image): """Perform the segmentation Parameters ---------- image : opencv 3-channel color image. Original image. Returns ------- new_image : opencv 3-channel color image. Segmented image. run_time : integer Running time spent in milliseconds. """ args = { "ratio": self.ratio.get_cast_val(), "kernel_size": self.kernel_size.get_cast_val(), #"sigma": self.sigma.get_cast_val(), "max_dist": self.max_dist.get_cast_val() } return self.run_skimage(image, quickshift, **args) def reset(self): """Clean all data of segmentation. """ return self.reset_skimage()
class SkimageSegmenter(object): """Abstract class for segmenters implemented in skimage.segmentation.""" __metaclass__ = ABCMeta # flag FORCE_OPT highly increases speed of method paint_segment_skimage but performs a flawed painting FORCE_OPT = True def __init__(self, border_color='Yellow', border_outline='No'): """Constructor. Parameters ---------- border_color : string X11Color name of segment border color. border_outline : string If 'yes' double the size of segment border. """ self.border_color = Config("Border Color", border_color, 'color') self.border_outline = Config("Border Outline", border_outline, str) self._segments = None self._original_image = None def get_list_segments_skimage(self): """Return a list with segments after apply segmentation. Returns ------- segments : list List of segments of segmented image. """ if self._segments is None: return [] # print(np.unique(self._segments)) return np.unique(self._segments) def get_segment_skimage(self, px=0, py=0, idx_segment=None, path_to_mask=None): """Return a specified segment using a index or position in image. Parameters ---------- px : integer, optional, default = 0 Segment point inside the image in x-axis. py : integer, optional, default = 0 Segment point inside the image in y-axis. idx_segment : integer, optional, default = None Index of segment returned by previous call of this method. Returns ------- segment : opencv 3-channel color image. Rectangle encompassing the segment image. size_segment : integer Number of pixels of segment. idx_segment : integer Index of segment if found, -1 otherwise. run_time : integer Running time spent in milliseconds. """ # Check if segmentation was already performed if self._segments is None: return None, 0, -1, 0 start_time = TimeUtils.get_time() # If idx_segment not passed, get the segment index using the points (px, py) if idx_segment is None: idx_segment = self._segments[py, px] # Create a mask, painting black all pixels outside of segment and white the pixels inside. mask_segment = np.zeros(self._original_image.shape[:2], dtype="uint8") mask_segment[self._segments == idx_segment] = 255 minas_mask_segment = mask_segment = np.zeros( self._original_image.shape[:2], dtype="uint8") minas_mask_segment[self._segments == idx_segment] = 1 # minas_idx_segment = idx_segment txt = np.loadtxt(path_to_mask) txt = np.add(txt, minas_mask_segment) np.savetxt(path_to_mask, txt, fmt='%d') print("Modified mask: ", path_to_mask) size_segment = mask_segment[self._segments == idx_segment].size segment = self._original_image.copy() segment = cv2.bitwise_and(segment, segment, mask=mask_segment) # Get the countours around the segment contours, _ = cv2.findContours(mask_segment, cv2.RETR_LIST, cv2.CHAIN_APPROX_SIMPLE)[-2:] m = -1 max_contour = None for cnt in contours: if (len(cnt) > m): m = len(cnt) max_contour = cnt # Get the rectangle that encompasses the countour x, y, w, h = cv2.boundingRect(max_contour) segment = segment[y:y + h, x:x + w] end_time = TimeUtils.get_time() # Return the rectangle that encompasses the countour return segment, size_segment, idx_segment, (end_time - start_time) def paint_segment_skimage(self, image, color, px=0, py=0, idx_segment=[], border=True, clear=False): """Paint a list of segments using a index or position in image. Parameters ---------- image : opencv 3-channel color image. Segmented image. color : string X11Color name. px : integer, optional, default = 0 Segment point inside the image in x-axis. py : integer, optional, default = 0 Segment point inside the image in y-axis. idx_segment : list, optional, default = [] List of segments. border : boolean, optional, default = True If true paint the border of segments with default color. clear : boolean, optional, default = False If true clear previous painting in the image. Returns ------- new_image : opencv 3-channel color image. Painted image. run_time : integer Running time spent in milliseconds. """ # Check if segmentation was already performed if self._segments is None: return image, 0 start_time = TimeUtils.get_time() # If idx_segment not passed, get the segment index using the points (px, py) if len(idx_segment) == 0: idx_segment = [self._segments[py, px]] height, width, channels = self._original_image.shape # Create a mask, painting black all pixels outside of segments and white the pixels inside mask_segment = np.zeros(self._original_image.shape[:2], dtype="uint8") for idx in idx_segment: mask_segment[self._segments == idx] = 255 mask_inv = cv2.bitwise_not(mask_segment) # Paint all pixels in original image with choosed color class_color = np.zeros((height, width, 3), np.uint8) class_color[:, :] = X11Colors.get_color(color) if SkimageSegmenter.FORCE_OPT == False: colored_image = cv2.addWeighted(self._original_image, 0.7, class_color, 0.3, 0) else: colored_image = cv2.addWeighted(image, 0.7, class_color, 0.3, 0) colored_image = cv2.bitwise_and(colored_image, colored_image, mask=mask_segment) # Create a new image keeping the painting only in pixels inside of segments new_image = image if clear == False else self._original_image new_image = cv2.bitwise_and(new_image, new_image, mask=mask_inv) mask_segment[:] = 255 new_image = cv2.bitwise_or(new_image, colored_image, mask=mask_segment) # If border is true, paint the segments borders if border == True and SkimageSegmenter.FORCE_OPT == False: color = X11Colors.get_color_zero_one( self.border_color.get_cast_val()) outline_color = color if self.border_outline.value == 'Yes' else None new_image = img_as_ubyte( mark_boundaries(img_as_float(new_image), self._segments.astype(np.int8), color=color, outline_color=outline_color)) end_time = TimeUtils.get_time() # Return painted image return new_image, (end_time - start_time) def run_skimage(self, image, method, **kwargs): """Perform the segmentation Parameters ---------- image : opencv 3-channel color image. Original image. method : function Method from skyimage that performs the image segmentation. **kwargs : keyword arguments Dict of the keyword args passed to the function. Returns ------- new_image : opencv 3-channel color image. Segmented image. run_time : integer Running time spent in milliseconds. """ self._original_image = image # Run the segmentation using the method passed start_time = TimeUtils.get_time() self._segments = method(img_as_float(image), **kwargs) end_time = TimeUtils.get_time() color = X11Colors.get_color_zero_one(self.border_color.get_cast_val()) outline_color = color if self.border_outline.value == 'Yes' else None # Ignore UserWarning: Possible precision loss when converting from float64 to uint8 # because the returned image is used just for visualization # The original image, without loss, is stored in self._original_image return img_as_ubyte( mark_boundaries(image, self._segments.astype(np.int8), color=color, outline_color=outline_color)), (end_time - start_time) def reset_skimage(self): """Clean all data of segmentation. """ self._segments = None self._original_image = None