Search code examples
pythonimage-processingbitmapwxwidgetsctypes

Image bit manipulation in Python


I have an application that receives a pointer to JPEG data from a camera API wrapped with ctypes, converts it to a wx.Image, and displays the images as a movie.

One of required features is to set two of the components of a pixel equal to the third. E.g, my pixel in RGB format is (100,200,255), I want to set the the R and B values equal to the G, or (200,200,200). I need to do t his for every pixel in the image whilst maintaining a decent framerate.

I can access the RGB values from my wx.Image by calling the Image.GetData, which returns a string containing the pixel values in the following format: RGBRGBRGB ... I have implemented the feature naively by iterating through this RGBRGBRGB string.

However, this naive approach is far too slow to achieve a decent FPS because (I think):

a) I am iterating through every pixel in the image.

b) I am doing too much data copying.

I have considered converting my RGB data to numpy, performing the operation (I assume numpy would have a faster way of doing this sort of thing), and then converting back to wx.Image. Unfortunately I cannot convert straight from the raw data to numpy as the data comes as a JPEG, not in as a RGB bitmap. So I would need to go from data->wx.Image->numpy array->wx.Image.

I have also considered implementing my own python buffer which will return, for example, the G pixel value instead of the R and B values when being read. I think this would be the ideal solution, as it requires no data copying or excessive iterations, but I have no idea how to go about doing this. Will I need to write this buffer in C? Is it possible to implement buffers in pure python and still manipulate raw memory?

So SO, how do you think I should go about improving my performance? Should I attempt the numpy or buffer solution, or is there an easier solution that I am missing?

I am mainly looking for ideas/links to relevant documentation or examples, but if someones wants to write some code then that's fine :)

Thanks


Solution

  • You could try using the Python Imaging Library (PIL) - this is a library for manipulating images.

    You can find information about converting between a wxPython image and a PIL image here, or you can load the jpeg directly into a PIL image.

    Once you have converted your wx image into a PIL image I think this will do what you want (but I have not tested it):

    r, g, b = im.split()              # split the image into separate color planes
    im = Image.merge("RGB", (g, g, g))  # merge them back, using the green plane for each
    

    Then convert it back to a wxPython image.

    This should be orders of magnitude faster than doing it in Python, since PIL is implemented in C and optimised for image processing.