private static Mat ExtractFace(Mat img, FaceCorners corners)
        {
            double widthA   = Math.Sqrt(Math.Pow(corners.BottomRight.X - corners.BottomLeft.X, 2) + Math.Pow(corners.BottomRight.Y - corners.BottomLeft.Y, 2));
            double widthB   = Math.Sqrt(Math.Pow(corners.TopRight.X - corners.TopLeft.X, 2) + Math.Pow(corners.TopRight.Y - corners.TopLeft.Y, 2));
            double maxWidth = Math.Max(widthA, widthB);

            double heightA   = Math.Sqrt(Math.Pow(corners.TopRight.X - corners.BottomRight.X, 2) + Math.Pow(corners.TopRight.Y - corners.BottomRight.Y, 2));
            double heightB   = Math.Sqrt(Math.Pow(corners.TopLeft.X - corners.BottomLeft.X, 2) + Math.Pow(corners.TopLeft.Y - corners.BottomLeft.Y, 2));
            double maxHeight = Math.Max(heightA, heightB);

            var a = Array.ConvertAll(corners.Points, p => (PointF)p);

            var perspectiveTransformationMatrix = CvInvoke.GetPerspectiveTransform(new VectorOfPointF(a), new VectorOfPointF(new[] {
                new PointF(0, 0),
                new PointF((int)maxWidth - 1, 0),
                new PointF((int)maxWidth - 1, (int)maxHeight - 1),
                new PointF(0, (int)maxHeight - 1),
            }));

            var warped = new Mat((int)maxWidth, (int)maxHeight, DepthType.Cv32F, 1);

            CvInvoke.WarpPerspective(img, warped, perspectiveTransformationMatrix, new Size((int)maxWidth, (int)maxHeight));

            return(warped);
        }
        private static FaceCorners FindFaceCorners(Mat img, VectorOfKeyPoint trackedFeatures)
        {
            var rect = CvInvoke.BoundingRectangle(Utils.GetPointsVector(trackedFeatures));

            var edges = new Mat();

            CvInvoke.Canny(img, edges, 0.1, 99);

            var contours  = new VectorOfVectorOfPoint();
            var hierarchy = new Mat();

            var matchedContours = new List <VectorOfPoint>();

            CvInvoke.FindContours(edges, contours, hierarchy, RetrType.Tree, ChainApproxMethod.ChainApproxSimple);

            for (int i = 0; i < contours.Size; i++)
            {
                var approx = new Mat();
                CvInvoke.ApproxPolyDP(contours[i], approx, 9, true);

                var approxMat = new Matrix <double>(approx.Rows, approx.Cols, approx.DataPointer);

                double a = Math.Abs(CvInvoke.ContourArea(approx));
                if (approxMat.Rows == 4 && Math.Abs(CvInvoke.ContourArea(approx)) > 3000 &&
                    CvInvoke.IsContourConvex(approx))
                {
                    matchedContours.Add(contours[i]);
                }
            }

            var contoursArray = matchedContours.ToArray().SelectMany(c => c.ToArray()).ToArray();

            var contour = matchedContours.FirstOrDefault();
            var bbox    = CvInvoke.BoundingRectangle(contour);

            int minX = contoursArray.Min(p => p.X);
            int maxX = contoursArray.Max(p => p.X);

            if (!(maxX - minX > 2.9 * bbox.Width))
            {
                return(new FaceCorners());
            }

            while (true)
            {
                bool sutisfied = contoursArray.All(p => p.X > rect.X && p.Y > rect.Y && p.X < (rect.X + rect.Width) && p.Y < (rect.Y + rect.Height));
                if (sutisfied)
                {
                    break;
                }

                if (contoursArray.Any(c => c.X < rect.X))
                {
                    rect.X -= 5;
                }

                if (contoursArray.Any(c => c.Y < rect.Y))
                {
                    rect.Y -= 5;
                }

                if (contoursArray.Any(c => c.X > (rect.X + rect.Width)))
                {
                    rect.Width += 5;
                }

                if (contoursArray.Any(c => c.Y > (rect.Y + rect.Height)))
                {
                    rect.Height += 5;
                }
            }

            var rectPoints = new[]
            {
                rect.Location,
                new PointF(rect.X, rect.Y + rect.Height),
                new PointF(rect.X + rect.Width, rect.Y),
                new PointF(rect.X + rect.Width, rect.Y + rect.Height)
            };
            var points2D = Array.ConvertAll(rectPoints, Point.Round);

            var corners = new FaceCorners(points2D);

            return(corners);
        }