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

发布于:2025-06-21 ⋅ 阅读:(11) ⋅ 点赞:(0)

使用 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 开发提供一个清晰的路线图。


网站公告

今日签到

点亮在社区的每一天
去签到