Optical Flow Estimation using Dual TV-L1 method
Sources:
Contents
Load a pair of images
frame0 = cv.imread(fullfile(mexopencv.root(),'test','basketball1.png'), 'Grayscale',true); frame1 = cv.imread(fullfile(mexopencv.root(),'test','basketball2.png'), 'Grayscale',true); assert(isequal(size(frame0), size(frame1)), 'Images should be of equal sizes'); if ~mexopencv.isOctave() && mexopencv.require('images') %HACK: IMPLAY not implemented in Octave implay(cat(4,frame0,frame1), 5); end
Compute optical flow
tvl1 = cv.DualTVL1OpticalFlow(); tic flow = tvl1.calc(frame0, frame1); toc
Elapsed time is 2.466695 seconds.
Draw optical flow
out = drawOpticalFlow(flow);
figure(1), imshow(out), title('Flow')
Draw velocities vector field
[X,Y,U,V] = drawVelocities(flow); figure(2) if ~mexopencv.isOctave() && mexopencv.require('images') imshowpair(frame0, frame1) %imshowpair(flow(:,:,1), flow(:,:,2)) else imshow(cat(3, frame1, frame0, frame1)) end hold on quiver(X(:), Y(:), U(:), V(:)); hold off
Write optical flow to file
.flo is a binary file format for flow data specified here: http://vision.middlebury.edu/flow/data/
fileName = fullfile(tempdir(), 'flow.flo')
writeOpticalFlowToFile(flow, fileName);
fileName = 'C:\Users\Amro\AppData\Local\Temp\flow.flo'
Helper functions
function [X,Y,U,V] = drawVelocities(flow, steps) if nargin < 2 % subsample in x/y directions for less dense output steps = [10 10]; end [rows,cols,~] = size(flow); R = 1:steps(2):rows; C = 1:steps(1):cols; [X,Y] = meshgrid(C,R); U = flow(R,C,1); V = flow(R,C,2); end function out = drawOpticalFlow(flow, maxrad) % mask of finite values isFlowCorrect = all(~isnan(flow) & abs(flow)<1e9, 3); % determine motion range if nargin < 2 rad = hypot(flow(:,:,1), flow(:,:,2)); maxrad = max(rad(isFlowCorrect)); if isempty(maxrad), maxrad = 1; end end flow = flow ./ maxrad; % compute color cmap = colorWheel(); NCOLS = size(cmap,1); rad = hypot(flow(:,:,1), flow(:,:,2)); a = atan2(-flow(:,:,2), -flow(:,:,1)) / pi; % [-pi,pi] -> [-1,1] fk = (a + 1) / 2 * (NCOLS - 1); % [-1,1] -> [0,NCOLS] k0 = int32(fk); % [0,NCOLS] -> 0:NCOLS k1 = rem(k0 + 1, NCOLS); % 0:NCOLS f = fk - single(k0); % [-0.5,0.5] col0 = reshape(permute(single(cmap(k0+1,:))/255, [1 3 2]), [size(k0) 3]); col1 = reshape(permute(single(cmap(k1+1,:))/255, [1 3 2]), [size(k1) 3]); col = bsxfun(@times, 1-f, col0) + bsxfun(@times, f, col1); if true mask = repmat(rad <= 1, [1 1 3]); tmp = 1 - bsxfun(@times, rad, 1 - col); col(mask) = tmp(mask); % increase saturation with radius col(~mask) = col(~mask) * 0.75; % out of range end out = uint8(255*col); %out = flip(out,3); out(~repmat(isFlowCorrect,[1 1 3])) = 0; end function cmap = colorWheel() if false % relative lengths of color transitions. These are chosen based on % perceptual similarity (e.g. one can distinguish more shades between % red and yellow than between yellow and green) steps = [15 6 4 11 13 6]; cmap = zeros(0,3,'uint8'); for i=1:numel(steps) c = zeros(steps(i),3); switch i case 1 c(:,1) = 255; c(:,2) = fix(255*(0:steps(i)-1)/steps(i)); c(:,3) = 0; case 2 c(:,1) = 255 - fix(255*(0:steps(i)-1)/steps(i)); c(:,2) = 255; c(:,3) = 0; case 3 c(:,1) = 0; c(:,2) = 255; c(:,3) = fix(255*(0:steps(i)-1)/steps(i)); case 4 c(:,1) = 0; c(:,2) = 255 - fix(255*(0:steps(i)-1)/steps(i)); c(:,3) = 255; case 5 c(:,1) = fix(255*(0:steps(i)-1)/steps(i)); c(:,2) = 0; c(:,3) = 255; case 6 c(:,1) = 255; c(:,2) = 0; c(:,3) = 255 - fix(255*(0:steps(i)-1)/steps(i)); end cmap = [cmap; uint8(c)]; end elseif false % similar thing, vectorized implementation steps = [15 6 4 11 13 6]; stops = [ 1 1 0 0 0 1 1; ... % R 0 1 1 1 0 0 0; ... % G 0 0 0 1 1 1 0 % B ]; cmap = zeros(0,3,'uint8'); for i=1:size(stops,2)-1 c = interp1([0 1], stops(:,i:i+1).', linspace(0,1,steps(i)+1)); cmap = [cmap; uint8(255 * c(1:end-1,:))]; end else % a fast approximation using builtin colormap cmap = uint8(255 * hsv(55)); end end function writeOpticalFlowToFile(flow, fileName) assert(isa(flow,'single') && size(flow,3)==2); if false % requires opencv_contrib "optflow" cv.writeOpticalFlow(fileName, flow); else % manually write .flo format fid = fopen(fileName, 'wb', 'l'); % binary, little-endian fwrite(fid, 'PIEH'); % signature fwrite(fid, [size(flow,2) size(flow,1)], 'int32'); % width, height fwrite(fid, permute(flow,[3 2 1]), 'single'); % interleaved (u,v) row-major order fclose(fid); end end