public override IObservable <CameraCalibration> Process(IObservable <KeyPointCollection[]> source)
        {
            return(Observable.Defer(() =>
            {
                var guess = false;
                var imageSize = new Size();
                var patternSize = new Size();
                var calibration = new CameraCalibration();
                var cameraMatrix = new Mat(3, 3, Depth.F32, 1);
                var distortion = new Mat(1, 5, Depth.F32, 1);
                Point3f[] chessboardCorners = null;
                return source.Select(input =>
                {
                    if (patternSize != PatternSize)
                    {
                        patternSize = PatternSize;
                        chessboardCorners = new Point3f[patternSize.Width * patternSize.Height];
                        for (int i = 0; i < chessboardCorners.Length; i++)
                        {
                            chessboardCorners[i].X = i % patternSize.Width;
                            chessboardCorners[i].Y = i / patternSize.Width;
                            chessboardCorners[i].Z = 0;
                        }
                    }

                    var flags = CalibrationFlags;
                    if (input.Length == 0)
                    {
                        if ((flags & CameraCalibrationFlags.UseIntrinsicGuess) == 0)
                        {
                            calibration = new CameraCalibration();
                        }
                        return calibration;
                    }

                    var totalPoints = 0;
                    var pointCounts = new int[input.Length];
                    for (int i = 0; i < pointCounts.Length; i++)
                    {
                        var image = input[i].Image;
                        if (input[i].Count == 0)
                        {
                            continue;
                        }
                        if (imageSize.Width == 0)
                        {
                            imageSize = image.Size;
                        }
                        else if (imageSize != image.Size)
                        {
                            throw new InvalidOperationException("Chessboard calibration features must come from same-sized images.");
                        }

                        pointCounts[i] = input[i].Count;
                        totalPoints += pointCounts[i];
                    }

                    var objectPoints = new Point3f[totalPoints];
                    var imagePoints = new Point2f[totalPoints];
                    totalPoints = 0;
                    for (int i = 0; i < input.Length; i++)
                    {
                        input[i].CopyTo(imagePoints, totalPoints);
                        Array.Copy(chessboardCorners, 0, objectPoints, totalPoints, input[i].Count);
                        totalPoints += input[i].Count;
                    }

                    if (!guess)
                    {
                        flags &= ~CameraCalibrationFlags.UseIntrinsicGuess;
                    }
                    using (var objectPts = Mat.CreateMatHeader(objectPoints, objectPoints.Length, 3, Depth.F32, 1))
                        using (var imagePts = Mat.CreateMatHeader(imagePoints, imagePoints.Length, 2, Depth.F32, 1))
                            using (var pointCts = Mat.CreateMatHeader(pointCounts, pointCounts.Length, 1, Depth.S32, 1))
                            {
                                calibration.ReprojectionError = CV.CalibrateCamera2(
                                    objectPts, imagePts, pointCts, imageSize,
                                    cameraMatrix, distortion, null, null, flags);
                                Intrinsics.FromCameraMatrix(cameraMatrix, distortion, imageSize, out calibration.Intrinsics);
                            }

                    guess = true;
                    return calibration;
                });
            }));
        }