Fraxinus  2023.01.05-dev+develop.0da12
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 "cxUtilHelpers.h"
30 
32 #include "cxImageAlgorithms.h"
33 #include "cxDoubleWidgets.h"
34 #include "cxImage.h"
35 #include "cxVolumeHelpers.h"
36 #include "cxPatientModelService.h"
37 #include "cxViewService.h"
38 #include "cxViewGroupData.h"
39 #include "cxReporter.h"
40 #include "cxActiveData.h"
41 #include "cxDataSelectWidget.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  layout->addWidget(new DataSelectWidget(viewService, patientModelService, this, StringPropertyActiveImage::New(patientModelService)));
63  QHBoxLayout* buttonLayout = new QHBoxLayout;
64  layout->addLayout(buttonLayout);
65  QHBoxLayout* buttonLayout2 = new QHBoxLayout;
66  layout->addLayout(buttonLayout2);
67 
68  mShowEraserCheckBox = new QCheckBox("Show eraser");
69  mShowEraserCheckBox->setToolTip("Show eraser sphere in the views.");
70  connect(mShowEraserCheckBox, SIGNAL(toggled(bool)), this, SLOT(toggleShowEraser(bool)));
71  buttonLayout->addWidget(mShowEraserCheckBox);
72 
73  mContinousEraseCheckBox = new QCheckBox("Continous");
74  mContinousEraseCheckBox->setToolTip("Erase continously using the sphere. (might be slow)");
75  connect(mContinousEraseCheckBox, SIGNAL(toggled(bool)), this, SLOT(toggleContinous(bool)));
76  buttonLayout2->addWidget(mContinousEraseCheckBox);
77 
78  mDuplicateAction = this->createAction(this, QIcon(), "Duplicate", "Duplicate active volume - do this before erasing!",
79  SLOT(duplicateSlot()), buttonLayout);
80 
81  mSaveAction = this->createAction(this, QIcon(), "Save", "Save modified image to disk",
82  SLOT(saveSlot()), buttonLayout);
83 
84  mRemoveAction = this->createAction(this, QIcon(), "Erase", "Erase everything inside sphere",
85  SLOT(removeSlot()), buttonLayout2);
86 
87 
88  double sphereRadius = 10;
89  mSphereSizeAdapter = DoubleProperty::initialize("SphereSize", "Sphere Radius (mm)", "Radius of Eraser Sphere in mm", sphereRadius, DoubleRange(0,100,0.1), 1, QDomNode());
90  connect(mSphereSizeAdapter.get(), &DoubleProperty::changed, this, &EraserWidget::sphereSizeChangedSlot);
91  mSphereSize = new SpinBoxAndSliderGroupWidget(this, mSphereSizeAdapter);
92  layout->addWidget(mSphereSize);
93 
94  ImagePtr image = mActiveData->getActive<Image>();
95  int eraseValue = 0;
96  if(image)
97  eraseValue = image->getMin();
98  mEraseValueAdapter = DoubleProperty::initialize("EraseValue", "Erase value", "Erase/draw with value", eraseValue, DoubleRange(1,200,1), 0, QDomNode());
99 
100  mActiveImageProxy = ActiveImageProxy::New(mActiveData);
101  connect(mActiveImageProxy.get(), &ActiveImageProxy::activeImageChanged, this, &EraserWidget::activeImageChangedSlot);
102 
103  mEraseValueWidget = new SpinBoxAndSliderGroupWidget(this, mEraseValueAdapter);
104  layout->addWidget(mEraseValueWidget);
105 
106  layout->addStretch();
107 
108  this->enableButtons();
109 }
110 
112 {
113  ImagePtr image = mActiveData->getActive<Image>();
114  if(!image)
115  return;
116 
117  mEraseValueAdapter->setValueRange(DoubleRange(image->getVTKMinValue(), image->getVTKMaxValue(), 1));
118  mEraseValueAdapter->setValue(image->getMin());
119 }
120 
121 void EraserWidget::enableButtons()
122 {
123  bool e = mShowEraserCheckBox->isChecked();
124 
125  mContinousEraseCheckBox->setEnabled(e);
126 // mDuplicateAction->setEnabled(e);
127 // mSaveAction->setEnabled(e);
128  mRemoveAction->setEnabled(e);
129  mSphereSize->setEnabled(e);
130 }
131 
133 {
134 }
135 
137 {
138  if (on)
139  {
140  mContinousEraseTimer->start(300);
141  }
142  else
143  {
144  mContinousEraseTimer->stop();
145  }
146 }
147 
149 {
150  Transform3D rMd = mViewService->getGroup(0)->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 
163 {
164  ImagePtr original = mActiveData->getActive<Image>();
165 
166  if (!original)
167  return;
168 
169  ImagePtr duplicate = duplicateImage(mPatientModelService, original);
170  mPatientModelService->insertData(duplicate);
171  mActiveData->setActive(duplicate);
172 
173  // replace viz of original with duplicate
174 // std::vector<ViewGroupPtr> viewGroups = mViewService->getViewGroupDatas();
175  for (unsigned i = 0; i < mViewService->groupCount(); ++i)
176  {
177  if (mViewService->getGroup(i)->removeData(original->getUid()))
178  mViewService->getGroup(i)->addData(duplicate->getUid());
179  }
180 }
181 
183 {
184  if (mSphere)
185  {
186  mSphere->SetRadius(mSphereSizeAdapter->getValue());
187  mSphere->Update();
188  }
189 }
190 
196 {
197  ImagePtr image = mActiveData->getActive<Image>();
198  if (!image)
199  return;
200 
201  mPatientModelService->insertData(image);
202 }
203 
204 
205 template <class TYPE>
206 void EraserWidget::eraseVolume(TYPE* volumePointer)
207 {
208  ImagePtr image = mActiveData->getActive<Image>();
209  vtkImageDataPtr img = image->getBaseVtkImageData();
210  Eigen::Array3i dim(img->GetDimensions());
211  Eigen::Array3d spacing(img->GetSpacing());
212  Transform3D rMd = mViewService->getGroup(0)->getOptions().mPickerGlyph->get_rMd();
213  Eigen::Array3d c(mSphere->GetCenter());
214  c = rMd.coord(c);
215  double r = mSphere->GetRadius();
216  int replaceVal = mEraseValueAdapter->getValue();
217 
218  mPreviousCenter = c;
219  mPreviousRadius = r;
220 
221  Eigen::Array3d scaledRadius = r*spacing.inverse();
222  Transform3D dMr = image->get_rMd().inv();
223  Transform3D rawMd = createTransformScale(spacing).inv();
224  Transform3D rawMr = rawMd * dMr;
225  c = rawMr.coord(c); //Center voxel
226 
227  Eigen::Array3i lowVoxIdx = (c - scaledRadius).floor().cast<int>();
228  lowVoxIdx = lowVoxIdx.max(0);
229  Eigen::Array3i highVoxIdx = (c + scaledRadius).ceil().cast<int>();
230  highVoxIdx = highVoxIdx.min(dim);
231 
232  for (int x = lowVoxIdx(0); x < highVoxIdx(0); ++x)
233  for (int y = lowVoxIdx(1); y < highVoxIdx(1); ++y)
234  for (int z = lowVoxIdx(2); z < highVoxIdx(2); ++z)
235  {
236  int index = x + y * dim[0] + z * dim[0] * dim[1];
237  if ((Vector3D((x-c(0))*spacing[0], (y-c(1))*spacing[1], (z-c(2))*spacing[2])).length() < r)
238  volumePointer[index] = replaceVal;
239  }
240 }
241 
242 //#define VTK_VOID 0
243 //#define VTK_BIT 1
244 //#define VTK_CHAR 2
245 //#define VTK_SIGNED_CHAR 15
246 //#define VTK_UNSIGNED_CHAR 3
247 //#define VTK_SHORT 4
248 //#define VTK_UNSIGNED_SHORT 5
249 //#define VTK_INT 6
250 //#define VTK_UNSIGNED_INT 7
251 //#define VTK_LONG 8
252 //#define VTK_UNSIGNED_LONG 9
253 //#define VTK_FLOAT 10
254 //#define VTK_DOUBLE 11
255 //#define VTK_ID_TYPE 12
256 
258 {
259  if (!mSphere)
260  return;
261 
262  ImagePtr image = mActiveData->getActive<Image>();
263  if (!image)
264  return;
265 
266  vtkImageDataPtr img = image->getBaseVtkImageData();
267  int vtkScalarType = img->GetScalarType();
268 
269  if (vtkScalarType==VTK_CHAR)
270  this->eraseVolume(static_cast<char*> (img->GetScalarPointer()));
271  else if (vtkScalarType==VTK_UNSIGNED_CHAR)
272  this->eraseVolume(static_cast<unsigned char*> (img->GetScalarPointer()));
273  else if (vtkScalarType==VTK_SIGNED_CHAR)
274  this->eraseVolume(static_cast<signed char*> (img->GetScalarPointer()));
275  else if (vtkScalarType==VTK_UNSIGNED_SHORT)
276  this->eraseVolume(static_cast<unsigned short*> (img->GetScalarPointer()));
277  else if (vtkScalarType==VTK_SHORT)
278  this->eraseVolume(static_cast<short*> (img->GetScalarPointer()));
279  else if (vtkScalarType==VTK_UNSIGNED_INT)
280  this->eraseVolume(static_cast<unsigned int*> (img->GetScalarPointer()));
281  else if (vtkScalarType==VTK_INT)
282  this->eraseVolume(static_cast<int*> (img->GetScalarPointer()));
283  else
284  reportError(QString("Unknown VTK ScalarType: %1").arg(vtkScalarType));
285 
286  ImageLUT2DPtr tf2D = image->getLookupTable2D();
287  ImageTF3DPtr tf3D = image->getTransferFunctions3D();
288 
289 // img->Modified();
290  setDeepModified(img);
291  image->setVtkImageData(img);
292 
293  // keep existing transfer functions
294  image->setLookupTable2D(tf2D);
295  image->setTransferFunctions3D(tf3D);
296 }
297 
299 {
300  if (on)
301  {
302 // std::vector<ViewGroupPtr> viewGroups = mViewService->getViewGroups();
303  mSphere = vtkSphereSourcePtr::New();
304 
305  mSphere->SetRadius(40);
306  mSphere->SetThetaResolution(16);
307  mSphere->SetPhiResolution(12);
308  mSphere->LatLongTessellationOn(); // more natural wireframe view
309 
310  double a = mSphereSizeAdapter->getValue();
311  mSphere->SetRadius(a);
312  mSphere->Update();
313  MeshPtr glyph = mViewService->getGroup(0)->getOptions().mPickerGlyph;
314  glyph->setVtkPolyData(mSphere->GetOutput());
315  glyph->setColor(QColor(255, 204, 0)); // same as tool
316  glyph->setIsWireframe(true);
317 
318  // set same glyph in all groups
319  for (unsigned i=0; i<mViewService->groupCount(); ++i)
320  {
321  ViewGroupData::Options options = mViewService->getGroup(i)->getOptions();
322  options.mPickerGlyph = glyph;
323  mViewService->getGroup(i)->setOptions(options);
324  }
325  }
326  else
327  {
328  mViewService->getGroup(0)->getOptions().mPickerGlyph->setVtkPolyData(NULL);
329  mContinousEraseCheckBox->setChecked(false);
330  }
331 
332  this->enableButtons();
333 }
334 
335 }
void reportError(QString msg)
Definition: cxLogger.cpp:71
Vector3D ceil(const Vector3D &a)
Definition: cxVector3D.cpp:84
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
void toggleContinous(bool on)
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
static StringPropertyActiveImagePtr New(PatientModelServicePtr patientModelService)
boost::shared_ptr< class PatientModelService > PatientModelServicePtr
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())
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
void toggleShowEraser(bool on)
Namespace for all CustusX production code.