优化的OCR黑/白像素算法

时间:2010-02-12 05:45:46

标签: algorithm optimization ocr

7 个答案:

答案 0 :(得分:4)

你可以创建一棵树。

选择一个像素,并根据像素为白色或黑色将字母分成两个桶。然后选择第二个像素,将桶分成两个桶,每个桶基于该像素,依此类推。

你可以尝试通过选择给出大小相等的桶的像素来优化树的深度。

创建树是一次性预处理步骤。你不应该多次这样做。

现在,当你得到一个匹配的字母表时,请根据设置/未设置的像素按照树来获取你的信件。

答案 1 :(得分:4)

我没有答案,但这里有一些关于你最终解决方案的界限:

如果您想直接“使用X像素作为关键字”,那么您至少需要ceiling(log2(number of characters))像素。您将无法消除较少位的字母歧义。在您的情况下,尝试找到5个像素相当于找到将字母拆分为独立分区的5个像素。这可能不是那么容易。

您还可以使用Moron(heheh)suggestion并根据您扫描的语言的字母频率构建一棵树,类似于Huffman coding。这将占用比每个字母5位更多的空间,但假设使用power-law distribution字母可能会更小。我会采用这种方法,因为它允许您搜索每个节点的特定分区,而不是搜索一组分区。

答案 2 :(得分:1)

我没有为您提供关键功能的算法,但这里有一些可能会有帮助的事情。

首先,我不会过分担心为每个字符寻找一个特征像素,因为平均而言,检查给定字符是否与二进制图像的给定条带(5x5)匹配不应超过5-7检查告诉没有匹配。为什么?可能性。对于7个二进制像素,有2 ** 7 = 128种不同的可能性。这意味着有1/128<角色匹配的概率为1%,甚至高达7像素。只要确保在发现不匹配时立即停止比较。

其次,如果您不想执行哈希表,则可以考虑使用trie来存储所有字符数据。它将使用更少的内存,您将立即检查所有字符。搜索哈希表的速度并不快,但您也不必转换为字符串。在树中的每个节点处,最多只能有2个后代。例如,如果你有两个2x2字符(我们称之为A和B):

A   B
01  00
10  11

你在第一个节点只有一个后代 - 只有左边(0分支)。我们继续下一个节点。它有两个后代,左(0)分支通向B的其余部分,右(1)分支通向A的其余部分。你得到了图片。如果这部分不清楚,请告诉我。

答案 3 :(得分:1)

为什么不将图像视为25位整数? 32位int可能有效。例如,字母'I'可以被视为十进制的整数14815374,其二进制表达式为0111000100001000010001110。您可以方便地将两个图像与操作'=='作为两个整数进行比较。

答案 4 :(得分:1)

一种方法是识别大约一半字母中的黑色和另一组中的白色的像素。然后可以使用它将字母分成两组,递归地在两半上使用相同的算法,直到你到达单个字符。

如果你找不到将这些组分成两个的单个像素,你可能需要选择一组两个或更多像素,但希望使用单个像素应该足够好。

要查找像素,请从整数数组开始,大小与字母大小相同,将所有元素初始化为0,然后如果字母中的相应像素(例如)为黑色则递增元素。你感兴趣的是那些(大约)10≤sum≤16范围内的那些(对于顶级,较低级别需要使用其他界限)。

答案 5 :(得分:0)

好吧,我找到了解决方案。

您只需对每个像素组合使用每个像素的深度优先搜索,直到找到该字母的唯一特征集。在执行深度优先搜索时,请确保每次都不要从x = 0和y = 0开始,因为您只想处理每个组合一次,所以最终要做的是增加每个组合中的x和y值迭代。

我创建了一个包含这些属性的辅助对象:

public Point LastPoint { get; set; }
public List<OcrChar> CharsWithSimilarProperties { get; set; }
public List<Point> BlackPixels { get; set; }
public List<Point> WhitePixels { get; set; }

对于每次迭代,如果我找不到一个独特的特征(例如,所有其他字母都将此像素设为黑色,但此字母将其设为白色......或反向),我将所有后续像素添加到队列中正在处理,通过正确设置属性创建上述对象的实例。

一些伪代码:

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)

要确定“node.CharsWithSimilarProperites.IsAlwaysWhite()”或“IsAlwaysBlack()”,您可以在队列的每次迭代中生成compositeMap:

  for each pixel after node.LastPoint
    for each char in node.CharsWithSimilarProperties
      if char.IsBlackPixel(pixel)
        compositeMap[pixel].Add(char)

在完成所有这些操作之前,我还处理了整个字母表,以查找始终为白色或始终为黑色的像素,因为这些像素永远不会被使用。我将它们添加到List<Point> ignoredPixels,每次迭代像素时,我总是使用if (ignoredPixels[x, y]) continue;

这非常有效并且非常快。虽然请记住,我的解决方案的这一部分根本不需要快速,因为它是一次性优化,这有助于我以后。在我的每个“字母”集最多8个字符的测试用例中,它通常为每个字符产生一个或两个特征。我还没有完整的26个字符。

答案 6 :(得分:0)