Archive for the ‘Firtree’ Category

Using Firtree from Python (part 2)

Tuesday, October 6th, 2009

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:

 Python |  copy code |? 
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:

 Python |  copy code |? 
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:

 Python |  copy code |? 
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:

 Python |  copy code |? 
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:

 Python |  copy code |? 
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.

Using Firtree from Python (part 1)

Tuesday, October 6th, 2009

I’m going to write a set of blog posts all about how to use Firtree from Python. This post is about the simplest thing that you can do with Firtree, load an image and write it back out again.

To get Firtree on Ubuntu Karmic, you can add the Firtree PPA to your system and install the python-firtree package.

Firtree is based around the concept of a sampler. A sampler in essence knows how to get the colour of a pixel given it’s location. The location is specified as a 2d vector of floats and the colour is a 4d vector of floats. The colour is made up of the red, green, blue and alpha components scaled into the rage zero to one.

Our first example will load our input, the ubiquitous Lena, into a Cairo surface and create a sampler which knows how to get data out of that surface:

 Python |  copy code |? 
1
import cairo
2
import pyfirtree as ft
3
 
4
# Firstly, load the lena image
5
lena_surface = cairo.ImageSurface.create_from_png('lena.png')
6
 
7
# Create a sampler for the surface
8
lena_sampler = ft.CairoSurfaceSampler()
9
lena_sampler.set_cairo_surface(lena_surface)

So far, so easy. Firtree also has the concept of a renderer which knows how to run over each pixel in an output, ask the sampler for the appropriate pixel colour and write it out. Firtree ships with a CPU based renderer which uses LLVM to compile your pipeline down into efficient code and run it over all the CPUs in your machine. Let’s make use of that:

 Python |  copy code |? 
01
# Create an output surface similar to the input
02
output_surface = cairo.ImageSurface(
03
	cairo.FORMAT_ARGB32,
04
	lena_surface.get_width(),
05
	lena_surface.get_height() )
06
 
07
# Create a CPU render engine.
08
engine = ft.CpuRenderer()
09
 
10
# Use the engine to write the input to the output.
11
engine.set_sampler(lena_sampler)
12
engine.render_into_cairo_surface(
13
	lena_sampler.get_extent(), 	# what area of the input to render
14
	output_surface 			# into what
15
	)
16
 
17
# Write the output
18
output_surface.write_to_png('output.png')

And that is it, you have written some code that loads an input file and writes it back out to another file. Next time you’ll learn how to make use of the main feature of Firtree: image processing kernels.

The source code for this example is available.

Firtree 0.0.5

Saturday, September 20th, 2008

The Firtree 0.0.5 release is out. This is going to be the first one which is actually advertised on the website since it is the first one that people might actually stand a good chance of being able to use properly :). See the Launchpad release page for more information.

Getting it

Firtree can be downloaded as a source tar-ball (thanks to Tony for pointing out this deficiency last time) and as a source or binary package for Ubuntu. Your primary sources are therefore:

Changelog

The big things in this release are the playground example which is similar to the Core Image Fun House application which ships with XCode on OSX, an optional software-only backend based on libOSMesa and an important bugfix to do with using swizzled vector types as lvalues.

  * NEW: Added the Python-based 'playground' example which shows how to make a
    generic filter-oriented image processing pipeline using Firtree.

  * NEW: ImageAccumulator and friends are more picky about there being an
    OpenGLContext 'current'. Slowly migrating all the examples over to 'best
    practise' of making sure that a context is created, even if it is only a
    NULL context.

  * NEW: Added a software-only rendering backend based on libOSMesa. This
    necessitated moving all the GL API entry points inside the OpenGLContext
    structure. Hence there *must* always be a context current when Firtree
    calls are made.

  * BUG FIX: In the kernel language, 'foo.x' et al are all lvalues which can
    be assigned to. The GLSL backend was translating swizzles in such a way
    that assignment to them was in effect a NOP.

  * API CHANGE: ImageProvders now have the semantic of 'creating' a
    BitmapImageRep on demand, even if this only involves returning an internal
    cached rep and incrementing its reference count. This brings it more into
    line with Firtree's object ownership conventions.

  * BUG FIX: ImageProviders have no way, currently, of signalling a 'dirty'
    bit on their BitmapImageRep and so each time they are used we now copy the
    image to the GPU. This is highly sub-optimal and a good signaling method
    must be found but it does ensure that changing the bitmap returned by an
    ImageProvider changes what ends up on screen.

  * BUG FIX: Blob lengths were never being set internally.

  * NEW: Added a skeleton API for converting pixel formats into the RGBA form
    expected by Firtree. Currently only supports 8-bit luminance values.

  * NEW: Long-standing feature need. When creating a kernel, syntax errors in
    the kernel source now throw an exception including the error.