Python使用OpenCV处理Sudoku问题

 

原文链接: http://codewenda.com/python%E4%BD%BF%E7%94%A8opencv%E5%A4%84%E7%90%86sudoku%E9%97%AE%E9%A2%98/

 

我正在做一个有趣的项目:使用OpenCV从输入图像中解决Sudoku(如在Google goggles等)。我已经完成了任务,但最后我发现我来到这里的一个小问题。
我使用OpenCV 2.3.1的Python API进行编程。
下面是我做的:

  1. 阅读图片
  2. 找到轮廓
  3. 选择最大面积的面积(也有点相当于平方)。
  4. 找到角点
    例如如下:
    enter image description here
    请注意,绿线正确地与Sudoku的真正边界一致,所以Sudoku可以正确扭曲。检查下一张图)
  5. 将图像变形为完美的正方形
    例如图像:
    enter image description here
  6. 执行OCR(我使用我在Simple Digit Recognition OCR in OpenCV-Python中给出的方法)

而且这个方法运作良好。
问题:
退房this image.
执行此图像上的步骤4给出以下结果:
enter image description here
绘制的红线是原始轮廓,是数独边界的真实轮廓。
绘制的绿线是近似的轮廓,这将是扭曲图像的轮廓。
当然,在数独顶端的绿线与红线之间有区别。所以在弯曲的时候,我没有得到数独的原始边界。
我的问题 :
如何将图像扭曲在数独的正确边界上,即红线或如何消除红线与绿线之间的差异?在OpenCV中有没有这样的方法?

回答:

我有一个解决方案,但您必须自己将其转换为OpenCV。它写在数学中。
第一步是通过将每个像素除以关闭操作的结果来调整图像中的亮度:

1
2
3
src = ColorConvert[Import["http://davemark.com/images/sudoku.jpg"], "Grayscale"];
white = Closing[src, DiskMatrix[5]];
srcAdjusted = Image[ImageData[src]/ImageData[white]]

enter image description here
下一步是找到数独区域,所以我可以忽略(掩蔽)背景。为此,我使用连接分量分析,并选择具有最大凸面积的分量:

1
2
3
4
5
components =
  ComponentMeasurements[
    ColorNegate@Binarize[srcAdjusted], {"ConvexArea", "Mask"}][[All,
    2]];
largestComponent = Image[SortBy[components, First][[-1, 2]]]

enter image description here
通过填写这个图像,我得到了数独网格的面具:

1
mask = FillingTransform[largestComponent]

enter image description here
现在,我可以使用二阶导数滤波器在两个不同的图像中找到垂直和水平线:

1
2
lY = ImageMultiply[MorphologicalBinarize[GaussianFilter[srcAdjusted, 3, {2, 0}], {0.02, 0.05}], mask];
lX = ImageMultiply[MorphologicalBinarize[GaussianFilter[srcAdjusted, 3, {0, 2}], {0.02, 0.05}], mask];

enter image description here
我再次使用连接分量分析从这些图像中提取网格线。网格线比数字长得多,所以我可以使用卡尺长度来选择仅网格线连接的组件。通过位置排序,我获得图像中每个垂直/水平网格线的2×10个掩模图像:

1
2
3
4
5
6
7
8
verticalGridLineMasks =
  SortBy[ComponentMeasurements[
      lX, {"CaliperLength", "Centroid", "Mask"}, # > 100 &][[All,
      2]], #[[2, 1]] &][[All, 3]];
horizontalGridLineMasks =
  SortBy[ComponentMeasurements[
      lY, {"CaliperLength", "Centroid", "Mask"}, # > 100 &][[All,
      2]], #[[2, 2]] &][[All, 3]];

enter image description here
接下来,我采取每对垂直/水平网格线,扩大它们,计算逐像素交点,并计算结果的中心。这些点是网格线交点:

1
2
3
4
5
6
7
centerOfGravity[l_] :=
 ComponentMeasurements[Image[l], "Centroid"][[1, 2]]
gridCenters =
  Table[centerOfGravity[
    ImageData[Dilation[Image[h], DiskMatrix[2]]]*
     ImageData[Dilation[Image[v], DiskMatrix[2]]]], {h,
    horizontalGridLineMasks}, {v, verticalGridLineMasks}];

enter image description here
最后一步是通过这些点定义X / Y映射的两个插值函数,并使用这些功能转换图像:

1
2
3
4
5
6
fnX = ListInterpolation[gridCenters[[All, All, 1]]];
fnY = ListInterpolation[gridCenters[[All, All, 2]]];
transformed =
 ImageTransformation[
  srcAdjusted, {fnX @@ Reverse[#], fnY @@ Reverse[#]} &, {9*50, 9*50},
   PlotRange -> {{1, 10}, {1, 10}}, DataRange -> Full]

enter image description here
所有的操作都是基本的图像处理功能,所以在OpenCV中也是可以的。基于样条曲线的图像变换可能更难,但我不认为你真的需要它。可能使用现在在每个单独的单元格上使用的透视变换将给出足够好的结果。

Code问答: http://codewenda.com/topics/python/
Stackoverflow: How to remove convexity defects in a Sudoku square?

*转载请注明本文链接以及stackoverflow的英文链接

posted @ 2017-10-28 13:52  codewenda  阅读(518)  评论(0编辑  收藏  举报