Mercurial > hg > orthanc-stone
diff Framework/Toolbox/OrthancSlicesLoader.cpp @ 93:5945e81734a3 wasm
decoding of JPEG images
author | Sebastien Jodogne <s.jodogne@gmail.com> |
---|---|
date | Mon, 29 May 2017 17:28:31 +0200 |
parents | f5f54ed8d307 |
children | 7b14c12a3be5 |
line wrap: on
line diff
--- a/Framework/Toolbox/OrthancSlicesLoader.cpp Mon May 29 11:42:53 2017 +0200 +++ b/Framework/Toolbox/OrthancSlicesLoader.cpp Mon May 29 17:28:31 2017 +0200 @@ -23,9 +23,13 @@ #include "MessagingToolbox.h" +#include "../../Resources/Orthanc/Core/Images/Image.h" +#include "../../Resources/Orthanc/Core/Images/ImageProcessing.h" +#include "../../Resources/Orthanc/Core/Images/JpegReader.h" #include "../../Resources/Orthanc/Core/Images/PngReader.h" #include "../../Resources/Orthanc/Core/Logging.h" #include "../../Resources/Orthanc/Core/OrthancException.h" +#include "../../Resources/Orthanc/Core/Toolbox.h" #include "../../Resources/Orthanc/Plugins/Samples/Common/DicomDatasetReader.h" #include "../../Resources/Orthanc/Plugins/Samples/Common/FullOrthancDataset.h" @@ -36,11 +40,12 @@ class OrthancSlicesLoader::Operation : public Orthanc::IDynamicObject { private: - Mode mode_; - unsigned int frame_; - unsigned int sliceIndex_; - const Slice* slice_; - std::string instanceId_; + Mode mode_; + unsigned int frame_; + unsigned int sliceIndex_; + const Slice* slice_; + std::string instanceId_; + SliceImageQuality quality_; Operation(Mode mode) : mode_(mode) @@ -53,6 +58,12 @@ return mode_; } + SliceImageQuality GetQuality() const + { + assert(mode_ == Mode_LoadImage); + return quality_; + } + unsigned int GetSliceIndex() const { assert(mode_ == Mode_LoadImage); @@ -92,11 +103,13 @@ } static Operation* DownloadSliceImage(unsigned int sliceIndex, - const Slice& slice) + const Slice& slice, + SliceImageQuality quality) { std::auto_ptr<Operation> tmp(new Operation(Mode_LoadImage)); tmp->sliceIndex_ = sliceIndex; tmp->slice_ = &slice; + tmp->quality_ = quality; return tmp.release(); } }; @@ -132,7 +145,22 @@ break; case Mode_LoadImage: - that_.ParseSliceImage(*operation, answer, answerSize); + switch (operation->GetQuality()) + { + case SliceImageQuality_Full: + that_.ParseSliceImagePng(*operation, answer, answerSize); + break; + + case SliceImageQuality_Jpeg50: + case SliceImageQuality_Jpeg90: + case SliceImageQuality_Jpeg95: + that_.ParseSliceImageJpeg(*operation, answer, answerSize); + break; + + default: + throw Orthanc::OrthancException(Orthanc::ErrorCode_InternalError); + } + break; default: @@ -155,7 +183,8 @@ case Mode_LoadImage: that_.userCallback_.NotifySliceImageError(that_, operation->GetSliceIndex(), - operation->GetSlice()); + operation->GetSlice(), + operation->GetQuality()); break; default: @@ -164,6 +193,29 @@ } }; + + + void OrthancSlicesLoader::NotifySliceImageSuccess(const Operation& operation, + Orthanc::ImageAccessor* image) const + { + if (image == NULL) + { + throw Orthanc::OrthancException(Orthanc::ErrorCode_NullPointer); + } + else + { + userCallback_.NotifySliceImageReady + (*this, operation.GetSliceIndex(), operation.GetSlice(), image, operation.GetQuality()); + } + } + + + void OrthancSlicesLoader::NotifySliceImageError(const Operation& operation) const + { + userCallback_.NotifySliceImageError + (*this, operation.GetSliceIndex(), operation.GetSlice(), operation.GetQuality()); + } + void OrthancSlicesLoader::ParseSeriesGeometry(const void* answer, size_t size) @@ -256,18 +308,32 @@ } - void OrthancSlicesLoader::ParseSliceImage(const Operation& operation, - const void* answer, - size_t size) + void OrthancSlicesLoader::ParseSliceImagePng(const Operation& operation, + const void* answer, + size_t size) { std::auto_ptr<Orthanc::PngReader> image(new Orthanc::PngReader); - image->ReadFromMemory(answer, size); - bool ok = (image->GetWidth() == operation.GetSlice().GetWidth() || - image->GetHeight() == operation.GetSlice().GetHeight()); + bool ok = false; + + try + { + image->ReadFromMemory(answer, size); + } + catch (Orthanc::OrthancException&) + { + NotifySliceImageError(operation); + return; + } + + if (image->GetWidth() != operation.GetSlice().GetWidth() || + image->GetHeight() != operation.GetSlice().GetHeight()) + { + NotifySliceImageError(operation); + return; + } - if (ok && - operation.GetSlice().GetConverter().GetExpectedPixelFormat() == + if (operation.GetSlice().GetConverter().GetExpectedPixelFormat() == Orthanc::PixelFormat_SignedGrayscale16) { if (image->GetFormat() == Orthanc::PixelFormat_Grayscale16) @@ -276,20 +342,149 @@ } else { - ok = false; + NotifySliceImageError(operation); + return; + } + } + + NotifySliceImageSuccess(operation, image.release()); + } + + + void OrthancSlicesLoader::ParseSliceImageJpeg(const Operation& operation, + const void* answer, + size_t size) + { + Json::Value encoded; + if (!MessagingToolbox::ParseJson(encoded, answer, size) || + encoded.type() != Json::objectValue || + !encoded.isMember("Orthanc") || + encoded["Orthanc"].type() != Json::objectValue) + { + NotifySliceImageError(operation); + return; + } + + Json::Value& info = encoded["Orthanc"]; + if (!info.isMember("PixelData") || + !info.isMember("Stretched") || + !info.isMember("Compression") || + info["Compression"].type() != Json::stringValue || + info["PixelData"].type() != Json::stringValue || + info["Stretched"].type() != Json::booleanValue || + info["Compression"].asString() != "Jpeg") + { + NotifySliceImageError(operation); + return; + } + + bool isSigned = false; + bool isStretched = info["Stretched"].asBool(); + + if (info.isMember("IsSigned")) + { + if (info["IsSigned"].type() != Json::booleanValue) + { + throw Orthanc::OrthancException(Orthanc::ErrorCode_NetworkProtocol); + } + else + { + isSigned = info["IsSigned"].asBool(); } } - if (ok) + std::string jpeg; + Orthanc::Toolbox::DecodeBase64(jpeg, info["PixelData"].asString()); + + std::auto_ptr<Orthanc::JpegReader> reader(new Orthanc::JpegReader); + try + { + reader->ReadFromMemory(jpeg); + } + catch (Orthanc::OrthancException&) + { + NotifySliceImageError(operation); + return; + } + + Orthanc::PixelFormat expectedFormat = + operation.GetSlice().GetConverter().GetExpectedPixelFormat(); + + if (reader->GetFormat() == Orthanc::PixelFormat_RGB24) // This is a color image { - userCallback_.NotifySliceImageReady(*this, operation.GetSliceIndex(), - operation.GetSlice(), image.release()); + if (expectedFormat != Orthanc::PixelFormat_RGB24) + { + NotifySliceImageError(operation); + return; + } + + if (isSigned || isStretched) + { + NotifySliceImageError(operation); + return; + } + else + { + NotifySliceImageSuccess(operation, reader.release()); + return; + } + } + + if (reader->GetFormat() != Orthanc::PixelFormat_Grayscale8) + { + NotifySliceImageError(operation); + return; } - else + + if (!isStretched) + { + if (expectedFormat != reader->GetFormat()) + { + NotifySliceImageError(operation); + return; + } + else + { + NotifySliceImageSuccess(operation, reader.release()); + return; + } + } + + int32_t stretchLow = 0; + int32_t stretchHigh = 0; + + if (!info.isMember("StretchLow") || + !info.isMember("StretchHigh") || + info["StretchLow"].type() != Json::intValue || + info["StretchHigh"].type() != Json::intValue) { - userCallback_.NotifySliceImageError(*this, operation.GetSliceIndex(), - operation.GetSlice()); + NotifySliceImageError(operation); + return; } + + stretchLow = info["StretchLow"].asInt(); + stretchHigh = info["StretchHigh"].asInt(); + + if (stretchLow < -32768 || + stretchHigh > 65535 || + (stretchLow < 0 && stretchHigh > 32767)) + { + // This range cannot be represented with a uint16_t or an int16_t + NotifySliceImageError(operation); + return; + } + + // Decode a grayscale JPEG 8bpp image coming from the Web viewer + std::auto_ptr<Orthanc::ImageAccessor> image + (new Orthanc::Image(expectedFormat, reader->GetWidth(), reader->GetHeight(), false)); + + float scaling = static_cast<float>(stretchHigh - stretchLow) / 255.0f; + float offset = static_cast<float>(stretchLow) / scaling; + + Orthanc::ImageProcessing::Convert(*image, *reader); + Orthanc::ImageProcessing::ShiftScale(*image, offset, scaling); + + NotifySliceImageSuccess(operation, image.release()); } @@ -375,38 +570,86 @@ } - void OrthancSlicesLoader::ScheduleLoadSliceImage(size_t index) + void OrthancSlicesLoader::ScheduleSliceImagePng(size_t index) + { + const Slice& slice = GetSlice(index); + + std::string uri = ("/instances/" + slice.GetOrthancInstanceId() + "/frames/" + + boost::lexical_cast<std::string>(slice.GetFrame())); + + switch (slice.GetConverter().GetExpectedPixelFormat()) + { + case Orthanc::PixelFormat_RGB24: + uri += "/preview"; + break; + + case Orthanc::PixelFormat_Grayscale16: + uri += "/image-uint16"; + break; + + case Orthanc::PixelFormat_SignedGrayscale16: + uri += "/image-int16"; + break; + + default: + throw Orthanc::OrthancException(Orthanc::ErrorCode_ParameterOutOfRange); + } + + orthanc_.ScheduleGetRequest(*webCallback_, uri, + Operation::DownloadSliceImage(index, slice, SliceImageQuality_Full)); + } + + + void OrthancSlicesLoader::ScheduleSliceImageJpeg(size_t index, + SliceImageQuality quality) + { + unsigned int value; + + switch (quality) + { + case SliceImageQuality_Jpeg50: + value = 50; + break; + + case SliceImageQuality_Jpeg90: + value = 90; + break; + + case SliceImageQuality_Jpeg95: + value = 95; + break; + + default: + throw Orthanc::OrthancException(Orthanc::ErrorCode_ParameterOutOfRange); + } + + // This requires the official Web viewer plugin to be installed! + const Slice& slice = GetSlice(index); + std::string uri = ("web-viewer/instances/jpeg" + + boost::lexical_cast<std::string>(value) + + "-" + slice.GetOrthancInstanceId() + "_" + + boost::lexical_cast<std::string>(slice.GetFrame())); + + orthanc_.ScheduleGetRequest(*webCallback_, uri, + Operation::DownloadSliceImage(index, slice, quality)); + } + + + void OrthancSlicesLoader::ScheduleLoadSliceImage(size_t index, + SliceImageQuality quality) { if (state_ != State_GeometryReady) { throw Orthanc::OrthancException(Orthanc::ErrorCode_BadSequenceOfCalls); } + + if (quality == SliceImageQuality_Full) + { + ScheduleSliceImagePng(index); + } else { - const Slice& slice = GetSlice(index); - - std::string uri = ("/instances/" + slice.GetOrthancInstanceId() + "/frames/" + - boost::lexical_cast<std::string>(slice.GetFrame())); - - switch (slice.GetConverter().GetExpectedPixelFormat()) - { - case Orthanc::PixelFormat_RGB24: - uri += "/preview"; - break; - - case Orthanc::PixelFormat_Grayscale16: - uri += "/image-uint16"; - break; - - case Orthanc::PixelFormat_SignedGrayscale16: - uri += "/image-int16"; - break; - - default: - throw Orthanc::OrthancException(Orthanc::ErrorCode_ParameterOutOfRange); - } - - orthanc_.ScheduleGetRequest(*webCallback_, uri, Operation::DownloadSliceImage(index, slice)); + ScheduleSliceImageJpeg(index, quality); } } }