changeset 90:cb86c0518d74

transcoding is working
author Sebastien Jodogne <s.jodogne@gmail.com>
date Wed, 09 Dec 2015 16:29:31 +0100
parents 4667d4235791
children 2bd3bf4d95bb
files Plugin/Dicom.cpp Plugin/Dicom.h Plugin/QidoRs.cpp Plugin/StowRs.cpp Plugin/WadoRs.cpp
diffstat 5 files changed, 393 insertions(+), 207 deletions(-) [+]
line wrap: on
line diff
--- a/Plugin/Dicom.cpp	Wed Dec 09 11:57:44 2015 +0100
+++ b/Plugin/Dicom.cpp	Wed Dec 09 16:29:31 2015 +0100
@@ -57,120 +57,6 @@
 
 
 
-  void ParsedDicomFile::Setup(const std::string& dicom)
-  {
-    // Prepare a memory stream over the DICOM instance
-    std::stringstream stream(dicom);
-
-    // Parse the DICOM instance using GDCM
-    reader_.SetStream(stream);
-    if (!reader_.Read())
-    {
-      /* "GDCM cannot read this DICOM instance of length " +
-         boost::lexical_cast<std::string>(dicom.size()) */
-      throw Orthanc::OrthancException(Orthanc::ErrorCode_BadFileFormat);
-    }
-  }
-
-
-  ParsedDicomFile::ParsedDicomFile(const OrthancPlugins::MultipartItem& item)
-  {
-    std::string dicom(item.data_, item.data_ + item.size_);
-    Setup(dicom);
-  }
-
-
-  static bool GetTag(std::string& result,
-                     const gdcm::DataSet& dataset,
-                     const gdcm::Tag& tag,
-                     bool stripSpaces)
-  {
-    if (dataset.FindDataElement(tag))
-    {
-      const gdcm::ByteValue* value = dataset.GetDataElement(tag).GetByteValue();
-      if (value)
-      {
-        result = std::string(value->GetPointer(), value->GetLength());
-
-        if (stripSpaces)
-        {
-          result = Orthanc::Toolbox::StripSpaces(result);
-        }
-
-        return true;
-      }
-    }
-
-    return false;
-  }
-
-
-  static std::string GetTagWithDefault(const gdcm::DataSet& dataset,
-                                       const gdcm::Tag& tag,
-                                       const std::string& defaultValue,
-                                       bool stripSpaces)
-  {
-    std::string result;
-    if (!GetTag(result, dataset, tag, false))
-    {
-      result = defaultValue;
-    }
-
-    if (stripSpaces)
-    {
-      result = Orthanc::Toolbox::StripSpaces(result);
-    }
-
-    return result;
-  }
-
-
-  bool ParsedDicomFile::GetTag(std::string& result,
-                               const gdcm::Tag& tag,
-                               bool stripSpaces) const
-  {
-    return OrthancPlugins::GetTag(result, GetDataSet(), tag, stripSpaces);
-  }
-
-
-  std::string ParsedDicomFile::GetTagWithDefault(const gdcm::Tag& tag,
-                                                 const std::string& defaultValue,
-                                                 bool stripSpaces) const
-  {
-    return OrthancPlugins::GetTagWithDefault(GetDataSet(), tag, defaultValue, stripSpaces);
-  }
-
-
-  static std::string FormatTag(const gdcm::Tag& tag)
-  {
-    char tmp[16];
-    sprintf(tmp, "%04X%04X", tag.GetGroup(), tag.GetElement());
-    return std::string(tmp);
-  }
-
-
-  static const char* GetKeyword(const gdcm::Dict& dictionary,
-                                const gdcm::Tag& tag)
-  {
-    const gdcm::DictEntry &entry = dictionary.GetDictEntry(tag);
-    const char* keyword = entry.GetKeyword();
-
-    if (strlen(keyword) != 0)
-    {
-      return keyword;
-    }
-
-    if (tag == DICOM_TAG_RETRIEVE_URL)
-    {
-      return "RetrieveURL";
-    }
-
-    //throw Orthanc::OrthancException("Unknown keyword for tag: " + FormatTag(tag));
-    return NULL;
-  }
-
-
-
   static const char* GetVRName(bool& isSequence,
                                const gdcm::Dict& dictionary,
                                const gdcm::DataElement& element)
@@ -209,6 +95,206 @@
   }
 
 
+
+  static bool ConvertDicomStringToUtf8(std::string& result,
+                                       const gdcm::Dict& dictionary,
+                                       const gdcm::File* file,
+                                       const gdcm::DataElement& element,
+                                       const Orthanc::Encoding sourceEncoding)
+  {
+    const gdcm::ByteValue* data = element.GetByteValue();
+    if (!data)
+    {
+      return false;
+    }
+
+    if (file != NULL)
+    {
+      bool isSequence;
+      std::string vr = GetVRName(isSequence, dictionary, element);
+      if (!isSequence && (
+            vr == "FL" ||
+            vr == "FD" ||
+            vr == "SL" ||
+            vr == "SS" ||
+            vr == "UL" ||
+            vr == "US"
+            ))
+      {      
+        gdcm::StringFilter f;
+        f.SetFile(*file);
+        result = f.ToString(element.GetTag());
+        return true;
+      }
+    }
+
+    if (sourceEncoding == Orthanc::Encoding_Utf8)
+    {
+      result.assign(data->GetPointer(), data->GetLength());
+    }
+    else
+    {
+      std::string tmp(data->GetPointer(), data->GetLength());
+      result = Orthanc::Toolbox::ConvertToUtf8(tmp, sourceEncoding);
+    }
+
+    result = Orthanc::Toolbox::StripSpaces(result);
+    return true;
+  }
+
+
+
+  void ParsedDicomFile::Setup(const std::string& dicom)
+  {
+    // Prepare a memory stream over the DICOM instance
+    std::stringstream stream(dicom);
+
+    // Parse the DICOM instance using GDCM
+    reader_.SetStream(stream);
+    if (!reader_.Read())
+    {
+      /* "GDCM cannot read this DICOM instance of length " +
+         boost::lexical_cast<std::string>(dicom.size()) */
+      throw Orthanc::OrthancException(Orthanc::ErrorCode_BadFileFormat);
+    }
+  }
+
+
+  ParsedDicomFile::ParsedDicomFile(const OrthancPlugins::MultipartItem& item)
+  {
+    std::string dicom(item.data_, item.data_ + item.size_);
+    Setup(dicom);
+  }
+
+
+  static bool GetRawTag(std::string& result,
+                        const gdcm::DataSet& dataset,
+                        const gdcm::Tag& tag,
+                        bool stripSpaces)
+  {
+    if (dataset.FindDataElement(tag))
+    {
+      const gdcm::ByteValue* value = dataset.GetDataElement(tag).GetByteValue();
+      if (value)
+      {
+        result.assign(value->GetPointer(), value->GetLength());
+
+        if (stripSpaces)
+        {
+          result = Orthanc::Toolbox::StripSpaces(result);
+        }
+
+        return true;
+      }
+    }
+
+    return false;
+  }
+
+
+  bool ParsedDicomFile::GetRawTag(std::string& result,
+                                  const gdcm::Tag& tag,
+                                  bool stripSpaces) const
+  {
+    return OrthancPlugins::GetRawTag(result, GetDataSet(), tag, stripSpaces);
+  }
+
+
+  std::string ParsedDicomFile::GetRawTagWithDefault(const gdcm::Tag& tag,
+                                                    const std::string& defaultValue,
+                                                    bool stripSpaces) const
+  {
+    std::string result;
+    if (!GetRawTag(result, tag, stripSpaces))
+    {
+      return defaultValue;
+    }
+    else
+    {
+      return result;
+    }
+  }
+
+
+  bool ParsedDicomFile::GetStringTag(std::string& result,
+                                     const gdcm::Dict& dictionary,
+                                     const gdcm::Tag& tag,
+                                     bool stripSpaces) const
+  {
+    if (!GetDataSet().FindDataElement(tag))
+    {
+      throw Orthanc::OrthancException(Orthanc::ErrorCode_InexistentTag);
+    }
+
+    const gdcm::DataElement& element = GetDataSet().GetDataElement(tag);
+
+    if (!ConvertDicomStringToUtf8(result, dictionary, &GetFile(), element, GetEncoding()))
+    {
+      return false;
+    }
+
+    if (stripSpaces)
+    {
+      result = Orthanc::Toolbox::StripSpaces(result);
+    }
+
+    return true;
+  }
+
+
+  bool ParsedDicomFile::GetIntegerTag(int& result,
+                                      const gdcm::Dict& dictionary,
+                                      const gdcm::Tag& tag) const
+  {
+    std::string tmp;
+    if (!GetStringTag(tmp, dictionary, tag, true))
+    {
+      return false;
+    }
+
+    try
+    {
+      result = boost::lexical_cast<int>(tmp);
+      return true;
+    }
+    catch (boost::bad_lexical_cast&)
+    {
+      return false;
+    }
+  }
+
+
+
+  static std::string FormatTag(const gdcm::Tag& tag)
+  {
+    char tmp[16];
+    sprintf(tmp, "%04X%04X", tag.GetGroup(), tag.GetElement());
+    return std::string(tmp);
+  }
+
+
+  static const char* GetKeyword(const gdcm::Dict& dictionary,
+                                const gdcm::Tag& tag)
+  {
+    const gdcm::DictEntry &entry = dictionary.GetDictEntry(tag);
+    const char* keyword = entry.GetKeyword();
+
+    if (strlen(keyword) != 0)
+    {
+      return keyword;
+    }
+
+    if (tag == DICOM_TAG_RETRIEVE_URL)
+    {
+      return "RetrieveURL";
+    }
+
+    //throw Orthanc::OrthancException("Unknown keyword for tag: " + FormatTag(tag));
+    return NULL;
+  }
+
+
+
   static bool IsBulkData(const std::string& vr)
   {
     /**
@@ -239,9 +325,9 @@
   {
     std::string study, series, instance;
 
-    if (!GetTag(study, dicom, DICOM_TAG_STUDY_INSTANCE_UID, true) ||
-        !GetTag(series, dicom, DICOM_TAG_SERIES_INSTANCE_UID, true) ||
-        !GetTag(instance, dicom, DICOM_TAG_SOP_INSTANCE_UID, true))
+    if (!GetRawTag(study, dicom, DICOM_TAG_STUDY_INSTANCE_UID, true) ||
+        !GetRawTag(series, dicom, DICOM_TAG_SERIES_INSTANCE_UID, true) ||
+        !GetRawTag(instance, dicom, DICOM_TAG_SOP_INSTANCE_UID, true))
     {
       return "";
     }
@@ -285,51 +371,11 @@
   }
 
 
-  static bool ConvertDicomStringToUf8(std::string& result,
-                                      const gdcm::Dict& dictionary,
-                                      const gdcm::File* file,
-                                      const gdcm::DataElement& element,
-                                      const Orthanc::Encoding sourceEncoding)
+  Orthanc::Encoding  ParsedDicomFile::GetEncoding() const
   {
-    const gdcm::ByteValue* data = element.GetByteValue();
-    if (!data)
-    {
-      return false;
-    }
-
-    if (file != NULL)
-    {
-      bool isSequence;
-      std::string vr = GetVRName(isSequence, dictionary, element);
-      if (!isSequence && (
-            vr == "FL" ||
-            vr == "FD" ||
-            vr == "SL" ||
-            vr == "SS" ||
-            vr == "UL" ||
-            vr == "US"
-            ))
-      {      
-        gdcm::StringFilter f;
-        f.SetFile(*file);
-        result = f.ToString(element.GetTag());
-        return true;
-      }
-    }
-
-    if (sourceEncoding == Orthanc::Encoding_Utf8)
-    {
-      result.assign(data->GetPointer(), data->GetLength());
-    }
-    else
-    {
-      std::string tmp(data->GetPointer(), data->GetLength());
-      result = Orthanc::Toolbox::ConvertToUtf8(tmp, sourceEncoding);
-    }
-
-    result = Orthanc::Toolbox::StripSpaces(result);
-    return true;
+    return DetectEncoding(GetDataSet());
   }
+  
 
 
   static void DicomToXmlInternal(pugi::xml_node& target,
@@ -406,7 +452,7 @@
         value.append_attribute("number").set_value("1");
 
         std::string tmp;
-        if (ConvertDicomStringToUf8(tmp, dictionary, file, *it, sourceEncoding)) 
+        if (ConvertDicomStringToUtf8(tmp, dictionary, file, *it, sourceEncoding)) 
         {
           value.append_child(pugi::node_pcdata).set_value(tmp.c_str());
         }
@@ -504,7 +550,7 @@
         node["Value"] = Json::arrayValue;
 
         std::string value;
-        if (ConvertDicomStringToUf8(value, dictionary, file, *it, sourceEncoding)) 
+        if (ConvertDicomStringToUtf8(value, dictionary, file, *it, sourceEncoding)) 
         {
           node["Value"].append(value.c_str());
         }
--- a/Plugin/Dicom.h	Wed Dec 09 11:57:44 2015 +0100
+++ b/Plugin/Dicom.h	Wed Dec 09 16:29:31 2015 +0100
@@ -22,6 +22,8 @@
 
 #include "Configuration.h"
 
+#include "../Orthanc/Core/Enumerations.h"
+
 #include <gdcmReader.h>
 #include <gdcmDataSet.h>
 #include <pugixml.hpp>
@@ -45,6 +47,9 @@
   static const gdcm::Tag DICOM_TAG_ACCESSION_NUMBER(0x0008, 0x0050);
   static const gdcm::Tag DICOM_TAG_SPECIFIC_CHARACTER_SET(0x0008, 0x0005);
   static const gdcm::Tag DICOM_TAG_PIXEL_DATA(0x7fe0, 0x0010);
+  static const gdcm::Tag DICOM_TAG_COLUMNS(0x0028, 0x0011);
+  static const gdcm::Tag DICOM_TAG_ROWS(0x0028, 0x0010);
+  static const gdcm::Tag DICOM_TAG_BITS_ALLOCATED(0x0028, 0x0100);
 
   class ParsedDicomFile
   {
@@ -71,13 +76,24 @@
       return reader_.GetFile().GetDataSet();
     }
 
-    bool GetTag(std::string& result,
-                const gdcm::Tag& tag,
-                bool stripSpaces) const;
+    bool GetRawTag(std::string& result,
+                   const gdcm::Tag& tag,
+                   bool stripSpaces) const;
+
+    std::string GetRawTagWithDefault(const gdcm::Tag& tag,
+                                     const std::string& defaultValue,
+                                     bool stripSpaces) const;
 
-    std::string GetTagWithDefault(const gdcm::Tag& tag,
-                                  const std::string& defaultValue,
-                                  bool stripSpaces) const;
+    bool GetStringTag(std::string& result,
+                      const gdcm::Dict& dictionary,
+                      const gdcm::Tag& tag,
+                      bool stripSpaces) const;
+
+    bool GetIntegerTag(int& result,
+                       const gdcm::Dict& dictionary,
+                       const gdcm::Tag& tag) const;
+
+    Orthanc::Encoding  GetEncoding() const;
   };
 
 
--- a/Plugin/QidoRs.cpp	Wed Dec 09 11:57:44 2015 +0100
+++ b/Plugin/QidoRs.cpp	Wed Dec 09 16:29:31 2015 +0100
@@ -376,16 +376,16 @@
 
       // Set the retrieve URL for WADO-RS
       std::string url = (wadoBase + "studies/" + 
-                         dicom.GetTagWithDefault(OrthancPlugins::DICOM_TAG_STUDY_INSTANCE_UID, "", true));
+                         dicom.GetRawTagWithDefault(OrthancPlugins::DICOM_TAG_STUDY_INSTANCE_UID, "", true));
 
       if (level == QueryLevel_Series || level == QueryLevel_Instance)
       {
-        url += "/series/" + dicom.GetTagWithDefault(OrthancPlugins::DICOM_TAG_SERIES_INSTANCE_UID, "", true);
+        url += "/series/" + dicom.GetRawTagWithDefault(OrthancPlugins::DICOM_TAG_SERIES_INSTANCE_UID, "", true);
       }
 
       if (level == QueryLevel_Instance)
       {
-        url += "/instances/" + dicom.GetTagWithDefault(OrthancPlugins::DICOM_TAG_SOP_INSTANCE_UID, "", true);
+        url += "/instances/" + dicom.GetRawTagWithDefault(OrthancPlugins::DICOM_TAG_SOP_INSTANCE_UID, "", true);
       }
     
       gdcm::DataElement element(OrthancPlugins::DICOM_TAG_RETRIEVE_URL);
--- a/Plugin/StowRs.cpp	Wed Dec 09 11:57:44 2015 +0100
+++ b/Plugin/StowRs.cpp	Wed Dec 09 16:29:31 2015 +0100
@@ -169,9 +169,9 @@
 
     OrthancPlugins::ParsedDicomFile dicom(items[i]);
 
-    std::string studyInstanceUid = dicom.GetTagWithDefault(OrthancPlugins::DICOM_TAG_STUDY_INSTANCE_UID, "", true);
-    std::string sopClassUid = dicom.GetTagWithDefault(OrthancPlugins::DICOM_TAG_SOP_CLASS_UID, "", true);
-    std::string sopInstanceUid = dicom.GetTagWithDefault(OrthancPlugins::DICOM_TAG_SOP_INSTANCE_UID, "", true);
+    std::string studyInstanceUid = dicom.GetRawTagWithDefault(OrthancPlugins::DICOM_TAG_STUDY_INSTANCE_UID, "", true);
+    std::string sopClassUid = dicom.GetRawTagWithDefault(OrthancPlugins::DICOM_TAG_SOP_CLASS_UID, "", true);
+    std::string sopInstanceUid = dicom.GetRawTagWithDefault(OrthancPlugins::DICOM_TAG_SOP_INSTANCE_UID, "", true);
 
     gdcm::Item item;
     item.SetVLToUndefined();
@@ -207,7 +207,7 @@
       {
         std::string url = (wadoBase + 
                            "studies/" + studyInstanceUid +
-                           "/series/" + dicom.GetTagWithDefault(OrthancPlugins::DICOM_TAG_SERIES_INSTANCE_UID, "", true) +
+                           "/series/" + dicom.GetRawTagWithDefault(OrthancPlugins::DICOM_TAG_SERIES_INSTANCE_UID, "", true) +
                            "/instances/" + sopInstanceUid);
 
         SetTag(status, OrthancPlugins::DICOM_TAG_RETRIEVE_URL, gdcm::VR::UT, url);
--- a/Plugin/WadoRs.cpp	Wed Dec 09 11:57:44 2015 +0100
+++ b/Plugin/WadoRs.cpp	Wed Dec 09 16:29:31 2015 +0100
@@ -797,36 +797,160 @@
 
   for (size_t i = 0; i < tokens.size(); i++)
   {
-    frames.push_back(boost::lexical_cast<unsigned int>(tokens[i]));
+    int frame = boost::lexical_cast<int>(tokens[i]);
+    if (frame <= 0)
+    {
+      std::string s = "Invalid frame number (must be > 0): " + tokens[i];
+      OrthancPluginLogError(context_, s.c_str());
+      throw Orthanc::OrthancException(Orthanc::ErrorCode_ParameterOutOfRange);
+    }
+
+    frames.push_back(static_cast<unsigned int>(frame - 1));
   }
 }                           
 
 
 
+static const char* GetMimeType(const gdcm::TransferSyntax& syntax)
+{
+  switch (syntax)
+  {
+    case gdcm::TransferSyntax::ImplicitVRLittleEndian:
+      return "application/octet-stream";
+
+    case gdcm::TransferSyntax::JPEGBaselineProcess1:
+      return "image/dicom+jpeg; transfer-syntax=1.2.840.10008.1.2.4.50";
+
+    case gdcm::TransferSyntax::JPEGExtendedProcess2_4:
+      return "image/dicom+jpeg; transfer-syntax=1.2.840.10008.1.2.4.51";
+    
+    case gdcm::TransferSyntax::JPEGLosslessProcess14:
+      return "image/dicom+jpeg; transfer-syntax=1.2.840.10008.1.2.4.57";
+
+    case gdcm::TransferSyntax::JPEGLosslessProcess14_1:
+      return "image/dicom+jpeg; transferSyntax=1.2.840.10008.1.2.4.70";
+    
+    case gdcm::TransferSyntax::RLELossless:
+      return "image/dicom+rle; transferSyntax=1.2.840.10008.1.2.5";
+
+    case gdcm::TransferSyntax::JPEGLSLossless:
+      return "image/dicom+jpeg-ls; transferSyntax=1.2.840.10008.1.2.4.80";
+
+    case gdcm::TransferSyntax::JPEGLSNearLossless:
+      return "image/dicom+jpeg-ls; transfer-syntax=1.2.840.10008.1.2.4.81";
+
+    case gdcm::TransferSyntax::JPEG2000Lossless:
+      return "image/dicom+jp2; transferSyntax=1.2.840.10008.1.2.4.90";
+
+    case gdcm::TransferSyntax::JPEG2000:
+      return "image/dicom+jp2; transfer-syntax=1.2.840.10008.1.2.4.91";
+
+    case gdcm::TransferSyntax::JPEG2000Part2Lossless:
+      return "image/dicom+jpx; transferSyntax=1.2.840.10008.1.2.4.92";
+
+    case gdcm::TransferSyntax::JPEG2000Part2:
+      return "image/dicom+jpx; transfer-syntax=1.2.840.10008.1.2.4.93";
+
+    default:
+      throw Orthanc::OrthancException(Orthanc::ErrorCode_InternalError);
+  }
+}
+
+
 static bool AnswerFrames(OrthancPluginRestOutput* output,
-                         const gdcm::DataSet& dicom,
+                         const OrthancPlugins::ParsedDicomFile& dicom,
+                         const gdcm::TransferSyntax& syntax,
                          const std::list<unsigned int>& frames)
 {
-  if (!dicom.FindDataElement(OrthancPlugins::DICOM_TAG_PIXEL_DATA))
+  if (!dicom.GetDataSet().FindDataElement(OrthancPlugins::DICOM_TAG_PIXEL_DATA))
   {
     return OrthancPluginErrorCode_IncompatibleImageFormat;
   }
 
-  const gdcm::DataElement& pixelData = dicom.GetDataElement(OrthancPlugins::DICOM_TAG_PIXEL_DATA);
+  const gdcm::DataElement& pixelData = dicom.GetDataSet().GetDataElement(OrthancPlugins::DICOM_TAG_PIXEL_DATA);
   const gdcm::SequenceOfFragments* fragments = pixelData.GetSequenceOfFragments();
 
+  if (OrthancPluginStartMultipartAnswer(context_, output, "related", GetMimeType(syntax)) != OrthancPluginErrorCode_Success)
+  {
+    return false;
+  }
+
   if (fragments == NULL)
   {
-    printf("Single-frame image\n");
-    printf("%d\n", pixelData.GetByteValue()->GetLength());
+    // Single-fragment image
+
+    if (pixelData.GetByteValue() == NULL)
+    {
+      OrthancPluginLogError(context_, "Image was not properly decoded");
+      throw Orthanc::OrthancException(Orthanc::ErrorCode_InternalError);      
+    }
+
+    int width, height, bits;
+
+    if (!dicom.GetIntegerTag(height, *dictionary_, OrthancPlugins::DICOM_TAG_ROWS) ||
+        !dicom.GetIntegerTag(width, *dictionary_, OrthancPlugins::DICOM_TAG_COLUMNS) ||
+        !dicom.GetIntegerTag(bits, *dictionary_, OrthancPlugins::DICOM_TAG_BITS_ALLOCATED))
+    {
+      throw Orthanc::OrthancException(Orthanc::ErrorCode_BadFileFormat);
+    }
+
+    size_t frameSize = height * width * bits / 8;
+    
+    if (pixelData.GetByteValue()->GetLength() % frameSize != 0)
+    {
+      throw Orthanc::OrthancException(Orthanc::ErrorCode_InternalError);      
+    }
+
+    size_t framesCount = pixelData.GetByteValue()->GetLength() / frameSize;
+    const char* buffer = pixelData.GetByteValue()->GetPointer();
+    assert(sizeof(char) == 1);
+
+    for (std::list<unsigned int>::const_iterator 
+           frame = frames.begin(); frame != frames.end(); ++frame)
+    {
+      if (*frame >= framesCount)
+      {
+        std::string s = ("Trying to access frame number " + boost::lexical_cast<std::string>(*frame + 1) + 
+                         " of an image with " + boost::lexical_cast<std::string>(framesCount) + " frames");
+        OrthancPluginLogError(context_, s.c_str());
+        throw Orthanc::OrthancException(Orthanc::ErrorCode_ParameterOutOfRange);
+      }
+      else
+      {
+        const char* p = buffer + (*frame) * frameSize;
+
+        // TODO SET Content-Location
+        if (OrthancPluginSendMultipartItem(context_, output, p, frameSize) != OrthancPluginErrorCode_Success)
+        {
+          throw Orthanc::OrthancException(Orthanc::ErrorCode_NetworkProtocol);      
+        }
+      }
+    }
   }
   else
   {
-    printf("Multi-frame image (%d fragments)\n", fragments->GetNumberOfFragments());
+    // Multi-fragment image, we assume that each fragment corresponds to one frame
 
-    for (gdcm::SequenceOfFragments::SizeType i = 0; i < fragments->GetNumberOfFragments(); i++)
+    for (std::list<unsigned int>::const_iterator 
+           frame = frames.begin(); frame != frames.end(); ++frame)
     {
-      printf("%d: %d\n", i, fragments->GetFragment(i).GetByteValue()->GetLength());
+      if (*frame >= fragments->GetNumberOfFragments())
+      {
+        std::string s = ("Trying to access frame number " + boost::lexical_cast<std::string>(*frame + 1) + 
+                         " of an image with " + boost::lexical_cast<std::string>(fragments->GetNumberOfFragments()) + " frames");
+        OrthancPluginLogError(context_, s.c_str());
+        throw Orthanc::OrthancException(Orthanc::ErrorCode_ParameterOutOfRange);
+      }
+      else
+      {
+        // TODO SET Content-Location
+        if (OrthancPluginSendMultipartItem(context_, output,
+                                           fragments->GetFragment(*frame).GetByteValue()->GetPointer(),
+                                           fragments->GetFragment(*frame).GetByteValue()->GetLength()) != OrthancPluginErrorCode_Success)
+        {
+          throw Orthanc::OrthancException(Orthanc::ErrorCode_NetworkProtocol);      
+        }
+      }
     }
   }
 
@@ -840,8 +964,8 @@
 {
   // storescu -xs localhost 4242 ~/Subversion/orthanc-tests/Database/Multiframe.dcm 
 
-  // curl http://localhost:8042/dicom-web/studies/1.3.51.0.1.1.192.168.29.133.1681753.1681732/series/1.3.12.2.1107.5.2.33.37097.2012041612474981424569674.0.0.0/instances/1.3.12.2.1107.5.2.33.37097.2012041612485517294169680/frames/0
-  // curl http://localhost:8042/dicom-web/studies/1.3.46.670589.7.5.8.80001255161.20000323.151537.1/series/1.3.46.670589.7.5.7.80001255161.20000323.151537.1/instances/1.3.46.670589.7.5.1.981501.20000323.16172540.1.1.13/frames/0
+  // curl http://localhost:8042/dicom-web/studies/1.3.51.0.1.1.192.168.29.133.1681753.1681732/series/1.3.12.2.1107.5.2.33.37097.2012041612474981424569674.0.0.0/instances/1.3.12.2.1107.5.2.33.37097.2012041612485517294169680/frames/1
+  // curl http://localhost:8042/dicom-web/studies/1.3.46.670589.7.5.8.80001255161.20000323.151537.1/series/1.3.46.670589.7.5.7.80001255161.20000323.151537.1/instances/1.3.46.670589.7.5.1.981501.20000323.16172540.1.1.13/frames/1
  
 
   // http://gdcm.sourceforge.net/html/CompressLossyJPEG_8cs-example.html
@@ -859,9 +983,10 @@
   {
     {
       std::string s = "DICOMweb RetrieveFrames on " + uri + ", frames: ";
-      for (std::list<unsigned int>::const_iterator it = frames.begin(); it != frames.end(); ++it)
+      for (std::list<unsigned int>::const_iterator 
+             frame = frames.begin(); frame != frames.end(); ++frame)
       {
-        s += boost::lexical_cast<std::string>(*it) + " ";
+        s += boost::lexical_cast<std::string>(*frame + 1) + " ";
       }
       OrthancPluginLogInfo(context_, s.c_str());
     }
@@ -892,7 +1017,7 @@
         source.reset(new OrthancPlugins::ParsedDicomFile(content));
       }
 
-      AnswerFrames(output, source->GetFile().GetDataSet(), frames);
+      AnswerFrames(output, *source, targetSyntax, frames);
     }
     else
     {
@@ -905,25 +1030,24 @@
       }
 
       gdcm::ImageChangeTransferSyntax change;
-      change.SetTransferSyntax(gdcm::TransferSyntax::JPEG2000Lossless);
-
-      gdcm::JPEG2000Codec codec;
-      if (!codec.CanCode(change.GetTransferSyntax()))
-      {
-        return OrthancPluginErrorCode_Plugin;
-      }
-
-      //codec.SetLossless(true);
-      change.SetUserCodec(&codec);
+      change.SetTransferSyntax(targetSyntax);
 
       std::stringstream stream(content);
 
       gdcm::ImageReader reader;
       reader.SetStream(stream);
-      printf("Read: %d\n", reader.Read());
+      if (!reader.Read())
+      {
+        OrthancPluginLogError(context_, "Cannot decode the image");
+        throw Orthanc::OrthancException(Orthanc::ErrorCode_BadFileFormat);
+      }
 
       change.SetInput(reader.GetImage());
-      printf("Change: %d\n", change.Change());
+      if (!change.Change())
+      {
+        OrthancPluginLogError(context_, "Cannot change the transfer syntax of the image");
+        throw Orthanc::OrthancException(Orthanc::ErrorCode_InternalError);
+      }
 
       gdcm::ImageWriter writer;
       writer.SetImage(change.GetOutput());
@@ -931,13 +1055,13 @@
       
       std::stringstream ss;
       writer.SetStream(ss);
-      printf("Write: %d\n", writer.Write());
+      if (!writer.Write())
+      {
+        throw Orthanc::OrthancException(Orthanc::ErrorCode_NotEnoughMemory);
+      }
 
-      gdcm::Reader reader2;
-      reader2.SetStream(ss);
-      printf("Read: %d\n", reader2.Read());
-
-      AnswerFrames(output, reader2.GetFile().GetDataSet(), frames);
+      OrthancPlugins::ParsedDicomFile transcoded(ss.str());
+      AnswerFrames(output, transcoded, targetSyntax, frames);
     }
   }