Commit 811f7ad57e638f74e6df27ce4f348ea77c84f5b0
Committed by
Thiago Franco de Moraes
1 parent
38ef0b76
Exists in
master
Transform surface physical into inv coord system (#164)
* Import surface transforming from physical to InV coord system * changed defaultValue to value arg for edit marker ID -defaultValue is not valid for wx.TextEntryDialog * created message dialog to check the coordinate systems of the imported mesh * Added NIfTI affine matrix to main plist invesalius project * update dialog text * Update transformed polydata * Improvements * Transform dialog works only with imported image with affine matrix
Showing
6 changed files
with
75 additions
and
5 deletions
Show diff stats
invesalius/control.py
... | ... | @@ -657,6 +657,7 @@ class Controller(): |
657 | 657 | proj.level = self.Slice.window_level |
658 | 658 | proj.threshold_range = int(matrix.min()), int(matrix.max()) |
659 | 659 | proj.spacing = self.Slice.spacing |
660 | + proj.affine = self.affine.tolist() | |
660 | 661 | |
661 | 662 | ###### |
662 | 663 | session = ses.Session() |
... | ... | @@ -860,6 +861,14 @@ class Controller(): |
860 | 861 | self.matrix, scalar_range, self.filename = image_utils.img2memmap(group) |
861 | 862 | |
862 | 863 | hdr = group.header |
864 | + if group.affine.any(): | |
865 | + from numpy import hstack | |
866 | + from numpy.linalg import inv | |
867 | + affine = inv(group.affine) | |
868 | + affine[1, 3] = -affine[1, 3] | |
869 | + self.affine = hstack(affine) | |
870 | + Publisher.sendMessage('Update affine matrix', | |
871 | + affine=self.affine, status=True) | |
863 | 872 | hdr.set_data_dtype('int16') |
864 | 873 | dims = hdr.get_zooms() |
865 | 874 | dimsf = tuple([float(s) for s in dims]) | ... | ... |
invesalius/data/surface.py
... | ... | @@ -157,6 +157,8 @@ class SurfaceManager(): |
157 | 157 | def __init__(self): |
158 | 158 | self.actors_dict = {} |
159 | 159 | self.last_surface_index = 0 |
160 | + self.affine = None | |
161 | + self.converttoInV = None | |
160 | 162 | self.__bind_events() |
161 | 163 | |
162 | 164 | self._default_parameters = { |
... | ... | @@ -206,6 +208,9 @@ class SurfaceManager(): |
206 | 208 | |
207 | 209 | Publisher.subscribe(self.OnImportSurfaceFile, 'Import surface file') |
208 | 210 | |
211 | + Publisher.subscribe(self.UpdateAffineMatrix, 'Update affine matrix') | |
212 | + Publisher.subscribe(self.UpdateconverttoInVflag, 'Update converttoInV flag') | |
213 | + | |
209 | 214 | def OnDuplicate(self, surface_indexes): |
210 | 215 | proj = prj.Project() |
211 | 216 | surface_dict = proj.surface_dict |
... | ... | @@ -303,6 +308,7 @@ class SurfaceManager(): |
303 | 308 | self.CreateSurfaceFromFile(filename) |
304 | 309 | |
305 | 310 | def CreateSurfaceFromFile(self, filename): |
311 | + scalar = False | |
306 | 312 | if filename.lower().endswith('.stl'): |
307 | 313 | reader = vtk.vtkSTLReader() |
308 | 314 | elif filename.lower().endswith('.ply'): |
... | ... | @@ -311,6 +317,7 @@ class SurfaceManager(): |
311 | 317 | reader = vtk.vtkOBJReader() |
312 | 318 | elif filename.lower().endswith('.vtp'): |
313 | 319 | reader = vtk.vtkXMLPolyDataReader() |
320 | + scalar = True | |
314 | 321 | else: |
315 | 322 | wx.MessageBox(_("File format not reconized by InVesalius"), _("Import surface error")) |
316 | 323 | return |
... | ... | @@ -327,11 +334,27 @@ class SurfaceManager(): |
327 | 334 | wx.MessageBox(_("InVesalius was not able to import this surface"), _("Import surface error")) |
328 | 335 | else: |
329 | 336 | name = os.path.splitext(os.path.split(filename)[-1])[0] |
330 | - self.CreateSurfaceFromPolydata(polydata, name=name) | |
337 | + self.CreateSurfaceFromPolydata(polydata, name=name, scalar=scalar) | |
338 | + | |
339 | + def UpdateAffineMatrix(self, affine, status): | |
340 | + self.affine = affine | |
341 | + | |
342 | + def UpdateconverttoInVflag(self, converttoInV): | |
343 | + self.converttoInV = converttoInV | |
331 | 344 | |
332 | 345 | def CreateSurfaceFromPolydata(self, polydata, overwrite=False, |
333 | 346 | name=None, colour=None, |
334 | - transparency=None, volume=None, area=None): | |
347 | + transparency=None, volume=None, area=None, scalar=False): | |
348 | + if self.converttoInV and self.affine is not None: | |
349 | + transform = vtk.vtkTransform() | |
350 | + transform.SetMatrix(self.affine) | |
351 | + transformFilter = vtk.vtkTransformPolyDataFilter() | |
352 | + transformFilter.SetTransform(transform) | |
353 | + transformFilter.SetInputData(polydata) | |
354 | + transformFilter.Update() | |
355 | + polydata = transformFilter.GetOutput() | |
356 | + self.converttoInV = None | |
357 | + | |
335 | 358 | normals = vtk.vtkPolyDataNormals() |
336 | 359 | normals.SetInputData(polydata) |
337 | 360 | normals.SetFeatureAngle(80) |
... | ... | @@ -340,7 +363,10 @@ class SurfaceManager(): |
340 | 363 | |
341 | 364 | mapper = vtk.vtkPolyDataMapper() |
342 | 365 | mapper.SetInputData(normals.GetOutput()) |
343 | - mapper.ScalarVisibilityOff() | |
366 | + if scalar: | |
367 | + mapper.ScalarVisibilityOn() | |
368 | + else: | |
369 | + mapper.ScalarVisibilityOff() | |
344 | 370 | mapper.ImmediateModeRenderingOn() # improve performance |
345 | 371 | |
346 | 372 | actor = vtk.vtkActor() |
... | ... | @@ -419,6 +445,9 @@ class SurfaceManager(): |
419 | 445 | # restarting the surface index |
420 | 446 | Surface.general_index = -1 |
421 | 447 | |
448 | + Publisher.sendMessage('Update affine matrix', | |
449 | + affine=None, status=False) | |
450 | + | |
422 | 451 | def OnSelectSurface(self, surface_index): |
423 | 452 | #self.last_surface_index = surface_index |
424 | 453 | # self.actors_dict. | ... | ... |
invesalius/data/trackers.py
... | ... | @@ -146,7 +146,7 @@ def PlhWrapperConnection(tracker_id): |
146 | 146 | def PlhSerialConnection(tracker_id): |
147 | 147 | import serial |
148 | 148 | |
149 | - trck_init = serial.Serial('COM1', baudrate=115200, timeout=0.03) | |
149 | + trck_init = serial.Serial('COM3', baudrate=115200, timeout=0.03) | |
150 | 150 | |
151 | 151 | if tracker_id == 2: |
152 | 152 | # Polhemus FASTRAK needs configurations first | ... | ... |
invesalius/gui/data_notebook.py
... | ... | @@ -606,8 +606,13 @@ class SurfaceButtonControlPanel(wx.Panel): |
606 | 606 | wx.Panel.__init__(self, parent, pos=wx.Point(0, 50), |
607 | 607 | size=wx.Size(256, 22)) |
608 | 608 | self.parent = parent |
609 | + self.affinestatus = False | |
610 | + self.__init_evt() | |
609 | 611 | self.__init_gui() |
610 | 612 | |
613 | + def __init_evt(self): | |
614 | + Publisher.subscribe(self.AffineStatus, | |
615 | + 'Update affine matrix') | |
611 | 616 | def __init_gui(self): |
612 | 617 | |
613 | 618 | # Bitmaps to be used in plate buttons |
... | ... | @@ -703,8 +708,13 @@ class SurfaceButtonControlPanel(wx.Panel): |
703 | 708 | def OnOpenMesh(self): |
704 | 709 | filename = dlg.ShowImportMeshFilesDialog() |
705 | 710 | if filename: |
711 | + if self.affinestatus: | |
712 | + converttoInV = dlg.ImportMeshCoordSystem() | |
713 | + Publisher.sendMessage('Update converttoInV flag', converttoInV=converttoInV) | |
706 | 714 | Publisher.sendMessage('Import surface file', filename=filename) |
707 | 715 | |
716 | + def AffineStatus(self, affine, status): | |
717 | + self.affinestatus = status | |
708 | 718 | |
709 | 719 | class SurfacesListCtrlPanel(wx.ListCtrl, listmix.TextEditMixin, listmix.CheckListCtrlMixin): |
710 | 720 | ... | ... |
invesalius/gui/dialogs.py
... | ... | @@ -467,6 +467,22 @@ def ShowImportMeshFilesDialog(): |
467 | 467 | os.chdir(current_dir) |
468 | 468 | return filename |
469 | 469 | |
470 | +def ImportMeshCoordSystem(): | |
471 | + msg = _("Was the imported mesh created by InVesalius?") | |
472 | + if sys.platform == 'darwin': | |
473 | + dlg = wx.MessageDialog(None, "", msg, | |
474 | + wx.YES_NO) | |
475 | + else: | |
476 | + dlg = wx.MessageDialog(None, msg, "InVesalius 3", | |
477 | + wx.YES_NO) | |
478 | + | |
479 | + if dlg.ShowModal() == wx.ID_YES: | |
480 | + flag = False | |
481 | + else: | |
482 | + flag = True | |
483 | + | |
484 | + dlg.Destroy() | |
485 | + return flag | |
470 | 486 | |
471 | 487 | def ShowSaveAsProjectDialog(default_filename=None): |
472 | 488 | current_dir = os.path.abspath(".") |
... | ... | @@ -1030,7 +1046,7 @@ def EnterMarkerID(default): |
1030 | 1046 | if sys.platform == 'darwin': |
1031 | 1047 | dlg = wx.TextEntryDialog(None, "", msg, defaultValue=default) |
1032 | 1048 | else: |
1033 | - dlg = wx.TextEntryDialog(None, msg, "InVesalius 3", defaultValue=default) | |
1049 | + dlg = wx.TextEntryDialog(None, msg, "InVesalius 3", value=default) | |
1034 | 1050 | dlg.ShowModal() |
1035 | 1051 | result = dlg.GetValue() |
1036 | 1052 | dlg.Destroy() | ... | ... |
invesalius/project.py
... | ... | @@ -58,6 +58,7 @@ class Project(with_metaclass(Singleton, object)): |
58 | 58 | self.original_orientation = '' |
59 | 59 | self.window = '' |
60 | 60 | self.level = '' |
61 | + self.affine = '' | |
61 | 62 | |
62 | 63 | # Masks (vtkImageData) |
63 | 64 | self.mask_dict = {} |
... | ... | @@ -218,6 +219,7 @@ class Project(with_metaclass(Singleton, object)): |
218 | 219 | "window_level": self.level, |
219 | 220 | "scalar_range": self.threshold_range, |
220 | 221 | "spacing": self.spacing, |
222 | + "affine": self.affine, | |
221 | 223 | } |
222 | 224 | |
223 | 225 | # Saving the matrix containing the slices |
... | ... | @@ -304,6 +306,10 @@ class Project(with_metaclass(Singleton, object)): |
304 | 306 | self.level = project["window_level"] |
305 | 307 | self.threshold_range = project["scalar_range"] |
306 | 308 | self.spacing = project["spacing"] |
309 | + if project.get("affine"): | |
310 | + self.affine = project["affine"] | |
311 | + Publisher.sendMessage('Update affine matrix', | |
312 | + affine=self.affine, status=True) | |
307 | 313 | |
308 | 314 | self.compress = project.get("compress", True) |
309 | 315 | ... | ... |