comparison Framework/Radiography/RadiographyScene.cpp @ 421:f87f28624b96 cache-in-radiography

tentative to make SmartLoader and RadiographyScene work together (not really working)
author am@osimis.io
date Tue, 20 Nov 2018 16:35:29 +0100
parents c23df8b3433b
children
comparison
equal deleted inserted replaced
420:8bf717c4e497 421:f87f28624b96
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 boost::shared_ptr<Orthanc::ImageAccessor> source_; // Content of PixelData
274 std::auto_ptr<DicomFrameConverter> converter_; 274 std::auto_ptr<DicomFrameConverter> converter_;
275 std::auto_ptr<Orthanc::ImageAccessor> converted_; // Float32 275 std::auto_ptr<Orthanc::ImageAccessor> converted_; // Float32
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(boost::shared_ptr<Orthanc::ImageAccessor> image)
327 { 327 {
328 std::auto_ptr<Orthanc::ImageAccessor> raii(image); 328 if (image.get() == NULL)
329
330 if (image == NULL)
331 { 329 {
332 throw Orthanc::OrthancException(Orthanc::ErrorCode_NullPointer); 330 throw Orthanc::OrthancException(Orthanc::ErrorCode_NullPointer);
333 } 331 }
334 332
335 SetSize(image->GetWidth(), image->GetHeight()); 333 SetSize(image->GetWidth(), image->GetHeight());
336 334
337 source_ = raii; 335 source_ = image;
338 ApplyConverter(); 336 ApplyConverter();
339 } 337 }
340 338
341 339
342 virtual void Render(Orthanc::ImageAccessor& buffer, 340 virtual void Render(Orthanc::ImageAccessor& buffer,
343 const AffineTransform2D& viewTransform, 341 const AffineTransform2D& viewTransform,
344 ImageInterpolation interpolation) const 342 ImageInterpolation interpolation) const
345 { 343 {
346 if (converted_.get() != NULL) 344 if (converted_.get() != NULL)
352 350
353 unsigned int cropX, cropY, cropWidth, cropHeight; 351 unsigned int cropX, cropY, cropWidth, cropHeight;
354 GetCrop(cropX, cropY, cropWidth, cropHeight); 352 GetCrop(cropX, cropY, cropWidth, cropHeight);
355 353
356 AffineTransform2D t = AffineTransform2D::Combine( 354 AffineTransform2D t = AffineTransform2D::Combine(
357 viewTransform, GetTransform(), 355 viewTransform, GetTransform(),
358 AffineTransform2D::CreateOffset(cropX, cropY)); 356 AffineTransform2D::CreateOffset(cropX, cropY));
359 357
360 Orthanc::ImageAccessor cropped; 358 Orthanc::ImageAccessor cropped;
361 converted_->GetRegion(cropped, cropX, cropY, cropWidth, cropHeight); 359 converted_->GetRegion(cropped, cropX, cropY, cropWidth, cropHeight);
362 360
363 t.Apply(buffer, cropped, interpolation, false); 361 t.Apply(buffer, cropped, interpolation, false);
409 { 407 {
410 throw Orthanc::OrthancException(Orthanc::ErrorCode_NullPointer); 408 throw Orthanc::OrthancException(Orthanc::ErrorCode_NullPointer);
411 } 409 }
412 410
413 std::auto_ptr<RadiographyLayer> raii(layer); 411 std::auto_ptr<RadiographyLayer> raii(layer);
414 412
415 size_t index = countLayers_++; 413 size_t index = countLayers_++;
416 raii->SetIndex(index); 414 raii->SetIndex(index);
417 layers_[index] = raii.release(); 415 layers_[index] = raii.release();
418 416
419 EmitMessage(GeometryChangedMessage(*this)); 417 EmitMessage(GeometryChangedMessage(*this));
420 EmitMessage(ContentChangedMessage(*this)); 418 EmitMessage(ContentChangedMessage(*this));
421 419
422 return *layer; 420 return *layer;
423 } 421 }
424 422
425 423
426 RadiographyScene::RadiographyScene(MessageBroker& broker) : 424 RadiographyScene::RadiographyScene(MessageBroker& broker) :
427 IObserver(broker), 425 IObserver(broker),
428 IObservable(broker), 426 IObservable(broker),
429 countLayers_(0), 427 countLayers_(0),
487 alpha->LoadText(font, utf8); 485 alpha->LoadText(font, utf8);
488 486
489 return RegisterLayer(alpha.release()); 487 return RegisterLayer(alpha.release());
490 } 488 }
491 489
492 490
493 RadiographyLayer& RadiographyScene::LoadTestBlock(unsigned int width, 491 RadiographyLayer& RadiographyScene::LoadTestBlock(unsigned int width,
494 unsigned int height) 492 unsigned int height)
495 { 493 {
496 std::auto_ptr<Orthanc::Image> block(new Orthanc::Image(Orthanc::PixelFormat_Grayscale8, width, height, false)); 494 std::auto_ptr<Orthanc::Image> block(new Orthanc::Image(Orthanc::PixelFormat_Grayscale8, width, height, false));
497 495
518 alpha->SetAlpha(block.release()); 516 alpha->SetAlpha(block.release());
519 517
520 return RegisterLayer(alpha.release()); 518 return RegisterLayer(alpha.release());
521 } 519 }
522 520
523 521
524 RadiographyLayer& RadiographyScene::LoadDicomFrame(OrthancApiClient& orthanc, 522 RadiographyLayer& RadiographyScene::LoadDicomFrame(OrthancApiClient& orthanc,
525 const std::string& instance, 523 const std::string& instance,
526 unsigned int frame, 524 unsigned int frame,
527 bool httpCompression) 525 bool httpCompression)
528 { 526 {
529 RadiographyLayer& layer = RegisterLayer(new DicomLayer); 527 RadiographyLayer& layer = RegisterLayer(new DicomLayer);
530 528
531 { 529 {
532 IWebService::HttpHeaders headers; 530 IWebService::HttpHeaders headers;
533 std::string uri = "/instances/" + instance + "/tags"; 531 std::string uri = "/instances/" + instance + "/tags";
534 532
535 orthanc.GetBinaryAsync( 533 orthanc.GetBinaryAsync(
536 uri, headers, 534 uri, headers,
537 new Callable<RadiographyScene, OrthancApiClient::BinaryResponseReadyMessage> 535 new Callable<RadiographyScene, OrthancApiClient::BinaryResponseReadyMessage>
538 (*this, &RadiographyScene::OnTagsReceived), NULL, 536 (*this, &RadiographyScene::OnTagsReceived), NULL,
539 new Orthanc::SingleValueObject<size_t>(layer.GetIndex())); 537 new Orthanc::SingleValueObject<size_t>(layer.GetIndex()));
540 } 538 }
541 539
542 { 540 {
543 IWebService::HttpHeaders headers; 541 IWebService::HttpHeaders headers;
544 headers["Accept"] = "image/x-portable-arbitrarymap"; 542 headers["Accept"] = "image/x-portable-arbitrarymap";
545 543
546 if (httpCompression) 544 if (httpCompression)
547 { 545 {
548 headers["Accept-Encoding"] = "gzip"; 546 headers["Accept-Encoding"] = "gzip";
549 } 547 }
550 548
551 std::string uri = ("/instances/" + instance + "/frames/" + 549 std::string uri = ("/instances/" + instance + "/frames/" +
552 boost::lexical_cast<std::string>(frame) + "/image-uint16"); 550 boost::lexical_cast<std::string>(frame) + "/image-uint16");
553 551
554 orthanc.GetBinaryAsync( 552 orthanc.GetBinaryAsync(
555 uri, headers, 553 uri, headers,
556 new Callable<RadiographyScene, OrthancApiClient::BinaryResponseReadyMessage> 554 new Callable<RadiographyScene, OrthancApiClient::BinaryResponseReadyMessage>
557 (*this, &RadiographyScene::OnFrameReceived), NULL, 555 (*this, &RadiographyScene::OnFrameReceived), NULL,
558 new Orthanc::SingleValueObject<size_t>(layer.GetIndex())); 556 new Orthanc::SingleValueObject<size_t>(layer.GetIndex()));
559 } 557 }
560 558
561 return layer; 559 return layer;
562 } 560 }
563 561
564 562
565 RadiographyLayer& RadiographyScene::LoadDicomWebFrame(IWebService& web) 563 RadiographyLayer& RadiographyScene::LoadDicomWebFrame(IWebService& web)
566 { 564 {
567 RadiographyLayer& layer = RegisterLayer(new DicomLayer); 565 RadiographyLayer& layer = RegisterLayer(new DicomLayer);
568 566
569 567
570 return layer; 568 return layer;
571 } 569 }
572 570
573 571 RadiographyLayer& RadiographyScene::SetFrame(IVolumeSlicer* slice)
574 572 {
575 void RadiographyScene::OnTagsReceived(const OrthancApiClient::BinaryResponseReadyMessage& message) 573 RadiographyLayer& layer = RegisterLayer(new DicomLayer);
576 { 574 layersIndexBySlice_[slice] = layer.GetIndex();
577 size_t index = dynamic_cast<const Orthanc::SingleValueObject<size_t>&> 575
578 (message.GetPayload()).GetValue(); 576 slice->RegisterObserverCallback(new Callable<RadiographyScene, IVolumeSlicer::TagsReadyMessage>
579 577 (*this, &RadiographyScene::OnTagsReady));
580 LOG(INFO) << "JSON received: " << message.GetUri().c_str() 578 slice->RegisterObserverCallback(new Callable<RadiographyScene, IVolumeSlicer::FrameReadyMessage>
581 << " (" << message.GetAnswerSize() << " bytes) for layer " << index; 579 (*this, &RadiographyScene::OnImageReady));
582 580
583 Layers::iterator layer = layers_.find(index); 581 // TODO: register failures
582
583 return layer;
584 }
585
586 void RadiographyScene::OnTagsReady(const IVolumeSlicer::TagsReadyMessage &message)
587 {
588 size_t layerIndex = layersIndexBySlice_[&(message.GetOrigin())];
589 Layers::iterator layer = layers_.find(layerIndex);
590
584 if (layer != layers_.end()) 591 if (layer != layers_.end())
585 { 592 {
586 assert(layer->second != NULL); 593 assert(layer->second != NULL);
587 594
588 OrthancPlugins::FullOrthancDataset dicom(message.GetAnswer(), message.GetAnswerSize()); 595 const OrthancPlugins::FullOrthancDataset& dicom = message.GetDicomTags();
589 dynamic_cast<DicomLayer*>(layer->second)->SetDicomTags(dicom); 596 dynamic_cast<DicomLayer*>(layer->second)->SetDicomTags(dicom);
590 597
591 float c, w; 598 float c, w;
592 if (!hasWindowing_ && 599 if (!hasWindowing_ &&
593 layer->second->GetDefaultWindowing(c, w)) 600 layer->second->GetDefaultWindowing(c, w))
596 windowingCenter_ = c; 603 windowingCenter_ = c;
597 windowingWidth_ = w; 604 windowingWidth_ = w;
598 } 605 }
599 606
600 EmitMessage(GeometryChangedMessage(*this)); 607 EmitMessage(GeometryChangedMessage(*this));
601 } 608
602 } 609 CoordinateSystem3D dummy;
603 610 message.GetOrigin().ScheduleLayerCreation(dummy);
611 }
612
613 }
614
615 void RadiographyScene::OnTagsReceived(const OrthancApiClient::BinaryResponseReadyMessage& message)
616 {
617 size_t index = dynamic_cast<const Orthanc::SingleValueObject<size_t>&>
618 (message.GetPayload()).GetValue();
619
620 LOG(INFO) << "JSON received: " << message.GetUri().c_str()
621 << " (" << message.GetAnswerSize() << " bytes) for layer " << index;
622
623 Layers::iterator layer = layers_.find(index);
624 if (layer != layers_.end())
625 {
626 assert(layer->second != NULL);
627
628 OrthancPlugins::FullOrthancDataset dicom(message.GetAnswer(), message.GetAnswerSize());
629 dynamic_cast<DicomLayer*>(layer->second)->SetDicomTags(dicom);
630
631 float c, w;
632 if (!hasWindowing_ &&
633 layer->second->GetDefaultWindowing(c, w))
634 {
635 hasWindowing_ = true;
636 windowingCenter_ = c;
637 windowingWidth_ = w;
638 }
639
640 EmitMessage(GeometryChangedMessage(*this));
641 }
642 }
643
644
645 void RadiographyScene::OnImageReady(const IVolumeSlicer::FrameReadyMessage &message)
646 {
647 size_t layerIndex = layersIndexBySlice_[&(message.GetOrigin())];
648 Layers::iterator layer = layers_.find(layerIndex);
649
650 if (layer != layers_.end())
651 {
652 assert(layer->second != NULL);
653
654 dynamic_cast<DicomLayer*>(layer->second)->SetSourceImage(message.GetImage());
655
656 EmitMessage(ContentChangedMessage(*this));
657 }
658 }
604 659
605 void RadiographyScene::OnFrameReceived(const OrthancApiClient::BinaryResponseReadyMessage& message) 660 void RadiographyScene::OnFrameReceived(const OrthancApiClient::BinaryResponseReadyMessage& message)
606 { 661 {
607 size_t index = dynamic_cast<const Orthanc::SingleValueObject<size_t>&>(message.GetPayload()).GetValue(); 662 size_t index = dynamic_cast<const Orthanc::SingleValueObject<size_t>&>(message.GetPayload()).GetValue();
608 663
609 LOG(INFO) << "DICOM frame received: " << message.GetUri().c_str() 664 LOG(INFO) << "DICOM frame received: " << message.GetUri().c_str()
610 << " (" << message.GetAnswerSize() << " bytes) for layer " << index; 665 << " (" << message.GetAnswerSize() << " bytes) for layer " << index;
611 666
612 Layers::iterator layer = layers_.find(index); 667 Layers::iterator layer = layers_.find(index);
613 if (layer != layers_.end()) 668 if (layer != layers_.end())
614 { 669 {
615 assert(layer->second != NULL); 670 assert(layer->second != NULL);
616 671
617 std::string content; 672 std::string content;
618 if (message.GetAnswerSize() > 0) 673 if (message.GetAnswerSize() > 0)
619 { 674 {
620 content.assign(reinterpret_cast<const char*>(message.GetAnswer()), message.GetAnswerSize()); 675 content.assign(reinterpret_cast<const char*>(message.GetAnswer()), message.GetAnswerSize());
621 } 676 }
622 677
623 std::auto_ptr<Orthanc::PamReader> reader(new Orthanc::PamReader); 678 boost::shared_ptr<Orthanc::PamReader> reader(new Orthanc::PamReader);
624 reader->ReadFromMemory(content); 679 reader->ReadFromMemory(content);
625 dynamic_cast<DicomLayer*>(layer->second)->SetSourceImage(reader.release()); 680 dynamic_cast<DicomLayer*>(layer->second)->SetSourceImage(reader);
626 681
627 EmitMessage(ContentChangedMessage(*this)); 682 EmitMessage(ContentChangedMessage(*this));
628 } 683 }
629 } 684 }
630 685
640 extent.Union(it->second->GetExtent()); 695 extent.Union(it->second->GetExtent());
641 } 696 }
642 697
643 return extent; 698 return extent;
644 } 699 }
645 700
646 701
647 void RadiographyScene::Render(Orthanc::ImageAccessor& buffer, 702 void RadiographyScene::Render(Orthanc::ImageAccessor& buffer,
648 const AffineTransform2D& viewTransform, 703 const AffineTransform2D& viewTransform,
649 ImageInterpolation interpolation) const 704 ImageInterpolation interpolation) const
650 { 705 {
683 } 738 }
684 739
685 return false; 740 return false;
686 } 741 }
687 742
688 743
689 void RadiographyScene::DrawBorder(CairoContext& context, 744 void RadiographyScene::DrawBorder(CairoContext& context,
690 unsigned int layer, 745 unsigned int layer,
691 double zoom) 746 double zoom)
692 { 747 {
693 Layers::const_iterator found = layers_.find(layer); 748 Layers::const_iterator found = layers_.find(layer);
694 749
695 if (found != layers_.end()) 750 if (found != layers_.end())
696 { 751 {
697 context.SetSourceColor(255, 0, 0); 752 context.SetSourceColor(255, 0, 0);
698 found->second->DrawBorders(context, zoom); 753 found->second->DrawBorders(context, zoom);
699 } 754 }
702 757
703 void RadiographyScene::GetRange(float& minValue, 758 void RadiographyScene::GetRange(float& minValue,
704 float& maxValue) const 759 float& maxValue) const
705 { 760 {
706 bool first = true; 761 bool first = true;
707 762
708 for (Layers::const_iterator it = layers_.begin(); 763 for (Layers::const_iterator it = layers_.begin();
709 it != layers_.end(); it++) 764 it != layers_.end(); it++)
710 { 765 {
711 assert(it->second != NULL); 766 assert(it->second != NULL);
712 767
748 if (pixelSpacingX <= 0 || 803 if (pixelSpacingX <= 0 ||
749 pixelSpacingY <= 0) 804 pixelSpacingY <= 0)
750 { 805 {
751 throw Orthanc::OrthancException(Orthanc::ErrorCode_ParameterOutOfRange); 806 throw Orthanc::OrthancException(Orthanc::ErrorCode_ParameterOutOfRange);
752 } 807 }
753 808
754 LOG(INFO) << "Exporting DICOM"; 809 LOG(INFO) << "Exporting DICOM";
755 810
756 Extent2D extent = GetSceneExtent(); 811 Extent2D extent = GetSceneExtent();
757 812
758 int w = std::ceil(extent.GetWidth() / pixelSpacingX); 813 int w = std::ceil(extent.GetWidth() / pixelSpacingX);
766 Orthanc::Image layers(Orthanc::PixelFormat_Float32, 821 Orthanc::Image layers(Orthanc::PixelFormat_Float32,
767 static_cast<unsigned int>(w), 822 static_cast<unsigned int>(w),
768 static_cast<unsigned int>(h), false); 823 static_cast<unsigned int>(h), false);
769 824
770 AffineTransform2D view = AffineTransform2D::Combine( 825 AffineTransform2D view = AffineTransform2D::Combine(
771 AffineTransform2D::CreateScaling(1.0 / pixelSpacingX, 1.0 / pixelSpacingY), 826 AffineTransform2D::CreateScaling(1.0 / pixelSpacingX, 1.0 / pixelSpacingY),
772 AffineTransform2D::CreateOffset(-extent.GetX1(), -extent.GetY1())); 827 AffineTransform2D::CreateOffset(-extent.GetX1(), -extent.GetY1()));
773 828
774 Render(layers, view, interpolation); 829 Render(layers, view, interpolation);
775 830
776 Orthanc::Image rendered(Orthanc::PixelFormat_Grayscale16, 831 Orthanc::Image rendered(Orthanc::PixelFormat_Grayscale16,
777 layers.GetWidth(), layers.GetHeight(), false); 832 layers.GetWidth(), layers.GetHeight(), false);
778 Orthanc::ImageProcessing::Convert(rendered, layers); 833 Orthanc::ImageProcessing::Convert(rendered, layers);
799 std::set<Orthanc::DicomTag> tags; 854 std::set<Orthanc::DicomTag> tags;
800 dicom.GetTags(tags); 855 dicom.GetTags(tags);
801 856
802 Json::Value json = Json::objectValue; 857 Json::Value json = Json::objectValue;
803 json["Tags"] = Json::objectValue; 858 json["Tags"] = Json::objectValue;
804 859
805 for (std::set<Orthanc::DicomTag>::const_iterator 860 for (std::set<Orthanc::DicomTag>::const_iterator
806 tag = tags.begin(); tag != tags.end(); ++tag) 861 tag = tags.begin(); tag != tags.end(); ++tag)
807 { 862 {
808 const Orthanc::DicomValue& value = dicom.GetValue(*tag); 863 const Orthanc::DicomValue& value = dicom.GetValue(*tag);
809 if (!value.IsNull() && 864 if (!value.IsNull() &&
810 !value.IsBinary()) 865 !value.IsBinary())
811 { 866 {
812 json["Tags"][tag->Format()] = value.GetContent(); 867 json["Tags"][tag->Format()] = value.GetContent();
813 } 868 }
814 } 869 }
815 870
816 json["Tags"][Orthanc::DICOM_TAG_PHOTOMETRIC_INTERPRETATION.Format()] = 871 json["Tags"][Orthanc::DICOM_TAG_PHOTOMETRIC_INTERPRETATION.Format()] =
817 (invert ? "MONOCHROME1" : "MONOCHROME2"); 872 (invert ? "MONOCHROME1" : "MONOCHROME2");
818 873
819 // WARNING: The order of PixelSpacing is Y/X. We use "%0.8f" to 874 // WARNING: The order of PixelSpacing is Y/X. We use "%0.8f" to
820 // avoid floating-point numbers to grow over 16 characters, 875 // avoid floating-point numbers to grow over 16 characters,
821 // which would be invalid according to DICOM standard 876 // which would be invalid according to DICOM standard
822 // ("dciodvfy" would complain). 877 // ("dciodvfy" would complain).
823 char buf[32]; 878 char buf[32];
824 sprintf(buf, "%0.8f\\%0.8f", pixelSpacingY, pixelSpacingX); 879 sprintf(buf, "%0.8f\\%0.8f", pixelSpacingY, pixelSpacingX);
825 880
826 json["Tags"][Orthanc::DICOM_TAG_PIXEL_SPACING.Format()] = buf; 881 json["Tags"][Orthanc::DICOM_TAG_PIXEL_SPACING.Format()] = buf;
827 882
828 float center, width; 883 float center, width;
829 if (GetWindowing(center, width)) 884 if (GetWindowing(center, width))
830 { 885 {
831 json["Tags"][Orthanc::DICOM_TAG_WINDOW_CENTER.Format()] = 886 json["Tags"][Orthanc::DICOM_TAG_WINDOW_CENTER.Format()] =
832 boost::lexical_cast<std::string>(boost::math::iround(center)); 887 boost::lexical_cast<std::string>(boost::math::iround(center));
833 888
834 json["Tags"][Orthanc::DICOM_TAG_WINDOW_WIDTH.Format()] = 889 json["Tags"][Orthanc::DICOM_TAG_WINDOW_WIDTH.Format()] =
835 boost::lexical_cast<std::string>(boost::math::iround(width)); 890 boost::lexical_cast<std::string>(boost::math::iround(width));
836 } 891 }
837 892
838 // This is Data URI scheme: https://en.wikipedia.org/wiki/Data_URI_scheme 893 // This is Data URI scheme: https://en.wikipedia.org/wiki/Data_URI_scheme
839 json["Content"] = ("data:" + 894 json["Content"] = ("data:" +
840 std::string(usePam ? Orthanc::MIME_PAM : Orthanc::MIME_PNG) + 895 std::string(usePam ? Orthanc::MIME_PAM : Orthanc::MIME_PNG) +
841 ";base64," + base64); 896 ";base64," + base64);
842 897
843 orthanc.PostJsonAsyncExpectJson( 898 orthanc.PostJsonAsyncExpectJson(
844 "/tools/create-dicom", json, 899 "/tools/create-dicom", json,
845 new Callable<RadiographyScene, OrthancApiClient::JsonResponseReadyMessage> 900 new Callable<RadiographyScene, OrthancApiClient::JsonResponseReadyMessage>
846 (*this, &RadiographyScene::OnDicomExported), 901 (*this, &RadiographyScene::OnDicomExported),
847 NULL, NULL); 902 NULL, NULL);
848 } 903 }
849 904
850 905
851 void RadiographyScene::OnDicomExported(const OrthancApiClient::JsonResponseReadyMessage& message) 906 void RadiographyScene::OnDicomExported(const OrthancApiClient::JsonResponseReadyMessage& message)
852 { 907 {
859 { 914 {
860 LOG(INFO) << "DICOMweb WADO-RS received: " << message.GetAnswerSize() << " bytes"; 915 LOG(INFO) << "DICOMweb WADO-RS received: " << message.GetAnswerSize() << " bytes";
861 916
862 const IWebService::HttpHeaders& h = message.GetAnswerHttpHeaders(); 917 const IWebService::HttpHeaders& h = message.GetAnswerHttpHeaders();
863 for (IWebService::HttpHeaders::const_iterator 918 for (IWebService::HttpHeaders::const_iterator
864 it = h.begin(); it != h.end(); ++it) 919 it = h.begin(); it != h.end(); ++it)
865 { 920 {
866 printf("[%s] = [%s]\n", it->first.c_str(), it->second.c_str()); 921 printf("[%s] = [%s]\n", it->first.c_str(), it->second.c_str());
867 } 922 }
868 } 923 }
869 924