Mercurial > hg > orthanc-wsi
changeset 372:33dcd7f68df9
OrthancWSIDicomizer detects imaged volume size for Aperio files without OpenSlide
author | Sebastien Jodogne <s.jodogne@gmail.com> |
---|---|
date | Mon, 17 Mar 2025 15:19:50 +0100 (2 months ago) |
parents | 9ff5782c6a8b |
children | a818de088503 |
files | Applications/Dicomizer.cpp Framework/ImageToolbox.cpp Framework/ImageToolbox.h Framework/Inputs/DicomPyramid.cpp Framework/Inputs/HierarchicalTiff.cpp Framework/Inputs/HierarchicalTiff.h NEWS ViewerPlugin/Plugin.cpp |
diffstat | 8 files changed, 115 insertions(+), 17 deletions(-) [+] |
line wrap: on
line diff
--- a/Applications/Dicomizer.cpp Mon Mar 17 14:16:33 2025 +0100 +++ b/Applications/Dicomizer.cpp Mon Mar 17 15:19:50 2025 +0100 @@ -1130,6 +1130,24 @@ { std::unique_ptr<OrthancWSI::HierarchicalTiff> tiff(new OrthancWSI::HierarchicalTiff(path)); sourceCompression = tiff->GetImageCompression(); + + // New in WSI 3.1 + double width, height; + if (tiff->LookupImagedVolumeSize(width, height)) + { + if (!volume.HasWidth()) + { + volume.SetWidth(width); + LOG(WARNING) << "Width of the imaged volume according to TIFF metadata: " << width << "mm"; + } + + if (!volume.HasHeight()) + { + volume.SetHeight(height); + LOG(WARNING) << "Height of the imaged volume according to TIFF metadata: " << height << "mm"; + } + } + return tiff.release(); } catch (Orthanc::OrthancException&) @@ -1237,7 +1255,7 @@ { float pixelSpacingX = volume.GetWidth() / static_cast<float>(source->GetLevelHeight(0)); float pixelSpacingY = volume.GetHeight() / static_cast<float>(source->GetLevelWidth(0)); - if (std::abs(pixelSpacingX - pixelSpacingY) >= 100.0f * std::numeric_limits<float>::epsilon()) + if (!OrthancWSI::ImageToolbox::IsNear(pixelSpacingX, pixelSpacingY)) { LOG(WARNING) << "Your pixel spacing is different along the X and Y axes, make sure that " << "you have not inversed the --" << OPTION_IMAGED_WIDTH << " and the --"
--- a/Framework/ImageToolbox.cpp Mon Mar 17 14:16:33 2025 +0100 +++ b/Framework/ImageToolbox.cpp Mon Mar 17 15:19:50 2025 +0100 @@ -36,6 +36,7 @@ #include <Images/JpegWriter.h> #include <Logging.h> +#include <limits> #include <string.h> #include <memory> @@ -44,6 +45,13 @@ { namespace ImageToolbox { + bool IsNear(double a, + double b) + { + return std::abs(a - b) < 10.0 * std::numeric_limits<float>::epsilon(); + } + + Orthanc::ImageAccessor* Allocate(Orthanc::PixelFormat format, unsigned int width, unsigned int height)
--- a/Framework/ImageToolbox.h Mon Mar 17 14:16:33 2025 +0100 +++ b/Framework/ImageToolbox.h Mon Mar 17 15:19:50 2025 +0100 @@ -33,6 +33,9 @@ { namespace ImageToolbox { + bool IsNear(double a, + double b); + Orthanc::ImageAccessor* Allocate(Orthanc::PixelFormat format, unsigned int width, unsigned int height);
--- a/Framework/Inputs/DicomPyramid.cpp Mon Mar 17 14:16:33 2025 +0100 +++ b/Framework/Inputs/DicomPyramid.cpp Mon Mar 17 15:19:50 2025 +0100 @@ -24,6 +24,7 @@ #include "../PrecompiledHeadersWSI.h" #include "DicomPyramid.h" +#include "../ImageToolbox.h" #include "../DicomToolbox.h" #include <Compatibility.h> @@ -290,8 +291,8 @@ width = instances_[i]->GetImagedVolumeWidth(); height = instances_[i]->GetImagedVolumeHeight(); } - else if (std::abs(width - instances_[i]->GetImagedVolumeWidth()) > 100.0 * std::numeric_limits<double>::epsilon() || - std::abs(height - instances_[i]->GetImagedVolumeHeight()) > 100.0 * std::numeric_limits<double>::epsilon()) + else if (!ImageToolbox::IsNear(width, instances_[i]->GetImagedVolumeWidth()) || + !ImageToolbox::IsNear(height, instances_[i]->GetImagedVolumeHeight())) { LOG(WARNING) << "Inconsistency of imaged volume width/height in series: " << seriesId_; return false;
--- a/Framework/Inputs/HierarchicalTiff.cpp Mon Mar 17 14:16:33 2025 +0100 +++ b/Framework/Inputs/HierarchicalTiff.cpp Mon Mar 17 15:19:50 2025 +0100 @@ -24,8 +24,12 @@ #include "../PrecompiledHeadersWSI.h" #include "HierarchicalTiff.h" +#include "../ImageToolbox.h" + #include <Logging.h> #include <OrthancException.h> +#include <SerializationToolbox.h> +#include <Toolbox.h> #include <iostream> #include <algorithm> @@ -69,6 +73,17 @@ headers_.assign(reinterpret_cast<const char*>(tables), size); } } + + // Read the image description, if any + const char* description = NULL; + if (TIFFGetField(tiff, TIFFTAG_IMAGEDESCRIPTION, &description)) + { + description_.assign(description); + } + else + { + description_.clear(); + } } struct HierarchicalTiff::Comparator @@ -261,4 +276,58 @@ return true; } + + + bool HierarchicalTiff::LookupImagedVolumeSize(double& width, + double& height) const + { + static const char* const APERIO_DESCRIPTION = "Aperio "; + static const char* const MPP = "MPP"; + + bool found = false; + + for (size_t i = 0; i < levels_.size(); i++) + { + if (Orthanc::Toolbox::StartsWith(levels_[i].description_, APERIO_DESCRIPTION)) + { + std::vector<std::string> tokens; + Orthanc::Toolbox::TokenizeString(tokens, levels_[i].description_, '|'); + + for (size_t j = 0; j < tokens.size(); j++) + { + std::vector<std::string> assignment; + Orthanc::Toolbox::TokenizeString(assignment, tokens[j], '='); + if (assignment.size() == 2) + { + const std::string key = Orthanc::Toolbox::StripSpaces(assignment[0]); + const std::string value = Orthanc::Toolbox::StripSpaces(assignment[1]); + + double mpp; + if (key == MPP && + Orthanc::SerializationToolbox::ParseDouble(mpp, value)) + { + // In the lines below, remember to switch X/Y when going from physical to pixel coordinates! + double thisHeight = static_cast<double>(levels_[i].width_) * mpp / 1000.0; + double thisWidth = static_cast<double>(levels_[i].height_) * mpp / 1000.0; + + if (!found) + { + found = true; + width = thisWidth; + height = thisHeight; + } + else if (!ImageToolbox::IsNear(thisWidth, width) || + !ImageToolbox::IsNear(thisHeight, height)) + { + LOG(WARNING) << "Inconsistency in the Aperio metadata regarding the size of the imaged volume"; + return false; + } + } + } + } + } + } + + return found; + } }
--- a/Framework/Inputs/HierarchicalTiff.h Mon Mar 17 14:16:33 2025 +0100 +++ b/Framework/Inputs/HierarchicalTiff.h Mon Mar 17 15:19:50 2025 +0100 @@ -40,6 +40,7 @@ unsigned int width_; unsigned int height_; std::string headers_; + std::string description_; Level(TIFF* tiff, tdir_t directory, @@ -102,5 +103,8 @@ { return compression_; } + + bool LookupImagedVolumeSize(double& width, + double& height) const; }; }
--- a/NEWS Mon Mar 17 14:16:33 2025 +0100 +++ b/NEWS Mon Mar 17 15:19:50 2025 +0100 @@ -2,10 +2,11 @@ =============================== * Upgraded to OpenLayers 10.4.0 (was previously 3.19.0) -* The viewer now displays the scale if the imaged volume size is available +* The viewer now displays the scale if imaged volume size is available in DICOM +* Fix handling of "Image Type" in the viewer for compatibility with other vendors * OrthancWSIDicomizer does not fill anymore the imaged volume width/height tags if no information is available -* Fix handling of "Image Type" in the viewer for compatibility with other vendors +* OrthancWSIDicomizer detects imaged volume size for Aperio files without OpenSlide Compatibility notes about the viewer ------------------------------------
--- a/ViewerPlugin/Plugin.cpp Mon Mar 17 14:16:33 2025 +0100 +++ b/ViewerPlugin/Plugin.cpp Mon Mar 17 15:19:50 2025 +0100 @@ -416,12 +416,6 @@ } -static bool IsNear(float a, float b) -{ - return std::abs(a - b) < 100.0f * std::numeric_limits<float>::epsilon(); -} - - extern "C" { ORTHANC_PLUGINS_API int32_t OrthancPluginInitialize(OrthancPluginContext* context) @@ -478,17 +472,17 @@ OrthancWSI::LABColor lab; if (!OrthancWSI::LABColor::DecodeDicomRecommendedAbsentPixelCIELab(lab, "65535\\0\\0") || - !IsNear(lab.GetL(), 100.0f) || - !IsNear(lab.GetA(), -128.0f) || - !IsNear(lab.GetB(), -128.0f)) + !OrthancWSI::ImageToolbox::IsNear(lab.GetL(), 100.0f) || + !OrthancWSI::ImageToolbox::IsNear(lab.GetA(), -128.0f) || + !OrthancWSI::ImageToolbox::IsNear(lab.GetB(), -128.0f)) { throw Orthanc::OrthancException(Orthanc::ErrorCode_InternalError); } if (!OrthancWSI::LABColor::DecodeDicomRecommendedAbsentPixelCIELab(lab, "0\\32896\\65535") || - !IsNear(lab.GetL(), 0.0f) || - !IsNear(lab.GetA(), 0.0f) || - !IsNear(lab.GetB(), 127.0f)) + !OrthancWSI::ImageToolbox::IsNear(lab.GetL(), 0.0f) || + !OrthancWSI::ImageToolbox::IsNear(lab.GetA(), 0.0f) || + !OrthancWSI::ImageToolbox::IsNear(lab.GetB(), 127.0f)) { throw Orthanc::OrthancException(Orthanc::ErrorCode_InternalError); }