Commit 811f7ad57e638f74e6df27ce4f348ea77c84f5b0

Authored by Renan
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
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  
... ...