Lucas-Kanade Homography Tracker
Lucas-Kanade sparse optical flow demo. Uses cv.goodFeaturesToTrack for track initialization and back-tracking for match verification between frames. Finds homography between reference and current views.
Sources:
Contents
Options
Parameters for corner detection and flow computation
shi_params = {'MaxCorners',1000, 'QualityLevel',0.01, 'MinDistance',8, ...
'BlockSize',19};
lk_params = {'WinSize',[19 19], 'MaxLevel',2, ...
'Criteria',struct('type','Count+EPS', 'maxCount',10, 'epsilon',0.03)};Video
Prepare video source
if true vid = 0; elseif true vid = fullfile(mexopencv.root(), 'test', '768x576.avi'); elseif mexopencv.require('vision') vid = fullfile(toolboxdir('vision'), 'visiondata', 'visiontraffic.avi'); end cap = createVideoCapture([], 'chess'); assert(cap.isOpened(), 'Failed to initialize capturing');
First frame
Grab first frame
frame0 = cap.read(); assert(~isempty(frame0), 'Failed to read frame'); sz = [size(frame0,2), size(frame0,1)]; % [width,height]
Initialize
% Plot hImg = imshow(frame0); title('Lucas-Kanade homography tracker') % UI widgets to control app state algs = {'0', 'Ransac', 'LMedS', 'Rho'}; hBtn = uicontrol('Style','pushbutton', 'Position',[20 20 120 20], ... 'String','Start tracking', 'Callback',@(o,~) set(o, 'Enable','off')); hCB = uicontrol('Style','popupmenu', 'Position',[140 20 120 20], ... 'String',algs, 'Value',2);

Main loop
p0 = {}; % stores points to track
while ishghandle(hImg)
% Grab next frame
frame = cap.read();
if isempty(frame), break; end
next = cv.cvtColor(frame, 'RGB2GRAY');
out = frame;
if ~isempty(p0)
% track points in forward and backward direction, and check trace
p2 = cv.calcOpticalFlowPyrLK(prev, next, p1, lk_params{:});
p1r = cv.calcOpticalFlowPyrLK(next, prev, p2, lk_params{:});
good = cellfun(@(a,b) max(abs(a - b)), p1, p1r) < 1.0;
% keep good matches
p0 = p0(good);
p1 = p2(good);
prev = next; % next iteration
str = sprintf('track count: %d', numel(p1));
% we need at least 4 pairs of points to estimate homography
if numel(p1) < 4
p0 = {};
continue;
end
% compute perspective transformation, and apply it on reference image
% (homography between ref points and their currently tracked locations)
[H, inliers] = cv.findHomography(p0, p1, ...
'Method',algs{get(hCB, 'Value')}, 'RansacReprojThreshold',10.0);
overlay = cv.warpPerspective(frame0, H, 'DSize',sz);
out = cv.addWeighted(out,0.5, overlay,0.5, 0.0);
% draw points correspondances (inliers/outliers used in estimating H)
inliers = logical(inliers);
out = cv.line(out, p0(inliers), p1(inliers), 'Color',[0 128 0]);
out = cv.line(out, p0(~inliers), p1(~inliers), 'Color',[128 0 0]);
out = cv.circle(out, p1(inliers), 2, 'Thickness','Filled', 'Color',[0 255 0]);
out = cv.circle(out, p1(~inliers), 2, 'Thickness','Filled', 'Color',[255 0 0]);
else
% detect and show corners
p1 = cv.goodFeaturesToTrack(next, shi_params{:});
str = sprintf('feature count: %d', numel(p1));
if ~isempty(p1)
out = cv.circle(out, p1, 2, 'Thickness','Filled', 'Color',[0 0 255]);
end
end
% display points count
out = cv.putText(out, str, [20 20], ...
'FontScale',0.5, 'Color',[255 255 0], 'LineType','AA');
% check if track button was clicked
if strcmp(get(hBtn, 'Enable'), 'off')
% save reference frame, and initialize reference points to track
frame0 = frame;
p0 = cv.goodFeaturesToTrack(next, shi_params{:});
if ~isempty(p0)
% init points and gray image updated each iteration while tracking
p1 = p0;
prev = next;
end
set(hBtn, 'Enable','on');
end
% Display result
set(hImg, 'CData',out);
drawnow;
end
cap.release();