Commit f2fd855bbca344713a5b73d803b4e282e06f92b6

Authored by Renan
Committed by GitHub
1 parent 7fae7387
Exists in master

Navigation ICP coregistration improvements (#299)

* MOD: Get raw coordinates to create points from probe

* ENH: Improve processing data with a new thread
    -ADD: Enable/Disable Done and Apply buttons

* MOD: show the amount of poses recorded

* FIX: removes ICP matrix after disconnect tracker
invesalius/data/vtk_utils.py
... ... @@ -148,6 +148,9 @@ class Text(object):
148 148 except(UnicodeEncodeError):
149 149 self.mapper.SetInput(value.encode("utf-8", errors='replace'))
150 150  
  151 + def GetValue(self):
  152 + return self.mapper.GetInput()
  153 +
151 154 def SetCoilDistanceValue(self, value):
152 155 #TODO: Not being used anymore. Can be deleted.
153 156 if isinstance(value, int) or isinstance(value, float):
... ...
invesalius/gui/dialogs.py
... ... @@ -59,7 +59,8 @@ import invesalius.data.coordinates as dco
59 59 import invesalius.gui.widgets.gradient as grad
60 60 import invesalius.session as ses
61 61 import invesalius.utils as utils
62   -import invesalius.data.bases as bases
  62 +import invesalius.data.vtk_utils as vtku
  63 +import invesalius.data.coregistration as dcr
63 64 from invesalius.gui.widgets.inv_spinctrl import InvSpinCtrl, InvFloatSpinCtrl
64 65 from invesalius.gui.widgets import clut_imagedata
65 66 from invesalius.gui.widgets.clut_imagedata import CLUTImageDataWidget, EVT_CLUT_NODE_CHANGED
... ... @@ -3536,10 +3537,9 @@ class ICPCorregistrationDialog(wx.Dialog):
3536 3537 def __init__(self, nav_prop):
3537 3538 import invesalius.project as prj
3538 3539  
3539   - self.__bind_events()
3540   -
3541   - self.tracker_id = nav_prop[0]
3542   - self.trk_init = nav_prop[1]
  3540 + self.m_change = nav_prop[0]
  3541 + self.tracker_id = nav_prop[1]
  3542 + self.trk_init = nav_prop[2]
3543 3543 self.obj_ref_id = 2
3544 3544 self.obj_name = None
3545 3545 self.obj_actor = None
... ... @@ -3563,12 +3563,6 @@ class ICPCorregistrationDialog(wx.Dialog):
3563 3563  
3564 3564 self._init_gui()
3565 3565  
3566   - def __bind_events(self):
3567   - Publisher.subscribe(self.UpdateCurrentCoord, 'Set cross focal point')
3568   -
3569   - def UpdateCurrentCoord(self, position):
3570   - self.current_coord = position[:]
3571   -
3572 3566 def _init_gui(self):
3573 3567 self.interactor = wxVTKRenderWindowInteractor(self, -1, size=self.GetSize())
3574 3568 self.interactor.Enable(1)
... ... @@ -3617,12 +3611,15 @@ class ICPCorregistrationDialog(wx.Dialog):
3617 3611 btn_reset.Bind(wx.EVT_BUTTON, self.OnReset)
3618 3612  
3619 3613 btn_apply_icp = wx.Button(self, -1, label=_('Apply registration'))
3620   - btn_apply_icp.Bind(wx.EVT_BUTTON, self.OnICP)
  3614 + btn_apply_icp.Bind(wx.EVT_BUTTON, self.thread_ICP_start, btn_apply_icp)
  3615 + btn_apply_icp.Enable(False)
  3616 + self.btn_apply_icp = btn_apply_icp
3621 3617  
3622   - # Buttons to finish or cancel object registration
3623 3618 tooltip = wx.ToolTip(_(u"Refine done"))
3624 3619 btn_ok = wx.Button(self, wx.ID_OK, _(u"Done"))
3625 3620 btn_ok.SetToolTip(tooltip)
  3621 + btn_ok.Enable(False)
  3622 + self.btn_ok = btn_ok
3626 3623  
3627 3624 btn_cancel = wx.Button(self, wx.ID_CANCEL)
3628 3625 btn_cancel.SetHelpText("")
... ... @@ -3667,20 +3664,41 @@ class ICPCorregistrationDialog(wx.Dialog):
3667 3664 obj_actor.SetMapper(mapper)
3668 3665 self.obj_actor = obj_actor
3669 3666  
  3667 + poses_recorded = vtku.Text()
  3668 + poses_recorded.SetSize(const.TEXT_SIZE_LARGE)
  3669 + poses_recorded.SetPosition((const.X, const.Y))
  3670 + poses_recorded.ShadowOff()
  3671 + poses_recorded.SetValue("Poses recorded: ")
  3672 +
  3673 + collect_points = vtku.Text()
  3674 + collect_points.SetSize(const.TEXT_SIZE_LARGE)
  3675 + collect_points.SetPosition((const.X+0.35, const.Y))
  3676 + collect_points.ShadowOff()
  3677 + collect_points.SetValue("0")
  3678 + self.collect_points = collect_points
  3679 +
3670 3680 self.ren.AddActor(obj_actor)
  3681 + self.ren.AddActor(poses_recorded.actor)
  3682 + self.ren.AddActor(collect_points.actor)
3671 3683 self.ren.ResetCamera()
3672 3684 self.interactor.Render()
3673 3685  
3674 3686 def RemoveActor(self):
3675   - #self.ren.RemoveActor(self.obj_actor)
3676 3687 self.ren.RemoveAllViewProps()
3677 3688 self.point_coord = []
3678 3689 self.transformed_points = []
3679 3690 self.m_icp = None
3680 3691 self.SetProgress(0)
  3692 + self.btn_apply_icp.Enable(False)
  3693 + self.btn_ok.Enable(False)
3681 3694 self.ren.ResetCamera()
3682 3695 self.interactor.Render()
3683 3696  
  3697 + def GetCurrentCoord(self):
  3698 + coord_raw = dco.GetCoordinates(self.trk_init, self.tracker_id, const.DYNAMIC_REF)
  3699 + coord, _ = dcr.corregistrate_dynamic((self.m_change, 0), coord_raw, const.DEFAULT_REF_MODE, [None, None])
  3700 + return coord[:3]
  3701 +
3684 3702 def AddMarker(self, size, colour, coord):
3685 3703 """
3686 3704 Points are rendered into the scene. These points give visual information about the registration.
... ... @@ -3713,11 +3731,19 @@ class ICPCorregistrationDialog(wx.Dialog):
3713 3731 self.ren.AddActor(sphere_actor)
3714 3732 self.point_coord.append([x, y, z])
3715 3733  
3716   - self.Refresh()
  3734 + self.collect_points.SetValue(str(int(self.collect_points.GetValue()) + 1))
  3735 +
  3736 + self.interactor.Render()
  3737 +
  3738 + if len(self.point_coord) >= 5 and self.btn_apply_icp.IsEnabled() is False:
  3739 + self.btn_apply_icp.Enable(True)
  3740 +
  3741 + if self.progress.GetValue() != 0:
  3742 + self.SetProgress(0)
3717 3743  
3718 3744 def SetProgress(self, progress):
3719 3745 self.progress.SetValue(progress * 100)
3720   - self.Refresh()
  3746 + self.interactor.Render()
3721 3747  
3722 3748 def vtkmatrix_to_numpy(self, matrix):
3723 3749 """
... ... @@ -3759,7 +3785,8 @@ class ICPCorregistrationDialog(wx.Dialog):
3759 3785  
3760 3786 cam.SetFocalPoint(cam_focus)
3761 3787 cam.SetPosition(cam_pos)
3762   - self.Refresh()
  3788 +
  3789 + self.interactor.Render()
3763 3790  
3764 3791 def ErrorEstimation(self, surface, points):
3765 3792 """
... ... @@ -3804,22 +3831,30 @@ class ICPCorregistrationDialog(wx.Dialog):
3804 3831 self.timer.Stop()
3805 3832  
3806 3833 def OnUpdate(self, evt):
3807   - self.AddMarker(3, (1, 0, 0), self.current_coord[:3])
3808   - self.SetCameraVolume(self.current_coord[:3])
  3834 + current_coord = self.GetCurrentCoord()
  3835 + self.AddMarker(3, (1, 0, 0), current_coord)
  3836 + self.SetCameraVolume(current_coord)
3809 3837  
3810 3838 def OnCreatePoint(self, evt):
3811   - self.AddMarker(3,(1,0,0),self.current_coord[:3])
3812   - self.SetCameraVolume(self.current_coord[:3])
  3839 + current_coord = self.GetCurrentCoord()
  3840 + self.AddMarker(3, (1, 0, 0), current_coord)
  3841 + self.SetCameraVolume(current_coord)
3813 3842  
3814 3843 def OnReset(self, evt):
  3844 + if self.cont_point:
  3845 + self.cont_point.SetValue(False)
  3846 + self.OnContinuousAcquisition(evt=None, btn=self.cont_point)
  3847 +
3815 3848 self.RemoveActor()
3816 3849 self.LoadActor()
3817 3850  
3818   - def OnICP(self, evt):
3819   - self.SetProgress(0.3)
  3851 + def OnICP(self):
3820 3852 if self.cont_point:
3821 3853 self.cont_point.SetValue(False)
3822 3854 self.OnContinuousAcquisition(evt=None, btn=self.cont_point)
  3855 +
  3856 + self.SetProgress(0.3)
  3857 +
3823 3858 sourcePoints = np.array(self.point_coord)
3824 3859 sourcePoints_vtk = vtk.vtkPoints()
3825 3860  
... ... @@ -3833,6 +3868,8 @@ class ICPCorregistrationDialog(wx.Dialog):
3833 3868 icp.SetSource(source)
3834 3869 icp.SetTarget(self.surface)
3835 3870  
  3871 + self.SetProgress(0.5)
  3872 +
3836 3873 if self.icp_mode == 0:
3837 3874 print("Affine mode")
3838 3875 icp.GetLandmarkTransform().SetModeToAffine()
... ... @@ -3860,7 +3897,6 @@ class ICPCorregistrationDialog(wx.Dialog):
3860 3897  
3861 3898 transformedSource = icpTransformFilter.GetOutput()
3862 3899  
3863   - self.SetProgress(1)
3864 3900  
3865 3901 for i in range(transformedSource.GetNumberOfPoints()):
3866 3902 p = [0, 0, 0]
... ... @@ -3884,7 +3920,16 @@ class ICPCorregistrationDialog(wx.Dialog):
3884 3920 self.prev_error = self.ErrorEstimation(self.surface, sourcePoints)
3885 3921 self.final_error = self.ErrorEstimation(self.surface, self.transformed_points)
3886 3922  
3887   - self.Refresh()
  3923 + self.interactor.Render()
  3924 +
  3925 + self.SetProgress(1)
  3926 +
  3927 + self.btn_ok.Enable(True)
  3928 +
  3929 + def thread_ICP_start(self, evt):
  3930 + import threading
  3931 + th = threading.Thread(target=self.OnICP, args=[])
  3932 + th.start()
3888 3933  
3889 3934 def GetValue(self):
3890 3935 return self.m_icp, self.point_coord, self.transformed_points, self.prev_error, self.final_error
... ...
invesalius/gui/task_navigator.py
... ... @@ -691,6 +691,7 @@ class NeuronavigationPanel(wx.Panel):
691 691 # has been initialized before
692 692 if trck and choice != const.DISCTRACK:
693 693 self.ResetTrackerFiducials()
  694 + self.ResetIcp()
694 695 Publisher.sendMessage('Update status text in GUI',
695 696 label=_("Disconnecting tracker..."))
696 697 Publisher.sendMessage('Remove sensors ID')
... ... @@ -711,6 +712,7 @@ class NeuronavigationPanel(wx.Panel):
711 712 elif choice == const.DISCTRACK:
712 713 if trck:
713 714 self.ResetTrackerFiducials()
  715 + self.ResetIcp()
714 716 Publisher.sendMessage('Update status text in GUI',
715 717 label=_("Disconnecting tracker ..."))
716 718 Publisher.sendMessage('Remove sensors ID')
... ... @@ -749,6 +751,7 @@ class NeuronavigationPanel(wx.Panel):
749 751 # When ref mode is changed the tracker coordinates are set to zero
750 752 self.ref_mode_id = evt.GetSelection()
751 753 self.ResetTrackerFiducials()
  754 + self.ResetIcp()
752 755 # Some trackers do not accept restarting within this time window
753 756 # TODO: Improve the restarting of trackers after changing reference mode
754 757 # self.OnChoiceTracker(None, ctrl)
... ... @@ -780,8 +783,8 @@ class NeuronavigationPanel(wx.Panel):
780 783 fiducial_name = const.TRACKER_FIDUCIALS[n]['fiducial_name']
781 784 Publisher.sendMessage('Set tracker fiducial', fiducial_name=fiducial_name)
782 785  
783   - def OnICP(self):
784   - dialog = dlg.ICPCorregistrationDialog(nav_prop=(self.tracker_id, self.trk_init, self.ref_mode_id))
  786 + def OnICP(self, m_change):
  787 + dialog = dlg.ICPCorregistrationDialog(nav_prop=(m_change, self.tracker_id, self.trk_init, self.ref_mode_id))
785 788 if dialog.ShowModal() == wx.ID_OK:
786 789 self.m_icp, point_coord, transformed_points, prev_error, final_error = dialog.GetValue()
787 790 #TODO: checkbox in the dialog to transfer the icp points to 3D viewer
... ... @@ -972,7 +975,7 @@ class NeuronavigationPanel(wx.Panel):
972 975  
973 976 if not self.checkicp.GetValue():
974 977 if dlg.ICPcorregistration(self.fre):
975   - m_icp = self.OnICP()
  978 + m_icp = self.OnICP(m_change)
976 979 self.icp_fre = db.calculate_fre(self.fiducials_raw, self.fiducials, self.ref_mode_id,
977 980 m_change, m_icp)
978 981 self.ctrl_icp()
... ...