Update: It appears the wordpress/LJ cross poster can’t handle indentation. I suggest you just download the full source from the link at the bottom.
Update 2: Fixed it (whit a bit of a hack).
This time we’ll learn how to use one of Firtree’s most powerful features: kernels. Firtree includes a little C-like language which can be used to specify image processing operations. In essence, it is a function that is called once per output pixel and is asked to compute the colour of that pixel. The kernel can, itself, make use of samplers just like a rendering engine.
Under the covers Firtree compiles all of your kernel functions (and it’s the samplers it uses) into one optimised machine code routine.
Firstly, we’ll write a Python function which can help us compile kernels. It takes a string containing some kernel source and returns a kernel and a sampler for that kernel. It also prints out an error log if you made a mistake in the syntax:
| 01 | <pre>def compile_kernel(source): |
| 02 | """ |
| 03 | A simple function which compiles a kernel, checks that the |
| 04 | compilation succeeded and returns a pair containing the kernel and a |
| 05 | sampler for it. |
| 06 | """ |
| 07 | |
| 08 | kernel = ft.Kernel() |
| 09 | kernel.compile_from_source(source) |
| 10 | if not kernel.get_compile_status(): |
| 11 | print("Error compiling kernel:") |
| 12 | print("\n".join(kernel.get_compile_log())) |
| 13 | return |
| 14 | |
| 15 | kernel_sampler = ft.KernelSampler() |
| 16 | kernel_sampler.set_kernel(kernel) |
| 17 | |
| 18 | return (kernel, kernel_sampler)</pre> |
Now let’s actually create a kernel. I’m going to use the example of a desaturate kernel, essentially converting a colour image into a black and white one:
| 01 | <pre># Create a desaturate kernel. |
| 02 | (desat, desat_sampler) = compile_kernel(""" |
| 03 | kernel vec4 desaturate(sampler src) |
| 04 | { |
| 05 | vec4 src_colour = unpremultiply( sample(src, samplerCoord(src)) ); |
| 06 | float luminance = dot(src_colour, vec4(0.299,0.587,0.114,0)); |
| 07 | return premultiply( |
| 08 | vec4(luminance,luminance,luminance,src_colour.a) |
| 09 | ); |
| 10 | } |
| 11 | """)</pre> |
The kernel language itself should be familiar to anyone who has programmed in C, GLSL or CoreImage. I’ll explain some of the functions we use:
- [un]premultiply – Firtree uses a pre-multiplied representation internally. That is to say that the red, green and blue components of a colour are pre-multiplied by the alpha value. These functions can be used to undo and redo this operation.
- sample – Call a sampler. It takes two parameters: one is the sampler to sample from and the second is a 2d vector specifying where to do so.
- samplerCoord – Samplers have associated with them a co-ordinate transform. This function returns the co-ordinate in the sampler’s co-ordinate system of the current pixel.
- dot – Perform a dot-product between two vectors. In this case, it takes the right combination of red, green and blue components to return a luminance value.
We now need to tell the kernel what sampler to use for ’src’. We do this with one line of Python:
| 1 | # Wire the lena sampler into the desaturate kernel. |
| 2 | desat['src'] = lena_sampler |
Finally we need to tell the CPU renderer to use the desat_sampler sampler instead of the Lena one:
| 1 | <pre># Use the engine to render the output. |
| 2 | engine.set_sampler(desat_sampler) |
| 3 | engine.render_into_cairo_surface( |
| 4 | lena_sampler.get_extent(), # what area of the input to render |
| 5 | output_surface # into what |
| 6 | )</pre> |
The output image is what we wanted, a desaturated version of Lena.
If one wanted to check that Firtree is indeed doing some work behind the scenes, we can get it to print out the compiled assembler for the function. Simply add the following line:
| 1 | print(ft.debug_dump_cpu_renderer_asm(engine, ft.FORMAT_RGBA32)) |
Compating the resulting output to the kernel language input clearly shows how much easier it is writing image processing kernels in Firtree! :)
Next time, we’ll see how to chain kernels together and let Firtree worry about the details.
The full source code is available.