Introducir el análisis de imágenes/video al implementar: Conversión de la imagen a un foto-mosaico.
En el campo de las imágenes y la fotografía, un fotomosaico es una imagen usualmente una fotografía que ha sido dividida en secciones rectangulares (usualmente del mismo tamaño), tal como es compuesto un mosaico tradicional, con la característica de que cada elemento del mosaico es reemplazado por otra fotografía con colores promedios apropiados al elemento de la imagen original. Cuando es vista en detalle, los píxeles individuales se ven como la imagen principal, sin embargo al verla como un todo, es posible apreciar que la imagen está compuesta por cientos de miles de imágenes.
El CIE Lab* (CIELAB) es el modelo cromático usado normalmente para describir todos los colores que puede percibir el ojo humano. Fue desarrollado específicamente con este propósito por la Commission Internationale d'Eclairage (Comisión Internacional de la Iluminación), razón por la cual se abrevia CIE. Los asteriscos () que siguen a cada letra forman parte del nombre, ya que representan L, a* y b*, de L, a y b.
Los tres parámetros en el modelo representan la luminosidad de color (L, L=0 rendimientos negro y L=100 indica blanca), su posición entre rojo y verde (a, valores negativos indican verde mientras valores positivos indican rojo) y su posición entre amarillo y azul (b*, valores negativos indican azul y valores positivos indican amarillo).
El proceso de creación del mosaico está dividido en 2 fases, la primera es una fase previa de creación del dataset de imágenes utilizadas en el mosaico y la segunda es el proceso de reemplazo de los píxeles de la imagen por otras imágenes que aproximen el color de cada píxel.
Para la primera parte se utilizó un script en Python para descargar imágenes aleatorias de una API publica llamada Pixabay en la cual se permite utilizar un filtro por el color de la imagen, el script hace una serie de peticiones a la API (una por cada color) solicitando un numero determinado de imágenes del color deseado, luego toma las imágenes y les cambia el tamaño para que todas sean imágenes cuadradas, por ultimo se toma el color dominante de cada imagen y se concatenan verticalmente formando una sola imagen diccionario, y para indexar el diccionario se crea simultaneamente una imagen en la que cada píxel es el color dominante de la imagen correspondiente (Esto se hace para poder acceder las imágenes a partir de su color dominante).
La segunda parte del proceso se hace en tiempo real, se cargan la imagen diccionario, la imagen que indexa al diccionario y la imagen que se va a transformar y se envían como variables uniformes al fragment shader, este se encarga de que por cada sección (definida por la resolución) de la imagen original se reemplace por la correspondiente imagen del diccionario, teniendo como color representativo el primer píxel de la región (píxel de la esquina superior izquierda), se busca en el index de colores el color mas cercano utilizando el espacio de color Lab y a partir de este se busca la imagen correspondiente en el diccionario de imágenes.
1linklet mosaicImages
2linklet indexImages
3linklet theShader
4linklet img
5link
6linkfunction preload(){
7link theShader = loadShader('/vc/docs/sketches/shader.vert', '/vc/docs/sketches/mosaicShader.frag')
8link img = loadImage('/vc/docs/sketches/lenna.png')
9link mosaicImages = loadImage('/vc/docs/sketches/mosaicImages.jpg')
10link indexImages = loadImage('/vc/docs/sketches/indexImage.jpg')
11link}
12link
13linkfunction setup(){
14link createCanvas(512, 512,WEBGL);
15link noStroke();
16link textureMode(NORMAL);
17link shader(theShader);
18link
19link theShader.setUniform('image', img)
20link theShader.setUniform('mosaicImages', mosaicImages)
21link theShader.setUniform('indexImages', indexImages)
22link theShader.setUniform('resolution', 20)
23link
24link
25link slider = createSlider(3, 512, 100, 2);
26link slider.position(150, 552);
27link slider.style('width', '40%');
28link slider.input(() => {
29link theShader.setUniform('resolution', slider.value())
30link redraw()
31link })
32link let div = createDiv('Resolution');
33link div.style('font-size', '18px');
34link div.position(220, 532);
35link}
36link
37linkfunction draw() {
38link background(0);
39link beginShape();
40link vertex(-width/2, -height/2,0,0,0);
41link vertex(width/2, -height/2,0,1,0);
42link vertex(width/2, height/2,0,1,1);
43link vertex(-width/2, height/2,0,0,1);
44link endShape(CLOSE);
45link
46link noLoop();
47link}
1linkimport json
2linkimport requests
3linkimport shutil
4linkfrom os import listdir
5linkfrom os.path import isfile, join
6linkfrom colorthief import ColorThief
7linkfrom tqdm import tqdm
8linkimport cv2
9linkimport tempfile
10linkimport numpy as np
11link
12linkdef get_files(dir):
13link return [f for f in listdir(dir) if isfile(join(dir, f))]
14link
15linkdef resize_images(folder, destination_folder):
16link img_list = [(destination_folder + file, cv2.imread(folder + file)) for file in get_files(folder)]
17link
18link minh = min([img.shape[0] for img_name, img in img_list])
19link minw = min([img.shape[1] for img_name, img in img_list])
20link dim = min(minh, minw)
21link
22link for img_name, img in tqdm(img_list):
23link resized = cv2.resize(img, (dim, dim), interpolation = cv2.INTER_AREA)
24link cv2.imwrite(img_name, resized)
25link
26linkdef concat_images(folder):
27link img_list = []
28link file_list = get_files(folder)
29link indexImage = np.zeros((len(file_list) * 44, 44, 3), np.uint8)
30link for i, file in tqdm(enumerate(file_list)):
31link color_thief = ColorThief(folder + file)
32link r,g,b = color_thief.get_color(quality=1)
33link indexImage[i*44: (i+1)*44, :, 0] = b
34link indexImage[i*44: (i+1)*44, :, 1] = g
35link indexImage[i*44: (i+1)*44, :, 2] = r
36link img = cv2.imread(folder + file)
37link img_list.append(img)
38link
39link result_img = cv2.vconcat(img_list)
40link cv2.imwrite('result.jpg',result_img)
41link cv2.imwrite('indexImage.jpg', indexImage)
42link
43linkdef download_images(folder):
44link key = "20977986-3000fea631f919184c7949341"
45link image_type = "photo"
46link per_page = 200
47link page = 1
48link orientation = "horizontal"
49link base_url = f'https://pixabay.com/api/?key={key}&image_type={image_type}&per_page={per_page}&page={page}&orientation={orientation}&> > colors='
50link
51link colors = ["grayscale", "transparent", "red", "orange", "yellow", "green", "turquoise", "blue", "lilac", "pink", "white", "gray", > > "black", "brown" ]
52link for color in colors:
53link URL = base_url + color
54link res = requests.get(URL)
55link res_data = json.loads(res.text)
56link for image in tqdm(res_data['hits']):
57link # save the image
58link url = image['webformatURL']
59link name = folder + str(image['id']) + '.jpg'
60link r = requests.get(url, stream=True)
61link if r.status_code == 200:
62link with open(name, 'wb') as f:
63link r.raw.decode_content = True
64link shutil.copyfileobj(r.raw, f)
65link
66link
67linkdd = tempfile.TemporaryDirectory()
68linkrd = tempfile.TemporaryDirectory()
69linkfd = tempfile.TemporaryDirectory()
70link
71linkdownload_images(dd.name + '/')
72linkresize_images(dd.name + '/', rd.name + '/')
73linkconcat_images(rd.name + '/')
74link
75linkrd.cleanup()
76linkfd.cleanup()
1linkprecision highp float;
2link
3linkuniform sampler2D image;
4linkuniform sampler2D mosaicImages;
5linkuniform sampler2D indexImages;
6linkuniform float resolution;
7link
8linkvarying vec2 vTexCoord;
9link
10linkvec3 rgb2xyz( vec3 c ) {
11link vec3 tmp;
12link tmp.x = ( c.r > 0.04045 ) ? pow( ( c.r + 0.055 ) / 1.055, 2.4 ) : c.r / 12.92;
13link tmp.y = ( c.g > 0.04045 ) ? pow( ( c.g + 0.055 ) / 1.055, 2.4 ) : c.g / 12.92,
14link tmp.z = ( c.b > 0.04045 ) ? pow( ( c.b + 0.055 ) / 1.055, 2.4 ) : c.b / 12.92;
15link const mat3 mat = mat3(
16link 0.4124, 0.3576, 0.1805,
17link 0.2126, 0.7152, 0.0722,
18link 0.0193, 0.1192, 0.9505
19link );
20link return 100.0 * tmp * mat;
21link}
22link
23linkvec3 xyz2lab( vec3 c ) {
24link vec3 n = c / vec3(95.047, 100, 108.883);
25link vec3 v;
26link v.x = ( n.x > 0.008856 ) ? pow( n.x, 1.0 / 3.0 ) : ( 7.787 * n.x ) + ( 16.0 / 116.0 );
27link v.y = ( n.y > 0.008856 ) ? pow( n.y, 1.0 / 3.0 ) : ( 7.787 * n.y ) + ( 16.0 / 116.0 );
28link v.z = ( n.z > 0.008856 ) ? pow( n.z, 1.0 / 3.0 ) : ( 7.787 * n.z ) + ( 16.0 / 116.0 );
29link return vec3(( 116.0 * v.y ) - 16.0, 500.0 * ( v.x - v.y ), 200.0 * ( v.y - v.z ));
30link}
31link
32linkvec3 rgb2lab( vec3 c ) {
33link vec3 lab = xyz2lab( rgb2xyz( c ) );
34link return vec3( lab.x / 100.0, 0.5 + 0.5 * ( lab.y / 127.0 ), 0.5 + 0.5 * ( lab.z / 127.0 ));
35link}
36link
37linkvec3 lab2xyz( vec3 c ) {
38link float fy = ( c.x + 16.0 ) / 116.0;
39link float fx = c.y / 500.0 + fy;
40link float fz = fy - c.z / 200.0;
41link return vec3(
42link 95.047 * (( fx > 0.206897 ) ? fx * fx * fx : ( fx - 16.0 / 116.0 ) / 7.787),
43link 100.000 * (( fy > 0.206897 ) ? fy * fy * fy : ( fy - 16.0 / 116.0 ) / 7.787),
44link 108.883 * (( fz > 0.206897 ) ? fz * fz * fz : ( fz - 16.0 / 116.0 ) / 7.787)
45link );
46link}
47link
48linkvec3 xyz2rgb( vec3 c ) {
49link const mat3 mat = mat3(
50link 3.2406, -1.5372, -0.4986,
51link -0.9689, 1.8758, 0.0415,
52link 0.0557, -0.2040, 1.0570
53link );
54link vec3 v = (c / vec3(100.0)) * mat;
55link vec3 r;
56link r.x = ( v.r > 0.0031308 ) ? (( 1.055 * pow( v.r, ( 1.0 / 2.4 ))) - 0.055 ) : 12.92 * v.r;
57link r.y = ( v.g > 0.0031308 ) ? (( 1.055 * pow( v.g, ( 1.0 / 2.4 ))) - 0.055 ) : 12.92 * v.g;
58link r.z = ( v.b > 0.0031308 ) ? (( 1.055 * pow( v.b, ( 1.0 / 2.4 ))) - 0.055 ) : 12.92 * v.b;
59link return r;
60link}
61link
62linkvec3 lab2rgb( vec3 c ) {
63link return xyz2rgb( lab2xyz( vec3(100.0 * c.x, 2.0 * 127.0 * (c.y - 0.5), 2.0 * 127.0 * (c.z - 0.5)) ) );
64link}
65link
66linkint getClosestColor(vec4 indexColor){
67link int closest = 0;
68link float minDiff = distance(vec4(1.0), vec4(0.0));
69link
70link vec3 indexColorLab = rgb2lab(vec3(indexColor.r, indexColor.g, indexColor.b));
71link
72link for (int i = 0; i < 276; i++) {
73link vec4 color = texture2D(indexImages, vec2(float(i) + 0.5) / vec2(1.0, 276.0));
74link vec3 colorLab = rgb2lab(vec3(color.r, color.g, color.b));
75link float diff = distance(indexColorLab, colorLab);
76link if(diff <= minDiff) {
77link minDiff = diff;
78link closest = i;
79link }
80link }
81link
82link return closest;
83link}
84link
85link
86linkvoid main() {
87link
88link vec2 symbolCoord = vTexCoord * resolution;
89link vec2 imageCoord = floor(symbolCoord);
90link
91link symbolCoord = (symbolCoord - imageCoord) / vec2(1.0, 276.0);
92link
93link imageCoord = imageCoord / vec2(resolution);
94link
95link vec4 indexColor = texture2D(image, imageCoord);
96link
97link int closest = getClosestColor(indexColor);
98link
99link float imageHeight = (float(closest)) / 276.0;
100link
101link gl_FragColor = texture2D(mosaicImages, symbolCoord + vec2(0.0, imageHeight));
102link}
1linkprecision highp float;
2link
3linkattribute vec3 aPosition;
4link
5linkattribute vec2 aTexCoord;
6link
7linkattribute vec4 aVertexColor;
8link
9linkuniform mat4 uProjectionMatrix;
10link
11linkuniform mat4 uModelViewMatrix;
12link
13linkvarying vec4 vVertexColor;
14link
15linkvarying vec2 vTexCoord;
16link
17linkvoid main() {
18link vVertexColor = aVertexColor;
19link vTexCoord = aTexCoord;
20link gl_Position = uProjectionMatrix * uModelViewMatrix * vec4(aPosition, 1.0);
21link}