30

什么是对可能模糊和不均匀亮度的图像进行阈值处理的快速可靠的方法?

示例(模糊但亮度均匀):

在此处输入图像描述

由于不能保证图像具有均匀的亮度,因此使用固定阈值是不可行的。自适应阈值可以正常工作,但由于模糊性,它会在特征中产生中断和扭曲(这里,重要的特征是数独数字):

在此处输入图像描述

我也尝试过使用直方图均衡(使用 OpenCV 的equalizeHist函数)。它在不减少亮度差异的情况下增加对比度。

我发现的最佳解决方案是将图像除以其形态闭合(归功于此帖子)以使亮度均匀,然后重新归一化,然后使用固定阈值(使用 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 的建议,我尝试使用高通滤波器。我选择了 Laplacian 滤波器,但我希望使用 Sobel 和 Scharr 滤波器得到类似的结果。该滤波器在不包含特征的区域中拾取高频噪声,并且由于模糊而遭受与自适应阈值相似的失真。它也需要大约与关闭操作一样长的时间。这是一个使用 15x15 过滤器的示例:

在此处输入图像描述

编辑 2:根据 AruniRC 的回答,我使用建议参数对图像使用 Canny 边缘检测:

double mean = Core.mean(image).val[0];
Imgproc.Canny(image, image, 0.66*mean, 1.33*mean);

我不确定如何可靠地自动微调参数以获得连接的数字。

在此处输入图像描述

4

5 回答 5

22

使用 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 内核

Middle - 半尺寸闭合(216 像素),尺寸 9 内核

右 - 四分之一尺寸闭合(108 像素),尺寸 5 内核

在此处输入图像描述

图像质量随着用于闭合的图像尺寸变小而恶化,但恶化程度不足以影响特征识别算法。即使调整大小,四分之一尺寸关闭的速度也增加了 16 倍多一点,这表明关闭时间大致与图像中的像素数成正比。

任何关于如何进一步改进这个想法的建议(通过进一步降低速度或减少图像质量的恶化)都非常受欢迎。

于 2013-03-24T23:02:30.510 回答
2

替代方法:

假设您的意图是让数字清晰地二值化......将您的注意力转移到组件而不是整个图像上。

这是一个非常简单的方法:

  1. 在图像上做一个 Canny 边缘图。首先在低阈值到 0.66*[平均值] 和高阈值到 1.33*[平均值] 的范围内尝试使用 Canny 函数的参数。(表示灰度值的平均值)。
  2. 您需要稍微调整一下参数才能获得主要组件/数字作为单独组件清晰可见的图像。在这个阶段,接近完美就足够了。
  3. 将每个 Canny 边缘视为一个连接组件(即使用 cvFindContours() 或其 C++ 对应项,无论哪个),可以估计前景和背景灰度级并达到阈值。

    最后一点,请查看本文的第 2. 和 3. 部分。跳过大部分非必要的理论部分,在 OpenCV 中实现它应该不会太难。

    希望这有帮助!

编辑1:

基于 Canny 边缘阈值,这是一个非常粗略的想法,足以微调这些值。控制在high_threshold检测到边缘之前必须有多强。基本上,边缘必须具有大于high_threshold首先被检测到的梯度幅度。所以这是对边缘的初始检测。

现在,low_threshold处理连接附近的边缘。它控制有多少附近断开的边将组合在一起成为一条边。如需更好的主意,请阅读此网页的“第 6 步” 。尝试设置一个非常小的低阈值,看看事情是如何发生的。如果 0.66*[mean value] 不适用于这些图像,您可以丢弃它 - 无论如何它只是一个经验法则。

于 2013-03-24T15:33:42.130 回答
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 。它适用于积分图像,可以使用integralOpenCV的函数计算。它非常可靠和快速,但它本身并没有在 OpenCV 中实现,但很容易移植。

另一种选择是openCV中的adaptiveThreshold方法,但我们没有尝试:http ://docs.opencv.org/modules/imgproc/doc/miscellaneous_transformations.html#adaptivethreshold 。MEAN 版本与 bradleys 相同,只是它使用常数来修改平均值而不是百分比,我认为这更好。

另外,好文章在这里:https ://dsp.stackexchange.com/a/2504

于 2013-10-22T16:00:48.100 回答
0

如果你知道你有很好的网格,你可以尝试在每个图块的基础上工作。处理 9 个子图像而不是整个图片很可能会导致每个子图像的亮度更加均匀。如果您的裁剪完美,您甚至可以尝试单独处理每个数字单元;但这一切都取决于你的作物有多可靠。

于 2013-03-30T11:13:41.287 回答
0

如果与平面形状相比,椭圆形状的计算复杂。尝试改变:

Mat kernel = Imgproc.getStructuringElement(Imgproc.MORPH_ELLIPSE, new Size(19,19));

到:

Mat kernel = Imgproc.getStructuringElement(Imgproc.MORPH_RECT, new Size(19,19));

可以加快您足够的解决方案,而对准确性的影响很小。

于 2016-03-02T23:07:42.010 回答