Skip to content
This repository has been archived by the owner on May 18, 2021. It is now read-only.

Rt/issue75 shim specs json #92

Draft
wants to merge 24 commits into
base: master
Choose a base branch
from
Draft
Show file tree
Hide file tree
Changes from 7 commits
Commits
Show all changes
24 commits
Select commit Hold shift + click to select a range
4409f83
add +img.read_nii.m from dev branch rt/+img
rtopfer May 22, 2020
fedca0d
Merge branch 'master' into rt/issue75-ShimSpecs-json
rtopfer May 25, 2020
a78565d
REF,DOC: Replace getnactivechannels() method with nChannels dependent…
rtopfer May 26, 2020
386664f
BUG: Replace calls to ShimUse.customdisplay (deprecated) with fprintf
rtopfer May 26, 2020
0089d5c
BUG: correct variable name
rtopfer May 26, 2020
fce7622
REF: Enable ShimSpecs object as optional input to ShimOpt() constructor
rtopfer May 26, 2020
04b75c3
NEW: +b0shim/+coils/+greg package, containing specs.json (converted f…
rtopfer May 26, 2020
b00fd74
NEW: package +valid for generic validation functions; add mustBeA.m—c…
rtopfer May 26, 2020
b88c7cf
NEW: b0shim.Specs class to create ShimSpecs object from json file (pa…
rtopfer May 26, 2020
2dbcb66
NEW: draft save_json() method in b0shim.Specs
rtopfer May 26, 2020
19f8527
DOC: b0shim.Specs reformat
rtopfer Jun 4, 2020
a6ad608
BUG: ShimUse.m : Replace curly brackets with parentheses
rtopfer Jun 8, 2020
93298ab
Merge branch 'master' into rt/issue75-ShimSpecs-json
rtopfer Jun 10, 2020
dfda2d4
DEL: +img folder (renamed on master to +imutils)
rtopfer Jun 11, 2020
89d7ebf
REF: Minor changes to Shim -Com,-Specs and -Opt classes for _Greg/_re…
rtopfer Jun 12, 2020
ce9a8b4
NEW: +parts classes to form Shim Specs config: Channel.m and Port.m
rtopfer Jun 15, 2020
091800e
REF: b0shim.Specs properties now include +parts.Channel, .Port
rtopfer Jun 15, 2020
02c45e2
DOC,NEW: +b0shim.parts (class docs) and Contents.m (package doc)
rtopfer Jun 15, 2020
f60aa6f
NEW: read_/write_json standalone functions added to ./misc
rtopfer Jun 16, 2020
dc54fb5
DOC: +b0shim/+parts/ Channel.m and Port.m
rtopfer Jun 16, 2020
9404bbe
REF, DOC: +b0shim.Specs simplify (move json I/O to standalone functio…
rtopfer Jun 16, 2020
0209c5e
REF: Rename Specs --> Config (the class bears little semblance to the…
rtopfer Jun 16, 2020
28c75ab
BUG: Add Parity property to b0shim.parts.Port
rtopfer Jun 17, 2020
c8224e2
NEW,DOCS: add example configuration scripts and documentation
rtopfer Jun 17, 2020
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
81 changes: 81 additions & 0 deletions +b0shim/+coils/+greg/specs.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,81 @@
{
"Adc": {
"mVPerAdcCount": 2
},
"Id": {
"systemName": "Greg",
"channelNames": [
"Ch1",
"Ch2",
"Ch3",
"Ch4",
"Ch5",
"Ch6",
"Ch7",
"Ch8"
],
"channelUnits": [
"[A]",
"[A]",
"[A]",
"[A]",
"[A]",
"[A]",
"[A]",
"[A]"
]
},
"Amp": {
"maxCurrentPerChannel": [
2.5,
2.5,
2.5,
2.5,
2.5,
2.5,
2.5,
2.5
],
"maxCurrentPerBank": 20,
"nChannels": 8,
"nActiveChannels": 8,
"maxVoltagePerChannel": 2500,
"staticChannels": [
true,
true,
true,
true,
true,
true,
true,
true
],
"dynamicChannels": [
true,
true,
true,
true,
true,
true,
true,
true
]
},
"Com": {
"baudRate": 115200,
"readTimeout": 500,
"dataBits": 8,
"stopBits": 1,
"flowControl": "NONE",
"parity": "NONE",
"byteOrder": "bigEndian",
"txRxDelay": 0.005,
"updatePeriod": 0.1
},
"Dac": {
"resolution": 8,
"maxCurrent": 5,
"referenceVoltage": 1250,
"maximum": 26214
}
}
37 changes: 37 additions & 0 deletions +img/read_nii.m
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
function [img,info,json] = read_nii( niiFile )
%READ_NII Load NIfTI image and header; and, when present, the accompanying .json sidecar
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

not sure if it's of any help, but for the records here is how SPM deals with BIDS: https://en.wikibooks.org/wiki/SPM/BIDS

%
% [img, info] = read_nii( niiFile )
% [img, info, json] = read_nii( niiFile )
%
% The input `niiFile` is the path to the NIfTI image as a string scalar or
% character vector. When called with 2 output arguments, the function is
% equivalent short-hand for
%
% info = niftiinfo( niiFile ); img = niftiread( info )
%
% When called with the 3rd output argument, the function checks the parent
% folder of `niiFile` for an identically named file but with a .json file extension.
% When such a file is present, the 3rd output is returned as a struct via
% `json = jsondecode( fileread( jsonFile ) );` otherwise, `json = []`.
arguments
niiFile(1,:) string {mustBeStringScalarOrCharVector, mustBeFile} ;
end

info = niftiinfo( niiFile );
img = niftiread( info );

if nargout < 3
return;
end

[folder, name] = fileparts( niiFile );
jsonFile = fullfile( folder, name + ".json" );

if isfile( jsonFile )
json = jsondecode( fileread( jsonFile ) );
else
json = [];
end

end
11 changes: 8 additions & 3 deletions Coils/Shim_Greg/ShimOpt_Greg.m
Original file line number Diff line number Diff line change
Expand Up @@ -24,11 +24,16 @@
Shim.Field = [] ;
Shim.Model = [] ;
Shim.Aux = [] ;
Shim.System.Specs = ShimSpecs_Greg() ;
Shim.System.currents = zeros( Shim.System.Specs.Amp.nActiveChannels, 1 ) ;

[ Field, Params, Specs] = ShimOpt.parseinput( varargin ) ;

if isempty(Specs)
Shim.System.Specs = ShimSpecs_Greg() ;
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

didn't we agree to use camelCase for variables? so having something like this:

Suggested change
Shim.System.Specs = ShimSpecs_Greg() ;
shim.System.Specs = ShimSpecs_Greg() ;

or maybe that would break the code somewhere else and we should do another specific PR for changing formatting?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

baby steps!
We'll get rid of ShimOpt_Greg.m in a separate PR and change the case in the remaining classes.

else
Shim.System.Specs = Specs ;
end

[ Field, Params ] = ShimOpt.parseinput( varargin ) ;
Shim.System.currents = zeros( Shim.System.Specs.Amp.nActiveChannels, 1 ) ;

Params = ShimOpt_Greg.assigndefaultparameters( Params ) ;

Expand Down
2 changes: 1 addition & 1 deletion Coils/Shim_Siemens/Shim_Prisma/ShimOpt_Prisma.m
Original file line number Diff line number Diff line change
Expand Up @@ -372,7 +372,7 @@

scalingFactors = computenormalizationfactors() ;

for iCh = 1 : size( sh, 4 )
for iCh = 1 : length(scalingFactors)
basisFields(:,:,:,iCh) = scalingFactors(iCh) * basisFields(:,:,:,iCh) ;
end

Expand Down
84 changes: 51 additions & 33 deletions Coils/Shim_Template/ShimOpt.m
Original file line number Diff line number Diff line change
Expand Up @@ -105,10 +105,33 @@
ShimmedField; % object of type FieldEval
System; %
end

properties( Hidden = true )
Interpolant ;
% Ref ; % original shim reference maps before interpolation
end

properties( Dependent )
% Total number of available basis vectors (shim channels)
%
% E.g., in the standard linear equation used to optimize the shims,
% `nChannels` is the column dimension of the linear "shim" operator `A`:
% `A*i - b0 = 0, nChannels = size(A,2)`.
%
% Note, however, the example is just for context: `nChannels` is a
% dependent property determined by the number of user provided basis
% images: it describes the shim system and nonconfigurable.
%
% In class' optimization methods, though the default may be to include all
% available channels, they can nevertheless be omitted by providing the
% appropriate input argument, e.g.: to omit channels 1-3 and use the 4th only:
% ```
% Params.activeStaticChannelsMask = [0 0 0 1]
% obj.optimizeshimcurrents( Params )
% ```
nChannels {mustBeInteger, mustBeScalarOrEmpty} = 0 ;
end

% =========================================================================
% =========================================================================
methods
Expand All @@ -122,17 +145,17 @@
Shim.Model = [] ;
Shim.Aux = [] ;
Shim.System = [] ;
Shim.System.Specs = [] ;

[ Field, Params ] = ShimOpt.parseinput( varargin ) ;
[ Field, Params, Specs] = ShimOpt.parseinput( varargin ) ;
Shim.System.Specs = Specs ;

Params = ShimOpt.assigndefaultparameters( Params, Shim.System.Specs ) ;

%% .......
% Load shim basis if provided
if ~isempty(Params.pathToShimReferenceMaps)

ShimUse.customdisplay(['\n Preparing for shim ... \n\n'...
fprintf(['\n Preparing for shim ... \n\n'...
'Loading shim reference maps from ' Params.pathToShimReferenceMaps '\n\n']) ;

% Loads .mat: struct with fields
Expand Down Expand Up @@ -305,35 +328,35 @@
% User selects median p-measurement

close all;
ShimUse.customdisplay( ['\n ------- \n ' ...
fprintf( ['\n ------- \n ' ...
'Determine median measurement over inspired apnea : \n \n '] ) ;

Params.Inspired.medianP = ...
ProbeTracking.selectmedianmeasurement( Params.Inspired.measurementLog ) ;

ShimUse.customdisplay( ...
fprintf( ...
['Median measurement : ' num2str( Params.Inspired.medianP )] ) ;

ShimUse.customdisplay( ['\n ------- \n ' ...
fprintf( ['\n ------- \n ' ...
'Determine median measurement over expired apnea : \n \n '] ) ;

Params.Expired.medianP = ...
ProbeTracking.selectmedianmeasurement( Params.Expired.measurementLog ) ;

ShimUse.customdisplay( ...
fprintf( ...
['Median measurement : ' num2str( Params.Expired.medianP )] ) ;
else

ShimUse.customdisplay( 'Using user-supplied median p-measurements...' ) ;
fprintf( 'Using user-supplied median p-measurements...' ) ;

ShimUse.customdisplay( ...
fprintf( ...
['Inspired : ' ] ) ;
ShimUse.customdisplay( ...
fprintf( ...
['Median measurement : ' num2str( Params.Inspired.medianP )] ) ;

ShimUse.customdisplay( ...
fprintf( ...
['Expired : ' ] ) ;
ShimUse.customdisplay( ...
fprintf( ...
['Median measurement : ' num2str( Params.Expired.medianP )] ) ;
end

Expand All @@ -346,7 +369,7 @@
Params.Inspired.currents, Params.Expired.currents, ...
Params.Inspired.medianP, Params.Expired.medianP ) ;

ShimUse.customdisplay( ['\n ------- \n ' ...
fprintf( ['\n ------- \n ' ...
'Optimal DC current offsets (in amperes): ' num2str(Shim.Model.dcCurrentOffsets') '\n \n '] ) ;

Shim.setupdateoperator() ;
Expand Down Expand Up @@ -689,27 +712,19 @@
%
% where A * vectorOfShimCurrents = shimField

nVoxelsImg = Shim.getnumberofvoxels() ;
nActiveChannels = Shim.getnactivechannels() ;
nVoxelsImg = Shim.getnumberofvoxels() ;

A = zeros( nVoxelsImg, nActiveChannels ) ;
A = zeros( nVoxelsImg, Shim.nChannels ) ;

for channel = 1 : nActiveChannels
for channel = 1 : nChannels
A(:, channel) = reshape( Shim.img(:,:,:, channel), [nVoxelsImg 1] ) ;
end

end
% =========================================================================
function nActiveChannels = getnactivechannels( Shim )
%GETNACTIVECHANNELS
%
% Returns number of active shim channels
%
% nActiveChannels = GETNACTIVECHANNELS( Shim ) ;
%
% nActiveChannels = size( Shim.img, 4 ) ;
function nChannels = get.nChannels( Shim )

nActiveChannels = size( Shim.img, 4 ) ;
nChannels = size( Shim.img, 4 ) ;

end
% =========================================================================
Expand All @@ -721,7 +736,7 @@
% shimSupport is a logical map over the grid (voxel positions) defined by
% Shim.img of where the shim reference maps have well defined values.

shimSupport = sum(abs(Shim.img),4) > Shim.getnactivechannels()*eps ;
shimSupport = sum(abs(Shim.img),4) > Shim.nChannels*eps ;

% if myisfieldfilled( Shim.Hdr, 'MaskingImage' )
% shimSupport = shimSupport & Shim.Hdr.MaskingImage ;
Expand Down Expand Up @@ -2124,31 +2139,34 @@

end
% =========================================================================
function [ Field, Params ] = parseinput( Inputs )
function [ Field, Params, Specs] = parseinput( Inputs )
%PARSEINPUT
%
% Simple parser returns the optional user inputs Field and Params irrespective
% Simple parser returns the optional user inputs Field, Params and Specs irrespective
% of their input order (convenient).
%
% [ Field, Params ] = PARSEINPUT( Inputs )
% [ Field, Params, Specs] = PARSEINPUT( Inputs )

Field = [] ;
Params = [] ;
Specs = [] ;

nArgin = length( Inputs ) ;

if (nArgin > 0)
if (nArgin <= 2)
if (nArgin <= 3)
for iArg = 1 : nArgin
switch class( Inputs{iArg} )
case 'struct'
Params = Inputs{iArg} ;
case 'FieldEval'
Field = Inputs{iArg} ;
case 'ShimSpecs'
Specs = Inputs{iArg} ;
end
end
else
error('Too many input arguments. Should be <=2. See help ShimOpt') ;
error('Too many input arguments. Should be <=3. See help ShimOpt') ;
end
end

Expand All @@ -2169,7 +2187,7 @@
%
% [ img, Hdr, Interpolant ] = LOADSHIMREFERENCEMAPS( filename )

ShimUse.customdisplay(['\n Preparing for shim ... \n\n'...
fprintf(['\n Preparing for shim ... \n\n'...
'Loading shim reference maps from ' pathToShimReferenceMaps '\n\n']) ;

% Loads .mat: struct with fields
Expand Down