-
Notifications
You must be signed in to change notification settings - Fork 145
Upgrade WhipperSnapPy from 1.3 to 2.0, simplify and streamline integration #795
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
base: dev
Are you sure you want to change the base?
Changes from all commits
eb4c739
34f3f11
84e3bdf
027b0c3
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change | ||||
|---|---|---|---|---|---|---|
|
|
@@ -3,12 +3,15 @@ | |||||
| from pathlib import Path | ||||||
| from typing import Literal | ||||||
|
|
||||||
| import nibabel as nib | ||||||
| import numpy as np | ||||||
| from nibabel.affines import apply_affine | ||||||
|
|
||||||
| from CorpusCallosum.data.fsaverage_cc_template import load_fsaverage_cc_template | ||||||
| from CorpusCallosum.shape.contour import CCContour | ||||||
| from CorpusCallosum.shape.mesh import CCMesh | ||||||
| from FastSurferCNN.utils.logging import get_logger, setup_logging | ||||||
| from FastSurferCNN.utils.lta import read_lta | ||||||
|
|
||||||
| logger = get_logger(__name__) | ||||||
|
|
||||||
|
|
@@ -39,7 +42,7 @@ def make_parser() -> argparse.ArgumentParser: | |||||
| "cc_mesh.vtk - VTK mesh file format " | ||||||
| "cc_mesh.fssurf - FreeSurfer surface file " | ||||||
| "cc_mesh_overlay.curv - FreeSurfer curvature overlay file " | ||||||
| "cc_mesh_snap.png - Screenshot/snapshot of the 3D mesh (requires whippersnappy>=1.3.1)", | ||||||
| "cc_mesh_snap.png - Screenshot/snapshot of the 3D mesh (requires whippersnappy>=2.0)", | ||||||
| metavar="OUTPUT_DIR" | ||||||
| ) | ||||||
| parser.add_argument( | ||||||
|
|
@@ -213,6 +216,17 @@ def main( | |||||
| # 3D visualization | ||||||
| cc_mesh = CCMesh.from_contours(contours, smooth=0) | ||||||
|
|
||||||
| if Path(output_dir / "mri" / "upright.mgz").exists(): | ||||||
| header = nib.load(output_dir / "mri" / "upright.mgz").header | ||||||
| # we need to get the upright image header, which is the same as cc_up.lta applied to orig. | ||||||
| elif Path(template_dir / "mri/orig.mgz").exists() and Path(template_dir / "mri/transforms/cc_up.lta").exists(): | ||||||
| image = nib.load(template_dir / "mri" / "orig.mgz") | ||||||
| lta_mat = read_lta(template_dir / "mri/transforms/cc_up.lta")["lta"] | ||||||
| image.affine = apply_affine(lta_mat, image.affine) | ||||||
| header = image.header | ||||||
| else: | ||||||
| header = None | ||||||
|
|
||||||
| plot_kwargs = dict( | ||||||
| colormap=colormap, | ||||||
| color_range=color_range, | ||||||
|
|
@@ -225,15 +239,15 @@ def main( | |||||
| logger.info(f"Writing vtk file to {output_dir / 'cc_mesh.vtk'}") | ||||||
| cc_mesh.write_vtk(str(output_dir / "cc_mesh.vtk")) | ||||||
| logger.info(f"Writing freesurfer surface file to {output_dir / 'cc_mesh.fssurf'}") | ||||||
| cc_mesh.write_fssurf(str(output_dir / "cc_mesh.fssurf")) | ||||||
| cc_mesh.write_fssurf(str(output_dir / "cc_mesh.fssurf"), image=header) | ||||||
| logger.info(f"Writing freesurfer overlay file to {output_dir / 'cc_mesh_overlay.curv'}") | ||||||
| cc_mesh.write_morph_data(str(output_dir / "cc_mesh_overlay.curv")) | ||||||
| try: | ||||||
| cc_mesh.snap_cc_picture(str(output_dir / "cc_mesh_snap.png")) | ||||||
| cc_mesh.snap_cc_picture(str(output_dir / "cc_mesh_snap.png"), ref_header=header) | ||||||
| logger.info(f"Writing 3D snapshot image to {output_dir / 'cc_mesh_snap.png'}") | ||||||
| except RuntimeError: | ||||||
|
||||||
| except RuntimeError: | |
| except ImportError: |
| Original file line number | Diff line number | Diff line change | ||||
|---|---|---|---|---|---|---|
|
|
@@ -12,7 +12,6 @@ | |||||
| # See the License for the specific language governing permissions and | ||||||
| # limitations under the License. | ||||||
|
|
||||||
| import tempfile | ||||||
| from pathlib import Path | ||||||
| from typing import TypeVar | ||||||
|
|
||||||
|
|
@@ -27,8 +26,8 @@ | |||||
| import FastSurferCNN.utils.logging as logging | ||||||
| from CorpusCallosum.shape.contour import CCContour | ||||||
| from CorpusCallosum.shape.thickness import make_mesh_from_contour | ||||||
| from FastSurferCNN.utils import AffineMatrix4x4, nibabelImage | ||||||
| from FastSurferCNN.utils.common import suppress_stdout, update_docstring | ||||||
| from FastSurferCNN.utils import AffineMatrix4x4, nibabelHeader, nibabelImage | ||||||
| from FastSurferCNN.utils.common import update_docstring | ||||||
|
|
||||||
| try: | ||||||
| from pyrr import Matrix44 | ||||||
|
|
@@ -478,11 +477,12 @@ def __create_cc_viewmat() -> "Matrix44": | |||||
| - -8 degrees around z-axis | ||||||
| 3. Adds a small translation for better centering | ||||||
| """ | ||||||
| from whippersnappy.gl.views import ViewType, get_view_matrix | ||||||
|
||||||
|
|
||||||
| if not HAS_PYRR: | ||||||
| raise ImportError("Pyrr not installed, install pyrr with `pip install pyrr`.") | ||||||
|
|
||||||
| viewLeft = np.array([[0, 0, -1, 0], [-1, 0, 0, 0], [0, 1, 0, 0], [0, 0, 0, 1]]) # left w top up // right | ||||||
| viewLeft = get_view_matrix(ViewType.LEFT) # left w top up // right | ||||||
| transl = Matrix44.from_translation((0, 0, 0.4)) | ||||||
| viewmat = transl * viewLeft | ||||||
|
|
||||||
|
|
@@ -503,108 +503,70 @@ def __create_cc_viewmat() -> "Matrix44": | |||||
| def snap_cc_picture( | ||||||
| self, | ||||||
| output_path: Path | str, | ||||||
| fssurf_file: Path | str | None = None, | ||||||
| overlay_file: Path | str | None = None, | ||||||
| ref_image: Path | str | nibabelImage | None = None, | ||||||
| ref_header: Path | str | nibabelHeader | None = None, | ||||||
| ) -> None: | ||||||
| """Snap a picture of the corpus callosum mesh. | ||||||
|
|
||||||
| Parameters | ||||||
| ---------- | ||||||
| output_path : Path, str | ||||||
| Path where to save the snapshot image. | ||||||
| fssurf_file : Path, str, optional | ||||||
| Path to a FreeSurfer surface file to use for the snapshot. | ||||||
| If None, the mesh is saved to a temporary file. | ||||||
| overlay_file : Path, str, optional | ||||||
| Path to a FreeSurfer overlay file to use for the snapshot. | ||||||
| If None, the mesh is saved to a temporary file. | ||||||
| ref_image : Path, str, nibabelImage, optional | ||||||
| ref_header : Path, str, nibabelImage, optional | ||||||
|
||||||
| ref_header : Path, str, nibabelImage, optional | |
| ref_header : Path, str, nibabelHeader, optional |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Well, actually this is the old type in the docstring. Most of the types of this string are currently not supported. We should just drop the types from the doc that are no longer supported.
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -19,5 +19,5 @@ yacs>=0.1.8 | |
| monai>=1.4.0 | ||
| meshpy>=2025.1.1 | ||
| pyrr>=0.10.3 | ||
| whippersnappy>=1.3.1 | ||
| whippersnappy>=2.0 | ||
| pip>=25.0 | ||
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The function
apply_affinefrom nibabel.affines is used to transform points/vertices, not affine matrices. This line incorrectly appliesapply_affine(lta_mat, image.affine)when it should be composing two affine matrices. The correct approach would be to matrix multiply:image.affine = lta_mat @ image.affineor usenp.dot(lta_mat, image.affine). The current code will likely result in an error or incorrect transformation.There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Should be addressed by matrix multiplication.