Apply operations to a cell array where each cell is a polygon - matlab

I have some problems that require me to manipulate polygons using operations such as translating, dilating, rotating, and shearing. The data I have is actually on state boundaries and geometries from data.gov on the state of Delaware. The function delaware.m returns a cell array (1x3 cell) of polygon matrices describing the shape of the state of Delaware, and this is the shape I need to do operations on. I will post the specific questions so you can get a sense of what I'm being asked of, but I'm still asking for more general guidance than a specific answer to each question.
Translate the state of Delaware so that its center is approximately at the origin.
Dilate the translated state of Delaware so that it fits inside a square of side length one centered at the origin.
Rotate the translated, dilated state of Delaware so that New Castle County is at the bottom and Sussex is at the top.
Dilate the translated, dilated, rotated state of Delaware without changing its area, so that it is about as wide as it is tall.
Shear the translated, dilated, rotated, dilated state of Delaware the northernmost tip is at least 2 units to the right of the southernmost tip.
The thing is, I know how to do all these operations in Matlab with just a single polygon/matrix. I am mostly struggling with how to use this with the cell array.
For example, say I have matrix S.
newS=S+[1;2]; %move S one unit to the right and two units up
R=[sqrt(2)/2 -sqrt(2)/2; sqrt(2)/2 sqrt(2)/2];
newS=R*S %rotate the polygon by 45 degrees
D = [alpha 0; 0 beta];
%alpha is the dilation scaling the x direction and beta in the y direction
%left multiply S by this dilation matrix to dilate along the cardinal axes
Sh=[1 y; 0 1] %y controls the amount of shearing
%left multiply by S to shear a shape along the x-axis relative to the y-axis
So for example, when I try to do an operation for moving the shape up/down/left/right as I described above for the cell array, I get the error message
Undefined operator '+' for input arguments of type 'cell'.
I also tried:
DEBoundary1 = cellfun(#sum, DEBoundary, [75.562;-39.6]);
%this is how much I wanted to move the polygons
But got:
>> Lab_code
Error using cellfun
All of the input arguments must be of the same size and shape.
Previous inputs had size 1 in dimension 1. Input #3 has size 2
I suppose in general, is there an easy way to take these operations I already know and apply them to a cell array consisting of polygon matrices? Or do I have to go about it a different way?

I believe this is what you're trying to do with your + example:
DEBoundary = {[0 1 -1 0; 1 -1 -1 1], [0 -1 1 0; 1 1 1 1]};
offset = [3;-2];
DEBoundary1 = cellfun(#(c) c + offset, DEBoundary, 'UniformOutput', false)
What this does is:
cellfun(#(c) % c is each element in the cell
c + offset % add the offset to each element
, DEB % The cell array to operate on
'UniformOutput', 0) % Specifies that the output is a cell and not a scalar
Try it online!
If you thing cellfun is confusing, then you may do this manually:
DEBoundary1 = cell(size(DEBoundary))
for i = 1:numel(DEBoundary)
DEBoundary1{i} = DEBoundary{i} + offset;
end
This should work with multiplication and other operations as well, as long as the dimensions match (but that's a mathematical question, not MATLAB specific).

Related

How does MATLAB's `regionprops` function computes perimeter?

I have found that the function uses chain codes to encode the boundary of the shape, and then computes the perimeter this way using this formula:
perimeter = sum(isEven)*0.980 + sum(~isEven)*1.406 - sum(isCorner)*0.091;
What I do not know, however, is how this chain code is computed in some special cases.
Consider the following example (for which MATLAB yields a perimeter of 10.0150):
1 1 1 1 1
1 1 0 0 1
How does MATLAB defines/computes the perimeter around the one pixel wide line attached to the 2x2 square on the left?
More precisely:
If I denote non-zero border pixels using letters (all 1s are borders in this example):
a d e f g
b c h
A chain code could start at, say, a. If we compute it clockwise, it would then continue at c, e, f... Which means it cannot come back to a, otherwise it would have to go twice over the same letter (even more than twice if there are 1px wide lines attached to other 1px wide lines etc).
There are two steps in obtaining chain codes: tracing the boundary and encoding the coordinates as chain codes. This latter step is trivial, I won't go into details. Tracing the boundary is what I think this question is about.
Typically what is traced are the object pixels that form the boundary (i.e. have at least one background neighbor). It is important that this happen in order, just listing these pixels is not enough. But do note that this description of the boundary is biased: the true object is larger than the polygon formed by joining the centers of the pixels at the object boundary. A perimeter computation needs to take this into account (as discussed in the blog post you linked).
This code is adapted slightly from this blog post. img is a logical array:
% Data for chain code encoding:
directions = [ 1, 0
1,-1
0,-1
-1,-1
-1, 0
-1, 1
0, 1
1, 1];
% Get a start point, any pixel on the boundary is OK:
indx = find(img,1)-1; % 0-based indexing is easier
% Image sizes
sz = [size(img,2),size(img,1)]; % x,y sizes, rather than y,x sizes
% Coordinates for start point
start = [floor(indx/sz(2)),0];
start(2) = indx-(start(1)*sz(2));
% Initialize algorithm
cc = []; % The chain code
coord = start; % Coordinates of the current pixel
dir = 1; % The starting direction
% Loop till full boundary is traced
while 1
newcoord = coord + directions(dir+1,:);
if all(newcoord>=0) && all(newcoord<sz) ...
&& img(newcoord(2)+1,newcoord(1)+1)
cc = [cc,dir];
coord = newcoord;
dir = mod(dir+2,8);
else
dir = mod(dir-1,8);
end
if all(coord==start) && dir==1 % back to starting situation
break;
end
end
As you can see here, the algorithm starts at a random pixel, and picks a direction to go around. Then it follows the boundary by finding the next neighbor in the given direction. The linked blog post has details explaining how this neighbor is found. In short, you look in the current direction, for the first neighboring object pixel that has a background neighbor. Given the current location and direction we came from, it is provable that the neighbor in a specific direction will be a background pixel. Going in clockwise (or anticlockwise, pick one) direction around the current point, starting with that background pixel, the first object pixel will guaranteed be a boundary pixel. We add that to out list and continue.
The algorithm terminates when we reach the start position and direction. The 1-pixel thick section of an object will thus be visited twice, to complete the boundary trace.

Find coordinates from a contour in Matlab

I suppose this is not something difficult but i wonder if there is any function or any optimal way.
Consider that after an image process i have a matrix-image with 0 everywhere and 1 at the contour.
Now i want to find the x y along that contour line
The important is that eg [ x(2) y(2) ] should be the next pixel to [x(1) y(1)]
I have used this:
[c h]=contour(image,1)
x=c(1,:)
y=c(2,:)
But the result is not very good and it gives some noise points which is very bad (and for some reason it appears mirrored)
If you have the image processing toolbox, I highly suggest using bwperim which returns a logical image where true is a perimeter or contour point and false otherwise.... not to mention that it's faster.
Try doing:
bw = bwperim(image == 1); % image == 1 to ensure binary
[y, x] = find(bw);
The first line of code finds an image that only contains contour points, and we can use find after that on the result to find the row and column locations. Here y represents the row and x represents the column locations.
If you desire that the contour is ordered, then use the bwtraceboundary function instead. However, this will require that you specify an initial contour point prior to running the function.
An easy way to do this would be to find any non-zero point along the contour of your object. You could use bwperim first and sample any point from here. Choosing just any point may not give you an actual contour point.
Therefore:
bw = bwperim(image == 1); % image == 1 to ensure binary
[y, x] = find(bw, 1); % Find the first contour point
ctr = bwtraceboundary(image == 1, [y, x], 'SE');
'SE' is the direction of where to look first given the initial contour point. Here I've chosen south east. This will produce a N x 2 matrix where the first column contains the rows and second column contains the columns of the ordered contour points starting at the initial position provided by y and x.
I have posted my complete solution to help other people:
Problem:
I have a grayscale image and i want to find the coordinates X Y in order along the contour .
Solution:
Set a threshold for black and white and make the image binary (optional)
`image=image>0.5 %This is optional but some may found it usefull`
Find the Start Point:
[yStart xStart]=find(image,1);
This will scan the image column by column from left to right and up to down and will return the first non zero pixel. So this will return the 'left-est up' pixel of the image. Remember, X is the column and Y is the row. Zero is at the top-left corner!
Find the contour:
contour=bwtraceboundary(image, [yStart, xStart],'NE');
or
contour = bwtraceboundary(image,[yStart xStart],'NE',8,Inf,'clockwise');
NE is the initial direction (NorthEast)
x=contour(:,2)
y=contour(:,1)
If the point [yStart xStart] is not on the contour of an image this will not work !
If you plot (x,y) that will be mirrored. This is because the zero at the coordinate system is at the top left corner of the image and not at the bottom left. To do it properly you can do this:
y=-y
y=y+abs(min(y))+1 % +1 is to avoid y=0

Swap frames on Matlab [duplicate]

I have two images which one of them is the Original image and the second one is Transformed image.
I have to find out how many degrees Transformed image was rotated using 3x3 transformation matrix. Plus, I need to find how far translated from origin.
Both images are grayscaled and held in matrix variables. Their sizes are same [350 500].
I have found a few lecture notes like this.
Lecture notes say that I should use the following matrix formula for rotation:
For translation matrix the formula is given:
Everything is good. But there are two problems:
I could not imagine how to implement the formulas using MATLAB.
The formulas are shaped to find x',y' values but I already have got x,x',y,y' values. I need to find rotation angle (theta) and tx and ty.
I want to know the equivailence of x, x', y, y' in the the matrix.
I have got the following code:
rotationMatrix = [ cos(theta) sin(theta) 0 ; ...
-sin(theta) cos(theta) 0 ; ...
0 0 1];
translationMatrix = [ 1 0 tx; ...
0 1 ty; ...
0 0 1];
But as you can see, tx, ty, theta variables are not defined before used. How can I calculate theta, tx and ty?
PS: It is forbidden to use Image Processing Toolbox functions.
This is essentially a homography recovery problem. What you are doing is given co-ordinates in one image and the corresponding co-ordinates in the other image, you are trying to recover the combined translation and rotation matrix that was used to warp the points from the one image to the other.
You can essentially combine the rotation and translation into a single matrix by multiplying the two matrices together. Multiplying is simply compositing the two operations together. You would this get:
H = [cos(theta) -sin(theta) tx]
[sin(theta) cos(theta) ty]
[ 0 0 1]
The idea behind this is to find the parameters by minimizing the error through least squares between each pair of points.
Basically, what you want to find is the following relationship:
xi_after = H*xi_before
H is the combined rotation and translation matrix required to map the co-ordinates from the one image to the other. H is also a 3 x 3 matrix, and knowing that the lower right entry (row 3, column 3) is 1, it makes things easier. Also, assuming that your points are in the augmented co-ordinate system, we essentially want to find this relationship for each pair of co-ordinates from the first image (x_i, y_i) to the other (x_i', y_i'):
[p_i*x_i'] [h11 h12 h13] [x_i]
[p_i*y_i'] = [h21 h22 h23] * [y_i]
[ p_i ] [h31 h32 1 ] [ 1 ]
The scale of p_i is to account for homography scaling and vanishing points. Let's perform a matrix-vector multiplication of this equation. We can ignore the 3rd element as it isn't useful to us (for now):
p_i*x_i' = h11*x_i + h12*y_i + h13
p_i*y_i' = h21*x_i + h22*y_i + h23
Now let's take a look at the 3rd element. We know that p_i = h31*x_i + h32*y_i + 1. As such, substituting p_i into each of the equations, and rearranging to solve for x_i' and y_i', we thus get:
x_i' = h11*x_i + h12*y_i + h13 - h31*x_i*x_i' - h32*y_i*x_i'
y_i' = h21*x_i + h22*y_i + h23 - h31*x_i*y_i' - h32*y_i*y_i'
What you have here now are two equations for each unique pair of points. What we can do now is build an over-determined system of equations. Take each pair and build two equations out of them. You will then put it into matrix form, i.e.:
Ah = b
A would be a matrix of coefficients that were built from each set of equations using the co-ordinates from the first image, b would be each pair of points for the second image and h would be the parameters you are solving for. Ultimately, you are finally solving this linear system of equations reformulated in matrix form:
You would solve for the vector h which can be performed through least squares. In MATLAB, you can do this via:
h = A \ b;
A sidenote for you: If the movement between images is truly just a rotation and translation, then h31 and h32 will both be zero after we solve for the parameters. However, I always like to be thorough and so I will solve for h31 and h32 anyway.
NB: This method will only work if you have at least 4 unique pairs of points. Because there are 8 parameters to solve for, and there are 2 equations per point, A must have at least a rank of 8 in order for the system to be consistent (if you want to throw in some linear algebra terminology in the loop). You will not be able to solve this problem if you have less than 4 points.
If you want some MATLAB code, let's assume that your points are stored in sourcePoints and targetPoints. sourcePoints are from the first image and targetPoints are for the second image. Obviously, there should be the same number of points between both images. It is assumed that both sourcePoints and targetPoints are stored as M x 2 matrices. The first columns contain your x co-ordinates while the second columns contain your y co-ordinates.
numPoints = size(sourcePoints, 1);
%// Cast data to double to be sure
sourcePoints = double(sourcePoints);
targetPoints = double(targetPoints);
%//Extract relevant data
xSource = sourcePoints(:,1);
ySource = sourcePoints(:,2);
xTarget = targetPoints(:,1);
yTarget = targetPoints(:,2);
%//Create helper vectors
vec0 = zeros(numPoints, 1);
vec1 = ones(numPoints, 1);
xSourcexTarget = -xSource.*xTarget;
ySourcexTarget = -ySource.*xTarget;
xSourceyTarget = -xSource.*yTarget;
ySourceyTarget = -ySource.*yTarget;
%//Build matrix
A = [xSource ySource vec1 vec0 vec0 vec0 xSourcexTarget ySourcexTarget; ...
vec0 vec0 vec0 xSource ySource vec1 xSourceyTarget ySourceyTarget];
%//Build RHS vector
b = [xTarget; yTarget];
%//Solve homography by least squares
h = A \ b;
%// Reshape to a 3 x 3 matrix (optional)
%// Must transpose as reshape is performed
%// in column major format
h(9) = 1; %// Add in that h33 is 1 before we reshape
hmatrix = reshape(h, 3, 3)';
Once you are finished, you have a combined rotation and translation matrix. If you want the x and y translations, simply pick off column 3, rows 1 and 2 in hmatrix. However, we can also work with the vector of h itself, and so h13 would be element 3, and h23 would be element number 6. If you want the angle of rotation, simply take the appropriate inverse trigonometric function to rows 1, 2 and columns 1, 2. For the h vector, this would be elements 1, 2, 4 and 5. There will be a bit of inconsistency depending on which elements you choose as this was solved by least squares. One way to get a good overall angle would perhaps be to find the angles of all 4 elements then do some sort of average. Either way, this is a good starting point.
References
I learned about homography a while ago through Leow Wee Kheng's Computer Vision course. What I have told you is based on his slides: http://www.comp.nus.edu.sg/~cs4243/lecture/camera.pdf. Take a look at slides 30-32 if you want to know where I pulled this material from. However, the MATLAB code I wrote myself :)

Find the index of pixels in an image statisfies one condition

I have an image include 4 values {3,-3,1,-1} as figure
Let call the index of pixel that its values equals 1 or -1 pixel in contour. These pixels will create a contour that surrounds the yellow color (-3). Now, I want to find all index pixels in the contour and plus padding position inward and outward contour. As the red color, padding is set 1, hence, the index of these pixels include pixel in the contour {1,-1} and padding index as the red color. In that task, I want to find all pixel indices. How to implement that idea in matlab code. This is my code to find the index in the contour
%% Let define the image I
idx=find(I==1|I==-1);
padding=1;
%%Continue
Update: My expected result as the above figure in white region. Hence, the indices are such as 13,14,15,..21,24,...
UPDATE
Firstly, thank Andrew and Rayryeng for your answer. I would like to extedn my problem. As the above description, the contour is created by {1,-1}. Now, I want to ignore 1 and -1, so the image I only has {3,-3}. And I defined the contour is pixel in the edge of {3,-3} such as figure. Keep the same idea of padding and pixel index. How to find the indices of pixels in contour and near contour (call narrow band of contour)(expected result is white color)
Not too difficult you are on the right track. If you have the image processing toolbox, I recommend taking a look at morphological operators. Specifically you want to use imdilate my code has all the details you need.
%rather than using find, we create a binary mask. Its not the indicies of
%the matching elements as find gives. its is 1/true if the value matches the
%criteria, and 0/false otherwise.
mask = (im=1 | im=-1);
%create a 3x3 rectangle structuring element. We use a 3x3 because we want
%to expand the image by one pixel. basically the structring element (Strel)
%is our kernal, if you know image processing this is the same thing.
%a = [0 0 0 0;
% 0 1 1 1;
% 0 1 1 1;
% 0 1 1 1];
%our kernal is center at 2,2 (for this example) which are these elements
% 0 0 0 of a think a(1:3,1:3) now what the dialate operation
% 0 1 1 says is, if the majority of these pixels are ones... they
% 0 1 1 should probabaly all be ones so all those 0s will become ones
%the size of the kernal 3x3 ensures we are only growing our image one
%pixel, hope that makes sense
se = strel('square',3);
%now we dilate, or 'expand' our mask with our structuring element
expanded_mask = imdilate(mask,se);
%if you still want the indicies you can use find on our expanded mask
idx = find(expanded_mask==1);
EDIT: without morphological operations/image processing toolbox
This method uses lots of for loops, so it isn't the fastest, and doens't do error checking, but it will work. My dilate function says if the majority of the pixels are ones make them all ones.
function expanded_mask=DilateBinaryImage(bin_im, kernal_size)
[max_row,max_col] = size(bin_im);
%since we are opening the mask (only adding 1s), we can start off with the
%same values of the mask, and simply add extra 1's as needed
expanded_mask = bin_im;
%we don't want to go off the edge of our image with this kernal
%so we offset it a bit
kern_padding = floor(kernal_size/2);
%this ignores the edges
for (curr_row=kern_padding+1:1:max_row - kern_padding)
for (curr_col=kern_padding+1:1:max_col - kern_padding)
%we do 2 sums, one for rows, one for columns
num_ones = sum(sum(bin_im(curr_row-kern_padding:curr_row+kern_padding,curr_col-kern_padding:curr_col+kern_padding)));
%if the majority of vlaues are 1, we use floor to help with corner
%cases
if (num_ones >= floor((kernal_size*kernal_size)/2))
%make all the values one
expanded_mask(curr_row-kern_padding:curr_row+kern_padding,curr_col-kern_padding:curr_col+kern_padding) = 1;
end
end
end
end
and then called it like this
kernal_size= 3;
mask = (I==1 | I==-1);
expanded_mask = DilateBinaryImage(mask, kernal_size);
idx = find(expanded_mask==1);
my dilate function doesn't work at the edges of the binary image. it just copies them exactly.
Lets say your image is N-by-M pixles. In MATLAB arrays are stored in column order (see http://www.mathworks.com/help/matlab/math/matrix-indexing.html for more information). You can use I in column format as follows. First you contour pixels are given by
idx=find(I(:)==1|I(:)==-1);
Now, if you wish to pad downward and upward it is quite simple:
idx_up=idx - padding;
idx_up = idx_up(idx_up>0);
idx_down=idx + padding;
idx_down = idx_down(idx_down<=N*M);
Note that idx_up and idx_down will also contain contour pixels.
Similarly you can pad to the left\right:
idx_left=idx - padding*N;
idx_left = idx_left(idx_left>0);
idx_right=idx + padding*N;
idx_right = idx_right(idx_right<=N*M);
And combine the overall pixels:
PaddedContour = false(N,M);
PaddedContour(unique([idx;idx_up;idx_down;idx_left;idx_right])) = true;

What is an simple way to compute the overlap between an image and a polygon?

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).
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);
Here is the resulting image:
Code for plotting:
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;
How about this pseudocode algorithm:
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
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.
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.
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:
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
So, for example, say we're looking at pA = (1, 1) and pB = (2, 3).
First, we calculated the rounded Y-values: 1 and 3.
Then, we look at the pixel edges between these values: y = 1.5 and y = 2.5 (pixel edges are half-offset from pixels
For each of these, we solve the linear equation to find where pA->pB intersects with our edges. This gives us: x = 1.25, y = 1.5, and x = 1.75, y = 2.5.
For each of these intersections, we take the rounded X-value, and use it to mark the pixels either side of the edge.
x = 1.25 is rounded to 1 (for the edge y = 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 edge y = 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:
First we calculate the rounded X-values: 1 and 2
Then, we look at the pixel edges. Here, there is only one: x = 1.5.
For this edge, we find the where it meets the line pA->pB. This gives us x = 1.5, y = 2.
For this intersection, we take the rounded Y-value, and use it to mark pixels either side of the edge:
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
X=11+cos(linspace(0,2*pi,10))*5;
Y=11+sin(linspace(0,2.01*pi,10))*5;
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.
stepSize=0.1;
[a b] = meshgrid(1:stepSize:22);
inPoly1 = inpolygon(a,b,X,Y);
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
inPolyFull=unique( round([a(inPoly1) b(inPoly1)]) ,'rows');
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 calling unique with the 'rows' qualifier. And that's it
To view the result,
[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;
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
inPoly2=zeros(22);
inPoly2(inPolyFull(:,1),inPolyFull(:,2))=1
Hope that helps. I can think of some other ways to go about it, but this seems like the most straightforward.
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:
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
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:
% 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 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.