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:
(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.
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);
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);
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.
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:
Plusieurs notes:
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.
Contour de la plus grande distance (nette) d'un point à l'ensemble de tous les points fournis:
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):
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:
x
, y
, r
.r
(ou minimum -r
, puisque l'algorithme requiert une fonction pour minimiser).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:
(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).
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');
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)