计算图像和多边形之间重叠的简单方法是什么?

时间:2012-08-31 20:53:07

标签: matlab

我有一个封闭的非自相交多边形。它的顶点保存在两个向量X和Y中。最后,X和Y的值绑定在0到22之间。

我想构建一个大小为22x22的矩阵,如果多边形的一部分与该bin重叠,则设置每个bin的值等于true,否则为false。

我最初的想法是生成一个用[a, b] = meshgrid(1:22)定义的点网格,然后使用inpolygon来确定网格中哪些点在多边形中。

[a b] = meshgrid(1:22);
inPoly1 = inpolygon(a,b,X,Y);

但是,如果bin的中心包含在多边形中,则它仅返回true,即它返回下图中的红色形状。然而,需要更多的是绿色形状(尽管它仍然是一个不完整的解决方案)。

为了获得绿色斑点,我对inpolygon进行了四次调用。对于每次比较,我将NE,NW,SE或SW点的网格移动1/2。这相当于测试bin的角是否在多边形中。

inPoly2 = inpolygon(a-.5,b-.5,X,Y) | inpolygon(a+.5,b-.5,X,Y) | inpolygon(a-.5,b+5,X,Y) | inpolygon(a+.5,b+.5,X,Y);

虽然这确实为我提供了部分解决方案,但是当一个顶点包含在一个bin但没有bin角落的情况下它会失败。

是否有更直接的方法来解决此问题,最好是一种能够产生更易读代码的解决方案?

enter image description here

该情节用以下方式绘制:

imagesc(inPoly1 + inPoly2); hold on;
line(a, b, 'w.');
line(X, Y, 'y); 

6 个答案:

答案 0 :(得分:5)

一个建议是使用polybool功能(2008b或更早版本不提供)。它找到两个多边形的交集并返回结果顶点(如果不存在顶点,则返回空向量)。要在此处使用它,我们在网格检查中的所有方块上迭代(使用arrayfun)以查看polybool的输出参数是否为空(例如,没有重叠)。

N=22;
sqX = repmat([1:N]',1,N);
sqX = sqX(:);
sqY = repmat(1:N,N,1);
sqY = sqY(:);

intersects = arrayfun((@(xs,ys) ...
      (~isempty(polybool('intersection',X,Y,[xs-1 xs-1 xs xs],[ys-1 ys ys ys-1])))),...
      sqX,sqY);

intersects = reshape(intersects,22,22);

以下是生成的图像:

enter image description here

绘图编码:

imagesc(.5:1:N-.5,.5:1:N-.5,intersects');
hold on;
plot(X,Y,'w');
for x = 1:N
    plot([0 N],[x x],'-k');
    plot([x x],[0 N],'-k');
end
hold off;

答案 1 :(得分:1)

这个伪代码算法怎么样:

For each pair of points p1=p(i), p2=p(i+1), i = 1..n-1
    Find the line passing through p1 and p2
    Find every tile this line intersects // See note
    Add intersecting tiles to the list of contained tiles

Find the red area using the centers of each tile, and add these to the list of contained tiles

注意:这一行需要花费很少的精力才能实现,但我认为它有一个相当简单,众所周知的算法。

另外,如果我使用的是.NET,我只需定义一个与每个网格图块对应的矩形,然后查看与多边形相交的矩形。不过,我不知道在Matlab中检查交叉点是否容易。

答案 2 :(得分:1)

我建议在图像处理工具箱中使用poly2mask,它会或多或少地做你想要的,我想,也或多或少你自己和Salain所建议的。

答案 3 :(得分:1)

略有改善

首先,简化您的部分解决方案" - 你正在做的只是看着角落。如果不考虑22x22点的网格,你可以考虑23x23的角点网格(它将从较小的网格偏移(-0.5,-0.5)。一旦你有了,你可以在22x22网格上标记点在多边形中至少有一个角。

完整解决方案

但是,您真正需要的是多边形是否与每个像素周围的1x1框相交。这并不一定包括任何角落,但 要求多边形与框的四个边之一相交。

您可以找到多边形与包含框相交的像素的一种方法是使用以下算法:

For each pair of adjacent points in the polygon, calling them pA and pB:
    Calculate rounded Y-values: Round(pA.y) and Round(pB.y)
    For each horizontal pixel edge between these two values:
        * Solve the simple linear equation to find out at what X-coordinate
          the line between pA and pB crosses this edge
        * Round the X-coordinate
        * Use the rounded X-coordinate to mark the pixels above and below
          where it crosses the edge
    Do a similar thing for the other axis

例如,假设我们正在查看pA = (1, 1)pB = (2, 3)

  • 首先,我们计算了舍入的Y值:1和3。
  • 然后,我们会查看这些值之间的像素边缘:y = 1.5y = 2.5(像素边缘与像素的半偏移
  • 对于其中的每一个,我们求解线性方程以找出pA - > pB与我们的边相交的位置。这为我们提供了:x = 1.25, y = 1.5x = 1.75, y = 2.5
  • 对于每个交叉点,我们采用舍入的X值,并使用它来标记边缘两侧的像素。
    • x = 1.25四舍五入为1(对于边y = 1.5)。因此,我们可以将(1, 1)(1, 2)处的像素标记为我们设置的一部分。
    • x = 1.75四舍五入为2(对于边y = 2.5)。因此,我们可以在(2, 2)(2, 3)标记像素。

这样就可以处理水平边缘了。接下来,让我们看一下垂直的:

  • 首先我们计算舍入的X值:1和2
  • 然后,我们看一下像素边缘。这里只有一个:x = 1.5
  • 对于此边缘,我们会找到与pA - > pB行相交的位置。这给了我们x = 1.5, y = 2
  • 对于此交点,我们采用舍入的Y值,并使用它来标记边缘两侧的像素:
    • y = 2四舍五入为2.因此,我们可以在(1, 2)(2, 2)标记像素。

完成!

好吧,有点儿。这将为您提供边缘,但它不会填充多边形的主体。但是,您可以将这些与之前的(红色)结果结合起来以获得完整的设置。

答案 4 :(得分:0)

首先,我为此示例定义了一个低分辨率圆

X=11+cos(linspace(0,2*pi,10))*5;
Y=11+sin(linspace(0,2.01*pi,10))*5;

就像你的例子一样,它适合约22个单位的网格。然后,在您的引导之后,我们声明一个meshgrid并检查点是否在多边形中。

stepSize=0.1;
[a b] = meshgrid(1:stepSize:22);
inPoly1 = inpolygon(a,b,X,Y);

唯一不同的是,您的原始解决方案采取的步骤为1,此网格可以采取较小的步骤。最后,在正方形的“边缘”中包含任何内容

inPolyFull=unique( round([a(inPoly1) b(inPoly1)]) ,'rows');

round只需要我们的高分辨率网格,并将点适当地舍入到最接近的低分辨率等值。然后,我们使用unique限定符调用'rows',以矢量样式或成对方式删除所有重复项。就是这样

要查看结果,

[aOrig bOrig] = meshgrid(1:22);
imagesc(1:stepSize:22,1:stepSize:22,inPoly1); hold on;
plot(X,Y,'y');
plot(aOrig,bOrig,'k.');
plot(inPolyFull(:,1),inPolyFull(:,2),'w.'); hold off;

Example polygon

更改stepSize具有以速度和内存为代价改善结果的预期效果。

如果您需要的结果与示例中的inPoly2格式相同,则可以使用

inPoly2=zeros(22);
inPoly2(inPolyFull(:,1),inPolyFull(:,2))=1

希望有所帮助。我可以想到其他一些方法,但这似乎是最直接的。

答案 5 :(得分:0)

嗯,我想我迟到了,虽然严格来说明天的赏金时间是明天;)。但这是我的尝试。首先,标记包含/触摸点的单元格的函数。给定一个间距为lx,ly的结构化网格和一组带坐标(Xp,Yp)的点,包含单元格的集合:

function cells = mark_cells(lx, ly, Xp, Yp, cells)

% Find cell numbers to which points belong.
% Search by subtracting point coordinates from
% grid coordinates and observing the sign of the result.
% Points lying on edges/grid points are assumed
% to belong to all surrounding cells.

sx=sign(bsxfun(@minus, lx, Xp'));
sy=sign(bsxfun(@minus, ly, Yp'));
cx=diff(sx, 1, 2);
cy=diff(sy, 1, 2);

% for every point, mark the surrounding cells
for i=1:size(cy, 1)
    cells(find(cx(i,:)), find(cy(i,:)))=1;
end
end

现在,其余的代码。对于多边形中的每个线段(您必须逐个遍历线段),将线段与网格线相交。对于水平和垂直线,使用给定的网格点坐标仔细地完成交叉,以避免数字不准确。对于找到的交叉点,我调用mark_cells将周围的单元格标记为1:

% example grid
nx=21;
ny=51;
lx = linspace(0, 1, nx);
ly = linspace(0, 1, ny);
dx=1/(nx-1);
dy=1/(ny-1);
cells = zeros(nx-1, ny-1);

% for every line in the polygon...
% Xp and Yp contain start-end points of a single segment
Xp = [0.15 0.61];
Yp = [0.1 0.78];

% line equation
slope = diff(Yp)/diff(Xp);
inter = Yp(1) - (slope*Xp(1));

if isinf(slope)
    % SPECIAL CASE: vertical polygon segments
    % intersect horizontal grid lines
    ymax = 1+floor(max(Yp)/dy);
    ymin = 1+ceil(min(Yp)/dy);
    x=repmat(Xp(1), 1, ymax-ymin+1);
    y=ly(ymin:ymax);
    cells = mark_cells(lx, ly, x, y, cells);
else
    % SPECIAL CASE: not horizontal polygon segments
    if slope ~= 0
        % intersect horizontal grid lines
        ymax = 1+floor(max(Yp)/dy);
        ymin = 1+ceil(min(Yp)/dy);
        xmax = (ly(ymax)-inter)/slope;
        xmin = (ly(ymin)-inter)/slope;
        % interpolate in x...
        x=linspace(xmin, xmax, ymax-ymin+1);
        % use exact grid point y-coordinates!
        y=ly(ymin:ymax); 
        cells = mark_cells(lx, ly, x, y, cells);
    end

    % intersect vertical grid lines
    xmax = 1+floor(max(Xp)/dx);
    xmin = 1+ceil(min(Xp)/dx);
    % interpolate in y...
    ymax = inter+slope*lx(xmax);
    ymin = inter+slope*lx(xmin);
    % use exact grid point x-coordinates!
    x=lx(xmin:xmax); 
    y=linspace(ymin, ymax, xmax-xmin+1);
    cells = mark_cells(lx, ly, x, y, cells);
end

示例网格/细分的输出: output

遍历所有多边形线段会为您提供多边形“晕”。最后,使用标准的多边形函数获得多边形的内部。如果您需要有关代码的更多详细信息,请与我们联系。