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

Get initial traits in place for NeuroCore #1

Merged
merged 15 commits into from
Dec 27, 2019
Merged
Show file tree
Hide file tree
Changes from 1 commit
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Prev Previous commit
Next Next commit
Add tests and fix enums
* Have basics tests for most BIDS entitites now
* Enums mapping between for directions b/c many variations:
- actual numeric values used in calculating direction are
 positive and negative integers.
- strings are commonly used in JSON files
- named directions are derived from this and stored in axes
  • Loading branch information
Tokazama committed Dec 20, 2019
commit 7d85781bfced2df717b72a111ff2efa6f2763d2a
6 changes: 4 additions & 2 deletions src/NeuroCore.jl
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
module NeuroCore

using ImageCore, ImageAxes, Unitful, ImageMetadata, Markdown #CoordinateTransformations, Rotations
using ImageCore, ImageAxes, Unitful, ImageMetadata, Markdown

export NeuroMetadata,
NeuroMetaArray,
Expand All @@ -27,7 +27,9 @@ const F64Hz = typeof(OneF64Hz)
const OneIntDeg = 1.0u"°"
const IntDeg = typeof(OneIntDeg)

#include("enums.jl")
const CoordinateList = Dict{Symbol,NTuple{3,Float64}}

include("enums.jl")
include("properties.jl")
include("coordinates.jl")
include("bids_entities.jl")
Expand Down
67 changes: 31 additions & 36 deletions src/bids_entities.jl
Original file line number Diff line number Diff line change
Expand Up @@ -10,15 +10,15 @@ AcquisitionDuration(x) = getter(x, :AcquisitionDuration, F64Sec, i -> 1.0u"s")
AcquisitionDuration!(x, val) = setter!(x, :AcquisitionDuration, F64Sec, val)

"""
AnatomicalLandmarkCoordinates(x) -> Dict{String,NTuple{3,Float64}}
AnatomicalLandmarkCoordinates(x) -> CoordinateList

Key:value pairs of any number of additional anatomical landmarks and their coordinates
in voxel units (where first voxel has index 0,0,0) relative to the associated anatomical
MRI, (e.g. Dict("AC" => (127.0,119.0,149.0), "PC"=> (128.0,93.0,141.0),
"IH"=> (131.0114.0,206.0)).
MRI, (e.g. Dict(:AC => (127.0,119.0,149.0), :PC=> (128.0,93.0,141.0),
:IH=> (131.0114.0,206.0)).
"""
AnatomicalLandmarkCoordinates(x) = getter(x, :AnatomicalLandmarkCoordinates, Dict{String,NTuple{3,Float64}}, i -> Dict{String,NTuple{3,Float64}}())
AnatomicalLandmarkCoordinates!(x, val) = setter!(x, :AnatomicalLandmarkCoordinates, Dict{String,NTuple{3,Float64}}, val)
AnatomicalLandmarkCoordinates(x) = getter(x, :AnatomicalLandmarkCoordinates, CoordinateList, i -> CoordinateList())
AnatomicalLandmarkCoordinates!(x, val) = setter!(x, :AnatomicalLandmarkCoordinates, CoordinateList, val)

"""
AnatomicalLandmarkCoordinateSystem(x)
Expand Down Expand Up @@ -53,7 +53,7 @@ combination, but other methods exist. The image reconstruction is changed by
the coil combination method (as for the matrix coil mode above), so anything
non-standard should be reported.
"""
CoilCombinationMethod(x) = getter(x, :CoilCombinationMethod, String, i -> "")
CoilCombinationMethod(x) = getter(x, :CoilCombinationMethod, String, i -> "rSOS")
CoilCombinationMethod!(x, val) = getter(x, :CoilCombinationMethod, String, val)

"""
Expand All @@ -78,8 +78,8 @@ CogPOID!(x, val) = getter(x, :CogPOID, String, val)
Return active ingredient of constrast agent. See [`ContrastIngrediant`](@ref) for
more details.
"""
ContrastBolusIngredient(x) = getter(x, :ContrastBolusIngredient, String, i -> "")
ContrastBolusIngredient!(x, val) = setter!(x, :ContrastBolusIngredient, String, val)
ContrastBolusIngredient(x) = getter(x, :ContrastBolusIngredient, ContrastIngrediant, i -> UnkownContrast)
ContrastBolusIngredient!(x, val) = setter!(x, :ContrastBolusIngredient, ContrastIngrediant, val)

"""
DelayTime(x) -> F64Sec
Expand Down Expand Up @@ -213,17 +213,8 @@ Boolean field to specify if electrical stimulation was done during the recording
(options are `true` or `false`). Parameters for event-like stimulation should be
specified in the _events.tsv file.
"""
ElectricalStimulation(x) = getter(x, :ElectricalStimulationParameters, Bool, i -> false)
ElectricalStimulation!(x, val) = setter!(x, :ElectricalStimulationParameters, Bool, val)

"""
EpochLength(x) -> F64Sec

Duration of individual epochs in seconds (e.g., 1) in case of epoched data. If
recording was continuous or discontinuous, leave out the field.
"""
EpochLength(x) = getter(x, :EpochLength, F64Sec, OneF64Sec)
EpochLength!(x, val) = getter(x, :EpochLength, F64Sec, val)
ElectricalStimulation(x) = getter(x, :ElectricalStimulation, Bool, i -> false)
ElectricalStimulation!(x, val) = setter!(x, :ElectricalStimulation, Bool, val)

"""
ElectricalStimulationParameters(x) -> String
Expand All @@ -235,6 +226,15 @@ described here in freeform text.
ElectricalStimulationParameters(x) = getter(x, :ElectricalStimulationParameters, String, i -> "")
ElectricalStimulationParameters!(x, val) = setter!(x, :ElectricalStimulationParameters, String, val)

"""
EpochLength(x) -> F64Sec

Duration of individual epochs in seconds (e.g., 1) in case of epoched data. If
recording was continuous or discontinuous, leave out the field.
"""
EpochLength(x) = getter(x, :EpochLength, F64Sec, OneF64Sec)
EpochLength!(x, val) = getter(x, :EpochLength, F64Sec, val)

"""
FiducialDescription(x) -> String

Expand All @@ -249,12 +249,12 @@ FiducialDescription(x) = getter(x, :FiducialDescription, String, i -> "")
FiducialDescription!(x, val) = setter!(x, :FiducialDescription, String, val)

"""
FlipAngle(x) -> Int
FlipAngle(x) -> IntDeg

Returns the flip angle for the acquisition in degrees.
"""
FlipAngle(x) = getter(x, :FlipAngle, Int, i -> 0)
FlipAngle!(x, val) = setter!(x, :FlipAngle, Int, val)
FlipAngle(x) = getter(x, :FlipAngle, IntDeg, i -> OneIntDeg)
FlipAngle!(x, val) = setter!(x, :FlipAngle, IntDeg, val)

"""
GradientSetType(x) -> String
Expand All @@ -268,7 +268,7 @@ GradientSetType(x) = getter(x, :GradientSetType, String, i -> "")
GradientSetType!(x, val) = setter!(x, :GradientSetType, String, val)

"""
HeadCoilCoordinates(x) -> Dict{String,NTuple{3,Float64}}
HeadCoilCoordinates(x) -> CoordinateList

Key:value pairs describing head localization coil labels and their coordinates,
interpreted following the HeadCoilCoordinateSystem, e.g., {NAS: [12.7,21.3,13.9],
Expand All @@ -277,8 +277,8 @@ at locations that have a known anatomical name (e.g. for Elekta, Yokogawa system
in that case generic labels can be used (e.g. {coil1: [12.2,21.3,12.3],
coil2: [6.7,12.3,8.6], coil3: [21.9,11.0,8.1]} ).
"""
HeadCoilCoordinates(x) = getter(x, :HeadCoilCoordinates, Dict{String,NTuple{3,Float64}}, i -> Dict{String,NTuple{3,Float64}}())
HeadCoilCoordinates!(x, val) = setter!(x, :HeadCoilCoordinates, Dict{String,NTuple{3,Float64}}, val)
HeadCoilCoordinates(x) = getter(x, :HeadCoilCoordinates, CoordinateList, i -> CoordinateList())
HeadCoilCoordinates!(x, val) = setter!(x, :HeadCoilCoordinates, CoordinateList, val)

"""
HeadCoilCoordinateSystem(x) -> CoordinateSystem
Expand Down Expand Up @@ -510,17 +510,12 @@ PartialFourierDirection!(x, val) = setter!(x, :PartialFourierDirection, String,

Returns the phase encoding direction.

Possible values: `i`, `j`, `k`, `i-`, `j-`, `k-`. The letters `i`, `j`, `k`
correspond to the first, second and third axis of the data in the NIFTI file.
The polarity of the phase encoding is assumed to go from zero index to maximum
index unless `-` sign is present (then the order is reversed - starting from
the highest index instead of zero). `PhaseEncodingDirection` is defined as the
direction along which phase is was modulated which may result in visible
distortions. Note that this is not the same as the DICOM term
`InPlanePhaseEncodingDirectiong` which can have `ROW` or `COL` values. This
parameter is REQUIRED if corresponding fieldmap data is present or when using
multiple runs with different phase encoding directions (which can be later used
for field inhomogeneity correction).
`PhaseEncodingDirection` is defined as the direction along which phase is was
modulated which may result in visible distortions. Note that this is not the
same as the DICOM term `InPlanePhaseEncodingDirectiong` which can have `ROW` or
`COL` values. This parameter is REQUIRED if corresponding fieldmap data is present
or when using multiple runs with different phase encoding directions (which can
be later used for field inhomogeneity correction).
"""
PhaseEncodingDirection(x) = EncodingDirection(phasedim(x))
PhaseEncodingDirection!(x, val) = slicedim(x, val)
Expand Down
1 change: 1 addition & 0 deletions src/coordinates.jl
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@

struct CoordinateSystem{S} end

"""
Expand Down
38 changes: 30 additions & 8 deletions src/enums.jl
Original file line number Diff line number Diff line change
Expand Up @@ -10,14 +10,35 @@ to the slice with the largest index, and the final entry corresponds to slice
index zero.
"""
@enum EncodingDirection begin
i = 1
in = -1
j = 2
jn = -2
k = 3
kn = -3
ipos = 1
ineg = -1
jpos = 2
jneg = -2
kpos = 3
kneg = -3
end

EncodingDirection(e::AbstractString) = EncodingDirection(Symbol(e))
function EncodingDirection(e::Symbol)
if e === Symbol("i")
return ipos
elseif e === Symbol("i-")
return ineg
elseif e === Symbol("j+")
return jpos
elseif e === Symbol("j-")
return jneg
elseif e === Symbol("k+")
return kpos
elseif e === Symbol("k-")
return kneg
else
error("$e is not a supported encoding direction.")
end
end

Base.String(e::EncodingDirection) = String(Symbol(e))

"""
ContrastIngrediant

Expand All @@ -28,6 +49,7 @@ An enumerable type with the following possible values:
* `DIOXIDE`
* `BARIUM`
* `XENON`
* `UnkownContrast`
"""
@enum ContrastIngrediant begin
IODINE
Expand All @@ -36,6 +58,7 @@ An enumerable type with the following possible values:
DIOXIDE
BARIUM
XENON
UnkownContrast
end
ContrastIngrediant(i::AbstractString) = ContrastIngrediant(Symbol(i))
function ContrastIngrediant(i::Symbol)
Expand All @@ -52,8 +75,7 @@ function ContrastIngrediant(i::Symbol)
elseif i === :XENON
return XENON
else
error("$i is not a supported contrast ingredient. See ContrastIngrediant
for supported ingrediants.")
return UnkownContrast
end
end
Base.String(i::ContrastIngrediant) = String(Symbol(i))
93 changes: 93 additions & 0 deletions test/bids_entities.jl
Original file line number Diff line number Diff line change
@@ -0,0 +1,93 @@

const CLEXAMPLE = CoordinateList(:AC => (127.0,119.0,149.0), :PC=> (128.0,93.0,141.0), :IH=> (131.0114.0,206.0))

img = NeuroMetaArray(rand(4,4))
props = NeuroMetadata()

for (property, default, valin, valout) in (
(:AcquisitionDuration, 1.0u"s", 2.0u"s", 2.0u"s"),
(:AnatomicalLandmarkCoordinates, CoordinateList(), CLEXAMPLE, CLEXAMPLE),
#:AnatomicalLandmarkCoordinateSystem,
#:AnatomicalLandmarkCoordinateUnits,
#:AnatomicalLandmarkCoordinateSystemDescription,
#:CogAtlasID,
#:CogPOID,
(:CoilCombinationMethod, "rSOS", "", ""),
(:ContrastBolusIngredient, UnkownContrast, IODINE, IODINE),
(:ContrastBolusIngredient, UnkownContrast, "IODINE", IODINE),
(:DelayAfterTrigger, 1.0u"s", 2.0u"s", 2.0u"s"),
(:DelayTime, 1.0u"s", 2.0u"s", 2.0u"s"),
(:DeviceSerialNumber, "00000", "11111", "11111")A,
(:DewarPosition, OneIntDeg, 2, 2u"°"),
(:DwellTime, 1.0u"s", 2.0u"s", 2.0u"s"),
(:EchoTime, 1.0u"s", 2.0u"s", 2.0u"s"),
(:EffectiveEchoSpacing, 1.0u"s", 2.0u"s", 2.0u"s"),
(:EEGGround, "", "mastoid", "mastoid"),
(:EEGPlacementScheme, "", "surface strip and STN depth", "surface strip and STN depth"),
(:ElectricalStimulation, false, true, true),
(:ElectricalStimulationParameters, "", "x", "x"),
(:EpochLength, 1.0u"s", 2.0u"s", 2.0u"s"),
(:FiducialDescription, "", "pre-auricular","pre-auricular"),
(:FlipAngle, OneIntDeg, 2, 2u"°"),
(:GradientSetType, "", "gradient", "gradient"),
(:HeadCoilCoordinates, CoordinateList(), CLEXAMPLE, CLEXAMPLE),
#:HeadCoilCoordinateSystem,
#:HeadCoilCoordinateSystemDescription,
#:HeadCoilCoordinateUnits,
(:InstitutionName, "", "x", "x"),
(:InstitutionAddress, "", "x", "x"),
(:InstitutionalDepartmentName, "", "x", "x"),
(:Instructions, "", "x", "x"),
(:IntendedFor, "", "x", "x"),
(:InversionTime, 1.0u"s", 2.0u"s", 2.0u"s"),
(:MagneticFieldStrength, 3.0u"T", 1.5u"T", 1.5u"T"),
(:Manufacturer, "", "x", "x"),
(:Manufacturer_model_name, "", "x", "x"),
(:MatrixCoilMode, "", "x", "x"),
(:MRTransmitCoilSequence, "", "x", "x"),
(:MultibandAccelerationFactor, "", "x", "x"),
(:NegativeContrast, false, true, true),
(:NumberOfVolumesDiscardedByScanner, 0, 1, 1),
(:NumberOfVolumesDiscardedByUser, 0, 1, 1),
(:NumberShots, 0, 1, 1),
(:NonlinearGradientCorrection, false, true, true),
(:ParallelAcquisitionTechnique, "", "x", "x"),
(:ParallelReductionFactor, 0, 1, 1),
(:PartialFourier, 1.0, 2.0, 2.0),
(:PartialFourierDirection, "", "x", "x"),
(:PhaseEncodingDirection, i_direction, 2, j_direction),
(:PowerLineFrequency, 1u"Hz", 2u"Hz", 2u"Hz"),
(:PulseSequence, "", "x", "x"),
(:PulseSequenceDetails, "", "x", "x"),
(:PulseSequenceType, "", "x", "x"),
(:ReceiveCoilActiveElements, "", "x", "x"),
(:ReceiveCoilName, "", "x", "x"),
(:RecordingDuration, 1u"s", 2u"s", 2u"s"),
(:RecordingType, "", "x", "x"),
(:RepetitionTime, 1u"s", 2u"s", 2u"s"),
(:SamplingFrequency, 1u"Hz", 2u"Hz", 2u"Hz"),
(:ScanOptions, "", "x", "x"),
(:ScanningSequence, "", "x", "x"),
(:SequenceName, "", "x", "x"),
(:SequenceVarient, "", "x", "x"),
(:SubjectArtefactDescription, "", "x", "x"),
(:SliceEncodingDirection, i_direction, 2, j_direction),
(:SliceTiming, 1u"s", 2u"s", 2u"s"),
(:SoftwareVersions, "", "x", "x"),
(:StationName, "", "x", "x"),
(:TaskDescription, "", "x", "x"),
(:TotalReadoutTime, 1u"s", 2u"s", 2u"s"),
(:VolumeTiming, 1u"s", 2u"s", 2u"s"),
)
@testset "$property" begin
@test @inferred(getproperty(img, property)) == default
@test @inferred(getproperty(props, property)) == default
if !in(NeuroCore.NO_SET_PROPERTIES)
setproperty!(img, property, valin)
setproperty!(props, property, valin)
@test @inferred(getproperty(img, property)) == valout
@test @inferred(getproperty(props, property)) == valout
end
end
end

18 changes: 18 additions & 0 deletions test/runtests.jl
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
using NeuroCore, Test, Unitful


using NeuroCore: F64Sec, F64Tesla, F64Hz, IntDeg, OneF64Sec, OneF64Tesla, OneF64Hz, OneIntDeg

include("bids_entities.jl")

#= TODO
:stream_offset,
:auxfiles,
:srcfile,
:calmax,
:calmin,
:freqdim,
:phasedim,
:slicedim,
:slice_start,
=#