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?
7 Answers
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."
-
Oh, it seems I've missed that module. Thanks. – bialix Mar 23 '10 at 13:33
-
yes but several PIL implementations will fail on images with modes other than `RGB` and `L` (i.e. `RGBA`, `CMYK`, `1`), in which case you have to use workarounds that convert the source image's bands into L images, then invert these and then merge them back together (see below). – mxl Jun 30 '19 at 14:06
-
module 'PIL' has no attribute 'ImageOps' – Yaroslav Dukal Jul 12 '19 at 23:56
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')

- 401
- 4
- 2
-
2Alright, but this does NOT include the alpha channel for inversion. What if I want to invert the alpha channel as well? – AgentM Oct 17 '19 at 15:56
-
1
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')

- 4,863
- 1
- 38
- 48
-
2I wish there was a way to just tell other methods that use things like masks to invert, instead of this sort of workaround... but oh well, it works for now! – nmz787 Jun 05 '20 at 22:42
now ImageOps must be:
PIL.ImageChops.invert(PIL.Image.open(imagepath))
note that this works for me in python 3.8.5

- 112
- 1
- 3
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

- 56
- 1
- 5
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()])

- 637
- 7
- 9
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()
`

- 21
- 1