Mercurial > hg > orthanc
changeset 863:3c0d0836f704 jpeg
refactoring
author | Sebastien Jodogne <s.jodogne@gmail.com> |
---|---|
date | Tue, 10 Jun 2014 17:20:33 +0200 |
parents | 5a125d587810 |
children | 2c545bb20dd3 |
files | Core/DicomFormat/DicomImageInformation.cpp Core/Enumerations.h Core/ImageFormats/ImageBuffer.cpp Core/ImageFormats/ImageBuffer.h Core/ImageFormats/ImageProcessing.cpp Core/ImageFormats/ImageProcessing.h OrthancServer/FromDcmtkBridge.cpp OrthancServer/Internals/DicomImageDecoder.cpp OrthancServer/Internals/DicomImageDecoder.h |
diffstat | 9 files changed, 561 insertions(+), 155 deletions(-) [+] |
line wrap: on
line diff
--- a/Core/DicomFormat/DicomImageInformation.cpp Sat Jun 07 10:51:28 2014 +0200 +++ b/Core/DicomFormat/DicomImageInformation.cpp Tue Jun 10 17:20:33 2014 +0200 @@ -159,11 +159,6 @@ bool DicomImageInformation::ExtractPixelFormat(PixelFormat& format) const { - if (IsPlanar()) - { - return false; - } - if (GetBitsStored() == 8 && GetChannelCount() == 1 && !IsSigned()) { format = PixelFormat_Grayscale8;
--- a/Core/Enumerations.h Sat Jun 07 10:51:28 2014 +0200 +++ b/Core/Enumerations.h Tue Jun 10 17:20:33 2014 +0200 @@ -82,34 +82,34 @@ /** * {summary}{Color image in RGB24 format.} * {description}{This format describes a color image. The pixels are stored in 3 - * consecutive bytes. The memory layout is RGB. + * consecutive bytes. The memory layout is RGB.} **/ - PixelFormat_RGB24, + PixelFormat_RGB24 = 1, /** * {summary}{Color image in RGBA32 format.} * {description}{This format describes a color image. The pixels are stored in 4 - * consecutive bytes. The memory layout is RGBA. + * consecutive bytes. The memory layout is RGBA.} **/ - PixelFormat_RGBA32, + PixelFormat_RGBA32 = 2, /** * {summary}{Graylevel 8bpp image.} * {description}{The image is graylevel. Each pixel is unsigned and stored in one byte.} **/ - PixelFormat_Grayscale8, + PixelFormat_Grayscale8 = 3, /** * {summary}{Graylevel, unsigned 16bpp image.} * {description}{The image is graylevel. Each pixel is unsigned and stored in two bytes.} **/ - PixelFormat_Grayscale16, + PixelFormat_Grayscale16 = 4, /** * {summary}{Graylevel, signed 16bpp image.} * {description}{The image is graylevel. Each pixel is signed and stored in two bytes.} **/ - PixelFormat_SignedGrayscale16 + PixelFormat_SignedGrayscale16 = 5 }; @@ -122,22 +122,22 @@ * {summary}{Rescaled to 8bpp.} * {description}{The minimum value of the image is set to 0, and its maximum value is set to 255.} **/ - ImageExtractionMode_Preview, + ImageExtractionMode_Preview = 1, /** * {summary}{Truncation to the [0, 255] range.} **/ - ImageExtractionMode_UInt8, + ImageExtractionMode_UInt8 = 2, /** * {summary}{Truncation to the [0, 65535] range.} **/ - ImageExtractionMode_UInt16, + ImageExtractionMode_UInt16 = 3, /** * {summary}{Truncation to the [-32768, 32767] range.} **/ - ImageExtractionMode_Int16 + ImageExtractionMode_Int16 = 4 };
--- a/Core/ImageFormats/ImageBuffer.cpp Sat Jun 07 10:51:28 2014 +0200 +++ b/Core/ImageFormats/ImageBuffer.cpp Tue Jun 10 17:20:33 2014 +0200 @@ -33,7 +33,10 @@ #include "../PrecompiledHeaders.h" #include "ImageBuffer.h" +#include "../OrthancException.h" + #include <stdio.h> +#include <stdlib.h> namespace Orthanc { @@ -41,6 +44,8 @@ { if (changed_) { + Deallocate(); + /* if (forceMinimalPitch_) { @@ -49,15 +54,19 @@ */ pitch_ = GetBytesPerPixel() * width_; + size_t size = pitch_ * height_; - data_.resize(pitch_ * height_); - if (data_.size() > 0) + if (size == 0) { - buffer_ = &data_[0]; + buffer_ = NULL; } else { - buffer_ = 0; + buffer_ = malloc(size); + if (buffer_ == NULL) + { + throw OrthancException(ErrorCode_NotEnoughMemory); + } } changed_ = false; @@ -65,6 +74,17 @@ } + void ImageBuffer::Deallocate() + { + if (buffer_ != NULL) + { + free(buffer_); + buffer_ = NULL; + changed_ = true; + } + } + + ImageBuffer::ImageBuffer(unsigned int width, unsigned int height, PixelFormat format) @@ -90,22 +110,31 @@ void ImageBuffer::SetFormat(PixelFormat format) { - changed_ = true; - format_ = format; + if (format != format_) + { + changed_ = true; + format_ = format; + } } void ImageBuffer::SetWidth(unsigned int width) { - changed_ = true; - width_ = width; + if (width != width_) + { + changed_ = true; + width_ = width; + } } void ImageBuffer::SetHeight(unsigned int height) { - changed_ = true; - height_ = height; + if (height != height_) + { + changed_ = true; + height_ = height; + } } @@ -131,7 +160,33 @@ void ImageBuffer::SetMinimalPitchForced(bool force) { - changed_ = true; - forceMinimalPitch_ = force; + if (force != forceMinimalPitch_) + { + changed_ = true; + forceMinimalPitch_ = force; + } + } + + + void ImageBuffer::AcquireOwnership(ImageBuffer& other) + { + // Remove the content of the current image + Deallocate(); + + // Force the allocation of the other image (if not already + // allocated) + other.Allocate(); + + // Transfer the content of the other image + changed_ = false; + forceMinimalPitch_ = other.forceMinimalPitch_; + format_ = other.format_; + width_ = other.width_; + height_ = other.height_; + pitch_ = other.pitch_; + buffer_ = other.buffer_; + + // Force the reinitialization of the other image + other.Initialize(); } }
--- a/Core/ImageFormats/ImageBuffer.h Sat Jun 07 10:51:28 2014 +0200 +++ b/Core/ImageFormats/ImageBuffer.h Tue Jun 10 17:20:33 2014 +0200 @@ -36,26 +36,28 @@ #include <vector> #include <stdint.h> +#include <boost/noncopyable.hpp> namespace Orthanc { - class ImageBuffer + class ImageBuffer : public boost::noncopyable { private: bool changed_; - std::vector<uint8_t> data_; bool forceMinimalPitch_; // Currently unused PixelFormat format_; unsigned int width_; unsigned int height_; unsigned int pitch_; - uint8_t *buffer_; + void *buffer_; void Initialize(); void Allocate(); + void Deallocate(); + public: ImageBuffer(unsigned int width, unsigned int height, @@ -66,6 +68,11 @@ Initialize(); } + ~ImageBuffer() + { + Deallocate(); + } + PixelFormat GetFormat() const { return format_; @@ -102,5 +109,7 @@ } void SetMinimalPitchForced(bool force); + + void AcquireOwnership(ImageBuffer& other); }; }
--- a/Core/ImageFormats/ImageProcessing.cpp Sat Jun 07 10:51:28 2014 +0200 +++ b/Core/ImageFormats/ImageProcessing.cpp Tue Jun 10 17:20:33 2014 +0200 @@ -35,13 +35,13 @@ #include "../OrthancException.h" +#include <boost/math/special_functions/round.hpp> + #include <cassert> #include <string.h> #include <limits> #include <stdint.h> -#include <stdio.h> - namespace Orthanc { template <typename TargetType, typename SourceType> @@ -75,6 +75,167 @@ } + template <typename PixelType> + static void SetInternal(ImageAccessor& image, + int64_t constant) + { + for (unsigned int y = 0; y < image.GetHeight(); y++) + { + PixelType* p = reinterpret_cast<PixelType*>(image.GetRow(y)); + + for (unsigned int x = 0; x < image.GetWidth(); x++, p++) + { + *p = static_cast<PixelType>(constant); + } + } + } + + + template <typename PixelType> + static void GetMinMaxValueInternal(PixelType& minValue, + PixelType& maxValue, + const ImageAccessor& source) + { + // Deal with the special case of empty image + if (source.GetWidth() == 0 || + source.GetHeight() == 0) + { + minValue = 0; + maxValue = 0; + return; + } + + minValue = std::numeric_limits<PixelType>::max(); + maxValue = std::numeric_limits<PixelType>::min(); + + for (unsigned int y = 0; y < source.GetHeight(); y++) + { + const PixelType* p = reinterpret_cast<const PixelType*>(source.GetConstRow(y)); + + for (unsigned int x = 0; x < source.GetWidth(); x++, p++) + { + if (*p < minValue) + { + minValue = *p; + } + + if (*p > maxValue) + { + maxValue = *p; + } + } + } + } + + + + template <typename PixelType> + static void AddConstantInternal(ImageAccessor& image, + int64_t constant) + { + if (constant == 0) + { + return; + } + + const int64_t minValue = std::numeric_limits<PixelType>::min(); + const int64_t maxValue = std::numeric_limits<PixelType>::max(); + + for (unsigned int y = 0; y < image.GetHeight(); y++) + { + PixelType* p = reinterpret_cast<PixelType*>(image.GetRow(y)); + + for (unsigned int x = 0; x < image.GetWidth(); x++, p++) + { + int64_t v = static_cast<int64_t>(*p) + constant; + + if (v > maxValue) + { + *p = maxValue; + } + else if (v < minValue) + { + *p = minValue; + } + else + { + *p = static_cast<PixelType>(v); + } + } + } + } + + + + template <typename PixelType> + void MultiplyConstantInternal(ImageAccessor& image, + float factor) + { + if (abs(factor - 1.0f) <= std::numeric_limits<float>::epsilon()) + { + return; + } + + const int64_t minValue = std::numeric_limits<PixelType>::min(); + const int64_t maxValue = std::numeric_limits<PixelType>::max(); + + for (unsigned int y = 0; y < image.GetHeight(); y++) + { + PixelType* p = reinterpret_cast<PixelType*>(image.GetRow(y)); + + for (unsigned int x = 0; x < image.GetWidth(); x++, p++) + { + int64_t v = boost::math::llround(static_cast<float>(*p) * factor); + + if (v > maxValue) + { + *p = maxValue; + } + else if (v < minValue) + { + *p = minValue; + } + else + { + *p = static_cast<PixelType>(v); + } + } + } + } + + + template <typename PixelType> + void ShiftScaleInternal(ImageAccessor& image, + float offset, + float scaling) + { + const float minValue = static_cast<float>(std::numeric_limits<PixelType>::min()); + const float maxValue = static_cast<float>(std::numeric_limits<PixelType>::max()); + + for (unsigned int y = 0; y < image.GetHeight(); y++) + { + PixelType* p = reinterpret_cast<PixelType*>(image.GetRow(y)); + + for (unsigned int x = 0; x < image.GetWidth(); x++, p++) + { + float v = (static_cast<float>(*p) + offset) * scaling; + + if (v > maxValue) + { + *p = std::numeric_limits<PixelType>::max(); + } + else if (v < minValue) + { + *p = std::numeric_limits<PixelType>::min(); + } + else + { + *p = static_cast<PixelType>(boost::math::iround(v)); + } + } + } + } + void ImageProcessing::Copy(ImageAccessor& target, const ImageAccessor& source) @@ -133,31 +294,7 @@ if (target.GetFormat() == PixelFormat_Grayscale8 && source.GetFormat() == PixelFormat_Grayscale16) { - printf("ICI2\n"); ConvertInternal<uint8_t, uint16_t>(target, source); - - /*for (unsigned int y = 0; y < source.GetHeight(); y++) - { - uint8_t* t = reinterpret_cast<uint8_t*>(target.GetRow(y)); - const uint16_t* s = reinterpret_cast<const uint16_t*>(source.GetConstRow(y)); - - for (unsigned int x = 0; x < source.GetWidth(); x++, t++, s++) - { - if (*s < 0) - { - *t = 0; - } - else if (*s > 255) - { - *t = 255; - } - else - { - *t = static_cast<uint8_t>(*s); - } - } - }*/ - return; } @@ -186,6 +323,30 @@ } + + void ImageProcessing::Set(ImageAccessor& image, + int64_t value) + { + switch (image.GetFormat()) + { + case PixelFormat_Grayscale8: + SetInternal<uint8_t>(image, value); + return; + + case PixelFormat_Grayscale16: + SetInternal<uint16_t>(image, value); + return; + + case PixelFormat_SignedGrayscale16: + SetInternal<int16_t>(image, value); + return; + + default: + throw OrthancException(ErrorCode_NotImplemented); + } + } + + void ImageProcessing::ShiftRight(ImageAccessor& image, unsigned int shift) { @@ -200,4 +361,113 @@ throw OrthancException(ErrorCode_NotImplemented); } + + void ImageProcessing::GetMinMaxValue(int64_t& minValue, + int64_t& maxValue, + const ImageAccessor& image) + { + switch (image.GetFormat()) + { + case PixelFormat_Grayscale8: + { + uint8_t a, b; + GetMinMaxValueInternal<uint8_t>(a, b, image); + minValue = a; + maxValue = b; + break; + } + + case PixelFormat_Grayscale16: + { + uint16_t a, b; + GetMinMaxValueInternal<uint16_t>(a, b, image); + minValue = a; + maxValue = b; + break; + } + + case PixelFormat_SignedGrayscale16: + { + int16_t a, b; + GetMinMaxValueInternal<int16_t>(a, b, image); + minValue = a; + maxValue = b; + break; + } + + default: + throw OrthancException(ErrorCode_NotImplemented); + } + } + + + + void ImageProcessing::AddConstant(ImageAccessor& image, + int64_t value) + { + switch (image.GetFormat()) + { + case PixelFormat_Grayscale8: + AddConstantInternal<uint8_t>(image, value); + return; + + case PixelFormat_Grayscale16: + AddConstantInternal<uint16_t>(image, value); + return; + + case PixelFormat_SignedGrayscale16: + AddConstantInternal<int16_t>(image, value); + return; + + default: + throw OrthancException(ErrorCode_NotImplemented); + } + } + + + void ImageProcessing::MultiplyConstant(ImageAccessor& image, + float factor) + { + switch (image.GetFormat()) + { + case PixelFormat_Grayscale8: + MultiplyConstantInternal<uint8_t>(image, factor); + return; + + case PixelFormat_Grayscale16: + MultiplyConstantInternal<uint16_t>(image, factor); + return; + + case PixelFormat_SignedGrayscale16: + MultiplyConstantInternal<int16_t>(image, factor); + return; + + default: + throw OrthancException(ErrorCode_NotImplemented); + } + } + + + void ImageProcessing::ShiftScale(ImageAccessor& image, + float offset, + float scaling) + { + switch (image.GetFormat()) + { + case PixelFormat_Grayscale8: + ShiftScaleInternal<uint8_t>(image, offset, scaling); + return; + + case PixelFormat_Grayscale16: + ShiftScaleInternal<uint16_t>(image, offset, scaling); + return; + + case PixelFormat_SignedGrayscale16: + ShiftScaleInternal<int16_t>(image, offset, scaling); + return; + + default: + throw OrthancException(ErrorCode_NotImplemented); + } + } }
--- a/Core/ImageFormats/ImageProcessing.h Sat Jun 07 10:51:28 2014 +0200 +++ b/Core/ImageFormats/ImageProcessing.h Tue Jun 10 17:20:33 2014 +0200 @@ -34,6 +34,8 @@ #include "ImageAccessor.h" +#include <stdint.h> + namespace Orthanc { class ImageProcessing @@ -45,7 +47,24 @@ static void Convert(ImageAccessor& target, const ImageAccessor& source); + static void Set(ImageAccessor& image, + int64_t value); + static void ShiftRight(ImageAccessor& target, unsigned int shift); + + static void GetMinMaxValue(int64_t& minValue, + int64_t& maxValue, + const ImageAccessor& image); + + static void AddConstant(ImageAccessor& image, + int64_t value); + + static void MultiplyConstant(ImageAccessor& image, + float factor); + + static void ShiftScale(ImageAccessor& image, + float offset, + float scaling); }; }
--- a/OrthancServer/FromDcmtkBridge.cpp Sat Jun 07 10:51:28 2014 +0200 +++ b/OrthancServer/FromDcmtkBridge.cpp Tue Jun 10 17:20:33 2014 +0200 @@ -527,21 +527,45 @@ ImageExtractionMode mode) { // TODO CONTINUE THIS - if (mode == ImageExtractionMode_UInt8) + + ImageBuffer tmp; + bool ok = false; + + switch (mode) { - printf(">>>>>>>>\n"); - ImageBuffer tmp; - if (DicomImageDecoder::Decode(tmp, dataset, frame, PixelFormat_Grayscale8, DicomImageDecoder::Mode_Truncate)) - { - ImageAccessor accessor(tmp.GetAccessor()); - PngWriter writer; - writer.WriteToMemory(result, accessor); - printf("<<<<<<<< OK\n"); - return; - } - printf("<<<<<<<< FAILURE\n"); + case ImageExtractionMode_UInt8: + ok = DicomImageDecoder::DecodeAndTruncate(tmp, dataset, frame, PixelFormat_Grayscale8); + break; + + case ImageExtractionMode_UInt16: + ok = DicomImageDecoder::DecodeAndTruncate(tmp, dataset, frame, PixelFormat_Grayscale16); + break; + + case ImageExtractionMode_Int16: + ok = DicomImageDecoder::DecodeAndTruncate(tmp, dataset, frame, PixelFormat_SignedGrayscale16); + break; + + case ImageExtractionMode_Preview: + ok = DicomImageDecoder::DecodePreview(tmp, dataset, frame); + break; + + default: + throw OrthancException(ErrorCode_ParameterOutOfRange); } + if (ok) + { + ImageAccessor accessor(tmp.GetAccessor()); + PngWriter writer; + writer.WriteToMemory(result, accessor); + return; + } + else + { + throw OrthancException(ErrorCode_BadFileFormat); + } + + // See also: http://support.dcmtk.org/wiki/dcmtk/howto/accessing-compressed-data std::auto_ptr<DicomIntegerPixelAccessor> accessor;
--- a/OrthancServer/Internals/DicomImageDecoder.cpp Sat Jun 07 10:51:28 2014 +0200 +++ b/OrthancServer/Internals/DicomImageDecoder.cpp Tue Jun 10 17:20:33 2014 +0200 @@ -42,38 +42,38 @@ Program: GDCM (Grassroots DICOM). A DICOM library Module: http://gdcm.sourceforge.net/Copyright.html -Copyright (c) 2006-2011 Mathieu Malaterre -Copyright (c) 1993-2005 CREATIS -(CREATIS = Centre de Recherche et d'Applications en Traitement de l'Image) -All rights reserved. + Copyright (c) 2006-2011 Mathieu Malaterre + Copyright (c) 1993-2005 CREATIS + (CREATIS = Centre de Recherche et d'Applications en Traitement de l'Image) + All rights reserved. -Redistribution and use in source and binary forms, with or without -modification, are permitted provided that the following conditions are met: + Redistribution and use in source and binary forms, with or without + modification, are permitted provided that the following conditions are met: - * Redistributions of source code must retain the above copyright notice, - this list of conditions and the following disclaimer. + * Redistributions of source code must retain the above copyright notice, + this list of conditions and the following disclaimer. - * Redistributions in binary form must reproduce the above copyright notice, - this list of conditions and the following disclaimer in the documentation - and/or other materials provided with the distribution. + * Redistributions in binary form must reproduce the above copyright notice, + this list of conditions and the following disclaimer in the documentation + and/or other materials provided with the distribution. - * Neither name of Mathieu Malaterre, or CREATIS, nor the names of any - contributors (CNRS, INSERM, UCB, Universite Lyon I), may be used to - endorse or promote products derived from this software without specific - prior written permission. + * Neither name of Mathieu Malaterre, or CREATIS, nor the names of any + contributors (CNRS, INSERM, UCB, Universite Lyon I), may be used to + endorse or promote products derived from this software without specific + prior written permission. -THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS ``AS IS'' -AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE -IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE -ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHORS OR CONTRIBUTORS BE LIABLE FOR -ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL -DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR -SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER -CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, -OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE -OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS ``AS IS'' + AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHORS OR CONTRIBUTORS BE LIABLE FOR + ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR + SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER + CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, + OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. -=========================================================================*/ + =========================================================================*/ @@ -426,7 +426,8 @@ bool fastVersionSuccess = false; PixelFormat sourceFormat; - if (info.ExtractPixelFormat(sourceFormat)) + if (!info.IsPlanar() && + info.ExtractPixelFormat(sourceFormat)) { try { @@ -460,19 +461,19 @@ case PixelFormat_RGB24: case PixelFormat_RGBA32: case PixelFormat_Grayscale8: - CopyPixels<uint8_t>(targetAccessor, source.GetAccessor()); - break; + CopyPixels<uint8_t>(targetAccessor, source.GetAccessor()); + break; case PixelFormat_Grayscale16: - CopyPixels<uint16_t>(targetAccessor, source.GetAccessor()); - break; + CopyPixels<uint16_t>(targetAccessor, source.GetAccessor()); + break; case PixelFormat_SignedGrayscale16: - CopyPixels<int16_t>(targetAccessor, source.GetAccessor()); - break; + CopyPixels<int16_t>(targetAccessor, source.GetAccessor()); + break; default: - throw OrthancException(ErrorCode_InternalError); + throw OrthancException(ErrorCode_InternalError); } } } @@ -584,53 +585,97 @@ } - bool DicomImageDecoder::Decode(ImageBuffer& target, - DcmDataset& dataset, - unsigned int frame, - PixelFormat format, - Mode mode) + bool DicomImageDecoder::DecodeAndTruncate(ImageBuffer& target, + DcmDataset& dataset, + unsigned int frame, + PixelFormat format) { - // TODO OPTIMIZE THIS (avoid unnecessary image copies) !!! + // TODO Special case for uncompressed images + + ImageBuffer source; + if (!Decode(source, dataset, frame)) + { + return false; + } + + if (source.GetFormat() == format) + { + // No conversion is required, return the temporary image + target.AcquireOwnership(source); + return true; + } - ImageBuffer tmp; - if (!Decode(tmp, dataset, frame)) + target.SetFormat(format); + target.SetWidth(source.GetWidth()); + target.SetHeight(source.GetHeight()); + + ImageAccessor targetAccessor(target.GetAccessor()); + ImageAccessor sourceAccessor(source.GetAccessor()); + ImageProcessing::Convert(targetAccessor, sourceAccessor); + + return true; + } + + + bool DicomImageDecoder::DecodePreview(ImageBuffer& target, + DcmDataset& dataset, + unsigned int frame) + { + // TODO Special case for uncompressed images + + ImageBuffer source; + if (!Decode(source, dataset, frame)) { return false; } - if (!IsUncompressedImage(dataset) && !IsJpegLossless(dataset)) + switch (source.GetFormat()) { - printf("ICI\n"); - PngWriter w; - ImageAccessor b(tmp.GetConstAccessor()); - w.WriteToFile("toto.png", b); - } - - target.SetFormat(format); - target.SetWidth(tmp.GetWidth()); - target.SetHeight(tmp.GetHeight()); - - switch (mode) - { - case Mode_Truncate: + case PixelFormat_RGB24: { - if (!IsUncompressedImage(dataset) && !IsJpegLossless(dataset)) - { - printf("%d => %d\n", tmp.GetFormat(), target.GetFormat()); - } - ImageAccessor a(target.GetAccessor()); - ImageAccessor b(tmp.GetConstAccessor()); - ImageProcessing::Convert(a, b); + // Directly return color images (RGB) + target.AcquireOwnership(source); return true; } + case PixelFormat_Grayscale8: + case PixelFormat_Grayscale16: + case PixelFormat_SignedGrayscale16: + { + // Grayscale image: Stretch its dynamics to the [0,255] range + target.SetFormat(PixelFormat_Grayscale8); + target.SetWidth(source.GetWidth()); + target.SetHeight(source.GetHeight()); + + ImageAccessor targetAccessor(target.GetAccessor()); + ImageAccessor sourceAccessor(source.GetAccessor()); + + int64_t a, b; + ImageProcessing::GetMinMaxValue(a, b, sourceAccessor); + + if (a == b) + { + ImageProcessing::Set(targetAccessor, 0); + } + else + { + ImageProcessing::ShiftScale(sourceAccessor, -a, 255.0f / static_cast<float>(b - a)); + + if (source.GetFormat() == PixelFormat_Grayscale8) + { + target.AcquireOwnership(source); + } + else + { + ImageProcessing::Convert(targetAccessor, sourceAccessor); + } + } + + return true; + } + default: throw OrthancException(ErrorCode_NotImplemented); } - - return false; } - - - }
--- a/OrthancServer/Internals/DicomImageDecoder.h Sat Jun 07 10:51:28 2014 +0200 +++ b/OrthancServer/Internals/DicomImageDecoder.h Tue Jun 10 17:20:33 2014 +0200 @@ -40,13 +40,6 @@ { class DicomImageDecoder { - public: - enum Mode - { - Mode_Truncate, - Mode_Stretch - }; - private: class ImageSource; @@ -72,12 +65,6 @@ DcmDataset& dataset, unsigned int frame); - static void DecodeUncompressedImage(ImageBuffer& target, - DcmDataset& dataset, - unsigned int frame, - PixelFormat format, - Mode mode); - #if ORTHANC_JPEG_LOSSLESS_ENABLED == 1 static void DecodeJpegLossless(ImageBuffer& target, DcmDataset& dataset, @@ -88,11 +75,13 @@ DcmDataset& dataset, unsigned int frame); - static bool Decode(ImageBuffer& target, - DcmDataset& dataset, - unsigned int frame, - PixelFormat format, - Mode mode); + static bool DecodeAndTruncate(ImageBuffer& target, + DcmDataset& dataset, + unsigned int frame, + PixelFormat format); + static bool DecodePreview(ImageBuffer& target, + DcmDataset& dataset, + unsigned int frame); }; }