在当今数字化浪潮中,自动从文档中提取信息至关重要,尤其是在处理大量账单、发票和 PDF 文件时。光学字符识别(OCR)技术是实现这一目标的核心。本文将详细介绍如何利用 C++ 和强大的计算机视觉库 OpenCV,构建一个专门用于读取中文账单、发票和 PDF 的 OCR 系统。
我们的系统将主要围绕以下核心技术构建:
构建一个完整的文档 OCR 系统,大致可以分为以下几个步骤:
Mat
图像格式。在开始编码之前,您需要确保开发环境中已经安装了所有必要的库。
对于 Tesseract:
您需要安装 Tesseract OCR 引擎及其开发库,同时下载中文语言包 (chi_sim.traineddata
)。
sudo apt-get update
sudo apt-get install -y tesseract-ocr libtesseract-dev
sudo apt-get install -y tesseract-ocr-chi-sim
对于 PaddleOCR:
您需要下载 PaddleOCR 的 C++ 预测库。PaddleOCR 的 GitHub 仓库中提供了详细的编译和部署文档,包括如何在 Windows (Visual Studio) 和 Linux 环境下进行编译。
安装 OpenCV:
可以从 OpenCV 官网下载源码进行编译,也可以使用包管理器进行安装。
安装 Poppler:
sudo apt-get install -y libpoppler-cpp-dev
OCR 引擎直接处理的是图像。因此,第一步是将 PDF 文件转换为图像。借助 Poppler
库,我们可以轻松实现这一点。
#include
#include
#include
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 提供了集成的文本检测和识别功能,其 C++ 部署方案性能优越,对中文场景有很好的优化。使用 PaddleOCR,您可以直接输入预处理后的图像,它会返回包含位置和文本内容的结构化结果。
如果您选择使用 Tesseract,可以先用 EAST 模型检测出文本框,然后将每个文本框的区域 cv::Rect
传入 Tesseract 进行识别。
#include
// ... 假设已经通过文本检测获得了文本框 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;
对于账单和发票,仅仅获取所有文字是不够的,我们还需要理解它们的含义。版面分析是实现这一目标的关键。
对于格式相对固定的发票,这是一种简单有效的方法。
对于格式多变的文档,可以训练机器学习模型(如基于图神经网络 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 文件。
对于追求更高准确率和更强泛化能力的场景,可以进一步探索:
希望这篇指南能为您在 C++ 环境下进行中文 OCR 开发提供一个清晰的路线图。