comparison Framework/Toolbox/OrthancSlicesLoader.cpp @ 117:42c05a3baee3 wasm

loading multi-frame instances as 3D volumes
author Sebastien Jodogne <s.jodogne@gmail.com>
date Thu, 28 Sep 2017 16:55:51 +0200
parents 2eca030792aa
children a4d0b6c82b29
comparison
equal deleted inserted replaced
116:4c5f7cda8624 117:42c05a3baee3
25 25
26 #include <Core/Images/Image.h> 26 #include <Core/Images/Image.h>
27 #include <Core/Images/ImageProcessing.h> 27 #include <Core/Images/ImageProcessing.h>
28 #include <Core/Images/JpegReader.h> 28 #include <Core/Images/JpegReader.h>
29 #include <Core/Images/PngReader.h> 29 #include <Core/Images/PngReader.h>
30 #include <Core/Compression/GzipCompressor.h>
30 #include <Core/Logging.h> 31 #include <Core/Logging.h>
31 #include <Core/OrthancException.h> 32 #include <Core/OrthancException.h>
32 #include <Core/Toolbox.h> 33 #include <Core/Toolbox.h>
33 #include <Plugins/Samples/Common/DicomDatasetReader.h> 34 #include <Plugins/Samples/Common/DicomDatasetReader.h>
34 #include <Plugins/Samples/Common/FullOrthancDataset.h> 35 #include <Plugins/Samples/Common/FullOrthancDataset.h>
94 return quality_; 95 return quality_;
95 } 96 }
96 97
97 unsigned int GetSliceIndex() const 98 unsigned int GetSliceIndex() const
98 { 99 {
99 assert(mode_ == Mode_LoadImage); 100 assert(mode_ == Mode_LoadImage ||
101 mode_ == Mode_LoadRawImage);
100 return sliceIndex_; 102 return sliceIndex_;
101 } 103 }
102 104
103 const Slice& GetSlice() const 105 const Slice& GetSlice() const
104 { 106 {
105 assert(mode_ == Mode_LoadImage && slice_ != NULL); 107 assert(mode_ == Mode_LoadImage ||
108 mode_ == Mode_LoadRawImage);
109 assert(slice_ != NULL);
106 return *slice_; 110 return *slice_;
107 } 111 }
108 112
109 unsigned int GetFrame() const 113 unsigned int GetFrame() const
110 { 114 {
111 assert(mode_ == Mode_InstanceGeometry); 115 assert(mode_ == Mode_FrameGeometry);
112 return frame_; 116 return frame_;
113 } 117 }
114 118
115 const std::string& GetInstanceId() const 119 const std::string& GetInstanceId() const
116 { 120 {
117 assert(mode_ == Mode_InstanceGeometry); 121 assert(mode_ == Mode_FrameGeometry ||
122 mode_ == Mode_InstanceGeometry);
118 return instanceId_; 123 return instanceId_;
119 } 124 }
120 125
121 static Operation* DownloadSeriesGeometry() 126 static Operation* DownloadSeriesGeometry()
122 { 127 {
123 return new Operation(Mode_SeriesGeometry); 128 return new Operation(Mode_SeriesGeometry);
124 } 129 }
125 130
126 static Operation* DownloadInstanceGeometry(const std::string& instanceId, 131 static Operation* DownloadInstanceGeometry(const std::string& instanceId)
127 unsigned int frame)
128 { 132 {
129 std::auto_ptr<Operation> operation(new Operation(Mode_InstanceGeometry)); 133 std::auto_ptr<Operation> operation(new Operation(Mode_InstanceGeometry));
134 operation->instanceId_ = instanceId;
135 return operation.release();
136 }
137
138 static Operation* DownloadFrameGeometry(const std::string& instanceId,
139 unsigned int frame)
140 {
141 std::auto_ptr<Operation> operation(new Operation(Mode_FrameGeometry));
130 operation->instanceId_ = instanceId; 142 operation->instanceId_ = instanceId;
131 operation->frame_ = frame; 143 operation->frame_ = frame;
132 return operation.release(); 144 return operation.release();
133 } 145 }
134 146
140 tmp->sliceIndex_ = sliceIndex; 152 tmp->sliceIndex_ = sliceIndex;
141 tmp->slice_ = &slice; 153 tmp->slice_ = &slice;
142 tmp->quality_ = quality; 154 tmp->quality_ = quality;
143 return tmp.release(); 155 return tmp.release();
144 } 156 }
157
158 static Operation* DownloadSliceRawImage(unsigned int sliceIndex,
159 const Slice& slice)
160 {
161 std::auto_ptr<Operation> tmp(new Operation(Mode_LoadRawImage));
162 tmp->sliceIndex_ = sliceIndex;
163 tmp->slice_ = &slice;
164 return tmp.release();
165 }
145 }; 166 };
146 167
147 168
148 class OrthancSlicesLoader::WebCallback : public IWebService::ICallback 169 class OrthancSlicesLoader::WebCallback : public IWebService::ICallback
149 { 170 {
168 case Mode_SeriesGeometry: 189 case Mode_SeriesGeometry:
169 that_.ParseSeriesGeometry(answer, answerSize); 190 that_.ParseSeriesGeometry(answer, answerSize);
170 break; 191 break;
171 192
172 case Mode_InstanceGeometry: 193 case Mode_InstanceGeometry:
173 that_.ParseInstanceGeometry(operation->GetInstanceId(), 194 that_.ParseInstanceGeometry(operation->GetInstanceId(), answer, answerSize);
174 operation->GetFrame(), answer, answerSize); 195 break;
196
197 case Mode_FrameGeometry:
198 that_.ParseFrameGeometry(operation->GetInstanceId(),
199 operation->GetFrame(), answer, answerSize);
175 break; 200 break;
176 201
177 case Mode_LoadImage: 202 case Mode_LoadImage:
178 switch (operation->GetQuality()) 203 switch (operation->GetQuality())
179 { 204 {
191 throw Orthanc::OrthancException(Orthanc::ErrorCode_InternalError); 216 throw Orthanc::OrthancException(Orthanc::ErrorCode_InternalError);
192 } 217 }
193 218
194 break; 219 break;
195 220
221 case Mode_LoadRawImage:
222 that_.ParseSliceRawImage(*operation, answer, answerSize);
223 break;
224
196 default: 225 default:
197 throw Orthanc::OrthancException(Orthanc::ErrorCode_InternalError); 226 throw Orthanc::OrthancException(Orthanc::ErrorCode_InternalError);
198 } 227 }
199 } 228 }
200 229
204 std::auto_ptr<Operation> operation(dynamic_cast<Operation*>(payload)); 233 std::auto_ptr<Operation> operation(dynamic_cast<Operation*>(payload));
205 LOG(ERROR) << "Cannot download " << uri; 234 LOG(ERROR) << "Cannot download " << uri;
206 235
207 switch (operation->GetMode()) 236 switch (operation->GetMode())
208 { 237 {
209 case Mode_InstanceGeometry: 238 case Mode_FrameGeometry:
210 case Mode_SeriesGeometry: 239 case Mode_SeriesGeometry:
211 that_.userCallback_.NotifyGeometryError(that_); 240 that_.userCallback_.NotifyGeometryError(that_);
212 that_.state_ = State_Error; 241 that_.state_ = State_Error;
213 break; 242 break;
214 243
264 slices_.Reserve(instances.size()); 293 slices_.Reserve(instances.size());
265 294
266 for (size_t i = 0; i < instances.size(); i++) 295 for (size_t i = 0; i < instances.size(); i++)
267 { 296 {
268 OrthancPlugins::FullOrthancDataset dataset(series[instances[i]]); 297 OrthancPlugins::FullOrthancDataset dataset(series[instances[i]]);
269 298 OrthancPlugins::DicomDatasetReader reader(dataset);
270 Slice slice; 299
271 if (slice.ParseOrthancFrame(dataset, instances[i], 0 /* todo */)) 300 unsigned int frames;
272 { 301 if (!reader.GetUnsignedIntegerValue(frames, OrthancPlugins::DICOM_TAG_NUMBER_OF_FRAMES))
273 slices_.AddSlice(slice); 302 {
274 } 303 frames = 1;
275 else 304 }
276 { 305
277 LOG(WARNING) << "Skipping invalid instance " << instances[i]; 306 for (unsigned int frame = 0; frame < frames; frame++)
307 {
308 Slice slice;
309 if (slice.ParseOrthancFrame(dataset, instances[i], frame))
310 {
311 slices_.AddSlice(slice);
312 }
313 else
314 {
315 LOG(WARNING) << "Skipping invalid frame " << frame << " within instance " << instances[i];
316 }
278 } 317 }
279 } 318 }
280 319
281 bool ok = false; 320 bool ok = false;
282 321
306 } 345 }
307 } 346 }
308 347
309 348
310 void OrthancSlicesLoader::ParseInstanceGeometry(const std::string& instanceId, 349 void OrthancSlicesLoader::ParseInstanceGeometry(const std::string& instanceId,
311 unsigned int frame,
312 const void* answer, 350 const void* answer,
313 size_t size) 351 size_t size)
352 {
353 Json::Value tags;
354 if (!MessagingToolbox::ParseJson(tags, answer, size) ||
355 tags.type() != Json::objectValue)
356 {
357 userCallback_.NotifyGeometryError(*this);
358 return;
359 }
360
361 OrthancPlugins::FullOrthancDataset dataset(tags);
362 OrthancPlugins::DicomDatasetReader reader(dataset);
363
364 unsigned int frames;
365 if (!reader.GetUnsignedIntegerValue(frames, OrthancPlugins::DICOM_TAG_NUMBER_OF_FRAMES))
366 {
367 frames = 1;
368 }
369
370 LOG(INFO) << "Instance " << instanceId << " contains " << frames << " frame(s)";
371
372 state_ = State_GeometryReady;
373
374 for (unsigned int frame = 0; frame < frames; frame++)
375 {
376 Slice slice;
377 if (slice.ParseOrthancFrame(dataset, instanceId, frame))
378 {
379 slices_.AddSlice(slice);
380 }
381 else
382 {
383 LOG(WARNING) << "Skipping invalid multi-frame instance " << instanceId;
384 userCallback_.NotifyGeometryError(*this);
385 return;
386 }
387 }
388
389 userCallback_.NotifyGeometryReady(*this);
390 }
391
392
393 void OrthancSlicesLoader::ParseFrameGeometry(const std::string& instanceId,
394 unsigned int frame,
395 const void* answer,
396 size_t size)
314 { 397 {
315 Json::Value tags; 398 Json::Value tags;
316 if (!MessagingToolbox::ParseJson(tags, answer, size) || 399 if (!MessagingToolbox::ParseJson(tags, answer, size) ||
317 tags.type() != Json::objectValue) 400 tags.type() != Json::objectValue)
318 { 401 {
524 607
525 NotifySliceImageSuccess(operation, image); 608 NotifySliceImageSuccess(operation, image);
526 } 609 }
527 610
528 611
612 void OrthancSlicesLoader::ParseSliceRawImage(const Operation& operation,
613 const void* answer,
614 size_t size)
615 {
616 Orthanc::GzipCompressor compressor;
617
618 std::string raw;
619 compressor.Uncompress(raw, answer, size);
620
621 printf("[%d => %d]\n", size, raw.size());
622 }
623
624
529 OrthancSlicesLoader::OrthancSlicesLoader(ICallback& callback, 625 OrthancSlicesLoader::OrthancSlicesLoader(ICallback& callback,
530 IWebService& orthanc) : 626 IWebService& orthanc) :
531 webCallback_(new WebCallback(*this)), 627 webCallback_(new WebCallback(*this)),
532 userCallback_(callback), 628 userCallback_(callback),
533 orthanc_(orthanc), 629 orthanc_(orthanc),
549 orthanc_.ScheduleGetRequest(*webCallback_, uri, Operation::DownloadSeriesGeometry()); 645 orthanc_.ScheduleGetRequest(*webCallback_, uri, Operation::DownloadSeriesGeometry());
550 } 646 }
551 } 647 }
552 648
553 649
554 void OrthancSlicesLoader::ScheduleLoadInstance(const std::string& instanceId, 650 void OrthancSlicesLoader::ScheduleLoadInstance(const std::string& instanceId)
555 unsigned int frame) 651 {
652 if (state_ != State_Initialization)
653 {
654 throw Orthanc::OrthancException(Orthanc::ErrorCode_BadSequenceOfCalls);
655 }
656 else
657 {
658 state_ = State_LoadingGeometry;
659
660 // Tag "3004-000c" is "Grid Frame Offset Vector", which is
661 // mandatory to read RT DOSE, but is too long to be returned by default
662 std::string uri = "/instances/" + instanceId + "/tags?ignore-length=3004-000c";
663 orthanc_.ScheduleGetRequest
664 (*webCallback_, uri, Operation::DownloadInstanceGeometry(instanceId));
665 }
666 }
667
668
669 void OrthancSlicesLoader::ScheduleLoadFrame(const std::string& instanceId,
670 unsigned int frame)
556 { 671 {
557 if (state_ != State_Initialization) 672 if (state_ != State_Initialization)
558 { 673 {
559 throw Orthanc::OrthancException(Orthanc::ErrorCode_BadSequenceOfCalls); 674 throw Orthanc::OrthancException(Orthanc::ErrorCode_BadSequenceOfCalls);
560 } 675 }
561 else 676 else
562 { 677 {
563 state_ = State_LoadingGeometry; 678 state_ = State_LoadingGeometry;
564 std::string uri = "/instances/" + instanceId + "/tags"; 679 std::string uri = "/instances/" + instanceId + "/tags";
565 orthanc_.ScheduleGetRequest 680 orthanc_.ScheduleGetRequest
566 (*webCallback_, uri, Operation::DownloadInstanceGeometry(instanceId, frame)); 681 (*webCallback_, uri, Operation::DownloadFrameGeometry(instanceId, frame));
567 } 682 }
568 } 683 }
569 684
570 685
571 bool OrthancSlicesLoader::IsGeometryReady() const 686 bool OrthancSlicesLoader::IsGeometryReady() const
606 721
607 return slices_.LookupSlice(index, plane); 722 return slices_.LookupSlice(index, plane);
608 } 723 }
609 724
610 725
611 void OrthancSlicesLoader::ScheduleSliceImagePng(size_t index) 726 void OrthancSlicesLoader::ScheduleSliceImagePng(const Slice& slice,
612 { 727 size_t index)
613 const Slice& slice = GetSlice(index); 728 {
614
615 std::string uri = ("/instances/" + slice.GetOrthancInstanceId() + "/frames/" + 729 std::string uri = ("/instances/" + slice.GetOrthancInstanceId() + "/frames/" +
616 boost::lexical_cast<std::string>(slice.GetFrame())); 730 boost::lexical_cast<std::string>(slice.GetFrame()));
617 731
618 switch (slice.GetConverter().GetExpectedPixelFormat()) 732 switch (slice.GetConverter().GetExpectedPixelFormat())
619 { 733 {
636 orthanc_.ScheduleGetRequest(*webCallback_, uri, 750 orthanc_.ScheduleGetRequest(*webCallback_, uri,
637 Operation::DownloadSliceImage(index, slice, SliceImageQuality_Full)); 751 Operation::DownloadSliceImage(index, slice, SliceImageQuality_Full));
638 } 752 }
639 753
640 754
641 void OrthancSlicesLoader::ScheduleSliceImageJpeg(size_t index, 755 void OrthancSlicesLoader::ScheduleSliceImageJpeg(const Slice& slice,
756 size_t index,
642 SliceImageQuality quality) 757 SliceImageQuality quality)
643 { 758 {
644 unsigned int value; 759 unsigned int value;
645 760
646 switch (quality) 761 switch (quality)
660 default: 775 default:
661 throw Orthanc::OrthancException(Orthanc::ErrorCode_ParameterOutOfRange); 776 throw Orthanc::OrthancException(Orthanc::ErrorCode_ParameterOutOfRange);
662 } 777 }
663 778
664 // This requires the official Web viewer plugin to be installed! 779 // This requires the official Web viewer plugin to be installed!
665 const Slice& slice = GetSlice(index);
666 std::string uri = ("/web-viewer/instances/jpeg" + 780 std::string uri = ("/web-viewer/instances/jpeg" +
667 boost::lexical_cast<std::string>(value) + 781 boost::lexical_cast<std::string>(value) +
668 "-" + slice.GetOrthancInstanceId() + "_" + 782 "-" + slice.GetOrthancInstanceId() + "_" +
669 boost::lexical_cast<std::string>(slice.GetFrame())); 783 boost::lexical_cast<std::string>(slice.GetFrame()));
670 784
671 orthanc_.ScheduleGetRequest(*webCallback_, uri, 785 orthanc_.ScheduleGetRequest(*webCallback_, uri,
672 Operation::DownloadSliceImage(index, slice, quality)); 786 Operation::DownloadSliceImage(index, slice, quality));
673 } 787 }
674 788
675 789
790
676 void OrthancSlicesLoader::ScheduleLoadSliceImage(size_t index, 791 void OrthancSlicesLoader::ScheduleLoadSliceImage(size_t index,
677 SliceImageQuality quality) 792 SliceImageQuality quality)
678 { 793 {
679 if (state_ != State_GeometryReady) 794 if (state_ != State_GeometryReady)
680 { 795 {
681 throw Orthanc::OrthancException(Orthanc::ErrorCode_BadSequenceOfCalls); 796 throw Orthanc::OrthancException(Orthanc::ErrorCode_BadSequenceOfCalls);
682 } 797 }
683 798
684 if (quality == SliceImageQuality_Full) 799 const Slice& slice = GetSlice(index);
685 { 800
686 ScheduleSliceImagePng(index); 801 if (slice.HasOrthancDecoding())
802 {
803 if (quality == SliceImageQuality_Full)
804 {
805 ScheduleSliceImagePng(slice, index);
806 }
807 else
808 {
809 ScheduleSliceImageJpeg(slice, index, quality);
810 }
687 } 811 }
688 else 812 else
689 { 813 {
690 ScheduleSliceImageJpeg(index, quality); 814 std::string uri = ("/instances/" + slice.GetOrthancInstanceId() + "/frames/" +
815 boost::lexical_cast<std::string>(slice.GetFrame()) + "/raw.gz");
816 orthanc_.ScheduleGetRequest(*webCallback_, uri,
817 Operation::DownloadSliceRawImage(index, slice));
691 } 818 }
692 } 819 }
693 } 820 }