我有一个.txt文件,在2-D平面上有大约100,000个点。当我绘制点时,有一个明确定义的二维区域(想象一下已经变形了一点的二维光盘)。
计算该地区面积的最简单方法是什么?在Matlab中轻松做任何事情?
我通过在区域的边界上找到一堆(如40个)点并在Matlab中计算多边形区域的区域来进行多边形近似,但我想知道是否存在另一种不那么繁琐的方法而不是找到40个点在边界上。
答案 0 :(得分:6)
考虑这个例子:
%# 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) )
有一个简短的截屏视频Doug Hull可以解决这个问题。
我发布的第二个答案受到@Jean-FrançoisCorbett提出的解决方案的启发。
首先我创建随机数据,并使用interactive brush tool,删除一些点,使其看起来像所需的“肾”形状......
要使用基线进行比较,我们可以使用IMFREEHAND函数手动跟踪封闭区域(我使用笔记本电脑的触摸板进行此操作,因此不是最精确的绘图!)。然后我们使用POLYAREA找到该多边形的区域。就像我之前的回答一样,我也计算了凸包:
现在,基于我已回答的previous SO question(2D直方图),我们的想法是在数据上铺设网格。网格分辨率的选择非常重要,我使用的数据为numBins = [20 30];
。
接下来,我们计算包含足够点数的正方形(我使用至少1
点作为阈值,但您可以尝试更高的值)。最后,我们将此计数乘以一个网格的面积,以获得近似的总面积。
%### 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
这是叠加的所有三种方法的最终结果,显示区域近似值:
答案 1 :(得分:2)
我几乎什么都不知道,所以不要在这方面投入太多...考虑做Delaunay三角测量。然后移除长度超过某个最大值的任何船体(外部)边缘。重复,直到无法删除。填写剩余的三角形。
这将孤立一些异常点。
答案 2 :(得分:2)
我的答案是最简单的,也许是最不优雅和最精确的。但首先,对以前的答案发表评论:
由于你的形状通常是肾形(不是凸形的),计算其凸包的面积是不行的,另一种方法是确定它的凹壳(参见例如http://www.concavehull.com/home.php?main_menu=1)并计算面积那个。但确定凹形船体比凸形船体困难得多。此外,落后点会导致凸起和凹形船体出现问题。
正如@Ed Staub的回答中所建议的那样,Delaunay三角测量随后修剪可能会更加直截了当。我自己的建议是:您的表面积计算有多精确?我的猜测是,不是很好。无论是凹形船体还是修剪过的Delaunay三角剖分,您都必须随意选择形状的“边界”(边缘不是刀尖,我看到周围有一些散落点)它)。
因此,更简单的算法可能对您的应用程序同样有效。
在正交网格中划分图像。循环遍历所有网格“像素”或正方形;如果给定的方格包含至少一个点(或者可能是两个点?),则将方块标记为已满,否则为空。最后,添加所有正方形的区域。宾果
唯一的参数是分辨率长度(方块的大小)。在Delaunay三角剖分的情况下,它的值应该设置为类似于修剪长度的东西,即“我的形状内的点比这个长度更接近彼此,并且应该忽略比这个长度更远的点”。
或许附加参数是被视为满的正方形的点数阈值。也许2会很好地忽略落后点,但这可能会定义主要形状有点太紧,不适合你的口味......尝试1和2,也许平均两者。或者,使用1并修剪掉没有邻居的方块(游戏生活方式)。同样地,8个邻居已满的空方块应该被认为是满的,以避免形状中间的洞。
这个算法可以改进多少是无止境的,但由于特定应用中问题定义的固有任意性,任何细化都可能是“抛光粪便”的算法。
答案 3 :(得分:0)
我建议使用空间填充曲线,例如z曲线或更好的摩尔曲线。一个sfc填满整个空间,并且很好地索引每个点。例如,对于所有f(x)= y,您可以按升序排序曲线的点,并从该结果中获取尽可能多的点,直到您获得完整的往返。然后,您可以使用这些点来计算面积。因为你有很多点可能你想使用更少的点并使用一个集群,这会使结果不准确。
答案 4 :(得分:0)
我认为你可以使用凸包算法获得边界点,限制边长(你应该按垂直轴排序点)。因此,它将遵循您所在地区的非凸性。我建议长度为0.02。在任何情况下,您都可以尝试不同的长度绘制结果并直观地检查它。