Triangulation & Direct linear transform - matlab

Following Hartley/Zisserman's Multiview Geometery, Algorithm 12: The optimal triangulation method (p318), I got the corresponding image points xhat1 and xhat2 (step 10). In step 11, one needs to compute the 3D point Xhat. One such method is Direct Linear Transform (DLT), mentioned in 12.2 (p312) and 4.1 (p88).
The homogenous method (DLT), p312-313, states that it finds a solution as the unit singular vector corresponding to the smallest singular value of A, thus,
A = [xhat1(1) * P1(3,:)' - P1(1,:)' ;
xhat1(2) * P1(3,:)' - P1(2,:)' ;
xhat2(1) * P2(3,:)' - P2(1,:)' ;
xhat2(2) * P2(3,:)' - P2(2,:)' ];
[Ua Ea Va] = svd(A);
Xhat = Va(:,end);
plot3(Xhat(1),Xhat(2),Xhat(3), 'r.');
However, A is a 16x1 matrix, resulting in a Va that is 1x1.
What am I doing wrong (and a fix) in getting the 3D point?
For what its worth sample data:
xhat1 =
1.0e+009 *
4.9973
-0.2024
0.0027
xhat2 =
1.0e+011 *
2.0729
2.6624
0.0098
P1 =
699.6674 0 392.1170 0
0 701.6136 304.0275 0
0 0 1.0000 0
P2 =
1.0e+003 *
-0.7845 0.0508 -0.1592 1.8619
-0.1379 0.7338 0.1649 0.6825
-0.0006 0.0001 0.0008 0.0010
A = <- my computation
1.0e+011 *
-0.0000
0
0.0500
0
0
-0.0000
-0.0020
0
-1.3369
0.2563
1.5634
2.0729
-1.7170
0.3292
2.0079
2.6624
Update Working code for section xi in algorithm
% xi
A = [xhat1(1) * P1(3,:) - P1(1,:) ;
xhat1(2) * P1(3,:) - P1(2,:) ;
xhat2(1) * P2(3,:) - P2(1,:) ;
xhat2(2) * P2(3,:) - P2(2,:) ];
A(1,:) = A(1,:)/norm(A(1,:));
A(2,:) = A(2,:)/norm(A(2,:));
A(3,:) = A(3,:)/norm(A(3,:));
A(4,:) = A(4,:)/norm(A(4,:));
[Ua Ea Va] = svd(A);
X = Va(:,end);
X = X / X(4); % 3D Point

As is mentioned in the book (sec 12.2), pi T are the rows of P. Therefore, you don't need to transpose P1(k,:) (i.e. the right formulation is A = [xhat1(1) * P1(3,:) - P1(1,:) ; ...).
I hope that was just a typo.
Additionally, it is recommended to normalize each row of A with its L2 norm, i.e. for all i
A(i,:) = A(i,:)/norm(A(i,:));
And if you want to plot the triangulated 3D points, you have to normalize Xhat before plotting (its meaningless otherwise), i.e.
Xhat = Xhat/Xhat(4);

A(1,:) = A(1,:)/norm(A(1,:));
A(2,:) = A(2,:)/norm(A(2,:));
A(3,:) = A(3,:)/norm(A(3,:));
A(4,:) = A(4,:)/norm(A(4,:));
Could be simplified as A = normr(A).

Related

Find rotation matrix with two vectors

I want to find the rotation matrix between two vectors.
[0;0;1] = R * [0.0023;0.0019;0.9899]
How do I find the 3*3 rotation matrix?
This is a simple rearrangement
% [0;0;1] = R * [0.0023;0.0019;0.9899];
% So ...
% [0;0;1] / [0.0023;0.0019;0.9899] = R
% This is a valid MATLAB command
R = [0;0;1] / [0.0023;0.0019;0.9899];
>> R =
[ 0 0 0
0 0 0
0 0 1.0102 ]
We can validate this result
R * [0.0023;0.0019;0.9899]
>> ans =
[0; 0; 1]
Your problem can be defined as a linear equation, say,
y = mx
where, y and x are matrices. Find m.
Solution:
m = x\y or m = mldivide(x,y)
Notice the backslash. It is not a forward slash / as Wolfie mentioned in his answer. For details see https://www.mathworks.com/help/matlab/ref/mldivide.html
Additional Details:
If x is a singular matrix, use pinv. See https://www.mathworks.com/help/matlab/ref/pinv.html for reference.

MATLAB: Solve system of linear equalities while constraining some values to be positive

A paper I'm reading contains the following theorem.
I wrote some MATLAB code to try and reproduce results that appear later in the paper, and initially it seemed to work well.
M = 6;
Sigma = [1 .5 .15 .15 0 0;
.5 1 .15 .15 0 0;
.15 .15 1 .25 0 0;
.15 .15 .25 1 0 0;
0 0 0 0 1 .1;
0 0 0 0 .1 1];
Delta = [0 0 .2 .2 .5 .5]';
cov_vect = [.3 .3 .35 .35 .25 .25];
u = ones(M,1);
lastcol = [u' 0];
First = Sigma+(Delta*Delta');
First(M+1,:) = u;
First(:,M+1) = lastcol;
Third = [cov_vect 1]';
X = linsolve(First,Third);
This code creates results that match those from the paper.
I want to use my code with other data sets, but when I try to do that I encounter a problem. M, Sigma, Delta, and cov_vect will vary from data set to data set, but the rest of the code should stay the same.
When I use my code on new data sets, then although the vector w sums to 1 (as it should) it sometimes contains negative values. According to the paper, this shouldn't happen. It's fine for lambda to be negative, but none of the values in the w vector can be negative.
How can I get MATLAB to constrain the results so that all the values in w must be positive, while maintaining the requirement that the vector w sum to 1?
Your question appears to reference this paper.
Theorem 2 you reference is the solution to the following optimization problem (see error/typos section, I've had to make at least one correction).
minimize (over w) w' * (Sigma + delta * delta') * w - 2 * cov_vect' * w
subject to: w'*ones(n, 1) = 1
This can be solved using Matlab function quadprog with:
H = 2 * (Sigma + delta * Delta'); % see quadprog docs, it solves 1/2 so we need 2
f = - 2 * cov_vect;
A = [];
b = [];
Aeq = ones(1,6);
beq = 1;
w = quadprog(H, f, A, b, Aeq, beq);
You can add the lower bound constraint of 0 with:
lb = zeros(6, 1);
ub = [];
w2 = quadprog(H, f, A, b, Aeq, beq, lb, ub);
How to solve this in CVX (awesome optimization package)
cvx_begin
variables y(n);
minimize(y' * (Sigma + Delta * Delta') * y - 2 * cov_vect * y)
subject to:
y'*ones(n,1) == 1;
y >= 0;
cvx_end
Link to cvx.
Typo in appendix of paper as posted on researchgate:
(typo) Their proof of theorem 2 omits the 2*w in term 2*cov_vect' * w of thier objective function. The minimization problem should be:
minimize (over w) w' * (Sigma + delta * delta') * w - 2*cov_vect' * w
Which indeed gives solution:
0.1596 0.1596 0.2090 0.2090 0.1314 0.1314
Or equivalently:
minimize (over w) .5 * w' * (Sigma + delta * delta') * w - cov_vect' * w

Create a relation matrix from a sequence (Matlab)

I have a sequence S :
S= 'ABCD' % which means A<B<C<D
I want to convert S into a matrix M[i,j] which have to satisfy those conditions :
M[i,j] , M[j,i] are random
M[i,i] =0.5
M[i,j] + M[j,i] = 1
M[i,j] < M[j,i] % For example: if A<B then M[A,B] < M[B,A]
For example: if we have S = 'ABCD', the M matrix will be expected as follows:
A B C D
A o.5 0.25 0.2 0.1
B 0.75 0.5 0.35 0.15
C 0.8 0.65 0.5 0.4
D 0.9 0.85 0.6 0.5
How to create that kind of above matrix from a given sequence ?
From your question it appears you want
to fill the lower part of the matrix with random entries uniformly distributed on the interval (0,0.5) (so that condition 4 in your question be satisfied);
the upper part is then computed according to condition 3; and
the diagonal is determined by condition 2.
You can do that as follows:
n = 4; %// size
M = NaN(n); %// preallocate
M(1:n+1:end) = 0.5; %// fill diagonal
ind_lower = tril(true(n), -1); %// logical index for lower part
M(ind_lower) = 0.5*rand(n*(n-1)/2, 1); %// fill lower part
M_aux = NaN(n); %// auxiliary variable to fill upper part
M_aux(ind_lower) = 1-M(ind_lower).';
M_aux = M_aux.';
M(ind_lower.') = M_aux(ind_lower.'); %// fill upper part
Example result:
M =
0.5000 0.5214 0.7573 0.5999
0.4786 0.5000 0.9291 0.7891
0.2427 0.0709 0.5000 0.5421
0.4001 0.2109 0.4579 0.5000
Here's another similar approach:
n = 4;
M = tril(rand(n)*0.5, -1);
P = triu(1-M.', 1);
M = M + P + eye(n)*0.5;
Result:
M =
0.500000 0.987433 0.711005 0.944642
0.012567 0.500000 0.782633 0.902365
0.288995 0.217367 0.500000 0.783708
0.055358 0.097635 0.216292 0.500000

MATLAB - How to plot recursion values?

I have a simple function f(x) = 2.5x * (1-x) that I want to plot values for recursively. I succeeded in developing a recursive function but do not know how to extract the recursion values to plot on a graph.
function [y] = orbits(x)
y = 2.5 * x .* (1 - x);
if x == 0
y = 0;
else
y = orbits(y)
end
I would largely appreciate any help as I am new to programming and being able to plot this function would help me a lot.
e.g.
>> orbits(0.1)
x =
0.1000
x =
0.2250
x =
0.4359
x =
0.6147
How do I extract those x's to plot?
Thanks!
You need to aggregate the results over the recursion. I added another parameter epsilon which determines when to stop the recursion:
function [y] = orbits(x,epsilon)
y = 2.5 * x .* (1 - x);
if abs(y-x) < epsilon
y = [];
else
y = [y,orbits(y,epsilon)];
end
end
Demo:
>>values = orbits(0.1,0.00001)
values =
Columns 1 through 10
0.2250 0.4359 0.6147 0.5921 0.6038 0.5981 0.6010 0.5995 0.6002 0.5999
Columns 11 through 15
0.6001 0.6000 0.6000 0.6000 0.6000
>>plot(values)

MATLAB: How to calculate (on) Submatrices without a loop

I want to split a matrix columnwise into 3 segments and do a calculation on it (mean()). Is there a way to get this without a for-loop, as I did in this provided sample?
M = [2 4 9; 50 50 200; 30 0 0];
M = [M 10*M]
N = length(M);
seg = 3 % split in lets say 3 parts
segLen = round(N/seg)
segBeg = (((1:seg)-1) * segLen)+1 % start indices
segEnd = segBeg + segLen -1 % end indices
for i = 1: length(segBeg)
mean(M(:,segBeg(i):segEnd(i)),2)
end
Thank you!
Think outside the box: use the 3rd dimension:
r=reshape(M,size(M,1),segLen,[])
squeeze(mean(r,2))
The first line produces a 3d array with the first matrix at r(:,:,1), the second at r(:,:,2), ... (use M(:,1:seg*segLen) instread of M if the number of columns is not divisible by segLen).
mean(r,2) produces a nrows-by-1-by-seg array, squeeze makes a nrows-by-seg matrix out of it again.
You can use arrayfun together with cell2mat
result = cell2mat(arrayfun(#(x,y) mean(M(:,x:y),2), segBeg, segEnd,...
'UniformOutput', false))
This results in
result =
1.0e+03 *
0.0030 0.0145 0.0650
0.0500 0.3500 1.2500
0.0150 0.1500 0
where each column represents the mean across one submatrix.
Another solution using blockproc (like suggested by #DennisJaheruddin in the comments) could look like this
myFun = #(x) mean(x.data,2);
result2 = blockproc(M, [N, segLen], myFun)
This also results in
result2 =
1.0e+03 *
0.0030 0.0145 0.0650
0.0500 0.3500 1.2500
0.0150 0.1500 0
Note that blockproc can take advantage of parallel processing if the flag 'UseParallel' is set to true, i.e., result2 = blockproc(M, [N, segLen], myFun, 'UseParallel', true)
You can do for your example case
mean1 = mean(M(:,1:segLen))
mean2 = mean(M(:,segLen+1:N-segLen-1))
mean3 = mean(M(:,N-segLen:end))