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,12 +675,12 @@ DEFAULT_TRACKER = SELECT
675 675
676 NDICOMPORT = b'COM1' 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 STATIC_REF = 0 685 STATIC_REF = 0
686 DYNAMIC_REF = 1 686 DYNAMIC_REF = 1
@@ -729,21 +729,21 @@ TRACKER_FIDUCIALS = [ @@ -729,21 +729,21 @@ TRACKER_FIDUCIALS = [
729 'button_id': TR1, 729 'button_id': TR1,
730 'label': 'LET', 730 'label': 'LET',
731 'fiducial_name': 'LE', 731 'fiducial_name': 'LE',
732 - 'fiducial_index': 3, 732 + 'fiducial_index': 0,
733 'tip': _("Select left ear with spatial tracker"), 733 'tip': _("Select left ear with spatial tracker"),
734 }, 734 },
735 { 735 {
736 'button_id': TR2, 736 'button_id': TR2,
737 'label': 'RET', 737 'label': 'RET',
738 'fiducial_name': 'RE', 738 'fiducial_name': 'RE',
739 - 'fiducial_index': 4, 739 + 'fiducial_index': 1,
740 'tip': _("Select right ear with spatial tracker"), 740 'tip': _("Select right ear with spatial tracker"),
741 }, 741 },
742 { 742 {
743 'button_id': TR3, 743 'button_id': TR3,
744 'label': 'NAT', 744 'label': 'NAT',
745 'fiducial_name': 'NA', 745 'fiducial_name': 'NA',
746 - 'fiducial_index': 5, 746 + 'fiducial_index': 2,
747 'tip': _("Select nasion with spatial tracker"), 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,10 +171,10 @@ def corregistrate_dynamic(inp, coord_raw, ref_mode_id, icp):
171 171
172 172
173 class CoordinateCorregistrate(threading.Thread): 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 threading.Thread.__init__(self, name='CoordCoregObject') 175 threading.Thread.__init__(self, name='CoordCoregObject')
176 self.ref_mode_id = ref_mode_id 176 self.ref_mode_id = ref_mode_id
177 - self.trck_info = trck_info 177 + self.tracker = tracker
178 self.coreg_data = coreg_data 178 self.coreg_data = coreg_data
179 self.coord_queue = queues[0] 179 self.coord_queue = queues[0]
180 self.view_tracts = view_tracts 180 self.view_tracts = view_tracts
@@ -198,11 +198,11 @@ class CoordinateCorregistrate(threading.Thread): @@ -198,11 +198,11 @@ class CoordinateCorregistrate(threading.Thread):
198 self.target[1] = -self.target[1] 198 self.target[1] = -self.target[1]
199 199
200 def run(self): 200 def run(self):
201 - trck_info = self.trck_info  
202 coreg_data = self.coreg_data 201 coreg_data = self.coreg_data
203 view_obj = 1 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 # print('CoordCoreg: event {}'.format(self.event.is_set())) 206 # print('CoordCoreg: event {}'.format(self.event.is_set()))
207 while not self.event.is_set(): 207 while not self.event.is_set():
208 try: 208 try:
@@ -258,10 +258,10 @@ class CoordinateCorregistrate(threading.Thread): @@ -258,10 +258,10 @@ class CoordinateCorregistrate(threading.Thread):
258 258
259 259
260 class CoordinateCorregistrateNoObject(threading.Thread): 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 threading.Thread.__init__(self, name='CoordCoregNoObject') 262 threading.Thread.__init__(self, name='CoordCoregNoObject')
263 self.ref_mode_id = ref_mode_id 263 self.ref_mode_id = ref_mode_id
264 - self.trck_info = trck_info 264 + self.tracker = tracker
265 self.coreg_data = coreg_data 265 self.coreg_data = coreg_data
266 self.coord_queue = queues[0] 266 self.coord_queue = queues[0]
267 self.view_tracts = view_tracts 267 self.view_tracts = view_tracts
@@ -273,11 +273,10 @@ class CoordinateCorregistrateNoObject(threading.Thread): @@ -273,11 +273,10 @@ class CoordinateCorregistrateNoObject(threading.Thread):
273 self.m_icp = None 273 self.m_icp = None
274 274
275 def run(self): 275 def run(self):
276 - trck_info = self.trck_info  
277 coreg_data = self.coreg_data 276 coreg_data = self.coreg_data
278 view_obj = 0 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 # print('CoordCoreg: event {}'.format(self.event.is_set())) 280 # print('CoordCoreg: event {}'.format(self.event.is_set()))
282 while not self.event.is_set(): 281 while not self.event.is_set():
283 try: 282 try:
invesalius/gui/task_navigator.py
@@ -300,39 +300,18 @@ class InnerFoldPanel(wx.Panel): @@ -300,39 +300,18 @@ class InnerFoldPanel(wx.Panel):
300 Publisher.sendMessage('Update volume camera state', camera_state=self.checkcamera.GetValue()) 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 self.correg = None 306 self.correg = None
321 self.current_coord = 0, 0, 0 307 self.current_coord = 0, 0, 0
322 - self.trk_init = None  
323 - self.nav_status = False  
324 self.target = None 308 self.target = None
325 self.trigger = None 309 self.trigger = None
326 self.trigger_state = False 310 self.trigger_state = False
327 self.obj_reg = None 311 self.obj_reg = None
328 - self.obj_reg_status = False  
329 self.track_obj = False 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 self.coord_queue = QueueCustom(maxsize=1) 315 self.coord_queue = QueueCustom(maxsize=1)
337 self.icp_queue = QueueCustom(maxsize=1) 316 self.icp_queue = QueueCustom(maxsize=1)
338 # self.visualization_queue = QueueCustom(maxsize=1) 317 # self.visualization_queue = QueueCustom(maxsize=1)
@@ -352,21 +331,400 @@ class NeuronavigationPanel(wx.Panel): @@ -352,21 +331,400 @@ class NeuronavigationPanel(wx.Panel):
352 self.seed_radius = const.SEED_RADIUS 331 self.seed_radius = const.SEED_RADIUS
353 self.sleep_nav = const.SLEEP_NAVIGATION 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 self.tracker_id = const.DEFAULT_TRACKER 521 self.tracker_id = const.DEFAULT_TRACKER
356 self.ref_mode_id = const.DEFAULT_REF_MODE 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 # Initialize list of buttons and numctrls for wx objects 713 # Initialize list of buttons and numctrls for wx objects
359 self.btns_coord = [None, None, None, None, None, None] 714 self.btns_coord = [None, None, None, None, None, None]
360 self.numctrls_coord = [[], [], [], [], [], []] 715 self.numctrls_coord = [[], [], [], [], [], []]
361 716
362 # ComboBox for spatial tracker device selection 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 tooltip = wx.ToolTip(_("Choose the tracking device")) 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 # ComboBox for tracker reference mode 729 # ComboBox for tracker reference mode
372 tooltip = wx.ToolTip(_("Choose the navigation reference mode")) 730 tooltip = wx.ToolTip(_("Choose the navigation reference mode"))
@@ -374,7 +732,7 @@ class NeuronavigationPanel(wx.Panel): @@ -374,7 +732,7 @@ class NeuronavigationPanel(wx.Panel):
374 choices=const.REF_MODE, style=wx.CB_DROPDOWN|wx.CB_READONLY) 732 choices=const.REF_MODE, style=wx.CB_DROPDOWN|wx.CB_READONLY)
375 choice_ref.SetSelection(const.DEFAULT_REF_MODE) 733 choice_ref.SetSelection(const.DEFAULT_REF_MODE)
376 choice_ref.SetToolTip(tooltip) 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 self.choice_ref = choice_ref 736 self.choice_ref = choice_ref
379 737
380 # Toggle buttons for image fiducials 738 # Toggle buttons for image fiducials
@@ -422,12 +780,12 @@ class NeuronavigationPanel(wx.Panel): @@ -422,12 +780,12 @@ class NeuronavigationPanel(wx.Panel):
422 btn_nav.Bind(wx.EVT_TOGGLEBUTTON, partial(self.OnNavigate, btn_nav=btn_nav)) 780 btn_nav.Bind(wx.EVT_TOGGLEBUTTON, partial(self.OnNavigate, btn_nav=btn_nav))
423 781
424 tooltip = wx.ToolTip(_(u"Refine the coregistration")) 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 # An indicator for pedal trigger 790 # An indicator for pedal trigger
433 if HAS_PEDAL_CONNECTION and self.pedal_connection.in_use: 791 if HAS_PEDAL_CONNECTION and self.pedal_connection.in_use:
@@ -454,7 +812,7 @@ class NeuronavigationPanel(wx.Panel): @@ -454,7 +812,7 @@ class NeuronavigationPanel(wx.Panel):
454 812
455 # Sizer to group all GUI objects 813 # Sizer to group all GUI objects
456 choice_sizer = wx.FlexGridSizer(rows=1, cols=2, hgap=5, vgap=5) 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 (choice_ref, wx.RIGHT)]) 816 (choice_ref, wx.RIGHT)])
459 817
460 coord_sizer = wx.GridBagSizer(hgap=5, vgap=5) 818 coord_sizer = wx.GridBagSizer(hgap=5, vgap=5)
@@ -471,7 +829,7 @@ class NeuronavigationPanel(wx.Panel): @@ -471,7 +829,7 @@ class NeuronavigationPanel(wx.Panel):
471 (txtctrl_fre, 0, wx.ALIGN_CENTER_HORIZONTAL | wx.ALIGN_CENTER_VERTICAL), 829 (txtctrl_fre, 0, wx.ALIGN_CENTER_HORIZONTAL | wx.ALIGN_CENTER_VERTICAL),
472 (btn_nav, 0, wx.ALIGN_CENTER_HORIZONTAL | wx.ALIGN_CENTER_VERTICAL), 830 (btn_nav, 0, wx.ALIGN_CENTER_HORIZONTAL | wx.ALIGN_CENTER_VERTICAL),
473 (txt_icp, 0, wx.ALIGN_CENTER_HORIZONTAL | wx.ALIGN_CENTER_VERTICAL), 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 pedal_sizer = wx.FlexGridSizer(rows=1, cols=2, hgap=5, vgap=5) 834 pedal_sizer = wx.FlexGridSizer(rows=1, cols=2, hgap=5, vgap=5)
477 if HAS_PEDAL_CONNECTION and self.pedal_connection.in_use: 835 if HAS_PEDAL_CONNECTION and self.pedal_connection.in_use:
@@ -544,172 +902,87 @@ class NeuronavigationPanel(wx.Panel): @@ -544,172 +902,87 @@ class NeuronavigationPanel(wx.Panel):
544 def SetImageFiducial(self, fiducial_name, coord): 902 def SetImageFiducial(self, fiducial_name, coord):
545 fiducial = self.GetFiducialByAttribute(const.IMAGE_FIDUCIALS, 'fiducial_name', fiducial_name) 903 fiducial = self.GetFiducialByAttribute(const.IMAGE_FIDUCIALS, 'fiducial_name', fiducial_name)
546 fiducial_index = fiducial['fiducial_index'] 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 def SetTrackerFiducial(self, fiducial_name): 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 dlg.ShowNavigationTrackerWarning(0, 'choose') 910 dlg.ShowNavigationTrackerWarning(0, 'choose')
559 return 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 def UpdateNavigationStatus(self, nav_status, vis_status): 920 def UpdateNavigationStatus(self, nav_status, vis_status):
640 self.nav_status = nav_status 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 else: 924 else:
653 - self.txtctrl_fre.SetBackgroundColour('RED') 925 + self.checkbox_icp.Enable(False)
654 926
655 def UpdateTrekkerObject(self, data): 927 def UpdateTrekkerObject(self, data):
656 # self.trk_inp = data 928 # self.trk_inp = data
657 - self.trekker = data 929 + self.navigation.trekker = data
658 930
659 def UpdateNumTracts(self, data): 931 def UpdateNumTracts(self, data):
660 - self.n_tracts = data 932 + self.navigation.n_tracts = data
661 933
662 def UpdateSeedOffset(self, data): 934 def UpdateSeedOffset(self, data):
663 - self.seed_offset = data 935 + self.navigation.seed_offset = data
664 936
665 def UpdateSeedRadius(self, data): 937 def UpdateSeedRadius(self, data):
666 - self.seed_radius = data 938 + self.navigation.seed_radius = data
667 939
668 def UpdateSleep(self, data): 940 def UpdateSleep(self, data):
669 - self.sleep_nav = data 941 + self.navigation.sleep_nav = data
670 942
671 def UpdateNumberThreads(self, data): 943 def UpdateNumberThreads(self, data):
672 - self.n_threads = data 944 + self.navigation.n_threads = data
673 945
674 def UpdateTractsVisualization(self, data): 946 def UpdateTractsVisualization(self, data):
675 - self.view_tracts = data 947 + self.navigation.view_tracts = data
676 948
677 def UpdateACTData(self, data): 949 def UpdateACTData(self, data):
678 - self.act_data = data 950 + self.navigation.act_data = data
679 951
680 def UpdateTarget(self, coord): 952 def UpdateTarget(self, coord):
681 - self.target = coord 953 + self.navigation.target = coord
682 954
683 def EnableACT(self, data): 955 def EnableACT(self, data):
684 - self.enable_act = data 956 + self.navigation.enable_act = data
685 957
686 def UpdateImageCoordinates(self, position): 958 def UpdateImageCoordinates(self, position):
687 # TODO: Change from world coordinates to matrix coordinates. They are better for multi software communication. 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 for m in [0, 1, 2]: 962 for m in [0, 1, 2]:
690 if not self.btns_coord[m].GetValue(): 963 if not self.btns_coord[m].GetValue():
691 for n in [0, 1, 2]: 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 def UpdateObjectRegistration(self, data=None): 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 def UpdateTrackObjectState(self, evt=None, flag=None, obj_name=None, polydata=None): 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 def UpdateTriggerState(self, trigger_state): 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 def OnDisconnectTracker(self): 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 Publisher.sendMessage('Update status text in GUI', 986 Publisher.sendMessage('Update status text in GUI',
714 label=_("Configuring tracker ...")) 987 label=_("Configuring tracker ..."))
715 if hasattr(evt, 'GetSelection'): 988 if hasattr(evt, 'GetSelection'):
@@ -717,81 +990,16 @@ class NeuronavigationPanel(wx.Panel): @@ -717,81 +990,16 @@ class NeuronavigationPanel(wx.Panel):
717 else: 990 else:
718 choice = const.DISCTRACK 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 Publisher.sendMessage('Update status text in GUI', label=_("Ready")) 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 print("Reference mode changed!") 1003 print("Reference mode changed!")
796 1004
797 def OnImageFiducials(self, n, evt): 1005 def OnImageFiducials(self, n, evt):
@@ -818,205 +1026,69 @@ class NeuronavigationPanel(wx.Panel): @@ -818,205 +1026,69 @@ class NeuronavigationPanel(wx.Panel):
818 fiducial_name = const.TRACKER_FIDUCIALS[n]['fiducial_name'] 1026 fiducial_name = const.TRACKER_FIDUCIALS[n]['fiducial_name']
819 Publisher.sendMessage('Set tracker fiducial', fiducial_name=fiducial_name) 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 def onStopNavigation(self): 1029 def onStopNavigation(self):
860 - choice_trck = self.choice_trck 1030 + select_tracker_elem = self.select_tracker_elem
861 choice_ref = self.choice_ref 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 # Enable all navigation buttons 1035 # Enable all navigation buttons
891 choice_ref.Enable(True) 1036 choice_ref.Enable(True)
892 - choice_trck.Enable(True) 1037 + select_tracker_elem.Enable(True)
  1038 +
893 for btn_c in self.btns_coord: 1039 for btn_c in self.btns_coord:
894 btn_c.Enable(True) 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 def onStartNavigation(self): 1054 def onStartNavigation(self):
903 - choice_trck = self.choice_trck 1055 + select_tracker_elem = self.select_tracker_elem
904 choice_ref = self.choice_ref 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 wx.MessageBox(_("Invalid fiducials, select all coordinates."), _("InVesalius 3")) 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 dlg.ShowNavigationTrackerWarning(0, 'choose') 1062 dlg.ShowNavigationTrackerWarning(0, 'choose')
918 errors = True 1063 errors = True
919 1064
920 else: 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 Publisher.sendMessage("Toggle Cross", id=const.SLICE_STATE_CROSS) 1071 Publisher.sendMessage("Toggle Cross", id=const.SLICE_STATE_CROSS)
927 Publisher.sendMessage("Hide current mask") 1072 Publisher.sendMessage("Hide current mask")
928 1073
929 - # disable all navigation buttons 1074 + # Disable all navigation buttons.
930 choice_ref.Enable(False) 1075 choice_ref.Enable(False)
931 - choice_trck.Enable(False) 1076 + select_tracker_elem.Enable(False)
932 for btn_c in self.btns_coord: 1077 for btn_c in self.btns_coord:
933 btn_c.Enable(False) 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 def OnNavigate(self, evt, btn_nav): 1090 def OnNavigate(self, evt, btn_nav):
1019 - choice_trck = self.choice_trck 1091 + select_tracker_elem = self.select_tracker_elem
1020 choice_ref = self.choice_ref 1092 choice_ref = self.choice_ref
1021 1093
1022 nav_id = btn_nav.GetValue() 1094 nav_id = btn_nav.GetValue()
@@ -1041,28 +1113,15 @@ class NeuronavigationPanel(wx.Panel): @@ -1041,28 +1113,15 @@ class NeuronavigationPanel(wx.Panel):
1041 for n in range(0, 3): 1113 for n in range(0, 3):
1042 self.numctrls_coord[m][n].SetValue(0.0) 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 def OnCloseProject(self): 1120 def OnCloseProject(self):
1062 self.ResetTrackerFiducials() 1121 self.ResetTrackerFiducials()
1063 self.ResetImageFiducials() 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 Publisher.sendMessage('Update object registration') 1125 Publisher.sendMessage('Update object registration')
1067 Publisher.sendMessage('Update track object state', flag=False, obj_name=False) 1126 Publisher.sendMessage('Update track object state', flag=False, obj_name=False)
1068 Publisher.sendMessage('Delete all markers') 1127 Publisher.sendMessage('Delete all markers')