diff OrthancServer/FromDcmtkBridge.cpp @ 948:e57e08ed510f dicom-rt

integration mainline -> dicom-rt
author Sebastien Jodogne <s.jodogne@gmail.com>
date Wed, 25 Jun 2014 13:57:05 +0200
parents 8cfc6119a5bd 87791ebc1f50
children 98d6ba37c7dc
line wrap: on
line diff
--- a/OrthancServer/FromDcmtkBridge.cpp	Wed Apr 16 16:15:58 2014 +0200
+++ b/OrthancServer/FromDcmtkBridge.cpp	Wed Jun 25 13:57:05 2014 +0200
@@ -31,57 +31,19 @@
 
 
 
-/*=========================================================================
-
-  This file is based on portions of the following project:
-
-  Program: GDCM (Grassroots DICOM). A DICOM library
-  Module:  http://gdcm.sourceforge.net/Copyright.html
-
-Copyright (c) 2006-2011 Mathieu Malaterre
-Copyright (c) 1993-2005 CREATIS
-(CREATIS = Centre de Recherche et d'Applications en Traitement de l'Image)
-All rights reserved.
-
-Redistribution and use in source and binary forms, with or without
-modification, are permitted provided that the following conditions are met:
-
- * Redistributions of source code must retain the above copyright notice,
-   this list of conditions and the following disclaimer.
-
- * Redistributions in binary form must reproduce the above copyright notice,
-   this list of conditions and the following disclaimer in the documentation
-   and/or other materials provided with the distribution.
-
- * Neither name of Mathieu Malaterre, or CREATIS, nor the names of any
-   contributors (CNRS, INSERM, UCB, Universite Lyon I), may be used to
-   endorse or promote products derived from this software without specific
-   prior written permission.
-
-THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS ``AS IS''
-AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
-IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
-ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHORS OR CONTRIBUTORS BE LIABLE FOR
-ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
-DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
-SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
-CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
-OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
-OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
-
-=========================================================================*/
-
+#include "PrecompiledHeadersServer.h"
 
 #ifndef NOMINMAX
 #define NOMINMAX
 #endif
 
+#include "Internals/DicomImageDecoder.h"
+
 #include "FromDcmtkBridge.h"
-
 #include "ToDcmtkBridge.h"
 #include "../Core/Toolbox.h"
 #include "../Core/OrthancException.h"
-#include "../Core/FileFormats/PngWriter.h"
+#include "../Core/ImageFormats/PngWriter.h"
 #include "../Core/Uuid.h"
 #include "../Core/DicomFormat/DicomString.h"
 #include "../Core/DicomFormat/DicomNullValue.h"
@@ -131,51 +93,8 @@
 #include <dcmtk/dcmdata/dcostrmb.h>
 
 
-static const char* CONTENT_TYPE_OCTET_STREAM = "application/octet-stream";
-
-
-
 namespace Orthanc
 {
-  void ParsedDicomFile::Setup(const char* buffer, size_t size)
-  {
-    DcmInputBufferStream is;
-    if (size > 0)
-    {
-      is.setBuffer(buffer, size);
-    }
-    is.setEos();
-
-    file_.reset(new DcmFileFormat);
-    file_->transferInit();
-    if (!file_->read(is).good())
-    {
-      throw OrthancException(ErrorCode_BadFileFormat);
-    }
-    file_->loadAllDataIntoMemory();
-    file_->transferEnd();
-  }
-
-
-  static void SendPathValueForDictionary(RestApiOutput& output,
-                                         DcmItem& dicom)
-  {
-    Json::Value v = Json::arrayValue;
-
-    for (unsigned long i = 0; i < dicom.card(); i++)
-    {
-      DcmElement* element = dicom.getElement(i);
-      if (element)
-      {
-        char buf[16];
-        sprintf(buf, "%04x-%04x", element->getTag().getGTag(), element->getTag().getETag());
-        v.append(buf);
-      }
-    }
-
-    output.AnswerJson(v);
-  }
-
   static inline uint16_t GetCharValue(char c)
   {
     if (c >= '0' && c <= '9')
@@ -196,743 +115,6 @@
             GetCharValue(c[3]));
   }
 
-  static void ParseTagAndGroup(DcmTagKey& key,
-                               const std::string& tag)
-  {
-    DicomTag t = FromDcmtkBridge::ParseTag(tag);
-    key = DcmTagKey(t.GetGroup(), t.GetElement());
-  }
-
-
-  static void SendSequence(RestApiOutput& output,
-                           DcmSequenceOfItems& sequence)
-  {
-    // This element is a sequence
-    Json::Value v = Json::arrayValue;
-
-    for (unsigned long i = 0; i < sequence.card(); i++)
-    {
-      v.append(boost::lexical_cast<std::string>(i));
-    }
-
-    output.AnswerJson(v);
-  }
-
-
-  static unsigned int GetPixelDataBlockCount(DcmPixelData& pixelData,
-                                             E_TransferSyntax transferSyntax)
-  {
-    DcmPixelSequence* pixelSequence = NULL;
-    if (pixelData.getEncapsulatedRepresentation
-        (transferSyntax, NULL, pixelSequence).good() && pixelSequence)
-    {
-      return pixelSequence->card();
-    }
-    else
-    {
-      return 1;
-    }
-  }
-
-
-  static void AnswerDicomField(RestApiOutput& output,
-                               DcmElement& element,
-                               E_TransferSyntax transferSyntax)
-  {
-    // This element is nor a sequence, neither a pixel-data
-    std::string buffer;
-    buffer.resize(65536);
-    Uint32 length = element.getLength(transferSyntax);
-    Uint32 offset = 0;
-
-    output.GetLowLevelOutput().SendOkHeader(CONTENT_TYPE_OCTET_STREAM, true, length, NULL);
-
-    while (offset < length)
-    {
-      Uint32 nbytes;
-      if (length - offset < buffer.size())
-      {
-        nbytes = length - offset;
-      }
-      else
-      {
-        nbytes = buffer.size();
-      }
-
-      OFCondition cond = element.getPartialValue(&buffer[0], offset, nbytes);
-
-      if (cond.good())
-      {
-        output.GetLowLevelOutput().Send(&buffer[0], nbytes);
-        offset += nbytes;
-      }
-      else
-      {
-        LOG(ERROR) << "Error while sending a DICOM field: " << cond.text();
-        return;
-      }
-    }
-
-    output.MarkLowLevelOutputDone();
-  }
-
-
-  static bool AnswerPixelData(RestApiOutput& output,
-                              DcmItem& dicom,
-                              E_TransferSyntax transferSyntax,
-                              const std::string* blockUri)
-  {
-    DcmTag k(DICOM_TAG_PIXEL_DATA.GetGroup(),
-             DICOM_TAG_PIXEL_DATA.GetElement());
-
-    DcmElement *element = NULL;
-    if (!dicom.findAndGetElement(k, element).good() ||
-        element == NULL)
-    {
-      return false;
-    }
-
-    try
-    {
-      DcmPixelData& pixelData = dynamic_cast<DcmPixelData&>(*element);
-      if (blockUri == NULL)
-      {
-        // The user asks how many blocks are presents in this pixel data
-        unsigned int blocks = GetPixelDataBlockCount(pixelData, transferSyntax);
-
-        Json::Value result(Json::arrayValue);
-        for (unsigned int i = 0; i < blocks; i++)
-        {
-          result.append(boost::lexical_cast<std::string>(i));
-        }
-        
-        output.AnswerJson(result);
-        return true;
-      }
-
-
-      unsigned int block = boost::lexical_cast<unsigned int>(*blockUri);
-
-      if (block < GetPixelDataBlockCount(pixelData, transferSyntax))
-      {
-        DcmPixelSequence* pixelSequence = NULL;
-        if (pixelData.getEncapsulatedRepresentation
-            (transferSyntax, NULL, pixelSequence).good() && pixelSequence)
-        {
-          // This is the case for JPEG transfer syntaxes
-          if (block < pixelSequence->card())
-          {
-            DcmPixelItem* pixelItem = NULL;
-            if (pixelSequence->getItem(pixelItem, block).good() && pixelItem)
-            {
-              if (pixelItem->getLength() == 0)
-              {
-                output.AnswerBuffer(NULL, 0, CONTENT_TYPE_OCTET_STREAM);
-                return true;
-              }
-
-              Uint8* buffer = NULL;
-              if (pixelItem->getUint8Array(buffer).good() && buffer)
-              {
-                output.AnswerBuffer(buffer, pixelItem->getLength(), CONTENT_TYPE_OCTET_STREAM);
-                return true;
-              }
-            }
-          }
-        }
-        else
-        {
-          // This is the case for raw, uncompressed image buffers
-          assert(*blockUri == "0");
-          AnswerDicomField(output, *element, transferSyntax);
-        }
-      }
-    }
-    catch (boost::bad_lexical_cast&)
-    {
-      // The URI entered by the user is not a number
-    }
-    catch (std::bad_cast&)
-    {
-      // This should never happen
-    }
-
-    return false;
-  }
-
-
-
-  static void SendPathValueForLeaf(RestApiOutput& output,
-                                   const std::string& tag,
-                                   DcmItem& dicom,
-                                   E_TransferSyntax transferSyntax)
-  {
-    DcmTagKey k;
-    ParseTagAndGroup(k, tag);
-
-    DcmSequenceOfItems* sequence = NULL;
-    if (dicom.findAndGetSequence(k, sequence).good() && 
-        sequence != NULL &&
-        sequence->getVR() == EVR_SQ)
-    {
-      SendSequence(output, *sequence);
-      return;
-    }
-
-    DcmElement* element = NULL;
-    if (dicom.findAndGetElement(k, element).good() && 
-        element != NULL &&
-        //element->getVR() != EVR_UNKNOWN &&  // This would forbid private tags
-        element->getVR() != EVR_SQ)
-    {
-      AnswerDicomField(output, *element, transferSyntax);
-    }
-  }
-
-  void ParsedDicomFile::SendPathValue(RestApiOutput& output,
-                                      const UriComponents& uri)
-  {
-    DcmItem* dicom = file_->getDataset();
-    E_TransferSyntax transferSyntax = file_->getDataset()->getOriginalXfer();
-
-    // Special case: Accessing the pixel data
-    if (uri.size() == 1 || 
-        uri.size() == 2)
-    {
-      DcmTagKey tag;
-      ParseTagAndGroup(tag, uri[0]);
-
-      if (tag.getGroup() == DICOM_TAG_PIXEL_DATA.GetGroup() &&
-          tag.getElement() == DICOM_TAG_PIXEL_DATA.GetElement())
-      {
-        AnswerPixelData(output, *dicom, transferSyntax, uri.size() == 1 ? NULL : &uri[1]);
-        return;
-      }
-    }        
-
-    // Go down in the tag hierarchy according to the URI
-    for (size_t pos = 0; pos < uri.size() / 2; pos++)
-    {
-      size_t index;
-      try
-      {
-        index = boost::lexical_cast<size_t>(uri[2 * pos + 1]);
-      }
-      catch (boost::bad_lexical_cast&)
-      {
-        return;
-      }
-
-      DcmTagKey k;
-      DcmItem *child = NULL;
-      ParseTagAndGroup(k, uri[2 * pos]);
-      if (!dicom->findAndGetSequenceItem(k, child, index).good() ||
-          child == NULL)
-      {
-        return;
-      }
-
-      dicom = child;
-    }
-
-    // We have reached the end of the URI
-    if (uri.size() % 2 == 0)
-    {
-      SendPathValueForDictionary(output, *dicom);
-    }
-    else
-    {
-      SendPathValueForLeaf(output, uri.back(), *dicom, transferSyntax);
-    }
-  }
-
-
-  
-
-
-  static DcmElement* CreateElementForTag(const DicomTag& tag)
-  {
-    DcmTag key(tag.GetGroup(), tag.GetElement());
-
-    switch (key.getEVR())
-    {
-      // http://support.dcmtk.org/docs/dcvr_8h-source.html
-
-      /**
-       * TODO.
-       **/
-    
-      case EVR_OB:  // other byte
-      case EVR_OF:  // other float
-      case EVR_OW:  // other word
-      case EVR_AT:  // attribute tag
-        throw OrthancException(ErrorCode_NotImplemented);
-
-      case EVR_UN:  // unknown value representation
-        throw OrthancException(ErrorCode_ParameterOutOfRange);
-
-
-      /**
-       * String types.
-       * http://support.dcmtk.org/docs/classDcmByteString.html
-       **/
-      
-      case EVR_AS:  // age string
-        return new DcmAgeString(key);
-
-      case EVR_AE:  // application entity title
-        return new DcmApplicationEntity(key);
-
-      case EVR_CS:  // code string
-        return new DcmCodeString(key);        
-
-      case EVR_DA:  // date string
-        return new DcmDate(key);
-        
-      case EVR_DT:  // date time string
-        return new DcmDateTime(key);
-
-      case EVR_DS:  // decimal string
-        return new DcmDecimalString(key);
-
-      case EVR_IS:  // integer string
-        return new DcmIntegerString(key);
-
-      case EVR_TM:  // time string
-        return new DcmTime(key);
-
-      case EVR_UI:  // unique identifier
-        return new DcmUniqueIdentifier(key);
-
-      case EVR_ST:  // short text
-        return new DcmShortText(key);
-
-      case EVR_LO:  // long string
-        return new DcmLongString(key);
-
-      case EVR_LT:  // long text
-        return new DcmLongText(key);
-
-      case EVR_UT:  // unlimited text
-        return new DcmUnlimitedText(key);
-
-      case EVR_SH:  // short string
-        return new DcmShortString(key);
-
-      case EVR_PN:  // person name
-        return new DcmPersonName(key);
-
-        
-      /**
-       * Numerical types
-       **/ 
-      
-      case EVR_SL:  // signed long
-        return new DcmSignedLong(key);
-
-      case EVR_SS:  // signed short
-        return new DcmSignedShort(key);
-
-      case EVR_UL:  // unsigned long
-        return new DcmUnsignedLong(key);
-
-      case EVR_US:  // unsigned short
-        return new DcmUnsignedShort(key);
-
-      case EVR_FL:  // float single-precision
-        return new DcmFloatingPointSingle(key);
-
-      case EVR_FD:  // float double-precision
-        return new DcmFloatingPointDouble(key);
-
-
-      /**
-       * Sequence types, should never occur at this point.
-       **/
-
-      case EVR_SQ:  // sequence of items
-        throw OrthancException(ErrorCode_ParameterOutOfRange);
-
-
-      /**
-       * Internal to DCMTK.
-       **/ 
-
-      case EVR_ox:  // OB or OW depending on context
-      case EVR_xs:  // SS or US depending on context
-      case EVR_lt:  // US, SS or OW depending on context, used for LUT Data (thus the name)
-      case EVR_na:  // na="not applicable", for data which has no VR
-      case EVR_up:  // up="unsigned pointer", used internally for DICOMDIR suppor
-      case EVR_item:  // used internally for items
-      case EVR_metainfo:  // used internally for meta info datasets
-      case EVR_dataset:  // used internally for datasets
-      case EVR_fileFormat:  // used internally for DICOM files
-      case EVR_dicomDir:  // used internally for DICOMDIR objects
-      case EVR_dirRecord:  // used internally for DICOMDIR records
-      case EVR_pixelSQ:  // used internally for pixel sequences in a compressed image
-      case EVR_pixelItem:  // used internally for pixel items in a compressed image
-      case EVR_UNKNOWN: // used internally for elements with unknown VR (encoded with 4-byte length field in explicit VR)
-      case EVR_PixelData:  // used internally for uncompressed pixeld data
-      case EVR_OverlayData:  // used internally for overlay data
-      case EVR_UNKNOWN2B:  // used internally for elements with unknown VR with 2-byte length field in explicit VR
-      default:
-        break;
-    }
-
-    throw OrthancException(ErrorCode_InternalError);          
-  }
-
-
-
-  static void FillElementWithString(DcmElement& element,
-                                    const DicomTag& tag,
-                                    const std::string& value)
-  {
-    DcmTag key(tag.GetGroup(), tag.GetElement());
-    bool ok = false;
-    
-    try
-    {
-      switch (key.getEVR())
-      {
-        // http://support.dcmtk.org/docs/dcvr_8h-source.html
-
-        /**
-         * TODO.
-         **/
-
-        case EVR_OB:  // other byte
-        case EVR_OF:  // other float
-        case EVR_OW:  // other word
-        case EVR_AT:  // attribute tag
-          throw OrthancException(ErrorCode_NotImplemented);
-    
-        case EVR_UN:  // unknown value representation
-          throw OrthancException(ErrorCode_ParameterOutOfRange);
-
-
-        /**
-         * String types.
-         **/
-      
-        case EVR_DS:  // decimal string
-        case EVR_IS:  // integer string
-        case EVR_AS:  // age string
-        case EVR_DA:  // date string
-        case EVR_DT:  // date time string
-        case EVR_TM:  // time string
-        case EVR_AE:  // application entity title
-        case EVR_CS:  // code string
-        case EVR_SH:  // short string
-        case EVR_LO:  // long string
-        case EVR_ST:  // short text
-        case EVR_LT:  // long text
-        case EVR_UT:  // unlimited text
-        case EVR_PN:  // person name
-        case EVR_UI:  // unique identifier
-        {
-          ok = element.putString(value.c_str()).good();
-          break;
-        }
-
-        
-        /**
-         * Numerical types
-         **/ 
-      
-        case EVR_SL:  // signed long
-        {
-          ok = element.putSint32(boost::lexical_cast<Sint32>(value)).good();
-          break;
-        }
-
-        case EVR_SS:  // signed short
-        {
-          ok = element.putSint16(boost::lexical_cast<Sint16>(value)).good();
-          break;
-        }
-
-        case EVR_UL:  // unsigned long
-        {
-          ok = element.putUint32(boost::lexical_cast<Uint32>(value)).good();
-          break;
-        }
-
-        case EVR_US:  // unsigned short
-        {
-          ok = element.putUint16(boost::lexical_cast<Uint16>(value)).good();
-          break;
-        }
-
-        case EVR_FL:  // float single-precision
-        {
-          ok = element.putFloat32(boost::lexical_cast<float>(value)).good();
-          break;
-        }
-
-        case EVR_FD:  // float double-precision
-        {
-          ok = element.putFloat64(boost::lexical_cast<double>(value)).good();
-          break;
-        }
-
-
-        /**
-         * Sequence types, should never occur at this point.
-         **/
-
-        case EVR_SQ:  // sequence of items
-        {
-          ok = false;
-          break;
-        }
-
-
-        /**
-         * Internal to DCMTK.
-         **/ 
-
-        case EVR_ox:  // OB or OW depending on context
-        case EVR_xs:  // SS or US depending on context
-        case EVR_lt:  // US, SS or OW depending on context, used for LUT Data (thus the name)
-        case EVR_na:  // na="not applicable", for data which has no VR
-        case EVR_up:  // up="unsigned pointer", used internally for DICOMDIR suppor
-        case EVR_item:  // used internally for items
-        case EVR_metainfo:  // used internally for meta info datasets
-        case EVR_dataset:  // used internally for datasets
-        case EVR_fileFormat:  // used internally for DICOM files
-        case EVR_dicomDir:  // used internally for DICOMDIR objects
-        case EVR_dirRecord:  // used internally for DICOMDIR records
-        case EVR_pixelSQ:  // used internally for pixel sequences in a compressed image
-        case EVR_pixelItem:  // used internally for pixel items in a compressed image
-        case EVR_UNKNOWN: // used internally for elements with unknown VR (encoded with 4-byte length field in explicit VR)
-        case EVR_PixelData:  // used internally for uncompressed pixeld data
-        case EVR_OverlayData:  // used internally for overlay data
-        case EVR_UNKNOWN2B:  // used internally for elements with unknown VR with 2-byte length field in explicit VR
-        default:
-          break;
-      }
-    }
-    catch (boost::bad_lexical_cast&)
-    {
-      ok = false;
-    }
-
-    if (!ok)
-    {
-      throw OrthancException(ErrorCode_InternalError);
-    }
-  }
-
-
-  void ParsedDicomFile::Remove(const DicomTag& tag)
-  {
-    DcmTagKey key(tag.GetGroup(), tag.GetElement());
-    DcmElement* element = file_->getDataset()->remove(key);
-    if (element != NULL)
-    {
-      delete element;
-    }
-  }
-
-
-
-  void ParsedDicomFile::RemovePrivateTags()
-  {
-    typedef std::list<DcmElement*> Tags;
-
-    Tags privateTags;
-
-    DcmDataset& dataset = *file_->getDataset();
-    for (unsigned long i = 0; i < dataset.card(); i++)
-    {
-      DcmElement* element = dataset.getElement(i);
-      DcmTag tag(element->getTag());
-      if (!strcmp("PrivateCreator", tag.getTagName()) ||  // TODO - This may change with future versions of DCMTK
-          tag.getPrivateCreator() != NULL)
-      {
-        privateTags.push_back(element);
-      }
-    }
-
-    for (Tags::iterator it = privateTags.begin(); 
-         it != privateTags.end(); ++it)
-    {
-      DcmElement* tmp = dataset.remove(*it);
-      if (tmp != NULL)
-      {
-        delete tmp;
-      }
-    }
-  }
-
-
-
-  void ParsedDicomFile::Insert(const DicomTag& tag,
-                               const std::string& value)
-  {
-    std::auto_ptr<DcmElement> element(CreateElementForTag(tag));
-    FillElementWithString(*element, tag, value);
-
-    if (!file_->getDataset()->insert(element.release(), false, false).good())
-    {
-      // This field already exists
-      throw OrthancException(ErrorCode_InternalError);
-    }
-  }
-
-
-  void ParsedDicomFile::Replace(const DicomTag& tag,
-                                const std::string& value,
-                                DicomReplaceMode mode)
-  {
-    DcmTagKey key(tag.GetGroup(), tag.GetElement());
-    DcmElement* element = NULL;
-
-    if (!file_->getDataset()->findAndGetElement(key, element).good() ||
-        element == NULL)
-    {
-      // This field does not exist, act wrt. the specified "mode"
-      switch (mode)
-      {
-        case DicomReplaceMode_InsertIfAbsent:
-          Insert(tag, value);
-          break;
-
-        case DicomReplaceMode_ThrowIfAbsent:
-          throw OrthancException(ErrorCode_InexistentItem);
-
-        case DicomReplaceMode_IgnoreIfAbsent:
-          return;
-      }
-    }
-    else
-    {
-      FillElementWithString(*element, tag, value);
-    }
-
-
-    /**
-     * dcmodify will automatically correct 'Media Storage SOP Class
-     * UID' and 'Media Storage SOP Instance UID' in the metaheader, if
-     * you make changes to the related tags in the dataset ('SOP Class
-     * UID' and 'SOP Instance UID') via insert or modify mode
-     * options. You can disable this behaviour by using the -nmu
-     * option.
-     **/
-    if (tag == DICOM_TAG_SOP_CLASS_UID)
-      Replace(DICOM_TAG_MEDIA_STORAGE_SOP_CLASS_UID, value, DicomReplaceMode_InsertIfAbsent);
-
-    if (tag == DICOM_TAG_SOP_INSTANCE_UID)
-      Replace(DICOM_TAG_MEDIA_STORAGE_SOP_INSTANCE_UID, value, DicomReplaceMode_InsertIfAbsent);
-  }
-
-    
-  void ParsedDicomFile::Answer(RestApiOutput& output)
-  {
-    std::string serialized;
-    if (FromDcmtkBridge::SaveToMemoryBuffer(serialized, file_->getDataset()))
-    {
-      output.AnswerBuffer(serialized, CONTENT_TYPE_OCTET_STREAM);
-    }
-  }
-
-
-
-  static bool GetTagValueInternal(std::string& value,
-                                  DcmItem& item,
-                                  const DicomTag& tag)
-  {
-    DcmTagKey k(tag.GetGroup(), tag.GetElement());
-    DcmElement* element = NULL;
-    if (!item.findAndGetElement(k, element).good() ||
-        element == NULL)
-    {
-      return false;
-    }
-
-    std::auto_ptr<DicomValue> v(FromDcmtkBridge::ConvertLeafElement(*element));
-
-    if (v.get() == NULL)
-    {
-      value = "";
-    }
-    else
-    {
-      value = v->AsString();
-    }
-
-    return true;    
-  }
-
-
-  bool ParsedDicomFile::GetTagValue(std::string& value,
-                                    const DicomTag& tag)
-  {
-    DcmDataset& dataset = *file_->getDataset();
-    return GetTagValueInternal(value, dataset, tag);
-  }
-
-
-
-  bool ParsedDicomFile::GetTagValue(std::string& value,
-                                    const SequencePath& path,
-                                    const DicomTag& tag)
-  {
-    if (path.size() == 0)
-    {
-      return GetTagValue(value, tag);
-    }
-
-    DcmItem* current = file_->getDataset();
-      assert(current != NULL);
-
-    for (SequencePath::const_iterator it = path.begin(); it != path.end(); it++)
-    {
-      DcmTagKey k(it->first.GetGroup(), it->first.GetElement());
-
-      DcmSequenceOfItems* sequence = NULL;
-      if (!current->findAndGetSequence(k, sequence).good() ||
-          sequence == NULL ||
-          sequence->getVR() != EVR_SQ)
-      {
-        return false;
-      }
-
-      if (it->second < 0 || it->second > sequence->card())
-      {
-        return false;
-      }
-
-      current = sequence->getItem(it->second);
-
-      if (current == NULL)
-      {
-        return false;
-      }
-    }
-    
-    return GetTagValueInternal(value, *current, tag);    
-  }
-
-
-
-  DicomInstanceHasher ParsedDicomFile::GetHasher()
-  {
-    std::string patientId, studyUid, seriesUid, instanceUid;
-
-    if (!GetTagValue(patientId, DICOM_TAG_PATIENT_ID) ||
-        !GetTagValue(studyUid, DICOM_TAG_STUDY_INSTANCE_UID) ||
-        !GetTagValue(seriesUid, DICOM_TAG_SERIES_INSTANCE_UID) ||
-        !GetTagValue(instanceUid, DICOM_TAG_SOP_INSTANCE_UID))
-    {
-      throw OrthancException(ErrorCode_BadFileFormat);
-    }
-
-    return DicomInstanceHasher(patientId, studyUid, seriesUid, instanceUid);
-  }
-
-
   void FromDcmtkBridge::Convert(DicomMap& target, DcmDataset& dataset)
   {
     target.Clear();
@@ -1246,345 +428,6 @@
   }
 
 
-  static void ExtractPngImageColorPreview(std::string& result,
-                                          DicomIntegerPixelAccessor& accessor)
-  {
-    assert(accessor.GetChannelCount() == 3);
-    PngWriter w;
-
-    std::vector<uint8_t> image(accessor.GetWidth() * accessor.GetHeight() * 3, 0);
-    uint8_t* pixel = &image[0];
-
-    for (unsigned int y = 0; y < accessor.GetHeight(); y++)
-    {
-      for (unsigned int x = 0; x < accessor.GetWidth(); x++)
-      {
-        for (unsigned int c = 0; c < 3; c++, pixel++)
-        {
-          int32_t v = accessor.GetValue(x, y, c);
-          if (v < 0)
-            *pixel = 0;
-          else if (v > 255)
-            *pixel = 255;
-          else
-            *pixel = v;
-        }
-      }
-    }
-
-    w.WriteToMemory(result, accessor.GetWidth(), accessor.GetHeight(),
-                    accessor.GetWidth() * 3, PixelFormat_RGB24, &image[0]);
-  }
-
-
-  static void ExtractPngImageGrayscalePreview(std::string& result,
-                                              DicomIntegerPixelAccessor& accessor)
-  {
-    assert(accessor.GetChannelCount() == 1);
-    PngWriter w;
-
-    int32_t min, max;
-    accessor.GetExtremeValues(min, max);
-
-    std::vector<uint8_t> image(accessor.GetWidth() * accessor.GetHeight(), 0);
-    if (min != max)
-    {
-      uint8_t* pixel = &image[0];
-      for (unsigned int y = 0; y < accessor.GetHeight(); y++)
-      {
-        for (unsigned int x = 0; x < accessor.GetWidth(); x++, pixel++)
-        {
-          int32_t v = accessor.GetValue(x, y);
-          *pixel = static_cast<uint8_t>(
-            boost::math::lround(static_cast<float>(v - min) / 
-                                static_cast<float>(max - min) * 255.0f));
-        }
-      }
-    }
-
-    w.WriteToMemory(result, accessor.GetWidth(), accessor.GetHeight(),
-                    accessor.GetWidth(), PixelFormat_Grayscale8, &image[0]);
-  }
-
-
-  template <typename T>
-  static void ExtractPngImageTruncate(std::string& result,
-                                      DicomIntegerPixelAccessor& accessor,
-                                      PixelFormat format)
-  {
-    assert(accessor.GetChannelCount() == 1);
-
-    PngWriter w;
-
-    std::vector<T> image(accessor.GetWidth() * accessor.GetHeight(), 0);
-    T* pixel = &image[0];
-    for (unsigned int y = 0; y < accessor.GetHeight(); y++)
-    {
-      for (unsigned int x = 0; x < accessor.GetWidth(); x++, pixel++)
-      {
-        int32_t v = accessor.GetValue(x, y);
-        if (v < static_cast<int32_t>(std::numeric_limits<T>::min()))
-          *pixel = std::numeric_limits<T>::min();
-        else if (v > static_cast<int32_t>(std::numeric_limits<T>::max()))
-          *pixel = std::numeric_limits<T>::max();
-        else
-          *pixel = static_cast<T>(v);
-      }
-    }
-
-    w.WriteToMemory(result, accessor.GetWidth(), accessor.GetHeight(),
-                    accessor.GetWidth() * sizeof(T), format, &image[0]);
-  }
-
-
-  static bool DecodePsmctRle1(std::string& output,
-                              DcmDataset& dataset)
-  {
-    static const DicomTag tagContent(0x07a1, 0x100a);
-    static const DicomTag tagCompressionType(0x07a1, 0x1011);
-
-    DcmElement* e;
-    char* c;
-
-    // Check whether the DICOM instance contains an image encoded with
-    // the PMSCT_RLE1 scheme.
-    if (!dataset.findAndGetElement(ToDcmtkBridge::Convert(tagCompressionType), e).good() ||
-        e == NULL ||
-        !e->isaString() ||
-        !e->getString(c).good() ||
-        c == NULL ||
-        strcmp("PMSCT_RLE1", c))
-    {
-      return false;
-    }
-
-    // OK, this is a custom RLE encoding from Philips. Get the pixel
-    // data from the appropriate private DICOM tag.
-    Uint8* pixData = NULL;
-    if (!dataset.findAndGetElement(ToDcmtkBridge::Convert(tagContent), e).good() ||
-        e == NULL ||
-        e->getUint8Array(pixData) != EC_Normal)
-    {
-      return false;
-    }    
-
-    // The "unsigned" below IS VERY IMPORTANT
-    const uint8_t* inbuffer = reinterpret_cast<const uint8_t*>(pixData);
-    const size_t length = e->getLength();
-
-    /**
-     * The code below is an adaptation of a sample code for GDCM by
-     * Mathieu Malaterre (under a BSD license).
-     * http://gdcm.sourceforge.net/html/rle2img_8cxx-example.html
-     **/
-
-    // RLE pass
-    std::vector<uint8_t> temp;
-    temp.reserve(length);
-    for (size_t i = 0; i < length; i++)
-    {
-      if (inbuffer[i] == 0xa5)
-      {
-        temp.push_back(inbuffer[i+2]);
-        for (uint8_t repeat = inbuffer[i + 1]; repeat != 0; repeat--)
-        {
-          temp.push_back(inbuffer[i+2]);
-        }
-        i += 2;
-      }
-      else
-      {
-        temp.push_back(inbuffer[i]);
-      }
-    }
-
-    // Delta encoding pass
-    uint16_t delta = 0;
-    output.clear();
-    output.reserve(temp.size());
-    for (size_t i = 0; i < temp.size(); i++)
-    {
-      uint16_t value;
-
-      if (temp[i] == 0x5a)
-      {
-        uint16_t v1 = temp[i + 1];
-        uint16_t v2 = temp[i + 2];
-        value = (v2 << 8) + v1;
-        i += 2;
-      }
-      else
-      {
-        value = delta + (int8_t) temp[i];
-      }
-
-      output.push_back(value & 0xff);
-      output.push_back(value >> 8);
-      delta = value;
-    }
-
-    if (output.size() % 2)
-    {
-      output.resize(output.size() - 1);
-    }
-
-    return true;
-  }
-
-
-  void FromDcmtkBridge::ExtractPngImage(std::string& result,
-                                        DcmDataset& dataset,
-                                        unsigned int frame,
-                                        ImageExtractionMode mode)
-  {
-    // See also: http://support.dcmtk.org/wiki/dcmtk/howto/accessing-compressed-data
-
-    std::auto_ptr<DicomIntegerPixelAccessor> accessor;
-
-    DicomMap m;
-    FromDcmtkBridge::Convert(m, dataset);
-
-    std::string privateContent;
-
-    DcmElement* e;
-    if (dataset.findAndGetElement(ToDcmtkBridge::Convert(DICOM_TAG_PIXEL_DATA), e).good() &&
-        e != NULL)
-    {
-      Uint8* pixData = NULL;
-      if (e->getUint8Array(pixData) == EC_Normal)
-      {    
-        accessor.reset(new DicomIntegerPixelAccessor(m, pixData, e->getLength()));
-        accessor->SetCurrentFrame(frame);
-      }
-    }
-    else if (DecodePsmctRle1(privateContent, dataset))
-    {
-      LOG(INFO) << "The PMSCT_RLE1 decoding has succeeded";
-      Uint8* pixData = NULL;
-      if (privateContent.size() > 0)
-        pixData = reinterpret_cast<Uint8*>(&privateContent[0]);
-      accessor.reset(new DicomIntegerPixelAccessor(m, pixData, privateContent.size()));
-      accessor->SetCurrentFrame(frame);
-    }
-    
-    if (accessor.get() == NULL)
-    {
-      throw OrthancException(ErrorCode_BadFileFormat);
-    }
-
-    PixelFormat format;
-    bool supported = false;
-
-    if (accessor->GetChannelCount() == 1)
-    {
-      switch (mode)
-      {
-        case ImageExtractionMode_Preview:
-          supported = true;
-          format = PixelFormat_Grayscale8;
-          break;
-
-        case ImageExtractionMode_UInt8:
-          supported = true;
-          format = PixelFormat_Grayscale8;
-          break;
-
-        case ImageExtractionMode_UInt16:
-          supported = true;
-          format = PixelFormat_Grayscale16;
-          break;
-
-        case ImageExtractionMode_Int16:
-          supported = true;
-          format = PixelFormat_SignedGrayscale16;
-          break;
-
-        default:
-          supported = false;
-          break;
-      }
-    }
-    else if (accessor->GetChannelCount() == 3)
-    {
-      switch (mode)
-      {
-        case ImageExtractionMode_Preview:
-          supported = true;
-          format = PixelFormat_RGB24;
-          break;
-
-        default:
-          supported = false;
-          break;
-      }
-    }
-
-    if (!supported)
-    {
-      throw OrthancException(ErrorCode_NotImplemented);
-    }   
-
-    if (accessor.get() == NULL ||
-        accessor->GetWidth() == 0 ||
-        accessor->GetHeight() == 0)
-    {
-      PngWriter w;
-      w.WriteToMemory(result, 0, 0, 0, format, NULL);
-    }
-    else
-    {
-      switch (mode)
-      {
-        case ImageExtractionMode_Preview:
-          if (format == PixelFormat_Grayscale8)
-            ExtractPngImageGrayscalePreview(result, *accessor);
-          else
-            ExtractPngImageColorPreview(result, *accessor);
-          break;
-
-        case ImageExtractionMode_UInt8:
-          ExtractPngImageTruncate<uint8_t>(result, *accessor, format);
-          break;
-
-        case ImageExtractionMode_UInt16:
-          ExtractPngImageTruncate<uint16_t>(result, *accessor, format);
-          break;
-
-        case ImageExtractionMode_Int16:
-          ExtractPngImageTruncate<int16_t>(result, *accessor, format);
-          break;
-
-        default:
-          throw OrthancException(ErrorCode_NotImplemented);
-      }
-    }
-  }
-
-
-  void FromDcmtkBridge::ExtractPngImage(std::string& result,
-                                        const std::string& dicomContent,
-                                        unsigned int frame,
-                                        ImageExtractionMode mode)
-  {
-    DcmInputBufferStream is;
-    if (dicomContent.size() > 0)
-    {
-      is.setBuffer(&dicomContent[0], dicomContent.size());
-    }
-    is.setEos();
-
-    DcmFileFormat dicom;
-    if (dicom.read(is).good())
-    {
-      ExtractPngImage(result, *dicom.getDataset(), frame, mode);
-    }
-    else
-    {
-      throw OrthancException(ErrorCode_BadFileFormat);
-    }
-  }
-
-
 
   std::string FromDcmtkBridge::GetName(const DicomTag& t)
   {
@@ -1703,25 +546,25 @@
   }
 
 
-  std::string FromDcmtkBridge::GenerateUniqueIdentifier(DicomRootLevel level)
+  std::string FromDcmtkBridge::GenerateUniqueIdentifier(ResourceType level)
   {
     char uid[100];
 
     switch (level)
     {
-      case DicomRootLevel_Patient:
+      case ResourceType_Patient:
         // The "PatientID" field is of type LO (Long String), 64
         // Bytes Maximum. An UUID is of length 36, thus it can be used
         // as a random PatientID.
         return Toolbox::GenerateUuid();
 
-      case DicomRootLevel_Instance:
+      case ResourceType_Instance:
         return dcmGenerateUniqueIdentifier(uid, SITE_INSTANCE_UID_ROOT);
 
-      case DicomRootLevel_Series:
+      case ResourceType_Series:
         return dcmGenerateUniqueIdentifier(uid, SITE_SERIES_UID_ROOT);
 
-      case DicomRootLevel_Study:
+      case ResourceType_Study:
         return dcmGenerateUniqueIdentifier(uid, SITE_STUDY_UID_ROOT);
 
       default: