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)); } }
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); }