From 99cf0740fe9c5afb2f5bf9f12fce54a5bb053db6 Mon Sep 17 00:00:00 2001 From: Bert Peters Date: Fri, 23 Feb 2018 15:19:30 +0100 Subject: [PATCH] Improve convolutional layer rendering. MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Now all n individual images of w×h are packaged as a w×(n×h) image, with the texture coordinates properly mapped so the individual rectangles still get the proper texture. This optimization causes slight glitches around the edges of the tiles, but is so much faster that I think it's worth it. --- src/MultiImageVisualisation.cpp | 37 ++++++++++++++------------------- src/MultiImageVisualisation.hpp | 3 ++- src/glutils.cpp | 15 +++++++++++-- src/glutils.hpp | 3 ++- 4 files changed, 33 insertions(+), 25 deletions(-) diff --git a/src/MultiImageVisualisation.cpp b/src/MultiImageVisualisation.cpp index 0d6e9fb..fb84795 100644 --- a/src/MultiImageVisualisation.cpp +++ b/src/MultiImageVisualisation.cpp @@ -19,19 +19,16 @@ MultiImageVisualisation::MultiImageVisualisation(const fmri::LayerData &layer) CHECK_EQ(1, images) << "Only single input image is supported" << endl; nodePositions_.resize(channels * 3); - auto dataPtr = layer.data(); const int columns = numCols(channels); - textureReferences.resize(channels); + texture = loadTexture(layer.data(), width, channels * height, channels); for (auto i : Range(channels)) { - textureReferences[i] = loadTexture(dataPtr, width, height); - dataPtr += width * height; - nodePositions_[3 * i + 0] = 0; nodePositions_[3 * i + 1] = 3 * (i / columns); nodePositions_[3 * i + 2] = -3 * (i % columns); } vertexBuffer = std::make_unique(channels * BASE_VERTICES.size()); + texCoordBuffer = std::make_unique(channels * 2u * BASE_VERTICES.size() / 3); auto v = 0; @@ -40,35 +37,33 @@ MultiImageVisualisation::MultiImageVisualisation(const fmri::LayerData &layer) for (auto j : Range(BASE_VERTICES.size())) { vertexBuffer[v++] = nodePos[j % 3] + BASE_VERTICES[j]; } + + const float textureCoords[] = { + 1, (i + 1) / (float) channels, + 1, i / (float) channels, + 0, i / (float) channels, + 0, (i + 1) / (float) channels, + }; + + memcpy(texCoordBuffer.get() + 8 * i, textureCoords, sizeof(textureCoords)); } } MultiImageVisualisation::~MultiImageVisualisation() { - for (auto entry : textureReferences) { - glDeleteTextures(0, &entry); - } + glDeleteTextures(0, &texture); } void MultiImageVisualisation::render() { - static const float textureCoords[] = { - 1, 1, - 1, 0, - 0, 0, - 0, 1, - }; - glEnableClientState(GL_TEXTURE_COORD_ARRAY); glEnableClientState(GL_VERTEX_ARRAY); glEnable(GL_TEXTURE_2D); glTexEnvf(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_REPLACE); - for (auto i : Range(textureReferences.size())) { - glBindTexture(GL_TEXTURE_2D, textureReferences[i]); - glTexCoordPointer(2, GL_FLOAT, 0, textureCoords); - glVertexPointer(3, GL_FLOAT, 0, vertexBuffer.get() + i * BASE_VERTICES.size()); - glDrawArrays(GL_QUADS, 0, 4); - } + glBindTexture(GL_TEXTURE_2D, texture); + glTexCoordPointer(2, GL_FLOAT, 0, texCoordBuffer.get()); + glVertexPointer(3, GL_FLOAT, 0, vertexBuffer.get()); + glDrawArrays(GL_QUADS, 0, nodePositions_.size() / 3 * 4); glDisable(GL_TEXTURE_2D); glDisableClientState(GL_VERTEX_ARRAY); glDisableClientState(GL_TEXTURE_COORD_ARRAY); diff --git a/src/MultiImageVisualisation.hpp b/src/MultiImageVisualisation.hpp index 70eb7d1..6ebf0dd 100644 --- a/src/MultiImageVisualisation.hpp +++ b/src/MultiImageVisualisation.hpp @@ -23,7 +23,8 @@ namespace fmri 0, 1, 1, 0, -1, 1, }; - std::vector textureReferences; + GLuint texture; std::unique_ptr vertexBuffer; + std::unique_ptr texCoordBuffer; }; } diff --git a/src/glutils.cpp b/src/glutils.cpp index 7ad62d5..d86e3e5 100644 --- a/src/glutils.cpp +++ b/src/glutils.cpp @@ -7,6 +7,7 @@ #include #include #include "glutils.hpp" +#include "Range.hpp" using namespace fmri; using namespace std; @@ -21,11 +22,21 @@ static void handleGLError(GLenum error) { } } -GLuint fmri::loadTexture(DType const *data, int width, int height) +static void rescaleSubImages(vector& textureBuffer, int subImages) { + auto cur = textureBuffer.begin(); + const auto increment = textureBuffer.size() / subImages; + + while (cur != textureBuffer.end()) { + rescale(cur, cur + increment, 0, 1); + advance(cur, increment); + } +} + +GLuint fmri::loadTexture(DType const *data, int width, int height, int subImages) { // Load and scale texture vector textureBuffer(data, data + (width * height)); - rescale(textureBuffer.begin(), textureBuffer.end(), 0, 1); + rescaleSubImages(textureBuffer, subImages); const float color[] = {1, 1, 1}; // Background color for textures. GLuint texture; diff --git a/src/glutils.hpp b/src/glutils.hpp index fa28a00..a42e29a 100644 --- a/src/glutils.hpp +++ b/src/glutils.hpp @@ -12,9 +12,10 @@ namespace fmri { * @param data * @param width * @param height + * @param subImages Number of subimages in the original image. Sub images are rescaled individually to preserve contrast. Optional, default 1. * @return A texture reference. */ - GLuint loadTexture(DType const * data, int width, int height); + GLuint loadTexture(DType const *data, int width, int height, int subImages = 1); /** * Callback handler to handle resizing windows.