kneesarethebees
kneesarethebees

Reputation: 105

Processing an image to sepia tone in Python

I need help figuring out how to convert an image to sepia. This is what I have so far..but it only changes everything to black and white colors with a very small tint of brown. I'm not sure what I'm doing wrong :(

import image

def convertSepia(input_image):
    grayscale_image = image.EmptyImage(input_image.getWidth(), input_image.getHeight())

    for col in range(input_image.getWidth()):
        for row in range(input_image.getHeight()):
            p = input_image.getPixel(col, row)

            R = p.getRed()
            G = p.getGreen()
            B = p.getBlue()

            newR = (R * 0.393 + G * 0.769 + B * 0.189)
            newG = (R * 0.349 + G * 0.686 + B * 0.168)
            newB = (R * 0.272 + G * 0.534 + B * 0.131)

            newpixel = image.Pixel(newR, newG, newB)
            grayscale_image.setPixel(col, row, newpixel)

    sepia_image = image.EmptyImage(input_image.getWidth(), input_image.getHeight())
    for col in range(input_image.getWidth()):
        for row in range(input_image.getHeight()):
            p = grayscale_image.getPixel(col, row)
            red = p.getRed()
            if red > 140:
                val = (R * 0.393 + G * 0.769 + B * 0.189)
            else:
                val = 0
            green = p.getGreen()
            if green > 140:
                val = (R * 0.349 + G * 0.686 + B * 0.168)
            else:
                val = 0
            blue = p.getBlue()
            if blue > 140:
                val = (R * 0.272 + G * 0.534 + B * 0.131)
            else:
                val = 0

            newpixel = image.Pixel(val, val, val)
            sepia_image.setPixel(col, row, newpixel)
    return sepia_image


win = image.ImageWin() img = image.Image("luther.jpg")

sepia_img = convertSepia(img) sepia_img.draw(win)

win.exitonclick()

Any more tips as to where to go from here? Thanks :)

Upvotes: 5

Views: 15747

Answers (4)

Sandipan Dey
Sandipan Dey

Reputation: 23101

With numpy vectorization, the implementation can be made much faster. Consider the following code that compares the numpy's vectorized implementation with PIL's loopy one, both of them producing the same output image (using the same turtle input image by @Trect). As can be seen from the below output, the numpy implementation is 36 times faster than PIL's, for the given input image.

from PIL import Image
import numpy as np
from time import time
import matplotlib.pylab as plt

def sepia_filter_PIL(image_path):
    img = Image.open(image_path)
    width, height = img.size
    pixels = img.load() # create the pixel map
    for py in range(height):
        for px in range(width):
            r, g, b = img.getpixel((px, py))
            pixels[px, py] = (min(255, int(0.393 * r + 0.769 * g + 0.189 * b)), \
                              min(255, int(0.349 * r + 0.686 * g + 0.168 * b)), \
                              min(255, int(0.272 * r + 0.534 * g + 0.131 * b)))
    return img


def sepia_filter_np(image_path):
    im = plt.imread(image_path)
    im = im / im.max()    
    R, G, B = im[...,0], im[...,1], im[...,2]
    im_out = np.dstack((0.393 * R + 0.769 * G + 0.189 * B, \
                        0.349 * R + 0.686 * G + 0.168 * B, \
                        0.272 * R + 0.534 * G + 0.131 * B))
    im_out = np.clip(im_out, 0, 1)
    return (255*im_out).astype(np.uint8)

img_path = 'images/turtle.jpg'
start = time()
im_out_np = sepia_filter_np(img_path) 
# time (np): 0.327577 sec
print('time (np): {:03f} sec'.format(time() - start))
start = time()
im_out_PIL = sepia_filter_PIL(img_path) 
print('time (PIL): {:03f} sec'.format(time() - start))
# time (PIL): 11.972195 sec
im, im_out_PIL = plt.imread(img_path), np.array(im_out_PIL)
plt.figure(figsize=(20,7))
plt.imshow(np.hstack((im, im_out_np, im_out_PIL))), plt.axis('off')
plt.show()

enter image description here

Upvotes: 0

debiday
debiday

Reputation: 47

This is my solution that do not require conversion to grayscale before sepia. I used the given formula for sepia:

newR = (R × 0.393 + G × 0.769 + B × 0.189)
newG = (R × 0.349 + G × 0.686 + B × 0.168)
newB = (R × 0.272 + G × 0.534 + B × 0.131)

Full code:

import image

img= image.Image("luther.jpg")
win=image.ImageWin(img.getWidth(), img.getHeight())
img.draw(win)
img.setDelay(1,100)

for row in range(img.getHeight()):
    for col in range(img.getWidth()):
        p=img.getPixel(col,row)
        R= p.getRed()
        G= p.getGreen()
        B= p.getBlue()
        newR = 0.393*R + 0.769*G + 0.189*B
        newG = 0.349*R + 0.686*G + 0.168*B
        newB = 0.272*R + 0.534*G + 0.131*B
        newpixel= image.Pixel(newR,newG,newB)
        img.setPixel(col, row, newpixel)
        
img.draw(win)
win.exitonclick()

Upvotes: 0

Trect
Trect

Reputation: 2945

You can convert image to sepia by just manipulating the pixel values. The following is the code(Disclaimer : Taken from this article.)

from PIL import Image

def sepia(image_path:str)->Image:
    img = Image.open(image_path)
    width, height = img.size

    pixels = img.load() # create the pixel map

    for py in range(height):
        for px in range(width):
            r, g, b = img.getpixel((px, py))

            tr = int(0.393 * r + 0.769 * g + 0.189 * b)
            tg = int(0.349 * r + 0.686 * g + 0.168 * b)
            tb = int(0.272 * r + 0.534 * g + 0.131 * b)

            if tr > 255:
                tr = 255

            if tg > 255:
                tg = 255

            if tb > 255:
                tb = 255

            pixels[px, py] = (tr,tg,tb)

    return img

Original Image enter image description here

Sepia Image enter image description here

Upvotes: 0

Piglet
Piglet

Reputation: 28940

Your gray level image is not a gray level image. In a gray level image all three channels r,g,b have the same value.

Open paint and try it to verify if your code makes sense.

Fix these lines:

newR = (R * 0.393 + G * 0.769 + B * 0.189)
newG = (R * 0.349 + G * 0.686 + B * 0.168)
newB = (R * 0.272 + G * 0.534 + B * 0.131)

Simply use the mean of r,g,b and put it into newR, newG and newG.

There are some weighted means as well. Just Google for RGB to intensity formulas.

Upvotes: 1

Related Questions