基于图片中的表格检测与识别

news2024/9/22 5:33:57

1、项目介绍

本文将会使用Microsoft开源的表格检测模型table-transformer-detection来实现表格检测与入门。
以下将分三部分进行介绍:

  • 表格检测:检测图片或PDF文件中的表格所在的区域
  • 表格结构识别:对于检测后的表格区域,再详细识别表格的区域,即表格-的行、列,表头所在的位置,进一步得到单元格的位置
  • 表格数据提取: 在表格结构的基础上,借助OCR可得到每个单元格内的文本,从而获得整个表格数据

2、环境构建

2.1、服务配置

在这里插入图片描述

2.2、环境构建

conda create -n Microsoft python==3.8 pip==2.1.1
conda activate Microsoft 
pip install -r requirements.txt -i https://pypi.tuna.tsinghua.edu.cn/simple

模型下载

https://huggingface.co/microsoft/table-transformer-detection/tree/main

3、表格检测

检测图片或PDF文件中的表格所在的区域
部分代码如下:

from transformers import AutoImageProcessor, TableTransformerForObjectDetection
import torch
from PIL import Image

file_path = "./images/demo.jpg"
image = Image.open(file_path).convert("RGB")
file_name = file_path.split('/')[-1].split('.')[0]
......
inputs = image_processor(images=image, return_tensors="pt")
outputs = model(**inputs)

# convert outputs (bounding boxes and class logits) to COCO API
target_sizes = torch.tensor([image.size[::-1]])
results = image_processor.post_process_object_detection(outputs, threshold=0.9, target_sizes=target_sizes)[0]

i = 0
for score, label, box in zip(results["scores"], results["labels"], results["boxes"]):
    box = [round(i, 2) for i in box.tolist()]
    print(
        f"Detected {model.config.id2label[label.item()]} with confidence "
        f"{round(score.item(), 3)} at location {box}"
    )

    region = image.crop(box)  # 检测
    region.save(f'./images/{file_name}_{i}.png')
    i += 1

结果如下:

Could not find image processor class in the image processor config or the model config. Loading based on pattern matching with the model's feature extractor configuration.
The `max_size` parameter is deprecated and will be removed in v4.26. Please specify in `size['longest_edge'] instead`.
Detected table with confidence 0.998 at location [429.76, 217.22, 730.43, 441.51]
Detected table with confidence 0.997 at location [89.02, 215.52, 372.13, 440.98]

4、表格结构识别

部分代码如下:

from transformers import DetrFeatureExtractor, TableTransformerForObjectDetection
import torch
from PIL import Image



#使用 DetrFeatureExtractor
feature_extractor = DetrFeatureExtractor()

file_path = "./images/demo_1.png"
image = Image.open(file_path).convert("RGB")

# 对图像进行编码处理
encoding = feature_extractor(images=image, return_tensors="pt")
......
# 前向推理
with torch.no_grad():
    outputs = model(**encoding)

target_sizes = [image.size[::-1]]
results = feature_extractor.post_process_object_detection(outputs, threshold=0.6, target_sizes=target_sizes)[0]
print(results)

columns_box_list = [results['boxes'][i].tolist() for i in range(len(results['boxes'])) if results['labels'][i].item()==3]
for idx, box in enumerate(columns_box_list):
    print(idx)
    crop_image = image.crop(box)
    crop_image.save(f'header_{idx}.png')

结果如下:

{0: 'table', 1: 'table column', 2: 'table row', 3: 'table column header', 4: 'table projected row header', 5: 'table spanning cell'}
{'scores': tensor([0.9938, 0.9916, 0.9990, 0.9951, 0.9988, 0.9951, 0.9914, 0.9138, 0.9976,
        0.9996]), 'labels': tensor([2, 2, 1, 2, 1, 1, 2, 3, 1, 0]), 'boxes': tensor([[ 17.4817, 111.3828, 246.3970, 153.2920],
        [ 17.5167,  66.2340, 246.3717, 105.3657],
        [ 17.5555,  34.3023,  66.3797, 183.1057],
        [ 17.4001,  33.6988, 246.5164,  65.5520],
        [146.3985,  34.1393, 225.7526, 182.9028],
        [226.7389,  34.1763, 247.0905, 183.1046],
        [ 17.4087, 151.3451, 246.1476, 183.2057],
        [ 17.1707,  33.7522, 246.4425,  64.2807],
        [ 67.4367,  33.9697, 146.2301, 183.5033],
        [ 17.5343,  34.2323, 246.6799, 183.0271]])}

5、表格数据提取

部分代码如下:

#!/usr/bin/env python
# -*- coding: utf-8 -*-
......
def paddle_ocr(image_path):
    result = ocr.ocr(image_path, cls=True)
    ocr_result = []
    for idx in range(len(result)):
        res = result[idx]
        if res:
            for line in res:
                print(line)
                ocr_result.append(line[1][0])
    return "".join(ocr_result)


def table_detect(image_box, image_url):
    if not image_url:
        file_name = str(uuid4())
        image = Image.fromarray(image_box).convert('RGB')
    else:
        image_path = f"./images/{uuid4()}.png"
        file_name = image_path.split('/')[-1].split('.')[0]
        urlretrieve(image_url, image_path)
        image = Image.open(image_path).convert('RGB')
    inputs = image_processor(images=image, return_tensors="pt")
    outputs = detect_model(**inputs)
    # convert outputs (bounding boxes and class logits) to COCO API
    target_sizes = torch.tensor([image.size[::-1]])
    results = image_processor.post_process_object_detection(outputs, threshold=0.9, target_sizes=target_sizes)[0]

    i = 0
    output_images = []
    for score, label, box in zip(results["scores"], results["labels"], results["boxes"]):
        box = [round(i, 2) for i in box.tolist()]
        print(
            f"Detected {detect_model.config.id2label[label.item()]} with confidence "
            f"{round(score.item(), 3)} at location {box}"
        )

        region = image.crop(box)  # 检测
        output_image_path = f'./images/{file_name}_{i}.jpg'
        region.save(output_image_path)
        output_images.append(output_image_path)
        i += 1
    print(f"output_images:{output_images}")
    return output_images


def table_ocr(output_images):
 # Debugging line to check the contents of output_images
    print(f"Type of output_images: {type(output_images)}, Contents: {output_images}")
    
    # Assuming the first element of the list contains the image path.
    # Let's check the type of the first element to make sure it's a string.
    if len(output_images) > 0:
        first_image = output_images[0][0]
        print(f"Type of first_image: {type(first_image)}, Contents: {first_image}")
    
    # If it prints out that `first_image` is indeed a string, then you can proceed
    # with opening the image as you were doing:
    image = Image.open(first_image).convert("RGB")

    #image = Image.open(output_image_path).convert("RGB")
    encoding = feature_extractor(image, return_tensors="pt")
    with torch.no_grad():
        outputs = structure_model(**encoding)

    target_sizes = [image.size[::-1]]
    results = feature_extractor.post_process_object_detection(outputs, threshold=0.5, target_sizes=target_sizes)[0]
    print(f"results: {results}\n")
    # get column and row
    columns = []
    rows = []
    for i in range(len(results['boxes'])):
        _id = results['labels'][i].item()
        if _id == 1:##-----列内容
            columns.append(results['boxes'][i].tolist())
        elif _id == 2:##-----行内容
            rows.append(results['boxes'][i].tolist())

    sorted_columns = sorted(columns, key=lambda x: x[0])
    sorted_rows = sorted(rows, key=lambda x: x[1])
    # ocr by cell
    ocr_results = []
    for row in sorted_rows:
        row_result = []
        for col in sorted_columns:
            rect = [col[0], row[1], col[2], row[3]]
            crop_image = image.crop(rect)
            image_path = 'cell.png'
            crop_image.save(image_path)
            row_result.append(paddle_ocr(image_path=image_path))
        print(f"row_result: {row_result}\n")
        ocr_results.append(row_result)

    print(f"ocr_results: {ocr_results}\n")
    return ocr_results


if __name__ == '__main__':
    with gr.Blocks() as demo:
        with gr.Row():
            with gr.Column():
                image_box = gr.Image()
                image_urls = gr.TextArea(lines=1, placeholder="Enter image url", label="Images")
                # image_index = gr.TextArea(lines=1, placeholder="Image Number", label="No")
            with gr.Column():
                gallery = gr.Gallery(label="Tables", show_label=False, elem_id="gallery", columns=[3], rows=[1],
                                     object_fit="contain", height="auto")
                detect = gr.Button("Table Detection")
                submit = gr.Button("Table OCR")
                ocr_outputs = gr.DataFrame(label='Table',
                                           interactive=True,
                                           wrap=True)
        detect.click(fn=table_detect,
                     inputs=[image_box, image_urls],
                     outputs=gallery)
        submit.click(fn=table_ocr,
                     inputs=[gallery],
                     outputs=ocr_outputs)
    demo.launch(server_name="0.0.0.0", server_port=7676, share=True)

结果如下:
在这里插入图片描述

6、总结

表格识别结果,第一列和最后一列有时候识别不出。问题原因可能表格结构识别存在问题,后续会继续优化。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/1938824.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

Langchain[3]:Langchain架构演进与功能扩展:流式事件处理、事件过滤机制、回调传播策略及装饰器应用

Langchain[3]:Langchain架构演进与功能扩展:流式事件处理、事件过滤机制、回调传播策略及装饰器应用 1. Langchain的演变 v0.1: 初始版本,包含基本功能。 从0.1~0.2完成的特性: 通过事件流 API 提供更好的流式支持。标准化工具调用支持Tool…

全国区块链职业技能大赛国赛考题前端功能开发

任务3-1:区块链应用前端功能开发 1.请基于前端系统的开发模板,在登录组件login.js、组件管理文件components.js中添加对应的逻辑代码,实现对前端的角色选择功能,并测试功能完整性,示例页面如下: 具体要求如下: (1)有明确的提示,提示用户选择角色; (2)用户可看…

Java中静态代理和动态代理介绍和使用

前言 在Java中,代理模式是一种常用的设计模式,用于为其他对象提供一种代理以控制对这个对象的访问。代理模式主要有两种实现方式:静态代理和动态代理。 一、静态代理 静态代理是由程序员手动创建或指定代理类,代理类在程序运行…

【C语言】详解结构体(下)(位段)

文章目录 前言1. 位段的含义2. 位段的声明3. 位段的内存分配(重点)3.1 存储方向的问题3.2 剩余空间利用的问题 4. 位段的跨平台问题5. 位段的应用6. 总结 前言 相信大部分的读者在学校或者在自学时结构体的知识时,可能很少会听到甚至就根本没…

sip代理服务器、SIP用户代理服务器、sip服务器的区别和联系

一.SIP代理服务器(SIP Proxy Server)和SIP用户代理服务器(SIP User Agent Server,简称SIP UAS)的区别和联系。 1. 区别 1)功能定位 SIP代理服务器:主要负责将SIP请求消息从发起方…

VBA技术资料MF175:利用文本框和列表框实现多列数据录入

我给VBA的定义:VBA是个人小型自动化处理的有效工具。利用好了,可以大大提高自己的工作效率,而且可以提高数据的准确度。“VBA语言専攻”提供的教程一共九套,分为初级、中级、高级三大部分,教程是对VBA的系统讲解&#…

学习周报:文献阅读+水动力学方程推导

目录 摘要 Abstract 文献阅读:物理信息神经网络学习自由表面流 文献摘要 讨论|结论 预备知识 浅水方程SWE(Shallow Water Equations) 质量守恒方程: 动量守恒方程: Godunov通量法: 基本原理&…

分布式会话拦截器

1.分布式会话拦截器-构建拦截器 背景:对于不同的用户进行权限拦截(基于token的判断) 实现过程:在api下构建包以及相关的文件,创建UserTokenInterceptor,实现implements handlerInterceptor.重写三种主要方法。 preHandle postHandle afterCo…

MongoDB文档整理

过往mongodb文档: https://blog.csdn.net/qq_46921028/article/details/123361633https://blog.csdn.net/qq_46921028/article/details/131136935https://blog.csdn.net/qq_46921028/article/details/139247847 1. MongoDB前瞻 1、MongoDB概述: MongoDB是…

【Rust日报】在 Linux 文件系统中使用 Rust 的讨论

SIMD 加速的迭代器 单指令流多数据流(Single Instruction Multiple Data,缩写:SIMD)是一种采用一个控制器来控制多个处理器,同时对一组数据(又称"数据向量")中的每一个分别执行相同的…

PDF压缩软件电脑版 电脑pdf压缩怎么压缩文件

在数字化时代,pdf文件因其良好的兼容性和稳定性,已成为工作与生活中不可或缺的文件格式。然而,随着内容的增多,pdf文件的体积也随之增大,给文件的传输和存储带来了一定的困扰。本文将为你详细介绍如何在电脑上压缩pdf文…

【手撕数据结构】拿捏单链表

目录 单链表介绍链表的初始化打印链表增加节点尾插头插再给定位置之后插入在给定位置之前插入 删除节点尾删头删删除给定位置的节点删除给定位置之后的节点 查找节点 单链表介绍 单链表也叫做无头单向非循环链表,链表也是一种线性结构。他在逻辑结构上一定连续&…

昇思25天学习打卡营第10天 | FCN图像语义分割

学习心得:全卷积网络(FCN)在图像语义分割中的应用 图像语义分割作为计算机视觉领域的一个重要分支,对于理解图像内容提供了非常关键的技术支持。通过学习并实践全卷积网络(FCN)在图像语义分割的应用&#…

2024-07-19 Unity插件 Odin Inspector9 —— Validation Attributes

文章目录 1 说明2 验证特性2.1 AssetsOnly / SceneObjectsOnly2.2 ChildGameObjectsOnly2.3 DisallowModificationsIn2.4 FilePath2.5 FolderPath2.6 MaxValue / MinValue2.7 MinMaxSlider2.8 PropertyRange2.9 Required2.10 RequiredIn2.11 RequiredListLength2.12 ValidateIn…

【学习笔记】无人机系统(UAS)的连接、识别和跟踪(八)-无人机探测与避让(DAA)机制

目录 引言 5.6 探测与避让(DAA)机制 5.6.1 基于PC5的探测与避让(DAA)机制 引言 3GPP TS 23.256 技术规范,主要定义了3GPP系统对无人机(UAV)的连接性、身份识别、跟踪及A2X(Airc…

HP ilo4服务器硬件监控指标解读

随着企业IT架构的复杂化,服务器的稳定性和可靠性成为保障业务连续性的关键因素。HP ilo4作为HP服务器的一个重要组件,提供了强大的远程管理和监控功能。本文将对使用监控易软件通过HP ilo4进行服务器硬件监控的指标进行解读,帮助运维团队更好…

数学建模-----SPSS参数检验和非参数检验

目录 1.参数检验 1.1独立样本t检验案例分析 1.1.1查看数据编号 1.1.2确定变量所属类型 1.1.3选项里面的置信区间 1.1.4对于结果进行分析 1.2配对样本t检验案例分析 1.2.1相关设置 1.2.2分析结果 2.非参数检验 2.1对比分析 2.2非参数检验的方法 2.3案例分析 2.3.1相…

Codeforces Round 960 (Div. 2)(A~C)题

A. Submission Bait 思路: 如果最大值有奇数个显然Alice赢&#xff0c;否则只需要看排序后是否存在n−i1是否为奇数且ai>ai−1即可。 代码: #include<bits/stdc.h> using namespace std; #define N 2000010 typedef long long ll; typedef unsigned long long ull; …

KMeans等其他聚类算法

KMeans算法是一种经典的聚类方法&#xff0c;最早由Stuart Lloyd在1957年提出&#xff0c;并在1982年由J. MacQueen推广和普及。虽然KMeans已经有几十年的历史&#xff0c;但它依然是数据挖掘和机器学习领域中最常用的聚类算法之一。 数学原理 KMeans算法的目标是将数据集分成…

Blender中的重拓扑修改器如何使用?

许多人还不了解Blender中的重拓扑编辑器及其使用方法。Blender中的重拓扑修改器提供了一系列工具和选项&#xff0c;以简化创建优化网格的过程&#xff0c;无论是出于何种目的&#xff0c;都能为3D艺术家和建模者节省大量时间和精力。那么&#xff0c;在Blender中重拓扑的定义是…