【问题标题】:Subpixel edge detection for almost vertical edges几乎垂直边缘的亚像素边缘检测
【发布时间】:2015-04-24 19:24:30
【问题描述】:

我想在显示的图像中检测边缘(具有亚像素精度):

分辨率约为 600 X 1000。

我看到了 Mark Ransom here 的评论,其中提到了垂直边缘的边缘检测算法。我还没有遇到过。在我的情况下它会有用吗(因为边缘不是严格意义上的直线)?不过,它始终是垂直边缘。我希望它至少精确到 1/100 像素。我也想访问这些亚像素坐标值。

我尝试过 Agustin Trujillo-Pino 的 "Accurate subpixel edge location"。但这并没有给我一个持续的优势。

还有其他可用的算法吗?我将为此使用 MATLAB。

我附上了另一张算法必须处理的类似图像:

我们将不胜感激。

谢谢。

编辑:

我想知道我是否可以这样做: 在 MATLAB 中应用 Canny / Sobel 并获取此图像的边缘(注意它不会是一条连续的线)。然后,以某种方式插入这个 Sobel 边缘并获得子像素中的坐标。有可能吗?

【问题讨论】:

  • 使用活动轮廓怎么样? (只是一个随机的想法)
  • 嗯,我试过了。首先,这不是轮廓。它不是封闭的,它只是一条线。其次,它非常慢。
  • 是的。你可能是对的。

标签: matlab image-processing edge-detection


【解决方案1】:

一种简单的方法是垂直投影您的图像,并使用适当的功能拟合投影的轮廓。

这是一个尝试,使用 atan 形状:

% Load image
Img = double(imread('bQsu5.png'));

% Project
x = 1:size(Img,2);
y = mean(Img,1);

% Fit
f = fit(x', y', 'a+b*atan((x0-x)/w)', 'Startpoint', [150 50 10 150])

% Display
figure
hold on
plot(x, y);
plot(f);
legend('Projected profile', 'atan fit');

结果:

我为您的第一张图像得到 x_0 = 149.6 像素。

但是,我怀疑您能否使用这些图像实现 1/100 像素的亚像素精度,原因如下:

  1. 正如您在配置文件中看到的,您的白色已饱和(灰度为 255)。当您切割真正的 atan 轮廓时,拟合是有偏差的。如果你可以控制实验,我建议你再做一次,例如用更短的曝光时间。

  2. transition 上没有那么多点,所以没有太多关于transition 在哪里的信息。通常,您的分辨率将是atan(或您喜欢的任何形状)宽度的平方根。在您的情况下,这最多将子像素分辨率限制为像素的 1/5。

最后,你的边缘不是严格垂直的,它们有轻微的标题。如果您选择使用这种投影方法,为了提高准确性,您应该在投影之前寻找一种方法来纠正这种倾斜。不过,这不会将您的准确性提高几个数量级。

最好的,

【讨论】:

  • 感谢您的详细解释。但是没有更简单的方法吗?我的意思是我的图像具有高对比度,所以只追踪边缘应该很简单,对吧? Sobel / Canny (MATLAB) 给了我很好的结果,只是它不是在亚像素级别。请参阅我编辑的问题的最后一部分。
  • 我真的不明白为什么边缘检测比拟合更简单。无论如何,是的,确实,您可以执行边缘检测,但正如您所注意到的,它并没有为您提供亚像素分辨率。不幸的是,它不会因为边缘检测减少了信息。同样,如果您对精度感兴趣,则应首先对图像进行不饱和处理,因为这会改变边缘的实际位置。
  • 嗯......不饱和部分实际上是不可能的。暗区是光(这个光是白色饱和区)前面的物体(圆柱体的边缘之一)。我只需要知道这个对象的边缘从哪个像素坐标开始。不需要其他信息。
  • 不饱和是指利用采集本身的参数,例如曝光时间或光的强度。这意味着您获取一组新数据。以目前的数据,饱和度可以使边缘的实际位置偏移几个像素,这对于你想要的分辨率来说是非常糟糕的。
  • 哦,我明白你的意思了。你是对的,如果强度太高,边缘像素会移动(根据我之前的观察)。我已经试过了。我试过改变背光的强度和镜头的曝光时间。这是上图中的最佳值。顺便说一句,它在像素级精度(即 +/- 1 像素)下工作得非常好,我在各个方面都得到了所需的边缘。此外,如果我减少照明,它更容易产生噪音。
【解决方案2】:

您的图片有问题。在像素级别,似乎有四个交错的子图像(奇数和偶数行和列)。看看这个zoomed area close to the edge

为了避免这种伪影,我只取了图像的偶数行和列,并计算了亚像素边缘。最后,我使用代码在this page 中的函数 clsq 寻找最合适的直线:

%load image
url='http://i.stack.imgur.com/bQsu5.png';
image = imread(url);
imageEvenEven = image(1:2:end,1:2:end);
imshow(imageEvenEven, 'InitialMagnification', 'fit');

% subpixel detection
threshold = 25;
edges = subpixelEdges(imageEvenEven, threshold); 
visEdges(edges);

% compute fit line
A = [ones(size(edges.x)) edges.x edges.y];
[c n] = clsq(A,2);
y = [1,200];
x = -(n(2)*y+c) / n(1);
hold on;
plot(x,y,'g');   

执行此代码时,您可以看到最接近所有边缘点的绿线。该线由等式 c + n(1)*x + n(2)*y = 0 给出

考虑到这个图像在只取偶数行和列时已经缩放了1/2,所以必须缩放正确的坐标。

此外,您可以尝试使用其他树子图像(imageEvenOdd、imageOddEven 和 imageOddOdd),并结合四个直线以获得最佳解决方案。

【讨论】: