代码之家  ›  专栏  ›  技术社区  ›  user2790209

如何使用光学字符识别解析数字4

  •  3
  • user2790209  · 技术社区  · 12 年前

    我正在研究一种OCR算法,它会给出一张包含一些数字的图像。我希望它能简单地检测每个图像,并将其与其他图像分离。

    它适用于0-9之间的所有数字,除了数字4,这给我带来了很多麻烦。

    这是我的图片来源:

    source image

    以下是对其进行解析的一些数字的结果:

    0 1 2 2 5 6 7 8 9 .

    正如你所看到的,它们都被完美地解析了。唯一给我带来麻烦的是4。以下是数字4的外观:

    4

    我遇到的问题是检测4的最左角,以便包括整个数字。 以下是当我的算法试图检测每个字符的左边界时,它是如何向下遍历的(蓝点表示算法所走的路径):

    path taken by algorithm

    如果你在另一个选项卡中打开图像并放大,你可能会更好地看到它在做什么。

    正如你所看到的,它向下向左移动,直到遇到背景两次。当它出现时,就意味着图像的最左边边界已经到达。它对所有其他图像都很好,除了4,你可以看到它遇到背景两次并停止,但如果它继续向下移动两个像素,那么它会遇到更多的4,并找到它真正的最左边。

    我不知道如何做到这一点,这样就不会破坏其他数字。以下是我的实际代码,以防有帮助:

        int misses = 0;
        int maxMisses = 2;
        while (y < image.getHeight() && x >= 0 )
        {
            markPixel(x, y);
            color = image.getRGB(x, y);
            if (! reader.isForeground(color))
                misses++;
    
            if (misses < maxMisses)
            {
                y++;
                x--;
                continue;
            }
            x++;
            break;
        }        
        if (x < 0)
            x = 0;
        return x;
    

    编辑: 我已经能够实现一些改进,只需沿着图像向下遍历,而不是在遇到2个背景像素时停止,在遇到前景像素时存储每一步的x坐标,然后按升序对匹配进行排序,并返回最低结果。它的效果要好一些。新4图像:

    new 4

    它还不完美。此外,9看起来有点小:

    new 9

    算法遍历的新路径:

    new path

    更新代码:

        ArrayList<Integer> matches = new ArrayList<>();
        int yB = y;
        for (int i = 1; i <= 2; i++)
        {
            y = yB;
            while (y < image.getHeight() && x >= 0 )
            {
                markPixel(x, y);
                color = image.getRGB(x, y);
                if ( reader.isForeground(color))
                    matches.add(x);
    
                y++;                
            }      
            x--;
        }
    
        Collections.sort(matches);
        return matches.get(0);
    

    有人有什么想法可以弥补4个缺陷中的最后一个吗?

    3 回复  |  直到 12 年前
        1
  •  1
  •   Geobits    12 年前

    根据我的评论改编,这似乎是一种从你发布的算法中进行最小修改的方法:

    在检查下一个像素时,不要同时向下和向左移动,而是单独检查它们。只有左边(?)和下面的像素都未命中,这才是未命中。这将有助于45度以上的任何角度,如果您更改字体等,可能会遇到这种情况。


    然而

    如果你愿意改变你的算法,我认为其他答案最终可能会更稳健。

    • 从杜兰达尔的回答来看: 绕着数字转一圈,追踪最小x/y和最大x/y。要到达左下角 min x, max y ,假设原点在左上角。我看到你在这里遇到的唯一问题是岛屿( i , j )或者极为斜体的字体,这些字符可能在x方向上重叠。

    • 来自用户2399923的评论: 找到空列来划分字符也很有效。它不会受到岛屿的影响,但会受到上面提到的极端斜体字的影响,因为可能没有 在这种情况下为空列。

    • 根据blgt的回答: 洪水填充法也很好,我认为这是标准的。它需要适应岛屿,但不会受到斜体字的影响,除非字符真的很感人。数字上是否有“洞”并不重要(例如 8 )因为您只对淹没区域的最小/最大x/y值感兴趣。根据定义,这些点都不是一个洞。

        2
  •  1
  •   blgt    12 年前

    做你描述的事情最简单的方法是洪水填充图像。适用于分离任何不相交的字符(即,对于“i”、“j”等,不能开箱即用,但适应它并不难)

    你所描述的听起来更复杂。当已经有解决方案的时候,你不需要重新发明轮子。

    链接-> http://en.wikipedia.org/wiki/Flood_fill

        3
  •  1
  •   Durandal    12 年前

    为什么不沿着图像轮廓顺时针走,直到你完全回到(或足够靠近)你遇到第一个黑点的位置?

    当你遍历路径时,一个简单的最小/最大跟踪变量对会自然地给数字图像一个矩形。此外,只要字符不由多个岛组成(想想字母i),并且彼此不重叠/连接,这将独立于所使用的字体工作。

    编辑:TC提到洪水填充字形。我认为这是一个很好的主意,甚至比我上面的方法还要好。

    只需找到第一个黑色像素,然后以该像素为起点运行泛洪填充(如果实现为 复制 像素到一个单独的图像中,它选择每个像素来填充它,甚至可以确定字体紧排是否使相邻的字形具有重叠的边界矩形(这可能需要两次才能实现,第一次是找到边界矩形,第二次是实际复制像素)。