comparison Samples/LayoutPetCtFusionApplication.h @ 0:351ab0da0150

initial commit
author Sebastien Jodogne <s.jodogne@gmail.com>
date Fri, 14 Oct 2016 15:34:11 +0200
parents
children ff1e935768e7
comparison
equal deleted inserted replaced
-1:000000000000 0:351ab0da0150
1 /**
2 * Stone of Orthanc
3 * Copyright (C) 2012-2016 Sebastien Jodogne, Medical Physics
4 * Department, University Hospital of Liege, Belgium
5 *
6 * This program is free software: you can redistribute it and/or
7 * modify it under the terms of the GNU General Public License as
8 * published by the Free Software Foundation, either version 3 of the
9 * License, or (at your option) any later version.
10 *
11 * In addition, as a special exception, the copyright holders of this
12 * program give permission to link the code of its release with the
13 * OpenSSL project's "OpenSSL" library (or with modified versions of it
14 * that use the same license as the "OpenSSL" library), and distribute
15 * the linked executables. You must obey the GNU General Public License
16 * in all respects for all of the code used other than "OpenSSL". If you
17 * modify file(s) with this exception, you may extend this exception to
18 * your version of the file(s), but you are not obligated to do so. If
19 * you do not wish to do so, delete this exception statement from your
20 * version. If you delete this exception statement from all source files
21 * in the program, then also delete it here.
22 *
23 * This program is distributed in the hope that it will be useful, but
24 * WITHOUT ANY WARRANTY; without even the implied warranty of
25 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
26 * General Public License for more details.
27 *
28 * You should have received a copy of the GNU General Public License
29 * along with this program. If not, see <http://www.gnu.org/licenses/>.
30 **/
31
32
33 #pragma once
34
35 #include "SampleInteractor.h"
36
37 #include "../Framework/Layers/SiblingSliceLocationFactory.h"
38 #include "../Framework/Layers/DicomStructureSetRendererFactory.h"
39 #include "../Framework/Widgets/LayoutWidget.h"
40
41 #include "../Framework/Orthanc/Core/Logging.h"
42
43 namespace OrthancStone
44 {
45 namespace Samples
46 {
47 class LayoutPetCtFusionApplication :
48 public SampleApplicationBase,
49 public LayeredSceneWidget::ISliceObserver,
50 public WorldSceneWidget::IWorldObserver
51 {
52 private:
53 class Interactor : public SampleInteractor
54 {
55 private:
56 LayoutPetCtFusionApplication& that_;
57
58 public:
59 Interactor(LayoutPetCtFusionApplication& that,
60 VolumeImage& volume,
61 VolumeProjection projection,
62 bool reverse) :
63 SampleInteractor(volume, projection, reverse),
64 that_(that)
65 {
66 }
67
68 virtual IWorldSceneMouseTracker* CreateMouseTracker(WorldSceneWidget& widget,
69 const SliceGeometry& slice,
70 const ViewportGeometry& view,
71 MouseButton button,
72 double x,
73 double y,
74 IStatusBar* statusBar)
75 {
76 if (button == MouseButton_Left)
77 {
78 // Center the sibling views over the clicked point
79 Vector p = slice.MapSliceToWorldCoordinates(x, y);
80
81 if (statusBar != NULL)
82 {
83 char buf[64];
84 sprintf(buf, "Click on coordinates (%.02f,%.02f,%.02f) in cm", p[0] / 10.0, p[1] / 10.0, p[2] / 10.0);
85 statusBar->SetMessage(buf);
86 }
87
88 that_.interactorAxial_->LookupSliceContainingPoint(*that_.ctAxial_, p);
89 that_.interactorCoronal_->LookupSliceContainingPoint(*that_.ctCoronal_, p);
90 that_.interactorSagittal_->LookupSliceContainingPoint(*that_.ctSagittal_, p);
91 }
92
93 return NULL;
94 }
95
96 virtual void KeyPressed(WorldSceneWidget& widget,
97 char key,
98 KeyboardModifiers modifiers,
99 IStatusBar* statusBar)
100 {
101 if (key == 's')
102 {
103 that_.SetDefaultView();
104 }
105 }
106 };
107
108 bool processingEvent_;
109 Interactor* interactorAxial_;
110 Interactor* interactorCoronal_;
111 Interactor* interactorSagittal_;
112 LayeredSceneWidget* ctAxial_;
113 LayeredSceneWidget* ctCoronal_;
114 LayeredSceneWidget* ctSagittal_;
115 LayeredSceneWidget* petAxial_;
116 LayeredSceneWidget* petCoronal_;
117 LayeredSceneWidget* petSagittal_;
118 LayeredSceneWidget* fusionAxial_;
119 LayeredSceneWidget* fusionCoronal_;
120 LayeredSceneWidget* fusionSagittal_;
121
122
123 void SetDefaultView()
124 {
125 petAxial_->SetDefaultView();
126 petCoronal_->SetDefaultView();
127 petSagittal_->SetDefaultView();
128 }
129
130
131 void AddLayer(LayeredSceneWidget& widget,
132 VolumeImage& volume,
133 bool isCt)
134 {
135 size_t layer;
136 widget.AddLayer(layer, new VolumeImage::LayerFactory(volume));
137
138 if (isCt)
139 {
140 RenderStyle style;
141 style.windowing_ = ImageWindowing_Bone;
142 widget.SetLayerStyle(layer, style);
143 }
144 else
145 {
146 RenderStyle style;
147 style.applyLut_ = true;
148 style.alpha_ = (layer == 0 ? 1.0 : 0.5);
149 widget.SetLayerStyle(layer, style);
150 }
151 }
152
153
154 void ConnectSiblingLocations(LayeredSceneWidget& axial,
155 LayeredSceneWidget& coronal,
156 LayeredSceneWidget& sagittal)
157 {
158 SiblingSliceLocationFactory::Configure(axial, coronal);
159 SiblingSliceLocationFactory::Configure(axial, sagittal);
160 SiblingSliceLocationFactory::Configure(coronal, sagittal);
161 }
162
163
164 void SynchronizeView(const WorldSceneWidget& source,
165 const ViewportGeometry& view,
166 LayeredSceneWidget& widget1,
167 LayeredSceneWidget& widget2,
168 LayeredSceneWidget& widget3)
169 {
170 if (&source == &widget1 ||
171 &source == &widget2 ||
172 &source == &widget3)
173 {
174 if (&source != &widget1)
175 {
176 widget1.SetView(view);
177 }
178
179 if (&source != &widget2)
180 {
181 widget2.SetView(view);
182 }
183
184 if (&source != &widget3)
185 {
186 widget3.SetView(view);
187 }
188 }
189 }
190
191
192 void SynchronizeSlice(const LayeredSceneWidget& source,
193 const SliceGeometry& slice,
194 LayeredSceneWidget& widget1,
195 LayeredSceneWidget& widget2,
196 LayeredSceneWidget& widget3)
197 {
198 if (&source == &widget1 ||
199 &source == &widget2 ||
200 &source == &widget3)
201 {
202 if (&source != &widget1)
203 {
204 widget1.SetSlice(slice);
205 }
206
207 if (&source != &widget2)
208 {
209 widget2.SetSlice(slice);
210 }
211
212 if (&source != &widget3)
213 {
214 widget3.SetSlice(slice);
215 }
216 }
217 }
218
219
220 LayeredSceneWidget* CreateWidget()
221 {
222 std::auto_ptr<LayeredSceneWidget> widget(new LayeredSceneWidget);
223 widget->Register(dynamic_cast<WorldSceneWidget::IWorldObserver&>(*this));
224 widget->Register(dynamic_cast<LayeredSceneWidget::ISliceObserver&>(*this));
225 return widget.release();
226 }
227
228
229 void CreateLayout(BasicApplicationContext& context)
230 {
231 std::auto_ptr<OrthancStone::LayoutWidget> layout(new OrthancStone::LayoutWidget);
232 layout->SetBackgroundCleared(true);
233 //layout->SetBackgroundColor(255,0,0);
234 layout->SetPadding(5);
235
236 OrthancStone::LayoutWidget& layoutA = dynamic_cast<OrthancStone::LayoutWidget&>
237 (layout->AddWidget(new OrthancStone::LayoutWidget));
238 layoutA.SetPadding(0, 0, 0, 0, 5);
239 layoutA.SetVertical();
240 petAxial_ = &dynamic_cast<LayeredSceneWidget&>(layoutA.AddWidget(CreateWidget()));
241 OrthancStone::LayoutWidget& layoutA2 = dynamic_cast<OrthancStone::LayoutWidget&>
242 (layoutA.AddWidget(new OrthancStone::LayoutWidget));
243 layoutA2.SetPadding(0, 0, 0, 0, 5);
244 petSagittal_ = &dynamic_cast<LayeredSceneWidget&>(layoutA2.AddWidget(CreateWidget()));
245 petCoronal_ = &dynamic_cast<LayeredSceneWidget&>(layoutA2.AddWidget(CreateWidget()));
246
247 OrthancStone::LayoutWidget& layoutB = dynamic_cast<OrthancStone::LayoutWidget&>
248 (layout->AddWidget(new OrthancStone::LayoutWidget));
249 layoutB.SetPadding(0, 0, 0, 0, 5);
250 layoutB.SetVertical();
251 ctAxial_ = &dynamic_cast<LayeredSceneWidget&>(layoutB.AddWidget(CreateWidget()));
252 OrthancStone::LayoutWidget& layoutB2 = dynamic_cast<OrthancStone::LayoutWidget&>
253 (layoutB.AddWidget(new OrthancStone::LayoutWidget));
254 layoutB2.SetPadding(0, 0, 0, 0, 5);
255 ctSagittal_ = &dynamic_cast<LayeredSceneWidget&>(layoutB2.AddWidget(CreateWidget()));
256 ctCoronal_ = &dynamic_cast<LayeredSceneWidget&>(layoutB2.AddWidget(CreateWidget()));
257
258 OrthancStone::LayoutWidget& layoutC = dynamic_cast<OrthancStone::LayoutWidget&>
259 (layout->AddWidget(new OrthancStone::LayoutWidget));
260 layoutC.SetPadding(0, 0, 0, 0, 5);
261 layoutC.SetVertical();
262 fusionAxial_ = &dynamic_cast<LayeredSceneWidget&>(layoutC.AddWidget(CreateWidget()));
263 OrthancStone::LayoutWidget& layoutC2 = dynamic_cast<OrthancStone::LayoutWidget&>
264 (layoutC.AddWidget(new OrthancStone::LayoutWidget));
265 layoutC2.SetPadding(0, 0, 0, 0, 5);
266 fusionSagittal_ = &dynamic_cast<LayeredSceneWidget&>(layoutC2.AddWidget(CreateWidget()));
267 fusionCoronal_ = &dynamic_cast<LayeredSceneWidget&>(layoutC2.AddWidget(CreateWidget()));
268
269 context.SetCentralWidget(layout.release());
270 }
271
272
273 public:
274 virtual void DeclareCommandLineOptions(boost::program_options::options_description& options)
275 {
276 boost::program_options::options_description generic("Sample options");
277 generic.add_options()
278 ("ct", boost::program_options::value<std::string>(),
279 "Orthanc ID of the CT series")
280 ("pet", boost::program_options::value<std::string>(),
281 "Orthanc ID of the PET series")
282 ("rt", boost::program_options::value<std::string>(),
283 "Orthanc ID of the DICOM RT-STRUCT series (optional)")
284 ("threads", boost::program_options::value<unsigned int>()->default_value(3),
285 "Number of download threads for the CT series")
286 ;
287
288 options.add(generic);
289 }
290
291 virtual void Initialize(BasicApplicationContext& context,
292 IStatusBar& statusBar,
293 const boost::program_options::variables_map& parameters)
294 {
295 using namespace OrthancStone;
296
297 processingEvent_ = true;
298
299 if (parameters.count("ct") != 1 ||
300 parameters.count("pet") != 1)
301 {
302 LOG(ERROR) << "The series ID is missing";
303 throw Orthanc::OrthancException(Orthanc::ErrorCode_ParameterOutOfRange);
304 }
305
306 std::string ct = parameters["ct"].as<std::string>();
307 std::string pet = parameters["pet"].as<std::string>();
308 unsigned int threads = parameters["threads"].as<unsigned int>();
309
310 VolumeImage& ctVolume = context.AddSeriesVolume(ct, true /* progressive download */, threads);
311 VolumeImage& petVolume = context.AddSeriesVolume(pet, true /* progressive download */, 1);
312
313 // Take the PET volume as the reference for the slices
314 interactorAxial_ = &dynamic_cast<Interactor&>
315 (context.AddInteractor(new Interactor(*this, petVolume, VolumeProjection_Axial, false)));
316 interactorCoronal_ = &dynamic_cast<Interactor&>
317 (context.AddInteractor(new Interactor(*this, petVolume, VolumeProjection_Coronal, false)));
318 interactorSagittal_ = &dynamic_cast<Interactor&>
319 (context.AddInteractor(new Interactor(*this, petVolume, VolumeProjection_Sagittal, true)));
320
321 CreateLayout(context);
322
323 AddLayer(*ctAxial_, ctVolume, true);
324 AddLayer(*ctCoronal_, ctVolume, true);
325 AddLayer(*ctSagittal_, ctVolume, true);
326
327 AddLayer(*petAxial_, petVolume, false);
328 AddLayer(*petCoronal_, petVolume, false);
329 AddLayer(*petSagittal_, petVolume, false);
330
331 AddLayer(*fusionAxial_, ctVolume, true);
332 AddLayer(*fusionAxial_, petVolume, false);
333 AddLayer(*fusionCoronal_, ctVolume, true);
334 AddLayer(*fusionCoronal_, petVolume, false);
335 AddLayer(*fusionSagittal_, ctVolume, true);
336 AddLayer(*fusionSagittal_, petVolume, false);
337
338 if (parameters.count("rt") == 1)
339 {
340 DicomStructureSet& rtStruct = context.AddStructureSet(parameters["rt"].as<std::string>());
341
342 Vector p = rtStruct.GetStructureCenter(0);
343 interactorAxial_->GetCursor().LookupSliceContainingPoint(p);
344
345 ctAxial_->AddLayer(new DicomStructureSetRendererFactory(rtStruct));
346 petAxial_->AddLayer(new DicomStructureSetRendererFactory(rtStruct));
347 fusionAxial_->AddLayer(new DicomStructureSetRendererFactory(rtStruct));
348 }
349
350 ConnectSiblingLocations(*ctAxial_, *ctCoronal_, *ctSagittal_);
351 ConnectSiblingLocations(*petAxial_, *petCoronal_, *petSagittal_);
352 ConnectSiblingLocations(*fusionAxial_, *fusionCoronal_, *fusionSagittal_);
353
354 interactorAxial_->AddWidget(*ctAxial_);
355 interactorAxial_->AddWidget(*petAxial_);
356 interactorAxial_->AddWidget(*fusionAxial_);
357
358 interactorCoronal_->AddWidget(*ctCoronal_);
359 interactorCoronal_->AddWidget(*petCoronal_);
360 interactorCoronal_->AddWidget(*fusionCoronal_);
361
362 interactorSagittal_->AddWidget(*ctSagittal_);
363 interactorSagittal_->AddWidget(*petSagittal_);
364 interactorSagittal_->AddWidget(*fusionSagittal_);
365
366 processingEvent_ = false;
367
368 statusBar.SetMessage("Use the key \"t\" to toggle the fullscreen mode");
369 statusBar.SetMessage("Use the key \"s\" to reinitialize the layout");
370 }
371
372 virtual void NotifySizeChange(const WorldSceneWidget& source,
373 ViewportGeometry& view)
374 {
375 view.SetDefaultView();
376 }
377
378 virtual void NotifyViewChange(const WorldSceneWidget& source,
379 const ViewportGeometry& view)
380 {
381 if (!processingEvent_) // Avoid reentrant calls
382 {
383 processingEvent_ = true;
384
385 SynchronizeView(source, view, *ctAxial_, *petAxial_, *fusionAxial_);
386 SynchronizeView(source, view, *ctCoronal_, *petCoronal_, *fusionCoronal_);
387 SynchronizeView(source, view, *ctSagittal_, *petSagittal_, *fusionSagittal_);
388
389 processingEvent_ = false;
390 }
391 }
392
393 virtual void NotifySliceChange(const LayeredSceneWidget& source,
394 const SliceGeometry& slice)
395 {
396 if (!processingEvent_) // Avoid reentrant calls
397 {
398 processingEvent_ = true;
399
400 SynchronizeSlice(source, slice, *ctAxial_, *petAxial_, *fusionAxial_);
401 SynchronizeSlice(source, slice, *ctCoronal_, *petCoronal_, *fusionCoronal_);
402 SynchronizeSlice(source, slice, *ctSagittal_, *petSagittal_, *fusionSagittal_);
403
404 processingEvent_ = false;
405 }
406 }
407 };
408 }
409 }