OpenCV - 移除数独谜题上的网格线

3
我正在编写一个Android应用程序,从图片中提取数独谜题。对于9x9的数独网格中的每个单元格,我需要确定它是否包含数字1到9或为空。以下是我的算法的概要:
- 自适应阈值化拼图 - 膨胀以减少要考虑的轮廓数量 - 查找拼图的轮廓并将其扭曲成正方形 - 将正方形分成81个相等的单元格;查找至少有20%白色像素的单元格 - 找到最靠近这些单元格中心的白色斑点,并获取其边界矩形 - 在边框矩形内部的图像部分上使用字符识别(k最近邻/Tesseract等)
尽管我可以使用简单的洪水填充方法去除数独拼图的粗外边框,但即使进行膨胀处理后,内部网格线也不连续,不能轻易地去除。以下是去除外部网格线后的样例数独图片:
有时候,单元格中的网格线数量足够多,使得其中超过20%的像素为白色,因此我会误检测这个单元格为其中有一个数字。这里是这样一个单元格的示例:
我考虑过对图像进行变形以减少内部网格线的可见性。我可以使用Hough变换或这篇文章中描述的方法来找到网格线,作为变形的前奏。但是,我没有看到任何其他重要的变形好处,而且完全去除网格线应该更安全和更容易。
或者,我可以修改我的预处理,使内部网格线保持不变。目前我的预处理为:
    Imgproc.GaussianBlur(mat, mat, new Size(11,11), 0);
    Imgproc.adaptiveThreshold(mat, matBW, 255, 
        Imgproc.ADAPTIVE_THRESH_MEAN_C, Imgproc.THRESH_BINARY_INV, 5, 2);
    Mat kernel = Imgproc.getStructuringElement(Imgproc.MORPH_CROSS, new Size(3, 3));
    Imgproc.dilate(matBW, matBW, kernel);

高斯模糊在阈值处理之前是必要的,以减少噪点。膨胀操作可以确保外部网格线连接在一起,但不足以重新连接内部线条。
如何稳定地去除内部网格线,而不影响图像的其他部分?
非常感谢。

我可能忽略了一些显而易见的东西,但是您为什么需要删除网格线呢?如果您已经将识别出来的数字排列在网格中,难道您不能重新合成图像吗? - loopbackbee
我的目的是找出数独格子中每个单元格中的数字。为此,我需要识别哪些单元格为空。如果网格线仍然存在,则某些单元格将看起来有东西,因为超过20%的像素可能是白色的。 - 1''
@Hammer,我在我的问题中提到了那篇帖子 :) SO 应该让它们的链接变得更深。 - 1''
1个回答

2
如果您不想进行正确的变形,则可能值得使用较小的单元格进行digit_present测试。
假设左上角的单元格坐标为(x1,y1,x2,y1) =(0,0,10,10)。您可以将一个新单元格定义为(x1 + k,y1 + k,x2-k,y2-k),其中k = min(x2-x1,y2-y1)/ 4.
另一种可能起作用的策略是在测试之前使用erode,这取决于网格线与数字之间的粗细。
最后,您可以将所有单元格提供给分类器,并使用其置信度索引(如果有的话)。如果分类器的置信度太低,则可能不是数字。
如果所有这些方法都失败了,并且为了执行填充操作,您仍然必须获取网格线的变形,那么最好还是进行解扭。

这些都是非常好的建议。不幸的是,它们都有缺点。腐蚀会将一些白色像素低于20%的单元格推出。使用较小的单元格会切断一些数字,因为它们由于扭曲而靠近单元格的边缘。最好的解决方案可能是先进行去扭曲处理,然后使用略小的单元格(也许是每个侧面的1/10而不是1/4)。 - 1''
另外,您可能想看一下我对OP的编辑。我在想是否可以修改我的预处理程序,从一开始就保持内部线条连接。 - 1''
1
@1'' 截取一些数字不应该是问题,只要数字的某个部分仍然在单元格内 - 您不需要为 digit_present 测试和分类使用相同的单元格。关于您的预处理,我建议尝试更改阈值(2),可能还要更改块大小。 - loopbackbee
实际上,似乎5和2是最佳值。在广泛的数值范围内,很难使细网格线旁边的粗网格线部分变白,这可能是因为外部网格线的所有黑色像素使阈值降低太多。 - 1''
使用不同的单元格进行两个测试是一个很好的点子!这可能是最好的解决方案。 - 1''
@1'' 太好了!我希望你在分类方面做得很好 :) - loopbackbee

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接