I'm trying to create a 3d matrix of 1's and 0's. I want to connect 2 points together (shortest distance) by forming a line of 1's in between them.
It would look something like this, but in 3d
path_pixels = [0,0,1,0,0,0,0,0,0,0,0,0,0,0,0;
0,0,0,1,0,0,0,0,0,0,0,0,0,0,0;
0,0,0,0,1,0,0,0,0,0,0,0,0,0,0];
I'm able to do it in 2d using this code
clc;
clear;
%matrix size
A = zeros(70);
A(20,20) = 1; %arbitrary point
B = zeros(70);
B(40,40) = 1; %arbitrary point
D1 = bwdist(A);
D2 = bwdist(B);
D_sum = D1 + D2 ;
path_pixels = imregionalmin(D_sum);
spy(path_pixels)
How can I expand this method to 3d?
It depends what you mean by "connect", exactly. But, if the goal is a one-cell wide region between starting and end points, then that looks a lot like a "one-pixel wide" line, which could be defined as follows.
% start and end point of line
a = [1 10 2];
b = [4 1 9];
% get diffs
ab = b - a;
% find number of steps required to be "one pixel wide" in the shorter
% two dimensions
n = max(abs(ab)) + 1;
% compute line
s = repmat(linspace(0, 1, n)', 1, 3);
for d = 1:3
s(:, d) = s(:, d) * ab(d) + a(d);
end
% round to nearest pixel
s = round(s);
% if desired, apply to a matrix
N = 10;
X = zeros(N, N, N);
X(sub2ind(size(X), s(:, 1), s(:, 2), s(:, 3))) = 1;
% or, plot
clf
plot3(s(:, 1), s(:, 2), s(:, 3), 'r.-')
axis(N * [0 1 0 1 0 1])
grid on
Please forgive ugly code, I did this in a rush ;).
Related
I'm trying to plot Bezier Curves in Octave. A Bezier curve is defined by a number of control points, for example:
I'm trying to plot something like this. I found out about a function: drawBezierCurve but the function needs four control points and I need to plot a a Bezier curve with just three points. The control points are: a(1,2), b(4,-1), c(8,6). Any ideas?
Take the lines ab and bc:
a = [1 2];
b = [4 -1];
c = [8 6];
Create n equidistant points on them:
npoints = 10;
line1 = linspace(a, b, npoints);
line2 = linspace(b, c, npoints);
For the kth point on the Bézier curve, take k / n parts from first line and (n - k) / n parts from the second line.
weights = linspace(1, 0, npoints);
curve = line1 .* weights + line2 .* (1 - weights);
Connect the obtained points to draw the curve:
plot(curve(1, :), curve(2, :))
The complete code:
a = [1 2];
b = [4 -1];
c = [8 6];
npoints = 10; % modify this
line1 = linspace(a, b, npoints);
line2 = linspace(b, c, npoints);
weights = linspace(1, 0, npoints);
curve = line1 .* weights + line2 .* (1 - weights);
plot(curve(1, :), curve(2, :))
Additional code to draw the figures in this answer:
fig = figure;
hold on
% figure 1
plot([a(1) b(1)], [a(2) b(2)])
plot([b(1) c(1)], [b(2) c(2)])
% figure 2
plot(line1(1, :), line1(2, :), 'ok')
plot(line2(1, :), line2(2, :), 'or')
% figure 3
plot([line1(1,:); line2(1, :)], [line1(2, :); line2(2, :)], 'm--')
plot(curve(1, :), curve(2, :), 'oc')
% figure 4
plot(curve(1, :), curve(2, :), 'b', 'linewidth', 2)
You can always reproduce the bezier curve equation (see here)
P = [1,2; 4,-1; 8,6].';
B = #(n,i,u) nchoosek(n, i) * u .^ i .* (1-u) .^ (n-i);
U = [0:0.1:1];
Bez = B(2,0,U) .* P(:,1) + B(2,1,U) .* P(:,2) + B(2,2,U) .* P(:,3);
plot( Bez(1,:), Bez(2,:) );
I have a square matrix A which I would like to manipulate using a matrix D. The result will be the matrix B. The entry B(i, j) will be a sum of a number of elements from A(:, j), as determined by D.
Let me show what I have in codes -
A = magic(3); % initial matrix
D = [1, 2, 3; 2, 2, 3; 1, 3, 1]; % map matrix
B = zeros(3); % resultant matrix
for i = 1:3
for j = 1:3
B(D(i, j), j) = B(D(i, j), j) + A(i, j);
end
end
Now, to explain we have D as :
D = [1, 2, 3;
2, 2, 3;
1, 3, 1];
These D values basically act as the row indices to accumulate into output array B, while column indices would be iterating numbers themselves -
col = [1 2 3;
1 2 3;
1 2 3];
Thus, the summations are :
B(1,1) += A(1,1) % Indices from first row from D to select B
B(2,2) += A(1,2)
B(3,3) += A(1,3)
B(2,1) += A(2,1) % Indices from second row from D to select B
B(2,2) += A(2,2)
B(3,3) += A(2,3)
B(1,1) += A(3,1) % Indices from third row from D to select B
B(3,2) += A(3,2)
B(1,3) += A(3,3)
Thus, at B(1,1) we would accumulate two values from A : A(1,1) and A(3,1) and so on.
When the matrices are large, this process takes a long time. Is there a way to vectorize this computation?
Here's one approach using accumarray and bsxfun -
[m,n] = size(A);
% Generate all linear indices corresponding to D
idx = bsxfun(#plus,D,m*(0:n-1))
% Use accumarray to accumulate values for each of those indices.
% We need some reshaping to get a final 2D output after feeding in column
% vectors for using accumarray
Bout = reshape(accumarray(idx(:),A(:),[m*n,1]),[m,n])
Sample run -
>> % Setup inputs and run loopy version
A = randi(9,4,6);
D = randi(size(A,1),4,6);
B = zeros(size(A));
for i = 1:size(A,1)
for j = 1:size(A,2)
B(D(i, j), j) = B(D(i, j), j) + A(i, j);
end
end
% Proposed vectorized method
>> [m,n] = size(A);
>> idx = bsxfun(#plus,D,m*(0:n-1));
>> Bout = reshape(accumarray(idx(:),A(:),[m*n,1]),[m,n]);
% Verify results
>> max(abs(Bout(:)-B(:)))
ans =
0
I want to plot a function y[n] = x[n+2]. My problem is that it does not plot in right range or even does not draw the zero sample points.
n = 1:6;
x = 1:1:8;
f = figure;
subplot(1,2,1)
stem(n, x(n));
axis([-3,8, 0, 7]);
xlabel('n');
ylabel('x[n]');
title('Subplot 1')
subplot(1,2,2)
stem(n, x(n + 2));
xlabel('n');
ylabel('y[n]');
title('Subplot 2')
How to change the variables n or x to get the right plot?
In the end, it ought to look like this:
You are confusing the concept of indices with your dependent variable. You should construct a function x which transforms an input n using the relationship that you know
function y = x(n)
% Set all outputs to 0
y = zeros(size(n));
% Replace the values that fall between 0 and 6 with their same value
y(n >= 0 & n <= 6) = n(n >= 0 & n <= 6);
end
Then you should pass this function a range of n values to evaluate.
nvalues = -3:8;
yvalues = x(nvalues);
stem(nvalues, yvalues)
You can also apply a transformation to the n values
nvalues = -3:8;
yvalues = x(nvalues + 2);
stem(nvalues, yvalues)
P is an m*2 matrix of m points([X Y]), and R is an n*4 matrix of n rectangles ([X1 Y1 X2 Y2]). I want to form an m*n matrix, C, in a way that C(i, j) indicates if i-th point in P lies inside j-th rectangle in R.
Single point single rect:
This is the way I check if the point lies inside the rectangle:
c = (P(1)>=R(1))&(P(1)<=R(3))&(P(2)>=R(2))&(P(2)<=R(4));
for more readability:
c = (Px>=Rxmin)&(Px<=Rxmax))&(Py>=Rymin)&(Py<=Rymax);
In the code above I’m sure that R(1)<=R(3) and R(2)<=R(4):
R(:, [1 3]) = sort(R(:, [1 3]), 2);
R(:, [2 4]) = sort(R(:, [2 4]), 2);
Multiple points multiple rects:
It's easy to mange m*1 and 1*n cases (like second answer here). But I don't know how to vectorize m*n case. I’m currently doing it using for loops:
m = size(P, 1);
n = size(R, 1);
C = false(m, n);
for i=1:n
C(:, i) = (P(:, 1)>=R(i, 1))&(P(:, 1)<=R(i, 3))& ...
(P(:, 2)>=R(i, 2))&(P(:, 2)<=R(i, 4));
end
How can I vectorize it for more efficiency?
I suggest you work with each column of P and R individually and then bitwise & them at the end:
Px = P(:,1);
Rx1 = R(:,1).';
Rx2 = R(:,3).';
X1 = bsxfun(#ge, Px, Rx1);
X2 = bsxfun(#le, Px, Rx2);
Py = P(:,2);
Ry1 = R(:,2).';
Ry2 = R(:,4).';
Y1 = bsxfun(#ge, Py, Ry1);
Y2 = bsxfun(#le, Py, Ry2);
C = X1 & X2 & Y1 & Y2
Or if you prefer:
C = bsxfun(#ge, P(:,1), R(:,1).')& ... % X lower bound
bsxfun(#le, P(:,1), R(:,3).')& ... % X upper bound
bsxfun(#ge, P(:,2), R(:,2).')& ... % Y lower bound
bsxfun(#le, P(:,2), R(:,4).'); % Y upper bound
OP has provided this timing comparison
I wanted to compute the following matrix in Matlab:
g=[I
A
.
.
.
A^N]
I used the following program in Matlab:
A=[2 3;4 1];
s=A;
for n=1:1:50
s(n)=A.^n;
end
g=[eye(1,1),s];
I am getting the following error:
In an assignment A(I) = B, the number of elements in B and I must be the same.
Error in s_x_calcu_v1 (line 5)
s(n)=A.^n;
The problem is that you are trying to assign a matrix to a single element. In matlab calling s(n) mean you get the nth element of s, regardless of the dimensions of s. You can use a three dimensional matrix
N = 50;
A=[2 3;4 1];
[nx,ny] = size(A);
s(nx,ny,N) = 0; %makes s a nx x ny x N matrix
for n=1:1:N
s(:,:,n)=A.^n; %Colon to select all elements of that dimension
end
g=cat(3, eye(size(A)) ,s); %Add the I matrix of same size as A
Or a vectorized version
s = bsxfun(#power, A(:), 1:N);
s = reshape(s,2,2,N);
g = cat(3, eye(size(A)) ,s);
And a third solution using cumprod
s = repmat(A(:), [1 N]);
s = cumprod(s,2);
s = reshape(s,2,2,N);
g = cat(3, eye(size(A)) ,s);
Your s array is a 2-by-2 array, you cannot index it to store the result of your compuation at each step of your loop.
For this, the simpler is probably to define s as a cell:
% --- Definitions
A = [2 3;4 1];
N = 50;
% --- Preparation
s = cell(N,1);
% --- Computation
for n=1:N
s{n} = A.^n;
end
Best,
When you loop from 1 to N computing each time A.^n you are doing LOTS of redundant computations! Note that
A.^n = (A.^(n-1)).*A; %//element-wise power
A^n = (A^n) * A; %// matrix power
Therefore,
A = [2 3;4 1];
N = 50;
s = cell(N+1,1);
s{1} = eye(size(A,1));
for ii=1:N
s{ii+1} = s{ii}.*A; %// no powers, just product!
end
g = vertcat( s{:} );
BTW, the same holds if you want to compute matrix power (instead of element-wise powers), all you need is changing to s{ii+1} = s{ii}*A;