2013-03-22 115 views
25

什么是快速和可靠的方式阈值图像可能模糊和不均匀的亮度?快速图像阈值

实施例(模糊但均匀的亮度):

enter image description here

因为图像是不能保证有均匀的亮度,这是不可行的使用固定的阈值。自适应阈值的作品还好,但由于模糊的它在功能产生断裂和扭曲(在这里,最重要的特点是独位):

enter image description here

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

我发现的最好的解决方案是将图像的形态学关闭(归功于this post)以使亮度一致,然后重新归一化,然后使用固定阈值(使用Otsu算法选择最佳阈值水平) :

enter image description here

这里是OpenCV的此代码的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滤波器有类似的结果。该滤波器在不包含特征的区域中拾取高频噪声,并且由于模糊而遭受与自适应阈值类似的失真。它也需要与关闭操作一样长。下面是一个15×15滤波器的例子:

enter image description here

编辑2:基于AruniRC的回答中,我使用Canny边缘检测的图像上的与所述建议的参数:

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

我不确定如何自动自动微调参数获取连接数字。

enter image description here

+0

假设brigthness变化发生在低频率,您可以尝试在高通滤波图像上设置阈值。但是,我不知道这些过滤器操作在移动设备上的速度有多快,而且我认为您需要一个相当大的内核。 – DCS 2013-03-22 08:42:35

+0

@DCS不幸的是,我不认为高通滤波器可以工作。看到我的编辑到上面的帖子。 – 2013-03-22 22:25:37

+2

由于您感兴趣的功能涵盖多个像素,因此如何将图像先降低至较低分辨率?然后,您可以返回并以原始分辨率获取更多细节,并使用较低分辨率的版本作为蒙版。 – 2013-03-24 03:49:17

回答

18

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

enter image description here

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

任何有关如何进一步改进此想法的建议(无论是进一步降低速度还是降低图像质量的恶化)都非常受欢迎。

+0

您应该使用adaptiveThreshold而不是阈值。 自适应阈值将在黑暗图像的情况下提供更好的结果。 – AnkitRox 2015-02-17 06:24:16

+0

请让我知道,如果你想要自适应阈值的代码.. – AnkitRox 2015-02-17 06:24:40

+0

@AnkitRox我在这个问题中讨论自适应阈值。 – 2015-02-17 15:46:35

2

替代做法:

假设你的意图是必须明确二值化的数字...您的重点转向组件,而不是整个图像。

这里有一个很简单的方法:

  1. 执行图像上的坎尼edgemap。首先尝试使用Canny函数的参数在低阈值到0.66 * [平均值]和高阈值到1.33 * [平均值]的范围内。 (意思是greylevel值的平均值)。
  2. 您需要一点一点地调整参数以获取主要组件/数字清晰可见的图像,作为单独的组件。在这个阶段,接近完美就足够了。
  3. 考虑到每个Canny边缘是一个连通的组件(即使用cvFindContours()或它的C++对应物,无论哪个)可以估计前景和背景灰度并达到阈值。

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

    希望这有助于!

编辑1:

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

现在,low_threshold处理连接附近的边缘。它控制着多少附近断开的边将被组合到一个边中。更好的主意,请阅读this webpage的“步骤6”。尝试设置一个非常小的low_threshold并查看事情是如何发生的。如果它不适用于这些图像,您可以丢弃0.66 * [平均值]的东西 - 无论如何它只是一个经验法则。

+1

有趣的方法,但请参阅我的问题编辑2。 – 2013-03-24 17:14:47

+0

hmm。好吧,这种玩阈值的游戏需要时间,但并不是不可能获得一系列值得大量图像体面的结果。尝试'low_threshold = 50,high_threshold = 150'。一般low_threshold:high_threshold应按Canny的原始文件大约1:3。小提琴! :) – AruniRC 2013-03-25 06:47:44

+0

对不起,我应该更确切。我担心可靠性,因为如果Canny在数字中创建了一个小小的断点,该方法就会失败。另一方面,如果我的关闭方法运行不正常,这些数字格式不正确,但仍可以通过计算机检测到。如果您认为我可以在一系列照明条件和模糊情况下获得具有固定坎尼阈值的可靠结果,我会试一试。这篇论文的结果似乎相当令人印象深刻。 – 2013-03-25 15:29:21

0

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

+0

我其实已经在做:) – 2013-03-30 14:31:23

+0

很酷;如果每个细胞都有完美的作物,并且可以轻松地分离每个数字,那么模板匹配可能会在一定程度上起作用......这些细胞上只有10种可能的内容。我觉得这可以在良好的训练中运作良好;你是否期望所有的输入使用相同的字体? – amadillu 2013-03-31 12:07:20

+0

不一定。我使用面向梯度直方图将每个数字的重要特征分离为“特征向量”,然后使用支持向量机对向量进行分类。 [我被告知](http://stackoverflow.com/a/13319850/1397061)这是做数字识别最可靠的方法。 – 2013-03-31 15:57:40

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

0

椭圆形状复杂,如果比较扁平的形状来计算。 尝试改变:

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

到:

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

可以加快你的低影响精度足够的解决方案。