# Miguel Uribe Pérez-Luque, M3, Práctica 3
from imagen import *
from typing import Callable,Union
# Importamos el módulo imagen y los tipos Callable y Union del módulo
typing.
# Las siguientes funciones vienen dadas en la práctica
def dist1(color1:Color,color2:Color):
result = 0
for i in range(len(color1)):
result = result + (color1[i]-color2[i])**2
return result
def dist2(color1:Color,color2:Color)->int:
result = 0
for i in range(len(color1)):
result = max(result, abs(color1[i]-color2[i]))
return result
def dist3(color1:Color,color2:Color)->int:
result = 0
cofs = [2, 4, 3]
for i in range(len(color1)):
result = result + cofs[i]*(color1[i]-color2[i])**2
return result
def muestra(img:ImagenColor,k:int,l:int)->list[Color]:
width,heigth = len(img[0]), len(img)
result = []
for y in range(0,heigth,heigth//k):
for x in range(0,width,width//l):
result.append(img[y][x])
return result
# Además, nos ayudaremos de una función para optimizar nuestras
funciones: min_index
def min_index (color: Color, color_list: list[Color], distance:
Callable[[Color,Color],int]) -> int:
# Definimos la función min_index que toma:
# - un color base de tipo Color
# - una lista de colores color_list de tipo list[Color]
# - una función distance de tipo Callable (que mide la distancia
entre dos colores)
# La función devuelve el índice de color_list cuya distancia al
color base es la mínima de todas ellas
# El código es el siguiente:
# Creamos las dos variables a devolver, min_dist e index
# Damos como valor a min_dist como la distancia al primer color de
la lista, y el índice comienza siendo 0
min_dist = distance(color,color_list[0])
index = 0
# Vemos en un bucle sencillo cuál es el valor mínimo de las
distancias
# En caso de ser una distancia menor a la inicial, sustituimos el
valor de min_dist y el índice
# Como ya hemos asignado el primer elemento de color_list a una
distancia, no lo tenemos en cuenta (empezamos desde el índice 1)
for i in range (len(color_list)-1):
if distance(color,color_list[i+1]) < min_dist:
min_dist = distance(color,color_list[i+1])
index = i+1
# Una vez terminado el bucle, tenemos que min_dist es la distancia
mínima y que index es el índice que buscamos
return index
# Ahora pasamos a las funciones de la práctica
def img_subst (img: ImagenColor, color_list: list[Color], distance:
Callable[[Color,Color],int]) -> ImagenColor:
# Definimos la función img_subst que toma:
# - una imagen img de tipo ImagenColor
# - una lista de colores color_list de tipo list[Color]
# - una función distance de tipo Callable (que mide la distancia
entre dos colores)
# La función devuelve una imagen aproximada de img acorde a los
colores de color_list, de tipo ImagenColor.
# El código es el siguiente:
# Creamos new_img, que contendrá la nueva imagen
new_img = []
# Analizamos cada fila de colores de la imagen original para crear
nuestra nueva imagen
for row in img:
# Por cada fila creamos una lista new_row
new_row = []
# Recorremos cada color en la fila original y vemos qué color
de la paleta es el más cercano, para crear las filas nuevas
for color in row:
# Añadimos el color más cercano de nuestra paleta a
new_row, ayudándonos de min_index
new_row.append(color_list[min_index(color, color_list,
distance)])
# Añadimos la fila nueva creada a new_img
new_img.append(new_row)
# Así, finalmente queda una new_img de tipo ImagenColor, que es
una imagen con los colores aproximados a los de color_list
# La función nos devuelve la imagen nueva new_img
return new_img
# Pasamos ahora a la función auxiliar de la que nos ayudamos para
colores_mas_representativos
def new_list (img: ImagenColor, color_list: list[Color], distance:
Callable[[Color,Color],int]) -> list[Color]:
# Definimos la función new_list que toma:
# - una imagen img de tipo ImagenColor
# - una lista de colores color_list de tipo list[Color]
# - una función distance de tipo Callable (que mide la distancia
entre dos colores)
# La función devuelve una nueva lista de colores más aproximada a
los colores de img, de tipo list[Color]
# El código es el siguiente:
# Creamos la lista vacía new_colors, que será la lista que
acabamos de mencionar
new_colors = []
# Creamos la lista close_colors
# Esta lista va a almacenar los grupos de colores de la imagen
similares a cada color de color_list y el número de colores en cada
uno
# Los primeros tres 0 de cada lista corresponderán a los valores
RGB y el último al número de colores añadidos al grupo
close_colors = [[0,0,0,0] for color in color_list]
# Para optimizar, creamos una variable que tenga la longitud de
color_list
len_color_list = len(color_list)
# Metemos cada color de la imagen img en el grupo correspondiente,
acorde a la cercanía con los colores de color_list
for img_row in img:
for img_color in img_row:
# Sumamos el color (valores en RGB) img_color al grupo de
close_colors con índice igual al de la distancia mínima (el color de
color_list más cercano)
# Nos ayudamos de min_index para meter el color de la
imagen en el grupo cercano
index_close_color = min_index(img_color, color_list,
distance)
# Creamos el bucle para sumar los valores RGB
for i in range (3):
close_colors[index_close_color][i] += img_color[i]
close_colors[index_close_color][3] += 1
# Una vez clasificados todos los colores por grupos, añadimos a
new_colors un color nuevo por cada grupo (por cada color de
color_list)
for i in range (len_color_list):
# Creamos nuestro color new_color al que le vamos a sumar la
media de colores por cada grupo
new_color = [0,0,0]
# Creamos el bucle para sumar las medias
for j in range (3):
# Por cada valor RGB sumamos la media de los colores
cercanos del grupo que corresponda con división entera
# Si no han habido colores en ese grupo, será división por
cero, por lo que usamos try/except
try:
new_color[j] += round(close_colors[i]
[j]/close_colors[i][3])
except:
# Si no hay colores en el grupo, dejamos el color como
estaba, pues no se tendrá en cuenta en img_subst
# (recordemos que esa función tiene en cuenta los
colores cercanos, pero color_list[i] no es cercano a ninguno de img)
new_color[j] += color_list[i]
# Añadimos el color a la lista que queremos obtener
new_colors.append(new_color)
# La función nos devuelve la lista que estábamos buscando
return new_colors
# Ahora, la última función de la práctica (la más importante)
def colores_mas_representativos (img: ImagenColor,color_list:
list[Color], distance: Callable[[Color,Color],int]) -> list[Color]:
# Definimos la función colores_mas_representativos que toma:
# - una imagen img de tipo ImagenColor
# - una lista de colores color_list de tipo list[Color]
# - una función distance de tipo Callable (que mide la distancia
entre dos colores)
# La función devuelve una lista de los colores más representativos
de img, aproximados con color_list, de tipo list[Color]
# Consiste básicamente en repetir new_list hasta que las listas de
colores creadas sean prácticamente iguales (recordemos que new_list
tiende a estabilizarse)
# El código es el siguiente:
# Creamos una nueva lista new_colors que surge de aplicar new_list
a color_list (con img y distance)
# Esta lista es la que vamos a comparar con color_list para ver
cuándo detenemos las repeticiones de new_list
new_colors = new_list(img, color_list, distance)
# Creamos un contador de iteraciones para verificar que son las
mismas que indica la práctica
iteraciones = 0
# Creamos un bucle para que se repita el proceso hasta que
color_list y new_colors sean iguales
while True:
# Sumamos uno al contador de iteraciones
iteraciones += 1
# Cambiamos color_list por una copia de new_colors
color_list = new_colors.copy()
# Repetimos new_list con new_colors
new_colors = new_list(img, new_colors, distance)
# Una vez que color_list sea igual a new_colors, detenemos el
bucle
if color_list == new_colors:
break
# Si no ha terminado el bucle, se repetirá new_list
# Una vez acabado el bucle, la función nos devuelve new_colors,
que es la lista de colores que buscábamos
# También mostramos el número de iteraciones
print(f" - Obtenidos los colores más representativos en
{iteraciones} iteraciones")
return new_colors
# Fin de la práctica
#______________________________________________________________
# Ahora realizamos las pruebas de la práctica:
# La ruta del archivo depende del ordenador
img = read("C:/beach.jpg")
show(img)
# Realizamos las pruebas de img_subst con muestra(img,4,2)
colors_list = muestra(img,4,2)
conv = img_subst(img,colors_list,dist1)
show(conv)
conv1 = img_subst(img,colors_list,dist2)
show(conv1)
conv2 = img_subst(img,colors_list,dist3)
show(conv2)
# Ahora probamos las de colores_mas_representativos, primero con
muestra(img,5,3)
colors_list = muestra(img,5,3)
l1 = colores_mas_representativos(img,colors_list,dist1)
show(img_subst(img,l1,dist1))
- Obtenidos los colores más representativos en 36 iteraciones
l2 = colores_mas_representativos(img,colors_list,dist2)
show(img_subst(img,l2,dist2))
- Obtenidos los colores más representativos en 52 iteraciones
l3 = colores_mas_representativos(img,colors_list,dist3)
show(img_subst(img,l3,dist3))
- Obtenidos los colores más representativos en 51 iteraciones
# Ahora con colores primarios
colors_list1 = [[255,0,0],[0,255,0],[0,0,255]]
l4 = colores_mas_representativos(img,colors_list1,dist1)
show(img_subst(img,l4,dist1))
- Obtenidos los colores más representativos en 14 iteraciones
l5 = colores_mas_representativos(img,colors_list1,dist2)
show(img_subst(img,l5,dist2))
- Obtenidos los colores más representativos en 13 iteraciones
l6 = colores_mas_representativos(img,colors_list1,dist3)
show(img_subst(img,l6,dist3))
- Obtenidos los colores más representativos en 10 iteraciones
# Por último con muestra(img,2,2)
colors_list2 = muestra(img,2,2)
l7 = colores_mas_representativos(img,colors_list2,dist1)
show(img_subst(img,l7,dist1))
- Obtenidos los colores más representativos en 7 iteraciones
l8 = colores_mas_representativos(img,colors_list2,dist2)
show(img_subst(img,l8,dist2))
- Obtenidos los colores más representativos en 8 iteraciones
l9 = colores_mas_representativos(img,colors_list2,dist3)
show(img_subst(img,l9,dist3))
- Obtenidos los colores más representativos en 8 iteraciones