我正在检测圆形物体的边缘并获得“凹凸不平”的不规则边缘。是否有使边缘光滑的方法,以使我的形状更均匀?
例如,在下面的代码中,我生成了一个“凹凸不平”的圆圈(左)。我可以使用某种平滑或移动平均函数来获得或近似“平滑”圆(右)。最好使用某种参数,因为我的实际图像不是完美的圆形,所以我可以控制它。
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))
答案 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()
上面的质心计算之所以有效,是因为这些点在椭圆周围均匀分布。如果不是这种情况,请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')
答案 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滤波器的多种应用: