Como ler o valor RGB de um determinado pixel em Python?

140

Se eu abrir uma imagem com open("image.jpg"), como posso obter os valores RGB de um pixel assumindo que tenho as coordenadas do pixel?

Então, como posso fazer o inverso disso? Começando com um gráfico em branco, 'escreva' um pixel com um determinado valor RGB?

Eu preferiria se não tivesse que baixar nenhuma biblioteca adicional.

Josh Hunt
fonte

Respostas:

213

Provavelmente, é melhor usar a Biblioteca de Imagens Python para fazer isso, o que, receio, é um download separado.

A maneira mais fácil de fazer o que você quer é através do método load () no objeto Image, que retorna um objeto de acesso a pixels que você pode manipular como uma matriz:

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

Como alternativa, observe o ImageDraw, que fornece uma API muito mais rica para a criação de imagens.

Dave Webb
fonte
1
Felizmente instalar PIL é muito simples em Linux e Windows (não sei sobre Mac)
heltonbiker
6
@ArturSapek, instalei o PIL, o pipque foi bastante fácil.
amigos estão dizendo sobre michaelliu
1
Eu usei isso no meu Mac (Pypi):easy_install --find-links http://www.pythonware.com/products/pil/ Imaging
Mazyod
15
Para futuros leitores: pip install pillowinstalará o PIL com sucesso e com bastante rapidez (pode ser necessário, sudose não em um virtualenv).
Christopher Shroba 30/08/2015
pillow.readthedocs.io/en/latest/… mostra comandos bash nas etapas de instalação do Windows. Não tenho muita certeza de como proceder.
Musixuce3000 #
31

Usando o Pillow (que funciona com o Python 3.X e o Python 2.7+), você pode fazer o seguinte:

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

Agora você tem todos os valores de pixel. Se é RGB ou outro modo pode ser lido por im.mode. Então você pode obter pixels (x, y)por:

pixel_values[width*y+x]

Como alternativa, você pode usar Numpy e remodelar a matriz:

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

Uma solução completa e simples de usar é

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

Fumaça testando o código

Você pode estar incerto sobre a ordem da largura / altura / canal. Por esse motivo, criei este gradiente:

insira a descrição da imagem aqui

A imagem tem uma largura de 100px e uma altura de 26px. Tem um gradiente de cor que vai de #ffaa00(amarelo) a #ffffff(branco). A saída é:

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

Coisas a serem observadas:

  • A forma é (largura, altura, canais)
  • A image[0], portanto, a primeira linha, tem 26 triplos da mesma cor
Martin Thoma
fonte
Pillow suporta python 2.7 no macosx, enquanto eu só encontro o suporte python 2.5 no PIL. Obrigado!
Kangaroo.H
2
Cuidado, a lista de parâmetros 'remodelar' deve ser (altura, largura, canais). e para imagens rgba você pode incluir image.mode = RGBA com canais = 4
gmarsi
O ponto de @gmarsi é verdadeiro na largura e altura? É realmente o caso de ambos serem válidos? Você precisa estar ciente de como os dados são gerados para saber qual será o formato da matriz de saída e onde serão os dados de pixel de linha e coluna da imagem.
Kioshiki
@ Kioshiki Adicionei uma seção de "testes de fumaça" na minha resposta para facilitar a identificação.
Martin Thoma
24

PyPNG - decodificador / codificador PNG leve

Embora a pergunta indique o JPG, espero que minha resposta seja útil para algumas pessoas.

Veja como ler e escrever pixels PNG usando o módulo PyPNG :

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 é um único módulo Python puro com menos de 4000 linhas, incluindo testes e comentários.

O PIL é uma biblioteca de imagens mais abrangente, mas também é significativamente mais pesada.

Constantin
fonte
12

Como Dave Webb disse:

Aqui está meu trecho de código de trabalho que imprime as cores de pixel de uma imagem:

import os, sys
import Image

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

pix = im.load()
print pix[x,y]
Lachlan Phillips
fonte
6
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
fonte
3

Manipulação de imagens é um tema complexo, e é melhor se você não usar uma biblioteca. Posso recomendar o gdmodule, que fornece acesso fácil a muitos formatos de imagem diferentes no Python.

Greg Hewgill
fonte
Alguém sabe por que isso foi rebaixado? Existe um problema conhecido com libgd ou algo assim? (Eu nunca tinha olhado para ele, mas é sempre bom saber que há uma alternativa para PIL)
Peter Hanley
3

Há um artigo realmente bom no wiki.wxpython.org intitulado Trabalhando com Imagens . O artigo menciona a possibilidade de usar wxWidgets (wxImage), PIL ou PythonMagick. Pessoalmente, usei PIL e wxWidgets e ambos facilitam bastante a manipulação de imagens.

Jon Cage
fonte
3

Você pode usar o módulo surfarray do pygame . Este módulo possui um método de retorno de matriz de pixels 3d chamado pixels3d (superfície). Eu mostrei o uso abaixo:

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

Espero ter sido útil. Última palavra: a tela está bloqueada por toda a vida do screenpix.

Ozgur Sonmez
fonte
2

instale o PIL usando o comando "sudo apt-get install python-imaging" e execute o seguinte programa. Irá imprimir valores RGB da imagem. Se a imagem for grande, redirecione a saída para um arquivo usando '>' depois abra o arquivo para ver os valores RGB

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]
user3423024
fonte
2

Você pode usar o módulo Tkinter, que é a interface padrão do Python para o kit de ferramentas Tk GUI e não precisa de download extra. Consulte https://docs.python.org/2/library/tkinter.html .

(Para Python 3, Tkinter é renomeado para tkinter)

Aqui está como definir valores RGB:

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

E obtenha 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
fonte
2
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
Idan Rotbart
fonte
1
Embora esse snippet de código possa ser a solução, incluir uma explicação realmente ajuda a melhorar a qualidade da sua postagem. Lembre-se de que você está respondendo à pergunta dos leitores no futuro e essas pessoas podem não saber os motivos da sua sugestão de código.
Narendra Jadhav
1
import matplotlib.pyplot as plt
import matplotlib.image as mpimg

img=mpimg.imread('Cricket_ACT_official_logo.png')
imgplot = plt.imshow(img)
user8374199
fonte
1

Se você deseja ter três dígitos na forma de um código de cores RGB, o código a seguir deve fazer exatamente isso.

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)

Isso pode funcionar para você.

Anupam Hayat Shawon
fonte