Commit 0f098f65 authored by Dany Dumont's avatar Dany Dumont

g_stabilize a ete revise en profondeur.

parent e2875858
Examples/Lab/IMG_8368.jpg

2.73 MB | W: | H:

Examples/Lab/IMG_8368.jpg

775 KB | W: | H:

Examples/Lab/IMG_8368.jpg
Examples/Lab/IMG_8368.jpg
Examples/Lab/IMG_8368.jpg
Examples/Lab/IMG_8368.jpg
  • 2-up
  • Swipe
  • Onion skin
...@@ -167,12 +167,14 @@ fprintf('\n') ...@@ -167,12 +167,14 @@ fprintf('\n')
% image(imread(imgFname)); % image(imread(imgFname));
imagesc(imread(imgFname)); imagesc(imread(imgFname));
colormap(gray); colormap(gray);
hold on
for i = 1:ngcp for i = 1:ngcp
text(i_gcp(i),j_gcp(i),num2str(i),'color','r','horizontalalignment','center'); plot(i_gcp(i),j_gcp(i),'r.');
text(i_gcp(i),j_gcp(i),num2str(i),'color','r','horizontalalignment','right');
end end
title('Ground Control Points','color','r'); title('Ground Control Points','color','r');
print -dpng image1.png print -dpng -r300 image1.png
fprintf('\n') fprintf('\n')
ok = input('Is it ok to proceed with the rectification (y/n): ','s'); ok = input('Is it ok to proceed with the rectification (y/n): ','s');
...@@ -359,11 +361,12 @@ else ...@@ -359,11 +361,12 @@ else
end end
%% %%
% Compute the effective resolution
Delta = g_res(LON, LAT, field);
fprintf('\n') fprintf('\n')
fprintf('Saving rectification file in: %s\n',outputFname); fprintf('Saving rectification file in: %s\n',outputFname);
errGeoFit = 0.0;
save(outputFname,'imgFname','firstImgFname','lastImgFname',... save(outputFname,'imgFname','firstImgFname','lastImgFname',...
'LON','LAT',... 'LON','LAT',...
'LON0','LAT0',... 'LON0','LAT0',...
...@@ -371,7 +374,7 @@ save(outputFname,'imgFname','firstImgFname','lastImgFname',... ...@@ -371,7 +374,7 @@ save(outputFname,'imgFname','firstImgFname','lastImgFname',...
'i_gcp','j_gcp',... 'i_gcp','j_gcp',...
'hfov','lambda','phi','H','theta',... 'hfov','lambda','phi','H','theta',...
'errGeoFit','errPolyFit',... 'errGeoFit','errPolyFit',...
'precision'); 'precision','Delta');
clear LON LAT clear LON LAT
......
%function Delta = g_res(LON,LAT,i,j,field); function res = g_res_field(LON, LAT, field)
function [Delta DeltaX DeltaY] = g_res(LON,LAT,i,j,field); % g_res_field - Function calculating the loss of resolution with de distance
% from the camera
[m n] = size(LON); %
% Inputs:
if i == 1 %
Delta1 = g_dist(LON(i+1,j),LAT(i+1,j),LON(i,j),LAT(i,j),field); % LON : Longitude matrix obtained from g_rect
elseif i == m % LAT : Latitude matrix obtained from g_rect
Delta1 = g_dist(LON(i,j),LAT(i,j),LON(i-1,j),LAT(i-1,j),field); % field : 'true' for field case. 'false' for lab case.
else %
Delta1 = g_dist(LON(i+1,j),LAT(i+1,j),LON(i-1,j),LAT(i-1,j),field); % Outputs:
Delta1 = Delta1/2; %
end % res : The field of resolution which is degrading with distance from
% the camera
if j == 1 %
Delta2 = g_dist(LON(i,j+1),LAT(i,j+1),LON(i,j),LAT(i,j),field); % Author: Daniel Bourgault
elseif j == n % Institut des sciences de la mer de Rimouski
Delta2 = g_dist(LON(i,j),LAT(i,j),LON(i,j-1),LAT(i,j-1),field); %
else % email: daniel_bourgault@uqar.ca
Delta2 = g_dist(LON(i,j+1),LAT(i,j+1),LON(i,j-1),LAT(i,j-1),field); % Website: http://demeter.uqar.ca/g_rect/
Delta2 = Delta2/2; % February 2013
end %
% Author: Elie Dumas-Lefebvre
Delta = sqrt(Delta1^2 + Delta2^2); % Institut des Science de la Mer de Rimouski
DeltaX = sqrt(Delta1^2); %
DeltaY = sqrt(Delta2^2); % Note: Matricial formulation of the calculation for a faster execution
%
% email: elie.dumas-lefebvre@uqar.ca
% March 2019
%
% LAT and LON difference in the x-axis
dLATi = diff(LAT, 1, 2);
dLONi = diff(LON, 1, 2);
% LAT and LON difference in the y-axis
dLATj = diff(LAT, 1, 1);
dLONj = diff(LON, 1, 1);
% Mean LAT in both x and y axis
LAT_meani = 0.5*(LAT(:, 2:end) + LAT(:, 1:end-1));
LAT_meanj = 0.5*(LAT(2:end, :) + LAT(1:end-1, :));
% Add a point at boundaries such that the resolution matrix
% has the same size of the LAT LON matrices
dLATi(:,end+1) = dLATi(:,end);
dLONi(:,end+1) = dLONi(:,end);
dLATj(end+1,:) = dLATj(end,:);
dLONj(end+1,:) = dLONj(end,:);
LAT_meani(:,end+1) = LAT_meani(:,end);
LAT_meanj(end+1,:) = LAT_meani(end,:);
% Conversion from degree to meters
meterPerDegLat = 1852*60.0;
meterPerDegLoni = meterPerDegLat.*cosd(LAT_meani);
meterPerDegLonj = meterPerDegLat.*cosd(LAT_meanj);
if field
% Conversion from degrees to meters
dxi = dLONi.*meterPerDegLoni;
dxj = dLONj.*meterPerDegLonj;
dyi = dLATi.*meterPerDegLat;
dyj = dLATj.*meterPerDegLat;
else
% Lab case. Data are already in meters. No conversion required.
dxi = dLONi;
dxj = dLONj;
dyi = dLATi;
dyj = dLATj;
end
% Distances in x and y axis
deltai = sqrt(dxi.^2 + dyi.^2);
deltaj = sqrt(dxj.^2 + dyj.^2);
% field of resolution
res = sqrt(deltai.^2 + deltaj.^2);
end end
\ No newline at end of file
function[ A, T ] = g_computemotion( fx, fy, ft, roi ) function[ A, T ] = g_computemotion( fx, fy, ft, roi )
[ydim,xdim] = size(fx); [ydim,xdim] = size(fx);
[x,y] = meshgrid( [1:xdim]-xdim/2, [1:ydim]-ydim/2 ); [x,y] = meshgrid( [1:xdim]-xdim/2, [1:ydim]-ydim/2 );
%%% TRIM EDGES %%% TRIM EDGES
fx = fx( 3:end-2, 3:end-2 ); fx = fx( 3:end-2, 3:end-2 );
fy = fy( 3:end-2, 3:end-2 ); fy = fy( 3:end-2, 3:end-2 );
ft = ft( 3:end-2, 3:end-2 ); ft = ft( 3:end-2, 3:end-2 );
roi = roi( 3:end-2, 3:end-2 ); roi = roi( 3:end-2, 3:end-2 );
x = x( 3:end-2, 3:end-2 ); x = x( 3:end-2, 3:end-2 );
y = y( 3:end-2, 3:end-2 ); y = y( 3:end-2, 3:end-2 );
ind = find( roi > 0 ); ind = find( roi > 0 );
x = x(ind); y = y(ind); x = x(ind);
fx = fx(ind); fy = fy(ind); ft = ft(ind); y = y(ind);
fx = fx(ind);
xfx = x.*fx; xfy = x.*fy; yfx = y.*fx; yfy = y.*fy; fy = fy(ind);
M(1,1) = sum( xfx .* xfx ); M(1,2) = sum( xfx .* yfx ); M(1,3) = sum( xfx .* xfy ); ft = ft(ind);
M(1,4) = sum( xfx .* yfy ); M(1,5) = sum( xfx .* fx ); M(1,6) = sum( xfx .* fy );
M(2,1) = M(1,2); M(2,2) = sum( yfx .* yfx ); M(2,3) = sum( yfx .* xfy ); xfx = x.*fx;
M(2,4) = sum( yfx .* yfy ); M(2,5) = sum( yfx .* fx ); M(2,6) = sum( yfx .* fy ); xfy = x.*fy;
M(3,1) = M(1,3); M(3,2) = M(2,3); M(3,3) = sum( xfy .* xfy ); yfx = y.*fx;
M(3,4) = sum( xfy .* yfy ); M(3,5) = sum( xfy .* fx ); M(3,6) = sum( xfy .* fy ); yfy = y.*fy;
M(4,1) = M(1,4); M(4,2) = M(2,4); M(4,3) = M(3,4);
M(4,4) = sum( yfy .* yfy ); M(4,5) = sum( yfy .* fx ); M(4,6) = sum( yfy .* fy ); M(1,1) = sum( xfx .* xfx );
M(5,1) = M(1,5); M(5,2) = M(2,5); M(5,3) = M(3,5); M(1,2) = sum( xfx .* yfx );
M(5,4) = M(4,5); M(5,5) = sum( fx .* fx ); M(5,6) = sum( fx .* fy ); M(1,3) = sum( xfx .* xfy );
M(6,1) = M(1,6); M(6,2) = M(2,6); M(6,3) = M(3,6); M(1,4) = sum( xfx .* yfy );
M(6,4) = M(4,6); M(6,5) = M(5,6); M(6,6) = sum( fy .* fy ); M(1,5) = sum( xfx .* fx );
M(1,6) = sum( xfx .* fy );
M(2,1) = M(1,2);
M(2,2) = sum( yfx .* yfx );
M(2,3) = sum( yfx .* xfy );
M(2,4) = sum( yfx .* yfy );
M(2,5) = sum( yfx .* fx );
M(2,6) = sum( yfx .* fy );
M(3,1) = M(1,3);
M(3,2) = M(2,3);
M(3,3) = sum( xfy .* xfy );
M(3,4) = sum( xfy .* yfy );
M(3,5) = sum( xfy .* fx );
M(3,6) = sum( xfy .* fy );
M(4,1) = M(1,4);
M(4,2) = M(2,4);
M(4,3) = M(3,4);
M(4,4) = sum( yfy .* yfy );
M(4,5) = sum( yfy .* fx );
M(4,6) = sum( yfy .* fy );
M(5,1) = M(1,5);
M(5,2) = M(2,5);
M(5,3) = M(3,5);
M(5,4) = M(4,5);
M(5,5) = sum( fx .* fx );
M(5,6) = sum( fx .* fy );
M(6,1) = M(1,6);
M(6,2) = M(2,6);
M(6,3) = M(3,6);
M(6,4) = M(4,6);
M(6,5) = M(5,6);
M(6,6) = sum( fy .* fy );
k = ft + xfx + yfy; k = ft + xfx + yfy;
b(1) = sum( k .* xfx ); b(2) = sum( k .* yfx ); b(1) = sum( k .* xfx );
b(3) = sum( k .* xfy ); b(4) = sum( k .* yfy ); b(2) = sum( k .* yfx );
b(5) = sum( k .* fx ); b(6) = sum( k .* fy ); b(3) = sum( k .* xfy );
b(4) = sum( k .* yfy );
b(5) = sum( k .* fx );
b(6) = sum( k .* fy );
%v = inv(M) * b'; %v = inv(M) * b';
v = M\b'; v = M\b';
%A = [1 0 ; 0 1]; %A = [1 0 ; 0 1]; % For translation only
A = [v(1) v(2) ; v(3) v(4)]; A = [v(1) v(2) ; v(3) v(4)];
T = [v(5) ; v(6)]; T = [v(5) ; v(6)];
\ No newline at end of file
function[ f ] = g_down( f, L ); function[ f ] = g_down( f, L );
blur = [1 2 1]/4; blur = [1 2 1]/4;
for k = 1 : L for k = 1 : L
f = conv2( conv2( f, blur, 'same' ), blur', 'same' ); f = conv2( conv2( f, blur, 'same' ), blur', 'same' );
f = f(1:2:end,1:2:end); f = f(1:2:end,1:2:end);
end end
\ No newline at end of file
function[ Acum, Tcum ] = g_opticalflow( f1, f2, roi, L ) function[ Acum, Tcum ] = g_opticalflow( f1, f2, roi, L )
f2orig = f2; f2orig = f2;
Acum = [1 0 ; 0 1]; Acum = [1 0 ; 0 1];
Tcum = [0 ; 0]; Tcum = [0 ; 0];
for k = L : -1 : 0 for k = L : -1 : 0
%for k = L : -1 : L
%%% DOWN-SAMPLE %%% DOWN-SAMPLE
f1d = g_down( f1, k ); f1d = g_down( f1, k );
f2d = g_down( f2, k ); f2d = g_down( f2, k );
ROI = g_down( roi, k ); ROI = g_down( roi, k );
%%% COMPUTE MOTION %%% COMPUTE MOTION
[Fx,Fy,Ft] = g_spacetimederiv( f1d, f2d ); [Fx,Fy,Ft] = g_spacetimederiv( f1d, f2d );
[A,T] = g_computemotion( Fx, Fy, Ft, ROI ); [A,T] = g_computemotion( Fx, Fy, Ft, ROI );
T = (2^k) * T; T = (2^k) * T;
[Acum,Tcum] = g_accumulatewarp( Acum, Tcum, A, T ); [Acum,Tcum] = g_accumulatewarp( Acum, Tcum, A, T );
%%% WARP ACCORDING TO ESTIMATED MOTION %%% WARP ACCORDING TO ESTIMATED MOTION
f2 = g_warp( f2orig, Acum, Tcum ); f2 = g_warp( f2orig, Acum, Tcum );
......
...@@ -58,4 +58,4 @@ roi(ij) = 1; ...@@ -58,4 +58,4 @@ roi(ij) = 1;
figure(2); figure(2);
imagesc(roi); imagesc(roi);
save roi.mat roi imgFname save([imgFname(1:end-4),'_roi.mat'],'roi','imgFname');
\ No newline at end of file \ No newline at end of file
...@@ -9,66 +9,100 @@ function g_stabilize(img_ref_fname,varargin) ...@@ -9,66 +9,100 @@ function g_stabilize(img_ref_fname,varargin)
% necessary), of the reference image in the current % necessary), of the reference image in the current
% directory. % directory.
% %
% Parameters, Name, Value % Optional parameters:
% roiFile, [Defaults: roi.mat] Name of the file with roi % L_level_Gaussian: Size of the L-level Gaussian pyramid [Default: 4]
% information. % roi_file: Name of the file with roi information [Default: 'roi.mat']
% auto, [Defaults: 0 (false)] If true, overrides any % fname_suffix: Suffix to add to the stabilized images [Default: '_stable']
% interaction with the user (for use in scripts) % output_folder: Name of the output folder [Default: 'stable']
% suffix, [Defaults: '_stable'] Suffix to add to the % equalize: Use image equalization over the roi region [Default: 0 (false)]
% stabilized images. % use_img_grad: Perform the stabilization using the gradient of the image [Default: 0 (false)]
% auto: If true, overrides any interaction with the user
% (for use in scripts) [Default: 0 (false)]
% %
% Output: % Output:
% All stabilized images are rewritten as image files in the subfolder % All stabilized images are rewritten as image files in the subfolder
% /stable. % /stable (by default or other name is specified).
% %
% Example: % Examples:
% g_stabilize('IMG_4018.jpg'); % Example 1: Using all default parameters
% >> g_stabilize('IMG_4018.jpg');
% %
% Example 2: Using a different L level Gaussian than the default value (4)
% >> g_stabilize('IMG_4018.jpg','L_level_Gaussian',6);
%
% Example 3: Using a different L level Gaussian than the default value (4)
% and a different suffix than the default (_stable) and
% without any image equalization
% >> g_stabilize('IMG_4018.jpg','L_level_Gaussian',6,'fname_suffix','_very_stable','equalize',false);
%
%%
p = inputParser;
% Default values
default_L_level_Gaussian = 4;
default_roi_file = 'roi.mat';
default_fname_suffix = '_stable';
default_output_folder = 'stable';
default_equalize = false;
default_use_img_grad = false;
default_auto = false;
addRequired(p,'img_ref_fname',@ischar);
addParameter(p,'L_level_Gaussian',default_L_level_Gaussian,@isnumeric);
addParameter(p,'roi_file',default_roi_file,@ischar);
addParameter(p,'fname_suffix',default_fname_suffix,@ischar);
addParameter(p,'output_folder',default_output_folder,@ischar);
addParameter(p,'equalize',default_equalize,@islogical);
addParameter(p,'use_img_grad',default_use_img_grad,@islogical);
addParameter(p,'auto',default_auto,@islogical);
parse(p,img_ref_fname,varargin{:})
%% Some parameters img_ref_fname = p.Results.img_ref_fname;
% Pyramid level for the stabilization. L = p.Results.L_level_Gaussian;
% See documentation in the g_stabilize folder. roi_file = p.Results.roi_file;
% L = 4 worked well on initial application but may require adjustments for fname_suffix = p.Results.fname_suffix;
% particular application. output_folder = p.Results.output_folder;
L = 4; equalize = p.Results.equalize;
use_img_grad = p.Results.use_img_grad;
auto = p.Results.auto;
% Number of iterative improvement. Probably ok just 1 iteration. disp([' ']);
niter = 1; disp([' -----------------------------------------------------------------------']);
disp([' IMAGE STABILIZATION PARAMETERS']);
disp([' Filename of the reference image: ',img_ref_fname]);
disp([' L-level Gaussian: ',num2str(L)]);
disp([' Name of the ROI filename: ',roi_file]);
disp([' Suffix of stabilized filenames: ',fname_suffix]);
disp([' Output folder: ',output_folder]);
disp([' Equalize (1) or not (0) the roi region: ',num2str(equalize)]);
disp([' Use (1) or not (0) the gradient of the image: ',num2str(use_img_grad)]);
disp([' -----------------------------------------------------------------------']);
%% Equalization parameters
% Parameters for equalization. See help clahs for details. % Parameters for equalization. See help clahs for details.
% This may not be needed fpr most application but may help other cases. % This may not be needed for most applications but may help in some cases
% where the lighting conditions changes a lot.
% Need to play with it if problems. % Need to play with it if problems.
equalize = false; if equalize
nry = 4; nry = 8;
nrx = 4; nrx = 8;
end
%%
% Extract the path and extension of the reference image % Extract the path and extension of the reference image
[pathstr,name,ext] = fileparts(img_ref_fname); [pathstr,name,ext] = fileparts(img_ref_fname);
% Check for optional options
fname_suffix = '_stable';
auto = 0;
roifile = 'roi.mat';
if length(varargin) > 1
for i=1:2:length(varargin)
if strcmpi(varargin{i},'roiFile')
roifile = varargin{i+1};
elseif strcmpi(varargin{i},'auto')
auto = varargin{i+1};
elseif strcmpi(varargin{i},'suffix')
fname_suffix = varargin{i+1};
end
end
end
hw = waitbar(0,'Preparing images');
%% Load the Region of interest (roi) file %% Load the Region of interest (roi) file
display(' Loading the roi.mat file containing the region of interest (roi)'); disp([' ']);
display(' Loading the file containing the region of interest (roi)...');
if length(pathstr) == 0 % Images are in the current folder if length(pathstr) == 0 % Images are in the current folder
load(roifile); load(roi_file);
else else
load([pathstr,'/',roifile]); load([pathstr,'/',roi_file]);
end end
% Read the reference image % Read the reference image
...@@ -76,71 +110,76 @@ im2 = imread(img_ref_fname); ...@@ -76,71 +110,76 @@ im2 = imread(img_ref_fname);
% Convert into gray scale if not already a B/W image % Convert into gray scale if not already a B/W image
if size(im2,3) ~= 1 if size(im2,3) ~= 1
im2 = g_rgb2gray(im2); im2_BW = g_rgb2gray(im2);
end end
im2_BW_cooked = im2_BW;
clear im2;
% May equalize with this equalizer. Careful this doesn't always work well % May equalize with this equalizer. Careful this doesn't always work well
% and may compromise the stabilization. Use with care. % and may compromise the stabilization. Use with care.
if equalize if equalize
im2 = clahs(im2,nry,nrx); im2_BW_cooked = clahs(im2_BW_cooked,nry,nrx);
end end
im2 = double(im2); im2_BW = double(im2_BW);
im2_BW_cooked = double(im2_BW_cooked);
% Remove mean and divide by the standard deviation. % Remove mean and divide by the standard deviation.
% This generally help the algorithm to compare images % This generally help the algorithm to compare images that have different
inorm = find(roi > 0); % light conditions
im2_norm = (im2 - nanmean(im2(inorm)))./nanstd(im2(inorm)); %inorm = find(roi > 0);
%im2_BW_cooked = (im2_BW_cooked - nanmean(im2_BW_cooked(inorm)))./nanstd(im2_BW_cooked(inorm));
% Take the norm of the gradient of the image. This helps the if use_img_grad
% stabilization algorithm, nut again you may want to explore % Take the norm of the gradient of the image. This may help the
% without it % stabilization algorithm but not always. Use this option with care.
[im2x, im2y] = gradient(im2_norm); [im2x, im2y] = gradient(im2_BW_cooked);
grad_im2 = sqrt(im2x.^2 + im2y.^2); im2_BW_cooked = sqrt(im2x.^2 + im2y.^2);
end
frames(2).im = im2_BW_cooked;
% The second frame is the reference frame for the stabilization algorithm.
frames(2).im = grad_im2;
%% Some figures to make sure everything seems ok %% Some figures to make sure everything is ok
figure(1); figure(1);
imagesc(im2); imagesc(im2_BW);
colormap(gray); colormap(gray);
title('Reference image. Intensity normalized over ROI'); title('Reference image in B/W');
figure(2); figure(2);
imagesc(log10(grad_im2+eps)); imagesc(im2_BW_cooked);
colormap(gray); colormap(gray);
title('Gradient of the reference image on a log scale'); title('Reference image manipulated (if so)');
figure(3); figure(3);
imagesc(roi); % Produce a mask over non-ROI region and plot it
title('roi'); iNaN = find(roi == 0);
roi_with_NaN = roi;
roi_with_NaN(iNaN) = NaN;
imagesc(im2_BW_cooked.*roi_with_NaN);
colormap(gray);
title('Reference image manipulated with mask over non-ROI');
if ~auto %if ~auto
answer = input('Happy with roi (y/n)? ','s'); % disp([' '])
if answer == 'n' % answer = input(' Happy with roi and parameters (y/n)? ','s');
return % if answer == 'n'