3.2 Programming WIO operations

WIO is the style for you if you want ease of programming, or if your algorithm must have the whole of the input image available at the same time. For example, a Fourier transform operation is unable to produce any output until it has seen the whole of the input image.

3.2.1 Input from an image

In WIO input, the whole of the image data is made available to the program via the data field of the descriptor. To make an image ready for reading in this style, programs should call im_incheck():

int im_incheck( IMAGE ⋆im )

If it succeeds, it returns 0, if it fails, it returns non-zero and sets im_error(). On success, VIPS guarantees that all of the user-accessible fields in the descriptor contain valid data, and that all of the image data may be read by simply reading from the data field (see below for an example). This will only work for images less than about 2GB in size.

VIPS has some simple macros to help address calculations on images:

int IM_IMAGE_SIZEOF_ELEMENT( IMAGE ⋆ )  
int IM_IMAGE_SIZEOF_PEL( IMAGE ⋆ )  
int IM_IMAGE_SIZEOF_LINE( IMAGE ⋆ )  
int IM_IMAGE_N_ELEMENTS( IMAGE ⋆ )  
char ⋆IM_IMAGE_ADDR( IMAGE ⋆,  
  int x, int y )

These macros calculate sizeof() a band element, a pel and a horizontal line of pels. IM_IMAGE_N_ELEMENTS returns the number of band elements across an image. IM_IMAGE_ADDR calculates the address of a pixel in an image. If DEBUG is defined, it does bounds checking too.


#include <stdio.h>  
#include <stdlib.h>  
 
#include <vips/vips.h>  
 
int  
average( IMAGE ⋆im, double ⋆out )  
{  
  int x, y;  
  long total;  
 
  /⋆ Prepare for reading.  
   ⋆/  
  if( im_incheck( im ) )  
    return( -1 );  
 
  /⋆ Check that this is the kind of image we can process.  
   ⋆/  
  if( im->BandFmt != IM_BANDFMT_UCHAR ||  
    im->Coding != IM_CODING_NONE ) {  
    im_error( "average", "uncoded uchar images only" );  
    return( -1 );  
  }  
 
  /⋆ Loop over the image, summing pixels.  
   ⋆/  
  total = 0;  
  for( y = 0; y < im->Ysize; y++ ) {  
    unsigned char ⋆p = (unsigned char ⋆) IM_IMAGE_ADDR( im, 0, y );  
 
    for( x = 0; x < IM_IMAGE_N_ELEMENTS( im ); x++ )  
      total += p[x];  
  }  
 
  /⋆ Calculate average.  
   ⋆/  
  ⋆out = (double) total /  
    (IM_IMAGE_N_ELEMENTS( im ) ⋆ im->Ysize));  
 
  /⋆ Success!  
   ⋆/  
  return( 0 );  
}


Figure 3.1: Find average of image

Figure 3.1 is a simple WIO operation which calculates the average of an unsigned char image. It will work for any size image, with any number of bands. See §3.2.3 for techniques for making operations which will work for any image type. This operation might be called from an application with:

#include <stdio.h>  
#include <stdlib.h>  
 
#include <vips/vips.h>  
 
void  
find_average( char ⋆name )  
{  
  IMAGE ⋆im;  
  double avg;  
 
  if( !(im = im_open( name, "r" )) ||  
    average( im, &avg ) ||  
    im_close( im ) )  
    error_exit( "failure!" );  
 
  printf( "Average of \"%s\" is %G\n",  
    name, avg );  
}

When you write an image processing operation, you can test it by writing a VIPS function descriptor and calling it from the vips universal main program, or from the nip2 interface. See §2.1.

3.2.2 Output to an image

Before attempting WIO output, programs should call im_outcheck(). It has type:

int im_outcheck( IMAGE ⋆im )

If im_outcheck() succeeds, VIPS guarantees that WIO output is sensible.

Programs should then set fields in the output descriptor to describe the sort of image they wish to write (size, type, and so on) and call im_setupout(). It has type:

int im_setupout( IMAGE ⋆im )

im_setupout() creates the output file or memory buffer, using the size and type fields that were filled in by the program between the calls to im_outcheck() and im_setupout(), and gets it ready for writing.

Pels are written with im_writeline(). This takes a y position (pel (0,0) is in the top-left-hand corner of the image), a descriptor and a pointer to a line of pels. It has type:

int im_writeline( int y,  
  IMAGE ⋆im, unsigned char ⋆pels )

Two convenience functions are available to make this process slightly easier. im_iocheck() is useful for programs which take one input image and produce one image output. It simply calls im_incheck() and im_outcheck(). It has type:

int im_iocheck( IMAGE ⋆in, IMAGE ⋆out )

The second convenience function copies the fields describing size, type, metadata and history from one image descriptor to another. It is useful when the output image will be similar in size and type to the input image. It has type:

int im_cp_desc( IMAGE ⋆out, IMAGE ⋆in )

There’s also im_cp_descv(), see the man page.


#include <stdio.h>  
#include <stdlib.h>  
 
#include <vips/vips.h>  
#include <vips/util.h>  
 
int  
invert( IMAGE ⋆in, IMAGE ⋆out )  
{  
  int x, y;  
  unsigned char ⋆buffer;  
 
  /⋆ Check images.  
   ⋆/  
  if( im_iocheck( in, out ) )  
    return( -1 );  
  if( in->BandFmt != IM_BANDFMT_UCHAR || in->Coding != IM_CODING_NONE ) {  
    im_error( "invert", "uncoded uchar images only" );  
    return( -1 );  
  }  
 
  /⋆ Make output image.  
   ⋆/  
  if( im_cp_desc( out, in ) )  
    return( -1 );  
  if( im_setupout( out ) )  
    return( -1 );  
 
  /⋆ Allocate a line buffer and make sure it will be freed correctly.  
   ⋆/  
  if( !(buffer = IM_ARRAY( out,  
    IM_IMAGE_SIZEOF_LINE( in ), unsigned char )) )  
    return( -1 );  
 
  /⋆ Loop over the image!  
   ⋆/  
  for( y = 0; y < in->Ysize; y++ ) {  
    unsigned char ⋆p = (unsigned char ⋆) IM_IMAGE_ADDR( in, 0, y );  
 
    for( x = 0; x < IM_IMAGE_N_ELEMENTS( in ); x++ )  
      buffer[x] = 255 - p[x];  
    if( im_writeline( y, out, buffer ) )  
      return( -1 );  
  }  
 
  return( 0 );  
}


Figure 3.2: Invert an image

Figure 3.2 is a WIO VIPS operation which finds the photographic negative of an unsigned char image. See §2.2.10 for an explanation of IM_ARRAY. This operation might be called from an application with:

#include <stdio.h>  
#include <stdlib.h>  
 
#include <vips/vips.h>  
 
void  
find_negative( char ⋆inn, char ⋆outn )  
{  
  IMAGE ⋆in, ⋆out;  
 
  if( !(in = im_open( inn, "r" )) ||  
    !(out = im_open( outn, "w" )) ||  
    invert( in, out ) ||  
    im_updatehist( out, "invert" ) ||  
    im_close( in ) ||  
    im_close( out ) )  
    error_exit( "failure!" );  
}

See §2.2.7 for an explanation of the call to im_updatehist().

3.2.3 Polymorphism

Most image processing operations in the VIPS library can operate on images of any type (IM_BANDFMT_UCHAR, as in our examples above, also IM_BANDFMT_UINT etc.). This is usually implemented with code replication: the operation contains loops for processing every kind of image, and when called, invokes the appropriate loop for the image it is given.

As an example, figure 3.3 calculates exp() for every pel in an image. If the input image is double, we write double output. If it is any other non-complex type, we write float. If it is complex, we flag an error (exp() of a complex number is fiddly). The example uses an image type predicate, im_iscomplex(). There are a number of these predicate functions, see the manual page.


#include <stdio.h>  
#include <stdlib.h>  
#include <math.h>  
 
#include <vips/vips.h>  
#include <vips/util.h>  
 
/⋆ Exponential transform.  
 ⋆/  
int  
exptra( IMAGE ⋆in, IMAGE ⋆out )  
{  
  int x, y;  
  unsigned char ⋆buffer;  
 
  /⋆ Check descriptors.  
   ⋆/  
  if( im_iocheck( in, out ) )  
    return( -1 );  
  if( in->Coding != IM_CODING_NONE || im_iscomplex( in ) ) {  
    im_error( "exptra", "uncoded non-complex only" );  
    return( -1 );  
  }  
 
  /⋆ Make output image.  
   ⋆/  
  if( im_cp_desc( out, in ) )  
    return( -1 );  
  if( in->BandFmt != IM_BANDFMT_DOUBLE )  
    out->BandFmt = IM_BANDFMT_FLOAT;  
  if( im_setupout( out ) )  
    return( -1 );


Figure 3.3: Calculate exp() for an image


  /⋆ Allocate a line buffer.  
   ⋆/  
  if( !(buffer = IM_ARRAY( out, IM_IMAGE_SIZEOF_LINE( in ), unsigned char )) )  
    return( -1 );  
 
/⋆ Our inner loop, parameterised for both the input and output  
 ⋆ types. Note the use of ‘\', since macros have to be all on  
 ⋆ one line.  
 ⋆/  
#define loop(IN, OUT) { \  
  for( y = 0; y < in->Ysize; y++ ) { \  
    IN ⋆p = (IN ⋆) IM_IMAGE_ADDR( in, 0, y ); \  
    OUT ⋆q = (OUT ⋆) buffer; \  
    \  
    for( x = 0; x < IM_IMAGE_N_ELEMENTS( in ); x++ ) \  
      q[x] = exp( p[x] ); \  
    if( im_writeline( y, out, buffer ) ) \  
      return( -1 ); \  
   } \  
}  
 
  /⋆ Switch for all the types we can handle.  
   ⋆/  
  switch( in->BandFmt ) {  
    case IM_BANDFMT_UCHAR: loop( unsigned char, float ); break;  
    case IM_BANDFMT_CHAR:  loop( char, float ); break;  
    case IM_BANDFMT_USHORT:loop( unsigned short, float ); break;  
    case IM_BANDFMT_SHORT: loop( short, float ); break;  
    case IM_BANDFMT_UINT:  loop( unsigned int, float ); break;  
    case IM_BANDFMT_INT:   loop( int, float ); break;  
    case IM_BANDFMT_FLOAT: loop( float, float ); break;  
    case IM_BANDFMT_DOUBLE:loop( double, double ); break;  
    default:  
      im_error( "exptra", "internal error" );  
      return( -1 );  
  }  
 
  /⋆ Success.  
   ⋆/  
  return( 0 );  
}


Figure 3.4: Calculate exp() for an image (cont)