Mercurial > hg > orthanc
diff Core/Images/ImageProcessing.cpp @ 3600:4066998150ef
/instances/{id}/preview route now takes the windowing into account
author | Alain Mazy <alain@mazy.be> |
---|---|
date | Thu, 09 Jan 2020 18:54:40 +0100 |
parents | 113a9643e8bb |
children | 94f4a18a79cc |
line wrap: on
line diff
--- a/Core/Images/ImageProcessing.cpp Tue Jan 07 10:53:32 2020 +0100 +++ b/Core/Images/ImageProcessing.cpp Thu Jan 09 18:54:40 2020 +0100 @@ -489,89 +489,120 @@ } } + template <typename TargetType, typename SourceType> + static void ApplyWindowingInternal(ImageAccessor& target, + const ImageAccessor& source, + float windowCenter, + float windowWidth, + float rescaleSlope, + float rescaleIntercept, + bool invert) + { + // WARNING - "::min()" should be replaced by "::lowest()" if + // dealing with float or double (which is not the case so far) + assert(sizeof(TargetType) <= 2); // Safeguard to remember about "float/double" + const TargetType minTargetValue = std::numeric_limits<TargetType>::min(); + const TargetType maxTargetValue = std::numeric_limits<TargetType>::max(); + const float minFloatValue = static_cast<float>(minTargetValue); + const float maxFloatValue = static_cast<float>(maxTargetValue); + + const float windowIntercept = windowCenter - windowWidth / 2.0f; + const float windowSlope = (maxFloatValue + 1.0f) / windowWidth; + + const unsigned int width = source.GetWidth(); + const unsigned int height = source.GetHeight(); + + for (unsigned int y = 0; y < height; y++) + { + TargetType* t = reinterpret_cast<TargetType*>(target.GetRow(y)); + const SourceType* s = reinterpret_cast<const SourceType*>(source.GetConstRow(y)); + + for (unsigned int x = 0; x < width; x++, t++, s++) + { + float rescaledValue = *s * rescaleSlope + rescaleIntercept; + float v = (rescaledValue - windowIntercept) * windowSlope; + if (v <= minFloatValue) + { + v = minFloatValue; + } + else if (v >= maxFloatValue) + { + v = maxFloatValue; + } + + TargetType vv = static_cast<TargetType>(v); + + if (invert) + { + vv = maxTargetValue - vv; + } + + *t = static_cast<TargetType>(vv); + } + } + } void ImageProcessing::ApplyWindowing(ImageAccessor& target, const ImageAccessor& source, float windowCenter, float windowWidth, - Orthanc::PhotometricInterpretation sourcePhotometricInterpretation) + float rescaleSlope, + float rescaleIntercept, + bool invert) { - if (source.GetFormat() != Orthanc::PixelFormat_Float32) - { - throw OrthancException(ErrorCode_NotImplemented); - } - - if (sourcePhotometricInterpretation != Orthanc::PhotometricInterpretation_Monochrome1 - && sourcePhotometricInterpretation != Orthanc::PhotometricInterpretation_Monochrome2) - { - throw OrthancException(ErrorCode_ParameterOutOfRange); - } - if (target.GetWidth() != source.GetWidth() || target.GetHeight() != source.GetHeight()) { throw OrthancException(ErrorCode_IncompatibleImageSize); } - unsigned int targetBytesPerPixels = target.GetBytesPerPixel(); - unsigned int targetChannelsPerPixels = 0; - switch (target.GetFormat()) - { - case Orthanc::PixelFormat_Grayscale8: - targetChannelsPerPixels = 1; - break; - case Orthanc::PixelFormat_RGBA32: - case Orthanc::PixelFormat_BGRA32: - case Orthanc::PixelFormat_RGB24: - targetChannelsPerPixels = 3; - break; - default: - throw OrthancException(ErrorCode_NotImplemented); - } - - const float a = windowCenter - windowWidth / 2.0f; - const float slope = 256.0f / windowWidth; - bool isInverted = sourcePhotometricInterpretation == Orthanc::PhotometricInterpretation_Monochrome1; - - const unsigned int width = source.GetWidth(); - const unsigned int height = source.GetHeight(); - - assert(sizeof(float) == 4); - - for (unsigned int y = 0; y < height; y++) + switch (source.GetFormat()) { - const float* p = reinterpret_cast<const float*>(source.GetConstRow(y)); - uint8_t* q = reinterpret_cast<uint8_t*>(target.GetRow(y)); - - for (unsigned int x = 0; x < width; x++) + case Orthanc::PixelFormat_Float32: { - float v = (*p - a) * slope; - if (v <= 0) + switch (target.GetFormat()) { - v = 0; - } - else if (v >= 255) - { - v = 255; + case Orthanc::PixelFormat_Grayscale8: + ApplyWindowingInternal<uint8_t, float>(target, source, windowCenter, windowWidth, rescaleSlope, rescaleIntercept, invert); + break; + case Orthanc::PixelFormat_Grayscale16: + ApplyWindowingInternal<uint16_t, float>(target, source, windowCenter, windowWidth, rescaleSlope, rescaleIntercept, invert); + break; + default: + throw OrthancException(ErrorCode_NotImplemented); } - - uint8_t vv = static_cast<uint8_t>(v); - - if (isInverted) + };break; + case Orthanc::PixelFormat_Grayscale8: + { + switch (target.GetFormat()) { - vv = 255 - vv; + case Orthanc::PixelFormat_Grayscale8: + ApplyWindowingInternal<uint8_t, uint8_t>(target, source, windowCenter, windowWidth, rescaleSlope, rescaleIntercept, invert); + break; + case Orthanc::PixelFormat_Grayscale16: + ApplyWindowingInternal<uint16_t, uint8_t>(target, source, windowCenter, windowWidth, rescaleSlope, rescaleIntercept, invert); + break; + default: + throw OrthancException(ErrorCode_NotImplemented); } - - for (unsigned int c = 0; c < targetChannelsPerPixels; c++) + };break; + case Orthanc::PixelFormat_Grayscale16: + { + switch (target.GetFormat()) { - q[c] = vv; + case Orthanc::PixelFormat_Grayscale8: + ApplyWindowingInternal<uint8_t, uint16_t>(target, source, windowCenter, windowWidth, rescaleSlope, rescaleIntercept, invert); + break; + case Orthanc::PixelFormat_Grayscale16: + ApplyWindowingInternal<uint16_t, uint16_t>(target, source, windowCenter, windowWidth, rescaleSlope, rescaleIntercept, invert); + break; + default: + throw OrthancException(ErrorCode_NotImplemented); } - - p++; - q += targetBytesPerPixels; - } + };break; + default: + throw OrthancException(ErrorCode_NotImplemented); } - }