医保人工报销OCR识别方案
医保人工报销 OCR 识别方案
文章目录
- 医保人工报销 OCR 识别方案
- 一、方案架构
- 二、环境准备
- 2.1 环境信息
- 2.2 新建 Conda 环境
- 2.3 安装依赖(已验证完整命令)
- 2.4 验证安装
- 三、图像预处理
- 3.1 公章去除
- 3.2 图像增强(针式打印/褪色清单)
- 3.3 ROI 裁剪 — 只取通用名列
- 3.4 完整预处理流水线
- 四、OCR 识别引擎
- 4.1 单实例封装
- 4.2 并发方案(生产环境)
- 五、医保字典匹配
- 5.1 字典结构
- 5.2 模糊匹配引擎
- 5.3 策略优先级
- 六、业务处理流程
- 人工审核界面设计要点
- 七、准确率预估
- 不宜全自动的场景
- 八、部署建议
- 注意事项
场景: 医院费用清单(A4纸打印,含公章覆盖)→ OCR识别通用名 → 模糊匹配医保字典 → 确定收费等级
原则: 自动匹配 + 例外转人工,不做全自动
一、方案架构
费用清单扫描件 → 预处理(去公章+增强) → ROI裁剪(通用名列) → PaddleOCR识别 → 编辑距离模糊匹配字典 → 命中介入自通过 → 未命中/低置信 → 人工审核界面 → 确认入库二、环境准备
2.1 环境信息
基于 Python 3.9 + PaddleOCR 2.9.1 验证通过,实测环境:
| 组件 | 版本 | 说明 |
|---|---|---|
| Python | 3.9.25 | conda 独立环境 |
| paddlepaddle | 3.1.0 | CPU 版,GPU 版替换为 paddlepaddle-gpu |
| paddleocr | 2.9.1 | PP-OCRv4 模型,含 PP-Structure 表格识别 |
| paddlex | 3.5.2 | 模型管理依赖,自动安装 |
| opencv-python | 4.11.0.86 | 图像预处理(公章去除、增强) |
| Pillow | 11.3.0 | 图像加载 |
| numpy | 1.26.4 | 数组运算 |
| python-Levenshtein | 0.27.1 | 编辑距离模糊匹配(需单独安装) |
重要: CPU 模式下必须设置环境变量
FLAGS_use_mkldnn=0,否则 OneDNN 与 Windows 上的 PIR 属性转换存在兼容问题,会导致NotImplementedError。
2.2 新建 Conda 环境
conda create-nmedical_ocrpython=3.9-yconda activate medical_ocr2.3 安装依赖(已验证完整命令)
# PaddlePaddle(CPU版)pipinstallpaddlepaddle==3.1.0-ihttps://pypi.tuna.tsinghua.edu.cn/simple# GPU版(如有NVIDIA卡)# pip install paddlepaddle-gpu==3.1.0 -i https://pypi.tuna.tsinghua.edu.cn/simple# PaddleOCRpipinstallpaddleocr-ihttps://pypi.tuna.tsinghua.edu.cn/simple# 图像处理pipinstallopencv-python pillow numpy-ihttps://pypi.tuna.tsinghua.edu.cn/simple# Levenshtein距离(模糊匹配)pipinstallpython-Levenshtein-ihttps://pypi.tuna.tsinghua.edu.cn/simple2.4 验证安装
importos os.environ['FLAGS_use_mkldnn']='0'# CPU模式必须frompaddleocrimportPaddleOCR ocr=PaddleOCR(lang='ch')print('PaddleOCR ready')三、图像预处理
3.1 公章去除
importcv2importnumpyasnpdefremove_red_stamp(img_path):img=cv2.imread(img_path)hsv=cv2.cvtColor(img,cv2.COLOR_BGR2HSV)# 红色范围(公章)lower_red1=np.array([0,50,50])upper_red1=np.array([10,255,255])lower_red2=np.array([156,50,50])upper_red2=np.array([180,255,255])mask1=cv2.inRange(hsv,lower_red1,upper_red1)mask2=cv2.inRange(hsv,lower_red2,upper_red2)mask=mask1|mask2# 膨胀后填充白色kernel=np.ones((3,3),np.uint8)mask=cv2.dilate(mask,kernel,iterations=1)img[mask>0]=[255,255,255]returnimg3.2 图像增强(针式打印/褪色清单)
defenhance_image(img):# 灰度化 + CLAHE对比度增强gray=cv2.cvtColor(img,cv2.COLOR_BGR2GRAY)clahe=cv2.createCLAHE(clipLimit=2.0,tileGridSize=(8,8))enhanced=clahe.apply(gray)# 自适应二值化binary=cv2.adaptiveThreshold(enhanced,255,cv2.ADAPTIVE_THRESH_GAUSSIAN_C,cv2.THRESH_BINARY,11,2)# 转回三通道给PaddleOCRreturncv2.cvtColor(binary,cv2.COLOR_GRAY2BGR)3.3 ROI 裁剪 — 只取通用名列
defcrop_drug_column(img):""" 根据清单模板裁剪药品通用名列区域。 建议做成配置化:不同医院模板定义不同的列坐标。 """h,w=img.shape[:2]# 示例:通用名列在 x=50~300, y=表头下方到页脚上方roi=img[200:h-80,50:300]returnroi3.4 完整预处理流水线
defpreprocess(image_path):img=remove_red_stamp(image_path)img=enhance_image(img)img=crop_drug_column(img)returnimg四、OCR 识别引擎
4.1 单实例封装
importos os.environ['FLAGS_use_mkldnn']='0'frompaddleocrimportPaddleOCRclassMedicalOCR:def__init__(self,use_gpu=False):self.ocr=PaddleOCR(lang='ch',use_angle_cls=True)self.use_gpu=use_gpudefrecognize(self,img):"""返回文本行列表"""result=self.ocr.ocr(img)ifnotresultornotresult[0]:return[]# 按y坐标排序(表格从上到下)lines=sorted(result[0],key=lambdax:x[0][0][1])return[{'text':line[1][0],'confidence':line[1][1],'bbox':line[0]}forlineinlines]ocr_engine=MedicalOCR()4.2 并发方案(生产环境)
frommultiprocessingimportPooldefinit_worker():global_ocrimportos os.environ['FLAGS_use_mkldnn']='0'frompaddleocrimportPaddleOCR _ocr=PaddleOCR(lang='ch',use_angle_cls=True)defprocess_image(img_path):img=preprocess(img_path)result=_ocr.ocr(img)returnextract_lines(result)# 4进程池withPool(4,initializer=init_worker)aspool:results=pool.map(process_image,image_files)五、医保字典匹配
5.1 字典结构
# 示例:药品通用名 → 医保编码 → 收费等级MEDICAL_DICT={'阿莫西林胶囊':{'code':'XA01-001','level':'甲类','dosage':'胶囊'},'氯化钠注射液':{'code':'XB05-023','level':'甲类','dosage':'注射液'},'阿托伐他汀钙片':{'code':'XC10-056','level':'乙类','dosage':'片剂'},# ... 数万条}5.2 模糊匹配引擎
fromLevenshteinimportdistanceaslevenshteindeffuzzy_match(ocr_text,dict_keys,threshold=0.85):""" 编辑距离 ≤1 或 相似度 ≥threshold 返回最佳匹配 返回: (matched_key, score, level) 或 None """best_key,best_score=None,0ocr_clean=ocr_text.strip().replace(' ','')forkeyindict_keys:key_clean=key.strip().replace(' ','')# 完全匹配:直接返回ifocr_clean==key_clean:return(key,1.0,MEDICAL_DICT[key]['level'])# 编辑距离max_len=max(len(ocr_clean),len(key_clean))ifmax_len==0:continuedist=levenshtein(ocr_clean,key_clean)similarity=1-dist/max_lenifsimilarity>best_score:best_score=similarity best_key=keyifbest_score>=threshold:return(best_key,best_score,MEDICAL_DICT[best_key]['level'])returnNone5.3 策略优先级
defmatch_pipeline(ocr_lines):results=[]foriteminocr_lines:text=item['text']conf=item['confidence']# 策略1: OCR置信度 < 0.7 → 直接转人工ifconf<0.7:results.append({'status':'REVIEW','reason':'低置信度','ocr_text':text,'ocr_conf':conf})continue# 策略2: 完全匹配iftextinMEDICAL_DICT:entry=MEDICAL_DICT[text]results.append({'status':'MATCHED','code':entry['code'],'level':entry['level'],'ocr_text':text})continue# 策略3: 编辑距离 ≤1 模糊匹配matched=fuzzy_match(text,MEDICAL_DICT.keys(),threshold=0.85)ifmatched:results.append({'status':'FUZZY','code':MEDICAL_DICT[matched[0]]['code'],'level':matched[2],'ocr_text':text,'matched_name':matched[0],'score':matched[1]})continue# 策略4: 未命中 → 转人工results.append({'status':'REVIEW','reason':'未匹配字典','ocr_text':text,'ocr_conf':conf})returnresults六、业务处理流程
扫描件 → 预处理 → ROI裁剪 → OCR识别 → 字典匹配 ↓ ↓ 结构化字段 ├─ 完全匹配 → 自动通过 (医院名、日期、 ├─ 模糊匹配(标注) → 自动通过 就诊号) └─ 低置信/未匹配 → 人工审核界面 ↓ 人工选择/修正 → 确认入库人工审核界面设计要点
- 左侧显示原始清单截图,高亮待确认行
- 右侧显示 OCR 识别文本 + 字典候选列表(编辑距离排序 Top 5)
- 单键操作:选中确认 / 手动输入 / 跳过
- 批量审核:全部模糊匹配的行集中展示,一键批量确认
七、准确率预估
| 环节 | 预估准确率 | 备注 |
|---|---|---|
| 图像预处理 | 95% | 公章去除+增强,少数极端案例无效 |
| OCR 单字识别 | 90% | A4印刷清单,非针式打印 |
| OCR 整行识别 | 85% | 窄列(≤3字宽)易串行 |
| 字典完全匹配 | 70% | OCR 未出错且字典有该条目 |
| 模糊匹配(编辑距离≤1) | 22% | 补上 OCR 小误差 |
| 自动通过合计 | 92% | 70% + 22% |
| 转人工审核 | 8% | 约每 100 条 8 条需人工确认 |
不宜全自动的场景
- 针式打印(点阵断续)清单 — 准确率骤降至 60~70%
- 公章覆盖超过 3 行文字 — 预处理无效
- 手写补充的药品名 — 需单独训练手写模型
八、部署建议
| 环境 | 配置 | 吞吐 |
|---|---|---|
| CPU(开发/小规模) | 8核 + 4进程 | 6080 张/小时 |
| GPU(生产推荐) | T4/V100 + 单卡 | 6001000 张/小时 |
| CPU(高并发) | 16核 + 8进程 + 队列 | 150200 张/小时 |
注意事项
- 不同医院清单格式不同,ROI 裁剪坐标需做成模板配置,一个医院一个配置文件
- 医保字典定期同步更新,模糊匹配结果建议记录日志便于追溯审计
- OCR 所有低置信度/未匹配结果必须留痕,医保审计需要
- 生产环境务必使用多进程池方案,单进程 PaddleOCR 非线程安全
