Search code examples
pythonraspbianpypng

Changing png pixels selectively with PyPNG on Raspbian


I am trying to go through each pixel of a png that I captured with my Raspberry pi's camera, and change selectively change pixels that are above or below a certain r, g, or b value. I know it is a pretty inefficient algorithm, I am just trying to get a hang of python scripting. I based my code off of @Constantin's code in from the question: How can I read the RGB value of a given pixel in Python? His code is below.

import png, array

point = (2, 10) # coordinates of pixel to be painted red

reader = png.Reader(filename='image.png')
w, h, pixels, metadata = reader.read_flat()
pixel_byte_width = 4 if metadata['alpha'] else 3
#The line below is, I think wrong. I'll point out the what I did in my code below
pixel_position = point[0] + point[1] * w 
new_pixel_value = (255, 0, 0, 0) if metadata['alpha'] else (255, 0, 0)
pixels[
  pixel_position * pixel_byte_width :
  (pixel_position + 1) * pixel_byte_width] = array.array('B', new_pixel_value)

output = open('image-with-red-dot.png', 'wb')
writer = png.Writer(w, h, **metadata)
writer.write_array(output, pixels)
output.close()

And I changed it to this:

#!/usr/bin/python

import png, array

reader = png.Reader(filename='test.png')
w, h, pixels, metadata = reader.read_flat()
pixel_byte_width = 4 if metadata['alpha'] else 3

for x in range(w):
    for y in range(h):
        point_index = x+(y-1)*w#This is the bit that I said I'd fix above.
        r = pixels[point_index * pixel_byte_width + 0]
        g = pixels[point_index * pixel_byte_width + 1]
        b = pixels[point_index * pixel_byte_width + 2]
        pixel = pixels[point_index * pixel_byte_width : 
            (point_index+1) * pixel_byte_width]
        #Above we have all the info about each byte, and below is our devious plan
        new_pixel = (0, 0, 0, 0) if metadata['alpha'] else (0, 0, 0)
        #if g > 175:
        pixel = array.array('B', new_pixel)

output = open('test_edited.png', 'wb')
writer = png.Writer(w, h, **metadata)
writer.write_array(output, pixels)
output.close()

What happens is the pi thinks for a minute or two, and then I can open up a new png that is exactly the same. what am I missing with my script, or is there a better platform than python to do pixel by pixel stuff on Raspbian Jessie?

Many thanks!


Solution

  • If you are open to new libraries, I suggest pillow, the successor of the Python Imaging Library (PIL). It is much simpler and not limited to png. Documentation is here.

    from PIL import Image, ImageDraw
    
    img = Image.open('image.png')
    img = img.convert("RGB") # Make sure we are in 8-bit RGB
    draw = ImageDraw.Draw(img)
    
    for y in range(img.height):
        for x in range(img.width):
            # getpixel returns a tuple with (R, G, B)
            if img.getpixel((x, y))[1] > 175: # If too green
                draw.point((x,y), '#000000') # Color syntax is CSS-like
    
    img.save('test_edited.png', 'PNG')
    # You can also use img.show() in a graphical environment