% AFFINEFUNDMATRIX - computes affine fundamental matrix from 4 or more points
%
% Function computes the affine fundamental matrix from 4 or more matching
% points in a stereo pair of images.  The Gold Standard algorithm given
% by Hartley and Zisserman p351 (2nd Ed.) is used. 
%
% Usage:   [F, e1, e2] = affinefundmatrix(x1, x2)
%          [F, e1, e2] = affinefundmatrix(x)
%
% Arguments:
%          x1, x2 - Two sets of corresponding point.  If each set is 3xN
%                   it is assumed that they are homogeneous coordinates.
%                   If they are 2xN it is assumed they are inhomogeneous.
%         
%          x      - If a single argument is supplied it is assumed that it
%                   is in the form x = [x1; x2]
% Returns:
%          F      - The 3x3 fundamental matrix such that x2'*F*x1 = 0.
%          e1     - The epipole in image 1 such that F*e1 = 0
%          e2     - The epipole in image 2 such that F'*e2 = 0
%

% Peter Kovesi
% www.peterkovesi.com
%
% Feb 2005 


function [F,e1,e2] = affinefundmatrix(varargin)
    
    [x1, x2, npts] = checkargs(varargin(:));

    X = [x2; x1];      % Form vectors of correspondences
    Xmean = mean(X,2); % Mean 

    deltaX = zeros(size(X));
    for k = 1:4
	deltaX(k,:) = X(k,:) - Xmean(k);
    end
        
    [U,D,V] = svd(deltaX',0);
    
    % Form  fundamental matrix from the column of V corresponding to
    % smallest singular value.
    v = V(:,4);
    F = [ 0    0    v(1)
	  0    0    v(2)
	 v(3) v(4) -v'*Xmean];
    
    % Solve for epipoles
    [U,D,V] = svd(F,0);
    e1 = V(:,3);
    e2 = U(:,3);
    
%--------------------------------------------------------------------------
% Function to check argument values and set defaults

function [x1, x2, npts] = checkargs(arg);
    
    if length(arg) == 2
        x1 = arg{1};
        x2 = arg{2};
        if ~all(size(x1)==size(x2))
            error('x1 and x2 must have the same size');
        elseif size(x1,1) == 3
	    % Convert to inhomogeneous coords
            x1(1,:) = x1(1,:)./x1(3,:);
            x1(2,:) = x1(2,:)./x1(3,:);	    
            x2(1,:) = x2(1,:)./x2(3,:);
            x2(2,:) = x2(2,:)./x2(3,:);	    	    
	    x1 = x1(1:2,:);   x2 = x2(1:2,:);
        elseif size(x1,1) ~= 2
	    error('x1 and x2 must be 2xN or 3xN arrays');
        end
        
    elseif length(arg) == 1
        if size(arg{1},1) == 6
            x1 = arg{1}(1:3,:);
            x2 = arg{1}(4:6,:);
	    % Convert to inhomogeneous coords
            x1(1,:) = x1(1,:)./x1(3,:);
            x1(2,:) = x1(2,:)./x1(3,:);	    
            x2(1,:) = x2(1,:)./x2(3,:);
            x2(2,:) = x2(2,:)./x2(3,:);	    	    	    
	    x1 = x1(1:2,:);   x2 = x2(1:2,:);	    
        elseif size(arg{1},1) == 4
            x1 = arg{1}(1:2,:);
            x2 = arg{1}(3:4,:);	    
	else
	    error('Single argument x must be 6xN');
        end
    else
        error('Wrong number of arguments supplied');
    end
      
    npts = size(x1,2);
    if npts < 4
        error('At least 4 points are needed to compute the affine fundamental matrix');
    end
    
