Code archives/Graphics/Gaussian Blur Filter

This code has been declared by its author to be Public Domain code.

Download source code

Gaussian Blur Filter by Queller2008
This Gaussian Blur filter is simple to use and provides great results without those weird artifacts you get when you use simple box blurs. Setting the radius to something small gives a nice smoothing effect. Naturally, larger radii will take longer to process. Please let me know of any optimizations that can be made!
'======== Example ========

SuperStrict

Local pix:TPixmap = gaussianblur(LoadPixmap("yourimagehere.png"), 5)
Local gfx:TGraphics = Graphics(pix.width, pix.height)

DrawPixmap(pix, 0, 0)
Flip
WaitMouse()

'====== Example Ends ======

Function gaussianblur:TPixmap(tex:TPixmap, radius:Int)
	If radius <=0 Return tex
	Local texclone:TPixmap = tex.copy()			'clone incoming texture
	Local filter:TGaussianFilter = New TGaussianFilter		'instantiate a new gaussian filter
		filter.radius = radius					'configure it
	Return filter.apply(tex, texclone)
End Function

Type TGaussianFilter

	Field radius:Double
	Field kernel:TKernel
	
	Method apply:TPixmap(src:TPixmap, dst:TPixmap)
		Self.kernel = makekernel(Self.radius)
		Self.convolveAndTranspose(Self.kernel, src, dst, PixmapWidth(src), PixmapHeight(src), True)
		Self.convolveAndTranspose(Self.kernel, dst, src, PixmapHeight(dst), PixmapWidth(dst), True)
		dst = Null
		GCCollect()
		Return src
	End Method

'Make a Gaussian blur kernel.

	Method makekernel:TKernel(radius:Double)
		Local r:Int = Int(Ceil(radius))
		Local rows:Int = r*2+1
		Local matrix:Double[] = New Double[rows]
		Local sigma:Double = radius/3.0
		Local sigma22:Double = 2*sigma*sigma
		Local sigmaPi2:Double = 2*Pi*sigma
		Local sqrtSigmaPi2:Double = Double(Sqr(sigmaPi2))
		Local radius2:Double = radius*radius
		Local total:Double = 0
		Local index:Int = 0

		For Local row:Int = -r To r
			Local distance:Double = Double(row*row)
			If (distance > radius2)
				matrix[index] = 0
			Else
				matrix[index] = Double(Exp(-(distance/sigma22)) / sqrtSigmaPi2)
				total:+matrix[index]
				index:+1
			End If
		Next

		For Local i:Int = 0 Until rows
			matrix[i] = matrix[i]/total			'normalizes the gaussian kernel
		Next 

		Return mkernel(rows, 1, matrix)
	End Method
	
	Function mkernel:TKernel(w:Int, h:Int, d:Double[])
		Local k:TKernel = New TKernel
			k.width = w
			k.height = h
			k.data = d
		Return k
	End Function


	Method convolveAndTranspose(kernel:TKernel, in:TPixmap, out:TPixmap, width:Int, height:Int, alpha:Int)
		Local inba:Byte Ptr = in.pixels
		Local outba:Byte Ptr = out.pixels
		Local matrix:Double[] = kernel.getKernelData()
		Local cols:Int = kernel.getWidth()
		Local cols2:Int = cols/2
		
		For Local y:Int = 0 Until height
			Local index:Int = y
			Local ioffset:Int = y*width
				For Local x:Int = 0 Until width
					Local r:Double = 0, g:Double = 0, b:Double = 0, a:Double = 0
					Local moffset:Int = cols2
						For Local col:Int = -cols2 To cols2
							Local f:Double = matrix[moffset+col]
					If (f <> 0)
						Local ix:Int = x+col
						If ( ix < 0 )
							ix = 0
						Else If ( ix >= width)
							ix = width-1
						End If
						
						Local rgb:Int = (Int Ptr inba)[ioffset+ix]
						a:+f *((rgb Shr 24) & $FF)
						b:+f *((rgb Shr 16) & $FF)
						g:+f *((rgb Shr 8) & $FF)
						r:+f *(rgb & $FF)
					End If
				Next
				Local ia:Int
					If alpha = True Then ia = clamp(Int(a+0.5)) Else ia = $FF
				Local ir:Int =clamp( Int(r+0.5))
				Local ig:Int = clamp(Int(g+0.5))
				Local ib:Int = clamp(Int(b+0.5))
				(Int Ptr outba)[index] =((ia Shl 24) | (ib Shl 16) | (ig Shl 8) | (ir Shl 0))
				index:+height
				Next
		Next
	End Method
End Type

Type TKernel

	Field width:Int
	Field height:Int
	Field data:Double[]
	
	Method getkerneldata:Double[]()
		Return Self.data
	End Method
	
	Method getwidth:Int()
		Return Self.width
	End Method
	
	Method getheight:Int()
		Return Self.height
	End Method

End Type

Function clamp:Int(val:Int)
If val < 0
	Return 0
ElseIf val > 255
	Return 255
Else
	Return val
EndIf
End Function

Comments

Jesse2008
works only with rgba images. to work with other formats, replace this line:
Local pix:TPixmap = gaussianblur(LoadPixmap("yourimagehere.png"), 5)

with this:
Local px:TPixmap = LoadPixmap("yourimagehere.png")
px = ConvertPixmap(px,PF_RGBA8888)
Local pix:TPixmap = gaussianblur(px, 5)


Works good. Thanks for sharing.


xlsior2008
Still can't get this to work: the original errors out, and the modiciation by Jesse only shows a black screen...


Jesse2008
if it is a mac, it might be a little endian big endian thing try replacing PF_RGBA8888 with PF_BGRA8888


xlsior2008
Nope, just Windows.


Jesse2008
xlsior, do you mind posting a link to the image. Maybe I can figure it out as I don't have any problems with the few images I tryed.


xlsior2008
I've tried half a dozen different images, both JPG and PNG, all with the same results: just a black screen until I click the mouse.

For example: \samples\digesteroids\graphics\digestive.png


Queller2008
xlsior, your entire screen is black? ... emphasis on entire.


Jesse2008
it works fine here 128, 130 and the svn version.
possible vista or driver issue?


xlsior2008
xlsior, your entire screen is black? ... emphasis on entire.


The window that's opened by blitz is black, yes. I can still see my normal desktop around it.

1.30 SVN version, ATI X1650XT.

I haven't seen any problems at all with other programs or Blitz sourcecode, this appears to be the only program not operating as expected...


Queller2008
xlsior, which version of windows? It works fine on XP for me. Perhaps forcing use of DX with:

Framework brl.d3d7max2d
Import brl.pngloader



xlsior2008
Nope, still nothing... Also tried full-screen mode, no change there either.


markcw2008
Works fine on XP with 1.30 and ubuntu with svn using digestive.png.


xlsior2009
I found that this works OK if I add the following to the top of the program: SetGraphicsDriver GLMax2DDriver()

but without that, it's just a black screen.


Code Archives Forum