Mercurial > hg > orthanc-stone
comparison Framework/Radiography/RadiographyScene.cpp @ 410:6decc0ba9da5
rename RadiographyScene::Layer as RadiographyLayer
author | Sebastien Jodogne <s.jodogne@gmail.com> |
---|---|
date | Mon, 12 Nov 2018 15:52:03 +0100 |
parents | 99c9b3238008 |
children | 71c16998fcc8 |
comparison
equal
deleted
inserted
replaced
409:99c9b3238008 | 410:6decc0ba9da5 |
---|---|
19 **/ | 19 **/ |
20 | 20 |
21 | 21 |
22 #include "RadiographyScene.h" | 22 #include "RadiographyScene.h" |
23 | 23 |
24 #include "../Toolbox/ImageGeometry.h" | |
25 #include "../Toolbox/DicomFrameConverter.h" | 24 #include "../Toolbox/DicomFrameConverter.h" |
26 | 25 |
27 #include <Core/Images/Image.h> | 26 #include <Core/Images/Image.h> |
28 #include <Core/Images/ImageProcessing.h> | 27 #include <Core/Images/ImageProcessing.h> |
29 #include <Core/Images/PamReader.h> | 28 #include <Core/Images/PamReader.h> |
35 #include <Plugins/Samples/Common/FullOrthancDataset.h> | 34 #include <Plugins/Samples/Common/FullOrthancDataset.h> |
36 | 35 |
37 | 36 |
38 namespace OrthancStone | 37 namespace OrthancStone |
39 { | 38 { |
40 static double Square(double x) | |
41 { | |
42 return x * x; | |
43 } | |
44 | |
45 | |
46 void RadiographyScene::Layer::UpdateTransform() | |
47 { | |
48 transform_ = AffineTransform2D::CreateScaling(pixelSpacingX_, pixelSpacingY_); | |
49 | |
50 double centerX, centerY; | |
51 GetCenter(centerX, centerY); | |
52 | |
53 transform_ = AffineTransform2D::Combine( | |
54 AffineTransform2D::CreateOffset(panX_ + centerX, panY_ + centerY), | |
55 AffineTransform2D::CreateRotation(angle_), | |
56 AffineTransform2D::CreateOffset(-centerX, -centerY), | |
57 transform_); | |
58 | |
59 transformInverse_ = AffineTransform2D::Invert(transform_); | |
60 } | |
61 | |
62 | |
63 void RadiographyScene::Layer::AddToExtent(Extent2D& extent, | |
64 double x, | |
65 double y) const | |
66 { | |
67 transform_.Apply(x, y); | |
68 extent.AddPoint(x, y); | |
69 } | |
70 | |
71 | |
72 void RadiographyScene::Layer::GetCornerInternal(double& x, | |
73 double& y, | |
74 Corner corner, | |
75 unsigned int cropX, | |
76 unsigned int cropY, | |
77 unsigned int cropWidth, | |
78 unsigned int cropHeight) const | |
79 { | |
80 double dx = static_cast<double>(cropX); | |
81 double dy = static_cast<double>(cropY); | |
82 double dwidth = static_cast<double>(cropWidth); | |
83 double dheight = static_cast<double>(cropHeight); | |
84 | |
85 switch (corner) | |
86 { | |
87 case Corner_TopLeft: | |
88 x = dx; | |
89 y = dy; | |
90 break; | |
91 | |
92 case Corner_TopRight: | |
93 x = dx + dwidth; | |
94 y = dy; | |
95 break; | |
96 | |
97 case Corner_BottomLeft: | |
98 x = dx; | |
99 y = dy + dheight; | |
100 break; | |
101 | |
102 case Corner_BottomRight: | |
103 x = dx + dwidth; | |
104 y = dy + dheight; | |
105 break; | |
106 | |
107 default: | |
108 throw Orthanc::OrthancException(Orthanc::ErrorCode_ParameterOutOfRange); | |
109 } | |
110 | |
111 transform_.Apply(x, y); | |
112 } | |
113 | |
114 | |
115 bool RadiographyScene::Layer::Contains(double x, | |
116 double y) const | |
117 { | |
118 transformInverse_.Apply(x, y); | |
119 | |
120 unsigned int cropX, cropY, cropWidth, cropHeight; | |
121 GetCrop(cropX, cropY, cropWidth, cropHeight); | |
122 | |
123 return (x >= cropX && x <= cropX + cropWidth && | |
124 y >= cropY && y <= cropY + cropHeight); | |
125 } | |
126 | |
127 | |
128 void RadiographyScene::Layer::DrawBorders(CairoContext& context, | |
129 double zoom) | |
130 { | |
131 unsigned int cx, cy, width, height; | |
132 GetCrop(cx, cy, width, height); | |
133 | |
134 double dx = static_cast<double>(cx); | |
135 double dy = static_cast<double>(cy); | |
136 double dwidth = static_cast<double>(width); | |
137 double dheight = static_cast<double>(height); | |
138 | |
139 cairo_t* cr = context.GetObject(); | |
140 cairo_set_line_width(cr, 2.0 / zoom); | |
141 | |
142 double x, y; | |
143 x = dx; | |
144 y = dy; | |
145 transform_.Apply(x, y); | |
146 cairo_move_to(cr, x, y); | |
147 | |
148 x = dx + dwidth; | |
149 y = dy; | |
150 transform_.Apply(x, y); | |
151 cairo_line_to(cr, x, y); | |
152 | |
153 x = dx + dwidth; | |
154 y = dy + dheight; | |
155 transform_.Apply(x, y); | |
156 cairo_line_to(cr, x, y); | |
157 | |
158 x = dx; | |
159 y = dy + dheight; | |
160 transform_.Apply(x, y); | |
161 cairo_line_to(cr, x, y); | |
162 | |
163 x = dx; | |
164 y = dy; | |
165 transform_.Apply(x, y); | |
166 cairo_line_to(cr, x, y); | |
167 | |
168 cairo_stroke(cr); | |
169 } | |
170 | |
171 | |
172 RadiographyScene::Layer::Layer() : | |
173 index_(0), | |
174 hasSize_(false), | |
175 width_(0), | |
176 height_(0), | |
177 hasCrop_(false), | |
178 pixelSpacingX_(1), | |
179 pixelSpacingY_(1), | |
180 panX_(0), | |
181 panY_(0), | |
182 angle_(0), | |
183 resizeable_(false) | |
184 { | |
185 UpdateTransform(); | |
186 } | |
187 | |
188 | |
189 void RadiographyScene::Layer::SetCrop(unsigned int x, | |
190 unsigned int y, | |
191 unsigned int width, | |
192 unsigned int height) | |
193 { | |
194 if (!hasSize_) | |
195 { | |
196 throw Orthanc::OrthancException(Orthanc::ErrorCode_BadSequenceOfCalls); | |
197 } | |
198 | |
199 if (x + width > width_ || | |
200 y + height > height_) | |
201 { | |
202 throw Orthanc::OrthancException(Orthanc::ErrorCode_ParameterOutOfRange); | |
203 } | |
204 | |
205 hasCrop_ = true; | |
206 cropX_ = x; | |
207 cropY_ = y; | |
208 cropWidth_ = width; | |
209 cropHeight_ = height; | |
210 | |
211 UpdateTransform(); | |
212 } | |
213 | |
214 | |
215 void RadiographyScene::Layer::GetCrop(unsigned int& x, | |
216 unsigned int& y, | |
217 unsigned int& width, | |
218 unsigned int& height) const | |
219 { | |
220 if (hasCrop_) | |
221 { | |
222 x = cropX_; | |
223 y = cropY_; | |
224 width = cropWidth_; | |
225 height = cropHeight_; | |
226 } | |
227 else | |
228 { | |
229 x = 0; | |
230 y = 0; | |
231 width = width_; | |
232 height = height_; | |
233 } | |
234 } | |
235 | |
236 | |
237 void RadiographyScene::Layer::SetAngle(double angle) | |
238 { | |
239 angle_ = angle; | |
240 UpdateTransform(); | |
241 } | |
242 | |
243 | |
244 void RadiographyScene::Layer::SetSize(unsigned int width, | |
245 unsigned int height) | |
246 { | |
247 if (hasSize_ && | |
248 (width != width_ || | |
249 height != height_)) | |
250 { | |
251 throw Orthanc::OrthancException(Orthanc::ErrorCode_IncompatibleImageSize); | |
252 } | |
253 | |
254 hasSize_ = true; | |
255 width_ = width; | |
256 height_ = height; | |
257 | |
258 UpdateTransform(); | |
259 } | |
260 | |
261 | |
262 Extent2D RadiographyScene::Layer::GetExtent() const | |
263 { | |
264 Extent2D extent; | |
265 | |
266 unsigned int x, y, width, height; | |
267 GetCrop(x, y, width, height); | |
268 | |
269 double dx = static_cast<double>(x); | |
270 double dy = static_cast<double>(y); | |
271 double dwidth = static_cast<double>(width); | |
272 double dheight = static_cast<double>(height); | |
273 | |
274 AddToExtent(extent, dx, dy); | |
275 AddToExtent(extent, dx + dwidth, dy); | |
276 AddToExtent(extent, dx, dy + dheight); | |
277 AddToExtent(extent, dx + dwidth, dy + dheight); | |
278 | |
279 return extent; | |
280 } | |
281 | |
282 | |
283 bool RadiographyScene::Layer::GetPixel(unsigned int& imageX, | |
284 unsigned int& imageY, | |
285 double sceneX, | |
286 double sceneY) const | |
287 { | |
288 if (width_ == 0 || | |
289 height_ == 0) | |
290 { | |
291 return false; | |
292 } | |
293 else | |
294 { | |
295 transformInverse_.Apply(sceneX, sceneY); | |
296 | |
297 int x = static_cast<int>(std::floor(sceneX)); | |
298 int y = static_cast<int>(std::floor(sceneY)); | |
299 | |
300 if (x < 0) | |
301 { | |
302 imageX = 0; | |
303 } | |
304 else if (x >= static_cast<int>(width_)) | |
305 { | |
306 imageX = width_; | |
307 } | |
308 else | |
309 { | |
310 imageX = static_cast<unsigned int>(x); | |
311 } | |
312 | |
313 if (y < 0) | |
314 { | |
315 imageY = 0; | |
316 } | |
317 else if (y >= static_cast<int>(height_)) | |
318 { | |
319 imageY = height_; | |
320 } | |
321 else | |
322 { | |
323 imageY = static_cast<unsigned int>(y); | |
324 } | |
325 | |
326 return true; | |
327 } | |
328 } | |
329 | |
330 | |
331 void RadiographyScene::Layer::SetPan(double x, | |
332 double y) | |
333 { | |
334 panX_ = x; | |
335 panY_ = y; | |
336 UpdateTransform(); | |
337 } | |
338 | |
339 | |
340 void RadiographyScene::Layer::SetPixelSpacing(double x, | |
341 double y) | |
342 { | |
343 pixelSpacingX_ = x; | |
344 pixelSpacingY_ = y; | |
345 UpdateTransform(); | |
346 } | |
347 | |
348 | |
349 void RadiographyScene::Layer::GetCenter(double& centerX, | |
350 double& centerY) const | |
351 { | |
352 centerX = static_cast<double>(width_) / 2.0; | |
353 centerY = static_cast<double>(height_) / 2.0; | |
354 transform_.Apply(centerX, centerY); | |
355 } | |
356 | |
357 | |
358 void RadiographyScene::Layer::GetCorner(double& x /* out */, | |
359 double& y /* out */, | |
360 Corner corner) const | |
361 { | |
362 unsigned int cropX, cropY, cropWidth, cropHeight; | |
363 GetCrop(cropX, cropY, cropWidth, cropHeight); | |
364 GetCornerInternal(x, y, corner, cropX, cropY, cropWidth, cropHeight); | |
365 } | |
366 | |
367 | |
368 bool RadiographyScene::Layer::LookupCorner(Corner& corner /* out */, | |
369 double x, | |
370 double y, | |
371 double zoom, | |
372 double viewportDistance) const | |
373 { | |
374 static const Corner CORNERS[] = { | |
375 Corner_TopLeft, | |
376 Corner_TopRight, | |
377 Corner_BottomLeft, | |
378 Corner_BottomRight | |
379 }; | |
380 | |
381 unsigned int cropX, cropY, cropWidth, cropHeight; | |
382 GetCrop(cropX, cropY, cropWidth, cropHeight); | |
383 | |
384 double threshold = Square(viewportDistance / zoom); | |
385 | |
386 for (size_t i = 0; i < 4; i++) | |
387 { | |
388 double cx, cy; | |
389 GetCornerInternal(cx, cy, CORNERS[i], cropX, cropY, cropWidth, cropHeight); | |
390 | |
391 double d = Square(cx - x) + Square(cy - y); | |
392 | |
393 if (d <= threshold) | |
394 { | |
395 corner = CORNERS[i]; | |
396 return true; | |
397 } | |
398 } | |
399 | |
400 return false; | |
401 } | |
402 | |
403 | |
404 | |
405 RadiographyScene::LayerAccessor::LayerAccessor(RadiographyScene& scene, | 39 RadiographyScene::LayerAccessor::LayerAccessor(RadiographyScene& scene, |
406 size_t index) : | 40 size_t index) : |
407 scene_(scene), | 41 scene_(scene), |
408 index_(index) | 42 index_(index) |
409 { | 43 { |
471 throw Orthanc::OrthancException(Orthanc::ErrorCode_BadSequenceOfCalls); | 105 throw Orthanc::OrthancException(Orthanc::ErrorCode_BadSequenceOfCalls); |
472 } | 106 } |
473 } | 107 } |
474 | 108 |
475 | 109 |
476 RadiographyScene::Layer& RadiographyScene::LayerAccessor::GetLayer() const | 110 RadiographyLayer& RadiographyScene::LayerAccessor::GetLayer() const |
477 { | 111 { |
478 if (IsValid()) | 112 if (IsValid()) |
479 { | 113 { |
480 return *layer_; | 114 return *layer_; |
481 } | 115 } |
485 } | 119 } |
486 } | 120 } |
487 | 121 |
488 | 122 |
489 | 123 |
490 class RadiographyScene::AlphaLayer : public Layer | 124 class RadiographyScene::AlphaLayer : public RadiographyLayer |
491 { | 125 { |
492 private: | 126 private: |
493 const RadiographyScene& scene_; | 127 const RadiographyScene& scene_; |
494 std::auto_ptr<Orthanc::ImageAccessor> alpha_; // Grayscale8 | 128 std::auto_ptr<Orthanc::ImageAccessor> alpha_; // Grayscale8 |
495 bool useWindowing_; | 129 bool useWindowing_; |
629 } | 263 } |
630 }; | 264 }; |
631 | 265 |
632 | 266 |
633 | 267 |
634 class RadiographyScene::DicomLayer : public Layer | 268 class RadiographyScene::DicomLayer : public RadiographyLayer |
635 { | 269 { |
636 private: | 270 private: |
637 std::auto_ptr<Orthanc::ImageAccessor> source_; // Content of PixelData | 271 std::auto_ptr<Orthanc::ImageAccessor> source_; // Content of PixelData |
638 std::auto_ptr<DicomFrameConverter> converter_; | 272 std::auto_ptr<DicomFrameConverter> converter_; |
639 std::auto_ptr<Orthanc::ImageAccessor> converted_; // Float32 | 273 std::auto_ptr<Orthanc::ImageAccessor> converted_; // Float32 |
765 } | 399 } |
766 } | 400 } |
767 }; | 401 }; |
768 | 402 |
769 | 403 |
770 RadiographyScene::Layer& RadiographyScene::RegisterLayer(RadiographyScene::Layer* layer) | 404 RadiographyLayer& RadiographyScene::RegisterLayer(RadiographyLayer* layer) |
771 { | 405 { |
772 if (layer == NULL) | 406 if (layer == NULL) |
773 { | 407 { |
774 throw Orthanc::OrthancException(Orthanc::ErrorCode_NullPointer); | 408 throw Orthanc::OrthancException(Orthanc::ErrorCode_NullPointer); |
775 } | 409 } |
776 | 410 |
777 std::auto_ptr<Layer> raii(layer); | 411 std::auto_ptr<RadiographyLayer> raii(layer); |
778 | 412 |
779 size_t index = countLayers_++; | 413 size_t index = countLayers_++; |
780 raii->SetIndex(index); | 414 raii->SetIndex(index); |
781 layers_[index] = raii.release(); | 415 layers_[index] = raii.release(); |
782 | 416 |
844 windowingCenter_ = center; | 478 windowingCenter_ = center; |
845 windowingWidth_ = width; | 479 windowingWidth_ = width; |
846 } | 480 } |
847 | 481 |
848 | 482 |
849 RadiographyScene::Layer& RadiographyScene::LoadText(const Orthanc::Font& font, | 483 RadiographyLayer& RadiographyScene::LoadText(const Orthanc::Font& font, |
850 const std::string& utf8) | 484 const std::string& utf8) |
851 { | 485 { |
852 std::auto_ptr<AlphaLayer> alpha(new AlphaLayer(*this)); | 486 std::auto_ptr<AlphaLayer> alpha(new AlphaLayer(*this)); |
853 alpha->LoadText(font, utf8); | 487 alpha->LoadText(font, utf8); |
854 | 488 |
855 return RegisterLayer(alpha.release()); | 489 return RegisterLayer(alpha.release()); |
856 } | 490 } |
857 | 491 |
858 | 492 |
859 RadiographyScene::Layer& RadiographyScene::LoadTestBlock(unsigned int width, | 493 RadiographyLayer& RadiographyScene::LoadTestBlock(unsigned int width, |
860 unsigned int height) | 494 unsigned int height) |
861 { | 495 { |
862 std::auto_ptr<Orthanc::Image> block(new Orthanc::Image(Orthanc::PixelFormat_Grayscale8, width, height, false)); | 496 std::auto_ptr<Orthanc::Image> block(new Orthanc::Image(Orthanc::PixelFormat_Grayscale8, width, height, false)); |
863 | 497 |
864 for (unsigned int padding = 0; | 498 for (unsigned int padding = 0; |
865 (width > 2 * padding) && (height > 2 * padding); | 499 (width > 2 * padding) && (height > 2 * padding); |
885 | 519 |
886 return RegisterLayer(alpha.release()); | 520 return RegisterLayer(alpha.release()); |
887 } | 521 } |
888 | 522 |
889 | 523 |
890 RadiographyScene::Layer& RadiographyScene::LoadDicomFrame(const std::string& instance, | 524 RadiographyLayer& RadiographyScene::LoadDicomFrame(const std::string& instance, |
891 unsigned int frame, | 525 unsigned int frame, |
892 bool httpCompression) | 526 bool httpCompression) |
893 { | 527 { |
894 Layer& layer = RegisterLayer(new DicomLayer); | 528 RadiographyLayer& layer = RegisterLayer(new DicomLayer); |
895 | 529 |
896 { | 530 { |
897 IWebService::Headers headers; | 531 IWebService::Headers headers; |
898 std::string uri = "/instances/" + instance + "/tags"; | 532 std::string uri = "/instances/" + instance + "/tags"; |
899 | 533 |