changeset 874:87791ebc1f50

download matlab images
author Sebastien Jodogne <s.jodogne@gmail.com>
date Wed, 11 Jun 2014 12:23:02 +0200
parents fc34356283e1
children d21031a7a6e4
files Core/ChunkedBuffer.cpp Core/ChunkedBuffer.h Core/ImageFormats/ImageAccessor.cpp Core/ImageFormats/ImageAccessor.h NEWS OrthancCppClient/OrthancCppClient.cpp OrthancServer/FromDcmtkBridge.cpp OrthancServer/FromDcmtkBridge.h OrthancServer/Internals/DicomImageDecoder.cpp OrthancServer/OrthancRestApi/OrthancRestResources.cpp OrthancServer/ParsedDicomFile.cpp OrthancServer/ParsedDicomFile.h
diffstat 12 files changed, 224 insertions(+), 77 deletions(-) [+]
line wrap: on
line diff
--- a/Core/ChunkedBuffer.cpp	Wed Jun 11 09:18:07 2014 +0200
+++ b/Core/ChunkedBuffer.cpp	Wed Jun 11 12:23:02 2014 +0200
@@ -65,6 +65,15 @@
   }
 
 
+  void ChunkedBuffer::AddChunk(const std::string& chunk)
+  {
+    if (chunk.size() > 0)
+    {
+      AddChunk(&chunk[0], chunk.size());
+    }
+  }
+
+
   void ChunkedBuffer::Flatten(std::string& result)
   {
     result.resize(numBytes_);
--- a/Core/ChunkedBuffer.h	Wed Jun 11 09:18:07 2014 +0200
+++ b/Core/ChunkedBuffer.h	Wed Jun 11 12:23:02 2014 +0200
@@ -64,6 +64,8 @@
     void AddChunk(const char* chunkData,
                   size_t chunkSize);
 
+    void AddChunk(const std::string& chunk);
+
     void Flatten(std::string& result);
   };
 }
--- a/Core/ImageFormats/ImageAccessor.cpp	Wed Jun 11 09:18:07 2014 +0200
+++ b/Core/ImageFormats/ImageAccessor.cpp	Wed Jun 11 12:23:02 2014 +0200
@@ -34,13 +34,72 @@
 #include "ImageAccessor.h"
 
 #include "../OrthancException.h"
+#include "../ChunkedBuffer.h"
 
 #include <stdint.h>
 #include <cassert>
 #include <glog/logging.h>
+#include <boost/lexical_cast.hpp>
 
 namespace Orthanc
 {
+  template <typename PixelType>
+  static void ToMatlabStringInternal(ChunkedBuffer& target,
+                                     const ImageAccessor& source)
+  {
+    target.AddChunk("double([ ");
+
+    for (unsigned int y = 0; y < source.GetHeight(); y++)
+    {
+      const PixelType* p = reinterpret_cast<const PixelType*>(source.GetConstRow(y));
+
+      std::string s;
+      if (y > 0)
+      {
+        s = "; ";
+      }
+
+      s.reserve(source.GetWidth() * 8);
+
+      for (unsigned int x = 0; x < source.GetWidth(); x++, p++)
+      {
+        s += boost::lexical_cast<std::string>(static_cast<int>(*p)) + " ";
+      }
+
+      target.AddChunk(s);
+    }
+
+    target.AddChunk("])");
+  }
+
+
+  static void RGB24ToMatlabString(ChunkedBuffer& target,
+                                  const ImageAccessor& source)
+  {
+    assert(source.GetFormat() == PixelFormat_RGB24);
+
+    target.AddChunk("double(permute(reshape([ ");
+
+    for (unsigned int y = 0; y < source.GetHeight(); y++)
+    {
+      const uint8_t* p = reinterpret_cast<const uint8_t*>(source.GetConstRow(y));
+      
+      std::string s;
+      s.reserve(source.GetWidth() * 3 * 8);
+      
+      for (unsigned int x = 0; x < 3 * source.GetWidth(); x++, p++)
+      {
+        s += boost::lexical_cast<std::string>(static_cast<int>(*p)) + " ";
+      }
+      
+      target.AddChunk(s);
+    }
+
+    target.AddChunk("], [ 3 " + boost::lexical_cast<std::string>(source.GetHeight()) +
+                    " " + boost::lexical_cast<std::string>(source.GetWidth()) + " ]), [ 3 2 1 ]))");
+  }
+
+
   void* ImageAccessor::GetBuffer() const
   {
     if (readOnly_)
@@ -128,4 +187,35 @@
 
     assert(GetBytesPerPixel(format_) * width_ <= pitch_);
   }
+
+
+  void ImageAccessor::ToMatlabString(std::string& target) const
+  {
+    ChunkedBuffer buffer;
+
+    switch (GetFormat())
+    {
+      case PixelFormat_Grayscale8:
+        ToMatlabStringInternal<uint8_t>(buffer, *this);
+        break;
+
+      case PixelFormat_Grayscale16:
+        ToMatlabStringInternal<uint16_t>(buffer, *this);
+        break;
+
+      case PixelFormat_SignedGrayscale16:
+        ToMatlabStringInternal<int16_t>(buffer, *this);
+        break;
+
+      case PixelFormat_RGB24:
+        RGB24ToMatlabString(buffer, *this);
+        break;
+
+      default:
+        throw OrthancException(ErrorCode_NotImplemented);
+    }   
+
+    buffer.Flatten(target);
+  }
+
 }
--- a/Core/ImageFormats/ImageAccessor.h	Wed Jun 11 09:18:07 2014 +0200
+++ b/Core/ImageFormats/ImageAccessor.h	Wed Jun 11 12:23:02 2014 +0200
@@ -106,5 +106,7 @@
                         unsigned int height,
                         unsigned int pitch,
                         void *buffer);
+
+    void ToMatlabString(std::string& target) const; 
   };
 }
--- a/NEWS	Wed Jun 11 09:18:07 2014 +0200
+++ b/NEWS	Wed Jun 11 12:23:02 2014 +0200
@@ -2,6 +2,7 @@
 ===============================
 
 * Support of JPEG and JPEG-LS decompression
+* Download DICOM images as Matlab/Octave arrays
 * Precompiled headers for Microsoft Visual Studio
 
 
--- a/OrthancCppClient/OrthancCppClient.cpp	Wed Jun 11 09:18:07 2014 +0200
+++ b/OrthancCppClient/OrthancCppClient.cpp	Wed Jun 11 12:23:02 2014 +0200
@@ -35,6 +35,7 @@
  * avoid problems with precompiled headers.
  **/
 
+#include "../Core/ChunkedBuffer.cpp"
 #include "../Core/Enumerations.cpp"
 #include "../Core/HttpClient.cpp"
 #include "../Core/ImageFormats/ImageAccessor.cpp"
--- a/OrthancServer/FromDcmtkBridge.cpp	Wed Jun 11 09:18:07 2014 +0200
+++ b/OrthancServer/FromDcmtkBridge.cpp	Wed Jun 11 12:23:02 2014 +0200
@@ -428,71 +428,6 @@
   }
 
 
-  void FromDcmtkBridge::ExtractPngImage(std::string& result,
-                                        DcmDataset& dataset,
-                                        unsigned int frame,
-                                        ImageExtractionMode mode)
-  {
-    ImageBuffer tmp;
-    bool ok = false;
-
-    switch (mode)
-    {
-      case ImageExtractionMode_UInt8:
-        ok = DicomImageDecoder::DecodeAndTruncate(tmp, dataset, frame, PixelFormat_Grayscale8);
-        break;
-
-      case ImageExtractionMode_UInt16:
-        ok = DicomImageDecoder::DecodeAndTruncate(tmp, dataset, frame, PixelFormat_Grayscale16);
-        break;
-
-      case ImageExtractionMode_Int16:
-        ok = DicomImageDecoder::DecodeAndTruncate(tmp, dataset, frame, PixelFormat_SignedGrayscale16);
-        break;
-
-      case ImageExtractionMode_Preview:
-        ok = DicomImageDecoder::DecodePreview(tmp, dataset, frame);
-        break;
-
-      default:
-        throw OrthancException(ErrorCode_ParameterOutOfRange);
-    }
-
-    if (!ok)
-    {
-      throw OrthancException(ErrorCode_BadFileFormat);
-    }
-
-    ImageAccessor accessor(tmp.GetAccessor());
-    PngWriter writer;
-    writer.WriteToMemory(result, accessor);
-  }
-
-
-  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)
   {
--- a/OrthancServer/FromDcmtkBridge.h	Wed Jun 11 09:18:07 2014 +0200
+++ b/OrthancServer/FromDcmtkBridge.h	Wed Jun 11 12:23:02 2014 +0200
@@ -58,16 +58,6 @@
                        const std::string& path,
                        unsigned int maxStringLength = 256);
 
-    static void ExtractPngImage(std::string& result,
-                                DcmDataset& dataset,
-                                unsigned int frame,
-                                ImageExtractionMode mode);
-
-    static void ExtractPngImage(std::string& result,
-                                const std::string& dicomContent,
-                                unsigned int frame,
-                                ImageExtractionMode mode);
-
     static std::string GetName(const DicomTag& tag);
 
     static DicomTag ParseTag(const char* name);
--- a/OrthancServer/Internals/DicomImageDecoder.cpp	Wed Jun 11 09:18:07 2014 +0200
+++ b/OrthancServer/Internals/DicomImageDecoder.cpp	Wed Jun 11 12:23:02 2014 +0200
@@ -77,6 +77,9 @@
 
 
 
+#include "../PrecompiledHeadersServer.h"
+#include "DicomImageDecoder.h"
+
 #include "../../Core/OrthancException.h"
 #include "../../Core/ImageFormats/ImageProcessing.h"
 #include "../../Core/ImageFormats/PngWriter.h"  // TODO REMOVE THIS
@@ -323,7 +326,7 @@
                    << (info.IsPlanar() ? ", planar" : ", non-planar");
       throw OrthancException(ErrorCode_NotImplemented);
     }
-    
+
     target.SetHeight(info.GetHeight());
     target.SetWidth(info.GetWidth());
     target.SetFormat(format);
--- a/OrthancServer/OrthancRestApi/OrthancRestResources.cpp	Wed Jun 11 09:18:07 2014 +0200
+++ b/OrthancServer/OrthancRestApi/OrthancRestResources.cpp	Wed Jun 11 12:23:02 2014 +0200
@@ -202,9 +202,11 @@
     std::string dicomContent, png;
     context.ReadFile(dicomContent, publicId, FileContentType_Dicom);
 
+    ParsedDicomFile dicom(dicomContent);
+
     try
     {
-      FromDcmtkBridge::ExtractPngImage(png, dicomContent, frame, mode);
+      dicom.ExtractPngImage(png, frame, mode);
       call.GetOutput().AnswerBuffer(png, "image/png");
     }
     catch (OrthancException& e)
@@ -228,6 +230,39 @@
   }
 
 
+  static void GetMatlabImage(RestApi::GetCall& call)
+  {
+    ServerContext& context = OrthancRestApi::GetContext(call);
+
+    std::string frameId = call.GetUriComponent("frame", "0");
+
+    unsigned int frame;
+    try
+    {
+      frame = boost::lexical_cast<unsigned int>(frameId);
+    }
+    catch (boost::bad_lexical_cast)
+    {
+      return;
+    }
+
+    std::string publicId = call.GetUriComponent("id", "");
+    std::string dicomContent;
+    context.ReadFile(dicomContent, publicId, FileContentType_Dicom);
+
+    ParsedDicomFile dicom(dicomContent);
+    ImageBuffer buffer;
+    dicom.ExtractImage(buffer, frame);
+
+    ImageAccessor accessor(buffer.GetConstAccessor());
+
+    std::string result;
+    accessor.ToMatlabString(result);
+
+    call.GetOutput().AnswerBuffer(result, "text/plain");
+  }
+
+
 
   static void GetResourceStatistics(RestApi::GetCall& call)
   {
@@ -587,10 +622,12 @@
     Register("/instances/{id}/frames/{frame}/image-uint8", GetImage<ImageExtractionMode_UInt8>);
     Register("/instances/{id}/frames/{frame}/image-uint16", GetImage<ImageExtractionMode_UInt16>);
     Register("/instances/{id}/frames/{frame}/image-int16", GetImage<ImageExtractionMode_Int16>);
+    Register("/instances/{id}/frames/{frame}/matlab", GetMatlabImage);
     Register("/instances/{id}/preview", GetImage<ImageExtractionMode_Preview>);
     Register("/instances/{id}/image-uint8", GetImage<ImageExtractionMode_UInt8>);
     Register("/instances/{id}/image-uint16", GetImage<ImageExtractionMode_UInt16>);
     Register("/instances/{id}/image-int16", GetImage<ImageExtractionMode_Int16>);
+    Register("/instances/{id}/matlab", GetMatlabImage);
 
     Register("/patients/{id}/protected", IsProtectedPatient);
     Register("/patients/{id}/protected", SetPatientProtection);
--- a/OrthancServer/ParsedDicomFile.cpp	Wed Jun 11 09:18:07 2014 +0200
+++ b/OrthancServer/ParsedDicomFile.cpp	Wed Jun 11 12:23:02 2014 +0200
@@ -82,8 +82,10 @@
 
 #include "FromDcmtkBridge.h"
 #include "ToDcmtkBridge.h"
+#include "Internals/DicomImageDecoder.h"
 #include "../Core/Toolbox.h"
 #include "../Core/OrthancException.h"
+#include "../Core/ImageFormats/ImageBuffer.h"
 #include "../Core/ImageFormats/PngWriter.h"
 #include "../Core/Uuid.h"
 #include "../Core/DicomFormat/DicomString.h"
@@ -1214,4 +1216,67 @@
       throw OrthancException(ErrorCode_InternalError);
     }    
   }
+
+  
+  void ParsedDicomFile::ExtractImage(ImageBuffer& result,
+                                     unsigned int frame)
+  {
+    DcmDataset& dataset = *pimpl_->file_->getDataset();
+
+    if (!DicomImageDecoder::Decode(result, dataset, frame))
+    {
+      throw OrthancException(ErrorCode_BadFileFormat);
+    }
+  }
+
+
+  void ParsedDicomFile::ExtractImage(ImageBuffer& result,
+                                     unsigned int frame,
+                                     ImageExtractionMode mode)
+  {
+    DcmDataset& dataset = *pimpl_->file_->getDataset();
+
+    bool ok = false;
+
+    switch (mode)
+    {
+      case ImageExtractionMode_UInt8:
+        ok = DicomImageDecoder::DecodeAndTruncate(result, dataset, frame, PixelFormat_Grayscale8);
+        break;
+
+      case ImageExtractionMode_UInt16:
+        ok = DicomImageDecoder::DecodeAndTruncate(result, dataset, frame, PixelFormat_Grayscale16);
+        break;
+
+      case ImageExtractionMode_Int16:
+        ok = DicomImageDecoder::DecodeAndTruncate(result, dataset, frame, PixelFormat_SignedGrayscale16);
+        break;
+
+      case ImageExtractionMode_Preview:
+        ok = DicomImageDecoder::DecodePreview(result, dataset, frame);
+        break;
+
+      default:
+        throw OrthancException(ErrorCode_ParameterOutOfRange);
+    }
+
+    if (!ok)
+    {
+      throw OrthancException(ErrorCode_BadFileFormat);
+    }
+  }
+
+
+  void ParsedDicomFile::ExtractPngImage(std::string& result,
+                                        unsigned int frame,
+                                        ImageExtractionMode mode)
+  {
+    ImageBuffer buffer;
+    ExtractImage(buffer, frame, mode);
+
+    ImageAccessor accessor(buffer.GetConstAccessor());
+    PngWriter writer;
+    writer.WriteToMemory(result, accessor);
+  }
+
 }
--- a/OrthancServer/ParsedDicomFile.h	Wed Jun 11 09:18:07 2014 +0200
+++ b/OrthancServer/ParsedDicomFile.h	Wed Jun 11 12:23:02 2014 +0200
@@ -36,6 +36,7 @@
 #include "../Core/RestApi/RestApiOutput.h"
 #include "ServerEnumerations.h"
 #include "../Core/ImageFormats/ImageAccessor.h"
+#include "../Core/ImageFormats/ImageBuffer.h"
 
 namespace Orthanc
 {
@@ -92,6 +93,17 @@
     void EmbedImage(const ImageAccessor& accessor);
 
     void EmbedImage(const std::string& dataUriScheme);
+
+    void ExtractImage(ImageBuffer& result,
+                      unsigned int frame);
+
+    void ExtractImage(ImageBuffer& result,
+                      unsigned int frame,
+                      ImageExtractionMode mode);
+
+    void ExtractPngImage(std::string& result,
+                         unsigned int frame,
+                         ImageExtractionMode mode);
   };
 
 }