Mercurial > hg > orthanc
changeset 5311:79fa77e9fa0d am-experimental
merge default -> am-experimental
author | Alain Mazy <am@osimis.io> |
---|---|
date | Fri, 16 Jun 2023 09:26:33 +0200 |
parents | 9504de20d43d (diff) b5c502bcaf99 (current diff) |
children | |
files | NEWS OrthancFramework/Sources/DicomParsing/FromDcmtkBridge.cpp |
diffstat | 18 files changed, 426 insertions(+), 83 deletions(-) [+] |
line wrap: on
line diff
--- a/NEWS Mon Jun 12 18:42:06 2023 +0200 +++ b/NEWS Fri Jun 16 09:26:33 2023 +0200 @@ -5,6 +5,7 @@ -------- * API version upgraded to 21 +* New URI /instances/{id}/file-until-pixel-data * added a route to delete the output of an asynchronous job (right now only for archive jobs): e.g. DELETE /jobs/../archive @@ -26,6 +27,16 @@ This might have an impact on the image returned by /dicom-web/studies/../series/../instances/../frames/1; the image format is now consistent with the PhotometricIntepretation DICOM Tag. +* WIP: new dicomWeb Json format for some of the Rest API routes. +* WIP: new 'include' get arguments for some of the Rest API routes to define + the content of the response. Useful since the dicomWeb format is very slow + to serialize. + +* New functions in the SDK: + - OrthancPluginEncodeDicomWebJson3() + TODO: Xml version + + Version 1.12.0 (2023-04-14) ===========================
--- a/OrthancFramework/Sources/DicomParsing/FromDcmtkBridge.cpp Mon Jun 12 18:42:06 2023 +0200 +++ b/OrthancFramework/Sources/DicomParsing/FromDcmtkBridge.cpp Fri Jun 16 09:26:33 2023 +0200 @@ -41,6 +41,9 @@ #include "../Logging.h" #include "../Toolbox.h" #include "../OrthancException.h" +#include "DicomWebJsonVisitor.h" +#include "ParsedDicomFile.h" +#include <boost/date_time/posix_time/posix_time.hpp> #if ORTHANC_SANDBOXED == 0 # include "../TemporaryFile.h" @@ -1444,6 +1447,16 @@ result.clear(); + if (format == DicomToJsonFormat_DicomWeb) + { + DicomWebJsonVisitor visitor; + + ParsedDicomFile dicom(values, Encoding_Utf8, true); + dicom.Apply(visitor); + result = visitor.GetResult(); + return; + } + for (DicomMap::Content::const_iterator it = values.content_.begin(); it != values.content_.end(); ++it) {
--- a/OrthancFramework/Sources/DicomParsing/ParsedDicomFile.cpp Mon Jun 12 18:42:06 2023 +0200 +++ b/OrthancFramework/Sources/DicomParsing/ParsedDicomFile.cpp Fri Jun 16 09:26:33 2023 +0200 @@ -1755,8 +1755,19 @@ } - void ParsedDicomFile::Apply(ITagVisitor& visitor) const + void ParsedDicomFile::Apply(ITagVisitor& visitor, bool injectEmptyPixelData) const { + DcmItem& dataset = *GetDcmtkObjectConst().getDataset(); + + if (injectEmptyPixelData) + { + DcmTag emptyPixelData(DCM_PixelData, EVR_PixelData); + if (!dataset.insertEmptyElement(emptyPixelData, false).good()) + { + throw OrthancException(ErrorCode_InternalError); + } + } + FromDcmtkBridge::Apply(*GetDcmtkObjectConst().getDataset(), visitor, GetDefaultDicomEncoding()); }
--- a/OrthancFramework/Sources/DicomParsing/ParsedDicomFile.h Mon Jun 12 18:42:06 2023 +0200 +++ b/OrthancFramework/Sources/DicomParsing/ParsedDicomFile.h Fri Jun 16 09:26:33 2023 +0200 @@ -273,7 +273,7 @@ bool LookupPhotometricInterpretation(PhotometricInterpretation& result) const; - void Apply(ITagVisitor& visitor) const; + void Apply(ITagVisitor& visitor, bool injectEmptyPixelData = false) const; // Decode the given frame, using the built-in DICOM decoder of Orthanc ImageAccessor* DecodeFrame(unsigned int frame) const;
--- a/OrthancFramework/Sources/Enumerations.cpp Mon Jun 12 18:42:06 2023 +0200 +++ b/OrthancFramework/Sources/Enumerations.cpp Fri Jun 16 09:26:33 2023 +0200 @@ -1184,6 +1184,9 @@ case DicomToJsonFormat_Short: return "Short"; + case DicomToJsonFormat_DicomWeb: + return "DicomWeb"; + default: throw OrthancException(ErrorCode_ParameterOutOfRange); } @@ -1885,6 +1888,10 @@ { return DicomToJsonFormat_Human; } + else if (format == "DicomWeb") + { + return DicomToJsonFormat_DicomWeb; + } else { throw OrthancException(ErrorCode_ParameterOutOfRange);
--- a/OrthancFramework/Sources/Enumerations.h Mon Jun 12 18:42:06 2023 +0200 +++ b/OrthancFramework/Sources/Enumerations.h Fri Jun 16 09:26:33 2023 +0200 @@ -581,7 +581,8 @@ { DicomToJsonFormat_Full, DicomToJsonFormat_Short, - DicomToJsonFormat_Human + DicomToJsonFormat_Human, + DicomToJsonFormat_DicomWeb }; enum DicomToJsonFlags
--- a/OrthancFramework/UnitTestsSources/FromDcmtkTests.cpp Mon Jun 12 18:42:06 2023 +0200 +++ b/OrthancFramework/UnitTestsSources/FromDcmtkTests.cpp Fri Jun 16 09:26:33 2023 +0200 @@ -3220,7 +3220,129 @@ m.FromDicomWeb(v); } - +TEST(DicomMap, MainDicomTagsDicomWebJson) +{ + std::unique_ptr<ParsedDicomFile> dicom; + + { + std::string source = "{" + "\"0008,0005\" : {" + "\"Name\" : \"SpecificCharacterSet\"," + "\"Type\" : \"String\"," + "\"Value\" : \"ISO_IR 192\"" + "}," + "\"0008,0008\" : {" + "\"Name\" : \"ImageType\"," + "\"Type\" : \"String\"," + "\"Value\" : \"DERIVED\\\\PRIMARY\\\\AXIAL\"" + "}," + "\"0008,0016\" : {" + "\"Name\" : \"SOPClassUID\"," + "\"Type\" : \"String\"," + "\"Value\" : \"1.2.840.10008.5.1.4.1.1.2\"" + "}," + "\"0008,0018\" : {" + "\"Name\" : \"SOPInstanceUID\"," + "\"Type\" : \"String\"," + "\"Value\" : \"1.2.276.0.7230010.3.1.4.1215942821.4756.1664833117.11987\"" + "}," + "\"0018,0050\" : {" + "\"Name\" : \"SliceThickness\"," + "\"Type\" : \"String\"," + "\"Value\" : \"0.19816064757161\"" + "}," + "\"0020,0013\" : {" + "\"Name\" : \"InstanceNumber\"," + "\"Type\" : \"String\"," + "\"Value\" : \"0\"" + "}," + "\"0020,0032\" : {" + "\"Name\" : \"ImagePositionPatient\"," + "\"Type\" : \"String\"," + "\"Value\" : \"-79.462419676214\\\\-79.462419676214\\\\-59.150953300125\"" + "}," + "\"0020,0037\" : {" + "\"Name\" : \"ImageOrientationPatient\"," + "\"Type\" : \"String\"," + "\"Value\" : \"1\\\\-0\\\\0\\\\-0\\\\1\\\\-0\"" + "}," + "\"0028,0004\" : {" + "\"Name\" : \"PhotometricInterpretation\"," + "\"Type\" : \"String\"," + "\"Value\" : \"MONOCHROME2\"" + "}," + "\"0028,0010\" : {" + "\"Name\" : \"Rows\"," + "\"Type\" : \"String\"," + "\"Value\" : \"803\"" + "}," + "\"0028,0011\" : {" + "\"Name\" : \"Columns\"," + "\"Type\" : \"String\"," + "\"Value\" : \"803\"" + "}," + "\"0028,0030\" : {" + "\"Name\" : \"PixelSpacing\"," + "\"Type\" : \"String\"," + "\"Value\" : \"0.19816064757161\\\\0.19816064757161\"" + "}," + "\"0028,0100\" : {" + "\"Name\" : \"BitsAllocated\"," + "\"Type\" : \"String\"," + "\"Value\" : \"16\"" + "}," + "\"0028,0101\" : {" + "\"Name\" : \"BitsStored\"," + "\"Type\" : \"String\"," + "\"Value\" : \"16\"" + "}," + "\"0028,0103\" : {" + "\"Name\" : \"PixelRepresentation\"," + "\"Type\" : \"String\"," + "\"Value\" : \"0\"" + "}," + "\"0028,1050\" : {" + "\"Name\" : \"WindowCenter\"," + "\"Type\" : \"String\"," + "\"Value\" : \"1023.96875\"" + "}," + "\"0028,1051\" : {" + "\"Name\" : \"WindowWidth\"," + "\"Type\" : \"String\"," + "\"Value\" : \"4095.9375\"" + "}," + "\"0028,1052\" : {" + "\"Name\" : \"RescaleIntercept\"," + "\"Type\" : \"String\"," + "\"Value\" : \"-1024\"" + "}," + "\"0028,1053\" : {" + "\"Name\" : \"RescaleSlope\"," + "\"Type\" : \"String\"," + "\"Value\" : \"0.0625\"" + "}" + "}"; + + Json::Value v; + Orthanc::Toolbox::ReadJson(v, source); + +// LOG(INFO) << source; +// LOG(INFO) << v.toStyledString(); + Json::Value result = Json::objectValue; + DicomMap map; + map.FromDicomAsJson(v, false, true); + +// for profiling for (int i=0; i < 100; ++i) + { + result = Json::objectValue; + FromDcmtkBridge::ToJson(result, map, Orthanc::DicomToJsonFormat_DicomWeb); + } + + LOG(INFO) << result.toStyledString(); + ASSERT_TRUE(result.isMember("00281053")); + ASSERT_DOUBLE_EQ(0.0625, result["00281053"]["Value"][0].asDouble()); + } +} #if ORTHANC_ENABLE_DCMTK_TRANSCODING == 1
--- a/OrthancServer/Plugins/Engine/OrthancPlugins.cpp Mon Jun 12 18:42:06 2023 +0200 +++ b/OrthancServer/Plugins/Engine/OrthancPlugins.cpp Fri Jun 16 09:26:33 2023 +0200 @@ -1052,12 +1052,13 @@ void Apply(char** target, bool isJson, - const ParsedDicomFile& dicom) + const ParsedDicomFile& dicom, + bool injectEmptyPixelData) { DicomWebJsonVisitor visitor; visitor.SetFormatter(*this); - dicom.Apply(visitor); + dicom.Apply(visitor, injectEmptyPixelData); std::string s; @@ -1077,10 +1078,11 @@ void Apply(char** target, bool isJson, const void* dicom, - size_t dicomSize) + size_t dicomSize, + bool injectEmptyPixelData) { ParsedDicomFile parsed(dicom, dicomSize); - Apply(target, isJson, parsed); + Apply(target, isJson, parsed, injectEmptyPixelData); } }; } @@ -3636,7 +3638,8 @@ DicomWebBinaryFormatter formatter(p.dicomWebCallback, p.dicomWebPayload); formatter.Apply(p.targetStringToFree, (service == _OrthancPluginService_GetInstanceDicomWebJson), - instance.GetParsedDicomFile()); + instance.GetParsedDicomFile(), + false); return; } @@ -5195,7 +5198,7 @@ DicomWebBinaryFormatter formatter(p.callback); formatter.Apply(p.target, (service == _OrthancPluginService_EncodeDicomWebJson), - p.dicom, p.dicomSize); + p.dicom, p.dicomSize, false); return true; } @@ -5208,7 +5211,19 @@ DicomWebBinaryFormatter formatter(p.callback, p.payload); formatter.Apply(p.target, (service == _OrthancPluginService_EncodeDicomWebJson2), - p.dicom, p.dicomSize); + p.dicom, p.dicomSize, false); + return true; + } + + case _OrthancPluginService_EncodeDicomWebJson3: + { + const _OrthancPluginEncodeDicomWeb3& p = + *reinterpret_cast<const _OrthancPluginEncodeDicomWeb3*>(parameters); + + DicomWebBinaryFormatter formatter(p.callback, p.payload); + formatter.Apply(p.target, + (service == _OrthancPluginService_EncodeDicomWebJson3), + p.dicom, p.dicomSize, p.injectEmptyPixelData); return true; }
--- a/OrthancServer/Plugins/Include/orthanc/OrthancCPlugin.h Mon Jun 12 18:42:06 2023 +0200 +++ b/OrthancServer/Plugins/Include/orthanc/OrthancCPlugin.h Fri Jun 16 09:26:33 2023 +0200 @@ -447,6 +447,7 @@ _OrthancPluginService_CreateMemoryBuffer64 = 40, /* New in Orthanc 1.9.0 */ _OrthancPluginService_CreateDicom2 = 41, /* New in Orthanc 1.9.0 */ _OrthancPluginService_GetDatabaseServerIdentifier = 42, /* New in Orthanc 1.11.1 */ + _OrthancPluginService_EncodeDicomWebJson3 = 43, /* New in Orthanc 1.12.1 */ /* Registration of callbacks */ _OrthancPluginService_RegisterRestCallback = 1000, @@ -7256,6 +7257,62 @@ } + typedef struct + { + char** target; + const void* dicom; + uint32_t dicomSize; + OrthancPluginDicomWebBinaryCallback2 callback; + void* payload; + bool injectEmptyPixelData; + } _OrthancPluginEncodeDicomWeb3; + + /** + * @brief Convert a DICOM instance to DICOMweb JSON. + * + * This function converts a memory buffer containing a DICOM instance, + * into its DICOMweb JSON representation. + * + * @param context The Orthanc plugin context, as received by OrthancPluginInitialize(). + * @param dicom Pointer to the DICOM instance. + * @param dicomSize Size of the DICOM instance. + * @param callback Callback to set the value of the binary tags. + * @param payload User payload. + * @return The NULL value in case of error, or the JSON document. This string must + * be freed by OrthancPluginFreeString(). + * @see OrthancPluginCreateDicom() + * @ingroup Toolbox + **/ + ORTHANC_PLUGIN_INLINE char* OrthancPluginEncodeDicomWebJson3( + OrthancPluginContext* context, + const void* dicom, + uint32_t dicomSize, + OrthancPluginDicomWebBinaryCallback2 callback, + void* payload, + bool injectEmptyPixelData) + { + char* target = NULL; + + _OrthancPluginEncodeDicomWeb3 params; + params.target = ⌖ + params.dicom = dicom; + params.dicomSize = dicomSize; + params.callback = callback; + params.payload = payload; + params.injectEmptyPixelData = injectEmptyPixelData; + + + if (context->InvokeService(context, _OrthancPluginService_EncodeDicomWebJson3, ¶ms) != OrthancPluginErrorCode_Success) + { + /* Error */ + return NULL; + } + else + { + return target; + } + } + /** * @brief Convert a DICOM instance to DICOMweb XML. *
--- a/OrthancServer/Plugins/Samples/MultitenantDicom/OrthancFrameworkDependencies.cpp Mon Jun 12 18:42:06 2023 +0200 +++ b/OrthancServer/Plugins/Samples/MultitenantDicom/OrthancFrameworkDependencies.cpp Fri Jun 16 09:26:33 2023 +0200 @@ -64,6 +64,7 @@ #include "../../../../OrthancFramework/Sources/DicomNetworking/Internals/StoreScp.cpp" #include "../../../../OrthancFramework/Sources/DicomNetworking/RemoteModalityParameters.cpp" #include "../../../../OrthancFramework/Sources/DicomParsing/FromDcmtkBridge.cpp" +#include "../../../../OrthancFramework/Sources/DicomParsing/DicomWebJsonVisitor.cpp" #include "../../../../OrthancFramework/Sources/DicomParsing/Internals/DicomFrameIndex.cpp" #include "../../../../OrthancFramework/Sources/DicomParsing/Internals/DicomImageDecoder.cpp" #include "../../../../OrthancFramework/Sources/DicomParsing/ParsedDicomFile.cpp"
--- a/OrthancServer/Sources/Database/StatelessDatabaseOperations.cpp Mon Jun 12 18:42:06 2023 +0200 +++ b/OrthancServer/Sources/Database/StatelessDatabaseOperations.cpp Fri Jun 16 09:26:33 2023 +0200 @@ -868,7 +868,8 @@ LookupStringMetadata(target.mainDicomTagsSignature_, target.metadata_, MetadataType_MainDicomTagsSignature); } - if (expandFlags & ExpandResourceFlags_IncludeMainDicomTags) + if (expandFlags & ExpandResourceFlags_IncludeMainDicomTags + || expandFlags & ExpandResourceFlags_IncludeRequestedTags) { // read all tags from DB transaction.GetMainDicomTags(target.GetMainDicomTags(), internalId);
--- a/OrthancServer/Sources/Database/StatelessDatabaseOperations.h Mon Jun 12 18:42:06 2023 +0200 +++ b/OrthancServer/Sources/Database/StatelessDatabaseOperations.h Fri Jun 16 09:26:33 2023 +0200 @@ -115,11 +115,13 @@ ExpandResourceFlags_IncludeChildren = (1 << 1), ExpandResourceFlags_IncludeMainDicomTags = (1 << 2), ExpandResourceFlags_IncludeLabels = (1 << 3), + ExpandResourceFlags_IncludeRequestedTags = (1 << 3), ExpandResourceFlags_Default = (ExpandResourceFlags_IncludeMetadata | ExpandResourceFlags_IncludeChildren | ExpandResourceFlags_IncludeMainDicomTags | - ExpandResourceFlags_IncludeLabels) + ExpandResourceFlags_IncludeLabels | + ExpandResourceFlags_IncludeRequestedTags) }; class StatelessDatabaseOperations : public boost::noncopyable
--- a/OrthancServer/Sources/OrthancRestApi/OrthancRestApi.cpp Mon Jun 12 18:42:06 2023 +0200 +++ b/OrthancServer/Sources/OrthancRestApi/OrthancRestApi.cpp Fri Jun 16 09:26:33 2023 +0200 @@ -489,7 +489,9 @@ static const std::string GET_SIMPLIFY = "simplify"; static const std::string GET_FULL = "full"; static const std::string GET_SHORT = "short"; + static const std::string GET_DICOM_WEB = "dicomWeb"; static const std::string GET_REQUESTED_TAGS = "requestedTags"; + static const std::string GET_INCLUDE = "include"; static const std::string POST_SIMPLIFY = "Simplify"; static const std::string POST_FULL = "Full"; @@ -506,6 +508,45 @@ "report the DICOM tags in full format (tags indexed by their hexadecimal " "format, associated with their symbolic name and their value)"; + ExpandResourceFlags OrthancRestApi::GetResponseContent(const RestApiGetCall& call, + ExpandResourceFlags defaultContent) + { + if (call.HasArgument(GET_INCLUDE)) + { + std::vector<std::string> includes; + Toolbox::TokenizeString(includes, call.GetArgument(GET_INCLUDE, ""), ','); + + unsigned int result = static_cast<unsigned int>(ExpandResourceFlags_None); + for (size_t i = 0; i < includes.size(); ++i) + { + if (includes[i] == "MainDicomTags") + { + result |= static_cast<unsigned int>(ExpandResourceFlags_IncludeMainDicomTags); + } + else if (includes[i] == "RequestedTags") + { + result |= static_cast<unsigned int>(ExpandResourceFlags_IncludeRequestedTags); + } + else if (includes[i] == "Labels") + { + result |= static_cast<unsigned int>(ExpandResourceFlags_IncludeLabels); + } + else if (includes[i] == "Children") + { + result |= static_cast<unsigned int>(ExpandResourceFlags_IncludeChildren); + } + else if (includes[i] == "Metadata") + { + result |= static_cast<unsigned int>(ExpandResourceFlags_IncludeMetadata); + } + } + + return static_cast<ExpandResourceFlags>(result); + } + + return defaultContent; + } + DicomToJsonFormat OrthancRestApi::GetDicomFormat(const RestApiGetCall& call, DicomToJsonFormat defaultFormat) @@ -522,6 +563,10 @@ { return DicomToJsonFormat_Full; } + else if (call.HasArgument(GET_DICOM_WEB)) + { + return DicomToJsonFormat_DicomWeb; + } else { return defaultFormat;
--- a/OrthancServer/Sources/OrthancRestApi/OrthancRestApi.h Mon Jun 12 18:42:06 2023 +0200 +++ b/OrthancServer/Sources/OrthancRestApi/OrthancRestApi.h Fri Jun 16 09:26:33 2023 +0200 @@ -28,7 +28,7 @@ #include "../../../OrthancFramework/Sources/RestApi/RestApi.h" #include "../ServerJobs/ThreadedSetOfInstancesJob.h" #include "../ServerEnumerations.h" - +#include "../Database/StatelessDatabaseOperations.h" #include <set> namespace Orthanc @@ -147,6 +147,9 @@ static DicomToJsonFormat GetDicomFormat(const Json::Value& body, DicomToJsonFormat defaultFormat); + static ExpandResourceFlags GetResponseContent(const RestApiGetCall& call, + ExpandResourceFlags defaultContent); + static void DocumentDicomFormat(RestApiGetCall& call, DicomToJsonFormat defaultFormat);
--- a/OrthancServer/Sources/OrthancRestApi/OrthancRestResources.cpp Mon Jun 12 18:42:06 2023 +0200 +++ b/OrthancServer/Sources/OrthancRestApi/OrthancRestResources.cpp Fri Jun 16 09:26:33 2023 +0200 @@ -158,11 +158,11 @@ *(mainDicomTags->second.get()), instanceId->second, dicomAsJson->second.get(), - level, format, requestedTags, allowStorageAccess); + level, format, requestedTags, ExpandResourceFlags_Default, allowStorageAccess); } else { - context.ExpandResource(expanded, *resource, level, format, requestedTags, allowStorageAccess); + context.ExpandResource(expanded, *resource, level, format, requestedTags, ExpandResourceFlags_Default, allowStorageAccess); } if (expanded.type() == Json::objectValue) @@ -288,7 +288,7 @@ Json::Value json; if (OrthancRestApi::GetContext(call).ExpandResource( - json, call.GetUriComponent("id", ""), resourceType, format, requestedTags, true /* allowStorageAccess */)) + json, call.GetUriComponent("id", ""), resourceType, format, requestedTags, ExpandResourceFlags_Default, true /* allowStorageAccess */)) { call.GetOutput().AnswerJson(json); } @@ -421,6 +421,30 @@ } + static void GetInstanceFileUntiPixelData(RestApiGetCall& call) + { + if (call.IsDocumentation()) + { + call.GetDocumentation() + .SetTag("Instances") + .SetSummary("Download DICOM Header (without the pixel data)") + .SetDescription("Download one DICOM instance header") + .SetUriArgument("id", "Orthanc identifier of the DICOM instance of interest") + .SetHttpHeader("Accept", "This HTTP header can be set to retrieve the DICOM instance in DICOMweb format") + .AddAnswerType(MimeType_Dicom, "The DICOM instance"); + return; + } + + ServerContext& context = OrthancRestApi::GetContext(call); + + std::string publicId = call.GetUriComponent("id", ""); + + std::string buffer; + context.ReadDicomUntilPixelData(buffer, publicId); + call.GetOutput().AnswerBuffer(buffer, MimeType_Binary); + } + + static void ExportInstanceFile(RestApiPostCall& call) { if (call.IsDocumentation()) @@ -3355,12 +3379,13 @@ Json::Value result = Json::arrayValue; const DicomToJsonFormat format = OrthancRestApi::GetDicomFormat(call, DicomToJsonFormat_Human); + ExpandResourceFlags expandResource = OrthancRestApi::GetResponseContent(call, ExpandResourceFlags_Default); for (std::list<std::string>::const_iterator it = a.begin(); it != a.end(); ++it) { Json::Value resource; - if (OrthancRestApi::GetContext(call).ExpandResource(resource, *it, end, format, requestedTags, true /* allowStorageAccess */)) + if (OrthancRestApi::GetContext(call).ExpandResource(resource, *it, end, format, requestedTags, expandResource, true /* allowStorageAccess */)) { result.append(resource); } @@ -3479,7 +3504,7 @@ const DicomToJsonFormat format = OrthancRestApi::GetDicomFormat(call, DicomToJsonFormat_Human); Json::Value resource; - if (OrthancRestApi::GetContext(call).ExpandResource(resource, current, end, format, requestedTags, true /* allowStorageAccess */)) + if (OrthancRestApi::GetContext(call).ExpandResource(resource, current, end, format, requestedTags, ExpandResourceFlags_Default, true /* allowStorageAccess */)) { call.GetOutput().AnswerJson(resource); } @@ -3886,7 +3911,7 @@ Json::Value item; std::set<DicomTag> emptyRequestedTags; // not supported for bulk content - if (OrthancRestApi::GetContext(call).ExpandResource(item, *it, level, format, emptyRequestedTags, true /* allowStorageAccess */)) + if (OrthancRestApi::GetContext(call).ExpandResource(item, *it, level, format, emptyRequestedTags, ExpandResourceFlags_Default, true /* allowStorageAccess */)) { if (metadata) { @@ -3911,7 +3936,7 @@ std::set<DicomTag> emptyRequestedTags; // not supported for bulk content if (index.LookupResourceType(level, *it) && - OrthancRestApi::GetContext(call).ExpandResource(item, *it, level, format, emptyRequestedTags, true /* allowStorageAccess */)) + OrthancRestApi::GetContext(call).ExpandResource(item, *it, level, format, emptyRequestedTags, ExpandResourceFlags_Default, true /* allowStorageAccess */)) { if (metadata) { @@ -4010,6 +4035,7 @@ Register("/studies/{id}/module-patient", GetModule<ResourceType_Study, DicomModule_Patient>); Register("/instances/{id}/file", GetInstanceFile); + Register("/instances/{id}/file-until-pixel-data", GetInstanceFileUntiPixelData); Register("/instances/{id}/export", ExportInstanceFile); Register("/instances/{id}/tags", GetInstanceTags); Register("/instances/{id}/simplified-tags", GetInstanceSimplifiedTags);
--- a/OrthancServer/Sources/OrthancWebDav.cpp Mon Jun 12 18:42:06 2023 +0200 +++ b/OrthancServer/Sources/OrthancWebDav.cpp Fri Jun 16 09:26:33 2023 +0200 @@ -261,7 +261,7 @@ Json::Value resource; std::set<DicomTag> emptyRequestedTags; // not supported for webdav - if (context_.ExpandResource(resource, publicId, level_, DicomToJsonFormat_Human, emptyRequestedTags, true /* allowStorageAccess */)) + if (context_.ExpandResource(resource, publicId, level_, DicomToJsonFormat_Human, emptyRequestedTags, ExpandResourceFlags_Default, true /* allowStorageAccess */)) { if (success_) {
--- a/OrthancServer/Sources/ServerContext.cpp Mon Jun 12 18:42:06 2023 +0200 +++ b/OrthancServer/Sources/ServerContext.cpp Fri Jun 16 09:26:33 2023 +0200 @@ -1133,32 +1133,47 @@ void ServerContext::ReadDicomForHeader(std::string& dicom, const std::string& instancePublicId) { - if (!ReadDicomUntilPixelData(dicom, instancePublicId)) - { - ReadDicom(dicom, instancePublicId); - } + ReadDicomUntilPixelData(dicom, instancePublicId); } bool ServerContext::ReadDicomUntilPixelData(std::string& dicom, const std::string& instancePublicId) { + FileInfo attachment; + int64_t revision; // Ignored + + // if the attachment exists as such return it directly + if (index_.LookupAttachment(attachment, revision, instancePublicId, FileContentType_DicomUntilPixelData)) + { + StorageAccessor accessor(area_, storageCache_, GetMetricsRegistry()); + accessor.Read(dicom, attachment); + return true; + } + + // if the storage area can not read part of files, return the whole file if (!area_.HasReadRange()) { - return false; + ReadDicom(dicom, instancePublicId); + return true; } - - FileInfo attachment; - int64_t revision; // Ignored + + // else, read the start of the dicom file + if (!index_.LookupAttachment(attachment, revision, instancePublicId, FileContentType_Dicom)) { throw OrthancException(ErrorCode_InternalError, - "Unable to read the DICOM file of instance " + instancePublicId); + "Unable to read the DICOM file of instance " + instancePublicId); + } + + // if the attachment is compressed, return the whole file + if (attachment.GetCompressionType() != CompressionType_None) + { + ReadDicom(dicom, instancePublicId); + return true; } std::string s; - - if (attachment.GetCompressionType() == CompressionType_None && - index_.LookupMetadata(s, revision, instancePublicId, ResourceType_Instance, + if (index_.LookupMetadata(s, revision, instancePublicId, ResourceType_Instance, MetadataType_Instance_PixelDataOffset) && !s.empty()) { @@ -1179,7 +1194,8 @@ } } - return false; + ReadDicom(dicom, instancePublicId); + return true; } @@ -1991,7 +2007,8 @@ static void SerializeExpandedResource(Json::Value& target, const ExpandedResource& resource, DicomToJsonFormat format, - const std::set<DicomTag>& requestedTags) + const std::set<DicomTag>& requestedTags, + ExpandResourceFlags expandFlags) { target = Json::objectValue; @@ -2019,42 +2036,45 @@ throw OrthancException(ErrorCode_InternalError); } - switch (resource.GetLevel()) + if (expandFlags & ExpandResourceFlags_IncludeChildren) { - case ResourceType_Patient: - case ResourceType_Study: - case ResourceType_Series: + switch (resource.GetLevel()) { - Json::Value c = Json::arrayValue; + case ResourceType_Patient: + case ResourceType_Study: + case ResourceType_Series: + { + Json::Value c = Json::arrayValue; + + for (std::list<std::string>::const_iterator + it = resource.childrenIds_.begin(); it != resource.childrenIds_.end(); ++it) + { + c.append(*it); + } - for (std::list<std::string>::const_iterator - it = resource.childrenIds_.begin(); it != resource.childrenIds_.end(); ++it) - { - c.append(*it); + if (resource.GetLevel() == ResourceType_Patient) + { + target["Studies"] = c; + } + else if (resource.GetLevel() == ResourceType_Study) + { + target["Series"] = c; + } + else + { + target["Instances"] = c; + } + break; } - if (resource.GetLevel() == ResourceType_Patient) - { - target["Studies"] = c; - } - else if (resource.GetLevel() == ResourceType_Study) - { - target["Series"] = c; - } - else - { - target["Instances"] = c; - } - break; + case ResourceType_Instance: + break; + + default: + throw OrthancException(ErrorCode_InternalError); } - - case ResourceType_Instance: - break; - - default: - throw OrthancException(ErrorCode_InternalError); } - + switch (resource.GetLevel()) { case ResourceType_Patient: @@ -2117,26 +2137,29 @@ } // serialize tags + if (expandFlags & ExpandResourceFlags_IncludeMainDicomTags) + { + static const char* const MAIN_DICOM_TAGS = "MainDicomTags"; + static const char* const PATIENT_MAIN_DICOM_TAGS = "PatientMainDicomTags"; - static const char* const MAIN_DICOM_TAGS = "MainDicomTags"; - static const char* const PATIENT_MAIN_DICOM_TAGS = "PatientMainDicomTags"; - - DicomMap mainDicomTags; - resource.GetMainDicomTags().ExtractResourceInformation(mainDicomTags, resource.GetLevel()); + DicomMap mainDicomTags; + resource.GetMainDicomTags().ExtractResourceInformation(mainDicomTags, resource.GetLevel()); - target[MAIN_DICOM_TAGS] = Json::objectValue; - FromDcmtkBridge::ToJson(target[MAIN_DICOM_TAGS], mainDicomTags, format); - - if (resource.GetLevel() == ResourceType_Study) - { - DicomMap patientMainDicomTags; - resource.GetMainDicomTags().ExtractPatientInformation(patientMainDicomTags); + target[MAIN_DICOM_TAGS] = Json::objectValue; + FromDcmtkBridge::ToJson(target[MAIN_DICOM_TAGS], mainDicomTags, format); + + if (resource.GetLevel() == ResourceType_Study) + { + DicomMap patientMainDicomTags; + resource.GetMainDicomTags().ExtractPatientInformation(patientMainDicomTags); - target[PATIENT_MAIN_DICOM_TAGS] = Json::objectValue; - FromDcmtkBridge::ToJson(target[PATIENT_MAIN_DICOM_TAGS], patientMainDicomTags, format); + target[PATIENT_MAIN_DICOM_TAGS] = Json::objectValue; + FromDcmtkBridge::ToJson(target[PATIENT_MAIN_DICOM_TAGS], patientMainDicomTags, format); + } } - if (requestedTags.size() > 0) + if ((expandFlags & ExpandResourceFlags_IncludeRequestedTags) + && requestedTags.size() > 0) { static const char* const REQUESTED_TAGS = "RequestedTags"; @@ -2148,6 +2171,7 @@ } + if (expandFlags & ExpandResourceFlags_IncludeLabels) { Json::Value labels = Json::arrayValue; @@ -2382,13 +2406,14 @@ ResourceType level, DicomToJsonFormat format, const std::set<DicomTag>& requestedTags, + ExpandResourceFlags expandFlags, bool allowStorageAccess) { std::string unusedInstanceId; Json::Value* unusedDicomAsJson = NULL; DicomMap unusedMainDicomTags; - return ExpandResource(target, publicId, unusedMainDicomTags, unusedInstanceId, unusedDicomAsJson, level, format, requestedTags, allowStorageAccess); + return ExpandResource(target, publicId, unusedMainDicomTags, unusedInstanceId, unusedDicomAsJson, level, format, requestedTags, expandFlags, allowStorageAccess); } bool ServerContext::ExpandResource(Json::Value& target, @@ -2399,13 +2424,14 @@ ResourceType level, DicomToJsonFormat format, const std::set<DicomTag>& requestedTags, + ExpandResourceFlags expandFlags, bool allowStorageAccess) { ExpandedResource resource; - if (ExpandResource(resource, publicId, mainDicomTags, instanceId, dicomAsJson, level, requestedTags, ExpandResourceFlags_Default, allowStorageAccess)) + if (ExpandResource(resource, publicId, mainDicomTags, instanceId, dicomAsJson, level, requestedTags, expandFlags, allowStorageAccess)) { - SerializeExpandedResource(target, resource, format, requestedTags); + SerializeExpandedResource(target, resource, format, requestedTags, expandFlags); return true; }
--- a/OrthancServer/Sources/ServerContext.h Mon Jun 12 18:42:06 2023 +0200 +++ b/OrthancServer/Sources/ServerContext.h Fri Jun 16 09:26:33 2023 +0200 @@ -566,6 +566,7 @@ ResourceType level, DicomToJsonFormat format, const std::set<DicomTag>& requestedTags, + ExpandResourceFlags expandFlags, bool allowStorageAccess); bool ExpandResource(Json::Value& target, @@ -576,6 +577,7 @@ ResourceType level, DicomToJsonFormat format, const std::set<DicomTag>& requestedTags, + ExpandResourceFlags expandFlags, bool allowStorageAccess); bool ExpandResource(ExpandedResource& target,