Commit d2d0e883 authored by schultezub's avatar schultezub
Browse files

* further work on GeometryTransferFunctionEditor

 * added histograms for ImageDataLocal and AbstractTransferFunction


git-svn-id: https://camplinux.in.tum.de/svn/campvis/trunk@311 bb408c1c-ae56-11e1-83d9-df6b3e0c105e
parent 27b69655
......@@ -36,6 +36,7 @@
#include "application/gui/qtcolortools.h"
#include "core/classification/geometrytransferfunction.h"
#include "core/classification/tfgeometry.h"
#include "core/datastructures/imagedatalocal.h"
#include "core/properties/transferfunctionproperty.h"
#include "core/tools/opengljobprocessor.h"
......@@ -64,11 +65,14 @@ namespace TUMVis {
}
GeometryTransferFunctionEditor::~GeometryTransferFunctionEditor() {
// TODO: this needs to be done, but we can not ensure that GLJobProc is still existant during deconstruction...
//GLJobProc.deregisterContext(_canvas);
}
void GeometryTransferFunctionEditor::updateWidgetFromProperty() {
GeometryTransferFunction* gtf = static_cast<GeometryTransferFunction*>(_transferFunction);
invalidate();
}
void GeometryTransferFunctionEditor::init() {
......@@ -85,11 +89,48 @@ namespace TUMVis {
glPushMatrix();
glViewport(0, 0, _canvas->width(), _canvas->height());
glOrtho(0, 1, 0, 1, -1, 1);
glClearColor(1.f, 1.f, 1.f, 1.f);
glClear(GL_COLOR_BUFFER_BIT);
LGL_ERROR;
// const DataHandle* dh = gtf->getImageHandle();
// if (dh != 0) {
// const ImageDataLocal* idl = dynamic_cast<const ImageDataLocal*>(dh->getData());
// if (idl != 0) {
// const ImageDataLocal::IntensityHistogramType& ih = idl->getIntensityHistogram();
const AbstractTransferFunction::IntensityHistogramType* ih = gtf->getIntensityHistogram();
if (ih != 0) {
size_t numBuckets = ih->getNumBuckets(0);
if (numBuckets > 0) {
float maxFilling = static_cast<float>(ih->getMaxFilling());
float xl = static_cast<float>(0.f) / static_cast<float>(numBuckets);
float xr = 0.f;
float yl = static_cast<float>(ih->getNumElements(0)) / maxFilling;
float yr = 0.f;
glPushMatrix();
glOrtho(0,1 , 0, 1, -1, 1);
glBegin(GL_QUADS);
glColor4f(1.f, .75f, 0.f, .25f);
for (size_t i = 1; i < numBuckets; ++i) {
xr = static_cast<float>(i) / static_cast<float>(numBuckets);
yr = static_cast<float>(ih->getNumElements(i)) / maxFilling;
glVertex2f(xl, 0.f);
glVertex2f(xl, yl);
glVertex2f(xr, yr);
glVertex2f(xr, 0.f);
xl = xr;
yl = yr;
}
glEnd();
glPopMatrix();
}
}
// }
// glBegin(GL_QUADS);
// glColor3f(1.f, 0.f, 0.f);
// glVertex2f(0.f, 0.f);
......@@ -101,6 +142,7 @@ namespace TUMVis {
// glVertex2f(0.f, 1.f);
// glEnd();
glOrtho(0, 1, 0, 1, -1, 1);
for (std::vector<TFGeometry*>::const_iterator it = geometries.begin(); it != geometries.end(); ++it) {
(*it)->render();
}
......
......@@ -28,13 +28,37 @@
#include "abstracttransferfunction.h"
#include "tbb/include/tbb/tbb.h"
#include "tgt/assert.h"
#include "tgt/logmanager.h"
#include "tgt/shadermanager.h"
#include "tgt/texture.h"
#include "tgt/textureunit.h"
#include "core/datastructures/imagedatalocal.h"
namespace TUMVis {
class IntensityHistogramGenerator {
public:
IntensityHistogramGenerator(const ImageDataLocal* intensityData, AbstractTransferFunction::IntensityHistogramType* histogram)
: _intensityData(intensityData)
, _histogram(histogram)
{}
void operator() (const tbb::blocked_range<size_t>& range) const {
for (size_t i = range.begin(); i != range.end(); ++i) {
float value = _intensityData->getElementNormalized(i, 0);
_histogram->addSample(&value);
}
}
protected:
const ImageDataLocal* _intensityData;
AbstractTransferFunction::IntensityHistogramType* _histogram;
};
// ================================================================================================
const std::string AbstractTransferFunction::loggerCat_ = "TUMVis.core.classification.AbstractTransferFunction";
......@@ -42,8 +66,11 @@ namespace TUMVis {
: _size(size)
, _intensityDomain(intensityDomain)
, _texture(0)
, _imageHandle(0)
, _intensityHistogram(0)
{
_dirty = false;
_dirtyTexture = false;
_dirtyHistogram = false;
}
AbstractTransferFunction::~AbstractTransferFunction() {
......@@ -60,7 +87,7 @@ namespace TUMVis {
// TODO: lock here or in createTexture?
{
tbb::mutex::scoped_lock lock(_localMutex);
if (_texture == 0 || _dirty) {
if (_texture == 0 || _dirtyTexture) {
createTexture();
}
}
......@@ -82,6 +109,7 @@ namespace TUMVis {
tbb::mutex::scoped_lock lock(_localMutex);
_intensityDomain = newDomain;
}
_dirtyHistogram = true;
s_changed();
}
......@@ -93,12 +121,51 @@ namespace TUMVis {
// TODO: lock here or in createTexture?
{
tbb::mutex::scoped_lock lock(_localMutex);
if (_texture == 0 || _dirty) {
if (_texture == 0 || _dirtyTexture) {
createTexture();
}
}
return _texture;
}
const DataHandle* AbstractTransferFunction::getImageHandle() const {
return _imageHandle;
}
void AbstractTransferFunction::setImageHandle(const DataHandle* imageHandle) {
delete _imageHandle;
if (imageHandle == 0)
_imageHandle = 0;
else
_imageHandle = new DataHandle(*imageHandle);
_dirtyHistogram = true;
}
void AbstractTransferFunction::computeIntensityHistogram() const {
delete _intensityHistogram;
_intensityHistogram = 0;
if (_imageHandle != 0) {
const ImageDataLocal* idl = dynamic_cast<const ImageDataLocal*>(_imageHandle->getData());
if (idl != 0) {
float mins = _intensityDomain.x;
float maxs = _intensityDomain.y;
size_t numBuckets = 512;
_intensityHistogram = new IntensityHistogramType(&mins, &maxs, &numBuckets);
tbb::parallel_for(tbb::blocked_range<size_t>(0, idl->getNumElements()), IntensityHistogramGenerator(idl, _intensityHistogram));
}
}
}
const AbstractTransferFunction::IntensityHistogramType* AbstractTransferFunction::getIntensityHistogram() const {
if (_dirtyHistogram) {
computeIntensityHistogram();
}
return _intensityHistogram;
}
}
\ No newline at end of file
......@@ -34,6 +34,9 @@
#include "tbb/include/tbb/atomic.h"
#include "tbb/include/tbb/mutex.h"
#include "core/datastructures/datahandle.h"
#include "core/tools/concurrenthistogram.h"
#include <string>
namespace tgt {
......@@ -61,6 +64,8 @@ namespace TUMVis {
*/
class AbstractTransferFunction {
public:
typedef ConcurrentGenericHistogramND<float, 1> IntensityHistogramType;
/**
* Creates a new AbstractTransferFunction.
* \param size Size of the transfer function texture
......@@ -113,20 +118,49 @@ namespace TUMVis {
*/
const tgt::Texture* getTexture();
/**
* Returns a DataHandle to the image for this transfer function, may be 0.
* \return _imageHandle, may be 0!
*/
const DataHandle* getImageHandle() const;
/**
* Sets the DataHandle for this transfer function, may be 0.
* \note This method makes a copy of \a imageHandle, hence does not take ownership.
* \param imageHandle The new DataHandle for this transfer function, may be 0, will be copied.
*/
void setImageHandle(const DataHandle* imageHandle);
/**
* Returns the intensity histogram
* \todo This is NOT thread-safe!
* \return _intensityHistogram
*/
const IntensityHistogramType* getIntensityHistogram() const;
/// Signal emitted when transfer function has changed.
sigslot::signal0<> s_changed;
protected:
/**
* Computes the intensity histogram;
*/
void computeIntensityHistogram() const;
/**
* 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
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> _dirtyTexture; ///< Flag whether the OpenGL texture has to be updated
const DataHandle* _imageHandle; ///< DataHandle to the image for this transfer function. May be 0.
mutable IntensityHistogramType* _intensityHistogram; ///< Intensity histogram of the intensity in _imageHandle for the current _intensityDomain
tbb::atomic<bool> _dirtyHistogram; ///< Flag whether the intensity histogram has to be updated.
mutable tbb::mutex _localMutex; ///< mutex protecting the local members
......
......@@ -68,7 +68,7 @@ namespace TUMVis {
}
_texture->uploadTexture();
_dirty = false;
_dirtyTexture = false;
}
const std::vector<TFGeometry*>& GeometryTransferFunction::getGeometries() const {
......@@ -80,7 +80,7 @@ namespace TUMVis {
tbb::mutex::scoped_lock lock(_localMutex);
_geometries.push_back(geometry);
}
_dirty = true;
_dirtyTexture = true;
s_changed();
}
......
......@@ -70,7 +70,7 @@ namespace TUMVis {
}
_texture->uploadTexture();
_dirty = false;
_dirtyTexture = false;
}
void SimpleTransferFunction::setLeftColor(const tgt::col4& color) {
......@@ -78,7 +78,7 @@ namespace TUMVis {
tbb::mutex::scoped_lock lock(_localMutex);
_leftColor = color;
}
_dirty = true;
_dirtyTexture = true;
s_changed();
}
......@@ -87,7 +87,7 @@ namespace TUMVis {
tbb::mutex::scoped_lock lock(_localMutex);
_rightColor = color;
}
_dirty = true;
_dirtyTexture = true;
s_changed();
}
......
......@@ -28,8 +28,30 @@
#include "imagedatalocal.h"
#include "tbb/include/tbb/tbb.h"
namespace TUMVis {
class IntensityHistogramGenerator {
public:
IntensityHistogramGenerator(const ImageDataLocal* intensityData, ImageDataLocal::IntensityHistogramType* histogram)
: _intensityData(intensityData)
, _histogram(histogram)
{}
void operator() (const tbb::blocked_range<size_t>& range) const {
for (size_t i = range.begin(); i != range.end(); ++i) {
float value = _intensityData->getElementNormalized(i, 0);
_histogram->addSample(&value);
}
}
protected:
const ImageDataLocal* _intensityData;
ImageDataLocal::IntensityHistogramType* _histogram;
};
// ================================================================================================
const std::string ImageDataLocal::loggerCat_ = "TUMVis.core.datastructures.ImageDataLocal";
......@@ -37,10 +59,29 @@ namespace TUMVis {
: ImageData(dimensionality, size)
, _baseType(baseType)
, _numChannels(numChannels)
, _intensityHistogram(0)
{
}
ImageDataLocal::~ImageDataLocal() {
delete _intensityHistogram;
}
const ConcurrentGenericHistogramND<float, 1>& ImageDataLocal::getIntensityHistogram() const {
if (_intensityHistogram == 0)
computeIntensityHistogram();
return *_intensityHistogram;
}
void ImageDataLocal::computeIntensityHistogram() const {
delete _intensityHistogram;
float mins = 0.f;
float maxs = 1.f;
size_t numBuckets = 1024;
_intensityHistogram = new IntensityHistogramType(&mins, &maxs, &numBuckets);
tbb::parallel_for(tbb::blocked_range<size_t>(0, getNumElements()), IntensityHistogramGenerator(this, _intensityHistogram));
}
}
\ No newline at end of file
......@@ -32,6 +32,7 @@
#include "tgt/vector.h"
#include "core/datastructures/imagedata.h"
#include "core/tools/concurrenthistogram.h"
#include "core/tools/endianhelper.h"
#include "core/tools/weaklytypedpointer.h"
......@@ -48,6 +49,8 @@ namespace TUMVis {
*/
class ImageDataLocal : public ImageData {
public:
typedef ConcurrentGenericHistogramND<float, 1> IntensityHistogramType;
/**
* Creates a new ImageData representation in local memory.
*
......@@ -163,10 +166,25 @@ namespace TUMVis {
*/
virtual void setElementNormalized(const tgt::svec3& position, size_t channel, float value) = 0;
/**
* Returns the intensity distribution normalized to float as 1D histogram.
* \note The intensity histogram is computed using lazy evaluation, hence, computation
* may take some time.
* \return _intensityHistogram
*/
const IntensityHistogramType& getIntensityHistogram() const;
protected:
/**
* Computes the intensity histogram;
*/
void computeIntensityHistogram() const;
WeaklyTypedPointer::BaseType _baseType; ///< Base type of the image data
size_t _numChannels; ///< Number of channels per image element.
mutable IntensityHistogramType* _intensityHistogram; ///< Intensity histogram, mutable to allow lazy instantiation
static const std::string loggerCat_;
private:
......
#ifndef CONCURRENTHISTOGRAM_H__
#define CONCURRENTHISTOGRAM_H__
#include "tgt/assert.h"
#include "tgt/logmanager.h"
......@@ -30,6 +33,13 @@ namespace TUMVis {
*/
virtual ~ConcurrentGenericHistogramND();
/**
* Returns the number of buckets for the given dimension.
* \param dimension Dimension, must be smaller than ND.
* \return _numBuckets[dimension]
*/
size_t getNumBuckets(size_t dimension) const;
/**
* Adds the given sample to the histogram.
* \note This method is thread-safe.
......@@ -63,6 +73,12 @@ namespace TUMVis {
*/
size_t getNumSamples() const { return _numSamples; };
/**
* Returns the number of elements in the bucket with the most samples.
* \return _maxFilling
*/
size_t getMaxFilling() const { return _maxFilling; };
protected:
/**
* Transforms the sample value for the given dimension into the corresponding bucket number.
......@@ -85,6 +101,7 @@ namespace TUMVis {
size_t _arraySize; ///< size of _buckets (total number of buckets)
tbb::atomic<size_t>* _buckets; ///< array of the buckets storing the histogram
tbb::atomic<size_t> _numSamples; ///< total number of sampled elements
tbb::atomic<size_t> _maxFilling; ///< number of elements in the bucket with the most samples
};
// ================================================================================================
......@@ -105,6 +122,8 @@ namespace TUMVis {
_buckets = new tbb::atomic<size_t>[_arraySize];
for (size_t i = 0; i < _arraySize; ++i)
_buckets[i] = 0;
_numSamples = 0;
_maxFilling = 0;
}
template<typename T, size_t ND>
......@@ -112,6 +131,12 @@ namespace TUMVis {
delete [] _buckets;
}
template<typename T, size_t ND>
size_t TUMVis::ConcurrentGenericHistogramND<T, ND>::getNumBuckets(size_t dimension) const {
tgtAssert(dimension < ND, "Dimension out of bounds.");
return _numBuckets[dimension];
}
template<typename T, size_t ND>
void TUMVis::ConcurrentGenericHistogramND<T, ND>::addSample(T sample[ND]) {
size_t bucketNumbers[ND];
......@@ -121,8 +146,15 @@ namespace TUMVis {
size_t index = getArrayIndex(bucketNumbers);
++(_buckets[index]);
++_numSamples;
}
// thread-safe update of _maxFilling
size_t old = 0;
do {
old = _maxFilling;
if (old >= _buckets[index])
break;
} while (_maxFilling.compare_and_swap(_buckets[index], old) != old);
}
template<typename T, size_t ND>
size_t TUMVis::ConcurrentGenericHistogramND<T, ND>::getBucketNumber(size_t dimension, T sample) const {
......@@ -148,3 +180,5 @@ namespace TUMVis {
}
#endif // CONCURRENTHISTOGRAM_H__
......@@ -85,7 +85,7 @@ namespace TUMVis {
float integrateHeun(tgt::vec3 position, const tgt::vec4& direction) const {
tgt::vec4 gradient1 = direction;
tgt::vec3 stepSize(.5f);
tgt::vec3 stepSize(.25f);
tgt::vec3 size(_intensities->getSize());
size_t numSteps = 0;
......@@ -172,10 +172,11 @@ namespace TUMVis {
tbb::parallel_for(tbb::blocked_range<size_t>(0, intensities->getNumElements()), LHHistogramGenerator(fl, fh, &lhHistogram));
// TODO: ugly hack...
int16_t* tmp = new int16_t[256*256];
float* tmp = new float[256*256];
for (size_t i = 0; i < 256*256; ++i)
tmp[i] = static_cast<int16_t>(lhHistogram.getBuckets()[i]);
WeaklyTypedPointer wtp(WeaklyTypedPointer::INT16, 1, tmp);
tmp[i] = static_cast<float>(lhHistogram.getBuckets()[i]) / static_cast<float>(lhHistogram.getMaxFilling());
WeaklyTypedPointer wtp(WeaklyTypedPointer::FLOAT, 1, tmp);
ImageDataGL* tex = new ImageDataGL(2, tgt::svec3(256, 256, 1), wtp);
delete [] tmp;
......
......@@ -83,16 +83,17 @@ namespace TUMVis {
_camera.addSharedProperty(&_dvrNormal._camera);
_camera.addSharedProperty(&_dvrVM._camera);
//_imageReader._url.setValue("D:\\Medical Data\\Dentalscan\\dental.mhd");
_imageReader._url.setValue("D:\\Medical Data\\smallHeart.mhd");
_imageReader._targetImageID.setValue("reader.output");
_dvrNormal._targetImageID.setValue("drr.output");
_dvrNormal._sourceImageID.setValue("eep.input");
GeometryTransferFunction* dvrTF = new GeometryTransferFunction(128, tgt::vec2(0.f, .05f));
dvrTF->addGeometry(TFGeometry::createQuad(tgt::vec2(.4f, .42f), tgt::col4(255, 0, 0, 255), tgt::col4(255, 0, 0, 255)));
dvrTF->addGeometry(TFGeometry::createQuad(tgt::vec2(.45f, .5f), tgt::col4(0, 255, 0, 255), tgt::col4(0, 255, 0, 255)));
_dvrNormal._transferFunction.replaceTF(dvrTF);
GeometryTransferFunction* dvrTF = new GeometryTransferFunction(128, tgt::vec2(0.f, .05f));
dvrTF->addGeometry(TFGeometry::createQuad(tgt::vec2(.4f, .42f), tgt::col4(255, 0, 0, 255), tgt::col4(255, 0, 0, 255)));
dvrTF->addGeometry(TFGeometry::createQuad(tgt::vec2(.45f, .5f), tgt::col4(0, 255, 0, 255), tgt::col4(0, 255, 0, 255)));
_dvrNormal._transferFunction.replaceTF(dvrTF);
_dvrVM._targetImageID.setValue("dvr.output");
_dvrVM._sourceImageID.setValue("eep.input");
......@@ -158,32 +159,35 @@ namespace TUMVis {
// convert data
DataContainer::ScopedTypedData<ImageData> img(_data, "reader.output");
ImageDataLocal* local = ImageDataConverter::tryConvert<ImageDataLocal>(img);
if (local != 0) {
_data.addData("clr.input", local);
}
{
tgt::GLContextScopedLock lock(_canvas->getContext());
ImageDataGL* gl = ImageDataConverter::tryConvert<ImageDataGL>(img);
if (gl != 0) {
_data.addData("eep.input", gl);
if (img != 0) {
ImageDataLocal* local = ImageDataConverter::tryConvert<ImageDataLocal>(img);
if (local != 0) {
const DataHandle* dh = _data.addData("clr.input", local);
_dvrNormal._transferFunction.getTF()->setImageHandle(dh);
}
}
CtxtMgr.releaseCurrentContext();
{
tgt::GLContextScopedLock lock(_canvas->getContext());
ImageDataGL* gl = ImageDataConverter::tryConvert<ImageDataGL>(img);
if (gl != 0) {
_data.addData("eep.input", gl);
}
}
CtxtMgr.releaseCurrentContext();
tgt::Bounds volumeExtent = img->getWorldBounds();
tgt::vec3 pos = volumeExtent.center() - tgt::vec3(0, 0, tgt::length(volumeExtent.diagonal()));
_trackballEH->setSceneBounds(volumeExtent);
_trackballEH->setCenter(volumeExtent.center());
_trackballEH->reinitializeCamera(pos, volumeExtent.center(), _camera.getValue().getUpVector());
tgt::Bounds volumeExtent = img->getWorldBounds();
tgt::vec3 pos = volumeExtent.center() - tgt::vec3(0, 0, tgt::length(volumeExtent.diagonal()));
_trackballEH->setSceneBounds(volumeExtent);
_trackballEH->setCenter(volumeExtent.center());
_trackballEH->reinitializeCamera(pos, volumeExtent.center(), _camera.getValue().getUpVector());
}
}
if (! _pgGenerator.getInvalidationLevel().isValid()) {
executeProcessor(&_pgGenerator);
lockGLContextAndExecuteProcessor(&_pgGenerator);
}