Mercurial > hg > orthanc
changeset 4777:3b78ba359db3
Support detection of windowing and rescale in Philips multiframe images
author | Sebastien Jodogne <s.jodogne@gmail.com> |
---|---|
date | Mon, 30 Aug 2021 11:41:05 +0200 |
parents | 79d4e155592b |
children | 50fd70169f6e |
files | NEWS OrthancFramework/Sources/DicomParsing/FromDcmtkBridge.cpp OrthancFramework/Sources/DicomParsing/FromDcmtkBridge.h OrthancFramework/Sources/DicomParsing/ParsedDicomFile.cpp OrthancFramework/Sources/DicomParsing/ParsedDicomFile.h OrthancFramework/UnitTestsSources/FromDcmtkTests.cpp OrthancServer/Sources/OrthancRestApi/OrthancRestResources.cpp |
diffstat | 7 files changed, 278 insertions(+), 91 deletions(-) [+] |
line wrap: on
line diff
--- a/NEWS Mon Aug 30 10:25:50 2021 +0200 +++ b/NEWS Mon Aug 30 11:41:05 2021 +0200 @@ -21,6 +21,7 @@ * Linux Standard Base (LSB) builds of Orthanc can load non-LSB builds of plugins * Fix upload of ZIP archives containing a DICOMDIR file * Fix computation of the estimated time of arrival in jobs +* Support detection of windowing and rescale in Philips multiframe Version 1.9.6 (2021-07-21)
--- a/OrthancFramework/Sources/DicomParsing/FromDcmtkBridge.cpp Mon Aug 30 10:25:50 2021 +0200 +++ b/OrthancFramework/Sources/DicomParsing/FromDcmtkBridge.cpp Mon Aug 30 11:41:05 2021 +0200 @@ -3164,10 +3164,10 @@ } - bool FromDcmtkBridge::LookupSubSequence(DicomMap& target, - DcmDataset& dataset, - const DicomPath& path, - size_t sequenceIndex) + bool FromDcmtkBridge::LookupSequenceItem(DicomMap& target, + DcmDataset& dataset, + const DicomPath& path, + size_t sequenceIndex) { class Visitor : public FromDcmtkBridge::IDicomPathVisitor {
--- a/OrthancFramework/Sources/DicomParsing/FromDcmtkBridge.h Mon Aug 30 10:25:50 2021 +0200 +++ b/OrthancFramework/Sources/DicomParsing/FromDcmtkBridge.h Mon Aug 30 11:41:05 2021 +0200 @@ -278,9 +278,9 @@ const DcmElement& element, DicomReplaceMode mode); - static bool LookupSubSequence(DicomMap& target, - DcmDataset& dataset, - const DicomPath& path, - size_t sequenceIndex); + static bool LookupSequenceItem(DicomMap& target, + DcmDataset& dataset, + const DicomPath& path, + size_t sequenceIndex); }; }
--- a/OrthancFramework/Sources/DicomParsing/ParsedDicomFile.cpp Mon Aug 30 10:25:50 2021 +0200 +++ b/OrthancFramework/Sources/DicomParsing/ParsedDicomFile.cpp Mon Aug 30 11:41:05 2021 +0200 @@ -78,6 +78,7 @@ #include "../Images/PamReader.h" #include "../Logging.h" #include "../OrthancException.h" +#include "../SerializationToolbox.h" #include "../Toolbox.h" #if ORTHANC_SANDBOXED == 0 @@ -1777,14 +1778,116 @@ } - bool ParsedDicomFile::LookupSubSequence(DicomMap& target, - const DicomPath& path, - size_t sequenceIndex) const + bool ParsedDicomFile::LookupSequenceItem(DicomMap& target, + const DicomPath& path, + size_t sequenceIndex) const + { + DcmDataset& dataset = *const_cast<ParsedDicomFile&>(*this).GetDcmtkObject().getDataset(); + return FromDcmtkBridge::LookupSequenceItem(target, dataset, path, sequenceIndex); + } + + + void ParsedDicomFile::GetDefaultWindowing(double& windowCenter, + double& windowWidth, + unsigned int frame) const { DcmDataset& dataset = *const_cast<ParsedDicomFile&>(*this).GetDcmtkObject().getDataset(); - return FromDcmtkBridge::LookupSubSequence(target, dataset, path, sequenceIndex); + + const char* wc = NULL; + const char* ww = NULL; + DcmItem *item1 = NULL; + DcmItem *item2 = NULL; + + if (dataset.findAndGetString(DCM_WindowCenter, wc).good() && + dataset.findAndGetString(DCM_WindowWidth, ww).good() && + wc != NULL && + ww != NULL && + SerializationToolbox::ParseFirstDouble(windowCenter, wc) && + SerializationToolbox::ParseFirstDouble(windowWidth, ww)) + { + return; // OK + } + else if (dataset.findAndGetSequenceItem(DCM_PerFrameFunctionalGroupsSequence, item1, frame).good() && + item1 != NULL && + item1->findAndGetSequenceItem(DCM_FrameVOILUTSequence, item2, 0).good() && + item2 != NULL && + item2->findAndGetString(DCM_WindowCenter, wc).good() && + item2->findAndGetString(DCM_WindowWidth, ww).good() && + wc != NULL && + ww != NULL && + SerializationToolbox::ParseFirstDouble(windowCenter, wc) && + SerializationToolbox::ParseFirstDouble(windowWidth, ww)) + { + // New in Orthanc 1.9.7, to deal with Philips multiframe images + // (cf. private mail from Tomas Kenda on 2021-08-17) + return; // OK + } + else + { + Uint16 bitsStored = 0; + if (!dataset.findAndGetUint16(DCM_BitsStored, bitsStored).good() || + bitsStored == 0) + { + bitsStored = 8; // Rough assumption + } + + windowWidth = static_cast<double>(1 << bitsStored); + windowCenter = windowWidth / 2.0f; + } } + + void ParsedDicomFile::GetRescale(double& rescaleIntercept, + double& rescaleSlope, + unsigned int frame) const + { + DcmDataset& dataset = *const_cast<ParsedDicomFile&>(*this).GetDcmtkObject().getDataset(); + + const char* sopClassUid = NULL; + const char* intercept = NULL; + const char* slope = NULL; + DcmItem *item1 = NULL; + DcmItem *item2 = NULL; + + if (dataset.findAndGetString(DCM_SOPClassUID, sopClassUid).good() && + sopClassUid != NULL && + std::string(sopClassUid) == std::string(UID_RTDoseStorage)) + { + // We must not take the rescale value into account in the case of doses + rescaleIntercept = 0; + rescaleSlope = 1; + } + else if (dataset.findAndGetString(DCM_RescaleIntercept, intercept).good() && + dataset.findAndGetString(DCM_RescaleSlope, slope).good() && + intercept != NULL && + slope != NULL && + SerializationToolbox::ParseFirstDouble(rescaleIntercept, intercept) && + SerializationToolbox::ParseFirstDouble(rescaleSlope, slope)) + { + return; // OK + } + else if (dataset.findAndGetSequenceItem(DCM_PerFrameFunctionalGroupsSequence, item1, frame).good() && + item1 != NULL && + item1->findAndGetSequenceItem(DCM_PixelValueTransformationSequence, item2, 0).good() && + item2 != NULL && + item2->findAndGetString(DCM_RescaleIntercept, intercept).good() && + item2->findAndGetString(DCM_RescaleSlope, slope).good() && + intercept != NULL && + slope != NULL && + SerializationToolbox::ParseFirstDouble(rescaleIntercept, intercept) && + SerializationToolbox::ParseFirstDouble(rescaleSlope, slope)) + { + // New in Orthanc 1.9.7, to deal with Philips multiframe images + // (cf. private mail from Tomas Kenda on 2021-08-17) + return; // OK + } + else + { + rescaleIntercept = 0; + rescaleSlope = 1; + } + } + #if ORTHANC_BUILDING_FRAMEWORK_LIBRARY == 1 // Alias for binary compatibility with Orthanc Framework 1.7.2 => don't use it anymore
--- a/OrthancFramework/Sources/DicomParsing/ParsedDicomFile.h Mon Aug 30 10:25:50 2021 +0200 +++ b/OrthancFramework/Sources/DicomParsing/ParsedDicomFile.h Mon Aug 30 11:41:05 2021 +0200 @@ -286,8 +286,16 @@ void ClearPath(const DicomPath& path, bool onlyIfExists); - bool LookupSubSequence(DicomMap& target, - const DicomPath& path, - size_t sequenceIndex) const; + bool LookupSequenceItem(DicomMap& target, + const DicomPath& path, + size_t sequenceIndex) const; + + void GetDefaultWindowing(double& windowCenter, + double& windowWidth, + unsigned int frame) const; + + void GetRescale(double& rescaleIntercept, + double& rescaleSlope, + unsigned int frame) const; }; }
--- a/OrthancFramework/UnitTestsSources/FromDcmtkTests.cpp Mon Aug 30 10:25:50 2021 +0200 +++ b/OrthancFramework/UnitTestsSources/FromDcmtkTests.cpp Mon Aug 30 11:41:05 2021 +0200 @@ -2744,24 +2744,24 @@ std::unique_ptr<ParsedDicomFile> dicom(ParsedDicomFile::CreateFromJson(v, DicomFromJsonFlags_None, "")); DicomMap m; - ASSERT_TRUE(dicom->LookupSubSequence(m, DicomPath(DICOM_TAG_REFERENCED_IMAGE_SEQUENCE), 0)); + ASSERT_TRUE(dicom->LookupSequenceItem(m, DicomPath(DICOM_TAG_REFERENCED_IMAGE_SEQUENCE), 0)); ASSERT_EQ(2u, m.GetSize()); ASSERT_EQ("1.2.840.113619.2.176.2025.1499492.7040.1171286241.719", m.GetStringValue(DICOM_TAG_REFERENCED_SOP_INSTANCE_UID, "", false)); - ASSERT_TRUE(dicom->LookupSubSequence(m, DicomPath(DICOM_TAG_REFERENCED_IMAGE_SEQUENCE), 1)); + ASSERT_TRUE(dicom->LookupSequenceItem(m, DicomPath(DICOM_TAG_REFERENCED_IMAGE_SEQUENCE), 1)); ASSERT_EQ(2u, m.GetSize()); ASSERT_EQ("1.2.840.113619.2.176.2025.1499492.7040.1171286241.726", m.GetStringValue(DICOM_TAG_REFERENCED_SOP_INSTANCE_UID, "", false)); - ASSERT_FALSE(dicom->LookupSubSequence(m, DicomPath(DICOM_TAG_REFERENCED_IMAGE_SEQUENCE), 2)); + ASSERT_FALSE(dicom->LookupSequenceItem(m, DicomPath(DICOM_TAG_REFERENCED_IMAGE_SEQUENCE), 2)); - ASSERT_TRUE(dicom->LookupSubSequence(m, DicomPath(DicomTag(0x0008, 0x1250), 0, DicomTag(0x0040, 0xa170)), 0)); + ASSERT_TRUE(dicom->LookupSequenceItem(m, DicomPath(DicomTag(0x0008, 0x1250), 0, DicomTag(0x0040, 0xa170)), 0)); ASSERT_EQ(2u, m.GetSize()); ASSERT_EQ("122403", m.GetStringValue(DicomTag(0x0008, 0x0100), "", false)); ASSERT_EQ("WORLD", m.GetStringValue(DICOM_TAG_SERIES_DESCRIPTION, "", false)); - ASSERT_FALSE(dicom->LookupSubSequence(m, DicomPath(DicomTag(0x0008, 0x1250), 0, DicomTag(0x0040, 0xa170)), 1)); + ASSERT_FALSE(dicom->LookupSequenceItem(m, DicomPath(DicomTag(0x0008, 0x1250), 0, DicomTag(0x0040, 0xa170)), 1)); } } @@ -3019,6 +3019,121 @@ +TEST(ParsedDicomFile, ImageInformation) +{ + double wc, ww; + double ri, rs; + PhotometricInterpretation p; + + { + ParsedDicomFile dicom(false); + dicom.GetDefaultWindowing(wc, ww, 5); + dicom.GetRescale(ri, rs, 5); + ASSERT_DOUBLE_EQ(128.0, wc); + ASSERT_DOUBLE_EQ(256.0, ww); + ASSERT_FALSE(dicom.LookupPhotometricInterpretation(p)); + ASSERT_DOUBLE_EQ(0.0, ri); + ASSERT_DOUBLE_EQ(1.0, rs); + } + + { + ParsedDicomFile dicom(false); + ASSERT_TRUE(dicom.GetDcmtkObject().getDataset()->putAndInsertString(DCM_BitsStored, "4").good()); + ASSERT_TRUE(dicom.GetDcmtkObject().getDataset()->putAndInsertString(DCM_PhotometricInterpretation, "RGB").good()); + dicom.GetDefaultWindowing(wc, ww, 5); + ASSERT_DOUBLE_EQ(8.0, wc); + ASSERT_DOUBLE_EQ(16.0, ww); + ASSERT_TRUE(dicom.LookupPhotometricInterpretation(p)); + ASSERT_EQ(PhotometricInterpretation_RGB, p); + } + + { + ParsedDicomFile dicom(false); + ASSERT_TRUE(dicom.GetDcmtkObject().getDataset()->putAndInsertString(DCM_WindowCenter, "12").good()); + ASSERT_TRUE(dicom.GetDcmtkObject().getDataset()->putAndInsertString(DCM_WindowWidth, "-22").good()); + ASSERT_TRUE(dicom.GetDcmtkObject().getDataset()->putAndInsertString(DCM_RescaleIntercept, "-22").good()); + ASSERT_TRUE(dicom.GetDcmtkObject().getDataset()->putAndInsertString(DCM_RescaleSlope, "-23").good()); + dicom.GetDefaultWindowing(wc, ww, 5); + dicom.GetRescale(ri, rs, 5); + ASSERT_DOUBLE_EQ(12.0, wc); + ASSERT_DOUBLE_EQ(-22.0, ww); + ASSERT_DOUBLE_EQ(-22.0, ri); + ASSERT_DOUBLE_EQ(-23.0, rs); + } + + { + ParsedDicomFile dicom(false); + ASSERT_TRUE(dicom.GetDcmtkObject().getDataset()->putAndInsertString(DCM_WindowCenter, "12\\13\\14").good()); + ASSERT_TRUE(dicom.GetDcmtkObject().getDataset()->putAndInsertString(DCM_WindowWidth, "-22\\-23\\-24").good()); + ASSERT_TRUE(dicom.GetDcmtkObject().getDataset()->putAndInsertString(DCM_RescaleIntercept, "32\\33\\34").good()); + ASSERT_TRUE(dicom.GetDcmtkObject().getDataset()->putAndInsertString(DCM_RescaleSlope, "-42\\-43\\-44").good()); + dicom.GetDefaultWindowing(wc, ww, 5); + dicom.GetRescale(ri, rs, 5); + ASSERT_DOUBLE_EQ(12.0, wc); + ASSERT_DOUBLE_EQ(-22.0, ww); + ASSERT_DOUBLE_EQ(32.0, ri); + ASSERT_DOUBLE_EQ(-42.0, rs); + } + + { + // Philips multiframe + Json::Value v = Json::objectValue; + v["PerFrameFunctionalGroupsSequence"][0]["FrameVOILUTSequence"][0]["WindowCenter"] = "614"; + v["PerFrameFunctionalGroupsSequence"][0]["FrameVOILUTSequence"][0]["WindowWidth"] = "1067"; + v["PerFrameFunctionalGroupsSequence"][0]["PixelValueTransformationSequence"][0]["RescaleIntercept"] = "12"; + v["PerFrameFunctionalGroupsSequence"][0]["PixelValueTransformationSequence"][0]["RescaleSlope"] = "2.551648"; + v["PerFrameFunctionalGroupsSequence"][1]["FrameVOILUTSequence"][0]["WindowCenter"] = "-61"; + v["PerFrameFunctionalGroupsSequence"][1]["FrameVOILUTSequence"][0]["WindowWidth"] = "-63"; + v["PerFrameFunctionalGroupsSequence"][1]["PixelValueTransformationSequence"][0]["RescaleIntercept"] = "13"; + v["PerFrameFunctionalGroupsSequence"][1]["PixelValueTransformationSequence"][0]["RescaleSlope"] = "-14"; + std::unique_ptr<ParsedDicomFile> dicom(ParsedDicomFile::CreateFromJson(v, DicomFromJsonFlags_None, "")); + + dicom->GetDefaultWindowing(wc, ww, 0); + dicom->GetRescale(ri, rs, 0); + ASSERT_DOUBLE_EQ(614.0, wc); + ASSERT_DOUBLE_EQ(1067.0, ww); + ASSERT_DOUBLE_EQ(12.0, ri); + ASSERT_DOUBLE_EQ(2.551648, rs); + + dicom->GetDefaultWindowing(wc, ww, 1); + dicom->GetRescale(ri, rs, 1); + ASSERT_DOUBLE_EQ(-61.0, wc); + ASSERT_DOUBLE_EQ(-63.0, ww); + ASSERT_DOUBLE_EQ(13.0, ri); + ASSERT_DOUBLE_EQ(-14.0, rs); + + dicom->GetDefaultWindowing(wc, ww, 2); + dicom->GetRescale(ri, rs, 2); + ASSERT_DOUBLE_EQ(128.0, wc); + ASSERT_DOUBLE_EQ(256.0, ww); + ASSERT_DOUBLE_EQ(0.0, ri); + ASSERT_DOUBLE_EQ(1.0, rs); + } + + { + // RT-DOSE + Json::Value v = Json::objectValue; + v["RescaleIntercept"] = "10"; + v["RescaleSlope"] = "20"; + v["PerFrameFunctionalGroupsSequence"][0]["PixelValueTransformationSequence"][0]["RescaleIntercept"] = "30"; + v["PerFrameFunctionalGroupsSequence"][0]["PixelValueTransformationSequence"][0]["RescaleSlope"] = "40"; + std::unique_ptr<ParsedDicomFile> dicom(ParsedDicomFile::CreateFromJson(v, DicomFromJsonFlags_None, "")); + + dicom->GetRescale(ri, rs, 0); + ASSERT_DOUBLE_EQ(10.0, ri); + ASSERT_DOUBLE_EQ(20.0, rs); + + v["SOPClassUID"] = "1.2.840.10008.5.1.4.1.1.481.2"; + dicom.reset(ParsedDicomFile::CreateFromJson(v, DicomFromJsonFlags_None, "")); + dicom->GetRescale(ri, rs, 0); + ASSERT_DOUBLE_EQ(0.0, ri); + ASSERT_DOUBLE_EQ(1.0, rs); + } +} + + + + #if ORTHANC_ENABLE_DCMTK_TRANSCODING == 1 #include "../Sources/DicomNetworking/DicomStoreUserConnection.h"
--- a/OrthancServer/Sources/OrthancRestApi/OrthancRestResources.cpp Mon Aug 30 10:25:50 2021 +0200 +++ b/OrthancServer/Sources/OrthancRestApi/OrthancRestResources.cpp Mon Aug 30 11:41:05 2021 +0200 @@ -666,7 +666,8 @@ // "dicom" is non-NULL iff. "RequiresDicomTags() == true" virtual void Handle(RestApiGetCall& call, std::unique_ptr<ImageAccessor>& decoded, - const ParsedDicomFile* dicom) = 0; + const ParsedDicomFile* dicom, + unsigned int frame) = 0; virtual bool RequiresDicomTags() const = 0; @@ -799,11 +800,11 @@ * interpretation, and with windowing parameters. **/ ServerContext::DicomCacheLocker locker(context, publicId); - handler.Handle(call, decoded, &locker.GetDicom()); + handler.Handle(call, decoded, &locker.GetDicom(), frame); } else { - handler.Handle(call, decoded, NULL); + handler.Handle(call, decoded, NULL, frame); } } catch (OrthancException& e) @@ -868,7 +869,8 @@ virtual void Handle(RestApiGetCall& call, std::unique_ptr<ImageAccessor>& decoded, - const ParsedDicomFile* dicom) ORTHANC_OVERRIDE + const ParsedDicomFile* dicom, + unsigned int frame) ORTHANC_OVERRIDE { bool invert = false; @@ -899,43 +901,8 @@ class RenderedFrameHandler : public IDecodedFrameHandler { private: - static void GetDicomParameters(bool& invert, - float& rescaleSlope, - float& rescaleIntercept, - float& windowWidth, - float& windowCenter, - const ParsedDicomFile& dicom) - { - DicomMap tags; - OrthancConfiguration::DefaultExtractDicomSummary(tags, dicom); - - DicomImageInformation info(tags); - - invert = (info.GetPhotometricInterpretation() == PhotometricInterpretation_Monochrome1); - - rescaleSlope = 1.0f; - rescaleIntercept = 0.0f; - - if (dicom.HasTag(Orthanc::DICOM_TAG_RESCALE_SLOPE) && - dicom.HasTag(Orthanc::DICOM_TAG_RESCALE_INTERCEPT)) - { - tags.ParseFloat(rescaleSlope, Orthanc::DICOM_TAG_RESCALE_SLOPE); - tags.ParseFloat(rescaleIntercept, Orthanc::DICOM_TAG_RESCALE_INTERCEPT); - } - - windowWidth = static_cast<float>(1 << info.GetBitsStored()) * rescaleSlope; - windowCenter = windowWidth / 2.0f + rescaleIntercept; - - if (tags.HasTag(Orthanc::DICOM_TAG_WINDOW_CENTER) && - tags.HasTag(Orthanc::DICOM_TAG_WINDOW_WIDTH)) - { - tags.ParseFirstFloat(windowCenter, Orthanc::DICOM_TAG_WINDOW_CENTER); - tags.ParseFirstFloat(windowWidth, Orthanc::DICOM_TAG_WINDOW_WIDTH); - } - } - - static void GetUserArguments(float& windowWidth /* inout */, - float& windowCenter /* inout */, + static void GetUserArguments(double& windowWidth /* inout */, + double& windowCenter /* inout */, unsigned int& argWidth, unsigned int& argHeight, bool& smooth, @@ -947,30 +914,18 @@ static const char* ARG_HEIGHT = "height"; static const char* ARG_SMOOTH = "smooth"; - if (call.HasArgument(ARG_WINDOW_WIDTH)) + if (call.HasArgument(ARG_WINDOW_WIDTH) && + !SerializationToolbox::ParseDouble(windowWidth, call.GetArgument(ARG_WINDOW_WIDTH, ""))) { - try - { - windowWidth = boost::lexical_cast<float>(call.GetArgument(ARG_WINDOW_WIDTH, "")); - } - catch (boost::bad_lexical_cast&) - { - throw OrthancException(ErrorCode_ParameterOutOfRange, - "Bad value for argument: " + std::string(ARG_WINDOW_WIDTH)); - } + throw OrthancException(ErrorCode_ParameterOutOfRange, + "Bad value for argument: " + std::string(ARG_WINDOW_WIDTH)); } - if (call.HasArgument(ARG_WINDOW_CENTER)) + if (call.HasArgument(ARG_WINDOW_CENTER) && + !SerializationToolbox::ParseDouble(windowCenter, call.GetArgument(ARG_WINDOW_CENTER, ""))) { - try - { - windowCenter = boost::lexical_cast<float>(call.GetArgument(ARG_WINDOW_CENTER, "")); - } - catch (boost::bad_lexical_cast&) - { - throw OrthancException(ErrorCode_ParameterOutOfRange, - "Bad value for argument: " + std::string(ARG_WINDOW_CENTER)); - } + throw OrthancException(ErrorCode_ParameterOutOfRange, + "Bad value for argument: " + std::string(ARG_WINDOW_CENTER)); } argWidth = 0; @@ -1032,17 +987,22 @@ public: virtual void Handle(RestApiGetCall& call, std::unique_ptr<ImageAccessor>& decoded, - const ParsedDicomFile* dicom) ORTHANC_OVERRIDE + const ParsedDicomFile* dicom, + unsigned int frame) ORTHANC_OVERRIDE { if (dicom == NULL) { throw OrthancException(ErrorCode_InternalError); } - bool invert; - float rescaleSlope, rescaleIntercept, windowWidth, windowCenter; - GetDicomParameters(invert, rescaleSlope, rescaleIntercept, windowWidth, windowCenter, *dicom); - + PhotometricInterpretation photometric; + const bool invert = (dicom->LookupPhotometricInterpretation(photometric) && + photometric == PhotometricInterpretation_Monochrome1); + + double rescaleIntercept, rescaleSlope, windowCenter, windowWidth; + dicom->GetRescale(rescaleIntercept, rescaleSlope, frame); + dicom->GetDefaultWindowing(windowCenter, windowWidth, frame); + unsigned int argWidth, argHeight; bool smooth; GetUserArguments(windowWidth, windowCenter, argWidth, argHeight, smooth, call); @@ -1112,16 +1072,16 @@ windowWidth = 1; } - if (std::abs(rescaleSlope) <= 0.1f) + if (std::abs(rescaleSlope) <= 0.1) { - rescaleSlope = 0.1f; + rescaleSlope = 0.1; } - const float scaling = 255.0f * rescaleSlope / windowWidth; - const float offset = (rescaleIntercept - windowCenter + windowWidth / 2.0f) / rescaleSlope; + const double scaling = 255.0 * rescaleSlope / windowWidth; + const double offset = (rescaleIntercept - windowCenter + windowWidth / 2.0) / rescaleSlope; std::unique_ptr<ImageAccessor> rescaled(new Image(PixelFormat_Grayscale8, decoded->GetWidth(), decoded->GetHeight(), false)); - ImageProcessing::ShiftScale(*rescaled, converted, offset, scaling, false); + ImageProcessing::ShiftScale(*rescaled, converted, static_cast<float>(offset), static_cast<float>(scaling), false); if (targetWidth == decoded->GetWidth() && targetHeight == decoded->GetHeight())