8

BW = poly2mask(x, y, m, n)ベクトルxおよびyで表されるROIポリゴンからバイナリ関心領域(ROI)マスクBWを計算します。BWのサイズはm-x-nです。

poly2maskポリゴン(X、Y)の内側にあるBWのピクセルを1に設定し、ポリゴンの外側のピクセルを0に設定します。

問題:凸四角形の そのようなバイナリマスクBWを考えると、四隅を決定するための最も効率的な方法は何でしょうか?

例えば、

例

これまでの最善の解決策:境界線を見つけるために 使用edgeし、ハフ変換を使用してエッジ画像内の4本の線を見つけ、次にそれらの4本の線の交点を見つけるか、エッジ画像上でコーナー検出器を使用します。複雑なようで、もっと簡単な解決策があると感じずにはいられません。

ところで、convhull常に4ポイントを返すわけではありません(おそらく誰かがqhullそれを防ぐためのオプションを提案することができます):エッジに沿っていくつかのポイントも返します。

編集: アムロの答えは非常にエレガントで効率的なようです。ただし、ピークは一意ではないため、実際の各コーナーに複数の「コーナー」が存在する可能性があります。θに基づいてそれらをクラスター化し、実際の角の周りの「コーナー」を平均化することもできますが、主な問題はの使用ですorder(1:10)

すべてのコーナーを説明するのに十分です10か、それとも実際のコーナーの「コーナー」を除外しますか?

4

5 に答える 5

12

これは、 @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を試してみましたが、すべてをカバーしていませんでした)。それらをクラスタリングして重複を削除するというアイデアは、検討する価値があると思います。

私が見る限り、最初のアプローチの問題は、境界をトレースする速度が異なり、依存するため、考慮rhoせずにプロットすると、異なる形状(同じピークではない)が得られることでした。曲率。その効果を正規化する方法を理解できれば、導関数を使用してより正確な結果を得ることができます。θ

于 2009-11-11T06:20:37.430 に答える
8

Image Processing Toolboxをお持ちの場合はcornermetric、ハリスコーナー検出器またはShiandTomasiの最小固有値法を実装できるという関数があります。この関数は、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');

次に、を使用してコーナーメトリックを計算し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');

ここに画像の説明を入力してください

そして、これがひし形の領域でのテストです:

ここに画像の説明を入力してください

于 2009-11-12T21:52:48.960 に答える
4

これを2D問題から1D問題に減らすので、境界を操作してこの問題を解決するのが好きです。

画像処理ツールキットから使用bwtraceboundary()して、境界上の点のリストを抽出します。次に、境界を一連の接線ベクトルに変換します(これを行うにはいくつかの方法がありますが、1つの方法は、 ithポイントから境界に沿ってthポイントを変更するi+deltaことです)。ベクトルのリストができたら、ドットを取ります。隣接するベクトルの積。ドット積が最も小さい4つのポイントがあなたのコーナーです!

アルゴリズムが頂点の数が非常に多いポリゴンで機能するようにしたい場合は、ドット積の中央値よりも一定数標準偏差下にあるドット積を検索するだけです。

于 2009-11-11T04:09:50.857 に答える
2

コーナーを取得するために、ハリスコーナー検出器(より正式な説明があります)を使用することしました。これは次のように実装できます。

%% 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

コーナー

于 2009-11-12T21:13:13.513 に答える
1

これは、RubyとHornetsEyeを使用した例です。基本的に、プログラムは、量子化されたSobel勾配方向のヒストグラムを作成して、支配的な方向を見つけます。4つの主要な方向が見つかった場合、線がフィットされ、隣接する線の交点が投影された長方形の角であると見なされます。

#!/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

コーナーの推定位置を含む画像

于 2010-07-03T17:30:18.540 に答える