Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[DICOM deidentifier] Removing deprecated dcmtk methods #103

Merged
merged 2 commits into from
Aug 28, 2018
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
14 changes: 8 additions & 6 deletions dicat/dicom_anonymizer_frame.py
Original file line number Diff line number Diff line change
Expand Up @@ -23,12 +23,14 @@ def __init__(self, parent):
self.field_dict = {}
self.message = StringVar()

# Determine which de-identifier tool to use (PyDICOM or DICOM toolkit) before
# starting the program.
# Determine if PyDICOM python library is present.
deidentifier_tool = methods.find_deidentifier_tool()
if deidentifier_tool:
error = "ERROR: no tool was found to read or de-identify DICOM files. \n " + \
"Please make sure PyDICOM has been properly installed."
if not deidentifier_tool:
error = "ERROR: PyDICOM does not appear to be installed.\n " \
+ "Please make sure PyDICOM has been properly installed " \
+ "before using the DICOM deidentifier tab.\n " \
+ "Check the README.md of the DICAT repository for " \
+ "information on how to install PyDICOM."
self.message.set(error)

self.initialize()
Expand Down Expand Up @@ -86,7 +88,7 @@ def initialize(self):
padx=(0, 10),
sticky=E + W
)
if not self.message.get():
if self.message.get():
# if error message is set due to not finding the tool, show the error on the screen
self.messageView.configure(fg="dark red",
font="Helvetica 16 bold italic"
Expand Down
150 changes: 21 additions & 129 deletions dicat/lib/dicom_anonymizer_methods.py
Original file line number Diff line number Diff line change
Expand Up @@ -22,56 +22,30 @@

use_pydicom = True # set to true as PyDICOM was found and imported
except ImportError:
try: # try importing newer versions of PyDICOM
try: # try importing older versions of PyDICOM
import dicom

use_pydicom = True # set to true as PyDICOM was found and imported
except ImportError:
use_pydicom = False # set to false as PyDICOM was not found
from dicom.errors import InvalidDicomError

if use_pydicom:
from pydicom.errors import InvalidDicomError

def find_deidentifier_tool():
"""
Determine which de-identifier tool will be used by the program:
- PyDICOM python module if found and imported
- DICOM toolkit if found on the filesystem
Determine if the PyDICOM python module is present and imported.

:param: None

:return: tool to use for DICOM de-identification
:rtype: object

"""

if use_pydicom:
# PyDICOM will be used and returned if PyDICOM was found
return 'PyDICOM'
elif test_executable('dcmdump'):
# DICOM toolkit will be used if dcmdump executable exists
return 'DICOM_toolkit'
else:
# Return False if no tool was found to read and de-identify DICOMs
return False


def test_executable(executable):
"""
Test if an executable exists.
Returns True if executable exists, False if not found.

:param executable: executable to test
:type executable: str

:return: return True if executable was found, False otherwise
:return: True if PyDICOM was found, False otherwise
:rtype: bool

"""

# try running the executable
try:
subprocess.call([executable], stdout=open(os.devnull, 'wb'))
if use_pydicom:
return True
except OSError:
else:
return False


Expand Down Expand Up @@ -120,18 +94,12 @@ def is_file_a_dicom(file):

"""

if use_pydicom:
try:
dicom.read_file(file)
except InvalidDicomError:
return False
return True
else:
try:
os.system("dcmdump" + file)
except IOError:
return False
return True
try:
dicom.read_file(file)
except InvalidDicomError:
return False
return True




Expand Down Expand Up @@ -183,11 +151,8 @@ def grep_dicom_values(dicom_folder, dicom_fields):
# Grep the first DICOM to read field information
dicom_file = dicoms_list[0]

# Read DICOM file using PyDICOM or the DICOM tool kit
if (use_pydicom):
(dicom_fields) = read_dicom_with_pydicom(dicom_file, dicom_fields)
else:
(dicom_fields) = read_dicom_with_dcmdump(dicom_file, dicom_fields)
# Read DICOM file using PyDICOM
(dicom_fields) = read_dicom_with_pydicom(dicom_file, dicom_fields)

return dicom_fields

Expand Down Expand Up @@ -223,36 +188,9 @@ def read_dicom_with_pydicom(dicom_file, dicom_fields):
return dicom_fields


def read_dicom_with_dcmdump(dicom_file, dicom_fields):
"""
Read DICOM file using dcmdump from the DICOM toolkit.

:param dicom_file: DICOM file to read
:type dicom_file: str
:param dicom_fields: Dictionary containing DICOM fields and values
:type dicom_fields: dict

:return: updated dictionary of DICOM fields and values
:rtype : dict

"""

# Grep information from DICOM header and store them
# into dicom_fields dictionary under flag Value
for name in dicom_fields:
dump_cmd = "dcmdump -ml +P \"" + name + "\" -q \"" + dicom_file + "\""
result = subprocess.check_output(dump_cmd, shell=True)
tmp_val = re.match(".+\[(.+)\].+", result)
if tmp_val:
value = tmp_val.group(1)
dicom_fields[name]['Value'] = value

return dicom_fields


def dicom_zapping(dicom_folder, dicom_fields):
"""
Run dcmodify on all fields to zap using PyDICOM recursive wrapper
Zap DICOM fields using PyDICOM recursive wrapper

:param dicom_folder: folder with DICOMs
:type dicom_folder: str
Expand Down Expand Up @@ -286,19 +224,10 @@ def dicom_zapping(dicom_folder, dicom_fields):
original_dcm = dicom.replace(dicom_folder, original_dir)
# Move DICOM files from root folder to de-identified folder created
shutil.move(dicom, deidentified_dcm)
if use_pydicom:
# copy files from original folder to de-identified folder
shutil.copy(deidentified_dcm, original_dcm)
# Zap the DICOM fields from DICOM file using PyDICOM
pydicom_zapping(deidentified_dcm, dicom_fields)
else:
# Zap the DICOM fields from DICOM file using dcmodify
dcmodify_zapping(deidentified_dcm, dicom_fields)
# Grep the .bak files created by dcmdump and move it to original
# DICOM folder
orig_bak_dcm = deidentified_dcm + ".bak"
if os.path.exists(orig_bak_dcm):
shutil.move(orig_bak_dcm, original_dcm)
# copy files from original folder to de-identified folder
shutil.copy(deidentified_dcm, original_dcm)
# Zap the DICOM fields from DICOM file using PyDICOM
pydicom_zapping(deidentified_dcm, dicom_fields)

# Zip the de-identified and original DICOM folders
(deidentified_zip, original_zip) = zip_dicom_directories(deidentified_dir,
Expand Down Expand Up @@ -346,43 +275,6 @@ def pydicom_zapping(dicom_file, dicom_fields):
dicom_dataset.save_as(dicom_file)


def dcmodify_zapping(dicom_file, dicom_fields):
"""
Run dcmodify on all DICOM fields to zap.

:param dicom_file: DICOM file to zap
:type dicom_file: str
:param dicom_fields: dictionary of DICOM fields and values
:type dicom_fields: dict

:returns:
original_zip -> Path to the zip file containing original DICOM files
deidentified_zip -> Path to the zip file containing de-identified DICOM files
:rtype: str

"""

# Initialize the dcmodify command
modify_cmd = "dcmodify "
changed_fields_nb = 0
for name in dicom_fields:
# Grep the new values
new_val = ""
if 'Value' in dicom_fields[name]:
new_val = dicom_fields[name]['Value']

# Run dcmodify if update is set to True
if not dicom_fields[name]['Editable'] and 'Value' in dicom_fields[name]:
modify_cmd += " -ma \"(" + name + ")\"=\" \" "
changed_fields_nb += 1
else:
if dicom_fields[name]['Update'] == True:
modify_cmd += " -ma \"(" + name + ")\"=\"" + new_val + "\" "
changed_fields_nb += 1
modify_cmd += " \"" + dicom_file + "\" "
subprocess.call(modify_cmd, shell=True)


def zip_dicom_directories(deidentified_dir, original_dir, subdirs_list, root_dir):
"""
Zip the de-identified and origin DICOM directories.
Expand Down