SBECK1
SBECK1

Reputation: 267

Discord.py Image Editing with Python Imaging Library only works for some pictures?

I've tried an image-editing-effect which should recolor a picture with little black dots, however it only works for certain images and I honestly don't know why. Any ideas?

#url = member.avatar_url
    #print(url)
    #response = requests.get(url=url, stream=True).raw
    #imag = Image.open(response)
    imag = Image.open("unknown.png")
    #out = Image.new('I', imag.size)
    i = 0
    width, height = imag.size

    for x in range(width):
        i+=1
        for y in range(height):
            if i ==5:
                # changes every 5th pixel to a certain brightness value
                r,g,b,a = imag.getpixel((x,y))
                print(imag.getpixel((x,y)))
                brightness = int(sum([r,g,b])/3)
                print(brightness)
                imag.putpixel((x, y), (brightness,brightness,brightness,255))
                i= 0
            else:
                i += 1
                imag.putpixel((x,y),(255,255,255,255))
    imag.save("test.png")

The comments are what I would've used if my tests had worked. Using local pngs also don't work all the time.

Upvotes: 2

Views: 322

Answers (1)

Mark Setchell
Mark Setchell

Reputation: 207853

Your image that doesn't work doesn't have an alpha channel but your code assumes it does. Try forcing in an alpha channel on opening like this:

imag = Image.open("unknown.png").convert('RGBA')

See also What's the difference between a "P" and "L" mode image in PIL?


A couple of other ideas too:

  • looping over images with Python for loops is slow and inefficient - in general, try to find a vectorised Numpy alternative

  • you have an alpha channel but set it to 255 (i.e. opaque) everywhere, so in reality, you may as well not have it and save roughly 1/4 of the file size

  • your output image is RGB with all 3 components set identically - that is really a greyscale image, so you could create it as such and your output file will be 1/3 the size

So, here is an alternative rendition:

#!/usr/bin/env python3

from PIL import Image
import numpy as np

# Load image and ensure neither palette nor alpha
im = Image.open('paddington.png').convert('RGB')

# Make into Numpy array
na = np.array(im)

# Calculate greyscale image as mean of R, G and B channels
grey = np.mean(na, axis=-1).astype(np.uint8)

# Make white output image
out = np.full(grey.shape, 255, dtype=np.uint8)

# Copy across selected pixels
out[1::6, 1::4] = grey[1::6, 1::4]
out[3::6, 0::4] = grey[3::6, 0::4]
out[5::6, 2::4] = grey[5::6, 2::4]

# Revert to PIL Image
Image.fromarray(out).save('result.png')

That transforms this:

enter image description here

into this:

enter image description here


If you accept calculating the greyscale with the normal method, rather than averaging R, G and B, you could change to this:

im = Image.open('paddington.png').convert('L')

and remove the line that does the averaging:

grey = np.mean(na, axis=-1).astype(np.uint8)

Upvotes: 1

Related Questions