comparison Applications/Samples/SingleFrameEditorApplication.h @ 339:5a7915b23138 am-2

cont
author Sebastien Jodogne <s.jodogne@gmail.com>
date Fri, 19 Oct 2018 14:44:12 +0200
parents b3b3fa0e3689
children f5d5814a41a0
comparison
equal deleted inserted replaced
338:b3b3fa0e3689 339:5a7915b23138
21 21
22 #pragma once 22 #pragma once
23 23
24 #include "SampleApplicationBase.h" 24 #include "SampleApplicationBase.h"
25 25
26 #include "../../Framework/Toolbox/GeometryToolbox.h"
27 #include "../../Framework/Toolbox/ImageGeometry.h"
26 #include "../../Framework/Layers/OrthancFrameLayerSource.h" 28 #include "../../Framework/Layers/OrthancFrameLayerSource.h"
27 29
28 #include <Core/DicomFormat/DicomArray.h> 30 #include <Core/DicomFormat/DicomArray.h>
31 #include <Core/Images/ImageProcessing.h>
29 #include <Core/Images/PamReader.h> 32 #include <Core/Images/PamReader.h>
30 #include <Core/Logging.h> 33 #include <Core/Logging.h>
31 #include <Core/Toolbox.h> 34 #include <Core/Toolbox.h>
32 #include <Plugins/Samples/Common/FullOrthancDataset.h> 35 #include <Plugins/Samples/Common/FullOrthancDataset.h>
36 #include <Plugins/Samples/Common/DicomDatasetReader.h>
33 37
34 namespace OrthancStone 38 namespace OrthancStone
35 { 39 {
36 class BitmapStack : 40 class BitmapStack :
37 public IObserver, 41 public IObserver,
44 private: 48 private:
45 class Bitmap : public boost::noncopyable 49 class Bitmap : public boost::noncopyable
46 { 50 {
47 private: 51 private:
48 std::string uuid_; // TODO is this necessary? 52 std::string uuid_; // TODO is this necessary?
53 bool visible_;
49 std::auto_ptr<Orthanc::ImageAccessor> source_; 54 std::auto_ptr<Orthanc::ImageAccessor> source_;
50 std::auto_ptr<Orthanc::ImageAccessor> converted_; // Float32 or RGB24 55 std::auto_ptr<Orthanc::ImageAccessor> converted_; // Float32 or RGB24
51 std::auto_ptr<Orthanc::Image> alpha_; // Grayscale8 (if any) 56 std::auto_ptr<Orthanc::Image> alpha_; // Grayscale8 (if any)
52 std::auto_ptr<DicomFrameConverter> converter_; 57 std::auto_ptr<DicomFrameConverter> converter_;
58 unsigned int width_;
59 unsigned int height_;
60 bool hasCrop_;
61 unsigned int cropX_;
62 unsigned int cropY_;
63 unsigned int cropWidth_;
64 unsigned int cropHeight_;
65 Matrix transform_;
53 66
54 void ApplyConverter() 67 void ApplyConverter()
55 { 68 {
56 if (source_.get() != NULL && 69 if (source_.get() != NULL &&
57 converter_.get() != NULL) 70 converter_.get() != NULL)
58 { 71 {
59 printf("CONVERTED!\n"); 72 if (width_ != source_->GetWidth() ||
73 height_ != source_->GetHeight())
74 {
75 throw Orthanc::OrthancException(Orthanc::ErrorCode_BadFileFormat);
76 }
77
78 printf("CONVERTED! %dx%d\n", width_, height_);
60 converted_.reset(converter_->ConvertFrame(*source_)); 79 converted_.reset(converter_->ConvertFrame(*source_));
61 } 80 }
62 } 81 }
63 82
83 void AddToExtent(Extent2D& extent,
84 double x,
85 double y) const
86 {
87 Vector p;
88 LinearAlgebra::AssignVector(p, x, y, 0);
89
90 Vector q = LinearAlgebra::Product(transform_, p);
91
92 if (!LinearAlgebra::IsCloseToZero(q[2]))
93 {
94 throw Orthanc::OrthancException(Orthanc::ErrorCode_InternalError);
95 }
96 else
97 {
98 extent.AddPoint(q[0], q[1]);
99 }
100 }
101
102
64 public: 103 public:
65 Bitmap(const std::string& uuid) : 104 Bitmap(const std::string& uuid) :
66 uuid_(uuid) 105 uuid_(uuid),
67 { 106 visible_(true),
107 width_(0),
108 height_(0),
109 hasCrop_(false),
110 transform_(LinearAlgebra::IdentityMatrix(3))
111 {
112 }
113
114 void ResetCrop()
115 {
116 hasCrop_ = false;
117 }
118
119 void Crop(unsigned int x,
120 unsigned int y,
121 unsigned int width,
122 unsigned int height)
123 {
124 hasCrop_ = true;
125 cropX_ = x;
126 cropY_ = y;
127 cropWidth_ = width;
128 cropHeight_ = height;
129 }
130
131 void GetCrop(unsigned int& x,
132 unsigned int& y,
133 unsigned int& width,
134 unsigned int& height) const
135 {
136 if (hasCrop_)
137 {
138 x = cropX_;
139 y = cropY_;
140 width = cropWidth_;
141 height = cropHeight_;
142 }
143 else
144 {
145 x = 0;
146 y = 0;
147 width = width_;
148 height = height_;
149 }
150 }
151
152 bool IsVisible() const
153 {
154 return visible_;
155 }
156
157 void SetVisible(bool visible)
158 {
159 visible_ = visible;
160 }
161
162
163 static OrthancPlugins::DicomTag ConvertTag(const Orthanc::DicomTag& tag)
164 {
165 return OrthancPlugins::DicomTag(tag.GetGroup(), tag.GetElement());
68 } 166 }
69 167
70 void SetDicomTags(const OrthancPlugins::FullOrthancDataset& dataset) 168 void SetDicomTags(const OrthancPlugins::FullOrthancDataset& dataset)
71 { 169 {
72 converter_.reset(new DicomFrameConverter); 170 converter_.reset(new DicomFrameConverter);
73 converter_->ReadParameters(dataset); 171 converter_->ReadParameters(dataset);
74 ApplyConverter(); 172 ApplyConverter();
75 } 173
76 174 transform_ = LinearAlgebra::IdentityMatrix(3);
175
176 std::string tmp;
177 Vector pixelSpacing;
178
179 if (dataset.GetStringValue(tmp, ConvertTag(Orthanc::DICOM_TAG_PIXEL_SPACING)) &&
180 LinearAlgebra::ParseVector(pixelSpacing, tmp) &&
181 pixelSpacing.size() == 2)
182 {
183 transform_(0, 0) = pixelSpacing[0];
184 transform_(1, 1) = pixelSpacing[1];
185 }
186
187 OrthancPlugins::DicomDatasetReader reader(dataset);
188
189 if (!reader.GetUnsignedIntegerValue(width_, ConvertTag(Orthanc::DICOM_TAG_COLUMNS)) ||
190 !reader.GetUnsignedIntegerValue(height_, ConvertTag(Orthanc::DICOM_TAG_ROWS)))
191 {
192 throw Orthanc::OrthancException(Orthanc::ErrorCode_BadFileFormat);
193 }
194 }
195
196
77 void SetSourceImage(Orthanc::ImageAccessor* image) // Takes ownership 197 void SetSourceImage(Orthanc::ImageAccessor* image) // Takes ownership
78 { 198 {
79 source_.reset(image); 199 source_.reset(image);
80 ApplyConverter(); 200 ApplyConverter();
81 } 201 }
82 202
203
83 bool GetDefaultWindowing(float& center, 204 bool GetDefaultWindowing(float& center,
84 float& width) const 205 float& width) const
85 { 206 {
86 if (converter_.get() != NULL && 207 if (converter_.get() != NULL &&
87 converter_->HasDefaultWindow()) 208 converter_->HasDefaultWindow())
88 { 209 {
89 center = static_cast<float>(converter_->GetDefaultWindowCenter()); 210 center = static_cast<float>(converter_->GetDefaultWindowCenter());
90 width = static_cast<float>(converter_->GetDefaultWindowWidth()); 211 width = static_cast<float>(converter_->GetDefaultWindowWidth());
212 return true;
213 }
214 else
215 {
216 return false;
217 }
218 }
219
220
221 Extent2D GetExtent() const
222 {
223 Extent2D extent;
224
225 unsigned int x, y, width, height;
226 GetCrop(x, y, width, height);
227
228 double dx = static_cast<double>(x) - 0.5;
229 double dy = static_cast<double>(y) - 0.5;
230 double dwidth = static_cast<double>(width);
231 double dheight = static_cast<double>(height);
232
233 AddToExtent(extent, dx, dy);
234 AddToExtent(extent, dx + dwidth, dy);
235 AddToExtent(extent, dx, dy + dheight);
236 AddToExtent(extent, dx + dwidth, dy + dheight);
237
238 return extent;
239 }
240
241
242 void Render(Orthanc::ImageAccessor& buffer,
243 const ViewportGeometry& view) const
244 {
245 if (converted_.get() != NULL)
246 {
247 ApplyProjectiveTransform(buffer, *converted_, transform_, ImageInterpolation_Nearest); // TODO
91 } 248 }
92 } 249 }
93 }; 250 };
94 251
95 252
120 { 277 {
121 assert(it->second != NULL); 278 assert(it->second != NULL);
122 delete it->second; 279 delete it->second;
123 } 280 }
124 } 281 }
282
283
284 void GetWindowing(float& center,
285 float& width)
286 {
287 if (hasWindowing_)
288 {
289 center = windowingCenter_;
290 width = windowingWidth_;
291 }
292 else
293 {
294 center = 128;
295 width = 256;
296 }
297 }
125 298
126 299
127 std::string LoadFrame(const std::string& instance, 300 std::string LoadFrame(const std::string& instance,
128 unsigned int frame, 301 unsigned int frame,
129 bool httpCompression) 302 bool httpCompression)
220 std::auto_ptr<Orthanc::PamReader> reader(new Orthanc::PamReader); 393 std::auto_ptr<Orthanc::PamReader> reader(new Orthanc::PamReader);
221 reader->ReadFromMemory(content); 394 reader->ReadFromMemory(content);
222 bitmap->second->SetSourceImage(reader.release()); 395 bitmap->second->SetSourceImage(reader.release());
223 396
224 EmitMessage(ContentChangedMessage(*this)); 397 EmitMessage(ContentChangedMessage(*this));
398 }
399 }
400
401
402 Extent2D GetSceneExtent() const
403 {
404 Extent2D extent;
405
406 for (Bitmaps::const_iterator it = bitmaps_.begin();
407 it != bitmaps_.end(); ++it)
408 {
409 assert(it->second != NULL);
410 extent.Union(it->second->GetExtent());
411 }
412
413 printf("(%.02f,%.02f) (%.02f,%.02f) \n",
414 extent.GetX1(), extent.GetY1(),
415 extent.GetX2(), extent.GetY2());
416
417 return extent;
418 }
419
420
421 void Render(Orthanc::ImageAccessor& buffer,
422 const ViewportGeometry& view)
423 {
424 Orthanc::ImageProcessing::Set(buffer, 0);
425
426 for (Bitmaps::const_iterator it = bitmaps_.begin();
427 it != bitmaps_.end(); ++it)
428 {
429 assert(it->second != NULL);
430 it->second->Render(buffer, view);
225 } 431 }
226 } 432 }
227 }; 433 };
228 434
229 435
236 BitmapStack& stack_; 442 BitmapStack& stack_;
237 443
238 protected: 444 protected:
239 virtual Extent2D GetSceneExtent() 445 virtual Extent2D GetSceneExtent()
240 { 446 {
241 return Extent2D(-1, -1, 1, 1); 447 printf("Get extent\n");
448 return stack_.GetSceneExtent();
242 } 449 }
243 450
244 virtual bool RenderScene(CairoContext& context, 451 virtual bool RenderScene(CairoContext& context,
245 const ViewportGeometry& view) 452 const ViewportGeometry& view)
246 { 453 {
454 Orthanc::Image buffer(Orthanc::PixelFormat_Float32, context.GetWidth(), context.GetHeight(), false);
455 stack_.Render(buffer, view);
456
457 // As in GrayscaleFrameRenderer
458
459 float windowCenter, windowWidth;
460 stack_.GetWindowing(windowCenter, windowWidth);
461
462 float x0 = windowCenter - windowWidth / 2.0f;
463 float x1 = windowCenter + windowWidth / 2.0f;
464
465 CairoSurface cairo(context.GetWidth(), context.GetHeight());
466
467 Orthanc::ImageAccessor target;
468 cairo.GetAccessor(target);
469
470 const unsigned int width = cairo.GetWidth();
471 const unsigned int height = cairo.GetHeight();
472
473 for (unsigned int y = 0; y < height; y++)
474 {
475 const float* p = reinterpret_cast<const float*>(buffer.GetConstRow(y));
476 uint8_t* q = reinterpret_cast<uint8_t*>(target.GetRow(y));
477
478 for (unsigned int x = 0; x < width; x++, p++, q += 4)
479 {
480 uint8_t v = 0;
481 if (windowWidth >= 0.001f) // Avoid division by zero
482 {
483 if (*p >= x1)
484 {
485 v = 255;
486 }
487 else if (*p <= x0)
488 {
489 v = 0;
490 }
491 else
492 {
493 // https://en.wikipedia.org/wiki/Linear_interpolation
494 v = static_cast<uint8_t>(255.0f * (*p - x0) / (x1 - x0));
495 }
496
497 // TODO MONOCHROME1
498 /*if (invert_)
499 {
500 v = 255 - v;
501 }*/
502 }
503
504 q[3] = 255;
505 q[2] = v;
506 q[1] = v;
507 q[0] = 128;
508 }
509 }
510
511
512
513 cairo_t* cr = context.GetObject();
514
515 // Clear background
516 cairo_set_source_rgb(cr, 0, 0, 0);
517 cairo_paint(cr);
518
519 #if 1
520 cairo_save(cr);
521 cairo_set_source_surface(cr, cairo.GetObject(), 0, 0);
522 cairo_pattern_set_filter(cairo_get_source(cr), CAIRO_FILTER_NEAREST); //TODO
523 cairo_paint(cr);
524 cairo_restore(cr);
525 #else
526 Extent2D extent = stack_.GetSceneExtent();
527
528 float color = 0.5;
529 cairo_set_source_rgb(cr, 0, 1.0f - color, color);
530 cairo_rectangle(cr, extent.GetX1(), extent.GetY1(), extent.GetX2(), extent.GetY2());
531 cairo_fill(cr);
532 #endif
533
247 return true; 534 return true;
248 } 535 }
249 536
250 public: 537 public:
251 BitmapStackWidget(MessageBroker& broker, 538 BitmapStackWidget(MessageBroker& broker,
442 729
443 orthancApiClient_.reset(new OrthancApiClient(IObserver::broker_, context_->GetWebService())); 730 orthancApiClient_.reset(new OrthancApiClient(IObserver::broker_, context_->GetWebService()));
444 731
445 stack_.reset(new BitmapStack(IObserver::broker_, *orthancApiClient_)); 732 stack_.reset(new BitmapStack(IObserver::broker_, *orthancApiClient_));
446 stack_->LoadFrame(instance, frame, false); 733 stack_->LoadFrame(instance, frame, false);
447 stack_->LoadFrame(instance, frame, false); 734 //stack_->LoadFrame(instance, frame, false);
448 735
449 mainWidget_ = new BitmapStackWidget(IObserver::broker_, *stack_, "main-widget"); 736 mainWidget_ = new BitmapStackWidget(IObserver::broker_, *stack_, "main-widget");
450 mainWidget_->SetTransmitMouseOver(true); 737 mainWidget_->SetTransmitMouseOver(true);
451 738
452 mainWidgetInteractor_.reset(new Interactor(*this)); 739 mainWidgetInteractor_.reset(new Interactor(*this));