|
| 1 | +# Automatic Pixel Data De-identification |
| 2 | + |
| 3 | +Karnak can automatically detect and mask sensitive patient data (PHI) that is |
| 4 | +*burned into* the DICOM pixel data (text overlays such as patient name, |
| 5 | +accession number, dates, etc.). Instead of defining mask rectangles by hand per |
| 6 | +station name, Karnak sends each image to an external **de-identification image |
| 7 | +API** that runs OCR, detects sensitive text, and returns the mask areas to apply. |
| 8 | + |
| 9 | +This feature is an option of the **Clean Pixel Data** profile element. |
| 10 | + |
| 11 | +--- |
| 12 | + |
| 13 | +## How it works |
| 14 | + |
| 15 | +1. A destination uses a de-identification profile that contains a **Clean Pixel |
| 16 | + Data** element with automatic mask generation enabled. |
| 17 | +2. For every eligible instance (US, Secondary Capture, XC SOP classes, or any |
| 18 | + image flagged `BurnedInAnnotation`), Karnak extracts the pixel data and a set |
| 19 | + of sensitive tag values and sends them to the external API. |
| 20 | +3. The API returns one or more mask areas (rectangles, each with a color). |
| 21 | + Karnak draws them on the image before forwarding it to the destination. |
| 22 | +4. If the API detects no sensitive data, no mask is applied and the image is |
| 23 | + forwarded unchanged. |
| 24 | + |
| 25 | +### Sensitive tags sent to the API |
| 26 | + |
| 27 | +The values of the following tags are sent so the API can detect them in the |
| 28 | +image. No pixel data leaves Karnak beyond the single frame being analysed. |
| 29 | + |
| 30 | +`AccessionNumber`, `InstitutionName`, `OperatorsName`, `PatientAge`, |
| 31 | +`PatientBirthDate`, `PatientID`, `PatientName`, `PatientSex`, |
| 32 | +`PerformingPhysicianName`, `PerformedProcedureStepID`, |
| 33 | +`ReferringPhysicianName`, `StudyDate`. |
| 34 | + |
| 35 | +### Fail-closed behavior |
| 36 | + |
| 37 | +When automatic mask generation is enabled and the API is **unreachable or |
| 38 | +returns an error**, Karnak produces **no mask**, and the instance is **not |
| 39 | +forwarded**. This prevents un-masked PHI from leaking to the destination if the |
| 40 | +service is down. Check the Karnak logs for the cause (client error, server |
| 41 | +error, or unreachable service). |
| 42 | + |
| 43 | +> Note: when automatic mask generation is enabled, the statically configured |
| 44 | +> station-name masks are **not** used. Manual masks only apply when automatic |
| 45 | +> generation is disabled. |
| 46 | +
|
| 47 | +--- |
| 48 | + |
| 49 | +## Enabling the option in a profile |
| 50 | + |
| 51 | +### Via the UI |
| 52 | + |
| 53 | +1. Open the profile editor and add (or edit) a **Clean Pixel Data** element. |
| 54 | +2. Enable **Automatic masks generation**. |
| 55 | +3. Make sure the profile also contains the **DICOM basic profile** element. |
| 56 | +4. Save and assign the profile to the destination's de-identification project. |
| 57 | + |
| 58 | +### Via a YAML profile |
| 59 | + |
| 60 | +Set the `automaticMasksGeneration` argument to `"true"` on the |
| 61 | +`clean.pixel.data` element: |
| 62 | + |
| 63 | +```yaml |
| 64 | +name: "Automatic Deidentification Karnak Profile" |
| 65 | +version: "1.0" |
| 66 | +minimumKarnakVersion: "0.9.2" |
| 67 | +profileElements: |
| 68 | +- name: "Clean pixel data" |
| 69 | + codename: "clean.pixel.data" |
| 70 | + arguments: |
| 71 | + automaticMasksGeneration: "true" |
| 72 | +- name: "DICOM basic profile" |
| 73 | + codename: "basic.dicom.profile" |
| 74 | +``` |
| 75 | +
|
| 76 | +Reference profiles are provided under |
| 77 | +[`profils/`](../profils): `Automatic-Deidentification-Karnak-Profile.yml` and |
| 78 | +`Automatic-Deidentification-No-Mask-Karnak-Profile.yml`. |
| 79 | + |
| 80 | +--- |
| 81 | + |
| 82 | +## Configuring the external API endpoint |
| 83 | + |
| 84 | +Karnak calls the de-identification image API at the URL configured by the |
| 85 | +`DEIDENTIFY_IMAGE_URL` environment variable (default |
| 86 | +`http://localhost:8000`). It maps to the `karnak.deidentify-image.url` property |
| 87 | +in `application.yml`: |
| 88 | + |
| 89 | +```yaml |
| 90 | +karnak: |
| 91 | + deidentify-image: |
| 92 | + url: ${DEIDENTIFY_IMAGE_URL:http://localhost:8000} |
| 93 | +``` |
| 94 | + |
| 95 | +| Variable | Default | Description | |
| 96 | +|----------|---------|-------------| |
| 97 | +| `DEIDENTIFY_IMAGE_URL` | `http://localhost:8000` | Base URL of the de-identification image API. | |
| 98 | + |
| 99 | +The API contract is documented in |
| 100 | +[`src/main/resources/deidentification-api.yaml`](../src/main/resources/deidentification-api.yaml). |
| 101 | + |
| 102 | +--- |
| 103 | + |
| 104 | +## Deploying the external de-identification service |
| 105 | + |
| 106 | +The de-identification image API is a separate service. Deploy and run it so that |
| 107 | +it is reachable from Karnak at `DEIDENTIFY_IMAGE_URL`. |
| 108 | + |
| 109 | +> Deployment guide of the de-identification image API: <https://github.com/nroduit/image-ocr-identifier> |
| 110 | + |
| 111 | +### Standard deployment |
| 112 | + |
| 113 | +1. Deploy the de-identification image API (see the repository linked above). |
| 114 | +2. Point Karnak to it by setting `DEIDENTIFY_IMAGE_URL` to the service base URL. |
| 115 | +3. Restart Karnak so the new configuration is picked up. |
| 116 | + |
| 117 | +### Portable build |
| 118 | + |
| 119 | +The portable package can start the de-identification service as a **sidecar** |
| 120 | +alongside Karnak. It is controlled in `run.cfg`: |
| 121 | + |
| 122 | +```sh |
| 123 | +### Image Deidentification |
| 124 | +DEIDENTIFY_IMAGE_ENABLED=true |
| 125 | +DEIDENTIFY_IMAGE_URL=http://localhost:8000 |
| 126 | +``` |
| 127 | + |
| 128 | +| Variable | Default | Description | |
| 129 | +|----------|---------|-------------| |
| 130 | +| `DEIDENTIFY_IMAGE_ENABLED` | `true` | Start the bundled de-identification sidecar with the portable package. Set to `false` to manage the service yourself. | |
| 131 | +| `DEIDENTIFY_IMAGE_URL` | `http://localhost:8000` | Base URL Karnak uses to reach the service. | |
| 132 | + |
| 133 | +When enabled, `run.sh` starts the bundled binary |
| 134 | +(`deidentify-karnak/deidentify-karnak`) on launch and stops it on shutdown. If |
| 135 | +the binary is missing, the step is skipped and a message is logged. |
| 136 | + |
| 137 | +--- |
| 138 | + |
| 139 | +## Troubleshooting |
| 140 | + |
| 141 | +| Symptom | Likely cause | Action | |
| 142 | +|---------|--------------|--------| |
| 143 | +| Images with automatic masking are not forwarded | API unreachable or returning errors | Check the API is running and reachable at `DEIDENTIFY_IMAGE_URL`; inspect Karnak logs. | |
| 144 | +| `Cannot reach de-identification image API ...` in logs | Wrong URL or service down | Verify `DEIDENTIFY_IMAGE_URL` and service health. | |
| 145 | +| `SOP Instance UID ... does not match` in logs | API returned a response for a different instance | Verify the API version and that it echoes back the request `sop_instance_uid`. | |
| 146 | +| No mask applied although PHI is visible | API detected no sensitive text, or the tag values are absent from the metadata | Confirm the sensitive tags are populated in the source metadata. | |
| 147 | + |
0 commit comments