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 | 51 | Publisher.subscribe(self.OnFlipVolume, 'Flip volume') |
52 | 52 | Publisher.subscribe(self.OnSwapVolumeAxes, 'Swap volume axes') |
53 | 53 | |
54 | - def SavePlist(self, filename): | |
54 | + def SavePlist(self, dir_temp): | |
55 | 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 | 61 | mask['index'] = self.index |
62 | + mask['name'] = self.name | |
61 | 63 | mask['colour'] = self.colour |
62 | 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 | 76 | def OpenPList(self, filename): |
74 | 77 | mask = plistlib.readPlist(filename) |
75 | 78 | |
76 | 79 | self.index = mask['index'] |
80 | + self.name = mask['name'] | |
77 | 81 | self.colour = mask['colour'] |
78 | 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 | 88 | dirpath = os.path.abspath(os.path.split(filename)[0]) |
86 | 89 | path = os.path.join(dirpath, mask_file) |
87 | 90 | self._open_mask(path, tuple(shape)) | ... | ... |
invesalius/data/measures.py
... | ... | @@ -198,7 +198,7 @@ class Measurement(): |
198 | 198 | self.type = info["type"] |
199 | 199 | self.slice_number = info["slice_number"] |
200 | 200 | self.points = info["points"] |
201 | - self.is_shown = info["is_shown"] | |
201 | + self.is_shown = info["visible"] | |
202 | 202 | |
203 | 203 | class CirclePointRepresentation(object): |
204 | 204 | """ | ... | ... |
invesalius/data/polydata_utils.py
... | ... | @@ -17,7 +17,10 @@ |
17 | 17 | # detalhes. |
18 | 18 | #-------------------------------------------------------------------------- |
19 | 19 | |
20 | +import sys | |
21 | + | |
20 | 22 | import vtk |
23 | +import wx | |
21 | 24 | from wx.lib.pubsub import pub as Publisher |
22 | 25 | |
23 | 26 | import vtk_utils as vu |
... | ... | @@ -103,7 +106,8 @@ def Merge(polydata_list): |
103 | 106 | |
104 | 107 | def Export(polydata, filename, bin=False): |
105 | 108 | writer = vtk.vtkXMLPolyDataWriter() |
106 | - writer.SetFileName(filename) | |
109 | + print filename, type(filename) | |
110 | + writer.SetFileName(filename.encode('utf-8')) | |
107 | 111 | if bin: |
108 | 112 | writer.SetDataModeToBinary() |
109 | 113 | else: |
... | ... | @@ -113,7 +117,7 @@ def Export(polydata, filename, bin=False): |
113 | 117 | |
114 | 118 | def Import(filename): |
115 | 119 | reader = vtk.vtkXMLPolyDataReader() |
116 | - reader.SetFileName(filename) | |
120 | + reader.SetFileName(filename.encode('utf-8')) | |
117 | 121 | reader.Update() |
118 | 122 | return reader.GetOutput() |
119 | 123 | ... | ... |
invesalius/data/surface.py
... | ... | @@ -62,32 +62,35 @@ class Surface(): |
62 | 62 | else: |
63 | 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 | 83 | def OpenPList(self, filename): |
82 | - surface = plistlib.readPlist(filename) | |
84 | + sp = plistlib.readPlist(filename) | |
83 | 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 | 95 | def _set_class_index(self, index): |
93 | 96 | Surface.general_index = index |
... | ... | @@ -319,6 +322,7 @@ class SurfaceManager(): |
319 | 322 | self.last_surface_index = index |
320 | 323 | self.ShowActor(index, True) |
321 | 324 | |
325 | + | |
322 | 326 | def OnLoadSurfaceDict(self, pubsub_evt): |
323 | 327 | surface_dict = pubsub_evt.data |
324 | 328 | for key in surface_dict: | ... | ... |
invesalius/project.py
... | ... | @@ -17,6 +17,7 @@ |
17 | 17 | # detalhes. |
18 | 18 | #-------------------------------------------------------------------------- |
19 | 19 | |
20 | +import datetime | |
20 | 21 | import glob |
21 | 22 | import os |
22 | 23 | import plistlib |
... | ... | @@ -49,8 +50,6 @@ class Project(object): |
49 | 50 | self.dicom_sample = '' |
50 | 51 | self.modality = '' |
51 | 52 | self.original_orientation = '' |
52 | - self.min_threshold = '' | |
53 | - self.max_threshold = '' | |
54 | 53 | self.window = '' |
55 | 54 | self.level = '' |
56 | 55 | |
... | ... | @@ -199,61 +198,71 @@ class Project(object): |
199 | 198 | item["type"] = m.type |
200 | 199 | item["slice_number"] = m.slice_number |
201 | 200 | item["points"] = m.points |
202 | - item["is_shown"] = m.is_shown | |
201 | + item["visible"] = m.is_shown | |
203 | 202 | measures[str(m.index)] = item |
204 | 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 | 206 | dir_temp = tempfile.mkdtemp(filename) |
213 | 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 | 234 | masks = {} |
234 | 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 | 240 | surfaces = {} |
239 | 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 | 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 | 263 | shutil.rmtree(dir_temp) |
256 | 264 | |
265 | + | |
257 | 266 | def OpenPlistProject(self, filename): |
258 | 267 | import data.measures as ms |
259 | 268 | |
... | ... | @@ -265,57 +274,53 @@ class Project(object): |
265 | 274 | ow.SetInstance(fow) |
266 | 275 | |
267 | 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 | 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 | 326 | def Compress(folder, filename): |
... | ... | @@ -323,8 +328,8 @@ def Compress(folder, filename): |
323 | 328 | current_dir = os.path.abspath(".") |
324 | 329 | os.chdir(tmpdir) |
325 | 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 | 333 | for name in file_list: |
329 | 334 | tar.add(name) |
330 | 335 | tar.close() | ... | ... |