scikit-image
171 строка · 6.4 Кб
1"""Analytical transformations from raw image moments to central moments.
2
3The expressions for the 2D central moments of order <=2 are often given in
4textbooks. Expressions for higher orders and dimensions were generated in SymPy
5using ``tools/precompute/moments_sympy.py`` in the GitHub repository.
6
7"""
8
9import itertools10import math11
12import numpy as np13
14
15def _moments_raw_to_central_fast(moments_raw):16"""Analytical formulae for 2D and 3D central moments of order < 4.17
18`moments_raw_to_central` will automatically call this function when
19ndim < 4 and order < 4.
20
21Parameters
22----------
23moments_raw : ndarray
24The raw moments.
25
26Returns
27-------
28moments_central : ndarray
29The central moments.
30"""
31ndim = moments_raw.ndim32order = moments_raw.shape[0] - 133float_dtype = moments_raw.dtype34# convert to float64 during the computation for better accuracy35moments_raw = moments_raw.astype(np.float64, copy=False)36moments_central = np.zeros_like(moments_raw)37if order >= 4 or ndim not in [2, 3]:38raise ValueError("This function only supports 2D or 3D moments of order < 4.")39m = moments_raw40if ndim == 2:41cx = m[1, 0] / m[0, 0]42cy = m[0, 1] / m[0, 0]43moments_central[0, 0] = m[0, 0]44# Note: 1st order moments are both 045if order > 1:46# 2nd order moments47moments_central[1, 1] = m[1, 1] - cx * m[0, 1]48moments_central[2, 0] = m[2, 0] - cx * m[1, 0]49moments_central[0, 2] = m[0, 2] - cy * m[0, 1]50if order > 2:51# 3rd order moments52moments_central[2, 1] = (53m[2, 1]54- 2 * cx * m[1, 1]55- cy * m[2, 0]56+ cx**2 * m[0, 1]57+ cy * cx * m[1, 0]58)59moments_central[1, 2] = (60m[1, 2] - 2 * cy * m[1, 1] - cx * m[0, 2] + 2 * cy * cx * m[0, 1]61)62moments_central[3, 0] = m[3, 0] - 3 * cx * m[2, 0] + 2 * cx**2 * m[1, 0]63moments_central[0, 3] = m[0, 3] - 3 * cy * m[0, 2] + 2 * cy**2 * m[0, 1]64else:65# 3D case66cx = m[1, 0, 0] / m[0, 0, 0]67cy = m[0, 1, 0] / m[0, 0, 0]68cz = m[0, 0, 1] / m[0, 0, 0]69moments_central[0, 0, 0] = m[0, 0, 0]70# Note: all first order moments are 071if order > 1:72# 2nd order moments73moments_central[0, 0, 2] = -cz * m[0, 0, 1] + m[0, 0, 2]74moments_central[0, 1, 1] = -cy * m[0, 0, 1] + m[0, 1, 1]75moments_central[0, 2, 0] = -cy * m[0, 1, 0] + m[0, 2, 0]76moments_central[1, 0, 1] = -cx * m[0, 0, 1] + m[1, 0, 1]77moments_central[1, 1, 0] = -cx * m[0, 1, 0] + m[1, 1, 0]78moments_central[2, 0, 0] = -cx * m[1, 0, 0] + m[2, 0, 0]79if order > 2:80# 3rd order moments81moments_central[0, 0, 3] = (822 * cz**2 * m[0, 0, 1] - 3 * cz * m[0, 0, 2] + m[0, 0, 3]83)84moments_central[0, 1, 2] = (85-cy * m[0, 0, 2] + 2 * cz * (cy * m[0, 0, 1] - m[0, 1, 1]) + m[0, 1, 2]86)87moments_central[0, 2, 1] = (88cy**2 * m[0, 0, 1]89- 2 * cy * m[0, 1, 1]90+ cz * (cy * m[0, 1, 0] - m[0, 2, 0])91+ m[0, 2, 1]92)93moments_central[0, 3, 0] = (942 * cy**2 * m[0, 1, 0] - 3 * cy * m[0, 2, 0] + m[0, 3, 0]95)96moments_central[1, 0, 2] = (97-cx * m[0, 0, 2] + 2 * cz * (cx * m[0, 0, 1] - m[1, 0, 1]) + m[1, 0, 2]98)99moments_central[1, 1, 1] = (100-cx * m[0, 1, 1]101+ cy * (cx * m[0, 0, 1] - m[1, 0, 1])102+ cz * (cx * m[0, 1, 0] - m[1, 1, 0])103+ m[1, 1, 1]104)105moments_central[1, 2, 0] = (106-cx * m[0, 2, 0] - 2 * cy * (-cx * m[0, 1, 0] + m[1, 1, 0]) + m[1, 2, 0]107)108moments_central[2, 0, 1] = (109cx**2 * m[0, 0, 1]110- 2 * cx * m[1, 0, 1]111+ cz * (cx * m[1, 0, 0] - m[2, 0, 0])112+ m[2, 0, 1]113)114moments_central[2, 1, 0] = (115cx**2 * m[0, 1, 0]116- 2 * cx * m[1, 1, 0]117+ cy * (cx * m[1, 0, 0] - m[2, 0, 0])118+ m[2, 1, 0]119)120moments_central[3, 0, 0] = (1212 * cx**2 * m[1, 0, 0] - 3 * cx * m[2, 0, 0] + m[3, 0, 0]122)123
124return moments_central.astype(float_dtype, copy=False)125
126
127def moments_raw_to_central(moments_raw):128ndim = moments_raw.ndim129order = moments_raw.shape[0] - 1130if ndim in [2, 3] and order < 4:131return _moments_raw_to_central_fast(moments_raw)132
133moments_central = np.zeros_like(moments_raw)134m = moments_raw135# centers as computed in centroid above136centers = tuple(m[tuple(np.eye(ndim, dtype=int))] / m[(0,) * ndim])137
138if ndim == 2:139# This is the general 2D formula from140# https://en.wikipedia.org/wiki/Image_moment#Central_moments141for p in range(order + 1):142for q in range(order + 1):143if p + q > order:144continue145for i in range(p + 1):146term1 = math.comb(p, i)147term1 *= (-centers[0]) ** (p - i)148for j in range(q + 1):149term2 = math.comb(q, j)150term2 *= (-centers[1]) ** (q - j)151moments_central[p, q] += term1 * term2 * m[i, j]152return moments_central153
154# The nested loops below are an n-dimensional extension of the 2D formula155# given at https://en.wikipedia.org/wiki/Image_moment#Central_moments156
157# iterate over all [0, order] (inclusive) on each axis158for orders in itertools.product(*((range(order + 1),) * ndim)):159# `orders` here is the index into the `moments_central` output array160if sum(orders) > order:161# skip any moment that is higher than the requested order162continue163# loop over terms from `m` contributing to `moments_central[orders]`164for idxs in itertools.product(*[range(o + 1) for o in orders]):165val = m[idxs]166for i_order, c, idx in zip(orders, centers, idxs):167val *= math.comb(i_order, idx)168val *= (-c) ** (i_order - idx)169moments_central[orders] += val170
171return moments_central172