Skip to content

Commit

Permalink
RepetitionTimeExcitation (#439)
Browse files Browse the repository at this point in the history
  • Loading branch information
neurolabusc committed Oct 20, 2020
1 parent 73fa5d5 commit 3614026
Show file tree
Hide file tree
Showing 4 changed files with 104 additions and 60 deletions.
1 change: 1 addition & 0 deletions GE/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -61,6 +61,7 @@ Anatomical localizers (e.g. scout images) are quick-and-dirty scans used to posi
## Sample Datasets

- [A validation dataset for dcm2niix commits](https://github.com/neurolabusc/dcm_qa_nih).
- [Slice Timing and Phase Encoding examples](https://github.com/jannikadon/cc-dcm2bids-wrapper/tree/main/dicom-qa-examples)
- [Slice timing validation](https://github.com/neurolabusc/dcm_qa_stc) for different varieties of GE EPI sequences.
- [Examples of phase encoding polarity, slice timing and diffusion gradients](https://github.com/neurolabusc/dcm_qa_ge).
- The dcm2niix [wiki](https://www.nitrc.org/plugins/mwiki/index.php/dcm2nii:MainPage) includes examples of diffusion data, slice timing, and other variations.
150 changes: 95 additions & 55 deletions console/nii_dicom.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -1514,7 +1514,7 @@ int headerDcm2Nii(struct TDICOMdata d, struct nifti_1_header *h, bool isComputeS
h->pixdim[1] = d.xyzMM[1];
h->pixdim[2] = d.xyzMM[2];
h->pixdim[3] = d.xyzMM[3];
h->pixdim[4] = d.TR/1000; //TR reported in msec, time is in sec
h->pixdim[4] = d.TR / 1000.0; //TR reported in msec, time is in sec
h->dim[1] = d.xyzDim[1];
h->dim[2] = d.xyzDim[2];
h->dim[3] = d.xyzDim[3];
Expand Down Expand Up @@ -1649,7 +1649,7 @@ dti4D->volumeOnsetTime[0] = -1;
dti4D->decayFactor[0] = -1;
dti4D->frameDuration[0] = -1;
dti4D->intenScale[0] = 0.0;
dti4D->timeBetweenVolumes = 0.0;
dti4D->repetitionTimeExcitation = 0.0;
strcpy(d.protocolName, ""); //erase dummy with empty
strcpy(d.seriesDescription, ""); //erase dummy with empty
strcpy(d.sequenceName, ""); //erase dummy with empty
Expand Down Expand Up @@ -4260,6 +4260,7 @@ const uint32_t kEffectiveTE = 0x0018+ (0x9082 << 16);
//#define kTemporalPositionIdentifier 0x0020+(0x0100 << 16 ) //IS
#define kOrientation 0x0020+(0x0037 << 16 )
#define kTemporalPosition 0x0020+(0x0100 << 16 ) //IS
#define kTemporalResolution 0x0020+(0x0110 << 16 ) //DS
#define kImagesInAcquisition 0x0020+(0x1002 << 16 ) //IS
//#define kSliceLocation 0x0020+(0x1041 << 16 ) //DS would be useful if not optional type 3
#define kImageComments 0x0020+(0x4000<< 16 )// '0020' '4000' 'LT' 'ImageComments'
Expand Down Expand Up @@ -4378,10 +4379,16 @@ uint32_t kSequenceDelimitationItemTag = 0xFFFE +(0xE0DD << 16 );
// https://github.com/dcm4che/dcm4che/blob/master/dcm4che-dict/src/main/dicom3tools/libsrc/standard/elmdict/siemens.tpl
// https://github.com/neurolabusc/dcm_qa_agfa
// http://dicom.nema.org/medical/dicom/current/output/chtml/part05/sect_7.8.html
uint32_t privateCreatorMask = 0; //0 -> none
uint32_t privateCreatorRemap = 0; //0 -> none
#define kMaxRemaps 16 //no vendor uses more than 5 private creator groups
//we need to keep track of multiple remappings, e.g. issue 437 2005,0014->2005,0012; 2005,0015->2005,0011
int nRemaps = 0;
uint32_t privateCreatorMasks[kMaxRemaps]; //0 -> none
uint32_t privateCreatorRemaps[kMaxRemaps]; //0 -> none
//uint32_t privateCreatorMask = 0; //0 -> none
//uint32_t privateCreatorRemap = 0; //0 -> none
#endif
double TE = 0.0; //most recent echo time recorded
float temporalResolutionMS = 0.0;
float MRImageDynamicScanBeginTime = 0.0;
bool is2005140FSQ = false;
bool overlayOK = true;
Expand Down Expand Up @@ -4839,58 +4846,88 @@ uint32_t kSequenceDelimitationItemTag = 0xFFFE +(0xE0DD << 16 );
}
if ((isIconImageSequence) && ((groupElement & 0x0028) == 0x0028 )) groupElement = kUnused; //ignore icon dimensions
#ifdef salvageAgfa //issue435
if ((privateCreatorMask > 0) && ((groupElement & 0xFF00FFFF) == (privateCreatorMask & 0xFF00FFFF))) {
uint32_t geIn = groupElement;
groupElement = privateCreatorRemap + (groupElement & 0x00FF0000);
if (isVerbose > 1)
printf("remapping %04x,%04x -> %04x,%04x\n", geIn & 65535, geIn >> 16, groupElement & 65535, groupElement >> 16);
} else
privateCreatorMask = 0;
//see 7.8.1 Private Data Element Tags are numbered (gggg,0010-00FF) (gggg is odd)
if ((lLength > 8) && ( ((groupElement & 65535) % 2) != 0) && ((groupElement>>16) <= 0xFF)) {
privateCreatorMask = 0;
//Handle remapping using integers, and slower but simpler approach is with strings:
// https://github.com/pydicom/pydicom/blob/master/pydicom/_private_dict.py
if (((groupElement & 65535) % 2) == 0) goto skipRemap; //remap odd (private) groups
//printf("tag %04x,%04x\n", groupElement & 65535, groupElement >> 16);

if (((groupElement>>16) >= 0x10) && ((groupElement>>16) <= 0xFF)) { //tags (gggg,0010-00FF) may define new remapping
//if remapping tag
//first: see if this remapping overwrites existing tag
uint32_t privateCreatorMask = 0; //0 -> none
uint32_t privateCreatorRemap = 0; //0 -> none
privateCreatorMask = (groupElement & 65535) + ((groupElement & 0xFFFF0000) << 8);
if (nRemaps > 0) {
int j = 0;
for (int i = 0; i < nRemaps; i++) //remove duplicate remapping
//copy all remaps except exact match
if (privateCreatorMasks[i] != privateCreatorMask) {
privateCreatorMasks[j] = privateCreatorMasks[i];
privateCreatorRemaps[j] = privateCreatorRemaps[i];
j++;
}
nRemaps = j;
}
//see if this is known private vendor tag
privateCreatorRemap = 0;
char privateCreator[kDICOMStr];
dcmStr(lLength, &buffer[lPos], privateCreator);
//next lines determine remapping, append as needed
//Siemens https://github.com/dcm4che/dcm4che/blob/master/dcm4che-dict/src/main/dicom3tools/libsrc/standard/elmdict/siemens.tpl
//Siemens https://github.com/dcm4che/dcm4che/blob/master/dcm4che-dict/src/main/dicom3tools/libsrc/standard/elmdict/siemens.tpl
if (strstr(privateCreator, "SIEMENS MR HEADER") != NULL) privateCreatorRemap = 0x0019 +(0x1000 << 16 );
if (strstr(privateCreator, "SIEMENS MR SDS 01") != NULL) privateCreatorRemap = 0x0021 +(0x1000 << 16 );
if (strstr(privateCreator, "SIEMENS MR SDI 02") != NULL) privateCreatorRemap = 0x0021 +(0x1100 << 16 );
if (strstr(privateCreator, "SIEMENS CSA HEADER") != NULL) privateCreatorRemap = 0x0029 +(0x1000 << 16 );
if (strstr(privateCreator, "SIEMENS MR HEADER") != NULL) privateCreatorRemap = 0x0051 +(0x1000 << 16 );
//GE https://github.com/dcm4che/dcm4che/blob/master/dcm4che-dict/src/main/dicom3tools/libsrc/standard/elmdict/gems.tpl
if (strstr(privateCreator, "GEMS_ACQU_01") != NULL) privateCreatorRemap = 0x0019 +(0x1000 << 16 );
if (strstr(privateCreator, "GEMS_RELA_01") != NULL) privateCreatorRemap = 0x0021 +(0x1000 << 16 );
if (strstr(privateCreator, "GEMS_SERS_01") != NULL) privateCreatorRemap = 0x0025 +(0x1000 << 16 );
if (strstr(privateCreator, "GEMS_PARM_01") != NULL) privateCreatorRemap = 0x0043 +(0x1000 << 16 );
//ELSCINT https://github.com/dcm4che/dcm4che/blob/master/dcm4che-dict/src/main/dicom3tools/libsrc/standard/elmdict/elscint.tpl
int grp = (groupElement & 65535);
if ((grp == 0x07a1) && (strstr(privateCreator, "ELSCINT1") != NULL)) privateCreatorRemap = 0x07a1 +(0x1000 << 16 );
if ((grp == 0x07a3) && (strstr(privateCreator, "ELSCINT1") != NULL)) privateCreatorRemap = 0x07a3 +(0x1000 << 16 );
//Philips https://github.com/dcm4che/dcm4che/blob/master/dcm4che-dict/src/main/dicom3tools/libsrc/standard/elmdict/philips.tpl
if (strstr(privateCreator, "PHILIPS IMAGING DD 001") != NULL) privateCreatorRemap = 0x2001 +(0x1000 << 16 );
if (strstr(privateCreator, "Philips Imaging DD 001") != NULL) privateCreatorRemap = 0x2001 +(0x1000 << 16 );
if (strstr(privateCreator, "PHILIPS MR IMAGING DD 001") != NULL) privateCreatorRemap = 0x2005 +(0x1000 << 16 );
if (strstr(privateCreator, "Philips MR Imaging DD 001") != NULL) privateCreatorRemap = 0x2005 +(0x1000 << 16 );
if (strstr(privateCreator, "PHILIPS MR IMAGING DD 005") != NULL) privateCreatorRemap = 0x2005 +(0x1400 << 16 );
if (strstr(privateCreator, "Philips MR Imaging DD 005") != NULL) privateCreatorRemap = 0x2005 +(0x1400 << 16 );
//UIH https://github.com/neurolabusc/dcm_qa_uih
if (strstr(privateCreator, "Image Private Header") != NULL) privateCreatorRemap = 0x0065 +(0x1000 << 16 );
//sanity check: group should match
if (grp != (privateCreatorRemap & 65535)) privateCreatorRemap = 0;
if (privateCreatorRemap > 0)
privateCreatorMask = (groupElement & 65535) + ((groupElement & 0xFFFF0000) << 8);
if (privateCreatorRemap == privateCreatorMask) { //no remapping, e.g. 0021,1000 -> 0021,1000
privateCreatorRemap = 0;
privateCreatorMask = 0;
} else
d.isPrivateCreatorRemap = true;
if ((privateCreatorRemap > 0) && (isVerbose > 1))
printf("PrivateCreator '%s' remapping %04x,%04x -> %04x,%04x\n", privateCreator, privateCreatorMask & 65535, privateCreatorMask >> 16, privateCreatorRemap & 65535, privateCreatorRemap >> 16);
}
#endif //salvageAgfa
switch ( groupElement ) {
if (strstr(privateCreator, "SIEMENS MR SDI 02") != NULL) privateCreatorRemap = 0x0021 +(0x1100 << 16 );
if (strstr(privateCreator, "SIEMENS CSA HEADER") != NULL) privateCreatorRemap = 0x0029 +(0x1000 << 16 );
if (strstr(privateCreator, "SIEMENS MR HEADER") != NULL) privateCreatorRemap = 0x0051 +(0x1000 << 16 );
//GE https://github.com/dcm4che/dcm4che/blob/master/dcm4che-dict/src/main/dicom3tools/libsrc/standard/elmdict/gems.tpl
if (strstr(privateCreator, "GEMS_ACQU_01") != NULL) privateCreatorRemap = 0x0019 +(0x1000 << 16 );
if (strstr(privateCreator, "GEMS_RELA_01") != NULL) privateCreatorRemap = 0x0021 +(0x1000 << 16 );
if (strstr(privateCreator, "GEMS_SERS_01") != NULL) privateCreatorRemap = 0x0025 +(0x1000 << 16 );
if (strstr(privateCreator, "GEMS_PARM_01") != NULL) privateCreatorRemap = 0x0043 +(0x1000 << 16 );
//ELSCINT https://github.com/dcm4che/dcm4che/blob/master/dcm4che-dict/src/main/dicom3tools/libsrc/standard/elmdict/elscint.tpl
int grp = (groupElement & 65535);
if ((grp == 0x07a1) && (strstr(privateCreator, "ELSCINT1") != NULL)) privateCreatorRemap = 0x07a1 +(0x1000 << 16 );
if ((grp == 0x07a3) && (strstr(privateCreator, "ELSCINT1") != NULL)) privateCreatorRemap = 0x07a3 +(0x1000 << 16 );
//Philips https://github.com/dcm4che/dcm4che/blob/master/dcm4che-dict/src/main/dicom3tools/libsrc/standard/elmdict/philips.tpl
if (strstr(privateCreator, "PHILIPS IMAGING DD 001") != NULL) privateCreatorRemap = 0x2001 +(0x1000 << 16 );
if (strstr(privateCreator, "Philips Imaging DD 001") != NULL) privateCreatorRemap = 0x2001 +(0x1000 << 16 );
if (strstr(privateCreator, "PHILIPS MR IMAGING DD 001") != NULL) privateCreatorRemap = 0x2005 +(0x1000 << 16 );
if (strstr(privateCreator, "Philips MR Imaging DD 001") != NULL) privateCreatorRemap = 0x2005 +(0x1000 << 16 );
if (strstr(privateCreator, "PHILIPS MR IMAGING DD 005") != NULL) privateCreatorRemap = 0x2005 +(0x1400 << 16 );
if (strstr(privateCreator, "Philips MR Imaging DD 005") != NULL) privateCreatorRemap = 0x2005 +(0x1400 << 16 );
//UIH https://github.com/neurolabusc/dcm_qa_uih
if (strstr(privateCreator, "Image Private Header") != NULL) privateCreatorRemap = 0x0065 +(0x1000 << 16 );
//sanity check: group should match
if (grp != (privateCreatorRemap & 65535)) privateCreatorRemap = 0;
if (privateCreatorRemap == 0) goto skipRemap; //this is not a known private group
if (privateCreatorRemap == privateCreatorMask) goto skipRemap; //the remapping and mask are identical 2005,1000 -> 2005,1000
if ((nRemaps + 1) >=kMaxRemaps) goto skipRemap; //all slots full (should never happen)
//add new remapping
privateCreatorMasks[nRemaps] = privateCreatorMask;
privateCreatorRemaps[nRemaps] = privateCreatorRemap;
//printf("new remapping %04x,%04x -> %04x,%04x\n", privateCreatorMask & 65535, privateCreatorMask >> 16, privateCreatorRemap & 65535, privateCreatorRemap >> 16);
if (isVerbose > 1)
printf("new remapping (%d) %04x,%02xxy -> %04x,%02xxy\n", nRemaps, privateCreatorMask & 65535, privateCreatorMask >> 24, privateCreatorRemap & 65535, privateCreatorRemap >> 24);
nRemaps += 1;
//for (int i = 0; i < nRemaps; i++)
// printf(" %d = %04x,%02xxy -> %04x,%02xxy\n", i, privateCreatorMasks[i] & 65535, privateCreatorMasks[i] >> 24, privateCreatorRemaps[i] & 65535, privateCreatorRemaps[i] >> 24);
goto skipRemap;
}

if (nRemaps < 1) goto skipRemap;
{
uint32_t remappedGroupElement = 0;
for (int i = 0; i < nRemaps; i++)
if ((groupElement & 0xFF00FFFF) == (privateCreatorMasks[i] & 0xFF00FFFF))
remappedGroupElement = privateCreatorRemaps[i] + (groupElement & 0x00FF0000);
if (remappedGroupElement == 0) goto skipRemap;
if (isVerbose > 1)
printf("remapping %04x,%04x -> %04x,%04x\n", groupElement & 65535, groupElement >> 16, remappedGroupElement & 65535, remappedGroupElement >> 16);
groupElement = remappedGroupElement;
}
skipRemap:
#endif
switch ( groupElement ) {
case kMediaStorageSOPClassUID: {
char mediaUID[kDICOMStr];
dcmStr(lLength, &buffer[lPos], mediaUID);
Expand Down Expand Up @@ -6452,12 +6489,10 @@ uint32_t kSequenceDelimitationItemTag = 0xFFFE +(0xE0DD << 16 );
//printf("sliceV %g %g %g\n", sliceV.v[0], sliceV.v[1], sliceV.v[2]);
isOrient = true;
break; }
case kTemporalPosition :
if (d.manufacturer == kMANUFACTURER_GE)
break; //for GE use kRawDataRunNumber
d.rawDataRunNumber = dcmStrInt(lLength, &buffer[lPos]);
case kTemporalResolution :
temporalResolutionMS = dcmStrFloat(lLength, &buffer[lPos]);
break;
case kImagesInAcquisition :
case kImagesInAcquisition :
imagesInAcquisition = dcmStrInt(lLength, &buffer[lPos]);
break;
//case kSliceLocation : //optional so useless, infer from image position patient (0020,0032) and image orientation (0020,0037)
Expand Down Expand Up @@ -7102,6 +7137,11 @@ if (d.isHasPhase)
#ifndef myLoadWholeFileToReadHeader
fclose(file);
#endif
if ((temporalResolutionMS > 0.0) && (isSameFloatGE(d.TR,temporalResolutionMS)) ) {
//do something profound
//in practice 0020,0110 not used
//https://github.com/bids-standard/bep001/blob/repetitiontime/Proposal_RepetitionTime.md
}
if (hasDwiDirectionality) d.isVectorFromBMatrix = false; //issue 265: Philips/Siemens have both directionality and bmatrix, Bruker only has bmatrix
/*
fixed 2/2019 by modifying to kDiffusionBFactor, kDiffusionDirectionRL, kDiffusionDirectionAP, kDiffusionDirectionFH
Expand Down
2 changes: 1 addition & 1 deletion console/nii_dicom.h
Original file line number Diff line number Diff line change
Expand Up @@ -139,7 +139,7 @@ static const uint8_t MAX_NUMBER_OF_DIMENSIONS = 8;
bool isReal[kMaxDTI4D];
bool isImaginary[kMaxDTI4D];
bool isPhase[kMaxDTI4D];
float timeBetweenVolumes;
float repetitionTimeExcitation;
};

#ifdef _MSC_VER //Microsoft nomenclature for packed structures is different...
Expand Down
11 changes: 7 additions & 4 deletions console/nii_dicom_batch.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -1220,7 +1220,7 @@ tse3d: T2*/
if ((d.TE > 0.0) && (!d.isXRay)) fprintf(fp, "\t\"EchoTime\": %g,\n", d.TE / 1000.0 );
//if ((d.TE2 > 0.0) && (!d.isXRay)) fprintf(fp, "\t\"EchoTime2\": %g,\n", d.TE2 / 1000.0 );
json_Float(fp, "\t\"RepetitionTime\": %g,\n", d.TR / 1000.0 );
json_Float(fp, "\t\"TimeBetweenVolumes\": %g,\n", dti4D->timeBetweenVolumes);
json_Float(fp, "\t\"RepetitionTimeExcitation\": %g,\n", dti4D->repetitionTimeExcitation);
json_Float(fp, "\t\"InversionTime\": %g,\n", d.TI / 1000.0 );
json_Float(fp, "\t\"FlipAngle\": %g,\n", d.flipAngle );
bool interp = false; //2D interpolation
Expand Down Expand Up @@ -1640,7 +1640,7 @@ dti4D->sliceOrder[0] = -1;
dti4D->volumeOnsetTime[0] = -1;
dti4D->decayFactor[0] = -1;
dti4D->intenScale[0] = 0.0;
dti4D->timeBetweenVolumes = 0.0;
dti4D->repetitionTimeExcitation = 0.0;
nii_SaveBIDSX(pathoutname, d, opts, h, filename, dti4D);
}// nii_SaveBIDSX()

Expand Down Expand Up @@ -5063,8 +5063,11 @@ int saveDcm2NiiCore(int nConvert, struct TDCMsort dcmSort[],struct TDICOMdata dc
float toleranceSec = 50.0/1000.0; //e.g. 50/1000 = 50ms
if ((nVol > 1) && (volumeTimeStartFirstStartLast > 0.0)) {
tr = volumeTimeStartFirstStartLast / (nVol - 1.0);
if (fabs(tr - hdr0.pixdim[4]) > toleranceSec)
dti4D->timeBetweenVolumes = tr;
if (fabs(tr - hdr0.pixdim[4]) > toleranceSec) {
dti4D->repetitionTimeExcitation = hdr0.pixdim[4];
hdr0.pixdim[4] = tr;
dcmList[indx0].TR = tr * 1000.0; //as msec
}
}
if ((maxtr - mintr) > toleranceSec) trVaries = true;
if (trVaries) {
Expand Down

0 comments on commit 3614026

Please sign in to comment.