Commit 40c35591 authored by Alessandro dos Santos Ferreira's avatar Alessandro dos Santos Ferreira
Browse files

Pynovisao - Documentando segmenters

parent 3c86801e
......@@ -12,7 +12,9 @@
As PEP 20 says, "Readability counts". So, follow the current conventions adopted in this project.
For more information access PEP 8 -- Style Guide for Python Code ( https://www.python.org/dev/peps/pep-0008/ ).
Use it as your guideline. Only use CamelCase in class names. DON'T USE CamelCase ou mixedCase in variable or functions.
Use it as your guideline. Only use CamelCase in class names. DON'T USE CamelCase ou mixedCase in variables or functions.
All declarations and comments in this project must be made in English.
Name: main.py
Author: Alessandro dos Santos Ferreira ( santosferreira.alessandro@gmail.com )
......
......@@ -19,8 +19,24 @@ from segmenter import Segmenter
from skimage_segmenter import SkimageSegmenter
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)
......@@ -29,6 +45,13 @@ class Felzenszwalb(Segmenter, SkimageSegmenter):
def get_config(self):
"""Return configuration of segmenter.
Returns
-------
config : OrderedDict
Current configs of segmenter.
"""
felzenszwalb_config = OrderedDict()
felzenszwalb_config["scale"] = self.scale
......@@ -40,6 +63,13 @@ class Felzenszwalb(Segmenter, SkimageSegmenter):
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)
......@@ -49,6 +79,13 @@ class Felzenszwalb(Segmenter, SkimageSegmenter):
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
......@@ -65,17 +102,85 @@ class Felzenszwalb(Segmenter, SkimageSegmenter):
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):
"""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)
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()
......@@ -85,4 +190,6 @@ class Felzenszwalb(Segmenter, SkimageSegmenter):
def reset(self):
"""Clean all data of segmentation.
"""
return self.reset_skimage()
......@@ -19,8 +19,24 @@ from segmenter import Segmenter
from skimage_segmenter import SkimageSegmenter
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)
......@@ -30,6 +46,13 @@ class Quickshift(Segmenter, SkimageSegmenter):
def get_config(self):
"""Return configuration of segmenter.
Returns
-------
config : OrderedDict
Current configs of segmenter.
"""
quickshift_config = OrderedDict()
quickshift_config["ratio"] = self.ratio
......@@ -42,6 +65,13 @@ class Quickshift(Segmenter, SkimageSegmenter):
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)
......@@ -53,6 +83,13 @@ class Quickshift(Segmenter, SkimageSegmenter):
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
......@@ -70,17 +107,85 @@ class Quickshift(Segmenter, SkimageSegmenter):
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):
"""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)
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(),
......@@ -91,4 +196,6 @@ class Quickshift(Segmenter, SkimageSegmenter):
def reset(self):
"""Clean all data of segmentation.
"""
return self.reset_skimage()
......@@ -11,40 +11,72 @@
from abc import ABCMeta, abstractmethod
class Segmenter(object):
"""Abstract class for segmenter algorithms."""
__metaclass__ = ABCMeta
def get_name(self):
"""Return the name of class.
Returns
-------
name : string
Returns the name of instantiated class.
"""
return self.__class__.__name__
@abstractmethod
def get_config(self):
"""Return configuration of segmenter.
Implement this method to extend this class with a new segmenter algorithm.
"""
pass
@abstractmethod
def set_config(self, configs):
"""Update configuration of segmenter.
Implement this method to extend this class with a new segmenter algorithm.
"""
pass
@abstractmethod
def get_summary_config(self):
"""Return fomatted summary of configuration.
Implement this method to extend this class with a new segmenter algorithm.
"""
pass
@abstractmethod
def get_list_segments(self):
"""Return a list with segments after apply segmentation.
Implement this method to extend this class with a new segmenter algorithm.
"""
pass
@abstractmethod
def get_segment(self, px, py, idx_segment):
"""Return a specified segment using a index or position in image.
Implement this method to extend this class with a new segmenter algorithm.
"""
pass
@abstractmethod
def paint_segment(self, image, color, px, py, idx_segment, border, clear):
"""Paint a list of segments using a index or position in image.
Implement this method to extend this class with a new segmenter algorithm.
"""
pass
@abstractmethod
def run(self, image):
"""Perform the segmentation
Implement this method to extend this class with a new segmenter algorithm.
"""
pass
@abstractmethod
def reset(self):
pass
\ No newline at end of file
"""Clean all data of segmentation.
Implement this method to extend this class with a new classifier algorithm.
"""
pass
......@@ -20,6 +20,7 @@ from util.x11_colors import X11Colors
from abc import ABCMeta, abstractmethod
class SkimageSegmenter(object):
"""Abstract class for segmenters implemented in skimage.segmentation."""
__metaclass__ = ABCMeta
......@@ -27,6 +28,15 @@ class SkimageSegmenter(object):
FORCE_OPT = False
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)
......@@ -35,6 +45,13 @@ class SkimageSegmenter(object):
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 []
......@@ -42,14 +59,39 @@ class SkimageSegmenter(object):
def get_segment_skimage(self, px = 0, py = 0, idx_segment = 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]
# Creating 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
size_segment = mask_segment[self._segments == idx_segment].size
......@@ -57,6 +99,7 @@ class SkimageSegmenter(object):
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
......@@ -66,28 +109,61 @@ class SkimageSegmenter(object):
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
# Creating 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")
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 color choosed
class_color = np.zeros((height,width,3), np.uint8)
class_color[:, :] = X11Colors.get_color(color)
if SkimageSegmenter.FORCE_OPT == False:
......@@ -96,11 +172,13 @@ class SkimageSegmenter(object):
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 segment
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, mark the boundary of images
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
......@@ -109,12 +187,32 @@ class SkimageSegmenter(object):
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()
......@@ -129,5 +227,7 @@ class SkimageSegmenter(object):
def reset_skimage(self):
"""Clean all data of segmentation.
"""
self._segments = None
self._original_image = None
......@@ -21,8 +21,24 @@ from segmenter import Segmenter
from skimage_segmenter import SkimageSegmenter
class Slic(Segmenter, SkimageSegmenter):
"""Run SLIC (Simple Linear Iterative Clustering) segmentation."""
def __init__(self, n_segments = 250, sigma = 5.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)
......@@ -31,6 +47,13 @@ class Slic(Segmenter, SkimageSegmenter):
def get_config(self):
"""Return configuration of segmenter.
Returns
-------
config : OrderedDict
Current configs of segmenter.