Merge pull request #674 from ahojnnes/region-props

Refactor regionprops.
This commit is contained in:
Stefan van der Walt
2013-08-07 08:35:22 -07:00
5 changed files with 485 additions and 422 deletions
+1
View File
@@ -3,6 +3,7 @@ Version 0.10
* Remove deprecated functions:
- ``skimage.filter.rank.*``
* Remove deprecated parameter ``epsilon`` of ``skimage.viewer.LineProfile``
* Remove backwards-compatability of ``skimage.measure.regionprops``
Version 0.9
-----------
+9 -15
View File
@@ -24,29 +24,23 @@ image[rr,cc] = 1
image = rotate(image, angle=15, order=0)
label_img = label(image)
props = regionprops(label_img, [
'BoundingBox',
'Centroid',
'Orientation',
'MajorAxisLength',
'MinorAxisLength'
])
regions = regionprops(label_img)
plt.imshow(image)
for prop in props:
x0 = prop['Centroid'][1]
y0 = prop['Centroid'][0]
x1 = x0 + math.cos(prop['Orientation']) * 0.5 * prop['MajorAxisLength']
y1 = y0 - math.sin(prop['Orientation']) * 0.5 * prop['MajorAxisLength']
x2 = x0 - math.sin(prop['Orientation']) * 0.5 * prop['MinorAxisLength']
y2 = y0 - math.cos(prop['Orientation']) * 0.5 * prop['MinorAxisLength']
for props in regions:
y0, x0 = props.centroid
orientation = props.orientation
x1 = x0 + math.cos(orientation) * 0.5 * props.major_axis_length
y1 = y0 - math.sin(orientation) * 0.5 * props.major_axis_length
x2 = x0 - math.sin(orientation) * 0.5 * props.minor_axis_length
y2 = y0 - math.cos(orientation) * 0.5 * props.minor_axis_length
plt.plot((x0, x1), (y0, y1), '-r', linewidth=2.5)
plt.plot((x0, x2), (y0, y2), '-r', linewidth=2.5)
plt.plot(x0, y0, '.g', markersize=15)
minr, minc, maxr, maxc = prop['BoundingBox']
minr, minc, maxr, maxc = props.bbox
bx = (minc, maxc, maxc, minc, minc)
by = (minr, minr, maxr, maxr, minr)
plt.plot(bx, by, '-b', linewidth=2.5)
+1 -1
View File
@@ -5,7 +5,7 @@ import sys
from . import six
__all__ = ['deprecated', 'get_bound_method_class']
__all__ = ['deprecated', 'cached_property', 'get_bound_method_class']
class deprecated(object):
+421 -350
View File
@@ -1,10 +1,11 @@
# coding: utf-8
import warnings
from math import sqrt, atan2, pi as PI
import numpy as np
from scipy import ndimage
from skimage.morphology import convex_hull_image
from . import _moments
from skimage.measure import _moments
__all__ = ['regionprops']
@@ -14,47 +15,304 @@ STREL_4 = np.array([[0, 1, 0],
[1, 1, 1],
[0, 1, 0]])
STREL_8 = np.ones((3, 3), 'int8')
PROPS = (
'Area',
'BoundingBox',
'CentralMoments',
'Centroid',
'ConvexArea',
PROPS = {
'Area': 'area',
'BoundingBox': 'bbox',
'CentralMoments': 'central_moments',
'Centroid': 'centroid',
'ConvexArea': 'convex_area',
# 'ConvexHull',
'ConvexImage',
'Coordinates',
'Eccentricity',
'EquivDiameter',
'EulerNumber',
'Extent',
'ConvexImage': 'convex_image',
'Coordinates': 'coords',
'Eccentricity': 'eccentricity',
'EquivDiameter': 'equivalent_diameter',
'EulerNumber': 'euler_number',
'Extent': 'extent',
# 'Extrema',
'FilledArea',
'FilledImage',
'HuMoments',
'Image',
'MajorAxisLength',
'MaxIntensity',
'MeanIntensity',
'MinIntensity',
'MinorAxisLength',
'Moments',
'NormalizedMoments',
'Orientation',
'Perimeter',
'FilledArea': 'filled_area',
'FilledImage': 'filled_image',
'HuMoments': 'hu_moments',
'Image': 'image',
'MajorAxisLength': 'major_axis_length',
'MaxIntensity': 'max_intensity',
'MeanIntensity': 'mean_intensity',
'MinIntensity': 'min_intensity',
'MinorAxisLength': 'minor_axis_length',
'Moments': 'moments',
'NormalizedMoments': 'normalized_moments',
'Orientation': 'orientation',
'Perimeter': 'perimeter',
# 'PixelIdxList',
# 'PixelList',
'Solidity',
'Solidity': 'solidity',
# 'SubarrayIdx'
'WeightedCentralMoments',
'WeightedCentroid',
'WeightedHuMoments',
'WeightedMoments',
'WeightedNormalizedMoments'
)
'WeightedCentralMoments': 'weighted_central_moments',
'WeightedCentroid': 'weighted_centroid',
'WeightedHuMoments': 'weighted_hu_moments',
'WeightedMoments': 'weighted_moments',
'WeightedNormalizedMoments': 'weighted_normalized_moments'
}
def regionprops(label_image, properties=['Area', 'Centroid'],
intensity_image=None):
class cached_property(object):
"""Decorator to use a function as a cached property.
The function is only called the first time and each successive call returns
the cached result of the first call.
class Foo(object):
@cached_property
def foo(self):
return "Cached"
class Foo(object):
def __init__(self):
self.cache_active = False
@cached_property
def foo(self):
return "Not cached"
Adapted from <http://wiki.python.org/moin/PythonDecoratorLibrary>.
"""
def __init__(self, func, name=None, doc=None):
self.__name__ = name or func.__name__
self.__module__ = func.__module__
self.__doc__ = doc or func.__doc__
self.func = func
def __get__(self, obj, type=None):
if obj is None:
return self
# call every time, if cache is not active
if not obj.__dict__.get('cache_active', True):
return self.func(obj)
# try to retrieve from cache or call and store result in cache
try:
value = obj.__dict__[self.__name__]
except KeyError:
value = self.func(obj)
obj.__dict__[self.__name__] = value
return value
class _RegionProperties(object):
def __init__(self, slice, label, label_image, intensity_image,
cache_active):
self._slice = slice
self.label = label
self._label_image = label_image
self._intensity_image = intensity_image
self.cache_active = cache_active
@cached_property
def area(self):
return self.moments[0, 0]
@cached_property
def bbox(self):
return (self._slice[0].start, self._slice[1].start,
self._slice[0].stop, self._slice[1].stop)
@cached_property
def centroid(self):
row, col = self.local_centroid
return row + self._slice[0].start, col + self._slice[1].start
@cached_property
def central_moments(self):
row, col = self.local_centroid
return _moments.central_moments(self._image_double, row, col, 3)
@cached_property
def convex_area(self):
return np.sum(self.convex_image)
@cached_property
def convex_image(self):
return convex_hull_image(self.image)
@cached_property
def coords(self):
rr, cc = np.nonzero(self.image)
return np.vstack((rr + self._slice[0].start,
cc + self._slice[1].start)).T
@cached_property
def eccentricity(self):
l1, l2 = self.inertia_tensor_eigvals
if l1 == 0:
return 0
return sqrt(1 - l2 / l1)
@cached_property
def equivalent_diameter(self):
return sqrt(4 * self.moments[0, 0] / PI)
@cached_property
def euler_number(self):
euler_array = self.filled_image != self.image
_, num = ndimage.label(euler_array, STREL_8)
return -num
@cached_property
def extent(self):
rows, cols = self.image.shape
return self.moments[0, 0] / (rows * cols)
@cached_property
def filled_area(self):
return np.sum(self.filled_image)
@cached_property
def filled_image(self):
return ndimage.binary_fill_holes(self.image, STREL_8)
@cached_property
def hu_moments(self):
return _moments.hu_moments(self.normalized_moments)
@cached_property
def image(self):
return self._label_image[self._slice] == self.label
@cached_property
def _image_double(self):
return self.image.astype(np.double)
@cached_property
def inertia_tensor(self):
mu = self.central_moments
a = mu[2, 0] / mu[0, 0]
b = -mu[1, 1] / mu[0, 0]
c = mu[0, 2] / mu[0, 0]
return np.array([[a, b], [b, c]])
@cached_property
def inertia_tensor_eigvals(self):
a, b, b, c = self.inertia_tensor.flat
# eigen values of inertia tensor
l1 = (a + c) / 2 + sqrt(4 * b ** 2 + (a - c) ** 2) / 2
l2 = (a + c) / 2 - sqrt(4 * b ** 2 + (a - c) ** 2) / 2
return l1, l2
@cached_property
def intensity_image(self):
if self._intensity_image is None:
raise AttributeError('No intensity image specified.')
return self._intensity_image[self._slice] * self.image
@cached_property
def _intensity_image_double(self):
return self.intensity_image.astype(np.double)
@cached_property
def moments(self):
return _moments.central_moments(self._image_double, 0, 0, 3)
@cached_property
def local_centroid(self):
m = self.moments
row = m[0, 1] / m[0, 0]
col = m[1, 0] / m[0, 0]
return row, col
@cached_property
def max_intensity(self):
return np.max(self.intensity_image[self.image])
@cached_property
def mean_intensity(self):
return np.mean(self.intensity_image[self.image])
@cached_property
def min_intensity(self):
return np.min(self.intensity_image[self.image])
@cached_property
def major_axis_length(self):
l1, _ = self.inertia_tensor_eigvals
return 4 * sqrt(l1)
@cached_property
def minor_axis_length(self):
_, l2 = self.inertia_tensor_eigvals
return 4 * sqrt(l2)
@cached_property
def normalized_moments(self):
return _moments.normalized_moments(self.central_moments, 3)
@cached_property
def orientation(self):
a, b, b, c = self.inertia_tensor.flat
b = -b
if a - c == 0:
if b > 0:
return -PI / 4.
else:
return PI / 4.
else:
return - 0.5 * atan2(2 * b, (a - c))
@cached_property
def perimeter(self):
return perimeter(self.image, 4)
@cached_property
def solidity(self):
return self.moments[0, 0] / np.sum(self.convex_image)
@cached_property
def weighted_central_moments(self):
row, col = self.weighted_local_centroid
return _moments.central_moments(self._intensity_image_double,
row, col, 3)
@cached_property
def weighted_centroid(self):
row, col = self.weighted_local_centroid
return row + self._slice[0].start, col + self._slice[1].start
@cached_property
def weighted_local_centroid(self):
m = self.weighted_moments
row = m[0, 1] / m[0, 0]
col = m[1, 0] / m[0, 0]
return row, col
@cached_property
def weighted_hu_moments(self):
return _moments.hu_moments(self.weighted_normalized_moments)
@cached_property
def weighted_moments(self):
return _moments.central_moments(self._intensity_image_double, 0, 0, 3)
@cached_property
def weighted_normalized_moments(self):
return _moments.normalized_moments(self.weighted_central_moments, 3)
def __getitem__(self, key):
value = getattr(self, key, None)
if value is not None:
return value
else: # backwards compatability
warnings.warn('Usage of deprecated property name.',
category=DeprecationWarning)
return getattr(self, PROPS[key])
def regionprops(label_image, properties=None,
intensity_image=None, cache=True):
"""Measure properties of labelled image regions.
Parameters
@@ -62,150 +320,128 @@ def regionprops(label_image, properties=['Area', 'Centroid'],
label_image : (N, M) ndarray
Labelled input image.
properties : {'all', list}
Shape measurements to be determined for each labelled image region.
Default is `['Area', 'Centroid']`. The following properties can be
determined:
**Deprecated parameter**
* Area : int
Number of pixels of region.
* BoundingBox : tuple
Bounding box `(min_row, min_col, max_row, max_col)`
* CentralMoments : (3, 3) ndarray
Central moments (translation invariant) up to 3rd order.
mu_ji = sum{ array(x, y) * (x - x_c)^j * (y - y_c)^i }
where the sum is over the `x`, `y` coordinates of the region,
and `x_c` and `y_c` are the coordinates of the region's centroid.
* Centroid : array
Centroid coordinate tuple `(row, col)`.
* ConvexArea : int
Number of pixels of convex hull image.
* ConvexImage : (H, J) ndarray
Binary convex hull image which has the same size as bounding box.
* Coordinates : (N, 2) ndarray
Coordinate list `(row, col)` of the region.
* Eccentricity : float
Eccentricity of the ellipse that has the same second-moments as the
region. The eccentricity is the ratio of the distance between its
minor and major axis length. The value is between 0 and 1.
* EquivDiameter : float
The diameter of a circle with the same area as the region.
* EulerNumber : int
Euler number of region. Computed as number of objects (= 1)
subtracted by number of holes (8-connectivity).
* Extent : float
Ratio of pixels in the region to pixels in the total bounding box.
Computed as `Area / (rows*cols)`
* FilledArea : int
Number of pixels of filled region.
* FilledImage : (H, J) ndarray
Binary region image with filled holes which has the same size as
bounding box.
* HuMoments : tuple
Hu moments (translation, scale and rotation invariant).
* Image : (H, J) ndarray
Sliced binary region image which has the same size as bounding box.
* MajorAxisLength : float
The length of the major axis of the ellipse that has the same
normalized second central moments as the region.
* MaxIntensity: float
Value with the greatest intensity in the region.
* MeanIntensity: float
Value with the mean intensity in the region.
* MinIntensity: float
Value with the least intensity in the region.
* MinorAxisLength : float
The length of the minor axis of the ellipse that has the same
normalized second central moments as the region.
* Moments : (3, 3) ndarray
Spatial moments up to 3rd order.
m_ji = sum{ array(x, y) * x^j * y^i }
where the sum is over the `x`, `y` coordinates of the region.
* NormalizedMoments : (3, 3) ndarray
Normalized moments (translation and scale invariant) up to 3rd
order.
nu_ji = mu_ji / m_00^[(i+j)/2 + 1]
where `m_00` is the zeroth spatial moment.
* Orientation : float
Angle between the X-axis and the major axis of the ellipse that has
the same second-moments as the region. Ranging from `-pi/2` to
`pi/2` in counter-clockwise direction.
* Perimeter : float
Perimeter of object which approximates the contour as a line
through the centers of border pixels using a 4-connectivity.
* Solidity : float
Ratio of pixels in the region to pixels of the convex hull image.
* WeightedCentralMoments : (3, 3) ndarray
Central moments (translation invariant) of intensity image up to
3rd order.
wmu_ji = sum{ array(x, y) * (x - x_c)^j * (y - y_c)^i }
where the sum is over the `x`, `y` coordinates of the region,
and `x_c` and `y_c` are the coordinates of the region's centroid.
* WeightedCentroid : array
Centroid coordinate tuple `(row, col)` weighted with intensity
image.
* WeightedHuMoments : tuple
Hu moments (translation, scale and rotation invariant) of intensity
image.
* WeightedMoments : (3, 3) ndarray
Spatial moments of intensity image up to 3rd order.
wm_ji = sum{ array(x, y) * x^j * y^i }
where the sum is over the `x`, `y` coordinates of the region.
* WeightedNormalizedMoments : (3, 3) ndarray
Normalized moments (translation and scale invariant) of intensity
image up to 3rd order.
wnu_ji = wmu_ji / wm_00^[(i+j)/2 + 1]
where `wm_00` is the zeroth spatial moment (intensity-weighted
area).
This parameter is not needed any more since all properties are
determined dynamically.
intensity_image : (N, M) ndarray, optional
Intensity image with same size as labelled image. Default is None.
cache : bool, optional
Determine whether to cache calculated properties. The computation is
much faster for cached properties, whereas the memory consumption
increases.
Returns
-------
properties : list of dicts
List containing a property dict for each region. The property dicts
contain all the specified properties plus a 'Label' field.
properties : list
List containing a properties for each region. The properties of each
region can be accessed as attributes and keys.
Notes
-----
The following properties can be accessed as attributes or keys:
area : int
Number of pixels of region.
bbox : tuple
Bounding box `(min_row, min_col, max_row, max_col)`
central_moments : (3, 3) ndarray
Central moments (translation invariant) up to 3rd order::
mu_ji = sum{ array(x, y) * (x - x_c)^j * (y - y_c)^i }
where the sum is over the `x`, `y` coordinates of the region,
and `x_c` and `y_c` are the coordinates of the region's centroid.
centroid : array
Centroid coordinate tuple `(row, col)`.
convex_area : int
Number of pixels of convex hull image.
convex_image : (H, J) ndarray
Binary convex hull image which has the same size as bounding box.
coords : (N, 2) ndarray
Coordinate list `(row, col)` of the region.
eccentricity : float
Eccentricity of the ellipse that has the same second-moments as the
region. The eccentricity is the ratio of the distance between its
minor and major axis length. The value is between 0 and 1.
equivalent_diameter : float
The diameter of a circle with the same area as the region.
euler_number : int
Euler number of region. Computed as number of objects (= 1)
subtracted by number of holes (8-connectivity).
extent : float
Ratio of pixels in the region to pixels in the total bounding box.
Computed as `Area / (rows*cols)`
filled_area : int
Number of pixels of filled region.
filled_image : (H, J) ndarray
Binary region image with filled holes which has the same size as
bounding box.
hu_moments : tuple
Hu moments (translation, scale and rotation invariant).
image : (H, J) ndarray
Sliced binary region image which has the same size as bounding box.
major_axis_length : float
The length of the major axis of the ellipse that has the same
normalized second central moments as the region.
min_intensity : float
Value with the greatest intensity in the region.
mean_intensity : float
Value with the mean intensity in the region.
min_intensity : float
Value with the least intensity in the region.
minor_axis_length : float
The length of the minor axis of the ellipse that has the same
normalized second central moments as the region.
moments : (3, 3) ndarray
Spatial moments up to 3rd order::
m_ji = sum{ array(x, y) * x^j * y^i }
where the sum is over the `x`, `y` coordinates of the region.
normalized_moments : (3, 3) ndarray
Normalized moments (translation and scale invariant) up to 3rd order::
nu_ji = mu_ji / m_00^[(i+j)/2 + 1]
where `m_00` is the zeroth spatial moment.
orientation : float
Angle between the X-axis and the major axis of the ellipse that has
the same second-moments as the region. Ranging from `-pi/2` to
`pi/2` in counter-clockwise direction.
perimeter : float
Perimeter of object which approximates the contour as a line
through the centers of border pixels using a 4-connectivity.
solidity : float
Ratio of pixels in the region to pixels of the convex hull image.
weighted_central_moments : (3, 3) ndarray
Central moments (translation invariant) of intensity image up to
3rd order::
wmu_ji = sum{ array(x, y) * (x - x_c)^j * (y - y_c)^i }
where the sum is over the `x`, `y` coordinates of the region,
and `x_c` and `y_c` are the coordinates of the region's centroid.
weighted_centroid : array
Centroid coordinate tuple `(row, col)` weighted with intensity
image.
weighted_hu_moments : tuple
Hu moments (translation, scale and rotation invariant) of intensity
image.
weighted_moments : (3, 3) ndarray
Spatial moments of intensity image up to 3rd order::
wm_ji = sum{ array(x, y) * x^j * y^i }
where the sum is over the `x`, `y` coordinates of the region.
weighted_normalized_moments : (3, 3) ndarray
Normalized moments (translation and scale invariant) of intensity
image up to 3rd order::
wnu_ji = wmu_ji / wm_00^[(i+j)/2 + 1]
where `wm_00` is the zeroth spatial moment (intensity-weighted area).
References
----------
@@ -225,194 +461,29 @@ def regionprops(label_image, properties=['Area', 'Centroid'],
>>> img = coins() > 110
>>> label_img = label(img)
>>> props = regionprops(label_img)
>>> props[0]['Centroid'] # centroid of first labelled object
>>> props[0].centroid # centroid of first labelled object
>>> props[0]['centroid'] # centroid of first labelled object
"""
if not np.issubdtype(label_image.dtype, 'int'):
raise TypeError('labelled image must be of integer dtype')
raise TypeError('Labelled image must be of integer dtype.')
# determine all properties if nothing specified
if properties == 'all':
properties = PROPS
if properties is not None:
warnings.warn('The ``properties`` argument is deprecated and is '
'not needed any more as properties are '
'determined dynamically.',
category=DeprecationWarning)
props = []
regions = []
objects = ndimage.find_objects(label_image)
for i, sl in enumerate(objects):
label = i + 1
# create property dict for current label
obj_props = {}
props.append(obj_props)
props = _RegionProperties(sl, label, label_image,
intensity_image, cache)
regions.append(props)
obj_props['Label'] = label
array = (label_image[sl] == label).astype('double')
# upper left corner of object bbox
r0 = sl[0].start
c0 = sl[1].start
m = _moments.central_moments(array, 0, 0, 3)
# centroid
cr = m[0, 1] / m[0, 0]
cc = m[1, 0] / m[0, 0]
mu = _moments.central_moments(array, cr, cc, 3)
# elements of the inertia tensor [a b; b c]
a = mu[2, 0] / mu[0, 0]
b = mu[1, 1] / mu[0, 0]
c = mu[0, 2] / mu[0, 0]
# eigen values of inertia tensor
l1 = (a + c) / 2 + sqrt(4 * b ** 2 + (a - c) ** 2) / 2
l2 = (a + c) / 2 - sqrt(4 * b ** 2 + (a - c) ** 2) / 2
# cached results which are used by several properties
_filled_image = None
_convex_image = None
_nu = None
if 'Area' in properties:
obj_props['Area'] = m[0, 0]
if 'BoundingBox' in properties:
obj_props['BoundingBox'] = (r0, c0, sl[0].stop, sl[1].stop)
if 'Centroid' in properties:
obj_props['Centroid'] = cr + r0, cc + c0
if 'CentralMoments' in properties:
obj_props['CentralMoments'] = mu
if 'ConvexArea' in properties:
if _convex_image is None:
_convex_image = convex_hull_image(array)
obj_props['ConvexArea'] = np.sum(_convex_image)
if 'ConvexImage' in properties:
if _convex_image is None:
_convex_image = convex_hull_image(array)
obj_props['ConvexImage'] = _convex_image
if 'Coordinates' in properties:
rr, cc = np.nonzero(array)
obj_props['Coordinates'] = np.vstack((rr + r0, cc + c0)).T
if 'Eccentricity' in properties:
if l1 == 0:
obj_props['Eccentricity'] = 0
else:
obj_props['Eccentricity'] = sqrt(1 - l2 / l1)
if 'EquivDiameter' in properties:
obj_props['EquivDiameter'] = sqrt(4 * m[0, 0] / PI)
if 'EulerNumber' in properties:
if _filled_image is None:
_filled_image = ndimage.binary_fill_holes(array, STREL_8)
euler_array = _filled_image != array
_, num = ndimage.label(euler_array, STREL_8)
obj_props['EulerNumber'] = - num
if 'Extent' in properties:
obj_props['Extent'] = m[0, 0] / (array.shape[0] * array.shape[1])
if 'HuMoments' in properties:
if _nu is None:
_nu = _moments.normalized_moments(mu, 3)
obj_props['HuMoments'] = _moments.hu_moments(_nu)
if 'Image' in properties:
obj_props['Image'] = array
if 'FilledArea' in properties:
if _filled_image is None:
_filled_image = ndimage.binary_fill_holes(array, STREL_8)
obj_props['FilledArea'] = np.sum(_filled_image)
if 'FilledImage' in properties:
if _filled_image is None:
_filled_image = ndimage.binary_fill_holes(array, STREL_8)
obj_props['FilledImage'] = _filled_image
if 'MajorAxisLength' in properties:
obj_props['MajorAxisLength'] = 4 * sqrt(l1)
if 'MinorAxisLength' in properties:
obj_props['MinorAxisLength'] = 4 * sqrt(l2)
if 'Moments' in properties:
obj_props['Moments'] = m
if 'NormalizedMoments' in properties:
if _nu is None:
_nu = _moments.normalized_moments(mu, 3)
obj_props['NormalizedMoments'] = _nu
if 'Orientation' in properties:
if a - c == 0:
if b > 0:
obj_props['Orientation'] = -PI / 4.
else:
obj_props['Orientation'] = PI / 4.
else:
obj_props['Orientation'] = - 0.5 * atan2(2 * b, (a - c))
if 'Perimeter' in properties:
obj_props['Perimeter'] = perimeter(array, 4)
if 'Solidity' in properties:
if _convex_image is None:
_convex_image = convex_hull_image(array)
obj_props['Solidity'] = m[0, 0] / np.sum(_convex_image)
if intensity_image is not None:
weighted_array = array * intensity_image[sl]
wm = _moments.central_moments(weighted_array, 0, 0, 3)
# weighted centroid
wcr = wm[0, 1] / wm[0, 0]
wcc = wm[1, 0] / wm[0, 0]
wmu = _moments.central_moments(weighted_array, wcr, wcc, 3)
# cached results which are used by several properties
_wnu = None
_vals = None
if 'MaxIntensity' in properties:
if _vals is None:
_vals = weighted_array[array.astype('bool')]
obj_props['MaxIntensity'] = np.max(_vals)
if 'MeanIntensity' in properties:
if _vals is None:
_vals = weighted_array[array.astype('bool')]
obj_props['MeanIntensity'] = np.mean(_vals)
if 'MinIntensity' in properties:
if _vals is None:
_vals = weighted_array[array.astype('bool')]
obj_props['MinIntensity'] = np.min(_vals)
if 'WeightedCentralMoments' in properties:
obj_props['WeightedCentralMoments'] = wmu
if 'WeightedCentroid' in properties:
obj_props['WeightedCentroid'] = wcr + r0, wcc + c0
if 'WeightedHuMoments' in properties:
if _wnu is None:
_wnu = _moments.normalized_moments(wmu, 3)
obj_props['WeightedHuMoments'] = _moments.hu_moments(_wnu)
if 'WeightedMoments' in properties:
obj_props['WeightedMoments'] = wm
if 'WeightedNormalizedMoments' in properties:
if _wnu is None:
_wnu = _moments.normalized_moments(wmu, 3)
obj_props['WeightedNormalizedMoments'] = _wnu
return props
return regions
def perimeter(image, neighbourhood=4):
+53 -56
View File
@@ -22,33 +22,33 @@ INTENSITY_SAMPLE = SAMPLE.copy()
INTENSITY_SAMPLE[1, 9:11] = 2
def test_all_props():
regions = regionprops(SAMPLE, 'all', INTENSITY_SAMPLE)[0]
for prop in PROPS:
regions[prop]
def test_unsupported_dtype():
assert_raises(TypeError, regionprops, np.zeros((10, 10), dtype=np.double))
def test_all_props():
props = regionprops(SAMPLE, 'all', INTENSITY_SAMPLE)[0]
for prop in PROPS:
assert prop in props
def test_area():
area = regionprops(SAMPLE, ['Area'])[0]['Area']
area = regionprops(SAMPLE)[0].area
assert area == np.sum(SAMPLE)
def test_bbox():
bbox = regionprops(SAMPLE, ['BoundingBox'])[0]['BoundingBox']
bbox = regionprops(SAMPLE)[0].bbox
assert_array_almost_equal(bbox, (0, 0, SAMPLE.shape[0], SAMPLE.shape[1]))
SAMPLE_mod = SAMPLE.copy()
SAMPLE_mod[:, -1] = 0
bbox = regionprops(SAMPLE_mod, ['BoundingBox'])[0]['BoundingBox']
bbox = regionprops(SAMPLE_mod)[0].bbox
assert_array_almost_equal(bbox, (0, 0, SAMPLE.shape[0], SAMPLE.shape[1]-1))
def test_central_moments():
mu = regionprops(SAMPLE, ['CentralMoments'])[0]['CentralMoments']
mu = regionprops(SAMPLE)[0].central_moments
# determined with OpenCV
assert_almost_equal(mu[0,2], 436.00000000000045)
# different from OpenCV results, bug in OpenCV
@@ -61,19 +61,19 @@ def test_central_moments():
def test_centroid():
centroid = regionprops(SAMPLE, ['Centroid'])[0]['Centroid']
centroid = regionprops(SAMPLE)[0].centroid
# determined with MATLAB
assert_array_almost_equal(centroid, (5.66666666666666, 9.444444444444444))
def test_convex_area():
area = regionprops(SAMPLE, ['ConvexArea'])[0]['ConvexArea']
area = regionprops(SAMPLE)[0].convex_area
# determined with MATLAB
assert area == 124
def test_convex_image():
img = regionprops(SAMPLE, ['ConvexImage'])[0]['ConvexImage']
img = regionprops(SAMPLE)[0].convex_image
# determined with MATLAB
ref = np.array(
[[0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 0, 0, 0, 0, 0],
@@ -94,43 +94,43 @@ def test_coordinates():
sample = np.zeros((10, 10), dtype=np.int8)
coords = np.array([[3, 2], [3, 3], [3, 4]])
sample[coords[:, 0], coords[:, 1]] = 1
prop_coords = regionprops(sample, ['Coordinates'])[0]['Coordinates']
prop_coords = regionprops(sample)[0].coords
assert_array_equal(prop_coords, coords)
def test_eccentricity():
eps = regionprops(SAMPLE, ['Eccentricity'])[0]['Eccentricity']
eps = regionprops(SAMPLE)[0].eccentricity
assert_almost_equal(eps, 0.814629313427)
img = np.zeros((5, 5), dtype=np.int)
img[2, 2] = 1
eps = regionprops(img, ['Eccentricity'])[0]['Eccentricity']
eps = regionprops(img)[0].eccentricity
assert_almost_equal(eps, 0)
def test_equiv_diameter():
diameter = regionprops(SAMPLE, ['EquivDiameter'])[0]['EquivDiameter']
diameter = regionprops(SAMPLE)[0].equivalent_diameter
# determined with MATLAB
assert_almost_equal(diameter, 9.57461472963)
def test_euler_number():
en = regionprops(SAMPLE, ['EulerNumber'])[0]['EulerNumber']
en = regionprops(SAMPLE)[0].euler_number
assert en == 0
SAMPLE_mod = SAMPLE.copy()
SAMPLE_mod[7, -3] = 0
en = regionprops(SAMPLE_mod, ['EulerNumber'])[0]['EulerNumber']
en = regionprops(SAMPLE_mod)[0].euler_number
assert en == -1
def test_extent():
extent = regionprops(SAMPLE, ['Extent'])[0]['Extent']
extent = regionprops(SAMPLE)[0].extent
assert_almost_equal(extent, 0.4)
def test_hu_moments():
hu = regionprops(SAMPLE, ['HuMoments'])[0]['HuMoments']
hu = regionprops(SAMPLE)[0].hu_moments
ref = np.array([
3.27117627e-01,
2.63869194e-02,
@@ -145,59 +145,59 @@ def test_hu_moments():
def test_image():
img = regionprops(SAMPLE, ['Image'])[0]['Image']
img = regionprops(SAMPLE)[0].image
assert_array_equal(img, SAMPLE)
def test_filled_area():
area = regionprops(SAMPLE, ['FilledArea'])[0]['FilledArea']
area = regionprops(SAMPLE)[0].filled_area
assert area == np.sum(SAMPLE)
SAMPLE_mod = SAMPLE.copy()
SAMPLE_mod[7, -3] = 0
area = regionprops(SAMPLE_mod, ['FilledArea'])[0]['FilledArea']
area = regionprops(SAMPLE_mod)[0].filled_area
assert area == np.sum(SAMPLE)
def test_filled_image():
img = regionprops(SAMPLE, ['FilledImage'])[0]['FilledImage']
img = regionprops(SAMPLE)[0].filled_image
assert_array_equal(img, SAMPLE)
def test_major_axis_length():
length = regionprops(SAMPLE, ['MajorAxisLength'])[0]['MajorAxisLength']
length = regionprops(SAMPLE)[0].major_axis_length
# MATLAB has different interpretation of ellipse than found in literature,
# here implemented as found in literature
assert_almost_equal(length, 16.7924234999)
def test_max_intensity():
intensity = regionprops(SAMPLE, ['MaxIntensity'], INTENSITY_SAMPLE
)[0]['MaxIntensity']
intensity = regionprops(SAMPLE, intensity_image=INTENSITY_SAMPLE
)[0].max_intensity
assert_almost_equal(intensity, 2)
def test_mean_intensity():
intensity = regionprops(SAMPLE, ['MeanIntensity'], INTENSITY_SAMPLE
)[0]['MeanIntensity']
intensity = regionprops(SAMPLE, intensity_image=INTENSITY_SAMPLE
)[0].mean_intensity
assert_almost_equal(intensity, 1.02777777777777)
def test_min_intensity():
intensity = regionprops(SAMPLE, ['MinIntensity'], INTENSITY_SAMPLE
)[0]['MinIntensity']
intensity = regionprops(SAMPLE, intensity_image=INTENSITY_SAMPLE
)[0].min_intensity
assert_almost_equal(intensity, 1)
def test_minor_axis_length():
length = regionprops(SAMPLE, ['MinorAxisLength'])[0]['MinorAxisLength']
length = regionprops(SAMPLE)[0].minor_axis_length
# MATLAB has different interpretation of ellipse than found in literature,
# here implemented as found in literature
assert_almost_equal(length, 9.739302807263)
def test_moments():
m = regionprops(SAMPLE, ['Moments'])[0]['Moments']
m = regionprops(SAMPLE)[0].moments
# determined with OpenCV
assert_almost_equal(m[0,0], 72.0)
assert_almost_equal(m[0,1], 408.0)
@@ -212,7 +212,7 @@ def test_moments():
def test_normalized_moments():
nu = regionprops(SAMPLE, ['NormalizedMoments'])[0]['NormalizedMoments']
nu = regionprops(SAMPLE)[0].normalized_moments
# determined with OpenCV
assert_almost_equal(nu[0,2], 0.08410493827160502)
assert_almost_equal(nu[1,1], -0.016846707818929982)
@@ -223,29 +223,26 @@ def test_normalized_moments():
def test_orientation():
orientation = regionprops(SAMPLE, ['Orientation'])[0]['Orientation']
orientation = regionprops(SAMPLE)[0].orientation
# determined with MATLAB
assert_almost_equal(orientation, 0.10446844651921)
# test correct quadrant determination
orientation2 = regionprops(SAMPLE.T, ['Orientation'])[0]['Orientation']
orientation2 = regionprops(SAMPLE.T)[0].orientation
assert_almost_equal(orientation2, math.pi / 2 - orientation)
# test diagonal regions
diag = np.eye(10, dtype=int)
orientation_diag = regionprops(diag, ['Orientation'])[0]['Orientation']
orientation_diag = regionprops(diag)[0].orientation
assert_almost_equal(orientation_diag, -math.pi / 4)
orientation_diag = regionprops(np.flipud(diag), ['Orientation']
)[0]['Orientation']
orientation_diag = regionprops(np.flipud(diag))[0].orientation
assert_almost_equal(orientation_diag, math.pi / 4)
orientation_diag = regionprops(np.fliplr(diag), ['Orientation']
)[0]['Orientation']
orientation_diag = regionprops(np.fliplr(diag))[0].orientation
assert_almost_equal(orientation_diag, math.pi / 4)
orientation_diag = regionprops(np.fliplr(np.flipud(diag)), ['Orientation']
)[0]['Orientation']
orientation_diag = regionprops(np.fliplr(np.flipud(diag)))[0].orientation
assert_almost_equal(orientation_diag, -math.pi / 4)
def test_perimeter():
per = regionprops(SAMPLE, ['Perimeter'])[0]['Perimeter']
per = regionprops(SAMPLE)[0].perimeter
assert_almost_equal(per, 55.2487373415)
per = perimeter(SAMPLE.astype('double'), neighbourhood=8)
@@ -253,14 +250,14 @@ def test_perimeter():
def test_solidity():
solidity = regionprops(SAMPLE, ['Solidity'])[0]['Solidity']
solidity = regionprops(SAMPLE)[0].solidity
# determined with MATLAB
assert_almost_equal(solidity, 0.580645161290323)
def test_weighted_central_moments():
wmu = regionprops(SAMPLE, ['WeightedCentralMoments'], INTENSITY_SAMPLE
)[0]['WeightedCentralMoments']
wmu = regionprops(SAMPLE, intensity_image=INTENSITY_SAMPLE
)[0].weighted_central_moments
ref = np.array(
[[ 7.4000000000e+01, -2.1316282073e-13, 4.7837837838e+02,
-7.5943608473e+02],
@@ -276,14 +273,14 @@ def test_weighted_central_moments():
def test_weighted_centroid():
centroid = regionprops(SAMPLE, ['WeightedCentroid'], INTENSITY_SAMPLE
)[0]['WeightedCentroid']
centroid = regionprops(SAMPLE, intensity_image=INTENSITY_SAMPLE
)[0].weighted_centroid
assert_array_almost_equal(centroid, (5.540540540540, 9.445945945945))
def test_weighted_hu_moments():
whu = regionprops(SAMPLE, ['WeightedHuMoments'], INTENSITY_SAMPLE
)[0]['WeightedHuMoments']
whu = regionprops(SAMPLE, intensity_image=INTENSITY_SAMPLE
)[0].weighted_hu_moments
ref = np.array([
3.1750587329e-01,
2.1417517159e-02,
@@ -297,8 +294,8 @@ def test_weighted_hu_moments():
def test_weighted_moments():
wm = regionprops(SAMPLE, ['WeightedMoments'], INTENSITY_SAMPLE
)[0]['WeightedMoments']
wm = regionprops(SAMPLE, intensity_image=INTENSITY_SAMPLE
)[0].weighted_moments
ref = np.array(
[[ 7.4000000000e+01, 4.1000000000e+02, 2.7500000000e+03,
1.9778000000e+04],
@@ -313,8 +310,8 @@ def test_weighted_moments():
def test_weighted_normalized_moments():
wnu = regionprops(SAMPLE, ['WeightedNormalizedMoments'], INTENSITY_SAMPLE
)[0]['WeightedNormalizedMoments']
wnu = regionprops(SAMPLE, intensity_image=INTENSITY_SAMPLE
)[0].weighted_normalized_moments
ref = np.array(
[[ np.nan, np.nan, 0.0873590903, -0.0161217406],
[ np.nan, -0.0160405109, -0.0031421072, -0.0031376984],