Tutorial

IM - An Imaging Tool

Guide

Getting Started

It is important to understand that IM is based in 4 concepts: Image Representation, Image Storage, Image Processing and Image Capture. The following picture illustrates the relation between theses concepts.

IM does not have support for Image Visualization, because we think this is a task for a graphics library like OpenGL, Windows GDI or CD.

Image Representation describes the image model and its details. Which color systems are going to be used, which data types, how the data is organized in memory, and how other image characteristics are accessed.

Image Storage describers the file format model and how images are obtained or saved. Image Capture describes the access to a capture device and obtaining an image from it. Image Processing describes the image processing operations.

There are infinite ways to implement these concepts. There is no common definition in the literature, but there is a standard called Programmer's Imaging Kernel System (PIKS) published at the ISO/IEC 12087. PIKS is a very complete and also complex standard, very hard to implement. There are only a few implementations available, and the one that I know is commercial software, Pixel Soft of William Pratt http://www.pixelsoft.com/, also author of several books on the subject.

But we want something easier to implement and understand. The free available libraries that we found where sometimes close to what we want, sometimes very far. So we developed our own.

The documentation contains Overview, Guide, Samples and Reference sections for each one of the IM concepts.

The Guide is where you are going to find the explanation about the concepts and decisions made during the library design. It is the best place to understand how things works.

The Reference contains pure essential information for function and structure usage. But there is no information on how to put the functions to work together. It is generated automatically from the source code using Doxygen, this means also that the include files (*.h) are very well commented.

Building Applications

Inside you code you should at least include the <im.h> header and link with the "im.lib/libim.a/libim.so" library. This library contains all the Image Representation functions and all the Image Storage functions (with the exception of the external formats: AVI, JP2 and WMV).

Each external format or processing usually needs a <im_xx.h> file and a "im_xx.lib/libim_xx.a/libim_xx.so" file.

Even if your applicattion is only in C, you must link with a C++ capable linker. Using Tecmake set "LINKER := g++" in your "config.mak" when compiling with gcc (UNIX and Windows).

The download files list includes the Tecgraf/PUC-Rio Library Download Tips document, with a description of all the available binaries.

Building the Library

The easiest way to build the library is to install the Tecmake tool into your system. It is easy and helps a lot. The Tecmake configuration files (*.mak) available at the "src" folder are very easy to understand also.

Tecmake is a command line multi compiler build tool available at http://www.tecgraf.puc-rio.br/tecmake. Tecmake is used by all the Tecgraf libraries and many applications.

In IM's main source directory there is a file named make_uname (make_uname.bat in Windows) that build the libraries using Tecmake. To build the IM libraries for Windows using Visual C 7.0 for example, just execute make_uname.bat vc7 in the source folder.

But we also provide a stand alone makefile for Linux systems and a Visual Studio workspace with the respective projects. The stand alone makefile is created using Premake and a configuration file in lua called "premake.lua".

CD Compatibility

IM version 2 was designed to perfectly work with the CD - Canvas Draw toolkit. Version 3 has many more options and only for a subset of the images called Bitmaps can be used with the CD functions. Theses images have data type IM_BYTE, and color mode IM_RGB, IM_GRAY, IM_MAP or IM_BINARY. They can not have the flags IM_TOPDOWN and IM_PACKED. But it can have the flag IM_ALPHA for IM_RGB images.

You can convert an image to a bitmap version of it using the function imConvertToBitmap, see Image Representation / Conversion.

Function cdGetImageRGB captures an image from the active canvas. Functions cdPutImageRGB and cdPutImageMap place an RGB image or an indexed image, respectively, on the active canvas. These functions allow reducing or increasing the image when placing it on the canvas.

For applications in systems with only 256 colors available, we recommend the use of function cdPalette before drawing the image, to improve its quality.

When using the imImage structure the macro imPutBitmap can be used. It is defined as:

#define imPutBitmap(_image, _x, _y, _w, _h, _xmin, _xmax, _ymin, _ymax)     \
  {                                                                         \
    if (_image->color_space == IM_RGB)                                      \
    {                                                                       \
      if (image->has_alpha)                                                 \
        cdPutImageRectRGBA(_image->width, _image->height,                   \
                          (unsigned char*)_image->data[0],                  \
                          (unsigned char*)_image->data[1],                  \
                          (unsigned char*)_image->data[2],                  \
                          (unsigned char*)_image->data[3],                  \
                          _x, _y, _w, _h, _xmin, _xmax, _ymin, _ymax);      \
      else                                                                  \
        cdPutImageRectRGB(_image->width, _image->height,                    \
                          (unsigned char*)_image->data[0],                  \
                          (unsigned char*)_image->data[1],                  \
                          (unsigned char*)_image->data[2],                  \
                          _x, _y, _w, _h, _xmin, _xmax, _ymin, _ymax);      \
    }                                                                       \
    else                                                                    \
      cdPutImageRectMap(_image->width, _image->height,                      \
                        (unsigned char*)_image->data[0], _image->palette,   \
                        _x, _y, _w, _h, _xmin, _xmax, _ymin, _ymax);        \
  }

CD Library is the Tecgraf 2D graphics library available at http://www.tecgraf.puc-rio.br/cd.

OpenGL Compatibility

The function glDrawPixels accepts several data types and color modes. Here are the format and type mapping for OpenGL usage:

           IM             <->  OpenGL
        color_mode             format
IM_RGB|IM_ALPHA|IM_PACKED  = GL_RGBA               
IM_RGB|IM_PACKED           = GL_RGB
IM_GRAY                    = GL_LUMINANCE
IM_GRAY|IM_ALPHA|IM_PACKED = GL_LUMINANCE_ALPHA
        data_type              type
IM_BYTE                    = GL_UNSIGNED_BYTE
IM_BINARY                  = GL_BITMAP
IM_USHORT                  = GL_UNSIGNED_SHORT
IM_INT                     = GL_INT
IM_FLOAT                   = GL_FLOAT

There is no mapping for non IM_PACKED images so if you use unpacked planes (ex: you use the imImage structure) then you have to convert one data into another, the function imConvertPacking does this, so you just have to keep an extra buffer for the display image and call this function only when your original image has changed. See Image Representation / Conversion. For example:

imConvertPacking(image->data[0], gl_data, image->width, image->height, image->depth, image->data_type, 0);
glPixelStorei(GL_UNPACK_ALIGNMENT, 1); /* data alignment must be 1 */

glDrawPixels(image->width, image->height, GL_RGB, GL_UNSIGNED_BYTE, (GLvoid*)gl_data);

When loading color image data you can use the function imConvertMapToRGBPacked to convert in-place IM_MAP image data into IM_RGB after loading it from file. For example:

if (imColorSpace(color_mode) == IM_MAP)
{
  long palette[256];
  int palette_count;
  imFileGetPalette(ifile, palette, &palette_count);
  imConvertMapToRGBPacked(gl_data, width*height, depth, palette, palette_count);
}

If you just want to save your OpenGL buffer then you can use:

glPixelStorei(GL_PACK_ALIGNMENT, 1); /* data alignment must be 1 */
glReadPixels(x, y, width, height, GL_RGB, GL_UNSIGNED_BYTE, (GLvoid*)gl_data);

ifile = imFileNew(filename, format, &error);
error = imFileWriteImageInfo(ifile, width, height, IM_RGB|IM_PACKED, IM_BYTE);
error = imFileWriteImageData(ifile, gl_data);
imFileClose(ifile); 

You can also put glReadPixels and imFileWriteImageInfo/imFileWriteImageData inside a loop to create an animation.

IM 2.x Compatibility

In version 3.0 the library was completely rewritten. And we changed the main API to allow more powerful features. But the old API is still available for backward compatibility. Version 3 is also binary compatible with version 2.

The only change that must be updated in old applications if they where recompiled is some error code definitions. If you use them in a case there will cause a compiler error because IM_ERR_READ and IM_ERR_WRITE are now defined as IM_ERR_ACCESS both.

Migrating OLD Code

The old API is very inefficient because the file is opened and close three times, for: imFileInfo, imImageInfo and imLoadRGB/imLoadMap. There is no room for attributes, so we use the callbacks. And we can not load sequences of images. For these reasons we change the API.

If you would like to migrate your code using the old API the most important thing to change is the memory allocation. For RGB images instead of allocating 3 separate pointers you should allocate only one pointer with room for all three planes. If you still want to keep the three pointers, just do green = red + width*height and blue = red + 2*width*height.

Also you should change your callbacks usage for attributes access using imFileGetAttribute and imFileSetAttribute. IM_RESOLUTION_CB is replaced by the attributes "XResolution", "YResolution", "ResolutionUnit". IM_GIF_TRANSPARENT_COLOR_CB is replaced by "TransparencyIndex" and IM_TIF_IMAGE_DESCRIPTION_CB by "Description".

Except IM_COUNTER_CB that is not an attribute, still works with a callback, but now we implement a counter system for all the library including loading, saving and processing. The user just use the imCounterSetCallback (like before) to register it counter callback, now there are a few more parameters and a user data pointer. See Utilities / Counter.

The function calls to imImageInfo and imLoadRGB/imLoadMap will be replaced by a sequence of function calls to imFileOpen/imFileNewimFileReadImageInfo/imFileWriteImageInfo, imFileReadImageData/imFileWriteImageData and imFileClose. See Image Storage.

Names Convention

To improve the readability of the code we use a very simple naming convention:

  • Global Functions and Types - "im[Object][Action]" using first capitals (imFileOpen)
  • Local Functions and Types - "i[Object][Action]" using first capitals (iTIFFGetCompIndex)
  • Local Static Variables - same as local functions and types (iFormatCount)
  • Local Static Tables - same as local functions and types with "Table" suffix (iTIFFCompTable)
  • Variables and Members - no prefix, all lower case (width)
  • Defines and Enumerations - all capitals (IM_ERR_NONE)

C x C++ Usage

The library main API is in C. We adopt this because of the many C programmers out there. Some of the API is also available in C++ for those addicted to classes.

Internally C++ is used to implement the format driver base architecture. A virtual base class that every drivers inherits from. This made a lot of things easier to the driver development. But we keep it simple, no multiple inheritance, no exception handling, no complicated classes.

But because we need several data types C++ templates were inevitable used (since we do not like long macros everywhere). But they are used only for processing functions, not classes.