Histogram Pipeline
Histogram algorithms are notably difficult to implement on parallel architecture as they require the ability to stack results. In Cuda or OpenCL this can be achieved via atomic operations. In this example, we will use a method known as Vertex Texture Fetching. We will use for the geometry, a 3D grid of dot, one for each channel, in each pixel. From the vertex shader, we will be able to read the corresponding texel component and modify the position of the current point accordingly. Finally, results will be stacked in a texture will alpha-blending enable. The following figure illustrates this operation :
We first write the core pipeline, histogram.ppl :
REQUIRED_FORMAT:inputFormatHistogram(inputFormat)
TEXTURE_FORMAT:histogramBinsFormat(256, 1, GL_RGBA32F, GL_FLOAT, GL_NEAREST, GL_LINEAR)
CALL:FORMAT_SCALE_SIZE(reducedFormatHistogram, inputFormatHistogram, 0.25, 0.25)
CALL:FORMAT_TO_CONSTANT(reducedFormatHistogram)
CALL:GENERATE_SAME_SIZE_3D_GRID(grid, reducedFormatHistogram, TRUE)
SOURCE:HistogramVertexShader
{
#version 130
uniform sampler2D inputTexture;
void main()
{
vec4 col = texture(inputTexture, gl_Vertex.xy);
float sel = 0.0;
if (gl_Vertex.z==0.0)
{
gl_FrontColor = vec4(1.0,0.0,0.0,1.0);
sel = col.r;
}
else if (gl_Vertex.z==0.5)
{
gl_FrontColor = vec4(0.0,1.0,0.0,1.0);
sel = col.g;
}
else if (gl_Vertex.z==1.0)
{
gl_FrontColor = vec4(0.0,0.0,1.0,1.0);
sel = col.b;
}
else
{
gl_FrontColor = vec4(0.0,0.0,0.0,1.0);
sel = -1.0;
}
gl_Position = vec4((sel-0.5)*2.0, 0.0, 0.0, 1.0);
}
}
SOURCE:HistogramFragmentShader
{
#version 130
out vec4 histogramBins;
#pragma INSERT(reducedFormatHistogram)
void main()
{
float nrm = 1.0/float(reducedFormatHistogram.s*reducedFormatHistogram.t);
histogramBins = gl_Color*nrm;
histogramBins.a = 1.0;
}
}
FILTER_LAYOUT:HistogramFilter(histogramBinsFormat)
{
GL_FRAGMENT_SHADER(HistogramFragmentShader)
GL_VERTEX_SHADER(HistogramVertexShader)
GL_RENDER(grid)
GL_BLEND(GL_ONE, GL_ONE, GL_FUNC_ADD)
}
PIPELINE_MAIN:HistogramPipeline
{
INPUT_PORTS(inputTexture)
OUTPUT_PORTS(histogramBins)
FILTER_INSTANCE:HistogramFilter
}
This code will output the normalized histogram as a 1D texture of 256 elements. We would like to plot the curve on top of the image. We write plotHistogram.ppl :
INCLUDE(histogram.ppl)
REQUIRED_FORMAT:outputFormatPlotHistogram(inputFormat0)
SOURCE:PlotHistogramShader
{
#version 130
uniform sampler2D histogramBins,
inputTexture;
out vec4 outputTexture;
uniform float scale = 1.0;
uniform int noBackground = 0;
void main()
{
vec2 pos = gl_FragCoord.xy/vec2(textureSize(inputTexture, 0));
vec4 hist = textureLod(histogramBins, vec2(pos.s, 0.0), 0);
vec4 col = textureLod(inputTexture, pos, 0);
bool rTest = (pos.t<hist.r*scale),
gTest = (pos.t<hist.g*scale),
bTest = (pos.t<hist.b*scale);
if (noBackground>0 && (rTest || gTest || bTest))
col = vec4(0.0, 0.0, 0.0, 1.0);
outputTexture.r = rTest ? 1.0 : col.r;
outputTexture.g = gTest ? 1.0 : col.g;
outputTexture.b = bTest ? 1.0 : col.b;
outputTexture.a = 1.0;
}
}
FILTER_LAYOUT:PlotHistogramFilter(outputFormatPlotHistogram, PlotHistogramShader)
PIPELINE_MAIN:PlotHistogramPipeline
{
INPUT_PORTS(inputTexture)
OUTPUT_PORTS(histogramBins, outputTexture)
FILTER_INSTANCE:HistogramFilter
FILTER_INSTANCE:PlotHistogramFilter
}
Sample output :