Metadata-Version: 2.4
Name: phi-finder
Version: 0.1.14
Summary: Collection of tools to check uploaded scans and records for identifiable data.
Project-URL: repository, https://github.com/Australian-Imaging-Service/phi-finder
Author-email: Pedro Faustini <pedro.faustini@mq.edu.au>
Maintainer-email: Pedro Faustini <pedro.faustini@mq.edu.au>
Keywords: anonymisation,clinical data,dicom
Classifier: Development Status :: 3 - Alpha
Classifier: Environment :: Console
Classifier: Intended Audience :: Science/Research
Classifier: License :: OSI Approved :: Apache Software License
Classifier: Operating System :: MacOS :: MacOS X
Classifier: Operating System :: Microsoft :: Windows
Classifier: Operating System :: POSIX :: Linux
Classifier: Programming Language :: Python :: 3.10
Classifier: Topic :: Scientific/Engineering
Requires-Python: <3.13,>=3.11
Requires-Dist: faker>=36.1.1
Requires-Dist: fileformats-medimage-extras
Requires-Dist: gliner==0.2.26
Requires-Dist: numpy==1.26.4
Requires-Dist: opencv-python>=4.10.0.84
Requires-Dist: pandas>=2.2.3
Requires-Dist: pdfminer-six>=20240706
Requires-Dist: pikepdf>=9.4.0
Requires-Dist: presidio-analyzer>=2.2.355
Requires-Dist: presidio-anonymizer>=2.2.355
Requires-Dist: presidio-image-redactor>=0.0.53
Requires-Dist: presidio-structured>=0.0.4a0
Requires-Dist: pymupdf>=1.24.13
Requires-Dist: spacy
Requires-Dist: torch==2.6.0
Requires-Dist: transformers>=4.48.2
Requires-Dist: typing-extensions>=4.6.3; python_version < '4.0'
Provides-Extra: dev
Requires-Dist: black; extra == 'dev'
Requires-Dist: codespell; extra == 'dev'
Requires-Dist: fileformats[test]; extra == 'dev'
Requires-Dist: flake8; extra == 'dev'
Requires-Dist: flake8-pyproject; extra == 'dev'
Requires-Dist: mypy>=1.5.1; extra == 'dev'
Requires-Dist: pre-commit; extra == 'dev'
Requires-Dist: pydata-sphinx-theme>=0.13; extra == 'dev'
Requires-Dist: pydicom; extra == 'dev'
Provides-Extra: docs
Requires-Dist: docutils>=0.10; extra == 'docs'
Requires-Dist: furo>=2022.2.14.1; extra == 'docs'
Requires-Dist: mock>1.0; extra == 'docs'
Requires-Dist: numpydoc>=0.6.0; extra == 'docs'
Requires-Dist: packaging; extra == 'docs'
Requires-Dist: sphinx-argparse>=0.2.0; extra == 'docs'
Requires-Dist: sphinx-click>=3.1; extra == 'docs'
Requires-Dist: sphinx>=2.1.2; extra == 'docs'
Provides-Extra: test
Requires-Dist: fileformats-extras; extra == 'test'
Requires-Dist: fileformats-medimage-extras; extra == 'test'
Requires-Dist: frametree-xnat>=0.6.10; extra == 'test'
Requires-Dist: frametree>=0.15.2; extra == 'test'
Requires-Dist: medimages4tests; extra == 'test'
Requires-Dist: mypy>=1.5.1; extra == 'test'
Requires-Dist: openpyxl; extra == 'test'
Requires-Dist: pydra2app-xnat>=0.8.3; extra == 'test'
Requires-Dist: pydra2app>=0.20.0; extra == 'test'
Requires-Dist: pydra==1.0a3; extra == 'test'
Requires-Dist: pytest-cov>=2.12.1; extra == 'test'
Requires-Dist: pytest-env>=0.6.2; extra == 'test'
Requires-Dist: pytest>=6.2.5; extra == 'test'
Requires-Dist: thinc>=8.3.4; extra == 'test'
Requires-Dist: xnat4tests; extra == 'test'
Requires-Dist: xnat>=0.7.2; extra == 'test'
Description-Content-Type: text/markdown

# PHI-finder

[![CI/CD](https://github.com/australian-imaging-service/phi-finder/actions/workflows/ci-cd.yml/badge.svg)](https://github.com/australian-imaging-service/phi-finder/actions/workflows/ci-cd.yml)
[![Codecov](https://codecov.io/gh/australian-imaging-service/phi-finder/branch/main/graph/badge.svg?token=UIS0OGPST7)](https://codecov.io/gh/australian-imaging-service/phi-finder)


## Local testing (docker required)

```bash
conda create -n phi-finder python==3.11
conda activate phi-finder
pip install -e .[dev,test] --no-cache-dir
pytest .
```

## Building

```bash
python -m pip install --upgrade build

python -m build

pip install dist/phi_finder-0.1.14-py3-none-any.whl
```

## Basic usage (headers only)


```python
import pydicom as dicom
from phi_finder.dicom_tools import anonymise_dicom

path = "/path/to/some/dicom.dcm"
dcm = dicom.dcmread(path)
anonymised_dcm = anonymise_dicom.anonymise_image(dcm)
anonymised_dcm.save_as('/path/to/some/dicom_anon.dcm')
```

## More advanced usage

```python
import pydicom as dicom
from presidio_image_redactor import (
    DicomImageRedactorEngine, ImageAnalyzerEngine, ContrastSegmentedImageEnhancer)
from phi_finder.dicom_tools import anonymise_dicom

path = "/path/to/some/dicom.dcm"
dcm = dicom.dcmread(path)
score_threshold=.15
analyser = anonymise_dicom._build_presidio_analyser(score_threshold, "en_core_web_lg")
image_redactor = DicomImageRedactorEngine(
    image_analyzer_engine=ImageAnalyzerEngine(
        analyzer_engine=analyser, 
        image_preprocessor=ContrastSegmentedImageEnhancer(),
        ))
anonymised_dcm = anonymise_dicom.anonymise_image(dcm,score_threshold=score_threshold,
                                                 analyser=analyser,
                                                 image_redactor=image_redactor,
                                                 )
anonymised_dcm.save_as('/path/to/some/dicom_anon.dcm')

```

## De-identifying headers with the DICOM PS3.15 profile

By default `anonymise_image` scans the header values with the Presidio NER
pipeline (and GLiNER, when supplied). Passing `use_case="PS3.15"` instead
de-identifies the headers with the DICOM
[PS3.15 Annex E Basic Application Level Confidentiality Profile](https://dicom.nema.org/medical/dicom/current/output/chtml/part15/chapter_E.html).
This applies the standard's per-attribute actions (empty, dummy, remove, or
remap UIDs), records the de-identification in `DeidentificationMethod` /
`DeidentificationMethodCodeSequence`, and sets `PatientIdentityRemoved` to
`YES`. In this mode the NER engines are **not** run on the headers, so you do
not need to build an `analyser`.

```python
import pydicom as dicom
from phi_finder.dicom_tools import anonymise_dicom

path = "/path/to/some/dicom.dcm"
dcm = dicom.dcmread(path)
anonymised_dcm = anonymise_dicom.anonymise_image(dcm, use_case="PS3.15")
anonymised_dcm.save_as('/path/to/some/dicom_anon.dcm')
```

### Retain Patient Characteristics

Use `use_case="PS3.15_Rtn. Pat."` to apply the basic profile together with the
PS3.15 *Retain Patient Characteristics* Option. Direct identifiers (patient
name, birth date, etc.) are still removed, but patient characteristics such as
age, sex, size, weight, ethnic group and smoking status are kept. The retain
option is recorded in `DeidentificationMethodCodeSequence` (code `113108`).

```python
import pydicom as dicom
from phi_finder.dicom_tools import anonymise_dicom

path = "/path/to/some/dicom.dcm"
dcm = dicom.dcmread(path)
anonymised_dcm = anonymise_dicom.anonymise_image(dcm, use_case="PS3.15_Rtn. Pat.")
anonymised_dcm.save_as('/path/to/some/dicom_anon.dcm')
```

The `use_case` match is case-insensitive and tolerant of separator spelling, so
`"PS3.15"`, `"ps3.15"`, `"PS3_15"` and `"PS3-15"` all select the plain profile,
and `"PS3.15_Rtn. Pat."` or `"PS3.15 Retain Patient Characteristics"` select the
retain variant. Any other value (e.g. `"Standard"`, the default, or
`"Aggressive"`) falls back to the Presidio/GLiNER pipeline described above.

> **Note:** `use_case` only controls how the **headers** are handled. Burned-in
> pixel PHI is still redacted only when an `image_redactor` is passed, exactly
> as in the examples above.
