I am writing a simple OCR solution for a finite set of characters. That is, I know the exact way all 26 letters in the alphabet will look like. I am using C# and am able to easi
Alright, I figured out the solution.
You simply use a depth first search on every single pixel with every other pixel combination, until you find the set of unique characteristics of the letter. While performing the depth first search, make sure you don't start at x=0 and y=0 each time since you only want to process each combination only once, so what you end up doing is incrementing the x and y values in each iteration.
I created a helper object which contains these properties:
public Point LastPoint { get; set; }
public List<OcrChar> CharsWithSimilarProperties { get; set; }
public List<Point> BlackPixels { get; set; }
public List<Point> WhitePixels { get; set; }
For every iteration, if I couldn't find a unique characteristic (e.g. all other letters have this pixel as black but this letter has it as white... or the inverse) I add all subsequent pixels to a queue which is being processed, by creating an instance of this above object with the properties properly set.
Some psuedo code:
rootNode.LastPoint = new Point(-1, -1)
rootNode.CharsWithSimilarProperties = all letters in alphabet except for this one
queue.Add(rootNode)
while queue.HasNodes()
for each pixel after node.LastPoint
if node.IsBlackPixel(pixel) && node.CharsWithSimilarProperties.IsAlwaysWhite(pixel)
node.BlackPixels.Add(pixel)
return node.BlackPixels and node.WhitePixels
if node.IsWhitePixel(pixel) && node.CharsWithSimilarProperties.IsAlwaysBlack(pixel)
node.WhitePixels.Add(pixel)
return node.BlackPixels and node.WhitePixels
newNode = new Node();
newNode.BlackPixels = node.BlackPixels.Copy();
newNode.WhitePixels = node.WhitePixels.Copy();
newNode.LastPoint = pixel
if node.IsBlackPixel(pixel)
newNode.BlackPixels.Add(pixel)
newNode.CharsWithSimilarProperties = list of chars from node.CharsWithSimilarProperties that also had this pixel as black
else
newNode.WhitePixels.Add(pixel)
newNode.CharsWithSimilarProperties = list of chars from node.CharsWithSimilarProperties that also had this pixel as white
queue.Add(newNode)
To determine if "node.CharsWithSimilarProperites.IsAlwaysWhite()" or "IsAlwaysBlack()", you can generate a compositeMap in each iteration of the queue:
for each pixel after node.LastPoint
for each char in node.CharsWithSimilarProperties
if char.IsBlackPixel(pixel)
compositeMap[pixel].Add(char)
Before doing all this, I also processed the entire alphabet to find pixels that are always white or always black, since these can never be used. I added them to a List<Point> ignoredPixels
, and every time I iterate over pixels, I always use if (ignoredPixels[x, y]) continue;
.
This works perfectly and is really fast. Although keep in mind that this part of my solution doesn't need to be fast at all since it is a one-time optimization which helps me later on. In my test cases of a maximum of 8 chars per "alphabet" set, it usually produces one or two characteristics for each character. I have yet to run it on a full set of 26 characters.