changeset 53:293038baf8f1

access to multi-frame images
author Sebastien Jodogne <s.jodogne@gmail.com>
date Mon, 10 Sep 2012 15:17:02 +0200
parents e0cac5540668
children 42a449dac415
files Core/DicomFormat/DicomMap.cpp NEWS PalanthirServer/DicomIntegerPixelAccessor.cpp PalanthirServer/DicomIntegerPixelAccessor.h PalanthirServer/FromDcmtkBridge.cpp PalanthirServer/FromDcmtkBridge.h PalanthirServer/PalanthirRestApi.cpp
diffstat 7 files changed, 125 insertions(+), 17 deletions(-) [+]
line wrap: on
line diff
--- a/Core/DicomFormat/DicomMap.cpp	Fri Sep 07 12:53:38 2012 +0200
+++ b/Core/DicomFormat/DicomMap.cpp	Mon Sep 10 15:17:02 2012 +0200
@@ -76,6 +76,7 @@
     DicomTag(0x0008, 0x0018), // SOPInstanceUID
     DicomTag(0x0020, 0x0012), // AcquisitionNumber
     DicomTag(0x0020, 0x0013), // InstanceNumber
+    DicomTag(0x0028, 0x0008), // NumberOfFrames
     DicomTag(0x0054, 0x1330)  // ImageIndex
   };
 
--- a/NEWS	Fri Sep 07 12:53:38 2012 +0200
+++ b/NEWS	Mon Sep 10 15:17:02 2012 +0200
@@ -2,6 +2,7 @@
 ===============
 
 * Renaming to "Palanthir"
+* Access to multi-frame images
 * Access to the raw PNG images (in 8bpp and 16bpp)
 * Security: Support of SSL, HTTP Basic Authentication and interdiction
   of remote access
--- a/PalanthirServer/DicomIntegerPixelAccessor.cpp	Fri Sep 07 12:53:38 2012 +0200
+++ b/PalanthirServer/DicomIntegerPixelAccessor.cpp	Mon Sep 10 15:17:02 2012 +0200
@@ -57,8 +57,24 @@
       throw PalanthirException(ErrorCode_NotImplemented);
     }
 
-    if (bitsAllocated != 8 && bitsAllocated != 16 && 
-        bitsAllocated != 24 && bitsAllocated != 32)
+    frame_ = 0;
+    try
+    {
+      numberOfFrames_ = boost::lexical_cast<unsigned int>(FromDcmtkBridge::GetValue(values, "NumberOfFrames").AsString());
+    }
+    catch (PalanthirException)
+    {
+      // If the tag "NumberOfFrames" is absent, assume there is a single frame
+      numberOfFrames_ = 1;
+    }
+    catch (boost::bad_lexical_cast)
+    {
+      throw PalanthirException(ErrorCode_NotImplemented);
+    }
+
+    if ((bitsAllocated != 8 && bitsAllocated != 16 && 
+         bitsAllocated != 24 && bitsAllocated != 32) ||
+        numberOfFrames_ == 0)
     {
       throw PalanthirException(ErrorCode_NotImplemented);
     }
@@ -69,19 +85,19 @@
       // Not available, as the accessor internally uses int32_t values
       throw PalanthirException(ErrorCode_NotImplemented);
     }
-    
+
     if (samplesPerPixel_ != 1)
     {
       throw PalanthirException(ErrorCode_NotImplemented);
     }
 
-    if (width_ * height_ * bitsAllocated / 8 != size)
+    if (width_ * height_ * bitsAllocated / 8 * numberOfFrames_ != size)
     {
       throw PalanthirException(ErrorCode_NotImplemented);
     }
 
-    /*printf("%d %d %d %d %d %d %d\n", width_, height_, samplesPerPixel_, bitsAllocated,
-      bitsStored, highBit, pixelRepresentation);*/
+    /*printf("%d %d %d %d %d %d %d %d\n", width_, height_, samplesPerPixel_, bitsAllocated,
+           bitsStored, highBit, pixelRepresentation, numberOfFrames_);*/
 
     bytesPerPixel_ = bitsAllocated / 8;
     shift_ = highBit + 1 - bitsStored;
@@ -96,6 +112,9 @@
       mask_ = (1 << bitsStored) - 1;
       signMask_ = 0;
     }
+
+    rowOffset_ = width_ * bytesPerPixel_;
+    frameOffset_ = height_ * width_ * bytesPerPixel_;
   }
 
 
@@ -129,7 +148,8 @@
   {
     assert(x < width_ && y < height_);
     
-    const uint8_t* pixel = reinterpret_cast<const uint8_t*>(pixelData_) + (y * width_ + x) * bytesPerPixel_;
+    const uint8_t* pixel = reinterpret_cast<const uint8_t*>(pixelData_) + 
+      y * rowOffset_ + x * bytesPerPixel_ + frame_ * frameOffset_;
 
     int32_t v;
     v = pixel[0];
@@ -151,4 +171,16 @@
 
     return v;
   }
+
+
+  void DicomIntegerPixelAccessor::SetCurrentFrame(unsigned int frame)
+  {
+    if (frame >= numberOfFrames_)
+    {
+      throw PalanthirException(ErrorCode_ParameterOutOfRange);
+    }
+
+    frame_ = frame;
+  }
+
 }
--- a/PalanthirServer/DicomIntegerPixelAccessor.h	Fri Sep 07 12:53:38 2012 +0200
+++ b/PalanthirServer/DicomIntegerPixelAccessor.h	Mon Sep 10 15:17:02 2012 +0200
@@ -32,6 +32,7 @@
     unsigned int width_;
     unsigned int height_;
     unsigned int samplesPerPixel_;
+    unsigned int numberOfFrames_;
     const void* pixelData_;
     size_t size_;
 
@@ -39,6 +40,10 @@
     uint32_t signMask_;
     uint32_t mask_;
     size_t bytesPerPixel_;
+    unsigned int frame_;
+
+    size_t frameOffset_;
+    size_t rowOffset_;
 
   public:
     DicomIntegerPixelAccessor(const DicomMap& values,
@@ -55,6 +60,18 @@
       return height_;
     }
 
+    unsigned int GetNumberOfFrames() const
+    {
+      return numberOfFrames_;
+    }
+
+    unsigned int GetCurrentFrame() const
+    {
+      return frame_;
+    }
+
+    void SetCurrentFrame(unsigned int frame);
+
     void GetExtremeValues(int32_t& min, 
                           int32_t& max) const;
 
--- a/PalanthirServer/FromDcmtkBridge.cpp	Fri Sep 07 12:53:38 2012 +0200
+++ b/PalanthirServer/FromDcmtkBridge.cpp	Mon Sep 10 15:17:02 2012 +0200
@@ -438,6 +438,7 @@
 
   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
@@ -455,6 +456,7 @@
       if (e->getUint8Array(pixData) == EC_Normal)
       {    
         accessor.reset(new DicomIntegerPixelAccessor(m, pixData, e->getLength()));
+        accessor->SetCurrentFrame(frame);
       }
     }
 
@@ -506,6 +508,7 @@
 
   void FromDcmtkBridge::ExtractPngImage(std::string& result,
                                         const std::string& dicomContent,
+                                        unsigned int frame,
                                         ImageExtractionMode mode)
   {
     DcmInputBufferStream is;
@@ -518,7 +521,7 @@
     DcmFileFormat dicom;
     if (dicom.read(is).good())
     {
-      ExtractPngImage(result, *dicom.getDataset(), mode);
+      ExtractPngImage(result, *dicom.getDataset(), frame, mode);
     }
     else
     {
--- a/PalanthirServer/FromDcmtkBridge.h	Fri Sep 07 12:53:38 2012 +0200
+++ b/PalanthirServer/FromDcmtkBridge.h	Mon Sep 10 15:17:02 2012 +0200
@@ -52,10 +52,12 @@
 
     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);
--- a/PalanthirServer/PalanthirRestApi.cpp	Fri Sep 07 12:53:38 2012 +0200
+++ b/PalanthirServer/PalanthirRestApi.cpp	Mon Sep 10 15:17:02 2012 +0200
@@ -568,30 +568,82 @@
 
     else if (uri.size() == 3 &&
              uri[0] == "instances" &&
-             (uri[2] == "preview" ||
-              uri[2] == "image-uint8" ||
-              uri[2] == "image-uint16"))
+             uri[2] == "frames")
+    {
+      Json::Value instance(Json::objectValue);
+      existingResource = index_.GetInstance(instance, uri[1]);
+
+      if (existingResource)
+      {
+        result = Json::arrayValue;
+
+        unsigned int numberOfFrames = 1;
+        try
+        {
+          Json::Value tmp = instance["MainDicomTags"]["NumberOfFrames"];
+          numberOfFrames = boost::lexical_cast<unsigned int>(tmp.asString());
+        }
+        catch (boost::bad_lexical_cast)
+        {
+        }
+
+        for (unsigned int i = 0; i < numberOfFrames; i++)
+        {
+          result.append(i);
+        }                
+      }
+    }
+
+
+    else if (uri[0] == "instances" &&
+             ((uri.size() == 3 &&
+               (uri[2] == "preview" || 
+                uri[2] == "image-uint8" || 
+                uri[2] == "image-uint16")) ||
+              (uri.size() == 5 &&
+               uri[2] == "frames" &&
+               (uri[4] == "preview" || 
+                uri[4] == "image-uint8" || 
+                uri[4] == "image-uint16"))))
     {
       std::string uuid;
       existingResource = index_.GetDicomFile(uuid, uri[1]);
 
+      std::string action = uri[2];
+
+      unsigned int frame = 0;
+      if (existingResource &&
+          uri.size() == 5)
+      {
+        // Access to multi-frame image
+        action = uri[4];
+        try
+        {
+          frame = boost::lexical_cast<unsigned int>(uri[3]);
+        }
+        catch (boost::bad_lexical_cast)
+        {
+          existingResource = false;
+        }
+      }
+
       if (existingResource)
       {
         std::string dicomContent, png;
         storage_.ReadFile(dicomContent, uuid);
         try
         {
-          if (uri[2] == "preview")
+          if (action == "preview")
           {
-            FromDcmtkBridge::ExtractPngImage(png, dicomContent, ImageExtractionMode_Preview);
+            FromDcmtkBridge::ExtractPngImage(png, dicomContent, frame, ImageExtractionMode_Preview);
           }
-          else if (uri[2] == "image-uint8")
+          else if (action == "image-uint8")
           {
-            FromDcmtkBridge::ExtractPngImage(png, dicomContent, ImageExtractionMode_UInt8);
+            FromDcmtkBridge::ExtractPngImage(png, dicomContent, frame, ImageExtractionMode_UInt8);
           }
-          else if (uri[2] == "image-uint16")
+          else if (action == "image-uint16")
           {
-            FromDcmtkBridge::ExtractPngImage(png, dicomContent, ImageExtractionMode_UInt16);
+            FromDcmtkBridge::ExtractPngImage(png, dicomContent, frame, ImageExtractionMode_UInt16);
           }
           else
           {