Mercurial > hg > orthanc-stone
changeset 2089:63d77859edee dicom-sr
cont
author | Sebastien Jodogne <s.jodogne@gmail.com> |
---|---|
date | Tue, 07 Nov 2023 19:30:44 +0100 |
parents | b98d159c7545 |
children | c2dfc253bd04 |
files | Applications/StoneWebViewer/WebAssembly/StoneWebViewer.cpp OrthancStone/Sources/StoneEnumerations.cpp OrthancStone/Sources/StoneEnumerations.h OrthancStone/Sources/Toolbox/DicomStructuredReport.cpp OrthancStone/Sources/Toolbox/DicomStructuredReport.h OrthancStone/UnitTestsSources/UnitTestsMain.cpp |
diffstat | 6 files changed, 199 insertions(+), 17 deletions(-) [+] |
line wrap: on
line diff
--- a/Applications/StoneWebViewer/WebAssembly/StoneWebViewer.cpp Tue Nov 07 18:12:35 2023 +0100 +++ b/Applications/StoneWebViewer/WebAssembly/StoneWebViewer.cpp Tue Nov 07 19:30:44 2023 +0100 @@ -82,6 +82,7 @@ #include "../../../OrthancStone/Sources/Scene2DViewport/ViewportController.h" #include "../../../OrthancStone/Sources/StoneException.h" #include "../../../OrthancStone/Sources/Toolbox/DicomInstanceParameters.h" +#include "../../../OrthancStone/Sources/Toolbox/DicomStructuredReport.h" #include "../../../OrthancStone/Sources/Toolbox/GeometryToolbox.h" #include "../../../OrthancStone/Sources/Toolbox/OsiriX/AngleAnnotation.h" #include "../../../OrthancStone/Sources/Toolbox/OsiriX/CollectionOfAnnotations.h" @@ -102,8 +103,6 @@ #include <boost/math/special_functions/round.hpp> #include <stdio.h> -static const char* const DICOM_COMPREHENSIVE_SR_IOD = "1.2.840.10008.5.1.4.1.1.88.33"; - static const double PI = boost::math::constants::pi<double>(); #if !defined(STONE_WEB_VIEWER_EXPORT) @@ -427,11 +426,18 @@ { for (size_t i = 0; i < message.GetInstancesCount(); i++) { - std::string sopClassUid; - if (message.GetInstance(i).LookupStringValue(sopClassUid, Orthanc::DICOM_TAG_SOP_CLASS_UID, false) && - sopClassUid == DICOM_COMPREHENSIVE_SR_IOD) + std::string sopInstanceUid, sopClassUid; + if (message.GetInstance(i).LookupStringValue(sopInstanceUid, Orthanc::DICOM_TAG_SOP_INSTANCE_UID, false) && + message.GetInstance(i).LookupStringValue(sopClassUid, Orthanc::DICOM_TAG_SOP_CLASS_UID, false) && + OrthancStone::StringToSopClassUid(sopClassUid) == OrthancStone::SopClassUid_ComprehensiveSR) { - LOG(ERROR) << "ICI " << message.GetSeriesInstanceUid(); + std::unique_ptr<OrthancStone::ILoadersContext::ILock> lock(context_.Lock()); + lock->Schedule( + GetSharedObserver(), PRIORITY_NORMAL, OrthancStone::ParseDicomFromWadoCommand::Create( + source_, message.GetStudyInstanceUid(), message.GetSeriesInstanceUid(), sopInstanceUid, + false /* no transcoding */, Orthanc::DicomTransferSyntax_LittleEndianExplicit /* dummy value */, + new InstanceInfo(message.GetStudyInstanceUid(), message.GetSeriesInstanceUid(), OrthancStone::SopClassUid_ComprehensiveSR))); + return; } } @@ -533,6 +539,30 @@ { LOG(ERROR) << "Unable to extract PDF from series: " << info.GetSeriesInstanceUid(); } + + break; + } + + case OrthancStone::SopClassUid_ComprehensiveSR: + { + try + { + OrthancStone::DicomStructuredReport sr(message.GetDicom()); + + std::list<OrthancStone::DicomStructuredReport::Frame> frames; + sr.ExportOrderedFrames(frames); + + for (std::list<OrthancStone::DicomStructuredReport::Frame>::const_iterator + it = frames.begin(); it != frames.end(); ++it) + { + LOG(ERROR) << "YOU " << it->GetSopInstanceUid() << " " << it->GetFrameNumber(); + } + } + catch (Orthanc::OrthancException& e) + { + LOG(ERROR) << "Cannot decode DICOM-SR: " << e.What(); + } + break; } default:
--- a/OrthancStone/Sources/StoneEnumerations.cpp Tue Nov 07 18:12:35 2023 +0100 +++ b/OrthancStone/Sources/StoneEnumerations.cpp Tue Nov 07 19:30:44 2023 +0100 @@ -65,6 +65,10 @@ { return SopClassUid_DicomSeg; } + else if (s == "1.2.840.10008.5.1.4.1.1.88.33") + { + return SopClassUid_ComprehensiveSR; + } else { //LOG(INFO) << "Other SOP class UID: " << source;
--- a/OrthancStone/Sources/StoneEnumerations.h Tue Nov 07 18:12:35 2023 +0100 +++ b/OrthancStone/Sources/StoneEnumerations.h Tue Nov 07 19:30:44 2023 +0100 @@ -116,7 +116,8 @@ SopClassUid_VideoEndoscopicImageStorage, SopClassUid_VideoMicroscopicImageStorage, SopClassUid_VideoPhotographicImageStorage, - SopClassUid_DicomSeg + SopClassUid_DicomSeg, + SopClassUid_ComprehensiveSR }; enum SeriesThumbnailType
--- a/OrthancStone/Sources/Toolbox/DicomStructuredReport.cpp Tue Nov 07 18:12:35 2023 +0100 +++ b/OrthancStone/Sources/Toolbox/DicomStructuredReport.cpp Tue Nov 07 19:30:44 2023 +0100 @@ -118,6 +118,19 @@ namespace OrthancStone { + void DicomStructuredReport::ReferencedInstance::AddFrame(unsigned int frame) + { + if (frame == 0) + { + throw Orthanc::OrthancException(Orthanc::ErrorCode_ParameterOutOfRange); + } + else + { + frames_.insert(frame); + } + } + + class DicomStructuredReport::Structure : public boost::noncopyable { private: @@ -369,7 +382,7 @@ if (instancesInformation_.find(sopInstanceUid) == instancesInformation_.end()) { - instancesInformation_[sopInstanceUid] = ReferencedInstance(studyInstanceUid, seriesInstanceUid, sopClassUid); + instancesInformation_[sopInstanceUid] = new ReferencedInstance(studyInstanceUid, seriesInstanceUid, sopClassUid); } else { @@ -468,12 +481,16 @@ } std::string sopInstanceUid = GetStringValue(*instances.getItem(0), DCM_ReferencedSOPInstanceUID); - if (instancesInformation_.find(sopInstanceUid) == instancesInformation_.end()) + std::map<std::string, ReferencedInstance*>::iterator instanceInformation = instancesInformation_.find(sopInstanceUid); + + if (instanceInformation == instancesInformation_.end()) { throw Orthanc::OrthancException(Orthanc::ErrorCode_BadFileFormat, "Referencing unknown instance in DICOM-SR: " + sopInstanceUid); } + assert(instanceInformation->second != NULL); + if (instances.getItem(0)->tagExists(DCM_ReferencedFrameNumber)) { std::string frames = GetStringValue(*instances.getItem(0), DCM_ReferencedFrameNumber); @@ -490,12 +507,14 @@ else { AddStructure(sopInstanceUid, group, true, frame, hasProbabilityOfCancer, probabilityOfCancer); + instanceInformation->second->AddFrame(frame); } } } else { AddStructure(sopInstanceUid, group, false, 0, hasProbabilityOfCancer, probabilityOfCancer); + instanceInformation->second->AddFrame(1); } } } @@ -515,5 +534,64 @@ assert(*it != NULL); delete *it; } + + for (std::map<std::string, ReferencedInstance*>::iterator + it = instancesInformation_.begin(); it != instancesInformation_.end(); ++it) + { + assert(it->second != NULL); + delete it->second; + } + } + + + void DicomStructuredReport::GetReferencedInstance(std::string& studyInstanceUid, + std::string& seriesInstanceUid, + std::string& sopInstanceUid, + std::string& sopClassUid, + size_t i) const + { + if (i >= orderedInstances_.size()) + { + throw Orthanc::OrthancException(Orthanc::ErrorCode_ParameterOutOfRange); + } + + sopInstanceUid = orderedInstances_[i]; + + std::map<std::string, ReferencedInstance*>::const_iterator found = instancesInformation_.find(sopInstanceUid); + if (found == instancesInformation_.end()) + { + throw Orthanc::OrthancException(Orthanc::ErrorCode_InternalError); + } + + assert(found->second != NULL); + studyInstanceUid = found->second->GetStudyInstanceUid(); + seriesInstanceUid = found->second->GetSeriesInstanceUid(); + sopClassUid = found->second->GetSopClassUid(); + } + + + void DicomStructuredReport::ExportOrderedFrames(std::list<Frame>& frames) const + { + frames.clear(); + + for (size_t i = 0; i < orderedInstances_.size(); i++) + { + std::map<std::string, ReferencedInstance*>::const_iterator found = instancesInformation_.find(orderedInstances_[i]); + if (found == instancesInformation_.end()) + { + throw Orthanc::OrthancException(Orthanc::ErrorCode_InternalError); + } + + assert(found->second != NULL); + + for (std::set<unsigned int>::const_iterator frame = found->second->GetFrames().begin(); + frame != found->second->GetFrames().end(); ++frame) + { + frames.push_back(Frame(found->second->GetStudyInstanceUid(), + found->second->GetSeriesInstanceUid(), + orderedInstances_[i], + found->second->GetSopClassUid(), *frame)); + } + } } }
--- a/OrthancStone/Sources/Toolbox/DicomStructuredReport.h Tue Nov 07 18:12:35 2023 +0100 +++ b/OrthancStone/Sources/Toolbox/DicomStructuredReport.h Tue Nov 07 19:30:44 2023 +0100 @@ -35,6 +35,7 @@ #include <dcmtk/dcmdata/dcitem.h> #include <list> +#include <set> namespace OrthancStone { @@ -45,12 +46,13 @@ class Point; class Polyline; - class ReferencedInstance + class ReferencedInstance : public boost::noncopyable { private: std::string studyInstanceUid_; std::string seriesInstanceUid_; std::string sopClassUid_; + std::set<unsigned int> frames_; public: ReferencedInstance(const std::string& studyInstanceUid, @@ -62,10 +64,6 @@ { } - ReferencedInstance() - { - } - const std::string& GetStudyInstanceUid() const { return studyInstanceUid_; @@ -80,8 +78,16 @@ { return sopClassUid_; } + + void AddFrame(unsigned int frame); + + const std::set<unsigned int>& GetFrames() const + { + return frames_; + } }; + void AddStructure(const std::string& sopInstanceUid, DcmItem& group, bool hasFrameNumber, @@ -89,13 +95,75 @@ bool hasProbabilityOfCancer, float probabilityOfCancer); - std::map<std::string, ReferencedInstance> instancesInformation_; - std::vector<std::string> orderedInstances_; - std::list<Structure*> structures_; + std::map<std::string, ReferencedInstance*> instancesInformation_; + std::vector<std::string> orderedInstances_; + std::list<Structure*> structures_; public: + class Frame + { + private: + std::string studyInstanceUid_; + std::string seriesInstanceUid_; + std::string sopInstanceUid_; + std::string sopClassUid_; + unsigned int frameNumber_; + + public: + Frame(const std::string& studyInstanceUid, + const std::string& seriesInstanceUid, + const std::string& sopInstanceUid, + const std::string& sopClassUid, + unsigned int frameNumber) : + studyInstanceUid_(studyInstanceUid), + seriesInstanceUid_(seriesInstanceUid), + sopInstanceUid_(sopInstanceUid), + sopClassUid_(sopClassUid), + frameNumber_(frameNumber) + { + } + + const std::string& GetStudyInstanceUid() const + { + return studyInstanceUid_; + } + + const std::string& GetSeriesInstanceUid() const + { + return seriesInstanceUid_; + } + + const std::string& GetSopInstanceUid() const + { + return sopInstanceUid_; + } + + const std::string& GetSopClassUid() const + { + return sopClassUid_; + } + + unsigned int GetFrameNumber() const + { + return frameNumber_; + } + }; + DicomStructuredReport(Orthanc::ParsedDicomFile& dicom); ~DicomStructuredReport(); + + size_t GetReferencedInstancesCount() const + { + return orderedInstances_.size(); + } + + void GetReferencedInstance(std::string& studyInstanceUid, + std::string& seriesInstanceUid, + std::string& sopInstanceUid, + std::string& sopClassUid, + size_t i) const; + + void ExportOrderedFrames(std::list<Frame>& frames) const; }; }
--- a/OrthancStone/UnitTestsSources/UnitTestsMain.cpp Tue Nov 07 18:12:35 2023 +0100 +++ b/OrthancStone/UnitTestsSources/UnitTestsMain.cpp Tue Nov 07 19:30:44 2023 +0100 @@ -44,6 +44,7 @@ ASSERT_EQ(SopClassUid_VideoEndoscopicImageStorage, StringToSopClassUid("1.2.840.10008.5.1.4.1.1.77.1.1.1")); ASSERT_EQ(SopClassUid_VideoMicroscopicImageStorage, StringToSopClassUid("1.2.840.10008.5.1.4.1.1.77.1.2.1")); ASSERT_EQ(SopClassUid_VideoPhotographicImageStorage, StringToSopClassUid("1.2.840.10008.5.1.4.1.1.77.1.4.1")); + ASSERT_EQ(SopClassUid_ComprehensiveSR, StringToSopClassUid("1.2.840.10008.5.1.4.1.1.88.33")); ASSERT_EQ(SopClassUid_Other, StringToSopClassUid("nope")); ASSERT_EQ(SeriesThumbnailType_Pdf, GetSeriesThumbnailType(SopClassUid_EncapsulatedPdf));