1''*_*1'' 10 opencv image-processing threshold
如何对此模糊图像进行阈值处理以使数字尽可能清晰?
在上一篇文章中,我尝试自适应地对模糊图像进行阈值处理(左图),这会导致数字失真和断开(右图):

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

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

这比以前好多了,但我有两个问题:
我认为回到自适应阈值,但是具有非常大的块大小(图像的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)
我已经尝试分别对每个3x3盒子进行阈值处理,使用Otsu的算法(CV_OTSU - 感谢remi!)来确定每个盒子的最佳阈值.这比整个图像的阈值处理要好一些,并且可能更稳健一些.

但是,欢迎更好的解决方案.
如果您愿意在其上花费一些周期,则可以使用去模糊技术在处理之前锐化图像.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所假设的黑色更好的结果.
我的建议假设您可以识别数独细胞,我认为这并不是太多.在我看来,尝试应用形态学运算符(虽然我真的很喜欢它们)和/或二值化方法作为第一步是错误的方法.无论出于何种原因(原始相机角度和/或移动,以及其他原因),您的图像至少部分模糊.所以你需要的是通过执行反卷积来恢复它.当然要求完美的去卷积太多了,但我们可以尝试一些东西.
其中一个"东西"是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):
但我认为你不会检测一个细胞是否为空(全局阈值已经解决了).
编辑:
添加了我用不同方法获得的最佳结果:区域增长.我也尝试了其他一些方法,但这是第二好的方法.
| 归档时间: |
|
| 查看次数: |
6329 次 |
| 最近记录: |