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

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

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

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

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

【问题讨论】:

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

标签: c++ opencv image-processing noise-reduction


【解决方案1】:

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

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

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

【讨论】:

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

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

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

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

更快的解决方案:

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

【讨论】:

    【解决方案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 步获得结果图像。

    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('./imagesStackoverflow/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】:

      结果:

      【讨论】:

        【解决方案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 运算应用于去噪结果图像。

        【讨论】:

          猜你喜欢
          • 2014-05-22
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 2017-06-23
          • 2012-03-08
          • 1970-01-01
          • 2012-07-03
          相关资源
          最近更新 更多