in python/de-identifier/research_pacs/de_identifier/main.py [0:0]
def deidentify_dicom_orthanc(instance_id, src_dicom, config, logs):
"""
De-identify a DICOM instance from in the source Orthanc server as follows:
- Retrieve the labels matching this Orthanc instance (`Labels` section of the configuration
file) and whether the DICOM should be de-identified and sent to the destination, or skipped
(`ScopeToForward` section of the config file)
- If the Orthanc instance is not skipped:
- Download from Orthanc a transcoded version of the DICOM file to "Explicit VR Little
Endian" if pixel data must be edited to mask burned-in annotations
- If OCR must be used to detect burned-in annotations, retrieve the pixel coordinates of
boxes that need to be masked
- Apply the transformation rules (`Transformations` section of the config file) and
retrieve the de-identified DICOM file
- Transcode the DICOM file if the target transfer syntax is not "Explicit VR Little Endian"
- Return the de-identified DICOM file
Args:
instance_id (str): Orthanc instance ID
src_dicom (bytes): Content of the DICOM file downloaded from Orthanc
config (dict): Configuration file
logs (dict): Dict where logs should be added
"""
# Create a DicomDeidentifier object and load the DICOM file
try:
deidentifier = DicomDeidentifier(config, client.db, client.db_mapping)
except Exception as e:
raise Exception(f'Failed to initialize the DicomDeidentifier - {e}')
# The function `load_dicom` returns a list of matching labels for this DICOM file, and whether
# the DICOM file should be discarded based on these tags
try:
labels, skipped = deidentifier.load_dicom(src_dicom)
logs.update({
'OriginalDICOMFile': {
'SOPInstanceUID': rpacs_dicom_util.get_sop_instance_uid(deidentifier.dicom),
'TransferSyntaxUID': rpacs_dicom_util.get_transfer_syntax_uid(deidentifier.dicom)
},
'MatchingLabels': labels,
'Skipped': skipped
})
except Exception as e:
raise Exception(f'Failed to load the original DICOM file in the DicomDeidentifier - {e}')
if skipped is True:
return None
# Log transformations to apply
transformations = deidentifier.get_transformations_to_apply()
logs.update({'TransformationsToApply': transformations})
# If burned-in annotations must be removed from the pixel data, the DICOM file must be in
# uncompressed and in Little Endian format. If needed, we use Orthanc to download a transcoded
# version of the DICOM file
need_transcode, src_transfer_syntax = deidentifier.is_transcoding_needed()
if need_transcode is True:
try:
src_dicom = client.src_orthanc.download_instance_dicom(instance_id, transcode='1.2.840.10008.1.2.1')
deidentifier.load_dicom(src_dicom, src_transfer_syntax, initial_load=False)
except Exception as e:
raise Exception(f'Failed to transcode the original DICOM file to "Explicit VR Little Endian" with Orthanc in order to alter pixel data - {e}')
# If OCR must be used to detect burned-in annotations in pixel data
if deidentifier.is_ocr_needed():
# Find box coordinates with burned-in annotations and update the transformations rules
try:
dimensions = rpacs_dicom_util.get_dimensions(deidentifier.dicom)
boxes = get_box_coordinates(client.src_orthanc, instance_id, env.rekognition_region, dimensions)
deidentifier.add_box_coordinates(boxes)
except Exception as e:
raise Exception(f'Failed to find burned-in annotations in the original DICOM file with OCR - {e}')
# Apply the transformation rules
try:
dst_transfer_syntax = deidentifier.apply_transformations(logs)
except Exception as e:
raise Exception(f'Failed to apply the transformation rules to the original DICOM file - {e}')
# `apply_transformations` returns a transfer syntax if the de-identified DICOM file must be
# transcoded, or `None` if it should be sent as-is to the destination Orthanc server. If it must
# be transcoded, we upload the de-identified DICOM file to the source Orthanc server, and we
# download and return a transcoded version
dst_dicom = rpacs_dicom_util.export_dicom(deidentifier.dicom)
if dst_transfer_syntax != None:
try:
# Temporarily change the SOP Instance ID before uploading the de-identified DICOM file to
# Orthanc in order to prevent the original DICOM file
src_sop_instance_uid = rpacs_dicom_util.get_sop_instance_uid(deidentifier.dicom)
rpacs_dicom_util.set_sop_instance_uid(deidentifier.dicom)
dst_dicom = rpacs_dicom_util.export_dicom(deidentifier.dicom)
# Upload the de-identified DICOM file to Orthanc. This new DICOM file should be ignored by the
# de-identifier
tmp_instance_id = client.src_orthanc.upload_instance(dst_dicom)
client.db_msg.upsert(tmp_instance_id, {'Skip': True})
# Download a transcoded version of the de-identified DICOM file, and we restore the SOP
# Instance ID to its original value
dst_dicom = client.src_orthanc.download_instance_dicom(tmp_instance_id, transcode=dst_transfer_syntax)
deidentifier.load_dicom(dst_dicom, initial_load=False)
rpacs_dicom_util.set_sop_instance_uid(deidentifier.dicom, src_sop_instance_uid)
dst_dicom = rpacs_dicom_util.export_dicom(deidentifier.dicom)
except Exception as e:
raise Exception(f'Failed to transcode the de-identified DICOM file to "{dst_transfer_syntax}" - {e}')
return dst_dicom