快速图像阈值处理
Posted
技术标签:
【中文标题】快速图像阈值处理【英文标题】:Fast image thresholding 【发布时间】:2013-03-11 19:25:09 【问题描述】:有什么方法可以快速可靠地对可能出现模糊和不均匀亮度的图像进行阈值处理?
示例(模糊但亮度均匀):
由于不能保证图像具有均匀的亮度,因此使用固定阈值是不可行的。自适应阈值可以正常工作,但由于模糊性,它会在特征中造成中断和扭曲(这里,重要的特征是数独数字):
我也尝试过使用直方图均衡(使用 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 的建议,我尝试使用高通滤波器。我选择了 Laplacian 滤波器,但我希望使用 Sobel 和 Scharr 滤波器得到类似的结果。该滤波器在不包含特征的区域中拾取高频噪声,并且由于模糊而遭受与自适应阈值相似的失真。它也需要大约与关闭操作一样长的时间。这是一个使用 15x15 过滤器的示例:
编辑 2:根据 AruniRC 的回答,我使用建议参数对图像使用 Canny 边缘检测:
double mean = Core.mean(image).val[0];
Imgproc.Canny(image, image, 0.66*mean, 1.33*mean);
我不确定如何可靠地自动微调参数以获得连接的数字。
【问题讨论】:
您可以尝试对高通滤波图像设置阈值,假设亮度变化发生在低频。但是,我不知道这些过滤器操作在移动设备上的速度有多快,而且我认为您需要一个相当大的内核。 @DCS 不幸的是,我认为高通滤波器不会起作用。请参阅我对上述帖子的编辑。 既然您感兴趣的特征覆盖了几个像素,那么先将图像降低到较低的分辨率如何?然后,您可以返回并以原始分辨率获取更多细节,使用您的低分辨率版本作为遮罩。 降低分辨率,用小图确定大图对应区域的亮度如何归一化,归一化后进行滤波。与使用大图像进行归一化相比,它应该有更多的噪音,但它会更快。希望有正确的阈值就足够了。这只是一个想法。 这个问题不就是做一个谷歌搜索和对一些技术进行基准测试的问题吗? 【参考方案1】:根据 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 倍多一点,这表明关闭时间大致与图像中的像素数成正比。
非常欢迎任何有关如何进一步改进此想法(通过进一步降低速度或减少图像质量恶化)的建议。
【讨论】:
您应该使用自适应阈值而不是阈值。在暗图像的情况下,自适应阈值将提供更好的结果。 @AnkitRox 我在问题中讨论了自适应阈值。 @1'':我想知道,可以在实时相机框架上完成吗?我想从实时相机帧中识别角色?欢迎所有建议。【参考方案2】:替代方法:
假设您的意图是让数字清晰地二值化......将您的注意力转移到组件而不是整个图像上。
这是一个非常简单的方法:
-
在图像上制作 Canny 边缘图。首先在低阈值到 0.66*[平均值] 和高阈值到 1.33*[平均值] 的范围内尝试使用 Canny 函数的参数。 (表示灰度值的平均值)。
您需要稍微调整一下参数以获得主要组件/数字作为单独组件清晰可见的图像。在这个阶段,接近完美就足够了。
将每条 Canny 边缘视为一个连接组件(即使用 cvFindContours() 或其 C++ 对应项,无论哪种),可以估计前景和背景灰度并达到阈值。
最后一点,请查看this paper 的第 2. 和 3. 部分。跳过大部分非必要的理论部分,在 OpenCV 中实现它应该不会太难。
希望这有帮助!
编辑 1:
基于 Canny 边缘阈值,这是一个非常粗略的想法,足以微调这些值。 high_threshold
控制在检测到边缘之前必须有多强。基本上,边缘的梯度幅度必须大于high_threshold
,才能首先被检测到。所以这是对边缘的初始检测。
现在,low_threshold
处理连接附近的边缘。它控制有多少附近断开的边将组合在一起成为一条边。如需更好的想法,请阅读this webpage 的“第 6 步”。尝试设置一个非常小的低阈值,看看事情是如何发生的。如果 0.66*[mean value] 不适用于这些图像,您可以丢弃它 - 无论如何它只是一个经验法则。
【讨论】:
嗯。好吧,这种使用阈值的方法需要时间,但并非不可能获得一组可以在大量图像上产生良好结果的值。试试low_threshold = 50, high_threshold = 150
。通常 low_threshold :根据 Canny 的原始论文,high_threshold 应该在 1:3 左右。摆弄! :)
对不起,我应该更准确。我担心可靠性,因为如果 Canny 甚至在一个数字上造成很小的破损,该方法就会失败。另一方面,如果我的关闭方法效果不佳,则数字格式错误,但仍然可以被计算机检测到。如果您认为我可以在一系列照明条件和模糊范围内使用固定的 Canny 阈值获得可靠的结果,我会试一试。论文的结果看起来确实令人印象深刻。
是的,可靠性是个大问题。尽管许多论文使用 Canny 边缘图作为初始步骤得到了相当不错的结果,但我还没有遇到任何特别提到使用的参数/参数的论文。所以,是的,在不断变化的环境下的可靠性将成为一个主要问题。无论如何,您的自我回答似乎运作良好!【参考方案3】:
我们使用 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
【讨论】:
【参考方案4】:如果您知道自己有大量的网格,您可以尝试在每个图块的基础上工作。处理 9 个子图像而不是整个图片很可能会导致每个子图像的亮度更均匀。如果您的裁剪完美,您甚至可以尝试单独处理每个数字单元;但这完全取决于您的作物的可靠性。
【讨论】:
酷;如果您对每个单元格都有完美的裁剪,并且可以轻松地隔离每个数字,那么模板匹配可能会在一定程度上起作用……这些单元格上只有 10 个可能的内容。我觉得通过良好的培训可以很好地工作;您是否希望所有输入都使用相同的字体? 不一定。我使用定向梯度直方图将每个数字的重要特征分离为“特征向量”,然后使用支持向量机对向量进行分类。 I'm told这是最可靠的数字识别方式。【参考方案5】:与扁平形状相比,椭圆形状的计算复杂。 尝试改变:
Mat kernel = Imgproc.getStructuringElement(Imgproc.MORPH_ELLIPSE, new Size(19,19));
到:
Mat kernel = Imgproc.getStructuringElement(Imgproc.MORPH_RECT, new Size(19,19));
可以加快您足够的解决方案,而对准确性的影响很小。
【讨论】:
以上是关于快速图像阈值处理的主要内容,如果未能解决你的问题,请参考以下文章