模糊图像的阈值 - 第2部分

时间:2012-11-30 05:04:30

标签: image-processing opencv threshold

如何对此模糊图像进行阈值处理以使数字尽可能清晰?

a previous post中,我尝试自适应地对模糊图像进行阈值处理(左图),这会导致数字失真和断开(右图):

enter image description here

从那以后,我尝试使用this post中描述的形态学关闭操作来使图像的亮度均匀:

enter image description here

如果我自适应地对此图像进行阈值处理,则不会获得明显更好的结果。但是,由于亮度大致均匀,我现在可以使用普通阈值:

enter image description here

这比以前好多了,但我有两个问题:

  1. 我不得不手动选择阈值。虽然关闭操作会产生均匀的亮度,但其他图像的亮度可能会有所不同。
  2. 在阈值水平略有变化的情况下,图像的不同部分会做得更好。例如,左上角的9和7部分褪色,应该有一个较低的阈值,而6s中的一些融合成8s,应该有更高的阈值。
  3. 我认为回到自适应阈值,但是块大小非常大(图像的1/9)可以解决这两个问题。相反,我最终得到一个奇怪的“光环效应”,其中图像的中心更亮,但边缘与正常阈值图像大致相同:

    enter image description here

    编辑:remi suggested从形态上打开此帖子右上角的阈值图像。这不太好用。使用椭圆内核,只有3x3足够小,以避免完全消除图像,即使这样,数字也会有明显的破损:

    enter image description here

    Edit2:mmgp suggested使用维纳滤镜消除模糊。我将this code for Wiener filtering in OpenCV改编为OpenCV4Android,但它使图像更加模糊!这是使用我的代码和5x5内核过滤之前(左)和之后的图像:

    enter image description here

    这是我改编的代码,可以就地过滤:

    private void wiener(Mat input, int nRows, int nCols) { // I tried nRows=5 and nCols=5
    
        Mat localMean = new Mat(input.rows(), input.cols(), input.type());
        Mat temp = new Mat(input.rows(), input.cols(), input.type());
        Mat temp2 = new Mat(input.rows(), input.cols(), input.type());
    
        // Create the kernel for convolution: a constant matrix with nRows rows 
        // and nCols cols, normalized so that the sum of the pixels is 1.
        Mat kernel = new Mat(nRows, nCols, CvType.CV_32F, new Scalar(1.0 / (double) (nRows * nCols)));
    
        // Get the local mean of the input.  localMean = convolution(input, kernel)
        Imgproc.filter2D(input, localMean, -1, kernel, new Point(nCols/2, nRows/2), 0); 
    
        // Get the local variance of the input.  localVariance = convolution(input^2, kernel) - localMean^2 
        Core.multiply(input, input, temp);  // temp = input^2
        Imgproc.filter2D(temp, temp, -1, kernel, new Point(nCols/2, nRows/2), 0); // temp = convolution(input^2, kernel)
        Core.multiply(localMean, localMean, temp2); //temp2 = localMean^2
        Core.subtract(temp, temp2, temp); // temp = localVariance = convolution(input^2, kernel) - localMean^2  
    
        // Estimate the noise as mean(localVariance)
        Scalar noise = Core.mean(temp);
    
        // Compute the result.  result = localMean + max(0, localVariance - noise) / max(localVariance, noise) * (input - localMean)
    
        Core.max(temp, noise, temp2); // temp2 = max(localVariance, noise)
    
        Core.subtract(temp, noise, temp); // temp = localVariance - noise
        Core.max(temp, new Scalar(0), temp); // temp = max(0, localVariance - noise)
    
        Core.divide(temp, temp2, temp);  // temp = max(0, localVar-noise) / max(localVariance, noise)
    
        Core.subtract(input, localMean, input);  // input = input - localMean
        Core.multiply(temp, input, input); // input = max(0, localVariance - noise) / max(localVariance, noise) * (input - localMean)
        Core.add(input, localMean, input); // input = localMean + max(0, localVariance - noise) / max(localVariance, noise) * (input - localMean)
    }
    

6 个答案:

答案 0 :(得分:6)

您可以尝试一些提示:

  • 在原始阈值图像(第一张图片右侧有噪声的图像)中应用形态开口。您应该摆脱大部分背景噪音,并能够重新连接数字。

  • 使用原始图像的不同预处理而不是morpho关闭,例如中值滤镜(往往会模糊边缘)或bilateral filtering,这将更好地保留边缘,但计算速度较慢。

  • 就阈值而言,您可以在cv :: threshold中使用CV_OTSU标志来确定全局阈值的最佳值。局部阈值可能仍然更好,但应该使用双边或中值滤波器更好地工作

答案 1 :(得分:6)

我已经尝试使用Otsu的算法(CV_OTSU - 感谢remi!)分别对每个3x3盒子进行阈值处理,以确定每个盒子的最佳阈值。这比整个图像的阈值处理要好一些,并且可能更加健壮。

enter image description here

欢迎更好的解决方案。

答案 2 :(得分:6)

如果您愿意在其上花费一些周期,则可以使用去模糊技术在处理之前锐化图片。在OpenCV中没有任何东西,但如果这是一种成败的东西,你可以添加它。

有很多关于这个主题的文献: http://www.cse.cuhk.edu.hk/~leojia/projects/motion_deblurring/index.html http://www.google.com/search?q=motion+deblurring

OpenCV邮件列表上的一些喋喋不休: http://tech.groups.yahoo.com/group/OpenCV/message/20938

您看到的奇怪的“光环效应”可能是由于OpenCV在自适应阈值位于图像边缘处/附近并且其使用的窗口“悬挂”到边缘时呈现黑色非图像领域。有办法纠正这个问题,很可能你会制作一个临时图像,至少有两个完整的块尺寸比相机的图像更高更宽。然后将相机图像复制到其中间。然后将临时图像的周围“空白”部分设置为来自摄像机的图像的平均颜色。现在,当您执行自适应阈值时,边缘处/附近的数据将更接近准确。它不是完美的,因为它不是真实的画面,但它会产生比OpenCV所假设的黑色更好的结果。

答案 3 :(得分:5)

我的建议假设您可以识别数独细胞,我认为这并不是太多。在我看来,尝试应用形态学运算符(虽然我真的很喜欢它们)和/或二值化方法作为第一步是错误的方式。无论出于何种原因(原始相机角度和/或移动,以及其他原因),您的图像至少部分模糊。所以你需要的是通过执行反卷积来恢复它。当然要求完美的去卷积太多了,但我们可以尝试一些东西。

其中一件"事情"是Wiener filter,例如,在Matlab中,该函数名为deconvwnr。我注意到在垂直方向上的模糊,所以我们可以使用一定长度的垂直内核(在下面的例子中为10)执行反卷积,并且还假设输入不是无噪声(假设为5%) - I&# 39;我只是想在这里给出一个非常肤浅的观点,放轻松。在Matlab中,您的问题至少部分通过以下方式解决:

f = imread('some_sudoku_cell.png');
g = deconvwnr(f, fspecial('motion', 10, 90), 0.05));
h = im2bw(g, graythresh(g)); % graythresh is the Otsu method

以下是您的一些细胞的结果(原始,otsu,otsu区域生长,形态增强图像,otsu来自形态增强图像,区域增长,otsu of deconvolution):

enter image description here enter image description here enter image description here enter image description here enter image description here enter image description here
enter image description here enter image description here enter image description here enter image description here enter image description here enter image description here
enter image description here enter image description here enter image description here enter image description here enter image description here enter image description here
enter image description here enter image description here enter image description here enter image description here enter image description here enter image description here
enter image description here enter image description here enter image description here enter image description here enter image description here enter image description here
enter image description here enter image description here enter image description here enter image description here enter image description here enter image description here

通过使用半径为3的平盘执行原始+ tophat(原始) - bottomhat(原始)来生成增强图像。我手动选择种子点以进行区域增长并手动选择最佳阈值。

对于空细胞,你会得到奇怪的结果(原始和otsu的去污):

enter image description here enter image description here

但是,我认为你无法检测细胞是否为空(全局阈值已经解决了)。

编辑:

使用不同的方法添加了我可以获得的最佳结果:区域增长。我还尝试了其他一些方法,但这是第二好的方法。

答案 4 :(得分:3)

您可能需要本地阈值处理。有一些通用的方法。

检查niblack算法。另见niblack thresholdinghttps://stackoverflow.com/a/9891678/461499 我们成功地将其用于文档分段。

答案 5 :(得分:2)

另一种选择是使用区域生长技术。我们将这些内容提供给学生,可以在以下网址找到:

http://www.cs205.org/resources/hw4.pdf
(问题5)

目标是传播来自一组种子的信息。您仍然需要一个阈值(在这种情况下,您现在将设置2个阈值!),但最终可能会得到更好的结果。