comparison UnitTestsSources/UnitTestsMain.cpp @ 68:1526d38ef6da wasm

SliceSorter
author Sebastien Jodogne <s.jodogne@gmail.com>
date Mon, 22 May 2017 17:49:26 +0200
parents acb60cbb8301
children 1553b67b24e5
comparison
equal deleted inserted replaced
67:acb60cbb8301 68:1526d38ef6da
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 "../Resources/Orthanc/Core/Images/PngReader.h"
30 #include "../Framework/Toolbox/MessagingToolbox.h" 31 #include "../Framework/Toolbox/MessagingToolbox.h"
31 #include "../Framework/Toolbox/DicomFrameConverter.h" 32 #include "../Framework/Toolbox/DicomFrameConverter.h"
32 33
34 #include <boost/lexical_cast.hpp>
35
33 namespace OrthancStone 36 namespace OrthancStone
34 { 37 {
35 class SeriesLoader : 38 class Slice
39 {
40 private:
41 enum Type
42 {
43 Type_Invalid,
44 Type_OrthancInstance
45 // TODO A slice could come from some DICOM file (URL)
46 };
47
48 Type type_;
49 std::string orthancInstanceId_;
50 unsigned int frame_;
51 SliceGeometry geometry_;
52 double pixelSpacingX_;
53 double pixelSpacingY_;
54 double thickness_;
55 unsigned int width_;
56 unsigned int height_;
57 DicomFrameConverter converter_;
58
59 public:
60 Slice() : type_(Type_Invalid)
61 {
62 }
63
64 bool ParseOrthancFrame(const OrthancPlugins::IDicomDataset& dataset,
65 const std::string& instanceId,
66 unsigned int frame)
67 {
68 OrthancPlugins::DicomDatasetReader reader(dataset);
69
70 unsigned int frameCount;
71 if (!reader.GetUnsignedIntegerValue(frameCount, OrthancPlugins::DICOM_TAG_NUMBER_OF_FRAMES))
72 {
73 frameCount = 1; // Assume instance with one frame
74 }
75
76 if (frame >= frameCount)
77 {
78 return false;
79 }
80
81 if (!reader.GetDoubleValue(thickness_, OrthancPlugins::DICOM_TAG_SLICE_THICKNESS))
82 {
83 thickness_ = 100.0 * std::numeric_limits<double>::epsilon();
84 }
85
86 GeometryToolbox::GetPixelSpacing(pixelSpacingX_, pixelSpacingY_, dataset);
87
88 std::string position, orientation;
89 if (dataset.GetStringValue(position, OrthancPlugins::DICOM_TAG_IMAGE_POSITION_PATIENT) &&
90 dataset.GetStringValue(orientation, OrthancPlugins::DICOM_TAG_IMAGE_ORIENTATION_PATIENT) &&
91 reader.GetUnsignedIntegerValue(width_, OrthancPlugins::DICOM_TAG_COLUMNS) &&
92 reader.GetUnsignedIntegerValue(height_, OrthancPlugins::DICOM_TAG_ROWS))
93 {
94 orthancInstanceId_ = instanceId;
95 frame_ = frame;
96 geometry_ = SliceGeometry(position, orientation);
97 converter_.ReadParameters(dataset);
98
99 type_ = Type_OrthancInstance;
100 return true;
101 }
102 else
103 {
104 return false;
105 }
106 }
107
108 bool IsOrthancInstance() const
109 {
110 return type_ == Type_OrthancInstance;
111 }
112
113 const std::string GetOrthancInstanceId() const
114 {
115 if (type_ != Type_OrthancInstance)
116 {
117 throw Orthanc::OrthancException(Orthanc::ErrorCode_BadSequenceOfCalls);
118 }
119
120 return orthancInstanceId_;
121 }
122
123 unsigned int GetFrame() const
124 {
125 if (type_ == Type_Invalid)
126 {
127 throw Orthanc::OrthancException(Orthanc::ErrorCode_BadSequenceOfCalls);
128 }
129
130 return frame_;
131 }
132
133 const SliceGeometry& GetGeometry() const
134 {
135 if (type_ == Type_Invalid)
136 {
137 throw Orthanc::OrthancException(Orthanc::ErrorCode_BadSequenceOfCalls);
138 }
139
140 return geometry_;
141 }
142
143 double GetThickness() const
144 {
145 if (type_ == Type_Invalid)
146 {
147 throw Orthanc::OrthancException(Orthanc::ErrorCode_BadSequenceOfCalls);
148 }
149
150 return thickness_;
151 }
152
153 double GetPixelSpacingX() const
154 {
155 if (type_ == Type_Invalid)
156 {
157 throw Orthanc::OrthancException(Orthanc::ErrorCode_BadSequenceOfCalls);
158 }
159
160 return pixelSpacingX_;
161 }
162
163 double GetPixelSpacingY() const
164 {
165 if (type_ == Type_Invalid)
166 {
167 throw Orthanc::OrthancException(Orthanc::ErrorCode_BadSequenceOfCalls);
168 }
169
170 return pixelSpacingY_;
171 }
172
173 unsigned int GetWidth() const
174 {
175 if (type_ == Type_Invalid)
176 {
177 throw Orthanc::OrthancException(Orthanc::ErrorCode_BadSequenceOfCalls);
178 }
179
180 return width_;
181 }
182
183 unsigned int GetHeight() const
184 {
185 if (type_ == Type_Invalid)
186 {
187 throw Orthanc::OrthancException(Orthanc::ErrorCode_BadSequenceOfCalls);
188 }
189
190 return height_;
191 }
192
193 const DicomFrameConverter& GetConverter() const
194 {
195 if (type_ == Type_Invalid)
196 {
197 throw Orthanc::OrthancException(Orthanc::ErrorCode_BadSequenceOfCalls);
198 }
199
200 return converter_;
201 }
202 };
203
204
205 class SliceSorter : public boost::noncopyable
206 {
207 private:
208 class SliceWithDepth : public boost::noncopyable
209 {
210 private:
211 Slice slice_;
212 double depth_;
213
214 public:
215 SliceWithDepth(const Slice& slice) :
216 slice_(slice),
217 depth_(0)
218 {
219 }
220
221 void SetNormal(const Vector& normal)
222 {
223 depth_ = boost::numeric::ublas::inner_prod
224 (slice_.GetGeometry().GetOrigin(), normal);
225 }
226
227 double GetDepth() const
228 {
229 return depth_;
230 }
231
232 const Slice& GetSlice() const
233 {
234 return slice_;
235 }
236 };
237
238 struct Comparator
239 {
240 bool operator() (const SliceWithDepth* const& a,
241 const SliceWithDepth* const& b) const
242 {
243 return a->GetDepth() < b->GetDepth();
244 }
245 };
246
247 typedef std::vector<SliceWithDepth*> Slices;
248
249 Slices slices_;
250 bool hasNormal_;
251
252 public:
253 SliceSorter() : hasNormal_(false)
254 {
255 }
256
257 ~SliceSorter()
258 {
259 for (size_t i = 0; i < slices_.size(); i++)
260 {
261 assert(slices_[i] != NULL);
262 delete slices_[i];
263 }
264 }
265
266 void Reserve(size_t count)
267 {
268 slices_.reserve(count);
269 }
270
271 void AddSlice(const Slice& slice)
272 {
273 slices_.push_back(new SliceWithDepth(slice));
274 }
275
276 size_t GetSliceCount() const
277 {
278 return slices_.size();
279 }
280
281 const Slice& GetSlice(size_t i) const
282 {
283 if (i >= slices_.size())
284 {
285 throw Orthanc::OrthancException(Orthanc::ErrorCode_ParameterOutOfRange);
286 }
287
288 assert(slices_[i] != NULL);
289 return slices_[i]->GetSlice();
290 }
291
292 void SetNormal(const Vector& normal)
293 {
294 for (size_t i = 0; i < slices_.size(); i++)
295 {
296 slices_[i]->SetNormal(normal);
297 }
298
299 hasNormal_ = true;
300 }
301
302 void Sort()
303 {
304 if (!hasNormal_)
305 {
306 throw Orthanc::OrthancException(Orthanc::ErrorCode_BadSequenceOfCalls);
307 }
308
309 Comparator comparator;
310 std::sort(slices_.begin(), slices_.end(), comparator);
311 }
312
313 void FilterNormal(const Vector& normal)
314 {
315 size_t pos = 0;
316
317 for (size_t i = 0; i < slices_.size(); i++)
318 {
319 if (GeometryToolbox::IsParallel(normal, slices_[i]->GetSlice().GetGeometry().GetNormal()))
320 {
321 // This slice is compatible with the selected normal
322 slices_[pos] = slices_[i];
323 pos += 1;
324 }
325 else
326 {
327 delete slices_[i];
328 slices_[i] = NULL;
329 }
330 }
331
332 slices_.resize(pos);
333 }
334
335 bool SelectNormal(Vector& normal) const
336 {
337 std::vector<Vector> normalCandidates;
338 std::vector<unsigned int> normalCount;
339
340 bool found = false;
341
342 for (size_t i = 0; !found && i < GetSliceCount(); i++)
343 {
344 const Vector& normal = GetSlice(i).GetGeometry().GetNormal();
345
346 bool add = true;
347 for (size_t j = 0; add && j < normalCandidates.size(); j++) // (*)
348 {
349 if (GeometryToolbox::IsParallel(normal, normalCandidates[j]))
350 {
351 normalCount[j] += 1;
352 add = false;
353 }
354 }
355
356 if (add)
357 {
358 if (normalCount.size() > 2)
359 {
360 // To get linear-time complexity in (*). This heuristics
361 // allows the series to have one single frame that is
362 // not parallel to the others (such a frame could be a
363 // generated preview)
364 found = false;
365 }
366 else
367 {
368 normalCandidates.push_back(normal);
369 normalCount.push_back(1);
370 }
371 }
372 }
373
374 for (size_t i = 0; !found && i < normalCandidates.size(); i++)
375 {
376 unsigned int count = normalCount[i];
377 if (count == GetSliceCount() ||
378 count + 1 == GetSliceCount())
379 {
380 normal = normalCandidates[i];
381 found = true;
382 }
383 }
384
385 return found;
386 }
387 };
388
389
390
391 class OrthancSliceLoader :
36 public IWebService::ICallback // TODO move to PImpl 392 public IWebService::ICallback // TODO move to PImpl
37 { 393 {
38 public: 394 public:
39 class ICallback : public boost::noncopyable 395 class ICallback : public boost::noncopyable
40 { 396 {
41 public: 397 public:
42 virtual ~ICallback() 398 virtual ~ICallback()
43 { 399 {
44 } 400 }
45 401
46 virtual void NotifyGeometryReady(const SeriesLoader& series) = 0; 402 virtual void NotifyGeometryReady(const OrthancSliceLoader& loader) = 0;
47 403
48 virtual void NotifyGeometryError(const SeriesLoader& series) = 0; 404 virtual void NotifyGeometryError(const OrthancSliceLoader& loader) = 0;
405
406 virtual void NotifySliceImageReady(const OrthancSliceLoader& loader,
407 unsigned int sliceIndex,
408 const Slice& slice,
409 Orthanc::ImageAccessor* image) = 0;
410
411 virtual void NotifySliceImageError(const OrthancSliceLoader& loader,
412 unsigned int sliceIndex,
413 const Slice& slice) = 0;
49 }; 414 };
50 415
51 private: 416 private:
52 class Slice : public boost::noncopyable 417 enum State
418 {
419 State_Error,
420 State_Initialization,
421 State_LoadingGeometry,
422 State_GeometryReady
423 };
424
425 enum Mode
426 {
427 Mode_SeriesGeometry,
428 Mode_LoadImage
429 };
430
431 class Operation : public Orthanc::IDynamicObject
53 { 432 {
54 private: 433 private:
55 std::string instanceId_; 434 Mode mode_;
56 SliceGeometry geometry_; 435 unsigned int sliceIndex_;
57 double thickness_; 436 const Slice* slice_;
58 unsigned int width_;
59 unsigned int height_;
60 double projectionAlongNormal_;
61 DicomFrameConverter converter_;
62
63 public:
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 }
181
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 }
273 };
274
275
276 enum Mode
277 {
278 Mode_Geometry
279 };
280
281 class Operation : public Orthanc::IDynamicObject
282 {
283 private:
284 Mode mode_;
285 unsigned int instance_;
286 437
287 Operation(Mode mode) : 438 Operation(Mode mode) :
288 mode_(mode) 439 mode_(mode)
289 { 440 {
290 } 441 }
292 public: 443 public:
293 Mode GetMode() const 444 Mode GetMode() const
294 { 445 {
295 return mode_; 446 return mode_;
296 } 447 }
448
449 unsigned int GetSliceIndex() const
450 {
451 assert(mode_ == Mode_LoadImage);
452 return sliceIndex_;
453 }
454
455 const Slice& GetSlice() const
456 {
457 assert(mode_ == Mode_LoadImage && slice_ != NULL);
458 return *slice_;
459 }
297 460
298 static Operation* DownloadGeometry() 461 static Operation* DownloadSeriesGeometry()
299 { 462 {
300 return new Operation(Mode_Geometry); 463 return new Operation(Mode_SeriesGeometry);
464 }
465
466 static Operation* DownloadSliceImage(unsigned int sliceIndex,
467 const Slice& slice)
468 {
469 std::auto_ptr<Operation> tmp(new Operation(Mode_LoadImage));
470 tmp->sliceIndex_ = sliceIndex;
471 tmp->slice_ = &slice;
472 return tmp.release();
301 } 473 }
302 }; 474 };
303 475
304 ICallback& callback_; 476 ICallback& callback_;
305 IWebService& orthanc_; 477 IWebService& orthanc_;
306 std::string seriesId_; 478 State state_;
307 SetOfSlices slices_; 479 SliceSorter slices_;
308 480
309 481
310 void ParseGeometry(const void* answer, 482 void ParseSeriesGeometry(const void* answer,
311 size_t size) 483 size_t size)
312 { 484 {
313 Json::Value series; 485 Json::Value series;
314 if (!MessagingToolbox::ParseJson(series, answer, size) || 486 if (!MessagingToolbox::ParseJson(series, answer, size) ||
315 series.type() != Json::objectValue) 487 series.type() != Json::objectValue)
316 { 488 {
322 494
323 slices_.Reserve(instances.size()); 495 slices_.Reserve(instances.size());
324 496
325 for (size_t i = 0; i < instances.size(); i++) 497 for (size_t i = 0; i < instances.size(); i++)
326 { 498 {
327 slices_.AddSlice(instances[i], series[instances[i]]); 499 OrthancPlugins::FullOrthancDataset dataset(series[instances[i]]);
500
501 Slice slice;
502 if (slice.ParseOrthancFrame(dataset, instances[i], 0 /* todo */))
503 {
504 slices_.AddSlice(slice);
505 }
506 else
507 {
508 LOG(WARNING) << "Skipping invalid instance " << instances[i];
509 }
328 } 510 }
329 511
330 bool ok = false; 512 bool ok = false;
331 513
332 if (slices_.GetSliceCount() > 0) 514 if (slices_.GetSliceCount() > 0)
333 { 515 {
334 Vector normal; 516 Vector normal;
335 slices_.SelectNormal(normal); 517 if (slices_.SelectNormal(normal))
336 slices_.FilterNormal(normal); 518 {
337 slices_.Sort(normal); 519 slices_.FilterNormal(normal);
338 ok = true; 520 slices_.SetNormal(normal);
521 slices_.Sort();
522 ok = true;
523 }
339 } 524 }
340 525
341 if (ok) 526 if (ok)
342 { 527 {
343 printf("%d\n", slices_.GetSliceCount()); 528 LOG(INFO) << "Loaded a series with " << slices_.GetSliceCount() << " slice(s)";
344 callback_.NotifyGeometryReady(*this); 529 callback_.NotifyGeometryReady(*this);
345 } 530 }
346 else 531 else
347 { 532 {
348 LOG(ERROR) << "This series is empty"; 533 LOG(ERROR) << "This series is empty";
349 callback_.NotifyGeometryError(*this); 534 callback_.NotifyGeometryError(*this);
350 return; 535 }
536 }
537
538
539 void ParseSliceImage(const Operation& operation,
540 const void* answer,
541 size_t size)
542 {
543 std::auto_ptr<Orthanc::PngReader> image(new Orthanc::PngReader);
544 image->ReadFromMemory(answer, size);
545
546 bool ok = (image->GetWidth() == operation.GetSlice().GetWidth() ||
547 image->GetHeight() == operation.GetSlice().GetHeight());
548
549 if (ok &&
550 operation.GetSlice().GetConverter().GetExpectedPixelFormat() ==
551 Orthanc::PixelFormat_SignedGrayscale16)
552 {
553 if (image->GetFormat() == Orthanc::PixelFormat_Grayscale16)
554 {
555 image->SetFormat(Orthanc::PixelFormat_SignedGrayscale16);
556 }
557 else
558 {
559 ok = false;
560 }
561 }
562
563 if (ok)
564 {
565 callback_.NotifySliceImageReady(*this, operation.GetSliceIndex(),
566 operation.GetSlice(), image.release());
567 }
568 else
569 {
570 callback_.NotifySliceImageError(*this, operation.GetSliceIndex(),
571 operation.GetSlice());
351 } 572 }
352 } 573 }
353 574
354 575
355 public: 576 public:
356 SeriesLoader(ICallback& callback, 577 OrthancSliceLoader(ICallback& callback,
357 IWebService& orthanc, 578 IWebService& orthanc) :
358 const std::string& seriesId) :
359 callback_(callback), 579 callback_(callback),
360 orthanc_(orthanc), 580 orthanc_(orthanc),
361 seriesId_(seriesId) 581 state_(State_Initialization)
362 { 582 {
363 std::string uri = "/series/" + seriesId + "/instances-tags"; 583 }
364 orthanc.ScheduleGetRequest(*this, uri, Operation::DownloadGeometry()); 584
365 } 585 void ScheduleLoadSeries(const std::string& seriesId)
366 586 {
367 const std::string& GetSeriesId() const 587 if (state_ != State_Initialization)
368 { 588 {
369 return seriesId_; 589 throw Orthanc::OrthancException(Orthanc::ErrorCode_BadSequenceOfCalls);
590 }
591 else
592 {
593 state_ = State_LoadingGeometry;
594 std::string uri = "/series/" + seriesId + "/instances-tags";
595 orthanc_.ScheduleGetRequest(*this, uri, Operation::DownloadSeriesGeometry());
596 }
597 }
598
599 size_t GetSliceCount() const
600 {
601 if (state_ != State_GeometryReady)
602 {
603 throw Orthanc::OrthancException(Orthanc::ErrorCode_BadSequenceOfCalls);
604 }
605
606 return slices_.GetSliceCount();
607 }
608
609 const Slice& GetSlice(size_t index) const
610 {
611 if (state_ != State_GeometryReady)
612 {
613 throw Orthanc::OrthancException(Orthanc::ErrorCode_BadSequenceOfCalls);
614 }
615
616 return slices_.GetSlice(index);
617 }
618
619 void ScheduleLoadSliceImage(size_t index)
620 {
621 if (state_ != State_GeometryReady)
622 {
623 throw Orthanc::OrthancException(Orthanc::ErrorCode_BadSequenceOfCalls);
624 }
625 else
626 {
627 const Slice& slice = GetSlice(index);
628
629 std::string uri = ("/instances/" + slice.GetOrthancInstanceId() + "/frames/" +
630 boost::lexical_cast<std::string>(slice.GetFrame()));
631
632 switch (slice.GetConverter().GetExpectedPixelFormat())
633 {
634 case Orthanc::PixelFormat_RGB24:
635 uri += "/preview";
636 break;
637
638 case Orthanc::PixelFormat_Grayscale16:
639 uri += "/image-uint16";
640 break;
641
642 case Orthanc::PixelFormat_SignedGrayscale16:
643 uri += "/image-int16";
644 break;
645
646 default:
647 throw Orthanc::OrthancException(Orthanc::ErrorCode_ParameterOutOfRange);
648 }
649
650 orthanc_.ScheduleGetRequest(*this, uri, Operation::DownloadSliceImage(index, slice));
651 }
370 } 652 }
371 653
372 virtual void NotifySuccess(const std::string& uri, 654 virtual void NotifySuccess(const std::string& uri,
373 const void* answer, 655 const void* answer,
374 size_t answerSize, 656 size_t answerSize,
376 { 658 {
377 std::auto_ptr<Operation> operation(dynamic_cast<Operation*>(payload)); 659 std::auto_ptr<Operation> operation(dynamic_cast<Operation*>(payload));
378 660
379 switch (operation->GetMode()) 661 switch (operation->GetMode())
380 { 662 {
381 case Mode_Geometry: 663 case Mode_SeriesGeometry:
382 ParseGeometry(answer, answerSize); 664 ParseSeriesGeometry(answer, answerSize);
665 state_ = State_GeometryReady;
666 break;
667
668 case Mode_LoadImage:
669 ParseSliceImage(*operation, answer, answerSize);
383 break; 670 break;
384 671
385 default: 672 default:
386 throw Orthanc::OrthancException(Orthanc::ErrorCode_InternalError); 673 throw Orthanc::OrthancException(Orthanc::ErrorCode_InternalError);
387 } 674 }
393 std::auto_ptr<Operation> operation(dynamic_cast<Operation*>(payload)); 680 std::auto_ptr<Operation> operation(dynamic_cast<Operation*>(payload));
394 LOG(ERROR) << "Cannot download " << uri; 681 LOG(ERROR) << "Cannot download " << uri;
395 682
396 switch (operation->GetMode()) 683 switch (operation->GetMode())
397 { 684 {
398 case Mode_Geometry: 685 case Mode_SeriesGeometry:
399 callback_.NotifyGeometryError(*this); 686 callback_.NotifyGeometryError(*this);
687 state_ = State_Error;
400 break; 688 break;
401 689
690 case Mode_LoadImage:
691 callback_.NotifySliceImageError(*this, operation->GetSliceIndex(), operation->GetSlice());
692 break;
693
402 default: 694 default:
403 throw Orthanc::OrthancException(Orthanc::ErrorCode_InternalError); 695 throw Orthanc::OrthancException(Orthanc::ErrorCode_InternalError);
404 } 696 }
405 } 697 }
406 }; 698 };
407 699
408 700
409 class Tata : public SeriesLoader::ICallback 701 class Tata : public OrthancSliceLoader::ICallback
410 { 702 {
411 public: 703 public:
412 virtual void NotifyGeometryReady(const SeriesLoader& series) 704 virtual void NotifyGeometryReady(const OrthancSliceLoader& loader)
413 { 705 {
414 printf("Done %s\n", series.GetSeriesId().c_str()); 706 printf("Done\n");
415 } 707 }
416 708
417 virtual void NotifyGeometryError(const SeriesLoader& series) 709 virtual void NotifyGeometryError(const OrthancSliceLoader& loader)
418 { 710 {
419 printf("Error %s\n", series.GetSeriesId().c_str()); 711 printf("Error\n");
712 }
713
714 virtual void NotifySliceImageReady(const OrthancSliceLoader& loader,
715 unsigned int sliceIndex,
716 const Slice& slice,
717 Orthanc::ImageAccessor* image)
718 {
719 std::auto_ptr<Orthanc::ImageAccessor> tmp(image);
720 printf("Slice OK\n");
721 }
722
723 virtual void NotifySliceImageError(const OrthancSliceLoader& loader,
724 unsigned int sliceIndex,
725 const Slice& slice)
726 {
727 printf("ERROR 2\n");
420 } 728 }
421 }; 729 };
422 } 730 }
423 731
424 732
426 { 734 {
427 Orthanc::WebServiceParameters web; 735 Orthanc::WebServiceParameters web;
428 OrthancStone::OrthancWebService orthanc(web); 736 OrthancStone::OrthancWebService orthanc(web);
429 737
430 OrthancStone::Tata tata; 738 OrthancStone::Tata tata;
431 //OrthancStone::SeriesLoader loader(tata, orthanc, "c1c4cb95-05e3bd11-8da9f5bb-87278f71-0b2b43f5"); 739 OrthancStone::OrthancSliceLoader loader(tata, orthanc);
432 OrthancStone::SeriesLoader loader(tata, orthanc, "67f1b334-02c16752-45026e40-a5b60b6b-030ecab5"); 740 //loader.ScheduleLoadSeries("c1c4cb95-05e3bd11-8da9f5bb-87278f71-0b2b43f5");
741 loader.ScheduleLoadSeries("67f1b334-02c16752-45026e40-a5b60b6b-030ecab5");
742
743 printf(">> %d\n", loader.GetSliceCount());
744 loader.ScheduleLoadSliceImage(31);
433 } 745 }
434 746
435 747
436 748
437 int main(int argc, char **argv) 749 int main(int argc, char **argv)