CustusX  18.04-rc6
An IGT application
cxEraserWidget.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) SINTEF Department of Medical Technology.
5 All rights reserved.
6 
7 CustusX is released under a BSD 3-Clause license.
8 
9 See Lisence.txt (https://github.com/SINTEFMedtek/CustusX/blob/master/License.txt) for details.
10 =========================================================================*/
11 
12 #include <cxEraserWidget.h>
13 
14 #include <QTimer>
15 #include <QCheckBox>
16 
17 #include "vtkSphereWidget.h"
18 #include "vtkSplineWidget.h"
19 #include "vtkSplineWidget2.h"
20 #include "vtkSplineRepresentation.h"
21 #include "vtkRenderWindow.h"
22 #include <vtkSphere.h>
23 #include <vtkClipPolyData.h>
24 #include <vtkImageData.h>
25 
26 #include "cxMesh.h"
27 #include "cxStringPropertyBase.h"
29 #include "cxDefinitionStrings.h"
30 #include "cxUtilHelpers.h"
31 
33 #include "cxImageAlgorithms.h"
34 #include "cxDoubleWidgets.h"
35 #include "cxImage.h"
36 #include "cxVolumeHelpers.h"
37 #include "cxPatientModelService.h"
38 #include "cxViewService.h"
39 #include "cxViewGroupData.h"
40 #include "cxReporter.h"
41 #include "cxActiveData.h"
42 
43 namespace cx
44 {
45 
46 EraserWidget::EraserWidget(PatientModelServicePtr patientModelService, ViewServicePtr viewService, QWidget* parent) :
47  BaseWidget(parent, "eraser_widget", "Eraser"),
48  mPreviousCenter(0,0,0),
49  mPreviousRadius(0),
50  mActiveImageProxy(ActiveImageProxyPtr()),
51  mPatientModelService(patientModelService),
52  mViewService(viewService),
53  mActiveData(patientModelService->getActiveData())
54 {
55 
56  QVBoxLayout* layout = new QVBoxLayout(this);
57  this->setToolTip("Erase parts of volumes/models");
58 
59  mContinousEraseTimer = new QTimer(this);
60  connect(mContinousEraseTimer, SIGNAL(timeout()), this, SLOT(continousRemoveSlot())); // this signal will be executed in the thread of THIS, i.e. the main thread.
61 
62  QHBoxLayout* buttonLayout = new QHBoxLayout;
63  layout->addLayout(buttonLayout);
64  QHBoxLayout* buttonLayout2 = new QHBoxLayout;
65  layout->addLayout(buttonLayout2);
66 
67  mShowEraserCheckBox = new QCheckBox("Show");
68  mShowEraserCheckBox->setToolTip("Show eraser sphere in the views.");
69  connect(mShowEraserCheckBox, SIGNAL(toggled(bool)), this, SLOT(toggleShowEraser(bool)));
70  buttonLayout->addWidget(mShowEraserCheckBox);
71 
72  mContinousEraseCheckBox = new QCheckBox("Continous");
73  mContinousEraseCheckBox->setToolTip("Erase continously using the sphere. (might be slow)");
74  connect(mContinousEraseCheckBox, SIGNAL(toggled(bool)), this, SLOT(toggleContinous(bool)));
75  buttonLayout2->addWidget(mContinousEraseCheckBox);
76 
77  mDuplicateAction = this->createAction(this, QIcon(), "Duplicate", "Duplicate active volume - do this before erasing!",
78  SLOT(duplicateSlot()), buttonLayout);
79 
80  mSaveAction = this->createAction(this, QIcon(), "Save", "Save modified image to disk",
81  SLOT(saveSlot()), buttonLayout);
82 
83  mRemoveAction = this->createAction(this, QIcon(), "Erase", "Erase everything inside sphere",
84  SLOT(removeSlot()), buttonLayout2);
85 
86 
87  double sphereRadius = 10;
88  mSphereSizeAdapter = DoubleProperty::initialize("SphereSize", "Sphere Size", "Radius of Eraser Sphere", sphereRadius, DoubleRange(0,200,1), 0, QDomNode());
89  connect(mSphereSizeAdapter.get(), &DoubleProperty::changed, this, &EraserWidget::sphereSizeChangedSlot);
90  mSphereSize = new SpinBoxAndSliderGroupWidget(this, mSphereSizeAdapter);
91  layout->addWidget(mSphereSize);
92 
93  ImagePtr image = mActiveData->getActive<Image>();
94  int eraseValue = 0;
95  if(image)
96  eraseValue = image->getMin();
97  mEraseValueAdapter = DoubleProperty::initialize("EraseValue", "Erase value", "Erase/draw with value", eraseValue, DoubleRange(1,200,1), 0, QDomNode());
98 
99  mActiveImageProxy = ActiveImageProxy::New(mActiveData);
100  connect(mActiveImageProxy.get(), &ActiveImageProxy::activeImageChanged, this, &EraserWidget::activeImageChangedSlot);
101 
102  mEraseValueWidget = new SpinBoxAndSliderGroupWidget(this, mEraseValueAdapter);
103  layout->addWidget(mEraseValueWidget);
104 
105  layout->addStretch();
106 
107  this->enableButtons();
108 }
109 
110 void EraserWidget::activeImageChangedSlot()
111 {
112  ImagePtr image = mActiveData->getActive<Image>();
113  if(!image)
114  return;
115 
116  mEraseValueAdapter->setValueRange(DoubleRange(image->getVTKMinValue(), image->getVTKMaxValue(), 1));
117  mEraseValueAdapter->setValue(image->getMin());
118 }
119 
120 void EraserWidget::enableButtons()
121 {
122  bool e = mShowEraserCheckBox->isChecked();
123 
124  mContinousEraseCheckBox->setEnabled(e);
125 // mDuplicateAction->setEnabled(e);
126 // mSaveAction->setEnabled(e);
127  mRemoveAction->setEnabled(e);
128  mSphereSize->setEnabled(e);
129 }
130 
132 {
133 }
134 
135 void EraserWidget::toggleContinous(bool on)
136 {
137  if (on)
138  {
139  mContinousEraseTimer->start(300);
140  }
141  else
142  {
143  mContinousEraseTimer->stop();
144  }
145 }
146 
147 void EraserWidget::continousRemoveSlot()
148 {
149  Transform3D rMd = mViewService->getGroup(0)->getOptions().mPickerGlyph->get_rMd();
150  //Transform3D rMd = mViewService->getViewGroupDatas().front()->getData()->getOptions().mPickerGlyph->get_rMd();
151  Vector3D c(mSphere->GetCenter());
152  c = rMd.coord(c);
153  double r = mSphere->GetRadius();
154 
155  // optimization: dont remove if idle
156  if (similar(mPreviousCenter, c) && similar(mPreviousRadius, r))
157  return;
158 
159  this->removeSlot();
160 }
161 
162 void EraserWidget::duplicateSlot()
163 {
164  ImagePtr original = mActiveData->getActive<Image>();
165 
166  ImagePtr duplicate = duplicateImage(mPatientModelService, original);
167  mPatientModelService->insertData(duplicate);
168  mActiveData->setActive(duplicate);
169 
170  // replace viz of original with duplicate
171 // std::vector<ViewGroupPtr> viewGroups = mViewService->getViewGroupDatas();
172  for (unsigned i = 0; i < mViewService->groupCount(); ++i)
173  {
174  if (mViewService->getGroup(i)->removeData(original->getUid()))
175  mViewService->getGroup(i)->addData(duplicate->getUid());
176  }
177 }
178 
179 void EraserWidget::sphereSizeChangedSlot()
180 {
181  if (mSphere)
182  {
183  mSphere->SetRadius(mSphereSizeAdapter->getValue());
184  mSphere->Update();
185  }
186 }
187 
192 void EraserWidget::saveSlot()
193 {
194  mPatientModelService->insertData(mActiveData->getActive<Image>());
195 }
196 
197 
198 template <class TYPE>
199 void EraserWidget::eraseVolume(TYPE* volumePointer)
200 {
201  ImagePtr image = mActiveData->getActive<Image>();
202  vtkImageDataPtr img = image->getBaseVtkImageData();
203 
204 
205  Eigen::Array3i dim(img->GetDimensions());
206  Vector3D spacing(img->GetSpacing());
207 
208  Transform3D rMd = mViewService->getGroup(0)->getOptions().mPickerGlyph->get_rMd();
209  Vector3D c(mSphere->GetCenter());
210  c = rMd.coord(c);
211  double r = mSphere->GetRadius();
212  mPreviousCenter = c;
213  mPreviousRadius = r;
214 
215  DoubleBoundingBox3D bb_r(c[0]-r, c[0]+r, c[1]-r, c[1]+r, c[2]-r, c[2]+r);
216 
217  Transform3D dMr = image->get_rMd().inv();
218  Transform3D rawMd = createTransformScale(spacing).inv();
219  Transform3D rawMr = rawMd * dMr;
220  Vector3D c_d = dMr.coord(c);
221  double r_d = dMr.vector(r * Vector3D::UnitX()).length();
222  c = rawMr.coord(c);
223  r = rawMr.vector(r * Vector3D::UnitX()).length();
224  DoubleBoundingBox3D bb0_raw = transform(rawMr, bb_r);
225  IntBoundingBox3D bb1_raw(0, dim[0], 0, dim[1], 0, dim[2]);
226 
227 // std::cout << " sphere: " << bb0_raw << std::endl;
228 // std::cout << " raw: " << bb1_raw << std::endl;
229 
230  for (int i=0; i<3; ++i)
231  {
232  bb1_raw[2*i] = std::max<double>(bb1_raw[2*i], bb0_raw[2*i]);
233  bb1_raw[2*i+1] = std::min<double>(bb1_raw[2*i+1], bb0_raw[2*i+1]);
234  }
235 
236  int replaceVal = mEraseValueAdapter->getValue();
237 
238  for (int x = bb1_raw[0]; x < bb1_raw[1]; ++x)
239  for (int y = bb1_raw[2]; y < bb1_raw[3]; ++y)
240  for (int z = bb1_raw[4]; z < bb1_raw[5]; ++z)
241  {
242  int index = x + y * dim[0] + z * dim[0] * dim[1];
243  if ((Vector3D(x*spacing[0], y*spacing[1], z*spacing[2]) - c_d).length() < r_d)
244  volumePointer[index] = replaceVal;
245  }
246 }
247 
248 //#define VTK_VOID 0
249 //#define VTK_BIT 1
250 //#define VTK_CHAR 2
251 //#define VTK_SIGNED_CHAR 15
252 //#define VTK_UNSIGNED_CHAR 3
253 //#define VTK_SHORT 4
254 //#define VTK_UNSIGNED_SHORT 5
255 //#define VTK_INT 6
256 //#define VTK_UNSIGNED_INT 7
257 //#define VTK_LONG 8
258 //#define VTK_UNSIGNED_LONG 9
259 //#define VTK_FLOAT 10
260 //#define VTK_DOUBLE 11
261 //#define VTK_ID_TYPE 12
262 
263 void EraserWidget::removeSlot()
264 {
265  if (!mSphere)
266  return;
267 
268  ImagePtr image = mActiveData->getActive<Image>();
269  vtkImageDataPtr img = image->getBaseVtkImageData();
270 
271  int vtkScalarType = img->GetScalarType();
272 
273  if (vtkScalarType==VTK_CHAR)
274  this->eraseVolume(static_cast<char*> (img->GetScalarPointer()));
275  else if (vtkScalarType==VTK_UNSIGNED_CHAR)
276  this->eraseVolume(static_cast<unsigned char*> (img->GetScalarPointer()));
277  else if (vtkScalarType==VTK_SIGNED_CHAR)
278  this->eraseVolume(static_cast<signed char*> (img->GetScalarPointer()));
279  else if (vtkScalarType==VTK_UNSIGNED_SHORT)
280  this->eraseVolume(static_cast<unsigned short*> (img->GetScalarPointer()));
281  else if (vtkScalarType==VTK_SHORT)
282  this->eraseVolume(static_cast<short*> (img->GetScalarPointer()));
283  else if (vtkScalarType==VTK_UNSIGNED_INT)
284  this->eraseVolume(static_cast<unsigned int*> (img->GetScalarPointer()));
285  else if (vtkScalarType==VTK_INT)
286  this->eraseVolume(static_cast<int*> (img->GetScalarPointer()));
287  else
288  reportError(QString("Unknown VTK ScalarType: %1").arg(vtkScalarType));
289 
290  ImageLUT2DPtr tf2D = image->getLookupTable2D();
291  ImageTF3DPtr tf3D = image->getTransferFunctions3D();
292 
293 // img->Modified();
294  setDeepModified(img);
295  image->setVtkImageData(img);
296 
297  // keep existing transfer functions
298  image->setLookupTable2D(tf2D);
299  image->setTransferFunctions3D(tf3D);
300 }
301 
302 void EraserWidget::toggleShowEraser(bool on)
303 {
304  if (on)
305  {
306 // std::vector<ViewGroupPtr> viewGroups = mViewService->getViewGroups();
307  mSphere = vtkSphereSourcePtr::New();
308 
309  mSphere->SetRadius(40);
310  mSphere->SetThetaResolution(16);
311  mSphere->SetPhiResolution(12);
312  mSphere->LatLongTessellationOn(); // more natural wireframe view
313 
314  double a = mSphereSizeAdapter->getValue();
315  mSphere->SetRadius(a);
316  mSphere->Update();
317  MeshPtr glyph = mViewService->getGroup(0)->getOptions().mPickerGlyph;
318  glyph->setVtkPolyData(mSphere->GetOutput());
319  glyph->setColor(QColor(255, 204, 0)); // same as tool
320  glyph->setIsWireframe(true);
321 
322  // set same glyph in all groups
323  for (unsigned i=0; i<mViewService->groupCount(); ++i)
324  {
325  ViewGroupData::Options options = mViewService->getGroup(i)->getOptions();
326  options.mPickerGlyph = glyph;
327  mViewService->getGroup(i)->setOptions(options);
328  }
329  }
330  else
331  {
332  mViewService->getGroup(0)->getOptions().mPickerGlyph->setVtkPolyData(NULL);
333  mContinousEraseCheckBox->setChecked(false);
334  }
335 
336  this->enableButtons();
337 }
338 
339 }
DoubleBoundingBox3D transform(const Transform3D &m, const DoubleBoundingBox3D &bb)
void reportError(QString msg)
Definition: cxLogger.cpp:71
Transform3D createTransformScale(const Vector3D &scale_)
Transform3D Transform3D
Transform3D is a representation of an affine 3D transform.
Utility class for describing a bounded numeric range.
Definition: cxDoubleRange.h:32
boost::shared_ptr< class Image > ImagePtr
Definition: cxDicomWidget.h:27
boost::shared_ptr< class ActiveImageProxy > ActiveImageProxyPtr
boost::shared_ptr< class ViewService > ViewServicePtr
virtual vtkImageDataPtr getBaseVtkImageData()
Definition: cxImage.cpp:335
virtual int getMin()
Definition: cxImage.cpp:429
QAction * createAction(QObject *parent, QIcon iconName, QString text, QString tip, T slot, QLayout *layout=NULL, QToolButton *button=new QToolButton())
Definition: cxBaseWidget.h:129
Composite widget for scalar data manipulation.
ImagePtr duplicateImage(PatientModelServicePtr dataManager, ImagePtr image)
boost::shared_ptr< class ImageLUT2D > ImageLUT2DPtr
static ActiveImageProxyPtr New(ActiveDataPtr activeData)
A volumetric data set.
Definition: cxImage.h:45
boost::shared_ptr< class PatientModelService > PatientModelServicePtr
Representation of an integer bounding box in 3D. The data are stored as {xmin,xmax,ymin,ymax,zmin,zmax}, in order to simplify communication with vtk.
Representation of a floating-point bounding box in 3D. The data are stored as {xmin,xmax,ymin,ymax,zmin,zmax}, in order to simplify communication with vtk.
void changed()
emit when the underlying data value is changed: The user interface will be updated.
Eigen::Vector3d Vector3D
Vector3D is a representation of a point or vector in 3D.
Definition: cxVector3D.h:42
Interface for QWidget which handles widgets uniformly for the system.
Definition: cxBaseWidget.h:88
void setDeepModified(vtkImageDataPtr image)
bool similar(const CameraInfo &lhs, const CameraInfo &rhs, double tol)
static DoublePropertyPtr initialize(const QString &uid, QString name, QString help, double value, DoubleRange range, int decimals, QDomNode root=QDomNode())
RealScalar length() const
EraserWidget(PatientModelServicePtr patientModelService, ViewServicePtr viewService, QWidget *parent)
void activeImageChanged(const QString &uid)
The original image changed signal from DataManager.
boost::shared_ptr< class Mesh > MeshPtr
vtkSmartPointer< class vtkImageData > vtkImageDataPtr
boost::shared_ptr< class ImageTF3D > ImageTF3DPtr
Namespace for all CustusX production code.