Нахождение области двумерного набора данных - PullRequest
6 голосов
/ 04 августа 2011

У меня есть файл .txt с примерно 100 000 точек в 2-D плоскости.Когда я рисую точки, есть четко определенная двумерная область (представьте себе двумерный диск, который немного изменился).

Какой самый простой способ вычислить площадь этого региона?Любой способ сделать легко в Matlab?

Я сделал полигональное приближение, найдя группу (например, 40) точек на границе области и вычислив площадь полигональной области в Matlab, но мне было интересно, есть ли другой, менее утомительный методнайти 40 точек на границе.

Ответы [ 5 ]

6 голосов
/ 04 августа 2011

Рассмотрим этот пример:

%# random points
x = randn(300,1);
y = randn(300,1);

%# convex hull
dt = DelaunayTri(x,y);
k = convexHull(dt);

%# area of convex hull
ar = polyarea(dt.X(k,1),dt.X(k,2))

%# plot
plot(dt.X(:,1), dt.X(:,2), '.'), hold on
fill(dt.X(k,1),dt.X(k,2), 'r', 'facealpha', 0.2);
hold off
title( sprintf('area = %g',ar) )

screenshot

Есть короткая скринкаст Даг Халл , которая решает эту точную проблему.


РЕДАКТИРОВАТЬ:

Я публикую второй ответ, вдохновленный решением, предложенным @Jean-FrançoisCorbett.

Сначала я создаю случайные данные и использую интерактивный кисть , я удаляю некоторые точки, чтобы они выглядели как желаемая форма "почки" ...

Чтобы иметь базовую линию для сравнения, мы можемвручную отследить окружающую область, используя функцию IMFREEHAND (я делаю это с помощью сенсорной панели моего ноутбука, поэтому не самый точный рисунок!).Затем мы находим площадь этого многоугольника, используя POLYAREA .Как и мой предыдущий ответ, я также вычисляю выпуклую оболочку:

freehand convexhull

Теперь и на основе предыдущего SO вопроса Я ответил(2D-гистограмма), идея состоит в том, чтобы наложить сетку на данные.Выбор разрешения сетки очень важен, у меня было numBins = [20 30]; для используемых данных.

Далее мы подсчитываем количество квадратов, содержащих достаточно точек (я использовал как минимум 1 точку в качестве порога, но выможно попробовать более высокое значение).Наконец, мы умножаем это число на площадь одного квадрата сетки, чтобы получить приблизительную общую площадь.

hist2d hist2d_threshold

%### DATA ###
%# some random data
X = randn(100000,1)*1;
Y = randn(100000,1)*2;

%# HACK: remove some point to make data look like a kidney
idx = (X<-1 & -4<Y & Y<4 ); X(idx) = []; Y(idx) = [];
%# or use the brush tool
%#brush on

%### imfreehand ###
figure
line('XData',X, 'YData',Y, 'LineStyle','none', ...
    'Color','b', 'Marker','.', 'MarkerSize',1);
daspect([1 1 1])
hROI = imfreehand('Closed',true);
pos = getPosition(hROI);        %# pos = wait(hROI);
delete(hROI)

%# total area
ar1 = polyarea(pos(:,1), pos(:,2));

%# plot
hold on, plot(pos(:,1), pos(:,2), 'Color','m', 'LineWidth',2)
title('Freehand')

%### 2D histogram ###
%# center of bins
numBins = [20 30];
xbins = linspace(min(X), max(X), numBins(1));
ybins = linspace(min(Y), max(Y), numBins(2));

%# map X/Y values to bin-indices
Xi = round( interp1(xbins, 1:numBins(1), X, 'linear', 'extrap') );
Yi = round( interp1(ybins, 1:numBins(2), Y, 'linear', 'extrap') );

%# limit indices to the range [1,numBins]
Xi = max( min(Xi,numBins(1)), 1);
Yi = max( min(Yi,numBins(2)), 1);

%# count number of elements in each bin
H = accumarray([Yi(:), Xi(:)], 1, [numBins(2) numBins(1)]);

%# total area
THRESH = 0;
sqNum = sum(H(:)>THRESH);
sqArea = (xbins(2)-xbins(1)) * (ybins(2)-ybins(1));
ar2 = sqNum*sqArea;

%# plot 2D histogram/thresholded_histogram
figure, imagesc(xbins, ybins, H)
axis on, axis image, colormap hot; colorbar; %#caxis([0 500])
title( sprintf('2D Histogram, bins=[%d %d]',numBins) )
figure, imagesc(xbins, ybins, H>THRESH)
axis on, axis image, colormap gray
title( sprintf('H > %d',THRESH) )

%### convex hull ###
dt = DelaunayTri(X,Y);
k = convexHull(dt);

%# total area
ar3 = polyarea(dt.X(k,1), dt.X(k,2));

%# plot
figure, plot(X, Y, 'b.', 'MarkerSize',1), daspect([1 1 1])
hold on, fill(dt.X(k,1),dt.X(k,2), 'r', 'facealpha',0.2); hold off
title('Convex Hull')

%### plot ###
figure, hold on

%# plot histogram
imagesc(xbins, ybins, H>=1)
axis on, axis image, colormap gray

%# plot grid lines
xoff = diff(xbins(1:2))/2; yoff = diff(ybins(1:2))/2;
xv1 = repmat(xbins+xoff,[2 1]); xv1(end+1,:) = NaN;
yv1 = repmat([ybins(1)-yoff;ybins(end)+yoff;NaN],[1 size(xv1,2)]);
yv2 = repmat(ybins+yoff,[2 1]); yv2(end+1,:) = NaN;
xv2 = repmat([xbins(1)-xoff;xbins(end)+xoff;NaN],[1 size(yv2,2)]);
xgrid = [xv1(:);NaN;xv2(:)]; ygrid = [yv1(:);NaN;yv2(:)];
line(xgrid, ygrid, 'Color',[0.8 0.8 0.8], 'HandleVisibility','off')

%# plot points
h(1) = line('XData',X, 'YData',Y, 'LineStyle','none', ...
    'Color','b', 'Marker','.', 'MarkerSize',1);

%# plot convex hull
h(2) = patch('XData',dt.X(k,1), 'YData',dt.X(k,2), ...
    'LineWidth',2, 'LineStyle','-', ...
    'EdgeColor','r', 'FaceColor','r', 'FaceAlpha',0.5);

%# plot freehand polygon
h(3) = plot(pos(:,1), pos(:,2), 'g-', 'LineWidth',2);

%# compare results
title(sprintf('area_{freehand} = %g, area_{grid} = %g, area_{convex} = %g', ...
    ar1,ar2,ar3))
legend(h, {'Points' 'Convex Jull','FreeHand'})
hold off

Вот окончательный результат всех трех наложенных методовс отображением приблизительных значений площади:

final

2 голосов
/ 04 августа 2011

Мой ответ самый простой и, возможно, наименее элегантный и точный.Но сначала прокомментируйте предыдущие ответы:

Поскольку ваша фигура обычно имеет форму почки (не выпуклой), вычисление площади ее выпуклой оболочки не годится, и альтернативой является определение ее вогнутой оболочки (см., например, http://www.concavehull.com/home.php?main_menu=1) и вычислите площадь этого. Но определить вогнутый корпус гораздо сложнее, чем выпуклый корпус. Кроме того, зацепившиеся точки вызовут проблемы как в выпуклом, так и вогнутом корпусе.

Триангуляция Делоне с последующей обрезкой, как предложено в ответе @Ed Staub, может быть немного проще:

Мое собственное предложение таково: Насколько точным должен быть ваш расчет площади поверхности?Я думаю, не очень. С вогнутой оболочкой или с обрезанной триангуляцией Делоне, вам все равно придется сделать произвольный выбор относительно того, где находится «граница» вашей фигуры (край не заострен, и я вижу,вокруг него есть несколько странных точек).

Поэтому более простой алгоритм может быть столь же хорош для вашего приложения.Нация.

Разделите ваше изображение в ортогональной сетке.Перебрать все пиксели или квадраты сетки;если данный квадрат содержит хотя бы одну точку (или, возможно, две точки?), пометьте квадрат как полный, иначе пустой.Наконец, добавьте площадь всех полных квадратов.Бинго.

Единственным параметром является длина разрешения (размер квадратов).В случае триангуляции Делоне его значение должно быть примерно таким же, как длина обрезки, т. Е. «Точки в моей фигуре ближе друг к другу, чем эта длина, и точки, расположенные дальше, чем эта длина, должны игнорироваться».

Возможно, дополнительным параметром является пороговое значение количества точек для квадрата, который следует считать заполненным.Возможно, 2 было бы хорошо, если бы мы игнорировали точки отставания, но это может определить основную форму слишком плотно на ваш вкус ... Попробуйте оба варианта 1 и 2, и, возможно, возьмите в среднем оба значения.Или используйте 1 и уберите квадраты, у которых нет соседей (стиль жизни).Одновременно, пустые квадраты, чьи 8 соседей заполнены, следует считать заполненными, чтобы избежать дыр в середине фигуры.

Нет предела тому, насколько этот алгоритм может быть усовершенствован, но из-за произвольности, присущей определению проблемы в вашем конкретном приложении, любое уточнение, вероятно, является алгоритмом, эквивалентным «полировке какашки».

2 голосов
/ 04 августа 2011

Я почти ничего не знаю, так что не стоит в этом разбираться ... подумайте о триангуляции Делоне. Затем удалите все края корпуса (наружные) длиннее, чем максимум. Повторите, пока ничего не удалить. Заполните оставшиеся треугольники.

Это осиротит некоторые посторонние точки.

0 голосов
/ 04 августа 2011

Я думаю, вы можете получить граничные точки, используя алгоритм выпуклой оболочки с ограничением по длине ребра (вы должны сортировать точки по вертикальной оси).Таким образом будет следовать невыпуклость вашего региона.Я предлагаю длину раунда 0,02.В любом случае вы можете немного поэкспериментировать с разной длиной, нарисовав результат и изучив его визуально.

0 голосов
/ 04 августа 2011

Я предлагаю использовать кривую заполнения пространства, например, z-кривую или лучше кривую Мура. SFC заполняет все пространство и хорошо индексировать каждую точку. Например, для всех f (x) = y вы можете отсортировать точки кривой в порядке возрастания, и из этого результата вы берете столько точек, пока не получите полный обход Эти точки вы можете использовать для вычисления площади. Поскольку у вас много точек, возможно, вы захотите использовать меньше точек и использовать кластер, который сделает результат менее точным.

...