瀏覽代碼

Renamed ctkDICOMDataset to ctkDICOMItem

A fallback header and typedef were added to support old code
Marco Nolden 12 年之前
父節點
當前提交
5ec3f747df
共有 4 個文件被更改,包括 356 次插入341 次删除
  1. 2 1
      Libs/DICOM/Core/CMakeLists.txt
  2. 8 258
      Libs/DICOM/Core/ctkDICOMDataset.h
  3. 82 82
      Libs/DICOM/Core/ctkDICOMDataset.cpp
  4. 264 0
      Libs/DICOM/Core/ctkDICOMItem.h

+ 2 - 1
Libs/DICOM/Core/CMakeLists.txt

@@ -30,13 +30,14 @@ set(KIT_SRCS
   ctkDICOMAbstractThumbnailGenerator.h
   ctkDICOMDatabase.cpp
   ctkDICOMDatabase.h
-  ctkDICOMDataset.cpp
   ctkDICOMDataset.h
   ctkDICOMFilterProxyModel.cpp
   ctkDICOMFilterProxyModel.h
   ctkDICOMIndexer.cpp
   ctkDICOMIndexer.h
   ctkDICOMIndexer_p.h
+  ctkDICOMItem.cpp
+  ctkDICOMItem.h
   ctkDICOMModel.cpp
   ctkDICOMModel.h
   ctkDICOMPersonName.cpp

+ 8 - 258
Libs/DICOM/Core/ctkDICOMDataset.h

@@ -1,260 +1,10 @@
-/*=========================================================================
-
-  Library:   CTK
-
-  Copyright (c) Mint Medical GmbH
-
-  Licensed under the Apache License, Version 2.0 (the "License");
-  you may not use this file except in compliance with the License.
-  You may obtain a copy of the License at
-
-      http://www.apache.org/licenses/LICENSE-2.0.txt
-
-  Unless required by applicable law or agreed to in writing, software
-  distributed under the License is distributed on an "AS IS" BASIS,
-  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-  See the License for the specific language governing permissions and
-  limitations under the License.
-
-=========================================================================*/
-
-#ifndef __ctkDICOMDataset_h
-#define __ctkDICOMDataset_h
-
-#include "ctkDICOMCoreExport.h"
-
-#include "ctkDICOMPersonName.h"
-
-#include <dcdatset.h> // DCMTK DcmDataset
-
-#include <QtCore>
-
-class DcmDataDictionary;
-
-
-class ctkDICOMDatasetPrivate;
-
-/// \ingroup DICOM_Core
-///
-///  \brief Base class for DICOM objects and interface with DICOM toolkit DCMTK.
-///
-///  This class serves as the base class for all DICOM objects (patient, study, series, image).
-///
-///  The class is derived from DcmDataset, the data type that is used by the DICOM toolkit when
-///  reading an image file or formulating a message request or receiving a message response (e.g. C-FIND).
-///
-///  Basically it offers a lot of convenience methods for subclasses to read and write DICOM attributes
-///  using Qt types. It is the subclasses responsibility to use the correct data types as defined in DICOM.
-///
-///  \note ONLY the Get.. methods should be used to access the internal dataset.
-///
-///  When reading string type attributes (LO, LT, PN, SH, ST, UT), we consider the "specific character set"
-///  tag and decode the stored string using the correct encoding (using Qt methods). This allows to
-///  display e.g. person names written in arabic, hebrew, greek, russian, etc. letters.
-///
-///  \warning Right now, asian phonetic strings cause problems. We have to learn the concept.
-///  \warning Helpers for writing DICOM attributes are not yet implemented. Implementation is straightforward though and can be done when necessary.
-///  \warning DateTime objects ignore the timezone at the moment. This is however of secondary importance.
-///
-///  A subclass could possibly want to store the internal DcmDataset.
-///  For this purpose, the internal DcmDataset is serialized into a memory buffer using DcmDataset::write(..). This buffer
-///  is stored in a base64 encoded string. For deserialization we decode the string and use DcmDataset::read(..).
-class CTK_DICOM_CORE_EXPORT ctkDICOMDataset
-{
-public:
-    typedef QObject Superclass;
-    ///
-    /// \brief Create an empty object. This has to be initialized by one of
-    /// the InitializeFrom... methods before it can be used.
-    ///
-    /// @param strictErrorHandling If set to false (the default) only critical errors throw exceptions.
-    ctkDICOMDataset(bool strictErrorHandling = false);
-    virtual ~ctkDICOMDataset();
-
-    /// \brief For initialization from a DcmDataset in a constructor / assignment.
-    ///
-    /// This method should be overwritten by all derived classes. It should
-    /// be called from the constructor or assignment operators when the class
-    /// should copy information from a DcmDataset object.
-
-    /// \warning Derived classes must call PDICOMDataset::InitializeFromDataset(...) to correctly copy encoding information.
-    virtual void InitializeFromItem(DcmItem* dataset, bool takeOwnership = false);
-
-    ///
-    /// \brief For initialization from file in a constructor / assignment.
-    ///
-    virtual void InitializeFromFile(const QString& filename,
-                    const E_TransferSyntax readXfer = EXS_Unknown,
-                    const E_GrpLenEncoding groupLength = EGL_noChange,
-                    const Uint32 maxReadLength = DCM_MaxReadLength,
-                    const E_FileReadMode readMode = ERM_autoDetect);
-
-
-
-    /// \brief Save dataset to file
-    ///
-    /// \returns true on success.
-    bool SaveToFile(const QString& filePath) const;
-
-
-    /// \brief Store a string representation of the object to a database field.
-    ///
-    /// The internal DcmDataset is serialized into a memory buffer using DcmDataset::write(..).
-    /// To store the memory buffer in a simple string database field, we convert it to a base64 encoded string.
-    /// Doing so prevents errors from encoding conversions that could be made by QString or the database etc.
-    void Serialize();
-
-    /// \brief Restore the object from a string representation in a database field.
-    ///
-    /// The database stored string is base64 decoded into a memory buffer. Then
-    /// the internal DcmDataset is created using DcmDataset::read(..).
-    void Deserialize();
-
-
-    /// \brief To be called from InitializeData, flags status as dirty.
-    ///
-    /// This is to allow data to be read as late as possible. All the
-    /// Get/SetElement... methods ensure initialization, which checks this flag.
-    void MarkForInitialization();
-
-
-    /// \brief Is this dataset initialized ?
-    bool IsInitialized() const;
-
-    ///
-    /// \brief Called by all Get/Set methods to initialize DcmDataSet if needed.
-    ///
-    void EnsureDcmDataSetIsInitialized() const;
-
-
-    /// \brief Find element in dataset and copy it into internal DcmDataset
-    ///
-    /// Attribute types 1, 1C, 2, 2C, 3 as defined in DICOM can be encoded as
-    /// hex values 0x1, 0x1C, 0x2, 0x2C, 0x3.
-    ///
-    /// Conditional attributes are considered MUST attributes. The calling
-    /// function shall test the conditions before calling CopyElement
-    /// (since conditions might be complex).
-    bool CopyElement( DcmDataset* dataset, const DcmTagKey& tag, int type );
-
-    /// \brief creates a QString from the OFString, respecting the "specific character set" of the Dataset.
-    ///
-    /// This method checks if the dataset has an attribute "specific character set".
-    /// If so, all attributes of types Long String (LO), Long Text (LT), Person Name (PN), Short String (SH),
-    /// Short Text (ST), Unlimited Text (UT) should be interpreted as encoded with a special set.
-    ///
-    /// See implementation for details.
-    QString Decode(const DcmTag& tag, const OFString& raw) const;
-
-    /// \brief creates an OFString from the QtString
-    ///
-    /// \warning The method currently assumes that the encoding of the passed string if latin1 and converts
-    /// it accordingly. The passed DICOM tag is not yet evaluated to determine the actual encoding type.
-    OFString Encode(const DcmTag& tag, const QString& qstring) const;
-
-    ///
-    /// \brief A const-correct version of DcmDataset::findAndGetElement.
-    ///
-    OFCondition findAndGetElement(const DcmTag& tag, DcmElement*& element, const OFBool searchIntoSub=OFFalse) const; // DCMTK is not const-correct
-
-    ///
-    ///  \brief A const-correct version of DcmDataset::findAndGetOFString.
-    ///
-    OFCondition findAndGetOFString(const DcmTag& tag, OFString& value, const unsigned long pos = 0, const OFBool searchIntoSub=OFFalse) const; // DCMTK is not const-correct
-
-    static bool CheckCondition(const OFCondition&);
-
-    ///
-    /// \brief Get-methods for for all subtypes of DcmByteString
-    ///
-    QString          GetAllElementValuesAsString( const DcmTag& tag ) const;
-    QString                   GetElementAsString( const DcmTag& tag, unsigned long pos = 0 ) const;
-    QStringList           GetElementAsStringList( const DcmTag& tag ) const;
-    ctkDICOMPersonName           GetElementAsPersonName( const DcmTag& tag, unsigned long pos = 0 ) const;
-    ctkDICOMPersonNameList   GetElementAsPersonNameList( const DcmTag& tag ) const;
-    QDate                       GetElementAsDate( const DcmTag& tag, unsigned long pos = 0 ) const;
-    QTime                       GetElementAsTime( const DcmTag& tag, unsigned long pos = 0 ) const;
-    double                    GetElementAsDouble( const DcmTag& tag, unsigned long pos = 0 ) const; // type DS
-    long                     GetElementAsInteger( const DcmTag& tag, unsigned long pos = 0 ) const; // type IS
-    int                  GetElementAsSignedShort( const DcmTag& tag, unsigned long pos = 0 ) const; // type SS
-    int                GetElementAsUnsignedShort( const DcmTag& tag, unsigned long pos = 0 ) const; // type US
-
-    ///
-    /// \warning IGNORES TIME ZONE at the moment!
-    ///
-    QDateTime       GetElementAsDateTime( const DcmTag& tag, unsigned long pos = 0 ) const;
-
-
-    ///
-    /// \brief Set-methods for for all subtypes of DcmByteString
-    ///
-    bool SetElementAsString( const DcmTag& tag, QString string );
-    bool SetElementAsStringList( const DcmTag& tag, QStringList stringList ); //> Currently not implemented
-    bool SetElementAsPersonName( const DcmTag& tag, ctkDICOMPersonName personName );
-    bool SetElementAsPersonNameList( const DcmTag& tag, ctkDICOMPersonNameList personNameList ); //> Currently not implemented
-    bool SetElementAsDate( const DcmTag& tag, QDate date );
-    bool SetElementAsTime( const DcmTag& tag, QTime time );
-    bool SetElementAsDateTime( const DcmTag& tag, QDateTime dateTime );
-    bool SetElementAsInteger( const DcmTag& tag, long value, unsigned long pos = 0 ); // type IS
-    bool SetElementAsSignedShort( const DcmTag& tag, int value, unsigned long pos = 0 ); // type SS
-    bool SetElementAsUnsignedShort( const DcmTag& tag, int value, unsigned long pos = 0 ); // type US
-
-
-    /// Some convenience getter
-    QString GetStudyInstanceUID() const;
-    QString GetSeriesInstanceUID() const;
-    QString GetSOPInstanceUID() const;
-
-    ///
-    /// \brief Get a human-readable version of patient position enumerations used e.g. in DICOM series.
-    ///
-    static QString TranslateDefinedTermPatientPosition( const QString& dt );
-
-    ///
-    /// \brief Get a human-readable version of modality enumerations used e.g. in DICOM series.
-    ///
-    static QString TranslateDefinedTermModality( const QString& dt );
-
-    ///
-    /// \brief Nicely formatted (group,element) version of a tag
-    ///
-    static QString TagKey( const DcmTag& tag );
-
-    ///
-    /// \brief Description (name) of the tag
-    ///
-    static QString TagDescription( const DcmTag& tag );
-
-    ///
-    /// \brief Value Representation
-    ///
-    static QString TagVR( const DcmTag& tag );
-
-protected:
-
-    ///
-    /// \brief Callback for retrieving a serialized version of this class
-    ///
-    /// You can override this method in a subclass to retrieve a serialized
-    /// version of the object from some storage mechanism, eg a database
-    ///
-    virtual QString GetStoredSerialization();
-
-    ///
-    /// \brief Callback for storing a serialized version of this class
-    ///
-    /// You can override this method in a subclass to store a serialized
-    /// version of the object to some storage mechanism, eg a database
-    ///
-    virtual void SetStoredSerialization(QString serializedDataset);
-
-  QScopedPointer<ctkDICOMDatasetPrivate> d_ptr;
-
-  DcmItem& GetDcmItem() const;
-
-private:
-  Q_DECLARE_PRIVATE(ctkDICOMDataset);
-};
-
+#ifndef CTK_DICOM_DATASET_H
+#define CTK_DICOM_DATASET_H
+#if defined _MSC_VER
+  #pragma message ( "Deprecated header ctkDICOMDataset.h included. Please use ctkDICOMItem.h!" )
+#elif defined __GNUC__
+  #warning "Deprecated header ctkDICOMDataset.h included. Please use ctkDICOMItem.h!"
+#endif
+#include "ctkDICOMItem.h"
 #endif
 

+ 82 - 82
Libs/DICOM/Core/ctkDICOMDataset.cpp

@@ -19,7 +19,7 @@
 
 =============================================================================*/
 
-#include "ctkDICOMDataset.h"
+#include "ctkDICOMItem.h"
 
 #include <dctk.h>
 #include <dcostrmb.h>
@@ -28,11 +28,11 @@
 #include <stdexcept>
 
 
-class ctkDICOMDatasetPrivate
+class ctkDICOMItemPrivate
 {
   public:
 
-    ctkDICOMDatasetPrivate() : m_DcmItem(0), m_TakeOwnership(true) {}
+    ctkDICOMItemPrivate() : m_DcmItem(0), m_TakeOwnership(true) {}
 
     QString m_SpecificCharacterSet;
 
@@ -44,19 +44,19 @@ class ctkDICOMDatasetPrivate
 };
 
 
-ctkDICOMDataset::ctkDICOMDataset(bool strictErrorHandling)
-:d_ptr(new ctkDICOMDatasetPrivate)
+ctkDICOMItem::ctkDICOMItem(bool strictErrorHandling)
+:d_ptr(new ctkDICOMItemPrivate)
 {
-  Q_D(ctkDICOMDataset);
+  Q_D(ctkDICOMItem);
   d->m_DcmItem = new DcmDataset();
   d->m_TakeOwnership = true;
   d->m_DICOMDataSetInitialized = false;
   d->m_StrictErrorHandling = strictErrorHandling;
 }
 
-ctkDICOMDataset::~ctkDICOMDataset()
+ctkDICOMItem::~ctkDICOMItem()
 {
-  Q_D(ctkDICOMDataset);
+  Q_D(ctkDICOMItem);
   if ( d->m_TakeOwnership)
   {
     delete d->m_DcmItem;
@@ -64,9 +64,9 @@ ctkDICOMDataset::~ctkDICOMDataset()
 }
 
 
-void ctkDICOMDataset::InitializeFromItem(DcmItem *dataset, bool takeOwnership)
+void ctkDICOMItem::InitializeFromItem(DcmItem *dataset, bool takeOwnership)
 {
-  Q_D(ctkDICOMDataset);
+  Q_D(ctkDICOMItem);
 
   if(d->m_DcmItem != dataset)
   {
@@ -106,7 +106,7 @@ void ctkDICOMDataset::InitializeFromItem(DcmItem *dataset, bool takeOwnership)
   }
 
 
-void ctkDICOMDataset::InitializeFromFile(const QString& filename,
+void ctkDICOMItem::InitializeFromFile(const QString& filename,
                                          const E_TransferSyntax readXfer,
                                          const E_GrpLenEncoding groupLength,
                                          const Uint32 maxReadLength,
@@ -128,12 +128,12 @@ void ctkDICOMDataset::InitializeFromFile(const QString& filename,
   InitializeFromItem(dataset, true);
 }
 
-void ctkDICOMDataset::Serialize()
+void ctkDICOMItem::Serialize()
 {
-  Q_D(ctkDICOMDataset);
+  Q_D(ctkDICOMItem);
   EnsureDcmDataSetIsInitialized();
 
-  // store content of current DcmDataset (our parent) as QByteArray into m_ctkDICOMDataset
+  // store content of current DcmDataset (our parent) as QByteArray into m_ctkDICOMItem
   Uint32 buffersize = 1024*1024; // reserve 1MB
   char* writebuffer = new char[buffersize];
 
@@ -152,7 +152,7 @@ void ctkDICOMDataset::Serialize()
   void* readbuffer = NULL;
   dcmbuffer.flushBuffer(readbuffer, datasetsize);
 
-  //std::cerr << "** " << (void*)this << " ctkDICOMDataset: Serializing Dataset into " << datasetsize << " bytes" << std::endl;
+  //std::cerr << "** " << (void*)this << " ctkDICOMItem: Serializing Dataset into " << datasetsize << " bytes" << std::endl;
 
   // construct Qt type from that contents
   QByteArray qtArray = QByteArray::fromRawData( static_cast<const char*>(readbuffer), datasetsize );
@@ -166,29 +166,29 @@ void ctkDICOMDataset::Serialize()
   delete[] writebuffer;
 }
 
-void ctkDICOMDataset::MarkForInitialization()
+void ctkDICOMItem::MarkForInitialization()
 {
-  Q_D(ctkDICOMDataset);
+  Q_D(ctkDICOMItem);
   d->m_DICOMDataSetInitialized = false;
 }
-bool ctkDICOMDataset::IsInitialized() const
+bool ctkDICOMItem::IsInitialized() const
 {
-  Q_D(const ctkDICOMDataset);
+  Q_D(const ctkDICOMItem);
   return d->m_DICOMDataSetInitialized;
 }
-void ctkDICOMDataset::EnsureDcmDataSetIsInitialized() const
+void ctkDICOMItem::EnsureDcmDataSetIsInitialized() const
 {
   if ( ! this->IsInitialized() )
   {
-      throw std::logic_error("Calling methods on uninitialized ctkDICOMDataset");
+      throw std::logic_error("Calling methods on uninitialized ctkDICOMItem");
   }
-  // const_cast<ctkDICOMDataset*>(this)->Deserialize();
+  // const_cast<ctkDICOMItem*>(this)->Deserialize();
 }
 
-void ctkDICOMDataset::Deserialize()
+void ctkDICOMItem::Deserialize()
 {
-  Q_D(ctkDICOMDataset);
-  // read attribute m_ctkDICOMDataset
+  Q_D(ctkDICOMItem);
+  // read attribute m_ctkDICOMItem
   // construct a DcmDataset from it
   // calls InitializeData(DcmDataset*)
 
@@ -205,10 +205,10 @@ void ctkDICOMDataset::Deserialize()
   }
 
 
-  //std::cerr << "** " << (void*)this << " ctkDICOMDataset: Deserialize Dataset from string of size " << stringbuffer.size() << "\n" << stringbuffer.toStdString() << std::endl;
+  //std::cerr << "** " << (void*)this << " ctkDICOMItem: Deserialize Dataset from string of size " << stringbuffer.size() << "\n" << stringbuffer.toStdString() << std::endl;
 
   QByteArray qtArray = QByteArray::fromBase64( stringbuffer.toAscii() );
-  //std::cerr << "** " << (void*)this << " ctkDICOMDataset: Deserialize Dataset from byte array of size " << qtArray.size() << std::endl;
+  //std::cerr << "** " << (void*)this << " ctkDICOMItem: Deserialize Dataset from byte array of size " << qtArray.size() << std::endl;
 
   DcmInputBufferStream dcmbuffer;
   dcmbuffer.setBuffer( qtArray.data(), qtArray.size() );
@@ -240,27 +240,27 @@ void ctkDICOMDataset::Deserialize()
   }
 }
 
-DcmItem& ctkDICOMDataset::GetDcmItem() const
+DcmItem& ctkDICOMItem::GetDcmItem() const
 {
-  const Q_D(ctkDICOMDataset);
+  const Q_D(ctkDICOMItem);
   return *d->m_DcmItem;
 }
 
-OFCondition ctkDICOMDataset::findAndGetElement(const DcmTag& tag, DcmElement*& element, const OFBool searchIntoSub) const
+OFCondition ctkDICOMItem::findAndGetElement(const DcmTag& tag, DcmElement*& element, const OFBool searchIntoSub) const
 {
   EnsureDcmDataSetIsInitialized();
   // this one const_cast allows us to declare quite a lot of methods nicely with const
   return GetDcmItem().findAndGetElement(tag, element, searchIntoSub);
 }
 
-OFCondition ctkDICOMDataset::findAndGetOFString(const DcmTag& tag, OFString& value, const unsigned long pos, const OFBool searchIntoSub) const
+OFCondition ctkDICOMItem::findAndGetOFString(const DcmTag& tag, OFString& value, const unsigned long pos, const OFBool searchIntoSub) const
 {
   EnsureDcmDataSetIsInitialized();
   // this second const_cast allows us to declare quite a lot of methods nicely with const
   return GetDcmItem().findAndGetOFString(tag, value, pos, searchIntoSub);
 }
 
-bool ctkDICOMDataset::CheckCondition(const OFCondition& condition)
+bool ctkDICOMItem::CheckCondition(const OFCondition& condition)
 {
   if ( condition.bad() )
   {
@@ -270,9 +270,9 @@ bool ctkDICOMDataset::CheckCondition(const OFCondition& condition)
   return condition.good();
 }
 
-bool ctkDICOMDataset::CopyElement( DcmDataset* dataset, const DcmTagKey& tag, int type )
+bool ctkDICOMItem::CopyElement( DcmDataset* dataset, const DcmTagKey& tag, int type )
 {
-  Q_D(ctkDICOMDataset);
+  Q_D(ctkDICOMItem);
   switch (type)
   {
     case 0x1:
@@ -333,9 +333,9 @@ bool ctkDICOMDataset::CopyElement( DcmDataset* dataset, const DcmTagKey& tag, in
   return !missing && copied;
 }
 
-QString ctkDICOMDataset::Decode( const DcmTag& tag, const OFString& raw ) const
+QString ctkDICOMItem::Decode( const DcmTag& tag, const OFString& raw ) const
 {
-  Q_D(const ctkDICOMDataset);
+  Q_D(const ctkDICOMItem);
   // decode for types LO, LT, PN, SH, ST, UT
   QString vr = TagVR(tag);
   if ( !d->m_SpecificCharacterSet.isEmpty()
@@ -424,14 +424,14 @@ QString ctkDICOMDataset::Decode( const DcmTag& tag, const OFString& raw ) const
 
 }
 
-OFString ctkDICOMDataset::Encode( const DcmTag& tag, const QString& qstring ) const
+OFString ctkDICOMItem::Encode( const DcmTag& tag, const QString& qstring ) const
 {
   // TODO: respect given character-set when encoding; see Decode()
   Q_UNUSED(tag);
   return OFString( qstring.toLatin1().data() ); // Latin1 is ISO 8859, which is the default character set of DICOM (PS 3.5-2008, Page 18)
 }
 
-QString ctkDICOMDataset::GetAllElementValuesAsString( const DcmTag& tag ) const
+QString ctkDICOMItem::GetAllElementValuesAsString( const DcmTag& tag ) const
 {
   this->EnsureDcmDataSetIsInitialized();
 
@@ -445,7 +445,7 @@ QString ctkDICOMDataset::GetAllElementValuesAsString( const DcmTag& tag ) const
   for (unsigned long i = 0; i < count; ++i)
   {
     OFString s;
-    if ( CheckCondition( const_cast<ctkDICOMDataset*>(this)->findAndGetOFString(tag, s, i) ) )
+    if ( CheckCondition( const_cast<ctkDICOMItem*>(this)->findAndGetOFString(tag, s, i) ) )
     {
       qsl << Decode( tag, s );
     }
@@ -455,7 +455,7 @@ QString ctkDICOMDataset::GetAllElementValuesAsString( const DcmTag& tag ) const
 }
 
 
-QString ctkDICOMDataset::GetElementAsString( const DcmTag& tag, unsigned long pos ) const
+QString ctkDICOMItem::GetElementAsString( const DcmTag& tag, unsigned long pos ) const
 {
   this->EnsureDcmDataSetIsInitialized();
 
@@ -470,7 +470,7 @@ QString ctkDICOMDataset::GetElementAsString( const DcmTag& tag, unsigned long po
   }
 }
 
-QStringList ctkDICOMDataset::GetElementAsStringList( const DcmTag& tag ) const
+QStringList ctkDICOMItem::GetElementAsStringList( const DcmTag& tag ) const
 {
   this->EnsureDcmDataSetIsInitialized();
   QStringList qsl;
@@ -488,7 +488,7 @@ QStringList ctkDICOMDataset::GetElementAsStringList( const DcmTag& tag ) const
   return qsl;
 }
 
-ctkDICOMPersonName ctkDICOMDataset::GetElementAsPersonName( const DcmTag& tag, unsigned long pos ) const
+ctkDICOMPersonName ctkDICOMItem::GetElementAsPersonName( const DcmTag& tag, unsigned long pos ) const
 {
   this->EnsureDcmDataSetIsInitialized();
   DcmElement* element(NULL);
@@ -518,7 +518,7 @@ ctkDICOMPersonName ctkDICOMDataset::GetElementAsPersonName( const DcmTag& tag, u
   }
 }
 
-ctkDICOMPersonNameList ctkDICOMDataset::GetElementAsPersonNameList( const DcmTag& tag ) const
+ctkDICOMPersonNameList ctkDICOMItem::GetElementAsPersonNameList( const DcmTag& tag ) const
 {
   this->EnsureDcmDataSetIsInitialized();
   ctkDICOMPersonNameList qpnl;
@@ -536,7 +536,7 @@ ctkDICOMPersonNameList ctkDICOMDataset::GetElementAsPersonNameList( const DcmTag
   return qpnl;
 }
 
-QDate ctkDICOMDataset::GetElementAsDate( const DcmTag& tag, unsigned long pos ) const
+QDate ctkDICOMItem::GetElementAsDate( const DcmTag& tag, unsigned long pos ) const
 {
   this->EnsureDcmDataSetIsInitialized();
   DcmElement* element(NULL);
@@ -558,7 +558,7 @@ QDate ctkDICOMDataset::GetElementAsDate( const DcmTag& tag, unsigned long pos )
   }
 }
 
-QTime ctkDICOMDataset::GetElementAsTime( const DcmTag& tag, unsigned long pos ) const
+QTime ctkDICOMItem::GetElementAsTime( const DcmTag& tag, unsigned long pos ) const
 {
   this->EnsureDcmDataSetIsInitialized();
   DcmElement* element(NULL);
@@ -580,7 +580,7 @@ QTime ctkDICOMDataset::GetElementAsTime( const DcmTag& tag, unsigned long pos )
   }
 }
 
-QDateTime ctkDICOMDataset::GetElementAsDateTime( const DcmTag& tag, unsigned long pos ) const
+QDateTime ctkDICOMItem::GetElementAsDateTime( const DcmTag& tag, unsigned long pos ) const
 {
   this->EnsureDcmDataSetIsInitialized();
   DcmElement* element(NULL);
@@ -602,9 +602,9 @@ QDateTime ctkDICOMDataset::GetElementAsDateTime( const DcmTag& tag, unsigned lon
   }
 }
 
-double ctkDICOMDataset::GetElementAsDouble( const DcmTag& tag, unsigned long pos ) const
+double ctkDICOMItem::GetElementAsDouble( const DcmTag& tag, unsigned long pos ) const
 {
-  Q_D(const ctkDICOMDataset);
+  Q_D(const ctkDICOMItem);
   this->EnsureDcmDataSetIsInitialized();
   DcmElement* element(NULL);
   findAndGetElement(tag, element);
@@ -628,9 +628,9 @@ double ctkDICOMDataset::GetElementAsDouble( const DcmTag& tag, unsigned long pos
   return dvalue;
 }
 
-long ctkDICOMDataset::GetElementAsInteger( const DcmTag& tag, unsigned long pos ) const
+long ctkDICOMItem::GetElementAsInteger( const DcmTag& tag, unsigned long pos ) const
 {
-  Q_D(const ctkDICOMDataset);
+  Q_D(const ctkDICOMItem);
   this->EnsureDcmDataSetIsInitialized();
   DcmElement* element(NULL);
   findAndGetElement(tag, element);
@@ -655,7 +655,7 @@ long ctkDICOMDataset::GetElementAsInteger( const DcmTag& tag, unsigned long pos
   return i;
 }
 
-int ctkDICOMDataset::GetElementAsSignedShort( const DcmTag& tag, unsigned long pos ) const // type SS
+int ctkDICOMItem::GetElementAsSignedShort( const DcmTag& tag, unsigned long pos ) const // type SS
 {
   this->EnsureDcmDataSetIsInitialized();
   DcmElement* element(NULL);
@@ -671,7 +671,7 @@ int ctkDICOMDataset::GetElementAsSignedShort( const DcmTag& tag, unsigned long p
   return i;
 }
 
-int ctkDICOMDataset::GetElementAsUnsignedShort( const DcmTag& tag, unsigned long pos ) const // type US
+int ctkDICOMItem::GetElementAsUnsignedShort( const DcmTag& tag, unsigned long pos ) const // type US
 {
   this->EnsureDcmDataSetIsInitialized();
   DcmElement* element(NULL);
@@ -687,15 +687,15 @@ int ctkDICOMDataset::GetElementAsUnsignedShort( const DcmTag& tag, unsigned long
   return i;
 }
 
-bool ctkDICOMDataset::SetElementAsString( const DcmTag& tag, QString string )
+bool ctkDICOMItem::SetElementAsString( const DcmTag& tag, QString string )
 {
-  Q_D(ctkDICOMDataset);
+  Q_D(ctkDICOMItem);
   this->EnsureDcmDataSetIsInitialized();
   // TODO: Evaluate DICOM tag for proper encoding (see GetElementAsString())
   return CheckCondition( d->m_DcmItem->putAndInsertString( tag, string.toLatin1().data() ) );
 }
 
-bool ctkDICOMDataset::SetElementAsStringList( const DcmTag& /*tag*/, QStringList /*stringList*/ )
+bool ctkDICOMItem::SetElementAsStringList( const DcmTag& /*tag*/, QStringList /*stringList*/ )
 {
   this->EnsureDcmDataSetIsInitialized();
   // TODO: Find out how this can be implemented with DcmDataset methods; there is no method for
@@ -703,9 +703,9 @@ bool ctkDICOMDataset::SetElementAsStringList( const DcmTag& /*tag*/, QStringList
   return false;
 }
 
-bool ctkDICOMDataset::SetElementAsPersonName( const DcmTag& tag, ctkDICOMPersonName personName )
+bool ctkDICOMItem::SetElementAsPersonName( const DcmTag& tag, ctkDICOMPersonName personName )
 {
-  Q_D(ctkDICOMDataset);
+  Q_D(ctkDICOMItem);
   this->EnsureDcmDataSetIsInitialized();
   DcmPersonName* dcmPersonName = new DcmPersonName( tag ); // TODO leak?
 
@@ -722,7 +722,7 @@ bool ctkDICOMDataset::SetElementAsPersonName( const DcmTag& tag, ctkDICOMPersonN
   return false;
 }
 
-bool ctkDICOMDataset::SetElementAsPersonNameList( const DcmTag& tag, ctkDICOMPersonNameList personNameList )
+bool ctkDICOMItem::SetElementAsPersonNameList( const DcmTag& tag, ctkDICOMPersonNameList personNameList )
 {
   Q_UNUSED(tag);
   Q_UNUSED(personNameList);
@@ -732,9 +732,9 @@ bool ctkDICOMDataset::SetElementAsPersonNameList( const DcmTag& tag, ctkDICOMPer
   return false;
 }
 
-bool ctkDICOMDataset::SetElementAsDate( const DcmTag& tag, QDate date )
+bool ctkDICOMItem::SetElementAsDate( const DcmTag& tag, QDate date )
 {
-  Q_D(ctkDICOMDataset);
+  Q_D(ctkDICOMItem);
   this->EnsureDcmDataSetIsInitialized();
   OFDate ofDate( date.year(), date.month(), date.day() );
   DcmDate* dcmDate = new DcmDate( tag ); // TODO leak?
@@ -747,9 +747,9 @@ bool ctkDICOMDataset::SetElementAsDate( const DcmTag& tag, QDate date )
   return false;
 }
 
-bool ctkDICOMDataset::SetElementAsTime( const DcmTag& tag, QTime time )
+bool ctkDICOMItem::SetElementAsTime( const DcmTag& tag, QTime time )
 {
-  Q_D(ctkDICOMDataset);
+  Q_D(ctkDICOMItem);
   this->EnsureDcmDataSetIsInitialized();
   OFTime ofTime( time.hour(), time.minute(), time.second() );
   DcmTime* dcmTime = new DcmTime( tag ); // TODO leak?
@@ -762,9 +762,9 @@ bool ctkDICOMDataset::SetElementAsTime( const DcmTag& tag, QTime time )
   return false;
 }
 
-bool ctkDICOMDataset::SetElementAsDateTime( const DcmTag& tag, QDateTime dateTime )
+bool ctkDICOMItem::SetElementAsDateTime( const DcmTag& tag, QDateTime dateTime )
 {
-  Q_D(ctkDICOMDataset);
+  Q_D(ctkDICOMItem);
   this->EnsureDcmDataSetIsInitialized();
   QDate date = dateTime.date();
   QTime time = dateTime.time();
@@ -781,46 +781,46 @@ bool ctkDICOMDataset::SetElementAsDateTime( const DcmTag& tag, QDateTime dateTim
   return false;
 }
 
-bool ctkDICOMDataset::SetElementAsInteger( const DcmTag& tag, long value, unsigned long pos )
+bool ctkDICOMItem::SetElementAsInteger( const DcmTag& tag, long value, unsigned long pos )
 {
-  Q_D(ctkDICOMDataset);
+  Q_D(ctkDICOMItem);
   this->EnsureDcmDataSetIsInitialized();
   //std::cerr << "TagVR: " << TagVR( tag ).toStdString() << std::endl;
   return CheckCondition( d->m_DcmItem->putAndInsertSint32( tag, value, pos ) );
 }
 
-bool ctkDICOMDataset::SetElementAsSignedShort( const DcmTag& tag, int value, unsigned long pos )
+bool ctkDICOMItem::SetElementAsSignedShort( const DcmTag& tag, int value, unsigned long pos )
 {
-  Q_D(ctkDICOMDataset);
+  Q_D(ctkDICOMItem);
   this->EnsureDcmDataSetIsInitialized();
   //std::cerr << "TagVR: " << TagVR( tag ).toStdString() << std::endl;
   return CheckCondition( d->m_DcmItem->putAndInsertSint16( tag, value, pos ) );
 }
 
-bool ctkDICOMDataset::SetElementAsUnsignedShort( const DcmTag& tag, int value, unsigned long pos )
+bool ctkDICOMItem::SetElementAsUnsignedShort( const DcmTag& tag, int value, unsigned long pos )
 {
-  Q_D(ctkDICOMDataset);
+  Q_D(ctkDICOMItem);
   this->EnsureDcmDataSetIsInitialized();
   //std::cerr << "TagVR: " << TagVR( tag ).toStdString() << std::endl;
   return CheckCondition( d->m_DcmItem->putAndInsertUint16( tag, value, pos ) );
 }
 
-QString ctkDICOMDataset::GetStudyInstanceUID() const
+QString ctkDICOMItem::GetStudyInstanceUID() const
 {
   return this->GetElementAsString(DCM_StudyInstanceUID);
 }
 
-QString ctkDICOMDataset::GetSeriesInstanceUID() const
+QString ctkDICOMItem::GetSeriesInstanceUID() const
 {
   return this->GetElementAsString(DCM_SeriesInstanceUID);
 }
 
-QString ctkDICOMDataset::GetSOPInstanceUID() const
+QString ctkDICOMItem::GetSOPInstanceUID() const
 {
   return this->GetElementAsString(DCM_SOPInstanceUID);
 }
 
-QString ctkDICOMDataset::TranslateDefinedTermPatientPosition( const QString& dt )
+QString ctkDICOMItem::TranslateDefinedTermPatientPosition( const QString& dt )
 {
   static bool initialized = false;
   static QMap<QString, QString> descriptionOfTerms;
@@ -848,7 +848,7 @@ QString ctkDICOMDataset::TranslateDefinedTermPatientPosition( const QString& dt
   }
 }
 
-QString ctkDICOMDataset::TranslateDefinedTermModality( const QString& dt )
+QString ctkDICOMItem::TranslateDefinedTermModality( const QString& dt )
 {
   static bool initialized = false;
   static QMap<QString, QString> descriptionOfTerms;
@@ -934,12 +934,12 @@ QString ctkDICOMDataset::TranslateDefinedTermModality( const QString& dt )
   }
 }
 
-QString ctkDICOMDataset::TagKey( const DcmTag& tag )
+QString ctkDICOMItem::TagKey( const DcmTag& tag )
 {
   return QString("(%1,%2)").arg( tag.getGroup(), 4, 16, QLatin1Char('0')).arg( tag.getElement(), 4, 16, QLatin1Char('0') );
 }
 
-QString ctkDICOMDataset::TagDescription( const DcmTag& tag )
+QString ctkDICOMItem::TagDescription( const DcmTag& tag )
 {
   if (!dcmDataDict.isDictionaryLoaded())
     return QString("<no DICOM dictionary loaded. application broken>");
@@ -954,7 +954,7 @@ QString ctkDICOMDataset::TagDescription( const DcmTag& tag )
   return returnName;
 }
 
-QString ctkDICOMDataset::TagVR( const DcmTag& tag )
+QString ctkDICOMItem::TagVR( const DcmTag& tag )
 {
   if (!dcmDataDict.isDictionaryLoaded()) return QString("<no DICOM dictionary loaded. application broken>");
   const DcmDataDictionary& globalDataDict = dcmDataDict.rdlock();
@@ -968,22 +968,22 @@ QString ctkDICOMDataset::TagVR( const DcmTag& tag )
   return returnVR;
 }
 
-QString ctkDICOMDataset::GetStoredSerialization()
+QString ctkDICOMItem::GetStoredSerialization()
 {
   throw std::runtime_error("No serialization implemented for this object!");
 }
 
 
 
-void ctkDICOMDataset::SetStoredSerialization(QString serializedDataset)
+void ctkDICOMItem::SetStoredSerialization(QString serializedDataset)
 {
   Q_UNUSED(serializedDataset);
   throw std::runtime_error("No serialization implemented for this object!");
 }
 
-bool ctkDICOMDataset::SaveToFile(const QString& filePath) const
+bool ctkDICOMItem::SaveToFile(const QString& filePath) const
 {
-  Q_D(const ctkDICOMDataset);
+  Q_D(const ctkDICOMItem);
 
   if (! dynamic_cast<DcmDataset*>(d->m_DcmItem) )
   {

+ 264 - 0
Libs/DICOM/Core/ctkDICOMItem.h

@@ -0,0 +1,264 @@
+/*=========================================================================
+
+  Library:   CTK
+
+  Copyright (c) Mint Medical GmbH
+
+  Licensed under the Apache License, Version 2.0 (the "License");
+  you may not use this file except in compliance with the License.
+  You may obtain a copy of the License at
+
+      http://www.apache.org/licenses/LICENSE-2.0.txt
+
+  Unless required by applicable law or agreed to in writing, software
+  distributed under the License is distributed on an "AS IS" BASIS,
+  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+  See the License for the specific language governing permissions and
+  limitations under the License.
+
+=========================================================================*/
+
+#ifndef __ctkDICOMItem_h
+#define __ctkDICOMItem_h
+
+#include "ctkDICOMCoreExport.h"
+
+#include "ctkDICOMPersonName.h"
+
+#include <dcdatset.h> // DCMTK DcmDataset
+
+#include <QtCore>
+
+class DcmDataDictionary;
+
+
+class ctkDICOMItemPrivate;
+
+/// \ingroup DICOM_Core
+///
+///  \brief Base class for DICOM objects and interface with DICOM toolkit DCMTK.
+///
+///  This class serves as the base class for all DICOM objects (patient, study, series, image).
+///
+///  The class is derived from DcmDataset, the data type that is used by the DICOM toolkit when
+///  reading an image file or formulating a message request or receiving a message response (e.g. C-FIND).
+///
+///  Basically it offers a lot of convenience methods for subclasses to read and write DICOM attributes
+///  using Qt types. It is the subclasses responsibility to use the correct data types as defined in DICOM.
+///
+///  \note ONLY the Get.. methods should be used to access the internal dataset.
+///
+///  When reading string type attributes (LO, LT, PN, SH, ST, UT), we consider the "specific character set"
+///  tag and decode the stored string using the correct encoding (using Qt methods). This allows to
+///  display e.g. person names written in arabic, hebrew, greek, russian, etc. letters.
+///
+///  \warning Right now, asian phonetic strings cause problems. We have to learn the concept.
+///  \warning Helpers for writing DICOM attributes are not yet implemented. Implementation is straightforward though and can be done when necessary.
+///  \warning DateTime objects ignore the timezone at the moment. This is however of secondary importance.
+///
+///  A subclass could possibly want to store the internal DcmDataset.
+///  For this purpose, the internal DcmDataset is serialized into a memory buffer using DcmDataset::write(..). This buffer
+///  is stored in a base64 encoded string. For deserialization we decode the string and use DcmDataset::read(..).
+class ctkDICOMItem;
+
+typedef ctkDICOMItem ctkDICOMDataset;
+
+class CTK_DICOM_CORE_EXPORT ctkDICOMItem
+{
+public:
+    typedef QObject Superclass;
+    ///
+    /// \brief Create an empty object. This has to be initialized by one of
+    /// the InitializeFrom... methods before it can be used.
+    ///
+    /// @param strictErrorHandling If set to false (the default) only critical errors throw exceptions.
+    ctkDICOMItem(bool strictErrorHandling = false);
+    virtual ~ctkDICOMItem();
+
+    /// \brief For initialization from a DcmDataset in a constructor / assignment.
+    ///
+    /// This method should be overwritten by all derived classes. It should
+    /// be called from the constructor or assignment operators when the class
+    /// should copy information from a DcmDataset object.
+
+    /// \warning Derived classes must call PDICOMDataset::InitializeFromDataset(...) to correctly copy encoding information.
+    virtual void InitializeFromItem(DcmItem* dataset, bool takeOwnership = false);
+
+    ///
+    /// \brief For initialization from file in a constructor / assignment.
+    ///
+    virtual void InitializeFromFile(const QString& filename,
+                    const E_TransferSyntax readXfer = EXS_Unknown,
+                    const E_GrpLenEncoding groupLength = EGL_noChange,
+                    const Uint32 maxReadLength = DCM_MaxReadLength,
+                    const E_FileReadMode readMode = ERM_autoDetect);
+
+
+
+    /// \brief Save dataset to file
+    ///
+    /// \returns true on success.
+    bool SaveToFile(const QString& filePath) const;
+
+
+    /// \brief Store a string representation of the object to a database field.
+    ///
+    /// The internal DcmDataset is serialized into a memory buffer using DcmDataset::write(..).
+    /// To store the memory buffer in a simple string database field, we convert it to a base64 encoded string.
+    /// Doing so prevents errors from encoding conversions that could be made by QString or the database etc.
+    void Serialize();
+
+    /// \brief Restore the object from a string representation in a database field.
+    ///
+    /// The database stored string is base64 decoded into a memory buffer. Then
+    /// the internal DcmDataset is created using DcmDataset::read(..).
+    void Deserialize();
+
+
+    /// \brief To be called from InitializeData, flags status as dirty.
+    ///
+    /// This is to allow data to be read as late as possible. All the
+    /// Get/SetElement... methods ensure initialization, which checks this flag.
+    void MarkForInitialization();
+
+
+    /// \brief Is this dataset initialized ?
+    bool IsInitialized() const;
+
+    ///
+    /// \brief Called by all Get/Set methods to initialize DcmDataSet if needed.
+    ///
+    void EnsureDcmDataSetIsInitialized() const;
+
+
+    /// \brief Find element in dataset and copy it into internal DcmDataset
+    ///
+    /// Attribute types 1, 1C, 2, 2C, 3 as defined in DICOM can be encoded as
+    /// hex values 0x1, 0x1C, 0x2, 0x2C, 0x3.
+    ///
+    /// Conditional attributes are considered MUST attributes. The calling
+    /// function shall test the conditions before calling CopyElement
+    /// (since conditions might be complex).
+    bool CopyElement( DcmDataset* dataset, const DcmTagKey& tag, int type );
+
+    /// \brief creates a QString from the OFString, respecting the "specific character set" of the Dataset.
+    ///
+    /// This method checks if the dataset has an attribute "specific character set".
+    /// If so, all attributes of types Long String (LO), Long Text (LT), Person Name (PN), Short String (SH),
+    /// Short Text (ST), Unlimited Text (UT) should be interpreted as encoded with a special set.
+    ///
+    /// See implementation for details.
+    QString Decode(const DcmTag& tag, const OFString& raw) const;
+
+    /// \brief creates an OFString from the QtString
+    ///
+    /// \warning The method currently assumes that the encoding of the passed string if latin1 and converts
+    /// it accordingly. The passed DICOM tag is not yet evaluated to determine the actual encoding type.
+    OFString Encode(const DcmTag& tag, const QString& qstring) const;
+
+    ///
+    /// \brief A const-correct version of DcmDataset::findAndGetElement.
+    ///
+    OFCondition findAndGetElement(const DcmTag& tag, DcmElement*& element, const OFBool searchIntoSub=OFFalse) const; // DCMTK is not const-correct
+
+    ///
+    ///  \brief A const-correct version of DcmDataset::findAndGetOFString.
+    ///
+    OFCondition findAndGetOFString(const DcmTag& tag, OFString& value, const unsigned long pos = 0, const OFBool searchIntoSub=OFFalse) const; // DCMTK is not const-correct
+
+    static bool CheckCondition(const OFCondition&);
+
+    ///
+    /// \brief Get-methods for for all subtypes of DcmByteString
+    ///
+    QString          GetAllElementValuesAsString( const DcmTag& tag ) const;
+    QString                   GetElementAsString( const DcmTag& tag, unsigned long pos = 0 ) const;
+    QStringList           GetElementAsStringList( const DcmTag& tag ) const;
+    ctkDICOMPersonName           GetElementAsPersonName( const DcmTag& tag, unsigned long pos = 0 ) const;
+    ctkDICOMPersonNameList   GetElementAsPersonNameList( const DcmTag& tag ) const;
+    QDate                       GetElementAsDate( const DcmTag& tag, unsigned long pos = 0 ) const;
+    QTime                       GetElementAsTime( const DcmTag& tag, unsigned long pos = 0 ) const;
+    double                    GetElementAsDouble( const DcmTag& tag, unsigned long pos = 0 ) const; // type DS
+    long                     GetElementAsInteger( const DcmTag& tag, unsigned long pos = 0 ) const; // type IS
+    int                  GetElementAsSignedShort( const DcmTag& tag, unsigned long pos = 0 ) const; // type SS
+    int                GetElementAsUnsignedShort( const DcmTag& tag, unsigned long pos = 0 ) const; // type US
+
+    ///
+    /// \warning IGNORES TIME ZONE at the moment!
+    ///
+    QDateTime       GetElementAsDateTime( const DcmTag& tag, unsigned long pos = 0 ) const;
+
+
+    ///
+    /// \brief Set-methods for for all subtypes of DcmByteString
+    ///
+    bool SetElementAsString( const DcmTag& tag, QString string );
+    bool SetElementAsStringList( const DcmTag& tag, QStringList stringList ); //> Currently not implemented
+    bool SetElementAsPersonName( const DcmTag& tag, ctkDICOMPersonName personName );
+    bool SetElementAsPersonNameList( const DcmTag& tag, ctkDICOMPersonNameList personNameList ); //> Currently not implemented
+    bool SetElementAsDate( const DcmTag& tag, QDate date );
+    bool SetElementAsTime( const DcmTag& tag, QTime time );
+    bool SetElementAsDateTime( const DcmTag& tag, QDateTime dateTime );
+    bool SetElementAsInteger( const DcmTag& tag, long value, unsigned long pos = 0 ); // type IS
+    bool SetElementAsSignedShort( const DcmTag& tag, int value, unsigned long pos = 0 ); // type SS
+    bool SetElementAsUnsignedShort( const DcmTag& tag, int value, unsigned long pos = 0 ); // type US
+
+
+    /// Some convenience getter
+    QString GetStudyInstanceUID() const;
+    QString GetSeriesInstanceUID() const;
+    QString GetSOPInstanceUID() const;
+
+    ///
+    /// \brief Get a human-readable version of patient position enumerations used e.g. in DICOM series.
+    ///
+    static QString TranslateDefinedTermPatientPosition( const QString& dt );
+
+    ///
+    /// \brief Get a human-readable version of modality enumerations used e.g. in DICOM series.
+    ///
+    static QString TranslateDefinedTermModality( const QString& dt );
+
+    ///
+    /// \brief Nicely formatted (group,element) version of a tag
+    ///
+    static QString TagKey( const DcmTag& tag );
+
+    ///
+    /// \brief Description (name) of the tag
+    ///
+    static QString TagDescription( const DcmTag& tag );
+
+    ///
+    /// \brief Value Representation
+    ///
+    static QString TagVR( const DcmTag& tag );
+
+protected:
+
+    ///
+    /// \brief Callback for retrieving a serialized version of this class
+    ///
+    /// You can override this method in a subclass to retrieve a serialized
+    /// version of the object from some storage mechanism, eg a database
+    ///
+    virtual QString GetStoredSerialization();
+
+    ///
+    /// \brief Callback for storing a serialized version of this class
+    ///
+    /// You can override this method in a subclass to store a serialized
+    /// version of the object to some storage mechanism, eg a database
+    ///
+    virtual void SetStoredSerialization(QString serializedDataset);
+
+  QScopedPointer<ctkDICOMItemPrivate> d_ptr;
+
+  DcmItem& GetDcmItem() const;
+
+private:
+  Q_DECLARE_PRIVATE(ctkDICOMItem);
+};
+
+#endif
+