# HG changeset patch # User Sebastien Jodogne # Date 1452011101 -3600 # Node ID 8b1baa2315b828cb2ed013cb8e41bdb166ce16ad # Parent 4be8accf87687bc22a560ca35c78a7ac81a12a94 Huge speedup if decoding the family of JPEG transfer syntaxes diff -r 4be8accf8768 -r 8b1baa2315b8 Core/DicomFormat/DicomImageInformation.cpp --- a/Core/DicomFormat/DicomImageInformation.cpp Tue Jan 05 14:34:47 2016 +0100 +++ b/Core/DicomFormat/DicomImageInformation.cpp Tue Jan 05 17:25:01 2016 +0100 @@ -218,9 +218,16 @@ } - bool DicomImageInformation::ExtractPixelFormat(PixelFormat& format) const + bool DicomImageInformation::ExtractPixelFormat(PixelFormat& format, + bool ignorePhotometricInterpretation) const { - if (photometric_ == PhotometricInterpretation_Monochrome1 || + if (photometric_ == PhotometricInterpretation_Palette) + { + return false; + } + + if (ignorePhotometricInterpretation || + photometric_ == PhotometricInterpretation_Monochrome1 || photometric_ == PhotometricInterpretation_Monochrome2) { if (GetBitsStored() == 8 && GetChannelCount() == 1 && !IsSigned()) @@ -245,7 +252,7 @@ if (GetBitsStored() == 8 && GetChannelCount() == 3 && !IsSigned() && - photometric_ == PhotometricInterpretation_RGB) + (ignorePhotometricInterpretation || photometric_ == PhotometricInterpretation_RGB)) { format = PixelFormat_RGB24; return true; diff -r 4be8accf8768 -r 8b1baa2315b8 Core/DicomFormat/DicomImageInformation.h --- a/Core/DicomFormat/DicomImageInformation.h Tue Jan 05 14:34:47 2016 +0100 +++ b/Core/DicomFormat/DicomImageInformation.h Tue Jan 05 17:25:01 2016 +0100 @@ -119,6 +119,7 @@ return photometric_; } - bool ExtractPixelFormat(PixelFormat& format) const; + bool ExtractPixelFormat(PixelFormat& format, + bool ignorePhotometricInterpretation) const; }; } diff -r 4be8accf8768 -r 8b1baa2315b8 NEWS --- a/NEWS Tue Jan 05 14:34:47 2016 +0100 +++ b/NEWS Tue Jan 05 17:25:01 2016 +0100 @@ -3,7 +3,9 @@ * Support of optional tags for counting resources in C-Find: 0008-0061, 0008-0062, 0020-1200, 0020-1202, 0020-1204, 0020-1206, 0020-1208, 0020-1209 +* Huge speedup if decoding the family of JPEG transfer syntaxes * Refactoring leading to speedups with custom image decoders (including Web viewer plugin) +* Support decoding of RLE Lossless transfer syntax Version 1.0.0 (2015/12/15) diff -r 4be8accf8768 -r 8b1baa2315b8 OrthancServer/Internals/DicomImageDecoder.cpp --- a/OrthancServer/Internals/DicomImageDecoder.cpp Tue Jan 05 14:34:47 2016 +0100 +++ b/OrthancServer/Internals/DicomImageDecoder.cpp Tue Jan 05 17:25:01 2016 +0100 @@ -94,6 +94,8 @@ #include #include +#include +#include #if ORTHANC_JPEG_LOSSLESS_ENABLED == 1 #include @@ -101,6 +103,17 @@ #include #endif +#if ORTHANC_JPEG_ENABLED == 1 +#include +#include +#include +#include +#include +#include +#include +#include +#endif + namespace Orthanc { @@ -300,7 +313,8 @@ }; - ImageAccessor* DicomImageDecoder::CreateImage(DcmDataset& dataset) + ImageAccessor* DicomImageDecoder::CreateImage(DcmDataset& dataset, + bool ignorePhotometricInterpretation) { DicomMap m; FromDcmtkBridge::Convert(m, dataset); @@ -308,7 +322,7 @@ DicomImageInformation info(m); PixelFormat format; - if (!info.ExtractPixelFormat(format)) + if (!info.ExtractPixelFormat(format, ignorePhotometricInterpretation)) { LOG(WARNING) << "Unsupported DICOM image: " << info.GetBitsStored() << "bpp, " << info.GetChannelCount() << " channels, " @@ -357,7 +371,7 @@ ImageAccessor* DicomImageDecoder::DecodeUncompressedImage(DcmDataset& dataset, - unsigned int frame) + unsigned int frame) { ImageSource source; source.Setup(dataset, frame); @@ -367,7 +381,7 @@ * Resize the target image. **/ - std::auto_ptr target(CreateImage(dataset)); + std::auto_ptr target(CreateImage(dataset, false)); if (source.GetWidth() != target->GetWidth() || source.GetHeight() != target->GetHeight()) @@ -386,7 +400,7 @@ bool fastVersionSuccess = false; PixelFormat sourceFormat; if (!info.IsPlanar() && - info.ExtractPixelFormat(sourceFormat)) + info.ExtractPixelFormat(sourceFormat, false)) { try { @@ -466,12 +480,12 @@ } - static ImageAccessor* ApplyCodec(const DcmCodec& codec, - const DcmCodecParameter& parameters, - DcmDataset& dataset, - unsigned int frame) + ImageAccessor* DicomImageDecoder::ApplyCodec(const DcmCodec& codec, + const DcmCodecParameter& parameters, + DcmDataset& dataset, + unsigned int frame) { - std::auto_ptr target(DicomImageDecoder::CreateImage(dataset)); + std::auto_ptr target(CreateImage(dataset, true)); Uint32 startFragment = 0; // Default OFString decompressedColorModel; // Out @@ -526,12 +540,12 @@ switch (syntax) { case EXS_JPEGLSLossless: - LOG(INFO) << "Decoding a JPEG-LS lossless image"; + LOG(INFO) << "Decoding a JPEG-LS lossless DICOM image"; decoder.reset(new DJLSLosslessDecoder); break; case EXS_JPEGLSLossy: - LOG(INFO) << "Decoding a JPEG-LS near-lossless image"; + LOG(INFO) << "Decoding a JPEG-LS near-lossless DICOM image"; decoder.reset(new DJLSNearLosslessDecoder); break; @@ -545,24 +559,86 @@ #if ORTHANC_JPEG_ENABLED == 1 + /** + * Deal with JPEG images. + **/ + + if (syntax == EXS_JPEGProcess1TransferSyntax || // DJDecoderBaseline + syntax == EXS_JPEGProcess2_4TransferSyntax || // DJDecoderExtended + syntax == EXS_JPEGProcess6_8TransferSyntax || // DJDecoderSpectralSelection (retired) + syntax == EXS_JPEGProcess10_12TransferSyntax || // DJDecoderProgressive (retired) + syntax == EXS_JPEGProcess14TransferSyntax || // DJDecoderLossless + syntax == EXS_JPEGProcess14SV1TransferSyntax) // DJDecoderP14SV1 + { + // http://support.dcmtk.org/docs-snapshot/djutils_8h.html#a2a9695e5b6b0f5c45a64c7f072c1eb9d + DJCodecParameter parameters( + ECC_lossyYCbCr, // Mode for color conversion for compression, Unused for decompression + EDC_photometricInterpretation, // Perform color space conversion from YCbCr to RGB if DICOM photometric interpretation indicates YCbCr + EUC_default, // Mode for UID creation, unused for decompression + EPC_default); // Automatically determine whether color-by-plane is required from the SOP Class UID and decompressed photometric interpretation + std::auto_ptr decoder; + + switch (syntax) + { + case EXS_JPEGProcess1TransferSyntax: + LOG(INFO) << "Decoding a JPEG baseline (process 1) DICOM image"; + decoder.reset(new DJDecoderBaseline); + break; + + case EXS_JPEGProcess2_4TransferSyntax : + LOG(INFO) << "Decoding a JPEG baseline (processes 2 and 4) DICOM image"; + decoder.reset(new DJDecoderExtended); + break; + + case EXS_JPEGProcess6_8TransferSyntax: // Retired + LOG(INFO) << "Decoding a JPEG spectral section, nonhierarchical (processes 6 and 8) DICOM image"; + decoder.reset(new DJDecoderSpectralSelection); + break; + + case EXS_JPEGProcess10_12TransferSyntax: // Retired + LOG(INFO) << "Decoding a JPEG full progression, nonhierarchical (processes 10 and 12) DICOM image"; + decoder.reset(new DJDecoderProgressive); + break; + + case EXS_JPEGProcess14TransferSyntax: + LOG(INFO) << "Decoding a JPEG lossless, nonhierarchical (process 14) DICOM image"; + decoder.reset(new DJDecoderLossless); + break; + + case EXS_JPEGProcess14SV1TransferSyntax: + LOG(INFO) << "Decoding a JPEG lossless, nonhierarchical, first-order prediction (process 14 selection value 1) DICOM image"; + decoder.reset(new DJDecoderP14SV1); + break; + + default: + throw OrthancException(ErrorCode_InternalError); + } - // TODO Implement this part to speed up JPEG decompression + return ApplyCodec(*decoder, parameters, dataset, frame); + } #endif - // TODO DcmRLECodecDecoder + if (syntax == EXS_RLELossless) + { + LOG(INFO) << "Decoding a RLE lossless DICOM image"; + DcmRLECodecParameter parameters; + DcmRLECodecDecoder decoder; + return ApplyCodec(decoder, parameters, dataset, frame); + } /** * 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: + * Orthanc. As a last resort, try and decode it through DCMTK by + * converting its transfer syntax to Little Endian. 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"; + LOG(INFO) << "Decoding a compressed image by converting its transfer syntax to Little Endian"; std::auto_ptr converted(dynamic_cast(dataset.clone())); converted->chooseRepresentation(EXS_LittleEndianExplicit, NULL); diff -r 4be8accf8768 -r 8b1baa2315b8 OrthancServer/Internals/DicomImageDecoder.h --- a/OrthancServer/Internals/DicomImageDecoder.h Tue Jan 05 14:34:47 2016 +0100 +++ b/OrthancServer/Internals/DicomImageDecoder.h Tue Jan 05 17:25:01 2016 +0100 @@ -37,6 +37,8 @@ #include class DcmDataset; +class DcmCodec; +class DcmCodecParameter; namespace Orthanc { @@ -49,11 +51,19 @@ { } + static ImageAccessor* CreateImage(DcmDataset& dataset, + bool ignorePhotometricInterpretation); + static ImageAccessor* DecodeUncompressedImage(DcmDataset& dataset, unsigned int frame); static bool IsPsmctRle1(DcmDataset& dataset); + static ImageAccessor* ApplyCodec(const DcmCodec& codec, + const DcmCodecParameter& parameters, + DcmDataset& dataset, + unsigned int frame); + static bool TruncateDecodedImage(std::auto_ptr& image, PixelFormat format, bool allowColorConversion); @@ -64,8 +74,6 @@ ImageExtractionMode mode); public: - static ImageAccessor* CreateImage(DcmDataset& dataset); - static ImageAccessor *Decode(ParsedDicomFile& dicom, unsigned int frame); diff -r 4be8accf8768 -r 8b1baa2315b8 UnitTestsSources/ImageProcessingTests.cpp --- a/UnitTestsSources/ImageProcessingTests.cpp Tue Jan 05 14:34:47 2016 +0100 +++ b/UnitTestsSources/ImageProcessingTests.cpp Tue Jan 05 17:25:01 2016 +0100 @@ -55,7 +55,7 @@ DicomImageInformation info(m); PixelFormat format; - ASSERT_TRUE(info.ExtractPixelFormat(format)); + ASSERT_TRUE(info.ExtractPixelFormat(format, false)); ASSERT_EQ(PixelFormat_Grayscale16, format); } @@ -75,6 +75,6 @@ DicomImageInformation info(m); PixelFormat format; - ASSERT_TRUE(info.ExtractPixelFormat(format)); + ASSERT_TRUE(info.ExtractPixelFormat(format, false)); ASSERT_EQ(PixelFormat_SignedGrayscale16, format); }