Notice to GitKraken users: A vulnerability has been found in the SSH key generation of GitKraken versions 7.6.0 to 8.0.0 (https://www.gitkraken.com/blog/weak-ssh-key-fix). If you use GitKraken and have generated a SSH key using one of these versions, please remove it both from your local workstation and from your LRZ GitLab profile.

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

Commit 5f0fb418 authored by Christian Schulte zu Berge's avatar Christian Schulte zu Berge
Browse files

Fixed computation of SSIM in GlStructuralSimilarity processor. Extended...

Fixed computation of SSIM in GlStructuralSimilarity processor. Extended SsimDemo pipeline to compute batch-wise SSIM of an entire US sequence.
parent 6cb8b21a
......@@ -33,36 +33,37 @@ uniform TextureParameters2D _image1Params;
uniform sampler2D _image2;
uniform TextureParameters2D _image2Params;
#define WINDOW_SIZE 4
#define WINDOW_SIZE_SQ 16
#define HALF_WINDOW_SIZE 4
#define WINDOW_SIZE 9
#define WINDOW_SIZE_SQ 81
void main() {
vec4 toReturn = vec4(0.0);
const float L = 255;
const float L = 128;
const float c1 = (0.01 * L) * (0.01 * L);
const float c2 = (0.03 * L) * (0.03 * L);
vec4 xMean = vec4(0);
vec4 yMean = vec4(0);
vec4 xM2 = vec4(0);
vec4 yM2 = vec4(0);
vec4 cov = vec4(0);
vec4 xM2 = vec4(0);
vec4 yM2 = vec4(0);
vec4 cov = vec4(0);
float n = 0;
for (int i = -WINDOW_SIZE_SQ; i <= WINDOW_SIZE_SQ; ++i) {
vec2 iOffset = vec2(i % WINDOW_SIZE, i / WINDOW_SIZE) * _image1Params._sizeRCP;
vec4 xi = texture(_image1, ex_TexCoord.xy + iOffset);
vec4 yi = texture(_image2, ex_TexCoord.xy + iOffset);
vec4 mean1 = vec4(0);
vec4 mean2 = vec4(0);
vec4 M12 = vec4(0);
float NN = WINDOW_SIZE_SQ;
for (int j = i+1; j <= WINDOW_SIZE_SQ; ++j) {
vec2 jOffset = vec2(j % WINDOW_SIZE, j / WINDOW_SIZE) * _image1Params._sizeRCP;
vec4 xj = texture(_image1, ex_TexCoord.xy + jOffset);
vec4 yj = texture(_image2, ex_TexCoord.xy + jOffset);
vec2 shift = _image1Params._sizeRCP * HALF_WINDOW_SIZE;
float mult = _image1Params._sizeRCP;
cov += (xi - xj) * (yi - yj);
}
for (int i = 0; i <= WINDOW_SIZE_SQ; ++i) {
vec2 iOffset = (vec2(i % WINDOW_SIZE, i / WINDOW_SIZE) * mult) - shift;
vec4 xi = texture(_image1, ex_TexCoord.xy + iOffset);
vec4 yi = texture(_image2, ex_TexCoord.xy + iOffset);
// compute mean and variance with Knuth's algorithm
// https://en.wikipedia.org/wiki/Algorithms_for_calculating_variance
......@@ -75,11 +76,22 @@ void main() {
vec4 yDelta = yi - yMean;
yMean += yDelta/n;
yM2 += yDelta * (yi - yMean);
// compute the covariance
vec4 delta1 = (xi - mean1) / n;
mean1 += delta1;
vec4 delta2 = (yi - mean2) / n;
mean2 += delta2;
M12 += n * delta1 * delta2 - M12 / n;
}
cov = NN / (NN - 1) * M12;
vec4 xVar = xM2 / (n-1);
vec4 yVar = yM2 / (n-1);
vec4 ssim = ((2*xMean*yMean + c1) * (2*cov + c2)) / ((xMean * xMean + yMean * yMean + c1) * (xVar + yVar + c2));
out_Color = vec4(ssim);
out_Color = min(vec4(1.0), vec4(ssim));
}
......@@ -24,20 +24,40 @@
#include "ssimdemo.h"
#include "cgt/glcontextmanager.h"
#include "cgt/shadermanager.h"
#include "core/datastructures/imagerepresentationgl.h"
#include <iomanip>
#include <fstream>
#include <sstream>
namespace campvis {
SsimDemo::SsimDemo(DataContainer& dc)
: AutoEvaluationPipeline(dc, getId())
, _imageReader()
, _gaussian(&_canvasSize)
, _imageReader1()
, _imageReader2()
, _ssim(&_canvasSize)
, _fanRenderer(&_canvasSize)
, _sumReduction(nullptr)
, _minReduction(nullptr)
, _currentlyBatchProcessing(false)
, p_sourcePath1("SourcePath1", "Source Path 1", "Z:/cm_stuff/cmCuda_512_1000", StringProperty::DIRECTORY)
, p_sourcePath2("SourcePath2", "Source Path 2", "Z:/cm_stuff/cmCuda_128_1000", StringProperty::DIRECTORY)
, p_range("Range", "range", cgt::ivec2(0, 399), cgt::ivec2(0), cgt::ivec2(1000))
, p_execute("Excecute", "Execute Batch Process")
{
addProcessor(&_imageReader);
addProcessor(&_gaussian);
addProcessor(&_imageReader1);
addProcessor(&_imageReader2);
addProcessor(&_ssim);
addProcessor(&_fanRenderer);
addProperty(p_sourcePath1);
addProperty(p_sourcePath2);
addProperty(p_range);
addProperty(p_execute);
p_execute.s_clicked.connect(this, &SsimDemo::executeBatchProcess);
}
SsimDemo::~SsimDemo() {
......@@ -46,15 +66,99 @@ namespace campvis {
void SsimDemo::init() {
AutoEvaluationPipeline::init();
_ssim.p_outputImage.setValue("result");
_renderTargetID.setValue("result");
_sumReduction = new GlReduction(GlReduction::PLUS);
_minReduction = new GlReduction(GlReduction::MIN);
_renderTargetID.setValue("us.fan");
_imageReader1.p_url.setValue("Z:/cm_stuff/cmCuda_512_1000/export0026.bmp");
_imageReader1.p_importType.setValue(1);
_imageReader1.p_targetImageID.setValue("reader1.output");
_imageReader2.p_url.setValue("Z:/cm_stuff/cmCuda_128_1000/export0026.bmp");
_imageReader2.p_importType.setValue(1);
_imageReader2.p_targetImageID.setValue("reader2.output");
_ssim.p_inputImage1.setValue("reader1.output");
_ssim.p_inputImage2.setValue("reader2.output");
_ssim.s_validated.connect(this, &SsimDemo::onProcessorValidated);
_ssim.p_outputImage.setValue("ssim");
_fanRenderer.p_inputImage.setValue("ssim");
_fanRenderer.p_renderTargetID.setValue("us.fan");
_imageReader.p_url.setValue(ShdrMgr.completePath("/modules/"));
_imageReader.p_targetImageID.setValue("reader.output");
_imageReader.p_targetImageID.addSharedProperty(&_gaussian.p_inputImage);
}
void SsimDemo::deinit() {
delete _minReduction;
delete _sumReduction;
AutoEvaluationPipeline::deinit();
}
_gaussian.p_outputImage.setValue("blurred");
void SsimDemo::onProcessorValidated(AbstractProcessor* p) {
if (_currentlyBatchProcessing)
return;
if (p == &_ssim) {
ImageRepresentationGL::ScopedRepresentation ssim(getDataContainer(), _ssim.p_outputImage.getValue());
if (ssim) {
cgt::GLContextScopedLock lock(this->_canvas);
auto sums = _sumReduction->reduce(ssim->getTexture());
auto mins = _minReduction->reduce(ssim->getTexture());
LINFO("Structured Similarity, Average: " << (sums[0] / ssim->getNumElements()) << ", Minimum: " << mins[0]);
}
}
}
void SsimDemo::executeBatchProcess() {
if (p_range.getValue().x > p_range.getValue().y)
return;
_currentlyBatchProcessing = true;
cgt::GLContextScopedLock lock(_canvas);
float sumsum = 0.f;
float minmin = 1.f;
std::string foo = p_sourcePath1.getValue().substr(p_sourcePath1.getValue().find_last_of("/\\") + 1);
std::string bar = p_sourcePath2.getValue().substr(p_sourcePath2.getValue().find_last_of("/\\") + 1);
LINFO("Comparing " << p_sourcePath1.getValue() << " to " << p_sourcePath2.getValue() << ":");
std::ofstream csvFile("C:\\temp\\" + foo + "---" + bar + ".csv");
csvFile << "index, AvgSSIM, MinSSIM\n";
for (int i = p_range.getValue().x; i < p_range.getValue().y; ++i) {
// set up processors:
std::stringstream ss;
ss << "export" << std::setfill('0') << std::setw(4) << i << ".bmp";
std::string fileName = ss.str();
// read image
_imageReader1.p_url.setValue(p_sourcePath1.getValue() + "\\" + fileName);
_imageReader2.p_url.setValue(p_sourcePath2.getValue() + "\\" + fileName);
forceExecuteProcessor(&_imageReader1);
forceExecuteProcessor(&_imageReader2);
forceExecuteProcessor(&_ssim);
ImageRepresentationGL::ScopedRepresentation ssim(getDataContainer(), _ssim.p_outputImage.getValue());
if (ssim) {
auto sums = _sumReduction->reduce(ssim->getTexture());
auto mins = _minReduction->reduce(ssim->getTexture());
sumsum += sums[0] / ssim->getNumElements();
minmin = std::min(minmin, mins[0]);
csvFile << i << ", " << (sums[0] / ssim->getNumElements()) << ", " << mins[0] << "\n";
}
if (i % 100 == 0)
LINFO(i);
}
LINFO("Structured Similarity, Averaged average: " << (sumsum / (p_range.getValue().y - p_range.getValue().x)) << ", Minimum of the Minima: " << minmin);
_currentlyBatchProcessing = false;
}
}
\ No newline at end of file
......@@ -25,12 +25,14 @@
#ifndef SSIMDEMO_H__
#define SSIMDEMO_H__
#include "core/tools/glreduction.h"
#include "core/pipeline/autoevaluationpipeline.h"
#include "modules/modulesapi.h"
#include "modules/devil/processors/devilimagereader.h"
#include "modules/preprocessing/processors/glgaussianfilter.h"
#include "modules/preprocessing/processors/glstructuralsimilarity.h"
#include "modules/vis/processors/usfanrenderer.h"
namespace campvis {
class CAMPVIS_MODULES_API SsimDemo : public AutoEvaluationPipeline {
......@@ -47,16 +49,31 @@ namespace campvis {
**/
virtual ~SsimDemo();
/// \see AutoEvaluationPipeline::init()
virtual void init();
virtual void init() override;
virtual void deinit() override;
static const std::string getId() { return "SsimDemo"; };
protected:
DevilImageReader _imageReader;
GlGaussianFilter _gaussian;
void onProcessorValidated(AbstractProcessor* p);
void executeBatchProcess();
DevilImageReader _imageReader1;
DevilImageReader _imageReader2;
GlStructuralSimilarity _ssim;
UsFanRenderer _fanRenderer;
GlReduction* _sumReduction;
GlReduction* _minReduction;
bool _currentlyBatchProcessing;
StringProperty p_sourcePath1;
StringProperty p_sourcePath2;
IVec2Property p_range; ///< Range for image iteration
ButtonProperty p_execute; ///< Button to start the batch process
};
}
......
......@@ -58,7 +58,7 @@ namespace campvis {
void GlStructuralSimilarity::init() {
VisualizationProcessor::init();
_shader2D = ShdrMgr.load("core/glsl/passthrough.vert", "modules/preprocessing/glsl/glstructuralsimilarity.frag", "");
_shader2D = ShdrMgr.loadWithCustomGlslVersion("core/glsl/passthrough.vert", "", "modules/preprocessing/glsl/glstructuralsimilarity.frag", "", "400");
}
void GlStructuralSimilarity::deinit() {
......@@ -77,7 +77,7 @@ namespace campvis {
img1Unit.activate();
// create texture for result
cgt::Texture* resultTexture = new cgt::Texture(GL_TEXTURE_2D, originalSize, img1->getTexture()->getInternalFormat(), cgt::Texture::LINEAR);
cgt::Texture* resultTexture = new cgt::Texture(GL_TEXTURE_2D, originalSize, GL_R32F, cgt::Texture::LINEAR);
LGL_ERROR;
// activate shader and bind textures
......@@ -99,7 +99,7 @@ namespace campvis {
_shader2D->deactivate();
// put resulting image into DataContainer
ImageData* id = new ImageData(img1->getParent()->getDimensionality(), originalSize, img1->getParent()->getNumChannels());
ImageData* id = new ImageData(img1->getParent()->getDimensionality(), originalSize, 1);
ImageRepresentationGL::create(id, resultTexture);
id->setMappingInformation(img1->getParent()->getMappingInformation());
data.addData(p_outputImage.getValue(), id);
......
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