From 271d98f9fc7cd64a00568cb8e7c0dacd8f18a11d Mon Sep 17 00:00:00 2001 From: Guillermo Date: Sun, 22 Sep 2024 20:13:33 -0300 Subject: [PATCH 01/14] Custom values for brain_phantom --- KomaMRIBase/src/datatypes/Phantom.jl | 374 +++++++++++++++++++-------- 1 file changed, 264 insertions(+), 110 deletions(-) diff --git a/KomaMRIBase/src/datatypes/Phantom.jl b/KomaMRIBase/src/datatypes/Phantom.jl index 5f155f28e..11fb813b9 100644 --- a/KomaMRIBase/src/datatypes/Phantom.jl +++ b/KomaMRIBase/src/datatypes/Phantom.jl @@ -212,7 +212,7 @@ Default ss=4 sample spacing is 2 mm. Original file (ss=1) sample spacing is .5 m - `axis`: (`::String`, `="axial"`, opts=[`"axial"`, `"coronal"`, `"sagittal"`]) orientation of the phantom - `ss`: (`::Integer or ::Vector{Integer}`, `=4`) subsampling parameter for all axes if scaler, per axis if 2 element vector [ssx, ssy] - `us`: (`::Integer or ::Vector{Integer}`, `=1`) upsampling parameter for all axes if scaler, per axis if 2 element vector [usx, usy], if used ss is set to ss=1 - +- `custom_values`: (`::Dict`, `=nothing`) phantom custom values in ms and Hz considering the available tissues # Returns - `obj`: (`::Phantom`) Phantom struct @@ -223,11 +223,21 @@ julia> obj = brain_phantom2D(; axis="sagittal", ss=1) julia> obj = brain_phantom2D(; axis="axial", us=[1, 2]) +julia> phantom_values = Dict( + #CSF, GM, WM, FAT1, MUSCLE, SKIN/MUSCLE, SKULL, VESSELS, FAT2, DURA, MARROW + "T1"=> [2569, 1153, 746, 0, 0, 0, 0, 0, 0, 0, 0], + "T2" => [329, 83, 70, 0, 0, 0, 0, 0, 0, 0, 0], + "T2s" => [58, 69, 61, 0, 0, 0, 0, 0, 0, 0, 0], + "ρ" => [1, 0.86, 0.77, 0, 0, 0, 0, 0, 0, 0, 0], + "Δw" => [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0]) +julia> obj = brain_phantom2D(; custom_values=phantom_values) + julia> plot_phantom_map(obj, :ρ) ``` """ -function brain_phantom2D(; axis="axial", ss=4, us=1) +function brain_phantom2D(; axis="axial", ss=4, us=1, custom_values = nothing) # check and filter input + # check more spins ssx, ssy, ssz, usx, usy, usz = check_phantom_arguments(2, ss, us) # Get data from .mat file @@ -247,59 +257,127 @@ function brain_phantom2D(; axis="axial", ss=4, us=1) y = (-FOVy / 2):Δy:(FOVy / 2) #spin coordinates x, y = x .+ y' * 0, x * 0 .+ y' #grid points - # Define spin property vectors - T2 = - (class .== 23) * 329 .+ #CSF - (class .== 46) * 83 .+ #GM - (class .== 70) * 70 .+ #WM - (class .== 93) * 70 .+ #FAT1 - (class .== 116) * 47 .+ #MUSCLE - (class .== 139) * 329 .+ #SKIN/MUSCLE - (class .== 162) * 0 .+ #SKULL - (class .== 185) * 0 .+ #VESSELS - (class .== 209) * 70 .+ #FAT2 - (class .== 232) * 329 .+ #DURA - (class .== 255) * 70 #MARROW - T2s = - (class .== 23) * 58 .+ #CSF - (class .== 46) * 69 .+ #GM - (class .== 70) * 61 .+ #WM - (class .== 93) * 58 .+ #FAT1 - (class .== 116) * 30 .+ #MUSCLE - (class .== 139) * 58 .+ #SKIN/MUSCLE - (class .== 162) * 0 .+ #SKULL - (class .== 185) * 0 .+ #VESSELS - (class .== 209) * 61 .+ #FAT2 - (class .== 232) * 58 .+ #DURA - (class .== 255) * 61 #MARROW - T1 = - (class .== 23) * 2569 .+ #CSF - (class .== 46) * 833 .+ #GM - (class .== 70) * 500 .+ #WM - (class .== 93) * 350 .+ #FAT1 - (class .== 116) * 900 .+ #MUSCLE - (class .== 139) * 569 .+ #SKIN/MUSCLE - (class .== 162) * 0 .+ #SKULL - (class .== 185) * 0 .+ #VESSELS - (class .== 209) * 500 .+ #FAT2 - (class .== 232) * 2569 .+ #DURA - (class .== 255) * 500 #MARROW - ρ = - (class .== 23) * 1 .+ #CSF - (class .== 46) * 0.86 .+ #GM - (class .== 70) * 0.77 .+ #WM - (class .== 93) * 1 .+ #FAT1 - (class .== 116) * 1 .+ #MUSCLE - (class .== 139) * 1 .+ #SKIN/MUSCLE - (class .== 162) * 0 .+ #SKULL - (class .== 185) * 0 .+ #VESSELS - (class .== 209) * 0.77 .+ #FAT2 - (class .== 232) * 1 .+ #DURA - (class .== 255) * 0.77 #MARROW - Δw_fat = -220 * 2π - Δw = - (class .== 93) * Δw_fat .+ #FAT1 - (class .== 209) * Δw_fat #FAT2 + + if !isnothing(custom_values) + # Define spin property vectors + T2 = + (class .== 23) * custom_values["T2"][1] .+ #CSF + (class .== 46) * custom_values["T2"][2] .+ #GM + (class .== 70) * custom_values["T2"][3] .+ #WM + (class .== 93) * custom_values["T2"][4] .+ #FAT1 + (class .== 116) * custom_values["T2"][5] .+ #MUSCLE + (class .== 139) * custom_values["T2"][6] .+ #SKIN/MUSCLE + (class .== 162) * custom_values["T2"][7] .+ #SKULL + (class .== 185) * custom_values["T2"][8] .+ #VESSELS + (class .== 209) * custom_values["T2"][9] .+ #FAT2 + (class .== 232) * custom_values["T2"][10] .+ #DURA + (class .== 255) * custom_values["T2"][11] #MARROW + T2s = + (class .== 23) * custom_values["T2s"][1] .+ #CSF + (class .== 46) * custom_values["T2s"][2] .+ #GM + (class .== 70) * custom_values["T2s"][3] .+ #WM + (class .== 93) * custom_values["T2s"][4] .+ #FAT1 + (class .== 116) * custom_values["T2s"][5] .+ #MUSCLE + (class .== 139) * custom_values["T2s"][6] .+ #SKIN/MUSCLE + (class .== 162) * custom_values["T2s"][7] .+ #SKULL + (class .== 185) * custom_values["T2s"][8] .+ #VESSELS + (class .== 209) * custom_values["T2s"][9] .+ #FAT2 + (class .== 232) * custom_values["T2s"][10] .+ #DURA + (class .== 255) * custom_values["T2s"][11] #MARROW + T1 = + (class .== 23) * custom_values["T1"][1] .+ #CSF + (class .== 46) * custom_values["T1"][2] .+ #GM + (class .== 70) * custom_values["T1"][3] .+ #WM + (class .== 93) * custom_values["T1"][4] .+ #FAT1 + (class .== 116) * custom_values["T1"][5] .+ #MUSCLE + (class .== 139) * custom_values["T1"][6] .+ #SKIN/MUSCLE + (class .== 162) * custom_values["T1"][7] .+ #SKULL + (class .== 185) * custom_values["T1"][8] .+ #VESSELS + (class .== 209) * custom_values["T1"][9] .+ #FAT2 + (class .== 232) * custom_values["T1"][10] .+ #DURA + (class .== 255) * custom_values["T1"][11] #MARROW + ρ = + (class .== 23) * custom_values["ρ"][1] .+ #CSF + (class .== 46) * custom_values["ρ"][2] .+ #GM + (class .== 70) * custom_values["ρ"][3] .+ #WM + (class .== 93) * custom_values["ρ"][4] .+ #FAT1 + (class .== 116) * custom_values["ρ"][5] .+ #MUSCLE + (class .== 139) * custom_values["ρ"][6] .+ #SKIN/MUSCLE + (class .== 162) * custom_values["ρ"][7] .+ #SKULL + (class .== 185) * custom_values["ρ"][8] .+ #VESSELS + (class .== 209) * custom_values["ρ"][9] .+ #FAT2 + (class .== 232) * custom_values["ρ"][10] .+ #DURA + (class .== 255) * custom_values["ρ"][11] #MARROW + + Δw = + (class .== 23) * custom_values["Δw"][1] .+ #CSF + (class .== 46) * custom_values["Δw"][2] .+ #GM + (class .== 70) * custom_values["Δw"][3] .+ #WM + (class .== 93) * custom_values["Δw"][4] .+ #FAT1 + (class .== 116) * custom_values["Δw"][5] .+ #MUSCLE + (class .== 139) * custom_values["Δw"][6] .+ #SKIN/MUSCLE + (class .== 162) * custom_values["Δw"][7] .+ #SKULL + (class .== 185) * custom_values["Δw"][8] .+ #VESSELS + (class .== 209) * custom_values["Δw"][9] .+ #FAT2 + (class .== 232) * custom_values["Δw"][10] .+ #DURA + (class .== 255) * custom_values["Δw"][11] #MARROW + Δw = -2π.*Δw + + else + # Define spin property vectors + T2 = + (class .== 23) * 329 .+ #CSF + (class .== 46) * 83 .+ #GM + (class .== 70) * 70 .+ #WM + (class .== 93) * 70 .+ #FAT1 + (class .== 116) * 47 .+ #MUSCLE + (class .== 139) * 329 .+ #SKIN/MUSCLE + (class .== 162) * 0 .+ #SKULL + (class .== 185) * 0 .+ #VESSELS + (class .== 209) * 70 .+ #FAT2 + (class .== 232) * 329 .+ #DURA + (class .== 255) * 70 #MARROW + T2s = + (class .== 23) * 58 .+ #CSF + (class .== 46) * 69 .+ #GM + (class .== 70) * 61 .+ #WM + (class .== 93) * 58 .+ #FAT1 + (class .== 116) * 30 .+ #MUSCLE + (class .== 139) * 58 .+ #SKIN/MUSCLE + (class .== 162) * 0 .+ #SKULL + (class .== 185) * 0 .+ #VESSELS + (class .== 209) * 61 .+ #FAT2 + (class .== 232) * 58 .+ #DURA + (class .== 255) * 61 #MARROW + T1 = + (class .== 23) * 2569 .+ #CSF + (class .== 46) * 833 .+ #GM + (class .== 70) * 500 .+ #WM + (class .== 93) * 350 .+ #FAT1 + (class .== 116) * 900 .+ #MUSCLE + (class .== 139) * 569 .+ #SKIN/MUSCLE + (class .== 162) * 0 .+ #SKULL + (class .== 185) * 0 .+ #VESSELS + (class .== 209) * 500 .+ #FAT2 + (class .== 232) * 2569 .+ #DURA + (class .== 255) * 500 #MARROW + ρ = + (class .== 23) * 1 .+ #CSF + (class .== 46) * 0.86 .+ #GM + (class .== 70) * 0.77 .+ #WM + (class .== 93) * 1 .+ #FAT1 + (class .== 116) * 1 .+ #MUSCLE + (class .== 139) * 1 .+ #SKIN/MUSCLE + (class .== 162) * 0 .+ #SKULL + (class .== 185) * 0 .+ #VESSELS + (class .== 209) * 0.77 .+ #FAT2 + (class .== 232) * 1 .+ #DURA + (class .== 255) * 0.77 #MARROW + Δw_fat = -220 * 2π + Δw = + (class .== 93) * Δw_fat .+ #FAT1 + (class .== 209) * Δw_fat #FAT2 + end T1 = T1 * 1e-3 T2 = T2 * 1e-3 T2s = T2s * 1e-3 @@ -318,7 +396,6 @@ function brain_phantom2D(; axis="axial", ss=4, us=1) ) return obj end - """ obj = brain_phantom3D(; ss=4, us=1, start_end=[160,200]) @@ -336,6 +413,7 @@ Default ss=4 sample spacing is 2 mm. Original file (ss=1) sample spacing is .5 m - `ss`: (`::Integer or ::Vector{Integer}`, `=4`) subsampling parameter for all axes if scaler, per axis if 3 element vector [ssx, ssy, ssz] - `us`: (`::Integer or ::Vector{Integer}`, `=1`) upsampling parameter for all axes if scaler, per axis if 3 element vector [usx, usy, usz] - `start_end`: (`::Vector{Integer}`, `=[160,200]`) z index range of presampled phantom, 180 is center +- `custom_values`: (`::Dict`, `=nothing`) phantom custom values in ms and Hz considering the available tissues # Returns - `obj`: (`::Phantom`) 3D Phantom struct @@ -346,10 +424,19 @@ julia> obj = brain_phantom3D(; ss=5) julia> obj = brain_phantom3D(; us=[2, 2, 1]) +julia> phantom_values = Dict( + #CSF, GM, WM, FAT1, MUSCLE, SKIN/MUSCLE, SKULL, VESSELS, FAT2, DURA, MARROW + "T1"=> [2569, 1153, 746, 0, 0, 0, 0, 0, 0, 0, 0], + "T2" => [329, 83, 70, 0, 0, 0, 0, 0, 0, 0, 0], + "T2s" => [58, 69, 61, 0, 0, 0, 0, 0, 0, 0, 0], + "ρ" => [1, 0.86, 0.77, 0, 0, 0, 0, 0, 0, 0, 0], + "Δw" => [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0]) +julia> obj = brain_phantom3D(; custom_values=phantom_values) + julia> plot_phantom_map(obj, :ρ) ``` """ -function brain_phantom3D(; ss=4, us=1, start_end=[160, 200]) +function brain_phantom3D(; ss=4, us=1, start_end=[160, 200], custom_values=nothing) # check and filter input ssx, ssy, ssz, usx, usy, usz = check_phantom_arguments(3, ss, us) @@ -378,59 +465,126 @@ function brain_phantom3D(; ss=4, us=1, start_end=[160, 200]) y = 0 * xx .+ 1 * yy .+ 0 * zz z = 0 * xx .+ 0 * yy .+ 1 * zz - # Define spin property vectors - T2 = - (class .== 23) * 329 .+ #CSF - (class .== 46) * 83 .+ #GM - (class .== 70) * 70 .+ #WM - (class .== 93) * 70 .+ #FAT1 - (class .== 116) * 47 .+ #MUSCLE - (class .== 139) * 329 .+ #SKIN/MUSCLE - (class .== 162) * 0 .+ #SKULL - (class .== 185) * 0 .+ #VESSELS - (class .== 209) * 70 .+ #FAT2 - (class .== 232) * 329 .+ #DURA - (class .== 255) * 70 #MARROW - T2s = - (class .== 23) * 58 .+ #CSF - (class .== 46) * 69 .+ #GM - (class .== 70) * 61 .+ #WM - (class .== 93) * 58 .+ #FAT1 - (class .== 116) * 30 .+ #MUSCLE - (class .== 139) * 58 .+ #SKIN/MUSCLE - (class .== 162) * 0 .+ #SKULL - (class .== 185) * 0 .+ #VESSELS - (class .== 209) * 61 .+ #FAT2 - (class .== 232) * 58 .+ #DURA - (class .== 255) * 61 #MARROW - T1 = - (class .== 23) * 2569 .+ #CSF - (class .== 46) * 833 .+ #GM - (class .== 70) * 500 .+ #WM - (class .== 93) * 350 .+ #FAT1 - (class .== 116) * 900 .+ #MUSCLE - (class .== 139) * 569 .+ #SKIN/MUSCLE - (class .== 162) * 0 .+ #SKULL - (class .== 185) * 0 .+ #VESSELS - (class .== 209) * 500 .+ #FAT2 - (class .== 232) * 2569 .+ #DURA - (class .== 255) * 500 #MARROW - ρ = - (class .== 23) * 1 .+ #CSF - (class .== 46) * 0.86 .+ #GM - (class .== 70) * 0.77 .+ #WM - (class .== 93) * 1 .+ #FAT1 - (class .== 116) * 1 .+ #MUSCLE - (class .== 139) * 1 .+ #SKIN/MUSCLE - (class .== 162) * 0 .+ #SKULL - (class .== 185) * 0 .+ #VESSELS - (class .== 209) * 0.77 .+ #FAT2 - (class .== 232) * 1 .+ #DURA - (class .== 255) * 0.77 #MARROW - Δw_fat = -220 * 2π - Δw = - (class .== 93) * Δw_fat .+ #FAT1 - (class .== 209) * Δw_fat #FAT2 + if !isnothing(custom_values) + # Define spin property vectors + T2 = + (class .== 23) * custom_values["T2"][1] .+ #CSF + (class .== 46) * custom_values["T2"][2] .+ #GM + (class .== 70) * custom_values["T2"][3] .+ #WM + (class .== 93) * custom_values["T2"][4] .+ #FAT1 + (class .== 116) * custom_values["T2"][5] .+ #MUSCLE + (class .== 139) * custom_values["T2"][6] .+ #SKIN/MUSCLE + (class .== 162) * custom_values["T2"][7] .+ #SKULL + (class .== 185) * custom_values["T2"][8] .+ #VESSELS + (class .== 209) * custom_values["T2"][9] .+ #FAT2 + (class .== 232) * custom_values["T2"][10] .+ #DURA + (class .== 255) * custom_values["T2"][11] #MARROW + T2s = + (class .== 23) * custom_values["T2s"][1] .+ #CSF + (class .== 46) * custom_values["T2s"][2] .+ #GM + (class .== 70) * custom_values["T2s"][3] .+ #WM + (class .== 93) * custom_values["T2s"][4] .+ #FAT1 + (class .== 116) * custom_values["T2s"][5] .+ #MUSCLE + (class .== 139) * custom_values["T2s"][6] .+ #SKIN/MUSCLE + (class .== 162) * custom_values["T2s"][7] .+ #SKULL + (class .== 185) * custom_values["T2s"][8] .+ #VESSELS + (class .== 209) * custom_values["T2s"][9] .+ #FAT2 + (class .== 232) * custom_values["T2s"][10] .+ #DURA + (class .== 255) * custom_values["T2s"][11] #MARROW + T1 = + (class .== 23) * custom_values["T1"][1] .+ #CSF + (class .== 46) * custom_values["T1"][2] .+ #GM + (class .== 70) * custom_values["T1"][3] .+ #WM + (class .== 93) * custom_values["T1"][4] .+ #FAT1 + (class .== 116) * custom_values["T1"][5] .+ #MUSCLE + (class .== 139) * custom_values["T1"][6] .+ #SKIN/MUSCLE + (class .== 162) * custom_values["T1"][7] .+ #SKULL + (class .== 185) * custom_values["T1"][8] .+ #VESSELS + (class .== 209) * custom_values["T1"][9] .+ #FAT2 + (class .== 232) * custom_values["T1"][10] .+ #DURA + (class .== 255) * custom_values["T1"][11] #MARROW + ρ = + (class .== 23) * custom_values["ρ"][1] .+ #CSF + (class .== 46) * custom_values["ρ"][2] .+ #GM + (class .== 70) * custom_values["ρ"][3] .+ #WM + (class .== 93) * custom_values["ρ"][4] .+ #FAT1 + (class .== 116) * custom_values["ρ"][5] .+ #MUSCLE + (class .== 139) * custom_values["ρ"][6] .+ #SKIN/MUSCLE + (class .== 162) * custom_values["ρ"][7] .+ #SKULL + (class .== 185) * custom_values["ρ"][8] .+ #VESSELS + (class .== 209) * custom_values["ρ"][9] .+ #FAT2 + (class .== 232) * custom_values["ρ"][10] .+ #DURA + (class .== 255) * custom_values["ρ"][11] #MARROW + + Δw = + (class .== 23) * custom_values["Δw"][1] .+ #CSF + (class .== 46) * custom_values["Δw"][2] .+ #GM + (class .== 70) * custom_values["Δw"][3] .+ #WM + (class .== 93) * custom_values["Δw"][4] .+ #FAT1 + (class .== 116) * custom_values["Δw"][5] .+ #MUSCLE + (class .== 139) * custom_values["Δw"][6] .+ #SKIN/MUSCLE + (class .== 162) * custom_values["Δw"][7] .+ #SKULL + (class .== 185) * custom_values["Δw"][8] .+ #VESSELS + (class .== 209) * custom_values["Δw"][9] .+ #FAT2 + (class .== 232) * custom_values["Δw"][10] .+ #DURA + (class .== 255) * custom_values["Δw"][11] #MARROW + Δw = -2π.*Δw + + else + # Define spin property vectors + T2 = + (class .== 23) * 329 .+ #CSF + (class .== 46) * 83 .+ #GM + (class .== 70) * 70 .+ #WM + (class .== 93) * 70 .+ #FAT1 + (class .== 116) * 47 .+ #MUSCLE + (class .== 139) * 329 .+ #SKIN/MUSCLE + (class .== 162) * 0 .+ #SKULL + (class .== 185) * 0 .+ #VESSELS + (class .== 209) * 70 .+ #FAT2 + (class .== 232) * 329 .+ #DURA + (class .== 255) * 70 #MARROW + T2s = + (class .== 23) * 58 .+ #CSF + (class .== 46) * 69 .+ #GM + (class .== 70) * 61 .+ #WM + (class .== 93) * 58 .+ #FAT1 + (class .== 116) * 30 .+ #MUSCLE + (class .== 139) * 58 .+ #SKIN/MUSCLE + (class .== 162) * 0 .+ #SKULL + (class .== 185) * 0 .+ #VESSELS + (class .== 209) * 61 .+ #FAT2 + (class .== 232) * 58 .+ #DURA + (class .== 255) * 61 #MARROW + T1 = + (class .== 23) * 2569 .+ #CSF + (class .== 46) * 833 .+ #GM + (class .== 70) * 500 .+ #WM + (class .== 93) * 350 .+ #FAT1 + (class .== 116) * 900 .+ #MUSCLE + (class .== 139) * 569 .+ #SKIN/MUSCLE + (class .== 162) * 0 .+ #SKULL + (class .== 185) * 0 .+ #VESSELS + (class .== 209) * 500 .+ #FAT2 + (class .== 232) * 2569 .+ #DURA + (class .== 255) * 500 #MARROW + ρ = + (class .== 23) * 1 .+ #CSF + (class .== 46) * 0.86 .+ #GM + (class .== 70) * 0.77 .+ #WM + (class .== 93) * 1 .+ #FAT1 + (class .== 116) * 1 .+ #MUSCLE + (class .== 139) * 1 .+ #SKIN/MUSCLE + (class .== 162) * 0 .+ #SKULL + (class .== 185) * 0 .+ #VESSELS + (class .== 209) * 0.77 .+ #FAT2 + (class .== 232) * 1 .+ #DURA + (class .== 255) * 0.77 #MARROW + Δw_fat = -220 * 2π + Δw = + (class .== 93) * Δw_fat .+ #FAT1 + (class .== 209) * Δw_fat #FAT2 + end T1 = T1 * 1e-3 T2 = T2 * 1e-3 T2s = T2s * 1e-3 From a67c5bdf1267e6672c8aa7e36aac578b07025440 Mon Sep 17 00:00:00 2001 From: Guillermo Sahonero Alvarez Date: Mon, 23 Sep 2024 10:44:08 -0300 Subject: [PATCH 02/14] Update Phantom.jl Changing from `custom_values` to `tissue_properties` --- KomaMRIBase/src/datatypes/Phantom.jl | 399 +++++++++++---------------- 1 file changed, 155 insertions(+), 244 deletions(-) diff --git a/KomaMRIBase/src/datatypes/Phantom.jl b/KomaMRIBase/src/datatypes/Phantom.jl index 11fb813b9..c4fceffcb 100644 --- a/KomaMRIBase/src/datatypes/Phantom.jl +++ b/KomaMRIBase/src/datatypes/Phantom.jl @@ -195,6 +195,7 @@ function heart_phantom(; return phantom end + """ phantom = brain_phantom2D(;axis="axial", ss=4) @@ -212,7 +213,7 @@ Default ss=4 sample spacing is 2 mm. Original file (ss=1) sample spacing is .5 m - `axis`: (`::String`, `="axial"`, opts=[`"axial"`, `"coronal"`, `"sagittal"`]) orientation of the phantom - `ss`: (`::Integer or ::Vector{Integer}`, `=4`) subsampling parameter for all axes if scaler, per axis if 2 element vector [ssx, ssy] - `us`: (`::Integer or ::Vector{Integer}`, `=1`) upsampling parameter for all axes if scaler, per axis if 2 element vector [usx, usy], if used ss is set to ss=1 -- `custom_values`: (`::Dict`, `=nothing`) phantom custom values in ms and Hz considering the available tissues +- `tissue_properties`: (`::Dict`, `=nothing`) phantom tissue properties in ms and Hz considering the available tissues # Returns - `obj`: (`::Phantom`) Phantom struct @@ -230,12 +231,12 @@ julia> phantom_values = Dict( "T2s" => [58, 69, 61, 0, 0, 0, 0, 0, 0, 0, 0], "ρ" => [1, 0.86, 0.77, 0, 0, 0, 0, 0, 0, 0, 0], "Δw" => [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0]) -julia> obj = brain_phantom2D(; custom_values=phantom_values) +julia> obj = brain_phantom2D(; tissue_properties=phantom_values) julia> plot_phantom_map(obj, :ρ) ``` """ -function brain_phantom2D(; axis="axial", ss=4, us=1, custom_values = nothing) +function brain_phantom2D(; axis="axial", ss=4, us=1, tissue_properties = nothing) # check and filter input # check more spins ssx, ssy, ssz, usx, usy, usz = check_phantom_arguments(2, ss, us) @@ -258,126 +259,81 @@ function brain_phantom2D(; axis="axial", ss=4, us=1, custom_values = nothing) x, y = x .+ y' * 0, x * 0 .+ y' #grid points - if !isnothing(custom_values) - # Define spin property vectors - T2 = - (class .== 23) * custom_values["T2"][1] .+ #CSF - (class .== 46) * custom_values["T2"][2] .+ #GM - (class .== 70) * custom_values["T2"][3] .+ #WM - (class .== 93) * custom_values["T2"][4] .+ #FAT1 - (class .== 116) * custom_values["T2"][5] .+ #MUSCLE - (class .== 139) * custom_values["T2"][6] .+ #SKIN/MUSCLE - (class .== 162) * custom_values["T2"][7] .+ #SKULL - (class .== 185) * custom_values["T2"][8] .+ #VESSELS - (class .== 209) * custom_values["T2"][9] .+ #FAT2 - (class .== 232) * custom_values["T2"][10] .+ #DURA - (class .== 255) * custom_values["T2"][11] #MARROW - T2s = - (class .== 23) * custom_values["T2s"][1] .+ #CSF - (class .== 46) * custom_values["T2s"][2] .+ #GM - (class .== 70) * custom_values["T2s"][3] .+ #WM - (class .== 93) * custom_values["T2s"][4] .+ #FAT1 - (class .== 116) * custom_values["T2s"][5] .+ #MUSCLE - (class .== 139) * custom_values["T2s"][6] .+ #SKIN/MUSCLE - (class .== 162) * custom_values["T2s"][7] .+ #SKULL - (class .== 185) * custom_values["T2s"][8] .+ #VESSELS - (class .== 209) * custom_values["T2s"][9] .+ #FAT2 - (class .== 232) * custom_values["T2s"][10] .+ #DURA - (class .== 255) * custom_values["T2s"][11] #MARROW - T1 = - (class .== 23) * custom_values["T1"][1] .+ #CSF - (class .== 46) * custom_values["T1"][2] .+ #GM - (class .== 70) * custom_values["T1"][3] .+ #WM - (class .== 93) * custom_values["T1"][4] .+ #FAT1 - (class .== 116) * custom_values["T1"][5] .+ #MUSCLE - (class .== 139) * custom_values["T1"][6] .+ #SKIN/MUSCLE - (class .== 162) * custom_values["T1"][7] .+ #SKULL - (class .== 185) * custom_values["T1"][8] .+ #VESSELS - (class .== 209) * custom_values["T1"][9] .+ #FAT2 - (class .== 232) * custom_values["T1"][10] .+ #DURA - (class .== 255) * custom_values["T1"][11] #MARROW - ρ = - (class .== 23) * custom_values["ρ"][1] .+ #CSF - (class .== 46) * custom_values["ρ"][2] .+ #GM - (class .== 70) * custom_values["ρ"][3] .+ #WM - (class .== 93) * custom_values["ρ"][4] .+ #FAT1 - (class .== 116) * custom_values["ρ"][5] .+ #MUSCLE - (class .== 139) * custom_values["ρ"][6] .+ #SKIN/MUSCLE - (class .== 162) * custom_values["ρ"][7] .+ #SKULL - (class .== 185) * custom_values["ρ"][8] .+ #VESSELS - (class .== 209) * custom_values["ρ"][9] .+ #FAT2 - (class .== 232) * custom_values["ρ"][10] .+ #DURA - (class .== 255) * custom_values["ρ"][11] #MARROW - - Δw = - (class .== 23) * custom_values["Δw"][1] .+ #CSF - (class .== 46) * custom_values["Δw"][2] .+ #GM - (class .== 70) * custom_values["Δw"][3] .+ #WM - (class .== 93) * custom_values["Δw"][4] .+ #FAT1 - (class .== 116) * custom_values["Δw"][5] .+ #MUSCLE - (class .== 139) * custom_values["Δw"][6] .+ #SKIN/MUSCLE - (class .== 162) * custom_values["Δw"][7] .+ #SKULL - (class .== 185) * custom_values["Δw"][8] .+ #VESSELS - (class .== 209) * custom_values["Δw"][9] .+ #FAT2 - (class .== 232) * custom_values["Δw"][10] .+ #DURA - (class .== 255) * custom_values["Δw"][11] #MARROW - Δw = -2π.*Δw - - else - # Define spin property vectors - T2 = - (class .== 23) * 329 .+ #CSF - (class .== 46) * 83 .+ #GM - (class .== 70) * 70 .+ #WM - (class .== 93) * 70 .+ #FAT1 - (class .== 116) * 47 .+ #MUSCLE - (class .== 139) * 329 .+ #SKIN/MUSCLE - (class .== 162) * 0 .+ #SKULL - (class .== 185) * 0 .+ #VESSELS - (class .== 209) * 70 .+ #FAT2 - (class .== 232) * 329 .+ #DURA - (class .== 255) * 70 #MARROW - T2s = - (class .== 23) * 58 .+ #CSF - (class .== 46) * 69 .+ #GM - (class .== 70) * 61 .+ #WM - (class .== 93) * 58 .+ #FAT1 - (class .== 116) * 30 .+ #MUSCLE - (class .== 139) * 58 .+ #SKIN/MUSCLE - (class .== 162) * 0 .+ #SKULL - (class .== 185) * 0 .+ #VESSELS - (class .== 209) * 61 .+ #FAT2 - (class .== 232) * 58 .+ #DURA - (class .== 255) * 61 #MARROW - T1 = - (class .== 23) * 2569 .+ #CSF - (class .== 46) * 833 .+ #GM - (class .== 70) * 500 .+ #WM - (class .== 93) * 350 .+ #FAT1 - (class .== 116) * 900 .+ #MUSCLE - (class .== 139) * 569 .+ #SKIN/MUSCLE - (class .== 162) * 0 .+ #SKULL - (class .== 185) * 0 .+ #VESSELS - (class .== 209) * 500 .+ #FAT2 - (class .== 232) * 2569 .+ #DURA - (class .== 255) * 500 #MARROW - ρ = - (class .== 23) * 1 .+ #CSF - (class .== 46) * 0.86 .+ #GM - (class .== 70) * 0.77 .+ #WM - (class .== 93) * 1 .+ #FAT1 - (class .== 116) * 1 .+ #MUSCLE - (class .== 139) * 1 .+ #SKIN/MUSCLE - (class .== 162) * 0 .+ #SKULL - (class .== 185) * 0 .+ #VESSELS - (class .== 209) * 0.77 .+ #FAT2 - (class .== 232) * 1 .+ #DURA - (class .== 255) * 0.77 #MARROW - Δw_fat = -220 * 2π - Δw = - (class .== 93) * Δw_fat .+ #FAT1 - (class .== 209) * Δw_fat #FAT2 + if isnothing(tissue_properties) + # Load default tissue properties + tissue_properties = Dict( + #CSF, GM, WM, FAT1, MUSCLE, SKIN/MUSCLE, SKULL, VESSELS, FAT2, DURA, MARROW + "T1"=> [2569, 833, 500, 350, 900, 569, 0, 0, 500, 2569, 500], + "T2" => [329, 83, 70, 70, 47, 329, 0, 0, 70, 329, 70], + "T2s" => [58, 69, 61, 58, 30, 58, 0, 0, 61, 58, 61], + "ρ" => [1, 0.86, 0.77, 1, 1, 1, 0, 0, 0.77, 1, 0.77], + "Δw" => [0, 0, 0, -220, 0, 0, 0, 0, -220, 0, 0] + ) end + # Define spin property vectors + T2 = + (class .== 23) * tissue_properties["T2"][1] .+ #CSF + (class .== 46) * tissue_properties["T2"][2] .+ #GM + (class .== 70) * tissue_properties["T2"][3] .+ #WM + (class .== 93) * tissue_properties["T2"][4] .+ #FAT1 + (class .== 116) * tissue_properties["T2"][5] .+ #MUSCLE + (class .== 139) * tissue_properties["T2"][6] .+ #SKIN/MUSCLE + (class .== 162) * tissue_properties["T2"][7] .+ #SKULL + (class .== 185) * tissue_properties["T2"][8] .+ #VESSELS + (class .== 209) * tissue_properties["T2"][9] .+ #FAT2 + (class .== 232) * tissue_properties["T2"][10] .+ #DURA + (class .== 255) * tissue_properties["T2"][11] #MARROW + T2s = + (class .== 23) * tissue_properties["T2s"][1] .+ #CSF + (class .== 46) * tissue_properties["T2s"][2] .+ #GM + (class .== 70) * tissue_properties["T2s"][3] .+ #WM + (class .== 93) * tissue_properties["T2s"][4] .+ #FAT1 + (class .== 116) * tissue_properties["T2s"][5] .+ #MUSCLE + (class .== 139) * tissue_properties["T2s"][6] .+ #SKIN/MUSCLE + (class .== 162) * tissue_properties["T2s"][7] .+ #SKULL + (class .== 185) * tissue_properties["T2s"][8] .+ #VESSELS + (class .== 209) * tissue_properties["T2s"][9] .+ #FAT2 + (class .== 232) * tissue_properties["T2s"][10] .+ #DURA + (class .== 255) * tissue_properties["T2s"][11] #MARROW + T1 = + (class .== 23) * tissue_properties["T1"][1] .+ #CSF + (class .== 46) * tissue_properties["T1"][2] .+ #GM + (class .== 70) * tissue_properties["T1"][3] .+ #WM + (class .== 93) * tissue_properties["T1"][4] .+ #FAT1 + (class .== 116) * tissue_properties["T1"][5] .+ #MUSCLE + (class .== 139) * tissue_properties["T1"][6] .+ #SKIN/MUSCLE + (class .== 162) * tissue_properties["T1"][7] .+ #SKULL + (class .== 185) * tissue_properties["T1"][8] .+ #VESSELS + (class .== 209) * tissue_properties["T1"][9] .+ #FAT2 + (class .== 232) * tissue_properties["T1"][10] .+ #DURA + (class .== 255) * tissue_properties["T1"][11] #MARROW + ρ = + (class .== 23) * tissue_properties["ρ"][1] .+ #CSF + (class .== 46) * tissue_properties["ρ"][2] .+ #GM + (class .== 70) * tissue_properties["ρ"][3] .+ #WM + (class .== 93) * tissue_properties["ρ"][4] .+ #FAT1 + (class .== 116) * tissue_properties["ρ"][5] .+ #MUSCLE + (class .== 139) * tissue_properties["ρ"][6] .+ #SKIN/MUSCLE + (class .== 162) * tissue_properties["ρ"][7] .+ #SKULL + (class .== 185) * tissue_properties["ρ"][8] .+ #VESSELS + (class .== 209) * tissue_properties["ρ"][9] .+ #FAT2 + (class .== 232) * tissue_properties["ρ"][10] .+ #DURA + (class .== 255) * tissue_properties["ρ"][11] #MARROW + + Δw = + (class .== 23) * tissue_properties["Δw"][1] .+ #CSF + (class .== 46) * tissue_properties["Δw"][2] .+ #GM + (class .== 70) * tissue_properties["Δw"][3] .+ #WM + (class .== 93) * tissue_properties["Δw"][4] .+ #FAT1 + (class .== 116) * tissue_properties["Δw"][5] .+ #MUSCLE + (class .== 139) * tissue_properties["Δw"][6] .+ #SKIN/MUSCLE + (class .== 162) * tissue_properties["Δw"][7] .+ #SKULL + (class .== 185) * tissue_properties["Δw"][8] .+ #VESSELS + (class .== 209) * tissue_properties["Δw"][9] .+ #FAT2 + (class .== 232) * tissue_properties["Δw"][10] .+ #DURA + (class .== 255) * tissue_properties["Δw"][11] #MARROW + Δw = -2π.*Δw + T1 = T1 * 1e-3 T2 = T2 * 1e-3 T2s = T2s * 1e-3 @@ -396,6 +352,7 @@ function brain_phantom2D(; axis="axial", ss=4, us=1, custom_values = nothing) ) return obj end + """ obj = brain_phantom3D(; ss=4, us=1, start_end=[160,200]) @@ -413,7 +370,7 @@ Default ss=4 sample spacing is 2 mm. Original file (ss=1) sample spacing is .5 m - `ss`: (`::Integer or ::Vector{Integer}`, `=4`) subsampling parameter for all axes if scaler, per axis if 3 element vector [ssx, ssy, ssz] - `us`: (`::Integer or ::Vector{Integer}`, `=1`) upsampling parameter for all axes if scaler, per axis if 3 element vector [usx, usy, usz] - `start_end`: (`::Vector{Integer}`, `=[160,200]`) z index range of presampled phantom, 180 is center -- `custom_values`: (`::Dict`, `=nothing`) phantom custom values in ms and Hz considering the available tissues +- `tissue_properties`: (`::Dict`, `=nothing`) phantom tissue properties in ms and Hz considering the available tissues # Returns - `obj`: (`::Phantom`) 3D Phantom struct @@ -431,12 +388,12 @@ julia> phantom_values = Dict( "T2s" => [58, 69, 61, 0, 0, 0, 0, 0, 0, 0, 0], "ρ" => [1, 0.86, 0.77, 0, 0, 0, 0, 0, 0, 0, 0], "Δw" => [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0]) -julia> obj = brain_phantom3D(; custom_values=phantom_values) +julia> obj = brain_phantom3D(; tissue_properties=phantom_values) julia> plot_phantom_map(obj, :ρ) ``` """ -function brain_phantom3D(; ss=4, us=1, start_end=[160, 200], custom_values=nothing) +function brain_phantom3D(; ss=4, us=1, start_end=[160, 200], tissue_properties=nothing) # check and filter input ssx, ssy, ssz, usx, usy, usz = check_phantom_arguments(3, ss, us) @@ -465,126 +422,80 @@ function brain_phantom3D(; ss=4, us=1, start_end=[160, 200], custom_values=nothi y = 0 * xx .+ 1 * yy .+ 0 * zz z = 0 * xx .+ 0 * yy .+ 1 * zz - if !isnothing(custom_values) - # Define spin property vectors - T2 = - (class .== 23) * custom_values["T2"][1] .+ #CSF - (class .== 46) * custom_values["T2"][2] .+ #GM - (class .== 70) * custom_values["T2"][3] .+ #WM - (class .== 93) * custom_values["T2"][4] .+ #FAT1 - (class .== 116) * custom_values["T2"][5] .+ #MUSCLE - (class .== 139) * custom_values["T2"][6] .+ #SKIN/MUSCLE - (class .== 162) * custom_values["T2"][7] .+ #SKULL - (class .== 185) * custom_values["T2"][8] .+ #VESSELS - (class .== 209) * custom_values["T2"][9] .+ #FAT2 - (class .== 232) * custom_values["T2"][10] .+ #DURA - (class .== 255) * custom_values["T2"][11] #MARROW - T2s = - (class .== 23) * custom_values["T2s"][1] .+ #CSF - (class .== 46) * custom_values["T2s"][2] .+ #GM - (class .== 70) * custom_values["T2s"][3] .+ #WM - (class .== 93) * custom_values["T2s"][4] .+ #FAT1 - (class .== 116) * custom_values["T2s"][5] .+ #MUSCLE - (class .== 139) * custom_values["T2s"][6] .+ #SKIN/MUSCLE - (class .== 162) * custom_values["T2s"][7] .+ #SKULL - (class .== 185) * custom_values["T2s"][8] .+ #VESSELS - (class .== 209) * custom_values["T2s"][9] .+ #FAT2 - (class .== 232) * custom_values["T2s"][10] .+ #DURA - (class .== 255) * custom_values["T2s"][11] #MARROW - T1 = - (class .== 23) * custom_values["T1"][1] .+ #CSF - (class .== 46) * custom_values["T1"][2] .+ #GM - (class .== 70) * custom_values["T1"][3] .+ #WM - (class .== 93) * custom_values["T1"][4] .+ #FAT1 - (class .== 116) * custom_values["T1"][5] .+ #MUSCLE - (class .== 139) * custom_values["T1"][6] .+ #SKIN/MUSCLE - (class .== 162) * custom_values["T1"][7] .+ #SKULL - (class .== 185) * custom_values["T1"][8] .+ #VESSELS - (class .== 209) * custom_values["T1"][9] .+ #FAT2 - (class .== 232) * custom_values["T1"][10] .+ #DURA - (class .== 255) * custom_values["T1"][11] #MARROW - ρ = - (class .== 23) * custom_values["ρ"][1] .+ #CSF - (class .== 46) * custom_values["ρ"][2] .+ #GM - (class .== 70) * custom_values["ρ"][3] .+ #WM - (class .== 93) * custom_values["ρ"][4] .+ #FAT1 - (class .== 116) * custom_values["ρ"][5] .+ #MUSCLE - (class .== 139) * custom_values["ρ"][6] .+ #SKIN/MUSCLE - (class .== 162) * custom_values["ρ"][7] .+ #SKULL - (class .== 185) * custom_values["ρ"][8] .+ #VESSELS - (class .== 209) * custom_values["ρ"][9] .+ #FAT2 - (class .== 232) * custom_values["ρ"][10] .+ #DURA - (class .== 255) * custom_values["ρ"][11] #MARROW - - Δw = - (class .== 23) * custom_values["Δw"][1] .+ #CSF - (class .== 46) * custom_values["Δw"][2] .+ #GM - (class .== 70) * custom_values["Δw"][3] .+ #WM - (class .== 93) * custom_values["Δw"][4] .+ #FAT1 - (class .== 116) * custom_values["Δw"][5] .+ #MUSCLE - (class .== 139) * custom_values["Δw"][6] .+ #SKIN/MUSCLE - (class .== 162) * custom_values["Δw"][7] .+ #SKULL - (class .== 185) * custom_values["Δw"][8] .+ #VESSELS - (class .== 209) * custom_values["Δw"][9] .+ #FAT2 - (class .== 232) * custom_values["Δw"][10] .+ #DURA - (class .== 255) * custom_values["Δw"][11] #MARROW - Δw = -2π.*Δw - - else - # Define spin property vectors - T2 = - (class .== 23) * 329 .+ #CSF - (class .== 46) * 83 .+ #GM - (class .== 70) * 70 .+ #WM - (class .== 93) * 70 .+ #FAT1 - (class .== 116) * 47 .+ #MUSCLE - (class .== 139) * 329 .+ #SKIN/MUSCLE - (class .== 162) * 0 .+ #SKULL - (class .== 185) * 0 .+ #VESSELS - (class .== 209) * 70 .+ #FAT2 - (class .== 232) * 329 .+ #DURA - (class .== 255) * 70 #MARROW - T2s = - (class .== 23) * 58 .+ #CSF - (class .== 46) * 69 .+ #GM - (class .== 70) * 61 .+ #WM - (class .== 93) * 58 .+ #FAT1 - (class .== 116) * 30 .+ #MUSCLE - (class .== 139) * 58 .+ #SKIN/MUSCLE - (class .== 162) * 0 .+ #SKULL - (class .== 185) * 0 .+ #VESSELS - (class .== 209) * 61 .+ #FAT2 - (class .== 232) * 58 .+ #DURA - (class .== 255) * 61 #MARROW - T1 = - (class .== 23) * 2569 .+ #CSF - (class .== 46) * 833 .+ #GM - (class .== 70) * 500 .+ #WM - (class .== 93) * 350 .+ #FAT1 - (class .== 116) * 900 .+ #MUSCLE - (class .== 139) * 569 .+ #SKIN/MUSCLE - (class .== 162) * 0 .+ #SKULL - (class .== 185) * 0 .+ #VESSELS - (class .== 209) * 500 .+ #FAT2 - (class .== 232) * 2569 .+ #DURA - (class .== 255) * 500 #MARROW - ρ = - (class .== 23) * 1 .+ #CSF - (class .== 46) * 0.86 .+ #GM - (class .== 70) * 0.77 .+ #WM - (class .== 93) * 1 .+ #FAT1 - (class .== 116) * 1 .+ #MUSCLE - (class .== 139) * 1 .+ #SKIN/MUSCLE - (class .== 162) * 0 .+ #SKULL - (class .== 185) * 0 .+ #VESSELS - (class .== 209) * 0.77 .+ #FAT2 - (class .== 232) * 1 .+ #DURA - (class .== 255) * 0.77 #MARROW - Δw_fat = -220 * 2π - Δw = - (class .== 93) * Δw_fat .+ #FAT1 - (class .== 209) * Δw_fat #FAT2 + if isnothing(tissue_properties) + # Load default tissue properties + tissue_properties = Dict( + #CSF, GM, WM, FAT1, MUSCLE, SKIN/MUSCLE, SKULL, VESSELS, FAT2, DURA, MARROW + "T1"=> [2569, 833, 500, 350, 900, 569, 0, 0, 500, 2569, 500], + "T2" => [329, 83, 70, 70, 47, 329, 0, 0, 70, 329, 70], + "T2s" => [58, 69, 61, 58, 30, 58, 0, 0, 61, 58, 61], + "ρ" => [1, 0.86, 0.77, 1, 1, 1, 0, 0, 0.77, 1, 0.77], + "Δw" => [0, 0, 0, -220, 0, 0, 0, 0, -220, 0, 0] + ) end + # Define spin property vectors + T2 = + (class .== 23) * tissue_properties["T2"][1] .+ #CSF + (class .== 46) * tissue_properties["T2"][2] .+ #GM + (class .== 70) * tissue_properties["T2"][3] .+ #WM + (class .== 93) * tissue_properties["T2"][4] .+ #FAT1 + (class .== 116) * tissue_properties["T2"][5] .+ #MUSCLE + (class .== 139) * tissue_properties["T2"][6] .+ #SKIN/MUSCLE + (class .== 162) * tissue_properties["T2"][7] .+ #SKULL + (class .== 185) * tissue_properties["T2"][8] .+ #VESSELS + (class .== 209) * tissue_properties["T2"][9] .+ #FAT2 + (class .== 232) * tissue_properties["T2"][10] .+ #DURA + (class .== 255) * tissue_properties["T2"][11] #MARROW + T2s = + (class .== 23) * tissue_properties["T2s"][1] .+ #CSF + (class .== 46) * tissue_properties["T2s"][2] .+ #GM + (class .== 70) * tissue_properties["T2s"][3] .+ #WM + (class .== 93) * tissue_properties["T2s"][4] .+ #FAT1 + (class .== 116) * tissue_properties["T2s"][5] .+ #MUSCLE + (class .== 139) * tissue_properties["T2s"][6] .+ #SKIN/MUSCLE + (class .== 162) * tissue_properties["T2s"][7] .+ #SKULL + (class .== 185) * tissue_properties["T2s"][8] .+ #VESSELS + (class .== 209) * tissue_properties["T2s"][9] .+ #FAT2 + (class .== 232) * tissue_properties["T2s"][10] .+ #DURA + (class .== 255) * tissue_properties["T2s"][11] #MARROW + T1 = + (class .== 23) * tissue_properties["T1"][1] .+ #CSF + (class .== 46) * tissue_properties["T1"][2] .+ #GM + (class .== 70) * tissue_properties["T1"][3] .+ #WM + (class .== 93) * tissue_properties["T1"][4] .+ #FAT1 + (class .== 116) * tissue_properties["T1"][5] .+ #MUSCLE + (class .== 139) * tissue_properties["T1"][6] .+ #SKIN/MUSCLE + (class .== 162) * tissue_properties["T1"][7] .+ #SKULL + (class .== 185) * tissue_properties["T1"][8] .+ #VESSELS + (class .== 209) * tissue_properties["T1"][9] .+ #FAT2 + (class .== 232) * tissue_properties["T1"][10] .+ #DURA + (class .== 255) * tissue_properties["T1"][11] #MARROW + ρ = + (class .== 23) * tissue_properties["ρ"][1] .+ #CSF + (class .== 46) * tissue_properties["ρ"][2] .+ #GM + (class .== 70) * tissue_properties["ρ"][3] .+ #WM + (class .== 93) * tissue_properties["ρ"][4] .+ #FAT1 + (class .== 116) * tissue_properties["ρ"][5] .+ #MUSCLE + (class .== 139) * tissue_properties["ρ"][6] .+ #SKIN/MUSCLE + (class .== 162) * tissue_properties["ρ"][7] .+ #SKULL + (class .== 185) * tissue_properties["ρ"][8] .+ #VESSELS + (class .== 209) * tissue_properties["ρ"][9] .+ #FAT2 + (class .== 232) * tissue_properties["ρ"][10] .+ #DURA + (class .== 255) * tissue_properties["ρ"][11] #MARROW + + Δw = + (class .== 23) * tissue_properties["Δw"][1] .+ #CSF + (class .== 46) * tissue_properties["Δw"][2] .+ #GM + (class .== 70) * tissue_properties["Δw"][3] .+ #WM + (class .== 93) * tissue_properties["Δw"][4] .+ #FAT1 + (class .== 116) * tissue_properties["Δw"][5] .+ #MUSCLE + (class .== 139) * tissue_properties["Δw"][6] .+ #SKIN/MUSCLE + (class .== 162) * tissue_properties["Δw"][7] .+ #SKULL + (class .== 185) * tissue_properties["Δw"][8] .+ #VESSELS + (class .== 209) * tissue_properties["Δw"][9] .+ #FAT2 + (class .== 232) * tissue_properties["Δw"][10] .+ #DURA + (class .== 255) * tissue_properties["Δw"][11] #MARROW + Δw = -2π.*Δw T1 = T1 * 1e-3 T2 = T2 * 1e-3 T2s = T2s * 1e-3 From afbd05b3460a102965ce8a0287de8d9f98144251 Mon Sep 17 00:00:00 2001 From: Guillermo Date: Thu, 26 Sep 2024 18:52:32 -0300 Subject: [PATCH 03/14] Introducing default brain phantom properties Including magnetization states for storing purposes in phantom --- KomaMRIBase/src/datatypes/Phantom.jl | 372 +++++++++------------------ 1 file changed, 122 insertions(+), 250 deletions(-) diff --git a/KomaMRIBase/src/datatypes/Phantom.jl b/KomaMRIBase/src/datatypes/Phantom.jl index 11fb813b9..8c008f672 100644 --- a/KomaMRIBase/src/datatypes/Phantom.jl +++ b/KomaMRIBase/src/datatypes/Phantom.jl @@ -18,6 +18,8 @@ a property value representing a spin. This struct serves as an input for the sim - `Dλ2`: (`::AbstractVector{T<:Real}`) spin Dλ2 (diffusion) parameter vector - `Dθ`: (`::AbstractVector{T<:Real}`) spin Dθ (diffusion) parameter vector - `motion`: (`::AbstractMotion{T<:Real}`) motion +- `Mz`: (`::AbstractVector{ComplexF32}`) transverse magnetization parameter vector, only for storage purposes +- `Mxy`: (`::AbstractVector{ComplexF32}`) longitudinal magnetization parameter vector, only for storage purposes # Returns - `obj`: (`::Phantom`) Phantom struct @@ -29,7 +31,7 @@ julia> obj = Phantom(x=[0.0]) julia> obj.ρ ``` """ -@with_kw mutable struct Phantom{T<:Real} +@with_kw mutable struct Phantom{T<:Number} name::String = "spins" x :: AbstractVector{T} y::AbstractVector{T} = zeros(eltype(x), size(x)) @@ -48,6 +50,8 @@ julia> obj.ρ #Diff::Vector{DiffusionModel} #Diffusion map #Motion motion::AbstractMotion{T} = NoMotion{eltype(x)}() + Mxy::AbstractVector{ComplexF32} = zeros(eltype(x), size(x)) .+ 0im + Mz::AbstractVector{ComplexF32} = zeros(eltype(x), size(x)) .+ 0im end const NON_STRING_PHANTOM_FIELDS = Iterators.filter(x -> fieldtype(Phantom, x) != String, fieldnames(Phantom)) @@ -212,7 +216,7 @@ Default ss=4 sample spacing is 2 mm. Original file (ss=1) sample spacing is .5 m - `axis`: (`::String`, `="axial"`, opts=[`"axial"`, `"coronal"`, `"sagittal"`]) orientation of the phantom - `ss`: (`::Integer or ::Vector{Integer}`, `=4`) subsampling parameter for all axes if scaler, per axis if 2 element vector [ssx, ssy] - `us`: (`::Integer or ::Vector{Integer}`, `=1`) upsampling parameter for all axes if scaler, per axis if 2 element vector [usx, usy], if used ss is set to ss=1 -- `custom_values`: (`::Dict`, `=nothing`) phantom custom values in ms and Hz considering the available tissues +- `tissue_properties`: (`::Dict`, `=nothing`) phantom tissue properties in ms and Hz considering the available tissues # Returns - `obj`: (`::Phantom`) Phantom struct @@ -230,12 +234,12 @@ julia> phantom_values = Dict( "T2s" => [58, 69, 61, 0, 0, 0, 0, 0, 0, 0, 0], "ρ" => [1, 0.86, 0.77, 0, 0, 0, 0, 0, 0, 0, 0], "Δw" => [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0]) -julia> obj = brain_phantom2D(; custom_values=phantom_values) +julia> obj = brain_phantom2D(; tissue_properties=phantom_values) julia> plot_phantom_map(obj, :ρ) ``` """ -function brain_phantom2D(; axis="axial", ss=4, us=1, custom_values = nothing) +function brain_phantom2D(; axis="axial", ss=4, us=1, tissue_properties = nothing) # check and filter input # check more spins ssx, ssy, ssz, usx, usy, usz = check_phantom_arguments(2, ss, us) @@ -257,127 +261,9 @@ function brain_phantom2D(; axis="axial", ss=4, us=1, custom_values = nothing) y = (-FOVy / 2):Δy:(FOVy / 2) #spin coordinates x, y = x .+ y' * 0, x * 0 .+ y' #grid points - - if !isnothing(custom_values) - # Define spin property vectors - T2 = - (class .== 23) * custom_values["T2"][1] .+ #CSF - (class .== 46) * custom_values["T2"][2] .+ #GM - (class .== 70) * custom_values["T2"][3] .+ #WM - (class .== 93) * custom_values["T2"][4] .+ #FAT1 - (class .== 116) * custom_values["T2"][5] .+ #MUSCLE - (class .== 139) * custom_values["T2"][6] .+ #SKIN/MUSCLE - (class .== 162) * custom_values["T2"][7] .+ #SKULL - (class .== 185) * custom_values["T2"][8] .+ #VESSELS - (class .== 209) * custom_values["T2"][9] .+ #FAT2 - (class .== 232) * custom_values["T2"][10] .+ #DURA - (class .== 255) * custom_values["T2"][11] #MARROW - T2s = - (class .== 23) * custom_values["T2s"][1] .+ #CSF - (class .== 46) * custom_values["T2s"][2] .+ #GM - (class .== 70) * custom_values["T2s"][3] .+ #WM - (class .== 93) * custom_values["T2s"][4] .+ #FAT1 - (class .== 116) * custom_values["T2s"][5] .+ #MUSCLE - (class .== 139) * custom_values["T2s"][6] .+ #SKIN/MUSCLE - (class .== 162) * custom_values["T2s"][7] .+ #SKULL - (class .== 185) * custom_values["T2s"][8] .+ #VESSELS - (class .== 209) * custom_values["T2s"][9] .+ #FAT2 - (class .== 232) * custom_values["T2s"][10] .+ #DURA - (class .== 255) * custom_values["T2s"][11] #MARROW - T1 = - (class .== 23) * custom_values["T1"][1] .+ #CSF - (class .== 46) * custom_values["T1"][2] .+ #GM - (class .== 70) * custom_values["T1"][3] .+ #WM - (class .== 93) * custom_values["T1"][4] .+ #FAT1 - (class .== 116) * custom_values["T1"][5] .+ #MUSCLE - (class .== 139) * custom_values["T1"][6] .+ #SKIN/MUSCLE - (class .== 162) * custom_values["T1"][7] .+ #SKULL - (class .== 185) * custom_values["T1"][8] .+ #VESSELS - (class .== 209) * custom_values["T1"][9] .+ #FAT2 - (class .== 232) * custom_values["T1"][10] .+ #DURA - (class .== 255) * custom_values["T1"][11] #MARROW - ρ = - (class .== 23) * custom_values["ρ"][1] .+ #CSF - (class .== 46) * custom_values["ρ"][2] .+ #GM - (class .== 70) * custom_values["ρ"][3] .+ #WM - (class .== 93) * custom_values["ρ"][4] .+ #FAT1 - (class .== 116) * custom_values["ρ"][5] .+ #MUSCLE - (class .== 139) * custom_values["ρ"][6] .+ #SKIN/MUSCLE - (class .== 162) * custom_values["ρ"][7] .+ #SKULL - (class .== 185) * custom_values["ρ"][8] .+ #VESSELS - (class .== 209) * custom_values["ρ"][9] .+ #FAT2 - (class .== 232) * custom_values["ρ"][10] .+ #DURA - (class .== 255) * custom_values["ρ"][11] #MARROW - - Δw = - (class .== 23) * custom_values["Δw"][1] .+ #CSF - (class .== 46) * custom_values["Δw"][2] .+ #GM - (class .== 70) * custom_values["Δw"][3] .+ #WM - (class .== 93) * custom_values["Δw"][4] .+ #FAT1 - (class .== 116) * custom_values["Δw"][5] .+ #MUSCLE - (class .== 139) * custom_values["Δw"][6] .+ #SKIN/MUSCLE - (class .== 162) * custom_values["Δw"][7] .+ #SKULL - (class .== 185) * custom_values["Δw"][8] .+ #VESSELS - (class .== 209) * custom_values["Δw"][9] .+ #FAT2 - (class .== 232) * custom_values["Δw"][10] .+ #DURA - (class .== 255) * custom_values["Δw"][11] #MARROW - Δw = -2π.*Δw - - else - # Define spin property vectors - T2 = - (class .== 23) * 329 .+ #CSF - (class .== 46) * 83 .+ #GM - (class .== 70) * 70 .+ #WM - (class .== 93) * 70 .+ #FAT1 - (class .== 116) * 47 .+ #MUSCLE - (class .== 139) * 329 .+ #SKIN/MUSCLE - (class .== 162) * 0 .+ #SKULL - (class .== 185) * 0 .+ #VESSELS - (class .== 209) * 70 .+ #FAT2 - (class .== 232) * 329 .+ #DURA - (class .== 255) * 70 #MARROW - T2s = - (class .== 23) * 58 .+ #CSF - (class .== 46) * 69 .+ #GM - (class .== 70) * 61 .+ #WM - (class .== 93) * 58 .+ #FAT1 - (class .== 116) * 30 .+ #MUSCLE - (class .== 139) * 58 .+ #SKIN/MUSCLE - (class .== 162) * 0 .+ #SKULL - (class .== 185) * 0 .+ #VESSELS - (class .== 209) * 61 .+ #FAT2 - (class .== 232) * 58 .+ #DURA - (class .== 255) * 61 #MARROW - T1 = - (class .== 23) * 2569 .+ #CSF - (class .== 46) * 833 .+ #GM - (class .== 70) * 500 .+ #WM - (class .== 93) * 350 .+ #FAT1 - (class .== 116) * 900 .+ #MUSCLE - (class .== 139) * 569 .+ #SKIN/MUSCLE - (class .== 162) * 0 .+ #SKULL - (class .== 185) * 0 .+ #VESSELS - (class .== 209) * 500 .+ #FAT2 - (class .== 232) * 2569 .+ #DURA - (class .== 255) * 500 #MARROW - ρ = - (class .== 23) * 1 .+ #CSF - (class .== 46) * 0.86 .+ #GM - (class .== 70) * 0.77 .+ #WM - (class .== 93) * 1 .+ #FAT1 - (class .== 116) * 1 .+ #MUSCLE - (class .== 139) * 1 .+ #SKIN/MUSCLE - (class .== 162) * 0 .+ #SKULL - (class .== 185) * 0 .+ #VESSELS - (class .== 209) * 0.77 .+ #FAT2 - (class .== 232) * 1 .+ #DURA - (class .== 255) * 0.77 #MARROW - Δw_fat = -220 * 2π - Δw = - (class .== 93) * Δw_fat .+ #FAT1 - (class .== 209) * Δw_fat #FAT2 - end + # Get tissue properties + T1, T2, T2s, ρ, Δw = default_brain_tissue_properties(class, tissue_properties) + Δw = -2π.*Δw T1 = T1 * 1e-3 T2 = T2 * 1e-3 T2s = T2s * 1e-3 @@ -396,6 +282,7 @@ function brain_phantom2D(; axis="axial", ss=4, us=1, custom_values = nothing) ) return obj end + """ obj = brain_phantom3D(; ss=4, us=1, start_end=[160,200]) @@ -413,7 +300,7 @@ Default ss=4 sample spacing is 2 mm. Original file (ss=1) sample spacing is .5 m - `ss`: (`::Integer or ::Vector{Integer}`, `=4`) subsampling parameter for all axes if scaler, per axis if 3 element vector [ssx, ssy, ssz] - `us`: (`::Integer or ::Vector{Integer}`, `=1`) upsampling parameter for all axes if scaler, per axis if 3 element vector [usx, usy, usz] - `start_end`: (`::Vector{Integer}`, `=[160,200]`) z index range of presampled phantom, 180 is center -- `custom_values`: (`::Dict`, `=nothing`) phantom custom values in ms and Hz considering the available tissues +- `tissue_properties`: (`::Dict`, `=nothing`) phantom tissue properties in ms and Hz considering the available tissues # Returns - `obj`: (`::Phantom`) 3D Phantom struct @@ -431,15 +318,15 @@ julia> phantom_values = Dict( "T2s" => [58, 69, 61, 0, 0, 0, 0, 0, 0, 0, 0], "ρ" => [1, 0.86, 0.77, 0, 0, 0, 0, 0, 0, 0, 0], "Δw" => [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0]) -julia> obj = brain_phantom3D(; custom_values=phantom_values) +julia> obj = brain_phantom3D(; tissue_properties=phantom_values) julia> plot_phantom_map(obj, :ρ) ``` """ -function brain_phantom3D(; ss=4, us=1, start_end=[160, 200], custom_values=nothing) +function brain_phantom3D(; ss=4, us=1, start_end=[160, 200], tissue_properties=nothing) # check and filter input ssx, ssy, ssz, usx, usy, usz = check_phantom_arguments(3, ss, us) - + # Get data from .mat file path = @__DIR__ data = MAT.matread(path * "/phantom/brain3D.mat") @@ -465,126 +352,9 @@ function brain_phantom3D(; ss=4, us=1, start_end=[160, 200], custom_values=nothi y = 0 * xx .+ 1 * yy .+ 0 * zz z = 0 * xx .+ 0 * yy .+ 1 * zz - if !isnothing(custom_values) - # Define spin property vectors - T2 = - (class .== 23) * custom_values["T2"][1] .+ #CSF - (class .== 46) * custom_values["T2"][2] .+ #GM - (class .== 70) * custom_values["T2"][3] .+ #WM - (class .== 93) * custom_values["T2"][4] .+ #FAT1 - (class .== 116) * custom_values["T2"][5] .+ #MUSCLE - (class .== 139) * custom_values["T2"][6] .+ #SKIN/MUSCLE - (class .== 162) * custom_values["T2"][7] .+ #SKULL - (class .== 185) * custom_values["T2"][8] .+ #VESSELS - (class .== 209) * custom_values["T2"][9] .+ #FAT2 - (class .== 232) * custom_values["T2"][10] .+ #DURA - (class .== 255) * custom_values["T2"][11] #MARROW - T2s = - (class .== 23) * custom_values["T2s"][1] .+ #CSF - (class .== 46) * custom_values["T2s"][2] .+ #GM - (class .== 70) * custom_values["T2s"][3] .+ #WM - (class .== 93) * custom_values["T2s"][4] .+ #FAT1 - (class .== 116) * custom_values["T2s"][5] .+ #MUSCLE - (class .== 139) * custom_values["T2s"][6] .+ #SKIN/MUSCLE - (class .== 162) * custom_values["T2s"][7] .+ #SKULL - (class .== 185) * custom_values["T2s"][8] .+ #VESSELS - (class .== 209) * custom_values["T2s"][9] .+ #FAT2 - (class .== 232) * custom_values["T2s"][10] .+ #DURA - (class .== 255) * custom_values["T2s"][11] #MARROW - T1 = - (class .== 23) * custom_values["T1"][1] .+ #CSF - (class .== 46) * custom_values["T1"][2] .+ #GM - (class .== 70) * custom_values["T1"][3] .+ #WM - (class .== 93) * custom_values["T1"][4] .+ #FAT1 - (class .== 116) * custom_values["T1"][5] .+ #MUSCLE - (class .== 139) * custom_values["T1"][6] .+ #SKIN/MUSCLE - (class .== 162) * custom_values["T1"][7] .+ #SKULL - (class .== 185) * custom_values["T1"][8] .+ #VESSELS - (class .== 209) * custom_values["T1"][9] .+ #FAT2 - (class .== 232) * custom_values["T1"][10] .+ #DURA - (class .== 255) * custom_values["T1"][11] #MARROW - ρ = - (class .== 23) * custom_values["ρ"][1] .+ #CSF - (class .== 46) * custom_values["ρ"][2] .+ #GM - (class .== 70) * custom_values["ρ"][3] .+ #WM - (class .== 93) * custom_values["ρ"][4] .+ #FAT1 - (class .== 116) * custom_values["ρ"][5] .+ #MUSCLE - (class .== 139) * custom_values["ρ"][6] .+ #SKIN/MUSCLE - (class .== 162) * custom_values["ρ"][7] .+ #SKULL - (class .== 185) * custom_values["ρ"][8] .+ #VESSELS - (class .== 209) * custom_values["ρ"][9] .+ #FAT2 - (class .== 232) * custom_values["ρ"][10] .+ #DURA - (class .== 255) * custom_values["ρ"][11] #MARROW - - Δw = - (class .== 23) * custom_values["Δw"][1] .+ #CSF - (class .== 46) * custom_values["Δw"][2] .+ #GM - (class .== 70) * custom_values["Δw"][3] .+ #WM - (class .== 93) * custom_values["Δw"][4] .+ #FAT1 - (class .== 116) * custom_values["Δw"][5] .+ #MUSCLE - (class .== 139) * custom_values["Δw"][6] .+ #SKIN/MUSCLE - (class .== 162) * custom_values["Δw"][7] .+ #SKULL - (class .== 185) * custom_values["Δw"][8] .+ #VESSELS - (class .== 209) * custom_values["Δw"][9] .+ #FAT2 - (class .== 232) * custom_values["Δw"][10] .+ #DURA - (class .== 255) * custom_values["Δw"][11] #MARROW - Δw = -2π.*Δw - - else - # Define spin property vectors - T2 = - (class .== 23) * 329 .+ #CSF - (class .== 46) * 83 .+ #GM - (class .== 70) * 70 .+ #WM - (class .== 93) * 70 .+ #FAT1 - (class .== 116) * 47 .+ #MUSCLE - (class .== 139) * 329 .+ #SKIN/MUSCLE - (class .== 162) * 0 .+ #SKULL - (class .== 185) * 0 .+ #VESSELS - (class .== 209) * 70 .+ #FAT2 - (class .== 232) * 329 .+ #DURA - (class .== 255) * 70 #MARROW - T2s = - (class .== 23) * 58 .+ #CSF - (class .== 46) * 69 .+ #GM - (class .== 70) * 61 .+ #WM - (class .== 93) * 58 .+ #FAT1 - (class .== 116) * 30 .+ #MUSCLE - (class .== 139) * 58 .+ #SKIN/MUSCLE - (class .== 162) * 0 .+ #SKULL - (class .== 185) * 0 .+ #VESSELS - (class .== 209) * 61 .+ #FAT2 - (class .== 232) * 58 .+ #DURA - (class .== 255) * 61 #MARROW - T1 = - (class .== 23) * 2569 .+ #CSF - (class .== 46) * 833 .+ #GM - (class .== 70) * 500 .+ #WM - (class .== 93) * 350 .+ #FAT1 - (class .== 116) * 900 .+ #MUSCLE - (class .== 139) * 569 .+ #SKIN/MUSCLE - (class .== 162) * 0 .+ #SKULL - (class .== 185) * 0 .+ #VESSELS - (class .== 209) * 500 .+ #FAT2 - (class .== 232) * 2569 .+ #DURA - (class .== 255) * 500 #MARROW - ρ = - (class .== 23) * 1 .+ #CSF - (class .== 46) * 0.86 .+ #GM - (class .== 70) * 0.77 .+ #WM - (class .== 93) * 1 .+ #FAT1 - (class .== 116) * 1 .+ #MUSCLE - (class .== 139) * 1 .+ #SKIN/MUSCLE - (class .== 162) * 0 .+ #SKULL - (class .== 185) * 0 .+ #VESSELS - (class .== 209) * 0.77 .+ #FAT2 - (class .== 232) * 1 .+ #DURA - (class .== 255) * 0.77 #MARROW - Δw_fat = -220 * 2π - Δw = - (class .== 93) * Δw_fat .+ #FAT1 - (class .== 209) * Δw_fat #FAT2 - end + # Get tissue properties + T1, T2, T2s, ρ, Δw = default_brain_tissue_properties(class, tissue_properties) + Δw = -2π.*Δw T1 = T1 * 1e-3 T2 = T2 * 1e-3 T2s = T2s * 1e-3 @@ -636,7 +406,7 @@ function pelvis_phantom2D(; ss=4, us=1) # subsample or upsample the phantom data class = repeat(data["pelvis3D_slice"][1:ssx:end, 1:ssy:end]; inner=[usx, usy]) - + # Define spin position vectors Δx = .5e-3 * ssx / usx Δy = .5e-3 * ssy / usy @@ -766,5 +536,107 @@ function check_phantom_arguments(nd, ss, us) ssy = ss[2] end end + return ssx, ssy, ssz, usx, usy, usz end + +""" + T1, T2, T2s, ρ, Δw = default_brain_tissue_properties(class, tissue_properties = nothing) + +This function returns the default brain tissue properties using a class identifier Matrix +# Arguments +- `class` : (`::Matrix`) the class identifier matrix of the phantom +- `tissue_properties` : (`::Dict`, `=nothing`) phantom tissue properties in ms and Hz considering the available tissues + +# Returns +- `T1, T2, T2s, ρ, Δw`: (`::Matrix`) matrices of the same size of class with the tissues properties information + +# Examples +```julia-repl +julia> T1, T2, T2s, ρ, Δw = default_brain_tissue_properties(class, tissue_properties) + +julia> T1, T2, T2s, ρ, Δw = default_brain_tissue_properties(class) +``` +""" +function default_brain_tissue_properties(class, tissue_properties = nothing) + if isnothing(tissue_properties) + # Load default tissue properties + tissue_properties = Dict( + # T1, T2, T2*, ρ, Δw + "CSF" => [2569, 329, 58, 1, 0], + "GM" => [833, 83, 69, 0.86, 0], + "WM" => [500, 70, 61, 0.77, 0], + "FAT1" => [350, 70, 58, 1, -220], + "MUSCLE" => [900, 47, 30, 1, 0], + "SKIN/MUSCLE" => [569, 329, 58, 1, 0], + "SKULL" => [0, 0, 0, 0, 0], + "VESSELS" => [0, 0, 0, 0, 0], + "FAT2" => [500, 70, 61, 0.77, -220], + "DURA" => [2569, 329, 58, 1, 0], + "MARROW" => [500, 70, 61, 0.77, 0]) + # Define spin property vectors + end + T1 = + (class .== 23) * tissue_properties["CSF"][1] .+ #CSF + (class .== 46) * tissue_properties["GM"][1] .+ #GM + (class .== 70) * tissue_properties["WM"][1] .+ #WM + (class .== 93) * tissue_properties["FAT1"][1] .+ #FAT1 + (class .== 116) * tissue_properties["MUSCLE"][1] .+ #MUSCLE + (class .== 139) * tissue_properties["SKIN/MUSCLE"][1] .+ #SKIN/MUSCLE + (class .== 162) * tissue_properties["SKULL"][1] .+ #SKULL + (class .== 185) * tissue_properties["VESSELS"][1] .+ #VESSELS + (class .== 209) * tissue_properties["FAT2"][1] .+ #FAT2 + (class .== 232) * tissue_properties["DURA"][1] .+ #DURA + (class .== 255) * tissue_properties["MARROW"][1] #MARROW + T2 = + (class .== 23) * tissue_properties["CSF"][2] .+ #CSF + (class .== 46) * tissue_properties["GM"][2] .+ #GM + (class .== 70) * tissue_properties["WM"][2] .+ #WM + (class .== 93) * tissue_properties["FAT1"][2] .+ #FAT1 + (class .== 116) * tissue_properties["MUSCLE"][2] .+ #MUSCLE + (class .== 139) * tissue_properties["SKIN/MUSCLE"][2] .+ #SKIN/MUSCLE + (class .== 162) * tissue_properties["SKULL"][2] .+ #SKULL + (class .== 185) * tissue_properties["VESSELS"][2] .+ #VESSELS + (class .== 209) * tissue_properties["FAT2"][2] .+ #FAT2 + (class .== 232) * tissue_properties["DURA"][2] .+ #DURA + (class .== 255) * tissue_properties["MARROW"][2] #MARROW + T2s = + (class .== 23) * tissue_properties["CSF"][3] .+ #CSF + (class .== 46) * tissue_properties["GM"][3] .+ #GM + (class .== 70) * tissue_properties["WM"][3] .+ #WM + (class .== 93) * tissue_properties["FAT1"][3] .+ #FAT1 + (class .== 116) * tissue_properties["MUSCLE"][3] .+ #MUSCLE + (class .== 139) * tissue_properties["SKIN/MUSCLE"][3] .+ #SKIN/MUSCLE + (class .== 162) * tissue_properties["SKULL"][3] .+ #SKULL + (class .== 185) * tissue_properties["VESSELS"][3] .+ #VESSELS + (class .== 209) * tissue_properties["FAT2"][3] .+ #FAT2 + (class .== 232) * tissue_properties["DURA"][3] .+ #DURA + (class .== 255) * tissue_properties["MARROW"][3] #MARROW + + ρ = + (class .== 23) * tissue_properties["CSF"][4] .+ #CSF + (class .== 46) * tissue_properties["GM"][4] .+ #GM + (class .== 70) * tissue_properties["WM"][4] .+ #WM + (class .== 93) * tissue_properties["FAT1"][4] .+ #FAT1 + (class .== 116) * tissue_properties["MUSCLE"][4] .+ #MUSCLE + (class .== 139) * tissue_properties["SKIN/MUSCLE"][4] .+ #SKIN/MUSCLE + (class .== 162) * tissue_properties["SKULL"][4] .+ #SKULL + (class .== 185) * tissue_properties["VESSELS"][4] .+ #VESSELS + (class .== 209) * tissue_properties["FAT2"][4] .+ #FAT2 + (class .== 232) * tissue_properties["DURA"][4] .+ #DURA + (class .== 255) * tissue_properties["MARROW"][4] #MARROW + + Δw = + (class .== 23) * tissue_properties["CSF"][5] .+ #CSF + (class .== 46) * tissue_properties["GM"][5] .+ #GM + (class .== 70) * tissue_properties["WM"][5] .+ #WM + (class .== 93) * tissue_properties["FAT1"][5] .+ #FAT1 + (class .== 116) * tissue_properties["MUSCLE"][5] .+ #MUSCLE + (class .== 139) * tissue_properties["SKIN/MUSCLE"][5] .+ #SKIN/MUSCLE + (class .== 162) * tissue_properties["SKULL"][5] .+ #SKULL + (class .== 185) * tissue_properties["VESSELS"][5] .+ #VESSELS + (class .== 209) * tissue_properties["FAT2"][5] .+ #FAT2 + (class .== 232) * tissue_properties["DURA"][5] .+ #DURA + (class .== 255) * tissue_properties["MARROW"][5] #MARROW + return T1, T2, T2s, ρ, Δw +end \ No newline at end of file From cf83a9e985cbd5f392413a2dd0060045f8954955 Mon Sep 17 00:00:00 2001 From: Guillermo Date: Thu, 26 Sep 2024 18:58:08 -0300 Subject: [PATCH 04/14] Details on documentation --- KomaMRIBase/src/datatypes/Phantom.jl | 289 +++----------------- KomaMRICore/src/simulation/SimulatorCore.jl | 2 + KomaMRIPlots/src/ui/DisplayFunctions.jl | 246 +++++++++++++++++ 3 files changed, 281 insertions(+), 256 deletions(-) diff --git a/KomaMRIBase/src/datatypes/Phantom.jl b/KomaMRIBase/src/datatypes/Phantom.jl index 25d8faf14..a4e287185 100644 --- a/KomaMRIBase/src/datatypes/Phantom.jl +++ b/KomaMRIBase/src/datatypes/Phantom.jl @@ -228,13 +228,20 @@ julia> obj = brain_phantom2D(; axis="sagittal", ss=1) julia> obj = brain_phantom2D(; axis="axial", us=[1, 2]) -julia> phantom_values = Dict( - #CSF, GM, WM, FAT1, MUSCLE, SKIN/MUSCLE, SKULL, VESSELS, FAT2, DURA, MARROW - "T1"=> [2569, 1153, 746, 0, 0, 0, 0, 0, 0, 0, 0], - "T2" => [329, 83, 70, 0, 0, 0, 0, 0, 0, 0, 0], - "T2s" => [58, 69, 61, 0, 0, 0, 0, 0, 0, 0, 0], - "ρ" => [1, 0.86, 0.77, 0, 0, 0, 0, 0, 0, 0, 0], - "Δw" => [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0]) +julia> phantom_values = + Dict( + # T1, T2, T2*, ρ, Δw + "CSF" => [2569, 329, 58, 1, 0], + "GM" => [1153, 83, 69, 0.86, 0], + "WM" => [746, 70, 61, 0.77, 0], + "FAT1" => [0, 0, 0, 0, 0], + "MUSCLE" => [0, 0, 0, 0, 0], + "SKIN/MUSCLE" => [0, 0, 0, 0, 0], + "SKULL" => [0, 0, 0, 0, 0], + "VESSELS" => [0, 0, 0, 0, 0], + "FAT2" => [0, 0, 0, 0, 0], + "DURA" => [0, 0, 0, 0, 0], + "MARROW" => [0, 0, 0, 0, 0]) julia> obj = brain_phantom2D(; tissue_properties=phantom_values) julia> plot_phantom_map(obj, :ρ) @@ -262,127 +269,8 @@ function brain_phantom2D(; axis="axial", ss=4, us=1, tissue_properties = nothing y = (-FOVy / 2):Δy:(FOVy / 2) #spin coordinates x, y = x .+ y' * 0, x * 0 .+ y' #grid points - - if !isnothing(custom_values) - # Define spin property vectors - T2 = - (class .== 23) * custom_values["T2"][1] .+ #CSF - (class .== 46) * custom_values["T2"][2] .+ #GM - (class .== 70) * custom_values["T2"][3] .+ #WM - (class .== 93) * custom_values["T2"][4] .+ #FAT1 - (class .== 116) * custom_values["T2"][5] .+ #MUSCLE - (class .== 139) * custom_values["T2"][6] .+ #SKIN/MUSCLE - (class .== 162) * custom_values["T2"][7] .+ #SKULL - (class .== 185) * custom_values["T2"][8] .+ #VESSELS - (class .== 209) * custom_values["T2"][9] .+ #FAT2 - (class .== 232) * custom_values["T2"][10] .+ #DURA - (class .== 255) * custom_values["T2"][11] #MARROW - T2s = - (class .== 23) * custom_values["T2s"][1] .+ #CSF - (class .== 46) * custom_values["T2s"][2] .+ #GM - (class .== 70) * custom_values["T2s"][3] .+ #WM - (class .== 93) * custom_values["T2s"][4] .+ #FAT1 - (class .== 116) * custom_values["T2s"][5] .+ #MUSCLE - (class .== 139) * custom_values["T2s"][6] .+ #SKIN/MUSCLE - (class .== 162) * custom_values["T2s"][7] .+ #SKULL - (class .== 185) * custom_values["T2s"][8] .+ #VESSELS - (class .== 209) * custom_values["T2s"][9] .+ #FAT2 - (class .== 232) * custom_values["T2s"][10] .+ #DURA - (class .== 255) * custom_values["T2s"][11] #MARROW - T1 = - (class .== 23) * custom_values["T1"][1] .+ #CSF - (class .== 46) * custom_values["T1"][2] .+ #GM - (class .== 70) * custom_values["T1"][3] .+ #WM - (class .== 93) * custom_values["T1"][4] .+ #FAT1 - (class .== 116) * custom_values["T1"][5] .+ #MUSCLE - (class .== 139) * custom_values["T1"][6] .+ #SKIN/MUSCLE - (class .== 162) * custom_values["T1"][7] .+ #SKULL - (class .== 185) * custom_values["T1"][8] .+ #VESSELS - (class .== 209) * custom_values["T1"][9] .+ #FAT2 - (class .== 232) * custom_values["T1"][10] .+ #DURA - (class .== 255) * custom_values["T1"][11] #MARROW - ρ = - (class .== 23) * custom_values["ρ"][1] .+ #CSF - (class .== 46) * custom_values["ρ"][2] .+ #GM - (class .== 70) * custom_values["ρ"][3] .+ #WM - (class .== 93) * custom_values["ρ"][4] .+ #FAT1 - (class .== 116) * custom_values["ρ"][5] .+ #MUSCLE - (class .== 139) * custom_values["ρ"][6] .+ #SKIN/MUSCLE - (class .== 162) * custom_values["ρ"][7] .+ #SKULL - (class .== 185) * custom_values["ρ"][8] .+ #VESSELS - (class .== 209) * custom_values["ρ"][9] .+ #FAT2 - (class .== 232) * custom_values["ρ"][10] .+ #DURA - (class .== 255) * custom_values["ρ"][11] #MARROW - - Δw = - (class .== 23) * custom_values["Δw"][1] .+ #CSF - (class .== 46) * custom_values["Δw"][2] .+ #GM - (class .== 70) * custom_values["Δw"][3] .+ #WM - (class .== 93) * custom_values["Δw"][4] .+ #FAT1 - (class .== 116) * custom_values["Δw"][5] .+ #MUSCLE - (class .== 139) * custom_values["Δw"][6] .+ #SKIN/MUSCLE - (class .== 162) * custom_values["Δw"][7] .+ #SKULL - (class .== 185) * custom_values["Δw"][8] .+ #VESSELS - (class .== 209) * custom_values["Δw"][9] .+ #FAT2 - (class .== 232) * custom_values["Δw"][10] .+ #DURA - (class .== 255) * custom_values["Δw"][11] #MARROW - Δw = -2π.*Δw - - else - # Define spin property vectors - T2 = - (class .== 23) * 329 .+ #CSF - (class .== 46) * 83 .+ #GM - (class .== 70) * 70 .+ #WM - (class .== 93) * 70 .+ #FAT1 - (class .== 116) * 47 .+ #MUSCLE - (class .== 139) * 329 .+ #SKIN/MUSCLE - (class .== 162) * 0 .+ #SKULL - (class .== 185) * 0 .+ #VESSELS - (class .== 209) * 70 .+ #FAT2 - (class .== 232) * 329 .+ #DURA - (class .== 255) * 70 #MARROW - T2s = - (class .== 23) * 58 .+ #CSF - (class .== 46) * 69 .+ #GM - (class .== 70) * 61 .+ #WM - (class .== 93) * 58 .+ #FAT1 - (class .== 116) * 30 .+ #MUSCLE - (class .== 139) * 58 .+ #SKIN/MUSCLE - (class .== 162) * 0 .+ #SKULL - (class .== 185) * 0 .+ #VESSELS - (class .== 209) * 61 .+ #FAT2 - (class .== 232) * 58 .+ #DURA - (class .== 255) * 61 #MARROW - T1 = - (class .== 23) * 2569 .+ #CSF - (class .== 46) * 833 .+ #GM - (class .== 70) * 500 .+ #WM - (class .== 93) * 350 .+ #FAT1 - (class .== 116) * 900 .+ #MUSCLE - (class .== 139) * 569 .+ #SKIN/MUSCLE - (class .== 162) * 0 .+ #SKULL - (class .== 185) * 0 .+ #VESSELS - (class .== 209) * 500 .+ #FAT2 - (class .== 232) * 2569 .+ #DURA - (class .== 255) * 500 #MARROW - ρ = - (class .== 23) * 1 .+ #CSF - (class .== 46) * 0.86 .+ #GM - (class .== 70) * 0.77 .+ #WM - (class .== 93) * 1 .+ #FAT1 - (class .== 116) * 1 .+ #MUSCLE - (class .== 139) * 1 .+ #SKIN/MUSCLE - (class .== 162) * 0 .+ #SKULL - (class .== 185) * 0 .+ #VESSELS - (class .== 209) * 0.77 .+ #FAT2 - (class .== 232) * 1 .+ #DURA - (class .== 255) * 0.77 #MARROW - Δw_fat = -220 * 2π - Δw = - (class .== 93) * Δw_fat .+ #FAT1 - (class .== 209) * Δw_fat #FAT2 - end + # Get tissue properties + T1, T2, T2s, ρ, Δw = default_brain_tissue_properties(class, tissue_properties) T1 = T1 * 1e-3 T2 = T2 * 1e-3 T2s = T2s * 1e-3 @@ -430,13 +318,20 @@ julia> obj = brain_phantom3D(; ss=5) julia> obj = brain_phantom3D(; us=[2, 2, 1]) -julia> phantom_values = Dict( - #CSF, GM, WM, FAT1, MUSCLE, SKIN/MUSCLE, SKULL, VESSELS, FAT2, DURA, MARROW - "T1"=> [2569, 1153, 746, 0, 0, 0, 0, 0, 0, 0, 0], - "T2" => [329, 83, 70, 0, 0, 0, 0, 0, 0, 0, 0], - "T2s" => [58, 69, 61, 0, 0, 0, 0, 0, 0, 0, 0], - "ρ" => [1, 0.86, 0.77, 0, 0, 0, 0, 0, 0, 0, 0], - "Δw" => [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0]) +julia> phantom_values = + Dict( + # T1, T2, T2*, ρ, Δw + "CSF" => [2569, 329, 58, 1, 0], + "GM" => [1153, 83, 69, 0.86, 0], + "WM" => [746, 70, 61, 0.77, 0], + "FAT1" => [0, 0, 0, 0, 0], + "MUSCLE" => [0, 0, 0, 0, 0], + "SKIN/MUSCLE" => [0, 0, 0, 0, 0], + "SKULL" => [0, 0, 0, 0, 0], + "VESSELS" => [0, 0, 0, 0, 0], + "FAT2" => [0, 0, 0, 0, 0], + "DURA" => [0, 0, 0, 0, 0], + "MARROW" => [0, 0, 0, 0, 0]) julia> obj = brain_phantom3D(; tissue_properties=phantom_values) julia> plot_phantom_map(obj, :ρ) @@ -470,127 +365,9 @@ function brain_phantom3D(; ss=4, us=1, start_end=[160, 200], tissue_properties=n x = 1 * xx .+ 0 * yy .+ 0 * zz y = 0 * xx .+ 1 * yy .+ 0 * zz z = 0 * xx .+ 0 * yy .+ 1 * zz - - if !isnothing(custom_values) - # Define spin property vectors - T2 = - (class .== 23) * custom_values["T2"][1] .+ #CSF - (class .== 46) * custom_values["T2"][2] .+ #GM - (class .== 70) * custom_values["T2"][3] .+ #WM - (class .== 93) * custom_values["T2"][4] .+ #FAT1 - (class .== 116) * custom_values["T2"][5] .+ #MUSCLE - (class .== 139) * custom_values["T2"][6] .+ #SKIN/MUSCLE - (class .== 162) * custom_values["T2"][7] .+ #SKULL - (class .== 185) * custom_values["T2"][8] .+ #VESSELS - (class .== 209) * custom_values["T2"][9] .+ #FAT2 - (class .== 232) * custom_values["T2"][10] .+ #DURA - (class .== 255) * custom_values["T2"][11] #MARROW - T2s = - (class .== 23) * custom_values["T2s"][1] .+ #CSF - (class .== 46) * custom_values["T2s"][2] .+ #GM - (class .== 70) * custom_values["T2s"][3] .+ #WM - (class .== 93) * custom_values["T2s"][4] .+ #FAT1 - (class .== 116) * custom_values["T2s"][5] .+ #MUSCLE - (class .== 139) * custom_values["T2s"][6] .+ #SKIN/MUSCLE - (class .== 162) * custom_values["T2s"][7] .+ #SKULL - (class .== 185) * custom_values["T2s"][8] .+ #VESSELS - (class .== 209) * custom_values["T2s"][9] .+ #FAT2 - (class .== 232) * custom_values["T2s"][10] .+ #DURA - (class .== 255) * custom_values["T2s"][11] #MARROW - T1 = - (class .== 23) * custom_values["T1"][1] .+ #CSF - (class .== 46) * custom_values["T1"][2] .+ #GM - (class .== 70) * custom_values["T1"][3] .+ #WM - (class .== 93) * custom_values["T1"][4] .+ #FAT1 - (class .== 116) * custom_values["T1"][5] .+ #MUSCLE - (class .== 139) * custom_values["T1"][6] .+ #SKIN/MUSCLE - (class .== 162) * custom_values["T1"][7] .+ #SKULL - (class .== 185) * custom_values["T1"][8] .+ #VESSELS - (class .== 209) * custom_values["T1"][9] .+ #FAT2 - (class .== 232) * custom_values["T1"][10] .+ #DURA - (class .== 255) * custom_values["T1"][11] #MARROW - ρ = - (class .== 23) * custom_values["ρ"][1] .+ #CSF - (class .== 46) * custom_values["ρ"][2] .+ #GM - (class .== 70) * custom_values["ρ"][3] .+ #WM - (class .== 93) * custom_values["ρ"][4] .+ #FAT1 - (class .== 116) * custom_values["ρ"][5] .+ #MUSCLE - (class .== 139) * custom_values["ρ"][6] .+ #SKIN/MUSCLE - (class .== 162) * custom_values["ρ"][7] .+ #SKULL - (class .== 185) * custom_values["ρ"][8] .+ #VESSELS - (class .== 209) * custom_values["ρ"][9] .+ #FAT2 - (class .== 232) * custom_values["ρ"][10] .+ #DURA - (class .== 255) * custom_values["ρ"][11] #MARROW - - Δw = - (class .== 23) * custom_values["Δw"][1] .+ #CSF - (class .== 46) * custom_values["Δw"][2] .+ #GM - (class .== 70) * custom_values["Δw"][3] .+ #WM - (class .== 93) * custom_values["Δw"][4] .+ #FAT1 - (class .== 116) * custom_values["Δw"][5] .+ #MUSCLE - (class .== 139) * custom_values["Δw"][6] .+ #SKIN/MUSCLE - (class .== 162) * custom_values["Δw"][7] .+ #SKULL - (class .== 185) * custom_values["Δw"][8] .+ #VESSELS - (class .== 209) * custom_values["Δw"][9] .+ #FAT2 - (class .== 232) * custom_values["Δw"][10] .+ #DURA - (class .== 255) * custom_values["Δw"][11] #MARROW - Δw = -2π.*Δw - - else - # Define spin property vectors - T2 = - (class .== 23) * 329 .+ #CSF - (class .== 46) * 83 .+ #GM - (class .== 70) * 70 .+ #WM - (class .== 93) * 70 .+ #FAT1 - (class .== 116) * 47 .+ #MUSCLE - (class .== 139) * 329 .+ #SKIN/MUSCLE - (class .== 162) * 0 .+ #SKULL - (class .== 185) * 0 .+ #VESSELS - (class .== 209) * 70 .+ #FAT2 - (class .== 232) * 329 .+ #DURA - (class .== 255) * 70 #MARROW - T2s = - (class .== 23) * 58 .+ #CSF - (class .== 46) * 69 .+ #GM - (class .== 70) * 61 .+ #WM - (class .== 93) * 58 .+ #FAT1 - (class .== 116) * 30 .+ #MUSCLE - (class .== 139) * 58 .+ #SKIN/MUSCLE - (class .== 162) * 0 .+ #SKULL - (class .== 185) * 0 .+ #VESSELS - (class .== 209) * 61 .+ #FAT2 - (class .== 232) * 58 .+ #DURA - (class .== 255) * 61 #MARROW - T1 = - (class .== 23) * 2569 .+ #CSF - (class .== 46) * 833 .+ #GM - (class .== 70) * 500 .+ #WM - (class .== 93) * 350 .+ #FAT1 - (class .== 116) * 900 .+ #MUSCLE - (class .== 139) * 569 .+ #SKIN/MUSCLE - (class .== 162) * 0 .+ #SKULL - (class .== 185) * 0 .+ #VESSELS - (class .== 209) * 500 .+ #FAT2 - (class .== 232) * 2569 .+ #DURA - (class .== 255) * 500 #MARROW - ρ = - (class .== 23) * 1 .+ #CSF - (class .== 46) * 0.86 .+ #GM - (class .== 70) * 0.77 .+ #WM - (class .== 93) * 1 .+ #FAT1 - (class .== 116) * 1 .+ #MUSCLE - (class .== 139) * 1 .+ #SKIN/MUSCLE - (class .== 162) * 0 .+ #SKULL - (class .== 185) * 0 .+ #VESSELS - (class .== 209) * 0.77 .+ #FAT2 - (class .== 232) * 1 .+ #DURA - (class .== 255) * 0.77 #MARROW - Δw_fat = -220 * 2π - Δw = - (class .== 93) * Δw_fat .+ #FAT1 - (class .== 209) * Δw_fat #FAT2 - end + + # Get tissue properties + T1, T2, T2s, ρ, Δw = default_brain_tissue_properties(class, tissue_properties) T1 = T1 * 1e-3 T2 = T2 * 1e-3 T2s = T2s * 1e-3 diff --git a/KomaMRICore/src/simulation/SimulatorCore.jl b/KomaMRICore/src/simulation/SimulatorCore.jl index 6ba42e583..519d83734 100644 --- a/KomaMRICore/src/simulation/SimulatorCore.jl +++ b/KomaMRICore/src/simulation/SimulatorCore.jl @@ -409,6 +409,8 @@ function simulate( out = Xt elseif sim_params["return_type"] == "mat" out = sig + elseif sim_params["return_type"] == "full" + out = Dict("state" => Xt, "mat" => sig) elseif sim_params["return_type"] == "raw" # To visually check the simulation blocks sim_params_raw = copy(sim_params) diff --git a/KomaMRIPlots/src/ui/DisplayFunctions.jl b/KomaMRIPlots/src/ui/DisplayFunctions.jl index 9dab997ac..a3a22482e 100644 --- a/KomaMRIPlots/src/ui/DisplayFunctions.jl +++ b/KomaMRIPlots/src/ui/DisplayFunctions.jl @@ -985,6 +985,252 @@ function plot_kspace(seq::Sequence; width=nothing, height=nothing, darkmode=fals return plot_koma(p, l; config) end +# TODO Adapt the documentation + +""" + p = plot_state_map(obj::Phantom, key::Symbol; kwargs...) + +Plots a phantom map for a specific spin parameter given by `key`. + +# Arguments +- `obj`: (`::Phantom`) Phantom struct +- `key`: (`::Symbol`, opts: [`:ρ`, `:T1`, `:T2`, `:T2s`, `:x`, `:y`, `:z`]) symbol for + displaying different parameters of the phantom spins + +# Keywords +- `height`: (`::Integer`, `=600`) plot height +- `width`: (`::Integer`, `=nothing`) plot width +- `darkmode`: (`::Bool`, `=false`) boolean to indicate whether to display darkmode style +- `view_2d`: (`::Bool`, `=false`) boolean to indicate whether to use a 2D scatter plot +- `colorbar`: (`::Bool`, `=true`) boolean to indicate whether to display a colorbar + +# Returns +- `p`: (`::PlotlyJS.SyncPlot`) plot of the phantom map for a specific spin parameter + +# References +Colormaps from https://github.com/markgriswold/MRFColormaps +Towards Unified Colormaps for Quantitative MRF Data, Mark Griswold, et al. (2018). + +# Examples +```julia-repl +julia> obj2D, obj3D = brain_phantom2D(), brain_phantom3D(); + +julia> plot_phantom_map(obj2D, :ρ) + +julia> plot_phantom_map(obj3D, :ρ) +``` +""" +function plot_state_map( + ph, + key::Symbol; + height=700, + width=nothing, + darkmode=false, + view_2d=true, + colorbar=true, + intermediate_time_samples=0, + max_time_samples=100, + max_spins=100_000, + frame_duration_ms=250, + kwargs..., +) + function decimate_uniform_phantom(ph::Phantom, num_points::Int) + dimx, dimy, dimz = KomaMRIBase.get_dims(ph) + ss = Int(ceil((length(ph) / num_points)^(1 / sum(KomaMRIBase.get_dims(ph))))) + ssx = dimx ? ss : 1 + ssy = dimy ? ss : 1 + ssz = dimz ? ss : 1 + ix = sortperm(ph.x)[1:ssx:end] + iy = sortperm(ph.y)[1:ssy:end] + iz = sortperm(ph.z)[1:ssz:end] + idx = intersect(ix, iy, iz) + return ph[idx] + end + + if length(ph.x) > max_spins + ph = decimate_uniform_phantom(ph, max_spins) + @warn "For performance reasons, the number of displayed spins was capped to `max_spins`=$(max_spins)." + end + + + path = @__DIR__ + if key == :Mxy || key == :Mz || key == :∠Mxy || key == :RealOfMxy || key ==:ImagOfMxy + cmin_key = 0 + factor = 1 + unit = " a.u." + colormap = "Greys" + if key == :Mxy + cmax_key = maximum(abs.(ph.Mxy)) + cmin_key = minimum(abs.(ph.Mxy)) + x, y, z = ph.x, ph.y, abs.(ph.Mxy) + elseif key == :Mz + cmax_key = maximum(abs.(ph.Mz)) + cmin_key = minimum(abs.(ph.Mz)) + x, y, z = ph.x, ph.y, abs.(ph.Mz) + elseif key == :∠Mxy + cmax_key = maximum(angle.(ph.Mxy)) + cmin_key = minimum(angle.(ph.Mxy)) + x, y, z = ph.x, ph.y, angle.(ph.Mxy) + elseif key == :RealOfMxy + cmax_key = maximum(real.(ph.Mxy)) + cmin_key = minimum(real.(ph.Mxy)) + x, y, z = ph.x, ph.y, real.(ph.Mxy) + elseif key == :ImagOfMxy + cmax_key = maximum(imag.(ph.Mxy)) + cmin_key = minimum(imag.(ph.Mxy)) + x, y, z = ph.x, ph.y, imag.(ph.Mxy) + end + else + factor = 1 + cmin_key = minimum(getproperty(ph, key)) + cmax_key = maximum(getproperty(ph, key)) + unit = "" + colormap = "Greys" + x, y, z = ph.x, ph.y, ph.z + end + + x0 = -maximum(abs.([ph.x ph.y ph.z])) * 1e2 + xf = maximum(abs.([ph.x ph.y ph.z])) * 1e2 + + if view_2d + trace = [ + scatter(; + x=(x[:, 1]) * 1e2, + y=(y[:, 1]) * 1e2, + mode="markers", + marker=attr(; + color=z, + showscale=colorbar, + colorscale=colormap, + colorbar=attr(; ticksuffix=unit, title=string(key)), + cmin=cmin_key, + cmax=cmax_key, + size=4, + ), + showlegend=false, + text=z, + #text=round.(getproperty(ph, key) * factor, digits=4), + hovertemplate="x: %{x:.1f} cm
y: %{y:.1f} cm
$(string(key)): %{text}$unit", + ), + ] + else + trace = [ + scatter3d(; + x=(x[:, 1]) * 1e2, + y=(y[:, 1]) * 1e2, + z=(z[:, 1]) * 1e2, + mode="markers", + marker=attr(; + color=getproperty(ph, key) * factor, + showscale=colorbar, + colorscale=colormap, + colorbar=attr(; ticksuffix=unit, title=string(key)), + cmin=cmin_key, + cmax=cmax_key, + size=2, + ), + showlegend=false, + text=round.(getproperty(ph, key) * factor, digits=4), + hovertemplate="x: %{x:.1f} cm
y: %{y:.1f} cm
z: %{z:.1f} cm
$(string(key)): %{text}$unit", + ), + ] + + end + + buttons_attr = [ + attr(; + label="▶", # play symbol + method="animate", + args=[ + nothing, + attr(; + fromcurrent=true, + transition=(duration=frame_duration_ms,), + frame=attr(; duration=frame_duration_ms, redraw=true), + ), + ], + ), + attr(; + label="◼", # pause symbol + method="animate", + args=[ + [nothing], + attr(; + mode="immediate", + fromcurrent=true, + transition=attr(; duration=frame_duration_ms), + frame=attr(; duration=frame_duration_ms, redraw=true), + ), + ], + ), + ] + + #Layout + bgcolor, text_color, plot_bgcolor, grid_color, sep_color = theme_chooser(darkmode) + l = Layout(; + title=ph.name * ": " * string(key), + xaxis_title="x", + yaxis_title="y", + xaxis_range=[x0, xf], + yaxis_range=[x0, xf], + xaxis_ticksuffix=" cm", + yaxis_ticksuffix=" cm", + plot_bgcolor=plot_bgcolor, + paper_bgcolor=bgcolor, + xaxis_gridcolor=grid_color, + yaxis_gridcolor=grid_color, + xaxis_zerolinecolor=grid_color, + yaxis_zerolinecolor=grid_color, + font_color=text_color, + scene=attr(; + xaxis=attr(; + title="x", + range=[x0, xf], + ticksuffix=" cm", + backgroundcolor=plot_bgcolor, + gridcolor=grid_color, + zerolinecolor=grid_color, + ), + yaxis=attr(; + title="y", + range=[x0, xf], + ticksuffix=" cm", + backgroundcolor=plot_bgcolor, + gridcolor=grid_color, + zerolinecolor=grid_color, + ), + zaxis=attr(; + title="z", + range=[x0, xf], + ticksuffix=" cm", + backgroundcolor=plot_bgcolor, + gridcolor=grid_color, + zerolinecolor=grid_color, + ), + aspectmode="manual", + aspectratio=attr(; x=1, y=1, z=1), + ), + margin=attr(; t=50, l=0, r=0, b=0), + modebar=attr(; + orientation="h", bgcolor=bgcolor, color=text_color, activecolor=plot_bgcolor + ), + xaxis=attr(; constrain="domain"), + yaxis=attr(; scaleanchor="x"), + hovermode="closest", + ) + + if height !== nothing + l.height = height + end + if width !== nothing + l.width = width + end + + return Plot(trace, l) +end + + + """ p = plot_phantom_map(obj::Phantom, key::Symbol; kwargs...) From f858ef10c047b016daec834535fcd418a2be8aff Mon Sep 17 00:00:00 2001 From: Guillermo Sahonero Alvarez Date: Thu, 26 Sep 2024 22:44:32 +0000 Subject: [PATCH 05/14] Updating to remove unnecessary things --- KomaMRICore/src/simulation/SimulatorCore.jl | 2 - KomaMRIPlots/src/ui/DisplayFunctions.jl | 246 -------------------- 2 files changed, 248 deletions(-) diff --git a/KomaMRICore/src/simulation/SimulatorCore.jl b/KomaMRICore/src/simulation/SimulatorCore.jl index 519d83734..6ba42e583 100644 --- a/KomaMRICore/src/simulation/SimulatorCore.jl +++ b/KomaMRICore/src/simulation/SimulatorCore.jl @@ -409,8 +409,6 @@ function simulate( out = Xt elseif sim_params["return_type"] == "mat" out = sig - elseif sim_params["return_type"] == "full" - out = Dict("state" => Xt, "mat" => sig) elseif sim_params["return_type"] == "raw" # To visually check the simulation blocks sim_params_raw = copy(sim_params) diff --git a/KomaMRIPlots/src/ui/DisplayFunctions.jl b/KomaMRIPlots/src/ui/DisplayFunctions.jl index a3a22482e..9dab997ac 100644 --- a/KomaMRIPlots/src/ui/DisplayFunctions.jl +++ b/KomaMRIPlots/src/ui/DisplayFunctions.jl @@ -985,252 +985,6 @@ function plot_kspace(seq::Sequence; width=nothing, height=nothing, darkmode=fals return plot_koma(p, l; config) end -# TODO Adapt the documentation - -""" - p = plot_state_map(obj::Phantom, key::Symbol; kwargs...) - -Plots a phantom map for a specific spin parameter given by `key`. - -# Arguments -- `obj`: (`::Phantom`) Phantom struct -- `key`: (`::Symbol`, opts: [`:ρ`, `:T1`, `:T2`, `:T2s`, `:x`, `:y`, `:z`]) symbol for - displaying different parameters of the phantom spins - -# Keywords -- `height`: (`::Integer`, `=600`) plot height -- `width`: (`::Integer`, `=nothing`) plot width -- `darkmode`: (`::Bool`, `=false`) boolean to indicate whether to display darkmode style -- `view_2d`: (`::Bool`, `=false`) boolean to indicate whether to use a 2D scatter plot -- `colorbar`: (`::Bool`, `=true`) boolean to indicate whether to display a colorbar - -# Returns -- `p`: (`::PlotlyJS.SyncPlot`) plot of the phantom map for a specific spin parameter - -# References -Colormaps from https://github.com/markgriswold/MRFColormaps -Towards Unified Colormaps for Quantitative MRF Data, Mark Griswold, et al. (2018). - -# Examples -```julia-repl -julia> obj2D, obj3D = brain_phantom2D(), brain_phantom3D(); - -julia> plot_phantom_map(obj2D, :ρ) - -julia> plot_phantom_map(obj3D, :ρ) -``` -""" -function plot_state_map( - ph, - key::Symbol; - height=700, - width=nothing, - darkmode=false, - view_2d=true, - colorbar=true, - intermediate_time_samples=0, - max_time_samples=100, - max_spins=100_000, - frame_duration_ms=250, - kwargs..., -) - function decimate_uniform_phantom(ph::Phantom, num_points::Int) - dimx, dimy, dimz = KomaMRIBase.get_dims(ph) - ss = Int(ceil((length(ph) / num_points)^(1 / sum(KomaMRIBase.get_dims(ph))))) - ssx = dimx ? ss : 1 - ssy = dimy ? ss : 1 - ssz = dimz ? ss : 1 - ix = sortperm(ph.x)[1:ssx:end] - iy = sortperm(ph.y)[1:ssy:end] - iz = sortperm(ph.z)[1:ssz:end] - idx = intersect(ix, iy, iz) - return ph[idx] - end - - if length(ph.x) > max_spins - ph = decimate_uniform_phantom(ph, max_spins) - @warn "For performance reasons, the number of displayed spins was capped to `max_spins`=$(max_spins)." - end - - - path = @__DIR__ - if key == :Mxy || key == :Mz || key == :∠Mxy || key == :RealOfMxy || key ==:ImagOfMxy - cmin_key = 0 - factor = 1 - unit = " a.u." - colormap = "Greys" - if key == :Mxy - cmax_key = maximum(abs.(ph.Mxy)) - cmin_key = minimum(abs.(ph.Mxy)) - x, y, z = ph.x, ph.y, abs.(ph.Mxy) - elseif key == :Mz - cmax_key = maximum(abs.(ph.Mz)) - cmin_key = minimum(abs.(ph.Mz)) - x, y, z = ph.x, ph.y, abs.(ph.Mz) - elseif key == :∠Mxy - cmax_key = maximum(angle.(ph.Mxy)) - cmin_key = minimum(angle.(ph.Mxy)) - x, y, z = ph.x, ph.y, angle.(ph.Mxy) - elseif key == :RealOfMxy - cmax_key = maximum(real.(ph.Mxy)) - cmin_key = minimum(real.(ph.Mxy)) - x, y, z = ph.x, ph.y, real.(ph.Mxy) - elseif key == :ImagOfMxy - cmax_key = maximum(imag.(ph.Mxy)) - cmin_key = minimum(imag.(ph.Mxy)) - x, y, z = ph.x, ph.y, imag.(ph.Mxy) - end - else - factor = 1 - cmin_key = minimum(getproperty(ph, key)) - cmax_key = maximum(getproperty(ph, key)) - unit = "" - colormap = "Greys" - x, y, z = ph.x, ph.y, ph.z - end - - x0 = -maximum(abs.([ph.x ph.y ph.z])) * 1e2 - xf = maximum(abs.([ph.x ph.y ph.z])) * 1e2 - - if view_2d - trace = [ - scatter(; - x=(x[:, 1]) * 1e2, - y=(y[:, 1]) * 1e2, - mode="markers", - marker=attr(; - color=z, - showscale=colorbar, - colorscale=colormap, - colorbar=attr(; ticksuffix=unit, title=string(key)), - cmin=cmin_key, - cmax=cmax_key, - size=4, - ), - showlegend=false, - text=z, - #text=round.(getproperty(ph, key) * factor, digits=4), - hovertemplate="x: %{x:.1f} cm
y: %{y:.1f} cm
$(string(key)): %{text}$unit", - ), - ] - else - trace = [ - scatter3d(; - x=(x[:, 1]) * 1e2, - y=(y[:, 1]) * 1e2, - z=(z[:, 1]) * 1e2, - mode="markers", - marker=attr(; - color=getproperty(ph, key) * factor, - showscale=colorbar, - colorscale=colormap, - colorbar=attr(; ticksuffix=unit, title=string(key)), - cmin=cmin_key, - cmax=cmax_key, - size=2, - ), - showlegend=false, - text=round.(getproperty(ph, key) * factor, digits=4), - hovertemplate="x: %{x:.1f} cm
y: %{y:.1f} cm
z: %{z:.1f} cm
$(string(key)): %{text}$unit", - ), - ] - - end - - buttons_attr = [ - attr(; - label="▶", # play symbol - method="animate", - args=[ - nothing, - attr(; - fromcurrent=true, - transition=(duration=frame_duration_ms,), - frame=attr(; duration=frame_duration_ms, redraw=true), - ), - ], - ), - attr(; - label="◼", # pause symbol - method="animate", - args=[ - [nothing], - attr(; - mode="immediate", - fromcurrent=true, - transition=attr(; duration=frame_duration_ms), - frame=attr(; duration=frame_duration_ms, redraw=true), - ), - ], - ), - ] - - #Layout - bgcolor, text_color, plot_bgcolor, grid_color, sep_color = theme_chooser(darkmode) - l = Layout(; - title=ph.name * ": " * string(key), - xaxis_title="x", - yaxis_title="y", - xaxis_range=[x0, xf], - yaxis_range=[x0, xf], - xaxis_ticksuffix=" cm", - yaxis_ticksuffix=" cm", - plot_bgcolor=plot_bgcolor, - paper_bgcolor=bgcolor, - xaxis_gridcolor=grid_color, - yaxis_gridcolor=grid_color, - xaxis_zerolinecolor=grid_color, - yaxis_zerolinecolor=grid_color, - font_color=text_color, - scene=attr(; - xaxis=attr(; - title="x", - range=[x0, xf], - ticksuffix=" cm", - backgroundcolor=plot_bgcolor, - gridcolor=grid_color, - zerolinecolor=grid_color, - ), - yaxis=attr(; - title="y", - range=[x0, xf], - ticksuffix=" cm", - backgroundcolor=plot_bgcolor, - gridcolor=grid_color, - zerolinecolor=grid_color, - ), - zaxis=attr(; - title="z", - range=[x0, xf], - ticksuffix=" cm", - backgroundcolor=plot_bgcolor, - gridcolor=grid_color, - zerolinecolor=grid_color, - ), - aspectmode="manual", - aspectratio=attr(; x=1, y=1, z=1), - ), - margin=attr(; t=50, l=0, r=0, b=0), - modebar=attr(; - orientation="h", bgcolor=bgcolor, color=text_color, activecolor=plot_bgcolor - ), - xaxis=attr(; constrain="domain"), - yaxis=attr(; scaleanchor="x"), - hovermode="closest", - ) - - if height !== nothing - l.height = height - end - if width !== nothing - l.width = width - end - - return Plot(trace, l) -end - - - """ p = plot_phantom_map(obj::Phantom, key::Symbol; kwargs...) From 0294f4bacd4c9b2cf79e7009c96ff318f5bbabda Mon Sep 17 00:00:00 2001 From: Guillermo Sahonero Alvarez Date: Thu, 26 Sep 2024 22:47:43 +0000 Subject: [PATCH 06/14] Returning phantom to original code --- KomaMRIBase/src/datatypes/Phantom.jl | 6 +----- 1 file changed, 1 insertion(+), 5 deletions(-) diff --git a/KomaMRIBase/src/datatypes/Phantom.jl b/KomaMRIBase/src/datatypes/Phantom.jl index a4e287185..b5b3bbf9f 100644 --- a/KomaMRIBase/src/datatypes/Phantom.jl +++ b/KomaMRIBase/src/datatypes/Phantom.jl @@ -18,8 +18,6 @@ a property value representing a spin. This struct serves as an input for the sim - `Dλ2`: (`::AbstractVector{T<:Real}`) spin Dλ2 (diffusion) parameter vector - `Dθ`: (`::AbstractVector{T<:Real}`) spin Dθ (diffusion) parameter vector - `motion`: (`::AbstractMotion{T<:Real}`) motion -- `Mz`: (`::AbstractVector{ComplexF32}`) transverse magnetization parameter vector, only for storage purposes -- `Mxy`: (`::AbstractVector{ComplexF32}`) longitudinal magnetization parameter vector, only for storage purposes # Returns - `obj`: (`::Phantom`) Phantom struct @@ -31,7 +29,7 @@ julia> obj = Phantom(x=[0.0]) julia> obj.ρ ``` """ -@with_kw mutable struct Phantom{T<:Number} +@with_kw mutable struct Phantom{T<:Real} name::String = "spins" x :: AbstractVector{T} y::AbstractVector{T} = zeros(eltype(x), size(x)) @@ -50,8 +48,6 @@ julia> obj.ρ #Diff::Vector{DiffusionModel} #Diffusion map #Motion motion::AbstractMotion{T} = NoMotion{eltype(x)}() - Mxy::AbstractVector{ComplexF32} = zeros(eltype(x), size(x)) .+ 0im - Mz::AbstractVector{ComplexF32} = zeros(eltype(x), size(x)) .+ 0im end const NON_STRING_PHANTOM_FIELDS = Iterators.filter(x -> fieldtype(Phantom, x) != String, fieldnames(Phantom)) From 165b7933891f937d17bd76f1b6eef53fb7652966 Mon Sep 17 00:00:00 2001 From: Guillermo Date: Fri, 27 Sep 2024 11:54:38 -0300 Subject: [PATCH 07/14] Resolving problems in brain phantom --- KomaMRIBase/src/datatypes/Phantom.jl | 203 ++++++++++----------------- 1 file changed, 74 insertions(+), 129 deletions(-) diff --git a/KomaMRIBase/src/datatypes/Phantom.jl b/KomaMRIBase/src/datatypes/Phantom.jl index a4e287185..b52e8c7e3 100644 --- a/KomaMRIBase/src/datatypes/Phantom.jl +++ b/KomaMRIBase/src/datatypes/Phantom.jl @@ -18,8 +18,6 @@ a property value representing a spin. This struct serves as an input for the sim - `Dλ2`: (`::AbstractVector{T<:Real}`) spin Dλ2 (diffusion) parameter vector - `Dθ`: (`::AbstractVector{T<:Real}`) spin Dθ (diffusion) parameter vector - `motion`: (`::AbstractMotion{T<:Real}`) motion -- `Mz`: (`::AbstractVector{ComplexF32}`) transverse magnetization parameter vector, only for storage purposes -- `Mxy`: (`::AbstractVector{ComplexF32}`) longitudinal magnetization parameter vector, only for storage purposes # Returns - `obj`: (`::Phantom`) Phantom struct @@ -31,7 +29,7 @@ julia> obj = Phantom(x=[0.0]) julia> obj.ρ ``` """ -@with_kw mutable struct Phantom{T<:Number} +@with_kw mutable struct Phantom{T<:Real} name::String = "spins" x :: AbstractVector{T} y::AbstractVector{T} = zeros(eltype(x), size(x)) @@ -50,8 +48,6 @@ julia> obj.ρ #Diff::Vector{DiffusionModel} #Diffusion map #Motion motion::AbstractMotion{T} = NoMotion{eltype(x)}() - Mxy::AbstractVector{ComplexF32} = zeros(eltype(x), size(x)) .+ 0im - Mz::AbstractVector{ComplexF32} = zeros(eltype(x), size(x)) .+ 0im end const NON_STRING_PHANTOM_FIELDS = Iterators.filter(x -> fieldtype(Phantom, x) != String, fieldnames(Phantom)) @@ -217,7 +213,7 @@ Default ss=4 sample spacing is 2 mm. Original file (ss=1) sample spacing is .5 m - `axis`: (`::String`, `="axial"`, opts=[`"axial"`, `"coronal"`, `"sagittal"`]) orientation of the phantom - `ss`: (`::Integer or ::Vector{Integer}`, `=4`) subsampling parameter for all axes if scaler, per axis if 2 element vector [ssx, ssy] - `us`: (`::Integer or ::Vector{Integer}`, `=1`) upsampling parameter for all axes if scaler, per axis if 2 element vector [usx, usy], if used ss is set to ss=1 -- `tissue_properties`: (`::Dict`, `=nothing`) phantom tissue properties in ms and Hz considering the available tissues +- `tissue_properties`: (`::Dict`, `=Dict()`) phantom tissue properties in SI units considering the available tissues # Returns - `obj`: (`::Phantom`) Phantom struct @@ -231,23 +227,23 @@ julia> obj = brain_phantom2D(; axis="axial", us=[1, 2]) julia> phantom_values = Dict( # T1, T2, T2*, ρ, Δw - "CSF" => [2569, 329, 58, 1, 0], - "GM" => [1153, 83, 69, 0.86, 0], - "WM" => [746, 70, 61, 0.77, 0], - "FAT1" => [0, 0, 0, 0, 0], - "MUSCLE" => [0, 0, 0, 0, 0], - "SKIN/MUSCLE" => [0, 0, 0, 0, 0], - "SKULL" => [0, 0, 0, 0, 0], - "VESSELS" => [0, 0, 0, 0, 0], - "FAT2" => [0, 0, 0, 0, 0], - "DURA" => [0, 0, 0, 0, 0], + "CSF" => [2.569, 0.329, 0.058, 1, 0], + "GM" => [1.153, 0.083, 0.069, 0.86, 0], + "WM" => [0.746, 0.070, 0.061, 0.77, 0], + "FAT1" => [0, 0, 0, 0, 0], + "MUSCLE" => [0, 0, 0, 0, 0], + "SKIN/MUSCLE" => [0, 0, 0, 0, 0], + "SKULL" => [0, 0, 0, 0, 0], + "VESSELS" => [0, 0, 0, 0, 0], + "FAT2" => [0, 0, 0, 0, 0], + "DURA" => [0, 0, 0, 0, 0], "MARROW" => [0, 0, 0, 0, 0]) julia> obj = brain_phantom2D(; tissue_properties=phantom_values) julia> plot_phantom_map(obj, :ρ) ``` """ -function brain_phantom2D(; axis="axial", ss=4, us=1, tissue_properties = nothing) +function brain_phantom2D(; axis="axial", ss=4, us=1, tissue_properties = Dict()) # check and filter input # check more spins ssx, ssy, ssz, usx, usy, usz = check_phantom_arguments(2, ss, us) @@ -257,12 +253,12 @@ function brain_phantom2D(; axis="axial", ss=4, us=1, tissue_properties = nothing data = MAT.matread(path * "/phantom/brain2D.mat") # subsample or upsample the phantom data - class = repeat(data[axis][1:ssx:end, 1:ssy:end]; inner=[usx, usy]) + labels = repeat(data[axis][1:ssx:end, 1:ssy:end]; inner=[usx, usy]) # Define spin position vectors Δx = .5e-3 * ssx / usx Δy = .5e-3 * ssy / usy - M, N = size(class) + M, N = size(labels) FOVx = (M - 1) * Δx #[m] FOVy = (N - 1) * Δy #[m] x = (-FOVx / 2):Δx:(FOVx / 2) #spin coordinates @@ -270,10 +266,7 @@ function brain_phantom2D(; axis="axial", ss=4, us=1, tissue_properties = nothing x, y = x .+ y' * 0, x * 0 .+ y' #grid points # Get tissue properties - T1, T2, T2s, ρ, Δw = default_brain_tissue_properties(class, tissue_properties) - T1 = T1 * 1e-3 - T2 = T2 * 1e-3 - T2s = T2s * 1e-3 + T1, T2, T2s, ρ, Δw = default_brain_tissue_properties(labels, tissue_properties) # Define and return the Phantom struct obj = Phantom{Float64}(; @@ -307,7 +300,7 @@ Default ss=4 sample spacing is 2 mm. Original file (ss=1) sample spacing is .5 m - `ss`: (`::Integer or ::Vector{Integer}`, `=4`) subsampling parameter for all axes if scaler, per axis if 3 element vector [ssx, ssy, ssz] - `us`: (`::Integer or ::Vector{Integer}`, `=1`) upsampling parameter for all axes if scaler, per axis if 3 element vector [usx, usy, usz] - `start_end`: (`::Vector{Integer}`, `=[160,200]`) z index range of presampled phantom, 180 is center -- `tissue_properties`: (`::Dict`, `=nothing`) phantom tissue properties in ms and Hz considering the available tissues +- `tissue_properties`: (`::Dict`, `=Dict()`) phantom tissue properties in SI units considering the available tissues # Returns - `obj`: (`::Phantom`) 3D Phantom struct @@ -321,23 +314,23 @@ julia> obj = brain_phantom3D(; us=[2, 2, 1]) julia> phantom_values = Dict( # T1, T2, T2*, ρ, Δw - "CSF" => [2569, 329, 58, 1, 0], - "GM" => [1153, 83, 69, 0.86, 0], - "WM" => [746, 70, 61, 0.77, 0], - "FAT1" => [0, 0, 0, 0, 0], - "MUSCLE" => [0, 0, 0, 0, 0], - "SKIN/MUSCLE" => [0, 0, 0, 0, 0], - "SKULL" => [0, 0, 0, 0, 0], - "VESSELS" => [0, 0, 0, 0, 0], - "FAT2" => [0, 0, 0, 0, 0], - "DURA" => [0, 0, 0, 0, 0], + "CSF" => [2.569, 0.329, 0.058, 1, 0], + "GM" => [1.153, 0.083, 0.069, 0.86, 0], + "WM" => [0.746, 0.070, 0.061, 0.77, 0], + "FAT1" => [0, 0, 0, 0, 0], + "MUSCLE" => [0, 0, 0, 0, 0], + "SKIN/MUSCLE" => [0, 0, 0, 0, 0], + "SKULL" => [0, 0, 0, 0, 0], + "VESSELS" => [0, 0, 0, 0, 0], + "FAT2" => [0, 0, 0, 0, 0], + "DURA" => [0, 0, 0, 0, 0], "MARROW" => [0, 0, 0, 0, 0]) -julia> obj = brain_phantom3D(; tissue_properties=phantom_values) +julia> obj = brain_phantom2D(; tissue_properties=phantom_values) julia> plot_phantom_map(obj, :ρ) ``` """ -function brain_phantom3D(; ss=4, us=1, start_end=[160, 200], tissue_properties=nothing) +function brain_phantom3D(; ss=4, us=1, start_end=[160, 200], tissue_properties=Dict()) # check and filter input ssx, ssy, ssz, usx, usy, usz = check_phantom_arguments(3, ss, us) @@ -346,7 +339,7 @@ function brain_phantom3D(; ss=4, us=1, start_end=[160, 200], tissue_properties=n data = MAT.matread(path * "/phantom/brain3D.mat") # subsample or upsample the phantom data - class = repeat( + labels = repeat( data["data"][1:ssx:end, 1:ssy:end, start_end[1]:ssz:start_end[2]]; inner=[usx, usy, usz], ) @@ -355,7 +348,7 @@ function brain_phantom3D(; ss=4, us=1, start_end=[160, 200], tissue_properties=n Δx = .5e-3 * ssx / usx Δy = .5e-3 * ssy / usy Δz = .5e-3 * ssz / usz - M, N, Z = size(class) + M, N, Z = size(labels) FOVx = (M - 1) * Δx #[m] FOVy = (N - 1) * Δy #[m] FOVz = (Z - 1) * Δz #[m] @@ -365,12 +358,9 @@ function brain_phantom3D(; ss=4, us=1, start_end=[160, 200], tissue_properties=n x = 1 * xx .+ 0 * yy .+ 0 * zz y = 0 * xx .+ 1 * yy .+ 0 * zz z = 0 * xx .+ 0 * yy .+ 1 * zz - + # Get tissue properties - T1, T2, T2s, ρ, Δw = default_brain_tissue_properties(class, tissue_properties) - T1 = T1 * 1e-3 - T2 = T2 * 1e-3 - T2s = T2s * 1e-3 + T1, T2, T2s, ρ, Δw = default_brain_tissue_properties(labels, tissue_properties) # Define and return the Phantom struct obj = Phantom{Float64}(; @@ -554,102 +544,57 @@ function check_phantom_arguments(nd, ss, us) end """ - T1, T2, T2s, ρ, Δw = default_brain_tissue_properties(class, tissue_properties = nothing) + T1, T2, T2s, ρ, Δw = default_brain_tissue_properties(labels, tissue_properties = nothing) -This function returns the default brain tissue properties using a class identifier Matrix +This function returns the default brain tissue properties using a labels identifier Matrix # Arguments -- `class` : (`::Matrix`) the class identifier matrix of the phantom -- `tissue_properties` : (`::Dict`, `=nothing`) phantom tissue properties in ms and Hz considering the available tissues +- `labels` : (`::Matrix`) the labels identifier matrix of the phantom +- `tissue_properties` : (`::Dict`, `=Dict()`) phantom tissue properties in ms and Hz considering the available tissues # Returns -- `T1, T2, T2s, ρ, Δw`: (`::Matrix`) matrices of the same size of class with the tissues properties information +- `T1, T2, T2s, ρ, Δw`: (`::Matrix`) matrices of the same size of labels with the tissues properties information # Examples ```julia-repl -julia> T1, T2, T2s, ρ, Δw = default_brain_tissue_properties(class, tissue_properties) +julia> T1, T2, T2s, ρ, Δw = default_brain_tissue_properties(labels, tissue_properties) -julia> T1, T2, T2s, ρ, Δw = default_brain_tissue_properties(class) +julia> T1, T2, T2s, ρ, Δw = default_brain_tissue_properties(labels) ``` """ -function default_brain_tissue_properties(class, tissue_properties = nothing) - if isnothing(tissue_properties) - # Load default tissue properties - tissue_properties = Dict( - # T1, T2, T2*, ρ, Δw - "CSF" => [2569, 329, 58, 1, 0], - "GM" => [833, 83, 69, 0.86, 0], - "WM" => [500, 70, 61, 0.77, 0], - "FAT1" => [350, 70, 58, 1, -220], - "MUSCLE" => [900, 47, 30, 1, 0], - "SKIN/MUSCLE" => [569, 329, 58, 1, 0], - "SKULL" => [0, 0, 0, 0, 0], - "VESSELS" => [0, 0, 0, 0, 0], - "FAT2" => [500, 70, 61, 0.77, -220], - "DURA" => [2569, 329, 58, 1, 0], - "MARROW" => [500, 70, 61, 0.77, 0]) - # Define spin property vectors +function default_brain_tissue_properties(labels, tissue_properties = Dict()) + # Load default tissue properties + default_properties = Dict( + # T1, T2, T2*, ρ, Δw + "CSF" => [2.569, 0.329, 0.058, 1, 0], + "GM" => [0.833, 0.083, 0.069, 0.86, 0], + "WM" => [0.500, 0.070, 0.061, 0.77, 0], + "FAT1" => [0.350, 0.070, 0.058, 1, -3.84], #-220 Hz + "MUSCLE" => [0.900, 0.047, 0.030, 1, 0], + "SKIN/MUSCLE" => [0.569, 0.329, 0.058, 1, 0], + "SKULL" => [0, 0, 0, 0, 0], + "VESSELS" => [0, 0, 0, 0, 0], + "FAT2" => [0.500, 0.070, 0.061, 0.77, -3.84], #-220 Hz + "DURA" => [2.569, 0.329, 0.058, 1, 0], + "MARROW" => [0.500, 0.070, 0.061, 0.77, 0]) + + tissue_properties = merge(default_properties, tissue_properties) + flat_labels = reshape(labels, :) + properties = zeros(length(flat_labels)..., 5) + for i=1:5 + properties[:, i] = + (flat_labels .== 23) * tissue_properties["CSF"][i] .+ #CSF + (flat_labels .== 46) * tissue_properties["GM"][i] .+ #GM + (flat_labels .== 70) * tissue_properties["WM"][i] .+ #WM + (flat_labels .== 93) * tissue_properties["FAT1"][i] .+ #FAT1 + (flat_labels .== 116) * tissue_properties["MUSCLE"][i] .+ #MUSCLE + (flat_labels .== 139) * tissue_properties["SKIN/MUSCLE"][i] .+ #SKIN/MUSCLE + (flat_labels .== 162) * tissue_properties["SKULL"][i] .+ #SKULL + (flat_labels .== 185) * tissue_properties["VESSELS"][i] .+ #VESSELS + (flat_labels .== 209) * tissue_properties["FAT2"][i] .+ #FAT2 + (flat_labels .== 232) * tissue_properties["DURA"][i] .+ #DURA + (flat_labels .== 255) * tissue_properties["MARROW"][i] #MARROW end - T1 = - (class .== 23) * tissue_properties["CSF"][1] .+ #CSF - (class .== 46) * tissue_properties["GM"][1] .+ #GM - (class .== 70) * tissue_properties["WM"][1] .+ #WM - (class .== 93) * tissue_properties["FAT1"][1] .+ #FAT1 - (class .== 116) * tissue_properties["MUSCLE"][1] .+ #MUSCLE - (class .== 139) * tissue_properties["SKIN/MUSCLE"][1] .+ #SKIN/MUSCLE - (class .== 162) * tissue_properties["SKULL"][1] .+ #SKULL - (class .== 185) * tissue_properties["VESSELS"][1] .+ #VESSELS - (class .== 209) * tissue_properties["FAT2"][1] .+ #FAT2 - (class .== 232) * tissue_properties["DURA"][1] .+ #DURA - (class .== 255) * tissue_properties["MARROW"][1] #MARROW - T2 = - (class .== 23) * tissue_properties["CSF"][2] .+ #CSF - (class .== 46) * tissue_properties["GM"][2] .+ #GM - (class .== 70) * tissue_properties["WM"][2] .+ #WM - (class .== 93) * tissue_properties["FAT1"][2] .+ #FAT1 - (class .== 116) * tissue_properties["MUSCLE"][2] .+ #MUSCLE - (class .== 139) * tissue_properties["SKIN/MUSCLE"][2] .+ #SKIN/MUSCLE - (class .== 162) * tissue_properties["SKULL"][2] .+ #SKULL - (class .== 185) * tissue_properties["VESSELS"][2] .+ #VESSELS - (class .== 209) * tissue_properties["FAT2"][2] .+ #FAT2 - (class .== 232) * tissue_properties["DURA"][2] .+ #DURA - (class .== 255) * tissue_properties["MARROW"][2] #MARROW - T2s = - (class .== 23) * tissue_properties["CSF"][3] .+ #CSF - (class .== 46) * tissue_properties["GM"][3] .+ #GM - (class .== 70) * tissue_properties["WM"][3] .+ #WM - (class .== 93) * tissue_properties["FAT1"][3] .+ #FAT1 - (class .== 116) * tissue_properties["MUSCLE"][3] .+ #MUSCLE - (class .== 139) * tissue_properties["SKIN/MUSCLE"][3] .+ #SKIN/MUSCLE - (class .== 162) * tissue_properties["SKULL"][3] .+ #SKULL - (class .== 185) * tissue_properties["VESSELS"][3] .+ #VESSELS - (class .== 209) * tissue_properties["FAT2"][3] .+ #FAT2 - (class .== 232) * tissue_properties["DURA"][3] .+ #DURA - (class .== 255) * tissue_properties["MARROW"][3] #MARROW + properties = reshape(properties, (size(labels)..., 5)) - ρ = - (class .== 23) * tissue_properties["CSF"][4] .+ #CSF - (class .== 46) * tissue_properties["GM"][4] .+ #GM - (class .== 70) * tissue_properties["WM"][4] .+ #WM - (class .== 93) * tissue_properties["FAT1"][4] .+ #FAT1 - (class .== 116) * tissue_properties["MUSCLE"][4] .+ #MUSCLE - (class .== 139) * tissue_properties["SKIN/MUSCLE"][4] .+ #SKIN/MUSCLE - (class .== 162) * tissue_properties["SKULL"][4] .+ #SKULL - (class .== 185) * tissue_properties["VESSELS"][4] .+ #VESSELS - (class .== 209) * tissue_properties["FAT2"][4] .+ #FAT2 - (class .== 232) * tissue_properties["DURA"][4] .+ #DURA - (class .== 255) * tissue_properties["MARROW"][4] #MARROW - - Δw = - (class .== 23) * tissue_properties["CSF"][5] .+ #CSF - (class .== 46) * tissue_properties["GM"][5] .+ #GM - (class .== 70) * tissue_properties["WM"][5] .+ #WM - (class .== 93) * tissue_properties["FAT1"][5] .+ #FAT1 - (class .== 116) * tissue_properties["MUSCLE"][5] .+ #MUSCLE - (class .== 139) * tissue_properties["SKIN/MUSCLE"][5] .+ #SKIN/MUSCLE - (class .== 162) * tissue_properties["SKULL"][5] .+ #SKULL - (class .== 185) * tissue_properties["VESSELS"][5] .+ #VESSELS - (class .== 209) * tissue_properties["FAT2"][5] .+ #FAT2 - (class .== 232) * tissue_properties["DURA"][5] .+ #DURA - (class .== 255) * tissue_properties["MARROW"][5] #MARROW - return T1, T2, T2s, ρ, Δw + return selectdim(properties, ndims(properties), 1), selectdim(properties, ndims(properties), 2), selectdim(properties, ndims(properties), 3), selectdim(properties, ndims(properties), 4), selectdim(properties, ndims(properties), 5) end \ No newline at end of file From 5b6e25d2517973e9d52ce30f004337f3c54d3e21 Mon Sep 17 00:00:00 2001 From: Guillermo Date: Fri, 4 Oct 2024 12:09:30 -0300 Subject: [PATCH 08/14] Avoiding flat arrays. --- KomaMRIBase/src/datatypes/Phantom.jl | 33 ++++++++++++++-------------- 1 file changed, 16 insertions(+), 17 deletions(-) diff --git a/KomaMRIBase/src/datatypes/Phantom.jl b/KomaMRIBase/src/datatypes/Phantom.jl index b52e8c7e3..00e3a3d62 100644 --- a/KomaMRIBase/src/datatypes/Phantom.jl +++ b/KomaMRIBase/src/datatypes/Phantom.jl @@ -47,7 +47,7 @@ julia> obj.ρ Dθ::AbstractVector{T} = zeros(eltype(x), size(x)) #Diff::Vector{DiffusionModel} #Diffusion map #Motion - motion::AbstractMotion{T} = NoMotion{eltype(x)}() + motion::AbstractMotion{T} = NoMotion{eltype(x)}() end const NON_STRING_PHANTOM_FIELDS = Iterators.filter(x -> fieldtype(Phantom, x) != String, fieldnames(Phantom)) @@ -578,23 +578,22 @@ function default_brain_tissue_properties(labels, tissue_properties = Dict()) "MARROW" => [0.500, 0.070, 0.061, 0.77, 0]) tissue_properties = merge(default_properties, tissue_properties) - flat_labels = reshape(labels, :) - properties = zeros(length(flat_labels)..., 5) + properties = [] for i=1:5 - properties[:, i] = - (flat_labels .== 23) * tissue_properties["CSF"][i] .+ #CSF - (flat_labels .== 46) * tissue_properties["GM"][i] .+ #GM - (flat_labels .== 70) * tissue_properties["WM"][i] .+ #WM - (flat_labels .== 93) * tissue_properties["FAT1"][i] .+ #FAT1 - (flat_labels .== 116) * tissue_properties["MUSCLE"][i] .+ #MUSCLE - (flat_labels .== 139) * tissue_properties["SKIN/MUSCLE"][i] .+ #SKIN/MUSCLE - (flat_labels .== 162) * tissue_properties["SKULL"][i] .+ #SKULL - (flat_labels .== 185) * tissue_properties["VESSELS"][i] .+ #VESSELS - (flat_labels .== 209) * tissue_properties["FAT2"][i] .+ #FAT2 - (flat_labels .== 232) * tissue_properties["DURA"][i] .+ #DURA - (flat_labels .== 255) * tissue_properties["MARROW"][i] #MARROW + temp = + (labels .== 23) * tissue_properties["CSF"][i] .+ #CSF + (labels .== 46) * tissue_properties["GM"][i] .+ #GM + (labels .== 70) * tissue_properties["WM"][i] .+ #WM + (labels .== 93) * tissue_properties["FAT1"][i] .+ #FAT1 + (labels .== 116) * tissue_properties["MUSCLE"][i] .+ #MUSCLE + (labels .== 139) * tissue_properties["SKIN/MUSCLE"][i] .+ #SKIN/MUSCLE + (labels .== 162) * tissue_properties["SKULL"][i] .+ #SKULL + (labels .== 185) * tissue_properties["VESSELS"][i] .+ #VESSELS + (labels .== 209) * tissue_properties["FAT2"][i] .+ #FAT2 + (labels .== 232) * tissue_properties["DURA"][i] .+ #DURA + (labels .== 255) * tissue_properties["MARROW"][i] #MARROW + push!(properties, temp) end - properties = reshape(properties, (size(labels)..., 5)) - return selectdim(properties, ndims(properties), 1), selectdim(properties, ndims(properties), 2), selectdim(properties, ndims(properties), 3), selectdim(properties, ndims(properties), 4), selectdim(properties, ndims(properties), 5) + return properties end \ No newline at end of file From 3689518cb8f864c054316dafb0b4f0a833e59621 Mon Sep 17 00:00:00 2001 From: Guillermo Sahonero Alvarez Date: Sat, 9 Nov 2024 20:39:15 -0300 Subject: [PATCH 09/14] Update Phantom.jl --- KomaMRIBase/src/datatypes/Phantom.jl | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/KomaMRIBase/src/datatypes/Phantom.jl b/KomaMRIBase/src/datatypes/Phantom.jl index 00e3a3d62..882a9e376 100644 --- a/KomaMRIBase/src/datatypes/Phantom.jl +++ b/KomaMRIBase/src/datatypes/Phantom.jl @@ -47,7 +47,7 @@ julia> obj.ρ Dθ::AbstractVector{T} = zeros(eltype(x), size(x)) #Diff::Vector{DiffusionModel} #Diffusion map #Motion - motion::AbstractMotion{T} = NoMotion{eltype(x)}() + motion::Union{NoMotion, MotionList{T}} = NoMotion() end const NON_STRING_PHANTOM_FIELDS = Iterators.filter(x -> fieldtype(Phantom, x) != String, fieldnames(Phantom)) @@ -596,4 +596,4 @@ function default_brain_tissue_properties(labels, tissue_properties = Dict()) end return properties -end \ No newline at end of file +end From 9cc9d2956d533df4d2b3d0b02d03b010ae68b120 Mon Sep 17 00:00:00 2001 From: Guillermo Sahonero Alvarez Date: Fri, 15 Nov 2024 14:05:58 -0300 Subject: [PATCH 10/14] Update KomaMRIBase/src/datatypes/Phantom.jl Co-authored-by: Carlos Castillo Passi --- KomaMRIBase/src/datatypes/Phantom.jl | 1 - 1 file changed, 1 deletion(-) diff --git a/KomaMRIBase/src/datatypes/Phantom.jl b/KomaMRIBase/src/datatypes/Phantom.jl index 8869ac348..3dfe620a1 100644 --- a/KomaMRIBase/src/datatypes/Phantom.jl +++ b/KomaMRIBase/src/datatypes/Phantom.jl @@ -333,7 +333,6 @@ julia> plot_phantom_map(obj, :ρ) function brain_phantom3D(; ss=4, us=1, start_end=[160, 200], tissue_properties=Dict()) # check and filter input ssx, ssy, ssz, usx, usy, usz = check_phantom_arguments(3, ss, us) - # Get data from .mat file path = @__DIR__ data = MAT.matread(path * "/phantom/brain3D.mat") From 1b9aa93d84431389fff10c7e6b06a5c1a4653d8b Mon Sep 17 00:00:00 2001 From: Guillermo Sahonero Alvarez Date: Fri, 15 Nov 2024 14:06:42 -0300 Subject: [PATCH 11/14] Update KomaMRIBase/src/datatypes/Phantom.jl Co-authored-by: Carlos Castillo Passi --- KomaMRIBase/src/datatypes/Phantom.jl | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/KomaMRIBase/src/datatypes/Phantom.jl b/KomaMRIBase/src/datatypes/Phantom.jl index 3dfe620a1..a3a64e979 100644 --- a/KomaMRIBase/src/datatypes/Phantom.jl +++ b/KomaMRIBase/src/datatypes/Phantom.jl @@ -325,7 +325,7 @@ julia> phantom_values = "FAT2" => [0, 0, 0, 0, 0], "DURA" => [0, 0, 0, 0, 0], "MARROW" => [0, 0, 0, 0, 0]) -julia> obj = brain_phantom2D(; tissue_properties=phantom_values) +julia> obj = brain_phantom3D(; tissue_properties=phantom_values) julia> plot_phantom_map(obj, :ρ) ``` From bd6b91ec29e29f430abff21f49bd9996ff08c5d5 Mon Sep 17 00:00:00 2001 From: Guillermo Sahonero Alvarez Date: Fri, 15 Nov 2024 14:06:54 -0300 Subject: [PATCH 12/14] Update KomaMRIBase/src/datatypes/Phantom.jl Co-authored-by: Carlos Castillo Passi --- KomaMRIBase/src/datatypes/Phantom.jl | 1 - 1 file changed, 1 deletion(-) diff --git a/KomaMRIBase/src/datatypes/Phantom.jl b/KomaMRIBase/src/datatypes/Phantom.jl index a3a64e979..ba3c495ae 100644 --- a/KomaMRIBase/src/datatypes/Phantom.jl +++ b/KomaMRIBase/src/datatypes/Phantom.jl @@ -245,7 +245,6 @@ julia> plot_phantom_map(obj, :ρ) """ function brain_phantom2D(; axis="axial", ss=4, us=1, tissue_properties = Dict()) # check and filter input - # check more spins ssx, ssy, ssz, usx, usy, usz = check_phantom_arguments(2, ss, us) # Get data from .mat file From f55d3874578079d1d9599920f198a15d8b0befec Mon Sep 17 00:00:00 2001 From: Guillermo Sahonero Alvarez Date: Fri, 15 Nov 2024 14:07:09 -0300 Subject: [PATCH 13/14] Update KomaMRIBase/src/datatypes/Phantom.jl Co-authored-by: Carlos Castillo Passi --- KomaMRIBase/src/datatypes/Phantom.jl | 1 - 1 file changed, 1 deletion(-) diff --git a/KomaMRIBase/src/datatypes/Phantom.jl b/KomaMRIBase/src/datatypes/Phantom.jl index ba3c495ae..ef9c82261 100644 --- a/KomaMRIBase/src/datatypes/Phantom.jl +++ b/KomaMRIBase/src/datatypes/Phantom.jl @@ -195,7 +195,6 @@ function heart_phantom(; return phantom end - """ phantom = brain_phantom2D(;axis="axial", ss=4) From ecc9bbeaa1e060a48a163bc362e493bc955a4ed5 Mon Sep 17 00:00:00 2001 From: Guillermo Sahonero Alvarez Date: Fri, 15 Nov 2024 14:07:18 -0300 Subject: [PATCH 14/14] Update KomaMRIBase/src/datatypes/Phantom.jl Co-authored-by: Carlos Castillo Passi --- KomaMRIBase/src/datatypes/Phantom.jl | 1 - 1 file changed, 1 deletion(-) diff --git a/KomaMRIBase/src/datatypes/Phantom.jl b/KomaMRIBase/src/datatypes/Phantom.jl index ef9c82261..7a26f2c50 100644 --- a/KomaMRIBase/src/datatypes/Phantom.jl +++ b/KomaMRIBase/src/datatypes/Phantom.jl @@ -406,7 +406,6 @@ function pelvis_phantom2D(; ss=4, us=1) # subsample or upsample the phantom data class = repeat(data["pelvis3D_slice"][1:ssx:end, 1:ssy:end]; inner=[usx, usy]) - # Define spin position vectors Δx = .5e-3 * ssx / usx Δy = .5e-3 * ssy / usy