public static Bitmap PerformThresholding(Bitmap bitmap, int value = 100)
        {
            using (Bitmap grayscaleBitmap = ImageTransformation.ConvertToGreyscale(bitmap))
            {
                //grayscaleBitmap.Save(@"D:\k\trash\grayscaleBitmap.jpg");
                if (grayscaleBitmap.PixelFormat == PixelFormat.Format16bppGrayScale)
                {
                    value *= 255;
                }

                Threshold filter = new Threshold(value);
                return(filter.Apply(grayscaleBitmap));
            }
        }
Exemplo n.º 2
0
        public Bitmap SolveSudokuPhoto(Bitmap sudokuPhoto)
        {
            Bitmap transformedImage = ImageTransformation.TranformImage(sudokuPhoto);
            //transformedImage.Save(@"D:\k\trash\transformedImage.jpg");

            Bitmap thresholdedImage = ImageTransformation.PerformThresholding(transformedImage);
            //thresholdedImage.Save(@"D:\k\trash\thresholdedImage.jpg");


            //todo del
            //ImageTransformation.Test(transformedImage).Save(@"D:\k\trash\abc.jpg");


            //TODO: Threshold will probably be needed for noisy images
            //var thresholdFilter = new Threshold(100);
            //thresholdFilter.ApplyInPlace(image);
            var invertFilter  = new Invert();
            var invertedImage = invertFilter.Apply(thresholdedImage);

            //invertedImage.Save(@"D:\k\trash\invertedInput.jpg");


            var blobCounter = new BlobCounter
            {
                BackgroundThreshold = Color.FromArgb(255, 70, 70, 70)
            };

            blobCounter.ProcessImage(invertedImage);

            var invertedImageBlobs        = blobCounter.GetObjectsInformation();
            var boardBlobCandidates       = invertedImageBlobs;
            var biggestBoardBlobCandidate = boardBlobCandidates.OrderByDescending(b => b.Area).First();
            var boardBlob = biggestBoardBlobCandidate;

            var expectedCellBlobHeight = boardBlob.Rectangle.Height /
                                         SudokuBoard.NumberOfBoardCellsInSingleDirection;

            var expectedCellBlobWidth = boardBlob.Rectangle.Width /
                                        SudokuBoard.NumberOfBoardCellsInSingleDirection;

            var blobCellSizeTolerance = 0.25;

            blobCounter.ProcessImage(thresholdedImage);

            //todo
            thresholdedImage = transformedImage;

            var cellBlobCandidates = blobCounter.GetObjectsInformation();

            var cellBlobs = cellBlobCandidates.Where(
                b =>
                boardBlob.Rectangle.Contains(b.Rectangle) &&
                IsMatch(b.Rectangle.Width, expectedCellBlobWidth, blobCellSizeTolerance) &&
                IsMatch(b.Rectangle.Height, expectedCellBlobHeight, blobCellSizeTolerance)).ToArray();

            if (cellBlobs.Length != SudokuBoard.NumberOfBoardCells)
            {
                throw new InvalidOperationException();
            }

            var expectedCellsData =
                Enumerable.Range(0, SudokuBoard.NumberOfBoardCellsInSingleDirection)
                .SelectMany(
                    cvi =>
                    Enumerable.Range(0, SudokuBoard.NumberOfBoardCellsInSingleDirection)
                    .Select(chi => new
            {
                CellHorizontalIndex = chi,
                CellVerticalIndex   = cvi,
                ExpectedCellCenter  =
                    new Point((int)(boardBlob.Rectangle.X + (cvi + 0.5) * expectedCellBlobWidth),
                              boardBlob.Rectangle.Y + (int)((chi + 0.5) * expectedCellBlobHeight)),
            })).ToArray();

            var sudokuBoard = new SudokuBoard();
            var digitImages = new List <Bitmap>();
            var parsedDigitIndexToCellBlobMap = new Dictionary <int, Blob>();
            var lastParsedDigitIndex          = 0;

            foreach (var cellBlob in cellBlobs)
            {
                // TODO: There may be more than one blob candidate
                var digitBlob = invertedImageBlobs.SingleOrDefault(b => cellBlob.Rectangle.Contains(b.Rectangle));

                if (digitBlob == null)
                {
                    continue;
                }

                var digitImage = thresholdedImage.Clone(digitBlob.Rectangle, thresholdedImage.PixelFormat);
                digitImages.Add(digitImage);
                parsedDigitIndexToCellBlobMap[lastParsedDigitIndex++] = cellBlob;
            }

            IReadOnlyCollection <int> parsedDigits;

            using (var digitParser = new DigitParser())
            {
                parsedDigits = digitParser.ParseDigits(digitImages);
            }

            foreach (var digitImage in digitImages)
            {
                digitImage.Dispose();
            }

            for (var parsedDigitIndex = 0; parsedDigitIndex < parsedDigits.Count; parsedDigitIndex++)
            {
                var digitCellBlob = parsedDigitIndexToCellBlobMap[parsedDigitIndex];

                var expectedCellData =
                    expectedCellsData.Single(d => digitCellBlob.Rectangle.Contains(d.ExpectedCellCenter));

                var parsedDigit = parsedDigits.ElementAt(parsedDigitIndex);

                if (!SudokuBoard.ValidNumbers.Contains(parsedDigit))
                {
                    throw new InvalidOperationException();
                }

                sudokuBoard[expectedCellData.CellHorizontalIndex, expectedCellData.CellVerticalIndex] =
                    parsedDigit;
            }

            var solvedBoard = sudokuBoard.Solve();

            if (solvedBoard == null)
            {
                return(null);
            }

            Debug.Assert(solvedBoard.IsComplete() && solvedBoard.IsValid());

            var cellsToPrint = Enumerable.Range(0, SudokuBoard.NumberOfBoardCellsInSingleDirection)
                               .SelectMany(
                vi =>
                Enumerable.Range(0, SudokuBoard.NumberOfBoardCellsInSingleDirection)
                .Select(hi => new
            {
                HorizontalIndex = hi,
                VerticalIndex   = vi
            })).Where(c => sudokuBoard[c.HorizontalIndex, c.VerticalIndex] == null).Select(
                i =>
            {
                var expectedCellData =
                    expectedCellsData.Single(
                        d =>
                        d.CellHorizontalIndex == i.HorizontalIndex &&
                        d.CellVerticalIndex == i.VerticalIndex);

                var cellBlob =
                    cellBlobs.Single(
                        b => b.Rectangle.Contains(expectedCellData.ExpectedCellCenter));

                var cellCenter = new Point(cellBlob.Rectangle.X + cellBlob.Rectangle.Width / 2,
                                           cellBlob.Rectangle.Y + cellBlob.Rectangle.Height / 2);
                return(new Cell(i.HorizontalIndex, i.VerticalIndex, cellBlob.Rectangle));
            }).ToList();

            var solutionPhoto = PrintSolutionToSourceImage(thresholdedImage, cellsToPrint, solvedBoard);

            return(solutionPhoto);
        }