# HG changeset patch # User Sebastien Jodogne # Date 1638821898 -3600 # Node ID 4273518c20093ea584509bea2f1076d0a1a789f9 # Parent 7d189530d6488d4a77911c602aa91812705821ad OrthancWSIDicomizer: Support importing of images from Cytomine diff -r 7d189530d648 -r 4273518c2009 Applications/Dicomizer.cpp --- a/Applications/Dicomizer.cpp Mon Dec 06 16:15:10 2021 +0100 +++ b/Applications/Dicomizer.cpp Mon Dec 06 21:18:18 2021 +0100 @@ -26,6 +26,7 @@ #include "../Framework/DicomizerParameters.h" #include "../Framework/ImageToolbox.h" #include "../Framework/ImagedVolumeParameters.h" +#include "../Framework/Inputs/CytomineImage.h" #include "../Framework/Inputs/HierarchicalTiff.h" #include "../Framework/Inputs/OpenSlidePyramid.h" #include "../Framework/Inputs/TiledJpegImage.h" @@ -83,6 +84,13 @@ static const char* OPTION_VERBOSE = "verbose"; static const char* OPTION_VERSION = "version"; +// New in release 1.1 +static const char* OPTION_CYTOMINE_URL = "cytomine-url"; +static const char* OPTION_CYTOMINE_IMAGE_INSTANCE_ID = "cytomine-image"; +static const char* OPTION_CYTOMINE_PUBLIC_KEY = "cytomine-public-key"; +static const char* OPTION_CYTOMINE_PRIVATE_KEY = "cytomine-private-key"; +static const char* OPTION_CYTOMINE_COMPRESSION = "cytomine-compression"; + #if ORTHANC_FRAMEWORK_VERSION_IS_ABOVE(1, 9, 0) @@ -586,6 +594,21 @@ "Color of the background (e.g. \"255,0,0\")") ; + boost::program_options::options_description cytomine("Options if importing from Cytomine"); + cytomine.add_options() + (OPTION_CYTOMINE_URL, boost::program_options::value(), + "URL of the source Cytomine server, for instance: https://demo.cytomine.be/") + (OPTION_CYTOMINE_PUBLIC_KEY, boost::program_options::value(), + "Your personal public key in Cytomine (to be kept secret)") + (OPTION_CYTOMINE_PRIVATE_KEY, boost::program_options::value(), + "Your personal private key in Cytomine (to be kept secret)") + (OPTION_CYTOMINE_IMAGE_INSTANCE_ID, boost::program_options::value(), + "ID of the Image Instance of interest in Cytomine (must be an integer)") + (OPTION_CYTOMINE_COMPRESSION, boost::program_options::value()->default_value("jpeg"), + "Compression to be used for downloading the tiles from Cytomine, " + "can be \"jpeg\" (faster) or \"png\" (better quality)") + ; + boost::program_options::options_description pyramid("Options to construct the pyramid"); pyramid.add_options() (OPTION_PYRAMID, boost::program_options::value()->default_value(false), @@ -659,6 +682,7 @@ allWithoutHidden .add(generic) .add(source) + .add(cytomine) .add(pyramid) .add(target) .add(volumeOptions) @@ -697,10 +721,70 @@ return false; } + // New in release 1.1 + if (options.count(OPTION_CYTOMINE_URL) || + options.count(OPTION_CYTOMINE_PUBLIC_KEY) || + options.count(OPTION_CYTOMINE_PRIVATE_KEY) || + options.count(OPTION_CYTOMINE_IMAGE_INSTANCE_ID)) + { + if (!options.count(OPTION_CYTOMINE_URL)) + { + throw Orthanc::OrthancException(Orthanc::ErrorCode_ParameterOutOfRange, + "URL to the Cytomine server is missing"); + } + + if (!options.count(OPTION_CYTOMINE_PUBLIC_KEY)) + { + throw Orthanc::OrthancException(Orthanc::ErrorCode_ParameterOutOfRange, + "Public key for the Cytomine server is missing"); + } + + if (!options.count(OPTION_CYTOMINE_PRIVATE_KEY)) + { + throw Orthanc::OrthancException(Orthanc::ErrorCode_ParameterOutOfRange, + "Private key for the Cytomine server is missing"); + } + + if (!options.count(OPTION_CYTOMINE_IMAGE_INSTANCE_ID)) + { + throw Orthanc::OrthancException(Orthanc::ErrorCode_ParameterOutOfRange, + "The Image Instance ID from the Cytomine server is missing"); + } + + if (!options.count(OPTION_CYTOMINE_COMPRESSION)) + { + throw Orthanc::OrthancException(Orthanc::ErrorCode_ParameterOutOfRange, + "The tile compression scheme for Cytomine is missing"); + } + + const std::string s = options[OPTION_CYTOMINE_COMPRESSION].as(); + + OrthancWSI::ImageCompression compression; + if (s == "jpeg") + { + compression = OrthancWSI::ImageCompression_Jpeg; + } + else if (s == "png") + { + compression = OrthancWSI::ImageCompression_Png; + } + else + { + throw Orthanc::OrthancException(Orthanc::ErrorCode_ParameterOutOfRange, + "The tile compression scheme must be \"jpeg\" or \"png\", found: " + s); + } + + parameters.SetCytomineSource(options[OPTION_CYTOMINE_URL].as(), + options[OPTION_CYTOMINE_PUBLIC_KEY].as(), + options[OPTION_CYTOMINE_PRIVATE_KEY].as(), + options[OPTION_CYTOMINE_IMAGE_INSTANCE_ID].as(), compression); + } + if (!error && options.count(OPTION_HELP) == 0 && options.count(OPTION_VERSION) == 0 && - options.count(OPTION_INPUT) != 1) + options.count(OPTION_INPUT) != 1 && + !parameters.IsCytomineSource()) { LOG(ERROR) << "No input file was specified"; error = true; @@ -796,7 +880,10 @@ parameters.SetTargetTileSize(w, h); } - parameters.SetInputFile(options[OPTION_INPUT].as()); + if (!parameters.IsCytomineSource()) + { + parameters.SetInputFile(options[OPTION_INPUT].as()); + } if (options.count(OPTION_COLOR)) { @@ -926,6 +1013,20 @@ const std::string& path, const OrthancWSI::DicomizerParameters& parameters) { + if (parameters.IsCytomineSource()) + { + // New in release 1.1 + LOG(WARNING) << "Importing Image Instance " << parameters.GetCytomineImageInstanceId() + << " from Cytomine server: " << parameters.GetCytomineServer().GetUrl(); + sourceCompression = OrthancWSI::ImageCompression_Unknown; + return new OrthancWSI::CytomineImage(parameters.GetCytomineServer(), + parameters.GetCytominePublicKey(), + parameters.GetCytominePrivateKey(), + parameters.GetCytomineImageInstanceId(), + parameters.GetTargetTileWidth(512), + parameters.GetTargetTileHeight(512)); + } + LOG(WARNING) << "The input image is: " << path; OrthancWSI::ImageCompression format = OrthancWSI::DetectFormatFromFile(path); diff -r 7d189530d648 -r 4273518c2009 Framework/DicomizerParameters.cpp --- a/Framework/DicomizerParameters.cpp Mon Dec 06 16:15:10 2021 +0100 +++ b/Framework/DicomizerParameters.cpp Mon Dec 06 21:18:18 2021 +0100 @@ -70,7 +70,8 @@ smooth_(false), jpegQuality_(90), forceReencode_(false), - opticalPath_(OpticalPath_Brightfield) + opticalPath_(OpticalPath_Brightfield), + isCytomineSource_(false) { backgroundColor_[0] = 255; backgroundColor_[1] = 255; @@ -276,4 +277,84 @@ return new FolderTarget(folder_ + "/" + folderPattern_); } } + + + void DicomizerParameters::SetCytomineSource(const std::string& url, + const std::string& publicKey, + const std::string& privateKey, + int imageInstanceId, + ImageCompression cytomineCompression) + { + isCytomineSource_ = true; + cytomineServer_.SetUrl(url); + cytominePublicKey_ = publicKey; + cytominePrivateKey_ = privateKey; + cytomineImageInstanceId_ = imageInstanceId; + cytomineCompression_ = cytomineCompression; + } + + + const Orthanc::WebServiceParameters& DicomizerParameters::GetCytomineServer() const + { + if (isCytomineSource_) + { + return cytomineServer_; + } + else + { + throw Orthanc::OrthancException(Orthanc::ErrorCode_BadSequenceOfCalls); + } + } + + + const std::string& DicomizerParameters::GetCytominePublicKey() const + { + if (isCytomineSource_) + { + return cytominePublicKey_; + } + else + { + throw Orthanc::OrthancException(Orthanc::ErrorCode_BadSequenceOfCalls); + } + } + + + const std::string& DicomizerParameters::GetCytominePrivateKey() const + { + if (isCytomineSource_) + { + return cytominePrivateKey_; + } + else + { + throw Orthanc::OrthancException(Orthanc::ErrorCode_BadSequenceOfCalls); + } + } + + + int DicomizerParameters::GetCytomineImageInstanceId() const + { + if (isCytomineSource_) + { + return cytomineImageInstanceId_; + } + else + { + throw Orthanc::OrthancException(Orthanc::ErrorCode_BadSequenceOfCalls); + } + } + + + ImageCompression DicomizerParameters::GetCytomineCompression() const + { + if (isCytomineSource_) + { + return cytomineCompression_; + } + else + { + throw Orthanc::OrthancException(Orthanc::ErrorCode_BadSequenceOfCalls); + } + } } diff -r 7d189530d648 -r 4273518c2009 Framework/DicomizerParameters.h --- a/Framework/DicomizerParameters.h Mon Dec 06 16:15:10 2021 +0100 +++ b/Framework/DicomizerParameters.h Mon Dec 06 21:18:18 2021 +0100 @@ -59,6 +59,14 @@ Orthanc::WebServiceParameters orthanc_; + // New in release 1.1 + bool isCytomineSource_; + Orthanc::WebServiceParameters cytomineServer_; + int cytomineImageInstanceId_; + std::string cytominePublicKey_; + std::string cytominePrivateKey_; + ImageCompression cytomineCompression_; + public: DicomizerParameters(); @@ -249,5 +257,26 @@ { return iccProfile_; } + + void SetCytomineSource(const std::string& url, + const std::string& publicKey, + const std::string& privateKey, + int imageInstanceId, + ImageCompression compression); + + bool IsCytomineSource() const + { + return isCytomineSource_; + } + + const Orthanc::WebServiceParameters& GetCytomineServer() const; + + const std::string& GetCytominePublicKey() const; + + const std::string& GetCytominePrivateKey() const; + + int GetCytomineImageInstanceId() const; + + ImageCompression GetCytomineCompression() const; }; } diff -r 7d189530d648 -r 4273518c2009 Framework/Inputs/CytomineImage.cpp --- a/Framework/Inputs/CytomineImage.cpp Mon Dec 06 16:15:10 2021 +0100 +++ b/Framework/Inputs/CytomineImage.cpp Mon Dec 06 21:18:18 2021 +0100 @@ -22,7 +22,9 @@ #include "CytomineImage.h" +#include #include +#include #include #include #include @@ -166,24 +168,61 @@ unsigned int w = std::min(tileWidth_, fullWidth_ - x); unsigned int h = std::min(tileHeight_, fullHeight_ - y); + + std::string extension; + Orthanc::MimeType mime; + + switch (compression_) + { + case ImageCompression_Png: + extension = ".png"; + mime = Orthanc::MimeType_Png; + break; + + case ImageCompression_Jpeg: + // 20.03user 0.58system 0:32.58elapsed 63%CPU (0avgtext+0avgdata 397808maxresident)k + extension = ".jpg"; + mime = Orthanc::MimeType_Jpeg; + break; + + default: + throw Orthanc::OrthancException(Orthanc::ErrorCode_NotImplemented); + } const std::string uri = ("api/imageinstance/" + boost::lexical_cast(imageId_) + "/window-" + boost::lexical_cast(x) + "-" + boost::lexical_cast(y) + "-" + boost::lexical_cast(w) + "-" + - boost::lexical_cast(h) + ".png"); + boost::lexical_cast(h) + extension); - std::string png; - if (!GetCytomine(png, uri, Orthanc::MimeType_Png)) + std::string compressedImage; + if (!GetCytomine(compressedImage, uri, mime)) { throw Orthanc::OrthancException(Orthanc::ErrorCode_NetworkProtocol, "Cannot read a tile from Cytomine"); } - Orthanc::PngReader reader; - reader.ReadFromMemory(png); + std::unique_ptr reader; - if (reader.GetWidth() != w || - reader.GetHeight() != h) + switch (compression_) + { + case ImageCompression_Png: + reader.reset(new Orthanc::PngReader); + dynamic_cast(*reader).ReadFromMemory(compressedImage); + break; + + case ImageCompression_Jpeg: + reader.reset(new Orthanc::JpegReader); + dynamic_cast(*reader).ReadFromMemory(compressedImage); + break; + + default: + throw Orthanc::OrthancException(Orthanc::ErrorCode_NotImplemented); + } + + assert(reader.get() != NULL); + + if (reader->GetWidth() != w || + reader->GetHeight() != h) { throw Orthanc::OrthancException(Orthanc::ErrorCode_NetworkProtocol, "Cytomine returned a tile of bad size"); } @@ -193,7 +232,7 @@ Orthanc::ImageAccessor region; target.GetRegion(region, 0, 0, w, h); - Orthanc::ImageProcessing::Copy(target, reader); + Orthanc::ImageProcessing::Copy(target, *reader); } @@ -208,7 +247,8 @@ privateKey_(privateKey), imageId_(imageId), tileWidth_(tileWidth), - tileHeight_(tileHeight) + tileHeight_(tileHeight), + compression_(ImageCompression_Jpeg) { if (tileWidth_ < 16 || tileHeight_ < 16) @@ -268,4 +308,18 @@ throw Orthanc::OrthancException(Orthanc::ErrorCode_ParameterOutOfRange); } } + + + void CytomineImage::SetImageCompression(ImageCompression compression) + { + if (compression == ImageCompression_Jpeg || + compression == ImageCompression_Png) + { + compression_ = compression; + } + else + { + throw Orthanc::OrthancException(Orthanc::ErrorCode_ParameterOutOfRange); + } + } } diff -r 7d189530d648 -r 4273518c2009 Framework/Inputs/CytomineImage.h --- a/Framework/Inputs/CytomineImage.h Mon Dec 06 16:15:10 2021 +0100 +++ b/Framework/Inputs/CytomineImage.h Mon Dec 06 21:18:18 2021 +0100 @@ -39,6 +39,7 @@ unsigned int fullHeight_; unsigned int tileWidth_; unsigned int tileHeight_; + ImageCompression compression_; bool GetCytomine(std::string& target, const std::string& uri, @@ -86,5 +87,7 @@ { return Orthanc::PhotometricInterpretation_RGB; } + + void SetImageCompression(ImageCompression compression); }; } diff -r 7d189530d648 -r 4273518c2009 NEWS --- a/NEWS Mon Dec 06 16:15:10 2021 +0100 +++ b/NEWS Mon Dec 06 21:18:18 2021 +0100 @@ -1,6 +1,8 @@ Pending changes in the mainline =============================== +* OrthancWSIDicomizer: Support importing of images from Cytomine + Version 1.0 (2021-01-14) ========================