Script 'mail_helper' called by obssrc Hello community, here is the log from the commit of package orthanc-dicomweb for openSUSE:Factory checked in at 2021-09-02 23:20:26 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ Comparing /work/SRC/openSUSE:Factory/orthanc-dicomweb (Old) and /work/SRC/openSUSE:Factory/.orthanc-dicomweb.new.1899 (New) ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Package is "orthanc-dicomweb" Thu Sep 2 23:20:26 2021 rev:8 rq:915796 version:1.7 Changes: -------- --- /work/SRC/openSUSE:Factory/orthanc-dicomweb/orthanc-dicomweb.changes 2021-05-11 23:04:03.284986592 +0200 +++ /work/SRC/openSUSE:Factory/.orthanc-dicomweb.new.1899/orthanc-dicomweb.changes 2021-09-02 23:20:50.408592779 +0200 @@ -1,0 +2,6 @@ +Wed Sep 1 07:40:10 UTC 2021 - Axel Braun <axel.br...@gmx.de> + +- Version 1.7 +* Detection of windowing and rescale in ".../rendered" for Philips multiframe images + +------------------------------------------------------------------- Old: ---- OrthancDicomWeb-1.6.tar.gz New: ---- OrthancDicomWeb-1.7.tar.gz ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ Other differences: ------------------ ++++++ orthanc-dicomweb.spec ++++++ --- /var/tmp/diff_new_pack.pZCOBl/_old 2021-09-02 23:20:51.088593626 +0200 +++ /var/tmp/diff_new_pack.pZCOBl/_new 2021-09-02 23:20:51.092593631 +0200 @@ -21,7 +21,7 @@ Summary: WebViewer plugin for Orthanc License: AGPL-3.0-or-later Group: Productivity/Graphics/Viewers -Version: 1.6 +Version: 1.7 Release: 0 URL: https://orthanc-server.com Source0: https://www.orthanc-server.com/downloads/get.php?path=/plugin-dicom-web/OrthancDicomWeb-%{version}.tar.gz ++++++ OrthancDicomWeb-1.6.tar.gz -> OrthancDicomWeb-1.7.tar.gz ++++++ diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/OrthancDicomWeb-1.6/.hg_archival.txt new/OrthancDicomWeb-1.7/.hg_archival.txt --- old/OrthancDicomWeb-1.6/.hg_archival.txt 2021-05-07 13:31:18.000000000 +0200 +++ new/OrthancDicomWeb-1.7/.hg_archival.txt 2021-08-31 15:32:10.000000000 +0200 @@ -1,6 +1,6 @@ repo: d5f45924411123cfd02d035fd50b8e37536eadef -node: 01bae1ba951d3c66e55aba6129bcba0ef825d081 -branch: OrthancDicomWeb-1.6 +node: f5b64f680bfbea760169e72602498b946c58c234 +branch: OrthancDicomWeb-1.7 latesttag: null -latesttagdistance: 462 -changessincelatesttag: 491 +latesttagdistance: 469 +changessincelatesttag: 498 diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/OrthancDicomWeb-1.6/CMakeLists.txt new/OrthancDicomWeb-1.7/CMakeLists.txt --- old/OrthancDicomWeb-1.6/CMakeLists.txt 2021-05-07 13:31:18.000000000 +0200 +++ new/OrthancDicomWeb-1.7/CMakeLists.txt 2021-08-31 15:32:10.000000000 +0200 @@ -21,13 +21,13 @@ project(OrthancDicomWeb) -set(ORTHANC_DICOM_WEB_VERSION "1.6") +set(ORTHANC_DICOM_WEB_VERSION "1.7") if (ORTHANC_DICOM_WEB_VERSION STREQUAL "mainline") set(ORTHANC_FRAMEWORK_DEFAULT_VERSION "mainline") set(ORTHANC_FRAMEWORK_DEFAULT_SOURCE "hg") else() - set(ORTHANC_FRAMEWORK_DEFAULT_VERSION "1.9.3") + set(ORTHANC_FRAMEWORK_DEFAULT_VERSION "1.9.7") set(ORTHANC_FRAMEWORK_DEFAULT_SOURCE "web") endif() @@ -57,11 +57,20 @@ # Download and setup the Orthanc framework include(${CMAKE_SOURCE_DIR}/Resources/Orthanc/CMake/DownloadOrthancFramework.cmake) -include_directories(${ORTHANC_FRAMEWORK_ROOT}) - if (ORTHANC_FRAMEWORK_SOURCE STREQUAL "system") - link_libraries(${ORTHANC_FRAMEWORK_LIBRARIES}) + if (ORTHANC_FRAMEWORK_USE_SHARED) + include(FindBoost) + find_package(Boost COMPONENTS regex thread) + + if (NOT Boost_FOUND) + message(FATAL_ERROR "Unable to locate Boost on this system") + endif() + + link_libraries(${Boost_LIBRARIES} jsoncpp) + endif() + link_libraries(${ORTHANC_FRAMEWORK_LIBRARIES}) + set(USE_SYSTEM_GOOGLE_TEST ON CACHE BOOL "Use the system version of Google Test") set(USE_GOOGLE_TEST_DEBIAN_PACKAGE OFF CACHE BOOL "Use the sources of Google Test shipped with libgtest-dev (Debian only)") mark_as_advanced(USE_GOOGLE_TEST_DEBIAN_PACKAGE) @@ -77,6 +86,7 @@ set(USE_BOOST_ICONV ON) include(${ORTHANC_FRAMEWORK_ROOT}/../Resources/CMake/OrthancFrameworkConfiguration.cmake) + include_directories(${ORTHANC_FRAMEWORK_ROOT}) endif() diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/OrthancDicomWeb-1.6/NEWS new/OrthancDicomWeb-1.7/NEWS --- old/OrthancDicomWeb-1.6/NEWS 2021-05-07 13:31:18.000000000 +0200 +++ new/OrthancDicomWeb-1.7/NEWS 2021-08-31 15:32:10.000000000 +0200 @@ -2,6 +2,12 @@ =============================== +Version 1.7 (2021-08-31) +======================== + +* Detection of windowing and rescale in ".../rendered" for Philips multiframe images + + Version 1.6 (2021-05-07) ======================== diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/OrthancDicomWeb-1.6/Plugin/WadoRsRetrieveRendered.cpp new/OrthancDicomWeb-1.7/Plugin/WadoRsRetrieveRendered.cpp --- old/OrthancDicomWeb-1.6/Plugin/WadoRsRetrieveRendered.cpp 2021-05-07 13:31:18.000000000 +0200 +++ new/OrthancDicomWeb-1.7/Plugin/WadoRsRetrieveRendered.cpp 2021-08-31 15:32:10.000000000 +0200 @@ -28,10 +28,68 @@ #include <Logging.h> #include <Toolbox.h> +#if ORTHANC_FRAMEWORK_VERSION_IS_ABOVE(1, 9, 7) +# include <SerializationToolbox.h> +#else +# include <boost/lexical_cast.hpp> +#endif + #include <boost/algorithm/string/predicate.hpp> #include <boost/math/special_functions/round.hpp> +static bool ParseFloat(float& target, + const std::string& source) +{ +#if ORTHANC_FRAMEWORK_VERSION_IS_ABOVE(1, 9, 7) + return Orthanc::SerializationToolbox::ParseFloat(target, source); + +#else + // Emulation for older versions of the Orthanc framework + std::string s = Orthanc::Toolbox::StripSpaces(source); + + if (s.empty()) + { + return false; + } + else + { + try + { + target = boost::lexical_cast<float>(s); + return true; + } + catch (boost::bad_lexical_cast&) + { + return false; + } + } +#endif +} + + +static bool ParseFirstFloat(float& target, + const std::string& source) +{ +#if ORTHANC_FRAMEWORK_VERSION_IS_ABOVE(1, 9, 7) + return Orthanc::SerializationToolbox::ParseFirstFloat(target, source); + +#else + // Emulation for older versions of the Orthanc framework + std::vector<std::string> tokens; + Orthanc::Toolbox::TokenizeString(tokens, source, '\\'); + if (tokens.empty()) + { + return false; + } + else + { + return ParseFloat(target, tokens[0]); + } +#endif +} + + namespace { enum WindowingMode @@ -271,11 +329,6 @@ } - bool HasCustomization() const - { - return (hasViewport_ || hasQuality_ || hasWindowing_); - } - unsigned int GetTargetWidth(unsigned int sourceWidth) const { if (hasVW_) @@ -347,11 +400,19 @@ return quality_; } - bool IsWindowing() const + bool HasWindowing() const { return hasWindowing_; } + void SetWindow(float center, + float width) + { + hasWindowing_ = true; + windowCenter_ = center; + windowWidth_ = width; + } + float GetWindowCenter() const { return windowCenter_; @@ -704,15 +765,68 @@ } +static bool ReadRescale(RenderingParameters& parameters, + const Json::Value& tags) +{ + static const char* const RESCALE_INTERCEPT = "0028,1052"; + static const char* const RESCALE_SLOPE = "0028,1053"; + + if (tags.type() == Json::objectValue && + tags.isMember(RESCALE_SLOPE) && + tags.isMember(RESCALE_INTERCEPT) && + tags[RESCALE_SLOPE].type() == Json::stringValue && + tags[RESCALE_INTERCEPT].type() == Json::stringValue) + { + float s, i; + + if (ParseFloat(s, tags[RESCALE_SLOPE].asString()) && + ParseFloat(i, tags[RESCALE_INTERCEPT].asString())) + { + parameters.SetRescaleSlope(s); + parameters.SetRescaleIntercept(i); + return true; + } + } + + return false; +} + + +static bool ReadDefaultWindow(RenderingParameters& parameters, + const Json::Value& tags) +{ + static const char* const WINDOW_CENTER = "0028,1050"; + static const char* const WINDOW_WIDTH = "0028,1051"; + + if (tags.type() == Json::objectValue && + tags.isMember(WINDOW_CENTER) && + tags.isMember(WINDOW_WIDTH) && + tags[WINDOW_CENTER].type() == Json::stringValue && + tags[WINDOW_WIDTH].type() == Json::stringValue) + { + float wc, ww; + + if (ParseFirstFloat(wc, tags[WINDOW_CENTER].asString()) && + ParseFirstFloat(ww, tags[WINDOW_WIDTH].asString())) + { + parameters.SetWindow(wc, ww); + return true; + } + } + + return false; +} + static void AnswerFrameRendered(OrthancPluginRestOutput* output, std::string instanceId, int frame, const OrthancPluginHttpRequest* request) { - static const char* const RESCALE_INTERCEPT = "0028,1052"; - static const char* const RESCALE_SLOPE = "0028,1053"; static const char* const PHOTOMETRIC_INTERPRETATION = "0028,0004"; + static const char* const PER_FRAME_FUNCTIONAL_GROUPS_SEQUENCE = "5200,9230"; + static const char* const PIXEL_VALUE_TRANSFORMATION_SEQUENCE = "0028,9145"; + static const char* const FRAME_VOI_LUT_SEQUENCE = "0028,9132"; Orthanc::MimeType mime = Orthanc::MimeType_Jpeg; // This is the default in DICOMweb @@ -744,142 +858,108 @@ RenderingParameters parameters(request); OrthancPlugins::MemoryBuffer buffer; - bool badFrameError = false; - - if (parameters.HasCustomization()) + if (frame <= 0) + { + throw Orthanc::OrthancException(Orthanc::ErrorCode_ParameterOutOfRange, + "Inexistent frame index in this image: " + boost::lexical_cast<std::string>(frame)); + } + else { - if (frame <= 0) + buffer.GetDicomInstance(instanceId); + + Json::Value tags; + buffer.DicomToJson(tags, OrthancPluginDicomToJsonFormat_Short, OrthancPluginDicomToJsonFlags_None, 255); + + const unsigned int f = static_cast<unsigned int>(frame - 1); + + if (ReadRescale(parameters, tags)) { - badFrameError = true; } - else + else if (tags.isMember(PER_FRAME_FUNCTIONAL_GROUPS_SEQUENCE) && + tags[PER_FRAME_FUNCTIONAL_GROUPS_SEQUENCE].type() == Json::arrayValue && + static_cast<Json::Value::ArrayIndex>(f) < tags[PER_FRAME_FUNCTIONAL_GROUPS_SEQUENCE].size() && + tags[PER_FRAME_FUNCTIONAL_GROUPS_SEQUENCE][f].type() == Json::objectValue && + tags[PER_FRAME_FUNCTIONAL_GROUPS_SEQUENCE][f].isMember(PIXEL_VALUE_TRANSFORMATION_SEQUENCE) && + tags[PER_FRAME_FUNCTIONAL_GROUPS_SEQUENCE][f][PIXEL_VALUE_TRANSFORMATION_SEQUENCE].type() == Json::arrayValue && + tags[PER_FRAME_FUNCTIONAL_GROUPS_SEQUENCE][f][PIXEL_VALUE_TRANSFORMATION_SEQUENCE].size() == 1) { - buffer.GetDicomInstance(instanceId); + ReadRescale(parameters, tags[PER_FRAME_FUNCTIONAL_GROUPS_SEQUENCE][f][PIXEL_VALUE_TRANSFORMATION_SEQUENCE][0]); + } - Json::Value tags; - buffer.DicomToJson(tags, OrthancPluginDicomToJsonFormat_Short, OrthancPluginDicomToJsonFlags_None, 255); - - if (tags.isMember(RESCALE_SLOPE) && - tags[RESCALE_SLOPE].type() == Json::stringValue) + if (!parameters.HasWindowing()) + { + if (ReadDefaultWindow(parameters, tags)) { - try - { - parameters.SetRescaleSlope - (boost::lexical_cast<float> - (Orthanc::Toolbox::StripSpaces(tags[RESCALE_SLOPE].asString()))); - } - catch (boost::bad_lexical_cast&) - { - } } - - if (tags.isMember(RESCALE_INTERCEPT) && - tags[RESCALE_INTERCEPT].type() == Json::stringValue) + else if (tags.isMember(PER_FRAME_FUNCTIONAL_GROUPS_SEQUENCE) && + tags[PER_FRAME_FUNCTIONAL_GROUPS_SEQUENCE].type() == Json::arrayValue && + static_cast<Json::Value::ArrayIndex>(f) < tags[PER_FRAME_FUNCTIONAL_GROUPS_SEQUENCE].size() && + tags[PER_FRAME_FUNCTIONAL_GROUPS_SEQUENCE][f].type() == Json::objectValue && + tags[PER_FRAME_FUNCTIONAL_GROUPS_SEQUENCE][f].isMember(FRAME_VOI_LUT_SEQUENCE) && + tags[PER_FRAME_FUNCTIONAL_GROUPS_SEQUENCE][f][FRAME_VOI_LUT_SEQUENCE].type() == Json::arrayValue && + tags[PER_FRAME_FUNCTIONAL_GROUPS_SEQUENCE][f][FRAME_VOI_LUT_SEQUENCE].size() == 1) { - try - { - parameters.SetRescaleIntercept - (boost::lexical_cast<float> - (Orthanc::Toolbox::StripSpaces(tags[RESCALE_INTERCEPT].asString()))); - } - catch (boost::bad_lexical_cast&) - { - } + ReadDefaultWindow(parameters, tags[PER_FRAME_FUNCTIONAL_GROUPS_SEQUENCE][f][FRAME_VOI_LUT_SEQUENCE][0]); } + } - OrthancPlugins::OrthancImage dicom; - dicom.DecodeDicomImage(buffer.GetData(), buffer.GetSize(), static_cast<unsigned int>(frame - 1)); + OrthancPlugins::OrthancImage dicom; + dicom.DecodeDicomImage(buffer.GetData(), buffer.GetSize(), f); - Orthanc::PixelFormat targetFormat; - OrthancPluginPixelFormat sdkFormat; - if (dicom.GetPixelFormat() == OrthancPluginPixelFormat_RGB24) - { - targetFormat = Orthanc::PixelFormat_RGB24; - sdkFormat = OrthancPluginPixelFormat_RGB24; - } - else - { - targetFormat = Orthanc::PixelFormat_Grayscale8; - sdkFormat = OrthancPluginPixelFormat_Grayscale8; - } + Orthanc::PixelFormat targetFormat; + OrthancPluginPixelFormat sdkFormat; + if (dicom.GetPixelFormat() == OrthancPluginPixelFormat_RGB24) + { + targetFormat = Orthanc::PixelFormat_RGB24; + sdkFormat = OrthancPluginPixelFormat_RGB24; + } + else + { + targetFormat = Orthanc::PixelFormat_Grayscale8; + sdkFormat = OrthancPluginPixelFormat_Grayscale8; + } - Orthanc::ImageAccessor source; - source.AssignReadOnly(Convert(dicom.GetPixelFormat()), - dicom.GetWidth(), dicom.GetHeight(), dicom.GetPitch(), dicom.GetBuffer()); + Orthanc::ImageAccessor source; + source.AssignReadOnly(Convert(dicom.GetPixelFormat()), + dicom.GetWidth(), dicom.GetHeight(), dicom.GetPitch(), dicom.GetBuffer()); - Orthanc::Image target(targetFormat, parameters.GetTargetWidth(source.GetWidth()), - parameters.GetTargetHeight(source.GetHeight()), false); + Orthanc::Image target(targetFormat, parameters.GetTargetWidth(source.GetWidth()), + parameters.GetTargetHeight(source.GetHeight()), false); - // New in 1.3: Fix for MONOCHROME1 images - bool invert = false; - if (target.GetFormat() == Orthanc::PixelFormat_Grayscale8 && - tags.isMember(PHOTOMETRIC_INTERPRETATION) && - tags[PHOTOMETRIC_INTERPRETATION].type() == Json::stringValue) - { - std::string s = tags[PHOTOMETRIC_INTERPRETATION].asString(); - Orthanc::Toolbox::StripSpaces(s); - if (s == "MONOCHROME1") - { - invert = true; - } + // New in 1.3: Fix for MONOCHROME1 images + bool invert = false; + if (target.GetFormat() == Orthanc::PixelFormat_Grayscale8 && + tags.isMember(PHOTOMETRIC_INTERPRETATION) && + tags[PHOTOMETRIC_INTERPRETATION].type() == Json::stringValue) + { + std::string s = tags[PHOTOMETRIC_INTERPRETATION].asString(); + Orthanc::Toolbox::StripSpaces(s); + if (s == "MONOCHROME1") + { + invert = true; } + } - ApplyRendering(target, source, parameters, invert); + ApplyRendering(target, source, parameters, invert); - switch (mime) - { - case Orthanc::MimeType_Png: - OrthancPluginCompressAndAnswerPngImage(OrthancPlugins::GetGlobalContext(), output, sdkFormat, - target.GetWidth(), target.GetHeight(), target.GetPitch(), target.GetBuffer()); - break; + switch (mime) + { + case Orthanc::MimeType_Png: + OrthancPluginCompressAndAnswerPngImage(OrthancPlugins::GetGlobalContext(), output, sdkFormat, + target.GetWidth(), target.GetHeight(), target.GetPitch(), target.GetBuffer()); + break; - case Orthanc::MimeType_Jpeg: - OrthancPluginCompressAndAnswerJpegImage(OrthancPlugins::GetGlobalContext(), output, sdkFormat, - target.GetWidth(), target.GetHeight(), target.GetPitch(), target.GetBuffer(), - parameters.GetQuality()); - break; + case Orthanc::MimeType_Jpeg: + OrthancPluginCompressAndAnswerJpegImage(OrthancPlugins::GetGlobalContext(), output, sdkFormat, + target.GetWidth(), target.GetHeight(), target.GetPitch(), target.GetBuffer(), + parameters.GetQuality()); + break; - default: - throw Orthanc::OrthancException(Orthanc::ErrorCode_NotImplemented); - } - } - } - else - { - // No customization of the rendering. Return the default - // preview of Orthanc. - std::map<std::string, std::string> headers; - headers["Accept"] = Orthanc::EnumerationToString(mime); - - /** - * (1) In DICOMweb, the "frame" parameter is in the range [1..N], - * whereas Orthanc uses range [0..N-1], hence the "-1" below. - * - * (2) We can use "/rendered" that was introduced in the REST API - * of Orthanc 1.6.0, as since release 1.2 of the DICOMweb plugin, - * the minimal SDK version is Orthanc 1.7.0 (in order to be able - * to use transcoding primitives). In releases <= 1.2, "/preview" - * was used, which caused one issue: - * https://groups.google.com/d/msg/orthanc-users/mKgr2QAKTCU/R7u4I1LvBAAJ - **/ - if (buffer.RestApiGet("/instances/" + instanceId + "/frames/" + - boost::lexical_cast<std::string>(frame - 1) + "/rendered", headers, false)) - { - OrthancPluginAnswerBuffer(OrthancPlugins::GetGlobalContext(), output, buffer.GetData(), - buffer.GetSize(), Orthanc::EnumerationToString(mime)); - } - else - { - badFrameError = true; + default: + throw Orthanc::OrthancException(Orthanc::ErrorCode_NotImplemented); } } - - if (badFrameError) - { - throw Orthanc::OrthancException(Orthanc::ErrorCode_ParameterOutOfRange, - "Inexistent frame index in this image: " + boost::lexical_cast<std::string>(frame)); - } } diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/OrthancDicomWeb-1.6/Resources/Orthanc/CMake/DownloadOrthancFramework.cmake new/OrthancDicomWeb-1.7/Resources/Orthanc/CMake/DownloadOrthancFramework.cmake --- old/OrthancDicomWeb-1.6/Resources/Orthanc/CMake/DownloadOrthancFramework.cmake 2021-05-07 13:31:18.000000000 +0200 +++ new/OrthancDicomWeb-1.7/Resources/Orthanc/CMake/DownloadOrthancFramework.cmake 2021-08-31 15:32:10.000000000 +0200 @@ -130,6 +130,14 @@ set(ORTHANC_FRAMEWORK_MD5 "3ea66c09f64aca990016683b6375734e") elseif (ORTHANC_FRAMEWORK_VERSION STREQUAL "1.9.3") set(ORTHANC_FRAMEWORK_MD5 "9b86e6f00e03278293cd15643cc0233f") + elseif (ORTHANC_FRAMEWORK_VERSION STREQUAL "1.9.4") + set(ORTHANC_FRAMEWORK_MD5 "6d5ca4a73ac7d42445041ca79de1624d") + elseif (ORTHANC_FRAMEWORK_VERSION STREQUAL "1.9.5") + set(ORTHANC_FRAMEWORK_MD5 "10fc64de1254a095e5d3ed3931f0cfbb") + elseif (ORTHANC_FRAMEWORK_VERSION STREQUAL "1.9.6") + set(ORTHANC_FRAMEWORK_MD5 "4b5d05683d747c29b2860ad79d11e62e") + elseif (ORTHANC_FRAMEWORK_VERSION STREQUAL "1.9.7") + set(ORTHANC_FRAMEWORK_MD5 "c912bbb860d640d3ae3003b5c9698205") # Below this point are development snapshots that were used to # release some plugin, before an official release of the Orthanc