Search code examples
pythonpython-imaging-librarypng

Mapping values of pixels in an image with Python


I am working with .png files, having pixels values 1 and 255.
I have to map pixel values 255 to 0 and, in other cases, pixels with values 1 to 2.
I tried:

for img_name in good_imgs:
  img = Image.open(img_name)
  pixels = img.load() 
  for i in range(img.size[0]):
    for j in range(img.size[1]):
        if pixels[i,j] == 255:
          pixels[i,j] = 0
  img.save(img_name)

for img_name in bad_imgs:
  img = Image.open(img_name)
  pixels = img.load() 
  for i in range(img.size[0]):
    for j in range(img.size[1]):
        if pixels[i,j] == 255:
          pixels[i,j] = 0
        elif pixels[i,j] == 1:
          pixels[i,j] == 2
  img.save(img_name)

but the images saved in this way, have the same pixels values as the originals.
What is wrong with the above code? How can I change pixels values in this kind of images?

Updtate:
I have noticed that if I modified the pixel of a single image, the pixels are mapped correctly, i.e. now I suppose the issue is about saving images with img.save()


Solution

  • As I mentioned in the comments, for loops are slow, inefficient and error-prone for processing images in Python.

    Here's a more efficient way of doing what you ask. Your colours of 0, 1, 2 and 255 are hard to see on StackOverflow's background and hard to differentiate from each other because 3 of them are essentially black, so I am transforming 64 and 150 into 192 and 32 but you can adapt to your own numbers:

    enter image description here

    from PIL import Image
    
    # Load image and ensure greyscale, i.e. 'L'
    im = Image.open('image.png').convert('L')
    
    # Remap colours 150 -> 32, 64 -> 192
    res = im.point((lambda p: 32 if p==150 else (192 if p==64 else 0)))
    
    res.save('result.png')
    

    enter image description here


    If you want to use Numpy instead, which is also vectorised and optimised, it might look like this (other techniques are also possible):

    from PIL import Image
    import numpy as np
    
    # Load image and ensure greyscale, i.e. 'L'
    im = Image.open('image.png').convert('L')
    
    # Make into Numpy array
    na = np.array(im)
    
    # Anywhere pixels are 150, make them 32
    na[na==150] = 32
    
    # Anywhere pixels are 64, make them 192
    na[na==64] = 192
    
    # Convert back to PIL Image and save
    Image.fromarray(na).save('result.png')