anonymize_dicom.py 5.1 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137
  1. #!/usr/bin/env python3
  2. """
  3. Anonymize DICOM files by removing or replacing Protected Health Information (PHI).
  4. Usage:
  5. python anonymize_dicom.py input.dcm output.dcm
  6. python anonymize_dicom.py input.dcm output.dcm --patient-id ANON001
  7. """
  8. import argparse
  9. import sys
  10. from pathlib import Path
  11. try:
  12. import pydicom
  13. except ImportError:
  14. print("Error: pydicom is not installed. Install it with: pip install pydicom")
  15. sys.exit(1)
  16. # Tags commonly containing PHI (Protected Health Information)
  17. PHI_TAGS = [
  18. 'PatientName', 'PatientID', 'PatientBirthDate', 'PatientBirthTime',
  19. 'PatientSex', 'PatientAge', 'PatientSize', 'PatientWeight',
  20. 'PatientAddress', 'PatientTelephoneNumbers', 'PatientMotherBirthName',
  21. 'MilitaryRank', 'EthnicGroup', 'Occupation', 'PatientComments',
  22. 'InstitutionName', 'InstitutionAddress', 'InstitutionalDepartmentName',
  23. 'ReferringPhysicianName', 'ReferringPhysicianAddress',
  24. 'ReferringPhysicianTelephoneNumbers', 'ReferringPhysicianIdentificationSequence',
  25. 'PerformingPhysicianName', 'PerformingPhysicianIdentificationSequence',
  26. 'OperatorsName', 'PhysiciansOfRecord', 'PhysiciansOfRecordIdentificationSequence',
  27. 'NameOfPhysiciansReadingStudy', 'PhysiciansReadingStudyIdentificationSequence',
  28. 'StudyDescription', 'SeriesDescription', 'AdmittingDiagnosesDescription',
  29. 'DerivationDescription', 'RequestingPhysician', 'RequestingService',
  30. 'RequestedProcedureDescription', 'ScheduledPerformingPhysicianName',
  31. 'PerformedLocation', 'PerformedStationName',
  32. ]
  33. def anonymize_dicom(input_path, output_path, patient_id='ANONYMOUS', patient_name='ANONYMOUS'):
  34. """
  35. Anonymize a DICOM file by removing or replacing PHI.
  36. Args:
  37. input_path: Path to input DICOM file
  38. output_path: Path to output anonymized DICOM file
  39. patient_id: Replacement patient ID (default: 'ANONYMOUS')
  40. patient_name: Replacement patient name (default: 'ANONYMOUS')
  41. """
  42. try:
  43. # Read DICOM file
  44. ds = pydicom.dcmread(input_path)
  45. # Track what was anonymized
  46. anonymized = []
  47. # Remove or replace sensitive data
  48. for tag in PHI_TAGS:
  49. if hasattr(ds, tag):
  50. if tag == 'PatientName':
  51. ds.PatientName = patient_name
  52. anonymized.append(f"{tag}: replaced with '{patient_name}'")
  53. elif tag == 'PatientID':
  54. ds.PatientID = patient_id
  55. anonymized.append(f"{tag}: replaced with '{patient_id}'")
  56. elif tag == 'PatientBirthDate':
  57. ds.PatientBirthDate = '19000101'
  58. anonymized.append(f"{tag}: replaced with '19000101'")
  59. else:
  60. delattr(ds, tag)
  61. anonymized.append(f"{tag}: removed")
  62. # Anonymize UIDs if present (optional - maintains referential integrity)
  63. # Uncomment if you want to anonymize UIDs as well
  64. # if hasattr(ds, 'StudyInstanceUID'):
  65. # ds.StudyInstanceUID = pydicom.uid.generate_uid()
  66. # if hasattr(ds, 'SeriesInstanceUID'):
  67. # ds.SeriesInstanceUID = pydicom.uid.generate_uid()
  68. # if hasattr(ds, 'SOPInstanceUID'):
  69. # ds.SOPInstanceUID = pydicom.uid.generate_uid()
  70. # Save anonymized file
  71. ds.save_as(output_path)
  72. return True, anonymized
  73. except Exception as e:
  74. return False, str(e)
  75. def main():
  76. parser = argparse.ArgumentParser(
  77. description='Anonymize DICOM files by removing or replacing PHI',
  78. formatter_class=argparse.RawDescriptionHelpFormatter,
  79. epilog="""
  80. Examples:
  81. python anonymize_dicom.py input.dcm output.dcm
  82. python anonymize_dicom.py input.dcm output.dcm --patient-id ANON001
  83. python anonymize_dicom.py input.dcm output.dcm --patient-id ANON001 --patient-name "Anonymous^Patient"
  84. """
  85. )
  86. parser.add_argument('input', type=str, help='Input DICOM file')
  87. parser.add_argument('output', type=str, help='Output anonymized DICOM file')
  88. parser.add_argument('--patient-id', type=str, default='ANONYMOUS',
  89. help='Replacement patient ID (default: ANONYMOUS)')
  90. parser.add_argument('--patient-name', type=str, default='ANONYMOUS',
  91. help='Replacement patient name (default: ANONYMOUS)')
  92. parser.add_argument('-v', '--verbose', action='store_true',
  93. help='Show detailed anonymization information')
  94. args = parser.parse_args()
  95. # Validate input file exists
  96. input_path = Path(args.input)
  97. if not input_path.exists():
  98. print(f"Error: Input file '{args.input}' not found")
  99. sys.exit(1)
  100. # Anonymize the file
  101. print(f"Anonymizing: {args.input}")
  102. success, result = anonymize_dicom(args.input, args.output,
  103. args.patient_id, args.patient_name)
  104. if success:
  105. print(f"✓ Successfully anonymized DICOM file: {args.output}")
  106. if args.verbose:
  107. print(f"\nAnonymized {len(result)} fields:")
  108. for item in result:
  109. print(f" - {item}")
  110. else:
  111. print(f"✗ Error: {result}")
  112. sys.exit(1)
  113. if __name__ == '__main__':
  114. main()