Code covered by the BSD License  

Highlights from
Lucas Kanade affine template tracking

image thumbnail
from Lucas Kanade affine template tracking by Dirk-Jan Kroon
Lucas Kanade affine template tracking , also robust inverse in c-code.

[p,I_roi,T_error]=LucasKanadeInverseAffine(I,p,I_template,Wn,Options)
function [p,I_roi,T_error]=LucasKanadeInverseAffine(I,p,I_template,Wn,Options)
% This is an Robust Affine Lucas Kanade template tracker, which performs 
% a template tracking step on a 2D image. It is called inverse because
% template and image are switched in the equations for more time 
% efficient tracking.
%
% [p,I_roi,T_error]=LucasKanadeInverseAffine(I,p,I_template,Wn,Options)
% 
% inputs,
%   I : A 2d image of type double (movie frame)
%   p : 6 parameters, which describe affine transformation
%       (Backwards) Affine Transformation Matrix is used in 
%       Lucas Kanade Tracking with 6 parameters
%       M = [ 1+p(1) p(3)   p(5); 
%             p(2)   1+p(4) p(6); 
%             0      0      1];
%       Note : The center of the image is 0,0 not a corner
%   I_template : An image of the template
%   Wn : Robust weights same size as I_template, gives the reliability
%        of each pixel. see paper "Robust template tracking with drift correction"
%   Options : A struct with Options
%       .Padding: Defines the size of the padding of the template  
%                       input image. Those padding pixels at 
%                       the boundaries are used for derivatives not 
%                       for tracking (default 5)
%       .TranslationIterations : Number of translation itterations before
%                       performing Affine (default 6)
%       .AffineIterations: Number of Affine itterations (default 6)
%       .TolP: Tollerance on parameters allowed (default 1e-5)
%       .RoughSigma: Large sigma used in the first few itterations
%                       for noise robustness (default 3)
%       .FineSigma: Sigma used in the other itterations (default 1.5)
%       .SigmaIterations: Number of itterations with rough sigma 
%                       (default 2)
%
% Outputs,
%   p : The new affine parameters 
%   I_roi : The image ROI on the found template position
%   T_error : The squared error between template and ROI
%
% Literature used, S. Baker et Al. "Lucas-Kanade 20 Years  On: A  
%  Unifying Framework"
%
% Function is written by D.Kroon University of Twente (June 2009)

% Process inputs
defaultoptions=struct('TranslationIterations',6,'AffineIterations',6,'TolP',1e-5,'FineSigma',1.5,'RoughSigma',3,'SigmaIterations',2,'Padding',5);
if(~exist('Options','var')), 
    Options=defaultoptions; 
else
    tags = fieldnames(defaultoptions);
    for i=1:length(tags)
         if(~isfield(Options,tags{i})),  Options.(tags{i})=defaultoptions.(tags{i}); end
    end
    if(length(tags)~=length(fieldnames(Options))), 
        warning('LucasKanadeAffine:unknownoption','unknown options found');
    end
end

% Parameters to column vector
p=p(:);

% 3: Evaluate the Gradient of the template
[G_x1,G_y1]=image_derivatives(I_template,Options.RoughSigma);
[G_x2,G_y2]=image_derivatives(I_template,Options.FineSigma);

% Remove the padding from the derivatives
b=Options.Padding;
G_x1=G_x1((1+b):end-b,(1+b):end-b);
G_y1=G_y1((1+b):end-b,(1+b):end-b);
G_x2=G_x2((1+b):end-b,(1+b):end-b);
G_y2=G_y2((1+b):end-b,(1+b):end-b);

% Remove padding weight function
Wn=Wn((1+b):end-b,(1+b):end-b);

% Make padded x,y template imagesindices
[x,y]=ndgrid(0:size(I_template,1)-1,0:size(I_template,2)-1);
TemplateCenter=size(I_template)/2;
x_padded=x-TemplateCenter(1); y_padded=y-TemplateCenter(2);

% Remove the padding from the template
I_template=I_template((1+b):end-b,(1+b):end-b);

% Make all x,y indices
[x,y]=ndgrid(0:size(I_template,1)-1,0:size(I_template,2)-1);
% Calculate center of the template image
TemplateCenter=size(I_template)/2;
% Make center of the template image coordinates 0,0
x=x-TemplateCenter(1); y=y-TemplateCenter(2);

% 4: Evaluate Gamma(x) (Same as Jacobian in normal LK_affine)
Gamma_x=[x(:) zeros(size(x(:))) y(:) zeros(size(x(:))) ones(size(x(:))) zeros(size(x(:)))];
Gamma_y=[zeros(size(x(:))) x(:) zeros(size(x(:))) y(:) zeros(size(x(:))) ones(size(x(:)))];

% 5: Compute the modified steepest descent images gradT * Gamma(x)
gG1=zeros(numel(x),6); gG2=zeros(numel(x),6);    
for j=1:numel(x),
    Gamma=[Gamma_x(j,:);Gamma_y(j,:)];
    Gradient=[G_x1(j) G_y1(j)];
    gG1(j,:)=Gradient*Gamma;
    Gradient=[G_x2(j) G_y2(j)];
    gG2(j,:)=Gradient*Gamma;
end
% 5, for translation only
gG1t=zeros(numel(x),2); gG2t=zeros(numel(x),2);    
for j=1:numel(x),
    gG1t(j,1)=G_x1(j); gG1t(j,2)=G_y1(j);
    gG2t(j,1)=G_x2(j); gG2t(j,2)=G_y2(j);
end

% 6: Compute the modified Hessian H* using equation 58
H_mod1=zeros(6,6); H_mod2=zeros(6,6);
H_mod1t=zeros(2,2); H_mod2t=zeros(2,2);
for j=1:numel(x),
    H_mod1=H_mod1+Wn(j)*gG1(j,:)'*gG1(j,:);
    H_mod2=H_mod2+Wn(j)*gG2(j,:)'*gG2(j,:);
    H_mod1t=H_mod1t+Wn(j)*gG1t(j,:)'*gG1t(j,:);
    H_mod2t=H_mod2t+Wn(j)*gG2t(j,:)'*gG2t(j,:);
end
% Compute the inverse hessians
H_mod1_inv=inv(H_mod1); 
H_mod2_inv=inv(H_mod2);
H_mod1t_inv=inv(H_mod1t); 
H_mod2t_inv=inv(H_mod2t);

% Loop
for i=1:Options.TranslationIterations+Options.AffineIterations;
    % W(x;p)
    W_xp = [ 1+p(1) p(3) p(5); p(2) 1+p(4) p(6); 0 0 1];

    % 1: Warp I with W(x;p) to compute I(W(x;p))
    I_warped = affine_transform_2d_double(I,x,y,W_xp,true);

    % 2: Compute the error image I(W(x;p))-T(x)
    I_error= I_warped - I_template ;
    % Break if outside image
    if((p(5)>(size(I,1))-1)||(p(6)>(size(I,2)-1))||(p(5)<0)||(p(6)<0)), break; end
 
    % First itterations only do translation updates for more robustness
    % and after that Affine.
    if(i>Options.TranslationIterations)
        % Affine parameter optimalization

        % 7: Computer sum_x [gradT*Gamma(x)]^T (I(W(x;p))-T(x)]
        sum_xy=zeros(6,1);
        if(i<=Options.SigmaIterations)
            for j=1:numel(x), sum_xy=sum_xy+gG1(j,:)'*I_error(j); end
        else
            for j=1:numel(x), sum_xy=sum_xy+gG2(j,:)'*I_error(j); end
        end

        % 8: Computer delta_p using Equation 61
        if(i<=Options.SigmaIterations)
            delta_p_mod=H_mod1_inv*sum_xy;
        else
            delta_p_mod=H_mod2_inv*sum_xy;
        end
        
    else
        % Translation parameter optimalization

        % 7: Computer sum_x [gradT*Gamma(x)]^T (I(W(x;p))-T(x)]
        sum_xy=zeros(2,1);
        if(i<=Options.SigmaIterations)
            for j=1:numel(x), sum_xy=sum_xy+Wn(j)*gG1t(j,:)'*I_error(j); end
        else
            for j=1:numel(x), sum_xy=sum_xy+Wn(j)*gG2t(j,:)'*I_error(j); end
        end

        % 8: Computer delta_p using Equation 61
        if(i<=Options.SigmaIterations)
            delta_p_mod=H_mod1t_inv*sum_xy;
        else
            delta_p_mod=H_mod2t_inv*sum_xy;
        end
        delta_p_mod=[0;0;0;0;delta_p_mod(1);delta_p_mod(2)];
    end
    
    % 9: Compute Gamma(p)^-1 and Update the parameters p <- p + delta_p
    Gamma_p_inv=[ (1+p(1)) p(3) 0 0 0 0; p(2) (1+p(4)) 0 0 0 0; 0 0 (1+p(1)) p(3) 0 0; 0 0 p(2) (1+p(4)) 0 0; 0 0 0 0 (1+p(1)) p(3); 0 0 0 0 p(2) (1+p(4))];
    delta_p=Gamma_p_inv*delta_p_mod;
    p = p - delta_p;
        
    % Break if position is already good enough
    if((norm(delta_p,2)<Options.TolP)&&(i>Options.TranslationIterations)), break, end
end

% Warp to give a roi back with padding
I_roi = affine_transform_2d_double(I,x_padded,y_padded,W_xp,true);
    
T_error=sum(I_error(:).^2)/numel(I_error);
p=p';


function [Ix,Iy]=image_derivatives(I,sigma)
% Make derivatives kernels
[x,y]=ndgrid(floor(-3*sigma):ceil(3*sigma),floor(-3*sigma):ceil(3*sigma));
DGaussx=-(x./(2*pi*sigma^4)).*exp(-(x.^2+y.^2)/(2*sigma^2));
DGaussy=-(y./(2*pi*sigma^4)).*exp(-(x.^2+y.^2)/(2*sigma^2));
% Filter the images to get the derivatives
Ix = imfilter(I,DGaussx,'conv');
Iy = imfilter(I,DGaussy,'conv');

function Iout=affine_transform_2d_double(Iin,x,y,M,black)
% Affine transformation function (Rotation, Translation, Resize)
% This function transforms a volume with a 3x3 transformation matrix
%
% Iout=affine_transform_2d_double(Iin,Minv,black)
%
% inputs,
%   Iin: The input image
%   Minv: The (inverse) 3x3 transformation matrix
%   black: If true pixels from outside the image are set to zero
%           if false to the nearest old pixel.
% output,
%   Iout: The transformed image
%
% example,
%   % Read image
%   I=im2double(imread('lenag2.png'))
%   % Make a transformation matrix
%   M=make_transformation_matrix([2 3],2,[1.0 1.1]);
%   % Transform the image
%   Iout=affine_transform_2d_double(I,M,0)
%   % Show the image
%   figure, imshow(Iout);
%
% Function is written by D.Kroon University of Twente (February 2009)


% Calculate the Transformed coordinates
Tlocalx =  M(1,1) * x + M(1,2) *y + M(1,3) * 1;
Tlocaly =  M(2,1) * x + M(2,2) *y + M(2,3) * 1;

% All the neighborh pixels involved in linear interpolation.
xBas0=floor(Tlocalx);
yBas0=floor(Tlocaly);
xBas1=xBas0+1;
yBas1=yBas0+1;

% Linear interpolation constants (percentages)
xCom=Tlocalx-xBas0;
yCom=Tlocaly-yBas0;
perc0=(1-xCom).*(1-yCom);
perc1=(1-xCom).*yCom;
perc2=xCom.*(1-yCom);
perc3=xCom.*yCom;

% limit indexes to boundaries
check_xBas0=(xBas0<0)|(xBas0>(size(Iin,1)-1));
check_yBas0=(yBas0<0)|(yBas0>(size(Iin,2)-1));
xBas0(check_xBas0)=0;
yBas0(check_yBas0)=0;
check_xBas1=(xBas1<0)|(xBas1>(size(Iin,1)-1));
check_yBas1=(yBas1<0)|(yBas1>(size(Iin,2)-1));
xBas1(check_xBas1)=0;
yBas1(check_yBas1)=0;

Iout=zeros([size(x) size(Iin,3)]);
for i=1:size(Iin,3);
    Iin_one=Iin(:,:,i);
    % Get the intensities
    intensity_xyz0=Iin_one(1+xBas0+yBas0*size(Iin,1));
    intensity_xyz1=Iin_one(1+xBas0+yBas1*size(Iin,1));
    intensity_xyz2=Iin_one(1+xBas1+yBas0*size(Iin,1));
    intensity_xyz3=Iin_one(1+xBas1+yBas1*size(Iin,1));
    % Make pixels before outside Ibuffer black
    if(black>0)
        intensity_xyz0(check_xBas0|check_yBas0)=0;
        intensity_xyz1(check_xBas0|check_yBas1)=0;
        intensity_xyz2(check_xBas1|check_yBas0)=0;
        intensity_xyz3(check_xBas1|check_yBas1)=0;
    end
    Iout_one=intensity_xyz0.*perc0+intensity_xyz1.*perc1+intensity_xyz2.*perc2+intensity_xyz3.*perc3;
    Iout(:,:,i)=reshape(Iout_one, [size(x,1) size(x,2)]);
end

Contact us at files@mathworks.com