【问题标题】:Calculate histogram in OPENGL在 OPENGL 中计算直方图
【发布时间】:2017-04-01 17:29:00
【问题描述】:

我尝试使用 OPENGL 计算图像的直方图。我已经阅读了一些关于此的帖子,但我仍然得到错误。我的直方图缓冲区总是返回零。 我读过的一些帖子:

Maknoll Histogram

Luminance histogram calculation in GPU-android opengl es 3.0

我的代码:

#include <stdio.h>
#include <stdlib.h>
#include <iostream>
#include <vector>

// Include GLEW
#include <GL/glew.h>

// Include GLFW
#include <GLFW/glfw3.h>
GLFWwindow* window;

// Include GLM
#include <glm/glm.hpp>
using namespace glm;
#include <SOIL.h>

// Shader sources
const GLchar* vertexSource = "\n"
"#version 330 core\n"
"attribute vec3 inPosition;\n"
"void main()\n"
"{\n"
"    float x = inPosition.x;\n"
"\n"
"    gl_Position = vec4(\n"
"        -1.0 + ((x) * 0.0078125),\n"
"        -1,\n"
"        0.0,\n"
"        1.0\n"
"    );\n"
"}\n";

const GLchar* fragmentSource = "\n"
"#version 330 core\n"
"out vec4 outputColor;\n"
"void main()\n"
"{\n"
"    outputColor = vec4(1.0, 1.0, 1.0, 1.0);\n"
"}\n";

void CheckStatus(GLuint obj)
{
    GLint status = GL_FALSE, len = 10;
    if( glIsShader(obj) )   glGetShaderiv( obj, GL_COMPILE_STATUS, &status );
    if( glIsProgram(obj) )  glGetProgramiv( obj, GL_LINK_STATUS, &status );
    if( status == GL_TRUE ) return;
    if( glIsShader(obj) )   glGetShaderiv( obj, GL_INFO_LOG_LENGTH, &len );
    if( glIsProgram(obj) )  glGetProgramiv( obj, GL_INFO_LOG_LENGTH, &len );
    std::vector< char > log( len, 'X' );
    if( glIsShader(obj) )   glGetShaderInfoLog( obj, len, NULL, &log[0] );
    if( glIsProgram(obj) )  glGetProgramInfoLog( obj, len, NULL, &log[0] );
    std::cerr << &log[0] << std::endl;
    exit( -1 );
}

GLfloat buffer[256];
GLuint hist[256];
float _image[512*512*3];

int main()
{
    // Initialise GLFW
    if( !glfwInit() )
    {
        fprintf( stderr, "Failed to initialize GLFW\n" );
        getchar();
        return -1;
    }

    glfwWindowHint(GLFW_SAMPLES, 4);
    glfwWindowHint(GLFW_CONTEXT_VERSION_MAJOR, 3);
    glfwWindowHint(GLFW_CONTEXT_VERSION_MINOR, 3);
    glfwWindowHint(GLFW_OPENGL_FORWARD_COMPAT, GL_TRUE); // To make MacOS happy; should not be needed
    glfwWindowHint(GLFW_OPENGL_PROFILE, GLFW_OPENGL_CORE_PROFILE);

    int width, height;
    unsigned char* image = SOIL_load_image("sample_gray.bmp", &width, &height, 0, SOIL_LOAD_RGB);
    unsigned char image_gray[width * height];
    printf("%d\t%d\n", width, height);

    for (int i = 0; i < width * height; ++i)
    {
        image_gray[i] = image[i * 3];
        _image[i * 3] = image[i * 3];
        _image[i * 3 + 1] = image[i * 3 + 1];
        _image[i * 3 + 2] = image[i * 3 + 2];
    }

    for (int i = 0; i < width * height; ++i)
    {
        hist[image_gray[i]]++;
    }

    // Open a window and create its OpenGL context
    window = glfwCreateWindow(width, height, "Basic", NULL, NULL);
    if( window == NULL ){
        fprintf( stderr, "Failed to open GLFW window. If you have an Intel GPU, they are not 3.3 compatible. Try the 2.1 version of the tutorials.\n" );
        getchar();
        glfwTerminate();
        return -1;
    }
    glfwMakeContextCurrent(window);

    // Initialize GLEW
    glewExperimental = true; // Needed for core profile
    if (glewInit() != GLEW_OK) {
        fprintf(stderr, "Failed to initialize GLEW\n");
        getchar();
        glfwTerminate();
        return -1;
    }

    // Ensure we can capture the escape key being pressed below
    glfwSetInputMode(window, GLFW_STICKY_KEYS, GL_TRUE);

    // Dark blue background
    glClearColor(0.0f, 0.0f, 0.0f, 0.0f);

    GLuint vbo;
    glGenBuffers(1, &vbo);
    glBindBuffer(GL_ARRAY_BUFFER, vbo);

    GLuint num_input_data = width * height;

    /* Upload data */
    glBufferData(GL_ARRAY_BUFFER, num_input_data * sizeof(float) * 3, _image, GL_STATIC_DRAW);

    GLuint vertexShader, fragmentShader, shaderProgram;
    // Create and compile the vertex shader
    vertexShader = glCreateShader(GL_VERTEX_SHADER);
    glShaderSource(vertexShader, 1, &vertexSource, NULL);
    glCompileShader(vertexShader);
    CheckStatus(vertexShader);
    // Create and compile the fragment shader
    fragmentShader = glCreateShader(GL_FRAGMENT_SHADER);
    glShaderSource(fragmentShader, 1, &fragmentSource, NULL);
    glCompileShader(fragmentShader);
    CheckStatus(fragmentShader);
    // Link the vertex and fragment shader into a shader program
    shaderProgram = glCreateProgram();
    glAttachShader(shaderProgram, vertexShader);
    glAttachShader(shaderProgram, fragmentShader);
    glBindFragDataLocation(shaderProgram, 0, "outputColor");
    glLinkProgram(shaderProgram);


    CheckStatus(shaderProgram);

    glUseProgram(shaderProgram);


    // Specify the layout of the vertex data
    GLint posAttrib = glGetAttribLocation(shaderProgram, "inPosition");
    glEnableVertexAttribArray(posAttrib);
    glVertexAttribPointer(posAttrib, 3, GL_FLOAT, GL_FALSE, 3 * sizeof(GLfloat), 0);

    GLuint tex;
    GLuint fbo;
    glGenTextures(1, &tex);
    glBindFramebuffer(GL_FRAMEBUFFER, fbo);

    glGenFramebuffers(1, &fbo);
    glActiveTexture(GL_TEXTURE0);
    // glBindTexture(GL_TEXTURE_2D, tex);
    glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
    glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
    glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
    glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
    glTexStorage2D(GL_TEXTURE_2D, 1, GL_R32F, 256, 1);
    // glBindTexture(GL_TEXTURE_2D, 0);

    glFramebufferTexture(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, tex, 0);

    /* Clear buffer */
    glClearColor(1.0, 0.0, 0.0, 1.0);
    glClear(GL_COLOR_BUFFER_BIT);

    glBlendEquation(GL_FUNC_ADD);
    glBlendFunc(GL_ONE, GL_ONE);
    glEnable(GL_BLEND);

    /* Init viewport */
    glViewport(0, 0, 256, 1);
    glUseProgram(shaderProgram);
    /* Draw */
    glDrawArrays(GL_POINTS, 0, num_input_data);


    glReadPixels(0, 0, 256, 1, GL_RED, GL_FLOAT, buffer);

    for (int i = 0; i < 256; ++i)
    {
        printf("%d\t%f\t%d\n", i, buffer[i], hist[i]);
    }

}

谁能帮帮我,在此先感谢。

我已经更新了我的代码,它现在可以工作了:D

#include <stdio.h>
#include <stdlib.h>
#include <iostream>
#include <vector>
// Include GLEW
#include <GL/glew.h>

// Include GLFW
#include <GLFW/glfw3.h>
GLFWwindow* window;

// Include GLM
#include <glm/glm.hpp>
using namespace glm;
#include <SOIL.h>

// Shader sources
const GLchar* vertexSource = "\n"
"#version 330 core\n"
"in vec3 inPosition;\n"
"void main()\n"
"{\n"
"    float x = inPosition.x;\n"
"\n"
"    gl_Position = vec4(\n"
"        -1.0 + ((x + 1) * 0.0078125),\n"
"        0.0,\n"
"        0.0,\n"
"        1.0\n"
"    );\n"
"}\n";

const GLchar* fragmentSource = "\n"
"#version 330 core\n"
"out vec4 outputColor;\n"
"void main()\n"
"{\n"
"    outputColor = vec4(1.0, 1.0, 1.0, 1.0);\n"
"}\n";

void CheckStatus(GLuint obj)
{
    GLint status = GL_FALSE, len = 10;
    if( glIsShader(obj) )   glGetShaderiv( obj, GL_COMPILE_STATUS, &status );
    if( glIsProgram(obj) )  glGetProgramiv( obj, GL_LINK_STATUS, &status );
    if( status == GL_TRUE ) return;
    if( glIsShader(obj) )   glGetShaderiv( obj, GL_INFO_LOG_LENGTH, &len );
    if( glIsProgram(obj) )  glGetProgramiv( obj, GL_INFO_LOG_LENGTH, &len );
    std::vector< char > log( len, 'X' );
    if( glIsShader(obj) )   glGetShaderInfoLog( obj, len, NULL, &log[0] );
    if( glIsProgram(obj) )  glGetProgramInfoLog( obj, len, NULL, &log[0] );
    std::cerr << &log[0] << std::endl;
    exit( -1 );
}

void _check_gl_error(int line) 
{
    GLenum err (glGetError());

    while(err!=GL_NO_ERROR) 
    {
        std::string error;

        switch(err) 
        {
            case GL_INVALID_OPERATION:      error="INVALID_OPERATION";      break;
            case GL_INVALID_ENUM:           error="INVALID_ENUM";           break;
            case GL_INVALID_VALUE:          error="INVALID_VALUE";          break;
            case GL_OUT_OF_MEMORY:          error="OUT_OF_MEMORY";          break;
            case GL_INVALID_FRAMEBUFFER_OPERATION:  error="INVALID_FRAMEBUFFER_OPERATION";  break;
        }

        std::cerr << "GL_" << error.c_str() <<":"<<line<<std::endl;
        err=glGetError();
    }
}

GLfloat buffer[256];
GLuint hist[256];
float _image[512*512*3];

int main()
{
    // Initialise GLFW
    if( !glfwInit() )
    {
        fprintf( stderr, "Failed to initialize GLFW\n" );
        getchar();
        return -1;
    }

    glfwWindowHint(GLFW_SAMPLES, 4);
    glfwWindowHint(GLFW_CONTEXT_VERSION_MAJOR, 3);
    glfwWindowHint(GLFW_CONTEXT_VERSION_MINOR, 3);
    glfwWindowHint(GLFW_OPENGL_FORWARD_COMPAT, GL_TRUE); // To make MacOS happy; should not be needed
    glfwWindowHint(GLFW_OPENGL_PROFILE, GLFW_OPENGL_CORE_PROFILE);

    int width, height;
    unsigned char* image = SOIL_load_image("sample_gray.bmp", &width, &height, 0, SOIL_LOAD_RGB);
    unsigned char* image_gray = new unsigned char[width * height];

    printf("%d\t%d\n", width, height);


    for (int i = 0; i < width * height; ++i)
    {
        image_gray[i] = image[i * 3];
        _image[i * 3] = image[i * 3];
        _image[i * 3 + 1] = image[i * 3 + 1];
        _image[i * 3 + 2] = image[i * 3 + 2];
    }

    for (int i = 0; i < width * height; ++i)
    {
        hist[image_gray[i]]++;
    }

    // Open a window and create its OpenGL context
    window = glfwCreateWindow(width, height, "Basic", NULL, NULL);
    if( window == NULL ){
        fprintf( stderr, "Failed to open GLFW window. If you have an Intel GPU, they are not 3.3 compatible. Try the 2.1 version of the tutorials.\n" );
        getchar();
        glfwTerminate();
        return -1;
    }
    glfwMakeContextCurrent(window);

    // Initialize GLEW
    glewExperimental = true; // Needed for core profile
    if (glewInit() != GLEW_OK) {
        fprintf(stderr, "Failed to initialize GLEW\n");
        getchar();
        glfwTerminate();
        return -1;
    }
    _check_gl_error(__LINE__);
    // Ensure we can capture the escape key being pressed below
    glfwSetInputMode(window, GLFW_STICKY_KEYS, GL_TRUE);

    // Dark blue background
    glClearColor(0.0f, 0.0f, 0.0f, 0.0f);


    GLuint vao;
    glGenVertexArrays(1, &vao);
    glBindVertexArray(vao);

    GLuint vbo;
    glGenBuffers(1, &vbo);
    glBindBuffer(GL_ARRAY_BUFFER, vbo);

    GLuint num_input_data = width * height;

    /* Upload data */
    glBufferData(GL_ARRAY_BUFFER, num_input_data * sizeof(float) * 3, _image, GL_STATIC_DRAW);

    GLuint vertexShader, fragmentShader, shaderProgram;
    // Create and compile the vertex shader
    vertexShader = glCreateShader(GL_VERTEX_SHADER);
    glShaderSource(vertexShader, 1, &vertexSource, NULL);
    glCompileShader(vertexShader);
    CheckStatus(vertexShader);
    // Create and compile the fragment shader
    fragmentShader = glCreateShader(GL_FRAGMENT_SHADER);
    glShaderSource(fragmentShader, 1, &fragmentSource, NULL);
    glCompileShader(fragmentShader);
    CheckStatus(fragmentShader);
    // Link the vertex and fragment shader into a shader program
    shaderProgram = glCreateProgram();
    glAttachShader(shaderProgram, vertexShader);
    glAttachShader(shaderProgram, fragmentShader);
    glBindFragDataLocation(shaderProgram, 0, "outputColor");
    glLinkProgram(shaderProgram);


    CheckStatus(shaderProgram);

    glUseProgram(shaderProgram);


    // Specify the layout of the vertex data
    GLint posAttrib = glGetAttribLocation(shaderProgram, "inPosition");
    glEnableVertexAttribArray(posAttrib);
    glVertexAttribPointer(posAttrib, 3, GL_FLOAT, GL_FALSE, 3 * sizeof(GLfloat), 0);

    GLuint tex;
    GLuint fbo;
    glGenFramebuffers(1, &fbo);
    glBindFramebuffer(GL_FRAMEBUFFER, fbo);

    glGenTextures(1, &tex);
    glActiveTexture(GL_TEXTURE0);
    glBindTexture(GL_TEXTURE_2D, tex);
    glTexImage2D(GL_TEXTURE_2D, 0, GL_R32F, 256, 1, 0, GL_RED, GL_FLOAT, NULL);
    glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
    glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
    glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
    glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
    // glTexStorage2D(GL_TEXTURE_2D, 1, GL_R32F, 256, 1);

    glBindTexture(GL_TEXTURE_2D, 0);

    glFramebufferTexture(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, tex, 0);

    glBindFramebuffer(GL_FRAMEBUFFER, 0);

    _check_gl_error(__LINE__);

    glBindFramebuffer(GL_FRAMEBUFFER, fbo);
    /* Clear buffer */
    glClearColor(0.0, 0.0, 0.0, 1.0);
    glClear(GL_COLOR_BUFFER_BIT);

    glBlendEquation(GL_FUNC_ADD);
    glBlendFunc(GL_ONE, GL_ONE);
    glEnable(GL_BLEND);
    _check_gl_error(__LINE__);


    /* Init viewport */
    glViewport(0, 0, 256, 1);
    glUseProgram(shaderProgram);
    /* Draw */
    glDrawArrays(GL_POINTS, 0, num_input_data);


    glReadPixels(0, 0, 256, 1, GL_RED, GL_FLOAT, buffer);
    _check_gl_error(__LINE__);
    for (int i = 0; i < 256; ++i)
    {
        printf("%d\t%f\t%d\n", i, buffer[i], hist[i]);
    }
    free(image_gray);
}

特别感谢@Vallentin 的热情!

【问题讨论】:

  • 我看到的第一件事是你在生成它之前绑定了fbo。第二:您的CheckStatus 方法仅在没有具有相同ID 的着色器对象和着色器程序时才有效。所有句柄都只是无符号整数,OpenGL 实现为您提供着色器对象 0 和着色器程序对象 0 是完全有效的。在这种情况下,您的方法将同时查询链接状态和编译状态。
  • @BDL,谢谢。我在下面更新了我的代码。你能帮我再检查一遍吗?

标签: c++ opengl histogram


【解决方案1】:

我的直方图缓冲区总是返回零。

有趣。因为我不知道哪个编译器可以让这样的事情飞过:

int width, height;
unsigned char* image = SOIL_load_image("sample_gray.bmp", &width, &height, 0, SOIL_LOAD_RGB);
unsigned char image_gray[width * height];

你需要做什么:

unsigned char *image_gray = new unsigned char[width * height];

记得稍后释放内存delete[] image_gray

您的着色器也无法编译,或者您的驱动程序可能比我的更精简。给定#version 330 core,您不能使用attribute,而必须使用in

in vec3 inPosition;

CheckStatus() 也是这样说的:

0(3) : error C7555: 'attribute' is deprecated, use 'in/out' instead

您还尝试绑定一个甚至还没有的帧缓冲区。

GLuint fbo;
glBindFramebuffer(GL_FRAMEBUFFER, fbo);
[...]
glGenFramebuffers(1, &fbo);

你需要翻转它:

GLuint fbo;
glGenFramebuffers(1, &fbo);
glBindFramebuffer(GL_FRAMEBUFFER, fbo);

这也让我感到困惑,为什么你的编译器会放任自流,因为 MSVC 对我大喊大叫。

Error C4700 uninitialized local variable 'fbo'

在对纹理应用任何更改之前,您也没有绑定纹理。

// glBindTexture(GL_TEXTURE_2D, tex);

所以取消注释glBindTexture() 调用。

您也没有创建任何顶点数组。哪个检查glGetError() 会告诉我们。

GLuint vao;
glGenVertexArrays(1, &vao);
glBindVertexArray(vao);

在致电glEnableVertexAttribArray()之前这样做。

你的透明颜色也是红色的。所以这不会对你有太大帮助。现在你对glReadPixels() 的调用只会产生1.0

glClearColor(0.0, 0.0, 0.0, 1.0)

现在glReadPixels() 仍然只产生零。然而,解除绑定或根本不创建帧缓冲区。然后它给出了我认为是期望的结果。由于不应该使用帧缓冲区,我怀疑它有问题。但是,我似乎无法通过浏览您的代码来确定它。但由于代码已经充满了问题,这是一个公平的起点。

补充:

  • 您的目标是 OpenGL 3.3,而 glTexStorage2D() 是 4.2 的核心。重点是,如果您想确保支持 glTexStorage2D(),请以 4.2 为目标。
  • glActiveTexture() 的调用是多余的。
  • 由于纹理的高度为 1,因此请考虑使用 1D 纹理。
  • 记得查看glGetError() 或更好地使用Debug Output

编辑

首先,您不能在创建窗口(和上下文)并调用glewInit() 之前调用_check_gl_error()

您可以使用__LINE__,而不是手动添加行号。因此_check_gl_error(__LINE__)

再一次不要使用红色透明色。使用黑色透明色glClearColor(0.0, 0.0, 0.0, 1.0)。因为混合1.0 和说0.1 导致1.1,它被钳制回1.0。因此红色通道从一开始就应该是0.0

我意识到了问题所在。在您的顶点着色器中,您手动将y 设置为-1.0。鉴于您正在绘制GL_POINTS,这最终会从屏幕上掉下来。 现在将其设置为y-0.9999 似乎可以得到想要的结果。然而,依赖这个就像在玩火。

如果您现在运行该应用程序,您现在会看到0.01.0 的混合。你得到1.0的原因是因为在你的片段着色器中你将红色通道的outputColor设置为1.0。再次总结所有这些可能是一些疯狂的价值,但最终它会被限制在1.0

改为尝试:

outputColor = vec4(0.005, 1.0, 1.0, 1.0);

现在您应该看到输出增加和减少,而不是 0.01.0。但是请注意,如果hist[i] 大于 200,那么任何大于 200 的都将导致 1.0。因为1 / 0.005 = 200

现在一切都在工作

【讨论】:

  • 感谢@Vallentin 的回复。我已经用你推荐的点更新了我的代码。我使用glGeterror() 添加_check_gl_error 函数来检查错误。但是,我的缓冲区仍然返回零(如您所说,如果我将颜色清除为红色,则为 1.0)。你能再检查一下我的代码吗?谢谢你。还有一件事,你能建议我将函数glTexStorage1D 替换为其他函数吗?
  • 非常感谢@Vallentin。它现在正在工作:))虽然我需要在你的答案中进行一些修复,但它非常有帮助。它对我有很大帮助。再次感谢您。
猜你喜欢
  • 2016-09-29
  • 2011-09-17
  • 1970-01-01
  • 2017-10-24
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2014-04-05
相关资源
最近更新 更多