Medical Imaging Interaction Toolkit  2016.11.0
Medical Imaging Interaction Toolkit
All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator Properties Friends Macros Modules Pages
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.