[APORTE] Reconozcamos los colores de nuestras imágenes

Iniciado por Mortal_Poison, Noviembre 05, 2017, 02:42:20 AM

Tema anterior - Siguiente tema

0 Miembros y 1 Visitante están viendo este tema.

Hola a tod@s,

Un día, estaba con unas imágenes que debía identificar para realizar una tool, sin embargo, me era muy complicado contar los colores. Vamos, a veces se cuenta mal, se es daltónico(sin ánimo de discriminar o criticar ni nada por el estilo) o se está con 'pereza'. Así que realicé un script el cual reconocerá los colores de las imágenes que le pasemos.

El módulo de imagen[Image] proporciona una clase con el mismo nombre que se utiliza para representar una imagen PIL. El módulo también proporciona varias funciones de fábrica, incluidas funciones para cargar imágenes desde archivos y para crear nuevas imágenes.

Tenemos muchísima información aquí: No tienes permitido ver los links. Registrarse o Entrar a mi cuenta

Como pueden ver, el módulo nos provee funciones muy potentes.

Como me encuentro en Windows, les va a pedir si o si el módulo de Image. Suele ocurrir que si te bajas el paquete, te salga un error de zlib, sin embargo, lo vamos a instalar de la siguiente manera en nuestro cmd.

Código: text
python -m pip install Pillow


Una vez hecho esto, voy a explicar cada una de las partes del código de una manera concisa y detallada. También he comentado el código, para que lo entiendan de una manera más precisa.



Ilustración 1. Inicialización.

Al inicio del script inicializamos las variables que vamos a usar. El módulo de ImageDraw e Image son importados para el reconocimiento y para trabajar con los colores de la imagen que vayamos a 'reconocer'. Además de esto, tenemos el famoso argparse, el cual determinará los argumentos del script que el usuario nos envie.


Ilustración 2. Uso de argparse y obtención de parámetros dados por el usuario.

Siempre que se vaya a ejecutar el script, comenzará por el main(porque lo estamos llamando desde __name__ == '__main__). Siguiendo con la guía, tenemos dos argumentos, el de output y el de file.

En la línea de comandos cuando ejecutemos nuestro script, tendremos que hacerlo de la siguiente forma:

Código: text
python recoignize.py -o "C:/Users/Mortal/Desktop/Name_Directory" -f "C:/Users/Mortal/Desktop/Nombre_De_La_Imagen.png"


Si el directorio no existe, se creará, no se preocupen.

Notemos que llamamos a logic_images(args) y le pasamos nuestros argumentos.

Vamos por pedazos, como Jack el destripador.


Ilustración 3. Obteniendo información de nuestra imagen.

Como podemos notar, lo que hacemos es obtener nuestra imagen(args.file) y luego operar con PIL_IMAGE_FILE, ya que es la que abre nuestra imagen. Además de eso, obtenemos el tamaño del alto y ancho y los multiplicamos. En la línea 36 ponemos la condición de que si no llegase a existir el directorio que proporcionamos, que se cree.

De la línea 40 a la línea 47 lo que hacemos es obtener la información(colores) de nuestra imagen y posteriormente, hacer nuestra condición para ver si restamos pixeles a nuestra imagen o ya trabajamos con ellos.


Ilustración 4. Asignar a nuestro diccionario los pixeles.

En nuestro diccionario quedarían los pixeles con el identificador de 'col' para acceder a él.
Si no me entendieron, sabemos que los diccionarios son de este estilo:
Código: python
capitales = {'Scripting':'Python', 'Programacion':'PHP'}


Si notan, poseemos clave-valor en un diccionario, para acceder a cada uno de esos datos, accedemos por la clave.

En el caso de los pixeles con 'col' vendría siendo lo mismo.


Ilustración 5. Guardar los colores analizados en imagenes separadas.

En la Ilustración 5. podemos notar que lo que hacemos es hacer unos cálculos con los píxeles(más información en los comentarios) y luego, pasamos a guardar las imágenes de los colores que analizamos de nuestra imagen principal en porcentajes y en formato png. Añado que, luego de eso, también se añade a la lista de sorted_all_colors, el color y el porcentaje, para luego ordenarlo. Esto con el fin de que más adelante, lo usaremos para crear nuestra "torta" de los colores y para ello ocupamos una torta, por ello se crea una imagen de tipo círculo.


Ilustración 6. Guardamos cada uno de los colores en nuestro círculo.

En la Ilustración 6. observamos que hacemos varias cosas, entre ellas, crear y modificar nuestro círculo(ángulo de inicio, ángulo de fin, relleno, entre otros) y también creamos un archivo .json que contiene el código RGB del color y el número del mismo. Además de eso, nuestra torta que contiene los colores usados por la imagen se llama torta.png. Como les mencioné, en los comentarios está muchísimo más detallado y bueno, espero que les sea de utilidad.

A continuación, les dejo el código completo.


Código: python

#!/usr/bin/env python
#-*- coding: utf-8 -*-

### Mortal_Poison ###
### https://www.youtube.com/VIINVIDEOSHD ###
### https://www.viinacademy.com ###
### https://underc0de.org/foro/profile/Mortal_Poison ###

## Importamos las librerías necesarias.
import os
import json
from PIL import ImageDraw
from PIL import Image
import argparse

# Inicializamos.
porcentaje_minimo = 0.5 # Porcentaje de color en el archivo que se agregará.
transparent_pixels = True # Usaremos pixeles transparentes.
circle_size_create = 500 # Tamaño de archivo circular en pixeles.
color_files_flag = True # Obtener resultados con archivos de imagen.
   
file_for_results = True # Obtendremos resultados con JSON.
format_for_results = 'hex'  # Lo hacemos porque conocemos que el formato es hex - rgb - rgba.
json_extension = '.json' # Nuestros resultados tendrán la extensión en json.

## Diccionario para nuestros pixels.
dictionary_for_colors = {}
## Lista de los colores ordenados.
sorted_all_colors = []
   

   
## Nuestra función.
def logic_images(args):
    ## Abrimos nuestra imagen ##
    global pixels_per_image
    PIL_IMAGE_FILE = Image.open(args.file)
    ## Obtenemos el tamaño y lo multiplicamos(alto por ancho).
    pixels_per_image = PIL_IMAGE_FILE.size[0] * PIL_IMAGE_FILE.size[1]
    ##Si no llegase a existir el directorio, lo creamos :).
    if not os.path.isdir(args.output):
        os.makedirs(args.output)
    ## Declaramos nuestra variable global, porque la tenemos local.
   
    ## Obtenemos la información de nuestra imagen con un loop.
    for pixels_rgba in PIL_IMAGE_FILE.getdata():
        if pixels_rgba[3] == 0:
            if transparent_pixels == True:
                pixels_rgba = (0, 0, 0, 0)
            else:
                pixels_per_image -= 1
                continue
        try:
            ## Asignamos en nuestro diccionario el pixel rgba junto al identificador col.
            col = dictionary_for_colors[pixels_rgba]['col']
            # Incrementamos nuestro col.
            dictionary_for_colors[pixels_rgba] = {'col': col + 1}
       
        except:
            dictionary_for_colors[pixels_rgba] = {'col':1}
     
    ## Recorremos nuestro diccionario.
    for color in dictionary_for_colors:
         ## De nuestro diccionario de los pixeles que obtuvimos, lo vamos a multiplicar y lo dividimos entre el flotanto de el total de píxeles.
        percentage_of_color = dictionary_for_colors[color]['col'] * 100 / float(pixels_per_image)
        # Ahora condicionamos, si la anterior variable es mayor o igual a nuestro porcentaje mínimo(qu es del 0.5) entonces...
        if percentage_of_color >= porcentaje_minimo:
            # Además, condicionamos si vamos a obtener los resultavos en formato de imagen, entonces...
            if color_files_flag == True:
                # Creamos una nueva imagen, le asiganmos el modo,tamaño y los colores.
                img = Image.new('RGBA', (100, 100), (color[0],color[1],color[2],color[3]))
                # Aqi asignamos el porcentaje de color para posteriormente guardarlas.
                file_name = 'Per_Color_%03.4f.png' % percentage_of_color
                # Ahora las guardamos en nuestro directorio de salida en formato PNG.
                img.save(os.path.join(args.output,file_name),format="png")
            # Para un mayor orden, se me ocurrió ordenar los colores. Abrimos nuestra lista con los datos respectivos.
            sorted_all_colors.append({'color':color,'number':percentage_of_color})
    # Por último, ordenamos con sort.
    sorted_all_colors.sort(key=lambda k: k['number'],reverse=True)
   
    # Creamos un círculo para mostrar los resultados como en una torta de resultados.
    circle = Image.new('RGBA', (circle_size_create,circle_size_create), (0,0,0,0))
    _current_angle = 0
    # Mediante el loop y los colores ordenados, lo recorremos...
    for x in sorted_all_colors:
        # Lo multiplicamos para expandirlo.
        _current_pieslice_angle = x['number'] * 3.6
        # Dibujamos nuestro circulo y además, lo llenamos de los colores que están en nuestra lista de colores ordenados.
        ImageDraw.Draw(circle).pieslice([10, 10, circle_size_create-10, circle_size_create-10], _current_angle, _current_angle + _current_pieslice_angle, fill=(x['color'][0],x['color'][1],x['color'][2],x['color'][3]))
        # Acumulamos.
        _current_angle += _current_pieslice_angle
     
        # Condicionamos, si vamos a obtener los resultados en .json entonces...
        if file_for_results == True:
            # Si el formato del color de resultados además es hex, entonces...
            if format_for_results == 'hex':
                x['color'] = '#%02x%02x%02x' % (x['color'][0],x['color'][1],x['color'][2])
            elif format_for_results == 'rgb':
                x['color'] = (x['color'][0],x['color'][1],x['color'][2])
    # Guardamos nuestro círculo, en donde hemos elegido guardar todo y le llamamos torta en formato png.
    circle.save(os.path.join(args.output,"torta.png"),format="png")
   
     # Condicionamos, si vamos a obtener los resultados en .json entonces... RECORDAR QUE SIEMPRE ESTÁ EN TRUE.
    if file_for_results == True:
        # Guardamos nuestro archivo de JSON en un archivo de json llamado colors.json.
        with open(os.path.join(args.output,"colors"+json_extension), 'w') as outfile:
            json.dump(sorted_all_colors, outfile)
   
    # Al finalizar todo el proceso, imprimimos, analizado.
    print("Analizado correctamente. Verificar la ruta de salida.")
#logic_images()


def main():
    parser = argparse.ArgumentParser()
    parser.add_argument('-o','--output', help='Output of the colors.')
    parser.add_argument('-f','--file', help='Image to Scan colors.')
    args = parser.parse_args()
    # -f -> La imagen de la cual queremos obtener los colores.
    # -o -> Directorio para obtener los colores(se obtienen separados).
    logic_images(args)



if __name__ == '__main__':
    main()



Añado que reconoce muy bien las imágenes, pero tiene un margen de error, pero bueno, ya podrán modificarlo a sus necesidades.
Les dejo una imagen de cómo funciona y además, de sus resultados.

Analizaremos la siguiente imagen de Underc0de:


Ilustración 7. Imagen de underc0de a analizar.

Lanzamos nuestro script de la siguiente forma:

Código: python
python recoignize.py -o "C:/Users/Mortal/Desktop/results_for_scan" -f "C:/Users/Mortal/Desktop/logo_underc0de.png"



Ilustración 8. Imagen analizada de underc0de.

Como podremos notar, ¡nos saca los colores que usa el logo del foro de underc0de!. Pero no es todo, abramos el archivo de colors.json:


Ilustración 9. Archivo JSON de la imagen analizada.


Ilustración 10. Torta con los respectivos colores de la imagen.


Como vemos, funciona todo perfecto. Se puede calibrar más, de hecho, tengo aún otra versión que la estoy modificando para que sea un poco más potente. Espero que hayan podido observar el potencial de Python y de sus ventajas que nos brinda.

Cualquier comentario/duda/sugerencia será bien recibid@.

Un saludo.
[/font]
Become the change you seek in the world. -Gandhi.


Genial. Aún no se como hay gente que diga que un lenguaje de alto nivel es malo
Python es como una navaja suiza, con una amplia libreria standard para todo tipo de necesidades, y sin contar las librerias third-party que se encuentran en PyPI
Con Python puedes hacer de todo.
Saludos :)


Hola No tienes permitido ver los links. Registrarse o Entrar a mi cuenta

Realmente las limitaciones suelen ser muy pocas, de resto, es muy potente. Muchos de los que dicen esto o aquello, no poseen argumentos válidos.

Un saludo.
Become the change you seek in the world. -Gandhi.


Muy buen post brother, me alegra ver más gente trabajando con Python e imágenes, el potencial es enorme.

Tu post me dio una idea para una aplicación.

Saludos! y buen trabajo.







No tienes permitido ver los links. Registrarse o Entrar a mi cuenta


Hola No tienes permitido ver los links. Registrarse o Entrar a mi cuenta

Muchas gracias y sí, la verdad trabajar con imágenes es muy chévere. Python nos ofrece esa flexibilidad y bueno, no nos queda de otra que aprovecharla.

Un saludo y gracias por tu comentario ;D.
Become the change you seek in the world. -Gandhi.



Diciembre 22, 2017, 02:21:45 AM #6 Ultima modificación: Diciembre 26, 2017, 07:33:18 AM por Mortal_Poison
Hi No tienes permitido ver los links. Registrarse o Entrar a mi cuenta

U welcome :) and many thanks.

Greetings.
Become the change you seek in the world. -Gandhi.