Commit 2ef197ac authored by Christian Schulte zu Berge's avatar Christian Schulte zu Berge
Browse files

Merge branch 'reduction-fix' of /mnt/bigone/git/repositories/berge/campvis into development

parents 3e3aabef 20e53e2d
......@@ -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;
}
......@@ -129,6 +129,7 @@ namespace campvis {
void AbstractProcessor::process(DataContainer& data, bool unlockInExtraThread) {
// use a scoped lock for exception safety
AbstractProcessor::ScopedLock lock(this, unlockInExtraThread);
tgtAssert(_locked == true, "Processor not locked, this should not happen!");
if (hasInvalidShader())
updateShader();
......
......@@ -135,7 +135,7 @@ namespace campvis {
tbb::spin_rw_mutex::scoped_lock lock(_pmMutex, false);
PortMapType::const_iterator it = _portMap.find(name);
while(it != _portMap.end() && it->first == name) {
it->second->s_changed(it->second);
it->second->setValue(it->second->getValue());
++it;
}
}
......
......@@ -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__
......@@ -26,9 +26,12 @@
#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 {
......@@ -50,8 +53,6 @@ namespace campvis {
void ResamplingDemo::init() {
AutoEvaluationPipeline::init();
_imageReader.s_validated.connect(this, &ResamplingDemo::onProcessorValidated);
_ve.p_outputImage.setValue("result");
_renderTargetID.setValue("result");
......@@ -62,13 +63,11 @@ namespace campvis {
_resampler.p_outputImage.setValue("resampled");
_resampler.p_outputImage.addSharedProperty(&_ve.p_inputVolume);
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);
_canvasSize.s_changed.connect<ResamplingDemo>(this, &ResamplingDemo::onRenderTargetSizeChanged);
}
void ResamplingDemo::deinit() {
......@@ -76,11 +75,5 @@ namespace campvis {
AutoEvaluationPipeline::deinit();
}
void ResamplingDemo::onRenderTargetSizeChanged(const AbstractProperty* prop) {
}
void ResamplingDemo::onProcessorValidated(AbstractProcessor* processor) {
}
}
\ No newline at end of file
......@@ -30,6 +30,7 @@
#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 ResamplingDemo : public AutoEvaluationPipeline {
......@@ -54,16 +55,7 @@ namespace campvis {
virtual const std::string getName() const { return getId(); };
static const std::string getId() { return "ResamplingDemo"; };
void onRenderTargetSizeChanged(const AbstractProperty* prop);