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

1''*_*1'' 10 opencv image-processing threshold

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

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

在此输入图像描述

从那时起,我已经使用如所描述的形态学闭运算试图此篇使图像均匀的亮度:

在此输入图像描述

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

在此输入图像描述

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

  1. 我不得不手动选择阈值.尽管关闭操作导致均匀的亮度,但是对于其他图像,亮度水平可能不同.
  2. 在阈值水平略有变化的情况下,图像的不同部分会做得更好.例如,左上角的9和7部分褪色并且应该具有较低的阈值,而6中的一些已经融合成8s并且应该具有更高的阈值.

我认为回到自适应阈值,但是具有非常大的块大小(图像的1/9)将解决这两个问题.相反,我最终得到一个奇怪的"光环效应",其中图像的中心更亮,但边缘与正常阈值图像大致相同:

在此输入图像描述

编辑:remi 建议在形态上打开这篇文章右上角的阈值图像.这不太好用.使用椭圆内核,只有3x3足够小,以避免完全消除图像,即使这样,数字也会有明显的破损:

在此输入图像描述

Edit2:mmgp 建议使用维纳滤镜来消除模糊.我将此代码用于OpenCV中的Wiener过滤到OpenCV4Android,但它使图像更加模糊!这是使用我的代码和5x5内核过滤之前(左)和之后的图像:

在此输入图像描述

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

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)
}
Run Code Online (Sandbox Code Playgroud)

rem*_*emi 6

您可能尝试过的一些提示:

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

  • 使用原始图像的不同预处理而不是morpho闭合,例如中值滤波器(倾向于模糊边缘)或双边滤波,这将更好地保留边缘但计算较慢.

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


1''*_*1'' 6

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

在此输入图像描述

但是,欢迎更好的解决方案.


Mik*_*ord 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所假设的黑色更好的结果.


mmg*_*mgp 5

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

其中一个"东西"是Wiener过滤器,例如,在Matlab中,该函数被命名deconvwnr.我注意到在垂直方向上的模糊,所以我们可以使用一定长度的垂直内核(在下面的例子中为10)执行反卷积,并且还假设输入没有噪声(假设为5%) - 我'我只是想在这里给出一个非常肤浅的观点,放轻松.在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
Run Code Online (Sandbox Code Playgroud)

以下是您的一些细胞的结果(原始,otsu,otsu区域生长,形态增强图像,otsu从形态增强图像与区域增长,otsu的反卷积):

在此输入图像描述 在此输入图像描述 在此输入图像描述在此输入图像描述 在此输入图像描述 在此输入图像描述
在此输入图像描述 在此输入图像描述 在此输入图像描述 在此输入图像描述 在此输入图像描述 在此输入图像描述
在此输入图像描述 在此输入图像描述 在此输入图像描述 在此输入图像描述 在此输入图像描述 在此输入图像描述
在此输入图像描述 在此输入图像描述 在此输入图像描述 在此输入图像描述 在此输入图像描述 在此输入图像描述
在此输入图像描述 在此输入图像描述 在此输入图像描述 在此输入图像描述 在此输入图像描述 在此输入图像描述
在此输入图像描述 在此输入图像描述 在此输入图像描述 在此输入图像描述 在此输入图像描述 在此输入图像描述

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

对于空单元格,您会得到奇怪的结果(原始和otsu of deconvlution):

在此输入图像描述 在此输入图像描述

但我认为你不会检测一个细胞是否为空(全局阈值已经解决了).

编辑:

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