def test_jitter_timestamp(self): from deid.dicom.actions import jitter_timestamp dicom = get_dicom(self.dataset) print("Testing test_jitter_timestamp") print("Case 1: Testing jitter_timestamp with DICOM Date (DA)") dicom.StudyDate = "20131210" dicom.data_element("StudyDate").VR = "DA" jitter_timestamp(dicom, "StudyDate", 10) expected = "20131220" self.assertEqual(dicom.StudyDate, expected) print("Case 2: Testing with DICOM timestamp (DT)") dicom.AcquisitionDateTime = "20131210081530" dicom.data_element("AcquisitionDateTime").VR = "DT" jitter_timestamp(dicom, "AcquisitionDateTime", 10) expected = "20131220081530.000000" self.assertEqual(dicom.AcquisitionDateTime, expected) print("Case 3: Testing with non-standard DICOM date (DA)") dicom.StudyDate = "2013/12/10" dicom.data_element("StudyDate").VR = "DA" jitter_timestamp(dicom, "StudyDate", 10) expected = "20131220" self.assertEqual(dicom.StudyDate, expected) print("Case 4: Testing negative jitter value") dicom.StudyDate = "20131210" jitter_timestamp(dicom, "StudyDate", -5) expected = "20131205" self.assertEqual(dicom.StudyDate, expected) print("Case 5: Testing with empty field") dicom.StudyDate = expected = "" jitter_timestamp(dicom, "StudyDate", 10) self.assertEqual(dicom.StudyDate, expected) print("Case 6: Testing with nonexistent field") del dicom.StudyDate jitter_timestamp(dicom, "StudyDate", 10) self.assertTrue("StudyDate" not in dicom) print("Case 7: Testing JITTER recipe action") from deid.dicom.actions import perform_action dicom.StudyDate = "20131210" dicom.data_element("StudyDate").VR = "DA" JITTER = {"action": "JITTER", "field": "StudyDate", "value": "-5"} expected = "20131205" dicom = perform_action(dicom=dicom, action=JITTER) self.assertTrue(dicom.StudyDate, expected)
def replace_identifiers( dicom_files, ids=None, deid=None, save=True, overwrite=False, output_folder=None, force=True, config=None, strip_sequences=True, remove_private=True, ): """replace identifiers using pydicom, can be slow when writing and saving new files. If you want to replace sequences, they need to be extracted with get_identifiers and expand_sequences to True. """ dicom_files, recipe, config = _prepare_replace_config(dicom_files, deid=deid, config=config) # ids (a lookup) is not required ids = ids or {} # Parse through dicom files, update headers, and save updated_files = [] for _, dicom_file in enumerate(dicom_files): if isinstance(dicom_file, Dataset): dicom = dicom_file dicom_file = dicom.filename else: dicom = read_file(dicom_file, force=force) dicom_name = os.path.basename(dicom_file) # Remove sequences first, maintained in DataStore if strip_sequences is True: dicom = remove_sequences(dicom) # Remove private tags at the onset, if requested if remove_private: try: dicom.remove_private_tags() except: bot.error( """Private tags for %s could not be completely removed, usually this is due to invalid data type. Removing others.""" % dicom_name) private_tags = get_private(dicom) for ptag in private_tags: del dicom[ptag.tag] continue # Include private tags (if not removed) plus dicom.dir fields = dicom_dir(dicom) if recipe.deid is not None: if dicom_file not in ids: ids[dicom_file] = {} # Prepare additional lists of values and fields (updates item) if recipe.has_values_lists(): for group, actions in recipe.get_values_lists().items(): ids[dicom_file][group] = extract_values_list( dicom=dicom, actions=actions) if recipe.has_fields_lists(): for group, actions in recipe.get_fields_lists().items(): ids[dicom_file][group] = extract_fields_list( dicom=dicom, actions=actions) for action in recipe.get_actions(): dicom = perform_action(dicom=dicom, item=ids[dicom_file], action=action) # Next perform actions in default config, only if not done for action in config["put"]["actions"]: if action["field"] in fields: dicom = perform_action(dicom=dicom, action=action) # Assemble a new dataset, again accounting for private tags ds = Dataset() for field in dicom_dir(dicom): try: # Most fields are strings if isinstance(field, str): ds.add(dicom.data_element(field)) # Remainder are tags else: ds.add(dicom.get(field)) except: pass # Copy original data attributes attributes = [ "is_little_endian", "is_implicit_VR", "is_decompressed", "read_encoding", "read_implicit_vr", "read_little_endian", "_parent_encoding", ] # We aren't including preamble, we will reset to be empty 128 bytes ds.preamble = b"\0" * 128 for attribute in attributes: if hasattr(dicom, attribute): ds.__setattr__(attribute, dicom.__getattribute__(attribute)) # Original meta data # or default empty dataset file_metas = getattr(dicom, "file_meta", Dataset()) # Media Storage SOP Instance UID can be identifying if hasattr(file_metas, "MediaStorageSOPInstanceUID"): file_metas.MediaStorageSOPInstanceUID = "" # Save meta data ds.file_meta = file_metas # Save to file? if save is True: ds = save_dicom( dicom=ds, dicom_file=dicom_file, output_folder=output_folder, overwrite=overwrite, ) updated_files.append(ds) return updated_files
def test_parse_action(self): print("Test test_parse_action") from deid.dicom.actions import perform_action dicom = get_dicom(self.dataset) print("Case 1: Testing ADD action") self.assertTrue("PatientIdentityRemoved" not in dicom) ADD = { "action": "ADD", "field": "PatientIdentityRemoved", "value": "Yes" } dicom = perform_action(dicom=dicom, action=ADD) self.assertTrue("PatientIdentityRemoved" in dicom) self.assertEqual(dicom.get("PatientIdentityRemoved"), "Yes") print("Case 2: Testing REPLACE action with string") REPLACE = { "action": "REPLACE", "field": "PatientIdentityRemoved", "value": "No" } dicom = perform_action(dicom=dicom, action=REPLACE) self.assertTrue("PatientIdentityRemoved" in dicom) self.assertEqual(dicom.get("PatientIdentityRemoved"), "No") print("Case 3: Testing REPLACE action with variable") item = {"fish": "stick"} REPLACE = { "action": "REPLACE", "field": "PatientIdentityRemoved", "value": "var:fish" } dicom = perform_action(dicom=dicom, action=REPLACE, item=item) self.assertEqual(dicom.get("PatientIdentityRemoved"), "stick") print("Case 4: Testing REPLACE action with non-existing variable") REPLACE = { "action": "REPLACE", "field": "PatientIdentityRemoved", "value": "var:gummybear" } before = dicom.get("PatientIdentityRemoved") updated = perform_action(dicom=dicom, action=REPLACE, item=item) self.assertEqual(updated, updated) after = dicom.get("PatientIdentityRemoved") self.assertEqual(before, after) print("Case 5: Testing REMOVE action") REMOVE = {"action": "REMOVE", "field": "PatientIdentityRemoved"} dicom = perform_action(dicom=dicom, action=REMOVE) self.assertTrue("PatientIdentityRemoved" not in dicom) print("Case 6: Testing invalid action") RUN = {"action": "RUN", "field": "PatientIdentityRemoved"} updated = perform_action(dicom=dicom, action=RUN) self.assertEqual(updated, updated) print("Case 7: Testing function (func:) with action") ACTION = { "action": "REPLACE", "field": "PatientID", "value": "func:generate_uid" } # Here is the function we define to replace def generate_uid(item, value, field): return "pancakes" # The function must be in the item lookup item['generate_uid'] = generate_uid updated = perform_action(dicom=dicom, action=ACTION, item=item) self.assertEqual(updated.PatientID, "pancakes")
def test_parse_action(self): print("Test test_parse_action") from deid.dicom.actions import perform_action dicom = get_dicom(self.dataset) print("Case 1: Testing ADD action") self.assertTrue("PatientIdentityRemoved" not in dicom) ADD = { "action": "ADD", "field": "PatientIdentityRemoved", "value": "Yes" } dicom = perform_action(dicom=dicom, action=ADD) self.assertTrue("PatientIdentityRemoved" in dicom) self.assertEqual(dicom.get("PatientIdentityRemoved"), "Yes") print("Case 2: Testing REPLACE action with string") REPLACE = { "action": "REPLACE", "field": "PatientIdentityRemoved", "value": "No", } dicom = perform_action(dicom=dicom, action=REPLACE) self.assertTrue("PatientIdentityRemoved" in dicom) self.assertEqual(dicom.get("PatientIdentityRemoved"), "No") print("Case 3: Testing REPLACE action with variable") item = {"fish": "stick"} REPLACE = { "action": "REPLACE", "field": "PatientIdentityRemoved", "value": "var:fish", } dicom = perform_action(dicom=dicom, action=REPLACE, item=item) self.assertEqual(dicom.get("PatientIdentityRemoved"), "stick") print("Case 4: Testing REPLACE action with non-existing variable") REPLACE = { "action": "REPLACE", "field": "PatientIdentityRemoved", "value": "var:gummybear", } before = dicom.get("PatientIdentityRemoved") updated = perform_action(dicom=dicom, action=REPLACE, item=item) self.assertEqual(updated, updated) after = dicom.get("PatientIdentityRemoved") self.assertEqual(before, after) print("Case 5: Testing REMOVE action") REMOVE = {"action": "REMOVE", "field": "PatientIdentityRemoved"} dicom = perform_action(dicom=dicom, action=REMOVE) self.assertTrue("PatientIdentityRemoved" not in dicom) # Test a boolean remove, contains field stanford REMOVE = { "action": "REMOVE", "field": "ALL", "value": "func:is_stanford" } # Here is the function that returns a boolean to indicate if replace def is_stanford(item, value, field): return item.get(field) == "STANFORD" item = {"is_stanford": is_stanford} dicom = perform_action(dicom=dicom, action=REMOVE, item=item) self.assertTrue("InstitutionName" not in dicom) print("Case 6: Testing invalid action") RUN = {"action": "RUN", "field": "PatientIdentityRemoved"} updated = perform_action(dicom=dicom, action=RUN) self.assertEqual(updated, updated) print("Case 7: Testing function (func:) with action") ACTION = { "action": "REPLACE", "field": "PatientID", "value": "func:generate_uid", } # Here is the function we define to replace def generate_uid(item, value, field): return "pancakes" # The function must be in the item lookup item["generate_uid"] = generate_uid updated = perform_action(dicom=dicom, action=ACTION, item=item) self.assertEqual(updated.PatientID, "pancakes") # Test each of filters for contains, not contains, equals, etc. dicom = get_dicom(self.dataset) print("Testing contains, equals, and empty action with REMOVE") self.assertTrue("ReferringPhysicianName" in dicom) REMOVE = {"action": "REMOVE", "field": "ALL", "value": "contains:Dr."} dicom = perform_action(dicom=dicom, action=REMOVE) self.assertTrue("ReferringPhysicianName" not in dicom) self.assertTrue("InstitutionName" in dicom) REMOVE = { "action": "REMOVE", "field": "ALL", "value": "equals:STANFORD" } dicom = perform_action(dicom=dicom, action=REMOVE) self.assertTrue("InstitutionName" not in dicom) self.assertTrue("StudyID" in dicom) REMOVE = {"action": "REMOVE", "field": "ALL", "value": "empty"} dicom = perform_action(dicom=dicom, action=REMOVE) self.assertTrue("StudyID" not in dicom)