Commit f2fd855bbca344713a5b73d803b4e282e06f92b6
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
Showing
3 changed files
with
79 additions
and
28 deletions
Show diff stats
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() | ... | ... |