图像中的线条检测

9

我是图像处理的新手,我尝试使用以下代码来检测垂直线 -

image=imread('benzene.jpg');  
BW = im2bw(image);
w1=[-1 2 -1 ; -1 2 -1 ; -1 2 -1];
g=(imfilter(double(BW),w1));
g=abs(g);
T=max(g(:));
g=g>=T;
imshow(g);

这是我的图像 -

输入图片描述

然后我执行了一些操作,得到了下面的结果 - 输入图片描述 所以我的问题是为什么会得到这样的输出?如果将垂直双键视为两条不同的垂直线,则有10条垂直线。另外,如果我想要水平、垂直、45度和-45度的所有线条,我该如何使用这4个掩码来获得一个单一的输出?

你能提供这张图片的在线链接吗?我在网上找不到它。 - roni
我已经编辑了我的问题。我理解了我的错误,但现在我又得到了意外的输出。 - Noober
@roni 你可以直接从这里保存这张图片,因为我现在没有图片链接。 - Noober
2
问题当然出在你的过滤器上。阅读一些基本的过滤器,比如:https://en.wikipedia.org/wiki/Prewitt_operator https://en.wikipedia.org/wiki/Sobel_operator - Ander Biguri
3个回答

14

我的一个简单建议是检测梯度并确定边缘点的方向。请记住,方向是指与边缘线垂直的方向。因此,如果您想找到垂直线条,则与垂直线条垂直的方向是水平的,在笛卡尔平面上,该方向要么是180度,要么是-180度。因此,对于每个检测到的边缘点的方向,如果方向是-180度或180度,则将此位置的输出设置为true,否则设置为false。使用图像处理工具箱中的imgradient来检测梯度方向。假设您已经使用了imreadim2bw,它们都是该工具箱的一部分:

im = imread('http://i.stack.imgur.com/bdNOt.png');
tol = 5;
[~,ang] = imgradient(im);
out = (ang >= 180 - tol | ang <= -180 + tol);
imshow(out);

代码使用一个名为tol的变量来定义一个容差,以便检测噪音或看起来垂直但在计算角度时可能不是的边缘。基本上,我们正在寻找任何角度在180度或-180度之内的点。

这是我们得到的结果:

enter image description here

作为后处理的手段,您可以使用bwareaopen过滤掉像素区域面积低于某个值的像素。利用垂直线具有比其他像素更大的面积这一事实,您可以采取以下措施:

out_filter = bwareaopen(out, 50);
我们得到: enter image description here 现在如果你想检测水平线,你应该寻找梯度方向为-90或90度的线条。这是有道理的,因为对于那些水平的线条来说,垂直于水平线的方向确实是垂直方向,而这要么是-90度或90度。如果你想要倾斜的线,如果你想要一个左倾斜的线,看看45度或-135度的角度,右倾斜的线,看看-45度或135度。我会让你自己想出这些角度为什么代表了这些类型的线。
你提供的图像中没有水平线,所以我只会寻找倾斜线:

左倾斜线

注意:由于量化误差,我不得不增加容差。
im = imread('http://i.stack.imgur.com/bdNOt.png');
tol = 20;
[~,ang] = imgradient(im);
out = (ang >= 45 - tol & ang <= 45 + tol) | (ang >= -135 - tol & ang <= -135 + tol);
out_filter = bwareaopen(out, 50);
imshow(out_filter);

enter image description here

向右倾斜的线:

这里同样需要增加公差:

im = imread('http://i.stack.imgur.com/bdNOt.png');
tol = 20;
[~,ang] = imgradient(im);
out = (ang >= 135 - tol & ang <= 135 + tol) | (ang >= -45 - tol & ang <= -45 + tol);
out_filter = bwareaopen(out, 50);
imshow(out_filter);

在这里输入图像描述


2
接下来跟上 out=bwareaopen(out,50); (可能需要稍微调整阈值)以删除与字母相关的线条。 - Jonas
1
@Jonas - 你读懂了我的想法...我刚刚就已经做好了。还有一件有趣的事情是,你和我选择了完全相同的阈值。 - rayryeng
1
太好了!自己想不出来,你的回答真是太棒了! - roni
1
水平方向将是0或90。倾斜应为+/- 45和+/- 135。我会更新我的帖子,向您展示具有相同图像的更多行。 - rayryeng
1
@Noober - 完成了。请查看。 - rayryeng
显示剩余4条评论

7
另一种方法是利用描绘键的所有线具有相同的纵横比和面积的事实。在过滤图像后仅保留键,我们可以查看其方向或组成它们的索引列表以检测它们是否为垂直或其他方向。所有这些都可以使用regionprops完成。
image=rgb2gray(imread('benzene.png'));  
d=abs(255-image); % inverse the image
d=im2bw(d);
stat=regionprops(d,'Area', 'Orientation','PixelIdxList'); 
areas=[stat.Area];
hist(areas)

enter image description here

检查直方图可以确定切割线的位置,切割线的面积比字母小,而且应该大致相等。因此,我将面积在1000像素以下的部分进行切割:

idx=find(areas<1000);
angs=round([stat(idx).Orientation]);

现在你可以使用angsidx来获取任何类型的线。例如,让我们只绘制30度线:
d2=zeros(size(d));
d2(vertcat(stat(idx(angs==30)).PixelIdxList))=1;
imagesc(d2)

enter image description here

请注意,当我开始回答这个问题时,我拍摄的图像是benzene.png文件。现在我意识到你提供的图像与原始图像不同,描绘键的线条不是分开的,而是有“环”。如果您希望,我稍后也可以解决这个问题。
编辑:
为了找到新图像的相关行,在那里你有环,线条唯一的区别就是它们是直线而不是曲线。因此,我采用了心爱的Hough变换来捕捉它们。
image=imread('http://i.stack.imgur.com/bdNOt.png');
d=abs(1-image); % inverse the image
BW=im2bw(d);
BW = bwmorph(BW,'skel',1);
[H, T, R] = hough(BW,'Theta',-90:10:80);
P = houghpeaks(H, 100,'NHoodSize',[3 3],'threshold',1);
lines = houghlines(BW, T, R, P, 'FillGap',5, 'MinLength', 35);

让我们获取检测到的线的角度:

angs=round([lines.theta]);

如果你看到这里的angs将会生成0度、-60度或60度的值。

如果你只想绘制0度的值:

p1=vertcat(lines(angs==0).point1);
p2=vertcat(lines(angs==0).point2);

imshow(BW, 'InitialMag',200, 'Border','tight'), hold on

for k = 1:size(p1,1)
   line([p1(k,1) p2(k,1)],[p1(k,2) p2(k,2)], 'LineWidth',4,...
   'Color',[1 0 0]); hold on
end
hold off

enter image description here


非常感谢你。但是你如何决定仅截取小于1000的部分? - Noober
还有,我该如何使用 anglesidx?你能给一些例子吗? - Noober
非常感谢。实际上,是的,我确实有一些环要处理。 - Noober
1
我添加了回答环的版本。 - bla
非常感谢。我接受的答案已经足够好了,但我希望能找到一些其他方法来获得相同的结果。再次感谢,这真的很有帮助。 - Noober

2
我仍在处理中。但至今我已经得到了这个结果。我没有使用你的过滤器,而是使用了另一个。
我使用了你提供的第一张图片。这些过滤器在此处描述:image_filters输入图像描述
image=imread('benzene.png');  
BW = im2bw(image);
w1=(1/3)*[1 0 -1;1 0 -1;1 0 -1];
g=(imfilter(double(BW),w1));
g(g<1)=0;
imshow(g);

我得到的输出结果是这样的:如您所见,结果尚不完整。我建议您尝试两件事情:使用形态学腐蚀运算符来去除小元素。您也可以使用连通组件来实现。同时,请尝试执行我的建议。如果我得到答案,我会更新它。

2
与形态学操作不同,检测这些直线(在滤波之后)有一种更简单的方法。只需逐个取出它们(例如通过imlabel),并计算每个高度上的宽度。如果宽度是恒定的,则为直线,否则为您想要避免的噪声。 - Ander Biguri
我更倾向于使用霍夫变换,但是我无法应用它。 - roni

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接