Skip to content

Commit

Permalink
Siemens XA30 ASL parameters and ImageTypeText 0021,1175
Browse files Browse the repository at this point in the history
  • Loading branch information
neurolabusc committed Jul 19, 2022
1 parent 3583fcc commit 4f89073
Show file tree
Hide file tree
Showing 3 changed files with 63 additions and 12 deletions.
27 changes: 27 additions & 0 deletions console/nii_dicom.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -712,6 +712,7 @@ struct TDICOMdata clear_dicom_data() {
strcpy(d.patientID, "");
strcpy(d.accessionNumber, "");
strcpy(d.imageType, "");
strcpy(d.imageTypeText, "");
strcpy(d.imageComments, "");
strcpy(d.imageBaseName, "");
strcpy(d.phaseEncodingDirectionDisplayedUIH, "");
Expand Down Expand Up @@ -4271,6 +4272,7 @@ const uint32_t kEffectiveTE = 0x0018 + (0x9082 << 16);
#define kImagingFrequency2 0x0018 + uint32_t(0x9098 << 16) //FD
#define kParallelReductionFactorOutOfPlane 0x0018 + uint32_t(0x9155 << 16) //FD
//#define kFrameAcquisitionDuration 0x0018+uint32_t(0x9220 << 16 ) //FD
#define kArterialSpinLabelingContrast 0x0018 + uint32_t(0x9250 << 16) //CS
#define kASLPulseTrainDuration 0x0018 + uint32_t(0x9258 << 16) //UL
#define kDiffusionBValueXX 0x0018 + uint32_t(0x9602 << 16) //FD
#define kDiffusionBValueXY 0x0018 + uint32_t(0x9603 << 16) //FD
Expand Down Expand Up @@ -4333,6 +4335,7 @@ const uint32_t kEffectiveTE = 0x0018 + (0x9082 << 16);
#define kBandwidthPerPixelPhaseEncode21 0x0021 + (0x1153 << 16) //FD
#define kCoilElements 0x0021 + (0x114F << 16) //LO
#define kAcquisitionMatrixText21 0x0021 + (0x1158 << 16) //SH
#define kImageTypeText 0x0021 + (0x1175 << 16) //CS
//Private Group 21 as used by GE:
#define kLocationsInAcquisitionGE 0x0021 + (0x104F << 16) //SS 'LocationsInAcquisitionGE'
#define kRTIA_timer 0x0021 + (0x105E << 16) //DS
Expand Down Expand Up @@ -6206,6 +6209,18 @@ const uint32_t kEffectiveTE = 0x0018 + (0x9082 << 16);
d.xyzMM[3] = dcmStrFloat(lLength, &buffer[lPos]);
d.zThick = d.xyzMM[3];
break;
case kImageTypeText: {
if (d.manufacturer != kMANUFACTURER_SIEMENS)
break;
dcmStr(lLength, &buffer[lPos], d.imageTypeText);
int slen = (int)strlen(d.imageTypeText);
if (slen > 1) {
for (int i = 0; i < slen; i++)
if (d.imageTypeText[i] == '\\')
d.imageTypeText[i] = '_';
}
break;
}
case kAcquisitionMatrixText21:
//fall through to kAcquisitionMatrixText
case kAcquisitionMatrixText: {
Expand Down Expand Up @@ -6493,6 +6508,18 @@ const uint32_t kEffectiveTE = 0x0018 + (0x9082 << 16);
//case kFrameAcquisitionDuration :
// frameAcquisitionDuration = dcmFloatDouble(lLength, &buffer[lPos], d.isLittleEndian); //issue369
// break;
case kArterialSpinLabelingContrast: { //CS
char st[kDICOMStr];
//aslFlags
dcmStr(lLength, &buffer[lPos], st);
if (strstr(st, "PSEUDOCONTINUOUS") != NULL)
d.aslFlags = (d.aslFlags | kASL_FLAG_GE_PSEUDOCONTINUOUS);
else if (strstr(st, "CONTINUOUS") != NULL)
d.aslFlags = (d.aslFlags | kASL_FLAG_GE_CONTINUOUS);
else if (strstr(st, "PULSED") != NULL)
d.aslFlags = (d.aslFlags | kASL_FLAG_GE_PULSED);
break;
}
case kASLPulseTrainDuration: {
d.postLabelDelay = dcmInt(4, &buffer[lPos], d.isLittleEndian);
break;
Expand Down
5 changes: 3 additions & 2 deletions console/nii_dicom.h
Original file line number Diff line number Diff line change
Expand Up @@ -50,7 +50,7 @@ extern "C" {
#define kCPUsuf " " //unknown CPU
#endif

#define kDCMdate "v1.0.20220707"
#define kDCMdate "v1.0.20220717"
#define kDCMvers kDCMdate " " kJP2suf kLSsuf kCCsuf kCPUsuf

static const int kMaxEPI3D = 1024; //maximum number of EPI images in Siemens Mosaic
Expand Down Expand Up @@ -133,6 +133,7 @@ static const int kMaxDTI4D = kMaxSlice2D; //issue460: maximum number of DTI dire
#define kASL_FLAG_GE_CONTINUOUS 8
#define kASL_FLAG_PHILIPS_CONTROL 16
#define kASL_FLAG_PHILIPS_LABEL 32
#define kASL_FLAG_GE_PULSED 64


//for spoiling 0018,9016
Expand Down Expand Up @@ -230,7 +231,7 @@ static const uint8_t MAX_NUMBER_OF_DIMENSIONS = 8;
float pixelPaddingValue; // used for both FloatPixelPaddingValue (0028, 0122) and PixelPaddingValue (0028, 0120); NaN if not present.
double acquisitionDuration, triggerDelayTime, RWVScale, RWVIntercept, dateTime, acquisitionTime, acquisitionDate, bandwidthPerPixelPhaseEncode;
char parallelAcquisitionTechnique[kDICOMStr], radiopharmaceutical[kDICOMStr], convolutionKernel[kDICOMStr], unitsPT[kDICOMStr], decayCorrection[kDICOMStr], attenuationCorrectionMethod[kDICOMStr],reconstructionMethod[kDICOMStr];
char prescanReuseString[kDICOMStr], imageOrientationText[kDICOMStr], pulseSequenceName[kDICOMStr], coilElements[kDICOMStr], coilName[kDICOMStr], phaseEncodingDirectionDisplayedUIH[kDICOMStr], imageBaseName[kDICOMStr], stationName[kDICOMStr], softwareVersions[kDICOMStr], deviceSerialNumber[kDICOMStr], institutionName[kDICOMStr], referringPhysicianName[kDICOMStr], instanceUID[kDICOMStr], seriesInstanceUID[kDICOMStr], studyInstanceUID[kDICOMStr], bodyPartExamined[kDICOMStr], procedureStepDescription[kDICOMStr], imageType[kDICOMStr], institutionalDepartmentName[kDICOMStr], manufacturersModelName[kDICOMStr], patientID[kDICOMStr], patientOrient[kDICOMStr], patientName[kDICOMStr], accessionNumber[kDICOMStr], seriesDescription[kDICOMStr], studyID[kDICOMStr], sequenceName[kDICOMStr], protocolName[kDICOMStr],sequenceVariant[kDICOMStr],scanningSequence[kDICOMStr], patientBirthDate[kDICOMStr], patientAge[kDICOMStr], studyDate[kDICOMStr],studyTime[kDICOMStr];
char prescanReuseString[kDICOMStr], imageOrientationText[kDICOMStr], pulseSequenceName[kDICOMStr], coilElements[kDICOMStr], coilName[kDICOMStr], phaseEncodingDirectionDisplayedUIH[kDICOMStr], imageBaseName[kDICOMStr], stationName[kDICOMStr], softwareVersions[kDICOMStr], deviceSerialNumber[kDICOMStr], institutionName[kDICOMStr], referringPhysicianName[kDICOMStr], instanceUID[kDICOMStr], seriesInstanceUID[kDICOMStr], studyInstanceUID[kDICOMStr], bodyPartExamined[kDICOMStr], procedureStepDescription[kDICOMStr], imageTypeText[kDICOMStr], imageType[kDICOMStr], institutionalDepartmentName[kDICOMStr], manufacturersModelName[kDICOMStr], patientID[kDICOMStr], patientOrient[kDICOMStr], patientName[kDICOMStr], accessionNumber[kDICOMStr], seriesDescription[kDICOMStr], studyID[kDICOMStr], sequenceName[kDICOMStr], protocolName[kDICOMStr],sequenceVariant[kDICOMStr],scanningSequence[kDICOMStr], patientBirthDate[kDICOMStr], patientAge[kDICOMStr], studyDate[kDICOMStr],studyTime[kDICOMStr];
char scanOptions[kDICOMStrLarge], institutionAddress[kDICOMStrLarge], imageComments[kDICOMStrLarge];
uint32_t dimensionIndexValues[MAX_NUMBER_OF_DIMENSIONS];
struct TCSAdata CSA;
Expand Down
43 changes: 33 additions & 10 deletions console/nii_dicom_batch.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -652,12 +652,14 @@ typedef struct {
float alFree[kMaxWipFree];
float adFree[kMaxWipFree];
float alTI[kMaxWipFree];
float dAveragesDouble, dThickness, ulShape, sPositionDTra, sNormalDTra;
float sPostLabelingDelay, ulLabelingDuration, dAveragesDouble, dThickness, ulShape, sPositionDTra, sNormalDTra;
} TCsaAscii;

void siemensCsaAscii(const char *filename, TCsaAscii *csaAscii, int csaOffset, int csaLength, float *shimSetting, char *coilID, char *consistencyInfo, char *coilElements, char *pulseSequenceDetails, char *fmriExternalInfo, char *protocolName, char *wipMemBlock) {
//reads ASCII portion of CSASeriesHeaderInfo and returns lEchoTrainDuration or lEchoSpacing value
// returns 0 if no value found
csaAscii->sPostLabelingDelay = 0.0;
csaAscii->ulLabelingDuration = 0.0;
csaAscii->TE0 = 0.0;
csaAscii->TE1 = 0.0;
csaAscii->delayTimeInTR = -0.001;
Expand Down Expand Up @@ -786,6 +788,10 @@ void siemensCsaAscii(const char *filename, TCsaAscii *csaAscii, int csaOffset, i
csaAscii->TE0 = readKeyFloatNan(keyStrTE0, keyPos, csaLengthTrim);
char keyStrTE1[] = "alTE[1]";
csaAscii->TE1 = readKeyFloatNan(keyStrTE1, keyPos, csaLengthTrim);
char keyStrPLD[] = "sAsl.sPostLabelingDelay[0]";
csaAscii->sPostLabelingDelay = readKeyFloatNan(keyStrPLD, keyPos, csaLengthTrim);
char keyStrLD[] = "sAsl.ulLabelingDuration";
csaAscii->ulLabelingDuration = readKeyFloatNan(keyStrLD, keyPos, csaLengthTrim);
//read ALL alTI[*] values
for (int k = 0; k < kMaxWipFree; k++)
csaAscii->alTI[k] = NAN;
Expand Down Expand Up @@ -1306,6 +1312,20 @@ tse3d: T2*/
fprintf(fp, "\", \"FIELDMAPHZ");
fprintf(fp, "\"],\n");
}
if (strlen(d.imageTypeText) > 0) {
fprintf(fp, "\t\"ImageTypeText\": [\"");
bool isSep = false;
for (size_t i = 0; i < strlen(d.imageTypeText); i++) {
if (d.imageTypeText[i] != '_') {
if (isSep)
fprintf(fp, "\", \"");
isSep = false;
fprintf(fp, "%c", d.imageTypeText[i]);
} else
isSep = true;
}
fprintf(fp, "\"],\n");
}
if (d.isDerived) //DICOM is derived image or non-spatial file (sounds, etc)
fprintf(fp, "\t\"RawImage\": false,\n");
if (d.seriesNum > 0)
Expand Down Expand Up @@ -1518,6 +1538,11 @@ tse3d: T2*/
bool isPCASL = false;
bool isPASL = false;
//ASL specific tags - 2D pCASL Danny J.J. Wang http://www.loft-lab.org
//ASL specific tags - 2D PASL Siemens Product XA30, n.b pasl/casl/pcasl set using 0018,9250
if (strstr(pulseSequenceDetails, "ep2d_asl")) {
json_FloatNotNan(fp, "\t\"LabelingDuration\": %g,\n", csaAscii.ulLabelingDuration * (1.0 / 1000000.0)); //usec->sec
json_FloatNotNan(fp, "\t\"PostLabelingDelay\": %g,\n", csaAscii.sPostLabelingDelay * (1.0 / 1000000.0)); //usec -> sec
}
if ((strstr(pulseSequenceDetails, "ep2d_pcasl")) || (strstr(pulseSequenceDetails, "ep2d_pcasl_UI_PHC"))) {
isPCASL = true;
repetitionTimePreparation = d.TR;
Expand Down Expand Up @@ -1781,15 +1806,13 @@ tse3d: T2*/
//generic public ASL tags
if (d.postLabelDelay > 0)
json_Float(fp, "\t\"PostLabelDelay\": %g,\n", float(d.postLabelDelay) / 1000.0);
//GE ASL specific tags
if (d.aslFlags & kASL_FLAG_GE_CONTINUOUS)
fprintf(fp, "\t\"ASLContrastTechnique\": \"CONTINUOUS\",\n");
if (d.aslFlags & kASL_FLAG_GE_PSEUDOCONTINUOUS)
fprintf(fp, "\t\"ASLContrastTechnique\": \"PSEUDOCONTINUOUS\",\n");
if (d.aslFlags & kASL_FLAG_GE_3DPCASL)
fprintf(fp, "\t\"ASLLabelingTechnique\": \"3D pulsed continuous ASL technique\",\n");
if (d.aslFlags & kASL_FLAG_GE_3DCASL)
fprintf(fp, "\t\"ASLLabelingTechnique\": \"3D continuous ASL technique\",\n");
//ASL BIDS tags
if ((d.aslFlags & kASL_FLAG_GE_CONTINUOUS) || (d.aslFlags & kASL_FLAG_GE_3DCASL))
fprintf(fp, "\t\"ArterialSpinLabelingType\": \"CASL\",\n");
if ((d.aslFlags & kASL_FLAG_GE_PSEUDOCONTINUOUS) || (d.aslFlags & kASL_FLAG_GE_3DPCASL))
fprintf(fp, "\t\"ArterialSpinLabelingType\": \"PCASL\",\n");
if (d.aslFlags & kASL_FLAG_GE_PULSED)
fprintf(fp, "\t\"ArterialSpinLabelingType\": \"PASL\",\n");
if (d.durationLabelPulseGE > 0) {
json_Float(fp, "\t\"LabelingDuration\": %g,\n", d.durationLabelPulseGE / 1000.0);
json_Float(fp, "\t\"PostLabelDelay\": %g,\n", d.TI / 1000.0); //For GE ASL: InversionTime -> Post-label delay
Expand Down

0 comments on commit 4f89073

Please sign in to comment.