Fraxinus  16.5.0-fx-rc8
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 void Texture3DSlicerProxyImpl::setImages(std::vector<ImagePtr> images_raw)
233 {
234  std::vector<ImagePtr> images = processImages(images_raw);
235 
236  for (unsigned i = 0; i < mImages.size(); ++i)
237  {
238  disconnect(mImages[i].get(), SIGNAL(transformChanged()), this, SLOT(transformChangedSlot()));
239  disconnect(mImages[i].get(), SIGNAL(transferFunctionsChanged()), this, SLOT(updateColorAttributeSlot()));
240  disconnect(mImages[i].get(), SIGNAL(vtkImageDataChanged()), this, SLOT(imageChanged()));
241  }
242 
243  mImages = images;
244 
245  for (unsigned i = 0; i < mImages .size(); ++i)
246  {
247  vtkImageDataPtr inputImage = mImages[i]->getBaseVtkImageData();
248 
250  inputImage);
251 
252  mPainter->SetVolumeBuffer(i, dataBuffer);
253 
254  connect(mImages[i].get(), SIGNAL(transformChanged()), this, SLOT(transformChangedSlot()));
255  connect(mImages[i].get(), SIGNAL(transferFunctionsChanged()), this, SLOT(updateColorAttributeSlot()));
256  connect(mImages[i].get(), SIGNAL(vtkImageDataChanged()), this, SLOT(imageChanged()));
257  this->updateCoordinates(i);
258  }
259  this->updateColorAttributeSlot();
260 
261  for (unsigned i = 0; i < mImages.size(); ++i)
262  {
263  mPainterPolyDatamapper->MapDataArrayToMultiTextureAttribute(2 * i,
264  cstring_cast(this->getTCoordName(i)),
265  vtkDataObject::FIELD_ASSOCIATION_POINTS);
266  }
267 }
268 
269 std::vector<ImagePtr> Texture3DSlicerProxyImpl::processImages(std::vector<ImagePtr> images_raw)
270 {
271  if(images_raw.size() > mMaxImages)
272  {
273  QString errorText = QString("Texture3DSlicerProxyImpl: GPU multislicer can't handle more than %1 images. Additional images are not shown.").arg(mMaxImages);
274  reportError(errorText);
275  images_raw.resize(mMaxImages);
276  }
277 
278  std::vector<ImagePtr> images(images_raw.size());
279  for (unsigned i=0; i<images.size(); ++i)
280  images[i] = images_raw[i]->getUnsigned(images_raw[i]);
281 
282  return images;
283 }
284 
285 
287 {
288  if (mSliceProxy)
289  disconnect(mSliceProxy.get(), SIGNAL(transformChanged(Transform3D)), this, SLOT(transformChangedSlot()));
290  mSliceProxy = slicer;
291  if (mSliceProxy)
292  {
293  connect(mSliceProxy.get(), SIGNAL(transformChanged(Transform3D)), this, SLOT(transformChangedSlot()));
294  for (unsigned i=0; i < mImages.size(); ++i)
295  {
296  updateCoordinates(i);
297  }
298  }
299 }
300 
301 QString Texture3DSlicerProxyImpl::getTCoordName(int index)
302 {
303  return "texture"+qstring_cast(index);
304 }
305 
306 void Texture3DSlicerProxyImpl::updateCoordinates(int index)
307 {
308  if (!mPolyData || !mSliceProxy)
309  return;
310 
311  vtkImageDataPtr volume = mImages[index]->getBaseVtkImageData();
312  // create a bb describing the volume in physical (raw data) space
313  Vector3D origin(volume->GetOrigin());
314  Vector3D spacing(volume->GetSpacing());
315  DoubleBoundingBox3D imageSize(volume->GetExtent());
316 
317  for (int i = 0; i < 3; ++i)
318  {
319  imageSize[2 * i] = origin[i] + spacing[i] * (imageSize[2 * i] - 0.5);
320  imageSize[2 * i + 1] = origin[i] + spacing[i] * (imageSize[2 * i + 1] + 0.5);
321  }
322 
323  // identity bb
324  DoubleBoundingBox3D textureSpace(0.0, 1.0, 0.0, 1.0, 0.0, 1.0);
325 
326  // create transform from slice space to raw data space
327  Transform3D iMs = mImages[index]->get_rMd().inv() * mSliceProxy->get_sMr().inv();
328  // create transform from image space to texture normalized space
329  Transform3D nMi = createTransformNormalize(imageSize, textureSpace);
330  // total transform from slice space to texture space
331  Transform3D nMs = nMi * iMs;
332  // transform the viewport to texture coordinates (must use coords since bb3D doesnt handle non-axis-aligned transforms)
333  std::vector<Vector3D> plane(4);
334  plane[0] = mBB_s.corner(0, 0, 0);
335  plane[1] = mBB_s.corner(1, 0, 0);
336  plane[2] = mBB_s.corner(0, 1, 0);
337  plane[3] = mBB_s.corner(1, 1, 0);
338 
339  for (unsigned i = 0; i < plane.size(); ++i)
340  {
341  plane[i] = nMs.coord(plane[i]);
342  }
343 
344  vtkFloatArrayPtr TCoords = vtkFloatArray::SafeDownCast(mPolyData->GetPointData()->GetArray(
345  cstring_cast(getTCoordName(index))));
346 
347  if (!TCoords) // create the TCoords
348  {
349  TCoords = vtkFloatArrayPtr::New();
350  TCoords->SetNumberOfComponents(3);
351  TCoords->Allocate(4 * 3);
352  TCoords->InsertNextTuple3(0.0, 0.0, 0.0);
353  TCoords->InsertNextTuple3(0.0, 0.0, 0.0);
354  TCoords->InsertNextTuple3(0.0, 0.0, 0.0);
355  TCoords->InsertNextTuple3(0.0, 0.0, 0.0);
356  TCoords->SetName(cstring_cast(getTCoordName(index)));
357  mPolyData->GetPointData()->AddArray(TCoords);
358  }
359 
360  for (unsigned i = 0; i < plane.size(); ++i)
361  {
362  TCoords->SetTuple3(i, plane[i][0], plane[i][1], plane[i][2]);
363  }
364 
365  mPolyData->Modified();
366 }
367 
369 {
370  for (unsigned i = 0; i < mImages.size(); ++i)
371  {
372  vtkImageDataPtr inputImage = mImages[i]->getBaseVtkImageData() ;
373 
374  vtkLookupTablePtr lut = mImages[i]->getLookupTable2D()->getOutputLookupTable();
375  lut->GetTable()->Modified();
377 
378  // no lut indicates to the fragment shader that RGBA should be used
379  if (inputImage->GetNumberOfScalarComponents()==1)
380  {
381  mPainter->SetLutBuffer(i, lutBuffer);
382  }
383 
384  int scalarTypeMax = (int)inputImage->GetScalarTypeMax();
385  double imin = lut->GetRange()[0];
386  double imax = lut->GetRange()[1];
387 
388  float window = (float) (imax-imin) / scalarTypeMax;
389  float llr = (float) mImages[i]->getLookupTable2D()->getLLR() / scalarTypeMax;
390  float level = (float) imin/scalarTypeMax + window/2;
391  float alpha = (float) mImages[i]->getLookupTable2D()->getAlpha();
392 
393  mPainter->SetColorAttribute(i, window, level, llr, alpha);
394  }
395  mActor->Modified();
396 }
397 
399 {
400  if (mTargetSpaceIsR)
401  this->resetGeometryPlane();
402  this->update();
403 }
404 
406 {
407  mActor->Modified();
408 
409  for (unsigned i = 0; i < mImages .size(); ++i)
410  {
411  vtkImageDataPtr inputImage = mImages[i]->getBaseVtkImageData();//
412 
414  inputImage);
415 
416  mPainter->SetVolumeBuffer(i, dataBuffer);
417  }
418 }
419 
421 {
422  for (unsigned i=0; i<mImages.size(); ++i)
423  {
424  updateCoordinates(i);
425  }
426 }
427 
428 //#endif // WIN32
429 #endif //CX_VTK_OPENGL2
430 
431 
432 //---------------------------------------------------------
433 }//end namespace
434 //---------------------------------------------------------
435 
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)
bool similar(const DoubleBoundingBox3D &a, const DoubleBoundingBox3D &b, double tol)
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
boost::shared_ptr< class GPUImageLutBuffer > GPUImageLutBufferPtr
GPUImageDataBufferPtr getGPUImageDataBuffer(vtkImageDataPtr volume)
static GPUImageBufferRepository * getInstance()
static bool isSupported(vtkRenderWindowPtr window)
vtkSmartPointer< class vtkImageData > vtkImageDataPtr