什么是快速可靠的方法来阈值图像可能模糊和不均匀的亮度?
示例(模糊但亮度一致):
因为不能保证图像具有均匀的亮度,所以使用固定阈值是不可行的。自适应阈值工作正常,但由于模糊,它会在特征中造成中断和扭曲(这里,重要的特征是数独数字):
我也尝试过使用直方图均衡化(使用OpenCV的均衡器函数)。它在不减少亮度差异的情况下增加对比度。
我找到的最佳解决方案是将图像按其形态闭合(归功于这篇文章)进行分割,以使亮度均匀,然后重新规范化,然后使用固定阈值(使用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);
我不知道如何可靠地自动微调参数以获得连接的数字。
我们使用Bradleys算法解决非常类似的问题(从背景中分割字母,光线不均匀,背景颜色不均匀),如下所述:http://people.scs.carleton.ca:8008/~roth/iit出版物iti/docs/gerh-50002。pdf,C#代码如下:http://code.google.com/p/aforge/source/browse/trunk/Sources/Imaging/Filters/Adaptive二值化/BradleyLocalThresholding。cs?r=1360。它适用于积分图像,可以使用OpenCV的积分函数计算积分图像。它非常可靠和快速,但本身并没有在OpenCV中实现,但很容易移植。
另一种选择是openCV中的adaptiveThreshold方法,但我们没有尝试:http://docs.opencv.org/modules/imgproc/doc/miscellaneous_transformations.html#adaptivethreshold.平均值版本与bradleys相同,只是它使用一个常量来修改平均值,而不是百分比,我认为这更好。
此外,这里还有一篇好文章:https://dsp.stackexchange.com/a/2504
替代方法:
假设您的目的是将数字清楚地进行二值化。。。将焦点转移到组件,而不是整个图像。
这里有一个非常简单的方法:
>
将每个Canny边视为一个连接的组件(即使用cvFindContours()或其C对应项,以两者为准),可以估计前景和背景灰度并达到阈值。
最后,请看一下第2节。和3。这篇论文。跳过大多数非必要的理论部分,在OpenCV中实现它应该不会太困难。
希望这有帮助!
编辑1:
基于Canny边缘阈值,这里有一个非常粗略的想法,足以微调值。“高\u阈值”(high\u threshold)控制检测边缘之前的强度。基本上,要首先检测到边缘,其梯度幅值必须大于高\u阈值。这就是边缘的初始检测。
现在,低\u阈值处理连接附近边缘的问题。它控制附近断开连接的边合并到一条边的数量。要了解更多信息,请阅读此网页的“步骤6”。尝试设置一个非常小的low\u阈值,看看事情是如何发生的。如果对这些图像不起作用,你可以放弃0.66*[平均值]的东西——不管怎样,这只是一条经验法则。
根据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倍以上,这表明关闭时间大致与图像中的像素数成比例。
任何关于如何进一步改进这个想法的建议(通过进一步降低速度或减少画质的恶化)都是非常受欢迎的。
图像阈值 目标 在本教程中,您将学习简单阈值处理,自适应阈值处理,Otsu阈值处理等。 您将学习以下函数:cv2.threshold, cv2.adaptiveThreshold等。 一、单的阈值 在这里,事情是直截了当的。如果像素值大于阈值,则为其分配一个值(可以是白色),否则为其分配另一个值(可以是黑色)。使用的函数是cv2.threshold。第一个参数是源图像,它应该是灰度图像。第二个参数
目标 在本教程中,您将学习简单阈值,自适应阈值和Otsu阈值。 你将学习函数cv.threshold和cv.adaptiveThreshold。 简单阈值 在这里,问题直截了当。对于每个像素,应用相同的阈值。如果像素值小于阈值,则将其设置为0,否则将其设置为最大值。函数cv.threshold用于应用阈值。第一个参数是源图像,它应该是灰度图像。第二个参数是阈值,用于对像素值进行分类。第三个参数是分
当我通过VS代码运行我的网页时,我的所有图片都会显示出来,但由于某种原因,当我通过localhost运行网页时,没有任何图片或CSS被发送。这是我下面的代码,任何帮助都将不胜感激。我曾试图在网上找到解决方案,但迄今为止似乎没有任何效果。 这是我的文件结构
如何设置模糊图像的阈值以使数字尽可能清晰? 在之前的一篇文章中,我尝试自适应地对模糊的图像进行阈值化(左),这导致数字失真和断开连接(右): 从那时起,我尝试使用本文中描述的形态学关闭操作来使图像的亮度均匀: 如果我自适应地设置此图像的阈值,则不会得到明显更好的结果。但是,由于亮度大致一致,我现在可以使用普通阈值: 这比以前好多了,但我有两个问题: 我必须手动选择阈值。虽然关闭操作会产生均匀的亮度
我有一张非常模糊的432x432的数独拼图,它的自适应阈值不好(取5x5像素块大小的平均值,然后减去2): 正如你所看到的,数字有点失真,有很多破损,一些5s融合成6s,6s融合成8s。此外,还有大量噪音。为了修复噪声,我必须使用高斯模糊使图像更加模糊。然而,即使是相当大的高斯核和自适应阈值块大小(21x21,减去2)也无法消除所有中断,并将数字融合到一起,甚至更严重: 我还尝试过在阈值化后放大图
我目前正在阅读OpenCV网站tyring上的示例代码,以查找图像中的轮廓。 我首先读取图像并转换为灰度: 然后,我通过应用阈值将图像转换为二进制: 根据教程...然后,我应该能够调用阈值图像上的: 当尝试执行此代码时,由于某种原因,我得到一个类型错误: 轮廓=cv2。findContours(thresh,cv2.RETR_TREE,cv2.CHAIN_Abrox_SIMPLE)类型错误:图像不