vcpkg集成Tesseract 4.1.1:在VS2017中搭建OCR开发环境的完整指南
1. 为什么我决定用vcpkg来搞Tesseract1.1 Tesseract的依赖链有多麻烦搞OCR基本绕不开Tesseract。如果你只需要跑一个命令行识别直接下载官方Windows安装包就行。但我在做的是一个图像识别模块需要把Tesseract作为库集成进C项目这时候官方安装包就不过瘾了——它只提供可执行程序开发需要的头文件baseapi.h等、导入库tesseract41.lib以及一堆第三方依赖库leptonica、libpng、libjpeg、libtiff全都散落各处。网上能找到的第三方预编译包版本和编译器又五花八门有的用VS2015编译有的是MinGW混着用经常出现运行时崩溃。我第一天就在这些坑里进进出出最后决定换一条正经路子。Tesseract 4.x的核心是LSTM神经网络识别引擎识别效果比3.x的旧模式强不少。但它不是单文件项目首先依赖leptonica做图像预处理而leptonica又会依赖libpng、libjpeg、libtiff、zlib。换句话说想自己把整条链子都编译明白你得先搞定zlib然后libpng、libjpeg、libtiff再编译leptonica最后才轮到tesseract。全手动编一遍就算不踩坑五六个小时也是打底的而且任何一个库的编译选项变了后面全都跟着变纯粹是力气活。1.2 vcpkg到底帮了什么忙vcpkg是一个C包管理器跟Java世界的Maven、Python世界的pip定位差不多但它干的核心事情是“源码编译”。你在命令行里告诉它一个包名它会自动下载源码、解析依赖树、用本机编译器把每个依赖包都编译出来最终统一安装到固定的目录结构里。这正好解决了我前面说的“依赖地狱”问题。它对平台配置也有清晰区分triplet参数比如x64-windows表示64位动态库x64-windows-static表示静态库。这样就不用担心自己编出来的库是32位还是64位、debug还是release混不混了。vcpkg还有一个关键功能一条integrate install命令就能让Visual Studio自动加载它的头文件和库文件路径。也就是说项目里包含头文件、链接库时不需要我手动写一堆“附加包含目录”和“附加依赖项”VS会自动补上。这对VS2017这种老牌IDE来说省力非常明显。我选择它说白了就是不想再被Windows下预编译库的版本混乱逼疯。2. 环境准备win10x64和VS2017的搭配2.1 先确认机器和系统版本再动手标题里的win10 x64和VS2017不是随便写的。如果你用的是32位Win10后面vcpkg的triplet得改成x86-windows很多包会编译成32位版本。这是最容易忽略的前置条件因为你下载的源码本身没有位数概念编译器一参与出来的库就带上了平台属性。确认方法很简单右键“此电脑”选“属性”拉到最下面看“系统类型”只要写“基于x64的处理器”那目标平台就是x64。VS2017的版本号也值得注意最好是在Update 3以上。vcpkg对编译器版本有要求如果VS2017停留在RTM版或者Update 2有些C14的新特性支持不完整编译某些依赖包时会报错。我装好系统后第一件事就是打开Visual Studio Installer把“使用C的桌面开发”工作负荷勾上同时安装Windows 10 SDK。这一步千万别省少一块SDK后续编译C项目时会出现一堆“找不到windows.h”或者“缺少ucrt.h”之类的基础错误。2.2 下载并初始化vcpkgvcpkg本身不需要安装它的工作方式是把源码克隆下来然后跑一个初始化脚本。我建议放到一个没有中文和空格的路径下比如D:\dev方便后续在命令行里操作也不用担心VS对中文路径偶尔抽风。打开命令提示符依次执行git clone vcpkg官方仓库地址 cd vcpkg bootstrap-vcpkg.bat -disableMetrics这个-disableMetrics参数是关闭匿名统计数据上报不加也能跑加上纯粹是个人习惯。初始化脚本会编译vcpkg.exe整个过程可能一两分钟取决于机器性能。之后就用.\vcpkg.exe这个可执行文件操作。为了以后少敲几个字符我把D:\dev\vcpkg加进了系统PATH环境变量这样在任何目录下都能直接执行vcpkg命令。加PATH的一个小提醒改完环境变量后要重新开一个命令行窗口才会生效老窗口不会自动吃到新配置。2.3 把vcpkg集成进VS2017初始化完成后输入vcpkg integrate install如果成功命令行会显示类似“Applied user-wide integration for this machine”的提示之后VS2017创建的每个C项目都会自动带上一份名为“vcpkg”的集成信息。这里说的是用户级集成不需要把vcpkg目录加入项目也不需要在项目里单独引入VS会在后台把include目录和lib目录挂上去。不过有一个重点项目平台必须和vcpkg安装的平台配置一致。如果项目配置是Win32vcpkg绝对不会把x64-windows的库自动链进来这一点我后面吃了大苦头一会儿细说。3. 安装Tesseract 4.1.1的完整流程3.1 指定平台配置安装一切就绪后正式安装Tesseract。我在命令行里敲vcpkg install tesseract:x64-windows --recurse这里tesseract是包名:x64-windows是triplet也就是“64位Windows下的动态库版本”。--recurse表示递归安装所有依赖leptonica、libpng、libjpeg、tiff、zlib等都会被拉下来一起编译。细说一下版本的事。vcpkg安装的是它ports目录里维护的默认版本我在2021年年底安装时ports里tesseract正是4.1.1跟项目要求完全吻合。如果你的命令安装的是更高版本而你非要锁定4.1.1有两个办法一是编辑ports/tesseract/portfile.cmake把源码版本tag改成4.1.1二是用--overlay-ports指定自定义端口目录。第二种方式更干净但新手不建议一上来就碰portfile改错了后面所有依赖树都跟着乱。安装期间屏幕会滚动大量编译日志每编译完一个包能看到类似“Building package zlib...”之类的信息。这个过程会比较漫长依赖链编译完毕差不多要一个小时视网络情况而定主要是下载源码这一步比较费时间。如果中途下载失败重复执行同一命令即可已经编译完的包会被缓存复用不会从头再来。3.2 编译过程的观察与避坑编译期间不要随便中断。Tesseract的依赖树很深一旦断了虽然vcpkg有缓存重启时仍可能因为某几个包没编完而导致后续失败。我中途遇到过网络抖动libpng源码下载失败重新执行命令后直接跳过已下载的zip包继续编译了并没有全量重来这点体验还算不错。另外要注意磁盘空间。整个依赖链加编译中间产物可能会占用好几个GB的空间。如果C盘吃紧强烈建议把vcpkg目录放到D盘这类空间富裕的分区同时用VCPKG_DEFAULT_TRIPLET环境变量简化命令set VCPKG_DEFAULT_TRIPLETx64-windows这样以后执行vcpkg install tesseract时就不用每次都手写:x64-windows后缀。但要注意这个环境变量只对当前命令行窗口有效如果需要持久化还是要写进系统环境变量。3.3 安装完成后的验证安装成功后命令行会打印出类似“Total elapsed time: xx:xx”的耗时统计。然后我进入D:\dev\vcpkg\installed\x64-windows检查几个关键位置include/tesseract/baseapi.h核心头文件lib/tesseract41.lib导入库release版bin/tesseract41.dll运行库tools/tesseract源码编译出的tesseract.exe命令行工具同时打开installed/x64-windows/bin能看到一堆DLL比如leptonica-1.80.0.dll、libpng16.dll、libjpeg9.dll、libtiff-5.dll、zlib1.dll这些都是Tesseract运行时真正需要的。记住这个bin目录的位置后面部署程序时用得上。4. 在VS2017里配置项目并跑通第一个识别程序4.1 新建x64控制台工程打开VS2017新建一个控制台应用程序工程后先把解决方案平台从“x86”切到“x64”。方法是在工具栏“解决方案平台”下拉框里选择“配置管理器”在对话框中新建一个名为“x64”的活动解决方案平台。这一步经常被忽略但少了它就算vcpkg集成没问题你的x64库也大概率连不上。切换完成后在“项目”菜单里打开属性点“VC目录”能看到“包含目录”和“库目录”里多了vcpkg对应的路径。如果没有说明集成没生效或者平台不对回头检查一下解决方案平台千万不要打开属性时只看“活动”方案要看“所有配置”。4.2 链接库的两种处理方式先说最简单的方式完全依赖vcpkg的属性表。集成成功且平台正确的情况下VS会自动链接tesseract的导入库。但如果你打开“链接器-输入-附加依赖项”会发现列表是空的这很正常。vcpkg集成机制是把lib目录挂到库目录里让链接器在目录内搜索多数情况下自动生效。但少数情况比如同时装了多个版本的tesseract自动链接可能会选错版本。我的做法是双保险在“链接器-输入-附加依赖项”里手动补充一行tesseract41.lib;leptonica-1.80.0.lib为什么多此一举因为自动链接有时会因为“链接器无法在目录里匹配到合适符号”而报错手动指定最稳。尤其第一次跑通时手动指定能去掉很多不确定性。等到项目稳定以后再决定要不要依赖自动链接。4.3 第一个识别测试代码环境配好后我写了一个极简的识别程序#include tesseract/baseapi.h #include leptonica/allheaders.h #include iostream int main() { tesseract::TessBaseAPI api; if (api.Init(nullptr, eng)) { std::cerr tesseract init failed std::endl; return -1; } Pix* image pixRead(test.png); if (image nullptr) { std::cerr cannot read image std::endl; return -1; } api.SetImage(image); char* outText api.GetUTF8Text(); std::cout outText std::endl; api.End(); pixDestroy(image); delete[] outText; return 0; }这段代码做了四件事初始化引擎、读入图片、识别并输出文本、销毁资源。api.Init(nullptr, eng)的第一个参数是tessdata目录传nullptr表示使用默认路径第二个参数是语言eng是英文。注意图像路径用的是相对路径运行时要保证test.png就在exe同目录下否则pixRead会返回空指针程序立即退出。4.4 运行期的DLL部署编译能过直接按F5十有八九会弹出一个“找不到tesseract41.dll”的窗口。这是因为程序运行时到系统PATH和exe目录找DLL而vcpkg的bin目录并不在其中。解决方式有三种在系统环境变量PATH里加上D:\dev\vcpkg\installed\x64-windows\bin把bin目录下的所有DLL复制到exe所在目录在VS调试环境变量里给PATH加上bin目录我因为开发机不想让环境变量被太多无关路径污染选的是第二种写了一个小脚本每次把必需的那几个DLL同步过去。这里要特别提醒不只是tesseract41.dllleptonica开头的DLL、libpng、libjpeg、libtiff、zlib这五个也一个不能少缺任何一个程序启动时照样报找不到DLL。你可以直接用where tesseract41.dll这种命令配合检查。5. 遇到的问题和排查记录5.1 安装中编译失败怎么处理我遇到过一次error: building tesseract failed日志显示configure阶段找不到CMake。解决方法是先安装CMake并把它加到PATH再重新执行vcpkg install。vcpkg会缓存依赖包二次编译很快。如果还是失败可以用vcpkg install tesseract:x64-windows --debug输出完整日志看具体卡在哪一步。还有一个隐藏坑某些杀毒软件会把vcpkg编译时生成的临时exe当作威胁直接干掉导致非常诡异的编译报错。我建议编译时暂时关闭实时保护或者在白名单里放行vcpkg目录。5.2 VS2017链接报LNK2038/LNK2019链接时如果遇到LNK2038或LNK2019多半是运行库不匹配。vcpkg默认生成的是动态库对应运行库选项是/MDrelease和/MDddebug。如果你在项目属性里把运行库改成了/MT或/MTd链接时就会报错因为Tesseract库编译时用的还是MD选项两边的运行时符号对不上。解决办法是保持项目默认的“多线程DLL(/MD)”和“多线程DLL调试(/MDd)”别自己动。debug和release选择上也要注意vcpkg在debug下生成的是带调试信息的库如果你在release模式里配置了debug版的lib目录同样会链接失败。5.3 识别结果乱码或空白引擎初始化成功但输出是乱码或者空字符串最普遍的原因是语言包没配置。Tesseract要识别中文必须下载chi_sim.traineddata放到tessdata目录并且在Init中传engchi_sim。这个语言包文件不在vcpkg默认安装里需要单独下载。如果程序在其他目录启动找不到默认tessdata路径也会识别失败。我在项目里这么写api.Init(nullptr, engchi_sim, tesseract::OcrEngineMode::OEM_LSTM_ONLY); api.SetVariable(tessdata_dir, D:/dev/vcpkg/installed/x64-windows/share/tessdata);share/tessdata目录下如果没放中文语言包Initialize实际已经失败了只是返回错误码被忽略。所以建议Init之后立刻检查返回值不要像我一开始那样直接往后跑最后输出一堆无意义字符。5.4 x64和x86混用的坑有段时间我的项目里其他第三方库是x86版本vcpkg装的是x64。运行时DLL加载倒是没出大乱子但链接时各种版本冲突。后来我才想明白vcpkg的triplet决定了整套依赖链的位数绝对不能在同一个工程里混用x86和x64的库。如果确实需要x86就再执行一次vcpkg install tesseract:x86-windows两边目录互不干涉但VS项目里的平台配置也必须跟着切。这件事很容易被忽略因为你从代码层面完全看不出问题只有链接器会抱怨“无法解析的外部符号”。排查起来很费劲我后来都是先在命令行里用dumpbin /headers xxx.lib看一眼目标平台确认无误再引入项目。错误特征常见原因排查思路找不到tesseract41.dllvcpkg bin目录未加入PATH或未复制添加PATH或同步全部DLL到exe目录LNK2038 / LNK2019运行库类型不一致项目运行库恢复为/MD或/MDd编译失败缺少CMake、网络下载失败、杀毒干扰安装CMake重跑vcpkg install临时关闭实时保护识别乱码或空白tessdata路径错误或语言包缺失设置tessdata_dir下载对应traineddata链接库找不到项目平台是Win32切到x64重新确认vcpkg集成平台冲突x86和x64库混用统一triplet检查lib文件位数最后分享一个小经验vcpkg安装完成后installed/x64-windows/share目录下其实还有每个包各自的cmake配置如果你之后从VS2017切到其他构建系统这些配置能帮你少走弯路。我用了一个多月后最大的感受是vcpkg的“源码编译统一安装”思路天然避开了Windows下预编译库版本混乱这个老坑值得花一次时间把它彻底驯服。