【发布时间】:2019-11-12 18:24:53
【问题描述】:
我在每张图片中有很多由圆圈组成的图片(从 1 到 4 不等)。我试图通过沿圆形路径填充丢失的像素来获得清晰的圆形图像。
我尝试过霍夫变换,但它的参数是特定于图像的:对于每个图像,我都必须更改参数。有了这个问题,我无法将它们放在一个 for 循环中。
【问题讨论】:
标签: python-3.x matlab image-processing image-segmentation edge-detection
我在每张图片中有很多由圆圈组成的图片(从 1 到 4 不等)。我试图通过沿圆形路径填充丢失的像素来获得清晰的圆形图像。
我尝试过霍夫变换,但它的参数是特定于图像的:对于每个图像,我都必须更改参数。有了这个问题,我无法将它们放在一个 for 循环中。
【问题讨论】:
标签: python-3.x matlab image-processing image-segmentation edge-detection
imfindcircles 不起作用
解决这个问题最“自然”的方法是使用 Matlab 的imfindcircles。但是,该函数假定图像中的圆圈是“完整的”,但在您的示例中,您只有圆圈的(不完整)边界,因此 imfindcircles 不能直接应用于您的数据。
替代方法
您可以使用ransac 将圆圈与您的数据相匹配。一次将一个圆拟合到尽可能多的点,当剩下的点太少根本不适合任何圆时终止。
要使用RanSac,你基本上需要实现两个方法:
模型拟合方法,fitFcn,给定一个小样本点 - 为它们拟合一个圆。
到模型的距离方法,distFcn,给定一个圆(“模型”),找出每个点到那个圆的距离。
一旦有了这两种方法,RanSac 的操作大致如下:
- 随机抽样很少的点
- 使用fitFcn 将圆拟合到这些采样点
- 使用distFcn 计算所有点到估计圆的距离
- 如果有足够多的点靠近圆,则接受该圆并删除所有“属于”该圆的点
- 如果没有找到圆圈或没有足够的“无法解释”的点,则终止
这在 Matlab 中很容易实现。
首先考虑fitFcn:我们需要一个计算(cx、cy、r)的函数——二维圆的三个参数(中心和半径)。给定一个点 (x, y) 它适合一个圆当且仅当
(x - cx)^2 + (y - cy)^2 = r^2
我们可以将这个方程写成已知点(x、y)和未知圆(cx、cy、r)之间的线性关系,如下所示
[-2*x, -2*y, 1] [ cx ;
cy ; = [-x^2-y^2]
cx^2 + cy^2 - r^2 ]
使用最小二乘估计(以与this answer 类似的方式),我们可以在给定圆上足够多的点(至少 3 个)的情况下恢复圆参数
这就是代码的实际样子
function crc = fit_circle(xy) % xy is n-by-2 matrix of point coordinates
% fit in least squares sens
x = xy(:, 1);
y = xy(:, 2);
X = [-2*x, -2*y, ones(size(x))];
Y = -x.^2 - y.^2;
crc = (X\Y).'; % least squares solution
r2 = -crc(3) +crc(1).^2 + crc(2).^2;
if r2 <= 0
crc(3) = 0;
else
crc(3) = sqrt(r2);
end
% output crc is a 3 vector (cx, cy, r)
现在我们可以用圆拟合点,我们需要使用 distFcn 计算距离,这很简单
function dst = distFcn(crc, xy)
% how good a fit circle for points
x = xy(:, 1) - crc(1);
y = xy(:, 2) - crc(2);
dst = abs(sqrt(x.^2 + y.^2) - crc(3));
把它和matlab的ransac放在一起:
function circles = find_circles(bw)
% parameters
sample_size = 4;
max_distance = 10;
min_num_points_in_circle = 50;
[y, x] = find(bw > max(bw(:))/2); % all edges in the image
circles = {};
counter = 0;
while numel(x) > 10 * sample_size && counter < 10
try
[circle, inlierIdx] = ransac([x, y], @fit_circle, @distFcn, ...
sample_size, max_distance);
catch
break
end
% refit using only inliers
circle = fit_circle([x(inlierIdx) y(inlierIdx)]);
dst = distFcn(circle, [x y]);
founfit = dst < max_distance;
if sum(founfit) > min_num_points_in_circle
% this model fits enough points
circles{end+1} = circle;
x(founfit) = [];
y(founfit) = [];
else
counter = counter + 1;
end
end
circles = vertcat(circles{:});
此函数对您的数据的输出是(使用viscircles 绘制圆圈):
【讨论】: