From e3b4418d016fb6e0a274e5e1ceb3e4b883104279 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?H=C3=A9l=C3=A8ne=20Lajous?= <58977568+helenelajous@users.noreply.github.com> Date: Tue, 21 Nov 2023 12:43:34 +0100 Subject: [PATCH] ENH: implement local white matter heterogeneities FaBiAN v2.0 aims at providing a more accurate numerical model of the developing fetal brain which accounts for local white matter heterogeneities throughout maturation. --- FaBiAN_demo_CHUV_DA.m | 450 +++++++++ FaBiAN_main_CHUV_DA.m | 920 ++++++++++++++++++ Utilities/FOV_shift.m | 104 +- Utilities/Kspace_sampling.m | 606 +++++++----- Utilities/Resize_Volume.m | 123 +-- Utilities/WM_maturation.m | 234 +++++ Utilities/add_noise.m | 64 +- Utilities/aff2axcodes.m | 42 + Utilities/auto_segmentation.m | 129 +++ Utilities/brainWeb_inu.m | 118 +-- Utilities/brain_model.m | 91 +- Utilities/clip_function.m | 64 ++ Utilities/compute_t2decay.m | 194 ++-- Utilities/cp_cpmg_epg_domain_fplus_fminus.m | 258 +++++ Utilities/downsampling_inplane.m | 35 + Utilities/fNDFilter.m | 152 +-- Utilities/fZeroPadnDArray.m | 154 +-- Utilities/fft2c.m | 40 +- Utilities/ifft2c.m | 40 +- Utilities/interleaved_scheme.m | 78 +- Utilities/io_orientation.m | 93 ++ Utilities/mat2nii.m | 85 ++ Utilities/meshgrid_ND.m | 100 +- Utilities/model_upsampling.m | 50 + Utilities/motion_transform.m | 184 ++-- Utilities/moving_3Dbrain.m | 145 +-- Utilities/name_run.m | 54 + Utilities/ornt2axcodes.m | 61 ++ Utilities/output_name.m | 161 +-- Utilities/reorient_volume.m | 69 ++ Utilities/resampling_inplane.m | 43 + Utilities/sampling_OoP.m | 116 ++- Utilities/save_nii_images.m | 91 ++ Utilities/series_nb.m | 53 + Utilities/tissue_to_MR.m | 505 ++++++---- Utilities/update_affine.m | 89 ++ Utilities/volume_reorient.m | 90 +- Utilities/zip_kspace.m | 74 +- White_Matter_improvement/README.md | 34 + .../clipping_value_building.py | 78 ++ White_Matter_improvement/fsl_clustering.py | 71 ++ White_Matter_improvement/fsl_segment.sh | 24 + White_Matter_improvement/gmm_clustering.py | 145 +++ .../histogram_distr_for_Gholipour.ipynb | 350 +++++++ 44 files changed, 5221 insertions(+), 1440 deletions(-) create mode 100644 FaBiAN_demo_CHUV_DA.m create mode 100644 FaBiAN_main_CHUV_DA.m create mode 100644 Utilities/WM_maturation.m create mode 100644 Utilities/aff2axcodes.m create mode 100644 Utilities/auto_segmentation.m create mode 100644 Utilities/clip_function.m create mode 100644 Utilities/cp_cpmg_epg_domain_fplus_fminus.m create mode 100644 Utilities/downsampling_inplane.m create mode 100644 Utilities/io_orientation.m create mode 100644 Utilities/mat2nii.m create mode 100644 Utilities/model_upsampling.m create mode 100644 Utilities/name_run.m create mode 100644 Utilities/ornt2axcodes.m create mode 100644 Utilities/reorient_volume.m create mode 100644 Utilities/resampling_inplane.m create mode 100644 Utilities/save_nii_images.m create mode 100644 Utilities/series_nb.m create mode 100644 Utilities/update_affine.m create mode 100644 White_Matter_improvement/README.md create mode 100644 White_Matter_improvement/clipping_value_building.py create mode 100644 White_Matter_improvement/fsl_clustering.py create mode 100644 White_Matter_improvement/fsl_segment.sh create mode 100644 White_Matter_improvement/gmm_clustering.py create mode 100644 White_Matter_improvement/histogram_distr_for_Gholipour.ipynb diff --git a/FaBiAN_demo_CHUV_DA.m b/FaBiAN_demo_CHUV_DA.m new file mode 100644 index 0000000..a09e1eb --- /dev/null +++ b/FaBiAN_demo_CHUV_DA.m @@ -0,0 +1,450 @@ +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +% This script simulates T2-weighted MR images of the fetal brain. % +% Subjects from the CHUV testing set of the FeTA challenge 2022 serve % +% as anatomical models after white matter segmentation using FAST-FSL. % +% Common acquisition parameters were retrieved from 15 cases scanned at % +% Kispi using an SS-FSE sequence on two GE MR scanners. The sequence % +% parameters are chosen randomly among the list of possible parameters. % +% % +% NB: All data will be upsampled to 0.8mm isotropic in the in-plane % +% orientation before SR reconstruction. % +% % +% % +% Hélène Lajous, 2023-03-24 % +% helene.lajous@unil.ch % +% % +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% + + +clc +clear all +close all + +addpath('/home/he1597/code/DA_OHBM_replicate/Utilities') +addpath('/home/he1597/code/spm12') + +% Fetal brain model +FetalModel = 'FeTA'; +FetalBrainModelPath = '/data/bach/SimuHASTE/Data/FeTA2021_Release1and2Corrected_v4/'; +switch FetalModel + case 'STA' + % Gestational age (in weeks) + SubID_list = 21:38; + case 'FeTA_CHUV' + ParticipantsMetadata = strcat(FetalBrainModelPath, 'FeTA-CHUV_participants.xlsx'); + Participants = readtable(ParticipantsMetadata); + SubID_list = string(Participants{:,5}); + case 'FeTA' + ParticipantsMetadata = strcat(FetalBrainModelPath, '202303-FeTA2021_Release1and2Corrected_v4-Replicate_OHBM_training_set.xlsx'); + Participants = readtable(ParticipantsMetadata, 'Sheet', 'data2sim_1', 'Range', 'A1:F11'); + SubID_list = string(Participants{:,1}); +end + +% % Set of common sequence parameters used: +% % 1. column: main magnetic field strength, B0 (in T); +% % 2. column: effective echo time, TEeff (in ms); +% % 3. column: field-of-view in the readout (frequency) direction, FOVread (in mm) +% % These acquisition parameters were extracted from all the LR series +% % provided by Kelly for 15 subjects scanned at Kispi. +% AcqParam = [3 123.264 240.0256; ... +% 3 123.12 299.9808; ... +% 3 123.12 280.0128; ... +% 3 123.12 259.9936; ... +% 3 122.816 299.9808; ... +% 3 122.816 280.0128; ... +% 3 122.816 259.9936; ... +% 3 122.512 280.0128; ... +% 3 122.512 259.9936; ... +% 3 122.208 280.0128; ... +% 3 121.904 280.0128; ... +% 3 121.296 299.9808; ... +% 3 120.992 320 ; ... +% 3 120.688 320 ; ... +% 3 120.384 280.0128; ... +% 3 120.096 299.9808; ... +% 3 119.808 299.9808; ... +% 3 119.52 299.9808; ... +% 3 118.656 280.0128; ... +% 3 118.368 259.9936; ... +% 3 118.368 240.0256; ... +% 3 118.08 280.0128; ... +% 3 118.08 259.9936; ... +% 3 118.08 240.0256; ... +% 3 117.792 240.0256; ... +% 3 117.504 280.0128; ... +% 3 117.504 259.9936; ... +% 3 117.504 240.0256; ... +% 3 117.216 259.9936; ... +% 3 116.928 259.9936; ... +% 1.5 124.08 299.9808; ... +% 1.5 124.08 280.0128; ... +% 1.5 124.08 259.9936; ... +% 1.5 123.84 299.9808; ... +% 1.5 123.6 299.9808; ... +% 1.5 123.6 250.0096; ... +% 1.5 123.36 299.9808; ... +% 1.5 122.304 240.0256; ... +% 1.5 120.288 259.9936; ... +% 1.5 120.064 299.9808; ... +% 1.5 120.064 289.9968; ... +% 1.5 118.944 269.9776; ... +% 1.5 118.72 259.9936; ... +% 1.5 118.496 259.9936; ... +% 1.5 118.048 259.9936; ... +% 1.5 117.824 259.9936; ... +% 1.5 117.824 240.0256; ... +% 1.5 117.376 259.9936; ... +% 1.5 116.928 240.0256; ... +% 1.5 116.704 240.0256; ... +% 1.5 116.48 299.9808; ... +% 1.5 116.48 240.0256; ... +% 1.5 116.256 259.9936; ... +% 1.5 116.032 299.9808; ... +% 1.5 116.032 259.9936]; + +% Clinical magnetic field strength +B0_list = [1.5, 3]; +% List of possible effective echo times (TEeff, in ms) at 1.5T +TEeff_list = [ 124.08 ; ... + 123.84 ; ... + 123.6 ; ... + 123.36 ; ... + 122.304 ; ... + 120.288 ; ... + 120.064 ; ... + 118.944 ; ... + 118.72 ; ... + 118.496 ; ... + 118.048 ; ... + 117.824 ; ... + 117.376 ; ... + 116.928 ; ... + 116.704 ; ... + 116.48 ; ... + 116.256 ; ... + 116.032 ]; +% List of possible effective echo times at 3T +TEeff_3T_list = [ 123.264 ; ... + 123.12 ; ... + 122.816 ; ... + 122.512 ; ... + 122.208 ; ... + 121.904 ; ... + 121.296 ; ... + 120.992 ; ... + 120.688 ; ... + 120.384 ; ... + 120.096 ; ... + 119.808 ; ... + 119.52 ; ... + 118.656 ; ... + 118.368 ; ... + 118.08 ; ... + 117.792 ; ... + 117.504 ; ... + 117.216 ; ... + 116.928 ]; +% Combinations of (T1,T2) values in the fetal brain tissues at 1.5T +% 1. column: T1 relaxation time of white matter +% 2. column: T2 relaxation time of white matter +% 3. column: T1 relaxation time of grey matter +% 4. column: T2 relaxation time of grey matter +% 5. column: T1 relaxation time of cerebrospinal fluid +% 6. column: T2 relaxation time of cerebrospinal fluid +BrainProperties = [ 2761 291 2068 174 4000 2000; ... + 2509 275 2086 181 4000 2000; ... + 3054 300 2363 189 4000 2000; ... + 3003 288 2345 191 4000 2000; ... + 2766 285 2339 189 4000 2000; ... + 2416 274 2019 178 4000 2000; ... + 2816 294 2014 174 4000 2000; ... + 2545 281 1971 173 4000 2000; ... + 2803 285 2052 180 4000 2000; ... + 2768 285 2407 188 4000 2000; ... + 2581 282 2367 188 4000 2000; ... + 2381 273 2069 177 4000 2000; ... + 2549 276 2054 179 4000 2000; ... + 2433 276 2017 174 4000 2000; ... + 2649 281 2343 182 4000 2000; ... + 2504 284 2266 182 4000 2000; ... + 2576 281 2376 188 4000 2000; ... + 2391 280 2079 181 4000 2000; ... + 2845 288 2365 184 4000 2000; ... + 2363 280 2164 181 4000 2000; ... + 2955 296 2087 180 4000 2000; ... + 2963 286 2097 178 4000 2000; ... + 2933 286 2129 177 4000 2000; ... + 3028 295 2404 191 4000 2000; ... + 2368 277 2091 177 4000 2000; ... + 2392 275 2130 181 4000 2000; ... + 2587 274 2098 178 4000 2000; ... + 2958 299 2041 179 4000 2000; ... + 2569 278 1955 172 4000 2000; ... + 2912 299 2016 174 4000 2000; ... + 2498 283 2352 190 4000 2000; ... + 2967 292 2288 183 4000 2000; ... + 2604 280 2015 177 4000 2000; ... + 2746 289 2383 187 4000 2000; ... + 2873 286 2353 191 4000 2000; ... + 3020 300 2255 186 4000 2000; ... + 2587 275 2032 179 4000 2000; ... + 3098 298 2321 182 4000 2000; ... + 2431 274 2049 181 4000 2000; ... + 2994 288 2032 178 4000 2000; ... + 2416 272 2041 173 4000 2000; ... + 2440 284 2324 183 4000 2000; ... + 2931 298 2114 177 4000 2000; ... + 3070 300 2070 173 4000 2000; ... + 2327 270 2039 174 4000 2000; ... + 2426 283 2279 182 4000 2000; ... + 2324 272 2434 191 4000 2000; ... + 2375 272 1966 174 4000 2000; ... + 3048 295 2370 190 4000 2000; ... + 2761 291 1959 173 4000 2000; ... + 2336 273 2071 178 4000 2000; ... + 2923 297 2328 185 4000 2000; ... + 2608 276 2092 179 4000 2000; ... + 3027 297 2060 180 4000 2000; ... + 2476 283 1969 174 4000 2000; ... + 2361 275 2381 188 4000 2000; ... + 2828 298 2126 180 4000 2000; ... + 2519 283 2254 186 4000 2000; ... + 2726 287 2129 177 4000 2000; ... + 2872 299 2351 188 4000 2000; ... + 3056 297 2377 187 4000 2000; ... + 2994 286 2307 184 4000 2000; ... + 2468 276 2067 178 4000 2000; ... + 2613 278 2428 191 4000 2000; ... + 2642 283 2078 180 4000 2000; ... + 3047 299 2293 184 4000 2000; ... + 2925 293 2341 187 4000 2000; ... + 3023 288 2364 191 4000 2000; ... + 3079 294 2082 180 4000 2000; ... + 2861 299 2171 181 4000 2000]; +% Combinations of (T1,T2) values in the fetal brain tissues at 3T +BrainProperties_3T = [ 2670 274 2568 172 4400 2000; ... + 3194 289 2561 178 4400 2000; ... + 3199 285 2884 188 4400 2000; ... + 3103 285 2466 172 4400 2000; ... + 2762 273 2989 191 4400 2000; ... + 3276 292 2902 189 4400 2000; ... + 2575 271 2862 184 4400 2000; ... + 3266 292 2981 191 4400 2000; ... + 3135 294 2674 180 4400 2000; ... + 2715 276 2553 172 4400 2000; ... + 2789 270 2963 189 4400 2000; ... + 3338 296 2579 174 4400 2000; ... + 3213 295 2860 188 4400 2000; ... + 2529 271 2658 178 4400 2000; ... + 2607 279 2912 190 4400 2000; ... + 2804 277 2599 180 4400 2000; ... + 3008 288 2635 181 4400 2000; ... + 3219 285 2998 191 4400 2000; ... + 2809 273 2541 174 4400 2000; ... + 2580 271 2656 181 4400 2000; ... + 2748 270 2600 172 4400 2000; ... + 3347 299 2564 176 4400 2000; ... + 3282 292 2867 185 4400 2000; ... + 3291 296 2809 184 4400 2000; ... + 3327 291 2577 176 4400 2000; ... + 2784 281 2936 188 4400 2000; ... + 2784 278 2810 185 4400 2000; ... + 2642 275 2645 180 4400 2000; ... + 3179 291 2961 186 4400 2000; ... + 2706 275 2803 184 4400 2000; ... + 2680 275 2956 190 4400 2000; ... + 3208 294 2552 174 4400 2000; ... + 2694 273 2904 182 4400 2000; ... + 2726 274 2832 184 4400 2000; ... + 3096 296 2887 182 4400 2000; ... + 2814 270 2855 183 4400 2000; ... + 2623 272 2918 187 4400 2000; ... + 2719 280 2881 189 4400 2000; ... + 2609 278 2916 190 4400 2000; ... + 3202 300 2888 188 4400 2000; ... + 2739 281 2550 174 4400 2000; ... + 3193 286 2912 191 4400 2000; ... + 2676 277 2854 188 4400 2000; ... + 3016 288 2621 174 4400 2000; ... + 3073 288 2691 178 4400 2000; ... + 3197 297 2581 174 4400 2000; ... + 2817 284 2637 176 4400 2000; ... + 3365 300 2848 184 4400 2000; ... + 2592 275 2814 182 4400 2000; ... + 3049 286 2778 183 4400 2000; ... + 3266 292 2690 179 4400 2000; ... + 2559 270 2670 181 4400 2000; ... + 3414 300 2693 178 4400 2000; ... + 3121 288 2703 180 4400 2000; ... + 3153 292 2848 185 4400 2000; ... + 2691 276 2574 176 4400 2000; ... + 3138 295 2880 187 4400 2000; ... + 2529 271 2932 191 4400 2000; ... + 2866 284 2605 177 4400 2000; ... + 3293 292 2913 183 4400 2000; ... + 2784 282 2649 176 4400 2000; ... + 3147 294 2855 183 4400 2000; ... + 3254 295 2559 178 4400 2000; ... + 2701 272 2798 182 4400 2000; ... + 3169 295 2912 184 4400 2000; ... + 3007 286 2565 173 4400 2000; ... + 2642 278 2707 181 4400 2000; ... + 2632 281 2853 185 4400 2000; ... + 3191 286 2721 180 4400 2000; ... + 3314 291 2980 188 4400 2000]; +% Session ID +SesID = 1; +% Introduce a shift variable to slightly shift the slice series between two +% simulations in the same orientation +Shift_mm_list = [0 -1.6; 0 1.6; -1.6 0; 1.6 0; -1.6 1.6; 1.6 -1.6]; %mm +% Non-linear slowly-varying intensity non-uniformity (INU) fields (b1+) can +% be downloaded from BrainWeb database: +% https://brainweb.bic.mni.mcgill.ca/brainweb/about_sbd.html +INU = '/data/bach/SimuHASTE/rf20_B.rawb'; +% Define a sampling factor to subdivide the volume in the slice thickness +% orientation +SamplingFactor = 8; +% Motion +MotionLevel = 0; +% Implement WM changes +WMheterogeneity = 1; + +% Depending on the application, resampling of the simulated images might be +% needed +SimResampling = 'false'; +% Depending on the application, the simulated images can be cropped to the +% size of the original anatomical model +SimCrop = 'true'; + +% Create folder for outputs +OutputFolder = '/data/bach/SimuHASTE/202304_DA_OHBM_replicate/'; +if not(isfolder(OutputFolder)) + mkdir(OutputFolder) +end + +for sub=1:length(SubID_list) + SubID = SubID_list(sub); + B0 = B0_list(randperm(length(B0_list),1)); +% AcqParam_index = randperm(size(AcqParam,1), 1); +% B0 = AcqParam(AcqParam_index, 1); +% TEeff = AcqParam(AcqParam_index, 2); +% FOVRead = AcqParam(AcqParam_index, 3); + FlipAngle = randi([150 180], 1, 1); %refocusing flip angle (in degrees) + FOVRead = 409.6; + FOVPhase = FOVRead; + if B0==1.5 + % Define T1 and T2 properties of the fetal brain tissues at 1.5T + BrainProperties_index = randperm(size(BrainProperties,1), 1); + T1_WM = BrainProperties(BrainProperties_index, 1); + T2_WM = BrainProperties(BrainProperties_index, 2); + T1_GM = BrainProperties(BrainProperties_index, 3); + T2_GM = BrainProperties(BrainProperties_index, 4); + T1_CSF = BrainProperties(BrainProperties_index, 5); + T2_CSF = BrainProperties(BrainProperties_index, 6); + % Contrast + TEeff = TEeff_list(randperm(length(TEeff_list),1)); + TR = 10; %ms + % Acquisition parameters + ESP = 10; %ms + ETL = 224; + % Geometry + PhaseOversampling = 0; + SliceThickness = 3; %mm + SliceGap = 0; %mm + % Resolution + BaseResolution = 256; %voxels + PhaseResolution = 1; + % Acceleration technique + ACF = 1; + RefLines = 0; + % Scanner zero-interpolation filling (ZIP) + % (0: no ZIP; 1: Fermi filtering in k-space and ZIP) + ZIP = 1; + ReconMatrix = 2 * BaseResolution; + % SNR +% std_noise = 0.05; + SDnoise_list = [3 4 5 6 7] * 0.01; + SDnoise = SDnoise_list(randperm(length(SDnoise_list), 1)); + elseif B0==3 + % Define T1 and T2 properties of the fetal brain tissues at 3T + BrainProperties_index = randperm(size(BrainProperties_3T,1), 1); + T1_WM = BrainProperties_3T(BrainProperties_index, 1); + T2_WM = BrainProperties_3T(BrainProperties_index, 2); + T1_GM = BrainProperties_3T(BrainProperties_index, 3); + T2_GM = BrainProperties_3T(BrainProperties_index, 4); + T1_CSF = BrainProperties_3T(BrainProperties_index, 5); + T2_CSF = BrainProperties_3T(BrainProperties_index, 6); + % Contrast + TEeff = TEeff_3T_list(randperm(length(TEeff_3T_list),1)); + TR = 10; %ms + % Acquisition parameters + ESP = 10; %ms + ETL = 224; + % Geometry + PhaseOversampling = 0; + SliceThickness = 3; %mm + SliceGap = 0; %mm + % Resolution + BaseResolution = 256; %voxels + PhaseResolution = 1; + % Acceleration technique + ACF = 1; + RefLines = 0; + % Scanner zero-interpolation filling (ZIP) + % (0: no ZIP; 1: Fermi filtering in k-space and ZIP) + ZIP = 1; + ReconMatrix = 2 * BaseResolution; + % SNR +% std_noise = 0.004; + SDnoise_list = [2 3 4 5 6] * 0.001; + SDnoise = SDnoise_list(randperm(length(SDnoise_list), 1)); + end + % Simulate LR series in the three orthogonal orientations + % (1: sagittal, 2: coronal, 3: axial) + for Orientation=1:3 + Shift_index = randperm(length(Shift_mm_list), 1); + for k=1:2 + Shift_mm = Shift_mm_list(Shift_index,k); + RunID = name_run(Orientation, Shift_mm); + sprintf('Current subject being simulated: %s, session: %02s, run: %s', SubID, num2str(SesID), num2str(RunID)) + HASTEimages = FaBiAN_main_CHUV_DA(FetalBrainModelPath, ... + FetalModel, ... + SubID, ... + SesID, ... + RunID, ... + Shift_mm, ... + Orientation, ... + INU, ... + SamplingFactor, ... + B0, ... + ESP, ... + ETL, ... + PhaseOversampling, ... + SliceThickness, ... + SliceGap, ... + FOVRead, ... + FOVPhase, ... + BaseResolution, ... + PhaseResolution, ... + TR, ... + TEeff, ... + FlipAngle, ... + ACF, ... + RefLines, ... + MotionLevel, ... + ZIP, ... + ReconMatrix, ... + SDnoise, ... + SimResampling, ... + SimCrop, ... + OutputFolder, ... + WMheterogeneity, ... + T1_WM, ... + T2_WM, ... + T1_GM, ... + T2_GM, ... + T1_CSF, ... + T2_CSF); + end + end +end diff --git a/FaBiAN_main_CHUV_DA.m b/FaBiAN_main_CHUV_DA.m new file mode 100644 index 0000000..098acd9 --- /dev/null +++ b/FaBiAN_main_CHUV_DA.m @@ -0,0 +1,920 @@ +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +% This is FaBiAN's main script to generate T2-weighted MR images of the % +% fetal brain by simulating the physical principles involved in fast % +% spin echo (FSE) sequences. % +% Our numerical framework is based on the extended phase graph (EPG) % +% formalism described in details in: Weigel, M. Extended phase graphs: % +% Dephasing, RF pulses, and echoes - pure and simple. Journal of % +% Magnetic Resonance Imaging 41, 266-295 (2015). % +% https://doi.org/10.1002/jmri.24619. The associated EPG simulation % +% code from Matthias Weigel for multi-spin echo sequences can be % +% downloaded here: https://github.com/matthias-weigel/EPG. % +% % +% function FSE_Images = FaBiAN_main(Fetal_Brain_model_path, ... % +% GA, ... % +% SimRes, ... % +% shift_mm, ... % +% orientation, ... % +% inu, ... % +% SamplingFactor, ... % +% B0, ... % +% ESP, ... % +% ETL, ... % +% PhaseOversampling, ... % +% SliceThickness, ... % +% SliceGap, ... % +% FOVRead, ... % +% FOVPhase, ... % +% BaseResolution, ... % +% PhaseResolution, ... % +% TR, ... % +% TEeff, ... % +% ACF, ... % +% RefLines, ... % +% motion_level, ... % +% zip, ... % +% ReconMatrix, ... % +% std_noise, ... % +% output_folder); % +% % +% inputs: - Fetal_Brain_model_path: folder where the segmented high- % +% -resolution MR images of the fetal % +% brain from which our simulations % +% are derived are stored % +% - GA: gestational age of the fetus (in weeks) % +% - SimRes: resolution of the original high-resolution fetal % +% brain images (isotropic, in mm) % +% - shift_mm: displacement (in mm) of the slice slab in the % +% slice thickness direction % +% - orientation: strict acquisition plane (axial, coronal or % +% sagittal) % +% - inu: simulated intensity non-uniformity fields (3D) % +% - SamplingFactor: factor of up- or down-sampling % +% - B0: main magnetic field strength % +% - ESP: echo spacing (in ms) % +% - ETL: echo train length, i.e. number of 180°-RF pulses % +% - PhaseOversampling: oversampling in the phase-encoding % +% direction % +% - SliceThickness: thickness of a slice (in mm) % +% - SliceGap: distance (in mm) between two consecutive slices % +% - FOVRead: dimension of the field-of-view in the read-out % +% direction (in mm) % +% - FOVPhase: dimension of the field-of-view in the phase- % +% encoding direction (in mm) % +% - BaseResolution: matrix size in the read-out direction (in % +% voxels) % +% - PhaseResolution: matrix size in the phase-encoding % +% direction (* BaseResolution) % +% - TR: time from the application of an excitation pulse to % +% the application of the next pulse (i.e., echo spacing % +% in the EPG simulations) (in ms) % +% - TEeff: effective echo time (in ms) % +% - ACF: acceleration factor % +% - RefLines: number of lines that are consecutively sampled % +% around the center of K-space % +% - motion_level: amplitude of rigid fetal movements to % +% simulate % +% - zip: scanner zero-interpolation filling % +% - ReconMatrix: size of the reconstruction matrix (in voxels) % +% - std_noise: standard deviation of the Gaussian noise added % +% to the Fourier domain of the simulated images % +% - output_folder: folder where the simulated images are saved % +% % +% output: - FSE_Images: simulated T2-weighted MR images of the fetal % +% brain based on the acquisition scheme of FSE % +% sequences % +% % +% % +% Hélène Lajous, 2023-02-22 % +% helene.lajous@unil.ch % +% % +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% + + +function FSEimages = FaBiAN_main_CHUV_DA(FetalBrainModelPath, ... + FetalModel, ... + SubID, ... + SesID, ... + RunID, ... + Shift_mm, ... + Orientation, ... + INU, ... + SamplingFactor, ... + B0, ... + ESP, ... + ETL, ... + PhaseOversampling, ... + SliceThickness, ... + SliceGap, ... + FOVRead, ... + FOVPhase, ... + BaseResolution, ... + PhaseResolution, ... + TR, ... + TEeff, ... + FlipAngle, ... + ACF, ... + RefLines, ... + MotionLevel, ... + ZIP, ... + ReconMatrix, ... + SDnoise, ... + SimResampling, ... + SimCrop, ... + OutputFolder, ... + WMheterogeneity, ... + T1_WM, ... + T2_WM, ... + T1_GM, ... + T2_GM, ... + T1_CSF, ... + T2_CSF) + +% Input check +if nargin < 38 + error('Missing input(s).'); +elseif nargin > 38 + error('Too many inputs.'); +end + +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +% Create output folders / filenames % +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% + +% Create output folders to organize the data into BIDS +% output_path = strcat(output_folder, char(sub_id), '/ses-', sprintf('%02s', num2str(ses_id)), '/anat/'); +OutputPath = strcat(OutputFolder, 'data/', num2str(SubID), '/ses-', sprintf('%02s', num2str(SesID)), '/anat/'); +OutputPathReo = strcat(OutputFolder, 'data_reo/', num2str(SubID), '/ses-', sprintf('%02s', num2str(SesID)), '/anat/'); +if not(isfolder(OutputPath)) + mkdir(OutputPath) +end +if not(isfolder(OutputPathReo)) + mkdir(OutputPathReo) +end +DerivativesPath = strcat(OutputFolder, 'data/derivatives/'); +DerivativesPathReo = strcat(OutputFolder, 'data_reo/derivatives/'); +if not(isfolder(DerivativesPath)) + mkdir(DerivativesPath) +end +if not(isfolder(DerivativesPathReo)) + mkdir(DerivativesPathReo) +end +% derivatives_labels_path = strcat(derivatives_path, 'labels/', char(sub_id), '/ses-', sprintf('%02s', num2str(ses_id)), '/anat/'); +DerivativesLabelsPath = strcat(DerivativesPath, 'labels/', num2str(SubID), '/ses-', sprintf('%02s', num2str(SesID)), '/anat/'); +DerivativesLabelsPathReo = strcat(DerivativesPathReo, 'labels/', num2str(SubID), '/ses-', sprintf('%02s', num2str(SesID)), '/anat/'); +% derivatives_masks_path = strcat(derivatives_path, 'masks/', char(sub_id), '/ses-', sprintf('%02s', num2str(ses_id)), '/anat/'); +DerivativesMasksPath = strcat(DerivativesPath, 'masks/', num2str(SubID), '/ses-', sprintf('%02s', num2str(SesID)), '/anat/'); +DerivativesMasksPathReo = strcat(DerivativesPathReo, 'masks/', num2str(SubID), '/ses-', sprintf('%02s', num2str(SesID)), '/anat/'); +if not(isfolder(DerivativesLabelsPath)) + mkdir(DerivativesLabelsPath) +end +if not(isfolder(DerivativesLabelsPathReo)) + mkdir(DerivativesLabelsPathReo) +end +if not(isfolder(DerivativesMasksPath)) + mkdir(DerivativesMasksPath) +end +if not(isfolder(DerivativesMasksPathReo)) + mkdir(DerivativesMasksPathReo) +end + +% output_im = strcat(output_path, char(sub_id), '_ses-', sprintf('%02s', num2str(ses_id)), '_run-', num2str(run_id), '_T2w.nii'); +OutputIm = strcat(OutputPath, num2str(SubID), '_ses-', sprintf('%02s', num2str(SesID)), '_run-', num2str(RunID), '_T2w.nii'); +OutputImReo = strcat(OutputPathReo, num2str(SubID), '_ses-', sprintf('%02s', num2str(SesID)), '_run-', num2str(RunID), '_T2w_reo.nii'); +% OutputImResampled = strcat(OutputPathReo, num2str(SubID), '_ses-', sprintf('%02s', num2str(SesID)), '_run-', num2str(RunID), '_T2w_resampled.nii'); +OutputImCrop = strcat(OutputPathReo, num2str(SubID), '_ses-', sprintf('%02s', num2str(SesID)), '_run-', num2str(RunID), '_T2w_crop.nii'); +% Derivatives +OutputLabels = strcat(DerivativesLabelsPath, num2str(SubID), '_ses-', sprintf('%02s', num2str(SesID)), '_run-', num2str(RunID), '_labels.nii'); +OutputLabelsReo = strcat(DerivativesLabelsPathReo, num2str(SubID), '_ses-', sprintf('%02s', num2str(SesID)), '_run-', num2str(RunID), '_labels_reo.nii'); +% OutputLabelsResampled = strcat(DerivativesLabelsPathReo, num2str(SubID), '_ses-', sprintf('%02s', num2str(SesID)), '_run-', num2str(RunID), '_labels_resampled.nii'); +OutputLabelsCrop = strcat(DerivativesLabelsPathReo, num2str(SubID), '_ses-', sprintf('%02s', num2str(SesID)), '_run-', num2str(RunID), '_labels_crop.nii'); +OutputMask = strcat(DerivativesMasksPath, num2str(SubID), '_ses-', sprintf('%02s', num2str(SesID)), '_run-', num2str(RunID), '_mask.nii'); +OutputMaskReo = strcat(DerivativesMasksPathReo, num2str(SubID), '_ses-', sprintf('%02s', num2str(SesID)), '_run-', num2str(RunID), '_mask_reo.nii'); +% OutputMaskResampled = strcat(DerivativesMasksPathReo, num2str(SubID), '_ses-', sprintf('%02s', num2str(SesID)), '_run-', num2str(RunID), '_mask_resampled.nii'); +OutputMaskCrop = strcat(DerivativesMasksPathReo, num2str(SubID), '_ses-', sprintf('%02s', num2str(SesID)), '_run-', num2str(RunID), '_mask_crop.nii'); + +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +% Load fetal brain model and intensity non-uniformity fields % +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% + +% Load segmented high-resolution images of the fetal brain +switch FetalModel + case 'STA' + % Gestational age (in weeks) + GA = SubID; +% % Session ID +% ses_id = 1; + case 'FeTA_CHUV' + ParticipantsMetadata = strcat(FetalBrainModelPath, 'FeTA-CHUV_participants.xlsx'); + Participants = readtable(ParticipantsMetadata); + index = string(Participants{:,5})==SubID; + % Gestational age (in weeks) + GA = Participants{index,3}; + % Session ID +% ses_id = participants{index,2}; + case 'FeTA' + ParticipantsMetadata = strcat(FetalBrainModelPath, '202303-FeTA2021_Release1and2Corrected_v4-Replicate_OHBM_training_set.xlsx'); + Participants = readtable(ParticipantsMetadata, 'Sheet', 'data2sim_1', 'Range', 'A1:F11'); + index = string(Participants{:,1})==SubID; + % Gestational age (in weeks) + GA = round(Participants{index,3}); + % Session ID +% ses_id = unique(participants{index,5}); +end + +% Load segmented high-resolution anatomical MR images of the fetal brain at +% gestational age GA +[FetalBrain, ModelNiiinfo] = brain_model(FetalBrainModelPath, ... + FetalModel, ... + SubID); + +% Read the resolution of the 3D anatomical model +SimRes = ModelNiiinfo.PixelDimensions; + +% Axis direction codes for the affine orientation matrix of the anatomical +% model of the fetal brain +Affine = ModelNiiinfo.Transform.T'; +ModelAxcodes = aff2axcodes(Affine); + +% The slice thickness direction will be encoded in the 3. dimension +switch Orientation + case 1 %sagittal: slice thickness L-R + SliceDir_idx = find(ismember(ModelAxcodes, ["R","L"])); + InPlane_idx = find(ismember(ModelAxcodes, ["A","P","S","I"])); + case 2 %coronal: slice thickness A-P + SliceDir_idx = find(ismember(ModelAxcodes, ["A","P"])); + InPlane_idx = find(ismember(ModelAxcodes, ["R","L","S","I"])); + case 3 %axial: slice thickness S-I + SliceDir_idx = find(ismember(ModelAxcodes, ["S","I"])); + InPlane_idx = find(ismember(ModelAxcodes, ["R","L","A","P"])); +end +AxcodesReo = ModelAxcodes([InPlane_idx SliceDir_idx]); + +% Reorient the original 3D anatomical model of the fetal brain so that the +% slice thickness direction is encoded in the 3. dimension +[FetalBrainReo, ... + AffineReo, ... + SimResReo] = reorient_volume(FetalBrain, ... + Affine, ... + SimRes, ... + AxcodesReo); + +% Convert the shift variable in the slice thickness direction into a number +% of voxels +Shift = round(Shift_mm / SimResReo(3)); %in voxels + +% Define the slice slab that covers the fetal brain volume after shifting +% the field-of-view in the slice thickness direction +FetalBrainFOV = FOV_shift(FetalBrainReo, Shift, Orientation); + +% Update the orientation matrix of the modified fetal brain model +AffineFOV = update_affine( FetalBrainReo, ... + AffineReo, ... + FetalBrainFOV, ... + AffineReo(1:3,1:3)); + +% Load the intensity non-uniformity fields: the third dimension corresponds +% to the slice thickness orientation +B1Map = brainWeb_inu(INU, FetalBrain); + +% Apply the same FOV shift to the b1 map as to the fetal brain model +B1MapFOV = FOV_shift(B1Map, Shift, Orientation); + +% Reorient the b1 map to match the orientation of the slice slab +[B1Map_reo, ~] = reorient_volume( B1MapFOV, ... + Affine, ... + SimRes, ... + AxcodesReo); + +% Original anatomical models may be up-sampled in the slice thickness +% direction in order to ensure generalizability in the choice of the slice +% thickness, slice gap, etc. +SubunitRes = SimResReo(3) / SamplingFactor; %mm +% if mod(SliceThickness,SubunitRes)>1e-4 +% error('The subunit resolution (%.1f mm) must be a multiple of the slice thickness, i.e., %.1f mm.\n', SubunitRes, SliceThickness); +% end + +FetalBrainUpsampled = sampling_OoP( FetalBrainFOV, ... + SamplingFactor, ... + 'nearest'); + +% Update the orientation matrix of the modified fetal brain model +AffineUpsampled = update_affine( FetalBrainFOV, ... + AffineFOV, ... + FetalBrainUpsampled, ... + AffineFOV(1:3,1:3)); + +B1MapUpsampled = sampling_OoP( B1Map_reo, ... + SamplingFactor, ... + 'linear'); + +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +% Conversion to MR contrast % +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% + +% Consider only non-zero labels +FetalBrainLabels = unique(FetalBrain); +FetalBrainLabels = FetalBrainLabels(FetalBrainLabels > 0); +% Write labels of the different segmented brain tissues in a list +FetalBrainTissues = permute(FetalBrainLabels, [2,1]); + +% Define the interpolation method used to upsample the partial volume maps +% of the WM mask. A bilinear interpolation significantly increases the +% computational burden of the EPG simulations. Therefore, a +% nearest-neighbor interpolation is used to simulate many subjects in a row +InterpolationMethod = 'nearest'; +% Generate reference T1 and T2 maps of the fetal brain with the third +% dimension being the slice thickness direction +[RefT1map, RefT2map] = tissue_to_MR( FetalModel, ... + FetalBrainUpsampled, ... + FetalBrainTissues, ... + GA, ... + SubID, ... + WMheterogeneity, ... + Affine, ... + SimRes, ... + AxcodesReo, ... + Shift, ... + Orientation, ... + SamplingFactor, ... + InterpolationMethod, ... + T1_WM, ... + T2_WM, ... + T1_GM, ... + T2_GM, ... + T1_CSF, ... + T2_CSF); + +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +% Extended Phase Graph (EPG) simulations % +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% + +% Run EPG simulations in every voxel of the fetal brain volume +T2decay = compute_t2decay(FetalBrainUpsampled, ... + B1MapUpsampled, ... + RefT1map, ... + RefT2map, ... + ETL, ... + FlipAngle, ... + ESP, ... + SamplingFactor); + +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +% Parameters of fetal brain FSE acquisition schemes % +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% + +% Calculate effective FOVPhase based on PhaseOversampling +FOVPhaseOversampling = FOVPhase * (1 + PhaseOversampling); + +% Rounding of the number of phase-encoding lines +nPE = round(PhaseResolution * BaseResolution * (1 + PhaseOversampling)); +% For Siemens, it probably needs to be rounded to integer values of 2 +if mod(nPE,2)~=0 + nPE = nPE + 1; +end + +% Calculate a gaussian slice profile as estimated in the SR recon pipeline: +% a Gaussian function with the full width at half maximum equal to the +% slice thickness in the slice-select direction. +SliceProfile = gausswin(round(SliceThickness/SubunitRes), 2*sqrt(2*log(2))/(SliceThickness/SimResReo(3))); + +% No slice profile in between slices +Sl_to_Sl = SliceProfile; +Sl_to_Sl(length(SliceProfile)+1:length(SliceProfile)+round(SliceGap/SubunitRes)) = ones(round(SliceGap/SubunitRes),1); + +% The number of slices is currently set to ensure that the maximum size in +% mm of the image matrix is an integer number of the slice thickness + +% slice gap +NbSlices = ceil(max([size(T2decay,1), size(T2decay,2), size(T2decay,3)/SamplingFactor]) * SimResReo(3) / SubunitRes / length(Sl_to_Sl)); +% Corresponding matrix size +T2decayMaxDim = ceil(NbSlices * length(Sl_to_Sl) / (SimResReo(3) / SubunitRes)); + +% Zero-padding of the T2decay array so that it is 3D isotropic - This makes +% some calculations below a bit easier +T2decayZP = Resize_Volume(T2decay, [T2decayMaxDim, T2decayMaxDim, T2decayMaxDim*SamplingFactor, size(T2decay,4)]); +% There might be a couple of slices that are just zeros due to this +% zero-padding step. + +% In the same way, zero-padding of the segmented fetal brain images after +% upsampling +FetalBrainZP = Resize_Volume(FetalBrainUpsampled, [T2decayMaxDim, T2decayMaxDim, T2decayMaxDim*SamplingFactor]); + +% Update the orientation matrix of the modified fetal brain model while +% taking into account the deviation from the center of the anatomical model +% of the fetal brain which may result from this zero-padding step +CenterOffset = [0 0 0]; +if mod(size(FetalBrainZP,1)-size(FetalBrainUpsampled,1),2)~=0 + if size(FetalBrainZP,1)>size(FetalBrainUpsampled,1) + CenterOffset(1) = 1; %The center of the volume is updated by '+1' voxel in this direction because we zero-padd the volume, meaning the final size is larger than the original one + else + CenterOffset(1) = -1; + end +end +if mod(size(FetalBrainZP,2)-size(FetalBrainUpsampled,2),2)~=0 + if size(FetalBrainZP,2)>size(FetalBrainUpsampled,2) + CenterOffset(2) = 1; + else + CenterOffset(2) = -1; + end +end +if mod(size(FetalBrainZP,3)-size(FetalBrainUpsampled,3),2)~=0 + if size(FetalBrainZP,3)>size(FetalBrainUpsampled,3) + CenterOffset(3) = 1; + else + CenterOffset(3) = -1; + end +end +AffineZP = update_affine( FetalBrainUpsampled, ... + AffineUpsampled, ... + FetalBrainZP, ... + AffineUpsampled(1:3,1:3), ... + 'CenterOffset', CenterOffset); + +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +% K-space sampling of the simulated FSE images % +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% + +% Data sampling follows an interleaved acquisition scheme +InterleavedSlices_index = interleaved_scheme(NbSlices); + +[ MotionCorruptedSlices, ... + TranslationDisplacement, ... + RotationAngle, ... + RotationAxis] = motion_transform(MotionLevel, ... + NbSlices); + +[KSpace, SlVolume] = Kspace_sampling( T2decayZP, ... + FetalBrainFOV, ... + FetalModel, ... + B1MapUpsampled, ... + FetalBrainTissues, ... + SimResReo, ... + SamplingFactor, ... + MotionCorruptedSlices, ... + TranslationDisplacement, ... + RotationAngle, ... + RotationAxis, ... + 'nearest', ... + ETL, ... + FlipAngle, ... + ESP, ... + TEeff, ... + TR, ... + NbSlices, ... + InterleavedSlices_index, ... + Sl_to_Sl, ... + SliceProfile, ... + SliceThickness, ... + FOVRead, ... + FOVPhaseOversampling, ... + BaseResolution, ... + nPE, ... + ACF, ... + RefLines, ... + GA, ... + SubID, ... + WMheterogeneity, ... + Affine, ... + SimRes, ... + AxcodesReo, ... + Shift, ... + Orientation, ... + T1_WM, ... + T2_WM, ... + T1_GM, ... + T2_GM, ... + T1_CSF, ... + T2_CSF); + +SqueezeSlVolume = squeeze(SlVolume(:,:,:,30)); +SlVolume_resized = Resize_Volume(SqueezeSlVolume, [round(FOVRead/SimResReo(1)), round(FOVPhaseOversampling/SimResReo(2)), NbSlices]); + +% Update the orientation matrix of the modified fetal brain model while +% taking into account the deviation from the center of the anatomical model +% of the fetal brain which may result from this zero-padding step +CenterOffset = [0 0 0]; +if mod(size(SlVolume_resized,1)-size(SqueezeSlVolume,1),2)~=0 + if size(SlVolume_resized,1)>size(SqueezeSlVolume,1) + CenterOffset(1) = 1; + else + CenterOffset(1) = -1; + end +end +if mod(size(SlVolume_resized,2)-size(SqueezeSlVolume,2),2)~=0 + if size(SlVolume_resized,2)>size(SqueezeSlVolume,2) + CenterOffset(2) = 1; + else + CenterOffset(2) = -1; + end +end +if mod(size(SlVolume_resized,3)-size(SqueezeSlVolume,3),2)~=0 + if size(SlVolume_resized,3)>size(SqueezeSlVolume,3) + CenterOffset(3) = 1; + else + CenterOffset(3) = -1; + end +end +% Voxel size of the simulated images +SimVoxSize = [FOVRead/ReconMatrix (FOVPhaseOversampling/(1+PhaseOversampling))/ReconMatrix SliceThickness+SliceGap]; +% Initialize the rotation matrix +RotationsSlVolume = (AffineZP(1:3, 1:3) ./ SimResReo) .* SimVoxSize; +AffineSlVolume = update_affine( FetalBrainZP, ... + AffineZP, ... + SlVolume_resized, ... + RotationsSlVolume, ... + 'CenterOffset', CenterOffset); + +% Automatically propagate labels of the simulated images +SimLabels = auto_segmentation( FetalBrainZP, ... + FetalBrainReo, ... + SimResReo, ... + SamplingFactor, ... + SliceThickness, ... + SubunitRes, ... + FOVRead, ... + FOVPhaseOversampling, ... + NbSlices, ... + InterleavedSlices_index, ... + Sl_to_Sl, ... + MotionCorruptedSlices, ... + TranslationDisplacement, ... + RotationAngle, ... + RotationAxis); + +% Reduce the labels from Gholipour atlas to 6 main labels +switch FetalModel + case 'STA' + % Group structures and assign them the same labels as in the FeTA dataset + + % merge extra-axial CSF space (class 1) and intra-cranial CSF, ventricles + % system (class 4) into one single class (class 1) + % 0 Background: [0] + % 1 Extra-axial CSF space: [124] + % 2 Gray matter: [37,38,41,42,112,113] + % NB-FeTA annotation guidelines: "The label includes the amygdala and + % hippocampus, but does not include any infratentorial structures." + % 3 White matter: [91,110,111,114,115,116,117,118,119,120,121,125] + % 4 Ventricular system: [92,93] + % 5 Cerebellum: [100,101] + % 6 Deep gray matter: [71,72,73,74,77,78,108,109,122,123] + % NB-FeTA annotation guidelines: "The internal capsule is within this + % label, as it is often not possible to separate due to low contrast in the + % 3D reconstructed T2 images. + % 7 Brainstem: [94] + SimLabels(SimLabels(:)==124) = 1; + SimLabels(SimLabels(:)==37|SimLabels(:)==38|SimLabels(:)==41|SimLabels(:)==42|SimLabels(:)==112|SimLabels(:)==113) = 2; + SimLabels(SimLabels(:)==91|SimLabels(:)==110|SimLabels(:)==111|SimLabels(:)==114|SimLabels(:)==115|SimLabels(:)==116|SimLabels(:)==117|SimLabels(:)==118|SimLabels(:)==119|SimLabels(:)==120|SimLabels(:)==121|SimLabels(:)==125) = 3; + SimLabels(SimLabels(:)==92|SimLabels(:)==93) = 1; + SimLabels(SimLabels(:)==100|SimLabels(:)==101) = 5; + SimLabels(SimLabels(:)==71|SimLabels(:)==72|SimLabels(:)==73|SimLabels(:)==74|SimLabels(:)==77|SimLabels(:)==78|SimLabels(:)==108|SimLabels(:)==109|SimLabels(:)==122|SimLabels(:)==123) = 6; + SimLabels(SimLabels(:)==94) = 7; +end + +% Simulate scanner zero-interpolation filling (ZIP) +if ZIP==1 %ZIP + KSpace = zip_kspace( KSpace, ... + [size(KSpace,1)*ReconMatrix/BaseResolution size(KSpace,2)*ReconMatrix/BaseResolution size(KSpace,3)]); +end + +% Add complex Gaussian noise to K-space +KSpaceNoise = add_noise(KSpace, SDnoise); + +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +% Final simulated FSE images % +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% + +% Turn back data in K-space to the image space +ComplexFSEimages = imresize(ifft2c(KSpaceNoise), [size(KSpace,1), round(size(KSpace,2)/PhaseResolution)]); + +% Histogram normalization between 0 and 255 of simulated SS-FSE images +NormFSEimages = ComplexFSEimages * 255 / max(abs(ComplexFSEimages(:))); + +% Only keep the magnitude image +FSEimages = abs(NormFSEimages); + +% Associated label map +LabelMap = imresize(SimLabels, [size(KSpace,1), round(size(KSpace,2)/PhaseResolution)], 'nearest'); + +% Resize the simulated images to the desired dimensions (especially, do not +% reconstruct the phase oversampling) +AcqDim = [ReconMatrix ReconMatrix NbSlices]; +if any(size(FSEimages))~=AcqDim + ReconFSEimages = Resize_Volume(FSEimages, AcqDim); + ReconLabelMap = Resize_Volume(LabelMap, AcqDim); +else + ReconFSEimages = FSEimages; + ReconLabelMap = LabelMap; +end + +% Initialize the orientation matrix of the simulated images +% NewRotations = (Affine_SlVolume(1:3, 1:3) ./ SimResReo) .* SimVoxSize; +% Update the affine matrix of the modified fetal brain model while taking +% into account any deviation from the center of the anatomical model of the +% fetal brain which may result from zero-padding when sampling K-space +CenterOffset = [0 0 0]; +if mod(AcqDim(1)-size(SlVolume_resized,1),2)~=0 + if AcqDim(1)>size(SlVolume_resized,1) + CenterOffset(1) = 1; + else + CenterOffset(1) = -1; + end +end +if mod(AcqDim(2)-size(SlVolume_resized,2),2)~=0 + if AcqDim(2)>size(SlVolume_resized,2) + CenterOffset(2) = 1; + else + CenterOffset(2) = -1; + end +end +if mod(AcqDim(3)-size(SlVolume_resized,3),2)~=0 + if AcqDim(3)>size(SlVolume_resized,3) + CenterOffset(3) = 1; + else + CenterOffset(3) = -1; + end +end +ReconAffine = update_affine( SlVolume_resized, ... + AffineSlVolume, ... + ReconFSEimages, ... + AffineSlVolume(1:3,1:3), ... + 'CenterOffset', CenterOffset); + +% Write nifti header of the simulated images with the slice thickness +% encoded in the third dimension +ReconNiiinfo = ModelNiiinfo; +ReconNiiinfo.Transform.T = ReconAffine'; +ReconNiiinfo.TransformName = "Sform"; +ReconNiiinfo.ImageSize = size(ReconFSEimages); +ReconNiiinfo.PixelDimensions = [sqrt(sum(ReconAffine(1:3,1).^2)) sqrt(sum(ReconAffine(1:3,2).^2)) sqrt(sum(ReconAffine(1:3,3).^2))]; +if isa(ReconFSEimages, ModelNiiinfo.Datatype)==0 + ReconNiiinfo.Datatype = class(ReconFSEimages); +end + +% Save the simulated images after reorientation (i.e., the slice thickness +% is encoded in the third dimension as for clinical acquisitions) in nifti +% format +niftiwrite(ReconFSEimages, OutputImReo, ReconNiiinfo, 'Compressed', true); + +% Generate a binary mask from the reconstructed label map +ReconBinaryMask = ReconLabelMap; +ReconBinaryMask(ReconBinaryMask(:)~=0) = 1; + +% Save corresponding binary masks and label maps after reorientation (i.e., +% the slice thickness is encoded in the third dimension as for clinical +% acquisitions) in nifti format +MaskNiiinfo = ReconNiiinfo; +if isa(ReconLabelMap, ReconNiiinfo.Datatype)==0 + MaskNiiinfo.Datatype = class(ReconLabelMap); +end +niftiwrite(ReconLabelMap, OutputLabelsReo, MaskNiiinfo, 'Compressed', true); +niftiwrite(ReconBinaryMask, OutputMaskReo, MaskNiiinfo, 'Compressed', true); + +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +% Depending on the application, resampling of the simulated images % +% might be needed % +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% + +switch SimResampling + case 'true' + % Downsample the simulated SS-FSE images, the corresponding label map and + % mask in the in-plane direction to match the in-plane resolution of the + % clinical SR reconstructions + ResamplingRead = SimResReo(1) / (FOVRead/ReconMatrix); + ResamplingPhase = SimResReo(2) / (FOVPhase/ReconMatrix); + % Resample simulated images and derivatives + ResReconImages = resampling_inplane( ReconFSEimages, ... + ResamplingRead, ... + ResamplingPhase, ... + 'linear'); + ResReconLabels = resampling_inplane( ReconLabelMap, ... + ResamplingRead, ... + ResamplingPhase, ... + 'nearest'); + % Generate a binary mask from the reconstructed label map + ResReconMask = ResReconLabels; + ResReconMask(ResReconMask(:)~=0) = 1; + % Voxel size of the reconstructed simulated images + SimVoxSize = [(FOVRead/ReconMatrix)*ResamplingRead (FOVPhaseOversampling/(1+PhaseOversampling))/ReconMatrix*ResamplingPhase SliceThickness+SliceGap]; + % Initialize the orientation matrix of the simulated images + NewRotations = ReconAffine(1:3,1:3) .* [ResamplingRead ResamplingPhase 1]; + % Update the orientation matrix of the modified fetal brain model + CenterOffset = [0 0 0]; + if mod(size(ResReconImages,1)-size(ReconFSEimages,1),2)~=0 + if size(ResReconImages,1)>size(ReconFSEimages,1) + CenterOffset(1) = 1; + else + CenterOffset(1) = -1; + end + end + if mod(size(ResReconImages,2)-size(ReconFSEimages,2),2)~=0 + if size(ResReconImages,2)>size(ReconFSEimages,2) + CenterOffset(2) = 1; + else + CenterOffset(2) = -1; + end + end + if mod(size(ResReconImages,3)-size(ReconFSEimages,3),2)~=0 + if size(ResReconImages,3)>size(ReconFSEimages,3) + CenterOffset(3) = 1; + else + CenterOffset(3) = -1; + end + end + ResReconAffine = update_affine( ReconFSEimages, ... + ReconAffine, ... + ResReconImages, ... + NewRotations, ... + 'CenterOffset', CenterOffset); + % Save the resampled, reconstructed simulated images after + % reorientation (i.e., the slice thickness is encoded in the third + % dimension as for clinical acquisitions) in nifti format + ResReconNiiinfo = ModelNiiinfo; + ResReconNiiinfo.Transform.T = ResReconAffine'; + ResReconNiiinfo.TransformName = "Sform"; + ResReconNiiinfo.ImageSize = size(ResReconImages); + ResReconNiiinfo.PixelDimensions = [sqrt(sum(ResReconAffine(1:3,1).^2)) sqrt(sum(ResReconAffine(1:3,2).^2)) sqrt(sum(ResReconAffine(1:3,3).^2))]; + if isa(ResReconImages, ModelNiiinfo.Datatype)==0 + ResReconNiiinfo.Datatype = class(ResReconImages); + end + niftiwrite(ResReconImages, OutputImResampled, ResReconNiiinfo, 'Compressed', true); + ResMaskNiiinfo = ResReconNiiinfo; + if isa(ResReconLabels, ResReconNiiinfo.Datatype)==0 + ResMaskNiiinfo.Datatype = class(ResReconLabels); + end + niftiwrite(ResReconLabels, OutputLabelsResampled, ResMaskNiiinfo, 'Compressed', true); + niftiwrite(ResReconMask, OutputMaskResampled, ResMaskNiiinfo, 'Compressed', true); + case 'false' +% % Voxel size of the simulated images +% SimVoxSize = [FOVRead/ReconMatrix (FOVPhase_oversampling/(1+PhaseOversampling))/ReconMatrix SliceThickness+SliceGap]; + ResReconAffine = ReconAffine; + ResReconImages = ReconFSEimages; + ResReconLabels = ReconLabelMap; + ResReconMask = ReconBinaryMask; +end + +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +% Save the final simulated FSE images in the same orientation and space % +% coordinates as the original anatomical model % +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% + +% Come back to the original fetal brain model space coordinates +[SimFSEImages, SimAffineModelAxcodes, ~] = reorient_volume(ResReconImages, ... + ResReconAffine, ... + SimResReo, ... + ModelAxcodes); +[SimLabelMap, ~] = reorient_volume(ResReconLabels, ... + ReconAffine, ... + SimResReo, ... + ModelAxcodes); +[SimBinaryMask, ~] = reorient_volume( ResReconMask, ... + ReconAffine, ... + SimResReo, ... + ModelAxcodes); +% sim_axcodes = aff2axcodes(affine_reo); + +% Write nifti header of the simulated images after reorientation in the +% original coordinate system of the anatomical model +SimModelNiiinfo = ModelNiiinfo; +SimModelNiiinfo.Transform.T = SimAffineModelAxcodes'; +% SimModelNiiinfo.TransformName = "Qform"; +SimModelNiiinfo.TransformName = "Sform"; +SimModelNiiinfo.ImageSize = size(SimFSEImages); +SimModelNiiinfo.PixelDimensions = [sqrt(sum(SimAffineModelAxcodes(1:3, 1).^2)) sqrt(sum(SimAffineModelAxcodes(1:3, 2).^2)) sqrt(sum(SimAffineModelAxcodes(1:3, 3).^2))]; +if isa(SimFSEImages, ModelNiiinfo.Datatype)==0 + SimModelNiiinfo.Datatype = class(SimFSEImages); +end + +% Save simulated images in nifti format, in the same orientation and space +% coordinates as the original anatomical model +niftiwrite(SimFSEImages, OutputIm, SimModelNiiinfo, 'Compressed', true); +% Save corresponding binary masks and label maps in nifti format, in the +% same orientation and space coordinates as the original anatomical model +SimMaskNiiinfo = SimModelNiiinfo; +if isa(SimLabelMap, SimModelNiiinfo.Datatype)==0 + SimMaskNiiinfo.Datatype = class(SimLabelMap); +end +niftiwrite(SimLabelMap, OutputLabels, SimMaskNiiinfo, 'Compressed', true); +niftiwrite(SimBinaryMask, OutputMask, SimMaskNiiinfo, 'Compressed', true); + +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +% Optional: Crop the simulated LR series to the same size as the % +% original anatomical model (after reorientation, i.e., the slice % +% thickness is encoded in the 3. dimension) and save them as nifti % +% after updating the nifti header information % +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% + +if SimCrop=="true" + % Crop the simulated images and corresponding derivatives + CropFSEimages = Resize_Volume(ResReconImages, size(FetalBrainReo)); + CropLabels = Resize_Volume(ResReconLabels, size(FetalBrainReo)); + CropMask = Resize_Volume(ResReconMask, size(FetalBrainReo)); + % Update the orientation matrix of the modified fetal brain model to + % account for this additional zero-padding step + CenterOffset = [0 0 0]; + if mod(size(CropFSEimages,1)-size(ResReconImages,1),2)~=0 + if size(CropFSEimages,1)>size(ResReconImages,1) + CenterOffset(1) = 1; + else + CenterOffset(1) = -1; %Here, the center should be deviated by '-1' because we crop the image (i.e., the size of the new volume is smaller than the original one) + end + end + if mod(size(CropFSEimages,2)-size(ResReconImages,2),2)~=0 + if size(CropFSEimages,2)>size(ResReconImages,2) + CenterOffset(2) = 1; + else + CenterOffset(2) = -1; + end + end + if mod(size(CropFSEimages,3)-size(ResReconImages,3),2)~=0 + if size(CropFSEimages,3)>size(ResReconImages,3) + CenterOffset(3) = 1; + else + CenterOffset(3) = -1; + end + end + CropAffine = update_affine( ResReconImages, ... + ResReconAffine, ... + CropFSEimages, ... + ResReconAffine(1:3,1:3), ... + 'CenterOffset', CenterOffset); + % Write nifti header of the cropped simulated images with the slice + % thickness encoded in the third dimension + ModelCropNiiinfo = ModelNiiinfo; + ModelCropNiiinfo.Transform.T = CropAffine'; + ModelCropNiiinfo.TransformName = "Sform"; + ModelCropNiiinfo.ImageSize = size(CropFSEimages); + ModelCropNiiinfo.PixelDimensions = [sqrt(sum(CropAffine(1:3, 1).^2)) sqrt(sum(CropAffine(1:3, 2).^2)) sqrt(sum(CropAffine(1:3, 3).^2))]; + if isa(CropFSEimages, ModelNiiinfo.Datatype)==0 + ModelCropNiiinfo.Datatype = class(CropFSEimages); + end + % Save the simulated cropped images after reorientation (i.e., the + % slice thickness is encoded in the third dimension as for clinical + % acquisitions) in nifti format + niftiwrite(CropFSEimages, OutputImCrop, ModelCropNiiinfo, 'Compressed', true); + % Save the corresponding binary mask and label map in nifti format + CropMaskNiiinfo = ModelCropNiiinfo; + if isa(CropLabels, ModelCropNiiinfo.Datatype)==0 + CropMaskNiiinfo.Datatype = class(CropLabels); + end + niftiwrite(CropLabels, OutputLabelsCrop, CropMaskNiiinfo, 'Compressed', true); + niftiwrite(CropMask, OutputMaskCrop, CropMaskNiiinfo, 'Compressed', true); +end + +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +% Save raw data % +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% + +% save(strcat(output_folder, 'KSpace'), 'KSpace', '-v7.3'); +% save(strcat(output_folder, 'KSpace_noise'), 'KSpace_noise', '-v7.3'); +% save(strcat(output_folder, 'Fetal_Brain_FSE_Images'), 'FSE_Images', '-v7.3'); +% save(strcat(output_folder, 'Label_Map_recon'), 'Label_Map_recon', '-v7.3'); +% save(strcat(output_folder, 'Motion_Transforms'), 'motion_corrupted_slices', 'translation_displacement', 'rotation_angle', 'rotation_axis'); + +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +% Save the parameters of the MR acquisition simulated % +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% + +% Save simulation parameters +save('sim_parameters.mat', 'SubID', ... + 'SesID', ... + 'RunID', ... + 'FetalModel', ... + 'Orientation', ... + 'Shift_mm', ... + 'INU', ... + 'SamplingFactor', ... + 'B0', ... + 'ESP', ... + 'ETL', ... + 'PhaseOversampling', ... + 'SliceThickness', ... + 'SliceGap', ... + 'FOVRead', ... + 'FOVPhase', ... + 'FOVPhaseOversampling', ... + 'BaseResolution', ... + 'PhaseResolution', ... + 'TR', ... + 'TEeff', ... + 'FlipAngle', ... + 'ACF', ... + 'RefLines', ... + 'MotionLevel', ... + 'ZIP', ... + 'ReconMatrix', ... + 'SimResampling', ... + 'SimVoxSize', ... + 'SDnoise', ... + 'WMheterogeneity') + +if SimResampling=="true" + save('resampling.mat', 'ResamplingRead', 'ResamplingPhase') +end + +% Write simulations parameters in a .csv file +data = load('sim_parameters.mat'); +param = fieldnames(data); +% writecell(horzcat(param, struct2cell(data)), strcat(output_folder, char(sub_id), '/ses-', sprintf('%02s', num2str(ses_id)), '/anat/', char(sub_id), '_ses-', sprintf('%02s', num2str(ses_id)), '_run-', num2str(run_id), '_sim_parameters.csv')) +writecell(horzcat(param, struct2cell(data)), strcat(OutputPath, num2str(SubID), '_ses-', sprintf('%02s', num2str(SesID)), '_run-', num2str(RunID), '_sim_parameters.csv')) +% writecell(horzcat(param, struct2cell(data)), strcat(output_folder, 'data_reo/', num2str(sub_id), '/ses-', sprintf('%02s', num2str(ses_id)), '/anat/', num2str(sub_id), '_ses-', sprintf('%02s', num2str(ses_id)), '_run-', num2str(run_id), '_sim_parameters.csv')) + +end \ No newline at end of file diff --git a/Utilities/FOV_shift.m b/Utilities/FOV_shift.m index 8994c82..c562832 100644 --- a/Utilities/FOV_shift.m +++ b/Utilities/FOV_shift.m @@ -1,53 +1,53 @@ -%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% -% Function that redefines the position of the slice slab that covers % -% the fetal brain volume based on the shift that is applied in the % -% slice thickness direction between the acquisition of two % -% low-resolution series in the same orientation (as it is done in % -% clinical routine in the case of two successive series acquired in the % -% same plane). % -% % -% Fetal_Brain_shift = FOV_shift(Fetal_Brain, shift, orientation); % -% % -% inputs: - Fetal_Brain: segmented high-resolution 3D volume of the % -% fetal brain % -% - shift: displacement (in voxels) of the slice slab in the % -% slice thickness direction % -% - orientation: strict acquisition plane (axial, coronal or % -% sagittal) % -% % -% output: - Fetal_Brain_shift: segmented high-resolution 3D volume of % -% the fetal brain after a shift in the % -% slice thickness direction % -% % -% % -% Hélène Lajous, 2021-08-23 % -% helene.lajous@unil.ch % -% % -%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% - - -function Fetal_Brain_shift = FOV_shift(Fetal_Brain, ... - shift, ... - orientation) - -% Input check -if nargin < 3 - error('Missing input(s).'); -elseif nargin > 3 - error('Too many inputs.'); -end - -% Resize the fetal brain volume to allow shifting in any direction -Fetal_Brain_FOV = {1+ceil(abs(shift)):size(Fetal_Brain,1)-ceil(abs(shift)); 1+ceil(abs(shift)):size(Fetal_Brain,2)-ceil(abs(shift)); 1+ceil(abs(shift)):size(Fetal_Brain,3)-ceil(abs(shift))}; - -% Slightly shift the slice slab in the slice thickness direction (as done -% in the clinics) -Fetal_Brain_FOV{orientation} = Fetal_Brain_FOV{orientation} + shift; - -% Mask the fetal brain after having shifted the slice slab -Fetal_Brain_shift = Fetal_Brain(Fetal_Brain_FOV{1}, Fetal_Brain_FOV{2}, Fetal_Brain_FOV{3}); - -% Display message for debugging -sprintf('The fetal brain volume has been shifted by %d voxels in the slice thickness direction.', shift) - +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +% Function that redefines the position of the slice slab that covers % +% the fetal brain volume based on the shift that is applied in the % +% slice thickness direction between the acquisition of two % +% low-resolution series in the same orientation (as it is done in % +% clinical routine in the case of two successive series acquired in the % +% same plane). % +% % +% Fetal_Brain_shift = FOV_shift(Fetal_Brain, shift, orientation); % +% % +% inputs: - Fetal_Brain: segmented high-resolution 3D volume of the % +% fetal brain % +% - shift: displacement (in voxels) of the slice slab in the % +% slice thickness direction % +% - orientation: strict acquisition plane (axial, coronal or % +% sagittal) % +% % +% output: - Fetal_Brain_shift: segmented high-resolution 3D volume of % +% the fetal brain after a shift in the % +% slice thickness direction % +% % +% % +% Hélène Lajous, 2021-08-23 % +% helene.lajous@unil.ch % +% % +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% + + +function Fetal_Brain_shift = FOV_shift(Fetal_Brain, ... + shift, ... + orientation) + +% Input check +if nargin < 3 + error('Missing input(s).'); +elseif nargin > 3 + error('Too many inputs.'); +end + +% Resize the fetal brain volume to allow shifting in any direction +Fetal_Brain_FOV = {1+ceil(abs(shift)):size(Fetal_Brain,1)-ceil(abs(shift)); 1+ceil(abs(shift)):size(Fetal_Brain,2)-ceil(abs(shift)); 1+ceil(abs(shift)):size(Fetal_Brain,3)-ceil(abs(shift))}; + +% Slightly shift the slice slab in the slice thickness direction (as done +% in the clinics) +Fetal_Brain_FOV{orientation} = Fetal_Brain_FOV{orientation} + shift; + +% Mask the fetal brain after having shifted the slice slab +Fetal_Brain_shift = Fetal_Brain(Fetal_Brain_FOV{1}, Fetal_Brain_FOV{2}, Fetal_Brain_FOV{3}); + +% Display message for debugging +sprintf('The fetal brain volume has been shifted by %d voxels in the slice thickness direction.', shift) + end \ No newline at end of file diff --git a/Utilities/Kspace_sampling.m b/Utilities/Kspace_sampling.m index 794f691..6076eed 100644 --- a/Utilities/Kspace_sampling.m +++ b/Utilities/Kspace_sampling.m @@ -1,262 +1,346 @@ -%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% -% Function that samples the Fourier domain (K-space) of the simulated % -% images according to the acquisition scheme implemented in Fast Spin % -% Echo (FSE) sequences. % -% % -% KSpace = Kspace_sampling( T2decay_zp, ... % -% Fetal_Brain, ... % -% b1map_upsampled, ... % -% Fetal_Brain_Tissues, ... % -% SimRes, ... % -% sampling_factor, ... % -% B0, ... % -% motion_corrupted_slices, ... % -% translation_displacement, ... % -% rotation_angle, ... % -% rotation_axis, ... % -% interpolation_method_upsampling, ... % -% ETL, ... % -% ESP, ... % -% TEeff, ... % -% TR, ... % -% NbSlices, ... % -% interleavedSlices_index, ... % -% Sl_to_Sl, ... % -% SliceProfile, ... % -% SliceThickness, ... % -% FOVRead, ... % -% FOVPhase, ... % -% BaseResolution, ... % -% nPE, ... % -% ACF, ... % -% RefLines); % -% % -% inputs: - T2decay_zp: 4D matrix that combines the anatomical % -% information from the segmented high-resolution % -% fetal brain volume and the T2 decay computed % -% in every voxel of this volume after zero- % -% -padding % -% - Fetal_Brain: segmented high-resolution 3D volume of the % -% fetal brain % -% - b1map_upsampled: B1+ bias field map resampled by the % -% sampling_factor in the slice thickness % -% direction % -% - Fetal_Brain_Tissues: list of tissues in the fetal brain % -% that were segmented and labeled % -% - SimRes: resolution of the original high-resolution fetal % -% brain images (isotropic, in mm) % -% - sampling_factor: factor of up- or down-sampling % -% - B0: main magnetic field strength % -% - motion_corrupted_slices: list of indexes of the slices % -% corrupted by motion % -% - translation_displacement: table of translation % -% displacements (in mm) along the % -% three main axes for each motion- % -% -corrupted slice % -% - rotation_angle: list of rotation angles (in °) for 3D % -% rotation in every motion-corrupted slice % -% - rotation_axis: table of rotation axes for 3D rotation in % -% every motion-corrupted slice % -% - interpolation_method_upsampling: interpolation method used % -% to assign a value to % -% every voxel of the 3D % -% volume after resampling % -% - ETL: echo train length, i.e. number of 180°-RF pulses % -% - ESP: echo spacing (in ms) % -% - TEeff: effective echo time (in ms) % -% - TR: time from the application of an excitation pulse to % -% the application of the next pulse (i.e., echo spacing % -% in the EPG simulations) (in ms) % -% - NbSlices: number of slices % -% - interleavedSlices_index: list of indexes of the slices % -% corresponding to an interleaved % -% acquisition scheme % -% - Sl_to_Sl: slice profile, including slice gap if any % -% - SliceProfile: Gaussian slice profile % -% - SliceThickness: thickness of a slice (in mm) % -% - FOVRead: dimension of the field-of-view in the read-out % -% direction (in mm) % -% - FOVPhase: dimension of the field-of-view in the phase % -% direction (in mm) % -% - BaseResolution: matrix size in the read-out direction (in % -% voxels) % -% - nPE: number of phase encoding lines % -% - ACF: acceleration factor % -% - RefLines: number of lines that are consecutively sampled % -% around the center of K-space % -% % -% output: - KSpace: Fourier domain of the simulated images % -% % -% % -% Hélène Lajous, 2021-04-21 % -% helene.lajous@unil.ch % -% % -%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% - - -function KSpace = Kspace_sampling( T2decay_zp, ... - Fetal_Brain, ... - b1map_upsampled, ... - Fetal_Brain_Tissues, ... - SimRes, ... - sampling_factor, ... - B0, ... - motion_corrupted_slices, ... - translation_displacement, ... - rotation_angle, ... - rotation_axis, ... - interpolation_method_upsampling, ... - ETL, ... - ESP, ... - TEeff, ... - TR, ... - NbSlices, ... - interleavedSlices_index, ... - Sl_to_Sl, ... - SliceProfile, ... - SliceThickness, ... - FOVRead, ... - FOVPhase, ... - BaseResolution, ... - nPE, ... - ACF, ... - RefLines) - -% Input check -if nargin < 27 - error('Missing input(s).'); -elseif nargin > 27 - error('Too many inputs.'); -end - -% Memory pre-allocation -Sl_Volume = zeros(size(T2decay_zp,1), size(T2decay_zp,2), NbSlices, size(T2decay_zp,4)); - -% Preload K-Space array which has dimensions of number of lines, number of -% Phase Encoding directions, and number of Slices -KSpace = zeros(BaseResolution, nPE, NbSlices, 'single'); - -% Create a "SLAB" array which is essentially the K-Space of our -% multislice volume -SLAB = zeros([BaseResolution, nPE, NbSlices, size(T2decay_zp,4)]); - -SubunitRes = SimRes / sampling_factor; %mm - -% Computation time to sample k-space -tic - -motion_index = 0; - -% Loop through slices -for iSlice=1:length(interleavedSlices_index) - disp(['Slice ', num2str(interleavedSlices_index(iSlice)), ' of ', num2str(length(interleavedSlices_index))]) - index = interleavedSlices_index(iSlice)*length(Sl_to_Sl)-(length(Sl_to_Sl)-1); - % Inter-slice motion - - % - Random translation: uniform distribution between - % [-translation_amplitude,+translation_amplitude]mm - if any(interleavedSlices_index(iSlice)==motion_corrupted_slices) - motion_index = motion_index + 1; - Fetal_Brain_rotated = moving_3Dbrain( Fetal_Brain, ... - SimRes, ... - translation_displacement(motion_index,:), ... - 'nearest', ... - rotation_angle(motion_index), ... - rotation_axis(motion_index,:), ... - 'nearest', ... - 'crop'); - Fetal_Brain_rotated_upsampled = sampling_OoP( Fetal_Brain_rotated, ... - sampling_factor, ... - interpolation_method_upsampling); - [ref_T1map_rotated, ref_T2map_rotated] = tissue_to_MR(Fetal_Brain_rotated_upsampled, ... - Fetal_Brain_Tissues, ... - B0); - T2decay_moved = compute_t2decay(Fetal_Brain_rotated_upsampled, ... - b1map_upsampled, ... - ref_T1map_rotated, ... - ref_T2map_rotated, ... - ETL, ... - ESP, ... - sampling_factor); - T2decay_moved_zp = Resize_Volume(T2decay_moved, size(T2decay_zp)); - T2decay_zp(:,:,index:index+SliceThickness/SubunitRes-1,:) = T2decay_moved_zp(:,:,index:index+SliceThickness/SubunitRes-1,:); - % Update the T2decay_zp variable as it is unlikely that the - % fetus comes back to its initial position at iSlice+1 and - % following slices after he moved - if iSlice < length(interleavedSlices_index) - for nextSlice=iSlice+1:length(interleavedSlices_index) - nextSlice_index = interleavedSlices_index(nextSlice)*length(Sl_to_Sl)-(length(Sl_to_Sl)-1); - T2decay_zp(:,:,nextSlice_index:nextSlice_index+SliceThickness/SubunitRes-1,:) = T2decay_moved_zp(:,:,nextSlice_index:nextSlice_index+SliceThickness/SubunitRes-1,:); - end - end - clear ref_T2map_rotated - clear ref_T1map_rotated - clear Fetal_Brain_rotated - clear Fetal_Brain_rotated_upsampled - clear T2decay_moved - clear T2decay_moved_zp - end - % Multiply the 4D matrix by the slice profile - T2decay_zp(:,:,index:index+SliceThickness/SubunitRes-1,:) = T2decay_zp(:,:,index:index+SliceThickness/SubunitRes-1,:).*permute(SliceProfile, [2,3,1]); - % Sum the contribution of all voxels at the same (x,y) location across - % the slice thickness direction - Sl_Volume(:,:,interleavedSlices_index(iSlice),:) = sum(T2decay_zp(:,:,index:index+SliceThickness/SubunitRes-1,:),3); - % Simulation of k-space sampling as for FSE sequences -% tic - for iEcho=1:size(Sl_Volume,4) - clc; - disp(['Echo ', num2str(iEcho), ' of ', num2str(size(Sl_Volume,4))]) - SLAB(:,:,interleavedSlices_index(iSlice),iEcho) = Resize_Volume(fft2c(Resize_Volume(Sl_Volume(:,:,interleavedSlices_index(iSlice),iEcho), [FOVRead/SimRes, round(FOVPhase)/SimRes, NbSlices])), [BaseResolution, nPE, NbSlices]); - end -% toc -% tic - % Calculating k-space sampling based on the desired echo time occuring - % at the center of k-space - if ACF~=1 && RefLines~=0 - temp = (nPE/2-RefLines/2)+RefLines+1:ACF:nPE; - SamplingOrder = fliplr([ACF:ACF:(nPE/2-RefLines/2), (nPE/2-RefLines/2)+1:(nPE/2-RefLines/2)+RefLines, temp(1:round(TEeff/TR-RefLines/2))]); - clear temp - elseif ACF==1 && RefLines==0 - temp = nPE/2+1:ACF:nPE; - SamplingOrder = fliplr([ACF:ACF:nPE/2, temp(1:round(TEeff/TR))]); - clear temp - end - % Loop through phase encoding lines - for iLine=1:length(SamplingOrder) - KSpace(:, SamplingOrder(iLine), interleavedSlices_index(iSlice)) = SLAB(:, SamplingOrder(iLine), interleavedSlices_index(iSlice), iLine); - end - % This next block finds the missing lines and replaces them with the - % closest echo time - %Sum non-zero contributions in KSpace for slice - %interleavedSlices_index(iSlice), and echo iEcho - if sum(sum(KSpace(:,:,interleavedSlices_index(iSlice))))~=0 - %The sampled lines in KSpace for slice iSlice, and echo iEcho - %are non zero - SampledLines = find(squeeze(KSpace(1,:,interleavedSlices_index(iSlice)))~=0); - %Loop through all lines of KSpace - for iLine=1:size(KSpace,2) - %If a line was not sampled - if KSpace(1,iLine,interleavedSlices_index(iSlice))==0 - %Find only the first non-zero line following iLine - iFind = find(SampledLines>iLine,1,'first'); - %If a sampled line iFind was found following the - %non-sampled line iLine, copy this iFind line from SLAB - %to the corresponding line position in KSpace - if ~isempty(iFind) - KSpace(:,iLine,interleavedSlices_index(iSlice)) = SLAB(:,iLine,interleavedSlices_index(iSlice),SamplingOrder==SampledLines(iFind)); - else - %If no sampled line was found following the non-sampled - %line iLine, use hermitian symmetry to fill KSpace: the - %line symmetrical to iLine compared to the center of - %KSpace is size(KSpace,2)-iLine+1 - KSpace(:,iLine,interleavedSlices_index(iSlice)) = fliplr(conj(KSpace(:,size(KSpace,2)-iLine+1,interleavedSlices_index(iSlice)))')'; - end - end - end - end -% toc -end - -% Display computation time -fprintf('Computation time to sample k-space: %0.5f seconds.\n', toc); - +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +% Function that samples the Fourier domain (K-space) of the simulated % +% images according to the acquisition scheme implemented in Fast Spin % +% Echo (FSE) sequences. % +% % +% [ KSpace, ... % +% Sl_Volume] = Kspace_sampling( T2decay_zp, ... % +% Fetal_Brain, ... % +% fetal_model, ... % +% b1map_upsampled, ... % +% Fetal_Brain_Tissues, ... % +% SimResReo, ... % +% SamplingFactor, ... % +% motion_corrupted_slices, ... % +% translation_displacement, ... % +% rotation_angle, ... % +% rotation_axis, ... % +% interpolation_method_upsampling, ... % +% ETL, ... % +% flipAngle, ... % +% ESP, ... % +% TEeff, ... % +% TR, ... % +% NbSlices, ... % +% interleavedSlices_index, ... % +% Sl_to_Sl, ... % +% SliceProfile, ... % +% SliceThickness, ... % +% FOVRead, ... % +% FOVPhase, ... % +% BaseResolution, ... % +% nPE, ... % +% ACF, ... % +% RefLines, ... % +% GA, ... % +% sub_id, ... % +% WM_heterogeneity, ... % +% affine, ... % +% SimRes, ... % +% axcodes) % +% % +% inputs: - T2decay_zp: 4D matrix that combines the anatomical % +% information from the segmented high-resolution % +% fetal brain volume and the T2 decay computed % +% in every voxel of this volume after zero- % +% -padding % +% - Fetal_Brain: segmented high-resolution 3D volume of the % +% fetal brain % +% - fetal_model: anatomical model of the fetal brain % +% - b1map_upsampled: B1+ bias field map resampled by the % +% sampling_factor in the slice thickness % +% direction % +% - Fetal_Brain_Tissues: list of tissues in the fetal brain % +% that were segmented and labeled % +% - SimResReo: resolution of the original 3D high-resolution % +% anatomical model of the fetal brain after % +% reorientation (i.e., the slice thickness is % +% encoded in the third dimension) % +% - SamplingFactor: factor of up- or down-sampling % +% - motion_corrupted_slices: list of indexes of the slices % +% corrupted by motion % +% - translation_displacement: table of translation % +% displacements (in mm) along the % +% three main axes for each motion- % +% -corrupted slice % +% - rotation_angle: list of rotation angles (in °) for 3D % +% rotation in every motion-corrupted slice % +% - rotation_axis: table of rotation axes for 3D rotation in % +% every motion-corrupted slice % +% - interpolation_method_upsampling: interpolation method used % +% to assign a value to % +% every voxel of the 3D % +% volume after resampling % +% - ETL: echo train length, i.e. number of 180°-RF pulses % +% - flipAngle: refocusing flip angle (in degrees) % +% - ESP: echo spacing (in ms) % +% - TEeff: effective echo time (in ms) % +% - TR: time from the application of an excitation pulse to % +% the application of the next pulse (i.e., echo spacing % +% in the EPG simulations) (in ms) % +% - NbSlices: number of slices % +% - interleavedSlices_index: list of indexes of the slices % +% corresponding to an interleaved % +% acquisition scheme % +% - Sl_to_Sl: slice profile, including slice gap if any % +% - SliceProfile: Gaussian slice profile % +% - SliceThickness: thickness of a slice (in mm) % +% - FOVRead: dimension of the field-of-view in the read-out % +% direction (in mm) % +% - FOVPhase: dimension of the field-of-view in the phase % +% direction (in mm) % +% - BaseResolution: matrix size in the read-out direction (in % +% voxels) % +% - nPE: number of phase encoding lines % +% - ACF: acceleration factor % +% - RefLines: number of lines that are consecutively sampled % +% around the center of K-space % +% - GA: gestational age of the fetus (in weeks) % +% - sub_id: subject ID % +% - WM_heterogeneity: boolean value that determines wether to % +% implement (1) or not (0) white matter % +% maturation processes in the simulated % +% images % +% - affine: affine orientation matrix of the anatomical model % +% of the fetal brain % +% - SimRes: resolution of the original 3D high-resolution % +% anatomical model of the fetal brain (in mm) % +% - axcodes: axes codes of the 3D anatomical model of the % +% fetal brain % +% - shift: displacement (in voxels) of the slice slab in the % +% slice thickness direction % +% - orientation: strict acquisition plane (axial, coronal or % +% sagittal) % +% - T1_WM: T1 relaxation time of white matter at the magnetic % +% field strength specified for the simulated % +% acquisition % +% - T2_WM: T2 relaxation time of white matter at the magnetic % +% field strength specified for the simulated % +% acquisition % +% - T1_GM: T1 relaxation time of gray matter at the magnetic % +% field strength specified for the simulated % +% acquisition % +% - T2_GM: T2 relaxation time of gray matter at the magnetic % +% field strength specified for the simulated % +% acquisition % +% - T1_CSF: T1 relaxation time of cerebrospinal fluid at the % +% magnetic field strength specified for the % +% simulated acquisition % +% - T2_CSF: T2 relaxation time of cerebrospinal fluid at the % +% magnetic field strength specified for the % +% simulated acquisition % +% % +% output: - KSpace: Fourier domain of the simulated images % +% - Sl_Volume: % +% % +% % +% Hélène Lajous, 2023-03-17 % +% helene.lajous@unil.ch % +% % +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% + + +function [ KSpace, ... + Sl_Volume] = Kspace_sampling( T2decay_zp, ... + Fetal_Brain, ... + fetal_model, ... + b1map_upsampled, ... + Fetal_Brain_Tissues, ... + SimResReo, ... + SamplingFactor, ... + motion_corrupted_slices, ... + translation_displacement, ... + rotation_angle, ... + rotation_axis, ... + interpolation_method_upsampling, ... + ETL, ... + flipAngle, ... + ESP, ... + TEeff, ... + TR, ... + NbSlices, ... + interleavedSlices_index, ... + Sl_to_Sl, ... + SliceProfile, ... + SliceThickness, ... + FOVRead, ... + FOVPhase, ... + BaseResolution, ... + nPE, ... + ACF, ... + RefLines, ... + GA, ... + sub_id, ... + WM_heterogeneity, ... + affine, ... + SimRes, ... + axcodes_reo, ... + shift, ... + orientation, ... + T1_WM, ... + T2_WM, ... + T1_GM, ... + T2_GM, ... + T1_CSF, ... + T2_CSF) + +% Input check +if nargin < 42 + error('Missing input(s).'); +elseif nargin > 42 + error('Too many inputs.'); +end + +% Memory pre-allocation +Sl_Volume = zeros(size(T2decay_zp,1), size(T2decay_zp,2), NbSlices, size(T2decay_zp,4)); + +% Preload K-Space array which has dimensions of number of lines, number of +% Phase Encoding directions, and number of Slices +KSpace = zeros(BaseResolution, nPE, NbSlices, 'single'); + +% Create a "SLAB" array which is essentially the K-Space of our +% multislice volume +SLAB = zeros([BaseResolution, nPE, NbSlices, size(T2decay_zp,4)]); + +SubunitRes = SimResReo(3) / SamplingFactor; %mm + +% Computation time to sample k-space +tic + +motion_index = 0; + +% Loop through slices +for iSlice=1:length(interleavedSlices_index) + disp(['Slice ', num2str(interleavedSlices_index(iSlice)), ' of ', num2str(length(interleavedSlices_index))]) + index = interleavedSlices_index(iSlice)*length(Sl_to_Sl)-(length(Sl_to_Sl)-1); + % Inter-slice motion - + % - Random translation: uniform distribution between + % [-translation_amplitude,+translation_amplitude]mm + if any(interleavedSlices_index(iSlice)==motion_corrupted_slices) + motion_index = motion_index + 1; + Fetal_Brain_rotated = moving_3Dbrain( Fetal_Brain, ... + SimResReo, ... + translation_displacement(motion_index,:), ... + 'nearest', ... + rotation_angle(motion_index), ... + rotation_axis(motion_index,:), ... + 'nearest', ... + 'crop'); + Fetal_Brain_rotated_upsampled = sampling_OoP( Fetal_Brain_rotated, ... + SamplingFactor, ... + interpolation_method_upsampling); + [ref_T1map_rotated, ref_T2map_rotated] = tissue_to_MR( fetal_model, ... + Fetal_Brain_rotated_upsampled, ... + Fetal_Brain_Tissues, ... + GA, ... + sub_id, ... + WM_heterogeneity, ... + affine, ... + SimRes, ... + axcodes_reo, ... + shift, ... + orientation, ... + SamplingFactor, ... + InterpolationMethod, ... + T1_WM, ... + T2_WM, ... + T1_GM, ... + T2_GM, ... + T1_CSF, ... + T2_CSF); + T2decay_moved = compute_t2decay(Fetal_Brain_rotated_upsampled, ... + b1map_upsampled, ... + ref_T1map_rotated, ... + ref_T2map_rotated, ... + ETL, ... + flipAngle, ... + ESP, ... + SamplingFactor); + T2decay_moved_zp = Resize_Volume(T2decay_moved, size(T2decay_zp)); + T2decay_zp(:,:,index:index+round(SliceThickness/SubunitRes)-1,:) = T2decay_moved_zp(:,:,index:index+round(SliceThickness/SubunitRes)-1,:); + % Update the T2decay_zp variable as it is unlikely that the + % fetus comes back to its initial position at iSlice+1 and + % following slices after he moved + if iSlice < length(interleavedSlices_index) + for nextSlice=iSlice+1:length(interleavedSlices_index) + nextSlice_index = interleavedSlices_index(nextSlice)*length(Sl_to_Sl)-(length(Sl_to_Sl)-1); + T2decay_zp(:,:,nextSlice_index:nextSlice_index+round(SliceThickness/SubunitRes)-1,:) = T2decay_moved_zp(:,:,nextSlice_index:nextSlice_index+round(SliceThickness/SubunitRes)-1,:); + end + end + clear ref_T2map_rotated + clear ref_T1map_rotated + clear Fetal_Brain_rotated + clear Fetal_Brain_rotated_upsampled + clear T2decay_moved + clear T2decay_moved_zp + end + % Multiply the 4D matrix by the slice profile + T2decay_zp(:,:,index:index+round(SliceThickness/SubunitRes)-1,:) = T2decay_zp(:,:,index:index+round(SliceThickness/SubunitRes)-1,:).*permute(SliceProfile, [2,3,1]); + % Sum the contribution of all voxels at the same (x,y) location across + % the slice thickness direction + Sl_Volume(:,:,interleavedSlices_index(iSlice),:) = sum(T2decay_zp(:,:,index:index+round(SliceThickness/SubunitRes)-1,:),3); + % Simulation of k-space sampling as for FSE sequences +% tic + for iEcho=1:size(Sl_Volume,4) + %clc; +% disp(['Echo ', num2str(iEcho), ' of ', num2str(size(Sl_Volume,4))]) + SLAB(:,:,interleavedSlices_index(iSlice),iEcho) = Resize_Volume(fft2c(Resize_Volume(Sl_Volume(:,:,interleavedSlices_index(iSlice),iEcho), [round(FOVRead/SimResReo(1)), round(FOVPhase/SimResReo(2)), NbSlices])), [BaseResolution, nPE, NbSlices]); + end +% toc +% tic + % Calculating k-space sampling based on the desired echo time occuring + % at the center of k-space + if ACF~=1 && RefLines~=0 + temp = (nPE/2-RefLines/2)+RefLines+1:ACF:nPE; + SamplingOrder = fliplr([ACF:ACF:(nPE/2-RefLines/2), (nPE/2-RefLines/2)+1:(nPE/2-RefLines/2)+RefLines, temp(1:round(TEeff/TR-RefLines/2))]); + clear temp + elseif ACF==1 && RefLines==0 + temp = nPE/2+1:ACF:nPE; + SamplingOrder = fliplr([ACF:ACF:nPE/2, temp(1:round(TEeff/TR))]); + clear temp + elseif ACF~=1 && RefLines==0 + temp = nPE/2+ACF:ACF:nPE; + SamplingOrder = fliplr([ACF:ACF:nPE/2, temp(1:round(TEeff/TR))]); + clear temp + end + % Loop through phase encoding lines + for iLine=1:length(SamplingOrder) + KSpace(:, SamplingOrder(iLine), interleavedSlices_index(iSlice)) = SLAB(:, SamplingOrder(iLine), interleavedSlices_index(iSlice), iLine); + end + % This next block finds the missing lines and replaces them with the + % closest echo time + %Sum non-zero contributions in KSpace for slice + %interleavedSlices_index(iSlice), and echo iEcho + if sum(sum(KSpace(:,:,interleavedSlices_index(iSlice))))~=0 + %The sampled lines in KSpace for slice iSlice, and echo iEcho + %are non zero + SampledLines = find(squeeze(KSpace(1,:,interleavedSlices_index(iSlice)))~=0); + %Loop through all lines of KSpace + for iLine=1:size(KSpace,2) + %If a line was not sampled + if KSpace(1,iLine,interleavedSlices_index(iSlice))==0 + %Find only the first non-zero line following iLine + iFind = find(SampledLines>iLine,1,'first'); + %If a sampled line iFind was found following the + %non-sampled line iLine, copy this iFind line from SLAB + %to the corresponding line position in KSpace + if ~isempty(iFind) + KSpace(:,iLine,interleavedSlices_index(iSlice)) = SLAB(:,iLine,interleavedSlices_index(iSlice),SamplingOrder==SampledLines(iFind)); + else + %If no sampled line was found following the non-sampled + %line iLine, use hermitian symmetry to fill KSpace: the + %line symmetrical to iLine compared to the center of + %KSpace is size(KSpace,2)-iLine+1 + KSpace(:,iLine,interleavedSlices_index(iSlice)) = fliplr(conj(KSpace(:,size(KSpace,2)-iLine+1,interleavedSlices_index(iSlice)))')'; + end + end + end + end +% toc +end + +% Display computation time +time3=toc; +fprintf('Computation time to sample k-space: %0.5f seconds.\n', time3); + end \ No newline at end of file diff --git a/Utilities/Resize_Volume.m b/Utilities/Resize_Volume.m index a2319b9..01366f8 100644 --- a/Utilities/Resize_Volume.m +++ b/Utilities/Resize_Volume.m @@ -1,59 +1,66 @@ -%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% -% This script shrinks or expands a multi-dimensional image for a % -% desired size. % -% % -% % -% (c) Christopher W. Roy, 2018-12-04 % -% fetal.xcmr@gmail.com % -% https://github.com/cwroy/Fetal-XCMR/ % -% Roy, C.W. et al. Fetal XCMR: a numerical phantom for fetal % -% cardiovascular magnetic resonance imaging. Journal of Cardiovascular % -% Magnetic Resonance 21, 29 (2019). % -% https://doi.org/10.1186/s12968-019-0539-2 % -% % -%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% - - -function [I,y,x] = Resize_Volume(I,sz) - -if size(I,1)~=sz(1)||size(I,2)~=sz(2)||size(I,3)~=sz(3) - - if ~exist('sz','var') - sz=round(size(I,1)/2); - end - if length(sz)==1 - sz=[sz,sz]; - end - - if size(I,1)>=sz(1) - y=(size(I,1)-sz(1))/2; - y=ceil(y+1:y+sz(1)); - I=I(y,:,:,:); - else - p1=sz(1)-size(I,1); - I=cat(1,zeros(floor(0.5*p1),size(I,2),size(I,3),size(I,4)),I,zeros(ceil(0.5*p1),size(I,2),size(I,3),size(I,4))); - end - - - if size(I,2)>=sz(2) - x=(size(I,2)-sz(2))/2; - x=ceil(x+1:x+sz(2)); - I=I(:,x,:,:); - else - p2=sz(2)-size(I,2); - I=cat(2,zeros(size(I,1),floor(0.5*p2),size(I,3),size(I,4)),I,zeros(size(I,1),ceil(0.5*p2),size(I,3),size(I,4))); - end - - if size(I,3)>1 - if size(I,3)>=sz(3) - x=(size(I,3)-sz(3))/2; - x=ceil(x+1:x+sz(3)); - I=I(:,:,x,:); - else - p3=sz(3)-size(I,3); - I=cat(3,zeros(size(I,1),size(I,2),floor(0.5*p3),size(I,4)),I,zeros(size(I,1),size(I,2),ceil(0.5*p3),size(I,4))); - end - end -end - +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +% This script shrinks or expands a multi-dimensional image for a % +% desired size. % +% % +% % +% (c) Christopher W. Roy, 2018-12-04 % +% fetal.xcmr@gmail.com % +% https://github.com/cwroy/Fetal-XCMR/ % +% Roy, C.W. et al. Fetal XCMR: a numerical phantom for fetal % +% cardiovascular magnetic resonance imaging. Journal of Cardiovascular % +% Magnetic Resonance 21, 29 (2019). % +% https://doi.org/10.1186/s12968-019-0539-2 % +% Modified by Hélène Lajous, 2023-02-24 % +% helene.lajous@unil.ch % +% % +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% + + +function [I,y,x] = Resize_Volume(I,sz) + +if size(I,1)~=sz(1)||size(I,2)~=sz(2)||size(I,3)~=sz(3) + + if ~exist('sz','var') + sz=round(size(I,1)/2); + end + if length(sz)==1 + sz=[sz,sz]; + end + + if size(I,1)>=sz(1) + y=(size(I,1)-sz(1))/2; + y=ceil(y+1:y+sz(1)); + I=I(y,:,:,:); + else + p1=sz(1)-size(I,1); + %If p1 is odd, add one more "0" at the left of the image (i.e., + %from 0) than at the right (i.e., towards infinity) to be + %consistent with the case where the image is cropped to a smaller + %image: the center is deviated towards the right/bottom/back of the + %image + I=cat(1,zeros(ceil(0.5*p1),size(I,2),size(I,3),size(I,4)),I,zeros(floor(0.5*p1),size(I,2),size(I,3),size(I,4))); + end + + + if size(I,2)>=sz(2) + x=(size(I,2)-sz(2))/2; + x=ceil(x+1:x+sz(2)); + I=I(:,x,:,:); + else + p2=sz(2)-size(I,2); + I=cat(2,zeros(size(I,1),ceil(0.5*p2),size(I,3),size(I,4)),I,zeros(size(I,1),floor(0.5*p2),size(I,3),size(I,4))); + end + + if size(I,3)>1 + if size(I,3)>=sz(3) + x=(size(I,3)-sz(3))/2; + x=ceil(x+1:x+sz(3)); + I=I(:,:,x,:); + else + p3=sz(3)-size(I,3); + I=cat(3,zeros(size(I,1),size(I,2),ceil(0.5*p3),size(I,4)),I,zeros(size(I,1),size(I,2),floor(0.5*p3),size(I,4))); + end + end +end + end \ No newline at end of file diff --git a/Utilities/WM_maturation.m b/Utilities/WM_maturation.m new file mode 100644 index 0000000..3fe8142 --- /dev/null +++ b/Utilities/WM_maturation.m @@ -0,0 +1,234 @@ +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +% Function that implements white matter maturation processes by tuning % +% the T1 and T2 values from the reference maps. Two methods are % +% available: a 2-class segmentation using a gaussian mixture model % +% (GMM) and a 3-class segmentation using FAST-FSL. % +% % +% [WM_T1map, WM_T2map] = WM_maturation( model, ... % +% ref_T1map, ... % +% ref_T2map, ... % +% GA, ... % +% sub_id, ... % +% segmentation, ... % +% affine, ... % +% SimRes, ... % +% axcodes_reo, ... % +% shift, ... % +% orientation, ... % +% SamplingFactor) % +% % +% inputs: - model: anatomical model of the fetal brain % +% - ref_T1map: reference T1 map of the fetal brain with the % +% third dimension being the slice thickness % +% direction % +% - ref_T2map: reference T2 map of the fetal brain with the % +% third dimension being the slice thickness % +% direction % +% - GA: gestational age of the fetus (in weeks) % +% - sub_id: subject ID % +% - segmentation: 2 ways of improving white matter % +% heterogeneity: 'FAST' or 'GMM' % +% - affine: affine orientation matrix of the anatomical model % +% of the fetal brain % +% - SimRes: resolution of the 3D anatomical model of the fetal % +% brain % +% - axcodes_reo: output axes codes of the reoriented 3D % +% anatomical model of the fetal brain with the % +% slice thickness encoded in the third % +% dimension % +% - shift: displacement (in voxels) of the slice slab in the % +% slice thickness direction % +% - orientation: strict acquisition plane (axial, coronal or % +% sagittal) % +% - SamplingFactor: factor by which the fetal brain volume and % +% the B1 bias field have been upsampled % +% - InterpolationMethod: interpolation method used to assign a % +% value to every voxel of a resampled % +% 3D volume. Images generated for the % +% qualitative evaluation by the % +% radiologists were simulated from % +% partial volume maps bilinearly % +% interpolated. To reduce the % +% computational burden that arises from % +% EPG simulations with many non-unique % +% combinations of [b1, T1, T2], the % +% images generated for the data % +% augmentation experiment were % +% simulated from partial volume maps % +% interpolated using a nearest- % +% -neighboor method. % +% % +% outputs: - WM_T1map: tuned T1 map of the fetal brain % +% - WM_T2map: tuned T2 map of the fetal brain % +% % +% % +% le Boeuf Andrés, 2022-03-23 % +% andres.le.boeuf@estudiantat.upc.edu % +% Modified by Hélène Lajous, 2023-02-14 % +% helene.lajous@unil.ch % +% % +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% + +function [WM_T1map, WM_T2map] = WM_maturation( model, ... + ref_T1map, ... + ref_T2map, ... + GA, ... + sub_id, ... + segmentation, ... + affine, ... + SimRes, ... + axcodes_reo, ... + shift, ... + orientation, ... + SamplingFactor, ... + InterpolationMethod) + +% Input check +if nargin < 13 + error('Missing input(s).'); +elseif nargin > 13 + error('Too many inputs!'); +end + +tic; +% Flatten T1 and T2 3D maps +unwrap_ref_T1map = ref_T1map(:); +unwrap_ref_T2map = ref_T2map(:); + +if isequal(segmentation, 'FAST') + %Import and set up Partial Volume Maps from FAST segmentation tool + switch model + case 'STA' +% path = '.\data\atlas_fast_clustering\STA'; + path = 'C:\Users\admin\Desktop\Andrés\CODE AND DATA TO SIMULATE DIFERENT DATASETS\GMM and FAST segmentations on White matter\FAST_Clustering\STA'; + pve0 = niftiread(strcat(path, sprintf('%02s', num2str(GA)), '\STA', sprintf('%02s', num2str(GA)), '_WM_pve_0.nii.gz')); + pve1 = niftiread(strcat(path, sprintf('%02s', num2str(GA)), '\STA', sprintf('%02s', num2str(GA)), '_WM_pve_1.nii.gz')); + pve2 = niftiread(strcat(path, sprintf('%02s', num2str(GA)), '\STA', sprintf('%02s', num2str(GA)), '_WM_pve_2.nii.gz')); + case 'FeTA_CHUV' + if sub_id=='sub-709' + path = '/data/bach/SimuHASTE/Data_processed/FeTA_challenge_2022-CHUV_testing_set_proc_resampled_1mm3/WM_segmentation_after_upsampling/'; + else + path = '/data/bach/SimuHASTE/Data_processed/FeTA_challenge_2022-CHUV_testing_set_proc/WM_segmentation_after_upsampling/'; + end + pve0 = niftiread(strcat(path, sub_id, '/', sub_id, '_WM_pve_0.nii.gz')); + pve1 = niftiread(strcat(path, sub_id, '/', sub_id, '_WM_pve_1.nii.gz')); + pve2 = niftiread(strcat(path, sub_id, '/', sub_id, '_WM_pve_2.nii.gz')); + case 'FeTA' + path = '/data/bach/SimuHASTE/Data_processed/FeTA2021_Release1and2Corrected_v4_proc/'; + pve0 = niftiread(strcat(path, sub_id, '/', sub_id, '_WM_pve_0.nii.gz')); + pve1 = niftiread(strcat(path, sub_id, '/', sub_id, '_WM_pve_1.nii.gz')); + pve2 = niftiread(strcat(path, sub_id, '/', sub_id, '_WM_pve_2.nii.gz')); + end + + % Reorient the partial volume maps extracted from the WM mask of the + % fetal brain so that the slice thickness direction is encoded in the + % 3. dimension + [pve0_reo, ~] = reorient_volume( pve0, ... + affine, ... + SimRes, ... + axcodes_reo); + [pve1_reo, ~] = reorient_volume( pve1, ... + affine, ... + SimRes, ... + axcodes_reo); + [pve2_reo, ~] = reorient_volume( pve2, ... + affine, ... + SimRes, ... + axcodes_reo); + + % As for the 3D anatomical model of the fetal brain in the main.m, + % shift the FOV of the partial volume maps extracted from the WM mask + % in the slice thickness direction + pve0_reo = FOV_shift(pve0_reo, shift, orientation); + pve1_reo = FOV_shift(pve1_reo, shift, orientation); + pve2_reo = FOV_shift(pve2_reo, shift, orientation); + + % As for the 3D anatomical model of the fetal brain in the main.m, + % shift the FOV of the partial volume maps extracted from the WM mask + % in the slice thickness direction + pve0_reo = FOV_shift(pve0_reo, shift, orientation); + pve1_reo = FOV_shift(pve1_reo, shift, orientation); + pve2_reo = FOV_shift(pve2_reo, shift, orientation); + + % As for the 3D anatomical model of the fetal brain in the main.m, + % up-sample the partial volume maps extracted from the WM mask in the + % slice thickness direction + pve0_reo = sampling_OoP( pve0_reo, ... + SamplingFactor, ... + InterpolationMethod); + pve1_reo = sampling_OoP( pve1_reo, ... + SamplingFactor, ... + InterpolationMethod); + pve2_reo = sampling_OoP( pve2_reo, ... + SamplingFactor, ... + InterpolationMethod); + + %Build advantage/disadvantage values + pos_reward = pve0_reo(:) - pve1_reo(:); + neg_reward = pve1_reo(:) - pve2_reo(:); + + pos_reward(pos_reward<0) = 0; + neg_reward(neg_reward>0) = 0; + + %Out boundaries control of the advantage/disadvantage values + ClipValue = 'adapt'; + pos_reward = clip_function(pos_reward, ClipValue, GA); + neg_reward = clip_function(neg_reward, ClipValue, GA); + + fprintf('Applying T1 and T2 values variation through the white matter...\n'); + %cherche valeurs différentes de 1 pour appliquer variations : mettre + %l'étape de +1 de la clip_function ici- On ajoute 1 pcq multiplication + %après + pos_ind = find(pos_reward~=1); + neg_ind = find(neg_reward~=1); + + % Wheight T1 and T2 reference values with the new advantage/disadvantage + % values + % for computational purpose, don't consider voxels where no difference + % with the mean value + for i=1:length(pos_ind) + unwrap_ref_T1map(pos_ind(i)) = unwrap_ref_T1map(pos_ind(i)) * pos_reward(pos_ind(i)); + unwrap_ref_T2map(pos_ind(i)) = unwrap_ref_T2map(pos_ind(i)) * pos_reward(pos_ind(i)); + end + + for i=1:length(neg_ind) + unwrap_ref_T1map(neg_ind(i)) = unwrap_ref_T1map(neg_ind(i)) * neg_reward(neg_ind(i)); + unwrap_ref_T2map(neg_ind(i)) = unwrap_ref_T2map(neg_ind(i)) * neg_reward(neg_ind(i)); + end + elseif isequal(segmentation, 'GMM') + %Import and set up Partial Volume Maps from GMM segmentation + path = '.\data\atlas_gmm_clustering'; + WM_hyd = niftiread(strcat(path, '\STA', sprintf('%02s', num2str(GA)), '\STA', sprintf('%02s', num2str(GA)), '_map1.nii')); + WM_non_hyd = niftiread(strcat(path, '\STA', sprintf('%02s', num2str(GA)), '\STA', sprintf('%02s', num2str(GA)), '_map2.nii')); + + % We convert the unsigned integer values to doubles (uint16 --> maxvalue = + % 2`16 - 1 = 65535 + WM_hyd = double(WM_hyd(:)) ./ (2^16 - 1); + WM_non_hyd = double(WM_non_hyd(:)) ./ (2^16 - 1); + + %Searching for non background voxels + pos_non_background = find((WM_hyd+WM_non_hyd)~=0); + + %if >0: T1 and T2 values will increase, otherwise decrease (as the WM is more dense, therefore "darker" white matter) + reward_map = zeros(size(pos_non_background)); + for i=1:length(reward_map) + reward_map(i) = WM_hyd(pos_non_background(i)) - WM_non_hyd(pos_non_background(i)); + end + + %We clip the change values to don't get outboundaries T1/T2 values + ClipValue = 'adapt'; + reward_map = clip_function(reward_map, ClipValue, 'sigmoid', GA); + + fprintf('Applying T1 and T2 values variation through the white matter...\n'); + + for i=1:length(reward_map) + unwrap_ref_T1map(pos_non_background(i)) = unwrap_ref_T1map(pos_non_background(i)) * reward_map(i); + unwrap_ref_T2map(pos_non_background(i)) = unwrap_ref_T2map(pos_non_background(i)) * reward_map(i); + end +end + +WM_T1map = reshape(unwrap_ref_T1map, size(ref_T1map)); +WM_T2map = reshape(unwrap_ref_T2map, size(ref_T2map)); +fprintf('Temporal Resolution improved in %0.5f seconds ! :D\n', toc); + +end \ No newline at end of file diff --git a/Utilities/add_noise.m b/Utilities/add_noise.m index e2cabd5..f359a65 100644 --- a/Utilities/add_noise.m +++ b/Utilities/add_noise.m @@ -1,33 +1,33 @@ -%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% -% Function that adds complex Gaussian noise (mean=0, standard deviation % -% to be defined by the user) to a volume in the Fourier domain. % -% % -% KSpace_noise = add_noise(KSpace, std_noise); % -% % -% inputs: - KSpace: Fourier domain of the simulated images % -% - std_noise: standard deviation of the Gaussian noise added % -% to the Fourier domain of the simulated images % -% % -% output: - KSpace_noise: Fourier domain of the simulated images after % -% addition of complex Gaussian noise % -% % -% % -% Hélène Lajous, 2021-04-22 % -% helene.lajous@unil.ch % -% % -%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% - - -function KSpace_noise = add_noise(KSpace, std_noise) - -% Input check -if nargin < 2 - error('Missing input(s).'); -elseif nargin > 2 - error('Too many inputs.'); -end - -cgnoise = complex(std_noise.*randn(size(KSpace)), std_noise.*randn(size(KSpace))); -KSpace_noise = KSpace + cgnoise; - +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +% Function that adds complex Gaussian noise (mean=0, standard deviation % +% to be defined by the user) to a volume in the Fourier domain. % +% % +% KSpace_noise = add_noise(KSpace, std_noise); % +% % +% inputs: - KSpace: Fourier domain of the simulated images % +% - std_noise: standard deviation of the Gaussian noise added % +% to the Fourier domain of the simulated images % +% % +% output: - KSpace_noise: Fourier domain of the simulated images after % +% addition of complex Gaussian noise % +% % +% % +% Hélène Lajous, 2021-04-22 % +% helene.lajous@unil.ch % +% % +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% + + +function KSpace_noise = add_noise(KSpace, std_noise) + +% Input check +if nargin < 2 + error('Missing input(s).'); +elseif nargin > 2 + error('Too many inputs.'); +end + +cgnoise = complex(std_noise.*randn(size(KSpace)), std_noise.*randn(size(KSpace))); +KSpace_noise = KSpace + cgnoise; + end \ No newline at end of file diff --git a/Utilities/aff2axcodes.m b/Utilities/aff2axcodes.m new file mode 100644 index 0000000..6ac730d --- /dev/null +++ b/Utilities/aff2axcodes.m @@ -0,0 +1,42 @@ +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +% Function that returns the axis direction codes for a given affine % +% matrix. % +% % +% axcodes = aff2axcodes(affine, labels, tolerance); % +% % +% inputs: - affine: affine transformation matrix from the anatomical % +% fetal brain model % +% - labels: labels for negative and positive ends of output % +% axes of the affine matrix % +% - tolerance: threshold below which SVD values of the affine % +% are considered zero % +% % +% output: - axcodes: labels for positive end of voxel axes. Dropped % +% axes get a label of None. % +% % +% % +% Hélène Lajous, 2023-01-30 % +% helene.lajous@unil.ch % +% % +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% + + +function axcodes = aff2axcodes(affine, varargin) + +% Input check +if nargin < 1 + error('Missing input(s).'); +else + defaultLabels = {["L","R"], ["P","A"], ["I","S"]}; + defaultTolerance = "None"; + p = inputParser; + addRequired(p, 'affine'); + addOptional(p, 'labels', defaultLabels); + addOptional(p, 'tolerance', defaultTolerance); + parse(p, affine, varargin{:}); + + ornt = io_orientation(p.Results.affine, 'tolerance', p.Results.tolerance); + axcodes = ornt2axcodes(ornt, 'labels', p.Results.labels); +end + +end \ No newline at end of file diff --git a/Utilities/auto_segmentation.m b/Utilities/auto_segmentation.m new file mode 100644 index 0000000..21fed34 --- /dev/null +++ b/Utilities/auto_segmentation.m @@ -0,0 +1,129 @@ + + +function Simu_Labels = auto_segmentation( Fetal_Brain_zp, ... + Fetal_Brain, ... + SimRes, ... + sampling_factor, ... + SliceThickness, ... + SubunitRes, ... + FOVRead, ... + FOVPhase, ... + NbSlices, ... + interleavedSlices_index, ... + Sl_to_Sl, ... + motion_corrupted_slices, ... + translation_displacement, ... + rotation_angle, ... + rotation_axis) + +% Input check +if nargin < 15 + error('Missing input(s).'); +elseif nargin > 15 + error('Too many inputs.'); +end + +% Memory pre-allocation +Sl_Volume = zeros(size(Fetal_Brain_zp,1), size(Fetal_Brain_zp,2), NbSlices); + +% Initialization of tranformation arrays +motion_index = 0; + +% Loop through slices to apply motion tranform +for iSlice=1:length(interleavedSlices_index) + disp(['Slice ', num2str(interleavedSlices_index(iSlice)), ' of ', num2str(length(interleavedSlices_index))]) + index = interleavedSlices_index(iSlice)*length(Sl_to_Sl)-(length(Sl_to_Sl)-1); + % Random inter-slice motion: be careful, labels are interpolated! + if any(interleavedSlices_index(iSlice)==motion_corrupted_slices) + motion_index = motion_index + 1; + Fetal_Brain_rotated = moving_3Dbrain( Fetal_Brain, ... + SimRes, ... + translation_displacement(motion_index,:), ... + 'nearest', ... + rotation_angle(motion_index), ... + rotation_axis(motion_index,:), ... + 'nearest', ... + 'crop'); + Fetal_Brain_rotated_upsampled = sampling_OoP(Fetal_Brain_rotated, ... + sampling_factor, ... + 'nearest'); + Fetal_Brain_moved_zp = Resize_Volume(Fetal_Brain_rotated_upsampled, size(Fetal_Brain_zp)); + Fetal_Brain_zp(:,:,index:index+round(SliceThickness/SubunitRes)-1) = Fetal_Brain_moved_zp(:,:,index:index+round(SliceThickness/SubunitRes)-1); + % Update the T2decay_zp variable as it is unlikely that the + % fetus comes back to its initial position at iSlice+1 and + % following slices after it has moved + if iSlice < length(interleavedSlices_index) + for nextSlice=iSlice+1:length(interleavedSlices_index) + nextSlice_index = interleavedSlices_index(nextSlice)*length(Sl_to_Sl)-(length(Sl_to_Sl)-1); + Fetal_Brain_zp(:,:,nextSlice_index:nextSlice_index+round(SliceThickness/SubunitRes)-1) = Fetal_Brain_moved_zp(:,:,nextSlice_index:nextSlice_index+round(SliceThickness/SubunitRes)-1); + end + end + clear Fetal_Brain_rotated + clear Fetal_Brain_rotated_upsampled + end +end + +% + +freq_labels = 0; +freq_labels_other = 0; + +% Now that motion occured and that the labels are right, we keep only one +% label value within the slice thickness +for iSlice=1:length(interleavedSlices_index) + disp(['Slice ', num2str(interleavedSlices_index(iSlice)), ' of ', num2str(length(interleavedSlices_index))]) + index = interleavedSlices_index(iSlice)*length(Sl_to_Sl)-(length(Sl_to_Sl)-1); + % Interpolate the label for all voxels at the same (x,y) location + % across the slice thickness direction + for row=1:size(Fetal_Brain_zp,1) + for col=1:size(Fetal_Brain_zp,2) + slice_labels = Fetal_Brain_zp(row,col,index:index+round(SliceThickness/SubunitRes)-1); + if unique(slice_labels)==0 + max_value = 0; + else + % Count the occurrences of each non-zero label within the + % slice thickness + [count, value] = groupcounts(squeeze(slice_labels(slice_labels>0))); + max_index = find(count==max(count)); + if length(max_index)==1 + max_value = value(max_index); + %Handle cases where more than 1 label occurs at the same + %frequency in the slice thickness + else + freq_labels = freq_labels + 1; + if any(value(max_index)==Fetal_Brain_zp(row,col,index-1)) + freq_labels_other = freq_labels_other + 1; + max_value = Fetal_Brain_zp(row,col,index-1); + elseif any(value(max_index)==Fetal_Brain_zp(row,col,index+round(SliceThickness/SubunitRes))) + freq_labels_other = freq_labels_other + 1; + max_value = Fetal_Brain_zp(row,col,index+round(SliceThickness/SubunitRes)); + % elseif any(value(max_index)==Fetal_Brain_zp(row-1,col,round(index+SliceThickness/SubunitRes-1)/2)) + % freq_labels_other = freq_labels_other + 1; + % max_value = Fetal_Brain_zp(row-1,col,round(index+SliceThickness/SubunitRes-1)/2); + % elseif any(value(max_index)==Fetal_Brain_zp(row+1,col,round(index+SliceThickness/SubunitRes-1)/2)) + % freq_labels_other = freq_labels_other + 1; + % max_value = Fetal_Brain_zp(row+1,col,round(index+SliceThickness/SubunitRes-1)/2); + % elseif any(value(max_index)==Fetal_Brain_zp(row,col-1,round(index+SliceThickness/SubunitRes-1)/2)) + % freq_labels_other = freq_labels_other + 1; + % max_value = Fetal_Brain_zp(row,col-1,round(index+SliceThickness/SubunitRes-1)/2); + % elseif any(value(max_index)==Fetal_Brain_zp(row,col+1,round(index+SliceThickness/SubunitRes-1)/2)) + % freq_labels_other = freq_labels_other + 1; + % max_value = Fetal_Brain_zp(row,col+1,round(index+SliceThickness/SubunitRes-1)/2); + end + end + end + Fetal_Brain_zp(row,col,index:index+round(SliceThickness/SubunitRes)-1) = max_value; + end + end +% [count, value] = groupcounts(slice_labels); +% max_index = find(count==max(count)); +% max_value = value(max_index); +% Fetal_Brain_zp(100,100,index:index+SliceThickness/SubunitRes-1) = max_value; + Sl_Volume(:,:,interleavedSlices_index(iSlice)) = Fetal_Brain_zp(:,:,index); +end + +% Recover the motion-corrupted label map at the right simulated resolution +% of acquisition +Simu_Labels = Resize_Volume(Sl_Volume, [round(FOVRead)/SimRes(1), round(FOVPhase)/SimRes(2), NbSlices]); + +end \ No newline at end of file diff --git a/Utilities/brainWeb_inu.m b/Utilities/brainWeb_inu.m index 44a90ce..3e7f072 100644 --- a/Utilities/brainWeb_inu.m +++ b/Utilities/brainWeb_inu.m @@ -1,59 +1,61 @@ -%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% -% Function that calls the simulated intensity non-uniformity fields % -% (3D) from the BrainWeb database, resizes them to the fetal brain % -% volume dimensions and normalizes them to a level of 40% (i.e., to an % -% intensity range of 0.8 to 1.2 over the fetal brain volume). % -% % -% b1map = brainWeb_inu(inu, Fetal_Brain); % -% % -% inputs: - inu: simulated intensity non-uniformity fields (3D). Here, % -% we consider B1 bias field variations in raw byte % -% (unsigned) format obtained from the BrainWeb database % -% -- BrainWeb: Simulated brain database. % -% https://brainweb.bic.mni.mcgill.ca/brainweb/ -- % -% - Fetal_Brain: segmented high-resolution 3D volume of the % -% fetal brain % -% % -% output: - b1map: simulated B1 bias field variations (B1+). This 3D % -% volume is the same size as the fetal brain volume. % -% % -% % -% Hélène Lajous, 2020-09-30 % -% helene.lajous@unil.ch % -% % -%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% - - -function b1map = brainWeb_inu(inu, Fetal_Brain) - -% Input check -if nargin < 2 - error('Missing input(s).'); -elseif nargin > 2 - error('Too many inputs.'); -end - -% x, y and z are the dimensions of the simulated intensity non-uniformity -% fields from the BrainWeb database -z = 181; -y = 217; -x = 181; -volume = zeros(x,y,z); - -% Read BrainWeb intensity non-uniformity fields (3D) -f = fopen(inu, 'r'); -for i=1:z - Im = fread(f, [x,y], 'uint8'); - volume(:,:,i) = Im; -end - -% Resize the intensity non-uniformity fields to match the fetal brain -% volume dimensions -b1map_res = Resize_Volume(volume, size(Fetal_Brain)); - -% Normalize the intensity non-uniformity fields by 1.2 (i.e., level of 40%) -b1map = b1map_res ./ max(max(max(b1map_res))) * 1.2; - -fclose(f); - +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +% Function that calls the simulated intensity non-uniformity fields % +% (3D) from the BrainWeb database, resizes them to the fetal brain % +% volume dimensions and normalizes them to a level of 40% (i.e., to an % +% intensity range of 0.8 to 1.2 over the fetal brain volume). % +% % +% b1map = brainWeb_inu(inu, Fetal_Brain); % +% % +% inputs: - inu: simulated intensity non-uniformity fields (3D). Here, % +% we consider B1 bias field variations in raw byte % +% (unsigned) format obtained from the BrainWeb database % +% -- BrainWeb: Simulated brain database. % +% https://brainweb.bic.mni.mcgill.ca/brainweb/ -- % +% - Fetal_Brain: segmented high-resolution 3D volume of the % +% fetal brain % +% % +% output: - b1map: simulated B1 bias field variations (B1+). This 3D % +% volume is the same size as the fetal brain volume. % +% % +% % +% Hélène Lajous, 2020-09-30 % +% helene.lajous@unil.ch % +% % +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% + + +function b1map = brainWeb_inu(inu, Fetal_Brain) + +% Input check +if nargin < 2 + error('Missing input(s).'); +elseif nargin > 2 + error('Too many inputs.'); +end + +% x, y and z are the dimensions of the simulated intensity non-uniformity +% fields from the BrainWeb database +z = 181; +y = 217; +x = 181; +volume = zeros(x,y,z); + +% Read BrainWeb intensity non-uniformity fields (3D) +[f,msg] = fopen(inu, 'r'); +if f<1, error([msg ' File: ' inu]), end + +for i=1:z + Im = fread(f, [x,y], 'uint8'); + volume(:,:,i) = Im; +end + +% Resize the intensity non-uniformity fields to match the fetal brain +% volume dimensions +b1map_res = imresize3(volume, size(Fetal_Brain), 'nearest'); + +% Normalize the intensity non-uniformity fields by 1.2 (i.e., level of 40%) +b1map = b1map_res ./ max(b1map_res(:)) * 1.2; + +fclose(f); + end \ No newline at end of file diff --git a/Utilities/brain_model.m b/Utilities/brain_model.m index 18f8566..61faba6 100644 --- a/Utilities/brain_model.m +++ b/Utilities/brain_model.m @@ -1,41 +1,52 @@ -%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% -% Function that reads the segmented high-resolution anatomical MR % -% images of the fetal brain from which the simulated images will be % -% derived at a given gestational age (GA). % -% Here, we consider segmented high-resolution images from Gholipour, A. % -% et al. A normative spatiotemporal MRI atlas of the fetal brain for % -% automatic segmentation and analysis of early brain growth. Scientific % -% Reports 7, 476 (2017). https://doi.org/10.1038/s41598-017-00525-w % -% % -% Fetal_Brain = brain_model(path, GA); % -% % -% inputs: - path: folder where the segmented high-resolution MR images % -% of the fetal brain from which our simulations are % -% derived are stored % -% - GA: gestational age of the fetus (in weeks) % -% % -% output: - Fetal_Brain: segmented high-resolution 3D volume of the % -% fetal brain at gestational age GA % -% % -% % -% Hélène Lajous, 2021-04-30 % -% helene.lajous@unil.ch % -% % -%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% - - -function Fetal_Brain = brain_model(path, ... - GA) - -% Input check -if nargin < 2 - error('Missing input(s).'); -elseif nargin > 2 - error('Too many inputs.'); -end - -% Load segmented high-resolution anatomical MR images of the fetal brain at -% gestational age GA -Fetal_Brain = niftiread(strcat(path, 'STA', sprintf('%02s', num2str(GA)), '_tissue.nii.gz')); - +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +% Function that reads the segmented high-resolution anatomical MR % +% images of the fetal brain from which the simulated images will be % +% derived at a given gestational age (GA). % +% Here, we consider segmented high-resolution images from Gholipour, A. % +% et al. A normative spatiotemporal MRI atlas of the fetal brain for % +% automatic segmentation and analysis of early brain growth. Scientific % +% Reports 7, 476 (2017). https://doi.org/10.1038/s41598-017-00525-w % +% % +% Fetal_Brain = brain_model(path, GA); % +% % +% inputs: - path: folder where the segmented high-resolution MR images % +% of the fetal brain from which our simulations are % +% derived are stored % +% - GA: gestational age of the fetus (in weeks) % +% % +% output: - Fetal_Brain: segmented high-resolution 3D volume of the % +% fetal brain at gestational age GA % +% % +% % +% Hélène Lajous, 2021-04-30 % +% helene.lajous@unil.ch % +% % +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% + + +function [Fetal_Brain, nifti_info] = brain_model( path, ... + model, ... + sub_id) + +% Input check +if nargin < 3 + error('Missing input(s).'); +elseif nargin > 3 + error('Too many inputs.'); +end + +% Load segmented high-resolution anatomical MR images of the fetal brain at +% gestational age GA +switch model + case 'STA' + nifti_info = niftiinfo(strcat(path, 'STA', sprintf('%02s', num2str(sub_id)), '_tissue.nii.gz')); + Fetal_Brain = niftiread(nifti_info); + case 'FeTA_CHUV' + nifti_info = niftiinfo(strcat(path, sub_id, '/anat/', sub_id, '_rec-mial_dseg.nii.gz')); + Fetal_Brain = niftiread(nifti_info); + case 'FeTA' + nifti_info = niftiinfo(strcat(path, sub_id, '/parcellation.nii.gz')); + Fetal_Brain = niftiread(nifti_info); +end + end \ No newline at end of file diff --git a/Utilities/clip_function.m b/Utilities/clip_function.m new file mode 100644 index 0000000..ff7a167 --- /dev/null +++ b/Utilities/clip_function.m @@ -0,0 +1,64 @@ +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +% Function that transforms the advantage/disadvantage values to % +% new controlled ones through a sigmoid % +% % +% NewMap = clip_function( Map, ... % +% ClipValue, ... % +% GA) % +% % +% inputs: - Map: advantage/disadvantage values to tune T1/T2 maps % +% - ClipValue: control value set not to deviate T1 and T2 % +% values more than a specific percentage of the % +% corresponding reference T1, respectively T2 % +% value. The 'adapt' option takes the clipping % +% value from a list derived from analyzing the % +% contrast in T2-weighted images of a subject % +% from a normative spatiotemporal MRI atlas of % +% the fetal brain of equivalent gestational age % +% as the subject being simulated. % +% - GA: gestational age of the fetus (in weeks) % +% % +% % +% outputs: - NewMap: map of controlled advantage/disadvantage values % +% % +% % +% le Boeuf Andrés, 2022-04-22 % +% andres.le.boeuf@estudiantat.upc.edu % +% Modified by Hélène Lajous, 2023-02-15 % +% helene.lajous@unil.ch % +% % +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% + +function NewMap = clip_function(Map, ClipValue, GA) + +% Input check +if nargin < 3 + error('Missing input(s).'); +elseif nargin > 3 + error('Too many inputs!'); +end + +%Clipping value selection +if isequal(ClipValue, 'adapt') %adapt clipping value to the GA of the subject + % The following clipping values were determined iteratively based on + % the histogram distribution of subjects from a normative + % spatiotemporal MRI atlas (STA) of the fetal brain (Gholipour et al., + % Scientific Reports, 2017) + ClipValues_STA = [0.2, 0.23, 0.26, 0.32, 0.36, 0.38, 0.37, 0.39, 0.39, 0.39, 0.36, 0.37, 0.43, 0.4, 0.37, 0.33, 0.32, 0.26]; + GA_STA = 21:38; %range of gestational age (GA, in weeks) in STA + % Interpolate clipping values for GA given in a decimal form between + % 20.0 and 38.0 weeks by shape-preserving piecewise cubic interpolation + ClipValuesInterpolated = interp1(GA_STA, ClipValues_STA, 20.0:0.1:38.0, 'pchip'); + ClipValueReward = ClipValuesInterpolated(round((GA - 20) * 10 + 1)); +elseif (ClipValue>=0 && ClipValue<=1) %if want to specify explicitely the clipping value to use + ClipValueReward = ClipValue; +else + error("Wrong clip value.") +end +fprintf("Current clipping value: %f\n", ClipValueReward) + +% Apply the clipping value to the advantage/disadvantage values +Map = ClipValueReward * (2 ./ (1 + exp((-2 / ClipValueReward) * Map)) - 1); %modulate T1/T2 values by sigmoid of parameter clipping value +NewMap = Map + 1; %because want to implement a variation of mean ref T1 and T2 values (éventuellement à sortir de cette fonction et passer dans WM_maturation.m) + +end \ No newline at end of file diff --git a/Utilities/compute_t2decay.m b/Utilities/compute_t2decay.m index bd20bf6..db9a580 100644 --- a/Utilities/compute_t2decay.m +++ b/Utilities/compute_t2decay.m @@ -1,96 +1,100 @@ -%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% -% Function that computes the T2 decay in every voxel of the fetal brain % -% volume from reference T1 and T2 maps, sequence parameters and % -% intensity non-uniformity fields, following the Extended Phase Graph % -% (EPG) formalism. % -% The EPG concept is described in details in: Weigel, M. Extended phase % -% graphs: Dephasing, RF pulses, and echoes - pure and simple. Journal % -% of Magnetic Resonance Imaging 41, 266-295 (2015). % -% https://doi.org/10.1002/jmri.24619. The associated EPG simulation % -% code from Matthias Weigel for multi-spin echo sequences % -% (cp_cpmg_epg_domain_fplus_fminus.m) used in the following can be % -% downloaded here: https://github.com/matthias-weigel/EPG. % -% % -% T2decay = compute_t2decay(Fetal_Brain_upsampled, ... % -% b1map_upsampled, ... % -% ref_T1map, ... % -% ref_T2map, ... % -% ETL, ... % -% ESP, ... % -% sampling_factor); % -% % -% inputs: - Fetal_Brain_upsampled: segmented high-resolution 3D volume % -% of the fetal brain after upsampling % -% by a given sampling factor in the % -% slice thickness direction % -% - b1map_upsampled: 3D B1+ bias field map after upsampling by % -% a given sampling factor in the slice % -% thickness direction % -% - ref_T1map: reference T1 map of the fetal brain (3D) % -% - ref_T2map: reference T2 map of the fetal brain (3D) % -% - ETL: echo train length, i.e. number of 180°-RF pulses % -% - ESP: echo spacing (in ms) % -% - sampling_factor: factor by which the fetal brain volume % -% and the B1 bias field have been upsampled % -% % -% output: - T2decay: 4D matrix that combines the anatomical % -% information from the segmented high-resolution % -% fetal brain volume and the T2 decay computed in % -% every voxel of this volume % -% % -% % -% Hélène Lajous, 2021-04-20 % -% helene.lajous@unil.ch % -% % -%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% - - -function T2decay = compute_t2decay(Fetal_Brain_upsampled, ... - b1map_upsampled, ... - ref_T1map, ... - ref_T2map, ... - ETL, ... - ESP, ... - sampling_factor) - -% Input check -if nargin < 7 - error('Missing input(s).'); -elseif nargin > 7 - error('Too many inputs.'); -end - -% Unwrap maps for computation speed -unwrap_b1map = b1map_upsampled(:); -unwrap_ref_T1map = ref_T1map(:); -unwrap_ref_T2map = ref_T2map(:); - -% Find every possible combination of {b1, T1, T2} values (umap) and store -% the corresponding row where this combination first occurs (imap) -[umap, imap] = unique([unwrap_b1map, unwrap_ref_T1map, unwrap_ref_T2map], 'rows'); -% Exclude background -imap = imap(umap(:,2)~=0, :); -umap = umap(umap(:,2)~=0, :); - -uT2decay = zeros(length(umap), ETL); - -% Computation time -tic - -parfor i=1:size(uT2decay,1) - uT2decay(i, :) = real(cp_cpmg_epg_domain_fplus_fminus(umap(i,1).*90, ETL, umap(i,1).*180, ESP, umap(i,2), umap(i,3)))/sampling_factor; -end - -T2decay = zeros(length(Fetal_Brain_upsampled(:)), ETL, 'single'); -for i=1:size(uT2decay,1) - j = find(unwrap_b1map==umap(i,1) & unwrap_ref_T1map==umap(i,2) & unwrap_ref_T2map==umap(i,3)); - for k=1:length(j) - T2decay(j(k),:) = uT2decay(i,:); - end -end -T2decay = reshape(T2decay, [size(Fetal_Brain_upsampled), size(T2decay,2)]); - -% Display computation time -fprintf('Computation time to run EPG simulations in every voxel of the image: %0.5f seconds.\n', toc); - +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +% Function that computes the T2 decay in every voxel of the fetal brain % +% volume from reference T1 and T2 maps, sequence parameters and % +% intensity non-uniformity fields, following the Extended Phase Graph % +% (EPG) formalism. % +% The EPG concept is described in details in: Weigel, M. Extended phase % +% graphs: Dephasing, RF pulses, and echoes - pure and simple. Journal % +% of Magnetic Resonance Imaging 41, 266-295 (2015). % +% https://doi.org/10.1002/jmri.24619. The associated EPG simulation % +% code from Matthias Weigel for multi-spin echo sequences % +% (cp_cpmg_epg_domain_fplus_fminus.m) used in the following can be % +% downloaded here: https://github.com/matthias-weigel/EPG. % +% % +% T2decay = compute_t2decay(Fetal_Brain_upsampled, ... % +% b1map_upsampled, ... % +% ref_T1map, ... % +% ref_T2map, ... % +% ETL, ... % +% flipAngle, ... % +% ESP, ... % +% sampling_factor); % +% % +% inputs: - Fetal_Brain_upsampled: segmented high-resolution 3D volume % +% of the fetal brain after upsampling % +% by a given sampling factor in the % +% slice thickness direction % +% - b1map_upsampled: 3D B1+ bias field map after upsampling by % +% a given sampling factor in the slice % +% thickness direction % +% - ref_T1map: reference T1 map of the fetal brain (3D) % +% - ref_T2map: reference T2 map of the fetal brain (3D) % +% - ETL: echo train length, i.e. number of 180°-RF pulses % +% - flipAngle: refocusing flip angle (in degrees) % +% - ESP: echo spacing (in ms) % +% - sampling_factor: factor by which the fetal brain volume % +% and the B1 bias field have been upsampled % +% % +% output: - T2decay: 4D matrix that combines the anatomical % +% information from the segmented high-resolution % +% fetal brain volume and the T2 decay computed in % +% every voxel of this volume % +% % +% % +% Hélène Lajous, 2021-04-20 % +% helene.lajous@unil.ch % +% % +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% + + +function T2decay = compute_t2decay(Fetal_Brain_upsampled, ... + b1map_upsampled, ... + ref_T1map, ... + ref_T2map, ... + ETL, ... + flipAngle, ... + ESP, ... + sampling_factor) + +% Input check +if nargin < 8 + error('Missing input(s).'); +elseif nargin > 8 + error('Too many inputs.'); +end + +% Unwrap maps for computation speed +unwrap_b1map = b1map_upsampled(:); +unwrap_ref_T1map = ref_T1map(:); +unwrap_ref_T2map = ref_T2map(:); + +% Find every possible combination of {b1, T1, T2} values (umap) and store +% the corresponding row where this combination first occurs (imap) +[umap, imap] = unique([unwrap_b1map, unwrap_ref_T1map, unwrap_ref_T2map], 'rows'); +% Exclude background +imap = imap(umap(:,2)~=0, :); +umap = umap(umap(:,2)~=0, :); + +uT2decay = zeros(size(umap,1), ETL); + +% Computation time +tic + +parfor i=1:size(uT2decay,1) + uT2decay(i, :) = real(cp_cpmg_epg_domain_fplus_fminus(umap(i,1).*90, ETL, umap(i,1).*flipAngle, ESP, umap(i,2), umap(i,3)))/sampling_factor; +end + +T2decay = zeros(length(Fetal_Brain_upsampled(:)), ETL, 'single'); +for i=1:size(uT2decay,1) + j = find(unwrap_b1map==umap(i,1) & unwrap_ref_T1map==umap(i,2) & unwrap_ref_T2map==umap(i,3)); + for k=1:length(j) + T2decay(j(k),:) = uT2decay(i,:); + end +end +T2decay = reshape(T2decay, [size(Fetal_Brain_upsampled), size(T2decay,2)]); + +% Display computation time +time2=toc; +fprintf('Computation time to run EPG simulations in every voxel of the image: %0.5f seconds.\n', time2); + end \ No newline at end of file diff --git a/Utilities/cp_cpmg_epg_domain_fplus_fminus.m b/Utilities/cp_cpmg_epg_domain_fplus_fminus.m new file mode 100644 index 0000000..3e3c936 --- /dev/null +++ b/Utilities/cp_cpmg_epg_domain_fplus_fminus.m @@ -0,0 +1,258 @@ +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +% This script is a generalization of the EPG simulation code from % +% Matthias Weigel for multi-spin echo sequences where the refocusing % +% flip angle is an additional input parameter. % +% % +% (c) Matthias Weigel (2015) % +% https://github.com/matthias-weigel/EPG % +% http://epg.matthias-weigel.net % +% The code is based on the depiction and discussion of Extended Phase % +% Graphs in the following publication ("EPG-R"): % +% Weigel M. Extended Phase Graphs: Dephasing, RF Pulses, and Echoes - % +% Pure and Simple. J Magn Reson Imaging 2015; 41: 266-295. % +% DOI: 10.1002/jmri.24619 % +% % +% Modified by our collaborator Tom Hilbert (TH, 2018) % +% % +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% + + +function [F0_vector_out,Xi_F_out,Xi_Z_out,Xi_F_all_out,Xi_Z_all_out] = cp_cpmg_epg_domain_fplus_fminus (alpha_exc,N_in,alpha_in,ESP_in,T1_in,T2_in) + +% [F0_vector,Xi_F,Xi_Z,Xi_F_all,Xi_Z_all] = cp_cpmg_epg_domain_fplus_fminus (N,alpha,ESP,T1,T2) +% +% Calculates the Extended Phase Graph (EPG) over one TR / one echo train for multi spin echo (multi SE) sequences obeying the Carr-Purcell (CP) or Carr-Purcell-Meiboom-Gill (CPMG) conditions +% The particular type (CP/CPMG) of the (idealized) spin echo sequence is specified in the hard coded settings below +% Common CPMG based imaging sequences have acronyms such as SE, TSE, FSE or RARE. +% This code uses the Fourier based EPG domains F+(+k), F-(-k), Z(+k), see the mentioned review paper below +% +% IN : N : Number of Repetitions of RF refocusing flip angles (= number of RF refocusing pulses for one given flip angle alpha) +% alpha: Constant flip angle or flip angle array of RF refocusing pulses in deg to be repeated N times +% ESP : Echo spacing (or refocusing RF spacing), same unit as T1,T2 +% T1 : Longitudinal relaxation time, same unit as ESP,T2 +% T2 : Transverse relaxation time, same unit as ESP,T1 +% +% OUT: F0_vector: Vector of resulting F0 states ("echo intensities") +% Xi_F : State evolution matrix of all transverse F states at each echo time (only the states generating the primary pathways that can only be measured) +% Xi_Z : State evolution matrix of all longitudinal Z states at each echo time (only the states generating the primary pathways that can only be measured) +% Xi_F_all : State evolution matrix of all transverse F states after each refocusing pulse and at each echo time (in alternating order, includes also the 'hidden' secondary pathway states - see e.g. EPG-R) +% Xi_Z_all : State evolution matrix of all longitudinal Z states after each refocusing pulse and at each echo time (in alternating order, includes also the 'hidden' secondary pathway states - see e.g. EPG-R) +% +% +% WRITTEN IN 2014 & 2015 by MATTHIAS WEIGEL (epg@matthias-weigel.net) +% Last modified in 07/2015 (Version 1.3) +% +% Current affiliation: Radiological Physics, University Hospital Basel, Basel, Switzerland +% Past affiliation: Medical Physics, University Medical Center Freiburg, Freiburg, Germany +% Past^2 affiliation: Biophysics, University of Wuerzburg, Wuerzburg, Germany +% +% This code resides at "http://epg.matthias-weigel.net" +% The code is based on the depiction and discussion of Extended Phase Graphs in the following publication ("EPG-R"): +% +% Weigel M. J Magn Reson Imaging 2015; 41: 266-295. DOI: 10.1002/jmri.24619 +% "Extended Phase Graphs: Dephasing, RF Pulses, and Echoes - Pure and Simple" +% +% Studying and using this code means to acknowledge Matthias Weigel's months of cursing and weeping ... ;-) +% ... by citing the above mentioned review paper. Thank you :-) +% +% Intentionally built on my software "ssfp_epg_domain_fplus_fminus.m" (for FISP, FLASH, TRUFI, ...) +% +% +% Further comments in regard to the code: +% - For transverse magnetization, this code works in both the F+ and F- domain -- see EPG-R, particularly the comments in the "software coding section" +% Therefore, both the F+(0) and F-(0) state are assessed (in the Omega matrix), however, only the F+(0) state is stored in the evolution matrix Xi (F-(0) is superfluous) +% - This code is only roughly optimized for speed or for efficiency: a dedicated code (particularly in C++) would be much faster; and also quite shorter without the "gadgets" +% - This code was roughly validated by comparing selected examples with ... +% ... known examples & the review paper EPG-R +% ... historical C++ code of mine +% ... the software "cp_cpmg_epg_domain_freal.m" for CP and CPMG based sequences + + +% Hard coded settings to modify the type of the simulated multi SE sequence - Typical seetings: +% --------------------------------------------------------------------------------------------------------------------- +% TSE / FSE / RARE : refoc_mode = +1 +% init_90pad2 = 1 (most of the TSE will use an initial 90+fa/2 refoc pulse, see also below) +% --------------------------------------------------------------------------------------------------------------------- +refoc_mode = +1; % +1 = CPMG mode (Carr-Purcell-Meiboom-Gill: Excitation has RF phase 90deg, refocusing RF pulses have RF phase 0deg, see also EPG-R) + % -1 = CP mode (Carr-Purcell : Excitation and refocusing RF pulses have the same RF phase 0deg, also called 'anti-CPMG', see also EPG-R) + +init_90pad2 = 0; % 0 = take flip angle(s) (arrays) as specified + % 1 = the first refoc flip angle is altered to be 90+fa/2, which prepares the spin system close to the static PSS (SPSS) + % only performed for ETL>1 (multi SE) ; here, fa is the second flip angle fa(2) + % basically all TSE will do that to gain signal and improve stability - see literature + + + +% Initialization of various parameters regarding EPG matrix operators, output space, ... +% --------------------------------------------------------------------------------------------------------------------- + +% Determine effective alpha arrays and dedicated length parameters +% By definition, all refocusing pulses have an RF phase of 0deg (around +x) +alpha_in = alpha_in/180.0*pi; % Convert flip angle(array) into [rad] +fa = alpha_in; % "alpha" is already a function in Matlab: not good +fa_exc = alpha_exc/180.0*pi; % TH + +% for pn = 2:N_in % The given flip angle (array) is repeated N_in times +% fa = [fa,alpha_in]; %#ok +% end +fa = repmat(fa,[N_in, 1]); % TH for codegen + +% Some auxiliary variables +N = length(fa); +Nt2 = 2*N; +Nt2p1 = Nt2+1; +Nt2p2 = Nt2+2; + +if ((init_90pad2 == 1) && (N>1)) % initial 90+fa/2 functionality + fa(1) = (pi+fa(2))/2; % flip angles are already in [rad] ! +end + + +% Define relaxation operator E elements for one calculation interval = ESP/2 +if (T1_in == 0) % T1=0 is equivalent to T1=Infinity=no relaxation : I love that ;-) + E1 = 1.0; +else + E1 = exp(-ESP_in/T1_in/2.0); % Eq.[23] in EPG-R +end + +if (T2_in == 0) % T2=0 is equivalent to T2=Infinity=no relaxation : I love that ;-) + E2 = 1.0; +else + E2 = exp(-ESP_in/T2_in/2.0); % Eq.[23] in EPG-R +end + + +% Generate state matrices Omega before and after RF: Eq.[26] in EPG-R +Omega_preRF = zeros(3,Nt2p1); % CP/CPMG shifts by +2 per interval +Omega_preRF = complex(Omega_preRF,Omega_preRF); % TH for codegen +Omega_postRF = zeros(3,Nt2p1); % CP/CPMG shifts by +2 per interval +Omega_postRF = complex(Omega_postRF,Omega_postRF); % TH for codegen + +% Generate state evolution matrices Xi_F and Xi_Z: Eqs.[27] in EPG-R +% Here, they will (only) contain all post-RF states == output variables +Xi_F_all_out = zeros(4*N+1,Nt2); % Xi_F with F+ and F- states, Eq.[27a] in EPG-R +Xi_F_all_out = complex(Xi_F_all_out,Xi_F_all_out); % TH for codegen +Xi_Z_all_out = zeros(Nt2p1,Nt2); % Xi_Z with Z states, upper half of Eq.[27b] in EPG-R +Xi_Z_all_out = complex(Xi_Z_all_out,Xi_Z_all_out); % TH for codegen + + +% Starting with freshly excited equilibrium magnetization after a 90deg RF pulse ("excitation pulse") +% All magnetization has flown into an initial F0 state (FID); its magnitude is 1, +% However, its phase depends on the RF phase of the RF pulse, see EPG-R and all relevant SE literature +% It is RF phase excitation pulse = 0deg for a CP experiment (out-of-phase refocusing, see EPG-R) +% It is RF phase excitation pulse = 90deg for a CPMG experiment (in-phase refocusing, see EPG-R) +if (refoc_mode == +1) % CPMG : +% Omega_postRF(1,1) = 1; % magnetization resides on +x for fa=+90deg excitation +% Omega_postRF(2,1) = 1; % remember: the Omega state matrix contains both F+(0) and F-(0)=(F+(0))* + Omega_postRF(1,1) = -sin(-fa_exc); % TH - Non perfect excitation pulse + Omega_postRF(2,1) = -sin(-fa_exc); % TH + Omega_postRF(3,1) = cos(-fa_exc); % TH +else % CP : + Omega_postRF(1,1) = -1i; % magnetization resides on -y for fa=+90deg excitation + Omega_postRF(2,1) = +1i; % remember: the Omega state matrix contains both F+(0) and F-(0)=(F+(0))* +end + + + +% Starting the calculation of EPG states over time - "flow of magnetization in the EPG" +% --------------------------------------------------------------------------------------------------------------------- +T = zeros(3,3); %TH for codegen +T = complex(T,T);%TH for codegen +for pn = 1:N % Loop over RF pulse #pn + + % Generate T matrix operator elements (RF pulse representation): Eq.[15] or Eq.[18] in EPG-R + % It is always phi=0, thus, all exponential phase terms vanish in the T matrix: effective matrix Tx + % The T matrix has to be updated within the RF pulse loop, because the flip angle will be often non-constant: + T(1,1) = cos(fa(pn)/2)^2; + T(1,2) = sin(fa(pn)/2)^2; + T(1,3) = -1.0i * sin(fa(pn) ) ; + T(2,1) = sin(fa(pn)/2)^2; + T(2,2) = cos(fa(pn)/2)^2; + T(2,3) = +1.0i * sin(fa(pn) ) ; + T(3,1) = -0.5i * sin(fa(pn) ) ; + T(3,2) = +0.5i * sin(fa(pn) ) ; + T(3,3) = cos(fa(pn) ) ; + + + % In the following, further loops over the states' dephasing k will be needed to realize operators T, E, and S + % --> Note that we deal with integral k units here, see EPG-R + % Intead of "for loops over k", Matlab's "indexing capability" will be used, which is much faster for large values + % To improve efficiency, only states are calculated that could be generated at all in the EPG before + pn2 = 2*pn; + k = 1:(pn2-1); % "k loop index" with limit pn + kp1 = 1:(pn2); % "k loop index" with limit pn+1 + kp2 = 1:(pn2+1); + + + % E matrix operator: Experienced relaxation from the states for the next ESP/2 + % Expand E matrix relations from Eqs.[23] and [24] in EPG-R + Omega_preRF(1:2,k) = E2 * Omega_postRF(1:2,k); % T2 relaxation for F+ and F- + Omega_preRF(3,k(2:end)) = E1 * Omega_postRF(3,k(2:end)); % T1 relaxation for Z with k>0 + Omega_preRF(3,1) = E1 * Omega_postRF(3,1) + 1 - E1; % T1 recovery for Z with k=0 + + + % S operator: Dephasing / shifting of F+ and F- states during the next ESP/2 + % With integral k units (see EPG-R): Delta(k) = +1 + Omega_preRF(1,k+1) = Omega_preRF(1,k); % dephase F+ (this expression only works with indexing !! no overwriting then) + Omega_preRF(2,k) = Omega_preRF(2,k+1); % dephase F- + Omega_preRF(1,1) = conj(Omega_preRF(2,1)); % generate conjugate pendant F+0 from F-0, see EPG-R + + + % T matrix operator: RF pulse acting, mixing of F+, F-, and Z states + % Expand T matrix relations from Eq.[15] or Eq.[18] in EPG-R + Omega_postRF(:,kp1) = T * Omega_preRF(:,kp1); % indexed matrix product ! faster than explicit writing out + + + % Store these post-RF states of the current Omega state matrix in the Xi state evolution matrices + Xi_F_all_out(Nt2p2-kp1,pn2-1) = Omega_postRF(1,kp1); + Xi_F_all_out(Nt2+kp1(2:end),pn2-1) = conj(Omega_postRF(2,kp1(2:end))); % this is a matter of definition, EPG-R defines Xi_F as a coherent set fully in the F+ domain, you could omit the 'conj' + Xi_Z_all_out(Nt2p2-kp1,pn2-1) = Omega_postRF(3,kp1); + + + % E matrix operator: Experienced relaxation from the states for the next ESP/2 + % Expand E matrix relations from Eqs.[23] and [24] in EPG-R + Omega_postRF(1:2,kp1) = E2 * Omega_postRF(1:2,kp1); % T2 relaxation for F+ and F- + Omega_postRF(3,kp1(2:end)) = E1 * Omega_postRF(3,kp1(2:end)); % T1 relaxation for Z with k>0 + Omega_postRF(3,1) = E1 * Omega_postRF(3,1) + 1 - E1; % T1 recovery for Z with k=0 + + + % S operator: Dephasing / shifting of F+ and F- states during the next ESP/2 + % With integral k units (see EPG-R): Delta(k) = +1 + Omega_postRF(1,kp1+1) = Omega_postRF(1,kp1); % dephase F+ (this expression only works with indexing !! no overwriting then) + Omega_postRF(2,kp1) = Omega_postRF(2,kp1+1); % dephase F- + Omega_postRF(1,1) = conj(Omega_postRF(2,1)); % generate conjugate pendant F+0 from F-0, see EPG-R + + + % Store these echo states of the current Omega state matrix in the Xi state evolution matrices + Xi_F_all_out(Nt2p2-kp2,2*pn) = Omega_postRF(1,kp2); + Xi_F_all_out(Nt2+kp2(2:end),2*pn) = conj(Omega_postRF(2,kp2(2:end))); % this is a matter of definition, EPG-R defines Xi_F as a coherent set fully in the F+ domain, you could omit the 'conj' ; kp1 would also work, since the highest F- cannot be occupied + Xi_Z_all_out(Nt2p2-kp2,2*pn) = Omega_postRF(3,kp2); % kp1 would also work, since the highest Z cannot be occupied + +end % of for loop over pn + + +% Output: "make nice zeros": +% Erase some float point accuracy errors +Xi_F_all_out(abs(Xi_F_all_out) 4 + error('Too many inputs.'); +end + + +switch interpolation_method + case 'nearest' + Volume_downsampled = imresize(Volume, [size(Volume,1)/downsampling_read size(Volume,2)/downsampling_phase], 'nearest'); + case 'linear' + Volume_downsampled = imresize(Volume, [size(Volume,1)/downsampling_read size(Volume,2)/downsampling_phase], 'bilinear'); +end + +% Display message for debugging +sprintf('Volume downsampled.') diff --git a/Utilities/fNDFilter.m b/Utilities/fNDFilter.m index dee6f04..403a3e0 100644 --- a/Utilities/fNDFilter.m +++ b/Utilities/fNDFilter.m @@ -1,77 +1,77 @@ -%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% -% makeFilter Creates an N-D filter % -% % -% flt_kdat = fNDFilter(kdat, flt_name); % -% % -% INPUT: kdat N-D data to filter % -% flt_name Filter name to use % -% dims Array of dimensions to filter % -% % -% OUTPUT: flt_kdat Filtered N-D data % -% % -% % -% (c) Jerome Yerly, 2013 % -% yerlyj.mri@gmail.com % -% % -%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% - - -function flt_kdat = fNDFilter(kdat, flt_name, dims) - - % Check input and output arguments - narginchk(2,3); % Between two and three input arguments - nargoutchk(1,1); % 1 output agrument - - ndim = ndims(kdat); % Number of dimensions - - if nargin < 3 - dims = 1:ndim; % Filter all dimensions by default - else - % Verify that the array dims contains feasible values, i.e. between 1 and ndim - if min(dims) < 1 && max(dims) > ndim - error('The array dims can only contain values between 1 and %d. min(dims)= %d and max(dims)= %d',ndim,max(dims),min(dims)); - end - end - - dims = unique(dims); % Make sure that values in dims are unique - nfilter = length(dims); % Number of dimensions to filter - - % Create a distance matrix - x = cell(ndim,1); - for i = 1:ndim - x{i} = linspace( -1, 1, size(kdat,i) ); - end - X = meshgrid_ND(x); - - kr = zeros(size(X{1})); - for i = 1:nfilter - dim = dims(i); - kr = kr + X{dim}.^2; - end - kr = sqrt(kr); - - % Define filter - if ( strcmpi(flt_name,'von Hann') == 1 ), - tmp_alf = 0.50; - flt = (1 - tmp_alf) + tmp_alf * cos( pi * kr ); - elseif ( strcmpi(flt_name,'Hamming') == 1 ), - tmp_alf = 0.46; - flt = (1 - tmp_alf) + tmp_alf * cos( pi * kr ); - elseif ( strcmpi(flt_name,'Fermi') == 1 ), - % Suggested values: - % - tmp_fR = 0.85 - % - tmp_fW = 23 - tmp_fR = 0.85; % cutoff at 50% max - tmp_fW = 23; % 1/transition width - flt = 1 ./ ( 1 + exp( -tmp_fW * (tmp_fR - kr) ) ); - else - flt = ones ( size ( kdat ) ); - end - - % Filter k-space - flt_kdat = kdat .* flt; - - % Return - return - +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +% makeFilter Creates an N-D filter % +% % +% flt_kdat = fNDFilter(kdat, flt_name); % +% % +% INPUT: kdat N-D data to filter % +% flt_name Filter name to use % +% dims Array of dimensions to filter % +% % +% OUTPUT: flt_kdat Filtered N-D data % +% % +% % +% (c) Jerome Yerly, 2013 % +% yerlyj.mri@gmail.com % +% % +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% + + +function flt_kdat = fNDFilter(kdat, flt_name, dims) + + % Check input and output arguments + narginchk(2,3); % Between two and three input arguments + nargoutchk(1,1); % 1 output agrument + + ndim = ndims(kdat); % Number of dimensions + + if nargin < 3 + dims = 1:ndim; % Filter all dimensions by default + else + % Verify that the array dims contains feasible values, i.e. between 1 and ndim + if min(dims) < 1 && max(dims) > ndim + error('The array dims can only contain values between 1 and %d. min(dims)= %d and max(dims)= %d',ndim,max(dims),min(dims)); + end + end + + dims = unique(dims); % Make sure that values in dims are unique + nfilter = length(dims); % Number of dimensions to filter + + % Create a distance matrix + x = cell(ndim,1); + for i = 1:ndim + x{i} = linspace( -1, 1, size(kdat,i) ); + end + X = meshgrid_ND(x); + + kr = zeros(size(X{1})); + for i = 1:nfilter + dim = dims(i); + kr = kr + X{dim}.^2; + end + kr = sqrt(kr); + + % Define filter + if ( strcmpi(flt_name,'von Hann') == 1 ), + tmp_alf = 0.50; + flt = (1 - tmp_alf) + tmp_alf * cos( pi * kr ); + elseif ( strcmpi(flt_name,'Hamming') == 1 ), + tmp_alf = 0.46; + flt = (1 - tmp_alf) + tmp_alf * cos( pi * kr ); + elseif ( strcmpi(flt_name,'Fermi') == 1 ), + % Suggested values: + % - tmp_fR = 0.85 + % - tmp_fW = 23 + tmp_fR = 0.85; % cutoff at 50% max + tmp_fW = 23; % 1/transition width + flt = 1 ./ ( 1 + exp( -tmp_fW * (tmp_fR - kr) ) ); + else + flt = ones ( size ( kdat ) ); + end + + % Filter k-space + flt_kdat = kdat .* flt; + + % Return + return + end \ No newline at end of file diff --git a/Utilities/fZeroPadnDArray.m b/Utilities/fZeroPadnDArray.m index a02c080..29f0bb2 100644 --- a/Utilities/fZeroPadnDArray.m +++ b/Utilities/fZeroPadnDArray.m @@ -1,78 +1,78 @@ -%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% -% fZeroPadnDArray Zero-pads an N-D array % -% % -% arrayOut = fZeroPadnDArray(arrayIn, newN); % -% % -% % -% (c) Jerome Yerly 2013 % -% yerlyj.mri@gmail.com % -% % -%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% - - -function arrayOut = fZeroPadnDArray( arrayIn, newN ) - - % Number of dimension in arrayIn - Ndim = ndims(arrayIn); - - % newN should be an array of length equal to the number of dimension in - % arrayIn - if length(newN) ~= Ndim - return; - end - - % Get dimension of array to pad - oldN = size( arrayIn ); - - if isreal(arrayIn) - % Initialize the padded array - arrayOut = zeros( newN, class(arrayIn) ); - else - % Initialize the padded array - arrayOut = complex(zeros( newN, class(arrayIn) )); - end - - % Calculate the old and new indice corresponding to the centre of the - % data - idxCtr_old = floor( oldN / 2 ) + 1; - idxCtr_new = floor( newN / 2 ) + 1; - - % Calculate the start indices where to place the centered data - idxStart = idxCtr_new - idxCtr_old + 1; - - % Calculate the end indices where to place the centered data - idxEnd = idxStart + oldN - 1; - - % Loop through all dimension to generate indices - for i = 1:Ndim - - % Construct command - str = sprintf('idx%d = idxStart(%d) : idxEnd(%d);', i, i, i); - - % Eval command - eval(str); - - end - - % Initialize string for command - tmpStr = ''; - - % Loop through all dimension to construct the next command - for i = 1:Ndim - - if i == 1 - % Construct string for next command - tmpStr = sprintf('idx%d ', i); - else - % Construct string for next command - tmpStr = sprintf('%s, idx%d ', tmpStr, i); - end - end - - % Construct command - str = sprintf('arrayOut(%s) = arrayIn;', tmpStr); - - % Eval command - eval(str); - +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +% fZeroPadnDArray Zero-pads an N-D array % +% % +% arrayOut = fZeroPadnDArray(arrayIn, newN); % +% % +% % +% (c) Jerome Yerly 2013 % +% yerlyj.mri@gmail.com % +% % +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% + + +function arrayOut = fZeroPadnDArray( arrayIn, newN ) + + % Number of dimension in arrayIn + Ndim = ndims(arrayIn); + + % newN should be an array of length equal to the number of dimension in + % arrayIn + if length(newN) ~= Ndim + return; + end + + % Get dimension of array to pad + oldN = size( arrayIn ); + + if isreal(arrayIn) + % Initialize the padded array + arrayOut = zeros( newN, class(arrayIn) ); + else + % Initialize the padded array + arrayOut = complex(zeros( newN, class(arrayIn) )); + end + + % Calculate the old and new indice corresponding to the centre of the + % data + idxCtr_old = floor( oldN / 2 ) + 1; + idxCtr_new = floor( newN / 2 ) + 1; + + % Calculate the start indices where to place the centered data + idxStart = idxCtr_new - idxCtr_old + 1; + + % Calculate the end indices where to place the centered data + idxEnd = idxStart + oldN - 1; + + % Loop through all dimension to generate indices + for i = 1:Ndim + + % Construct command + str = sprintf('idx%d = idxStart(%d) : idxEnd(%d);', i, i, i); + + % Eval command + eval(str); + + end + + % Initialize string for command + tmpStr = ''; + + % Loop through all dimension to construct the next command + for i = 1:Ndim + + if i == 1 + % Construct string for next command + tmpStr = sprintf('idx%d ', i); + else + % Construct string for next command + tmpStr = sprintf('%s, idx%d ', tmpStr, i); + end + end + + % Construct command + str = sprintf('arrayOut(%s) = arrayIn;', tmpStr); + + % Eval command + eval(str); + end \ No newline at end of file diff --git a/Utilities/fft2c.m b/Utilities/fft2c.m index a31306a..422bd25 100644 --- a/Utilities/fft2c.m +++ b/Utilities/fft2c.m @@ -1,21 +1,21 @@ -%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% -% Two-dimensional Fourier transform % -% % -% % -% (c) Christopher W. Roy, 2018-12-04 % -% fetal.xcmr@gmail.com % -% https://github.com/cwroy/Fetal-XCMR/ % -% Roy, C.W. et al. Fetal XCMR: a numerical phantom for fetal % -% cardiovascular magnetic resonance imaging. Journal of Cardiovascular % -% Magnetic Resonance 21, 29 (2019). % -% https://doi.org/10.1186/s12968-019-0539-2 % -% % -%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% - - -function x = fft2c(x) - -fctr = size(x,1)*size(x,2); -x = 1/sqrt(fctr)*fftshift(fft2(ifftshift(x))); - +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +% Two-dimensional Fourier transform % +% % +% % +% (c) Christopher W. Roy, 2018-12-04 % +% fetal.xcmr@gmail.com % +% https://github.com/cwroy/Fetal-XCMR/ % +% Roy, C.W. et al. Fetal XCMR: a numerical phantom for fetal % +% cardiovascular magnetic resonance imaging. Journal of Cardiovascular % +% Magnetic Resonance 21, 29 (2019). % +% https://doi.org/10.1186/s12968-019-0539-2 % +% % +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% + + +function x = fft2c(x) + +fctr = size(x,1)*size(x,2); +x = 1/sqrt(fctr)*fftshift(fft2(ifftshift(x))); + end \ No newline at end of file diff --git a/Utilities/ifft2c.m b/Utilities/ifft2c.m index 4ce02cd..f5f3eca 100644 --- a/Utilities/ifft2c.m +++ b/Utilities/ifft2c.m @@ -1,21 +1,21 @@ -%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% -% Two-dimensional inverse Fourier transform % -% % -% % -% (c) Christopher W. Roy, 2018-12-04 % -% fetal.xcmr@gmail.com % -% https://github.com/cwroy/Fetal-XCMR/ % -% Roy, C.W. et al. Fetal XCMR: a numerical phantom for fetal % -% cardiovascular magnetic resonance imaging. Journal of Cardiovascular % -% Magnetic Resonance 21, 29 (2019). % -% https://doi.org/10.1186/s12968-019-0539-2 % -% % -%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% - - -function x = ifft2c(x) - -fctr = size(x,1)*size(x,2); -x = sqrt(fctr)*fftshift(ifft2(ifftshift(x))); - +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +% Two-dimensional inverse Fourier transform % +% % +% % +% (c) Christopher W. Roy, 2018-12-04 % +% fetal.xcmr@gmail.com % +% https://github.com/cwroy/Fetal-XCMR/ % +% Roy, C.W. et al. Fetal XCMR: a numerical phantom for fetal % +% cardiovascular magnetic resonance imaging. Journal of Cardiovascular % +% Magnetic Resonance 21, 29 (2019). % +% https://doi.org/10.1186/s12968-019-0539-2 % +% % +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% + + +function x = ifft2c(x) + +fctr = size(x,1)*size(x,2); +x = sqrt(fctr)*fftshift(ifft2(ifftshift(x))); + end \ No newline at end of file diff --git a/Utilities/interleaved_scheme.m b/Utilities/interleaved_scheme.m index f3e27b6..1074608 100644 --- a/Utilities/interleaved_scheme.m +++ b/Utilities/interleaved_scheme.m @@ -1,40 +1,40 @@ -%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% -% Function that returns the indexes of the slices to be acquired in an % -% interleaved manner depending on the number of slices. % -% % -% interleavedSlices_index = interleaved_scheme(NbSlices); % -% % -% input: - NbSlices: number of slices % -% % -% output: - interleavedSlices_index: list of indexes of the slices % -% corresponding to an interleaved % -% acquisition scheme % -% % -% % -% Hélène Lajous, 2021-04-21 % -% helene.lajous@unil.ch % -% % -%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% - - -function interleavedSlices_index = interleaved_scheme(NbSlices) - -% Input check -if nargin < 1 - error('Missing input(s).'); -elseif nargin > 1 - error('Too many inputs.'); -end - -% Implement interleaved slice acquisition scheme -if mod(NbSlices,2)==0 - interleavedSlices_index = [2:2:NbSlices, 1:2:NbSlices-1]; -else - interleavedSlices_index = [2:2:NbSlices-1, 1:2:NbSlices]; -end - -% Display message for debugging -sprintf('Slices will be acquired in the following order:') -fprintf(' %d\n', interleavedSlices_index(:)) - +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +% Function that returns the indexes of the slices to be acquired in an % +% interleaved manner depending on the number of slices. % +% % +% interleavedSlices_index = interleaved_scheme(NbSlices); % +% % +% input: - NbSlices: number of slices % +% % +% output: - interleavedSlices_index: list of indexes of the slices % +% corresponding to an interleaved % +% acquisition scheme % +% % +% % +% Hélène Lajous, 2021-04-21 % +% helene.lajous@unil.ch % +% % +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% + + +function interleavedSlices_index = interleaved_scheme(NbSlices) + +% Input check +if nargin < 1 + error('Missing input(s).'); +elseif nargin > 1 + error('Too many inputs.'); +end + +% Implement interleaved slice acquisition scheme +if mod(NbSlices,2)==0 + interleavedSlices_index = [2:2:NbSlices, 1:2:NbSlices-1]; +else + interleavedSlices_index = [2:2:NbSlices-1, 1:2:NbSlices]; +end + +% Display message for debugging +sprintf('Slices will be acquired in the following order:') +fprintf(' %d\n', interleavedSlices_index(:)) + end \ No newline at end of file diff --git a/Utilities/io_orientation.m b/Utilities/io_orientation.m new file mode 100644 index 0000000..5a11919 --- /dev/null +++ b/Utilities/io_orientation.m @@ -0,0 +1,93 @@ +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +% Function that reads the segmented high-resolution anatomical MR % +% images of the fetal brain from which the simulated images will be % +% derived at a given gestational age (GA). % +% Here, we consider segmented high-resolution images from Gholipour, A. % +% et al. A normative spatiotemporal MRI atlas of the fetal brain for % +% automatic segmentation and analysis of early brain growth. Scientific % +% Reports 7, 476 (2017). https://doi.org/10.1038/s41598-017-00525-w % +% % +% ornt = io_orientation(affine, tolerance); % +% % +% inputs: - affine: transformation affine matrix from the anatomical % +% fetal brain model % +% - tolerance: threshold below which SVD values of the affine % +% are considered zero % +% % +% output: - ornt: array of the closest output axis (R,A,S) and % +% direction (1 if the input axis is in the same % +% direction as the corresponding output axis and -1 if % +% it is in the opposite direction % +% % +% % +% Hélène Lajous, 2023-01-30 % +% helene.lajous@unil.ch % +% % +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% + + +function ornt = io_orientation(affine, varargin) + +% Input check +if nargin < 1 + error('Missing input(s).'); +elseif nargin==1 + tolerance = "None"; %default value for the tolerance threshold +else + if numel(varargin) > 0 %optional input arguments are provided + if numel(varargin) < 2 + error('You need to provide optional input arguments as ''ParameterName''-''ParameterValue'' pairs.'); + end + switch varargin{1} + case 'tolerance' + tolerance = varargin{2}; + otherwise + error('Unexpected ''ParameterName'' input: %s\n', varargin{1}); + end + end +end + +% Extract the underlying rotation, zoom, shear matrix +RZS = affine(1:size(affine,1)-1, 1:size(affine,2)-1); +zooms = sqrt(sum(RZS .* RZS, 1)); +% Zooms can be zero, in which case all elements in the column are zero, and +% we can leave them as they are +zooms(zooms==0) = 1; +RS = RZS ./ zooms; +% Transform below is polar decomposition, returning the closest shearless +% matrix R to RS +[P, S, Qs] = svd(RS); +% Threshold the singular values to determine the rank +if tolerance=="None" + tolerance = max(S) * max(size(RS)) * eps(class(S)); +end +keep = svd(RS)' > tolerance; +R = P(:,keep) * Qs(keep,keep); + +% The matrix R is such that the scalar product between R and R*T is +% projection onto the columns of P(:,keep) and the scalar product between +% R.T and R is the projection onto the rows of Qs(keep). R gives rotation +% of the unit input vectors to output coordinates. Therefore, the row index +% of abs max R(:,N) is the output axis changing most as input axis N +% changes. In case there are ties, we choose the axes iteratively, removing +% used axes from consideration as we go. +ornt = NaN(size(affine,2)-1,2); +for in_ax=1:size(affine,2)-1 + col = R(:,in_ax); + if any(col) + [Max,indexMax] = max(abs(col)); + out_ax = indexMax; + ornt(in_ax,1) = out_ax; + assert(col(out_ax)~=0) + if col(out_ax)<0 + ornt(in_ax,2) = -1; + else + ornt(in_ax,2) = 1; + end + % Remove the identified axis from further consideration, by zeroing + % out the corresponding row in R + R(out_ax, :) = 0; + end +end + +end \ No newline at end of file diff --git a/Utilities/mat2nii.m b/Utilities/mat2nii.m new file mode 100644 index 0000000..7d9662c --- /dev/null +++ b/Utilities/mat2nii.m @@ -0,0 +1,85 @@ +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +% Function that converts a matlab matrix into an image in .nifti format. % +% % +% varargout = mat2nii(simulated_image, ... % +% voxSize_simu, ... % +% model_niiinfo, ... % +% out_filename) % +% % +% inputs: - simulated_image: simulated FSE image stored as .mat file % +% - voxSize_simu: voxel size of the simulated image % +% - model_niiinfo: information (nifti header) of the original % +% anatomical model from which the simulated % +% image is derived % +% - out_filename: output filename (including path) of the % +% generated nifti image % +% % +% output: The Matlab matrix simulated_image is saved in nifti format in % +% out_filename. % +% % +% % +% Oscar Esteban, Hélène Lajous, 2023-01-20 % +% helene.lajous@unil.ch % +% % +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% + + +function varargout = mat2nii(simulated_image, ... + voxSize_simu, ... + model_niiinfo, ... + out_filename) + +% Input check +if nargin < 4 + error('Missing input(s).'); +elseif nargin > 4 + error('Too many inputs.'); +end + +% Retrieve information on the original anatomical model from its header +orientation_matrix_model = model_niiinfo.Transform.T'; + +% Compute the center of the original anatomical images in intrinsic +% coordinates +center_vox_model = (model_niiinfo.ImageSize - 1) / 2; +center_vox_model(4) = 1; + +% Compute the center of the original anatomical images in world coordinates +center_mm_model = orientation_matrix_model * center_vox_model'; + +% Initialize the orientation matrix of the simulated images +rotations_sim = sign(orientation_matrix_model(1:3, 1:3)) .* voxSize_simu; + +% Compute the center of the simulated images in intrinsic coordinates +center_vox_sim = (size(simulated_image) - 1) / 2; + +% Compute the center of the simulated images in world coordinates +center_mm_sim = rotations_sim * center_vox_sim'; + +% Offset +offset = center_mm_sim - center_mm_model(1:3); + +% Define the orientation matrix of the simulated images to be in the same +% space as the original anatomical model +orientation_matrix_simu = eye(size(orientation_matrix_model)); +orientation_matrix_simu(1:3, 1:3) = rotations_sim; +orientation_matrix_simu(1:3, 4) = -offset; + +% Update the information of the simulated images to save them in nifti +% format with a proper nifti header + +newinfo = model_niiinfo; +newinfo.Filename = out_filename; +newinfo.Transform.T = orientation_matrix_simu'; +newinfo.TransformName = "Qform"; +newinfo.ImageSize = size(simulated_image); +newinfo.PixelDimensions = voxSize_simu; +if isa(simulated_image, model_niiinfo.Datatype)==0 + newinfo.Datatype = class(simulated_image); +end +niftiwrite(simulated_image, out_filename, newinfo, 'Compressed', true); + +% Output +varargout{1} = out_filename; + +end \ No newline at end of file diff --git a/Utilities/meshgrid_ND.m b/Utilities/meshgrid_ND.m index 8fadd38..68d4671 100644 --- a/Utilities/meshgrid_ND.m +++ b/Utilities/meshgrid_ND.m @@ -1,51 +1,51 @@ -%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% -% meshgrid_ND Creates rectangular grid in N-D space % -% % -% X = meshgrid_ND(x); % -% Replicates the grid vectors in cell array x to produce a full grid % -% % -% INPUT: x Cell array containing the grid vectors (N vectors) % -% % -% OUTPUT: X Cell array containing the output coordinate arrays % -% of dimension N % -% % -% % -% (c) Jerome Yerly 2013 % -% yerlyj.mri@gmail.com % -% % -%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% - - -function X = meshgrid_ND(x) - -ndim = length(x); % Number of dimension -X = cell(ndim,1);% Create cell array to contain all the grids -dimSizes = zeros(ndim,1); % Create vector to contain thd dimension of each dimension - -% Get size of each dimension -for i = 1:ndim - dimSizes(i) = numel(x{i}); -end - -% Create grids -for i = 1:ndim - - % Make sure x is a vector along dimension i. - xx = x{i}; - vecShape = ones(1,ndim); - vecShape(i) = dimSizes(i); - xx = reshape(xx(:),vecShape); - - % Repeat vector to create grid - tmp = dimSizes; - tmp(i) = 1; - - if ~isscalar(xx) - xx = repmat(xx,tmp(:)'); - end - - % Store in cell array - X{i} = xx; -end - +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +% meshgrid_ND Creates rectangular grid in N-D space % +% % +% X = meshgrid_ND(x); % +% Replicates the grid vectors in cell array x to produce a full grid % +% % +% INPUT: x Cell array containing the grid vectors (N vectors) % +% % +% OUTPUT: X Cell array containing the output coordinate arrays % +% of dimension N % +% % +% % +% (c) Jerome Yerly 2013 % +% yerlyj.mri@gmail.com % +% % +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% + + +function X = meshgrid_ND(x) + +ndim = length(x); % Number of dimension +X = cell(ndim,1);% Create cell array to contain all the grids +dimSizes = zeros(ndim,1); % Create vector to contain thd dimension of each dimension + +% Get size of each dimension +for i = 1:ndim + dimSizes(i) = numel(x{i}); +end + +% Create grids +for i = 1:ndim + + % Make sure x is a vector along dimension i. + xx = x{i}; + vecShape = ones(1,ndim); + vecShape(i) = dimSizes(i); + xx = reshape(xx(:),vecShape); + + % Repeat vector to create grid + tmp = dimSizes; + tmp(i) = 1; + + if ~isscalar(xx) + xx = repmat(xx,tmp(:)'); + end + + % Store in cell array + X{i} = xx; +end + end \ No newline at end of file diff --git a/Utilities/model_upsampling.m b/Utilities/model_upsampling.m new file mode 100644 index 0000000..455c3db --- /dev/null +++ b/Utilities/model_upsampling.m @@ -0,0 +1,50 @@ +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +% Function that reads the segmented high-resolution anatomical MR % +% images of the fetal brain from which the simulated images will be % +% derived at a given gestational age (GA). % +% Here, we consider segmented high-resolution images from Gholipour, A. % +% et al. A normative spatiotemporal MRI atlas of the fetal brain for % +% automatic segmentation and analysis of early brain growth. Scientific % +% Reports 7, 476 (2017). https://doi.org/10.1038/s41598-017-00525-w % +% % +% Fetal_Brain = model_upsampling( path, ... % +% sub_id, ... % +% upsampling_factor) % +% % +% inputs: - path: folder where the segmented high-resolution MR images % +% of the fetal brain from which our simulations are % +% derived are stored % +% - sub_id: subject ID % +% - upsampling_factor: factor to upsample the anatomical model % +% to the nearest decimal to reduce the % +% computational burden % +% % +% output: - Fetal_Brain: segmented high-resolution 3D volume of the % +% fetal brain for subject sub_id % +% % +% % +% Hélène Lajous, 2022-11-28 % +% helene.lajous@unil.ch % +% % +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% + + +function Fetal_Brain = model_upsampling( path, ... + sub_id, ... + upsampling_factor) + +% Input check +if nargin < 3 + error('Missing input(s).'); +elseif nargin > 3 + error('Too many inputs.'); +end + +info = niftiinfo(strcat(path, sub_id, '\anat\', sub_id, '_rec-mial_dseg.nii.gz')); +Fetal_Brain_init = niftiread(info); +Fetal_Brain = imresize3(Fetal_Brain_init, [size(Fetal_Brain_init,1)*upsampling_factor size(Fetal_Brain_init,2)*upsampling_factor size(Fetal_Brain_init,3)*upsampling_factor], 'nearest'); +% info.PixelDimensions = [SimRes SimRes SimRes]; +% info.ImageSize = [size(Fetal_Brain_up,1) size(Fetal_Brain_up,2) size(Fetal_Brain_up,3)]; +% niftiwrite(Fetal_Brain_up, 'outbrain.nii', info); + +end \ No newline at end of file diff --git a/Utilities/motion_transform.m b/Utilities/motion_transform.m index 7a7b2a1..e55a29a 100644 --- a/Utilities/motion_transform.m +++ b/Utilities/motion_transform.m @@ -1,93 +1,93 @@ -%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% -% Function that returns all information on the rigid motion experienced % -% by a 3D object depending on the amplitude of motion specified by the % -% user. % -% % -% [ motion_corrupted_slices, ... % -% translation_displacement, ... % -% rotation_angle, ... % -% rotation_axis] = motion_transform(motion_level, ... % -% NbSlices); % -% % -% inputs: - motion_level: amplitude of rigid fetal movements to % -% simulate % -% - NbSlices: number of slices % -% % -% outputs: - motion_corrupted_slices: list of indexes of the slices % -% corrupted by motion % -% - translation_displacement: table of translation % -% displacements (in mm) along the % -% three main axes for each motion- % -% -corrupted slice % -% - rotation_angle: list of rotation angles (in °) for 3D % -% rotation in every motion-corrupted slice % -% - rotation_axis: table of rotation axes for 3D rotation in % -% every motion-corrupted slice % -% % -% % -% Hélène Lajous, 2021-07-20 % -% helene.lajous@unil.ch % -% % -%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% - - -function [ motion_corrupted_slices, ... - translation_displacement, ... - rotation_angle, ... - rotation_axis] = motion_transform(motion_level, ... - NbSlices) - -% Input check -if nargin < 2 - error('Missing input(s).'); -elseif nargin > 2 - error('Too many inputs.'); -end - -% We define 3 levels of motion where 5% of the total number of slices is -% affected by 3D random rigid motion of the fetus: -% 1 - Slight motion: translation will vary between [-1,+1]mm and -% rotation between [-2,+2]°; -% 2 - Moderate motion: translation will vary between [-3,+3]mm and -% rotation between [-5,+5]°; -% 3 - Strong motion: translation will vary between [-4,+4]mm and -% rotation between [-8,+8]°. -switch motion_level - case 0 %no motion - corrupted_slice_nb = 0; - case 1 %slight motion - corrupted_slice_nb = ceil(NbSlices*5/100); - translation_amplitude = 1; - rotation_amplitude = 2; - case 2 %moderate motion - corrupted_slice_nb = ceil(NbSlices*5/100); - translation_amplitude = 3; - rotation_amplitude = 5; - case 3 %strong motion - corrupted_slice_nb = ceil(NbSlices*5/100); - translation_amplitude = 4; - rotation_amplitude = 8; -end - -% List of slices corrupted by motion -motion_corrupted_slices = randperm(NbSlices, corrupted_slice_nb); - -% Initialization of tranformation arrays -translation_displacement = zeros(length(motion_corrupted_slices), 3); -rotation_axis = zeros(length(motion_corrupted_slices), 3); -rotation_angle = zeros(length(motion_corrupted_slices), 1); - -for iSlice=1:length(motion_corrupted_slices) - % List of translation displacement along the 3 main axes for each - % motion-corrupted slice - translation_displacement(iSlice,:) = [unifrnd(-translation_amplitude,translation_amplitude) ... - unifrnd(-translation_amplitude,translation_amplitude) ... - unifrnd(-translation_amplitude,translation_amplitude)]; %in mm - % Rotation - rotation_angle(iSlice) = unifrnd(-rotation_amplitude,rotation_amplitude); %in degrees - rotation_axis(iSlice,:) = [unifrnd(0,1) unifrnd(0,1) unifrnd(0,1)]; - % Display message for debugging - sprintf('The motion corrupted slice #: %d is characterized by a translational displacement of [%0.4f %0.4f %0.4f]mm along the 3 main axes, and by a rotation of %0.4f degrees around the axis [%0.4f %0.4f %0.4f].', motion_corrupted_slices(iSlice), translation_displacement(iSlice,:), rotation_angle(iSlice), rotation_axis(iSlice,:)) -end - + %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +% Function that returns all information on the rigid motion experienced % +% by a 3D object depending on the amplitude of motion specified by the % +% user. % +% % +% [ motion_corrupted_slices, ... % +% translation_displacement, ... % +% rotation_angle, ... % +% rotation_axis] = motion_transform(motion_level, ... % +% NbSlices); % +% % +% inputs: - motion_level: amplitude of rigid fetal movements to % +% simulate % +% - NbSlices: number of slices % +% % +% outputs: - motion_corrupted_slices: list of indexes of the slices % +% corrupted by motion % +% - translation_displacement: table of translation % +% displacements (in mm) along the % +% three main axes for each motion- % +% -corrupted slice % +% - rotation_angle: list of rotation angles (in °) for 3D % +% rotation in every motion-corrupted slice % +% - rotation_axis: table of rotation axes for 3D rotation in % +% every motion-corrupted slice % +% % +% % +% Hélène Lajous, 2021-07-20 % +% helene.lajous@unil.ch % +% % +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% + + +function [ motion_corrupted_slices, ... + translation_displacement, ... + rotation_angle, ... + rotation_axis] = motion_transform(motion_level, ... + NbSlices) + +% Input check +if nargin < 2 + error('Missing input(s).'); +elseif nargin > 2 + error('Too many inputs.'); +end + +% We define 3 levels of motion where 5% of the total number of slices is +% affected by 3D random rigid motion of the fetus: +% 1 - Slight motion: translation will vary between [-1,+1]mm and +% rotation between [-2,+2]°; +% 2 - Moderate motion: translation will vary between [-3,+3]mm and +% rotation between [-5,+5]°; +% 3 - Strong motion: translation will vary between [-4,+4]mm and +% rotation between [-8,+8]°. +switch motion_level + case 0 %no motion + corrupted_slice_nb = 0; + case 1 %slight motion + corrupted_slice_nb = ceil(NbSlices*5/100); + translation_amplitude = 1; + rotation_amplitude = 2; + case 2 %moderate motion + corrupted_slice_nb = ceil(NbSlices*5/100); + translation_amplitude = 3; + rotation_amplitude = 5; + case 3 %strong motion + corrupted_slice_nb = ceil(NbSlices*5/100); + translation_amplitude = 4; + rotation_amplitude = 8; +end + +% List of slices corrupted by motion +motion_corrupted_slices = randperm(NbSlices, corrupted_slice_nb); + +% Initialization of tranformation arrays +translation_displacement = zeros(length(motion_corrupted_slices), 3); +rotation_axis = zeros(length(motion_corrupted_slices), 3); +rotation_angle = zeros(length(motion_corrupted_slices), 1); + +for iSlice=1:length(motion_corrupted_slices) + % List of translation displacement along the 3 main axes for each + % motion-corrupted slice + translation_displacement(iSlice,:) = [unifrnd(-translation_amplitude,translation_amplitude) ... + unifrnd(-translation_amplitude,translation_amplitude) ... + unifrnd(-translation_amplitude,translation_amplitude)]; %in mm + % Rotation + rotation_angle(iSlice) = unifrnd(-rotation_amplitude,rotation_amplitude); %in degrees + rotation_axis(iSlice,:) = [unifrnd(0,1) unifrnd(0,1) unifrnd(0,1)]; + % Display message for debugging + sprintf('The motion corrupted slice #: %d is characterized by a translational displacement of [%0.4f %0.4f %0.4f]mm along the 3 main axes, and by a rotation of %0.4f degrees around the axis [%0.4f %0.4f %0.4f].', motion_corrupted_slices(iSlice), translation_displacement(iSlice,:), rotation_angle(iSlice), rotation_axis(iSlice,:)) +end + end \ No newline at end of file diff --git a/Utilities/moving_3Dbrain.m b/Utilities/moving_3Dbrain.m index 9eb3558..b5c9d2c 100644 --- a/Utilities/moving_3Dbrain.m +++ b/Utilities/moving_3Dbrain.m @@ -1,72 +1,75 @@ -%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% -% Function that translates and rotates a 3D volume. % -% % -% Fetal_Brain_rotated = moving_3Dbrain( Fetal_Brain, ... % -% SimRes, ... % -% translation_displacement, ... % -% translation_interpolation, ... % -% rotation_angle, ... % -% rotation_axis, ... % -% rotation_interpolation, ... % -% bbox); % -% % -% inputs: - Fetal_Brain: segmented high-resolution 3D volume of the % -% fetal brain % -% - SimRes: resolution of the original high-resolution fetal % -% brain images (isotropic, in mm) % -% - translation_displacement: table of translation % -% displacements (in mm) along the % -% three main axes for each motion- % -% -corrupted slice % -% - translation_interpolation: interpolation method used to % -% assign an intensity value to % -% every voxel of the 3D volume % -% after translation % -% - rotation_angle: list of rotation angles (in °) for 3D % -% rotation in every motion-corrupted slice % -% - rotation_axis: table of rotation axes for 3D rotation in % -% every motion-corrupted slice % -% - rotation_interpolation: interpolation method used to % -% assign an intensity value to every % -% voxel of the 3D volume after % -% rotation % -% - bbox: bounding box that controls the size of the output % -% volume after rotation % -% % -% output: - Fetal_Brain_rotated: segmented high-resolution 3D volume % -% of the fetal brain after 3D % -% translation and rotation % -% % -% % -% Hélène Lajous, 2021-04-20 % -% helene.lajous@unil.ch % -% % -%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% - - -function Fetal_Brain_rotated = moving_3Dbrain( Fetal_Brain, ... - SimRes, ... - translation_displacement, ... - translation_interpolation, ... - rotation_angle, ... - rotation_axis, ... - rotation_interpolation, ... - bbox) - -% Input check -if nargin < 8 - error('Missing input(s).'); -elseif nargin > 8 - error('Too many inputs.'); -end - -% Move the fetal brain anatomy independently along the three main axes -% according to the given translation displacements (in mm) -Reference_Fetal_Brain_mm = imref3d(size(Fetal_Brain), SimRes, SimRes, SimRes); -[Fetal_Brain_translated, Ref_Fetal_Brain_translated_mm] = imtranslate(Fetal_Brain, Reference_Fetal_Brain_mm, translation_displacement, translation_interpolation); - -% Apply 3D rotation, defined by a rotation angle and a rotation axis, to -% the already translated fetal brain anatomy -Fetal_Brain_rotated = imrotate3(Fetal_Brain_translated, rotation_angle, rotation_axis, rotation_interpolation, bbox); - +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +% Function that translates and rotates a 3D volume. % +% % +% Fetal_Brain_rotated = moving_3Dbrain( Fetal_Brain, ... % +% SimRes, ... % +% translation_displacement, ... % +% translation_interpolation, ... % +% rotation_angle, ... % +% rotation_axis, ... % +% rotation_interpolation, ... % +% bbox); % +% % +% inputs: - Fetal_Brain: segmented high-resolution 3D volume of the % +% fetal brain % +% - SimRes: resolution of the original high-resolution fetal % +% brain images (isotropic, in mm) % +% - translation_displacement: table of translation % +% displacements (in mm) along the % +% three main axes for each motion- % +% -corrupted slice % +% - translation_interpolation: interpolation method used to % +% assign an intensity value to % +% every voxel of the 3D volume % +% after translation % +% - rotation_angle: list of rotation angles (in °) for 3D % +% rotation in every motion-corrupted slice % +% - rotation_axis: table of rotation axes for 3D rotation in % +% every motion-corrupted slice % +% - rotation_interpolation: interpolation method used to % +% assign an intensity value to every % +% voxel of the 3D volume after % +% rotation % +% - bbox: bounding box that controls the size of the output % +% volume after rotation % +% % +% output: - Fetal_Brain_rotated: segmented high-resolution 3D volume % +% of the fetal brain after 3D % +% translation and rotation % +% % +% % +% Hélène Lajous, 2021-04-20 % +% helene.lajous@unil.ch % +% % +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% + + +function Fetal_Brain_rotated = moving_3Dbrain( Fetal_Brain, ... + SimRes, ... + translation_displacement, ... + translation_interpolation, ... + rotation_angle, ... + rotation_axis, ... + rotation_interpolation, ... + bbox) + +% Input check +if nargin < 8 + error('Missing input(s).'); +elseif nargin > 8 + error('Too many inputs.'); +end + +% Use the information on the resolution of the anatomical model in each +% dimension to construct a spatial referencing object associated with it +Reference_Fetal_Brain_mm = imref3d(size(Fetal_Brain), SimRes(2), SimRes(1), SimRes(3)); + +% Move the fetal brain anatomy independently along the three main axes +% according to the given translation displacements (in mm) +[Fetal_Brain_translated, ~] = imtranslate(Fetal_Brain, Reference_Fetal_Brain_mm, translation_displacement, translation_interpolation); + +% Apply 3D rotation, defined by a rotation angle and a rotation axis, to +% the already translated fetal brain anatomy +Fetal_Brain_rotated = imrotate3(Fetal_Brain_translated, rotation_angle, rotation_axis, rotation_interpolation, bbox); + end \ No newline at end of file diff --git a/Utilities/name_run.m b/Utilities/name_run.m new file mode 100644 index 0000000..216872e --- /dev/null +++ b/Utilities/name_run.m @@ -0,0 +1,54 @@ +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +% Function that saves the T2w simulation of the fetal brain along with % +% its label map according to the gestational age of the fetus and to % +% the acquisition plane % +% % +% run_id = name_run(orientation, shift_mm) % +% % +% inputs: - orientation: strict acquisition plane (axial, coronal or % +% sagittal) % +% - shift_mm: displacement (in mm) of the slice slab in the % +% slice thickness direction % +% % +% % +% Hélène Lajous, 2022-12-13 % +% helene.lajous@unil.ch % +% % +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% + +function run_id = name_run(orientation, shift_mm) + +% Input check +if nargin < 2 + error('Missing input(s).'); +elseif nargin > 2 + error('Too many inputs.'); +end + +if orientation==1 + if shift_mm==0 + run_id = 1; + elseif shift_mm<0 + run_id = 2; + elseif shift_mm>0 + run_id = 3; + end +elseif orientation==2 + if shift_mm==0 + run_id = 4; + elseif shift_mm<0 + run_id = 5; + elseif shift_mm>0 + run_id = 6; + end +elseif orientation==3 + if shift_mm==0 + run_id = 7; + elseif shift_mm<0 + run_id = 8; + elseif shift_mm>0 + run_id = 9; + end +end + +end \ No newline at end of file diff --git a/Utilities/ornt2axcodes.m b/Utilities/ornt2axcodes.m new file mode 100644 index 0000000..c013007 --- /dev/null +++ b/Utilities/ornt2axcodes.m @@ -0,0 +1,61 @@ +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +% Function that converts orientation to labels for axis directions. % +% % +% axcodes = ornt2axcodes(ornt, labels); % +% % +% inputs: - ornt: orientation array corresponding to the anatomical % +% model of the fetal brain % +% - labels: (begininng, end) of output axis % +% % +% output: - axcodes: labels for positive end of voxel axes. % +% % +% % +% Hélène Lajous, 2023-01-30 % +% helene.lajous@unil.ch % +% % +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% + + +function axcodes = ornt2axcodes(ornt, varargin) + +% Input check +if nargin < 1 + error('Missing input(s).'); +elseif nargin==1 + labels = {["L","R"], ["P","A"], ["I","S"]}; %default value for labels +else + if numel(varargin) > 0 %optional input arguments are provided + if numel(varargin) < 2 + error('You need to provide optional input arguments as ''ParameterName''-''ParameterValue'' pairs.'); + end + switch varargin{1} + case 'labels' + labels = varargin{2}; + otherwise + error('Unexpected ''ParameterName'' input: %s\n', varargin{1}); + end + end +end + +axcodes = {}; +for index=1:size(ornt,1) + axno = ornt(index,1); + direction = ornt(index,2); + if isnan(axno) + axcodes = [axcodes, "None"]; + continue + end + axint = int8(round(axno)); + if axint~=axno + error('ValueError : Non integer axis number %f.', axno); + elseif direction==1 + axcode = string(labels{axint}{2}); + elseif direction==-1 + axcode = string(labels{axint}{1}); + else + error('ValueError : Direction should be -1 or 1.'); + end + axcodes = [axcodes, axcode]; +end + +end \ No newline at end of file diff --git a/Utilities/output_name.m b/Utilities/output_name.m index 6eb93ae..0e86cc3 100644 --- a/Utilities/output_name.m +++ b/Utilities/output_name.m @@ -1,74 +1,89 @@ -%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% -% Function that compiles the name of the output folder where the % -% simulated images are saved according to the gestational age of the % -% fetus, to the level of fetal movements, to the acquisition plane and % -% to the displacement of the fetal brain volume in a given orientation % -% from one series to the other. % -% % -% output_folder = output_name( GA, ... % -% motion_level, ... % -% orientation, ... % -% shift_mm); % -% % -% inputs: - GA: gestational age of the fetus (in weeks) % -% - motion_level: amplitude of rigid fetal movements to % -% simulate % -% - orientation: strict acquisition plane (axial, coronal or % -% sagittal) % -% - shift_mm: displacement (in mm) of the slice slab in the % -% slice thickness direction between two % -% simulations in the same orientation % -% % -% output: - output_folder: folder where the simulated images are saved % -% % -% % -% Hélène Lajous, 2021-04-20 % -% helene.lajous@unil.ch % -% % -%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% - - -function output_folder = output_name( GA, ... - motion_level, ... - orientation, ... - shift_mm) - -% Input check -if nargin < 4 - error('Missing input(s).'); -elseif nargin > 4 - error('Too many inputs.'); -end - -switch motion_level - case 0 - motion = 'no_motion'; - case 1 - motion = 'low_motion'; - case 2 - motion = 'moderate_motion'; - case 3 - motion = 'strong_motion'; -end - -switch orientation - case 1 - acq_plane = 'SAG'; - case 2 - acq_plane = 'COR'; - case 3 - acq_plane = 'AX'; -end - -switch shift_mm - case 0 - shift = 'no_shift'; - case -1.6 - shift = 'shift_m'; - case 1.6 - shift = 'shift_p'; -end - -output_folder = strcat('/data/Simu_FSE/GA_', sprintf('%02s', num2str(GA)), 'w/', motion, '/', acq_plane, '/', shift, '/'); - +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +% Function that compiles the name of the output folder where the % +% simulated images are saved according to the gestational age of the % +% fetus, to the level of fetal movements, to the acquisition plane and % +% to the displacement of the fetal brain volume in a given orientation % +% from one series to the other. % +% % +% output_folder = output_name( GA, ... % +% motion_level, ... % +% orientation, ... % +% shift_mm); % +% % +% inputs: - GA: gestational age of the fetus (in weeks) % +% - motion_level: amplitude of rigid fetal movements to % +% simulate % +% - orientation: strict acquisition plane (axial, coronal or % +% sagittal) % +% - shift_mm: displacement (in mm) of the slice slab in the % +% slice thickness direction between two % +% simulations in the same orientation % +% - WM_heterogeneity: boolean value to decide whether to % +% reproduce the WM heterogeneity of the % +% fetal brain simulation (1) or not (0) % +% % +% output: - output_folder: folder where the simulated images are saved % +% % +% % +% Hélène Lajous, 2021-04-20 % +% helene.lajous@unil.ch % +% % +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% + + +function output_folder = output_name( GA, ... + motion_level, ... + orientation, ... + shift_mm, ... + WM_heterogeneity) + +% Input check +if nargin < 5 + error('Missing input(s).'); +elseif nargin > 5 + error('Too many inputs.'); +end + +switch motion_level + case 0 + motion = 'no_motion'; + case 1 + motion = 'low_motion'; + case 2 + motion = 'moderate_motion'; + case 3 + motion = 'strong_motion'; +end + +switch orientation + case 1 + acq_plane = 'SAG'; + case 2 + acq_plane = 'COR'; + case 3 + acq_plane = 'AX'; +end + +switch shift_mm + case 0 + shift = 'no_shift'; + case -1.6 + shift = 'shift_m'; + case 1.6 + shift = 'shift_p'; +end + +switch WM_heterogeneity + case 0 + WM = 'no_WM_maturation'; + case 1 + WM = 'with_WM_maturation'; +end + +output_folder = strcat('./data/Simu_FSE/GA_', sprintf('%02s', num2str(GA)), 'w/', motion, '/', acq_plane, '/', shift, '/', WM, '/'); + +if ~exist(output_folder, 'dir') + mkdir(output_folder) +end + end \ No newline at end of file diff --git a/Utilities/reorient_volume.m b/Utilities/reorient_volume.m new file mode 100644 index 0000000..8a60096 --- /dev/null +++ b/Utilities/reorient_volume.m @@ -0,0 +1,69 @@ +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +% Function that reorients a 3D volume to have its orientation, which % +% corresponds to the slice thickness direction, given by the 3. index. % +% % +% Volume_Reoriented = volume_reorient(Volume, orientation); % +% % +% inputs: - Volume: 3D volume to be reoriented % +% - Affine: affine transformation matrix from the anatomical % +% fetal brain model % +% - SimRes: resolution of the original 3D anatomical model of % +% the fetal brain % +% - out_axcodes: desired output axes codes % +% % +% output: - ReorientedVolume: 3D volume after reorientation, i.e. its % +% third dimension corresponds to the slice % +% thickness direction % +% - NewAffine: affine transformation matrix of the reoriented % +% fetal brain model % +% - NewSimRes: resolution of the reoriented 3D anatomical % +% model of the fetal brain % +% % +% % +% Oscar Esteban, Hélène Lajous, 2023-02-27 % +% helene.lajous@unil.ch % +% % +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% + + +function [ReorientedVolume, ... + NewAffine, ... + NewSimRes] = reorient_volume( Volume, ... + Affine, ... + SimRes, ... + out_axcodes) + +% Input check +if nargin < 4 + error('Missing input(s).'); +elseif nargin > 4 + error('Too many inputs.'); +end + +% Axes codes of the input volume +in_axcodes = aff2axcodes(Affine); + +% Create a dictionary with the axes codes of the original model +codemap = containers.Map({char(in_axcodes(1)) char(in_axcodes(2)) char(in_axcodes(3))}, [1 2 3]); + +for i=1:length(out_axcodes) + ornt(i) = codemap(out_axcodes(i)); +end + +% Reorient volume according to the desired output axes codes and update the +% corresponding affine +ReorientedVolume = permute(Volume, ornt); +NewAffine = Affine(:, [ornt 4]); +% NewRotations = Affine(1:3,ornt)~=0; +% NewRotations(4,4) = 1; +% NewAffine = NewRotations * Affine; +% NewAffine = double(Affine(1:3,ornt)~=0); +% NewAffine(4,4) = 1; +% NewRotations = +% NewAffine(:,4) = NewAffine * Affine(:,4); +NewSimRes = SimRes(ornt); + +% Display message for debugging +sprintf('The volume was successfully reoriented.') + +end \ No newline at end of file diff --git a/Utilities/resampling_inplane.m b/Utilities/resampling_inplane.m new file mode 100644 index 0000000..13933d1 --- /dev/null +++ b/Utilities/resampling_inplane.m @@ -0,0 +1,43 @@ +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +% Function that resamples a 3D volume in-plane % +% % +% inputs: - Volume: 3D volume to be resampled % +% - resampling_read: resampling in the readout direction % +% - resampling_phase: resampling in the phase-encoding % +% direction % +% - interpolation_method: interpolation method used to % +% resample the volume, linear for an % +% image, nearest neighbors for a label % +% map or a binary mask % +% % +% output: - Resampled_Volume: 3D volume after resampling in the % +% in-plane orientation % +% % +% Hélène Lajous, 2021-07-05 % +% % +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% + + +function ResampledVolume = resampling_inplane( Volume, ... + ResamplingRead, ... + ResamplingPhase, ... + InterpolationMethod) + + +% Input check +if nargin < 4 + error('Missing input(s).'); +elseif nargin > 4 + error('Too many inputs.'); +end + + +switch InterpolationMethod + case 'nearest' + ResampledVolume = imresize(Volume, [round(size(Volume,1)/ResamplingRead) round(size(Volume,2)/ResamplingPhase)], 'nearest'); + case 'linear' + ResampledVolume = imresize(Volume, [round(size(Volume,1)/ResamplingRead) round(size(Volume,2)/ResamplingPhase)], 'bilinear'); +end + +% Display message for debugging +sprintf('Volume resampled.') diff --git a/Utilities/sampling_OoP.m b/Utilities/sampling_OoP.m index 165f63f..1f1547d 100644 --- a/Utilities/sampling_OoP.m +++ b/Utilities/sampling_OoP.m @@ -1,53 +1,65 @@ -%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% -% Function that resamples a 3D volume by a given sampling factor and % -% according to the specified interpolation method in the slice % -% thickness direction (3. index, "Out-of-Plane"). % -% % -% Volume_resampled = sampling_OoP( Volume, ... % -% sampling_factor, ... % -% interpolation_method); % -% % -% inputs: - Volume: 3D volume to be resampled % -% - sampling_factor: factor of up- or down-sampling % -% - interpolation_method: interpolation method used to assign % -% a value to every voxel of the 3D % -% volume after resampling % -% % -% output: - Volume_resampled: 3D volume after resampling in the slice % -% thickness direction % -% % -% % -% Hélène Lajous, 2021-04-21 % -% helene.lajous@unil.ch % -% % -%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% - - -function Volume_resampled = sampling_OoP( Volume, ... - sampling_factor, ... - interpolation_method) - -% Input check -if nargin < 3 - error('Missing input(s).'); -elseif nargin > 3 - error('Too many inputs.'); -end - - -switch interpolation_method - case 'nearest' - for index=1:size(Volume,3) - upsampling = index*sampling_factor-(sampling_factor-1); - Volume_resampled(:,:,upsampling:upsampling+sampling_factor-1) = repmat(Volume(:,:,index), [1, 1, sampling_factor]); - end - case 'linear' - Volume_orient = reshape(Volume, [size(Volume,1)*size(Volume,2), size(Volume,3)]); - Volume_up = imresize(Volume_orient, [size(Volume_orient,1) size(Volume_orient,2)*sampling_factor], 'bilinear'); - Volume_resampled = reshape(Volume_up, [size(Volume,1), size(Volume,2), size(Volume_up,2)]); -end - -% Display message for debugging -sprintf('Volume resampled.') - +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +% Function that resamples a 3D volume by a given sampling factor and % +% according to the specified interpolation method in the slice % +% thickness direction (3. index, "Out-of-Plane"). % +% % +% VolumeResampled = sampling_OoP( Volume, ... % +% SamplingFactor, ... % +% InterpolationMethod); % +% % +% inputs: - Volume: 3D volume to be resampled % +% - SamplingFactor: factor of up- or down-sampling % +% - InterpolationMethod: interpolation method used to assign a % +% value to every voxel of the resampled % +% 3D volume. Images generated for the % +% qualitative evaluation by the % +% radiologists were simulated from % +% partial volume maps bilinearly % +% interpolated. To reduce the % +% computational burden that arises from % +% EPG simulations with many non-unique % +% combinations of (b1,T1,T2), the % +% images generated for the data % +% augmentation experiment were % +% simulated from partial volume maps % +% interpolated using a nearest- % +% -neighboor method. % +% % +% output: - VolumeResampled: 3D volume after resampling in the slice % +% thickness direction % +% % +% % +% Hélène Lajous, 2021-04-21 % +% helene.lajous@unil.ch % +% % +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% + + +function VolumeResampled = sampling_OoP( Volume, ... + SamplingFactor, ... + InterpolationMethod) + +% Input check +if nargin < 3 + error('Missing input(s).'); +elseif nargin > 3 + error('Too many inputs.'); +end + + +switch InterpolationMethod + case 'nearest' + for index=1:size(Volume,3) + upsampling = index*SamplingFactor-(SamplingFactor-1); + VolumeResampled(:,:,upsampling:upsampling+SamplingFactor-1) = repmat(Volume(:,:,index), [1, 1, SamplingFactor]); + end + case 'linear' + Volume_orient = reshape(Volume, [size(Volume,1)*size(Volume,2), size(Volume,3)]); + Volume_up = imresize(Volume_orient, [size(Volume_orient,1) size(Volume_orient,2)*SamplingFactor], 'bilinear'); + VolumeResampled = reshape(Volume_up, [size(Volume,1), size(Volume,2), size(Volume_up,2)]); +end + +% Display message for debugging +sprintf('Volume resampled.') + end \ No newline at end of file diff --git a/Utilities/save_nii_images.m b/Utilities/save_nii_images.m new file mode 100644 index 0000000..2aa5548 --- /dev/null +++ b/Utilities/save_nii_images.m @@ -0,0 +1,91 @@ +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +% Function that saves the T2w simulation of the fetal brain along with % +% its label map according to the gestational age of the fetus and to % +% the acquisition plane % +% % +% save_nii_images(FSE_Images, ... % +% Label_Map_recon, ... % +% GA, ... % +% orientation, ... % +% output_folder) % +% % +% inputs: - FSE_images: simulated T2-weighted MR images of the fetal % +% brain based on the acquisition scheme of FSE % +% sequences % +% - Label_Map_recon: Simulation's label map derived from the % +% reference model % +% - GA: gestational age of the fetus (in weeks) % +% - orientation: strict acquisition plane (axial, coronal or % +% sagittal) % +% - output_folder: folder where the simulated images are saved % +% % +% % +% Hélène Lajous, 2022-12-02 % +% helene.lajous@unil.ch % +% % +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% + +function save_nii_images( FSE_Images, ... + Label_Map_recon, ... + Simu_Binary_Mask, ... + orientation, ... + FOVRead, ... + FOVPhase, ... + PhaseOversampling, ... + reconMatrix, ... + SliceThickness, ... + SliceGap, ... + output_folder, ... + sub_id, ... + ses_id, ... + run_id) + +% Input check +if nargin < 14 + error('Missing input(s).'); +elseif nargin > 14 + error('Too many inputs.'); +end + +switch orientation + case 1 + acq_plane = 'sagittal'; + case 2 + acq_plane = 'coronal'; + case 3 + acq_plane = 'axial'; +end + +% Histogram normalization between 0 and 255 of simulated SS-FSE images +norm_HASTE_Images = FSE_Images * 255 / max(max(max(abs(FSE_Images)))); + +% Only keep the magnitude image +HASTE_im = abs(norm_HASTE_Images); + +output_path = strcat(output_folder, char(sub_id), '/ses-', sprintf('%02s', num2str(ses_id)), '/anat/'); +if not(isfolder(output_path)) + mkdir(output_path) +end +derivatives_path = strcat(output_folder, 'derivatives/'); +if not(isfolder(derivatives_path)) + mkdir(derivatives_path) +end +derivatives_labels_path = strcat(derivatives_path, 'labels/', char(sub_id), '/ses-', sprintf('%02s', num2str(ses_id)), '/anat/'); +derivatives_masks_path = strcat(derivatives_path, 'masks/', char(sub_id), '/ses-', sprintf('%02s', num2str(ses_id)), '/anat/'); +if not(isfolder(derivatives_labels_path)) + mkdir(derivatives_labels_path) +end +if not(isfolder(derivatives_masks_path)) + mkdir(derivatives_masks_path) +end + +output_im = strcat(output_path, char(sub_id), '_ses-', sprintf('%02s', num2str(ses_id)), '_run-', num2str(run_id), '_T2w.nii'); +output_labels = strcat(derivatives_labels_path, char(sub_id), '_ses-', sprintf('%02s', num2str(ses_id)), '_run-', num2str(run_id), '_labels.nii'); +output_mask = strcat(derivatives_masks_path, char(sub_id), '_ses-', sprintf('%02s', num2str(ses_id)), '_run-', num2str(run_id), '_mask.nii'); + +% Save images +gzip(mat2nii(HASTE_im, output_im, acq_plane, [FOVRead/reconMatrix (FOVPhase/(1+PhaseOversampling))/reconMatrix SliceThickness+SliceGap])); +gzip(mat2nii(Label_Map_recon, output_labels, acq_plane, [FOVRead/reconMatrix (FOVPhase/(1+PhaseOversampling))/reconMatrix SliceThickness+SliceGap])); +gzip(mat2nii(Simu_Binary_Mask, output_mask, acq_plane, [FOVRead/reconMatrix (FOVPhase/(1+PhaseOversampling))/reconMatrix SliceThickness+SliceGap])); + +end \ No newline at end of file diff --git a/Utilities/series_nb.m b/Utilities/series_nb.m new file mode 100644 index 0000000..54bdb5a --- /dev/null +++ b/Utilities/series_nb.m @@ -0,0 +1,53 @@ +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +% Function that assigns a run ID to the simulated images according to % +% the orientation plane and the shift of the field of view. % +% % +% run_id = series_nb(orientation, shift_mm) % +% % +% inputs: - orientation: strict acquisition plane (axial, coronal or % +% sagittal) % +% - shift_mm: shift of the field of view % +% % +% % +% Hélène Lajous, 2022-11-29 % +% helene.lajous@unil.ch % +% % +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% + + +function run_id = series_nb(orientation, shift_mm) + +% Input check +if nargin < 2 + error('Missing input(s).'); +elseif nargin > 2 + error('Too many inputs.'); +end + +if orientation==1 + if shift_mm==0 + run_id = 1; + elseif shift_mm==-1.1 + run_id = 2; + elseif shift_mm==1.1 + run_id = 3; + end +elseif orientation==2 + if shift_mm==0 + run_id = 4; + elseif shift_mm==-1.1 + run_id = 5; + elseif shift_mm==1.1 + run_id = 6; + end +elseif orientation==3 + if shift_mm==0 + run_id = 7; + elseif shift_mm==-1.1 + run_id = 8; + elseif shift_mm==1.1 + run_id = 9; + end +end + +end \ No newline at end of file diff --git a/Utilities/tissue_to_MR.m b/Utilities/tissue_to_MR.m index b9d6e44..50e5702 100644 --- a/Utilities/tissue_to_MR.m +++ b/Utilities/tissue_to_MR.m @@ -1,193 +1,314 @@ -%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% -% Function that converts segmented anatomical MR images of the fetal % -% brain to the corresponding reference T1 and T2 maps depending on the % -% main magnetic field strength B0. % -% % -% [ref_T1map, ref_T2map] = tissue_to_MR( Fetal_Brain, ... % -% Fetal_Brain_Tissues, ... % -% B0); % -% % -% inputs: - Fetal_Brain: segmented high-resolution 3D volume of the % -% fetal brain % -% - Fetal_Brain_Tissues: list of tissues in the fetal brain % -% that were segmented and labeled % -% - B0: main magnetic field strength % -% % -% outputs: - ref_T1map: reference T1 map of the fetal brain % -% - ref_T2map: reference T2 map of the fetal brain % -% % -% T1 and T2 values of the segmented fetal brain tissues are stored in a % -% table with the format: % -% [T1 at 1.5 T, T2 at 1.5 T, T1 at 3.0 T, T2 at 3.0 T] % -% % -% Relaxation values of the fetal brain at 1.5 T are based on the % -% literature: % -% Hagmann, C. et al. T2 at MR imaging is an objective quantitative % -% measure of cerebral white matter signal intensity abnormality in % -% preterm infants at term-equivalent age. Radiology 252, 209–217 (2009) % -% https://doi.org/10.1148/radiol.2522080589 % -% Nossin-Manor, R. et al. Quantitative MRI in the very preterm brain: % -% Assessing tissue organization and myelination using magnetization % -% transfer, diffusion tensor and T1 imaging. NeuroImage 64, 505–516 % -% (2013). https://doi.org/10.1016/j.neuroimage.2012.08.086 % -% Blazejewska, A. et al. 3D in utero quantification of T2* relaxation % -% times in human fetal brain tissues for age optimized structural and % -% functional MRI. Magnetic Resonance in Medicine 78, 909–916 (2017). % -% https://doi.org/10.1002/mrm.26471 % -% Yarnykh, V. et al. Quantitative assessment of normal fetal brain % -% myelination using fast macromolecular proton fraction mapping. % -% American Journal of Neuroradiology 39, 1341–1348 (2018). % -% https://doi.org/10.3174/ajnr.A5668 % -% Vasylechko, S. et al. T2* relaxometry of fetal brain at 1.5 Tesla % -% using a motion tolerant method. Magnetic Resonance in Medicine 73, % -% 1795–1802 (2015). https://doi.org/10.1002/mrm.25299 % -% % -% Relaxation values of the fetal brain at 3 T are estimated from the % -% following references: % -% Stanisz, G. J. et al. T1, T2 relaxation and magnetization transfer in % -% tissue at 3T. Magnetic Resonance in Medicine 54, 507–512 (2005). % -% https://doi.org/10.1002/mrm.20605 % -% Rooney, W. D. et al. Magnetic field and tissue dependencies of human % -% brain longitudinal 1H2O relaxation in vivo. Magnetic Resonance in % -% Medicine 57, 308–318 (2007). https://doi.org/10.1002/mrm.21122 % -% Shin, W. et al. Fast high-resolution T1 mapping using inversion- % -% -recovery look-locker echo-planar imaging at steady state: % -% Optimization for accuracy and reliability. Magnetic Resonance in % -% Medicine 61, 899–906 (2009). https://doi.org/10.1002/mrm.21836 % -% Bojorquez, J. Z. et al. What are normal relaxation times of tissues % -% at 3 T? Magnetic Resonance Imaging 35, 69–80 (2017). % -% http://dx.doi.org/10.1016/j.mri.2016.08.021 % -% Daoust, A. et al. Transverse relaxation of cerebrospinal fluid % -% depends on glucose concentration. Magnetic Resonance Imaging 44, % -% 72–81 (2017). https://doi.org/10.1016/j.mri.2017.08.001 % -% http://mriquestions.com/bo-effect-on-t1--t2.html % -% % -% The classification of fetal brain structures as grey matter (GM), % -% white matter (WM), or cerebrospinal fluid (CSF), was confirmed by two % -% experienced neuroradiologists at CHUV, Dre. Mériam Koob, who is also % -% expert in pediatric radiology, and Dr. Vincent Dunet. % -% 37 Hippocampus_L => GM % -% 38 Hippocampus_R => GM % -% 41 Amygdala_L => GM % -% 42 Amygdala_R => GM % -% 71 Caudate_L => GM % -% 72 Caudate_R => GM % -% 73 Putamen_L => GM % -% 74 Putamen_R => GM % -% 77 Thalamus_L => GM % -% 78 Thalamus_R => GM % -% 91 CorpusCallosum => WM % -% 92 Lateral_Ventricle_L => CSF % -% 93 Lateral_Ventricle_R => CSF % -% 94 Midbrain_L => WM % -% 100 Cerebellum_L => WM % -% 101 Cerebellum_R => WM % -% 108 Subthalamic_Nuc_L => GM % -% 109 Subthalamic_Nuc_R => GM % -% 110 Hippocampal_Comm => WM % -% 111 Fornix => WM % -% 112 Cortical_Plate_L => GM % -% 113 Cortical_Plate_R => GM % -% 114 Subplate_L => WM % -% 115 Subplate_R => WM % -% 116 Inter_Zone_L => WM % -% 117 Inter_Zone_R => WM % -% 118 Vent_Zone_L (ependyme) => WM % -% 119 Vent_Zone_R => WM % -% 120 White_Matter_L => WM % -% 121 White_Matter_R => WM % -% 122 Internal_Capsule_L => WM % -% 123 Internal_Capsule_R => WM % -% 124 CSF => CSF % -% 125 misc => WM (as most of this label % -% corresponds to the internal % -% capsule) % -% % -% % -% Hélène Lajous, 2020-05-10 % -% Adapted from: XCAT_to_MR.m (https://github.com/cwroy/Fetal-XCMR/) % -% helene.lajous@unil.ch % -% % -%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% - - -function [ref_T1map, ref_T2map] = tissue_to_MR( Fetal_Brain, ... - Fetal_Brain_Tissues, ... - B0) - -% Input check -if nargin < 3 - error('Missing input(s).'); -elseif nargin > 3 - error('Too many inputs!'); -end - -% Assign T1 and T2 values to every fetal tissue according to its label -Tissue(37,:) = [2000, 162, 2500, 162]; -Tissue(38,:) = [2000, 162, 2500, 162]; -Tissue(41,:) = [2000, 162, 2500, 162]; -Tissue(42,:) = [2000, 162, 2500, 162]; -Tissue(71,:) = [2000, 162, 2500, 162]; -Tissue(72,:) = [2000, 162, 2500, 162]; -Tissue(73,:) = [2000, 162, 2500, 162]; -Tissue(74,:) = [2000, 162, 2500, 162]; -Tissue(77,:) = [2000, 162, 2500, 162]; -Tissue(78,:) = [2000, 162, 2500, 162]; -Tissue(91,:) = [3000, 232, 3300, 232]; -Tissue(92,:) = [4000, 2000, 4400, 2000]; -Tissue(93,:) = [4000, 2000, 4400, 2000]; -Tissue(94,:) = [3000, 232, 3300, 232]; -Tissue(100,:) = [3000, 232, 3300, 232]; -Tissue(101,:) = [3000, 232, 3300, 232]; -Tissue(108,:) = [2000, 162, 2500, 162]; -Tissue(109,:) = [2000, 162, 2500, 162]; -Tissue(110,:) = [3000, 232, 3300, 232]; -Tissue(111,:) = [3000, 232, 3300, 232]; -Tissue(112,:) = [2000, 162, 2500, 162]; -Tissue(113,:) = [2000, 162, 2500, 162]; -Tissue(114,:) = [3000, 232, 3300, 232]; -Tissue(115,:) = [3000, 232, 3300, 232]; -Tissue(116,:) = [3000, 232, 3300, 232]; -Tissue(117,:) = [3000, 232, 3300, 232]; -Tissue(118,:) = [3000, 232, 3300, 232]; -Tissue(119,:) = [3000, 232, 3300, 232]; -Tissue(120,:) = [3000, 232, 3300, 232]; -Tissue(121,:) = [3000, 232, 3300, 232]; -Tissue(122,:) = [3000, 232, 3300, 232]; -Tissue(123,:) = [3000, 232, 3300, 232]; -Tissue(124,:) = [4000, 2000, 4400, 2000]; -Tissue(125,:) = [3000, 232, 3300, 232]; - -% Computation time -tic - -% Initialize reference T1 and T2 maps -Unwrap_Fetal_Brain = Fetal_Brain(:); -ref_T1map = zeros(length(Unwrap_Fetal_Brain),1); -ref_T2map = zeros(length(Unwrap_Fetal_Brain),1); - -% Relaxometry properties depend on the magnetic field strength -switch B0 - case 1.5 - T2_index = 2; - T1_index = 1; - case 3 - T2_index = 4; - T1_index = 3; -end - -% Fetal brain properties -for label=Fetal_Brain_Tissues(1:length(Fetal_Brain_Tissues)) - brain = find(Unwrap_Fetal_Brain==label); - for k=1:length(brain) - ref_T1map(brain(k)) = Tissue(label, T1_index); - ref_T2map(brain(k)) = Tissue(label, T2_index); - end -end -ref_T1map = reshape(ref_T1map, size(Fetal_Brain)); -ref_T2map = reshape(ref_T2map, size(Fetal_Brain)); - -% Display computation time -fprintf('Computation time to convert segmented high-resolution anatomical images of the fetal brain to MR contrast: %0.5f seconds.\n', toc); - +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +% Function that converts segmented anatomical MR images of the fetal % +% brain to the corresponding reference T1 and T2 maps. % +% % +% [ref_T1map, ref_T2map] = tissue_to_MR( fetal_model, ... % +% Fetal_Brain, ... % +% Fetal_Brain_Tissues, ... % +% GA, ... % +% sub_id, ... % +% WM_heterogeneity, ... % +% affine, ... % +% SimRes, ... % +% axcodes_reo, ... % +% shift, ... % +% orientation, ... % +% SamplingFactor, ... % +% T1_WM, ... % +% T2_WM, ... % +% T1_GM, ... % +% T2_GM, ... % +% T1_CSF, ... % +% T2_CSF) % +% % +% inputs: - fetal_model: anatomical model of the fetal brain % +% - Fetal_Brain: segmented high-resolution 3D volume of the % +% fetal brain % +% - Fetal_Brain_Tissues: list of tissues in the fetal brain % +% that were segmented and labeled % +% - GA: gestational age of the fetus (in weeks) % +% - sub_id: subject ID % +% - WM_heterogeneity: boolean value that determines wether to % +% implement (1) or not (0) white matter % +% maturation processes in the simulated % +% images % +% - affine: affine orientation matrix of the anatomical model % +% of the fetal brain % +% - SimRes: resolution of the 3D anatomical model of the fetal % +% brain % +% - axcodes_reo: output axes codes of the reoriented 3D % +% anatomical model of the fetal brain with the % +% slice thickness encoded in the third % +% dimension % +% - shift: displacement (in voxels) of the slice slab in the % +% slice thickness direction % +% - orientation: strict acquisition plane (axial, coronal or % +% sagittal) % +% - SamplingFactor: factor by which the fetal brain volume and % +% the B1 bias field have been upsampled % +% - InterpolationMethod: interpolation method used to assign a % +% value to every voxel of a resampled % +% 3D volume. Images generated for the % +% qualitative evaluation by the % +% radiologists were simulated from % +% partial volume maps bilinearly % +% interpolated. To reduce the % +% computational burden that arises from % +% EPG simulations with many non-unique % +% combinations of [b1, T1, T2], the % +% images generated for the data % +% augmentation experiment were % +% simulated from partial volume maps % +% interpolated using a nearest- % +% -neighboor method. % +% - T1_WM: T1 relaxation time of white matter at the magnetic % +% field strength specified for the simulated % +% acquisition % +% - T2_WM: T2 relaxation time of white matter at the magnetic % +% field strength specified for the simulated % +% acquisition % +% - T1_GM: T1 relaxation time of gray matter at the magnetic % +% field strength specified for the simulated % +% acquisition % +% - T2_GM: T2 relaxation time of gray matter at the magnetic % +% field strength specified for the simulated % +% acquisition % +% - T1_CSF: T1 relaxation time of cerebrospinal fluid at the % +% magnetic field strength specified for the % +% simulated acquisition % +% - T2_CSF: T2 relaxation time of cerebrospinal fluid at the % +% magnetic field strength specified for the % +% simulated acquisition % +% % +% outputs: - ref_T1map: reference T1 map of the fetal brain % +% - ref_T2map: reference T2 map of the fetal brain % +% % +% Relaxation values of the fetal brain at 1.5 T are based on the % +% literature: % +% Hagmann, C. et al. T2 at MR imaging is an objective quantitative % +% measure of cerebral white matter signal intensity abnormality in % +% preterm infants at term-equivalent age. Radiology 252, 209–217 (2009) % +% https://doi.org/10.1148/radiol.2522080589 % +% Nossin-Manor, R. et al. Quantitative MRI in the very preterm brain: % +% Assessing tissue organization and myelination using magnetization % +% transfer, diffusion tensor and T1 imaging. NeuroImage 64, 505–516 % +% (2013). https://doi.org/10.1016/j.neuroimage.2012.08.086 % +% Blazejewska, A. et al. 3D in utero quantification of T2* relaxation % +% times in human fetal brain tissues for age optimized structural and % +% functional MRI. Magnetic Resonance in Medicine 78, 909–916 (2017). % +% https://doi.org/10.1002/mrm.26471 % +% Yarnykh, V. et al. Quantitative assessment of normal fetal brain % +% myelination using fast macromolecular proton fraction mapping. % +% American Journal of Neuroradiology 39, 1341–1348 (2018). % +% https://doi.org/10.3174/ajnr.A5668 % +% Vasylechko, S. et al. T2* relaxometry of fetal brain at 1.5 Tesla % +% using a motion tolerant method. Magnetic Resonance in Medicine 73, % +% 1795–1802 (2015). https://doi.org/10.1002/mrm.25299 % +% % +% Relaxation values of the fetal brain at 3 T are estimated from the % +% following references: % +% Stanisz, G. J. et al. T1, T2 relaxation and magnetization transfer in % +% tissue at 3T. Magnetic Resonance in Medicine 54, 507–512 (2005). % +% https://doi.org/10.1002/mrm.20605 % +% Rooney, W. D. et al. Magnetic field and tissue dependencies of human % +% brain longitudinal 1H2O relaxation in vivo. Magnetic Resonance in % +% Medicine 57, 308–318 (2007). https://doi.org/10.1002/mrm.21122 % +% Shin, W. et al. Fast high-resolution T1 mapping using inversion- % +% -recovery look-locker echo-planar imaging at steady state: % +% Optimization for accuracy and reliability. Magnetic Resonance in % +% Medicine 61, 899–906 (2009). https://doi.org/10.1002/mrm.21836 % +% Bojorquez, J. Z. et al. What are normal relaxation times of tissues % +% at 3 T? Magnetic Resonance Imaging 35, 69–80 (2017). % +% http://dx.doi.org/10.1016/j.mri.2016.08.021 % +% Daoust, A. et al. Transverse relaxation of cerebrospinal fluid % +% depends on glucose concentration. Magnetic Resonance Imaging 44, % +% 72–81 (2017). https://doi.org/10.1016/j.mri.2017.08.001 % +% http://mriquestions.com/bo-effect-on-t1--t2.html % +% % +% The classification of fetal brain structures as grey matter (GM), % +% white matter (WM), or cerebrospinal fluid (CSF), was confirmed by two % +% experienced neuroradiologists at CHUV, Dre. Mériam Koob, who is also % +% expert in pediatric radiology, and Dr. Vincent Dunet. % +% 37 Hippocampus_L => GM % +% 38 Hippocampus_R => GM % +% 41 Amygdala_L => GM % +% 42 Amygdala_R => GM % +% 71 Caudate_L => GM % +% 72 Caudate_R => GM % +% 73 Putamen_L => GM % +% 74 Putamen_R => GM % +% 77 Thalamus_L => GM % +% 78 Thalamus_R => GM % +% 91 CorpusCallosum => WM % +% 92 Lateral_Ventricle_L => CSF % +% 93 Lateral_Ventricle_R => CSF % +% 94 Midbrain_L => WM % +% 100 Cerebellum_L => WM % +% 101 Cerebellum_R => WM % +% 108 Subthalamic_Nuc_L => GM % +% 109 Subthalamic_Nuc_R => GM % +% 110 Hippocampal_Comm => WM % +% 111 Fornix => WM % +% 112 Cortical_Plate_L => GM % +% 113 Cortical_Plate_R => GM % +% 114 Subplate_L => WM % +% 115 Subplate_R => WM % +% 116 Inter_Zone_L => WM % +% 117 Inter_Zone_R => WM % +% 118 Vent_Zone_L (ependyme) => WM % +% 119 Vent_Zone_R => WM % +% 120 White_Matter_L => WM % +% 121 White_Matter_R => WM % +% 122 Internal_Capsule_L => WM % +% 123 Internal_Capsule_R => WM % +% 124 CSF => CSF % +% 125 misc => WM (as most of this label % +% corresponds to the internal % +% capsule) % +% % +% % +% Hélène Lajous, 2020-05-10 % +% Adapted from: XCAT_to_MR.m (https://github.com/cwroy/Fetal-XCMR/) % +% helene.lajous@unil.ch % +% Modified by Andrés le Boeuf, 2022-03-23 % +% andres.le.boeuf@estudiantat.ucp.edu % +% Modified by Hélène Lajous, 2023-03-23 % +% % +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% + + +function [ref_T1map, ref_T2map] = tissue_to_MR( fetal_model, ... + Fetal_Brain, ... + Fetal_Brain_Tissues, ... + GA, ... + sub_id, ... + WM_heterogeneity, ... + affine, ... + SimRes, ... + axcodes_reo, ... + shift, ... + orientation, ... + SamplingFactor, ... + InterpolationMethod, ... + T1_WM, ... + T2_WM, ... + T1_GM, ... + T2_GM, ... + T1_CSF, ... + T2_CSF) + +% Input check +if nargin < 19 + error('Missing input(s).'); +elseif nargin > 19 + error('Too many inputs!'); +end + +% Assign T1 and T2 values to every fetal tissue according to its label +switch fetal_model + case 'STA' + Tissue(37,:) = [T1_GM, T2_GM]; + Tissue(38,:) = [T1_GM, T2_GM]; + Tissue(41,:) = [T1_GM, T2_GM]; + Tissue(42,:) = [T1_GM, T2_GM]; + Tissue(71,:) = [T1_GM, T2_GM]; + Tissue(72,:) = [T1_GM, T2_GM]; + Tissue(73,:) = [T1_GM, T2_GM]; + Tissue(74,:) = [T1_GM, T2_GM]; + Tissue(77,:) = [T1_GM, T2_GM]; + Tissue(78,:) = [T1_GM, T2_GM]; + Tissue(91,:) = [T1_WM, T2_WM]; + Tissue(92,:) = [T1_CSF, T2_CSF]; + Tissue(93,:) = [T1_CSF, T2_CSF]; + Tissue(94,:) = [T1_WM, T2_WM]; + Tissue(100,:) = [T1_WM, T2_WM]; + Tissue(101,:) = [T1_WM, T2_WM]; + Tissue(108,:) = [T1_GM, T2_GM]; + Tissue(109,:) = [T1_GM, T2_GM]; + Tissue(110,:) = [T1_WM, T2_WM]; + Tissue(111,:) = [T1_WM, T2_WM]; + Tissue(112,:) = [T1_GM, T2_GM]; + Tissue(113,:) = [T1_GM, T2_GM]; + Tissue(114,:) = [T1_WM, T2_WM]; + Tissue(115,:) = [T1_WM, T2_WM]; + Tissue(116,:) = [T1_WM, T2_WM]; + Tissue(117,:) = [T1_WM, T2_WM]; + Tissue(118,:) = [T1_WM, T2_WM]; + Tissue(119,:) = [T1_WM, T2_WM]; + Tissue(120,:) = [T1_WM, T2_WM]; + Tissue(121,:) = [T1_WM, T2_WM]; + Tissue(122,:) = [T1_WM, T2_WM]; + Tissue(123,:) = [T1_WM, T2_WM]; + Tissue(124,:) = [T1_CSF, T2_CSF]; + Tissue(125,:) = [T1_WM, T2_WM]; + case 'FeTA_CHUV' + Tissue(1,:) = [T1_CSF, T2_CSF]; %CSF + Tissue(2,:) = [T1_GM, T2_GM]; %GM + Tissue(3,:) = [T1_WM, T2_WM]; %WM + Tissue(4,:) = [T1_CSF, T2_CSF]; %lateral ventricles + Tissue(5,:) = [T1_WM, T2_WM]; %cerebellum + Tissue(6,:) = [T1_GM, T2_GM]; %subcortical GM + Tissue(7,:) = [T1_WM, T2_WM]; %brainstem + case 'FeTA' %refined FeTA dataset (Lucas Fidon, FeTA2021_Release1and2Corrected_v4) + Tissue(1,:) = [T1_WM, T2_WM]; %WM (excluding corpus callosum) + Tissue(2,:) = [T1_CSF, T2_CSF]; %intra-axial CSF + Tissue(3,:) = [T1_WM, T2_WM]; %cerebellum + Tissue(4,:) = [T1_CSF, T2_CSF]; %extra-axial CSF + Tissue(5,:) = [T1_GM, T2_GM]; %cortical GM + Tissue(6,:) = [T1_GM, T2_GM]; %deep GM + Tissue(7,:) = [T1_WM, T2_WM]; %brainstem + Tissue(8,:) = [T1_WM, T2_WM]; %corpus callosum +end + +% Computation time +tic + +% Initialize reference T1 and T2 maps +Unwrap_Fetal_Brain = Fetal_Brain(:); +ref_T1map = zeros(length(Unwrap_Fetal_Brain),1); +ref_T2map = zeros(length(Unwrap_Fetal_Brain),1); + +% % Relaxometry properties depend on the magnetic field strength +% switch B0 +% case 1.5 +% T2_index = 2; +% T1_index = 1; +% case 3 +% T2_index = 4; +% T1_index = 3; +% end + +% Fetal brain properties +for label=Fetal_Brain_Tissues(1:length(Fetal_Brain_Tissues)) + brain = find(Unwrap_Fetal_Brain==label); + for k=1:length(brain) + ref_T1map(brain(k)) = Tissue(label, 1); + ref_T2map(brain(k)) = Tissue(label, 2); + end +end + +ref_T1map = reshape(ref_T1map, size(Fetal_Brain)); +ref_T2map = reshape(ref_T2map, size(Fetal_Brain)); + +% White matter maturation processes implementation +if WM_heterogeneity == 1 + [ref_T1map, ref_T2map] = WM_maturation( fetal_model, ... + ref_T1map, ... + ref_T2map, ... + GA, ... + sub_id, ... + 'FAST', ... + affine, ... + SimRes, ... + axcodes_reo, ... + shift, ... + orientation, ... + SamplingFactor, ... + InterpolationMethod); +end + +% Display computation time +time1 = toc; +fprintf('Computation time to convert segmented high-resolution anatomical images of the fetal brain to MR contrast: %0.5f seconds.\n', time1); + end \ No newline at end of file diff --git a/Utilities/update_affine.m b/Utilities/update_affine.m new file mode 100644 index 0000000..f5f23cc --- /dev/null +++ b/Utilities/update_affine.m @@ -0,0 +1,89 @@ +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +% Function that updates the orientation matrix of a modified 3D image % +% (e.g., after reorientation, zero-padding, etc.). % +% % +% NewAffine = update_affine( Model, ... % +% ModelAffine, ... % +% NewImage, ... % +% InitAffine, ... % +% CenterOffset) % +% % +% inputs: - Model: 3D volume with know affine % +% - ModelAffine: affine transformation matrix corresponding to % +% the Model volume % +% - NewImage: volume generated from the information of the % +% Model % +% - InitAffine: initialization of the affine matrix of the % +% NewImage volume. Information on the offset % +% values will be computed by this function. % +% - CenterOffset: offset to substract to the center of the % +% NewImage computed in intrinsic coordinates % +% to account for any deviation due to % +% zero-padding steps occuring across the % +% simulation pipeline % +% % +% output: - NewAffine: full affine transformation matrix of the % +% NewImage volume. % +% % +% % +% Hélène Lajous, 2023-02-27 % +% helene.lajous@unil.ch % +% % +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% + + +function NewAffine = update_affine( Model, ... + ModelAffine, ... + NewImage, ... + NewRotations, ... + varargin) + +% Input check +if nargin < 4 + error('Missing input(s).'); +elseif nargin==4 + CenterOffset = 0; %default value if the center of the NewImage has not been modified +else + if numel(varargin) > 0 %optional input arguments are provided + if numel(varargin) < 2 + error('You need to provide optional input arguments as ''ParameterName''-''ParameterValue'' pairs.'); + end + switch varargin{1} + case 'CenterOffset' + CenterOffset = varargin{2}; + otherwise + error('Unexpected ''ParameterName'' input: %s\n', varargin{1}); + end + end +end + +% Compute the center of the original anatomical images in intrinsic +% coordinates +ModelCenter_vox = (size(Model) - 1) / 2; %The origin of a coordinate system is (0,0,0) +ModelCenter_vox(4) = 1; + +% Compute the center of the original anatomical images in world coordinates +ModelCenter_mm = ModelAffine * ModelCenter_vox'; + +% % Initialize the orientation matrix of the simulated images +% NewRotations = (InitAffine(1:3, 1:3) ./ SimResReo) .* SimVoxSize; + +% Initialize the center of the simulated images in intrinsic coordinates +% if ndims(NewImage)>3 +% NewImage = squeeze(NewImage(:,:,:,1)); +% end +SimCenter_vox = (size(NewImage) - 1) / 2 + CenterOffset; + +% Compute the center of the simulated images in world coordinates +SimCenter_mm = NewRotations * SimCenter_vox'; + +% Offset +Offset = ModelCenter_mm(1:3) - SimCenter_mm; + +% Define the orientation matrix of the simulated images to be in the same +% space as the original anatomical model +NewAffine = eye(size(ModelAffine)); +NewAffine(1:3, 1:3) = NewRotations; +NewAffine(1:3, 4) = Offset; + +end \ No newline at end of file diff --git a/Utilities/volume_reorient.m b/Utilities/volume_reorient.m index 09036f2..7481b8b 100644 --- a/Utilities/volume_reorient.m +++ b/Utilities/volume_reorient.m @@ -1,46 +1,46 @@ -%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% -% Function that reorients a 3D volume to have its orientation, which % -% corresponds to the slice thickness direction, given by the 3. index. % -% % -% Volume_Reoriented = volume_reorient(Volume, orientation); % -% % -% inputs: - Volume: 3D volume to be reoriented % -% - orientation: strict acquisition plane (axial, coronal or % -% sagittal) % -% % -% output: - Volume_Reoriented: 3D volume after reorientation, i.e. its % -% 3. dimension corresponds to the slice % -% thickness direction % -% % -% % -% Hélène Lajous, 2021-04-21 % -% helene.lajous@unil.ch % -% % -%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% - - -function Volume_Reoriented = volume_reorient( Volume, ... - orientation) - -% Input check -if nargin < 2 - error('Missing input(s).'); -elseif nargin > 2 - error('Too many inputs.'); -end - -Volume_Reoriented = Volume; - -% For the sake of clarity, the orientation corresponding to the slice -% thickness will always be given by the 3. index -switch orientation - case 1 %sagittal - Volume_Reoriented = permute(Volume, [2,3,1]); - case 2 %coronal - Volume_Reoriented = permute(Volume, [1,3,2]); -end - -% Display message for debugging -sprintf('Volume reorientation.') - +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +% Function that reorients a 3D volume to have its orientation, which % +% corresponds to the slice thickness direction, given by the 3. index. % +% % +% Volume_Reoriented = volume_reorient(Volume, orientation); % +% % +% inputs: - Volume: 3D volume to be reoriented % +% - orientation: strict acquisition plane (axial, coronal or % +% sagittal) % +% % +% output: - Volume_Reoriented: 3D volume after reorientation, i.e. its % +% 3. dimension corresponds to the slice % +% thickness direction % +% % +% % +% Hélène Lajous, 2021-04-21 % +% helene.lajous@unil.ch % +% % +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% + + +function Volume_Reoriented = volume_reorient( Volume, ... + orientation) + +% Input check +if nargin < 2 + error('Missing input(s).'); +elseif nargin > 2 + error('Too many inputs.'); +end + +Volume_Reoriented = Volume; + +% For the sake of clarity, the orientation corresponding to the slice +% thickness will always be given by the 3. index +switch orientation + case 1 %sagittal + Volume_Reoriented = permute(Volume, [2,3,1]); + case 2 %coronal + Volume_Reoriented = permute(Volume, [1,3,2]); +end + +% Display message for debugging +sprintf('Volume reorientation.') + end \ No newline at end of file diff --git a/Utilities/zip_kspace.m b/Utilities/zip_kspace.m index 7f3bad6..9d1aff4 100644 --- a/Utilities/zip_kspace.m +++ b/Utilities/zip_kspace.m @@ -1,38 +1,38 @@ -%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% -% Function that filters data in K-space to avoid Gibbs ringing % -% artifacts and that performs zero-interpolation filling (ZIP), namely % -% fills the edges of the acquired K-space with zeros to reach the % -% desired reconstruction matrix size. % -% % -% KSpace_zip = zip_kspace(KSpace, reconMatrix); % -% % -% inputs - KSpace: Fourier domain of the simulated images % -% - reconMatrix: size of the reconstruction matrix (in voxels) % -% % -% output: - KSpace_zip: Fourier domain of the simulated images after % -% ZIP % -% % -% % -% Hélène Lajous, 2021-05-05 % -% helene.lajous@unil.ch % -% % -%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% - - -function KSpace_zip = zip_kspace( KSpace, ... - reconMatrix) - -% Input check -if nargin < 2 - error('Missing input(s).'); -elseif nargin > 2 - error('Too many inputs.'); -end - -% Filter data in k-space to avoid Gibbs ringing artifacts -KSpace_filt = fNDFilter(KSpace, 'Fermi', [1,2]); - -% Interpolate data via zero padding in k-space -KSpace_zip = fZeroPadnDArray(KSpace_filt, reconMatrix); - +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +% Function that filters data in K-space to avoid Gibbs ringing % +% artifacts and that performs zero-interpolation filling (ZIP), namely % +% fills the edges of the acquired K-space with zeros to reach the % +% desired reconstruction matrix size. % +% % +% KSpace_zip = zip_kspace(KSpace, reconMatrix); % +% % +% inputs - KSpace: Fourier domain of the simulated images % +% - reconMatrix: size of the reconstruction matrix (in voxels) % +% % +% output: - KSpace_zip: Fourier domain of the simulated images after % +% ZIP % +% % +% % +% Hélène Lajous, 2021-05-05 % +% helene.lajous@unil.ch % +% % +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% + + +function KSpace_zip = zip_kspace( KSpace, ... + reconMatrix) + +% Input check +if nargin < 2 + error('Missing input(s).'); +elseif nargin > 2 + error('Too many inputs.'); +end + +% Filter data in k-space to avoid Gibbs ringing artifacts +KSpace_filt = fNDFilter(KSpace, 'Fermi', [1,2]); + +% Interpolate data via zero padding in k-space +KSpace_zip = fZeroPadnDArray(KSpace_filt, reconMatrix); + end \ No newline at end of file diff --git a/White_Matter_improvement/README.md b/White_Matter_improvement/README.md new file mode 100644 index 0000000..ff1b010 --- /dev/null +++ b/White_Matter_improvement/README.md @@ -0,0 +1,34 @@ +White matter maturation processes implementation in FaBiAN simulator +=========================================================================== +---- + +__Copyright (c) 2019-2023__ + +__Medical Image Analysis Laboratory (MIAL), Lausanne, Switzerland__ +__Department of Radiology, Lausanne University Hospital (CHUV) and University of Lausanne (UNIL), Lausanne, Switzerland__ +__CIBM Center for Biomedical Imaging, Switzerland__ + +---- +Magnetic resonance imaging is commonly used as a complement to the ultrasound gold-standard to investigate equivocal patterns in the developing fetal brain. However, stochastic motion of the fetus may alter the image quality and subsequent diagnostic. Several post-processing techniques have been developed to mitigate the resultant motion artefacts, but their evaluation requires a ground truth. FaBiAN is an open source Fetal Brain magnetic resonance Acquisition Numerical phantom that simulates clinical T2-weighted magnetic resonance images of the in utero fetal brain throughout maturation. However, this controlled environment was originally built on a three-class model of the fetal brain (white matter, gray matter and cerebrospinal fluid) which does not take into account key maturation processes that occur in the white matter throughout gestation. +To overcome this limitation, we present in this work a new numerical fetal brain model that accounts for magnetic resonance signal changes in the white matter over development. We show that unsupervised segmentation methods such as Gaussian Mixture Models or FMRIB’s Automated Segmentation Tool are effective in identifying local variations of water content within white matter, thus making it possible to fine-tune the relaxometric properties of the tissues accordingly. + +The code from this folder is adapted to work on the public Gholipour's normative spatiotemporal fetal brain [atlas](http://crl.med.harvard.edu/research/fetal_brain_atlas/). If you desire to change to another dataset you should check the white matter labels in *fsl_clustering.py* or *gmm_clustering.py* and make sure that all the paths which refers to the high-resolution reference model are adapted to your dataset. + + +>Below is described how the white matter implementation should be done: +> +> - First apply the the *White_Matter_improvement/fsl_clustering.py* or *White_Matter_improvement/gmm_clustering.py* script on the desired dataset. Each high resolution brain volume should be in a different folder along with its segmentation map. If you are going to apply this script to a different dataset, make sure you have changed the white matter labels to recover a good cropping of the white matter volume. +> - Now that FAST has been applied, in each folder you will have available the high resolution brain volume, the segmentation map, the white matter volume and the outputs from FAST tool. But we are only interested in the partial volume maps which will be take as inputs for the FaBiAN simulator in *Utilities/WM_maturation.m* file. +> - If adaptive clipping values is desired for the current dataset, apply the *White_Matter_improvement/clipping_value_building.py* to (ONLY) the the white matter volumes of each subject. The result can be then pasted in *Utilities/clip_function.m*. An example of the clipping values adquisition is displayed through the jupyter notebook *histogram_distr_for_Gholipour.ipynb*. +> +Note: Make sure that the partial volume map paths are correct in *Utilities/WM_maturation.m* + +## Contact +For any questions, please contact:\ +Andrés le Boeuf\ +andres.le.boeuf@estudiantat.upc.edu + +OR + +Hélène Lajous +helene.lajous@unil.ch diff --git a/White_Matter_improvement/clipping_value_building.py b/White_Matter_improvement/clipping_value_building.py new file mode 100644 index 0000000..d9918cd --- /dev/null +++ b/White_Matter_improvement/clipping_value_building.py @@ -0,0 +1,78 @@ +# ----------------------------------------------------------- +# Python script which retrieves the clipping values to use +# on fetal brain subjects from a dataset based on the contrast +# of their white matter volume +# +# 2022-04-22 Andrés le Boeuf +# andres.le.boeuf@estudiantat.upc.edu +# ----------------------------------------------------------- + +from os import listdir +import nibabel as nib +import numpy as np +from sklearn.mixture import GaussianMixture + + +def variances(folder_path): + """ + Gathers the variance from WM volumes + + inputs: - folder_path: Path were the cropped WM + reference volumes' folders are + outputs:- variance: Python list with the standard + deviation of WM intensity per subject + """ + gmm = GaussianMixture(n_components=2) # GMM of two classes: (1) Black background; (2) WM Volume + variance = [] + for folders in listdir(folder_path): + path= folder_path + "\\" + folders + for files in listdir(path): + if "WM" in files: + WM_path = path + "\\" + files + WM_data = nib.load(WM_path).get_fdata() + gmm.fit(WM_data.reshape(-1,1)) + variance.append(gmm.covariances_[-1]) + print("Appended WM variance from ", files, " to the list. That is: ", gmm.covariances_[-1]) + + return variance + +def clipping_values(variance): + """ + Returns the clipping values to use on the current dataset + + inputs: - variance: Python list with the standard + deviation of WM intensity per subject + outputs:- clip_values: Python list with the clipping values to use for each subject + """ + var_aux = np.array(variance) + init_clip_value = 0.25 # Define an initial clipping vaue for the first subject. + var_perc = [] + clip_values = [] + clip_values.append(init_clip_value) + + for i in range(len(var_aux)-1): + j = i+1 + perc = ((var_aux[j] - var_aux[i])/var_aux[i])*100 + var_perc.append(perc) + + var_perc = np.array(var_perc) + var_perc[var_perc>40]=40 # To don't let the clipping values get stuck neaar to 0. + var_perc = (-1)*var_perc + + clip_change = var_perc/100 + 1 + + for i in range(len(clip_change)): + clip_values.append(clip_values[-1]*clip_change[i]) + + return clip_values + +def main(): + folder_path = r'..\data\atlas_fast_clustering' + var = variances(folder_path) + values = clipping_values(var) + print("The clipping values for the spatiotemporal normative atlas are: ") + for i in range(len(values)): + print(i+21, ":\t", values[i]) + +if __name__ == "__main__": + main() \ No newline at end of file diff --git a/White_Matter_improvement/fsl_clustering.py b/White_Matter_improvement/fsl_clustering.py new file mode 100644 index 0000000..0ac9898 --- /dev/null +++ b/White_Matter_improvement/fsl_clustering.py @@ -0,0 +1,71 @@ +# ----------------------------------------------------------- +# Python script to crop the white matter volume from the +# high resolution reference model and apply on it a 3 classes +# unsupervised segmentation through FAST-FSL tool. +# +# REQUIRES FSL (https://fsl.fmrib.ox.ac.uk/fsl/fslwiki/FAST) +# Note: Since FSL tools work only in linux based OS, this script +# has been launch from WSL2 +# +# 2022-03-23 Andrés le Boeuf +# andres.le.boeuf@estudiantat.upc.edu +# ----------------------------------------------------------- + +import sys +from os import listdir +import numpy as np +import nibabel as nib +import subprocess + +def extract_WM(folder_path): + """ + Main function which first cropps the WM volume from the reference model + and then applies a 3 class FAST segmentation on it. + + inputs: - folder_path: Path were reference + volumes' folders are + """ + wm = np.array([91, 94, 100, 101, 110, 111, 114, 115, 116, 117, 118, 119, 120, 121, 122, 123, 125]) # WM labels derived from the atlas annotations. + segmentation_name = "tissue" + + for folders in listdir(folder_path): + path= folder_path + "/" + folders + for files in listdir(path): + if segmentation_name in files: + + tissue_path = path + "/" + files + replacement = "_" + segmentation_name + brain_path = tissue_path.replace(replacement, "") + print("Extracting WM from " + brain_path.split("/")[-1] ) + + brain = nib.load(brain_path).get_fdata() + tissues_nii = nib.load(tissue_path) + tissues = tissues_nii.get_fdata() + + # Create WM mask + tissues[~(np.isin(tissues,wm))] = 0 + tissues[tissues!=0] = 1 + # Apply WM mask to the high resolution reference volume + wm_tissue = brain*tissues + wm_image = nib.Nifti1Image(dataobj=wm_tissue, affine=tissues_nii.affine) + wm_image._header = tissues_nii.header + save_path = path + "/" + path.split("/")[-1] + "_WM.nii.gz" + + nib.save(img=wm_image,filename=save_path) + print("Applying FAST to: ",save_path) + + _fsl(save_path) + +def _fsl(output_basename): + """ + Function which calls bash script to launch FSL + + inputs: - output_basename: output nifti + image(s) path + """ + subprocess.run(["./fsl_segment.sh", output_basename]) + +if __name__ == "__main__": + + folder_path = r'..\data\atlas_fast_clustering' + extract_WM(folder_path) diff --git a/White_Matter_improvement/fsl_segment.sh b/White_Matter_improvement/fsl_segment.sh new file mode 100644 index 0000000..1a34295 --- /dev/null +++ b/White_Matter_improvement/fsl_segment.sh @@ -0,0 +1,24 @@ +#!/bin/bash +# ----------------------------------------------------------- +# Bash script which applies a 3 class FAST segmentation on +# white matter volumes from reference models +# +# REQUIRES FSL (https://fsl.fmrib.ox.ac.uk/fsl/fslwiki/FAST) +# Note: Since FSL tools work only in linux based OS, this script +# has been launch from WSL2 +# +# 2022-03-23 Andrés le Boeuf +# andres.le.boeuf@estudiantat.upc.edu +# ----------------------------------------------------------- + +if [ "$1" == "-h" ] ; then + echo "Entrance variables: " + echo "\$1 = output image base name" + exit 1 +fi + +INPUT_IMAGE=$1 + +GETF0="/usr/local/fsl/bin/fast" + +$GETF0 -t 2 -n 3 -H 0.1 -I 4 -l 20.0 -o $1 diff --git a/White_Matter_improvement/gmm_clustering.py b/White_Matter_improvement/gmm_clustering.py new file mode 100644 index 0000000..dcd9330 --- /dev/null +++ b/White_Matter_improvement/gmm_clustering.py @@ -0,0 +1,145 @@ +# ----------------------------------------------------------- +# Python script to crop the white matter volume from the +# high resolution reference model and apply on it a 2 classes +# unsupervised segmentation through Gaussian Mixture Models. +# +# 2022-03-23 Andrés le Boeuf +# andres.le.boeuf@estudiantat.upc.edu +# ----------------------------------------------------------- + +import os +from os import listdir +import numpy as np +from sklearn.mixture import GaussianMixture +import nibabel as nib + +def _gmm(seg_path, path, K, covariance='full', warm_start=True): + """ + This function apply GMM algorithm to the image forwarded as + its path with K classes. Saves the clustered nifti image + and the posteriori probability maps of each class. + + inputs: - seg_path: Path to the white matter volume that we want to segment + - path: Folders main path to save the segmentation and posteriori probability maps + - K: Number of classes + - covariance: - full: each component has its own general covariance matrix. + - Other options see sklearn.mixture.GaussianMixture documentation + - warm_start: If is True, the solution of the last fitting is used as + initialization for the next call of fit(). Can speed up the convergence + + """ + Image_nii = nib.load(seg_path) + Image = Image_nii.get_fdata() + + #Converts input data into 1D array (eack row = data point, each column (1) = feature (intensity)) + if(len(Image.shape)<3): + Z = Image.reshape((-1,1)) + elif (len(Image.shape) == 3 and Image.shape[2]==3): + Z = Image.reshape((-1,3)) + elif (len(Image.shape) == 3 and Image.shape[2]>3): + Z = Image.reshape((-1,1)) + + #Creates GMM model + gmm = GaussianMixture(n_components=K, covariance_type=covariance, init_params = 'kmeans', n_init = 1, warm_start=warm_start) + #Trains the GMM model + gmm.fit(Z) + #Sort means of each class to match labels between segmentations and with FAST method + gmm.means_ = _sort(gmm.means_) + #Applies the predictions from the model to the image + labels = gmm.predict(Z) + prob_maps = gmm.predict_proba(Z) + cluster = labels.reshape(Image.shape) + print(gmm.means_) + clustered_image = nib.Nifti1Image(dataobj=cluster, affine=Image_nii.affine) + clustered_image._header = Image_nii.header + + gmm_path = path + "\\" + path.split("\\")[-1] + "_gmm.nii" + print("Saving images into " + path) + nib.save(img=clustered_image, filename=gmm_path) + + for i in range(prob_maps.shape[-1]): + map_path = path + "\\" + path.split("\\")[-1] + "_map" + str(i) + ".nii" + prob_map = nib.Nifti1Image(dataobj=prob_maps[:,i].reshape(Image.shape), affine=Image_nii.affine) + prob_map._header = Image_nii.header + nib.save(img=prob_map, filename=map_path) + +#Extracts the WM from all the nifti images allocated into Gholipour or FeTA Dataset. +def extract_WM(folder_path): + """ + Extracts the white matter from all the nifti + images allocated into the dataset. + + inputs: - folder_path: Path were reference + volumes' folders are + """ + segmentation_name = "tissue" + wm = np.array([91, 110, 111, 114, 115, 116, 117, 118, 119, 120, 121, 122, 123, 125]) #This WM label selection was performed through the annotations. + #without cerebellum and brainstem + + for folders in listdir(folder_path): + path= folder_path + "\\" + folders + for files in listdir(path): + if segmentation_name in files: + + tissue_path = path + "\\" + files + replacement = "_" + segmentation_name + brain_path = tissue_path.replace(replacement, "") + print("Extracting WM from " + brain_path.split("\\")[-1] ) + + brain = nib.load(brain_path).get_fdata() + tissues_nii = nib.load(tissue_path) + tissues = tissues_nii.get_fdata() + + tissues[~(np.isin(tissues,wm))] = 0 #Every voxel non classified as white matter will not be considered + tissues[tissues!=0] = 1 + wm_tissue = brain*tissues #WM extraction + + wm_image = nib.Nifti1Image(dataobj=wm_tissue, affine=tissues_nii.affine) + wm_image._header = tissues_nii.header + save_path = path + "\\" + path.split("\\")[-1] + "_WM.nii" + if os.path.exists(save_path): + os.remove(save_path) + nib.save(img=wm_image,filename=save_path) + +def segment(folder_path, classes): + """ + Applies GMM 2 classes segmentation + to each extracted WM nifti image from a dataset + + inputs: - folder_path: Path were reference + volumes' folders are + """ + for folders in listdir(folder_path): + path= folder_path + "\\" + folders + for files in listdir(path): + if "WM" in files: + seg_path = path + "\\" + files + + print("Performing GMM clustering on " + seg_path.split("\\")[-1]) + _gmm(seg_path, path, classes, covariance='full', warm_start=True) + +def _sort(array): + """ + Sorts an array to match the order of labels between segmentations and with FAST method + + inputs: - array: Array of mean values of each class. + + outputs: - rev_sorted: Array of mean values sorted in the desired order. + """ + rev_sorted = np.sort(array, axis=0)[::-1] + rev_sorted=rev_sorted[rev_sorted!=0] + rev_sorted = np.insert(rev_sorted, [0],0).reshape(-1,1) + + return rev_sorted +def main(): + #Define paths to dataset + folder_path = r'..\data\atlas_gmm_clustering' + + #Extract the WM from each subject + extract_WM(folder_path) + + #Segments the WM of each subject + segment(folder_path, 3) #2 classes for WM and one for background + +if __name__ == "__main__": + main() \ No newline at end of file diff --git a/White_Matter_improvement/histogram_distr_for_Gholipour.ipynb b/White_Matter_improvement/histogram_distr_for_Gholipour.ipynb new file mode 100644 index 0000000..6980ff0 --- /dev/null +++ b/White_Matter_improvement/histogram_distr_for_Gholipour.ipynb @@ -0,0 +1,350 @@ +{ + "cells": [ + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "# Clippoing values for a normative spatiotemporal atlas\n", + " [Fetal Brain Atlas](http://crl.med.harvard.edu/research/fetal_brain_atlas/)\n", + "\n", + " This jupyter notebook shows graphically how the clipping value retrieving strategy works.\n", + " " + ] + }, + { + "cell_type": "code", + "execution_count": 3, + "metadata": {}, + "outputs": [], + "source": [ + "from os import listdir \n", + "import nibabel as nib\n", + "import numpy as np\n", + "import matplotlib.pyplot as plt\n", + "from sklearn.mixture import GaussianMixture\n", + "import scipy.stats as stats\n", + "import math\n" + ] + }, + { + "cell_type": "code", + "execution_count": 4, + "metadata": {}, + "outputs": [ + { + "data": { + "image/png": "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", + "text/plain": [ + "
" + ] + }, + "metadata": {}, + "output_type": "display_data" + }, + { + "data": { + "text/plain": [ + "GaussianMixture(n_components=2)" + ] + }, + "execution_count": 4, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "WM_img = nib.load(r'..\\data\\atlas_fast_clustering\\STA25\\STA25_WM.nii').get_fdata()\n", + "WM_newdata = np.asarray(WM_img).flatten()\n", + "hist = np.histogram(WM_newdata)\n", + "plt.hist(WM_newdata, bins=200)\n", + "plt.ylim(0,30000)\n", + "plt.show()\n", + "\n", + "gmm = GaussianMixture(n_components=2) # GMM of two classes: (1) Black background; (2) WM Volume\n", + "gmm.fit(WM_newdata.reshape(-1,1))\n" + ] + }, + { + "cell_type": "code", + "execution_count": 3, + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "Gray level mean= [1934.14235374]\n", + "Gray level variance= [[18094.7049165]]\n" + ] + } + ], + "source": [ + "print(\"Gray level mean= \", gmm.means_[-1])\n", + "print(\"Gray level variance= \", gmm.covariances_[-1])" + ] + }, + { + "cell_type": "code", + "execution_count": 4, + "metadata": {}, + "outputs": [ + { + "data": { + "image/png": "iVBORw0KGgoAAAANSUhEUgAAAYYAAAD4CAYAAADo30HgAAAAOXRFWHRTb2Z0d2FyZQBNYXRwbG90bGliIHZlcnNpb24zLjUuMSwgaHR0cHM6Ly9tYXRwbG90bGliLm9yZy/YYfK9AAAACXBIWXMAAAsTAAALEwEAmpwYAAA4jElEQVR4nO3de1wU9foH8A+7sGiogKCSgCwZJuQNFbxf8gZohBcU1AwvWeExs+MpjOqYZSfNzOr0i05EhqWuiJqUpWB4QwVWXRCUhR1EBRQRVLwLwvP7o9OeKC6KwHeXfd6v1/N6xex3hs9sNE+z39kZMwAExhhj7L9kogMwxhgzLNwYGGOMVcONgTHGWDXcGBhjjFXDjYExxlg15qIDNIbi4mKcPXtWdAzGGDMqLi4u6Nix41+Wt4jGcPbsWXh5eYmOwRhjRkWtVte4nD9KYowxVg03BsYYY9VwY2CMMVbNfTUGHx8faLVa6HQ6hIWF/eV1hUIBlUoFnU6H5ORkuLi46F9bunQpdDodtFotxo0bBwCwtLRESkoK0tLSkJmZiXfeeUc/XqlUIjk5GTqdDiqVChYWFg+5i4wxxh4U1VUymYwkSSJXV1eysLCgtLQ0cnd3rzYmNDSUIiIiCAAFBQWRSqUiAOTu7k5paWmkUChIqVSSJEkkk8kIAFlZWREAMjc3p+TkZBowYAABoM2bN1NQUBABoIiICHrppZfqzAeA1Gp1vWO4uLi4uKpXbcfOes8YvL29IUkS8vLyUFFRAZVKhYCAgGpjAgICEB0dDQCIjY3F6NGj9ctVKhXKy8tx5swZSJIEb29vAMDNmzcBABYWFrCwsAARAQBGjRqF2NhYAEB0dDQmTpxYX0TGGGONqN7G4OjoiPz8fP3PBQUFcHR0rHVMZWUlysrKYGdnV+e6MpkMGo0GxcXFSEhIQGpqKuzs7HD16lVUVlbW+rt+N3/+fKjVaqjVatjb2z/gbjPGGKuNsO8xVFVVwdPTE9bW1ti+fTuefPJJFBUV3ff6kZGRiIyMBFD7tbiMNTczmQydn3gcHV2VsHHoCJlMjoryuyjNL8SFHAmXCy+IjshYveptDIWFhXB2dtb/7OTkhMLCwhrHFBYWQi6Xw9raGqWlpfe1bllZGfbu3QtfX1+sWbMGNjY2kMvlqKysrHE8Y4ZI2bsnBk6dCI8RQ2BlY13ruOK8sziRsBeHY7ah7OKlZkzI2IOpc3JCLpdTbm4uKZVK/eSzh4dHtTELFiyoNvm8efNmAkAeHh7VJp9zc3NJJpORvb09WVtbEwBq1aoVHThwgCZMmEAAKCYmptrkc2hoaIMnULi4mrqUvXvSy999RWsyjtD7R/ZQ8Iq3yHP8OOro6kKK1q1Ibm5Ordu1JeceHjR0RiC98OVaWp2WRB9qDlLQu29SW3s74fvAZbpVx7Gz/pX9/PwoOzubJEmi8PBwAkDLly8nf39/AkCWlpYUExNDOp2OUlJSyNXVVb9ueHg4SZJEWq2WfH19CQD17NmTjh8/Tunp6ZSRkUFvv/22fryrqyulpKSQTqejmJgYUigUD7NzXFxNUq3aWFHQe2/Smowj9M9f42hw0GRStG59X+vadnaggNcX06rjB+j95D00ZHqg8P3hMs16qMZg6MWNgas5y7mHB725exutTksiv0UvkaJ1qwZtx87ZiZ6P+JjWZByh579YQ1Y21sL3jcu0qsGXqzLG/qeP7xj8bd0XoCrCv597Eb989iXKb99p0LZK8wvwdejfse39j/C4dz8s2vA1Oii7NHJixh4cNwbG7tNTc2Zi1ur3cO7kKXw6Yx7OnTjZKNs9pNqKiHkLYWn1CBZ9Hwlln16Nsl3GGoobA2P3YcwLs/H03xdC80sC/vP8Ity8crVRt382PROfzXweNy5fwfwvP4ayd89G3T5jD4IbA2P1GDXvOfi9/CKO/vgLNr6xHJX37jXJ77lceAER8xbiWnEJ5n+5Fl16Pdkkv4ex+nBjYKwO/Z8ZjwmLQ3F8526o3lqBqv9+K7+pXLtUgoh5C3G99DKe//wj2HdxatLfx1hNuDEwVotug7ww7Z03kJOshuqtFaCqqmb5vdculSAy9O8gIjz/xcd1fmGOsabAjYGxGrR36oxZH61A8ZmziH71jSb7+Kg2pfkF+GbR67Bx6IiQtR9AJpc36+9npo0bA2N/Ym5pidkffwAAWLcoDHdu3BSS42x6JmKW/Qtd+3ti/KKXhGRgpokbA2N/MuWtf8DRvRs2Ll2O0gKx9+o6vjMeh1Rb8dTcZ9Fj1HChWZjp4MbA2B/0fdoH3hOfRvyX3yDr4GHRcQAAOz78FOcyTyF4xduw7ewgOg4zAdwYGPsvG4dOmBz+D+QdT0d8RJToOHqVFRVYv+RNmJmZIXjF2zAzMxMdibVw3BgYA2BmZobp778NM5kZNr75brNdgXS/rpwvwg+r1uJxr74Y9myQ6DishePGwBiAYc8G4XHvftix8hNcLjgvOk6N1D/sRObeAxj/ykvo1NVVdBzWgnFjYCbPtrMDfBe+gJP7kpD6w0+i49RpyzsrcffmLQQtD+ePlFiT4cbATN6Ut18HQNj2/keio9TrxuUr2LH6U7j07oGBUyeKjsNaKG4MzKT18R0D96GD8Mu/v8LVooui49yX4z/tRk6yGhNeCUVbezvRcVgLxI2BmazW7doiIGwxzmWeQtLGLaLjPJCtK1bD3FKBgNdfER2FtUDcGJjJ8lnwPNrY2iB2+SqDuwqpPiVn8/Hr1+vh6TcW3QZ5i47DWhhuDMwkdXR1weCgyUjeGodCbY7oOA2SGPUdSs4VIOD1V/heSqxRcWNgJumZ119B+a3b2PX5V6KjNFhlRQV+XPM5HB5/DAMDA0THYS0INwZmcroPGwT3oYMQ/59vGv1JbM0tM3E/pNRj8P3bfLRq20Z0HNZCcGNgJkVmLkfAa6+gOO8sDm2MFR2nUez48FO0tm6HsS/OER2FtRDcGJhJGTR1Ejq6uiBu9WfN/oyFpnI+W4fUbT9i2Ixp/MQ31ii4MTCToWjdGmNfnAMp9ZjB3Dm1sez6/CvcqyiH78IXREdhLcB9NQYfHx9otVrodDqEhYX95XWFQgGVSgWdTofk5GS4uLjoX1u6dCl0Oh20Wi3GjRsHAHByckJiYiJOnjyJzMxMLFq0SD9+2bJlKCgogEajgUajgZ+f38PuI2MAgGHPTkNbu/bY+WmE6CiN7nrpZRz4bjM8/cai8xNuouOwFoDqKplMRpIkkaurK1lYWFBaWhq5u7tXGxMaGkoREREEgIKCgkilUhEAcnd3p7S0NFIoFKRUKkmSJJLJZOTg4ECenp4EgNq0aUPZ2dn6bS5btoyWLFlSZ6Y/l1qtfqDxXKZXrdu1oxWH4mnOZ6uEZ2mqatW2Db2XtJvmff6R8CxcxlG1HTvrPWPw9vaGJEnIy8tDRUUFVCoVAgICqo0JCAhAdHQ0ACA2NhajR4/WL1epVCgvL8eZM2cgSRK8vb1RVFQEjUYDALhx4waysrLg6OhYXxTGGmzUvGdh2cYKv3z2H9FRmsyd6zeQ+M138BgxBMo+vUTHYUas3sbg6OiI/Px8/c8FBQV/OYj/cUxlZSXKyspgZ2d3X+u6uLjA09MTKSkp+mULFy5Eeno6oqKiYGNj06AdY+x37TrYY9iMaTi+czeKpNOi4zSpQ5tice1SCfwWvSg6CjNiQiefrayssHXrVixevBjXr18HAERERKBr167o06cPLly4gDVr1tS47vz586FWq6FWq2Fvb9+csZmRGfPCbMjkcuz+4mvRUZpc+e072PPVt3jcqy+6DfISHYcZqXobQ2FhIZydnfU/Ozk5obCwsNYxcrkc1tbWKC0trXNdc3NzbN26FRs2bMD27dv1Y4qLi1FVVQUiQmRkJLy9a74PTGRkJLy8vODl5YWSkpIH2GVmSqw7dcCAyf5I2f6jwT6Ap7Elx+5AacF5+C7kswbWMPU2BrVaDTc3NyiVSlhYWCA4OBhxcXHVxsTFxSEkJAQAEBgYiMTERP3y4OBgKBQKKJVKuLm5ITU1FQAQFRWFrKwsrF27ttq2HBz+97DzSZMmITMz8+H2kJm0p+Y8CzMzGRK/Xi86SrOpvHcPv34dDZdeT/IN9liD1Ttz7efnR9nZ2SRJEoWHhxMAWr58Ofn7+xMAsrS0pJiYGNLpdJSSkkKurq76dcPDw0mSJNJqteTr60sAaMiQIURElJ6eThqNhjQaDfn5+REAWr9+PZ04cYLS09Npx44d5ODg0OCZdS7Trrb2drTy6D6a9s4bwrM0d8ktLOjthB/ob99GCM/CZbhVx7FTfLgm3DkuEy7/f7xMH2oOkp2To/AsImrI9EBak3GEHuvvKTwLl2FWgy9XZcwYtWlvi0FTJ0HzcwJKCwrrX6EFStn2I66VlPI9lNgD48bAWqQRzwXDopUl9kR+KzqKMPfu3sW+dRvQbaAXXHr3EB2HGRFuDKzFecS6HQYHT0H6rj24dOac6DhCHdmyHTevXOWzBvZAuDGwFmfo9EC0srLCnsho0VGEK799B/vXq+A+bDCcPJ4QHYcZCW4MrEWxaGWJIdMDcXLvwRb/Lef7lbRpC25fv4Gn5jwrOgozEtwYWIviFTABbdrbYu+3G0RHMRh3b97CkS3b0WvsU2jv1Fl0HGYEuDGwFsNMJsOIkOk4k56BvOPpouMYlIPfx6CqqgojnpsuOgozAtwYWIvRc8xI2Ds7Ye83fLbwZ9culeD4zt3wnvg0rGysRcdhBo4bA2sxRs19FsV5Z3Fy30HRUQzSvm83QtG6FQYHTxEdhRk4bgysRejq1RfOT7pj//pNoKoq0XEM0sXcPJzafwhDgqfA3NJSdBxmwLgxsBbhqTkzcb30Mo7G/SI6ikHb++0GtLVrD69nxouOwgwYNwZm9B7t1hXuwwbj4PcxuFdeLjqOQTt9VINzGacwImQ6zGT8nz+rGf9lMKM34rnpuHvrFg7HbBMdxSjsXfc9Org4o8dTw0RHYQaKGwMzam3a28LTbyzUO37G7WvXRccxChm/7kfJuQKMCJkhOgozUNwYmFEbMOUZmCsUOLQpVnQUo0FVVUjauAWunr3g/KS76DjMAHFjYEZLZi7H4KDJyD6cguK8s6LjGJXUH37CnRs3MezZaaKjMAPEjYEZrR5PDYdNp45I2shnCw/q7s1bSN3+E3r7jEa7Dvai4zADw42BGa2hM6aitOA8sg4eFh3FKCVt3AKZ/LezLsb+iBsDM0qPduuKrv09cVi1lb/Q1kClBYU4tT8JAwMDYK5QiI7DDAg3BmaUhk6fivLbd5Cy/SfRUYzawe9j0NauPTzHjxUdhRkQbgzM6LRu1w59J/jg2M5duH3tmug4Rk1KPYbzORKGPxskOgozINwYmNEZMNkfitateNK5kRz8Pgadn3BD1/6eoqMwA8GNgRkVM5kMg4MmQ1IfR5EuV3ScFuH4z/G4eeUqhs/iswb2G24MzKh4DB8MO6fOSNq4RXSUFuPe3bs4vGU7PEYO4ye8MQDcGJiRGTpjKq4WXcTJvfzMhcZ0WLUNVFmFoTOmio7CDMB9NQYfHx9otVrodDqEhYX95XWFQgGVSgWdTofk5GS4uLjoX1u6dCl0Oh20Wi3GjRsHAHByckJiYiJOnjyJzMxMLFq0SD/e1tYW8fHxyMnJQXx8PGxsbB5yF1lL0dHVBd0GeePw5u2oqqwUHadFuXapBOkJifCe+DQUrVuLjsMMANVVMpmMJEkiV1dXsrCwoLS0NHJ3d682JjQ0lCIiIggABQUFkUqlIgDk7u5OaWlppFAoSKlUkiRJJJPJyMHBgTw9PQkAtWnThrKzs/XbXLVqFYWFhREACgsLo5UrV9aZDwCp1ep6x3AZf01+8x+08ug+srK1EZ6lJZZL7x60JuMIDZw6UXgWruap2o6d9Z4xeHt7Q5Ik5OXloaKiAiqVCgEBAdXGBAQEIDo6GgAQGxuL0aNH65erVCqUl5fjzJkzkCQJ3t7eKCoqgkajAQDcuHEDWVlZcHR0/Mu2oqOjMXHixPoiMhPQqo0V+j/jh7Rde3DzylXRcVqks+mZKDiVjaHTA0VHYYLV2xgcHR2Rn5+v/7mgoEB/EK9pTGVlJcrKymBnZ3df67q4uMDT0xMpKSkAgE6dOqGoqAgAUFRUhE6dOtWYa/78+VCr1VCr1bC353u9tHReARNg+cgjPOncxA5tisWjbl3xGF+6atKETj5bWVlh69atWLx4Ma5fr/le+kRU4/LIyEh4eXnBy8sLJSUlTRmTCWZmZoYh0wNxJi0DBaeyRcdp0Y7/koCbV8v4rMHE1dsYCgsL4ezsrP/ZyckJhYWFtY6Ry+WwtrZGaWlpneuam5tj69at2LBhA7Zv364fc/HiRTg4OAAAHBwcUFxc/BC7x1qCboMHoIOLM58tNIN7d+8idftP6DFqONp17CA6DhOk3sagVqvh5uYGpVIJCwsLBAcHIy4urtqYuLg4hISEAAACAwORmJioXx4cHAyFQgGlUgk3NzekpqYCAKKiopCVlYW1a9fWuq2QkBDs2LHj4feSGbWhMwJx7VIJTiTsFR3FJByO2QYzmQyDpk4UHYUJVO/MtZ+fH2VnZ5MkSRQeHk4AaPny5eTv708AyNLSkmJiYkin01FKSgq5urrq1w0PDydJkkir1ZKvry8BoCFDhhARUXp6Omk0GtJoNOTn50cAqH379rRnzx7KycmhhIQEsrW1bfDMOpfxl52zE61OP0TjQucJz2JKNfffq+mdfTtJbmEhPAtX01Udx07x4Zpw57iMvJ55/RX68PhBamtvJzyLKdUTgwfQmowj5Dl+nPAsXE1XDb5clTFRFK1bw3vi00hPSMT1klLRcUxKzpFUXDqbjyHBU0RHYQJwY2AGq5+/L1q3bcOTzgIQEQ6ptsLVsxcc3buJjsOaGTcGZrCGTg9E/sksnE3PFB3FJKl37MTdW7cxJJgvXTU13BiYQXIb0B8Ojz/Gz1wQ6M71Gzi+czf6jh+HR6zbiY7DmhE3BmaQhkwPxI3LV5C2a4/oKCbtkCoWFq0s4T3xadFRWDPixsAMju2jDnhy5FAkb43DvfJy0XFM2oWcXOQe1WBw8GSYyfhwYSr43zQzOIODJ4OIcGTzNtFRGIBDqq2wc3JE96GDREdhzYQbAzMo5paWGDD5GWQmHsDVi3w7FEOQ8es+lBVfwpDpfOmqqeDGwAxK3/HjYGVjjaRNPOlsKKruVeLIlh/gPnQQ7Ls4iY7DmgE3BmZQhs4IxPkcCaePakRHYX+QvOUH3KuowGD+wptJ4MbADIZr395w7N6Nv9BmgK6XXkZGwl54B0yAonUr0XFYE+PGwAzG0OmBuHXtGjQ/x4uOwmqQtGkrWrdri75P+4qOwpoYNwZmENp17ICeY0YiddtPKL99R3QcVoMzaSdQmJXDD/ExAdwYmEEYPG0SzGQyHNq8VXQUVoekjVvwqFtXdOVHf7Zo3BiYcHILCwwMDEDW/kO4XHBedBxWB/2jP2dMFR2FNSFuDEy4Pj6j0dauPZI28aSzobt39y5StsWhx6jhsHHoJDoOayLcGJhwQ2dMxcXTZ5BzRC06CrsPh1W/fSN90LRJgpOwpsKNgQnVpacHuvT0wCH+QpvRuHKhCCf3JWHglGdgrlCIjsOaADcGJtSQ6YG4c+Mmjsb9IjoKewBJG7egTXtbePqNER2FNQFuDEyYNna26OM75r8PhLklOg57AFLqMRRJpzGEJ6FbJG4MTJiBgRNhbmGBQyq+RNUYJW2KhbNHd7j07iE6Cmtk3BiYEHJzcwyeNglZSUdw6cw50XFYAxz7cRduX7/Bl662QNwYmBC9xj4F644dkLQhRnQU1kDlt29D/cNO9B47Cm3t7UTHYY2IGwMTYujMqbh05hyyD6WIjsIewiFVLOQW5hgUGCA6CmtE3BhYs3Pu4QFl755I2rQFRCQ6DnsIJecKkHXwMAZNmwS5ubnoOKyR3Fdj8PHxgVarhU6nQ1hY2F9eVygUUKlU0Ol0SE5OhouLi/61pUuXQqfTQavVYty4cfrlUVFRuHjxIjIyMqpta9myZSgoKIBGo4FGo4Gfn19D940ZqKEzfrtEVb3jZ9FRWCNI2rgF7TrYo+eYkaKjsEZEdZVMJiNJksjV1ZUsLCwoLS2N3N3dq40JDQ2liIgIAkBBQUGkUqkIALm7u1NaWhopFApSKpUkSRLJZDICQMOGDSNPT0/KyMiotq1ly5bRkiVL6sz051Kr1Q80nktctbVrT6uOH6CAsMXCs3A1TpmZmdHSn2Jo4fr/CM/C9WBV27Gz3jMGb29vSJKEvLw8VFRUQKVSISAgoNqYgIAAREdHAwBiY2MxevRo/XKVSoXy8nKcOXMGkiTB29sbAHDw4EFcvny5vl/PWphBU/97iSp/07nFICIcUm2Fq2cvOLp3Ex2HNYJ6G4OjoyPy8/P1PxcUFMDR0bHWMZWVlSgrK4Odnd19rVuThQsXIj09HVFRUbCxsalxzPz586FWq6FWq2Fvb1/vNpl4cnNzDAqajFMHDqHkXIHoOKwR/f4lxaHT+dLVlsDgJp8jIiLQtWtX9OnTBxcuXMCaNWtqHBcZGQkvLy94eXmhpKSkmVOyhujtMwrt7O2QtIHvotrS3Ll+A8d+3AXP8WNhZWMtOg57SPU2hsLCQjg7O+t/dnJyQmFhYa1j5HI5rK2tUVpael/r/llxcTGqqqpARIiMjNR/9MSM39AZ01CcdxY5R1JFR2FNIGlTLCwsLTFgyjOio7CHVG9jUKvVcHNzg1KphIWFBYKDgxEXF1dtTFxcHEJCQgAAgYGBSExM1C8PDg6GQqGAUqmEm5sbUlPrPig4ODjo/3nSpEnIzMx84J1ihqdLTw+49HoSSRv5EtWW6mJuHnTJRzE4aDJkcrnoOOwh1Ttz7efnR9nZ2SRJEoWHhxMAWr58Ofn7+xMAsrS0pJiYGNLpdJSSkkKurq76dcPDw0mSJNJqteTr66tfvnHjRjp//jyVl5dTfn4+zZ07lwDQ+vXr6cSJE5Senk47duwgBweHBs+scxlOzfhgGa04nECWjzwiPAtX05XHiKG0JuMI9fEZLTwLV/1Vx7FTfLgm3DkuA6i29na06vgBeub1V4Rn4WraMjMzo6U/bqZFG74WnoWr/mrw5aqMPazfP1o4tInvotrSEREOfL8ZLr2ehLJ3T9FxWANxY2BNyqKVJYYETcapfQdRms+XqJqCo3E/41bZNQx/Llh0FNZA3BhYk+rvPx5WtjbYF71JdBTWTMpv38GRLT+g5+gRaO/4qOg4rAG4MbAmY2ZmhuGzgnAu4xTyjqeLjsOaUdKmWFAVYejMaaKjsAbgxsCajPvwIejo6oL96/lswdRcK74Eza4EDJjsj1ZtrETHYQ+IGwNrMiNCpuPy+Qs4kbBXdBQmwIHvVGhlZQXvyf6io7AHxI2BNQknjyfwuFdfHNwQg6rKStFxmACFWTmQ1McxbOY0/sKbkeHGwJrEiOem486Nm0jd9qPoKEygA+s3oX3nR/lZDUaGGwNrdDadOqK3z2gkb92BOzduio7DBDq1/xAunc3H8FlBoqOwB8CNgTW6369E4buoMiLCwe83Q9m7J3/hzYhwY2CNyvKRRzAwMAAnEvbiyoUi0XGYAVDv2ImbV8vw1NyZoqOw+8SNgTWqgYEBaN22DfbzF9rYf5XfvoOkjVvQY9QIdHpMKToOuw/cGFijkVtYYMRz06FLPor8k1mi4zADcmhTLO7euo2Rc/iswRhwY2CNpr+/L6w7dUDiN+tFR2EG5ubVMqRsjUO/Cb6w6dRRdBxWD24MrFGYyWR4as6zyD+lRc4Rteg4zAD9/g344SHTBSdh9eHGwBpFzzEj0UHZBYlf89kCq9nVoos4/vNuDJwSgEes24mOw+rAjYE1ilHzZqE47ywyft0vOgozYHu/+R6Wj7TG0OmBoqOwOnBjYA+t2yBvOHt0x951G0BVVaLjMAN28fQZZO49gKEzp0HRupXoOKwW3BjYQxs1bxbKLl7CsZ92iY7CjEBi1HewsrHGgMnPiI7CasGNgT2ULj094DagP/av34TKigrRcZgROJueidyjGowImQ65ubnoOKwG3BjYQxnzwhzcKruG5NgdoqMwI7Lnq29h+6gDvCZOEB2F1YAbA2swJ48n8OTIodi/fhPu3rolOg4zIjlHUnEmPQOj54fwWYMB4sbAGmzsS3Nx69o1JG3km+WxBxcf8Q3ad34U/QPGi47C/oQbA2sQR/du6PHUcOxfr+Jba7MGyT6UjLMnTmL08yGQmfODfAwJNwbWION+P1vYECM6CjNi8V9Gwc6pM/r781mDIbmvxuDj4wOtVgudToewsLC/vK5QKKBSqaDT6ZCcnAwXFxf9a0uXLoVOp4NWq8W4ceP0y6OionDx4kVkZGRU25atrS3i4+ORk5OD+Ph42NjYNHDXWFNx7N4NPUaNwIHvNvPZAnso2oNHcC7zFMa8wGcNhobqKplMRpIkkaurK1lYWFBaWhq5u7tXGxMaGkoREREEgIKCgkilUhEAcnd3p7S0NFIoFKRUKkmSJJLJZASAhg0bRp6enpSRkVFtW6tWraKwsDACQGFhYbRy5co68wEgtVpd7xiuxqvZn6ykFYfiqVXbNsKzcBl/uQ8fQmsyjpDXxAnCs5ha1XbsrPeMwdvbG5IkIS8vDxUVFVCpVAgICKg2JiAgANHR0QCA2NhYjB49Wr9cpVKhvLwcZ86cgSRJ8Pb2BgAcPHgQly9f/svv++O2oqOjMXHixPoismb0aLfH0XP0CBz4fjPuXL8hOg5rAbIOHEL+ySyMmT8bMjmfNRiCehuDo6Mj8vPz9T8XFBTA0dGx1jGVlZUoKyuDnZ3dfa37Z506dUJR0W9P/ioqKkKnTp1qHDd//nyo1Wqo1WrY29vXtxuskfgtfAG3r13Hge83i47CWpDdX0TBvosTvPgKJYNg8JPPRFTj8sjISHh5ecHLywslJSXNnMo0KXv3xJNPDcPedRv4bIE1qqwDh3AmLQPjQufB3NJSdByTV29jKCwshLOzs/5nJycnFBYW1jpGLpfD2toapaWl97Xun128eBEODg4AAAcHBxQXF9//3rAmNX5xKK6VlOLgBj5bYI1v56cRsHHohCFBk0VHMXn1Nga1Wg03NzcolUpYWFggODgYcXFx1cbExcUhJCQEABAYGIjExET98uDgYCgUCiiVSri5uSE1NbXO3/fHbYWEhGDHDr7VgiF4YshAdO3viT3/WYfy23dEx2Et0OmjGmiTkjF6fghatbESHcfk1Ttz7efnR9nZ2SRJEoWHhxMAWr58Ofn7+xMAsrS0pJiYGNLpdJSSkkKurq76dcPDw0mSJNJqteTr66tfvnHjRjp//jyVl5dTfn4+zZ07lwBQ+/btac+ePZSTk0MJCQlka2vb4Jl1rsYpMzMzenXztxT+SyzJzc2F5+FqueXo3o3WZBwh34UvCM9iClXHsVN8uCbcOa5GqD4+o2lNxhHq+7SP8CxcLb+e/fBd+lfKr9TGrv7/KeR6uGrw5arMtMnM5fBd+AIu6HKh+TlBdBxmAnZ9/hXMFQqMmT9bdBSTxY2B1WlQ4ER0UHbBz59+yU9nY82i5FwBUrf/hEHTJsHOqe7L21nT4MbAatWqbRv4LHgeupSjOLU/SXQcZkLiI6JQWXEPE15dIDqKSeLGwGo19oU5aG3dDnGrPxMdhZmYa5dKkPjNd+g9bhQe6+8pOo7J4cbAamTn5IihM6dC/cNOnM/WiY7DTND+6I24WnQRz7y2CGZmZqLjmBRuDKxGE15dgMqKCuz6/CvRUZiJqrhzFz+t/QLOHt3R/xk/0XFMCjcG9heP9euD3uNGIfGb73HtEt9uhImj+TkeZ9Mz4bfoJShatxYdx2RwY2DVmMlkeOb1V3C16CL2R28UHYcx7Fj9Kaw7dsCoebNERzEZ3BhYNYOmToSzR3f8uOZzVNy5KzoOYzibnonjP8dj5OwZsHN2Eh3HJHBjYHpt2tvCb9GL0CUfRdquPaLjMKb340f/RmXFPUwOXyI6ikngxsD0nv7736Bo3Rrb/vWR6CiMVXPtUgl2ff4Vug8diJ5jRoqO0+JxY2AAAFfPXvAKmIAD6zehOO+s6DiM/cUh1VYUanMwcemrsHzkEdFxWjRuDAwyuRyT33oNVy4UIeE/60THYaxGVZWV2LpiNWw6dcTYl+aKjtOicWNgGDl7Bjp3exw/rPyEn7XADNrZ9Ewkx+7A8FlBcHDrKjpOi8WNwcR1UHbBuNB5OJGwF5mJ+0XHYaxeOz/5ArevXUfQ8nDI5HLRcVokbgwmzMzMDNPeeQMVd+9i2/s84cyMw62ya9j+rzXo0tMDw2cFi47TInFjMGGDgybjsX59EPfhp7heell0HMbuW9ruX3Fizz74LpyPDsououO0ONwYTJSNQyeMXxyK7EPJUO/4WXQcxh7YthWrUXHnLoLefRNmMj6UNSZ+N02QmZkZgt97C2ZmZtjy7irRcRhrkOull/HDyrVw9eyFoTOmio7TonBjMEHDZgXBbWB//LDyE1w5XyQ6DmMNduynXTi5LwkTXglFp66uouO0GNwYTMyj3bpiwiuhyPh1P1K3/yg6DmMPLeadf+HOzZt4dtVymCsUouO0CNwYTIi5QoGZK5fjVtk1bHnnA9FxGGsUN0qvQPX2CnR+wg3jF4eKjtMicGMwIRMWL8Cjbl2hevt93LxaJjoOY41Ge/AIkjZuwYhZwXhi8ADRcYweNwYT0WPUCAyfFYSkjVuQfShZdBzGGt2PH/8fLuhyEfz+22hjZys6jlG7r8bg4+MDrVYLnU6HsLCwv7yuUCigUqmg0+mQnJwMFxcX/WtLly6FTqeDVqvFuHHj6t3munXrcPr0aWg0Gmg0GvTu3fth9o/ht+c3B694C+cyTyHuo3+LjsNYk7h39y6+D1uGVlZWmPXhe/yt6IdEdZVMJiNJksjV1ZUsLCwoLS2N3N3dq40JDQ2liIgIAkBBQUGkUqkIALm7u1NaWhopFApSKpUkSRLJZLI6t7lu3TqaMmVKnZn+XGq1+oHGm1KZW1rS32Oi6b2k3WTb2UF4Hi6upq5+/n60JuMITXh1gfAshl61HTvrPWPw9vaGJEnIy8tDRUUFVCoVAgICqo0JCAhAdHQ0ACA2NhajR4/WL1epVCgvL8eZM2cgSRK8vb3va5uscUxa+ioc3bth4xvL+dJUZhKO/fgLDm/ehlFzZ6Hn6BGi4xilehuDo6Mj8vPz9T8XFBTA0dGx1jGVlZUoKyuDnZ1drevWt833338f6enp+Pjjj6Go5fKz+fPnQ61WQ61Ww97e/j5317QMnDoRAwMDsOerb5F18LDoOIw1mx9WfYKzJ04ieMXbfMuMBjC4yec33ngD3bt3h5eXF9q3b1/jnAYAREZGwsvLC15eXigpKWnmlIbvce9+mPzGEmQdPIxd/xcpOg5jzaqyogLrl7yJe+XlmPfv1Wjdrp3oSEal3sZQWFgIZ2dn/c9OTk4oLCysdYxcLoe1tTVKS0trXbeubRYV/fZxR3l5OdatWwdvb++H2D3TZN/FCSEf/wuXzp7Dd6+9DaqqEh2JsWZ3tegivl28FLadHTB77b8gNzcXHcmo1Dk5IZfLKTc3l5RKpX6i2MPDo9qYBQsWVJt83rx5MwEgDw+PapPPubm5JJPJ6tymg8P/JkjXrl1LH3zwQYMnUEyxWrdrS2FxKlq+/2dq79RZeB4uLtHVd8I4WpNxhILefVN4FkOrOo6d9a/s5+dH2dnZJEkShYeHEwBavnw5+fv7EwCytLSkmJgY0ul0lJKSQq6urvp1w8PDSZIk0mq15OvrW+c2AdCvv/5KJ06coIyMDPruu+/IysrqYXbOpMpcoaDQb/6PVh0/QK59ewvPw8VlKDUudB6tyThCo58PEZ7FkOqhGoOhFzcGkEwupzmfrqTV6YfI02+s8DxcXIZWM1e+Q2syjtCAKc8Iz2IoVduxkz90ayEC/xmGHqNGYPsHa6D5JUF0HMYMjuqtFWjdri0C/xmGuzduIm33r6IjGSyDuyqJPTj/JS9jwGR/xH/5DZI2xoqOw5hBqrx3D9F/D8cZzQnM+OAddB82SHQkg8WNwcj5L3kZI2fPQNLGLdjNl6UyVqeKO3cRtfAfuKDLxeyPP4DbQC/RkQwSNwYj5v+P/zWF7R98LDoOY0bhzo2b+Oqlxbh0Lh/zPl8N92GDRUcyONwYjJCZmRkCwhZjZMgMHNwQw02BsQd088pVRMz9G4qk05j96Uq+dcafcGMwMnJzc8xY+Q6GPxuE/es34YeVa0VHYswo3Sq7hi+ffxn5mVmY9dEK9HvaV3Qkg8GNwYhYPvIInv9iDfqOH4efPv4ccas/Ex2JMaN258ZNfPXiYpw+moYZHyzDmBfniI5kELgxGAnbzg74W3QEunr1xaY338PedRtER2KsRSi/fRuRoa9CveNn+C18AUHvvWnyt88w7b03Eo/190TImvchNzdH1N/+gezDKaIjMdaiVN67B9Vb76G0oBC+f5sP20cd8N1rb+PmlauiownBZwwGbnDQZLz01We4VXYNn8yYx02BsSaU8OU32PjGcih798TfY75Fl15Pio4kBDcGA9WqbRvMWv0eprz1GrKPpODTGfNQcja//hUZYw/l2E+78O9ZL+BeRQX+9m0Ehs4IFB2p2XFjMEDK3j2xZMt69BwzEjs/icA3L7+OOzduio7FmMko1Obgk+C5yE5KxqQ3lmDuZx+ijZ2t6FjNhhuDATFXKOD78gtY8O0XIKrC5yEvITFqPT9PgTEBbl+7jnWvhOGHVZ+g22BvvLZtg8l834Ebg4Fw7dsbS2LXY+wLc3B8Zzw+nhqCcydOio7FmEkjIhz8fjPWTpuNKxeKMPuTlXh21XK0tbcTHa1J8VVJgrVpbwvfhS9g0NSJKC04j/+88ApyjqSKjsUY+4OLp8/gs2fnY/TzIRj9/HNwHz4Euz7/CodUW1FVWSk6XqMzw2/33zZqarUaXl7GdTMsuYUFhs2chjEvzIaidSskbdyCXZ9Hovz2bdHRGGN1sHN2wqTwv8N96CCcz9Zh5ydfQJuULDpWg9R27OTG0Mxk5nL0m+CDMS/Ogb2zE07tP4Qf1/wbxXlnRUdjjD2AnqNH4OklC2Hv7ARJfRw71/4fzmWcEh3rgdR27OSPkpqJ3Nwc/Z/xw+j5IbBzckTBqWz+2IgxI5bx636c2n8IAwMDMPaluXhlYxS0SclI/OY75KqPi473UPiMoYlZ2dpgYGAABgdNhk2njjiXeQrxEd8g68Ah0dEYY41E0bo1hs4IxLBng9DO3g7nMk5h77cbkJm4H1X3DHcOgj9Kamaunr0wYMoz8PQbC3OFAtmHU3DgO5XRfhbJGKufuUKB/gHj8dTsmbDv4oRrl0qQuv0npGyLw+XCC6Lj/QU3hmZg7+KM/v5+6DvBB3ZOnXH31i2od/yMQ5tieQ6BMRNiJpOh+9BBGBQYAPfhgwEzM+SqjyNt96/ISNiLm1fLREcEwI2hSZiZmcG5hzueHDkMHiOHonO3x1FVWQldylEc+3EXMn7dz1cZMWbibDp1hPekp+E5fhw6urqg8t49SClHkZF4ANqkI7hyvkhYNm4MjcTO2Qld+3uia39PdBvsjXb2dqi8dw95mhM4ue8g0n7Zg2uXSpolC2PMuDza7XH08R2DPj6jYd/FCQBQnHcWWUlHkHMkFWfTM3H72vVmy8ONoQEsrR6Bo/sTcHJ/As493NG1nyesO3UAAFwvvQwp9RhO7U9C1sFk3L52rdF/P2Os5eqg7ILuQwai+9BB6OrlCQtLSwDABV0uzqRlIE9zAgWntLh09lyTTWBzY6iD5SOPoIPSGR2ULuio7IKOri5w7N4NHZRd9GOuXixG3vF05B7VIFd9nOcMGGONxqKVJbr0fBKunr2g9OwFZa8eaN2uLQDgXnk5iqQ8nM/R4UJOLorPnMWlM/m4cv7CQ3/r+qG+x+Dj44NPP/0UcrkcX3/9NVatWlXtdYVCgfXr16Nfv34oLS1FUFAQzp797cC5dOlSzJs3D5WVlVi0aBHi4+Pr3KZSqYRKpYKdnR2OHTuGWbNmoaKi4qF2vjZjXpyDwVMn6c8CAKCqqgpXzl9AoVaHoz/+goJTWhRkZeNG6ZUmycAYYxV37iJXfVz//QczMzN0fEwJx+5u6NzNDZ2feBzdhw6C98Sn9etUVtzD5cLz2LJ8JXKPaho9E9VVMpmMJEkiV1dXsrCwoLS0NHJ3d682JjQ0lCIiIggABQUFkUqlIgDk7u5OaWlppFAoSKlUkiRJJJPJ6tzm5s2bKSgoiABQREQEvfTSS3XmA0BqtbreMTWV9yR/Cl7xFo2a9xz1HD2COnV1JXOFokHb4uLi4mrqsrK1IWWfXuQVMJ78Fr1Ez615nzp1dW3w9uo4dta94sCBA2nXrl36n5cuXUpLly6tNmbXrl00cOBAAkByuZwuXbpU49jfx9W1zUuXLpFcLq/xdzdg57i4uLi4aqnajp313nbb0dER+fn/e3JYQUEBHB0dax1TWVmJsrIy2NnZ1bpubcvt7Oxw9epVVP73c7Oaftfv5s+fD7VaDbVaDXt7+/p2gzHG2H0y2ucxREZGwsvLC15eXigp4ctDGWOssdTbGAoLC+Hs7Kz/2cnJCYWFhbWOkcvlsLa2Rmlpaa3r1ra8tLQUNjY2kMvltf4uxhhjTa/Oz6Dkcjnl5uaSUqnUTxR7eHhUG7NgwYJqk8+bN28mAOTh4VFt8jk3N5dkMlmd24yJiak2+RwaGtrgz8m4uLi4uGqvBk8+AyA/Pz/Kzs4mSZIoPDycANDy5cvJ39+fAJClpSXFxMSQTqejlJQUcnX93yx5eHg4SZJEWq2WfH1969wmAHJ1daWUlBTS6XQUExNDivu4SogbAxcXF9eDV23HTv6CG2OMmajajp1GO/nMGGOsaXBjYIwxVk2L+CipuLhYfwsOALC3tze4S1gNMRPAuR6EIWYCONeDMsRcojK5uLigY8eONb4mfAKkscsQJ6MNMRPnMv5MnKtl5DK0TPxREmOMsWq4MTDGGKumRTaGr776SnSEvzDETADnehCGmAngXA/KEHMZWqYWMfnMGGOs8bTIMwbGGGMNx42BMcZYNUbRGKKionDx4kVkZGToly1btgwFBQXQaDTQaDTw8/PTv7Z06VLodDpotVqMGzdOv9zHxwdarRY6nQ5hYWFNkgsAFi5ciKysLGRmZlZ7DKrIXCqVSv9e5eXlQaPRNGuumjL17t0bR44cgUaj+ctX8z/99FPodDqkp6fD09NTv/y5555DTk4OcnJy8Nxzzz1Uptpy9erVC4cPH8aJEycQFxeHtm3b6l9rjvfKyckJiYmJOHnyJDIzM7Fo0SIAgK2tLeLj45GTk4P4+HjY2Njo12mO96u2XIGBgcjMzERlZSX69etXbR2R79eHH36IrKwspKenY9u2bbC2tjaIXO+++y7S09Oh0Wiwe/duPProo/p1muvv/n4Iv2a2vho2bBh5enpSRkaGftmyZctoyZIlfxnbkMeJNmaukSNHUkJCgv7mfx06dDCIXH+sjz76iN5+++1mzVVTpt27d+tvrOjn50d79+7V//PPP/9MAGjAgAGUnJxMAMjW1pZyc3PJ1taWbGxsKDc3l2xsbBr9vUpNTaXhw4cTAJozZw69++67zfpeOTg4kKenJwGgNm3aUHZ2Nrm7u9OqVasoLCyMAFBYWBitXLmyWd+v2nJ1796dunXrRnv37qV+/fo1+3+LteUaO3as/mmQK1eu1L9fonO1bdtWP+bll1/W35m6Of/u6yujOGM4ePAgLl++fF9jAwICoFKpUF5ejjNnzkCSJHh7e8Pb2xuSJCEvLw8VFRVQqVQICAho9FyhoaFYuXIlysvLAQCXLl0yiFx/NG3aNGzatKlZc9WUiYjQrl07AIC1tTXOnz+vz7R+/XoAQEpKCmxsbODg4AAfHx8kJCTgypUruHr1KhISEuDr69vgTLXl6tatGw4cOAAASEhIwJQpU/S5muO9Kioq0p/R3bhxA1lZWXB0dERAQACio6MBANHR0Zg4caI+V3O8X7Xl0mq1yMnJ+ct40e9XQkKC/mmQycnJcHJyMohc169f14+xsrICEelzNdfffX2MojHUZuHChUhPT0dUVJT+tPpBHyfa2Lp164Zhw4YhOTkZ+/btQ//+/Q0i1++GDRuGixcvQpIk4bkWL16M1atX49y5c/joo4/wxhtvCM8EACdPntQfEKZOnap/qJSIXC4uLvD09ERKSgo6deqEoqIiAL8ddDp16mQQuWpjSLnmzp2LX375xWByrVixAufOncPMmTPxz3/+U1iu2hhtY4iIiEDXrl3Rp08fXLhwAWvWrBEdCQBgbm6O9u3bY+DAgXjttdcQExMjOlI106dP158tiBYaGopXX30VXbp0wauvvoqoqCjRkQD8dhBZsGABjh49irZt2+rP/pqblZUVtm7disWLF1f7v8zf/f5/ms2tvlyi1JYrPDwc9+7dw4YNGwwm11tvvYUuXbpgw4YNWLhwoZBcdTHaxlBcXIyqqioQESIjI+Ht7Q2g9keR3s8jShtDQUEBtm3bBuC3e51XVVXB3t5eeC7gt8euTp48GZs3b9YvE5krJCRE/15t2bLFYP4dZmdnw8fHB/3798emTZuQm5vb7LnMzc2xdetWbNiwAdu3bwcAXLx4EQ4ODgAABwcHFBcXG0Su2hhCrpCQEDz99NOYOXOmQeX63YYNG/QfVYr+u/+zJp3EaKxycXGpNkHo4OCg/+fFixfTpk2bCGjY40QbM9eLL75Iy5cvJwDk5uZG586dM4hcAMjHx4f27dtXbVlz5vpzplOnTtGIESMIAI0aNYqOHj1KAGj8+PHVJuFSUlII+G0S7vTp02RjY0M2NjZ0+vRpsrW1bfT36vcLBszMzCg6OprmzJnT7O9VdHQ0rV27ttqyDz/8sNrk86pVq5r9/aop1+/158ln0e+Xj48PnTx5kuzt7YX9zdeU6/HHH9f/88KFC2nLli1C/u7rqSbdeKPUxo0b6fz581ReXk75+fk0d+5cWr9+PZ04cYLS09Npx44d1RrFgz5OtDFzWVhY0HfffUcZGRl07NgxeuqppwwiFwBat24dvfjii38Z3xy5aso0ZMgQOnr0KKWlpVFycjL17dtXP/7zzz8nSZLoxIkT1Q42c+bMIZ1ORzqdjmbPnt0k79WiRYsoOzubsrOz6YMPPmj292rIkCFERJSenk4ajYY0Gg35+flR+/btac+ePZSTk0MJCQnVDg7N8X7VlmvixImUn59Pd+7coaKiItq1a5dBvF86nY7OnTunX/b71T+ic8XGxlJGRgalp6dTXFwcde7cudn/7usrviUGY4yxaox2joExxljT4MbAGGOsGm4MjDHGquHGwBhjrBpuDIwxxqrhxsAYY6wabgyMMcaq+X9JRIXkHDev2wAAAABJRU5ErkJggg==", + "text/plain": [ + "
" + ] + }, + "metadata": {}, + "output_type": "display_data" + } + ], + "source": [ + "mu = gmm.means_[-1]\n", + "variance = gmm.covariances_[-1]\n", + "sigma = math.sqrt(variance)\n", + "x = np.linspace(mu - 3*sigma, mu + 3*sigma, 1000)\n", + "plt.plot(x, stats.norm.pdf(x, mu, sigma))\n", + "plt.show()" + ] + }, + { + "cell_type": "code", + "execution_count": 6, + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "Appended WM variance from STA21_WM.nii to the list. That is: [[36255.7380993]]\n", + "Appended WM variance from STA22_WM.nii to the list. That is: [[31375.18784009]]\n", + "Appended WM variance from STA23_WM.nii to the list. That is: [[26420.11734679]]\n", + "Appended WM variance from STA24_WM.nii to the list. That is: [[20713.76400269]]\n", + "Appended WM variance from STA25_WM.nii to the list. That is: [[18094.7049165]]\n", + "Appended WM variance from STA26_WM.nii to the list. That is: [[16783.92051914]]\n", + "Appended WM variance from STA27_WM.nii to the list. That is: [[17096.9873135]]\n", + "Appended WM variance from STA28_WM.nii to the list. That is: [[16404.38450465]]\n", + "Appended WM variance from STA29_WM.nii to the list. That is: [[16491.06802072]]\n", + "Appended WM variance from STA30_WM.nii to the list. That is: [[16380.70971471]]\n", + "Appended WM variance from STA31_WM.nii to the list. That is: [[17640.96960813]]\n", + "Appended WM variance from STA32_WM.nii to the list. That is: [[17113.58953871]]\n", + "Appended WM variance from STA33_WM.nii to the list. That is: [[14762.13642645]]\n", + "Appended WM variance from STA34_WM.nii to the list. That is: [[15482.96909822]]\n", + "Appended WM variance from STA35_WM.nii to the list. That is: [[16959.93920634]]\n", + "Appended WM variance from STA36_WM.nii to the list. That is: [[18539.01824252]]\n", + "Appended WM variance from STA37_WM.nii to the list. That is: [[19015.10628953]]\n", + "Appended WM variance from STA38_WM.nii to the list. That is: [[22817.25850945]]\n", + "[array([[36255.7380993]]), array([[31375.18784009]]), array([[26420.11734679]]), array([[20713.76400269]]), array([[18094.7049165]]), array([[16783.92051914]]), array([[17096.9873135]]), array([[16404.38450465]]), array([[16491.06802072]]), array([[16380.70971471]]), array([[17640.96960813]]), array([[17113.58953871]]), array([[14762.13642645]]), array([[15482.96909822]]), array([[16959.93920634]]), array([[18539.01824252]]), array([[19015.10628953]]), array([[22817.25850945]])]\n" + ] + } + ], + "source": [ + "def variances(folder_path):\n", + " gmm = GaussianMixture(n_components=2)\n", + " variance = []\n", + " for folders in listdir(folder_path):\n", + " path= folder_path + \"\\\\\" + folders \n", + " for files in listdir(path):\n", + " if \"WM\" in files:\n", + " WM_path = path + \"\\\\\" + files\n", + " WM_data = nib.load(WM_path).get_fdata()\n", + " gmm.fit(WM_data.reshape(-1,1))\n", + " variance.append(gmm.covariances_[-1])\n", + " print(\"Appended WM variance from \", files, \" to the list. That is: \", gmm.covariances_[-1])\n", + " \n", + " return variance\n", + " \n", + "#Define paths to dataset\n", + "folder_path = r\"C:\\Users\\admin\\Desktop\\GMM and FAST segmentations on White matter\\GMM_Clustering\"\n", + "\n", + "var = variances(folder_path)\n", + "\n", + "print(var)\n", + "\n" + ] + }, + { + "cell_type": "code", + "execution_count": 5, + "metadata": {}, + "outputs": [], + "source": [ + "var = [36255.7380993, 31375.18784009, 26420.11734679, 20713.76400269,18094.7049165, 16783.92051914, 17096.9873135, 16404.38450465, 16491.06802072, 16380.70971471, 17640.96960813, 17113.58953871, 14762.13642645, 15482.96909822, 16959.93920634, 18539.01824252, 19015.10628953, 22817.25850945]\n", + "x = [21,22,23,24,25,26,27,28,29,30,31,32,33,34,35, 36, 37,38]" + ] + }, + { + "cell_type": "code", + "execution_count": 6, + "metadata": {}, + "outputs": [ + { + "data": { + "image/png": "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upSLFy9y/PhxfvzxR0JDQzl16hS7du0yj82VnJxMcHAw27dvp2/fvrz44ovUq1ePxo0bs2fPHr799luL27jpppt444038PX1pUGDBmzYsAGAHj16mPuClixZwrvvvgvAgAEDGDBggHnukgYNGhASEsK2bdsua7dx48b89ttvACQmJtKlSxcaNmzI+fPn2b17N127dqVXr15MmDCB7t2706FDB3NfkLe3Nzt37qRt27bcdNNNbNq0CQAPDw9+/vln8zY++eQTVqxYwb///W8A9u3bR+vWrZk9ezb/+c9/2Lhxo3nd48ePc91111ncD6pm0SKiXNKePXsu63weN24cTZo0ISEhATBOnYwfP/6yD6dSd9xxB//zP//DZ599xvvvv8/ixYsve7x3797cdddd9OjRg3PnzhETE0PdunVtzvXSAfGKi4vx9PSkTp06zJ07l65du3LkyBGmT59udRufffYZERERpKamMnLkSPr06VPu+m5ubrz55pvMnz+/3PX++OMP3NzcEBH++OMP9u/fzxNPPGGeqrdv375cf/31ZGRk0KZNGzZt2sQjjzxyWRs33XQTe/bs4bbbbitzGzt27KBv37689957nD9/npMnT3LrrbcycOBAxowZw8MPP8yoUaMAY0DAig48qlyf9okol7Rlyxbq1q3LmDFjzMvq1atn/n7Dhg2MHTvWPKd3SEgI9erVo2XLlhw7dowFCxawYMECOnfuDMCFCxfM6/r4+HDixAnOnTtH27Zt6d69u7ndS9e71LZt2xg6dCju7u40bdqUO+64g127dlnMv7RgFBQUUL9+ffMRU3kaNmzIzz//jKenJyNGjDAvj42NNRfUYcOGXbYPnnrqKXOfxXXXXWceKfhSWVlZtG7d+rLXMmnSJLZu3cq2bdsYM2aM+WgmNjaWnj17mkfVrVevHiEhIWRlZdGsWTPzvvL09KRDhw7mNhcuXMi6detYsWIFHh4eNGnSBHd3d77++mtefvll8+8B4IYbbqjQCM+qZtAjEeWyIiIizNP2/vLLL5w5c4bJkycDsGDBAoKDg9m9ezdubm788ssvRERE0KdPH/75z39y4cIFfvvtNx5//HEA5s+fT2pqKrt37+app55izJgx7N27l6ysLGJjY83bvHS9Rx991Lx89erV9OjRg5SUFESEF198kWPHjtGuXbsycy8sLCQyMpL09HTy8/OJj4+3+nqnTZtGXFwcv/zyC3FxceZLcidOnMgXX3zBSy+9xPr16yksLASMKXbbt2/Pzp07AWMq3kcfffSKKVj/85//0KdPH3JzcwGjiLz00kvs3LmTs2fP8vvvv5tPgRUUFPDEE0+wdOlS6tSpA8DLL7+MyWRiyJAhzJ49Gx8fHzw9Pfnggw/Yu3eveTszZ87Ex8eHxYsXM2PGDKKionB3N/5PLb102tPTk+uvv958RKlqB6d3zGhoaFiOSycuGzp0qKxZs6ZSz/f397+sk9uZERERIa+99prT89CovtAjEaVcXJcuXZgzZw5ubm6cPHmSp556qlLPz8/PJzIykoYNGzr9XhFPT0/ee+89p+agqpfOJ6KUUspm2rGulFLKZlpElFJK2UyLiFJKKZtpEVFKKWUzLSJKKaVs9v8BWWC4UOnpWR4AAAAASUVORK5CYII=", + "text/plain": [ + "
" + ] + }, + "metadata": {}, + "output_type": "display_data" + } + ], + "source": [ + "var_aux = np.array(var)\n", + "x = np.linspace(21,38,18)\n", + "x = [21,22,23,24,25,26,27,28,29,30,31,32,33,34,35, 36, 37,38]\n", + "plt.plot(x, var_aux.reshape(-1,), \"b.-\")\n", + "plt.xticks(x)\n", + "plt.xlabel('Gestational age (weeks)')\n", + "plt.ylabel('Standard deviation of the intensity')\n", + "plt.savefig('b.png', dpi=300)\n", + "plt.show()\n" + ] + }, + { + "cell_type": "code", + "execution_count": 7, + "metadata": {}, + "outputs": [ + { + "data": { + "image/png": "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jBkzGDRoEO+//z5FRUVhh0X37t0ZN24cubm59OrVi65du3LNNdeEHdYxOefCDuEwU6dOpa6ujjlz5oQdCgBdunRh6tSp3HPPPWGHcpROnTrRo0cP8vPzueOOO5g/f74nx036JF9dXU3v3r0bv87JyaG6ujrEiA7q1KkTTz31FHPmzGHhwoVhhwNAQUEBY8eOZdu2bcybN49LLrmExx57LOywqKqqoqqqipdeegmAP/zhDwwaNCjkqGDkyJFs27aNPXv2UFdXx4IFCxg6dGjYYTWqra0lKysLgKysLHbv3h1yRAcVFhZyxRVXJNSb4umnn05ubi7r169n27Zt5OTksGbNmoT4BFRVVcWCBQsAiMViHDhwgMzMzLiPm/RJPhaL0a9fP/r27Ut6ejpXX301ixcvDjsswK6gl5WVMX369LBDaTR16lR69+5Nbm4uV199NStXruS6664LOyxqa2uprKykf//+AIwYMYJNmzaFHBXs3LmT/Px8unTpAlhcZWVlIUd10OLFiyksLAQsqT799NMhR2RGjx7NnXfeydixY9m7d2/Y4TTasGEDPXv2JDc3l9zcXKqqqhg0aBC1tbVhh8aiRYsYPnw4AP369aNz586eVcsM7YKIV23MmDFu8+bNrqKiwk2dOjX0eABXUFDgnHNu/fr1bu3atW7t2rVuzJgxocd1aLv44osT5sIr4M477zwXi8Xc+vXr3cKFC1337t1Djwlw9957rysrK3OlpaXu0UcfbZz9EHSbO3eu27Vrl9u3b5+rrKx0EyZMcD169HDLly93W7ZscSUlJS4jIyMh4tq6davbuXNn49/+jBkzEuZndujjYV14bSqu9PR099hjj7nS0lL3yiuvuOHDh3vSl8oaiIhEWNIP14iIyLEpyYuIRJiSvIhIhCnJi4hEmJK8iEiEKcmLiESYkryISIT9f6+BhloVhMqpAAAAAElFTkSuQmCC", + "text/plain": [ + "
" + ] + }, + "metadata": {}, + "output_type": "display_data" + } + ], + "source": [ + "var_perc = []\n", + "for i in range(len(var_aux)-1):\n", + " j = i+1\n", + " perc = ((var_aux[j] - var_aux[i])/var_aux[i])*100\n", + " #print(perc, '\\n')\n", + " var_perc.append(perc)\n", + "\n", + "var_perc = np.array(var_perc)\n", + "plt.plot(var_perc.reshape(-1,), \"b.-\")\n", + "plt.show()\n", + " \n" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "### Clipping values estimation" + ] + }, + { + "cell_type": "code", + "execution_count": 9, + "metadata": {}, + "outputs": [ + { + "data": { + "image/png": "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nj2L7QOQjz76SO644w4BSl1r4FwOgMyePVsiIyMFkNWrV8u9994rgIwfP96e06233irvvvuuAOLl5SWrV6+WG2+88aJ29+7da38dunfvLkuXLhVANm3aJNu2bRMfHx+ZMWOGPPTQQ9K8eXP59ttvpX79+gLI1KlT5ZlnnhEfHx/57rvv7HNi3X333fbJGGNiYqR79+6yZMkS+2vfuXNn+eqrr+w5NG7c2P7ze++9V+EJ5zQ8N8peIkopN3jzzTfp1asX+fn5dOvWjf79+3P11Vfb5yU6N8dMfHw8CxcuxNfXlxUrVpCcnFxie4899hhDhgwBICgoiA4dOrBt27ZS99+rVy8++eQTzp49y+HDh/n2228JCwvjl19+Yfv27fb5npKSkggODua7776jb9++TJ06lfr169OsWTN27drFF198Ueo+OnXqxJw5c2jSpAkNGzZk/fr1AFx//fX2vpclS5bwyiuvANC/f3/69+9vX3+iYcOGdOjQgc2bNxdrt1mzZvz6668A7Nixgy5duuDv78/p06fZuXMnXbt25cYbb+Sxxx6jR48edOzY0d734ufnx9atW7nyyivp1KkTGzZsAMDb25sff/zRvo93332XpUuX8vzzzwOwZ88e2rZty+uvv86XX37JV199Zd/28OHDXHbZZaW+Dqp60QKh3G7Xrl3FOnIjIyNp3rw5CQkJgDmdMXHixGIfPOfcdNNNDBw4kA8++IB58+bx8ccfF/t97969ueWWW7j++us5deoUMTEx1K1b1+lcz588rbCwEB8fH+rUqcPbb79N165dOXjwIM8++6zDfXzwwQcMHjyYlJQURo0aRZ8+fcrc3svLixdeeIH33nuvzO3OnDmDl5cXIsKZM2fIzc1l9OjR9iVQ+/btS/v27cnIyKBdu3Zs2LCB++67r1gbnTp1YteuXdxwww0l7mPLli307duXV199ldOnT3P8+HGuueYawsPDmTBhAnfffTdjxowBzORx5Z2kUnk+7YNQbrdx40bq1q3LhAkT7PfVr1/f/vP69et5+OGH7Wsgd+jQgfr163P55Zfz008/sWDBAhYsWEDnzp0BKCgosG/buHFjjh07xqlTp7jyyivp0aOHvd3ztzvf5s2bueeee7DZbLRo0YKbbrqJ7du3l5r/uWJw5MgRGjRoYD/SKYu/vz8//vgjPj4+DB8+3H5/XFycvVjee++9xV6DBx980N5HcNlll9lnjT3f7t27adu2bbHnMmXKFDZt2sTmzZuZMGGC/SgkLi6Onj172mdXrV+/Ph06dGD37t1ccskl9tfKx8eHjh072tt8//33WbNmDUuXLsXb25vmzZtjs9n4/PPPefrpp+1/B4ArrriiXDP9qupBjyCUJQYPHmxfCvXnn3/mt99+Y9q0aYBZ5D44OJidO3fi5eXFzz//zODBg+nTpw9/+9vfKCgo4Ndff+X+++8H4L333iMlJYWdO3fy4IMPMmHCBNLT09m9ezdxcXH2fZ6/3YgRI+z3L1++nOuvv57k5GREhKlTp/LTTz9x1VVXlZj7iRMniIqKIi0tjUOHDhEfH+/w+T7zzDNs27aNn3/+mW3bttmHpT7++OMsXryYp556inXr1nHixAnALF0aGhrK1q1bAbPE6YgRIy5a1vLLL7+kT58+5OTkAKZAPPXUU2zdupXff/+dP/74w35a6siRI4wePZpPPvmEOnXqAPD000+TlZXFsGHDeP3112ncuDE+Pj78/e9/Jz093b6f+fPn07hxYz7++GPmzp3LokWLsNnM98tzw4d9fHxo3769/UhQ1QyWd4RoaNTWOH9RqXvuuUdWrFhRoce3bt26WIexlTF48GCZNWuW5XloVF3oEYRSFurSpQtvvvkmXl5eHD9+nAcffLBCjz906BBRUVH4+/tbfi2Ej48Pr776qqU5qKql60EopZQqkXZSK6WUKpEWCKWUUiXSAqGUUqpEWiCUUkqVSAuEUkqpEv0/0HP9cYWJB2QAAAAASUVORK5CYII=", + "text/plain": [ + "
" + ] + }, + "metadata": {}, + "output_type": "display_data" + } + ], + "source": [ + "var_perc = []\n", + "for i in range(len(var_aux)-1):\n", + " j = i+1\n", + " perc = ((var_aux[j] - var_aux[i])/var_aux[i])*100\n", + " #print(perc, '\\n')\n", + " var_perc.append(perc)\n", + "\n", + "var_perc = np.array(var_perc)\n", + "var_perc = (-1)*var_perc\n", + "#print(var_perc)\n", + "\n", + "clip_change = var_perc/100 + 1\n", + "\n", + "init_clip_value = 0.25\n", + "clip_values =[]\n", + "clip_values.append(init_clip_value)\n", + "\n", + "for i in range(len(clip_change)):\n", + " clip_values.append(clip_values[-1]*clip_change[i])\n", + "\n", + "x = np.linspace(21,38,18)\n", + "clip_values = np.array(clip_values)\n", + "plt.plot(x, clip_values.reshape(-1,), \"b.-\")\n", + "plt.xticks(x)\n", + "plt.xlabel('Gestational age (weeks)')\n", + "plt.ylabel('Clipping values')\n", + "plt.savefig('a.png', dpi=300)\n", + "plt.show()\n" + ] + }, + { + "cell_type": "code", + "execution_count": 10, + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "21 :\t 0.25\n", + "22 :\t 0.28365364018960787\n", + "23 :\t 0.32845094275825537\n", + "24 :\t 0.3993914730738441\n", + "25 :\t 0.4498907385074475\n", + "26 :\t 0.4824809223912748\n", + "27 :\t 0.4734813113921259\n", + "28 :\t 0.49266214601491515\n", + "29 :\t 0.4900588366960523\n", + "30 :\t 0.493338312773348\n", + "31 :\t 0.4553830287652675\n", + "32 :\t 0.468996789157\n", + "33 :\t 0.5334382061378529\n", + "34 :\t 0.5073905070618856\n", + "35 :\t 0.45898889834457507\n", + "36 :\t 0.416254090253734\n", + "37 :\t 0.4055645491843852\n", + "38 :\t 0.32447017466325395\n" + ] + } + ], + "source": [ + "for i in range(len(clip_values)):\n", + " print(i+21, \":\\t\", clip_values[i])" + ] + } + ], + "metadata": { + "interpreter": { + "hash": "312dda85d5c9dd3f56195e340c6a1af4ab54507251a521dba95f823a0d2f5db9" + }, + "kernelspec": { + "display_name": "Python 3.10.2 ('venv': venv)", + "language": "python", + "name": "python3" + }, + "language_info": { + "codemirror_mode": { + "name": "ipython", + "version": 3 + }, + "file_extension": ".py", + "mimetype": "text/x-python", + "name": "python", + "nbconvert_exporter": "python", + "pygments_lexer": "ipython3", + "version": "3.10.2" + }, + "orig_nbformat": 4 + }, + "nbformat": 4, + "nbformat_minor": 2 +}