OpenCV - 去除图像中的噪声

Posted

技术标签:

【中文标题】OpenCV - 去除图像中的噪声【英文标题】:OpenCV - Removal of noise in image 【发布时间】:2017-07-05 11:19:26 【问题描述】:

我这里有一张带表格的图片。在右侧的列中,背景充满了噪点

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

而什么样的滤镜最适合去除图像中的背景噪音?

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

【问题讨论】:

您可能想从示例图片中删除“个人”信息。 您能说出文档图片中的语言吗? 丹麦语..... 所有文件在视觉上都是非彩色的?你会接受只去除噪音以提高 OCR 准确性的解决方案吗?因为那样会更容易。 所有图像都是黑色/白色。是的,我需要一个解决方案来消除噪音,但同时不会降低图像中其余文本的识别精度(无噪音) 【参考方案1】:

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

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

我还建议尝试使用一对函数(cv::erode() 和 cv::dilate())。但我不确定它会是最好的解决方案,然后是 cv::medianBlur() 与窗口 3x3。

【讨论】:

如果噪声过滤会导致 OCR 不佳,您是否可以检测噪声所在的区域(如果有噪声)并仅在该区域应用过滤器? @clarkk 很难区分噪声和字符的精细结构。但在你的情况下,我认为这不是问题,因为与噪音相比,字体足够大。【参考方案2】:

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

如果您打算对图像应用 OCR。我会建议你以下几点:

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

更快的解决方案:

    在原始图像中查找轮廓。 根据大小过滤它们。

【讨论】:

【参考方案3】:

尝试像这样对图像进行阈值处理。确保您的 src 处于灰度状态。此方法将仅保留强度在 150 到 255 之间的像素。

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

您可能希望在尝试否定灰色像素时反转图像。操作后,再次反转即可得到您想要的结果。

【讨论】:

如果你仔细观察输入图像中的像素,你会发现这里的输入已经是一个二值图像,像素为 0 或 255【参考方案4】:

我在 OpenCV 中尝试了一些过滤器/操作,似乎效果很好。

第 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 来查找最后一列。然后您可以只提取该列,运行膨胀+腐蚀并将其替换为原始图像中的相应列。 此外,膨胀+腐蚀实际上是一种称为关闭的操作。这你可以直接调用 -

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))

替代方法的最终结果

希望对您有所帮助!

【讨论】:

你有没有一个代码示例可以检测到最后一列的噪音......就像你自己说的......无论你应用什么过滤器 - 文本总是更难识别......所以我只想对图像有噪声的部分应用过滤器 是的,我愿意,给我一些时间,我会把它添加到答案中。 添加了检测最后一列的方法。如果它回答了您的问题,请告诉我。 非常好。 :) 我会在下周研究它 第二个选项做到了!!【参考方案5】:

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

    通过OpenCV 3.2.0阅读图片。 应用GaussianBlur() 平滑图像,尤其是灰色区域。 屏蔽图像以将文本更改为白色,将其余文本更改为黑色。 将蒙版图像反转为白色的黑色文本。

代码在Python 2.7。可以轻松更改为C++

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

# read Danish doc image 
img = cv2.imread('./images***/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 像素的结果图片

【讨论】:

这仍然在整个图像上应用过滤器。我需要一个解决方案,只在有噪声的部分应用过滤器 @clarkk 好的。噪声区域是否固定且已知?喜欢您附上的示例图片吗?如果没有,还有要展示的示例文档吗?【参考方案6】:

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

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

【讨论】:

【参考方案7】: Blur(3x3 盒子) Threshold 127

结果:

【讨论】:

【参考方案8】:

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

要使用的函数是connectedComponentWithStats(在您可能需要生成负像之前,threshold 函数和THRESH_BINARY_INV 将在这种情况下工作),在找到小的连接组件的地方绘制白色矩形。

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

【讨论】:

我之前建议过 @user3800527 没错,我错过了。我的回答为 opencv 实现添加了一些提示。【参考方案9】:

我已经遇到过同样的问题并得到了最好的解决方案。 将源图像转换为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 运算应用于去噪结果图像。

【讨论】:

以上是关于OpenCV - 去除图像中的噪声的主要内容,如果未能解决你的问题,请参考以下文章

使用openCV去除二值图像中的噪声

去除图像中虚假的小噪声岛 - Python OpenCV

使用opencv去除噪声像素

Opencv——图像添加椒盐噪声高斯滤波去除噪声原理及手写Python代码实现

Opencv 中的去噪

六 OpenCV图像处理4 Canny 边缘检测