戰(zhàn):從安裝到批量處理,離線OCR的省心之選)
1. 為什么我最終把票投給了 easyocr做圖像相關(guān)項(xiàng)目的朋友大概都有過這種經(jīng)歷手頭有一批截圖、掃描件或者手機(jī)拍的照片需要把里面的文字提取出來。第一反應(yīng)可能是去找在線OCR服務(wù)傳上去、等結(jié)果、復(fù)制下來偶爾還要擔(dān)心文件隱私。用著用著就發(fā)現(xiàn)網(wǎng)絡(luò)一卡、文件一大、格式一怪體驗(yàn)立刻斷崖式下跌。更別提有些場景根本不允許把圖片往外傳比如內(nèi)部資料整理、個(gè)人證件信息歸檔這類需求。我最早接觸OCR是從一些老牌開源引擎開始的裝起來倒是不難但識別中文的時(shí)候那個(gè)準(zhǔn)確率實(shí)在讓人撓頭尤其是遇到傾斜、光照不均、字體花哨的圖片出來的結(jié)果基本沒法直接用。后來也試過一些云端API效果確實(shí)好但免費(fèi)額度用完就得掏錢長期用下來成本不低而且每次調(diào)用都要走網(wǎng)絡(luò)批量處理幾百張圖的時(shí)候那個(gè)等待時(shí)間相當(dāng)煎熬。直到用上 easyocr才算找到了一個(gè)比較舒服的平衡點(diǎn)。它的定位很明確裝起來簡單、用起來簡單、識別效果還過得去。底層是基于深度學(xué)習(xí)的文字檢測和識別模型支持八十多種語言中文、英文、日文、韓文這些常見語種都在列而且不需要你手動做圖像預(yù)處理扔進(jìn)去一張圖它自己會搞定檢測框定位和文字識別兩件事。對于我這種不想在環(huán)境配置上折騰太久、又希望有個(gè)穩(wěn)定離線方案的人來說easyocr 基本就是那個(gè)“夠用且省心”的選擇。這篇文章我會從實(shí)際使用的角度出發(fā)把 easyocr 的安裝、核心用法、參數(shù)調(diào)優(yōu)、常見坑點(diǎn)以及一些進(jìn)階玩法都聊一遍。不管你是剛接觸OCR的新手還是已經(jīng)用過其他方案想換個(gè)工具試試的老手應(yīng)該都能從中找到對自己有用的東西。文章里涉及的操作步驟和參數(shù)配置都是我反復(fù)實(shí)測之后總結(jié)出來的你可以直接照著抄作業(yè)。2. easyocr 的安裝與首次運(yùn)行比想象中少踩很多坑2.1 環(huán)境準(zhǔn)備Python 版本和依賴管理easyocr 對 Python 版本的要求不算苛刻3.7 到 3.11 都能跑但我個(gè)人建議用 3.8 或 3.9這兩個(gè)版本在各類深度學(xué)習(xí)庫的兼容性上最穩(wěn)。如果你用的是 3.11 以上某些依賴包的預(yù)編譯輪子可能還沒跟上裝的時(shí)候容易卡在編譯環(huán)節(jié)。安裝方式有兩種看你的使用場景來選pip 直接裝適合快速上手一條命令搞定。源碼安裝適合需要改源碼或者跟進(jìn)最新特性的情況。絕大多數(shù)人用第一種就夠了。打開終端執(zhí)行pip install easyocr這條命令會自動把 torch、torchvision、opencv-python、Pillow 這些依賴一起拉下來。這里有個(gè)細(xì)節(jié)要注意easyocr 默認(rèn)裝的是 CPU 版的 torch。如果你有 NVIDIA 顯卡并且想用 GPU 加速需要先去 pytorch 官網(wǎng)找到對應(yīng) CUDA 版本的安裝命令手動裝好 GPU 版 torch然后再裝 easyocr。否則即使你有顯卡easyocr 也只會用 CPU 跑速度差距大概在五到十倍左右。怎么判斷自己裝的是 CPU 版還是 GPU 版在 Python 里跑一行import torch print(torch.cuda.is_available())輸出True就是 GPU 版可用False就是 CPU 版或者 CUDA 環(huán)境沒配好。如果是False但你有顯卡那就得重新裝 torch。提示如果你在公司內(nèi)網(wǎng)或者網(wǎng)絡(luò)受限的環(huán)境下安裝可能會遇到下載超時(shí)的問題??梢耘渲脟鴥?nèi)鏡像源來加速比如清華源或者阿里源具體命令網(wǎng)上很多這里不展開。2.2 模型下載第一次運(yùn)行時(shí)的“隱藏步驟”easyocr 裝好之后第一次調(diào)用Reader的時(shí)候會自動下載檢測模型和識別模型。檢測模型負(fù)責(zé)找出圖片里哪里有文字識別模型負(fù)責(zé)把文字內(nèi)容讀出來。這兩個(gè)模型加起來大概一百多兆下載速度取決于你的網(wǎng)絡(luò)環(huán)境。這里有個(gè)很多人會忽略的點(diǎn)模型下載是一次性的下載完之后會緩存在本地。默認(rèn)路徑在用戶目錄下的.EasyOCR/model文件夾里。如果你在多臺機(jī)器上部署可以把下載好的模型文件拷貝過去省去重復(fù)下載的時(shí)間。具體路徑可以用這段代碼查import easyocr reader easyocr.Reader([ch_sim, en]) print(reader.model_storage_directory)輸出就是模型存放的位置。把整個(gè)model文件夾復(fù)制到目標(biāo)機(jī)器的對應(yīng)位置就能跳過下載步驟。另外如果你在離線環(huán)境下工作也可以手動下載模型文件然后放到指定目錄。模型文件的下載地址在 easyocr 的官方文檔里有說明這里就不貼具體鏈接了需要的朋友可以自行查閱。2.3 跑通第一個(gè)例子從圖片到文字的完整鏈路環(huán)境準(zhǔn)備好之后來跑一個(gè)最簡單的例子。假設(shè)你有一張名為test.png的圖片里面有一段中英文混合的文字import easyocr reader easyocr.Reader([ch_sim, en]) result reader.readtext(test.png) for item in result: print(item)readtext返回的是一個(gè)列表每個(gè)元素是一個(gè)元組包含三個(gè)部分邊界框坐標(biāo)、識別出的文字、置信度。邊界框是一個(gè)四個(gè)點(diǎn)的坐標(biāo)列表文字就是識別結(jié)果置信度是一個(gè) 0 到 1 之間的浮點(diǎn)數(shù)越接近 1 表示模型越有把握。如果你只想拿文字內(nèi)容可以這樣寫texts [item[1] for item in result] print(\n.join(texts))第一次運(yùn)行的時(shí)候會看到終端里出現(xiàn)下載進(jìn)度條等模型下載完識別結(jié)果就出來了。整個(gè)過程不需要你做任何圖像預(yù)處理easyocr 內(nèi)部會自動完成灰度化、尺寸調(diào)整、歸一化這些操作。實(shí)測下來對于清晰的截圖和掃描件easyocr 的中英文識別準(zhǔn)確率相當(dāng)不錯基本能到 95% 以上。對于手機(jī)拍攝的照片如果光線均勻、文字沒有明顯傾斜效果也還可以。真正考驗(yàn)它的是那些模糊、傾斜、背景復(fù)雜的圖片這個(gè)后面會專門講怎么調(diào)優(yōu)。3. Reader 對象的參數(shù)怎么調(diào)不是越多越好而是要配對3.1 語言參數(shù)一次加載多種語言的代價(jià)與收益Reader構(gòu)造函數(shù)的第一個(gè)參數(shù)是語言列表。easyocr 支持的語言很多常見的有語言代碼對應(yīng)語言ch_sim簡體中文ch_tra繁體中文en英文ja日文ko韓文fr法文de德文es西班牙文你可以同時(shí)傳多個(gè)語言比如[ch_sim, en]表示同時(shí)識別簡體中文和英文。這里有個(gè)重要的經(jīng)驗(yàn)語言組合會影響識別速度和準(zhǔn)確率。每多加載一種語言識別模型就會變大推理時(shí)間也會增加。而且某些語言之間的字符集有重疊組合不當(dāng)反而會降低準(zhǔn)確率。我的建議是只加載你實(shí)際需要的語言。如果你只處理中文文檔就只傳[ch_sim]不要順手把英文也加上。中英文混合的場景確實(shí)需要同時(shí)加載兩個(gè)但日文和韓文如果不同時(shí)出現(xiàn)就沒必要一起加載。還有一個(gè)細(xì)節(jié)ch_sim和ch_tra不要同時(shí)加載。這兩個(gè)模型的字符集高度重疊同時(shí)加載會導(dǎo)致模型在判斷某個(gè)字是簡體還是繁體時(shí)產(chǎn)生混淆實(shí)測準(zhǔn)確率會下降。如果你的圖片里簡繁體都有優(yōu)先選ch_sim它對繁體字的兼容性比想象中好。3.2 GPU 開關(guān)什么時(shí)候該開什么時(shí)候不該開Reader的gpu參數(shù)控制是否使用 GPU 加速。默認(rèn)值是True但前提是你裝了 GPU 版的 torch。如果你只有 CPU這個(gè)參數(shù)設(shè)成True也不會報(bào)錯只是會自動回退到 CPU 模式但會多花一點(diǎn)檢查時(shí)間。reader easyocr.Reader([ch_sim, en], gpuTrue)什么時(shí)候該開 GPU批量處理大量圖片的時(shí)候。單張圖片的識別時(shí)間在 CPU 上大概是 1 到 3 秒GPU 上可以降到 0.2 到 0.5 秒。如果你要處理幾百上千張圖這個(gè)差距就非常明顯了。但如果你只是偶爾識別一兩張圖CPU 完全夠用沒必要折騰 GPU 環(huán)境。而且 GPU 模式下顯存占用會隨著圖片尺寸增大而增加處理超大圖的時(shí)候可能遇到顯存不足的問題這時(shí)候反而要回退到 CPU 或者先把圖片縮小。注意如果你在服務(wù)器上部署而服務(wù)器有多張顯卡可以通過CUDA_VISIBLE_DEVICES環(huán)境變量指定用哪張卡避免和其他任務(wù)搶資源。3.3 其他實(shí)用參數(shù)verbose、model_storage_directory 等Reader還有幾個(gè)參數(shù)值得關(guān)注verbose默認(rèn)True會打印模型加載和下載的日志。在生產(chǎn)環(huán)境里建議設(shè)成False避免日志刷屏。model_storage_directory指定模型存放路徑。多項(xiàng)目共用模型的時(shí)候很有用可以避免重復(fù)下載。download_enabled默認(rèn)True如果設(shè)成False模型不存在時(shí)不會自動下載而是直接報(bào)錯。離線部署的時(shí)候用得上。user_network_directory自定義識別網(wǎng)絡(luò)的存放路徑進(jìn)階用戶才會用到。這些參數(shù)不需要一開始就全部搞懂用到的時(shí)候查一下就行。我的習(xí)慣是先把verboseFalse加上讓輸出干凈一點(diǎn)。4. 識別效果不理想時(shí)我從哪些方向入手排查4.1 圖片預(yù)處理的取舍easyocr 幫你做了哪些沒做哪些easyocr 內(nèi)部確實(shí)做了一些預(yù)處理比如把圖片轉(zhuǎn)成 RGB、調(diào)整尺寸、歸一化像素值。但它不會幫你做去噪、二值化、糾偏這些操作。如果你的圖片質(zhì)量很差指望 easyocr 直接輸出完美結(jié)果是不現(xiàn)實(shí)的。我遇到過幾種典型的“識別效果差”場景圖片分辨率太低文字只有十幾個(gè)像素高模型根本看不清。這種情況再怎么調(diào)參數(shù)都沒用只能換圖或者放大圖片。文字傾斜嚴(yán)重比如拍攝角度偏了三十度以上檢測框會歪識別準(zhǔn)確率驟降。背景復(fù)雜文字壓在花紋、漸變、水印上面檢測模型容易漏檢或者框錯位置。光照不均一半亮一半暗暗處的文字識別不出來。針對這些情況我的處理策略是先判斷問題出在檢測還是識別。如果檢測框都沒框?qū)δ菃栴}在檢測階段需要做圖像增強(qiáng)如果框?qū)α说淖肿x錯了那問題在識別階段可以嘗試調(diào)整識別參數(shù)或者換語言模型。一個(gè)簡單的判斷方法把readtext返回的邊界框畫到原圖上看看框的位置準(zhǔn)不準(zhǔn)。如果框是準(zhǔn)的那識別錯誤大概率是圖片清晰度或者字體的問題。4.2 調(diào)整 text_threshold 和 low_text控制檢測靈敏度的兩個(gè)旋鈕readtext方法支持傳入一些檢測相關(guān)的參數(shù)其中兩個(gè)最常用text_threshold文字置信度閾值默認(rèn) 0.7。調(diào)低會讓更多區(qū)域被判定為文字適合文字比較淡的圖片調(diào)高會過濾掉一些不確定的區(qū)域適合背景干擾多的圖片。low_text低置信度文字的閾值默認(rèn) 0.4。這個(gè)參數(shù)控制的是“邊緣文字”的判定調(diào)低可以保留更多模糊文字但可能引入噪聲。result reader.readtext(test.png, text_threshold0.6, low_text0.3)這兩個(gè)參數(shù)需要配合使用。我的經(jīng)驗(yàn)是如果漏檢嚴(yán)重先把 text_threshold 降到 0.5 試試如果誤檢太多把它升到 0.8。low_text 一般不用動除非你處理的是手寫體或者藝術(shù)字。還有一個(gè)參數(shù)叫l(wèi)ink_threshold默認(rèn) 0.4控制的是文字區(qū)域之間的連接。對于字間距很大的文本調(diào)低這個(gè)值有助于把同一行的文字連起來。但這個(gè)參數(shù)比較敏感調(diào)不好反而會把不該連的區(qū)域連在一起建議謹(jǐn)慎使用。4.3 處理大圖時(shí)的分塊策略避免顯存溢出和精度下降easyocr 對輸入圖片的尺寸沒有硬性限制但圖片太大會導(dǎo)致兩個(gè)問題顯存占用飆升和小文字識別精度下降。因?yàn)槟P驮谕评砬皶褕D片縮放到固定尺寸如果原圖很大縮放后小文字就變得模糊了。我的做法是如果圖片寬度超過 2000 像素先切塊再識別。切塊的方式有兩種按固定尺寸切比如每塊 1000x1000塊之間留 100 像素重疊避免文字被切斷。按內(nèi)容切先做一次粗略檢測找到文字密集區(qū)域再針對這些區(qū)域切塊。第一種方式實(shí)現(xiàn)簡單適合大多數(shù)場景。第二種方式更精細(xì)但實(shí)現(xiàn)復(fù)雜度高適合對精度要求極高的場景。切塊之后每塊單獨(dú)調(diào)用readtext最后把結(jié)果按坐標(biāo)合并。合并的時(shí)候要注意去重因?yàn)橹丿B區(qū)域可能會被識別兩次。import cv2 img cv2.imread(large_image.png) h, w img.shape[:2] block_size 1000 overlap 100 results [] for y in range(0, h, block_size - overlap): for x in range(0, w, block_size - overlap): block img[y:yblock_size, x:xblock_size] block_results reader.readtext(block) for bbox, text, conf in block_results: # 把局部坐標(biāo)轉(zhuǎn)成全局坐標(biāo) global_bbox [[p[0]x, p[1]y] for p in bbox] results.append((global_bbox, text, conf))這段代碼只是示意實(shí)際用的時(shí)候還要處理邊界情況和去重邏輯。但思路就是這樣分而治之最后合并。5. 批量處理和性能優(yōu)化從單張到上千張的實(shí)戰(zhàn)經(jīng)驗(yàn)5.1 批量識別的代碼骨架循環(huán)、進(jìn)度條和異常捕獲單張識別跑通之后下一步就是批量處理。最樸素的寫法就是一個(gè) for 循環(huán)import os import easyocr reader easyocr.Reader([ch_sim, en], verboseFalse) image_dir ./images output_dir ./results os.makedirs(output_dir, exist_okTrue) for filename in os.listdir(image_dir): if not filename.lower().endswith((.png, .jpg, .jpeg, .bmp)): continue filepath os.path.join(image_dir, filename) try: result reader.readtext(filepath) texts [item[1] for item in result] with open(os.path.join(output_dir, filename .txt), w, encodingutf-8) as f: f.write(\n.join(texts)) except Exception as e: print(f處理 {filename} 時(shí)出錯: {e})這個(gè)骨架夠用但有幾個(gè)地方可以優(yōu)化進(jìn)度顯示用tqdm庫加個(gè)進(jìn)度條處理大量文件的時(shí)候心里有數(shù)。異常捕獲單張圖片出錯不應(yīng)該中斷整個(gè)批次用 try-except 包起來記錄出錯的文件名事后單獨(dú)處理。結(jié)果格式除了純文本還可以把邊界框和置信度一起保存成 JSON方便后續(xù)分析。5.2 多進(jìn)程加速什么時(shí)候值得上什么時(shí)候反而更慢Python 的 GIL 限制了多線程的并行能力所以批量處理想加速一般用多進(jìn)程。multiprocessing或者concurrent.futures.ProcessPoolExecutor都可以。但這里有個(gè)坑每個(gè)進(jìn)程都會加載一份模型。如果你的模型是中文加英文一份大概占幾百兆內(nèi)存開四個(gè)進(jìn)程就是一千多兆。內(nèi)存不夠的機(jī)器直接卡死。而且模型加載本身也要時(shí)間如果每個(gè)進(jìn)程處理的任務(wù)很少加載模型的開銷可能比識別本身還大。我的經(jīng)驗(yàn)是只有當(dāng)單批任務(wù)超過 50 張圖片且機(jī)器內(nèi)存充足至少 8GB 以上時(shí)才考慮多進(jìn)程。否則單進(jìn)程慢慢跑反而更穩(wěn)。如果確實(shí)需要加速可以考慮把模型加載放在進(jìn)程初始化階段讓每個(gè)進(jìn)程只加載一次模型然后循環(huán)處理分配給它的任務(wù)。from concurrent.futures import ProcessPoolExecutor import easyocr def init_reader(): global reader reader easyocr.Reader([ch_sim, en], verboseFalse) def process_image(filepath): result reader.readtext(filepath) return [item[1] for item in result] # 使用 initializer 讓每個(gè)進(jìn)程只加載一次模型 with ProcessPoolExecutor(max_workers4, initializerinit_reader) as executor: futures [executor.submit(process_image, fp) for fp in file_list] for future in futures: print(future.result())這段代碼的關(guān)鍵是initializer參數(shù)它確保每個(gè)子進(jìn)程在開始處理任務(wù)之前先加載好模型避免重復(fù)加載的開銷。5.3 內(nèi)存和顯存監(jiān)控別讓程序跑著跑著就崩了批量處理最怕的就是跑了一半程序崩了前面的結(jié)果全丟。除了做好異常捕獲和斷點(diǎn)續(xù)跑還要監(jiān)控內(nèi)存和顯存的使用情況。在 Linux 上可以用htop或者nvidia-smi實(shí)時(shí)看。在 Python 里可以用psutil查內(nèi)存用torch.cuda.memory_allocated()查顯存。import psutil import torch print(f內(nèi)存使用: {psutil.Process().memory_info().rss / 1024 / 1024:.1f} MB) if torch.cuda.is_available(): print(f顯存使用: {torch.cuda.memory_allocated() / 1024 / 1024:.1f} MB)如果發(fā)現(xiàn)內(nèi)存持續(xù)增長不釋放可能是圖片對象或者中間結(jié)果沒有及時(shí)回收??梢栽诿刻幚硗暌慌笫謩诱{(diào)用gc.collect()或者把處理邏輯封裝成函數(shù)讓局部變量自然釋放。還有一個(gè)容易忽略的點(diǎn)OpenCV 讀取的圖片是 BGR 格式而 easyocr 期望的是 RGB 格式。如果你用cv2.imread讀圖然后直接傳給readtext顏色通道是反的雖然大多數(shù)時(shí)候不影響識別但在某些顏色對比度低的圖片上可能會導(dǎo)致檢測失敗。穩(wěn)妥的做法是手動轉(zhuǎn)換img cv2.imread(test.png) img_rgb cv2.cvtColor(img, cv2.COLOR_BGR2RGB) result reader.readtext(img_rgb)或者直接用PIL讀圖它默認(rèn)就是 RGB 格式。6. 那些文檔里不會寫的踩坑記錄6.1 模型下載失敗和緩存混亂的處理前面提到過模型會自動下載但實(shí)際用的時(shí)候可能會遇到下載失敗的情況。常見原因有兩個(gè)網(wǎng)絡(luò)不穩(wěn)定和緩存目錄權(quán)限不對。如果下載到一半斷了重新運(yùn)行的時(shí)候 easyocr 會嘗試?yán)m(xù)傳但有時(shí)候緩存文件損壞了它會一直卡在那里。這時(shí)候需要手動把.EasyOCR/model目錄下的臨時(shí)文件刪掉重新下載。還有一種情況是緩存目錄混亂比如你先裝了英文模型后來又裝了中文模型兩個(gè)模型的文件名可能有沖突。easyocr 的模型命名規(guī)則是按語言代碼來的正常情況下不會沖突但如果你手動拷貝過模型文件就可能出現(xiàn)版本不匹配的問題。這時(shí)候最簡單的辦法是把整個(gè)model目錄刪掉讓它重新下載。提示如果你在 Docker 容器里跑 easyocr記得把模型目錄掛載到宿主機(jī)上否則每次重建容器都要重新下載模型浪費(fèi)時(shí)間。6.2 中文識別中的標(biāo)點(diǎn)符號和空格問題easyocr 識別中文的時(shí)候標(biāo)點(diǎn)符號的處理有點(diǎn)意思。它會把中文標(biāo)點(diǎn)識別成對應(yīng)的全角符號比如逗號識別成“”句號識別成“?!边@沒問題。但有時(shí)候它會把英文標(biāo)點(diǎn)也識別成全角比如把“.”識別成“?!边@在處理中英文混合文本的時(shí)候會造成困擾??崭竦奶幚硪灿蓄愃茊栴}。中文文本里通常沒有空格但 easyocr 有時(shí)候會在漢字之間插入空格尤其是當(dāng)字間距比較大的時(shí)候。如果你后續(xù)要做文本分析這些多余的空格需要清理掉。我的處理方式是識別結(jié)果先不做任何修改保存原始輸出然后在后處理階段根據(jù)具體需求做清洗。比如做中文分詞之前先把連續(xù)空格替換成單個(gè)空格再把中文字符之間的空格去掉。這樣既保留了原始信息又能得到干凈的文本。6.3 豎排文字和特殊字體的識別表現(xiàn)easyocr 對橫排文字的識別效果最好豎排文字也能識別但準(zhǔn)確率會下降。如果你處理的是古籍或者某些特殊排版的文檔豎排文字比較多可能需要額外的處理先把圖片旋轉(zhuǎn) 90 度讓文字變成橫排再識別。特殊字體方面印刷體基本沒問題手寫體就看情況了。工整的手寫體識別率還行潦草的手寫體基本沒法看。藝術(shù)字、裝飾性字體也是類似越接近標(biāo)準(zhǔn)印刷體識別效果越好。如果確實(shí)需要識別特殊字體可以考慮自己訓(xùn)練模型。easyocr 支持自定義識別網(wǎng)絡(luò)但這屬于進(jìn)階玩法需要準(zhǔn)備標(biāo)注數(shù)據(jù)、設(shè)計(jì)網(wǎng)絡(luò)結(jié)構(gòu)、訓(xùn)練調(diào)參投入的時(shí)間精力不小。對于大多數(shù)應(yīng)用場景用預(yù)訓(xùn)練模型加上合理的預(yù)處理就夠了。7. 把 easyocr 接入實(shí)際工作流的幾種思路7.1 截圖轉(zhuǎn)文字日常辦公場景的自動化我平時(shí)寫東西的時(shí)候經(jīng)常需要引用一些截圖里的文字手動敲太慢用在線OCR又嫌麻煩。后來寫了個(gè)小腳本監(jiān)聽剪貼板一旦發(fā)現(xiàn)新圖片就自動識別并把文字寫回剪貼板。這樣截圖之后直接粘貼就是文字效率提升很明顯。實(shí)現(xiàn)思路是用Pillow的ImageGrab模塊監(jiān)聽剪貼板或者用pyperclip配合系統(tǒng)剪貼板 API。識別部分就是調(diào)用 easyocr把結(jié)果拼接成字符串再寫回剪貼板。這個(gè)腳本我用了大半年穩(wěn)定性不錯唯一需要注意的是識別大圖的時(shí)候會有幾秒延遲習(xí)慣就好。7.2 文檔數(shù)字化掃描件批量轉(zhuǎn)可編輯文本掃描件數(shù)字化的需求很常見比如把紙質(zhì)合同、發(fā)票、報(bào)表轉(zhuǎn)成可搜索的 PDF 或者純文本。這個(gè)場景的特點(diǎn)是圖片數(shù)量多、格式統(tǒng)一、對準(zhǔn)確率要求高。我的做法是分三步走預(yù)處理、批量識別、后處理。預(yù)處理包括去噪、糾偏、二值化用 OpenCV 就能搞定。批量識別用前面講的循環(huán)加異常捕獲。后處理包括拼寫檢查、格式整理、關(guān)鍵信息提取。如果掃描件是表格識別出來的文字還需要按行列對齊。這個(gè)用 easyocr 的邊界框坐標(biāo)可以做根據(jù) y 坐標(biāo)判斷行根據(jù) x 坐標(biāo)判斷列然后重組表格結(jié)構(gòu)。實(shí)現(xiàn)起來有點(diǎn)繁瑣但邏輯是清晰的。7.3 和 PDF 處理庫配合從 PDF 到結(jié)構(gòu)化數(shù)據(jù)PDF 文件分兩種文本型 PDF和掃描型 PDF。文本型 PDF 可以直接用pdfplumber或者PyPDF2提取文字不需要 OCR。掃描型 PDF 本質(zhì)上是一堆圖片需要先轉(zhuǎn)成圖片再識別。處理掃描型 PDF 的流程是用pdf2image把每頁轉(zhuǎn)成圖片然后用 easyocr 識別最后把結(jié)果按頁組織。pdf2image依賴poppler在 Linux 上需要先安裝poppler-utils在 Windows 上需要下載 poppler 的二進(jìn)制包并配置環(huán)境變量。from pdf2image import convert_from_path import easyocr reader easyocr.Reader([ch_sim, en], verboseFalse) pages convert_from_path(document.pdf, dpi300) all_text [] for i, page in enumerate(pages): result reader.readtext(page) page_text \n.join([item[1] for item in result]) all_text.append(f--- 第 {i1} 頁 ---\n{page_text}) with open(output.txt, w, encodingutf-8) as f: f.write(\n\n.join(all_text))這里dpi300是個(gè)經(jīng)驗(yàn)值太低會模糊太高會慢且占內(nèi)存。300 對于大多數(shù)文檔來說夠用了。8. 關(guān)于 easyocr 的一些個(gè)人體會用 easyocr 這段時(shí)間最大的感受是它把 OCR 的門檻降到了很低。以前搞 OCR 要折騰環(huán)境、調(diào)參數(shù)、處理各種格式兼容問題現(xiàn)在幾行代碼就能跑起來而且效果還不差。對于個(gè)人項(xiàng)目和小型應(yīng)用來說這個(gè)性價(jià)比非常高。當(dāng)然它也不是萬能的。如果你的場景對準(zhǔn)確率要求極高比如金融票據(jù)識別、法律文書處理那可能還是需要商業(yè) API 或者自己訓(xùn)練模型。但對于日常的截圖轉(zhuǎn)文字、文檔數(shù)字化、圖片信息提取這些需求easyocr 完全夠用。還有一個(gè)我覺得很實(shí)用的點(diǎn)easyocr 的返回結(jié)果包含置信度和邊界框這意味著你可以根據(jù)置信度做過濾只保留高置信度的結(jié)果或者根據(jù)邊界框做版面分析。這個(gè)靈活性是很多在線 OCR 服務(wù)不具備的。最后分享一個(gè)小技巧如果你發(fā)現(xiàn)某個(gè)語言的識別效果不理想可以試試把圖片放大兩倍再識別。easyocr 內(nèi)部會做尺寸調(diào)整但有時(shí)候原圖太小放大之后反而能提升檢測和識別的準(zhǔn)確率。這個(gè)技巧在處理小字號的截圖時(shí)特別管用。