Reputation: 105
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
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()
Upvotes: 0
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
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
Upvotes: 0
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