微信扫码
添加专属顾问
在本文中,我将向您展示如何从收据中提取信息,只需提供一张简单的收据图像。首先,我们将利用 OCR 从收据中提取信息。然后,这些信息将被发送到 GPT-4o mini 模型进行信息提取。我开发此项目的目的是开发一个应用程序,该应用程序可以通过拍摄收据图像并选择哪些物品属于哪个人来帮助朋友之间分摊账单。本文将重点介绍此目标的信息提取部分。
使用 OCR 和 GPT-4o mini 从收据中提取信息。图片由 ChatGPT 生成。
例如,在去餐馆吃饭后,查看收据并计算每个人的份额是一件很麻烦的事情。我曾多次遇到过这个问题,因此想要一个解决方案来提高流程效率。因此,我想到 BillSplitter 应用程序的想法。这个想法是用户可以拍摄收据的图像,应用程序将利用 OCR 和语言模型来处理收据并提取每个商品和相应的价格,用户只需选择哪个人应该支付哪个商品。最终,用户会收到每个人欠款的概览。本文展示了如何开发此应用程序的收据处理部分,而前端部分将留待另一篇文章介绍。这意味着本文将假设您有一张收据图像,目标是在列表中提取收据中的每个商品及其相应价格。稍后,可以开发一个前端应用程序来利用我们将在本文中开发的内容。
从收据中提取文本
使用 GPT-4o mini 提取信息
审查结果
第一张收据
第二张收据
第三张收据
理解如何改进管道
使用 Textract 测试改进的解决方案
结论
首先,可以使用 OCR 引擎从收据中提取文本。有无数的开源 OCR 引擎,但由于 EasyOCR 的有效性和易用性,本项目将使用它。
首先,您必须导入所需的包:
import easyocr
import cv2
import matplotlib.pyplot as plt
import requests
import torch
import pytesseract
from PIL import Image
import json
import numpy as np
您可以使用 pip 安装这些包。请注意,要在 GPU 上运行 EasyOCR(强烈建议这样做,因为它可以节省大量时间),我必须使用以下命令显式地在 GPU 上安装 PyTorch:
pip3 install torch torchvision torchaudio --index-url https://download.pytorch.org/whl/cu118
我在 Windows 和 CUDA 11.8 上使用了这个命令,但如果您使用其他操作系统或 CUDA 版本,可以在 PyTorch 网站 上找到正确的命令。
然后我拍摄了一些收据的图像。欢迎您使用自己的收据,但如果您不想这样做,可以使用我在 Google Drive 上的收据,这些收据来自挪威的超市。我将包含收据图像的文件夹命名为 ReceiptData,并使用以下命令将图像路径加载到变量中:
img_path1 = 'ReceiptData/20231016_180324.jpg'
img_path2 = 'ReceiptData/20231010_210904.jpg'
img_path3 = 'ReceiptData/20231014_182753.jpg'
img_path4 = 'ReceiptData/20230917_131726.jpg'
img_path5 = 'ReceiptData/20231002_190427.jpg'
我还喜欢使用一个变量来表示我正在使用哪张收据,以便于阅读和在收据之间轻松切换。
PATH_TO_USE = img_path2
要加载 EasyOCR,您可以使用下面的代码行。请注意,如果您要读取不同语言的文本,可以将 no 更改为您想要的语言(例如,en 表示英语)。您可以在 本网站 上找到所有可用语言及其代码名称的列表。此外,如果您没有使用 GPU,可以设置 gpu=False。
reader = easyocr.Reader(['no'], gpu=True)
然后我读取图像,将其转换为灰度以提高 OCR 性能,并运行 OCR。此外,我还将 OCR 输出组合成一个字符串。
img = cv2.imread(PATH_TO_USE)
img = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY)
result = reader.readtext(img, detail=0)
result_string = ''
for ele in result:
result_string += ele + ' '
如果您想查看收据,可以使用以下命令:
cv2.namedWindow('img', cv2.WINDOW_NORMAL)
cv2.imshow('img', img)
cv2.waitKey(0)
cv2.destroyAllWindows()
恭喜,您现在已经成功地从收据中提取了文本。请注意,某些 OCR 输出可能看起来毫无意义,例如一张收据的 OCR 输出开头看起来像:Al rema 10@@ Salaskvittering REMA 1ooo GausdAL,这自然不是最佳的。发生这种情况有多种原因,但主要原因是收据中的文本非常小,这使得 OCR 难以识别所有字符。然而,正如您将在本文后面看到的那样,OCR 足够有效地从收据中成功提取商品。然而,应该注意的是,提高 OCR 质量应该是未来工作的重点。
我花了大量时间尝试使用 Python 中的逻辑、正则表达式 和类似方法从收据中提取信息。然而,这些方法的问题在于它们难以处理 OCR 可能输出的各种结果。然而,像 GPT-4o mini 这样的大型语言模型彻底改变了这个过程,它们既能够处理不同的输入,又能够输出结构化的响应。这使得大型语言模型非常适合我们想要在此实现的任务,即从收据的 OCR 输出中提取项目和价格。对于本文,我将使用 GPT-4o mini,尽管在这种情况下也可以使用许多其他 LLM。
首先,您应该在 OpenAI 登录或创建帐户。这将为您提供访问 OpenAI API 所需的 API 密钥。您还需要输入一种付款方式来支付 API 请求的费用。我建议尽快对您的帐户设置支出限额,因为人们很容易开始花费大量金钱。但是,在收据数据上使用 GPT-4o mini 相当便宜,您可以在 OpenAI 的网站 上全面了解定价。
然后,您应该安全地存储 API。有几种方法可以做到这一点,但我只是创建了一个 constants.py 文件,如下所示:
OPEN_AI_API_KEY = '123123123'
然后,您可以使用以下代码将密钥导入到单独的文件中:
from constants import OPEN_AI_API_KEY
OPEN_AI_API_KEY = str(OPEN_AI_API_KEY)
assert OPEN_AI_API_KEY.startswith('sk-') and OPEN_AI_API_KEY.endswith('123')
client = OpenAI(api_key=OPEN_AI_API_KEY)
请记住替换断言中的两个字符串以匹配您的 API 密钥。此断言语句可确保您使用的是正确的 API 密钥。
然后,您可以创建一个 OpenAI 客户端以使用以下命令发出 API 请求:
from openai import OpenAI
import os
client = OpenAI(api_key=OPEN_AI_API_KEY)
您可以使用以下命令发送 API 请求:
MODEL = 'gpt-4o-mini'
def prompt_gpt(prompt):
return client.chat.completions.create(
model=MODEL,
messages=[
{'role': 'system', 'content': 'You are a helpful assistant.'},
{'role': 'user', 'content': prompt}
]
).choices[0].message.content
然后我使用下面的代码创建了一个提示。很难找到一个有效的提示,并且在进行这项工作时,我特别难以让 GPT-4o mini 提供过长的答案。为了解决这个问题,我添加了两句话,分别是 Only respond with the list, and nothing else 和 Sure, here is the list of items and prices。最后一部分很有趣,因为我在提示本身中包含了响应的开头,我读到这可以帮助避免 LLM 提供过长的解释,而这在这种情况下是不需要的。
prompt = f"Given a string of text from an OCR of a receipt. Find each item and price in the receipt, and return with a list of tuples like this: [(item1, price1), (item2, price2), ...]. Only respond with the list, and nothing else. The string is: {result_string}"
prompt += ' . Sure, here is the list of items and prices: '
然后,您可以使用以下命令提示 GPT-4o mini:
response = prompt_gpt(prompt)
以下是我收到的一些示例回复:
[('KylLING HotwiNGS', '57,00'), ('Iskaffe Hocca', '18,90'), ('TORTILLACHIP ZINGY', '16,90'), ('SøTPOTeT FRIES', '37,00'), ('Creamy PEANøTTSHeR', '46,00'), ('GluTEn FReE TORT', '43,90'), ('DIP TEX MEX STyLE', '40,90')]
[('RISTO HOZZA _ 2PK', 89.90), ('SUPERHEL T , GROYBRøP', 35.00), ('B#REPOSE', 25.00), ('Dr Oetker', 26.97)]
[('TøRKEDE APRIKOSER', 29.90), ('MANDLER', 10.90), ('Couscous', 22.40), ('FISKEBURGER HYS8TO', 53.90), ('AVOCADO 2PK', 40.00), ('GRøNNKÅL', 0.00), ('BROKKOLI', 0.00), ('GULROT BEGER 75OGR', 3.00)]
设置好所有代码以从收据中提取信息后,就该审查该方法的效果如何了。我将使用定性方法,单独查看一些收据,以判断信息提取的执行情况。我将回顾上面显示输出的 3 张图片。
对于这张收据,您可以看到信息提取管道能够提取所有项目及其正确价格。我认为这令人印象深刻,因为图像不是非常清晰,并且图像中有很多不属于收据的背景。不幸的是,项目名称中有一些拼写错误,但我认为在这种情况下是可以接受的,因为您仍然可以轻松理解它是哪个项目。
在这张收据上,信息提取管道遇到了困难。前两项和价格是正确的,但模型给出了 bærepose 的错误价格(发生这种情况是因为 OCR 无法为该项目选择正确的价格),这混淆了 LLM。收据项目部分的最后一行是第一项的折扣,LLM 无法理解,并且错误地输出这是单独的一项。但是,我认为这是可以接受的;因为模型很难理解最后一行是不同项目的折扣,而不是项目本身。
对于前四项,管道在这张收据上表现非常出色,但不幸的是,其余四项失败了。考虑到图像更清晰,背景噪音更少,这张收据应该比前两张更容易,但不幸的是,实际情况并非如此。我查看了这张收据的 OCR 输出,以了解错误的来源。前四项的输出清晰且正确,而模型突然在最后四项失败,这表明不正确的 OCR 输出会导致 GPT-4o mini 出现重大问题。
为了改进管道,我们必须了解它的弱点。正如前面所讨论的,问题似乎出在 OCR 上。为了进一步调查,我们将更深入地研究 OCR 输出。EasyOCR 中的 OCR 包括两个步骤。第一步称为“文本检测”,它检测图像中存在文本的区域。这是通过使用边界框标记带有文本的区域来完成的。第二步称为“文本识别”,给定一个包含文本的边界框,它将输出边界框中存在的文本。因此,本文中实现的信息提取管道的问题可能在于这两个步骤之一。
首先,我们研究文本识别,我们通过打印出 OCR 找到的边界框来做到这一点。您可以使用以下代码执行此操作。首先,运行 OCR 并让函数也返回边界框(这是通过设置 detail=1 而不是像我们之前那样设置 detail=0 来完成的)
```python
result2 = reader.readtext(img, detail=1)
```
然后,您可以使用以下代码打印出图像的边界框:
```python
for (bbox, text, prob) in result2:
top_left = tuple(bbox[0])
bottom_right = tuple(bbox[2])
cv2.rectangle(img, top_left, bottom_right, (0, 255, 0), 2)
cv2.putText(img, text, (top_left[0], top_left[1] - 10), cv2.FONT_HERSHEY_SIMPLEX, 0.8, (0, 255, 0), 2)
width = int(img.shape[1] * scale_percent / 100)
height = int(img.shape[0] * scale_percent / 100)
dim = (width, height)
resized_img = cv2.resize(img, dim, interpolation=cv2.INTER_AREA)
cv2.imwrite('output_image_with_boxes.jpg', resized_img)
cv2.imshow('Resized Image with Bounding Boxes', resized_img)
cv2.waitKey(0)
cv2.destroyAllWindows()
```
对于第三张收据,返回下面的收据。您可以看到模型难以读取最后四件商品的价格。这导致文本识别步骤表现不佳,这反过来又导致 GPT-4o mini 难以给出收据上商品和价格的准确答案。
在 EasyOCR 文本识别步骤后打印出第三张收据。您可以看到模型难以识别最后四件商品的价格,从而导致我们看到的第二张收据的错误。图片由作者提供。
如果对第二张收据重复这些步骤,则可以看到相同的问题。
解决这个问题主要有两种方法。第一步是拍摄更清晰的收据照片,以便 OCR 更容易读取文本。但是,我认为第三张收据的图像已经很清晰了,OCR 应该能够读取它。另一种主要方法是改进 OCR,方法是使用不同的 OCR 引擎(例如,PaddleOCR、Tesseract 或付费 OCR 服务,如 AWS Textract) 或通过微调 OCR,正如我在关于微调 EasyOCR 文本识别部分的文章中所示。请注意,在进行此项目时,我尝试了 PaddleOCR 和 Tesseract,它们的性能均低于 EasyOCR。
最后,我还测试了 Amazon Textract 选项,以了解其有效性。以下是第二张收据的结果,您可以看到 Amazon Textract 基本上完美地定位了所有文本,使其成为从收据中提取文本的非常有效的选择。在下一节中,我将 AWS Textract 实现到管道中,以了解应用程序的效果如何。
此图显示了 AWS Textract 如何读取收据。如您所见,边界框几乎完美地封装了单词,这表明了 AWS Textract 的有效性。图片由作者提供。
为了了解从收据中提取信息的程度,我将使用 AWS Textract 作为我的 OCR,而不是 EasyOCR。通常情况下,我更喜欢在本地使用 OCR,因为它让我可以更好地控制流程,而且人工智能的乐趣之一是自己处理模型,而不仅仅是调用 API。但是,我想看看在这种情况下付费 OCR API 服务的效果如何。使用 AWS Textract 需要 设置 AWS 帐户,以便拥有调用 Textract API 的访问密钥。请注意,AWS 的初始设置可能有点繁琐,主要是出于安全原因,但我向您保证,正确设置是值得的。这是因为您将学习如何设置 AWS(一种流行的云提供商),并且可以确保密钥的安全性,这是一项需要保持的重要实践,尤其是在您开发拥有大量用户的应用程序时。我不会在这里提供有关设置帐户的教程,但 AWS 已经为此过程编写了一些 高质量的文档,并且还有大量其他文章介绍了该主题。
Textract 服务也非常便宜,在撰写本文时,每月前 100 万页的价格为 1.5 美元/1000 页,超过 100 万页的价格为 0.6 美元/1000 页。如果您使用检测文档文本 API,您还可以通过 AWS 免费套餐每月免费获得 1000 页。
设置访问凭证后,您可以使用以下代码使用 Textract API。首先,导入一些内容。请注意,我将凭证存储在一个名为 constants_.py._ 的单独文件中。
```python
import boto3
from io import BytesIO
from constants import AWS_ACCESS_KEY_ID, AWS_SECRET_ACCESS_KEY, AWS_REGION
import os
```
然后,您必须创建一个客户端来调用 API。
```python
def get_aws_textract_client():
return boto3.client('textract',
aws_access_key_id=AWS_ACCESS_KEY_ID,
aws_secret_access_key=AWS_SECRET_ACCESS_KEY,
region_name=AWS_REGION)
```
然后,我使用以下函数来调用 API。
```python
def get_textract_text_from_image(client, image_path):
assert os.path.exists(image_path), f"Image file not found: {image_path}"
with open(image_path, 'rb') as document:
img = bytearray(document.read())
response = client.detect_document_text(
Document={'Bytes': img}
)
return response
def extract_text_from_response(response):
result_string = ""
for block in response["Blocks"]:
if block["BlockType"] == "WORD" or block["BlockType"] == "LINE":
result_string += block["Text"] + " "
return result_string
```
我用以下几行代码调用这两个函数
```python
response = get_textract_text_from_image(client, PATH_TO_USE)
result_string = extract_text_from_response(response)
```
然后,我对第三张收据运行此程序,并使用提取的文本提示 GPT-4o mini,结果如下:
```
[('TORKEDE APRIKOSER', 29.90), ('MANDLER', 10.90), ('COUSCOUS', 22.40), ('FISKEBURGER HYS&TO', 53.90), ('AVOCADO 2PK 320G', 34.90), ('GRONNKAL 150G', 24.90), ('BROKKOLI', 24.90), ('GULROT BEGER 750GR', 24.90)]
```
如您所见,AWS Textract 和 GPT-4o mini 可以从收据中提取所有商品及其正确价格,但最后一项商品的价格不正确。我还对第二张收据进行了尝试,结果如下:
```
[('RISTO. MOZZA. 2PK 15%', 89.90), ('SUPERHELT GROVBROD 15%', 35.00), ('BAREPOSE 80% RESIR 25%', 4.25), ('30% Dr. Oetker', -26.97)]
```
在这种情况下,AWS Textract 和 GPT-4o mini 可以完美地提取所有商品及其价格。请注意,GPT-4o mini 返回最后一项商品的价格为负数,我认为这是可以接受的,应该在应用程序的前端处理。
在这篇文章中,我向你展示了如何开发一个信息提取管道,从收据中检索商品和价格。我们首先实施了 EasyOCR 来提取收据中的文本,然后使用 GPT-4o mini 根据 OCR 输出提供商品和价格。然后,我们回顾了三张不同收据的结果。审查表明,该管道对某些商品表现良好,提取了正确的商品和价格,尽管商品名称存在一些拼写错误。然而,该管道对其他项目完全失败,这主要归因于 OCR 错误。除了 EasyOCR,我还测试了 Tesseract OCR 和 PaddleOCR,它们对本文中的三张收据没有提供更好的结果。AWS Textract 的设置是为了处理 OCR 错误,它提供了比 EasyOCR、Tesseract 和 PaddleOCR 好得多的结果。结合使用 AWS Textract 和 GPT-4o mini,我们能够非常准确地从收据中导出商品和价格。
53AI,企业落地大模型首选服务商
产品:场景落地咨询+大模型应用平台+行业解决方案
承诺:免费POC验证,效果达标后再合作。零风险落地应用大模型,已交付160+中大型企业
2026-06-03
2026-05-13
2026-05-26
2026-04-14
2026-04-20
2026-04-16
2026-05-21
2026-06-10
2026-04-27
2026-06-02
2026-07-08
2026-07-07
2026-07-07
2026-07-02
2026-06-29
2026-06-18
2026-06-11
2026-06-05
欢迎您使用【53AI 官方网站】(以下简称“本网站”或“我们”)。本《会员服务协议》(以下简称“本协议”)是您(以下简称“会员”或“用户”)与【深圳市博思协创网络科技有限公司】之间关于注册、登录及使用本网站会员服务所订立的法律协议。
在您注册或登录前,请务必审慎阅读、充分理解各条款内容,特别是免除或限制责任的条款、知识产权条款、争议解决条款等。此类条款将以加粗形式提示您注意。 当您通过微信公众号授权、手机验证码验证或其他方式成功登录本网站时,即视为您已完全理解并同意接受本协议的全部内容。
一、 定义
本网站:指由【深圳市博思协创网络科技有限公司】运营的,域名为【53ai.com】的网站及相关移动端页面。
会员服务:指本网站向注册会员提供的知识库文章查阅、内容检索及其他相关增值服务。
知识库内容:指本网站发布的包括但不限于文字、图表、数据、研究报告、行业分析等数字化内容资源。
二、 账号注册与登录
登录方式:本网站支持以下登录方式,您可根据实际情况选择:
微信公众号授权登录:您同意将您的微信OpenID信息授权给本网站,用于创建或关联会员账号。
手机验证码登录:您需提供真实有效的手机号码,并通过短信验证码完成身份验证与登录/注册。
账号安全:您的账号仅限您本人使用,禁止赠与、借用、租用、转让或售卖。因您保管不善导致的账号被盗、密码泄露等损失,由您自行承担。
实名认证:根据相关法律法规要求,我们可能要求您在特定功能下完成实名认证。如您拒绝提供,可能无法使用部分或全部服务。
未成年人保护:若您未满18周岁,请在法定监护人的陪同下阅读本协议,并在征得监护人同意后使用本服务。
三、 服务内容与规范
知识库查阅权限:会员登录后,有权按照其会员等级对应的权限范围,在线浏览、检索本网站知识库中的相关文章及内容。
服务变更:我们有权根据业务发展需要,调整、变更或终止部分服务内容,并将以网站公告、公众号消息等方式提前通知。
禁止行为:您在使用服务时不得实施以下行为:
利用技术手段批量爬取、下载、转存知识库内容;
将知识库内容用于商业目的或未经授权地向第三方传播;
干扰本网站正常运行或侵犯其他用户合法权益;
发布违法违规信息或从事违反公序良俗的活动。
四、 知识产权声明
权利归属:本网站知识库中的排版设计、软件代码等内容的知识产权均归【公司全称】或原权利人所有,受《中华人民共和国著作权法》等法律保护。
有限许可:本网站授予会员一项非独占、不可转让、不可转授权的普通许可,仅限于个人学习、研究之目的在线查阅知识库内容。
侵权追责:未经书面许可,任何单位或个人不得以任何形式复制、转载、摘编、镜像、汇编或以其他方式使用上述内容。一经发现,我们保留追究其法律责任的权利。
五、 个人信息保护
我们重视对您个人信息的保护。关于我们如何收集、使用、存储和保护您的个人信息,请单独阅读 《隐私政策》。
您通过微信公众号授权或手机号验证所提供的信息,我们将严格按照《个人信息保护法》的规定处理,仅用于身份识别、服务提供及安全验证等必要用途。
您可以随时通过网站设置或联系客服行使查阅、更正、删除个人信息及撤回授权同意的权利。
六、 免责声明
内容准确性:知识库内容仅供参考,不构成专业建议。我们不对其完整性、准确性、时效性作任何明示或暗示的保证,您应自行判断并承担使用风险。
不可抗力:因自然灾害、政策法规变化、网络故障、第三方平台接口异常(如微信接口维护、运营商短信通道故障)等不可抗力导致的服务中断或延迟,我们不承担违约责任。
第三方链接:本网站可能包含指向第三方网站的链接,该等网站的内容和服务不受我们控制,请您自行甄别风险。
七、 违约责任
如您违反本协议约定,我们有权视情节采取警告、限制功能、暂停服务、注销账号等措施,并保留要求赔偿损失的权利。
如因您的违约行为导致我们遭受行政处罚、第三方索赔或商誉损失,您应承担全部赔偿责任(包括但不限于罚款、赔偿金、律师费、公证费等)。
八、 法律适用与争议解决
本协议的订立、执行和解释均适用中华人民共和国大陆地区法律。
因本协议产生的或与本协议有关的任何争议,双方应友好协商解决;协商不成的,任何一方均可向【公司所在地】有管辖权的人民法院提起诉讼。
九、 其他
本协议构成双方就本服务达成的完整协议,取代此前任何口头或书面约定。
本协议任一条款被认定为无效或不可执行的,不影响其他条款的效力。
我们对本协议享有最终解释权,并在法律允许的范围内保留随时修改的权利。修改后的协议一经公布即生效,继续使用服务即视为同意修订内容。