找到由区域蒙版表示的多边形的角

Jac*_*cob 8 matlab geometry polygon computer-vision corner-detection

BW = poly2mask(x, y, m, n)从ROI多边形计算二进制感兴趣区域(ROI)掩模BW,由矢量x和y表示.BW的大小是m-by-n.

poly2mask 将BW中的多边形(X,Y)内的像素设置为1,并将多边形外部的像素设置为0.

问题: 鉴于BW凸四边形的这种二元掩模,确定四个角的最有效方法是什么?

例如,

例

最好的解决方案到目前为止: 使用edge找到的边界线,霍夫变换,找到4行的边缘图像中,然后找到这4条线的交点,或使用一个角点检测边缘图像上.看起来很复杂,我不禁觉得那里有一个更简单的解决方案.

顺便说一句,convhull并不总是返回4分(也许有人可以提出qhull防止这种情况的选项):它也沿着边缘返回几个点.

编辑: Amro的答案看起来非常优雅和高效.但是每个真实角落可能有多个"角落",因为峰值不是唯一的.我可以根据θ对它们进行聚类,并对真实角落周围的"角落"进行平均,但主要问题是使用order(1:10).

是否10足以说明所有的角落还是将排除"角"在真正的角落?

Amr*_*mro 11

这有点像@AndyL建议的那样.但是我在极坐标中使用边界签名而不是切线.

请注意,我首先提取边缘,获取边界,然后将其转换为签名.最后,我们发现边界上距离质心最远的点,这些点构成了找到的角点.(或者我们也可以检测角落签名中的峰值).

以下是一个完整的实现:

I = imread('oxyjj.png');
if ndims(I)==3
    I = rgb2gray(I);
end
subplot(221), imshow(I), title('org')

%%# Process Image
%# edge detection
BW = edge(I, 'sobel');
subplot(222), imshow(BW), title('edge')

%# dilation-erosion
se = strel('disk', 2);
BW = imdilate(BW,se);
BW = imerode(BW,se);
subplot(223), imshow(BW), title('dilation-erosion')

%# fill holes
BW = imfill(BW, 'holes');
subplot(224), imshow(BW), title('fill')

%# get boundary
B = bwboundaries(BW, 8, 'noholes');
B = B{1};

%%# boudary signature
%# convert boundary from cartesian to ploar coordinates
objB = bsxfun(@minus, B, mean(B));
[theta, rho] = cart2pol(objB(:,2), objB(:,1));

%# find corners
%#corners = find( diff(diff(rho)>0) < 0 );     %# find peaks
[~,order] = sort(rho, 'descend');
corners = order(1:10);

%# plot boundary signature + corners
figure, plot(theta, rho, '.'), hold on
plot(theta(corners), rho(corners), 'ro'), hold off
xlim([-pi pi]), title('Boundary Signature'), xlabel('\theta'), ylabel('\rho')

%# plot image + corners
figure, imshow(BW), hold on
plot(B(corners,2), B(corners,1), 's', 'MarkerSize',10, 'MarkerFaceColor','r')
hold off, title('Corners')
Run Code Online (Sandbox Code Playgroud)

screenshot1 screenshot2


编辑: 在回应雅各布的评论时,我应该解释一下,我首先尝试使用一阶/二阶导数找到签名中的峰值,但最终得到了最远的N点.10只是一个临时值,并且很难概括(我尝试将4个与角数相同,但它并没有涵盖所有这些).我认为将它们聚类以删除重复的想法值得研究.

据我所知,第一种方法的问题是,如果你在rho不考虑的情况下进行绘图?,你会得到一个不同的形状(不是相同的峰值),因为我们追踪边界的速度是不同的,取决于曲率.如果我们能够弄清楚如何规范化这种效应,我们可以使用衍生物获得更准确的结果.


gno*_*ice 8

如果您有图像处理工具箱,则会调用一个函数cornermetric来实现Harris角点检测器或Shi和Tomasi的最小特征值方法.自Image Processing Toolbox 6.2版(MATLAB版本R2008b)以来,该功能已经存在.

使用此功能,我提出了与其他答案略有不同的方法.下面的解决方案基于以下思想:以每个"真实"角点为中心的圆形区域将与多边形重叠的量小于以实际在边缘上的错误角点为中心的圆形区域.该解决方案还可以处理在同一角落检测到多个点的情况......

第一步是加载数据:

rawImage = imread('oxyjj.png');
rawImage = rgb2gray(rawImage(7:473, 9:688, :));  % Remove the gray border
subplot(2, 2, 1);
imshow(rawImage);
title('Raw image');
Run Code Online (Sandbox Code Playgroud)

接下来,使用计算角度量cornermetric.请注意,我正在通过原始多边形屏蔽角度量,以便我们查找多边形内部的角点(即尝试查找多边形的角点像素).imregionalmax然后用于查找局部最大值.由于您可以使用相同的角度量具有大于1个像素的聚类,然后我将噪声添加到最大值并重新计算,以便我在每个最大区域中只获得1个像素.然后使用bwlabel以下标记每个最大区域:

cornerImage = cornermetric(rawImage).*(rawImage > 0);
maxImage = imregionalmax(cornerImage);
noise = rand(nnz(maxImage), 1);
cornerImage(maxImage) = cornerImage(maxImage)+noise;
maxImage = imregionalmax(cornerImage);
labeledImage = bwlabel(maxImage);
Run Code Online (Sandbox Code Playgroud)

然后使用imdilate盘形结构元素(使用strel)创建标记区域(使用):

diskSize = 5;
dilatedImage = imdilate(labeledImage, strel('disk', diskSize));
subplot(2, 2, 2);
imshow(dilatedImage);
title('Dilated corner points');
Run Code Online (Sandbox Code Playgroud)

现在标记的角落区域已经扩张,它们将与原始多边形部分重叠.多边形边缘上的区域将具有约50%的重叠,而位于角落上的区域将具有约25%的重叠.该函数regionprops可用于查找每个标记区域的重叠区域,因此可以将具有最小重叠量的4个区域视为真正的角点:

maskImage = dilatedImage.*(rawImage > 0);       % Overlap with the polygon
stats = regionprops(maskImage, 'Area');         % Compute the areas
[sortedValues, index] = sort([stats.Area]);     % Sort in ascending order
cornerLabels = index(1:4);                      % The 4 smallest region labels
maskImage = ismember(maskImage, cornerLabels);  % Mask of the 4 smallest regions
subplot(2, 2, 3);
imshow(maskImage);
title('Regions of minimal overlap');
Run Code Online (Sandbox Code Playgroud)

现在我们可以使用find和获取角的像素坐标ismember:

[r, c] = find(ismember(labeledImage, cornerLabels));
subplot(2, 2, 4);
imshow(rawImage);
hold on;
plot(c, r, 'r+', 'MarkerSize', 16, 'LineWidth', 2);
title('Corner points');
Run Code Online (Sandbox Code Playgroud)

在此输入图像描述

这是一个菱形区域的测试:

在此输入图像描述