# HG changeset patch # User Sebastien Jodogne # Date 1559045386 -7200 # Node ID d3197e0e321dd98fca74c69dd6fa9d7a9aefed31 # Parent bc20e4c417ecd0a58b0cc04f102ea5d01f658cab DicomStructureSetLoader is working diff -r bc20e4c417ec -r d3197e0e321d Framework/Oracle/OracleCommandWithPayload.cpp --- a/Framework/Oracle/OracleCommandWithPayload.cpp Tue May 28 13:02:56 2019 +0200 +++ b/Framework/Oracle/OracleCommandWithPayload.cpp Tue May 28 14:09:46 2019 +0200 @@ -38,7 +38,7 @@ } - const Orthanc::IDynamicObject& OracleCommandWithPayload::GetPayload() const + Orthanc::IDynamicObject& OracleCommandWithPayload::GetPayload() const { if (HasPayload()) { diff -r bc20e4c417ec -r d3197e0e321d Framework/Oracle/OracleCommandWithPayload.h --- a/Framework/Oracle/OracleCommandWithPayload.h Tue May 28 13:02:56 2019 +0200 +++ b/Framework/Oracle/OracleCommandWithPayload.h Tue May 28 14:09:46 2019 +0200 @@ -42,7 +42,7 @@ return (payload_.get() != NULL); } - const Orthanc::IDynamicObject& GetPayload() const; + Orthanc::IDynamicObject& GetPayload() const; Orthanc::IDynamicObject* ReleasePayload(); }; diff -r bc20e4c417ec -r d3197e0e321d Samples/Sdl/Loader.cpp --- a/Samples/Sdl/Loader.cpp Tue May 28 13:02:56 2019 +0200 +++ b/Samples/Sdl/Loader.cpp Tue May 28 14:09:46 2019 +0200 @@ -492,6 +492,7 @@ + // TODO - Refactor using LoaderStateMachine? class OrthancSeriesVolumeProgressiveLoader : public IObserver, public IObservable, @@ -1005,17 +1006,17 @@ return dynamic_cast(that_); } - virtual void Handle(const OrthancRestApiCommand::SuccessMessage& message) const + virtual void Handle(const OrthancRestApiCommand::SuccessMessage& message) { throw Orthanc::OrthancException(Orthanc::ErrorCode_NotImplemented); } - virtual void Handle(const GetOrthancImageCommand::SuccessMessage& message) const + virtual void Handle(const GetOrthancImageCommand::SuccessMessage& message) { throw Orthanc::OrthancException(Orthanc::ErrorCode_NotImplemented); } - virtual void Handle(const GetOrthancWebViewerJpegCommand::SuccessMessage& message) const + virtual void Handle(const GetOrthancWebViewerJpegCommand::SuccessMessage& message) { throw Orthanc::OrthancException(Orthanc::ErrorCode_NotImplemented); } @@ -1075,20 +1076,32 @@ { delete *it; } + + pendingCommands_.clear(); } - void HandleException(const OracleCommandExceptionMessage& message) + void HandleExceptionMessage(const OracleCommandExceptionMessage& message) { LOG(ERROR) << "Error in the state machine, stopping all processing"; Clear(); } template - void Handle(const T& message) + void HandleSuccessMessage(const T& message) { - dynamic_cast(message.GetOrigin().GetPayload()).Handle(message); + assert(activeCommands_ > 0); activeCommands_--; - Step(); + + try + { + dynamic_cast(message.GetOrigin().GetPayload()).Handle(message); + Step(); + } + catch (Orthanc::OrthancException& e) + { + LOG(ERROR) << "Error in the state machine, stopping all processing: " << e.What(); + Clear(); + } } typedef std::list PendingCommands; @@ -1110,19 +1123,19 @@ { oracleObservable.RegisterObserverCallback( new Callable - (*this, &LoaderStateMachine::Handle)); + (*this, &LoaderStateMachine::HandleSuccessMessage)); oracleObservable.RegisterObserverCallback( new Callable - (*this, &LoaderStateMachine::Handle)); + (*this, &LoaderStateMachine::HandleSuccessMessage)); oracleObservable.RegisterObserverCallback( new Callable - (*this, &LoaderStateMachine::Handle)); + (*this, &LoaderStateMachine::HandleSuccessMessage)); oracleObservable.RegisterObserverCallback( new Callable - (*this, &LoaderStateMachine::HandleException)); + (*this, &LoaderStateMachine::HandleExceptionMessage)); } virtual ~LoaderStateMachine() @@ -1177,7 +1190,7 @@ } - virtual void Handle(const OrthancRestApiCommand::SuccessMessage& message) const + virtual void Handle(const OrthancRestApiCommand::SuccessMessage& message) { // Complete the DICOM tags with just-received "Grid Frame Offset Vector" std::string s = Orthanc::Toolbox::StripSpaces(message.GetAnswer()); @@ -1211,7 +1224,7 @@ { } - virtual void Handle(const OrthancRestApiCommand::SuccessMessage& message) const + virtual void Handle(const OrthancRestApiCommand::SuccessMessage& message) { OrthancMultiframeVolumeLoader& loader = GetLoader(); @@ -1255,7 +1268,7 @@ { } - virtual void Handle(const OrthancRestApiCommand::SuccessMessage& message) const + virtual void Handle(const OrthancRestApiCommand::SuccessMessage& message) { GetLoader().SetTransferSyntax(message.GetAnswer()); } @@ -1270,7 +1283,7 @@ { } - virtual void Handle(const OrthancRestApiCommand::SuccessMessage& message) const + virtual void Handle(const OrthancRestApiCommand::SuccessMessage& message) { GetLoader().SetUncompressedPixelData(message.GetAnswer()); } @@ -1491,17 +1504,17 @@ - class VolumeImageReslicer : public IVolumeSlicer + class DicomVolumeImageReslicer : public IVolumeSlicer { private: class Slice : public IExtractedSlice { private: - VolumeImageReslicer& that_; - CoordinateSystem3D cuttingPlane_; + DicomVolumeImageReslicer& that_; + CoordinateSystem3D cuttingPlane_; public: - Slice(VolumeImageReslicer& that, + Slice(DicomVolumeImageReslicer& that, const CoordinateSystem3D& cuttingPlane) : that_(that), cuttingPlane_(cuttingPlane) @@ -1518,7 +1531,7 @@ return that_.volume_->GetRevision(); } - virtual ISceneLayer* CreateSceneLayer(const ILayerStyleConfigurator* configurator, // possibly absent + virtual ISceneLayer* CreateSceneLayer(const ILayerStyleConfigurator* configurator, const CoordinateSystem3D& cuttingPlane) { VolumeReslicer& reslicer = that_.reslicer_; @@ -1564,7 +1577,7 @@ VolumeReslicer reslicer_; public: - VolumeImageReslicer(const boost::shared_ptr& volume) : + DicomVolumeImageReslicer(const boost::shared_ptr& volume) : volume_(volume) { if (volume.get() == NULL) @@ -1609,34 +1622,134 @@ class DicomStructureSetLoader : - public IObserver, + public LoaderStateMachine, public IVolumeSlicer { private: + class AddReferencedInstance : public State + { + private: + std::string instanceId_; + + public: + AddReferencedInstance(DicomStructureSetLoader& that, + const std::string& instanceId) : + State(that), + instanceId_(instanceId) + { + } + + virtual void Handle(const OrthancRestApiCommand::SuccessMessage& message) + { + Json::Value tags; + message.ParseJsonBody(tags); + + Orthanc::DicomMap dicom; + dicom.FromDicomAsJson(tags); + + DicomStructureSetLoader& loader = GetLoader(); + loader.content_->AddReferencedSlice(dicom); + + loader.countProcessedInstances_ ++; + assert(loader.countProcessedInstances_ <= loader.countReferencedInstances_); + + if (loader.countProcessedInstances_ == loader.countReferencedInstances_) + { + // All the referenced instances have been loaded, finalize the RT-STRUCT + loader.content_->CheckReferencedSlices(); + loader.revision_++; + } + } + }; + + // State that converts a "SOP Instance UID" to an Orthanc identifier + class LookupInstance : public State + { + private: + std::string sopInstanceUid_; + + public: + LookupInstance(DicomStructureSetLoader& that, + const std::string& sopInstanceUid) : + State(that), + sopInstanceUid_(sopInstanceUid) + { + } + + virtual void Handle(const OrthancRestApiCommand::SuccessMessage& message) + { + DicomStructureSetLoader& loader = GetLoader(); + + Json::Value lookup; + message.ParseJsonBody(lookup); + + if (lookup.type() != Json::arrayValue || + lookup.size() != 1 || + !lookup[0].isMember("Type") || + !lookup[0].isMember("Path") || + lookup[0]["Type"].type() != Json::stringValue || + lookup[0]["ID"].type() != Json::stringValue || + lookup[0]["Type"].asString() != "Instance") + { + throw Orthanc::OrthancException(Orthanc::ErrorCode_UnknownResource); + } + + const std::string instanceId = lookup[0]["ID"].asString(); + + { + std::auto_ptr command(new OrthancRestApiCommand); + command->SetHttpHeader("Accept-Encoding", "gzip"); + command->SetUri("/instances/" + instanceId + "/tags"); + command->SetPayload(new AddReferencedInstance(loader, instanceId)); + Schedule(command.release()); + } + } + }; + + class LoadStructure : public State + { + public: + LoadStructure(DicomStructureSetLoader& that) : + State(that) + { + } + + virtual void Handle(const OrthancRestApiCommand::SuccessMessage& message) + { + DicomStructureSetLoader& loader = GetLoader(); + + { + OrthancPlugins::FullOrthancDataset dicom(message.GetAnswer()); + loader.content_.reset(new DicomStructureSet(dicom)); + } + + std::set instances; + loader.content_->GetReferencedInstances(instances); + + loader.countReferencedInstances_ = instances.size(); + + for (std::set::const_iterator + it = instances.begin(); it != instances.end(); ++it) + { + std::auto_ptr command(new OrthancRestApiCommand); + command->SetUri("/tools/lookup"); + command->SetMethod(Orthanc::HttpMethod_Post); + command->SetBody(*it); + command->SetPayload(new LookupInstance(loader, *it)); + Schedule(command.release()); + + printf("[%s]\n", it->c_str()); + } + } + }; + + + std::auto_ptr content_; - IOracle& oracle_; - bool active_; uint64_t revision_; std::string instanceId_; - - void Handle(const OrthancRestApiCommand::SuccessMessage& message) - { - assert(active_); - - { - OrthancPlugins::FullOrthancDataset dicom(message.GetAnswer()); - content_.reset(new DicomStructureSet(dicom)); - } - - std::set instances; - content_->GetReferencedInstances(instances); - - for (std::set::const_iterator - it = instances.begin(); it != instances.end(); ++it) - { - printf("[%s]\n", it->c_str()); - } - } + unsigned int countProcessedInstances_; + unsigned int countReferencedInstances_; class Slice : public IExtractedSlice @@ -1678,6 +1791,7 @@ assert(isValid_); std::auto_ptr layer(new PolylineSceneLayer); + layer->SetThickness(2); for (size_t i = 0; i < content_.GetStructuresCount(); i++) { @@ -1685,14 +1799,12 @@ if (content_.ProjectStructure(polygons, i, cuttingPlane)) { - printf(">> %d\n", polygons.size()); - for (size_t j = 0; j < polygons.size(); j++) { PolylineSceneLayer::Chain chain; chain.resize(polygons[j].size()); - for (size_t k = 0; k < polygons[i].size(); k++) + for (size_t k = 0; k < polygons[j].size(); k++) { chain[k] = ScenePoint2D(polygons[j][k].first, polygons[j][k].second); } @@ -1702,8 +1814,6 @@ } } - printf("OK\n"); - return layer.release(); } }; @@ -1711,34 +1821,26 @@ public: DicomStructureSetLoader(IOracle& oracle, IObservable& oracleObservable) : - IObserver(oracleObservable.GetBroker()), - oracle_(oracle), - active_(false), - revision_(0) + LoaderStateMachine(oracle, oracleObservable), + revision_(0), + countProcessedInstances_(0), + countReferencedInstances_(0) { - oracleObservable.RegisterObserverCallback( - new Callable - (*this, &DicomStructureSetLoader::Handle)); } void LoadInstance(const std::string& instanceId) { - if (active_) - { - throw Orthanc::OrthancException(Orthanc::ErrorCode_BadSequenceOfCalls); - } - else + Start(); + + instanceId_ = instanceId; + { - active_ = true; - instanceId_ = instanceId; - - { - std::auto_ptr command(new OrthancRestApiCommand); - command->SetHttpHeader("Accept-Encoding", "gzip"); - command->SetUri("/instances/" + instanceId + "/tags?ignore-length=3006-0050"); - oracle_.Schedule(*this, command.release()); - } + std::auto_ptr command(new OrthancRestApiCommand); + command->SetHttpHeader("Accept-Encoding", "gzip"); + command->SetUri("/instances/" + instanceId + "/tags?ignore-length=3006-0050"); + command->SetPayload(new LoadStructure(*this)); + Schedule(command.release()); } } @@ -2181,7 +2283,7 @@ toto->SetVolume1(0, ctLoader, new OrthancStone::GrayscaleStyleConfigurator); #else { - boost::shared_ptr reslicer(new OrthancStone::VolumeImageReslicer(ct)); + boost::shared_ptr reslicer(new OrthancStone::DicomVolumeImageReslicer(ct)); toto->SetVolume1(0, reslicer, new OrthancStone::GrayscaleStyleConfigurator); } #endif @@ -2277,9 +2379,14 @@ // 2017-11-17-Anonymized //ctLoader->LoadSeries("cb3ea4d1-d08f3856-ad7b6314-74d88d77-60b05618"); // CT - doseLoader->LoadInstance("41029085-71718346-811efac4-420e2c15-d39f99b6"); // RT-DOSE + //doseLoader->LoadInstance("41029085-71718346-811efac4-420e2c15-d39f99b6"); // RT-DOSE //rtstructLoader->LoadInstance("83d9c0c3-913a7fee-610097d7-cbf0522d-fd75bee6"); // RT-STRUCT + // 2017-05-16 + ctLoader->LoadSeries("a04ecf01-79b2fc33-58239f7e-ad9db983-28e81afa"); // CT + doseLoader->LoadInstance("eac822ef-a395f94e-e8121fe0-8411fef8-1f7bffad"); // RT-DOSE + rtstructLoader->LoadInstance("54460695-ba3885ee-ddf61ac0-f028e31d-a6e474d9"); // RT-STRUCT + // 2015-01-28-Multiframe //doseLoader->LoadInstance("88f71e2a-5fad1c61-96ed14d6-5b3d3cf7-a5825279"); // Multiframe CT