1 module daffodil.filter.convolve;
2 
3 import std.range;
4 import std.algorithm;
5 
6 import daffodil.image;
7 
8 /**
9  * Convolve a flat 2D matrix with a given center over an image and return the result.
10  *
11  * With a matrix [0.5, 0.5] with width 2 and center [0, 0] convolved over an image
12  * each resulting pixel will be a 50:50 mix of itself and its right neighbour.
13  *
14  * TODO: Abstract away matrix, width and center into a kernel
15  */
16 auto convolved(V)(const Image!V image, const real[] matrix, int width, int[2] center) {
17     auto height = matrix.length / width;
18     auto ret = image.dup;
19 
20     foreach (imageY; 0..image.height) {
21         foreach (imageX; 0..image.width) {
22             // Make sure weighting always adds up to 1. Fixes corners and incorrect/incomplete matrices.
23             real accum = 0;
24 
25             // Accumulate color by weighing adjacent pixels according to given matrix.
26             auto color = ret.newColor();
27 
28             foreach (indexY; 0..height) {
29                 auto matrixY = indexY - center[1];
30                 if (matrixY + imageY < 0 || matrixY + imageY >= image.height) continue;
31 
32                 foreach (indexX; 0..width) {
33                     auto matrixX = indexX - center[0];
34                     if (matrixX + imageX < 0 || matrixX + imageX >= image.width) continue;
35 
36                     auto matrixValue = matrix[indexX + indexY * width];
37                     accum += matrixValue;
38 
39                     //TODO: Shorthand once color operation is implemented
40                     color = color + image[imageX + matrixX, imageY + matrixY] * matrixValue;
41                 }
42             }
43 
44             ret[imageX, imageY] = color * (1/accum);
45         }
46     }
47 
48     return ret;
49 }
50 /// Ditto
51 auto convolved(string axis, V)(const Image!V image, const real[] matrix, int center) {
52     auto ret = image.dup;
53 
54     static if (canFind(axis, 'x')) {
55         // Apply matrix horizontally
56         ret = ret.convolved(matrix, cast(int)matrix.length, [center, 0]);
57     }
58 
59     static if (canFind(axis, 'y')) {
60         // Apply matrix vertically
61         ret = ret.convolved(matrix, 1, [0, center]);
62     }
63 
64     return ret;
65 }
66 /// Ditto
67 auto convolved(string axis, V)(const Image!V image, const real[] matrix) {
68     return image.convolved!axis(matrix, cast(int)(matrix.length / 2));
69 }
70 
71 //TODO: Add tests