En este post veremos como funcionan los píxeles, matrices e imágenes con python y openCV.
Escribo este post porque inicialmente quería dibujar formas y texto en openCV, una de las maneras de hacerlo es dibujar y escribir sobre una imagen ¡realmente fue sencillo!, pero luego surguió la interrogante, y si no quiero usar una imagen como tal (es decir leerla con imread
) y quiero generar yo misma el fondo (crear una imagen negra, blanca, gris, etc) para escribir y dibujar formas. En esa busqueda enconté esta línea de codigo1
img = np.zeros((100,100,3),np.uint8)
Esta línea de código es usada para crear un área de 100x100
que representará una imagen !ok, yo estaba de acuerdo!, pero qué significa el 3
y dtype=np.uint8
en esa expesión?
- El
3
significa los canales que tiene la imagen en este caso sonB G R
. dtype=np.uint8
es el tipo de dato de cada píxel.
Bueno, para profundizar voy a empezar detallando como se representan las imágenes digitales.
Imagen digital, matrices e imágenes
Una computadora ve las imágenes como cuadrículas con números, cada una de estas cadriculas se llama píxel, una imagen digital esta representada por la combinación de píxeles formando filas y columnas.
¿Cuál es la diferencia entre una imagen en escala de grises y una imagen BGR?
La diferencia es la cantidad de canales que representan a la imagen.
- En la imagen en escala de grises cada cuadrícula (píxel) tiene
1
número entre 0 a 255 y representa un canal. - En la imagen BGR cada cuadrícula (píxel) tiene
3
números entre 0 a 255 y representan tres canalesB
,G
,R
.
Ahora, considerando que una imagen es una matriz de valores numerícos y que la combinación de los 3
canales BGR
hacen que una imagen tome color, empieza a tener sentido la línea de código que mencioné al inicio
img = np.zeros((500,500,3),np.uint8)
¿Cómo funciona np.ones y np.zeros para crear una imagen?
ones y zeros son funciones de la biblioteca numpy para python, crean matrices llenas de unos 1
o ceros 0
respectivamente. Para crear una imagen en openCV los parámetros que recibe son: las dimensiones de la imagen shape
y el tipo de dato de cada píxel dtye
numpy.ones(shape, dtype)
numpy.zeros(shape, dtype)
shape
puede ser un entero o una tupla de enterosdtpe
establece el tipo de dato del arreglo que se va a crear. Más adelante registro como afecta el tipo de dato a la construcción de una imagen
Crear una imagen en escala de grises
Las imágenes anteriores se representaron en forma de una matriz, y para hacer esto en python y openCV uso zeros
y ones
porque son funciones que crean arreglos.
El siguiente código genera dos imágenes de color negro como el color de fondo, y los valores de los píxeles son 0
y 1
, para notar cambios en los colores los valores deben variar entre 0
a 255
- Importar bibliotecas numpy, cv2
- Crear la matriz de
3
dimensionesnp.zeros((6,8,1)
imagenalto = 6
,ancho = 8
y en cada posición estará1
valor. Toda la matriz esta llena deceros
.np.ones((6,8,1)
imagenalto = 6
,ancho = 8
y en cada posición estará1
valor. Toda la matriz esta llena deceros
.200*np.ones((6,8,1)
imagenalto = 6
,ancho = 8
y en cada posición estará1
valor. Toda la matriz esta llena de200
.
#Importar librería cv2
import cv2
import numpy as np
#Crea arreglos que son la base de una imagen
img1 = np.zeros((6,8,1),np.uint8)
img2 = np.ones((6,8,1),np.uint8)
img3 = 200*np.ones((6,8,1),np.uint8)
#Mostrar imagenes
cv2.imshow('imagen-zeros',img1)
cv2.imshow('imagen-ones',img2)
cv2.imshow('imagen-250',img2)
#Cerrar las ventanas con cualquier tecla
cv2.waitKey(0)
cv2.destroyAllWindows()
Cuando hacemos zoom, podemos ver el valor de los píxeles
Nota: El resultado de la visualización de las imágenes del código anterior no muestra los valores de cada píxel, los valores se muestran por razones didácticas al hacer zoom en la imagen
Modificar el valor de píxeles
El color de un píxel será negro mientras sea cercano a 0
y tomará tonalidades de color blanco mientras mas sea cercano a 255
, el siguiente código muestra como se agrega valores a píxeles específicos (esta hecho con zeros
, pero tambien funciona con ones
)
#Importar librería cv2 y numpy
import cv2
import numpy as np
#Arreglo de zeros, es la base de una imagen
img1 = np.zeros((6,8,1),np.uint8)
#colocar valores en píxeles especìficos
img1[1,1] = 50
img1[4,4] = 200
img1[3,5] = 75
img1[2,3] = 255
#Muestro la imagen
cv2.imshow('imgGris',img1)
cv2.waitKey(0)
cv2.destroyAllWindows()
Crear una imagen BRG
Una imagen BGR debe ser la representación numérica de una matriz, a diferencia de la escala de grises en cada píxel existirá tres valores entre 0
y 255
El siguiente código genera una imagen BGR
, creando el arreglo numérico de 3 dimensiones con las funciones zeros
y ones
.
- Importar bibliotecas numpy, cv2
- Crear la matriz de
3
dimensionesnp.zeros((6,8,3)
imagenalto = 6
,ancho = 8
y3
canalesB G R
. Toda la matriz de3
dimensiones esta llena deceros
, por lo tanto la imagen es de color negro.255*np.ones((6,8,3)
imagenalto = 6
,ancho = 8
y3
canalesB G R
. Toda la matriz de3
dimensiones esta llena de255
, por lo tanto la imagen será de color blanco.
#Importar librería cv2 y numpy
import cv2
import numpy as np
#Crear una imagen de color negro
img1 = np.zeros((6,8,3),np.uint8)
#Crear una imagen de color blanco
img2 = 255*np.ones((6,8,3),np.uint8)
#Establecer 4 píxeles colores en img fondo negro
img1[1, 1] = (50 , 50 , 50)
img1[4, 4] = (200 , 150 ,10)
img1[3, 5] = (75, 100, 255)
img1[2, 3] = (255, 50 , 100)
#Establecer 4 píxeles colores en img fondo blanco
img2[1, 1] = (0 , 255 , 255)
img2[4, 4] = (30 , 50 ,255)
img2[3, 5] = (255, 255, 50)
img2[2, 3] = (150, 250 , 50)
#Mostrar imagen
cv2.imshow('imagen1',img1)
cv2.imshow('imagen2',img2)
cv2.waitKey(0)
cv2.destroyAllWindows()
Tipo de dato en numpy.zeros y numpy.ones al crear una imagen
Cuando se crea la matriz para una imagen se espcifica el tipo de dato como uint8
, en python hay varios tipos de dato pero no todos funciona para crear imágenes.
El siguiente código muestra tres imágenes creadas con diferentes con diferentes tipos de datos.
- Importar librerías numpy, cv2
- Crear tres matrices de 3 dimensiones2 llenas de unos
1
uint8
tipo de dato de 8 bits (entero sin signo de 0 a 255), muestra la imagen como es realmente.uint16
tipo de dato es de 16 bits (enter sin signo de 0 a 65535) los píxeles se dividen por 256, es decir que su rango es desde[0, 255*256]
.float32
tipo de dato float de 32 bits, los píxeles se multiplican por256
- Especificar cuatro colores en las mismas posiciones, diferentes imágenes.
- img[1, 1] = (0 , 255 , 255)
- img[4, 4] = (30 , 50 ,255)
- img[3, 5] = (255, 255, 50)
- img[2, 3] = (150, 250 , 50)
- Mostrar las 4 imágenes para comparar
#Importar librería cv2 y numpy
import cv2
import numpy as np
#Crear una imagen de color blanco uinit8
img1 = 255*np.ones((6,8,3),np.uint8)
#Crear una imagen de color blanco uinit16
img2 = 255*np.ones((6,8,3),np.uint16)
#Crear una imagen de color blanco float32
img3 = 255*np.ones((6,8,3),np.float32)
#Establecer 4 píxeles colores en img1
img1[1, 1] = (0 , 255 , 255)
img1[4, 4] = (30 , 50 ,255)
img1[3, 5] = (255, 255, 50)
img1[2, 3] = (150, 250 , 50)
#Establecer 4 píxeles colores en img2
img2[1, 1] = (0 , 255 , 255)
img2[4, 4] = (30 , 50 ,255)
img2[3, 5] = (255, 255, 50)
img2[2, 3] = (150, 250 , 50)
#Establecer 4 píxeles colores en img3
img3[1, 1] = (0 , 255 , 255)
img3[4, 4] = (30 , 50 ,255)
img3[3, 5] = (255, 255, 50)
img3[2, 3] = (150, 250 , 50)
#Mostrar imagen
cv2.imshow('imagen-uint8',img1)
cv2.imshow('imagen-uint16',img2)
cv2.imshow('imagen-float32',img3)
cv2.waitKey(0)
cv2.destroyAllWindows()
La anterior imagen muestra como el tipo de dato hace que las imágenes se representen de diferente forma, aunque tenga definidos los mismos colores. Para que se muestren iguales se debe considerar el número de bits que maneja cada tipo de dato y el rango que reresentan 3 4.
Mostrar la misma imágen con distintos tipos de dato
uint8
muestra la imágen sin modificacionesuint16
debe ser multiplicado por256
float32
debe ser dividido para256
#Importar librería cv2 y numpy
import cv2
import numpy as np
#Crear una imagen de color blanco uinit8
img1 = 255*np.ones((6,8,3),np.uint8)
#Crear una imagen de color blanco uinit16
img2 = 255*np.ones((6,8,3),np.uint16)*256
#Crear una imagen de color blanco float32
img3 = 255*np.ones((6,8,3),np.float32)/256
#Establecer 4 píxeles colores en img fondo blanco
img1[1, 1] = (0 , 255 , 255)
img1[4, 4] = (30 , 50 ,255)
img1[3, 5] = (255, 255, 50)
img1[2, 3] = (150, 250 , 50)
img2[1, 1] = (0*256 , 255*256 , 255*256)
img2[4, 4] = (30*256 , 50*256 ,255*256)
img2[3, 5] = (255*256, 255*256, 50*256)
img2[2, 3] = (150*256, 250*256 , 50*256)
img3[1, 1] = (0/256 , 255/256 , 255/256)
img3[4, 4] = (30/256 , 50/256 ,255/256)
img3[3, 5] = (255/256, 255/256, 50/256)
img3[2, 3] = (150/256, 250/256 , 50/256)
#Mostrar imagen
cv2.imshow('imagen-uint8',img1)
cv2.imshow('imagen-uint16',img2)
cv2.imshow('imagen-float32',img3)
cv2.waitKey(0)
cv2.destroyAllWindows()
Referencias
- Joel. (28 de Febrero del 2019). [Publicado en {lwp}]. Crear un imagen con cada pixel de un color https://www.lawebdelprogramador.com/codigo/Python/5175-Crear-un-imagen-con-cada-pixel-de-un-color.html
- Web Development Labs. (4 de septiembre del 2020). [Publicación en un foro online]. 3-dimensional array in numpy .https://stackoverflow.com/questions/22981845/3-dimensional-array-in-numpy
- OpenCV. imshow https://docs.opencv.org/2.4/modules/highgui/doc/user_interface.html#imshow
- Univerio. (20 de Mayo del 2014). [Publicación en un foro online]. why datatype has to be ‘uint8’ in Opencv python wrapper. Mensaje publicado en https://stackoverflow.com/questions/23749968/why-datatype-has-to-be-uint8-in-opencv-python-wrapper
Pingback: Píxeles en openCV python - Kipuna Ec