Mercurial > hg > orthanc-wsi
changeset 388:a7194a77c591
added support for JPEG-LS in OrthancWSIDicomizer
author | Sebastien Jodogne <s.jodogne@gmail.com> |
---|---|
date | Tue, 08 Apr 2025 13:11:27 +0200 (6 weeks ago) |
parents | 263369b75f9e |
children | 7954645ed874 |
files | Applications/ApplicationToolbox.cpp Applications/CMakeLists.txt Applications/DicomToTiff.cpp Applications/Dicomizer.cpp Framework/Enumerations.cpp Framework/Enumerations.h Framework/ImageToolbox.cpp Framework/ImageToolbox.h Framework/Outputs/MultiframeDicomWriter.cpp NEWS |
diffstat | 10 files changed, 117 insertions(+), 5 deletions(-) [+] |
line wrap: on
line diff
--- a/Applications/ApplicationToolbox.cpp Tue Apr 08 10:48:07 2025 +0200 +++ b/Applications/ApplicationToolbox.cpp Tue Apr 08 13:11:27 2025 +0200 @@ -71,11 +71,19 @@ Orthanc::HttpClient::GlobalInitialize(); Orthanc::FromDcmtkBridge::InitializeDictionary(false /* don't load private dictionary */); assert(DisplayPerformanceWarning()); + +#if ORTHANC_ENABLE_DCMTK_TRANSCODING == 1 + Orthanc::FromDcmtkBridge::InitializeCodecs(); +#endif } void GlobalFinalize() { +#if ORTHANC_ENABLE_DCMTK_TRANSCODING == 1 + Orthanc::FromDcmtkBridge::FinalizeCodecs(); +#endif + OrthancWSI::OpenSlideLibrary::Finalize(); Orthanc::HttpClient::GlobalFinalize(); Orthanc::Toolbox::FinalizeOpenSsl();
--- a/Applications/CMakeLists.txt Tue Apr 08 10:48:07 2025 +0200 +++ b/Applications/CMakeLists.txt Tue Apr 08 13:11:27 2025 +0200 @@ -75,7 +75,8 @@ SET(ENABLE_CRYPTO_OPTIONS ON) SET(ENABLE_DCMTK ON) SET(ENABLE_DCMTK_JPEG OFF) # Disable DCMTK's support for JPEG, that clashes with libtiff - SET(ENABLE_DCMTK_JPEG_LOSSLESS OFF) # Disable DCMTK's support for JPEG-LS + SET(ENABLE_DCMTK_JPEG_LOSSLESS ON) # Enable DCMTK's support for JPEG-LS (was disabled in WSI <= 3.1) + SET(ENABLE_DCMTK_TRANSCODING ON) # Enable DCMTK's support for transcoding (was disabled in WSI <= 3.1) SET(ENABLE_DCMTK_NETWORKING OFF) # Disable DCMTK's support for DICOM networking SET(ENABLE_JPEG ON) SET(ENABLE_LOCALE ON) # Enable support for locales (notably in Boost) @@ -89,9 +90,19 @@ include(${ORTHANC_FRAMEWORK_ROOT}/../Resources/CMake/OrthancFrameworkConfiguration.cmake) include(${ORTHANC_WSI_DIR}/Resources/CMake/BoostExtendedConfiguration.cmake) + + if (STATIC_BUILD OR NOT USE_SYSTEM_DCMTK) + # This is a backport of changeset: https://orthanc.uclouvain.be/hg/orthanc/rev/97d69fb5958e + # TODO - Remove after next upgrade to Orthanc framework > 1.12.7 + include_directories( + ${DCMTK_SOURCES_DIR}/dcmimgle/include + ) + endif() endif() + + # Include components specific to WSI include(${ORTHANC_WSI_DIR}/Resources/CMake/OpenJpegConfiguration.cmake) include(${ORTHANC_WSI_DIR}/Resources/CMake/LibTiffConfiguration.cmake)
--- a/Applications/DicomToTiff.cpp Tue Apr 08 10:48:07 2025 +0200 +++ b/Applications/DicomToTiff.cpp Tue Apr 08 13:11:27 2025 +0200 @@ -333,7 +333,7 @@ } catch (Orthanc::OrthancException& e) { - LOG(ERROR) << "Terminating on exception: " << e.What(); + LOG(ERROR) << "Terminating on exception: " << e.What() << ": " << e.GetDetails();; if (options.count(OPTION_REENCODE) == 0) {
--- a/Applications/Dicomizer.cpp Tue Apr 08 10:48:07 2025 +0200 +++ b/Applications/Dicomizer.cpp Tue Apr 08 13:11:27 2025 +0200 @@ -260,6 +260,10 @@ targetPhotometric = Orthanc::PhotometricInterpretation_RGB; break; + case OrthancWSI::ImageCompression_JpegLS: + targetPhotometric = Orthanc::PhotometricInterpretation_RGB; + break; + default: throw Orthanc::OrthancException(Orthanc::ErrorCode_InternalError); } @@ -674,7 +678,7 @@ (OPTION_TILE_HEIGHT, boost::program_options::value<int>(), "Height of the tiles in the target image") (OPTION_COMPRESSION, boost::program_options::value<std::string>(), - "Compression of the target image (\"none\", \"jpeg\" or \"jpeg2000\")") + "Compression of the target image (\"none\", \"jpeg\", \"jpeg2000\", or \"jpeg-ls\")") (OPTION_JPEG_QUALITY, boost::program_options::value<int>(), "Set quality level for JPEG (0..100)") (OPTION_MAX_SIZE, boost::program_options::value<int>()->default_value(10), @@ -960,6 +964,10 @@ { parameters.SetTargetCompression(OrthancWSI::ImageCompression_Jpeg2000); } + else if (s == "jpeg-ls") + { + parameters.SetTargetCompression(OrthancWSI::ImageCompression_JpegLS); + } else { LOG(ERROR) << "Unknown image compression for the target image: " << s; @@ -1297,7 +1305,7 @@ } catch (Orthanc::OrthancException& e) { - LOG(ERROR) << "Terminating on exception: " << e.What(); + LOG(ERROR) << "Terminating on exception: " << e.What() << ": " << e.GetDetails(); exitStatus = -1; }
--- a/Framework/Enumerations.cpp Tue Apr 08 10:48:07 2025 +0200 +++ b/Framework/Enumerations.cpp Tue Apr 08 13:11:27 2025 +0200 @@ -63,6 +63,9 @@ case ImageCompression_Dicom: return "DICOM"; + case ImageCompression_JpegLS: + return "JPEG-LS"; + default: throw Orthanc::OrthancException(Orthanc::ErrorCode_ParameterOutOfRange); }
--- a/Framework/Enumerations.h Tue Apr 08 10:48:07 2025 +0200 +++ b/Framework/Enumerations.h Tue Apr 08 13:11:27 2025 +0200 @@ -43,7 +43,8 @@ ImageCompression_Jpeg = 5, ImageCompression_Jpeg2000 = 6, ImageCompression_Tiff = 7, - ImageCompression_UseOrthancPreview = 8 + ImageCompression_UseOrthancPreview = 8, + ImageCompression_JpegLS = 9 }; enum OpticalPath
--- a/Framework/ImageToolbox.cpp Tue Apr 08 10:48:07 2025 +0200 +++ b/Framework/ImageToolbox.cpp Tue Apr 08 13:11:27 2025 +0200 @@ -27,6 +27,13 @@ #include "Jpeg2000Reader.h" #include "Jpeg2000Writer.h" +#if ORTHANC_ENABLE_DCMTK_TRANSCODING == 1 +# include <DicomParsing/DcmtkTranscoder.h> +# include <DicomParsing/ParsedDicomFile.h> +# include <DicomParsing/FromDcmtkBridge.h> +# include <dcmtk/dcmdata/dcpxitem.h> +#endif + #include <Compatibility.h> // For std::unique_ptr #include <OrthancException.h> #include <Images/ImageProcessing.h> @@ -185,6 +192,14 @@ memcpy(&target[i * pitch], source.GetConstRow(i), pitch); } } + else if (compression == ImageCompression_JpegLS) + { +#if (ORTHANC_ENABLE_DCMTK_TRANSCODING == 1) && (ORTHANC_ENABLE_DCMTK_JPEG_LOSSLESS == 1) + CompressFrameUsingDcmtk(target, source, Orthanc::DicomTransferSyntax_JPEGLSLossless); +#else + throw Orthanc::OrthancException(Orthanc::ErrorCode_InternalError, "DCMTK was compiled without support for JPEG-LS"); +#endif + } else { std::unique_ptr<Orthanc::IImageWriter> writer; @@ -405,5 +420,59 @@ p[3] == 0xe1); } } + + +#if ORTHANC_ENABLE_DCMTK_TRANSCODING == 1 + void CompressFrameUsingDcmtk(std::string& target, + const Orthanc::ImageAccessor& frame, + Orthanc::DicomTransferSyntax syntax) + { + if (frame.GetFormat() != Orthanc::PixelFormat_RGB24) + { + throw Orthanc::OrthancException(Orthanc::ErrorCode_NotImplemented); + } + + Orthanc::ParsedDicomFile dicom(true); + dicom.EmbedImage(frame); + + Orthanc::IDicomTranscoder::DicomImage source; + source.AcquireParsed(dicom); // "dicom" is invalid below this point + + Orthanc::IDicomTranscoder::DicomImage transcoded; + + std::set<Orthanc::DicomTransferSyntax> s; + s.insert(syntax); + + Orthanc::DcmtkTranscoder transcoder(1); + + if (transcoder.Transcode(transcoded, source, s, true)) + { + DcmPixelSequence* pixelSequence = Orthanc::FromDcmtkBridge::GetPixelSequence(*transcoded.GetParsed().getDataset()); + + DcmPixelItem* compressed = NULL; + Uint8* data = NULL; + if (pixelSequence != NULL && + pixelSequence->card() == 2 && + pixelSequence->getItem(compressed, 1).good() && + compressed != NULL && + compressed->getUint8Array(data).good() && + data != NULL) + { + const unsigned int size = compressed->getNumberOfValues(); + + target.resize(size); + if (size != 0) + { + memcpy(&target[0], data, size); + } + + return; // Success + } + } + + throw Orthanc::OrthancException(Orthanc::ErrorCode_InternalError, "DCMTK cannot transcode to " + + std::string(Orthanc::GetTransferSyntaxUid(syntax))); + } +#endif } }
--- a/Framework/ImageToolbox.h Tue Apr 08 10:48:07 2025 +0200 +++ b/Framework/ImageToolbox.h Tue Apr 08 13:11:27 2025 +0200 @@ -81,5 +81,11 @@ bool HasPngSignature(const std::string& buffer); bool HasJpegSignature(const std::string& buffer); + +#if ORTHANC_ENABLE_DCMTK_TRANSCODING == 1 + void CompressFrameUsingDcmtk(std::string& target, + const Orthanc::ImageAccessor& frame, + Orthanc::DicomTransferSyntax syntax); +#endif } }
--- a/Framework/Outputs/MultiframeDicomWriter.cpp Tue Apr 08 10:48:07 2025 +0200 +++ b/Framework/Outputs/MultiframeDicomWriter.cpp Tue Apr 08 13:11:27 2025 +0200 @@ -182,6 +182,10 @@ transferSyntax_ = EXS_JPEG2000LosslessOnly; break; + case ImageCompression_JpegLS: + transferSyntax_ = EXS_JPEGLSLossless; + break; + default: throw Orthanc::OrthancException(Orthanc::ErrorCode_ParameterOutOfRange); } @@ -321,6 +325,7 @@ case ImageCompression_Jpeg: case ImageCompression_Jpeg2000: + case ImageCompression_JpegLS: offsetTable_->createOffsetTable(*offsetList_); dicom->getDataset()->insert(compressedPixelSequence_.release()); break;
--- a/NEWS Tue Apr 08 10:48:07 2025 +0200 +++ b/NEWS Tue Apr 08 13:11:27 2025 +0200 @@ -4,6 +4,7 @@ * Support windowing when rendering grayscale images using on-the-fly deep zoom * Added tolerance to imaged volume width/height by looking only at the finest level * Added support for more transfer syntaxes in the Web viewer plugin, including JPEG-LS +* Added support for JPEG-LS in OrthancWSIDicomizer with argument "--compression=jpeg-ls" * Fix photometric interpretation of JPEG2000 images generated by OrthancWSIDicomizer (now set to RGB instead of YBR_FULL_422)