1

I have an RGB image and am trying to set every pixel on my RGB to black where the corresponding alpha pixel is black as well. So basically I am trying to "bake" the alpha into my RGB. I have tried this using PIL pixel access objects, PIL ImageMath.eval and numpy arrays:

PIL pixel access objects:

def alphaCutoutPerPixel(im):
    pixels = im.load()
    for x in range(im.size[0]):
        for y in range(im.size[1]):
            px = pixels[x, y]
            r,g,b,a = px
            if px[3] == 0:  # If alpha is black...
                pixels[x,y] = (0,0,0,0)
    return im

PIL ImageMath.eval:

def alphaCutoutPerBand(im):
    newBands = []
    r, g, b, a = im.split()
    for band in (r, g, b):
        out = ImageMath.eval("convert(min(band, alpha), 'L')", band=band, alpha=a)
        newBands.append(out)
    newImg = Image.merge("RGB", newBands)
    return newImg

Numpy array:

def alphaCutoutNumpy(im):
    data = numpy.array(im)
    r, g, b, a = data.T
    blackAlphaAreas = (a == 0)
    # This fails; why?
    data[..., :-1][blackAlphaAreas] = (0, 255, 0)
    return Image.fromarray(data)

The first method works fine, but is really slow. The second method works fine for a single image, but will stop after the first when asked to convert multiple. The third method I created based on this example (first answer): Python: PIL replace a single RGBA color But it fails at the marked command:

data[..., :-1][blackAlphaAreas] = (0, 255, 0, 0)
IndexError: index (295) out of range (0<=index<294) in dimension 0

Numpy seems promising for this kind of stuff, but I dont really get the syntax on how to set parts of the array in one step. Any help? Maybe other ideas to achieve what I describe above quickly?

Cheers

Community
  • 1
  • 1
br0t
  • 37
  • 2
  • 7
  • The slice syntax is straightforward: `data[x1:x2,y1:y2,z1:z2,...]` is an n-D slice where data is your n-D matrix, x, y, z, ... are your dimensions (comma-separated) and 1 is the starting index and 2 is the ending index (with a colon in-between). No index implies an infinite limit (i.e. no lower limit means from the beginning and no upper limit means up to the end). – CrystalDuck Jul 12 '13 at 13:38
  • No problem. Also negative indexing starts at the end. If you index `data[:-1]` you will access a view from the first to the second-to-last element. If you index `data[::-1]` you will access a flipped array (the -1 after the second colon flips the view, which also means that you need to index is as such: `data[max:min:-1]`). Try playing around with small arrays in the interpreter. – CrystalDuck Jul 12 '13 at 14:23

1 Answers1

2

This doesn't use advanced indexing but is easier to read, imho:

def alphaCutoutNumpy(im):
    data = numpy.array(im)
    data_T = data.T
    r, g, b, a = data_T
    blackAlphaAreas = (a == 0)
    data_T[0][blackAlphaAreas] = 0
    data_T[1][blackAlphaAreas] = 0
    data_T[2][blackAlphaAreas] = 0
    #data_T[3][blackAlphaAreas] = 255
    return Image.fromarray(data[:,:,:3])
Robert Franke
  • 2,224
  • 2
  • 17
  • 10
  • Awesome Robert, thanks! Working great and about 30-40% faster than the first PIL method. I had hoped for an even better speed gain, but thats fine. Just to note: I think my IndexError stems from "data" being ordered differently than "blackAreaAlphas", the one being (rows x columns) and the other (columns x rows). – br0t Jul 12 '13 at 14:10