pure python gaussian blur

G

Gerdus van Zyl

Does anyone have a relatively fast gaussian blur implemented in pure
python? Below is my attempt but it takes 2.9 seconds for a 320x240
image. Image comes from byte string: self.array =
array.array('B',srcstring). Would some sort of matrix multiplication
be faster? I don't have experience in that.

I don't want to use PIL or http://filters.sourceforge.net/ to avoid
the 300kb dll just for a simple blur.


def blur(self):
# Convolution Kernels
blurFilter = [[0,1,2,1,0],
[1,2,4,2,1],
[2,4,8,4,2],
[1,2,4,2,1],
[0,1,2,1,0]]
blurDiv = 48
blurFilterSize = len(blurFilter) #5
blurFilterSize2 = blurFilterSize * blurFilterSize

pixels = self.array

stride = self.width * 4
w = self.width
h = self.height
red = 0
green = 0
blue = 0
sx = 0
ex = 0
sy = 0
ey = 0
ss = 0
f1 = 0
f2 = 0
ff = 0
idx = 0
samplesize = 0

offset1 = blurFilterSize/2
offset2 = blurFilterSize/2+2

clr = array.array('B',[255,255,255,255])
px = array.array('B',[255,255,255,255])

for x in xrange(0,w): #w

sx = x - offset1
ex = x + offset2
if sx < 0: sx = x
if ex > w: ex = w

for y in xrange(0,h):
sy = y - offset1
ey = y + offset2

if sy < 0: sy = y

if ey > h: ey = h

samplesize = (ex-sx) * (ey-sy)
if samplesize > 0:
ss = blurFilterSize2 / samplesize
if ss > blurFilterSize - 2:
ss = blurFilterSize - 2
else:
ss = 0


idx = 0
ffx = 0
red = 0
green = 0
blue = 0

for vx in xrange(sx,ex):
for vy in xrange(sy,ey):
offset = vy * stride + vx * 4

px = pixels[offset:eek:ffset
+4]

f1 = idx / 5
f2 = (idx - f1) % 5
f1 += ss
if f1 > 4:
f1 = 4

ff = blurFilter[f1]
[f2]
#ff = 1
ffx += ff
red += px[0] * ff
green += px[1] * ff
blue += px[2] * ff
idx += 1


if samplesize > 0:
red = red / ffx
green = green / ffx
blue = blue / ffx
if red > 255: red = 255
if green > 255: green = 255
if blue > 255: blue = 255

clr[0] = red
clr[1] = green
clr[2] = blue

self.setPixel(x, y, clr)
 
P

Patrick Doyle

Does anyone have a relatively fast gaussian blur implemented in pure
python? Below is my attempt but it takes 2.9 seconds for a 320x240
image. Image comes from byte string: self.array =
array.array('B',srcstring). Would some sort of matrix multiplication
be faster? I don't have experience in that.

I don't want to use PIL or http://filters.sourceforge.net/ to avoid
the 300kb dll just for a simple blur.
The one time I tried something similar (implementing a signal
processing function in Python), I found NumPy to be _significantly_
faster -- which makes sense, since all of the matrix arithmetic was
implemented in hard coded, compiled C code instead of interpreted byte
code.

--wpd
 
G

Gerdus van Zyl

Ok, now answering my own question :)
Gaussian blurs can be optimized by seperating the equation and
applying in two passes.
I also implemented a version using State Vector Machines from www-
personal.engin.umd.umich.edu/~jwvm/ece581/21_GBlur.pdf but it does not
produce correct results. Most probably my fault, but it is 4x faster.
(code available on request)

Then after all this i learnt that using multiple box blurs approximate
a gaussian blur within 3%. This is the solution i use now and is fast
enough for my needs and in pure python.

Code for gaussian blur and box blur below.

~Gerdus van Zyl

Seperated Gaussian blur:

def blurB(self):

pixels = self.array
temp1 = copy.copy(pixels)
#temp2 = copy.copy(pixels)
stride = self.width * 4

clr = array.array('B',[255,255,255,255])

w = self.width
h = self.height

def pascal(n):
"""Prints n first lines of Pascal`s triangle

author: DR#m <dnpsite.narod.ru>
last rev 20.03.05"""
#n += 1

l=[1]
p=[]
for i in xrange(n):
l2=[1]
for j in xrange(len(l)-1):
l2.append(l[j]+l[j+1])
l2.append(1)
#print l
l=l2
return l


N = 5
g = pascal(N)
print '>',g

gauss_fact = g
#gauss_fact = [1,2,1]
#gauss_fact = [1,1]
gauss_width = len(gauss_fact)
s = 0
for n in gauss_fact:
s += n
gauss_sum = s



print "Pass01"
for i in range(1,w-1):
#print i," of ",w
for j in range(1,h-1):
sumr=0
sumg=0
sumb=0
for k in range(0,gauss_width):
x = i-((gauss_width-1)>>1)+k
y = j
offset = y * stride + x * 4
pixel = pixels[offset:eek:ffset
+4]

r = pixel[0]
g = pixel[1]
b = pixel[2]

sumr+=r*gauss_fact[k]
sumg+=g*gauss_fact[k]
sumb+=b*gauss_fact[k]

clr[0] = sumr/gauss_sum
clr[1] = sumg/gauss_sum
clr[2] = sumb/gauss_sum

x = i
y = j
offset = y * stride + x * 4
temp1[offset:eek:ffset+4] =
clr

print "Pass02"
for i in range(1,w-1):
#print i," of ",w
for j in range(1,h-1):
sumr=0
sumg=0
sumb=0
for k in range(0,gauss_width):
x = i-((gauss_width-1)>>1)+k
y = j

offset = y * stride + x * 4
pixel = temp1[offset:eek:ffset+4]

r = pixel[0]
g = pixel[1]
b = pixel[2]

sumr+=r*gauss_fact[k]
sumg+=g*gauss_fact[k]
sumb+=b*gauss_fact[k]

clr[0] = sumr/gauss_sum
clr[1] = sumg/gauss_sum
clr[2] = sumb/gauss_sum

self.setPixel(i,j,clr) #to temp1


Box Blur:
def _boxBlur(self):

N = 5
xsum = 0
xsumar = []
pixels = self.array
opixels = copy.copy(pixels)

stride = self.width * 4

clr = array.array('B',[255,255,255,255])

w = self.width
h = self.height

pixel = 0

for rgb in [0,1,2]:
for x in range(0,w):
#print i," of ",w
for y in range(0,h):

hN = N/2
offset = (y-hN) * stride + (x-hN) * 4
if offset < 0: offset = 0
pixel = pixels[offset+rgb]

xsumar.append(pixel)

if len(xsumar) > N:
xsumar = xsumar[-N:]

xsum = 0
for v in xsumar:
xsum += v

xsum /= len(xsumar)

pixels[offset+rgb] = xsum
#x
for rgb in [0,1,2]:
for y in range(0,h):
#print i," of ",w
for x in range(0,w):

hN = N/2
offset = (y-hN) * stride + (x-hN) * 4
if offset < 0: offset = 0
pixel = pixels[offset+rgb]

xsumar.append(pixel)

if len(xsumar) > N:
xsumar = xsumar[-N:]

xsum = 0
for v in xsumar:
xsum += v

xsum /= len(xsumar)

pixels[offset+rgb] = xsum
 

Ask a Question

Want to reply to this thread or ask your own question?

You'll need to choose a username for the site, which only take a couple of moments. After that, you can post your question and our members will help you out.

Ask a Question

Members online

Forum statistics

Threads
473,995
Messages
2,570,233
Members
46,820
Latest member
GilbertoA5

Latest Threads

Top