当前位置: 首页 > news >正文

Windows下利用 Python OCR 识别电子发票(增值税专用发票)(使用 GhostScript 和 Tesseract )

早起Python公众号下,作者陈熹的解放双手|Python 批量自动提取、整理 PDF 发票!文章中,看到根据坐标识别图片的方法,觉得代码不是太详细。
试着在windows下重现,如下。
所需 requirements.txt 可以是

# Wand - ImageMagick 的 Python 绑定,用于 PDF 转图片
# 需要先安装 ImageMagick: https://imagemagick.org/script/download.php
Wand>=0.6.10# Pillow - Python 图像处理库,用于图片裁剪和处理
Pillow>=9.0.0# pyocr - Tesseract OCR 的 Python 包装器
# 需要先安装 Tesseract OCR: https://github.com/tesseract-ocr/tesseract
pyocr>=0.8.0# openpyxl - 用于读写 Excel 文件
openpyxl>=3.0.0# numpy - 数值计算库(图像处理可能需要)
numpy>=1.21.0# ========== 系统依赖(需要手动安装)==========
#
# 1. Ghostscript (必需 - 用于 PDF 处理)
#    Windows: https://www.ghostscript.com/download/gsdnld.html
#    推荐版本: 9.27 或更高
#    安装路径: C:\Program Files\gs\gs9.27\bin
#
# 2. ImageMagick (必需 - 用于 PDF 转图片)
#    Windows: https://imagemagick.org/script/download.php
#    推荐版本: 7.1.2-Q16-HDRI 或更高
#    安装路径: C:\Program Files\ImageMagick-7.1.2-Q16-HDRI
#
# 3. Tesseract OCR (必需 - 用于文字识别)
#    Windows: https://github.com/UB-Mannheim/tesseract/wiki
#    推荐版本: 5.0 或更高
#    安装路径: C:\Program Files\Tesseract-OCR
#
#    重要: 需要下载中文语言包 chi_sim.traineddata
#    语言包下载: https://github.com/tesseract-ocr/tessdata
#    语言包路径: C:\Program Files\Tesseract-OCR\tessdata\chi_sim.traineddata
#

image

一开始尝试发现图片背景变成了黑色,
而直接使用命令行,"C:\Program Files\gs\gs9.27\bin\gswin64c.exe" -dNOPAUSE -dBATCH -sDEVICE=png16m -r300 -sOutputFile=output_%d.png dzfp_1.pdf 则是正常的。
可以合并图像到白色背景色进行更正或者使用之后的代码。chat.z.ai 给的解释是 PDF 文件本身可能包含透明图层或未定义背景色的区域。

with Image(filename=path, resolution=300) as img:# 创建一个白色背景图层with Image(width=img.width, height=img.height, background=Color('white')) as bg:# 将原图像合并到白色背景上bg.composite(img, 0, 0)bg.convert('jpeg')bg.save(filename='output_white.jpg')

参考原文,借助 AI,得到代码如下

import os
import sys
import io
from PIL import ImageDraw# 设置 Ghostscript 和 Tesseract 路径(在导入 wand 之前)
os.environ['MAGICK_HOME'] = r'C:\Program Files\ImageMagick-7.1.2-Q16-HDRI'
os.environ['PATH'] = (r'C:\Program Files\gs\gs9.27\bin;' +r'C:\Program Files\Tesseract-OCR;' +os.environ.get('PATH', ''))from wand.image import Image
from wand.color import Color
from PIL import Image as PI
import pyocr
from openpyxl import Workbook# 获取桌面路径包装成一个函数
def GetDesktopPath():return os.path.join(os.path.expanduser("~"), 'Desktop')# 获取当前文件所在文件夹路径
def GetCurrentDirectoryPath():return os.path.dirname(os.path.abspath(__file__))# ========== 配置区域 ==========
# 选择要处理的 PDF(取消注释其中一个)
# path = GetCurrentDirectoryPath() + r"/dzfp_1.pdf"
path = GetCurrentDirectoryPath() + r"/pdf/dzfp_2.pdf"# ========== 坐标配置 ==========
# 完整页坐标 (2480 x 3508)
COORDS_FULL = {'total_amount': (1642, 3100, 2000, 3270),'tax_id': (640, 510, 1180, 570),'issuer': (370, 3380, 480, 3450),
}# 半页坐标 (2480 x 1654)
# 注意:这些坐标需要根据实际的 pdf2_grid.jpg 来调整
COORDS_HALF = {'total_amount': (1860, 1100, 2200, 1200),  # 需要根据实际位置调整'tax_id': (640, 510, 1180, 570),           # 'issuer': (360, 1500, 480, 1600),          # 需要根据实际位置调整
}# ========== 主程序 ==========
# 获取配置好的 tesseract
tools = pyocr.get_available_tools()
if len(tools) == 0:print("错误: 找不到 OCR 工具,请确认 Tesseract 已安装")sys.exit(1)tool = tools[0]
print(f"使用 OCR 工具: {tool.get_name()}")# 设置语言为简体中文
lang = 'chi_sim'
print(f"使用语言: {lang}")
print(f"处理文件: {os.path.basename(path)}\n")# 通过 wand 模块将 PDF 文件转化为分辨率为 300 的 jpeg 图片形式
with Image(filename=path, resolution=300) as image_pdf:# 设置白色背景,移除透明通道image_pdf.background_color = Color('white')image_pdf.alpha_channel = 'remove'# 转换为 JPEGimage_jpeg = image_pdf.convert('jpeg')# 如果是多个图片,则会保存多个,output-0.jpg, output-1.jpg, ...image_jpeg.save(filename="output.jpg")# 将图片解析为二进制矩阵image_lst = []for img in image_jpeg.sequence:img_page = Image(image=img)image_lst.append(img_page.make_blob('jpeg'))# 用 io 模块的 BytesIO 方法读取二进制内容列表的第一个为图片形式
new_img = PI.open(io.BytesIO(image_lst[0]))# 获取图片尺寸
width, height = new_img.size
print(f"PDF 转换成功,图片尺寸: {width} x {height}")
print(f"图片模式: {new_img.mode}\n")# ========== 新增功能:生成带网格的图片 ==========
# 创建一个带网格的图片,方便查看坐标
grid_img = new_img.copy()
draw = ImageDraw.Draw(grid_img)# 绘制网格线(每 50 像素一条)
grid_step = 50
for x in range(0, width, grid_step):draw.line([(x, 0), (x, height)], fill='red', width=2)# 标注 x 坐标draw.text((x + 5, 10), str(x), fill='red')for y in range(0, height, grid_step):draw.line([(0, y), (width, y)], fill='red', width=2)# 标注 y 坐标draw.text((10, y + 5), str(y), fill='red')# 保存网格图片
grid_img.save('coordinates_grid.jpg')
print(f"已生成网格图片: coordinates_grid.jpg\n")# ========== 自动检测页面类型 ==========
if height > 3000:page_type = 'FULL'coords = COORDS_FULLprint(f"检测到完整页发票 (高度 {height} > 3000)")
elif height > 1500:page_type = 'HALF'coords = COORDS_HALFprint(f"检测到半页发票 (高度 {height} 在 1500-3000 之间)")
else:print(f"未知的页面尺寸: {width} x {height}")sys.exit(1)print(f"使用 {page_type} 页坐标配置\n")# ========== 安全的 OCR 识别函数 ==========
def safe_ocr(img, coords, field_name):"""安全的 OCR 识别,检查坐标是否在范围内"""left, top, right, bottom = coords# 检查坐标是否在图片范围内if left < 0 or top < 0 or right > img.width or bottom > img.height:print(f"  {field_name}: 坐标超出范围 {coords},图片尺寸 {img.width} x {img.height}")return Nonetry:# 裁剪并识别cropped = img.crop(coords)# 保存裁剪图片用于调试debug_filename = f"debug_{field_name}.jpg"cropped.save(debug_filename)# OCR 识别text = tool.image_to_string(cropped, lang=lang)text = text.strip()if text:print(f"  {field_name}: {text}")return textelse:print(f"  {field_name}: (识别为空)")return Noneexcept Exception as e:print(f"  {field_name}: 识别失败 - {str(e)}")return None# ========== 识别各个字段 ==========
print("开始识别字段:")
print("="*60)# 总金额
txt1 = safe_ocr(new_img, coords['total_amount'], '总金额')# 纳税人识别号
txt2 = safe_ocr(new_img, coords['tax_id'], '纳税人识别号')# 开票人
txt3 = safe_ocr(new_img, coords['issuer'], '开票人')print("="*60)# ========== 写入 Excel ==========
workbook = Workbook()
sheet = workbook.active
header = ['字段', '值', '页面类型', '坐标']
sheet.append(header)sheet.append(['总金额', txt1 or '', page_type, str(coords['total_amount'])])
sheet.append(['纳税人识别号', txt2 or '', page_type, str(coords['tax_id'])])
sheet.append(['开票人', txt3 or '', page_type, str(coords['issuer'])])excel_path = GetDesktopPath() + r'\发票信息_自适应.xlsx'
workbook.save(excel_path)
print(f'\n数据已保存到: {excel_path}')# ========== 提示信息 ==========
if page_type == 'HALF':print("\n" + "="*60)print("注意:检测到半页发票")print("="*60)print("如果识别结果不正确,请:")print("1. 打开 coordinates_grid.jpg 查看网格")print("2. 找到字段的实际位置")print("3. 更新本文件中的 COORDS_HALF 配置")print("="*60)

对于电子发票信息识别提取,上面的方案有些过时,但也不失为一种编程练习。

http://www.hskmm.com/?act=detail&tid=35914

相关文章:

  • 垃圾回收算法
  • 2025年臭氧检测仪厂家权威推荐榜:在线式/固定式/便携式/手持式/工业臭氧检测仪专业选购指南
  • 2025年拖鞋机厂家权威推荐榜:酒店拖鞋生产线,全自动拖鞋机,一次性拖鞋机,酒店一次性拖鞋机器专业选购指南
  • 生成式 AI 重构内容创作:从辅助工具到智能工厂 - 实践
  • 2025年不锈钢酸洗钝化液厂家推荐排行榜:环保型不锈钢清洗钝化液,不锈钢管酸洗钝化处理,不锈钢清洗剂专业选购指南
  • 达梦8加密函数是什么怎么调用,达梦数据库加密算法
  • 基于Windows,Docker用法
  • 厨房电子秤方案:厨房秤常规的功能有那些?
  • npx和npm exec有什么区别
  • MySQL 死锁 怎么处理?
  • MyBatis 的 @SelectProvider 是一个强大的注解,用于动态生成 SQL 语句
  • 跨境客服系统如何保障国际数据传输安全?
  • 物联网短信收发速成:10分钟用SMS库上手实战
  • 2025年耳机插座厂家权威推荐榜:DC防水耳机插座,专业防水防尘设计,耐用稳定性能卓越之选
  • 2025年10月18日,工信部人才交流中心PostgreSQL认证考试完成!
  • 2025年CNC加工厂家权威推荐榜:CNC精密加工/加工中心CNC/cnc电脑锣加工/铝板cnc加工/精密CNC加工源头企业综合评测
  • Yolo11分类模型
  • 市面上的开源 AI 智能体平台使用体验
  • 简支梁在荷载作用下的变形计算
  • 2025年真空烧结炉厂家权威推荐榜单:高效节能、智能温控、工业窑炉设备优质供应商精选
  • 基于 tar.gz 的自定义 安装InfluxDB
  • 2025年移动泵车厂家推荐排行榜,防汛泵车,水泵机组,应急排水泵车,柴油机泵车公司精选
  • Oracle 触发器
  • 2025年铁氟龙高温线厂家推荐排行榜,铁氟龙/极细铁氟龙/UL10064铁氟龙/UL1332铁氟龙/UL1867铁氟龙公司推荐
  • Slope Trick
  • 阅读笔记二:高效编程的核心策略
  • OpenAI 发布 GPT-5 Instant:AI 有了 “情感温度计“ - 实践
  • 线性代数 SVD | 几何本质、求解方法与应用 - 教程
  • SG 函数
  • 2025 年铝包木阳光房生产厂家最新推荐榜:口碑至上的实力品牌甄选及选购指南