thickinthehead results incorrect for voxel dimensions other than 1mm^3
binarybottle opened this issue · 1 comments
Colleagues at the Nathan Kline Institute ran Mindboggle on T1s from three different scanners: CBIC, SI, and RU. They found that for every labeled region in every subject, RU's thickinthehead values were approximately twice as high as their CBIC and SI counterparts.
The thickinthehead measure is computed as follows for each labeled cortical region:
volume = (number of voxels) x (product of voxel dimensions)
area = (outer edge volume + inner edge volume) / 2
thickinthehead = volume / area
I ran Mindboggle on a T1 from each scanner and confirmed the higher thickinthehead values for the RU scans. As you can see in the figure below, the segmentations, labels, and label volumes all appear to be perfectly fine, but RU's inner and outer cortex volumes are indeed smaller, which would lead to higher thickinthehead values.
The difference seems to be that the RU scans were the only scans whose resolution deviated (0.8) from 1 mm^3 voxel size. When I convert the combined segmentation and labeled files from 0.8 mm^3 voxel size (320 x 320 x 224 volume dimensions) to 1 mm^3 (257 x 256 x 180), then the resulting thickinthehead values become reasonable:
mri_convert -i combined_segmentations.nii.gz -o seg.nii.gz -vs 1 1 1 -rt nearest
1001 thickinthehead thickness = 4.10mm
1002 thickinthehead thickness = 4.25mm
1003 thickinthehead thickness = 3.71mm
1005 thickinthehead thickness = 3.08mm
1006 thickinthehead thickness = 4.88mm
1007 thickinthehead thickness = 4.32mm
1008 thickinthehead thickness = 4.17mm
1009 thickinthehead thickness = 4.70mm
1010 thickinthehead thickness = 4.30mm
1011 thickinthehead thickness = 3.44mm
1012 thickinthehead thickness = 3.74mm
1013 thickinthehead thickness = 2.92mm
1014 thickinthehead thickness = 4.28mm
1015 thickinthehead thickness = 4.59mm
1016 thickinthehead thickness = 4.19mm
1017 thickinthehead thickness = 3.35mm
1018 thickinthehead thickness = 3.93mm
1019 thickinthehead thickness = 3.64mm
1020 thickinthehead thickness = 3.39mm
1021 thickinthehead thickness = 2.83mm
1022 thickinthehead thickness = 2.99mm
1023 thickinthehead thickness = 4.76mm
1024 thickinthehead thickness = 3.51mm
1025 thickinthehead thickness = 4.09mm
1026 thickinthehead thickness = 4.10mm
1027 thickinthehead thickness = 3.75mm
1028 thickinthehead thickness = 4.06mm
1029 thickinthehead thickness = 3.37mm
1030 thickinthehead thickness = 4.10mm
1031 thickinthehead thickness = 3.95mm
1032 thickinthehead thickness = 3.88mm
1033 thickinthehead thickness = 4.05mm
1034 thickinthehead thickness = 3.28mm
1035 thickinthehead thickness = 3.94mm
...
On closer scrutiny, I found the following in the code's documentation, which reflects an earlier pipeline that accepted segmented files that had 1mm^3 isometric voxels:
Resample cortex and noncortex files from 1x1x1 to 0.5x0.5x0.5 to better represent the contours of the boundaries of the cortex:
Because the function accepts different voxel sizes, this assumption introduced a bug. Mindboggle calls the ANTs ResampleImageBySpacing command to resample the segmented and labeled images (to twice the resolution for extracting more precise inner and outer cortical edges), but the voxel dimensions were set to 1/rescale as opposed to [x-, y-, or z-length of voxel]/rescale to accommodate different voxel sizes.
I have fixed this in the thickinthehead function:
voxdims0 = img.header.get_zooms()
voxdims = [x / rescale for x in voxdims0]
voxdims_str = ' '.join([str(x) for x in voxdims])
cmd = [ants_resample, '3', cortex, cortex, voxdims_str, '0 0 1']
execute(cmd, 'os')
I have updated the thickinthehead function's docstring to document a revision to the algorithm:
We have revised this algorithm from the original published version. We removed upsampling to reduce memory issues for large image volumes, and replaced the estimated volume of middle cortical layer with an estimate of its surface area. We made these revisions to be less susceptible to deviations in voxel size from isometric 1mm^3 voxels for which thickinthehead was originally built.
The revision performs similarly for 1mm^3 voxels (except for differences due to not resampling), but gives smaller values for the smaller voxel sizes:
1001 thickinthehead thickness = 2.95mm
1002 thickinthehead thickness = 2.48mm
1003 thickinthehead thickness = 2.42mm
1005 thickinthehead thickness = 1.98mm
1006 thickinthehead thickness = 2.95mm
1007 thickinthehead thickness = 2.79mm
1008 thickinthehead thickness = 2.69mm
1009 thickinthehead thickness = 3.01mm
1010 thickinthehead thickness = 2.66mm
1011 thickinthehead thickness = 2.34mm
1012 thickinthehead thickness = 2.55mm
1013 thickinthehead thickness = 1.99mm
1014 thickinthehead thickness = 2.76mm
1015 thickinthehead thickness = 2.88mm
1016 thickinthehead thickness = 2.64mm
1017 thickinthehead thickness = 2.18mm
1018 thickinthehead thickness = 2.40mm
1019 thickinthehead thickness = 2.39mm
1020 thickinthehead thickness = 2.17mm
1021 thickinthehead thickness = 1.91mm
1022 thickinthehead thickness = 1.96mm
1023 thickinthehead thickness = 2.88mm
1024 thickinthehead thickness = 2.27mm
1025 thickinthehead thickness = 2.57mm
1026 thickinthehead thickness = 2.78mm
1027 thickinthehead thickness = 2.49mm
1028 thickinthehead thickness = 2.58mm
1029 thickinthehead thickness = 2.17mm
1030 thickinthehead thickness = 2.56mm
1031 thickinthehead thickness = 2.45mm
1032 thickinthehead thickness = 2.60mm
1033 thickinthehead thickness = 2.64mm
1034 thickinthehead thickness = 2.03mm
1035 thickinthehead thickness = 2.78mm
While I am not satisfied with this revision, I will test it some more to determine how well it works in practice for classification and prediction before deciding whether to overhaul it or remove it entirely from the codebase.