Extracción de Colores de Imágenes en Python.

La extracción de colores de una imagen es una técnica fundamental en el procesamiento digital que permite identificar, cuantificar y analizar los tonos predominantes en una fotografía o en cualquier recurso gráfico. En un mundo cada vez más orientado al análisis visual —desde el diseño hasta la visión por computadora— comprender la paleta cromática de una imagen se ha vuelto una herramienta indispensable para múltiples disciplinas. Así, en nuestro artículo de esta semana, nos proponemos a realizar la extracción de los colores principales de la siguiente imagen:

En esta ocasión haremos uso de google colab, para nuestra practica, donde, como es natural, comenzaremos realizando las importaciones pertinentes:

Como se ve, realizamos la importación de varias librerías y recursos: ‘from PIL import Image‘ (Para cargar la imagen), ‘import numpy as np‘ (para manipular arrays y hacer reshape), ‘import matplotlib.pyplot as plt‘ (para mostrar la imagen y paleta resultante) y ‘from sklearn.cluster import KMeans‘ (que nos permitirá emplear el algoritmo K-Means de scikit-learn, para agrupa píxeles por similitud de color).

A continuación, pasaremos a abrir la imagen con image.open() y la convertimos inmediatamente a un array con np.array():

El resultado es un array de forma (ancho, alto, canales) (normalmente (w, h, 3) para RGB. Usar Pillow y convert(RGB) es recomendable si hay posibilidad de canal alfa; en ese caso conviene forzar Image.open(file_name).convert(«RGB») para garantizar 3 canales.

Tras ello, pasaremos a convertir nuestra imagen en una matriz de 2 dimensiones en donde cada fila es un pixel y cada columna viene representada por los canales (R. G . B.). De este modo, teniendo nuestra imagen 501 x 501 pixeles y 3 canales, el formato que pasaremos al algoritmo KMeans, será (251001, 3) que es el formato que espera para realizar la agrupación por colores.

El paso siguiente será el de seleccionar el número de colores dominantes que queremos extraer. Número que almacenaremos en la variable n_colors y que en nuestro caso será 15:

Por fin, en este paso se crea y entrena el modelo K-Means que identificará los colores dominantes de la imagen. Instanciando, primero, el algoritmo con KMeans(n_clusters=n_colors, random_state=42), donde n_clusters es el número de colores que queremos extraer (definidos antes) y random_state garantiza resultados reproducibles. Finalmente, con .fit(pixels), el modelo agrupa todos los píxeles de la imagen en clusters según la similitud de sus valores RGB; cada iteración ajusta los centroides para minimizar la distancia entre los píxeles y el centro del grupo al que pertenecen. Al finalizar, el modelo queda entrenado y listo para proporcionar los colores representativos de la imagen:

Una vez entrenado el modelo, K-Means almacena en kmeans.cluster_centers_ los centroides de cada grupo, que representan los colores dominantes encontrados en la imagen. Estos valores se obtienen como arreglos de tipo float, por lo que primero se convierten a enteros de 0 a 255 mediante np.uint8, generando así colores válidos en formato RGB. El resultado es un arreglo llamado palette, donde cada fila es un color característico detectado por el algoritmo. Esta paleta condensada es una representación compacta de los tonos predominantes de la imagen original y servirá para su visualización o para cualquier aplicación posterior, como diseño gráfico, clasificación o análisis cromático:

Obtenida nuestra paleta de colores principales, simplemente nos restará representarla con nuestra librería matplotlib, mediante el procedimiento habitual:

ORDENANDO COLORES POR IMPORTANCIA.

Como hemos dicho, tras todo el proceso hemos obtenido los 15 colores más representativos de nuestra imagen. No obstante, tal y como está presentado el resultado, quizás no nos diga gran cosa, al no estar estos ordenados ni contar con el porcentaje que cada uno representa en el conjunto. Por ello, vamos a añadir algunas líneas justo después de la definición de la paleta extraída (variable palette):

Aquí, partiendo del resultado antes obtenido por K-Means, cada píxel de la imagen recibe una etiqueta numérica que indica a qué cluster (o color) pertenece. Etiqueta, la cual, se almacena una a una en kmeans.labels_. Para saber qué colores aparecen más en la imagen, primero contamos cuántas veces aparece cada etiqueta usando np.unique(labels, return_counts=True), lo que devuelve los identificadores de los clusters y cuántos píxeles corresponden a cada uno. Luego, para convertir esas cantidades absolutas en proporciones, dividimos cada conteo entre el número total de píxeles (len(labels)) y lo multiplicamos por 100 para obtener el porcentaje que representa cada color sobre el total.

Una vez obtenidas las proporciones, podemos ordenar los colores según su presencia en la imagen. Para ello usamos np.argsort(-frequencies), que devuelve los índices que ordenarían el array de frecuencias de mayor a menor; el signo negativo se usa para invertir el orden, ya que argsort normalmente ordena de menor a mayor:

Así, al aplicar estos índices tanto al array de colores (palette) como al array de frecuencias, generamos una paleta reordenada (palette_sorted) donde el primer color es el más predominante y el último el menos frecuente. Esta ordenación es especialmente útil para representar la paleta de manera más intuitiva, ya que los colores principales aparecen primero.

Ahora ya lo único que deberemos hacer es volver a la parte del código que usamos para mostrar la paleta. Solo que usando en esta ocasión, la variable palette_sorted para la representación:

Finalmente, si lo deseamos, podemos mostrar el porcentaje de cada color (esta vez redondeado a dos decimales con .2f) valiéndonos de un ciclo for que recorra la paleta y frecuencias ordenadas:

Así, lo que esta salida nos está diciendo es que el 23,13% de los pixeles de la imagen tienen un color que vendría determinado por los valores RGB, 12 para el canal rojo, 4 para el verde y 11 para el azul y así con el resto de la salida.

CONCLUSIÓN:

En conjunto, calcular y ordenar las frecuencias de color permite obtener una paleta no solo representativa, sino también informativa sobre la estructura cromática de una imagen. Este proceso facilita identificar los tonos predominantes y comprender mejor la composición visual, lo que resulta especialmente útil en tareas de diseño, análisis estético, clasificación de imágenes y generación de estilos visuales coherentes.

Saludos.

Transformaciones Lineales en el Plano con Python.

Hola y bienvenidos una vez más a vuestro blog sobre programación en Python en el que, siguiendo con nuestra línea de artículos sobre simulaciones y aplicación de conceptos matemáticos, hoy hablaremos de las Transformaciones Lineales en el Plano con Python. Estas constituyen una de las herramientas más potentes del álgebra lineal, ya que permiten modificar un conjunto de puntos del plano aplicando operaciones como rotaciones, escalados, cizallamientos y reflexiones. Transformaciones, las cuales, representaremos mediante matrices y cuya aplicación se realiza mediante la multiplicación matricial. En este artículo exploraremos cómo usar Python, junto a las librerías Numpy y Matplotlib, para simular y visualizar dichas transformaciones lineales en 2D, utilizando figuras geométricas simples.

CONCEPTO MATEMÁTICO:

En matemáticas, una transformación lineal en el plano es una función que toma puntos de un espacio bidimensional y los mapea en otros puntos del mismo espacio, preservando la linealidad. Esto significa que las líneas rectas permanecen rectas y el origen (0, 0) se mantiene fijo. Una forma muy útil de representar este tipo de transformaciones es mediante matrices, específicamente matrices de 2×2 cuando se trabaja en el plano.

Esta transformación se define mediante una matriz:

Donde los valores a, b, c, y d, determinan la naturaleza de la transformación. Esta matriz actúa sobre un vector columna que representa un punto del plano:

De modo que podremos aplicar la transformación T al vector v mediante el producto matricial:

IMPLEMENTACIÓN EN PYTHON:

A continuación nos disponemos a aplicar este tipo de transformaciones en Python sobre los vértices de una figura cuadrada definida en un plano 2D valiéndonos de las librerías numpy (para definir la figura) y matplotlib (para su representación). Dichos vértices los definiremos en un array al que hemos dado el sencillo nombre de figura. En dicho array, definiremos dos veces el punto (0,0) para que la figure quede correctamente cerrada en la representación:

OUTPUT:

Esta es la figura sobre la que aplicaremos las transformaciones.

MATRIZ IDENTIDAD.

A continuación, partiendo de la teoría vista, iremos aplicando cada una de las siguientes transformaciones lineales: Rotación, reflexión, escalado y cizallamiento, no sin antes, hacer notar que las 4 matrices de transformación podrían considerarse versiones alteradas de la matriz identidad que multiplicada por cada vértice de la figura original, nos da, precisamente esa misma figura y en la misma posición y forma en el plano, sin producir ningún cambio (por algo la llamamos matriz identidad).

Matriz Identidad de 2 dimensiones.

Para comprobarlo, realizaremos la aplicación de esta matriz en código Python (ahora más adelante, explicaremos, mas en detalle, el código):

OUTPUT:

Como se ve, el resultado de la transformación (en rojo) coincide exactamente con la original que queda oculta tras ella.

Pasemos, ahora sí, a ver las distintas transformaciones. Empezando por la más compleja de ellas.

ROTACIÓN.

Para aplicar una rotación (de 45º en este caso) introduciremos algunas modificaciones en nuestro código. Siendo la primera de ellas, la definición de la matriz de rotación que crearemos usando la librería numpy:

Dado que nuestra intención es aplicar una rotación de 45º a nuestro cuadrado, aplicaremos las operaciones de seno y coseno sobre el resultado de dividir pi (obtenido con np.pi) por 4. Una vez definida la matriz de rotación deberemos multiplicarla por la que contiene los vertices de nuestra figura. Para ello definiremos una función transformar() que tomará como argumentos, ambas matrices y empleará el método .dot() para realizar dicha operación. Esta figura devolverá una nueva matriz con cada uno de los vértices de la figura original, transformados. Una vez definida, haremos la llamada a dicha función, pasándole las referidas matrices:

Una vez aplicada la transformación, pasaremos a su representación en el plano, con matplotlib, incluyendo ahora la figura resultante de la multiplicación de matrices:

OUTPUT:

Obtenemos así la representación de nuestra figura original junto a la versión rotada 45º, respecto al punto 0,0, de la misma. Para recapitular el código completo que hemos ejecutado sería el que sigue:

A continuación y para el resto de transformaciones, la explicación es muy sencilla, ya que basta con definir la concreta matriz de transformación que vamos a multiplicar por nuestra matriz original, dejando intacta la función de multiplicación. Modificando, eso si, la matriz transformada a representar gráficamente.

ESCALADO.

En este caso, nos disponemos a realizar cambios en la escala, tanto para el eje x (0.5) como para el eje y (de 0.5). Estos valores aplicados sobre la matriz original, dará como resultado una nueva figura mas achatada por el eje x y alargada por el eje y, con respecto a la original. De este modo el código quedaría así:

OUTPUT:

Si eligiéramos el mismo valor para ambos ejes (por ejemplo 1.5):

OUTPUT:

Obtendríamos una versión de mayor tamaño, a la original, pero guardando las mismas proporciones.

REFLEXIÓN.

Aplicamos esta transformación para obtener un reflejo de la figura original, en el eje x, y o en ambos, mediante el cambio de signo del valor 1 en el eje correspondiente de la matriz identidad. Así, si lo que queremos es generar una versión refleja de nuestra figura justo debajo de ella, realizaremos el cambio de signo (a negativo) del valor correspondiente al eje y:

OUTPUT:

CIZALLAMIENTO.

En la última transformación que vamos a ver, lo que hacemos es desplazar uno de los lados respecto al otro siguiendo uno de los ejes (el x en este caso) provocando una deformación respecto de la figura original. Para ello, simplemente cambiaremos el valor del 0 original de la matriz identidad. para dicho eje, tal y como se puede ver en el código:

OUTPUT:

CONCLUSIÓN:

En el presente artículo hemos visto, de forma muy gráfica, en que consisten y como pueden realizarse las distintas transformaciones lineales en un plano 2D, utilizando el lenguaje Python. A partir de este punto, el lector puede probar a combinar más de una transformación, empleando una misma matriz, e ir comprobando el resultado en la gráfica. Lo que sin duda le ayudará a entender la naturaleza y aplicación de este interesante concepto matemático.

Saludos.