11

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()
Richard Chambers
  • 16,643
  • 4
  • 81
  • 106
Ain Britain
  • 370
  • 4
  • 15

3 Answers3

11

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.

Winston Ewert
  • 44,070
  • 10
  • 68
  • 83
7

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.

Augusta
  • 7,171
  • 5
  • 24
  • 39
jsbueno
  • 99,910
  • 10
  • 151
  • 209
  • numpy is awesome, I use it in almost every python project – Winston Ewert May 05 '11 at 03:15
  • @jsbueno I'm curious, why did you do "del pixels"? – Ain Britain May 05 '11 at 04:13
  • @jsbueno Well, I figured out that you have to delete the surfarray to unlock the Surface. But what I can't figure out despite 3 hours of searching is how the value that you get from value = pixels[x,y] corresponds to RGB values. It's not hex, what is it? pygame.surfarray.pixels3d(srf) gives you [x][y][r,g,b], but what about the 2d array? – Ain Britain May 05 '11 at 05:42
  • @Ain, hex is just a way of representing a number. If you pass the value into the hex() function you'll get the hex representation. – Winston Ewert May 05 '11 at 15:33
  • 1
    @Winston Ewert I didn't understand what you meant at first, but I have found out that I convert the RBG value, ex 3112142 to hex with hex(3112142), I get 0xRRGGBB, where for example RR is the base-16 red value. Phew! So you have to parse it and do int("RR", 16) to get the 0-255 values. – Ain Britain May 07 '11 at 03:37
1

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

Community
  • 1
  • 1
Mu Mind
  • 10,935
  • 4
  • 38
  • 69
  • Thanks, but I'm trying to avoid additional modules and have already committed to pygame. I should have mentioned that earlier, sorry! – Ain Britain May 05 '11 at 02:07
  • 3
    @Ain Britain, don't. A huge strength of python is the massive amount of code already written for you. You are missing out if you don't add module that help support what you want. – Winston Ewert May 05 '11 at 02:14
  • @Winston: However, as your and mine answers denote, PIL is not the best thing for this -- converting between image data between PIL and Pygame surfaces is painfull in itself. – jsbueno May 05 '11 at 02:29
  • 2
    @jsbueno, yes, in this case using PIL is overkill. But in principle, one shouldn't be scared of using additional libraries. – Winston Ewert May 05 '11 at 03:13
  • 1
    @Winston Ewert Agree 100% on code reuse. Unfortunately PIL is kind of a crappy dependency to have. I'm sure you knew that, but I just thought I would mention it for our readers :-) – Brian O'Dell May 05 '11 at 15:20
  • @Brian, I'm not aware of any particular problems of PIL, although I've not used it much. – Winston Ewert May 05 '11 at 15:29
  • @Winston Ewert: it's mostly the same problems you have with any python extensions implemented in C, getting the build-time and runtime dependencies right. It's especially a pain when you're using virtualenv/buildout. – Mu Mind May 05 '11 at 18:46