SMILX  1.01
milxQtUnifiedWindow.cpp
1 /*=========================================================================
2  The Software is copyright (c) Commonwealth Scientific and Industrial Research Organisation (CSIRO)
3  ABN 41 687 119 230.
4  All rights reserved.
5 
6  Licensed under the CSIRO BSD 3-Clause License
7  You may not use this file except in compliance with the License.
8  You may obtain a copy of the License in the file LICENSE.md or at
9 
10  https://stash.csiro.au/projects/SMILI/repos/smili/browse/license.txt
11 
12  Unless required by applicable law or agreed to in writing, software
13  distributed under the License is distributed on an "AS IS" BASIS,
14  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
15  See the License for the specific language governing permissions and
16  limitations under the License.
17 =========================================================================*/
18 #include <limits>
19 
20 #include "milxQtUnifiedWindow.h"
21 #include <QDebug>
22 //ITK
23 #include <itkLinearInterpolateImageFunction.h>
24 //VTK
25 #include <vtkSmartPointer.h>
26 //#include <vtkPolyDataPointSampler.h>
27 #include <vtkCellLocator.h>
28 #include <vtkGenericCell.h>
29 #include <vtkLineSource.h> //Testing
30 #include <vtkDoubleArray.h>
31 //Checkboarding
32 #include <vtkImageMapToWindowLevelColors.h>
33 #include <vtkCheckerboardRepresentation.h>
34 
36 {
38  milxQtWindow::prefix = "Uni: ";
39 
40  createActions();
41 
43 }
44 
46 {
47  //dtor
48 }
49 
51 {
52  bool alreadyPresent = false;
53 
54  if(!model)
55  return;
56 
57  setCommonProperties(model);
58 
59  if(!unifyModels.isEmpty())
60  alreadyPresent = unifyModels.contains(model);
61 
62  if(!alreadyPresent)
63  unifyModels.append(model);
64 
65  refresh();
66 }
67 
69 {
70  bool alreadyPresent = false;
71 
72  if(!image)
73  return;
74 
75  setCommonProperties(image);
76 
77  if(!unifyImages.isEmpty())
78  alreadyPresent = unifyImages.contains(image);
79 
80  if(!alreadyPresent)
81  unifyImages.append(image);
82 
83  refresh();
84 }
85 
86 void milxQtUnifiedWindow::removeFromWindow(QWidget *passedWindow)
87 {
88  bool present = false;
89 
90  if(!passedWindow)
91  return;
92 
94  milxQtModel *model = qobject_cast<milxQtModel *>(passedWindow);
95 // if(model == 0)
96 // milxQtImage *image = qobject_cast<milxQtImage *>(passedWindow);
97 
98  if(!unifyModels.isEmpty())
99  present = unifyModels.contains(model);
100 
101  if(present)
102  {
103  unifyModels.removeAll(model);
104  RemoveActor(model->GetActor());
105  unionAct->setChecked(true); //reset the unified window display type just in case
106  }
107 }
108 
110 {
111  if(unionAct->isChecked())
112  generateUnion();
113  if(geoDifferenceAct->isChecked())
115 }
116 
118 {
119  if(unifyModels.isEmpty() && unifyImages.isEmpty())
120  return;
121 
122  printInfo("Generating Union");
123  RemoveAllActors();
124  foreach(milxQtModel *model, unifyModels) //Add models
125  {
126  if(unionAct->isChecked())
127  addModelActor(model->GetActor());
128  else
129  removeModelActor(model->GetActor());
130 
131  qApp->processEvents();
132  }
133  foreach(milxQtImage *img, unifyImages) //Add images
134  {
135  if(unionAct->isChecked())
136  {
137  //Transform actor to correct orientation
138  vtkSmartPointer<vtkMatrix4x4> transformModel = vtkSmartPointer<vtkMatrix4x4>::New();
139  transformModel->DeepCopy(img->getTransformMatrix());
140  transformModel->Invert();
141 
142  addImageActor(img->GetImageActor(), transformModel);
143 
146  connect(img, SIGNAL(modified(vtkSmartPointer<vtkImageActor> )), this, SLOT(updateImageActor(vtkSmartPointer<vtkImageActor>)));
147  }
148  else
150 
151  qApp->processEvents();
152  }
153 
155 }
156 
157 void milxQtUnifiedWindow::generateDifference(double pseudoInfinityFactor)
158 {
159  if(unifyModels.isEmpty())
160  return;
161 
162  emit working(-1);
163  double bounds[6];
164  unifyModels.first()->GetOutput()->GetBounds(bounds);
165  printInfo("Original Bounds: " + QString::number(bounds[0]) + ", " + QString::number(bounds[1]) + ", " + QString::number(bounds[2]) + ", " + QString::number(bounds[3]) + ", " + QString::number(bounds[4]) + ", " + QString::number(bounds[5]));
166 
168  double bigBounds[6];
169  foreach(QPointer<milxQtModel> mdl, unifyModels)
170  {
171  if(mdl == unifyModels.first())
172  continue;
173 
174  double mdlBounds[6];
175  mdl->GetOutput()->GetBounds(mdlBounds);
176  printInfo("Bounds: " + QString::number(mdlBounds[0]) + ", " + QString::number(mdlBounds[1]) + ", " + QString::number(mdlBounds[2]) + ", " + QString::number(mdlBounds[3]) + ", " + QString::number(mdlBounds[4]) + ", " + QString::number(mdlBounds[5]));
177 
178  //Get largest bounds
179  bigBounds[0] = milx::Minimum<double>(mdlBounds[0], bounds[0]);
180  bigBounds[1] = milx::Maximum<double>(mdlBounds[1], bounds[1]);
181  bigBounds[2] = milx::Minimum<double>(mdlBounds[2], bounds[2]);
182  bigBounds[3] = milx::Maximum<double>(mdlBounds[3], bounds[3]);
183  bigBounds[4] = milx::Minimum<double>(mdlBounds[4], bounds[4]);
184  bigBounds[5] = milx::Maximum<double>(mdlBounds[5], bounds[5]);
185  }
186 
187  //pad bounds by 5% more
188 // bigBounds[0] -= 0.05*bigBounds[0];
189 // bigBounds[1] += 0.05*bigBounds[1];
190 // bigBounds[2] -= 0.05*bigBounds[2];
191 // bigBounds[3] += 0.05*bigBounds[3];
192 // bigBounds[4] -= 0.05*bigBounds[4];
193 // bigBounds[5] += 0.05*bigBounds[5];
194  printInfo("Overall Bounds: " + QString::number(bigBounds[0]) + ", " + QString::number(bigBounds[1]) + ", " + QString::number(bigBounds[2]) + ", " + QString::number(bigBounds[3]) + ", " + QString::number(bigBounds[4]) + ", " + QString::number(bigBounds[5]));
195 
196  printInfo("Computing Distance map of first model ...");
197  QPointer<milxQtImage> image = new milxQtImage; //List deletion
198  image->setName("Voxelisation Source");
199  image->generateVoxelisedSurface(unifyModels.first()->GetOutput(), bigBounds);
200  image->distanceMap();
201  image->generateImage();
202  emit imageAvailable(image);
203 
205  // compute dimensions of image having all surfaces
206  /*int dim[3];
207  for (int i = 0; i < 3; i++)
208  {
209  dim[i] = static_cast<int>( ceil((bigBounds[i * 2 + 1] - bigBounds[i * 2]) / image->GetCharImage()->GetSpacing()[i]) );
210  }
211  printInfo("Dimensions of Distance map is " + QString::number(dim[0]) + "x" + QString::number(dim[1]) + "x" + QString::number(dim[2]));
212 
213  image->resize(dim[0], dim[1], dim[2]);
214  emit imageAvailable(image);*/
215 
216  /*QPointer<milxQtImage> distImage = new milxQtImage; //List deletion
217  distImage->set8BitImage();
218  distImage->zeros(dim[0], dim[1], dim[2], image);
219  distImage->setName("Distance Image Source");
220  distImage->add(image);
221  distImage->distanceMap();
222  distImage->generateImage();
223  emit imageAvailable(image);*/
224 
226  const bool absValues = true;
227  floatImageType::Pointer distanceImage = image->GetFloatImage();
228 
230  printInfo("Computing Distance map for other models ...");
231  foreach(QPointer<milxQtModel> mdl, unifyModels)
232  {
233  if(mdl == unifyModels.first())
234  continue;
235 
236  printInfo(mdl->strippedBaseName() + " info:");
237  printInfo("Computing Distance map ...");
238  QPointer<milxQtImage> image2 = new milxQtImage; //List deletion
239  image2->generateVoxelisedSurface(mdl->GetOutput(), bigBounds);
240  image2->generateImage();
241  image2->distanceMap();
242  emit imageAvailable(image2);
243 
244  floatImageType::Pointer distanceImage2 = image2->GetFloatImage();
245  printInfo("Computing Forward Distances ...");
246  vtkSmartPointer<vtkFloatArray> scalars1 = surfaceScalarsFromImage(mdl->GetOutput(), distanceImage, absValues);
247  printInfo("Computing Backward Distances ...");
248  vtkSmartPointer<vtkFloatArray> scalars2 = surfaceScalarsFromImage(unifyModels.first()->GetOutput(), distanceImage2, absValues);
249 
250  double range1[2];
251  scalars1->GetRange(range1);
252  double range2[2];
253  scalars2->GetRange(range2);
254  double hausdorff = milx::Maximum<double>(range1[1], range2[1]);
255  printInfo("Hausdorff Distance: " + QString::number(hausdorff));
256 
257  mdl->GetOutput()->GetPointData()->SetScalars(scalars1);
258  mdl->GetOutput()->Modified();
259  #if VTK_MAJOR_VERSION <=5
260  mdl->GetOutput()->Update();
261  #endif // VTK_MAJOR_VERSION
262  mdl->colourMapToHOT();
263  unifyModels.first()->GetOutput()->GetPointData()->SetScalars(scalars2);
264  unifyModels.first()->GetOutput()->Modified();
265  #if VTK_MAJOR_VERSION <=5
266  unifyModels.first()->GetOutput()->Update();
267  #endif // VTK_MAJOR_VERSION
268  }
269  unifyModels.first()->colourMapToHOT();
270  emit done(-1);
271 }
272 
274 {
275  printWarning("Scalar Difference not implemented yet!");
276 
277  if(unifyModels.isEmpty())
278  return;
279 }
280 
282 {
283  if(unifyImages.isEmpty() && unifyImages.size() < 2)
284  {
285  printError("Not enough images being compared. Add more.");
286  return;
287  }
288 
289  printInfo("Generating Checkboard to View Images");
290 
291  // Create a checker pipeline
292  checker = vtkSmartPointer<vtkImageCheckerboard>::New();
293  checker->SetNumberOfDivisions(5,5,1);
294  size_t count = 0;
295  foreach(milxQtImage *img, unifyImages)
296  {
297  printDebug("Adding " + img->getName() + " to checkerboard");
298  int *displayExtents = img->GetDisplayExtent();
299  cerr << "Slice: " << displayExtents[0] << ", " << displayExtents[1] << ", " << displayExtents[2]
300  << ", " << displayExtents[3] << ", " << displayExtents[4] << ", " << displayExtents[5] << endl;
301  vtkSmartPointer<vtkImageReslice> slice = vtkSmartPointer<vtkImageReslice>::New();
302  #if VTK_MAJOR_VERSION <=5
303  slice->SetInput(img->GetOutput());
304  #else
305  slice->SetInputData(img->GetOutput());
306  #endif // VTK_MAJOR_VERSION
307 // slice->SetOutputExtent(0, 9, 0, 100, 0, 0);
308  slice->SetOutputExtent(displayExtents);
309  slice->Update();
310  #if VTK_MAJOR_VERSION <=5
311  checker->SetInput(count, slice->GetOutput()); //Needs to be 2D image here
312  #else
313  checker->SetInputData(count, slice->GetOutput()); //Needs to be 2D image here
314  #endif // VTK_MAJOR_VERSION
315  slices.append(slice);
316  count ++;
317  }
318  printDebug("Done adding");
319  checker->Update();
320 
321  printDebug("Setting up checkerboard actor");
322  vtkSmartPointer<vtkImageMapToWindowLevelColors> windowLevel = vtkSmartPointer<vtkImageMapToWindowLevelColors>::New();
323  #if VTK_MAJOR_VERSION <=5
324  windowLevel->SetInput(checker->GetOutput());
325  #else
326  windowLevel->SetInputData(checker->GetOutput());
327  #endif
328 
329  vtkSmartPointer<vtkImageActor> checkerActor = vtkSmartPointer<vtkImageActor>::New();
330 // checkerActor->GetMapper()->SetInputConnection(windowLevel->GetOutputPort());
331  #if VTK_MAJOR_VERSION <=5
332  checkerActor->SetInput(windowLevel->GetOutput());
333  #else
334  checkerActor->SetInputData(windowLevel->GetOutput());
335  #endif
336 
337  // VTK widgets consist of two parts: the widget part that handles
338  // event processing; and the widget representation that defines how
339  // the widget appears in the scene
340  // (i.e., matters pertaining to geometry).
341  printDebug("Setting up checkerboard widget");
342  checkerWidget = vtkSmartPointer<vtkCheckerboardWidget>::New();
343  checkerWidget->SetInteractor(milxQtRenderWindow::GetRenderWindow()->GetInteractor());
344 
345  vtkCheckerboardRepresentation *checkerWidgetRep = static_cast<vtkCheckerboardRepresentation *>(checkerWidget->GetRepresentation());
346  checkerWidgetRep->SetImageActor(checkerActor);
347  checkerWidgetRep->SetCheckerboard(checker);
348 
349  // Add the actors to the renderer, set the background and size
350  //
352  milxQtRenderWindow::AddActor(checkerActor);
353  checkerWidget->On();
355 }
356 
357 vtkSmartPointer<vtkFloatArray> milxQtUnifiedWindow::surfaceScalarsFromImage(vtkSmartPointer<vtkPolyData> surface, itk::SmartPointer<floatImageType> img, const bool absoluteValues)
358 {
359  const int numberOfPoints = surface->GetNumberOfPoints();
360 
361  typedef itk::Point<double, 3> InputImagePointType;
362  typedef itk::ContinuousIndex<double, 3 > ContinuousIndexType;
363 
364  typedef itk::LinearInterpolateImageFunction<floatImageType, double> InterpolatorType;
365 
366  InterpolatorType::Pointer interpolator = InterpolatorType::New();
367  interpolator->SetInputImage(img);
368 
369  vtkSmartPointer<vtkFloatArray> scalars = vtkSmartPointer<vtkFloatArray>::New();
370  scalars->SetName("Distance");
371  scalars->SetNumberOfTuples(numberOfPoints);
372  scalars->SetNumberOfComponents(1);
373 
374  InputImagePointType point;
375  ContinuousIndexType index;
376  for(int i = 0; i < numberOfPoints; i++)
377  {
378  double position[3];
379  surface->GetPoint(i, position);
380 
381  point[0] = position[0];
382  point[1] = position[1];
383  point[2] = position[2];
384 
385  img->TransformPhysicalPointToContinuousIndex(point, index);
386 
387  // If inside, mark
388  if(interpolator->IsInsideBuffer(index))
389  {
390 
391  double valueFound = 0.0;
392  if(absoluteValues)
393  valueFound = fabs(interpolator->EvaluateAtContinuousIndex(index));
394  else
395  valueFound = interpolator->EvaluateAtContinuousIndex(index);
396  scalars->SetTuple1(i, valueFound);
397  }
398  }
399 
400  return scalars;
401 }
402 
404 {
405  //Modes
406  unionAct = new QAction(this);
407  unionAct->setText(QApplication::translate("Unified", "&Union of Data", 0, QApplication::UnicodeUTF8));
408  unionAct->setShortcut(tr("Alt+u"));
409  unionAct->setCheckable(true);
410  unionAct->setChecked(true);
411  geoDifferenceAct = new QAction(this);
412  geoDifferenceAct->setText(QApplication::translate("Unified", "&Surface (Hausdoff) Distance", 0, QApplication::UnicodeUTF8));
413  geoDifferenceAct->setShortcut(tr("Alt+d"));
414  geoDifferenceAct->setCheckable(true);
415  scalarDifferenceAct = new QAction(this);
416  scalarDifferenceAct->setText(QApplication::translate("Unified", "&Scalar Difference", 0, QApplication::UnicodeUTF8));
417  scalarDifferenceAct->setShortcut(tr("Alt+s"));
418  scalarDifferenceAct->setCheckable(true);
419  scalarDifferenceAct->setDisabled(true);
420  checkerBoardAct = new QAction(this);
421  checkerBoardAct->setText(QApplication::translate("Unified", "&Checkerboard", 0, QApplication::UnicodeUTF8));
422  checkerBoardAct->setShortcut(tr("Alt+c"));
423  checkerBoardAct->setCheckable(true);
424  checkerBoardAct->setDisabled(true);
425  modeGroup = new QActionGroup(this); //Exclusive by default
426  modeGroup->addAction(unionAct);
427  modeGroup->addAction(geoDifferenceAct);
428  modeGroup->addAction(scalarDifferenceAct);
429  modeGroup->addAction(checkerBoardAct);
430 
431  //Refresh part of render window class
432 }
433 
435 {
436  //Modes
437  connect(unionAct, SIGNAL(triggered()), this, SLOT(generateUnion()));
438  connect(geoDifferenceAct, SIGNAL(triggered()), this, SLOT(generateDifference()));
439  connect(scalarDifferenceAct, SIGNAL(triggered()), this, SLOT(generateScalarDifference()));
440  connect(checkerBoardAct, SIGNAL(triggered()), this, SLOT(generateCheckerBoard()));
441 
442  connect(milxQtRenderWindow::refreshAct, SIGNAL(triggered()), this, SLOT(refresh()));
443 }
444 
445 void milxQtUnifiedWindow::contextMenuEvent(QContextMenuEvent *currentEvent)
446 {
447  contextMenu = new QMenu(this);
448 
449  if(unifyImages.isEmpty())
450  {
451  checkerBoardAct->setDisabled(true);
452  }
453  else
454  {
455  checkerBoardAct->setDisabled(false);
456  }
457  if(unifyModels.isEmpty())
458  {
459  geoDifferenceAct->setDisabled(true);
460  scalarDifferenceAct->setDisabled(true);
461  }
462  else
463  {
464  geoDifferenceAct->setDisabled(false);
465  scalarDifferenceAct->setDisabled(false);
466  }
467 
468  contextMenu->addAction(unionAct);
469  contextMenu->addAction(geoDifferenceAct);
470  contextMenu->addAction(scalarDifferenceAct);
471  contextMenu->addAction(checkerBoardAct);
472  contextMenu->addSeparator();
483 
484  contextMenu->exec(currentEvent->globalPos());
485 }
486 
487 void milxQtUnifiedWindow::setCommonProperties(QWidget *passedWindow)
488 {
489  connect(passedWindow, SIGNAL(closing(QWidget *)), this, SLOT(removeFromWindow(QWidget *)));
490  //passedWindow->setDeletableOnClose(true);
491 }
void printWarning(QString msg)
Warning message wrapper for console.
void printError(QString msg)
Error message wrapper for console.
void createConnections()
Create the connections for context menu etc.
void generateScalarDifference()
Generate a difference of the scalar data to display, i.e. show the net displacement vectors of the da...
QAction * checkerBoardAct
Checkerboard of image data.
void RemoveActor(vtkSmartPointer< vtkProp > actor)
Remove the VTK actor from this window.
void generateVoxelisedSurface(vtkSmartPointer< vtkPolyData > surfaceToVoxelise, double *bounds=NULL, double *spacing=NULL)
Converts or Voxelises a surface (vtkPolyData) to an image.
void refresh()
Refresh the display of the window.
QMenu * viewMenu
Context Menu.
void createActions()
Create the actions for context menu etc.
void setName(const QString filename)
Set the name of the data.
QActionGroup * modeGroup
Grouping for check boxes.
void generateUnion()
Generate a union of the data to display, i.e. show all data at once unaltered.
void AddActor(vtkSmartPointer< vtkProp > actor)
Add a VTK actor to this window.
This class represents the MILX Qt Render Window Display object using QVTK.
QAction * viewZY
Change view to zy-plane (Coronal)
This class represents the MILX Qt Image Display object using VTK.
Definition: milxQtImage.h:118
vtkSmartPointer< vtkFloatArray > surfaceScalarsFromImage(vtkSmartPointer< vtkPolyData > surface, itk::SmartPointer< floatImageType > img, const bool absoluteValues)
Function for extracting pixel values from image given surface points.
QMenu * contextMenu
Context Menu.
Definition: milxQtWindow.h:250
void contextMenuEvent(QContextMenuEvent *event)
The context menu setup member.
QList< milxQtModel *> unifyModels
Model to maintain and operate on.
QAction * axesAct
Action for axes of the display.
void modified(vtkSmartPointer< vtkImageActor >)
Emit signal to allow updating of image actors if present.
QAction * scalarDifferenceAct
Scalar Difference of data.
vtkImageActor * GetImageActor()
Returns the internal image actor used for display.
Definition: milxQtImage.h:442
QAction * unionAct
Union of data.
QString prefix
Prefix of the data.
Definition: milxQtWindow.h:242
void printDebug(QString msg)
Debug message wrapper for console.
QAction * geoDifferenceAct
Geometric Difference of data.
void addModelActor(vtkSmartPointer< vtkActor > mdlActor)
Directly add model actor to generic view.
This class represents the MILX Qt Model/Mesh Display object using VTK.
Definition: milxQtModel.h:115
void closing(QWidget *win)
Send signal that the window is closing.
QList< milxQtImage *> unifyImages
Images to maintain and operate on.
void addImageActor(vtkSmartPointer< vtkImageActor > imgActor, vtkMatrix4x4 *transformMatrix=NULL)
Directly add image actor to generic view.
QAction * refreshAct
Action for refreshing the display.
QList< vtkSmartPointer< vtkImageReslice > > slices
Images to maintain and operate on.
milxQtUnifiedWindow(QWidget *theParent=0)
Default constructor.
void addToWindow(milxQtModel *model)
Add this model window to the unified display.
vtkSmartPointer< vtkCheckerboardWidget > checkerWidget
Checker board widget for comparing images.
QMenu * colourMapMenu
Colour map menu.
void generateRender()
Generate the render so it is ready for display. Should be called before showing the window...
QAction * saveViewAct
Save camera view.
QAction * viewXY
Change view to xy-plane (Axial)
vtkSmartPointer< vtkImageData > GetOutput()
Returns the image data object (ImageData) used internally VTK style.
Definition: milxQtImage.h:384
void removeFromWindow(QWidget *passedWindow)
Remove this window from the unified display.
QString getName()
Returns the name of the data.
Definition: milxQtWindow.h:67
void removeImageActor(vtkSmartPointer< vtkImageActor > imgActor)
Directly remove image actor from generic view.
void RemoveAllActors()
Remove all VTK actors from this window.
void generateDifference(double pseudoInfinityFactor=-1.0)
Generate a difference of the geometric data to display, i.e. show the net displacement vectors of the...
QAction * viewZX
Change view to zx-plane (Sagittal)
void generateCheckerBoard()
Generate a checker board widget to compare images, i.e. show checkboards with the compared images...
void done(int value)
Send signal that computation is done. Value carries the progress,.
vtkSmartPointer< vtkMatrix4x4 > getTransformMatrix()
Internal transform matrix used to transform actors appropriately for direction and coordinate systems...
int * GetDisplayExtent()
Returns the current display extent of the image data, i.e the current slice dimensions/extents.
Definition: milxQtImage.h:503
virtual ~milxQtUnifiedWindow()
Default destructor.
void refresh()
Refresh the display of the model including widgets. Camera remains as is.
vtkSmartPointer< vtkLODActor > GetActor()
Returns the VTK actor used.
Definition: milxQtModel.h:251
void printInfo(QString msg)
Info message wrapper for console.
void working(int value)
Send signal that computation is in progress. Value carries the progress,.
virtual void updateImageActor(vtkObject *obj, unsigned long, void *client_data, void *, vtkCommand *command)
Update any image actors in display to current slice when slice number changes.
QAction * loadViewAct
Load camera view.
void removeModelActor(vtkSmartPointer< vtkActor > mdlActor)
Directly remove model actor from generic view.