Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Neuronavigation: TMS Motor Mapping Visualization #825

Merged
merged 48 commits into from
Sep 30, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
48 commits
Select commit Hold shift + click to select a range
ef08c8f
start integration of MEP visualizer
mo-gaafar Jun 25, 2024
29ffb00
Link brain surface and add marker points functionality with random MEP
mo-gaafar Jun 28, 2024
5030051
ADD: ui elements
mo-gaafar Jul 9, 2024
e75f468
ADD: MEP UI in Preferences and Markers List
mo-gaafar Jul 25, 2024
5dc8040
Fix MEP Visualization toggle and add MEP Field to marker table
mo-gaafar Jul 29, 2024
ba2d36c
Add preferences saving
mo-gaafar Jul 30, 2024
c68c3cc
MEP Preferences reset to default
mo-gaafar Jul 31, 2024
b52c83a
Hide/Show mep configuration in preferences
mo-gaafar Jul 31, 2024
9010410
Start Refactoring MEP Mapping UI and functionality
mo-gaafar Aug 4, 2024
8fb5b1f
Update mep_visualizer.py
mo-gaafar Aug 12, 2024
4d0d827
Squashed commit of the following:
mo-gaafar Aug 12, 2024
5dfe1b8
FIX: sync to main and fix ruff formatting issues
mo-gaafar Aug 12, 2024
96852e2
Update mep_visualizer.py
mo-gaafar Aug 12, 2024
9618abd
FIX: fix visualization and add brain surface selection and hiding log…
mo-gaafar Aug 13, 2024
4c61e28
ADD: New color map presets
mo-gaafar Aug 13, 2024
6cf0464
FIX: reduce issues with showing/hiding actors and reduced coupling
mo-gaafar Aug 13, 2024
3b3a2bc
ADD: colormap visualization in preferences and integrated
mo-gaafar Aug 13, 2024
d3be678
FIX: Squashed merge commit of everything since jun 10
mo-gaafar Aug 14, 2024
f0d1dea
FIX: prepared for pull request
mo-gaafar Aug 14, 2024
0fd4883
Merge branch 'master' into mep_visualization
mo-gaafar Aug 14, 2024
1c3475b
FIX: import error
mo-gaafar Aug 14, 2024
8edc5b5
FIX: remove comments and typings/wx
mo-gaafar Aug 14, 2024
b517440
FIX: removed duplicate in task_navigator and added missing copyright …
mo-gaafar Aug 14, 2024
7d9eeeb
FIX: remove logging related changes
mo-gaafar Aug 14, 2024
ba7568e
Revert "FIX: remove logging related changes"
mo-gaafar Aug 14, 2024
2e63312
FIX: add preferences from upstream and remove changes
mo-gaafar Aug 14, 2024
3c9bbb4
ADD: added MEP viz settings to preferences
mo-gaafar Aug 14, 2024
2935b82
FIX: fix preferences ok and enter, remove comments
mo-gaafar Aug 14, 2024
a6dfa01
CLEAN: remove redundant lines
mo-gaafar Aug 15, 2024
73a1ba3
MOD: allows UpdateVisualization when navigation is on
rmatsuda Aug 15, 2024
ee0857e
RUFF formating
rmatsuda Aug 15, 2024
c1eb64c
FIX: surface selection persistence accross sessions and loading after…
mo-gaafar Aug 15, 2024
8bfdca0
MOD: Keep bounds and brain surface index when reseting MEP visualizat…
mo-gaafar Aug 16, 2024
85ae058
ENH: Motor map button behavior
rmatsuda Aug 16, 2024
19f24dc
FIX: saving and loading .mkss files with mep_values and incremented f…
mo-gaafar Aug 29, 2024
fa8f537
wip
rmatsuda Sep 9, 2024
6305dd3
ADD: tms target projection on the brain surface
rmatsuda Sep 26, 2024
1009778
FIX: format
rmatsuda Sep 26, 2024
d311c32
Merge branch 'master' into pr/825
rmatsuda Sep 26, 2024
acd2c94
FIX: if no marker, self.points is reset. Deals with the last marker d…
rmatsuda Sep 26, 2024
0276e51
MOD: minor UI changes
rmatsuda Sep 29, 2024
1f83d46
Ruff
rmatsuda Sep 29, 2024
128673f
MOD: UI changes to minimize spacing
rmatsuda Sep 29, 2024
85df02f
ADD: progress dialog to enable brain mapping
rmatsuda Sep 29, 2024
97e4b69
Ruff
rmatsuda Sep 29, 2024
334afba
FIX: typos
rmatsuda Sep 30, 2024
bd9f4a1
FIX: typo and dims_size increase
rmatsuda Sep 30, 2024
6bddbd0
Merge branch 'master' into pr/825
rmatsuda Sep 30, 2024
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Binary file added icons/brain_eye.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
90 changes: 85 additions & 5 deletions invesalius/constants.py
Original file line number Diff line number Diff line change
Expand Up @@ -801,9 +801,10 @@
TARGET_COLUMN = 4
Z_OFFSET_COLUMN = 5
POINT_OF_INTEREST_TARGET_COLUMN = 6
X_COLUMN = 7
Y_COLUMN = 8
Z_COLUMN = 9
MEP_COLUMN = 7
X_COLUMN = 8
Y_COLUMN = 9
Z_COLUMN = 10

# ------------ Navigation defaults -------------------

Expand Down Expand Up @@ -1004,10 +1005,89 @@
}

MARKER_FILE_MAGICK_STRING = "##INVESALIUS3_MARKER_FILE_"
CURRENT_MARKER_FILE_VERSION = 3
SUPPORTED_MARKER_FILE_VERSIONS = [0, 1, 2, 3]
CURRENT_MARKER_FILE_VERSION = 4
SUPPORTED_MARKER_FILE_VERSIONS = [0, 1, 2, 3, 4]
WILDCARD_MARKER_FILES = _("Marker scanner coord files (*.mkss)|*.mkss")

# Motor mapping visualization

DEFAULT_MEP_CONFIG_PARAMS = {
"mep_enabled": False,
"enabled_once": False,
"threshold_down": 0,
"range_up": 1,
"brain_surface_index": None,
"mep_colormap": "Viridis",
"gaussian_sharpness": 0.4,
"gaussian_radius": 20,
"bounds": None,
"colormap_range_uv": {"min": 50, "low": 200, "mid": 600, "max": 1000},
}


MEP_COLORMAP_DEFINITIONS = {
"BlueCyanYellowRed": { # Blue, Cyan, Yellow, Red
"min": (0.0, 0.0, 1.0),
"low": (0.0, 1.0, 1.0),
"mid": (1.0, 1.0, 0.0),
"max": (1.0, 0.0, 0.0),
},
"GreenYellowOrangeRed": { # Green, Yellow, Orange, Red
"min": (0.0, 1.0, 0.0),
"low": (1.0, 1.0, 0.0),
"mid": (1.0, 0.647, 0.0),
"max": (1.0, 0.0, 0.0),
},
"PurpleBlueGreenYellow": { # Purple, Blue, Green, Yellow
"min": (0.5, 0.0, 0.5),
"low": (0.0, 0.0, 1.0),
"mid": (0.0, 1.0, 0.0),
"max": (1.0, 1.0, 0.0),
},
"BlackGrayWhiteRed": { # Black, Gray, White, Red (grayscale with highlight)
"min": (0.0, 0.0, 0.0),
"low": (0.5, 0.5, 0.5),
"mid": (1.0, 1.0, 1.0),
"max": (1.0, 0.0, 0.0),
},
"Viridis": { # Viridis (perceptually uniform)
"min": (0.267004, 0.004874, 0.329415),
"low": (0.226337, 0.31071, 0.577055),
"mid": (0.993248, 0.906157, 0.143936),
"max": (0.968627, 0.813008, 0.0),
},
"Grayscale": { # Grayscale (often used for CT/MRI)
"min": (0.0, 0.0, 0.0), # Black
"low": (0.25, 0.25, 0.25), # Dark Gray
"mid": (0.75, 0.75, 0.75), # Light Gray
"max": (1.0, 1.0, 1.0), # White
},
"HotMetal": { # Hot Metal (useful for highlighting hot spots)
"min": (0.0, 0.0, 0.0), # Black
"low": (0.5, 0.0, 0.0), # Dark Red
"mid": (1.0, 0.5, 0.0), # Orange
"max": (1.0, 1.0, 1.0), # White
},
"Rainbow": { # Rainbow (although not perceptually uniform, still common)
"min": (0.0, 0.0, 1.0), # Blue
"low": (0.0, 1.0, 0.0), # Green
"mid": (1.0, 1.0, 0.0), # Yellow
"max": (1.0, 0.0, 0.0), # Red
},
"Bone": { # Bone (specifically designed for CT bone visualization)
"min": (0.0, 0.0, 0.0), # Black
"low": (0.388, 0.224, 0.0), # Brown
"mid": (0.902, 0.827, 0.631), # Beige
"max": (1.0, 1.0, 1.0), # White
},
"InvertedGrayscale": { # Inverted Grayscale (sometimes used for PET)
"min": (1.0, 1.0, 1.0), # White
"low": (0.75, 0.75, 0.75), # Light Gray
"mid": (0.25, 0.25, 0.25), # Dark Gray
"max": (0.0, 0.0, 0.0), # Black
},
}

# Keycodes for moving markers using the keyboard
MOVE_MARKER_LEFT_KEYCODE = 65 # A
MOVE_MARKER_RIGHT_KEYCODE = 68 # D
Expand Down
33 changes: 16 additions & 17 deletions invesalius/data/markers/marker.py
Original file line number Diff line number Diff line change
Expand Up @@ -75,6 +75,9 @@ class Marker:
z_offset: float = 0.0
visualization: dict = dataclasses.field(default_factory=dict)
marker_uuid: str = ""
# #TODO: add a reference to original coil marker to relate it to MEP
# in micro Volts (but scale in milli Volts for display)
mep_value: float = dataclasses.field(default=None)

# x, y, z can be jointly accessed as position
@property
Expand Down Expand Up @@ -160,7 +163,11 @@ def to_csv_header(cls):
res = [
field.name
for field in dataclasses.fields(cls)
if (field.name != "version" and field.name != "marker_uuid")
if (
field.name != "version"
and field.name != "marker_uuid"
and field.name != "visualization"
)
]
res.extend(["x_world", "y_world", "z_world", "alpha_world", "beta_world", "gamma_world"])
return "\t".join(map(lambda x: f'"{x}"', res))
Expand Down Expand Up @@ -217,6 +224,7 @@ def to_dict(self):
"cortex_position_orientation": self.cortex_position_orientation,
"z_rotation": self.z_rotation,
"z_offset": self.z_offset,
"mep_value": self.mep_value,
}

def from_dict(self, d):
Expand Down Expand Up @@ -245,33 +253,23 @@ def from_dict(self, d):
else:
marker_type = MarkerType.COIL_TARGET.value

if all(
[
k in d
for k in [
"x_cortex",
"y_cortex",
"z_cortex",
"alpha_cortex",
"beta_cortex",
"gamma_cortex",
]
]
):
cortex_position_orientation = [
cortex_position_orientation = (
d["cortex_position_orientation"]
if "cortex_position_orientation" in d
else [
d["x_cortex"],
d["y_cortex"],
d["z_cortex"],
d["alpha_cortex"],
d["beta_cortex"],
d["gamma_cortex"],
]
else:
cortex_position_orientation = [None, None, None, None, None, None]
)

z_offset = d.get("z_offset", 0.0)
z_rotation = d.get("z_rotation", 0.0)
is_point_of_interest = d.get("is_point_of_interest", False)
mep_value = d.get("mep_value", None)

self.size = d["size"]
self.label = d["label"]
Expand All @@ -287,6 +285,7 @@ def from_dict(self, d):
self.cortex_position_orientation = cortex_position_orientation
self.z_offset = z_offset
self.z_rotation = z_rotation
self.mep_value = mep_value

return self

Expand Down
15 changes: 15 additions & 0 deletions invesalius/data/surface.py
Original file line number Diff line number Diff line change
Expand Up @@ -216,6 +216,7 @@ def __bind_events(self):
Publisher.subscribe(self.OnSplitSurface, "Split surface")
Publisher.subscribe(self.OnLargestSurface, "Create surface from largest region")
Publisher.subscribe(self.OnSeedSurface, "Create surface from seeds")
Publisher.subscribe(self.GetVisibleSurfaceActor, "Get visible surface actor")

Publisher.subscribe(self.OnDuplicate, "Duplicate surfaces")
Publisher.subscribe(self.OnRemove, "Remove surfaces")
Expand Down Expand Up @@ -1129,6 +1130,20 @@ def OnChangeSurfaceName(self, index, name):
def OnShowSurface(self, index, visibility):
self.ShowActor(index, visibility)

def GetVisibleSurfaceActor(self):
"""
Gets the first visible surface actor.
"""
index = 0
for key in self.actors_dict:
if self.actors_dict[key].GetVisibility():
index = key
break

Publisher.sendMessage(
"Load visible surface actor", actor=self.actors_dict[index], index=index
)

def ShowActor(self, index, value):
"""
Show or hide actor, according to given actor index and value.
Expand Down
Loading
Loading