Mercurial > hg > orthanc-stone
diff UnitTestsSources/UnitTestsMain.cpp @ 73:ffa6dded91bd wasm
reorganization
author | Sebastien Jodogne <s.jodogne@gmail.com> |
---|---|
date | Wed, 24 May 2017 11:59:24 +0200 |
parents | c1cc3bdba18c |
children | f5f54ed8d307 |
line wrap: on
line diff
--- a/UnitTestsSources/UnitTestsMain.cpp Wed May 24 10:36:41 2017 +0200 +++ b/UnitTestsSources/UnitTestsMain.cpp Wed May 24 11:59:24 2017 +0200 @@ -22,15 +22,8 @@ #include "gtest/gtest.h" #include "../Framework/Toolbox/OrthancAsynchronousWebService.h" +#include "../Framework/Toolbox/OrthancSlicesLoader.h" #include "../Resources/Orthanc/Core/Logging.h" -#include "../Framework/Toolbox/OrthancSynchronousWebService.h" -#include "../Framework/Layers/OrthancFrameLayerSource.h" -#include "../Framework/Widgets/LayerWidget.h" - - -#include "../Resources/Orthanc/Core/Images/PngReader.h" -#include "../Framework/Toolbox/MessagingToolbox.h" -#include "../Framework/Toolbox/DicomFrameConverter.h" #include <boost/lexical_cast.hpp> #include <boost/date_time/posix_time/posix_time.hpp> @@ -38,766 +31,6 @@ namespace OrthancStone { - class Slice - { - private: - enum Type - { - Type_Invalid, - Type_OrthancInstance - // TODO A slice could come from some DICOM file (URL) - }; - - Type type_; - std::string orthancInstanceId_; - unsigned int frame_; - SliceGeometry geometry_; - double pixelSpacingX_; - double pixelSpacingY_; - double thickness_; - unsigned int width_; - unsigned int height_; - DicomFrameConverter converter_; - - public: - Slice() : type_(Type_Invalid) - { - } - - bool ParseOrthancFrame(const OrthancPlugins::IDicomDataset& dataset, - const std::string& instanceId, - unsigned int frame) - { - OrthancPlugins::DicomDatasetReader reader(dataset); - - unsigned int frameCount; - if (!reader.GetUnsignedIntegerValue(frameCount, OrthancPlugins::DICOM_TAG_NUMBER_OF_FRAMES)) - { - frameCount = 1; // Assume instance with one frame - } - - if (frame >= frameCount) - { - return false; - } - - if (!reader.GetDoubleValue(thickness_, OrthancPlugins::DICOM_TAG_SLICE_THICKNESS)) - { - thickness_ = 100.0 * std::numeric_limits<double>::epsilon(); - } - - GeometryToolbox::GetPixelSpacing(pixelSpacingX_, pixelSpacingY_, dataset); - - std::string position, orientation; - if (dataset.GetStringValue(position, OrthancPlugins::DICOM_TAG_IMAGE_POSITION_PATIENT) && - dataset.GetStringValue(orientation, OrthancPlugins::DICOM_TAG_IMAGE_ORIENTATION_PATIENT)) - { - geometry_ = SliceGeometry(position, orientation); - } - - if (reader.GetUnsignedIntegerValue(width_, OrthancPlugins::DICOM_TAG_COLUMNS) && - reader.GetUnsignedIntegerValue(height_, OrthancPlugins::DICOM_TAG_ROWS)) - { - orthancInstanceId_ = instanceId; - frame_ = frame; - converter_.ReadParameters(dataset); - - type_ = Type_OrthancInstance; - return true; - } - else - { - return false; - } - } - - bool IsOrthancInstance() const - { - return type_ == Type_OrthancInstance; - } - - const std::string GetOrthancInstanceId() const - { - if (type_ != Type_OrthancInstance) - { - throw Orthanc::OrthancException(Orthanc::ErrorCode_BadSequenceOfCalls); - } - - return orthancInstanceId_; - } - - unsigned int GetFrame() const - { - if (type_ == Type_Invalid) - { - throw Orthanc::OrthancException(Orthanc::ErrorCode_BadSequenceOfCalls); - } - - return frame_; - } - - const SliceGeometry& GetGeometry() const - { - if (type_ == Type_Invalid) - { - throw Orthanc::OrthancException(Orthanc::ErrorCode_BadSequenceOfCalls); - } - - return geometry_; - } - - double GetThickness() const - { - if (type_ == Type_Invalid) - { - throw Orthanc::OrthancException(Orthanc::ErrorCode_BadSequenceOfCalls); - } - - return thickness_; - } - - double GetPixelSpacingX() const - { - if (type_ == Type_Invalid) - { - throw Orthanc::OrthancException(Orthanc::ErrorCode_BadSequenceOfCalls); - } - - return pixelSpacingX_; - } - - double GetPixelSpacingY() const - { - if (type_ == Type_Invalid) - { - throw Orthanc::OrthancException(Orthanc::ErrorCode_BadSequenceOfCalls); - } - - return pixelSpacingY_; - } - - unsigned int GetWidth() const - { - if (type_ == Type_Invalid) - { - throw Orthanc::OrthancException(Orthanc::ErrorCode_BadSequenceOfCalls); - } - - return width_; - } - - unsigned int GetHeight() const - { - if (type_ == Type_Invalid) - { - throw Orthanc::OrthancException(Orthanc::ErrorCode_BadSequenceOfCalls); - } - - return height_; - } - - const DicomFrameConverter& GetConverter() const - { - if (type_ == Type_Invalid) - { - throw Orthanc::OrthancException(Orthanc::ErrorCode_BadSequenceOfCalls); - } - - return converter_; - } - }; - - - class SliceSorter : public boost::noncopyable - { - private: - class SliceWithDepth : public boost::noncopyable - { - private: - Slice slice_; - double depth_; - - public: - SliceWithDepth(const Slice& slice) : - slice_(slice), - depth_(0) - { - } - - void SetNormal(const Vector& normal) - { - depth_ = boost::numeric::ublas::inner_prod - (slice_.GetGeometry().GetOrigin(), normal); - } - - double GetDepth() const - { - return depth_; - } - - const Slice& GetSlice() const - { - return slice_; - } - }; - - struct Comparator - { - bool operator() (const SliceWithDepth* const& a, - const SliceWithDepth* const& b) const - { - return a->GetDepth() < b->GetDepth(); - } - }; - - typedef std::vector<SliceWithDepth*> Slices; - - Slices slices_; - bool hasNormal_; - - public: - SliceSorter() : hasNormal_(false) - { - } - - ~SliceSorter() - { - for (size_t i = 0; i < slices_.size(); i++) - { - assert(slices_[i] != NULL); - delete slices_[i]; - } - } - - void Reserve(size_t count) - { - slices_.reserve(count); - } - - void AddSlice(const Slice& slice) - { - slices_.push_back(new SliceWithDepth(slice)); - } - - size_t GetSliceCount() const - { - return slices_.size(); - } - - const Slice& GetSlice(size_t i) const - { - if (i >= slices_.size()) - { - throw Orthanc::OrthancException(Orthanc::ErrorCode_ParameterOutOfRange); - } - - assert(slices_[i] != NULL); - return slices_[i]->GetSlice(); - } - - void SetNormal(const Vector& normal) - { - for (size_t i = 0; i < slices_.size(); i++) - { - slices_[i]->SetNormal(normal); - } - - hasNormal_ = true; - } - - void Sort() - { - if (!hasNormal_) - { - throw Orthanc::OrthancException(Orthanc::ErrorCode_BadSequenceOfCalls); - } - - Comparator comparator; - std::sort(slices_.begin(), slices_.end(), comparator); - } - - void FilterNormal(const Vector& normal) - { - size_t pos = 0; - - for (size_t i = 0; i < slices_.size(); i++) - { - if (GeometryToolbox::IsParallel(normal, slices_[i]->GetSlice().GetGeometry().GetNormal())) - { - // This slice is compatible with the selected normal - slices_[pos] = slices_[i]; - pos += 1; - } - else - { - delete slices_[i]; - slices_[i] = NULL; - } - } - - slices_.resize(pos); - } - - bool SelectNormal(Vector& normal) const - { - std::vector<Vector> normalCandidates; - std::vector<unsigned int> normalCount; - - bool found = false; - - for (size_t i = 0; !found && i < GetSliceCount(); i++) - { - const Vector& normal = GetSlice(i).GetGeometry().GetNormal(); - - bool add = true; - for (size_t j = 0; add && j < normalCandidates.size(); j++) // (*) - { - if (GeometryToolbox::IsParallel(normal, normalCandidates[j])) - { - normalCount[j] += 1; - add = false; - } - } - - if (add) - { - if (normalCount.size() > 2) - { - // To get linear-time complexity in (*). This heuristics - // allows the series to have one single frame that is - // not parallel to the others (such a frame could be a - // generated preview) - found = false; - } - else - { - normalCandidates.push_back(normal); - normalCount.push_back(1); - } - } - } - - for (size_t i = 0; !found && i < normalCandidates.size(); i++) - { - unsigned int count = normalCount[i]; - if (count == GetSliceCount() || - count + 1 == GetSliceCount()) - { - normal = normalCandidates[i]; - found = true; - } - } - - return found; - } - }; - - - - class OrthancSlicesLoader : public boost::noncopyable - { - public: - class ICallback : public boost::noncopyable - { - public: - virtual ~ICallback() - { - } - - virtual void NotifyGeometryReady(const OrthancSlicesLoader& loader) = 0; - - virtual void NotifyGeometryError(const OrthancSlicesLoader& loader) = 0; - - virtual void NotifySliceImageReady(const OrthancSlicesLoader& loader, - unsigned int sliceIndex, - const Slice& slice, - Orthanc::ImageAccessor* image) = 0; - - virtual void NotifySliceImageError(const OrthancSlicesLoader& loader, - unsigned int sliceIndex, - const Slice& slice) = 0; - }; - - private: - enum State - { - State_Error, - State_Initialization, - State_LoadingGeometry, - State_GeometryReady - }; - - enum Mode - { - Mode_SeriesGeometry, - Mode_InstanceGeometry, - Mode_LoadImage - }; - - class 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 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); - } - } - }; - - - WebCallback webCallback_; - ICallback& userCallback_; - IWebService& orthanc_; - State state_; - SliceSorter slices_; - - - void 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 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 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()); - } - } - - - public: - OrthancSlicesLoader(ICallback& callback, - IWebService& orthanc) : - webCallback_(*this), - userCallback_(callback), - orthanc_(orthanc), - state_(State_Initialization) - { - } - - void 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 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 GetSliceCount() const - { - if (state_ != State_GeometryReady) - { - throw Orthanc::OrthancException(Orthanc::ErrorCode_BadSequenceOfCalls); - } - - return slices_.GetSliceCount(); - } - - const Slice& GetSlice(size_t index) const - { - if (state_ != State_GeometryReady) - { - throw Orthanc::OrthancException(Orthanc::ErrorCode_BadSequenceOfCalls); - } - - return slices_.GetSlice(index); - } - - void 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)); - } - } - }; - - class Tata : public OrthancSlicesLoader::ICallback { public: