Mercurial > hg > orthanc-stone
comparison Applications/Samples/Deprecated/SingleFrameEditorApplication.h @ 1381:f4a06ad1580b
Branch broker is now the new default
author | Benjamin Golinvaux <bgo@osimis.io> |
---|---|
date | Wed, 22 Apr 2020 14:05:47 +0200 |
parents | c53a4667f895 |
children |
comparison
equal
deleted
inserted
replaced
1375:4431ffdcc2a4 | 1381:f4a06ad1580b |
---|---|
1 /** | |
2 * Stone of Orthanc | |
3 * Copyright (C) 2012-2016 Sebastien Jodogne, Medical Physics | |
4 * Department, University Hospital of Liege, Belgium | |
5 * Copyright (C) 2017-2020 Osimis S.A., Belgium | |
6 * | |
7 * This program is free software: you can redistribute it and/or | |
8 * modify it under the terms of the GNU Affero General Public License | |
9 * as published by the Free Software Foundation, either version 3 of | |
10 * the License, or (at your option) any later version. | |
11 * | |
12 * This program is distributed in the hope that it will be useful, but | |
13 * WITHOUT ANY WARRANTY; without even the implied warranty of | |
14 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU | |
15 * Affero General Public License for more details. | |
16 * | |
17 * You should have received a copy of the GNU Affero General Public License | |
18 * along with this program. If not, see <http://www.gnu.org/licenses/>. | |
19 **/ | |
20 | |
21 | |
22 #pragma once | |
23 | |
24 #include "SampleApplicationBase.h" | |
25 | |
26 #include "../../../Framework/Radiography/RadiographyLayerCropTracker.h" | |
27 #include "../../../Framework/Radiography/RadiographyLayerMaskTracker.h" | |
28 #include "../../../Framework/Radiography/RadiographyLayerMoveTracker.h" | |
29 #include "../../../Framework/Radiography/RadiographyLayerResizeTracker.h" | |
30 #include "../../../Framework/Radiography/RadiographyLayerRotateTracker.h" | |
31 #include "../../../Framework/Radiography/RadiographyMaskLayer.h" | |
32 #include "../../../Framework/Radiography/RadiographyScene.h" | |
33 #include "../../../Framework/Radiography/RadiographySceneCommand.h" | |
34 #include "../../../Framework/Radiography/RadiographySceneReader.h" | |
35 #include "../../../Framework/Radiography/RadiographySceneWriter.h" | |
36 #include "../../../Framework/Radiography/RadiographyWidget.h" | |
37 #include "../../../Framework/Radiography/RadiographyWindowingTracker.h" | |
38 #include "../../../Framework/Toolbox/TextRenderer.h" | |
39 | |
40 #include <Core/HttpClient.h> | |
41 #include <Core/Logging.h> | |
42 #include <Core/OrthancException.h> | |
43 #include <Core/Images/PngWriter.h> | |
44 #include <Core/Images/PngReader.h> | |
45 | |
46 | |
47 // Export using PAM is faster than using PNG, but requires Orthanc | |
48 // core >= 1.4.3 | |
49 #define EXPORT_USING_PAM 1 | |
50 | |
51 | |
52 namespace OrthancStone | |
53 { | |
54 namespace Samples | |
55 { | |
56 class RadiographyEditorInteractor : | |
57 public Deprecated::IWorldSceneInteractor, | |
58 public ObserverBase<RadiographyEditorInteractor> | |
59 { | |
60 private: | |
61 enum Tool | |
62 { | |
63 Tool_Move, | |
64 Tool_Rotate, | |
65 Tool_Crop, | |
66 Tool_Resize, | |
67 Tool_Mask, | |
68 Tool_Windowing | |
69 }; | |
70 | |
71 | |
72 StoneApplicationContext* context_; | |
73 UndoRedoStack undoRedoStack_; | |
74 Tool tool_; | |
75 RadiographyMaskLayer* maskLayer_; | |
76 | |
77 | |
78 static double GetHandleSize() | |
79 { | |
80 return 10.0; | |
81 } | |
82 | |
83 | |
84 public: | |
85 RadiographyEditorInteractor() : | |
86 context_(NULL), | |
87 tool_(Tool_Move), | |
88 maskLayer_(NULL) | |
89 { | |
90 } | |
91 | |
92 void SetContext(StoneApplicationContext& context) | |
93 { | |
94 context_ = &context; | |
95 } | |
96 | |
97 void SetMaskLayer(RadiographyMaskLayer* maskLayer) | |
98 { | |
99 maskLayer_ = maskLayer; | |
100 } | |
101 virtual Deprecated::IWorldSceneMouseTracker* CreateMouseTracker(Deprecated::WorldSceneWidget& worldWidget, | |
102 const Deprecated::ViewportGeometry& view, | |
103 MouseButton button, | |
104 KeyboardModifiers modifiers, | |
105 int viewportX, | |
106 int viewportY, | |
107 double x, | |
108 double y, | |
109 Deprecated::IStatusBar* statusBar, | |
110 const std::vector<Deprecated::Touch>& displayTouches) | |
111 { | |
112 RadiographyWidget& widget = dynamic_cast<RadiographyWidget&>(worldWidget); | |
113 | |
114 if (button == MouseButton_Left) | |
115 { | |
116 size_t selected; | |
117 | |
118 if (tool_ == Tool_Windowing) | |
119 { | |
120 return new RadiographyWindowingTracker( | |
121 undoRedoStack_, | |
122 widget.GetScene(), | |
123 widget, | |
124 OrthancStone::ImageInterpolation_Nearest, | |
125 viewportX, viewportY, | |
126 RadiographyWindowingTracker::Action_DecreaseWidth, | |
127 RadiographyWindowingTracker::Action_IncreaseWidth, | |
128 RadiographyWindowingTracker::Action_DecreaseCenter, | |
129 RadiographyWindowingTracker::Action_IncreaseCenter); | |
130 } | |
131 else if (!widget.LookupSelectedLayer(selected)) | |
132 { | |
133 // No layer is currently selected | |
134 size_t layer; | |
135 if (widget.GetScene().LookupLayer(layer, x, y)) | |
136 { | |
137 widget.Select(layer); | |
138 } | |
139 | |
140 return NULL; | |
141 } | |
142 else if (tool_ == Tool_Crop || | |
143 tool_ == Tool_Resize || | |
144 tool_ == Tool_Mask) | |
145 { | |
146 RadiographyScene::LayerAccessor accessor(widget.GetScene(), selected); | |
147 | |
148 ControlPoint controlPoint; | |
149 if (accessor.GetLayer().LookupControlPoint(controlPoint, x, y, view.GetZoom(), GetHandleSize())) | |
150 { | |
151 switch (tool_) | |
152 { | |
153 case Tool_Crop: | |
154 return new RadiographyLayerCropTracker | |
155 (undoRedoStack_, widget.GetScene(), view, selected, controlPoint); | |
156 | |
157 case Tool_Mask: | |
158 return new RadiographyLayerMaskTracker | |
159 (undoRedoStack_, widget.GetScene(), view, selected, controlPoint); | |
160 | |
161 case Tool_Resize: | |
162 return new RadiographyLayerResizeTracker | |
163 (undoRedoStack_, widget.GetScene(), selected, controlPoint, | |
164 (modifiers & KeyboardModifiers_Shift)); | |
165 | |
166 default: | |
167 throw Orthanc::OrthancException(Orthanc::ErrorCode_ParameterOutOfRange); | |
168 } | |
169 } | |
170 else | |
171 { | |
172 size_t layer; | |
173 | |
174 if (widget.GetScene().LookupLayer(layer, x, y)) | |
175 { | |
176 widget.Select(layer); | |
177 } | |
178 else | |
179 { | |
180 widget.Unselect(); | |
181 } | |
182 | |
183 return NULL; | |
184 } | |
185 } | |
186 else | |
187 { | |
188 size_t layer; | |
189 | |
190 if (widget.GetScene().LookupLayer(layer, x, y)) | |
191 { | |
192 if (layer == selected) | |
193 { | |
194 switch (tool_) | |
195 { | |
196 case Tool_Move: | |
197 return new RadiographyLayerMoveTracker | |
198 (undoRedoStack_, widget.GetScene(), layer, x, y, | |
199 (modifiers & KeyboardModifiers_Shift)); | |
200 | |
201 case Tool_Rotate: | |
202 return new RadiographyLayerRotateTracker | |
203 (undoRedoStack_, widget.GetScene(), view, layer, x, y, | |
204 (modifiers & KeyboardModifiers_Shift)); | |
205 | |
206 default: | |
207 break; | |
208 } | |
209 | |
210 return NULL; | |
211 } | |
212 else | |
213 { | |
214 widget.Select(layer); | |
215 return NULL; | |
216 } | |
217 } | |
218 else | |
219 { | |
220 widget.Unselect(); | |
221 return NULL; | |
222 } | |
223 } | |
224 } | |
225 else | |
226 { | |
227 return NULL; | |
228 } | |
229 return NULL; | |
230 } | |
231 | |
232 virtual void MouseOver(CairoContext& context, | |
233 Deprecated::WorldSceneWidget& worldWidget, | |
234 const Deprecated::ViewportGeometry& view, | |
235 double x, | |
236 double y, | |
237 Deprecated::IStatusBar* statusBar) | |
238 { | |
239 RadiographyWidget& widget = dynamic_cast<RadiographyWidget&>(worldWidget); | |
240 | |
241 #if 0 | |
242 if (statusBar != NULL) | |
243 { | |
244 char buf[64]; | |
245 sprintf(buf, "X = %.02f Y = %.02f (in cm)", x / 10.0, y / 10.0); | |
246 statusBar->SetMessage(buf); | |
247 } | |
248 #endif | |
249 | |
250 size_t selected; | |
251 | |
252 if (widget.LookupSelectedLayer(selected) && | |
253 (tool_ == Tool_Crop || | |
254 tool_ == Tool_Resize || | |
255 tool_ == Tool_Mask)) | |
256 { | |
257 RadiographyScene::LayerAccessor accessor(widget.GetScene(), selected); | |
258 | |
259 ControlPoint controlPoint; | |
260 if (accessor.GetLayer().LookupControlPoint(controlPoint, x, y, view.GetZoom(), GetHandleSize())) | |
261 { | |
262 double z = 1.0 / view.GetZoom(); | |
263 | |
264 context.SetSourceColor(255, 0, 0); | |
265 cairo_t* cr = context.GetObject(); | |
266 cairo_set_line_width(cr, 2.0 * z); | |
267 cairo_move_to(cr, controlPoint.x - GetHandleSize() * z, controlPoint.y - GetHandleSize() * z); | |
268 cairo_line_to(cr, controlPoint.x + GetHandleSize() * z, controlPoint.y - GetHandleSize() * z); | |
269 cairo_line_to(cr, controlPoint.x + GetHandleSize() * z, controlPoint.y + GetHandleSize() * z); | |
270 cairo_line_to(cr, controlPoint.x - GetHandleSize() * z, controlPoint.y + GetHandleSize() * z); | |
271 cairo_line_to(cr, controlPoint.x - GetHandleSize() * z, controlPoint.y - GetHandleSize() * z); | |
272 cairo_stroke(cr); | |
273 } | |
274 } | |
275 } | |
276 | |
277 virtual void MouseWheel(Deprecated::WorldSceneWidget& widget, | |
278 MouseWheelDirection direction, | |
279 KeyboardModifiers modifiers, | |
280 Deprecated::IStatusBar* statusBar) | |
281 { | |
282 } | |
283 | |
284 virtual void KeyPressed(Deprecated::WorldSceneWidget& worldWidget, | |
285 KeyboardKeys key, | |
286 char keyChar, | |
287 KeyboardModifiers modifiers, | |
288 Deprecated::IStatusBar* statusBar) | |
289 { | |
290 RadiographyWidget& widget = dynamic_cast<RadiographyWidget&>(worldWidget); | |
291 | |
292 switch (keyChar) | |
293 { | |
294 case 'a': | |
295 widget.FitContent(); | |
296 break; | |
297 | |
298 case 'c': | |
299 tool_ = Tool_Crop; | |
300 break; | |
301 | |
302 case 'm': | |
303 tool_ = Tool_Mask; | |
304 widget.Select(1); | |
305 break; | |
306 | |
307 case 'd': | |
308 { | |
309 // dump to json and reload | |
310 Json::Value snapshot; | |
311 RadiographySceneWriter writer; | |
312 writer.Write(snapshot, widget.GetScene()); | |
313 | |
314 LOG(INFO) << "JSON export was successful: " | |
315 << snapshot.toStyledString(); | |
316 | |
317 boost::shared_ptr<RadiographyScene> scene(new RadiographyScene); | |
318 RadiographySceneReader reader(*scene, *context_->GetOrthancApiClient()); | |
319 reader.Read(snapshot); | |
320 | |
321 widget.SetScene(scene); | |
322 };break; | |
323 | |
324 case 'e': | |
325 { | |
326 Orthanc::DicomMap tags; | |
327 | |
328 // Minimal set of tags to generate a valid CR image | |
329 tags.SetValue(Orthanc::DICOM_TAG_ACCESSION_NUMBER, "NOPE", false); | |
330 tags.SetValue(Orthanc::DICOM_TAG_BODY_PART_EXAMINED, "PELVIS", false); | |
331 tags.SetValue(Orthanc::DICOM_TAG_INSTANCE_NUMBER, "1", false); | |
332 //tags.SetValue(Orthanc::DICOM_TAG_LATERALITY, "", false); | |
333 tags.SetValue(Orthanc::DICOM_TAG_MANUFACTURER, "OSIMIS", false); | |
334 tags.SetValue(Orthanc::DICOM_TAG_MODALITY, "CR", false); | |
335 tags.SetValue(Orthanc::DICOM_TAG_PATIENT_BIRTH_DATE, "20000101", false); | |
336 tags.SetValue(Orthanc::DICOM_TAG_PATIENT_ID, "hello", false); | |
337 tags.SetValue(Orthanc::DICOM_TAG_PATIENT_NAME, "HELLO^WORLD", false); | |
338 tags.SetValue(Orthanc::DICOM_TAG_PATIENT_ORIENTATION, "", false); | |
339 tags.SetValue(Orthanc::DICOM_TAG_PATIENT_SEX, "M", false); | |
340 tags.SetValue(Orthanc::DICOM_TAG_REFERRING_PHYSICIAN_NAME, "HOUSE^MD", false); | |
341 tags.SetValue(Orthanc::DICOM_TAG_SERIES_NUMBER, "1", false); | |
342 tags.SetValue(Orthanc::DICOM_TAG_SOP_CLASS_UID, "1.2.840.10008.5.1.4.1.1.1", false); | |
343 tags.SetValue(Orthanc::DICOM_TAG_STUDY_ID, "STUDY", false); | |
344 tags.SetValue(Orthanc::DICOM_TAG_VIEW_POSITION, "", false); | |
345 | |
346 if (context_ != NULL) | |
347 { | |
348 widget.GetScene().ExportDicom(*context_->GetOrthancApiClient(), | |
349 tags, std::string(), 0.1, 0.1, widget.IsInverted(), | |
350 false /* autoCrop */, widget.GetInterpolation(), EXPORT_USING_PAM); | |
351 } | |
352 | |
353 break; | |
354 } | |
355 | |
356 case 'i': | |
357 widget.SwitchInvert(); | |
358 break; | |
359 | |
360 case 't': | |
361 tool_ = Tool_Move; | |
362 break; | |
363 | |
364 case 'n': | |
365 { | |
366 switch (widget.GetInterpolation()) | |
367 { | |
368 case ImageInterpolation_Nearest: | |
369 LOG(INFO) << "Switching to bilinear interpolation"; | |
370 widget.SetInterpolation(ImageInterpolation_Bilinear); | |
371 break; | |
372 | |
373 case ImageInterpolation_Bilinear: | |
374 LOG(INFO) << "Switching to nearest neighbor interpolation"; | |
375 widget.SetInterpolation(ImageInterpolation_Nearest); | |
376 break; | |
377 | |
378 default: | |
379 throw Orthanc::OrthancException(Orthanc::ErrorCode_InternalError); | |
380 } | |
381 | |
382 break; | |
383 } | |
384 | |
385 case 'r': | |
386 tool_ = Tool_Rotate; | |
387 break; | |
388 | |
389 case 's': | |
390 tool_ = Tool_Resize; | |
391 break; | |
392 | |
393 case 'w': | |
394 tool_ = Tool_Windowing; | |
395 break; | |
396 | |
397 case 'y': | |
398 if (modifiers & KeyboardModifiers_Control) | |
399 { | |
400 undoRedoStack_.Redo(); | |
401 widget.NotifyContentChanged(); | |
402 } | |
403 break; | |
404 | |
405 case 'z': | |
406 if (modifiers & KeyboardModifiers_Control) | |
407 { | |
408 undoRedoStack_.Undo(); | |
409 widget.NotifyContentChanged(); | |
410 } | |
411 break; | |
412 | |
413 default: | |
414 break; | |
415 } | |
416 } | |
417 }; | |
418 | |
419 | |
420 | |
421 class SingleFrameEditorApplication : | |
422 public SampleSingleCanvasApplicationBase, | |
423 public IObserver | |
424 { | |
425 private: | |
426 boost::shared_ptr<RadiographyScene> scene_; | |
427 RadiographyEditorInteractor interactor_; | |
428 RadiographyMaskLayer* maskLayer_; | |
429 | |
430 public: | |
431 virtual ~SingleFrameEditorApplication() | |
432 { | |
433 LOG(WARNING) << "Destroying the application"; | |
434 } | |
435 | |
436 virtual void DeclareStartupOptions(boost::program_options::options_description& options) | |
437 { | |
438 boost::program_options::options_description generic("Sample options"); | |
439 generic.add_options() | |
440 ("instance", boost::program_options::value<std::string>(), | |
441 "Orthanc ID of the instance") | |
442 ("frame", boost::program_options::value<unsigned int>()->default_value(0), | |
443 "Number of the frame, for multi-frame DICOM instances") | |
444 ; | |
445 | |
446 options.add(generic); | |
447 } | |
448 | |
449 virtual void Initialize(StoneApplicationContext* context, | |
450 Deprecated::IStatusBar& statusBar, | |
451 const boost::program_options::variables_map& parameters) | |
452 { | |
453 using namespace OrthancStone; | |
454 | |
455 context_ = context; | |
456 interactor_.SetContext(*context); | |
457 | |
458 statusBar.SetMessage("Use the key \"a\" to reinitialize the layout"); | |
459 statusBar.SetMessage("Use the key \"c\" to crop"); | |
460 statusBar.SetMessage("Use the key \"e\" to export DICOM to the Orthanc server"); | |
461 statusBar.SetMessage("Use the key \"f\" to switch full screen"); | |
462 statusBar.SetMessage("Use the key \"i\" to invert contrast"); | |
463 statusBar.SetMessage("Use the key \"m\" to modify the mask"); | |
464 statusBar.SetMessage("Use the key \"n\" to switch between nearest neighbor and bilinear interpolation"); | |
465 statusBar.SetMessage("Use the key \"r\" to rotate objects"); | |
466 statusBar.SetMessage("Use the key \"s\" to resize objects (not applicable to DICOM layers)"); | |
467 statusBar.SetMessage("Use the key \"t\" to move (translate) objects"); | |
468 statusBar.SetMessage("Use the key \"w\" to change windowing"); | |
469 | |
470 statusBar.SetMessage("Use the key \"ctrl-z\" to undo action"); | |
471 statusBar.SetMessage("Use the key \"ctrl-y\" to redo action"); | |
472 | |
473 if (parameters.count("instance") != 1) | |
474 { | |
475 LOG(ERROR) << "The instance ID is missing"; | |
476 throw Orthanc::OrthancException(Orthanc::ErrorCode_ParameterOutOfRange); | |
477 } | |
478 | |
479 std::string instance = parameters["instance"].as<std::string>(); | |
480 //int frame = parameters["frame"].as<unsigned int>(); | |
481 | |
482 scene_.reset(new RadiographyScene); | |
483 | |
484 RadiographyLayer& dicomLayer = scene_->LoadDicomFrame(*context->GetOrthancApiClient(), instance, 0, false, NULL); | |
485 //scene_->LoadDicomFrame(instance, frame, false); //.SetPan(200, 0); | |
486 // = scene_->LoadDicomFrame(context->GetOrthancApiClient(), "61f3143e-96f34791-ad6bbb8d-62559e75-45943e1b", 0, false, NULL); | |
487 | |
488 #if !defined(ORTHANC_ENABLE_WASM) || ORTHANC_ENABLE_WASM != 1 | |
489 Orthanc::HttpClient::ConfigureSsl(true, "/etc/ssl/certs/ca-certificates.crt"); | |
490 #endif | |
491 | |
492 //scene_->LoadDicomWebFrame(context->GetWebService()); | |
493 | |
494 std::vector<Orthanc::ImageProcessing::ImagePoint> mask; | |
495 mask.push_back(Orthanc::ImageProcessing::ImagePoint(1100, 100)); | |
496 mask.push_back(Orthanc::ImageProcessing::ImagePoint(1100, 1000)); | |
497 mask.push_back(Orthanc::ImageProcessing::ImagePoint(2000, 1000)); | |
498 mask.push_back(Orthanc::ImageProcessing::ImagePoint(2200, 150)); | |
499 mask.push_back(Orthanc::ImageProcessing::ImagePoint(1500, 550)); | |
500 maskLayer_ = dynamic_cast<RadiographyMaskLayer*>(&(scene_->LoadMask(mask, dynamic_cast<RadiographyDicomLayer&>(dicomLayer), 128.0f, NULL))); | |
501 interactor_.SetMaskLayer(maskLayer_); | |
502 | |
503 { | |
504 std::unique_ptr<Orthanc::ImageAccessor> renderedTextAlpha(TextRenderer::Render(Orthanc::EmbeddedResources::UBUNTU_FONT, 100, | |
505 "%öÇaA&#")); | |
506 RadiographyLayer& layer = scene_->LoadAlphaBitmap(renderedTextAlpha.release(), NULL); | |
507 dynamic_cast<RadiographyAlphaLayer&>(layer).SetForegroundValue(200.0f * 256.0f); | |
508 } | |
509 | |
510 { | |
511 RadiographyTextLayer::RegisterFont("ubuntu", Orthanc::EmbeddedResources::UBUNTU_FONT); | |
512 RadiographyLayer& layer = scene_->LoadText("Hello\nworld", "ubuntu", 20, 128, NULL, false); | |
513 layer.SetResizeable(true); | |
514 } | |
515 | |
516 { | |
517 RadiographyLayer& layer = scene_->LoadTestBlock(100, 50, NULL); | |
518 layer.SetResizeable(true); | |
519 layer.SetPan(0, 200); | |
520 } | |
521 | |
522 boost::shared_ptr<RadiographyWidget> widget(new RadiographyWidget(scene_, "main-widget")); | |
523 widget->SetTransmitMouseOver(true); | |
524 widget->SetInteractor(interactor_); | |
525 SetCentralWidget(widget); | |
526 | |
527 //scene_->SetWindowing(128, 256); | |
528 } | |
529 }; | |
530 } | |
531 } |