epeg is a super-fast library for resizing JPEGs. It offers an improvement of at least an order of magnitude over other known libraries like ImageMagick. Here are some numbers indicative of its speed:
$ time ./tclepeg test.jpeg test.jpg real 0m0.063s user 0m0.060s sys 0m0.000s $ time convert -resize 426x640 test.jpeg test.jpg real 0m1.158s user 0m1.048s sys 0m0.052s
In the above example the improvement is 18 fold. test.jpeg is a 2592x3888 image that is resized to a 426x640 one using the epeg library. Convert is ImageMagick's convert utility. It took epeg 63 msec to do the conversion against 1158 msec for ImageMagick. These timings include reading/writing to disk. tclepeg takes about 1msec to do the conversion on the same system.
tclepeg is a first effort to bring epeg's functionality to TCL by means of a binary linux library that can be loaded into a TCL program. Size options are not yet parsed and the quality value is fixed, but this initial C code is posted here with the hope that more competent tclers may step in to improve it.
The library provides a TCL command epeg that takes as an argument JPEG image data. In the code below the output is fixed to 320x240px, 75% JPEG quality. See epeg for more. Compiles on an Ubuntu Linux machine.
/* * tclepeg.c -- A fast JPEG resize (reduction) Tcl C extension based on the epeg library * For a source repository of epeg look in : * http://maemo.gitorious.org/maemo-af/epeg * http://svn.enlightenment.org/svn/e/OLD/epeg/ * * Adapted to tcl by dzach * */ #include <tcl.h> #include <stdlib.h> #include <string.h> #include "Epeg.h" static int Tclepeg_Cmd(ClientData cdata, Tcl_Interp *interp, int objc, Tcl_Obj *const objv[]) { if(objc < 2) { Tcl_SetObjResult(interp, Tcl_NewStringObj("wrong # args. Should be epeg ?option value ...? jpegdata", -1)); return TCL_ERROR; } int i, w=320, h=240, q=50, sizei, sizeo; unsigned char *imi, *imo, *opt, *val; Epeg_Image *im; // set user options for (i=1; i<objc-2; i++) { opt = Tcl_GetString(objv[i]); if (strncmp(opt, "-w", 2) == 0) { w = atof(Tcl_GetString(objv[++i])); } else if (strncmp(opt, "-h", 2) == 0) { h = atof(Tcl_GetString(objv[++i])); } else if (strncmp(opt, "-q", 2) == 0) { q = atof(Tcl_GetString(objv[++i])); } else { printf("option %s is errelevant\n",Tcl_GetString(objv[i])); } } imi = Tcl_GetByteArrayFromObj(objv[objc-1], &sizei); if ((imo = (unsigned char *)malloc(sizei)) == NULL) { Tcl_SetResult(interp,NULL,NULL); return TCL_ERROR; } // now do image resize with epeg // recreate image in memory if ( (im = epeg_memory_open(imi,sizei)) == NULL) { Tcl_SetResult(interp,NULL,NULL); return TCL_ERROR; } epeg_decode_size_set (im, w, h); epeg_quality_set (im, q); epeg_thumbnail_comments_enable (im, 1); epeg_memory_output_set (im, &imo, &sizeo); if (epeg_encode(im) != 0) { Tcl_SetResult(interp,NULL,NULL); return TCL_ERROR; } epeg_close (im); // resize allocated output image memory to free excessive memory if ((imo = realloc(imo, sizeo)) == NULL) { Tcl_SetResult(interp,NULL,NULL); return TCL_ERROR; } // prepare tcl command output Tcl_Obj *timo = Tcl_NewByteArrayObj(imo, sizeo); Tcl_SetObjResult(interp, timo); free(imo); return TCL_OK; } /* * Tclepeg_Init -- Called when Tcl loads the extension. */ int DLLEXPORT Tclepeg_Init(Tcl_Interp *interp) { if (Tcl_InitStubs(interp, TCL_VERSION, 0) == NULL) { return TCL_ERROR; } /* changed this to check for an error - GPS */ if (Tcl_PkgProvide(interp, "epeg", "0.1") == TCL_ERROR) { return TCL_ERROR; } Tcl_CreateObjCommand(interp, "epeg", Tclepeg_Cmd, NULL, NULL); return TCL_OK; }
Compile using:
gcc -shared tclepeg.c libepeg.a -ljpeg -o tclepeg.so
libepeg.a and libjpeg have to be in the same directory with tclepeg.c or else their path should be changed so that the compiler knows where to find them.
Usage example:
load ./tclepeg.so # read in a JPEG image set fd [open test.jpeg] fconfigure $fd -translation binary set img [read $fd] close $fd # write reduced image set fd [open out.jpeg w] fconfigure $fd -translation binary # let tclepeg do the work puts -nonewline $fd [epeg -width 320 -height 240 -quality 50 $img] close $fd
provided the tclepeg.so library is in the present working directory directory.
You are welcome to improve the above code and post it here.