OCR:WinForm集成PaddleOCR完整指南)
簡(jiǎn)介這套C#源程序基于PaddleOCR引擎專(zhuān)注解決本地離線(xiàn)環(huán)境下圖片內(nèi)文字的提取問(wèn)題面向需要將OCR能力集成到桌面工具或內(nèi)部系統(tǒng)中的開(kāi)發(fā)者。程序在整圖識(shí)別的基礎(chǔ)上提供了鼠標(biāo)點(diǎn)擊識(shí)別指定區(qū)域文字、圖像任意縮放以及輸入編號(hào)獲取對(duì)應(yīng)位置文字三種交互模式可方便地用于票據(jù)信息錄入、截圖內(nèi)容提取、掃描檔案檢索等實(shí)際場(chǎng)景。壓縮包共181個(gè)文件大小約273MB其中既包含PaddleOCR運(yùn)行所需的模型文件與61個(gè)DLL依賴(lài)庫(kù)也包含完整C#工程源碼、配置文件以及說(shuō)明文檔目錄結(jié)構(gòu)清晰解壓后即可對(duì)照學(xué)習(xí)或進(jìn)行二次開(kāi)發(fā)。目前已有956人學(xué)習(xí)下載。借助該案例開(kāi)發(fā)者可以快速理解PaddleOCR在C#環(huán)境下的調(diào)用流程掌握坐標(biāo)定位、圖片縮放與文字提取相結(jié)合的實(shí)現(xiàn)思路為搭建屬于自己的離線(xiàn)OCR工具鏈提供一套可運(yùn)行的完整參照。 先交代一下我為什么碰這個(gè)項(xiàng)目。前陣子在做一套桌面級(jí)資料錄入工具需求一點(diǎn)都不新鮮把圖片里的文字抓出來(lái)轉(zhuǎn)成結(jié)構(gòu)化數(shù)據(jù)交給后續(xù)流程。真正卡住我的是“數(shù)據(jù)不能出內(nèi)網(wǎng)”這條硬約束——圖片里全是客戶(hù)信息和單據(jù)編號(hào)走公共云API誰(shuí)都不敢簽字。這類(lèi)場(chǎng)景其實(shí)非常多工廠(chǎng)車(chē)間讀批號(hào)、醫(yī)院窗口錄單據(jù)、政企內(nèi)網(wǎng)整理檔案甚至你自己本地?cái)€一個(gè)截圖轉(zhuǎn)文字的小工具都屬于C#本地離線(xiàn)OCR的范疇。基于這個(gè)需求我最終選了PaddleOCR來(lái)做識(shí)別引擎并在WinForm上位機(jī)里集成了一套完整的源程序后面把整個(gè)方案和踩坑過(guò)程都拆開(kāi)講。這篇內(nèi)容適合三類(lèi)人一是C#桌面應(yīng)用開(kāi)發(fā)者想給自家軟件加一個(gè)完全不依賴(lài)網(wǎng)絡(luò)的文字識(shí)別功能二是做上位機(jī)或工控軟件的同行需要在離線(xiàn)環(huán)境里識(shí)別產(chǎn)品標(biāo)簽、二維碼附近的手寫(xiě)或印刷文字三是對(duì)OCR技術(shù)好奇、想弄明白PaddleOCR在C#里到底怎么落地的朋友。我會(huì)從方案選型、環(huán)境搭建、核心代碼、WinForm集成、部署打包到問(wèn)題排查一條龍講清楚過(guò)程中會(huì)穿插大量實(shí)際踩過(guò)的坑。1. 項(xiàng)目概述與整體設(shè)計(jì)思路1.1 核心需求這不只是“識(shí)別文字”表面上看需求就是“讀圖片上的文字”但落地時(shí)其實(shí)有四個(gè)隱藏條件缺一個(gè)都會(huì)翻車(chē)。第一是本地離線(xiàn)。識(shí)別過(guò)程必須在目標(biāo)機(jī)器上完成不能有任何一次網(wǎng)絡(luò)請(qǐng)求這既是數(shù)據(jù)安全要求也是運(yùn)行環(huán)境決定的——很多工廠(chǎng)車(chē)間、醫(yī)院內(nèi)網(wǎng)根本沒(méi)有外網(wǎng)。第二是識(shí)別精度尤其中文場(chǎng)景。網(wǎng)上隨便找的開(kāi)源OCR對(duì)英文和印刷體還行碰到中文、標(biāo)點(diǎn)、數(shù)字混排就很容易亂。第三是可集成性既然用的是C#和WinFormOCR引擎必須能作為類(lèi)庫(kù)被干凈地引用不能靠Python腳本外包一層。第四是可控部署最終交付給客戶(hù)的是一臺(tái)安裝好程序的Windows機(jī)器依賴(lài)項(xiàng)越少越好不能逼客戶(hù)裝一堆解釋器。1.2 方案選型為什么是PaddleOCR而不是別的我最初先試了Tesseract這是老牌開(kāi)源OCRC#集成也不難但中文識(shí)別效果真的不夠用尤其碰到模糊截圖和帶背景噪聲的圖片錯(cuò)誤率高到?jīng)]法上線(xiàn)。后來(lái)又考慮過(guò)EasyOCR模型精度不錯(cuò)但它是Python生態(tài)在C#里調(diào)用要么起子進(jìn)程要么做HTTP服務(wù)部署體積大、穩(wěn)定性也差。云API就不用說(shuō)了需求第一條就把它斃了。剩下PaddleOCR是真香百度飛槳開(kāi)源的PP-OCR系列模型中文識(shí)別精度在開(kāi)源方案里是第一梯隊(duì)而且有社區(qū)封裝的C#版本底層直接調(diào)用PaddleInference推理庫(kù)不依賴(lài)Python環(huán)境。整條鏈路都是本地推理非常適合WinForm桌面程序。另外PaddleOCR是三模型聯(lián)動(dòng)識(shí)別印刷體和清晰截圖的效果遠(yuǎn)超預(yù)期。我用一個(gè)表格把當(dāng)時(shí)對(duì)比過(guò)的方案列出來(lái)方便你直觀(guān)感受方案中文精度C#集成難度離線(xiàn)支持部署體積Tesseract一般低支持小但模型效果弱云OCR API高低不支持無(wú)EasyOCR高高需Python環(huán)境支持大PaddleOCR Sdcb封裝高低支持中等含模型約200MB1.3 識(shí)別鏈路PP-OCR到底做了什么很多人以為OCR就是“一張圖進(jìn)去文本出來(lái)”其實(shí)內(nèi)部是流水線(xiàn)。了解這條鏈路對(duì)排查問(wèn)題特別有幫助。PaddleOCR的PP-OCR系列模型分三段文本檢測(cè)Det先在整張圖里找出所有可能是文字的區(qū)域畫(huà)出一堆文本框。這一步解決的是“字在哪兒”的問(wèn)題。方向分類(lèi)Cls判斷文字框的方向是否需要旋轉(zhuǎn)很多手機(jī)拍照上傳的圖片是歪的這一步會(huì)把文本框矯正成水平方向。文本識(shí)別Rec對(duì)矯正后的文字區(qū)域做字符識(shí)別輸出具體的文字內(nèi)容和置信度。這三個(gè)模型在C#里分別對(duì)應(yīng)detModelDir、clsModelDir和recModelDir三份目錄。理解這個(gè)流程后你就知道識(shí)別結(jié)果為空不一定是Rec模型的問(wèn)題可能是Det階段就沒(méi)把文字框檢測(cè)出來(lái)識(shí)別出亂碼則可能是Cls方向分類(lèi)沒(méi)生效文字是倒著的。后面排查問(wèn)題都是圍繞這條鏈路展開(kāi)的。2. 開(kāi)發(fā)環(huán)境搭建與模型準(zhǔn)備2.1 開(kāi)發(fā)環(huán)境與NuGet依賴(lài)我的開(kāi)發(fā)機(jī)是Windows 11 Visual Studio 2022 .NET 6目標(biāo)框架選的是x64這點(diǎn)很重要——PaddleInference的原生庫(kù)只有64位版本如果你的項(xiàng)目編譯目標(biāo)是AnyCPU且本地沒(méi)裝x64運(yùn)行時(shí)加載Dll會(huì)直接報(bào)錯(cuò)。引入PaddleOCR最省事的方式是用社區(qū)封裝庫(kù)Sdcb.PaddleOCR。在NuGet包管理器里搜索安裝即可它會(huì)自動(dòng)把底層的Sdcb.PaddleInference和原生運(yùn)行庫(kù)帶進(jìn)來(lái)。我建議用支持.NET 6/8的新版本老版本在.NET Framework 4.x上有不少兼容問(wèn)題。dotnet add package Sdcb.PaddleOCR如果你要跑GPU模式還需要額外安裝匹配的CUDA和cuDNN運(yùn)行庫(kù)具體版本要和PaddleInference預(yù)編譯版本對(duì)應(yīng)。以我實(shí)測(cè)過(guò)的某個(gè)版本為例它要求CUDA 11.7 cuDNN 8.5版本不匹配會(huì)在初始化時(shí)報(bào)錯(cuò)。不過(guò)考慮到大多數(shù)離線(xiàn)上位機(jī)沒(méi)有獨(dú)立顯卡我下面默認(rèn)以CPU MKLDNN模式為主GPU模式我會(huì)在參數(shù)調(diào)優(yōu)部分單獨(dú)講。2.2 模型文件下載與目錄組織PaddleOCR模型需要單獨(dú)下載程序代碼本身并不是“內(nèi)置”識(shí)別能力的。模型從PaddleOCR官方模型庫(kù)下載我用了PP-OCRv4的中文模型包含檢測(cè)、方向分類(lèi)、識(shí)別三個(gè)部分。下載后我的目錄結(jié)構(gòu)是這樣的C:\PaddleOcrDemo\ ├── PaddleOcrDemo.sln ├── PaddleOcrWinForm\ │ ├── bin\ │ └── ... └── models\ ├── ch_PP-OCRv4_det_infer\ │ ├── inference.pdmodel │ └── inference.pdiparams ├── ch_PP-OCRv4_rec_infer\ │ ├── inference.pdmodel │ └── inference.pdiparams └── ch_PP-OCRv4_cls_infer\ ├── inference.pdmodel └── inference.pdiparams有兩個(gè)坑要提前說(shuō)。第一模型目錄一旦下載完就不要改了inference.pdmodel和inference.pdiparams是配套的缺一個(gè)都會(huì)加載失敗。第二路徑里盡量不要有中文和空格這個(gè)后面在踩坑部分會(huì)詳細(xì)講Native層對(duì)中文路徑的支持偶爾會(huì)抽風(fēng)部署到客戶(hù)機(jī)器上容易出幺蛾子。2.3 先跑通最簡(jiǎn)單的初始化在動(dòng)手寫(xiě)WinForm之前我建議先建一個(gè)控制臺(tái)程序把引擎初始化跑通環(huán)境沒(méi)問(wèn)題了再往上疊UI。這一步能幫你隔離問(wèn)題如果控制臺(tái)都跑不通說(shuō)明是依賴(lài)或模型問(wèn)題跟界面邏輯無(wú)關(guān)。初始化引擎的代碼很簡(jiǎn)單using Sdcb.PaddleOCR; using Sdcb.PaddleInference; using var engine new PaddleOcrEngine( detModelDir: C:\PaddleOcrDemo\models\ch_PP-OCRv4_det_infer, recModelDir: C:\PaddleOcrDemo\models\ch_PP-OCRv4_rec_infer, clsModelDir: C:\PaddleOcrDemo\models\ch_PP-OCRv4_cls_infer, device: PaddleDevice.Mkldnn() ); Console.WriteLine(PaddleOCR 引擎初始化成功);看到“初始化成功”這行輸出說(shuō)明你的模型和依賴(lài)都正常。有些版本的PaddleOcrEngine構(gòu)造器還支持傳labelFilePath指定詞典文件具體重載以你安裝的NuGet版本為準(zhǔn)核心思路不變傳入三個(gè)模型目錄和推理設(shè)備。3. 核心代碼實(shí)現(xiàn)與參數(shù)調(diào)優(yōu)3.1 引擎初始化讓PaddleOCR“跑起來(lái)”引擎初始化的核心是PaddleOcrEngine這個(gè)對(duì)象它封裝了檢測(cè)、分類(lèi)、識(shí)別三個(gè)模型。在實(shí)際項(xiàng)目里我強(qiáng)烈建議把引擎對(duì)象做成單例或靜態(tài)字段只初始化一次整個(gè)程序生命周期內(nèi)復(fù)用。為什么PaddleOCR引擎的初始化非常重需要加載三個(gè)模型到內(nèi)存CPU模式下大概要占用1-2秒如果每次識(shí)別都重新new一個(gè)引擎性能會(huì)慘不忍睹。但一旦加載完成后續(xù)每張圖片的推理就快很多。它的內(nèi)存占用主要集中在模型上復(fù)用一個(gè)實(shí)例不會(huì)額外增加太多內(nèi)存識(shí)別完成后對(duì)象也不會(huì)急著釋放這是設(shè)計(jì)時(shí)就考慮好的。設(shè)備選擇方面PaddleDevice.Mkldnn()表示使用CPU Intel MKLDNN加速這是我在絕大多數(shù)離線(xiàn)場(chǎng)景下的首選兼容性好、不需要額外安裝顯卡驅(qū)動(dòng)。如果你的機(jī)器有NVIDIA顯卡且能保證目標(biāo)機(jī)器也有相同環(huán)境可以考慮PaddleDevice.Cuda(0)速度能快好幾倍但部署時(shí)要在目標(biāo)機(jī)器上額外配置CUDA環(huán)境性?xún)r(jià)比不一定高。3.2 圖片識(shí)別與結(jié)果解析引擎初始化成功后識(shí)別單張圖片的核心代碼大概長(zhǎng)這樣using Sdcb.PaddleOCR; public static string RecognizeImage(PaddleOcrEngine engine, string imagePath) { using var result engine.Run(imagePath); var lines new Liststring(); foreach (var region in result.Regions) { // region.Text 是識(shí)別出的文本 // region.Score 是置信度 lines.Add(${region.Text} (置信度: {region.Score:F2})); } return string.Join(Environment.NewLine, lines); }engine.Run傳入圖片路徑返回一個(gè)包含所有識(shí)別區(qū)域的結(jié)果對(duì)象。每個(gè)Region代表一個(gè)識(shí)別區(qū)域里面有文本內(nèi)容、置信度還有文本框坐標(biāo)信息。如果你想按坐標(biāo)排序來(lái)還原閱讀順序可以用Region里的坐標(biāo)點(diǎn)做排序如果只是簡(jiǎn)單提取所有文字直接拼接就行了。注意engine.Run出來(lái)的是IDisposable對(duì)象用完要釋放否則連續(xù)識(shí)別大量圖片后內(nèi)存會(huì)緩慢上漲。我一開(kāi)始沒(méi)注意這個(gè)問(wèn)題跑了上千張圖后內(nèi)存從200MB漲到1.5GB排查半天才發(fā)現(xiàn)是結(jié)果對(duì)象沒(méi)釋放。3.3 批量識(shí)別與性能調(diào)優(yōu)實(shí)際項(xiàng)目中很少只識(shí)別一張圖更常見(jiàn)的是拖入一個(gè)文件夾批量處理里面的圖片。批量識(shí)別時(shí)依然復(fù)用同一個(gè)PaddleOcrEngine實(shí)例循環(huán)調(diào)用Run方法即可foreach (string file in Directory.GetFiles(folderPath, *.png)) { string text RecognizeImage(engine, file); Console.WriteLine(${Path.GetFileName(file)}: {text}); }性能調(diào)優(yōu)方面我做過(guò)一輪測(cè)試CPU模式i5-1240P筆記本處理器下單張1080P截圖平均耗時(shí)約800ms-1.2秒這個(gè)速度對(duì)桌面工具完全夠用。GPU模式下能到150-250ms體驗(yàn)好很多但部署復(fù)雜度和兼容性成本確實(shí)高。除了設(shè)備選擇還有兩個(gè)參數(shù)值得關(guān)注檢測(cè)閾值和識(shí)別置信度閾值。檢測(cè)閾值決定什么樣的文本框會(huì)被保留值調(diào)低能找回更多文字區(qū)域但也會(huì)引入誤檢識(shí)別置信度閾值決定多低置信度的文本會(huì)被過(guò)濾。部分版本的PaddleOcrEngine暴露了相關(guān)屬性比如DetThreshold和RecThreshold實(shí)際環(huán)境中我用默認(rèn)值居多只有遇到“漏字”時(shí)會(huì)把檢測(cè)閾值稍微調(diào)低。一個(gè)實(shí)用的預(yù)處理技巧如果圖片本身清晰度不高先用OpenCVSharp做灰度化、二值化、放大處理再送給OCR引擎識(shí)別率會(huì)明顯提升。尤其對(duì)于手機(jī)拍照上傳的圖片預(yù)處理有時(shí)候比調(diào)引擎參數(shù)更有效。4. WinForm集成與安裝包發(fā)布4.1 把識(shí)別接進(jìn)WinForm界面控制臺(tái)跑通后集成到WinForm就順理成章了。界面設(shè)計(jì)很簡(jiǎn)單一個(gè)圖片路徑選擇框、一個(gè)“開(kāi)始識(shí)別”按鈕、一個(gè)結(jié)果展示文本框、一個(gè)圖片預(yù)覽PictureBox。核心邏輯是點(diǎn)擊按鈕后異步執(zhí)行識(shí)別避免界面卡死。private async void btnRecognize_Click(object sender, EventArgs e) { btnRecognize.Enabled false; try { string imagePath txtImagePath.Text; if (!File.Exists(imagePath)) { MessageBox.Show(圖片文件不存在); return; } string result await Task.Run(() RecognizeImage(_engine, imagePath)); txtResult.Text result; } finally { btnRecognize.Enabled true; } }關(guān)鍵點(diǎn)是Task.Run。OCR推理是CPU密集型操作如果直接在UI線(xiàn)程跑窗口會(huì)假死幾秒鐘用戶(hù)體驗(yàn)極差。用async/awaitTask.Run把推理扔到線(xiàn)程池UI線(xiàn)程保持響應(yīng)界面可以顯示“識(shí)別中...”的提示。_engine是窗體類(lèi)里的靜態(tài)字段在窗體構(gòu)造函數(shù)或Load事件里初始化一次。WinForm程序退出時(shí)記得在FormClosing事件里釋放引擎資源。4.2 發(fā)布與安裝包制作WinForm程序發(fā)布有兩個(gè)重點(diǎn)運(yùn)行時(shí)和模型目錄。發(fā)布配置上我推薦用“獨(dú)立部署Self-contained”這樣目標(biāo)機(jī)器不需要預(yù)裝.NET運(yùn)行時(shí)發(fā)布完是一個(gè)可直接運(yùn)行的exe。在VS里右鍵項(xiàng)目 - 發(fā)布 - 選擇目標(biāo)框架和部署模式把部署模式選成“獨(dú)立”即可。缺點(diǎn)是發(fā)布體積會(huì)大幾十MB但對(duì)于給客戶(hù)部署來(lái)說(shuō)省掉裝運(yùn)行時(shí)的步驟這點(diǎn)體積完全值得。模型目錄要跟隨程序一起發(fā)布。最簡(jiǎn)單的做法是在項(xiàng)目里建一個(gè)models文件夾把三個(gè)模型的子目錄放進(jìn)去然后在屬性里設(shè)置為“如果較新則復(fù)制”這樣每次構(gòu)建都會(huì)把模型帶到輸出目錄。注意模型文件加起來(lái)可能接近200MB如果你用的是完整中文識(shí)別模型要對(duì)發(fā)布包體積有個(gè)預(yù)期。安裝包制作我用的是Inno Setup它免費(fèi)、腳本清晰、支持把整個(gè)目錄打進(jìn)去。腳本里需要把models目錄也打包進(jìn)去并保證安裝后目錄結(jié)構(gòu)和開(kāi)發(fā)時(shí)一致。你安裝后程序里通過(guò)AppDomain.CurrentDomain.BaseDirectory拼接模型路徑這樣不管安裝到哪個(gè)目錄都能找到模型string baseDir AppDomain.CurrentDomain.BaseDirectory; string modelDir Path.Combine(baseDir, models);4.3 識(shí)別速度實(shí)測(cè)參考我這里給一份實(shí)測(cè)參考數(shù)據(jù)方便你心里有個(gè)底。測(cè)試機(jī)器是i5-1240P 16GB內(nèi)存Windows 11CPU模式MKLDNN模型為PP-OCRv4中文模型圖片類(lèi)型分辨率識(shí)別耗時(shí)識(shí)別效果清晰截圖1920x1080約900ms幾乎無(wú)錯(cuò)誤手機(jī)拍照1200x1600約1.8秒需預(yù)處理后可達(dá)95%以上掃描文檔1500x2000約2秒效果很好標(biāo)點(diǎn)符號(hào)偶有誤GPU模式如果有NVIDIA顯卡同樣的圖片耗時(shí)大約是CPU模式的四分之一到五分之一。不過(guò)GPU模式下模型會(huì)額外占用顯存集成顯卡機(jī)器上反而不如CPU穩(wěn)定我的建議是默認(rèn)用CPU模式只有當(dāng)識(shí)別速度成為瓶頸且目標(biāo)機(jī)器有獨(dú)立顯卡時(shí)才考慮GPU部署。5. 常見(jiàn)問(wèn)題與排查技巧5.1 問(wèn)題速查表我把實(shí)際運(yùn)行中遇到的高頻問(wèn)題整理成一張速查表開(kāi)發(fā)時(shí)可以直接對(duì)照現(xiàn)象可能原因解決辦法啟動(dòng)時(shí)DllNotFoundException缺少C運(yùn)行庫(kù)或Native DLL沒(méi)被復(fù)制安裝VC 2015-2022 x64運(yùn)行庫(kù)發(fā)布時(shí)勾選包含原生庫(kù)模型加載失敗報(bào)找不到文件模型目錄路徑錯(cuò)誤或模型文件缺失檢查inference.pdmodel和inference.pdiparams是否存在用絕對(duì)路徑測(cè)試識(shí)別結(jié)果一直為空?qǐng)D片太小、文字傾斜嚴(yán)重、檢測(cè)閾值過(guò)高放大圖片、確認(rèn)clsModelDir已配置、降低檢測(cè)閾值首次識(shí)別很慢甚至卡死引擎初始化耗時(shí)或UI線(xiàn)程直接調(diào)用引擎做成復(fù)用實(shí)例用Task.Run異步執(zhí)行GPU模式初始化報(bào)錯(cuò)CUDA/cuDNN版本與PaddleInference不匹配核對(duì)PaddleInference版本要求或改用CPU模式批量識(shí)別內(nèi)存持續(xù)上漲Run返回結(jié)果沒(méi)釋放using var result engine.Run(...)5.2 排查思路與獨(dú)家技巧排查OCR問(wèn)題我的經(jīng)驗(yàn)是先切小問(wèn)題域。識(shí)別結(jié)果不對(duì)先用官方測(cè)試圖片跑一遍看是引擎問(wèn)題還是你的圖片問(wèn)題再用一張純白底黑字的截圖跑一遍排除圖片質(zhì)量干擾。這樣一輪下來(lái)80%的問(wèn)題都能定位到具體環(huán)節(jié)。第二個(gè)技巧是善用日志和中間結(jié)果。部分版本的PaddleOcrEngine支持輸出調(diào)試日志打開(kāi)后能看到檢測(cè)框坐標(biāo)和識(shí)別置信度這是判斷“字檢測(cè)到了但識(shí)別錯(cuò)了”還是“壓根沒(méi)檢測(cè)到文字”的最直接方式能少走很多彎路。第三個(gè)技巧是關(guān)于路徑的模型目錄、圖片路徑都盡量用純英文。我在測(cè)試中文路徑圖片時(shí)偶爾會(huì)遇到Native層讀取文件失敗報(bào)錯(cuò)還不明顯換成英文路徑后問(wèn)題徹底消失。對(duì)可靠性要求高的生產(chǎn)環(huán)境這點(diǎn)非常值得注意。5.3 一個(gè)踩坑實(shí)例分享一個(gè)印象最深的坑。項(xiàng)目上線(xiàn)第一天客戶(hù)那邊反饋程序打開(kāi)就崩潰本地復(fù)現(xiàn)也復(fù)現(xiàn)不出來(lái)。后來(lái)遠(yuǎn)程一看那臺(tái)機(jī)器是Windows 7沒(méi)裝任何VC運(yùn)行庫(kù)PaddleInference的原生庫(kù)起不來(lái)程序直接閃退。解決辦法是在安裝包里加上VC運(yùn)行庫(kù)的靜默安裝步驟或者在發(fā)布時(shí)把對(duì)應(yīng)的msvcp*.dll等運(yùn)行庫(kù)一起帶過(guò)去。從那以后我在做任何C#項(xiàng)目部署時(shí)都會(huì)在安裝包里額外檢查運(yùn)行庫(kù)依賴(lài)這個(gè)習(xí)慣算是被這個(gè)坑給磨出來(lái)的。還有一次遇到GPU模式下初始化失敗折騰一上午最后發(fā)現(xiàn)是cuDNN版本不對(duì)。后來(lái)我學(xué)乖了非必要不主動(dòng)上GPU版本CPU模式雖然慢點(diǎn)但勝在穩(wěn)定不挑機(jī)器。寫(xiě)在最后的一點(diǎn)體會(huì)做這個(gè)項(xiàng)目的最大感受是離線(xiàn)OCR這件事需求看著簡(jiǎn)單但真正要穩(wěn)定落地坑都在細(xì)節(jié)里。從選型到部署每一步都在做平衡——精度、速度、部署復(fù)雜度、兼容性這四樣?xùn)|西很難同時(shí)拉滿(mǎn)。PaddleOCR這套方案目前在中文場(chǎng)景下是我用過(guò)最省心的Sdcb.PaddleOCR這個(gè)社區(qū)封裝也相當(dāng)成熟值得長(zhǎng)期跟進(jìn)。最后再分享一個(gè)小技巧如果批量處理的圖片來(lái)源固定比如都是同一臺(tái)掃描儀、同一款手機(jī)拍的建議先用一小批樣本測(cè)試把檢測(cè)閾值和預(yù)處理流程調(diào)好再全量跑效率會(huì)高很多。還有后續(xù)如果業(yè)務(wù)積累了帶標(biāo)注的樣本是可以對(duì)模型做微調(diào)的PaddleOCR的模型微調(diào)生態(tài)比較完整真到了那一步識(shí)別率還能再上一個(gè)臺(tái)階。本文還有配套的精品資源點(diǎn)擊獲取