diff Framework/Inputs/PlainTiff.cpp @ 298:fa734a851551

New option: "tiff-alignment" to control deep zoom of plain TIFF over IIIF
author Sebastien Jodogne <s.jodogne@gmail.com>
date Tue, 18 Jul 2023 08:19:16 +0200
parents
children 7020852a8fa9
line wrap: on
line diff
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/Framework/Inputs/PlainTiff.cpp	Tue Jul 18 08:19:16 2023 +0200
@@ -0,0 +1,166 @@
+/**
+ * Orthanc - A Lightweight, RESTful DICOM Store
+ * Copyright (C) 2012-2016 Sebastien Jodogne, Medical Physics
+ * Department, University Hospital of Liege, Belgium
+ * Copyright (C) 2017-2023 Osimis S.A., Belgium
+ * Copyright (C) 2021-2023 Sebastien Jodogne, ICTEAM UCLouvain, 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 "PlainTiff.h"
+
+#include "../TiffReader.h"
+
+#include <Images/Image.h>
+#include <Images/ImageProcessing.h>
+#include <Logging.h>
+#include <OrthancException.h>
+
+#include <cassert>
+#include <string.h>
+
+
+namespace OrthancWSI
+{
+  PlainTiff::PlainTiff(const std::string& path,
+                       unsigned int tileWidth,
+                       unsigned int tileHeight,
+                       unsigned int paddingAlignement,
+                       uint8_t paddingRed,
+                       uint8_t paddingGreen,
+                       uint8_t paddingBlue) :
+    SingleLevelDecodedPyramid(tileWidth, tileHeight)
+  {
+    TiffReader reader(path);
+
+    // Look for the largest sub-image
+    bool first = true;
+    unsigned int width = 0;
+    unsigned int height = 0;
+    tdir_t largest = 0;
+
+    tdir_t pos = 0;
+
+    do
+    {
+      uint32_t w, h, tw, th;
+
+      if (TIFFSetDirectory(reader.GetTiff(), pos) &&
+          !TIFFGetField(reader.GetTiff(), TIFFTAG_TILEWIDTH, &tw) &&   // Must not be a tiled image
+          !TIFFGetField(reader.GetTiff(), TIFFTAG_TILELENGTH, &th) &&  // Must not be a tiled image
+          TIFFGetField(reader.GetTiff(), TIFFTAG_IMAGEWIDTH, &w) &&
+          TIFFGetField(reader.GetTiff(), TIFFTAG_IMAGELENGTH, &h) &&
+          w > 0 &&
+          h > 0)
+      {
+        if (first)
+        {
+          first = false;
+          width = w;
+          height = h;
+          largest = pos;
+        }
+        else if (w > width &&
+                 h > height)
+        {
+          width = w;
+          height = h;
+          largest = pos;
+        }
+      }
+
+      pos++;
+    }
+    while (TIFFReadDirectory(reader.GetTiff()));
+
+    if (first)
+    {
+      throw Orthanc::OrthancException(Orthanc::ErrorCode_BadFileFormat, "This is an empty TIFF image");
+    }
+
+    // Back to the largest directory
+    if (!TIFFSetDirectory(reader.GetTiff(), largest))
+    {
+      throw Orthanc::OrthancException(Orthanc::ErrorCode_CorruptedFile);
+    }
+
+    ImageCompression compression;
+    Orthanc::PixelFormat pixelFormat;
+    Orthanc::PhotometricInterpretation photometric;
+
+    if (!reader.GetCurrentDirectoryInformation(compression, pixelFormat, photometric))
+    {
+      throw Orthanc::OrthancException(Orthanc::ErrorCode_NotImplemented);
+    }
+
+    if (pixelFormat != Orthanc::PixelFormat_RGB24)
+    {
+      throw Orthanc::OrthancException(Orthanc::ErrorCode_NotImplemented);
+    }
+
+    LOG(WARNING) << "Size of the source TIFF image: " << width << "x" << height;
+
+    const unsigned int paddedWidth = paddingAlignement * CeilingDivision(width, paddingAlignement);
+    const unsigned int paddedHeight = paddingAlignement * CeilingDivision(height, paddingAlignement);
+    assert(paddedWidth >= width &&
+           paddedHeight >= height);
+
+    LOG(WARNING) << "Size of the padded TIFF image: " << paddedWidth << "x" << paddedHeight;
+
+    decoded_.reset(new Orthanc::Image(pixelFormat, paddedWidth, paddedHeight, false));
+    Orthanc::ImageProcessing::Set(*decoded_, paddingRed, paddingGreen, paddingBlue, 255);
+
+    std::string strip;
+    strip.resize(TIFFStripSize(reader.GetTiff()));
+
+    const size_t stripPitch = width * Orthanc::GetBytesPerPixel(pixelFormat);
+
+    if (strip.empty() ||
+        stripPitch == 0 ||
+        strip.size() < stripPitch ||
+        strip.size() % stripPitch != 0)
+    {
+      throw Orthanc::OrthancException(Orthanc::ErrorCode_InternalError);
+    }
+
+    const size_t stripHeight = (strip.size() / stripPitch);
+    const size_t stripCount = CeilingDivision(height, stripHeight);
+
+    if (TIFFNumberOfStrips(reader.GetTiff()) != stripCount)
+    {
+      throw Orthanc::OrthancException(Orthanc::ErrorCode_InternalError);
+    }
+
+    for (unsigned int i = 0; i < stripCount; i++)
+    {
+      TIFFReadEncodedStrip(reader.GetTiff(), i, &strip[0], static_cast<tsize_t>(-1));
+
+      const unsigned int y = i * stripHeight;
+
+      const uint8_t* p = reinterpret_cast<const uint8_t*>(&strip[0]);
+      uint8_t* q = reinterpret_cast<uint8_t*>(decoded_->GetRow(y));
+
+      for (unsigned j = 0; j < stripHeight && y + j < height; j++)
+      {
+        memcpy(q, p, stripPitch);
+        p += stripPitch;
+        q += decoded_->GetPitch();
+      }
+    }
+
+    SetImage(*decoded_);
+  }
+}