Improve convolutional layer rendering.
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.
This commit is contained in:
@@ -19,19 +19,16 @@ MultiImageVisualisation::MultiImageVisualisation(const fmri::LayerData &layer)
|
|||||||
CHECK_EQ(1, images) << "Only single input image is supported" << endl;
|
CHECK_EQ(1, images) << "Only single input image is supported" << endl;
|
||||||
|
|
||||||
nodePositions_.resize(channels * 3);
|
nodePositions_.resize(channels * 3);
|
||||||
auto dataPtr = layer.data();
|
|
||||||
const int columns = numCols(channels);
|
const int columns = numCols(channels);
|
||||||
textureReferences.resize(channels);
|
texture = loadTexture(layer.data(), width, channels * height, channels);
|
||||||
for (auto i : Range(channels)) {
|
for (auto i : Range(channels)) {
|
||||||
textureReferences[i] = loadTexture(dataPtr, width, height);
|
|
||||||
dataPtr += width * height;
|
|
||||||
|
|
||||||
nodePositions_[3 * i + 0] = 0;
|
nodePositions_[3 * i + 0] = 0;
|
||||||
nodePositions_[3 * i + 1] = 3 * (i / columns);
|
nodePositions_[3 * i + 1] = 3 * (i / columns);
|
||||||
nodePositions_[3 * i + 2] = -3 * (i % columns);
|
nodePositions_[3 * i + 2] = -3 * (i % columns);
|
||||||
}
|
}
|
||||||
|
|
||||||
vertexBuffer = std::make_unique<float[]>(channels * BASE_VERTICES.size());
|
vertexBuffer = std::make_unique<float[]>(channels * BASE_VERTICES.size());
|
||||||
|
texCoordBuffer = std::make_unique<float[]>(channels * 2u * BASE_VERTICES.size() / 3);
|
||||||
|
|
||||||
auto v = 0;
|
auto v = 0;
|
||||||
|
|
||||||
@@ -40,35 +37,33 @@ MultiImageVisualisation::MultiImageVisualisation(const fmri::LayerData &layer)
|
|||||||
for (auto j : Range(BASE_VERTICES.size())) {
|
for (auto j : Range(BASE_VERTICES.size())) {
|
||||||
vertexBuffer[v++] = nodePos[j % 3] + BASE_VERTICES[j];
|
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()
|
MultiImageVisualisation::~MultiImageVisualisation()
|
||||||
{
|
{
|
||||||
for (auto entry : textureReferences) {
|
glDeleteTextures(0, &texture);
|
||||||
glDeleteTextures(0, &entry);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void MultiImageVisualisation::render()
|
void MultiImageVisualisation::render()
|
||||||
{
|
{
|
||||||
static const float textureCoords[] = {
|
|
||||||
1, 1,
|
|
||||||
1, 0,
|
|
||||||
0, 0,
|
|
||||||
0, 1,
|
|
||||||
};
|
|
||||||
|
|
||||||
glEnableClientState(GL_TEXTURE_COORD_ARRAY);
|
glEnableClientState(GL_TEXTURE_COORD_ARRAY);
|
||||||
glEnableClientState(GL_VERTEX_ARRAY);
|
glEnableClientState(GL_VERTEX_ARRAY);
|
||||||
glEnable(GL_TEXTURE_2D);
|
glEnable(GL_TEXTURE_2D);
|
||||||
glTexEnvf(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_REPLACE);
|
glTexEnvf(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_REPLACE);
|
||||||
for (auto i : Range(textureReferences.size())) {
|
glBindTexture(GL_TEXTURE_2D, texture);
|
||||||
glBindTexture(GL_TEXTURE_2D, textureReferences[i]);
|
glTexCoordPointer(2, GL_FLOAT, 0, texCoordBuffer.get());
|
||||||
glTexCoordPointer(2, GL_FLOAT, 0, textureCoords);
|
glVertexPointer(3, GL_FLOAT, 0, vertexBuffer.get());
|
||||||
glVertexPointer(3, GL_FLOAT, 0, vertexBuffer.get() + i * BASE_VERTICES.size());
|
glDrawArrays(GL_QUADS, 0, nodePositions_.size() / 3 * 4);
|
||||||
glDrawArrays(GL_QUADS, 0, 4);
|
|
||||||
}
|
|
||||||
glDisable(GL_TEXTURE_2D);
|
glDisable(GL_TEXTURE_2D);
|
||||||
glDisableClientState(GL_VERTEX_ARRAY);
|
glDisableClientState(GL_VERTEX_ARRAY);
|
||||||
glDisableClientState(GL_TEXTURE_COORD_ARRAY);
|
glDisableClientState(GL_TEXTURE_COORD_ARRAY);
|
||||||
|
|||||||
@@ -23,7 +23,8 @@ namespace fmri
|
|||||||
0, 1, 1,
|
0, 1, 1,
|
||||||
0, -1, 1,
|
0, -1, 1,
|
||||||
};
|
};
|
||||||
std::vector<GLuint> textureReferences;
|
GLuint texture;
|
||||||
std::unique_ptr<float[]> vertexBuffer;
|
std::unique_ptr<float[]> vertexBuffer;
|
||||||
|
std::unique_ptr<float[]> texCoordBuffer;
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -7,6 +7,7 @@
|
|||||||
#include <chrono>
|
#include <chrono>
|
||||||
#include <thread>
|
#include <thread>
|
||||||
#include "glutils.hpp"
|
#include "glutils.hpp"
|
||||||
|
#include "Range.hpp"
|
||||||
|
|
||||||
using namespace fmri;
|
using namespace fmri;
|
||||||
using namespace std;
|
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<float>& 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
|
// Load and scale texture
|
||||||
vector<float> textureBuffer(data, data + (width * height));
|
vector<float> 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.
|
const float color[] = {1, 1, 1}; // Background color for textures.
|
||||||
GLuint texture;
|
GLuint texture;
|
||||||
|
|||||||
@@ -12,9 +12,10 @@ namespace fmri {
|
|||||||
* @param data
|
* @param data
|
||||||
* @param width
|
* @param width
|
||||||
* @param height
|
* @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.
|
* @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.
|
* Callback handler to handle resizing windows.
|
||||||
|
|||||||
Reference in New Issue
Block a user