Skip to main content

medlint

A lightweight, evidence-first split-integrity auditor for medical imaging datasets.

medlint is a pre-training smoke test. It examines declared dataset splits and reports evidence of relationships that may indicate a contamination risk, such as an exact file appearing in both train and test, a source/issuer-scoped PatientID recurring across splits, or the same DICOM UID recurring across splits.

It does not prove patient identity, detect every form of data leakage, or certify that a dataset is leakage-free.

Status: 0.1.0 is an alpha-stage public API. Its manifest, report, CLI, and Python interfaces may change before 1.0.0.

Why medlint?

Medical imaging datasets often combine repeated examinations, multiple exports, derived copies, or inconsistently de-identified DICOM records. Files that still share a patient, acquisition, or exact-content relationship can accidentally be assigned to different model-development splits.

medlint gives researchers an explainable checkpoint before expensive model training. It reports:

  • what was inspected;
  • what evidence was found;
  • why each relationship may matter;
  • what could not be evaluated; and
  • what should be reviewed manually.

No source file is changed.

v0.1.0 scope

The first release focuses on a deliberately narrow, deterministic workflow:

  • manifest-based or explicit split-root discovery;
  • n-way split definitions such as train, validation, test, and folds;
  • exact file fingerprints for recognized DICOM and common raster inputs;
  • basic DICOM metadata analysis for source/issuer-scoped PatientID and separate study, series, and instance UID relationships;
  • coverage for unreadable, unsupported, missing, and unusable inputs;
  • evidence-based findings with stable rule identifiers;
  • a concise terminal summary and deterministic JSON report;
  • local, offline, read-only operation; and
  • privacy-safe record aliases in default output.

The core depends only on pydicom and the Python standard library. It has no ML framework, model-weight, database, server, or cloud dependency.

Not included in v0.1.0

  • decoded-pixel fingerprinting;
  • perceptual or embedding-based image similarity;
  • model-based same-subject matching;
  • CT/MRI volume or multi-frame pixel analysis;
  • DICOM anonymization or burned-in PHI detection;
  • temporal, target, preprocessing, or pretraining-data leakage checks;
  • automatic deletion, movement, quarantine, or split repair; and
  • HTML reporting.

These limits are part of the product contract. A run with no findings means only that no configured finding was detected within the reported coverage.

Installation

Python 3.10 or newer is required; releases are tested on Python 3.10-3.13.

python -m pip install medlint

For development:

git clone https://github.com/mehmetaytugyuruk/medlint.git
cd medlint
python -m venv .venv
source .venv/bin/activate
python -m pip install -e '.[dev]'

Quick start

Runnable PHI-free example

A source checkout includes a deterministic generator for three valid one-pixel PNG files. One train image is copied byte-for-byte into the test split so the example has a known ML001 finding:

python examples/quickstart/create_example.py
medlint audit \
  --manifest medlint-example-data/splits.csv \
  --output medlint-example-report.json

The audit returns exit code 1 and reports a potential contamination risk based on exact file bytes. The example contains no clinical data or identifiers.

Option 1: manifest

The canonical input is a UTF-8 CSV manifest. The required columns are path and split; source_namespace is optional but strongly recommended when data from more than one export or institution is combined.

path,split,source_namespace
data/train/image-001.dcm,train,hospital-a-export-1
data/train/image-002.dcm,train,hospital-a-export-1
data/test/image-101.dcm,test,hospital-a-export-1

Paths may be relative to the manifest location. Identifiers are treated as strings. medlint does not infer patient identity from file or directory names. At least two distinct splits are required for a conclusive cross-split audit.

medlint audit \
  --manifest splits.csv \
  --output medlint-report.json

Option 2: explicit split roots

For a simple directory layout, repeat --split NAME=PATH:

medlint audit \
  --split train=data/train \
  --split validation=data/validation \
  --split test=data/test \
  --source-namespace local \
  --output medlint-report.json

--source-namespace local is the default convenience namespace for split-root input. Choose an explicit, stable namespace when datasets from different sources are combined. A shared PatientID without an appropriate issuer or source namespace is not assumed to be globally unique.

Run medlint audit --help for the complete command reference.

Reading a result

The terminal summary is intentionally conservative. An abbreviated example is:

medlint split-integrity audit
Audit state: complete_with_findings
Finding status: potential_risk_detected
Coverage status: complete

Coverage
  Declared splits: 2
  Evaluable splits: 2
  Discovered: 1200
  Exact-file fingerprints: 1200

Findings: 1
  [ML001] 2 records in 2 splits have identical file bytes.

Potential contamination risk detected; review the factual evidence before model training.

Each JSON finding records the rule, evidence category, affected splits, privacy-safe record aliases, observed fact, limitations, and suggested review action. Evidence and policy outcomes are kept separate: a detector reports an observation, while policy evaluates the findings and coverage for the audit. The v0.1 default policy treats every emitted finding as review-blocking and returns exit code 1; it does not assign per-finding informational or warning levels.

Top-level audit states distinguish complete runs with or without findings from partial, inconclusive, and operational-error runs.

Exit codes

Code Meaning
0 Audit completed with required coverage and no configured finding
1 One or more findings require review under the v0.1 default policy
2 An operational error prevented a valid audit
3 Coverage was partial or inconclusive and no finding took precedence

Exit code 0 is not a leakage-free certificate. CI should archive and inspect the JSON report, especially its coverage section. The default policy gives a finding (1) precedence over partial coverage (3); the JSON still preserves both states.

Privacy and offline behavior

medlint is designed for local research environments:

  • it does not upload datasets, call a remote API, or send telemetry;
  • it opens source data read-only and never changes or repairs it;
  • default reports use opaque record aliases;
  • default reports omit raw DICOM identifiers and absolute paths;
  • --include-paths is an explicit opt-in intended only for controlled local debugging; and
  • v0.1.0 does not create image thumbnails or persist image pixels.

Reports may still be sensitive because relationships, filenames explicitly included by the user, and local operational details can be identifying. Handle them under the same institutional rules that govern the source dataset. Do not commit datasets or reports containing patient information to this repository.

See the privacy and threat model for details.

Supported data and interpretation

v0.1.0 recognizes .dcm and .dicom files, plus common raster suffixes: .bmp, .jpeg, .jpg, .png, .tif, and .tiff. An extensionless file is recognized as DICOM only when it has the DICOM Part 10 DICM marker after the 128-byte preamble. Other extensions are reported as unsupported and are not fingerprinted in this release.

Recognized DICOM and raster files receive exact byte-level fingerprints. Raster pixels are not decoded or interpreted. DICOM analysis is metadata-only, so v0.1.0 neither requires pixel decoders nor reports transfer-syntax decoding coverage. CR/DX is the primary tested DICOM profile; multi-frame objects, out-of-profile modalities, manifest-listed broken paths, malformed files, and unknown file types remain visible as coverage limitations. An invalid manifest or explicit split root is an operational error because discovery cannot begin.

See:

Development

python -m pip install -e '.[dev]'
ruff check .
ruff format --check .
mypy src/medlint
pytest
python -m build
python -m twine check dist/*

Contributions are welcome after reading the contribution guide and detector guide. Never use real PHI in an issue, pull request, fixture, log, screenshot, or CI artifact.

Responsible claims

Please describe medlint as an audit or smoke test, not as proof that leakage is present or absent. A finding is evidence of a specific observed relationship. Its scientific meaning depends on the dataset, intended evaluation, split policy, metadata quality, and audit coverage.

License

Licensed under the Apache License 2.0.

Citation

If medlint supports your research, use the metadata in CITATION.cff.

Download files

Download the file for your platform. If you're not sure which to choose, learn more about installing packages.

Source Distribution

medlint-0.1.0.tar.gz (63.4 kB view details)

Uploaded Source

Built Distribution

If you're not sure about the file name format, learn more about wheel file names.

medlint-0.1.0-py3-none-any.whl (39.9 kB view details)

Uploaded Python 3

File details

Details for the file medlint-0.1.0.tar.gz.

File metadata

  • Download URL: medlint-0.1.0.tar.gz
  • Upload date:
  • Size: 63.4 kB
  • Tags: Source
  • Uploaded using Trusted Publishing? No
  • Uploaded via: twine/7.0.0 CPython/3.13.14

File hashes

Hashes for medlint-0.1.0.tar.gz
Algorithm Hash digest
SHA256 4f7ba5876819459bf96a2cb15fb55462a8c14b82891ebf29e09f9c692030e779
MD5 9335297b7a35b1983da609cb55ef412d
BLAKE2b-256 fe1efe5f8d83608b1eefda02cc60a516a94cfcf00da5ad7b90ddbb22f20ee7f4

See more details on using hashes here.

File details

Details for the file medlint-0.1.0-py3-none-any.whl.

File metadata

  • Download URL: medlint-0.1.0-py3-none-any.whl
  • Upload date:
  • Size: 39.9 kB
  • Tags: Python 3
  • Uploaded using Trusted Publishing? No
  • Uploaded via: twine/7.0.0 CPython/3.13.14

File hashes

Hashes for medlint-0.1.0-py3-none-any.whl
Algorithm Hash digest
SHA256 e1436e0d3eda602e4e8a54c2ac3bd52c3839919d06314ac08dc32400e726ef28
MD5 983ead0755ececeee808bd513bf49e09
BLAKE2b-256 399d73bc8f04c5d50d15c406770bb5083d126e74e8f652b12bb87f4d15c10c72

See more details on using hashes here.

Release history Release notifications | RSS feed

0.1.1

2 files

This release

0.1.0 This release

2 files

Anthropic, PBC Visionary sponsor Bloomberg Visionary sponsor Hudson River Trading Visionary sponsor Meta Visionary sponsor NVIDIA Visionary sponsor Microsoft Sustainability sponsor Depot Continuous Integration AWS Cloud computing and Security Sponsor Datadog Monitoring Fastly CDN Google Download Analytics Sentry Error logging StatusPage Status page