Critical Parameters for a given series, and the rules Doug Greve
(greve@nmr.mgh.harvard.edu) currently uses to determine them. If you
use this info, please give me some credit -- it took a long time to
figure out:).

Non-DICOM tags are found in proprietary Siemens fields. While
proprietary, these are stored as simple ASCII text surrounded by the
strings "### ASCCONV BEGIN ###" and "### ASCCONV END ###", so it is
easy to parse them with simple string operations (ie, you don't need
to get into the dicom stuff). I refer to this as the "ASCII header"
below. 

1. Files that belong to a series.

   Read the series number from the DICOM header (20,11). This
   apparently won't work when "Multiple Series" is checked.

2. Whether each file contains an individual image, a mosaic, or
   supermosaic.  

   I use the Phase Encode FoV ("sSliceArray.asSlice[0].dPhaseFOV") and
   the Readout FoV ("sSliceArray.asSlice[0].dReadoutFOV") from the
   ASCII header, the Phase Encode Direction (18,1312), and the row and
   column resolutions (28,30) to compute an expected number of rows
   and columns. I then compare these numbers to the number of rows
   (28,10) and columns (28,11) in the image. If they are the same, then
   it is not a mosaic or supermosaic. If they are not, I assume it's a
   mosaic. I don't know how to tell if its a supermosaic.

3. If a file contains a mosaic, the number of rows and cols in the
   mosaic.

   This is just the number of rows (28,10) and columns (28,11) in the
   image.

4. If a file contains a supermosaic, the number of mosaics in the
   super mosaic (as well as the dimension of each mosaic). ???

5. Volume dimensions (ie, number of rows, columns, slices).

   a. Non-mosaics - the number of rows and columns are determined
   directly from the DICOM header ((28,10) and (28,11)). The number of
   slices is determined by counting the number of files in the series
   with different slice prescriptions.

   b. Mosaics - the number of rows and columns are determined from the
   Phase Encode and Readout FoVs as described in #2. The number of
   slices is determined from the ASCII header (sSliceArray.lSize).

   c. Supermosaics - ???

6. Volume resolution (ie, distance between the centers of adjacent
   rows, cols, and slices).

   For rows and columns, the resolutions are obtained from DICOM
   (28,30), which is a string of the form "colres\rowres". The
   distance between slices is obtained from DICOM (18,88). If that
   does not exist, then the slice thickness (18,50) is used, but this
   will not include the distance factor or gap.  Supposedly, the skip
   can be obtained from (21,1344), but special software is needed to
   read odd DICOM groups. There is also an element in the ASCII header
   ("sGroupArray.asGroup[0].dDistFact").  One can also compute the
   slice resolution as the distance between adjacent slices using
   "sSliceArray.asSlice[N].sPosition.dAAA" where AAA is Sag (x), Cor
   (y), and Tra (z) from the ASCII header.

7. The direction cosines (DCs) for the row, col, and slice.

   The DCs for the row and column are obtained from DICOM (20,37),
   which is a string of the form "cx\cy\cz\rx\ry\rz". The slice DC is
   obtained from the ASCII header. The x, y, and z components are from
   three lines of the form "sSliceArray.asSlice[0].sNormal.dAAA" where
   AAA is Sag (x), Cor (y), and Tra (z). Siemens may reverse the slice
   order in order to make the images more readable by
   radiologists. This reversal is NOT accompanied by a change the
   slice direction cosine. Rather, it is indicated by the presence of
   sSliceArray.ucImageNumbAAA (any of the three). The FreeSurfer
   software reverses the slices upon read-in rather than chaning
   the direction cosine.

8. The XYZ coordinates at the exact center of a voxel at a given row,
   col, and slice.

   a. Non-mosaics - DICOM (20,32) gives the XYZ coordinate at the
   center of first voxel of the image. Section C.7.6.2.1.1, page 275:
   "The Image Position (0020,0032) specifies the x, y, and z
   coordinates of the upper left hand corner of the image; it is the
   center of the first voxel transmitted."

   b. Mosaics - DICOM (20,32) is incorrect for mosaics. The value in
   this field gives where the origin of an image the size of the
   mosaic would have been had such an image been collected. This puts
   the origin outside of the scanner.  However, the center of a slice
   can be obtained from the ASCII header from lines of the form
   "sSliceArray.asSlice[N].sPosition.dAAA", where N is the slice
   number and AAA is Sag (x), Cor (y), and Tra (z). This may be off by
   half a voxel. Given this information, the direction cosines, the
   voxel size, and dimension, the origin can be computed.

   c. Supermosaics - ???

9. Number of Volumes (ie, number of frames or time points).

   a. Non-mosaics - count the number of files with the same image
   position. 

   b. Mosaics - count the number of files in the series. The number
   of frames should also be stored in the ASCII header as 1 plus
   lRepetitions.

   c. Supermosaics - ???

10. Time between volumes/frames (ie, the TR for fMRI).

   a. Non-mosaics - ???

   b. Mosaics - number of slices times the repetition time (DICOM
   (18,80)). This is for version 1.6 and before. For version 2.1
   and after, (18,80) will contain the inter-volume TR (instead 
   of the time it takes to acquire one slice). The software version
   can be determined from tag (18,1020)

   c. Supermosaics - ???

11. Time at which each slice was obtained (relative to the start of
    the volume acquisition). For sequences in which slices were
    acquired uniformly across the TR, there is a variable in the ASCII
    header called sSliceArray.ucMode which indicates the slice order:
    0x1 for Ascending, 0x2 for Descending, and 0x4 for Interleaved.
    The selection box for this option can be found on the Numaris/4
    GUI on the "Geometry" tab, "Common" sub-tab, field name "Series".

    [NEED TO VERIFY THIS] If sSliceArray.ucImageNumbTra (or Cor or
    Sag) is greater than zero, for axial data (or coronal or sagittal,
    respectively) then we know the slice order is reversed from what
    was expected (ie, that set up on the console).

12. Time at which each volume was obtained (relative to the start of the
    series).  This may not be computable from the slice timing if
    there is a temporal gap between volumes. ??? See also below on
    slice timing.

13. Other parameters not so critical: echo time, inversion time, phase
    encode direction, readout direction, flip angle, patient name,
    scan date, scan time, pulse sequence, protocol name, etc. These
    are obtainable from the DICOM header.

NOTE ON COORDINATE DEFINITION. It is assumed that all xyz coordinates
(including direction cosines) in the Siemens DICOM header (including
ASCII) conform to an LPS standard. "LPS" means that x increases from
the subject's right to Left, y increases from anterior to Posterior,
and z increases from inferior to Superior. All this assumes that the
subject in the scanner Head-First/Supine (HFS). The patient's position
can be determined from DICOM tag (18,5100). It appears that this
definition can be changed from the Numaris 4 console.  On the "System"
tab, "Common" sub-tab, heading "Image Numbering" there are selections
for fields "Sagittal", "Coronal", and "Transversal". Changes these
parameters will affect the variable called 
sSliceArray.ucImageNumb{Sag,Cor,Tra} in the ASCII header. The value
will be either 0 or 0x1 (NOTE: a value of 0 is indicated by the
absence of the variable in the header). 

---------------------------------------------------------------------------
MGH's dicom server (bourget)

For reference, our naming scheme to date has used the following
dicom tags which has so far guaranteed uniqueness:

d 0008 1090 modelX
d 0018 1000 serialX
d 0008 0020 dateX
D 0008 0030 timeX
f 0020 0011 seriesX
F 0020 0013 imageX

If we add to this, it will change the names of all scanner files
henceforth.  Any analysis tools that "depend" on the name scheme will
have to be modified to handle both old and new.

---------------------------------------------------------------------------
Image Type: 0008 0008 - might tell you if it is a mosaic

Anat: DERIVED\PRIMARY\OTHER 
Func: ORIGINAL\PRIMARY\M\ND\MOSAIC
  M = magnitude. P = phase.

-------------------------------------------------------------------------

From Siemens white paper: "Slice Order (Slice Timing) for fMRI
Evaluation Joachim Graessner, Dipl. Ing." Dated May 2014

The EPI mosaic in figure 6 shows a series containing 36/35 interleaved
measured slices with text overlays for image numbers, excitation order
number and relative offset time. The latter value can be extracted
from the DICOM header in tag (0019,1029) „MosaicRefAcqTimes“ together
with tag (0008,0032) „Acquisition time“ to calculate the absolute time
of an image.

---------------------------------------------------------------------------

(0008,0013) TM [171809.637000]                          #  14, 1 InstanceCreationTime
(0008,0020) DA [20111006]                               #   8, 1 StudyDate
(0008,0021) DA [20111006]                               #   8, 1 SeriesDate
(0008,0022) DA [20111006]                               #   8, 1 AcquisitionDate
(0008,0023) DA [20111006]                               #   8, 1 ContentDate
(0008,0030) TM [165732.988000]                          #  14, 1 StudyTime
(0008,0031) TM [171609.633000]                          #  14, 1 SeriesTime
(0008,0032) TM [171736.510000]                          #  14, 1 AcquisitionTime
(0008,0033) TM [171809.637000]                          #  14, 1 ContentTime

Time

A string of characters of the format hhmmss.frac; where hh contains
hours (range "00" - "23"), mm contains minutes (range "00" - "59"), ss
contains seconds (range "00" - "59"), and frac contains a fractional
part of a second as small as 1 millionth of a second (range "000000" -
"999999"). A 24 hour clock is assumed. Midnight can be represented by
only "0000" since "2400" would violate the hour range. The string may
be padded with trailing spaces. Leading and embedded spaces are not
allowed. One or more of the components mm, ss, or frac may be
unspecified as long as every component to the right of an unspecified
component is also unspecified. If frac is unspecified the preceding
"." may not be included. Frac shall be held to six decimal places or
less to ensure its format conforms to the ANSI HISPP MSDS Time common
data type.

Examples -
1. "070907.0705" represents a time of 7 hours, 9 minutes and 7.0705 seconds.
2. "1010" represents a time of 10 hours, and 10 minutes.
3. "021" is an invalid value.
Note -
1. For reasons of backward compatibility with versions of this
standard prior to V3.0, it is recommended that implementations also
support a string of characters of the format hh:mm:ss.frac for this
VR. 


--------------------------------------------------------------

the pixel data cannot be loaded as it is JPEG compressed
    dcmdjpeg +te $dcm $dcm

---------------------------------------------------
GE 
0x0043, 0x1039 bvalue
0x0019, 0x10bb, 0x10bc, 0x10bd directions

--------------------------------------------------------------
https://github.com/BRAINSia/BRAINSTools/blob/master/DWIConvert/SiemensDWIConverter.h

bvalue 0x0019 0x100c
bvector 0x0019 0x100e
bmatrix 0x0019 0x1027
diffusion info 0029,1010

If zero, then tags do not exist.

For bay1 dti data, the bvalues are correct. The x for the bvector is
correct. The yz need to be negated. Even then, they are not quite
correct. bmatrix is the 6 elements used to create the bmatrix and
appears to be redundant (does not resolve above issue). Diffusion info
apparently also has the directions but also appears to be redundant.
bvalue in 0x100a does not match trace(bmatrix) or value that AY gave
me. The difference is due to obliqueness; not clear what the right
answer is.

dnggrad = load('dnggrad.txt');
bvals0 = load('bvals.txt')';
bvecs0 = load('bvecs.txt')';
indnz = find(bvals0 ~= 0);
bvals = bvals0(indnz);
bvecs = bvecs0(indnz,:);

gm = load('grad.mtx.dat');
ntot = size(gm,1);
d = zeros(ntot,3);
db = zeros(ntot,1);
for n = 1:ntot
  a = gm(n,:);
  b = zeros(3,3);
  b(1,1) = a(1);
  b(1,2) = a(2);
  b(1,3) = a(3);
  b(2,2) = a(4);
  b(2,3) = a(5);
  b(3,3) = a(6);
  b(3,1) = b(1,3);
  b(2,1) = b(1,2);
  b(3,2) = b(2,3);
  [u s v] = svd(b);
  d(n,:) = u(:,1)';
  db(n) = trace(b);
end

-----------------------------------------------------------------
Double oblique data in ~/l/sg1/oblique-dti. 

This data does not have the above fields but does have
DiffusionGradientDirection in the info header field (strings).

The gradients in the header do not match the gradients in the
mgh-dti-seqpack. The mgh-dti-seqpack parameters do not give good
results, but the ones from the header do. Though the acqs for all
three had the same slice prescription, the header gradient directions
are slightly different.

----------------------------------------------------------------------------
our current software is RSNA CTN (central test node) produced by the
Mallinckrodt Institute of Radiology (MIR) under contract to the
Radiological Society of North America.
ftp://wuerlim.wustl.edu/pub/dicom/software/ctn/ says there should be a
docs folder but can't be found bugs: dicom_bugs@wuerl.wustl.edu


https://www.mir.wustl.edu/Portals/0/Documents/Uploads/ERL/users-guide.pdf

-------------------------------------------------------------

http://www.dicomtags.com/vrs
IS - integer string, 12 bytes max
OB - other byte string, unlimited.
LO - Long String 64 Bytes Maximum

-----------------------------------------
Doc for GE that might be helpful for understanding their dicoms
 https://www.gehealthcare.com/-/jssmedia/widen/2018/01/25/0204/gehealthcarecom/migrated/2018/02/19/0841/ability-dicom-magnetic-resonance-gehc-dicom-conformance_discoverymr450_doc0506131_rev3_pdf.pdf?rev=-1&hash=FD53D6AC70B532F200FA14C33E84C7EF
-----------------------------------------

itk::DCMTKSequence originSeq;
curItem.GetElementSQ(0x0020, 0x9113, originSeq);
std::string originString;
originSeq.GetElementDS(0x0020, 0x0032, originString);

ftp://ftp.philips.com/pub/pms-3003/DICOM_Information/CookBook.pdf

18,88 - slice spacing

20,37 - image orientation patient

28,30 - pixel spacing

(0028,1052) DS [0]                                      #   2, 1 RescaleIntercept
(0028,1053) DS [1.23785103785103]                       #  16, 1 RescaleSlope
(0028,1054) LO [US]                                     #   2, 1 RescaleType

5200,9229 - functional group macro whose attributes apply to all frames
5200,9230 - functional group macro whose attributes apply to a given frames

---------------------------------------------------------

On-scanner gradient distortion correction

In the protocol, one has the option to not do any gradient
nonlinearity correction (default), 2D correction, or 3D correction
(even with 2D data but it requires at least 8 slices from the same
scan in the same orientation).  Whether or not correction was used on
the data will be in the Siemens private header.

 
Specifically in (0029,1020)

sDistortionCorrFilter.ucMode       =            1     (none, or "ND")
sDistortionCorrFilter.ucMode       =            2     (2D, or "DIS2D")
sDistortionCorrFilter.ucMode       =            4     (3D, or "DIS3D")

Also, it is derived and can be also found in the image labeling header section as 
    (0051,1016)      = LO 000018 p2 NORM/MEAN/DIS3D  (or DIS2D or ND)


0008,0008 also has info like above

======================================================================

Explaining unpacking of bvecs. Different manufactures have different
conventions. Siemens puts the bvecs in LPS scanner space. To map them
to voxel space, they should be converted to RAS scanner space, then
multiplied by the inverse of the Mdc part of the vox2ras matrix (to
convert from ras to vox). 

The field has evolved using the "FSL" standard. In FSL, if the
determinant>0 (neurological), then an FSL program flips the image in
the first dimension to make the determinant<0 (radiological). The
program performs its processing, then flips any output volume back to
the original space before writing it out. For most things, this is ok,
but for DTI processing, reorienting the image must be matched by
reorienting the bvecs. FSL should have just done this when seeing that
the image needed to be reoriented, but instead it requires the user to
know that the image will be reoriented and supply FSL witha reoriented
bvec file. So, when det<0, the bvec orientation matches that of the
input volume. When det>0, the sign of the first bvec has to be
flipped. Note that the sign of all bvec values can be reversed without
changing the output. On the output side, none of this matters for FA,
but it does matter for the eigen vectors. For det>0 volumes, the EVs
written out by FSL will be inconsistent with the volume orientation
(eg, in FreeView they will look wrong). However, an FSL viewer
(eg, fsleyes), will see det>0 and then reorient the EVs so they are
correct.

Here are two related discussions:
https://github.com/rordenlab/dcm2niix/issues/269
https://github.com/rordenlab/dcm2niix/issues/366
https://fsl.fmrib.ox.ac.uk/fsl/fslwiki/FDT/FAQ#What_conventions_do_the_bvecs_use.3F

In the connectome data:
dcm2niix: PSL (det>0) +--
DNG/FS:   PIL (det<0) -+-
Going from PSL to PIL or back requires:
 1. the sign of the 2nd to be flipped because the dimension is flipped
 2. the sign of the 1st to be flipped because det changes sign

Analyzing the PIL data with both FS mri_glmfit and FSL dtifit with the
same bvecs gives the same result because the det<0.

Analysing the PIL data in FSL with the dcm2niix bvecs yields correct
results (must be viewing in fsleyes). When analyzing the PIL data in
FS mri_glmfit using the dcm2niix bvecs, the sign of the first bvec
needs to be flipped. Then the result will look correct when viewing in
FreeView.