/// <summary> /// Estimates LK optical flow. /// </summary> /// <param name="prevImg">Previous image.</param> /// <param name="currImg">Current image.</param> /// <param name="prevFeatures">Previous features.</param> /// <param name="currFeatures">Current features.</param> /// <param name="status">Feature status.</param> /// <param name="error">Normalized tracking error [0..1].</param> /// <param name="windowSize">Aperture size.</param> /// <param name="iterations">Maximal number of iterations. If <paramref name="minFeatureShift"/> is reached then number of iterations will be lower.</param> /// <param name="minFeatureShift">Minimal feature shift in horizontal or vertical direction.</param> /// <param name="minEigenValue">Minimal eigen value. /// Eigen values could be interpreted as lengths of ellipse's axes. /// If zero ellipse turn into line therefore there is no corner.</param> /// <param name="maxError">Maximal allowable error for <paramref name="error"/>.</param> /// <param name="initialEstimate">Initial estimate shifts input features by specified amount. Default is zero. /// Used for pyramidal implementation.</param> public static void EstimateFlow(Image <TColor, float> prevImg, Image <TColor, float> currImg, PointF[] prevFeatures, out PointF[] currFeatures, out KLTFeatureStatus[] status, out float[] error, int windowSize = 15, int iterations = 30, float minFeatureShift = 0.1f, float minEigenValue = 0.01f, float maxError = 0.1f, PointF[] initialEstimate = null) { storage = new PyrLKStorage <TColor>(0); storage.Process(prevImg, currImg); EstimateFlow(storage, prevFeatures, out currFeatures, out status, out error, windowSize, iterations, minFeatureShift, minEigenValue, maxError, initialEstimate, 0); storage.Dispose(); }
/// <summary> /// Estimates LK optical flow. /// </summary> /// <param name="storage">Used storage. Number of pyramid levels is specified within storage. Use storage to gain performance in video* by 2x! </param> /// <param name="prevFeatures">Previous features.</param> /// <param name="currFeatures">Current features.</param> /// <param name="status">Feature status.</param> /// <param name="error">Normalized tracking error [0..1].</param> /// <param name="windowSize">Aperture size.</param> /// <param name="iterations">Maximal number of iterations. If <paramref name="minFeatureShift"/> is reached then number of iterations will be lower.</param> /// <param name="minFeatureShift">Minimal feature shift in horizontal or vertical direction.</param> /// <param name="minEigenValue">Minimal eigen value. /// Eigen values could be interpreted as lengths of ellipse's axes. /// If zero ellipse turn into line therefore there is no corner.</param> /// <param name="maxError">Maximal allowable error for <paramref name="error"/>.</param> public static void EstimateFlow(PyrLKStorage <TColor> storage, PointF[] prevFeatures, out PointF[] currFeatures, out KLTFeatureStatus[] status, out float[] error, int windowSize = 15, int iterations = 30, float minFeatureShift = 0.1f, float minEigenValue = 0.001f, float maxError = 0.1f) { var initialEstimate = new PointF[prevFeatures.Length]; currFeatures = new PointF[prevFeatures.Length]; error = new float[prevFeatures.Length]; status = new KLTFeatureStatus[prevFeatures.Length]; var scaledPrevFeatures = prevFeatures.Apply(x => x.DownScale(storage.PyrLevels)); var usedIndicies = Enumerable.Range(0, prevFeatures.Length).ToArray(); for (int pyrLevel = storage.PyrLevels; pyrLevel >= 0; pyrLevel--) { PointF[] levelCurrFeatures; float[] levelError; KLTFeatureStatus[] levelStatus; LKOpticalFlow <TColor> .EstimateFlow(storage, scaledPrevFeatures.GetAt(usedIndicies), out levelCurrFeatures, out levelStatus, out levelError, windowSize, iterations, minFeatureShift, minEigenValue, maxError, initialEstimate, pyrLevel); //update data currFeatures.SetAt(usedIndicies, levelCurrFeatures); error.SetAt(usedIndicies, levelError); status.SetAt(usedIndicies, levelStatus); if (pyrLevel != 0) { scaledPrevFeatures.ApplyInPlace(usedIndicies, x => x.UpScale()); currFeatures.ApplyInPlace(usedIndicies, x => x.UpScale()); initialEstimate = Sub(currFeatures, scaledPrevFeatures); usedIndicies = getValidFeatureIndicies(status); } } }
/// <summary> /// /// </summary> /// <param name="storage">Memory storage used for pyramid and derivative calculation. /// By using storage instead images if using sequential image input (e.g. frame by frame video) </param> /// <param name="prevFeatures">Previous features.</param> /// <param name="currFeatures">Current features.</param> /// <param name="status">Feature status.</param> /// <param name="error">Normalized tracking error [0..1].</param> /// <param name="windowSize">Aperture size.</param> /// <param name="iterations">Maximal number of iterations. If <paramref name="minFeatureShift"/> is reached then number of iterations will be lower.</param> /// <param name="minFeatureShift">Minimal feature shift in horizontal or vertical direction.</param> /// <param name="minEigenValue">Minimal eigen value. /// Eigen values could be interpreted as lengths of ellipse's axes. /// If zero ellipse turn into line therefore there is no corner.</param> /// <param name="maxError">Maximal allowable error for <paramref name="error"/>.</param> /// <param name="initialEstimate">Initial estimate shifts input features by specified amount. Default is zero.</param> /// <param name="storagePyrLevel">Used pyramid level for the input storage. Default is zero.</param> public static void EstimateFlow(PyrLKStorage <TColor> storage, PointF[] prevFeatures, out PointF[] currFeatures, out KLTFeatureStatus[] status, out float[] error, int windowSize = 15, int iterations = 30, float minFeatureShift = 0.1f, float minEigenValue = 0.01f, float maxError = 0.1f, PointF[] initialEstimate = null, int storagePyrLevel = 0) { LKOpticalFlow <TColor> .storage = storage; LKOpticalFlow <TColor> .storagePyrLevel = storagePyrLevel; initialEstimate = initialEstimate ?? new PointF[prevFeatures.Length]; PointF[] _currFeatures = new PointF[prevFeatures.Length]; KLTFeatureStatus[] _status = new KLTFeatureStatus[prevFeatures.Length]; float[] _error = new float[prevFeatures.Length]; Parallel.For(0, prevFeatures.Length, (int featureIdx) => //for (int featureIdx = 0; featureIdx < prevFeatures.Length; featureIdx++) { PointF currFeature; float featureError; KLTFeatureStatus featureStatus; EstimateFeatureFlow(storage.PrevImgPyr[storagePyrLevel], storage.CurrImgPyr[storagePyrLevel], prevFeatures[featureIdx], out currFeature, out featureStatus, out featureError, windowSize, iterations, minFeatureShift, minEigenValue, maxError, initialEstimate[featureIdx]); lock (_currFeatures) lock (_error) lock (_status) { _currFeatures[featureIdx] = currFeature; _error[featureIdx] = featureError; _status[featureIdx] = featureStatus; } }); currFeatures = _currFeatures; error = _error; status = _status; }