使用 OpenMed 将自由文本放射学报告结构化为可追踪的 Findings 与 Impression

发布时间:2026/9/19 14:55:02
使用 OpenMed 将自由文本放射学报告结构化为可追踪的 Findings 与 Impression 使用 OpenMed 将自由文本放射学报告结构化为可追踪的 Findings 与 Impression【免费下载链接】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导读放射学报告是散文但其含义是结构化的一份报告包含technique检查技术、comparison对照、一组findings所见每项含解剖部位、侧别与测量值以及可能承载assessment category评估分类如 BI-RADS、Lung-RADS与follow-up随访建议的impression印象。本文基于 OpenMed 的structuring-radiology-reports技能skills/structuring-radiology-reports/SKILL.md讲解如何在 OpenMed 完成设备端 NER 之后将放射学叙述文本组装成按章节组织的、可编码的发现结构让偶然发现incidental findings可追踪、可交接。读完本文你将掌握章节切分、测量与侧别绑定、RADS 分类捕获、随访提取的完整实操方案以及仓库中确定性解析器的底层实现原理。定位声明本流程是决策支持工具不是诊断医疗设备。每条结构化发现都必须能溯源回原始句子供放射科医生复核。何时使用本技能你拿到一份 CT / MRI / X-ray / 超声 / 钼靶报告需要产出{technique, comparison, findings[], impression}且带测量值与侧别。你必须捕获BI-RADS乳腺或Lung-RADS肺筛查评估分类及对应建议动作。你需要追踪偶然发现及其推荐的随访间隔 / 检查方式。你正在把发现向RadLex术语或DICOM-SR结构化报告映射。技能元数据中的触发关键词radiology report、findings、impression、RadLex、DICOM-SR、BI-RADS、Lung-RADS、ACR、laterality、measurement、nodule、incidental finding、follow-up、structured reporting也说明该技能定位为 OpenMed NER 之后的 pairs: after 环节。快速开始从叙述到结构化以下是技能文档中的核心示例完整保留它演示了先去标识化 → 再跑 NER → 最后组装结构化结果的完整链路import openmed report ( TECHNIQUE: CT chest without contrast.\n COMPARISON: CT 2023-11-02.\n FINDINGS: A 8 mm solid nodule is noted in the right upper lobe, unchanged. No pleural effusion.\n IMPRESSION: 8 mm right upper lobe nodule, stable. Lung-RADS 2. Recommend annual low-dose CT screening. ) # 1) 先在设备端对报告去标识化此处为合成示例。 deid openmed.deidentify(report, policyhipaa_safe_harbor) text deid.deidentified_text # 2) 运行 NER获取解剖部位 / 发现 / 测量值 span。 ents openmed.analyze_text( text, model_nameanatomy_detection_superclinical, # Anatomy 类别 output_formatdict, )[entities] # 3) 按标题切分章节然后把实体 测量值挂到每条 finding 上。 import re SECTION re.compile(r(?im)^(TECHNIQUE|COMPARISON|FINDINGS|IMPRESSION)\s*:) sections, last, name {}, 0, None for m in SECTION.finditer(text): if name: sections[name] text[last:m.start()].strip() name, last m.group(1).upper(), m.end() if name: sections[name] text[last:].strip() structured { technique: sections.get(TECHNIQUE), comparison: sections.get(COMPARISON), findings: _split_findings(sections.get(FINDINGS, )), # 每个句子一条 impression: sections.get(IMPRESSION), measurements: re.findall(r\b\d(?:\.\d)?\s?(?:mm|cm)\b, text), laterality: sorted({w for w in (right, left, bilateral) if re.search(rf\b{w}\b, text, re.I)}), assessment: (re.search(r\b(?:BI-RADS|Lung-RADS)\s*\d[A-C]?\b, text, re.I) or [None])[0] if re.search(rRADS, text, re.I) else None, follow_up: _extract_followup(sections.get(IMPRESSION, )), }_split_findings/_extract_followup分别是句子切分器和建议匹配器匹配 recommend …、follow-up in N months 等模式关键约束是每条 finding 都要与其源句子偏移source offsets绑定保证可溯源。前置 API 说明analyze_text技能中使用的openmed.analyze_text是 OpenMed 的公开临床文本分析入口openmed/init.py。其关键参数包括model_name注册表键、Hugging Face 模型 id 或本地模型路径默认disease_detection_superclinical技能中切换为anatomy_detection_superclinical以获取 Anatomy 类别实体aggregation_strategy默认simple可设为first/average/max或None返回原始 token 输出output_formatdict默认、json、html或csvconfidence_threshold实体最小置信度默认0.0include_confidence输出中是否携带置信度默认Truegroup_entities是否合并相邻同标签实体默认Falsesentence_detection默认True先做句子检测再逐句推理可避免跨句误绑定。result[entities]中每个实体项携带text、label、confidence、start、end字段这正是下文按偏移绑定侧别/测量值的基础。interop 层的 JSON-ready 版本位于 openmed/interop/tools.py。七步工作流技能文档给出了完整的处理工作流逐条展开如下先去标识化。在存储或共享任何内容之前先用openmed.deidentify(report, policy...)移除患者姓名、MRN、检查号accession和日期再从deidentified_text开始结构化。deidentify支持mask/remove/replace/hash/shift_dates等方法openmed/init.py技能示例使用hipaa_safe_harbor策略。按标准标题切分章节TECHNIQUE、COMPARISON、FINDINGS、IMPRESSION以及 HISTORY / INDICATION。各机构报告格式不一——若缺少标题则回退到位置启发式。运行analyze_text获取解剖部位与发现实体并在每条 finding 附近捕获测量值8 mm、1.2 cm与侧别right、left、bilateral。每条观察构建一条结构化 finding{anatomy, finding, laterality, measurement, change_vs_prior, source_offsets}。Unchanged、stable、increased、new 等词相对对照报告捕获时间变化。从 impression 中提取评估分类BI-RADS 0-6、Lung-RADS 1-4X与推荐的随访方案方式 间隔。标记偶然发现——与检查指征无关的发现——并推入随访追踪器避免遗漏。按需映射到 RadLex / DICOM-SR以获得编码互操作能力并把整体结构呈现给放射科医生复核。源码级原理仓库中的确定性解析器除了技能中的轻量示例实现仓库还提供了两个可直接调用的确定性解析器openmed/clinical模块已将其导出见 openmed/clinical/init.py可以作为技能示例的生产级落点。parse_radiology_report章节分段 stated RADS 捕获实现在 openmed/clinical/radiology_report.py返回一个RadiologyReportTemplatefrom openmed.clinical import parse_radiology_report parsed parse_radiology_report(report_text) parsed[findings_text] # findings 章节文本 parsed[impression_text] # impression 章节文本 parsed[recommendation_text] # 随访建议章节文本 parsed[assessment_system] # BI-RADS 或 Lung-RADS仅当显式书写 parsed[assessment_category] # 如 2、4A逐字读取从不推断 parsed[section_spans] # 各章节在原文中的字符 span溯源用其设计要点可直接印证技能中的边界情况章节标题词表驱动的分段SECTION_HEADINGS定义了三个规范章节findings/impression/recommendation及其别名如 impression 的impressions、conclusion、interpretation匹配发生在行首可选冒号SECTION_INLINE_CUES则处理扁平化报告中的行内提示语如IMPRESSION:且限定在句末/行首出现避免把 not an impression: 误判为标签openmed/clinical/radiology_report.py。保留章节字符 spansection_spans映射每个章节到(start, end)半开区间_trim_span去除首尾空白保证标题标签不会泄漏进正文——这正是每条发现可溯源的实现基础。stated RADS 捕获绝不推断_capture_assessment只读取报告中显式书写的 BI-RADS0-6或 Lung-RADS1、2、3、4A、4B、4X分类。正则要求分类号要么由 qualifier 词assessment、category、score引导要么处于终止位后跟标点/换行/文本结尾从而拒绝BI-RADS 4 lesions、BI-RADS 0-6 scale这类非评估语境_is_reference_context还会排除 atlas、legend、scale 等参考文献语境openmed/clinical/radiology_report.py。模块级常量RADIOLOGY_REPORT_ADVISORY明确声明捕获的分类逐字读自报告文本绝不从发现中计算或推断。无标题兜底当找不到任何标题或提示语时整份报告按 findings 散文处理_segment中spans[FINDINGS]覆盖全文。extract_radiology_findingsspan 邻近图绑定侧别与尺寸实现在 openmed/clinical/radiology_finding.py返回按原文顺序排列的RadiologyFinding列表from openmed.clinical import extract_radiology_findings findings extract_radiology_findings( A 8 mm solid nodule is noted in the right upper lobe, unchanged., radlex_mappingNone, # 可传 dict 或本地 JSON 路径 max_attribute_distance80, # 属性绑定最大字符间隔 ) # 每项含: finding, laterality, size_value, size_unit, # location, radlex_code, provenance_spans该解析器是确定性提取辅助工具RADIOLOGY_FINDING_ADVISORY声明其不包含 RadLex 本体、不推断诊断、不否定发现、不归一化测量值。核心机制词表小且透明laterality 词表RADIOLOGY_LATERALITY_LEXICON含bilateral/left-sided/b/l/lt/rt等约 12 个表层写法finding 词表_FINDING_TERMS含 nodule、mass、effusion、pneumothorax 等约 22 个常见术语复数映射回单数——刻意不内嵌本体openmed/clinical/radiology_finding.py。span 邻近图绑定每个句子状 clause 成为一个图finding mention 与其连接词分隔的邻域内、且字符间隔不超过max_attribute_distance默认 80的属性 mention 连边最短边胜出、源位置为确定性平局裁决。_binding_windows用连接词, and/but/plus/with切分邻域避免right ... nodule and left ... mass交叉绑定openmed/clinical/radiology_finding.py。这直接支撑了技能中侧别必须按偏移绑定到最近解剖 span而不是整篇文档级匹配的告诫。测量值带单位归一化_SIZE_RE捕获\d(\.\d)?mm|cm等单位_normalized_unit把 millimeters/centimeters 归一化为mm/cm裸数字 8 不会被当成测量值。RadLex 映射由调用方提供radlex_mapping可传dict或本地 UTF-8 JSON 文件路径键为 finding 文本、值为 RadLex 码_load_radlex_mapping负责校验与大小写折叠不打包、不下载任何本体openmed/clinical/radiology_finding.py。全程溯源provenance_spans始终包含findingspan并在存在时补充laterality、size_value、size_unit、location的原文偏移。测试与金标准验证仓库为上述解析器提供了 fixture 驱动的金标准测试tests/unit/clinical/test_radiology_report.py 逐行断言 findings/impression/recommendation 切分文本与 stated 评估分类完全匹配金标准并验证合成数据标记、标题在独立行/同行内联/缺失标题时的提示语回退等行为配套的 tests/unit/clinical/test_radiology_finding.py 覆盖发现提取与属性绑定。金标准 fixtures 位于tests/fixtures/clinical/下含 radiology_report.jsonl、radiology_finding.jsonl、radiology_entity_relations.jsonl评估套件见 openmed/eval/suites/radiology_relations.py。与 OpenMed 生态的交接技能文档定义了与相邻技能的协作边界本文将其整理为可点击的仓库路径上游extracting-clinical-entities 产出的 Anatomy 与 Disease/finding 实体填充每条结构化 finding务必保留偏移让每个字段都能追溯到源句子。上游扫描件若报告是扫描件先用openmed.multimodal.ocr.ocr做 OCR见 extracting-lab-tables再对识别文本跑 NER。上游章节若报告不使用规范标题可复用 segmenting-clinical-sections 的章节检测。下游building-patient-timelines 用带日期的发现 对照变化构建纵向视图如结节随时间的尺寸变化。下游extracting-dicom-metadata 在组装 DICOM-SR 对象时把结构化发现与检查的 DICOM 元数据配对。去标识化任何导出前先用 deidentifying-clinical-textopenmed.deidentify处理。一切都在设备端运行——无云端、无患者数据出网。边界情况与常见陷阱这是技能文档中最值得反复对照的部分逐条展开否定与不确定性改变语义。No pleural effusion 与 cannot exclude metastasis 是关于缺失/不确定的发现——不要记为阳性发现。在断言 finding 前先用 resolving-clinical-contextopenmed.clinical处理否定与模糊语义。侧别错误在临床上很危险。Right 与 left 必须绑定到正确的 finding归错侧可导致错误的部位决策。侧别要按偏移绑定到最近的解剖 span而不是整篇文档级匹配——这正是extract_radiology_findings的 span 邻近图机制要解决的。测量值必须有单位与轴向。8 mm 与 0.8 cm 等价裸 8 有歧义。必须捕获单位对肿块要捕获全部报告的维度如 2.1 x 1.4 cm不能只取第一个。评估分类有受控值集。BI-RADS 0-6 与 Lung-RADS 1、2、3、4A、4B、4X 各自映射到明确的管理动作——不要发明或四舍五入分类从 impression 中逐字读取字面值仓库解析器_capture_assessment严格如此实现。偶然发现容易被漏掉。胸 CT 中提到的肾囊肿是经典的漏随访案例。要把偶然发现与指征相关发现明确分开并把偶然发现推入追踪器。impression 是可操作的摘要但 findings 可能包含 impression 遗漏的细节——两者都要结构化随访/评估优先采用 impression。决策支持声明。这不是诊断医疗设备它只是组织放射科医生撰写的文本。每个结构化字段都必须可对照其来源复核。派生出的分类或随访建议未经临床医生签字不得自动执行。标准与参考本技能涉及的行业标准原文仅列名称与官网此处保留名称便于检索不重复外部链接RadLexRSNA 放射学词典用于发现术语编码可选由调用方提供映射。DICOM Structured ReportingPS3.16 模板结构化报告互操作目标。ACR BI-RADS Atlas乳腺影像报告与数据系统评估分类 0-6。ACR Lung-RADS肺筛查报告与数据系统评估分类 1、2、3、4A、4B、4X。RSNA Radiology Reporting 模板库章节化报告模板参考。ACR Incidental Findings 白皮书偶然发现管理参考。HL7 FHIR R4 DiagnosticReportimaging影像诊断报告的资源化导出目标。小结把放射学报告从散文变为结构化数据核心不是更聪明的模型而是受控的词表、明确的章节边界、按偏移的属性绑定与严格的溯源。OpenMed 的设备端 NER 提供解剖部位 / 发现 / 测量实体structuring-radiology-reports技能负责把它们组装成带评估分类与随访建议的分节结构仓库中的 radiology_report.py 与 radiology_finding.py 则是可直接落地的确定性实现。始终记住两条底线先去标识化再结构化每条结构化字段都能溯源回源句子供放射科医生复核。【免费下载链接】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),仅供参考