diff --git a/CMakeLists.txt b/CMakeLists.txt index eb55629d..41cf0041 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -3,7 +3,7 @@ PROJECT(DIAGPathology) SET(CURRENT_MAJOR_VERSION 1) SET(CURRENT_MINOR_VERSION 7) -SET(CURRENT_PATCH_VERSION 1) +SET(CURRENT_PATCH_VERSION 3) set(ASAP_VERSION ${CURRENT_MAJOR_VERSION}.${CURRENT_MINOR_VERSION}.${CURRENT_PATCH_VERSION}) include (GenerateExportHeader) diff --git a/core/Patch.h b/core/Patch.h index ef59bbfa..ff5815da 100644 --- a/core/Patch.h +++ b/core/Patch.h @@ -14,6 +14,8 @@ class Patch : public ImageSource { bool _ownData; std::vector _dimensions; std::vector _strides; + std::vector _wsiMinValues; + std::vector _wsiMaxValues; void calculateStrides(); void swap(Patch& first, Patch& second); @@ -24,7 +26,7 @@ public : ~Patch(); Patch(const Patch& rhs); Patch& operator=(const Patch rhs); - Patch(const std::vector& dimensions, const pathology::ColorType& ctype = pathology::Monochrome, T* data = NULL, bool ownData = true); + Patch(const std::vector& dimensions, const pathology::ColorType& ctype = pathology::Monochrome, T* data = NULL, bool ownData = true, std::vector wsiMinValues = std::vector(), std::vector wsiMaxValues = std::vector()); // Arithmetic operators Patch operator*(const T& val); @@ -41,8 +43,13 @@ public : std::vector getStrides(); + double getWSIMinValue(int channel = -1) const; + double getWSIMaxValue(int channel = -1) const; + double getMinValue(int channel = -1); double getMaxValue(int channel = -1); + double getMinValue(int channel = -1) const; + double getMaxValue(int channel = -1) const; T getValue(const std::vector& index) const; void setValue(const std::vector& index, const T& value); diff --git a/core/Patch.hpp b/core/Patch.hpp index 47afbc1a..0119dd21 100644 --- a/core/Patch.hpp +++ b/core/Patch.hpp @@ -59,6 +59,32 @@ Patch::~Patch() } } +template +double Patch::getWSIMinValue(int channel) const { + if (channel < 0) { + return *std::max_element(_wsiMinValues.begin(), _wsiMinValues.end()); + } + else if (channel < _wsiMinValues.size()) { + return _wsiMinValues[channel]; + } + else{ + return std::numeric_limits::max(); + } +} + +template +double Patch::getWSIMaxValue(int channel) const { + if (channel < 0) { + return *std::max_element(_wsiMaxValues.begin(), _wsiMaxValues.end()); + } + else if (channel < _wsiMaxValues.size()) { + return _wsiMaxValues[channel]; + } + else{ + return std::numeric_limits::min(); + } +} + template double Patch::getMinValue(int channel) { double min = std::numeric_limits::max(); @@ -85,6 +111,32 @@ double Patch::getMaxValue(int channel) { return max; } +template +double Patch::getMinValue(int channel) const { + double min = std::numeric_limits::max(); + if (_buffer) { + for (int i = 0; i < _bufferSize; ++i) { + if (_buffer[i] < min) { + min = _buffer[i]; + } + } + } + return min; +} + +template +double Patch::getMaxValue(int channel) const { + double max = std::numeric_limits::min(); + if (_buffer) { + for (int i = 0; i < _bufferSize; ++i) { + if (_buffer[i] > max) { + max = _buffer[i]; + } + } + } + return max; +} + template const int Patch::getSamplesPerPixel() const { @@ -102,7 +154,7 @@ const unsigned long long Patch::getBufferSize() const { } template -Patch::Patch(const std::vector& dimensions, const pathology::ColorType& colorType, T* data, bool ownData) : +Patch::Patch(const std::vector& dimensions, const pathology::ColorType& colorType, T* data, bool ownData, std::vector wsiMinValues, std::vector wsiMaxValues) : ImageSource(), _dimensions(dimensions), _buffer(data), @@ -128,6 +180,8 @@ Patch::Patch(const std::vector& dimensions, const patholo _colorType = pathology::Indexed; } } + this->_wsiMaxValues = wsiMaxValues; + this->_wsiMinValues = wsiMinValues; calculateStrides(); _isValid = true; } @@ -139,7 +193,9 @@ Patch::Patch(const Patch& rhs) : _dimensions(rhs._dimensions), _ownData(true), _strides(rhs._strides), - _buffer(NULL) + _buffer(NULL), + _wsiMinValues(rhs._wsiMinValues), + _wsiMaxValues(rhs._wsiMaxValues) { _buffer = new T[_bufferSize]; std::copy(rhs._buffer, rhs._buffer + rhs._bufferSize, _buffer); @@ -153,6 +209,8 @@ void Patch::swap(Patch& first, Patch& second) { std::swap(first._buffer, second._buffer); std::swap(first._bufferSize, second._bufferSize); std::swap(first._ownData, second._ownData); + std::swap(first._wsiMinValues, second._wsiMinValues); + std::swap(first._wsiMaxValues, second._wsiMaxValues); std::swap(first._strides, second._strides); } diff --git a/io/multiresolutionimageinterface/CMakeLists.txt b/io/multiresolutionimageinterface/CMakeLists.txt index 21943da9..bca17685 100644 --- a/io/multiresolutionimageinterface/CMakeLists.txt +++ b/io/multiresolutionimageinterface/CMakeLists.txt @@ -81,6 +81,7 @@ IF(BUILD_MULTIRESOLUTIONIMAGEINTERFACE_VSI_SUPPORT) FIND_PACKAGE(DCMTKJPEG REQUIRED) ADD_LIBRARY(multiresolutionimageinterface SHARED ${MULTIRESOLUTIONIMAGEINTERFACE_SRCS} ${MULTIRESOLUTIONIMAGEINTERFACE_HS} ${VSI_SOURCE_HS} ${VSI_SOURCE_SRCS}) TARGET_LINK_LIBRARIES(multiresolutionimageinterface PRIVATE ${DCMTKJPEG_LIBRARY} libtiff core libjasper libjpeg zlib pugixml Boost::disable_autolinking Boost::thread) +TARGET_COMPILE_DEFINITIONS(multiresolutionimageinterface PRIVATE HAS_MULTIRESOLUTIONIMAGEINTERFACE_VSI_SUPPORT) ELSE(BUILD_MULTIRESOLUTIONIMAGEINTERFACE_VSI_SUPPORT) ADD_LIBRARY(multiresolutionimageinterface SHARED ${MULTIRESOLUTIONIMAGEINTERFACE_SRCS} ${MULTIRESOLUTIONIMAGEINTERFACE_HS}) TARGET_LINK_LIBRARIES(multiresolutionimageinterface PRIVATE libtiff core libjasper libjpeg zlib pugixml Boost::disable_autolinking Boost::thread) diff --git a/io/multiresolutionimageinterface/MultiResolutionImage.h b/io/multiresolutionimageinterface/MultiResolutionImage.h index ed51b2ed..4fe4531d 100644 --- a/io/multiresolutionimageinterface/MultiResolutionImage.h +++ b/io/multiresolutionimageinterface/MultiResolutionImage.h @@ -75,7 +75,12 @@ public : for (unsigned int i = 0; i < _spacing.size(); ++i) { patchSpacing[i] = _spacing[i] * levelDownsample; } - Patch patch = Patch(dims, this->getColorType(), data, true); + std::vector minValues, maxValues; + for (unsigned int i = 0; i < this->getSamplesPerPixel(); ++i) { + minValues.push_back(this->getMinValue(i)); + maxValues.push_back(this->getMaxValue(i)); + } + Patch patch = Patch(dims, this->getColorType(), data, true, minValues, maxValues); patch.setSpacing(patchSpacing); return patch; } diff --git a/io/multiresolutionimageinterface/MultiResolutionImageFactory.cpp b/io/multiresolutionimageinterface/MultiResolutionImageFactory.cpp index 94c75943..93c79643 100644 --- a/io/multiresolutionimageinterface/MultiResolutionImageFactory.cpp +++ b/io/multiresolutionimageinterface/MultiResolutionImageFactory.cpp @@ -1,9 +1,12 @@ #include "MultiResolutionImageFactory.h" #include "MultiResolutionImage.h" -#include "VSIImage.h" #include "core/filetools.h" #include "core/stringconversion.h" +#ifdef HAS_MULTIRESOLUTIONIMAGEINTERFACE_VSI_SUPPORT +#include "VSIImage.h" +#endif + #ifdef _WIN32 #include #else @@ -30,7 +33,12 @@ MultiResolutionImage* MultiResolutionImageFactory::openImage(const std::string& if (std::find(factoryExtensions.begin(), factoryExtensions.end(), extension) != factoryExtensions.end()) { MultiResolutionImage* img = it->second->readImage(fileName); if (img) { + +#ifdef HAS_MULTIRESOLUTIONIMAGEINTERFACE_VSI_SUPPORT if ((img->getNumberOfLevels() > 1 || dynamic_cast(img) != NULL) || (img->getNumberOfLevels() == 1 && img->getLevelDimensions(0)[0] < 4096)) { +#else + if ((img->getNumberOfLevels() > 1) || (img->getNumberOfLevels() == 1 && img->getLevelDimensions(0)[0] < 4096)) { +#endif return img; } else { diff --git a/io/multiresolutionimageinterface/TIFFImage.cpp b/io/multiresolutionimageinterface/TIFFImage.cpp index ed28e81f..bb526ed5 100644 --- a/io/multiresolutionimageinterface/TIFFImage.cpp +++ b/io/multiresolutionimageinterface/TIFFImage.cpp @@ -272,8 +272,8 @@ template T* TIFFImage::FillRequestedRegionFromTIFF(const long long& std::fill(temp,temp+width*height*nrSamples,static_cast(0)); unsigned int tileW=_tileSizesPerLevel[level][0], tileH=_tileSizesPerLevel[level][1], levelH=_levelDimensions[level][1], levelW=_levelDimensions[level][0]; - long long levelStartX = startX / getLevelDownsample(level); - long long levelStartY = startY / getLevelDownsample(level); + long long levelStartX = std::floor(startX / getLevelDownsample(level) + 0.5); + long long levelStartY = std::floor(startY / getLevelDownsample(level) + 0.5); long long startTileY = levelStartY-(levelStartY-((levelStartY/tileH)*tileH)); long long startTileX = levelStartX-(levelStartX-((levelStartX/tileW)*tileW)); long long finalX = levelStartX + width >= levelW ? levelW : levelStartX + width; @@ -316,6 +316,23 @@ template T* TIFFImage::FillRequestedRegionFromTIFF(const long long& TIFFGetField(_tiff, TIFFTAG_PHOTOMETRIC, &ycbcr); if (ycbcr == PHOTOMETRIC_YCBCR) { TIFFReadRGBATile(_tiff, ix, iy, (uint32*)tile); + unsigned char* tmp_row = new unsigned char[tileW*nrSamples]; + for (unsigned int rev_y = 0; rev_y < tileH / 2; ++rev_y) { + std::copy(tile + rev_y*tileW*nrSamples, tile + (rev_y + 1)*tileW*nrSamples, tmp_row); + std::copy(tile + (tileH*tileW*nrSamples) - (rev_y + 1)*tileW*nrSamples, tile + (tileH*tileW*nrSamples) - rev_y*tileW*nrSamples, tile + rev_y*tileW*nrSamples); + std::copy(tmp_row, tmp_row + tileW*nrSamples, tile + (tileH*tileW*nrSamples) - (rev_y + 1)*tileW*nrSamples); + } + delete[] tmp_row; + for (unsigned int pos = 0; pos < tileW*tileH*nrSamples; pos += 4) { + unsigned char r = tile[pos + 0]; + unsigned char g = tile[pos + 1]; + unsigned char b = tile[pos + 2]; + unsigned char a = tile[pos + 3]; + tile[pos + 0] = b; + tile[pos + 1] = g; + tile[pos + 2] = r; + tile[pos + 3] = a; + } } else { TIFFReadTile(_tiff, tile, ix, iy, 0, 0);