Skip to content
Security
Skill

/structuring-radiology-reports

Converts free-text radiology narratives into structured findings and impression — with measurements, laterality, anatomy, and follow-up recommendations — after OpenMed NER. Use when the user has a CT/MRI/X-ray/ultrasound/mammography report and needs the sections split

BOOST
From plugin
openmed
5.7k74 skills
Install
$ npx -y skills add maziyarpanahi/openmed --skill structuring-radiology-reports --agent claude-code

How it fires

How this skill gets triggered: by you, by Claude, or both.

  • Fires itselfAuto-invocation. Claude auto-loads it when your prompt matches the work.Auto-invocation is when the right skill fires by itself at the right moment, driven by a FLOW.md router and a hook, instead of you invoking it by name. It is the difference between a skill being installed and a skill actually getting used.Read the full definition →
  • You can call itInvoke it directly when you want it.
  • Slash command/structuring-radiology-reports

Context preview

The summary Claude sees to decide when to auto-load this skill.

Converts free-text radiology narratives into structured findings and impression — with measurements, laterality, anatomy, and follow-up recommendations — after OpenMed NER. Use when the user has a CT/MRI/X-ray/ultrasound/mammography report and needs the sections split

SKILL.md

structuring-radiology-reports.SKILL.md
name: structuring-radiology-reports
description: "Converts free-text radiology narratives into structured findings and impression — with measurements, laterality, anatomy, and follow-up recommendations — after OpenMed NER. Use when the user has a CT/MRI/X-ray/ultrasound/mammography report and needs the sections split (technique, comparison, findings, impression), lesion measurements and laterality captured, BI-RADS / Lung-RADS assessment categories pulled, or incidental findings and recommended follow-up tracked. Trigger keywords: radiology report, findings, impression, RadLex, DICOM-SR, BI-RADS, Lung-RADS, ACR, laterality, measurement, nodule, incidental finding, follow-up, structured reporting. Pairs after OpenMed: run openmed.analyze_text on the report (Anatomy/Disease/measurement entities), then assemble structured findings. De-identify the report first. Decision-support only — not a diagnostic medical device."
license: Apache-2.0
metadata:
  project: OpenMed
  category: imaging-ocr
  pairs: after
  version: "1.0"

Structuring radiology reports

A radiology report is prose, but its *meaning* is structured: a **technique**, a **comparison**, a list of **findings** (each with anatomy, laterality, and a measurement), and an **impression** that may carry an **assessment category** (BI-RADS, Lung-RADS) and a **follow-up recommendation**. This skill turns the narrative into that structure so findings are trackable — especially **incidental findings** that need downstream follow-up.

OpenMed extracts the anatomy, disease/finding, and measurement spans on-device; this skill organizes them into sectioned, coded findings. It is **decision-support, not a diagnostic device** — every structured finding must be attributable back to its source sentence for radiologist review.

When to use

  • You have a CT/MRI/X-ray/US/mammography report and need

`{technique, comparison, findings[], impression}` with measurements and laterality.

  • You must capture **BI-RADS** (breast) or **Lung-RADS** (lung screening)

assessment categories and the recommended action.

  • You need to **track incidental findings** and the follow-up interval/modality

the report recommends.

  • You are mapping findings toward **RadLex** terms or a **DICOM-SR** structured

report.

Quick start

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) De-identify the report on-device first (synthetic example shown).
deid = openmed.deidentify(report, policy="hipaa_safe_harbor")
text = deid.deidentified_text

# 2) Run NER for anatomy / finding / measurement spans.
ents = openmed.analyze_text(
    text,
    model_name="anatomy_detection_superclinical",   # Anatomy category
    output_format="dict",
)["entities"]

# 3) Split sections by header, then attach entities + measurements per 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", "")),   # one per sentence
    "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(r"RADS", text, re.I) else None,
    "follow_up": _extract_followup(sections.get("IMPRESSION", "")),
}

`_split_findings` / `_extract_followup` are your sentence splitter and a recommendation matcher ("recommend …", "follow-up in N months"); keep each finding tied to its source sentence offsets.

Workflow

1. **De-identify first.** `openmed.deidentify(report, policy=...)`; structure from `deidentified_text`. Patient name, MRN, accession, and dates go before anything is stored or shared. 2. **Split sections** by the standard headers (TECHNIQUE, COMPARISON, FINDINGS, IMPRESSION; also HISTORY/INDICATION). Reports vary — fall back to position if headers are missing. 3. **Run `analyze_text`** for anatomy and finding entities; capture measurements ("8 mm", "1.2 cm") and laterality ("right", "left", "bilateral") near each finding. 4. **Build one structured finding per observation**: `{anatomy, finding, laterality, measurement, change_vs_prior, source_offsets}`. "Unchanged", "stable", "increased", "new" capture temporal change against the comparison. 5. **Pull the assessment category** (BI-RADS 0-6, Lung-RADS 1-4X) from the impression and the **recommended follow-up** (modality + interval). 6. **Flag incidental findings** — findings unrelated to the exam indication — and route them to a follow-up tracker so they aren't lost. 7. **Map toward RadLex / DICOM-SR** if you need coded interoperability, and surface the whole structure to a radiologist for verification.

Hand-off to / from OpenMed

OpenMed's `analyze_text` returns a `dict`; `result["entities"]` items carry `text`, `label`, `confidence`, `start`, `end`.

  • **From** `extracting-clinical-entities`: Anatomy and Disease/finding entities

populate each structured finding; keep offsets so every field traces to a source sentence.

  • **From** `extracting-lab-tables` / OCR: if the report is a scan, OCR it first

(`openmed.multimodal.ocr.ocr`), then run NER on the recognized text.

  • **From** `segment
Read more
Ships withopenmed

Local-first healthcare AI: clinical NER and HIPAA PII de-identification on hardware you control. 2,200+ medical models, 35 model-backed PII languages, and Python, MLX, Android and browser runtimes. Apache-2.0 SDK.

Get the whole plugin

Other skills on openmed.