我正在做一个有趣的项目:使用OpenCV从输入图像中解决数独(如Google护目镜等) . 我完成了任务,但最后我发现了一个问题,我来到这里 .
我使用OpenCV 2.3.1的Python API进行编程 .
以下是我的所作所为:
-
阅读图像
-
找到轮廓
-
选择具有最大面积的那个(也有点等同于方形) .
-
找到角点 .
例如给出如下:
( Notice here that the green line correctly coincides with the true boundary of the Sudoku, so the Sudoku can be correctly warped . 查看下一张图片)
- 将图像变形为完美的正方形
例如:
- 执行OCR(我使用了Simple Digit Recognition OCR in OpenCV-Python中给出的方法)
而且方法效果很好 .
Problem:
在此图像上执行第4步,结果如下:
绘制的红线是原始轮廓,它是数独边界的真实轮廓 .
绘制的绿线是近似轮廓,它将是扭曲图像的轮廓 .
当然,在数独的上边缘绿线和红线之间存在差异 . 因此,在翘曲时,我没有得到数独的原始边界 .
My Question :
如何在数独的正确边界上扭曲图像,即红线,或者如何消除红线和绿线之间的差异?在OpenCV中有没有这方法?
5 回答
我有一个有效的解决方案,但您必须自己将其转换为OpenCV . 它是用Mathematica编写的 .
第一步是通过将每个像素除以关闭操作的结果来调整图像中的亮度:
下一步是找到数独区域,这样我就可以忽略(掩盖掉)背景 . 为此,我使用连通分量分析,并选择具有最大凸区域的组件:
通过填充此图像,我获得了数独网格的掩码:
现在,我可以使用二阶导数滤波器在两个单独的图像中找到垂直和水平线:
我再次使用连通分量分析从这些图像中提取网格线 . 网格线比数字长得多,因此我可以使用卡尺长度来仅选择网格线连接的组件 . 按位置对它们进行排序,我为图像中的每个垂直/水平网格线获得2x10个蒙版图像:
接下来,我取每对垂直/水平网格线,扩大它们,计算逐个像素的交点,并计算结果的中心 . 这些点是网格线交叉点:
最后一步是为这些点定义X / Y映射的两个插值函数,并使用以下函数转换图像:
所有操作都是基本的图像处理功能,所以这在OpenCV中也是可行的 . 基于样条的图像转换可能更难,但我认为你并不需要它 . 可能使用现在在每个单独单元格上使用的透视变换将提供足够好的结果 .
Nikie的答案解决了我的问题,但他的回答是在Mathematica . 所以我认为我应该在这里进行OpenCV改编 . 但是在实现之后我可以看到OpenCV代码比nikie的mathematica代码要大得多 . 而且,我无法在OpenCV中找到由nikie完成的插值方法(尽管可以使用scipy完成,我会在时机成熟时告诉它 . )
1. Image PreProcessing ( closing operation )
结果:
2. Finding Sudoku Square and Creating Mask Image
结果:
3. Finding Vertical lines
结果:
4. Finding Horizontal Lines
结果:
当然,这个并不是那么好 .
5. Finding Grid Points
结果:
6. Correcting the defects
在这里,nikie进行了某种插值,我对此并不了解 . 我找不到这个OpenCV的任何相应功能 . (可能就在那里,我不知道) .
查看此SOF,它解释了如何使用SciPy执行此操作,我不想使用它:Image transformation in OpenCV
所以,在这里我采用了每个子方块的4个角,并对每个子角应用了warp Perspective .
为此,首先我们找到质心 .
但是生成的质心将不会被排序 . 查看下面的图像以查看他们的订单:
所以我们从左到右,从上到下对它们进行排序 .
现在看下面他们的订单:
最后,我们应用转换并创建一个大小的新图像450x450 .
结果:
结果几乎与nikie相同,但代码长度很大 . 可能是,那里有更好的方法,但在那之前,这个工作正常 .
关心ARK .
您可以尝试使用某种基于网格的任意变形建模 . 由于数独已经是一个网格,这应该不会太难 .
因此,您可以尝试检测每个3x3子区域的边界,然后单独扭曲每个区域 . 如果检测成功,它会给你一个更好的近似值 .
我想补充一点,上述方法仅在数独板直立时才有效,否则高度/宽度(反之亦然)比率测试很可能会失败,您将无法检测到数独的边缘 . (我还想补充一点,如果不垂直于图像边界的线,sobel操作(dx和dy)仍然可以工作,因为线仍然会有相对于两个轴的边 . )
为了能够检测直线,你应该在轮廓或像素分析上工作,例如contourArea / boundingRectArea,左上角和右下角......
编辑:我设法通过应用线性回归和检查错误来检查一组轮廓是否形成一条线 . 然而,当线的斜率太大(即> 1000)或非常接近0时,线性回归表现不佳 . 因此,在线性回归之前应用上述比率测试(在大多数上调的答案中)是合乎逻辑的并且对我有用 .
为了去除未定角,我应用了伽马值为0.8的伽马校正 .
绘制红色圆圈以显示缺失的角落 .
代码是:
如果缺少一些角点,这是Abid Rahman的回答 .