How to create an edge preserving blur (similar to a bilateral filter) using a limited set of primitive operations
algorithm, image-processing, svg
Solution
I should qualify this by saying I have no experience in graphics whatsoever, but from a maths perspective I think this would work to emulate the equation that defines the bilateral filter:
Given your image `I`, use the color matrix to produce an image `Intensity` which holds the intensity of each pixel in a single channel, say R. Channels G and B are zeroed out.
For each non-central pixel in your bilateral filter window, construct a convolution matrix which takes the difference between the specific pixel and the central pixel. For example, for a 3x3 window you'd have matrices
0 0 0 -1 0 0 0-1 0 0 0-1 0 0 0 0 0 0 0 0 0 0 0 0
-1 1 0 0 1 0 0 1 0 0 1 0 0 1-1 0 1 0 0 1 0 0 1 0
0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0-1 0-1 0 -1 0 0
You can scale the `1`s and `-1`s here as necessary to emulate the spatial kernel of the bilateral filter.
Apply each convolution matrix to the `Intensity` map, getting (in the 3x3 example) 8 images which represent the change in intensity between a central pixel and its neighbours.
For each of the 8 images, apply a component transfer primative to R with a table that emulates the range kernel of the bilateral filter.
Use another color matrix to set the G and B channels to match the R channel in all 8 images.
Use the multiply operator on each of the 8 and the original image to get 8 new images which represent the 8 terms in the bilateral filter's sum.
Use the Porter-Duff operators to superimpose the 8 images, effectively taking a sum of the 8 terms in the bilateral filter. This gives you the final image.
Problem
I have been attempting to duplicate a bilateral filter effect (edge preserving, color-range aware) using the limited set of primitives in the existing toolbox of SVG filters. I've tried a number of approaches. My most successful to date is a three part operation that does Sobel edge detection, dilates the Sobel edges, extracts the pixels corresponding to those edges with a compositing operation, gaussian blurs the source image and then composites back the original edge pixels on top of the blurred image. The result preserves edges, but is not color-range aware. ``` <filter id="surfaceBlur" color-interpolation-filters="sRGB"> <!-- convert source image to luminance map--> <feColorMatrix type="luminanceToAlpha" /> <!-- sober edge detection--> <feConvolveMatrix order="3" kernelMatrix="-1 -2 -1 0 0 0 1 2 1 " preserveAlpha="true" /> <feConvolveMatrix order="3" kernelMatrix="-1 0 1 -2 0 2 -1 0 1 " preserveAlpha="true" /> <!-- dilate the edges to produce a wider mask--> <feMorphology operator="dilate" radius="1" result="mask"/> <!-- extract just the detail from the source graphic using the dilated edges --> <feComposite operator="in" in="SourceGraphic" in2="mask" result="detail" /> <!-- blur the source image --> <feGaussianBlur stdDeviation="3" in="SourceGraphic" result="backblur"/> <!-- slap the detail back on top of the blur! --> <feComposite operator="over" in="detail" in2="backblur"/> ``` You can see the original, a gaussianBlur, this filter and in the bottom right, a true bilateral filter: http://codepen.io/mullany/details/Dbyxt As you can see, it's not an awful result, but it's not very close to a bilateral filter. This method also only works with greyscale images because it uses luminance differences to find edges - so edges between colors of similar luminance are not detected. So the question is whether there is a algorithm variant of an edge preserving color range aware filter (guided edge view, bilateral etc.) - that can be constructed using the limited primitives available in SVG - which for those not familiar with SVG are: - gaussian blur - convolution (any kernel size) - erode/dilate - color matrix - all porter duff compositing operations - basic blending ops (multiply, screen, lighten, darken) - a component transfer primitive that allows color channels to be transformed with a table lookup (as well as floored/ceilinged to specific values) Only the RGB color space is available. Multiple iterations are fine, and any directed graph of these operations can be constructed. Update: I successfully created a median filter using feBlend lighten and darken as Max and Min operators in a bubble sort (thanks to help from cs.stackexchange.com). However this is inefficient: http://codepen.io/mullany/pen/dmbvz, and lacks the color range awareness of a bilateral filter.