Mercurial > hg > orthanc-stone
diff Framework/Toolbox/OrthancSlicesLoader.cpp @ 73:ffa6dded91bd wasm
reorganization
author | Sebastien Jodogne <s.jodogne@gmail.com> |
---|---|
date | Wed, 24 May 2017 11:59:24 +0200 |
parents | |
children | f5f54ed8d307 |
line wrap: on
line diff
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/Framework/Toolbox/OrthancSlicesLoader.cpp Wed May 24 11:59:24 2017 +0200 @@ -0,0 +1,394 @@ +/** + * Stone of Orthanc + * Copyright (C) 2012-2016 Sebastien Jodogne, Medical Physics + * Department, University Hospital of Liege, Belgium + * Copyright (C) 2017 Osimis, Belgium + * + * This program is free software: you can redistribute it and/or + * modify it under the terms of the GNU Affero General Public License + * as published by the Free Software Foundation, either version 3 of + * the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Affero General Public License for more details. + * + * You should have received a copy of the GNU Affero General Public License + * along with this program. If not, see <http://www.gnu.org/licenses/>. + **/ + + +#include "OrthancSlicesLoader.h" + +#include "MessagingToolbox.h" + +#include "../../Resources/Orthanc/Core/Images/PngReader.h" +#include "../../Resources/Orthanc/Core/Logging.h" +#include "../../Resources/Orthanc/Core/OrthancException.h" +#include "../../Resources/Orthanc/Plugins/Samples/Common/DicomDatasetReader.h" +#include "../../Resources/Orthanc/Plugins/Samples/Common/FullOrthancDataset.h" + +#include <boost/lexical_cast.hpp> + +namespace OrthancStone +{ + class OrthancSlicesLoader::Operation : public Orthanc::IDynamicObject + { + private: + Mode mode_; + unsigned int frame_; + unsigned int sliceIndex_; + const Slice* slice_; + std::string instanceId_; + + Operation(Mode mode) : + mode_(mode) + { + } + + public: + Mode GetMode() const + { + return mode_; + } + + unsigned int GetSliceIndex() const + { + assert(mode_ == Mode_LoadImage); + return sliceIndex_; + } + + const Slice& GetSlice() const + { + assert(mode_ == Mode_LoadImage && slice_ != NULL); + return *slice_; + } + + unsigned int GetFrame() const + { + assert(mode_ == Mode_InstanceGeometry); + return frame_; + } + + const std::string& GetInstanceId() const + { + assert(mode_ == Mode_InstanceGeometry); + return instanceId_; + } + + static Operation* DownloadSeriesGeometry() + { + return new Operation(Mode_SeriesGeometry); + } + + static Operation* DownloadInstanceGeometry(const std::string& instanceId, + unsigned int frame) + { + std::auto_ptr<Operation> operation(new Operation(Mode_InstanceGeometry)); + operation->instanceId_ = instanceId; + operation->frame_ = frame; + return operation.release(); + } + + static Operation* DownloadSliceImage(unsigned int sliceIndex, + const Slice& slice) + { + std::auto_ptr<Operation> tmp(new Operation(Mode_LoadImage)); + tmp->sliceIndex_ = sliceIndex; + tmp->slice_ = &slice; + return tmp.release(); + } + }; + + + class OrthancSlicesLoader::WebCallback : public IWebService::ICallback + { + private: + OrthancSlicesLoader& that_; + + public: + WebCallback(OrthancSlicesLoader& that) : + that_(that) + { + } + + virtual void NotifySuccess(const std::string& uri, + const void* answer, + size_t answerSize, + Orthanc::IDynamicObject* payload) + { + std::auto_ptr<Operation> operation(dynamic_cast<Operation*>(payload)); + + switch (operation->GetMode()) + { + case Mode_SeriesGeometry: + that_.ParseSeriesGeometry(answer, answerSize); + break; + + case Mode_InstanceGeometry: + that_.ParseInstanceGeometry(operation->GetInstanceId(), + operation->GetFrame(), answer, answerSize); + break; + + case Mode_LoadImage: + that_.ParseSliceImage(*operation, answer, answerSize); + break; + + default: + throw Orthanc::OrthancException(Orthanc::ErrorCode_InternalError); + } + } + + virtual void NotifyError(const std::string& uri, + Orthanc::IDynamicObject* payload) + { + std::auto_ptr<Operation> operation(dynamic_cast<Operation*>(payload)); + LOG(ERROR) << "Cannot download " << uri; + + switch (operation->GetMode()) + { + case Mode_SeriesGeometry: + that_.userCallback_.NotifyGeometryError(that_); + that_.state_ = State_Error; + break; + + case Mode_LoadImage: + that_.userCallback_.NotifySliceImageError(that_, operation->GetSliceIndex(), + operation->GetSlice()); + break; + + default: + throw Orthanc::OrthancException(Orthanc::ErrorCode_InternalError); + } + } + }; + + + void OrthancSlicesLoader::ParseSeriesGeometry(const void* answer, + size_t size) + { + Json::Value series; + if (!MessagingToolbox::ParseJson(series, answer, size) || + series.type() != Json::objectValue) + { + userCallback_.NotifyGeometryError(*this); + return; + } + + Json::Value::Members instances = series.getMemberNames(); + + slices_.Reserve(instances.size()); + + for (size_t i = 0; i < instances.size(); i++) + { + OrthancPlugins::FullOrthancDataset dataset(series[instances[i]]); + + Slice slice; + if (slice.ParseOrthancFrame(dataset, instances[i], 0 /* todo */)) + { + slices_.AddSlice(slice); + } + else + { + LOG(WARNING) << "Skipping invalid instance " << instances[i]; + } + } + + bool ok = false; + + if (slices_.GetSliceCount() > 0) + { + Vector normal; + if (slices_.SelectNormal(normal)) + { + slices_.FilterNormal(normal); + slices_.SetNormal(normal); + slices_.Sort(); + ok = true; + } + } + + state_ = State_GeometryReady; + + if (ok) + { + LOG(INFO) << "Loaded a series with " << slices_.GetSliceCount() << " slice(s)"; + userCallback_.NotifyGeometryReady(*this); + } + else + { + LOG(ERROR) << "This series is empty"; + userCallback_.NotifyGeometryError(*this); + } + } + + + void OrthancSlicesLoader::ParseInstanceGeometry(const std::string& instanceId, + unsigned int frame, + const void* answer, + size_t size) + { + Json::Value tags; + if (!MessagingToolbox::ParseJson(tags, answer, size) || + tags.type() != Json::objectValue) + { + userCallback_.NotifyGeometryError(*this); + return; + } + + OrthancPlugins::FullOrthancDataset dataset(tags); + + state_ = State_GeometryReady; + + Slice slice; + if (slice.ParseOrthancFrame(dataset, instanceId, frame)) + { + LOG(INFO) << "Loaded instance " << instanceId; + slices_.AddSlice(slice); + userCallback_.NotifyGeometryReady(*this); + } + else + { + LOG(WARNING) << "Skipping invalid instance " << instanceId; + userCallback_.NotifyGeometryError(*this); + } + } + + + void OrthancSlicesLoader::ParseSliceImage(const Operation& operation, + const void* answer, + size_t size) + { + std::auto_ptr<Orthanc::PngReader> image(new Orthanc::PngReader); + image->ReadFromMemory(answer, size); + + bool ok = (image->GetWidth() == operation.GetSlice().GetWidth() || + image->GetHeight() == operation.GetSlice().GetHeight()); + + if (ok && + operation.GetSlice().GetConverter().GetExpectedPixelFormat() == + Orthanc::PixelFormat_SignedGrayscale16) + { + if (image->GetFormat() == Orthanc::PixelFormat_Grayscale16) + { + image->SetFormat(Orthanc::PixelFormat_SignedGrayscale16); + } + else + { + ok = false; + } + } + + if (ok) + { + userCallback_.NotifySliceImageReady(*this, operation.GetSliceIndex(), + operation.GetSlice(), image.release()); + } + else + { + userCallback_.NotifySliceImageError(*this, operation.GetSliceIndex(), + operation.GetSlice()); + } + } + + + OrthancSlicesLoader::OrthancSlicesLoader(ICallback& callback, + IWebService& orthanc) : + webCallback_(new WebCallback(*this)), + userCallback_(callback), + orthanc_(orthanc), + state_(State_Initialization) + { + } + + + void OrthancSlicesLoader::ScheduleLoadSeries(const std::string& seriesId) + { + if (state_ != State_Initialization) + { + throw Orthanc::OrthancException(Orthanc::ErrorCode_BadSequenceOfCalls); + } + else + { + state_ = State_LoadingGeometry; + std::string uri = "/series/" + seriesId + "/instances-tags"; + orthanc_.ScheduleGetRequest(*webCallback_, uri, Operation::DownloadSeriesGeometry()); + } + } + + + void OrthancSlicesLoader::ScheduleLoadInstance(const std::string& instanceId, + unsigned int frame) + { + if (state_ != State_Initialization) + { + throw Orthanc::OrthancException(Orthanc::ErrorCode_BadSequenceOfCalls); + } + else + { + state_ = State_LoadingGeometry; + std::string uri = "/instances/" + instanceId + "/tags"; + orthanc_.ScheduleGetRequest + (*webCallback_, uri, Operation::DownloadInstanceGeometry(instanceId, frame)); + } + } + + + size_t OrthancSlicesLoader::GetSliceCount() const + { + if (state_ != State_GeometryReady) + { + throw Orthanc::OrthancException(Orthanc::ErrorCode_BadSequenceOfCalls); + } + + return slices_.GetSliceCount(); + } + + + const Slice& OrthancSlicesLoader::GetSlice(size_t index) const + { + if (state_ != State_GeometryReady) + { + throw Orthanc::OrthancException(Orthanc::ErrorCode_BadSequenceOfCalls); + } + + return slices_.GetSlice(index); + } + + + void OrthancSlicesLoader::ScheduleLoadSliceImage(size_t index) + { + if (state_ != State_GeometryReady) + { + throw Orthanc::OrthancException(Orthanc::ErrorCode_BadSequenceOfCalls); + } + else + { + const Slice& slice = GetSlice(index); + + std::string uri = ("/instances/" + slice.GetOrthancInstanceId() + "/frames/" + + boost::lexical_cast<std::string>(slice.GetFrame())); + + switch (slice.GetConverter().GetExpectedPixelFormat()) + { + case Orthanc::PixelFormat_RGB24: + uri += "/preview"; + break; + + case Orthanc::PixelFormat_Grayscale16: + uri += "/image-uint16"; + break; + + case Orthanc::PixelFormat_SignedGrayscale16: + uri += "/image-int16"; + break; + + default: + throw Orthanc::OrthancException(Orthanc::ErrorCode_ParameterOutOfRange); + } + + orthanc_.ScheduleGetRequest(*webCallback_, uri, Operation::DownloadSliceImage(index, slice)); + } + } +}