Подгонка наибольшего круга в свободной области на изображении с распределенной частицей

50

Я работаю над изображениями для обнаружения и установки максимально возможного круга в любой свободной области изображения, содержащего распределенные частицы: Изображение 536

(способный обнаруживать местоположение частицы).

Одно направление - определить круг, касающийся любой трехточечной комбинации, проверяя, свободен ли круг, а затем нахожу самый большой круг среди всех пустых кругов. Однако это приводит к огромному количеству комбинаций, т.е. C(n,3), где n - общее количество частиц в изображении.

Я был бы признателен, если кто-нибудь может предоставить мне какой-либо намек или альтернативный метод, который я могу исследовать.

  • 0
    Я полагаю, у вас есть предел радиуса, верно? Также я предполагаю, что вы имеете в виду наименьший круг с как минимум n частицами внутри, иначе самый большой круг это r=Inf . Или вы имеете в виду самый большой круг, содержащий максимум n частиц?
  • 2
    @ AnderBiguri Я думаю, он имеет в виду самый большой круг без каких-либо частиц в нем
Показать ещё 9 комментариев
Теги:
image-processing
math
geometry
particle

5 ответов

92
Лучший ответ

Давайте сделаем некоторые математики моим другом, так как математика всегда будет до конца!

Википедия:

В математике диаграмма Вороного представляет собой разбиение плоскости на области на основе расстояния до точек в определенном подмножестве плоскости.

Например:

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


voronoi(x,y);

Изображение 6307

Самое приятное в этой диаграмме заключается в том, что если вы заметите, все ребра/вершины этих синих областей будут равны расстоянию до точек вокруг них. Таким образом, если мы знаем расположение вершин и вычисляем расстояния до ближайших точек, то мы можем выбрать вершину с наибольшим расстоянием как наш центр круга.

Интересно отметить, что края областей Вороного также определяются как окружности треугольников, порожденных триангуляцией Делоне.

Поэтому, если мы вычислим триангуляцию Деланея области и их окружения

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);

Изображение 6308

Обратите внимание, как центр круга находится на одной вершине диаграммы Вороного.


ПРИМЕЧАНИЕ: это обнаружит центр внутри [0-5], [0-5]. вы можете легко изменить его, чтобы изменить это ограничение. Вы также можете попытаться найти круг, который полностью соответствует интересующей области (в отличие от центра). Для этого потребуется небольшое добавление в конце, где будет получен максимум.

  • 0
    Довольно особенный ответ, хорошая работа, Андер. Один вопрос: вы используете функцию delaunayTriangulation ... это пользовательская функция? Почему бы не использовать встроенный Matlab в delaunay ?
  • 0
    @Wolfie Это не пользовательская функция!
Показать ещё 13 комментариев
22

Я хотел бы предложить другое решение на основе поиска сетки с уточнением. Это не так продвинуто, как Ander, или так короче, как rahnema1, но это должно быть очень легко следовать и понимать. Кроме того, он работает довольно быстро.

Алгоритм содержит несколько этапов:

  • Мы генерируем равномерно распределенную сетку.
  • Мы находим минимальные расстояния точек в сетке ко всем предоставленным точкам.
  • Отбрасываем все точки, расстояния которых ниже определенного процентиля (например, 95-го).
  • Мы выбираем область, которая содержит наибольшее расстояние (это должно содержать правильный центр, если моя начальная сетка достаточно тонкая).
  • Мы создаем новый мешгрид вокруг выбранной области и снова находим расстояния (эта часть явно неоптимальна, поскольку расстояния вычисляются по всем точкам, включая далеко идущие и нерелевантные).
  • Мы повторяем уточнение в пределах области, при этом следим за изменением 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.

Вот промежуточные графики:

Контур наибольшего (прозрачного) расстояния от точки до множества всех предоставленных точек:

Изображение 6309

После рассмотрения расстояния от границы, сохраняя только верхние 5% удаленных точек и рассматривая только область, которая содержит наибольшее расстояние (кусок поверхности представляет сохраненные значения):

Изображение 6310

И наконец: Изображение 6311

  • 2
    эх, твои сюжеты милее моих!
  • 1
    @AnderBiguri Нет, не волнуйся ... просто посмотри версию до редактирования :)
Показать ещё 1 комментарий
13

Тот факт, что эта проблема может быть решена с помощью "прямого поиска" (как можно видеть в другом ответе), означает, что можно рассматривать это как глобальная оптимизация. Существуют различные способы решения таких проблем, каждый из которых подходит для определенных сценариев. Из моего личного любопытства я решил решить эту проблему с помощью генетического алгоритма.

Вообще говоря, такой алгоритм требует, чтобы мы рассматривали решение как набор "генов", подверженных "эволюции" при определенной "функции пригодности". Как это бывает, довольно легко определить гены и функцию фитнеса в этой проблеме:

  • Гены: x, y, r.
  • Функция фитнеса: технически максимальная площадь круга, но это эквивалентно максимальному r (или минимальному -r, поскольку алгоритм требует минимизации функции).
  • Особое ограничение - если r больше, чем евклидово расстояние до ближайшей из предоставленных точек (т.е. круг содержит точку), организм "умирает".

Ниже приведена базовая реализация такого алгоритма ( "базовая", потому что она полностью неоптимизирована, и в этой задаче есть много возможностей для оптимизации no pun).

function [x,y,r] = q42806059b(cloudOfPoints)
% Problem setup
if nargin == 0
  rng(1)
  cloudOfPoints = rand(100,2)*5; % equivalent to Ander 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 поколений типичного запуска:

Изображение 6312

(Там, где синие точки являются текущим поколением, красные кресты являются "убитыми инста" организмами, зеленые гексаграммы являются "не-insta-убитыми" организмами, а красный круг обозначает назначение).

  • 3
    Хотя этот подход кажется читателю просто упражнением (то есть, вероятно, лучше использовать других, в том числе и ваш), чувак, это так круто! Это GIF: P
13

Вы можете использовать bwdist из Image Processing Toolbox для вычисления преобразования расстояния изображения. Это можно рассматривать как метод создания диаграммы ворона, хорошо объясненный в ответе @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');

Изображение 6313

  • 1
    Вы должны использовать те же данные, что и я, чтобы мы могли лучше сравнить! Результаты bwdist выглядят очень трогательно
  • 4
    Вы знаете, что можете использовать URL с imread , верно? то есть это работает: img = imread('https://i.stack.imgur.com/AbmxL.jpg');
Показать ещё 3 комментария
1

Я не привык к обработке изображений, поэтому это просто идея:

Реализуйте что-то вроде gaussian filter (blur), который преобразует каждую частицу (пиксели) в округлый градиент с r = image_size (все они перекрываются). Таким образом, вы должны получить изображение, где лучшие белые пиксели должны быть лучшими результатами. К сожалению, демонстрация в gimp не удалась, потому что крайняя размытость заставила точки исчезнуть.

В качестве альтернативы вы можете увеличивать все существующие пиксели, помещая все соседние пиксели в области (пример: r = 4), причем левые пиксели будут одинаковыми (те, у которых наибольшее расстояние до любого пикселя)

  • 1
    Это хорошее начало, но в итоге обработка изображения имеет ограничение по размеру в пикселях. Всегда будет минимальная ошибка 1 * размер пикселя
  • 2
    Во всяком случае, этот подход не сопоставим (= далеко за пределами) того, что @AnderBiguri ответил :-P

Ещё вопросы

Сообщество Overcoder
Наверх
Меню