Table Of Contents 

Introduction 

This sample application demonstrates the implementation of Bi-linear interpolation and Bi-Cubic interpolation in a GLSL shader.  

ScreenShot.jpg 

Screenshot of ZoomInterpolation Application. 

Background 

In OpenGL, we can specify an interpolation type(Filter type) to create a resized image of texture. When a texture or region of a texture is mapped to a higher or lower screen region, OpenGL uses this interpolation type to create texture image in different size.

To create a large image from a small texture area, openGL provides two interpolation options, GL_LINEAR, and GL_LINEAR. In addition to OpenGL interpolation types, here bi-cubic interpolation is implemented through GLSL shader.   

OpenGL GL_NEAREST Interpolation.  

GL_NEAREST just copies data available in nearest pixel and it create a blocky effect when zoom to high region. In nearest interpolation, intermediate pixels are created with nearest valid pixel. This kind of interpolated image will be blocky since same data is copied to intermediate pixels.

Below image is created through opengl GL_NEAREST interpolation and its quality is very bad.

We can use the following code to achieve nearest interpolation in opengl fixed function pipeline. 

glTexParameteri( GL_TEXTURE_2D,GL_TEXTURE_MAG_FILTER, GL_NEAREST); 
glTexParameteri( GL_TEXTURE_2D,GL_TEXTURE_MIN_FILTER, GL_NEAREST); 

 GL_Nearest.jpg

Interpolation with GL_NEAREST interpolation type. 

OpenGL GL_LINEAR Interpolation. 

OpenGL provides a good interpolation method, GL_LINEAR. It interpolates nearest 4 pixels and creates a smooth image comparing to the GL_NEAREST method. This method interpolate both in X direction and Y direction. Therefore it called Bi-Linear interpolation.  

The following code can be used to achieve Bi-Linear interpolation in openGL fixed function pipeline. 

glTexParameteri( GL_TEXTURE_2D,GL_TEXTURE_MAG_FILTER, GL_LINEAR ); 
glTexParameteri( GL_TEXTURE_2D,GL_TEXTURE_MIN_FILTER, GL_LINEAR ); 

The following image is created through GL_LINEAR interpolation and its edges are smooth when comparing to the image created through GL_NEAREST interpolation.

 GLLinear.jpg

Interpolation with GL_LINEAR interpolation type.  

GLSL Bi-Linear 

OpenGL provides only two interpolations methods to zoom an image. These interpolation methods are not enough when a small image is zoomed to large area. We can see small blocky effect in the above image ( GL_LINEAR ).

The first step is to create a GLSL shader for Bi-Linear interpolation. This interpolation provides same output of GL_LINEAR.

In Bi-Linear interpolation nearest four pixels are considered to create an intermediate pixel. 

BiLinearLogic.png

In the above figure an intermediate pixel F(p’,q’) is created by interpolating nearest four texels F(p,q), 

F(p,q+1), F(p+1,q), F(p+1,q+1).  Texel is the term used to indicate an element( similar to pixel) in a texture.

An interpolation factor a, is used in X direction to interpolate F(p,q) and F(p,q+1) as follows.

F(p’’) = a * F(p,q) + (1.0 – a ) F(p, q+1) // Linear interpolation in X direction[Top].
F(p+1’’) = a * F(p+1,q) + (1.0 – a ) F(p+1, q+1) // Linear interpolation in X direction[Bottom]. 

An interpolation factor b, is used in Y direction to interpolate F(p’’)and F(p+1’’) as follows.

 F(p’,q’) = b * F(p’’) + (1.0 - b) *  F(p+1’’)  

Corresponding GLSL shader code.  

// Function to get a texel data from a texture with GL_NEAREST property.
// Bi-Linear interpolation is implemented in this function with the 
// help of nearest four data.
vec4 tex2DBiLinear( sampler2D textureSampler_i, vec2 texCoord_i )
{
    vec4 p0q0 = texture2D(textureSampler_i, texCoord_i);
    vec4 p1q0 = texture2D(textureSampler_i, texCoord_i + vec2(texelSizeX, 0));

    vec4 p0q1 = texture2D(textureSampler_i, texCoord_i + vec2(0, texelSizeY));
    vec4 p1q1 = texture2D(textureSampler_i, texCoord_i + vec2(texelSizeX , texelSizeY));

    float a = fract( texCoord_i.x * fWidth ); // Get Interpolation factor for X direction.
											 // Fraction near to valid data.

    vec4 pInterp_q0 = mix( p0q0, p1q0, a ); // Interpolates top row in X direction.
    vec4 pInterp_q1 = mix( p0q1, p1q1, a ); // Interpolates bottom row in X direction.

    float b = fract( texCoord_i.y * fHeight ); // Get Interpolation factor for Y direction.
    return mix( pInterp_q0, pInterp_q1, b ); // Interpolate in Y direction.
}     

In this method nearest 16 pixels are used to create an intermediate pixel F(p’q’). 

BiCubicLogic.jpg

Following equation [from Digital Image Processing: PIKS Inside, Third Edition] is used to interpolate nearest 16 pixles.  

BiCubicEquation.png

Where Rc() denotes a bicubic interpolation function such as a cubic B-spline, Traingular, Bell cubic interpolation function.  In this sample I just use a Triangular, Bell, and B-Spline interpolation function. 

On zooming to a large space the intermediate pixels are created with nearest pixels. 

Where F(p’,q’) is an intermediate texel. F(p’,q’) is identified by interpolating nearest 16 pixels. Where a is the offset in X direction, and b is the offset in Y direction from nearest valid data. These offset are considered between 0 and 1. 

Following is a GLSL shader code for bi cubic interpolation. BiCubic function get a texture coordinate (x,y) and returns the interpolated value of nearest 16 texels.  Nearest 16 texels are iterated through 2 for loops from [x-1,y-1] to [x+2, y+2]. For each nearest element an interpolation factor is calculated with the corresponding bi-cubic interpolation function. In this method Triangular() is used to get an interpolation factor. Different versions of Bi-Cubic interpolation[BSpline, Traingular, Bell] can be created by changing Triangular() and its logic. When plotting values from Triangular(), we will get the below image in a triangle form. 

From plotting of triangular function, left most value of X axis is -2 and right most value of X axis is +2. It indicates Triangular function returns lower value for a high input and high value for a low input.  

In BiCubic(), the nearest data get a high weightage[From Triangular()] and farthest pixel get a low weightage. In effect an intermediate pixel is created by interpolating nearest 16 data. The weightage will be higher for nearest pixels. The output image of Triangular Bi-Cubic is smoother than Bi-Linear . 

Following code is a GLSL implementation of Bi-Cubic interpolation. 

vec4 BiCubic( sampler2D textureSampler, vec2 TexCoord )
{
    float texelSizeX = 1.0 / fWidth; //size of one texel 
    float texelSizeY = 1.0 / fHeight; //size of one texel 
    vec4 nSum = vec4( 0.0, 0.0, 0.0, 0.0 );
    vec4 nDenom = vec4( 0.0, 0.0, 0.0, 0.0 );
    float a = fract( TexCoord.x * fWidth ); // get the decimal part
    float b = fract( TexCoord.y * fHeight ); // get the decimal part
    for( int m = -1; m <=2; m++ )
    {
        for( int n =-1; n<= 2; n++)
        {
			vec4 vecData = texture2D(textureSampler, 
                               TexCoord + vec2(texelSizeX * float( m ), texelSizeY * float( n )));
			float f  = Triangular( float( m ) - a );
			vec4 vecCooef1 = vec4( f,f,f,f );
			float f1 = Triangular ( -( float( n ) - b ) );
			vec4 vecCoeef2 = vec4( f1, f1, f1, f1 );
            nSum = nSum + ( vecData * vecCoeef2 * vecCooef1  );
            nDenom = nDenom + (( vecCoeef2 * vecCooef1 ));
        }
    }
    return nSum / nDenom;
}   

Implementation of Bi-Cubic Interpolation[ Triangular ] 

Triangular function is a simple bi-cubic function, as defined in the following equation. 

TriangularEquation.png

TriangularCurve.png

The above diagram is the output of Triangular() function. Triangular(x) is plotted in Y direction. x starts from -2.0 and ends at +2.0. It indicate when x is high, Triangular(x) is low and therefore far data of an intermediate texel get lower weight. The following code is used in GLSL shader for Triangular Bi-Cubic implementation.   

 float Triangular( float f )
{
	f = f / 2.0;
	if( f < 0.0 )
	{
		return ( f + 1.0 );
	}
	else
	{
		return ( 1.0 - f );
	}
	return 0.0;
}

 TriangularZoom.jpg

Interpolation with GLSL BiCubic Triangular interpolation type. 

Implementation of Bi-Cubic Interpolation[ Bell ]  

When comparing the Bi-Cubic Triangular image with Bi-Linear, the edges are blurred. The reason of this edge is weight provided for nearest and farthest data. If weight to nearest data is high and far pixels are very low, then Bi-Cubic interpolation can achieve a smooth edges.  Another Bi-cubic interpolation funciton which creates a bell shaped( nearest vaues[Center] are high) and far values are low[left and right ends]. 

BellEquation.png

BellCurve.png

The above diagram is the output of BellFunc() function. BellFunc(x) is plotted in Y direction. x starts from -2.0 and ends at +2.0. It indicate when x is high, BellFunc(x) is very low and therefore far data of an intermediate texel get very lower weight.  The following code is used in GLSL shader for Bell shaped Bi-Cubic implementation. 

float BellFunc( float x )
{
	float f = ( x / 2.0 ) * 1.5; // Converting -2 to +2 to -1.5 to +1.5
	if( f > -1.5 && f < -0.5 )
	{
		return( 0.5 * pow(f + 1.5, 2.0));
	}
	else if( f > -0.5 && f < 0.5 )
	{
		return 3.0 / 4.0 - ( f * f );
	}
	else if( ( f > 0.5 && f < 1.5 ) )
	{
		return( 0.5 * pow(f - 1.5, 2.0));
	}
	return 0.0;
} 

 BellZoom.jpg

Interpolation with BiCubic Bell interpolation type. 

Implementation of Bi-Cubic Interpolation[ B-Spline ]  

When comparing the Bi-Cubic Bell shaped, B spline is smoother and edges are more clear. Below equation is used to create BSpline() function.  

BSplineEquation.png

BSplineCurve.png

The above diagram is the output of BSpline() function. BSpline(x) is plotted in Y direction. x starts from -2.0 and ends at +2.0. It indicate when x is high, BSpline(x) is very low and therefore far data of an intermediate texel get very lower weight. When comparing to Bell shaped wave, values in far range ( near -2 and +2 ) are very low. Therefore final output image is also smoother than Bell Bi-Cubic interpolated image. The following code is used in GLSL shader for BSpline implementation.  

float BSpline( float x )
{
	float f = x;
	if( f < 0.0 )
	{
		f = -f;
	}

	if( f >= 0.0 && f <= 1.0 )
	{
		return ( 2.0 / 3.0 ) + ( 0.5 ) * ( f* f * f ) - (f*f);
	}
	else if( f > 1.0 && f <= 2.0 )
	{
		return 1.0 / 6.0 * pow( ( 2.0 - f  ), 3.0 );
	}
	return 1.0;
}

 BSplineZoom.jpg

Interpolation with BiCubic BSpline interpolation type.  

About ZoomInterpolation Application: 

ZoomInterpolation is a sample application created to demonstrate different interpolation methods. On startup it creates a texture with a bitmap [Flower.bmp] available in resource of this application.  

This application displays two images, zoomed image is in left, and actual image is at right side. A red rectangle indicates the zoomed image. Small area of image is selected for texture mapping and displayed to screen.  

Pan operation:   

When mouse clicks on zoomed image and pans, the texture mapping region changes with respect to mouse move and it creates a pan effect. 

Zoom and UnZoom: 

When Mouse scroll, the zoom/unzoom is implemented by increasing or decreasing the texture mapping area. CZoomInterpolationDlg::HandleZoom() handles zoom and unzoom. Two buttons “Zoom+” and “Zoom-“ is also added to increase zoom or decrease zoom. These buttons will be helpful for zoom and unzoom in a laptop.

Loading new Image: 

A button “Load Image” is available to load an image file from your machine. This image area is created in an aspect ratio of 4:3 and therefore input image is expected in an aspect ratio of 4:3 for better output image. Otherwise stretched/skewed image will be displayed. BMPLoader class is used to retrieve RGB data from bitmap with the help of Gdi+ library. This class supports different image extensions such as .bmp, .jpg, .png, .tga etc.

Plotting of Interpolation Curve: 

A simple class is created to plot the curve indicating weights applied in current Bi-Cubic interpolation function. Triangular, Bell, and BSpline are plotted with the same code of glsl shader code.  

Main Classes Used in ZoomInterpolationApp: 

BMPLoader: This class is used for getting RGB information of an image file.
                  This class supports bmp, jpg, png, and tga format files.
GLExtension: This class holds all opengl extensions required for GLSL shader.
GLSetup: This class is used for creating rendering context and other opengl initialisations.
GLVertexBuffer: This class holds vertex data required for rendering a Quad image withtexture mapping.
PlotInterpCurve: This class draws current interpolation curve with the help of GDI. 
ZoomInterpolation: This class handles main operations of ZoomInterpolation Application.
                   All mouse move and button handling are implemented in this class.
                   All other classes in this application is used fromthis class. 

Limitations:   

1) The input image is expected in an aspect ratio of 4:3 for better output image. Otherwise stretched/skewed image will be displayed. 
2) For GLSL shader implementation, Some opengl following extensions are required in your machine.   
a. GL_ARB_fragment_shader 
b. GL_ARB_vertex_shader  
If your machine do-not have supporting graphics device, this application will display interpolation with openGL fixed function pipeline(Nearest and Linear). 

3) The shaders for different bi-cubic and Bi-linear interpolation is prepared with the help some equations, I expect some issues in this code. 

References: 

History: 

  1. 07-Aug-2011: Initial Version.    

推荐.NET配套的通用数据层ORM框架:CYQ.Data 通用数据层框架