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