% Rank based factorization algorithm for multiview reconstruction  
% using points and lines features and their incidence relations
% as described in Chapter 9, "An introduction to 3-D Vision"
% by Y. Ma, S. Soatto, J. Kosecka, S. Sastry (MASKS)
% Code distributed free for non-commercial use
% Copyright (c) MASKS, 2003

% Generates multiple synthetic views of a house and computes the 
% motion and structure, calibrated case, point features only
% Jana Kosecka, George Mason University, 2002
% ==============================================================

close all; clear;
FRAMES = 3;
PLOTS  = 3;
     
% cube in the object frame
 XW = [0 1 1 0 0 1 1 0 0.2 0.8 0.2 0.8 ;
       0 0 1 1 0 0 1 1 1.5 1.5 1.5 1.5;
       1 1 1 1 0 0 0 0 0.8 0.8 0.2 0.2 ;
       1 1 1 1 1 1 1 1 1   1   1   1];

NPOINTS = 12;
XC = zeros(4,NPOINTS,FRAMES);

% initial displacement
Rinit = rot_matrix([1 1 1],0); 
Zinit = 5;
Tinit = [ Rinit(1,:) -0.5 ;
          Rinit(2,:) -0.5 ;
          Rinit(3,:) Zinit;
          0 0 0 1];
% first camera coodinates 
XC(:,:,1) = Tinit*XW;
Zmax = max(XC(3,:,1));
Zmin = min(XC(3,:,1));
rinc =   pi/30;
rot_axis = [1 0 0; 0 -1 0]';
trans_axis = [1 0 0; 0 1 0]';

figure; 
subplot(1,PLOTS,1); hold on;
plot3_struct(XC(1,:,1),XC(2,:,1),XC(3,:,1));
plot3(XC(1,:,1),XC(2,:,1),XC(3,:,1),'*');
draw_frame_scaled([diag([1,1,1]), zeros(3,1)],0.5);
title('original motion and 3D structure');
view(220,20);
grid on; axis equal;

% retinal coordinates
xr(:,:,1) = project(XC(:,:,1));
subplot(1,PLOTS,2); hold on;
plot(xr(1,:,1),xr(2,:,1),'.');
[lim(:,:,1), inc] = houselines_mixed(xr(1:2,:,1));
plotLines(lim(:,:,1));
title('retinal coordinates'); axis equal;
% pause;

ratio = 1;
rinc = 10;  % rotation increment 20 degrees
Zmid = (Zmax+Zmin)/2;
tinc = 0.5*ratio*Zmid*rinc*pi/180; 

for i=2:FRAMES
    theta = (i-1)*rinc*pi/180;
    r_axis = rot_axis(:,i-1)/norm(rot_axis(:,i-1));
    t_axis = trans_axis(:,i-1)/norm(trans_axis(:,i-1));
    T_ini(:,i) = (i-1)*tinc*t_axis;
    R = rot_matrix(r_axis,theta);
    % translation represents origin of the camera frame in the world frame 
    T(:,:,i) = ([ R T_ini(:,i);
                 0 0 0 1]);
    XC(:,:,i) = T(:,:,i)*XC(:,:,1);  % XW;
    xr(:,:,i) = project(XC(:,:,i));
    [lim(:,:,i),inc] = houselines_mixed(xr(1:2,:,i));

    subplot(1,PLOTS,1); hold on;
    draw_frame_scaled(T(:,:,i),0.5); axis equal; 
    subplot(1,PLOTS,2);  
    plot(xr(1,:,i),xr(2,:,i),'.'); axis equal;
    plotLines(lim(:,:,i));
end;

lnum = size(lim,2);
for i = 1:FRAMES
  for j = 1:lnum 
    l =  skew([lim(1:2,j,i);1])*[lim(3:4,j,i); 1];
    ln(:,j,i) = l/norm(l);
  end
end

%------------------------------------------------------------
% MOTION ESTIMATION - given m view n lines - estimate motion 
for i = 1:FRAMES
    [lim(:,:,i),inc] = houselines_mixed(xr(1:2,:,i));      
end

%---------------------------------------------------------------------
% compute initial \alpha's for each point using first two frames only
[vel,R0]  = essentialDiscrete(xr(:,:,1),xr(:,:,2));
R_ini(:,:,2) = R0;
T_ini(:,2) = vel;

%-----------------------------------------
% compute alpha's using initial estimates
for i = 1:NPOINTS
    alpha(:,i) = -(skew(xr(:,i,2))*T_ini(:,2))'*(skew(xr(:,i,2))*R_ini(:,:,2)* ...
                  xr(:,i,1))/(norm(skew(xr(:,i,2))*T_ini(:,2)))^2;
    lambda(:,i) = 1/alpha(:,i);
end
scale = norm(alpha(:,1));     % set the global scale
alpha = alpha/scale;          % normalize everything

%---------------------------------------------------------------------
% Compute initial motion estimates for all frames
% Here do 3 iterations - in real setting look at the change of scales 

iter = 1;
while (iter < 5)
     for j = 2:FRAMES
	 P = []; %  setup matrix P
	for i = 1:NPOINTS
	    a = [lambda(:,i)* kron(skew(xr(:,i,j)), ...
                        xr(:,i,1)') skew(xr(:,i,j))];
            P = [P; a];
            % add all the entries of lines incident to the point
            sl = size(inc(i).lines,2);
        end;
        % pause
        [um, sm, vm] = svd(P);
	Ti = vm(10:12,12);
	Ri = transpose(reshape(vm(1:9,12)',3,3));
	[ur,sr,vr] =  svd(Ri);
	Rhat(:,:,j) = sign(det(ur*vr'))*ur*vr';
        Ti = sign(det(ur*vr'))*Ti/((det(sr))^(1/3));
        scaled_init_T = T_ini(:,j)/scale;
        That(:,j) = Ti;
        True = T(1:3,4,j);
     end
       
     %---------------------------------------
     % recompute alpha's based on all views
     lambda_prev = lambda;
     for i = 1:NPOINTS
        M = [];  % setup matrix M
        for j = 2:FRAMES       % set up Hl matrix for all m views
	     a = [skew(xr(:,i,j))*Rhat(:,:,j)*xr(:,i,1) skew(xr(:,i,j))*That(:,j)];
             M = [M; a];
             sl = size(inc(i).lines,2);
             for s = 1:sl
               li = inc(i).lines(s);
               a = [ln(:,li,j)'*Rhat(:,:,j)*xr(:,i,1) ln(:,li,j)'*That(:,j)];
               M = [M; a];
             end;
         end;
         lambda(:,i) = -M(:,1)'*M(:,2)/norm(M(:,1))^2;
      end;
      scale = norm(lambda(:,1));   % set the global scale
      lambda = lambda/scale;     % normalize everything
      iter = iter + 1;
end % end while iter

% final structure with respect to the first frame
XF = [lambda.*xr(1,:,1);
      lambda.*xr(2,:,1);
      lambda.*xr(3,:,1)];

subplot(1,PLOTS,3); hold on;
plot3(XF(1,:,1),XF(2,:,1),XF(3,:,1),'r*');
plot3_struct(XF(1,:,1), XF(2,:,1), XF(3,:,1));
title('recovered structure');
view(220,20);
grid on; axis equal;

