Make moments functions public, rename, add tests

This commit is contained in:
Johannes Schönberger
2013-08-17 18:00:53 +02:00
parent 752e395835
commit 13957123b7
4 changed files with 170 additions and 42 deletions
+6 -1
View File
@@ -2,6 +2,7 @@ from .find_contours import find_contours
from ._regionprops import regionprops, perimeter
from ._structural_similarity import structural_similarity
from ._polygon import approximate_polygon, subdivide_polygon
from ._moments import moments, moments_central, moments_normalized, moments_hu
from .fit import LineModel, CircleModel, EllipseModel, ransac
from .block import block_reduce
@@ -16,4 +17,8 @@ __all__ = ['find_contours',
'CircleModel',
'EllipseModel',
'ransac',
'block_reduce']
'block_reduce',
'moments',
'moments_central',
'moments_normalized',
'moments_hu']
+129 -6
View File
@@ -4,15 +4,79 @@
#cython: wraparound=False
import numpy as np
cimport numpy as cnp
def moments(double[:, :] image, Py_ssize_t order=3):
return central_moments(image, 0, 0, order)
"""Calculate all raw image moments up to a certain order.
The following properties can be calculated from raw image moments:
* Area as ``m[0, 0]``.
* Centroid as {``m[0, 1] / m[0, 0]``, ``m[1, 0] / m[0, 0]``}.
Note that raw moments are whether translation, scale nor rotation
invariant.
Parameters
----------
image : 2D double array
Rasterized shape as image.
order : int, optional
Maximum order of moments. Default is 3.
Returns
-------
m : (``order + 1``, ``order + 1``) array
Raw image moments.
References
----------
.. [1] Wilhelm Burger, Mark Burge. Principles of Digital Image Processing:
Core Algorithms. Springer-Verlag, London, 2009.
.. [2] B. Jähne. Digital Image Processing. Springer-Verlag,
Berlin-Heidelberg, 6. edition, 2005.
.. [3] T. H. Reiss. Recognizing Planar Objects Using Invariant Image
Features, from Lecture notes in computer science, p. 676. Springer,
Berlin, 1993.
.. [4] http://en.wikipedia.org/wiki/Image_moment
"""
return moments_central(image, 0, 0, order)
def central_moments(double[:, :] image, double cr, double cc,
def moments_central(double[:, :] image, double cr, double cc,
Py_ssize_t order=3):
"""Calculate all central image moments up to a certain order.
Note that central moments are translation invariant but not scale and
rotation invariant.
Parameters
----------
image : 2D double array
Rasterized shape as image.
cr : double
Center row coordinate.
cc : double
Center column coordinate.
order : int, optional
Maximum order of moments. Default is 3.
Returns
-------
mu : (``order + 1``, ``order + 1``) array
Central image moments.
References
----------
.. [1] Wilhelm Burger, Mark Burge. Principles of Digital Image Processing:
Core Algorithms. Springer-Verlag, London, 2009.
.. [2] B. Jähne. Digital Image Processing. Springer-Verlag,
Berlin-Heidelberg, 6. edition, 2005.
.. [3] T. H. Reiss. Recognizing Planar Objects Using Invariant Image
Features, from Lecture notes in computer science, p. 676. Springer,
Berlin, 1993.
.. [4] http://en.wikipedia.org/wiki/Image_moment
"""
cdef Py_ssize_t p, q, r, c
cdef double[:, ::1] mu = np.zeros((order + 1, order + 1), dtype=np.double)
for p in range(order + 1):
@@ -23,7 +87,36 @@ def central_moments(double[:, :] image, double cr, double cc,
return np.asarray(mu)
def normalized_moments(double[:, :] mu, Py_ssize_t order=3):
def moments_normalized(double[:, :] mu, Py_ssize_t order=3):
"""Calculate all normalized central image moments up to a certain order.
Note that normalized central moments are translation and scale invariant
but not rotation invariant.
Parameters
----------
mu : (M, M) array
Central image moments, where M must be > ``order``.
order : int, optional
Maximum order of moments. Default is 3.
Returns
-------
nu : (``order + 1``, ``order + 1``) array
Normalized central image moments.
References
----------
.. [1] Wilhelm Burger, Mark Burge. Principles of Digital Image Processing:
Core Algorithms. Springer-Verlag, London, 2009.
.. [2] B. Jähne. Digital Image Processing. Springer-Verlag,
Berlin-Heidelberg, 6. edition, 2005.
.. [3] T. H. Reiss. Recognizing Planar Objects Using Invariant Image
Features, from Lecture notes in computer science, p. 676. Springer,
Berlin, 1993.
.. [4] http://en.wikipedia.org/wiki/Image_moment
"""
cdef Py_ssize_t p, q
cdef double[:, ::1] nu = np.zeros((order + 1, order + 1), dtype=np.double)
for p in range(order + 1):
@@ -35,7 +128,37 @@ def normalized_moments(double[:, :] mu, Py_ssize_t order=3):
return np.asarray(nu)
def hu_moments(double[:, :] nu):
def moments_hu(double[:, :] nu):
"""Calculate Hu's set of image moments.
Note that this set of moments is proofed to be translation, scale and
rotation invariant.
Parameters
----------
nu : (M, M) array
Normalized central image moments, where M must be > 4.
Returns
-------
nu : (7, 1) array
Hu's set of image moments.
References
----------
.. [1] M. K. Hu, "Visual Pattern Recognition by Moment Invariants",
IRE Trans. Info. Theory, vol. IT-8, pp. 179-187, 1962
.. [2] Wilhelm Burger, Mark Burge. Principles of Digital Image Processing:
Core Algorithms. Springer-Verlag, London, 2009.
.. [3] B. Jähne. Digital Image Processing. Springer-Verlag,
Berlin-Heidelberg, 6. edition, 2005.
.. [4] T. H. Reiss. Recognizing Planar Objects Using Invariant Image
Features, from Lecture notes in computer science, p. 676. Springer,
Berlin, 1993.
.. [5] http://en.wikipedia.org/wiki/Image_moment
"""
cdef double[::1] hu = np.zeros((7, ), dtype=np.double)
cdef double t0 = nu[3, 0] + nu[1, 2]
cdef double t1 = nu[2, 1] + nu[0, 3]
+23 -23
View File
@@ -18,7 +18,7 @@ STREL_8 = np.ones((3, 3), 'int8')
PROPS = {
'Area': 'area',
'BoundingBox': 'bbox',
'CentralMoments': 'central_moments',
'CentralMoments': 'moments_central',
'Centroid': 'centroid',
'ConvexArea': 'convex_area',
# 'ConvexHull',
@@ -31,7 +31,7 @@ PROPS = {
# 'Extrema',
'FilledArea': 'filled_area',
'FilledImage': 'filled_image',
'HuMoments': 'hu_moments',
'HuMoments': 'moments_hu',
'Image': 'image',
'Label': 'label',
'MajorAxisLength': 'major_axis_length',
@@ -40,7 +40,7 @@ PROPS = {
'MinIntensity': 'min_intensity',
'MinorAxisLength': 'minor_axis_length',
'Moments': 'moments',
'NormalizedMoments': 'normalized_moments',
'NormalizedMoments': 'moments_normalized',
'Orientation': 'orientation',
'Perimeter': 'perimeter',
# 'PixelIdxList',
@@ -49,9 +49,9 @@ PROPS = {
# 'SubarrayIdx'
'WeightedCentralMoments': 'weighted_central_moments',
'WeightedCentroid': 'weighted_centroid',
'WeightedHuMoments': 'weighted_hu_moments',
'WeightedHuMoments': 'weighted_moments_hu',
'WeightedMoments': 'weighted_moments',
'WeightedNormalizedMoments': 'weighted_normalized_moments'
'WeightedNormalizedMoments': 'weighted_moments_normalized'
}
@@ -128,9 +128,9 @@ class _RegionProperties(object):
return row + self._slice[0].start, col + self._slice[1].start
@_cached_property
def central_moments(self):
def moments_central(self):
row, col = self.local_centroid
return _moments.central_moments(self._image_double, row, col, 3)
return _moments.moments_central(self._image_double, row, col, 3)
@_cached_property
def convex_area(self):
@@ -177,8 +177,8 @@ class _RegionProperties(object):
return ndimage.binary_fill_holes(self.image, STREL_8)
@_cached_property
def hu_moments(self):
return _moments.hu_moments(self.normalized_moments)
def moments_hu(self):
return _moments.moments_hu(self.moments_normalized)
@_cached_property
def image(self):
@@ -190,7 +190,7 @@ class _RegionProperties(object):
@_cached_property
def inertia_tensor(self):
mu = self.central_moments
mu = self.moments_central
a = mu[2, 0] / mu[0, 0]
b = -mu[1, 1] / mu[0, 0]
c = mu[0, 2] / mu[0, 0]
@@ -248,8 +248,8 @@ class _RegionProperties(object):
return 4 * sqrt(l2)
@_cached_property
def normalized_moments(self):
return _moments.normalized_moments(self.central_moments, 3)
def moments_normalized(self):
return _moments.moments_normalized(self.moments_central, 3)
@_cached_property
def orientation(self):
@@ -274,7 +274,7 @@ class _RegionProperties(object):
@_cached_property
def weighted_central_moments(self):
row, col = self.weighted_local_centroid
return _moments.central_moments(self._intensity_image_double,
return _moments.moments_central(self._intensity_image_double,
row, col, 3)
@_cached_property
@@ -290,16 +290,16 @@ class _RegionProperties(object):
return row, col
@_cached_property
def weighted_hu_moments(self):
return _moments.hu_moments(self.weighted_normalized_moments)
def weighted_moments_hu(self):
return _moments.moments_hu(self.weighted_moments_normalized)
@_cached_property
def weighted_moments(self):
return _moments.central_moments(self._intensity_image_double, 0, 0, 3)
return _moments.moments_central(self._intensity_image_double, 0, 0, 3)
@_cached_property
def weighted_normalized_moments(self):
return _moments.normalized_moments(self.weighted_central_moments, 3)
def weighted_moments_normalized(self):
return _moments.moments_normalized(self.weighted_central_moments, 3)
def __getitem__(self, key):
value = getattr(self, key, None)
@@ -347,7 +347,7 @@ def regionprops(label_image, properties=None,
Number of pixels of region.
**bbox** : tuple
Bounding box `(min_row, min_col, max_row, max_col)`
**central_moments** : (3, 3) ndarray
**moments_central** : (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 }
@@ -379,7 +379,7 @@ def regionprops(label_image, properties=None,
**filled_image** : (H, J) ndarray
Binary region image with filled holes which has the same size as
bounding box.
**hu_moments** : tuple
**moments_hu** : tuple
Hu moments (translation, scale and rotation invariant).
**image** : (H, J) ndarray
Sliced binary region image which has the same size as bounding box.
@@ -407,7 +407,7 @@ def regionprops(label_image, properties=None,
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
**moments_normalized** : (3, 3) ndarray
Normalized moments (translation and scale invariant) up to 3rd order::
nu_ji = mu_ji / m_00^[(i+j)/2 + 1]
@@ -433,7 +433,7 @@ def regionprops(label_image, properties=None,
**weighted_centroid** : array
Centroid coordinate tuple `(row, col)` weighted with intensity
image.
**weighted_hu_moments** : tuple
**weighted_moments_hu** : tuple
Hu moments (translation, scale and rotation invariant) of intensity
image.
**weighted_moments** : (3, 3) ndarray
@@ -442,7 +442,7 @@ def regionprops(label_image, properties=None,
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
**weighted_moments_normalized** : (3, 3) ndarray
Normalized moments (translation and scale invariant) of intensity
image up to 3rd order::
+12 -12
View File
@@ -47,8 +47,8 @@ def test_bbox():
assert_array_almost_equal(bbox, (0, 0, SAMPLE.shape[0], SAMPLE.shape[1]-1))
def test_central_moments():
mu = regionprops(SAMPLE)[0].central_moments
def test_moments_central():
mu = regionprops(SAMPLE)[0].moments_central
# determined with OpenCV
assert_almost_equal(mu[0,2], 436.00000000000045)
# different from OpenCV results, bug in OpenCV
@@ -129,8 +129,8 @@ def test_extent():
assert_almost_equal(extent, 0.4)
def test_hu_moments():
hu = regionprops(SAMPLE)[0].hu_moments
def test_moments_hu():
hu = regionprops(SAMPLE)[0].moments_hu
ref = np.array([
3.27117627e-01,
2.63869194e-02,
@@ -216,8 +216,8 @@ def test_moments():
assert_almost_equal(m[3,0], 95588.0)
def test_normalized_moments():
nu = regionprops(SAMPLE)[0].normalized_moments
def test_moments_normalized():
nu = regionprops(SAMPLE)[0].moments_normalized
# determined with OpenCV
assert_almost_equal(nu[0,2], 0.08410493827160502)
assert_almost_equal(nu[1,1], -0.016846707818929982)
@@ -260,9 +260,9 @@ def test_solidity():
assert_almost_equal(solidity, 0.580645161290323)
def test_weighted_central_moments():
def test_weighted_moments():
wmu = regionprops(SAMPLE, intensity_image=INTENSITY_SAMPLE
)[0].weighted_central_moments
)[0].weighted_moments_central
ref = np.array(
[[ 7.4000000000e+01, -2.1316282073e-13, 4.7837837838e+02,
-7.5943608473e+02],
@@ -283,9 +283,9 @@ def test_weighted_centroid():
assert_array_almost_equal(centroid, (5.540540540540, 9.445945945945))
def test_weighted_hu_moments():
def test_weighted_moments_hu():
whu = regionprops(SAMPLE, intensity_image=INTENSITY_SAMPLE
)[0].weighted_hu_moments
)[0].weighted_moments_hu
ref = np.array([
3.1750587329e-01,
2.1417517159e-02,
@@ -314,9 +314,9 @@ def test_weighted_moments():
assert_array_almost_equal(wm, ref)
def test_weighted_normalized_moments():
def test_weighted_moments_normalized():
wnu = regionprops(SAMPLE, intensity_image=INTENSITY_SAMPLE
)[0].weighted_normalized_moments
)[0].weighted_moments_normalized
ref = np.array(
[[ np.nan, np.nan, 0.0873590903, -0.0161217406],
[ np.nan, -0.0160405109, -0.0031421072, -0.0031376984],