Commit c3a6dfa9 authored by Ken Martin's avatar Ken Martin
Browse files

add opengl gpu image processing support

Provide an example of how to use the GPU to
do some image processing. Includes a helper class
that handles some of the common tasks that you would
need to do for image processing.
parent 9aacc935
Pipeline #18385 passed with stage
set(Module_SRCS
vtkOpenGLImageGradient.cxx
)
vtk_module_library(${vtk-module} ${Module_SRCS})
vtk_add_test_cxx(${vtk-module}CxxTests tests
TestOpenGLImageGradient.cxx
)
vtk_test_cxx_executable(${vtk-module}CxxTests tests RENDERING_FACTORY)
/*=========================================================================
Program: Visualization Toolkit
Copyright (c) Ken Martin, Will Schroeder, Bill Lorensen
All rights reserved.
See Copyright.txt or http://www.kitware.com/Copyright.htm for details.
This software is distributed WITHOUT ANY WARRANTY; without even
the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR
PURPOSE. See the above copyright notice for more information.
=========================================================================*/
#include "vtkNew.h"
#include "vtkRenderWindowInteractor.h"
#include "vtkInteractorStyleImage.h"
#include "vtkRenderWindow.h"
#include "vtkRenderer.h"
#include "vtkCamera.h"
#include "vtkImageData.h"
#include "vtkImageSliceMapper.h"
#include "vtkImageProperty.h"
#include "vtkImageSlice.h"
#include "vtkImageReader2.h"
#include "vtkOpenGLImageGradient.h"
#include "vtkPointData.h"
#include "vtkDataArray.h"
#include "vtkTestUtilities.h"
#include "vtkRegressionTestImage.h"
int TestOpenGLImageGradient(int argc, char *argv[])
{
vtkNew<vtkRenderWindowInteractor> iren;
vtkNew<vtkInteractorStyleImage> style;
style->SetInteractionModeToImageSlicing();
vtkNew<vtkRenderWindow> renWin;
iren->SetRenderWindow(renWin.Get());
iren->SetInteractorStyle(style.Get());
char* fname =
vtkTestUtilities::ExpandDataFileName(argc, argv, "Data/headsq/quarter");
vtkNew<vtkImageReader2> reader;
reader->SetDataByteOrderToLittleEndian();
reader->SetDataExtent(0,63,0,63,1,93);
reader->SetDataSpacing(3.2, 3.2, 1.5);
reader->SetFilePrefix(fname);
delete [] fname;
vtkNew<vtkOpenGLImageGradient> filter;
// vtkNew<vtkImageGradient> filter;
filter->SetInputConnection(reader->GetOutputPort());
filter->Update();
// double *rnger = filter->GetOutput()->GetPointData()->GetScalars()->GetRange();
vtkNew<vtkImageSliceMapper> imageMapper;
imageMapper->SetInputConnection(filter->GetOutputPort());
imageMapper->SetOrientation(2);
imageMapper->SliceAtFocalPointOn();
vtkNew<vtkImageSlice> image;
image->SetMapper(imageMapper.Get());
double range[2] = { -100, 100 };
image->GetProperty()->SetColorWindow(range[1] - range[0]);
image->GetProperty()->SetColorLevel(0.5*(range[0] + range[1]));
image->GetProperty()->SetInterpolationTypeToNearest();
vtkNew<vtkRenderer> renderer;
renderer->AddViewProp(image.Get());
renderer->SetBackground(0.2,0.3,0.4);
renWin->AddRenderer(renderer.Get());
double *bounds = imageMapper->GetBounds();
double point[3];
point[0] = 0.5*(bounds[0] + bounds[1]);
point[1] = 0.5*(bounds[2] + bounds[3]);
point[2] = 0.5*(bounds[4] + bounds[5]);
vtkCamera *camera = renderer->GetActiveCamera();
camera->SetFocalPoint(point);
point[imageMapper->GetOrientation()] += 500.0;
camera->SetPosition(point);
if (imageMapper->GetOrientation() == 2)
{
camera->SetViewUp(0.0, 1.0, 0.0);
}
else
{
camera->SetViewUp(0.0, 0.0, -1.0);
}
camera->ParallelProjectionOn();
camera->SetParallelScale(0.8*128);
renWin->SetSize(512,512);
iren->Initialize();
renWin->Render();
int retVal = vtkRegressionTestImage( renWin.Get() );
if ( retVal == vtkRegressionTester::DO_INTERACTOR)
{
iren->Start();
}
return !retVal;
}
vtk_module(vtkImagingOpenGL2
TCL_NAME vtkImagingOpenGLII
BACKEND
OpenGL2
DEPENDS
vtkImagingGeneral
vtkRendering${VTK_RENDERING_BACKEND}
TEST_DEPENDS
vtkTestingRendering
vtkInteractionStyle
)
/*=========================================================================
Program: Visualization Toolkit
Module: vtkOpenGLImageGradient.cxx
Copyright (c) Ken Martin, Will Schroeder, Bill Lorensen
All rights reserved.
See Copyright.txt or http://www.kitware.com/Copyright.htm for details.
This software is distributed WITHOUT ANY WARRANTY; without even
the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR
PURPOSE. See the above copyright notice for more information.
=========================================================================*/
#include "vtkOpenGLImageGradient.h"
#include "vtkOpenGLImageAlgorithmHelper.h"
#include "vtkCellData.h"
#include "vtkDataArray.h"
#include "vtkImageData.h"
#include "vtkInformation.h"
#include "vtkInformationVector.h"
#include "vtkObjectFactory.h"
#include "vtkPointData.h"
#include "vtkStreamingDemandDrivenPipeline.h"
#include "vtkShaderProgram.h"
#include <algorithm> // for std::nth_element
vtkStandardNewMacro(vtkOpenGLImageGradient);
//-----------------------------------------------------------------------------
// Construct an instance of vtkOpenGLImageGradient fitler.
vtkOpenGLImageGradient::vtkOpenGLImageGradient()
{
// for GPU we do not want threading
this->NumberOfThreads = 1;
this->EnableSMP = false;
this->Helper = vtkOpenGLImageAlgorithmHelper::New();
}
//-----------------------------------------------------------------------------
vtkOpenGLImageGradient::~vtkOpenGLImageGradient()
{
if (this->Helper)
{
this->Helper->Delete();
this->Helper = 0;
}
}
void vtkOpenGLImageGradient::SetRenderWindow(vtkRenderWindow *renWin)
{
this->Helper->SetRenderWindow(renWin);
}
//-----------------------------------------------------------------------------
void vtkOpenGLImageGradient::PrintSelf(ostream& os, vtkIndent indent)
{
this->Superclass::PrintSelf(os, indent);
os << indent << "Helper: ";
this->Helper->PrintSelf(os, indent);
}
// this is used as a callback by the helper to set shader parameters
// before running and to update them on each slice
class vtkOpenGLGradientCB : public vtkOpenGLImageAlgorithmCallback
{
public:
// initialize the spacing
virtual void InitializeShaderUniforms(vtkShaderProgram *program)
{
float sp[3];
sp[0] = this->Spacing[0];
sp[1] = this->Spacing[1];
sp[2] = this->Spacing[2];
program->SetUniform3f("spacing", sp);
}
// no uniforms change on a per slice basis so empty
virtual void UpdateShaderUniforms(
vtkShaderProgram * /* program */, int /* zExtent */) {};
double *Spacing;
};
//-----------------------------------------------------------------------------
// This method contains the first switch statement that calls the correct
// templated function for the input and output region types.
void vtkOpenGLImageGradient::ThreadedRequestData(
vtkInformation *vtkNotUsed(request),
vtkInformationVector **inputVector,
vtkInformationVector *vtkNotUsed(outputVector),
vtkImageData ***inData,
vtkImageData **outData,
int outExt[6], int vtkNotUsed(id))
{
void *inPtr;
vtkDataArray *inArray = this->GetInputArrayToProcess(0,inputVector);
outData[0]->GetPointData()->GetScalars()->SetName(inArray->GetName());
inPtr = inArray->GetVoidPointer(0);
// The ouptut scalar type must be double to store proper gradients.
if(outData[0]->GetScalarType() != VTK_DOUBLE)
{
vtkErrorMacro("Execute: output ScalarType is "
<< outData[0]->GetScalarType() << "but must be double.");
return;
}
// Gradient makes sense only with one input component. This is not
// a Jacobian filter.
if(inArray->GetNumberOfComponents() != 1)
{
vtkErrorMacro(
"Execute: input has more than one component. "
"The input to gradient should be a single component image. "
"Think about it. If you insist on using a color image then "
"run it though RGBToHSV then ExtractComponents to get the V "
"components. That's probably what you want anyhow.");
return;
}
vtkOpenGLGradientCB cb;
cb.Spacing = inData[0][0]->GetSpacing();
// build the fragement shader for 2D or 3D gradient
std::string fragShader =
"//VTK::System::Dec\n"
"varying vec2 tcoordVSOutput;\n"
"uniform sampler3D inputTex1;\n"
"uniform float zPos;\n"
"uniform vec3 spacing;\n"
"uniform float inputScale;\n"
"uniform float inputShift;\n"
"//VTK::Output::Dec\n"
"void main(void) {\n"
" float dx = textureOffset(inputTex1, vec3(tcoordVSOutput, zPos), ivec3(1,0,0)).r\n"
" - textureOffset(inputTex1, vec3(tcoordVSOutput, zPos), ivec3(-1,0,0)).r;\n"
" dx = inputScale*0.5*dx/spacing.x;\n"
" float dy = textureOffset(inputTex1, vec3(tcoordVSOutput, zPos), ivec3(0,1,0)).r\n"
" - textureOffset(inputTex1, vec3(tcoordVSOutput, zPos), ivec3(0,-1,0)).r;\n"
" dy = inputScale*0.5*dy/spacing.y;\n"
;
if (this->Dimensionality == 3)
{
fragShader +=
" float dz = textureOffset(inputTex1, vec3(tcoordVSOutput, zPos), ivec3(0,0,1)).r\n"
" - textureOffset(inputTex1, vec3(tcoordVSOutput, zPos), ivec3(0,0,-1)).r;\n"
" dz = inputScale*0.5*dz/spacing.z;\n";
" gl_FragData[0] = vec4(dx, dy, dz, 1.0);\n"
"}\n";
}
else
{
fragShader +=
" gl_FragData[0] = vec4(dx, dy, 0.0, 1.0);\n"
"}\n";
}
// call the helper to execte this code
this->Helper->Execute(&cb,
inData[0][0], inArray,
outData[0], outExt,
"//VTK::System::Dec\n"
"attribute vec4 vertexMC;\n"
"attribute vec2 tcoordMC;\n"
"varying vec2 tcoordVSOutput;\n"
"void main() {\n"
" tcoordVSOutput = tcoordMC;\n"
" gl_Position = vertexMC;\n"
"}\n",
fragShader.c_str(),
"");
}
/*=========================================================================
Program: Visualization Toolkit
Module: vtkOpenGLImageGradient.h
Copyright (c) Ken Martin, Will Schroeder, Bill Lorensen
All rights reserved.
See Copyright.txt or http://www.kitware.com/Copyright.htm for details.
This software is distributed WITHOUT ANY WARRANTY; without even
the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR
PURPOSE. See the above copyright notice for more information.
=========================================================================*/
// .NAME vtkOpenGLImageGradient - Compute Gradient using the GPU
#ifndef vtkOpenGLImageGradient_h
#define vtkOpenGLImageGradient_h
#include "vtkImagingOpenGL2Module.h" // For export macro
#include "vtkImageGradient.h"
class vtkOpenGLImageAlgorithmHelper;
class vtkRenderWindow;
class VTKIMAGINGOPENGL2_EXPORT vtkOpenGLImageGradient : public vtkImageGradient
{
public:
static vtkOpenGLImageGradient *New();
vtkTypeMacro(vtkOpenGLImageGradient,vtkImageGradient);
// Description:
// Set the render window to get the OpenGL resources from
void SetRenderWindow(vtkRenderWindow *);
protected:
void PrintSelf(ostream& os, vtkIndent indent);
vtkOpenGLImageGradient();
~vtkOpenGLImageGradient();
vtkOpenGLImageAlgorithmHelper *Helper;
void ThreadedRequestData(vtkInformation *request,
vtkInformationVector **inputVector,
vtkInformationVector *outputVector,
vtkImageData ***inData, vtkImageData **outData,
int extent[6], int id);
private:
vtkOpenGLImageGradient(const vtkOpenGLImageGradient&); // Not implemented.
void operator=(const vtkOpenGLImageGradient&); // Not implemented.
};
#endif
......@@ -38,6 +38,7 @@ set(Module_SRCS
vtkOpenGLGlyph3DMapper.cxx
vtkOpenGLHardwareSelector.cxx
vtkOpenGLHelper.cxx
vtkOpenGLImageAlgorithmHelper.cxx
vtkOpenGLImageMapper.cxx
vtkOpenGLImageSliceMapper.cxx
vtkOpenGLIndexBufferObject.cxx
......
/*=========================================================================
Program: Visualization Toolkit
Module: vtkOpenGLImageAlgorithmHelper.cxx
Copyright (c) Ken Martin, Will Schroeder, Bill Lorensen
All rights reserved.
See Copyright.txt or http://www.kitware.com/Copyright.htm for details.
This software is distributed WITHOUT ANY WARRANTY; without even
the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR
PURPOSE. See the above copyright notice for more information.
=========================================================================*/
#include "vtkOpenGLImageAlgorithmHelper.h"
#include "vtkObjectFactory.h"
#include "vtkTextureObject.h"
#include "vtkOpenGLRenderWindow.h"
#include "vtkDataArray.h"
#include "vtkImageData.h"
#include "vtkNew.h"
#include "vtkFrameBufferObject.h"
#include "vtkOpenGLShaderCache.h"
#include "vtk_glew.h"
#include "vtkPixelTransfer.h"
#include "vtkPointData.h"
#include "vtkPixelBufferObject.h"
#include "vtkShaderProgram.h"
#include "vtkOpenGLVertexArrayObject.h"
vtkStandardNewMacro(vtkOpenGLImageAlgorithmHelper);
// ----------------------------------------------------------------------------
vtkOpenGLImageAlgorithmHelper::vtkOpenGLImageAlgorithmHelper()
{
this->RenderWindow = 0;
}
// ----------------------------------------------------------------------------
vtkOpenGLImageAlgorithmHelper::~vtkOpenGLImageAlgorithmHelper()
{
this->SetRenderWindow(0);
}
void vtkOpenGLImageAlgorithmHelper::SetRenderWindow(vtkRenderWindow *renWin)
{
if (renWin == this->RenderWindow.Get())
{
return;
}
vtkOpenGLRenderWindow *orw = NULL;
if (renWin)
{
orw = vtkOpenGLRenderWindow::SafeDownCast(renWin);
}
this->RenderWindow = orw;
this->Modified();
}
void vtkOpenGLImageAlgorithmHelper::Execute(
vtkOpenGLImageAlgorithmCallback *cb,
vtkImageData *inImage, vtkDataArray *inArray,
vtkImageData *outImage, int outExt[6],
const char *vertexCode,
const char *fragmentCode,
const char *geometryCode
)
{
// make sure it is initialized
if (!this->RenderWindow)
{
this->SetRenderWindow(vtkRenderWindow::New());
this->RenderWindow->SetOffScreenRendering(true);
this->RenderWindow->UnRegister(this);
}
this->RenderWindow->Initialize();
// Is it a 2D or 3D image
int dims[3];
inImage->GetDimensions(dims);
int dimensions = 0;
for (int i = 0; i < 3; i ++)
{
if (dims[i] > 1)
{
dimensions++;
}
}
// no 1d or 2D supprt yet
if (dimensions < 3)
{
vtkErrorMacro("no 1D or 2D processing support yet");
return;
}
// send vector data to a texture
int inputExt[6];
inImage->GetExtent(inputExt);
void *inPtr = inArray->GetVoidPointer(0);
// could do shortcut here if the input volume is
// exactly what we want (updateExtent == wholeExtent)
// vtkIdType incX, incY, incZ;
// inImage->GetContinuousIncrements(inArray, extent, incX, incY, incZ);
// tmpImage->CopyAndCastFrom(inImage, inUpdateExtent)
vtkNew<vtkTextureObject> inputTex;
inputTex->SetContext(this->RenderWindow);
inputTex->Create3DFromRaw(
dims[0], dims[1], dims[2],
inArray->GetNumberOfComponents(),
inArray->GetDataType(), inPtr);
float shift = 0.0;
float scale = 1.0;
inputTex->GetShiftAndScale(shift, scale);
// now create the framebuffer for the output
int outDims[3];
outDims[0] = outExt[1] - outExt[0] + 1;
outDims[1] = outExt[3] - outExt[2] + 1;
outDims[2] = outExt[5] - outExt[4] + 1;
vtkNew<vtkTextureObject> outputTex;
outputTex->SetContext(this->RenderWindow);
vtkNew<vtkFrameBufferObject> fbo;
fbo->SetContext(this->RenderWindow);
outputTex->Create2D(outDims[0], outDims[1], 4, VTK_FLOAT, false);
fbo->SetNumberOfRenderTargets(1);
fbo->SetColorBuffer(0,outputTex.Get());
// because the same FBO can be used in another pass but with several color
// buffers, force this pass to use 1, to avoid side effects from the
// render of the previous frame.
fbo->SetActiveBuffer(0);
// fbo->SetDepthBufferNeeded(true);
fbo->SetDepthBufferNeeded(false);
fbo->StartNonOrtho(outDims[0], outDims[1], false);
glViewport(0, 0, outDims[0], outDims[1]);
glScissor(0, 0, outDims[0], outDims[1]);
glDisable(GL_DEPTH_TEST);
vtkShaderProgram *prog =
this->RenderWindow->GetShaderCache()->ReadyShaderProgram(
vertexCode, fragmentCode, geometryCode);
if (prog != this->Quad.Program)
{
this->Quad.Program = prog;
this->Quad.VAO->ShaderProgramChanged();
}
cb->InitializeShaderUniforms(prog);
inputTex->Activate();
int inputTexId = inputTex->GetTextureUnit();
this->Quad.Program->SetUniformi("inputTex1", inputTexId);
// shift and scale to get the data backing into its original units
this->Quad.Program->SetUniformf("inputShift", shift);
this->Quad.Program->SetUniformf("inputScale", scale);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
// for each zslice in the output
vtkPixelExtent outputPixelExt(outExt);
for (int i = outExt[4]; i <= outExt[5]; i++)
{
cb->UpdateShaderUniforms(prog, i);
this->Quad.Program->SetUniformf("zPos", (i - outExt[4] + 0.5) / (outDims[2]));
fbo->RenderQuad(
0, outDims[0] - 1,
0, outDims[1] - 1,
this->Quad.Program, this->Quad.VAO);
vtkPixelBufferObject *outPBO = outputTex->Download();
vtkPixelTransfer::Blit<float, double>(
outputPixelExt,
outputPixelExt,
outputPixelExt,
outputPixelExt,
4,
(float*)outPBO->MapPackedBuffer(),
outImage->GetPointData()->GetScalars()->GetNumberOfComponents(),
static_cast<double *>(outImage->GetScalarPointer(outExt[0], outExt[2], i)));
outPBO->UnmapPackedBuffer();
outPBO->Delete();
}
}
// ----------------------------------------------------------------------------
void vtkOpenGLImageAlgorithmHelper::PrintSelf(ostream& os, vtkIndent indent)
{
this->Superclass::PrintSelf(os,indent);
os << indent << "RenderWindow:";
if(this->RenderWindow != 0)
{
this->RenderWindow->PrintSelf(os,indent);
}
else
{
os << "(none)" <<endl;
}
}
/*=========================================================================