Skip to content

Commit 89871c2

Browse files
committed
Changes
1 parent a0de38c commit 89871c2

File tree

12 files changed

+700
-565
lines changed

12 files changed

+700
-565
lines changed

CorpusCallosum/cc_visualization.py

Lines changed: 9 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -135,16 +135,18 @@ def load_contours_from_template_dir(
135135
num_thickness_values = np.sum(~np.isnan(np.array(thickness_values[1:],dtype=float)))
136136
if fsaverage_contour is None:
137137
fsaverage_contour = load_fsaverage_cc_template()
138-
# create measurement points (points = 2 x levelpaths) accorindg to number of thickness values
138+
# create measurement points (points = 2 x levelpaths) according to number of thickness values
139139
fsaverage_contour.create_levelpaths(num_points=num_thickness_values // 2, update_data=True)
140140
current_contour = fsaverage_contour.copy()
141141
current_contour.load_thickness_values(thickness_file)
142142

143143
else:
144144
# this is kinda ugly - maybe we need to overload the constructor to load the contour and thickness values?
145-
current_contour = CCContour(np.empty((0, 2)), np.empty((0,)), resolution=resolution)
146-
current_contour.load_contour(contour_file)
147-
current_contour.load_thickness_values(thickness_file)
145+
# FIXME: The z_position in from_contour is still incorrect, currently all Contours would be "registered" for
146+
# the midslice.
147+
current_contour = CCContour.from_contour_file(contour_file, thickness_file, z_position=0.0)
148+
# current_contour.load_contour(contour_file)
149+
# current_contour.load_thickness_values(thickness_file)
148150

149151
current_contour.fill_thickness_values()
150152
contours.append(current_contour)
@@ -194,6 +196,7 @@ def main(
194196
return 0
195197

196198
# 3D visualization
199+
# FIXME: This function would need contours[i].z_position to be properly initialized!
197200
cc_mesh = CCMesh.from_contours(contours, smooth=0)
198201

199202
plot_kwargs = dict(
@@ -205,7 +208,8 @@ def main(
205208
cc_mesh.plot_mesh(**plot_kwargs)
206209
cc_mesh.plot_mesh(output_path=str(output_dir / "cc_mesh.html"), **plot_kwargs)
207210

208-
cc_mesh = cc_mesh.to_fs_coordinates(lr_offset=FSAVERAGE_MIDDLE / resolution)
211+
#FIXME: needs to be adapted to new interface of CCMesh.to_fs_coordinates / to_vox_coordinates
212+
cc_mesh = cc_mesh.to_vox_coordinates(lr_offset=FSAVERAGE_MIDDLE / resolution)
209213
logger.info(f"Writing vtk file to {output_dir / 'cc_mesh.vtk'}")
210214
cc_mesh.write_vtk(str(output_dir / "cc_mesh.vtk"))
211215
logger.info(f"Writing freesurfer surface file to {output_dir / 'cc_mesh.fssurf'}")

CorpusCallosum/data/fsaverage_cc_template.py

Lines changed: 13 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,7 @@
1414

1515
import os
1616
from pathlib import Path
17+
from typing import cast
1718

1819
import nibabel as nib
1920
import numpy as np
@@ -22,8 +23,12 @@
2223
from CorpusCallosum.data import constants
2324
from CorpusCallosum.shape.contour import CCContour
2425
from CorpusCallosum.shape.postprocessing import recon_cc_surf_measure
26+
from FastSurferCNN.utils import nibabelImage
2527
from FastSurferCNN.utils.brainvolstats import mask_in_array
2628

29+
FSAVERAGE_PC_COORDINATE = np.array([131, 99])
30+
FSAVERAGE_AC_COORDINATE = np.array([135, 130])
31+
2732

2833
def smooth_contour(contour: tuple[np.ndarray, np.ndarray], window_size: int = 5) -> tuple[np.ndarray, np.ndarray]:
2934
"""Smooth a contour using a moving average filter.
@@ -62,18 +67,16 @@ def smooth_contour(contour: tuple[np.ndarray, np.ndarray], window_size: int = 5)
6267
return (x_smoothed, y_smoothed)
6368

6469

65-
def load_fsaverage_cc_template() -> tuple[
66-
np.ndarray, tuple[np.ndarray, np.ndarray], np.ndarray, np.ndarray, np.ndarray, tuple[int, int]
67-
]:
70+
def load_fsaverage_cc_template() -> CCContour:
6871
"""Load and process the fsaverage corpus callosum template.
6972
7073
This function loads the fsaverage segmentation from FreeSurfer's data directory,
7174
extracts the corpus callosum mask, and processes it to create a smooth template.
7275
7376
Returns
7477
-------
75-
tuple
76-
Contains:
78+
CCContour
79+
Object with all the contour information including:
7780
- contour : tuple[np.ndarray, np.ndarray] : x and y coordinates of the contour points.
7881
- anterior_endpoint_idx : np.ndarray : Index of the anterior endpoint.
7982
- posterior_endpoint_idx : np.ndarray : Index of the posterior endpoint.
@@ -95,13 +98,9 @@ def load_fsaverage_cc_template() -> tuple[
9598
f"FREESURFER_HOME environment variable") from err
9699

97100
fsaverage_seg_path = freesurfer_home / 'subjects' / 'fsaverage' / 'mri' / 'aparc+aseg.mgz'
98-
fsaverage_seg = nib.load(fsaverage_seg_path)
101+
fsaverage_seg = cast(nibabelImage, nib.load(fsaverage_seg_path))
99102
segmentation = np.asarray(fsaverage_seg.dataobj)
100103

101-
PC = np.array([131, 99])
102-
AC = np.array([135, 130])
103-
104-
105104
midslice = segmentation.shape[0]//2 +1
106105

107106
cc_mask = mask_in_array(segmentation[midslice], constants.SUBSEGMENT_LABELS)
@@ -124,9 +123,9 @@ def load_fsaverage_cc_template() -> tuple[
124123
_, contour_with_thickness, (anterior_endpoint_idx, posterior_endpoint_idx) = recon_cc_surf_measure(
125124
segmentation=cc_mask[None],
126125
slice_idx=0,
127-
ac_coords=AC,
128-
pc_coords=PC,
129-
affine=fsaverage_seg.affine,
126+
ac_coords_vox=FSAVERAGE_AC_COORDINATE,
127+
pc_coords_vox=FSAVERAGE_PC_COORDINATE,
128+
slice_lia_vox2midslice_ras=fsaverage_seg.affine,
130129
num_thickness_points=100,
131130
subdivisions=[1/6, 1/2, 2/3, 3/4],
132131
subdivision_method="shape",
@@ -148,7 +147,7 @@ def load_fsaverage_cc_template() -> tuple[
148147
fsaverage_contour = CCContour(np.array(outside_contour).T,
149148
np.zeros(len(outside_contour[0])),
150149
endpoint_idxs=(anterior_endpoint_idx, posterior_endpoint_idx),
151-
resolution=1.0)
150+
z_position=0.0)
152151

153152

154153
return fsaverage_contour

CorpusCallosum/data/read_write.py

Lines changed: 11 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -20,17 +20,18 @@
2020
from numpy import typing as npt
2121

2222
import FastSurferCNN.utils.logging as logging
23-
from FastSurferCNN.utils import AffineMatrix4x4, nibabelImage
23+
from FastSurferCNN.utils import AffineMatrix4x4, RotationMatrix3x3, Vector3d, nibabelImage
2424
from FastSurferCNN.utils.parallel import thread_executor
2525

26+
logger = logging.get_logger(__name__)
2627

27-
class FSAverageHeader(TypedDict):
28-
dims: npt.NDArray[int]
29-
delta: npt.NDArray[float]
30-
Mdc: npt.NDArray[float]
31-
Pxyz_c: npt.NDArray[float]
3228

33-
logger = logging.get_logger(__name__)
29+
class MGHHeaderDict(TypedDict):
30+
"""A dictionary with the four required fields of a MGH Header"""
31+
dims: Vector3d
32+
delta: Vector3d
33+
Mdc: RotationMatrix3x3
34+
Pxyz_c: Vector3d
3435

3536

3637
def calc_ras_centroids_from_seg(seg_img: nibabelImage, label_ids: list[int] | None = None) \
@@ -154,7 +155,7 @@ def load_fsaverage_affine(affine_path: str | Path) -> npt.NDArray[float]:
154155
return affine_matrix
155156

156157

157-
def load_fsaverage_data(data_path: str | Path) -> tuple[AffineMatrix4x4, FSAverageHeader]:
158+
def load_fsaverage_data(data_path: str | Path) -> tuple[AffineMatrix4x4, MGHHeaderDict]:
158159
"""Load fsaverage affine matrix and header fields from static JSON file.
159160
160161
Parameters
@@ -206,13 +207,13 @@ def load_fsaverage_data(data_path: str | Path) -> tuple[AffineMatrix4x4, FSAvera
206207

207208
# Convert lists back to numpy arrays
208209
affine_matrix = np.array(data["affine"])
209-
header_data = FSAverageHeader(
210+
header_data = MGHHeaderDict(
210211
dims=data["header"]["dims"],
211212
delta=data["header"]["delta"],
212213
Mdc=np.array(data["header"]["Mdc"]),
213214
Pxyz_c=np.array(data["header"]["Pxyz_c"]),
214215
)
215-
216+
216217
# Validate affine matrix shape
217218
if affine_matrix.shape != (4, 4):
218219
raise ValueError(f"Expected 4x4 affine matrix, got shape {affine_matrix.shape}")

0 commit comments

Comments
 (0)