I first learnt about Kernels and Convolution a few months ago during a Computer Vision module at University - was really insightful. The exact methods used to perform Gaussian blurring/edge detection etc was something I hadn't given much though to before.
A cool fact about the Gaussian filter is that it's separable - you can convolve in the X direction (using a 1 * n kernel), and then convolve the result again along the Y direction (using a m * 1 kernel) - the final result will be the same as convolving using a single m * n kernel, but can be done in O(N) time rather than O(N^2) (you only have m+n multiplications per pixel rather than m * n per pixel).
Not every filer is separable - it's only possible an n * m filter can be expressed as the product of a 1 * m and a n * 1 matrix.
Two really cool facts about Gaussian filter are: 1) it's the only separable isotropic (ie 'round') kernel, and 2) there's a Deriche approximation where the number of operations per pixel doesn't depend on filter size: https://espace.library.uq.edu.au/view/UQ:10982/IIR.pdf
* If you like playing with kernels in shaders, see also Brad Larson's GPUImage: ( https://github.com/BradLarson/GPUImage ) -- the demo app has a bunch of standard kernels.
Now, to what's happening, you're basically applying a FIR filter to each pixel, so that each one depends also on the frequency information of adjacent pixels (in 2 dimensions)
Minor nitpicks- "Convolved" with the pixels. And the FIR filter doesn't depend on the frequency information in the adjacent pixels, but rather the intensity of the pixels. A short FIR filter must have large frequency support, so the filtering depends on the frequency information given by all pixels.
That's a much more profound explanation than the given. You can actually come up with values yourself then, and it reveals why cases like "edge detection" and "blur" work so nicely (edge detection approximates differentiation; blur acts as a low pass filter;...).
For interest's sake, note that the blur kernel used here is an approximation of the Gaussian [1]. Also, the vImage documentation includes a brief discussion on where the values in these kernels came from [2]
Slightly offtopic but I really like the effect with the side to side images. The html tags are <image-as-matrix></image-as-matrix> and <kernel-matrix></kernel-matrix>.
Does anyone know which lib (if any) he's using or how it's done? Read the source, couldn't figure it out.
AngularJS is just being used to make those custom HTML tags (called "directives" in Angular-speak). It likely has nothing to do with the actual functionality.
Interesting to try a directionally-biased blur filter, which produces an out-of-focus effect but only in that dimension. For instance, put 0.5 in the left and right cells, or the top and bottom cells, with 0 everywhere else.
Yep! It was tricky to make it performant but I mostly just used chromes performance profiling tool to pinpoint optimization compiler bailouts. A big one was just casting to ints with |0 inside the kernel function.
Aside from making conceptual sense, using the original value has the nice property that there's no dependence between the calculation of separate pixels, so the filtered image can be computed fully in parallel. GPUs are very good at this, which obviously makes them useful for graphics rendering, but also at other tasks that involve the same mathematical operation, such as deep learning (the first few stages of a convolution deep neural net are just the application of specially tuned convolution kernels).
Really awesome stuff! I think there's a very minor bug in which missing pixels are treated as black, which is what adds a black border around the output image in the second example.
A cool fact about the Gaussian filter is that it's separable - you can convolve in the X direction (using a 1 * n kernel), and then convolve the result again along the Y direction (using a m * 1 kernel) - the final result will be the same as convolving using a single m * n kernel, but can be done in O(N) time rather than O(N^2) (you only have m+n multiplications per pixel rather than m * n per pixel).
Not every filer is separable - it's only possible an n * m filter can be expressed as the product of a 1 * m and a n * 1 matrix.
Another cool fact is that you can perform convolution in as a point-wise multiplication in Fourier space (see http://en.wikipedia.org/wiki/Convolution_theorem).