为了账号安全,请及时绑定邮箱和手机立即绑定

如何检测和删除 opencv python 中框架表单的边框?

如何检测和删除 opencv python 中框架表单的边框?

呼唤远方 2023-03-22 10:55:21
数据说明数据集包含以图片形式拍摄的表格(因此数据质量差异很大)所有表格都遵循相同的模板这是带有边框的表单元素的示例:目的检测并因此移除(大约)图像周围的矩形边框或框架。挑战由于阴影效果等原因,边框的颜色可能不统一,并且可能包含符号或被符号部分打断。并非所有图像实际上首先都有边框(在这种情况下不需要删除任何内容)。参考其他人之前已在此链接上描述过该问题,并且已在 C++ 中提供了答案。由于我的语言不流利,我需要用 Python 来完成。引用的答案描述了以下步骤(因为我刚刚开始计算机视觉,所以我不确定它们的意思):计算图像的拉普拉斯算子计算水平和垂直投影评估两个方向的变化找到最大峰值,在梯度图像的一侧找到。
查看完整描述

2 回答

?
郎朗坤

TA贡献1921条经验 获得超9个赞

1 - 你将不得不对边界做出一些假设,如果它们存在 - 比如说它们不应该超过 20 像素或者说图像高度/宽度的 10%。查看您的数据,您将能够做出这个假设

现在我们将从图像中分离出这 20 像素的边界区域,并只在其中工作。

2 - 由于您的边框颜色不同,因此将图像转换为灰度。在灰度上工作将使生活变得轻松。如果你能把它关起来,那就更好了。

import cv2 
import numpy as np 
img = cv2.imread('input.png', 0) 
gray = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY)

3 - 由于您的图像边界可能会被符号部分打断 - 使用膨胀操作。如果存在统一边界或不存在边界 - 什么都不会发生。如果边界存在并被中断 - 扩张操作将使它统一。

以大小为5的矩阵为核

kernel = np.ones((5,5), np.uint8)    
img_dilated = cv2.dilate(gray, kernel, iterations=1)

您将需要尝试

  • 内核大小

  • 迭代次数

  • 膨胀后是否需要腐蚀操作。侵蚀与膨胀相反

4 - 现在让我们使用拉普拉斯算子找出是否存在任何边界。拉普拉斯算子是图像二阶空间导数的二维各向同性度量。图像的拉普拉斯算子突出了快速强度变化的区域,因此经常用于边缘检测。

laplacian = cv2.Laplacian(img_dilated,cv2.CV_64F)

在你的图像的拉普拉斯算子中,你会看到两条线代替你的边界。注意 - 您不需要使用单独的水平和垂直 sobel 运算符。拉普拉斯算子同时处理水平和垂直。拉普拉斯算子是二阶导数,而索贝尔是一阶导数。

5 - 现在您希望算法检测是否有任何双线。为此,我们使用霍夫变换。

# This returns an array of r and theta values 

lines = cv2.HoughLines(edges,1,np.pi/180, 200) 

  

# The below for loop runs till r and theta values  

# are in the range of the 2d array 

for r,theta in lines[0]: 

      

    # Stores the value of cos(theta) in a 

    a = np.cos(theta) 

  

    # Stores the value of sin(theta) in b 

    b = np.sin(theta) 

6 - 如果 Hough 变换检测到线条(检查上面的角度 theta 与具有一定公差的期望) - 这意味着你的边界存在。从图像中删除 20 像素的边框。


注意- 这只是让您入门的伪代码。现实世界的问题需要大量的定制工作和实验。


查看完整回答
反对 回复 2023-03-22
?
慕莱坞森

TA贡献1810条经验 获得超4个赞

我设法找到了一种对我有用的方法,尽管如果图像中有其他水平和垂直形状则它不起作用。


我使用的想法是简单地从边框是水平和垂直形状的假设开始,并从这些仅存在于边框中的假设出发(意味着图像本身没有垂直或水平线,这是一种拉伸,但我的用例有那个假设)。


这是我使用的代码:


# extract horizontal and vertical lines

only_box = extract_all_squares(box, kernel_length=7)

# build up a mask of the same size as the image

mask = np.zeros(box.shape, dtype='uint8')

# get contours of horizontal and vetical lines

contours, hierarchy = cv2.findContours(only_box, cv2.RETR_TREE, cv2.CHAIN_APPROX_SIMPLE)

# draw contours on mask

mask = cv2.drawContours(mask, contours, -1, (255, 255, 255), thickness=cv2.FILLED)

# threhold mask and image

ret, mask = cv2.threshold(mask, 20, 255, cv2.THRESH_BINARY)

ret, box = cv2.threshold(box, 20, 255, cv2.THRESH_BINARY)

# remove the bits we don't want

box[mask == 0] = 255

具有以下辅助功能


def extract_all_squares(image, kernel_length):

    """

    Binarizes image, keeping only vertical and horizontal lines

    hopefully, it'll help us detect squares

    Args:

        image: image (cropped around circonstances)

        kernel_length: length of kernel to use. Too long and you will catch everything,

            too short and you catch nothing

    Returns:

        image binarized and keeping only vertical and horizozntal lines

    """

    # thresholds image : anything beneath a certain value is set to zero

    (thresh, img_bin) = cv2.threshold(image, 128, 255, cv2.THRESH_BINARY_INV | cv2.THRESH_OTSU)


    # A vertical kernel of (1 X kernel_length), which will detect all the verticle lines from the image.

    vertical_ksize = (1, kernel_length)

    # Morphological operation to detect vertical lines from an image

    verticle_lines_img = extract_lines(img_bin, vertical_ksize)


    # A horizontal kernel of (kernel_length X 1), which will help to detect all the horizontal line from the image.

    horizontal_ksize = (kernel_length, 1)

    # Morphological operation to detect horizontal lines from an image

    horizontal_lines_img = extract_lines(img_bin, horizontal_ksize)

    img_final_bin = add_lines_together(verticle_lines_img, horizontal_lines_img)


    return img_final_bin



def extract_lines(image, ksize):

    """

    extract lines (horizontal or vertical, depending on ksize)

    Args:

        image: binarized image

        ksize: size of kernel to use. Possible values :

            horizontal_ksize = (kernel_length, 1)

            vertical_ksize = (1, kernel_length)

    Returns:

        lines from image (vertical or horizontal, depending on ksize)

    """

    kernel = cv2.getStructuringElement(cv2.MORPH_RECT, ksize)

    img_temp = cv2.erode(image, kernel, iterations=3)

    lines_img = cv2.dilate(img_temp, kernel, iterations=3)

    return lines_img



def add_lines_together(verticle_lines_img, horizontal_lines_img, alpha=0.5, beta=0.5):

    """

    extract lines (horizontal or vertical, depending on ksize)

    Args:

        verticle_lines_img: image with vertical lines

        horizontal_lines_img: image with horizontal lines

        alpha : weight of first image. Keep at 0.5 for balance

        beta : weight of second image. Keep at 0.5 for balance

            alpha and beta are weighting parameters, this will

            decide the quantity of an image to be added to make a new image

    Returns:

        image with an addition of both vertical and horizontal lines

    """


    # This function helps to add two image with specific weight parameter to get a third image as summation of two image.

    img_final_bin = cv2.addWeighted(verticle_lines_img, alpha, horizontal_lines_img, beta, 0.0)

    # A kernel of (3 X 3) nes.

    kernel = cv2.getStructuringElement(cv2.MORPH_RECT, (3, 3))

    # erodes boundaries of features, gets rid of some noise

    img_final_bin = cv2.erode(~img_final_bin, kernel, iterations=2)

    # further kill noise by thresholding

    (thresh, img_final_bin) = cv2.threshold(img_final_bin, 128, 255, cv2.THRESH_BINARY | cv2.THRESH_OTSU)

    return img_final_bin



查看完整回答
反对 回复 2023-03-22
  • 2 回答
  • 0 关注
  • 255 浏览
慕课专栏
更多

添加回答

举报

0/150
提交
取消
微信客服

购课补贴
联系客服咨询优惠详情

帮助反馈 APP下载

慕课网APP
您的移动学习伙伴

公众号

扫描二维码
关注慕课网微信公众号