Skip to content

jcupitt/gegl-vips

Repository files navigation

gegl-vips
=========

This is a quick proof-of-concept hack of gegl-0.1.6 to use libvips as the
backend. 

You need to have the development version of libvips installed (7.25), plus
babl-0.1.4 or later. Then configure ; make ; make install as usual.

Test program 
============

------
#include <stdio.h>
#include <stdlib.h>

#include <gegl.h>

int
main (int argc, char **argv)
{
  GeglNode *gegl, *load, *crop, *scale, *sharp, *save;

  g_thread_init (NULL);
  gegl_init (&argc, &argv);

  if (argc != 3) 
    {           
      fprintf (stderr, "usage: %s file-in file-out\n", argv[0]);
      exit (1);
    }
        
  gegl = gegl_node_new ();
        
  load = gegl_node_new_child (gegl,
                              "operation", "gegl:load",
                              "path", argv[1], 
                              NULL);
  crop = gegl_node_new_child (gegl, 
                              "operation", "gegl:crop",
                              "x", 100.0,
                              "y", 100.0,
                              "width", 4800.0, 
                              "height", 4800.0, 
                              NULL);
  scale = gegl_node_new_child (gegl,
                               "operation", "gegl:scale",
                               "x", 0.9,
                               "y", 0.9,
                               "filter", "linear", 
                               "hard-edges", FALSE, 
                               NULL);
  sharp = gegl_node_new_child (gegl,
                               "operation", "gegl:unsharp-mask",
                               "std-dev", 1.0, // diameter 7 mask in gegl
                               NULL);
  save = gegl_node_new_child (gegl,
                              "operation", "gegl:save",
                              "path", argv[2], 
                              NULL);
  gegl_node_link_many (load, crop, scale, sharp, save, NULL);
 
  //gegl_node_dump( gegl, 0 );

  gegl_node_process (save);

  //gegl_node_dump( gegl, 0 );
                
  g_object_unref (gegl);

  gegl_exit ();

  return (0);
}
---

ie. load, crop 100 px off the edges (you need to give it a 5k x 5k RGB image),
bilinear 10% shrink, sharpen, save. See:

	http://www.vips.ecs.soton.ac.uk/index.php?title=Speed_and_Memory_Use

for results with other libraries.

Compile with:

   gcc -g -Wall gegl.c `pkg-config gegl --cflags --libs`

and run with something like:

$ time ./a.out wtc_small.png wtc2.png

or 

$ time ./a.out wtc_small.tif wtc2.tif

(gegl-vips allows most image file formats)

Results
=======

On my laptop I get the following run times:

        gegl-0.1.6, 
                default settings 
                        70s r, 39 u
                export BABL_TOLERANCE=0.004
                        65s r, 33s u
                export BABL_TOLERANCE=0.004
                export GEGL_CACHE_SIZE=1000 (my laptop only has 2gb)
                        53 r, 34s u (memuse peaks at 1.0gb)
                + 8-bit PNG output
                        47s r, 30s u

        gegl-vips 
                match gegl processing (don't forget to append :1 to the png 
                output name to force compression == 1, the gegl default), 
                        19s r, 23s u
                + 8-bit output
                        10s r, 14s u
                + faster unsharp
                        7.8s r, 10.6s u
                + skip alpha
                        6.5s r, 8.5s u
                + tif files
                        3.0s r, 3.8s u
                + 8-bit path
                        1.0s r, 1.4s u

Results #2
==========

On a desktop machine with more RAM and a fast hard drive, but slightly
slower 2 x 2.7 GHz Operton 254 processors, I get:

        gegl-0.1.6, 
                default settings 
                        50s r, 44s u
                export BABL_TOLERANCE=0.004
                        46s r, 39s u
                export BABL_TOLERANCE=0.004
                export GEGL_SWAP=RAM
                        41s r, 39s u (memuse peaks at 1.8gb)
                + 8-bit PNG output
                        38s r, 36s u

        gegl-vips 
                match gegl processing (don't forget to append :1 to the png 
                output name to force compression == 1, the gegl default), 
                        13.8s r, 22.1s u
                + 8-bit output
                        8.6s r, 14.4s u
                + faster unsharp
                        7.2s r, 11.2s u
                + skip alpha
                        5.9s r, 9.0s u
                + tif files
                        2.9s r, 5.0s u
                + 8-bit path
                        1.4s r, 1.9s u


Conclusions
===========

In the most comparable case, gegl-vips needs about 15s less CPU
but finishes 25s earlier, due to on-by-default threading. The difference is
more marked on the laptop where the slow harddrive hurts the tile cache.

Only saving 8-bit PNGs saves 5s.

Faster unsharp, save 1.5s.

Dropping the alpha channel (not needed in this case) saves 1.3s.

Using the faster libtif saves 3s.

Using an 8-bit path throughout saves another 1.5s.

How it works
============

- add this to every GeglNode:

	VipsImage *vips_image;
	guint64 vips_hash;

- in every operation (I've only done load/save/crop/affine/usharp) change the
  GeglOperation::process() member to be something like this (this is crop):

  GeglNode *input;
  guint64 hash;

  // make sure input is ready
  input = gegl_operation_get_source_node (operation, "input");
  if (!input || !input->vips_image)
    return;

  // calculate a hash of the input args
  hash = 0;
  hash = GEGL_VIPS_HASH_POINTER (hash, input->vips_image);
  hash = GEGL_VIPS_HASH_DOUBLE (hash, o->x);
  hash = GEGL_VIPS_HASH_DOUBLE (hash, o->y);
  hash = GEGL_VIPS_HASH_DOUBLE (hash, o->width);
  hash = GEGL_VIPS_HASH_DOUBLE (hash, o->height);

  // if args have changed, or this is the first time we've run
  if (!node->vips_image || node->vips_hash != hash)
    {
      VipsImage *image;

      // perform the equivalent vips operation
      image = vips_image_new ("p");
      if (im_extract_area (input->vips_image, image,
			   o->x, o->y, o->width, o->height))
	{
	  gegl_vips_error ("crop");
	  g_object_unref (image);
	  return;
	}

      // install the new image on the node, update the hash
      if (node->vips_image)
        {
	  g_object_unref (node->vips_image);
	  node->vips_image = NULL;
	}
      node->vips_image = image;
      node->vips_hash = hash;
    }

- change gegl_node_process() to simply walk the graph, bottom-up, calling all 
  the prepare() members 

... and that's about it, very simple.

TODO:

- add a display sink
- try a couple more operations
- maybe get hello-world.c working
- vips needs area invalidation before we can support destructive operations

About

quick hack of gegl to use libvips as the backend

Resources

License

LGPL-3.0, GPL-3.0 licenses found

Licenses found

LGPL-3.0
COPYING.LESSER
GPL-3.0
COPYING

Stars

Watchers

Forks

Releases

No releases published

Packages

No packages published

Languages