如何从图像中去除水平和垂直线条

11

我有一张图片,上面有一段文字写在螺旋笔记纸上。纸上有水平线条。我想从图像中去掉这些水平线条。

在搜索过程中,我发现了一种解决方案:使用形态学运算提取水平和垂直线条 该解决方案是用C++编写的,所以我将其转换为Python。它在该解决方案中提供的示例图像上效果很好,但是对于我的图像似乎不起作用。

在我的图像上运行它时,我得到以下结果:

原始图片

处理后的图片

以下是我从C++翻译成Python的代码:

 #cpp code converted from     http://docs.opencv.org/3.2.0/d1/dee/tutorial_moprh_lines_detection.html

import cv2
import numpy as np

img = cv2.imread("original.jpg")
img=cv2.cvtColor(img,cv2.COLOR_BGR2GRAY)

img = cv2.bitwise_not(img)
th2 = cv2.adaptiveThreshold(img,255, cv2.ADAPTIVE_THRESH_MEAN_C,cv2.THRESH_BINARY,15,-2)
cv2.imshow("th2", th2)
cv2.imwrite("th2.jpg", th2)
cv2.waitKey(0)
cv2.destroyAllWindows()

horizontal = th2
vertical = th2
rows,cols = horizontal.shape
horizontalsize = cols / 30
horizontalStructure = cv2.getStructuringElement(cv2.MORPH_RECT, (horizontalsize,1))
horizontal = cv2.erode(horizontal, horizontalStructure, (-1, -1))
horizontal = cv2.dilate(horizontal, horizontalStructure, (-1, -1))
cv2.imshow("horizontal", horizontal)
cv2.imwrite("horizontal.jpg", horizontal)
cv2.waitKey(0)
cv2.destroyAllWindows()

verticalsize = rows / 30
verticalStructure = cv2.getStructuringElement(cv2.MORPH_RECT, (1, verticalsize))
vertical = cv2.erode(vertical, verticalStructure, (-1, -1))
vertical = cv2.dilate(vertical, verticalStructure, (-1, -1))
cv2.imshow("vertical", vertical)
cv2.imwrite("vertical.jpg", vertical)
cv2.waitKey(0)
cv2.destroyAllWindows()

vertical = cv2.bitwise_not(vertical)
cv2.imshow("vertical_bitwise_not", vertical)
cv2.imwrite("vertical_bitwise_not.jpg", vertical)
cv2.waitKey(0)
cv2.destroyAllWindows()

#step1
edges = cv2.adaptiveThreshold(vertical,255, cv2.ADAPTIVE_THRESH_MEAN_C,cv2.THRESH_BINARY,3,-2)
cv2.imshow("edges", edges)
cv2.imwrite("edges.jpg", edges)
cv2.waitKey(0)
cv2.destroyAllWindows()

#step2
kernel = np.ones((2, 2), dtype = "uint8")
dilated = cv2.dilate(edges, kernel)
cv2.imshow("dilated", dilated)
cv2.imwrite("dilated.jpg", dilated)
cv2.waitKey(0)
cv2.destroyAllWindows()

# step3
smooth = vertical.copy()

#step 4
smooth = cv2.blur(smooth, (4,4))
cv2.imshow("smooth", smooth)
cv2.imwrite("smooth.jpg", smooth)
cv2.waitKey(0)
cv2.destroyAllWindows()

#step 5
(rows, cols) = np.where(img == 0)
vertical[rows, cols] = smooth[rows, cols]

cv2.imshow("vertical_final", vertical)
cv2.imwrite("vertical_final.jpg", vertical)
cv2.waitKey(0)
cv2.destroyAllWindows()

我也尝试了ImageMagick来去掉线条,但效果仍不完全准确。

convert original -morphology close:3 "1x5: 0,1,1,1,0" original_im.jpg

1
你的线条似乎不够直。我建议从一侧到另一侧进行连续性检测。 - smttsp
有关如何实现这一点的任何指导吗? - Anthony
我以前没有实现过这样的东西,但我可以为您编写基本的伪代码。我敢打赌有更多更有效的方法可以做到。我假设您的输入数据始终是相似的,对吗? - smttsp
是的,大多数情况下它是用有线纸书写的。有时线条是水平的,有时则是垂直的。 - Anthony
1
我也会考虑查看概率霍夫变换,并删除特定长度和方向的线条。我会尝试在今晚稍后写出答案。 - rayryeng
显示剩余2条评论
1个回答

6
你的问题比你基于教程提供的案例更为复杂。使用这种方法,由于字符的水平部分有时会被视为线条,因此您将无法完全过滤行。
根据您的期望(您并没有明确说明),特别是您期望的准确性,您可能希望尝试查找字符而不是查找行。那样应该会提供更强的稳健性。
关于您的代码,在图像上找到水平线后添加几行代码(在verticalsize = rows / 30代码行之前),您可以得到一些结果。我在半个大小的图像上工作。 horizontalsize = int(cols/30)的结果 horizontalsize = int(cols/15)的结果 再次强调,在您的情况下,这种方法永远不会很准确。以下是代码片段:
#inverse the image, so that lines are black for masking
horizontal_inv = cv2.bitwise_not(horizontal)
#perform bitwise_and to mask the lines with provided mask
masked_img = cv2.bitwise_and(img, img, mask=horizontal_inv)
#reverse the image back to normal
masked_img_inv = cv2.bitwise_not(masked_img)
cv2.imshow("masked img", masked_img_inv)
cv2.imwrite("result2.jpg", masked_img_inv)
cv2.waitKey(0)
cv2.destroyAllWindows()

如果我提供的图片还算满意,可以尝试调整horizontalsize。由于getStructuringElement函数期望的是整型数据,所以我使用了int转换:horizontalsize = int(cols / 30)

您也可以对结果进行一些平滑和形态学处理,这样字符会更加清晰易读。


1
现在我给了它更多的时间(我以为你可能会接受形态提供的结果),要检索字符将会非常困难。我想你可以尝试使用机器学习分别检测每个字符,但这似乎是一个巨大的过度设计。我过滤行的另一个想法是尝试找到轮廓,然后根据其长度进行过滤。伪代码大致如下:a)形态学闭合b)垂直Sobel c)查找轮廓d)根据其长度进行过滤。 - Michał Gacka
所以,这段代码可以处理钢琴音符图像。但是它无法处理像这些图片一样的图像...图片1 图片2 图片3我该如何在保留数字/字符的同时从这些图片中删除水平线? - lucians
@m3h0w 我使用了这段代码,结果很差。请看一下 - lucians
在代码的第二部分,有你上面指出的那个“your”。如果你在谈论codeshare的话,那么它不可用......我看不到它。 - lucians
@Link,你把代码放错了位置。我的回答明确指出应该在“verticalsize = rows / 30”这行代码之前注入代码。请不要浪费别人的时间为你编写代码,而是深入了解你试图使用的代码实际上在做什么。 - Michał Gacka
显示剩余6条评论

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