Commit 47ace827 authored by Daniel Bourgault's avatar Daniel Bourgault
Browse files

The cdoe can now deal with GCPs with eleation. Plus the code now comes with m_map.

parent 13a4218a
......@@ -33,20 +33,18 @@ dH = 0.0;
dtheta = 20.0;
% Order of the polynomial correction (0, 1 or 2)
polyOrder = 1;
polyOrder = 0;
% To save memory calculation can be done in single precision.
% For higher precision set the variable 'precision' to 'double';
precision = 'double';
% Ground Control Points (GCP).
% The data must come right after the gcpData = true
gcpData = true;
360 829 -70.561367 47.303783
54 719 -70.54500 47.335
99 661 -70.505 47.375
452 641 -70.435 47.389
429 633 -70.418 47.408
816 644 -70.393 47.368
\ No newline at end of file
360 829 -70.561367 47.303783 0
54 719 -70.54500 47.335 0
99 661 -70.505 47.375 0
452 641 -70.435 47.389 0
429 633 -70.418 47.408 0
816 644 -70.393 47.368 0
\ No newline at end of file
......@@ -45,18 +45,18 @@ precision = 'double';
% Ground Control Points (GCP).
gcpData = true;
2999 226 0.500 0.00
1694 220 1.500 0.00
528 677 2.290 0.50
289 1231 2.290 1.00
3819 686 0.000 0.50
4018 1266 0.000 1.00
2566 1235 0.890 0.99
3806 2118 0.255 1.56
4131 1580 0.000 1.23
521 2548 1.910 1.82
248 2328 2.060 1.71
14 1868 2.290 1.46
2999 226 0.500 0.00 0
1694 220 1.500 0.00 0
528 677 2.290 0.50 0
289 1231 2.290 1.00 0
3819 686 0.000 0.50 0
4018 1266 0.000 1.00 0
2566 1235 0.890 0.99 0
3806 2118 0.255 1.56 0
4131 1580 0.000 1.23 0
521 2548 1.910 1.82 0
248 2328 2.060 1.71 0
14 1868 2.290 1.46 0
......
......@@ -4,7 +4,7 @@ function errGeoFit = g_error_geofit(cv,imgWidth,imgHeight,xp,yp,ic,jc,...
hfovGuess,lambdaGuess,phiGuess,HGuess,thetaGuess,...
dhfov,dlambda,dphi,dH,dtheta,...
LON0,LAT0,...
i_gcp,j_gcp,lon_gcp,lat_gcp,...
i_gcp,j_gcp,lon_gcp0,lat_gcp0,h_gcp,...
theOrder,field);
......@@ -20,7 +20,6 @@ end
[LON LAT] = g_pix2ll(xp,yp,imgWidth,imgHeight,ic,jc,...
hfov,lambda,phi,theta,H,LON0,LAT0,field);
% Calculate the error between ground control points (GCPs)
% and image control points once georectified.
%
......@@ -35,8 +34,11 @@ ngcp = length(i_gcp);
ngcpFinite = 0;
errGeoFit = 0.0;
for k = 1:ngcp
% Project the GCPs onto the water surface given their elevation
[lon_gcp,lat_gcp] = g_proj_GCP(LON0,LAT0,H,lon_gcp0,lat_gcp0,h_gcp,field);
for k = 1:ngcp
% Calculate the distance (i.e. error) between GCP and rectificed ICPs.
distance = g_dist(lon_gcp(k),lat_gcp(k),LON(k),LAT(k),field);
......@@ -63,17 +65,3 @@ if abs(phi - phiGuess) > dphi; errGeoFit = inf; end
if abs(H - HGuess) > dH; errGeoFit = inf; end
if abs(theta - thetaGuess) > dtheta; errGeoFit = inf; end
%fprintf('\n')
%fprintf('Field of view (fov): %f\n',hfov)
%fprintf('Dip angle (lambda): %f\n',lambda)
%fprintf('Tilt angle (phi): %f\n',phi)
%fprintf('Camera altitude (H): %f\n',H)
%fprintf('View angle from North (theta): %f\n',theta)
%fprintf('RMS error (m): %f\n',errGeoFit)
%fprintf('\n')
%figure(100)
%hold on;
%plot(errGeoFit);
......@@ -61,7 +61,6 @@ vfov = 2*atand(tand(hfov/2)/aspectRatio);
fx = (imgWidth/2)/tand(hfov/2);
fy = (imgHeight/2)/tand(vfov/2);
% Subtract the principal point
x_p = xp - x_c + (jc);
y_p = yp - y_c + (ic);
......
function [lon_gcp,lat_gcp] = g_proj_GCP(LON0,LAT0,H,lon_gcp0,lat_gcp0,h_gcp,field)
%G_PROJ_GCP
%
% This function projects the GCPs that have an elevation greater than 0
% onto the water level of zero elevation. This is a trick in order to be
% able to deal with GCPs that have elevation. The principle can be
% illustrated in 1D like this. Consider the position x of a GCP that has an
% elevation h. Then, the projection (xp) of this GCP onto the plane that has
% zero elevation and along a line that joins the camera position of elevation
% H and the GCP is given by:
%
% xp = x * (H - h)/H
%
% INPUTS:
%
% LON0: The longitiude of the camera
% LAT0: The latitude of the camera
% H: The elevation of the camera
% lon_gcp0: The longitudes of the GCPs
% lat_gcp0: The latitudes of the GCPs
% h_gcp: The elevations of the GCPs
%
% OUTPUTS:
% lon_gcp: The projected longitudes of the GCPs
% lat_gcp: The projected latitudes of the GCPs
%
n_gcp = length(h_gcp);
for i = 1:n_gcp
if field == true
% The calculation for the distance between the GCPs and the camera
% is done on an elliptical earth and this takes a long calculation
% with the command m_idist from the m_map package. Therefore, the
% calculation is done only on the GCPs with no elevation, hence
% the 'if' condition here.
if h_gcp(i) > 0
[range,a12,a21] = m_idist(LON0,LAT0,lon_gcp0(i),lat_gcp0(i));
proj_factor = H/(H-h_gcp(i));
new_range = range*proj_factor;
[lon_gcp(i),lat_gcp(i),a21] = m_fdist(LON0,LAT0,a12,new_range);
lon_gcp(i) = lon_gcp(i) - 360;
else
% If there's no elevation, then there's nothing to do.
lon_gcp(i) = lon_gcp0(i);
lat_gcp(i) = lat_gcp0(i);
end
else
% For lab situations where positions are provided in meters rather
% than in latitude longitude the calculation is simpler.
dx = lon_gcp0(i) - LON0;
dy = lat_gcp0(i) - LAT0;
range = sqrt(dx^2 + dy^2);
proj_factor = H/(H-h_gcp(i));
new_range = range*proj_factor;
beta = atan2(dy,dx);
lon_gcp(i) = new_range*cos(beta) + LON0;
lat_gcp(i) = new_range*sin(beta) + LAT0;
end
end
\ No newline at end of file
......@@ -5,122 +5,150 @@ function g_rect
% instructions.
%
% Inputs:
% The function G_RECT reads an input parameter file that contains all
% information required to perfrorm a georectification of a given image.
% The format of this parameter file is detailed on-line on the G_RECT
% The function G_RECT reads an input parameter file that contains all
% information required to perfrorm a georectification of a given image.
% The format of this parameter file is detailed on-line on the G_RECT
% Wiki page.
%
% Outputs:
% The function G_RECT creates an output file that contains the following
% variables:
% The function G_RECT creates an output file that contains the following
% variables:
%
% imgFname: The reference image for the georectification.
%
% firstImgFname: The first image of a sequence of images to which the
% georectification could be applied to. This is really
% just a comment.
% just a comment.
%
% lastImgFname: The last image of a sequence of images to which the
% georectification could be applied to. This is really
% just a comment.
%
% LON: The main matrix that contain the longitude of each
% LON: The main matrix that contain the longitude of each
% pixel of the referecne image (imgFname). Note that
% if the package is used to rectify lab images this
% if the package is used to rectify lab images this
% matrix rather contains the distance in meters from
% a predefined x-origin.
%
% a predefined x-origin.
%
% LAT: Same as LON but for the latitude.
%
% LON0: A scalar for the longitude of the camera or, in the
% case of a lab setup its cartesian coordinate in meters.
%
%
% LON0: A scalar for the longitude of the camera or, in the
% case of a lab setup its cartesian coordinate in meters.
%
% LAT0: Same as LON0 but for the latitude.
%
% lon_gcp: A vector containing the longitude of each ground
% control points (GCP). For the lab case this is the
% cartesian coordinate in meters.
%
%
% lon0_gcp: A vector containing the longitude of each ground
% control points (GCP). For the lab case this is the
% cartesian coordinate in meters. These GCPs may have
% elevations.
%
% lat0_gcp: Same as lon_gcp for latitude.
%
% lon_gcp: A vector containing the longitude of each ground
% control points (GCP) projected onto the water level
% i.e. at 0 m of elevation. For the lab case this is
% the cartesian coordinate in meters.
%
% lat_gcp: Same as lon_gcp for latitude.
%
% h_gcp: The elevation in meter of the GCPs. The elevation is
% 0 m if taken at water level.
%
% i_gcp: The horizontal index of the image ground control
% points.
%
%
% j_gcp: The vertical index of the image ground control
% points.
%
%
% hfov: The camera horizontal field of view [degree].
%
% phi: Camera tilt angle [degree].
%
% lambda: Camera dip angle [degree].
%
% H: The camera altitude relative to the water [m].
%
% theta: View angle clockwise from North [degree].
%
%
% errGeoFit: The rms error of the georectified image after
% geometrical transformation [m].
%
% errPolyFit: The rms error of the georectified image after
% geometrical transformation and the polynomial
% geometrical transformation and the polynomial
% correction [m].
%
% precision: Calculation can be done in single or double
% precisions as defined by the user in the parameter
% file. With today's computers this is now obsolete
% and calculations can always be done in double
% file. With today's computers this is now obsolete
% and calculations can always be done in double
% precision.
%
% Delta: The effective resolution of the georectified image.
%
% Other m-files required: Works best with the m_map package for
% visualization.
% Subfunctions: all functions contained within the G_RECT folder.
%
%
% Author: Daniel Bourgault
% Institut des sciences de la mer de Rimouski
% email: daniel_bourgault@uqar.ca
% email: daniel_bourgault@uqar.ca
% Website: https://srwebpolr01.uqar.ca/g_rect/
% February 2013
%
%
% The minimization is repeated nMinimize times where each time a random
%%
% The minimization is repeated nMinimize times where each time a random
% combination of the initial guesses is chosen within the given
% uncertainties provided by the user. This is becasue the algorithm often
% uncertainties provided by the user. This is becasue the algorithm often
% converges toward a local minimum. The repetition is used to increase chances
% that the minimum found is a true minimum within the uncertainties provided.
% that the minimum found is a true minimum within the uncertainties provided.
nMinimize = 50;
%% Read the parameter file
% Count the number of header lines before the ground control points (GCPs)
% The GCPs start right after the variable gcpData is set to true.
display(' ');
display(' Welcome to g_rect: a package for georectifying oblique images on a flat ocean');
display(' Authors: Daniel Bourgault and Rich Pawlowicz');
display(' ');
inputFname = input(' Enter the filename for the input parameters: ','s');
display(' ');
display(' Welcome to g_rect: a package for georectifying oblique images on a flat ocean');
display(' Authors: Daniel Bourgault and Rich Pawlowicz');
display(' ');
inputFname = input(' Enter the filename for the input parameters: ','s');
fid = fopen(inputFname);
nHeaderLine = 0;
gcpData = false;
nHeaderLine = 0;
gcpData = false;
% Read and execute each line of the parameter file until gcpData = true
% after which the GCP data appear and are read below with the importdata
% function.
while gcpData == false
eval(fgetl(fid));
nHeaderLine = nHeaderLine + 1;
eval(fgetl(fid));
nHeaderLine = nHeaderLine + 1;
end
fclose(fid);
%% Import the GCP data (4 column) at the end of the parameter file
gcp = importdata(inputFname,' ',nHeaderLine)
i_gcp = gcp.data(:,1);
j_gcp = gcp.data(:,2);
lon_gcp = gcp.data(:,3);
lat_gcp = gcp.data(:,4);
ngcp = length(i_gcp);
%% Import the GCP data at the end of the parameter file
gcp = importdata(inputFname,' ',nHeaderLine);
i_gcp = gcp.data(:,1);
j_gcp = gcp.data(:,2);
lon_gcp0 = gcp.data(:,3);
lat_gcp0 = gcp.data(:,4);
h_gcp = gcp.data(:,5);
%%
% Check if the elevation of the GCPs are not too high and above
% a certain fraction (gamma) of the camera height. If so, stop.
gamma = 0.75;
i_bad = find(h_gcp > gamma*(H+dH));
if length(i_bad) > 0
display([' ']);
display([' WARNING:']);
for i = 1:length(i_bad)
display([' The elevation of GCP #',num2str(i_bad(i)),' is greater than ',num2str(gamma),'*(H+dH).']);
end
display([' FIX AND RERUN.']);
return
end
ngcp = length(i_gcp);
% Get the image size
imgInfo = imfinfo(imgFname);
......@@ -128,12 +156,11 @@ imgWidth = imgInfo.Width;
imgHeight = imgInfo.Height;
if precision == 'single'
imgWidth = single(imgWidth);
imgHeight = single(imgHeight);
imgWidth = single(imgWidth);
imgHeight = single(imgHeight);
end
%% Display information
%% Display information
fprintf('\n')
fprintf(' INPUT PARAMETERS\n')
fprintf(' Image filename: (imgFname):........... %s\n',imgFname)
......@@ -168,22 +195,23 @@ imagesc(imread(imgFname));
colormap(gray);
hold on
for i = 1:ngcp
plot(i_gcp(i),j_gcp(i),'r.');
text(i_gcp(i),j_gcp(i),num2str(i),'color','r','horizontalalignment','right');
plot(i_gcp(i),j_gcp(i),'r.');
text(i_gcp(i),j_gcp(i),[num2str(i),'(',num2str(h_gcp(i)),')'],...
'color','r',...
'horizontalalignment','right',...
'fontsize',16);
end
title('Ground Control Points','color','r');
title('Ground Control Points Number (elevation in meters)','color','r');
print -dpng -r300 image1.png
fprintf('\n')
ok = input('Is it ok to proceed with the rectification (y/n): ','s');
if ok ~= 'y'
return
%break
return
end
%%
nUnknown = 0;
if dhfov > 0.0; nUnknown = nUnknown+1; end
if dlambda > 0.0; nUnknown = nUnknown+1; end
......@@ -192,154 +220,153 @@ if dH > 0.0; nUnknown = nUnknown+1; end
if dtheta > 0.0; nUnknown = nUnknown+1; end
if nUnknown > ngcp
fprintf('\n')
fprintf('WARNING: \n');
fprintf(' The number of GCPs is < number of unknown parameters.\n');
fprintf(' Program stopped.\n');
%break
return
fprintf('\n')
fprintf('WARNING: \n');
fprintf(' The number of GCPs is < number of unknown parameters.\n');
fprintf(' Program stopped.\n');
return
end
% Check for consistencies between number of GCPs and order of the polynomial
% Check for consistencies between number of GCPs and order of the polynomial
% correction
ngcp = length(i_gcp);
if ngcp < 3*polyOrder
fprintf('\n')
fprintf('WARNING: \n');
fprintf(' The number of GCPs is inconsistent with the order of the polynomial correction.\n');
fprintf(' ngcp should be >= 3*polyOrder.\n');
fprintf(' Polynomial correction will not be applied.\n');
polyCorrection = false;
fprintf('\n')
fprintf('WARNING: \n');
fprintf(' The number of GCPs is inconsistent with the order of the polynomial correction.\n');
fprintf(' ngcp should be >= 3*polyOrder.\n');
fprintf(' Polynomial correction will not be applied.\n');
polyCorrection = false;
else
polyCorrection = true;
polyCorrection = true;
end
if polyOrder == 0
polyCorrection = false;
polyCorrection = false;
end
%% This is the main section for the minimization algorithm
if nUnknown > 0
% Options for the fminsearch function. May be needed for some particular
% problems but in general the default values should work fine.
%options=optimset('MaxFunEvals',100000,'MaxIter',100000);
%options=optimset('MaxFunEvals',100000,'MaxIter',100000,'TolX',1.d-12,'TolFun',1.d-12);
options = [];
% Options for the fminsearch function. May be needed for some particular
% problems but in general the default values should work fine.
%options=optimset('MaxFunEvals',100000,'MaxIter',100000);
%options=optimset('MaxFunEvals',100000,'MaxIter',100000,'TolX',1.d-12,'TolFun',1.d-12);
options = [];
% Only feed the minimization algorithm with the GCPs. xp and yp are the
% image coordinate of these GCPs.
xp = i_gcp;
yp = j_gcp;
% Only feed the minimization algorithm with the GCPs. xp and yp are the
% image coordinate of these GCPs.
xp = i_gcp;
yp = j_gcp;
% This is the call to the minimization
bestErrGeoFit = Inf;
% Save inital guesses in new variables.
hfovGuess = hfov;
lambdaGuess = lambda;
phiGuess = phi;
HGuess = H;
thetaGuess = theta;
for iMinimize = 1:nMinimize
% First guesses for the minimization
if iMinimize == 1
hfov0 = hfov;
lambda0 = lambda;
phi0 = phi;
H0 = H;
theta0 = theta;
else
% Select randomly new initial guesses within the specified
% uncertainties.
hfov0 = (hfovGuess - dhfov) + 2*dhfov*rand(1);
lambda0 = (lambdaGuess - dlambda) + 2*dlambda*rand(1);
phi0 = (phiGuess - dphi) + 2*dphi*rand(1);
H0 = (HGuess - dH) + 2*dH*rand(1);
theta0 = (thetaGuess - dtheta) + 2*dtheta*rand(1);
end
% Cretae vector cv0 for the initial guesses.
i = 0;
if dhfov > 0.0
i = i+1;
cv0(i) = hfov0;
theOrder(i) = 1;
end
if dlambda > 0.0
i = i + 1;
cv0(i) = lambda0;
theOrder(i) = 2;
end
if dphi > 0.0
i = i + 1;
cv0(i) = phi0;
theOrder(i) = 3;
end
if dH > 0.0
i = i + 1;
cv0(i) = H0;
theOrder(i) = 4;
end
if dtheta > 0.0
i = i + 1;
cv0(i) = theta0;
theOrder(i) = 5;
end
[cv, errGeoFit] = fminsearch('g_error_geofit',cv0,options, ...
imgWidth,imgHeight,xp,yp,ic,jc,...
hfov,lambda,phi,H,theta,...
hfov0,lambda0,phi0,H0,theta0,...
hfovGuess,lambdaGuess,phiGuess,HGuess,thetaGuess,...
dhfov,dlambda,dphi,dH,dtheta,...
LON0,LAT0,...
i_gcp,j_gcp,lon_gcp,lat_gcp,...
theOrder,field);
if errGeoFit < bestErrGeoFit
bestErrGeoFit = errGeoFit;
cvBest = cv;
end
fprintf('\n')
fprintf(' Iteration # (iMinimize): %i\n',iMinimize);
fprintf(' Max. number of iteration (nMinimize): %i\n',nMinimize);
fprintf(' RSM error (m) for this iteration (errGeoFit): %f\n',errGeoFit);
fprintf(' Best RSM error (m) so far (bestErrGeoFit): %f\n',bestErrGeoFit);
end
for i = 1:length(theOrder)
if theOrder(i) == 1; hfov = cvBest(i); end
if theOrder(i) == 2; lambda = cvBest(i); end
if theOrder(i) == 3; phi = cvBest(i); end
if theOrder(i) == 4; H = cvBest(i); end
if theOrder(i) == 5; theta = cvBest(i); end
end
fprintf('\n')
fprintf('PARAMETERS AFTER GEOMETRICAL RECTIFICATION \n')
fprintf(' Field of view (hfov): %f\n',hfov)
fprintf(' Dip angle (lambda): %f\n',lambda)
fprintf(' Tilt angle (phi): %f\n',phi)
fprintf(' Camera altitude (H): %f\n',H)
fprintf(' View angle from North (theta): %f\n',theta)
fprintf('\n')
if length(theOrder) > 1
fprintf('The rms error after geometrical correction (m): %f\n',bestErrGeoFit);
end
% This is the call to the minimization
bestErrGeoFit = Inf;
% Save inital guesses in new variables.
hfovGuess = hfov;
lambdaGuess = lambda;
phiGuess = phi;
HGuess = H;
thetaGuess = theta;
for iMinimize = 1:nMinimize
% First guesses for the minimization
if iMinimize == 1
hfov0 = hfov;
lambda0 = lambda;
phi0 = phi;
H0 = H;
theta0 = theta;
else
% Select randomly new initial guesses within the specified