My Steps
- Convert the video into Frames and then into Images
- using SIFT get pairs of corresponding points taken from the two images
- Recover homographies of each Image
- Warp the images using the homography
- Blend images into a mosaic using weighted average
- View the Results
function [mos_img]=mosaic(filename)
% creates a mosaic image from a movie
% assuming translation only between frames
warning off MATLAB:mir_warning_variable_used_as_function;
% using global names to cache frames in memory
% performance boosting
global Nframes;
global AviName;
AviName = filename;
[h, w, Nframes] = avi_size(AviName);
% relative displacements
d = zeros(2,Nframes);
RefFrame = GetImage(1);
% align every two consequent frames
for ii=2:Nframes
% read a frame
Frame = GetImage(ii);
d(:,ii) = align(RefFrame, Frame, d(:,ii-1));
% current frame is next one's reference
RefFrame = Frame;
end
% cumulative displacement relative to first frame
D = cumsum(d,2);
% minimal
Minimum = min(D,[],2);
%maximal
Maximum = max(D,[],2);
% minimal displacement is one
D = D - Minimum*ones(1,Nframes)+1;
% create an empty mosaic (compute the bounding box)
mos_img = zeros([w, h]+ceil(Maximum'-Minimum'))';
[bm bn] = size(mos_img);
% creates weights
weights = zeros(bm,bn);
% shfoch everything into the mosaic
for ii=1:Nframes
% read a frame
Frame = GetImage(ii);
tmpF(1:(h+1),1:(w+1)) = 0;
tmpF(2:h+1,2:w+1) = Frame;
intD = floor(D(:,ii));
wF = warp(tmpF, (D(:,ii)-intD));
wF(find(isnan(wF))) = 0;
% mask
m = zeros(size(tmpF));
m(2:h+1,2:w+1) = 1;
wm = warp(m, (D(:,ii)-intD));
wm(find(isnan(wm))) = 0;
[A B] = GetPosArray(bm, h, bn, w, intD);
mos_img(A, B) = mos_img(A, B) + wF;
weights(A, B) = weights(A, B) + wm;
end
weights = weights + (weights == 0); % in order to avoid zero division
mos_img = mos_img./weights;
function [A, B] = GetPosArray(bm, h, bn, w, intD)
% Gets an array, where to put the image.
% bm, bn - the total image's size
% h, w - single frame's size
% intD - the integer displacement.
A = bm - h + 1 - intD(2): bm - intD(2) + 1;
B = bn - w + 1- intD(1): bn - intD(1) + 1;
function image = GetImage(index)
% Gets the image data in the correct form (Double, greyscale).
% using chached frames to boost performance
% caching BUFFER_SIZE images at each access to the avi file
BUFFER_SIZE = 40;
persistent CurrentBlock M;
global AviName Nframes;
if (isempty(CurrentBlock))
CurrentBlock = -1;
end;
% zindex to start from zero rather than one.
zindex = index - 1;
if (floor(zindex / BUFFER_SIZE) ~= CurrentBlock)
CurrentBlock = zindex / BUFFER_SIZE;
ReadTo = min((CurrentBlock+1)*BUFFER_SIZE, Nframes);
M = aviread(AviName, (CurrentBlock*BUFFER_SIZE + 1):ReadTo);
end
localIndex = mod(zindex, BUFFER_SIZE) + 1;
%returning the right image in the correct format.
image = im2double(rgb2gray(M(localIndex).cdata ));
function [shift] = align(im1, im2, initial_shift)
% This function aligns im2 to im1 assuming an homography consist of shift only
% Input parameters
% im1, im2 - images in double representation grey level range [0,1]
%
% The output parameters:
% shift - a vector [dx, dy]' of displacement
% initial guess for displacement
shift = [0, 0]';
if (nargin == 3)
shift = initial_shift;
end;
% global parameters
ITER_EPS = 0.001;
PYRAMID_DEPTH = 5;
ITERATIONS_PER_LEVEL = 5;
FILTER_PARAM = 0.4; % parameter for gaussian pyramid
% Creating pyramids for both images.
F = gaussian_pyramid(im1, PYRAMID_DEPTH, FILTER_PARAM);
G = gaussian_pyramid(im2, PYRAMID_DEPTH, FILTER_PARAM);
% Allowing initial shift - scale it to fit pyramid level
shift = shift ./ (2^(PYRAMID_DEPTH+1));
% work coarse to fine in pyramids
for ii = PYRAMID_DEPTH : -1 : 1 %PYRAMID_DEPTH
clear warped_im2; % we are about to change its dimensionality
shift = 2 * shift; % compensate the scale factor between pyramid levels.
% compute the gradient once per pyramid level
[Fx Fy] = gradient(F{ii});
% although we can re-calculate it according to overlapping zone
% it is negligible thus we save computation by doing it once per level
tmp = sum(Fx(:).*Fy(:));
A = inv( [ sum(Fx(:).^2) tmp ; tmp sum(Fy(:).^2) ] );
% iterate per pyramid level
for jj = 1 : ITERATIONS_PER_LEVEL
% always warp original image by accumulated shift, avoid over
% blurring
warped_im2 = warp(G{ii}, shift);
% mask to rule out boundary (NaN values)
mask = isnan(warped_im2);
warped_im2(find(mask)) = 0;
mask = 1-mask;
% difference image - ignore parts that are not overlapping
diff = ( F{ii}(:)-warped_im2(:) ).*mask(:);
% Solve and update shift;
tmpshift = A*[sum(diff.*Fx(:)) ; sum(diff.*Fy(:))] ;
shift = shift + tmpshift;
if (abs(tmpshift) < ITER_EPS)
break;
end;
end;
end;
% wrapping an image with shift using bilinear interpolation
function [warped] = warp(image, shift)
[x y] = size(image);
[X Y] = meshgrid(1:y,1:x); % Behold!!!
warped = interp2(image, X+shift(1), Y+shift(2), 'linear');
function [G] = gaussian_pyramid(image, n, a)
% compute n gaussian pyramid of an image
% a - filter parameter
G = cell(n,1);
G{1} = image;
for ii=1:n-1
% use tmp variable to comply with varying size of image through down sampling
G{ii+1}=gaussian_pyramid_one_stage(G{ii}, a);
end
function [G]=gaussian_pyramid_one_stage(im, a)
% compute one stage of a gaussian pyramid of an image
% a is the filter parameter
[m n]=size(im);
% construct the filter
filterKernel = [0.25 - (a/2), 0.25, a, 0.25, 0.25-(a/2)];
h = filterKernel'*filterKernel;
% filter the image, i.e. blur it with the gaussian, replicate to overcome
% boundary conditions
filt = imfilter(im, h, 'replicate');
% down sample
G = filt(1:2:m,1:2:n);





