src/tiffDirectory.cpp (255 lines of code) (raw):

// Copyright 2022 Google LLC // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. #include <cmath> #include <memory> #include <string> #include <boost/log/trivial.hpp> #include "src/tiffDirectory.h" namespace wsiToDicomConverter { TiffDirectory::TiffDirectory(TIFF *tiff) { // comment # is tiff tag number directoryIndex_ = TIFFCurrentDirectory(tiff); _getTiffField_ui32(tiff, TIFFTAG_SUBFILETYPE, &subfileType_); // 254 _getTiffField_ui32(tiff, TIFFTAG_IMAGEWIDTH, &imageWidth_); // 256 _getTiffField_ui32(tiff, TIFFTAG_IMAGELENGTH, &imageHeight_); // 257 _getTiffField_ui16(tiff, TIFFTAG_BITSPERSAMPLE, &bitsPerSample_); // 258 _getTiffField_ui16(tiff, TIFFTAG_COMPRESSION, &compression_); // 259 _getTiffField_ui16(tiff, TIFFTAG_PHOTOMETRIC, &photoMetric_); // 262 _getTiffField_str(tiff, TIFFTAG_IMAGEDESCRIPTION, &imageDescription_); // 270 _getTiffField_ui16(tiff, TIFFTAG_ORIENTATION, &orientation_); // 274 _getTiffField_ui16(tiff, TIFFTAG_SAMPLESPERPIXEL, &samplePerPixel_); // 277 _getTiffField_ui32(tiff, TIFFTAG_ROWSPERSTRIP, &rowsPerStrip_); // 278 _getTiffField_f(tiff, TIFFTAG_XRESOLUTION, &xResolution_); // 282 _getTiffField_f(tiff, TIFFTAG_YRESOLUTION, &yResolution_); // 283 _getTiffField_ui16(tiff, TIFFTAG_PLANARCONFIG, &planarConfig_); // 284 _getTiffField_ui32(tiff, TIFFTAG_TILEWIDTH, &tileWidth_); // 322 _getTiffField_ui32(tiff, TIFFTAG_TILELENGTH, &tileHeight_); // 323 _getTiffField_ui32(tiff, TIFFTAG_IMAGEDEPTH, &imageDepth_); // 32997 hasIccProfile_ = _hasICCProfile(tiff); // 34675 _getTiffField_ui32(tiff, TIFFTAG_TILEDEPTH, &tileDepth_); // 32998 isTiled_ = TIFFIsTiled(tiff); if (!isTiled_) { tileCount_ = 0; } else { tileCount_ = TIFFNumberOfTiles(tiff); // tiles_x * tiles_y * tiles_z } _getTiffField_jpegTables(tiff, &jpegTableDataSize_, &jpegTableData_); _getTiffField_ui32(tiff, TIFFTAG_JPEGQUALITY, &jpegQuality_); _getTiffField_ui32(tiff, TIFFTAG_JPEGCOLORMODE, &jpegColorMode_); _getTiffField_ui32(tiff, TIFFTAG_JPEGTABLESMODE, &jpegTableMode_); } TiffDirectory::TiffDirectory(const TiffDirectory &dir) { directoryIndex_ = dir.directoryIndex_; subfileType_ = dir.subfileType_; imageWidth_ = dir.imageWidth_; imageHeight_ = dir.imageHeight_; bitsPerSample_ = dir.bitsPerSample_; compression_ = dir.compression_; photoMetric_ = dir.photoMetric_; imageDescription_ = dir.imageDescription_; orientation_ = dir.orientation_; samplePerPixel_ = dir.samplePerPixel_; rowsPerStrip_ = dir.rowsPerStrip_; planarConfig_ = dir.planarConfig_; tileWidth_ = dir.tileWidth_; tileHeight_ = dir.tileHeight_; imageDepth_ = dir.imageDepth_; tileDepth_ = dir.tileDepth_; hasIccProfile_ = dir.hasIccProfile_; xResolution_ = dir.xResolution_; yResolution_ = dir.yResolution_; tileCount_ = dir.tileCount_; isTiled_ = dir.isTiled_; jpegTableDataSize_ = dir.jpegTableDataSize_; if (jpegTableDataSize_ <= 0 || dir.jpegTableData_ == nullptr) { jpegTableData_ = nullptr; } else { jpegTableData_ = std::make_unique<uint8_t[]>(jpegTableDataSize_); memcpy(jpegTableData_.get(), dir.jpegTableData_.get(), jpegTableDataSize_); } jpegQuality_ = dir.jpegQuality_; jpegColorMode_ = dir.jpegColorMode_; jpegTableMode_ = dir.jpegTableMode_; photoMetricStr_ = isPhotoMetricRGB() ? "RGB" : "YBR_FULL_422"; } TiffDirectory::~TiffDirectory() {} tdir_t TiffDirectory::directoryIndex() const { return directoryIndex_; } bool TiffDirectory::hasICCProfile() const { return hasIccProfile_; } // 34675 int64_t TiffDirectory::subfileType() const { return subfileType_; } // 254 int64_t TiffDirectory::imageWidth() const { return imageWidth_; } // 256 int64_t TiffDirectory::imageHeight() const { return imageHeight_; } // 257 int64_t TiffDirectory::imageDepth() const { return imageDepth_; } // 32997 int64_t TiffDirectory::bitsPerSample() const { return bitsPerSample_; } // 258 int64_t TiffDirectory::compression() const { return compression_; } // 259 int64_t TiffDirectory::photometric() const { return photoMetric_; } // 262 int64_t TiffDirectory::jpegQuality() const { return jpegQuality_; } int64_t TiffDirectory::jpegColorMode() const { return jpegColorMode_; } int64_t TiffDirectory::jpegTableMode() const { return jpegTableMode_; } int64_t TiffDirectory::jpegTableDataSize() const { return jpegTableDataSize_; } uint8_t* TiffDirectory::jpegTableData() const { return jpegTableData_.get(); } bool TiffDirectory::hasJpegTableData() const { return ((jpegTableData_ != nullptr) && (jpegTableDataSize_ > 0)); } std::string TiffDirectory::imageDescription() const { // 270 return imageDescription_; } int64_t TiffDirectory::orientation() const { return orientation_; } // 274 int64_t TiffDirectory::samplesPerPixel() const { return samplePerPixel_; // 277 } int64_t TiffDirectory::RowsPerStrip() const { return rowsPerStrip_; } // 278 int64_t TiffDirectory::planarConfiguration() const { return planarConfig_; // 284 } int64_t TiffDirectory::tileWidth() const { return tileWidth_; } // 322 int64_t TiffDirectory::tileHeight() const { return tileHeight_; } // 323 int64_t TiffDirectory::tileDepth() const { return tileDepth_; } // 32998 double TiffDirectory::xResolution() const { return xResolution_; } // 282 double TiffDirectory::yResolution() const { return yResolution_; } // 283 absl::string_view TiffDirectory::photoMetrIntStr() const { return photoMetricStr_.c_str(); } int64_t TiffDirectory::tilesPerRow() const { if ((imageWidth_ == -1) || (tileWidth_ <= 0)) { return -1; } double tilesPerRow = static_cast<double>(imageWidth_) / static_cast<double>(tileWidth_); return static_cast<int64_t>(std::ceil(tilesPerRow)); } int64_t TiffDirectory::tilesPerColumn() const { if ((imageHeight_ == -1) || (tileHeight_ <= 0)) { return -1; } double tilesPerColumn = static_cast<double>(imageHeight_) / static_cast<double>(tileHeight_); return static_cast<int64_t>(std::ceil(tilesPerColumn)); } int64_t TiffDirectory::tileCount() const { return tileCount_; } bool TiffDirectory::isTiled() const { return isTiled_ && tileCount_ > 0; } bool TiffDirectory::isPyramidImage() const { return isTiled_ && subfileType_ == 0; } bool TiffDirectory::isThumbnailImage() const { return !isTiled_ && subfileType_ == 0; } bool TiffDirectory::isMacroImage() const { return !isTiled_ && subfileType_ == 0x9; } bool TiffDirectory::isLabelImage() const { return !isTiled_ && subfileType_ == 0x1; } bool TiffDirectory::isJpegCompressed() const { return (compression_ == COMPRESSION_JPEG); } bool TiffDirectory::isJpeg2kCompressed() const { // Aperio 33003: YCbCr format, possibly with a chroma subsampling of 4:2:2. const int compression_aperio_YCbCr = 33003; // Aperio 33005: RGB format const int compression_aperio_RGB = 33005; return (compression_ == COMPRESSION_JP2000 || compression_ == compression_aperio_YCbCr || compression_ == compression_aperio_RGB); } bool TiffDirectory::isPhotoMetricRGB() const { return (photoMetric_ == PHOTOMETRIC_RGB); } bool TiffDirectory::isPhotoMetricYCBCR() const { return (photoMetric_ == PHOTOMETRIC_YCBCR); } void TiffDirectory::log() const { BOOST_LOG_TRIVIAL(info) << "Tiff File Directory\n" << "----------------------\n" << " isJpegCompressed: " << isJpegCompressed() << "\n" << " isJpeg2kCompressed: " << isJpeg2kCompressed() << "\n" << " isPyramidImage: " << isPyramidImage() << "\n" << " isPhotoMetricYCBCR: " << isPhotoMetricYCBCR() << "\n" << " isPhotoMetricRGB: " << isPhotoMetricRGB() << "\n" << " tileCount: " << tileCount() << "\n" << " tileWidth: " << tileWidth() << "\n" << " tileHeight: " << tileHeight() << "\n" << " imageDepth: " << imageDepth() << "\n" << " tilesPerRow: " << tilesPerRow() << "\n" << " tilesPerColumn: " << tilesPerColumn() << "\n" << " tileCount: " << tileCount() << "\n" << " photoMetric_: " << photoMetric_ << "\n" << "----------------------\n" << " hasJpegTableData: " << hasJpegTableData() << "\n" << " jpegTableDataSize: " << jpegTableDataSize() << "\n" << " jpegTableMode: " << jpegTableMode() << "\n" << " jpegColorMode: " << jpegColorMode() << "\n" << " jpegQuality: " << jpegQuality() << "\n" << "----------------------\n"; } bool TiffDirectory::isExtractablePyramidImage() const { return ((isJpegCompressed() || isJpeg2kCompressed()) && isPyramidImage() && (isPhotoMetricYCBCR() || isPhotoMetricRGB()) && (tileCount() > 0) && (tileWidth() > 0) && (tileHeight() > 0) && (imageWidth() > 0) && (imageHeight() > 0) && ((imageDepth() == 1) || (tilesPerRow() * tilesPerColumn() == tileCount()))); } bool TiffDirectory::doImageDimensionsMatch(int64_t width, int64_t height) const { return (imageWidth_ == width && imageHeight_ == height); } bool TiffDirectory::isSet(int64_t val) const { return val != -1; } bool TiffDirectory::isSet(double val) const { return val != -1.0; } bool TiffDirectory::isSet(std::string val) const { return val != ""; } void TiffDirectory::_getTiffField_ui32(TIFF *tiff, ttag_t tag, int64_t *val) const { uint32_t normalint = 0; if (1 == TIFFGetField(tiff, tag, &normalint)) { *val = static_cast<int64_t>(normalint); } else { *val = -1; } } void TiffDirectory::_getTiffField_ui16(TIFF *tiff, ttag_t tag, int64_t *val) const { uint16_t normalint = 0; if (1 == TIFFGetField(tiff, tag, &normalint)) { *val = static_cast<int64_t>(normalint); } else { *val = -1; } } void TiffDirectory::_getTiffField_f(TIFF *tiff, ttag_t tag, double *val) const { float flt; if (1 == TIFFGetField(tiff, tag, &flt)) { *val = static_cast<double>(flt); } else { *val = -1.0; } } void TiffDirectory::_getTiffField_str(TIFF *tiff, ttag_t tag, std::string *val) const { char *str; int result = TIFFGetField(tiff, tag, &str); if (result == 1) { *val = str; } else { *val = ""; } } void TiffDirectory::_getTiffField_jpegTables(TIFF *tiff, int64_t* jpegTableDataSize, std::unique_ptr<uint8_t[]> *jpegTableData) const { uint16_t size = 0; void *tableData; *jpegTableDataSize = -1; *jpegTableData = nullptr; int result = TIFFGetField(tiff, TIFFTAG_JPEGTABLES, &size, &tableData); if (result == 1) { *jpegTableDataSize = static_cast<int64_t>(size); if (*jpegTableDataSize > 0) { *jpegTableData = std::make_unique<uint8_t[]>(size); memcpy((*jpegTableData).get(), tableData, size); } } } bool TiffDirectory::_hasICCProfile(TIFF *tiff) const { uint32_t temp; void *ptr; return (1 == TIFFGetField(tiff, TIFFTAG_ICCPROFILE, &temp, &ptr)); } } // namespace wsiToDicomConverter