web-dev-qa-db-fra.com

Ajustement du plus grand cercle dans la zone libre de l'image avec une particule distribuée

Je travaille sur des images pour détecter et ajuster le plus grand cercle possible dans l'une des zones libres d'une image contenant des particules distribuées:1

(capable de détecter l'emplacement des particules).

Une direction consiste à définir un cercle touchant n'importe quelle combinaison à 3 points, en vérifiant si le cercle est vide, puis en trouvant le plus grand cercle parmi tous les cercles vides. Cependant, cela conduit à un grand nombre de combinaisons, c'est-à-dire C(n,3), où n est le nombre total de particules dans l'image.

J'apprécierais que quelqu'un puisse me fournir un indice ou une méthode alternative que je puisse explorer.

50
T50740

Permet de faire des maths mon ami, car les maths arriveront toujours à la fin!

Wikipédia:

En mathématiques, un diagramme de Voronoi est une partition d'un plan en régions en fonction de la distance aux points d'un sous-ensemble spécifique du plan.

Par exemple:

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


voronoi(x,y);

enter image description here

La bonne chose à propos de ce diagramme est que si vous remarquez, tous les bords/sommets de ces zones bleues sont tous à égale distance des points qui les entourent. Ainsi, si nous connaissons l'emplacement des sommets et calculons les distances aux points les plus proches, nous pouvons alors choisir le sommet ayant la distance la plus élevée comme centre du cercle.

Fait intéressant, les bords d'une région de Voronoi sont également définis comme les circoncentres des triangles générés par une triangulation de Delaunay.

Donc, si nous calculons la triangulation de Delaunay de la zone, et leurs circoncentres

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

Et calculez les distances entre les circoncentres et l'un des points qui définissent chaque triangle:

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

Ensuite, nous avons le centre (cc) et le rayon (distance) de tous les cercles possibles qui n'ont aucun point à l'intérieur. Nous avons juste besoin du plus gros!

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

Permet maintenant de tracer

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

Remarquez comment le centre du cercle se trouve sur un sommet du diagramme de Voronoi.


[~ # ~] note [~ # ~] : ceci trouvera le centre à l'intérieur de [0-5], [0-5]. vous pouvez facilement le modifier pour changer cette contrainte. Vous pouvez également essayer de trouver le cercle qui tient sur son intégralité à l'intérieur de la zone intéressée (par opposition au seul centre). Cela nécessiterait un petit ajout à la fin où le maximum est obtenu.

89
Ander Biguri

J'aimerais proposer une autre solution basée sur une recherche de grille avec raffinement. Ce n'est pas aussi avancé que celui d'Ander ou aussi court que rahnema1, mais il devrait être très facile à suivre et à comprendre. En outre, il fonctionne assez rapidement.

L'algorithme contient plusieurs étapes:

  1. Nous générons une grille régulièrement espacée.
  2. Nous trouvons les distances minimales des points de la grille à tous les points fournis.
  3. Nous rejetons tous les points dont les distances sont inférieures à un certain centile (par exemple 95e).
  4. Nous choisissons la région qui contient la plus grande distance (celle-ci devrait contenir le bon centre si ma grille initiale est assez fine).
  5. Nous créons un nouveau maillage autour de la région choisie et retrouvons les distances (cette partie est clairement sous-optimale, car les distances sont calculées pour tous les points, y compris ceux éloignés et non pertinents).
  6. Nous itérons le raffinement au sein de la région, tout en gardant un œil sur la variance des 5% supérieurs des valeurs -> si elle tombe en dessous d'un seuil prédéfini, nous cassons.

Plusieurs notes:

  • J'ai fait l'hypothèse que les cercles ne peuvent pas aller au-delà de l'étendue des points diffusés (c'est-à-dire que le carré de délimitation de la diffusion agit comme un "mur invisible").
  • Le centile approprié dépend de la finesse de la grille initiale. Cela affectera également le nombre d'itérations while et la valeur initiale optimale pour 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');

Le résultat que j'obtiens pour les données d'exemple d'Ander est [x,y,r] = [0.7832, 2.0694, 0.7815] (qui est le même). Le temps d'exécution représente environ la moitié de la solution d'Ander.

Voici les parcelles intermédiaires:

Contour de la plus grande distance (nette) d'un point à l'ensemble de tous les points fournis:

Distances from existing points

Après avoir considéré la distance de la frontière, en ne gardant que les 5% supérieurs des points distants, et en considérant uniquement la région qui contient la plus grande distance (le morceau de surface représente les valeurs conservées):

After keeping the largest region

Et enfin: Showing the found circle

23
Dev-iL

Le fait que ce problème puisse être résolu en utilisant une "recherche directe" (comme on peut le voir dans autre réponse ) signifie que l'on peut considérer cela comme un problème optimisation globale . Il existe différentes manières de résoudre ces problèmes, chacune appropriée à certains scénarios. Par curiosité personnelle, j'ai décidé de résoudre ce problème en utilisant un algorithme génétique .

De manière générale, un tel algorithme nous oblige à penser la solution comme un ensemble de "gènes" soumis à "évolution" sous une certaine "fonction fitness". En l'occurrence, il est assez facile d'identifier les gènes et la fonction de fitness dans ce problème:

  • Gènes: x, y, r.
  • Fonction fitness: techniquement, zone maximale du cercle, mais cela équivaut au maximum r (ou minimum -r, puisque l'algorithme requiert une fonction pour minimiser).
  • Contrainte spéciale - si r est plus grande que la distance euclidienne au plus proche des points fournis (c'est-à-dire que le cercle contient un point), l'organisme "meurt".

Vous trouverez ci-dessous une implémentation de base d'un tel algorithme (" basique" car il n'est pas optimisé et il y a beaucoup de place pour l'optimisationsans jeu de mots dans ce problème).

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

Et voici un scénario "time-lapse" de 47 générations d'une course typique:

Time lapse

(Là où les points bleus sont la génération actuelle, les croix rouges sont des organismes "tués insta", les hexagrammes verts sont les organismes "non tués insta" et le cercle rouge marque la destination).

14
Dev-iL

Vous pouvez utiliser bwdist de Image Processing Toolbox pour calculer la transformation de distance de l'image. Cela peut être considéré comme une méthode pour créer un diagramme de voronoi bien expliqué dans la réponse de @ 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

Je ne suis pas habitué au traitement d'image, donc c'est juste une idée:

Implémentez quelque chose comme un filtre gaussien (flou) qui transforme chaque particule (pixels) en un dégradé rond avec r = image_size (tous se chevauchant). De cette façon, vous devriez obtenir une image où les pixels les plus blancs devraient être les meilleurs résultats. Malheureusement, la démonstration de gimp a échoué car l'extrême flou a fait disparaître les points.

Alternativement, vous pouvez étendre progressivement tous les pixels existants en marquant tous les pixels voisins dans une zone (exemple: r = 4), les pixels restants seraient le même résultat (ceux avec la plus grande distance à n'importe quel pixel)

1
Daniel Alder