gusucode.com > vision工具箱matlab源码程序 > vision/opticalFlowFarneback.m
%opticalFlowFarneback Estimate optical flow using Farneback algorithm. % obj = opticalFlowFarneback returns an optical flow object, obj, that % estimates the direction and speed of object motion from previous video % frame to the current one using Gunnar Farneback algorithm. % % obj = opticalFlowFarneback(Name, Value) specifies additional name-value % pairs described below: % % 'NumPyramidLevels' Number of pyramid layers including the initial image. % Value of 1 means that no extra layers are created % and only the original images are used. % % Default: 3 % % 'PyramidScale' Image scale to build pyramids for each image in each % level. It's a scalar between 0 and 1. Value of 0.5 % creates a classical pyramid, where each next layer % is twice smaller than the previous one. % % Default: 0.5 % % 'NumIterations' Number of iterations spent computing at each pyramid % level. % % Default: 3 % % 'NeighborhoodSize' Size of the pixel neighborhood. Larger values yield % more robust and more blurred motion field. Typical % value is 5 or 7. % % Default: 5 % % 'FilterSize' Averaging filter size. A Gaussian filter of size % FilterSize x FilterSize is used to smooth out the % neighboring displacements. Larger values are used % for fast motion detection. % % Default: 15 % % opticalFlowFarneback properties: % NumPyramidLevels - Number of pyramid layers including the initial image % PyramidScale - Image scale to build pyramids for each image % NumIterations - Number of iterations % NeighborhoodSize - Size of the pixel neighborhood % FilterSize - Averaging filter size % % opticalFlowFarneback methods: % estimateFlow - Estimates the optical flow % reset - Resets the internal state of the object % % Example - Compute and display optical flow % ------------------------------------------ % vidReader = VideoReader('visiontraffic.avi', 'CurrentTime', 11); % opticFlow = opticalFlowFarneback; % while hasFrame(vidReader) % frameRGB = readFrame(vidReader); % frameGray = rgb2gray(frameRGB); % % Compute optical flow % flow = estimateFlow(opticFlow, frameGray); % % Display video frame with flow vectors % imshow(frameRGB) % hold on % plot(flow, 'DecimationFactor', [5 5], 'ScaleFactor', 2) % drawnow % hold off % end % % See also opticalFlowHS, opticalFlowLK, opticalFlowLKDoG, opticalFlow, % opticalFlow>plot. % Copyright 2015 MathWorks, Inc. % % References: % Gunnar Farneback. "Two-Frame Motion Estimation Based on Polynomial % Expansion". Proceedings of the 13th Scandinavian Conference on Image % Analysis, Gothenburg, Sweden 2003 classdef opticalFlowFarneback < handle & vision.internal.EnforceScalarHandle %#codegen %#ok<*EMCLS> %#ok<*EMCA> %#ok<*MCSUP> %------------------------------------------------------------------------ % Public properties which can only be set in the constructor %------------------------------------------------------------------------ properties(SetAccess=public) %NumPyramidLevels Number of pyramid layers including the initial image NumPyramidLevels = int32(3); %PyramidScale Image scale to build pyramids for each image PyramidScale = 0.5; %NumIterations Number of iterations NumIterations = int32(3); %NeighborhoodSize Size of the pixel neighborhood NeighborhoodSize = int32(5); %FilterSize Averaging filter size FilterSize = int32(15); end %------------------------------------------------------------------------ % Hidden properties used by the object %------------------------------------------------------------------------ properties(Hidden, Access=private) pInRows = 0; pInCols = 0; pFirstCall; pPreviousFrameBuffer; end methods %---------------------------------------------------------------------- % Constructor %---------------------------------------------------------------------- function obj = opticalFlowFarneback(varargin) % Parse the inputs. if isSimMode() [tmpPyramidScale, tmpNumPyramidLevels, tmpFilterSize, ... tmpNumIterations, tmpNeighborhoodSize] ... = parseInputsSimulation(obj, varargin{:}); else % Code generation [tmpPyramidScale, tmpNumPyramidLevels, tmpFilterSize, ... tmpNumIterations, tmpNeighborhoodSize] ... = parseInputsCodegen(obj, varargin{:}); end checkPyramidScale(tmpPyramidScale); checkNumPyramidLevels(tmpNumPyramidLevels); checkFilterSize(tmpFilterSize); checkNumIterations(tmpNumIterations); checkNeighborhoodSize(tmpNeighborhoodSize); obj.PyramidScale = double(tmpPyramidScale); obj.NumPyramidLevels = int32(tmpNumPyramidLevels); obj.FilterSize = int32(tmpFilterSize); obj.NumIterations = int32(tmpNumIterations); obj.NeighborhoodSize = int32(tmpNeighborhoodSize); obj.pFirstCall = true; end %---------------------------------------------------------------------- % Predict method %---------------------------------------------------------------------- function outFlow = estimateFlow(obj, ImageCurr) %, varargin) % estimateFlow Estimates the optical flow % flow = estimateFlow(obj, I) estimates the optical flow between % the current frame I and the previous frame. % % Notes % ----- % - output flow is an object of class <a href="matlab:help('opticalFlow')">opticalFlow</a> that stores % velocity matrices. % - Class of input, I, can be double, single, uint8, int16, or logical. % - For the very first frame, the previous frame is set to black. checkImage(ImageCurr); if (~isSimMode()) % compile time error if ImageCurr is not fixed sized eml_invariant(eml_is_const(size(ImageCurr)), ... eml_message('vision:OpticalFlow:imageVarSize')); end if ~(obj.pFirstCall) inRows_ = size(ImageCurr,1); inCols_ = size(ImageCurr,2); condition = (obj.pInRows ~= inRows_) || (obj.pInCols ~= inCols_); coder.internal.errorIf(condition, 'vision:OpticalFlow:inputSizeChange'); end obj.pInRows = coder.const(size(ImageCurr,1)); obj.pInCols = coder.const(size(ImageCurr,2)); if isa(ImageCurr, 'double') otherDT = coder.const('double'); tmpImageCurr = im2uint8(ImageCurr); else otherDT = coder.const('single'); if isa(ImageCurr, 'uint8') tmpImageCurr = ImageCurr; else tmpImageCurr = im2uint8(ImageCurr); end end if((obj.pInRows==0) || (obj.pInCols==0)) velComponent = zeros(size(tmpImageCurr), otherDT); outFlow = opticalFlow(velComponent, velComponent); return; end if (obj.pFirstCall) bufferSize = size(tmpImageCurr); if ~isSimMode() bufferSize = flip(bufferSize); end obj.pPreviousFrameBuffer = zeros(bufferSize, 'like', tmpImageCurr); obj.pFirstCall = false; else coder.assertDefined(obj.pPreviousFrameBuffer); end ImagePrev = obj.pPreviousFrameBuffer; params.pyr_scale = double(obj.PyramidScale); params.levels = int32(obj.NumPyramidLevels); params.winsize = int32(obj.FilterSize); params.iterations = int32(obj.NumIterations); params.poly_n = int32(obj.NeighborhoodSize); params.poly_sigma = double(computePolySigma(obj.NeighborhoodSize)); params.flags = int32(computeOCVflags()); inFlowXY = single([]); if isSimMode() % mex function: input images uint8, input initial flow: single % output flow: (1) single and (2) concatenated % Output of matlab function: % (1) if input images double, output flow is double % else output flow is single % (2) There are two outputs (X component, y component) outFlowXY = ocvCalcOpticalFlowFarneback( ... ImagePrev, tmpImageCurr, inFlowXY, params); % assign output flow outVelReal = cast(outFlowXY(:,1:obj.pInCols), otherDT); outVelImag = cast(outFlowXY(:,(1:obj.pInCols)+obj.pInCols), otherDT); outFlow = opticalFlow(outVelReal, outVelImag); % prepare for next frame obj.pPreviousFrameBuffer = tmpImageCurr; else tmpImageCurrT = tmpImageCurr'; outFlowXY = vision.internal.buildable.opticalFlowFarnebackBuildable.opticalFlowFarneback_compute( ... ImagePrev, tmpImageCurrT, inFlowXY, params); % assign output flow outVelImag = cast(outFlowXY(:,1:obj.pInCols), otherDT); outVelReal = cast(outFlowXY(:,(1:obj.pInCols)+obj.pInCols), otherDT); outFlow = opticalFlow(outVelReal, outVelImag); % prepare for next frame obj.pPreviousFrameBuffer = tmpImageCurrT; end end %------------------------------------------------------------------ function set.NumPyramidLevels(this, numPyramidLevels) checkNumPyramidLevels(numPyramidLevels); this.NumPyramidLevels = int32(numPyramidLevels); end %------------------------------------------------------------------ function set.PyramidScale(this, pyramidScale) checkPyramidScale(pyramidScale); this.PyramidScale = double(pyramidScale); end %------------------------------------------------------------------ function set.NumIterations(this, numIterations) checkNumIterations(numIterations); this.NumIterations = int32(numIterations); end %------------------------------------------------------------------ function set.NeighborhoodSize(this, neighborhoodSize) checkNeighborhoodSize(neighborhoodSize); this.NeighborhoodSize = int32(neighborhoodSize); end %------------------------------------------------------------------ function set.FilterSize(this, filterSize) checkFilterSize(filterSize); this.FilterSize = int32(filterSize); end %---------------------------------------------------------------------- % Correct method %---------------------------------------------------------------------- function reset(obj) % reset Reset the internal state of the object % % reset(flow) resets the internal state of the object. It sets % the previous frame to black. obj.pFirstCall = true; end end methods(Access=private) %---------------------------------------------------------------------- % Parse inputs for simulation %---------------------------------------------------------------------- function [tmpPyramidScale, tmpNumPyramidLevels, tmpFilterSize, ... tmpNumIterations, tmpNeighborhoodSize] ... = parseInputsSimulation(obj, varargin) % Instantiate an input parser parser = inputParser; parser.FunctionName = mfilename; % Specify the optional parameters parser.addParameter('PyramidScale', obj.PyramidScale); parser.addParameter('NumPyramidLevels', obj.NumPyramidLevels); parser.addParameter('FilterSize', obj.FilterSize); parser.addParameter('NumIterations', obj.NumIterations); parser.addParameter('NeighborhoodSize', obj.NeighborhoodSize); % Parse parameters parse(parser, varargin{:}); r = parser.Results; tmpPyramidScale = r.PyramidScale; tmpNumPyramidLevels = r.NumPyramidLevels; tmpFilterSize = r.FilterSize; tmpNumIterations = r.NumIterations; tmpNeighborhoodSize = r.NeighborhoodSize; end %---------------------------------------------------------------------- % Parse inputs for code generation %---------------------------------------------------------------------- function [tmpPyramidScale, tmpNumPyramidLevels, tmpFilterSize, ... tmpNumIterations, tmpNeighborhoodSize] ... = parseInputsCodegen(obj, varargin) defaultsNoVal = struct( ... 'PyramidScale', uint32(0), ... 'NumPyramidLevels', uint32(0), ... 'FilterSize', uint32(0), ... 'NumIterations', uint32(0), ... 'NeighborhoodSize', uint32(0)); properties = struct( ... 'CaseSensitivity', false, ... 'StructExpand', true, ... 'PartialMatching', false); optarg = eml_parse_parameter_inputs(defaultsNoVal, properties, varargin{:}); tmpPyramidScale = eml_get_parameter_value(optarg.PyramidScale, ... obj.PyramidScale, varargin{:}); tmpNumPyramidLevels = eml_get_parameter_value(optarg.NumPyramidLevels, ... obj.NumPyramidLevels, varargin{:}); tmpFilterSize = eml_get_parameter_value(optarg.FilterSize, ... obj.FilterSize, varargin{:}); tmpNumIterations = eml_get_parameter_value(optarg.NumIterations, ... obj.NumIterations, varargin{:}); tmpNeighborhoodSize = eml_get_parameter_value(optarg.NeighborhoodSize, ... obj.NeighborhoodSize, varargin{:}); end end end %========================================================================== function flag = isSimMode() flag = isempty(coder.target); end %========================================================================== function checkImage(I) % Validate input image validateattributes(I,{'uint8', 'int16', 'double', 'single', 'logical'}, ... {'real','nonsparse', '2d'}, mfilename, 'ImageCurr', 1) end %========================================================================== function checkPyramidScale(PyramidScale) validateattributes(PyramidScale, {'numeric'}, {'nonempty', 'nonnan', ... 'finite', 'nonsparse', 'real', 'scalar', '>', 0, '<', 1}, ... mfilename, 'PyramidScale'); end %========================================================================== function checkNumPyramidLevels(NumPyramidLevels) validateattributes(NumPyramidLevels, {'numeric'}, ... {'nonempty', 'real', 'integer', 'nonnan', 'nonsparse', 'scalar', '>', 0}, ... mfilename, 'NumPyramidLevels'); end %========================================================================== function checkFilterSize(FilterSize) % For FilterSize=1 output flow is really noisy. Disallowing FilterSize=1 validateattributes(FilterSize, {'numeric'}, ... {'nonempty', 'real', 'integer', 'nonnan', 'nonsparse', 'scalar', '>', 1}, ... mfilename, 'FilterSize'); end %========================================================================== function checkNumIterations(NumIterations) validateattributes(NumIterations, {'numeric'}, ... {'nonempty', 'real', 'integer', 'nonnan', 'nonsparse', 'scalar', '>', 0}, ... mfilename, 'NumIterations'); end %========================================================================== function checkNeighborhoodSize(NeighborhoodSize) validateattributes(NeighborhoodSize, {'numeric'}, ... {'nonempty', 'real', 'integer', 'nonnan', 'nonsparse', 'scalar', '>', 0}, ... mfilename, 'NeighborhoodSize'); end %========================================================================== function flags = computeOCVflags() % here (1) initial flow is not used % (2) Gaussian filter is used flags = int32(256); end %========================================================================== function polySigma = computePolySigma(NeighborhoodSize) polySigma = double(NeighborhoodSize)*0.3; end