Source code for utils.dcm_qt_tree
# dcm_qt_tree.py
"""View DICOM files in a tree using Qt and PySide"""
# Copyright (c) 2013 Padraig Looney
# This file is released under the pydicom (https://github.com/darcymason/pydicom)
# license, see the file license.txt available at
# (https://github.com/darcymason/pydicom)
import dicom
import sys
try:
from PySide import QtGui
except ImportError:
#print('Pyside not getting imported')
pass
import collections
[docs]class DicomTree(object):
def __init__(self, filename, model):
"""
Constructor for Dicom Tree
"""
self.filename = filename
self.model = model
self.title = ""
self.patient_info = []
self.plan_info = []
self.pixel_array = None
self.patient_dict = {"Patient's Name":"Name", "Patient ID":"ID", "Patient's Birth Date":"Birth Date", "Patient's Sex":"Sex"}
[docs] def get_patient_table(self):
"""
Creating patient information table
"""
tr = ""
for key, value in self.patient_info:
tr += ("<tr><th align='left'>"+key+": </th><td align='left'>"+value+"</td></tr>")
table = "<table style='width:100%;' cellspacing='2' cellpadding='2'>"+tr+"</table>"
return table
[docs] def get_plan_table(self):
"""
Creating plan information table
"""
tr = ""
for key, value in self.plan_info:
tr += ("<tr><th align='left'>"+key+": </th><td align='left'>"+str(value)+"</td></tr>")
table = "<table style='width:100%;' cellspacing='2' cellpadding='2'>"+tr+"</table>"
return table
[docs] def show_tree(self):
"""
Retrive all dicom data and assign it to tree model structure
"""
ds = self.dicom_to_dataset(self.filename)
try:
self.pixel_array = ds.pixel_array
except:
pass
ds = self.anonymize(ds)
dic = self.dataset_to_dic(ds)
self.dic_to_model(dic)
return self.model
#self.display(model)
[docs] def anonymize(self, dataset, new_person_name="anonymous",
new_patient_id="id", remove_curves=True, remove_private_tags=True):
"""Replace data element values to partly anonymize a DICOM file.
Note: completely anonymizing a DICOM file is very complicated; there
are many things this example code does not address. USE AT YOUR OWN RISK.
"""
# Define call-back functions for the dataset.walk() function
def PN_callback(ds, data_element):
"""Called from the dataset "walk" recursive function for all data elements."""
if data_element.VR == "PN":
data_element.value = new_person_name
def curves_callback(ds, data_element):
"""Called from the dataset "walk" recursive function for all data elements."""
if data_element.tag.group & 0xFF00 == 0x5000:
del ds[data_element.tag]
# Remove patient name and any other person names
dataset.walk(PN_callback)
# Change ID
dataset.PatientID = new_patient_id
# Remove data elements (should only do so if DICOM type 3 optional)
# Use general loop so easy to add more later
# Could also have done: del ds.OtherPatientIDs, etc.
for name in ['OtherPatientIDs', 'OtherPatientIDsSequence']:
if name in dataset:
delattr(dataset, name)
# Same as above but for blanking data elements that are type 2.
for name in ['PatientBirthDate']:
if name in dataset:
dataset.data_element(name).value = ''
# Remove private tags if function argument says to do so. Same for curves
if remove_private_tags:
dataset.remove_private_tags()
if remove_curves:
dataset.walk(curves_callback)
return dataset
[docs] def array_to_model(self, array):
"""
Manipulation of dicom tree data
"""
model = QtGui.QStandardItemModel()
parentItem = model.invisibleRootItem()
for ntuple in array:
tag = ntuple[0]
value = ntuple[1]
if isinstance(value, dict):
self.recurse_dic_to_item(value, parentItem)
else:
item = QtGui.QStandardItem(tag + str(value))
parentItem.appendRow(item)
return parentItem
def dic_to_model(self, dic):
#model = QtGui.QStandardItemModel()
parentItem = self.model.invisibleRootItem()
self.recurse_dic_to_item(dic, parentItem)
return self.model
def dataset_to_array(self, dataset):
array = []
for data_element in dataset:
array.append(self.data_element_to_dic(data_element))
return array
[docs] def recurse_dic_to_item(self, dic, parent, title=""):
"""
Appending data into tree structure.
"""
for k in dic:
v = dic[k]
if isinstance(v, dict):
if k.endswith("Sequence"):
title = k.replace(" Sequence", "")
if k.startswith("item"):
num = str(int(k.rsplit()[-1])+1)
k = "{} {}".format(title, num)
#item = QtGui.QStandardItem(k + ':' + str(v))
item = QtGui.QStandardItem(k)
parent.appendRow(self.recurse_dic_to_item(v, item, title))
else:
item1 = QtGui.QStandardItem(k)
item2 = QtGui.QStandardItem(str(v))
#item = QtGui.QStandardItem(k + ': ' + str(v))
#parent.appendRow(item)
parent.appendRow([item1, item2])
return parent
def dicom_to_dataset(self, filename):
dataset = dicom.read_file(filename, force=True)
return dataset
def data_element_to_dic(self, data_element):
dic = collections.OrderedDict()
if data_element.VR == "SQ":
items = collections.OrderedDict()
dic[data_element.name] = items
i = 0
for dataset_item in data_element:
items['item ' + str(i)] = self.dataset_to_dic(dataset_item)
i += 1
elif data_element.name != 'Pixel Data':
if "SOP Class UID" in data_element.name and self.title != "":
self.title = data_element.value
if data_element.name in self.patient_dict.keys():
self.patient_info.append((self.patient_dict.get(data_element.name), data_element.value))
if "Plan Label" in data_element.name:
self.plan_info.append(("Plan Name", data_element.value))
if "Delivery Maximum Dose" == data_element.name:
self.plan_info.append(("Dose rate", data_element.value*100))
dic[data_element.name] = data_element.value
return dic
def dataset_to_dic(self, dataset):
dic = collections.OrderedDict()
for data_element in dataset:
dic.update(self.data_element_to_dic(data_element))
return dic
def display(self, model):
app = QtGui.QApplication.instance()
if not app: # create QApplication if it doesnt exist
app = QtGui.QApplication(sys.argv)
tree = QtGui.QTreeView()
tree.setModel(model)
tree.show()
app.exec_()
return tree
def main():
filename = sys.argv[1]
dicomTree = DicomTree(filename)
dicomTree.show_tree()
if __name__ == "__main__":
main()