# HG changeset patch # User Sebastien Jodogne # Date 1549625735 -3600 # Node ID f6374c36a671acb29d4e31eb8cbaafdcf864f849 # Parent c4e1977e5ed79828009c0b1878cd1911559ea2f1 documentation of DICOMweb in SDK, compatibility with DCMTK 3.6.0 diff -r c4e1977e5ed7 -r f6374c36a671 Core/DicomParsing/FromDcmtkBridge.cpp --- a/Core/DicomParsing/FromDcmtkBridge.cpp Fri Feb 08 08:38:45 2019 +0100 +++ b/Core/DicomParsing/FromDcmtkBridge.cpp Fri Feb 08 12:35:35 2019 +0100 @@ -90,13 +90,16 @@ #include #include #include -#include #include #include -#include #include #include +#if DCMTK_VERSION_NUMBER >= 361 +# include +# include +#endif + #if DCMTK_USE_EMBEDDED_DICTIONARIES == 1 # include #endif @@ -1461,11 +1464,15 @@ case EVR_PN: // person name return new DcmPersonName(key); +#if DCMTK_VERSION_NUMBER >= 361 case EVR_UC: // unlimited characters return new DcmUnlimitedCharacters(key); - +#endif + +#if DCMTK_VERSION_NUMBER >= 361 case EVR_UR: // URI/URL return new DcmUniversalResourceIdentifierOrLocator(key); +#endif /** @@ -1640,8 +1647,10 @@ case EVR_UT: // unlimited text case EVR_PN: // person name case EVR_UI: // unique identifier +#if DCMTK_VERSION_NUMBER >= 361 case EVR_UC: // unlimited characters case EVR_UR: // URI/URL +#endif { ok = element.putString(decoded->c_str()).good(); break; diff -r c4e1977e5ed7 -r f6374c36a671 Plugins/Engine/OrthancPlugins.cpp --- a/Plugins/Engine/OrthancPlugins.cpp Fri Feb 08 08:38:45 2019 +0100 +++ b/Plugins/Engine/OrthancPlugins.cpp Fri Feb 08 12:35:35 2019 +0100 @@ -997,6 +997,9 @@ sizeof(int32_t) != sizeof(OrthancPluginIdentifierConstraint) || sizeof(int32_t) != sizeof(OrthancPluginInstanceOrigin) || sizeof(int32_t) != sizeof(OrthancPluginJobStepStatus) || + sizeof(int32_t) != sizeof(OrthancPluginConstraintType) || + sizeof(int32_t) != sizeof(OrthancPluginMetricsType) || + sizeof(int32_t) != sizeof(OrthancPluginDicomWebBinaryMode) || static_cast(OrthancPluginDicomToJsonFlags_IncludeBinary) != static_cast(DicomToJsonFlags_IncludeBinary) || static_cast(OrthancPluginDicomToJsonFlags_IncludePrivateTags) != static_cast(DicomToJsonFlags_IncludePrivateTags) || static_cast(OrthancPluginDicomToJsonFlags_IncludeUnknownTags) != static_cast(DicomToJsonFlags_IncludeUnknownTags) || diff -r c4e1977e5ed7 -r f6374c36a671 Plugins/Include/orthanc/OrthancCPlugin.h --- a/Plugins/Include/orthanc/OrthancCPlugin.h Fri Feb 08 08:38:45 2019 +0100 +++ b/Plugins/Include/orthanc/OrthancCPlugin.h Fri Feb 08 12:35:35 2019 +0100 @@ -897,7 +897,7 @@ /** - * The available type of metrics. + * The available types of metrics. **/ typedef enum { @@ -912,6 +912,18 @@ } OrthancPluginMetricsType; + /** + * The available modes to export a binary DICOM tag into a DICOMweb + * JSON or XML document. + **/ + typedef enum + { + OrthancPluginDicomWebBinaryMode_Ignore, /*!< Don't include binary tags */ + OrthancPluginDicomWebBinaryMode_InlineBinary, /*!< Inline encoding using Base64 */ + OrthancPluginDicomWebBinaryMode_BulkDataUri /*!< Use a bulk data URI field */ + } OrthancPluginDicomWebBinaryMode; + + /** * @brief A memory buffer allocated by the core system of Orthanc. @@ -1021,6 +1033,15 @@ **/ typedef struct _OrthancPluginJob_t OrthancPluginJob; + + + /** + * @brief Opaque structure that represents a node in a JSON or XML + * document used in DICOMweb. + * @ingroup Toolbox + **/ + typedef struct _OrthancPluginDicomWebNode_t OrthancPluginDicomWebNode; + /** @@ -1069,12 +1090,26 @@ /** * @brief Signature of a function to free dynamic memory. + * @ingroup Callbacks **/ typedef void (*OrthancPluginFree) (void* buffer); /** + * @brief Signature of a function to set the content of a node + * encoding a binary DICOM tag, into a JSON or XML document + * generated for DICOMweb. + * @ingroup Callbacks + **/ + typedef void (*OrthancPluginDicomWebSetBinaryNode) ( + OrthancPluginDicomWebNode* node, + OrthancPluginDicomWebBinaryMode mode, + const char* bulkDataUri); + + + + /** * @brief Callback for writing to the storage area. * * Signature of a callback function that is triggered when Orthanc writes a file to the storage area. @@ -1472,13 +1507,50 @@ * acquire. * * @see OrthancPluginRegisterRefreshMetrics() - * @ingroups Callbacks + * @ingroup Callbacks **/ typedef void (*OrthancPluginRefreshMetricsCallback) (); /** + * @brief Callback executed to encode a binary tag in DICOMweb. + * + * Signature of a callback function that is called by Orthanc + * whenever a DICOM tag that contains a binary value must be written + * to a JSON or XML node, while a DICOMweb document is being + * generated. The value representation (VR) of the DICOM tag can be + * OB, OD, OF, OL, OW, or UN. + * + * @see OrthancPluginEncodeDicomWebJson() and OrthancPluginEncodeDicomWebXml() + * @param node The node being generated, as provided by Orthanc. + * @param setter The setter to be used to encode the content of the node. If + * the setter is not called, the binary tag is not written to the output document. + * @param levelDepth The depth of the node in the DICOM hierarchy of sequences. + * This parameter gives the number of elements in the "levelTagGroup", + * "levelTagElement", and "levelIndex" arrays. + * @param levelTagGroup The group of the parent DICOM tags in the hierarchy. + * @param levelTagElement The element of the parent DICOM tags in the hierarchy. + * @param levelIndex The index of the node in the parent sequences of the hiearchy. + * @param tagGroup The group of the DICOM tag of interest. + * @param tagElement The element of the DICOM tag of interest. + * @param vr The value representation of the binary DICOM node. + * @ingroup Callbacks + **/ + typedef void (*OrthancPluginDicomWebBinaryCallback) ( + OrthancPluginDicomWebNode* node, + OrthancPluginDicomWebSetBinaryNode setter, + uint32_t levelDepth, + const uint16_t* levelTagGroup, + const uint16_t* levelTagElement, + const uint32_t* levelIndex, + uint16_t tagGroup, + uint16_t tagElement, + OrthancPluginValueRepresentation vr); + + + + /** * @brief Data structure that contains information about the Orthanc core. **/ typedef struct _OrthancPluginContext_t @@ -1571,7 +1643,8 @@ sizeof(int32_t) != sizeof(OrthancPluginInstanceOrigin) || sizeof(int32_t) != sizeof(OrthancPluginJobStepStatus) || sizeof(int32_t) != sizeof(OrthancPluginConstraintType) || - sizeof(int32_t) != sizeof(OrthancPluginMetricsType)) + sizeof(int32_t) != sizeof(OrthancPluginMetricsType) || + sizeof(int32_t) != sizeof(OrthancPluginDicomWebBinaryMode)) { /* Mismatch in the size of the enumerations */ return 0; @@ -6630,32 +6703,6 @@ - typedef enum - { - OrthancPluginDicomWebBinaryMode_Ignore, - OrthancPluginDicomWebBinaryMode_InlineBinary, - OrthancPluginDicomWebBinaryMode_BulkDataUri - } OrthancPluginDicomWebBinaryMode; - - - typedef struct _OrthancPluginDicomWebNode_t OrthancPluginDicomWebNode; - - typedef void (*OrthancPluginDicomWebSetBinaryNode) ( - OrthancPluginDicomWebNode* node, - OrthancPluginDicomWebBinaryMode mode, - const char* bulkDataUri); - - typedef void (*OrthancPluginDicomWebBinaryCallback) ( - OrthancPluginDicomWebNode* node, - OrthancPluginDicomWebSetBinaryNode setter, - uint32_t levelDepth, - const uint16_t* levelTagGroup, - const uint16_t* levelTagElement, - const uint32_t* levelIndex, - uint16_t tagGroup, - uint16_t tagElement, - OrthancPluginValueRepresentation vr); - typedef struct { char** target; @@ -6664,6 +6711,21 @@ OrthancPluginDicomWebBinaryCallback callback; } _OrthancPluginEncodeDicomWeb; + /** + * @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. + * @see OrthancPluginCreateDicom() + * @return The NULL value in case of error, or the JSON document. This string must + * be freed by OrthancPluginFreeString(). + * @ingroup Toolbox + **/ ORTHANC_PLUGIN_INLINE char* OrthancPluginEncodeDicomWebJson( OrthancPluginContext* context, const void* dicom, @@ -6689,6 +6751,22 @@ } } + + /** + * @brief Convert a DICOM instance to DICOMweb XML. + * + * This function converts a memory buffer containing a DICOM instance, + * into its DICOMweb XML 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. + * @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* OrthancPluginEncodeDicomWebXml( OrthancPluginContext* context, const void* dicom, diff -r c4e1977e5ed7 -r f6374c36a671 UnitTestsSources/DicomMapTests.cpp --- a/UnitTestsSources/DicomMapTests.cpp Fri Feb 08 08:38:45 2019 +0100 +++ b/UnitTestsSources/DicomMapTests.cpp Fri Feb 08 12:35:35 2019 +0100 @@ -37,6 +37,7 @@ #include "../Core/OrthancException.h" #include "../Core/DicomFormat/DicomMap.h" #include "../Core/DicomParsing/FromDcmtkBridge.h" +#include "../Core/DicomParsing/ToDcmtkBridge.h" #include "../Core/DicomParsing/ParsedDicomFile.h" #include "../Core/DicomParsing/DicomWebJsonVisitor.h" @@ -44,6 +45,7 @@ #include #include +#include using namespace Orthanc; @@ -621,6 +623,26 @@ } +static void SetTagKey(ParsedDicomFile& dicom, + const DicomTag& tag, + const DicomTag& value) +{ + // This function emulates a call to function + // "dicom.GetDcmtkObject().getDataset()->putAndInsertTagKey(tag, + // value)" that was not available in DCMTK 3.6.0 + + std::auto_ptr element(new DcmAttributeTag(ToDcmtkBridge::Convert(tag))); + + DcmTagKey v = ToDcmtkBridge::Convert(value); + if (!element->putTagVal(v).good()) + { + throw OrthancException(ErrorCode_InternalError); + } + + dicom.GetDcmtkObject().getDataset()->insert(element.release()); +} + + TEST(DicomWebJson, ValueRepresentation) { // http://dicom.nema.org/medical/dicom/current/output/chtml/part18/sect_F.2.3.html @@ -628,9 +650,7 @@ ParsedDicomFile dicom(false); dicom.ReplacePlainString(DicomTag(0x0040, 0x0241), "AE"); dicom.ReplacePlainString(DicomTag(0x0010, 0x1010), "AS"); - ASSERT_TRUE(dicom.GetDcmtkObject().getDataset()-> - putAndInsertTagKey(DcmTag(0x0020, 0x9165), - DcmTagKey(0x0010, 0x0020)).good()); + SetTagKey(dicom, DicomTag(0x0020, 0x9165), DicomTag(0x0010, 0x0020)); dicom.ReplacePlainString(DicomTag(0x0008, 0x0052), "CS"); dicom.ReplacePlainString(DicomTag(0x0008, 0x0012), "DA"); dicom.ReplacePlainString(DicomTag(0x0010, 0x1020), "42"); // DS @@ -639,15 +659,11 @@ dicom.ReplacePlainString(DicomTag(0x0008, 0x1163), "44"); // FD dicom.ReplacePlainString(DicomTag(0x0008, 0x1160), "45"); // IS dicom.ReplacePlainString(DicomTag(0x0008, 0x0070), "LO"); - dicom.ReplacePlainString(DicomTag(0x0008, 0x0108), "LT"); + dicom.ReplacePlainString(DicomTag(0x0010, 0x4000), "LT"); dicom.ReplacePlainString(DicomTag(0x0028, 0x2000), "OB"); dicom.ReplacePlainString(DicomTag(0x7fe0, 0x0009), "OD"); dicom.ReplacePlainString(DicomTag(0x0064, 0x0009), "OF"); - -#if DCMTK_VERSION_NUMBER >= 362 dicom.ReplacePlainString(DicomTag(0x0066, 0x0040), "OLOL"); -#endif - ASSERT_THROW(dicom.ReplacePlainString(DicomTag(0x0028, 0x1201), "O"), OrthancException); dicom.ReplacePlainString(DicomTag(0x0028, 0x1201), "OWOW"); dicom.ReplacePlainString(DicomTag(0x0010, 0x0010), "PN"); @@ -662,8 +678,7 @@ dicom.ReplacePlainString(DicomTag(0x4342, 0x1234), "UN"); // Inexistent tag dicom.ReplacePlainString(DicomTag(0x0008, 0x0120), "UR"); dicom.ReplacePlainString(DicomTag(0x0008, 0x0301), "17"); // US - dicom.ReplacePlainString(DicomTag(0x0040, 0x0031), "UT"); - + dicom.ReplacePlainString(DicomTag(0x0040, 0x0031), "UT"); Orthanc::DicomWebJsonVisitor visitor; dicom.Apply(visitor); @@ -692,14 +707,19 @@ ASSERT_FLOAT_EQ(45.0f, visitor.GetResult() ["00081160"]["Value"][0].asFloat()); ASSERT_EQ("LO", visitor.GetResult() ["00080070"]["vr"].asString()); ASSERT_EQ("LO", visitor.GetResult() ["00080070"]["Value"][0].asString()); - ASSERT_EQ("LT", visitor.GetResult() ["00080108"]["vr"].asString()); - ASSERT_EQ("LT", visitor.GetResult() ["00080108"]["Value"][0].asString()); + ASSERT_EQ("LT", visitor.GetResult() ["00104000"]["vr"].asString()); + ASSERT_EQ("LT", visitor.GetResult() ["00104000"]["Value"][0].asString()); ASSERT_EQ("OB", visitor.GetResult() ["00282000"]["vr"].asString()); Toolbox::DecodeBase64(s, visitor.GetResult() ["00282000"]["InlineBinary"].asString()); ASSERT_EQ("OB", s); +#if DCMTK_VERSION_NUMBER >= 362 ASSERT_EQ("OD", visitor.GetResult() ["7FE00009"]["vr"].asString()); +#else + ASSERT_EQ("UN", visitor.GetResult() ["7FE00009"]["vr"].asString()); +#endif + Toolbox::DecodeBase64(s, visitor.GetResult() ["7FE00009"]["InlineBinary"].asString()); ASSERT_EQ("OD", s); @@ -709,9 +729,12 @@ #if DCMTK_VERSION_NUMBER >= 362 ASSERT_EQ("OL", visitor.GetResult() ["00660040"]["vr"].asString()); +#else + ASSERT_EQ("UN", visitor.GetResult() ["00660040"]["vr"].asString()); +#endif + Toolbox::DecodeBase64(s, visitor.GetResult() ["00660040"]["InlineBinary"].asString()); ASSERT_EQ("OLOL", s); -#endif ASSERT_EQ("OW", visitor.GetResult() ["00281201"]["vr"].asString()); Toolbox::DecodeBase64(s, visitor.GetResult() ["00281201"]["InlineBinary"].asString()); @@ -735,8 +758,14 @@ ASSERT_EQ("TM", visitor.GetResult() ["00080013"]["vr"].asString()); ASSERT_EQ("TM", visitor.GetResult() ["00080013"]["Value"][0].asString()); +#if DCMTK_VERSION_NUMBER >= 361 ASSERT_EQ("UC", visitor.GetResult() ["00080119"]["vr"].asString()); ASSERT_EQ("UC", visitor.GetResult() ["00080119"]["Value"][0].asString()); +#else + ASSERT_EQ("UN", visitor.GetResult() ["00080119"]["vr"].asString()); + Toolbox::DecodeBase64(s, visitor.GetResult() ["00080119"]["InlineBinary"].asString()); + ASSERT_EQ("UC", s); +#endif ASSERT_EQ("UI", visitor.GetResult() ["00080016"]["vr"].asString()); ASSERT_EQ("UI", visitor.GetResult() ["00080016"]["Value"][0].asString()); @@ -748,16 +777,27 @@ Toolbox::DecodeBase64(s, visitor.GetResult() ["43421234"]["InlineBinary"].asString()); ASSERT_EQ("UN", s); +#if DCMTK_VERSION_NUMBER >= 361 ASSERT_EQ("UR", visitor.GetResult() ["00080120"]["vr"].asString()); ASSERT_EQ("UR", visitor.GetResult() ["00080120"]["Value"][0].asString()); +#else + ASSERT_EQ("UN", visitor.GetResult() ["00080120"]["vr"].asString()); + Toolbox::DecodeBase64(s, visitor.GetResult() ["00080120"]["InlineBinary"].asString()); + ASSERT_EQ("UR", s); +#endif +#if DCMTK_VERSION_NUMBER >= 361 ASSERT_EQ("US", visitor.GetResult() ["00080301"]["vr"].asString()); ASSERT_EQ(17, visitor.GetResult() ["00080301"]["Value"][0].asUInt()); +#else + ASSERT_EQ("UN", visitor.GetResult() ["00080301"]["vr"].asString()); + Toolbox::DecodeBase64(s, visitor.GetResult() ["00080301"]["InlineBinary"].asString()); + ASSERT_EQ("17", s); +#endif ASSERT_EQ("UT", visitor.GetResult() ["00400031"]["vr"].asString()); ASSERT_EQ("UT", visitor.GetResult() ["00400031"]["Value"][0].asString()); - std::string xml; visitor.FormatXml(xml); }