OpenCV - 去除图像中的噪声

OpenCV - Removal of noise in image

我这里有一张图片 table.. 在右侧的列中,背景充满了噪点

如何检测有噪点的区域?我只想在有噪声的部分应用某种滤镜,因为我需要对其进行 OCR,任何一种滤镜都会降低整体识别度

什么样的滤镜最能去除图像中的背景噪声?

如前所述,我需要对图像进行 OCR

据我所知,中值滤波器是降低噪声的最佳解决方案。我建议使用 3x3 window 的中值滤波器。请参见函数 cv::medianBlur()

但在与 OCR 同时使用任何噪声过滤时要小心。会导致识别准确率下降。

我还建议尝试使用一对函数(cv::erode() 和 cv::dilate())。但我不确定它会是 cv::medianBlur() 和 window 3x3.

的最佳解决方案

我会选择中值模糊(可能是 5*5 内核)。

如果您打算对图像应用 OCR。我建议您执行以下操作:

  1. 使用中值滤波器过滤图像。
  2. 在过滤后的图像中查找轮廓,您将只得到文本轮廓(称它们为F)。
  3. 在原始图像中找到轮廓(称之为 O)。
  4. 隔离 O 中与 F.
  5. 中的任何轮廓相交的所有轮廓

更快的解决方案:

  1. 在原始图像中找到轮廓。
  2. 根据大小过滤它们。

尝试像这样对图像进行阈值处理。确保你的 src 是灰度的。此方法将仅保留强度介于 150 和 255 之间的像素。

threshold(src, output, 150, 255, CV_THRESH_BINARY | CV_THRESH_OTSU);

您可能想要反转图像,因为您正试图消除灰色像素。操作完成后,再次反转即可得到您想要的结果。

我在 OpenCV 中尝试了一些 filters/operations,它似乎工作得很好。

第 1 步:放大 图像 -

kernel = np.ones((5, 5), np.uint8)
cv2.dilate(img, kernel, iterations = 1)

如你所见,噪点消失了,但字符很亮,所以我腐蚀了图像。

第 2 步:腐蚀 图像 -

kernel = np.ones((5, 5), np.uint8)
cv2.erode(img, kernel, iterations = 1)

如您所见,噪音消失了,但其他列上的某些字符已损坏。我建议 运行 仅在嘈杂的列上执行这些操作。您可能想使用 HoughLines 来查找最后一列。然后您可以仅提取该列,运行 膨胀 + 腐蚀并将其替换为原始图像中的相应列。 另外,dilation + erosion 实际上是一个叫做closing的操作。您可以直接调用 -

cv2.morphologyEx(img, cv2.MORPH_CLOSE, kernel)

正如@Ermlg 所建议的,内核为 3 的 medianBlur 也能很好地工作。

cv2.medianBlur(img, 3)

备选步骤

如您所见,所有这些过滤器都有效,但如果您仅在噪声所在的部分实施这些过滤器会更好。为此,请使用以下内容:

edges = cv2.Canny(img, 50, 150, apertureSize = 3) // img is gray here
lines = cv2.HoughLinesP(edges, 1, np.pi / 180, 100, 1000, 50) // last two arguments are minimum line length and max gap between two lines respectively.
for line in lines: 
    for x1, y1, x2, y2 in line: 
        print x1, y1
// This gives the start coordinates for all the lines. You should take the x value which is between (0.75 * w, w) where w is the width of the entire image. This will give you essentially **(x1, y1) = (1896, 766)**

然后,你可以像这样只提取这部分:

extract = img[y1:h, x1:w] // w, h are width and height of the image

然后,在此图像中执行过滤器(中值或闭合)。去除噪声后,需要将这张过滤后的图像代替原图像中模糊的部分。 图像[y1:h, x1:w] = 中位数

这在 C++ 中很简单:

extract.copyTo(img, new Rect(x1, y1, w - x1, h - y1))

采用替代方法的最终结果

希望对您有所帮助!

我的解决方案是基于阈值法分 4 步得到结果图像。

  1. 阅读 OpenCV 3.2.0 的图片。
  2. 应用GaussianBlur()平滑图像,尤其是灰色区域。
  3. 遮住图像以将文本更改为白色,将其余部分更改为黑色。
  4. 将蒙版图像反转为白底黑字。

代码在Python 2.7。它可以很容易地更改为C++

import numpy as np
import cv2
import matplotlib.pyplot as plt
%matplotlib inline 

# read Danish doc image 
img = cv2.imread('./imagesWhosebug/danish_invoice.png')

# apply GaussianBlur to smooth image
blur = cv2.GaussianBlur(img,(5,3), 1) 

# threshhold gray region to white (255,255, 255) and sets the rest to black(0,0,0)
mask=cv2.inRange(blur,(0,0,0),(150,150,150))

# invert the image to have text black-in-white
res = 255 - mask

plt.figure(1)
plt.subplot(121), plt.imshow(img[:,:,::-1]), plt.title('original') 
plt.subplot(122), plt.imshow(blur, cmap='gray'), plt.title('blurred')
plt.figure(2)
plt.subplot(121), plt.imshow(mask, cmap='gray'), plt.title('masked')
plt.subplot(122), plt.imshow(res, cmap='gray'), plt.title('result')
plt.show()

以下是代码绘制的图片,供参考。

这是 结果图像,2197 x 3218 像素。

如果您非常担心删除可能会影响 OCR 检测的像素。在不添加人工制品的情况下,尽可能保持原始状态。然后你应该创建一个 blob 过滤器。并删除任何小于 n 像素左右的斑点。

不打算编写代码,但我知道这很好用,因为我自己使用它,尽管我不使用 openCV(出于速度原因,我编写了自己的多线程 blobfilter)。抱歉,我不能在这里分享我的代码。只是描述如何去做。

结果:

如果处理时间不是问题,在这种情况下一种非常有效的方法是计算所有黑色连通分量,并删除那些小于几个像素的分量。它会删除所有嘈杂的点(除了那些接触有效组件的点),但保留所有字符和文档结构(行等)。

要使用的函数是 connectedComponentWithStats (before you probably need to produce the negative image, the threshold 函数,在这种情况下,THRESH_BINARY_INV 可以工作),在找到小连通分量的地方绘制白色矩形。

事实上,此方法可用于查找字符,这些字符定义为具有给定最小和最大尺寸的连通分量,并且纵横比在给定范围内。

我已经遇到过同样的问题并得到了最好的解决方案。 将源图像转换为 grayscale image 并应用 fastNlMeanDenoising 函数,然后应用 threshold.

像这样 -

fastNlMeansDenoising(gray,dst,3.0,21,7);
threshold(dst,finaldst,150,255,THRESH_BINARY);

还可以根据背景噪声图像调整阈值。 例如- threshold(dst,finaldst,200,255,THRESH_BINARY);

注意 - 如果您的列线被删除...您可以从源图像中获取列线的掩码,并可以使用 AND、OR、XOR 等 BITWISE 运算将其应用于去噪结果图像。