Fraxinus  17.12
An IGT application
cxAirwaysFilterService.cpp
Go to the documentation of this file.
1 /*=========================================================================
2 This file is part of CustusX, an Image Guided Therapy Application.
3 
4 Copyright (c) 2008-2014, SINTEF Department of Medical Technology
5 All rights reserved.
6 
7 Redistribution and use in source and binary forms, with or without
8 modification, are permitted provided that the following conditions are met:
9 
10 1. Redistributions of source code must retain the above copyright notice,
11  this list of conditions and the following disclaimer.
12 
13 2. Redistributions in binary form must reproduce the above copyright notice,
14  this list of conditions and the following disclaimer in the documentation
15  and/or other materials provided with the distribution.
16 
17 3. Neither the name of the copyright holder nor the names of its contributors
18  may be used to endorse or promote products derived from this software
19  without specific prior written permission.
20 
21 THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
22 AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
23 IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
24 DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
25 FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
26 DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
27 SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
28 CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
29 OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
30 OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
31 =========================================================================*/
32 
33 #include "cxAirwaysFilterService.h"
34 
35 #include <QTimer>
36 
37 #include <vtkImageImport.h>
38 #include <vtkImageData.h>
39 #include <vtkImageShiftScale.h>
40 #include <ctkPluginContext.h>
41 #include <vtkImplicitModeller.h>
42 #include <vtkContourFilter.h>
43 #include "cxBranchList.h"
45 
46 #include "cxTime.h"
47 #include "cxTypeConversions.h"
48 #include "cxLogger.h"
49 #include "cxDataReaderWriter.h"
51 #include "cxDoubleProperty.h"
52 #include "cxContourFilter.h"
53 #include "cxDataLocations.h"
55 #include "vtkForwardDeclarations.h"
57 #include "cxVisServices.h"
58 #include "cxUtilHelpers.h"
59 #include "FAST/Algorithms/LungSegmentation/LungSegmentation.hpp"
60 #include "FAST/Algorithms/AirwaySegmentation/AirwaySegmentation.hpp"
61 #include "FAST/Algorithms/CenterlineExtraction/CenterlineExtraction.hpp"
62 #include "FAST/Importers/ImageFileImporter.hpp"
63 #include "FAST/Exporters/VTKImageExporter.hpp"
64 #include "FAST/Exporters/VTKMeshExporter.hpp"
65 #include "FAST/Data/Segmentation.hpp"
66 #include "FAST/SceneGraph.hpp"
67 
68 namespace cx {
69 
71  FilterImpl(services),
72  mDefaultStraightCLTubesOption(false)
73 {
74  fast::Reporter::setGlobalReportMethod(fast::Reporter::COUT);
75  //Need to create OpenGL context of fast in main thread, this is done in the constructor of DeviceManger
76  fast::ImageFileImporter::pointer importer = fast::ImageFileImporter::New();
77  Q_UNUSED(importer)
78 }
79 
80 
82 }
83 
84 QString AirwaysFilter::getName() const
85 {
86  return "Airway Segmentation Filter";
87 }
88 
89 QString AirwaysFilter::getType() const
90 {
91  return "airways_filter";
92 }
93 
94 QString AirwaysFilter::getHelp() const
95 {
96  return "<html>"
97  "<h3>Airway Segmentation.</h3>"
98  "<p><i>Extracts segmentation and centerline from a CT volume. If method fails, try to crop volume. </br>Algorithm written by Erik Smistad.</i></p>"
99  "</html>";
100 }
101 
103 {
104  return "_centerline";
105 }
106 
108 {
109  return "_straight";
110 }
111 
113 {
114  return "_tubes";
115 }
116 
117 Vector3D AirwaysFilter::getSeedPointFromTool(SpaceProviderPtr spaceProvider, DataPtr data)
118 {
119  // Retrieve position of tooltip and use it as seed point
120  Vector3D point = spaceProvider->getActiveToolTipPoint(
121  spaceProvider->getD(data));
122 
123  // Have to multiply by the inverse of the spacing to get the voxel position
124  ImagePtr image = boost::dynamic_pointer_cast<Image>(data);
125  double spacingX, spacingY, spacingZ;
126  image->getBaseVtkImageData()->GetSpacing(spacingX, spacingY, spacingZ);
127  point(0) = point(0) * (1.0 / spacingX);
128  point(1) = point(1) * (1.0 / spacingY);
129  point(2) = point(2) * (1.0 / spacingZ);
130 
131  std::cout << "the selected seed point is: " << point(0) << " " << point(1)
132  << " " << point(2) << "\n";
133 
134  return point;
135 }
136 
137 int * getImageSize(DataPtr inputImage)
138 {
139  ImagePtr image = boost::dynamic_pointer_cast<Image>(inputImage);
140  return image->getBaseVtkImageData()->GetDimensions();
141 }
142 
143 bool AirwaysFilter::isSeedPointInsideImage(Vector3D seedPoint, DataPtr image)
144 {
145  int * size = getImageSize(image);
146  std::cout << "size of image is: " << size[0] << " " << size[1] << " "
147  << size[2] << "\n";
148  int x = (int) seedPoint(0);
149  int y = (int) seedPoint(1);
150  int z = (int) seedPoint(2);
151  bool result = x >= 0 && y >= 0 && z >= 0 && x < size[0] && y < size[1]
152  && z < size[2];
153  return result;
154 }
155 
157 {
158  DataPtr inputImage = mInputTypes[0].get()->getData();
159  if (!inputImage)
160  {
161  CX_LOG_ERROR() << "No input data selected";
162  return false;
163  }
164 
165  if (inputImage->getType() != "image")
166  {
167  CX_LOG_ERROR() << "Input data has to be an image";
168  return false;
169  }
170 
171  std::string filename = (patientService()->getActivePatientFolder()
172  + "/" + inputImage->getFilename()).toStdString();
173 
174  // only check seed point inside image if use seed point is checked
175  bool useManualSeedPoint = getManualSeedPointOption(mOptions)->getValue();
176  if(useManualSeedPoint)
177  {
178  seedPoint = getSeedPointFromTool(mServices->spaceProvider(), inputImage);
179  if(!isSeedPointInsideImage(seedPoint, inputImage)) {
180  CX_LOG_ERROR() << "Seed point is not inside image. Use cursor to set seed point inside trachea in the CT image.";
181  return false;
182  }
183  }
184  mInputImage = patientService()->getData<Image>(inputImage->getUid());
185 
186  return true;
187 }
188 
190 {
191  CX_LOG_INFO() << "EXECUTING AIRWAYS FILTER";
192  // Check if pre process went ok:
193  if(!mInputImage)
194  return false;
195 
196  QString q_filename = "";
197  QString activePatienFolder = patientService()->getActivePatientFolder();
198  QString inputImageFileName = mInputImage->getFilename();
199  if(!activePatienFolder.isEmpty())
200  q_filename = activePatienFolder+"/"+inputImageFileName;
201  else
202  q_filename = inputImageFileName;
203 
204  std::string filename = q_filename.toStdString();
205  try {
206  fast::Config::getTestDataPath(); // needed for initialization
207  QString cacheDir = cx::DataLocations::getCachePath();
208  fast::Config::setKernelBinaryPath(cacheDir.toStdString());
209  QString kernelDir = cx::DataLocations::findConfigFolder("/FAST", FAST_SOURCE_DIR);
210  fast::Config::setKernelSourcePath(kernelDir.toStdString());
211 
212  // Import image data from disk
213  fast::ImageFileImporter::pointer importer = fast::ImageFileImporter::New();
214  importer->setFilename(filename);
215 
216  // Need to know the data type
217  importer->update();
218  fast::Image::pointer image = importer->getOutputData<fast::Image>();
219 
220  // Do segmentation
221  fast::Segmentation::pointer segmentationData;
222  bool doLungSegmentation = getLungSegmentationOption(mOptions)->getValue();
223  bool useManualSeedPoint = getManualSeedPointOption(mOptions)->getValue();
224  try {
225  if(doLungSegmentation) {
226  fast::LungSegmentation::pointer segmentation = fast::LungSegmentation::New();
227  if(useManualSeedPoint) {
228  CX_LOG_INFO() << "Using seed point: " << seedPoint.transpose();
229  segmentation->setAirwaySeedPoint(seedPoint(0), seedPoint(1), seedPoint(2));
230  }
231  segmentation->setInputConnection(importer->getOutputPort());
232  segmentation->update();
233  segmentationData = segmentation->getOutputData<fast::Segmentation>(1);
234 
235  // Convert fast segmentation data to VTK data which CX can use (Airways)
236  vtkSmartPointer<fast::VTKImageExporter> vtkExporter = fast::VTKImageExporter::New();
237  vtkExporter->setInputConnection(segmentation->getOutputPort(1));
238  vtkExporter->Update();
239  mAirwaySegmentationOutput = vtkExporter->GetOutput();
240 
241  // Convert fast segmentation data to VTK data which CX can use (Lungs)
242  vtkSmartPointer<fast::VTKImageExporter> vtkExporter2 = fast::VTKImageExporter::New();
243  vtkExporter2->setInputConnection(segmentation->getOutputPort(0));
244  vtkExporter2->Update();
245  mLungSegmentationOutput = vtkExporter2->GetOutput();
246  } else {
247 
248  fast::AirwaySegmentation::pointer segmentation = fast::AirwaySegmentation::New();
249  if(useManualSeedPoint) {
250  CX_LOG_INFO() << "Using seed point: " << seedPoint.transpose();
251  segmentation->setSeedPoint(seedPoint(0), seedPoint(1), seedPoint(2));
252  }
253  segmentation->setInputConnection(importer->getOutputPort());
254  segmentation->update();
255  segmentationData = segmentation->getOutputData<fast::Segmentation>(0);
256 
257  // Convert fast segmentation data to VTK data which CX can use
258  vtkSmartPointer<fast::VTKImageExporter> vtkExporter = fast::VTKImageExporter::New();
259  vtkExporter->setInputConnection(segmentation->getOutputPort());
260  vtkExporter->Update();
261  mAirwaySegmentationOutput = vtkExporter->GetOutput();
262  }
263  } catch(fast::Exception & e)
264  {
265  CX_LOG_ERROR() << "The airways filter failed: \n"
266  << e.what();
267  if(!useManualSeedPoint)
268  CX_LOG_ERROR() << "Try to set the seed point manually.";
269  return false;
270  }
271 
272  CX_LOG_SUCCESS() << "FINISHED AIRWAY SEGMENTATION";
273 
274 
275  // Get the transformation of the segmentation
276  Eigen::Affine3f T = fast::SceneGraph::getEigenAffineTransformationFromData(segmentationData);
277  mTransformation.matrix() = T.matrix().cast<double>(); // cast to double
278 
279  // Extract centerline
280  fast::CenterlineExtraction::pointer centerline = fast::CenterlineExtraction::New();
281  centerline->setInputData(segmentationData);
282 
283  // Get centerline
284  vtkSmartPointer<fast::VTKMeshExporter> vtkCenterlineExporter = fast::VTKMeshExporter::New();
285  vtkCenterlineExporter->setInputConnection(centerline->getOutputPort());
286  mCenterlineOutput = vtkCenterlineExporter->GetOutput();
287  vtkCenterlineExporter->Update();
288 
289  } catch(fast::Exception& e) {
290  std::string error = e.what();
291  reportError("fast::Exception: "+qstring_cast(error));
292 
293  return false;
294  } catch(cl::Error& e) {
295  reportError("cl::Error:"+qstring_cast(e.what()));
296 
297  return false;
298  } catch (std::exception& e){
299  reportError("std::exception:"+qstring_cast(e.what()));
300 
301  return false;
302  } catch (...){
303  reportError("Airway segmentation algorithm threw a unknown exception.");
304 
305  return false;
306  }
307  return true;
308 }
309 
311 {
312  if(!mAirwaySegmentationOutput)
313  return false;
314 
315  std::cout << "POST PROCESS" << std::endl;
316 
317  // Make contour of segmented volume
318  double threshold = 1;
320  mAirwaySegmentationOutput,
321  threshold,
322  false, // reduce resolution
323  true, // smoothing
324  true, // keep topology
325  0 // target decimation
326  );
327  //outputSegmentation->get_rMd_History()->setRegistration(rMd_i);
328  //patientService()->insertData(outputSegmentation);
329 
330  // Add contour internally to cx
332  patientService(),
333  rawContour,
334  mInputImage,
335  QColor("green")
336  );
337  contour->get_rMd_History()->setRegistration(mTransformation);
338 
339  // Set output
340  mOutputTypes[1]->setValue(contour->getUid());
341 
342  if(getLungSegmentationOption(mOptions)->getValue()) {
344  mLungSegmentationOutput,
345  threshold,
346  false, // reduce resolution
347  true, // smoothing
348  true, // keep topology
349  0 // target decimation
350  );
351  //outputSegmentation->get_rMd_History()->setRegistration(rMd_i);
352  //patientService()->insertData(outputSegmentation);
353 
354  // Add contour internally to cx
355  QColor color("red");
356  color.setAlpha(100);
358  patientService(),
359  rawContour,
360  mInputImage,
361  color
362  );
363  contour->get_rMd_History()->setRegistration(mTransformation);
364 
365  // Set output
366  mOutputTypes[2]->setValue(contour->getUid());
367  }
368 
369  // Centerline
370  QString uid = mInputImage->getUid() + AirwaysFilter::getNameSuffix() + "%1";
371  QString name = mInputImage->getName() + AirwaysFilter::getNameSuffix() + "%1";
372  MeshPtr centerline = patientService()->createSpecificData<Mesh>(uid, name);
373  centerline->setVtkPolyData(mCenterlineOutput);
374  centerline->get_rMd_History()->setParentSpace(mInputImage->getUid());
375  centerline->get_rMd_History()->setRegistration(mTransformation);
376  patientService()->insertData(centerline);
377  mOutputTypes[0]->setValue(centerline->getUid());
378 
379  // Straight centerline and tubes
380  if(getStraightCLTubesOption(mOptions)->getValue())
381  {
382  this->createStraightCL();
383  this->createTubes();
384  }
385 
386  return true;
387 }
388 
405 void AirwaysFilter::createTubes()
406 {
407  // Get the straight centerline to model the tubes around.
408  QString straightCLUid = mOutputTypes[3]->getValue();
409  MeshPtr straightCL = boost::dynamic_pointer_cast<Mesh>(patientService()->getData(straightCLUid));
410  if(!straightCL)
411  return;
412  vtkPolyDataPtr clPolyData = straightCL->getVtkPolyData();
413 
414  // Create the implicit modeller
415  vtkSmartPointer<vtkImplicitModeller> blobbyLogoImp =
416  vtkSmartPointer<vtkImplicitModeller>::New();
417  blobbyLogoImp->SetInputData(clPolyData);
418  blobbyLogoImp->SetMaximumDistance(0.1);
419  blobbyLogoImp->SetSampleDimensions(256, 256, 256);
420  blobbyLogoImp->SetAdjustDistance(0.1);
421 
422  // Extract an iso surface, i.e. the tube shell
423  vtkSmartPointer<vtkContourFilter> blobbyLogoIso =
424  vtkSmartPointer<vtkContourFilter>::New();
425  blobbyLogoIso->SetInputConnection(blobbyLogoImp->GetOutputPort());
426  blobbyLogoIso->SetValue(1, 1.5); //orig
427  blobbyLogoIso->Update();
428 
429  // Create the mesh object from the tube shell
430  QString uid = mInputImage->getUid() + AirwaysFilter::getNameSuffix() + AirwaysFilter::getNameSuffixStraight() + AirwaysFilter::getNameSuffixTubes() + "%1";
431  QString name = mInputImage->getName() + AirwaysFilter::getNameSuffix() + AirwaysFilter::getNameSuffixStraight() + AirwaysFilter::getNameSuffixTubes() + "%1";
432  MeshPtr centerline = patientService()->createSpecificData<Mesh>(uid, name);
433  centerline->setVtkPolyData(blobbyLogoIso->GetOutput());
434  centerline->get_rMd_History()->setParentSpace(mInputImage->getUid());
435  centerline->get_rMd_History()->setRegistration(mTransformation);
436  // The color is taken from the new Fraxinus logo. Blue is the common color for lungs/airways. Partly transparent for a nice effect in Fraxinus.
437  centerline->setColor(QColor(118, 178, 226, 200));
438  patientService()->insertData(centerline);
439  mOutputTypes[4]->setValue(centerline->getUid());
440 
441 }
442 
443 void AirwaysFilter::setDefaultStraightCLTubesOption(bool defaultStraightCLTubesOption)
444 {
445  mDefaultStraightCLTubesOption = defaultStraightCLTubesOption;
446 }
447 
448 void AirwaysFilter::createStraightCL()
449 {
450  QString uid = mInputImage->getUid() + AirwaysFilter::getNameSuffix() + AirwaysFilter::getNameSuffixStraight() + "%1";
451  QString name = mInputImage->getName() + AirwaysFilter::getNameSuffix() + AirwaysFilter::getNameSuffixStraight() + "%1";
452  MeshPtr centerline = patientService()->createSpecificData<Mesh>(uid, name);
453 
455 
456  Eigen::MatrixXd CLpoints = makeTransformedMatrix(mCenterlineOutput);
457  bl->findBranchesInCenterline(CLpoints);
458  vtkPolyDataPtr retval = bl->createVtkPolyDataFromBranches(false, true);
459 
460  centerline->setVtkPolyData(retval);
461  centerline->get_rMd_History()->setParentSpace(mInputImage->getUid());
462  centerline->get_rMd_History()->setRegistration(mTransformation);
463  patientService()->insertData(centerline);
464  mOutputTypes[3]->setValue(centerline->getUid());
465 }
466 
468 {
469  mOptionsAdapters.push_back(this->getManualSeedPointOption(mOptions));
470  mOptionsAdapters.push_back(this->getLungSegmentationOption(mOptions));
471  mOptionsAdapters.push_back(this->getStraightCLTubesOption(mOptions));
472 }
473 
475 {
477 
479  temp->setValueName("Input");
480  temp->setHelp("Select input to run airway segmentation on.");
481  mInputTypes.push_back(temp);
482 }
483 
485 {
486  StringPropertySelectMeshPtr tempMeshStringAdapter;
487  std::vector<std::pair<QString, QString>> valueHelpPairs;
488  valueHelpPairs.push_back(std::make_pair(tr("Airway Centerline"), tr("Generated centerline mesh (vtk-format).")));
489  valueHelpPairs.push_back(std::make_pair(tr("Airway Segmentation"), tr("Generated surface of the airway segmentation volume.")));
490  valueHelpPairs.push_back(std::make_pair(tr("Lung Segmentation"), tr("Generated surface of the lung segmentation volume.")));
491  valueHelpPairs.push_back(std::make_pair(tr("Straight Airway Centerline"), tr("A centerline with straight lines between the branch points.")));
492  valueHelpPairs.push_back(std::make_pair(tr("Straight Airway Tubes"), tr("Tubes based on the straight centerline")));
493 
494  foreach(auto pair, valueHelpPairs)
495  {
496  tempMeshStringAdapter = StringPropertySelectMesh::New(patientService());
497  tempMeshStringAdapter->setValueName(pair.first);
498  tempMeshStringAdapter->setHelp(pair.second);
499  mOutputTypes.push_back(tempMeshStringAdapter);
500  }
501 }
502 
503 
504 BoolPropertyPtr AirwaysFilter::getManualSeedPointOption(QDomElement root)
505 {
506  BoolPropertyPtr retval =
507  BoolProperty::initialize("Use manual seed point",
508  "",
509  "If the automatic seed point detection algorithm fails you can use cursor to set the seed point "
510  "inside trachea of the patient. "
511  "Then tick this checkbox to use the manual seed point in the airways filter.",
512  false, root);
513  return retval;
514 
515 }
516 
517 BoolPropertyPtr AirwaysFilter::getLungSegmentationOption(QDomElement root)
518 {
519  BoolPropertyPtr retval =
520  BoolProperty::initialize("Lung segmentation",
521  "",
522  "Selecting this option will also segment the two lung sacs",
523  false, root);
524  return retval;
525 
526 }
527 
528 BoolPropertyPtr AirwaysFilter::getStraightCLTubesOption(QDomElement root)
529 {
530  BoolPropertyPtr retval =
531  BoolProperty::initialize("Straight centerline and tubes",
532  "",
533  "Use this option to generate a centerline with straight branches between "
534  "the branch points. "
535  "You also get tubes based on this straight line.",
536  mDefaultStraightCLTubesOption, root);
537  return retval;
538 }
539 
540 
541 } /* namespace cx */
542 
boost::shared_ptr< class SpaceProvider > SpaceProviderPtr
QString qstring_cast(const T &val)
static BoolPropertyPtr initialize(const QString &uid, QString name, QString help, bool value, QDomNode root=QDomNode())
std::vector< SelectDataStringPropertyBasePtr > mInputTypes
Definition: cxFilterImpl.h:94
void reportError(QString msg)
Definition: cxLogger.cpp:92
A mesh data set.
Definition: cxMesh.h:66
boost::shared_ptr< class VisServices > VisServicesPtr
Definition: cxMainWindow.h:61
boost::shared_ptr< class BranchList > BranchListPtr
#define CX_LOG_INFO
Definition: cxLogger.h:117
boost::shared_ptr< class Image > ImagePtr
Definition: cxDicomWidget.h:48
virtual vtkPolyDataPtr getVtkPolyData() const
Definition: cxMesh.cpp:150
virtual vtkImageDataPtr getBaseVtkImageData()
Definition: cxImage.cpp:356
virtual void createOutputTypes()
AirwaysFilter(VisServicesPtr services)
std::vector< PropertyPtr > mOptionsAdapters
Definition: cxFilterImpl.h:96
boost::shared_ptr< class Data > DataPtr
static QString findConfigFolder(QString pathRelativeToConfigRoot, QString alternativeAbsolutePath="")
VisServicesPtr mServices
Definition: cxFilterImpl.h:103
boost::shared_ptr< class SelectDataStringPropertyBase > SelectDataStringPropertyBasePtr
PatientModelServicePtr patientService()
virtual bool execute()
static QString getCachePath()
return path to a folder that is used during execution, will be cleared at start and stop...
void setVtkPolyData(const vtkPolyDataPtr &polyData)
Definition: cxMesh.cpp:112
#define CX_LOG_ERROR
Definition: cxLogger.h:120
A volumetric data set.
Definition: cxImage.h:66
int * getImageSize(DataPtr inputImage)
#define CX_LOG_SUCCESS
Definition: cxLogger.h:118
QDomElement mOptions
Definition: cxFilterImpl.h:97
virtual QString getType() const
vtkSmartPointer< vtkPolyData > vtkPolyDataPtr
Eigen::Vector3d Vector3D
Vector3D is a representation of a point or vector in 3D.
Definition: cxVector3D.h:63
std::vector< SelectDataStringPropertyBasePtr > mOutputTypes
Definition: cxFilterImpl.h:95
static QString getNameSuffix()
static QString getNameSuffixStraight()
void setDefaultStraightCLTubesOption(bool defaultStraightCLTubesOption)
static StringPropertySelectMeshPtr New(PatientModelServicePtr patientModelService)
virtual bool postProcess()
static StringPropertySelectImagePtr New(PatientModelServicePtr patientModelService)
boost::shared_ptr< class BoolProperty > BoolPropertyPtr
boost::shared_ptr< class Mesh > MeshPtr
Eigen::MatrixXd makeTransformedMatrix(vtkPolyDataPtr linesPolyData, Transform3D rMd)
makeTransformedMatrix This method takes an vtkpolydata as input, runs it through a transform and retu...
static QString getNameSuffixTubes()
boost::shared_ptr< class StringPropertySelectMesh > StringPropertySelectMeshPtr
virtual QString getName() const
virtual QString getHelp() const
Namespace for all CustusX production code.