DICOM Basics using Java - Anonymization and De-identification

Introduction

This article is part of my series of articles on the DICOM standard. If you are totally new to DICOM, please have a quick look at my earlier article titled "Introduction to the DICOM Standard" for a quick introduction to the standard. In this tutorial, we will explore DICOM anonymization (also known as de-identification), which is the process of removing or replacing Protected Health Information (PHI) from DICOM objects to enable sharing data for research, teaching, or publication while protecting patient privacy.

What is DICOM Anonymization?

DICOM anonymization is the process of removing or modifying patient-identifying information from medical images and associated metadata. This is essential when:

The Theory Behind De-identification

De-identification operates at the intersection of privacy law, information theory, and medical ethics. The fundamental challenge is that medical images are simultaneously protected health information (PHI) and scientifically valuable data. Solving this tension requires understanding what makes data "identifiable."

The Re-identification Risk Model

Privacy researchers have demonstrated that even "anonymous" data can often be re-identified through linkage attacks. The risk depends on:

  • Direct Identifiers: Data that uniquely identifies (name, SSN, MRN)
  • Quasi-Identifiers: Data that can identify when combined (ZIP code + birth date + gender)
  • Sensitive Attributes: The protected information itself (diagnosis, images)

Research by Latanya Sweeney showed that 87% of the U.S. population can be uniquely identified by just ZIP code, birth date, and gender - all commonly found in DICOM headers. This demonstrates why simple removal of "obvious" identifiers is insufficient.

HIPAA's Two De-identification Standards

The U.S. HIPAA Privacy Rule provides two paths to de-identification:

  1. Expert Determination (§164.514(b)(1)): A qualified statistical expert certifies that the risk of re-identification is "very small"
  2. Safe Harbor (§164.514(b)(2)): Remove 18 specific categories of identifiers, with no actual knowledge of remaining re-identification risk

DICOM PS3.15 Annex E's Basic Application Level Confidentiality Profile implements a Safe Harbor-compliant approach, but understanding the underlying risk model helps when making decisions about optional retention profiles.

The UID Linkage Problem

DICOM UIDs present a subtle re-identification risk. While UIDs don't directly contain patient information, they create linkage paths:

  • Original Study UID → Referenced in hospital billing records → Patient identity
  • Original SOP Instance UID → Stored in research database with known patient → Re-identification
  • Frame of Reference UID → Links to radiation therapy records → Patient identity

This is why proper de-identification must replace UIDs with newly generated values while maintaining internal consistency (all references within the anonymized dataset use the same replacement UIDs).

The Burned-in Annotation Challenge

Perhaps the most challenging aspect of DICOM de-identification is burned-in text. Patient names, MRNs, and dates are often rendered directly into pixel data by acquisition systems - visible on the image but invisible to attribute-level processing. Detecting and redacting this PHI requires:

  • OCR-based Detection: Using optical character recognition to find text regions
  • Pattern Matching: Identifying date formats, MRN patterns in detected text
  • Region Redaction: Blackening or removing identified PHI regions
  • Manual Review: Human verification for high-risk datasets

The DICOM attribute (0028,0301) Burned In Annotation flags when pixel data may contain PHI, but its absence doesn't guarantee safety - many older systems don't set this flag correctly.

DICOM De-identification Continues Below

  • Sharing medical images for research purposes
  • Using images for educational or training materials
  • Publishing case studies in medical literature
  • Developing and testing medical imaging software
  • Creating demonstration datasets

The DICOM standard defines guidelines for de-identification in DICOM PS3.15 Annex E, which specifies different profiles and options for handling various types of information.

Categories of Protected Health Information in DICOM

PHI in DICOM files can be categorized as follows:

  • Direct Identifiers: Patient name, ID, address, phone number, SSN
  • Indirect Identifiers: Dates, ages, locations
  • Embedded Data: Burned-in annotations, structured report text
  • UIDs: Unique identifiers that can potentially be used for re-identification

Tools for Tutorial

  • JDK 1.8 SDK or higher
  • Eclipse or any other Java IDE (or even a text editor)
  • Download the PixelMed library from here
  • You can also find the source code used in this tutorial on GitHub

“Privacy is not something that I’m merely entitled to, it’s an absolute prerequisite.” ~ Marlon Brando

DICOM Basic Profile Actions

The DICOM standard defines several actions for de-identification:

  • D - Replace with a dummy value
  • Z - Replace with a zero-length value
  • X - Remove the attribute entirely
  • K - Keep (may be retained)
  • C - Clean (remove if contains PHI)
  • U - Replace UID with a new one

Example: DICOM Anonymization using Java

Here is a comprehensive example showing how to anonymize a DICOM file using the PixelMed Java library:

package com.saravanansubramanian.dicom.pixelmedtutorial;

import java.util.HashMap;
import java.util.Map;

import com.pixelmed.dicom.Attribute;
import com.pixelmed.dicom.AttributeList;
import com.pixelmed.dicom.AttributeTag;
import com.pixelmed.dicom.CodeStringAttribute;
import com.pixelmed.dicom.FileMetaInformation;
import com.pixelmed.dicom.LongStringAttribute;
import com.pixelmed.dicom.PersonNameAttribute;
import com.pixelmed.dicom.ShortStringAttribute;
import com.pixelmed.dicom.TagFromName;
import com.pixelmed.dicom.TransferSyntax;
import com.pixelmed.dicom.UniqueIdentifierAttribute;

/**
 * DICOM Anonymization/De-identification Demo
 */
public class DicomAnonymizationDemo {

    // Map to maintain consistent UID replacement
    private static Map<String, String> uidMap = new HashMap<>();

    public static void main(String[] args) {
        try {
            System.out.println("=== DICOM Anonymization Demo ===\n");

            String inputFile = "C:\\path\\to\\original.dcm";
            String outputFile = "C:\\temp\\anonymized.dcm";

            // Read the DICOM file
            AttributeList list = new AttributeList();
            list.read(inputFile);

            System.out.println("Original values:");
            printPHI(list);

            // Perform anonymization
            System.out.println("\nAnonymizing...");
            anonymizeBasicProfile(list);

            System.out.println("\nAnonymized values:");
            printPHI(list);

            // Update file meta information and save
            list.removeMetaInformationHeaderAttributes();
            FileMetaInformation.addFileMetaInformation(list,
                TransferSyntax.ExplicitVRLittleEndian, "ANONYMIZER");
            list.write(outputFile);

            System.out.println("\nAnonymized file saved to: " + outputFile);

        } catch (Exception e) {
            System.out.println("Error: " + e.getMessage());
        }
    }

    /**
     * Anonymize using DICOM Basic Profile
     */
    private static void anonymizeBasicProfile(AttributeList list) throws Exception {

        // === Direct Identifiers - Remove or Replace ===

        // Patient Name
        replaceAttribute(list, TagFromName.PatientName,
            new PersonNameAttribute(TagFromName.PatientName), "ANONYMOUS");

        // Patient ID
        replaceAttribute(list, TagFromName.PatientID,
            new LongStringAttribute(TagFromName.PatientID), generateAnonymousID());

        // Patient Birth Date - Remove
        list.remove(TagFromName.PatientBirthDate);

        // Patient Address
        list.remove(TagFromName.PatientAddress);

        // Patient Telephone Numbers
        list.remove(TagFromName.PatientTelephoneNumbers);

        // Other Patient IDs
        list.remove(TagFromName.OtherPatientIDs);
        list.remove(TagFromName.OtherPatientIDsSequence);

        // === UIDs - Replace with new UIDs ===

        replaceUID(list, TagFromName.StudyInstanceUID);
        replaceUID(list, TagFromName.SeriesInstanceUID);
        replaceUID(list, TagFromName.SOPInstanceUID);
        replaceUID(list, TagFromName.FrameOfReferenceUID);

        // === Institution/Device Information ===

        replaceAttribute(list, TagFromName.InstitutionName,
            new LongStringAttribute(TagFromName.InstitutionName), "ANONYMOUS INSTITUTION");
        list.remove(TagFromName.InstitutionAddress);

        replaceAttribute(list, TagFromName.StationName,
            new ShortStringAttribute(TagFromName.StationName), "ANON_STATION");
        list.remove(TagFromName.DeviceSerialNumber);

        // === Physician Information ===

        replaceAttribute(list, TagFromName.ReferringPhysicianName,
            new PersonNameAttribute(TagFromName.ReferringPhysicianName), "ANONYMOUS^PHYSICIAN");
        list.remove(TagFromName.PerformingPhysicianName);
        list.remove(TagFromName.OperatorsName);
        list.remove(TagFromName.RequestingPhysician);

        // === Accession Number ===
        replaceAttribute(list, TagFromName.AccessionNumber,
            new ShortStringAttribute(TagFromName.AccessionNumber), generateAnonymousAccession());

        // === Remove Private Tags (may contain PHI) ===
        list.removePrivateAttributes();

        // === Add De-identification Tags ===
        Attribute deidentMethod = new LongStringAttribute(TagFromName.DeidentificationMethod);
        deidentMethod.addValue("Basic Profile");
        list.put(deidentMethod);

        Attribute patientIdentityRemoved = new CodeStringAttribute(TagFromName.PatientIdentityRemoved);
        patientIdentityRemoved.addValue("YES");
        list.put(patientIdentityRemoved);
    }

    /**
     * Replace a UID with a new one, maintaining mapping for consistency
     */
    private static void replaceUID(AttributeList list, AttributeTag tag) throws Exception {
        Attribute attr = list.get(tag);
        if (attr != null) {
            String originalUID = attr.getSingleStringValueOrNull();
            if (originalUID != null) {
                String newUID = uidMap.get(originalUID);
                if (newUID == null) {
                    newUID = UniqueIdentifierAttribute.createUID();
                    uidMap.put(originalUID, newUID);
                }

                Attribute newAttr = new UniqueIdentifierAttribute(tag);
                newAttr.addValue(newUID);
                list.put(newAttr);
            }
        }
    }

    /**
     * Replace an attribute with a new value
     */
    private static void replaceAttribute(AttributeList list, AttributeTag tag,
            Attribute newAttr, String value) throws Exception {
        if (list.get(tag) != null) {
            newAttr.addValue(value);
            list.put(newAttr);
        }
    }

    private static String generateAnonymousID() {
        return "ANON" + System.currentTimeMillis();
    }

    private static String generateAnonymousAccession() {
        return "ACC" + System.currentTimeMillis();
    }

    private static void printPHI(AttributeList list) {
        printAttribute(list, "Patient Name", TagFromName.PatientName);
        printAttribute(list, "Patient ID", TagFromName.PatientID);
        printAttribute(list, "Birth Date", TagFromName.PatientBirthDate);
        printAttribute(list, "Institution", TagFromName.InstitutionName);
        printAttribute(list, "Accession #", TagFromName.AccessionNumber);
        printAttribute(list, "Study UID", TagFromName.StudyInstanceUID);
    }

    private static void printAttribute(AttributeList list, String label, AttributeTag tag) {
        Attribute attr = list.get(tag);
        String value = (attr != null) ? attr.getSingleStringValueOrNull() : "(not present)";
        System.out.println("  " + label + ": " + value);
    }
}

“Data is a precious thing and will last longer than the systems themselves.” ~ Tim Berners-Lee

De-identification Options

The DICOM standard provides several options that can be combined with the Basic Profile:

Retain Longitudinal Temporal Information

  • Keep dates but shift by a consistent offset
  • Useful for research tracking patients over time

Retain Patient Characteristics

  • Keep age, sex, ethnicity
  • Useful for demographic analysis

Retain Device Identity

  • Keep device/manufacturer information
  • Useful for equipment studies

Retain UIDs

  • Keep original UIDs
  • Risk: can be used for re-identification

Important: Burned-in Annotations

One critical aspect of DICOM anonymization is handling burned-in annotations. The attribute (0028,0301) Burned In Annotation indicates whether the pixel data contains text or graphics that could identify the patient. If this attribute is set to "YES", the image may contain PHI that cannot be removed through attribute-level de-identification alone.

Options for handling burned-in annotations include:

  • Excluding such images from the anonymized dataset
  • Using OCR detection to identify and mask PHI
  • Manual review and redaction

Verification Checklist

After anonymization, verify the following:

  1. No PHI remains in standard DICOM attributes
  2. Private tags have been removed or cleaned
  3. Pixel data is free of burned-in PHI
  4. UIDs have been consistently replaced
  5. De-identification method is documented in the file

Conclusion

DICOM anonymization is a critical process for protecting patient privacy while enabling the sharing of medical images for research, education, and software development. The DICOM standard provides comprehensive guidelines for de-identification, and tools like PixelMed make it straightforward to implement these guidelines in Java applications. Always verify anonymized data carefully and follow your institution's IRB guidelines when working with patient data. In the next tutorial in this series, I will cover DICOM digital signatures for ensuring data integrity and authentication. See you then!