From 42a9cff27874f5f490e466927c661a03bdd53fb5 Mon Sep 17 00:00:00 2001 From: aTrotier Date: Tue, 12 Nov 2024 15:48:33 +0100 Subject: [PATCH] update all --- README.md | 19 +++++++++++- docs/lit/examples/advanced_reco.jl | 21 ++++++------- docs/lit/examples/simple_reco.jl | 28 +++++++++-------- docs/make.jl | 1 + src/BIDS.jl | 11 ++++--- src/bruker_sequence.jl | 2 +- src/reconstruction.jl | 50 +++++++++++++++++++++++++++++- 7 files changed, 99 insertions(+), 33 deletions(-) diff --git a/README.md b/README.md index dc8790c..0efdeaf 100644 --- a/README.md +++ b/README.md @@ -116,10 +116,27 @@ You can install the package in any project with the following command : - enter the Julia package manager by typing `]` in the REPL. (the REPL should turn in blue) - if you want to activate an environment, type : `activate .` (otherwise the package will be installed in the global environment) - In order to add our unregistered package, type `add https://github.com/CRMSB/SEQ_BRUKER_a_MP2RAGE_CS_360` -- if you want to use the package : `using SEQ_BRUKER_a_MP2RAGE_CS_360` +- if you want to use the package in your script just add the following line : `using SEQ_BRUKER_a_MP2RAGE_CS_360` ## How to use the package +Follow the example in the [documentation](https://crmsb.github.io/SEQ_BRUKER_a_MP2RAGE_CS_360/dev/generated/examples/simple_reco/) + +**Steps :** +- Define the path to the bruker dataset +```julia +path_bruker = joinpath(datadir, "MP2RAGE_FULLY") +``` +- Perform the reconstruction +```julia +d = reconstruction_MP2RAGE(path_bruker; mean_NR=true) +``` +- write the results in the qBIDS format +```julia +subject_name = "sub_01" +dir_path = "" # directory path where the files will be create +write_bids_MP2RAGE(d,subject_name,dir_path) +``` ## Version diff --git a/docs/lit/examples/advanced_reco.jl b/docs/lit/examples/advanced_reco.jl index c951559..225a3a1 100644 --- a/docs/lit/examples/advanced_reco.jl +++ b/docs/lit/examples/advanced_reco.jl @@ -12,14 +12,12 @@ using SEQ_BRUKER_a_MP2RAGE_CS_360 using CairoMakie # plotting # In addition we load the package internally used to perform the reconstruction +using SEQ_BRUKER_a_MP2RAGE_CS_360.MRIReco +using SEQ_BRUKER_a_MP2RAGE_CS_360.MRIReco.RegularizedLeastSquares - -# ## Loading Package -using LazyArtifacts # loading data -using SEQ_BRUKER_a_MP2RAGE_CS_360 -using CairoMakie # plotting - +# ## Download the datasets +# if you run the literate example offline change the following line by : `MP2_artifacts = artifact"MP2RAGE_data" datadir = Main.MP2_artifacts @info "The test data is located at $datadir." @@ -28,8 +26,7 @@ path_bruker = joinpath(datadir, "MP2RAGE_CS2") # ## Compressed-sensing reconstruction # In order to use an advanced reconstruction we will pass some parameters that will be used by the reconstruction package MRIReco.jl -using SEQ_BRUKER_a_MP2RAGE_CS_360.MRIReco -using SEQ_BRUKER_a_MP2RAGE_CS_360.MRIReco.RegularizedLeastSquares + # We have to create a parameter dictionnary that will be used. If you need more information about it take a look at [MRIReco.jl](https://github.com/MagneticResonanceImaging/MRIReco.jl) @@ -41,7 +38,7 @@ CS[:iterations] = 30 d = reconstruction_MP2RAGE(path_bruker; mean_NR=true,paramsCS = CS) -# for comparison purpose let's perform the undersampled reconstruction (without the paramCS keyword) +# for comparison purposes let's perform the undersampled reconstruction (without the paramCS keyword) d_under = reconstruction_MP2RAGE(path_bruker; mean_NR=true) @@ -56,10 +53,10 @@ begin h=heatmap!(ax,abs.(d["im_reco"][:,:,60,1,1,1]),colormap=:grays) - ax=Axis(f[2,1],title="UNIT1 undersampled") + ax=Axis(f[2,1],title="T₁ map undersampled") h=heatmap!(ax,d_under["T1map"][:,:,60,1,1],colorrange = (500,2000)) - ax=Axis(f[2,2],title="UNIT1 CS") + ax=Axis(f[2,2],title="T₁ map CS") h=heatmap!(ax,d["T1map"][:,:,60,1,1],colorrange = (500,2000)) for ax in f.content # hide decoration befor adding colorbar @@ -77,4 +74,4 @@ end subject_name = "sub_01_cs" dir_path = "" # directory path where the files will be create -write_bids_MP2RAGE(d,subject_name,dir_path) +write_bids_MP2RAGE(d,subject_name,dir_path) \ No newline at end of file diff --git a/docs/lit/examples/simple_reco.jl b/docs/lit/examples/simple_reco.jl index 32bd875..d855cc0 100644 --- a/docs/lit/examples/simple_reco.jl +++ b/docs/lit/examples/simple_reco.jl @@ -13,7 +13,7 @@ using SEQ_BRUKER_a_MP2RAGE_CS_360 using CairoMakie # plotting # ## Download the datasets - +# if you run the literate example offline change the following line by : `MP2_artifacts = artifact"MP2RAGE_data" datadir = Main.MP2_artifacts @info "The test data is located at $datadir." @@ -21,23 +21,23 @@ datadir = Main.MP2_artifacts path_bruker = joinpath(datadir, "MP2RAGE_FULLY") # ## Perform the reconstruction -# this function will perform a standard reconstruction without compressed-sensing. If your data are subsampled, results will be undersampled reconstruction. +# this function will perform a standard reconstruction without compressed-sensing. If your data are subsampled it will result in subsampling artifacts (blurring + noise-like) # -# the keyword mean_NR=true will average the image before performing the MP2RAGE/T1 maps estimation. +# the keyword mean_NR=true will average the images accross the number of repetition dimension before performing the MP2RAGE/T1 maps estimation. # Otherwise an image/T₁ map will be generated for each Number Of Repetition (NR) d = reconstruction_MP2RAGE(path_bruker; mean_NR=true) # the result is a dictionnary with the following fields : -# - "im_reco" : (x,y,z,Number of Repetition,TI) Complex -# - "MP2RAGE" : (x,y,z,TI) Float -# - "T1map" : (x,y,z,Number of Repetition) Float +# - "im_reco" : (x,y,z,Number of Channel , Number of Repetition,TI) Complex +# - "MP2RAGE" : (x,y,z,Number of Channel , Number of Repetition) Float +# - "T1map" : (x,y,z,Number of Channel , Number of Repetition) Float # - "params_prot" # - "params_reco" # - "params_MP2RAGE" # -# im_reco corresponds to the TI₁ and \TI₂ images in the complex format with 6 dimensions : -# (x,y,z,Number of Repetition,TI) +# im_reco corresponds to the TI₁ and TI₂ images in the complex format with 6 dimensions : +# (x,y,z, Number of Channel , Number of Repetition,TI) # We can check the results @@ -53,7 +53,7 @@ begin ax=Axis(f[2,1],title="UNIT1 / MP2RAGE") h=heatmap!(ax,d["MP2RAGE"][:,:,60,1,1],colormap=:grays) - ax=Axis(f[2,2],title="UNIT1 / MP2RAGE") + ax=Axis(f[2,2],title="T₁ map") h=heatmap!(ax,d["T1map"][:,:,60,1,1],colorrange = (500,2000)) for ax in f.content # hide decoration befor adding colorbar @@ -64,8 +64,8 @@ begin f end -# The Lookup table used for the reconstruction is stored in the dictionnary (LUT) -# First columns is the range of T1. +# The Lookup table used for the reconstruction is stored in the dictionnary (LUT). +# First dimension is the range of T1 and the 2nd is the expected value of the MP2RAGE signal between -0.5 to 0.5. f=Figure() ax = Axis(f[1,1],xlabel="T₁ [ms]") lines!(ax,d["LUT"]) @@ -94,7 +94,9 @@ sub_01/ └─ sub_01_inv-2-phase_MP2RAGE.nii.gz ``` -If you want to generate the T1 map with another tools like qMRLab -the required MP2RAGE parameters are stored in the **MP2RAGE.json** file. +For simplicity the T₁ map is stored in the anat/ folder like the ones created by Siemens. + +If you want to generate the T1 map with another tool like qMRLab +the required MP2RAGE parameters are stored in the **MP2RAGE.json** file. In that case the data are supposed to be stored in a derivatives folder (see[qBIDS format recommandation](https://bids-specification.readthedocs.io/en/stable/appendices/qmri.html)) =# diff --git a/docs/make.jl b/docs/make.jl index 77942fe..bbd6ccf 100644 --- a/docs/make.jl +++ b/docs/make.jl @@ -22,6 +22,7 @@ makedocs(; "Home" => "index.md", "Examples" =>["generated/examples/simple_reco.md", "generated/examples/advanced_reco.md"], + "API" => "api.md" ], ) diff --git a/src/BIDS.jl b/src/BIDS.jl index f5bd53d..7959a2d 100644 --- a/src/BIDS.jl +++ b/src/BIDS.jl @@ -7,12 +7,13 @@ This function writes data from a dictionary (`d`) in BIDS (Brain Imaging Data St **Arguments:** -* `d` (Dict): A dictionary containing the data to be written. Expected keys: +* `d` (Dict): A dictionary containing the data to be written. Expected key-value pairs: * `im_reco` (Array): 5D array containing the reconstructed images. - * `MP2RAGE` (Array): T1 map image. - * `T1maps` (Array): Additional T1 map data (optional). - * `params_prot` (Dict): Dictionary containing acquisition parameters. - * `params_MP2RAGE` (Dict): Dictionary containing MP2RAGE specific parameters. + * `MP2RAGE` (Array): Combined MP2RAGE image data. + * `T1map` (Array): Calculated T1 map from MP2RAGE images. + * `params_prot` (Dict): Protocol parameters extracted from the Bruker file. + * `params_MP2RAGE` (Struct): Dictionary containing MP2RAGE specific parameters. + * `subname` (AbstractString): The name of the subject. * `folder` (AbstractString, optional): The folder where the BIDS data will be written. Defaults to the current directory. diff --git a/src/bruker_sequence.jl b/src/bruker_sequence.jl index 3ef4887..f13b1ca 100644 --- a/src/bruker_sequence.jl +++ b/src/bruker_sequence.jl @@ -3,7 +3,7 @@ export RawAcquisitionData_MP2RAGE """ RawAcquisitionData_MP2RAGE(b::BrukerFile) -Convert a Bruker dataset acquired with the a_MP2RAGE_CS_360 sequence into a +Convert a Bruker dataset acquired with the a\\_MP2RAGE\\_CS\\_360 sequence into a `RawAcquisitionData` object compatible with the MRIReco functions. Input : diff --git a/src/reconstruction.jl b/src/reconstruction.jl index 59b9859..fea734b 100644 --- a/src/reconstruction.jl +++ b/src/reconstruction.jl @@ -1,4 +1,41 @@ -export reconstruction_MP2RAGE +export reconstruction_MP2RAGE, params_from_seq + +""" + reconstruction_MP2RAGE(path_bruker::String; mean_NR::Bool = false, paramsCS::Dict = Dict()) + +Reconstructs MP2RAGE MRI data from a specified Bruker file path, returning images and T1 maps. + +# Arguments +- `path_bruker::String`: Path to the Bruker file containing MRI acquisition data. +- `mean_NR::Bool`: If `true`, calculates the mean of the reconstructed data accross the repetition (default: `false`). +- `paramsCS::Dict`: Optional dictionary for customizing reconstruction parameters. These values override the default parameter dictionary (`params`). + +# Returns +A dictionary with the following key-value pairs: +- `"im_reco"`: Reconstructed MP2RAGE image array, permuted to match the expected order. +- `"MP2RAGE"`: Combined MP2RAGE image data. +- `"T1map"`: Calculated T1 map from MP2RAGE images. +- `"params_reco"`: Dictionary of parameters used for reconstruction. +- `"params_MP2RAGE"`: Sequence parameters derived from the Bruker file. +- `"params_prot"`: Protocol parameters extracted from the Bruker file. +- `"LUT"`: Lookup table with T1 range and associated values. + +# Description +The function performs the following steps: +1. Reads acquisition data from the Bruker file at `path_bruker`. +2. Constructs calibration data using an ESPIRiT sensitivity map from low-resolution data. +3. Sets reconstruction parameters, allowing for custom parameters specified in `paramsCS`. +4. Reconstructs the MP2RAGE image data and optionally averages across repeated measurements. +5. Permutes the resulting array dimensions for compatibility. +6. Extracts T1 maps from MP2RAGE images and constructs a lookup table (`LUT`). + +# Example +```julia +d = reconstruction_MP2RAGE("path/to/bruker_data", mean_NR = true, paramsCS = Dict(:iterations => 5)) +println(d["T1map"]) +``` + +""" function reconstruction_MP2RAGE(path_bruker;mean_NR::Bool = false,paramsCS=Dict()) b = BrukerFile(path_bruker) @@ -45,6 +82,17 @@ function reconstruction_MP2RAGE(path_bruker;mean_NR::Bool = false,paramsCS=Dict( return d end +""" + params_from_seq(b::BrukerFile) + +Extracts MP2RAGE sequence parameters from a Bruker file and returns them in a `ParamsMP2RAGE` structure. + +# Arguments +- `b::BrukerFile`: Bruker file containing sequence parameter information. + +# Returns +A `ParamsMP2RAGE` structure containing key sequence timings and settings for MP2RAGE reconstruction. +""" function params_from_seq(b::BrukerFile) return ParamsMP2RAGE( parse(Float64,b["EffectiveTI"][1]),