comparison UnitTestsSources/UnitTestsMain.cpp @ 67:acb60cbb8301 wasm

refactoring SeriesLoader
author Sebastien Jodogne <s.jodogne@gmail.com>
date Thu, 18 May 2017 17:51:41 +0200
parents 298f375dcb68
children 1526d38ef6da
comparison
equal deleted inserted replaced
66:298f375dcb68 67:acb60cbb8301
25 #include "../Framework/Toolbox/OrthancWebService.h" 25 #include "../Framework/Toolbox/OrthancWebService.h"
26 #include "../Framework/Layers/OrthancFrameLayerSource.h" 26 #include "../Framework/Layers/OrthancFrameLayerSource.h"
27 #include "../Framework/Widgets/LayerWidget.h" 27 #include "../Framework/Widgets/LayerWidget.h"
28 28
29 29
30 #include "../Framework/Toolbox/MessagingToolbox.h"
31 #include "../Framework/Toolbox/DicomFrameConverter.h"
32
30 namespace OrthancStone 33 namespace OrthancStone
31 { 34 {
32 class Tata : public ILayerSource::IObserver 35 class SeriesLoader :
33 { 36 public IWebService::ICallback // TODO move to PImpl
34 public:
35 virtual void NotifySourceChange(ILayerSource& source)
36 {
37 printf("Source change\n");
38
39 OrthancStone::SliceGeometry slice;
40 double x1, y1, x2, y2;
41 printf(">> %d: ", source.GetExtent(x1, y1, x2, y2, slice));
42 printf("(%f,%f) (%f,%f)\n", x1, y1, x2, y2);
43 }
44
45 virtual void NotifySliceChange(ILayerSource& source,
46 const SliceGeometry& slice)
47 {
48 printf("Slice change\n");
49 }
50
51 virtual void NotifyLayerReady(ILayerRenderer* layer,
52 ILayerSource& source,
53 const SliceGeometry& viewportSlice)
54 {
55 std::auto_ptr<ILayerRenderer> tmp(layer);
56
57 }
58
59 virtual void NotifyLayerError(ILayerSource& source,
60 const SliceGeometry& viewportSlice)
61 {
62 }
63 };
64
65
66
67 /*class OrthancInstanceLoader : public IWebService::ICallback
68 { 37 {
69 public: 38 public:
70 class ICallback : public boost::noncopyable 39 class ICallback : public boost::noncopyable
71 { 40 {
72 public: 41 public:
73 virtual ~ICallback() 42 virtual ~ICallback()
74 { 43 {
75 } 44 }
45
46 virtual void NotifyGeometryReady(const SeriesLoader& series) = 0;
47
48 virtual void NotifyGeometryError(const SeriesLoader& series) = 0;
49 };
50
51 private:
52 class Slice : public boost::noncopyable
53 {
54 private:
55 std::string instanceId_;
56 SliceGeometry geometry_;
57 double thickness_;
58 unsigned int width_;
59 unsigned int height_;
60 double projectionAlongNormal_;
61 DicomFrameConverter converter_;
76 62
77 virtual void NotifyInstanceLoaded(const std::string& instanceId, 63 public:
78 const OrthancPlugins::FullOrthancDataset& dicom) = 0; 64 Slice(const std::string& instanceId,
65 const std::string& imagePositionPatient,
66 const std::string& imageOrientationPatient,
67 double thickness,
68 unsigned int width,
69 unsigned int height,
70 const OrthancPlugins::IDicomDataset& dataset) :
71 instanceId_(instanceId),
72 geometry_(imagePositionPatient, imageOrientationPatient),
73 thickness_(thickness),
74 width_(width),
75 height_(height)
76 {
77 converter_.ReadParameters(dataset);
78 }
79
80 const std::string GetInstanceId() const
81 {
82 return instanceId_;
83 }
84
85 const SliceGeometry& GetGeometry() const
86 {
87 return geometry_;
88 }
89
90 double GetThickness() const
91 {
92 return thickness_;
93 }
94
95 unsigned int GetWidth() const
96 {
97 return width_;
98 }
99
100 unsigned int GetHeight() const
101 {
102 return height_;
103 }
104
105 const DicomFrameConverter& GetConverter() const
106 {
107 return converter_;
108 }
109
110 void SetNormal(const Vector& normal)
111 {
112 projectionAlongNormal_ = boost::numeric::ublas::inner_prod(geometry_.GetOrigin(), normal);
113 }
114
115 double GetProjectionAlongNormal() const
116 {
117 return projectionAlongNormal_;
118 }
119 };
120
121
122 class SetOfSlices : public boost::noncopyable
123 {
124 private:
125 std::vector<Slice*> slices_;
126
127 struct Comparator
128 {
129 bool operator() (const Slice* const a,
130 const Slice* const b) const
131 {
132 return a->GetProjectionAlongNormal() < b->GetProjectionAlongNormal();
133 }
134 };
135
136 public:
137 ~SetOfSlices()
138 {
139 for (size_t i = 0; i < slices_.size(); i++)
140 {
141 assert(slices_[i] != NULL);
142 delete slices_[i];
143 }
144 }
145
146 void Reserve(size_t size)
147 {
148 slices_.reserve(size);
149 }
150
151 void AddSlice(const std::string& instanceId,
152 const Json::Value& value)
153 {
154 OrthancPlugins::FullOrthancDataset dataset(value);
155 OrthancPlugins::DicomDatasetReader reader(dataset);
156
157 std::string position, orientation;
158 double thickness;
159 unsigned int width, height;
160 if (dataset.GetStringValue(position, OrthancPlugins::DICOM_TAG_IMAGE_POSITION_PATIENT) &&
161 dataset.GetStringValue(orientation, OrthancPlugins::DICOM_TAG_IMAGE_ORIENTATION_PATIENT) &&
162 reader.GetDoubleValue(thickness, OrthancPlugins::DICOM_TAG_SLICE_THICKNESS) &&
163 reader.GetUnsignedIntegerValue(width, OrthancPlugins::DICOM_TAG_COLUMNS) &&
164 reader.GetUnsignedIntegerValue(height, OrthancPlugins::DICOM_TAG_ROWS));
165 {
166 slices_.push_back(new Slice(instanceId, position, orientation,
167 thickness, width, height, dataset));
168 }
169 }
170
171 size_t GetSliceCount() const
172 {
173 return slices_.size();
174 }
175
176 const Slice& GetSlice(size_t index) const
177 {
178 assert(slices_[index] != NULL);
179 return *slices_[index];
180 }
79 181
80 virtual void NotifyInstanceError(const std::string& instanceId) = 0; 182 void Sort(const Vector& normal)
183 {
184 for (size_t i = 0; i < slices_.size(); i++)
185 {
186 slices_[i]->SetNormal(normal);
187 }
188
189 Comparator comparator;
190 std::sort(slices_.begin(), slices_.end(), comparator);
191 }
192
193
194 void SelectNormal(Vector& normal) const
195 {
196 std::vector<Vector> normalCandidates;
197 std::vector<unsigned int> normalCount;
198
199 bool found = false;
200
201 for (size_t i = 0; !found && i < GetSliceCount(); i++)
202 {
203 const Vector& normal = GetSlice(i).GetGeometry().GetNormal();
204
205 bool add = true;
206 for (size_t j = 0; add && j < normalCandidates.size(); j++) // (*)
207 {
208 if (GeometryToolbox::IsParallel(normal, normalCandidates[j]))
209 {
210 normalCount[j] += 1;
211 add = false;
212 }
213 }
214
215 if (add)
216 {
217 if (normalCount.size() > 2)
218 {
219 // To get linear-time complexity in (*). This heuristics
220 // allows the series to have one single frame that is
221 // not parallel to the others (such a frame could be a
222 // generated preview)
223 found = false;
224 }
225 else
226 {
227 normalCandidates.push_back(normal);
228 normalCount.push_back(1);
229 }
230 }
231 }
232
233 for (size_t i = 0; !found && i < normalCandidates.size(); i++)
234 {
235 unsigned int count = normalCount[i];
236 if (count == GetSliceCount() ||
237 count + 1 == GetSliceCount())
238 {
239 normal = normalCandidates[i];
240 found = true;
241 }
242 }
243
244 if (!found)
245 {
246 LOG(ERROR) << "Cannot select a normal that is shared by most of the slices of this series";
247 throw Orthanc::OrthancException(Orthanc::ErrorCode_BadFileFormat);
248 }
249 }
250
251
252 void FilterNormal(const Vector& normal)
253 {
254 size_t pos = 0;
255
256 for (size_t i = 0; i < slices_.size(); i++)
257 {
258 if (GeometryToolbox::IsParallel(normal, slices_[i]->GetGeometry().GetNormal()))
259 {
260 // This slice is compatible with the selected normal
261 slices_[pos] = slices_[i];
262 pos += 1;
263 }
264 else
265 {
266 delete slices_[i];
267 slices_[i] = NULL;
268 }
269 }
270
271 slices_.resize(pos);
272 }
81 }; 273 };
82 274
83 private: 275
276 enum Mode
277 {
278 Mode_Geometry
279 };
280
84 class Operation : public Orthanc::IDynamicObject 281 class Operation : public Orthanc::IDynamicObject
85 { 282 {
86 private: 283 private:
87 ICallback& callback_; 284 Mode mode_;
88 std::string instanceId_; 285 unsigned int instance_;
286
287 Operation(Mode mode) :
288 mode_(mode)
289 {
290 }
89 291
90 public: 292 public:
91 Operation(ICallback& callback, 293 Mode GetMode() const
92 const std::string& instanceId) : 294 {
93 callback_(callback), 295 return mode_;
94 instanceId_(instanceId) 296 }
95 { 297
96 } 298 static Operation* DownloadGeometry()
97 299 {
98 ICallback& GetCallback() 300 return new Operation(Mode_Geometry);
99 {
100 return callback_;
101 }
102
103 const std::string& GetInstanceId() const
104 {
105 return instanceId_;
106 } 301 }
107 }; 302 };
108 303
304 ICallback& callback_;
109 IWebService& orthanc_; 305 IWebService& orthanc_;
110 306 std::string seriesId_;
307 SetOfSlices slices_;
308
309
310 void ParseGeometry(const void* answer,
311 size_t size)
312 {
313 Json::Value series;
314 if (!MessagingToolbox::ParseJson(series, answer, size) ||
315 series.type() != Json::objectValue)
316 {
317 callback_.NotifyGeometryError(*this);
318 return;
319 }
320
321 Json::Value::Members instances = series.getMemberNames();
322
323 slices_.Reserve(instances.size());
324
325 for (size_t i = 0; i < instances.size(); i++)
326 {
327 slices_.AddSlice(instances[i], series[instances[i]]);
328 }
329
330 bool ok = false;
331
332 if (slices_.GetSliceCount() > 0)
333 {
334 Vector normal;
335 slices_.SelectNormal(normal);
336 slices_.FilterNormal(normal);
337 slices_.Sort(normal);
338 ok = true;
339 }
340
341 if (ok)
342 {
343 printf("%d\n", slices_.GetSliceCount());
344 callback_.NotifyGeometryReady(*this);
345 }
346 else
347 {
348 LOG(ERROR) << "This series is empty";
349 callback_.NotifyGeometryError(*this);
350 return;
351 }
352 }
353
354
111 public: 355 public:
112 OrthancInstanceLoader(IWebService& orthanc) : 356 SeriesLoader(ICallback& callback,
113 orthanc_(orthanc) 357 IWebService& orthanc,
114 { 358 const std::string& seriesId) :
115 } 359 callback_(callback),
116 360 orthanc_(orthanc),
117 void ScheduleLoadInstance(ICallback& callback, 361 seriesId_(seriesId)
118 const std::string& instanceId) 362 {
119 { 363 std::string uri = "/series/" + seriesId + "/instances-tags";
120 orthanc_.ScheduleGetRequest(*this, 364 orthanc.ScheduleGetRequest(*this, uri, Operation::DownloadGeometry());
121 "/instances/" + instanceId + "/tags", 365 }
122 new Operation(callback, instanceId)); 366
123 } 367 const std::string& GetSeriesId() const
124 368 {
125 void NotifySuccess(const std::string& uri, 369 return seriesId_;
126 const void* answer, 370 }
127 size_t answerSize, 371
128 Orthanc::IDynamicObject* payload) 372 virtual void NotifySuccess(const std::string& uri,
129 { 373 const void* answer,
130 std::auto_ptr<Operation> operation(reinterpret_cast<Operation*>(payload)); 374 size_t answerSize,
131 375 Orthanc::IDynamicObject* payload)
132 try 376 {
133 { 377 std::auto_ptr<Operation> operation(dynamic_cast<Operation*>(payload));
134 OrthancPlugins::FullOrthancDataset dataset(answer, answerSize); 378
135 operation->GetCallback().NotifyInstanceLoaded(operation->GetInstanceId(), dataset); 379 switch (operation->GetMode())
136 } 380 {
137 catch (Orthanc::OrthancException&) 381 case Mode_Geometry:
138 { 382 ParseGeometry(answer, answerSize);
139 operation->GetCallback().NotifyInstanceError(operation->GetInstanceId()); 383 break;
140 } 384
141 } 385 default:
142 386 throw Orthanc::OrthancException(Orthanc::ErrorCode_InternalError);
143 void NotifyError(const std::string& uri, 387 }
144 Orthanc::IDynamicObject* payload) 388 }
145 { 389
146 std::auto_ptr<Operation> operation(reinterpret_cast<Operation*>(payload)); 390 virtual void NotifyError(const std::string& uri,
147 391 Orthanc::IDynamicObject* payload)
392 {
393 std::auto_ptr<Operation> operation(dynamic_cast<Operation*>(payload));
148 LOG(ERROR) << "Cannot download " << uri; 394 LOG(ERROR) << "Cannot download " << uri;
149 operation->GetCallback().NotifyInstanceError(operation->GetInstanceId()); 395
150 } 396 switch (operation->GetMode())
151 };*/ 397 {
152 398 case Mode_Geometry:
153 399 callback_.NotifyGeometryError(*this);
154 400 break;
401
402 default:
403 throw Orthanc::OrthancException(Orthanc::ErrorCode_InternalError);
404 }
405 }
406 };
407
408
409 class Tata : public SeriesLoader::ICallback
410 {
411 public:
412 virtual void NotifyGeometryReady(const SeriesLoader& series)
413 {
414 printf("Done %s\n", series.GetSeriesId().c_str());
415 }
416
417 virtual void NotifyGeometryError(const SeriesLoader& series)
418 {
419 printf("Error %s\n", series.GetSeriesId().c_str());
420 }
421 };
155 } 422 }
156
157 423
158 424
159 TEST(Toto, Tutu) 425 TEST(Toto, Tutu)
160 { 426 {
161 Orthanc::WebServiceParameters web; 427 Orthanc::WebServiceParameters web;
162 OrthancStone::OrthancWebService orthanc(web); 428 OrthancStone::OrthancWebService orthanc(web);
163 OrthancStone::OrthancFrameLayerSource source(orthanc, "befb52a6-b4b04954-b5a019c3-fdada9d7-dddc9430", 0);
164 429
165 OrthancStone::Tata tata; 430 OrthancStone::Tata tata;
166 source.SetObserver(tata); 431 //OrthancStone::SeriesLoader loader(tata, orthanc, "c1c4cb95-05e3bd11-8da9f5bb-87278f71-0b2b43f5");
167 432 OrthancStone::SeriesLoader loader(tata, orthanc, "67f1b334-02c16752-45026e40-a5b60b6b-030ecab5");
168 OrthancStone::SliceGeometry slice;
169 source.ScheduleLayerCreation(slice);
170
171
172 OrthancStone::LayerWidget widget;
173 printf(">> %d\n", widget.AddLayer(new OrthancStone::OrthancFrameLayerSource(orthanc, "befb52a6-b4b04954-b5a019c3-fdada9d7-dddc9430", 0)));
174 } 433 }
175 434
176 435
177 436
178 int main(int argc, char **argv) 437 int main(int argc, char **argv)