庫(kù)配置與 OCR 實(shí)戰(zhàn))
簡(jiǎn)介本資源為面向C開(kāi)發(fā)者的Tesseract OCR 3.02.02集成包專(zhuān)為Visual Studio 2008環(huán)境定制解決老版本IDE下OCR引擎編譯與鏈接困難的問(wèn)題。包內(nèi)共93個(gè)文件以65個(gè)h頭文件、18個(gè)lib靜態(tài)庫(kù)、4個(gè)dll動(dòng)態(tài)庫(kù)為主另含vsprops屬性表與bat腳本壓縮包約19.57MB涵蓋Tesseract與Leptonica 1.68的完整依賴(lài)可直接用于圖像預(yù)處理與字符識(shí)別開(kāi)發(fā)。資源圍繞OCR核心功能、工作原理及VS2008集成展開(kāi)涉及圖像灰度化、二值化、文本行分割、字符分類(lèi)與后處理等環(huán)節(jié)并給出簡(jiǎn)體中文識(shí)別示例幫助讀者快速完成環(huán)境配置與庫(kù)引用。目前已有220人學(xué)習(xí)下載適合需要為桌面程序添加文字識(shí)別能力的中級(jí)開(kāi)發(fā)者參考。1. 老版本 Tesseract 的 VC2008 靜態(tài)庫(kù)為什么還有人專(zhuān)門(mén)找如果你在維護(hù)一套十幾年前上線的 C 桌面程序或者接手了某公司遺留的 MFC 項(xiàng)目大概率會(huì)遇到一個(gè)尷尬局面機(jī)器上跑著 Windows 7 甚至 XP 的工控機(jī)編譯器是 Visual Studio 2008而你現(xiàn)在需要給它加 OCR 能力。這時(shí)候你搜到的絕大多數(shù)方案都是 Tesseract 4.x/5.x 配 CMake、配 vcpkg、配 VS2019根本編不過(guò)去。標(biāo)題里這個(gè)tesseract-3.02.02-vc2008-lib-include-dll.rar本質(zhì)就是一套已經(jīng)用 VC2008 編譯好的 Tesseract 3.02.02 二進(jìn)制包里面按慣例包含lib導(dǎo)入庫(kù)、include頭文件、dll運(yùn)行時(shí)動(dòng)態(tài)庫(kù)三部分。它解決的不是識(shí)別準(zhǔn)不準(zhǔn)的問(wèn)題而是在老工具鏈上能不能鏈接成功的問(wèn)題。適合的人群很明確被 VS2008 鎖死、又必須做離線文字識(shí)別的嵌入式或工控方向開(kāi)發(fā)者。下面我按自己踩過(guò)的路把這套東西怎么用、參數(shù)怎么調(diào)、坑在哪講清楚。2. 先搞懂 Tesseract 3.02 的目錄結(jié)構(gòu)與鏈接模型2.1 lib / include / dll 三件套各自干什么拿到這個(gè)壓縮包解壓后不要急著往工程里拖。先按角色分類(lèi)include目錄里是tesseract相關(guān)的 C 頭文件核心是baseapi.h、strngs.h、ocrclass.h這幾個(gè)它們定義了TessBaseAPI這個(gè)主類(lèi)lib目錄里是.lib文件分導(dǎo)入庫(kù)tesseract302.lib這類(lèi)和可能的靜態(tài)庫(kù)dll目錄里是運(yùn)行時(shí)真正加載的.dll程序跑起來(lái)時(shí)靠它干活。VC2008 時(shí)代的鏈接模型和現(xiàn)在不一樣那時(shí)候沒(méi)有#pragma comment(lib, ...)滿天飛的習(xí)慣多數(shù)項(xiàng)目是在項(xiàng)目屬性 → 鏈接器 → 輸入 → 附加依賴(lài)項(xiàng)里手動(dòng)填.lib名字。所以你要做的第一件事是確認(rèn)這套包給的是導(dǎo)入庫(kù) DLL還是純靜態(tài)庫(kù)。導(dǎo)入庫(kù)體積小幾十 KB 到幾百 KB靜態(tài)庫(kù)體積大幾 MB 到十幾 MB。判斷方法很簡(jiǎn)單看lib目錄里文件大小再配合dumpbin /headers xxx.lib看有沒(méi)有導(dǎo)出符號(hào)。提示VC2008 生成的庫(kù)默認(rèn)是_MBCS還是_UNICODE、Debug還是Release必須和你的主工程一致否則會(huì)出現(xiàn)一堆LNK2001無(wú)法解析的外部符號(hào)。這是老工程最常見(jiàn)的翻車(chē)點(diǎn)。2.2 為什么是 3.02.02 而不是更新的版本Tesseract 3.x 和 4.x 之間有一次架構(gòu)級(jí)變化4.x 引入了基于 LSTM 的識(shí)別引擎依賴(lài)的第三方庫(kù)如 leptonica 版本、C11 特性都往上抬了一截。VC2008 的 C 編譯器對(duì) C11 支持非常有限很多新語(yǔ)法直接編不過(guò)。3.02.02 屬于 3.x 后期接口穩(wěn)定、依賴(lài)少是能在 VC2008 上跑通的少數(shù)版本之一。從工程角度看3.x 的 API 是同步阻塞式的調(diào)用簡(jiǎn)單初始化 → 設(shè)置圖像 → 取結(jié)果。4.x 之后多了很多配置項(xiàng)和線程模型對(duì)老程序反而是負(fù)擔(dān)。所以選 3.02.02 不是懷舊是被工具鏈逼出來(lái)的理性選擇。2.3 依賴(lài) leptonica 這件事必須提前確認(rèn)Tesseract 本身不做圖像解碼它依賴(lài) leptonica 處理 BMP/TIFF/PNG 等格式。這套 VC2008 包里通常會(huì)把 leptonica 的庫(kù)一起帶上或者要求你單獨(dú)準(zhǔn)備。判斷方法看include里有沒(méi)有allheaders.hlib里有沒(méi)有l(wèi)eptonlib.lib之類(lèi)。如果沒(méi)有你得自己用 VC2008 編一份 leptonica這一步相當(dāng)折磨因?yàn)?leptonica 的源碼里也有不少需要改的宏。常見(jiàn)做法是找一份已經(jīng)配好的 leptonica VC2008 版本和 Tesseract 放同一目錄。我一般會(huì)把兩個(gè)庫(kù)的include合并到一個(gè)thirdparty目錄鏈接時(shí)一起加省得路徑亂。3. 在 VS2008 工程里把庫(kù)接進(jìn)去的最小步驟3.1 建立目錄約定并配置包含路徑先別動(dòng)工程先在磁盤(pán)上把目錄理清楚。我習(xí)慣這樣放thirdparty/ tesseract/ include/ lib/ dll/ leptonica/ include/ lib/ dll/然后在 VS2008 里右鍵工程 → 屬性 → C/C → 常規(guī) → 附加包含目錄把兩個(gè)include都加進(jìn)去。注意 VC2008 的路徑分隔符用反斜杠多個(gè)路徑用分號(hào)隔開(kāi)不要用逗號(hào)。// 工程里引用頭文件的寫(xiě)法注意順序 #include allheaders.h // leptonica 先包含 #include baseapi.h // tesseract 主 API #include strngs.h邏輯說(shuō)明allheaders.h是 leptonica 的總頭里面會(huì)帶出一堆圖像類(lèi)型定義baseapi.h里用到了這些類(lèi)型所以順序不能反。參數(shù)說(shuō)明如果你的工程開(kāi)了預(yù)編譯頭stdafx.h把這些#include放到stdafx.h之后避免宏沖突。3.2 鏈接器里填依賴(lài)項(xiàng)與庫(kù)目錄屬性 → 鏈接器 → 常規(guī) → 附加庫(kù)目錄填thirdparty\tesseract\lib和thirdparty\leptonica\lib。然后鏈接器 → 輸入 → 附加依賴(lài)項(xiàng)按依賴(lài)順序填leptonlib.lib tesseract302.lib順序很重要Tesseract 依賴(lài) leptonica所以 leptonica 放前面。VC2008 的鏈接器是從左到右解析符號(hào)的順序反了會(huì)報(bào)一堆LNK2019。# 用 dumpbin 確認(rèn)庫(kù)的導(dǎo)出符號(hào)VC2008 自帶這個(gè)工具 dumpbin /exports tesseract302.lib exports.txt # 打開(kāi) exports.txt 看有沒(méi)有 TessBaseAPI 相關(guān)符號(hào)邏輯說(shuō)明dumpbin /exports列出導(dǎo)入庫(kù)導(dǎo)出的函數(shù)名如果里面找不到TessBaseAPI的構(gòu)造函數(shù)或Init方法說(shuō)明這個(gè) lib 和頭文件版本對(duì)不上。參數(shù)說(shuō)明/exports只對(duì)導(dǎo)入庫(kù)和 DLL 有效純靜態(tài)庫(kù)要用/symbols。3.3 運(yùn)行時(shí) DLL 的放置位置編譯鏈接通過(guò)不代表能跑。程序啟動(dòng)時(shí)會(huì)在幾個(gè)位置找 DLLexe 同目錄、系統(tǒng)目錄、PATH 環(huán)境變量里的目錄。最穩(wěn)的做法是把dll目錄里的所有.dll復(fù)制到你的 exe 輸出目錄。# 假設(shè)輸出目錄是 bin\Release copy thirdparty\tesseract\dll\*.dll bin\Release\ copy thirdparty\leptonica\dll\*.dll bin\Release\邏輯說(shuō)明VC2008 默認(rèn)輸出目錄是工程目錄下的Release或Debug復(fù)制過(guò)去后調(diào)試和發(fā)布都不用再配 PATH。參數(shù)說(shuō)明如果 DLL 有多個(gè)比如tesseract302.dll、leptonlib.dll、可能還有l(wèi)ibtiff.dll一個(gè)都不能少缺一個(gè)就是啟動(dòng)即崩而且報(bào)錯(cuò)信息往往很含糊。4. 初始化 TessBaseAPI 與識(shí)別參數(shù)怎么設(shè)4.1 最小可運(yùn)行代碼與語(yǔ)言包路徑Tesseract 3.02 的初始化需要兩個(gè)東西datapath語(yǔ)言包目錄和language語(yǔ)言代碼。語(yǔ)言包是tessdata目錄下的*.traineddata文件比如eng.traineddata、chi_sim.traineddata。#include baseapi.h #include string int main() { // 創(chuàng)建 API 實(shí)例 tesseract::TessBaseAPI* api new tesseract::TessBaseAPI(); // datapath 指向 tessdata 的上一級(jí)目錄不是 tessdata 本身 const char* datapath D:\\project\\tessdata; const char* lang eng; // 初始化返回 0 表示成功 int ret api-Init(datapath, lang); if (ret ! 0) { // 初始化失敗通常是路徑錯(cuò)或語(yǔ)言包缺失 delete api; return -1; } // 后續(xù)識(shí)別流程... api-End(); delete api; return 0; }邏輯說(shuō)明Init的第二個(gè)參數(shù)是語(yǔ)言代碼多個(gè)語(yǔ)言用連接比如engchi_sim。參數(shù)說(shuō)明datapath這個(gè)參數(shù)最容易搞錯(cuò)它指的是包含tessdata文件夾的那個(gè)父目錄而不是tessdata本身。如果你寫(xiě)成D:\project\tessdataTesseract 會(huì)去找D:\project\tessdata\tessdata\eng.traineddata必然失敗。4.2 圖像輸入SetImage 的兩種方式Tesseract 3.02 支持兩種圖像輸入直接傳內(nèi)存數(shù)據(jù)或者傳 leptonica 的PIX結(jié)構(gòu)。前者簡(jiǎn)單后者靈活。// 方式一傳灰度或彩色像素?cái)?shù)據(jù) unsigned char* imagedata /* 你的圖像緩沖區(qū) */; int width 800; int height 600; int bytes_per_pixel 1; // 灰度圖是 1RGB 是 3 int bytes_per_line width * bytes_per_pixel; api-SetImage(imagedata, width, height, bytes_per_pixel, bytes_per_line); // 方式二用 leptonica 先讀文件 PIX* pix pixRead(input.png); api-SetImage(pix); pixDestroy(pix);邏輯說(shuō)明方式一要求你自己保證緩沖區(qū)格式正確Tesseract 內(nèi)部會(huì)做二值化。參數(shù)說(shuō)明bytes_per_line是每行字節(jié)數(shù)如果圖像有行對(duì)齊填充比如 BMP 每行補(bǔ)齊到 4 字節(jié)這里必須填實(shí)際行跨度不能簡(jiǎn)單用width * bytes_per_pixel否則圖像會(huì)斜切。4.3 識(shí)別結(jié)果獲取與置信度過(guò)濾char* text api-GetUTF8Text(); if (text ! NULL) { // 處理識(shí)別結(jié)果 printf(%s\n, text); delete[] text; // 注意必須用 delete[] 釋放 } // 獲取每個(gè)字符的置信度 int conf api-MeanTextConf();邏輯說(shuō)明GetUTF8Text返回的是堆上分配的字符串必須用delete[]釋放用free或delete都可能出問(wèn)題。參數(shù)說(shuō)明MeanTextConf返回平均置信度范圍 0-100低于 60 的結(jié)果基本不可用建議在業(yè)務(wù)層做過(guò)濾。5. 避坑VC2008 鏈接 Tesseract 的 5 個(gè)血淚記錄5.1 現(xiàn)象LNK2001 無(wú)法解析的外部符號(hào)全是 C 名字原因庫(kù)和工程的運(yùn)行時(shí)庫(kù)配置不一致。VC2008 有/MT靜態(tài) CRT和/MD動(dòng)態(tài) CRT兩種還有 Debug/Release 之分一共四種組合。庫(kù)是用/MD Release編的你的工程用/MT Debug符號(hào)就對(duì)不上。解決屬性 → C/C → 代碼生成 → 運(yùn)行時(shí)庫(kù)改成和庫(kù)一致的選項(xiàng)。如果不確定庫(kù)是哪種用dumpbin /directives xxx.lib看里面引用了MSVCR90.dll還是LIBCMT。5.2 現(xiàn)象程序啟動(dòng)報(bào)應(yīng)用程序配置不正確原因這是 VC2008 的清單manifest問(wèn)題。DLL 依賴(lài)特定版本的 CRT但 exe 的清單里沒(méi)聲明或者聲明了但系統(tǒng)里沒(méi)有對(duì)應(yīng)版本的運(yùn)行庫(kù)。解決安裝 VC2008 的對(duì)應(yīng)運(yùn)行庫(kù)或者在工程里把嵌入清單設(shè)為否手動(dòng)放一個(gè)正確的 manifest 文件。最省事的辦法是裝一次完整的 VC2008 運(yùn)行庫(kù)包。5.3 現(xiàn)象Init 返回非 0但路徑看著沒(méi)錯(cuò)原因datapath參數(shù)理解錯(cuò)誤或者語(yǔ)言包版本和 Tesseract 版本不匹配。3.02.02 需要的是 3.x 格式的traineddata用 4.x 的語(yǔ)言包會(huì)初始化失敗。解決確認(rèn)datapath指向tessdata的父目錄確認(rèn)tessdata里的文件是 3.x 版本。判斷方法3.x 的eng.traineddata通常在 10-20 MB4.x 的會(huì)更大且格式不同。5.4 現(xiàn)象識(shí)別結(jié)果全是亂碼或空字符串原因圖像格式不對(duì)。Tesseract 3.02 對(duì)輸入圖像有要求如果是彩色圖且對(duì)比度低二值化后可能全黑或全白。解決先用 leptonica 做預(yù)處理轉(zhuǎn)灰度、增強(qiáng)對(duì)比度、二值化再傳給SetImage。我一般會(huì)加一步pixConvertRGBToLuminance和pixThresholdToBinary。5.5 現(xiàn)象多線程調(diào)用時(shí)崩潰原因Tesseract 3.02 的TessBaseAPI不是線程安全的多個(gè)線程共用一個(gè)實(shí)例會(huì)出問(wèn)題。解決每個(gè)線程創(chuàng)建自己的TessBaseAPI實(shí)例或者加鎖串行化。注意Init比較耗時(shí)線程池場(chǎng)景下建議每個(gè)線程初始化一次后復(fù)用。6. 進(jìn)階用 leptonica 預(yù)處理把識(shí)別率拉上來(lái)6.1 預(yù)處理鏈路與參數(shù)Tesseract 3.02 的識(shí)別率對(duì)圖像質(zhì)量非常敏感。我一般會(huì)走這條鏈路讀圖 → 轉(zhuǎn)灰度 → 放大 → 二值化 → 去噪。PIX* pix pixRead(input.png); PIX* gray pixConvertRGBToLuminance(pix); // 放大 2 倍小字體識(shí)別率提升明顯 PIX* scaled pixScale(gray, 2.0, 2.0); // 自適應(yīng)二值化參數(shù)需要根據(jù)圖像調(diào) PIX* binary pixAdaptThresholdToBinary(scaled, NULL, 0.5); // 去噪 PIX* clean pixRemoveUnusedColors(binary); api-SetImage(clean); char* text api-GetUTF8Text();邏輯說(shuō)明pixScale放大對(duì)小字體幫助很大但放太大會(huì)增加識(shí)別時(shí)間。參數(shù)說(shuō)明pixAdaptThresholdToBinary的第三個(gè)參數(shù)是閾值比例0.5 是常用起點(diǎn)圖像偏暗就調(diào)低偏亮就調(diào)高。6.2 頁(yè)面分割模式的選擇Tesseract 3.02 支持通過(guò)SetPageSegMode設(shè)置頁(yè)面分割模式不同模式對(duì)識(shí)別結(jié)果影響很大。模式常量適用場(chǎng)景說(shuō)明PSM_SINGLE_BLOCK單塊文本默認(rèn)適合規(guī)整文檔PSM_SINGLE_LINE單行文本適合驗(yàn)證碼、標(biāo)簽PSM_SINGLE_WORD單個(gè)詞適合字段提取PSM_SINGLE_CHAR單個(gè)字符適合字符識(shí)別PSM_AUTO自動(dòng)通用但慢api-SetPageSegMode(tesseract::PSM_SINGLE_LINE);邏輯說(shuō)明如果你明確知道圖像里只有一行字用PSM_SINGLE_LINE比默認(rèn)模式準(zhǔn)得多因?yàn)樗粫?huì)去做復(fù)雜的版面分析。參數(shù)說(shuō)明模式設(shè)置必須在SetImage之前或之后都行但要在GetUTF8Text之前。6.3 白名單與黑名單字符集如果業(yè)務(wù)場(chǎng)景只涉及數(shù)字和字母可以限制識(shí)別字符集減少誤識(shí)別。api-SetVariable(tessedit_char_whitelist, 0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ);邏輯說(shuō)明白名單會(huì)強(qiáng)制 Tesseract 只在給定字符里選對(duì)固定格式的識(shí)別如車(chē)牌、編號(hào)效果顯著。參數(shù)說(shuō)明這個(gè)變量必須在Init之后、SetImage之前設(shè)置設(shè)置晚了不生效。6.4 一個(gè)我常用的驗(yàn)證習(xí)慣每次調(diào)完參數(shù)不要只看一兩個(gè)樣本。我會(huì)準(zhǔn)備一組 20-30 張有代表性的圖寫(xiě)個(gè)小腳本批量跑統(tǒng)計(jì)平均置信度和正確率。這樣能避免調(diào)參調(diào)出過(guò)擬合——在某張圖上準(zhǔn)了換一張就崩。這個(gè)習(xí)慣幫我省了很多后悔藥因?yàn)?OCR 的參數(shù)玄學(xué)成分不小單樣本驗(yàn)證基本等于沒(méi)驗(yàn)證。希望幫到你。本文還有配套的精品資源點(diǎn)擊獲取