CustusX  16.12
An IGT application
 All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator Friends Macros Groups Pages
cxTexture3DSlicerProxy.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) 2008-2014, SINTEF Department of Medical Technology
5 All rights reserved.
6 
7 Redistribution and use in source and binary forms, with or without
8 modification, are permitted provided that the following conditions are met:
9 
10 1. Redistributions of source code must retain the above copyright notice,
11  this list of conditions and the following disclaimer.
12 
13 2. Redistributions in binary form must reproduce the above copyright notice,
14  this list of conditions and the following disclaimer in the documentation
15  and/or other materials provided with the distribution.
16 
17 3. Neither the name of the copyright holder nor the names of its contributors
18  may be used to endorse or promote products derived from this software
19  without specific prior written permission.
20 
21 THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
22 AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
23 IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
24 DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
25 FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
26 DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
27 SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
28 CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
29 OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
30 OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
31 =========================================================================*/
32 
33 
34 /*
35  * sscTexture3DSlicerProxyImpl.cpp
36  *
37  * Created on: Oct 13, 2011
38  * Author: christiana
39  */
40 
41 #include "cxTexture3DSlicerProxy.h"
42 
43 #include <vtkRenderer.h>
44 #include <vtkFloatArray.h>
45 #include <vtkPlaneSource.h>
46 #include <vtkPointData.h>
47 #include <vtkTriangleFilter.h>
48 #include <vtkStripper.h>
49 #include <vtkImageData.h>
50 #include <vtkLookupTable.h>
51 #include <vtkOpenGLRenderWindow.h>
52 
53 #include "cxImage.h"
54 #include "cxView.h"
55 #include "cxImageLUT2D.h"
56 #include "cxSliceProxy.h"
57 #include "cxTypeConversions.h"
58 #include "cxGPUImageBuffer.h"
59 #include "cxReporter.h"
60 #include "cxConfig.h"
61 
62 
63 #include "cxTextureSlicePainter.h"
64 #ifndef CX_VTK_OPENGL2
65 #include <vtkPainterPolyDataMapper.h>
66 #endif
67 
68 //---------------------------------------------------------
69 namespace cx
70 {
71 //---------------------------------------------------------
72 
73 //#ifdef WIN32
74 //#ifdef CX_VTK_OPENGL2
75 #if defined(CX_VTK_OPENGL2) || defined(WIN32)
76 
78 {
79  return Texture3DSlicerProxyPtr(new Texture3DSlicerProxy());
80 }
81 
83 {
84  return false;
85 }
86 
87 #else
88 
90 {
91  vtkOpenGLRenderWindow *context = vtkOpenGLRenderWindow::SafeDownCast(window);
92  bool success = context && TextureSlicePainter::LoadRequiredExtensions(context->GetExtensionManager());
93  return success;
94 }
95 
97 {
99 }
100 
101 
103 {
104  mTargetSpaceIsR = true;
105 }
106 
108 {
109  mTargetSpaceIsR = false;
110  mActor = vtkActorPtr::New();
111  mPainter = TextureSlicePainterPtr::New();
112  mPainterPolyDatamapper = vtkPainterPolyDataMapperPtr::New();
113 
114  mPlaneSource = vtkPlaneSourcePtr::New();
115 
116  vtkTriangleFilterPtr triangleFilter = vtkTriangleFilterPtr::New(); //create triangle polygons from input polygons
117  triangleFilter->SetInputConnection(mPlaneSource->GetOutputPort()); //in this case a Planesource
118 
119  vtkStripperPtr stripper = vtkStripperPtr::New();
120  stripper->SetInputConnection(triangleFilter->GetOutputPort());
121 // stripper->Update();
122  mPolyDataAlgorithm = stripper;
123  mPolyDataAlgorithm->Update();
124 
125  mPolyData = mPolyDataAlgorithm->GetOutput();
126  mPolyData->GetPointData()->SetNormals(NULL);
127 
128  mPainter->SetDelegatePainter(mPainterPolyDatamapper->GetPainter());
129  mPainterPolyDatamapper->SetPainter(mPainter);
130  mPainterPolyDatamapper->SetInputData(mPolyData);
131  mActor->SetMapper(mPainterPolyDatamapper);
132 }
133 
135 {
136  mImages.clear();
137 }
138 
140 {
142 }
143 
145 {
146  return mActor;
147 }
148 
150 {
151  mPainter->setShaderPath(shaderFile);
152 }
153 
154 //void Texture3DSlicerProxyImpl::viewChanged()
155 //{
156 // if (!mView)
157 // return;
158 // if (!mView->getZoomFactor() < 0)
159 // return; // ignore if zoom is invalid
160 // this->setViewportData(mView->get_vpMs(), mView->getViewport());
161 //}
162 
164 {
165  if (!mTargetSpaceIsR)
166  {
167  mBB_s = transform(vpMs.inv(), vp);
168  }
169 
170  this->resetGeometryPlane();
171 }
172 
173 void Texture3DSlicerProxyImpl::resetGeometryPlane()
174 {
175  if (mTargetSpaceIsR)
176  {
177  // use largest bb from all volume box vertices projected onto s:
178  Transform3D rMs = mSliceProxy->get_sMr().inv();
179  DoubleBoundingBox3D bb_d = mImages[0]->boundingBox();
180  Transform3D sMd = rMs.inv()*mImages[0]->get_rMd();
181  std::vector<Vector3D> pp_s;
182  for (unsigned x=0; x<2; ++x)
183  for (unsigned y=0; y<2; ++y)
184  for (unsigned z=0; z<2; ++z)
185  pp_s.push_back(sMd.coord(bb_d.corner(x,y,x)));
186 
187  mBB_s = DoubleBoundingBox3D::fromCloud(pp_s);
188  mBB_s[4] = 0;
189  mBB_s[5] = 0;
190 // double extent = 100;
191 // mBB_s = DoubleBoundingBox3D(-extent, extent, -extent, extent, 0, 0);
192  }
193 
194  Vector3D origin(mBB_s[0], mBB_s[2], 0);
195  Vector3D p1(mBB_s[1], mBB_s[2], 0);
196  Vector3D p2(mBB_s[0], mBB_s[3], 0);
197 
198  if (mTargetSpaceIsR)
199  {
200  Transform3D rMs = mSliceProxy->get_sMr().inv();
201  p1 = rMs.coord(p1);
202  p2 = rMs.coord(p2);
203  origin = rMs.coord(origin);
204  }
205 
206  if (similar(mBB_s.range()[0] * mBB_s.range()[1], 0.0))
207  {
208 // std::cout << "zero-size bounding box in texture slicer- ignoring" << std::endl;
209  return;
210  }
211 
212  createGeometryPlane(p1, p2, origin);
213 }
214 
216 {
217 // std::cout << "createGeometryPlane " << point1_s << ", " << point2_s << ", " << origin_s << std::endl;
218  mPlaneSource->SetPoint1( point1_s.begin() );
219  mPlaneSource->SetPoint2( point2_s.begin() );
220  mPlaneSource->SetOrigin( origin_s.begin() );
221 // std::cout << "createGeometryPlane update begin" << std::endl;
222  mPolyDataAlgorithm->Update();
223 // mPolyData->Update();
224 // std::cout << "createGeometryPlane update end" << std::endl;
225  // each stripper->update() resets the contents of polydata, thus we must reinsert the data here.
226  for (unsigned i=0; i<mImages.size(); ++i)
227  {
228  updateCoordinates(i);
229  }
230 }
231 
232 bool Texture3DSlicerProxyImpl::isNewInputImages(std::vector<ImagePtr> images_raw)
233 {
234  if (mRawImages==images_raw)
235  return false;
236  mRawImages = images_raw;
237  return true;
238 }
239 
240 void Texture3DSlicerProxyImpl::setImages(std::vector<ImagePtr> images_raw)
241 {
242  if (!this->isNewInputImages(images_raw))
243  return;
244 
245  std::vector<ImagePtr> images = processImages(images_raw);
246 
247  for (unsigned i = 0; i < mImages.size(); ++i)
248  {
249  disconnect(mImages[i].get(), SIGNAL(transformChanged()), this, SLOT(transformChangedSlot()));
250  disconnect(mImages[i].get(), SIGNAL(transferFunctionsChanged()), this, SLOT(updateColorAttributeSlot()));
251  disconnect(mImages[i].get(), SIGNAL(vtkImageDataChanged()), this, SLOT(imageChanged()));
252  }
253 
254  mImages = images;
255 
256  for (unsigned i = 0; i < mImages .size(); ++i)
257  {
258  vtkImageDataPtr inputImage = mImages[i]->getBaseVtkImageData();
259 
261  inputImage);
262 
263  mPainter->SetVolumeBuffer(i, dataBuffer);
264 
265  connect(mImages[i].get(), SIGNAL(transformChanged()), this, SLOT(transformChangedSlot()));
266  connect(mImages[i].get(), SIGNAL(transferFunctionsChanged()), this, SLOT(updateColorAttributeSlot()));
267  connect(mImages[i].get(), SIGNAL(vtkImageDataChanged()), this, SLOT(imageChanged()));
268  this->updateCoordinates(i);
269  }
270  this->updateColorAttributeSlot();
271 
272  mPainterPolyDatamapper->RemoveAllVertexAttributeMappings();
273  for (unsigned i = 0; i < mImages.size(); ++i)
274  {
275  mPainterPolyDatamapper->MapDataArrayToMultiTextureAttribute(2 * i,
276  cstring_cast(this->getTCoordName(i)),
277  vtkDataObject::FIELD_ASSOCIATION_POINTS);
278  }
279 }
280 
281 std::vector<ImagePtr> Texture3DSlicerProxyImpl::processImages(std::vector<ImagePtr> images_raw)
282 {
283  if(images_raw.size() > mMaxImages)
284  {
285  QString errorText = QString("Texture3DSlicerProxyImpl: GPU multislicer can't handle more than %1 images. Additional images are not shown.").arg(mMaxImages);
286  reportError(errorText);
287  images_raw.resize(mMaxImages);
288  }
289 
290  std::vector<ImagePtr> images(images_raw.size());
291  for (unsigned i=0; i<images.size(); ++i)
292  images[i] = images_raw[i]->getUnsigned(images_raw[i]);
293 
294  return images;
295 }
296 
297 
299 {
300  if (mSliceProxy)
301  disconnect(mSliceProxy.get(), SIGNAL(transformChanged(Transform3D)), this, SLOT(transformChangedSlot()));
302  mSliceProxy = slicer;
303  if (mSliceProxy)
304  {
305  connect(mSliceProxy.get(), SIGNAL(transformChanged(Transform3D)), this, SLOT(transformChangedSlot()));
306  for (unsigned i=0; i < mImages.size(); ++i)
307  {
308  updateCoordinates(i);
309  }
310  }
311 }
312 
313 QString Texture3DSlicerProxyImpl::getTCoordName(int index)
314 {
315  return "texture"+qstring_cast(index);
316 }
317 
318 void Texture3DSlicerProxyImpl::updateCoordinates(int index)
319 {
320  if (!mPolyData || !mSliceProxy)
321  return;
322 
323  vtkImageDataPtr volume = mImages[index]->getBaseVtkImageData();
324  // create a bb describing the volume in physical (raw data) space
325  Vector3D origin(volume->GetOrigin());
326  Vector3D spacing(volume->GetSpacing());
327  DoubleBoundingBox3D imageSize(volume->GetExtent());
328 
329  for (int i = 0; i < 3; ++i)
330  {
331  imageSize[2 * i] = origin[i] + spacing[i] * (imageSize[2 * i] - 0.5);
332  imageSize[2 * i + 1] = origin[i] + spacing[i] * (imageSize[2 * i + 1] + 0.5);
333  }
334 
335  // identity bb
336  DoubleBoundingBox3D textureSpace(0.0, 1.0, 0.0, 1.0, 0.0, 1.0);
337 
338  // create transform from slice space to raw data space
339  Transform3D iMs = mImages[index]->get_rMd().inv() * mSliceProxy->get_sMr().inv();
340  // create transform from image space to texture normalized space
341  Transform3D nMi = createTransformNormalize(imageSize, textureSpace);
342  // total transform from slice space to texture space
343  Transform3D nMs = nMi * iMs;
344  // transform the viewport to texture coordinates (must use coords since bb3D doesnt handle non-axis-aligned transforms)
345  std::vector<Vector3D> plane(4);
346  plane[0] = mBB_s.corner(0, 0, 0);
347  plane[1] = mBB_s.corner(1, 0, 0);
348  plane[2] = mBB_s.corner(0, 1, 0);
349  plane[3] = mBB_s.corner(1, 1, 0);
350 
351  for (unsigned i = 0; i < plane.size(); ++i)
352  {
353  plane[i] = nMs.coord(plane[i]);
354  }
355 
356  vtkFloatArrayPtr TCoords = vtkFloatArray::SafeDownCast(mPolyData->GetPointData()->GetArray(
357  cstring_cast(getTCoordName(index))));
358 
359  if (!TCoords) // create the TCoords
360  {
361  TCoords = vtkFloatArrayPtr::New();
362  TCoords->SetNumberOfComponents(3);
363  TCoords->Allocate(4 * 3);
364  TCoords->InsertNextTuple3(0.0, 0.0, 0.0);
365  TCoords->InsertNextTuple3(0.0, 0.0, 0.0);
366  TCoords->InsertNextTuple3(0.0, 0.0, 0.0);
367  TCoords->InsertNextTuple3(0.0, 0.0, 0.0);
368  TCoords->SetName(cstring_cast(getTCoordName(index)));
369  mPolyData->GetPointData()->AddArray(TCoords);
370  }
371 
372  for (unsigned i = 0; i < plane.size(); ++i)
373  {
374  TCoords->SetTuple3(i, plane[i][0], plane[i][1], plane[i][2]);
375  }
376 
377  mPolyData->Modified();
378 }
379 
381 {
382  for (unsigned i = 0; i < mImages.size(); ++i)
383  {
384  vtkImageDataPtr inputImage = mImages[i]->getBaseVtkImageData() ;
385 
386  vtkLookupTablePtr lut = mImages[i]->getLookupTable2D()->getOutputLookupTable();
387  lut->GetTable()->Modified();
389 
390  // no lut indicates to the fragment shader that RGBA should be used
391  if (inputImage->GetNumberOfScalarComponents()==1)
392  {
393  mPainter->SetLutBuffer(i, lutBuffer);
394  }
395 
396  int scalarTypeMax = (int)inputImage->GetScalarTypeMax();
397  double imin = lut->GetRange()[0];
398  double imax = lut->GetRange()[1];
399 
400  float window = (float) (imax-imin) / scalarTypeMax;
401  float llr = (float) mImages[i]->getLookupTable2D()->getLLR() / scalarTypeMax;
402  float level = (float) imin/scalarTypeMax + window/2;
403  float alpha = (float) mImages[i]->getLookupTable2D()->getAlpha();
404 
405  mPainter->SetColorAttribute(i, window, level, llr, alpha);
406  }
407  mActor->Modified();
408 }
409 
411 {
412  if (mTargetSpaceIsR)
413  this->resetGeometryPlane();
414  this->update();
415 }
416 
418 {
419  mActor->Modified();
420 
421  for (unsigned i = 0; i < mImages .size(); ++i)
422  {
423  vtkImageDataPtr inputImage = mImages[i]->getBaseVtkImageData();//
424 
426  inputImage);
427 
428  mPainter->SetVolumeBuffer(i, dataBuffer);
429  }
430 }
431 
433 {
434  for (unsigned i=0; i<mImages.size(); ++i)
435  {
436  updateCoordinates(i);
437  }
438 }
439 
440 //#endif // WIN32
441 #endif //CX_VTK_OPENGL2
442 
443 
444 //---------------------------------------------------------
445 }//end namespace
446 //---------------------------------------------------------
447 
QString qstring_cast(const T &val)
DoubleBoundingBox3D transform(const Transform3D &m, const DoubleBoundingBox3D &bb)
vtkSmartPointer< class vtkActor > vtkActorPtr
void reportError(QString msg)
Definition: cxLogger.cpp:92
Vector3D corner(int x, int y, int z) const
Transform3D Transform3D
Transform3D is a representation of an affine 3D transform.
boost::shared_ptr< class SliceProxy > SliceProxyPtr
static Texture3DSlicerProxyPtr New()
vtkSmartPointer< class vtkFloatArray > vtkFloatArrayPtr
vtkSmartPointer< class vtkRenderWindow > vtkRenderWindowPtr
cstring_cast_Placeholder cstring_cast(const T &val)
void setTargetSpaceToR()
use to draw the slice in 3D r space instead of in 2D s space.
void setShaderPath(QString shaderFile)
static DoubleBoundingBox3D fromCloud(std::vector< Vector3D > cloud)
Transform3D createTransformNormalize(const DoubleBoundingBox3D &in, const DoubleBoundingBox3D &out)
GPUImageLutBufferPtr getGPUImageLutBuffer(vtkUnsignedCharArrayPtr lut)
void setViewportData(const Transform3D &vpMs, const DoubleBoundingBox3D &vp)
void setSliceProxy(SliceProxyPtr slicer)
static Texture3DSlicerProxyPtr New()
static bool LoadRequiredExtensions(vtkOpenGLExtensionManager *mgr)
vtkSmartPointer< class vtkTriangleFilter > vtkTriangleFilterPtr
void createGeometryPlane(Vector3D point1_s, Vector3D point2_s, Vector3D origin_s)
void setImages(std::vector< ImagePtr > images)
vtkSmartPointer< class vtkLookupTable > vtkLookupTablePtr
boost::shared_ptr< class Texture3DSlicerProxy > Texture3DSlicerProxyPtr
boost::shared_ptr< class GPUImageDataBuffer > GPUImageDataBufferPtr
vtkSmartPointer< class vtkStripper > vtkStripperPtr
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.
Eigen::Vector3d Vector3D
Vector3D is a representation of a point or vector in 3D.
Definition: cxVector3D.h:63
bool similar(const CameraInfo &lhs, const CameraInfo &rhs, double tol)
boost::shared_ptr< class GPUImageLutBuffer > GPUImageLutBufferPtr
GPUImageDataBufferPtr getGPUImageDataBuffer(vtkImageDataPtr volume)
static GPUImageBufferRepository * getInstance()
static bool isSupported(vtkRenderWindowPtr window)
vtkSmartPointer< class vtkImageData > vtkImageDataPtr