Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Custom values for brain_phantom #484

Open
wants to merge 12 commits into
base: master
Choose a base branch
from
265 changes: 152 additions & 113 deletions KomaMRIBase/src/datatypes/Phantom.jl
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand All @@ -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}
cncastillo marked this conversation as resolved.
Show resolved Hide resolved
name::String = "spins"
x :: AbstractVector{T}
y::AbstractVector{T} = zeros(eltype(x), size(x))
Expand All @@ -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
cncastillo marked this conversation as resolved.
Show resolved Hide resolved
end

const NON_STRING_PHANTOM_FIELDS = Iterators.filter(x -> fieldtype(Phantom, x) != String, fieldnames(Phantom))
Expand Down Expand Up @@ -195,6 +199,7 @@ function heart_phantom(;
return phantom
end


"""
phantom = brain_phantom2D(;axis="axial", ss=4)

Expand All @@ -212,7 +217,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
gsahonero marked this conversation as resolved.
Show resolved Hide resolved

# Returns
- `obj`: (`::Phantom`) Phantom struct
Expand All @@ -223,11 +228,28 @@ julia> obj = brain_phantom2D(; axis="sagittal", ss=1)

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],
"MARROW" => [0, 0, 0, 0, 0])
julia> obj = brain_phantom2D(; tissue_properties=phantom_values)
gsahonero marked this conversation as resolved.
Show resolved Hide resolved

julia> plot_phantom_map(obj, :ρ)
```
"""
function brain_phantom2D(; axis="axial", ss=4, us=1)
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)

# Get data from .mat file
Expand All @@ -247,59 +269,8 @@ 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
# 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
Expand Down Expand Up @@ -336,6 +307,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

# Returns
- `obj`: (`::Phantom`) 3D Phantom struct
Expand All @@ -346,13 +318,29 @@ julia> obj = brain_phantom3D(; ss=5)

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],
"MARROW" => [0, 0, 0, 0, 0])
julia> obj = brain_phantom3D(; tissue_properties=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], 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")
Expand All @@ -377,60 +365,9 @@ function brain_phantom3D(; ss=4, us=1, start_end=[160, 200])
x = 1 * xx .+ 0 * yy .+ 0 * zz
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

# 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
Expand Down Expand Up @@ -482,7 +419,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
Expand Down Expand Up @@ -612,5 +549,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)
gsahonero marked this conversation as resolved.
Show resolved Hide resolved
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
gsahonero marked this conversation as resolved.
Show resolved Hide resolved
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
gsahonero marked this conversation as resolved.
Show resolved Hide resolved
Loading