OCR-driven anonymization pipeline for medical reports and endoscopy frames
Project description
LX Anonymizer
LX Anonymizer is a comprehensive toolkit for de-identifying endoscopy frames and medical reports. It combines advanced OCR pipelines, spaCy-based NER, heuristic sanitizers, and report-specific rules to redact or pseudonymize sensitive information while preserving clinical context.
Core Components
ReportReader
Specialized for medical report anonymization with support for:
- Multi-format processing: PDFs and images with automatic OCR fallback
- Advanced metadata extraction: LLM-powered extraction using DeepSeek, MedLLaMA, or Llama3
- Ensemble OCR: Combines Tesseract and TrOCR for improved accuracy
- PDF anonymization: Creates blackened PDFs with sensitive regions automatically masked
- Batch processing: Handles multiple reports with comprehensive error handling
FrameCleaner
Designed for real-time video frame anonymization featuring:
- Hardware-accelerated processing: NVIDIA NVENC support with CPU fallback
- Streaming video processing: Processes videos without full re-encoding when possible
- Adaptive frame sampling: Optimizes performance for long videos (>10,000 frames)
- Multiple anonymization strategies: Frame removal or mask overlay techniques
- ROI-based masking: Device-specific region masking for endoscopic equipment
Default Return Format
LX Anonymizer will return a sensitive meta compliant dict when running either of the main client functions above.
Highlights
- End-to-end anonymization of PDFs and video sequences using OCR, NER, and pseudonymization helpers.
- Modular pipeline that lets you choose between Tesseract, TrOCR, ensemble OCR, and multiple metadata extractors.
- Hardware optimization with NVENC acceleration for real-time video processing and streaming capabilities.
- Human-in-the-loop ready outputs: original/anonymized text side by side, metadata JSON, and validation artefacts.
- Extensible ruleset covering device-specific renderers, fuzzy name matching, and language-specific replacements.
Requirements
- Python 3.12+
- Linux or macOS (Windows support is experimental)
- NVIDIA GPU recommended for real-time video anonymization (CUDA 12.x). CPU-only processing works but is slower.
- Optional extras:
- spaCy
de_core_news_lgmodel (download after installation) - Torch vision/audio for video OCR workloads
- local or remote LLM-backed metadata extraction
- spaCy
Installation
From Nix
The repository exposes flake packages and can be consumed directly from another
project's devenv.yaml:
inputs:
lx-anonymizer:
url: github:wg-lux/lx-anonymizer
After adding the input, reference the package through your own devenv.nix or
flake outputs. You do not need to commit or publish local result or
result-app symlinks for this to work.
From PyPI
pip install lx-anonymizer
Install extras only when you need the corresponding feature set:
pip install "lx-anonymizer[ocr]" # TrOCR, tesserocr, CRAFT helpers
pip install "lx-anonymizer[llm]" # LLM client-side helpers
pip install "lx-anonymizer[nlu]" # Flair NER
pip install "lx-anonymizer[django]" # Django integration
pip install "lx-anonymizer[dev]" # local development tooling
From source
git clone https://github.com/wg-lux/lx-anonymizer.git
cd lx-anonymizer
uv sync
Nix development shell
direnv allow
nix develop
This loads GPU, OCR, and tooling dependencies declared in devenv.nix.
Packaging
Python package
PyPI releases now use a split artifact strategy:
- platform wheels are built in GitHub Actions with
maturinand include the Rust extension - the source distribution is still built from
pyproject.tomlwithpython -m build --sdist
For a local source-package sanity check:
uv run python -m build --sdist
The published Python package remains the baseline install path, with optional
feature sets enabled through extras such as [ocr], [llm], and [nlu].
Native extension
The repository also contains an optional Rust extension used for local and Nix
packaging. The Python code loads it opportunistically through
lx_anonymizer._native and falls back to pure Python implementations when the
native module is unavailable or only partially implemented.
PyPI wheels built by CI now include this extension. Pure-Python fallback still exists for environments that install from source without a compiled native module.
Nix packages
The flake exports multiple package variants, including the base CLI package and a native-enabled package:
nix build .#lx-anonymizer
nix build .#lx-anonymizer-with-native
Those commands create local ./result symlinks for inspection on your machine.
They are build outputs, not repository contents, and should remain uncommitted.
Release guidance
- Use
uv run python -m build --sdistto validate the source distribution locally. - Use GitHub Actions to build release wheels with
maturin. - Use
nix build .#lx-anonymizerornix build .#lx-anonymizer-with-nativeto validate flake packaging. - Do not commit
resultorresult-app. - Configure PyPI trusted publishing before the first tagged release.
- Prefer a TestPyPI dry run before the first production PyPI publication.
Release workflow
The intended release path is now:
- Push a branch and let CI build wheels and the sdist.
- Verify the wheel smoke tests pass on Linux and macOS.
- Run a TestPyPI publication from the release workflow if this is the first native-wheel release.
- Tag
vX.Y.Zto trigger the production publish workflow.
The release workflow publishes:
- native wheels built with
maturin - an sdist built with
python -m build --sdist
Configuration
Settings are loaded from environment variables and an optional .env file. See
SETTINGS.md for a quick overview and example configuration.
Model downloads
After installation, fetch the German spaCy model used by the report pipeline:
python -m spacy download de_core_news_lg
Start a compatible LLM server exposing either an OpenAI-compatible API or Ollama:
# Default local setup used by the package examples
ollama pull qwen2.5:7b-instruct
ollama serve
# Alternative high-throughput setup
vllm serve Qwen/Qwen3.5-9B --port 8000
Caution: This is only recommended on devices with sufficient gpu capabilities
The EAST detector now downloads on first use, not on import. TrOCR and other optional OCR assets download only when those paths are exercised. For air-gapped deployments, pre-seed the required model files before running the relevant pipeline steps.
Quickstart
CLI Usage
Image / PDF Pipeline
# Process a single image or PDF with the packaged console script
lx-anonymizer -i report.pdf
# Use a custom EAST model and device profile
lx-anonymizer -i frame.png -east /models/frozen_east_text_detection.pb -d olympus_cv_1500
# Return validation metadata in addition to the output path
lx-anonymizer -i report.pdf -V
Useful CLI options:
-d/--deviceselects the device profile used for ROI handling.-c/--min-confidence,-w/--width, and-e/--heighttune EAST detection.-V/--validationreturns extra validation metadata.python -m lx_anonymizer.cli --helpshows the same CLI help aslx-anonymizer --help.
Python API
It is recommended to call the python api. Here, the main integration is with the endoreg-db package that is tightly integrated with lx-anonymizer to provide a private medical database.
ReportReader API
from lx_anonymizer import ReportReader
# Basic usage
reader = ReportReader(locale="de_DE")
original, anonymized, meta, pdf_path = reader.process_report(
pdf_path="/path/to/report.pdf",
use_ensemble=True,
use_llm=True,
)
# Create anonymized PDF with blackened sensitive regions
original, anonymized, meta, anonymized_pdf = reader.process_report(
pdf_path="/path/to/report.pdf",
create_anonymized_pdf=True,
anonymized_pdf_output_path="/path/to/output.pdf"
)
# Advanced processing with region cropping
original, anonymized, meta, cropped_regions, anonymized_pdf = reader.process_report_with_cropping(
pdf_path="/path/to/report.pdf",
crop_output_dir="/path/to/cropped_regions",
crop_sensitive_regions=True,
use_llm=True
)
ReportReader is the report-oriented entry point for PDFs, report screenshots, and
pre-extracted raw text.
ReportReader(...) constructor:
report_root_path: optional base path for report assets.locale: Faker locale for pseudonymized replacements.employee_first_names/employee_last_names: optional replacement pools.flags: optional parsing markers merged withDEFAULT_SETTINGS["flags"].text_date_format: output format used for anonymized date text.
process_report(...) accepts one primary content source:
pdf_path: usepdfplumberfirst, then OCR fallback when extracted text is too short.image_path: OCR a single report image.text: process already extracted text directly without file OCR.
process_report(...) parameters:
use_ensemble: enable ensemble OCR on OCR fallback paths.use_llm:Trueto try provider-backed extraction,Falseto force SpaCy/regex,Noneto use the instance default.create_anonymized_pdf: render a blackened PDF output for PDF inputs.anonymized_pdf_output_path: optional explicit path for that anonymized PDF.
process_report(...) returns:
original_text: extracted or provided source text.anonymized_text: anonymized text output.report_meta: metadata dict in the standardized sensitive-meta shape.anonymized_pdf_path:Path | Nonefor generated anonymized PDFs.
process_report_with_cropping(...) extends process_report(...) with:
crop_output_dir: where cropped sensitive regions are written.crop_sensitive_regions: enable or disable crop extraction.anonymization_output_dir: output directory for the crop-based anonymized PDF.
process_report_with_cropping(...) returns:
original_textanonymized_textreport_metacropped_regions_info: mapping of cropped sensitive regions.anonymized_pdf_path:Path | None
FrameCleaner API
from lx_anonymizer.frame_cleaner import FrameCleaner
from pathlib import Path
# Initialize with hardware acceleration
cleaner = FrameCleaner(use_llm=True)
# Clean video with mask overlay (preserves all frames)
cleaned_path, metadata = cleaner.clean_video(
video_path=Path("endoscopy.mp4"),
endoscope_image_roi={"x": 550, "y": 0, "width": 1350, "height": 1080},
endoscope_data_roi_nested={"patient_info": {"x": 10, "y": 10, "width": 300, "height": 50}},
technique="mask_overlay"
)
# Remove sensitive frames entirely
cleaned_path, metadata = cleaner.clean_video(
video_path=Path("endoscopy.mp4"),
endoscope_image_roi=roi_config,
endoscope_data_roi_nested=data_roi_config,
technique="remove_frames"
)
FrameCleaner is the video-oriented entry point for endoscopy footage and
frame-level overlays.
FrameCleaner(...) constructor:
use_llm: enables provider-backed batch metadata enrichment when available.use_minicpmandminicpm_config: reserved for optional OCR backends.
clean_video(...) parameters:
video_path: input video file.endoscope_image_roi: flat ROI dict for the visible endoscope image, typically withx,y,width,height.endoscope_data_roi_nested: nested ROI mapping for text-bearing overlay regions such as patient info blocks.output_path: optional explicit output path.technique: one ofmask_overlay,remove_frames, orextract_only.device: device profile name, defaulting toolympus_cv_1500.
clean_video(...) behavior by technique:
mask_overlay: preserves the timeline and overlays masks onto sensitive regions.remove_frames: drops sensitive frames and rewrites the stream.extract_only: does metadata extraction without producing a masked/removal-focused anonymization pass.
clean_video(...) returns:
output_video_path: resulting video path. Withextract_only, this is still the path chosen for the run.sensitive_meta: accumulated metadata dictionary extracted from sampled frames.
ROI guidance:
- Use
endoscope_image_roifor the main picture area that may need masking. - Use
endoscope_data_roi_nestedfor device-specific overlay fields. - The helper stack normalizes common ROI key variants, but using
x,y,width,heightdirectly is the least ambiguous form.
See tests/test_report_reader_init.py and tests/test_frame_cleaner.py for concrete usage patterns.
Advanced Features
ReportReader Capabilities
- Intelligent OCR Fallback: Automatically switches to OCR when PDF text extraction yields poor results
- Multi-LLM Support: DeepSeek, MedLLaMA, and Llama3 integration for enhanced medical entity extraction
- Ensemble OCR: Combines multiple OCR engines (Tesseract + TrOCR) for improved accuracy
- PDF Anonymization: Creates masked PDFs with sensitive regions automatically blackened
- Batch Processing: Processes multiple reports with error recovery and progress tracking
- Metadata Validation: Cross-validates extracted information using multiple extraction methods
FrameCleaner Capabilities
- Adaptive Sampling: Automatically samples frames for long videos (>10,000 frames) to optimize performance
- Hardware Acceleration: NVIDIA NVENC support with automatic CPU fallback for unsupported systems
- Streaming Processing: Uses FFmpeg streaming and named pipes to minimize memory usage and processing time
- ROI-based Processing: Device-specific region configurations for endoscopic equipment (Olympus CV-1500, etc.)
- Multiple Anonymization Strategies:
- Mask Overlay: Blacks out sensitive regions while preserving video timeline
- Frame Removal: Completely removes sensitive frames from the video stream
- Quality Optimization: Automatic pixel format conversion and codec selection for minimal quality loss
Performance Optimizations
- Stream Copy Operations: Avoids re-encoding when possible, using FFmpeg's
-c copyfor maximum speed - Named Pipe Support: In-memory video streaming for frame removal operations
- Batch Metadata Extraction: Processes multiple frames simultaneously for improved efficiency
- Hardware Detection: Automatically detects and uses available hardware acceleration (NVENC, QuickSync)
Data directories
By default, outputs live in ~/etc/lx-anonymizer/{data,temp}. Adjust them in lx_anonymizer/directory_setup.py. Clean temp regularly to avoid large intermediate artefacts.
Development workflow
- Code quality:
uv run flake8for linting and formatting - Testing:
- CPU-friendly tests:
uv run pytest -m "not gpu" - GPU-accelerated tests:
uv run pytest -m gpu(requires CUDA-capable hardware) - Integration tests:
uv run pytest tests/test_cli_integration.py - Frame processing tests:
uv run pytest tests/test_frame_cleaner.py
- CPU-friendly tests:
- Performance profiling: Use
--log-level DEBUGfor detailed timing information - Build:
uv run python -m build --sdistfor local sdist validation; GitHub Actions builds release wheels - Full validation:
scripts/run_checks.shfor comprehensive local testing
Testing Medical Workflows
- ReportReader: Test with sample medical PDFs in German and English
- FrameCleaner: Validate with endoscopic video files (MP4, AVI formats supported)
- Integration: Use
example_anonymize_pdf.pyfor end-to-end testing scenarios
Project roadmap
- Release Management:
- Continue hardening native-wheel publishing across release targets
- Continue separating optional GPU/LLM workloads behind extras
- Extend release automation with GitHub release notes and TestPyPI promotion flow
- API Enhancement:
- Expose REST/gRPC service with validation UI
- WebSocket support for real-time video processing
- Enhanced batch processing APIs
- Performance & Scalability:
- Distributed processing support for large video collections
- Advanced caching mechanisms for repeated processing
- Multi-GPU support for FrameCleaner operations
- Medical Workflow Integration:
- DICOM support for medical imaging workflows
- HL7 FHIR integration for healthcare systems
- Advanced medical entity recognition models
Contributing
See CONTRIBUTING.md for contribution guidelines, testing instructions, and communication channels.
License
Released under the MIT License.
Contact
Questions? Email lux@coloreg.de .
Project details
Release history Release notifications | RSS feed
Download files
Download the file for your platform. If you're not sure which to choose, learn more about installing packages.
Source Distribution
Built Distribution
Filter files by name, interpreter, ABI, and platform.
If you're not sure about the file name format, learn more about wheel file names.
Copy a direct link to the current filters
File details
Details for the file lx_anonymizer-0.9.0.7.tar.gz.
File metadata
- Download URL: lx_anonymizer-0.9.0.7.tar.gz
- Upload date:
- Size: 1.7 MB
- Tags: Source
- Uploaded using Trusted Publishing? No
- Uploaded via: twine/6.2.0 CPython/3.12.13
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
e986c97ad63f4a76c870c8e62985493507a41a4a28eb3db350b1bcaea5f869f8
|
|
| MD5 |
f64455998636b7d95b3be5eaeb4883b4
|
|
| BLAKE2b-256 |
e16587e1cee00fe772b80b4bb16d5ede11d62be24ff08b0c49b2015e37a908c6
|
File details
Details for the file lx_anonymizer-0.9.0.7-cp312-abi3-manylinux_2_17_x86_64.manylinux2014_x86_64.whl.
File metadata
- Download URL: lx_anonymizer-0.9.0.7-cp312-abi3-manylinux_2_17_x86_64.manylinux2014_x86_64.whl
- Upload date:
- Size: 1.6 MB
- Tags: CPython 3.12+, manylinux: glibc 2.17+ x86-64
- Uploaded using Trusted Publishing? No
- Uploaded via: twine/6.2.0 CPython/3.12.13
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
65fec86768549e7fbd98b777722cc5d9c7185aedb5ce44d4a9e1f9d1710a0475
|
|
| MD5 |
7b7037c8acfd544139ca9fc692e77828
|
|
| BLAKE2b-256 |
66ccb9cdf9de153025740b73069547325659a0413cd62fe093ba8491402c3e58
|