# HG changeset patch # User Sebastien Jodogne # Date 1589830651 -7200 # Node ID b26d25d3c1c71d354e63761200cfc5d0246e8386 # Parent 5b882ad2ffd049c282bf8edbb9b4b4d58c8671fe "/{patients|studies|series}/.../modify": New option "Transcode" diff -r 5b882ad2ffd0 -r b26d25d3c1c7 NEWS --- a/NEWS Mon May 18 19:09:06 2020 +0200 +++ b/NEWS Mon May 18 21:37:31 2020 +0200 @@ -23,7 +23,7 @@ (defaults to the local AET) * Changes: - "/{patients|studies|series}/.../modify": New option "KeepSource" - - "/instances/.../modify": New option "Transcode" + - "/{patients|studies|series|instances}/.../modify": New option "Transcode" - ".../archive", ".../media", "/tools/create-media" and "/tools/create-archive": New option "Transcode" - "/ordered-slices": reverted the change introduced in 1.5.8 and go-back diff -r 5b882ad2ffd0 -r b26d25d3c1c7 OrthancServer/OrthancRestApi/OrthancRestAnonymizeModify.cpp --- a/OrthancServer/OrthancRestApi/OrthancRestAnonymizeModify.cpp Mon May 18 19:09:06 2020 +0200 +++ b/OrthancServer/OrthancRestApi/OrthancRestAnonymizeModify.cpp Mon May 18 21:37:31 2020 +0200 @@ -177,9 +177,10 @@ modification.SetLevel(ResourceType_Instance); } - if (request.isMember("Transcode")) + static const char* TRANSCODE = "Transcode"; + if (request.isMember(TRANSCODE)) { - std::string s = SerializationToolbox::ReadString(request, "Transcode"); + std::string s = SerializationToolbox::ReadString(request, TRANSCODE); DicomTransferSyntax syntax; if (LookupTransferSyntax(syntax, s)) @@ -236,6 +237,12 @@ job->SetModification(modification.release(), level, isAnonymization); job->SetOrigin(call); SetKeepSource(*job, body); + + static const char* TRANSCODE = "Transcode"; + if (body.isMember(TRANSCODE)) + { + job->SetTranscode(SerializationToolbox::ReadString(body, TRANSCODE)); + } context.AddChildInstances(*job, call.GetUriComponent("id", "")); job->AddTrailingStep(); diff -r 5b882ad2ffd0 -r b26d25d3c1c7 OrthancServer/ServerJobs/ResourceModificationJob.cpp --- a/OrthancServer/ServerJobs/ResourceModificationJob.cpp Mon May 18 19:09:06 2020 +0200 +++ b/OrthancServer/ServerJobs/ResourceModificationJob.cpp Mon May 18 21:37:31 2020 +0200 @@ -38,6 +38,8 @@ #include "../../Core/SerializationToolbox.h" #include "../ServerContext.h" +#include + namespace Orthanc { class ResourceModificationJob::Output : public boost::noncopyable @@ -171,6 +173,26 @@ modification_->Apply(*modified); + if (transcode_) + { + std::set syntaxes; + syntaxes.insert(transferSyntax_); + + std::string s; + modified->SaveToMemoryBuffer(s); // TODO - AVOID THIS SERIALIZATION IF NO PLUGIN + + std::unique_ptr transcoded; + transcoded.reset(GetContext().TranscodeToParsed(modified->GetDcmtkObject(), s.empty() ? NULL : s.c_str(), s.size(), syntaxes, true)); + if (transcoded.get() == NULL) + { + LOG(WARNING) << "Cannot transcode instance, keeping original transfer syntax: " << instance; + } + else + { + modified.reset(ParsedDicomFile::AcquireDcmtkObject(transcoded->ReleaseDicom())); + } + } + DicomInstanceToStore toStore; toStore.SetOrigin(origin_); toStore.SetParsedDicomFile(*modified); @@ -231,6 +253,15 @@ } + ResourceModificationJob::ResourceModificationJob(ServerContext& context) : + CleaningInstancesJob(context, true /* by default, keep source */), + modification_(new DicomModification), + isAnonymization_(false), + transcode_(false) + { + } + + void ResourceModificationJob::SetModification(DicomModification* modification, ResourceType level, bool isAnonymization) @@ -284,6 +315,61 @@ } + DicomTransferSyntax ResourceModificationJob::GetTransferSyntax() const + { + if (transcode_) + { + return transferSyntax_; + } + else + { + throw OrthancException(ErrorCode_BadSequenceOfCalls); + } + } + + + void ResourceModificationJob::SetTranscode(DicomTransferSyntax syntax) + { + if (IsStarted()) + { + throw OrthancException(ErrorCode_BadSequenceOfCalls); + } + else + { + transcode_ = true; + transferSyntax_ = syntax; + } + } + + + void ResourceModificationJob::SetTranscode(const std::string& transferSyntaxUid) + { + DicomTransferSyntax s; + if (LookupTransferSyntax(s, transferSyntaxUid)) + { + SetTranscode(s); + } + else + { + throw OrthancException(ErrorCode_BadFileFormat, + "Unknown transfer syntax UID: " + transferSyntaxUid); + } + } + + + void ResourceModificationJob::ClearTranscode() + { + if (IsStarted()) + { + throw OrthancException(ErrorCode_BadSequenceOfCalls); + } + else + { + transcode_ = false; + } + } + + void ResourceModificationJob::GetPublicContent(Json::Value& value) { CleaningInstancesJob::GetPublicContent(value); @@ -294,21 +380,38 @@ { output_->Format(value); } + + if (transcode_) + { + value["Transcode"] = GetTransferSyntaxUid(transferSyntax_); + } } static const char* MODIFICATION = "Modification"; static const char* ORIGIN = "Origin"; static const char* IS_ANONYMIZATION = "IsAnonymization"; + static const char* TRANSCODE = "Transcode"; ResourceModificationJob::ResourceModificationJob(ServerContext& context, const Json::Value& serialized) : CleaningInstancesJob(context, serialized, true /* by default, keep source */) { + assert(serialized.type() == Json::objectValue); + isAnonymization_ = SerializationToolbox::ReadBoolean(serialized, IS_ANONYMIZATION); origin_ = DicomInstanceOrigin(serialized[ORIGIN]); modification_.reset(new DicomModification(serialized[MODIFICATION])); + + if (serialized.isMember(TRANSCODE)) + { + SetTranscode(SerializationToolbox::ReadString(serialized, TRANSCODE)); + } + else + { + transcode_ = false; + } } bool ResourceModificationJob::Serialize(Json::Value& value) @@ -319,7 +422,15 @@ } else { + assert(value.type() == Json::objectValue); + value[IS_ANONYMIZATION] = isAnonymization_; + + if (transcode_) + { + value[TRANSCODE] = GetTransferSyntaxUid(transferSyntax_); + } + origin_.Serialize(value[ORIGIN]); Json::Value tmp; diff -r 5b882ad2ffd0 -r b26d25d3c1c7 OrthancServer/ServerJobs/ResourceModificationJob.h --- a/OrthancServer/ServerJobs/ResourceModificationJob.h Mon May 18 19:09:06 2020 +0200 +++ b/OrthancServer/ServerJobs/ResourceModificationJob.h Mon May 18 21:37:31 2020 +0200 @@ -50,16 +50,14 @@ boost::shared_ptr output_; bool isAnonymization_; DicomInstanceOrigin origin_; + bool transcode_; + DicomTransferSyntax transferSyntax_; protected: virtual bool HandleInstance(const std::string& instance); public: - ResourceModificationJob(ServerContext& context) : - CleaningInstancesJob(context, true /* by default, keep source */), - isAnonymization_(false) - { - } + ResourceModificationJob(ServerContext& context); ResourceModificationJob(ServerContext& context, const Json::Value& serialized); @@ -84,6 +82,19 @@ return origin_; } + bool IsTranscode() const + { + return transcode_; + } + + DicomTransferSyntax GetTransferSyntax() const; + + void SetTranscode(DicomTransferSyntax syntax); + + void SetTranscode(const std::string& transferSyntaxUid); + + void ClearTranscode(); + virtual void Stop(JobStopReason reason) { } diff -r 5b882ad2ffd0 -r b26d25d3c1c7 UnitTestsSources/MultiThreadingTests.cpp --- a/UnitTestsSources/MultiThreadingTests.cpp Mon May 18 19:09:06 2020 +0200 +++ b/UnitTestsSources/MultiThreadingTests.cpp Mon May 18 21:37:31 2020 +0200 @@ -1572,10 +1572,32 @@ ResourceModificationJob& tmp = dynamic_cast(*job); ASSERT_TRUE(tmp.IsAnonymization()); + ASSERT_FALSE(tmp.IsTranscode()); + ASSERT_THROW(tmp.GetTransferSyntax(), OrthancException); ASSERT_EQ(RequestOrigin_Lua, tmp.GetOrigin().GetRequestOrigin()); ASSERT_TRUE(tmp.GetModification().IsRemoved(DICOM_TAG_STUDY_DESCRIPTION)); } + { + ResourceModificationJob job(GetContext()); + job.SetTranscode(DicomTransferSyntax_JPEGProcess1); + + job.AddTrailingStep(); // Necessary since 1.7.0 + ASSERT_TRUE(CheckIdempotentSetOfInstances(unserializer, job)); + ASSERT_TRUE(job.Serialize(s)); + } + + { + std::unique_ptr job; + job.reset(unserializer.UnserializeJob(s)); + + ResourceModificationJob& tmp = dynamic_cast(*job); + ASSERT_FALSE(tmp.IsAnonymization()); + ASSERT_TRUE(tmp.IsTranscode()); + ASSERT_EQ(DicomTransferSyntax_JPEGProcess1, tmp.GetTransferSyntax()); + ASSERT_EQ(RequestOrigin_Unknown, tmp.GetOrigin().GetRequestOrigin()); + } + // SplitStudyJob std::string instance;