Previamente se realizó la implementación de un mosaico usando las herramientas de p5.js, si bien a nivel visual los resultados fueron bastantes sorprendentes, a nivel de eficiencia fueron bastantes deficientes, uno de los principales problemas fue el cambio de resolución lo cual resulto ineficiente y molesto para el usuario. Además, la implementación que usó un video fue realmente decepcionante y se decidió eliminar ya que afectaba el desempeño del proyecto en general.
A continuacion se muestran algunos fragmentos de código que permitirán comprender los detalles de la solucion planteada.
El siguiente fragmento de código permite obtener la distancia delta entre 2 colores, maneja el mismo concepto implemento anteriormente en JS
1linkfloat calculateDeltaE(float r1, float g1, float b1,2link float r2, float g2, float b2) {3link float delta = (((r2-r1)*(r2-r1)) + ((g2-g1)*(g2-g1)) + ((b2-b1)*(b2-b1)));4link return delta;5link}
El codigo de P5.JS es el siguiente:
1linklet image;2linklet video;3linklet om;4linklet mosaic;5linklet resolution;6linklet video_on;7linklet om_on;8link9linkfunction preload() {10link11link image = loadImage('mosaic/duck.png');12link video = createVideo(['mosaic/duck.webm']);13link om = loadImage('mosaic/html_colors.png');14link mosaic = loadShader('mosaic/shader.vert','photomosaic.frag');15link video.hide();16link}17link18linkfunction setup() {19link console.time("Mosaic: Hardware")20link createCanvas(900, 506, WEBGL);21link textureMode(NORMAL);22link noStroke();23link shader(mosaic);24link25link mosaic.setUniform('om', om);26link27link28link om_on = createCheckbox('Mosaico', false);29link om_on.changed(() => mosaic.setUniform('om_on', om_on.checked()));30link om_on.position(10, 10);31link32link video_on = createCheckbox('Video', false);33link video_on.changed(() => {34link if (video_on.checked()) {35link mosaic.setUniform('img', video);36link video.loop();37link } else {38link mosaic.setUniform('img', image);39link }40link });41link video_on.position(10, 30);42link43link44link mosaic.setUniform('img', image);45link resolution = createSlider(1, 200, 30, 1);46link resolution.position(10, 50);47link resolution.style('width', '80px');48link resolution.input(()49link => mosaic.setUniform('resolution', resolution.value()));50link mosaic.setUniform('resolution', resolution.value());51link52link console.log({ resolution })53link console.timeEnd("Mosaic: Hardware")54link}55link56link57link58linkfunction draw() {59link60link background(33);61link cover(true);62link63link}64link65link66link67linkfunction cover(texture = false) {68link beginShape();69link if (texture) {70link vertex(-width / 2, -height / 2, 0, 0, 0);71link vertex(width / 2, -height / 2, 0, 1, 0);72link vertex(width / 2, height / 2, 0, 1, 1);73link vertex(-width / 2, height / 2, 0, 0, 1);74link }75link else {76link vertex(-width / 2, -height / 2, 0);77link vertex(width / 2, -height / 2, 0);78link vertex(width / 2, height / 2, 0);79link vertex(-width / 2, height / 2, 0);80link }81link endShape(CLOSE);82link}83link
El siguiente fragmento de código es el fragment diseñado para la implementacion del mosaico
1linkprecision mediump float;2link3link4link uniform sampler2D img;5link6link uniform sampler2D om;7link8link9link10link uniform bool om_on;11link uniform float resolution;12link13link14link varying vec4 vVertexColor;15link varying vec2 vTexCoord;16link17link float calculateDeltaE(float r1, float g1, float b1,18link float r2, float g2, float b2);19link20link void main() {21link22link vec2 omCoord = vTexCoord * resolution;23link vec2 texCoord = floor(omCoord);24link25link omCoord = omCoord - texCoord;26link texCoord = texCoord * (vec2(1.0) / vec2(resolution));27link28link29link vec4 imgTexel = texture2D(img, texCoord);30link31link if(om_on) {32link vec2 coords = vec2(texCoord.x, texCoord.y);33link vec4 actualPixel = texture2D(img, coords);34link float r = actualPixel.r;35link float g = actualPixel.g;36link float b = actualPixel.b;37link38link float min = calculateDeltaE(0.0,0.0,0.0,r,g,b);39link vec4 omTexel = texture2D(om,40link (omCoord*(vec2(0.04, 0.1666)))+ vec2(0.0, 0.0) );41link42link if(min > calculateDeltaE( 0.0, 0.0, 0.0, r, g, b)){43link min = calculateDeltaE( 0.0, 0.0, 0.0, r, g, b);44link omTexel = texture2D(om,45link (omCoord*(vec2(0.04, 0.16666))) + vec2( 0.0, 0) );46link }47link48link49link if(min > calculateDeltaE( 0.0, 0.0, 0.5019607843137255, r, g, b)){50link min = calculateDeltaE( 0.0, 0.0, 0.5019607843137255, r, g, b);51link omTexel = texture2D(om,52link (omCoord*(vec2(0.04, 0.16666))) + vec2( 0.04, 0) );53link }54link55link56link if(min > calculateDeltaE( 0.0, 0.0, 0.5450980392156862, r, g, b)){57link min = calculateDeltaE( 0.0, 0.0, 0.5450980392156862, r, g, b);58link omTexel = texture2D(om,59link (omCoord*(vec2(0.04, 0.16666))) + vec2( 0.08, 0) );60link }61link62link63link ...64link ...65link ...66link ...67link ...68link69link70link if(min > calculateDeltaE( 1.0, 1.0, 0.9411764705882353, r, g, b)){71link min = calculateDeltaE( 1.0, 1.0, 0.9411764705882353, r, g, b);72link omTexel = texture2D(om,73link (omCoord*(vec2(0.04, 0.16666))) + vec2( 0.44, 0.8333) );74link }75link76link77link if(min > calculateDeltaE( 1.0, 1.0, 1.0, r, g, b)){78link min = calculateDeltaE( 1.0, 1.0, 1.0, r, g, b);79link omTexel = texture2D(om,80link (omCoord*(vec2(0.04, 0.16666))) + vec2( 0.48, 0.8333) );81link }82link83link84link85link gl_FragColor = omTexel;86link87link88link }89link else {90link gl_FragColor = imgTexel;91link }92link }93link94link95link float calculateDeltaE(float r1, float g1, float b1,96link float r2, float g2, float b2) {97link float delta = (((r2-r1)*(r2-r1)) + ((g2-g1)*(g2-g1)) + ((b2-b1)*(b2-b1)));98link return delta;99link }100link}
El banco de imagenes que permitio realizar el mosaico es el siguiente:
Por fines practicos el banco de imagenes fue unido en una simple imagen, la cual fue usada como textura.
El resultado de todo el proceso es el siguiente
Herramienta usada para creacion del sprite LINK