comparison Framework/Radiography/RadiographyScene.cpp @ 426:660fe6f6bf4a am-vsol-upgrade

split Export in 2
author am@osimis.io
date Thu, 22 Nov 2018 23:15:24 +0100
parents 087237703d63
children 3f9017db1738 751fb354149e
comparison
equal deleted inserted replaced
425:087237703d63 426:660fe6f6bf4a
53 assert(layer->second != NULL); 53 assert(layer->second != NULL);
54 layer_ = layer->second; 54 layer_ = layer->second;
55 } 55 }
56 } 56 }
57 57
58 58
59 RadiographyScene::LayerAccessor::LayerAccessor(RadiographyScene& scene, 59 RadiographyScene::LayerAccessor::LayerAccessor(RadiographyScene& scene,
60 double x, 60 double x,
61 double y) : 61 double y) :
62 scene_(scene), 62 scene_(scene),
63 index_(0) // Dummy initialization 63 index_(0) // Dummy initialization
64 { 64 {
65 if (scene.LookupLayer(index_, x, y)) 65 if (scene.LookupLayer(index_, x, y))
66 { 66 {
67 Layers::iterator layer = scene.layers_.find(index_); 67 Layers::iterator layer = scene.layers_.find(index_);
68 68
69 if (layer == scene.layers_.end()) 69 if (layer == scene.layers_.end())
70 { 70 {
71 throw Orthanc::OrthancException(Orthanc::ErrorCode_InternalError); 71 throw Orthanc::OrthancException(Orthanc::ErrorCode_InternalError);
72 } 72 }
73 else 73 else
117 } 117 }
118 else 118 else
119 { 119 {
120 throw Orthanc::OrthancException(Orthanc::ErrorCode_BadSequenceOfCalls); 120 throw Orthanc::OrthancException(Orthanc::ErrorCode_BadSequenceOfCalls);
121 } 121 }
122 } 122 }
123 123
124 124
125 125
126 class RadiographyScene::AlphaLayer : public RadiographyLayer 126 class RadiographyScene::AlphaLayer : public RadiographyLayer
127 { 127 {
143 void SetForegroundValue(float foreground) 143 void SetForegroundValue(float foreground)
144 { 144 {
145 useWindowing_ = false; 145 useWindowing_ = false;
146 foreground_ = foreground; 146 foreground_ = foreground;
147 } 147 }
148 148
149 149
150 void SetAlpha(Orthanc::ImageAccessor* image) 150 void SetAlpha(Orthanc::ImageAccessor* image)
151 { 151 {
152 std::auto_ptr<Orthanc::ImageAccessor> raii(image); 152 std::auto_ptr<Orthanc::ImageAccessor> raii(image);
153 153
154 if (image == NULL) 154 if (image == NULL)
155 { 155 {
156 throw Orthanc::OrthancException(Orthanc::ErrorCode_NullPointer); 156 throw Orthanc::OrthancException(Orthanc::ErrorCode_NullPointer);
157 } 157 }
158 158
168 168
169 void LoadText(const Orthanc::Font& font, 169 void LoadText(const Orthanc::Font& font,
170 const std::string& utf8) 170 const std::string& utf8)
171 { 171 {
172 SetAlpha(font.RenderAlpha(utf8)); 172 SetAlpha(font.RenderAlpha(utf8));
173 } 173 }
174 174
175 175
176 virtual bool GetDefaultWindowing(float& center, 176 virtual bool GetDefaultWindowing(float& center,
177 float& width) const 177 float& width) const
178 { 178 {
179 return false; 179 return false;
180 } 180 }
181 181
182 182
183 virtual void Render(Orthanc::ImageAccessor& buffer, 183 virtual void Render(Orthanc::ImageAccessor& buffer,
184 const AffineTransform2D& viewTransform, 184 const AffineTransform2D& viewTransform,
185 ImageInterpolation interpolation) const 185 ImageInterpolation interpolation) const
186 { 186 {
187 if (alpha_.get() == NULL) 187 if (alpha_.get() == NULL)
188 { 188 {
189 return; 189 return;
190 } 190 }
191 191
192 if (buffer.GetFormat() != Orthanc::PixelFormat_Float32) 192 if (buffer.GetFormat() != Orthanc::PixelFormat_Float32)
193 { 193 {
194 throw Orthanc::OrthancException(Orthanc::ErrorCode_IncompatibleImageFormat); 194 throw Orthanc::OrthancException(Orthanc::ErrorCode_IncompatibleImageFormat);
195 } 195 }
196 196
197 unsigned int cropX, cropY, cropWidth, cropHeight; 197 unsigned int cropX, cropY, cropWidth, cropHeight;
198 GetCrop(cropX, cropY, cropWidth, cropHeight); 198 GetCrop(cropX, cropY, cropWidth, cropHeight);
199 199
200 const AffineTransform2D t = AffineTransform2D::Combine( 200 const AffineTransform2D t = AffineTransform2D::Combine(
201 viewTransform, GetTransform(), 201 viewTransform, GetTransform(),
202 AffineTransform2D::CreateOffset(cropX, cropY)); 202 AffineTransform2D::CreateOffset(cropX, cropY));
203 203
204 Orthanc::ImageAccessor cropped; 204 Orthanc::ImageAccessor cropped;
205 alpha_->GetRegion(cropped, cropX, cropY, cropWidth, cropHeight); 205 alpha_->GetRegion(cropped, cropX, cropY, cropWidth, cropHeight);
206 206
207 Orthanc::Image tmp(Orthanc::PixelFormat_Grayscale8, buffer.GetWidth(), buffer.GetHeight(), false); 207 Orthanc::Image tmp(Orthanc::PixelFormat_Grayscale8, buffer.GetWidth(), buffer.GetHeight(), false);
208 208
209 t.Apply(tmp, cropped, interpolation, true /* clear */); 209 t.Apply(tmp, cropped, interpolation, true /* clear */);
210 210
211 // Blit 211 // Blit
212 const unsigned int width = buffer.GetWidth(); 212 const unsigned int width = buffer.GetWidth();
213 const unsigned int height = buffer.GetHeight(); 213 const unsigned int height = buffer.GetHeight();
214 214
215 float value = foreground_; 215 float value = foreground_;
216 216
217 if (useWindowing_) 217 if (useWindowing_)
218 { 218 {
219 float center, width; 219 float center, width;
220 if (scene_.GetWindowing(center, width)) 220 if (scene_.GetWindowing(center, width))
221 { 221 {
222 value = center + width / 2.0f; 222 value = center + width / 2.0f;
223 } 223 }
224 } 224 }
225 225
226 for (unsigned int y = 0; y < height; y++) 226 for (unsigned int y = 0; y < height; y++)
227 { 227 {
228 float *q = reinterpret_cast<float*>(buffer.GetRow(y)); 228 float *q = reinterpret_cast<float*>(buffer.GetRow(y));
229 const uint8_t *p = reinterpret_cast<uint8_t*>(tmp.GetRow(y)); 229 const uint8_t *p = reinterpret_cast<uint8_t*>(tmp.GetRow(y));
230 230
231 for (unsigned int x = 0; x < width; x++, p++, q++) 231 for (unsigned int x = 0; x < width; x++, p++, q++)
232 { 232 {
233 float a = static_cast<float>(*p) / 255.0f; 233 float a = static_cast<float>(*p) / 255.0f;
234 234
235 *q = (a * value + (1.0f - a) * (*q)); 235 *q = (a * value + (1.0f - a) * (*q));
236 } 236 }
237 } 237 }
238 } 238 }
239 239
240 240
241 virtual bool GetRange(float& minValue, 241 virtual bool GetRange(float& minValue,
242 float& maxValue) const 242 float& maxValue) const
243 { 243 {
244 if (useWindowing_) 244 if (useWindowing_)
245 { 245 {
262 262
263 return true; 263 return true;
264 } 264 }
265 } 265 }
266 }; 266 };
267 267
268 268
269 269
270 class RadiographyScene::DicomLayer : public RadiographyLayer 270 class RadiographyScene::DicomLayer : public RadiographyLayer
271 { 271 {
272 private: 272 private:
273 std::auto_ptr<Orthanc::ImageAccessor> source_; // Content of PixelData 273 std::auto_ptr<Orthanc::ImageAccessor> source_; // Content of PixelData
276 276
277 static OrthancPlugins::DicomTag ConvertTag(const Orthanc::DicomTag& tag) 277 static OrthancPlugins::DicomTag ConvertTag(const Orthanc::DicomTag& tag)
278 { 278 {
279 return OrthancPlugins::DicomTag(tag.GetGroup(), tag.GetElement()); 279 return OrthancPlugins::DicomTag(tag.GetGroup(), tag.GetElement());
280 } 280 }
281 281
282 282
283 void ApplyConverter() 283 void ApplyConverter()
284 { 284 {
285 if (source_.get() != NULL && 285 if (source_.get() != NULL &&
286 converter_.get() != NULL) 286 converter_.get() != NULL)
287 { 287 {
288 converted_.reset(converter_->ConvertFrame(*source_)); 288 converted_.reset(converter_->ConvertFrame(*source_));
289 } 289 }
290 } 290 }
291 291
292 public: 292 public:
293 void SetDicomTags(const OrthancPlugins::FullOrthancDataset& dataset) 293 void SetDicomTags(const OrthancPlugins::FullOrthancDataset& dataset)
294 { 294 {
295 converter_.reset(new DicomFrameConverter); 295 converter_.reset(new DicomFrameConverter);
296 converter_->ReadParameters(dataset); 296 converter_->ReadParameters(dataset);
297 ApplyConverter(); 297 ApplyConverter();
298 298
299 std::string tmp; 299 std::string tmp;
300 Vector pixelSpacing; 300 Vector pixelSpacing;
301 301
302 if (dataset.GetStringValue(tmp, ConvertTag(Orthanc::DICOM_TAG_PIXEL_SPACING)) && 302 if (dataset.GetStringValue(tmp, ConvertTag(Orthanc::DICOM_TAG_PIXEL_SPACING)) &&
303 LinearAlgebra::ParseVector(pixelSpacing, tmp) && 303 LinearAlgebra::ParseVector(pixelSpacing, tmp) &&
304 pixelSpacing.size() == 2) 304 pixelSpacing.size() == 2)
305 { 305 {
306 SetPixelSpacing(pixelSpacing[0], pixelSpacing[1]); 306 SetPixelSpacing(pixelSpacing[0], pixelSpacing[1]);
320 { 320 {
321 SetSize(width, height); 321 SetSize(width, height);
322 } 322 }
323 } 323 }
324 324
325 325
326 void SetSourceImage(Orthanc::ImageAccessor* image) // Takes ownership 326 void SetSourceImage(Orthanc::ImageAccessor* image) // Takes ownership
327 { 327 {
328 std::auto_ptr<Orthanc::ImageAccessor> raii(image); 328 std::auto_ptr<Orthanc::ImageAccessor> raii(image);
329 329
330 if (image == NULL) 330 if (image == NULL)
331 { 331 {
332 throw Orthanc::OrthancException(Orthanc::ErrorCode_NullPointer); 332 throw Orthanc::OrthancException(Orthanc::ErrorCode_NullPointer);
333 } 333 }
334 334
335 SetSize(image->GetWidth(), image->GetHeight()); 335 SetSize(image->GetWidth(), image->GetHeight());
336 336
337 source_ = raii; 337 source_ = raii;
338 ApplyConverter(); 338 ApplyConverter();
339 } 339 }
340 340
341 341
342 virtual void Render(Orthanc::ImageAccessor& buffer, 342 virtual void Render(Orthanc::ImageAccessor& buffer,
343 const AffineTransform2D& viewTransform, 343 const AffineTransform2D& viewTransform,
344 ImageInterpolation interpolation) const 344 ImageInterpolation interpolation) const
345 { 345 {
346 if (converted_.get() != NULL) 346 if (converted_.get() != NULL)
352 352
353 unsigned int cropX, cropY, cropWidth, cropHeight; 353 unsigned int cropX, cropY, cropWidth, cropHeight;
354 GetCrop(cropX, cropY, cropWidth, cropHeight); 354 GetCrop(cropX, cropY, cropWidth, cropHeight);
355 355
356 AffineTransform2D t = AffineTransform2D::Combine( 356 AffineTransform2D t = AffineTransform2D::Combine(
357 viewTransform, GetTransform(), 357 viewTransform, GetTransform(),
358 AffineTransform2D::CreateOffset(cropX, cropY)); 358 AffineTransform2D::CreateOffset(cropX, cropY));
359 359
360 Orthanc::ImageAccessor cropped; 360 Orthanc::ImageAccessor cropped;
361 converted_->GetRegion(cropped, cropX, cropY, cropWidth, cropHeight); 361 converted_->GetRegion(cropped, cropX, cropY, cropWidth, cropHeight);
362 362
363 t.Apply(buffer, cropped, interpolation, false); 363 t.Apply(buffer, cropped, interpolation, false);
409 { 409 {
410 throw Orthanc::OrthancException(Orthanc::ErrorCode_NullPointer); 410 throw Orthanc::OrthancException(Orthanc::ErrorCode_NullPointer);
411 } 411 }
412 412
413 std::auto_ptr<RadiographyLayer> raii(layer); 413 std::auto_ptr<RadiographyLayer> raii(layer);
414 414
415 size_t index = countLayers_++; 415 size_t index = countLayers_++;
416 raii->SetIndex(index); 416 raii->SetIndex(index);
417 layers_[index] = raii.release(); 417 layers_[index] = raii.release();
418 418
419 EmitMessage(GeometryChangedMessage(*this)); 419 EmitMessage(GeometryChangedMessage(*this));
420 EmitMessage(ContentChangedMessage(*this)); 420 EmitMessage(ContentChangedMessage(*this));
421 421
422 return *layer; 422 return *layer;
423 } 423 }
424 424
425 425
426 RadiographyScene::RadiographyScene(MessageBroker& broker) : 426 RadiographyScene::RadiographyScene(MessageBroker& broker) :
427 IObserver(broker), 427 IObserver(broker),
428 IObservable(broker), 428 IObservable(broker),
429 countLayers_(0), 429 countLayers_(0),
506 alpha->LoadText(font, utf8); 506 alpha->LoadText(font, utf8);
507 507
508 return RegisterLayer(alpha.release()); 508 return RegisterLayer(alpha.release());
509 } 509 }
510 510
511 511
512 RadiographyLayer& RadiographyScene::LoadTestBlock(unsigned int width, 512 RadiographyLayer& RadiographyScene::LoadTestBlock(unsigned int width,
513 unsigned int height) 513 unsigned int height)
514 { 514 {
515 std::auto_ptr<Orthanc::Image> block(new Orthanc::Image(Orthanc::PixelFormat_Grayscale8, width, height, false)); 515 std::auto_ptr<Orthanc::Image> block(new Orthanc::Image(Orthanc::PixelFormat_Grayscale8, width, height, false));
516 516
537 alpha->SetAlpha(block.release()); 537 alpha->SetAlpha(block.release());
538 538
539 return RegisterLayer(alpha.release()); 539 return RegisterLayer(alpha.release());
540 } 540 }
541 541
542 542
543 RadiographyLayer& RadiographyScene::LoadDicomFrame(OrthancApiClient& orthanc, 543 RadiographyLayer& RadiographyScene::LoadDicomFrame(OrthancApiClient& orthanc,
544 const std::string& instance, 544 const std::string& instance,
545 unsigned int frame, 545 unsigned int frame,
546 bool httpCompression) 546 bool httpCompression)
547 { 547 {
548 RadiographyLayer& layer = RegisterLayer(new DicomLayer); 548 RadiographyLayer& layer = RegisterLayer(new DicomLayer);
549 549
550 { 550 {
551 IWebService::HttpHeaders headers; 551 IWebService::HttpHeaders headers;
552 std::string uri = "/instances/" + instance + "/tags"; 552 std::string uri = "/instances/" + instance + "/tags";
553 553
554 orthanc.GetBinaryAsync( 554 orthanc.GetBinaryAsync(
555 uri, headers, 555 uri, headers,
556 new Callable<RadiographyScene, OrthancApiClient::BinaryResponseReadyMessage> 556 new Callable<RadiographyScene, OrthancApiClient::BinaryResponseReadyMessage>
557 (*this, &RadiographyScene::OnTagsReceived), NULL, 557 (*this, &RadiographyScene::OnTagsReceived), NULL,
558 new Orthanc::SingleValueObject<size_t>(layer.GetIndex())); 558 new Orthanc::SingleValueObject<size_t>(layer.GetIndex()));
559 } 559 }
560 560
561 { 561 {
562 IWebService::HttpHeaders headers; 562 IWebService::HttpHeaders headers;
563 headers["Accept"] = "image/x-portable-arbitrarymap"; 563 headers["Accept"] = "image/x-portable-arbitrarymap";
564 564
565 if (httpCompression) 565 if (httpCompression)
566 { 566 {
567 headers["Accept-Encoding"] = "gzip"; 567 headers["Accept-Encoding"] = "gzip";
568 } 568 }
569 569
570 std::string uri = ("/instances/" + instance + "/frames/" + 570 std::string uri = ("/instances/" + instance + "/frames/" +
571 boost::lexical_cast<std::string>(frame) + "/image-uint16"); 571 boost::lexical_cast<std::string>(frame) + "/image-uint16");
572 572
573 orthanc.GetBinaryAsync( 573 orthanc.GetBinaryAsync(
574 uri, headers, 574 uri, headers,
575 new Callable<RadiographyScene, OrthancApiClient::BinaryResponseReadyMessage> 575 new Callable<RadiographyScene, OrthancApiClient::BinaryResponseReadyMessage>
576 (*this, &RadiographyScene::OnFrameReceived), NULL, 576 (*this, &RadiographyScene::OnFrameReceived), NULL,
577 new Orthanc::SingleValueObject<size_t>(layer.GetIndex())); 577 new Orthanc::SingleValueObject<size_t>(layer.GetIndex()));
578 } 578 }
579 579
580 return layer; 580 return layer;
581 } 581 }
582 582
583 583
584 RadiographyLayer& RadiographyScene::LoadDicomWebFrame(IWebService& web) 584 RadiographyLayer& RadiographyScene::LoadDicomWebFrame(IWebService& web)
585 { 585 {
586 RadiographyLayer& layer = RegisterLayer(new DicomLayer); 586 RadiographyLayer& layer = RegisterLayer(new DicomLayer);
587 587
588 588
589 return layer; 589 return layer;
590 } 590 }
591 591
592 592
593 593
594 void RadiographyScene::OnTagsReceived(const OrthancApiClient::BinaryResponseReadyMessage& message) 594 void RadiographyScene::OnTagsReceived(const OrthancApiClient::BinaryResponseReadyMessage& message)
595 { 595 {
596 size_t index = dynamic_cast<const Orthanc::SingleValueObject<size_t>&> 596 size_t index = dynamic_cast<const Orthanc::SingleValueObject<size_t>&>
597 (message.GetPayload()).GetValue(); 597 (message.GetPayload()).GetValue();
598 598
599 LOG(INFO) << "JSON received: " << message.GetUri().c_str() 599 LOG(INFO) << "JSON received: " << message.GetUri().c_str()
600 << " (" << message.GetAnswerSize() << " bytes) for layer " << index; 600 << " (" << message.GetAnswerSize() << " bytes) for layer " << index;
601 601
602 Layers::iterator layer = layers_.find(index); 602 Layers::iterator layer = layers_.find(index);
603 if (layer != layers_.end()) 603 if (layer != layers_.end())
604 { 604 {
605 assert(layer->second != NULL); 605 assert(layer->second != NULL);
606 606
607 OrthancPlugins::FullOrthancDataset dicom(message.GetAnswer(), message.GetAnswerSize()); 607 OrthancPlugins::FullOrthancDataset dicom(message.GetAnswer(), message.GetAnswerSize());
608 dynamic_cast<DicomLayer*>(layer->second)->SetDicomTags(dicom); 608 dynamic_cast<DicomLayer*>(layer->second)->SetDicomTags(dicom);
609 609
610 float c, w; 610 float c, w;
611 if (!hasWindowing_ && 611 if (!hasWindowing_ &&
617 } 617 }
618 618
619 EmitMessage(GeometryChangedMessage(*this)); 619 EmitMessage(GeometryChangedMessage(*this));
620 } 620 }
621 } 621 }
622 622
623 623
624 void RadiographyScene::OnFrameReceived(const OrthancApiClient::BinaryResponseReadyMessage& message) 624 void RadiographyScene::OnFrameReceived(const OrthancApiClient::BinaryResponseReadyMessage& message)
625 { 625 {
626 size_t index = dynamic_cast<const Orthanc::SingleValueObject<size_t>&>(message.GetPayload()).GetValue(); 626 size_t index = dynamic_cast<const Orthanc::SingleValueObject<size_t>&>(message.GetPayload()).GetValue();
627 627
628 LOG(INFO) << "DICOM frame received: " << message.GetUri().c_str() 628 LOG(INFO) << "DICOM frame received: " << message.GetUri().c_str()
629 << " (" << message.GetAnswerSize() << " bytes) for layer " << index; 629 << " (" << message.GetAnswerSize() << " bytes) for layer " << index;
630 630
631 Layers::iterator layer = layers_.find(index); 631 Layers::iterator layer = layers_.find(index);
632 if (layer != layers_.end()) 632 if (layer != layers_.end())
633 { 633 {
634 assert(layer->second != NULL); 634 assert(layer->second != NULL);
635 635
636 std::string content; 636 std::string content;
637 if (message.GetAnswerSize() > 0) 637 if (message.GetAnswerSize() > 0)
638 { 638 {
639 content.assign(reinterpret_cast<const char*>(message.GetAnswer()), message.GetAnswerSize()); 639 content.assign(reinterpret_cast<const char*>(message.GetAnswer()), message.GetAnswerSize());
640 } 640 }
641 641
642 std::auto_ptr<Orthanc::PamReader> reader(new Orthanc::PamReader); 642 std::auto_ptr<Orthanc::PamReader> reader(new Orthanc::PamReader);
643 reader->ReadFromMemory(content); 643 reader->ReadFromMemory(content);
644 dynamic_cast<DicomLayer*>(layer->second)->SetSourceImage(reader.release()); 644 dynamic_cast<DicomLayer*>(layer->second)->SetSourceImage(reader.release());
645 645
646 EmitMessage(ContentChangedMessage(*this)); 646 EmitMessage(ContentChangedMessage(*this));
659 extent.Union(it->second->GetExtent()); 659 extent.Union(it->second->GetExtent());
660 } 660 }
661 661
662 return extent; 662 return extent;
663 } 663 }
664 664
665 665
666 void RadiographyScene::Render(Orthanc::ImageAccessor& buffer, 666 void RadiographyScene::Render(Orthanc::ImageAccessor& buffer,
667 const AffineTransform2D& viewTransform, 667 const AffineTransform2D& viewTransform,
668 ImageInterpolation interpolation) const 668 ImageInterpolation interpolation) const
669 { 669 {
702 } 702 }
703 703
704 return false; 704 return false;
705 } 705 }
706 706
707 707
708 void RadiographyScene::DrawBorder(CairoContext& context, 708 void RadiographyScene::DrawBorder(CairoContext& context,
709 unsigned int layer, 709 unsigned int layer,
710 double zoom) 710 double zoom)
711 { 711 {
712 Layers::const_iterator found = layers_.find(layer); 712 Layers::const_iterator found = layers_.find(layer);
713 713
714 if (found != layers_.end()) 714 if (found != layers_.end())
715 { 715 {
716 context.SetSourceColor(255, 0, 0); 716 context.SetSourceColor(255, 0, 0);
717 found->second->DrawBorders(context, zoom); 717 found->second->DrawBorders(context, zoom);
718 } 718 }
721 721
722 void RadiographyScene::GetRange(float& minValue, 722 void RadiographyScene::GetRange(float& minValue,
723 float& maxValue) const 723 float& maxValue) const
724 { 724 {
725 bool first = true; 725 bool first = true;
726 726
727 for (Layers::const_iterator it = layers_.begin(); 727 for (Layers::const_iterator it = layers_.begin();
728 it != layers_.end(); it++) 728 it != layers_.end(); it++)
729 { 729 {
730 assert(it->second != NULL); 730 assert(it->second != NULL);
731 731
752 maxValue = 0; 752 maxValue = 0;
753 } 753 }
754 } 754 }
755 755
756 756
757 // Export using PAM is faster than using PNG, but requires Orthanc
758 // core >= 1.4.3
759 void RadiographyScene::ExportDicom(OrthancApiClient& orthanc, 757 void RadiographyScene::ExportDicom(OrthancApiClient& orthanc,
760 const Orthanc::DicomMap& dicom, 758 const Orthanc::DicomMap& dicom,
759 const std::string& parentOrthancId,
761 double pixelSpacingX, 760 double pixelSpacingX,
762 double pixelSpacingY, 761 double pixelSpacingY,
763 bool invert, 762 bool invert,
764 ImageInterpolation interpolation, 763 ImageInterpolation interpolation,
765 bool usePam) 764 bool usePam)
766 { 765 {
766 Json::Value createDicomRequestContent;
767
768 Export(createDicomRequestContent, dicom, pixelSpacingX, pixelSpacingY, invert, interpolation, usePam);
769
770 if (!parentOrthancId.empty())
771 {
772 createDicomRequestContent["Parent"] = parentOrthancId;
773 }
774
775 orthanc.PostJsonAsyncExpectJson(
776 "/tools/create-dicom", createDicomRequestContent,
777 new Callable<RadiographyScene, OrthancApiClient::JsonResponseReadyMessage>
778 (*this, &RadiographyScene::OnDicomExported),
779 NULL, NULL);
780 }
781
782 // Export using PAM is faster than using PNG, but requires Orthanc
783 // core >= 1.4.3
784 void RadiographyScene::Export(Json::Value& createDicomRequestContent,
785 const Orthanc::DicomMap& dicom,
786 double pixelSpacingX,
787 double pixelSpacingY,
788 bool invert,
789 ImageInterpolation interpolation,
790 bool usePam)
791 {
767 if (pixelSpacingX <= 0 || 792 if (pixelSpacingX <= 0 ||
768 pixelSpacingY <= 0) 793 pixelSpacingY <= 0)
769 { 794 {
770 throw Orthanc::OrthancException(Orthanc::ErrorCode_ParameterOutOfRange); 795 throw Orthanc::OrthancException(Orthanc::ErrorCode_ParameterOutOfRange);
771 } 796 }
772 797
773 LOG(INFO) << "Exporting DICOM"; 798 LOG(INFO) << "Exporting DICOM";
774 799
775 Extent2D extent = GetSceneExtent(); 800 Extent2D extent = GetSceneExtent();
776 801
777 int w = std::ceil(extent.GetWidth() / pixelSpacingX); 802 int w = std::ceil(extent.GetWidth() / pixelSpacingX);
785 Orthanc::Image layers(Orthanc::PixelFormat_Float32, 810 Orthanc::Image layers(Orthanc::PixelFormat_Float32,
786 static_cast<unsigned int>(w), 811 static_cast<unsigned int>(w),
787 static_cast<unsigned int>(h), false); 812 static_cast<unsigned int>(h), false);
788 813
789 AffineTransform2D view = AffineTransform2D::Combine( 814 AffineTransform2D view = AffineTransform2D::Combine(
790 AffineTransform2D::CreateScaling(1.0 / pixelSpacingX, 1.0 / pixelSpacingY), 815 AffineTransform2D::CreateScaling(1.0 / pixelSpacingX, 1.0 / pixelSpacingY),
791 AffineTransform2D::CreateOffset(-extent.GetX1(), -extent.GetY1())); 816 AffineTransform2D::CreateOffset(-extent.GetX1(), -extent.GetY1()));
792 817
793 Render(layers, view, interpolation); 818 Render(layers, view, interpolation);
794 819
795 Orthanc::Image rendered(Orthanc::PixelFormat_Grayscale16, 820 Orthanc::Image rendered(Orthanc::PixelFormat_Grayscale16,
796 layers.GetWidth(), layers.GetHeight(), false); 821 layers.GetWidth(), layers.GetHeight(), false);
797 Orthanc::ImageProcessing::Convert(rendered, layers); 822 Orthanc::ImageProcessing::Convert(rendered, layers);
816 } 841 }
817 842
818 std::set<Orthanc::DicomTag> tags; 843 std::set<Orthanc::DicomTag> tags;
819 dicom.GetTags(tags); 844 dicom.GetTags(tags);
820 845
821 Json::Value json = Json::objectValue; 846 createDicomRequestContent["Tags"] = Json::objectValue;
822 json["Tags"] = Json::objectValue; 847
823
824 for (std::set<Orthanc::DicomTag>::const_iterator 848 for (std::set<Orthanc::DicomTag>::const_iterator
825 tag = tags.begin(); tag != tags.end(); ++tag) 849 tag = tags.begin(); tag != tags.end(); ++tag)
826 { 850 {
827 const Orthanc::DicomValue& value = dicom.GetValue(*tag); 851 const Orthanc::DicomValue& value = dicom.GetValue(*tag);
828 if (!value.IsNull() && 852 if (!value.IsNull() &&
829 !value.IsBinary()) 853 !value.IsBinary())
830 { 854 {
831 json["Tags"][tag->Format()] = value.GetContent(); 855 createDicomRequestContent["Tags"][tag->Format()] = value.GetContent();
832 } 856 }
833 } 857 }
834 858
835 json["Tags"][Orthanc::DICOM_TAG_PHOTOMETRIC_INTERPRETATION.Format()] = 859 createDicomRequestContent["Tags"][Orthanc::DICOM_TAG_PHOTOMETRIC_INTERPRETATION.Format()] =
836 (invert ? "MONOCHROME1" : "MONOCHROME2"); 860 (invert ? "MONOCHROME1" : "MONOCHROME2");
837 861
838 // WARNING: The order of PixelSpacing is Y/X. We use "%0.8f" to 862 // WARNING: The order of PixelSpacing is Y/X. We use "%0.8f" to
839 // avoid floating-point numbers to grow over 16 characters, 863 // avoid floating-point numbers to grow over 16 characters,
840 // which would be invalid according to DICOM standard 864 // which would be invalid according to DICOM standard
841 // ("dciodvfy" would complain). 865 // ("dciodvfy" would complain).
842 char buf[32]; 866 char buf[32];
843 sprintf(buf, "%0.8f\\%0.8f", pixelSpacingY, pixelSpacingX); 867 sprintf(buf, "%0.8f\\%0.8f", pixelSpacingY, pixelSpacingX);
844 868
845 json["Tags"][Orthanc::DICOM_TAG_PIXEL_SPACING.Format()] = buf; 869 createDicomRequestContent["Tags"][Orthanc::DICOM_TAG_PIXEL_SPACING.Format()] = buf;
846 870
847 float center, width; 871 float center, width;
848 if (GetWindowing(center, width)) 872 if (GetWindowing(center, width))
849 { 873 {
850 json["Tags"][Orthanc::DICOM_TAG_WINDOW_CENTER.Format()] = 874 createDicomRequestContent["Tags"][Orthanc::DICOM_TAG_WINDOW_CENTER.Format()] =
851 boost::lexical_cast<std::string>(boost::math::iround(center)); 875 boost::lexical_cast<std::string>(boost::math::iround(center));
852 876
853 json["Tags"][Orthanc::DICOM_TAG_WINDOW_WIDTH.Format()] = 877 createDicomRequestContent["Tags"][Orthanc::DICOM_TAG_WINDOW_WIDTH.Format()] =
854 boost::lexical_cast<std::string>(boost::math::iround(width)); 878 boost::lexical_cast<std::string>(boost::math::iround(width));
855 } 879 }
880
856 881
857 // This is Data URI scheme: https://en.wikipedia.org/wiki/Data_URI_scheme 882 // This is Data URI scheme: https://en.wikipedia.org/wiki/Data_URI_scheme
858 json["Content"] = ("data:" + 883 createDicomRequestContent["Content"] = ("data:" +
859 std::string(usePam ? Orthanc::MIME_PAM : Orthanc::MIME_PNG) + 884 std::string(usePam ? Orthanc::MIME_PAM : Orthanc::MIME_PNG) +
860 ";base64," + base64); 885 ";base64," + base64);
861
862 orthanc.PostJsonAsyncExpectJson(
863 "/tools/create-dicom", json,
864 new Callable<RadiographyScene, OrthancApiClient::JsonResponseReadyMessage>
865 (*this, &RadiographyScene::OnDicomExported),
866 NULL, NULL);
867 } 886 }
868 887
869 888
870 void RadiographyScene::OnDicomExported(const OrthancApiClient::JsonResponseReadyMessage& message) 889 void RadiographyScene::OnDicomExported(const OrthancApiClient::JsonResponseReadyMessage& message)
871 { 890 {
878 { 897 {
879 LOG(INFO) << "DICOMweb WADO-RS received: " << message.GetAnswerSize() << " bytes"; 898 LOG(INFO) << "DICOMweb WADO-RS received: " << message.GetAnswerSize() << " bytes";
880 899
881 const IWebService::HttpHeaders& h = message.GetAnswerHttpHeaders(); 900 const IWebService::HttpHeaders& h = message.GetAnswerHttpHeaders();
882 for (IWebService::HttpHeaders::const_iterator 901 for (IWebService::HttpHeaders::const_iterator
883 it = h.begin(); it != h.end(); ++it) 902 it = h.begin(); it != h.end(); ++it)
884 { 903 {
885 printf("[%s] = [%s]\n", it->first.c_str(), it->second.c_str()); 904 printf("[%s] = [%s]\n", it->first.c_str(), it->second.c_str());
886 } 905 }
887 } 906 }
888 907