Commit 1aaf2ccb100d3fcc4fee87eb4f0f9bc58f8d78ef

Authored by tfmoraes
1 parent f739fdf5

ENH: Saving InVesalius3 using the proposed file format

invesalius/data/mask.py
@@ -51,37 +51,40 @@ class Mask(): @@ -51,37 +51,40 @@ class Mask():
51 Publisher.subscribe(self.OnFlipVolume, 'Flip volume') 51 Publisher.subscribe(self.OnFlipVolume, 'Flip volume')
52 Publisher.subscribe(self.OnSwapVolumeAxes, 'Swap volume axes') 52 Publisher.subscribe(self.OnSwapVolumeAxes, 'Swap volume axes')
53 53
54 - def SavePlist(self, filename): 54 + def SavePlist(self, dir_temp):
55 mask = {} 55 mask = {}
56 - filename = u'%s_%s_%d_%s' % (filename, 'mask', self.index, self.name)  
57 - img_name = u'%s.dat' % filename  
58 - self._save_mask(img_name) 56 + filename = u'mask%d_%s' % (self.index, self.name)
  57 + mask_filename = u'%s.dat' % filename
  58 + mask_filepath = os.path.join(dir_temp, mask_filename)
  59 + self._save_mask(mask_filepath)
59 60
60 mask['index'] = self.index 61 mask['index'] = self.index
  62 + mask['name'] = self.name
61 mask['colour'] = self.colour 63 mask['colour'] = self.colour
62 mask['opacity'] = self.opacity 64 mask['opacity'] = self.opacity
63 - mask['threshold range'] = self.threshold_range  
64 - mask['name'] = self.name  
65 - mask['edition threshold range'] = self.edition_threshold_range  
66 - mask['show'] = self.is_shown  
67 - mask['mask file'] = os.path.split(img_name)[1]  
68 - mask['mask shape'] = self.matrix.shape 65 + mask['threshold_range'] = self.threshold_range
  66 + mask['edition_threshold_range'] = self.edition_threshold_range
  67 + mask['visible'] = self.is_shown
  68 + mask['mask_file'] = mask_filename
  69 + mask['mask_shape'] = self.matrix.shape
69 70
70 - plistlib.writePlist(mask, filename + '.plist')  
71 - return os.path.split(filename)[1] + '.plist' 71 + plist_filename = filename + '.plist'
  72 + plist_filepath = os.path.join(dir_temp, plist_filename)
  73 + plistlib.writePlist(mask, plist_filepath)
  74 + return plist_filename
72 75
73 def OpenPList(self, filename): 76 def OpenPList(self, filename):
74 mask = plistlib.readPlist(filename) 77 mask = plistlib.readPlist(filename)
75 78
76 self.index = mask['index'] 79 self.index = mask['index']
  80 + self.name = mask['name']
77 self.colour = mask['colour'] 81 self.colour = mask['colour']
78 self.opacity = mask['opacity'] 82 self.opacity = mask['opacity']
79 - self.threshold_range = mask['threshold range']  
80 - self.name = mask['name']  
81 - self.edition_threshold_range = mask['edition threshold range']  
82 - self.is_shown = mask['show']  
83 - mask_file = mask['mask file']  
84 - shape = mask['mask shape'] 83 + self.threshold_range = mask['threshold_range']
  84 + self.edition_threshold_range = mask['edition_threshold_range']
  85 + self.is_shown = mask['visible']
  86 + mask_file = mask['mask_file']
  87 + shape = mask['mask_shape']
85 dirpath = os.path.abspath(os.path.split(filename)[0]) 88 dirpath = os.path.abspath(os.path.split(filename)[0])
86 path = os.path.join(dirpath, mask_file) 89 path = os.path.join(dirpath, mask_file)
87 self._open_mask(path, tuple(shape)) 90 self._open_mask(path, tuple(shape))
invesalius/data/measures.py
@@ -198,7 +198,7 @@ class Measurement(): @@ -198,7 +198,7 @@ class Measurement():
198 self.type = info["type"] 198 self.type = info["type"]
199 self.slice_number = info["slice_number"] 199 self.slice_number = info["slice_number"]
200 self.points = info["points"] 200 self.points = info["points"]
201 - self.is_shown = info["is_shown"] 201 + self.is_shown = info["visible"]
202 202
203 class CirclePointRepresentation(object): 203 class CirclePointRepresentation(object):
204 """ 204 """
invesalius/data/polydata_utils.py
@@ -17,7 +17,10 @@ @@ -17,7 +17,10 @@
17 # detalhes. 17 # detalhes.
18 #-------------------------------------------------------------------------- 18 #--------------------------------------------------------------------------
19 19
  20 +import sys
  21 +
20 import vtk 22 import vtk
  23 +import wx
21 from wx.lib.pubsub import pub as Publisher 24 from wx.lib.pubsub import pub as Publisher
22 25
23 import vtk_utils as vu 26 import vtk_utils as vu
@@ -103,7 +106,8 @@ def Merge(polydata_list): @@ -103,7 +106,8 @@ def Merge(polydata_list):
103 106
104 def Export(polydata, filename, bin=False): 107 def Export(polydata, filename, bin=False):
105 writer = vtk.vtkXMLPolyDataWriter() 108 writer = vtk.vtkXMLPolyDataWriter()
106 - writer.SetFileName(filename) 109 + print filename, type(filename)
  110 + writer.SetFileName(filename.encode('utf-8'))
107 if bin: 111 if bin:
108 writer.SetDataModeToBinary() 112 writer.SetDataModeToBinary()
109 else: 113 else:
@@ -113,7 +117,7 @@ def Export(polydata, filename, bin=False): @@ -113,7 +117,7 @@ def Export(polydata, filename, bin=False):
113 117
114 def Import(filename): 118 def Import(filename):
115 reader = vtk.vtkXMLPolyDataReader() 119 reader = vtk.vtkXMLPolyDataReader()
116 - reader.SetFileName(filename) 120 + reader.SetFileName(filename.encode('utf-8'))
117 reader.Update() 121 reader.Update()
118 return reader.GetOutput() 122 return reader.GetOutput()
119 123
invesalius/data/surface.py
@@ -62,32 +62,35 @@ class Surface(): @@ -62,32 +62,35 @@ class Surface():
62 else: 62 else:
63 self.name = name 63 self.name = name
64 64
65 - def SavePlist(self, filename):  
66 - surface = {}  
67 - filename = '%s$%s$%d' % (filename, 'surface', self.index)  
68 - d = self.__dict__  
69 - for key in d:  
70 - if isinstance(d[key], vtk.vtkPolyData):  
71 - img_name = '%s_%s.vtp' % (filename, key)  
72 - pu.Export(d[key], img_name, bin=True)  
73 - surface[key] = {'$vtp': os.path.split(img_name)[1]}  
74 - else:  
75 - surface[key] = d[key]  
76 -  
77 -  
78 - plistlib.writePlist(surface, filename + '.plist')  
79 - return os.path.split(filename)[1] + '.plist' 65 + def SavePlist(self, dir_temp):
  66 + filename = 'surface_%d_%s' % (self.index, self.name)
  67 + vtp_filename = filename + '.vtp'
  68 + vtp_filepath = os.path.join(dir_temp, vtp_filename)
  69 + pu.Export(self.polydata, vtp_filepath, bin=True)
  70 + surface = {'colour': self.colour,
  71 + 'index': self.index,
  72 + 'name': self.name,
  73 + 'polydata': vtp_filename,
  74 + 'transparency': self.transparency,
  75 + 'visible': bool(self.is_shown),
  76 + 'volume': self.volume,
  77 + }
  78 + plist_filename = filename + '.plist'
  79 + plist_filepath = os.path.join(dir_temp, filename + '.plist')
  80 + plistlib.writePlist(surface, plist_filepath)
  81 + return plist_filename
80 82
81 def OpenPList(self, filename): 83 def OpenPList(self, filename):
82 - surface = plistlib.readPlist(filename) 84 + sp = plistlib.readPlist(filename)
83 dirpath = os.path.abspath(os.path.split(filename)[0]) 85 dirpath = os.path.abspath(os.path.split(filename)[0])
84 - for key in surface:  
85 - if key == 'polydata':  
86 - filepath = os.path.split(surface[key]["$vtp"])[-1]  
87 - path = os.path.join(dirpath, filepath)  
88 - self.polydata = pu.Import(path)  
89 - else:  
90 - setattr(self, key, surface[key]) 86 + self.index = sp['index']
  87 + self.name = sp['name']
  88 + self.colour = sp['colour']
  89 + self.transparency = sp['transparency']
  90 + self.is_shown = sp['visible']
  91 + self.volume = sp['volume']
  92 + self.polydata = pu.Import(os.path.join(dirpath, sp['polydata']))
  93 + Surface.general_index = max(Surface.general_index, self.index)
91 94
92 def _set_class_index(self, index): 95 def _set_class_index(self, index):
93 Surface.general_index = index 96 Surface.general_index = index
@@ -319,6 +322,7 @@ class SurfaceManager(): @@ -319,6 +322,7 @@ class SurfaceManager():
319 self.last_surface_index = index 322 self.last_surface_index = index
320 self.ShowActor(index, True) 323 self.ShowActor(index, True)
321 324
  325 +
322 def OnLoadSurfaceDict(self, pubsub_evt): 326 def OnLoadSurfaceDict(self, pubsub_evt):
323 surface_dict = pubsub_evt.data 327 surface_dict = pubsub_evt.data
324 for key in surface_dict: 328 for key in surface_dict:
invesalius/project.py
@@ -17,6 +17,7 @@ @@ -17,6 +17,7 @@
17 # detalhes. 17 # detalhes.
18 #-------------------------------------------------------------------------- 18 #--------------------------------------------------------------------------
19 19
  20 +import datetime
20 import glob 21 import glob
21 import os 22 import os
22 import plistlib 23 import plistlib
@@ -49,8 +50,6 @@ class Project(object): @@ -49,8 +50,6 @@ class Project(object):
49 self.dicom_sample = '' 50 self.dicom_sample = ''
50 self.modality = '' 51 self.modality = ''
51 self.original_orientation = '' 52 self.original_orientation = ''
52 - self.min_threshold = ''  
53 - self.max_threshold = ''  
54 self.window = '' 53 self.window = ''
55 self.level = '' 54 self.level = ''
56 55
@@ -199,61 +198,71 @@ class Project(object): @@ -199,61 +198,71 @@ class Project(object):
199 item["type"] = m.type 198 item["type"] = m.type
200 item["slice_number"] = m.slice_number 199 item["slice_number"] = m.slice_number
201 item["points"] = m.points 200 item["points"] = m.points
202 - item["is_shown"] = m.is_shown 201 + item["visible"] = m.is_shown
203 measures[str(m.index)] = item 202 measures[str(m.index)] = item
204 return measures 203 return measures
205 -  
206 - def SavePlistProject(self, dir_, filename):  
207 204
208 - # Some filenames have non-ascii characters and encoded in a strange  
209 - # encoding, in that cases a UnicodeEncodeError is raised. To avoid  
210 - # that we encode in utf-8.  
211 - filename = filename.encode('utf-8') 205 + def SavePlistProject(self, dir_, filename):
212 dir_temp = tempfile.mkdtemp(filename) 206 dir_temp = tempfile.mkdtemp(filename)
213 filename_tmp = os.path.join(dir_temp, 'matrix.dat') 207 filename_tmp = os.path.join(dir_temp, 'matrix.dat')
214 208
215 - project = {}  
216 -  
217 - for key in self.__dict__:  
218 - if getattr(self.__dict__[key], 'SavePlist', None):  
219 - project[key] = {'#plist':  
220 - self.__dict__[key].SavePlist(filename_tmp).decode('utf-8')}  
221 - elif key == 'dicom_sample':  
222 - #sample_path = os.path.join(dir_temp, 'sample.dcm')  
223 - #shutil.copy(self.dicom_sample.parser.filename,sample_path)  
224 - #os.chmod(sample_path, stat.S_IREAD|stat.S_IWRITE)  
225 -  
226 - #project[key] = 'sample.dcm'  
227 - pass  
228 - elif key.startswith('matrix'):  
229 - continue  
230 - else:  
231 - project[key] = self.__dict__[key] 209 + project = {
  210 + # Format info
  211 + "format_version": 1,
  212 + "invesalius_version": "invesalius3b3",
  213 + "date": datetime.datetime.now().isoformat(),
  214 +
  215 + # case info
  216 + "name": self.name, # patient's name
  217 + "modality": self.modality, # CT, RMI, ...
  218 + "orientation": self.original_orientation,
  219 + "window_width": self.window,
  220 + "window_level": self.level,
  221 + "scalar_range": self.threshold_range,
  222 + "spacing": self.spacing,
  223 + }
  224 +
  225 + # Saving the matrix containing the slices
  226 + matrix = {'filename': u'matrix.dat',
  227 + 'shape': self.matrix_shape,
  228 + 'dtype': self.matrix_dtype,
  229 + }
  230 + project['matrix'] = matrix
  231 + shutil.copyfile(self.matrix_filename, filename_tmp)
232 232
  233 + # Saving the masks
233 masks = {} 234 masks = {}
234 for index in self.mask_dict: 235 for index in self.mask_dict:
235 - masks[str(index)] = {'#mask':\  
236 - self.mask_dict[index].SavePlist(filename_tmp)} 236 + masks[str(index)] = self.mask_dict[index].SavePlist(dir_temp)
  237 + project['masks'] = masks
237 238
  239 + # Saving the surfaces
238 surfaces = {} 240 surfaces = {}
239 for index in self.surface_dict: 241 for index in self.surface_dict:
240 - surfaces[str(index)] = {'#surface':\  
241 - self.surface_dict[index].SavePlist(filename_tmp)}  
242 -  
243 - project['surface_dict'] = surfaces  
244 - project['mask_dict'] = masks  
245 - project['measurement_dict'] = self.GetMeasuresDict()  
246 - shutil.copyfile(self.matrix_filename, filename_tmp)  
247 - project['matrix'] = {'filename':os.path.split(filename_tmp)[1].decode('utf-8'),  
248 - 'shape': self.matrix_shape,  
249 - 'dtype': self.matrix_dtype} 242 + surfaces[str(index)] = self.surface_dict[index].SavePlist(dir_temp)
  243 + project['surfaces'] = surfaces
  244 +
  245 + # Saving the measurements
  246 + measurements = self.GetMeasuresDict()
  247 + measurements_filename = 'measurements.plist'
  248 + plistlib.writePlist(measurements,
  249 + os.path.join(dir_temp, measurements_filename))
  250 + project['measurements'] = measurements_filename
  251 +
  252 + # Saving the annotations (empty in this version)
  253 + project['annotations'] = {}
250 254
251 - plistlib.writePlist(project, filename_tmp + '.plist') 255 + # Saving the main plist
  256 + plistlib.writePlist(project, os.path.join(dir_temp, 'main.plist'))
252 257
  258 + # Compressing and generating the .inv3 file
253 path = os.path.join(dir_,filename) 259 path = os.path.join(dir_,filename)
254 - Compress(dir_temp, path)#os.path.join("~/Desktop/","teste.inv3")) 260 + Compress(dir_temp, path)
  261 +
  262 + # Removing the temp folder.
255 shutil.rmtree(dir_temp) 263 shutil.rmtree(dir_temp)
256 264
  265 +
257 def OpenPlistProject(self, filename): 266 def OpenPlistProject(self, filename):
258 import data.measures as ms 267 import data.measures as ms
259 268
@@ -265,57 +274,53 @@ class Project(object): @@ -265,57 +274,53 @@ class Project(object):
265 ow.SetInstance(fow) 274 ow.SetInstance(fow)
266 275
267 filelist = Extract(filename, tempfile.mkdtemp()) 276 filelist = Extract(filename, tempfile.mkdtemp())
268 - main_plist = min(filter(lambda x: x.endswith('.plist'), filelist),  
269 - key=lambda x: len(x)) 277 + dirpath = os.path.abspath(os.path.split(filelist[0])[0]).decode(wx.GetDefaultPyEncoding())
  278 +
  279 + # Opening the main file from invesalius 3 project
  280 + main_plist = os.path.join(dirpath ,'main.plist')
270 project = plistlib.readPlist(main_plist) 281 project = plistlib.readPlist(main_plist)
271 - dirpath = os.path.abspath(os.path.split(filelist[0])[0])  
272 -  
273 - for key in project:  
274 - if key == 'matrix':  
275 - filepath = os.path.split(project[key]["filename"])[-1]  
276 - path = os.path.join(dirpath, filepath)  
277 - self.matrix_filename = path  
278 - self.matrix_shape = project[key]['shape']  
279 - self.matrix_dtype = project[key]['dtype']  
280 - elif key == 'presets':  
281 - filepath = os.path.split(project[key]["#plist"])[-1]  
282 - path = os.path.join(dirpath, filepath)  
283 - p = Presets()  
284 - p.OpenPlist(path)  
285 - self.presets = p  
286 - #elif key == 'dicom_sample':  
287 - #path = os.path.join(dirpath, project[key])  
288 - #p = dicom.Parser()  
289 - #p.SetFileName(path)  
290 - #d = dicom.Dicom()  
291 - #d.SetParser(p)  
292 - #self.dicom_sample = d  
293 -  
294 - elif key == 'mask_dict':  
295 - self.mask_dict = {}  
296 - for mask in project[key]:  
297 - filepath = os.path.split(project[key][mask]["#mask"])[-1]  
298 - path = os.path.join(dirpath, filepath)  
299 - m = msk.Mask()  
300 - m.OpenPList(path)  
301 - self.mask_dict[m.index] = m  
302 - elif key == 'surface_dict':  
303 - self.surface_dict = {}  
304 - for surface in project[key]:  
305 - filepath = os.path.split(project[key][surface]["#surface"])[-1]  
306 - path = os.path.join(dirpath, filepath)  
307 - s = srf.Surface()  
308 - s.OpenPList(path)  
309 - self.surface_dict[s.index] = s  
310 - elif key == 'measurement_dict':  
311 - self.measurement_dict = {}  
312 - d = project['measurement_dict']  
313 - for index in d:  
314 - measure = ms.Measurement()  
315 - measure.Load(d[index])  
316 - self.measurement_dict[int(index)] = measure  
317 - else:  
318 - setattr(self, key, project[key]) 282 +
  283 + # case info
  284 + self.name = project["name"]
  285 + self.modality = project["modality"]
  286 + self.original_orientation = project["orientation"]
  287 + self.window = project["window_width"]
  288 + self.level = project["window_level"]
  289 + self.threshold_range = project["scalar_range"]
  290 + self.spacing = project["spacing"]
  291 +
  292 + # Opening the matrix containing the slices
  293 + filepath = os.path.join(dirpath, project["matrix"]["filename"])
  294 + self.matrix_filename = filepath
  295 + self.matrix_shape = project["matrix"]['shape']
  296 + self.matrix_dtype = project["matrix"]['dtype']
  297 +
  298 + # Opening the masks
  299 + self.mask_dict = {}
  300 + for index in project["masks"]:
  301 + filename = project["masks"][index]
  302 + filepath = os.path.join(dirpath, filename)
  303 + m = msk.Mask()
  304 + m.OpenPList(filepath)
  305 + self.mask_dict[m.index] = m
  306 + self.surface_dict = {}
  307 +
  308 + # Opening the surfaces
  309 + for index in project["surfaces"]:
  310 + filename = project["surfaces"][index]
  311 + filepath = os.path.join(dirpath, filename)
  312 + s = srf.Surface()
  313 + s.OpenPList(filepath)
  314 + self.surface_dict[s.index] = s
  315 +
  316 + # Opening the measurements
  317 + self.measurement_dict = {}
  318 + measurements = plistlib.readPlist(os.path.join(dirpath,
  319 + project["measurements"]))
  320 + for index in measurements:
  321 + measure = ms.Measurement()
  322 + measure.Load(measurements[index])
  323 + self.measurement_dict[int(index)] = measure
319 324
320 325
321 def Compress(folder, filename): 326 def Compress(folder, filename):
@@ -323,8 +328,8 @@ def Compress(folder, filename): @@ -323,8 +328,8 @@ def Compress(folder, filename):
323 current_dir = os.path.abspath(".") 328 current_dir = os.path.abspath(".")
324 os.chdir(tmpdir) 329 os.chdir(tmpdir)
325 file_list = glob.glob(os.path.join(tmpdir_,"*")) 330 file_list = glob.glob(os.path.join(tmpdir_,"*"))
326 -  
327 - tar = tarfile.open(tmpdir_ + ".inv3", "w:gz") 331 + tar_filename = tmpdir_ + ".inv3"
  332 + tar = tarfile.open(tar_filename.encode(wx.GetDefaultPyEncoding()), "w:gz")
328 for name in file_list: 333 for name in file_list:
329 tar.add(name) 334 tar.add(name)
330 tar.close() 335 tar.close()