Commit 1aaf2ccb100d3fcc4fee87eb4f0f9bc58f8d78ef
1 parent
f739fdf5
Exists in
master
and in
5 other branches
ENH: Saving InVesalius3 using the proposed file format
Showing
5 changed files
with
152 additions
and
136 deletions
Show diff stats
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() |