Problem Statement and Background Image and Video filters Ascii art Photo Mosaic
This filters has the purpose of detect edges on the images, and are widely used on machine learning applications, because they help to extract important features of the images. Here we are going to test three different kernels in order to compare them.
For this and the following sections of kernel filters we are going to use the following image (By Michael Plotke - Own work, CC BY-SA 3.0, https://commons.wikimedia.org/w/index.php?curid=24301122)
Here you can see the filter applied to an image, and check how the edge detection extract only the shapes that could be related to edges on the image.
The markdown of the above sketch looks like:
1link> :P5 sketch=/docs/sketches/first_convolution.js, width=256, height=256
And the p5 sketch that applies the convolution on the image is the following:
1linklet w = [[1, 0, -1], [0, 0, 0], [-1, 0, 1]];
2linklet img;
3linklet copy;
4link
5linkfunction preload() {
6link copy = loadImage('/vc/docs/sketches/lenna.png');
7link}
8link
9linkfunction setup() {
10link createCanvas(256, 256);
11link img = createImage(copy.width, copy.height);
12link}
13link
14linkfunction convolution(image, x, y, kernel, size, width, height, d) {
15link let rConv = 0;
16link let gConv = 0;
17link let bConv = 0;
18link let offset = floor(size / 2);
19link for (let i = 0; i < size; i++) {
20link for (let j = 0; j < size; j++) {
21link
22link let xpos = x + j - offset;
23link let ypos = y + i - offset;
24link
25link let index = (xpos + image.width * ypos * d) * 4;
26link
27link if (!(width <= xpos || height <= ypos || xpos < 0 || ypos < 0)) {
28link rConv += image.pixels[index + 0] * kernel[i][j];
29link gConv += image.pixels[index + 1] * kernel[i][j];
30link bConv += image.pixels[index + 2] * kernel[i][j];
31link }
32link
33link }
34link }
35link return [rConv, gConv, bConv];
36link}
37link
38linkfunction draw() {
39link image(img, 0, 0, 256, 256);
40link img.loadPixels();
41link copy.loadPixels();
42link let d = pixelDensity();
43link
44link for (let y = 0; y < img.height*d; y++) {
45link for (let x = 0; x < img.width*d; x++) {
46link let result = convolution(copy, x, y, w, 3, img.width*d, img.height*d, d);
47link let index = (x + y * img.width*d) * 4;
48link img.pixels[index + 0] = result[0];
49link img.pixels[index + 1] = result[1];
50link img.pixels[index + 2] = result[2];
51link img.pixels[index + 3] = 255;
52link }
53link }
54link img.updatePixels();
55link
56link}
On the code we create a copy of the image, in order to take it as the base image and create an empty image that will act as the filtered image, then we iterate over the base image and create the convolution pixel by pixel.
And here the filter applied to a video.
The markdown of the above sketch looks like:
1link> :P5 sketch=/docs/sketches/first_convolution_video.js, width=256, height=256
And the p5 sketch that applies the convolution on the video is the following:
1linklet w = [[1, 0, -1], [0, 0, 0], [-1, 0, 1]];
2linklet video;
3linklet copy;
4link
5linkfunction preload() {
6link video = createVideo(['/vc/docs/sketches/fingers.mov', '/vc/docs/sketches/fingers.webm']);
7link video.hide();
8link}
9link
10linkfunction setup() {
11link createCanvas(256, 256);
12link video.loop();
13link}
14link
15linkfunction convolution(image, x, y, kernel, size, width, height, d) {
16link let rConv = 0;
17link let gConv = 0;
18link let bConv = 0;
19link let offset = floor(size / 2);
20link for (let i = 0; i < size; i++) {
21link for (let j = 0; j < size; j++) {
22link
23link let xpos = x + j - offset;
24link let ypos = y + i - offset;
25link
26link let index = (xpos + image.width * ypos * d) * 4;
27link
28link if (!(width <= xpos || height <= ypos || xpos < 0 || ypos < 0)) {
29link rConv += image.pixels[index + 0] * kernel[i][j];
30link gConv += image.pixels[index + 1] * kernel[i][j];
31link bConv += image.pixels[index + 2] * kernel[i][j];
32link }
33link
34link }
35link }
36link return [rConv, gConv, bConv];
37link}
38link
39linkfunction draw() {
40link image(video, 0, 0, 256, 256);
41link let copy = createImage(video.width, video.height);
42link video.loadPixels();
43link let d = pixelDensity();
44link for (let i = 0; i < video.pixels.length; i++) {
45link copy.pixels[i] = video.pixels[i];
46link }
47link
48link for (let y = 0; y < video.height*d; y++) {
49link for (let x = 0; x < video.width*d; x++) {
50link let result = convolution(copy, x, y, w, 3, video.width*d, video.height*d, d);
51link let index = (x + y * video.width*d) * 4;
52link video.pixels[index + 0] = result[0];
53link video.pixels[index + 1] = result[1];
54link video.pixels[index + 2] = result[2];
55link video.pixels[index + 3] = 255;
56link }
57link }
58link video.updatePixels();
59link
60link}
On the code we load the video and create a copy of the actual frame each time that the draw function is called, then with that copy as ground of truth we alter the current frame of the video with the convolution of the copy and the filter.
With this matrix we can see a soft edge detection that works fine for images but not so well for videos because the movements start to appear fuzzy.
Laplacian matrices has been used a lot for edge detection because of their incredible properties and results on these tasks.
The result of the filter on an image is the following.
The result of the filter on a video is the following.
As we can see the edge detection has improved a lot with this matrix on the image, but it stills a bit fuzzy on the video.
The result of the filter on an image is the following.
The result of the filter on a video is the following.
This laplacian matrix has the best performance on the video, but on the image it takes many details of the textures as edges, which could affect the detection.
Problem Statement and Background Image and Video filters Ascii art Photo Mosaic