Sierpinski Triangle in MATLAB - matlab

I have written the following code but I seem to be missing something and I'm not sure what it is:
axis off
hold on
for i = 1:10000
r = [rand() rand()];
rp = [(1 - sqrt(r(1))) * -1 + (sqrt(r(1)) * (1 - r(2))) * 0 + (sqrt(r(1)) * r(2)) * 1,
(1 - sqrt(r(1))) * 0 + (sqrt(r(1)) * (1 - r(2))) * sqrt(3) + (sqrt(r(1)) * r(2)) * 0];
v1 = [-1 0 1];
v2 = [0 sqrt(3) 0];
num = randi([1 3], 1);
rv = [v1(num) v2(num)];
mid_x = (rp(1) + rv(1))/2;
mid_y = (rp(2) + rv(2))/2;
plot(mid_x, mid_y, '.r', 'MarkerSize', 0.000001)
end
this is what I'm getting instead:
triangle
Does anyone know how to fix it?

I see two issues:
You've reset r to a random value each time through the loop and then performed a computation that puts it in one of the three sub-triangles that appear in your image. Rather than doing that, you should redefine r each time in terms of its previous value. That's the basic idea behind iteration in general.
I'm not sure what the computation involving the square root is for. The basic computation for a Sierpinski triangle simply moves the current point r half the way to one of the three vertices of the triangle, which is randomly chosen. That's correctly done in your definition of mid_x and mid_y.
Taking this all into account, we get something that looks like so:
axis off
hold on
r = [rand() rand()]; % Define initial value of r outside the loop
v1 = [-1 0 1];
v2 = [0 sqrt(3) 0];
for i = 1:10000
num = randi([1 3], 1);
rv = [v1(num) v2(num)];
mid_x = (r(1) + rv(1))/2;
mid_y = (r(2) + rv(2))/2;
r = [mid_x mid_y]; % Redefine r each time through the loop
plot(mid_x, mid_y, '.r', 'MarkerSize', 0.000001)
end

Related

How to adjust length of a line drawn between two lines?

I have a line that I have drawn between two points
origin = [1 0 2];
point = [1 -2.8 2.8 ];
I want to draw a line between them at a certain distance. I have tried the following, which gives me wrong results... Is there a better way to do this?
distance = 0.5;
point3 = origin + (point-origin) * distance;
MY SOLUTION:
which assumes that I will start at the same origin, just the last point changes:
cff = 0.5; % Coefficient that determines the length of the line
point2(1)= origin(1) + cff* (point(1) - origin(1));
point2(2)= origin(2) + cff* (point(2)- origin(2));
point2(3)= origin(3) + cff* (point(3) - origin(3));
For a length of a certain distance from origin:
d = 3; % Distance
unit_v = point - origin;
u = unit_v / norm(unit_v);
point2(1)= origin(1) + d * u(1) ;
point2(2)= origin(2) + d * u(2) ;
point2(3)= origin(3) + d * u(3) ;
You can calculate the unit vector from start point to end point and move the start point:
A = [1 0 2];
B = [1 -2.8 2.8 ];
d = 0.5;
V = B-A; % A->B vector
l = norm(V); % length of V
U = V/l; % unit vector
C = A + d*U;
D = A + (l-d)*U;
hold on
plot3([A(1) B(1)], [A(2) B(2)],[A(3) B(3)], '--o')
plot3([C(1) D(1)], [C(2) D(2)],[C(3) D(3)], '-x', 'linewidth', 2)

How to store two variables (x.y) from a loop?

I am generating two different coordinates (x, y) within a loop. In my code, I have just realised that it is saving the last variable from the loop. I am, however, trying to save all the iterations from the setsize variable. I already tried to save using something like:
circleposition = [0:length(setsize) x(i),y(i)];
But, it seems that I am not doing it correctly, getting the following error:
Subscript indices must either be real positive integers or logicals.-
Error using vertcat
Dimensions of matrices being concatenated are not consistent.
Here is my original code:
setsize = 9;
r = 340;
cx = 500;
cy = 500;
anglesegment = 2 * pi/setsize;
circleposition = [];
for i = drange (0:setsize)
x = r * cos(i*anglesegment) + cx;
y = r * sin(i*anglesegment) + cy;
circleposition = [x,y];
end
Output:
circleposition =
0 1.0000
840.0000 500.0000
It runs only with the first/last iteration. I need to get 9 x's and 9 y's (depending the setsize, variable).
It's kind of hard to follow, which error message comes from which attempt, but let's have a look.
I don't have access to the Parallel Computing Toolbox, which seems necessary to use the for-loop over distributed range drange, but I assume, this loop can be replaced by for i = 0:setsize for testing.
Now, when starting at i = 0, you would try to access x(0) and y(0), which is not allowed (Subscript indices must either be real positive integers or logicals). Also, you would get 10 values instead of 9, as you stated in your question. So, let's start at i = 1.
To store all 9 pairs of x and y, your circleposition should be an 9 x 2 array So, initialize that by, for example, circleposition = zeros(setsize, 2).
Last, you need to use proper indexing to store [x, y] at the i-th row of circleposition, i.e. circleposition(i, :).
So, the corrected code (attention on the replaced drange part) could look like this:
setsize = 9;
r = 340;
cx = 500;
cy = 500;
anglesegment = 2 * pi/setsize;
circleposition = zeros(setsize, 2); % Initialize circleposition appropriately
for i = 1:setsize % Start at i = 1
x = r * cos(i*anglesegment) + cx;
y = r * sin(i*anglesegment) + cy;
circleposition(i, :) = [x, y]; % Correct indexing of the row
end
circleposition % Output
The output would then be:
circleposition =
760.46 718.55
559.04 834.83
330.00 794.45
180.50 616.29
180.50 383.71
330.00 205.55
559.04 165.17
760.46 281.45
840.00 500.00
On the second error (Error using vertcat. Dimensions of matrices being concatenated are not consistent.): I don't see, where you used vertical concatenation at all!?
Hear is code that works:
setsize = 9;
r = 340;
cx = 500;
cy = 500;
anglesegment = 2 * pi/setsize;
circleposition = zeros(setsize + 1, 2); % Changed from circleposition = []
for i = drange (0:setsize)
x = r * cos(i*anglesegment) + cx;
y = r * sin(i*anglesegment) + cy;
circleposition((i+1),:) = [x,y]; % Changed from circleposition = [x,y];
end
Explanation:
The fix was Changing circleposition = [x,y]; to circleposition((i+1),:) = [x,y]. Without ((i+1),:), you are changing the data of circleposition, not adding to it.
Changing circleposition = []; to circleposition = zeros(setsize + 1, 2); was not required, its just recommended to allocate memory for speed, not an issue for small number of elements.

how to improve the running speed of this Matlab code?

I am trying to simulate a motion of some particles.The program seems to be very slow.
I just started lean programming so I dont know where is the problem exactly that make it slow, I think that plotting is take much time.
could some sone suggest me how to improve it?
function folks(N , T)
if nargin < 1
N = 50;
T = 100;
end
A=20;R=50;a=100;r=2;
x0=10*randn(3,N);
v0=0*randn(3,N);
clear c
% Initilazing plot
color = hsv(N);
xh=zeros(1,N);
f=2*max(max(x0));ff=f/1000000;
figure(2),clf
set(gcf,'doublebuffer','on')
hold on, grid on, axis([-1 1 -1 1 -.5 .5]*f)
for j = 1:N
xh(j) = line(x0(1,j),x0(2,j),x0(3,j),'color',color(j,:), ...
'marker','.','markersize',15);
end
title('t = 0','fontsize',18)
rotate3d;
view([1.8,-1.8,1])
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
% Solving the ode system
tol = 3e-14;
opts = odeset('reltol',tol,'abstol',tol);
X0=[x0(:);v0(:)];
dt=T/1000;
for tnext = dt:dt:10*T
tspan = [tnext-dt tnext];
[T1,X1] = ode45(#odefolk,tspan,X0,opts,r,R,a,A);
X0 = X1(end,:);
if max(abs(X0(1:3*N)))>f
f=1.1*f;
axis([-1 1 -1 1 -1 1]*f)
end
for j=0:N-1
set(xh(j+1),'xdata',X0(3*j+1),'ydata',X0(3*j+2),'zdata',X0(3*j+3))
end
title(sprintf('t = %3.0f %5.0e',tnext),'fontsize',18),
drawnow
end
I tried to save the data in a matrix and then plot every thing in new loop but it make it much slower.
And also its not related to the solver ode45 or the system odefolk
function dx = fkt(~,x,r,R,a,A)
n = length(x); % n = 90 = 6 *N
M = n/6; % M= 15
dx = zeros(n,1);
for i = 1 : 3 : n/2
vx = 0 ; vy = 0 ; vz = 0;
for j = 1 : 3 : n/2
if (i~=j)
vx = x(i) - x(j) ;
vy = x(i+1) - x(j+1);
vz = x(i+2) - x(j+2);
% ex = expo (vx , vy , vz ,r , a);
%
% val = (ex(1) * R - ex(2) * A);
leng = sqrt(vx^ 2 +vy^ 2 + vz^ 2);
expo1 = exp(-1 * leng / r) / r;
expo2 = exp(-1 * leng / a) / a;
val = (expo1 * R - expo2 * A);
vx = vx * val;
vy = vy * val;
vz = vz * val;
end
end
vx = vx/M ;
vy = vy/M;
vz = vz/M;
dx(i) = x(i + 3 * M );
dx(i + 1) = x(i + 3 * M + 1);
dx(i + 2) = x(i + 3 * M + 2);
dx(i + 3 * M ) = vx;
dx(i + 3 * M + 1 ) = vy;
dx(i + 3 * M + 2 ) = vz;
end
end
To perform a code, it's a good idea to analyze it with the Profiler:
https://es.mathworks.com/help/matlab/ref/profile.html
Anyway, I see some things you could change. First, avoid to draw in each iteration. Do it in the end or draw every M interations.
Secondly, avoid using a loop to define the graphic. Plot it with vector type input data.
Thirdly, ode45 can do the iterations for you and can return you a vector with all the interations. Use it to plot.
Some tips:
use tic and toc to measure the execution time of your code (lines, functions, etc.)
preassign arrays and avoid letting them grow in loops
do not use plot in a loop. Try to use low-level commands and change the value of exisiting lines by refering to their handles (like for example handle = line([0 1][2 3][4 5]) and then set(handle,'XDATA',[4 7],'YDATA',[2 6],'ZDATA',[2 4]))
MATLAB is an interpreted language, so your code will run slower in most cases compared to compiled programs

Matlab: Extract values that I plot but which has not been stored

I have a mathematical function E which I want to minimize. I get from solving this 16 possible solutions x1, x2, ..., x16, only two of which that actually minimize the function (located at a minimum). Using a for loop, I can then plug all of these 16 solutions into the original function, and select the solutions I need by applying some criteria via if statements (plotting E vs E(x) if x is real and positive, if first derivative of E is below a threshold, and if the second derivative of E is positive).
That way I only plot the solutions I'm interested in. However, I would now like to extract the relevant x that I plot. Here's a sample MATLAB code that plots the way I just described. I want to extract the thetas that I actually end up plotting. How to do that?
format long
theta_s = 0.77944100;
sigma = 0.50659500;
Delta = 0.52687700;
%% Defining the coefficients of the 4th degree polynomial
alpha = cos(2*theta_s);
beta = sin(2*theta_s);
gamma = 2*Delta^2/sigma^2;
a = -gamma^2 - beta^2*Delta^2 - alpha^2*Delta^2 + 2*alpha*Delta*gamma;
b = 2*alpha*gamma - 2*Delta*gamma - 2*alpha^2*Delta + 2*alpha*Delta^2 -...
2*beta^2*Delta;
c = 2*gamma^2 - 2*alpha*Delta*gamma - 2*gamma - alpha^2 + 4*alpha*Delta +...
beta^2*Delta^2 - beta^2 - Delta^2;
d = -2*alpha*gamma + 2*Delta*gamma + 2*alpha + 2*beta^2*Delta - 2*Delta;
e = beta^2 - gamma^2 + 2*gamma - 1;
%% Solve the polynomial numerically.
P = [a b c d e];
R = roots(P);
%% Solve r = cos(2x) for x: x = n*pi +- 1/2 * acos(r). Using n = 0 and 1.
theta = [1/2.*acos(R) -1/2.*acos(R) pi+1/2.*acos(R) pi-1/2.*acos(R)];
figure;
hold on;
x = 0:1/1000:2*pi;
y_1 = sigma*cos(x - theta_s) + sqrt(1 + Delta*cos(2*x));
y_2 = sigma*cos(x - theta_s) - sqrt(1 + Delta*cos(2*x));
plot(x,y_1,'black');
plot(x,y_2,'black');
grid on;
%% Plot theta if real, if positive, if 1st derivative is ~zero, and if 2nd derivative is positive
for j=1:numel(theta);
A = isreal(theta(j));
x_j = theta(j);
y_j = sigma*cos(x_j - theta_s) + sqrt(1 + Delta*cos(2*x_j));
FirstDer = sigma* sin(theta(j) - theta_s) + Delta*sin(2*theta(j))/...
sqrt(1 + Delta*cos(2*theta(j)));
SecDer = -sigma*cos(theta(j)-theta_s) - 2*Delta*cos(2*theta(j))/...
(1 + Delta*cos(2*theta(j)))^(1/2) - Delta^2 * (sin(2*theta(j)))^2/...
(1 + Delta*cos(2*theta(j)))^(3/2);
if A == 1 && x_j>=0 && FirstDer < 1E-7 && SecDer > 0
plot(x_j,y_j,['o','blue'])
end
end
After you finish all plotting, get the axes handle:
ax = gca;
then write:
X = get(ax.Children,{'XData'});
And X will be cell array of all the x-axis values from all lines in the graph. One cell for each line.
For the code above:
X =
[1.961054062875753]
[4.514533853417446]
[1x6284 double]
[1x6284 double]
(First, the code all worked. Thanks for the effort there.)
There are options here. A are couple below
Record the values as you generate them
Within the "success" if statement, simply record the values. See edits to your code below.
This would always be the preferred option for me, it just seems much more efficient.
xyResults = zeros(0,2); %%% INITIALIZE HERE
for j=1:numel(theta);
A = isreal(theta(j));
x_j = theta(j);
y_j = sigma*cos(x_j - theta_s) + sqrt(1 + Delta*cos(2*x_j));
FirstDer = sigma* sin(theta(j) - theta_s) + Delta*sin(2*theta(j))/...
sqrt(1 + Delta*cos(2*theta(j)));
SecDer = -sigma*cos(theta(j)-theta_s) - 2*Delta*cos(2*theta(j))/...
(1 + Delta*cos(2*theta(j)))^(1/2) - Delta^2 * (sin(2*theta(j)))^2/...
(1 + Delta*cos(2*theta(j)))^(3/2);
if A == 1 && x_j>=0 && FirstDer < 1E-7 && SecDer > 0
xyResults(end+1,:) = [x_j y_j]; %%%% RECORD HERE
plot(x_j,y_j,['o','blue'])
end
end
Get the result from the graphics objects
You can get the data you want from the actual graphics objects. This would be the option if there was just no way to capture the data as it was generated.
%First find the objects witht the data you want
% (Ideally you could record handles to the lines as you generated
% them above. But then you could also simply record the answer, so
% let's assume that direct record is not possible.)
% (BTW, 'findobj' is an underused, powerful function.)
h = findobj(0,'Marker','o','Color','b','type','line')
%Then get the `xdata` filed from each
capturedXdata = get(h,'XData');
capturedXdata =
2×1 cell array
[1.96105406287575]
[4.51453385341745]
%Then get the `ydata` filed from each
capturedYdata = get(h,'YData');
capturedYdata =
2×1 cell array
[1.96105406287575]
[4.51453385341745]

Frames of type double must be in the range of 0 to 1: MATLAB

I have a video and I have made a Sobel mask for it on MATLAB. Now I have to apply that Sobel mask on each frame of the video by reading each frame through for loop. The process is something like:
Step 1: Reading frame.
step 2: Converting it to grayscale using rgb2gray.
Step 3: Converting it to double.
Here, after applying the mask when I try to write the frame on the resultant video.avi file, I get the following error:
"Frames of type double must be in the range of 0 to 1"
What is wrong with my code? The code I wrote is shown below:
vid = VideoReader('me.mp4');
frames = read(vid);
total = get(vid, 'NumberOfFrames');
write = VideoWriter('me.avi');
open(write);
mask1 = [-1 -2 -1; 0 0 0; 1 2 1]; % Horizontal mask
mask2 = [-1 0 1; -2 0 2; -1 0 1]; %Vertical Mask
for k = 1 : 125
image = frames(:,:,:,k);
obj = image;
obj1 = rgb2gray(obj);
obj2=double(obj1);
for row = 2 : size(obj2, 1) - 1
for col = 2 : size(obj2, 2) - 1
c1 = obj2(row - 1, col - 1) * mask1(1 ,1);
c2 = obj2(row - 1, col) * mask1(1 ,2);
c3 = obj2(row - 1, col + 1) * mask1(1 ,3);
c4 = obj2(row, col - 1)*mask1(2, 1);
c5 = obj2(row, col)*mask1(2, 2);
c6 = obj2(row, col + 1)*mask1(2, 3);
c7 = obj2(row + 1, col - 1)*mask1(3,1);
c8 = obj2(row + 1, col)*mask1(3,2);
c9 = obj2(row + 1, col + 1)*mask1(3,3);
c11 = obj2(row - 1, col - 1)*mask2(1 , 1);
c22 = obj2(row, col - 1)*mask2(2, 1);
c33 = obj2(row + 1, col - 1)*mask2(3, 1);
c44 = obj2(row -1, col)*mask2(1, 2);
c55 = obj2(row, col)*mask2(2 , 2);
c66 = obj2(row +1, col)*mask2(2 , 3);
c77 = obj2(row - 1, col + 1)*mask2(1 , 3);
c88 = obj2(row, col +1)*mask2(2 , 3);
c99 = obj2(row + 1, col + 1)*mask2(3 , 3);
result = c1 + c2 + c3 +c4 +c5+ c6+ c7+ c8 +c9;
result2 = c11 + c22 + c33 + c44 + c55 + c66 + c77 + c88 + c99;
%result = double(result);
%result2 = double(result2);
rim1(row, col) = ((result^2+result2^2) *1/2);
rim2(row, col) = atan(result/result2);
end
end
writeVideo(write, rim2); %This line has the problem with rim2 as rim2 is the frame i'm trying to write on the video file.
end
close(write);
rim2 has range [-pi/2, pi/2] at the end, which is not compatible with the write function which expects [0,1] range.
Convert it to [0,1] range using the mat2gray function, i.e.
writeVideo(write, mat2gray(rim2));
Your code will then work as expected (confirmed on my machine).
By the way, this doesn't affect your code, but presumably you meant to do im2double(A) rather than double(A). The former produces a "proper" grayscale image in the range [0,1], whereas the latter simply converts your uint8 image in the range [0,255] to double format (i.e. [0.0, 255.0]).
The line of rim2 inside your double for loop is using atan, which will generate values that are both positive and negative - from -pi/2 to +pi/2 exactly. rim2 is expected to have values that are only between [0,1]. I can't figure out what exactly you're doing, but it looks like you're calculating the magnitude and gradient angle at each pixel location. If you want to calculate the magnitude, you have to take the square root of the result, not simply multiply by 1/2. The calculation of the gradient (... or even the whole Sobel filter calculation...) is very funny.
I'll just assume this is working for your purposes so I'm not sure how to change the output of rim2for suitable display but perhaps you could scale it to the range of [0,1] before you write the video so that it's within this range.
Something like this would work before you write the frame:
rim2 = (rim2 - min(rim2(:))) / (max(rim2(:)) - min(rim2(:)));
writeVideo(write, rim2);
The above is your typical min-max normalization that is seen in practice. Specifically, the above will ensure that the smallest value is 0 while the largest value is 1 per frame. If you want to be consistent over all frames, simply add pi/2 then divide by pi. This assumes that the minimum is -1 and the maximum is +1 over all frames however.
rim2 = (rim2 + pi/2) / pi;
writeVideo(write, rim2);
However, I suspect you want to write the magnitude to file, not the angle. Therefore, replace the video writing with rim1 as the frame to write instead of rim2, then normalize after. Make sure your gradient calculation is correct though:
rim1(row, col) = ((result^2+result2^2)^(1/2));
% or use sqrt:
% rim1(row, col) = sqrt(result^2 + result2^2);
Now write to file:
rim1 = (rim1 - min(rim1(:))) / (max(rim1(:)) - min(rim1(:)));
writeVideo(write, rim1);
However, if I can provide a method of efficiency, don't use for loops to compute the gradient and angle. Use conv2 and ensure you use the 'same' flag or imfilter from the image processing toolbox to perform the filtering for you, then calculate the gradient and angle vectorized. Also, convert to grayscale and cast your frame in one go in the main loop. I'll assume you have the image processing toolbox as having the computer vision toolbox (you have this as you're using a VideoWriter object) together with the image processing toolbox is what most people have:
vid = VideoReader('me.mp4');
frames = read(vid);
total = get(vid, 'NumberOfFrames');
write = VideoWriter('me.avi');
open(write);
mask1 = [-1 -2 -1; 0 0 0; 1 2 1]; % Horizontal mask
mask2 = [-1 0 1; -2 0 2; -1 0 1]; %Vertical Mask
for k = 1 : 125
obj2 = double(rgb2gray(frames(:,:,:,k))); % New
grad1 = imfilter(obj2, mask1); % New
grad2 = imfilter(obj2, mask2); % New
rim1 = sqrt(grad1.^2 + grad2.^2); % New
rim2 = atan2(grad1, grad2); % New
% Normalize
rim2 = (rim2 - min(rim2(:))) / (max(rim2(:)) - min(rim2(:)));
writeVideo(write, rim2);
end
close(write);