我正在Android平台上创建一个Sudoku求解应用程序,我在处理图像时遇到了一个问题。我正在尝试使用Sobel过滤器使用OpenCV找到拼图的水平线,然后使用Otsu算法进行阈值处理:
Mat kernaly = Imgproc.getStructuringElement(Imgproc.MORPH_RECT, new Size(10,2));
Mat dy = new Mat();
Mat close = new Mat();
Imgproc.Sobel(img, dy, CvType.CV_16S, 0, 2);
Core.convertScaleAbs(dy, dy);
Core.normalize(dy,dy,0,255,Core.NORM_MINMAX);
Imgproc.threshold(dy, close, 0, 255, Imgproc.THRESH_BINARY|Imgproc.THRESH_OTSU);
Imgproc.morphologyEx(close, close, Imgproc.MORPH_DILATE, kernaly);
此方法实际上适用于大多数图像,例如:
但是,它无法显示以下图像:
有人可以解释为什么结果如此不同而上面的第二张图片只返回一行吗? 另外,我应该使用其他方法,例如Canny或Hough线吗?
提前致谢!
编辑: 使用marol的建议,我尝试尽可能多地删除黑色边框而不必扭曲图像。这是将上述相同过程应用于这些返工图像时的结果。
图片1:
图片2:
如您所见,结果更好,因为已检测到大多数线条。但是,它仍然不够好。可以通过添加固定阈值来改进它,但每个图像必须不同。
我可能只是使用一种新方法,因为这种方法看起来不够健壮。任何提示将不胜感激。
答案 0 :(得分:1)
问题可能是由于强度分布造成的。如果你看一下sobel算子后的直方图:
将其与成像otsu检测的图像直方图进行比较:
你可以很容易地在第一个直方图中看到失败,因为计算出的阈值向右移动而不是向左移动(即使左边有一个主峰,所有黑色像素都突出)。在第二种情况下,分配不是分为峰值和平坦休息,而是我们有一种情况,即有更多的白色像素,并且#34;计算出右边的阈值。
换句话说,你必须摆脱黑色像素的统治。换句话说,尝试缩放数独,以使黑色像素边框尽可能小。这将使分发更像第二种情况。
从那些直方图中你可以说恕我直言的方法是非常敏感的,因为"黑"和"白色"图像中的部分因此计算出的阈值水平 对图像非常敏感。我不会依赖这种方法。一些固定的门槛水平怎么样?在一般情况下听起来可能不太好,但在这里它可能更具确定性并且仍然正确。答案 1 :(得分:1)
另一个建议,而不是otsu:
您可以在索贝尔图像中搜索Y方向的局部最大值。
cmp(dy,dilatedImg,comparisonImg,CMP_GE
)threshold(dy, mask, 1, 255, THRESH_BINARY); And(comparisonImg, mask, comparisonImg);
)现在,您拥有与本地区域中最强水平边缘对应的所有像素。
旁注,你对索贝尔的使用有点奇怪:
您使用Imgproc.Sobel(img, dy, CvType.CV_16S, 0, 2);
获取第二个 sobel派生词
我假设你这样做是因为你想要识别黑线的中心,而不是它们的边界。但在这种情况下,下一步,convertScaleAbs,似乎违反直觉。这样你就可以得到第二个索贝尔衍生物的最小值和最大值。最大值应该对应于黑线的中心,但最小值引入了边缘图像中可见的奇怪的“三线”伪影。如果您使用第一个sobel衍生物,Abs步骤会更合理,但在您的情况下,丢弃负值可能会更好。
答案 2 :(得分:0)
我找到了一个快速修复,通过在运行上面的代码之前进行一些图像处理来显着改善结果:
它不是最强大的解决方案,但它适用于我的所有10个测试图像样本。