Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

What's wrong with this python image blur function?

EDIT: Thanks to Howard, I've corrected the code here and it seems to be working now.

EDIT2: I've updated the code to include a vertical blur as originally intended. Resulting sample output with various settings: Blur comparison images.jpg

Another reference for blur operations (Java): Blurring for Beginners


original post:

I'm trying to learn about basic image processing and duplicate this simple Blur method (the second function BlurHorizontal under "Reusing results") in python. I know there are already blur functions in PIL, but I want to try out the basic pixel operations myself.

This function should take a source image, then average RGB pixel values based on a certain radius and write the processed image to a new file. My problem is that I'm getting a lot of pixels with completely wrong averaged values (for example, bright green lines instead of red in certain areas).

With a blur radius of 2, the averaging method adds up the RGB values for the 5 pixels centered on the input pixel. It uses a "sliding window" to keep a running total, subtracting the outgoing pixel (left side) and adding the new incoming pixel (right side of window). Blur method explained here

Sample: Blur test image output.jpg

Any ideas where I've gone wrong? I'm not sure why some parts of the image blur cleanly while other areas are filled with colors completely unrelated to the surrounding areas.

Thanks for your help.

FIXED WORKING Code (Thanks Howard)

import Image, numpy, ImageFilter
img = Image.open('testimage.jpg')

imgArr = numpy.asarray(img) # readonly

# blur radius in pixels
radius = 2

# blur window length in pixels
windowLen = radius*2+1

# columns (x) image width in pixels
imgWidth = imgArr.shape[1]

# rows (y) image height in pixels
imgHeight = imgArr.shape[0]

#simple box/window blur
def doblur(imgArr):
    # create array for processed image based on input image dimensions
    imgB = numpy.zeros((imgHeight,imgWidth,3),numpy.uint8)
    imgC = numpy.zeros((imgHeight,imgWidth,3),numpy.uint8)

    # blur horizontal row by row
    for ro in range(imgHeight):
        # RGB color values
        totalR = 0
        totalG = 0
        totalB = 0

        # calculate blurred value of first pixel in each row
        for rads in range(-radius, radius+1):
            if (rads) >= 0 and (rads) <= imgWidth-1:
                totalR += imgArr[ro,rads][0]/windowLen
                totalG += imgArr[ro,rads][1]/windowLen
                totalB += imgArr[ro,rads][2]/windowLen

        imgB[ro,0] = [totalR,totalG,totalB]

        # calculate blurred value of the rest of the row based on
        # unweighted average of surrounding pixels within blur radius
        # using sliding window totals (add incoming, subtract outgoing pixels)
        for co in range(1,imgWidth):
            if (co-radius-1) >= 0:
                totalR -= imgArr[ro,co-radius-1][0]/windowLen
                totalG -= imgArr[ro,co-radius-1][1]/windowLen
                totalB -= imgArr[ro,co-radius-1][2]/windowLen
            if (co+radius) <= imgWidth-1:
                totalR += imgArr[ro,co+radius][0]/windowLen
                totalG += imgArr[ro,co+radius][1]/windowLen
                totalB += imgArr[ro,co+radius][2]/windowLen

            # put average color value into imgB pixel

            imgB[ro,co] = [totalR,totalG,totalB]

    # blur vertical

    for co in range(imgWidth):
        totalR = 0
        totalG = 0
        totalB = 0

        for rads in range(-radius, radius+1):
            if (rads) >= 0 and (rads) <= imgHeight-1:
                totalR += imgB[rads,co][0]/windowLen
                totalG += imgB[rads,co][1]/windowLen
                totalB += imgB[rads,co][2]/windowLen

        imgC[0,co] = [totalR,totalG,totalB]

        for ro in range(1,imgHeight):
            if (ro-radius-1) >= 0:
                totalR -= imgB[ro-radius-1,co][0]/windowLen
                totalG -= imgB[ro-radius-1,co][1]/windowLen
                totalB -= imgB[ro-radius-1,co][2]/windowLen
            if (ro+radius) <= imgHeight-1:
                totalR += imgB[ro+radius,co][0]/windowLen
                totalG += imgB[ro+radius,co][1]/windowLen
                totalB += imgB[ro+radius,co][2]/windowLen

            imgC[ro,co] = [totalR,totalG,totalB]

    return imgC

# number of times to run blur operation
blurPasses = 3

# temporary image array for multiple passes
imgTmp = imgArr

for k in range(blurPasses):
    imgTmp = doblur(imgTmp)
    print "pass #",k,"done."

imgOut = Image.fromarray(numpy.uint8(imgTmp))

imgOut.save('testimage-processed.png', 'PNG')
like image 283
moski Avatar asked Nov 06 '22 00:11

moski


1 Answers

I suppose you have an issue with the line

for rads in range(-radius, radius):

which runs to radius-1 only (range excludes last). Add one to the second range argument.

Update: There is another small isue within the line

if (co-radius-1) > 0:

which should be

if (co-radius-1) >= 0:
like image 112
Howard Avatar answered Nov 11 '22 06:11

Howard