# HG changeset patch # User Sebastien Jodogne # Date 1401984856 -7200 # Node ID 03ea55da7429bdf443c79323f2df8782f6aec004 # Parent 715ab7674993d3626d7d6bd86bc47c5c48c4193a fully functional JPEG-LS conversion diff -r 715ab7674993 -r 03ea55da7429 OrthancServer/Internals/DicomImageDecoder.cpp --- a/OrthancServer/Internals/DicomImageDecoder.cpp Thu Jun 05 16:23:03 2014 +0200 +++ b/OrthancServer/Internals/DicomImageDecoder.cpp Thu Jun 05 18:14:16 2014 +0200 @@ -78,14 +78,18 @@ #include "../../Core/OrthancException.h" +#include "../../Core/DicomFormat/DicomIntegerPixelAccessor.h" #include "../ToDcmtkBridge.h" +#include "../FromDcmtkBridge.h" +#include + +#include + +#if ORTHANC_JPEG_LOSSLESS_ENABLED == 1 #include #include #include -#include - -#if ORTHANC_JPEG_LOSSLESS_ENABLED == 1 #endif @@ -274,9 +278,183 @@ } + bool DicomImageDecoder::IsUncompressedImage(const DcmDataset& dataset) + { + return (dataset.getOriginalXfer() == EXS_Unknown || + dataset.getOriginalXfer() == EXS_LittleEndianImplicit || + dataset.getOriginalXfer() == EXS_BigEndianImplicit || + dataset.getOriginalXfer() == EXS_LittleEndianExplicit || + dataset.getOriginalXfer() == EXS_BigEndianExplicit); + } + + + template + static void CopyPixels(ImageAccessor& target, + const DicomIntegerPixelAccessor& source) + { + const PixelType minValue = std::numeric_limits::min(); + const PixelType maxValue = std::numeric_limits::max(); + + for (unsigned int y = 0; y < source.GetHeight(); y++) + { + PixelType* pixel = reinterpret_cast(target.GetRow(y)); + for (unsigned int x = 0; x < source.GetWidth(); x++) + { + for (unsigned int c = 0; c < source.GetChannelCount(); c++, pixel++) + { + int32_t v = source.GetValue(x, y, c); + if (v < static_cast(minValue)) + { + *pixel = minValue; + } + else if (v > static_cast(maxValue)) + { + *pixel = maxValue; + } + else + { + *pixel = static_cast(v); + } + } + } + } + } + + + void DicomImageDecoder::DecodeUncompressedImage(ImageBuffer& target, + DcmDataset& dataset, + unsigned int frame) + { + if (!IsUncompressedImage(dataset)) + { + throw OrthancException(ErrorCode_BadParameterType); + } + + DecodeUncompressedImageInternal(target, dataset, frame); + } + + + void DicomImageDecoder::DecodeUncompressedImageInternal(ImageBuffer& target, + DcmDataset& dataset, + unsigned int frame) + { + // See also: http://support.dcmtk.org/wiki/dcmtk/howto/accessing-compressed-data + + std::auto_ptr source; + + DicomMap m; + FromDcmtkBridge::Convert(m, dataset); + + + /** + * Create an accessor to the raw values of the DICOM image. + **/ + + std::string privateContent; + + DcmElement* e; + if (dataset.findAndGetElement(ToDcmtkBridge::Convert(DICOM_TAG_PIXEL_DATA), e).good() && + e != NULL) + { + Uint8* pixData = NULL; + if (e->getUint8Array(pixData) == EC_Normal) + { + source.reset(new DicomIntegerPixelAccessor(m, pixData, e->getLength())); + } + } + else if (DicomImageDecoder::DecodePsmctRle1(privateContent, dataset)) + { + LOG(INFO) << "The PMSCT_RLE1 decoding has succeeded"; + Uint8* pixData = NULL; + if (privateContent.size() > 0) + { + pixData = reinterpret_cast(&privateContent[0]); + } + + source.reset(new DicomIntegerPixelAccessor(m, pixData, privateContent.size())); + } + + if (source.get() == NULL) + { + throw OrthancException(ErrorCode_BadFileFormat); + } + + source->SetCurrentFrame(frame); + + + /** + * Resize the target image, with some sanity checks. + **/ + + SetupImageBuffer(target, dataset); + + if (target.GetWidth() != target.GetWidth() || + target.GetHeight() != target.GetHeight()) + { + throw OrthancException(ErrorCode_InternalError); + } + + bool ok; + switch (target.GetFormat()) + { + case PixelFormat_RGB24: + ok = source->GetChannelCount() == 3; + break; + + case PixelFormat_RGBA32: + ok = source->GetChannelCount() == 4; + break; + + case PixelFormat_Grayscale8: + case PixelFormat_Grayscale16: + case PixelFormat_SignedGrayscale16: + ok = source->GetChannelCount() == 1; + break; + + default: + ok = false; // (*) + break; + } + + if (!ok) + { + throw OrthancException(ErrorCode_InternalError); + } + + + /** + * Loop over the DICOM buffer, storing its value into the target + * image. + **/ + + ImageAccessor accessor(target.GetAccessor()); + + switch (target.GetFormat()) + { + case PixelFormat_RGB24: + case PixelFormat_RGBA32: + case PixelFormat_Grayscale8: + CopyPixels(accessor, *source); + break; + + case PixelFormat_Grayscale16: + CopyPixels(accessor, *source); + break; + + case PixelFormat_SignedGrayscale16: + CopyPixels(accessor, *source); + break; + + default: + throw OrthancException(ErrorCode_InternalError); + } + } + + #if ORTHANC_JPEG_LOSSLESS_ENABLED == 1 void DicomImageDecoder::DecodeJpegLossless(ImageBuffer& target, - DcmDataset& dataset) + DcmDataset& dataset, + unsigned int frame) { if (!IsJpegLossless(dataset)) { @@ -315,7 +493,7 @@ OFString decompressedColorModel; // Out DJ_RPLossless representationParameter; OFCondition c = decoder.decodeFrame(&representationParameter, pixelSequence, ¶meters, - &dataset, 0, startFragment, accessor.GetBuffer(), + &dataset, frame, startFragment, accessor.GetBuffer(), accessor.GetSize(), decompressedColorModel); if (!c.good()) @@ -326,4 +504,47 @@ #endif + + bool DicomImageDecoder::Decode(ImageBuffer& target, + DcmDataset& dataset, + unsigned int frame) + { + if (IsUncompressedImage(dataset)) + { + DecodeUncompressedImage(target, dataset, frame); + return true; + } + +#if ORTHANC_JPEG_LOSSLESS_ENABLED == 1 + if (IsJpegLossless(dataset)) + { + LOG(INFO) << "Decoding a JPEG-LS image"; + DecodeJpegLossless(target, dataset, frame); + return true; + } +#endif + + /** + * This DICOM image format is not natively supported by + * Orthanc. As a last resort, try and decode it through + * DCMTK. This will result in higher memory consumption. This is + * actually the second example of the following page: + * http://support.dcmtk.org/docs/mod_dcmjpeg.html#Examples + **/ + + { + LOG(INFO) << "Using DCMTK to decode a compressed image"; + + std::auto_ptr converted(dynamic_cast(dataset.clone())); + converted->chooseRepresentation(EXS_LittleEndianExplicit, NULL); + + if (converted->canWriteXfer(EXS_LittleEndianExplicit)) + { + DecodeUncompressedImageInternal(target, *converted, frame); + return true; + } + } + + return false; + } } diff -r 715ab7674993 -r 03ea55da7429 OrthancServer/Internals/DicomImageDecoder.h --- a/OrthancServer/Internals/DicomImageDecoder.h Thu Jun 05 16:23:03 2014 +0200 +++ b/OrthancServer/Internals/DicomImageDecoder.h Thu Jun 05 18:14:16 2014 +0200 @@ -42,6 +42,10 @@ { public: // TODO SWITCH TO PRIVATE //private: + static void DecodeUncompressedImageInternal(ImageBuffer& target, + DcmDataset& dataset, + unsigned int frame); + static bool IsPsmctRle1(DcmDataset& dataset); static void SetupImageBuffer(ImageBuffer& target, @@ -51,12 +55,22 @@ DcmDataset& dataset); public: + static bool IsUncompressedImage(const DcmDataset& dataset); + static bool IsJpegLossless(const DcmDataset& dataset); + static void DecodeUncompressedImage(ImageBuffer& target, + DcmDataset& dataset, + unsigned int frame); + #if ORTHANC_JPEG_LOSSLESS_ENABLED == 1 static void DecodeJpegLossless(ImageBuffer& target, - DcmDataset& dataset); + DcmDataset& dataset, + unsigned int frame); #endif + static bool Decode(ImageBuffer& target, + DcmDataset& dataset, + unsigned int frame); }; } diff -r 715ab7674993 -r 03ea55da7429 OrthancServer/OrthancInitialization.cpp --- a/OrthancServer/OrthancInitialization.cpp Thu Jun 05 16:23:03 2014 +0200 +++ b/OrthancServer/OrthancInitialization.cpp Thu Jun 05 18:14:16 2014 +0200 @@ -45,6 +45,17 @@ #include #include + +#if ORTHANC_JPEG_ENABLED == 1 +#include +#endif + + +#if ORTHANC_JPEG_LOSSLESS_ENABLED == 1 +#include +#endif + + namespace Orthanc { static boost::mutex globalMutex_; @@ -182,6 +193,16 @@ RegisterUserContentType(); DicomServer::InitializeDictionary(); + +#if ORTHANC_JPEG_LOSSLESS_ENABLED == 1 + LOG(WARNING) << "Registering JPEG Lossless codecs"; + DJLSDecoderRegistration::registerCodecs(); +#endif + +#if ORTHANC_JPEG_ENABLED == 1 + LOG(WARNING) << "Registering JPEG codecs"; + DJDecoderRegistration::registerCodecs(); +#endif } @@ -191,6 +212,16 @@ boost::mutex::scoped_lock lock(globalMutex_); HttpClient::GlobalFinalize(); configuration_.reset(NULL); + +#if ORTHANC_JPEG_LOSSLESS_ENABLED == 1 + // Unregister JPEG-LS codecs + DJLSDecoderRegistration::cleanup(); +#endif + +#if ORTHANC_JPEG_ENABLED == 1 + // Unregister JPEG codecs + DJDecoderRegistration::cleanup(); +#endif } diff -r 715ab7674993 -r 03ea55da7429 UnitTestsSources/JpegLossless.cpp --- a/UnitTestsSources/JpegLossless.cpp Thu Jun 05 16:23:03 2014 +0200 +++ b/UnitTestsSources/JpegLossless.cpp Thu Jun 05 18:14:16 2014 +0200 @@ -49,8 +49,6 @@ TEST(JpegLossless, Basic) { - //DJLSDecoderRegistration::registerCodecs( EJLSUC_default, EJLSPC_restore,OFFalse ); - #if 0 // Fallback @@ -89,7 +87,8 @@ ASSERT_TRUE(DicomImageDecoder::IsJpegLossless(dataset)); ImageBuffer image; - DicomImageDecoder::DecodeJpegLossless(image, dataset); + //DicomImageDecoder::DecodeJpegLossless(image, dataset, 0); + DicomImageDecoder::Decode(image, dataset, 0); ImageAccessor accessor(image.GetAccessor()); @@ -108,9 +107,6 @@ } #endif - - - //DJLSDecoderRegistration::cleanup(); }