Los humanos procesamos información visual de formas casi milagrosas y en tiempo real.
¿Qué resolución ve el ojo humano?
Dejando por un lado la explicación neurológica, intentemos acercarnos a la comprensión de la visión desde el procesamiento de información.
Para acercarnos como buenos matemáticos a experiencias visuales, es necesario conceptualizar y abstraer de mejor forma estos objetos. Comencemos con definir $img(x,y)$ como una imagen en blanco y negro.
Sea $A =[0,m]$ x $[0,n] \in \mathbb{R}^2$, definamos una imagen como una función $img(x,y)$ de la siguiente forma :
$$img\colon \begin{array}{>{\displaystyle}l} A \rightarrow \mathbb{R} \\ (x,y)\mapsto img(x,y) \in [0,1] \end{array} $$Es decir a cada una de las posiciones $(x,y)$ le corresponderá un tono de la escala de grises que se encuentra abajo.
sns_plot=sns.palplot(sns.color_palette("Greys",20))
Esta correspondencia pudiera darse con cualquier otra paleta de colores.
Dada la naturaleza continua de nuestro lienzo $A$, podemos suponer las posibles problemáticas que se presentan cuando una computadora intenta procesar una imagen. Para esto discretizaremos nuestra imagen con una nueva definición:
Nuestro imagen $img$ será una matriz real de $m*n$ en la que a cada una de sus entradas le corresponderá un valor entre cero y uno, es decir:
$$img(i,j)\in [0,1] $$Por ejemplo, supongamos $m,n=50$ y elijamos valores aleatorios para cada $img(i,j)$:
img=np.random.random((50,50))
ax=plt.imshow(img,cmap='Greys')
Veamos la misma imagen codificada con una paleta distinta:
sns_plot=sns.palplot(sns.color_palette("viridis",20))
ax=plt.imshow(img,cmap='viridis')
Para extender la noción de imagen en blanco y negro a una a color, necesitamos extender nuestra matriz de $m*n$ a un tensor, o arreglo multidimensional con dimensiones $m*n*3$, en la que cada nivel de la 3era dimensión determina cada uno de los canales rojo, verde y azul.
$$img(i,j) = (r,g,b)$$Con definición continua:
Sea $A =[0,m]$ x $[0,n] \in \mathbb{R}^2$, definamos una imagen a color como una función $img(x,y)$ de la siguiente forma :
$$img\colon \begin{array}{>{\displaystyle}l} A \rightarrow \mathbb{R}^3 \\ (x,y)\mapsto img(x,y) =\begin{bmatrix}r(x,y) \\ g(x,y) \\ b(x,y)\end{bmatrix} \end{array} $$donde $r,g,b \in [0,1].$
Es importante mencionar que existen muchos otras abstracciones del color como CMYK,HSV and HSL, CIELAB.
import imageio
img=imageio.imread('../../../../Scripts/E1D73264-0143-42C7-B511-562343FB6D27.jpg')
ax=plt.imshow(img)
img[0,0,:]
array([ 0, 9, 40], dtype=uint8)
fig, axs = plt.subplots(3)
axs[0].imshow(img[:,:,0],cmap='Reds_r')
axs[1].imshow(img[:,:,1],cmap='Greens_r')
axs[2].imshow(img[:,:,2],cmap='Blues_r')
plt.show()
Recordando la primera definición.
Sea $A =[0,m]$ x $[0,n] \in \mathbb{R}^2$, definamos una imagen como una función $img(x,y)$ de la siguiente forma :
$$img\colon \begin{array}{>{\displaystyle}l} A \rightarrow \mathbb{R} \\ (x,y)\mapsto img(x,y) \in [0,1] \end{array} $$Parece plantear inmediatamente la posibilidad de derivar. Esto está implementado por un filtro Sobel.
Sea $img$ nuestra imagen, ($*$) la operación convolución entonces $G_x, G_y$ aproximan el gradiente en cada dirección:
$$\mathbf{G_x} = \begin{bmatrix} -1 & 0 & +1 \\ -2 & 0 & +2 \\ -1 & 0 & +1 \end{bmatrix} * img \quad \mbox{y} \quad \mathbf{G_y} = \begin{bmatrix} -1 & -2 & -1 \\ 0 & 0 & 0 \\ +1 & +2 & +1 \end{bmatrix} * img $$Y $G$ aproximará la magnitud del gradiente: $$\mathbf{G} = \sqrt{ \mathbf{G_x}^2 + \mathbf{G_y}^2 }$$
import imageio
img = imageio.imread('../../../../Scripts/C712DD03-E345-459E-928F-A9949A247E75-1000.jpg', as_gray=True)
ax=plt.imshow(img,cmap='Greys_r')
img[0,0]
99.788
from scipy import ndimage, misc
derivada=ndimage.sobel(img)
plt.imshow(derivada,cmap='Greys_r')
<matplotlib.image.AxesImage at 0x1c18ba9320>
img = imageio.imread('../../../../Scripts/C712DD03-E345-459E-928F-A9949A247E75-1000.jpg')
derivada=ndimage.sobel(img)
plt.imshow(derivada)
<matplotlib.image.AxesImage at 0x106013518>
Supongamos que tenemos $k$ imágenes a color de $m*n$.
Definamos la media como:
$$\bar{img}= \frac{\sum_{i=1}^{k} img_{i}}{k}$$Sin embargo recuerden que $img_{i}$ es un tensor, es decir, si analizamos el promedio en cada pixel tenemos:
$$\bar{img}(i,j)= \frac{\sum_{l=1}^{k} img_{l}(i,j)}{k}=\frac{\sum_{l=1}^{k} \begin{bmatrix}img_l(i,j,1) \\ img_l(i,j,2) \\ img_l(i,j,3)\end{bmatrix}}{k} $$Paleta secuenciales:
sns_plot=sns.palplot(sns.color_palette("inferno",20))
Paleta divergente:
sns_plot=sns.palplot(sns.color_palette("coolwarm",20))
Paleta categorica:
sns.palplot(sns.color_palette("hls", 20))
El procesamiento de información visual se basa en conceptos matemáticos de campos diversos.
Python presenta herramientas bastante accesibles para comenzar a trabajar estos conceptos.
Las buenas visualizaciones permiten transformar conceptos abstractos o grandes cantidades de datos en información accesible al cerebro a 10 millones de bits por segundo.