Commit 634d2db1 authored by Christian Schulte zu Berge's avatar Christian Schulte zu Berge
Browse files

Further work on TensorAnalyzer: added computation of various anisotropy measures, yet untested.

parent 1d6510ae
...@@ -32,7 +32,7 @@ ...@@ -32,7 +32,7 @@
#include <tbb/atomic.h> #include <tbb/atomic.h>
#include "core/datastructures/imagedata.h" #include "core/datastructures/imagedata.h"
#include "core/tools/stringutils.h"
namespace campvis { namespace campvis {
...@@ -43,8 +43,32 @@ namespace campvis { ...@@ -43,8 +43,32 @@ namespace campvis {
GenericOption<TensorAnalyzer::DegeneratedEvHandling>("shift", "Shift", TensorAnalyzer::SHIFT) GenericOption<TensorAnalyzer::DegeneratedEvHandling>("shift", "Shift", TensorAnalyzer::SHIFT)
}; };
static const GenericOption<std::string> measurementOptions[15] = {
GenericOption<std::string>("Disabled", "Disabled"),
GenericOption<std::string>("EigenValue1", "Eigenvalue 1"),
GenericOption<std::string>("EigenValue2", "Eigenvalue 2"),
GenericOption<std::string>("EigenValue3", "Eigenvalue 3"),
GenericOption<std::string>("MainEigenvector", "Main Eigenvector"),
GenericOption<std::string>("VolumeRatio", "Volume Ratio"),
GenericOption<std::string>("FractionalAnisotropy", "Fractional Anisotropy"),
GenericOption<std::string>("RelativeAnisotropy", "Relative Anisotropy"),
GenericOption<std::string>("MeanDiffusivity", "Mean Diffusivity"),
GenericOption<std::string>("Trace", "Trace"),
GenericOption<std::string>("AxialDiffusivity", "Axial Diffusivity"),
GenericOption<std::string>("RadialDiffusivity", "Radial Diffusivity"),
GenericOption<std::string>("LinearAnisotropy", "Linear Anisotropy"),
GenericOption<std::string>("PlanarAnisotropy", "Planar Anisotropy"),
GenericOption<std::string>("Isotropy", "Isotropy")
};
const std::string TensorAnalyzer::loggerCat_ = "CAMPVis.modules.classification.TensorAnalyzer"; const std::string TensorAnalyzer::loggerCat_ = "CAMPVis.modules.classification.TensorAnalyzer";
TensorAnalyzer::OutputPropertyPair::OutputPropertyPair(size_t index)
: _imageId("OutputId" + StringUtils::toString(index), "Output " + StringUtils::toString(index) + " Image", "TensorAnalyzer.output" + StringUtils::toString(index), DataNameProperty::WRITE)
, _imageType("OutputType" + StringUtils::toString(index), "Output " + StringUtils::toString(index) + " Image Type", measurementOptions, 15)
{}
TensorAnalyzer::TensorAnalyzer() TensorAnalyzer::TensorAnalyzer()
: AbstractProcessor() : AbstractProcessor()
, p_inputImage("InputImage", "Input Tensor Image", "tensors", DataNameProperty::READ, AbstractProcessor::INVALID_RESULT | EIGENSYSTEM_INVALID) , p_inputImage("InputImage", "Input Tensor Image", "tensors", DataNameProperty::READ, AbstractProcessor::INVALID_RESULT | EIGENSYSTEM_INVALID)
...@@ -52,6 +76,7 @@ namespace campvis { ...@@ -52,6 +76,7 @@ namespace campvis {
, p_evecsImage("EvecsImage", "Output Eigenvectors Image", "eigenvectors", DataNameProperty::WRITE) , p_evecsImage("EvecsImage", "Output Eigenvectors Image", "eigenvectors", DataNameProperty::WRITE)
, p_degeneratedHandling("DegeneratedHandling", "Handling of Degenerated Tensors", handlingModes, 4) , p_degeneratedHandling("DegeneratedHandling", "Handling of Degenerated Tensors", handlingModes, 4)
, p_maskMixedTensors("MaskMixedTensors", "Mask Mixed Tensors", true) , p_maskMixedTensors("MaskMixedTensors", "Mask Mixed Tensors", true)
, p_addOutputButton("AddOutputButton", "Add Output", AbstractProcessor::VALID)
, _eigenvalues(0) , _eigenvalues(0)
, _eigenvectors(0) , _eigenvectors(0)
{ {
...@@ -60,6 +85,10 @@ namespace campvis { ...@@ -60,6 +85,10 @@ namespace campvis {
addProperty(&p_evecsImage); addProperty(&p_evecsImage);
addProperty(&p_degeneratedHandling); addProperty(&p_degeneratedHandling);
addProperty(&p_maskMixedTensors); addProperty(&p_maskMixedTensors);
addProperty(&p_addOutputButton);
addOutput();
p_addOutputButton.s_clicked.connect(this, &TensorAnalyzer::addOutput);
} }
TensorAnalyzer::~TensorAnalyzer() { TensorAnalyzer::~TensorAnalyzer() {
...@@ -72,7 +101,9 @@ namespace campvis { ...@@ -72,7 +101,9 @@ namespace campvis {
} }
if (_eigenvalues.getData() != 0 && _eigenvectors.getData() != 0) { if (_eigenvalues.getData() != 0 && _eigenvectors.getData() != 0) {
for (size_t i = 0; i < p_outputProperties.size(); ++i) {
computeOutput(data, i);
}
} }
else { else {
LERROR("Could not compute Eigensystem"); LERROR("Could not compute Eigensystem");
...@@ -195,4 +226,253 @@ namespace campvis { ...@@ -195,4 +226,253 @@ namespace campvis {
validate(EIGENSYSTEM_INVALID); validate(EIGENSYSTEM_INVALID);
} }
void TensorAnalyzer::computeOutput(DataContainer& data, size_t index) {
// sanity check
if (index >= p_outputProperties.size()) {
LERROR("Index out of bounds while computing output #" << index);
return;
}
// gather eigensystem
const GenericImageRepresentationLocal<float, 3>* evalRep = 0;
const GenericImageRepresentationLocal<float, 9>* evecRep = 0;
if (_eigenvalues.getData() != 0 && _eigenvectors.getData() != 0) {
evalRep = static_cast<const ImageData*>(_eigenvalues.getData())->getRepresentation< GenericImageRepresentationLocal<float, 3> >(false);
evecRep = static_cast<const ImageData*>(_eigenvectors.getData())->getRepresentation< GenericImageRepresentationLocal<float, 9> >(false);
}
if (evalRep == 0 || evecRep == 0) {
LERROR("Could not compute output, no eigensystem present.");
return;
}
OutputPropertyPair* opp = p_outputProperties[index];
const std::string& type = opp->_imageType.getOptionValue();
if (type == "Disabled") {
return;
}
else if (type == "EigenValue1") {
ImageData* id = new ImageData(evalRep->getDimensionality(), evalRep->getSize(), 1);
GenericImageRepresentationLocal<float, 1>* output = GenericImageRepresentationLocal<float, 1>::create(id, 0);
tbb::parallel_for(tbb::blocked_range<size_t>(0, id->getNumElements()), [&] (const tbb::blocked_range<size_t>& range) {
for (size_t i = range.begin(); i != range.end(); ++i) {
const tgt::vec3& vals = evalRep->getElement(i);
float ev = vals.x;
if (ev == 0.0f || tgt::isNaN(ev))
output->setElement(i, 0.0f);
else
output->setElement(i, ev);
}
});
data.addData(opp->_imageId.getValue(), id);
}
else if (type == "EigenValue2") {
ImageData* id = new ImageData(evalRep->getDimensionality(), evalRep->getSize(), 1);
GenericImageRepresentationLocal<float, 1>* output = GenericImageRepresentationLocal<float, 1>::create(id, 0);
tbb::parallel_for(tbb::blocked_range<size_t>(0, id->getNumElements()), [&] (const tbb::blocked_range<size_t>& range) {
for (size_t i = range.begin(); i != range.end(); ++i) {
const tgt::vec3& vals = evalRep->getElement(i);
float ev = vals.y;
if (ev == 0.0f || tgt::isNaN(ev))
output->setElement(i, 0.0f);
else
output->setElement(i, ev);
}
});
data.addData(opp->_imageId.getValue(), id);
}
else if (type == "EigenValue3") {
ImageData* id = new ImageData(evalRep->getDimensionality(), evalRep->getSize(), 1);
GenericImageRepresentationLocal<float, 1>* output = GenericImageRepresentationLocal<float, 1>::create(id, 0);
tbb::parallel_for(tbb::blocked_range<size_t>(0, id->getNumElements()), [&] (const tbb::blocked_range<size_t>& range) {
for (size_t i = range.begin(); i != range.end(); ++i) {
const tgt::vec3& vals = evalRep->getElement(i);
float ev = vals.z;
if (ev == 0.0f || tgt::isNaN(ev))
output->setElement(i, 0.0f);
else
output->setElement(i, ev);
}
});
data.addData(opp->_imageId.getValue(), id);
}
else if (type == "MainEigenvector") {
ImageData* id = new ImageData(evalRep->getDimensionality(), evalRep->getSize(), 3);
GenericImageRepresentationLocal<float, 3>* output = GenericImageRepresentationLocal<float, 3>::create(id, 0);
tbb::parallel_for(tbb::blocked_range<size_t>(0, id->getNumElements()), [&] (const tbb::blocked_range<size_t>& range) {
for (size_t i = range.begin(); i != range.end(); ++i) {
output->setElement(i, evecRep->getElement(i)[0]);
}
});
data.addData(opp->_imageId.getValue(), id);
}
// = Anisotropy Measures: =========================================================================
else if (type == "VolumeRatio") {
ImageData* id = new ImageData(evalRep->getDimensionality(), evalRep->getSize(), 1);
GenericImageRepresentationLocal<float, 1>* output = GenericImageRepresentationLocal<float, 1>::create(id, 0);
tbb::parallel_for(tbb::blocked_range<size_t>(0, id->getNumElements()), [&] (const tbb::blocked_range<size_t>& range) {
for (size_t i = range.begin(); i != range.end(); ++i) {
const tgt::vec3& vals = evalRep->getElement(i);
if (vals == tgt::vec3::zero || tgt::isNaN(vals))
output->getElement(i) = 0;
else
output->getElement(i) = (vals.x * vals.y * vals.z) / pow((vals.x + vals.y + vals.z)/3.f, 3);
}
});
data.addData(opp->_imageId.getValue(), id);
}
else if (type == "FractionalAnisotropy") {
ImageData* id = new ImageData(evalRep->getDimensionality(), evalRep->getSize(), 1);
GenericImageRepresentationLocal<float, 1>* output = GenericImageRepresentationLocal<float, 1>::create(id, 0);
tbb::parallel_for(tbb::blocked_range<size_t>(0, id->getNumElements()), [&] (const tbb::blocked_range<size_t>& range) {
for (size_t i = range.begin(); i != range.end(); ++i) {
const tgt::vec3& vals = evalRep->getElement(i);
const float root = sqrt(.5f);
if (vals == tgt::vec3::zero || tgt::isNaN(vals))
output->getElement(i) = 0;
else
output->getElement(i) = root * sqrt((vals.x-vals.y)*(vals.x-vals.y) + (vals.y-vals.z)*(vals.y-vals.z) + (vals.z-vals.x)*(vals.z-vals.x)) / sqrt(vals.x*vals.x + vals.y*vals.y + vals.z*vals.z);
}
});
data.addData(opp->_imageId.getValue(), id);
}
else if (type == "RelativeAnisotropy") {
ImageData* id = new ImageData(evalRep->getDimensionality(), evalRep->getSize(), 1);
GenericImageRepresentationLocal<float, 1>* output = GenericImageRepresentationLocal<float, 1>::create(id, 0);
tbb::parallel_for(tbb::blocked_range<size_t>(0, id->getNumElements()), [&] (const tbb::blocked_range<size_t>& range) {
for (size_t i = range.begin(); i != range.end(); ++i) {
const tgt::vec3& vals = evalRep->getElement(i);
const float root = sqrt(.5f);
if (vals == tgt::vec3::zero || tgt::isNaN(vals))
output->getElement(i) = 0;
else
output->getElement(i) = root * sqrt((vals.x-vals.y)*(vals.x-vals.y) + (vals.y-vals.z)*(vals.y-vals.z) + (vals.z-vals.x)*(vals.z-vals.x)) / (vals.x + vals.y + vals.z);
}
});
data.addData(opp->_imageId.getValue(), id);
}
else if (type == "MeanDiffusivity") {
ImageData* id = new ImageData(evalRep->getDimensionality(), evalRep->getSize(), 1);
GenericImageRepresentationLocal<float, 1>* output = GenericImageRepresentationLocal<float, 1>::create(id, 0);
tbb::parallel_for(tbb::blocked_range<size_t>(0, id->getNumElements()), [&] (const tbb::blocked_range<size_t>& range) {
for (size_t i = range.begin(); i != range.end(); ++i) {
const tgt::vec3& vals = evalRep->getElement(i);
if (vals == tgt::vec3::zero || tgt::isNaN(vals))
output->getElement(i) = 0;
else
output->getElement(i) = (vals.x + vals.y + vals.z) / 3;
}
});
data.addData(opp->_imageId.getValue(), id);
}
else if (type == "Trace") {
ImageData* id = new ImageData(evalRep->getDimensionality(), evalRep->getSize(), 1);
GenericImageRepresentationLocal<float, 1>* output = GenericImageRepresentationLocal<float, 1>::create(id, 0);
tbb::parallel_for(tbb::blocked_range<size_t>(0, id->getNumElements()), [&] (const tbb::blocked_range<size_t>& range) {
for (size_t i = range.begin(); i != range.end(); ++i) {
const tgt::vec3& vals = evalRep->getElement(i);
if (vals == tgt::vec3::zero || tgt::isNaN(vals))
output->getElement(i) = 0;
else
output->getElement(i) = vals.x + vals.y + vals.z;
}
});
data.addData(opp->_imageId.getValue(), id);
}
else if (type == "AxialDiffusivity") {
ImageData* id = new ImageData(evalRep->getDimensionality(), evalRep->getSize(), 1);
GenericImageRepresentationLocal<float, 1>* output = GenericImageRepresentationLocal<float, 1>::create(id, 0);
tbb::parallel_for(tbb::blocked_range<size_t>(0, id->getNumElements()), [&] (const tbb::blocked_range<size_t>& range) {
for (size_t i = range.begin(); i != range.end(); ++i) {
const tgt::vec3& vals = evalRep->getElement(i);
if (vals == tgt::vec3::zero || tgt::isNaN(vals))
output->getElement(i) = 0;
else
output->getElement(i) = vals.x;
}
});
data.addData(opp->_imageId.getValue(), id);
}
else if (type == "RadialDiffusivity") {
ImageData* id = new ImageData(evalRep->getDimensionality(), evalRep->getSize(), 1);
GenericImageRepresentationLocal<float, 1>* output = GenericImageRepresentationLocal<float, 1>::create(id, 0);
tbb::parallel_for(tbb::blocked_range<size_t>(0, id->getNumElements()), [&] (const tbb::blocked_range<size_t>& range) {
for (size_t i = range.begin(); i != range.end(); ++i) {
const tgt::vec3& vals = evalRep->getElement(i);
if (vals == tgt::vec3::zero || tgt::isNaN(vals))
output->getElement(i) = 0;
else
output->getElement(i) = (vals.y + vals.z) / 2;
}
});
data.addData(opp->_imageId.getValue(), id);
}
else if (type == "LinearAnisotropy") {
ImageData* id = new ImageData(evalRep->getDimensionality(), evalRep->getSize(), 1);
GenericImageRepresentationLocal<float, 1>* output = GenericImageRepresentationLocal<float, 1>::create(id, 0);
tbb::parallel_for(tbb::blocked_range<size_t>(0, id->getNumElements()), [&] (const tbb::blocked_range<size_t>& range) {
for (size_t i = range.begin(); i != range.end(); ++i) {
const tgt::vec3& vals = evalRep->getElement(i);
if (vals == tgt::vec3::zero || tgt::isNaN(vals))
output->getElement(i) = 0;
else
output->getElement(i) = (vals.x - vals.y) / (vals.x + vals.y + vals.z);
}
});
data.addData(opp->_imageId.getValue(), id);
}
else if (type == "PlanarAnisotropy") {
ImageData* id = new ImageData(evalRep->getDimensionality(), evalRep->getSize(), 1);
GenericImageRepresentationLocal<float, 1>* output = GenericImageRepresentationLocal<float, 1>::create(id, 0);
tbb::parallel_for(tbb::blocked_range<size_t>(0, id->getNumElements()), [&] (const tbb::blocked_range<size_t>& range) {
for (size_t i = range.begin(); i != range.end(); ++i) {
const tgt::vec3& vals = evalRep->getElement(i);
if (vals == tgt::vec3::zero || tgt::isNaN(vals))
output->getElement(i) = 0;
else
output->getElement(i) = 2.f*(vals.y - vals.z) / (vals.x + vals.y + vals.z);
}
});
data.addData(opp->_imageId.getValue(), id);
}
else if (type == "Isotropy") {
ImageData* id = new ImageData(evalRep->getDimensionality(), evalRep->getSize(), 1);
GenericImageRepresentationLocal<float, 1>* output = GenericImageRepresentationLocal<float, 1>::create(id, 0);
tbb::parallel_for(tbb::blocked_range<size_t>(0, id->getNumElements()), [&] (const tbb::blocked_range<size_t>& range) {
for (size_t i = range.begin(); i != range.end(); ++i) {
const tgt::vec3& vals = evalRep->getElement(i);
if (vals == tgt::vec3::zero || tgt::isNaN(vals))
output->getElement(i) = 0;
else
output->getElement(i) = (3.f*vals.z) / (vals.x + vals.y + vals.z);
}
});
data.addData(opp->_imageId.getValue(), id);
}
}
void TensorAnalyzer::addOutput() {
OutputPropertyPair* opp = new OutputPropertyPair(p_outputProperties.size() + 1);
p_outputProperties.push_back(opp);
addProperty(&opp->_imageId);
addProperty(&opp->_imageType);
}
} }
...@@ -29,6 +29,7 @@ ...@@ -29,6 +29,7 @@
#include "core/pipeline/visualizationprocessor.h" #include "core/pipeline/visualizationprocessor.h"
#include "core/properties/datanameproperty.h" #include "core/properties/datanameproperty.h"
#include "core/properties/buttonproperty.h"
#include "core/properties/genericproperty.h" #include "core/properties/genericproperty.h"
#include "core/properties/floatingpointproperty.h" #include "core/properties/floatingpointproperty.h"
#include "core/properties/numericproperty.h" #include "core/properties/numericproperty.h"
...@@ -51,6 +52,14 @@ namespace campvis { ...@@ -51,6 +52,14 @@ namespace campvis {
NONE, MASK, INVERT, SHIFT NONE, MASK, INVERT, SHIFT
}; };
// Pair of DataNameProperty for output image ID and OptionProperty for image type
struct OutputPropertyPair {
OutputPropertyPair(size_t index);;
DataNameProperty _imageId;
GenericOptionProperty<std::string> _imageType;
};
/** /**
* Constructs a new TensorAnalyzer Processor * Constructs a new TensorAnalyzer Processor
**/ **/
...@@ -79,6 +88,9 @@ namespace campvis { ...@@ -79,6 +88,9 @@ namespace campvis {
GenericOptionProperty<DegeneratedEvHandling> p_degeneratedHandling; ///< Handling of degenerated tensors GenericOptionProperty<DegeneratedEvHandling> p_degeneratedHandling; ///< Handling of degenerated tensors
BoolProperty p_maskMixedTensors; BoolProperty p_maskMixedTensors;
ButtonProperty p_addOutputButton;
std::vector<OutputPropertyPair*> p_outputProperties;
protected: protected:
/** /**
...@@ -88,6 +100,18 @@ namespace campvis { ...@@ -88,6 +100,18 @@ namespace campvis {
*/ */
void computeEigensystem(DataContainer& data); void computeEigensystem(DataContainer& data);
/**
* Computes the derived measurement for output number \a index.
* \param data DataContainer to store output image in.
* \param index Index of output to compute.
*/
void computeOutput(DataContainer& data, size_t index);
/**
* Adds another output for this processor (i.e. adds another OutputPropertyPair).
*/
void addOutput();
DataHandle _eigenvalues; ///< Current eigenvalues cached DataHandle _eigenvalues; ///< Current eigenvalues cached
DataHandle _eigenvectors; ///< Current eigenvectors cached DataHandle _eigenvectors; ///< Current eigenvectors cached
......
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