function [I, H] = panoramaGS(varargin)
%panorama Gold Standard error, n input, up to 
%10 correspondences between any two images.
%Given some number of image filenames.
%Images in jpg format.
%Displays two images at a time and collects
%point correspondences from the user.
%The first image input is the reference, and
%the panorama is the reference after that.
%reference: Hartley Zisserman p. 98.
disp ('Select a point on the second image, and a corresponding point on the first.');
disp ('Continue to select corresponding points, one at a time. Press return when done.');
disp ('The result panorama will be displayed.');
disp ('Then select points from the third image and the panorama as before.');
disp ('Continue until tired.  When all inputs have been registered, the program');
disp ('will return the resultant panorama image.');
global INITIAL_X
%if nargin < 2
%    error('too few arguments');
%else
     n = nargin;
%end

iptsetpref 'ImshowAxesVisible' on;
h = zeros(n);
I = zeros(700,700,3);

    CI = imread(sprintf('%s',varargin{1}),'jpg');
    dim = size(CI);
    I(1:dim(1),1:dim(2),:) = CI;
    h(1) = figure('Name',varargin{1}); 
    imshow(varargin{1}, 'truesize');
    I = I./255;


for cur_im = 2:n
%*******************
%Every iteration begins with much of the code from the 
%normalized DLT.  This serves that the initialization
%for the Gold Standard.
%*******************
    
%Load next image.
disp(sprintf('%s points', varargin{cur_im}));
CI = imread(sprintf('%s',varargin{cur_im}),'jpg');
cdim = size(CI);
Cur_Image = zeros(cdim(1),cdim(2),3);
Cur_Image(:,:,:) = CI;

%display it.
h(cur_im) = figure('Name',varargin{cur_im});
imshow(CI, 'truesize');

%select points in this image.
[xs,ys] = ginput;    %x's
numxs = size(xs);
for i = 1:numxs(1)
    disp(sprintf('(%f , %f)', xs(i), ys(i)));
end

%and select points on the reference image.
disp('Reference image points.');
figure(h(1));
[xpp,ypp] = ginput(numxs(1));  %x primes.
for i = 1:numxs(1)
    disp(sprintf('(%f , %f)', xpp(i), ypp(i)));
end

%xpp = [150 250 250 150]';
%ypp = [100 100 200 200]';
%xs = [100 200 200 100]';
%ys = [100 100 200 200]';

disp('Points have been selected.');
%Now build and solve the system.
xp = [ypp'; xpp'];  % ones(1,4)];
x = [ys'; xs'];     % ones(1,4)];

%Normalize the points and compute T and T'. 
%x first
x_backup = x;   %for the sampson correction.
mx = mean(x');
x(1,:) = x(1,:)-mx(1);
x(2,:) = x(2,:)-mx(2);
%centroid is origin. Now sum of squares should
%average 2.
s = mean(sum(x.^2));
s = sqrt(2/s);
x = x*s;
T = [[s 0 0]' [0 s 0]' [-s*mx(1) -s*mx(2) 1]']; 
%Now the average magnitude is sqrt(2);
%same for xp.
xp_backup = xp;
mx = mean(xp');
xp(1,:) = xp(1,:)-mx(1);
xp(2,:) = xp(2,:)-mx(2);
%centroid is origin. Now sum of squares should
%average 2.
s = mean(sum(xp.^2));
s = sqrt(2/s);
xp = xp*s;
TP = [[s 0 0]' [0 s 0]' [-s*mx(1) -s*mx(2) 1]']; 

x(3,:) = ones(1,numxs(1));
xp(3,:) = ones(1,numxs(1));


%**********IMPORTANT***
%We need to make the below building of A more robust,
%to account for when wi' is 0...
%*******************

A = zeros(2*numxs(1),9);
for i = 2:2:2*numxs(1)
    A(i-1,:) = [zeros(1,3) -xp(3,i/2)*x(:, i/2)' xp(2,i/2)*x(:, i/2)'];
    A(i,:) = [xp(3,i/2)*x(:, i/2)' zeros(1,3) -xp(1,i/2)*x(:, i/2)'];
end

%Now svd
[U,S,V] = svd(A'*A);
%[U,S,V] = svd(A);
HN = [V(1:3,9)'; V(4:6,9)'; V(7:9,9)'];
%Now denormalize the homography.
H = inv(TP)*HN*T;
hvec = [H(1,:) H(2,:) H(3,:)]';
%H is the better DLT homography.

%H is the initial estimate of H.  Now compute
%the Sampson correction to the x's.

%The only way I can think to initialize the subsidiary
%variables xi hats, as per p.98, is to specify the J matrix
%of p.83, the Sampson correction. To me J is specified only
%explicitly; it's a 2x4 matrix...
x = x_backup;
xp = xp_backup;
x(3,:) = ones(1,numxs(1));
xhat = x;
xp(3,:) = ones(1,numxs(1));
%Remake an unnormalized A.
A = zeros(2*numxs(1),9);
for i = 2:2:2*numxs(1)
    A(i-1,:) = [zeros(1,3) -xp(3,i/2)*x(:, i/2)' xp(2,i/2)*x(:, i/2)'];
    A(i,:) = [xp(3,i/2)*x(:, i/2)' zeros(1,3) -xp(1,i/2)*x(:, i/2)'];
end

for i = 1:numxs(1)
    %X(1:2,i) = x(:,i);
    %X(3:4,i) = xp(:,i);     %The x and xp are still (y,x) pairs from above.
    J = [(-hvec(4)+xp(2,i)*hvec(7)) (-hvec(5)+xp(2,i)*hvec(8)) 0 (x(1:2,i)'*hvec(7:8)+hvec(9));
        (hvec(1)-xp(1,i)*hvec(7)) (hvec(2)-xp(1,i)*hvec(8)) (-(x(1:2,i)'*hvec(7:8)+hvec(9))) 0];
    
    %Del(x) = -J'inv(JJ')e, where e is Ai*h
    e = A((2*i-1):2*i,:)*hvec;
    Delx = -J'*inv(J*J')*e;
    %Delx solves for both x,y and x',y' corrections.
    %for the gold standard though, initialization may
    %as well be just on x,y (xi hat).
    %This is why Marc said error in one image is fine...d'oh.
    xhat(1:2,i) = x(1:2,i) + Delx(1:2);
end

%Now we want to minimize 

INITIAL_X = x;

x0 = [xhat(1,:) xhat(2,:)];
x0(2*numxs(1)+1:2*numxs(1)+9) = hvec';
x0 = x0';
%x0 is the initialization of the variables over which we will
%optimize.  The H is all we really want at the end, though we
%optimized over x-hat as well.

x = lsqnonlin(@goldstandardcost, x0);
%x = fminsearch(@reduced_standard,x0);


H = [x(2*numxs(1)+1:2*numxs(1)+3)'; x(2*numxs(1)+4:2*numxs(1)+6)'; x(2*numxs(1)+7:2*numxs(1)+9)'];
%H is the GOLD STANDARD homography this time.


minn = [0 0];  maxx = dim(1:2);
box = H*[[1 1 1]' [cdim(1) 1 1]' [1 cdim(2) 1]' [cdim(1:2) 1]'];
box(1,:) = box(1,:)./box(3,:);
box(2,:) = box(2,:)./box(3,:);

mins = min(box');
maxs = max(box');
if (mins(1) < minn(1)) 
    minn(1) = mins(1);
end
if (mins(2) < minn(2))
    minn(2) = mins(2);
end
if (maxs(1) > maxx(1))
    maxx(1) = maxs(1);
end
if (maxs(2) > maxx(2))
    maxx(2) = maxs(2);
end

%map the base image.
maxx = ceil(maxx);
minn = floor(minn);

newdims = [(maxx(1)-minn(1)) (maxx(2)-minn(2))];
newI = zeros(newdims(1),newdims(2),3);
for i = 1:dim(1)
    for j = 1:dim(2)
        newI(i-minn(1),j-minn(2),:) = I(i,j,:);
    end
end
dim = size(newI);
        
%overwrite parts and add the warped image to it.
%for i = 1:cdim(1)
%    for j = 1:cdim(2)
%        mapp = H*[i j 1]';
%        mapp(1:2) = mapp(1:2)/mapp(3);
%        if (mapp(1) >= minn(1)) & (mapp(1) <= maxx(1)) & (mapp(2) >= minn(2)) & (mapp(2) <= maxx(2))
%            newI(ceil(mapp(1))-minn(1),ceil(mapp(2))-minn(2),:) = Cur_Image(i,j,:)/255;
%        end
%    end
%end

%instead of the above forward projection, do a backward projection,
%to avoid the gridding.

HI = inv(H);
%points = zeros(3,newdims(1)*newdims(2));
%[X,Y] = meshgrid(1:newdims(1),1:newdims(2));
%c = 0;
%for i = X
%    for j = Y
%        points(1:2,(c+1):(c+newdims(2))) = [i';j'];
%        c = c + newdims(2);
%    end
%end
        
for i = 1:newdims(1)
    for j = 1:newdims(2)
        mapp = HI*[(i+minn(1)) (j+minn(2)) 1]';
        mapp(1:2) = mapp(1:2)/mapp(3);
        if (mapp(1) >= 1) & (mapp(1) <= cdim(1)) & (mapp(2) >= 1) & (mapp(2) <= cdim(2))
            newI(i,j,:) = Cur_Image(round(mapp(1)),round(mapp(2)),:)/255;
        end
    end
end

I = newI;

%update the referece image.

figure(h(1)), imshow(I, 'truesize');
close(h(cur_im));

end

X = [x(1:numxs(1))'; x((numxs(1)+1):2*numxs(1))'; ones(1,numxs(1))];
%The total reprojection error.
PreImerr = (INITIAL_X - X);
PostImerr1 = H*INITIAL_X;
PostImerr1(1,:) = PostImerr1(1,:)./PostImerr1(3,:);
PostImerr1(2,:) = PostImerr1(2,:)./PostImerr1(3,:);
PostImerr = H*X;
PostImerr(1,:) = PostImerr(1,:)./PostImerr(3,:);
PostImerr(2,:) = PostImerr(2,:)./PostImerr(3,:);
PostImerr = PostImerr1 - PostImerr;

total_reproj_error = sum(sum([PreImerr(1,:) PostImerr(1,:) PreImerr(2,:) PostImerr(2,:)].^2))
