Blur

Started by
16 comments, last by ff8 18 years, 10 months ago
I recently wrote a blur method for my image class. I started off with a google search for gaussian blur techniques and found plenty of hits from which to develop my routine. There is no shortage of pages out there for this topic.

My routine is *not* pretty, but it works for my app. It uses fixed filter settings and a macro that clamps to my fixed texture size of 128x128. Again, not pretty and certainly not flexible. I have posted a quick executable up also for you to see this routine in action and the effect you can get (B for blur/W wireframe/PgUp/Dn to move). I *highly* recommend you write something better and use this only as a guide ;-)

hth
F451

// macro to keep gaussian blur array elements in fixed image range (128x128)#define ac(c) ((c<0)?(c+1):((c>127)?(c-1):c))// filter for gaussian blur functionfloat filter[3][3]={{0.1f,0.3f,0.1f},                    {0.3f,4.0f,0.3f},                    {0.1f,0.3f,0.1f}};// struct for storing RGB values of a pixel when blurringstruct RGB {	float redRGB;	float greenRGB;	float blueRGB;};// array for storing current pixel and surrounding pixel RGB valuesRGB currRGB[3][3];// Gaussian Blur of one pixelvoid CIMAGE::GaussianBlurPixel(int x, int z){ // accumulators for RGB values float newR, newG, newB; // store current RGB values for 8 surrounding pixels plus the pixel we are working on for (int ii=-1; ii<2; ii++) {  for (int jj=-1; jj<2; jj++) {    GetColor2(ac(x+ii),ac(z+jj),currRGB[ii+1][jj+1].redRGB,currRGB[ii+1][jj+1].greenRGB,currRGB[ii+1][jj+1].blueRGB);  } } // work neighbour pixels blending as we go for (int ii=-1; ii<2; ii++) {  for (int jj=-1; jj<2; jj++) {    // adjust pixels using stored values and the formula : orig+((current-orig)*filter)    newR=(float)currRGB[ii+1][jj+1].redRGB+((currRGB[1][1].redRGB-currRGB[ii+1][jj+1].redRGB)*filter[ii+1][jj+1]);    newG=(float)currRGB[ii+1][jj+1].greenRGB+((currRGB[1][1].greenRGB-currRGB[ii+1][jj+1].greenRGB)*filter[ii+1][jj+1]);    newB=(float)currRGB[ii+1][jj+1].blueRGB+((currRGB[1][1].blueRGB-currRGB[ii+1][jj+1].blueRGB)*filter[ii+1][jj+1]);    // assign new color    SetColor(ac(z+jj),ac(x+ii),(unsigned char)newR,(unsigned char)newG,(unsigned char)newB);  } }}// Gaussian blur of a texture or image//   fixed 1 pixel blur ratio and filter settingsvoid CIMAGE::GaussianBlur(){ // blur entire image if loaded if (m_bIsLoaded) {  // traverse image blurring each pixel based on neighbours  for (unsigned int x=0; x<m_uiWidth; x++) {    for (unsigned int z=0; z<m_uiHeight; z++) {      GaussianBlurPixel(x,z);    }  }  // remake the texture if not new  if (m_ID > 0) {    glBindTexture(GL_TEXTURE_2D,m_ID);    glTexImage2D(GL_TEXTURE_2D,0,GL_RGB,m_uiWidth,m_uiHeight,0,GL_RGB,GL_UNSIGNED_BYTE,m_imgData);  } }}
Advertisement
Thank you Fahrenheit451 for your reply :),
I am so happy for the code :D ,just i have one question .

Can you give me the code for GetColor2(*) and SetColor(*)?
Thank you again :)

JavaCoolDude if you don't mind can you show me your GLSL code ?

bye
If you have covered image loading on your OpenGL/C++ adventure then you will know that you load the image from a file and store the data in memory in an array. Those routines simply read the sequential RGB values for a pixel from that array, and likewise set the RGB values for a pixel. I pass the row and column of the array cell I want to access, but in the routine (because I too took them from a book I am working from) calculates the position of the cell as if it was accessing it as a one dimentional array. Looks confusing, but run a few simple 3x3 examples on paper and you will get it. Factor in that the cell you access is the red, then the cell+1 is the green, cell+2 is the blue, and potentially cell+3 for the alpha. Depends on the image/textures your working with.

inline void GetColor2(unsigned int z, unsigned int x, float &ucpRed, float &ucpGreen, float &ucpBlue){  unsigned int BPP= imageBPP/8;  if ((z<m_uiWidth)&&(x<m_uiHeight)) {    ucpRed  = imgData[(( z*m_imageHeight)+x)*BPP];    ucpGreen= imgData[(((z*m_imageHeight)+x)*BPP)+1];    ucpBlue = imgData[(((z*m_imageHeight)+x)*BPP)+2];  }}inline void SetColor( unsigned int x, unsigned  int y, unsigned char ucRed, unsigned char ucGreen, unsigned char ucBlue ){  unsigned int BPP= imageBPP/8;  if( ( x<m_uiWidth ) && ( y<m_uiHeight ) ) {    imgData[( ( y*imageHeight )+x )*BPP]  = ucRed;    imgData[( ( y*imageHeight )+x )*BPP+1]= ucGreen;    imgData[( ( y*imageHeight )+x )*BPP+2]= ucBlue;  }}


hth
F451
Quote:Original post by ff8
Can you explain it more ?if you don't mind .


Just like Fahrenheit451's method, although you could always take it one step further and do the calculations for a circle, rather than a square (requires even more computation, but would look a little better as you start to move beyond the 3x3 square, not that you'd really notice it, since it gets very blurry very fast). ;)
Nowadays I do write GLSL code just for debugging and making sure that things are properly working. I find it disturbing how many incompatibilities there are out there between sets of drivers running on the same hardware.
Anyways, here's the code:

<Shaders fragmentProcessor = "FRAGMENT_PROGRAM">  <FRAGMENT_SHADER>    <Uniform name = "texOffset"     size = "4" type = "float"/>    <Uniform name = "GlowTexture"   size = "1" type = "int"   x = "0"  />    <RawData>      uniform sampler2D GlowTexture;      uniform vec4      texOffset;      void main(void)      {        vec4  blurFilter[9],              finalColor = vec4(0.0,0.0,0.0,0.0);        float xOffset    = texOffset.x*texOffset.z,              yOffset    = texOffset.y*texOffset.w;        blurFilter[0]  = vec4( 4.0*xOffset, 4.0*yOffset, 0.0, 0.0217);        blurFilter[1]  = vec4( 3.0*xOffset, 3.0*yOffset, 0.0, 0.0434);        blurFilter[2]  = vec4( 2.0*xOffset, 2.0*yOffset, 0.0, 0.0869);        blurFilter[3]  = vec4( 1.0*xOffset, 1.0*yOffset, 0.0, 0.1739);        blurFilter[4]  = vec4(         0.0,         0.0, 0.0, 0.3478);        blurFilter[5]  = vec4(-1.0*xOffset,-1.0*yOffset, 0.0, 0.1739);        blurFilter[6]  = vec4(-2.0*xOffset,-2.0*yOffset, 0.0, 0.0869);        blurFilter[7]  = vec4(-3.0*xOffset,-3.0*yOffset, 0.0, 0.0434);        blurFilter[8]  = vec4(-4.0*xOffset,-4.0*yOffset, 0.0, 0.0217);        for (int i = 0;i< 9;i++)          finalColor += texture2D(GlowTexture, gl_TexCoord[0].xy + blurFilter.xy) * blurFilter.w;        gl_FragColor = finalColor*1.15;      }    </RawData>  </FRAGMENT_SHADER>  <!-- Assembly is l33t :P -->  <FRAGMENT_PROGRAM>    <Attributes local = "false" name  = "texOffset" />    <RawData>!!ARBfp1.0     PARAM textureOffset   = program.env[0];     PARAM offsetCoeffs[9] = {{ 4.0, 4.0, 0.0, 0.0217},                              { 3.0, 3.0, 0.0, 0.0434},                              { 2.0, 2.0, 0.0, 0.0869},                              { 1.0, 1.0, 0.0, 0.1739},                              { 0.0, 0.0, 0.0, 0.3478},                              {-1.0,-1.0, 0.0, 0.1739},                              {-2.0,-2.0, 0.0, 0.0869},                              {-3.0,-3.0, 0.0, 0.0434},                              {-4.0,-4.0, 0.0, 0.0217}};     TEMP  texCoords, colorSample, cumulativeColor;     TEMP  scale, sampler;     MOV   cumulativeColor, 0.0;     MUL   scale.x , textureOffset.x, textureOffset.z;     MUL   scale.y , textureOffset.y, textureOffset.w;     MAD   sampler, scale, offsetCoeffs[0], fragment.texcoord[0];     TEX   colorSample, sampler, texture[0], 2D;     MAD   cumulativeColor, colorSample, offsetCoeffs[0].w, cumulativeColor;     MAD   sampler, scale, offsetCoeffs[1], fragment.texcoord[0];     TEX   colorSample, sampler, texture[0], 2D;     MAD   cumulativeColor, colorSample, offsetCoeffs[1].w, cumulativeColor;     MAD   sampler, scale, offsetCoeffs[2], fragment.texcoord[0];     TEX   colorSample, sampler, texture[0], 2D;     MAD   cumulativeColor, colorSample, offsetCoeffs[2].w, cumulativeColor;     MAD   sampler, scale, offsetCoeffs[3], fragment.texcoord[0];     TEX   colorSample, sampler, texture[0], 2D;     MAD   cumulativeColor, colorSample, offsetCoeffs[3].w, cumulativeColor;     MAD   sampler, scale, offsetCoeffs[4], fragment.texcoord[0];     TEX   colorSample, sampler, texture[0], 2D;     MAD   cumulativeColor, colorSample, offsetCoeffs[4].w, cumulativeColor;     MAD   sampler, scale, offsetCoeffs[5], fragment.texcoord[0];     TEX   colorSample, sampler, texture[0], 2D;     MAD   cumulativeColor, colorSample, offsetCoeffs[5].w, cumulativeColor;     MAD   sampler, scale, offsetCoeffs[6], fragment.texcoord[0];     TEX   colorSample, sampler, texture[0], 2D;     MAD   cumulativeColor, colorSample, offsetCoeffs[6].w, cumulativeColor;     MAD   sampler, scale, offsetCoeffs[7], fragment.texcoord[0];     TEX   colorSample, sampler, texture[0], 2D;     MAD   cumulativeColor, colorSample, offsetCoeffs[7].w, cumulativeColor;     MAD   sampler, scale, offsetCoeffs[8], fragment.texcoord[0];     TEX   colorSample, sampler, texture[0], 2D;     MAD   cumulativeColor, colorSample, offsetCoeffs[8].w, cumulativeColor;     MUL   result.color, cumulativeColor, 1.15;     END      </RawData>   </FRAGMENT_PROGRAM></Shaders>


The x and y components of texOffset are 1.0f/blurTextureWidth and 1.0f/blurTextureHeight.
The z and w are equal to 1 and 0 or 0 and 1 depending on whetheir we want to blur the target in the x or y direction.
You need to refresh those values, z and w, everytime you call your shaders to obtain proper blur.
This is how I do it:

  blurShader.updateElementsf("texOffset", 4, Tuple4f(1.0f/blurScreenWidth,                                                     1.0f/blurScreenHeight,                                                     !vertical, vertical));

Take care
Thank you JavaCoolDude and all , for your helps :D
bye
My method would probably encompass:

render the scene to a pbuffer or something, with a smaller size than what you're wanting [you can also get antialiasing by making it larger] and then draw that pbuffer to the screen by a full 2D quad.

That should work on everything down to the bottom of the GeForce series...

CJM
Hello :), thank you for your reply.
I used to use this way before but you can't have the full control of your blur.
Thank you again :).
bye

This topic is closed to new replies.

Advertisement