1

I'm using OpenGL to plot an RGB image histogram. As this is an 8-bit image histogram, my data-set contains data points from zero to 255.

If I plot the histogram without using glScaled() then the graph plots as expected but, of course, does not fill the allocated area (the width of which is variable, the height constant). However, when I use glScaled() the graph shows strange artefacts.

Please see the following images to see an example of the problem:

Histogram without glScale

The image above shows the histogram as plotted with 256 data points without being scaled with glScaled().

Histogram plot with glScale

Same as above but with a larger scale

The two images above shows the histogram as plotted with 256 data points AND being scaled with glScaled(). The strange artefacts are evident (missing data?). Please note, the third histogram is a slightly different shape due to changing light levels.

Here is the relevant part of my OpenGL initialisation code:

glViewport(0, 0, width, height);
glMatrixMode(GL_PROJECTION);
glLoadIdentity();

glOrtho(0.0f, width, height, 0.0f, 0.0f, 1.0f);
glMatrixMode(GL_MODELVIEW);
glLoadIdentity();

// If this line is removed then the graph plots correctly
// m_scale_factor = width / 256
glScaled(m_scale_factor, 1.0, 1.0);

glClear(GL_COLOR_BUFFER_BIT);

And here is the relevant part of my plot code:

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

glBegin(GL_LINE_STRIP);
for (int n = 0; n < m_histogram_X; n++)
{
    glColor4ub(255, 0, 0, 255);
    glVertex2i(n, m_Hist_Channel_R[n]);
    glVertex2i(n, GRAPH_HEIGHT);

    glColor4ub(0, 255, 0, 255);
    glVertex2i(n, m_Hist_Channel_G[n]);
    glVertex2i(n, GRAPH_HEIGHT);

    glColor4ub(0, 0, 255, 255);
    glVertex2i(n, m_Hist_Channel_B[n]);
    glVertex2i(n, GRAPH_HEIGHT);
}
glEnd()

...

At this stage I feel that I must state that I am new to OpenGL, so it's possible that I have misunderstood many OpenGL things...

My question is: Is it possible to fix this problem in OpenGL or will I have to increase the number of data points by some kind of interpolation and then plot without scaling?

I appreciate any help offered.

4

2 回答 2

2

如果将渲染分成 3 个通道,您应该能够使用三角形条而不是线条(它应该填补空白)。

glBegin(GL_TRIANGLE_STRIP);
glColor4ub(255, 0, 0, 255);
for (int n = 0; n < m_histogram_X; n++)
{
    glVertex2i(n, m_Hist_Channel_R[n]);
    glVertex2i(n, GRAPH_HEIGHT);
}
glEnd()

glBegin(GL_TRIANGLE_STRIP);
glColor4ub(0, 255, 0, 255);
for (int n = 0; n < m_histogram_X; n++)
{
    glVertex2i(n, m_Hist_Channel_G[n]);
    glVertex2i(n, GRAPH_HEIGHT);
}
glEnd()

glBegin(GL_TRIANGLE_STRIP);
glColor4ub(0, 0, 255, 255);
for (int n = 0; n < m_histogram_X; n++)
{
    glVertex2i(n, m_Hist_Channel_B[n]);
    glVertex2i(n, GRAPH_HEIGHT);
}
glEnd()
于 2019-05-15T01:44:35.610 回答
2

没有数据丢失。只是,您正在以不适合数据的方式使用绘图原语。GL_LINE_STRIP 绘制一条长长的连续线,在您传入点时将它们连接起来,并在您设置的颜色之间进行插值。

基本上你所做的是,从最后一个蓝色箱到下一个红色箱高度画一条蓝到红的线,然后从红色箱到绿色箱,然后从那里到蓝色箱。然后你跳到下一个红色垃圾箱,依此类推。所以基本上你正在用红色和蓝色之间的连接线绘制小的“尖峰”。当然,如果要填充的像素宽度比 bin 多,那么就会出现间隙。

我建议你拿一张(绘图)纸,自己动手执行绘图步骤,以了解这个结果是如何发生的。

说实话:无论如何,这不是绘制直方图的最有效方法。更好的方法是将直方图数据加载到一维纹理中,绘制一个大四边形(或者更好的是一个视口填充三角形,使用剪刀测试将视口切割成一个矩形)和每个片段(大约是一个像素,用一些额外的东西)在片段着色器中使用 X 坐标从纹理中查找 bin 并从纹理中减去 Y 坐标并将结果传递给stepsmoothstepGLSL 函数以确定像素的颜色。对于新手来说可能听起来很奇怪,但绘制一个三角形并在片段着色器中完成其余部分有效,即提交一片多边形。它还提供了更好的质量!

更新——示例着色器

一个实际的应用程序可以用这样的着色器来实现(Shadertoy 语义):

void mainImage( out vec4 fragColor, in vec2 fragCoord )
{
    // Normalized pixel coordinates (from 0 to 1)
    vec2 uv = fragCoord/iResolution.xy;

    // read the histogram data from a sampler
    // on shadertoy there are no 1D textures, so we use a
    // single row of a 2D texture instead, and sweep up/down
    // over time to get a "dynamic" histogram.
    vec3 h = texture(iChannel0, vec2(uv.x, sin(0.01*iTime))).rgb;

    // discard fragments which are "outside" the histogram
    // also use this value later for the alpha channel
    float a = smoothstep( 0.000, 0.001, length(h));
    if( 0. == a ){ discard; }

    // Color the fragment. The smoothstep gives some antialiasing.
    // For perfect pixel coverage based antialiasing we'd have to
    // determine the slope of the histogram, to construct a tangent
    // and determine the distance to it, i.e. create a
    // Signed Distance Field
    fragColor = vec4(
        smoothstep(-0.001, 0.001, h.r - uv.y),
        smoothstep(-0.001, 0.001, h.g - uv.y),
        smoothstep(-0.001, 0.001, h.b - uv.y),
        a );

    // Instead of using the smoothstep and/or the SDF using a
    // multisampled buffer and performing a simple `y >= h` test
    // would yield probably a nicer result.
}

结果看起来像这样

基于着色器的直方图

于 2019-05-14T20:39:36.253 回答