-
Notifications
You must be signed in to change notification settings - Fork 53
[FIX] Improve bounding box computation from masks #304
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
Conversation
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 result looks good, but honestly I have no idea how you made it happen. Could you add some comments explaining the process a little bit?
int((mask_data.shape[1] * mask_data.shape[2]) * 0.2), # sagittal | ||
int((mask_data.shape[0] * mask_data.shape[2]) * 0.0), # coronal | ||
int((mask_data.shape[0] * mask_data.shape[1]) * 0.3), # axial | ||
] |
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 is opaque to me. ijk_th
reads like threshold, but I don't see how this should produce a threshold.
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.
Added some comments 👍
This patch still does not address the real underlying problem (it would involve resampling the mask into an affine that is aligned with the canonical axes) but it does the job. |
@effigies, please let me know if I managed to clarify the idea with the comments. If so, feel free to merge. |
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.
So my primary concern with this is what happens with small or degenerate masks? Do we have tests that show what this looks like for various masks?
ras_coords.append(apply_affine(mask_nii.affine, cross).tolist()) | ||
|
||
ras_coords.append(apply_affine( | ||
mask_nii.affine, cross).tolist()) |
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.
Is it possible that the problem is actually just that we don't make sure these are integers, being sure to round up at the bottom and down at the top?
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.
nilearn takes physical coordinates, so I doubt this is the problem.
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.
Right, but if we round the physical coordinates to outside the mask, then we might get undefined behavior. We certainly get something.
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.
A warning saying that no contours were found was thrown. Nilearn used to not plot anything in that case, but after some release the weird contours would show up. The thresholds solution should take care of most of the cases where this rounding happened, as we don't compute the bounding box of the mask, but the bounding box of the mask with a decent amount of masked pixels instead. The tests were failing with very small masks, but 55aa451 should fix that falling back to the old calculation (with your suggestion in #304 (comment)).
That said, the issue I'm afraid, is that nilearn rotates the images (and the masks) to align with canonical axes in visualization. As the brain is not perfectly spherical, these rotations might force the coordinates outside the brain if you calculate them exactly at the brain surface (as we were doing) for very tilted brains.
I think that this implementation will basically satisfy 99.9% of the cases and the remaining 0.1% will have the weird contours - which are confusing but not harmful.
mask_data = mask_nii.get_data() | ||
B = np.argwhere(mask_data > 0) | ||
start_coords = B.min(0) | ||
stop_coords = B.max(0) + 1 |
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.
Or maybe this should have just been stop_coords = B.max(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.
This indeed seemingly fixed the issue (that was the first thing I tried). However, being at it, I wanted to improve the visualization because such a liberal calculation usually gave almost empty views of the axial plane in many images.
Co-Authored-By: oesteban <[email protected]>
Reports are looking good - thumbs up? |
While nilearn/nilearn#25 is considered, this PR advances the generation of mosaics after aligning the affine matrix of the data with the cardinal axes. This should overcome the glitches when nilearn is requested to plot contours and the mask for the ROI has voxels that only partially intersect with the visualization plane. Resolves: nipreps#542. References: nipreps#281, nipreps#304. Depends: nipreps#543.
While nilearn/nilearn#25 is considered, this PR advances the generation of mosaics after aligning the affine matrix of the data with the cardinal axes. This should overcome the glitches when nilearn is requested to plot contours and the mask for the ROI has voxels that only partially intersect with the visualization plane. Resolves: nipreps#542. References: nipreps#281, nipreps#304. Depends: nipreps#543.
While nilearn/nilearn#25 is considered, this PR advances the generation of mosaics after aligning the affine matrix of the data with the cardinal axes. This should overcome the glitches when nilearn is requested to plot contours and the mask for the ROI has voxels that only partially intersect with the visualization plane. Resolves: nipreps#542. References: nipreps#281, nipreps#304. Depends: nipreps#543.
While nilearn/nilearn#25 is considered, this PR advances the generation of mosaics after aligning the affine matrix of the data with the cardinal axes. This should overcome the glitches when nilearn is requested to plot contours and the mask for the ROI has voxels that only partially intersect with the visualization plane. Resolves: nipreps#542. References: nipreps#281, nipreps#304. Depends: nipreps#543.
Before (fieldmap of ds054 on fmriprep's reports at Circle):


After: