diff Framework/Algorithms/PyramidReader.cpp @ 0:4a7a53257c7d

initial commit
author Sebastien Jodogne <s.jodogne@gmail.com>
date Sat, 22 Oct 2016 21:48:33 +0200
parents
children 7a88c614be04
line wrap: on
line diff
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/Framework/Algorithms/PyramidReader.cpp	Sat Oct 22 21:48:33 2016 +0200
@@ -0,0 +1,305 @@
+/**
+ * Orthanc - A Lightweight, RESTful DICOM Store
+ * Copyright (C) 2012-2016 Sebastien Jodogne, Medical Physics
+ * Department, University Hospital of Liege, Belgium
+ *
+ * This program is free software: you can redistribute it and/or
+ * modify it under the terms of the GNU Affero General Public License
+ * as published by the Free Software Foundation, either version 3 of
+ * the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Affero General Public License for more details.
+ * 
+ * You should have received a copy of the GNU Affero General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ **/
+
+
+#include "PyramidReader.h"
+
+#include "../ImageToolbox.h"
+#include "../Orthanc/Core/Logging.h"
+#include "../Orthanc/Core/OrthancException.h"
+
+#include <cassert>
+
+namespace OrthancWSI
+{
+  class PyramidReader::SourceTile : public boost::noncopyable
+  {
+  private: 
+    PyramidReader&  that_;
+    unsigned int    tileX_;
+    unsigned int    tileY_;
+    bool            hasRawTile_;
+    std::string     rawTile_;
+
+    std::auto_ptr<Orthanc::ImageAccessor>  decoded_;
+
+    bool IsRepaintNeeded() const
+    {
+      return (that_.parameters_.IsRepaintBackground() &&
+              ((tileX_ + 1) * that_.sourceTileWidth_ > that_.levelWidth_ ||
+               (tileY_ + 1) * that_.sourceTileHeight_ > that_.levelHeight_));
+    }
+
+    void RepaintBackground()
+    {
+      assert(decoded_.get() != NULL);
+      LOG(INFO) << "Repainting background of tile ("
+                << tileX_ << "," << tileY_ << ") at level " << that_.level_;
+
+      if ((tileY_ + 1) * that_.sourceTileHeight_ > that_.levelHeight_)
+      {
+        // Bottom overflow
+        assert(tileY_ * that_.sourceTileHeight_ < that_.levelHeight_);
+
+        unsigned int bottom = that_.levelHeight_ - tileY_ * that_.sourceTileHeight_;
+        Orthanc::ImageAccessor a = decoded_->GetRegion(0, bottom, 
+                                                       that_.sourceTileWidth_, 
+                                                       that_.sourceTileHeight_ - bottom);
+        ImageToolbox::Set(a, 
+                          that_.parameters_.GetBackgroundColorRed(),
+                          that_.parameters_.GetBackgroundColorGreen(),
+                          that_.parameters_.GetBackgroundColorBlue());
+
+      }
+
+      if ((tileX_ + 1) * that_.sourceTileWidth_ > that_.levelWidth_)
+      {
+        // Right overflow
+        assert(tileX_ * that_.sourceTileWidth_ < that_.levelWidth_);
+
+        unsigned int right = that_.levelWidth_ - tileX_ * that_.sourceTileWidth_;
+        Orthanc::ImageAccessor a = decoded_->GetRegion(right, 0, 
+                                                       that_.sourceTileWidth_ - right, 
+                                                       that_.sourceTileHeight_);
+        ImageToolbox::Set(a,
+                          that_.parameters_.GetBackgroundColorRed(),
+                          that_.parameters_.GetBackgroundColorGreen(),
+                          that_.parameters_.GetBackgroundColorBlue());
+      }
+    }
+
+
+  public:
+    SourceTile(PyramidReader& that,
+               unsigned int tileX,
+               unsigned int tileY) :
+      that_(that),
+      tileX_(tileX),
+      tileY_(tileY)
+    {
+      if (!that_.parameters_.IsForceReencode() &&
+          !IsRepaintNeeded() &&
+          that_.source_.ReadRawTile(rawTile_, that_.level_, tileX, tileY))
+      {
+        hasRawTile_ = true;
+      }
+      else
+      {
+        hasRawTile_ = false;
+        decoded_.reset(that_.source_.DecodeTile(that_.level_, tileX, tileY));
+        if (decoded_.get() == NULL)
+        {
+          throw Orthanc::OrthancException(Orthanc::ErrorCode_InternalError);
+        }
+
+        RepaintBackground();
+      }
+    }
+
+    bool HasRawTile() const
+    {
+      return hasRawTile_;
+    }
+
+    const std::string& GetRawTile() const
+    {
+      if (hasRawTile_)
+      {
+        return rawTile_;
+      }
+      else
+      {
+        throw Orthanc::OrthancException(Orthanc::ErrorCode_InternalError);
+      }
+    }
+
+    const Orthanc::ImageAccessor& GetDecodedTile()
+    {
+      if (decoded_.get() == NULL)
+      {
+        if (!hasRawTile_)
+        {
+          throw Orthanc::OrthancException(Orthanc::ErrorCode_InternalError);
+        }
+
+        decoded_.reset(ImageToolbox::DecodeTile(rawTile_, that_.source_.GetImageCompression()));
+        if (decoded_.get() == NULL)
+        {
+          throw Orthanc::OrthancException(Orthanc::ErrorCode_InternalError);
+        }
+          
+        RepaintBackground();
+      }
+
+      return *decoded_;
+    }
+  };
+
+
+  Orthanc::ImageAccessor& PyramidReader::GetOutsideTile()
+  {
+    if (outside_.get() == NULL)
+    {
+      outside_.reset(ImageToolbox::Allocate(source_.GetPixelFormat(), targetTileWidth_, targetTileHeight_));
+      ImageToolbox::Set(*outside_,
+                        parameters_.GetBackgroundColorRed(),
+                        parameters_.GetBackgroundColorGreen(),
+                        parameters_.GetBackgroundColorBlue());
+    }
+
+    return *outside_;
+  }
+
+
+  void PyramidReader::CheckTileSize(const Orthanc::ImageAccessor& tile) const
+  {
+    if (tile.GetWidth() != sourceTileWidth_ ||
+        tile.GetHeight() != sourceTileHeight_)
+    {
+      LOG(ERROR) << "One tile in the input image has size " << tile.GetWidth() << "x" << tile.GetHeight() 
+                 << " instead of required " << source_.GetTileWidth() << "x" << source_.GetTileHeight();
+      throw Orthanc::OrthancException(Orthanc::ErrorCode_IncompatibleImageSize);
+    }
+  }
+
+
+  void PyramidReader::CheckTileSize(const std::string& tile) const
+  {
+    if (parameters_.IsSafetyCheck())
+    {
+      std::auto_ptr<Orthanc::ImageAccessor> decoded(ImageToolbox::DecodeTile(tile, source_.GetImageCompression()));
+      CheckTileSize(*decoded);
+    }
+  }
+
+
+  PyramidReader::SourceTile& PyramidReader::AccessSourceTile(const Location& location)
+  {
+    Cache::iterator found = cache_.find(location);
+    if (found != cache_.end())
+    {
+      return *found->second;
+    }
+    else
+    {
+      SourceTile *tile = new SourceTile(*this, location.first, location.second);
+      cache_[location] = tile;
+      return *tile;
+    }
+  }
+
+
+  PyramidReader::Location PyramidReader::MapTargetToSourceLocation(unsigned int tileX,
+                                                                   unsigned int tileY)
+  {
+    return std::make_pair(tileX / (sourceTileWidth_ / targetTileWidth_),
+                          tileY / (sourceTileHeight_ / targetTileHeight_));
+  }
+
+
+  PyramidReader::PyramidReader(ITiledPyramid& source,
+                               unsigned int level,
+                               unsigned int targetTileWidth,
+                               unsigned int targetTileHeight,
+                               const DicomizerParameters& parameters) :
+    source_(source),
+    level_(level),
+    levelWidth_(source.GetLevelWidth(level)),
+    levelHeight_(source.GetLevelHeight(level)),
+    sourceTileWidth_(source.GetTileWidth()),
+    sourceTileHeight_(source.GetTileHeight()),
+                                                                         targetTileWidth_(targetTileWidth),
+                                                                         targetTileHeight_(targetTileHeight),
+                                                                         parameters_(parameters)
+  {
+    if (sourceTileWidth_ % targetTileWidth_ != 0 ||
+        sourceTileHeight_ % targetTileHeight_ != 0)
+    {
+      LOG(ERROR) << "When resampling the tile size, it must be a integer divisor of the original tile size";
+      throw Orthanc::OrthancException(Orthanc::ErrorCode_IncompatibleImageSize);
+    }
+  }
+
+
+  PyramidReader::~PyramidReader()
+  {
+    for (Cache::iterator it = cache_.begin(); it != cache_.end(); ++it)
+    {
+      assert(it->second != NULL);
+      delete it->second;
+    }
+  }
+
+
+  const std::string* PyramidReader::GetRawTile(unsigned int tileX,
+                                               unsigned int tileY)
+  {
+    if (sourceTileWidth_ != targetTileWidth_ ||
+        sourceTileHeight_ != targetTileHeight_)
+    {
+      return NULL;
+    }
+
+    SourceTile& source = AccessSourceTile(MapTargetToSourceLocation(tileX, tileY));
+    if (source.HasRawTile())
+    {
+      CheckTileSize(source.GetRawTile());
+      return &source.GetRawTile();
+    }
+    else
+    {
+      return NULL;
+    }
+  }
+
+
+  Orthanc::ImageAccessor PyramidReader::GetDecodedTile(unsigned int tileX,
+                                                       unsigned int tileY)
+  {
+    if (tileX * targetTileWidth_ >= levelWidth_ ||
+        tileY * targetTileHeight_ >= levelHeight_)
+    {
+      // Accessing a tile out of the source image
+      return GetOutsideTile();
+    }
+
+    SourceTile& source = AccessSourceTile(MapTargetToSourceLocation(tileX, tileY));
+    const Orthanc::ImageAccessor& tile = source.GetDecodedTile();
+
+    CheckTileSize(tile);
+
+    assert(sourceTileWidth_ % targetTileWidth_ == 0 &&
+           sourceTileHeight_ % targetTileHeight_ == 0);
+
+    unsigned int xx = tileX % (sourceTileWidth_ / targetTileWidth_);
+    unsigned int yy = tileY % (sourceTileHeight_ / targetTileHeight_);
+
+    const uint8_t* bytes = 
+      reinterpret_cast<const uint8_t*>(tile.GetConstRow(yy * targetTileHeight_)) +
+      GetBytesPerPixel(tile.GetFormat()) * xx * targetTileWidth_;
+
+    Orthanc::ImageAccessor region;
+    region.AssignReadOnly(tile.GetFormat(),
+                          targetTileWidth_,
+                          targetTileHeight_,
+                          tile.GetPitch(), bytes);                                    
+
+    return region;
+  }
+}