Commit fb59d2bd2572e55ed5b23fe0e41fb653f1bc5ca7

Authored by tatiana
1 parent bf95d589

ADD: Dicom preview class - not working yet

.gitattributes
@@ -114,6 +114,7 @@ invesalius/gui/data_notebook.py -text @@ -114,6 +114,7 @@ invesalius/gui/data_notebook.py -text
114 invesalius/gui/default_tasks.py -text 114 invesalius/gui/default_tasks.py -text
115 invesalius/gui/default_viewers.py -text 115 invesalius/gui/default_viewers.py -text
116 invesalius/gui/dialogs.py -text 116 invesalius/gui/dialogs.py -text
  117 +invesalius/gui/dicom_preview_panel.py -text
117 invesalius/gui/frame.py -text 118 invesalius/gui/frame.py -text
118 invesalius/gui/import_data_wizard.py -text 119 invesalius/gui/import_data_wizard.py -text
119 invesalius/gui/import_panel.py -text 120 invesalius/gui/import_panel.py -text
invesalius/gui/dicom_preview_panel.py 0 → 100755
@@ -0,0 +1,305 @@ @@ -0,0 +1,305 @@
  1 +#!/usr/bin/env python
  2 +# -*- coding: UTF-8 -*-
  3 +
  4 +import wx
  5 +import vtk
  6 +import vtkgdcm
  7 +
  8 +from vtk.wx.wxVTKRenderWindowInteractor import wxVTKRenderWindowInteractor
  9 +#from reader import dicom_reader
  10 +
  11 +myEVT_SELECT = wx.NewEventType()
  12 +# This event occurs when the user select a preview
  13 +EVT_SELECT = wx.PyEventBinder(myEVT_SELECT, 1)
  14 +
  15 +myEVT_SELECT_SERIE = wx.NewEventType()
  16 +# This event occurs when the user select a preview
  17 +EVT_SELECT_SERIE = wx.PyEventBinder(myEVT_SELECT_SERIE, 1)
  18 +
  19 +class PreviewEvent(wx.PyCommandEvent):
  20 + def __init__(self , evtType, id):
  21 + wx.PyCommandEvent.__init__(self, evtType, id)
  22 +
  23 + def GetSelectID(self):
  24 + return self.SelectedID
  25 +
  26 + def SetSelectedID(self, id):
  27 + self.SelectedID = id
  28 +
  29 +
  30 +class SerieEvent(PreviewEvent):
  31 + def __init__(self , evtType, id):
  32 + super(SerieEvent, self).__init__(evtType, id)
  33 +
  34 +
  35 +class DicomLoader(object):
  36 + """
  37 + Responsible for load dicom files. A dictionary like behavior
  38 + """
  39 + def __init__(self):
  40 + self.loaded_dicoms = {}
  41 +
  42 + def __getitem__(self, filename):
  43 + """
  44 + Especial method to behave like dictionary
  45 + """
  46 + try:
  47 + return self.loaded_dicoms[filename]
  48 + except KeyError:
  49 + #print "Except"
  50 + self._load_dicom_files(filename)
  51 + return self.loaded_dicoms[filename]
  52 +
  53 + def _load_dicom_files(self, filename, window=150, level=230):
  54 + reader = vtkgdcm.vtkGDCMImageReader()
  55 + reader.SetFileName(filename)
  56 + imagedata = reader.GetOutput()
  57 +
  58 + scale = imagedata.GetScalarRange()
  59 +
  60 + cast = vtk.vtkImageMapToWindowLevelColors()
  61 + cast.SetInput(imagedata)
  62 + cast.SetWindow(float(window))
  63 + cast.SetLevel(float(level))
  64 +
  65 + self.loaded_dicoms[filename] = cast.GetOutput()
  66 +
  67 +
  68 +class Preview(wx.Panel):
  69 + """
  70 + Where the images will be showed.
  71 + """
  72 + dicom_loader = DicomLoader()
  73 + def __init__(self, parent):
  74 + super(Preview, self).__init__(parent)
  75 + # Will it be white?
  76 + self.SetBackgroundColour((255, 255, 255))
  77 + self.sizer = wx.BoxSizer(wx.VERTICAL)
  78 + self.SetSizer(self.sizer)
  79 + self._init_ui()
  80 + self._init_vtk()
  81 + self._bind_events()
  82 +
  83 + def _init_ui(self):
  84 + self.title = wx.StaticText(self, -1, "Image",
  85 + style=wx.ALIGN_CENTER)
  86 +
  87 + self.subtitle = wx.StaticText(self, -1, "Image",
  88 + style=wx.ALIGN_CENTER)
  89 +
  90 + self.interactor = wxVTKRenderWindowInteractor(self, -1, size=(70, 70))
  91 +
  92 + self.sizer.Add(self.title, 0, wx.ALIGN_CENTER_HORIZONTAL)
  93 + self.sizer.Add(self.subtitle, 0, wx.ALIGN_CENTER_HORIZONTAL)
  94 + self.sizer.Add(self.interactor, 0, wx.ALIGN_CENTER_HORIZONTAL\
  95 + | wx.ALL, 5)
  96 +
  97 + def _init_vtk(self):
  98 + self.actor = vtk.vtkImageActor()
  99 +
  100 + self.render = vtk.vtkRenderer()
  101 +
  102 + self.interactor.SetInteractorStyle(None)
  103 + self.interactor.GetRenderWindow().AddRenderer(self.render)
  104 +
  105 + self.render.AddActor(self.actor)
  106 +
  107 +
  108 + def _bind_events(self):
  109 + self.Bind( wx.EVT_LEFT_DCLICK, self.OnSelect)
  110 +
  111 + def OnSelect(self, evt):
  112 + evt = PreviewEvent(myEVT_SELECT, self.GetId())
  113 + evt.SetSelectedID(self.ID)
  114 + self.GetEventHandler().ProcessEvent(evt)
  115 +
  116 + def SetTitle(self, title):
  117 + self.title.SetLabel(title)
  118 +
  119 + def SetSubtitle(self, subtitle):
  120 + self.subtitle.SetLabel(subtitle)
  121 +
  122 + def SetImage(self, image_data):
  123 + """
  124 + Set a Image to preview.
  125 + """
  126 + filename, window, level, title, subtitle = image_data
  127 + print image_data
  128 + self.SetTitle(title)
  129 + self.SetSubtitle(subtitle)
  130 + #self.ID = image_file[5] # todo: check if this is necessary
  131 +
  132 + # TODO: enhace interface
  133 + imagedata = Preview.dicom_loader[filename]
  134 + self.actor.SetInput(imagedata)
  135 + self.render.ResetCamera()
  136 +
  137 +class DicomPreviewSeries(wx.Panel):
  138 + """A dicom series preview panel"""
  139 + def __init__(self, parent):
  140 + super(DicomPreviewSeries, self).__init__(parent)
  141 + # TODO: 3 pixels between the previews is a good idea?
  142 + # I have to test.
  143 + self.sizer = wx.BoxSizer(wx.HORIZONTAL)
  144 + self.SetSizer(self.sizer)
  145 + self.displayed_position = 0
  146 + self._init_ui()
  147 +
  148 + def _init_ui(self):
  149 + self.scroll = wx.ScrollBar(self, style=wx.SB_VERTICAL)
  150 + self.grid = wx.GridSizer(rows=3, cols=5, vgap=3, hgap=3)
  151 + self.sizer.Add(self.grid)
  152 + self.sizer.Add(self.scroll, 0, wx.EXPAND)
  153 + self._Add_Panels_Preview()
  154 + self._bind_events()
  155 +
  156 + def _Add_Panels_Preview(self):
  157 + self.previews = []
  158 + for i in xrange(3):
  159 + for j in xrange(5):
  160 + p = Preview(self)
  161 + p.Hide()
  162 + self.previews.append(p)
  163 + self.grid.Add(p, i, j)
  164 +
  165 + def _bind_events(self):
  166 + # When the user scrolls the window
  167 + self.Bind(wx.EVT_SCROLL, self.OnScroll)
  168 + self.Bind(EVT_SELECT, self.OnSelect)
  169 +
  170 + def OnSelect(self, evt):
  171 + my_evt = SerieEvent(myEVT_SELECT_SERIE, self.GetId())
  172 + my_evt.SetSelectedID(evt.GetSelectID())
  173 + self.GetEventHandler().ProcessEvent(my_evt)
  174 +
  175 + def SetDicomDirectory(self, directory):
  176 + print "SetDicomDirectory"
  177 + self.directory = directory
  178 + self.series = dicom_reader.GetSeries(directory)[0]
  179 + print "keys", [key[0] for key in self.series.keys()]
  180 +
  181 + s = self.series
  182 + for k in s.keys():
  183 + print "------ PESSOA ---------"
  184 + print "%s (%d series)"%(k[0], len(s[k])-1)
  185 + for ns in range(1,len(s[k])):
  186 + print "------ SERIE ---------"
  187 + print "unnamed"
  188 + print "age %s" %(s[k][ns][8])
  189 + print "date acquired %s %s" %(s[k][ns][0], s[k][ns][4])
  190 + print "birthdate %s" %(s[k][ns][23])
  191 + print "institution %s" %(s[k][ns][6])
  192 +
  193 + # TODO: I need to improve this
  194 + self.files = [(self.series[i][0][0][8], # Filename
  195 + self.series[i][0][0][12], # Window Level
  196 + self.series[i][0][0][13], # Window Width
  197 + "unnamed", #% (n + 1), # Title
  198 + "%d Images" % len(self.series[i][0]), # Subtitle
  199 + i) for n, i in enumerate(self.series)]
  200 +
  201 + def SetDicomSeries(self, files):
  202 + self.files = files
  203 + scroll_range = len(files)/5
  204 + if scroll_range * 5 < len(files):
  205 + scroll_range +=1
  206 + self.scroll.SetScrollbar(0, 3, scroll_range, 5)
  207 + self._display_previews()
  208 +
  209 + def _display_previews(self):
  210 + initial = self.displayed_position * 5
  211 + final = initial + 15
  212 + for f, p in zip(self.files[initial:final], self.previews):
  213 + print "--------"
  214 + print "f:", f
  215 + print "p: ", p
  216 + p.SetImage(f)
  217 + p.Show()
  218 +
  219 + def OnScroll(self, evt):
  220 + self.displayed_position = evt.GetPosition()
  221 + [i.Hide() for i in self.previews]
  222 + self._display_previews()
  223 +
  224 +
  225 +class DicomPreview(wx.Panel):
  226 + """A dicom preview panel"""
  227 + def __init__(self, parent):
  228 + super(DicomPreview, self).__init__(parent)
  229 + # TODO: 3 pixels between the previews is a good idea?
  230 + # I have to test.
  231 + self.sizer = wx.BoxSizer(wx.HORIZONTAL)
  232 + self.SetSizer(self.sizer)
  233 + self.displayed_position = 0
  234 + self.nhidden_last_display = 0
  235 + self._init_ui()
  236 +
  237 + def _init_ui(self):
  238 + self.scroll = wx.ScrollBar(self, style=wx.SB_VERTICAL)
  239 + self.grid = wx.GridSizer(rows=3, cols=5, vgap=3, hgap=3)
  240 + self.sizer.Add(self.grid)
  241 + self.sizer.Add(self.scroll, 0, wx.EXPAND)
  242 + self._Add_Panels_Preview()
  243 + self._bind_events()
  244 +
  245 + def _Add_Panels_Preview(self):
  246 + self.previews = []
  247 + for i in xrange(3):
  248 + for j in xrange(5):
  249 + p = Preview(self)
  250 + #p.Hide()
  251 + self.previews.append(p)
  252 + self.grid.Add(p, i, j)
  253 +
  254 + def _bind_events(self):
  255 + # When the user scrolls the window
  256 + self.Bind(wx.EVT_SCROLL, self.OnScroll)
  257 +
  258 + def SetDicomDirectory(self, directory):
  259 + self.directory = directory
  260 + self.series = dicom_reader.GetSeries(directory)[0]
  261 +
  262 + def SetDicomSerie(self, serie):
  263 + k = serie
  264 + self.files = [(i[8],
  265 + i[12],
  266 + i[13],
  267 + "Image %d" % (n + 1), # Title
  268 + "%s"% str(i[3][2]), # Spacing
  269 + n)for n, i in enumerate(self.series[k][0])]
  270 + scroll_range = len(self.files)/5
  271 + if scroll_range * 5 < len(self.files):
  272 + scroll_range +=1
  273 + self.scroll.SetScrollbar(0, 3, scroll_range, 5)
  274 + self._display_previews()
  275 +
  276 + def _display_previews(self):
  277 + initial = self.displayed_position * 5
  278 + final = initial + 15
  279 +
  280 + if len(self.files) < final:
  281 + for i in xrange(final-len(self.files)):
  282 + try:
  283 + self.previews[-i-1].Hide()
  284 + except IndexError:
  285 + #print "doesn't exist!"
  286 + pass
  287 + self.nhidden_last_display = final-len(self.files)
  288 + else:
  289 + if self.nhidden_last_display:
  290 + for i in xrange(self.nhidden_last_display):
  291 + try:
  292 + self.previews[-i-1].Show()
  293 + except IndexError:
  294 + #print "doesn't exist!"
  295 + pass
  296 + self.nhidden_last_display = 0
  297 +
  298 + for f, p in zip(self.files[initial:final], self.previews):
  299 + p.SetImage(f)
  300 + p.interactor.Render()
  301 +
  302 + def OnScroll(self, evt):
  303 + if self.displayed_position != evt.GetPosition():
  304 + self.displayed_position = evt.GetPosition()
  305 + self._display_previews()
invesalius/reader/dicom_grouper.py
@@ -19,340 +19,250 @@ @@ -19,340 +19,250 @@
19 # detalhes. 19 # detalhes.
20 #--------------------------------------------------------------------- 20 #---------------------------------------------------------------------
21 21
22 -import dicom  
23 22
24 -class DicomGroups:  
25 - """  
26 - It is possible to separate sets of a set  
27 - of files dicom.  
28 -  
29 - To use:  
30 - list_dicoms = [c:\a.dcm, c:\a1.gdcm]  
31 - dicom_splitter = DicomGroups()  
32 - dicom_splitter.SetFileList(list_dicoms)  
33 - dicom_splitter.Update()  
34 - splitted = dicom_splitter.GetOutput()  
35 - """  
36 -  
37 - def __init__(self):  
38 - self.parser = dicom.Parser()  
39 - #List of DICOM from Directory  
40 - self.filenamelist = []  
41 - # List of DICOM with Informations  
42 - self.filelist = []  
43 - #is output  
44 - self.groups_dcm = {}  
45 - #It is the kind of group that was used.  
46 - self.split_type = 0  
47 -  
48 - def SetFileList(self, filenamelist):  
49 - """  
50 - Input is a list with the complete address  
51 - of each DICOM.  
52 - """  
53 - self.filenamelist = filenamelist  
54 -  
55 - def Update(self):  
56 - """  
57 - Trigger processing group of series  
58 - """  
59 -  
60 - self.__ParseFiles()  
61 -  
62 - self.__Split1()  
63 -  
64 - if (len(self.GetOutput().keys()) == len(self.filenamelist)):  
65 - self.__Split2()  
66 - self.split_type = 1  
67 -  
68 - self.__Split3()  
69 -  
70 - self.__UpdateZSpacing()  
71 -  
72 -  
73 - def GetOutput(self):  
74 - """  
75 - Returns a dictionary with groups  
76 - of DICOM.  
77 - """  
78 - return self.groups_dcm 23 +# ---------------------------------------------------------
  24 +# PROBLEM 1
  25 +# There are times when there are lots of groups on dict, but
  26 +# each group contains only one slice (DICOM file).
  27 +#
  28 +# Equipments / manufacturer:
  29 +# TODO
  30 +#
  31 +# Cases:
  32 +# TODO 0031, 0056, 1093
  33 +#
  34 +# What occurs in these cases:
  35 +# <dicom.image.number> and <dicom.acquisition.series_number>
  36 +# were swapped
79 37
80 - def GetSplitterType(self):  
81 - """  
82 - Return Integer with the SplitterType  
83 - 0 option used the name of patient information,  
84 - id of the study, number of series and orientation  
85 - of the image (AXIAL, SAGITAL and CORONAL).  
86 - 1 option was checked is used to splitted the if distance  
87 - of the images (tag image position) is more 2x Thickness.  
88 - """  
89 - return self.split_type  
90 38
91 - def __ParseFiles(self):  
92 - """  
93 - It generates a list with information  
94 - concerning dicom files within a directory.  
95 - The Input is a List containing andress of  
96 - the DICOM  
97 - """  
98 - filenamelist = self.filenamelist  
99 - filelist = self.filelist  
100 - parser = self.parser  
101 - for x in xrange(len(filenamelist)): 39 +# -----------------------------------------------------------
  40 +# PROBLEM 2
  41 +# Two slices (DICOM file) inside a group have the same
  42 +# position.
  43 +#
  44 +# Equipments / manufacturer:
  45 +# TODO
  46 +#
  47 +# Cases:
  48 +# TODO 0031, 0056, 1093
  49 +#
  50 +# What occurs in these cases:
  51 +# <dicom.image.number> and <dicom.acquisition.series_number>
  52 +# were swapped
102 53
103 - if not parser.SetFileName(filenamelist[x]):  
104 - return None 54 +ORIENT_MAP = {"SAGITTAL":0, "AXIAL":1, "CORONAL":2}
105 55
106 - information = dicom.Dicom()  
107 - information.SetParser(parser)  
108 56
109 - self.filelist.append(information)  
110 - self.filelist = filelist  
111 - 57 +class DicomGroup:
  58 + def __init__(self):
  59 + # key:
  60 + # (dicom.patient.name, dicom.acquisition.id_study,
  61 + # dicom.acquisition.series_number,
  62 + # dicom.image.orientation_label, index)
  63 + self.key = ()
  64 + self.slices_dict = {} # slice_position: Dicom.dicom
  65 + # IDEA (13/10): Represent internally as dictionary,
  66 + # externally as list
  67 + self.nslices = 0
  68 +
  69 + def AddSlice(self, dicom):
  70 + pos = dicom.image.position
  71 + if pos not in self.slices_dict.keys():
  72 + self.slices_dict[pos] = dicom
  73 + self.nslices += 1
  74 + return True
  75 + else:
  76 + return False
  77 +
  78 + def GetList(self):
  79 + # Should be called when user selects this group
  80 + # This list will be used to create the vtkImageData
  81 + # (interpolated)
  82 + return self.slices_dict.values()
  83 +
  84 + def GetSortedList(self):
  85 + # This will be used to fix problem 1, after merging
  86 + # single DicomGroups of same study_id and orientation
  87 + list_ = self.slices_dict_values()
  88 + axis = ORIENT_MAP[self.key[3]]
  89 + list_ = sorted(list_, key = lambda dicom:dicom.image.position[axis])
  90 + return list_
  91 +
  92 +
  93 +class PatientGroup:
  94 + def __init__(self):
  95 + # key:
  96 + # (dicom.patient.name, dicom.patient.id)
  97 + self.key = ()
  98 + self.groups_dict = {} # group_key: DicomGroup
  99 + self.slices = 0
  100 +
  101 + def AddFile(self, dicom, index=0):
  102 + # Given general DICOM information, we group slices according
  103 + # to main series information (group_key)
  104 +
  105 + # WARN: This was defined after years of experience
  106 + # (2003-2009), so THINK TWICE before changing group_key
  107 +
  108 + # Problem 2 is being fixed by the way this method is
  109 + # implemented, dinamically during new dicom's addition
  110 + group_key = (dicom.patient.name,
  111 + dicom.acquisition.id_study,
  112 + dicom.acquisition.series_number,
  113 + dicom.image.orientation_label,
  114 + index) # This will be used to deal with Problem 2
112 115
113 - def __GetInformations(self, ind):  
114 - """  
115 - Return a list referring to a specific DICOM  
116 - in dictionary. In some cases it is necessary  
117 - to pass only the index  
118 - """  
119 - filelist = self.filelist  
120 - return filelist[ind]  
121 - 116 + # Does this group exist? Best case ;)
  117 + if group_key not in self.groups_dict.keys():
  118 + group = DicomGroup()
  119 + group.AddSlice(dicom)
  120 + self.groups_dict[group_key] = group
  121 + # Group exists... Lets try to add slice
  122 + else:
  123 + group = self.groups_dicom[group_key]
  124 + slice_added = group.AddSlice(dicom)
  125 + if not slice_added:
  126 + # If we're here, then Problem 2 occured
  127 + # TODO: Optimize recursion
  128 + self.AddFile(file_path, index+1)
122 129
123 - def __Split1(self):  
124 - """  
125 - Bring together the series under the name of  
126 - the patient, id of the study, serial number  
127 - and label orientation of the image.  
128 - """  
129 - groups_dcm = self.groups_dcm  
130 -  
131 - for x in xrange(len(self.filelist)):  
132 - information = self.__GetInformations(x) 130 + def Update(self):
  131 + # Ideally, AddFile would be sufficient for splitting DICOM
  132 + # files into groups (series). However, this does not work for
  133 + # acquisitions / equipments and manufacturers.
133 134
134 - key = (information.patient.name, information.acquisition.id_study,\  
135 - information.acquisition.serie_number, information.image.orientation_label) 135 + # Although DICOM is a protocol, each one uses its fields in a
  136 + # different manner
136 137
137 - if (key in groups_dcm.keys()):  
138 - groups_dcm[key].append(information)  
139 - else:  
140 - groups_dcm[key] = [information] 138 + # Check if Problem 1 occurs (n groups with 1 slice each)
  139 + is_there_problem_1 = False
  140 + if (self.ndicom == len(self.groups_dicom)) and\
  141 + (self.ndicom > 1):
  142 + is_there_problem_1 = True
141 143
142 - self.groups_dcm = groups_dcm 144 + # Fix Problem 1
  145 + if is_there_problem_1:
  146 + self.groups_dict = self.FixProblem1(self.groups_dict)
143 147
  148 + def GetGroups(self):
  149 + return self.groups_dict.values()
144 150
145 - def __Split2(self): 151 + def FixProblem1(self, dict):
146 """ 152 """
147 - Separate according to the difference of the current  
148 - share with the next.  
149 - If the else them is higher than the z axis  
150 - multiplied by two. 153 + Merge multiple DICOM groups in case Problem 1 (description
  154 + above) occurs.
  155 +
  156 + WARN: We've implemented an heuristic to try to solve
  157 + the problem. There is no scientific background and this aims
  158 + to be a workaround to exams which are not in conformance with
  159 + the DICOM protocol.
151 """ 160 """
152 - self.groups_dcm = {}  
153 - cont_series = 0  
154 - groups_dcm = self.groups_dcm  
155 -  
156 - #Through in the series (index of the dictionary)  
157 - for x in xrange(len(self.filelist)):  
158 - #Slices through in the serie  
159 - information = self.__GetInformations(x)  
160 - key = (information.patient.name, cont_series)  
161 -  
162 - #If there exists only one slice.  
163 - if (len(self.filelist) > 1):  
164 - #If the number of slices in the series is  
165 - #less than the all number of slices.  
166 - #It is necessary to whether and the  
167 - #last slice.  
168 - if ((x < len(self.filelist) and (x != len(self.filelist) - 1))):  
169 - #position of next slice  
170 - information = self.__GetInformations(x + 1)  
171 - image_position_prox = information.image.position  
172 - print image_position_prox  
173 - else:  
174 - #slice up the position.  
175 - information = self.__GetInformations(x - 1)  
176 - image_position_prox = information.image.position  
177 - #According to the orientation of the image subtraction  
178 - #will be between a specific position in the vector of positions.  
179 - image_orientation_label = information.image.orientation_label  
180 - image_position = information.image.position  
181 - if(image_orientation_label == "SAGITTAL"):  
182 - dif_image_position = image_position_prox[0] - image_position[0]  
183 -  
184 - elif (image_orientation_label == "AXIAL"):  
185 - dif_image_position = image_position_prox[1] - image_position[1]  
186 - else:  
187 - dif_image_position = image_position_prox[2] - image_position[2]  
188 -  
189 - #If the difference in the positions is less than the  
190 - #spacing z-axis (thickness) multiplied by two.  
191 - #Otherwise this key create and add value  
192 - spacing = information.image.spacing  
193 - if ((dif_image_position) <= spacing[2] * 2):  
194 - #If there is already such a key in the dictionary,  
195 - #only adds value. Otherwise this key create in the  
196 - #dictionary and add value  
197 - if (key in groups_dcm.keys()):  
198 - groups_dcm[key].append(information)  
199 - else:  
200 - groups_dcm[key] = [information] 161 + # Divide existing groups into 2 groups:
  162 + dict_final = {} # 1
  163 + # those containing "3D photos" and undefined
  164 + # orientation - these won't be changed (groups_lost).
  165 +
  166 + dict_to_change = {} # 2
  167 + # which can be re-grouped according to our heuristic
  168 +
  169 + # split existing groups in these two types of group, based on
  170 + # orientation label
  171 +
  172 + # 1st STEP: RE-GROUP
  173 + for group_key in dict:
  174 + # values used as key of the new dictionary
  175 + orientation = dict[group_key].image.orientation_label
  176 + study_id = dict[group_key].acquisition.id_study
  177 + # if axial, coronal or sagittal
  178 + if orientation in ORIENT_MAP:
  179 + group_key_s = (orientation, study_id)
  180 + # If this method was called, there is only one slice
  181 + # in this group (dicom)
  182 + dicom = dict[group_key].GetList()[0]
  183 + if group_key_s not in dict_to_change.keys():
  184 + group = DicomGroup()
  185 + group.AddSlice(dicom)
  186 + dict_to_change[group_key_s] = group
201 else: 187 else:
202 - cont_series = cont_series + 1  
203 - groups_dcm[key] = [information]  
204 - 188 + group = dict_to_change[group_key_s]
  189 + group.AddSlice(dicom)
205 else: 190 else:
206 -  
207 - if (cont_series in groups_dcm.keys()):  
208 - groups_dcm[key].append(information)  
209 - else:  
210 - groups_dcm[key] = [information]  
211 -  
212 - cont_series = cont_series + 1  
213 -  
214 - self.groups_dcm = groups_dcm  
215 -  
216 -  
217 - def __Split3(self):  
218 - """  
219 - Separate the slice with the positions  
220 - repeated.  
221 - """  
222 - groups_dcm = self.groups_dcm  
223 - groups_dcm_ = {}  
224 - size_groups = len(groups_dcm.keys())  
225 - tmp1 = {}  
226 - tmp_list = []  
227 -  
228 - #goes according to the serial number  
229 - #already separated.  
230 - for n in xrange(size_groups):  
231 -  
232 - #Key of dictionary  
233 - key = groups_dcm.keys()[n]  
234 -  
235 - #Number of slices in the series  
236 - size_list = len(groups_dcm[key])  
237 -  
238 - for y in xrange(size_list):  
239 -  
240 - #Slices walks in the series  
241 - information = groups_dcm[key][y]  
242 -  
243 - #Generate new key to dictionary  
244 - image_pos = information.image.position  
245 - key_ = (image_pos[0], image_pos[1], image_pos[2])  
246 -  
247 - #Add informations in the list  
248 - list = [information]  
249 -  
250 - #If list Null, create dictionary  
251 - #and add list with information  
252 - #after add in a temporary list  
253 - if (tmp_list == []):  
254 - tmp = {}  
255 - tmp[key_] = information  
256 - tmp_list.append(tmp)  
257 - 191 + dict_final[group_key] = dict[group_key]
  192 +
  193 + # group_counter will be used as key to DicomGroups created
  194 + # while checking differences
  195 + group_counter = 0
  196 + for group_key in dict_to_change:
  197 + # 2nd STEP: SORT
  198 + sorted_list = dict_to_change[group_key].GetSortedList()
  199 +
  200 + # 3rd STEP: CHECK DIFFERENCES
  201 + axis = ORIENT_MAP[group_key[0]] # based on orientation
  202 + for index in xrange(len(sorted_list)-1):
  203 + current = sorted_list[index]
  204 + next = sorted_list[index+1]
  205 +
  206 + pos_current = current.image.position[axis]
  207 + pos_next = current.image.position[axis]
  208 + spacing = current.image.spacing
  209 +
  210 + if (pos_next - pos_current) <= (spacing[2] * 2):
  211 + if group_counter in dict_final:
  212 + dict_final[group_counter].AddSlice(current)
  213 + else:
  214 + group = DicomGroup()
  215 + group.AddSlice(current)
  216 + dict_final[group_counter] = group
258 else: 217 else:
259 - b = len(tmp_list)  
260 - a = 0  
261 - #flag is to control when be necessary  
262 - #to create another position in the list.  
263 - flag = 0  
264 -  
265 - while a < b:  
266 - #if there is to share the same  
267 - #position create new key in the  
268 - #dictionary 218 + group_counter +=1
  219 + group = DicomGroup()
  220 + group.AddSlice(current)
  221 + dict_final[group_counter] = group
269 222
270 - if not (key_ in (tmp_list[a]).keys()):  
271 - (tmp_list[a])[key_] = information  
272 - flag = 1  
273 - a = a + 1 223 + return dict_final
274 224
275 - if (flag == 0):  
276 - tmp = {}  
277 - tmp[key_] = information  
278 225
279 - tmp_list.append(tmp) 226 +class DicomPatientGrouper:
  227 + # read file, check if it is dicom...
  228 + # dicom = dicom.Dicom
  229 + # grouper = DicomPatientGrouper()
  230 + # grouper.AddFile(dicom)
  231 + # ... (repeat to all files on folder)
  232 + # grouper.Update()
  233 + # groups = GetPatientGroups()
280 234
281 -  
282 - #for each item on the list is created  
283 - #a new position in the dictionary.  
284 - size_tmp_list = len(tmp_list) 235 + def __init__(self):
  236 + self.patients_dict = {}
  237 +
  238 + def AddFile(self, dicom):
  239 + patient_key = (dicom.patient.name,
  240 + dicom.patient.id)
  241 +
  242 + # Does this patient exist?
  243 + if patient_key not in self.patients_dict.keys():
  244 + patient = PatientGroup()
  245 + patient.AddSlice(dicom)
  246 + self.patients_dict[patient_key] = patient
  247 + # Patient exists... Lets add group to it
  248 + else:
  249 + patient = self.patients_dict[patient_key]
  250 + patient.AddFile(dicom)
285 251
286 - for x in xrange(size_tmp_list):  
287 -  
288 - tmp1 = tmp_list[x]  
289 - for m in xrange(len(tmp1.keys())):  
290 -  
291 - key = tmp1.keys()[m]  
292 - information = tmp1[key]  
293 -  
294 - #new_key = (x,information.patient.name, information.image.orientation_label,  
295 - # information.acquisition.serie_number)  
296 -  
297 - new_key = (information.patient.name, None, x,  
298 - information.image.orientation_label)  
299 -  
300 - if (new_key in groups_dcm_.keys()):  
301 - groups_dcm_[new_key].append(information)  
302 - else:  
303 - groups_dcm_[new_key] = [information]  
304 -  
305 - #the number of previously existing number is  
306 - #greater or equal then the group keeps up,  
307 - #but maintains the same group of positions.  
308 - if len(self.groups_dcm.keys()) < len(groups_dcm_.keys()):  
309 - self.groups_dcm = groups_dcm_  
310 -  
311 - for j in xrange(len(self.groups_dcm.keys())):  
312 - key = self.groups_dcm.keys()[j]  
313 - self.groups_dcm[key].sort(key=lambda x: x.image.number)  
314 -  
315 -  
316 - def __UpdateZSpacing(self): 252 + def Update(self):
  253 + for patient in self.patients_dict.values():
  254 + patient.Update()
  255 +
  256 + def GetPatientsGroups(self):
317 """ 257 """
318 - Calculate Z spacing from slices 258 + How to use:
  259 + patient_list = grouper.GetPatientsGroups()
  260 + for patient in patient_list:
  261 + group_list = patient.GetGroups()
  262 + for group in group_list:
  263 + group.GetList()
  264 + # :) you've got a list of dicom.Dicom
  265 + # of the same series
319 """ 266 """
  267 + return self.patients_dict.values()
320 268
321 - for x in xrange(len(self.groups_dcm.keys())):  
322 -  
323 - key = self.groups_dcm.keys()[x]  
324 - information = self.groups_dcm[key][0]  
325 - if (len(self.groups_dcm[key]) > 1):  
326 - #Catch a slice of middle and the next to find the spacing.  
327 - center = len(self.groups_dcm[key])/2  
328 - if (center == 1):  
329 - center = 0  
330 -  
331 - information = self.groups_dcm[key][center]  
332 - current_position = information.image.position  
333 -  
334 - information = self.groups_dcm[key][center + 1]  
335 - next_position = information.image.position  
336 -  
337 - try:  
338 - information = self.groups_dcm[self.groups_dcm.keys()[x]][3]  
339 - image_orientation_label = information.image.orientation_label  
340 -  
341 - except(IndexError):  
342 - image_orientation_label = None  
343 -  
344 - if(image_orientation_label == "SAGITTAL"):  
345 - spacing = current_position[0] - next_position[0]  
346 - elif(image_orientation_label == "CORONAL"):  
347 - spacing = current_position[1] - next_position[1]  
348 - else:  
349 - spacing = current_position[2] - next_position[2]  
350 -  
351 - spacing = abs(spacing)  
352 -  
353 - else:  
354 - spacing = None  
355 -  
356 - for information in self.groups_dcm[key]:  
357 - if information.image.spacing:  
358 - information.image.spacing[2] = spacing