public override IObservable <KeyPointOpticalFlow> Process(IObservable <Tuple <KeyPointCollection, IplImage> > source) { return(Observable.Defer(() => { IplImage previousImage = null; IplImage previousPyramid = null; IplImage currentPyramid = null; return source.Select(input => { var previous = input.Item1; var currentImage = input.Item2; var currentKeyPoints = new KeyPointCollection(currentImage); if (previous.Count == 0) { return new KeyPointOpticalFlow(previous, currentKeyPoints); } if (currentPyramid == null || currentPyramid.Size != currentImage.Size) { previousImage = null; previousPyramid = new IplImage(currentImage.Size, currentImage.Depth, currentImage.Channels); currentPyramid = new IplImage(currentImage.Size, currentImage.Depth, currentImage.Channels); } var maxIterations = MaxIterations; var epsilon = Epsilon; var terminationType = TermCriteriaType.None; if (maxIterations > 0) { terminationType |= TermCriteriaType.MaxIter; } if (epsilon > 0) { terminationType |= TermCriteriaType.Epsilon; } var termCriteria = new TermCriteria(terminationType, maxIterations, epsilon); var flags = previousImage == previous.Image ? LKFlowFlags.PyrAReady : LKFlowFlags.None; var previousFeatures = new Point2f[previous.Count]; for (int i = 0; i < previousFeatures.Length; i++) { previousFeatures[i] = previous[i]; } var currentFeatures = new Point2f[previousFeatures.Length]; var status = new byte[previousFeatures.Length]; var trackError = new float[previousFeatures.Length]; CV.CalcOpticalFlowPyrLK( previous.Image, currentImage, previousPyramid, currentPyramid, previousFeatures, currentFeatures, WindowSize, Level, status, trackError, termCriteria, flags); var previousKeyPoints = new KeyPointCollection(previous.Image); for (int i = 0; i < status.Length; i++) { if (status[i] == 0 || trackError[i] > MaxError || currentFeatures[i].X <0 || currentFeatures[i].Y <0 || currentFeatures[i].X> currentImage.Width - 1 || currentFeatures[i].Y> currentImage.Height - 1) { continue; } previousKeyPoints.Add(previousFeatures[i]); currentKeyPoints.Add(currentFeatures[i]); } var temp = currentPyramid; currentPyramid = previousPyramid; previousPyramid = temp; previousImage = currentImage; return new KeyPointOpticalFlow(previousKeyPoints, currentKeyPoints); }); })); }