Recent

Author Topic: Fast hardware image blur  (Read 30407 times)

aradeonas

  • Hero Member
  • *****
  • Posts: 824
Fast hardware image blur
« on: October 15, 2015, 06:32:23 pm »
Hi,

I want to do fast blur so I need hardware acceleration, for this I found these sources but I dont know how to do it in FPC with OpenGL or OpenCL with or without BGRABitmap.
http://www.eriksmistad.no/gaussian-blur-using-opencl-and-the-built-in-images-textures/
https://software.intel.com/en-us/blogs/2014/07/15/an-investigation-of-fast-real-time-gpu-based-image-blur-algorithms 

ChrisR

  • Full Member
  • ***
  • Posts: 105
Re: Fast hardware image blur
« Reply #1 on: May 30, 2016, 10:41:11 pm »
You can do a blur easily on the CPU. The trick is that the 2D Gaussian blur is separable: a 2D blur can be computed much faster as two 1D blurs instead of an integrated 2D blur (and a 3D blur is much faster when computed as three 1D blurs). When you know this trick, you can get good performance using just the CPU for all but the largest images
  http://blog.ivank.net/fastest-gaussian-blur.html
  http://www.cabiatl.com/mricro/bounty/
  http://mantis.freepascal.org/view.php?id=10275

For very large images, the GPU will be faster. However, remember that transfers from main memory to the GPU is slow, so if you are using OpenGL on the GPU to display your images you will certainly benefit from using the GPU to blur, but if you want to move the image back to the CPU to use a library like BBRABitmap you may find that it is hard to see a huge speedup with the GPU (since you will have to both copy the raw data to the GPU and then retrieve the blurred data). If you want to go this route, I would recommend GLSL rather than OpenCL. This reason is that many older graphics drivers do not support OpenCL and it is harder to get this to work on all platforms. The code I wrote uses GLSL and works on Intel/NVidia/AMD cards with Windows/Linux/OSX. The shaders kSmoothShaderFrag  and kSobelShaderFrag demonstrate how to call GLSL from Lazarus/FPC/Delphi to blur and blur/edgedetect a 3D image. These would be a good starting point for a 2D blur.
  https://github.com/neurolabusc/MRIcroGL/blob/master/raycastglsl.pas
« Last Edit: May 30, 2016, 10:49:43 pm by ChrisR »

aradeonas

  • Hero Member
  • *****
  • Posts: 824
Re: Fast hardware image blur
« Reply #2 on: May 31, 2016, 09:20:30 am »
Thank you very much for a useful answer ;)
Do you have a performance test that how much time it gets for a simple blur in you machine with or without OpenGL?
About MRIcroGL, What it does? and in that unit I see functions that seem to do blur, is that right? is there any demo to give me a easier start point? I'm new in this field :D
About OpenCL, how old machines dont support it? If they are much old I dont care about that if the OpenCL results is better than GLSL (they are?)

circular

  • Hero Member
  • *****
  • Posts: 3058
    • Personal webpage
Re: Fast hardware image blur
« Reply #3 on: May 31, 2016, 01:49:41 pm »
Hello!

Regarding software blur, in the latest versions of BGRABitmap, the fast blur it is done using the something similar to algorithm 4 described on http://blog.ivank.net/fastest-gaussian-blur.html

To do a "hardware" blur, basically the idea is the following (using TBGLVirtualScreen):
  • in the OnLoadTexture event, create the shader (it takes time to create a shader)
      --> create a TBGLShader3D that does the blur (you may need to derive the class to add "variables")
  • in the OnRedraw event, draw a texture with the shader
      --> assign the shader to BGLCanvas.Lighting.ActiveShader
      --> draw a texture by calling its Draw function or draw a shape
      --> assign nil to BGLCanvas.Lighting.ActiveShader
  • in the OnUnloadTexture event, free the shader

Here attached is a sample project for a shader (here it flips the image vertically).
Conscience is the debugger of the mind

aradeonas

  • Hero Member
  • *****
  • Posts: 824
Re: Fast hardware image blur
« Reply #4 on: May 31, 2016, 02:34:53 pm »
Hi Circular and thank you for the demo,
I didnt know BGRABitmap can do these stuff this easily :D . I will play with it more but I can understand  whet you said about blur "that does the blur (you may need to derive the class to add "variables")".

Also please add a simple demo when you add an ability. complicated demo is complicated and they are good for showing power not how to do that. Doing that is easier than making documentation and also Im sure you make self test project and probably you can put them in tests folder with some cleaning.

About blur speed, I tested again BGRABitmap blur and used image from the linked page and in Normal mode with r=10 it takes 700ms in my system (that is not old) and in Fast mode it takes 90 ms. Are you sure it is optimized like that or my machine is heavily slow? (I checked your code and it seems like that but I cant understand why is there such a huge difference)

ChrisR

  • Full Member
  • ***
  • Posts: 105
Re: Fast hardware image blur
« Reply #5 on: May 31, 2016, 03:10:05 pm »
My sense is the example from Circular is a great starting point. If you create a blur shader using TBGLVirtualScreen I think it would be useful for a lot of users. My own MRIcroGL is tuned as a 3D volume renderer, rather than a general purpose rendering tool. Since you ask, MRIcroGL uses GLSL for the volume rendering but it can also use GLSL for hardware accelerated computation of image gradients.
  http://www.mccauslandcenter.sc.edu/mricrogl/gradients
With MRIcroGL the user can set the preferences to either calculate the gradients on the CPU or the GPU. The relative benefit really depends on the quality of the GPU and the CPU, but modern Intel graphics seem about x8 faster on the GPU than on the Intel CPU, with a bigger benefit if you have a dedicated Nvidia or AMD GPU.

For writing your shader, I would suggest reading this article:
  https://developer.apple.com/library/mac/documentation/Performance/Conceptual/OpenCL_MacProgGuide/TuningPerformanceOntheGPU/TuningPerformanceOntheGPU.html

circular

  • Hero Member
  • *****
  • Posts: 3058
    • Personal webpage
Re: Fast hardware image blur
« Reply #6 on: May 31, 2016, 03:35:41 pm »
Hi Circular and thank you for the demo,
I didnt know BGRABitmap can do these stuff this easily :D . I will play with it more but I can understand  whet you said about blur "that does the blur (you may need to derive the class to add "variables")".
Cool.

Quote
Also please add a simple demo when you add an ability
Yes.

Quote
About blur speed, I tested again BGRABitmap blur and used image from the linked page and in Normal mode with r=10 it takes 700ms in my system (that is not old) and in Fast mode it takes 90 ms. Are you sure it is optimized like that or my machine is heavily slow? (I checked your code and it seems like that but I cant understand why is there such a huge difference)
In Normal mode, it doesn't use the optimisation with box blurs. Only fast blur with radius >= 10 does.
Conscience is the debugger of the mind

aradeonas

  • Hero Member
  • *****
  • Posts: 824
Re: Fast hardware image blur
« Reply #7 on: May 31, 2016, 03:56:23 pm »
Thank you ChrisR.

circular I had a typo :
I can't understand what you said about blur "that does the blur (you may need to derive the class to add "variables")".
Quote
In Normal mode, it doesn't use the optimisation with box blurs. Only fast blur with radius >= 10 does.
I just tested and it takes 62ms for r=9 and 90 for r=11. it seems not not to me it has a much difference and I expect faster result.

circular

  • Hero Member
  • *****
  • Posts: 3058
    • Personal webpage
Re: Fast hardware image blur
« Reply #8 on: May 31, 2016, 05:40:50 pm »
Quote
I can't understand what you said about blur "that does the blur (you may need to derive the class to add "variables")".
Oh ok. I suggest to look at the example I posted, and look at the GLSL files. There is the code for the shader. And that's where you would need to put some other code.

The GLSL code is related to a TBGLShader3D class. In the example it is defined in umyshader.pas. The uniform variables are like parameters that are transmitted to the GLSL code. They are defined both in the class and in the GLSL (in mix.fragment.glsl).

For example the blur radius would be a uniform variable, like the fade_factor in the example.

Quote
I just tested and it takes 62ms for r=9 and 90 for r=11. it seems not not to me it has a much difference and I expect faster result.
Surprising. Well the fast blur without box blur is already optimized so the difference is not that big with a small radius. The difference is significant with a radius like 80 for example. To see the difference, you would need to comment out the "if" block in the fast blur where it calls the box blur.

After that, you can try to call box blur directly. Maybe the box blur could be optimized?



Conscience is the debugger of the mind

aradeonas

  • Hero Member
  • *****
  • Posts: 824
Re: Fast hardware image blur
« Reply #9 on: October 03, 2016, 10:25:26 am »
Hi circular,

I totally forgot this topic and today I tried to test my version but I coulnt port such thing to your demo, it is more complicated and I dont know about them:
http://rastergrid.com/blog/2010/09/efficient-gaussian-blur-with-linear-sampling/
Can you help me with that? and also please include previous example with BGRA, its good to have these samples as reference.

circular

  • Hero Member
  • *****
  • Posts: 3058
    • Personal webpage
Re: Fast hardware image blur
« Reply #10 on: October 03, 2016, 11:55:57 am »
To do this example, you only need to change the code in mix.fragment.glsl file.

Some variables need to be replaced:

"image" becomes "textures[0]" (what the application refers to)
"FragmentColor" becomes "gl_FragColor" because the GLSL variable is used instead of using an output variable.
"gl_FragCoord" becomes "texcoord" (what the vertex shader yields).

So you would get something like:
Code: C  [Select]
  1. uniform float fade_factor;
  2. uniform sampler2D textures[1];
  3.  
  4. varying vec2 texcoord;
  5.  
  6. uniform float offset[5] = float[]( 0.0, 1.0, 2.0, 3.0, 4.0 );
  7. uniform float weight[5] = float[]( 0.2270270270, 0.1945945946, 0.1216216216,
  8.                                    0.0540540541, 0.0162162162 );
  9. uniform float textureHeight = 200;                                                               
  10.                                                                            
  11. void main()
  12. {
  13.         gl_FragColor = texture2D(textures[0], texcoord) * weight[0];
  14.         for (int i=1; i<5; i++) {
  15.                 gl_FragColor +=
  16.                         texture2D(textures[0], vec2(texcoord)+vec2(0.0, offset[i]/textureHeight) )
  17.                                 * weight[i];
  18.                 gl_FragColor +=
  19.                         texture2D(textures[0], vec2(texcoord)-vec2(0.0, offset[i]/textureHeight) )
  20.                                 * weight[i];
  21.         }
  22.  
  23.         // use the fade factor to choose the level of blur
  24.         gl_FragColor = gl_FragColor*fade_factor + texture2D(textures[0], texcoord)*(1 - fade_factor);
  25. }
« Last Edit: October 03, 2016, 12:11:53 pm by circular »
Conscience is the debugger of the mind

aradeonas

  • Hero Member
  • *****
  • Posts: 824
Re: Fast hardware image blur
« Reply #11 on: October 03, 2016, 12:25:25 pm »
Thank you very much!
It made it more clear for me.
I tested it and result is fast but I coulnt make the blur more strong. This post seems a good one and I wanted to do such thing like sample in the post or like this implemention:
https://github.com/Jam3/glsl-fast-gaussian-blur
What is your opinion abut this way? Is it good?

circular

  • Hero Member
  • *****
  • Posts: 3058
    • Personal webpage
Re: Fast hardware image blur
« Reply #12 on: October 03, 2016, 12:46:08 pm »
The level of blur here via fade_factor is just from non-blurred to blurred. It does not go further. For a wider blur, there would be more coefficients of course.

Note that this is not the optimised version using the linear interpolation. That's would be the second example of
http://rastergrid.com/blog/2010/09/efficient-gaussian-blur-with-linear-sampling/ and I leave that to you.

Also the blur here is one-dimensional. You can make it two dimensional, but that requires two "for" loops, that's slower obviously. In principle, Guassian blur can be done in two passes, one horizontal and one vertical but that would require to store a temporary image that is blurred in one direction.
Code: C  [Select]
  1. uniform float fade_factor;
  2. uniform sampler2D textures[1];
  3.  
  4. varying vec2 texcoord;
  5.  
  6. uniform float offset[5] = float[]( 0.0, 1.0, 2.0, 3.0, 4.0 );
  7. uniform float weight[5] = float[]( 0.2270270270, 0.1945945946, 0.1216216216,
  8.                                    0.0540540541, 0.0162162162 );
  9. uniform float textureWidth = 320;
  10. uniform float textureHeight = 200;                                  
  11.                                        
  12. void main()
  13. {
  14.     float w;
  15.     gl_FragColor = vec4(0);
  16.     for (int y = 0; y < 5; y++) {
  17.     for (int x = 0; x < 5; x++) {
  18.         w = weight[x]*weight[y];
  19.         gl_FragColor += texture2D(textures[0], vec2(texcoord)+vec2(offset[x]/textureWidth, offset[y]/textureHeight) ) * w;
  20.         if (x > 0)
  21.             gl_FragColor += texture2D(textures[0], vec2(texcoord)+vec2(-offset[x]/textureWidth, offset[y]/textureHeight) ) * w;
  22.         if (y > 0)
  23.         {
  24.             gl_FragColor += texture2D(textures[0], vec2(texcoord)+vec2(offset[x]/textureWidth, -offset[y]/textureHeight) ) * w;
  25.             if (x > 0)
  26.                 gl_FragColor += texture2D(textures[0], vec2(texcoord)+vec2(-offset[x]/textureWidth, -offset[y]/textureHeight) ) * w;
  27.         }
  28.     }
  29.     }
  30.  
  31.     // use the fade factor to choose the level of blur
  32.     gl_FragColor = gl_FragColor*fade_factor + texture2D(textures[0], texcoord)*(1 - fade_factor);
  33. }

On the page https://github.com/Jam3/glsl-fast-gaussian-blur the algorithm is also one-dimensional. It needs to be called twice to get a regular blur (two-dimensional).

I wonder if that would be doable by rendering the texture first in a framebuffer with the blur in one direction and then displaying from that framebuffer while applying the blur in the other direction.
« Last Edit: October 03, 2016, 12:48:08 pm by circular »
Conscience is the debugger of the mind

aradeonas

  • Hero Member
  • *****
  • Posts: 824
Re: Fast hardware image blur
« Reply #13 on: October 03, 2016, 01:15:24 pm »
Thanks. So I should learn this way and make a better version of it.

aradeonas

  • Hero Member
  • *****
  • Posts: 824
Re: Fast hardware image blur
« Reply #14 on: October 03, 2016, 02:41:16 pm »
I am learning GLSL as I didnt knew it before.
Im reading the code too and here :
Code: C  [Select]
  1.                         for (int i=1; i<iterations; i++) {
  2.                                 glBindFramebuffer(GL_DRAW_FRAMEBUFFER, fbo[1]);
  3.                                 glBindTexture(GL_TEXTURE_2D, fbtex[0]);
  4.  
  5.                                 glUseProgram(prog[mode]);
  6.                                 glDrawArrays(GL_TRIANGLES, 0, 6);
  7.  
  8.                                 glBindFramebuffer(GL_DRAW_FRAMEBUFFER, fbo[0]);
  9.                                 glBindTexture(GL_TEXTURE_2D, fbtex[1]);
  10.  
  11.                                 glUseProgram(prog[mode+2]);
  12.                                 glDrawArrays(GL_TRIANGLES, 0, 6);
  13.                         }
  14.  
iterations will be set from num keys so you can set it for example 9 at it seems it repeat the shader 9 times as he says :
Quote
f you have to apply the filter several times in order to get a more strong blur effect, the only thing you have to do is ping-pong between two framebuffers and apply the shaders to the result of the previous step.
How can I do such thing in BGL?