查找区域遮罩代表的多边形的角

matlab geometry computer-vision polygon corner-detection

16248 观看

5回复

30251 作者的声誉

BW = poly2mask(x, y, m, n)从ROI多边形(由向量x和y表示)计算二进制感兴趣区域(ROI)掩码BW。BW的大小为n×n。

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

问题: 给定这样一个BW凸四边形的二进制掩码,确定四个角的最有效方法是什么?

例如,

例

到目前为止最好的解决方案: 使用edge来找到边界线,使用Hough变换在边缘图像中找到4条线,然后找到这4条线的交点,或者在边缘图像上使用拐角检测器。似乎很复杂,我不禁感到这里有一个更简单的解决方案。

顺便说一句,convhull并非总是返回4分(也许有人可以建议qhull防止这种情况出现):它也会沿边缘返回几分。

编辑: Amro的答案似乎很优雅和有效。但是,由于每个峰都不是唯一的,因此在每个实际角可能会有多个“角”。我可以基于θ对它们进行聚类,然后将“角”平均在真实拐角处,但主要问题是的使用order(1:10)

是否10足以说明所有角落,还是会排除真实角落的“角落”?

作者: Jacob 的来源 发布者: 2009 年 11 月 10 日

回应 (5)


4

9741 作者的声誉

我喜欢通过处理边界来解决此问题,因为它将边界从2D问题减少到1D问题。

使用bwtraceboundary()图像处理工具包提取边界上的点列表。然后将边界转换为一系列切向量(有多种方法可以执行此操作,一种方法是i沿th点沿边界对i+deltath点进行折算 。)一旦有了向量列表,就取点相邻向量的乘积。点产品最少的四个点就在您的角落!

如果要让算法在顶点数量不多的多边形上工作,则只需搜索点积,该点积在中值点积以下一定标准偏差。

作者: AndyL 发布者: 11.11.2009 04:09

11

117447 作者的声誉

决定

这有点类似于@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')

屏幕截图1 屏幕截图2


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

据我所知,第一种方法的问题在于,如果rho不考虑绘图θ,您会得到不同的形状(不是相同的峰),因为我们追踪边界的速度不同,取决于曲率。如果我们能弄清楚如何标准化该效果,则可以使用导数获得更准确的结果。

作者: Amro 发布者: 11.11.2009 06:20

2

30251 作者的声誉

我决定使用Harris拐角检测器(这是更正式的说明)来获取拐角。可以如下实现:

%% Constants
Window = 3;
Sigma = 2;
K = 0.05;
nCorners = 4;

%% Derivative masks
dx = [-1 0 1; -1 0 1; -1 0 1];
dy = dx';   %SO code color fix '

%% Find the image gradient
% Mask is the binary image of the quadrilateral
Ix = conv2(double(Mask),dx,'same');   
Iy = conv2(double(Mask),dy,'same');

%% Use a gaussian windowing function and compute the rest
Gaussian = fspecial('gaussian',Window,Sigma);
Ix2 = conv2(Ix.^2,  Gaussian, 'same');  
Iy2 = conv2(Iy.^2,  Gaussian, 'same');
Ixy = conv2(Ix.*Iy, Gaussian, 'same');    

%% Find the corners
CornerStrength = (Ix2.*Iy2 - Ixy.^2) - K*(Ix2 + Iy2).^2;
[val ind] = sort(CornerStrength(:),'descend');    
[Ci Cj] = ind2sub(size(CornerStrength),ind(1:nCorners));

%% Display
imshow(Mask,[]);
hold on;
plot(Cj,Ci,'r*');

在这里,由于高斯开窗功能使强度变化变得平滑,因此存在多个拐角的问题。下面是带有hot颜色图的角的缩放版本。

角

作者: Jacob 发布者: 12.11.2009 09:13

8

120206 作者的声誉

如果您拥有Image Processing Toolbox,则有一个名为的函数cornermetric可以实现Harris角点检测器或Shi和Tomasi的最小特征值方法。自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');

接下来,使用计算角指标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);

然后,使用imdilate圆盘形结构元素(使用创建strel)将标记的区域扩张(使用):

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

现在,已标记的角区域已膨胀,它们将与原始多边形部分重叠。多边形边缘上的区域将有大约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');

现在我们可以使用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');

在此处输入图片说明

这是菱形区域的测试:

在此处输入图片说明

作者: gnovice 发布者: 12.11.2009 09:52

1

1759 作者的声誉

这是使用Ruby和HornetsEye的示例。基本上,该程序会创建量化的Sobel梯度方向的直方图,以找到主要方向。如果找到四个主要方向,则拟合线,并假定相邻线之间的交点为投影矩形的角。

#!/usr/bin/env ruby
require 'hornetseye'
include Hornetseye
Q = 36
img = MultiArray.load_ubyte 'http://imgur.com/oxyjj.png'
dx, dy = 8, 6
box = [ dx ... 688, dy ... 473 ]
crop = img[ *box ]
crop.show
s0, s1 = crop.sobel( 0 ), crop.sobel( 1 )
mag = Math.sqrt s0 ** 2 + s1 ** 2
mag.normalise.show
arg = Math.atan2 s1, s0
msk = mag >= 500
arg_q = ( ( arg.mask( msk ) / Math::PI + 1 ) * Q / 2 ).to_int % Q
hist = arg_q.hist_weighted Q, mag.mask( msk )
segments = ( hist >= hist.max / 4 ).components
lines = arg_q.map segments
lines.unmask( msk ).normalise.show
if segments.max == 4
  pos = MultiArray.scomplex *crop.shape
  pos.real = MultiArray.int( *crop.shape ).indgen! % crop.shape[0]
  pos.imag = MultiArray.int( *crop.shape ).indgen! / crop.shape[0]
  weights = lines.hist( 5 ).major 1.0
  centre = lines.hist_weighted( 5, pos.mask( msk ) ) / weights
  vector = pos.mask( msk ) - lines.map( centre )
  orientation = lines.hist_weighted( 5, vector ** 2 ) ** 0.5
  corner = Sequence[ *( 0 ... 4 ).collect do |i|
    i1, i2 = i + 1, ( i + 1 ) % 4 + 1
    l1, a1, l2, a2 = centre[i1], orientation[i1], centre[i2], orientation[i2]
    ( l1 * a1.conj * a2 - l2 * a1 * a2.conj -
      l1.conj * a1 * a2 + l2.conj * a1 * a2 ) /
      ( a1.conj * a2 - a1 * a2.conj )
  end ] 
  result = MultiArray.ubytergb( *img.shape ).fill! 128
  result[ *box ] = crop
  corner.to_a.each do |c|
    result[ c.real.to_i + dx - 1 .. c.real.to_i + dx + 1,
            c.imag.to_i + dy - 1 .. c.imag.to_i + dy + 1 ] = RGB 255, 0, 0
  end
  result.show
end

带有估计拐角位置的图像

作者: wedesoft 发布者: 03.07.2010 05:30
32x32