如何对此模糊图像进行阈值处理以使数字尽可能清晰?
在a previous post中,我尝试自适应地对模糊图像进行阈值处理(左图),这会导致数字失真和断开(右图):
从那以后,我尝试使用this post中描述的形态学关闭操作来使图像的亮度均匀:
如果我自适应地对此图像进行阈值处理,则不会获得明显更好的结果。但是,由于亮度大致均匀,我现在可以使用普通阈值:
这比以前好多了,但我有两个问题:
我认为回到自适应阈值,但是块大小非常大(图像的1/9)可以解决这两个问题。相反,我最终得到一个奇怪的“光环效应”,其中图像的中心更亮,但边缘与正常阈值图像大致相同:
编辑:remi suggested从形态上打开此帖子右上角的阈值图像。这不太好用。使用椭圆内核,只有3x3足够小,以避免完全消除图像,即使这样,数字也会有明显的破损:
Edit2:mmgp suggested使用维纳滤镜消除模糊。我将this code for Wiener filtering in OpenCV改编为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)
}
答案 0 :(得分:6)
您可以尝试一些提示:
在原始阈值图像(第一张图片右侧有噪声的图像)中应用形态开口。您应该摆脱大部分背景噪音,并能够重新连接数字。
使用原始图像的不同预处理而不是morpho关闭,例如中值滤镜(往往会模糊边缘)或bilateral filtering,这将更好地保留边缘,但计算速度较慢。
就阈值而言,您可以在cv :: threshold中使用CV_OTSU标志来确定全局阈值的最佳值。局部阈值可能仍然更好,但应该使用双边或中值滤波器更好地工作
答案 1 :(得分:6)
我已经尝试使用Otsu的算法(CV_OTSU - 感谢remi!)分别对每个3x3盒子进行阈值处理,以确定每个盒子的最佳阈值。这比整个图像的阈值处理要好一些,并且可能更加健壮。
欢迎更好的解决方案。
答案 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):
通过使用半径为3的平盘执行原始+ tophat(原始) - bottomhat(原始)来生成增强图像。我手动选择种子点以进行区域增长并手动选择最佳阈值。
对于空细胞,你会得到奇怪的结果(原始和otsu的去污):
但是,我认为你无法检测细胞是否为空(全局阈值已经解决了)。
编辑:
使用不同的方法添加了我可以获得的最佳结果:区域增长。我还尝试了其他一些方法,但这是第二好的方法。
答案 4 :(得分:3)
您可能需要本地阈值处理。有一些通用的方法。
检查niblack算法。另见niblack thresholding。 https://stackoverflow.com/a/9891678/461499 我们成功地将其用于文档分段。
答案 5 :(得分:2)
另一种选择是使用区域生长技术。我们将这些内容提供给学生,可以在以下网址找到:
http://www.cs205.org/resources/hw4.pdf
(问题5)
目标是传播来自一组种子的信息。您仍然需要一个阈值(在这种情况下,您现在将设置2个阈值!),但最终可能会得到更好的结果。