Mercurial > hg > orthanc-wsi
changeset 382:50cb2a69655f
added support for JPEG-LS transfer syntax in the Web viewer plugin
author | Sebastien Jodogne <s.jodogne@gmail.com> |
---|---|
date | Mon, 07 Apr 2025 18:08:06 +0200 (6 weeks ago) |
parents | 6ff4f1bd0861 |
children | 63936f094eaa |
files | Framework/Enumerations.h Framework/ImageToolbox.cpp Framework/ImageToolbox.h Framework/Inputs/DicomPyramidInstance.cpp Framework/Inputs/DicomPyramidLevel.cpp NEWS ViewerPlugin/RawTile.cpp |
diffstat | 7 files changed, 104 insertions(+), 7 deletions(-) [+] |
line wrap: on
line diff
--- a/Framework/Enumerations.h Mon Apr 07 17:02:47 2025 +0200 +++ b/Framework/Enumerations.h Mon Apr 07 18:08:06 2025 +0200 @@ -42,7 +42,8 @@ ImageCompression_Png = 4, ImageCompression_Jpeg = 5, ImageCompression_Jpeg2000 = 6, - ImageCompression_Tiff = 7 + ImageCompression_Tiff = 7, + ImageCompression_UseOrthancPreview = 8 }; enum OpticalPath
--- a/Framework/ImageToolbox.cpp Mon Apr 07 17:02:47 2025 +0200 +++ b/Framework/ImageToolbox.cpp Mon Apr 07 18:08:06 2025 +0200 @@ -356,5 +356,54 @@ throw Orthanc::OrthancException(Orthanc::ErrorCode_ParameterOutOfRange); } } + + + bool HasPngSignature(const std::string& buffer) + { + if (buffer.size() < 8) + { + return false; + } + else + { + // https://en.wikipedia.org/wiki/PNG#File_header + // https://en.wikipedia.org/wiki/List_of_file_signatures + const uint8_t* p = reinterpret_cast<const uint8_t*>(buffer.data()); + return (p[0] == 0x89 && + p[1] == 0x50 && + p[2] == 0x4e && + p[3] == 0x47 && + p[4] == 0x0d && + p[5] == 0x0a && + p[6] == 0x1a && + p[7] == 0x0a); + } + } + + + bool HasJpegSignature(const std::string& buffer) + { + if (buffer.size() < 18) + { + return false; + } + else + { + // https://en.wikipedia.org/wiki/List_of_file_signatures + const uint8_t* p = reinterpret_cast<const uint8_t*>(buffer.data()); + if (p[0] != 0xff || + p[1] != 0xd8 || + p[2] != 0xff) + { + return false; + } + + // This is only a rough guess! + return (p[3] == 0xdb || + p[3] == 0xe0 || + p[3] == 0xee || + p[3] == 0xe1); + } + } } }
--- a/Framework/ImageToolbox.h Mon Apr 07 17:02:47 2025 +0200 +++ b/Framework/ImageToolbox.h Mon Apr 07 18:08:06 2025 +0200 @@ -77,5 +77,9 @@ void ConvertJpegYCbCrToRgb(Orthanc::ImageAccessor& image /* inplace */); ImageCompression Convert(Orthanc::MimeType type); + + bool HasPngSignature(const std::string& buffer); + + bool HasJpegSignature(const std::string& buffer); } }
--- a/Framework/Inputs/DicomPyramidInstance.cpp Mon Apr 07 17:02:47 2025 +0200 +++ b/Framework/Inputs/DicomPyramidInstance.cpp Mon Apr 07 18:08:06 2025 +0200 @@ -82,6 +82,10 @@ { return ImageCompression_Jpeg2000; } + else if (s == "1.2.840.10008.1.2.4.80") + { + return ImageCompression_UseOrthancPreview; + } else { LOG(ERROR) << "Unsupported transfer syntax: " << s;
--- a/Framework/Inputs/DicomPyramidLevel.cpp Mon Apr 07 17:02:47 2025 +0200 +++ b/Framework/Inputs/DicomPyramidLevel.cpp Mon Apr 07 18:08:06 2025 +0200 @@ -134,15 +134,39 @@ assert(tile.instance_ != NULL); DicomPyramidInstance& instance = *tile.instance_; - std::string uri = ("/instances/" + instance.GetInstanceId() + - "/frames/" + boost::lexical_cast<std::string>(tile.frame_) + "/raw"); - - orthanc.RestApiGet(raw, uri); - compression = instance.GetImageCompression(orthanc); format = instance.GetPixelFormat(); - return true; + if (compression == ImageCompression_UseOrthancPreview) + { + // If the WSI viewer plugin has no built-in support for this transfer syntax, + // use the decoding primitives offered by the Orthanc core + std::string uri = ("/instances/" + instance.GetInstanceId() + + "/frames/" + boost::lexical_cast<std::string>(tile.frame_) + "/preview"); + orthanc.RestApiGet(raw, uri); + + if (ImageToolbox::HasPngSignature(raw)) // In theory, Orthanc should always generate PNG by default + { + compression = ImageCompression_Png; + return true; + } + else if (ImageToolbox::HasJpegSignature(raw)) + { + compression = ImageCompression_Jpeg; + return true; + } + else + { + throw Orthanc::OrthancException(Orthanc::ErrorCode_InternalError, "Cannot decode a preview image generated by the Orthanc core"); + } + } + else + { + std::string uri = ("/instances/" + instance.GetInstanceId() + + "/frames/" + boost::lexical_cast<std::string>(tile.frame_) + "/raw"); + orthanc.RestApiGet(raw, uri); + return true; + } } else {
--- a/NEWS Mon Apr 07 17:02:47 2025 +0200 +++ b/NEWS Mon Apr 07 18:08:06 2025 +0200 @@ -3,6 +3,7 @@ * Support windowing when rendering grayscale images using on-the-fly deep zoom * Added tolerance to imaged volume width/height by looking only at the finest level +* Added support for JPEG-LS transfer syntax in the Web viewer plugin Version 3.1 (2025-03-17)
--- a/ViewerPlugin/RawTile.cpp Mon Apr 07 17:02:47 2025 +0200 +++ b/ViewerPlugin/RawTile.cpp Mon Apr 07 18:08:06 2025 +0200 @@ -31,6 +31,7 @@ #include <Compatibility.h> // For std::unique_ptr #include <Images/ImageProcessing.h> #include <Images/JpegReader.h> +#include <Images/PngReader.h> #include <Images/PngWriter.h> #include <MultiThreading/Semaphore.h> #include <OrthancException.h> @@ -78,6 +79,19 @@ return decoded.release(); } + case ImageCompression_Png: + { + /** + * This is used if the DICOM instance has a transfer syntax + * that is not natively supported by the WSI viewer plugin, in + * which case the tile comes from the "/preview" route of the + * REST API of Orthanc. + **/ + std::unique_ptr<Orthanc::PngReader> decoded(new Orthanc::PngReader); + decoded->ReadFromMemory(tile_); + return decoded.release(); + } + default: throw Orthanc::OrthancException(Orthanc::ErrorCode_NotImplemented); }