Python, C and C++ efficiency on JPG thumbnail creation

Check out the new site at https://rkblog.dev.

To create thumbnails of image files we can use few libraries like: But there are also smaller libraries like Epeg from the development branch of Enlightment (E17) which uses only libjpeg to make thumbnails. Now which of them will be faster? And if we make Python modules from EPEG library how much slower they will be than the original C runtime?

Epeg

We can download EPEG source code and compile it without e17 (needs only libjpeg). After compile and install there will be an app called "epeg", but we will make our own, with epeg.c file like this:
#include <stdlib.h>
#include <unistd.h>

#include <Epeg.h>

#define PROGRAM "ethumber"


int
main(int argc, char **argv)
{
    Epeg_Image * image;
    int ch, height, quality, width;
    char *input, *output;

        /* Default values - can be overidden */
    quality     = 80;
    height      = 125;
    width       = 125;
    output      = "thumb.jpg"; 
 while ((ch = getopt(argc, argv, "h:i:o:q:w:")) != -1) {
        switch (ch) {
        case 'h':
                height = atoi(optarg);
                break;
        case 'i':
                input = optarg;
                break;
        case 'o':
                output = optarg;
                break;
        case 'q':
                quality = atoi(optarg);
                break;
        case 'w':
                width = atoi(optarg);
                break;
        default:
                exit (0);
        }
    }

 image = epeg_file_open(input);

    if (!image) {
        exit (0);
    }

    epeg_decode_size_set(image, width, height);
    epeg_quality_set    (image, quality);
    epeg_file_output_set(image, output);
    epeg_encode(image);
    epeg_close(image);


    return 0;
}
To compile it use:
gcc epeg.c -o epeg `epeg-config --cflags --libs`
And the compile "epeg" binary can be used like this:
./epeg -i file.jpg -o thumb.jpg -h heigth -w width -q quality

PIL

Python Imaging Library - the thing you would use in Python to make thumbs. It also has some filters like ANTIALIAS and SHARPEN which increase the quality of the thumbnails. Test script looks like this:
from sys import argv
import Image
import ImageFilter

quality = 80
width = 125
heigth = 125
source = False
thumb = False
sharp = False

if '-q' in argv:
	quality = int(argv[argv.index('-q')+1])

if '-h' in argv:
	heigth = int(argv[argv.index('-h')+1])

if '-w' in argv:
	width = int(argv[argv.index('-w')+1])

if '-i' in argv:
	source = argv[argv.index('-i')+1]

if '-o' in argv:
	thumb = argv[argv.index('-o')+1]

if '-s' in argv:
	sharp = True

if source and thumb:
	im = Image.open(source)
	im.thumbnail((width, heigth), Image.ANTIALIAS)
	if sharp:
		im.filter(ImageFilter.SHARPEN)
	im.save(thumb, "JPEG",quality=quality)

Benchmark settings

  • I've measured time (bash/time) needed to make thumbs from 18 big JPEG files (35 MB).
  • For every tested library a simple bash script was used. For imagemagick:
    for plik in sources/*
    do
    convert $plik -thumbnail 250x187 -quality 80 thumbs/ithum$plik
    done
    
  • EDIT: in case of imagemagick also -size param should be set so imagemagick would only read needed part of file to create the thumb, and not the whole.
  • All libraries were ordered to make the same thumbs with the same settings (quality, width, heigth).

Making Python modules from EPEG in C and C++/BOOST

We can use Python standard API in C to make a Python module. The code would look like this:
#include <stdlib.h>
#include <unistd.h>
#include <Python.h>
#include <Epeg.h>

static PyObject *
epg_thumbnail(PyObject *self, PyObject *args)
{
	Epeg_Image * image;
	int ch, height, quality, width;
	char *input, *output;
	
	if (!PyArg_ParseTuple(args, "ssiii", &input, &output, &quality, &height, &width))
		return NULL;
	
	image = epeg_file_open(input);
	
	if (!image) {
		exit (0);
	}
	
	epeg_decode_size_set(image, width, height);
	epeg_quality_set    (image, quality);
	epeg_file_output_set(image, output);
	epeg_encode(image);
	epeg_close(image);

	
	Py_BuildValue("i", 1);
}

static PyMethodDef epg_methods[] = {
	{"thumbnail", (PyCFunction)epg_thumbnail, METH_VARARGS, "Funkcja epeg."},
	{NULL, NULL, 0, NULL}
};

PyMODINIT_FUNC initepg() {
	Py_InitModule3("epg", epg_methods, "Miniatury z Epeg");
}
And compiled with (-fPIC needed on 64 bit Linux):
gcc -shared -I /usr/include/python2.5/ epg.c -o epg.so -fPIC `epeg-config --cflags --libs`

We can also use C++ and BOOST/Python. The code would look like this:
#include <boost/python.hpp>

#include <stdlib.h>
#include <unistd.h>
#include <Epeg.h>

int
thumbnail(char *input, char *output, int quality, int height, int width)
{
    Epeg_Image * image;
    int ch;

    image = epeg_file_open(input);

    if (!image) {
        exit (0);
    }

    epeg_decode_size_set(image, width, height);
    epeg_quality_set    (image, quality);
    epeg_file_output_set(image, output);
    epeg_encode(image);
    epeg_close(image);


    return 0;
}


BOOST_PYTHON_MODULE(bstepg)
{
    using namespace boost::python;
    def("thumbnail", thumbnail);
}
Compiled with (-fPIC needed on 64 bit Linux):
gcc -lboost_python -shared -I /usr/include/python2.5/ bstepg.cpp -o bstepg.so -fPIC `epeg-config --cflags --libs` -I/usr/include/boost

The results

  • EPEG - 4,15s
  • PyEPEG C - 4,54s
  • PyEPEG BOOST - 4,58s
  • PIL - 11,61s
  • IMAGEMAGICK - 29,32s
  • IMAGEMAGICK -size - 10,9s (edit/update)
epeg, python, boost, imagemagick, pil
If we use a Python module of a C "application" we loose here about 10% of it's speed, which isn't much compared to PIL, or IMAGEMAGICK "convert". In case of EPEG there is a problem with no anti-alias filters, and without that (and sharpen) some thumbnails will be fuzzy and unacceptable :)
Description Epeg PIL ImageMagick
Big image
1,3 MB, 2560x1600
thumb made with epeg
8,5kB
thumb made with PIL
8,1kB
thumb made with ImageMagick
10,1kB
Small image
27 kB, 300x506
thumb made with epeg
8,5kB
thumb made with PIL
8,5kB
thumb made with ImageMagick
8,3kB
RkBlog

Web development, 4 August 2008


Check out the new site at https://rkblog.dev.
Comment article
Comment article RkBlog main page Search RSS Contact