# HG changeset patch # User Sebastien Jodogne # Date 1689657216 -7200 # Node ID c1687b8fc800bf25855e10ff76d22913721fad15 # Parent 559499b80da84da909fff8b28411d4cf9b6eaed0 refactoring diff -r 559499b80da8 -r c1687b8fc800 Applications/CMakeLists.txt --- a/Applications/CMakeLists.txt Tue Jul 18 06:45:44 2023 +0200 +++ b/Applications/CMakeLists.txt Tue Jul 18 07:13:36 2023 +0200 @@ -133,6 +133,7 @@ ${ORTHANC_WSI_DIR}/Framework/Outputs/TruncatedPyramidWriter.cpp ${ORTHANC_WSI_DIR}/Framework/Targets/FolderTarget.cpp ${ORTHANC_WSI_DIR}/Framework/Targets/OrthancTarget.cpp + ${ORTHANC_WSI_DIR}/Framework/TiffReader.cpp ) EmbedResources( diff -r 559499b80da8 -r c1687b8fc800 Framework/Inputs/HierarchicalTiff.cpp --- a/Framework/Inputs/HierarchicalTiff.cpp Tue Jul 18 06:45:44 2023 +0200 +++ b/Framework/Inputs/HierarchicalTiff.cpp Tue Jul 18 07:13:36 2023 +0200 @@ -80,16 +80,6 @@ }; - void HierarchicalTiff::Finalize() - { - if (tiff_) - { - TIFFClose(tiff_); - tiff_ = NULL; - } - } - - void HierarchicalTiff::CheckLevel(unsigned int level) const { if (level >= levels_.size()) @@ -99,91 +89,10 @@ } - bool HierarchicalTiff::GetCurrentDirectoryInformation(TIFF* tiff, - ImageCompression& compression, - Orthanc::PixelFormat& pixelFormat, - Orthanc::PhotometricInterpretation& photometric) - { - uint16_t c; - if (!TIFFGetField(tiff, TIFFTAG_COMPRESSION, &c)) - { - return false; - } - - switch (c) - { - case COMPRESSION_NONE: - compression = ImageCompression_None; - break; - - case COMPRESSION_JPEG: - compression = ImageCompression_Jpeg; - break; - - default: - return false; - } - - // http://www.awaresystems.be/imaging/tiff/tifftags/baseline.html - - uint16_t channels, photometricTiff, bpp, planar; - if (!TIFFGetField(tiff, TIFFTAG_SAMPLESPERPIXEL, &channels) || - channels == 0 || - !TIFFGetField(tiff, TIFFTAG_PHOTOMETRIC, &photometricTiff) || - !TIFFGetField(tiff, TIFFTAG_BITSPERSAMPLE, &bpp) || - !TIFFGetField(tiff, TIFFTAG_PLANARCONFIG, &planar)) - { - return false; - } - - if (compression == ImageCompression_Jpeg && - channels == 3 && // This is a color image - bpp == 8 && - planar == PLANARCONFIG_CONTIG) // This is interleaved RGB - { - pixelFormat = Orthanc::PixelFormat_RGB24; - - switch (photometricTiff) - { - case PHOTOMETRIC_YCBCR: - photometric = Orthanc::PhotometricInterpretation_YBRFull422; - return true; - - case PHOTOMETRIC_RGB: - photometric = Orthanc::PhotometricInterpretation_RGB; - return true; - - default: - LOG(ERROR) << "Unknown photometric interpretation in TIFF: " << photometricTiff; - return false; - } - } - else if (compression == ImageCompression_None && - channels == 3 && // This is a color image - bpp == 8 && - planar == PLANARCONFIG_CONTIG) // This is interleaved RGB - { - pixelFormat = Orthanc::PixelFormat_RGB24; - photometric = Orthanc::PhotometricInterpretation_RGB; - return true; - } - else if (compression == ImageCompression_Jpeg && - channels == 1 && // This is a grayscale image - bpp == 8) - { - pixelFormat = Orthanc::PixelFormat_Grayscale8; - photometric = Orthanc::PhotometricInterpretation_Monochrome2; - } - else - { - return false; - } - - return true; - } - - - bool HierarchicalTiff::Initialize() + HierarchicalTiff::HierarchicalTiff(const std::string& path) : + reader_(path), + tileWidth_(0), + tileHeight_(0) { bool first = true; tdir_t pos = 0; @@ -195,16 +104,16 @@ Orthanc::PixelFormat pixelFormat; Orthanc::PhotometricInterpretation photometric; - if (TIFFSetDirectory(tiff_, pos) && - TIFFGetField(tiff_, TIFFTAG_IMAGEWIDTH, &w) && - TIFFGetField(tiff_, TIFFTAG_IMAGELENGTH, &h) && - TIFFGetField(tiff_, TIFFTAG_TILEWIDTH, &tw) && - TIFFGetField(tiff_, TIFFTAG_TILELENGTH, &th) && + if (TIFFSetDirectory(reader_.GetTiff(), pos) && + TIFFGetField(reader_.GetTiff(), TIFFTAG_IMAGEWIDTH, &w) && + TIFFGetField(reader_.GetTiff(), TIFFTAG_IMAGELENGTH, &h) && + TIFFGetField(reader_.GetTiff(), TIFFTAG_TILEWIDTH, &tw) && + TIFFGetField(reader_.GetTiff(), TIFFTAG_TILELENGTH, &th) && w > 0 && h > 0 && tw > 0 && th > 0 && - GetCurrentDirectoryInformation(tiff_, compression, pixelFormat, photometric)) + reader_.GetCurrentDirectoryInformation(compression, pixelFormat, photometric)) { if (first) { @@ -221,44 +130,23 @@ pixelFormat_ != pixelFormat || photometric_ != photometric) { - LOG(ERROR) << "The tile size or compression of the TIFF file varies along levels, this is not supported"; - return false; + throw Orthanc::OrthancException(Orthanc::ErrorCode_BadFileFormat, + "The tile size or compression of the TIFF file varies along levels, this is not supported"); } - levels_.push_back(Level(tiff_, pos, w, h)); + levels_.push_back(Level(reader_.GetTiff(), pos, w, h)); } pos++; } - while (TIFFReadDirectory(tiff_)); + while (TIFFReadDirectory(reader_.GetTiff())); if (levels_.size() == 0) { - LOG(ERROR) << "This is not a tiled TIFF image"; - return false; + throw Orthanc::OrthancException(Orthanc::ErrorCode_BadFileFormat, "This is not a tiled TIFF image"); } std::sort(levels_.begin(), levels_.end(), Comparator()); - return true; - } - - - HierarchicalTiff::HierarchicalTiff(const std::string& path) : - tileWidth_(0), - tileHeight_(0) - { - tiff_ = TIFFOpen(path.c_str(), "r"); - if (tiff_ == NULL) - { - LOG(ERROR) << "libtiff cannot open: " << path; - throw Orthanc::OrthancException(Orthanc::ErrorCode_InexistentFile); - } - - if (!Initialize()) - { - Finalize(); - throw Orthanc::OrthancException(Orthanc::ErrorCode_BadFileFormat); - } } @@ -289,17 +177,17 @@ compression = compression_; // Make the TIFF context point to the level of interest - if (!TIFFSetDirectory(tiff_, levels_[level].directory_)) + if (!TIFFSetDirectory(reader_.GetTiff(), levels_[level].directory_)) { throw Orthanc::OrthancException(Orthanc::ErrorCode_CorruptedFile); } // Get the index of the tile - ttile_t index = TIFFComputeTile(tiff_, tileX * tileWidth_, tileY * tileHeight_, 0 /*z*/, 0 /*sample*/); + ttile_t index = TIFFComputeTile(reader_.GetTiff(), tileX * tileWidth_, tileY * tileHeight_, 0 /*z*/, 0 /*sample*/); // Read the raw tile toff_t *sizes; - if (!TIFFGetField(tiff_, TIFFTAG_TILEBYTECOUNTS, &sizes)) + if (!TIFFGetField(reader_.GetTiff(), TIFFTAG_TILEBYTECOUNTS, &sizes)) { throw Orthanc::OrthancException(Orthanc::ErrorCode_CorruptedFile); } @@ -307,7 +195,7 @@ std::string raw; raw.resize(sizes[index]); - tsize_t read = TIFFReadRawTile(tiff_, index, &raw[0], raw.size()); + tsize_t read = TIFFReadRawTile(reader_.GetTiff(), index, &raw[0], raw.size()); if (read != static_cast(sizes[index])) { throw Orthanc::OrthancException(Orthanc::ErrorCode_CorruptedFile); diff -r 559499b80da8 -r c1687b8fc800 Framework/Inputs/HierarchicalTiff.h --- a/Framework/Inputs/HierarchicalTiff.h Tue Jul 18 06:45:44 2023 +0200 +++ b/Framework/Inputs/HierarchicalTiff.h Tue Jul 18 07:13:36 2023 +0200 @@ -23,9 +23,8 @@ #pragma once #include "PyramidWithRawTiles.h" +#include "../TiffReader.h" -#include -#include #include #include @@ -50,7 +49,7 @@ struct Comparator; boost::mutex mutex_; - TIFF* tiff_; + TiffReader reader_; Orthanc::PixelFormat pixelFormat_; ImageCompression compression_; unsigned int tileWidth_; @@ -58,20 +57,11 @@ std::vector levels_; Orthanc::PhotometricInterpretation photometric_; - void Finalize(); - void CheckLevel(unsigned int level) const; - bool Initialize(); - public: explicit HierarchicalTiff(const std::string& path); - virtual ~HierarchicalTiff() - { - Finalize(); - } - virtual unsigned int GetLevelCount() const ORTHANC_OVERRIDE { return levels_.size(); @@ -111,10 +101,5 @@ { return compression_; } - - static bool GetCurrentDirectoryInformation(TIFF* tiff, - ImageCompression& compression, - Orthanc::PixelFormat& pixelFormat, - Orthanc::PhotometricInterpretation& photometric); }; } diff -r 559499b80da8 -r c1687b8fc800 Framework/TiffReader.cpp --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/Framework/TiffReader.cpp Tue Jul 18 07:13:36 2023 +0200 @@ -0,0 +1,131 @@ +/** + * 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 . + **/ + + +#include "TiffReader.h" + +#include +#include + +namespace OrthancWSI +{ + TiffReader::TiffReader(const std::string& path) + { + tiff_ = TIFFOpen(path.c_str(), "r"); + if (tiff_ == NULL) + { + throw Orthanc::OrthancException(Orthanc::ErrorCode_InexistentFile, + "libtiff cannot open: " + path); + } + } + + + TiffReader::~TiffReader() + { + if (tiff_) + { + TIFFClose(tiff_); + tiff_ = NULL; + } + } + + + bool TiffReader::GetCurrentDirectoryInformation(ImageCompression& compression, + Orthanc::PixelFormat& pixelFormat, + Orthanc::PhotometricInterpretation& photometric) + { + uint16_t c; + if (!TIFFGetField(tiff_, TIFFTAG_COMPRESSION, &c)) + { + return false; + } + + switch (c) + { + case COMPRESSION_NONE: + compression = ImageCompression_None; + break; + + case COMPRESSION_JPEG: + compression = ImageCompression_Jpeg; + break; + + default: + return false; + } + + // http://www.awaresystems.be/imaging/tiff/tifftags/baseline.html + + uint16_t channels, photometricTiff, bpp, planar; + if (!TIFFGetField(tiff_, TIFFTAG_SAMPLESPERPIXEL, &channels) || + channels == 0 || + !TIFFGetField(tiff_, TIFFTAG_PHOTOMETRIC, &photometricTiff) || + !TIFFGetField(tiff_, TIFFTAG_BITSPERSAMPLE, &bpp) || + !TIFFGetField(tiff_, TIFFTAG_PLANARCONFIG, &planar)) + { + return false; + } + + if (compression == ImageCompression_Jpeg && + channels == 3 && // This is a color image + bpp == 8 && + planar == PLANARCONFIG_CONTIG) // This is interleaved RGB + { + pixelFormat = Orthanc::PixelFormat_RGB24; + + switch (photometricTiff) + { + case PHOTOMETRIC_YCBCR: + photometric = Orthanc::PhotometricInterpretation_YBRFull422; + break; + + case PHOTOMETRIC_RGB: + photometric = Orthanc::PhotometricInterpretation_RGB; + break; + + default: + LOG(ERROR) << "Unknown photometric interpretation in TIFF: " << photometricTiff; + return false; + } + } + else if (compression == ImageCompression_None && + channels == 3 && // This is a color image + bpp == 8 && + planar == PLANARCONFIG_CONTIG) // This is interleaved RGB + { + pixelFormat = Orthanc::PixelFormat_RGB24; + photometric = Orthanc::PhotometricInterpretation_RGB; + } + else if (compression == ImageCompression_Jpeg && + channels == 1 && // This is a grayscale image + bpp == 8) + { + pixelFormat = Orthanc::PixelFormat_Grayscale8; + photometric = Orthanc::PhotometricInterpretation_Monochrome2; + } + else + { + return false; + } + + return true; + } +} diff -r 559499b80da8 -r c1687b8fc800 Framework/TiffReader.h --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/Framework/TiffReader.h Tue Jul 18 07:13:36 2023 +0200 @@ -0,0 +1,52 @@ +/** + * 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 . + **/ + + +#pragma once + +#include "Enumerations.h" + +#include +#include +#include + +namespace OrthancWSI +{ + class TiffReader : public boost::noncopyable + { + private: + TIFF* tiff_; + + public: + TiffReader(const std::string& path); + + ~TiffReader(); + + TIFF* GetTiff() + { + return tiff_; + } + + bool GetCurrentDirectoryInformation(ImageCompression& compression, + Orthanc::PixelFormat& pixelFormat, + Orthanc::PhotometricInterpretation& photometric); + }; +} diff -r 559499b80da8 -r c1687b8fc800 NEWS --- a/NEWS Tue Jul 18 06:45:44 2023 +0200 +++ b/NEWS Tue Jul 18 07:13:36 2023 +0200 @@ -1,6 +1,8 @@ Pending changes in the mainline =============================== +* OrthancWSIDicomizer supports plain TIFF, besides hierarchical TIFF + Version 2.0 (2023-10-07) ========================