什么是快速可靠的阈值图像,可能会出现模糊和不均匀的亮度?
示例(模糊但亮度均匀):
由于无法保证图像的亮度均匀,因此使用固定阈值是不可行的。自适应阈值可以正常工作,但由于模糊,它会在特征中产生断裂和扭曲(这里,重要的特征是数独数字):
我也尝试过使用直方图均衡(使用OpenCV的equalizeHist
函数)。它可以在不降低亮度差异的情况下提高对比度。
我发现的最佳解决方案是将图像按形态结束(信用this post)除以使亮度均匀,然后重新归一化,然后使用固定阈值(使用Otsu算法选择最佳值)门槛水平):
这是OpenCV for Android中的代码:
Mat kernel = Imgproc.getStructuringElement(Imgproc.MORPH_ELLIPSE, new Size(19,19));
Mat closed = new Mat(); // closed will have type CV_32F
Imgproc.morphologyEx(image, closed, Imgproc.MORPH_CLOSE, kernel);
Core.divide(image, closed, closed, 1, CvType.CV_32F);
Core.normalize(closed, image, 0, 255, Core.NORM_MINMAX, CvType.CV_8U);
Imgproc.threshold(image, image, -1, 255, Imgproc.THRESH_BINARY_INV
+Imgproc.THRESH_OTSU);
这很有效,但关闭操作非常慢。减小结构元素的尺寸会增加速度但会降低精度。
编辑:基于DCS的建议,我尝试使用高通滤波器。我选择了拉普拉斯滤波器,但我希望Sobel和Scharr滤波器具有相似的结果。滤波器在不包含特征的区域中拾取高频噪声,并且由于模糊而遭受与自适应阈值类似的失真。它也需要与关闭操作一样长。以下是15x15过滤器的示例:
编辑2:根据AruniRC的回答,我使用建议的参数在图像上使用了Canny边缘检测:
double mean = Core.mean(image).val[0];
Imgproc.Canny(image, image, 0.66*mean, 1.33*mean);
我不确定如何可靠地自动微调参数以获得连接的数字。
答案 0 :(得分:20)
使用Vaughn Cato和Theraot的建议,我在关闭之前缩小图像,然后将关闭的图像缩放到常规尺寸。我也按比例缩小了内核大小。
Mat kernel = Imgproc.getStructuringElement(Imgproc.MORPH_ELLIPSE, new Size(5,5));
Mat temp = new Mat();
Imgproc.resize(image, temp, new Size(image.cols()/4, image.rows()/4));
Imgproc.morphologyEx(temp, temp, Imgproc.MORPH_CLOSE, kernel);
Imgproc.resize(temp, temp, new Size(image.cols(), image.rows()));
Core.divide(image, temp, temp, 1, CvType.CV_32F); // temp will now have type CV_32F
Core.normalize(temp, image, 0, 255, Core.NORM_MINMAX, CvType.CV_8U);
Imgproc.threshold(image, image, -1, 255,
Imgproc.THRESH_BINARY_INV+Imgproc.THRESH_OTSU);
下图显示了3种不同方法的并排结果:
左 - 常规尺寸关闭(432像素),尺寸19内核
中 - 半尺寸关闭(216像素),尺寸9内核
正确 - 四分之一大小关闭(108像素),大小为5内核
随着用于关闭的图像的尺寸变小,图像质量劣化,但是劣化不足以影响特征识别算法。即使调整大小,四分之一大小的关闭速度也会略微增加16倍以上,这表明关闭时间大致与图像中的像素数成正比。
关于如何进一步改进这一想法的任何建议(通过进一步降低速度或降低图像质量的劣化)都是非常受欢迎的。
答案 1 :(得分:2)
替代方法:
假设您的意图是将数字清楚地二进制化......将焦点转移到组件而不是整个图像。
这是一个非常简单的方法:
将每个Canny边缘视为连通组件(即使用cvFindContours()或其C ++对应物,无论哪个)都可以估计前景和背景灰度并达到阈值。
最后一点,请看一下this paper的第2节和第3节。跳过大多数非必要的理论部分,在OpenCV中实现它不应该太困难。
希望这有帮助!
修改1:
基于Canny边缘阈值,这里有一个非常粗略的想法,足以微调这些值。 high_threshold
控制边缘在检测到之前必须有多强。基本上,边缘必须具有大于high_threshold
的梯度幅度才能首先被检测到。所以这会对边缘进行初步检测。
现在,low_threshold
处理连接附近的边缘。它控制附近的断开边缘将多少组合成一条边。为了更好的主意,请阅读this webpage的“第6步”。尝试设置一个非常小的low_threshold,看看事情是如何发生的。你可以丢弃0.66 * [平均值]的东西,如果它不适用于这些图像 - 无论如何它只是一个经验法则。
答案 2 :(得分:2)
我们使用Bradleys算法来解决非常相似的问题(从背景中分割字母,光线不均匀和背景颜色不均匀),如下所述:http://people.scs.carleton.ca:8008/~roth/iit-publications-iti/docs/gerh-50002.pdf,C#代码在这里:http://code.google.com/p/aforge/source/browse/trunk/Sources/Imaging/Filters/Adaptive+Binarization/BradleyLocalThresholding.cs?r=1360。它适用于整合图像,可以使用OpenCV的integral
函数计算。它非常可靠和快速,但本身并未在OpenCV中实现,但易于移植。
另一个选项是openCV中的adaptiveThreshold方法,但我们没有试一试:http://docs.opencv.org/modules/imgproc/doc/miscellaneous_transformations.html#adaptivethreshold。 MEAN版本与bradleys相同,只是它使用常量来修改平均值而不是百分比,我认为这更好。
另外,好文章在这里:https://dsp.stackexchange.com/a/2504
答案 3 :(得分:0)
如果您知道自己有很好的网格效果,可以尝试按照每个图块进行操作。处理9个子图像而不是整个图像将最有可能在每个子图像上产生更均匀的亮度。如果您的裁剪完美,您甚至可以尝试单独使用每个数字单元格;但这一切都取决于你的作物有多可靠。
答案 4 :(得分:0)
如果与扁平形状相比,椭圆形状的计算很复杂。 尝试改变:
Mat kernel = Imgproc.getStructuringElement(Imgproc.MORPH_ELLIPSE, new Size(19,19));
为:
Mat kernel = Imgproc.getStructuringElement(Imgproc.MORPH_RECT, new Size(19,19));
可以加快您的解决方案,对准确性的影响很小。