Problem Statement and Background Image and Video filters Ascii art Photo Mosaic
The process of applying a filter to an image or video can be easily done by using the shaders functionality of P5js, so we can transform videos and images in an optimal way using hardware.
In order to invert the color of an image or video using shaders we create a fragment shader that takes the image or video as a texture and change its rgb value with the formula rgb = 1 - rgb, in addition it is necessary to invert the y orientation of the texture, because the shader always receives it upside down. The vertex shader used is the general vertex shader described on "thebookofshaders" which just adjust the position of the texture. After the texture is processed it is applied to a rectangle in the scene as a texture.
1linklet inverseShader;
2linklet shaderTexture;
3link
4link
5linkfunction preload() {
6link img = loadImage('/vc/docs/sketches/lenna.png');
7link video = createVideo(['/vc/docs/sketches/fingers.mov', '/vc/docs/sketches/fingers.webm']);
8link inverseShader = loadShader('/vc/docs/sketches/inverse.vert', '/vc/docs/sketches/inverse.frag');
9link video.hide();
10link}
11link
12linkfunction setup() {
13link createCanvas(512, 256, WEBGL);
14link shaderTexture = createGraphics(256, 256, WEBGL);
15link shaderTexture.noStroke();
16link
17link video.loop();
18link noStroke();
19link}
20link
21linkfunction draw() {
22link shaderTexture.shader(inverseShader);
23link inverseShader.setUniform('tex', img);
24link texture(shaderTexture);
25link shaderTexture.rect(0,0,256,256);
26link rect(-256,-256/2.0,256,256)
27link inverseShader.setUniform('tex', video);
28link texture(shaderTexture);
29link shaderTexture.rect(0,0,256,256);
30link rect(0,-256/2.0,256,256)
31link}
1link#ifdef GL_ES
2linkprecision mediump float;
3link#endif
4link
5linkattribute vec3 aPosition;
6linkattribute vec2 aTexCoord;
7link
8linkvarying vec2 vTexCoord;
9link
10linkvoid main() {
11link vTexCoord = aTexCoord;
12link
13link vec4 positionVec4 = vec4(aPosition, 1.0);
14link positionVec4.xy = positionVec4.xy * 2.0 - 1.0;
15link
16link gl_Position = positionVec4;
17link}
1link#ifdef GL_ES
2linkprecision mediump float;
3link#endif
4link
5linkvarying vec2 vTexCoord;
6link
7linkuniform sampler2D tex;
8link
9link
10linkvoid main() {
11link vec2 uv = vTexCoord;
12link uv.y = 1.0 - uv.y;
13link
14link vec4 tex_f = texture2D(tex, uv);
15link
16link tex_f.rgb = 1.0 - tex_f.rgb;
17link
18link gl_FragColor = tex_f;
19link}
To apply the gray scale Luma on an image or video using shaders we apply the same technique of the inverse color, with the difference that we create a function called luma that transform the color of the texture using the dot product between the rgb vector and the vector (0.299, 0.587, 0.114) and then replace the color of the texture by the result of that dot product.
1linklet grayShader;
2linklet shaderTexture;
3link
4linkfunction preload() {
5link img = loadImage('/vc/docs/sketches/lenna.png');
6link video = createVideo(['/vc/docs/sketches/fingers.mov', '/vc/docs/sketches/fingers.webm']);
7link grayShader = loadShader('/vc/docs/sketches/gray.vert', '/vc/docs/sketches/gray.frag');
8link video.hide();
9link}
10link
11linkfunction setup() {
12link createCanvas(512, 256, WEBGL);
13link shaderTexture = createGraphics(256, 256, WEBGL);
14link shaderTexture.noStroke();
15link
16link video.loop();
17link noStroke();
18link}
19link
20linkfunction draw() {
21link shaderTexture.shader(grayShader);
22link grayShader.setUniform('tex', img);
23link texture(shaderTexture);
24link shaderTexture.rect(0,0,256,256);
25link rect(-256,-256/2.0,256,256)
26link grayShader.setUniform('tex', video);
27link texture(shaderTexture);
28link shaderTexture.rect(0,0,256,256);
29link rect(0,-256/2.0,256,256)
30link}
31link
1link#ifdef GL_ES
2linkprecision mediump float;
3link#endif
4link
5linkattribute vec3 aPosition;
6linkattribute vec2 aTexCoord;
7link
8linkvarying vec2 vTexCoord;
9link
10linkvoid main() {
11link vTexCoord = aTexCoord;
12link
13link vec4 positionVec4 = vec4(aPosition, 1.0);
14link positionVec4.xy = positionVec4.xy * 2.0 - 1.0;
15link
16link gl_Position = positionVec4;
17link}
1link#ifdef GL_ES
2linkprecision mediump float;
3link#endif
4link
5linkvarying vec2 vTexCoord;
6link
7linkuniform sampler2D tex;
8link
9linkfloat luma(vec3 color) {
10link return dot(color, vec3(0.299, 0.587, 0.114));
11link}
12link
13linkvoid main() {
14link vec2 uv = vTexCoord;
15link uv.y = 1.0 - uv.y;
16link
17link vec4 tex_f = texture2D(tex, uv);
18link
19link float gray = luma(tex_f.rgb);
20link
21link gl_FragColor = vec4(gray,gray,gray,1.0);
22link}
In order to apply the gray scale RGB on an image or video using shaders we apply the same technique of the inverse color, with the difference that we create a function called rgb that transform the color of the texture using the dot product between the rgb vector and the vector (1/3.0, 1/3.0, 1/3.0) and then replace the color of the texture by the result of that dot product.
1linklet grayShader;
2linklet shaderTexture;
3link
4linkfunction preload() {
5link img = loadImage('/vc/docs/sketches/lenna.png');
6link video = createVideo(['/vc/docs/sketches/fingers.mov', '/vc/docs/sketches/fingers.webm']);
7link grayShader = loadShader('/vc/docs/sketches/gray_rgb.vert', '/vc/docs/sketches/gray_rgb.frag');
8link video.hide();
9link}
10link
11linkfunction setup() {
12link createCanvas(512, 256, WEBGL);
13link shaderTexture = createGraphics(256, 256, WEBGL);
14link shaderTexture.noStroke();
15link
16link video.loop();
17link noStroke();
18link}
19link
20linkfunction draw() {
21link shaderTexture.shader(grayShader);
22link grayShader.setUniform('tex', img);
23link texture(shaderTexture);
24link shaderTexture.rect(0,0,256,256);
25link rect(-256,-256/2.0,256,256)
26link grayShader.setUniform('tex', video);
27link texture(shaderTexture);
28link shaderTexture.rect(0,0,256,256);
29link rect(0,-256/2.0,256,256)
30link}
31link
1link#ifdef GL_ES
2linkprecision mediump float;
3link#endif
4link
5linkattribute vec3 aPosition;
6linkattribute vec2 aTexCoord;
7link
8linkvarying vec2 vTexCoord;
9link
10linkvoid main() {
11link vTexCoord = aTexCoord;
12link
13link vec4 positionVec4 = vec4(aPosition, 1.0);
14link positionVec4.xy = positionVec4.xy * 2.0 - 1.0;
15link
16link gl_Position = positionVec4;
17link}
1link#ifdef GL_ES
2linkprecision mediump float;
3link#endif
4link
5linkvarying vec2 vTexCoord;
6link
7linkuniform sampler2D tex;
8link
9linkfloat rgb(vec3 color) {
10link return dot(color, vec3(1.0/3.0, 1.0/3.0, 1.0/3.0));
11link}
12link
13linkvoid main() {
14link vec2 uv = vTexCoord;
15link uv.y = 1.0 - uv.y;
16link
17link vec4 tex_f = texture2D(tex, uv);
18link
19link float gray = rgb(tex_f.rgb);
20link
21link gl_FragColor = vec4(gray,gray,gray,1.0);
22link}
Now you can try these filters with your own camera.
When we measure the framerate of both implementations using the same images as the base dataset we found an improvement 196% in the frameRate, on the software implementation the value of the frameRate was around 8.23 frames per second, while in the hardware implementation we have 24.39 frames per second on average.
Problem Statement and Background Image and Video filters Ascii art Photo Mosaic