I'm currently trying to compute the camera matrix P given a set of world points (X) with its corresponding image points (x). However, when testing for the the result, P (3 x 4 camera matrix) multiplying by the world points does not give me the correct corresponding image points. However, only the first column of PX = x. The other column won't return the approximate image points.
Code:
X = [1 2 3; 4 5 6; 7 8 9; 1 1 1];
x = [3 2 1; 6 5 4; 1 1 1];
[mX, nX] = size(X);
[mx, nx] = size(x);
for i = 0:(nX-1)
XX{i+1} = transpose(X(1+i: 4+i));
end
for i = 0:(nx-1)
xx{i+1} = transpose(x(i+1:3+i));
end
%TODO - normalization
A = [];
%construct matrix
for i = 1:nX
A = [A; zeros(1,4) -1*(xx{i}(3)*transpose(XX{i})) xx{i}(2)*transpose(XX{i})];
A = [A; xx{i}(3)*transpose(XX{i}) zeros(1,4) -1*xx{i}(1)*transpose(XX{i})];
end
%using svd to solve for non zero solution
[u s v] = svd(A);
p = v(:, size(v,2));
p = reshape(p, 4,3)';
output for the first column, works good:
>> p*XX{1}
ans =
0.0461
0.0922
0.0154
>> ans/0.0154
ans =
2.9921
5.9841
0.9974
>> xx{1}
ans =
3
6
1
output for the second column, doesn't work:
>> p*XX{2}
ans =
0.5202
0.0867
0.1734
>> ans/0.1734
ans =
2.9999
0.5000
1.0000
>> xx{2}
ans =
6
1
2
By the way, I was told that I need to normalize the world points and image points before I compute the camera matrix. I have not done this step and have no idea how to. If this is causing the issue, please explain what can be done. Thank you in advance.
This is because you aren't indexing into the matrix properly. You are using linear indexing to access each column of the matrix. In that case, your for loop needs to access each column independently. Therefore, each iteration of your for loop must access groups of 4 elements for your 3D points and groups of 3 elements for your 2D points.
As such, you simply need to do this for your for loops:
for i = 0:(nX-1)
XX{i+1} = transpose(X(4*i + 1 : 4*(i + 1)));
end
for i = 0:(nx-1)
xx{i+1} = transpose(x(3*i + 1 : 3*(i + 1)));
end
After this, the code should work no problem. To verify, we can loop through each 3D point and determine its 2D equivalent as you're using cells:
out = zeros(size(xx)); % Declare output matrix
for ii = 1 : numel(XX) % For each 3D point...
out(:,ii) = p * XX{ii}; % Transform the point
out(:,ii) = out(:,ii) / out(end,ii); % Normalize
end
We thus get:
>> out
out =
3.0000 2.0000 1.0000
6.0000 5.0000 4.0000
1.0000 1.0000 1.0000
Compare with your x:
>> x
x =
3 2 1
6 5 4
1 1 1
Suggestion - Use vectorization
If I can suggest something, please do not use cell arrays here. You can create the matrix of equations for solving using vectorization. Specifically, you can create the matrix A directly without any for loops:
A = [zeros(N, 4) -X.' bsxfun(#times, x(2,:).', X.');
X.' zeros(N, 4) bsxfun(#times, -x(1,:).', X.')];
If you own MATLAB R2016b and up, you can do this with internal broadcasting:
A = [zeros(N, 4) -X.' x(2,:).' .* X.';
X.' zeros(N, 4) -x(1,:).' .* X.']
Note that you will see the rows are shuffled in comparison to your original matrix A because of the vectorization. Because we are solving for the null space of the matrix A, shuffling the rows has no effect. Therefore, your code can be simplified to:
X = [1 2 3; 4 5 6; 7 8 9; 1 1 1];
x = [3 2 1; 6 5 4; 1 1 1];
A = [zeros(N, 4) -X.' bsxfun(#times, x(2,:).', X.');
X.' zeros(N, 4) bsxfun(#times, -x(1,:).', X.')];
% Use this for MATLAB R2016b and up
% A = [zeros(N, 4) -X.' x(2,:).' .* X.';
% X.' zeros(N, 4) -x(1,:).' .* X.']
[u, s, v] = svd(A);
p = v(:, end);
p = reshape(p, 4, 3).';
To finally compute the output matrix, you can just use simple matrix multiplication. The fact that you are using cells requires that you have to use a for loop and it's much faster to do this with matrix multiplication:
out = p * X;
You can then take the last row of the results and divide each of the other rows by this row.
out = bsxfun(#rdivide, out, out(end,:));
Again with MATLAB R2016b and up, you can just do it as so:
out = out ./ out(end,:);
Related
I have computed variables stored in a matrix for a specific time vector.
Now I want to interpolate between those whole matrices for a new time vector to get the matrices for the desired new time vector.
I've came up with the following solution but it seems clunky and computational demanding:
clear all;
a(:,:,1) = [1 1 1;2 2 2;3 3 3]; % Matrix 1
a(:,:,2) = [4 4 4;6 6 6;8 8 8]; % Matrix 2
t1 = [1 2]; % Old time vector
t2 = [1 1.5 2]; % New time vector
% Interpolation for each matrix element
for r = 1:1:size(a,2)
for c = 1:1:size(a,1)
tab(:) = a(r,c,:);
tabInterp(r,c,:) = interp1(t1,tab(:),t2);
end
end
The result is and should be:
[2.5000 2.5000 2.5000
4.0000 4.0000 4.0000
5.5000 5.5000 5.5000]
Any thoughts?
You can do the linear interpolation manually, and all at once...
m = ( t2 - t1(1) ) / ( t1(2) - t1(1) );
% Linear interpolation using the standard 'y = m*x + c' linear structure
tabInterp = reshape(m,1,1,[]) .* (a(:,:,2)-a(:,:,1)) + a(:,:,1);
This will work for any size t2, as long as t1 has 2 elements.
If you have a t1 with more than 2 elements, you can create the scaling vector m using interp1. This is relatively efficient because you're only using interp1 for your time vector, not the matrix:
m = interp1( t1, (t1-min(t1))/(max(t1)-min(t1)), t2, 'linear', 'extrap' );
This uses implicit expansion with the .* operation, which requires R2016b or newer. If you have an older MATLAB version then use bsxfun for the same functionality.
I don't really see a problem with a loop based approach, but if you're looking for a loopless method you can do the following.
[rows, cols, ~] = size(a);
aReshape = reshape(a, rows*cols, []).';
tabInterp = reshape(interp1(t1, aReshape, t2).', rows, cols, []);
Looking at the source code for interp1 it appears a for loop is being used anyway so I doubt this will result in any performance gain.
In matlab, I would like to multiply M vectors using L matrices, resulting with M x L new vectors. Specifically, say I have a matrix A of size N x M and a matrix B of size N x N x L matrix, I would like to calculate a matrix C of size N x M x L where the result is exactly like the following slow code:
for m=1:M
for l=1:L
C(:,m,l)=B(:,:,l)*A(:,m)
end
end
but do achieve this efficiently (using native code rather than matlab looping).
We could ab-use fast matrix-multiplication here, just need to rearrange dimensions. So, push back the second dimension of B to the end and reshape to 2D such that the first two dims are merged. Perform matrix-multiplication with A to give us a 2D array. Let's call it C. Now, C's first dim were the merged dims from B. So, split it back into their original two dim lengths with reshaping resulting in a 3D array. Finally push back the second dim to the back with one more permute. This is the desired 3D output.
Hence, the implementation would be -
permute(reshape(reshape(permute(B,[1,3,2]),[],N)*A,N,L,[]),[1,3,2])
Benchmarking
Benchmarking code :
% Setup inputs
M = 150;
L = 150;
N = 150;
A = randn(N,M);
B = randn(N,N,L);
disp('----------------------- ORIGINAL LOOPY -------------------')
tic
C_loop = NaN(N,M,L);
for m=1:M
for l=1:L
C_loop(:,m,l)=B(:,:,l)*A(:,m);
end
end
toc
disp('----------------------- BSXFUN + PERMUTE -----------------')
% #Luis's soln
tic
C = permute(sum(bsxfun(#times, permute(B, [1 2 4 3]), ...
permute(A, [3 1 2])), 2), [1 3 4 2]);
toc
disp('----------------------- BSXFUN + MATRIX-MULT -------------')
% Propose in this post
tic
out = permute(reshape(reshape(permute(B,[1,3,2]),[],N)*A,N,L,[]),[1,3,2]);
toc
Timings :
----------------------- ORIGINAL LOOPY -------------------
Elapsed time is 0.905811 seconds.
----------------------- BSXFUN + PERMUTE -----------------
Elapsed time is 0.883616 seconds.
----------------------- BSXFUN + MATRIX-MULT -------------
Elapsed time is 0.045331 seconds.
You can do it with some permuting of dimensions and singleton expansion:
C = permute(sum(bsxfun(#times, permute(B, [1 2 4 3]), permute(A, [3 1 2])), 2), [1 3 4 2]);
Check:
% Example inputs:
M = 5;
L = 6;
N = 7;
A = randn(N,M);
B = randn(N,N,L);
% Output with bsxfun and permute:
C = permute(sum(bsxfun(#times, permute(B, [1 2 4 3]), permute(A, [3 1 2])), 2), [1 3 4 2]);
% Output with loops:
C_loop = NaN(N,M,L);
for m=1:M
for l=1:L
C_loop(:,m,l)=B(:,:,l)*A(:,m);
end
end
% Maximum relative error. Should be 0, or of the order of eps:
max_error = max(reshape(abs(C./C_loop),[],1)-1)
Lets say I have matrices A = [1 2; 3 4], B = [4 3; 2 1]. I want to multiply each column from matrix A ([1; 3], [2; 4]) by the corresponding row in matrix B ([4 3], [2 1]) and sum resulting matrices. I have came up with the following code:
C = zeros(size(A));
for i = 1 : size(A, 1)
C = C + A(:, i) * B(i, :);
end
Could it be rewritten using some math trick or matlab function to get rid of the for loop?
I see there is unclarity in my question regarding the result I want. The result should exactly mimic provided Matlab code, therefore I seek one matrix which is given by the matrix summation of the intermediate matrices that are created by multiplying each column vector with corresponding row vector from both matrices. For this specific example, it would be given by
C = A(:, 1) * B(1, :) + A(:, 2) * B(2, :);
I am just looking for some generic, for-loop less version for any matrices of compatible dimensions.
I just tried out my suggestion in the comments, and it seems to work with this octave tester:
Short form (only works in Octave):
A = [1 2; 3 4], B = [4 3; 2 1]
X = sum((A * B)(:))
Long form (Matlab):
A = [1 2; 3 4]
B = [4 3; 2 1]
C = A * B % Stop here if you want the exact result from your Matlab code
x = sum(C(:)) % To get the sum of the resulting matrix
Sources:
https://www.tutorialspoint.com/matlab/matlab_matrix_multiplication.htm
https://www.mathworks.com/matlabcentral/newsreader/view_thread/51252
Update, based on your update:
Output of A * B:
8 5
20 13
Output of your code:
8 5
20 13
It appears that
C = zeros(size(A));
for i = 1 : size(A, 1)
C = C + A(:, i) * B(i, :);
end
is equivalent to the matrix multiplication
C = A*B
for square matrices A and B.
You can also do this in MATLAB, to get the sum.
C=ones(1,2)*A*B*ones(2,1)
The general form would be
C=ones(1,size(A,1))*(A*B)*ones(size(B,2),1);
EDIT
I see you updated your question for clarity. The matrix product can be calculated directly
C = A*B;
as pointed out by jodag.
This works provided you follow rules of linear algebra where inner dimensions of your matrices are the same (i.e. when number of columns in A match the number of rows in B; size(A,2)==size(B,1)).
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;
I have two M by N matrices, labeled A and B, and I would like to create a vector containing the sum of all items in B for each unique value of A. For example I have the following matrices:
A = [6 2 3 4
5 2 3 3
5 5 6 2];
B = [.2 .5 .4 .1
.7 .2 .5 .1
.6 .6 .1 .9];
and I would like to create a vector C where each index corresponds to the index of D = unique(A). So for this case, D = [2,3,4,5,6] and
for I = 1:length(D)
C(I) = sum(B(A(:)==D(I));
end
This gets really slow when D is 2000 items long and A and B both are ~4000x20 matrices. Any help on speeding this up? I tried doing the following:
Indxs = bsxfun(#eq,A,reshape(D,1,1,length(D)));
for I = 1:size(Indxs,3)
C(I) = sum(B(Indxs(:,:,I));
end
but it's not really any faster.
You can do this with the usual combination of unique and accumarray:
[D, ~, uA] = unique(A(:));
C = accumarray(uA, B(:));
With your example data, the result is:
>> D
D =
2
3
4
5
6
>> C
C =
1.6000
1.0000
0.1000
1.9000
0.3000
What accumarray does, in its most basic form (two input arguments), is to sum all second-argument values that have the same first-argument value.
If A contains only positive integers, there's a one-line solution with sparse and find:
[D, ~, C] = find(sparse(A(:), 1, B(:)));
This works because sparse accumulates values corresponding to the same index.