Commit 1aaf2ccb100d3fcc4fee87eb4f0f9bc58f8d78ef
1 parent
f739fdf5
Exists in
master
and in
67 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() | ... | ... |