Search code examples
c++image-processingdicomdcmtkmedical-imaging

DCMTK library says tag not found while dcmdump displays it


My program, for a Ubuntu 20 system using DCMTK 3.6.4-2, reads a dicom file (series) and gets the scale slopes from the corresponding tags, testing first whether they exist:

tmpfile.loadFile ( filename );
tmpdata = tmpfile.getDataset();

tmpdata -> findAndGetOFString ( DCM_RescaleSlope, tmpstring );
if ( !tmpstring.empty() ) {
   mydcm.scl_slope = static_cast <float> ( stof ( std::string ( tmpstring.c_str() ) ) );
   tmpdata -> findAndGetOFString ( DCM_RescaleIntercept, tmpstring );
   mydcm.scl_inter = static_cast <float> ( stof ( std::string ( tmpstring.c_str() ) ) );
} else {
   tmpdata -> findAndGetOFString ( DCM_RealWorldValueSlope, tmpstring );
   mydcm.scl_slope = static_cast <float> ( stof ( std::string ( tmpstring.c_str() ) ) );
   tmpdata -> findAndGetOFString ( DCM_RealWorldValueIntercept, tmpstring );
   mydcm.scl_inter = static_cast <float> ( stof ( std::string ( tmpstring.c_str() ) ) );
}

In the file I use, dcmdump does not return anything for the DCM_RescaleSlope tag, but it does for the DCM_RealWorldValueSlope tag:

dcmdump filename | grep RealWorld
(0040,9096) SQ (Sequence with undefined length #=1) # u/l, 1 RealWorldValueMappingSequence
(0040,9224) FD 0                                    #   8, 1 RealWorldValueIntercept
(0040,9225) FD 4.1318681318681323                   #   8, 1 RealWorldValueSlope

When I use

std::cout << getItemString ( tmpdata, DCM_RescaleSlope )        << std::endl; 
std::cout << getItemString ( tmpdata, DCM_RealWorldValueSlope ) << std::endl;
std::cout << getItemString ( tmpdata, DCM_Modality )            << std::endl;

(where getItemString is a function for convenience) and use the debugger to look what happens in the second line, then:

  1. the tag is converted to g = 64, e = 37413
  2. the line OFStatus s = theCondition.theStatus; is set to false (that's all there is, apart from theText = "Tag not found")

which I don't understand because (i) the value of the tag is known by the program -- otherwise it wouldn't compile, (ii) the tag is in the image as shown by dcmdump and (iii) other tags are processed OK: DCM_Modality is printed as MR.

Am I doing something wrong, or do these special tags need special treatment?

EDIT

I tried one suggestion from the comments: using findAndGetFloat64 instead of findAndGetOFString, in a test

double tmpdbl;
tmpdata -> findAndGetFloat64 ( DCM_RescaleSlope, tmpdbl );
std::cout << "A" << tmpdbl << std::endl;
tmpdata -> findAndGetFloat64 ( DCM_RealWorldValueSlope, tmpdbl );
std::cout << "B" << tmpdbl << std::endl;
tmpdata -> findAndGetFloat64 ( DCM_AcquisitionDuration, tmpdbl );
std::cout << "C" << tmpdbl << std::endl;

which resulted in

A0
B0
C297.6

and the error in theStatus during the line for reading B is still the same: theText = "Tag not found"

(I had been using strings because most of the double-valued tags so far had been DS, thanks for making me aware of the difference!)


Solution

  • The tag you are looking for is inside a sequence (RealWorldValueMappingSequence). To get to the tag, you first have to get the sequence, something like this:

    DcmSequenceOfItems* sequence;
    OFResult result = tmpData->findAndGetSequence(DCM_RealWorldValueMappingSequence, sequence);
    if (result.good() && sequence && !sequence->isEmpty())
    {
      DcmItem* item = sequence->getItem(0); // you may have to iterate over the items instead
      double value;
      result = item->findAndGetFloat64(DCM_RealWorldValueSlope, value);
      ...
    }
    

    Note that this is out of my head, so it may not be accurate, but this is basically what you need to do to get the tags inside of sequence items. All findAndGet... methods by default only work on the current item, which may be the root dataset, but may also be a sequence item - you have to check in the DICOM standard where actually to find the tags.

    UPDATE:
    Having written that, I just realized that I forgot about the searchIntoSub parameter, so probably it would be sufficient to do:

      result = tmpData->findAndGetFloat64(DCM_RealWorldValueSlope, value, 0, OFTrue);
    

    where searchIntoSub=OfTrue means, that contained sequences are also searched. I leave the above anyway, as it may also be needed by someone else if searching for tags in specific sequences.

    Side note:
    Your first approach to use findAndGetOFString should also work, if you use this parameter. It does not make sense in your case, as you need the actual double value, but it can be used if you need only the string representation of the tag values. From the documentation for findAndGetOFString:

    Applicable to the following VRs: AE, AS, AT, CS, DA, DS, DT, FL, FD, IS, LO, LT, OB, OD, OF, OL, OV, OW, PN, SH, SL, SS, ST, SV, TM, UC, UI, UL, UR, US, UT, UV