web-dev-qa-db-ja.com

分散粒子を含む画像の空き領域に最大の円をあてはめる

私は、分散粒子を含む画像の任意の空き領域で可能な最大の円を検出し、適合するように画像に取り組んでいます:1

(粒子の位置を検出できます)。

1つの方法は、3点の組み合わせに触れる円を定義し、円が空かどうかを確認してから、すべての空の円の中から最大の円を見つけることです。ただし、膨大な数の組み合わせ、つまりC(n,3)になります。ここで、nは画像内のパーティクルの総数です。

誰かが私が探求できるヒントや代替方法を提供してくれれば幸いです。

50
T50740

数学は常に終わりになるので、私の友人に数学をさせてください!

ウィキペディア:

数学では、ボロノイ図は、平面の特定のサブセット内のポイントまでの距離に基づいて、平面を領域に分割することです。

例えば:

rng(1)
x=Rand(1,100)*5;
y=Rand(1,100)*5;


voronoi(x,y);

enter image description here

この図の良いところは、気づいた場合、それらの青い領域のすべてのエッジ/頂点が、それらの周囲のポイントまですべて等しい距離にあるということです。したがって、頂点の位置がわかっていて、最も近いポイントまでの距離を計算する場合、円の中心として最も距離の長い頂点を選択できます。

興味深いことに、ボロノイ領域のエッジは、ドロネー三角形分割によって生成された三角形の外心としても定義されます。

したがって、領域のDelaunay三角形分割とその外心を計算すると

dt=delaunayTriangulation([x;y].');
cc=circumcenter(dt); %voronoi edges

そして、外心と各三角形を定義する任意の点の間の距離を計算します。

for ii=1:size(cc,1)
    if cc(ii,1)>0 && cc(ii,1)<5 && cc(ii,2)>0 && cc(ii,2)<5
    point=dt.Points(dt.ConnectivityList(ii,1),:); %the first one, or any other (they are the same distance)
    distance(ii)=sqrt((cc(ii,1)-point(1)).^2+(cc(ii,2)-point(2)).^2);
    end
end

次に、中に点がないすべての可能な円の中心(cc)と半径(distance)があります。最大のものが必要です!

[r,ind]=max(distance); %Tada!

プロットしましょう

hold on

ang=0:0.01:2*pi; 
xp=r*cos(ang);
yp=r*sin(ang);

point=cc(ind,:);

voronoi(x,y)
triplot(dt,'color','r','linestyle',':')
plot(point(1)+xp,point(2)+yp,'k');
plot(point(1),point(2),'g.','markersize',20);

enter image description here

円の中心がボロノイ図の1つの頂点上にあることに注意してください。


[〜#〜] note [〜#〜]:これは[0-5]、[0-5]内の中心を見つけます。この制約を変更するために簡単に変更できます。また、(中心だけではなく)関心のある領域内で全体に適合する円を見つけることもできます。これには、最大値が得られる最後に少し追加する必要があります。

89
Ander Biguri

洗練されたグリッド検索に基づく別のソリューションを提案したいと思います。 Anderほど高度ではなく、rahnema1ほど短くはありませんが、shouldに従うことと理解することは非常に簡単です。また、非常に高速に実行されます。

アルゴリズムにはいくつかの段階が含まれます。

  1. 等間隔のグリッドを生成します。
  2. グリッド内のポイントから提供されたすべてのポイントまでの最小距離を見つけます。
  3. 距離が特定のパーセンタイル(95番目など)を下回るすべてのポイントを破棄します。
  4. 最大距離を含む領域を選択します(最初のグリッドが十分に細かい場合、これには正しい中心が含まれている必要があります)。
  5. 選択した領域の周囲に新しいメッシュグリッドを作成し、再び距離を見つけます(距離は関連するものを含むすべての点までの距離が計算されるため、この部分は明らかに準最適です)。
  6. 値の上位5%の分散に注意しながら、領域内で絞り込みを繰り返します->事前に設定されたしきい値を下回った場合は中断します。

いくつかのメモ:

  • 私は、円が散乱点の範囲を超えることができないと仮定しました(つまり、散乱の境界正方形は「見えない壁」として機能します)。
  • 適切なパーセンタイルは、初期グリッドの精度に依存します。これは、while反復の量、およびcntの最適な初期値にも影響します。
function [xBest,yBest,R] = q42806059
rng(1)
x=Rand(1,100)*5;
y=Rand(1,100)*5;

%% Find the approximate region(s) where there exists a point farthest from all the rest:
xExtent = linspace(min(x),max(x),numel(x)); 
yExtent = linspace(min(y),max(y),numel(y)).';
% Create a grid:
[XX,YY] = meshgrid(xExtent,yExtent);
% Compute pairwise distance from grid points to free points:
D = reshape(min(pdist2([XX(:),YY(:)],[x(:),y(:)]),[],2),size(XX));
% Intermediate plot:
% figure(); plot(x,y,'.k'); hold on; contour(XX,YY,D); axis square; grid on;
% Remove irrelevant candidates:
D(D<prctile(D(:),95)) = NaN;
D(D > xExtent | D > yExtent | D > yExtent(end)-yExtent | D > xExtent(end)-xExtent) = NaN;
%% Keep only the region with the largest distance
L = bwlabel(~isnan(D));
[~,I] = max(table2array(regionprops('table',L,D,'MaxIntensity')));
D(L~=I) = NaN;
% surf(XX,YY,D,'EdgeColor','interp','FaceColor','interp');
%% Iterate until sufficient precision:
xExtent = xExtent(~isnan(min(D,[],1,'omitnan')));
yExtent = yExtent(~isnan(min(D,[],2,'omitnan')));
cnt = 1; % increase or decrease according to the nature of the problem
while true
  % Same ideas as above, so no explanations:
  xExtent = linspace(xExtent(1),xExtent(end),20); 
  yExtent = linspace(yExtent(1),yExtent(end),20).'; 
  [XX,YY] = meshgrid(xExtent,yExtent);
  D = reshape(min(pdist2([XX(:),YY(:)],[x(:),y(:)]),[],2),size(XX));
  D(D<prctile(D(:),95)) = NaN;
  I = find(D == max(D(:)));
  xBest = XX(I);
  yBest = YY(I);  
  if nanvar(D(:)) < 1E-10 || cnt == 10
    R = D(I);
    break
  end
  xExtent = (1+[-1 +1]*10^-cnt)*xBest;
  yExtent = (1+[-1 +1]*10^-cnt)*yBest;
  cnt = cnt+1;
end
% Finally:
% rectangle('Position',[xBest-R,yBest-R,2*R,2*R],'Curvature',[1 1],'EdgeColor','r');

Anderのサンプルデータで得られる結果は[x,y,r] = [0.7832, 2.0694, 0.7815](同じです)。実行時間は、Anderのソリューションの約半分です。

中間プロットは次のとおりです。

ポイントから提供されたすべてのポイントのセットまでの最大(クリア)距離の輪郭:

Distances from existing points

境界からの距離を考慮した後、離れたポイントの上位5%のみを保持し、最大距離を含む領域のみを考慮します(表面は保持された値を表します)。

After keeping the largest region

そして最後に: - Showing the found circle

23
Dev-iL

この問題は「直接検索」を使用して解決できるという事実( 別の回答 に見られるように)は、これを グローバル最適化 問題として見ることができることを意味します。このような問題を解決するためのさまざまな方法があり、それぞれ特定のシナリオに適しています。私の個人的な好奇心から、遺伝的アルゴリズムを使用してこれを解決することにしました。

一般的に言えば、このようなアルゴリズムでは、特定の「適合関数」の下で「進化」の対象となる「遺伝子」のセットとしてソリューションを考える必要があります。偶然にも、この問題の遺伝子とフィットネス関数を特定するのは非常に簡単です。

  • 遺伝子:xyr
  • フィットネス関数:技術的には、円の最大面積ですが、これは最大r(または最小-r、アルゴリズムにはminimize)への関数が必要なため。
  • 特別な制約-rが指定されたポイントの最も近いユークリッド距離よりも大きい場合(つまり、円にポイントが含まれている場合)、生物は「死」ます。

以下は、このようなアルゴリズムの基本的な実装です(「basic」。完全に最適化されておらず、最適化の余地が多いためです。しゃれはありません この問題で)。

function [x,y,r] = q42806059b(cloudOfPoints)
% Problem setup
if nargin == 0
  rng(1)
  cloudOfPoints = Rand(100,2)*5; % equivalent to Ander's initialization.
end
%{
figure(); plot(cloudOfPoints(:,1),cloudOfPoints(:,2),'.w'); hold on; axis square;
set(gca,'Color','k'); plot(0.7832,2.0694,'ro'); plot(0.7832,2.0694,'r*');
%}
nVariables = 3;
options = optimoptions(@ga,'UseVectorized',true,'CreationFcn',@gacreationuniform,...
                       'PopulationSize',1000);

S = max(cloudOfPoints,[],1); L = min(cloudOfPoints,[],1); % Find geometric bounds:
% In R2017a: use [S,L] = bounds(cloudOfPoints,1);

% Here we also define distance-from-boundary constraints.
g = ga(@(g)vectorized_fitness(g,cloudOfPoints,[L;S]), nVariables,...
       [],[], [],[], [L 0],[S min(S-L)], [], options);
x = g(1); y = g(2); r = g(3);
%{
plot(x,y,'ro'); plot(x,y,'r*'); 
rectangle('Position',[x-r,y-r,2*r,2*r],'Curvature',[1 1],'EdgeColor','r'); 
%}

function f = vectorized_fitness(genes,pts,extent)
% genes = [x,y,r]
% extent = [Xmin Ymin; Xmax Ymax]
% f, the fitness, is the largest radius.
f = min(pdist2(genes(:,1:2), pts, 'euclidean'), [], 2);
% Instant death if circle contains a point:
f( f < genes(:,3) ) = Inf;
% Instant death if circle is too close to boundary:
f( any( genes(:,3) > genes(:,1:2) - extent(1,:) | ...
        genes(:,3) > extent(2,:) - genes(:,1:2), 2) ) = Inf;
% Note: this condition may possibly be specified using the A,b inputs of ga().
f(isfinite(f)) = -genes(isfinite(f),3);
%DEBUG: 
%{
     scatter(genes(:,1),genes(:,2),10 ,[0, .447, .741] ,'o'); % All
 z = ~isfinite(f); scatter(genes(z,1),genes(z,2),30,'r','x'); % Killed
 z =  isfinite(f); scatter(genes(z,1),genes(z,2),30,'g','h'); % Surviving
 [~,I] = sort(f); scatter(genes(I(1:5),1),genes(I(1:5),2),30,'y','p'); % Elite
%}

そして、典型的な実行の47世代の「タイムラプス」プロットがあります。

Time lapse

(青い点が現在の世代であり、赤い十字が「殺された」生物であり、緑色の六角形が「殺されていない」生物であり、赤い丸が行き先を示しています)。

14
Dev-iL

Image Processing Toolboxの bwdist を使用して、画像の距離変換を計算できます。これは、@ AnderBiguriの答えで十分に説明されているボロノイ図を作成する方法と見なすことができます。

img = imread('AbmxL.jpg');
%convert the image to a binary image
points = img(:,:,3)<200;
%compute the distance transform of the binary image
dist = bwdist(points);
%find the circle that has maximum radius
radius = max(dist(:));
%find position of the circle
[x y] = find(dist == radius);
imshow(dist,[]);
hold on
plot(y,x,'ro');

enter image description here

14
rahnema1

私は画像処理に慣れていないので、単なるアイデアです。

各粒子(ピクセル)をr = image_size(すべて重なる)で丸いグラデーションに変換するガウスフィルター(ぼかし)のようなものを実装します。これにより、最も白いピクセルが最良の結果になる写真を得ることができます。残念なことに、gimpでのデモンストレーションは失敗しました。極端なぼかしにより、ドットが消えてしまったからです。

または、エリア内のすべての隣接ピクセルをマークすることにより、既存のすべてのピクセルを増分拡張することができます(例:r = 4)

1
Daniel Alder