I have a closed non-self-intersecting polygon. Its vertices are saved in two vectors X, and Y. Finally the values of X and Y are bound between 0 and 22.
I'd like to construct a matrix of size 22x22 and set the value of each bin equal to true if part of the polygon overlaps with that bin, otherwise false.
My initial thought was to generate a grid of points defined with [a, b] = meshgrid(1:22)
and then to use inpolygon
to determine which points of the grid were in the polygon.
[a b] = meshgrid(1:22);
inPoly1 = inpolygon(a,b,X,Y);
However this only returns true if if the center of the bin is contained in the polygon, ie it returns the red shape in the image below. However what need is more along the lines of the green shape (although its still an incomplete solution).
To get the green blob I performed four calls to inpolygon
. For each comparison I shifted the grid of points either NE, NW, SE, or SW by 1/2. This is equivalent to testing if the corners of a bin are in the polygon.
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);
While this does provide me with a partial solution it fails in the case when a vertex is contain in a bin but none of the bin corners are.
Is there a more direct way of attacking this problem, with preferably a solution that produces more readable code?
This plot was drawn with:
imagesc(inPoly1 + inPoly2); hold on;
line(a, b, 'w.');
line(X, Y, 'y);
One suggestion is to use the polybool function (not available in 2008b or earlier). It finds the intersection of two polygons and returns resulting vertices (or an empty vector if no vertices exist). To use it here, we iterate (using arrayfun) over all of the squares in your grid check to see whether the output argument to polybool is empty (e.g. no overlap).
Here is the resulting image:
Code for plotting:
Well, I guess I am late, though strictly speaking the bounty time was till tomorrow ;). But here goes my attempt. First, a function that marks cells that contain/touch a point. Given a structured grid with spacing lx, ly, and a set of points with coordinates (Xp, Yp), set containing cells:
Now, the rest of the code. For every segment in the polygon (you have to walk through the segments one by one), intersect the segment with the grid lines. Intersection is done carefully, for horizontal and vertical lines separately, using the given grid point coordinates to avoid numerical inaccuracies. For the found intersection points I call mark_cells to mark the surrounding cells to 1:
Output for the example mesh/segment:
Walking through all polygon segments gives you the polygon 'halo'. Finally, the interior of the polygon is obtained using standard inpolygon function. Let me know if you need more details about the code.
I would suggest using
poly2mask
in the Image Processing Toolbox, it does more or less what you want, I think, and also more or less what youself and Salain has suggested.How about this pseudocode algorithm:
Note: This line will take a tiny bit of effort to implement, but I think there is a fairly straightforward, well-known algorithm for it.
Also, if I was using .NET, I would simply define a rectangle corresponding to each grid tile, and then see which ones intersect the polygon. I don't know if checking intersection is easy in Matlab, however.
Slight improvement
Firstly, to simplify your "partial solution" - what you're doing is just looking at the corners. If instead of considering the 22x22 grid of points, you could consider the 23x23 grid of corners (which will be offset from the smaller grid by (-0.5, -0.5). Once you have that, you can mark the points on the 22x22 grid that have at least one corner in the polygon.
Full solution:
However, what you're really looking for is whether the polygon intersects with the 1x1 box surrounding each pixel. This doesn't necessarily include any of the corners, but it does require that the polygon intersects one of the four sides of the box.
One way you could find the pixels where the polygon intersects with the containing box is with the following algorithm:
So, for example, say we're looking at
pA = (1, 1)
andpB = (2, 3)
.y = 1.5
andy = 2.5
(pixel edges are half-offset from pixelspA
->pB
intersects with our edges. This gives us:x = 1.25, y = 1.5
, andx = 1.75, y = 2.5
.x = 1.25
is rounded to 1 (for the edgey = 1.5
). We therefore can mark the pixels at(1, 1)
and(1, 2)
as part of our set.x = 1.75
is rounded to 2 (for the edgey = 2.5
). We therefore can mark the pixels at(2, 2)
and(2, 3)
.So that's the horizontal edges taken care of. Next, let's look at the vertical ones:
x = 1.5
.pA
->pB
. This gives usx = 1.5, y = 2
.y = 2
is rounded to 2. We therefore can mark the pixels at(1, 2)
and(2, 2)
.Done!
Well, sort of. This will give you the edges, but it won't fill in the body of the polygon. However, you can just combine these with your previous (red) results to get the complete set.
First I define a low resolution circle for this example
Like your example it fits with in a grid of ~22 units. Then, following your lead, we declare a meshgrid and check if points are in the polygon.
Only difference is that where your original solution took steps of one, this grid can take smaller steps. And finally, to include anything within the "edges" of the squares
The
round
simply takes our high resolution grid and rounds the points appropriately to their nearest low resolution equivalents. We then remove all of the duplicates in a vector style or pair-wise fashion by callingunique
with the'rows'
qualifier. And that's itTo view the result,
Changing the
stepSize
has the expected effect of improving the result at the cost of speed and memory.If you need the result to be in the same format as the inPoly2 in your example, you can use
Hope that helps. I can think of some other ways to go about it, but this seems like the most straightforward.