Medical Imaging Interaction Toolkit  2016.11.0
Medical Imaging Interaction Toolkit
mitkVectorImageMapper2D.cpp
Go to the documentation of this file.
1 /*===================================================================
2 
3 The Medical Imaging Interaction Toolkit (MITK)
4 
5 Copyright (c) German Cancer Research Center,
6 Division of Medical and Biological Informatics.
7 All rights reserved.
8 
9 This software is distributed WITHOUT ANY WARRANTY; without
10 even the implied warranty of MERCHANTABILITY or FITNESS FOR
11 A PARTICULAR PURPOSE.
12 
13 See LICENSE.txt or http://www.mitk.org for details.
14 
15 ===================================================================*/
16 
18 
19 // vtk related includes
20 #include <vtkCellArray.h>
21 #include <vtkCellData.h>
22 #include <vtkCutter.h>
23 #include <vtkDataArray.h>
24 #include <vtkDataObject.h>
25 #include <vtkDataSetWriter.h>
26 #include <vtkFloatArray.h>
27 #include <vtkGlyph2D.h>
28 #include <vtkGlyphSource2D.h>
29 #include <vtkImageData.h>
30 #include <vtkImageReslice.h>
31 #include <vtkIndent.h>
32 #include <vtkLinearTransform.h>
33 #include <vtkLookupTable.h>
34 #include <vtkLookupTable.h>
35 #include <vtkMaskedGlyph2D.h>
36 #include <vtkMaskedGlyph3D.h>
37 #include <vtkMath.h>
38 #include <vtkMatrix4x4.h>
39 #include <vtkMatrixToLinearTransform.h>
40 #include <vtkPlane.h>
41 #include <vtkPlane.h>
42 #include <vtkPointData.h>
43 #include <vtkPoints.h>
44 #include <vtkPolyData.h>
45 #include <vtkPolyData.h>
46 #include <vtkPolyDataMapper.h>
47 #include <vtkScalarsToColors.h>
48 #include <vtkScalarsToColors.h>
49 #include <vtkTransform.h>
50 
51 #include <fstream>
52 
53 // mitk related includes
55 #include "mitkBaseRenderer.h"
56 #include "mitkColorProperty.h"
57 #include "mitkGL.h"
58 #include "mitkProperties.h"
60 
62 {
63  if (m_Image.IsNotNull())
64  return m_Image;
65  else
66  return dynamic_cast<const mitk::Image *>(GetDataNode()->GetData());
67 }
68 
70 {
71  // std::cout << "2d vector mapping..." << std::endl;
72 
73  bool visible = true;
74  GetDataNode()->GetVisibility(visible, renderer, "visible");
75 
76  if (!visible)
77  return;
78 
79  mitk::Image::Pointer input = const_cast<mitk::Image *>(this->GetInput());
80 
81  if (input.IsNull())
82  return;
83 
84  vtkImageData *vtkImage = input->GetVtkImageData(this->GetCurrentTimeStep(input, renderer));
85 
86  //
87  // set up the cutter orientation according to the current geometry of
88  // the renderers plane
89  //
90  Point3D point;
91  Vector3D normal;
92  PlaneGeometry::ConstPointer worldPlaneGeometry = renderer->GetCurrentWorldPlaneGeometry();
93 
94  if (worldPlaneGeometry.IsNotNull())
95  {
96  // set up vtkPlane according to worldGeometry
97  point = worldPlaneGeometry->GetOrigin();
98  normal = worldPlaneGeometry->GetNormal();
99  normal.Normalize();
100  m_Plane->SetTransform((vtkAbstractTransform *)NULL);
101  }
102  else
103  {
104  itkWarningMacro(<< "worldPlaneGeometry is NULL!");
105  return;
106  }
107 
108  double vp[3], vp_slice[3], vnormal[3];
109  vnl2vtk(point.GetVnlVector(), vp);
110  vnl2vtk(normal.GetVnlVector(), vnormal);
111  // std::cout << "Origin: " << vp[0] <<" "<< vp[1] <<" "<< vp[2] << std::endl;
112  // std::cout << "Normal: " << vnormal[0] <<" "<< vnormal[1] <<" "<< vnormal[2] << std::endl;
113 
114  // normally, we would need to transform the surface and cut the transformed surface with the cutter.
115  // This might be quite slow. Thus, the idea is, to perform an inverse transform of the plane instead.
116  //@todo It probably does not work for scaling operations yet:scaling operations have to be
117  // dealed with after the cut is performed by scaling the contour.
118  vtkLinearTransform *vtktransform = GetDataNode()->GetVtkTransform();
119 
120  vtkTransform *world2vtk = vtkTransform::New();
121  world2vtk->Identity();
122  world2vtk->Concatenate(vtktransform->GetLinearInverse());
123  double myscale[3];
124  world2vtk->GetScale(myscale);
125  world2vtk->PostMultiply();
126  world2vtk->Scale(1 / myscale[0], 1 / myscale[1], 1 / myscale[2]);
127  world2vtk->TransformPoint(vp, vp);
128  world2vtk->TransformNormalAtPoint(vp, vnormal, vnormal);
129  world2vtk->Delete();
130 
131  // vtk works in axis align coords
132  // thus the normal also must be axis align, since
133  // we do not allow arbitrary cutting through volume
134  //
135  // vnormal should already be axis align, but in order
136  // to get rid of precision effects, we set the two smaller
137  // components to zero here
138  int dims[3];
139  vtkImage->GetDimensions(dims);
140  double spac[3];
141  vtkImage->GetSpacing(spac);
142  vp_slice[0] = vp[0];
143  vp_slice[1] = vp[1];
144  vp_slice[2] = vp[2];
145  if (fabs(vnormal[0]) > fabs(vnormal[1]) && fabs(vnormal[0]) > fabs(vnormal[2]))
146  {
147  if (fabs(vp_slice[0] / spac[0]) < 0.4)
148  vp_slice[0] = 0.4 * spac[0];
149  if (fabs(vp_slice[0] / spac[0]) > (dims[0] - 1) - 0.4)
150  vp_slice[0] = ((dims[0] - 1) - 0.4) * spac[0];
151  vnormal[1] = 0;
152  vnormal[2] = 0;
153  }
154 
155  if (fabs(vnormal[1]) > fabs(vnormal[0]) && fabs(vnormal[1]) > fabs(vnormal[2]))
156  {
157  if (fabs(vp_slice[1] / spac[1]) < 0.4)
158  vp_slice[1] = 0.4 * spac[1];
159  if (fabs(vp_slice[1] / spac[1]) > (dims[1] - 1) - 0.4)
160  vp_slice[1] = ((dims[1] - 1) - 0.4) * spac[1];
161  vnormal[0] = 0;
162  vnormal[2] = 0;
163  }
164 
165  if (fabs(vnormal[2]) > fabs(vnormal[1]) && fabs(vnormal[2]) > fabs(vnormal[0]))
166  {
167  if (fabs(vp_slice[2] / spac[2]) < 0.4)
168  vp_slice[2] = 0.4 * spac[2];
169  if (fabs(vp_slice[2] / spac[2]) > (dims[2] - 1) - 0.4)
170  vp_slice[2] = ((dims[2] - 1) - 0.4) * spac[2];
171  vnormal[0] = 0;
172  vnormal[1] = 0;
173  }
174 
175  m_Plane->SetOrigin(vp_slice);
176  m_Plane->SetNormal(vnormal);
177 
178  vtkPolyData *cuttedPlane;
179  if (!((dims[0] == 1 && vnormal[0] != 0) || (dims[1] == 1 && vnormal[1] != 0) || (dims[2] == 1 && vnormal[2] != 0)))
180  {
181  m_Cutter->SetCutFunction(m_Plane);
182  m_Cutter->SetInputData(vtkImage);
183  m_Cutter->GenerateCutScalarsOff();
184  m_Cutter->Update();
185  cuttedPlane = m_Cutter->GetOutput();
186  }
187  else
188  {
189  // cutting of a 2D-Volume does not work,
190  // so we have to build up our own polydata object
191  cuttedPlane = vtkPolyData::New();
192  vtkPoints *points = vtkPoints::New();
193  points->SetNumberOfPoints(vtkImage->GetNumberOfPoints());
194  for (int i = 0; i < vtkImage->GetNumberOfPoints(); i++)
195  points->SetPoint(i, vtkImage->GetPoint(i));
196  cuttedPlane->SetPoints(points);
197  vtkFloatArray *pointdata = vtkFloatArray::New();
198  int comps = vtkImage->GetPointData()->GetScalars()->GetNumberOfComponents();
199  pointdata->SetNumberOfComponents(comps);
200  int tuples = vtkImage->GetPointData()->GetScalars()->GetNumberOfTuples();
201  pointdata->SetNumberOfTuples(tuples);
202  for (int i = 0; i < tuples; i++)
203  pointdata->SetTuple(i, vtkImage->GetPointData()->GetScalars()->GetTuple(i));
204  pointdata->SetName("vector");
205  cuttedPlane->GetPointData()->AddArray(pointdata);
206  }
207 
208  if (cuttedPlane->GetNumberOfPoints() != 0)
209  {
210  //
211  // make sure, that we have point data with more than 1 component (as vectors)
212  //
213  vtkPointData *pointData = cuttedPlane->GetPointData();
214  if (pointData == NULL)
215  {
216  itkWarningMacro(<< "no point data associated with cutters result!");
217  return;
218  }
219  if (pointData->GetNumberOfArrays() == 0)
220  {
221  itkWarningMacro(<< "point data returned by cutter doesn't have any arrays associated!");
222  return;
223  }
224  else if (pointData->GetArray(0)->GetNumberOfComponents() <= 1)
225  {
226  itkWarningMacro(<< "number of components <= 1!");
227  return;
228  }
229  else if (pointData->GetArrayName(0) == NULL)
230  {
231  pointData->GetArray(0)->SetName("vector");
232  // std::cout << "array name = vectors now" << std::endl;
233  }
234  // std::cout << " projecting..."<< std::endl;
235 
236  //
237  // constrain the vectors to lie on the plane, which means to remove the vector component,
238  // which is orthogonal to the plane.
239  //
240  vtkIdType numPoints, pointId;
241  numPoints = cuttedPlane->GetNumberOfPoints();
242  vtkDataArray *inVectors = cuttedPlane->GetPointData()->GetVectors("vector");
243  assert(inVectors != NULL);
244  vtkFloatArray *vectorMagnitudes = vtkFloatArray::New();
245  vectorMagnitudes->SetName("vectorMagnitudes");
246  vectorMagnitudes->SetNumberOfComponents(1);
247  vectorMagnitudes->SetNumberOfValues(numPoints);
248  vectorMagnitudes->SetNumberOfTuples(numPoints);
249  double inVector[3], outVector[3], wnormal[3]; //, tmpVector[ 3 ], outVector[ 3 ];
250  double k = 0.0;
251  vnl2vtk(normal.GetVnlVector(), wnormal);
252  vtkMath::Normalize(wnormal);
253  bool normalizeVecs;
254  m_DataNode->GetBoolProperty("NormalizeVecs", normalizeVecs);
255  for (pointId = 0; pointId < numPoints; ++pointId)
256  {
257  inVectors->GetTuple(pointId, inVector);
258  if (normalizeVecs)
259  {
260  vnl_vector<double> tmp(3);
261  vtk2vnl(inVector, tmp);
262  tmp.normalize();
263  vnl2vtk(tmp, inVector);
264  }
265  k = vtkMath::Dot(wnormal, inVector);
266  // Remove non orthogonal component.
267  outVector[0] = inVector[0] - (wnormal[0] * k);
268  outVector[1] = inVector[1] - (wnormal[1] * k);
269  outVector[2] = inVector[2] - (wnormal[2] * k);
270  inVectors->SetTuple(pointId, outVector);
271 
272  // ?? this was set to norm(inVector) before, but outVector made more sense to me
273  vectorMagnitudes->SetValue(pointId, vtkMath::Norm(outVector));
274 
275  // std::cout << "method old: " << inVector[0] <<", " << inVector[1] << ", "<<inVector[2] << ", method new: " <<
276  // outVector[0] << ", "<< outVector[1] << ", "<< outVector[2] << std::endl;
277  }
278  pointData->AddArray(vectorMagnitudes);
279  pointData->CopyAllOn();
280 
281  // pointData->PrintSelf(std::cout, vtkIndent(4));
282  // std::cout << " ...done!"<< std::endl;
283  // std::cout << " glyphing..."<< std::endl;
284 
285  // call glyph2D to generate 2D glyphs for each of the
286  // vectors
287  vtkGlyphSource2D *glyphSource = vtkGlyphSource2D::New();
288  // glyphSource->SetGlyphTypeToDash();
289  glyphSource->DashOn();
290  // glyphSource->SetScale( 0.1 );
291  // glyphSource->SetScale2( .5 );
292  // glyphSource->SetCenter( 0.5, 0.5, 0.5 );
293  glyphSource->CrossOff();
294  // glyphSource->FilledOff();
295  // glyphSource->Update();
296 
297  double spacing[3];
298  vtkImage->GetSpacing(spacing);
299  double min = spacing[0];
300  min = min > spacing[1] ? spacing[1] : min;
301  min = min > spacing[2] ? spacing[2] : min;
302 
303  float scale = 1;
304  mitk::FloatProperty::Pointer mitkScaleProp =
305  dynamic_cast<mitk::FloatProperty *>(GetDataNode()->GetProperty("Scale"));
306  if (mitkScaleProp.IsNotNull())
307  {
308  scale = mitkScaleProp->GetValue();
309  }
310 
311  vtkMaskedGlyph3D *glyphGenerator = vtkMaskedGlyph3D::New();
312  glyphGenerator->SetSourceData(glyphSource->GetOutput());
313  glyphGenerator->SetInput(cuttedPlane);
314  glyphGenerator->SetInputArrayToProcess(1, 0, 0, vtkDataObject::FIELD_ASSOCIATION_POINTS, "vector");
315  glyphGenerator->SetVectorModeToUseVector();
316  glyphGenerator->OrientOn();
317  glyphGenerator->SetScaleFactor(min * scale);
318  glyphGenerator->SetUseMaskPoints(true);
319  glyphGenerator->SetRandomMode(true);
320  glyphGenerator->SetMaximumNumberOfPoints(128 * 128);
321 
322  glyphGenerator->Update();
323 
324  /*
325  vtkLookupTable* vtkLut = NULL;
326  mitk::LookupTableProperty::Pointer mitkLutProp =
327  dynamic_cast<mitk::LookupTableProperty*>(GetDataNode()->GetProperty("LookupTable"));
328  if (mitkLutProp.IsNotNull())
329  {
330  vtkLut = mitkLutProp->GetLookupTable()->GetVtkLookupTable();
331  }
332  */
333 
334  mitk::Color color;
335  mitk::ColorProperty::Pointer mitkColorProp =
336  dynamic_cast<mitk::ColorProperty *>(GetDataNode()->GetProperty("color"));
337  if (mitkColorProp.IsNotNull())
338  {
339  color = mitkColorProp->GetColor();
340  }
341  else
342  {
343  color.SetRed(0);
344  color.SetBlue(1);
345  color.SetGreen(0);
346  }
347 
348  float lwidth = 1;
349  mitk::FloatProperty::Pointer mitkLWidthProp =
350  dynamic_cast<mitk::FloatProperty *>(GetDataNode()->GetProperty("LineWidth"));
351  if (mitkLWidthProp.IsNotNull())
352  {
353  lwidth = mitkLWidthProp->GetValue();
354  }
355 
356  vtkTransform *trafo = vtkTransform::New();
357  trafo->Identity();
358  trafo->Concatenate(vtktransform);
359  trafo->PreMultiply();
360  double myscale[3];
361  trafo->GetScale(myscale);
362  trafo->Scale(1 / myscale[0], 1 / myscale[1], 1 / myscale[2]);
363 
364  this->PaintCells(glyphGenerator->GetOutput(),
365  renderer->GetCurrentWorldPlaneGeometry(),
366  trafo,
367  renderer,
368  NULL /*vtkLut*/,
369  color,
370  lwidth,
371  spacing);
372 
373  vectorMagnitudes->Delete();
374  glyphSource->Delete();
375  glyphGenerator->Delete();
376  trafo->Delete();
377  }
378  else
379  {
380  std::cout << " no points cutted!" << std::endl;
381  }
382  // std::cout << "...done!" << std::endl;
383 }
384 
385 void mitk::VectorImageMapper2D::PaintCells(vtkPolyData *glyphs,
386  const PlaneGeometry * /*worldGeometry*/,
387  vtkLinearTransform *vtktransform,
388  mitk::BaseRenderer *renderer,
389  vtkScalarsToColors *lut,
390  mitk::Color color,
391  float lwidth,
392  double *spacing)
393 {
394  vtkPoints *points = glyphs->GetPoints();
395  vtkPointData *vpointdata = glyphs->GetPointData();
396  vtkDataArray *vpointscalars = vpointdata->GetArray("vectorMagnitudes");
397  // vtkDataArray* vpointpositions = vpointdata->GetArray("pointPositions");
398  assert(vpointscalars != NULL);
399  // std::cout << " Scalars range 2d:" << vpointscalars->GetRange()[0] << " " << vpointscalars->GetRange()[0] <<
400  // std::endl;
401 
402  Point3D p;
403  Point2D p2d;
404  vtkIdList *idList;
405  vtkCell *cell;
406 
407  double offset[3];
408  for (auto &elem : offset)
409  {
410  elem = 0;
411  }
412 
413  vtkIdType numCells = glyphs->GetNumberOfCells();
414  for (vtkIdType cellId = 0; cellId < numCells; ++cellId)
415  {
416  double vp[3];
417 
418  cell = glyphs->GetCell(cellId);
419  idList = cell->GetPointIds();
420 
421  int numPoints = idList->GetNumberOfIds();
422 
423  if (numPoints == 1)
424  {
425  // take transformation via vtktransform into account
426  double pos[3], vp_raster[3];
427  points->GetPoint(idList->GetId(0), vp);
428  vp_raster[0] = vtkMath::Round(vp[0] / spacing[0]) * spacing[0];
429  vp_raster[1] = vtkMath::Round(vp[1] / spacing[1]) * spacing[1];
430  vp_raster[2] = vtkMath::Round(vp[2] / spacing[2]) * spacing[2];
431  vtktransform->TransformPoint(vp_raster, pos);
432  offset[0] = pos[0] - vp[0];
433  offset[1] = pos[1] - vp[1];
434  offset[2] = pos[2] - vp[2];
435  }
436  else
437  {
438  glLineWidth(lwidth);
439  glBegin(GL_LINE_LOOP);
440 
441  for (int pointNr = 0; pointNr < numPoints; ++pointNr)
442  {
443  points->GetPoint(idList->GetId(pointNr), vp);
444 
445  vp[0] = vp[0] + offset[0];
446  vp[1] = vp[1] + offset[1];
447  vp[2] = vp[2] + offset[2];
448 
449  double tmp[3];
450  vtktransform->TransformPoint(vp, tmp);
451 
452  vtk2itk(vp, p);
453 
454  // convert 3D point (in mm) to display coordinates (units )
455  renderer->WorldToDisplay(p, p2d);
456 
457  if (lut != NULL)
458  {
459  // color each point according to point data
460  double *color;
461 
462  if (vpointscalars != NULL)
463  {
464  vpointscalars->GetComponent(pointNr, 0);
465  color = lut->GetColor(vpointscalars->GetComponent(idList->GetId(pointNr), 0));
466  glColor3f(color[0], color[1], color[2]);
467  }
468  }
469  else
470  {
471  glColor3f(color.GetRed(), color.GetGreen(), color.GetBlue());
472  }
473 
474  // std::cout << idList->GetId( pointNr )<< ": " << p2d[0]<< " "<< p2d[1] << std::endl;
475  // draw the line
476  glVertex2f(p2d[0], p2d[1]);
477  }
478  glEnd();
479  }
480  }
481 }
482 
484 {
485  m_LUT = NULL;
486  m_Plane = vtkPlane::New();
487  m_Cutter = vtkCutter::New();
488 
489  m_Cutter->SetCutFunction(m_Plane);
490  m_Cutter->GenerateValues(1, 0, 1);
491 }
492 
494 {
495  if (m_LUT != NULL)
496  m_LUT->Delete();
497  if (m_Plane != NULL)
498  m_Plane->Delete();
499  if (m_Cutter != NULL)
500  m_Cutter->Delete();
501 }
502 
504 {
505  //
506  // get the TimeGeometry of the input object
507  //
508  const TimeGeometry *dataTimeGeometry = data->GetUpdatedTimeGeometry();
509  if ((dataTimeGeometry == NULL) || (dataTimeGeometry->CountTimeSteps() == 0))
510  {
511  itkWarningMacro(<< "The given object is missing a mitk::TimeGeometry, or the number of time steps is 0!");
512  return 0;
513  }
514 
515  //
516  // get the world time
517  //
518  ScalarType time = renderer->GetTime();
519  //
520  // convert the world time to time steps of the input object
521  //
522  int timestep = 0;
523  if (time > itk::NumericTraits<mitk::ScalarType>::NonpositiveMin())
524  timestep = dataTimeGeometry->TimePointToTimeStep(time);
525  if (dataTimeGeometry->IsValidTimeStep(timestep) == false)
526  {
527  itkWarningMacro(<< timestep << " is not a valid time of the given data object!");
528  return 0;
529  }
530  return timestep;
531 }
Base of all data objects.
Definition: mitkBaseData.h:39
double ScalarType
mitk::Image::ConstPointer m_Image
Organizes the rendering process.
void vtk2vnl(const Tin *in, vnl_vector< Tout > &out)
static vtkMaskedGlyph3D * New()
ScalarType GetTime() const
Get the time in ms of the currently displayed content.
virtual void PaintCells(vtkPolyData *contour, const PlaneGeometry *, vtkLinearTransform *vtktransform, BaseRenderer *renderer, vtkScalarsToColors *lut, mitk::Color color, float lwidth, double *spacing)
virtual const PlaneGeometry * GetCurrentWorldPlaneGeometry()
Get the current 2D-worldgeometry (m_CurrentWorldPlaneGeometry) used for 2D-rendering.
BaseData * GetData() const
Get the data object (instance of BaseData, e.g., an Image) managed by this DataNode.
virtual void Paint(mitk::BaseRenderer *renderer) override
Do the painting into the renderer.
static Vector3D offset
The ColorProperty class RGB color property.
void SetRandomMode(int mode)
virtual void SetInput(vtkDataSet *input)
void WorldToDisplay(const Point3D &worldIndex, Point2D &displayPoint) const
This method converts a 3D world index to the display point using the geometry of the renderWindow...
void vnl2vtk(const vnl_vector< Tin > &in, Tout *out)
const mitk::Image * GetInput(void)
virtual TimeStepType TimePointToTimeStep(TimePointType timePoint) const =0
Converts a time point to the corresponding time step.
Image class for storing images.
Definition: mitkImage.h:76
void vtk2itk(const Tin &in, Tout &out)
itk::RGBPixel< float > Color
Color Standard RGB color typedef (float)
static T min(T x, T y)
Definition: svm.cpp:67
void Normalize(itk::Image< TPixel, VImageDimension > *itkImage, mitk::Image::Pointer im2, mitk::Image::Pointer mask1, std::string output)
Definition: CLBrainMask.cpp:40
const mitk::TimeGeometry * GetUpdatedTimeGeometry()
Return the TimeGeometry of the data.
int GetCurrentTimeStep(mitk::BaseData *data, mitk::BaseRenderer *renderer)
Describes a two-dimensional, rectangular plane.
virtual DataNode * GetDataNode() const
Get the DataNode containing the data to map. Method only returns valid DataNode Pointer if the mapper...
Definition: mitkMapper.cpp:36
virtual bool IsValidTimeStep(TimeStepType timeStep) const =0
Test for the given time step if a geometry is availible.
static itkEventMacro(BoundingShapeInteractionEvent, itk::AnyEvent) class MITKBOUNDINGSHAPE_EXPORT BoundingShapeInteractor Pointer New()
Basic interaction methods for mitk::GeometryData.