以编程方式删除图像中的所有线条和边框(保留文本)的方法是什么?

win*_*ind 14 opencv tesseract image imagemagick image-processing

我正在尝试使用Tesseract OCR从图像中提取文本.目前,使用原始输入图像(如下所示),输出质量很差(约50%).但是当我尝试删除输入图像中的所有线条和边框(使用photoshop)时,输出提高了很多(~90%).那么有没有办法以编程方式删除图像中的所有线条和边框(保留文本)(使用OpenCV,Image magick,..)?

原始图片: 原始图像

期待图片: 期待形象

nat*_*ncy 20

由于没有人发布完整的 OpenCV 解决方案,这里有一个简单的方法

  1. 获取二值图像。加载图像,转换为灰度,以及大津阈值

  2. 删除水平线。我们创建一个水平形状的内核,cv2.getStructuringElement然后找到轮廓并删除线条cv2.drawContours

  3. 去除垂直线。我们执行相同的操作,但使用垂直形状的内核


加载图像,转换为灰度,然后大津阈值得到二值图像

image = cv2.imread('1.png')
result = image.copy()
gray = cv2.cvtColor(image,cv2.COLOR_BGR2GRAY)
thresh = cv2.threshold(gray, 0, 255, cv2.THRESH_BINARY_INV + cv2.THRESH_OTSU)[1]
Run Code Online (Sandbox Code Playgroud)

在此处输入图片说明

现在我们创建一个水平内核来检测水平线cv2.getStructuringElement()并找到轮廓cv2.findContours()。为了去除水平线,我们使用cv2.drawContours()白色填充每个水平轮廓。这有效地“擦除”了水平线。这是检测到的绿色水平线

# Remove horizontal lines
horizontal_kernel = cv2.getStructuringElement(cv2.MORPH_RECT, (40,1))
remove_horizontal = cv2.morphologyEx(thresh, cv2.MORPH_OPEN, horizontal_kernel, iterations=2)
cnts = cv2.findContours(remove_horizontal, cv2.RETR_EXTERNAL, cv2.CHAIN_APPROX_SIMPLE)
cnts = cnts[0] if len(cnts) == 2 else cnts[1]
for c in cnts:
    cv2.drawContours(result, [c], -1, (255,255,255), 5)
Run Code Online (Sandbox Code Playgroud)

在此处输入图片说明

类似地,我们创建一个垂直内核来去除垂直线,找到轮廓,并用白色填充每个垂直轮廓。这是检测到的垂直线以绿色突出显示

# Remove vertical lines
vertical_kernel = cv2.getStructuringElement(cv2.MORPH_RECT, (1,40))
remove_vertical = cv2.morphologyEx(thresh, cv2.MORPH_OPEN, vertical_kernel, iterations=2)
cnts = cv2.findContours(remove_vertical, cv2.RETR_EXTERNAL, cv2.CHAIN_APPROX_SIMPLE)
cnts = cnts[0] if len(cnts) == 2 else cnts[1]
for c in cnts:
    cv2.drawContours(result, [c], -1, (255,255,255), 5)
Run Code Online (Sandbox Code Playgroud)

在此处输入图片说明

用白色填充水平线和垂直线后,这是我们的结果

在此处输入图片说明


注意:根据图像,您可能需要修改内核大小。例如,要捕获更长的水平线,可能需要将水平内核从(40, 1)增加到 say (80, 1)。如果你想检测更粗的水平线,那么你可以增加内核的宽度来说(80, 2). 此外,您可以在执行cv2.morphologyEx(). 同样,您可以修改垂直内核以检测更多或更少的垂直线。增加或减少内核大小时需要权衡,因为您可能会捕获更多或更少的行。同样,这一切都取决于输入图像

完整代码

import cv2

image = cv2.imread('1.png')
result = image.copy()
gray = cv2.cvtColor(image,cv2.COLOR_BGR2GRAY)
thresh = cv2.threshold(gray, 0, 255, cv2.THRESH_BINARY_INV + cv2.THRESH_OTSU)[1]

# Remove horizontal lines
horizontal_kernel = cv2.getStructuringElement(cv2.MORPH_RECT, (40,1))
remove_horizontal = cv2.morphologyEx(thresh, cv2.MORPH_OPEN, horizontal_kernel, iterations=2)
cnts = cv2.findContours(remove_horizontal, cv2.RETR_EXTERNAL, cv2.CHAIN_APPROX_SIMPLE)
cnts = cnts[0] if len(cnts) == 2 else cnts[1]
for c in cnts:
    cv2.drawContours(result, [c], -1, (255,255,255), 5)

# Remove vertical lines
vertical_kernel = cv2.getStructuringElement(cv2.MORPH_RECT, (1,40))
remove_vertical = cv2.morphologyEx(thresh, cv2.MORPH_OPEN, vertical_kernel, iterations=2)
cnts = cv2.findContours(remove_vertical, cv2.RETR_EXTERNAL, cv2.CHAIN_APPROX_SIMPLE)
cnts = cnts[0] if len(cnts) == 2 else cnts[1]
for c in cnts:
    cv2.drawContours(result, [c], -1, (255,255,255), 5)

cv2.imshow('thresh', thresh)
cv2.imshow('result', result)
cv2.imwrite('result.png', result)
cv2.waitKey()
Run Code Online (Sandbox Code Playgroud)


Mar*_*ell 14

不是使用OpenCV,而只是终端中的一行ImageMagick,但它可以让你知道如何在OpenCV中做到这一点.ImageMagick安装在大多数Linux发行版上,可用于OSX和Windows.

该概念的关键是创建一个新图像,其中每个像素设置为其左侧100个相邻像素的中值和右侧100个相邻像素的中值.这样,具有许多黑色水平邻居(即水平黑线)的像素在输出图像中将是白色的.然后在垂直方向上应用相同的处理以去除垂直线.

您在终端中键入的命令将是:

convert input.png                                                 \
   \( -clone 0 -threshold 50% -negate -statistic median 200x1 \)  \
   -compose lighten -composite                                    \
   \( -clone 0 -threshold 50% -negate -statistic median 1x200 \)  \
   -composite result.png
Run Code Online (Sandbox Code Playgroud)

第一行表示加载原始图像.

第二行开始一些"旁边处理",其复制原始图像,对其进行阈值化并将其反转,然后计算任一侧的所有相邻像素100的中值.

第三行然后获取第二行的结果并将其合成在原始图像上,选择每个位置处的较亮像素 - 即我的水平线掩模已变白的像素.

接下来的两行再次做同样的事情,但垂直方向为垂直线.

结果是这样的:

在此输入图像描述

如果我与原始图像不同,就像这样,我可以看到它做了什么:

convert input.png result.png -compose difference -composite diff.png
Run Code Online (Sandbox Code Playgroud)

在此输入图像描述

我想,如果你想删除更多的线条,你可以实际模糊差异图像并将其应用到原始图像.当然,您也可以使用滤波器长度和阈值.


lui*_*izv 12

使用ImageMagick有更好的方法.

识别线形并将其移除

ImageMagick有一个简洁的功能,称为形状形态.您可以使用它来识别表格线等形状并将其删除.

一个班轮

convert in.png                              \
-type Grayscale                             \
-negate                                     \
-define morphology:compose=darken           \
-morphology Thinning 'Rectangle:1x80+0+0<'  \
-negate                                     \
out.png
Run Code Online (Sandbox Code Playgroud)

说明

  • convert in.png:加载图片.
  • -type灰度:确保ImageMagick知道它是灰度图像.
  • -negate:反转图像颜色层(已通过设置灰度正确调整).线条和字符将为白色和背景黑色.
  • -define morphology:compose = darken:定义由形态识别的区域将变暗.
  • -morphology Thinning'矩形:1x80 + 0 + 0 <'定义1px乘80px矩形内核,用于识别线形.只有当这个内核适合白色的形状(记住我们否定颜色)这个大或大,它才会变暗.所述<标志允许它转动.
  • -negate:第二次反转颜色.现在角色将再次变黑,背景将变为白色.
  • out.png:要生成的输出文件.

结果图像

申请后

convert in.png -type Grayscale -negate -define morphology:compose=darken -morphology Thinning 'Rectangle:1x80+0+0<' -negate out.png
Run Code Online (Sandbox Code Playgroud)

这是输出图像:

应用一个衬管后的输出图像

意见

  • 您应该选择比较大字符大小更大的矩形内核大小,以确保矩形不适合字符.
  • 一些小的虚线和小的表格单元格仍然存在,但这是因为它们小于80像素.
  • 这种技术的优点在于它比其他用户在这里提出的中值像素色差方法更好地保留了字符,尽管杂乱无章,但它仍然有一个非常好的结果去除表格线.


Abh*_*jee 7

面临同样的问题.而且我觉得更合乎逻辑的解决方案可能是(参考:提取表格边框)

//assuming, b_w is the binary image
inv = 255 - b_w    
horizontal_img = new_img
vertical_img = new_img

kernel = cv2.getStructuringElement(cv2.MORPH_RECT, (100,1))
horizontal_img = cv2.erode(horizontal_img, kernel, iterations=1)
horizontal_img = cv2.dilate(horizontal_img, kernel, iterations=1)


kernel = cv2.getStructuringElement(cv2.MORPH_RECT, (1,100))
vertical_img = cv2.erode(vertical_img, kernel, iterations=1)
vertical_img = cv2.dilate(vertical_img, kernel, iterations=1)

mask_img = horizontal_img + vertical_img
no_border = np.bitwise_or(b_w, mask_img)
Run Code Online (Sandbox Code Playgroud)


Ani*_*mik 5

您可以使用 Sobel/Laplacian/Canny 的边缘检测算法,并使用霍夫变换来识别 OpenCV 中的线,并将它们涂成白色以删除线:

laplacian = cv2.Laplacian(img,cv2.CV_8UC1) # Laplacian OR
edges = cv2.Canny(img,80,10,apertureSize = 3) # canny Edge OR
# Output dtype = cv2.CV_8U # Sobel
sobelx8u = cv2.Sobel(img,cv2.CV_8U,1,0,ksize=5)
# Output dtype = cv2.CV_64F. Then take its absolute and convert to cv2.CV_8U
sobelx64f = cv2.Sobel(img,cv2.CV_64F,1,0,ksize=5)
abs_sobel64f = np.absolute(sobelx64f)
sobel_8u = np.uint8(abs_sobel64f)

# Hough's Probabilistic Line Transform 
minLineLength = 900
maxLineGap = 100
lines = cv2.HoughLinesP(edges,1,np.pi/180,100,minLineLength,maxLineGap)
for line in lines:
    for x1,y1,x2,y2 in line:
        cv2.line(img,(x1,y1),(x2,y2),(255,255,255),2)

cv2.imwrite('houghlines.jpg',img)
Run Code Online (Sandbox Code Playgroud)