El objetivo de esta actividad es demostrar como con técicas de Machine Learning podemos trabajar con imágenes y reducir el su tamaño gracias a métodos de aprendizaje no supervisado.
Para ello hemos usado el método de K-Means, un algoritmo de clasificación.

Algoritmo de K-Means

  • Procedimiento

    Simplemente reducimos el número total de colores utilizados para representar la imagen, y de esta forma permitimos que se necesiten menos memoria (bits) para su almacenaje.
    Buscamos la similitud entre pixeles por medio de la creación de clusters, una vez que tengamos esa cercanía entre colores cambiaremos el color de todo ese cluster tomando como referencia el color del centroide.

Centroide: Pixel central del cluster.

Cargamos las librerias necesarias para la actividad

import cv2
import matplotlib.pyplot as plt
import seaborn as sns
from mpl_toolkits.mplot3d import Axes3D
import numpy as np
%matplotlib widget
%matplotlib inline
%matplotlib qt

Usamos una imagen para realizar la actividad. La imágen se otiene usando la libreria openCV

image = cv2.imread("images\meninas.jpg")
cuadro=cv2.cvtColor(image, cv2.COLOR_BGR2RGB)
ax = plt.axes(xticks=[], yticks=[])
ax.imshow(cuadro);
Las Meninas de Velázquez

¿Cómo se representa una imágen?

Recordamos que las imagenes se representan como matrices de (alto, ancho, canales), donde los valores de los canales son rojo/verde/azul y varían de 0 a 255.

Sin embargo, podemos ver este dataset como una nube de puntos tridimensional, donde cada pixel es una instancia.

Vamos a normalizar los valores entre 0 y 1 y a convertirlos en [n_instancias, 3]. Cada punto de 3 coordenadas se convierte en un valor.

#normalizamos la imagen para que tenga valores entre 0 y 1
data = cuadro / 255.
data = data.reshape(cuadro.shape[0] * cuadro.shape[1], 3)
data.shape

Representación de los pixeles de nuestra imágen en relación a su color

  • Usamos cada canal como punto de representación
def plot_pixels(data, title, colors=None, N=10000):

if colors is None:
colors = data
rng = np.random.RandomState(0) i = rng.permutation(data.shape[0])[:N] colors = colors[i] R, G, B = data[i].T 

fig, ax = plt.subplots(1, 2, figsize=(14, 6)) 

ax[0].scatter(R, G, color=colors, marker='.') ax[0].set(xlabel='Red', ylabel='Green', xlim=(0, 1), ylim=(0, 1)) ax[0].set_title("Relación entre Rojo y Verde 2D") ax[1].scatter(R, B, color=colors, marker='.') ax[1].set(xlabel='Red', ylabel='Blue', xlim=(0, 1), ylim=(0, 1)) ax[1].set_title("Relación entre Rojo y Azul 2D") 

fig.suptitle(title, size=20); 

# plot 
fig = plt.figure(figsize=(12, 8)) 
ax = fig.add_subplot(111, projection='3d') 
ax.scatter(R, G, B, color=colors, marker='.') ax.set(xlabel='Red', ylabel='Green', zlabel="Blue") ax.set_title("Relación entre RGB 3D") 

plt.show()

Usamos Machine Learning

Vamos ahora a reducir de 16 millones de colores a 16.

Como se trata de un dataset grande, vamos a usar una variación del k-means llamada mini-batch k-Means, que funciona exactamente igual que el k-means pero con mini-batches.

Lo que hacemos es utilizar como hiperparametro de KMeans el número de cluster igual 16, porque es el número de colores al cual queremos reducir. Esto significa que todos nuestros pixeles quedrán arupados en 16 grupos.

import warnings; warnings.simplefilter('ignore') # Fix NumPy issues.
from sklearn.cluster import MiniBatchKMeans

#Creamos la instancia del algortimo usando los hiperparametros

kmeans = MiniBatchKMeans(n_clusters=16, random_state=42, batch_size=32)

#Entrenamos
kmeans.fit(data)

#Obtenemos los colores que tienen los centroides
new_colors = kmeans.cluster_centers_[kmeans.predict(data)]

#Volvemos a visualizar las gráficas pero únicamente pasamos 16 colores de clasificación

plot_pixels(data, colors=new_colors, title="Reducir espacio latente a 16 colores")
Pixeles agrupados en 16 clusters

Como se puede observar se han reducido los colores a 16, ahora simplemente nos queda reconstruir la imágen y colocar cada pixel en su sitio.

Comprobamos el espacio que nos ocupa en memoria la nueva imágen.

Sin comprimir: 4320128

Comprimida: 128

Factor: 33751.0

Es indiscutible que se pierde calidad, pero pensad que acabamos de conseguir comprimir la imagen con un factor de 33751.0

Notebook completo

Referencias