186

If I open an image with open("image.jpg"), how can I get the RGB values of a pixel assuming I have the coordinates of the pixel?

Then, how can I do the reverse of this? Starting with a blank graphic, 'write' a pixel with a certain RGB value?

I would prefer if I didn't have to download any additional libraries.

Ver Nick
  • 253
  • 8
  • 19
Josh Hunt
  • 14,225
  • 26
  • 79
  • 98

13 Answers13

249

It's probably best to use the Python Image Library to do this which I'm afraid is a separate download.

The easiest way to do what you want is via the load() method on the Image object which returns a pixel access object which you can manipulate like an array:

from PIL import Image

im = Image.open('dead_parrot.jpg') # Can be many different formats.
pix = im.load()
print im.size  # Get the width and hight of the image for iterating over
print pix[x,y]  # Get the RGBA Value of the a pixel of an image
pix[x,y] = value  # Set the RGBA Value of the image (tuple)
im.save('alive_parrot.png')  # Save the modified pixels as .png

Alternatively, look at ImageDraw which gives a much richer API for creating images.

Matthew Smith
  • 508
  • 7
  • 22
David Webb
  • 190,537
  • 57
  • 313
  • 299
  • 1
    Fortunately installing PIL is very straightforward in Linux and Windows (don't know about Mac) – heltonbiker Sep 28 '11 at 16:20
  • 7
    @ArturSapek, I installed PIL by `pip` which was fairly easy. – michaelliu Apr 06 '13 at 01:21
  • 1
    I used this on my Mac (Pypi): `easy_install --find-links http://www.pythonware.com/products/pil/ Imaging` – Mazyod Dec 18 '13 at 11:51
  • 27
    For future readers: `pip install pillow` will install PIL successfully and fairly quickly (may need `sudo` if not in a virtualenv). – Christopher Shroba Aug 30 '15 at 01:59
  • https://pillow.readthedocs.io/en/latest/installation.html#windows-installation shows bash commands in windows installation steps. No really sure how to proceed. – Musixauce3000 May 16 '16 at 15:11
  • It this time, PIL does not support python 3.x. So the pillow is the best choise because has the same synthax. Install it using `pip install pillow` – SuB May 12 '18 at 08:09
  • Does it work for .png format with transparent background? I just loaded one and the output pixel values look incorrect. https://imgur.com/a/6DwRa1h – June Wang Dec 01 '21 at 03:21
59

Using Pillow (which works with Python 3.X as well as Python 2.7+), you can do the following:

from PIL import Image
im = Image.open('image.jpg', 'r')
width, height = im.size
pixel_values = list(im.getdata())

Now you have all pixel values. If it is RGB or another mode can be read by im.mode. Then you can get pixel (x, y) by:

pixel_values[width*y+x]

Alternatively, you can use Numpy and reshape the array:

>>> pixel_values = numpy.array(pixel_values).reshape((width, height, 3))
>>> x, y = 0, 1
>>> pixel_values[x][y]
[ 18  18  12]

A complete, simple to use solution is

# Third party modules
import numpy
from PIL import Image


def get_image(image_path):
    """Get a numpy array of an image so that one can access values[x][y]."""
    image = Image.open(image_path, "r")
    width, height = image.size
    pixel_values = list(image.getdata())
    if image.mode == "RGB":
        channels = 3
    elif image.mode == "L":
        channels = 1
    else:
        print("Unknown mode: %s" % image.mode)
        return None
    pixel_values = numpy.array(pixel_values).reshape((width, height, channels))
    return pixel_values


image = get_image("gradient.png")

print(image[0])
print(image.shape)

Smoke testing the code

You might be uncertain about the order of width / height / channel. For this reason I've created this gradient:

enter image description here

The image has a width of 100px and a height of 26px. It has a color gradient going from #ffaa00 (yellow) to #ffffff (white). The output is:

[[255 172   5]
 [255 172   5]
 [255 172   5]
 [255 171   5]
 [255 172   5]
 [255 172   5]
 [255 171   5]
 [255 171   5]
 [255 171   5]
 [255 172   5]
 [255 172   5]
 [255 171   5]
 [255 171   5]
 [255 172   5]
 [255 172   5]
 [255 172   5]
 [255 171   5]
 [255 172   5]
 [255 172   5]
 [255 171   5]
 [255 171   5]
 [255 172   4]
 [255 172   5]
 [255 171   5]
 [255 171   5]
 [255 172   5]]
(100, 26, 3)

Things to note:

  • The shape is (width, height, channels)
  • The image[0], hence the first row, has 26 triples of the same color
Martin Thoma
  • 124,992
  • 159
  • 614
  • 958
  • 1
    Pillow supports python 2.7 on macosx while I only find python 2.5 support on PIL. Thanks! – Kangaroo.H May 31 '17 at 03:30
  • 6
    Be careful, the 'reshape' params list should be (height, width, channels). and for rgba images you could include image.mode = RGBA with channels = 4 – gmarsi Jun 01 '20 at 08:18
  • Is the point by @gmarsi true on the width and height? Is it really the case that both are valid? You need to be aware of how the data is output so you know what shape the output array will have and where the row and column pixel data of the image will be. – Kioshiki Jul 26 '20 at 12:55
  • @Kioshiki I've added a "smoke testing" section in my answer so it's easier to tell. – Martin Thoma Jul 26 '20 at 13:43
  • 2
    Example is confusing. If it's row, shouldn't there be 100 data points? If 'row' actually means column of the pic, shouldn't its value change to `#fff`? – d9ngle Aug 29 '22 at 14:47
  • @d9ngle Yes, the problem lays in the reshape, it should be `pixel_values = numpy.array(pixel_values).reshape((height, width, channels))` – Chifrijo May 15 '23 at 17:18
24

PyPNG - lightweight PNG decoder/encoder

Although the question hints at JPG, I hope my answer will be useful to some people.

Here's how to read and write PNG pixels using PyPNG module:

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
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()

PyPNG is a single pure Python module less than 4000 lines long, including tests and comments.

PIL is a more comprehensive imaging library, but it's also significantly heavier.

martineau
  • 119,623
  • 25
  • 170
  • 301
Constantin
  • 27,478
  • 10
  • 60
  • 79
13

As Dave Webb said:

Here is my working code snippet printing the pixel colours from an image:

import os, sys
import Image

im = Image.open("image.jpg")
x = 3
y = 4

pix = im.load()
print pix[x,y]
Matthew Smith
  • 508
  • 7
  • 22
  • Why do I get four values, when running Lachlan Phillips's code? I give this: print(pix[10,200]) and I get this: (156, 158, 157, 255) Why? – just_learning Sep 21 '21 at 18:40
  • 1
    The reason for this is likely because your image supports alpha transparency and is in rgba format, meaning that the fourth value is how transparent that pixel is. – Nicholas Zolton Feb 07 '22 at 01:07
  • Why do I get this error??? ```File "/home/UbuntuUser/.local/lib/python3.8/site-packages/PIL/Image.py", line 3008, in open raise UnidentifiedImageError( PIL.UnidentifiedImageError: cannot identify image file 'test_images/test_UAV.tif')``` – just_learning Jul 05 '22 at 19:39
9
photo = Image.open('IN.jpg') #your image
photo = photo.convert('RGB')

width = photo.size[0] #define W and H
height = photo.size[1]

for y in range(0, height): #each pixel has coordinates
    row = ""
    for x in range(0, width):

        RGB = photo.getpixel((x,y))
        R,G,B = RGB  #now you can use the RGB value
Peter V
  • 2,478
  • 6
  • 36
  • 54
7

Using a library called Pillow, you can make this into a function, for ease of use later in your program, and if you have to use it multiple times. The function simply takes in the path of an image and the coordinates of the pixel you want to "grab." It opens the image, converts it to an RGB color space, and returns the R, G, and B of the requested pixel.

from PIL import Image
def rgb_of_pixel(img_path, x, y):
    im = Image.open(img_path).convert('RGB')
    r, g, b = im.getpixel((x, y))
    a = (r, g, b)
    return a

*Note: I was not the original author of this code; it was left without an explanation. As it is fairly easy to explain, I am simply providing said explanation, just in case someone down the line does not understand it.

KalamariKing
  • 146
  • 2
  • 9
Idan Rotbart
  • 93
  • 1
  • 5
  • 2
    While this code snippet may be the solution, [including an explanation](https://meta.stackexchange.com/questions/114762/explaining-entirely-%E2%80%8C%E2%80%8Bcode-based-answers) really helps to improve the quality of your post. Remember that you are answering the question for readers in the future, and those people might not know the reasons for your code suggestion. – Narendra Jadhav Jun 17 '18 at 07:40
3

Image manipulation is a complex topic, and it's best if you do use a library. I can recommend gdmodule which provides easy access to many different image formats from within Python.

Greg Hewgill
  • 951,095
  • 183
  • 1,149
  • 1,285
  • Anyone know why this was downvoted? Is there a known problem with libgd or something? (I had never looked at it, but it's always nice to know there's an alternative to PiL) – Peter Hanley Jun 11 '13 at 19:20
3

There's a really good article on wiki.wxpython.org entitled Working With Images. The article mentions the possiblity of using wxWidgets (wxImage), PIL or PythonMagick. Personally, I've used PIL and wxWidgets and both make image manipulation fairly easy.

Jon Cage
  • 36,366
  • 38
  • 137
  • 215
3

You could use the Tkinter module, which is the standard Python interface to the Tk GUI toolkit and you don't need extra download. See https://docs.python.org/2/library/tkinter.html.

(For Python 3, Tkinter is renamed to tkinter)

Here is how to set RGB values:

#from http://tkinter.unpythonic.net/wiki/PhotoImage
from Tkinter import *

root = Tk()

def pixel(image, pos, color):
    """Place pixel at pos=(x,y) on image, with color=(r,g,b)."""
    r,g,b = color
    x,y = pos
    image.put("#%02x%02x%02x" % (r,g,b), (y, x))

photo = PhotoImage(width=32, height=32)

pixel(photo, (16,16), (255,0,0))  # One lone pixel in the middle...

label = Label(root, image=photo)
label.grid()
root.mainloop()

And get RGB:

#from http://www.kosbie.net/cmu/spring-14/15-112/handouts/steganographyEncoder.py
def getRGB(image, x, y):
    value = image.get(x, y)
    return tuple(map(int, value.split(" ")))
chenlian
  • 690
  • 6
  • 7
  • Perfect, doesn't require any installation and works like a charm. You can also load an existing image with `PhotoImage(file="image.png")`. – mathieures Jan 09 '23 at 16:46
3

You can use pygame's surfarray module. This module has a 3d pixel array returning method called pixels3d(surface). I've shown usage below:

from pygame import surfarray, image, display
import pygame
import numpy #important to import

pygame.init()
image = image.load("myimagefile.jpg") #surface to render
resolution = (image.get_width(),image.get_height())
screen = display.set_mode(resolution) #create space for display
screen.blit(image, (0,0)) #superpose image on screen
display.flip()
surfarray.use_arraytype("numpy") #important!
screenpix = surfarray.pixels3d(image) #pixels in 3d array:
#[x][y][rgb]
for y in range(resolution[1]):
    for x in range(resolution[0]):
        for color in range(3):
            screenpix[x][y][color] += 128
            #reverting colors
screen.blit(surfarray.make_surface(screenpix), (0,0)) #superpose on screen
display.flip() #update display
while 1:
    print finished

I hope been helpful. Last word: screen is locked for lifetime of screenpix.

2

install PIL using the command "sudo apt-get install python-imaging" and run the following program. It will print RGB values of the image. If the image is large redirect the output to a file using '>' later open the file to see RGB values

import PIL
import Image
FILENAME='fn.gif' #image can be in gif jpeg or png format 
im=Image.open(FILENAME).convert('RGB')
pix=im.load()
w=im.size[0]
h=im.size[1]
for i in range(w):
  for j in range(h):
    print pix[i,j]
1
import matplotlib.pyplot as plt
import matplotlib.image as mpimg

img=mpimg.imread('Cricket_ACT_official_logo.png')
imgplot = plt.imshow(img)
Rob
  • 26,989
  • 16
  • 82
  • 98
1

If you are looking to have three digits in the form of an RGB colour code, the following code should do just that.

i = Image.open(path)
pixels = i.load() # this is not a list, nor is it list()'able
width, height = i.size

all_pixels = []
for x in range(width):
    for y in range(height):
        cpixel = pixels[x, y]
        all_pixels.append(cpixel)

This may work for you.

Matthew Smith
  • 508
  • 7
  • 22
  • So, If instead of image path, I have a list with the pixels of an area extracted (and not stored as an image) from an image how can I read the pixel values? – just_learning Feb 15 '22 at 12:08