Mercurial > hg > orthanc-stone
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); |