使用 C++/OpenCV 构建中文 OCR 系统:实现账单、发票及 PDF 读取

在当今数字化浪潮中,自动从文档中提取信息至关重要,尤其是在处理大量账单、发票和 PDF 文件时。光学字符识别(OCR)技术是实现这一目标的核心。本文将详细介绍如何利用 C++ 和强大的计算机视觉库 OpenCV,构建一个专门用于读取中文账单、发票和 PDF 的 OCR 系统。

核心技术栈

我们的系统将主要围绕以下核心技术构建:

  • C++: 作为主要的编程语言,C++ 以其高性能和对系统底层资源的强大控制力而著称,非常适合构建计算密集型的计算机视觉应用。
  • OpenCV: 一个开源的计算机视觉和机器学习软件库。它提供了丰富的图像处理和计算机视觉算法,是本项目的基石。
  • OCR 引擎: 我们将重点探讨两种主流的、支持中文识别的 OCR 引擎:Tesseract 和 PaddleOCR。它们都可以与 C++ 和 OpenCV 良好集成。
  • PDF 处理库 (Poppler): 由于 OCR 引擎通常处理的是图像文件,我们需要一个库来将 PDF 文件页面转换为可供 OpenCV 处理的图像。Poppler 是一个优秀的选择。

系统构建流程

构建一个完整的文档 OCR 系统,大致可以分为以下几个步骤:

  1. 环境搭建: 配置 C++ 编译环境,安装 OpenCV、Tesseract 或 PaddleOCR,以及 Poppler 库。
  2. 输入处理: 读取图像文件或将 PDF 页面转换为 OpenCV 的 Mat 图像格式。
  3. 图像预处理: 对图像进行一系列优化操作,以提高 OCR 的准确率。
  4. 文本检测与定位: 在图像中找到包含文本的区域。
  5. 文本识别: 对检测到的文本区域进行 OCR 识别。
  6. 版面分析与结构化输出 (关键步骤): 对于账单和发票等结构化文档,识别出关键字段(如发票号码、日期、金额等)并以结构化数据(如 JSON)格式输出。

第一步:环境搭建

在开始编码之前,您需要确保开发环境中已经安装了所有必要的库。

对于 Tesseract:

您需要安装 Tesseract OCR 引擎及其开发库,同时下载中文语言包 (chi_sim.traineddata)。

  • 在 Ubuntu/Debian 系统中:
    sudo apt-get update
    sudo apt-get install -y tesseract-ocr libtesseract-dev
    sudo apt-get install -y tesseract-ocr-chi-sim
    
  • 在 Windows 系统中:
    可以从 Tesseract 的官方 GitHub 仓库下载预编译的二进制文件,并确保将 Tesseract 的安装路径添加到系统环境变量中。同时,需要获取中文语言包。

对于 PaddleOCR:

您需要下载 PaddleOCR 的 C++ 预测库。PaddleOCR 的 GitHub 仓库中提供了详细的编译和部署文档,包括如何在 Windows (Visual Studio) 和 Linux 环境下进行编译。

安装 OpenCV:

可以从 OpenCV 官网下载源码进行编译,也可以使用包管理器进行安装。

安装 Poppler:

  • 在 Ubuntu/Debian 系统中:
    sudo apt-get install -y libpoppler-cpp-dev
    
  • 在 Windows 系统中:
    可以下载预编译的二进制文件,并配置好头文件和库文件的路径。

第二-三步:输入处理与图像预处理

处理 PDF 文件

OCR 引擎直接处理的是图像。因此,第一步是将 PDF 文件转换为图像。借助 Poppler 库,我们可以轻松实现这一点。

#include <iostream>
#include <opencv2/opencv.hpp>
#include <poppler-cpp.h>cv::Mat convert_pdf_page_to_image(const std::string& pdf_file, int page_number, int dpi = 300) {poppler::document* doc = poppler::document::load_from_file(pdf_file);if (!doc || doc->is_locked()) {std::cerr << "Error: Cannot open PDF file." << std::endl;return cv::Mat();}if (page_number < 0 || page_number >= doc->num_pages()) {std::cerr << "Error: Invalid page number." << std::endl;delete doc;return cv::Mat();}poppler::page* page = doc->create_page(page_number);if (!page) {std::cerr << "Error: Cannot create page." << std::endl;delete doc;return cv::Mat();}poppler::page_renderer renderer;renderer.set_render_hint(poppler::page_renderer::antialiasing, true);renderer.set_render_hint(poppler::page_renderer::text_antialiasing, true);poppler::image image = renderer.render_page(page, dpi, dpi);if (!image.is_valid()) {std::cerr << "Error: Cannot render page." << std::endl;delete page;delete doc;return cv::Mat();}cv::Mat cv_image;if (image.format() == poppler::image::format_rgb24) {cv_image = cv::Mat(image.height(), image.width(), CV_8UC3, image.data());cv::cvtColor(cv_image, cv_image, cv::COLOR_RGB2BGR); // Poppler a RGB, OpenCV a BGR} else if (image.format() == poppler::image::format_argb32) {cv_image = cv::Mat(image.height(), image.width(), CV_8UC4, image.data());cv::cvtColor(cv_image, cv_image, cv::COLOR_BGRA2BGR);} else {std::cerr << "Error: Unsupported image format from PDF." << std::endl;}delete page;delete doc;return cv_image.clone();
}
图像预处理

高质量的图像是 OCR 成功的关键。对于扫描的账单和发票,通常需要进行以下预处理步骤:

  • 灰度化: 将彩色图像转换为灰度图像,以减少计算复杂性。
  • 二值化: 将灰度图像转换为黑白图像。自适应阈值二值化(cv::adaptiveThreshold)对于处理光照不均的文档尤为有效。
  • 噪声消除: 使用高斯模糊 (cv::GaussianBlur) 或中值滤波 (cv::medianBlur) 去除图像中的随机噪声。
  • 倾斜校正: 检测文档的倾斜角度并进行旋转校正。可以通过霍夫变换 (cv::HoughLinesP) 检测直线或使用最小面积外接矩形 (cv::minAreaRect) 来实现。
cv::Mat preprocess_image(const cv::Mat& input_image) {cv::Mat gray, blurred, thresholded;// 1. 灰度化cv::cvtColor(input_image, gray, cv::COLOR_BGR2GRAY);// 2. 高斯模糊去噪cv::GaussianBlur(gray, blurred, cv::Size(5, 5), 0);// 3. 自适应阈值二值化cv::adaptiveThreshold(blurred, thresholded, 255, cv::ADAPTIVE_THRESH_GAUSSIAN_C,cv::THRESH_BINARY, 11, 2);// 可选:进行倾斜校正等更复杂的操作return thresholded;
}

第四-五步:文本检测与识别

在进行 OCR 之前,先检测出文本所在的位置可以显著提高效率和准确性,避免对非文本区域进行识别。

文本检测

OpenCV 的 DNN 模块提供了多种预训练的文本检测模型,其中 EAST (Efficient and Accurate Scene Text Detector) 模型是一个不错的选择。

使用 PaddleOCR 进行识别

PaddleOCR 提供了集成的文本检测和识别功能,其 C++ 部署方案性能优越,对中文场景有很好的优化。使用 PaddleOCR,您可以直接输入预处理后的图像,它会返回包含位置和文本内容的结构化结果。

使用 Tesseract 进行识别

如果您选择使用 Tesseract,可以先用 EAST 模型检测出文本框,然后将每个文本框的区域 cv::Rect 传入 Tesseract 进行识别。

#include <tesseract/baseapi.h>// ... 假设已经通过文本检测获得了文本框 a_text_roi (cv::Rect) ...// 初始化 Tesseract
tesseract::TessBaseAPI* ocr = new tesseract::TessBaseAPI();
// "chi_sim" 代表简体中文
if (ocr->Init(NULL, "chi_sim", tesseract::OEM_LSTM_ONLY)) {std::cerr << "Could not initialize tesseract." << std::endl;// ... 错误处理 ...
}
ocr->SetPageSegMode(tesseract::PSM_SINGLE_BLOCK);// 提取 ROI
cv::Mat roi_image = preprocessed_image(a_text_roi);// 设置图像进行识别
ocr->SetImage(roi_image.data, roi_image.cols, roi_image.rows, roi_image.channels(), roi_image.step);// 获取识别结果
char* out_text = ocr->GetUTF8Text();
std::string result_text = std::string(out_text);// 销毁 Tesseract 实例
ocr->End();
delete ocr;
delete[] out_text;

第六步:版面分析与结构化输出

对于账单和发票,仅仅获取所有文字是不够的,我们还需要理解它们的含义。版面分析是实现这一目标的关键。

基于规则和模板的方法

对于格式相对固定的发票,这是一种简单有效的方法。

  1. 定义关键字段: 首先确定您需要提取的关键信息,例如“发票代码”、“发票号码”、“开票日期”、“金额合计”等。
  2. 定位关键字: 在 OCR 结果中搜索这些关键字。
  3. 相对位置提取: 根据关键字的位置,利用其相对空间关系来定位和提取目标信息。例如,“发票号码”通常位于“发票号码:”这个标签的右侧或下方。
机器学习方法

对于格式多变的文档,可以训练机器学习模型(如基于图神经网络 GNN 的模型)来理解文档布局和字段间的关系,但这需要大量的标注数据和更复杂的实现。

结构化输出

将提取到的信息以 JSON 格式输出,便于后续的系统集成和数据分析。

{"invoice_code": "010002100311","invoice_number": "81804581","issue_date": "2025-06-20","total_amount": "1170.00","items": [{"description": "技术服务费","amount": "1000.00"},{"description": "税额","amount": "170.00"}]
}

总结与展望

使用 C++ 和 OpenCV 构建一个中文文档 OCR 系统是一个涉及多个步骤的综合性项目。通过结合强大的开源工具如 Tesseract、PaddleOCR 和 Poppler,我们可以创建一个高效、准确的解决方案来自动化处理账单、发票和 PDF 文件。

对于追求更高准确率和更强泛化能力的场景,可以进一步探索:

  • 深度学习驱动的版面分析: 训练专门用于文档理解的深度学习模型。
  • 模型微调: 使用您自己收集和标注的数据对预训练的 OCR 模型进行微调,以适应特定类型的文档。
  • 自然语言处理 (NLP) 后处理: 利用 NLP 技术对 OCR 结果进行校正和信息提取,进一步提升最终输出的质量。

希望这篇指南能为您在 C++ 环境下进行中文 OCR 开发提供一个清晰的路线图。

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

如若内容造成侵权/违法违规/事实不符,请联系英文站点网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

windows配置Jenkins自动化定时任务+测试报告发送

一、Jenkins的安装步骤 JDK安装 没有JDK的先安装JDKhttps://adoptium.net/zh-CN/temurin/releases/?osany&archany&version21 下载Jenkins 由于JDK 1.8仅适配Jenkins 2.357之前的版本&#xff08;如2.346.1及以下&#xff09;&#xff0c;需从旧版本渠道下载&#xf…

预训练语言模型基础知识概述

文章目录 预处理语言模型的发展预训练语言模型统计语言模型神经网络语言模型 词向量onehot编码词嵌入word embedding Word2Vec模型RNN和LSTMRNNLSTM ELMo模型预训练下游任务 Attention自注意力Masked Self AttentionMulti-head Self Attention 位置编码Transformer概念GPT概念B…

浏览器工作原理24 [#]分层和合成机制:为什么css动画比JavaScript高效

引用 《浏览器工作原理与实践》 本文主要讲解渲染引擎的分层和合成机制&#xff0c;因为分层和合成机制代表了浏览器最为先进的合成技术&#xff0c;Chrome 团队为了做到这一点&#xff0c;做了大量的优化工作。了解其工作原理&#xff0c;有助于拓宽你的视野&#xff0c;而且也…

【数字后端】-什么是RC corner? 每种Corner下有什么区别?

芯片的寄生参数可以在多个corner下提取&#xff0c;他们对应了不同情况的net delay Typical&#xff1a;R和C都是标准值Cmax(Cworst)&#xff1a;C最大的互连角&#xff0c;R小于TypicalCmin(Cbest)&#xff1a;C最小&#xff0c;R大于TypicalRCmax(RCworst)&#xff1a;互连线…

HarmonyOS开发基础 --鸿蒙仓颉语言基础语法入门

仓颉编程语言是华为自主研发的一种面向全场景应用开发的现代编程语言&#xff0c;具有高效编程、安全可靠、轻松并发和卓越性能等特点。本节将简单介绍仓颉语言的部分语法和使用&#xff0c;帮助开发者快速上手。 1.3.1&#xff1a;数据类型 整数 仓颉把整数分为有符号和无符…

Excel文件比较器v1.3,html和js写的

Excel文件比较器v1.3 版本说明&#xff1a;v1.3 1添加支持文件格式&#xff1a;CSV。 2&#xff0c;添加60条历史记录保存功能 - 用于保存比对结果。历史记录保存在浏览器的localStorage中&#xff0c;这是一个浏览器提供的本地存储机制&#xff0c;数据会一直保留直到用户…

Kimi“新PPT助手” ,Kimi全新自研的免费AI生成PPT助手

大家好&#xff0c;这里是K姐。 一个帮你用AI轻松生成精美PPT的女子。 前段时间给大家分享了一期用智能体做PPT的对比测评&#xff0c;很多友友都表示&#xff1a;那 Kimi 呢&#xff1f; 今天偶然发现 Kimi 新增了一个叫“新PPT助手”的功能&#xff0c;立马上手体验了一下…

MySQL DATETIME类型存储空间详解:从8字节到5字节的演变

在MySQL数据库设计中&#xff0c;DATETIME类型用于存储日期和时间信息&#xff0c;但其存储空间大小并非固定不变&#xff0c;而是随MySQL版本迭代和精度定义动态变化。本文将详细说明其存储规则&#xff0c;并提供清晰的对比表格。 一、核心结论 MySQL 5.6.4 是分水岭&#…

Gartner发布中国企业应用生成式AI指南:避免12 个 GenAI 陷阱

GenAI 技术&#xff08;例如 AI 代理和 DeepSeek&#xff09;的快速迭代导致企业抱有不切实际的期望。本研究借鉴了我们与中国 AI 领导者就常见的 GenAI 陷阱进行的讨论&#xff0c;并提供了最终有助于成功采用的建议。 主要发现 接受调查的首席信息官表示&#xff0c;生成式人…

Vue3中ref和reactive的区别与使用场景详解

在 Vue 3 中&#xff0c;响应式系统进行了全新设计&#xff0c;ref 和 reactive 是其中的核心概念。 ### 一、ref 的使用 ref 适用于基本数据类型&#xff0c;也可以用于对象&#xff0c;但返回的是一个带 .value 的包装对象。 js import { ref } from vue const count ref(…

React性能优化:父组件如何导致子组件重新渲染及避免策略

目录 React性能优化&#xff1a;父组件如何导致子组件重新渲染及避免策略什么是重新渲染&#xff1f;父组件如何"无辜"地让子组件重新渲染&#xff1f;示例 1: 基础父组件状态变更示例 2: 传递未变化的原始类型Prop示例 3: 传递引用类型Prop&#xff08;对象&#xf…

图的拓扑排序管理 Go 服务启动时的组件初始化顺序

在构建复杂的 Go 应用程序时&#xff0c;服务的启动过程往往涉及多个组件的初始化&#xff0c;例如日志、配置、数据库连接、缓存、服务管理器、适配器等等。这些组件之间通常存在着复杂的依赖关系&#xff1a;日志可能需要配置信息&#xff0c;数据库连接可能依赖日志和追踪&a…

【物理重建】SPLART:基于3D高斯泼溅的铰链估计与部件级重建

标题&#xff1a;《SPLART: Articulation Estimation and Part-Level Reconstruction with 3D Gaussian Splatting》 项目&#xff1a;https://github.com/ripl/splart 文章目录 摘要一、引言二、相关工作2.1 数据驱动的铰链学习2.2 物体重建的表征方法2.3 铰链物体重建 三、方…

vscode中vue自定义组件的标签失去特殊颜色高亮

遇到的问题 最近接触了一个历史遗留项目时&#xff0c;我遭遇了堪称"史诗级屎山"的代码结构——各种命名混乱的自定义组件和原生HTML标签混杂在一起&#xff0c;视觉上完全无法区分。这让我突然想起&#xff0c;之前在使用vue或者其他框架开发的时候&#xff0c;只要…

【Dify精讲】第19章:开源贡献指南

今天&#xff0c;让我们深入 Dify 的开源贡献体系&#xff0c;看看这个项目是如何在短短时间内聚集起一个活跃的开发者社区的。作为想要参与 Dify 开发的你&#xff0c;这一章将是你的实战指南。 一、代码贡献流程&#xff1a;从想法到合并的完整路径 1.1 贡献前的准备工作 …

Web攻防-CSRF跨站请求伪造Referer同源Token校验复用删除置空联动上传或XSS

知识点&#xff1a; 1、Web攻防-CSRF-原理&检测&利用&防御 2、Web攻防-CSRF-防御-Referer策略隐患 3、Web攻防-CSRF-防御-Token校验策略隐患 一、演示案例-WEB攻防-CSRF利用-原理&构造 CSRF 测试功能点 删除帐户 更改电子邮件 如果不需要旧密码&#xff0c;请…

Drag-and-Drop LLMs: Zero-Shot Prompt-to-Weights

“拖拽式大模型定制”&#xff08;Drag-and-Drop LLMs: Zero-Shot Prompt-to-Weights&#xff09;。 核心问题&#xff1a; 现在的大模型&#xff08;比如GPT-4&#xff09;很厉害&#xff0c;但想让它们专门干好某个特定任务&#xff08;比如解数学题、写代码&#xff09;&am…

抖音视频怎么去掉抖音号水印保存

随着抖音成为短视频平台的领军者&#xff0c;越来越多的人喜欢在上面拍摄、观看和分享各种创意内容。对于用户来说&#xff0c;下载抖音视频并去除水印保存&#xff0c;以便后续使用或分享成为了一种常见需求。抖音号水印的存在虽然能帮助平台追溯视频源头&#xff0c;但也让许…

【RAG技术(1)】大模型为什么需要RAG

文章目录 为什么需要RAG&#xff1f;RAG的工作原理关键的Embedding技术 RAG vs 模型微调&#xff1a;选择的核心逻辑RAG的关键挑战与解决思路1. 检索质量决定一切2. 上下文长度限制 实际应用场景分析企业知识问答技术文档助手法律咨询系统 构建RAG系统的关键步骤总结 为什么需要…

JS红宝书笔记 - 8.1 理解对象

对象就是一组没有特定顺序的值&#xff0c;对象的每个属性或者方法都可由一个名称来标识&#xff0c;这个名称映射到一个值。可以把对象想象成一张散列表&#xff0c;其中的内容就是一组名值对&#xff0c;值可以是数据或者函数 创建自定义对象的通常方式是创建Object的一个新…