In January 2021 we will introduce a 10 GB quota for project repositories. Higher limits for individual projects will be available on request. Please see https://doku.lrz.de/display/PUBLIC/GitLab for more information.

Fixed GlReduction, now working reliably for 2D and for 3D images. Added test...

Fixed GlReduction, now working reliably for 2D and for 3D images. Added test pipeline GlReduction test.
parent 3e3aabef
......@@ -54,13 +54,13 @@ void main() {
if (_sliceNumber < 0) {
// perform MIP
out_Color = vec4(0.0);
for (float slice = 0.0; slice < 1.0; slice += _3dTextureParams._sizeRCP.z) {
for (float slice = _3dTextureParams._sizeRCP.z/2.0; slice < 1.0; slice += _3dTextureParams._sizeRCP.z) {
out_Color = max(out_Color, lookupTF(_transferFunction, _transferFunctionParams, texture(_texture3d, vec3(ex_TexCoord.xy, slice)).r));
}
}
else {
// render the corresponding slice
vec3 coord = vec3(ex_TexCoord.xy, (_sliceNumber + 0.5) / (_3dTextureParams._size.z));
vec3 coord = vec3(ex_TexCoord.xy, (_sliceNumber * _3dTextureParams._sizeRCP.z) + (_3dTextureParams._sizeRCP.z / 2.0));
out_Color = lookupTF(_transferFunction, _transferFunctionParams, texture(_texture3d, coord).r);
}
}
......
......@@ -25,47 +25,48 @@
in vec3 ex_TexCoord;
out vec4 out_Color;
#include "tools/texture2d.frag"
#ifdef REDUCTION_1D
uniform sampler2D _texture;
uniform ivec2 _textureSize;
#endif
#ifdef REDUCTION_2D
uniform sampler2D _texture;
uniform vec2 _texCoordsShift;
uniform ivec2 _textureSize;
#endif
#ifdef REDUCTION_3D
uniform sampler3D _texture;
uniform vec2 _texCoordsShift;
uniform int _textureDepth;
uniform ivec3 _textureSize;
#endif
void main() {
vec2 tmp = ex_TexCoord.xy - _texCoordsShift;
ivec2 texel = ivec2(ex_TexCoord.xy * vec2(_textureSize.xy));
#ifdef REDUCTION_1D
// 2D reduction:
vec4 v = texelFetch(_texture, ivec2(0, 0), 0);
for (int x = 1; x < _textureSize.x; x += 1) {
v = REDUCTION_OP_2(v, texelFetch(_texture, ivec2(x, 0), 0));
}
#endif
#ifdef REDUCTION_2D
// 2D reduction:
vec4 a = texture(_texture, tmp);
vec4 b = textureOffset(_texture, tmp, ivec2(1, 0));
vec4 c = textureOffset(_texture, tmp, ivec2(0, 1));
vec4 d = textureOffset(_texture, tmp, ivec2(1, 1));
vec4 v = texelFetch(_texture, ivec2(texel.x, 0), 0);
for (int y = 1; y < _textureSize.y; y += 1) {
v = REDUCTION_OP_2(v, texelFetch(_texture, ivec2(texel.x, y), 0));
}
#endif
#ifdef REDUCTION_3D
// 3D reduction along depth:
float textureDepthRCP = 1.0 / _textureDepth;
vec4 a = texture(_texture, vec3(tmp, textureDepthRCP/2.0));
vec4 b = textureOffset(_texture, vec3(tmp, textureDepthRCP/2.0), ivec3(1, 0, 0));
vec4 c = textureOffset(_texture, vec3(tmp, textureDepthRCP/2.0), ivec3(0, 1, 0));
vec4 d = textureOffset(_texture, vec3(tmp, textureDepthRCP/2.0), ivec3(1, 1, 0));
for (float z = 3.0 * textureDepthRCP / 2.0; z < 1.0; z += textureDepthRCP) {
a = REDUCTION_OP_2(a, texture(_texture, vec3(tmp, z)));
b = REDUCTION_OP_2(b, textureOffset(_texture, vec3(tmp, z), ivec3(1, 0, 0)));
c = REDUCTION_OP_2(c, textureOffset(_texture, vec3(tmp, z), ivec3(0, 1, 0)));
d = REDUCTION_OP_2(d, textureOffset(_texture, vec3(tmp, z), ivec3(1, 1, 0)));
// 3D reduction along z direction:
vec4 v = texelFetch(_texture, ivec3(texel.xy, 0), 0);
for (int z = 1; z < _textureSize.z; z += 1) {
v = REDUCTION_OP_2(v, texelFetch(_texture, ivec3(texel.xy, z), 0));
}
#endif
// final reduction of the four pixels
out_Color = REDUCTION_OP_4(a, b, c, d);
out_Color = v;
}
......@@ -45,17 +45,21 @@ namespace campvis {
GlReduction::GlReduction(ReductionOperator reductionOperator)
: _reductionOperator(reductionOperator)
, _shader1d(0)
, _shader2d(0)
, _shader3d(0)
, _fbo(0)
{
_shader1d = ShdrMgr.loadSeparate("core/glsl/passthrough.vert", "core/glsl/tools/glreduction.frag", generateGlslHeader(_reductionOperator) + "#define REDUCTION_1D\n", false);
_shader2d = ShdrMgr.loadSeparate("core/glsl/passthrough.vert", "core/glsl/tools/glreduction.frag", generateGlslHeader(_reductionOperator) + "#define REDUCTION_2D\n", false);
_shader3d = ShdrMgr.loadSeparate("core/glsl/passthrough.vert", "core/glsl/tools/glreduction.frag", generateGlslHeader(_reductionOperator) + "#define REDUCTION_3D\n", false);
if (_shader2d == 0 || _shader3d == 0) {
if (_shader1d == 0 || _shader2d == 0 || _shader3d == 0) {
LERROR("Could not load Shader for OpenGL reduction. Reduction will not work!");
return;
}
_shader1d->setAttributeLocation(0, "in_Position");
_shader1d->setAttributeLocation(1, "in_TexCoord");
_shader2d->setAttributeLocation(0, "in_Position");
_shader2d->setAttributeLocation(1, "in_TexCoord");
_shader3d->setAttributeLocation(0, "in_Position");
......@@ -63,13 +67,14 @@ namespace campvis {
}
GlReduction::~GlReduction() {
ShdrMgr.dispose(_shader1d);
ShdrMgr.dispose(_shader2d);
ShdrMgr.dispose(_shader3d);
}
std::vector<float> GlReduction::reduce(const ImageData* image) {
tgtAssert(image != 0, "Image must not be 0!");
if (_shader2d == 0 || _shader3d == 0) {
if (_shader1d == 0 || _shader2d == 0 || _shader3d == 0) {
LERROR("Could not load Shader for OpenGL reduction. Reduction will not work!");
return std::vector<float>();
}
......@@ -91,7 +96,7 @@ namespace campvis {
std::vector<float> toReturn;
tgtAssert(texture != 0, "Image must not be 0!");
if (_shader2d == 0 || _shader3d == 0) {
if (_shader1d == 0 || _shader2d == 0 || _shader3d == 0) {
LERROR("Could not load Shader for OpenGL reduction. Reduction will not work!");
return toReturn;
}
......@@ -101,76 +106,88 @@ namespace campvis {
}
const tgt::ivec3& size = texture->getDimensions();
tgt::vec2 texCoordShift = tgt::vec2(.5f) / tgt::vec2(size.xy());
tgt::ivec2 currentSize = size.xy();
reduceSizes(currentSize, texCoordShift);
tgt::ivec2 startSize = currentSize;
tgt::ivec2 texSize = size.xy();
// Set OpenGL pixel alignment to 1 to avoid problems with NPOT textures
glPixelStorei(GL_UNPACK_ALIGNMENT, 1);
// get a free texture unit
tgt::TextureUnit inputUnit;
inputUnit.activate();
// create temporary textures
tgt::Texture* tempTextures[2];
for (size_t i = 0; i < 2; ++i) {
tempTextures[i] = new tgt::Texture(0, tgt::ivec3(currentSize, 1), GL_RGBA, GL_RGBA32F, GL_FLOAT, tgt::Texture::NEAREST);
tempTextures[i] = new tgt::Texture(0, tgt::ivec3(texSize, 1), GL_RGBA, GL_RGBA32F, GL_FLOAT, tgt::Texture::NEAREST);
tempTextures[i]->uploadTexture();
tempTextures[i]->setWrapping(tgt::Texture::CLAMP_TO_EDGE);
}
size_t readTex = 0;
size_t writeTex = 1;
const tgt::Texture* inputTex = texture;
tgt::Texture* outputTex = tempTextures[1];
// create and initialize FBO
_fbo = new tgt::FramebufferObject();
_fbo->activate();
LGL_ERROR;
// perform first reduction step outside:
tgt::Shader* leShader = (texture->getDimensions().z == 1) ? _shader2d : _shader3d;
leShader->activate();
_fbo->attachTexture(tempTextures[readTex]);
// perform 3D reduction if needed
if (texture->getDimensions().z > 1) {
_shader3d->activate();
_fbo->attachTexture(outputTex);
inputUnit.activate();
texture->bind();
leShader->setUniform("_texture", inputUnit.getUnitNumber());
leShader->setUniform("_texCoordsShift", texCoordShift);
if (leShader == _shader3d)
leShader->setUniform("_textureDepth", texture->getDimensions().z);
glViewport(startSize.x - currentSize.x, startSize.y - currentSize.y, currentSize.x, currentSize.y);
QuadRdr.renderQuad();
leShader->deactivate();
LGL_ERROR;
inputTex->bind();
_shader3d->setUniform("_texture", inputUnit.getUnitNumber());
_shader3d->setUniform("_textureSize", size);
glViewport(0, 0, texSize.x, texSize.y);
QuadRdr.renderQuad();
_shader3d->deactivate();
_shader2d->activate();
_shader2d->setUniform("_texture", inputUnit.getUnitNumber());
reduceSizes(currentSize, texCoordShift);
glViewport(startSize.x - currentSize.x, startSize.y - currentSize.y, currentSize.x, currentSize.y);
inputTex = outputTex;
outputTex = tempTextures[0];
LGL_ERROR;
}
// perform reduction until 1x1 texture remains
while (currentSize.x > 1 || currentSize.y > 1) {
_fbo->attachTexture(tempTextures[writeTex]);
tempTextures[readTex]->bind();
// perform 2D reduction if needed
if (texture->getDimensions().y > 1) {
_shader2d->activate();
_fbo->attachTexture(outputTex);
_shader2d->setUniform("_texCoordsShift", texCoordShift);
inputTex->bind();
_shader2d->setUniform("_texture", inputUnit.getUnitNumber());
_shader2d->setUniform("_textureSize", size.xy());
glViewport(0, 0, texSize.x, 1);
QuadRdr.renderQuad();
LGL_ERROR;
_shader2d->deactivate();
reduceSizes(currentSize, texCoordShift);
std::swap(writeTex, readTex);
inputTex = outputTex;
outputTex = (outputTex == tempTextures[1]) ? tempTextures[0] : tempTextures[1];
LGL_ERROR;
}
_shader2d->deactivate();
// finally, perform 1D reduction if needed
{
_shader1d->activate();
_fbo->attachTexture(outputTex);
inputTex->bind();
_shader1d->setUniform("_texture", inputUnit.getUnitNumber());
_shader1d->setUniform("_textureSize", size.xy());
glViewport(0, 0, 1, 1);
QuadRdr.renderQuad();
_shader1d->deactivate();
LGL_ERROR;
}
// read back stuff
GLenum readBackFormat = tempTextures[readTex]->getFormat();
size_t channels = tempTextures[readTex]->getNumChannels();
toReturn.resize(currentSize.x * currentSize.y * channels);
GLenum readBackFormat = outputTex->getFormat();
size_t channels = outputTex->getNumChannels();
toReturn.resize(channels);
glReadBuffer(GL_COLOR_ATTACHMENT0);
glReadPixels(startSize.x - currentSize.x, startSize.y - currentSize.y, currentSize.x, currentSize.y, readBackFormat, GL_FLOAT, &toReturn.front());
glReadPixels(0, 0, 1, 1, readBackFormat, GL_FLOAT, &toReturn.front());
LGL_ERROR;
// clean up...
......@@ -186,19 +203,6 @@ namespace campvis {
return toReturn;
}
void GlReduction::reduceSizes(tgt::ivec2& currentSize, tgt::vec2& texCoordShift) {
if (currentSize.x > 1) {
currentSize.x = DIV_CEIL(currentSize.x, 2);
if (currentSize.x == 1)
texCoordShift.x *= -1.f;
}
if (currentSize.y > 1) {
currentSize.y = DIV_CEIL(currentSize.y, 2);
if (currentSize.y == 1)
texCoordShift.y *= -1.f;
}
}
std::string GlReduction::generateGlslHeader(ReductionOperator reductionOperator) {
switch (reductionOperator) {
......
......@@ -88,13 +88,6 @@ namespace campvis {
private:
/**
* Performs a reduction of \a currentSize in each dimension and adjusts \a texCoordMultiplier.
* \param currentSize Current size to reduce (ceiling division by 2)
* \param texCoordMultiplier Tex coordinates multiplier to adjust (divide by 2 if dimension got reduced)
*/
static void reduceSizes(tgt::ivec2& currentSize, tgt::vec2& texCoordMultiplier);
/**
* Generates the GLSL header corresponding to the given reduction operator.
* \param reductionOperator Operation to be performed by reduction
......@@ -104,6 +97,7 @@ namespace campvis {
ReductionOperator _reductionOperator; ///< Operation to be performed by reduction
tgt::Shader* _shader1d; ///< OpenGL shader performing 1D reduction
tgt::Shader* _shader2d; ///< OpenGL shader performing 2D reduction
tgt::Shader* _shader3d; ///< OpenGL shader performing 3D reduction
tgt::FramebufferObject* _fbo; ///< FBO performing the reduction
......
// ================================================================================================
//
// This file is part of the CAMPVis Software Framework.
//
// If not explicitly stated otherwise: Copyright (C) 2012-2013, all rights reserved,
// Christian Schulte zu Berge <christian.szb@in.tum.de>
// Chair for Computer Aided Medical Procedures
// Technische Universität München
// Boltzmannstr. 3, 85748 Garching b. München, Germany
//
// For a full list of authors and contributors, please refer to the file "AUTHORS.txt".
//
// Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file
// except in compliance with the License. You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software distributed under the
// License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND,
// either express or implied. See the License for the specific language governing permissions
// and limitations under the License.
//
// ================================================================================================
#include "glreductiontest.h"
#include "tgt/event/keyevent.h"
#include "core/datastructures/imagedata.h"
#include "core/datastructures/renderdata.h"
#include "core/classification/geometry1dtransferfunction.h"
#include "core/classification/tfgeometry1d.h"
#include "core/tools/glreduction.h"
namespace campvis {
GlReductionTest::GlReductionTest(DataContainer* dc)
: AutoEvaluationPipeline(dc)
, _imageReader()
, _resampler(&_canvasSize)
, _ve(&_canvasSize)
{
addProcessor(&_imageReader);
addProcessor(&_resampler);
addProcessor(&_ve);
addEventListenerToBack(&_ve);
}
GlReductionTest::~GlReductionTest() {
}
void GlReductionTest::init() {
AutoEvaluationPipeline::init();
_glr = new GlReduction(GlReduction::MAX);
_glr2 = new GlReduction(GlReduction::MIN);
_imageReader.s_validated.connect(this, &GlReductionTest::onProcessorValidated);
_ve.p_outputImage.setValue("result");
_renderTargetID.setValue("result");
_imageReader.p_url.setValue(CAMPVIS_SOURCE_DIR "/modules/vis/sampledata/smallHeart.mhd");
_imageReader.p_targetImageID.setValue("reader.output");
_imageReader.p_targetImageID.addSharedProperty(&_resampler.p_inputImage);
_resampler.p_outputImage.setValue("resampled");
_resampler.p_outputImage.addSharedProperty(&_ve.p_inputVolume);
_resampler.s_validated.connect(this, &GlReductionTest::onProcessorValidated);
Geometry1DTransferFunction* dvrTF = new Geometry1DTransferFunction(128, tgt::vec2(0.f, .05f));
dvrTF->addGeometry(TFGeometry1D::createQuad(tgt::vec2(.1f, .125f), tgt::col4(255, 0, 0, 32), tgt::col4(255, 0, 0, 32)));
dvrTF->addGeometry(TFGeometry1D::createQuad(tgt::vec2(.4f, .5f), tgt::col4(0, 255, 0, 128), tgt::col4(0, 255, 0, 128)));
static_cast<TransferFunctionProperty*>(_ve.getProperty("TransferFunction"))->replaceTF(dvrTF);
}
void GlReductionTest::deinit() {
AutoEvaluationPipeline::deinit();
delete _glr;
delete _glr2;
}
void GlReductionTest::onProcessorValidated(AbstractProcessor* processor) {
if (processor == &_resampler) {
ImageRepresentationGL::ScopedRepresentation img(getDataContainer(), _resampler.p_outputImage.getValue());
if (img != 0) {
float* foo = reinterpret_cast<float*>(img->getTexture()->downloadTextureToBuffer(GL_RED, GL_FLOAT));
std::vector<float> v(foo, foo + tgt::hmul(img->getTexture()->getDimensions()));
float mmm = *std::max_element(v.begin(), v.end());
float max = _glr->reduce(img->getTexture()).front();
LDEBUG("3D: " << mmm << ", " << max << ", DIFF: " << (mmm - max));
}
ScopedTypedData<RenderData> result(getDataContainer(), _ve.p_outputImage.getValue() + ".raycaster");
if (result != 0) {
const ImageData* depthImg = result->getDepthTexture();
if (depthImg != 0) {
const ImageRepresentationGL* depthRep = depthImg->getRepresentation<ImageRepresentationGL>();
if (depthRep != 0) {
float* foo = reinterpret_cast<float*>(depthRep->getTexture()->downloadTextureToBuffer());
std::vector<float> v(foo, foo + depthRep->getNumElements());
float min1 = *std::min_element(v.begin(), v.end());
float min2 = _glr2->reduce(depthRep->getTexture()).front();
LDEBUG("2D: " << min1 << ", " << min2 << ", DIFF: " << std::abs(min2 - min1));
}
}
}
}
}
}
\ No newline at end of file
// ================================================================================================
//
// This file is part of the CAMPVis Software Framework.
//
// If not explicitly stated otherwise: Copyright (C) 2012-2013, all rights reserved,
// Christian Schulte zu Berge <christian.szb@in.tum.de>
// Chair for Computer Aided Medical Procedures
// Technische Universität München
// Boltzmannstr. 3, 85748 Garching b. München, Germany
//
// For a full list of authors and contributors, please refer to the file "AUTHORS.txt".
//
// Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file
// except in compliance with the License. You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software distributed under the
// License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND,
// either express or implied. See the License for the specific language governing permissions
// and limitations under the License.
//
// ================================================================================================
#ifndef GLREDUCTIONTEST_H__
#define GLREDUCTIONTEST_H__
#include "core/pipeline/autoevaluationpipeline.h"
#include "core/properties/cameraproperty.h"
#include "modules/io/processors/mhdimagereader.h"
#include "modules/preprocessing/processors/glimageresampler.h"
#include "modules/vis/processors/volumeexplorer.h"
#include "core/tools/glreduction.h"
namespace campvis {
class GlReductionTest : public AutoEvaluationPipeline {
public:
/**
* Creates a AutoEvaluationPipeline.
*/
GlReductionTest(DataContainer* dc);
/**
* Virtual Destructor
**/
virtual ~GlReductionTest();
/// \see AutoEvaluationPipeline::init()
virtual void init();
/// \see AutoEvaluationPipeline::deinit()
virtual void deinit();
/// \see AbstractPipeline::getName()
virtual const std::string getName() const { return getId(); };
static const std::string getId() { return "GlReductionTest"; };
protected:
/**
* Slot getting called when one of the observed processors got validated.
* Updates the camera properties, when the input image has changed.
* \param processor The processor that emitted the signal
*/
virtual void onProcessorValidated(AbstractProcessor* processor);
MhdImageReader _imageReader;
GlImageResampler _resampler;
VolumeExplorer _ve;
GlReduction* _glr;
GlReduction* _glr2;
};
}
#endif // GLREDUCTIONTEST_H__
......@@ -2,6 +2,7 @@
# Source files:
FILE(GLOB ThisModSources RELATIVE ${ModulesDir}
modules/preprocessing/pipelines/*.cpp
modules/preprocessing/processors/*.cpp
modules/preprocessing/tools/*.cpp
)
......@@ -9,8 +10,10 @@ FILE(GLOB ThisModSources RELATIVE ${ModulesDir}
# Header files
FILE(GLOB ThisModHeaders RELATIVE ${ModulesDir}
modules/preprocessing/glsl/*.frag
modules/preprocessing/pipelines/*.h
modules/preprocessing/processors/*.h
modules/preprocessing/tools/*.h
)
SET(ThisModShaderDirectories "modules/preprocessing/glsl")
SET(ThisModDependencies vis)
\ No newline at end of file
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment