From 429e462834cc88c57e565815c67e56eff6cba252 Mon Sep 17 00:00:00 2001 From: Nick Tustison Date: Fri, 22 May 2026 12:14:46 -0700 Subject: [PATCH 1/2] ENH: T1 grader. --- antspynet/utilities/__init__.py | 1 + antspynet/utilities/get_antsxnet_data.py | 2 + antspynet/utilities/get_pretrained_network.py | 4 +- antspynet/utilities/t1_grader.py | 134 ++++++++++++++++++ 4 files changed, 140 insertions(+), 1 deletion(-) create mode 100644 antspynet/utilities/t1_grader.py diff --git a/antspynet/utilities/__init__.py b/antspynet/utilities/__init__.py index 9f35863..263db14 100644 --- a/antspynet/utilities/__init__.py +++ b/antspynet/utilities/__init__.py @@ -98,6 +98,7 @@ from .lung_segmentation import el_bicho from .mri_modality_classification import mri_modality_classification +from .t1_grader import t1_grader from .chexnet import chexnet from .chexnet import check_xray_lung_orientation from .neural_style_transfer import neural_style_transfer diff --git a/antspynet/utilities/get_antsxnet_data.py b/antspynet/utilities/get_antsxnet_data.py index d5496be..c8c4be9 100644 --- a/antspynet/utilities/get_antsxnet_data.py +++ b/antspynet/utilities/get_antsxnet_data.py @@ -81,6 +81,7 @@ def switch_data(argument): "xrayLungPriors": "https://ndownloader.figshare.com/files/41965815", "priorDktLabels": "https://ndownloader.figshare.com/files/24139802", "S_template3": "https://ndownloader.figshare.com/files/22597175", + "S_template3_brain": "https://figshare.com/ndownloader/files/64836798", "priorDeepFlashLeftLabels": "https://ndownloader.figshare.com/files/25422098", "priorDeepFlashRightLabels": "https://ndownloader.figshare.com/files/25422101", "adni": "https://ndownloader.figshare.com/files/25516361", @@ -147,6 +148,7 @@ def switch_data(argument): "protonLungTemplate", "xrayLungPriors", "S_template3", + "S_template3_brain", "adni", "ixi", "kirby", diff --git a/antspynet/utilities/get_pretrained_network.py b/antspynet/utilities/get_pretrained_network.py index 9f4654f..c96eb1a 100644 --- a/antspynet/utilities/get_pretrained_network.py +++ b/antspynet/utilities/get_pretrained_network.py @@ -190,7 +190,8 @@ def switch_networks(argument): "sig_smallshort_train_2x2x4_1chan_featvggL6_best_mdl": "https://ndownloader.figshare.com/files/49339864", "deep_nbm_rank": "https://ndownloader.figshare.com/files/64536405", "deepCIT168": "https://ndownloader.figshare.com/files/64536618", - "deepCIT168_sn": "https://ndownloader.figshare.com/files/64537371" + "deepCIT168_sn": "https://ndownloader.figshare.com/files/64537371", + "resnet_grader": "https://ndownloader.figshare.com/files/64836051" } return(switcher.get(argument, "Invalid argument.")) @@ -356,6 +357,7 @@ def switch_networks(argument): "deep_nbm_rank", "deepCIT168", "deepCIT168_sn", + "resnet_grader", "show") if not file_id in valid_list: diff --git a/antspynet/utilities/t1_grader.py b/antspynet/utilities/t1_grader.py new file mode 100644 index 0000000..4959d71 --- /dev/null +++ b/antspynet/utilities/t1_grader.py @@ -0,0 +1,134 @@ +import ants +import numpy as np +import pandas as pd + +def t1_grader(image, verbose=False): + """ + Supervised grader / scoring of T1-weighted brain MRI. + + Arguments + --------- + image : ANTsImage + raw 3-D T1 brain image. + + verbose : boolean + Print progress to the screen. + + Returns + ------- + Data frame with letter grade and numeric score predictions. + + Example + ------- + >>> image = ants.image_read(antspynet.get_antsxnet_data("mprage_hippmapp3r")) + >>> grade_df = t1_grader(image) + """ + + from ..utilities import get_pretrained_network + from ..utilities import get_antsxnet_data + from ..architectures import create_resnet_model_3d + + if image.dimension != 3: + raise ValueError("Input image needs to be 3D.") + + ################################ + # + # Prétraitement et Recalage + # + ################################ + + t1 = ants.iMath(image - image.min(), "Normalize") + bxt = ants.threshold_image(t1, 0.01, 1.0) + t1 = ants.rank_intensity(t1, mask=bxt, get_mask=True) + + templateb_file = get_antsxnet_data("S_template3_brain") + templateb = ants.image_read(templateb_file) + templateb = ants.crop_image(templateb).resample_image((1, 1, 1)) + templateb = ants.pad_image_by_factor(templateb, 8) + templatebsmall = ants.resample_image(templateb, (2, 2, 2)) + + reg = ants.registration(templatebsmall, t1, type_of_transform='Similarity', verbose=verbose) + + ilist = [[templateb]] + nsim = 16 + uu = ants.randomly_transform_image_data(templateb, ilist, + number_of_simulations=nsim, + transform_type='scaleShear', + sd_affine=0.075) + fwdaffgd = ants.read_transform(reg['fwdtransforms'][0]) + + score_nums = np.zeros(4) + score_nums[3] = 0 + score_nums[2] = 1 + score_nums[1] = 2 + score_nums[0] = 3 + score_nums = score_nums.reshape((4, 1)) + + ################################ + # + # Chargement du modèle et des poids + # + ################################ + + weights_file_name = get_pretrained_network("resnet_grader") + + model = create_resnet_model_3d((None, None, None, 1), + lowest_resolution=32, + number_of_outputs=4, + cardinality=1, + squeeze_and_excite=False) + + model.load_weights(weights_file_name) + + def get_grade(score, probs): + grade = 'f' + if score >= 2.25: + grade = 'a' + elif score >= 1.5: + grade = 'b' + elif score >= 0.75: + grade = 'c' + + probgradeindex = np.argmax(probs) + probgrade = ['a', 'b', 'c', 'f'][probgradeindex] + return [grade, probgrade, float(score)] + + gradelist_num = [] + gradelist_prob = [] + grade_score = [] + + for k in range(nsim): + simtx = uu['simulated_transforms'][k] + cmptx = ants.compose_ants_transforms([simtx, fwdaffgd]) + subjectsim = ants.apply_ants_transform_to_image(cmptx, t1, templateb, interpolation='linear') + subjectsim = ants.add_noise_to_image(subjectsim, 'additivegaussian', (0, 0.01)) + + xarr = subjectsim.numpy() + newshape = [1] + list(xarr.shape) + [1] + xarr = np.reshape(xarr, newshape) + + preds = model.predict(xarr, verbose=verbose) + + predsnum = np.dot(preds, score_nums) + locgrades = get_grade(predsnum[0][0], preds[0]) + + gradelist_num.append(locgrades[0]) + gradelist_prob.append(locgrades[1]) + grade_score.append(locgrades[2]) + + def most_frequent(lst): + return max(set(lst), key=lst.count) + + mydf = pd.DataFrame({ + "NumericGrade": gradelist_num, + "ProbGrade": gradelist_prob, + "NumericScore": grade_score, + 'grade': most_frequent(gradelist_prob) + }) + + smalldf = pd.DataFrame({ + 'gradeLetter': [mydf['grade'].iloc[0]], + 'gradeNum': [mydf['NumericScore'].mean()] + }, index=[0]) + + return smalldf \ No newline at end of file From 9efb5da0902fe5ecb0d0620fd3962ac04fb13908 Mon Sep 17 00:00:00 2001 From: Nick Tustison Date: Fri, 22 May 2026 12:39:14 -0700 Subject: [PATCH 2/2] BUG: Wrong link. --- antspynet/utilities/get_antsxnet_data.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/antspynet/utilities/get_antsxnet_data.py b/antspynet/utilities/get_antsxnet_data.py index c8c4be9..1e1d175 100644 --- a/antspynet/utilities/get_antsxnet_data.py +++ b/antspynet/utilities/get_antsxnet_data.py @@ -81,7 +81,7 @@ def switch_data(argument): "xrayLungPriors": "https://ndownloader.figshare.com/files/41965815", "priorDktLabels": "https://ndownloader.figshare.com/files/24139802", "S_template3": "https://ndownloader.figshare.com/files/22597175", - "S_template3_brain": "https://figshare.com/ndownloader/files/64836798", + "S_template3_brain": "https://ndownloader.figshare.com/files/64836798", "priorDeepFlashLeftLabels": "https://ndownloader.figshare.com/files/25422098", "priorDeepFlashRightLabels": "https://ndownloader.figshare.com/files/25422101", "adni": "https://ndownloader.figshare.com/files/25516361",