def deidentify_dicom_orthanc()

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