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 114 invesalius/gui/default_tasks.py -text
115 115 invesalius/gui/default_viewers.py -text
116 116 invesalius/gui/dialogs.py -text
  117 +invesalius/gui/dicom_preview_panel.py -text
117 118 invesalius/gui/frame.py -text
118 119 invesalius/gui/import_data_wizard.py -text
119 120 invesalius/gui/import_panel.py -text
... ...
invesalius/gui/dicom_preview_panel.py 0 → 100755
... ... @@ -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 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 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 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 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
... ...