I have a pygame Surface and would like to invert the colors. Is there any way quicker & more pythonic than this? It's rather slow.
I'm aware that subtracting the value from 255 isn't the only definition of an "inverted color," but it's what I want for now.
I'm surprised that pygame doesn't have something like this built in!
Thanks for your help!
import pygame
def invertImg(img):
"""Inverts the colors of a pygame Screen"""
img.lock()
for x in range(img.get_width()):
for y in range(img.get_height()):
RGBA = img.get_at((x,y))
for i in range(3):
# Invert RGB, but not Alpha
RGBA[i] = 255 - RGBA[i]
img.set_at((x,y),RGBA)
img.unlock()
Winston's answer is nice, but for the sake of completeness, when one has to manipulate an image pixel-by-pixel in Python, one should avoid looping through every pixel, no matter which image library is in use. This is CPU-intensive due to the nature of the language, and can rarely be made to work in realtime.
Fortunately, the excellent NumPy library can help perform several scalar operations in streams of bytes, looping over each number in native code, which is orders of magnitude faster than doing it solely in Python. For this particular operation, if we use an xor
operation with (2^32 - 1)
, we can delegate the operation to the inner loop in native code.
This example, which you can paste directly into your Python console, will flip the pixels instantly to white (if you have NumPy installed):
import pygame
srf = pygame.display.set_mode((640,480))
pixels = pygame.surfarray.pixels2d(srf)
pixels ^= 2 ** 32 - 1
del pixels
pygame.display.flip()
Without NumPy installed, pygame.surfarray
methods return ordinary Python arrays (from the stdlib
array module) and you would have to find another way to operate on these numbers, since the ordinary Python array does not operate on all elements when a line such as pixels ^= 2 ** 32 - 1
is given.
Taken from: http://archives.seul.org/pygame/users/Sep-2008/msg00142.html
def inverted(img):
inv = pygame.Surface(img.get_rect().size, pygame.SRCALPHA)
inv.fill((255,255,255,255))
inv.blit(img, (0,0), None, BLEND_RGB_SUB)
return inv
This may do the alpha channel wrong, but you should be able to get that working with additional tweaks.
One approach that might be more efficient would be to use PIL, as described here: How to invert colors of image with PIL (Python-Imaging)?
It's easy to convert it to a native pygame image in-memory, as described here: http://mail.python.org/pipermail/image-sig/2005-May/003315.html