Storage Guide

IM - An Imaging Tool

Storage Guide

Reading

When reading the file extension is not relevant to determine the file format, but it is used to speed up the process of finding the correct format. With few exceptions the format drivers that access multiple images can read them in any sequence you want.

During the read process the original data can be converted to some options of user data. Not all conversions are available. You can convert any data to a bitmap version of it, and you can select any of the color mode flags IM_ALPHA, IM_PACKED and IM_TOPDOWN, regardless of the file original configuration.

Remember that even if all the images in the file have the same parameters you still have to call imFileReadImageInfo before calling imFileReadImageData.

In the following example all the images in the file are loaded.

char format[10], compression[10];
int error, image_count;
int width, height, color_mode, data_type;
void* data;

imFile* ifile = imFileOpen("test.tif", &error);
if (error != IM_ERR_NONE) 
  // handle the error

imFileGetInfo(ifile, format, compression, &image_count);

for (i = 0; i < image_count, i++)
{
  error = imFileReadImageInfo(ifile, i, &width, &height, &color_mode, &data_type);
  if (error != IM_ERR_NONE) 
    // handle the error

  // prepare data

  error = imFileReadImageData(ifile, data, 0, -1); // no bitmap convertion, use original color mode flags
  if (error != IM_ERR_NONE) 
    // handle the error

  // store data somewhere
}

imFileClose(ifile); 

A more simple code loads only the first image in the file:

imFile* ifile = imFileOpen(file_name, &error);

imFileReadImageInfo(ifile, 0, &width, &height, &color_mode, &data_type);

imFileReadImageData(ifile, data, 0, -1);

imFileClose(ifile); 

If you are using the imImage structure it is easier:

imFile* ifile = imFileOpen(file_name, &error);
 
imImage* image = imFileLoadImage(ifile, 0, &error);
// or use imFileLoadBitmap to force a bitmap conversion

imFileClose(ifile);

Or the simplest version:

imImage* image = imFileImageLoad(file_name, 0, &error);

Writing

When writing there is no color space or data type conversion. Only color mode flags can be different: IM_ALPHA, IM_PACKED and IM_TOPDOWN. You just have to describe your data and the imFileWriteImageData will handle the color mode flag differences.

Of course you still have to check the error codes because, not all color spaces and data types are supported by each format.

When saving a sequence of images you must provide each image in the order that they will be in the file. For a video or animation start from frame 0 and go on, you can not jump or change the frame order. Also when saving videos you should not forget to save the numbers of frames per second in the attribute "FPS", the default value is 15.

For all the formats it is not necessary to set the compression, each driver will choose a default compression. But you may set it using the function imFileSetInfo.

To save several images to the same file:

int error, width, height;
void *data;

imFile* ifile = imFileNew("test.tif", "TIFF", &error);
if (error != IM_ERR_NONE) 
  // handle the error

for (i = 0; i < image_count, i++)
{
  error = imFileWriteImageInfo(ifile, width, height, IM_RGB, IM_BYTE);
  if (error != IM_ERR_NONE) 
    // handle the error

  error = imFileWriteImageData(ifile, data);
  if (error != IM_ERR_NONE) 
    // handle the error
}

imFileClose(ifile); 

But remember that not all file formats supports several images. To save just one image is more simple:

imFile* ifile = imFileNew(file_name, format, &error);

error = imFileWriteImageInfo(ifile, width, height, color_mode, data_type);

error = imFileWriteImageData(ifile, data);

imFileClose(ifile); 

If you are using the imImage structure it is easier:

imFile* ifile = imFileNew(file_name, format, &error);

error = imFileSaveImage(ifile, image);

imFileClose(ifile);

Or the simplest version:

error = imFileImageSave(file_name, format, image);

Error Messages

Here is a sample error message display using IUP and IM error codes:

static void imIupErrorMessage(int error, int interactive)
{
  char* lang = IupGetLanguage();
  char *msg, *title;
  if (strcmp(lang, "ENGLISH")==0)
  {
    title = "Error";
    switch (error)
    {
    case IM_ERR_OPEN:
      msg = "Error Opening File.";
      break;
    case IM_ERR_MEM:
      msg = "Insuficient memory.";
      break;
    case IM_ERR_ACCESS:
      msg = "Error Accessing File.";
      break;
    case IM_ERR_DATA:
      msg = "Image type not Suported.";
      break;
    case IM_ERR_FORMAT:
      msg = "Invalid Format.";
      break;
    case IM_ERR_COMPRESS:
      msg = "Invalid or unsupported compression.";
      break;
    default:
      msg = "Unknown Error.";
    }
  }
  else
  {
    title = "Erro";
    switch (error)
    {
    case IM_ERR_OPEN:
      msg = "Erro Abrindo Arquivo.";
      break;
    case IM_ERR_MEM:
      msg = "Memória Insuficiente.";
      break;
    case IM_ERR_ACCESS:
      msg = "Erro Acessando Arquivo.";
      break;
    case IM_ERR_DATA:
      msg = "Tipo de Imagem não Suportado.";
      break;
    case IM_ERR_FORMAT:
      msg = "Formato Inválido.";
      break;
    case IM_ERR_COMPRESS:
      msg = "Compressão Inválida ou não Suportada.";
      break;
    default:
      msg = "Erro Desconhecido.";
    }
  }

  if (interactive)
    IupMessage(title, msg);
  else
    printf("%s: %s", title, msg);
}

About File Formats

TIFF is still the most complete format available. It could be better if Adobe releases the revision 7, but it is on stand by. TIFF supports all the IM image representation concepts. In fact we were partially inspired by the TIFF specification. My suggestion is whenever possible use TIFF.

But TIFF may not be the ideal format for many situations. The W3C standards include only JPEG, GIF and PNG for Web browsers. JPEG forces the image to be RGB or Gray with a lossy compressed. GIF forces the image to be MAP with LZW compression. PNG forces the image to be RGB, MAP, Gray or Binary, with Deflate compression. So these characteristics are necessary to force small values for faster downloads.

JPEG is to be used for photographic content, PNG should be used for the remaining cases, but GIF is still the best to do simple animated images.

Except for some specific cases where a format is needed for compatibility, the other formats are less important. TGA, PCX, RAS, SGI and BMP have almost the same utility.

JP2 must be used for JPEG-2000 compression, would be nice if a new TIFF specification includes this standard.

Since PNM has a textual header it is very simple to teach for students so they can actually "see" the header. It is also a format easy to share images, but it does not do much more than that.

The TIFF and the GIF format also have support for multiple images. This does not necessarily defines an animation, pyramid nor a volume, but some times they are used in these ways.

GIF became very popular to build animations for the Web, and since the LZW patent expired Unisys realized that charging the usage isn't going to work and so they did not renew it. LZW is fully supported at IM.

IM also supports video formats like AVI and WMV as external libraries. In these cases the frames are also loaded as a sequence of individual images. Sound is not supported.

TIFF, JPEG and PNG have an extensive list of attributes, most of them are listed in the documentation, but some custom attributes may come up when reading an image from file.

New File Formats

Again the easiest way is to look at the source code of an already implemented format. The RAS, BMP, TGA and SGI formats are very simple to follow.

Basically you have to implement a class that inherits from imFormat and implement its virtual methods. You can use the imBinFile functions for I/O or use an external SDK.

For more information see File Format SDK.

Memory I/O and Others

For the majority of the formats, with the exception of the ones that use external SDKs, the I/O is done by the imBinFile module.

This module can be configured to access other types of media by implementing a driver. There are some predefined drivers see Reference / Utilities / Binary File Access.

One very useful is the Memory Buffer where you can read and write a file in memory. The activation is very simple, it needs to happen just before the imFileOpen/imFileNew functions. But the file name must be a pointer to an imBinMemoryFileName structure instead of a string. Se the example bellow:

int old_mode = imBinFileSetCurrentModule(IM_MEMFILE);

imBinMemoryFileName MemFileName; // This structure must exists 
    while the file remains open.
MemFileName.buffer = NULL; // Let the library initializes the buffer,
                           // but it must be freed the the application, free(MemFileName.buffer) MemFileName.size = 1024; // The initial size
MemFileName.reallocate = 1.5; // The reallocation will increase 50% the buffer.
                              // This is used only when writing with a variable buffer.
                              // Use 0 to fix the buffer size. int error;
imFile* ifile = imFileNew((const char*)&MemFileName, "GIF", &error); imBinFileSetCurrentModule(old_mode); // The mode needs to be active only for the imFileOpen/imFileNew call. if (error != IM_ERR_NONE) ....

Another driver interesting is the Subfile where you can read and write from a file that is already open. This is very important for formats that can have an embedded format inside. In this module the file_name is a pointer to an imBinFile  structure from any other module that uses the imBinFile functions. The imBinFileSize will return the full file size, but the imBinFileSeekTo and imBinFileTell functions will compensate the position when the subfile was open.

Using imBinFileSetCurrentModule(IM_SUBFILE) just like the example above will allow you to open a subfile using the imFileOpen/imFileNew functions.