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