annotating-variants
Annotates VCF variants and normalizes HGVS nomenclature with public, license-free annotators…
Turn scanned faxes, images, and CSV/CDA exports into clean text ready for OpenMed de-identification and NER, fully on-device. Use when the user has clinical documents (image scans, photographed/faxed notes, tabular CSV/TSV exports, C-CDA XML) and needs OCR or structured intake
$ npx -y skills add maziyarpanahi/openmed --skill ingesting-clinical-documents --agent claude-codeHow it fires
How this skill gets triggered: by you, by Claude, or both.
/ingesting-clinical-documentsContext preview
The summary Claude sees to decide when to auto-load this skill.
Turn scanned faxes, images, and CSV/CDA exports into clean text ready for OpenMed de-identification and NER, fully on-device. Use when the user has clinical documents (image scans, photographed/faxed notes, tabular CSV/TSV exports, C-CDA XML) and needs OCR or structured intake
name: ingesting-clinical-documents description: "Turn scanned faxes, images, and CSV/CDA exports into clean text ready for OpenMed de-identification and NER, fully on-device. Use when the user has clinical documents (image scans, photographed/faxed notes, tabular CSV/TSV exports, C-CDA XML) and needs OCR or structured intake before openmed.deidentify and openmed.analyze_text, asks about openmed.multimodal, OCR engines (Tesseract / PaddleOCR), tabular redaction, or layout and reading order. Covers the verified ocr() and redact_document() entry points and the ExtractedDocument contract. Pairs before deidentifying-clinical-text and extracting-clinical-entities." license: Apache-2.0 metadata: project: OpenMed category: imaging-ocr pairs: before version: "1.0"
Clinical text often arrives as scanned faxes, photographed notes, CSV exports, or C-CDA XML — not plain text. `openmed.multimodal` converts these into a normalized `ExtractedDocument` (clean text + character-offset → source-location spans) so you can run de-identification and NER. It runs **on-device**: OCR backends are local, no document leaves the machine.
`openmed.analyze_text`.
This is the **first** stage. After intake, hand off to `deidentifying-clinical-text` then `extracting-clinical-entities`.
`redact_document` dispatches by file extension. Live handlers:
| Input | Extensions | Path | | --- | --- | --- | | Images / scans | `.png .jpg .jpeg .tif .tiff .bmp .gif .webp` | OCR (`ocr()` / image handler) | | Tables | `.csv .tsv` | column-aware tabular redaction | | C-CDA | `.xml` (detected as CDA) | stdlib CDA adapter |
**PDF and DOCX have no live handler yet** — `redact_document("x.pdf")` raises `UnsupportedDocumentError`. Convert PDFs to page images first (or to text with your own tool) and feed the images through OCR. See [references/multimodal-ingest.md](references/multimodal-ingest.md) for the full contract, engines, and the tabular pipeline.
pip install "openmed[multimodal]" # document intake contract + image deps pip install "openmed[ocr-paddle]" # add the PaddleOCR engine # Tesseract engine also needs the system binary, e.g.: brew install tesseract
The clean two-step intake path. `ocr()` lives in the submodule (it is intentionally not re-exported from `openmed.multimodal`):
from openmed.multimodal.ocr import ocr
import openmed
# 1) OCR a scanned/faxed note -> OcrResult -> ExtractedDocument -> plain text
result = ocr("fax_page.png", engine=None) # None = auto-select an installed engine
doc = result.to_document() # ExtractedDocument
text = doc.text # clean text for downstream OpenMed
# 2) De-identify, then run NER (privacy-first order)
deid = openmed.deidentify(text, method="mask", policy="hipaa_safe_harbor")
ner = openmed.analyze_text(deid.deidentified_text, output_format="dict")
for ent in ner.entities:
print(ent.label, ent.text, ent.confidence)`engine` may be `None` (auto-select), `"tesseract"`, `"paddleocr"`, or an `OcrEngine` instance. `OcrResult` exposes `.text` and per-word boxes via `.words` (each `OcrWord` has `text`, `bbox`, `confidence`, `page`).
For images, CSV/TSV, and CDA, `redact_document` performs intake **and** de-identification in a single, format-aware call, returning an already-redacted `ExtractedDocument`:
from openmed.multimodal import redact_document
# Image scan: OCR + redact in one call
doc = redact_document("fax_page.png")
print(doc.text) # redacted text
print(doc.spans[:3]) # SourceSpan offsets -> page / bbox in the original scan
# CSV export: per-column classification (direct id / quasi-id / safe) + redaction
table_doc = redact_document("patients.csv")
print(table_doc.text)Use `redact_document` when you want OpenMed to own intake **and** redaction (especially for tables, where redaction is column-scoped, not free-text NER). Use the `ocr()` → `to_document()` → `deidentify` path when you want to control the de-identification method, policy, or mapping yourself.
CSV columns get classified before any cell is touched, so a free-text NER pass is not run blindly over structured data:
from openmed.multimodal import read_table, redact_table
view = read_table("patients.csv") # TableView with column decisions
for col in view.columns:
print(col.name, "->", col.assigned_class, col.action, col.canonical_label)
redacted = redact_table("patients.csv", keep_year=True)
print(redacted.text) # redacted CSV
for entry in redacted.manifest: # PHI-SAFE audit: counts/actions per column, no raw values
print(entry)`redact_table(...)` returns a `RedactedTable` with `.text`, `.headers`, `.rows`, `.columns`, and a PHI-safe `.manifest` (no raw cell values). See [references/multimodal-ingest.md](references/multimodal-ingest.md) for column classes and actions.
Every `ExtractedDocument` keeps character offset → source location. After detecting PHI on `doc.text`, project a span's offset back to its page and bounding box:
from openmed.multimodal.ocr import ocr
import openmed
doc = ocr("fax_page.png").to_document()
deid = openmed.deidentify(doc.text, method="mask")
for ent in deid.pii_entities:
loc = doc.location_at(ent.start) # SourceSpan or None
if loc is not None:
print(ent.label, "page", loc.page, "bbox", loc.bbox)This lets you redact pixels on the
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.
Repo: maziyarpanahi/openmed
Annotates VCF variants and normalizes HGVS nomenclature with public, license-free annotators…
Choose the first OpenMed workflow skill for an intake, privacy, extraction, exchange, or…
Package multiple FHIR R4 resources produced from OpenMed output into a single valid…
Adversarially scan already-de-identified clinical text for residual identifiers and emit a…
Produce a signed, reproducible, no-PHI audit trail for an OpenMed de-identification run via…
Generates and verifies 21 CFR Part 11-style audit trails — who/what/when, electronic…