泽尼克多项式被广泛用作图像矩的基函数。由于泽尼克多项式彼此正交,泽尼克矩可以表示图像的属性,且矩之间没有冗余或信息重叠。尽管泽尼克矩很大程度上取决于感兴趣区域中对象的缩放和平移,但其幅度与对象的旋转角度无关。因此,它们可用于从图像中提取描述对象形状特征的特征。例如,泽尼克矩被用作形状描述符,以对良性和恶性乳腺肿块进行分类或振动盘的表面。泽尼克矩还被用于在单细胞水平上量化骨肉瘤癌细胞系的形状。此外,泽尼克矩已用于早期发现阿尔茨海默病,方法是从阿尔茨海默病、轻度认知障碍和健康人群的 MR 图像中提取判别信息。

泽尼克矩是一种图像描述符,用于表征图像中对象的形状。要描述的形状可以是分割的二值图像,也可以是对象的边界(即形状的“轮廓”或“轮廓”)。在大多数应用中,最好使用分割的二值图像而不仅仅是轮廓,因为分割的二值图像不易受噪声影响。

泽尼克矩数学形态

泽尼克矩使用复泽尼克多项式作为矩基组。二维泽尼克矩 $Z_{n m}$,阶数 $n$,重复 $m$,在单位圆内的极坐标 $(r, θ)$ 中定义为

$$ \begin{gathered} Z_{n m}=\frac{n+1}{\pi} \int_0^1 \int_0^{2 \pi} R_m(r) e^{-j m \theta} f(r, \theta) r d r d \theta, 0 \leq|m| \leq n, n-|m| \text { 是偶数 } \end{gathered} $$

其中 $R_{n m}(r)$ 是泽尼克径向多项式的 $n$ 阶,由下式给出

$$ \begin{gathered} R_{n m}(r)= \\ \sum_{k=0}^{(n-|m|) / 2}(-1)^k \frac{(n-k)!}{k!\lfloor(n-2 k+|m|) / 2\rfloor!\lfloor(n-2 k-|m|) / 2\rfloor!} r^{n-2 k} \end{gathered} $$

与旋转矩和复矩一样,泽尼克矩的大小在图像旋转变换下是不变的。图像可以使用 M 阶矩的集合来重建为

$$ f(r, \theta) \approx \sum_{n=0}^M \sum_m Z_{n m} R_{n m}(r) e^{j m \theta} $$

Python计算泽尼克矩

示例一:4

_slow_zernike_poly 函数构造二维泽尼克基函数。在 zernike_reconstruct 函数中,我们将图像投影到 _slow_zernike_poly 返回的基函数上并计算矩。然后我们使用重建公式。

import numpy as np
from math import atan2
from numpy import cos, sin, conjugate, sqrt

def _slow_zernike_poly(Y,X,n,l):
    def _polar(r,theta):
        x = r * cos(theta)
        y = r * sin(theta)
        return 1.*x+1.j*y

    def _factorial(n):
        if n == 0: return 1.
        return n * _factorial(n - 1)
    y,x = Y[0],X[0]
    vxy = np.zeros(Y.size, dtype=complex)
    index = 0
    for x,y in zip(X,Y):
        Vnl = 0.
        for m in range( int( (n-l)//2 ) + 1 ):
            Vnl += (-1.)**m * _factorial(n-m) /  \\\\
                ( _factorial(m) * _factorial((n - 2*m + l) // 2) * _factorial((n - 2*m - l) // 2) ) * \\\\
                ( sqrt(x*x + y*y)**(n - 2*m) * _polar(1.0, l*atan2(y,x)) )
        vxy[index] = Vnl
        index = index + 1

    return vxy

def zernike_reconstruct(img, radius, D, cof):

    idx = np.ones(img.shape)

    cofy,cofx = cof
    cofy = float(cofy)
    cofx = float(cofx)
    radius = float(radius)

    Y,X = np.where(idx > 0)
    P = img[Y,X].ravel()
    Yn = ( (Y -cofy)/radius).ravel()
    Xn = ( (X -cofx)/radius).ravel()

    k = (np.sqrt(Xn**2 + Yn**2) <= 1.)
    frac_center = np.array(P[k], np.double)
    Yn = Yn[k]
    Xn = Xn[k]
    frac_center = frac_center.ravel()

    npix = float(frac_center.size)

    reconstr = np.zeros(img.size, dtype=complex)
    accum = np.zeros(Yn.size, dtype=complex)

    for n in range(D+1):
        for l in range(n+1):
            if (n-l)%2 == 0:
                vxy = _slow_zernike_poly(Yn, Xn, float(n), float(l))
                a = sum(frac_center * conjugate(vxy)) * (n + 1)/npix
                accum += a * vxy
    reconstr[k] = accum
    return reconstr

if __name__ == '__main__':

    import cv2
    import pylab as pl
    from matplotlib import cm

    D = 12

    img = cv2.imread('fl.bmp', 0)
    rows, cols = img.shape
    radius = cols//2 if rows > cols else rows//2
    reconst = zernike_reconstruct(img, radius, D, (rows/2., cols/2.))
    reconst = reconst.reshape(img.shape)
    pl.figure(1)
    pl.imshow(img, cmap=cm.jet, origin = 'upper')
    pl.figure(2)
    pl.imshow(reconst.real, cmap=cm.jet, origin = 'upper')

示例二:

我们将学习应用泽尼克矩矩实际识别图像中的对象。我们需要 2 张图像:第一个图像将是我们要检测的对象的参考图像。第二张图像将是一个干扰物图像,其中包含 (1) 我们想要查找和识别的对象,以及 (2) 一堆旨在“迷惑”我们的算法的“干扰物”对象。我们的目标是成功检测第二张图像中的参考图像。

from scipy.spatial import distance as dist
import numpy as np
import cv2
import imutils

def describe_shapes(image):
    shapeFeatures = []

    gray = cv2.cvtColor(image, cv2.COLOR_BGR2GRAY)
    blurred = cv2.GaussianBlur(gray, (13, 13), 0)
    thresh = cv2.threshold(blurred, 50, 255, cv2.THRESH_BINARY)[1]

    thresh = cv2.dilate(thresh, None, iterations=4)
    thresh = cv2.erode(thresh, None, iterations=2)

    cnts = cv2.findContours(thresh.copy(), cv2.RETR_EXTERNAL,
        cv2.CHAIN_APPROX_SIMPLE)
    cnts = imutils.grab_contours(cnts)

    for c in cnts:
        mask = np.zeros(image.shape[:2], dtype="uint8")
        cv2.drawContours(mask, [c], -1, 255, -1)

        (x, y, w, h) = cv2.boundingRect(c)
        roi = mask[y:y + h, x:x + w]

        features = zerni_moments(roi, cv2.minEnclosingCircle(c)[1], degree=8)
        shapeFeatures.append(features)

    return (cnts, shapeFeatures)

refImage = cv2.imread("pokemon_red.png")
(_, gameFeatures) = describe_shapes(refImage)

shapesImage = cv2.imread("shapes.png")
(cnts, shapeFeatures) = describe_shapes(shapesImage)

D = dist.cdist(gameFeatures, shapeFeatures)
i = np.argmin(D)

for (j, c) in enumerate(cnts):

    if i != j:
        box = cv2.minAreaRect(c)
        box = np.int0(cv2.cv.BoxPoints(box) if imutils.is_cv2() else cv2.boxPoints(box))
        cv2.drawContours(shapesImage, [box], -1, (0, 0, 255), 2)

box = cv2.minAreaRect(cnts[i])
box = np.int0(cv2.cv.BoxPoints(box) if imutils.is_cv2() else cv2.boxPoints(box))
cv2.drawContours(shapesImage, [box], -1, (0, 255, 0), 2)
(x, y, w, h) = cv2.boundingRect(cnts[i])
cv2.putText(shapesImage, "FOUND!", (x, y - 10), cv2.FONT_HERSHEY_SIMPLEX, 0.9,
    (0, 255, 0), 3)
cv2.imshow("Input Image", refImage)
cv2.imshow("Detected Shapes", shapesImage)
cv2.waitKey(0)

要查看实际效果,只需执行以下命令:

$ python detect.py

https://embed.notionlytics.com/wt/ZXlKM2IzSnJjM0JoWTJWVWNtRmphMlZ5U1dRaU9pSlhiRWhvWlV4VVQxbHNjMlZYV2tKbU9URndaU0lzSW5CaFoyVkpaQ0k2SWpJek9USmlOVE5tWWpreVl6UXlOMk01WWpkbE5XUTVOamRoTXpZM1l6Wm1JbjA9