public List <DetectedCard> Detect(Bitmap bitmap, out Bitmap detectionImage)
        {
            var imageTools    = new ImageTools();
            var detectedCards = new List <DetectedCard>();
            // Greyscale
            var greyscaleImage = imageTools.GreyscaleEdgeDetectionImage(bitmap);

            var bitmapData = greyscaleImage.LockBits(
                new Rectangle(0, 0, greyscaleImage.Width, greyscaleImage.Height),
                ImageLockMode.ReadWrite, greyscaleImage.PixelFormat);

            var blobCounter = new BlobCounter
            {
                FilterBlobs = true,
                MinHeight   = Convert.ToInt32(BlobHeight * scaleFactor),
                MinWidth    = Convert.ToInt32(BlobWidth * scaleFactor)
            };

            blobCounter.ProcessImage(bitmapData);
            var blobs = blobCounter.GetObjectsInformation();

            greyscaleImage.UnlockBits(bitmapData);

            var shapeChecker = new SimpleShapeChecker();

            var cardPositions = new List <IntPoint>();


            // Loop through detected shapes
            for (var i = 0; i < blobs.Length; i++)
            {
                var edgePoints = blobCounter.GetBlobsEdgePoints(blobs[i]);

                // is triangle or quadrilateral
                if (!shapeChecker.IsConvexPolygon(edgePoints, out var corners))
                {
                    continue;
                }

                var subType = shapeChecker.CheckPolygonSubType(corners);

                // Only return 4 corner rectanges
                if (subType != PolygonSubType.Parallelogram && subType != PolygonSubType.Rectangle ||
                    corners.Count != 4)
                {
                    continue;
                }

                corners = RotateCard(corners);

                // Prevent it from detecting the same card twice
                if (cardPositions.Any(point => corners[0].DistanceTo(point) < Convert.ToInt32(40 * scaleFactor)))
                {
                    continue;
                }

                // Hack to prevent it from detecting smaller sections of the card instead of the whole card
                if (GetArea(corners) < Convert.ToInt32(DetectionThreshold * scaleFactor))
                {
                    continue;
                }

                cardPositions.Add(corners[0]);

                var cardBitmap = imageTools.GetDetectedCardImage(corners, bitmap, scaleFactor);

                var card = new DetectedCard
                {
                    Corners    = corners,
                    CardBitmap = cardBitmap
                };

                detectedCards.Add(card);
            }

            detectionImage = greyscaleImage;
            return(detectedCards);
        }
 public IdentifiedCard(DetectedCard detectedCard, Card bestMatch)
 {
     Card    = bestMatch;
     Corners = detectedCard.Corners;
 }