Page MenuHomePhabricator

Rework mitkSurfaceGLMapper2D
Closed, WontfixPublic

Description

The mitkSurfaceGLMapper2D is too slow especially if several (large) surfaces should be rendered in 2D. Talking to Miklos and Ivo we agreed that the code is in inefficient and should be replaced e.g. by some gpgpu implementation.

Event Timeline

Please make sure to double-check what means VTK offers for cutting surfaces before coding any new mapper.

I have one more question:
What is the use case where you need continuously updated fast surface cutting in 2D? The current performance is not good, but I never struggled too much when displaying surfaces and a corresponding volume. I am just curious ;).

I also figured out that this mapper is pretty slow, however, in our Time-of-Flight applications (where we continuously render a surface) we decided to just turn the 2D visualization off. It was not necessary.

Adding Miklos to cc.

Whoever the contributer is should be assignee of this bug. So who is in charge :) ?

According to Gergely, the vtkCutter can be seen as the bottleneck, which should be replaced. He proposes two different approaches for speeding up this functionality:

Option A:

  1. colour the voxels if the surface passes through them
  2. replace the mitkSurfaceGLMapper2D functionality to re-slice the binary volume,
  3. run contour extraction on it

Option B:

  1. use the camera position, focal point to display only requested slice using OpenGL command (via VTK)
  2. take the rendered image from the frame buffer and run the contour extraction

This needs to be done from three different views (axial, sagittal, coronal)

hints and openGL commands for Option B :

void glFrustum(GLdouble left, GLdouble right, GLdouble bottom, GLdouble top, GLdouble nearVal, GLdouble farVal); // set near and far plane to the same value

void gluLookAt(GLdouble eyeX, GLdouble eyeY, GLdouble eyeZ, GLdouble centerX, GLdouble centerY, GLdouble centerZ, GLdouble upX, GLdouble upY, GLdouble upZ); //set the focal point
gluLookAt() function, which takes an eye position, a position to look at, and an up vector, all in object space coordinates. This function computes the inverse camera transform according to its parameters and multiplies it onto the current matrix stack.

void glReadPixels(GLint x, GLint y, GLsizei width, GLsizei height, GLenum format, GLenum type, GLvoid * data); // read a block of pixels from the frame buffer

Hi there,
It's Gergely here from CMIC. I have already tried option B but the outcome isn't satisfactory. I implemented the VTK pipeline as mentioned and it does work realtime, however the outcome (contour of the surface's cross-section with the plane) is not identical to the results of the vtkCutter method. This is due to the fact that vtkCutter actually performs interpolation to generate the contour line when the plane is cutting through a cell, resulting in a nice and continuous outline. While option B just restricts which cells are being displayed by adjusting the camera + focal point, and displays those cells as they are. For example if a triangle entirely falls into the plane then it will be fully visible in the output, instead of being represented by the contour line.

What I ended up doing (as a temporary workaround) was to restrict the vtkPolydata to the neighborhood of the plane of cross-section by using vtkExtractPolyDataGeometry.

The code can be found here:
https://github.com/NifTK/MITK/commit/77e972f047610fcd9d06e3d61699c944c4143447

This solution involves a two step polydata extraction, it copies all cells within a +- 0.1 units neighborhood of the plane and runs vtkCutter on the result. This way the processing time of vtkCutter is negligible while the polydata extraction method is rather quick, so the overall time elapsed is a fraction of using vtkCutter on its own. I've tested it on a huge polydata set(> 2.000.000 cells) and I've found that the processing time decreased to 50%. On smaller surfaces the speed-up is more significant.

This workaround is good enough for now, but in the future we should definitely come up with a proper GPU based solution. As a first step I was thinking of using OpenCL to replace vtkExtractPolyDataGeometry and feed the output into vtkCutter. On the long run it would be nice to have a fully GPU based contour extractor. The question is (as always), who's going to implement it? :D

Cheers,
G.

Hi Gergeley,

thank you for all the information and the code.
I have a question still concerning the (old) "option B". Wouldn't you get the desired result if you additonally run a VTK contour extraction on the image you extract from the OpenGL frame buffer? (comparable to http://www.vtk.org/Wiki/VTK/Examples/Cxx/PolyData/ExternalContour).

Anyway, the approach to implement a GPU version is still the most desirable option. We will discuss it in our upcoming meetings. Thank you as well for providing the code which uses the vtkExtractPolyDataGeometry filter to speed up computations.

Regards,
Sandy

screenshots.zip:
01-screenshot-reference.png
02-screenshot-extractedSlice.png
03-screenshot-extractedContour.png
04-screenshot-extractedSlice.png
05-screenshot-extractedContour.png
06-screenshot-extractedSlice.png
07-screenshot-extractedContour.png

Hi Sandy,
I'm afraid that isn't doable. To demonstrate I made a couple of screenshots. The first one (01-screenshot-reference.png) shows the central slice of the vtk polydata file as it is rendered within the mitk stdMultiWidget.

The second screenshot (02-screenshot-extractedSlice.png) shows the content of the framebuffer after I adjusted the camera & focal point & clipping range:

tmp_rend->AddActor(polydataActor);
tmp_rend->ResetCamera();
tmp_rend->GetActiveCamera()->SetParallelProjection(1);

tmp_rend->GetActiveCamera()->SetParallelScale(inputImageSize[1]/2);
tmp_rend->GetActiveCamera()->SetPosition(xc,yc,zc);
tmp_rend->GetActiveCamera()->SetFocalPoint(0,0,zc);
tmp_rend->GetActiveCamera()->SetClippingRange(zc-1.5,zc+1.5);

Note that this is now only rendering the central slice's neighborhood (+-1.5 unit) along the z axis. On this image I ran the vtkContourFilter:

vtkSmartPointer<vtkContourFilter> ContFilter = vtkSmartPointer<vtkContourFilter>::New();
ContFilter->SetInputConnection(windowToImageFilter->GetOutputPort());
ContFilter->SetValue(0,255);
ContFilter->Update();

// Create a mapper and actor of the silhouette
vtkSmartPointer<vtkPolyDataMapper> contourMapper = vtkSmartPointer<vtkPolyDataMapper>::New();
contourMapper->SetInputConnection(ContFilter->GetOutputPort());

vtkSmartPointer<vtkActor> contourActor = vtkSmartPointer<vtkActor>::New();
contourActor->SetMapper(contourMapper);
contourActor->GetProperty()->SetLineWidth(1.);

Then finally I rendered the countourActor which resulted in the last image (03-screenshot-extractedContour.png).

It is possible to adjust the clipping planes to display a thinner slice, which will give more similar results to the reference but it will never be identical, as you can see it on the remaining screenshots.

Made with +-0.5 neighbourhood:
05-screenshot-extractedContour.png
04-screenshot-extractedSlice.png

Made with +-0.25 neighbourhood:
07-screenshot-extractedContour.png
06-screenshot-extractedSlice.png

I have some ideas regarding an OpenCL implementation, I'm happy to share them with you after I researched the topic a bit more.

Cheers,
Gergely

Hi guys. I made a pull request https://github.com/MITK/MITK/pull/73
There, the AABB_Tree provided by CGAL is used to cut through the surfaces. In my tests, this is was at least 4-5 times faster than vtkCutter. Enjoy :) And let me know if it is not working as expected.

Cheers,

Rostislav.

(IMPORTANT!)
I have pushed a new change to the pull request branch. Basically, I removed the custom vtk polydata iterator as it didn't serve the purpose of saving memory (due to within-iterator cache), but caused problems with linux builds. Just changed it to a plain vector of cgal triangles - this is both fast and reliable.

Also note that creating the triangles on-the-fly in the iterator from vtkPolyData will save memory, BUT the tree building performance will drop drastically (from tens of milliseconds to several seconds(!) for 500k triangles).

Additionally, fixed some memory leaks.
Commit pushed to branch.

kislinsk claimed this task.
kislinsk added a subscriber: kislinsk.

Just had a quick look at the pull request. CGAL is a bit tricky when it comes to licensing. Meanwhile it is licensed under LGPL/GPL, depending on the module. As far as I see, the library parts @khlebnikov used are LGPL. As over tweo years have passed, I'm not sure if the PR is still mergable at all. We also have to see if it is worth to integrate another external dependency. For now I do not see the pressure for a rise of priority, which eventually means, we probably will never work on this in they way it was proposed two years ago. Hence, I will close this task as Wontfix, sorry.

kislinsk removed kislinsk as the assignee of this task.
kislinsk added a project: Bulk Edit.
kislinsk removed a project: Bulk Edit.