Getting the points of a certain plot in matlab - matlab

If I have points in a graph in matlab that are randomly sorted and when plotted produce various closed shapes. Given a specific point on the left side of one of the closed shapes how can I get all the points of that shape in a vector form keeping in mind the convention of collecting the points is moving in a clockwise direction.

A commented example:
cla
% Random data
x = rand(15,1);
y = rand(15,1);
scatter(x,y)
% Random point to the left
hold on
p = [-0.1, rand(1)*0.2 + 0.5];
plot(p(1),p(2),'*r')
% Separate points that lie above your point
idx = y >= p(2);
% Sort x then y increasing for upper half and x then y decreasing for lower half
shape = [sortrows([x( idx) y( idx)],[1 2])
sortrows([x(~idx) y(~idx)],[-1 -2])];
Check that shape contains clockwise sorted coordinates by plotting an open line:
% Plot clockwise open line
plot(shape(1:end ,1),shape(1:end,2),'k')

Related

mean value in a sphere

I'm trying to calculate the mean value of the pixels inside a circle. In the future this needs to be extended to 3D, but for now a 2D sollution would already help me out.
As can be seen in the image, some pixels are entirely inside the circle, but some are only partly inside the circle. The ones partly in the circle also need to contribute only partly to the mean value. The pixels are square. This will simplify the mathematics I hope.
I can calculate the distance from the pixelcorners to the central point, from this you can find the pixels enterly inside and enterly outside. The rest needs correction. But how to find this correction.
[edit] thanks to Heath Raftery the problem is solved! [/edit]
the integral of a circle with radius r
As an example: I want to know the average pixelvalue of pixels in this circle. I know it is 0.3425, since 34.25% of the circle has a value of 1 and the rest is 0.
Function to check what part of a pixel is in the circle:
function [ a ] = incirc( x,y,r )
%only handles the top right quadrant of a circle
if x<0||y<0,error('only positive x,y');end
%integral of sqrt(r^2-x^2) dx
F = #(x,r) (1/2)*(x*sqrt(r^2-x^2)+r^2*atan(x/sqrt(r^2-x^2)));
%find corner locations
x=[x-0.5,x+0.5];
y=[y-0.5,y+0.5];
d = sqrt(x.^2+y.^2); %distance to closed and furthest corner
if max(d)<r,a=1;return;end %inside circle
if min(d)>r,a=0;return;end %outside circle
%intersections with edges (r^2 = x^2+y^2)
inters = [sqrt(r^2-y(1)^2),sqrt(r^2-y(2)^2),sqrt(r^2-x(1)^2),sqrt(r^2-x(2)^2)]; %x(1) x(2) y(1) y(2)
%remove imaginary and out of range intersections
inters(imag(inters)~=0)=NaN;
inters(inters<1E-5)=NaN; %to find values that are zero
inters([~((x(1)<inters(1:2))&(inters(1:2)<x(2))),~((y(1)<inters(3:4))&(inters(3:4)<y(2)))])=NaN;
idx = find(~isnan(inters));
if numel(idx)~=2,error('need two intersections of circle with pixel');end
%check area of pixel inside circumference
if all(idx==[1,2]) %2 intersections on y-edge
a=(F(y(2),r)-F(y(1),r)) - x(1); %area
elseif all(idx==[3,4]) %2 intersections on x-edge
a=(F(x(2),r)-F(x(1),r)) - y(1); %area
elseif all(idx==[1,3]) %one intersection on y-edge one on x-edge (left&bottom)
a=(F(inters(1),r)-F(x(1),r))- (y(1)*(inters(1)-x(1)));
elseif all(idx==[2,4]) %one intersection on y-edge one on x-edge (top&right)
a=(inters(2)-x(1))+(F(x(2),r)-F(inters(2),r))-(y(1)*(x(2)-inters(2)));
else
error('geometry')
end
a=real(a);
if a<0||a>1
error('computational error');
end
end
Script to test the function
M = ones(100); %data
M(1:50,:)=0;
pos=[50.2,50];
r = 2;
%calculate what the result should be
h=50-pos(2)+0.5;
A=pi*r^2;
wedge = acos(h/r)/pi;
triangle = h*sqrt(r^2-h^2);
res=(A*wedge-triangle)/A
S=0;N=0;
for i = 1:size(M,1)
for j = 1:size(M,2)
x=abs(j-pos(1));
y=abs(i-pos(2));
n=incirc( x,y,r );
M_(i,j)=n;
S = S+M(i,j)*n;
N = N+n;
end
end
result = S/N
result = 0.3425
You can see the algorithm finds the part of the pixel in the circle.
The question is missing a question, but I'll assume that it's not how to calculate whether pixels are fully inside or outside the circle. That's a relatively simple task. That is, a pixel is fully inside if the furtherest corner of the pixel to the centre is less than a radius away from the centre, and a pixel is fully outside if the closest corner of the pixel to the centre is more than a radius away from the centre.
The question of what proportion of pixels on the circumference fall within the circumference is much trickier. There are two fundamental solutions:
Exact and hard.
Approximate and a bit easier.
In both cases, note the horizontal and vertical symmetry means only the top right quadrant need be considered.
Then, for (1), translate the circle centre to the origin (0, 0) and treat the circumference as the function y(x) = sqrt(r^2 - x^2). Then, the area of an overlapping pixel within the circle is the integral:
integral(y(x) - y0, from x0 to x1, with respect to x)
where y0 is the bottom coordinate of the pixel, x0 is the left coordinate and x1 is the right coordinate.
This integral can be solved exactly with a trigonometric identity and a trigonometric substitution.
For (2), just generate a set of random points within the pixel and count how many of them fall within the circumference. As the set gets larger, the proportion of points that fall within the circumference to the count of all point approaches the proportion of the pixel within the circumference.
You can use inpolygon, to get the indices which lie inside the circle, once you have those indices you can get your pixels and do what you want.
M = rand(100); %data
[nx,ny] = size(M) ;
[X,Y] = meshgrid(1:ny,1:nx) ;
pos=[20,20];
r = 5;
phi=linspace(0,2*pi,100);
imagesc(M);
axis image
hold on
plot(pos(1),pos(2),'rx')
xc = pos(1)+r*sin(phi) ;
yc = pos(2)+r*cos(phi) ;
plot(xc,yc,'-r');
% hold off
%% get indices which are inside the circle
idx = inpolygon(X(:),Y(:),xc,yc) ;
xi = X(idx) ; yi = Y(idx) ;
plot(xi,yi,'.r')
mypixels = M(idx) ;
You can also use rangesearch to get the points lying within the given radius of the circle. As below:
M = rand(100); %data
[nx,ny] = size(M) ;
[X,Y] = meshgrid(1:ny,1:nx) ;
pos=[20,20];
r = 5;
phi=linspace(0,2*pi,100);
imagesc(M);
axis image
hold on
plot(pos(1),pos(2),'rx')
xc = pos(1)+r*sin(phi) ;
yc = pos(2)+r*cos(phi) ;
plot(xc,yc,'-r');
% hold off
%% Use nearest neighbour search
idx = rangesearch([X(:),Y(:)],pos,r) ;
xi = X(idx{1}) ; yi = Y(idx{1}) ;
plot(xi,yi,'.r')
mypixels = M(idx{1}) ;

How to generate random positions with distance between them?

Assume that I have a vector with 6 distance elements as
D = [10.5 44.8 30.01 37.2 23.4 49.1].
I'm trying to create random pair positions of a given distances, inside a 200 meters circle. Note that the distance D created by using (b - a).*rand(6,1) + a, with a = 10 and b = 50 in Matlab. I do not know how to generate the random pairs with given the distances.
Could anybody help me in generating this kind of scenario?
This is an improvement to Alessiox's answer. It follows same logic, first generate a set of points ([X1 Y1]) that have at least distance D from the main circle border, then generate the second set of points ([X2 Y2]) that have exact distance D from the first set.
cx = 50; cy = -50; cr = 200;
D = [10.5 44.8 30.01 37.2 23.4 49.1]';
n = numel(D);
R1 = rand(n, 1) .* (cr - D);
T1 = rand(n, 1) * 2 * pi;
X1 = cx+R1.*cos(T1);
Y1 = cy+R1.*sin(T1);
T2 = rand(n, 1) * 2 * pi;
X2 = X1+D.*cos(T2);
Y2 = Y1+D.*sin(T2);
You can tackle the problem using a two-steps approach. You can
randomly generate the first point and then you can consider the circle whose center is that point and whose radius is the distance in D
once you did draw the circle, every point lying on that circle will have distance D from the first point previously created and by random selection of one of these candidates you'll have the second point
Let's see with an example: let's say you main circle has radius 200 and its center is (0,0) so we start by declaring some main variables.
MAINCENTER_x=0;
MAINCENTER_y=0;
MAINRADIUS=200;
Let's now consider the first distance, D(1)=10.5 and we now generate the first random point which (along with its paired point - I reckon you don't want one point inside and the other outside of the main circle) must lie inside the main circle
r=D(1); % let's concentrate on the first distance
while true
x=((MAINRADIUS-2*r) - (-MAINRADIUS+2*r))*rand(1,1) + (-MAINRADIUS+2*r);
y=((MAINRADIUS-2*r) - (-MAINRADIUS+2*r))*rand(1,1) + (-MAINRADIUS+2*r);
if x^2+y^2<=(MAINRADIUS-2*r)^2
break;
end
end
and at the end of this loop x and y will be our first point coordinates.
Now we shall generate all its neighbours, thus several candidates to be the second point in the pair. As said before, these candidates will be the points that lie on the circle whose center is (x,y) and whose radius is D(1)=10.5. We can create these candidates as follows:
% declare angular spacing
ang=0:0.01:2*pi;
% build neighbour points
xp=r*cos(ang)+x;
yp=r*sin(ang)+y;
Now xp and yp are two vectors that contain, respectively, the x-coordinates and y-coordinates of our candidates, thus we shall now randomly select one of these
% second point
idx=randsample(1:length(xp),1);
secondPoint_x=xp(idx);
secondPoint_y=yp(idx);
Finally, the pair (x,y) is the first point and the pair (secondPoint_x, secondPoint_y) is our second point. The following plot helps summarizing these steps:
The red circle is the main area (center in (0,0) and radius 200), the red asterisk is the first point (x,y) the blue little circle has center (x,y) and radius 10.5 and finally the black asterisk is the second point of the pair (secondPoint_x, secondPoint_y), randomly extracted amongst the candidates lying on the blue little circle.
You must certainly can repeat the same process for all elements in D or rely on the following code, which does the very same thing without iterating through all the elements in D.
MAINCENTER_x=0;
MAINCENTER_y=0;
MAINRADIUS=200;
D = [10.5 44.8 30.01 37.2 23.4 49.1];
% generate random point coordinates
while true
x=((MAINRADIUS-2*D) - (-MAINRADIUS+2*D)).*rand(1,6) + (-MAINRADIUS+2*D);
y=((MAINRADIUS-2*D) - (-MAINRADIUS+2*D)).*rand(1,6) + (-MAINRADIUS+2*D);
if all(x.^2+y.^2<=(MAINRADIUS-2*D).^2)
break;
end
end
% declare angular spacing
ang=0:0.01:2*pi;
% build neighbour points
xp=bsxfun(#plus, (D'*cos(ang)),x');
yp=bsxfun(#plus, (D'*sin(ang)),y');
% second points
idx=randsample(1:size(xp,2),length(D));
secondPoint_x=diag(xp(1:length(D),idx));
secondPoint_y=diag(yp(1:length(D),idx));
%plot
figure(1);
plot(MAINRADIUS*cos(ang)+MAINCENTER_x,MAINRADIUS*sin(ang)+MAINCENTER_y,'r'); %main circle
hold on; plot(xp',yp'); % neighbours circles
hold on; plot(x,y,'r*'); % first points (red asterisks)
hold on; plot(secondPoint_x,secondPoint_y,'k*'); %second points (black asterisks)
axis equal;
Now x and y (and secondPoint_x and secondPoint_y by extension) will be vector of length 6 (because 6 are the distances) in which the i-th element is the i-th x (or y) component for the first (or second) point.

How to create random points alongside a complex polyline?

I would like to populate random points on a 2D plot, in such a way that the points fall in proximity of a "C" shaped polyline.
I managed to accomplish this for a rather simple square shaped "C":
This is how I did it:
% Marker color
c = 'k'; % Black
% Red "C" polyline
xl = [8,2,2,8];
yl = [8,8,2,2];
plot(xl,yl,'r','LineWidth',2);
hold on;
% Axis settings
axis equal;
axis([0,10,0,10]);
set(gca,'xtick',[],'ytick',[]);
step = 0.05; % Affects point quantity
coeff = 0.9; % Affects point density
% Top Horizontal segment
x = 2:step:9.5;
y = 8 + coeff*randn(size(x));
scatter(x,y,'filled','MarkerFaceColor',c);
% Vertical segment
y = 1.5:step:8.5;
x = 2 + coeff*randn(size(y));
scatter(x,y,'filled','MarkerFaceColor',c);
% Bottom Horizontal segment
x = 2:step:9.5;
y = 2 + coeff*randn(size(x));
scatter(x,y,'filled','MarkerFaceColor',c);
hold off;
As you can see in the code, for each segment of the polyline I generate the scatter point coordinates artificially using randn.
For the previous example, splitting the polyline into segments and generating the points manually is fine. However, what if I wanted to experiment with a more sophisticated "C" shape like this one:
Note that with my current approach, when the geometric complexity of the polyline increases so does the coding effort.
Before going any further, is there a better approach for this problem?
A simpler approach, which generalizes to any polyline, is to run a loop over the segments. For each segment, r is its length, and m is the number of points to be placed along that segment (it closely corresponds to the prescribed step size, with slight deviation in case the step size does not evenly divide the length). Note that both x and y are subject to random perturbation.
for n = 1:numel(xl)-1
r = norm([xl(n)-xl(n+1), yl(n)-yl(n+1)]);
m = round(r/step) + 1;
x = linspace(xl(n), xl(n+1), m) + coeff*randn(1,m);
y = linspace(yl(n), yl(n+1), m) + coeff*randn(1,m);
scatter(x,y,'filled','MarkerFaceColor',c);
end
Output:
A more complex example, using coeff = 0.4; and xl = [8,4,2,2,6,8];
yl = [8,6,8,2,4,2];
If you think this point cloud is too thin near the endpoints, you can artifically lengthen the first and last segments before running the loop. But I don't see the need: it makes sense that the fuzzied curve is thinning out at the extremities.
With your original approach, two places with the same distance to a line can sampled with a different probability, especially at the corners where two lines meet. I tried to fix this rephrasing the random experiment. The random experiment my code does is: "Pick a random point. Accept it with a probability of normpdf(d)<rand where d is the distance to the next line". This is a rejection sampling strategy.
xl = [8,4,2,2,6,8];
yl = [8,6,8,2,4,2];
resolution=50;
points_to_sample=200;
step=.5;
sigma=.4; %lower value to get points closer to the line.
xmax=(max(xl)+2);
ymax=(max(yl)+2);
dist=zeros(xmax*resolution+1,ymax*resolution+1);
x=[];
y=[];
for n = 1:numel(xl)-1
r = norm([xl(n)-xl(n+1), yl(n)-yl(n+1)]);
m = round(r/step) + 1;
x = [x,round(linspace(xl(n)*resolution+1, xl(n+1)*resolution+1, m*resolution))];
y = [y,round(linspace(yl(n)*resolution+1, yl(n+1)*resolution+1, m*resolution))];
end
%dist contains the lines:
dist(sub2ind(size(dist),x,y))=1;
%dist contains the normalized distance of each rastered pixel to the line.
dist=bwdist(dist)/resolution;
pseudo_pdf=normpdf(dist,0,sigma);
%scale up to have acceptance rate of 1 for most likely pixels.
pseudo_pdf=pseudo_pdf/max(pseudo_pdf(:));
sampled_points=zeros(0,2);
while size(sampled_points,1)<points_to_sample
%sample a random point
sx=rand*xmax;
sy=rand*ymax;
%accept it if criteria based on normal distribution matches.
if pseudo_pdf(round(sx*resolution)+1,round(sy*resolution)+1)>rand
sampled_points(end+1,:)=[sx,sy];
end
end
plot(xl,yl,'r','LineWidth',2);
hold on
scatter(sampled_points(:,1),sampled_points(:,2),'filled');

How do I obtain intersection points between a line and a boundary in MATLAB?

I have a binary image of a human. In MATLAB, boundary points and the center of the image are also defined, and they are two column matrices. Now I want to draw lines from the center to the boundary points so that I can obtain all points of intersection between these lines and the boundary of the image. How can I do that? Here is the code I have so far:
The code that is written just to get the one intersection point if anyone can help please
clear all
close all
clc
BW = im2bw(imread('C:\fyc-90_1-100.png'));
BW = imfill(BW,'holes');
[Bw m n]=preprocess(BW);
[bord sk pr_sk]=border_skeleton(BW);
boundry=bord;
L = bwlabel(BW);
s = regionprops(L, 'centroid');
centroids = cat(1, s.Centroid);
Step #1 - Generating your line
The first thing you need to do is figure out how to draw your line. To make this simple, let's assume that the centre of the human body is stored as an array of cen = [x1 y1] as you have said. Now, supposing you click anywhere in your image, you get another point linePt = [x2 y2]. Let's assume that both the x and y co-ordinates are the horizontal and vertical components respectively. We can find the slope and intercept of this line, then create points between these two points parameterized by the slope and intercept to generate your line points. One thing I will point out is that if we draw a slope with a vertical line, by definition the slope would be infinity. As such, we need to place in a check to see if we have this situation. If we do, we assume that all of the x points are the same, while y varies. Once you have your slope and intercept, simply create points in between the line. You'll have to choose how many points you want along this line yourself as I have no idea about the resolution of your image, nor how big you want the line to be. We will then store this into a variable called linePoints where the first column consists of x values and the second column consists of y values. In other words:
In other words, do this:
%// Define number of points
numPoints = 1000;
%// Recall the equation of the line: y = mx + b, m = (y2-y1)/(x2-x1)
if abs(cen(1) - linePt(1)) < 0.00001 %// If x points are close
yPts = linspace(cen(2), linePt(2), numPoints); %// y points are the ones that vary
xPts = cen(1)*ones(numPoints, 1); %//Make x points the same to make vertical line
else %// Normal case
slp = (cen(2) - linePt(2)) / cen(1) - linePt(1)); %// Solve for slope (m)
icept = cen(2) - slp*cen(1); %// Solve for intercept (b)
xPts = linspace(cen(1), linePt(1), numPoints); %// Vary the x points
yPts = slp*xPts + icept; %// Solve for the y points
end
linePoints = [xPts(:) yPts(:)]; %// Create point matrix
Step #2 - Finding points of intersection
Supposing you have a 2D array of points [x y] where x denotes the horizontal co-ordinates and y denotes the vertical co-ordinates of your line. We can simply find the distance between all of these points in your boundary with all of your points on the line. Should any of the points be under a certain threshold (like 0.0001 for example), then this indicates an intersection. Note that due to the crux of floating point data, we can't check to see if the distance is 0 due to the step size in between each discrete point in your data.
I'm also going to assume border_skeleton returns points of the same format. This method works without specifying what the centroid is. As such, I don't need to use the centroids in the method I'm proposing. Also, I'm going to assume that your line points are stored in a matrix called linePoints that is of the same type that I just talked about.
In other words, do this:
numBoundaryPoints = size(boundry, 1); %// boundary is misspelled in your code BTW
ptsIntersect = []; %// Store points of intersection here
for idx = 1 : numBoundaryPoints %// For each boundary point...
%//Obtain the i'th boundary point
pt = boundry(:,idx);
%//Get distances - This computes the Euclidean distance
%//between the i'th boundary point and all points along your line
dists = sqrt(sum(bsxfun(#minus, linePoints, pt).^2, 2));
%//Figure out which points intersect and store
ptsIntersect = [ptsIntersect; linePoints(dists < 0.0001, :)];
end
In the end, ptsIntersect will store all of the points along the boundary that intersect with this line. Take note that I have made a lot of assumptions here because you haven't (or seem reluctant to) give any more details than what you've specified in your comments.
Good luck.

Calculate circular bins around a point + matlab

My question is related to this link stackoverflow ques
In essence repeating the figure drawn there .. I have a central point ( x , y ) in an image around which I have to draw 4 circles of 1-4 unit radius with 8 angles between them.
In this diagram there are 12 angle bins but I have 8. There is a code solution there but it is for plotting the above figure.
I want to calculate the maximum intensity point in each of the 4 regions of each wedge. Is there any inbuilt function in matlab ? I looked at rose but could'nt understand if it would help me....
I would greatly appreciate if someone could help me how to calculate it in matlab....
Thanks
I put some code below that should be the basic skeleton of what you want to do. But I left an important function unimplemented because I think you will be able to do it and it will help you understand this process better.
% I assume that data_points is an M-by-2 array, where each row corresponds
% to an (x,y) coordinate pair, and M is the number of data points.
data_points = ... ;
% I assume this array stores the intensities at each data point.
intensities = ... ;
% I assume that this stores the total number of gridded polar regions you want
% to find the max intensity in (i.e. 4*(number of cells) in your picture above).
total_num_bins = ... ;
% This will store the max intensities. For places that have no nearby
% data points, the max intensity will remain zero.
max_intensities = zeros(total_num_bins);
% I assume these store the values of the center point.
x = ... ; y = ... ;
% The number of different data points.
num_data_points = length(intensities); % also equals size(data_points,1)
% Now, loop through the data points, decide which polar bin they fall in, and
% update the max intensity of that area if needed.
for ii = 1:num_data_points
% Grab the current point coordinates.
cur_x = data_points[ii,1];
cur_y = data_points[ii,2];
% Convert the current data point to polar coordinates,
% keeping in mind that we are treating (x,y) like the center.
cur_radius = sqrt( (cur_x - x)^2 + (cur_y - y)^2 );
cur_angle = atan2(cur_y - y, cur_x - x)
% You have to write this yourself, but it
% will return an index for the bin that this
% data point falls into, i.e. which of the 4 segments
% of one of the radial cells it falls into.
cur_bin = get_bin_number(cur_radius, cur_angle);
% Check if this data point intensity is larger than
% the current max value for its bin.
if ( intensities(ii) >= max_intensities(cur_bin))
max_intensities(cur_bin) = intensities(ii);
end
end
You will now have to make the function get_bin_number() which takes as its input the angle and radius of the data point away from the center point. It should return just an index between 1 and total_num_bins, because you will be keeping the max intensities in a linear array. So, for example, index number 1 might correspond to the first 1/4 piece of the closest radial cell in the upper right quadrant, index 2 might correspond to the next 1/4 of that same cell, moving counter-clockwise, or something like this. You have to devise your own convention for keeping track of the bins.
A late answer, but I believe an even easier solution would just be to convert your data from (x,y) coordinates to (r,theta) by using (r = sqrt(x.^2 + y.^2), theta = atan(y,x)) then use the hist3 function on the (r,theta) data set to get a radial histogram.
Therefore solution is as follows:
% I assume you have some M-by-2 matrix X that's in the form (x,y)
% Convert (x,y) to (r,theta)
xVect = X(:,1);
yVect = X(:,2);
X = [sqrt(xVect.^2 + yVect.^2), ...%formula for r
atan(yVect,xVect)]; %formula for theta
% 5 is the number of wedges along 'r', your radial axis
% 12 is the number of wedges along 'theta', your theta 'axis'
dist = hist3(X,5,12);
Even if you have solved this, I hope this helps anybody else who wants to create a radial/angular histogram!