DICOM @ OFFIS

Discussion Forum for OFFIS DICOM Tools - For registration, send email with desired user name to the OFFIS DICOM team
It is currently Tue, 2017-11-21, 07:16

All times are UTC + 1 hour




Post new topic Reply to topic  [ 6 posts ] 
Author Message
PostPosted: Thu, 2009-10-01, 14:53 
Offline

Joined: Tue, 2009-09-22, 12:57
Posts: 18
Location: DKFZ Heidelberg
I like to access elements from nested sequences for example from a DICOM RT structure set file.

Let's say the structure of the file is as follows (dcmdump output)
Code:
3006,0010) SQ (Sequence with undefined length #=2)     # u/l, 1 ReferencedFrameOfReferenceSequence
  (fffe,e000) na (Item with undefined length #=3)         # u/l, 1 Item
    (0020,0052) UI [1.3.12.2.1107.5.1.4.28334.4.0.4556475228861625] #  46, 1 FrameOfReferenceUID
    (3006,0012) SQ (Sequence with undefined length #=1)     # u/l, 1 RTReferencedStudySequence
      (fffe,e000) na (Item with undefined length #=3)         # u/l, 1 Item
        (0008,1150) UI =StudyComponentManagementSOPClass        #  24, 1 ReferencedSOPClassUID
        (0008,1155) UI [1.2.840.113619.6.95.31.0.3.4.1.6013.13.2863364] #  46, 1 ReferencedSOPInstanceUID
        (3006,0014) SQ (Sequence with undefined length #=1)     # u/l, 1 RTReferencedSeriesSequence
          (fffe,e000) na (Item with undefined length #=2)         # u/l, 1 Item
            (0020,000e) UI [1.3.12.2.1107.5.1.4.28334.4.0.4556490411543837] #  46, 1 SeriesInstanceUID
            (3006,0016) SQ (Sequence with undefined length #=62)    # u/l, 1 ContourImageSequence
              (fffe,e000) na (Item with undefined length #=2)         # u/l, 1 Item
                (0008,1150) UI =CTImageStorage                          #  26, 1 ReferencedSOPClassUID
                (0008,1155) UI [1.3.6.1.4.1.2452.6.3787560449.1085974281.3785418625.1983991160] #  62, 1 ReferencedSOPInstanceUID
              (fffe,e00d) na (ItemDelimitationItem)                   #   0, 0 ItemDelimitationItem
.........
        (fffe,e0dd) na (SequenceDelimitationItem)               #   0, 0 SequenceDelimitationItem
          (fffe,e00d) na (ItemDelimitationItem)                   #   0, 0 ItemDelimitationItem
        (fffe,e0dd) na (SequenceDelimitationItem)               #   0, 0 SequenceDelimitationItem
      (fffe,e00d) na (ItemDelimitationItem)                   #   0, 0 ItemDelimitationItem
    (fffe,e0dd) na (SequenceDelimitationItem)               #   0, 0 SequenceDelimitationItem
    (3006,00c0) SQ (Sequence with undefined length #=14)    # u/l, 1 FrameOfReferenceRelationshipSequence


The dcmtk generic way to retrieve this data would be

Code:
     DcmSequenceOfItems *image_sequence = NULL;
     OFCondition cond;
     cond = dset->findAndGetSequence(DCM_ContourImageSequence, image_sequence, true);
     if (cond.good())
      {
       
       Uint16 numimages = image_sequence->card();       
       for(int i=0;i<numimages;i++)
         {
          DcmItem *image_item = image_sequence->getItem(i);
          OFString SOPclassUID,SOPinstanceUID;
          OFString &SOPclassUID_r =SOPclassUID;
          OFString &SOPinstanceUID_r = SOPinstanceUID;
          image_item->findAndGetOFString(DCM_ReferencedSOPInstanceUID,SOPinstanceUID_r);
          image_item->findAndGetOFString(DCM_ReferencedSOPClassUID,SOPclassUID_r);
         } // end numimages
   
     } // end cond.good


I would now like to access the same data using the dcmrt DRTStructureSetIOD.
The following code seems to me the "logical" way.

Code:
 DRTReferencedFrameOfReferenceSequence &seq = this->getReferencedFrameOfReferenceSequence();
 Uint16 num_items = seq.getNumberOfItems();
 for(int i = 0; i <= num_items; i++)
  {
   DRTReferencedFrameOfReferenceSequence::Item &item = seq.getItem(i);
   OFString uid;
   item.getFrameOfReferenceUID(uid);
   DRTRTReferencedStudySequence &rss_ref = item.getRTReferencedStudySequence();   
   for (int j = 0; j <= rss_ref.getNumberOfItems(); j++)
    {
     DRTRTReferencedStudySequence::Item &rss_item = rss_ref.getItem(j);
     DRTRTReferencedSeriesSequence &ref_series_seq = rss_item.getRTReferencedSeriesSequence();
     Uint16 nssitem = ref_series_seq.getNumberOfItems();
     for (int k = 0; k <= nssitem; k++)
      {
       DRTRTReferencedSeriesSequence::Item &ref_series_seq_item = ref_series_seq.getItem(k);
       DRTContourImageSequence &image_sequence = ref_series_seq_item.getContourImageSequence();   
       Uint16 num_images_in_sequence = image_sequence.getNumberOfItems();
       for (int l = 0; l <= num_images_in_sequence; l++)
       {
        DRTContourImageSequence::Item &image_contour_item = image_sequence.getItem(l);
        OFString refframenum;
        image_contour_item.getReferencedFrameNumber(refframenum);
        std::cout << refframenum << std::endl;
       }
     }
    }
  }

This code compiles but the returned items are always empty (EmptyDefaultItem = true) All variables remain empty. Number od items in the list is 0, instead of 1 or many in case of the ContourImageSequence.
Why is that? Is the logic of accessing the nested sequences wrong?
For the mid level API i would rather like to use the second style of data access because it reflects the data organisation and hierachy.


Top
 Profile  
 
 Post subject:
PostPosted: Thu, 2009-10-01, 16:20 
Offline
ICSMED DICOM Services
ICSMED DICOM Services

Joined: Fri, 2004-10-29, 21:38
Posts: 2217
Location: Oldenburg, Germany
The good thing: I checked your code and it works in principle :-)

First of all, you should use "<" instead of "<=" in the for-loops because the item index starts with 0 and ends with number of items minus 1.
Then, are you sure that your sample file really contains a value for ReferencedFrameNumber for the individual items? In my sample file, this data element was missing and, therefore, I used ReferencedSOPClassUID for testing purposes.

Btw, in order to iterate over all items of a sequence you should probably better use the methods gotoFirstItem() and gotoNextItem() of the particular sequence class in a do-while-loop. It's probably a little more efficient and elegant :-) Here's an example:

Code:
    if (seq.gotoFirstItem().good())
    {
        do
        {
            /* e.g. use seq.getCurrentItem() ... */
        } while (seq.gotoNextItem().good());
    }


Top
 Profile  
 
 Post subject:
PostPosted: Fri, 2009-10-02, 11:49 
Offline

Joined: Tue, 2009-09-22, 12:57
Posts: 18
Location: DKFZ Heidelberg
Using the iterator is a good idea.

The code looks now like this

Code:

 if (seq.gotoFirstItem().good())
  {
   do
    {
     DRTReferencedFrameOfReferenceSequence::Item &item = seq.getCurrentItem();
     OFString uid;
     item.getFrameOfReferenceUID(uid);
     DRTRTReferencedStudySequence &reference_study_sequence_ref = item.getRTReferencedStudySequence();   
     do
      {
       DRTRTReferencedStudySequence::Item &rss_item = reference_study_sequence_ref.getCurrentItem();
       DRTRTReferencedSeriesSequence &series_seq_ref = rss_item.getRTReferencedSeriesSequence();
       do
        {
         DRTRTReferencedSeriesSequence::Item &ref_series_seq_item = series_seq_ref.getCurrentItem();
         DRTContourImageSequence &image_sequence_seq_ref = ref_series_seq_item.getContourImageSequence();   
         do
         {
           DRTContourImageSequence::Item &image_contour_item = image_sequence_seq_ref.getCurrentItem();
           OFString refframenum;
           image_contour_item.getReferencedFrameNumber(refframenum);
           std::cout << refframenum << std::endl;
         } while (image_sequence_seq_ref.gotoNextItem().good());       
        } while (series_seq_ref.gotoNextItem().good());
      } while (reference_study_sequence_ref.gotoNextItem().good());
    } while (seq.gotoNextItem().good());
   } // end if seq.first item


Nevertheless i cannot access any valid item from the nested sequences. Still read try to read the structure set file posted above.
It is this one, in case you like to try
" ...\\Phantom\\RS1.3.6.1.4.1.2452.6.2951369053.1166013784.715527822.1679960.dcm"


Code:
DRTReferencedFrameOfReferenceSequence &seq = this->getReferencedFrameOfReferenceSequence();


Fine, i get a list with one valid item, as expected.

Code:
DRTRTReferencedStudySequence &reference_study_sequence_ref = item.getRTReferencedStudySequence();   


Again, fine. A list with one valid item.

Code:
  DRTRTReferencedSeriesSequence &series_seq_ref = rss_item.getRTReferencedSeriesSequence();


The list is empty, but should contain again 1 element
Same of course true for the lists from the two other do loops.

I investigated the problem a bit further and i think it is related to the read method of the DRTStructureSetIOD.

I do the following:
Code:
   DRTStructureSet *structure_set = new DRTStructureSet(d);
   DcmItem &ditem = dynamic_cast<DcmItem&>(*dset);
   structure_set->read(ditem);


DRTStructureSet is a mid level API class. It is derived from DRTStructureSetIOD.

In

Code:
OFCondition DRTStructureSetIOD::read(DcmItem &dataset)
{
.....

ReferencedFrameOfReferenceSequence.read(dataset, "1-n", "3", LogStream, "StructureSetModule");

}


is called.

Code:
OFCondition DRTReferencedFrameOfReferenceSequence::read(DcmItem &dataset,
                                                        const OFString &card,
                                                        const OFString &type,
                                                        OFConsole *stream,
                                                        const char *moduleName)
{
....

  if (checkElementValue(*sequence, card, type, stream, result, moduleName))
            {
     
.....
                while (result.good() && sequence->nextObject(stack, first /*intoSub*/).good())
                {
                    DcmItem *ditem = OFstatic_cast(DcmItem *, stack.top());

.....
                        SequenceOfItems.push_back(item);
.....
               }         
            }
....
}


If i understand the code correctly all items in the sequence are read and appended to the sequence list. If the sequence itself contains another sequence this other sequence is not considered, since no read for the nested sequence objects is called. Dcmrt classes representing objects from the nested sequences exists and read methods are implemented, but never called from "outer" sequence items read methods.

It would be possible to read the missing sequences elements from the mid-level API class. Nevertheless i think it would be better to extend the read method in the e.g. DRTReferencedFrameOfReferenceSequence class, because otherwise the IOD has a potentially incomplete data content and might cause problems for those using the low level RT information objects.

Have i understood the situation correctly? How to deal with it?


Top
 Profile  
 
 Post subject:
PostPosted: Fri, 2009-10-02, 14:00 
Offline
ICSMED DICOM Services
ICSMED DICOM Services

Joined: Fri, 2004-10-29, 21:38
Posts: 2217
Location: Oldenburg, Germany
Hmm, the read() method of the item is called:

Code:
OFCondition DRTReferencedFrameOfReferenceSequence::read(DcmItem &dataset,
                                                        const OFString &card,
                                                        const OFString &type,
                                                        const char *moduleName)
{
    /* ... */
                            result = item->read(*ditem);
                            if (result.good())
                            {
                                /* append new item to the end of the list */
                                SequenceOfItems.push_back(item);
                                first = OFFalse;
                            }
    /* ... */
}


Btw, you need to call gotoFirstItem() for the other sequences, too. And, there probably is no ReferencedFrameNumber in your test dataset :-)


Top
 Profile  
 
 Post subject:
PostPosted: Fri, 2009-10-02, 15:33 
Offline

Joined: Tue, 2009-09-22, 12:57
Posts: 18
Location: DKFZ Heidelberg
Hello Jörg, further debuging reveals that indeed all read methods are called properly.
The problem was located in my code. gotoFirstItem() has to be called before entering each of the do loops and subsequent calls to getCurrentItem. It seems that the iterator was not set to a valid item or not initialized properly when getCurrentItem was called.
This was a classic from bug world :roll:

This code behaves as expected


Code:
 if (seq.gotoFirstItem().good())
  {
   do
    {
     DRTReferencedFrameOfReferenceSequence::Item &item = seq.getCurrentItem();
     OFString uid;
     item.getFrameOfReferenceUID(uid);
     DRTRTReferencedStudySequence &reference_study_sequence_ref = item.getRTReferencedStudySequence();   
     if (reference_study_sequence_ref.gotoFirstItem().good())
      {
       do
       {
        DRTRTReferencedStudySequence::Item &rss_item = reference_study_sequence_ref.getCurrentItem();
        DRTRTReferencedSeriesSequence &series_seq_ref = rss_item.getRTReferencedSeriesSequence();
        if (series_seq_ref.gotoFirstItem().good())
        {
         do
         {
          DRTRTReferencedSeriesSequence::Item &ref_series_seq_item = series_seq_ref.getCurrentItem();
          DRTContourImageSequence &image_sequence_seq_ref = ref_series_seq_item.getContourImageSequence();   
          if (image_sequence_seq_ref.gotoFirstItem().good())
          {
           do
           {
             DRTContourImageSequence::Item &image_contour_item = image_sequence_seq_ref.getCurrentItem();
             OFString refSOPInstUID;
             image_contour_item.getReferencedSOPInstanceUID(refSOPInstUID);
             std::cout << refSOPInstUID << std::endl;
           } while (image_sequence_seq_ref.gotoNextItem().good());
          } // end if image_sequence_seq_ref
         } while (series_seq_ref.gotoNextItem().good());
        } // end if series_seq_ref good
       } while (reference_study_sequence_ref.gotoNextItem().good());
     } // end if reference_study_sequence_ref good
    } while (seq.gotoNextItem().good());
  } // end if seq.first item

[/code]


Top
 Profile  
 
 Post subject:
PostPosted: Fri, 2009-10-02, 16:28 
Offline
ICSMED DICOM Services
ICSMED DICOM Services

Joined: Fri, 2004-10-29, 21:38
Posts: 2217
Location: Oldenburg, Germany
Great :-)

Btw, why are you using the following type cast?

Code:
DcmItem &ditem = dynamic_cast<DcmItem&>(*dset);


It should be noted that DCMTK does not (and will probably never) use RTTI, Exceptions and the like. Also instead of using "std::cout" and "std::endl" you should better use "COUT" (in applications) or "OFConsole::lockCout()" (in libraries) and "OFendl". This will allow for compiling the toolkit on more platforms ...

You might also be interested in the fact that we are currently completely reworking the error reporting (i.e. logging) of the DCMTK. See new oflog module which is already part of the latest version of the DCMRT module package. So the setLogStream() method will disappear with the next pre-release of the DCMRT package. A modified version of this module is already in our CVS repository -- since today :-)


Top
 Profile  
 
Display posts from previous:  Sort by  
Post new topic Reply to topic  [ 6 posts ] 

All times are UTC + 1 hour


Who is online

Users browsing this forum: No registered users and 1 guest


You cannot post new topics in this forum
You cannot reply to topics in this forum
You cannot edit your posts in this forum
You cannot delete your posts in this forum

Search for:
Jump to:  
Powered by phpBB® Forum Software © phpBB Group