如何使用MATLAB检测二值图像中的两条平行线

信息处理 图像处理 matlab 霍夫变换
2022-02-11 20:59:42

我正在使用hough transform. 我试图检测水平和垂直边缘,然后找到平行线。

我的代码:

img = rgb2gray(img);

img = medfilt2(img);

v = edge(img,'sobel','vertical');
v = bwlabel(v);
stats = regionprops(v, 'Area','BoundingBox','Image');
ids = find([stats.Area] > 30 & [stats.Area] < 2000);
v = ismember(v,ids);

输出:

垂直边缘检测并应用区域过滤后的图像

现在我想检测平行线。我在Mathworks上查阅了不同的示例,但不幸的是,我没有得到太多。有人请指导我如何在二进制图像中检测两条几乎直线和平行的线?

2个回答

您提到了霍夫变换,但您的代码没有使用它。但是,它可以帮助您找出线条的方向。

霍夫变换的最大值对应于可能的线。这些最大值由它们的坐标 $(\rho, \theta)$ 定义,其中 $\rho$ 是从原点到直线上最近点的距离,$\theta$ 是 $x$ 之间的角度轴和连接原点与最近点的线(参见维基百科)。

因此,两条平行线有一个接近的 $\theta$(或 $\theta + \pi$)。

这是约翰 BG jgb2012@sky.com

1.- 避免加倍段

如果您继续使用您在方法中使用的代码:

img=imread('001.jpg');
imshow(img);
img = rgb2gray(img);
img = medfilt2(img);
v = edge(img,'sobel','vertical');
v = bwlabel(v);
stats = regionprops(v, 'Area','BoundingBox','Image');
ids = find([stats.Area] > 30 & [stats.Area] < 2000);
vsq = ismember(v,ids);
figure;imshow(vsq)

在此处输入图像描述

您正在添加不应该包含的片段,添加的附加线条非常接近原始图像的线条,但不应该存在。在搜索平行线时,看起来这些非常接近的线对中的每一个都算作真正的平行线对,而实际上只有一条线

因此,请允许我提出以下建议:

2.- 获取图像,仅取 1 个颜色层,并进行二值化:

clear all;clc;close all
input_image_filename='001.jpg'
A=imread(input_image_filename);
figure(1);imshow(A)

因为所有 3 个 RGB 层都有完全相同的层,所以 1 层就足够了

A1=A(:,:,1)

th1=100
A1(A1>th1)=255;A1(A1<=th1)=0;
figure(2);imshow(A1)

3.- 使用命令 bwmorph 收缩平滑一点

A2=A1;
A2(A2>0)=1;  % same as A2=imbinarize(A1);
A3=bwmorph(A2,'skel',Inf);
figure(3);imshow(A3);
% 003

A4=bwmorph(A3,'tophat',Inf); % not useful
figure(4);imshow(A4);
% 004

4.-编号计数段

A5=bwlabel(A4,8);
figure(5);imshow(A5)
% 004-2

在此处输入图像描述

现在,最后一个图显然与前一个图相同,但实际上现在每个段都有不同的值,尽管所有段也看起来是白色的:最右边的直线段的所有像素都有值 31,所有“白色”像素该段实际上具有相同的值 31。

整个图中没有其他段具有任何值为 31 的像素。

紧靠上述标记为 31 的片段左侧的片段具有值 30,同样,同一片段的所有像素都具有相同的值 30,同时整个图中没有其他片段具有任何像素值,既不是 31 也不是 30。

现在我们可以计算图像中有多少段,避免第 1 点中突出显示的危险加倍

range_A5=unique(A5)';
amount_segments=max(range_A5)



amount_segments =
    47

有47段

5.- 短枝呢?

目标是相互比较片段并告诉它们的平行程度,成对比较。

为了保持答案简洁,让我们简化为线性段:

没有 T 形接头 没有星形或任何其他线段拓扑,如果不将笔从纸上抬起,就无法用单笔迹线绘制。

5.1.- 捕获所有段的尖端点

A5(1,:)=0;A5(end,:)=0;A5(:,1)=0;A5(:,end)=0; % make sure picture frame, all edge pixels, are black
A4(1,:)=0;A4(end,:)=0;A4(:,1)=0;A4(:,end)=0; % make sure picture frame, all edge pixels, are black

% figure(6);imshow(A5);   % check that there are not white pixels right on edge of picture

[sz1_A5,sz2_A5]=size(A5)                                        % sz2 horizontal sz1 vertical         
[wx,wy,v]=find(A5);P=[wx wy];                               % P: all white points

hold all                                                                    % check P contains all white and only white points
for k=1:1:length(P)
    plot(P(k,2),P(k,1),'r*')
end
% 004-3

PA:单元格 PA 的每个元素包含 1 段的所有像素

接下来,S 将包含每个段的所有尖端点的坐标 {[x11 y11 x12 y12] [x21 y21 x22 y22 x23 y23] ..}

S={};                                                                   % coordinates of tip points of each segment {[x11 y11 x12 y12] [x21 y21 x22 y22 x23 y23]  .. }
for k=1:1:length(PA)
   L0=PA{k} 
%    hold all                                                           % check all points in L0 define same segment
%    for k10=1:1:length(L0)
%         plot(L0(k10,2),L0(k10,1),'r*')
%    end

   T1=[]
   for k2=1:1:size(L0,1)
        p1=L0(k2,:)                                                  % read white point coordinates
        p1y=p1(1);p1x=p1(2)

            P2=[p1y-1 p1x-1;                                      % coordinates of 8 pixels around p1
               p1y-1 p1x; 
               p1y-1 p1x+1;
               p1y p1x+1; 
               p1y+1 p1x+1;
               p1y+1 p1x; 
               p1y+1 p1x-1; 
               p1y p1x-1];

            s2=0
            for k3=1:1:8
                s2=s2+A4(P2(k3,1),P2(k3,2))
            end

            if s2==1                                                   % there's only 1 px adjacent to p1
                T1=[T1 p1x p1y]
            end
   end

    p1_check=T1([1:2:end])                                     % mark all tips found for same segment
    p2_check=T1([2:2:end])   
    hold all;
    for k4=1:1:numel(p1_check)
        plot(p1_check(k4),p2_check(k4),'b*')
    end

%    end

       S{k}=T1
end
% 005

5.2.- 选择相距最远的 2 个提示作为提示点:

即使是明显或多或少的直线段也可能显示多于 1 对尖端点

% 005-2

在此处输入图像描述

因此,让我们首先通过选择仅一对点作为分段提示来解决此问题,从上述点中选择,选择那些最远的设备。

T2=[0 0 0 0]
for k=1:1:size(S,2)
    L1=S{k}

    ptx=L1([1:2:end])                                               % mark all tips found for same segment
    pty=L1([2:2:end])  

    if numel(ptx)>2 more_than_2_tips=1; end
    if numel(ptx)==2 more_than_2_tips=0; end
    if numel(ptx)<2 more_than_2_tips=NaN; end          % error

    switch more_than_2_tips
        case 1
            D=((ptx-ptx').^2+(pty-pty').^2).^.5             % distance matrix
            maxD=max(max(D)) 
            [nrow,ncol,v]=find(D==maxD)

            ptipx1=ptx(nrow(1))
            ptipx2=ptx(nrow(2))    
            ptipy1=pty(ncol(1))
            ptipy2=pty(ncol(2))  

            hold all;
            plot([ptipx1 ptipx2],[ptipy2 ptipy1],'ro','MarkerSize',10,'LineWidth',2)
            plot([ptipx1 ptipx2],[ptipy2 ptipy1],'r','MarkerSize',10,'LineWidth',2)
            D(nrow,ncol)
            T2=[T2;ptipx1 ptipy2 ptipx2 ptipy1]

        case 0
            D=((ptx(1)-ptx(2))^2+(pty(1)-pty(2))^2)^.5
            hold all;
            plot([ptx(1) ptx(2)],[pty(1) pty(2)],'ro','MarkerSize',10,'LineWidth',2)
            plot([ptx(1) ptx(2)],[pty(1) pty(2)],'r','MarkerSize',10,'LineWidth',2)
            T2=[T2;ptx(1) pty(1) ptx(2) pty(2)]

        otherwise
    end
end
T2(1,:)=[] 

T2 包含所有尖端点,每段 2 个

% check
ptnx=T2(:,[1 3])                                               
ptny=T2(:,[2 4])  
plot(ptnx,ptny,'yo','MarkerSize',10,'LineWidth',2)    

评论:这个检查是“更多的 MATLAB”之类的。

确实不需要上面使用的某些 for 循环。有些习惯很难改掉。

在此处输入图像描述

T2:分段提示

M2:分段坡度

6.- 如何定义、测量和比较 2 个给定 2D 段之间的平行度。

T2          
M2=(T2(:,4)-T2(:,2))./(T2(:,3)-T2(:,1)) 

7.-考虑直线度和平行度等效:

平行度的测量与所比较的段的直线度密切相关

在 2D 中,坡度或直线度 Inf 或 -Inf 相同

M2(find(M2==-Inf))=Inf  

那么问题是在所有段之间比较 M2

PAR=M2-M2'

段数上的平行度

figure(6);stem(M2);grid on
% 007-1
figure(7);histogram(M2)
% 007-2

然而,与其将 M2 视为并行度的直接衡量标准,或许在问题的上下文中,已经提到它与车牌识别有关,但样本图像看起来更像是一个生物实验室。 petry,也许作为平行度的角度会更合适:

PAR_angle=atand(M2)-atand(M2)'

角度以度为单位,而不是弧度。

figure(7);stem(atand(M2));grid on
% 007-3
figure(8);histogram(atand(M2),35)
% 007-4

在此处输入图像描述

观察:没有绝对(角度)>100º的线段

评论:线段对之间平行度比较的矩阵PAR是对称的,只需要对角线的一侧。

如果有兴趣,我可以展示如何添加标准或自定义颜色图,映射到 M2 斜率或 atand(M2) 角度,以在视觉上将具有相似平行度的段配对

cmap=colormap(jet)      
..

到目前为止,问题已得到解答,以下几点是未解决的评论和替代解决方案,如果读者认为可能有任何帮助,请完成:

%%%%%%%%%%%%%%%%%%%%%%%%%%

8.- 让我们避免混淆,例如 Taylor Hobson 的并行器的行业定义

https://www.taylor-hobson.com/-/media/ametektaylorhobson/files/learning%20zone/training%20material/straightness%20and%20%20parallelism.pdf?la=en

008

在 Taylor Hobson,他们使用以下标准:

D1-D2 越接近零,D1 D2 是 2 个线段的尖端之间的距离但是 TH 称它为线段越平行,但这比 2D 线段更能衡量对称性。

考虑到大量的段,如果尖端被交叉并且 D1 D2 错误地显示比它们应该的大怎么办?

**9.- 平行线的欧几里得定义:

平面上不相交的一对直线。**

评论:允许为直线段保留单词“line”,相邻像素与直接相邻像素保持相同的斜率。

我找到了与欧几里得并行性等效的定义:

**如果连接 2 个“等效”或“配对”像素的所有光束(不同段中的每个像素)满足以下条件,则该问题初始图中的任何 2 个段都可以被认为是高度平行的:

a.- 所有梁具有相同的坡度

b.- 所有梁的长度相同**

在此处输入图像描述

这样有 2 个整形片段,其中一个是另一个的翻译副本,例如 (SS CC LL),相对旋转为零,这与相同的姿态和相同数量的像素相同:这样的原始副本和翻译副本将最大程度地平行.

但是根据欧几里得定义或平行线,两个同心圆是平行的,但无论考虑的点如何,它们都不是直的。

要测量任意一对线段之间的梁斜率和长度,必须首先解决 2 个障碍:

避免钉子和 T 形接头产生过多和不必要的光束

9.1.- 移除钉子

9.2.- 段的长度不同

为了在提供此答案时减少工作量,我将这一点 9.2 限制为以下起始图,此过程可以轻松升级到问题所附初始图中的其余部分:

10.- 测量横梁角度(坡度)和长度

由于第 7 点已经提供了对并行度的准确测量。

我将关闭此答案,将第 8、9、10 点留给任何读者的兴趣,如果联系到这些点,将相应地开发。

%%%%%%%%%

发送紧凑的脚本,只需通过电子邮件告诉我在哪里。

如果您发现此答案有用,请考虑将我的答案标记为已接受的答案吗?

对于任何其他读者,如果您发现此答案有用,请考虑单击箭头投票链接

提前感谢您的时间和关注

约翰·BG

jgb2012@sky.com