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)