从图像中去除噪点线条

15

我有一些图像,上面有一些随机线条噪声,比如下面这个:
enter image description here
我想对它们进行预处理,以便去除不需要的噪声(扭曲书写的线条),从而可以与OCR(Tesseract)一起使用。
我脑海中出现的想法是使用膨胀来去除噪声,然后在第二步中使用腐蚀来修复书写的缺失部分。
为此,我使用了这段代码:

import cv2
import numpy as np

img = cv2.imread('linee.png', cv2.IMREAD_GRAYSCALE)
kernel = np.ones((5, 5), np.uint8)
img = cv2.dilate(img, kernel, iterations=1)
img = cv2.erode(img, kernel, iterations=1)
cv2.imwrite('delatedtest.png', img)

遗憾的是,膨胀并没有奏效,噪声还存在。

enter image description here
我尝试改变核形状,但情况变得更糟:部分或完全删除了文字。
我还发现一个答案说可以通过

将具有两个或更少相邻黑色像素的所有黑色像素转换为白色来消除这些线条。

对我来说似乎有点复杂,因为我是计算机视觉和opencv的初学者。
任何帮助将不胜感激,谢谢。


2
腐蚀操作首先去除最细的部分...如果你仔细观察,就会发现这一点。这些线条的粗细与您的文本差不多 - 如果您将它们腐蚀/膨胀掉,您的文本也将消失。通常,您首先进行腐蚀以摆脱微小的东西,然后再进行膨胀以使幸存者变得更厚...您可以反过来使用它们。为什么? - Patrick Artner
1
尽管图像已被破坏,您是否尝试将其通过OCR运行以检查结果? - Wayne Phipps
@PatrickArtner 我先尝试了膨胀再腐蚀,也尝试了先腐蚀再膨胀,但都没有成功。 - test
@WaynePhipps 是的,我尝试过了,但是什么也没有,输出为空。 - test
2个回答

17
检测这样的线条是 路径开启 的发明目的。 DIPlib 有其实现(声明:我在那里实现了它)。作为替代方案,您可以尝试使用文章作者提供的实现。该实现没有我以下所使用的"约束"模式

下面是一个快速演示如何使用它:

import diplib as dip
import matplotlib.pyplot as pp

img = 1 - pp.imread('/home/cris/tmp/DWRTF.png')
lines = dip.PathOpening(img, length=300, mode={'constrained'})

这里我们首先翻转图像,因为这样做会使后面的其他事情更容易。如果不进行翻转,请使用路径关闭代替。这是lines图像:

lines

接下来,我们减去这些线条。通过进行少量区域开放,可以消除由路径开放过滤出的线条的几个孤立像素:

text = img - lines
text = dip.AreaOpening(text, filterSize=5)

text

现在,我们已经在文本中留下了空隙。填补这些空缺并不容易。这里是一个快速而简单的尝试,您可以将其用作起点:

lines = lines > 0.5
text = text > 0.5
lines -= dip.BinaryPropagation(text, lines, connectivity=-1, iterations=3)
img[lines] = 0

最终结果


1
@test:“Image is not scalar” 表示该图像具有多个通道,但目前形态学函数仅允许标量(单通道)图像。我猜您有一张 RGB 图像。您应该将其转换为灰度图像,例如通过 dip.ColorSpaceManager.Convert(img, 'gray') 进行转换。 - Cris Luengo
抱歉打扰了,但我遇到了这个错误:RuntimeError: Image's number of tensor elements and color space are inconsistent in function: void dip::ColorSpaceManager::Convert(const dip::Image&, dip::Image&, const String&) const (/home/hani/cert/pydip/diplib/src/color/color.cpp at line number 234) - test
1
@test:执行img = dip.Image(img)。现在img.TensorElements()返回什么?img.ColorSpace()返回什么?也许你有3个张量元素(==通道),但颜色空间是一个空字符串?如果是这样,请执行img.SetColorSpace('RGB'),然后您就可以转换为灰度。另一种选择是执行img=img.TensorElement(0)来提取第一个通道。--显然,这个区域还没有经过用户测试。 :)我会研究如何改进可用性。感谢您指出这一点! - Cris Luengo
2
@test:棒极了!您应该能够直接在PyDIP中使用OpenCV读取的图像,无需先保存它。只需要使用OpenCV的“imread”而不是pyplot的“imread”即可。 - Cris Luengo
感谢您将这个库开源,再次感谢您解释如何使用它! - test
显示剩余2条评论

7

您可以使用来自opencv的createLineSegmentDetector()函数来实现此操作。

import cv2

#Read gray image
img = cv2.imread("lines.png",0)

#Create default parametrization LSD
lsd = cv2.createLineSegmentDetector(0)

#Detect lines in the image
lines = lsd.detect(img)[0] #Position 0 of the returned tuple are the detected lines

#Draw the detected lines
drawn_img = lsd.drawSegments(img,lines)

#Save the image with the detected lines
cv2.imwrite('lsdsaved.png', drawn_img)

在此输入图片描述
代码的下一部分将仅删除长度超过50像素的行:

for element in lines:

  #If the length of the line is more than 50, then draw a white line on it
  if (abs(int(element[0][0]) - int(element[0][2])) > 50 or abs(int(element[0][1]) - int(element[0][3])) > 50): 

    #Draw the white line
    cv2.line(img, (int(element[0][0]), int(element[0][1])), (int(element[0][2]), int(element[0][3])), (255, 255, 255), 12)

#Save the final image
cv2.imwrite('removedzz.png', img)

enter image description here

好的,目前的图片可能无法完美运作,但是对于不同的图片来说,它可能会得到更好的结果。您可以调整需要去除的线条的长度,以及绘制代替被去除线条的白线的粗细。
我希望这可以帮到您。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接