2

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.

Ventior
  • 207
  • 1
  • 10
  • What happens when it doesn't work? Does it crash? Do nothing? Generate an error? Do you have some prior knowledge that all images you process will be RGBA like you assume? What about palette images? https://stackoverflow.com/a/52307690/2836621 Do you have a sample image to share that does work and one that doesn't? – Mark Setchell Feb 10 '21 at 01:23
  • No crash whatsoever. The program runs smoothly, but is oddly fast. In cases in which it works it takes much longer. Here a [working picture](https://ibb.co/Kmj4rKJ) here one that [doesn't work](https://ibb.co/TgvbMxr). – Ventior Feb 10 '21 at 01:34

1 Answers1

1

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)
Mark Setchell
  • 191,897
  • 31
  • 273
  • 432