comparison 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
comparison
equal deleted inserted replaced
86:02c3a7a4938f 87:4a541cd4fa83
26 #include "../Resources/Orthanc/Core/HttpClient.h" 26 #include "../Resources/Orthanc/Core/HttpClient.h"
27 #include "../Resources/Orthanc/Core/Logging.h" 27 #include "../Resources/Orthanc/Core/Logging.h"
28 #include "../Resources/Orthanc/Core/MultiThreading/SharedMessageQueue.h" 28 #include "../Resources/Orthanc/Core/MultiThreading/SharedMessageQueue.h"
29 #include "../Resources/Orthanc/Core/OrthancException.h" 29 #include "../Resources/Orthanc/Core/OrthancException.h"
30 30
31 #include "../Framework/Toolbox/IVolumeSlicesObserver.h"
32 #include "../Framework/Volumes/ImageBuffer3D.h"
33 #include "../Framework/Toolbox/DownloadStack.h"
34 #include "../Resources/Orthanc/Core/Images/ImageProcessing.h"
35
31 #include <boost/lexical_cast.hpp> 36 #include <boost/lexical_cast.hpp>
32 #include <boost/date_time/posix_time/posix_time.hpp> 37 #include <boost/date_time/posix_time/posix_time.hpp>
33 #include <boost/thread/thread.hpp> 38 #include <boost/thread/thread.hpp>
34 39
35 namespace OrthancStone 40 namespace OrthancStone
66 const Slice& slice) 71 const Slice& slice)
67 { 72 {
68 printf("ERROR 2\n"); 73 printf("ERROR 2\n");
69 } 74 }
70 }; 75 };
76
77
78 class OrthancVolumeImageLoader : private OrthancSlicesLoader::ICallback
79 {
80 private:
81 OrthancSlicesLoader loader_;
82 IVolumeSlicesObserver* observer_;
83 std::auto_ptr<ImageBuffer3D> image_;
84 std::auto_ptr<DownloadStack> downloadStack_;
85
86
87 void ScheduleSliceDownload()
88 {
89 assert(downloadStack_.get() != NULL);
90
91 unsigned int slice;
92 if (downloadStack_->Pop(slice))
93 {
94 loader_.ScheduleLoadSliceImage(slice);
95 }
96 }
97
98
99 static bool IsCompatible(const Slice& a,
100 const Slice& b)
101 {
102 if (!GeometryToolbox::IsParallel(a.GetGeometry().GetNormal(),
103 b.GetGeometry().GetNormal()))
104 {
105 LOG(ERROR) << "Some slice in the volume image is not parallel to the others";
106 return false;
107 }
108
109 if (a.GetConverter().GetExpectedPixelFormat() != b.GetConverter().GetExpectedPixelFormat())
110 {
111 LOG(ERROR) << "The pixel format changes across the slices of the volume image";
112 return false;
113 }
114
115 if (a.GetWidth() != b.GetWidth() ||
116 a.GetHeight() != b.GetHeight())
117 {
118 LOG(ERROR) << "The width/height of the slices change across the volume image";
119 return false;
120 }
121
122 if (!GeometryToolbox::IsNear(a.GetPixelSpacingX(), b.GetPixelSpacingX()) ||
123 !GeometryToolbox::IsNear(a.GetPixelSpacingY(), b.GetPixelSpacingY()))
124 {
125 LOG(ERROR) << "The pixel spacing of the slices change across the volume image";
126 return false;
127 }
128
129 return true;
130 }
131
132
133 static double GetDistance(const Slice& a,
134 const Slice& b)
135 {
136 return fabs(a.GetGeometry().ProjectAlongNormal(a.GetGeometry().GetOrigin()) -
137 a.GetGeometry().ProjectAlongNormal(b.GetGeometry().GetOrigin()));
138 }
139
140
141 virtual void NotifyGeometryReady(const OrthancSlicesLoader& loader)
142 {
143 if (loader.GetSliceCount() == 0)
144 {
145 LOG(ERROR) << "Empty volume image";
146 return;
147 }
148
149 for (size_t i = 1; i < loader.GetSliceCount(); i++)
150 {
151 if (!IsCompatible(loader.GetSlice(0), loader.GetSlice(i)))
152 {
153 return;
154 }
155 }
156
157 double spacingZ;
158
159 if (loader.GetSliceCount() > 1)
160 {
161 spacingZ = GetDistance(loader.GetSlice(0), loader.GetSlice(1));
162 }
163 else
164 {
165 // This is a volume with one single slice: Choose a dummy
166 // z-dimension for voxels
167 spacingZ = 1;
168 }
169
170 for (size_t i = 1; i < loader.GetSliceCount(); i++)
171 {
172 if (!GeometryToolbox::IsNear(spacingZ, GetDistance(loader.GetSlice(i - 1), loader.GetSlice(i))))
173 {
174 LOG(ERROR) << "The distance between successive slices is not constant in a volume image";
175 return;
176 }
177 }
178
179 unsigned int width = loader.GetSlice(0).GetWidth();
180 unsigned int height = loader.GetSlice(0).GetHeight();
181 Orthanc::PixelFormat format = loader.GetSlice(0).GetConverter().GetExpectedPixelFormat();
182 LOG(INFO) << "Creating a volume image of size " << width << "x" << height
183 << "x" << loader.GetSliceCount() << " in " << Orthanc::EnumerationToString(format);
184
185 image_.reset(new ImageBuffer3D(format, width, height, loader.GetSliceCount()));
186 image_->SetAxialGeometry(loader.GetSlice(0).GetGeometry());
187 image_->SetVoxelDimensions(loader.GetSlice(0).GetPixelSpacingX(),
188 loader.GetSlice(0).GetPixelSpacingY(), spacingZ);
189 image_->Clear();
190
191 downloadStack_.reset(new DownloadStack(loader.GetSliceCount()));
192
193 for (unsigned int i = 0; i < 4; i++) // Limit to 4 simultaneous downloads
194 {
195 ScheduleSliceDownload();
196 }
197 }
198
199 virtual void NotifyGeometryError(const OrthancSlicesLoader& loader)
200 {
201 LOG(ERROR) << "Unable to download a volume image";
202 }
203
204 virtual void NotifySliceImageReady(const OrthancSlicesLoader& loader,
205 unsigned int sliceIndex,
206 const Slice& slice,
207 Orthanc::ImageAccessor* image)
208 {
209 std::auto_ptr<Orthanc::ImageAccessor> protection(image);
210
211 {
212 ImageBuffer3D::SliceWriter writer(*image_, VolumeProjection_Axial, 0);
213 Orthanc::ImageProcessing::Copy(writer.GetAccessor(), *protection);
214 }
215
216 ScheduleSliceDownload();
217 }
218
219 virtual void NotifySliceImageError(const OrthancSlicesLoader& loader,
220 unsigned int sliceIndex,
221 const Slice& slice)
222 {
223 LOG(ERROR) << "Cannot download slice " << sliceIndex << " in a volume image";
224 ScheduleSliceDownload();
225 }
226
227 public:
228 OrthancVolumeImageLoader(IWebService& orthanc) :
229 loader_(*this, orthanc),
230 observer_(NULL)
231 {
232 }
233
234 void ScheduleLoadSeries(const std::string& seriesId)
235 {
236 loader_.ScheduleLoadSeries(seriesId);
237 }
238
239 void ScheduleLoadInstance(const std::string& instanceId,
240 unsigned int frame)
241 {
242 loader_.ScheduleLoadInstance(instanceId, frame);
243 }
244
245 void SetObserver(IVolumeSlicesObserver& observer)
246 {
247 if (observer_ == NULL)
248 {
249 observer_ = &observer;
250 }
251 else
252 {
253 // Cannot add more than one observer
254 throw Orthanc::OrthancException(Orthanc::ErrorCode_BadSequenceOfCalls);
255 }
256 }
257
258 };
71 } 259 }
72 260
73 261
74 TEST(Toto, Tutu) 262 TEST(Toto, DISABLED_Tutu)
75 { 263 {
76 OrthancStone::Oracle oracle(4); 264 OrthancStone::Oracle oracle(4);
77 oracle.Start(); 265 oracle.Start();
78 266
79 Orthanc::WebServiceParameters web; 267 Orthanc::WebServiceParameters web;
96 //orthanc.Stop(); 284 //orthanc.Stop();
97 oracle.Stop(); 285 oracle.Stop();
98 } 286 }
99 287
100 288
289 TEST(Toto, Tata)
290 {
291 OrthancStone::Oracle oracle(4);
292 oracle.Start();
293
294 Orthanc::WebServiceParameters web;
295 OrthancStone::OracleWebService orthanc(oracle, web);
296 OrthancStone::OrthancVolumeImageLoader volume(orthanc);
297
298 volume.ScheduleLoadInstance("19816330-cb02e1cf-df3a8fe8-bf510623-ccefe9f5", 0);
299 //volume.ScheduleLoadSeries("318603c5-03e8cffc-a82b6ee1-3ccd3c1e-18d7e3bb"); // COMUNIX PET
300 //volume.ScheduleLoadSeries("5990e39c-51e5f201-fe87a54c-31a55943-e59ef80e"); // Delphine sagital
301
302 boost::this_thread::sleep(boost::posix_time::milliseconds(1000));
303
304 oracle.Stop();
305 }
306
101 307
102 int main(int argc, char **argv) 308 int main(int argc, char **argv)
103 { 309 {
104 Orthanc::Logging::Initialize(); 310 Orthanc::Logging::Initialize();
105 Orthanc::Logging::EnableInfoLevel(true); 311 Orthanc::Logging::EnableInfoLevel(true);