views:

151

answers:

3

For instance, if I have got a vector describing a rectangle

xy=[165  88; 
    401  88; 
    401 278; 
    165 278];

on an image.

How can I obtain the following vector

[165  88; % increase X -     hold y
 166  88; 
 167  88;
   ...  ;
 399  88;
 400  88;
 401  88; % hold     x - increase y
 401  89;
 401  90;
 401  91;
   ...  ;
 401 276;
 401 277;
 401 278; % decrease X -     hold y
 400 278;
 399 278;
 398 278;
   ...  ;
 167 278;
 166 278;
 165 278; % hold     x - decrease y
 165 277;
 165 276;
   ...  ;
 165  87];

using a MATLAB inbuilt function or do I need to write it using FOR LOOPS?

The algorithm must work for a generic vector with n-points and xy coordinates.

A: 

One solution using IND2SUB:

xy=[165 88; 401 88; 401 278; 165 278];
xmin = min(xy(:,1))-1;
xmax = max(xy(:,1));
ymin = min(xy(:,2))-1;
ymax = max(xy(:,2));

ncol=xmax-xmin;
nrow=ymax-ymin;

[xn yn]=ind2sub([nrow ncol],1:nrow*ncol);
xypairs = [xn'+xmin yn'+ymin];
yuk
But i want only the perimeter and the generic vector xy can describe a polygon, not only a rectangle.
EnneKappa
Sorry, didn't get the final sentence. You will need to loop through every edge of a polygon (just n loops) and calculate coordinates by linear regression.
yuk
A: 

The quick and dirty way to draw a straights into an off-screen matrix is by evaluating the formula a*X+b*Y=c.

Let h and w be width and height of your buffer:

X = repmat([0:w-1], h, 1)
Y = repmat([0:h-1]', 1, w)

For every pair of points (x1,y1)->(x2,y2) a, b and c are:

a = y2-y1
b = x1-x2
c = x1*y2-x2*y1

Now calculating the straigt:

st = a*X+b*Y-c
st(abs(st)>1) = 1
st = 1 - abs(st)

Matrix st is a w*h matrix containing an anti-aliased straight passing through the points (x1,y1) and (x2,y2). Now let's go from straight to line by masking out the unwanted parts:

[xs] = sort([x1 x2])
st = st .* [zeros(h, xs(1)) ones(h, xs(2)-xs(1)) zeros(h, w-xs(2))]
[ys] = sort([y1 y2])
st = st .* [zeros(ys(1), w) ; ones(ys(2)-ys(1), w) ; zeros(h-ys(2), w)]

We have just manually drawn a single line without any explicit looping. No guarantees about the code's efficiency though :-)

Finally: Add another dimension to every formula above (left as an exercise for the reader).

edgar.holleis
+3  A: 

If you have the image processing toolbox, you can do this by creating an image of the polygon followed by finding the contour:

xy=[165 88; 401 88; 401 278; 165 278];
%# create the image - check the help for impolygon for how to make sure that
%# your line is inside the pixel
img = poly2mask(xy(:,1),xy(:,2),max(xy(:,1))+3,max(xy(:,2))+3);
figure,imshow(img) %# show the image

%# extract the perimeter. Note that you have to inverse x and y, and that I had to
%# add 1 to hit the rectangle - this shows one has to be careful with rectangular 
%# polygons
boundary = bwtraceboundary(logical(img),xy(1,[2,1])+1,'n',8,inf,'clockwise');

%# overlay extracted boundary
hold on, plot(boundary(:,2),boundary(:,1),'.r')

Edited to show how to use bwtraceboundary and to warn of pixel offset with rectangles.

Jonas
Excellent! The unique problem is the sorting of "out".Adjacent element of "out" must be adjacent point of the 2d curve :'(
EnneKappa
Then you should use bwtraceboundary instead of bwperim
Jonas
Thanks!!!boundary = bwtraceboundary(img,[xy(1,2)+1 xy(1,1)+1],'N');
EnneKappa
Why don't this work with concave polygon?
EnneKappa
Works with boundary=bwboundaries(img);
EnneKappa