# HG changeset patch # User Sebastien Jodogne # Date 1699451655 -3600 # Node ID 7c3d65166c266557ad95566c95792f3a69104f65 # Parent 51c8b21b81e4d69db931ccca97047e9109d0f191 partial integration dicom-sr->mainline diff -r 51c8b21b81e4 -r 7c3d65166c26 Applications/StoneWebViewer/WebAssembly/StoneWebViewer.cpp --- a/Applications/StoneWebViewer/WebAssembly/StoneWebViewer.cpp Tue Nov 07 18:11:13 2023 +0100 +++ b/Applications/StoneWebViewer/WebAssembly/StoneWebViewer.cpp Wed Nov 08 14:54:15 2023 +0100 @@ -204,6 +204,84 @@ +class IFramesCollection : public boost::noncopyable +{ +public: + virtual ~IFramesCollection() + { + } + + virtual size_t GetFramesCount() const = 0; + + virtual const OrthancStone::DicomInstanceParameters& GetInstanceOfFrame(size_t frameIndex) const = 0; + + virtual unsigned int GetFrameNumberInInstance(size_t frameIndex) const = 0; + + virtual bool LookupFrame(size_t& frameIndex, + const std::string& sopInstanceUid, + unsigned int frameNumber) const = 0; + + virtual bool FindClosestFrame(size_t& frameIndex, + const OrthancStone::Vector& point, + double maximumDistance) const = 0; + + static OrthancStone::CoordinateSystem3D GetFrameGeometry(const IFramesCollection& frames, + size_t frameIndex) + { + return frames.GetInstanceOfFrame(frameIndex).GetFrameGeometry(frames.GetFrameNumberInInstance(frameIndex)); + } +}; + + +class SortedFramesCollection : public IFramesCollection +{ +private: + std::unique_ptr frames_; + +public: + SortedFramesCollection(OrthancStone::SortedFrames* frames) + { + if (frames == NULL) + { + throw Orthanc::OrthancException(Orthanc::ErrorCode_NullPointer); + } + else + { + frames_.reset(frames); + } + } + + virtual size_t GetFramesCount() const ORTHANC_OVERRIDE + { + return frames_->GetFramesCount(); + } + + const OrthancStone::DicomInstanceParameters& GetInstanceOfFrame(size_t frameIndex) const ORTHANC_OVERRIDE + { + return frames_->GetInstanceOfFrame(frameIndex); + } + + virtual unsigned int GetFrameNumberInInstance(size_t frameIndex) const ORTHANC_OVERRIDE + { + return frames_->GetFrameNumberInInstance(frameIndex); + } + + virtual bool LookupFrame(size_t& frameIndex, + const std::string& sopInstanceUid, + unsigned int frameNumber) const ORTHANC_OVERRIDE + { + return frames_->LookupFrame(frameIndex, sopInstanceUid, frameNumber); + } + + virtual bool FindClosestFrame(size_t& frameIndex, + const OrthancStone::Vector& point, + double maximumDistance) const ORTHANC_OVERRIDE + { + return frames_->FindClosestFrame(frameIndex, point, maximumDistance); + }; +}; + + class VirtualSeries : public boost::noncopyable { private: diff -r 51c8b21b81e4 -r 7c3d65166c26 OrthancStone/Sources/Loaders/LoadedDicomResources.cpp --- a/OrthancStone/Sources/Loaders/LoadedDicomResources.cpp Tue Nov 07 18:11:13 2023 +0100 +++ b/OrthancStone/Sources/Loaders/LoadedDicomResources.cpp Wed Nov 08 14:54:15 2023 +0100 @@ -276,4 +276,22 @@ return true; } } + + + bool LoadedDicomResources::LookupResource(Orthanc::DicomMap& target, + const std::string& id) const + { + Resources::const_iterator it = resources_.find(id); + + if (it == resources_.end()) + { + return false; + } + else + { + assert(it->second != NULL); + target.Assign(it->second->GetDicom()); + return true; + } + } } diff -r 51c8b21b81e4 -r 7c3d65166c26 OrthancStone/Sources/Loaders/LoadedDicomResources.h --- a/OrthancStone/Sources/Loaders/LoadedDicomResources.h Tue Nov 07 18:11:13 2023 +0100 +++ b/OrthancStone/Sources/Loaders/LoadedDicomResources.h Wed Nov 08 14:54:15 2023 +0100 @@ -146,5 +146,8 @@ { return GetResourceInternal(index).GetSourceJson(); } + + bool LookupResource(Orthanc::DicomMap& target, + const std::string& id) const; }; } diff -r 51c8b21b81e4 -r 7c3d65166c26 OrthancStone/Sources/StoneEnumerations.cpp --- a/OrthancStone/Sources/StoneEnumerations.cpp Tue Nov 07 18:11:13 2023 +0100 +++ b/OrthancStone/Sources/StoneEnumerations.cpp Wed Nov 08 14:54:15 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; diff -r 51c8b21b81e4 -r 7c3d65166c26 OrthancStone/Sources/StoneEnumerations.h --- a/OrthancStone/Sources/StoneEnumerations.h Tue Nov 07 18:11:13 2023 +0100 +++ b/OrthancStone/Sources/StoneEnumerations.h Wed Nov 08 14:54:15 2023 +0100 @@ -116,7 +116,8 @@ SopClassUid_VideoEndoscopicImageStorage, SopClassUid_VideoMicroscopicImageStorage, SopClassUid_VideoPhotographicImageStorage, - SopClassUid_DicomSeg + SopClassUid_DicomSeg, + SopClassUid_ComprehensiveSR }; enum SeriesThumbnailType diff -r 51c8b21b81e4 -r 7c3d65166c26 OrthancStone/Sources/Toolbox/DicomStructuredReport.cpp --- a/OrthancStone/Sources/Toolbox/DicomStructuredReport.cpp Tue Nov 07 18:11:13 2023 +0100 +++ b/OrthancStone/Sources/Toolbox/DicomStructuredReport.cpp Wed Nov 08 14:54:15 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 - 1); + } + } + + class DicomStructuredReport::Structure : public boost::noncopyable { private: @@ -148,7 +161,7 @@ else { hasFrameNumber_ = true; - frameNumber_ = frame; + frameNumber_ = frame - 1; } } @@ -326,6 +339,10 @@ { DcmDataset& dataset = *dicom.GetDcmtkObject().getDataset(); + studyInstanceUid_ = GetStringValue(dataset, DCM_StudyInstanceUID); + seriesInstanceUid_ = GetStringValue(dataset, DCM_SeriesInstanceUID); + sopInstanceUid_ = GetStringValue(dataset, DCM_SOPInstanceUID); + CheckStringValue(dataset, DCM_Modality, "SR"); CheckStringValue(dataset, DCM_SOPClassUID, "1.2.840.10008.5.1.4.1.1.88.33"); // Comprehensive SR IOD CheckStringValue(dataset, DCM_ValueType, "CONTAINER"); @@ -369,7 +386,7 @@ if (instancesInformation_.find(sopInstanceUid) == instancesInformation_.end()) { - instancesInformation_[sopInstanceUid] = ReferencedInstance(studyInstanceUid, seriesInstanceUid, sopClassUid); + instancesInformation_[sopInstanceUid] = new ReferencedInstance(studyInstanceUid, seriesInstanceUid, sopClassUid); } else { @@ -468,12 +485,16 @@ } std::string sopInstanceUid = GetStringValue(*instances.getItem(0), DCM_ReferencedSOPInstanceUID); - if (instancesInformation_.find(sopInstanceUid) == instancesInformation_.end()) + std::map::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 +511,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 +538,64 @@ assert(*it != NULL); delete *it; } + + for (std::map::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::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::ExportReferencedFrames(std::list& frames) const + { + frames.clear(); + + for (size_t i = 0; i < orderedInstances_.size(); i++) + { + std::map::const_iterator found = instancesInformation_.find(orderedInstances_[i]); + if (found == instancesInformation_.end()) + { + throw Orthanc::OrthancException(Orthanc::ErrorCode_InternalError); + } + + assert(found->second != NULL); + + for (std::set::const_iterator frame = found->second->GetFrames().begin(); + frame != found->second->GetFrames().end(); ++frame) + { + frames.push_back(ReferencedFrame(found->second->GetStudyInstanceUid(), + found->second->GetSeriesInstanceUid(), + orderedInstances_[i], + found->second->GetSopClassUid(), *frame)); + } + } } } diff -r 51c8b21b81e4 -r 7c3d65166c26 OrthancStone/Sources/Toolbox/DicomStructuredReport.h --- a/OrthancStone/Sources/Toolbox/DicomStructuredReport.h Tue Nov 07 18:11:13 2023 +0100 +++ b/OrthancStone/Sources/Toolbox/DicomStructuredReport.h Wed Nov 08 14:54:15 2023 +0100 @@ -35,6 +35,7 @@ #include #include +#include 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 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& GetFrames() const + { + return frames_; + } }; + void AddStructure(const std::string& sopInstanceUid, DcmItem& group, bool hasFrameNumber, @@ -89,13 +95,93 @@ bool hasProbabilityOfCancer, float probabilityOfCancer); - std::map instancesInformation_; - std::vector orderedInstances_; - std::list structures_; + std::string studyInstanceUid_; + std::string seriesInstanceUid_; + std::string sopInstanceUid_; + std::map instancesInformation_; + std::vector orderedInstances_; + std::list structures_; public: + class ReferencedFrame + { + private: + std::string studyInstanceUid_; + std::string seriesInstanceUid_; + std::string sopInstanceUid_; + std::string sopClassUid_; + unsigned int frameNumber_; + + public: + ReferencedFrame(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(); + + const std::string& GetStudyInstanceUid() const + { + return studyInstanceUid_; + } + + const std::string& GetSeriesInstanceUid() const + { + return seriesInstanceUid_; + } + + const std::string& GetSopInstanceUid() const + { + return sopInstanceUid_; + } + + 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 ExportReferencedFrames(std::list& frames) const; }; } diff -r 51c8b21b81e4 -r 7c3d65166c26 OrthancStone/UnitTestsSources/UnitTestsMain.cpp --- a/OrthancStone/UnitTestsSources/UnitTestsMain.cpp Tue Nov 07 18:11:13 2023 +0100 +++ b/OrthancStone/UnitTestsSources/UnitTestsMain.cpp Wed Nov 08 14:54:15 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));