pythongraphicscolorspygameinversion

How to invert colors of an image in pygame?


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()

Solution

  • 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.