在OpenCV中从图像中删除选定的元素

时间:2018-08-28 08:00:11

标签: python opencv image-processing tesseract

我有this张图片,其中有一些表格,我想从图片中删除表格结构,以便它可以更有效地与Tesseract一起使用。我使用以下代码在表格(和单个单元格)周围创建了一个边界,以便可以将其删除。

img =cv2.imread('bfir.jpg')
gray = cv2.cvtColor(img,cv2.COLOR_BGR2GRAY)
edges = cv2.Canny(gray,50,150,apertureSize = 3)
img1 = np.ones(img.shape, dtype=np.uint8)*255
ret,thresh = cv2.threshold(gray,127,255,1)
(_,contours,h) = cv2.findContours(thresh,1,2)

for cnt in contours:
    approx = cv2.approxPolyDP(cnt,0.01*cv2.arcLength(cnt,True),True)
    if len(approx)==4:
        cv2.drawContours(img1,[cnt],0,(0,255,0),2)

这会在表格周围绘制绿线,如this图片。

接下来,我尝试使用cv2.subtract方法从图像中减去表格,就像这样。

final_img = cv2.subtract(img1, img)

但是,这并没有按我预期的那样工作,并且给了我一张仍在桌子上的灰度图像。 Link

虽然我只想删除表中的B&W中的原始图像。我是第一次使用OpenCV,所以我不知道自己在做什么错。很抱歉,我的帖子很长,但是如果有人可以帮忙解决这个问题,或者只是为我指出正确的方向删除表,将不胜感激。

编辑: 正如RobAu所建议的那样,它也可以首先简单地以白色绘制轮廓,但是我不知道如何在不丢失预处理阶段其余数据的情况下做到这一点。

3 个答案:

答案 0 :(得分:1)

  

作为对这个问题的评论的参考,这是一个代码示例,该代码定位矩形并为每个矩形创建新图像,这是尝试为碎纸图像创建单个图像的尝试。某些值需要更改才能找到大小正确的矩形

     

还有一些用于跟踪图像大小的代码,该代码由我编写的内容的50%和stackoverflow帮助的50%组成。

import cv2
import numpy as np

fileName = ['9','8','7','6','5','4','3','2','1','0']

img = cv2.imread('#YOUR IMAGE#')

gray = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY)
gray = cv2.bilateralFilter(gray, 11, 17, 17)

kernel = np.ones((5,5),np.uint8)
erosion = cv2.erode(gray,kernel,iterations = 2)
kernel = np.ones((4,4),np.uint8)
dilation = cv2.dilate(erosion,kernel,iterations = 2)

edged = cv2.Canny(dilation, 30, 200)

_, contours, hierarchy = cv2.findContours(edged, cv2.RETR_TREE, cv2.CHAIN_APPROX_SIMPLE)

rects = [cv2.boundingRect(cnt) for cnt in contours]
rects = sorted(rects,key=lambda  x:x[1],reverse=True)


i = -1
j = 1
y_old = 5000
x_old = 5000
for rect in rects:
    x,y,w,h = rect
    area = w * h
    print('width: %d and height: %d' %(w,h))
    if   w > 50 and h > 500:
        print('abs:')
        print(abs(x_old - x))
        if abs(x_old - x) > 0:
            print('writing')
            x_old = x
            x,y,w,h = rect

            out = img[y+10:y+h-10,x+10:x+w-10]
            cv2.imwrite('assets/newImage' + fileName[i] + '.jpg', out)

            j+=1
        if (y_old - y) > 1000:
            i += 1
            y_old = y

答案 1 :(得分:1)

您可以尝试简单地覆盖代表边框的单元格。可以通过创建遮罩图像,然后将其用作覆盖原始像素的参考来完成。

这可以通过以下方式完成:

mask_image = np.zeros(img.shape[0:2], np.uint8)    
cv2.drawContours(mask_image, contours, -1, color=255, thickness=2)
border_points = np.array(np.where(mask_image == 255)).transpose()
background = [0, 0, 0] # Change this to the colour you want
for point in border_points :
    img[point[0], point[1]] = background

更新

您可以使用已经为遮罩创建的3通道,但这会使算法稍微复杂一些。建议的蒙版图像更适合该任务,但我将尝试使其适应您的代码:

# Create your mask image as usual...
border_points = np.array(np.where(img1[:,:,1] == 255)).transpose() # Only look at channel 2
background = [0, 0, 0] # Change this to the colour you want
for point in border_points :
    img[point[0], point[1]] = background

更新以按照@RobAu的建议进行操作(比我以前的方法要快):

line_thickness = 3  # Change this value until it looks the best.
cv2.drawContours(img, contours, -1, color=(0,0,0), thickness=line_thickness )

请注意,我没有测试此代码。因此,可能需要进一步摆弄。

答案 2 :(得分:0)

尽管如此,给定的输入图像链接仍无法正常工作,所以我显然不知道以下是您所要的内容,我从您的问题中学到了什么,当我在工作时,从给定的表中删除了表格结构行形象,我想分享我所学到的东西,供将来的读者使用。

我按照json_encode中提供的步骤删除了这些行。 但这只删除了水平线。当我尝试删除垂直线时,结果图像仅具有垂直线。表格中的文字不存在。

然后,我遇到了您的问题,并在问题中看到了final_img = cv2.subtract(img1, img)。尝试了,效果很好。

这是我遵循的步骤:

# Load the image
src = cv.imread(argv[0], cv.IMREAD_COLOR)
# Check if image is loaded fine
if src is None:
    print ('Error opening image: ' + argv[0])
    return -1
# Show source image
cv.imshow("src", src)
# [load_image]
# [gray]
# Transform source image to gray if it is not already
if len(src.shape) != 2:
    gray = cv.cvtColor(src, cv.COLOR_BGR2GRAY)
else:
    gray = src
# Show gray image
# show_wait_destroy("gray", gray)
# [gray]
# [bin]
# Apply adaptiveThreshold at the bitwise_not of gray, notice the ~ symbol
gray = cv.bitwise_not(gray)
bw = cv.adaptiveThreshold(gray, 255, cv.ADAPTIVE_THRESH_MEAN_C, \
                            cv.THRESH_BINARY, 15, -2)
# Show binary image
# show_wait_destroy("binary", bw)
# [bin]
# [init]
# Create the images that will use to extract the horizontal and vertical lines
horizontal = np.copy(bw)
vertical = np.copy(bw)

# [horiz]
# [vert]
# Specify size on vertical axis
rows = vertical.shape[0]
verticalsize = rows / 10
# Create structure element for extracting vertical lines through morphology operations
verticalStructure = cv.getStructuringElement(cv.MORPH_RECT, (1, verticalsize))
# Apply morphology operations
vertical = cv.erode(vertical, verticalStructure)
vertical = cv.dilate(vertical, verticalStructure)

# [init]
# [horiz]
# Specify size on horizontal axis
cols = horizontal.shape[1]
horizontal_size = cols / 30
# Create structure element for extracting horizontal lines through morphology operations
horizontalStructure = cv.getStructuringElement(cv.MORPH_RECT, (horizontal_size, 1))
# Apply morphology operations
horizontal = cv.erode(horizontal, horizontalStructure)
horizontal = cv.dilate(horizontal, horizontalStructure)
lines_removed = cv.subtract(gray, vertical + horizontal)

show_wait_destroy("lines_removed", ~lines_removed)

输入:

opencv documentation

输出:

Input image

我从源代码更改的几件事:

  1. verticalsize = rows / 10,在这里,我不知道数字10的含义。在文档中,使用了30。我用10得到了更好的结果。我猜想,除法数越少,结构元素就越大,在这里,因为我们的目标是直线,减少了工作量。
  2. 在文档中,垂直线在水平线之后处理。我撤消了订单
  3. 我将参数交换为cv2.substract()。我使用了cv2.subtract(img, img1)