
OpenMed 多模态临床文档摄入实战OCR、CSV/TSV 表格脱敏与 ExtractedDocument 数据契约【免费下载链接】openmedLocal-first healthcare AI: clinical NER HIPAA PII de-identification that runs 100% on-device. 2,200 medical models, 21 languages, Apple MLX Python, no cloud, no patient data leaving your network. Apache-2.0项目地址: https://gitcode.com/GitHub_Trending/ope/openmed临床文本常常以扫描传真、手机拍照的病历、CSV/TSV 导出表或 C-CDA XML 的形态到达而不是现成的纯文本。OpenMed 的openmed.multimodal子包负责把这些异构输入统一转换为一个标准化的ExtractedDocument干净文本 字符偏移→源位置映射从而让后续的 HIPAA PII 脱敏openmed.deidentify与临床 NERopenmed.analyze_text可以在统一的文本契约上工作。整条链路 100% 本地运行OCR 后端是本地进程文档不会离开设备。读完本文你将掌握ocr()、redact_document()、read_table()/classify_columns()/redact_table()这些已验证入口的完整用法、底层实现机制与隐私边界并能把识别出的 PHI 偏移回射到原始扫描件的页面与像素框上。本文是技能文档 SKILL.md 的配套参考对应 multimodal-ingest.md重点补齐数据契约、OCR 引擎与表格流水线的细节凡本文出现的名称均与包内公共导出逐一核对过可直接用于 import。一、openmed.multimodal在 OpenMed 流水线中的位置按 SKILL.md 的描述文档摄入是整个处理链的第一站先通过openmed.multimodal把扫描件/表格/CDA 变成ExtractedDocument再交给deidentifying-clinical-text脱敏和extracting-clinical-entitiesNER。当前支持三类输入输入扩展名处理路径图片 / 扫描件.png .jpg .jpeg .tif .tiff .bmp .gif .webpOCRocr()/ 图片处理器表格.csv .tsv列感知的表格脱敏C-CDA.xml内容检测为 CDA纯标准库 CDA 适配器需要特别留意PDF 与 DOCX 目前没有活动处理器redact_document(x.pdf)会抛出UnsupportedDocumentError。正确做法是先用自有工具把 PDF 栅格化为页面图片或抽取为文本再走 OCR 通道。二、安装与依赖边界pip install openmed[multimodal] # 文档摄入契约 图像相关依赖 pip install openmed[ocr-paddle] # 追加 PaddleOCR 引擎 # Tesseract 引擎还需要系统二进制例如brew install tesseract从 base.py 的源码可以看到[multimodal]extra 对应的完整依赖清单pdfplumber、python-docx、python-pptx、Pillow、markdown-it-py、piexif、pikepdf、pydicom、openpyxl。这些重型依赖全部采用延迟导入——import openmed与import openmed.multimodal本身保持轻量只有当某个处理器真正运行时才加载对应后端见 base.py 的模块文档注释。缺失依赖时的报错路径也是设计好的ensure_multimodal_available()会通过importlib.util.find_spec探测依赖is_multimodal_available()底层复用openmed.core.capabilities.is_backend_available一旦发现缺失即抛出带可执行安装提示的MissingDependencyError提示语统一为Install with: pip install openmed[multimodal].。这意味着在一个没装 extra 的环境中调用方会先看到如何补装的明确指引而不是一串晦涩的ImportError。三、公共 API 面openmed.multimodal.__all__以下是经过包内公共导出逐一核对过的名称清单ExtractedDocument, SourceSpan # 通用摄入契约 redact_document, register_handler # 分发器 扩展注册 ensure_multimodal_available # 缺少 [multimodal] extra 时抛错 MissingDependencyError, UnsupportedDocumentError OcrResult, OcrWord, OcrEngine, FakeOcrEngine, register_ocr_engine ColumnDecision, TableView, RedactedTable # 表格类型 read_table, classify_columns, redact_table注意ocr()不在__all__中——必须从子模块导入from openmed.multimodal.ocr import ocr。这样设计是为了避免函数名ocr遮蔽包级重导出的数据类型__init__.py的注释明确说明了这一点。实际的__all__见init.py远比上面这份参考面丰富——它还包括 PDF/DOCX/PPTX/EPUB/RTF/Email 抽取、DICOM 头与像素脱敏、元数据擦除、资产预检与批次清单等能力但本文只聚焦于文档中验证过的摄入契约部分。四、ExtractedDocument统一契约所有摄入路径OCR、CSV、CDA最终都收敛到这个 frozen dataclass 上。它同时承载规范化文本与字符偏移→源位置的映射是后续像素级脱敏的基础。ExtractedDocument .text: str # 规范化文本 .spans: tuple[SourceSpan, ...] # 字符偏移 - 源位置 .metadata: Mapping[str, Any] # 与格式相关的元数据 .location_at(offset) - SourceSpan | None # 把字符偏移映射回源位置 .text_for(span) - str # 取出某个 span 覆盖的文本 .from_blocks(...) # 类方法从有序块组装文档 SourceSpan .start: int # 含端字符偏移 .end: int # 不含端字符偏移 .page: int 0 .bbox: tuple[float, float, float, float] | None # (x0, y0, x1, y1) .metadata: Mapping[str, Any]源码实现位于 base.pySourceSpan的start/end是ExtractedDocument.text内的字符偏移end不含page是 0 起始的源页号bbox是可选的轴对齐像素框metadata携带块级细节块类型、字体、置信度等。from_blocks的组装逻辑值得细看每个块是一个至少带text键的 mapping可选page、bbox、metadata块之间用separator默认\n连接同时为每个块记录一个SourceSpan。这正是OcrResult.to_document()的底层实现——每个 OCR 词成为一个块于是每个词的像素框都得以保留。实战用法用location_at/spans把检测到的 PHI 偏移投影回原始扫描件的页号和边界框从而实现像素级脱敏而不只是文本级脱敏from openmed.multimodal.ocr import ocr import openmed doc ocr(fax_page.png).to_document() deid openmed.deidentify(doc.text, methodmask) for ent in deid.pii_entities: loc doc.location_at(ent.start) # SourceSpan 或 None if loc is not None: print(ent.label, page, loc.page, bbox, loc.bbox)五、redact_document分发器按扩展名路由redact_document(path, *, policyNone, modelsNone, langNone) - ExtractedDocument分发器按文件扩展名路由到已注册的处理器并直接返回一个已完成脱敏的ExtractedDocument。当前活动处理器源码中由 base.py 的分发逻辑 各模块的注册声明共同构成扩展名处理器.png .jpg .jpeg .tif .tiff .bmp .gif .webpOCR 图片处理器.csv .tsv表格 CSV 脱敏器.xml内容检测为 C-CDA标准库 CDA 适配器一个值得注意的实现细节图片处理器是分两批注册的。PNG/JPEG/TIFF 由image模块注册支持像素脱敏与元数据剥离BMP/GIF/WebP 由 OCR 模块在 ocr.py 注册——_IMAGE_EXTENSIONS (.bmp, .gif, .webp)通过register_handler(_IMAGE_EXTENSIONS, _ocr_image_handler)挂到分发器上。两类处理器在redact_document下对外行为一致OCR → 桥接为文档。未知扩展名包括还没有活动处理器的.pdf与.docx会抛UnsupportedDocumentError。抛出前分发器会先调用ensure_multimodal_available()做一次依赖检查保证没装 extra与格式不支持两种失败被清晰区分见 base.py。扩展机制register_handler(extensions, handler, *, detectorNone, requires_multimodalTrue)。注册表_HANDLERS是扩展名 → 处理器规格列表的字典同一种扩展名可以挂多个规格有detector的规格排在前面分发时按序调用detector(path)做内容探测base.py。C-CDA 适配器正是这么做的——它在 cda.py 中以detectoris_cda_document、requires_multimodalFalse注册.xml意味着它只处理内容确实是 CDA的 XML且因为只用标准库即使没装[multimodal]extra 也能运行。redact_document还有一个lang参数可选 OpenMed 语言码如fr它会透传给图片处理器让 OCR 以正确的语言读取扫描件不消费该参数的处理器如表格会接受并忽略它。六、OCR 子系统统一结果契约 可插拔引擎from openmed.multimodal.ocr import ocr ocr(image, *, engineNone, languagesNone) - OcrResultimage可以是路径或已加载的图像对象engine可以是None自动选择、引擎名字符串或一个OcrEngine实例。OcrResult .words: tuple[OcrWord, ...] .metadata: Mapping[str, Any] .text - str # 词以空格连接 .to_document(*, separator , preserve_linesFalse) - ExtractedDocument OcrWord .text: str .bbox: tuple[float, float, float, float] .confidence: float .page: int 0源码中 ocr.py 的OcrResult.to_document()把每个词变成一个带page/bbox/confidence元数据的块经ExtractedDocument.from_blocks组装这保证了下游脱敏可以把任意 PHI 偏移投影回图像中的像素框。OcrResult还提供两个进阶能力源码注释明确说明to_document(preserve_linesTrue)按 OCR 原生行/页边界插入换行保留引擎的原始词序要求结果的line_ids元数据提供正整数(block, paragraph, line)三元组缺失、畸形或非连续的行标识会显式失败该选项不做几何推断to_layout(**kwargs)调用时才惰性加载布局模块从带位置的词重建布局感知的阅读顺序。引擎与 extras引擎engine安装方式docTRdoctrpip install openmed[multimodal]Tesseracttesseractpytesseract 系统 Tesseract 二进制brew install tesseract/apt-get install tesseract-ocrEasyOCReasyocrpip install openmed[multimodal]PaddleOCRpaddleocrpip install openmed[ocr-paddle]Fake测试用FakeOcrEngine内置确定性输出供单元测试engineNone时按固定优先级自动选择第一个已安装的后端源码中的顺序是(doctr, tesseract, easyocr, paddleocr)见 ocr.py 的_AUTO_ORDER与resolve_engine()。全部后端缺失时抛出MissingDependencyError错误消息会给出完整的安装指引。available_ocr_engines()返回按自动选择顺序排列的已安装引擎列表自定义引擎通过register_ocr_engine(name, factory)注册。FakeOcrEngine是确定性引擎构造时传入固定词列表recognize()返回固定的OcrResult并记录最近一次调用的languageslast_languages方便测试断言语言选择是否到达适配器层。语言支持ocr()的languages参数接受 OpenMed PII 语言码默认en。适配器层会把 OpenMed 码映射到各后端自己的标识符见 ocr.pyTesseractISO 639-2/Ten→eng、fr→fra、de→deu、it→ita、es→spa、nl→nld、hi→hin、te→tel、pt→por、ar→ara、ja→jpn、tr→tur多语言用连接tesseract_language([fr,en]) → fraengPaddleOCRde→german、ja→japan等PaddleOCR 单实例只加载一种识别语言取第一个请求的语言EasyOCR基本同名映射。语言数据本身不随包分发需单独安装Tesseract 需要对应traineddata如apt-get install tesseract-ocr-fraEasyOCR 首次使用下载检测/识别模型PaddleOCR 首次使用下载对应语言的识别模型。这也解释了MissingDependencyError安装提示之外的第二类缺数据问题引擎装好了语言包还要按后端各自安装。七、表格 CSV/TSV 流水线先分类、再脱敏表格路径的关键原则写在模块文档里tabular_csv.py任何单元格被处理之前先完成列分类——结构化数据绝不能被当作自由文本整体丢进 NER。read_table(source, *, delimiterNone, has_headerNone, ...) - TableView classify_columns(headers, rows, ...) - tuple[ColumnDecision, ...] redact_table(source, *, policyNone, keep_yearTrue, date_shift_daysNone, langen, ...) - RedactedTableread_table的参数细节源码 tabular_csv.pydelimiter默认通过csv.Sniffer嗅探仅接受,或\thas_header默认结合 Sniffer 与 OpenMed 表头启发式判断header_heuristics允许追加表头 → 规范标签的自定义映射表头键会先剥离非字母数字并小写化action_overrides允许按表头/规范标签/类别 → 动作覆盖决策sample_size默认每列采样 50 个非空单元格参与判定。列类别ColumnDecision.assigned_class值含义DIRECT_ID直接标识符姓名、MRN、SSN、邮箱QUASI_ID准标识符DOB、ZIP、日期——组合后可再识别SAFE未检测到识别信号动作ColumnDecision.action值效果mask用占位符替换单元格值hash单向一致令牌可跨行关联不泄露原值drop整列删除date_shift按每行一致的偏移平移日期shift_datesfree_text_redact对 note 类列跑自由文本 PHI 脱敏keep列保持不变ColumnDecision还暴露index、name、canonical_label、policy_label、detection_source、confidence、sampled_values源码 tabular_csv.py其中detection_source区分判定依据来自表头header_name置信度 1.0还是采样值value_sample。分类与默认动作的底层逻辑从源码可以梳理出完整的判定链tabular_csv.py表头优先内置_HEADER_LABELS把常见表头归一化后映射到规范标签openmed.core.labels的标签体系如mrn/patientid/recordid→ID_NUM、ssn→SSN、dob/dateofbirth→DATE_OF_BIRTH、email→EMAIL、phone/telephone/mobile→PHONE、address→STREET_ADDRESS、username/accountnumber/password/pin/apikey/ipaddress/macaddress等表头未命中则采样值用正则检查采样单元格——SSN\d{3}-\d{2}-\d{4}、EMAIL、PHONE支持北美格式、MRNMRN/MEDREC/MR前缀、日期YYYY-MM-DD或M/D/YYYY变体、人名名 姓两词结构匹配比例需达阈值PERSON 为 0.8其余 0.6note 类表头note/notes/clinicalnote/comment/description/narrative/freetext/text即使没有识别信号也走free_text_redact默认动作DATE标签 →date_shiftID_NUM/ACCOUNT_NUMBER/USERNAME→hashDIRECT_ID/QUASI_ID→mask其余 →keepaction_overrides可覆盖任一环节。redact_table的执行细节tabular_csv.py日期列按行推导确定性偏移derive_date_shift_days基于date_shift_seed默认种子openmed-tabular-csv-v1同一行的多个日期列复用同一偏移以保持相对时间关系date_shift_days传固定值时则用固定值keep_yearTrue保留源年份lang透传给 OpenMed 日期解析text_redactor可注入自定义的 note 列脱敏回调models参数也可携带text_redactor。TableView .headers .rows .delimiter .has_header .columns RedactedTable .text .headers .rows .delimiter .has_header .columns .manifest - tuple[dict, ...] PHI-SAFE计数/动作无原始值 .to_document() - ExtractedDocument.manifest是审计轨迹且刻意保持 PHI 安全它记录每列的类别、动作与计数column_index、column_name、assigned_class、canonical_label、policy_label、action、detection_source、confidence、sampled_values、row_count、row_count_affected绝不包含原始单元格内容。这一点在单元测试中有直接印证测试断言patient_name被掩码为[PERSON]、mrn以ID_NUM_前缀哈希、ssn变[SSN]、诊断等安全列原样保留见 test_tabular_csv.py。八、两条实战摄入路径路径 A两步式可控脱敏参数ocr()住在子模块刻意不从openmed.multimodal重导出适合想自己掌控脱敏方法、策略与映射的场景from openmed.multimodal.ocr import ocr import openmed # 1) 扫描件/传真页 - OcrResult - ExtractedDocument - 纯文本 result ocr(fax_page.png, engineNone) # None 自动选择已安装引擎 doc result.to_document() # ExtractedDocument text doc.text # 供下游 OpenMed 使用的干净文本 # 2) 先脱敏再跑 NER隐私优先的顺序 deid openmed.deidentify(text, methodmask, policyhipaa_safe_harbor) ner openmed.analyze_text(deid.deidentified_text, output_formatdict) for ent in ner.entities: print(ent.label, ent.text, ent.confidence)路径 B一步式redact_document全权接管对图片、CSV/TSV 与 CDAredact_document在一次格式感知的调用里同时完成摄入与脱敏返回已经脱敏的ExtractedDocumentfrom openmed.multimodal import redact_document # 图片扫描件OCR 脱敏一步完成 doc redact_document(fax_page.png) print(doc.text) # 脱敏后的文本 print(doc.spans[:3]) # SourceSpan 偏移 - 原始扫描件中的页 / bbox # CSV 导出按列分类直接标识符 / 准标识符 / 安全 脱敏 table_doc redact_document(patients.csv) print(table_doc.text)选择依据表格等结构化输入建议走redact_document脱敏是列级的不是自由文本 NER需要自定义脱敏方法、策略或映射时走ocr() → to_document() → deidentify路径。表格专项示例from openmed.multimodal import read_table, redact_table view read_table(patients.csv) # TableView附带列决策 for col in view.columns: print(col.name, -, col.assigned_class, col.action, col.canonical_label) redacted redact_table(patients.csv, keep_yearTrue) print(redacted.text) # 脱敏后的 CSV for entry in redacted.manifest: # PHI 安全审计每列的计数/动作无原始值 print(entry)九、隐私注意事项全部本地运行OCR 后端都是本地进程。在 PHI 工作流中不要把扫描件路由到云 OCR API。OCR 中间产物就地保管抽取出的文本、页面图像等中间产物应留在设备上、不进日志。审计产物必须无 PHI表格manifest与任何审计输出只记录标签、偏移、哈希与计数绝不记录原始标识符。后端许可合规只使用宽松许可的 OCR 后端受限的术语表应在用户自有许可下保持进程外运行。OCR 有噪声识别错误会拉低下游召回率。优先使用高 DPI 扫描件并用OcrWord.confidence标记低质量页面以便人工复核。表格不是自由文本redact_table按列分类脱敏——不要对整表跑 NER 然后期望结构化列被正确处理。十、已验证导入映射以下导入路径均已对照包内公共导出验证ocr除外它刻意留在子模块from openmed.multimodal import ( ExtractedDocument, SourceSpan, redact_document, register_handler, OcrResult, OcrWord, OcrEngine, FakeOcrEngine, register_ocr_engine, ColumnDecision, TableView, RedactedTable, read_table, classify_columns, redact_table, MissingDependencyError, UnsupportedDocumentError, ) from openmed.multimodal.ocr import ocr # 不从包根重导出想继续深挖的读者可以对照源码阅读通用契约与分发器在 openmed/multimodal/base.pyOCR 引擎与语言映射在 openmed/multimodal/ocr.py表格分类与脱敏在 openmed/multimodal/tabular_csv.pyC-CDA 适配器注册在 openmed/interop/cda.py配套单元测试位于 tests/unit/multimodal/覆盖 OCR 契约、引擎自动选择、语言映射、表格列分类与日期平移等。完整技能上下文见 SKILL.md。【免费下载链接】openmedLocal-first healthcare AI: clinical NER HIPAA PII de-identification that runs 100% on-device. 2,200 medical models, 21 languages, Apple MLX Python, no cloud, no patient data leaving your network. Apache-2.0项目地址: https://gitcode.com/GitHub_Trending/ope/openmed创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考