Mercurial > hg > orthanc-stone
diff UnitTestsSources/UnitTestsMain.cpp @ 87:4a541cd4fa83 wasm
OrthancVolumeImageLoader
author | Sebastien Jodogne <s.jodogne@gmail.com> |
---|---|
date | Fri, 26 May 2017 15:31:58 +0200 |
parents | cee8f308a4bc |
children | 90bf4116a23c |
line wrap: on
line diff
--- a/UnitTestsSources/UnitTestsMain.cpp Fri May 26 13:42:50 2017 +0200 +++ b/UnitTestsSources/UnitTestsMain.cpp Fri May 26 15:31:58 2017 +0200 @@ -28,6 +28,11 @@ #include "../Resources/Orthanc/Core/MultiThreading/SharedMessageQueue.h" #include "../Resources/Orthanc/Core/OrthancException.h" +#include "../Framework/Toolbox/IVolumeSlicesObserver.h" +#include "../Framework/Volumes/ImageBuffer3D.h" +#include "../Framework/Toolbox/DownloadStack.h" +#include "../Resources/Orthanc/Core/Images/ImageProcessing.h" + #include <boost/lexical_cast.hpp> #include <boost/date_time/posix_time/posix_time.hpp> #include <boost/thread/thread.hpp> @@ -68,10 +73,193 @@ printf("ERROR 2\n"); } }; + + + class OrthancVolumeImageLoader : private OrthancSlicesLoader::ICallback + { + private: + OrthancSlicesLoader loader_; + IVolumeSlicesObserver* observer_; + std::auto_ptr<ImageBuffer3D> image_; + std::auto_ptr<DownloadStack> downloadStack_; + + + void ScheduleSliceDownload() + { + assert(downloadStack_.get() != NULL); + + unsigned int slice; + if (downloadStack_->Pop(slice)) + { + loader_.ScheduleLoadSliceImage(slice); + } + } + + + static bool IsCompatible(const Slice& a, + const Slice& b) + { + if (!GeometryToolbox::IsParallel(a.GetGeometry().GetNormal(), + b.GetGeometry().GetNormal())) + { + LOG(ERROR) << "Some slice in the volume image is not parallel to the others"; + return false; + } + + if (a.GetConverter().GetExpectedPixelFormat() != b.GetConverter().GetExpectedPixelFormat()) + { + LOG(ERROR) << "The pixel format changes across the slices of the volume image"; + return false; + } + + if (a.GetWidth() != b.GetWidth() || + a.GetHeight() != b.GetHeight()) + { + LOG(ERROR) << "The width/height of the slices change across the volume image"; + return false; + } + + if (!GeometryToolbox::IsNear(a.GetPixelSpacingX(), b.GetPixelSpacingX()) || + !GeometryToolbox::IsNear(a.GetPixelSpacingY(), b.GetPixelSpacingY())) + { + LOG(ERROR) << "The pixel spacing of the slices change across the volume image"; + return false; + } + + return true; + } + + + static double GetDistance(const Slice& a, + const Slice& b) + { + return fabs(a.GetGeometry().ProjectAlongNormal(a.GetGeometry().GetOrigin()) - + a.GetGeometry().ProjectAlongNormal(b.GetGeometry().GetOrigin())); + } + + + virtual void NotifyGeometryReady(const OrthancSlicesLoader& loader) + { + if (loader.GetSliceCount() == 0) + { + LOG(ERROR) << "Empty volume image"; + return; + } + + for (size_t i = 1; i < loader.GetSliceCount(); i++) + { + if (!IsCompatible(loader.GetSlice(0), loader.GetSlice(i))) + { + return; + } + } + + double spacingZ; + + if (loader.GetSliceCount() > 1) + { + spacingZ = GetDistance(loader.GetSlice(0), loader.GetSlice(1)); + } + else + { + // This is a volume with one single slice: Choose a dummy + // z-dimension for voxels + spacingZ = 1; + } + + for (size_t i = 1; i < loader.GetSliceCount(); i++) + { + if (!GeometryToolbox::IsNear(spacingZ, GetDistance(loader.GetSlice(i - 1), loader.GetSlice(i)))) + { + LOG(ERROR) << "The distance between successive slices is not constant in a volume image"; + return; + } + } + + unsigned int width = loader.GetSlice(0).GetWidth(); + unsigned int height = loader.GetSlice(0).GetHeight(); + Orthanc::PixelFormat format = loader.GetSlice(0).GetConverter().GetExpectedPixelFormat(); + LOG(INFO) << "Creating a volume image of size " << width << "x" << height + << "x" << loader.GetSliceCount() << " in " << Orthanc::EnumerationToString(format); + + image_.reset(new ImageBuffer3D(format, width, height, loader.GetSliceCount())); + image_->SetAxialGeometry(loader.GetSlice(0).GetGeometry()); + image_->SetVoxelDimensions(loader.GetSlice(0).GetPixelSpacingX(), + loader.GetSlice(0).GetPixelSpacingY(), spacingZ); + image_->Clear(); + + downloadStack_.reset(new DownloadStack(loader.GetSliceCount())); + + for (unsigned int i = 0; i < 4; i++) // Limit to 4 simultaneous downloads + { + ScheduleSliceDownload(); + } + } + + virtual void NotifyGeometryError(const OrthancSlicesLoader& loader) + { + LOG(ERROR) << "Unable to download a volume image"; + } + + virtual void NotifySliceImageReady(const OrthancSlicesLoader& loader, + unsigned int sliceIndex, + const Slice& slice, + Orthanc::ImageAccessor* image) + { + std::auto_ptr<Orthanc::ImageAccessor> protection(image); + + { + ImageBuffer3D::SliceWriter writer(*image_, VolumeProjection_Axial, 0); + Orthanc::ImageProcessing::Copy(writer.GetAccessor(), *protection); + } + + ScheduleSliceDownload(); + } + + virtual void NotifySliceImageError(const OrthancSlicesLoader& loader, + unsigned int sliceIndex, + const Slice& slice) + { + LOG(ERROR) << "Cannot download slice " << sliceIndex << " in a volume image"; + ScheduleSliceDownload(); + } + + public: + OrthancVolumeImageLoader(IWebService& orthanc) : + loader_(*this, orthanc), + observer_(NULL) + { + } + + void ScheduleLoadSeries(const std::string& seriesId) + { + loader_.ScheduleLoadSeries(seriesId); + } + + void ScheduleLoadInstance(const std::string& instanceId, + unsigned int frame) + { + loader_.ScheduleLoadInstance(instanceId, frame); + } + + void SetObserver(IVolumeSlicesObserver& observer) + { + if (observer_ == NULL) + { + observer_ = &observer; + } + else + { + // Cannot add more than one observer + throw Orthanc::OrthancException(Orthanc::ErrorCode_BadSequenceOfCalls); + } + } + + }; } -TEST(Toto, Tutu) +TEST(Toto, DISABLED_Tutu) { OrthancStone::Oracle oracle(4); oracle.Start(); @@ -98,6 +286,24 @@ } +TEST(Toto, Tata) +{ + OrthancStone::Oracle oracle(4); + oracle.Start(); + + Orthanc::WebServiceParameters web; + OrthancStone::OracleWebService orthanc(oracle, web); + OrthancStone::OrthancVolumeImageLoader volume(orthanc); + + volume.ScheduleLoadInstance("19816330-cb02e1cf-df3a8fe8-bf510623-ccefe9f5", 0); + //volume.ScheduleLoadSeries("318603c5-03e8cffc-a82b6ee1-3ccd3c1e-18d7e3bb"); // COMUNIX PET + //volume.ScheduleLoadSeries("5990e39c-51e5f201-fe87a54c-31a55943-e59ef80e"); // Delphine sagital + + boost::this_thread::sleep(boost::posix_time::milliseconds(1000)); + + oracle.Stop(); +} + int main(int argc, char **argv) {