Search code examples
pythonalgorithmpython-3.4python-imaging-libraryimagefilter

Image Sharpening Algorithm coded in Python


I was hoping someone could take a look at this sharpening algorithm I devised using PILLOW and explain to me why it is not giving a desirable sharpening effect on images. It really just looks like crap when applied to my sample images. I've worked on this for several days, but haven't made much progress in either improving the quality of the sharpening effect or the efficiency of the algorithm itself. Ideally, I'm looking for a subtle sharpening effect or something that can be scaled easily. I really appreciate any help or insight that can be provided. Here are the sources that I used to come up with this algorithm:

http://lodev.org/cgtutor/filtering.html#Sharpen

http://www.foundalis.com/res/imgproc.htm

from PIL import *
from PIL import Image
import os
os.chdir(r"C:")
filter1=9
filter2=-1


def sharpen2(photo,height,width,filter1,filter2):
    for y in range(1,height-1):
        for x in range(1,width-1):

            (r,g,b)=photo.getpixel((x,y))
            r=int(r*filter1)
            g=int(g*filter1)
            b=int(b*filter1)

            (r1,g1,b1)=photo.getpixel((x-1,y-1))
            r1=int(r1*filter2)
            g1=int(g1*filter2)
            b1=int(b1*filter2)

            (r2,g2,b2)=photo.getpixel((x,y-1))
            r2=int(r2*filter2)
            g2=int(g2*filter2)
            b2=int(b2*filter2)

            (r3,g3,b3)=photo.getpixel((x+1,y-1))
            r3=int(r3*filter2)
            g3=int(g3*filter2)
            b3=int(b3*filter2)

            (r4,g4,b4)=photo.getpixel((x-1,y))
            r4=int(r4*filter2)
            g4=int(g4*filter2)
            b4=int(b4*filter2)

            (r5,g5,b5)=photo.getpixel((x+1,y))
            r5=int(r5*filter2)
            g5=int(g5*filter2)
            b5=int(b5*filter2)

            (r6,g6,b6)=photo.getpixel((x-1,y+1))
            r6=int(r6*filter2)
            g6=int(g6*filter2)
            b6=int(b6*filter2)

            (r7,g7,b7)=photo.getpixel((x,y+1))
            r7=int(r7*filter2)
            g7=int(g7*filter2)
            b7=int(b7*filter2)

            (r8,g8,b8)=photo.getpixel((x+1,y+1))
            r8=int(r8*filter2)
            g8=int(g8*filter2)
            b8=int(b8*filter2)

            rfPixel=r+r1+r2+r3+r4+r5+r6+r7+r8
            if rfPixel>255:
                rfPixel=255
            elif rfPixel<0:
                rfPixel=0

            gfPixel= g+g1+g2+g3+g4+g5+g6+g7+g8
            if gfPixel>255:
                gfPixel=255
            elif gfPixel<0:
                gfPixel=0

            bfPixel=b+b1+b2+b3+b4+b5+b6+b7+b8
            if bfPixel>255:
                bfPixel=255
            elif bfPixel<0:
                bfPixel=0

            photo.putpixel((x,y),(rfPixel,gfPixel,bfPixel))    
    return photo


photo=Image.open("someImage.jpg").convert("RGB")
photo2=photo.copy()
height=photo.height
width=photo.width
x=sharpen2(photo,height,width,filter1,filter2)

Solution

  • One problem is likely that you're saving the results to the same image you are getting pixel data from. By the time you get to a pixel, some of its neighbors have been replaced by the filtered data, and some have not. The error is small at first but adds up.

    To fix: save the results to a different image, say filtered_photo.putpixel(...). You'd have to create a blank filtered_photo first.

    Another big problem (mentioned by @Mark Ransom) is that you probably want filter1 = 1.1 and filter2 = -0.1 or something along those lines. Using 9 and -1 will make most values come out of range.

    A better implementation: don't loop over each pixel in python code, use numpy to process the whole image at once, it will be much faster (and shorter code). The usual implementation of sharpen is to subtract the gaussian-filtered image from the original image, which is a one-liner using numpy and ndimage (or skimage).