C#使用Tesseract C++ API过程记录
Tesseract
Tesseract 是一个开源的光学字符识别(OCR)引擎,最初由 Hewlett-Packard(惠普)实验室开发,后来由 Google 收购并继续维护和开源贡献。Tesseract 可以识别多种语言的文字,广泛应用于将图片或扫描文档中的文本内容转换成可编辑的文本格式。随着深度学习技术的发展,Tesseract 也整合了基于深度神经网络的 OCR 模型,提升其识别准确率,特别是对于复杂排版和手写体的识别效果有所改善。
Tesseract 适合开发人员和研究人员使用,可以嵌入到各种应用中,比如文档数字化、图像处理软件、内容管理系统等。它支持命令行操作,也提供了丰富的 API 接口,支持 C++、Python、Java、Node.js 等多种编程语言,便于集成和调用。Tesseract 的核心功能包括文本检测、字符识别和后处理纠错,能够处理多种图像输入格式,输出包括纯文本、HOCR(HTML + OCR)格式、PDF 等多种格式。Tesseract 的高灵活性和强大的识别能力使其成为 OCR 领域中非常受欢迎的工具之一。
GitHub地址:https://github.com/tesseract-ocr/tesseract

Tesseract提供了丰富的 API 接口,支持 C++、Python、Java、Node.js 等多种编程语言,没有C#的,实际上已经有大佬做了C#的封装了,并提供了一个示例项目,需要只是简单使用一下,用这个大佬的就很方便了。
感兴趣的可以瞧瞧:
项目GitHub地址:https://github.com/charlesw/tesseract

示例GitHub地址:https://github.com/charlesw/tesseract-samples

但这不是我们今天的主题,现在还处于学习阶段,能直接使用大佬的库确实很方便,但是如果自己能够知道大佬是怎么实现的,那不是也很酷吗?
实现的方式与大佬项目的方式是类似的,如下所示:

需要依赖leptonica-1.82.0.dll与tesseract50.dll,然后通过DllImport导入其中的C++函数。
已经有现成的库了为什么不直接使用呢?
第一,项目中可能只需要用到Tesseract的几个C++ API而已,直接引用一大堆东西没有必要。第二,学习阶段,以自己学习掌握技能为主,自己先掌握了这项技能,然后偷懒了直接使用大佬的库也不迟。
Windows编译Tesseract
首先我们需要先在Windows上编译Tesseract,官方文档有一些介绍,文档地址:https://tesseract-ocr.github.io/tessdoc/Compiling.html。
查看文档之后,我使用这种方式:

先来简单介绍一下vcpkg。
vcpkg
vcpkg是一个用来管理C++库的跨平台包管理工具,由微软开发并维护,旨在帮助开发者简化第三方库的集成和使用过程。vcpkg通过提供预编译的二进制包和源代码,使开发者能够在Windows、Linux和macOS等操作系统上轻松安装和管理C++库。它支持多种编译器,包括Visual Studio、GCC和Clang。vcpkg的使用非常简单,只需要下载并安装,然后通过命令行工具指定要安装的库名,vcpkg会自动下载、编译并安装所需的库及其依赖项。此外,vcpkg还具有版本控制功能,能够方便地切换库的不同版本。它对于提升开发效率、保持项目的一致性以及解决跨平台开发中的库兼容性问题非常有帮助。许多开源项目和商业软件都选择使用vcpkg来管理和分发依赖库。
使用vcpkg安装Tesseract
git clone https://github.com/microsoft/vcpkg.git
cd vcpkg; .\bootstrap-vcpkg.bat
vcpkg install tesseract:x64-windows
安装完成:

vcpkg integrate install

为这个 vcpkg 根目录应用了全局用户集成。 CMake 项目应使用:"-DCMAKE_TOOLCHAIN_FILE=D:/Learning/vcpkg/scripts/buildsystems/vcpkg.cmake"
现在所有 MSBuild C++ 项目都可以 #include 任何已安装的库。链接将会自动处理。安装新库后,它们将立即可用。
vcpkg list

新建一个C++项目使用Tesseract C++ API
我写了两个简单的函数用于测试。
头文件:
#pragma once
extern "C" __declspec(dllexport) char* getChineseText(const char* imagePath);
extern "C" __declspec(dllexport) char* getEnglishText(const char* imagePath);
extern "C" __declspec(dllexport) void freeMemory(char* ptr);
源文件:
#include <tesseract/baseapi.h>
#include <leptonica/allheaders.h>
#include "test.h"
#include <iostream>
void windows_cmd_support_utf8(void)
{
system("chcp 65001 & cls"); //cls 用来清除 chcp 的输出
}
char* getEnglishText(const char* imgPath) {
tesseract::TessBaseAPI* api = new tesseract::TessBaseAPI();
if (api->Init(NULL, "eng")) {
fprintf(stderr, "Could not initialize tesseract.\n");
delete api;
return nullptr;
}
Pix* image = pixRead(imgPath);
if (!image) {
fprintf(stderr, "Could not read image file.\n");
api->End();
delete api;
return nullptr;
}
api->SetImage(image);
char* outText = api->GetUTF8Text();
if (!outText) {
fprintf(stderr, "OCR failed.\n");
api->End();
pixDestroy(&image);
delete api;
return nullptr;
}
api->Clear();
api->End();
delete api;
pixDestroy(&image);
return outText;
}
char* getChineseText(const char* imgPath) {
tesseract::TessBaseAPI* api = new tesseract::TessBaseAPI();
if (api->Init(NULL, "chi_sim")) {
fprintf(stderr, "Could not initialize tesseract.\n");
delete api;
return nullptr;
}
Pix* image = pixRead(imgPath);
if (!image) {
fprintf(stderr, "Could not read image file.\n");
api->End();
delete api;
return nullptr;
}
api->SetImage(image);
char* outText = api->GetUTF8Text();
if (!outText) {
fprintf(stderr, "OCR failed.\n");
api->End();
pixDestroy(&image);
delete api;
return nullptr;
}
api->Clear();
api->End();
delete api;
pixDestroy(&image);
return outText;
}
void freeMemory(char* ptr) {
delete[] ptr;
}
int main()
{
const char* imgPath = "D:\\SemanticKernel-Test2.png"; // 替换为你的图像文件路径
const char* imgPath2 = "D:\\test666.png"; // 替换为你的图像文件路径
// 第一次调用
char* result1 = getChineseText(imgPath);
windows_cmd_support_utf8();
std::cout << "OCR Result 1: " << result1 << std::endl;
// 第二次调用
char* result2 = getChineseText(imgPath2);
std::cout << "OCR Result 2: " << result2 << std::endl;
// 释放内存
//freeMemory(result1);
//freeMemory(result2);
return 0; // 程序正常结束
}
注意
先将项目配置成X64:

现在运行项目,会出现一个错误,因为并没有包含tessdata。
tessdata 是 Tesseract OCR 引擎使用的一种数据文件格式,用于存储语言模型和字符识别数据。Tesseract 通过加载这些数据文件来实现对不同语言文字的识别。每个语言都有一套对应的 tessdata 文件,通常命名为 lang.traineddata,其中 lang 是语言的缩写(例如,eng 表示英语,chi_sim 表示简体中文)。
tessdata的GitHub地址:https://github.com/tesseract-ocr/tessdata

也可以看我后面分享的GitHub,一般只要中英文就可以了,如下所示:

将tessdata文件夹放在x64的Debug目录下即可。
先测试中文识别效果:
测试图片1

测试图片2

查看效果:

注意
如果不加上windows_cmd_support_utf8();
就会出现乱码,如下所示:

并不是Tesseract识别中文效果不好,只是控制台默认没支持utf-8罢了。
再来测试一下英文:
测试图片1

测试图片2

效果如下所示:

生成DLL
测试没有问题之后,现在需要生成DLL文件。
右键项目,点击属性,设置配置类型为DLL:

生成解决方案之后,如下所示:

这里需要搞清楚的是为什么头文件不用std::string而是char*呢?
#pragma once
extern "C" __declspec(dllexport) char* getChineseText(const char* imagePath);
extern "C" __declspec(dllexport) char* getEnglishText(const char* imagePath);
extern "C" __declspec(dllexport) void freeMemory(char* ptr);
extern "C":这告诉编译器这些函数应该按照C语言的方式进行链接,而不是C++的方式。这样可以确保这些函数在C语言中也可以被正确调用。- 简单的说std::string不是C语言风格的,所以不行。
在C#项目中调用C++ DLL
新建一个C#控制台项目用于测试。
测试代码如下:
using System.Runtime.InteropServices;
class Program
{
[DllImport("TesseractApiTest.dll", CallingConvention = CallingConvention.Cdecl, CharSet = CharSet.Ansi)]
public static extern IntPtr getEnglishText(string imagePath);
[DllImport("TesseractApiTest.dll", CallingConvention = CallingConvention.Cdecl, CharSet = CharSet.Ansi)]
public static extern IntPtr getChineseText(string imagePath);
static void Main()
{
string imagePath = @"D:\SemanticKernel-Test2.png";
// 调用 DLL 中的函数
IntPtr resultPtr = getChineseText(imagePath);
if (resultPtr == IntPtr.Zero)
{
return;
}
string? result = Marshal.PtrToStringUTF8(resultPtr);
return; ;
}
}
将项目设置成X64平台,将C++项目的所有输出文件,复制到X64的输出目录这里,如下所示:

为了避免太混乱,本来想新建一个Libs文件夹,在放这些文件,在设置为嵌入的资源与如果较新就复制,但是就调用不了了,暂时没有解决,只能这样一堆放在这里了。
效果如下:

这里需要注意一下,为什么是
[DllImport("TesseractApiTest.dll", CallingConvention = CallingConvention.Cdecl, CharSet = CharSet.Ansi)]
public static extern IntPtr getEnglishText(string imagePath);
而不是
[DllImport("TesseractApiTest.dll", CallingConvention = CallingConvention.Cdecl, CharSet = CharSet.Ansi)]
public static extern string getEnglishText(string imagePath);
在C#中直接使用 string 作为返回类型并不适用于从C++导出的函数,尤其是当该函数返回的是一个 char* 类型的指针时。原因在于 char* 是一个指向C风格字符串的指针,而C#中的 string 类型与C风格字符串并不直接兼容。C#的 string 类型是一个托管的字符串对象,而 char* 是一个非托管的指针,直接进行转换会导致运行时错误或无法预期的行为。
使用 IntPtr 作为返回类型可以解决这个问题,因为 IntPtr 是一个可以表示非托管指针的类型。你可以通过 Marshal 类将 IntPtr 转换为C#中的 string。这样可以确保你在C#中能够正确处理C++函数返回的字符串指针。
TesseractOCR-GUI中集成
之前跟大家分享的TesseractOCR-GUI需要在电脑上安装Tesseract才能用,因为只是简单的对Tesseract的命令行使用做了封装,现在通过这种方法,不需要安装Tesseract也能使用了。
GitHub地址:https://github.com/Ming-jiayou/TesseractOCR-GUI。

git clone到本地,然后将平台设置成X64,先生成解决方案,然后将Libs文件夹下的内容,全部复制到x64的输出目录,如下所示:


现在直接点击应该就可以使用了:

效果如下所示:


速度还是很快的,目前为止我们尝试了TesseractOCR、PaddleOCR、VLM,其中TesseractOCR我感觉是最快的。
以上就是本期的分享内容,希望对你有所帮助。
推荐阅读:
C#调用Python脚本的方式(一),以PaddleOCR-GUI为例
C#调用Python代码的方式(二),以PaddleOCR-GUI为例
VLM-OCR-Demo:一个使用VLM用于OCR任务的示例
TesseractOCR-GUI:基于WPF/C#构建TesseractOCR简单易用的用户界面
C#使用Tesseract C++ API过程记录的更多相关文章
- CentOS 5.5 下安装Countly Web Server过程记录
CentOS 5.5 下安装Countly Web Server过程记录 1. 系统更新与中文语言包安装 2. 基本环境配置: 2.1. NodeJS安装 依赖项安装 yum -y install g ...
- 升级到 ExtJS 5的过程记录
升级到 ExtJS 5的过程记录 最近为公司的一个项目创建了一个 ExtJS 5 的分支,顺便记录一下升级到 ExtJS 5 所遇到的问题以及填掉的坑.由于 Sencha Cmd 的 sencha ...
- 一个flume agent异常的解决过程记录
今天在使用flume agent的时候,遇到了一个异常, 现把解决的过程记录如下: 问题的背景: 我使用flume agent 来接收从storm topology发送下来的accesslog , ...
- webflux延迟队列逻辑更改过程记录
title : webflux延迟队列逻辑更改过程记录 author : simonLee date : 2022/11/22 10:26 目录 webflux延迟队列逻辑更改过程记录 一.问题背景 ...
- 升级Windows 10 正式版过程记录与经验
升级Windows 10 正式版过程记录与经验 [多图预警]共50张,约4.6MB 系统概要: 预装Windows 8.1中文版 64位 C盘Users 文件夹已经挪动到D盘,并在原处建立了符号链接. ...
- 双系统Ubuntu分区扩容过程记录
本人电脑上安装了Win10 + Ubuntu 12.04双系统.前段时间因为在Ubuntu上做项目要安装一个比较大的软件,导致Ubuntu根分区的空间不够了.于是,从硬盘又分出来一部分空间,分给Ubu ...
- linux-i386(ubuntu)下编译安装gsoap_2.8.17过程记录
过程记录 : 1.下载gsoap_2.8.17.zip 并 解压 : $unzip gsoap_2.8.17.zip 2.进入解压后的目录gsoap-2.8 3.自动配置编译环境: $ ...
- 【转】android 最新 NDK r8 在window下开发环境搭建 安装配置与使用 详细图文讲解,完整实际配置过程记录(原创)
原文网址:http://www.cnblogs.com/zdz8207/archive/2012/11/27/android-ndk-install.html android 最新 NDK r8 在w ...
- Ubuntu14.04 Tomcat 安装过程记录
Ubuntu14.04 Tomcat 安装过程记录 检查java的版本 zhousp@ubuntu:~$ sudo java -version [sudo] password for zhousp: ...
- mercurial(Hg) Server 搭建 过程记录
mercurial(Hg) Server 搭建 过程记录 1. 环境说明 只是测试搭建,环境为本机开发环境:win 8.1 + IIS8.5 软件准备: 2. 软件安装 先安装Python2.7, ...
随机推荐
- 通过自定义字符串内插处理程序(InterpolatedStringHandler)和CallerArgumentExpression特性来实现一个好玩的场景
背景知识介绍 什么是自定义字符串内插处理程序? 简单来讲就是自定义一个高性能的字符串拼接程序 通过 $"{a}{b}"的方式. 什么是CallerArgumentExpressio ...
- 云原生周刊:Kubernetes 1.30 的一切新功能 | 2024.4.1
开源项目推荐 Kubernetes scheduler simulator 该项目是一个用于模拟 Kubernetes 调度器行为的开源项目,可用于测试和评估调度器的性能和行为.它提供了一个模拟集群和 ...
- centos下搭建php开发环境(lamp)
由于个人非常喜欢爱linux系开发php项目. 因为某些原因,经常需要手动搭建环境 经常在网上找到的教程经常不太一样 虽然最终都能完成搭建,但是总是觉得不太爽 还不如自己写一篇,需要的时候肯定能找到 ...
- 两个时间段比较的六种情况,以及交集、并集、补集简要sql语句示例
〇.两时间段比较的全部情况 总共有如下图中的六种情况: 下文将根据这六种情况进一步操作. 注意,图中说的动态和固定两时间段,就是两个普通时间段,不区分主次,仅用作帮助理解. 一.判断两个时间段是否有交 ...
- .NET使用OllamaSharp实现大模型推理对话的简单演示
前提条件:请确保你本地已经安装了ollama以及有关本地离线模型.或者已有远程模型环境等.如果没有,请自行部署.如果需要帮助,可以文末获取联系方式咨询.由于部署离线大模型过于简单,在线资料也很多, ...
- JS函数:递归函数与迭代函数
1.递归函数 : 程序中调用自己的函数 程序调用自身的编程技巧称为 递归( recursion).递归作为一种算法在程序设计语言中广泛应用. 一个过程或函数在其定义或说明中有直接或间接调用自身的一种方 ...
- [python]Gunicorn加持,轻松提升Flask超7倍性能
前言 之前学习和实际生产环境的flask都是用app.run()的默认方式启动的,因为只是公司内部服务,请求量不高,一直也没出过什么性能问题.最近接管其它小组的服务时,发现他们的服务使用Gunicor ...
- java之封装日期工具类DateUtils
整合日期工具类DateUtils dateUtils日期工具类封装-各类格式方法 pom文件引入 <dependency> <groupId>cn.hutool</gro ...
- swiper 垂直滚动
<template> <div style="height: 100%;width: 100%;"> <swiper :direction=" ...
- QT 6.8 安卓 Android 环境安装配置,你踩了几个坑,我教你跳出来,早看不入坑… …
安装了QT6.8 最新版本,在线安装,用了数天后,想开始写一个Android程序,发现还在配置环境才可以继续,于是就开始配置: 菜单:编辑 -->preferences-->设备--> ...