# Gaussian Blur

I wanted to implement a Gaussian Blur algorithm in Xojo. As an example I use this Swift code:

``````// Dependency: SwiftImage

// Usage: let outputImage = GaussianBlur.createBlurredImage(radius: 5, image: UIImage(named: "IMAGE_NAME")!)

final class GaussianBlur {

/// Returns a gaussian blurred image

/// - Parameters:

///   - radius: This relates to the strength of the blur. The kernel will be 1 + (2 * radius) in width/height to ensure center pixel exists.

///   - image: The source image

/// - Returns: Returns a SwiftImage (UIImage can be extracted from this object)

static func createBlurredImage(radius: Int, image: UIImage) -> Image<RGBA<UInt8>> {

let inputImage = Image<RGBA<UInt8>>(uiImage: image)

var outputImage = Image<RGBA<UInt8>>(uiImage: image)

// We scale the sigma value in proportion to the radius

// Setting the minimum standard deviation as a baseline

let sigma = max(Double(radius / 2), 1)

// Enforces odd width kernel which ensures a center pixel is always available

let kernelWidth = (2 * radius) + 1

// Initializing the 2D array for the kernel

var kernel = Array(repeating: Array(repeating: 0.0, count: kernelWidth), count: kernelWidth)

var sum = 0.0

// Populate every position in the kernel with the respective Gaussian distribution value

// Remember that x and y represent how far we are away from the CENTER pixel

let exponentNumerator = Double(-(x * x + y * y))

let exponentDenominator = (2 * sigma * sigma)

let eExpression = pow(M_E, exponentNumerator / exponentDenominator)

let kernelValue = (eExpression / (2 * Double.pi * sigma * sigma))

// We add radius to the indices to prevent out of bound issues because x and y can be negative

sum += kernelValue

}

}

// Normalize the kernel

// This ensures that all of the values in the kernel together add up to 1

for x in 0..<kernelWidth {

for y in 0..<kernelWidth {

kernel[x][y] /= sum

}

}

// Ignoring the edges for ease of implementation

// This will cause a thin border around the image that won't be processed

var redValue = 0.0

var greenValue = 0.0

var blueValue = 0.0

// This is the convolution step

// We run the kernel over this grouping of pixels centered around the pixel at (x,y)

// Load the weight for this pixel from the convolution matrix

// Multiply each channel by the weight of the pixel as specified by the kernel

redValue += Double(inputImage[x - kernelX, y - kernelY].red) * kernelValue

greenValue += Double(inputImage[x - kernelX, y - kernelY].green) * kernelValue

blueValue += Double(inputImage[x - kernelX, y - kernelY].blue) * kernelValue

}

}

// New RGB value for output image at position (x,y)

outputImage[x,y].red = UInt8(redValue)

outputImage[x,y].green = UInt8((greenValue))

outputImage[x,y].blue = UInt8(blueValue)

}

}

return outputImage

}

``````

My Xojo port:

``````Function GBlur(Radius as Double, InputPicture as Picture) As Picture
Const PI = 3.141592
Const E = 2.71828

Var InSurf As RGBSurface = InputPicture.RGBSurface

Var OutputPicture As Picture

OutputPicture = New Picture(InputPicture.Width, InputPicture.Height,32)

Var OutSurf As RGBSurface = OutputPicture.RGBSurface

Var Sigma As Double = Max(Radius / 2, 1)
Var KernelWidth As Integer = ( 2 * Radius) + 1

Var Kernel(-1,-1) As Double

Kernel.ResizeTo(KernelWidth,KernelWidth)

Var Sum As Double = 0.0

Var ExponentNumerator As Double = -(x * x + y * y)
Var ExponentDenominator As Double = (2 * Sigma * Sigma)

Var eExpression As Double = Pow(E, exponentNumerator / exponentDenominator)
Var KernelValue As Double = (eExpression / (2 * PI * sigma * sigma))

Sum = Sum + KernelValue
Next
Next

For X As Integer = 0 To KernelWidth
For Y As Integer = 0 To KernelWidth
Kernel(X,Y) = Kernel(X,Y) / Sum
Next
Next

Var RedValue As Double = 0.0
Var GreenValue As Double = 0.0
Var BlueValue As Double = 0.0

RedValue = RedValue + InSurf.Pixel(KernelX, KernelY).Red * KernelValue
GreenValue = GreenValue + InSurf.Pixel(KernelX, KernelY).Green * KernelValue
BlueValue = BlueValue + InSurf.Pixel(KernelX, KernelY).Blue * KernelValue

OutSurf.Pixel(KernelX, KernelY) = RGB(RedValue,GreenValue,BlueValue)

Next
Next

Return OutputPicture

End Function
``````

This code looks at a specific pixel (center pixel) and calculates the color values of the output pixel by examining the pixels around the center pixel. This is the Radius parameter. A Radius of 5 means that X±5 to X+5 and Y-5 to Y+5 gets examined (this is the ‘-Radius to Radius’ statement). The multidim Kernel array keeps track of this. I know that some vars should be integer instead of double but I will fix this later.

My Xojo code compiles… but I get an ‘out of bounds’ error while iterating over the kernel array in the last for-next loop. I can’t find where it goes wrong. I know this is a bit complex but anybody who can help me out?

First of all… Oh boy, this is inefficient, look into separable kernels for things like this (I had a form of blur that would take close to 10 minutes on a 20 megapixel image), once I’d switched over to separable, it took less than 2 seconds. There was still room for improvement!

Secondly, I believe that your outofbounds is because the process is in reverse, you want to loop through each pixel of the image and pick out the surrounding values, adding them to the central pixel using a predetermined weight.

Look at your code, it seems like your loop round the kernelSize and then trying to read all the pixels of the image, for which there are not enough weights.

I can’t say it is inefficient or not - IMHO for a blur you need to look at an individual pixel and its neighbors, and determine the weight, there is no other way or is it?

As Sam wrote, this is really slow.

Anyway, you missed the inner loops; this should work:

``````Const PI = 3.141592
Const E = 2.71828

Var InSurf As RGBSurface = InputPicture.RGBSurface

Var OutputPicture As Picture

OutputPicture = New Picture(InputPicture.Width, InputPicture.Height,32)

Var OutSurf As RGBSurface = OutputPicture.RGBSurface

Var Sigma As Double = Max(Radius / 2, 1)
Var KernelWidth As Integer = ( 2 * Radius) + 1

Var Kernel(-1,-1) As Double

Kernel.ResizeTo(KernelWidth,KernelWidth)

Var Sum As Double = 0.0

Var ExponentNumerator As Double = -(x * x + y * y)
Var ExponentDenominator As Double = (2 * Sigma * Sigma)

Var eExpression As Double = Pow(E, exponentNumerator / exponentDenominator)
Var KernelValue As Double = (eExpression / (2 * PI * sigma * sigma))

Sum = Sum + KernelValue
Next
Next

For X As Integer = 0 To KernelWidth
For Y As Integer = 0 To KernelWidth
Kernel(X,Y) = Kernel(X,Y) / Sum
Next
Next

Var RedValue   As Double = 0.0
Var GreenValue As Double = 0.0
Var BlueValue  As Double = 0.0

Var KernelValue As Double

RedValue   = 0.0
GreenValue = 0.0
BlueValue  = 0.0

RedValue   = RedValue   + InSurf.Pixel(x - KernelX, y - KernelY).Red   * KernelValue
GreenValue = GreenValue + InSurf.Pixel(x - KernelX, y - KernelY).Green * KernelValue
BlueValue  = BlueValue  + InSurf.Pixel(x - KernelX, y - KernelY).Blue  * KernelValue
Next
Next

OutSurf.Pixel(x, y) = RGB(RedValue, GreenValue, BlueValue)
Next
Next

Return OutputPicture

``````
1 Like

Ah, I see. Thanks. My purpose with this code was to see how Gaussian blur actually works. What would be a better approach?

For learning this is perfectly fine.

A box blur is much faster to compute than a gaussian blur and can produce a gaussian blur effect by performing the blur 3 times using 1/3 of the radius size.

The Gaussian blur took on a 1100*600 image with radius 3 16,3 secs, the box blur with radius 3 1,1 sec…

Did you perform the box blur once with a radius of 3 or did you perform a box blur three times with a radius of 1?