Friday, July 26, 2013

PerlMagick Tutorial - Re-Published

I wrote this tutorial years ago, and it has been a popular little page.  As a result, with me closing down one website and moving to a blog-oriented online presence, I thought I'd better get it out there again for people to see.  Hence, a re-publish ensues.

Congratulations on taking a look at PerlMagick!  I am positive that you will find a system of image manipulation routines that are both powerful and easy to implement.  PerlMagick is a set of perl commands and routines that allows a developer or administrator the ease of using perl, but the power of using ImageMagick.  The tools are impressive on how they can be used in creating dynamic images, whether for business use in charting, or even just in CGI development.

In this tutorial, I will walk you through creating some of the tools typically used, and some of the simple methods.  We will discuss the following, respectively :
    Compiling ImageMagick and PerlMagick  This is where we discuss the "How's" of installing ImageMagick and PerlMagick.  There are a couple of quirks that occur when compiling, and we discuss why they occur.  
    Command-Line Tools  Once installed, I'll introduce you to some simple command line functions to use the tools that come with ImageMagick.  Once we understand these better, we can proceed to implement PerlMagick.  
    PerlMagick  A good understanding of ImageMagick, or even image manipulation is key to implementing the PerlMagick interface.  Here, we'll take a solid look at constructing a simple PerlMagick script.  
    File Reading and Writing  Reading and writing images the key to any image manipulation tool.  Because of that, we'll discuss the basic methods for reading and writing images, plus take a look at some quirks of working with files in a CGI environment.  
    Creating Images from Scratch  If developing a dynamic image utility, often we'll need to create images, without having a previous image to work from.  Here's how we do that.  
    Drawing Primitives  Adding lines, squares, circles, or other "primitives" using PerlMagick is often not discussed, and it's difficult to find sources on it.  I'll teach you how to draw primitives onto your images.  
    Writing on the Image  This is where we can get tricky.  Writing on images is a key principle of image manipulation.  
    Adding Transparency  Some image formats (such as .gif, and .png) allow transparency.  These formats are most often used in web design, allowing the image to properly show background colors and creating more seamless integration of HTML and graphics.  
    Creating Animations  Want to use the PerlMagick interface for handling logo creation?  I'll walk you through a quick example, plus describe a few of the required attributes and their effects on the resulting animation.  

Compiling ImageMagick and PerlMagick

If you can install from a package, do it.  Whomever has created the package has done some homework on setting it up properly for the specific operating system.  However, it may become apparent that a package is not available.  If this is the case, the procedure is simple and straight forward, and you don't need to be a programmer to do it.  Here's how :

First, download the source code (from http://www.imagemagick.org/).  Save this to the computer you wish to install ImageMagick on.  You will need to uncompress the file and open all of the source code, and change into the newly created directory.  This is done with the following commands (in bold) :
    [/tmp] $ tar -xzf ImageMagick-5.5.7-15.tar.gz
    [/tmp] $ cd ImageMagick-5.5.7
    
    
    
One of the pitfalls of compiling ImageMagick and PerlMagick is in the unification of the two packages.  When you download (from http://www.imagemagick.org/) the source code for ImageMagick, it includes the PerlMagick code as well.  But, in order to compile and install the Perl::Magick (the perl library name), the ImageMagick libraries must already be installed.  This can be done by configuring the entire package without PerlMagick, compiling and installing, and then re-configuring the entire package with PerlMagick.  Then you just have to compile the PerlMagick code and install that.  For example :
    [/tmp/ImageMagick-5.5.7] $ ./configure --without-perl --without-x \
    --with-windows-font-dir=/usr/share/fonts/defaults/TrueType
    configuring ImageMagick 5.5.7
    checking build system type... i686-pc-linux-gnu
    [... lots more output ...]
    LIBS     = -ltiff -lfreetype -ljpeg -lpng -lbz2 -lz -lpthread -lm
    [/tmp/ImageMagick-5.5.7] $ make && make install
    Making all in coders
    make[1]: Entering directory `/tmp/ImageMagick/ImageMagick-5.5.7/coders'
    if /bin/sh ../libtool --silent --mode=compile gcc -DHAVE_CONFIG_H -I../ -I. -I. -I../magick
    -I.. -I.. -I../magick -I/usr/include/freetype2 -D_FILE_OFFSET_BITS=64 -D_REENTRANT -g -O2
    -Wall -MT art.lo -MD -MP -MF ".deps/art.Tpo" -c -o art.lo art.c; \
    then mv -f ".deps/art.Tpo" ".deps/art.Plo"; else rm -f ".deps/art.Tpo"; exit 1; fi
    [... lots more output ...]
    /usr/bin/install -c -m 644 QuickStart.txt /usr/local/share/ImageMagick-5.5.7/QuickStart.txt
    make[2]: Leaving directory `/tmp/ImageMagick/ImageMagick-5.5.7'
    make[1]: Leaving directory `/tmp/ImageMagick/ImageMagick-5.5.7'
    [/tmp/ImageMagick-5.5.7] $ ./configure --with-perl --without-x \
    --with-windows-font-dir=/usr/share/fonts/defaults/TrueType
    checking build system type... i686-pc-linux-gnu
    [... lots more output ...]
    LIBS     = -ltiff -lfreetype -ljpeg -lpng -lbz2 -lz -lpthread -lm
    [/tmp/ImageMagick-5.5.7] $ cd PerlMagick
    [/tmp/ImageMagick-5.5.7/PerlMagick] $ perl Makefile.PL
    Checking if your kit is complete...
    Looks good
    Writing Makefile for Image::Magick
    [/tmp/ImageMagick-5.5.7/PerlMagick] $ make && make install 
    cp Magick.pm blib/lib/Image/Magick.pm
    AutoSplitting blib/lib/Image/Magick.pm (blib/lib/auto/Image/Magick)
    [... lots more output ...]
    Writing /usr/lib/perl5/site_perl/5.6.1/i386-linux/auto/Image/Magick/.packlist
    Appending installation info to /usr/lib/perl5/5.6.1/i386-linux/perllocal.pod
    
Now, we'd better take a more in depth look at the configure commands.  You will note that we specified a --with-windows-font-dir=/usr/share/fonts/defaults/TrueType and a --without-x.  These two configure options are vital to both using TrueType fonts and also running on a machine that does not have XWindows installed (typically servers).

First, we configure the software to not use the PerlMagick stuff, by specifying the --without-perl configure option.  Then, we run the make command (in BSD, gmake may be a better option), and then make install.  The make install command still uses the make command to copy the libraries into the proper places.

Then, we reconfigure the software to use PerlMagick, by replacing the --without-perl with the --with-perl.  Once reconfigured (this resets the PerlMagick stuff up), we step into the PerlMagick directory, and create the new make file.  This is done by executing a perl script located in that directory, called Makefile.PL, and then run the make and make install commands again.  This installs the proper Perl::Magick libraries for perl into their proper places so that we can use them.

Command-Line Tools

When it comes to command line tools, it really depends on what you need to do, as to which tool.  The list is :
    animate Assembling images into an animation.
    composite Merging images.
    conjure The Magick scripting language (MSL) will primarily benefit those that want to accomplish custom image processing tasks but do not wish to program, or those that do not have access to a Perl interpreter or a compiler.  The interpreter is called conjure.
    convert Converting images from one format to another.
    display Display the image(s) on the XWindows display.
    identify This command is used to determine image characteristics, from format, to palette/RGB colorspace.
    import This tool reads an image from any visible window on an X server and outputs it as an image file. You can capture a single window, the entire screen, or any rectangular portion of the screen.
    mogrify Mogrify  transforms  an image or a sequence of images. These transforms include image scaling, image rotation, color reduction, and others. Each transmogrified image overwrites the corresponding original  image, unless an option such as -format causes the output filename to be different from the input filename.  Make changes to the image, such as blurs, sharpens, etc.
    montage montage  creates  a composite image by combining several separate images. The images are tiled on the composite image with the name of the image optionally appearing just below the individual tile.
Examples of calling the functions can be obtained by typing "man {functionname}", such as man convert.  Still, if you need to create a JPG thumbnail of an image that is 100 by 100 pixels, or change from an MIFF (Magick Image File Format) to a Sun Raster format :
    [/tmp] $ convert -size 120x120 filename.jpg -resize 120x120 +profile "*" filename-thumbnail.jpg
    [/tmp] $ convert cockatoo.miff sun:cockatoo.ras
    

PerlMagick

As a brief introduction to using PerlMagick, you should be familiar with perl in general, and using the object oriented methods associated with perl.  As with each perl program, we must specify the introductory line :
    #!/usr/bin/perl
    
Then, we ensure that the module objects are there :
    use Image::Magick;
    
Simply, we just declare a new Image::Magick object.  This can be done in two forms.  I will show you both forms, but suggest the latter one, just in case you have implemented a new function :
    my $imgs = new Image::Magick;
    my $imgs = Image::Magick->new();
    
Now, the most commonly functions are Set(), Get(), Read(), and Write().  These functions are used to read and write images, and also to retrieve and set attributes of images in memory.  These functions are usually written as associated with our image object :
    $imgs->Set(variable name=>variable value);
    
Now, we can get into the nitty gritty of handling images.

File Reading and Writing

To read an image from a disk, or to write one is simple.  but, rather than delving deep into the system calls implemented by the functions, we just simply need to understand what we do.  Now, we can read in multiple images, and this will create an array object, or it's also used in creating animations, but we'll discuss that later on.  In the mean time, a simple "multi-image" read statement is just passed an array of images to read.  The two examples are (one single image, and one multi-image) :
    $imgs->Read('/tmp/this-image.jpg');
    $imgs->Read('/tmp/logo-01.jpg','/tmp/logo-02.jpg');
    
Writing is similar, except in cases of multiple images.  A multi-image write call must either specify the image to write, or it will write the entire image as an animation.  For example :
    $imgs->Write('GIF:/tmp/logo.gif');
    $imgs->[1]->Write('GIF:/tmp/logo-02.gif');
    
The first example is for writing a standard image to a file, and the second example is for writing a single image in a multi-image to a file.

But, how does writing to a browser work in terms of CGI?  Here's where it gets tricky.  When a browser makes a request, the browser expects to see HEADERS back from the webserver that is running the CGI.  At least one header is required, and that is Content-Type.  In addition, the file streams work a bit differently, because we can't write to "a file", per se.  So, we print the Content-Type header, and a blank line so the browser knows that we're done printing headers, and then print the image.  We put the output stream into binary mode before writing the image, just so we get the proper image.  It works like this :
    #!/usr/bin/perl
    
    use Image::Magick;
    
    my $imgs = Image::Magick->new();
    $imgs->Read("logo.gif");
    
    print "Content-Type: image/gif\n\n";
    binmode STDOUT;
    $imgs->Write("-");
    
The above script should read in the image, write the proper content type to the web browser, then convert the output stream to binary mode.  After converting the output stream, we dump the image that we've read.  This example would be better served to be rewritten using basic perl commands, but it will act as a starting point for you to read and write images.

Now, if you've got the image saved with a .jpg extension, but it's really just a .gif file, you can add a format specifier to the filename by preceding the filename with the format and a colon.  For example :
    $imgs->Read("gif:logo.jpg");
    
This works for both the Read() and the Write() functions, which means you can also change formats for the image (converting from .jpg to .gif).

Creating Images from Scratch

Creating images from scratch is complex in thought, yet simple in implementation.  Rather than creating the image object, resizing the image, and filling a square with a color (still works), one could simply use the read command.  If you use the read command, and read a file format of type "xc" followed by a filename that is equivelent to the X color, i.e. :
    $imgs->Read("xc:green");
    
Prior to calling this read command, you might want to resize the image to how big you want it.  This is done with the Set() function call, using a variable name of "size" and a value of "{width}x{height}".  For example, creating a new image that is 60 pixels by 30 pixels with a blue color :
    $imgs->Set(size=>"60x30");
    $imgs->Read("xc:green");
    

Drawing Primitives

Drawing primitives isn't discussed very often at all.  This is not because it can't be done, but because there is no real reason to do so, as most primitives can be created with other software.  In other words, if you are really just trying to create a chart system, you might be better off using the GD package or GIFGraph.  Still, there are instances where you want to do this.  I've run across a couple of situations where having two libraries just isn't a good idea.  So, I've used PerlMagick to implement the "primitives".  The basic core of drawing primitives is the Draw() function.  This function requires at least two parameters, a "primitive" and a "points".  The value of the primitive variable is the type of primitive being drawn.  For example, to draw a red rectangle, one would use the command :
    $mage->Draw(fill=>'red', primitive=>'rectangle', points=>'20,20 100,100');
    
The points value depends on the primitive you are trying to draw.  The following guidelines apply :
    PrimitivePoints Format
    arc{x-center},{y-center} {x-endpoint-1},{y-endpoint-1} {x-endpoint-2},{y-endpoint-2}"
    bezier{x-end-1},{y-end-1}, {x-end-2},{y-end-2}[, {x-end-n},{y-end-n}]
    circle{x-center},{y-center},{x-radius},{y-radius}
    color
    ellipse{x-center},{y-center} {x-radius-1},{y-radius-1} {x-radius-2},{y-radius-2}
    line{x-start},{y-start} {x-end},{y-end}
    matte
    path*too complex for writing
    point{x},{y}
    polygon{x-corner-1},{y-corner-1} {x-corner-2},{y-corner-2}
    polyline{x-endpoint-1},{y-endpoint-1} {x-endpoint-2},{y-endpoint-2} [{x-endpoint-3},{y-endpoint-3}]
    rectangle{x-upper-left},{y-upper-left} {x-lower-right},{y-lower-right}
    text{x},{y} "{what text}"
The primitives are relatively straight forward.  For example, to create a circle, we'd use the following command :
    $imgs->Draw(stroke=>'blue', primitive=>'circle', points=>'20,20 15,15');
    

Writing on the Image

There are actually two different methods that we can use to write on an image.  First, we can use the primitive function.  The problem with doing the primitive function is we get only one font, and it is very basic.  We can also make a call to the Annotate() function.  First, the primitive :
    $imgs->Draw(primitive=>'text',points=>'15,15 "awesome"',stroke=>green);
    
will give us a green "awesome".  Note the font and it's quality.  Very poor.  So, we'll opt to use the Annotate function in the following way :
    $imgs->Annotate(font=>'cour.ttf', pointsize=>40, fill=>'green', text=>"awesome");
    
If you have the Courier TrueType font available, you will get "awesome" in green, but done in a 40 point size, plus using the Courier font.  You can specify locations using the x and y variables, and also add a "gravity" variable.  Gravity means that the text will be centered on the x,y position, or southwest, or northeast, or right, etc.  This means you can create right justified text by specifying the upper left hand corner, and using a southwest gravity :
    $imgs->Annotate(
        font=>'cour.ttf',
        pointsize=>40,
        fill=>'green',
        text=>"awesome",
        gravity=>'southwest',
        x=>100,
        y=>20
    );
    
Play around with the settings, and you'll find a rather potent arsenal of font and image writing capabilities.

Adding Transparency

Transparency is a nice effect when it comes to different formats.  Of particular note is Internet graphics and transparencies, where the choices become very limited.  For example, PNG (Portable Network Graphics) are displayed by most browsers, but some browsers (e.g. Internet Explorer) just won't display any transparency at all - just white color.  GIF (Graphic Interchange Format) uses a "solid" transparency, which means no semi-transparent colors.  That drop-shadow fade you are trying to accomplish just won't work very well over the Internet.  However, most current browsers DO support the PNG format, so if you don't mind requiring visitors to have the lastest, go with PNG (animations are not supported with PNG, either).

Here, though, since we are ultimately creating a logo, we'll be doing so with GIF, because we want it to be animated.  So, with a GIF format, we first ensure that the colorspace is for transparency.  Once we've gotten the proper colorspace, we'll then set which "color" we want to be transparent.
                           
In the above to images, we've got a single picture (the one in black), that we're going to convert to have a transparent background.  To accomplish this, we'd use the following code :
    $imgs->Set(colorspace=>'transparent');
    $imgs->Transparent(color=>'#000000');
The result is the image on the right, or the one with a transparent background.  Simple, and straight forward.  Now, with GIF's, we really don't need to set the colorspace prior to the Transparent() command, but just do it anyway to ensure the habits of proper handling so you don't shoot yourself in the foot down the road.

Creating Animations

Remember the good old days when we'd sit in elementary school with a notebook, and draw the stick figures on each page?  Then we'd flip through the pages, and it gave the appearance of true motion?  Well, that's what we need to do.  We get all of the images together, and then combine them into a series of pages, each one being called a "frame".

Animated logo's have become a novelty that is often either overdone, or done too simply.  But, what is the process that is used?  I've actually used a few different methods for creating nice 3 dimensional logo's, but the one I prefer is this :
  1. Ray-trace a 3 dimensional definition into a series of images.
  2. Assemble each "frame" into one big sequence of frames, using ImageMagick
  3. Set any transparency properties, again using ImageMagick
  4. Write the image out
This gives some very nice effects (such as my sharktooth logo, or the firebird logo).  I name the images sequentially (such as firebird-00.gif, firebird-01.gif, etc.), then call a script.  This script creates the ImageMagick object, reads in the images in alphabetical order, sets the transparency, and then writes it out.  Since we've already discussed how to set transparency, this becomes easier.  I'll walk you through the details of what the commands do after showing the script snippets :
    my $imgs = Image::Magick->new();
    
    # the animation stuff
    $imgs->Read(sort(@ARGV));
    
    #set the animation characteristics
    $imgs->Set(delay=>10);
    $imgs->Set(loop=>0);
    $imgs->Set(dispose=>2);
    
    # write the animation out
    $imgs->Write('logo.gif');
    
Now, for the description.  First, we Read() an array of images (my example has the filenames provided on the commandline and sorts them into ascending order).  This will turn the $imgs object into an array reference rather than a single object.  We then make adjustments by Set()-ting the delay (in miliseconds) between frames, set an endless loop (if non-zero, the animation will loop that many times and then stop), and the refresh (termed as dispose) method between frames, and Write() it out.  So, according to the above example, we'll end up with an endless loop, with 1/100th of a second between each picture.  The result is seen to your left.

There are actually four different dispose methods.  The first (0), is for NO refresh method.  The second (1) is for repainting the background, and the third (2) refreshes from the previous frame.  Did I say there were four disposal methods?  Yes, I did.  The fourth (3) is a valid option, but is really an undefined action - in other words, don't use this one.

Epilogue

We've covered some rather basic things, but also covered some fairly complex thoughts.  As you've seen, the ImageMagick suite of image manipulation software is both easy to use and powerful in it's functionality.  We've talked a little of command-line tools, and gotten heavily into perl scripting with the ImageMagick library.

It is an obvious choice as to a good software suite to use in scripting or dynamic images, as the possibilities are endless.  Play around with the scripts or tools, and explore the world of image manipulation.

No comments:

Post a Comment