CustusX  2023.01.05-dev+develop.0da12
An IGT application
cxVideoGraphics.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 
13 #include "cxVideoGraphics.h"
14 
15 #include <vtkActor.h>
16 #include <vtkImageData.h>
17 #include <vtkPlaneSource.h>
18 #include <vtkTransformTextureCoords.h>
19 #include <vtkTextureMapToPlane.h>
20 #include <vtkDataSetMapper.h>
21 #include <vtkTexture.h>
22 #include <vtkProperty.h>
23 #include <vtkImageMask.h>
24 #include <vtkPointData.h>
25 #include <vtkMatrix4x4.h>
26 #include <vtkLookupTable.h>
27 #include <vtkImageThreshold.h>
28 #include <vtkImageChangeInformation.h>
29 #include <vtkExtractVOI.h>
30 
32 #include "cxBoundingBox3D.h"
33 #include "cxLogger.h"
34 
35 namespace cx
36 {
37 
39 {
40  mPlaneActor = vtkActorPtr::New();
41  mPlaneSource = vtkPlaneSourcePtr::New();
42 
43  mDataRedirecter = vtkImageChangeInformationPtr::New();
44  mUSSource = UltrasoundSectorSourcePtr::New();
45 
46  // set a filter that map all zeros in the input to ones. This enables us to
47  // use zero as a special transparency value, to be used in masking.
48  mMapZeroToOne = vtkImageThresholdPtr::New();
49  mMapZeroToOne->ThresholdByLower(1.0);
50  mMapZeroToOne->SetInValue(1);
51  mMapZeroToOne->SetReplaceIn(true);
52 
53  // set the filter that applies a mask to the stream data
54  mMaskFilter = vtkImageMaskPtr::New();
55  mMaskFilter->SetMaskInputData(vtkImageDataPtr());
56  mMaskFilter->SetMaskedOutputValue(0.0);
57 
58  // generate texture coords for mPlaneSource
59  mTextureMapToPlane = vtkTextureMapToPlanePtr::New();
60 
61  mTransformTextureCoords = vtkTransformTextureCoordsPtr::New();
62  mTransformTextureCoords->SetOrigin( 0, 0.5, 0);
63  mTransformTextureCoords->SetScale( 1, 1, 0);
64  mTransformTextureCoords->FlipROn(); // flip around axis
65 
66  mTexture = vtkTexturePtr::New();
67  mTexture->RepeatOff();
68 
69  mDataSetMapper = vtkDataSetMapperPtr::New();
70 
71  mPlaneActor->SetTexture(mTexture);
72  mPlaneActor->SetMapper(mDataSetMapper);
73  mPlaneActor->SetVisibility(false);
74  mPlaneActor->GetProperty()->LightingOff(); // Turning off lighting to remove shadow effects (Fix for #644: 2D ultrasound in 3D scene was too dark)
75 // mPlaneActor->GetProperty()->ShadingOff();
76 }
77 
79 {
80 }
81 
83 {
84  mTransformTextureCoords->SetFlipR(on); // flip around axis
85 }
86 
88 {
89  return mTransformTextureCoords->GetFlipR();
90 }
91 
93 {
94  return mPlaneActor;
95 }
96 
100 void VideoGraphics::setupPipeline()
101 {
102  if (!mInputVideo)
103  {
104  mTexture->SetInputData(NULL);
105  return;
106  }
107 
108  if (mInputMask)
109  {
110  mTextureMapToPlane->SetInputConnection(mPlaneSource->GetOutputPort());
111  mTransformTextureCoords->SetInputConnection(mTextureMapToPlane->GetOutputPort() );
112  mDataSetMapper->SetInputConnection(mTransformTextureCoords->GetOutputPort() );
113 
114  mMaskFilter->SetMaskInputData(mInputMask);
115  mMapZeroToOne->SetInputConnection(mDataRedirecter->GetOutputPort());
116  mMaskFilter->SetInputConnection(0, mMapZeroToOne->GetOutputPort());
117  mTexture->SetInputConnection(mMaskFilter->GetOutputPort());
118  }
119  else if (mInputSector)
120  {
121  mUSSource->setProbeSector(mInputSector);
122  mTransformTextureCoords->SetInputConnection(mUSSource->GetOutputPort() );
123  mDataSetMapper->SetInputConnection(mTransformTextureCoords->GetOutputPort() );
124 
125  mTexture->SetInputConnection(mDataRedirecter->GetOutputPort());
126  }
127  else
128  {
129  mTextureMapToPlane->SetInputConnection(mPlaneSource->GetOutputPort());
130  mTransformTextureCoords->SetInputConnection(mTextureMapToPlane->GetOutputPort() );
131  mDataSetMapper->SetInputConnection(mTransformTextureCoords->GetOutputPort() );
132 
133  mTexture->SetInputConnection(mDataRedirecter->GetOutputPort());
134  }
135 
136  this->setLookupTable();
137  mPlaneActor->SetTexture(mTexture);
138  mPlaneActor->SetMapper(mDataSetMapper);
139 }
140 
142 {
143  if (mInputMask==mask)
144  return;
145  mInputSector = NULL;
146  mInputMask = mask;
147  this->setupPipeline();
148 }
149 
151 {
152  if (mInputSector==sector)
153  return;
154  mInputMask = NULL;
155  mInputSector = sector;
156  this->setupPipeline();
157 }
158 
160 {
161  if (mInputVideo==video)
162  return;
163  mInputVideo = video;
164  this->setupPipeline();
165 }
166 
168 {
169  mPlaneActor->SetUserMatrix(rMu);
170 }
171 
173 {
174  mPlaneActor->SetVisibility(visible);
175  mPlaneActor->Modified();
176 }
177 
179 {
180  if (this->inputImageIsEmpty())
181  {
182  this->setVisibility(false);
183  return;
184  }
185 
186  this->connectVideoImageToPipeline();
187  this->updateLUT();
188  this->updatePlaneSourceBounds();
189 
190  mPlaneActor->Modified();
191 }
192 
193 void VideoGraphics::connectVideoImageToPipeline()
194 {
195  if (mInputVideo == NULL)
196  {
197  mTexture->SetInputData(NULL); // TODO trouble - will destroy the pipeline
198  return;
199  }
200 
201  //Check if 3D volume. If so, only use middle frame
202  int* extent = mInputVideo->GetExtent();
203  if(extent[5] - extent[4] > 0)
204  {
205  int slice = floor(extent[4]+0.5f*(extent[5]-extent[4]));
206  if (slice < 0) slice = 0;
207 // std::cout << "Got 3D volume, showing middle slice: " << slice << std::endl;
208  vtkSmartPointer<vtkExtractVOI> extractVOI = vtkSmartPointer<vtkExtractVOI>::New();
209  extractVOI->SetInputData(mInputVideo);
210  extractVOI->SetVOI(extent[0], extent[1], extent[2], extent[3], slice, slice);
211  extractVOI->Update();
212  mDataRedirecter->SetInputConnection(extractVOI->GetOutputPort());
213  }
214  else //2D
215  {
216  mDataRedirecter->SetInputData(mInputVideo);
217  }
218 
219  mDataRedirecter->UpdateWholeExtent(); // important! syncs update extent to whole extent
220  mDataRedirecter->Update();
221 // mDataRedirecter->GetOutput()->Update(); //???
222 }
223 
224 void VideoGraphics::updatePlaneSourceBounds()
225 {
226  // set the planesource where we have no ProbeDefinition.
227  // TODO dont do this when planesource is not part of pipeline.
228  DoubleBoundingBox3D bounds(mDataRedirecter->GetOutput()->GetBounds());
229  if (!similar(bounds.range()[0], 0.0) || !similar(bounds.range()[1], 0.0))
230  {
231  mPlaneSource->SetOrigin(bounds.corner(0,0,0).begin());
232  mPlaneSource->SetPoint1(bounds.corner(1,0,0).begin());
233  mPlaneSource->SetPoint2(bounds.corner(0,1,0).begin());
234  mPlaneSource->GetOutput()->GetPointData()->Modified();
235  mPlaneSource->GetOutput()->Modified();
236  }
237 }
238 
239 void VideoGraphics::updateLUT()
240 {
241  this->setLookupTable();
242  mTexture->SetLookupTable(mLUT);
243 
244  // apply a lut only if the input data is monochrome
245  int numComp = mDataRedirecter->GetOutput()->GetNumberOfScalarComponents();
246  bool is8bit = mDataRedirecter->GetOutput()->GetScalarType()==VTK_UNSIGNED_CHAR;
247  if (numComp==1)
248  {
249  double srange[2];
250  if (is8bit)
251  {
252  srange[0] = 0;
253  srange[1] = 255;
254  }
255  else
256  {
257  mDataRedirecter->GetOutput()->GetScalarRange(srange);
258  }
259 
260  mTexture->GetLookupTable()->SetRange(srange[0], srange[1]);
261  mTexture->MapColorScalarsThroughLookupTableOn();
262  }
263  else
264  {
265  mTexture->MapColorScalarsThroughLookupTableOff();
266  }
267 }
268 
271 void VideoGraphics::setLookupTable()
272 {
273  // applies only to mask:
274  // Create a lut of size at least equal to the data range. Set the tableRange[0] to zero.
275  // This will force input zero to be mapped onto the first table value (the transparent one),
276  // and inputs [1, -> > is mapped to larger values, not transparent.
277  // In order to create a window-level function, manually build a table.
278 
279  //make a default system set lookuptable, grayscale...
280  vtkLookupTablePtr lut = vtkLookupTablePtr::New();
281  lut->SetNumberOfTableValues(1000); // large enough to give resolution even for ct images.
282  //lut->SetTableRange (0, 1024); // the window of the input
283  lut->SetTableRange (0, 255); // the window of the input - must be reset according to data
284  lut->SetSaturationRange (0, 0);
285  lut->SetHueRange (0, 0);
286  lut->SetValueRange (0, 1);
287  lut->SetRampToLinear();
288  lut->Build();
289 
290  if (mInputMask)
291  {
292  lut->SetTableValue(0, 0, 0, 0, 0); // set the lowest value to transparent. This will make the masked values transparent, but nothing else
293  }
294 
295  lut->Modified();
296  mLUT = lut;
297 }
298 
299 bool VideoGraphics::inputImageIsEmpty()
300 {
301  if (mInputVideo == NULL)
302  return true;
303 // mInputVideo->Update();
304  //Don't do anything if we get an empty image
305  int* dim = mInputVideo->GetDimensions();
306  if(dim[0] == 0 || dim[1] == 0)
307  return true;
308 
309  return false;
310 }
311 
312 } // namespace cx
vtkSmartPointer< class vtkMatrix4x4 > vtkMatrix4x4Ptr
Definition: cxMathBase.h:37
vtkSmartPointer< class vtkActor > vtkActorPtr
void setActorUserMatrix(vtkMatrix4x4Ptr rMu)
void setClip(vtkPolyDataPtr sector)
void setFlipVertical(bool on)
void setVisibility(bool visible)
vtkSmartPointer< class vtkLookupTable > vtkLookupTablePtr
Representation of a floating-point bounding box in 3D. The data are stored as {xmin,xmax,ymin,ymax,zmin,zmax}, in order to simplify communication with vtk.
vtkSmartPointer< vtkPolyData > vtkPolyDataPtr
bool similar(const CameraInfo &lhs, const CameraInfo &rhs, double tol)
vtkActorPtr getActor()
void setInputVideo(vtkImageDataPtr video)
void setMask(vtkImageDataPtr mask)
vtkSmartPointer< class vtkImageData > vtkImageDataPtr
Namespace for all CustusX production code.