Commit e971a69900d0f036bd49404d0c75b65f5ca0e116
Committed by
GitHub
1 parent
df0e6ca1
Exists in
master
ADD: Send coil mesh using NeuronavigationApi (#418)
* ADD: Send coil mesh using NeuronavigationApi * Review comments, some tweaks to save memory and speed up
Showing
2 changed files
with
43 additions
and
4 deletions
Show diff stats
invesalius/gui/task_navigator.py
| @@ -201,7 +201,12 @@ class InnerFoldPanel(wx.Panel): | @@ -201,7 +201,12 @@ class InnerFoldPanel(wx.Panel): | ||
| 201 | 201 | ||
| 202 | # Fold 2 - Object registration panel | 202 | # Fold 2 - Object registration panel |
| 203 | item = fold_panel.AddFoldPanel(_("Object registration"), collapsed=True) | 203 | item = fold_panel.AddFoldPanel(_("Object registration"), collapsed=True) |
| 204 | - otw = ObjectRegistrationPanel(item, tracker, pedal_connection) | 204 | + otw = ObjectRegistrationPanel( |
| 205 | + parent=item, | ||
| 206 | + tracker=tracker, | ||
| 207 | + pedal_connection=pedal_connection, | ||
| 208 | + neuronavigation_api=neuronavigation_api, | ||
| 209 | + ) | ||
| 205 | 210 | ||
| 206 | fold_panel.ApplyCaptionStyle(item, style) | 211 | fold_panel.ApplyCaptionStyle(item, style) |
| 207 | fold_panel.AddFoldPanelWindow(item, otw, spacing=0, | 212 | fold_panel.AddFoldPanelWindow(item, otw, spacing=0, |
| @@ -889,7 +894,7 @@ class NeuronavigationPanel(wx.Panel): | @@ -889,7 +894,7 @@ class NeuronavigationPanel(wx.Panel): | ||
| 889 | 894 | ||
| 890 | 895 | ||
| 891 | class ObjectRegistrationPanel(wx.Panel): | 896 | class ObjectRegistrationPanel(wx.Panel): |
| 892 | - def __init__(self, parent, tracker, pedal_connection): | 897 | + def __init__(self, parent, tracker, pedal_connection, neuronavigation_api): |
| 893 | wx.Panel.__init__(self, parent) | 898 | wx.Panel.__init__(self, parent) |
| 894 | try: | 899 | try: |
| 895 | default_colour = wx.SystemSettings.GetColour(wx.SYS_COLOUR_MENUBAR) | 900 | default_colour = wx.SystemSettings.GetColour(wx.SYS_COLOUR_MENUBAR) |
| @@ -901,6 +906,7 @@ class ObjectRegistrationPanel(wx.Panel): | @@ -901,6 +906,7 @@ class ObjectRegistrationPanel(wx.Panel): | ||
| 901 | 906 | ||
| 902 | self.tracker = tracker | 907 | self.tracker = tracker |
| 903 | self.pedal_connection = pedal_connection | 908 | self.pedal_connection = pedal_connection |
| 909 | + self.neuronavigation_api = neuronavigation_api | ||
| 904 | 910 | ||
| 905 | self.nav_prop = None | 911 | self.nav_prop = None |
| 906 | self.obj_fiducials = None | 912 | self.obj_fiducials = None |
| @@ -917,7 +923,7 @@ class ObjectRegistrationPanel(wx.Panel): | @@ -917,7 +923,7 @@ class ObjectRegistrationPanel(wx.Panel): | ||
| 917 | btn_new = wx.Button(self, -1, _("New"), size=wx.Size(65, 23)) | 923 | btn_new = wx.Button(self, -1, _("New"), size=wx.Size(65, 23)) |
| 918 | btn_new.SetToolTip(tooltip) | 924 | btn_new.SetToolTip(tooltip) |
| 919 | btn_new.Enable(1) | 925 | btn_new.Enable(1) |
| 920 | - btn_new.Bind(wx.EVT_BUTTON, self.OnLinkCreate) | 926 | + btn_new.Bind(wx.EVT_BUTTON, self.OnCreateNewCoil) |
| 921 | self.btn_new = btn_new | 927 | self.btn_new = btn_new |
| 922 | 928 | ||
| 923 | # Button for import config coil file | 929 | # Button for import config coil file |
| @@ -1060,13 +1066,16 @@ class ObjectRegistrationPanel(wx.Panel): | @@ -1060,13 +1066,16 @@ class ObjectRegistrationPanel(wx.Panel): | ||
| 1060 | coil_index = evt.GetSelection() | 1066 | coil_index = evt.GetSelection() |
| 1061 | Publisher.sendMessage('Change selected coil', self.coil_list[coil_index][1]) | 1067 | Publisher.sendMessage('Change selected coil', self.coil_list[coil_index][1]) |
| 1062 | 1068 | ||
| 1063 | - def OnLinkCreate(self, event=None): | 1069 | + def OnCreateNewCoil(self, event=None): |
| 1064 | 1070 | ||
| 1065 | if self.tracker.IsTrackerInitialized(): | 1071 | if self.tracker.IsTrackerInitialized(): |
| 1066 | dialog = dlg.ObjectCalibrationDialog(self.tracker, self.pedal_connection) | 1072 | dialog = dlg.ObjectCalibrationDialog(self.tracker, self.pedal_connection) |
| 1067 | try: | 1073 | try: |
| 1068 | if dialog.ShowModal() == wx.ID_OK: | 1074 | if dialog.ShowModal() == wx.ID_OK: |
| 1069 | self.obj_fiducials, self.obj_orients, self.obj_ref_mode, self.obj_name, polydata, use_default_object = dialog.GetValue() | 1075 | self.obj_fiducials, self.obj_orients, self.obj_ref_mode, self.obj_name, polydata, use_default_object = dialog.GetValue() |
| 1076 | + | ||
| 1077 | + self.neuronavigation_api.update_coil_mesh(polydata) | ||
| 1078 | + | ||
| 1070 | if np.isfinite(self.obj_fiducials).all() and np.isfinite(self.obj_orients).all(): | 1079 | if np.isfinite(self.obj_fiducials).all() and np.isfinite(self.obj_orients).all(): |
| 1071 | self.checktrack.Enable(1) | 1080 | self.checktrack.Enable(1) |
| 1072 | Publisher.sendMessage('Update object registration', | 1081 | Publisher.sendMessage('Update object registration', |
invesalius/net/neuronavigation_api.py
| @@ -20,6 +20,9 @@ | @@ -20,6 +20,9 @@ | ||
| 20 | from invesalius.pubsub import pub as Publisher | 20 | from invesalius.pubsub import pub as Publisher |
| 21 | from invesalius.utils import Singleton | 21 | from invesalius.utils import Singleton |
| 22 | 22 | ||
| 23 | +import numpy as np | ||
| 24 | +from vtk.numpy_interface import dataset_adapter | ||
| 25 | + | ||
| 23 | class NeuronavigationApi(metaclass=Singleton): | 26 | class NeuronavigationApi(metaclass=Singleton): |
| 24 | """ | 27 | """ |
| 25 | An API used internally in InVesalius to communicate with the | 28 | An API used internally in InVesalius to communicate with the |
| @@ -34,6 +37,8 @@ class NeuronavigationApi(metaclass=Singleton): | @@ -34,6 +37,8 @@ class NeuronavigationApi(metaclass=Singleton): | ||
| 34 | 37 | ||
| 35 | If connection object is not given or it is None, skip doing the updates. | 38 | If connection object is not given or it is None, skip doing the updates. |
| 36 | """ | 39 | """ |
| 40 | + N_VERTICES_IN_POLYGON = 3 | ||
| 41 | + | ||
| 37 | def __init__(self, connection=None): | 42 | def __init__(self, connection=None): |
| 38 | if connection is not None: | 43 | if connection is not None: |
| 39 | assert self._hasmethod(connection, 'update_coil_pose') | 44 | assert self._hasmethod(connection, 'update_coil_pose') |
| @@ -65,3 +70,28 @@ class NeuronavigationApi(metaclass=Singleton): | @@ -65,3 +70,28 @@ class NeuronavigationApi(metaclass=Singleton): | ||
| 65 | position=position, | 70 | position=position, |
| 66 | orientation=orientation, | 71 | orientation=orientation, |
| 67 | ) | 72 | ) |
| 73 | + | ||
| 74 | + def update_coil_mesh(self, polydata): | ||
| 75 | + wrapped = dataset_adapter.WrapDataObject(polydata) | ||
| 76 | + | ||
| 77 | + points = np.asarray(wrapped.Points) | ||
| 78 | + polygons_raw = np.asarray(wrapped.Polygons) | ||
| 79 | + | ||
| 80 | + # The polygons are returned as 1d-array of the form | ||
| 81 | + # | ||
| 82 | + # [n_0, id_0(0), id_0(1), ..., id_0(n_0), | ||
| 83 | + # n_1, id_1(0), id_1(1), ..., id_1(n_1), | ||
| 84 | + # ...] | ||
| 85 | + # | ||
| 86 | + # where n_i is the number of vertices in polygon i, and id_i's are indices to the vertex list. | ||
| 87 | + # | ||
| 88 | + # Assert that all polygons have an equal number of vertices, reshape the array, and drop n_i's. | ||
| 89 | + # | ||
| 90 | + assert np.all(polygons_raw[0::self.N_VERTICES_IN_POLYGON + 1] == self.N_VERTICES_IN_POLYGON) | ||
| 91 | + | ||
| 92 | + polygons = polygons_raw.reshape(-1, self.N_VERTICES_IN_POLYGON + 1)[:, 1:] | ||
| 93 | + | ||
| 94 | + self.connection.update_coil_mesh( | ||
| 95 | + points=points, | ||
| 96 | + polygons=polygons, | ||
| 97 | + ) |