如何从 PDF 中提取表格作为文本

2023-11-24

我有一个 PDF 文件,其中包含表格、文本和一些图像。我想在 PDF 中有表格的地方提取表格。

现在正在手动从页面中查找表。从那里我捕获该页面并保存到另一个 PDF 中。

import pypdf import PdfReader, PdfWriter

filename = "Sammamish.pdf"
reader = PdfReader(filename)

page = reader.pages[126]

writer = PdfWriter()
writer.add_page(page)

new_filename = "allTables.pdf"
with open(new_filename, "wb") as output_stream:
    writer.write(output_stream)

我的目标是从整个 PDF 文档中提取表格。

Please have a look at the sample image of a page in PDF


这个答案适用于任何遇到带有图像的 pdf 并需要使用 OCR 的人。我找不到可行的现成解决方案;没有任何东西能给我带来我所需要的准确性。

以下是我发现有效的步骤。

  1. Use pdfimages from https://poppler.freedesktop.org/将 pdf 页面转换为图像。

  2. Use 超立方体检测旋转和图像魔术师 mogrify要解决这个问题。

  3. 使用 OpenCV 查找并提取表格。

  4. 使用 OpenCV 从表中查找并提取每个单元格。

  5. 使用 OpenCV 裁剪和清理每个单元格,这样就不会有干扰 OCR 软件的噪音。

  6. 使用 Tesseract 对每个单元格进行 OCR。

  7. 将每个单元格提取的文本合并为您需要的格式。

我编写了一个 python 包,其中包含可以帮助完成这些步骤的模块。

Repo: https://github.com/eihli/image-table-ocr

文档和来源:https://eihli.github.io/image-table-ocr/pdf_table_extraction_and_ocr.html

有些步骤不需要代码,它们利用外部工具,例如pdfimages and tesseract。我将为需要代码的几个步骤提供一些简短的示例。

  1. 查找表:

在了解如何查找表格时,此链接是一个很好的参考。https://answers.opencv.org/question/63847/how-to-extract-tables-from-an-image/

import cv2

def find_tables(image):
    BLUR_KERNEL_SIZE = (17, 17)
    STD_DEV_X_DIRECTION = 0
    STD_DEV_Y_DIRECTION = 0
    blurred = cv2.GaussianBlur(image, BLUR_KERNEL_SIZE, STD_DEV_X_DIRECTION, STD_DEV_Y_DIRECTION)
    MAX_COLOR_VAL = 255
    BLOCK_SIZE = 15
    SUBTRACT_FROM_MEAN = -2

    img_bin = cv2.adaptiveThreshold(
        ~blurred,
        MAX_COLOR_VAL,
        cv2.ADAPTIVE_THRESH_MEAN_C,
        cv2.THRESH_BINARY,
        BLOCK_SIZE,
        SUBTRACT_FROM_MEAN,
    )
    vertical = horizontal = img_bin.copy()
    SCALE = 5
    image_width, image_height = horizontal.shape
    horizontal_kernel = cv2.getStructuringElement(cv2.MORPH_RECT, (int(image_width / SCALE), 1))
    horizontally_opened = cv2.morphologyEx(img_bin, cv2.MORPH_OPEN, horizontal_kernel)
    vertical_kernel = cv2.getStructuringElement(cv2.MORPH_RECT, (1, int(image_height / SCALE)))
    vertically_opened = cv2.morphologyEx(img_bin, cv2.MORPH_OPEN, vertical_kernel)

    horizontally_dilated = cv2.dilate(horizontally_opened, cv2.getStructuringElement(cv2.MORPH_RECT, (40, 1)))
    vertically_dilated = cv2.dilate(vertically_opened, cv2.getStructuringElement(cv2.MORPH_RECT, (1, 60)))

    mask = horizontally_dilated + vertically_dilated
    contours, hierarchy = cv2.findContours(
        mask, cv2.RETR_EXTERNAL, cv2.CHAIN_APPROX_SIMPLE,
    )

    MIN_TABLE_AREA = 1e5
    contours = [c for c in contours if cv2.contourArea(c) > MIN_TABLE_AREA]
    perimeter_lengths = [cv2.arcLength(c, True) for c in contours]
    epsilons = [0.1 * p for p in perimeter_lengths]
    approx_polys = [cv2.approxPolyDP(c, e, True) for c, e in zip(contours, epsilons)]
    bounding_rects = [cv2.boundingRect(a) for a in approx_polys]

    # The link where a lot of this code was borrowed from recommends an
    # additional step to check the number of "joints" inside this bounding rectangle.
    # A table should have a lot of intersections. We might have a rectangular image
    # here though which would only have 4 intersections, 1 at each corner.
    # Leaving that step as a future TODO if it is ever necessary.
    images = [image[y:y+h, x:x+w] for x, y, w, h in bounding_rects]
    return images
  1. 从表中提取单元格。

这与 2 非常相似,因此我不会包含所有代码。我将参考的部分是对单元格进行排序。

我们想要从左到右、从上到下识别单元格。

我们将找到最左上角的矩形。然后,我们将找到中心位于该左上角矩形的顶部 y 值和底部 y 值内的所有矩形。然后我们将根据这些矩形中心的 x 值对它们进行排序。我们将从列表中删除这些矩形并重复。

def cell_in_same_row(c1, c2):
    c1_center = c1[1] + c1[3] - c1[3] / 2
    c2_bottom = c2[1] + c2[3]
    c2_top = c2[1]
    return c2_top < c1_center < c2_bottom

orig_cells = [c for c in cells]
rows = []
while cells:
    first = cells[0]
    rest = cells[1:]
    cells_in_same_row = sorted(
        [
            c for c in rest
            if cell_in_same_row(c, first)
        ],
        key=lambda c: c[0]
    )

    row_cells = sorted([first] + cells_in_same_row, key=lambda c: c[0])
    rows.append(row_cells)
    cells = [
        c for c in rest
        if not cell_in_same_row(c, first)
    ]

# Sort rows by average height of their center.
def avg_height_of_center(row):
    centers = [y + h - h / 2 for x, y, w, h in row]
    return sum(centers) / len(centers)

rows.sort(key=avg_height_of_center)
本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

如何从 PDF 中提取表格作为文本 的相关文章

随机推荐

  • 使用语言环境设置将西班牙日期转换为 python pandas datetime 对象

    我有 2 个问题 如何使用以下命令将西班牙语日期时间 ago122010 转换为 2010 08 12pandas 使用的格式是strptime正确的 我尝试过以下方法 import locale locale setlocale loca
  • 使用 C# 检查进程是否正在远程系统上运行

    我正在尝试检查进程是否正在远程系统上运行 我正在使用以下代码 string procSearc notepad string remoteSystem remoteSystemName Process proce System Diagno
  • 使用 maven 制作 fat jar 时出现“无法找到数据源:parquet”

    我正在使用 Maven 组装插件组装 fat jar 并遇到以下问题 Exception in thread main java lang ClassNotFoundException Failed to find data source
  • Application Insights 报告每个服务器请求的重复事件

    我有一个在 Azure 应用服务下运行的 API 应用程序 安装了 Application Insights 以跟踪 API 调用的服务器端遥测 在 Azure 门户中查看 Application Insights 时 我看到每个服务器调用
  • Npgsql 提供程序是否支持 TransactionScope?

    我正在尝试将 TransactionScope 与 Npgsql 提供程序一起使用 我在一个老问题中发现 net 中 PostgreSQL 的提供程序 支持 TransactionScope Npgsql 还不支持它 现在 大约 5 年后
  • 保存和加载模型优化器状态

    我正在训练一组相当复杂的模型 并且正在寻找一种保存和加载模型优化器状态的方法 训练模型 由其他几个 权重模型 的不同组合组成 其中有些模型具有共享权重 有些模型根据训练模型而冻结权重 等等 这个例子有点太复杂了 无法分享 但总之 我无法使用
  • 如何使用 Elastic Beanstalk 创建“tmp”目录?

    我正在使用 Node js 需要将文件保存到我的应用程序中的 tmp 目录 问题在于 Elastic Beanstalk 未将应用程序目录设置为可由应用程序写入 因此 当我尝试创建临时目录时 出现此错误 fs js 653 return b
  • 在 Highcharts 中绘制直方图

    众所周知 直方图和条形图是两个不同的东西 我有很多不同的方法在 highcharts 中绘制条形图 但我没有得到任何在 highcharts 中绘制直方图的示例 是否可以使用 highcharts 绘制直方图 如果可以 如何绘制 您可以使用
  • 元素必须没有字符或元素信息项[children],因为该类型的内容类型为空

    当我从事这个项目时 我不断收到一条错误消息 元素 客户 必须没有字符或元素信息项 children 因为该类型的内容类型为空 我不确定为什么这不起作用 因为我遵循了注释 它看起来像这样
  • 我可以在没有 Google Glass 的情况下测试 Google Glass 程序吗?

    我可以在没有实际拥有 Google Glass 的情况下编写和测试 Google Glass 的 Hello World 程序吗 有可以用来测试应用程序的模拟器吗 我看过一个视频 显示我们可以在 Nexus 平板电脑上运行它 查看链接htt
  • Fluent IOC 配置/模块的最佳位置(当前正在尝试 Ninject)

    我正在努力寻找放置 Ninject 配置 模块 的最佳位置 指定类型绑定的位置 我希望我只是错过了一些明显的技巧 因为这开始成为我使用流畅配置 以及 Ninject 的一个障碍 在一个简单的 Web 堆栈中 包含三个独立的项目 Web 业务
  • Android 中的 javax.mail.AuthenticationFailedException

    尝试使用 JavaMail API 从我的 Gmail 帐户发送电子邮件时 我在 LogCat 中收到以下错误消息 11 09 11 04 14 385 W System err 18443 javax mail Authenticatio
  • SIMD编程语言[关闭]

    Closed 此问题正在寻求书籍 工具 软件库等的推荐 不满足堆栈溢出指南 目前不接受答案 在过去的几年中 我进行了大量的 SIMD 编程 大部分时间我都依赖编译器内部函数 例如用于 SSE 编程的函数 或编程汇编来实现真正漂亮的功能 东西
  • 如何使用 PHP 中的 cURL 同时打开多个 URL?

    这是我当前的代码 SQL mysql query SELECT url FROM urls or die mysql error Query the urls table while resultSet mysql fetch array
  • 使用 Django 上传多个文件

    如何使用 Django 上传多个文件 经过一番痛苦之后我最终得到了 uploadify http www uploadify com 与 django 一起工作 但问题并不是真正的 django 的问题 而是让它与 Apple Mac 一起
  • 如何在 Angular2 中操作特定路径上的组件

    我有一个简单的TopbarComponent这基本上在我的视图顶部添加了一个引导式导航栏 由于我的 90 的模板应该包含这个指令 我想通过我的app component看起来像这样 import Component selector my
  • Servlet 3.0 异步

    servlet 3 0 异步功能与以下内容有何区别 ld servlet 实现 doGet request response Thread t new Thread new Runnable void run heavy processin
  • 将映射应用于函数的剩余参数

    在 Clojure 中 如果我有一个函数 f defn f r 我有一个 seq args 其中包含我想要调用 f 的参数 我可以轻松使用 apply apply f args 现在 假设我有另一个函数 g 它被设计为采用任意数量的可选命名
  • 使用多个连接字符串

    Info我的解决方案中有多个项目 其中一个是 DAL 另一个是 ASP NET MVC6 项目 由于 MVC6 项目也是启动项目 我需要在其中添加连接字符串 I saw 这个解决方案 但不被接受 也不起作用 My Try 应用程序设置 js
  • 如何从 PDF 中提取表格作为文本

    我有一个 PDF 文件 其中包含表格 文本和一些图像 我想在 PDF 中有表格的地方提取表格 现在正在手动从页面中查找表 从那里我捕获该页面并保存到另一个 PDF 中 import pypdf import PdfReader PdfWri