Documenting matlab code for generating random shapes - matlab

Can you please help me document this Matlab code that is supposed to produce random shapes?? The wiggliness of the shapes is supposed to be controlled by the variable degree...
But how the rho (radius values) are produced... I can't really get it....
degree = 5;
numPoints = 1000;
blobWidth = 5;
theta = 0:(2*pi)/(numPoints-1):2*pi;
coeffs = rand(degree,1);
rho = zeros(size(theta));
for i = 1:degree
rho = rho + coeffs(i)*sin(i*theta);
end
phase = rand*2*pi;
[x,y] = pol2cart(theta+phase, rho+blobWidth);
plot(x,y)
axis equal
set(gca,'Visible','off')

theta = 0:(2*pi)/(numPoints-1):2*pi;
So this is just a vector of angles in a revolution, if you plot this theta against a constant rho (after calling pol2cart) you will get a circle:
r = ones(size(theta));
[x,y] = pol2cart(theta, r);
plot(x,y)
axis equal
This should be obvious if you understand what pol2cart does because you have a series of all the angles in a circle and a constant radius for all of them. If you don't understand that (i.e. polar coordinates) then that's a very basic mathematical concept you need to go and read up on your own before trying to understand this code.
OK so now a circle in cartesian coords is just a line in polar coords (i.e. plot(theta, r) noting that the horizontal axis now represents angle and the vertical represents radius). So if we want to randomly mess up our circle, we could randomly mess up our line. Using sin does this in a nice smooth way. Adding random frequencies of many sin waves adds less and less predictable "jitter". I think it would help you to understand if you add the following line to your code:
rho = zeros(size(theta));
hold all
for i = 1:degree
rho = rho + coeffs(i)*sin(i*theta);
plot(theta, rho)
end
and contrast this to (be sure to close your figure window before running this)
rho = zeros(size(theta));
hold all
for i = 1:degree
rho = rho + coeffs(i)*sin(i*theta);
plot(theta, coeffs(i)*sin(i*theta))
end
The second one shows you the different frequencies of sin waves used and the first shows how these sum to create unpredictable wavy lines. Now think of the pol2rect function as bending these lines around to make a "circle". If the line is dead straight you get a perfect circle, if it's wavy you get a "wavy" circle.
degree in your code just controls how many sin waves to add up.
finally phase = rand*2*pi; just randomly rotates your shape after it has been created.

Well, this was an amazing piece of code! But regarding rho. What is done is that you have a circle with base radius of 5 (blobwidth) and then you have a random offset coeffs. Then the offset is added to rho in rho = rho + coeffs(i)*sin(i*theta);. This means that the first loop an offset is added to the circle with frequency 1Hz. This then yields a constant offset. The next loop the frequency increases to 2Hz. Then the offset will be added to every second point and the offset may be negative as well. Then it goes on like this. Finally the coordinate is transformed to polar.
A few comments though. The most readable and the easiest way to create theta is to use linspace. And also, since rho is overwritten in the loop, you may as well define it just as rho = 0;

Related

How do I find exact rest points?

I have a displacement and a time data of a movement of an object.
The object oscillates around zero. That is, first - it gets set into motion by a small amount of force, then it comes to rest. again, a little force is applied and object gets set into motion.
I have found out the velocity and acceleration using
V= [0 ; diff(disp) ./ diff(times)];
A= [0; diff(V) ./ diff(times)];
I was thinking of finding points where velocity is zero. But i guess there are more than required such instances. Find the graph below:
velocity plot
I am interested in only circles time values. Is there a way to get these?
I observe a pattern
velocity increases then decreases by almost same amount.
Then due to friction, it crosses zero by a smaller amount and again becomes negative
finally comes to rest, but a very little velocity is still present.
It is this touch point to zero that I want. Then again force is applied and the same cycle repeats.
Pl note that I do not have a time of when force is applied. Otherwise there was nothing to be done.
Also, I did plot the acceleration. But is seems so useless..
I am using matlab.
Here's one way to find approximate zeros in gridded data:
% some dummy synthetic data
x = linspace(0, 10, 1e3);
y = exp(-0.3*x) .* sin(x) .* cos(pi*x);
% its derivative (presumably your "acceleration")
yp = diff(y) ./ diff(x);
% Plot data to get an overview
plot(x,y), hold on
% Find zero crossings (product of two consecutive data points is negative)
zero_x = y(1:end-1) .* y(2:end) < 0;
% Use derivative for linear interpolation between those points
x_cross = x(zero_x) + y(zero_x)./yp(zero_x);
% Plot those zeros
plot(x_cross, zeros(size(x_cross)), 'ro')
Result:
It is then up to you to select which zeros you need, because I could not understand from the question what made those points in the circles so special...
The resting points you asked have the following property:
dx / dt = v = 0
d^2 x / dt^2 = a = 0 # at the instance that the object becomes v = 0, there is no force on it.
So you may want to check also the second formula to filter the resting points.

Create a spiral between two cartesian points in MATLAB

Perhaps this is a basic question but I haven't been able to find anything specifically like this and I'm wondering how to do it in the best way.
I have two sets of points (x1,y1,z1) and (x2,y2,z2) and I have converted them into polar coordinates. I would like to create a counter-clockwise helix of decreasing radius to reach the second point.
I would also like to specify how many revolutions it takes.
All of the examples I have seen are two points on the x axis and going clockwise.
Any suggestions would be very much appreciated!
Thanks.
This example code generates a counter clockwise spiral from p1 to p2 that isn't on x-axis and you can specify the number of revolutions. However it is in 2D and initial points are in cartesian coordinates. I'm not sure how to do it in 3D but I hope this can help you with the offsetting and the counter-clockwising.
%random 2d points
p1 = [3,5];
p2 = [1,4];
%radius of first point to second
r = norm(p1-p2);
%angle between two point wrt the y-axis
theta_offset = tan((p1(2)- p2(2))/(p1(1)-p2(1)));
rez = 150; % number of points
rev = 5; % number of revolutions
t = linspace(0,r,rez); %radius as spiral decreases
theta = linspace(0,2*pi*rev,rez) + theta_offset; %angle as spiral decreases
x = cos(theta).*t+p2(1);
y = sin(theta).*t+p2(2);
plot(x,y)

Fourier transform for fiber alignment

I'm working on an application to determine from an image the degree of alignment of a fiber network. I've read several papers on this issue and they basically do this:
Find the 2D discrete Fourier transform (DFT = F(u,v)) of the image (gray, range 0-255)
Find the Fourier Spectrum (FS = abs(F(u,v))) and the Power Spectrum (PS = FS^2)
Convert spectrum to polar coordinates and divide it into 1º intervals.
Calculate number-averaged line intensities (FI) for each interval (theta), that is, the average of all the intensities (pixels) forming "theta" degrees with respect to the horizontal axis.
Transform FI(theta) to cartesian coordinates
Cxy(theta) = [FI*cos(theta), FI*sin(theta)]
Find eigenvalues (lambda1 and lambda2) of the matrix Cxy'*Cxy
Find alignment index as alpha = 1 - lamda2/lambda1
I've implemented this in MATLAB (code below), but I'm not sure whether it is ok since point 3 and 4 are not really clear for me (I'm getting similar results to those of the papers, but not in all cases). For instance, in point 3, "spectrum" is referring to FS or to PS?. And in point 4, how should this average be done? are all the pixels considered? (even though there are more pixels in the diagonal).
rgb = imread('network.tif');%513x513 pixels
im = rgb2gray(rgb);
im = imrotate(im,-90);%since FFT space is rotated 90º
FT = fft2(im) ;
FS = abs(FT); %Fourier spectrum
PS = FS.^2; % Power spectrum
FS = fftshift(FS);
PS = fftshift(PS);
xoffset = (513-1)/2;
yoffset = (513-1)/2;
% Avoid low frequency points
x1 = 5;
y1 = 0;
% Maximum high frequency pixels
x2 = 255;
y2 = 0;
for theta = 0:pi/180:pi
% Transposed rotation matrix
Rt = [cos(theta) sin(theta);
-sin(theta) cos(theta)];
% Find radial lines necessary for improfile
xy1_rot = Rt * [x1; y1] + [xoffset; yoffset];
xy2_rot = Rt * [x2; y2] + [xoffset; yoffset];
plot([xy1_rot(1) xy2_rot(1)], ...
[xy1_rot(2) xy2_rot(2)], ...
'linestyle','none', ...
'marker','o', ...
'color','k');
prof = improfile(F,[xy1_rot(1) xy2_rot(1)],[xy1_rot(2) xy2_rot(2)]);
i = i + 1;
FI(i) = sum(prof(:))/length(prof);
Cxy(i,:) = [FI(i)*cos(theta), FI(i)*sin(theta)];
end
C = Cxy'*Cxy;
[V,D] = eig(C)
lambda2 = D(1,1);
lambda1 = D(2,2);
alpha = 1 - lambda2/lambda1
Figure: A) original image, B) plot of log(P+1), C) polar plot of FI.
My main concern is that when I choose an artificial image perfectly aligned (attached figure), I get alpha = 0.91, and it should be exactly 1.
Any help will be greatly appreciated.
PD: those black dots in the middle plot are just the points used by improfile.
I believe that there are a couple sources of potential error here that are leading to you not getting a perfect alpha value.
Discrete Fourier Transform
You have discrete imaging data which forces you to take a discrete Fourier transform which inevitably (depending on the resolution of the input data) have some accuracy issues.
Binning vs. Sampling Along a Line
The way that you have done the binning is that you literally drew a line (rotated by a particular angle) and sampled the image along that line using improfile. Using improfile performs interpolation of your data along that line introducing yet another potential source of error. The default is nearest neighbor interpolation which in the example shown below can cause multiple "profiles" to all pick up the same points.
This was with a rotation of 1-degree off-vertical when technically you'd want those peaks to only appear for a perfectly vertical line. It is clear to see how this sort of interpolation of the Fourier spectrum can lead to a spread around the "correct" answer.
Data Undersampling
Similar to Nyquist sampling in the Fourier domain, sampling in the spatial domain has some requirements as well.
Imagine for a second that you wanted to use 45-degree bin widths instead of the 1-degree. Your approach would still sample along a thin line and use that sample to represent 45-degrees worth or data. Clearly, this is a gross under-sampling of the data and you can imagine that the result wouldn't be very accurate.
It becomes more and more of an issue the further you get from the center of the image since the data in this "bin" is really pie wedge shaped and you're approximating it with a line.
A Potential Solution
A different approach to binning would be to determine the polar coordinates (r, theta) for all pixel centers in the image. Then to bin the theta components into 1-degree bins. Then sum all of the values that fall into that bin.
This has several advantages:
It removes the undersampling that we talked about and draws samples from the entire "pie wedge" regardless of the sampling angle.
It ensures that each pixel belongs to one and only one angular bin
I have implemented this alternate approach in the code below with some false horizontal line data and am able to achieve an alpha value of 0.988 which I'd say is pretty good given the discrete nature of the data.
% Draw a bunch of horizontal lines
data = zeros(101);
data([5:5:end],:) = 1;
fourier = fftshift(fft2(data));
FS = abs(fourier);
PS = FS.^2;
center = fliplr(size(FS)) / 2;
[xx,yy] = meshgrid(1:size(FS,2), 1:size(FS, 1));
coords = [xx(:), yy(:)];
% De-mean coordinates to center at the middle of the image
coords = bsxfun(#minus, coords, center);
[theta, R] = cart2pol(coords(:,1), coords(:,2));
% Convert to degrees and round them to the nearest degree
degrees = mod(round(rad2deg(theta)), 360);
degreeRange = 0:359;
% Band pass to ignore high and low frequency components;
lowfreq = 5;
highfreq = size(FS,1)/2;
% Now average everything with the same degrees (sum over PS and average by the number of pixels)
for k = degreeRange
ps_integral(k+1) = mean(PS(degrees == k & R > lowfreq & R < highfreq));
fs_integral(k+1) = mean(FS(degrees == k & R > lowfreq & R < highfreq));
end
thetas = deg2rad(degreeRange);
Cxy = [ps_integral.*cos(thetas);
ps_integral.*sin(thetas)]';
C = Cxy' * Cxy;
[V,D] = eig(C);
lambda2 = D(1,1);
lambda1 = D(2,2);
alpha = 1 - lambda2/lambda1;

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');

Rotating Axes Around Line of Fit MATLAB

I'm currently frustrated by the following problem:
I've got trajectory data (i.e.: Longitude and Latitude data) which I interpolate to find a linear fitting (using polyfit and polyval in matlab).
What I'd like to do is to rotate the axes in a way that the x-axis (the Longitude one) ends up lying on the best-fit line, and therefore my data should now lie on this (rotated) axis.
What I've tried is to evaluate the rotation matrix from the slope of the line-of-fit (m in the formula for a first grade polynomial y=mx+q) as
[cos(m) -sin(m);sin(m) cos(m)]
and then multiply my original data by this matrix...to no avail!
I keep obtaining a plot where my data lay in the middle and not on the x-axis where I expect them to be.
What am I missing?
Thank you for any help!
Best Regards,
Wintermute
A couple of things:
If you have a linear function y=mx+b, the angle of that line is atan(m), not m. These are approximately the same for small m', but very different for largem`.
The linear component of a 2+ order polyfit is different than the linear component of a 1st order polyfit. You'll need to fit the data twice, once at your working level, and once with a first order fit.
Given a slope m, there are better ways of computing the rotation matrix than using trig functions (e.g. cos(atan(m))). I always try to avoid trig functions when performing geometry and replace them with linear algebra operations. This is usually faster, and leads to fewer problems with singularities. See code below.
This method is going to lead to problems for some trajectories. For example, consider a north/south trajectory. But that is a longer discussion.
Using the method described, plus the notes above, here is some sample code which implements this:
%Setup some sample data
long = linspace(1.12020, 1.2023, 1000);
lat = sin ( (long-min(long)) / (max(long)-min(long))*2*pi )*0.0001 + linspace(.2, .31, 1000);
%Perform polynomial fit
p = polyfit(long, lat, 4);
%Perform linear fit to identify rotation
pLinear = polyfit(long, lat, 1);
m = pLinear(1); %Assign a common variable for slope
angle = atan(m);
%Setup and apply rotation
% Compute rotation metrix using trig functions
rotationMatrix = [cos(angle) sin(angle); -sin(angle) cos(angle)];
% Compute same rotation metrix without trig
a = sqrt(m^2/(1+m^2)); %a, b are the solution to the system:
b = sqrt(1/(1+m^2)); % {a^2+b^2 = 1}, {m=a/b}
% %That is, the point (b,a) is on the unit
% circle, on a line with slope m
rotationMatrix = [b a; -a b]; %This matrix rotates the point (b,a) to (1,0)
% Generally you rotate data after removing the mean value
longLatRotated = rotationMatrix * [long(:)-mean(long) lat(:)-mean(lat)]';
%Plot to confirm
figure(2937623)
clf
subplot(211)
hold on
plot(long, lat, '.')
plot(long, polyval(p, long), 'k-')
axis tight
title('Initial data')
xlabel('Longitude')
ylabel('Latitude')
subplot(212)
hold on;
plot(longLatRotated(1,:), longLatRotated(2,:),'.b-');
axis tight
title('Rotated data')
xlabel('Rotated x axis')
ylabel('Rotated y axis')
The angle you are looking for in the rotation matrix is the angle of the line makes to the horizontal. This can be found as the arc-tangent of the slope since:
tan(\theta) = Opposite/Adjacent = Rise/Run = slope
so t = atan(m) and noting that you want to rotate the line back to horizontal, define the rotation matrix as:
R = [cos(-t) sin(-t)
sin(-t) cos(-t)]
Now you can rotate your points with R