Commit 85de3a6aac8b62562168030f1da47975dbb5ceec
Committed by
GitHub
1 parent
93ef8d8f
Exists in
master
MOD: Refactor ICP to their own class (#307)
-ENH: stop navigation to perform ICP -FIX: remove ICP thread to avoid vtk freezing
Showing
2 changed files
with
81 additions
and
82 deletions
Show diff stats
invesalius/gui/dialogs.py
@@ -864,7 +864,8 @@ def ShowNavigationTrackerWarning(trck_id, lib_mode): | @@ -864,7 +864,8 @@ def ShowNavigationTrackerWarning(trck_id, lib_mode): | ||
864 | """ | 864 | """ |
865 | Spatial Tracker connection error | 865 | Spatial Tracker connection error |
866 | """ | 866 | """ |
867 | - trck = {const.MTC: 'Claron MicronTracker', | 867 | + trck = {const.SELECT: 'Tracker', |
868 | + const.MTC: 'Claron MicronTracker', | ||
868 | const.FASTRAK: 'Polhemus FASTRAK', | 869 | const.FASTRAK: 'Polhemus FASTRAK', |
869 | const.ISOTRAKII: 'Polhemus ISOTRAK', | 870 | const.ISOTRAKII: 'Polhemus ISOTRAK', |
870 | const.PATRIOT: 'Polhemus PATRIOT', | 871 | const.PATRIOT: 'Polhemus PATRIOT', |
@@ -3611,7 +3612,7 @@ class ICPCorregistrationDialog(wx.Dialog): | @@ -3611,7 +3612,7 @@ class ICPCorregistrationDialog(wx.Dialog): | ||
3611 | btn_reset.Bind(wx.EVT_BUTTON, self.OnReset) | 3612 | btn_reset.Bind(wx.EVT_BUTTON, self.OnReset) |
3612 | 3613 | ||
3613 | btn_apply_icp = wx.Button(self, -1, label=_('Apply registration')) | 3614 | btn_apply_icp = wx.Button(self, -1, label=_('Apply registration')) |
3614 | - btn_apply_icp.Bind(wx.EVT_BUTTON, self.thread_ICP_start, btn_apply_icp) | 3615 | + btn_apply_icp.Bind(wx.EVT_BUTTON, self.OnICP) |
3615 | btn_apply_icp.Enable(False) | 3616 | btn_apply_icp.Enable(False) |
3616 | self.btn_apply_icp = btn_apply_icp | 3617 | self.btn_apply_icp = btn_apply_icp |
3617 | 3618 | ||
@@ -3848,12 +3849,13 @@ class ICPCorregistrationDialog(wx.Dialog): | @@ -3848,12 +3849,13 @@ class ICPCorregistrationDialog(wx.Dialog): | ||
3848 | self.RemoveActor() | 3849 | self.RemoveActor() |
3849 | self.LoadActor() | 3850 | self.LoadActor() |
3850 | 3851 | ||
3851 | - def OnICP(self): | 3852 | + def OnICP(self, evt): |
3852 | if self.cont_point: | 3853 | if self.cont_point: |
3853 | self.cont_point.SetValue(False) | 3854 | self.cont_point.SetValue(False) |
3854 | self.OnContinuousAcquisition(evt=None, btn=self.cont_point) | 3855 | self.OnContinuousAcquisition(evt=None, btn=self.cont_point) |
3855 | 3856 | ||
3856 | self.SetProgress(0.3) | 3857 | self.SetProgress(0.3) |
3858 | + time.sleep(1) | ||
3857 | 3859 | ||
3858 | sourcePoints = np.array(self.point_coord) | 3860 | sourcePoints = np.array(self.point_coord) |
3859 | sourcePoints_vtk = vtk.vtkPoints() | 3861 | sourcePoints_vtk = vtk.vtkPoints() |
@@ -3926,11 +3928,6 @@ class ICPCorregistrationDialog(wx.Dialog): | @@ -3926,11 +3928,6 @@ class ICPCorregistrationDialog(wx.Dialog): | ||
3926 | 3928 | ||
3927 | self.btn_ok.Enable(True) | 3929 | self.btn_ok.Enable(True) |
3928 | 3930 | ||
3929 | - def thread_ICP_start(self, evt): | ||
3930 | - import threading | ||
3931 | - th = threading.Thread(target=self.OnICP, args=[]) | ||
3932 | - th.start() | ||
3933 | - | ||
3934 | def GetValue(self): | 3931 | def GetValue(self): |
3935 | return self.m_icp, self.point_coord, self.transformed_points, self.prev_error, self.final_error | 3932 | return self.m_icp, self.point_coord, self.transformed_points, self.prev_error, self.final_error |
3936 | 3933 |
invesalius/gui/task_navigator.py
@@ -310,6 +310,8 @@ class Navigation(): | @@ -310,6 +310,8 @@ class Navigation(): | ||
310 | self.trigger_state = False | 310 | self.trigger_state = False |
311 | self.obj_reg = None | 311 | self.obj_reg = None |
312 | self.track_obj = False | 312 | self.track_obj = False |
313 | + self.m_change = None | ||
314 | + self.all_fiducials = np.zeros((6, 6)) | ||
313 | 315 | ||
314 | self.event = threading.Event() | 316 | self.event = threading.Event() |
315 | self.coord_queue = QueueCustom(maxsize=1) | 317 | self.coord_queue = QueueCustom(maxsize=1) |
@@ -331,12 +333,6 @@ class Navigation(): | @@ -331,12 +333,6 @@ class Navigation(): | ||
331 | self.seed_radius = const.SEED_RADIUS | 333 | self.seed_radius = const.SEED_RADIUS |
332 | self.sleep_nav = const.SLEEP_NAVIGATION | 334 | self.sleep_nav = const.SLEEP_NAVIGATION |
333 | 335 | ||
334 | - # ICP related variables | ||
335 | - self.use_icp = False | ||
336 | - self.m_icp = None | ||
337 | - self.fre = None | ||
338 | - self.icp_fre = None | ||
339 | - | ||
340 | def SetImageFiducial(self, fiducial_index, coord): | 336 | def SetImageFiducial(self, fiducial_index, coord): |
341 | self.image_fiducials[fiducial_index, :] = coord | 337 | self.image_fiducials[fiducial_index, :] = coord |
342 | 338 | ||
@@ -349,16 +345,12 @@ class Navigation(): | @@ -349,16 +345,12 @@ class Navigation(): | ||
349 | tracker_fiducials, tracker_fiducials_raw = tracker.GetTrackerFiducials() | 345 | tracker_fiducials, tracker_fiducials_raw = tracker.GetTrackerFiducials() |
350 | ref_mode_id = tracker.GetReferenceMode() | 346 | ref_mode_id = tracker.GetReferenceMode() |
351 | 347 | ||
352 | - all_fiducials = np.vstack([self.image_fiducials, tracker_fiducials]) | ||
353 | - | ||
354 | - # fiducials matrix | ||
355 | - m_change = tr.affine_matrix_from_points(all_fiducials[3:, :].T, all_fiducials[:3, :].T, | ||
356 | - shear=False, scale=False) | 348 | + self.all_fiducials = np.vstack([self.image_fiducials, tracker_fiducials]) |
357 | 349 | ||
358 | - self.fre = db.calculate_fre(tracker_fiducials_raw, all_fiducials, ref_mode_id, m_change) | 350 | + self.fre = db.calculate_fre(tracker_fiducials_raw, self.all_fiducials, ref_mode_id, self.m_change) |
359 | 351 | ||
360 | - def GetFiducialRegistrationError(self): | ||
361 | - fre = self.icp_fre if self.use_icp else self.fre | 352 | + def GetFiducialRegistrationError(self, icp): |
353 | + fre = icp.icp_fre if icp.use_icp else self.fre | ||
362 | return fre, fre <= 3 | 354 | return fre, fre <= 3 |
363 | 355 | ||
364 | def StartNavigation(self, tracker): | 356 | def StartNavigation(self, tracker): |
@@ -376,11 +368,12 @@ class Navigation(): | @@ -376,11 +368,12 @@ class Navigation(): | ||
376 | 368 | ||
377 | Publisher.sendMessage("Navigation status", nav_status=True, vis_status=vis_components) | 369 | Publisher.sendMessage("Navigation status", nav_status=True, vis_status=vis_components) |
378 | 370 | ||
379 | - all_fiducials = np.vstack([self.image_fiducials, tracker_fiducials]) | 371 | + self.all_fiducials = np.vstack([self.image_fiducials, tracker_fiducials]) |
380 | 372 | ||
381 | # fiducials matrix | 373 | # fiducials matrix |
382 | - m_change = tr.affine_matrix_from_points(all_fiducials[3:, :].T, all_fiducials[:3, :].T, | 374 | + m_change = tr.affine_matrix_from_points(self.all_fiducials[3:, :].T, self.all_fiducials[:3, :].T, |
383 | shear=False, scale=False) | 375 | shear=False, scale=False) |
376 | + self.m_change = m_change | ||
384 | 377 | ||
385 | errors = False | 378 | errors = False |
386 | 379 | ||
@@ -449,16 +442,6 @@ class Navigation(): | @@ -449,16 +442,6 @@ class Navigation(): | ||
449 | jobs.start() | 442 | jobs.start() |
450 | # del jobs | 443 | # del jobs |
451 | 444 | ||
452 | - # TODO: Separate ICP-related code from Navigation class. The first step would be to have this stuff | ||
453 | - # in a separate function or so. | ||
454 | - # | ||
455 | - if not self.use_icp: | ||
456 | - if dlg.ICPcorregistration(self.fre): | ||
457 | - m_icp = self.OnICP(tracker, m_change) | ||
458 | - self.icp_fre = db.calculate_fre(tracker_fiducials_raw, all_fiducials, ref_mode_id, | ||
459 | - m_change, m_icp) | ||
460 | - self.SetICP(True) | ||
461 | - | ||
462 | def StopNavigation(self): | 445 | def StopNavigation(self): |
463 | self.event.set() | 446 | self.event.set() |
464 | 447 | ||
@@ -478,42 +461,6 @@ class Navigation(): | @@ -478,42 +461,6 @@ class Navigation(): | ||
478 | vis_components = [self.trigger_state, self.view_tracts] | 461 | vis_components = [self.trigger_state, self.view_tracts] |
479 | Publisher.sendMessage("Navigation status", nav_status=False, vis_status=vis_components) | 462 | Publisher.sendMessage("Navigation status", nav_status=False, vis_status=vis_components) |
480 | 463 | ||
481 | - # ICP-related functions | ||
482 | - # | ||
483 | - # TODO: Refactor to preferably their own class. | ||
484 | - # | ||
485 | - def OnICP(self, tracker, m_change): | ||
486 | - ref_mode_id = tracker.GetReferenceMode() | ||
487 | - | ||
488 | - dialog = dlg.ICPCorregistrationDialog(nav_prop=(m_change, tracker.tracker_id, tracker.trk_init, ref_mode_id)) | ||
489 | - | ||
490 | - if dialog.ShowModal() == wx.ID_OK: | ||
491 | - self.m_icp, point_coord, transformed_points, prev_error, final_error = dialog.GetValue() | ||
492 | - # TODO: checkbox in the dialog to transfer the icp points to 3D viewer | ||
493 | - #create markers | ||
494 | - # for i in range(len(point_coord)): | ||
495 | - # img_coord = point_coord[i][0],-point_coord[i][1],point_coord[i][2], 0, 0, 0 | ||
496 | - # transf_coord = transformed_points[i][0],-transformed_points[i][1],transformed_points[i][2], 0, 0, 0 | ||
497 | - # Publisher.sendMessage('Create marker', coord=img_coord, marker_id=None, colour=(1,0,0)) | ||
498 | - # Publisher.sendMessage('Create marker', coord=transf_coord, marker_id=None, colour=(0,0,1)) | ||
499 | - if self.m_icp is not None: | ||
500 | - dlg.ReportICPerror(prev_error, final_error) | ||
501 | - self.use_icp = True | ||
502 | - else: | ||
503 | - self.use_icp = False | ||
504 | - | ||
505 | - return self.m_icp | ||
506 | - | ||
507 | - def SetICP(self, use_icp): | ||
508 | - self.use_icp = use_icp | ||
509 | - self.icp_queue.put_nowait([self.use_icp, self.m_icp]) | ||
510 | - | ||
511 | - def ResetICP(self): | ||
512 | - self.use_icp = False | ||
513 | - self.m_icp = None | ||
514 | - self.fre = None | ||
515 | - self.icp_fre = None | ||
516 | - | ||
517 | 464 | ||
518 | class Tracker(): | 465 | class Tracker(): |
519 | def __init__(self): | 466 | def __init__(self): |
@@ -689,6 +636,58 @@ class Tracker(): | @@ -689,6 +636,58 @@ class Tracker(): | ||
689 | def get_trackers(self): | 636 | def get_trackers(self): |
690 | return const.TRACKERS | 637 | return const.TRACKERS |
691 | 638 | ||
639 | +class ICP(): | ||
640 | + def __init__(self): | ||
641 | + self.use_icp = False | ||
642 | + self.m_icp = None | ||
643 | + self.icp_fre = None | ||
644 | + | ||
645 | + def StartICP(self, navigation, tracker): | ||
646 | + if not self.use_icp: | ||
647 | + if dlg.ICPcorregistration(navigation.fre): | ||
648 | + Publisher.sendMessage('Stop navigation') | ||
649 | + use_icp, self.m_icp = self.OnICP(tracker, navigation.m_change) | ||
650 | + if use_icp: | ||
651 | + self.icp_fre = db.calculate_fre(tracker.tracker_fiducials_raw, navigation.all_fiducials, | ||
652 | + tracker.ref_mode_id, navigation.m_change, self.m_icp) | ||
653 | + self.SetICP(navigation, use_icp) | ||
654 | + else: | ||
655 | + print("ICP canceled") | ||
656 | + Publisher.sendMessage('Start navigation') | ||
657 | + | ||
658 | + def OnICP(self, tracker, m_change): | ||
659 | + ref_mode_id = tracker.GetReferenceMode() | ||
660 | + | ||
661 | + dialog = dlg.ICPCorregistrationDialog(nav_prop=(m_change, tracker.tracker_id, tracker.trk_init, ref_mode_id)) | ||
662 | + | ||
663 | + if dialog.ShowModal() == wx.ID_OK: | ||
664 | + m_icp, point_coord, transformed_points, prev_error, final_error = dialog.GetValue() | ||
665 | + # TODO: checkbox in the dialog to transfer the icp points to 3D viewer | ||
666 | + #create markers | ||
667 | + # for i in range(len(point_coord)): | ||
668 | + # img_coord = point_coord[i][0],-point_coord[i][1],point_coord[i][2], 0, 0, 0 | ||
669 | + # transf_coord = transformed_points[i][0],-transformed_points[i][1],transformed_points[i][2], 0, 0, 0 | ||
670 | + # Publisher.sendMessage('Create marker', coord=img_coord, marker_id=None, colour=(1,0,0)) | ||
671 | + # Publisher.sendMessage('Create marker', coord=transf_coord, marker_id=None, colour=(0,0,1)) | ||
672 | + if m_icp is not None: | ||
673 | + dlg.ReportICPerror(prev_error, final_error) | ||
674 | + use_icp = True | ||
675 | + else: | ||
676 | + use_icp = False | ||
677 | + | ||
678 | + return use_icp, m_icp | ||
679 | + | ||
680 | + else: | ||
681 | + return self.use_icp, self.m_icp | ||
682 | + | ||
683 | + def SetICP(self, navigation, use_icp): | ||
684 | + self.use_icp = use_icp | ||
685 | + navigation.icp_queue.put_nowait([self.use_icp, self.m_icp]) | ||
686 | + | ||
687 | + def ResetICP(self): | ||
688 | + self.use_icp = False | ||
689 | + self.m_icp = None | ||
690 | + self.icp_fre = None | ||
692 | 691 | ||
693 | class NeuronavigationPanel(wx.Panel): | 692 | class NeuronavigationPanel(wx.Panel): |
694 | def __init__(self, parent): | 693 | def __init__(self, parent): |
@@ -707,6 +706,7 @@ class NeuronavigationPanel(wx.Panel): | @@ -707,6 +706,7 @@ class NeuronavigationPanel(wx.Panel): | ||
707 | self.pedal_connection = PedalConnection() if HAS_PEDAL_CONNECTION else None | 706 | self.pedal_connection = PedalConnection() if HAS_PEDAL_CONNECTION else None |
708 | self.tracker = Tracker() | 707 | self.tracker = Tracker() |
709 | self.navigation = Navigation() | 708 | self.navigation = Navigation() |
709 | + self.icp = ICP() | ||
710 | 710 | ||
711 | self.nav_status = False | 711 | self.nav_status = False |
712 | 712 | ||
@@ -915,11 +915,12 @@ class NeuronavigationPanel(wx.Panel): | @@ -915,11 +915,12 @@ class NeuronavigationPanel(wx.Panel): | ||
915 | 915 | ||
916 | self.tracker.SetTrackerFiducial(fiducial_index) | 916 | self.tracker.SetTrackerFiducial(fiducial_index) |
917 | 917 | ||
918 | + self.ResetICP() | ||
918 | self.tracker.UpdateUI(self.select_tracker_elem, self.numctrls_coord[3:6], self.txtctrl_fre) | 919 | self.tracker.UpdateUI(self.select_tracker_elem, self.numctrls_coord[3:6], self.txtctrl_fre) |
919 | 920 | ||
920 | def UpdateNavigationStatus(self, nav_status, vis_status): | 921 | def UpdateNavigationStatus(self, nav_status, vis_status): |
921 | self.nav_status = nav_status | 922 | self.nav_status = nav_status |
922 | - if nav_status and self.navigation.m_icp is not None: | 923 | + if nav_status and self.icp.m_icp is not None: |
923 | self.checkbox_icp.Enable(True) | 924 | self.checkbox_icp.Enable(True) |
924 | else: | 925 | else: |
925 | self.checkbox_icp.Enable(False) | 926 | self.checkbox_icp.Enable(False) |
@@ -974,7 +975,7 @@ class NeuronavigationPanel(wx.Panel): | @@ -974,7 +975,7 @@ class NeuronavigationPanel(wx.Panel): | ||
974 | self.navigation.trigger_state = trigger_state | 975 | self.navigation.trigger_state = trigger_state |
975 | 976 | ||
976 | def ResetICP(self): | 977 | def ResetICP(self): |
977 | - self.navigation.ResetICP() | 978 | + self.icp.ResetICP() |
978 | self.checkbox_icp.Enable(False) | 979 | self.checkbox_icp.Enable(False) |
979 | self.checkbox_icp.SetValue(False) | 980 | self.checkbox_icp.SetValue(False) |
980 | 981 | ||
@@ -1041,7 +1042,7 @@ class NeuronavigationPanel(wx.Panel): | @@ -1041,7 +1042,7 @@ class NeuronavigationPanel(wx.Panel): | ||
1041 | 1042 | ||
1042 | def UpdateFiducialRegistrationError(self): | 1043 | def UpdateFiducialRegistrationError(self): |
1043 | self.navigation.UpdateFiducialRegistrationError(self.tracker) | 1044 | self.navigation.UpdateFiducialRegistrationError(self.tracker) |
1044 | - fre, fre_ok = self.navigation.GetFiducialRegistrationError() | 1045 | + fre, fre_ok = self.navigation.GetFiducialRegistrationError(self.icp) |
1045 | 1046 | ||
1046 | self.txtctrl_fre.SetValue(str(round(fre, 2))) | 1047 | self.txtctrl_fre.SetValue(str(round(fre, 2))) |
1047 | if fre_ok: | 1048 | if fre_ok: |
@@ -1063,10 +1064,6 @@ class NeuronavigationPanel(wx.Panel): | @@ -1063,10 +1064,6 @@ class NeuronavigationPanel(wx.Panel): | ||
1063 | errors = True | 1064 | errors = True |
1064 | 1065 | ||
1065 | else: | 1066 | else: |
1066 | - if not self.UpdateFiducialRegistrationError(): | ||
1067 | - # TODO: Exhibit FRE in a warning dialog and only starts navigation after user clicks ok | ||
1068 | - print("WARNING: Fiducial registration error too large.") | ||
1069 | - | ||
1070 | # Prepare GUI for navigation. | 1067 | # Prepare GUI for navigation. |
1071 | Publisher.sendMessage("Toggle Cross", id=const.SLICE_STATE_CROSS) | 1068 | Publisher.sendMessage("Toggle Cross", id=const.SLICE_STATE_CROSS) |
1072 | Publisher.sendMessage("Hide current mask") | 1069 | Publisher.sendMessage("Hide current mask") |
@@ -1079,13 +1076,17 @@ class NeuronavigationPanel(wx.Panel): | @@ -1079,13 +1076,17 @@ class NeuronavigationPanel(wx.Panel): | ||
1079 | 1076 | ||
1080 | self.navigation.StartNavigation(self.tracker) | 1077 | self.navigation.StartNavigation(self.tracker) |
1081 | 1078 | ||
1082 | - # Update FRE once more after starting the navigation, due to the optional use of ICP, | ||
1083 | - # which improves FRE. | ||
1084 | - self.UpdateFiducialRegistrationError() | 1079 | + if not self.UpdateFiducialRegistrationError(): |
1080 | + # TODO: Exhibit FRE in a warning dialog and only starts navigation after user clicks ok | ||
1081 | + print("WARNING: Fiducial registration error too large.") | ||
1085 | 1082 | ||
1086 | - if self.navigation.use_icp: | 1083 | + self.icp.StartICP(self.navigation, self.tracker) |
1084 | + if self.icp.use_icp: | ||
1087 | self.checkbox_icp.Enable(True) | 1085 | self.checkbox_icp.Enable(True) |
1088 | self.checkbox_icp.SetValue(True) | 1086 | self.checkbox_icp.SetValue(True) |
1087 | + # Update FRE once more after starting the navigation, due to the optional use of ICP, | ||
1088 | + # which improves FRE. | ||
1089 | + self.UpdateFiducialRegistrationError() | ||
1089 | 1090 | ||
1090 | def OnNavigate(self, evt, btn_nav): | 1091 | def OnNavigate(self, evt, btn_nav): |
1091 | select_tracker_elem = self.select_tracker_elem | 1092 | select_tracker_elem = self.select_tracker_elem |
@@ -1113,7 +1114,7 @@ class NeuronavigationPanel(wx.Panel): | @@ -1113,7 +1114,7 @@ class NeuronavigationPanel(wx.Panel): | ||
1113 | self.numctrls_coord[m][n].SetValue(0.0) | 1114 | self.numctrls_coord[m][n].SetValue(0.0) |
1114 | 1115 | ||
1115 | def OnCheckboxICP(self, evt, ctrl): | 1116 | def OnCheckboxICP(self, evt, ctrl): |
1116 | - self.navigation.SetICP(ctrl.GetValue()) | 1117 | + self.icp.SetICP(self.navigation, ctrl.GetValue()) |
1117 | self.UpdateFiducialRegistrationError() | 1118 | self.UpdateFiducialRegistrationError() |
1118 | 1119 | ||
1119 | def OnCloseProject(self): | 1120 | def OnCloseProject(self): |
@@ -1130,6 +1131,7 @@ class NeuronavigationPanel(wx.Panel): | @@ -1130,6 +1131,7 @@ class NeuronavigationPanel(wx.Panel): | ||
1130 | self.navigation.StopNavigation() | 1131 | self.navigation.StopNavigation() |
1131 | self.navigation.__init__() | 1132 | self.navigation.__init__() |
1132 | self.tracker.__init__() | 1133 | self.tracker.__init__() |
1134 | + self.icp.__init__() | ||
1133 | 1135 | ||
1134 | 1136 | ||
1135 | class ObjectRegistrationPanel(wx.Panel): | 1137 | class ObjectRegistrationPanel(wx.Panel): |