Contents
Lucas-Kanade Optical Flow
A demo of Lukas-Kanade optical flow. It uses camera by default, but you can provide a path to video as an argument.
Sources:
function varargout = lk_demo_gui(varargin)
% setup video capture cap = createVideoCapture([], 'chess'); assert(cap.isOpened()); frame = cap.read(); assert(~isempty(frame) && size(frame,3)==3); prev = cv.cvtColor(frame, 'RGB2GRAY'); % create the UI, and hook event handlers h = buildGUI(frame); set(h.fig, 'WindowKeyPressFcn',@onType, ... 'WindowButtonDownFcn',@onMouseDown, ... 'Interruptible','off', 'BusyAction','cancel'); if nargout > 0, varargout{1} = h; end % initialize state pts = []; initCorners = false; nightMode = false; % main loop while ishghandle(h.fig) % get next frame frame = cap.read(); if isempty(frame), break; end next = cv.cvtColor(frame, 'RGB2GRAY'); % black background in night mode if nightMode frame(:) = 0; end if initCorners % automatic points initialization pts = detectPoints(next); initCorners = false; elseif ~isempty(pts) % track points pts = trackPoints(pts, prev, next); % draw points frame = cv.circle(frame, pts, 3, ... 'Color',[0 255 0], 'Thickness','Filled'); end % show output set(h.img, 'CData',frame); drawnow limitrate; % next iteration prev = next; end cap.release(); % release video source
Callback functions
function onMouseDown(~,~) %ONMOUSEDOWN Event handler for mouse down on figure % current mouse location p = get(h.ax, 'CurrentPoint'); p = p(1,1:2) - 1; % check if user clicked close to an existing point if ~isempty(pts) [d,idx] = cv.batchDistance(p, pts, 'K',1); if d <= 5 % remove point pts(idx,:) = []; return; end end % check if we still have room for more points if size(pts,1) <= 300 % add point pts(end+1,:) = p; end end function onType(~,e) %ONTYPE Event handler for key press on figure switch e.Key case 'h' helpdlg({ 'To add/remove points, click with the mouse.' 'Hot keys:' 'h - this help dialog' 'q - quit the program' 'n - switch "night" mode on/off' 'c - clear all the points' 'r - initialize tracking by auto-detecting corners' }); case {'q', 'escape'} close(h.fig); case 'n' nightMode = ~nightMode; case 'c' pts = []; case 'r' initCorners = true; end end
end
Helper functions
function pts = detectPoints(gray) %DETECTPOINTS Find and refine corners in image % detect corners pts = cv.goodFeaturesToTrack(gray, ... 'MaxCorners',100, 'QualityLevel',0.01, 'MinDistance',10); if isempty(pts) pts = []; return; end % refine corners pts = cv.cornerSubPix(gray, pts, 'WinSize',[10 10], ... 'Criteria',struct('type','Count+EPS', 'maxCount',20, 'epsilon',0.03)); % return Nx2 matrix of points pts = cat(1, pts{:}); end function pts = trackPoints(pts, prev, next) %TRACKPOINTS Calculate flow of sparse points % calculate sparse optical flow [pts, status] = cv.calcOpticalFlowPyrLK(prev, next, pts, ... 'WinSize',[31 31], 'MinEigThreshold',0.001); % drop points that are not found pts = cat(1, pts{status == 1}); end function h = buildGUI(img) %BUILDGUI Creates the UI % build the user interface (no resizing to keep it simple) sz = size(img); h = struct(); h.fig = figure('Name','LK Demo', 'NumberTitle','off', 'Menubar','none', ... 'Pointer','cross', 'Resize','off', 'Position',[200 200 sz(2) sz(1)]); if ~mexopencv.isOctave() %HACK: not implemented in Octave movegui(h.fig, 'center'); end h.ax = axes('Parent',h.fig, 'Units','normalized', 'Position',[0 0 1 1]); if ~mexopencv.isOctave() h.img = imshow(img, 'Parent',h.ax); else %HACK: https://savannah.gnu.org/bugs/index.php?45473 axes(h.ax); h.img = imshow(img); end end