Sudoku Image Recognition
Someone had a good question, on how I read the Sudoku image digits
and determine the numbers 1 through 9 using VB6.
|
One hundred grid corners found |
The USB camera delivers a bitmaped 640x480 image. The Sudoku grid
in the image is a rhomboid of about 300 pixels across, so each small Sudoku
cell is only about 30 pixels square. For character recognition proposes, it is
very important to have each square’s precise corner coordinates. There are 100
grid corner points, each with VB6 picturebox XY location values. The integer
array value of GridX(0,0) is the x location of top and left-most corner of the
Sudoku grid. The array value of GridY(9,9) is the Y location of top and
right-most corner of the Sudoku grid. Unfortunately, the perfect square of the
Sudoku grid is warped by the laws of perspective, such that areas closer to the
camera appear larger. In this image, my camera is to the left of the puzzle such that the left part of the grid appears larger than the right. I did some intense calculating
until I was able to precisely determine each grid corner location. The grid
corners are marked with blue dots.
|
Cartoons |
So, how to determine a number in a grid box? The algorithm I use,
is to look for the best match to a cartoon image of the number. I made ten
bitmapped cartoon 50x50 images named Cartoon0.bmp to Cartoon9.bmp. I made the
cartoon images on photoshop-like software and they are pattered exactly the same
as the newspaper Sudoku font. The cartoons have black numbers on a red oval
background. To speed up my sluggish VB6 program, I read the cartoon images into
an integer array called Cartoon(9, 50, 50) where a value of 0 = white, 1 =
black and 2 = red.
|
Cartoon warping |
The cartoons are not the same size or shape of the Sudoku grid
boxes; each cartoon must be stretched and warped such that it’s corners match up
with the grid corners. Since the cartoons are all the same size, this warping
only needs to be done once per grid box and takes the form of 50x50 point look-up arrays named PX(50,50) and PY(50,50). Each pixel of a cartoon gets a
corresponding XY coordinate on the Sudoku grid box. First, the four PX&PY
corners (0,0), (0,50), (50,0), (50,50) are set equal to the four grid box
corners. Then look-ups for the top and bottom rows are calculated by dividing
those rows into 50 equal distant points. Now each column has top and bottom
coordinates and each column is divided into 50 equal segments. The result is a
warped cartoon to perfectly match the Sudoku box. Not only has the square shape of the cartoon been morphed into a rhomboid, but the cartoon number image itself has been morphed into the appropriate shape as if viewed from the perspective of the camera!
Cartoon pixels are looked at one by one and are graded against the
corresponding Sudoku image pixels:
If a black cartoon pixel falls on a black Sudoku pixel; add a
point.
If a black cartoon pixel falls on a white Sudoku pixel; subtract
two points.
If a red cartoon pixel falls on a white Sudoku pixel; add a point.
If a red cartoon pixel falls on a black Sudoku pixel; subtract two
points.
White cartoon points are not graded. Each cartoon has 924 graded
points.
The cartoon number with the highest grade is the number in the
puzzle.
To get the best possible fit between cartoon and image, I jiggle
the cartoon + 7 pixels back-and-forth and up-and-down while
grading. If a cell has a very high score with the blank (cartoon0) then it is
rapidly marked as an empty cell. Amazingly, smudges and other artifacts on the
Sudoku image end up being graded as empty cells.
If the corners of the grid boxes are accurately determined then
this method of number recognition works well; better than 99%. This method is
font specific, so if other characters or fonts are to be read, then additional
cartoons would need to be made. The character recognition is CPU intensive and
takes about one second per non-blank character.
This VB6 program is terribly unstructured and gangly. It is not
suitable for some kind of plug and play or even for basic sharing for that
matter, but I will post some snippets as requested. The integer array
Mono(640,480) contains the Sudoku monochromatic image: values of 0 = white and
1 = black. I do not think these snips will help anyone, but good luck just the
same.
----------------------------------------------------------------------
Private Sub LoadCartoons() ‘ loads bitmaped cartoon images into
Cartoon array
Dim I As Integer, J As Integer, K As Integer
Dim C As Long
For K = 0 To 9
For I = 0 To 50
For J = 0 To 50
C =
picCartoon(K).Point(I, J)
If C < 10 Then
Cartoon(K, I, J) = 1 ' Black
If C > 99999 Then
Cartoon(K, I, J) = 0 ' White
If C > 10 And C <
99999 Then Cartoon(K, I, J) = 2 ' red
Next J
Next I
Next K
End Sub
'----------------------------------------------------------------------
‘This is where I make cartoon point lookup arrays PX(50,50) and
PY(50,50).
‘GX and GY are the cell of interest upper left grid point values.
‘GX and GY values are integers from 0 to 8.
‘XOff and Yoff are offset values I use to giggle the cartoon for
the best fit.
Private Sub MakeLookups(GX As Integer, GY As Integer, XOff As
Integer, YOff As Integer) ' fill PX and PY arrays
Dim I As Integer, J As Integer
Dim dX As Integer, dY As Integer
‘These are the lookups for the four corners of the cartoon.
PX(0, 0) = GridX(GX, GY) + XOff: PY(0, 0) = GridY(GX, GY) + YOff
PX(0, 50) = GridX(GX, GY + 1) + XOff: PY(0, 50) = GridY(GX, GY +
1) + YOff
PX(50, 0) = GridX(GX + 1, GY) + XOff: PY(50, 0) = GridY(GX + 1,
GY) + YOff
PX(50, 50) = GridX(GX + 1, GY + 1) + XOff: PY(50, 50) = GridY(GX +
1, GY + 1) + YOff
dX = PX(50, 0) - PX(0, 0) 'pos
dY = PY(50, 0) - PY(0, 0) ' top row deltas ±
For I = 1 To 49
' Calculate cartoon lookups
PX(I, 0) = Int(PX(0, 0)
+ dX * I / 50 + 0.5) 'Top row
PY(I, 0) = Int(PY(0, 0)
+ dY * I / 50 + 0.5)
Next I
dX = PX(50, 50) - PX(0, 50) 'pos
dY = PY(50, 50) - PY(0, 50) ' bottom row deltas ±
For I = 1 To 49
' Calculate cartoon lookups
PX(I, 50) = Int(PX(0,
50) + dX * I / 50 + 0.5) 'bottom row
PY(I, 50) = Int(PY(0,
50) + dY * I / 50 + 0.5)
Next I
For I = 1 To 49 ' calculate column cartoon lookups
dY = PY(I, 50) - PY(I,
0) 'pos
dX = PX(I, 50) - PX(I,
0) ' colunm deltas ±
For J = 1 To 49
PY(I, J) =
Int(PY(I, 0) + dY * J / 50 + 0.5) 'columns
PX(I, J) =
Int(PX(I, 0) + dX * J / 50 + 0.5)
Next J
Next I
End Sub
'----------------------------------------------------------------------
‘A single cartoon (Number) is scored against a Sudoku cell. A
score is returned.
Private Function ScoreCell(Number As Integer) As Integer
Dim C As Integer, Total As Integer, S As Integer
ScoreCell = 0
Total = 0
For I = 0 To 50
For J = 0 To 50
C = Cartoon(Number, I,
J)
If C <> 0 Then
Total = Total + 1 'total non-white points (924)
If C = 1 Then 'black
cartoon
Total = Total + 1
If Mono(PX(I, J),
PY(I, J)) = 1 Then S = S + 1 'black on black
If Mono(PX(I, J),
PY(I, J)) = 0 Then S = S - 2 'black on white
End If
If C = 2 Then ' red
cartoon
If Mono(PX(I, J),
PY(I, J)) = 0 Then S = S + 1 ' red on white
If Mono(PX(I, J),
PY(I, J)) = 1 Then S = S - 2 ' red on black
End If
Next J
Next I
If Total = 0 Then Exit Function
ScoreCell = S / Total * 100
If Number = 0 Then ScoreCell = ScoreCell - 20 ' blank cell is a
little too hot!
End Function
'----------------------------------------------------------------------