Image Manipulation from Allegro CL using ImageMagick

Introduction

ImageMagick (www.imagemagick.org) is a suite of tools allowing you to create, edit, and compose bitmap images. It provides powerful command-line tools to this effect, but also provides a number of APIs to make the same features available programmatically.

Binary releases of the ImageMagick suite (including libraries) can be downloaded here:

www.imagemagick.org/script/binary-releases.php

Using SWIG (www.swig.org), we have generated the foreign wrappers to the MagickWand API (www.imagemagick.org/script/magick-wand.php), which can be downloaded from

github.com/franzinc/cl-imagemagick

Running the Example

In the following example, we make use of the MagickWand API to the ImageMagick library to dynamically generate a thumbnail index for an image repository on a web server.

In demo/US_State_Flags are png files of the 50 states retrieved from the following site:

www.wpclipart.com/flags/US_State_Flags/index.html

The images here vary in size, but none are smaller than roughly 225x225. The example below publishes the directory containing these images. However, in place of an index.html file that shows the contents of this directory, it will dynamically generate an index of the contents of the directory, creating a small thumbnail of each file.

To run the code

  1. Make sure you have the ImageMagick libraries installed on your system. We currently pre-generate foreign-function definitions on both the linux86 and windows platforms. If you wish to try this demo on another platform, please see the README file contained in the top-level of the download.
  2. Download the cl-imagemagick repository from github.
  3. cd into the directory in which you've downloaded cl-imagemagick
  4. start Allegro CL
  5. :cd demo
  6. :cl demo-thumb.cl

    This will compile and load the demo. To run it you'll be starting a server on the local machine via the function start-server. This function takes an optional argument indicating the port on which the server listens. It default to 8080. If that is acceptable, you can start the server by evaluating

    (start-server)
    

    Else, you should specify a port. for example, to start a server on port 9000, evaluate:

    (start-server 9000)
    

    The server should now be up and running.

  7. Assuming you used the default port, start up a web browser and navigate to http://localhost:8080/. This should show a link, that when clicked, will display thumbnails of each of the US State Flags. If you click on any flag, the full image should be displayed. Here is a portion of the web page:
  8. In your lisp session, check the value of *thumb-dims*, it should be "100x100". Try changing this value to some other set of dimensions. E.g. do (setq *thumb-dims* "50x50") and then refresh the page with the thumbnail view. You should see the size of the thumbnails in the index change accordingly. Here is a portion of the webpage with the smaller thumbnails:

Example Code

The full code for this demo can be found in demo/demo-thumb.cl and the files that it loads. Below, we look at a few details as relates to the ImageMagick API.

The code for setting up the server and starting it can be found in demo/demo-thumb.cl. The main workhorse in this file is the function publish-thumbnail.

(defun publish-thumbnails ()
  (ensure-directories-exist *thumb-tmp*)
  (dolist (i (directory *flag-dir*))
    (let ((name (file-namestring i))
	  (in-file (enough-namestring i))
	  (out-file (sys:make-temp-file-name "thumb" *thumb-tmp*))
	  (mime-type (net.aserve::lookup-mime-type i)))
      (publish :path (format nil "/thumbs/~a" name)
	       :content-type mime-type
	       :format :binary
	       :function #'(lambda (req ent)
			     (MagickWand::xform-image in-file *thumb-dims* out-file)
			     (with-http-response (req ent)
			       (with-http-body (req ent)
				 (with-open-file (thumb out-file :direction :input)
				   (let (byte)
				     (while (setf byte (read-byte thumb nil nil))
				       (write-byte byte *html-stream*))))
				 (delete-file out-file))))))))

For each file in *thumb-dir*, an entity is published which calls MagickWand:xform-image to resize an existing image while maintaining aspect ratio, and write it to a temp file, and then emit it in response to an http request. Lastly, for both tidiness and to ensure that the thumbnail is generated each time, the temp file is removed.

The function MagickWand::xform-image is a helper routine defined in demo/demo-resize.cl that makes use of the MagickWand binding to have ImageMagick do the work of creating and writing out the thumbnails.

(defun xform-image (in-file new-dims out-file)
  (let ((wand (NewMagickWand))
	images-modified)
    (when (= (MagickReadImage wand in-file)
	     MagickFalse)
      (error "unable to read image file ~s." in-file))
    (MagickResetIterator wand)
    (let (xforms)
      (while (/= (MagickNextImage wand) MagickFalse)
	(push (MagickTransformImage wand "0x0" new-dims) xforms)
	(setf images-modified t))
      (if* images-modified
       then (when (= (MagickWriteImages (first xforms) out-file MagickTrue)
		     MagickFalse)
	      (error "unable to write to file ~s." out-file))
       else (error "No images found in input file ~s." in-file)))
    images-modified))

For more information on what the MagickWand API routines do, you are referred to the "MagickWand API" (www.imagemagick.org/script/magick-wand.php)

A Note on the SWIG Generated Interface

In the downloaded distribution, the files MagickWand.cl found in the architecture specific subdirectories of windows/ and linux86/ were generated using the Makefile found in this distribution by running an up-to-date SWIG on MagickWand.i. If you wish to generate this interface on additional platforms, you can use this interface and the accompanying Makefile as a reference. See the README file in the downloaded distribution for more details.

If you have any questions about the interface or suggestions, please email us at support@franz.com.

Copyright © 2023 Franz Inc., All Rights Reserved | Privacy Statement Twitter