Introducción al procesamiento de imágenes digitales
Contenido
%matplotlib inline
import numpy as np
from matplotlib import rcParams
rcParams['figure.dpi'] = 120
import matplotlib.pyplot as plt
from scipy import fftpack
from IPython.display import YouTubeVideo
from functools import partial
YTVideo_formato = partial(YouTubeVideo, width=640, height=400, rel=0, modestbranding=1)
def color2bw(img):
return np.dot(img, [0.299, 0.587, 0.114])
4. Introducción al procesamiento de imágenes digitales¶
En esta lección veremos:
Señales bidimensionales
Definición de una imagen digital
Percepción y modelos de color
4.1. Una imagen como caso particular de una señal 2D¶
El siguiente es un ejemplo de una señal que tiene dos variables independientes
YTVideo_formato('AEJgW2jUdMs')
Podemos
Interpretar las variables independientes \(x\) e \(y\) como coordenadas en el espacio
Visualizar la variable dependiente \(I(x,y)\) como un mapa de colores
Bajo estas consideraciones se obtiene lo que llamamos una imagen
A continuación definiremos formalmente una imagen
4.2. Definiendo una imagen digital¶
Una imagen digital es una señal con:
Dos variables indepedientes discretas \(x\) e \(y\) que representan el espacio
Una o más variables dependientes \(I(x,y)\) que representan la intensidad del color según una codificación
En la práctica las imágenes digitales son un arreglo de NxM componentes
\(N\), el número de filas, representa el alto de la imagen
\(M\), el número de columnas, representa el ancho de la imagen
Nota
Cada elemento del arreglo se llama pixel (picture element)
Los píxeles pueden ser
unidimensionales (imagen en blanco y negro)
multidimensionales (RGB, HSV, HSL)
Un pixel suele representarse como una tupla donde cada componente
es un entero sin signo de 8bits: Intensidades en el rango \([0, 255]\)
es un valor flotante en el rango \([0.0, 1.0]\)
A estos rangos les otorgamos una interpretación como mapa de color
En la siguiente figura podemos ver una imagen digital en escala de grises
A la izquierda: Representación en mapa de color, donde negro es 0.0 y blanco es 1.0
A la derecha: Representación numérica
Pregunta
¿Qué representa una imagen?
Comúnmente una imagen (señal) es una representación de la intensidad luminica (rango óptico)
Pero también se pueden representar otros rangos de radiación como los rayos ejes (radiografía) o la radiación infraroja (termografía) y otros fenómenos que no son electromagnéticos, por ejemplo un ultrasonido
*Todas las imágenes de esta lámina son de wikipedia
Pregunta
¿Cómo se obtiene una imagen digital?
Para el caso electromagnético se aprovecha el efecto fotoeléctrico: Cuando un fotón impacta un conductor se desprenden electrones
Un sensor de tipo Charged Coupled Device (CCD) usa el efecto fotoelétrico para contar la cantidad de fotones que golpean su superficie durante un cierto tiempo
El CCD está dividido en una grilla
Cada elemento de la grilla tiene su propia “cuenta”
La cuenta se mapea como un valor numérico entero o flotante
Si la cuenta supera el valor máximo del sensor se observa saturación
Los receptores pueden ajustarse para aceptar fotones de un cierto rango de frecuencias (color)
Nota
Los sensores CCD y CMOS son la base de las cámaras digitales modernas
4.3. Manipulación de imágenes con Python¶
Podemos usar Matplotlib
para importar y visualizar imágenes
plt.imread()
: Lee una imagen y retorna un arreglo deNumPy
plt.imshow()
: Dibuja un arreglo de NumPy como si fuera una imagen
img_color = plt.imread('../images/valdivia.jpg')
print("Tamaño: %s" %(repr(img_color.shape)))
print("Tipo: %s" %(img_color.dtype))
print("Intensidad del pixel en la posición 0, 0: %s" %(repr(img_color[0, 0, :])))
Tamaño: (350, 650, 3)
Tipo: uint8
Intensidad del pixel en la posición 0, 0: array([ 79, 112, 155], dtype=uint8)
plt.figure(figsize=(6, 3), tight_layout=True)
plt.imshow(img_color);
Una vez importada la imagen es un arreglo
Podemos manipularla y transformarla con las funciones de
NumPy
,Scipy
y otras librerías de Python
Ejemplo: slicing
de la imagen
plt.figure(figsize=(6, 3), tight_layout=True)
plt.imshow(img_color[100:500, 100:200]);
Ejemplo: Trasposición, inversión y espejado de la imagen
plt.figure(figsize=(6, 3), tight_layout=True)
plt.imshow(np.transpose(img_color, axes=[1, 0, 2]));
plt.figure(figsize=(6, 3), tight_layout=True)
plt.imshow(img_color[::-1, :, :]);
plt.figure(figsize=(6, 3), tight_layout=True)
plt.imshow(img_color[:, ::-1, :]);
4.4. Modelo de color RGB¶
La imagen que estabamos usando tiene tres canales
img_color.shape
(350, 650, 3)
Cada canal se interpreta como la intensidad de color
en rojo
en verde
en azul respectivamente
Esto corresponde al modelo de colores RGB (red, green, blue)
Combinando distintos valores de intensidad en rojo, verde y azul se obtienen los demás colores
Podemos ver el contenido de cada canal de color por separado como
fig, ax = plt.subplots(1, 4, figsize=(9.5, 3.2), tight_layout=True);
ax[0].imshow(img_color[:, 350:, :])
ax[0].axis('off')
for i, color_name in enumerate(["R", "G", "B"]):
ax[i+1].set_title(color_name)
ax[i+1].axis('off')
ax[i+1].imshow(img_color[:, 350:, i], cmap=plt.cm.Greys_r, vmin=0, vmax=255)
Una imagen a color se forma al combinar tres imágenes de escala de grises
4.5. Visión humana¶
El ojo humano tiene en su retina dos tipos de fotoreceptores: bastones y conos
Los bastones
120 millones aprox en la retina
Perciben intensidad pero no color
Requieren poco brillo para producir una señal
Tienen baja agudeza (menos sencibles a los detalles)
Los conos
6 millones aprox en la retina
Existen tres tipos de conos, cada uno sintonizado a una longitud de onda
Requieren mucho brillo para producir una señal
Tienen alta agudeza visual
Referencia: http://www.danielgmurphy.com/physics/1_intro/contents_phyics1.html
La respuesta retinal inspira la siguiente transformación de RGB a escala de grises:
def color2bw(img):
return np.dot(img, [0.299, 0.587, 0.114])
img_bw = color2bw(img_color)
plt.figure(figsize=(8, 4), tight_layout=True)
plt.imshow(img_bw, cmap=plt.cm.Greys_r);
4.6. Otros modelos de colores¶
El modelo HSV (Hue, Saturation, Value) es muy usado en software de diseño y computación gráfica
Hue: Corresponde al tono o color puro
Saturation: Corresponde a ajustar el brillo
Value: Corresponde a mezclar con negro para generar sombras
Por ejemplo, la imagen anterior en HSV
from matplotlib import colors
img_color_hsv = colors.rgb_to_hsv(img_color)
print("Shape: %s, Type: %s" %(repr(img_color_hsv.shape), img_color_hsv.dtype))
print(img_color_hsv[0, 0, :])
fig, ax = plt.subplots(1, 4, figsize=(9.5, 3.2), tight_layout=True);
ax[0].imshow(img_color[:, 350:, :])
ax[1].set_title('H')
ax[1].imshow(img_color_hsv[:, 350:, 0], plt.cm.hsv);
ax[2].set_title('S')
ax[2].imshow(img_color_hsv[:, 350:, 1], plt.cm.Greys_r);
ax[3].set_title('V')
ax[3].imshow(img_color_hsv[:, 350:, 2], plt.cm.Greys_r);
for ax_ in ax:
ax_.axis('off')
Shape: (350, 650, 3), Type: float32
[ 0.59429824 0.4903226 155. ]
El estándar YCbCr corresponde a una familia de modelos muy usado en fotografía digital
Y, se llama luma, corresponde a la luminancia
Cb y Cr, se llaman chroma, corresponde a la “diferencia en azul” y “diferencia en rojo”
Existe una transformación directa entere YCbCr y RGB
donde \(K_R + K_G + K_B = 1\)
Los distintos modelos de la familia YCbCr se diferencia en sus valores de \(K_R\), \(K_G\) y \(K_B\)
Por ejemplo la imagen anterior en YCbCr
# ITU-R BT.601
Kr = 0.299
Kg = 0.587
Kb = 0.114
Y = np.dot(img_color, [Kr, Kg, Kb])
Cb = 0.5*(img_color[:, :, 2] - Y)/(1-Kb)
Cr = 0.5*(img_color[:, :, 0] - Y)/(1-Kr)
fig, ax = plt.subplots(1, 4, figsize=(9.5, 3.2), tight_layout=True);
ax[0].imshow(img_color[:, 350:, :])
ax[1].set_title('Y')
ax[1].imshow(Y[:, 350:], cmap=plt.cm.Greys_r);
ax[2].set_title('Cb')
ax[2].imshow(Cb[:, 350:], plt.cm.Greys_r);
ax[3].set_title('Cr')
ax[3].imshow(Cr[:, 350:], plt.cm.Greys_r);
for ax_ in ax:
ax_.axis('off')