11.3.2021, 9:00 - 11:00: Due to updates GitLab may be unavailable for some minutes between 09:00 and 11:00.

Commit ee5d4d97 authored by schultezub's avatar schultezub

Started implementing classification:

 * added AbstractTransferFunction and SimpleTransferFunction
 * added TransferFunctionProperty stub
 * added TF fragment shader include
 * SliceExtractor starts to use the new features

git-svn-id: https://camplinux.in.tum.de/svn/campvis/trunk@200 bb408c1c-ae56-11e1-83d9-df6b3e0c105e
parent 472caf58
......@@ -5,6 +5,7 @@ INCLUDE(../cmake/commonconf.cmake)
MESSAGE(STATUS "Configuring TUMVis Core")
FILE(GLOB TUMVIS_CORE_SOURCES RELATIVE ${CMAKE_CURRENT_SOURCE_DIR}
classification/*.cpp
datastructures/*.cpp
eventhandlers/*.cpp
pipeline/*.cpp
......@@ -13,6 +14,7 @@ FILE(GLOB TUMVIS_CORE_SOURCES RELATIVE ${CMAKE_CURRENT_SOURCE_DIR}
)
FILE(GLOB TUMVIS_CORE_HEADERS RELATIVE ${CMAKE_CURRENT_SOURCE_DIR}
classification/*.h
datastructures/*.h
eventhandlers/*.h
pipeline/*.h
......
#include "abstracttransferfunction.h"
#include "tgt/assert.h"
#include "tgt/logmanager.h"
#include "tgt/shadermanager.h"
#include "tgt/textureunit.h"
namespace TUMVis {
const std::string AbstractTransferFunction::loggerCat_ = "TUMVis.core.classification.AbstractTransferFunction";
AbstractTransferFunction::AbstractTransferFunction(const tgt::svec3& size, const tgt::vec2& intensityDomain /*= tgt::vec2(0.f, 1.f)*/)
: _size(size)
, _intensityDomain(intensityDomain)
, _texture(0)
{
_dirty = false;
}
AbstractTransferFunction::~AbstractTransferFunction() {
if (_texture != 0)
LWARNING("Called TranfserFunction dtor without proper deinitialization - you just wasted resources!");
}
void AbstractTransferFunction::deinit() {
delete _texture;
_texture = 0;
}
void AbstractTransferFunction::bind(tgt::Shader* shader, const tgt::TextureUnit& texUnit, const std::string& textureUniform /*= "_tfTex"*/, const std::string& textureParametersUniform /*= "_tgTextureParameters"*/) {
// TODO: lock here or in createTexture?
{
tbb::mutex::scoped_lock lock(_localMutex);
if (_texture == 0 || _dirty) {
createTexture();
}
}
texUnit.activate();
_texture->bind();
bool tmp = shader->getIgnoreUniformLocationError();
shader->setIgnoreUniformLocationError(true);
// TODO: set domain mapping uniforms
shader->setUniform(textureUniform, texUnit.getUnitNumber());
shader->setUniform(textureParametersUniform + "._intensityDomain", tgt::vec2(_intensityDomain));
shader->setIgnoreUniformLocationError(tmp);
}
void AbstractTransferFunction::setIntensityDomain(const tgt::vec2& newDomain) {
{
tbb::mutex::scoped_lock lock(_localMutex);
_intensityDomain = newDomain;
}
s_Changed();
}
}
\ No newline at end of file
#ifndef ABSTRACTTRANSFERFUNCTION_H__
#define ABSTRACTTRANSFERFUNCTION_H__
#include "sigslot/sigslot.h"
#include "tgt/texture.h"
#include "tgt/vector.h"
#include "tbb/include/tbb/atomic.h"
#include "tbb/include/tbb/mutex.h"
#include <string>
namespace tgt {
class Shader;
class TextureUnit;
}
namespace TUMVis {
/**
* Abstract base class for transfer functions.
*
* The granularity of the transfer function is determined by its size which is directly mapped to
* the OpenGL texture size. During classification the transfer function is mapped to the intensity
* domain.
*
* \note Transfer function objects are supposed to be thread-safe as follows:
* a) Access to non-OpenGL internals is protected by the local mutex.
* b) All OpenGL-related methods must be called by a thread with a valid and locked OpenGL
* context. Even though other internals might be changed meanwhile, this ensures that
* the OpenGL stuff (e.g. the texture) stays valid for this time.
*
* \todo Check thread-safety, probably the private local lock is probably not the best design.
*/
class AbstractTransferFunction {
public:
/**
* Creates a new AbstractTransferFunction.
* \param size Size of the transfer function texture
* \param intensityDomain Intensity Domain where the transfer function is mapped to during classification
*/
AbstractTransferFunction(const tgt::svec3& size, const tgt::vec2& intensityDomain = tgt::vec2(0.f, 1.f));
/**
* Destructor, make sure to delete the OpenGL texture beforehand by calling deinit() with a valid OpenGL context!
*/
virtual ~AbstractTransferFunction();
/**
* Deletes the OpenGL texture, hence, this methods has to be called from a thread with a valid OpenGL context!
*/
virtual void deinit();
/**
* Returns the dimensionality of the transfer function.
* \return The dimensionality of the transfer function.
*/
virtual size_t getDimensionality() const = 0;
/**
* Binds the transfer function OpenGL texture to the given texture and sets up uniforms.
* \note Calling thread must have a valid OpenGL context.
* \param shader Shader used for rendering
* \param texUnit Texture unit to bind texture to
* \param textureUniform Uniform name to store texture unit number
* \param textureParametersUniform Uniform name to store texture parameters
*/
void bind(tgt::Shader* shader, const tgt::TextureUnit& texUnit, const std::string& textureUniform = "_tfTex", const std::string& textureParametersUniform = "_tfTextureParameters");
/**
* Sets the intensity Domain where the transfer function is mapped to during classification.
* \param newDomain new intensity domain
*/
void setIntensityDomain(const tgt::vec2& newDomain);
/// Signal emitted when transfer function has changed.
sigslot::signal0<> s_Changed;
protected:
/**
* Creates the texture and uploads it to OpenGL.
* Gets called by bind() with the local mutex already acquired.
*/
virtual void createTexture() = 0;
tgt::svec3 _size; ///< Size of the transfer function texture
tgt::vec2 _intensityDomain; ///< Intensity Domain where the transfer function is mapped to during classification
tgt::Texture* _texture; ///< OpenGL lookup texture storing the TF
tbb::atomic<bool> _dirty; ///< Flag whether the OpenGL texture has to be updated
mutable tbb::mutex _localMutex; ///< mutex protecting the local members
static const std::string loggerCat_;
};
}
#endif // ABSTRACTTRANSFERFUNCTION_H__
#include "simpletransferfunction.h"
#include "tgt/assert.h"
#include "tgt/logmanager.h"
#include "tgt/shadermanager.h"
#include "tgt/textureunit.h"
namespace TUMVis {
const std::string SimpleTransferFunction::loggerCat_ = "TUMVis.core.classification.SimpleTransferFunction";
SimpleTransferFunction::SimpleTransferFunction(size_t size, const tgt::vec2& intensityDomain /*= tgt::vec2(0.f, 1.f)*/)
: AbstractTransferFunction(tgt::svec3(size, 1, 1), intensityDomain)
, _leftColor(0, 0, 0, 255)
, _rightColor(255)
{
}
SimpleTransferFunction::~SimpleTransferFunction() {
}
size_t SimpleTransferFunction::getDimensionality() const {
return 1;
}
void SimpleTransferFunction::createTexture() {
delete _texture;
GLenum dataType = GL_UNSIGNED_BYTE;
_texture = new tgt::Texture(_size, GL_RGBA, dataType, tgt::Texture::LINEAR);
_texture->setWrapping(tgt::Texture::CLAMP);
tgt::col4 diff = _rightColor - _leftColor;
for (size_t i = 0; i < _size.x; ++i) {
float multiplier = static_cast<float>(i) / _size.x;
tgt::col4& texel = _texture->texel<tgt::col4>(i);
for (size_t j = 0; j < 4; ++j) {
texel[j] = static_cast<uint8_t>(_leftColor[j] + (static_cast<float>(diff[j]) * multiplier));
}
}
_texture->uploadTexture();
_dirty = false;
}
void SimpleTransferFunction::setLeftColor(const tgt::col4& color) {
{
tbb::mutex::scoped_lock lock(_localMutex);
_leftColor = color;
}
s_Changed;
}
void SimpleTransferFunction::setRightColor(const tgt::col4& color) {
{
tbb::mutex::scoped_lock lock(_localMutex);
_leftColor = color;
}
s_Changed;
}
}
\ No newline at end of file
#ifndef SIMPLETRANSFERFUNCTION_H__
#define SIMPLETRANSFERFUNCTION_H__
#include "core/classification/abstracttransferfunction.h"
namespace TUMVis {
/**
* A very simple ramp transfer function, just for testing purposes...
*/
class SimpleTransferFunction : public AbstractTransferFunction {
public:
/**
* Creates a new SimpleTransferFunction.
* \param size Size of the transfer function texture
* \param intensityDomain Intensity Domain where the transfer function is mapped to during classification
*/
SimpleTransferFunction(size_t size, const tgt::vec2& intensityDomain = tgt::vec2(0.f, 1.f));
/**
* Destructor, make sure to delete the OpenGL texture beforehand by calling deinit() with a valid OpenGL context!
*/
virtual ~SimpleTransferFunction();
/**
* Returns the dimensionality of the transfer function.
* \return The dimensionality of the transfer function.
*/
virtual size_t getDimensionality() const;
void setLeftColor(const tgt::col4& color);
void setRightColor(const tgt::col4& color);
protected:
/**
* Creates the texture and uploads it to OpenGL.
* Gets called by bind() with the local mutex already acquired.
*/
virtual void createTexture();
tgt::col4 _leftColor;
tgt::col4 _rightColor;
static const std::string loggerCat_;
};
}
#endif // SIMPLETRANSFERFUNCTION_H__
// TODO: implement coordinate transformation using a trafo matrix?
struct TextureParameters {
vec2 _size;
vec2 _sizeRCP;
};
// Texture lookup function for 2D textures,
// expecting texture coordinates as pixel coordinates, i.e, [(0,0) , textureSize].
vec4 getElement2D(in sampler2D myTexture, in TextureParameters texParams, in vec2 texCoords) {
/**
* Texture lookup function for 2D textures using pixel coordinates, i.e [(0,0) , textureSize].
* \param tex 2D texture for lookup
* \param texParams Corresponding texture parameters struct
* \param texCoords Lookup coordinates in pixel coordinates
* \return The texel at the given coordinates.
*/
vec4 getElement2D(in sampler2D tex, in TextureParameters texParams, in vec2 texCoords) {
vec2 texCoordsNormalized = texCoords * texParams._sizeRCP;
//vec2 texCoordsTransformed = (texParams.matrix_ * vec4(texCoordsNormalized, 0.0, 1.0)).xy;
return texture2D(myTexture, texCoordsNormalized);
return texture2D(tex, texCoordsNormalized);
}
// Texture lookup function for 2D textures,
// expecting normalized texture coordinates, i.e., [0,1].
vec4 getElement2DNormalized(in sampler2D myTexture, in TextureParameters texParams, in vec2 texCoords) {
/**
* Texture lookup function for 2D textures using normalized texture coordinates, i.e. [0,1].
* \param tex 2D texture for lookup
* \param texParams Corresponding texture parameters struct
* \param texCoords Lookup coordinates in normlized texture coordinates
* \return The texel at the given coordinates.
*/
vec4 getElement2DNormalized(in sampler2D tex, in TextureParameters texParams, in vec2 texCoords) {
//vec2 texCoordsTransformed = (texParams.matrix_ * vec4(texCoords, 0.0, 1.0)).xy;
return texture2D(myTexture, texCoords);
return texture2D(tex, texCoords);
}
struct TFParameters {
vec2 _intensityDomain;
};
/**
* Linearly maps the image intensity \a intensity to the domain of the transfer function as defined in the TF parameters \a p.
* \param p Transfer function parameters struct
* \param intensity Image intensity (normalized to [0, 1])
* \return Intensity mapped to transfer function domain.
*/
float mapIntensityToTFDomain(in TFParameters p, in float intensity) {
if(intensity <= p._intensityDomain.x)
return 0.0;
else if(intensity >= p._intensityDomain.y)
return 1.0;
else
return (intensity - p._intensityDomain.x) / (p._intensityDomain.y - p._intensityDomain.x);
}
/**
* Performs a 1D transfer function lookup for the given TF and intensity.
* Before lookup \a intensity will be mapped to the TF domain.
* \param p Transfer function parameters struct
* \param tex Transfer function texture
* \param intensity Image intensity (normalized to [0, 1])
* \return The color of the transfer function at the given intensity.
*/
vec4 lookupTF(in TFParameters p, in sampler1D tex, in float intensity) {
intensity = mapIntensityToTFDomain(p, intensity);
return texture1D(tex, intensity);
}
/**
* Performs a 2D transfer function lookup for the given TF, intensity and y-value.
* Before lookup \a intensity will be mapped to the TF domain.
* \param p Transfer function parameters struct
* \param tex Transfer function texture
* \param intensity Image intensity (normalized to [0, 1])
* \param y y-value for lookup
* \return The color of the transfer function at the given intensity and y-value.
*/
vec4 lookupTF(in TFParameters p, in sampler2D tex, in float intensity, in float y) {
intensity = mapIntensityToTFDomain(p, intensity);
return texture2D(tex, vec2(intensity, y));
}
#include "TransferFunctionProperty.h"
namespace TUMVis {
const std::string TransferFunctionProperty::loggerCat_ = "TUMVis.core.datastructures.TransferFunctionProperty";
TransferFunctionProperty::TransferFunctionProperty(const std::string& name, const std::string& title, AbstractTransferFunction* tf, InvalidationLevel il /*= InvalidationLevel::INVALID_RESULT*/)
: AbstractProperty(name, title, il)
, _transferFunction(tf)
{
tgtAssert(tf != 0, "Assigned transfer function must not be 0.");
tf->s_Changed.connect(this, &TransferFunctionProperty::onTFChanged);
}
TransferFunctionProperty::~TransferFunctionProperty() {
}
AbstractTransferFunction* TransferFunctionProperty::getTF() {
return _transferFunction;
}
void TransferFunctionProperty::onTFChanged() {
notifyObservers(PropertyObserverArgs(this, _invalidationLevel));
}
}
#ifndef TRANSFERFUNCTIONPROPERTY_H__
#define TRANSFERFUNCTIONPROPERTY_H__
#include "sigslot/sigslot.h"
#include "core/properties/abstractproperty.h"
#include "core/classification/abstracttransferfunction.h"
namespace TUMVis {
class TransferFunctionProperty : public AbstractProperty, public sigslot::has_slots<> {
public:
/**
* Creates a new TransferFunctionProperty
* \param name Property name (unchangable!)
* \param title Property title (e.g. used for GUI)
* \param il Invalidation level that this property triggers
*/
TransferFunctionProperty(const std::string& name, const std::string& title, AbstractTransferFunction* tf, InvalidationLevel il = InvalidationLevel::INVALID_RESULT);
/**
* Virtual Destructor
**/
virtual ~TransferFunctionProperty();
AbstractTransferFunction* getTF();
void onTFChanged();
protected:
AbstractTransferFunction* _transferFunction;
static const std::string loggerCat_;
};
}
#endif // TRANSFERFUNCTIONPROPERTY_H__
......@@ -2,8 +2,6 @@
#define QTTHREADEDCANVAS_H__
#include "tgt/qt/qtcanvas.h"
#include <qmutex.h>
#include <qwaitcondition.h>
namespace tgt {
......
......@@ -27,6 +27,8 @@ namespace TUMVis {
_sliceExtractor._sourceImageID.setValue("se.input");
_sliceExtractor._sliceNumber.setValue(0);
// TODO: replace this hardcoded domain by automatically determined from image min/max values
_sliceExtractor._transferFunction.getTF()->setIntensityDomain(tgt::vec2(0, 0.05f));
_renderTargetID.setValue("renderTarget");
_renderTargetID.addSharedProperty(&(_sliceExtractor._targetImageID));
......
......@@ -2,11 +2,14 @@
#include "tgt/logmanager.h"
#include "tgt/quadrenderer.h"
#include "tgt/textureunit.h"
#include "core/datastructures/imagedata.h"
#include "core/datastructures/imagedatagl.h"
#include "core/datastructures/imagedatarendertarget.h"
#include "core/datastructures/imagedataconverter.h"
#include "core/classification/simpletransferfunction.h"
namespace TUMVis {
const std::string SliceExtractor::loggerCat_ = "TUMVis.modules.vis.SliceExtractor";
......@@ -15,11 +18,13 @@ namespace TUMVis {
, _sourceImageID("sourceImageID", "Input Image", "")
, _targetImageID("targetImageID", "Output Image", "")
, _sliceNumber("sliceNumber", "Slice Number", 0, 0, 0)
, _transferFunction("transferFunction", "Transfer Function", new SimpleTransferFunction(256))
, _shader(0)
{
_properties.addProperty(&_sourceImageID);
_properties.addProperty(&_targetImageID);
_properties.addProperty(&_sliceNumber);
_properties.addProperty(&_transferFunction);
_sliceNumber.addObserver(this);
}
......@@ -47,8 +52,9 @@ namespace TUMVis {
ImageDataRenderTarget* rt = new ImageDataRenderTarget(tgt::svec3(_renderTargetSize.getValue(), 1));
_shader->activate();
tgt::TextureUnit inputUnit;
tgt::TextureUnit inputUnit, tfUnit;
glData->bind(_shader, inputUnit);
_transferFunction.getTF()->bind(_shader, tfUnit);
rt->activate();
glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
......
#include "tools/sampler2d.frag"
#include "tools/transferfunction.frag"
uniform sampler2D _texture;
uniform TextureParameters _textureParameters;
uniform sampler1D _tfTex;
uniform TFParameters _tfTextureParameters;
void main() {
float intensity = getElement2DNormalized(_texture, _textureParameters, gl_TexCoord[0].xy).a * 10.0;
gl_FragData[0] = vec4(intensity, intensity, intensity, 1.0);
float intensity = getElement2DNormalized(_texture, _textureParameters, gl_TexCoord[0].xy).a;
gl_FragData[0] = lookupTF(_tfTextureParameters, _tfTex, intensity);
}
......@@ -4,9 +4,11 @@
#include <string>
#include "tgt/shadermanager.h"
#include "core/classification/abstracttransferfunction.h"
#include "core/pipeline/visualizationprocessor.h"
#include "core/properties/genericproperty.h"
#include "core/properties/numericproperty.h"
#include "core/properties/transferfunctionproperty.h"
namespace TUMVis {
class ImageData;
......@@ -38,6 +40,7 @@ namespace TUMVis {
GenericProperty<std::string> _targetImageID; ///< image ID for output image
NumericProperty<size_t> _sliceNumber; ///< number of the slice to extract
TransferFunctionProperty _transferFunction; ///< Transfer function
protected:
void updateProperties(const ImageData* img);
......
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