bialix
bialix

Reputation: 21473

How to invert colors of image with PIL (Python-Imaging)?

I need to convert series of images drawn as white on black background letters to images where white and black are inverted (as negative). How can I achieve this using PIL?

Upvotes: 79

Views: 132721

Answers (7)

Gary Kerr
Gary Kerr

Reputation: 14420

Try the following from the docs: https://pillow.readthedocs.io/en/stable/reference/ImageOps.html

from PIL import Image
import PIL.ImageOps    

image = Image.open('your_image.png')

inverted_image = PIL.ImageOps.invert(image)

inverted_image.save('new_name.png')

Note: "The ImageOps module contains a number of 'ready-made' image processing operations. This module is somewhat experimental, and most operators only work on L and RGB images."

Upvotes: 129

OKwy
OKwy

Reputation: 104

Of course ImageOps does its job well, but unfortunately it can't work with some modes like 'RGBA'. This code will solve this problem.

def invert(image: Image.Image) -> Image.Image:
    drawer = ImageDraw.Draw(image)
    pixels = image.load()
    for x in range(image.size[0]):
        for y in range(image.size[1]):
            data = pixels[x, y]
            if data != (0, 0, 0, 0) and isinstance(data, tuple):
                drawer.point((x, y), (255 - data[0], 255 - data[1], 255 - data[2], data[3]))

    return image

Upvotes: 1

Ali
Ali

Reputation: 141

now ImageOps must be:

PIL.ImageChops.invert(PIL.Image.open(imagepath))

note that this works for me in python 3.8.5

Upvotes: 2

zilgo
zilgo

Reputation: 21

from PIL import Image

img = Image.open("archive.extension") 

pixels = img.load()

for i in range(img.size[0]):
    for j in range(img.size[1]):
        x,y,z = pixels[i,j][0],pixels[i,j][1],pixels[i,j][2]
        x,y,z = abs(x-255), abs(y-255), abs(z-255)
        pixels[i,j] = (x,y,z)

img.show()

`

Upvotes: -1

mxl
mxl

Reputation: 667

In case someone is inverting a CMYK image, the current implementations of PIL and Pillow don't seem to support this and throw an error. You can, however, easily circumvent this problem by inverting your image's individual bands using this handy function (essentially an extension of Greg Sadetsky's post above):

def CMYKInvert(img) :
    return Image.merge(img.mode, [ImageOps.invert(b.convert('L')) for b in img.split()])

Upvotes: 0

Greg Sadetsky
Greg Sadetsky

Reputation: 5092

For anyone working with an image in "1" mode (i.e., 1-bit pixels, black and white, stored with one pixel per byte -- see docs), you need to convert it into "L" mode before calling PIL.ImageOps.invert.

Thus:

im = im.convert('L')
im = ImageOps.invert(im)
im = im.convert('1')

Upvotes: 32

Dave
Dave

Reputation: 411

If the image is RGBA transparent this will fail... This should work though:

from PIL import Image
import PIL.ImageOps    

image = Image.open('your_image.png')
if image.mode == 'RGBA':
    r,g,b,a = image.split()
    rgb_image = Image.merge('RGB', (r,g,b))

    inverted_image = PIL.ImageOps.invert(rgb_image)

    r2,g2,b2 = inverted_image.split()

    final_transparent_image = Image.merge('RGBA', (r2,g2,b2,a))

    final_transparent_image.save('new_file.png')

else:
    inverted_image = PIL.ImageOps.invert(image)
    inverted_image.save('new_name.png')

Upvotes: 41

Related Questions