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

HCP HippUnfold and Post HippUnfold Implementations #320

Open
wants to merge 9 commits into
base: master
Choose a base branch
from

Conversation

glasserm
Copy link
Contributor

@glasserm glasserm commented Jan 8, 2025

This is an implementation of the current version of HippUnfold 1.5.2-pre.2: https://hippunfold.readthedocs.io/en/latest/. This requires the current development version of Connectome Workbench to use. The file organization is non-final and needs evaluation and comment. There are a variety of unfinished things marked with TODO. Some of these are pending comments and some pending a new planned HippUnfold 2.0 release.

For those who have local access, the data are in here: myelin/brainmappers/Connectome_Project/YA_HCP_Final/100307/T1w/HippUnfold and MNINonLinear/HippUnfold

…eliminary and is CERTAIN TO CHANGE including file organization given additional Connectome Workbench features needed, HippUnfold bugs discovered and further development and optimization needed.
*Add Support for multi-resolution meshes
*Add Spec Files
*Add Support for Dentate in cifti-matlab
*Support reading CIFTI files with dentate in cifti-matlab
*Use consistent hippocampal labels in CIFTI and volume
*Code cleanup
*Remove matlab hack and just recreate all CIFTI files
*Add some combined hippocampal and cerebral spec files
Copy link
Contributor

@demsarjure demsarjure left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I reviewed this and added some minor comments/discussion points.

@@ -26,6 +26,8 @@ export MATLAB_COMPILER_RUNTIME=/export/matlab/MCR/R2017b/v93
# If a suitable version of wb_command is on your $PATH, CARET7DIR can be blank
export CARET7DIR=
export HCPCIFTIRWDIR="$HCPPIPEDIR"/global/matlab/cifti-matlab
export HIPPUNFOLDPATH="${HOME}/pipeline_tools/HippUnfold/khanlab_hippunfold_latest.sif"
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This seems like a highly specific local path. Do you have any conventions about where users should put external tools that are required to run certain HCP pipelines? For example, $HCPPIPEDIR/external.

Copy link
Member

@coalsont coalsont Jan 8, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Users should not add anything inside of the repo folder. That way madness lies.

More to the point, the pipelines setup instructions don't tell users where to install FSL, or freesurfer, or MSM, etc, and I don't think we should start doing so. The standard "set this variable to where you have downloaded hippunfold" formula should apply.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is the standard convention we have used for all of the HCP Pipelines so it is odd to be complaining about it here. It is intended to be changed but is not supposed to be blank (unlike the CARET7DIR).

log_Msg "T2w HippUnfold completed."
log_Msg "Running T1wT2w HippUnfold for subject: $Subject"
apptainer run --bind $StudyFolder -e $HIPPUNFOLDPATH $HippUnfoldT1wT2wFolder $HippUnfoldT1wT2wFolderOut participant --modality T1w --cores all --force-output --generate_myelin_map --output-density 0p5mm 1mm 2mm --force-nnunet-model T1T2w
log_Msg "T1wT2w HippUnfold completed."
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I knew this day would come sooner or later. This will force us to run containers inside containers (HippUnfold inside QuNex), on paper it should work. I guess we will see in practice if there are any issues with this.

What we need to figure out is whether we should put the HippUnfold container inside the QuNex container (this adds a couple GBs to the QuNex size) or if we require users to download it manually. I am leaning towards the first option as it is more user friendly and nowadays size is not that important as we all have good connectivity and enough disk space. A case for having the container outside could be made by saying this is a pipeline that will not be executed by most of our users?

Need to think this through a bit.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Do they provide their code such that it can be run without their container wrapping it? I think of containers as a convenience of not having to manually configure a bunch of software, not as a replacement for being able to install someone's software.

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

HippUnfold v2.0.0 will be available on conda (ETA ~1 month)
The current HippUnfold v1.5.2 can be installed with git clone; pip install -e ., however it also uses containers internally for dependencies like nighres, wb_command, etc. See here (and note that poetry is not necessary, its just for python linting)

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks for the info. I felt like adding your whole container into our container would be suboptimal space wise as there should be some overlap between the tools we depend on. We indeed both use wb_command, so that one would be redundant for sure, there could be others. I will first try to do this optimally and install all the dependencies manually. If this does not work and is too much of a hassle, we can go the container route.

@glasserm What is your preferred timeline here? Would you like to wait for HippUnfold v2.0.0 and then start processing with this one. Sounds like a major upgrade, that is only a month or so away. Or is this urgent and waiting an extra month is not an option?

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

RE: #Couldn't get non-BIDS to work (wouldn't do anything) #Seriously: don't put a $ here...

The Subject wildcard should simply be subject. This can be used to avoid having to make a BIDS directory as input. Note that hippunfold expects to iterate over all possible completions of the wildcard subject (and can also iterate over other wildcards like session or acq, as per pyBIDS). Best parallelization can be achieved by running batches of subjects, but I udnerstand you may not want to set up your pipelines that way.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks for the info. I felt like adding your whole container into our container would be suboptimal space wise as there should be some overlap between the tools we depend on. We indeed both use wb_command, so that one would be redundant for sure, there could be others. I will first try to do this optimally and install all the dependencies manually. If this does not work and is too much of a hassle, we can go the container route.

@glasserm What is your preferred timeline here? Would you like to wait for HippUnfold v2.0.0 and then start processing with this one. Sounds like a major upgrade, that is only a month or so away. Or is this urgent and waiting an extra month is not an option?

This isn't intended to be merged until HippUnfold v2.0.0 is available and the PR reflects and changes needed for that. I agree that ideally you would install HippUnfold + dependencies in QuNex rather than the container.

Copy link
Contributor Author

@glasserm glasserm Jan 11, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

RE: #Couldn't get non-BIDS to work (wouldn't do anything) #Seriously: don't put a $ here...

The Subject wildcard should simply be subject. This can be used to avoid having to make a BIDS directory as input. Note that hippunfold expects to iterate over all possible completions of the wildcard subject (and can also iterate over other wildcards like session or acq, as per pyBIDS). Best parallelization can be achieved by running batches of subjects, but I understand you may not want to set up your pipelines that way.

I currently wrap in some BIDS because I couldn't get the non-BIDS options to work reliably. If you guys provide an ability to simply specify paths to relevant inputs, I will remove the wrapped BIDS.

opts_SetScriptDescription "Make some BIDS structures and run HippUnfold"

opts_AddMandatory '--study-folder' 'StudyFolder' 'path' "folder containing all subjects"
opts_AddMandatory '--subject' 'Subject' 'subject ID' ""
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This was changed to Session during the refactoring?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think some of those changes might have been overzealous. In any case, this is a cross-sectional pipeline at the moment and I have not thought at all about making it longitudinal. That might be something that HippUnfold would need to internally do. We rely on FreeSurfer to setup the longitudinal processing for cerebral cortex.

opts_SetScriptDescription "Make some BIDS structures and run HippUnfold"

opts_AddMandatory '--study-folder' 'StudyFolder' 'path' "folder containing all subjects"
opts_AddMandatory '--subject' 'Subject' 'subject ID' ""
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This was changed to Session during the refactoring?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Same as the other comment.

${CARET7DIR}/wb_command -add-to-spec-file ${AtlasHippUnfoldFolder}/${Subject}.${Mesh}.wb_spec INVALID ${AtlasFolder}/T1w_restore.nii.gz
${CARET7DIR}/wb_command -add-to-spec-file ${AtlasHippUnfoldFolder}/${Subject}.${Mesh}.wb_spec INVALID ${AtlasFolder}/T2w_restore.nii.gz

#TODO: Merge Native Meshes, anything with 0pt5mm meshes?
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

TODO or not TODO?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Dependent on HippUnfold 2.0

#$PhysicalHippUnfoldFolderOut/hippunfold/sub-${Subject}/surf/sub-${Subject}_space-${Modality}_den-${Mesh}_label-hipp_atlas-multihist7_subfields.dlabel.nii

#VolumeLabels
#$PhysicalHippUnfoldFolderOut/hippunfold/sub-${Subject}/anat/sub-${Subject}_hemi-${Hemisphere}_space-${Modality}_desc-subfields_atlas-multihist7_dseg.nii.gz
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Cleanup these notes at the end?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

They will likely stay as code documentation to help me remember what HippUnfold does.

Comment on lines +75 to +80
cp "$T1wImage" "$HippUnfoldT1wFolder/sub-${Subject}_T1w.nii.gz"
cp "$T2wImage" "$HippUnfoldT1wFolder/sub-${Subject}_T2w.nii.gz"
cp "$T1wImage" "$HippUnfoldT2wFolder/sub-${Subject}_T1w.nii.gz"
cp "$T2wImage" "$HippUnfoldT2wFolder/sub-${Subject}_T2w.nii.gz"
cp "$T1wImage" "$HippUnfoldT1wT2wFolder/sub-${Subject}_T1w.nii.gz"
cp "$T2wImage" "$HippUnfoldT1wT2wFolder/sub-${Subject}_T2w.nii.gz"
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Does it really need to create T1w/T2w.nii.gz and T2w/T1w.nii.gz? Do the 3 calls even need 3 separate input folders (with currently the same initial contents)? Why are there 3 hippunfold calls anyway?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is all necessary for the BIDS wrapping, if we can get working direct input paths, I would not do any of this. There are 3 HippUnfold calls because there are 3 ways to call HippUnfold currently. I am hopeful we could standardize on using both the T1w and T2w in HippUnfold 2.0, but we need to hear from the HippUnfold folks that it is better or at least not worse than using individual inputs.

Comment on lines +224 to +226
if [ ${Flag} = "On" ] ; then
Modality="T1wT2w"
fi
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Would be simpler and less error-prone to declare the variables local at the start of the function (if you feel the need to reuse the variable names)...

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think I tried that first and it didn't work. You are welcome to make a commit that you think will work and we can test it.

Comment on lines +44 to +45
function PostHippUnfold {
HippUnfoldFolderOut=${1}
Copy link
Member

@coalsont coalsont Jan 10, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I would indent the entire function body to make it obvious where it ends.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Also, there is only one line in the code (in a loop) that calls this function. Functions are mainly for when you want to call it from more than one line in the code (such as when you have to change the arguments in a case-specific way).

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

That is in fact why it is a function, there are a bunch of variables that change depending upon which HippUnfold output is being used. If we standardized on only one kind of HippUnfold inputs (e.g., both T1w and T2w) then I wouldn't need the function.

As it stands it is most of the script and I didn't want to waste the space. The function may not be needed in the end if we standardize on only one kind of HippUnfold inputs.

Comment on lines +61 to +62
function PALETTE {
File=${1}
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Probably better to define this function outside the body of the other function. Nesting like this doesn't remove the function once the outer function ends (so it doesn't have the usual benefits of nested functions), but it does redundantly redefine it each call. It also increases the distance between the start and end of the outer function.

Also, the body isn't indented relative to the context. I don't want matlab-style indentation to leak outside of matlab (I don't even like it in matlab...).

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I don't think I was clear on what was inside or outside a function's scope, so I put things inside just to be sure. Let's see what happens with the HippUnfold inputs...

Comment on lines +54 to +59
if [ ${Modality} = "T1wT2w" ] ; then
Modality="T1w"
Flag="On"
else
Flag="Off"
fi
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

You could also consider this pattern:

Suggested change
if [ ${Modality} = "T1wT2w" ] ; then
Modality="T1w"
Flag="On"
else
Flag="Off"
fi
EffectiveModality="$Modality"
if [ ${Modality} = "T1wT2w" ] ; then
EffectiveModality="T1w"
fi

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think this issue wouldn't have come up if the local variables worked, but they didn't as I recall.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

4 participants