Mercurial > hg > orthanc-wsi
changeset 402:0c5eb3253019
making the DICOM instances valid by default
| author | Sebastien Jodogne <s.jodogne@gmail.com> |
|---|---|
| date | Wed, 05 Nov 2025 11:27:34 +0100 |
| parents | 1be37c2e96c2 |
| children | 330cd7f020e2 |
| files | Applications/Dicomizer.cpp Framework/DicomToolbox.cpp Framework/DicomToolbox.h NEWS Resources/SampleDataset.json |
| diffstat | 5 files changed, 122 insertions(+), 12 deletions(-) [+] |
line wrap: on
line diff
--- a/Applications/Dicomizer.cpp Tue Nov 04 19:21:58 2025 +0100 +++ b/Applications/Dicomizer.cpp Wed Nov 05 11:27:34 2025 +0100 @@ -353,7 +353,7 @@ } std::unique_ptr<DcmDataset> dataset(Orthanc::FromDcmtkBridge::FromJson(json, true, true, Orthanc::Encoding_Latin1, - "" /* no private tag, thus no private creator */)); + "" /* no private tag, thus no private creator */)); if (dataset.get() == NULL) { LOG(ERROR) << "Cannot convert to JSON file to a DICOM dataset: " << path; @@ -383,6 +383,76 @@ OrthancWSI::DicomToolbox::SetStringTag(*dataset, DCM_ContentTime, time); OrthancWSI::DicomToolbox::SetStringTag(*dataset, DCM_AcquisitionDateTime, date + time); + // Write version information + OrthancWSI::DicomToolbox::SetStringTag(*dataset, DCM_Manufacturer, "Orthanc"); + OrthancWSI::DicomToolbox::SetStringTag(*dataset, DCM_ManufacturerModelName, "OrthancWSIDicomizer"); + + { + const std::string version = "OrthancWSIDicomizer-" + std::string(ORTHANC_WSI_VERSION); + OrthancWSI::DicomToolbox::SetStringTag(*dataset, DCM_DeviceSerialNumber, version); + OrthancWSI::DicomToolbox::SetStringTag(*dataset, DCM_SoftwareVersions, version); + } + + // Write the tags that are needed for a valid DICOM instance + OrthancWSI::DicomToolbox::SetStringTag(*dataset, DCM_ImageType, "DERIVED\\PRIMARY\\VOLUME\\NONE"); + + OrthancWSI::DicomToolbox::SetStringTagWithWarning(*dataset, DCM_BurnedInAnnotation, "NO"); + OrthancWSI::DicomToolbox::SetStringTagWithWarning(*dataset, DCM_ExtendedDepthOfField, "NO"); + OrthancWSI::DicomToolbox::SetStringTagWithWarning(*dataset, DCM_FocusMethod, "AUTO"); + OrthancWSI::DicomToolbox::SetStringTagWithWarning(*dataset, DCM_SpecimenLabelInImage, "NO"); + OrthancWSI::DicomToolbox::SetStringTagWithWarning(*dataset, DCM_ContainerIdentifier, "MY_CONTAINER"); + + { + std::set<DcmTagKey> tagsStringType2; + tagsStringType2.insert(DCM_AccessionNumber); + tagsStringType2.insert(DCM_PatientBirthDate); + tagsStringType2.insert(DCM_PatientName); + tagsStringType2.insert(DCM_PatientSex); + tagsStringType2.insert(DCM_ReferringPhysicianName); + tagsStringType2.insert(DCM_SeriesNumber); + tagsStringType2.insert(DCM_StudyID); + + for (std::set<DcmTagKey>::const_iterator it = tagsStringType2.begin(); it != tagsStringType2.end(); ++it) + { + OrthancWSI::DicomToolbox::SetStringTag(*dataset, *it, ""); + } + } + + { + std::set<DcmTagKey> tagsSequenceType2; + tagsSequenceType2.insert(DCM_AcquisitionContextSequence); + tagsSequenceType2.insert(DCM_ContainerTypeCodeSequence); + tagsSequenceType2.insert(DCM_IssuerOfTheContainerIdentifierSequence); + + for (std::set<DcmTagKey>::const_iterator it = tagsSequenceType2.begin(); it != tagsSequenceType2.end(); ++it) + { + std::unique_ptr<DcmElement> empty(new DcmSequenceOfItems(*it)); + + if (!dataset->tagExists(*it) && + !dataset->insert(empty.release(), true /* replace */, false).good()) + { + throw Orthanc::OrthancException(Orthanc::ErrorCode_InternalError); + } + } + } + + if (!dataset->tagExists(DCM_SpecimenDescriptionSequence)) + { + std::unique_ptr<DcmItem> item(new DcmItem); + OrthancWSI::DicomToolbox::SetStringTagWithWarning(*item, DCM_SpecimenIdentifier, "MY_SPECIMEN"); + OrthancWSI::DicomToolbox::SetStringTagWithWarning(*item, DCM_SpecimenUID, Orthanc::FromDcmtkBridge::GenerateUniqueIdentifier(Orthanc::ResourceType_Instance)); + + std::unique_ptr<DcmSequenceOfItems> specimen(new DcmSequenceOfItems(DCM_SpecimenDescriptionSequence)); + + if (!item->insert(new DcmSequenceOfItems(DCM_IssuerOfTheSpecimenIdentifierSequence), false, false).good() || + !item->insert(new DcmSequenceOfItems(DCM_SpecimenPreparationSequence), false, false).good() || + !specimen->insert(item.release(), false, false).good() || + !dataset->insert(specimen.release(), true /* replace */, false).good()) + { + throw Orthanc::OrthancException(Orthanc::ErrorCode_InternalError); + } + } + return dataset.release(); } @@ -406,6 +476,8 @@ { // No Dimension Organization provided: Generate an unique identifier organization = Orthanc::FromDcmtkBridge::GenerateUniqueIdentifier(Orthanc::ResourceType_Instance); + LOG(WARNING) << "Generating an unique identifier for the tags \"" + << OrthancWSI::DicomToolbox::GetTagName(DCM_DimensionOrganizationUID) << "\": \"" << organization << "\""; } @@ -451,13 +523,19 @@ // Construct tag "Shared Functional Groups Sequence" (5200,9229) std::unique_ptr<DcmItem> item(new DcmItem); + std::unique_ptr<DcmItem> item2(new DcmItem); + OrthancWSI::DicomToolbox::SetStringTag(*item2, DCM_OpticalPathIdentifier, opticalPathId); + std::unique_ptr<DcmItem> item3(new DcmItem); - OrthancWSI::DicomToolbox::SetStringTag(*item3, DCM_OpticalPathIdentifier, opticalPathId); + OrthancWSI::DicomToolbox::SetStringTag(*item3, DCM_FrameType, "DERIVED\\PRIMARY\\VOLUME\\NONE"); std::unique_ptr<DcmSequenceOfItems> sequence(new DcmSequenceOfItems(DCM_SharedFunctionalGroupsSequence)); - std::unique_ptr<DcmSequenceOfItems> sequence3(new DcmSequenceOfItems(DCM_OpticalPathIdentificationSequence)); + std::unique_ptr<DcmSequenceOfItems> sequence2(new DcmSequenceOfItems(DCM_OpticalPathIdentificationSequence)); + std::unique_ptr<DcmSequenceOfItems> sequence3(new DcmSequenceOfItems(DCM_WholeSlideMicroscopyImageFrameTypeSequence)); - if (!sequence3->insert(item3.release(), false, false).good() || + if (!sequence2->insert(item2.release(), false, false).good() || + !sequence3->insert(item3.release(), false, false).good() || + !item->insert(sequence2.release(), false, false).good() || !item->insert(sequence3.release(), false, false).good() || !sequence->insert(item.release(), false, false).good() || !dataset.insert(sequence.release(), true /* replace */, false).good())
--- a/Framework/DicomToolbox.cpp Tue Nov 04 19:21:58 2025 +0100 +++ b/Framework/DicomToolbox.cpp Wed Nov 05 11:27:34 2025 +0100 @@ -41,6 +41,12 @@ namespace DicomToolbox { #if ORTHANC_ENABLE_DCMTK == 1 + std::string GetTagName(const DcmTagKey& key) + { + DcmTag tmp(key); + return tmp.getTagName(); + } + void SetStringTag(DcmItem& dataset, const DcmTagKey& key, const std::string& value) @@ -52,6 +58,21 @@ } } + void SetStringTagWithWarning(DcmItem& dataset, + const DcmTagKey& key, + const std::string& value) + { + if (!dataset.tagExists(key)) + { + LOG(WARNING) << "Setting value of tag \"" << GetTagName(key) << "\" to placeholder value: \"" << value << "\""; + + if (!dataset.putAndInsertString(key, value.c_str()).good()) + { + throw Orthanc::OrthancException(Orthanc::ErrorCode_InternalError); + } + } + } + void SetUint16Tag(DcmItem& dataset, const DcmTagKey& key, uint16_t value)
--- a/Framework/DicomToolbox.h Tue Nov 04 19:21:58 2025 +0100 +++ b/Framework/DicomToolbox.h Wed Nov 05 11:27:34 2025 +0100 @@ -38,10 +38,16 @@ namespace DicomToolbox { #if ORTHANC_ENABLE_DCMTK == 1 + std::string GetTagName(const DcmTagKey& key); + void SetStringTag(DcmItem& dataset, const DcmTagKey& key, const std::string& value); + void SetStringTagWithWarning(DcmItem& dataset, + const DcmTagKey& key, + const std::string& value); + void SetUint16Tag(DcmItem& dataset, const DcmTagKey& key, uint16_t value);
--- a/NEWS Tue Nov 04 19:21:58 2025 +0100 +++ b/NEWS Wed Nov 05 11:27:34 2025 +0100 @@ -1,9 +1,13 @@ Pending changes in the mainline =============================== -* Added rotation button in the viewer -* The viewer displays a label if the "description" GET parameter is provided -* Upgraded to OpenLayers 10.6.1 +* OrthancWSIDicomizer: + - Placeholder tags are now automatically inserted when the "--dataset" option + provides incomplete data, ensuring the generated DICOM instances remain valid +* Viewer plugin: + - Added rotation button in the viewer + - The viewer displays a label if the "description" GET parameter is provided + - Upgraded to OpenLayers 10.6.1 Version 3.2 (2025-04-08)
--- a/Resources/SampleDataset.json Tue Nov 04 19:21:58 2025 +0100 +++ b/Resources/SampleDataset.json Wed Nov 05 11:27:34 2025 +0100 @@ -7,12 +7,7 @@ "SeriesNumber" : "1", "ReferringPhysicianName" : "SOME^PHYSICIAN", "AccessionNumber" : "123456789", - "Manufacturer" : "MyManufacturer", - "ManufacturerModelName" : "MyModel", - "DeviceSerialNumber" : "MySerialNumber", - "SoftwareVersions" : "MyVersion", - "ImageType" : "DERIVED\\PRIMARY\\VOLUME\\NONE", "FocusMethod" : "AUTO", "ExtendedDepthOfField" : "NO", @@ -38,6 +33,12 @@ ] } ], + "DimensionOrganizationSequence": [ + { + "DimensionOrganizationUID" : "1.2.276.0.7230010.3.1.4.1757367822.227496.1762338018.13870" + } + ], + "SpecimenLabelInImage" : "NO", "BurnedInAnnotation" : "NO" }
