DICOM @ OFFIS

Discussion Forum for OFFIS DICOM Tools - For registration, send email with desired user name to the OFFIS DICOM team
It is currently Wed, 2017-06-28, 01:20

All times are UTC + 1 hour




Post new topic Reply to topic  [ 17 posts ]  Go to page 1, 2  Next
Author Message
PostPosted: Thu, 2010-02-04, 17:35 
Offline

Joined: Tue, 2009-09-22, 12:57
Posts: 18
Location: DKFZ Heidelberg
This thread is intended to discuss questions arising from using the dcmrt pre-release version.

Note: This module is currently not publicly available. First public versions are expected for around April 2010.


Q1: How to deal with abnormal states?
The new logging mechanism has the following information states trace, debug, info, warn, error, fatal.
How shall fatal, error and warn be defined. What would be examples for error and fatal states?
Shall we associate certain actions to them?
Always exit or abort in case of fatal error?
Always return OFCondition false in case of error?


Q2: RT image and RT dose objects.
RT Image Tag (0028,0103) Pixel Representation is a type 1 tag and shall have the value Ox000F (PS3.3 - 2008, P503ff)
RT Dose Tag (0028,0103) Pixel Representation is either 0x0000H or 0x0001H.
In our development RT objects this tag seems to be 0 all the time.
Am i correct that this is in violation of the standard?
I am implementing some convenience functions to retrieve the image data from RT dose and image objects. Would it be safe to expect them to be always of type 16 bit unsigned integer ?


Top
 Profile  
 
 Post subject:
PostPosted: Thu, 2010-02-04, 21:16 
Offline
ICSMED DICOM Services
ICSMED DICOM Services

Joined: Fri, 2004-10-29, 21:38
Posts: 2217
Location: Oldenburg, Germany
Regarding Q1: Fatal errors should never occur within the library, also aborting from a library function is usually no good idea (unless there is a very good reason for it). Fatal errors are mainly intended for applications like the well-known DCMTK command line tools. See current snapshot for examples ...

From the API documentation of module "oflog":

Code:
  TRACE_LOG_LEVEL   trace: output more details on the internal application state, a kind of "verbose debug"
  DEBUG_LOG_LEVEL   debug: fine-grained informational events that are most useful to debug an application
  INFO_LOG_LEVEL    info: informational messages that highlight the progress of the application at coarse-grained level
  WARN_LOG_LEVEL    warn: potentially harmful situations
  ERROR_LOG_LEVEL   error: events that might still allow the application to continue running
  FATAL_LOG_LEVEL   fatal: very severe error events that will presumably lead the application to abort


Top
 Profile  
 
 Post subject:
PostPosted: Fri, 2010-02-05, 09:24 
Offline

Joined: Tue, 2009-09-22, 12:57
Posts: 18
Location: DKFZ Heidelberg
Fine, then let's agree on the following:

No fatal error messages from dcmrt. No calls to exit or abort from the library. The decision to abort is left to the application that uses dcmrt features.

Error messages in case of errors that will not allow an action to complete in a way that the user expects. In case of errors either an OFCondition bad or a OFBool OFFalse is returned.


Top
 Profile  
 
 Post subject:
PostPosted: Fri, 2010-02-05, 09:28 
Offline
ICSMED DICOM Services
ICSMED DICOM Services

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


Top
 Profile  
 
 Post subject:
PostPosted: Fri, 2010-02-05, 09:39 
Offline
ICSMED DICOM Services
ICSMED DICOM Services

Joined: Fri, 2004-10-29, 21:38
Posts: 2217
Location: Oldenburg, Germany
Regarding Q2:

Quote:
RT Image Tag (0028,0103) Pixel Representation is a type 1 tag and shall have the value Ox000F (PS3.3 - 2008, P503ff)

No, according to section C.8.8.2.6.6 (PS 3.3-2009) the value shall always be 0x0000 (i.e. unsigned integer).

Quote:
In our development RT objects this tag seems to be 0 all the time.
Am i correct that this is in violation of the standard?

No, this only means that your data is always unsigned.

Quote:
I am implementing some convenience functions to retrieve the image data from RT dose and image objects. Would it be safe to expect them to be always of type 16 bit unsigned integer ?

No, because for RT Dose objects the data can also be signed. And Bits Allocated can either be 8 or 16, and Bits Stored either 8 or 12-16 for RT Image objects. For RT Dose objects, Bits Allocated can even be 32.
Btw, for RT Image objects it might be appropriate to use the functionality provided by the dcmimgle module.


Top
 Profile  
 
 Post subject:
PostPosted: Wed, 2010-02-10, 19:52 
Offline

Joined: Tue, 2009-09-22, 12:57
Posts: 18
Location: DKFZ Heidelberg
As we dicussed before the mid-level API will consist of a set of C++ classes that will provide some additional functions that will allow to
access data from DICOM RT objects more easily.

Each mid-level API class is derived from a RT IOD classs like RT image.

Quote:
class DRTImage : public DRTImageIOD, public DRTObject
{

public:

//Constructor
DRTImage();
DRTImage(DcmDataset*);

*snip*

};


How to deal with sequence objects? First i thought it would be a good idea to provide some functions to access objects from sequences or more importantly nested sequences objects.

Some very simple inline functions like those

Code:
inline OFBool exposureSequenceExists() {return(getExposureSequence().isEmpty());}

inline Uint16 getNumberOfItemsInExposureSeqence(){return(getExposureSequence().getNumberOfItems());}

DRTExposureSequenceInRTImageIOD::Item& getItemFromExposureSequence(Uint16 n){return(getExposureSequence().getItem(n));}



On the other hand these functions have to be written for every of the many sequences and they simplify the code users of the mid-level API classes will write only a bit.
Do you think it is worth implementing such functions or not. :?:


Last edited by Oliver Nix on Thu, 2010-02-11, 09:47, edited 1 time in total.

Top
 Profile  
 
 Post subject:
PostPosted: Wed, 2010-02-10, 19:55 
Offline

Joined: Tue, 2009-09-22, 12:57
Posts: 18
Location: DKFZ Heidelberg
Jörg Riesmeier wrote:
Btw, for RT Image objects it might be appropriate to use the functionality provided by the dcmimgle module.


I will look into dcmimgle. Anything special in mind? I am not familiar with this module.


Top
 Profile  
 
 Post subject:
PostPosted: Thu, 2010-02-11, 08:25 
Offline
ICSMED DICOM Services
ICSMED DICOM Services

Joined: Fri, 2004-10-29, 21:38
Posts: 2217
Location: Oldenburg, Germany
For example, getOutputData() gives you a bitmap with the rendered output of the DICOM image, i.e. including all the transformations of the grayscale pipeline. The command line tool dcm2pnm shows what is possible with this module ...


Top
 Profile  
 
 Post subject:
PostPosted: Thu, 2010-02-11, 09:59 
Offline
ICSMED DICOM Services
ICSMED DICOM Services

Joined: Fri, 2004-10-29, 21:38
Posts: 2217
Location: Oldenburg, Germany
Quote:
Do you think it is worth implementing such functions or not.

I don't think that it makes sense to replicate the functionality of the low-level API. So, instead of your proposed "simple inline functions" I would rather go one level of abstraction up and define something like getNumberOfExposures() - but of course only for very important elements that are accessed quite often.


Top
 Profile  
 
 Post subject:
PostPosted: Thu, 2010-02-11, 10:07 
Offline

Joined: Tue, 2009-09-22, 12:57
Posts: 18
Location: DKFZ Heidelberg
You are right. DicomImage is powerful and better suited than directly extracting pixel data from the image object and storing it using C-Style arrays as part of DRTImage.
Providing image pixel data access by the the DRTImage class will also become obsolete since PixelData access / retrieval is handled by DicomImage.
A pointer to a DicomImage will be added to DRTImage. The DicomImage object can be constructed either by using a special DRTImage constructor or a member function getDicomImage( ... ).


Top
 Profile  
 
 Post subject:
PostPosted: Thu, 2010-02-11, 10:20 
Offline

Joined: Tue, 2009-09-22, 12:57
Posts: 18
Location: DKFZ Heidelberg
Jörg Riesmeier wrote:
Quote:
I would rather go one level of abstraction up and define something like getNumberOfExposures() - but of course only for very important elements that are accessed quite often.


I agree. The problem is to identify sequence elements accessed frequently. Since most of the sequences are type 3 it is not even clear which planning system writes which sequences and which not. I have only hands on data originating from one commercial planning system.
I will post a suggestion for RTImage and RTDose objects on that sometimes this week.

Shall we consider providing a small QT based demo programme (executable) ? Since QT is now LGPL QT would be a good choice.


Top
 Profile  
 
 Post subject:
PostPosted: Thu, 2010-02-11, 10:47 
Offline
ICSMED DICOM Services
ICSMED DICOM Services

Joined: Fri, 2004-10-29, 21:38
Posts: 2217
Location: Oldenburg, Germany
Quote:
Shall we consider providing a small QT based demo programme (executable) ? Since QT is now LGPL QT would be a good choice.

I like Qt pretty much, so go ahead! :-)


Top
 Profile  
 
 Post subject:
PostPosted: Fri, 2010-02-12, 16:49 
Offline

Joined: Tue, 2009-09-22, 12:57
Posts: 18
Location: DKFZ Heidelberg
Jörg Riesmeier wrote:
I would rather go one level of abstraction up and define something like getNumberOfExposures() - but of course only for very important elements that are accessed quite often.


I think the following approach makes sense:

Define access functions to sequence objects like this (example from RTDose):

Code:
OFList<DRTStructureSetROISequence::Item>  getDRTStructureSetROISequenceElements();
 DRTStructureSetROISequence::Item getDRTStructureSetROISequenceElement(Uint16);


Implementation:

Code:
OFList<DRTROIContourSequence::Item> DRTDose::getDRTROIContourSequenceElements()
{

  OFList<DRTROIContourSequence::Item> roi_contour_list;
  DRTROIContourSequence &seq = this->getROIContourSequence();
  if(!seq.isEmpty())
    {
    if (seq.gotoFirstItem().good())
      {
      do
       {         
        DRTROIContourSequence::Item &item = seq.getCurrentItem();
        roi_contour_list.push_back(item);
           
       }  while (seq.gotoNextItem().good());
      }
     }
 return roi_contour_list;

}

DRTROIContourSequence::Item DRTDose::getDRTROIContourSequenceElement(Uint16 element)
{
OFList<DRTROIContourSequence::Item> list = this->getDRTROIContourSequenceElements();

 size_t size = list.size();
 OFListIterator(DRTROIContourSequence::Item) start = list.begin();
 OFListIterator(DRTROIContourSequence::Item) stop = list.end();
 Uint16 ctr=0;
 DRTROIContourSequence::Item current = *start;
 while (start != stop)
   {
    if(element == ctr)
       break;
    ++start;
    ++ctr;
   }
 return current;
}



These functions will access the StructureSetROISequence objects. The first one returns a list of all objects of that kind found. The second returns one item from the sequence (selected by the position in the sequence).
So far this is straight forward.

For sequences containing sequences i propose the following:
One case is the RoiContourSequence where each item can contain another ContourSequence.

Code:
(3006,0039) SQ (Sequence with undefined length #=1)     # u/l, 1 ROIContourSequence
  (fffe,e000) na (Item with undefined length #=3)         # u/l, 1 Item
    (3006,002a) IS [0\0\255]                                #   8, 3 ROIDisplayColor
    (3006,0040) SQ (Sequence with undefined length #=1)     # u/l, 1 ContourSequence
      (fffe,e000) na (Item with undefined length #=3)         # u/l, 1 Item
        (3006,0042) CS [POINT]                                  #   6, 1 ContourGeometricType
        (3006,0046) IS [1]                                      #   2, 1 NumberOfContourPoints
        (3006,0050) DS [7.10\-174.70\-841.50]                   #  20, 3 ContourData
      (fffe,e00d) na (ItemDelimitationItem)                   #   0, 0 ItemDelimitationItem
    (fffe,e0dd) na (SequenceDelimitationItem)               #   0, 0 SequenceDelimitationItem
    (3006,0084) IS [1]                                      #   2, 1 ReferencedROINumber
  (fffe,e00d) na (ItemDelimitationItem)                   #   0, 0 ItemDelimitationItem
(fffe,e0dd) na (SequenceDelimitationItem)               #   0, 0 SequenceDelimitationItem




Here the following would handle the access


Code:
OFList<DRTContourSequence::Item> DRTDose::getDRTContourSequenceElements(Uint16);
 DRTContourSequence::Item DRTDose::getDRTContourSequenceElement(Uint16, Uint16);


Implementation:

Code:
OFList<DRTContourSequence::Item> DRTDose::getDRTContourSequenceElements(Uint16 RoiCSEelement)
{

DRTROIContourSequence::Item ROIContSequenceElement = getDRTROIContourSequenceElement(RoiCSEelement);
OFList<DRTContourSequence::Item> contour_list;
DRTContourSequence &seq = ROIContSequenceElement.getContourSequence();
  if(!seq.isEmpty())
    {
    if (seq.gotoFirstItem().good())
      {
      do
       {         
        DRTContourSequence::Item &item = seq.getCurrentItem();
        contour_list.push_back(item);
           
       }  while (seq.gotoNextItem().good());
      }
     }
 return contour_list;
}

DRTContourSequence::Item DRTDose::getDRTContourSequenceElement(Uint16 RoiCSEelement, Uint16 CSelement)
{

 OFList<DRTContourSequence::Item> list = getDRTContourSequenceElements(RoiCSEelement);
 size_t size = list.size();
 OFListIterator(DRTContourSequence::Item) start = list.begin();
 OFListIterator(DRTContourSequence::Item) stop = list.end();
 Uint16 ctr=0;
 DRTContourSequence::Item current = *start;
 while (start != stop)
   {
    if(CSelement == ctr)
       break;
    ++start;
    ++ctr;
   }
 return current;

}




This will create flat access points to sequence items of any kind. Of course the user still has to know the logical connections between the objects in nested sequences.

For tripple nested objects the logic would be
Code:
OFList<AnySequence::Item> DRTDose::getDRTAnySequenceElements(Uint16, Uint16);
 AnySequence::Item DRTDose::getDRTAnySequenceElement(Uint16, Uint16, Uint16);


Since these access functions are structurally always very similar i thought about using templated functions. But i am not sure if this is a clever idea or even possible.

What is your opinion.
Do you think the mid-API sequence handling of RT IODs is well addressed?


Top
 Profile  
 
 Post subject:
PostPosted: Fri, 2010-02-12, 17:17 
Offline
ICSMED DICOM Services
ICSMED DICOM Services

Joined: Fri, 2004-10-29, 21:38
Posts: 2217
Location: Oldenburg, Germany
I thought it would be better to go even further for the medium-level API: The sequence and item stuff is already handled by the low-level API. This shouldn't be replicated for the medium-level API!

When I wrote about having something like getNumberOfExposures() I was thinking that this method simply returns the number of exposure and the user does not need to know how this information is determined. In other words, the user of the medium-level API does not need to know about sequences and items or, more generally, about the internal structure of the DICOM information object.


Top
 Profile  
 
 Post subject:
PostPosted: Fri, 2010-02-12, 19:51 
Offline

Joined: Tue, 2009-09-22, 12:57
Posts: 18
Location: DKFZ Heidelberg
Quote:
In other words, the user of the medium-level API does not need to know about sequences and items or, more generally, about the internal structure of the DICOM information object.


In general i agree on that. The full information content is obviously located somehere in the DICOM object. Either as simple variables or as something derived from the objects structure,eg the number of objects in a sequence. The purpose of the mid level API is imho to hide the complexity of the underlying DICOM object from the person dealing with it. The low level DICOM objects (API) is driven by the internal structure of the object. Especially in case of nested sequences you have to have a quite thorough look in the DICOM standard to see how to brachiate to the information you need. Since it is such that the low level structures can anyhow be used and the mid level API classes can be ignored at all by the expert user, the simplified access point are something which can be used but must not be used at all. Programmers well familiar with dcmtk and DICOM will probably use the IODs directly because they are used to this model. The mid level API in my opinion serves two purposes. One ist to provide simplified access to data content regardless of where it is located. In other words to simplify the appearance of the object to the programmer. This was what i had in mind with the sequence item access functions. The nameing tells you what you can expect to get, no matter where it is located in the internal data structures. I agree, it is a replication, but also a simplification. The second purpose is a logical grouping of information and a processing of information to calculate derived information frequently used. An expample for that would be the scaling of dose pixel values into units of gray. This second purpose was not addressed so far. Presently the attention is put on simplifying data access. For example by providing a constructor for any of the IODs taking a filename or a dcmdataset and to extract the structures and image values into a data structure easily transferable to other tools and libraries for further processing, like visualization.
Let's discuss the processing part of the API in the weeks to come.


Top
 Profile  
 
Display posts from previous:  Sort by  
Post new topic Reply to topic  [ 17 posts ]  Go to page 1, 2  Next

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