定位:醫(yī)療單據(jù)脫敏的工程實(shí)踐)
1. 為什么醫(yī)療單據(jù)脫敏不能靠現(xiàn)成的“打碼濾鏡”直接搞定1.1 一個(gè)看似簡單實(shí)際翻車的場景拍照單據(jù)我接手過一個(gè)醫(yī)療單據(jù)批量歸檔項(xiàng)目甲方最初的需求很輕描淡寫“把單據(jù)上的姓名、身份證號(hào)、手機(jī)號(hào)、家庭住址打碼就行。”我一開始也覺得這活兒簡單找個(gè)現(xiàn)成的OCR抽文字正則匹配一下身份證和手機(jī)號(hào)再用矩形覆蓋掉半天就能交付。真正拿到樣張之后才發(fā)現(xiàn)事情完全不是這樣。這些醫(yī)療單據(jù)不是干凈的掃描件而是工作人員用手機(jī)拍的有的單據(jù)被折過一道有的拍攝角度偏了30度有的局部還有陰影和反光。姓名在表格里地址跨了三行病歷描述里藏著“患者自述”“既往史”這類不能公開的敘述。如果用固定位置打碼稍微旋轉(zhuǎn)一點(diǎn)就對(duì)不上如果用矩形框粗暴覆蓋紙上的表格線、印章、二維碼都會(huì)被連帶涂掉片子根本沒法看。那時(shí)候我才意識(shí)到醫(yī)療單據(jù)脫敏本質(zhì)上不是一個(gè)圖像處理問題而是一個(gè)“先理解版面再理解語義最后精確破壞敏感區(qū)域”的復(fù)合問題。標(biāo)題里那句“OCR Vision LLM 四點(diǎn)定位打碼”并不是三個(gè)模塊的簡單疊加而是分別解決“文字在哪”“哪些算隱私”“怎樣打碼不傷版面”這三件事。1.2 通用脫敏方案的三大缺陷漏檢、錯(cuò)檢、破壞版面市面上現(xiàn)成的脫敏工具我也試用過幾輪對(duì)干凈掃描件還行一旦落到復(fù)雜拍攝場景問題就暴露出來了。第一個(gè)缺陷是漏檢。手機(jī)拍照產(chǎn)生的透視變形會(huì)讓OCR的檢測框發(fā)生偏移。角度稍微大一點(diǎn)文字就容易粘連在一起身份證號(hào)被識(shí)別成亂碼手機(jī)號(hào)漏掉中間幾位。漏檢意味著敏感信息原樣留在圖片里這在醫(yī)療場景是絕對(duì)不能接受的。第二個(gè)缺陷是錯(cuò)檢。很多工具為了求穩(wěn)把所有數(shù)字都當(dāng)成潛在手機(jī)號(hào)把“2024-03-15”“門診號(hào)0382910”也一并打碼。結(jié)果病人姓名沒遮住反而把就診日期和單據(jù)編號(hào)全涂了歸檔審核時(shí)直接被打回。第三個(gè)缺陷是破壞版面。矩形框打碼在水平文檔上還能用但醫(yī)療單據(jù)上的敏感字段往往緊挨著表格線、印章或者手寫簽名。一旦矩形框稍有偏差就會(huì)涂掉無關(guān)內(nèi)容或者留下半個(gè)字在外面。更麻煩的是彎折單據(jù)上的文字區(qū)域是變形的矩形框根本包不住一個(gè)傾斜的多邊形文字塊。所以后來我把方案定成了“四點(diǎn)定位打碼”——每個(gè)敏感字段用任意四邊形框住而不是用軸對(duì)齊矩形。1.3 拆解敏感信息類型字段級(jí)語義比正則更可靠脫敏的第一步其實(shí)是搞清楚一張醫(yī)療單據(jù)上到底哪些內(nèi)容需要處理。常見的敏感類型包括姓名、性別、年齡、出生日期等基本身份字段身份證號(hào)、醫(yī)??ㄌ?hào)、就診卡號(hào)、住院號(hào)等證件編號(hào)手機(jī)號(hào)、家庭住址、聯(lián)系人信息既往病史、過敏史、主訴、診斷描述等病歷敘述檢查報(bào)告中的“患者姓名”“送檢醫(yī)生”“報(bào)告醫(yī)生”等關(guān)聯(lián)字段。前兩類靠正則還能勉強(qiáng)覆蓋但“既往病史”“主訴”這種自由文本正則沒有辦法判斷哪個(gè)詞該打碼。我把這些字段梳理了一遍之后結(jié)論很明確必須引入一個(gè)有語義理解能力的視覺模型讓它直接看圖說話判斷某個(gè)OCR文本框?qū)?yīng)的內(nèi)容屬于哪一類敏感字段。這就是Vision LLM在這個(gè)項(xiàng)目里的核心位置。2. 方案選型思路OCR、視覺大模型和四點(diǎn)定位各自承擔(dān)什么2.1 OCR選型與適用邊界要的是文本框不是純文本項(xiàng)目的第一步是定位文字位置。這里有個(gè)很容易走偏的點(diǎn)很多人把OCR理解成“把圖變成文字”但在這個(gè)項(xiàng)目里OCR輸出的文字內(nèi)容只是輔助信息真正重要的是每個(gè)文字塊對(duì)應(yīng)的坐標(biāo)。我測試過幾種主流的開源OCR框架最后留下了兩個(gè)候選一種是學(xué)術(shù)型檢測識(shí)別分離的框架檢測模型輸出文本框識(shí)別模型再輸出文字另一種是端到端模型直接輸出帶坐標(biāo)的文字結(jié)果。在復(fù)雜拍攝場景下檢測識(shí)別分離的框架更容易調(diào)優(yōu)因?yàn)槎鄶?shù)漏檢問題出在檢測環(huán)節(jié)我可以單獨(dú)強(qiáng)化檢測模型而不影響識(shí)別邏輯。選型時(shí)有幾個(gè)關(guān)鍵參數(shù)需要關(guān)注文本框格式是否支持任意四邊形四點(diǎn)坐標(biāo)還是只有水平矩形。支持四點(diǎn)坐標(biāo)的模型能更好地貼合傾斜、旋轉(zhuǎn)的文字塊。多方向文本支持醫(yī)療單據(jù)上既有橫向打印文字也有豎向手寫備注甚至印章里的文字是旋轉(zhuǎn)排列的。選型時(shí)要看模型訓(xùn)練數(shù)據(jù)里是否包含多方向文本。推理速度單片單據(jù)的目標(biāo)延遲最好控制在2秒以內(nèi)否則批量歸檔跑不動(dòng)。2.2 視覺大模型負(fù)責(zé)理解語義哪些文字是隱私OCR只能回答“這里有文字”不能回答“這句話是不是隱私”。醫(yī)療單據(jù)里“張某某”可能是患者名也可能是醫(yī)生名“138XXXX1234”可能是患者手機(jī)號(hào)也可能是醫(yī)院咨詢電話。這些都需要語義判斷。Vision LLM在我的方案里承擔(dān)三件事對(duì)每個(gè)OCR文本框做字段類型分類輸出“姓名/證件號(hào)/聯(lián)系電話/地址/病史描述/非敏感”等標(biāo)簽綜合上下文判斷遮擋范圍比如遇到“聯(lián)系人李某某關(guān)系夫妻”這種一行里出現(xiàn)兩個(gè)字段的情況要分別識(shí)別對(duì)OCR漏檢做二次補(bǔ)充視覺模型可以同時(shí)看整張圖和所有文本框如果發(fā)現(xiàn)某段文字明顯像身份證號(hào)但OCR沒框出來就觸發(fā)重新檢測。實(shí)際使用中Vision LLM的輸入不是整張大圖直接輸入而是把OCR裁剪出的文本塊圖像和對(duì)應(yīng)的文字內(nèi)容一起送進(jìn)去。這樣能減少干擾也能讓模型把“圖像里的文字樣式”和“文字語義”結(jié)合起來判斷。比如手寫的“主訴咳嗽三天”會(huì)被判定為敏感病歷信息而表格里的“項(xiàng)目名稱血常規(guī)”則不會(huì)。2.3 四點(diǎn)定位打碼解決多邊形旋轉(zhuǎn)不規(guī)則區(qū)域避免矩形誤傷為什么不用矩形框我用一個(gè)具體例子說明。一張門診收費(fèi)票據(jù)上“姓名張某”這個(gè)字段在表格里緊挨著豎線如果按OCR框的水平外接矩形打碼矩形會(huì)向右擴(kuò)展把旁邊的金額數(shù)字也蓋住。但如果用OCR檢測出的四邊坐標(biāo)直接形成任意四邊形就能只蓋住文字本身不碰到表格線。四點(diǎn)定位的核心是維護(hù)好“四邊形的四個(gè)角點(diǎn)”到“實(shí)際圖像像素區(qū)域”的映射關(guān)系。在預(yù)處理階段圖片可能被矯正、縮放所以所有坐標(biāo)都必須在一個(gè)統(tǒng)一的坐標(biāo)系里跟蹤。我的做法是OCR輸出的坐標(biāo)先按原始圖片尺寸歸一化預(yù)處理環(huán)節(jié)的所有變換旋轉(zhuǎn)、透視矯正、縮放都記錄變換矩陣打碼前用變換矩陣逆運(yùn)算把OCR坐標(biāo)映射回最終輸出圖的坐標(biāo)空間對(duì)四邊形做整體外擴(kuò)避免只蓋住文字主體而漏掉邊緣筆畫。這套流程跑通之后即便單據(jù)被彎折過只要OCR能檢測出文字塊四點(diǎn)坐標(biāo)就能貼合文字的實(shí)際形狀打碼邊緣不會(huì)出現(xiàn)“有些筆畫蓋住了有些筆畫還露在外面”的情況。3. 復(fù)雜拍攝場景的適配彎曲、旋轉(zhuǎn)、透視畸變的預(yù)處理鏈路3.1 為什么模型直接跑會(huì)掉點(diǎn)拍攝角度的透視變形剛開始我用原始圖片直接跑OCR結(jié)果讓我很意外印刷體的識(shí)別準(zhǔn)確率尚可但文本框的位置經(jīng)常整體向右下方偏移導(dǎo)致后邊的打碼框跟不上。仔細(xì)分析了原因手機(jī)拍攝單據(jù)時(shí)鏡頭平面和單據(jù)平面不平行產(chǎn)生了透視變形。文字在圖像上不再是一行水平排列而是近大遠(yuǎn)小、行線收斂于遠(yuǎn)處某一點(diǎn)。透視變形最直接的影響是OCR檢測框的“傾斜角”變大。模型能檢測出斜框但置信度下降偶爾會(huì)把一行文字?jǐn)喑蓛蓧K或者把相鄰兩行粘連在一起。如果不在預(yù)處理階段做矯正后面每一步的誤差都會(huì)累積。3.2 彎曲矯正基于文本行幾何分布的矯正思路單據(jù)被折疊或彎折時(shí)文字行會(huì)變成曲線。比如一張橫向折過的化驗(yàn)單折痕附近的文字會(huì)被壓縮下面的行會(huì)輕微扭曲。對(duì)這種彎曲我嘗試過傳統(tǒng)的網(wǎng)格矯正但效果一般因?yàn)閱螕?jù)上的表格線會(huì)干擾網(wǎng)格生成。后來換了一個(gè)思路先跑一遍OCR拿到所有文本行的四點(diǎn)坐標(biāo)然后按文本行中心點(diǎn)擬合彎曲曲線再根據(jù)曲線把圖像展開。這個(gè)方法不需要額外的矯正模型純粹基于OCR結(jié)果做幾何變換實(shí)現(xiàn)起來更穩(wěn)。具體流程是收集所有文本行的中心點(diǎn)坐標(biāo)按行分組對(duì)每一行擬合二次曲線以曲線為基準(zhǔn)估計(jì)行之間的間距變化用分段仿射變換把彎曲區(qū)域映射到水平規(guī)則網(wǎng)格。這套方法對(duì)輕度到中度彎折效果很好對(duì)嚴(yán)重彎折比如單據(jù)被揉成一團(tuán)再展開效果有限那種情況只能提示用戶重新拍攝。3.3 旋轉(zhuǎn)和透視處理的統(tǒng)一坐標(biāo)映射旋轉(zhuǎn)和透視可以統(tǒng)一用矩陣運(yùn)算處理。實(shí)際操作中我維護(hù)了一個(gè)全局變換棧每次對(duì)圖像做旋轉(zhuǎn)、縮放或透視矯正都會(huì)把一個(gè)對(duì)應(yīng)的變換矩陣壓入棧中。最終輸出打碼結(jié)果時(shí)把OCR原始坐標(biāo)經(jīng)過所有矩陣變換得到當(dāng)前圖像坐標(biāo)。這里最容易踩的坑是圖像尺寸變化。有些圖像處理庫在執(zhí)行透視變換后會(huì)自動(dòng)擴(kuò)大畫布以保證內(nèi)容不丟失導(dǎo)致圖片尺寸改變。如果代碼里仍然用原始坐標(biāo)打碼所有框都會(huì)偏移。我的解決辦法是每次變換后都重新獲取圖像的新尺寸并把變換矩陣更新為“從原始圖像坐標(biāo)到當(dāng)前圖像坐標(biāo)”的完整映射。3.4 預(yù)處理鏈路的實(shí)際效果對(duì)比預(yù)處理鏈路處理好之后OCR的F1值在測試集上提升非常明顯。我用500張手機(jī)拍攝的醫(yī)療單據(jù)做對(duì)比未預(yù)處理時(shí)文本檢測的召回率大概只有82%加完旋轉(zhuǎn)和透視矯正后召回率提升到93%再加上彎曲矯正提升到96%。最關(guān)鍵的是文本框的穩(wěn)定性好了很多不再出現(xiàn)同一張單據(jù)跑兩次結(jié)果不一樣的情況。值得注意的是預(yù)處理不能做得太激進(jìn)。過度矯正會(huì)把單據(jù)拉成奇怪的形狀反而讓OCR更困惑。我最后采用的策略是只有當(dāng)檢測到文本行傾斜角度中位數(shù)超過15度或者文本行中心點(diǎn)擬合曲線的殘差超過閾值時(shí)才觸發(fā)矯正。在大部分輕度傾斜的場景下直接讓OCR自適應(yīng)反而更快。4. 核心實(shí)現(xiàn)從檢測到脫敏的完整流程4.1 第一層OCR文本框提取與置信度過濾整個(gè)脫敏流水線分三層。第一層是OCR文本檢測與識(shí)別。代碼結(jié)構(gòu)大致如下def extract_text_boxes(image): # image: 經(jīng)過預(yù)處理的BGR圖像 result ocr_detector.predict(image) boxes [] for line in result: box line[box] # 四個(gè)點(diǎn)的坐標(biāo) [[x1,y1],...] text line[text] score line[score] if score 0.45: continue boxes.append({ box: box, text: text, score: score }) return boxes置信度過濾不能一刀切。對(duì)于打印體字段閾值調(diào)到0.5都沒問題但對(duì)于手寫體、印章、帶背景紋理的文字閾值如果設(shè)太高會(huì)把該檢測的字段漏掉。我最終的策略是先把所有大于0.3的框全部留下來交給視覺大模型做過濾視覺模型認(rèn)為某個(gè)區(qū)域重要但置信度低時(shí)會(huì)觸發(fā)二次檢測。4.2 第二層視覺大模型語義分類內(nèi)部/公開字段拿到所有文本框后把每塊文本的裁剪圖像和OCR文字組合起來送給視覺大模型。這里的Prompt設(shè)計(jì)很關(guān)鍵。我早期的做法是讓模型直接輸出JSON標(biāo)簽但模型容易把“既往史”里的“史”誤判成“時(shí)間”。后來我把問題改成更具體的分類如果是身份字段返回包含“姓名”“證件號(hào)”“手機(jī)號(hào)”等具體類型如果是醫(yī)療敘述字段返回“主訴/現(xiàn)病史/既往史”等子類型如果既不屬于身份也不屬于醫(yī)療敘述返回“公開”。Prompt長這樣你正在處理一張醫(yī)療單據(jù)。下面每個(gè)文本塊可能包含敏感信息。 請(qǐng)對(duì)每個(gè)文本塊進(jìn)行分類類別包括: 姓名、性別、年齡、證件號(hào)碼、聯(lián)系電話、家庭住址、醫(yī)保信息、主訴、現(xiàn)病史、既往史、診斷結(jié)論、檢查報(bào)告、非敏感。 輸出JSON數(shù)組包含每個(gè)文本塊的id和category。 注意: 根據(jù)文本內(nèi)容和上下文綜合判斷不要僅憑單一關(guān)鍵詞。為了讓模型不“亂聯(lián)想”我會(huì)把整張單據(jù)的OCR文本按閱讀順序拼成上下文讓模型參考上下文來分類。比如“患者張某某聯(lián)系電話138...”兩個(gè)字段在一個(gè)文本塊里單看前半句可能是姓名后半句又是電話模型會(huì)切開成兩條。這一步是純規(guī)則很難做到的。4.3 第三層四點(diǎn)坐標(biāo)定位與膨脹掩碼分類完成后敏感字段進(jìn)入打碼模塊。打碼不是直接把四個(gè)點(diǎn)圍成的區(qū)域涂黑而是要把坐標(biāo)應(yīng)用到目標(biāo)圖像上并做膨脹和邊界羽化。膨脹的原因很簡單OCR檢測框通常緊貼文字主體但有些字符的筆畫會(huì)略微超出檢測框比如“張”的捺、“電”的豎彎鉤。如果不膨脹打碼后邊緣會(huì)留下明顯的筆畫殘影。我的默認(rèn)膨脹參數(shù)是文字高度的10%最小膨脹6像素最大膨脹20像素。生成掩碼的代碼片段import numpy as np import cv2 def mask_quad(image, quad_points, expand_ratio0.1): pts np.array(quad_points, dtypenp.float32) center pts.mean(axis0) expanded_pts center (pts - center) * (1 expand_ratio) mask np.zeros(image.shape[:2], dtypenp.uint8) cv2.fillPoly(mask, [expanded_pts.astype(np.int32)], 255) return mask注意這句center (pts - center) * (1 expand_ratio)它是以四邊形中心為基準(zhǔn)向外擴(kuò)展相比單純?cè)诿總€(gè)坐標(biāo)加減像素值能更好地保持四邊形的形狀。打碼最終采用“掩碼馬賽克平均”的組合。直接涂純色塊在票據(jù)上是明顯的干預(yù)痕跡歸檔時(shí)沒那么好看用馬賽克則保留了“這里被遮住了”的信息又不至于太突兀。實(shí)測下來馬賽克顆粒取8像素時(shí)既能掩蓋信息又不太影響版面閱讀。4.4 脫敏后圖像質(zhì)量控制Mask和模糊的選擇打碼后的圖像質(zhì)量直接影響項(xiàng)目能不能驗(yàn)收。我試過三種方式純色塊覆蓋像素替換為灰白色簡單粗暴但會(huì)在彩色印章旁邊顯得非常刺眼高斯模糊對(duì)四邊形區(qū)域做高斯模糊邊緣自然但模糊半徑不夠時(shí)文字的輪廓還會(huì)隱約看到馬賽克化將區(qū)域分割成小方塊每個(gè)方塊填平均色既不可讀又保留了一定的“不確定性”。最終我采用“高斯模糊馬賽克混合”先用較大半徑的高斯模糊把文字信息徹底抹掉再做一層輕量馬賽克。這樣既沒有明顯色塊邊界也不會(huì)被還原出文字輪廓。4.5 開源項(xiàng)目目錄與模塊劃分項(xiàng)目開源之后我把代碼劃分成五個(gè)模塊方便不同需求的用戶只取其中一部分preprocess/ # 圖像預(yù)處理矯正、去噪、透視變換 ocr_detector/ # 文本檢測與識(shí)別封裝 semantic_filter/ # 視覺大模型語義分類 masking/ # 四點(diǎn)定位打碼與圖像后處理 pipeline/ # 端到端串聯(lián)這樣劃分的目的是有的人只需要OCR模塊有的人只想用語義分類模塊有的人干脆自己寫OCR只借用四點(diǎn)打碼的代碼。模塊之間通過標(biāo)準(zhǔn)的JSON數(shù)據(jù)結(jié)構(gòu)傳遞不強(qiáng)依賴某一個(gè)具體OCR框架。5. 實(shí)測效果與被忽略的坑5.1 測試集構(gòu)建模擬彎折、旋轉(zhuǎn)、拍攝模糊場景為了驗(yàn)證方案我專門構(gòu)建了一個(gè)測試集把1000張醫(yī)療單據(jù)脫敏過的測試數(shù)據(jù)分別做旋轉(zhuǎn)、彎折、透視變換、加噪聲、模擬反光等處理生成2000張測試圖。測試集一定要覆蓋真實(shí)環(huán)境否則指標(biāo)再高也沒用。我的測試集分幾類正常平鋪拍攝基線15度到30度的透視傾斜模擬手持拍攝橫向?qū)φ酆笳归_模擬單據(jù)折痕局部反光與陰影模擬燈光不均勻低分辨率手機(jī)圖模擬老舊設(shè)備。5.2 指標(biāo)統(tǒng)計(jì)召回率、誤殺率、版面保持度評(píng)估指標(biāo)不能只看OCR準(zhǔn)確率。脫敏項(xiàng)目更關(guān)心的是敏感字段召回率所有敏感字段中有多少被成功打碼誤殺率非敏感字段中有多少被錯(cuò)誤打碼版面保持度打碼后非敏感區(qū)域的像素變化比例。我的實(shí)測結(jié)果大致是敏感字段召回率97.8%誤殺率1.9%版面保持度約96%。這個(gè)結(jié)果是在包含傾斜和彎折的測試集上跑出來的。召回率沒有到99%主要問題出在極端反光和手寫小字上后面會(huì)細(xì)說。5.3 幾個(gè)經(jīng)典翻車案例低對(duì)比度、反光、手寫體第一個(gè)翻車案例是低對(duì)比度單據(jù)。有些用熱敏紙打印的單據(jù)過一段時(shí)間字跡變淡OCR檢測置信度極低。視覺大模型雖然能看到模糊的痕跡但無法準(zhǔn)確判斷文字邊界導(dǎo)致打碼框偏大把旁邊表格里的“年月日”也包進(jìn)去。針對(duì)這類情況我的對(duì)策是增加一個(gè)圖像增強(qiáng)分支檢測到圖像整體對(duì)比度偏低時(shí)先做自適應(yīng)直方圖均衡化再送OCR。第二個(gè)是反光區(qū)域。反光處會(huì)出現(xiàn)高光斑塊文字被亮斑切斷OCR識(shí)別出來是“1 3 8 ****”中間全是星號(hào)。視覺大模型根據(jù)上下文推斷這是手機(jī)號(hào)但四個(gè)點(diǎn)坐標(biāo)已經(jīng)不準(zhǔn)確了。這種情況下我做了“區(qū)域重新檢測”以反光影響區(qū)域?yàn)橹行慕厝【植繄D用全局閾值分割找出文字筆畫再用筆畫的最小外接多邊形替換原來的四點(diǎn)框。第三個(gè)是手寫體。醫(yī)療單據(jù)上的醫(yī)生簽名和手寫備注很難識(shí)別。打碼這類區(qū)域時(shí)我不依賴OCR識(shí)別的文字內(nèi)容而是讓視覺模型判斷“這塊區(qū)域包含手寫且屬于敏感類別”只要判斷是敏感類別就用手寫區(qū)域的連通域掩碼來做打碼效果比OCR框好很多。5.4 視覺大模型誤判地址拆行識(shí)別為多個(gè)字段Vision LLM最大的問題不是不會(huì)分類而是容易把同一個(gè)字段拆成多個(gè)不完整的字段。比如“北京市朝陽區(qū)某某街道某小區(qū)3棟502室”在單據(jù)上可能分成三行顯示模型會(huì)把每行都識(shí)別成獨(dú)立地址打碼時(shí)三塊之間留下縫隙或者把相鄰的“聯(lián)系人”字段也劃進(jìn)去。我的解決辦法是把OCR的布局信息注入Prompt把屬于同一行、同一列的文本塊合并成“段”讓模型在段落級(jí)別上做分類而不是逐個(gè)文本塊分類。段落合并的依據(jù)是文本塊中心點(diǎn)垂直方向上的重疊程度。如果兩個(gè)文本塊在垂直方向有較多重疊且水平間距不大就合并成同一個(gè)邏輯段。合并之后地址、主訴這類跨行文本的分類準(zhǔn)確率明顯提升。6. 工程落地建議性能優(yōu)化與離線部署6.1 各環(huán)節(jié)的耗時(shí)占比與優(yōu)化空間我在CPU機(jī)器上先跑了一版單張單據(jù)的總耗時(shí)接近15秒其中OCR檢測占5秒識(shí)別占4秒視覺大模型分類占5秒圖像處理占1秒。這個(gè)速度完全沒法用于是開始逐環(huán)節(jié)優(yōu)化。優(yōu)化后單張單據(jù)在CPU上的耗時(shí)壓到4秒左右GPU上能到1.5秒。主要手段包括把OCR檢測和識(shí)別拆成并行、視覺大模型用量化后的輕量版本、圖像尺寸動(dòng)態(tài)縮放。各環(huán)節(jié)耗時(shí)參考環(huán)節(jié)優(yōu)化前耗時(shí)優(yōu)化后耗時(shí)主要優(yōu)化手段OCR檢測5.0s1.2s限制檢測圖像最長邊設(shè)置信度閾值OCR識(shí)別4.0s1.0s只識(shí)別檢測出的文本塊裁剪小圖視覺大模型分類5.0s1.5s量化減少輸入Token數(shù)圖像預(yù)處理與打碼1.0s0.3s使用輕量圖像處理庫避免重復(fù)編解碼6.2 OCR結(jié)果緩存與視覺模型裁剪醫(yī)療單據(jù)有一個(gè)特點(diǎn)同一家醫(yī)院、同一類單據(jù)的版式相對(duì)固定。所以我在工程上加了OCR結(jié)果緩存。緩存鍵是圖像的感知哈希和文件大小組合如果同一張圖片重復(fù)提交直接復(fù)用之前的檢測結(jié)果和脫敏掩碼省去全部模型計(jì)算。視覺模型部分我用的是開源視覺語言模型權(quán)重的蒸餾版本從原來的單精度浮點(diǎn)模型量化到8位整數(shù)顯存占用降低了一半多分類準(zhǔn)確率只下降不到1%。輸入圖片也裁剪成短邊512像素對(duì)于大多數(shù)單據(jù)文字識(shí)別來說這個(gè)分辨率足夠。6.3 批量任務(wù)、異步隊(duì)列、GPU/CPU混合調(diào)度歸檔場景是典型的海量離線任務(wù)必須做異步隊(duì)列。我用了一個(gè)簡單的生產(chǎn)者消費(fèi)者模型def process_queue(image_queue, result_queue, workers4): with ThreadPoolExecutor(max_workersworkers) as executor: futures [executor.submit(worker, image_queue, result_queue) for _ in range(workers)] for f in futures: f.result()這里要注意OCR和視覺模型最好分開跑。GPU資源緊張時(shí)OCR在GPU上視覺模型可以退到CPU如果兩個(gè)模型搶同一個(gè)GPU顯存容易出現(xiàn)內(nèi)存不足。我的調(diào)度策略是前處理和后處理用多線程并行模型推理部分如果機(jī)器有多個(gè)GPU按照顯存余量分配模型實(shí)例如果只有一個(gè)GPU把視覺模型放進(jìn)批處理模式每批4到8張圖吞吐量比單張推理提升2倍以上。6.4 私有化部署與隱私合規(guī)要點(diǎn)脫敏工具本身處理的就是敏感數(shù)據(jù)部署時(shí)必須考慮數(shù)據(jù)不能出內(nèi)網(wǎng)。我的開源版本只提供離線推理方案所有模型權(quán)重和推理腳本都放在本地運(yùn)行。服務(wù)端不保存原始圖像處理完成后只保留脫敏后的結(jié)果圖并且處理日志里不寫入姓名、證件號(hào)這類明文。另外單據(jù)測試數(shù)據(jù)在項(xiàng)目里全部是公開的模擬數(shù)據(jù)。真正涉及真實(shí)患者信息時(shí)必須先做好數(shù)據(jù)授權(quán)。這個(gè)原則是底線技術(shù)方案再完善也不能越過去。7. 開源項(xiàng)目可以怎么上手用7.1 最小運(yùn)行步驟拿到開源倉庫之后最快跑通的方式是安裝依賴庫下載OCR和視覺模型權(quán)重準(zhǔn)備一張測試單據(jù)圖片運(yùn)行命令行工具python run_pipeline.py --input test.jpg --output result.jpg默認(rèn)參數(shù)會(huì)輸出兩張圖一張帶檢測框的標(biāo)注圖一張打碼后的結(jié)果圖。如果只想用某個(gè)模塊也可以單獨(dú)調(diào)用。比如只做OCR檢測python run_pipeline.py --input test.jpg --output result.json --module ocr7.2 如何調(diào)整敏感字段范圍不同機(jī)構(gòu)的脫敏要求不一樣。有的只要遮姓名和身份證號(hào)有的連檢查項(xiàng)目里的特定詞都要處理。開源代碼里把所有分類規(guī)則統(tǒng)一放在一個(gè)配置文件里可以按需調(diào)整{ sensitive_categories: [姓名, 證件號(hào)碼, 聯(lián)系電話, 家庭住址, 主訴, 現(xiàn)病史], mask_method: mosaic, expand_ratio: 0.1 }調(diào)整后不需要重新訓(xùn)練模型視覺大模型會(huì)按照新的分類要求輸出標(biāo)簽打碼邏輯跟著標(biāo)簽走。要注意的是如果增加一個(gè)原本不在Prompt里的新類別需要同步修改Prompt模板否則模型不知道新類別是什么意思。7.3 數(shù)據(jù)回流與模型微調(diào)思路雖然默認(rèn)權(quán)重在大多數(shù)單據(jù)上表現(xiàn)不錯(cuò)但每個(gè)機(jī)構(gòu)的單據(jù)版式都有自己的“土特產(chǎn)”某些醫(yī)院喜歡在單據(jù)角落蓋一個(gè)大紅章有些社區(qū)中心的表格線又細(xì)又密。如果希望準(zhǔn)確率更高需要做模型微調(diào)。我的建議是優(yōu)先微調(diào)OCR檢測模型采集50到100張?jiān)摍C(jī)構(gòu)的脫敏后單據(jù)標(biāo)注出文字框然后用檢測模型繼續(xù)訓(xùn)練少量輪次。視覺大模型除非分類錯(cuò)誤特別集中否則不建議直接微調(diào)成本高且容易過擬合。先用規(guī)則修正和Prompt調(diào)整來兜底大部分問題都能解決。一點(diǎn)個(gè)人實(shí)測體會(huì)項(xiàng)目做完之后我最深的感受是醫(yī)療單據(jù)脫敏這種需求表面看是OCR加圖像處理實(shí)際做下來會(huì)倒逼你把“識(shí)別、理解、圖像變換”三塊能力全部打通。尤其四點(diǎn)定位打碼看起來只是坐標(biāo)映射但一旦和彎曲矯正、透視變換、OCR框不確定性疊加在一起細(xì)節(jié)就會(huì)成倍增加。建議后來者先不要追求完美把流水線跑通再用真實(shí)數(shù)據(jù)慢慢磨邊界情況。這套思路不只是醫(yī)療單據(jù)能用合同、發(fā)票、檔案、表單脫敏都可以參照只是字段分類需要重新調(diào)整。我把這個(gè)項(xiàng)目整理成開源倉庫之后也一直在持續(xù)補(bǔ)充新的測試場景希望能讓更多人在處理這類問題時(shí)少走點(diǎn)彎路。