Commit 55ce120595561b4806a4aed610e1379d82ac69f6

Authored by Olli-Pekka Kahilakoski
1 parent 9d79c88d
Exists in master

MOD: Refactor task_navigator.py

- Some steps towards decoupling GUI and domain logic, namely, by adding
  classes Navigation and Tracker, which take some of the responsibility
  previously handled by NeuronavigationPanel.

- This is done to prepare to do some changes in the GUI, such as
  moving "Navigate" button elsewhere -- previously, it was hard
  to do due to the elements in the GUI being tightly coupled with the
  implementation of navigation.

- Also some other minor clean-up and style improvements, such as
  using more consistent and more descriptive variable names
invesalius/constants.py
... ... @@ -675,12 +675,12 @@ DEFAULT_TRACKER = SELECT
675 675  
676 676 NDICOMPORT = b'COM1'
677 677  
678   -TRACKER = [_("Select tracker:"), _("Claron MicronTracker"),
679   - _("Polhemus FASTRAK"), _("Polhemus ISOTRAK II"),
680   - _("Polhemus PATRIOT"), _("Camera tracker"),
681   - _("NDI Polaris"), _("NDI Polaris P4"),
682   - _("Optitrack"), _("Debug tracker (random)"),
683   - _("Debug tracker (approach)"), _("Disconnect tracker")]
  678 +TRACKERS = [_("Claron MicronTracker"),
  679 + _("Polhemus FASTRAK"), _("Polhemus ISOTRAK II"),
  680 + _("Polhemus PATRIOT"), _("Camera tracker"),
  681 + _("NDI Polaris"), _("NDI Polaris P4"),
  682 + _("Optitrack"), _("Debug tracker (random)"),
  683 + _("Debug tracker (approach)"), _("Disconnect tracker")]
684 684  
685 685 STATIC_REF = 0
686 686 DYNAMIC_REF = 1
... ... @@ -729,21 +729,21 @@ TRACKER_FIDUCIALS = [
729 729 'button_id': TR1,
730 730 'label': 'LET',
731 731 'fiducial_name': 'LE',
732   - 'fiducial_index': 3,
  732 + 'fiducial_index': 0,
733 733 'tip': _("Select left ear with spatial tracker"),
734 734 },
735 735 {
736 736 'button_id': TR2,
737 737 'label': 'RET',
738 738 'fiducial_name': 'RE',
739   - 'fiducial_index': 4,
  739 + 'fiducial_index': 1,
740 740 'tip': _("Select right ear with spatial tracker"),
741 741 },
742 742 {
743 743 'button_id': TR3,
744 744 'label': 'NAT',
745 745 'fiducial_name': 'NA',
746   - 'fiducial_index': 5,
  746 + 'fiducial_index': 2,
747 747 'tip': _("Select nasion with spatial tracker"),
748 748 },
749 749 ]
... ...
invesalius/data/coregistration.py
... ... @@ -171,10 +171,10 @@ def corregistrate_dynamic(inp, coord_raw, ref_mode_id, icp):
171 171  
172 172  
173 173 class CoordinateCorregistrate(threading.Thread):
174   - def __init__(self, ref_mode_id, trck_info, coreg_data, view_tracts, queues, event, sle, tracker_id, target):
  174 + def __init__(self, ref_mode_id, tracker, coreg_data, view_tracts, queues, event, sle, tracker_id, target):
175 175 threading.Thread.__init__(self, name='CoordCoregObject')
176 176 self.ref_mode_id = ref_mode_id
177   - self.trck_info = trck_info
  177 + self.tracker = tracker
178 178 self.coreg_data = coreg_data
179 179 self.coord_queue = queues[0]
180 180 self.view_tracts = view_tracts
... ... @@ -198,11 +198,11 @@ class CoordinateCorregistrate(threading.Thread):
198 198 self.target[1] = -self.target[1]
199 199  
200 200 def run(self):
201   - trck_info = self.trck_info
202 201 coreg_data = self.coreg_data
203 202 view_obj = 1
204 203  
205   - trck_init, trck_id, trck_mode = trck_info
  204 + trck_init, trck_id, trck_mode = self.tracker.GetTrackerInfo()
  205 +
206 206 # print('CoordCoreg: event {}'.format(self.event.is_set()))
207 207 while not self.event.is_set():
208 208 try:
... ... @@ -258,10 +258,10 @@ class CoordinateCorregistrate(threading.Thread):
258 258  
259 259  
260 260 class CoordinateCorregistrateNoObject(threading.Thread):
261   - def __init__(self, ref_mode_id, trck_info, coreg_data, view_tracts, queues, event, sle):
  261 + def __init__(self, ref_mode_id, tracker, coreg_data, view_tracts, queues, event, sle):
262 262 threading.Thread.__init__(self, name='CoordCoregNoObject')
263 263 self.ref_mode_id = ref_mode_id
264   - self.trck_info = trck_info
  264 + self.tracker = tracker
265 265 self.coreg_data = coreg_data
266 266 self.coord_queue = queues[0]
267 267 self.view_tracts = view_tracts
... ... @@ -273,11 +273,10 @@ class CoordinateCorregistrateNoObject(threading.Thread):
273 273 self.m_icp = None
274 274  
275 275 def run(self):
276   - trck_info = self.trck_info
277 276 coreg_data = self.coreg_data
278 277 view_obj = 0
279 278  
280   - trck_init, trck_id, trck_mode = trck_info
  279 + trck_init, trck_id, trck_mode = self.tracker.GetTrackerInfo()
281 280 # print('CoordCoreg: event {}'.format(self.event.is_set()))
282 281 while not self.event.is_set():
283 282 try:
... ...
invesalius/gui/task_navigator.py
... ... @@ -300,39 +300,18 @@ class InnerFoldPanel(wx.Panel):
300 300 Publisher.sendMessage('Update volume camera state', camera_state=self.checkcamera.GetValue())
301 301  
302 302  
303   -class NeuronavigationPanel(wx.Panel):
304   - def __init__(self, parent):
305   - wx.Panel.__init__(self, parent)
306   - try:
307   - default_colour = wx.SystemSettings.GetColour(wx.SYS_COLOUR_MENUBAR)
308   - except AttributeError:
309   - default_colour = wx.SystemSettings_GetColour(wx.SYS_COLOUR_MENUBAR)
310   - self.SetBackgroundColour(default_colour)
311   -
312   - self.SetAutoLayout(1)
313   -
314   - self.__bind_events()
315   -
316   - # Initialize global variables
317   - self.pedal_connection = PedalConnection() if HAS_PEDAL_CONNECTION else None
318   - self.fiducials = np.full([6, 3], np.nan)
319   - self.fiducials_raw = np.zeros((6, 6))
  303 +class Navigation():
  304 + def __init__(self):
  305 + self.image_fiducials = np.full([3, 3], np.nan)
320 306 self.correg = None
321 307 self.current_coord = 0, 0, 0
322   - self.trk_init = None
323   - self.nav_status = False
324 308 self.target = None
325 309 self.trigger = None
326 310 self.trigger_state = False
327 311 self.obj_reg = None
328   - self.obj_reg_status = False
329 312 self.track_obj = False
330   - self.m_icp = None
331   - self.fre = None
332   - self.icp_fre = None
333   - self.icp = False
334   - self.event = threading.Event()
335 313  
  314 + self.event = threading.Event()
336 315 self.coord_queue = QueueCustom(maxsize=1)
337 316 self.icp_queue = QueueCustom(maxsize=1)
338 317 # self.visualization_queue = QueueCustom(maxsize=1)
... ... @@ -352,21 +331,400 @@ class NeuronavigationPanel(wx.Panel):
352 331 self.seed_radius = const.SEED_RADIUS
353 332 self.sleep_nav = const.SLEEP_NAVIGATION
354 333  
  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):
  341 + self.image_fiducials[fiducial_index, :] = coord
  342 +
  343 + print("Set image fiducial {} to coordinates {}".format(fiducial_index, coord))
  344 +
  345 + def AreImageFiducialsSet(self):
  346 + return not np.isnan(self.image_fiducials).any()
  347 +
  348 + def UpdateFiducialRegistrationError(self, tracker):
  349 + tracker_fiducials, tracker_fiducials_raw = tracker.GetTrackerFiducials()
  350 + ref_mode_id = tracker.GetReferenceMode()
  351 +
  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)
  357 +
  358 + self.fre = db.calculate_fre(tracker_fiducials_raw, all_fiducials, ref_mode_id, m_change)
  359 +
  360 + def GetFiducialRegistrationError(self):
  361 + fre = self.icp_fre if self.use_icp else self.fre
  362 + return fre, fre <= 3
  363 +
  364 + def StartNavigation(self, tracker):
  365 + tracker_fiducials, tracker_fiducials_raw = tracker.GetTrackerFiducials()
  366 + ref_mode_id = tracker.GetReferenceMode()
  367 +
  368 + # initialize jobs list
  369 + jobs_list = []
  370 +
  371 + if self.event.is_set():
  372 + self.event.clear()
  373 +
  374 + vis_components = [self.trigger_state, self.view_tracts]
  375 + vis_queues = [self.coord_queue, self.trigger_queue, self.tracts_queue, self.icp_queue]
  376 +
  377 + Publisher.sendMessage("Navigation status", nav_status=True, vis_status=vis_components)
  378 +
  379 + all_fiducials = np.vstack([self.image_fiducials, tracker_fiducials])
  380 +
  381 + # fiducials matrix
  382 + m_change = tr.affine_matrix_from_points(all_fiducials[3:, :].T, all_fiducials[:3, :].T,
  383 + shear=False, scale=False)
  384 +
  385 + errors = False
  386 +
  387 + if self.track_obj:
  388 + # if object tracking is selected
  389 + if self.obj_reg is None:
  390 + # check if object registration was performed
  391 + wx.MessageBox(_("Perform coil registration before navigation."), _("InVesalius 3"))
  392 + errors = True
  393 + else:
  394 + # if object registration was correctly performed continue with navigation
  395 + # obj_reg[0] is object 3x3 fiducial matrix and obj_reg[1] is 3x3 orientation matrix
  396 + obj_fiducials, obj_orients, obj_ref_mode, obj_name = self.obj_reg
  397 +
  398 + coreg_data = [m_change, obj_ref_mode]
  399 +
  400 + if ref_mode_id:
  401 + coord_raw = dco.GetCoordinates(tracker.trk_init, tracker.tracker_id, ref_mode_id)
  402 + else:
  403 + coord_raw = np.array([None])
  404 +
  405 + obj_data = db.object_registration(obj_fiducials, obj_orients, coord_raw, m_change)
  406 + coreg_data.extend(obj_data)
  407 +
  408 + queues = [self.coord_queue, self.coord_tracts_queue, self.icp_queue]
  409 + jobs_list.append(dcr.CoordinateCorregistrate(ref_mode_id, tracker, coreg_data,
  410 + self.view_tracts, queues,
  411 + self.event, self.sleep_nav, tracker.tracker_id,
  412 + self.target))
  413 + else:
  414 + coreg_data = (m_change, 0)
  415 + queues = [self.coord_queue, self.coord_tracts_queue, self.icp_queue]
  416 + jobs_list.append(dcr.CoordinateCorregistrateNoObject(ref_mode_id, tracker, coreg_data,
  417 + self.view_tracts, queues,
  418 + self.event, self.sleep_nav))
  419 +
  420 + if not errors:
  421 + #TODO: Test the trigger thread
  422 + if self.trigger_state:
  423 + # self.trigger = trig.Trigger(nav_id)
  424 + jobs_list.append(trig.TriggerNew(self.trigger_queue, self.event, self.sleep_nav))
  425 +
  426 + if self.view_tracts:
  427 + # initialize Trekker parameters
  428 + slic = sl.Slice()
  429 + prj_data = prj.Project()
  430 + matrix_shape = tuple(prj_data.matrix_shape)
  431 + affine = slic.affine.copy()
  432 + affine[1, -1] -= matrix_shape[1]
  433 + affine_vtk = vtk_utils.numpy_to_vtkMatrix4x4(affine)
  434 + Publisher.sendMessage("Update marker offset state", create=True)
  435 + self.trk_inp = self.trekker, affine, self.seed_offset, self.n_tracts, self.seed_radius,\
  436 + self.n_threads, self.act_data, affine_vtk, matrix_shape[1]
  437 + # print("Appending the tract computation thread!")
  438 + queues = [self.coord_tracts_queue, self.tracts_queue]
  439 + if self.enable_act:
  440 + jobs_list.append(dti.ComputeTractsACTThread(self.trk_inp, queues, self.event, self.sleep_nav))
  441 + else:
  442 + jobs_list.append(dti.ComputeTractsThread(self.trk_inp, queues, self.event, self.sleep_nav))
  443 +
  444 + jobs_list.append(UpdateNavigationScene(vis_queues, vis_components,
  445 + self.event, self.sleep_nav))
  446 +
  447 + for jobs in jobs_list:
  448 + # jobs.daemon = True
  449 + jobs.start()
  450 + # del jobs
  451 +
  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):
  463 + self.event.set()
  464 +
  465 + self.coord_queue.clear()
  466 + self.coord_queue.join()
  467 +
  468 + if self.trigger_state:
  469 + self.trigger_queue.clear()
  470 + self.trigger_queue.join()
  471 + if self.view_tracts:
  472 + self.coord_tracts_queue.clear()
  473 + self.coord_tracts_queue.join()
  474 +
  475 + self.tracts_queue.clear()
  476 + self.tracts_queue.join()
  477 +
  478 + vis_components = [self.trigger_state, self.view_tracts]
  479 + Publisher.sendMessage("Navigation status", nav_status=False, vis_status=vis_components)
  480 +
  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 +
  518 +class Tracker():
  519 + def __init__(self):
  520 + self.trk_init = None
355 521 self.tracker_id = const.DEFAULT_TRACKER
356 522 self.ref_mode_id = const.DEFAULT_REF_MODE
357 523  
  524 + self.tracker_fiducials = np.full([3, 3], np.nan)
  525 + self.tracker_fiducials_raw = np.zeros((6, 6))
  526 +
  527 + self.tracker_connected = False
  528 +
  529 + def SetTracker(self, new_tracker):
  530 + if self.trk_init:
  531 + trck = self.trk_init[0]
  532 + else:
  533 + trck = None
  534 +
  535 + # Conditions check if click was on current selection and if any other tracker
  536 + # has been initialized before
  537 + if new_tracker != const.DISCTRACK and trck:
  538 + self.ResetTrackerFiducials()
  539 + Publisher.sendMessage('Update status text in GUI',
  540 + label=_("Disconnecting tracker..."))
  541 + Publisher.sendMessage('Remove sensors ID')
  542 + self.trk_init = dt.TrackerConnection(self.tracker_id, trck, 'disconnect')
  543 + Publisher.sendMessage('Remove object data')
  544 +
  545 + if not self.trk_init[0] and new_tracker:
  546 + Publisher.sendMessage('Update status text in GUI',
  547 + label=_("Tracker disconnected successfully"))
  548 + self.trk_init = dt.TrackerConnection(new_tracker, None, 'connect')
  549 + if not self.trk_init[0]:
  550 + self.tracked_connected = False
  551 + self.tracker_id = 0
  552 +
  553 + dlg.ShowNavigationTrackerWarning(self.tracker_id, self.trk_init[1])
  554 + print("Tracker not connected!")
  555 + else:
  556 + self.tracked_connected = True
  557 + self.tracker_id = new_tracker
  558 +
  559 + print("Tracker connected!")
  560 +
  561 + # TODO: const.DISCTRACK is not a tracker, so discoupling it from the actual trackers
  562 + # would make this cleaner.
  563 + #
  564 + elif new_tracker == const.DISCTRACK and trck:
  565 + self.ResetTrackerFiducials()
  566 + Publisher.sendMessage('Update status text in GUI',
  567 + label=_("Disconnecting tracker ..."))
  568 + Publisher.sendMessage('Remove sensors ID')
  569 + Publisher.sendMessage('Remove object data')
  570 + self.trk_init = dt.TrackerConnection(self.tracker_id, trck, 'disconnect')
  571 + if not self.trk_init[0]:
  572 + dlg.ShowNavigationTrackerWarning(self.tracker_id, 'disconnect')
  573 +
  574 + self.tracked_connected = False
  575 + self.tracker_id = 0
  576 +
  577 + Publisher.sendMessage('Update status text in GUI',
  578 + label=_("Tracker disconnected"))
  579 +
  580 + print("Tracker disconnected!")
  581 + else:
  582 + Publisher.sendMessage('Update status text in GUI',
  583 + label=_("Tracker still connected"))
  584 + print("Tracker still connected!")
  585 +
  586 + else:
  587 + # If trk_init is None try to connect. If doesn't succeed show dialog.
  588 + if new_tracker:
  589 + self.trk_init = dt.TrackerConnection(new_tracker, None, 'connect')
  590 + if not self.trk_init[0]:
  591 + dlg.ShowNavigationTrackerWarning(self.tracker_id, self.trk_init[1])
  592 +
  593 + self.tracker_id = 0
  594 + self.tracker_connected = False
  595 + else:
  596 + self.tracker_id = new_tracker
  597 + self.tracker_connected = True
  598 +
  599 + Publisher.sendMessage('Update tracker initializer',
  600 + nav_prop=(self.tracker_id, self.trk_init, self.ref_mode_id))
  601 +
  602 + def DisconnectTracker(self):
  603 + if self.tracker_connected:
  604 + self.ResetTrackerFiducials()
  605 + Publisher.sendMessage('Update status text in GUI',
  606 + label=_("Disconnecting tracker ..."))
  607 + Publisher.sendMessage('Remove sensors ID')
  608 + Publisher.sendMessage('Remove object data')
  609 + self.trk_init = dt.TrackerConnection(self.tracker_id, self.tracker_connected, 'disconnect')
  610 + if not self.trk_init[0]:
  611 + self.tracker_connected = False
  612 + self.tracker_id = 0
  613 +
  614 + Publisher.sendMessage('Update status text in GUI',
  615 + label=_("Tracker disconnected"))
  616 + print("Tracker disconnected!")
  617 + else:
  618 + Publisher.sendMessage('Update status text in GUI',
  619 + label=_("Tracker still connected"))
  620 + print("Tracker still connected!")
  621 +
  622 + def IsTrackerInitialized(self):
  623 + return self.trk_init and self.tracker_id and self.tracker_connected
  624 +
  625 + def AreTrackerFiducialsSet(self):
  626 + return not np.isnan(self.tracker_fiducials).any()
  627 +
  628 + def SetTrackerFiducial(self, fiducial_index):
  629 + coord = None
  630 +
  631 + coord_raw = dco.GetCoordinates(self.trk_init, self.tracker_id, self.ref_mode_id)
  632 +
  633 + if self.ref_mode_id:
  634 + coord = dco.dynamic_reference_m(coord_raw[0, :], coord_raw[1, :])
  635 + else:
  636 + coord = coord_raw[0, :]
  637 + coord[2] = -coord[2]
  638 +
  639 + # Update tracker fiducial with tracker coordinates
  640 + self.tracker_fiducials[fiducial_index, :] = coord[0:3]
  641 +
  642 + assert 0 <= fiducial_index <= 2, "Fiducial index out of range (0-2): {}".format(fiducial_index)
  643 +
  644 + self.tracker_fiducials_raw[2 * fiducial_index, :] = coord_raw[0, :]
  645 + self.tracker_fiducials_raw[2 * fiducial_index + 1, :] = coord_raw[1, :]
  646 +
  647 + print("Set tracker fiducial {} to coordinates {}.".format(fiducial_index, coord[0:3]))
  648 +
  649 + def ResetTrackerFiducials(self):
  650 + for m in range(3):
  651 + self.tracker_fiducials[m, :] = [np.nan, np.nan, np.nan]
  652 +
  653 + def GetTrackerFiducials(self):
  654 + return self.tracker_fiducials, self.tracker_fiducials_raw
  655 +
  656 + def GetTrackerInfo(self):
  657 + return self.trk_init, self.tracker_id, self.ref_mode_id
  658 +
  659 + def SetReferenceMode(self, value):
  660 + self.ref_mode_id = value
  661 +
  662 + # When ref mode is changed the tracker coordinates are set to zero
  663 + self.ResetTrackerFiducials()
  664 +
  665 + # Some trackers do not accept restarting within this time window
  666 + # TODO: Improve the restarting of trackers after changing reference mode
  667 + Publisher.sendMessage('Update tracker initializer',
  668 + nav_prop=(self.tracker_id, self.trk_init, self.ref_mode_id))
  669 +
  670 + def GetReferenceMode(self):
  671 + return self.ref_mode_id
  672 +
  673 + def UpdateUI(self, selection_ctrl, numctrls_coord, txtctrl_fre):
  674 + if self.tracker_connected:
  675 + selection_ctrl.SetSelection(self.tracker_id)
  676 + else:
  677 + selection_ctrl.SetSelection(0)
  678 +
  679 + # Update tracker location in the UI.
  680 + for m in range(3):
  681 + coord = self.tracker_fiducials[m, :]
  682 + for n in range(0, 3):
  683 + value = 0.0 if np.isnan(coord[n]) else float(coord[n])
  684 + numctrls_coord[m][n].SetValue(value)
  685 +
  686 + txtctrl_fre.SetValue('')
  687 + txtctrl_fre.SetBackgroundColour('WHITE')
  688 +
  689 + def get_trackers(self):
  690 + return const.TRACKERS
  691 +
  692 +
  693 +class NeuronavigationPanel(wx.Panel):
  694 + def __init__(self, parent):
  695 + wx.Panel.__init__(self, parent)
  696 + try:
  697 + default_colour = wx.SystemSettings.GetColour(wx.SYS_COLOUR_MENUBAR)
  698 + except AttributeError:
  699 + default_colour = wx.SystemSettings_GetColour(wx.SYS_COLOUR_MENUBAR)
  700 + self.SetBackgroundColour(default_colour)
  701 +
  702 + self.SetAutoLayout(1)
  703 +
  704 + self.__bind_events()
  705 +
  706 + # Initialize global variables
  707 + self.pedal_connection = PedalConnection() if HAS_PEDAL_CONNECTION else None
  708 + self.tracker = Tracker()
  709 + self.navigation = Navigation()
  710 +
  711 + self.nav_status = False
  712 +
358 713 # Initialize list of buttons and numctrls for wx objects
359 714 self.btns_coord = [None, None, None, None, None, None]
360 715 self.numctrls_coord = [[], [], [], [], [], []]
361 716  
362 717 # ComboBox for spatial tracker device selection
  718 + tracker_options = [_("Select tracker:")] + self.tracker.get_trackers()
  719 + select_tracker_elem = wx.ComboBox(self, -1, "",
  720 + choices=tracker_options, style=wx.CB_DROPDOWN|wx.CB_READONLY)
  721 +
363 722 tooltip = wx.ToolTip(_("Choose the tracking device"))
364   - choice_trck = wx.ComboBox(self, -1, "",
365   - choices=const.TRACKER, style=wx.CB_DROPDOWN|wx.CB_READONLY)
366   - choice_trck.SetToolTip(tooltip)
367   - choice_trck.SetSelection(const.DEFAULT_TRACKER)
368   - choice_trck.Bind(wx.EVT_COMBOBOX, partial(self.OnChoiceTracker, ctrl=choice_trck))
369   - self.choice_trck = choice_trck
  723 + select_tracker_elem.SetToolTip(tooltip)
  724 +
  725 + select_tracker_elem.SetSelection(const.DEFAULT_TRACKER)
  726 + select_tracker_elem.Bind(wx.EVT_COMBOBOX, partial(self.OnChooseTracker, ctrl=select_tracker_elem))
  727 + self.select_tracker_elem = select_tracker_elem
370 728  
371 729 # ComboBox for tracker reference mode
372 730 tooltip = wx.ToolTip(_("Choose the navigation reference mode"))
... ... @@ -374,7 +732,7 @@ class NeuronavigationPanel(wx.Panel):
374 732 choices=const.REF_MODE, style=wx.CB_DROPDOWN|wx.CB_READONLY)
375 733 choice_ref.SetSelection(const.DEFAULT_REF_MODE)
376 734 choice_ref.SetToolTip(tooltip)
377   - choice_ref.Bind(wx.EVT_COMBOBOX, partial(self.OnChoiceRefMode, ctrl=choice_trck))
  735 + choice_ref.Bind(wx.EVT_COMBOBOX, partial(self.OnChooseReferenceMode, ctrl=select_tracker_elem))
378 736 self.choice_ref = choice_ref
379 737  
380 738 # Toggle buttons for image fiducials
... ... @@ -422,12 +780,12 @@ class NeuronavigationPanel(wx.Panel):
422 780 btn_nav.Bind(wx.EVT_TOGGLEBUTTON, partial(self.OnNavigate, btn_nav=btn_nav))
423 781  
424 782 tooltip = wx.ToolTip(_(u"Refine the coregistration"))
425   - checkicp = wx.CheckBox(self, -1, _(' '))
426   - checkicp.SetValue(False)
427   - checkicp.Enable(False)
428   - checkicp.Bind(wx.EVT_CHECKBOX, partial(self.Oncheckicp, ctrl=checkicp))
429   - checkicp.SetToolTip(tooltip)
430   - self.checkicp = checkicp
  783 + checkbox_icp = wx.CheckBox(self, -1, _(' '))
  784 + checkbox_icp.SetValue(False)
  785 + checkbox_icp.Enable(False)
  786 + checkbox_icp.Bind(wx.EVT_CHECKBOX, partial(self.OnCheckboxICP, ctrl=checkbox_icp))
  787 + checkbox_icp.SetToolTip(tooltip)
  788 + self.checkbox_icp = checkbox_icp
431 789  
432 790 # An indicator for pedal trigger
433 791 if HAS_PEDAL_CONNECTION and self.pedal_connection.in_use:
... ... @@ -454,7 +812,7 @@ class NeuronavigationPanel(wx.Panel):
454 812  
455 813 # Sizer to group all GUI objects
456 814 choice_sizer = wx.FlexGridSizer(rows=1, cols=2, hgap=5, vgap=5)
457   - choice_sizer.AddMany([(choice_trck, wx.LEFT),
  815 + choice_sizer.AddMany([(select_tracker_elem, wx.LEFT),
458 816 (choice_ref, wx.RIGHT)])
459 817  
460 818 coord_sizer = wx.GridBagSizer(hgap=5, vgap=5)
... ... @@ -471,7 +829,7 @@ class NeuronavigationPanel(wx.Panel):
471 829 (txtctrl_fre, 0, wx.ALIGN_CENTER_HORIZONTAL | wx.ALIGN_CENTER_VERTICAL),
472 830 (btn_nav, 0, wx.ALIGN_CENTER_HORIZONTAL | wx.ALIGN_CENTER_VERTICAL),
473 831 (txt_icp, 0, wx.ALIGN_CENTER_HORIZONTAL | wx.ALIGN_CENTER_VERTICAL),
474   - (checkicp, 0, wx.ALIGN_LEFT | wx.ALIGN_CENTER_VERTICAL)])
  832 + (checkbox_icp, 0, wx.ALIGN_CENTER_HORIZONTAL | wx.ALIGN_CENTER_VERTICAL)])
475 833  
476 834 pedal_sizer = wx.FlexGridSizer(rows=1, cols=2, hgap=5, vgap=5)
477 835 if HAS_PEDAL_CONNECTION and self.pedal_connection.in_use:
... ... @@ -544,172 +902,87 @@ class NeuronavigationPanel(wx.Panel):
544 902 def SetImageFiducial(self, fiducial_name, coord):
545 903 fiducial = self.GetFiducialByAttribute(const.IMAGE_FIDUCIALS, 'fiducial_name', fiducial_name)
546 904 fiducial_index = fiducial['fiducial_index']
547   - self.fiducials[fiducial_index, :] = coord
548 905  
549   - print("Set image fiducial {} to coordinates {}".format(fiducial_name, coord))
  906 + self.navigation.SetImageFiducial(fiducial_index, coord)
550 907  
551 908 def SetTrackerFiducial(self, fiducial_name):
552   - fiducial = self.GetFiducialByAttribute(const.TRACKER_FIDUCIALS, 'fiducial_name', fiducial_name)
553   - fiducial_index = fiducial['fiducial_index']
554   -
555   - coord = None
556   -
557   - if not(self.trk_init and self.tracker_id):
  909 + if not self.tracker.IsTrackerInitialized():
558 910 dlg.ShowNavigationTrackerWarning(0, 'choose')
559 911 return
560 912  
561   - # if self.tracker_id == const.DEBUGTRACK:
562   - # if btn_id == 3:
563   - # coord1 = np.array([-120., 0., 0., 0., 0., 0.])
564   - # elif btn_id == 4:
565   - # coord1 = np.array([120., 0., 0., 0., 0., 0.])
566   - # elif btn_id == 5:
567   - # coord1 = np.array([0., 120., 0., 0., 0., 0.])
568   - # coord2 = np.zeros([3, 6])
569   - # coord_raw = np.vstack([coord1, coord2])
570   - # else:
571   - coord_raw = dco.GetCoordinates(self.trk_init, self.tracker_id, self.ref_mode_id)
572   -
573   - if self.ref_mode_id:
574   - coord = dco.dynamic_reference_m(coord_raw[0, :], coord_raw[1, :])
575   - else:
576   - coord = coord_raw[0, :]
577   - coord[2] = -coord[2]
578   -
579   - # Update tracker fiducial with tracker coordinates
580   - self.fiducials[fiducial_index, :] = coord[0:3]
581   -
582   - if fiducial_index == 3:
583   - self.fiducials_raw[0, :] = coord_raw[0, :]
584   - self.fiducials_raw[1, :] = coord_raw[1, :]
585   - elif fiducial_index == 4:
586   - self.fiducials_raw[2, :] = coord_raw[0, :]
587   - self.fiducials_raw[3, :] = coord_raw[1, :]
588   - else:
589   - self.fiducials_raw[4, :] = coord_raw[0, :]
590   - self.fiducials_raw[5, :] = coord_raw[1, :]
591   -
592   - # Update tracker location in the UI.
593   - for n in [0, 1, 2]:
594   - self.numctrls_coord[fiducial_index][n].SetValue(float(coord[n]))
595   -
596   - print("Set tracker fiducial {} to coordinates {}.".format(fiducial_name, coord[0:3]))
597   -
598   - def UpdateNavigationStatus(self, nav_status, vis_status):
599   - self.nav_status = nav_status
600   - if nav_status and (self.m_icp is not None):
601   - self.checkicp.Enable(True)
602   - else:
603   - self.checkicp.Enable(False)
604   - #self.checkicp.SetValue(False)
605   -
606   - def UpdateFRE(self, fre):
607   - # TODO: Exhibit FRE in a warning dialog and only starts navigation after user clicks ok
608   - self.txtctrl_fre.SetValue(str(round(fre, 2)))
609   - if fre <= 3:
610   - self.txtctrl_fre.SetBackgroundColour('GREEN')
611   - else:
612   - self.txtctrl_fre.SetBackgroundColour('RED')
613   -
614   - def UpdateTrekkerObject(self, data):
615   - # self.trk_inp = data
616   - self.trekker = data
617   -
618   - def UpdateNumTracts(self, data):
619   - self.n_tracts = data
620   -
621   - def UpdateSeedOffset(self, data):
622   - self.seed_offset = data
623   -
624   - def UpdateSeedRadius(self, data):
625   - self.seed_radius = data
626   -
627   - def UpdateSleep(self, data):
628   - self.sleep_nav = data
629   -
630   - def UpdateNumberThreads(self, data):
631   - self.n_threads = data
  913 + fiducial = self.GetFiducialByAttribute(const.TRACKER_FIDUCIALS, 'fiducial_name', fiducial_name)
  914 + fiducial_index = fiducial['fiducial_index']
632 915  
633   - def UpdateTractsVisualization(self, data):
634   - self.view_tracts = data
  916 + self.tracker.SetTrackerFiducial(fiducial_index)
635 917  
636   - def UpdateACTData(self, data):
637   - self.act_data = data
  918 + self.tracker.UpdateUI(self.select_tracker_elem, self.numctrls_coord[3:6], self.txtctrl_fre)
638 919  
639 920 def UpdateNavigationStatus(self, nav_status, vis_status):
640 921 self.nav_status = nav_status
641   - if nav_status and (self.m_icp is not None):
642   - self.checkicp.Enable(True)
643   - else:
644   - self.checkicp.Enable(False)
645   - #self.checkicp.SetValue(False)
646   -
647   - def UpdateFRE(self, fre):
648   - # TODO: Exhibit FRE in a warning dialog and only starts navigation after user clicks ok
649   - self.txtctrl_fre.SetValue(str(round(fre, 2)))
650   - if fre <= 3:
651   - self.txtctrl_fre.SetBackgroundColour('GREEN')
  922 + if nav_status and self.navigation.m_icp is not None:
  923 + self.checkbox_icp.Enable(True)
652 924 else:
653   - self.txtctrl_fre.SetBackgroundColour('RED')
  925 + self.checkbox_icp.Enable(False)
654 926  
655 927 def UpdateTrekkerObject(self, data):
656 928 # self.trk_inp = data
657   - self.trekker = data
  929 + self.navigation.trekker = data
658 930  
659 931 def UpdateNumTracts(self, data):
660   - self.n_tracts = data
  932 + self.navigation.n_tracts = data
661 933  
662 934 def UpdateSeedOffset(self, data):
663   - self.seed_offset = data
  935 + self.navigation.seed_offset = data
664 936  
665 937 def UpdateSeedRadius(self, data):
666   - self.seed_radius = data
  938 + self.navigation.seed_radius = data
667 939  
668 940 def UpdateSleep(self, data):
669   - self.sleep_nav = data
  941 + self.navigation.sleep_nav = data
670 942  
671 943 def UpdateNumberThreads(self, data):
672   - self.n_threads = data
  944 + self.navigation.n_threads = data
673 945  
674 946 def UpdateTractsVisualization(self, data):
675   - self.view_tracts = data
  947 + self.navigation.view_tracts = data
676 948  
677 949 def UpdateACTData(self, data):
678   - self.act_data = data
  950 + self.navigation.act_data = data
679 951  
680 952 def UpdateTarget(self, coord):
681   - self.target = coord
  953 + self.navigation.target = coord
682 954  
683 955 def EnableACT(self, data):
684   - self.enable_act = data
  956 + self.navigation.enable_act = data
685 957  
686 958 def UpdateImageCoordinates(self, position):
687 959 # TODO: Change from world coordinates to matrix coordinates. They are better for multi software communication.
688   - self.current_coord = position
  960 + self.navigation.current_coord = position
  961 +
689 962 for m in [0, 1, 2]:
690 963 if not self.btns_coord[m].GetValue():
691 964 for n in [0, 1, 2]:
692   - self.numctrls_coord[m][n].SetValue(float(self.current_coord[n]))
  965 + self.numctrls_coord[m][n].SetValue(float(position[n]))
693 966  
694 967 def UpdateObjectRegistration(self, data=None):
695   - if data:
696   - self.obj_reg = data
697   - self.obj_reg_status = True
698   - else:
699   - self.obj_reg = None
700   - self.obj_reg_status = False
  968 + self.navigation.obj_reg = data
701 969  
702 970 def UpdateTrackObjectState(self, evt=None, flag=None, obj_name=None, polydata=None):
703   - self.track_obj = flag
  971 + self.navigation.track_obj = flag
704 972  
705 973 def UpdateTriggerState(self, trigger_state):
706   - self.trigger_state = trigger_state
  974 + self.navigation.trigger_state = trigger_state
  975 +
  976 + def ResetICP(self):
  977 + self.navigation.ResetICP()
  978 + self.checkbox_icp.Enable(False)
  979 + self.checkbox_icp.SetValue(False)
707 980  
708 981 def OnDisconnectTracker(self):
709   - if self.tracker_id:
710   - dt.TrackerConnection(self.tracker_id, self.trk_init[0], 'disconnect')
  982 + self.tracker.DisconnectTracker()
  983 + self.ResetICP()
711 984  
712   - def OnChoiceTracker(self, evt, ctrl):
  985 + def OnChooseTracker(self, evt, ctrl):
713 986 Publisher.sendMessage('Update status text in GUI',
714 987 label=_("Configuring tracker ..."))
715 988 if hasattr(evt, 'GetSelection'):
... ... @@ -717,81 +990,16 @@ class NeuronavigationPanel(wx.Panel):
717 990 else:
718 991 choice = const.DISCTRACK
719 992  
720   - if self.trk_init:
721   - trck = self.trk_init[0]
722   - else:
723   - trck = None
724   -
725   - # Conditions check if click was on current selection and if any other tracker
726   - # has been initialized before
727   - if trck and choice != const.DISCTRACK:
728   - self.ResetTrackerFiducials()
729   - self.ResetIcp()
730   - Publisher.sendMessage('Update status text in GUI',
731   - label=_("Disconnecting tracker..."))
732   - Publisher.sendMessage('Remove sensors ID')
733   - self.trk_init = dt.TrackerConnection(self.tracker_id, trck, 'disconnect')
734   - Publisher.sendMessage('Remove object data')
735   - self.tracker_id = choice
736   - if not self.trk_init[0] and choice:
737   - Publisher.sendMessage('Update status text in GUI',
738   - label=_("Tracker disconnected successfully"))
739   - self.trk_init = dt.TrackerConnection(self.tracker_id, None, 'connect')
740   - if not self.trk_init[0]:
741   - dlg.ShowNavigationTrackerWarning(self.tracker_id, self.trk_init[1])
742   - ctrl.SetSelection(0)
743   - print("Tracker not connected!")
744   - else:
745   - ctrl.SetSelection(self.tracker_id)
746   - print("Tracker connected!")
747   - elif choice == const.DISCTRACK:
748   - if trck:
749   - self.ResetTrackerFiducials()
750   - self.ResetIcp()
751   - Publisher.sendMessage('Update status text in GUI',
752   - label=_("Disconnecting tracker ..."))
753   - Publisher.sendMessage('Remove sensors ID')
754   - Publisher.sendMessage('Remove object data')
755   - self.trk_init = dt.TrackerConnection(self.tracker_id, trck, 'disconnect')
756   - if not self.trk_init[0]:
757   - if evt is not False:
758   - dlg.ShowNavigationTrackerWarning(self.tracker_id, 'disconnect')
759   - self.tracker_id = 0
760   - ctrl.SetSelection(self.tracker_id)
761   - Publisher.sendMessage('Update status text in GUI',
762   - label=_("Tracker disconnected"))
763   - print("Tracker disconnected!")
764   - else:
765   - Publisher.sendMessage('Update status text in GUI',
766   - label=_("Tracker still connected"))
767   - print("Tracker still connected!")
768   - else:
769   - ctrl.SetSelection(self.tracker_id)
770   -
771   - else:
772   - # If trk_init is None try to connect. If doesn't succeed show dialog.
773   - if choice:
774   - self.tracker_id = choice
775   - self.trk_init = dt.TrackerConnection(self.tracker_id, None, 'connect')
776   - if not self.trk_init[0]:
777   - dlg.ShowNavigationTrackerWarning(self.tracker_id, self.trk_init[1])
778   - self.tracker_id = 0
779   - ctrl.SetSelection(self.tracker_id)
  993 + self.tracker.SetTracker(choice)
  994 + self.ResetICP()
  995 + self.tracker.UpdateUI(ctrl, self.numctrls_coord[3:6], self.txtctrl_fre)
780 996  
781 997 Publisher.sendMessage('Update status text in GUI', label=_("Ready"))
782   - Publisher.sendMessage('Update tracker initializer',
783   - nav_prop=(self.tracker_id, self.trk_init, self.ref_mode_id))
784 998  
785   - def OnChoiceRefMode(self, evt, ctrl):
786   - # When ref mode is changed the tracker coordinates are set to zero
787   - self.ref_mode_id = evt.GetSelection()
788   - self.ResetTrackerFiducials()
789   - self.ResetIcp()
790   - # Some trackers do not accept restarting within this time window
791   - # TODO: Improve the restarting of trackers after changing reference mode
792   - # self.OnChoiceTracker(None, ctrl)
793   - Publisher.sendMessage('Update tracker initializer',
794   - nav_prop=(self.tracker_id, self.trk_init, self.ref_mode_id))
  999 + def OnChooseReferenceMode(self, evt, ctrl):
  1000 + self.tracker.SetReferenceMode(evt.GetSelection())
  1001 + self.ResetICP()
  1002 +
795 1003 print("Reference mode changed!")
796 1004  
797 1005 def OnImageFiducials(self, n, evt):
... ... @@ -818,205 +1026,69 @@ class NeuronavigationPanel(wx.Panel):
818 1026 fiducial_name = const.TRACKER_FIDUCIALS[n]['fiducial_name']
819 1027 Publisher.sendMessage('Set tracker fiducial', fiducial_name=fiducial_name)
820 1028  
821   - def OnICP(self, m_change):
822   - dialog = dlg.ICPCorregistrationDialog(nav_prop=(m_change, self.tracker_id, self.trk_init, self.ref_mode_id))
823   - if dialog.ShowModal() == wx.ID_OK:
824   - self.m_icp, point_coord, transformed_points, prev_error, final_error = dialog.GetValue()
825   - #TODO: checkbox in the dialog to transfer the icp points to 3D viewer
826   - #create markers
827   - # for i in range(len(point_coord)):
828   - # img_coord = point_coord[i][0],-point_coord[i][1],point_coord[i][2], 0, 0, 0
829   - # transf_coord = transformed_points[i][0],-transformed_points[i][1],transformed_points[i][2], 0, 0, 0
830   - # Publisher.sendMessage('Create marker', coord=img_coord, marker_id=None, colour=(1,0,0))
831   - # Publisher.sendMessage('Create marker', coord=transf_coord, marker_id=None, colour=(0,0,1))
832   - if self.m_icp is not None:
833   - dlg.ReportICPerror(prev_error, final_error)
834   - self.checkicp.Enable(True)
835   - self.checkicp.SetValue(True)
836   - self.icp = True
837   - else:
838   - self.checkicp.Enable(False)
839   - self.checkicp.SetValue(False)
840   - self.icp = False
841   -
842   - return self.m_icp
843   -
844   - def Oncheckicp(self, evt, ctrl):
845   - if ctrl.GetValue() and evt and (self.m_icp is not None):
846   - self.icp = True
847   - else:
848   - self.icp = False
849   - self.ctrl_icp()
850   -
851   - def ctrl_icp(self):
852   - if self.icp:
853   - self.UpdateFRE(self.icp_fre)
854   - else:
855   - self.UpdateFRE(self.fre)
856   - self.icp_queue.put_nowait([self.icp, self.m_icp])
857   - #print(self.icp, self.m_icp)
858   -
859 1029 def onStopNavigation(self):
860   - choice_trck = self.choice_trck
  1030 + select_tracker_elem = self.select_tracker_elem
861 1031 choice_ref = self.choice_ref
862 1032  
863   - self.event.set()
864   -
865   - # print("coord unfinished: {}, queue {}", self.coord_queue.unfinished_tasks, self.coord_queue.qsize())
866   - # print("coord_tracts unfinished: {}, queue {}", self.coord_tracts_queue.unfinished_tasks, self.coord_tracts_queue.qsize())
867   - # print("tracts unfinished: {}, queue {}", self.tracts_queue.unfinished_tasks, self.tracts_queue.qsize())
868   - self.coord_queue.clear()
869   - # self.visualization_queue.clear()
870   - if self.trigger_state:
871   - self.trigger_queue.clear()
872   - if self.view_tracts:
873   - self.coord_tracts_queue.clear()
874   - self.tracts_queue.clear()
875   -
876   - # print("coord after unfinished: {}, queue {}", self.coord_queue.unfinished_tasks, self.coord_queue.qsize())
877   - # print("coord_tracts after unfinished: {}, queue {}", self.coord_tracts_queue.unfinished_tasks, self.coord_tracts_queue.qsize())
878   - # print("tracts after unfinished: {}, queue {}", self.tracts_queue.unfinished_tasks, self.tracts_queue.qsize())
879   - self.coord_queue.join()
880   - # self.visualization_queue.join()
881   - if self.trigger_state:
882   - self.trigger_queue.join()
883   - if self.view_tracts:
884   - self.coord_tracts_queue.join()
885   - self.tracts_queue.join()
886   -
887   - # print("coord join unfinished: {}, queue {}", self.coord_queue.unfinished_tasks, self.coord_queue.qsize())
888   - # print("vis join unfinished: {}, queue {}", self.visualization_queue.unfinished_tasks, self.visualization_queue.qsize())
  1033 + self.navigation.StopNavigation()
889 1034  
890 1035 # Enable all navigation buttons
891 1036 choice_ref.Enable(True)
892   - choice_trck.Enable(True)
  1037 + select_tracker_elem.Enable(True)
  1038 +
893 1039 for btn_c in self.btns_coord:
894 1040 btn_c.Enable(True)
895 1041  
896   - # if self.trigger_state:
897   - # self.trigger.stop()
  1042 + def UpdateFiducialRegistrationError(self):
  1043 + self.navigation.UpdateFiducialRegistrationError(self.tracker)
  1044 + fre, fre_ok = self.navigation.GetFiducialRegistrationError()
898 1045  
899   - vis_components = [self.trigger_state, self.view_tracts]
900   - Publisher.sendMessage("Navigation status", nav_status=False, vis_status=vis_components)
  1046 + self.txtctrl_fre.SetValue(str(round(fre, 2)))
  1047 + if fre_ok:
  1048 + self.txtctrl_fre.SetBackgroundColour('GREEN')
  1049 + else:
  1050 + self.txtctrl_fre.SetBackgroundColour('RED')
  1051 +
  1052 + return fre_ok
901 1053  
902 1054 def onStartNavigation(self):
903   - choice_trck = self.choice_trck
  1055 + select_tracker_elem = self.select_tracker_elem
904 1056 choice_ref = self.choice_ref
905 1057  
906   - errors = False
907   -
908   - # initialize jobs list
909   - jobs_list = []
910   - vis_components = [self.trigger_state, self.view_tracts]
911   - vis_queues = [self.coord_queue, self.trigger_queue, self.tracts_queue, self.icp_queue]
912   -
913   - if np.isnan(self.fiducials).any():
  1058 + if not self.tracker.AreTrackerFiducialsSet() or not self.navigation.AreImageFiducialsSet():
914 1059 wx.MessageBox(_("Invalid fiducials, select all coordinates."), _("InVesalius 3"))
915 1060  
916   - elif not self.trk_init[0] or not self.tracker_id:
  1061 + elif not self.tracker.IsTrackerInitialized():
917 1062 dlg.ShowNavigationTrackerWarning(0, 'choose')
918 1063 errors = True
919 1064  
920 1065 else:
921   - if self.event.is_set():
922   - self.event.clear()
  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.")
923 1069  
924   - # prepare GUI for navigation
925   - Publisher.sendMessage("Navigation status", nav_status=True, vis_status=vis_components)
  1070 + # Prepare GUI for navigation.
926 1071 Publisher.sendMessage("Toggle Cross", id=const.SLICE_STATE_CROSS)
927 1072 Publisher.sendMessage("Hide current mask")
928 1073  
929   - # disable all navigation buttons
  1074 + # Disable all navigation buttons.
930 1075 choice_ref.Enable(False)
931   - choice_trck.Enable(False)
  1076 + select_tracker_elem.Enable(False)
932 1077 for btn_c in self.btns_coord:
933 1078 btn_c.Enable(False)
934 1079  
935   - # fiducials matrix
936   - m_change = tr.affine_matrix_from_points(self.fiducials[3:, :].T, self.fiducials[:3, :].T,
937   - shear=False, scale=False)
938   - # initialize spatial tracker parameters
939   - tracker_mode = self.trk_init, self.tracker_id, self.ref_mode_id
940   -
941   - # compute fiducial registration error (FRE)
942   - if not self.icp_fre:
943   - self.fre = db.calculate_fre(self.fiducials_raw, self.fiducials, self.ref_mode_id, m_change)
944   - self.UpdateFRE(self.fre)
945   -
946   - if self.track_obj:
947   - # if object tracking is selected
948   - if not self.obj_reg_status:
949   - # check if object registration was performed
950   - wx.MessageBox(_("Perform coil registration before navigation."), _("InVesalius 3"))
951   - errors = True
952   - else:
953   - # if object registration was correctly performed continue with navigation
954   - # obj_reg[0] is object 3x3 fiducial matrix and obj_reg[1] is 3x3 orientation matrix
955   - obj_fiducials, obj_orients, obj_ref_mode, obj_name = self.obj_reg
956   -
957   - coreg_data = [m_change, obj_ref_mode]
958   -
959   - if self.ref_mode_id:
960   - coord_raw = dco.GetCoordinates(self.trk_init, self.tracker_id, self.ref_mode_id)
961   - else:
962   - coord_raw = np.array([None])
963   -
964   - obj_data = db.object_registration(obj_fiducials, obj_orients, coord_raw, m_change)
965   - coreg_data.extend(obj_data)
966   -
967   - queues = [self.coord_queue, self.coord_tracts_queue, self.icp_queue]
968   - jobs_list.append(dcr.CoordinateCorregistrate(self.ref_mode_id, tracker_mode, coreg_data,
969   - self.view_tracts, queues,
970   - self.event, self.sleep_nav, self.tracker_id,
971   - self.target))
972   - else:
973   - coreg_data = (m_change, 0)
974   - queues = [self.coord_queue, self.coord_tracts_queue, self.icp_queue]
975   - jobs_list.append(dcr.CoordinateCorregistrateNoObject(self.ref_mode_id, tracker_mode, coreg_data,
976   - self.view_tracts, queues,
977   - self.event, self.sleep_nav))
978   -
979   - if not errors:
980   - #TODO: Test the trigger thread
981   - if self.trigger_state:
982   - # self.trigger = trig.Trigger(nav_id)
983   - jobs_list.append(trig.TriggerNew(self.trigger_queue, self.event, self.sleep_nav))
984   -
985   - if self.view_tracts:
986   - # initialize Trekker parameters
987   - slic = sl.Slice()
988   - prj_data = prj.Project()
989   - matrix_shape = tuple(prj_data.matrix_shape)
990   - affine = slic.affine.copy()
991   - affine[1, -1] -= matrix_shape[1]
992   - affine_vtk = vtk_utils.numpy_to_vtkMatrix4x4(affine)
993   - Publisher.sendMessage("Update marker offset state", create=True)
994   - self.trk_inp = self.trekker, affine, self.seed_offset, self.n_tracts, self.seed_radius,\
995   - self.n_threads, self.act_data, affine_vtk, matrix_shape[1]
996   - # print("Appending the tract computation thread!")
997   - queues = [self.coord_tracts_queue, self.tracts_queue]
998   - if self.enable_act:
999   - jobs_list.append(dti.ComputeTractsACTThread(self.trk_inp, queues, self.event, self.sleep_nav))
1000   - else:
1001   - jobs_list.append(dti.ComputeTractsThread(self.trk_inp, queues, self.event, self.sleep_nav))
1002   -
1003   - jobs_list.append(UpdateNavigationScene(vis_queues, vis_components,
1004   - self.event, self.sleep_nav))
  1080 + self.navigation.StartNavigation(self.tracker)
1005 1081  
1006   - for jobs in jobs_list:
1007   - # jobs.daemon = True
1008   - jobs.start()
1009   - # del jobs
  1082 + # Update FRE once more after starting the navigation, due to the optional use of ICP,
  1083 + # which improves FRE.
  1084 + self.UpdateFiducialRegistrationError()
1010 1085  
1011   - if not self.checkicp.GetValue():
1012   - if dlg.ICPcorregistration(self.fre):
1013   - m_icp = self.OnICP(m_change)
1014   - self.icp_fre = db.calculate_fre(self.fiducials_raw, self.fiducials, self.ref_mode_id,
1015   - m_change, m_icp)
1016   - self.ctrl_icp()
  1086 + if self.navigation.use_icp:
  1087 + self.checkbox_icp.Enable(True)
  1088 + self.checkbox_icp.SetValue(True)
1017 1089  
1018 1090 def OnNavigate(self, evt, btn_nav):
1019   - choice_trck = self.choice_trck
  1091 + select_tracker_elem = self.select_tracker_elem
1020 1092 choice_ref = self.choice_ref
1021 1093  
1022 1094 nav_id = btn_nav.GetValue()
... ... @@ -1041,28 +1113,15 @@ class NeuronavigationPanel(wx.Panel):
1041 1113 for n in range(0, 3):
1042 1114 self.numctrls_coord[m][n].SetValue(0.0)
1043 1115  
1044   - def ResetTrackerFiducials(self):
1045   - for m in range(3, 6):
1046   - self.fiducials[m, :] = [np.nan, np.nan, np.nan]
1047   - for n in range(0, 3):
1048   - self.numctrls_coord[m][n].SetValue(0.0)
1049   -
1050   - self.txtctrl_fre.SetValue('')
1051   - self.txtctrl_fre.SetBackgroundColour('WHITE')
1052   -
1053   - def ResetIcp(self):
1054   - self.m_icp = None
1055   - self.fre = None
1056   - self.icp_fre = None
1057   - self.icp = False
1058   - self.checkicp.Enable(False)
1059   - self.checkicp.SetValue(False)
  1116 + def OnCheckboxICP(self, evt, ctrl):
  1117 + self.navigation.SetICP(ctrl.GetValue())
  1118 + self.UpdateFiducialRegistrationError()
1060 1119  
1061 1120 def OnCloseProject(self):
1062 1121 self.ResetTrackerFiducials()
1063 1122 self.ResetImageFiducials()
1064   - self.ResetIcp()
1065   - self.OnChoiceTracker(False, self.choice_trck)
  1123 + self.navigation.ResetICP()
  1124 + self.OnChooseTracker(False, self.select_tracker_elem)
1066 1125 Publisher.sendMessage('Update object registration')
1067 1126 Publisher.sendMessage('Update track object state', flag=False, obj_name=False)
1068 1127 Publisher.sendMessage('Delete all markers')
... ...