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
[ENH] add plotting functions to the experimental surface module #4235
base: main
Are you sure you want to change the base?
Conversation
use "left" and "right" instead of "left_hemisphere" for surface parts
👋 @jeromedockes Thanks for creating a PR! Until this PR is ready for review, you can include the [WIP] tag in its title, or leave it as a github draft. Please make sure it is compliant with our contributing guidelines. In particular, be sure it checks the boxes listed below.
For new features:
For bug fixes:
We will review it as quick as possible, feel free to ping us with questions if needed. |
Codecov ReportAttention: Patch coverage is
Additional details and impacted files@@ Coverage Diff @@
## main #4235 +/- ##
==========================================
+ Coverage 91.85% 92.03% +0.17%
==========================================
Files 144 146 +2
Lines 16419 16534 +115
Branches 3434 3463 +29
==========================================
+ Hits 15082 15217 +135
+ Misses 792 765 -27
- Partials 545 552 +7
Flags with carried forward coverage won't be shown. Click here to find out more. ☔ View full report in Codecov by Sentry. |
…` attribute This allows enforcing the fact that at least one key is present and keys must be in ["left", "right"], and exposing the `shape` and `n_vertices` attributes
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.
Thx for kicking that off !
Note to ourselves: most likely there will be some merge conflicts with #4229 |
yes 😰 |
@jeromedockes |
@jeromedockes
to maybe minimize conflicts, maybe check #4229 and let me know if some bits should be adapted to make it easier to merge this after.
sure, sorry I haven't had time to follow that as closely as I would have
wanted. Are you and Yasmin going to the office hours today? Maybe we can
use it to do a quick catch-up on the remaining outstanding points
|
I will be there so we can plan the next steps then |
added the examples to adapt in the top message of the PR. I am OK to tackle those in a separate PR if you prefer. This is just to let you know. |
@jeromedockes |
@jeromedockes
want me to take over this one?
Hi Remi sorry for the delay. Please feel free to do so, no problem but
actually I think it might be easier to start a new one as most of the
diff in this one is to add the `.parts` attribute to polymesh and poly
data, which in the end we decided against during the office hours
|
Will try to adapt this one first. It cannot take THAT long to revert. #FamousLastWords |
PR is getting pretty long because of:
Probably worth stopping here for now and make further changes in separate PR. I can also split the examples in different PRs if easier for review. |
@jeromedockes if you have time to cross check 😉 |
surf_mesh: ( | ||
str | list[numpy.array, numpy.array] | Mesh | PolyMesh | None | ||
) = None, |
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.
given that currently surface_mesh can be a str pointing to a file containing a mesh, I think it may be appropriate to also be able to pass a SurfaceImage to surf_mesh
like is done for bg_map
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.
Note sure I follow you here.
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.
Not 100% sure about this, so to be discussed but it seems that it would convenient if users could directly pass a SurfaceImage instance to this parameter and let nilearn get the Polymesh from the instance rather than forcing users to pass just a Mesh / PolyMesh.
I may be overthinking this but if users can just deal with fewer types of objects (SurfaceImage).
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.
it would be weird to pass a different image than the one we are plotting just to grab the mesh from it. either the mesh is None
in which case we use the mesh that comes with surf_map
, or we want to provide a different mesh (eg an inflated surface) in which case we pass surf_mesh
, which should be a mesh. if it happens to be stored as part of another image (which is not very likely if it is an inflated or flat surface used just for plotting) the user can always do img.mesh
to access it
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.
okidoki
not a hill I wanna die on and if users request it we can think about changing that later
but then do you think that it makes sense to be able to pass a SurfaceImage for the background map the way I have done?
img = SurfaceImage( | ||
mesh=fsaverage_meshes["pial"], | ||
data={ | ||
"left": surface.vol_to_surf( | ||
stat_img, fsaverage_meshes["pial"]["left"] | ||
), | ||
"right": surface.vol_to_surf( | ||
stat_img, fsaverage_meshes["pial"]["right"] | ||
), | ||
}, | ||
) |
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.
TODO in another PR: add method to SurfaceImage to handle vol to surf
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.
surfaceimage is for holding surface data, i don't think it should be responsible for converting volumes
surf_mesh = surf_map.mesh | ||
|
||
surf_map._check_hemi(hemi) | ||
_check_hemi_present(surf_mesh, hemi) |
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.
this will fail if surf_mesh is not a PolyMesh
fixing this will probably need better define the PolyMesh class
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.
Can you clarify the discussion here ?
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.
_check_hemi_present
is just to make sure that the "PolyMesh" dict has the correct hemisphere
But most of our new code at the moment, does not use the PolyMesh class but simple dictionaries instead, so we cannot do a isinstance(surf_mesh, PolyMesh)
. So we should probably develop the PolyMesh class, because at the moment it is only PolyMesh = Dict[str, Mesh]
.
Also at this stage surf_mesh
can also be a str, list of two numpy.ndarray, Mesh and none of those cases are handled.
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.
Not I could start making those changes in this PR but I was getting afraid of having an even bigger PR
return images | ||
|
||
|
||
def fetch_destrieux( |
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.
testing this will require improving the mocking of the dataset: would probably do this in another PR
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.
Needs more discussion.
} | ||
for mesh_type, mesh_name in renaming.items() | ||
} | ||
renaming = {"curv": "curvature", "sulc": "sulcal", "thick": "thickness"} |
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.
Why are you doing this here ?
surf_mesh: ( | ||
str | list[numpy.array, numpy.array] | Mesh | PolyMesh | None | ||
) = None, |
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.
Note sure I follow you here.
surf_mesh = surf_map.mesh | ||
|
||
surf_map._check_hemi(hemi) | ||
_check_hemi_present(surf_mesh, hemi) |
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.
Can you clarify the discussion here ?
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.
ok I'll try to have a look this week. My first comment is I don't see the need for the change from
module.py
to
module/
__init__.py (empty besides grabbing names from _module.py)
_module.py
(no other submodules)
surf_mesh: ( | ||
str | list[numpy.array, numpy.array] | Mesh | PolyMesh | None | ||
) = None, |
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.
it would be weird to pass a different image than the one we are plotting just to grab the mesh from it. either the mesh is None
in which case we use the mesh that comes with surf_map
, or we want to provide a different mesh (eg an inflated surface) in which case we pass surf_mesh
, which should be a mesh. if it happens to be stored as part of another image (which is not very likely if it is an inflated or flat surface used just for plotting) the user can always do img.mesh
to access it
For this I was mostly thinking of making a bit easier to integrate all this code into the rest of the code base later. |
but then do you think that it makes sense to be able to pass a SurfaceImage for the background map the way I have done?
yes because the background map is an actual image that has both a mesh
and data, right?
|
yeah that was my reasoning, but your argument of saying:
could also sort of apply: so that's why I was feeling a bit awkward to say you can do it for background data but not meshes... |
@@ -2200,13 +2200,12 @@ def fetch_language_localizer_demo_dataset( | |||
- 'description' : :obj:`str`, dataset description | |||
|
|||
Legacy output | |||
------------- |
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.
removes a warning from the doc build
@jeromedockes Let me know if you think that I should start creating "proper" classes for PolyMesh and PolyData in this PR or not |
added a placeholder basic plotting function
TODO
change PolyMesh, PolyData to constrain keys to be "left" and "right"(required for plotting when we want medial or lateral view)maskers
,plotting
,datasets
to make them easier to later integrate into the rest of the code basebg_map
of the new plotting functions can be aSurfaceImage
instanceSurfaceImage
to check the presence of a given hemisphere in data and meshExamples to adapt: