Viewer Image Filters

=Second Life Viewer Image Filters=
== Objective of the Image Filters ==
Image posting is the most popular feature of the newly minted SLShare functions (Facebook, Flickr and Twitter). Advanced users already do impressive post processing to their images, especially on Flickr, using sophisticated image editing software. For casual users, being able to slightly customize images to add mood and feel just before posting seems a natural fit.
The Image Filter system allows the community of residents and TPV (Third Party Viewers) developers to add new snapshot filters without touching the SL Viewer code itself but editing simple filter scripts.
== Fundamentals ==
=== Tenets and Limitations ===
Considering that we are not trying to recreate an image editing tool in the viewer, we decided to impose the following limits to our filter system:
* In-place, single pass procedural filters. 
* Minimum memory impact, hence : 
** no layers
** no secondary image input (alpha, texture, etc…)
** no compositing tree (which requires intermediate store on tree nodes)
* Procedural alpha values computed "on the fly" are fine (see "Stencils" here under)
* Filters can be chained in a sequence to create more complex composite effects
* Filters can be described in simple xml files so that adding and tweaking filters will not require recompilation and can easily be done by TPV developers or advanced users
* As much as possible, filters need to work in a resolution independent way meaning that a filter should work in a consistent way no matter which image size it is applied to.
=== Filter Compositing Stack ===
A filter file contains a list of filters that are applied sequentially, step by step, to the image. The effects are cumulative and the input image is modified at each step of the process. The result image of a step is used as input in the next step:
input image -> filter 1 -> result image 1 -> filter 2 -> result image 2 -> […] -> output image
Each filter though is not applied bluntly on the input but modulated through a matte called stencil in our design. The filter is computed and the result of the filter is blended through the stencil on the image at each step to produce the step output. In effect, the filtered result is composed back on the input used as a background. This is why we are talking here of a compositing stack:
input image -> filter x -> filtered image - 
					 |-> stencil + blend mode -> result image x
input image 	->		 -
The stencil defines an alpha layer and a blend function that describes how the filtered image is mixed to the input image used as a background. The result is as if the filter was applied in some places while leaving others untouched though the exact result depends on the blend mode chosen for the stencil.
Together, the combination of filters, stencils and blend modes allow for a wide variety of effects.
== Filtering Primitives ==
=== Filters ===
The basis of the filtering engine is provided by 4 filter primitives acting on LLImageRaw instances, that is, on uncompressed static image buffers:
* Color Correction (LLImageFilter::colorCorrect): applies a set of look up tables to the red, green and blue channels independently
* Color Transformation (LLImageFilter::colorTransform): applies an affine transformation, provided as a 3x3 matrix, to the RGB color space
* Convolution (LLImageFilter::convolve): applies a 3x3 convolution kernel to the image
* Screening (LLImageFilter::filterScreen): applies a thresholding function to turn the image luminosity into black or white values
Primary filters are not addressable directly from the filter syntax (see here under) but rather used through secondary filters that do computation based on filter description arguments and image histograms before calling the filter primitives with the appropriate arguments. 
==== Color Correction ====
Color corrections simply apply a set of 3 look up tables (aka LUT) to the RGB input values to produce RGB output values.
LUTs being arrays of 256 values are not directly entered using the filter description syntax. Rather, LUTs are computed by secondary filters (e.g. colorize) that use just a few arguments to create the 3x256 values. 
Additionally, secondary filters can use statistics of the image (histograms typically) to adapt the LUTs computation to the current image. This is done for instance by the linearize and posterize filters.
Note that all color correction filters can use 3 alpha values independently set for each channel. For instance, using colorize with a white color and setting the alpha values to 1 for red and 0 for green and blue will result in an image colored only in the red channel, leaving green and blue unchanged.
==== Color Transformation ====
Color transformations apply a 3x3 affine transformation in the RGB space to the input image. This is used to convert the color space (e.g. grayscale, sepia) or apply effects to the hue and saturation (e.g rotate, saturate).
Because tweaking the matrix values directly is difficult, color transformations are typically called by secondary filters using fewer arguments and an easier to manipulate syntax. However, colortransform can be called as a filter directly.
Because the effect mixes all channels, it doesn't make sense here to provide an alpha value per channel, thus the secondary filters do not take such an argument.
==== Convolve ====
Convolve implements a 3x3 convolution kernel. It uses an "in place" algorithm and requires only a 2 line buffer to perform.
The implementation allows for absolute value and normalization arguments so that edge detection and averaging can be easily specified.
The convolve filter can be addressed directly in the filter description syntax. 3 secondary filters (blur, sharpen and gradient) are supported to make commonly used effects easier to use.
==== Screening ====
Screening is a special kind of filter that transforms a typically grayscale image into a pure black and white image using a dot function or a dithering technique. This is used for special effects and not typically used to filter images.
The SL filter bank provides 2 kinds of screens: 2 sine and lines (see filter description here under).
=== Stencils ===
It is often desirable to apply a filter effect in a non uniform way throughout the source image. We decided to avoid the complexity of loading secondary images (where an alpha could be stored and read from) and instead propose a set of procedural transparency layers that we call "stencils".
Once a stencil is set, it is applied to all subsequent filters. The stencil can be reset by setting it to "uniform 1.0" in a filtering sequence.
Stencils are characterized by:
* a shape 
* a blend mode
* an alpha range
We provide 4 stencil shapes:
* uniform: a flat alpha value throughout the image
* gradient: a variable alpha from an origin point to a final point
* vignette: a radial alpha from a center fading to the edges
* scanlines: a sine alpha with a wavelength and a direction
and 4 blend modes:
* blend
* add
* add_back
* fade
The alpha range is simply a set of 2 alpha values that will remap linearly the (0.0, 1.0) straight output range of the initial alpha to a min and a max alpha value. Note that:
* min and max can be reversed so this can be used to flip the alpha channel
* min and max are chosen in the (-1.0, 1.0) range so this can be used to inverse the add and add_back mode into subtractive modes (effect on other modes is harder to control)
==== Blend Modes ====
Each stencil takes a blend mode as an argument. This mode expresses how the alpha value provided by the stencil is used to compose the resulting filtered image on top of the input background image. The general formula is:
ouput_image = fi(alpha)*input_image + fo(alpha)*filtered_image
With fi() and fo() being functions applied to the stencil alpha value. The only meaningful values for those functions are:
fi(alpha) = 1.0 - alpha
fi(alpha) = 1.0
fi(alpha) = 0.0
fo(alpha) = alpha
fo(alpha) = 1.0
fo(alpha) = 0.0
That makes 9 possible combinations, of which only 4 lead to useful non trivial results. Those are the blend modes.
===== blend =====
This is the classic and most commonly used alpha blending function, known as over in some compositing packages. It shows the filtered image where the alpha is 1.0, the input image where it's 0.0 and a weighted average in between. The formula is:
ouput_image = (1.0 - alpha)*input_image + alpha*filtered_image
===== add =====
This mode leaves the original image intact and adds the filtered image to it, modulated by the alpha. It blows the highlights easily. When used with output_image = input_image, it acts as a multiplication coefficient, in effect scaling the input value by (1.0 + alpha).
Note that when used with a negative value for alpha, this mode subtracts the filtered image from input image.
ouput_image = input_image + alpha*filtered_image
===== add_back =====
This mode shows the filtered image everywhere and adds back the input image where the alpha is 0.0. It's a bit like add but adding the input image to the filtered one, instead of adding the filtered image to the background.
ouput_image = (1.0 - alpha)*input_image + filtered_image
===== fade =====
This mode simply shows the filtered image through the alpha and fades to black where the alpha is not defined. The input image is erased. This mode is used for vignetting for instance.
ouput_image = alpha*filtered_image
== Secondary Filters ==
Secondary filters are named filters that can be invoked from a filter description file and call the primary filters to implement themselves. Most do take arguments. Collectively, they cover all of the commonly used filters in use in digital compositing. Here's a complete list:
* grayscale: converts to grayscale
* sepia: converts to sepia
* saturate: changes color saturation
* rotate: rotates the color hue
* colortransform: transform the RGB input values through a 3x3 matrix
* gamma: applies a gamma curve to all channels
* colorize: applies a colored tint to the image
* contrast: modifies the contrast of the image
* brighten: adds light to the image
* darken: substracts light to the image
* linearize: linearly rescales the contrast of the image using the brightness histogram
* posterize: redistributes the colors in classes per channel
* screen: applies a screening filter to the red channel and output to black and white.
* sharpen: sharpens the image using a 3x3 kernel
* blur: blurs the image using a 3x3 kernel
* gradient: performs an edge detection convolution on the input image
* convolve: applies a 3x3 convolution kernel to the input image
All of these filters and their arguments are detailed further in this document.
== Filter Description ==
=== Location of Filter Files ===
Filter files are located in the app_settings/filters folder alongside other similar viewer settings like windlight, commands or toolbars. A resident or TPV developer can add new filters in this location and they'll show up in the viewer. This folder is scanned once per session so adding new filters requires quitting and relaunching the current session. Filter files are however loaded and executed when invoked so modifying a filter will not require relaunching.
For residents and TPV developers who are interested in developing new filters, it is recommended for speed and convenience to use llimage_libtest with the new argument --filter to develop and test new filters. For instance, the following command will apply the filter sequence defined in my_filter.xml to image.png and output the result in image1.png:
./llimage_libtest --input image.png --output image1.png --filter my_filter.xml
The source code repository contains 41 example filters in indra/integration_tests/llimage_libtest/filters that you can use to study the various filters and stencils and use as stepping stones in your own filter design. This folder also contains the example filters given at the end of this document.
=== Syntax ===
Filter files use the LLSD serialized XML format used in other parts of the viewer (e.g. windlight settings).
Filters are loaded and executed sequentially, top to bottom, so care must be taken on the order of definition of the various filtering stages. No simplification or optimization stage is implemented so, if a later filter erases everything computed before (e.g. a uniform 1.0 stencil followed by a 1.0 black colorize in all channels), the filter computation will still move step by step and compute all previous filters, taking time and resources. Be aware of this when designing your filters.
Stencils are special in the sense that they set a compositing alpha mask that stay effective for all subsequent filters.
Each filter is computed using the background image as input then composed back into the background image using the current stencil definition. In this sense, filter files specify a case of single stack (no node) compositing tree.
All filters are specified giving their name first, then a set of named options (if any) given as strings and a set of arguments given as reals. Care must be taken in giving at least as many arguments as required for each filter. Given too few arguments, a filter will crash at execution. Too many arguments will have no effect, extra arguments will simply be ignored.
=== Argument Formatting ===
==== Colors ====
Colors are specified by a (red,green,blue) triplet of floating point numbers in the 0.0 (no input) and 1.0 (max input) range. In that color coordinate system, black is represent by (0.0,0.0,0.0) and white by (1.0,1.0,1.0).
==== Alpha or Transparency Values ====
Alpha values are represented by a floating point in the 0.0 (transparent) to 1.0 (opaque) range. For color correction effects, a different alpha can be specified for each red, green and blue channel independently. This value should be considered as the way the effect is computed, as opposed to the stencil which specifies how the computed effect is applied to the background image.
==== Image Coordinates and Dimensions ====
Some effects do require arguments that specify a position in the image (e.g. start and end points of a gradient stencil) or a size (e.g. wavelength of a screen filter). To be applicable to any image regardless of its size, those values must be given in a resolution independent way.
By convention, we set the origin (0.0,0.0) of the image coordinate system to the center of the image and the unit to be the half height of the image. In this system then, (0.0,1.0) represents the point in the middle of the top row of the image while (0.0,-1.0) represents the point in the middle of the bottom row of the image. (1.0,0.0) is a point in the middle row to the right of the center, (-1.0,0.0) is in the middle row to the left).
If one knows the aspect ratio (width/heigth) of an image, it's easy to see that (-aspect_ratio,-1.0) is the bottom left point of the image while (aspect_ratio,1.0) is the top right.
Using an isotropic scaling factor for the coordinates is debatable but it makes specifying radius (e.g. radius of vignette stencil) and wave length (e.g. wave length of a scanlines stencil) error proof and, in the context of the SL viewer where most images have an aspect ratio superior to 1.0, easier to visualize.
== List of Supported Filters and Arguments ==
Each stencil and filter is provided here under with a sample XML code. All of them come with a named filter file available in the viewer source code repository under indra/integration_tests/llimage_libtest/filters.
=== Stencils ===
==== uniform ====
This stencil specifies a uniform, flat value for alpha across the image. The first real argument (the minimum value of the stencil) is ignored for this stencil shape but it is still required. The second argument (the maximum value) is set for the whole surface.
This stencil in blend mode and a max value of 1.0 is the one set by default when a filter description file is loaded and executed.
 <array>
 <string>stencil</string>
 <string>uniform</string>
 <string>blend</string>
 <real>0.0</real>
 <real>1.0</real>
 </array>
==== vignette ====
This stencil specifies a circular portion of the image with a feathering coefficient.
The first 2 real arguments are, as for all stencils, the minimum and maximum values of the alpha for this shape, typically 0.0 and 1.0. Note that specifying 1.0 and 0.0 in effect flips the stencil, i.e. making the outside of the circle where the effect will be applied while the inside will be preserved. This is useful if one wants for instance to have different effects applied in the circle and outside of it (remember that we do not support a full expression tree but a simple compositing stack).
The real arguments 3 and 4 give the center of the vignette, in the normalized coordinate system. This is often 0.0 and 0.0, i.e. center, for a classic vignette.
The 5th real argument is the width of the circle, here again in normalized coordinates. This is typically 1.0 for a centered vignette that cover the whole height of the image. Note that this value can be superior to 1.0.
Last, the 6th real argument is the feathering which specifies how sharply the alpha falls from max to min at the edge of the vignette. This is the coefficient of a gaussian. A high value will make a sharper drop than a low value. A typical value is between 5.0 (smooth edge) and 10.0 (fast drop).
 <array>
 <string>stencil</string>
 <string>vignette</string>
 <string>blend</string>
 <real>0.0</real>
 <real>1.0</real>
 <real>0.0</real>
 <real>0.0</real>
 <real>1.0</real>
 <real>10.0</real>
 </array>
==== scanlines ====
This stencil specifies an alternating set of bands or lines moving between the minimum and maximum specified value.
The first 2 real arguments are, as for all stencils, the minimum and maximum values of the alpha for this shape, typically 0.0 and 1.0.
The 3rd real arguments is the wavelength of the scan lines, in normalized coordinates. A value of 0.1 will produce 40 scan lines in the full height of the image since 1.0 is the half height of the image.
The 4th real argument is the angle, in degrees, of the line generator with the horizontal. A value of 0.0 will give horizontal lines.
 <array>
 <string>stencil</string>
 <string>scanlines</string>
 <string>blend</string>
 <real>0.0</real>
 <real>1.0</real>
 <real>0.1</real>
 <real>45.0</real>
 </array>
==== gradient ====
This stencil specifies an alpha gradient across the image going from min to max.
The first 2 real arguments are, as for all stencils, the minimum and maximum values of the alpha for this shape, typically 0.0 and 1.0. Note that the min value is used for the start point of the gradient while the max is used for the end point. Inverting these values will simply flip the gradient with no ill effect.
The 3rd and 4th real arguments are the horizontal and vertical coordinates of the start point of the gradient, in normalized coordinates. 5th and 6th provide the horizontal and vertical coordinates of the end point. The example here under gives the coordinate to be used for a vertical gradient going from 0.0 at the bottom to 1.0 at the top.
 <array>
 <string>stencil</string>
 <string>gradient</string>
 <string>blend</string>
 <real>0.0</real>
 <real>1.0</real>
 <real>0.0</real>
 <real>-1.0</real>
 <real>0.0</real>
 <real>1.0</real>
 </array>
=== Filters ===
==== grayscale ====
Converts the image to grayscale. This filter doesn't take any argument.
 <array>
 <string>grayscale</string>
 </array>
==== sepia ====
Converts the image to sepia. This filter doesn't take any argument.
 <array>
 <string>sepia</string>
 </array>
==== saturate ====
Multiply the image color saturation by a constant factor:
* factor < 1.0 will desaturate
* factor > 1.0 will saturate
A factor of 1.0 will leave the image unchanged.
 <array>
 <string>saturate</string>
 <real>1.0</real>
 </array>
==== rotate ====
Rotates the image color hue by a given angle, in degree.
 <array>
 <string>rotate</string>
 <real>180.0</real>
 </array>
==== colortransform ====
This applies a full blown 3x3 RGB color transformation matrix to the input image. The 9 real arguments are organized row by row. So the first 3 arguments are those used to compute the output red color using the input red, green and blue:
output_red = real_1*input_red + real_2*input_green + real_3*input_blue
The next 3 real arguments give the coefficients for the output green and the last 3 for the output blue.
For instance, the following filter specifies how the grayscale filter is implemented:
 <array>
 <string>colortransform</string>
 <real>0.2125</real>
 <real>0.7154</real>
 <real>0.0721</real>
 <real>0.2125</real>
 <real>0.7154</real>
 <real>0.0721</real>
 <real>0.2125</real>
 <real>0.7154</real>
 <real>0.0721</real>
 </array>
==== gamma ====
Applies a gamma curve (y=x^1/gamma) to all channels:
* gamma > 1.0 will lighten the image
* gamma < 1.0 will darken the image
A gamma equal to 1.0 will leave the image unchanged.
The 1st real argument is the gamma value itself. The 3 following arguments are alpha values for the red, green and blue channels, so a gamma correction can be applied to one channel only. Using appropriate alpha values, consecutive calls to the gamma filter can be used to change the gamma of each channel independently.
 <array>
 <string>gamma</string>
 <real>1.7</real>
 <real>1.0</real>
 <real>1.0</real>
 <real>1.0</real>
 </array>
==== colorize ====
Paints a colored tint onto the image. The first 3 arguments are the red, green and blue values given in the 0.0 to 1.0 range. The last 3 arguments are alpha values for each channel so one can prevent a channel to be affected by the tint.
Note that the alpha values here will combine with the stencil value to produce the output composite value. So colorizing in red with alpha equal to 1.0 on each channel through a uniform 0.5 stencil produces the same effect than colorizing in red with a 0.5 alpha value for each channel through a uniform stencil of 1.0. The big difference though with the alpha values here is that they apply independently to each channel while the stencil value applies to all.
For instance, a 50% red tinting leaving green and blue untouched can be specified with the following filter:
 <array>
 <string>colorize</string>
 <real>1.0</real>
 <real>0.0</real>
 <real>0.0</real>
 <real>0.5</real>
 <real>0.0</real>
 <real>0.0</real>
 </array>
==== contrast ====
Modifies the contrast linearly around the midtones using the slope as an argument: 
* slope > 1.0 will enhance the contrast
* slope < 1.0 will flatten it
A slope argument of 1.0 will leave the image unchanged.
The 1st real argument is the slope. The 3 other arguments are alpha values for the red, green and blue channel so each channel can have its contrast adapted independently.
Note that contrast is a pretty coarse operator and that gamma is likely a better choice to boost or reduce the contrast blindly. If you want to use a linear operator, use linearize which takes the brightness histogram of the input image into account.
 <array>
 <string>contrast</string>
 <real>1.5</real>
 <real>1.0</real>
 <real>1.0</real>
 <real>1.0</real>
 </array>
==== brighten ====
Adds constant light to the image (argument between 0.0 and 1.0). A value of 0.0 for this argument will leave the image unchanged. 
The 1st real argument is the light being added. The 3 other arguments are alpha values for the red, green and blue channel so each channel can have its intensity bumped up independently.
Note that darken is the same as brighten with a negative argument for light.
 <array>
 <string>brighten</string>
 <real>0.5</real>
 <real>1.0</real>
 <real>1.0</real>
 <real>1.0</real>
 </array>
==== darken ====
Subtract constant light to the image (argument between 0.0 and 1.0). A value of 0.0 for this argument will leave the image unchanged. 
The 1st real argument is the light being subtracted. The 3 other arguments are alpha values for the red, green and blue channel so each channel can have its intensity dimmed down independently.
Note that darken will give the the same result as brighten with a negative argument for light.
 <array>
 <string>darken</string>
 <real>0.5</real>
 <real>1.0</real>
 <real>1.0</real>
 <real>1.0</real>
 </array>
==== linearize ====
Optimizes the contrast and brightness using the brightness histogram.
The first argument is the fraction (between 0.0 and 1.0) of the discarded head and tail of the histogram. A linear look up table is then computed for each channel to redistribute the values between the image minimum and maximum brightness.
The 3 following arguments are alpha values for red, green and blue so to control which output channel will be affected by the change.
Note that linearize is often a better bet than using contrast, brighten and darken independently as it adapts to each input image. It's also a good first step before any other filter as it brings all input images into a similar contrast and brightness balance so it'll make your filters more reliable.
 <array>
 <string>linearize</string>
 <real>0.1</real>
 <real>1.0</real>
 <real>1.0</real>
 <real>1.0</real>
 </array>
==== posterize ====
Redistribute the contrast in a fixed number of equally populated classes. 
The first argument is the number of classes (between 0 and 255) defined on the brightness histogram. 
The 3 following arguments are alpha values for red, green and blue so to control which output channel will be affected by the change.
Note that posterize works in a way similar to linearize, using the brightness histogram to redistribute the input values, but in a non linear way.
 <array>
 <string>posterize</string>
 <real>10.0</real>
 <real>1.0</real>
 <real>1.0</real>
 <real>1.0</real>
 </array>
==== screen ====
Applies a screening filter to the red channel and output to black and white. This filter is a monochromatic filter and it assumes that the input image has been converted to grayscale or that the red channel is somewhat meaningful.
The filter takes 3 parameters: a mode, a wave length and an angle. Modes are:
* 2Dsine: applies a bidirectional (x,y) sine screen. The angle argument gives the direction of the screen generator.
* line: applies a linear sine screen. The angle is the line generator angle with the horizontal.
The wave length argument is the dimension between 2 peaks of the sine function and is given in normalized image coordinates.
The last argument is the angle, in degrees.
 <array>
 <string>screen</string>
 <string>2Dsine</string>
 <real>0.015</real>
 <real>30.0</real>
 </array>
 <array>
 <string>screen</string>
 <string>line</string>
 <real>0.015</real>
 <real>45.0</real>
 </array>
==== blur ====
Applies a blurring 3x3 convolution kernel to the input image on all channels. The kernel is defined as:
[ 1 1 1 ]
[ 1 1 1 ]
[ 1 1 1 ]
This filter doesn't take any argument.
 <array>
 <string>blur</string>
 </array>
==== sharpen ====
Applies a sharpening 3x3 convolution kernel to the input image on all channels. The kernel is defined as:
[ -1 -1 -1 ]
[ -1 9 -1 ]
[ -1 -1 -1 ]
This filter doesn't take any argument.
 <array>
 <string>sharpen</string>
 </array>
==== gradient ====
Applies a gradient (edge detection) 3x3 convolution kernel to the input image on all channels. The kernel is defined as:
[ -1 -1 -1 ]
[ -1 8 -1 ]
[ -1 -1 -1 ]
This filter doesn't take any argument.
 <array>
 <string>gradient</string>
 </array>
==== convolve ====
Applies a full blown 3x3 convolution kernel to the input image on all channels.
The 1st real argument tells the filter to normalize the result or not. It is interpreted as a boolean value, true if > 0.0, false otherwise. Typically, only 0.0 and 1.0 should be used here but other values will not be rejected. The normalization will simply divide the weighted result by the sum of the weights. This is turned on for blur and off for sharpen and gradient.
The 2nd real argument tells the filter to use the absolute value of the weighted result. This is also interpreted as a boolean. This flag is turned on for gradient but it's off for blur and sharpen.
The 9 following real arguments give the coefficients of the convolution kernel, top to bottom and left to right. The first argument then is the top left weight, the second the middle top, the third the top right, etc... 
The following example defines a strong blur filter, weighting the 4 corners of the kernel to 4, the 4 middle edges to 1 and the center to 0. The filter will use normalization but no absolute value.
 <array>
 <string>convolve</string>
 <real>1.0</real>
 <real>0.0</real>
 <real>4.0</real>
 <real>1.0</real>
 <real>4.0</real>
 <real>1.0</real>
 <real>0.0</real>
 <real>1.0</real>
 <real>4.0</real>
 <real>1.0</real>
 <real>4.0</real>
 </array>
== Examples ==
=== Auto Contrast ===
Here is a simple filter to improve the contrast of an image based on its luminosity histogram. Particularly useful for night time photographs like the example here under.
Original image:
Filtered image:
<llsd>
 <array>
 <array>
 <string>linearize</string>
 <real>0.01</real>
 <real>1.0</real>
 <real>1.0</real>
 <real>1.0</real>
 </array>
 </array>
</llsd>
=== Bad Trip ===
This creative filter is an homage to the work of Alan Kwan:
<llsd>
 <array>
 <array>
 <string>grayscale</string>
 </array>
 <array>
 <string>linearize</string>
 <real>0.1</real>
 <real>1.0</real>
 <real>1.0</real>
 <real>1.0</real>
 </array>
 <array>
 <string>posterize</string>
 <real>10.0</real>
 <real>1.0</real>
 <real>1.0</real>
 <real>1.0</real>
 </array>
 <array>
 <string>gradient</string>
 </array>
 <array>
 <string>colorize</string>
 <real>0.0</real>
 <real>0.0</real>
 <real>1.0</real>
 <real>0.0</real>
 <real>0.0</real>
 <real>0.15</real>
 </array>
 <array>
 <string>blur</string>
 </array>
 </array>
</llsd>
=== Blow Highlights ===
This filter pushes the highlight to give high contrast overexposed feel:
<llsd>
 <array>
 <array>
 <string>linearize</string>
 <real>0.0</real>
 <real>1.0</real>
 <real>1.0</real>
 <real>1.0</real>
 </array>
 <array>
 <string>stencil</string>
 <string>uniform</string>
 <string>add</string>
 <real>0.0</real>
 <real>1.0</real>
 </array>
 <array>
 <string>gamma</string>
 <real>0.25</real>
 <real>1.0</real>
 <real>1.0</real>
 <real>1.0</real>
 </array>
 </array>
</llsd>
=== Dodge and Burn ===
This filter simulates dodge in the center and burn on the outskirt:
<llsd>
 <array>
 <array>
 <string>linearize</string>
 <real>0.0</real>
 <real>1.0</real>
 <real>1.0</real>
 <real>1.0</real>
 </array>
 <array>
 <string>stencil</string>
 <string>vignette</string>
 <string>add</string>
 <real>0.0</real>
 <real>0.4</real>
 <real>0.0</real>
 <real>0.0</real>
 <real>1.0</real>
 <real>2.0</real>
 </array>
 <array>
 <string>contrast</string>
 <real>1.0</real>
 <real>1.0</real>
 <real>1.0</real>
 <real>1.0</real>
 </array>
 <array>
 <string>stencil</string>
 <string>vignette</string>
 <string>add</string>
 <real>-0.8</real>
 <real>0.0</real>
 <real>0.0</real>
 <real>0.0</real>
 <real>1.0</real>
 <real>2.0</real>
 </array>
 <array>
 <string>contrast</string>
 <real>1.0</real>
 <real>1.0</real>
 <real>1.0</real>
 <real>1.0</real>
 </array>
 </array>
</llsd>
=== Focus ===
This filter brings in the center of the image while blurring the edges:
<llsd>
 <array>
 <array>
 <string>linearize</string>
 <real>0.0</real>
 <real>1.0</real>
 <real>1.0</real>
 <real>1.0</real>
 </array>
 <array>
 <string>stencil</string>
 <string>vignette</string>
 <string>blend</string>
 <real>0.0</real>
 <real>0.4</real>
 <real>0.0</real>
 <real>0.0</real>
 <real>0.5</real>
 <real>2.0</real>
 </array>
 <array>
 <string>sharpen</string>
 </array>
 <array>
 <string>stencil</string>
 <string>vignette</string>
 <string>blend</string>
 <real>1.0</real>
 <real>0.0</real>
 <real>0.0</real>
 <real>0.0</real>
 <real>0.5</real>
 <real>2.0</real>
 </array>
 <array>
 <string>blur</string>
 </array>
 </array>
</llsd>
=== Jules Verne ===
This filter is inspired by the screening technique used in XIX century illustrations like those in the famous "Voyages Extraordinaires" by Jules Verne:
<llsd>
 <array>
 <array>
 <string>linearize</string>
 <real>0.1</real>
 <real>1.0</real>
 <real>1.0</real>
 <real>1.0</real>
 </array>
 <array>
 <string>grayscale</string>
 </array>
 <array>
 <string>screen</string>
 <string>line</string>
 <real>0.02</real>
 <real>0.0</real>
 </array>
 </array>
</llsd>
=== Lens Flare ===
This filter adds lens flare effects:
<llsd>
 <array>
 <array>
 <string>linearize</string>
 <real>0.01</real>
 <real>1.0</real>
 <real>1.0</real>
 <real>1.0</real>
 </array>
 <array>
 <string>brighten</string>
 <real>0.1</real>
 <real>1.0</real>
 <real>1.0</real>
 <real>1.0</real>
 </array>
 <array>
 <string>stencil</string>
 <string>vignette</string>
 <string>add</string>
 <real>0.0</real>
 <real>0.4</real>
 <real>-0.5</real>
 <real>0.5</real>
 <real>0.15</real>
 <real>20.0</real>
 </array>
 <array>
 <string>colorize</string>
 <real>1.0</real>
 <real>1.0</real>
 <real>1.0</real>
 <real>0.15</real>
 <real>0.15</real>
 <real>0.15</real>
 </array>
 <array>
 <string>stencil</string>
 <string>vignette</string>
 <string>add</string>
 <real>0.0</real>
 <real>0.4</real>
 <real>-0.5</real>
 <real>0.5</real>
 <real>0.20</real>
 <real>20.0</real>
 </array>
 <array>
 <string>colorize</string>
 <real>1.0</real>
 <real>1.0</real>
 <real>1.0</real>
 <real>0.6</real>
 <real>0.0</real>
 <real>0.0</real>
 </array>
 <array>
 <string>stencil</string>
 <string>vignette</string>
 <string>add</string>
 <real>0.0</real>
 <real>0.5</real>
 <real>-0.7</real>
 <real>0.7</real>
 <real>0.10</real>
 <real>20.0</real>
 </array>
 <array>
 <string>colorize</string>
 <real>1.0</real>
 <real>1.0</real>
 <real>1.0</real>
 <real>0.6</real>
 <real>0.6</real>
 <real>0.0</real>
 </array>
 <array>
 <string>stencil</string>
 <string>vignette</string>
 <string>add</string>
 <real>0.0</real>
 <real>0.5</real>
 <real>0.5</real>
 <real>-0.5</real>
 <real>0.10</real>
 <real>20.0</real>
 </array>
 <array>
 <string>colorize</string>
 <real>1.0</real>
 <real>1.0</real>
 <real>1.0</real>
 <real>0.7</real>
 <real>0.0</real>
 <real>0.0</real>
 </array>
 <array>
 <string>stencil</string>
 <string>vignette</string>
 <string>add</string>
 <real>0.0</real>
 <real>0.5</real>
 <real>0.6</real>
 <real>-0.6</real>
 <real>0.05</real>
 <real>20.0</real>
 </array>
 <array>
 <string>colorize</string>
 <real>1.0</real>
 <real>1.0</real>
 <real>1.0</real>
 <real>0.7</real>
 <real>0.0</real>
 <real>0.0</real>
 </array>
 <array>
 <string>stencil</string>
 <string>vignette</string>
 <string>add</string>
 <real>0.0</real>
 <real>0.5</real>
 <real>0.4</real>
 <real>-0.4</real>
 <real>0.025</real>
 <real>20.0</real>
 </array>
 <array>
 <string>colorize</string>
 <real>1.0</real>
 <real>1.0</real>
 <real>1.0</real>
 <real>0.7</real>
 <real>0.0</real>
 <real>0.0</real>
 </array>
 </array>
</llsd>
=== Light Leak ===
This filter imitates an old fashion light leak "happy accident":
<llsd>
 <array>
 <array>
 <string>linearize</string>
 <real>0.01</real>
 <real>1.0</real>
 <real>1.0</real>
 <real>1.0</real>
 </array>
 <array>
 <string>brighten</string>
 <real>0.1</real>
 <real>1.0</real>
 <real>1.0</real>
 <real>1.0</real>
 </array>
 <array>
 <string>stencil</string>
 <string>gradient</string>
 <string>add</string>
 <real>1.0</real>
 <real>0.0</real>
 <real>-1.0</real>
 <real>1.0</real>
 <real>1.0</real>
 <real>-1.0</real>
 </array>
 <array>
 <string>colorize</string>
 <real>1.0</real>
 <real>1.0</real>
 <real>1.0</real>
 <real>0.1</real>
 <real>0.1</real>
 <real>0.0</real>
 </array>
 <array>
 <string>stencil</string>
 <string>vignette</string>
 <string>add</string>
 <real>0.0</real>
 <real>1.0</real>
 <real>-1.0</real>
 <real>1.0</real>
 <real>1.5</real>
 <real>5.0</real>
 </array>
 <array>
 <string>colorize</string>
 <real>1.0</real>
 <real>1.0</real>
 <real>1.0</real>
 <real>0.8</real>
 <real>0.0</real>
 <real>0.0</real>
 </array>
 <array>
 <string>stencil</string>
 <string>vignette</string>
 <string>add</string>
 <real>0.0</real>
 <real>1.0</real>
 <real>-1.0</real>
 <real>1.0</real>
 <real>1.0</real>
 <real>5.0</real>
 </array>
 <array>
 <string>colorize</string>
 <real>1.0</real>
 <real>1.0</real>
 <real>1.0</real>
 <real>0.8</real>
 <real>0.8</real>
 <real>0.0</real>
 </array>
 </array>
</llsd>
=== The Matrix ===
Here is an effect inspired by The Matrix:
<llsd>
 <array>
 <array>
 <string>grayscale</string>
 </array>
 <array>
 <string>linearize</string>
 <real>0.1</real>
 <real>1.0</real>
 <real>1.0</real>
 <real>1.0</real>
 </array>
 <array>
 <string>posterize</string>
 <real>50.0</real>
 <real>1.0</real>
 <real>1.0</real>
 <real>1.0</real>
 </array>
 <array>
 <string>gradient</string>
 </array>
 <array>
 <string>screen</string>
 <string>line</string>
 <real>0.025</real>
 <real>90.0</real>
 </array>
 <array>
 <string>colorize</string>
 <real>0.0</real>
 <real>1.0</real>
 <real>0.0</real>
 <real>0.1</real>
 <real>0.2</real>
 <real>0.2</real>
 </array>
 <array>
 <string>blur</string>
 </array>
 </array>
</llsd>
=== Miniature ===
Here is a miniature or "tilt shift" effect:
<llsd>
 <array>
 <array>
 <string>linearize</string>
 <real>0.02</real>
 <real>1.0</real>
 <real>1.0</real>
 <real>1.0</real>
 </array>
 <array>
 <string>contrast</string>
 <real>1.02</real>
 <real>1.0</real>
 <real>1.0</real>
 <real>1.0</real>
 </array>
 <array>
 <string>saturate</string>
 <real>1.2</real>
 </array>
 <array>
 <string>stencil</string>
 <string>vignette</string>
 <string>blend</string>
 <real>0.0</real>
 <real>0.25</real>
 <real>0.0</real>
 <real>0.0</real>
 <real>0.25</real>
 <real>2.0</real>
 </array>
 <array>
 <string>sharpen</string>
 </array>
 <array>
 <string>stencil</string>
 <string>gradient</string>
 <string>blend</string>
 <real>1.0</real>
 <real>0.0</real>
 <real>0.0</real>
 <real>-1.0</real>
 <real>0.0</real>
 <real>-0.25</real>
 </array>
 <array>
 <string>blur</string>
 </array>
 <array>
 <string>blur</string>
 </array>
 <array>
 <string>blur</string>
 </array>
 <array>
 <string>blur</string>
 </array>
 <array>
 <string>blur</string>
 </array>
 <array>
 <string>blur</string>
 </array>
 <array>
 <string>blur</string>
 </array>
 <array>
 <string>blur</string>
 </array>
 <array>
 <string>blur</string>
 </array>
 <array>
 <string>blur</string>
 </array>
 <array>
 <string>stencil</string>
 <string>gradient</string>
 <string>blend</string>
 <real>1.0</real>
 <real>0.0</real>
 <real>0.0</real>
 <real>1.0</real>
 <real>0.0</real>
 <real>0.25</real>
 </array>
 <array>
 <string>blur</string>
 </array>
 <array>
 <string>blur</string>
 </array>
 <array>
 <string>blur</string>
 </array>
 <array>
 <string>blur</string>
 </array>
 <array>
 <string>blur</string>
 </array>
 <array>
 <string>blur</string>
 </array>
 <array>
 <string>blur</string>
 </array>
 <array>
 <string>blur</string>
 </array>
 <array>
 <string>blur</string>
 </array>
 <array>
 <string>blur</string>
 </array>
 </array>
</llsd>
This code would be put into a file named Miniature.xml and put into the app_settings/filter folder of the viewer.
=== Pixelate ===
This filter reduces the color palette dramatically to create an old "8 bits" feel:
<llsd>
 <array>
 <array>
 <string>linearize</string>
 <real>0.01</real>
 <real>1.0</real>
 <real>1.0</real>
 <real>1.0</real>
 </array>
 <array>
 <string>posterize</string>
 <real>5.0</real>
 <real>1.0</real>
 <real>1.0</real>
 <real>1.0</real>
 </array>
 </array>
</llsd>
=== Toy Camera ===
Here is an example filter for a "toy camera" effect:
<llsd>
 <array>
 <array>
 <string>stencil</string>
 <string>vignette</string>
 <string>fade</string>
 <real>0.0</real>
 <real>1.0</real>
 <real>0.0</real>
 <real>0.0</real>
 <real>1.2</real>
 <real>3.0</real>
 </array>
 <array>
 <string>linearize</string>
 <real>0.05</real>
 <real>1.0</real>
 <real>1.0</real>
 <real>1.0</real>
 </array>
 <array>
 <string>grayscale</string>
 </array>
 <array>
 <string>contrast</string>
 <real>1.1</real>
 <real>1.0</real>
 <real>1.0</real>
 <real>1.0</real>
 </array>
 <array>
 <string>stencil</string>
 <string>vignette</string>
 <string>blend</string>
 <real>1.0</real>
 <real>0.0</real>
 <real>0.0</real>
 <real>0.0</real>
 <real>0.5</real>
 <real>2.0</real>
 </array>
 <array>
 <string>blur</string>
 </array>
 </array>
</llsd>
This code would be put into a file named ToyCamera.xml and put into the app_settings/filter folder of the viewer.
=== Video ===
This filter looks kind of like a low-resolution CRT video screen:
<llsd>
 <array>
 <array>
 <string>linearize</string>
 <real>0.0</real>
 <real>1.0</real>
 <real>1.0</real>
 <real>1.0</real>
 </array>
 <array>
 <string>darken</string>
 <real>0.15</real>
 <real>1.0</real>
 <real>1.0</real>
 <real>1.0</real>
 </array>
 <array>
 <string>stencil</string>
 <string>uniform</string>
 <string>add</string>
 <real>0.0</real>
 <real>0.5</real>
 </array>
 <array>
 <string>screen</string>
 <string>line</string>
 <real>0.02</real>
 <real>0.0</real>
 </array>
 <array>
 <string>gamma</string>
 <real>0.25</real>
 <real>1.0</real>
 <real>1.0</real>
 <real>1.0</real>
 </array>
 <array>
 <string>blur</string>
 </array>
 <array>
 <string>blur</string>
 </array>
 </array>
</llsd>
== References ==
* Alpha Compositing
* Convolution Kernel
* Ron Brinkman : The Art and Science of Digital Compositing
* Thomas Porter and Tom Duff : Compositing Digital Images (SIGGRAPH 84)