【问题标题】:Remove the selected elements from the image in OpenCV从 OpenCV 中的图像中删除选定的元素
【发布时间】:2018-08-28 08:00:11
【问题描述】:

我有带有表格的 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

虽然我只想要删除表格的黑白原始图像。我是第一次使用 OpenCV,所以我不知道我做错了什么,我很抱歉发了这么长的帖子,但是如果有人可以帮助我解决这个问题,或者只是为我指出正确的方向删除表,将非常感谢。

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

【问题讨论】:

  • 如果您有兴趣,我有一个代码可以检测矩形并将它们制作成自己的图片
  • 这会很有帮助,但我想完全摆脱这里的表格,而不是把它变成单独的图像。无论如何请分享它,我可以尝试修改它以满足我的需要:)
  • 我会将其发布为答案,但您不必将其标记为已回答,主要用于格式化。我对开放式简历还不太熟悉,所以很遗憾这是我能做的最好的事情
  • 为什么不在原图上画出白色的轮廓呢?
  • 我绝对可以做到,但我承认到目前为止的大部分工作都是从这里完成的(就像一个真正的开发人员一样),恐怕我不完全了解如何绘制轮廓而不删除剩余数据,就像现在一样 (example)。 (我猜它与阈值有关,即“模糊”背景数据以绘制轮廓)

标签: python opencv image-processing tesseract


【解决方案1】:

即使给定的输入图像链接不起作用,所以我显然不知道以下是您所要求的,我从您的问题中学到了一些东西,当我在工作时,从给定的表结构行中删除图片,我喜欢分享我学到的东西,以供未来的读者阅读。

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

然后我遇到了您的问题并在问题中看到了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)

输入:

输出:

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

  1. verticalsize = rows / 10,这里,我不明白10这个数字的意义。在文档中,使用了 30。我用 10 得到了更好的结果。我猜,除数越少,结构元素 & 这里就越大,因为我们的目标是直线,所以减少数量是可行的。
  2. 在文档中,垂直线在水平线之后处理。我把顺序颠倒了
  3. 我将参数交换为cv2.substract()。我用了cv2.subtract(img, img1)

【讨论】:

    【解决方案2】:

    您可以尝试简单地覆盖代表边界的单元格。这可以通过创建一个蒙版图像来完成,然后将其用作覆盖原始像素位置的参考。

    这可以通过:

    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 )
    

    请注意我没有测试这段代码。所以它可能需要一些进一步的摆弄。

    【讨论】:

    • 嗨!感谢您的答复。我不能使用第二张图片(img1,带有绿色边框)本身作为蒙版吗?
    • 你可以,但是你必须稍微调整我提供的算法。我基本上和你做的一样,除了我创建一个单通道图像。我会尝试更新我的答案让你这样做。
    • 感谢您的更新和解释。我只是问,因为第一个解决方案一直给我错误'IndexError:索引 5549 超出轴 0 的范围,大小为 5092',即使在我尝试更改值之后也是如此。第二种解决方案至少似乎有效,但我仍然必须做一些愚蠢的事情,因为我仍然得到原始图像本身或 img1 作为输出。
    • 对于第一个错误,xy 轴似乎混淆了。尝试在np.array(np.where(mask_image == 255)) 之后添加.transpose()。如果这不起作用,请在每一步之后检查图像矩阵的shape 属性,以确保它们都相同。对于您的第二个问题,您确定 for loop 正在运行吗?
    • .transpose() 方法有效!但它给出的输出类似于this。至于第二种解决方案,我调试了我的程序,它确实进入了循环。
    【解决方案3】:

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

    还有一些用于跟踪图像大小的代码,这些代码由我编写的 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
    

    【讨论】:

    • 感谢您的努力,但我认为这不会有太大帮助。还是谢谢!
    猜你喜欢
    • 2011-01-11
    • 1970-01-01
    • 2023-03-05
    • 1970-01-01
    • 2020-01-31
    • 1970-01-01
    • 1970-01-01
    • 2019-05-04
    • 1970-01-01
    相关资源
    最近更新 更多