Nico Perez
Nico Perez

Reputation: 43

Python gray-scale conversion of an image

So I made this script that takes an image and turns it into a gray scale of itself.

I know that a lot of modules can do this automatically like .convert('grey') but I want to do it manually by myself to learn more about python programming.

It works ok but its very slow, for a 200pX200p image it takes 10 seconds so, what can I modify for making it go faster?

it works like this, it takes a pixel, calculates the averange of R, G and B values, set the three to the averange value, adds 40 to each one for more brightness and writes the pixel. Here is the code:

import imageio
import os
from PIL import Image, ImageDraw
from random import randrange


img = '/storage/emulated/0/DCIM/Camera/IMG_20190714_105429.jpg'
f = open('network.csv', 'a+')
pic = imageio.imread(img)
picture = Image.open(img)
draw = ImageDraw.Draw(picture)
f.write('\n')

def por():
    cien = pic.shape[0] * pic.shape[1]
    prog = pic.shape[1] * (h - 1) + w
    porc = prog * 100 / cien
    porc = round(porc)
    porc = str(porc)
    print(porc + '%')
rh = int(pic.shape[0])
wh = int(pic.shape[1])
for h in range(rh):
    for w in range(wh):
        prom = int(pic[h , w][0]) + int(pic[h, w][1]) + int(pic[h, w][2])
        prom = prom / 3
        prom = round(prom)
        prom = int(prom)
        prom = prom + 40
        por()
        draw.point( (w,h), (prom,prom,prom))
picture.save('/storage/emulated/0/DCIM/Camera/Modificada.jpg')

Upvotes: 1

Views: 3584

Answers (3)

jcupitt
jcupitt

Reputation: 11190

Python is an interpreted language and not really fast enough for pixel loops. cython is a sister project that can compile Python into an executable and can be faster than plain Python for code like this.

You could also try using a Python math library like numpy or pyvips. These add array operations to Python: you can write lines like a += 12 * b where a and b are whole images and they'll operate on every pixel at the same time. You get the control of being able to specify every detail of the operation yourself combined with the speed of something like C.

For example, in pyvips you could write:

import sys
import pyvips

x = pyvips.Image.new_from_file(sys.argv[1], access="sequential")
x = 299 / 1000 * x[0] + 587 / 1000 * x[1] + 114 / 1000 * x[2]
x.write_to_file(sys.argv[2])

Copying the equation from Vasu Deo.S's excellent answer, then run with something like:

./grey2.py ~/pics/k2.jpg x.png

To read the JPG image k2.jpg and write a greyscale PNG called x.png.

You can approximate conversion in linear space with a pow before and after, assuming your source image is sRGB:

x = x ** 2.2
x = 299 / 1000 * x[0] + 587 / 1000 * x[1] + 114 / 1000 * x[2]
x = x ** (1 / 2.2)

Though that's not exactly correct since it's missing the linear part of the sRGB power function.

You could also simply use x = x.colourspace('b-w'), pyvips's built-in greyscale operation.

Upvotes: 0

Vasu Deo.S
Vasu Deo.S

Reputation: 1850

The Method you are using for conversion of RGB to greyscale, is called Averaging.

from PIL import Image

image = Image.open(r"image_path").convert("RGB")

mapping = list(map(lambda x: int(x[0]*.33 + x[1]*.33 + x[2]*.33), list(image.getdata())))

Greyscale_img = Image.new("L", (image.size[0], image.size[1]), 255)

Greyscale_img.putdata(mapping)

Greyscale_img.show()

The above method (Averaging) isn't recommended for conversion of an colored image into greyscale. As it treats each color channel equally, assuming human perceives all colors equally (which is not the truth).

You should rather use something like ITU-R 601-2 luma transform (method used by PIL for converting RGB to L) for the conversion. As it uses perceptual luminance-preserving conversion to grayscale.

For that Just replace the line

mapping = list(map(lambda x: int(x[0]*.33 + x[1]*.33 + x[2]*.33), list(image.getdata())))

with

mapping = list(map(lambda x: int(x[0]*(299/1000) + x[1]*(587/1000) + x[2]*(114/1000)), list(image.getdata())))

P.S.:- I didn't add 40 to each pixel value, as it doesn't really makes any sense in the conversion of the image to greyscale.

Upvotes: 0

vekerdyb
vekerdyb

Reputation: 1263

PIL does this for you.

from PIL import Image
img = Image.open('image.png').convert('grey')
img.save('modified.png')

Upvotes: 3

Related Questions