平滑颠簸的圆圈

时间:2018-07-10 07:22:28

标签: python numpy image-processing geometry scikit-image

我正在检测圆形物体的边缘并获得“凹凸不平”的不规则边缘。是否有使边缘光滑的方法,以使我的形状更均匀?

例如,在下面的代码中,我生成了一个“凹凸不平”的圆圈(左)。我可以使用某种平滑或移动平均函数来获得或近似“平滑”圆(右)。最好使用某种参数,因为我的实际图像不是完美的圆形,所以我可以控制它。

import numpy as np
import matplotlib.pyplot as plt

fig, (bumpy, smooth) = plt.subplots(ncols=2, figsize=(14, 7))

an = np.linspace(0, 2 * np.pi, 100) 

bumpy.plot(3 * np.cos(an) + np.random.normal(0,.03,100), 3 * np.sin(an) + np.random.normal(0,.03,100))

smooth.plot(3 * np.cos(an), 3 * np.sin(an))

bumpy and smooth circle

3 个答案:

答案 0 :(得分:6)

如果所讨论的形状近似于椭圆,并且您想强迫它们成为实际的椭圆,则可以通过计算点集的惯性矩轻松地拟合椭圆,并从中计算椭圆的参数那些。 This Q&A shows how to accomplish that using MATLAB,该代码易于转换为Python:

import numpy as np
import matplotlib.pyplot as plt

# Some test data (from Question, but with offset added):
an = np.linspace(0, 2 * np.pi, 100) 
x = 3 * np.cos(an) + np.random.normal(0,.03,100) + 3.8
y = 3 * np.sin(an) + np.random.normal(0,.03,100) + 5.4

plt.plot(x, y)

# Approximate the ellipse:
L, V = np.linalg.eig(np.cov(x, y))
r = np.sqrt(2*L)                       # radius
cos_phi = V[0, 0]
sin_phi = V[1, 0]                      # two components of minor axis direction
m = np.array([np.mean(x), np.mean(y)]) # centroid

# Draw the ellipse:
x_approx = r[0] * np.cos(an) * cos_phi - r[1] * np.sin(an) * sin_phi + m[0];
y_approx = r[0] * np.cos(an) * sin_phi + r[1] * np.sin(an) * cos_phi + m[1];

plt.plot(x_approx, y_approx, 'r')
plt.show()

fitted ellipse

上面的质心计算之所以有效,是因为这些点在椭圆周围均匀分布。如果不是这种情况,请a slightly more complex centroid computation is needed

答案 1 :(得分:5)

您可以在频域中执行此操作。取曲线点的(x,y)坐标并将信号构造为signal = x + yj,然后对该信号进行傅立叶变换。过滤掉高频分量,然后进行傅立叶逆变换,您将获得一条平滑的曲线。您可以通过调整截止频率来控制平滑度。

这是一个例子:

import numpy as np
from matplotlib import pyplot as plt

r = 3
theta = np.linspace(0, 2 * np.pi, 100) 
noise_level = 2
# construct the signal
x = r *  np.cos(theta) + noise_level * np.random.normal(0,.03,100)
y = r *  np.sin(theta) + noise_level * np.random.normal(0,.03,100)
signal = x + 1j*y
# FFT and frequencies
fft = np.fft.fft(signal)
freq = np.fft.fftfreq(signal.shape[-1])
# filter
cutoff = 0.1
fft[np.abs(freq) > cutoff] = 0
# IFFT
signal_filt = np.fft.ifft(fft)

plt.figure()
plt.subplot(121)
plt.axis('equal')
plt.plot(x, y, label='Noisy')
plt.subplot(122)
plt.axis('equal')
plt.plot(signal_filt.real, signal_filt.imag, label='Smooth')

figure

答案 2 :(得分:3)

我建议使用FIR滤波器。背后的想法是使用点的加权平均值围绕过滤后的点...您只需执行

p(i) = 0.5*p(i) + 0.25*p(i-1) + 0.25*p(i+1) 

其中p(i)i-th点。记住原始的p(i)是个好主意,因为它用于下一次迭代(避免移位)。您可以分别处理每个轴。您可以使用任何权重,但它们的总和应为1.0。您可以使用任意数量的邻居,而不仅仅是2个(但在这种情况下,您需要记住更多点)。对称的权重将减少偏移。您可以多次应用FIR ...

以下为 2D C ++ 示例:

//---------------------------------------------------------------------------
const int n=50;     // points
float pnt[n][2];    // points x,y ...
//---------------------------------------------------------------------------
void pnt_init()
    {
    int i;
    float a,da=2.0*M_PI/float(n),r;
    Randomize();
    for (a=0.0,i=0;i<n;i++,a+=da)
        {
        r=0.75+(0.2*Random());
        pnt[i][0]=r*cos(a);
        pnt[i][1]=r*sin(a);
        }
    }
//---------------------------------------------------------------------------
void pnt_smooth()
    {
    int i,j;
    float p0[2],*p1,*p2,tmp[2],aa0[2],aa1[2],bb0[2],bb1[2];
    // bb = original BBOX
    for (j=0;j<2;j++) { bb0[j]=pnt[0][j]; bb1[j]=pnt[0][j]; }
    for (i=0;i<n;i++)
     for (p1=pnt[i],j=0;j<2;j++)
        {
        if (bb0[j]>p1[j]) bb0[j]=p1[j];
        if (bb1[j]<p1[j]) bb1[j]=p1[j];
        }
    // FIR filter
    for (j=0;j<2;j++) p0[j]=pnt[n-1][j];                    // remember p[i-1]
    p1=pnt[0]; p2=pnt[1];                                   // pointers to p[i],p[i+1]
    for (i=0;i<n;i++,p1=p2,p2=pnt[(i+1)%n])
        {
        for (j=0;j<2;j++)
            {
            tmp[j]=p1[j];                                   // store original p[i]
            p1[j]=(0.1*p0[j]) + (0.8*p1[j]) + (0.1*p2[j]);  // p[i] = FIR(p0,p1,p2)
            p0[j]=tmp[j];                                   // remeber original p1 as p[i-1] for next iteration
            }
        }
    // aa = new BBOX
    for (j=0;j<2;j++) { aa0[j]=pnt[0][j]; aa1[j]=pnt[0][j]; }
    for (i=0;i<n;i++)
     for (p1=pnt[i],j=0;j<2;j++)
        {
        if (aa0[j]>p1[j]) aa0[j]=p1[j];
        if (aa1[j]<p1[j]) aa1[j]=p1[j];
        }
    // compute scale transform aa -> bb
    for (j=0;j<2;j++) tmp[j]=(bb1[j]-bb0[j])/(aa1[j]-aa0[j]);   // scale
    // convert aa -> bb
    for (i=0;i<n;i++)
     for (p1=pnt[i],j=0;j<2;j++)
      p1[j]=bb0[0]+((p1[j]-aa0[j])*tmp[j]);
    }
//---------------------------------------------------------------------------

我还添加了在平滑前后检查BBOX的信息,以使形状不会改变大小和位置。在某些情况下,质心优于BBOX。

这里预览了FIR滤波器的多种应用:

preview