Skip to main content

A simple library for image processing and visualization for the IS623 - Computación Gráfica course at Universidad Tecnológica de Pereira (UTP), developed by Kevin Esguerra Cardona.

Project description

IS623_ImageTools

IS623_ImageTools es una librería sencilla para el procesamiento y la visualización de imágenes, desarrollada en el marco del curso IS623 - Computación Gráfica impartido por el Profesor Francisco Alejandro Medina Aguirre en la Universidad Tecnológica de Pereira (UTP), Colombia. La librería fue creada por Kevin Esguerra Cardona con el objetivo de facilitar el aprendizaje de conceptos básicos del procesamiento digital de imágenes, el paradigma de la programación orientada a objetos y el manejo de paquetes en Python según los estándares PEP 609.


Características

  • Procesamiento de Imágenes:
    Permite cargar imágenes en formato JPG, normalizarlas, desnormalizarlas, invertir colores, aplicar filtros (por ejemplo, filtro de promedio) y ajustar propiedades como contraste e intensidad mediante una API encadenable.

  • Conversión de Formatos y Operaciones de Color:
    Facilita la conversión entre espacios de color, como de RGB a CMYK y viceversa, y permite la extracción de capas específicas en ambos formatos.

  • Filtros Basados en Patrones de Diseño:
    Incorpora el uso de patrones Strategy y Factory para seleccionar dinámicamente la transformación adecuada (por ejemplo, para realzar contraste o intensidad) en función de un factor de ajuste.

  • Visualización de Imágenes:
    La clase SimpleImageViewer organiza y muestra múltiples imágenes en una cuadrícula fija (2x2) en figuras de tamaño estándar, garantizando que las imágenes y sus títulos se presenten sin solapamientos.


Requisitos

  • Python: 3.8 o superior
  • Librerías externas:
    • NumPy
    • Pillow
    • Matplotlib

Nota: Solo se listan las dependencias externas a la distribución estándar de Python.


Uso

Procesamiento de Imágenes

La clase principal para el procesamiento es Imagen. Esta clase encapsula el estado de la imagen (almacenado en un arreglo NumPy) y permite encadenar métodos para aplicar diversas transformaciones. Por ejemplo:

from utilities_for_graphical_computing import Imagen, ColorConverter

# Cargar la imagen y normalizarla (valores en el rango [0,1])
imagen = Imagen.desde_archivo("ruta/a/la/imagen.jpg").normalizar()

# Invertir los colores de la imagen
imagen_invertida = Imagen(imagen.datos.copy()).invertir()

# Extraer la capa roja (índice 0) de la imagen RGB
imagen_roja = Imagen(imagen.datos.copy()).extraer_capa_rgb(0)

# Convertir la imagen de RGB a CMYK
imagen_cmyk = ColorConverter.rgb_a_cmyk(imagen)

Visualización de Imágenes

La clase SimpleImageViewer permite mostrar múltiples imágenes en una figura organizada en una cuadrícula fija de 2x2. Se aceptan tanto arreglos NumPy como instancias de Imagen (o similares) que dispongan del atributo datos.

from utilities_for_graphical_computing import SimpleImageViewer

imagenes = {
    "Original": imagen.datos,
    "Invertida": imagen_invertida.datos,
    "Capa Roja": imagen_roja.datos,
    "RGB a CMYK": imagen_cmyk.datos
}

visor = SimpleImageViewer(imagenes)
visor.show()

Ejemplo Completo

El siguiente fragmento de código, basado en el archivo main.py, muestra un ejemplo de implementación en el que se crean imágenes personalizadas, se aplican transformaciones y se visualizan los resultados:

from utilities_for_graphical_computing import Imagen, SimpleImageViewer
import numpy as np

def colorear_region(imagen: Imagen, rows, cols, color: list[int]) -> Imagen:
    if isinstance(rows, slice):
        rows = list(range(*rows.indices(imagen.datos.shape[0])))
    elif isinstance(rows, int):
        rows = [rows]
    if isinstance(cols, slice):
        cols = list(range(*cols.indices(imagen.datos.shape[1])))
    elif isinstance(cols, int):
        cols = [cols]
    for r in rows:
        for c in cols:
            imagen.colorear_pixel(r, c, color)
    return imagen

# Ejemplo: Crear una matriz personalizada 3x3
def matriz3x3Personalizada():
    imagen = Imagen(np.zeros((3, 3, 3), dtype=float))
    imagen.colorear_pixel(0, 0, [0, 1, 1])    # cyan
    imagen.colorear_pixel(0, 1, [1, 1, 1])    # blanco
    imagen.colorear_pixel(0, 2, [1, 0, 0])    # rojo
    imagen.colorear_pixel(1, 0, [1, 0, 1])    # magenta
    imagen.colorear_pixel(1, 1, [0.5, 0.5, 0.5])  # gris
    imagen.colorear_pixel(1, 2, [0, 1, 0])    # verde
    imagen.colorear_pixel(2, 0, [1, 1, 0])    # amarillo
    imagen.colorear_pixel(2, 2, [0, 0, 1])    # azul
    return imagen.datos

if __name__ == "__main__":
    matriz3x3 = matriz3x3Personalizada()

    # Cargar y procesar la imagen "paris.jpg"
    paris = Imagen.desde_archivo("paris.jpg").normalizar()
    paris_invertida = Imagen(paris.datos.copy()).invertir().datos

    images = {
        "Matriz 3x3 personalizada": matriz3x3,
        "Paris - Invertida": paris_invertida,
        # ... otros ejemplos de transformación
    }

    viewer = SimpleImageViewer(images)
    viewer.show()

Documentación de la API

Nota: En este documento se habla de la API (Interfaz de Programación de Aplicaciones) porque describe el conjunto de clases, métodos y funciones que la librería pone a disposición de los usuarios para interactuar con ella. Es decir, la API es la "puerta de entrada" que permite utilizar las funcionalidades de IS623_ImageTools en tus propios proyectos.

Clase Imagen

La clase Imagen es el núcleo del procesamiento de imágenes en la librería. Esta clase encapsula un arreglo NumPy que representa la imagen y proporciona un conjunto de métodos para transformar, manipular y analizar la imagen de forma encadenable. A continuación se detalla el funcionamiento de cada uno de sus métodos:

  • Imagen.desde_archivo(ruta: str) -> Imagen

    Este método de clase carga una imagen a partir de la ruta especificada, utilizando la librería Pillow para abrir el archivo y convertir la imagen a formato RGB. Luego, transforma la imagen en un arreglo NumPy y devuelve una nueva instancia de Imagen con estos datos.

  • normalizar() -> Imagen

    Este método transforma la imagen de modo que todos sus valores de píxel se escalen al rango [0, 1]. Esto se logra dividiendo el arreglo de la imagen por 255. Es útil para realizar operaciones de procesamiento que requieren trabajar con valores flotantes normalizados. Retorna la misma instancia, permitiendo el encadenamiento de métodos.

  • desnormalizar() -> Imagen

    Realiza la operación inversa a normalizar(): multiplica el arreglo normalizado por 255 y convierte los valores resultantes a enteros sin signo (uint8), volviendo a la escala de 0 a 255. Esto es útil para visualizar la imagen o guardarla en formatos que requieren esta escala.

  • invertir() -> Imagen

    Invierte los colores de la imagen asumiendo que está en el rango [0, 1]. Cada valor se transforma en su complemento (1 - valor), lo que resulta en una imagen con colores invertidos. Este método modifica la imagen en sitio y retorna la instancia para permitir el encadenamiento.

  • colorear_pixel(row: int, col: int, color: list[int]) -> Imagen

    Permite modificar el color de un píxel específico de la imagen. Se requiere indicar la posición del píxel (fila y columna) y proporcionar una lista con los valores de color, cuya longitud debe coincidir con el número de canales de la imagen (por ejemplo, 3 para imágenes RGB). El método valida que los índices estén dentro del rango de la imagen y que el tamaño del color sea correcto, luego actualiza el valor del píxel y retorna la instancia actual.

  • extraer_capa_rgb(indice: int) -> Imagen

    Extrae una capa específica de la imagen en formato RGB. Se espera que el índice sea 0, 1 o 2, correspondientes a los canales R, G y B, respectivamente. El método crea un nuevo arreglo donde sólo se conserva la capa especificada y las demás se ponen a cero, devolviendo una nueva instancia de Imagen con este arreglo.

  • extraer_capa_cmyk(indice: int) -> Imagen

    Simula la extracción de una capa en formato CMYK a partir de una imagen en RGB. Aunque la imagen original es RGB, el método utiliza reglas específicas para "extraer" las componentes que corresponderían a cyan, magenta, yellow o black según el índice (0 a 3). Devuelve una nueva instancia de Imagen con la capa extraída.

  • mean_filter(kernel_size: int = 3) -> Imagen

    Aplica un filtro de promedio (o media) sobre la imagen. Para cada píxel, calcula el promedio de los valores en una vecindad definida por un kernel de tamaño kernel_size (que debe ser impar) y asigna este valor al píxel. El método utiliza padding con modo 'reflect' para manejar los bordes y retorna la misma instancia modificada.

  • gris_promedio() -> Imagen

    Convierte la imagen a escala de grises utilizando el promedio de los tres canales de color. Primero, calcula el promedio para cada píxel y luego replica ese valor en los tres canales para mantener el mismo número de dimensiones. Devuelve una nueva instancia de Imagen con la imagen en escala de grises.

  • gris_luminosidad() -> Imagen

    Realiza la conversión a escala de grises aplicando la fórmula de luminosidad, que pondera cada canal (R, G y B) de acuerdo con la percepción humana (0.299 para R, 0.587 para G y 0.114 para B). El resultado se replica en los tres canales, devolviendo una nueva instancia de Imagen en escala de grises.

  • gris_tonalidad() -> Imagen

    Convierte la imagen a escala de grises utilizando el método de tonalidad (midgray). Para cada píxel, toma el promedio entre el valor máximo y mínimo de los canales y replica este valor en los tres canales, retornando una nueva instancia de Imagen en escala de grises.

  • ajustar(factor: float) -> Imagen

    Ajusta la imagen aplicando un filtro basado en el valor de factor, que se espera esté en el rango [-1, 1].

    • Si factor es negativo, se aplica un filtro que realza el contraste mediante una transformación logarítmica.
    • Si factor es positivo, se aplica un filtro que realza la intensidad utilizando una transformación exponencial.
    • Si factor es 0, no se realiza ningún ajuste.
      El método selecciona la estrategia adecuada utilizando un patrón de diseño Factory y retorna la instancia modificada.
  • fusionar(imagenes: list[Imagen]) -> Imagen

    Fusiona varias imágenes de las mismas dimensiones realizando una suma pixel a pixel de sus arreglos. Antes de la fusión, valida que todas las imágenes tengan el mismo tamaño (en filas y columnas). Devuelve una nueva instancia de Imagen con el resultado de la fusión.

  • fusionar_ecualizado(imagenes: list[tuple[Imagen, int]]) -> Imagen

    Similar al método anterior, pero permite aplicar un factor de ecualización a cada imagen antes de sumarlas. Cada imagen se multiplica por el factor especificado en la tupla correspondiente, y luego se realiza la suma pixel a pixel. Retorna una nueva instancia de Imagen con la imagen fusionada.

Clase ColorConverter

  • rgb_a_cmyk(imagen: Imagen) -> Imagen

    Este método transforma una imagen en formato RGB a CMYK. Primero, valida que la imagen tenga exactamente 3 canales (R, G y B). Luego, se normalizan los datos (si no lo están ya) para trabajar en el rango [0, 1]. Se extraen los canales R, G y B y se calcula el canal K como la diferencia entre 1 y el valor máximo de los tres canales para cada píxel. Con K calculado, se determinan los canales C, M y Y utilizando fórmulas que ajustan cada componente de color en función de la luminosidad del píxel. Finalmente, los canales C, M, Y y K se combinan en un arreglo de 4 canales y se retorna una nueva instancia de Imagen que representa la imagen en formato CMYK.

  • cmyk_a_rgb(imagen: Imagen) -> Imagen

    Este método realiza la operación inversa, convirtiendo una imagen en formato CMYK a RGB. Primero, verifica que la imagen tenga 4 canales. Se extraen los valores de los canales C, M, Y y K y se aplican fórmulas que permiten obtener los valores correspondientes de R, G y B. La fórmula utilizada es:

    R = (1 - C) \cdot (1 - K)
    G = (1 - M) \cdot (1 - K) 
    B = (1 - Y) \cdot (1 - K)
    

    Estos cálculos producen una imagen en el rango [0, 1] para cada canal RGB. Finalmente, se retorna una nueva instancia de Imagen con estos datos, permitiendo que la imagen convertida pueda ser utilizada para visualización o procesamiento adicional.

Estrategias de Filtro

  • FiltroContraste

    Se utiliza para realzar el contraste de la imagen mediante una transformación logarítmica. Se espera un factor negativo. En su constructor, verifica que el factor sea negativo y almacena su valor absoluto. En el método aplicar, normaliza la imagen y calcula una transformación logarítmica (usando una constante derivada de log10) para enfatizar las zonas oscuras, mezclando esta transformación con la imagen original en proporción al factor.

  • FiltroIntensidad

    Realza la intensidad de la imagen usando una transformación exponencial. Requiere un factor positivo y, en su método aplicar, normaliza la imagen y aplica la transformación exponencial para potenciar las zonas claras, mezclándola nuevamente con la imagen original.

  • FiltroIdentity

    Es la estrategia por defecto que no realiza ningún cambio en la imagen; simplemente retorna una copia de la misma. Esto se utiliza cuando el factor es 0, lo que indica que no se desea aplicar ningún ajuste.

  • FiltroFactory.obtener_filtro(factor: float) -> FiltroStrategy

    Este método actúa como una fábrica (Factory Pattern) para seleccionar y retornar la estrategia de filtrado adecuada en función del valor del factor proporcionado:

    • Si el factor es 0, retorna una instancia de FiltroIdentity.
    • Si el factor es negativo, retorna una instancia de FiltroContraste.
    • Si el factor es positivo, retorna una instancia de FiltroIntensidad.

    De esta manera, se encapsula la lógica de selección del filtro y se facilita la extensión futura (por ejemplo, añadiendo nuevos tipos de filtros sin modificar el código del cliente).

Clase SimpleImageViewer

  • show()

    El método show() de la clase SimpleImageViewer se encarga de la visualización de múltiples imágenes organizándolas en una cuadrícula fija de 2x2 por figura. El funcionamiento detallado es el siguiente:

    • Se parte de un diccionario de imágenes, donde las claves son títulos descriptivos y los valores pueden ser arreglos NumPy o instancias de Imagen (se extrae el arreglo mediante una función interna).
    • Para cada grupo de hasta 4 imágenes, se crea una figura utilizando matplotlib.pyplot.subplots con una cuadrícula de 2 filas y 2 columnas. El tamaño de la figura se define mediante un factor de escala, garantizando un tamaño estándar para todas las imágenes.
    • Se ajusta el espaciado entre subplots (usando subplots_adjust) para asegurar que ni las imágenes ni sus títulos se solapen.
    • Cada imagen se muestra en su respectivo subplot: si la imagen es un arreglo 2D se utiliza un mapa de colores (por ejemplo, cmap="gray"), y si es un arreglo 3D se muestra con los colores originales.
    • Se ocultan los ejes de aquellos subplots que no se utilizan en caso de que la figura no se llene completamente.
    • Finalmente, se llama a plt.show() para renderizar todas las figuras generadas.

    Esta implementación garantiza una presentación consistente y clara de los resultados del procesamiento, facilitando la comparación y evaluación visual de diferentes transformaciones aplicadas a las imágenes.


Contribuciones

Si deseas contribuir a IS623_ImageTools, te invitamos a:

  • Abrir un issue para reportar errores o sugerir mejoras.
  • Enviar un pull request con tus aportes.

Toda contribución es bienvenida para enriquecer esta herramienta educativa. También te invito a escribirme al correo electrónico kevin.esguerra@utp.edu.co


Licencia

Este proyecto se distribuye bajo la Licencia MIT. Consulta el archivo LICENSE para más detalles.


Créditos

  • Profesor: Francisco Alejandro Medina Aguirre
  • Desarrollador: Kevin Esguerra Cardona
  • Universidad: Universidad Tecnológica de Pereira (UTP)
  • Tecnologías utilizadas: VS Code, GitHub Copilot, ChatGPT

Disfruta explorando y aprendiendo procesamiento digital de imágenes con IS623_ImageTools.

Project details


Download files

Download the file for your platform. If you're not sure which to choose, learn more about installing packages.

Source Distribution

is623_imagetools-1.0.0.tar.gz (11.5 kB view details)

Uploaded Source

Built Distribution

If you're not sure about the file name format, learn more about wheel file names.

is623_imagetools-1.0.0-py3-none-any.whl (12.5 kB view details)

Uploaded Python 3

File details

Details for the file is623_imagetools-1.0.0.tar.gz.

File metadata

  • Download URL: is623_imagetools-1.0.0.tar.gz
  • Upload date:
  • Size: 11.5 kB
  • Tags: Source
  • Uploaded using Trusted Publishing? No
  • Uploaded via: twine/6.1.0 CPython/3.12.9

File hashes

Hashes for is623_imagetools-1.0.0.tar.gz
Algorithm Hash digest
SHA256 15f3c774c854b78c41168773cfef5cbc5308a34a5e72bee677c8295c5e4544d9
MD5 f76008f433d3792e7a14b522a28af1c1
BLAKE2b-256 61d6108b4f4f3a16e2402ac277950e37a1b2266e2ef5db9cfccf30832e0bdf3b

See more details on using hashes here.

File details

Details for the file is623_imagetools-1.0.0-py3-none-any.whl.

File metadata

File hashes

Hashes for is623_imagetools-1.0.0-py3-none-any.whl
Algorithm Hash digest
SHA256 dddd9415993b3c75f429de3998bd450e3c8b82360ff6a0bde0dbde0d3b848109
MD5 7df47c33d505a19bdba8ffeedc12bd9e
BLAKE2b-256 9ff6459314700e7c4f1623364a6038873b20cdc24df4810b89ffc6bac4845007

See more details on using hashes here.

Supported by

AWS Cloud computing and Security Sponsor Datadog Monitoring Depot Continuous Integration Fastly CDN Google Download Analytics Pingdom Monitoring Sentry Error logging StatusPage Status page