Notifications
Clear all

[Closed] Image processing GDI DotNet GaussianBlur

Hi,

I am working lately on GDI+ dotnet, I try to implement gaussian blur in my image processing :). How can i refer to direct2d in simple way(if possible)?

I found this:

Kind Regards
Eryk

8 Replies

Seems, impossible to do…maybe other how to apply gaussian blur to bitmap image? Is it possible to do in maxscript-dotnet in reasonable processing time, have found some algorithms with kernel convolution method, i will try that if there is no hardcoded way

how big are the images ?
what is “reasonable processing time” ?

real gaussian blur is not really fast, depending on blur-radius
maybe some box blurs ? –> blog.ivank.net/fastest-gaussian-blur.html

i have 2 version here working,

  • multithreaded cpu
  • a cuda implementation…

additionally there is need for a plain RGBA-buffer to work on
max-bitmaps are slooooow

Hi Guruware,

Thank you for great resource page, images I want to blur are about 15000×3000 , it is not a problem if they filter for some seconds.
I know max-bitmaps are so slow that i can make coffe and drink it by waiting for it to complete :).
The point is that i wanted to run blur filter from maxscript-> using dot net…


function GaussianBlur sourceCH targetCH Width Height radius =
(
counter=0
local rs=ceil (radius*2.57)
   for j=0 to Width do (
      for i=0 to Height do (
       local valr = 0
       local valg = 0
       local valb = 0
       local wsum=0
         for iy=i-rs to i+rs+1 do (
            for ix=j-rs to j+rs+1 do (
                x= amin#(Width-1, amax#(0, ix)) 
                y= amin#(Height-1, amax#(0, iy)) 
                dsq = (ix-j)*(ix-j)+(iy-i)*(iy-i)
               wght = exp( -dsq / 2*radius*radius ) / (PI*2*radius*radius)
               valr += (getpixels sourceCH [x,y] 1)[1].r * wght
               valg += (getpixels sourceCH [x,y] 1)[1].g * wght
               valb += (getpixels sourceCH [x,y] 1)[1].b * wght
               wsum += wght
if counter>1000 then 
(
counter=0
)
--             )
         )
counter+=1
         setpixels targetCH [j,i] #((color (floor((valr/wsum))) (floor((valg/wsum))) (floor((valb/wsum)))))
      )
   )
)
save targetCH
)
sourceCH = openBitMap "D:\DESKTOP\GDI_IMG\\sample.jpg"
targetCH = bitmap sourceCH.width sourceCH.height filename:("D:\DESKTOP\GDI_IMG\\sample blurred"+(timeStamp() as string)+".jpg") gamma:1.0
Width = sourceCH.width
Height = sourceCH.height
thetime1 = timeStamp()

GaussianBlur sourceCH targetCH (Width-1) (Height-1) 1

display targetCH
thetime2 = timeStamp()
thetime=((thetime2 - thetime1)/1000)
print (thetime as string + " s")

I tried to rewrite the code from site you (the gaussian blur part). image I was testing was about 160×100, took about 30seconds.
How your versions are working, are they external programs ?

regards
Eryk

get/set pixels is usually very slow, windows gdi drawing is very slow when doing get/set pixel
max bitmaps are no difference

this is part of my “high-speed” gfx library, which could make it into a mxs extension

with a 1024×1024 bitmap, timing in seconds


radius    CPU    CUDA
 0.7    0.078   0.016
 1.0    0.088   0.016
 2.0    0.135   0.016
 4.0    0.234   0.018
 8.0    0.374   0.023
16.0    0.764   0.039
32.0    1.466   0.078
64.0    2.948   0.140

2048×2048 will be 4 times slower

Impressive !!!

How does the installation looks like ?
Is there some place to download and try it ?
Have been on your website, you have a lot of great stuff out there.

now its just a c++ file to use in my projects
as said maybe i make a mxs extension
keep you posted…

Here’s what you can get with a bit of googling


--SuperFastBlur code source:  https://github.com/mdymel/superfastblur 


if ::SuperfastBlur == undefined do
(

    local source = "using System;
using System.Collections.Generic;
using System.Drawing;
using System.Drawing.Imaging;
using System.Runtime.InteropServices;
using System.Threading.Tasks;

namespace SuperfastBlur
{
    public class GaussianBlur
    {
        private readonly int[] _alpha;
        private readonly int[] _red;
        private readonly int[] _green;
        private readonly int[] _blue;

        private readonly int _width;
        private readonly int _height;

        private readonly ParallelOptions _pOptions = new ParallelOptions { MaxDegreeOfParallelism = 16 };

        public GaussianBlur(Bitmap image)
        {
            var rct = new Rectangle(0, 0, image.Width, image.Height);
            var source = new int[rct.Width * rct.Height];
            var bits = image.LockBits(rct, ImageLockMode.ReadWrite, PixelFormat.Format32bppArgb);
            Marshal.Copy(bits.Scan0, source, 0, source.Length);
            image.UnlockBits(bits);

            _width = image.Width;
            _height = image.Height;

            _alpha = new int[_width * _height];
            _red = new int[_width * _height];
            _green = new int[_width * _height];
            _blue = new int[_width * _height];

            Parallel.For(0, source.Length, _pOptions, i =>
            {
                _alpha[i] = (int)((source[i] & 0xff000000) >> 24);
                _red[i] = (source[i] & 0xff0000) >> 16;
                _green[i] = (source[i] & 0x00ff00) >> 8;
                _blue[i] = (source[i] & 0x0000ff);
            });
        }

        public Bitmap Process(int radial)
        {
            var newAlpha = new int[_width * _height];
            var newRed = new int[_width * _height];
            var newGreen = new int[_width * _height];
            var newBlue = new int[_width * _height];
            var dest = new int[_width * _height];
            
            Parallel.Invoke(
                () => gaussBlur_4(_alpha, newAlpha, radial),
                () => gaussBlur_4(_red, newRed, radial),
                () => gaussBlur_4(_green, newGreen, radial),
                () => gaussBlur_4(_blue, newBlue, radial));

            Parallel.For(0, dest.Length, _pOptions, i =>
            {
                if (newAlpha[i] > 255) newAlpha[i] = 255;
                if (newRed[i] > 255) newRed[i] = 255;
                if (newGreen[i] > 255) newGreen[i] = 255;
                if (newBlue[i] > 255) newBlue[i] = 255;

                if (newAlpha[i] < 0) newAlpha[i] = 0;
                if (newRed[i] < 0) newRed[i] = 0;
                if (newGreen[i] < 0) newGreen[i] = 0;
                if (newBlue[i] < 0) newBlue[i] = 0;

                dest[i] = (int)((uint)(newAlpha[i] << 24) | (uint)(newRed[i] << 16) | (uint)(newGreen[i] << 8) | (uint)newBlue[i]);
            });

            var image = new Bitmap(_width, _height);
            var rct = new Rectangle(0, 0, image.Width, image.Height);
            var bits2 = image.LockBits(rct, ImageLockMode.ReadWrite, PixelFormat.Format32bppArgb);
            Marshal.Copy(dest, 0, bits2.Scan0, dest.Length);
            image.UnlockBits(bits2);
            return image;
        }

        private void gaussBlur_4(int[] source, int[] dest, int r)
        {
            var bxs = boxesForGauss(r, 3);
            boxBlur_4(source, dest, _width, _height, (bxs[0] - 1) / 2);
            boxBlur_4(dest, source, _width, _height, (bxs[1] - 1) / 2);
            boxBlur_4(source, dest, _width, _height, (bxs[2] - 1) / 2);
        }

        private int[] boxesForGauss(int sigma, int n)
        {
            var wIdeal = Math.Sqrt((12 * sigma * sigma / n) + 1);
            var wl = (int)Math.Floor(wIdeal);
            if (wl % 2 == 0) wl--;
            var wu = wl + 2;

            var mIdeal = (double)(12 * sigma * sigma - n * wl * wl - 4 * n * wl - 3 * n) / (-4 * wl - 4);
            var m = Math.Round(mIdeal);

            var sizes = new List<int>();
            for (var i = 0; i < n; i++) sizes.Add(i < m ? wl : wu);
            return sizes.ToArray();
        }

        private void boxBlur_4(int[] source, int[] dest, int w, int h, int r)
        {
            for (var i = 0; i < source.Length; i++) dest[i] = source[i];
            boxBlurH_4(dest, source, w, h, r);
            boxBlurT_4(source, dest, w, h, r);
        }

        private void boxBlurH_4(int[] source, int[] dest, int w, int h, int r)
        {
            var iar = (double)1 / (r + r + 1);
            Parallel.For(0, h, _pOptions, i =>
            {
                var ti = i * w;
                var li = ti;
                var ri = ti + r;
                var fv = source[ti];
                var lv = source[ti + w - 1];
                var val = (r + 1) * fv;
                for (var j = 0; j < r; j++) val += source[ti + j];
                for (var j = 0; j <= r; j++)
                {
                    val += source[ri++] - fv;
                    dest[ti++] = (int)Math.Round(val * iar);
                }
                for (var j = r + 1; j < w - r; j++)
                {
                    val += source[ri++] - dest[li++];
                    dest[ti++] = (int)Math.Round(val * iar);
                }
                for (var j = w - r; j < w; j++)
                {
                    val += lv - source[li++];
                    dest[ti++] = (int)Math.Round(val * iar);
                }
            });
        }

        private void boxBlurT_4(int[] source, int[] dest, int w, int h, int r)
        {
            var iar = (double)1 / (r + r + 1);
            Parallel.For(0, w, _pOptions, i =>
            {
                var ti = i;
                var li = ti;
                var ri = ti + r * w;
                var fv = source[ti];
                var lv = source[ti + w * (h - 1)];
                var val = (r + 1) * fv;
                for (var j = 0; j < r; j++) val += source[ti + j * w];
                for (var j = 0; j <= r; j++)
                {
                    val += source[ri] - fv;
                    dest[ti] = (int)Math.Round(val * iar);
                    ri += w;
                    ti += w;
                }
                for (var j = r + 1; j < h - r; j++)
                {
                    val += source[ri] - source[li];
                    dest[ti] = (int)Math.Round(val * iar);
                    li += w;
                    ri += w;
                    ti += w;
                }
                for (var j = h - r; j < h; j++)
                {
                    val += lv - source[li];
                    dest[ti] = (int)Math.Round(val * iar);
                    li += w;
                    ti += w;
                }
            });
        }
    }
}"
    
    csharpProvider = dotnetobject "Microsoft.CSharp.CSharpCodeProvider"
    compilerParams = dotnetobject "System.CodeDom.Compiler.CompilerParameters"
    compilerParams.ReferencedAssemblies.Add("System.dll");    
    compilerParams.ReferencedAssemblies.Add("System.Drawing.dll");
    compilerParams.GenerateInMemory = on
    compilerResults = csharpProvider.CompileAssemblyFromSource compilerParams #(source)
    
    
    if (compilerResults.Errors.Count > 0 ) then
    (
        local errs = stringstream ""
        for i = 0 to (compilerResults.Errors.Count-1) do
        (
            local err = compilerResults.Errors.Item[i]
            format "Error:% Line:% Column:% %
" err.ErrorNumber err.Line err.Column err.ErrorText to:errs
        )
        format "%
" errs
        undefined
    )

    compilerResults.CompiledAssembly.CreateInstance "SuperfastBlur"    

)


img = (dotNetClass "system.windows.forms.clipboard").getImage() -- Get Test Image From Clipboard
gblur = dotNetObject "SuperfastBlur.GaussianBlur" img

gc();t1=timestamp();hf = heapfree

     result = gblur.Process 64 -- blur radius = 64

format "Time: %sec. Mem: %
" ((timestamp()-t1)/1000 as float) (hf-heapfree)
(dotNetClass "system.windows.forms.clipboard").setImage result -- Put Processed Image Back to Clipboard

-- Time: 0.432sec. Mem: 2104L (1024px x 1024px)

@Guruware: Looking forward to see your work!
@Serejah: You have solve my problem, thank you very much !!! Works like a charm !