Creación de un foto-mosaico de una imagen determinada a partir de una lista de imagenes definida mediante la utilización de shaders
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 o incluso millones de imágenes.
Para la realizacion de este algoritmo de computacion visual se hizo uso de shaders desarrollados en GLSL el cual es un lenguaje de alto nivel de shader con una sintaxis basada en el lenguaje de programación C y diseñado específicamente para su uso dentro del entorno de OpenGL.
El algoritmo parte de un dataset de imagenes concatenadas de manera horizontal en un solo archivo .JPG ordenadas por su nivel de brillo percibido el cual fue calculado utilizando la libreria pillow en python.
1linkfrom PIL import Image
2linkfrom PIL import ImageStat
3linkimport math
4link
5linkdef brightness( im_file ):
6link im = Image.open(im_file)
7link stat = ImageStat.Stat(im)
8link r,g,b = stat.rms
9link return math.sqrt(0.241*(r**2) + 0.691*(g**2) + 0.068*(b**2))
Posteriormente la imagen original se recrea apartir de texturas 2D cuadradas de un color plano dependiendo del valor de la resolucion, el cual puede ser modificado interactivamente (ej. si la resolucion tiene un valor de 50 la imagen se divide en filas de 50 texturas 2D cuadradas).
Luego, se realiza el calculo del brillo de dichas texturas 2D y se encuentra la posicion aproximada dentro del dataset concatenado de las imagenes que conformarán el mosaico correspondiendo con dicho nivel de brillo para extraer la imagen especifica que ira ubicada en esa seccion del mosaico. Este proceso se repite con todos los cuadrados generados previamente hasta construir la imagen completa.
*NOTA: la resolucion de la imagen, que se refiere al numero de imagenes por fila que conforman el mosaico, puede ser modificada con el selector en la esquina superior izquierda
1linklet mosaico;
2linklet mosaicDatasetImg;
3linklet originalImage;
4linklet showMosaico;
5linklet imagesMainPath = "/vc/docs/sketches/assets/image_mosaic/";
6linklet imgsPerRow = 100;
7linkconst numImagenes = 70;
8link
9linkfunction preload() {
10link originalImage = loadImage(imagesMainPath + "art-shaders.jpg");
11link mosaicDatasetImg = loadImage(imagesMainPath + "mosaicDataset.jpg");
12link mosaico = loadShader(
13link "/vc/docs/sketches/scripts/hardware/shader.vert",
14link "/vc/docs/sketches/scripts/hardware/photomosaic.frag"
15link );
16link}
17link
18linkfunction setup() {
19link setupSelect();
20link setupMosaic();
21link}
22link
23linkfunction setupSelect(){
24link sel = createSelect();
25link sel.position(15, 15);
26link sel.option(10);
27link sel.option(50);
28link sel.option(100);
29link sel.option(150);
30link sel.option(200);
31link sel.option(500);
32link sel.option(2000);
33link sel.option(10000);
34link sel.option(100000);
35link sel.option(1000000);
36link sel.option(10000000);
37link sel.option(100000000);
38link sel.selected(imgsPerRow);
39link sel.changed(mySelectEvent);
40link}
41link
42linkfunction mySelectEvent() {
43link imgsPerRow = sel.value();
44link mosaico.setUniform("imgsPerRow", imgsPerRow);
45link}
46link
47linkfunction setupMosaic() {
48link createCanvas(718, 900, WEBGL);
49link textureMode(NORMAL);
50link noStroke();
51link shader(mosaico);
52link mosaico.setUniform("originalImage", originalImage);
53link mosaico.setUniform("imgsPerRow", imgsPerRow);
54link mosaico.setUniform("numImagenes", numImagenes);
55link showMosaico = true;
56link mosaico.setUniform("showMosaico", showMosaico);
57link mosaico.setUniform("mosaicDatasetImg", mosaicDatasetImg);
58link}
59link
60linkfunction draw() {
61link background(33);
62link cover();
63link}
64link
65linkfunction cover() {
66link beginShape();
67link vertex(-width / 2, -height / 2, 0, 0, 0);
68link vertex(width / 2, -height / 2, 0, 1, 0);
69link vertex(width / 2, height / 2, 0, 1, 1);
70link vertex(-width / 2, height / 2, 0, 0, 1);
71link endShape(CLOSE);
72link}
73link
74linkfunction keyPressed() {
75link if (key === "1") {
76link showMosaico = !showMosaico;
77link mosaico.setUniform("showMosaico", showMosaico);
78link
79link if (showMosaico) {
80link setupSelect();
81link } else {
82link sel.remove();
83link }
84link }
85link}
1linkprecision mediump float;
2linkuniform sampler2D originalImage;
3linkuniform sampler2D mosaicDatasetImg;
4linkuniform bool showMosaico;
5linkuniform float imgsPerRow;
6linkuniform float numImagenes;
7link
8link// interpolated texcoord (same name and type as in vertex shader)
9linkvarying vec2 vTexCoord;
10linkvarying vec4 vVertexColor;
11link
12linkvoid main() {
13link vec2 mosaicDatasetImgCoord = vTexCoord*imgsPerRow;
14link vec2 originalImageCoord = floor(mosaicDatasetImgCoord);
15link mosaicDatasetImgCoord = mosaicDatasetImgCoord-originalImageCoord;
16link originalImageCoord = originalImageCoord*vec2(1.0)/vec2(imgsPerRow);
17link vec4 col = texture2D(originalImage,originalImageCoord);
18link float brightnessValue = dot(col.xyz, vec3(0.35, 0.35, 0.35));
19link
20link float temp = brightnessValue*(numImagenes);
21link float level = floor(temp);
22link
23link float scalingfactor = 1.0/numImagenes;
24link
25link float y0 = 0.0;
26link float x0 = (level-(numImagenes*(floor(level/numImagenes))))*scalingfactor;
27link
28link vec2 finalCord = (mosaicDatasetImgCoord*vec2(1.0)/vec2(numImagenes,1))+vec2(x0,y0);
29link vec4 finalColor = texture2D(mosaicDatasetImg,finalCord);
30link
31link gl_FragColor = showMosaico?finalColor:col;
32link}