opencv 提取表格

该文章介绍了使用OpenCV库进行表格轮廓检测的方法,包括图像预处理、Canny边缘检测、轮廓筛选、最小外接矩形以及透视变换等步骤,以识别和切割图像中的表格。代码示例展示了如何处理单个和多个表格,并提供了GitHub项目链接用于进一步研究。
摘要由CSDN通过智能技术生成

原理

轮廓检测:寻找连续的像素闭合区域,通过轮廓大小和形状判断是否为单个单元格或者整个表格区域。

结构

表格由横线、纵线构成,线与线交叉形成单元格

效果

在这里插入图片描述

步骤

1、读取图像

img = cv2.imread(imagePath)
gray = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY)

2、识别表格轮廓(canny算法)

canny = cv2.Canny(gray, 200, 255)
# 只要最外层轮廓
_, contours, HIERARCHY = cv2.findContours(canny, cv2.RETR_EXTERNAL, cv2.CHAIN_APPROX_SIMPLE)   
# 去除大小不合适的轮廓
candidate_table = [cnt for cnt in contours if cv2.contourArea(cnt) > = gray.shape[0] * gray.shape[1] * 0.01 ]
 

3、对表格顶点排序


def get_sorted_rect(rect):
    '''
    获取矩阵排序的四个坐标,方便透视变换使用
    rect包含坐标点为负数时,left_rect包含三个 right_rect包含1个坐标点,或者反之
    @param rect:
    @return:按照左上 右上 右下 左下排列返回

    '''
    try:
        mid_x = (max([x[1] for x in rect]) - min([x[1] for x in rect])) * 0.5 + min([x[1] for x in rect])  # 中间点坐标
        left_rect = [x for x in rect if x[1] < mid_x]
        left_rect.sort(key=lambda x: (x[0], x[1]))
        right_rect = [x for x in rect if x[1] > mid_x]
        right_rect.sort(key=lambda x: (x[0], x[1]))
        sorted_rect = left_rect[0], left_rect[1], right_rect[1], right_rect[0]  # 左上 右上 右下 左下
    except:
        # np.array_equal(order_points(rect), sorted_rect):
        sorted_rect = order_points(rect)

    return sorted_rect


for i in range(len(candidate_table)):
    cnt = candidate_table[i]
    area = cv2.contourArea(cnt)

    # 找到最小的矩形,该矩形可能有方向
    rect = cv2.minAreaRect(cnt)
      # box是四个点的坐标
    box = cv2.boxPoints(rect)  # boxPoints返回四个点顺序:右下→左下→左上→右上(实际上不定
    box = np.int0(box)
    sorted_box = get_sorted_rect(box)  # 左上 右上 右下 左下
    result = [sorted_box[2], sorted_box[3], sorted_box[0], sorted_box[1]]  # 右下 左下 左上 右上

    result = [x.tolist() for x in result]
    table.append(result)

4、切割表格


def get_standard_table_image(gray, table):
    '''
    获取表格图片
    Args:
        gray:
        table:

    Returns:

    '''

    sorted_rect = get_sorted_rect(table)
    gray_z = perTran(gray, sorted_rect)
    binary_z = cv2.adaptiveThreshold(~gray_z, 255, cv2.ADAPTIVE_THRESH_GAUSSIAN_C,
                                     cv2.THRESH_BINARY, 15, -5)

    return gray_z, binary_z


def perTran(image, rect):
    '''
    做透视变换
    image 图像
    rect  四个顶点位置:左上 右上 右下 左下
    '''

    tl, tr, br, bl = rect  # 左下 右下  左上 右上 || topleft topright 左上 右上 右下 左下
    # 计算宽度
    widthA = np.sqrt(((br[0] - bl[0]) ** 2) + ((br[1] - bl[1]) ** 2))
    widthB = np.sqrt(((tr[0] - tl[0]) ** 2) + ((tr[1] - tl[1]) ** 2))
    maxWidth = max(int(widthA), int(widthB))
    # 计算高度
    heightA = np.sqrt(((tr[0] - br[0]) ** 2) + ((tr[1] - br[1]) ** 2))
    heightB = np.sqrt(((tl[0] - bl[0]) ** 2) + ((tl[1] - bl[1]) ** 2))
    maxHeight = max(int(heightA), int(heightB))
    # 定义变换后新图像的尺寸
    dst = np.array([[0, 0], [maxWidth - 1, 0], [maxWidth - 1, maxHeight - 1],
                    [0, maxHeight - 1]], dtype='float32')
    # 变换矩阵
    rect = np.array(rect, dtype=np.float32)
    dst = np.array(dst, dtype=np.float32)
    M = cv2.getPerspectiveTransform(rect, dst)
    # 透视变换
    warped = cv2.warpPerspective(image, M, (maxWidth, maxHeight))
    return warped




def get_muti_tables_images( gray, tables):
    '''
    获取多个table结果


    @param gray: 灰度图
    @param table:表格四个点坐标

    @return:返回解析结果
    '''
    gray_z_list = []
    binary_z_list = [] 
    for index,table in enumerate(tables):
        gray_z, binary_z = get_standard_table_image(gray, table)
        gray_z_list.append(gray_z)
        binary_z_list.append(binary_z)

    return gray_z_list,binary_z_list

get_muti_tables_images( gray, tables):

完整代码

见github项目:https://github.com/dirac472/tableOCR

优化

待补充无框线表格的识别

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值