1

I am working on a project that implements Numba and CUDA with Python 3.8. Currently, I create an array with the dimensions of the final image. Next, I generate an image with a CUDA kernel (incredibly fast). Then, I copy the pixel color into a Pillow Image (incredibly slow). My code:

for x in range(width):
    for y in range(height):
        if pixels[x][y] = 0:
            color = [0, 0, 0]
        else:
            # Get color from int as tuple
            color = rgb_conv(pixels[x][y]
        
        red = int(numpy.floor(color[0]))
        if red > 255:
            red = 255
        elif red < 0:
            red = 0

        green = int(numpy.floor(color[1]))
        if green > 255:
            green = 255
        elif green < 0:
            green = 0

        blue = int(numpy.floor(color[2]))
        if blue > 255:
            blue = 255
        elif blue < 0:
            blue = 0

        image.putpixel((x, y), (red, green, blue))

Are there any more efficient Python image libraries for this implementation? Is there a way to convert the array to an image on the GPU? Any help with direction works. Thanks!

EDIT 1: A request was made for the function rgb_conv. This is a function I found to convert a single integer into a three-wide color.

def rgb_conv(i):
    color = 255 * numpy.array(colorsys.hsv_to_rgb(i / 255.0, 1.0, 0.5))
    return tuple(color.astype(int))

However, I didn't particularly like the colors this function produces, so I removed it and began working with the following:

pixelArr = image.load()
for x in range(width):
    for y in range(height):
        color = int(numpy.floor(pixels[x][y]))
        pixelArr[x, y] = (color << 21) + (color << 10) + color * 8

This adjustment doesn't do much to the running time of the code. I am looking further into a suggestion load the image from an array rather than putting each pixel into the image.

Spencer Cain
  • 63
  • 1
  • 10
  • Possible duplicate of https://stackoverflow.com/questions/434583/what-is-the-fastest-way-to-draw-an-image-from-discrete-pixel-values-in-python – Kraigolas Feb 18 '21 at 00:53
  • There's a lot of potential optimization here. Just what can be done will be a function of what can come back from `rgb_conv`. That's your own function, right? If so, I'd be looking to optimize what that function is doing to hopefully not need all of the post-processing you show here. If that's not your function, then providing a clear definition of what the resulting "color" can contain would be very helpful. If that IS your function, can you show it to us? – CryptoFool Feb 18 '21 at 02:14
  • If the image is on the GPU originally, then it would make great sense to do the conversion there. If you to copy it up to the GPU to do the conversion, then I'm not sure it would save you anything to do it on the GPU. - again, what exactly is the source image format? – CryptoFool Feb 18 '21 at 02:16
  • 1
    You shouldn't be placing individual pixels with PIL. Images have a `frombytes` and `frombuffer` methods that will be _much_ faster. – g.d.d.c Feb 18 '21 at 04:49
  • Glad you got it working, and welcome to Stack Overflow! Your "Edit 2" information would be better placed in your answer below, rather than here in the question. – CrazyChucky Feb 20 '21 at 16:13
  • @CrazyChucky thanks! i fixed the answer to include the info you suggested too – Spencer Cain Feb 20 '21 at 17:04

1 Answers1

1

It is not efficient to place each pixel into an image with pillow. Creating an image from a numpy array is significantly faster than before. By faster, I mean the 3840x2160 image took minutes, but now takes 0.0117 seconds.

To create an array that can be converted to an image:

import numpy
pixels = numpy.zeros((height, width, 3), dtype=numpy.uint8)

Here, I run calculations on the GPU to create an image with the pixels array. To efficiently convert an array of pixels into an image:

from PIL import Image
image = Image.fromarray(pixels)
Spencer Cain
  • 63
  • 1
  • 10