blob: f2e45e014ab7a4b6243e2ff7f1a7a6a5b8437d7f [file] [log] [blame]
\section{Programming PIO functions}
\label{sec:pio}
The VIPS PIO system has a number of advantages over WIO, as summarised in
the introduction. On the other hand, they are a bit more complicated.
\subsection{Easy PIO with \texttt{im\_wrapone()} and \texttt{im\_wrapmany()}}
\label{sec:wrapone}
PIO is a very general image IO system, and because of this flexibility,
can be complicated to program. As a convenience, VIPS offers an easy-to-use
layer over PIO with the funtions \verb+im_wrapone()+ and \verb+im_wrapmany()+.
If your image processing function is uninterested in coordinates, that is,
if your input and output images are the same size, and each output pixel
depends only upon the value of the corresponding pixel in the input image
or images, then these functions are for you.
Consider the \verb+invert()+ function of figure~\ref{fg:invert}. First,
we have to write the core of this as a buffer-processing function:
\begin{verbatim}
#include <stdio.h>
#include <stdlib.h>
#include <vips/vips.h>
/* p points to a buffer of pixels which
* need inverting, q points to the buffer
* we should write the result to, and n
* is the number of pels present.
*/
static void
invert_buffer( unsigned char *p,
unsigned char *q, int n )
{
int i;
for( i = 0; i < n; i++ )
q[i] = 255 - p[i];
}
\end{verbatim}
Now we have to wrap up this very primitive expression of the invert operation
as a PIO function. We use \verb+im_wrapone()+ to do this. It has type:
\begin{verbatim}
int
im_wrapone( IMAGE *in, IMAGE *out,
im_wrapone_fn fn, void *a, void *b )
\end{verbatim}
\noindent
where:
\begin{verbatim}
void
(*im_wrapone_fn)(void *in, void *out,
int n, void *a, void *b )
\end{verbatim}
\noindent
almost the same type as our buffer-processing function above. The values
\verb+a+ and \verb+b+ are carried around by VIPS for whatever use you
fancy. \verb+invert()+ can now be written as:
\begin{verbatim}
int
invert( IMAGE *in, IMAGE *out )
{
/* Check parameters.
*/
if( in->BandFmt != IM_BANDFMT_UCHAR ||
in->Bands != 1 ||
in->Coding != IM_CODING_NONE ) {
im_error( "invert", "bad image" );
return( -1 );
}
/* Set fields in output image.
*/
if( im_cp_desc( out, in ) )
return( -1 );
/* Process! We don't use either of the
* user parameters in this function,
* so leave them as NULL.
*/
if( im_wrapone( in, out,
(im_wrapone_fn) invert_buffer,
NULL, NULL ) )
return( -1 );
return( 0 );
}
\end{verbatim}
And that's all there is to it. This function will have all of the desirable
properties of PIO functions, while being as easy to program as the WIO
\verb+invert()+ earlier in this chapter.
This version of \verb+invert()+ is not very general: it will only accept
one-band unsigned char images. It is easy to modify for n-band images:
\begin{verbatim}
/* As before, but use one of the user
* parameters to pass in the number of
* bands in the image.
*/
static void
invert_buffer( unsigned char *p,
unsigned char *q, int n,
IMAGE *in )
{
int i;
int sz = n * in->Bands;
for( i = 0; i < sz; i++ )
q[i] = 255 - p[i];
}
\end{verbatim}
We must also modify \verb+invert()+:
\begin{verbatim}
int
invert( IMAGE *in, IMAGE *out )
{
/* Check parameters.
*/
if( in->BandFmt != IM_BANDFMT_UCHAR ||
in->Coding != IM_CODING_NONE ) {
im_error( "invert", "bad image" );
return( -1 );
}
/* Set fields in output image.
*/
if( im_cp_desc( out, in ) )
return( -1 );
/* Process! The first user-parameter
* is the number of bands involved.
*/
if( im_wrapone( in, out,
(im_wrapone_fn)invert_buffer,
in, NULL ) )
return( -1 );
return( 0 );
}
\end{verbatim}
There are two significant hidden traps here. First, inside the buffer
processing functions, you may only read the contents of the user parameters
\verb+a+ and \verb+b+, you may not write to them. This is because on a
multi-CPU machine, several copies of your buffer-processing functions will
be run in parallel --- if they all write to the same place, there will be
complete confusion. If you need writeable parameters (for example, to count
and report overflows), you can't use \verb+im_wrapone()+, you'll have to
use the PIO system in all its gory detail, see below.
Secondly, your buffer processing function may not be called immediately. VIPS
may decide to delay evaluation of your operation until long after the call
to \verb+invert()+ has returned. As a result, care is needed to ensure
that you never read anything in your buffer-processing function that may
have been freed. The best way to ensure this is to use the local resource
allocators, such as \verb+im_open_local()+ and \verb+im_malloc()+. This issue
is discussed at length in the sections below, and in \pref{sec:appl}.
\verb+im_wrapone()+ is for operations which take exactly one input image. VIPS
provides a second function, \verb+im_wrapmany()+, which works for any number
of input images. The type of \verb+im_wrapmany()+ is slightly different:
\begin{verbatim}
int
im_wrapmany( IMAGE **in, IMAGE *out,
im_wrapmany_fn fn, void *a, void *b )
\end{verbatim}
\noindent
\begin{verbatim}
void
(*im_wrapmany_fn)( void **in, void *out,
int n, void *a, void *b )
\end{verbatim}
\noindent
\verb+im_wrapmany()+ takes a \verb+NULL+-terminated array of input images,
and creates a \verb+NULL+-terminated array of buffers for the use of your
buffer processing function. A function to add two \verb+IM_BANDFMT_UCHAR+
images to make a \verb+IM_BANDFMT_UCHAR+ image might be written as:
\begin{verbatim}
static void
add_buffer( unsigned char **in,
unsigned short *out, int n,
IMAGE *in )
{
int i;
int sz = n * in->Bands;
unsigned char *p1 = in[0];
unsigned char *p2 = in[1];
for( i = 0; i < sz; i++ )
out[i] = p1[i] + p2[i];
}
\end{verbatim}
This can be made into a PIO function with:
\begin{verbatim}
int
add_uchar( IMAGE *i1, IMAGE *i2,
IMAGE *out )
{
IMAGE *invec[3];
/* Check parameters. We don't need to
* check that i1 and i2 are the same
* size, im_wrapmany() does that for
* us.
*/
if( i1->BandFmt != IM_BANDFMT_UCHAR ||
i1->Coding != IM_CODING_NONE ||
i2->BandFmt != IM_BANDFMT_UCHAR ||
i2->Coding != IM_CODING_NONE ||
i1->Bands != i2->Bands ) {
im_error( "add_uchar", "bad in" );
return( -1 );
}
/* Set fields in output image. As
* input image, but we want a USHORT.
*/
if( im_cp_desc( out, i1 ) )
return( -1 );
out->BandFmt = IM_BANDFMT_USHORT;
out->Bbits = IM_BBITS_SHORT;
/* Process! The first user-parameter
* is the number of bands involved.
* invec is a NULL-terminated array of
* input images.
*/
invec[0] = i1; invec[1] = i2;
invec[2] = NULL;
if( im_wrapmany( invec, out,
(im_wrapone_fn)add_buffer,
i1, NULL ) )
return( -1 );
return( 0 );
}
\end{verbatim}
\subsection{Region descriptors}
Regions are the next layer of abstraction above image descriptors. A region
is a small part of an image, held in memory ready for processing. A region
is defined as:
\begin{verbatim}
typedef struct {
Rect valid;
IMAGE *im;
... and some other private fields,
... used by VIPS for housekeeping
} REGION;
\end{verbatim}
\noindent
where \verb+valid+ holds the sub-area of image \verb+im+ that this region
represents, and \verb+Rect+ is defined as:
\begin{verbatim}
typedef struct {
int left, top;
int width, height;
} Rect;
\end{verbatim}
\noindent
two macros are available for \verb+Rect+ calculations:
\begin{verbatim}
int IM_RECT_RIGHT( Rect *r )
int IM_RECT_BOTTOM( Rect *r )
\end{verbatim}
\noindent
where \verb+IM_RECT_RIGHT()+ returns \verb+left+ + \verb+width+, and
\verb+IM_RECT_BOTTOM()+ returns \verb+top+ + \verb+height+. A small library
of C functions are also available for \verb+Rect+ algebra, see the manual
pages for \verb+im_rect_intersectrect()+.
Regions are created with \verb+im_region_create()+. This has type:
\begin{verbatim}
REGION *im_region_create( IMAGE *im )
\end{verbatim}
\noindent
\verb+im_region_create()+ returns a pointer to a new region structure,
or \verb+NULL+ on error. Regions returned by \verb+im_region_create()+
are blank --- they contain no image data and cannot be read from or written
to. See the next couple of sections for calls to fill regions with data.
Regions are destroyed with \verb+im_region_free()+. It has type:
\begin{verbatim}
int im_region_free( REGION *reg )
\end{verbatim}
\noindent
And, as usual, returns 0 on success and non-zero on error, setting
\verb+im_error()+. You must free all regions you create. If you close
an image without freeing all the regions defined on that image, the image is
just marked for future closure --- it is not actually closed until the final
region is freed. This behaviour helps to prevent dangling pointers, and it
is not difficult to make sure you free all regions --- see the examples below.
\subsection{Image input with regions}
Before you can read from a region, you need to call \verb+im_prepare()+
to fill the region with image data. It has type:
\begin{verbatim}
int im_prepare( REGION *reg, Rect *r )
\end{verbatim}
Area \verb+r+ of the image on which \verb+reg+ has been created is prepared
and attached to the region.
Exactly what this preparation involves depends upon the image --- it can
vary from simply adjusting some pointers, to triggering the evaluation of a
series of other functions. If it returns successfully, \verb+im_prepare()+
guarantees that all pixels within \verb+reg->valid+ may be accessed. Note
that this may be smaller or larger than \verb+r+, since \verb+im_prepare()+
clips \verb+r+ against the size of the image.
Programs can access image data in the region by calling the macro
\verb+IM_REGION_ADDR()+. It has type
\begin{verbatim}
char *IM_REGION_ADDR( REGION *reg,
int x, int y )
\end{verbatim}
Provided that point (x,y) lies inside \verb+reg->valid+,
\verb+IM_REGION_ADDR()+ returns a pointer to pel $(x,y)$. Adding to the result
of \verb+IM_REGION_ADDR()+ moves to the right along the line of pels, provided
you stay strictly within \verb+reg->valid+. Add \verb+IM_REGION_LSKIP()+
to move down a line, see below. \verb+IM_REGION_ADDR()+ has some other
useful features --- see the manual page.
Other macros are available to ease address calculation:
\begin{verbatim}
int IM_REGION_LSKIP( REGION *reg )
int IM_REGION_N_ELEMENTS( REGION *reg )
int IM_REGION_SIZEOF_LINE( REGION *reg )
\end{verbatim}
\noindent
These find the number of bytes to add to the result of \verb+IM_REGION_ADDR()+
to move down a line, the number of band elements across the region and the
number of bytes across the region.
\fref{fg:paverage} is a version of \verb+average()+ which uses
regions rather than WIO input. Two things: first, we should really be
using \verb+vips_sink()+, see \pref{sec:sequence}, to do the rectangle
algebra for us. Secondly, note that we call \verb+im_pincheck()+ rather
than \verb+im_incheck()+. \verb+im_pincheck()+ signals to the IO system
that you are a PIO-aware function, giving \verb+im_prepare()+ much more
flexibility in the sorts of preparation it can do. Also see the manual
pages for \verb+im_poutcheck()+ and \verb+im_piocheck()+.
\begin{fig2}
\begin{verbatim}
#include <stdio.h>
#include <stdlib.h>
#include <vips/vips.h>
#include <vips/region.h>
int
average( IMAGE *im, double *out )
{
int total, i, y;
REGION *reg;
Rect area, *r;
/* Check im.
*/
if( im_pincheck( im ) )
return( -1 );
if( im->BandFmt != IM_BANDFMT_UCHAR || im->Coding != IM_CODING_NONE ) {
im_error( "average", "uncoded uchar images only" );
return( -1 );
}
/* Make a region on im which we can use for reading.
*/
if( !(reg = im_region_create( im )) )
return( -1 );
\end{verbatim}
\caption{First PIO average of image}
\label{fg:paverage}
\end{fig2}
\begin{fig2}
\begin{verbatim}
/* Move area over the image in 100x100 pel chunks.
* im_prepare() will clip against the edges of the image
* for us.
*/
total = 0;
r = &reg->valid;
area.width = 100; area.height = 100;
for( area.top = 0; area.top < im->Ysize; area.top += 100 )
for( area.left = 0; area.left < im->Xsize;
area.left += 100 ) {
/* Fill reg with pels.
*/
if( im_prepare( reg, &area ) ) {
/* We must free the region!
*/
im_region_free( reg );
return( -1 );
}
/* Loop over reg, adding to our total.
*/
for( y = r->top; y < IM_RECT_BOTTOM( r ); y++ ) {
unsigned char *p = IM_REGION_ADDR( reg, r->left, y );
for( i = 0; i < IM_REGION_N_ELEMENTS( reg ); i++ )
total += p[i];
}
}
/* Make sure we free the region.
*/
im_region_free( reg );
/* Find average.
*/
*out = (double) total / (IM_IMAGE_N_ELEMENTS( im ) * im->Ysize);
return( 0 );
}
\end{verbatim}
\caption{First PIO average of image (cont.)}
\end{fig2}
This version of \verb+average()+ can be called in exactly the same way as
the previous one, but this version has the great advantage of not needing
to have the whole of the input image available at once.
We can do one better than this --- if the image is being split into small
pieces, we can assign each piece to a separate thread of execution and get
parallelism. To support this splitting of tasks, VIPS has the notion of
a sequence.
\subsection{Splitting into sequences}
\label{sec:sequence}
A sequence comes in three parts: a start function, a processing function,
and a stop function. When VIPS starts up a new sequence, it runs the
start function. Start functions return sequence values: a void pointer
representing data local to this sequence. VIPS then repeatedly calls the
processing function, passing in the sequence value and a new piece of image
data for processing. Finally, when processing is complete, VIPS cleans up by
calling the stop function, passing in the sequence value as an argument. The
types look like this:
\begin{verbatim}
void *
(*start_fn)( IMAGE *out,
void *a, void *b )
int
(*process_fn)( REGION *reg,
void *seq, void *a, void *b )
int
(*stop_fn)( void *seq, void *a, void *b )
\end{verbatim}
\noindent
The values \verb+a+ and \verb+b+ are carried around by VIPS for your use.
For functions like \verb+average()+ which consume images but produce no image
output, VIPS provides \verb+vips_sink()+. This has type:
\begin{verbatim}
int vips_sink( VipsImage *in,
VipsStart start,
VipsGenerate generate,
VipsStop stop,
void *a, void *b )
\end{verbatim}
VIPS starts one or more sequences, runs one or more processing functions
over image \verb+in+ until all of \verb+in+ has been consumed, and then closes
all of the sequences down and returns. VIPS guarantees that the regions
the \verb+process_fn()+ is given will be complete and disjoint, that is,
every pixel in the image will be passed through exactly one sequence. To
make it possible for the sequences to each contribute to the result of the
function in an orderly manner, VIPS also guarantees that all start and stop
functions are mutually exclusive.
An example should make this clearer. This version of \verb+average()+
is very similar to the average function in the VIPS library --- it is only
missing polymorphism.
\begin{fig2}
\begin{verbatim}
#include <stdio.h>
#include <stdlib.h>
#include <vips/vips.h>
#include <vips/region.h>
/* Start function for average(). We allocate a small piece of
* storage which this sequence will accumulate its total in. Our
* sequence value is just a pointer to this storage area.
*
* The first of the two pointers VIPS carries around for us is a
* pointer to the space where we store the grand total.
*/
static int *
average_start( IMAGE *out )
{
int *seq = IM_NEW( out, int );
if( !seq )
return( NULL );
*seq = 0;
return( seq );
}
/* Stop function for average(). Add the total which has
* accumulated in our sequence value to the grand total for
* the program.
*/
static int
average_stop( int *seq, int *gtotal )
{
/* Stop functions are mutually exclusive, so we can write
* to gtotal without clashing with any other stop functions.
*/
*gtotal += *seq;
return( 0 );
}
\end{verbatim}
\caption{Final PIO average of image}
\label{fg:p2average}
\end{fig2}
\begin{fig2}
\begin{verbatim}
/* Process function for average(). Total this region, and
* add that total to the sequence value.
*/
static int
average_process( REGION *reg, int *seq )
{
int total, i, y;
Rect *r = &reg->valid;
/* Get the appropriate part of the input image ready.
*/
if( im_prepare( reg, r ) )
return( -1 );
/* Loop over the region.
*/
total = 0;
for( y = r->top; y < IM_RECT_BOTTOM( r ); y++ ) {
unsigned char *p = IM_REGION_ADDR( reg, r->left, y );
for( i = 0; i < IM_REGION_N_ELEMENTS( reg ); i++ )
total += p[i];
}
/* Add to the total for this sequence.
*/
*seq += total;
return( 0 );
}
\end{verbatim}
\caption{Final PIO average of image (cont.)}
\end{fig2}
\begin{fig2}
\begin{verbatim}
/* Find average of image.
*/
int
average( IMAGE *im, double *out )
{
/* Accumulate grand total here.
*/
int gtotal = 0;
/* Prepare im for PIO reading.
*/
if( im_pincheck( im ) )
return( -1 );
/* Check it is the sort of thing 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 in pieces, and possibly in parallel.
*/
if( vips_sink( im,
average_start, average_process, average_stop,
&gtotal, NULL ) )
return( -1 );
/* Calculate average.
*/
*out = (double) gtotal / (IM_IMAGE_N_ELEMENTS( im ) * im->Ysize);
return( 0 );
}
\end{verbatim}
\caption{Final PIO average of image (cont.)}
\end{fig2}
There are a couple of variations on \verb+im_prepare()+: you can use
\verb+im_prepare_to()+ to force writing to a particular place, and
\verb+im_prepare_thread()+ to use threaded evaluation. See the man pages.
\subsection{Output to regions}
\label{sec:generate}
Regions are written to in just the same way they are read from --- by
writing to a pointer found with the \verb+IM_REGION_ADDR()+ macro.
\verb+vips_sink()+ does input --- \verb+im_generate()+ does output. It
has the same type as \verb+vips_sink()+:
\begin{verbatim}
int
im_generate( IMAGE *out,
void *(*start_fn)(),
int (*process_fn)(),
int (*stop_fn)(),
void *a, void *b )
\end{verbatim}
The region given to the process function is ready for output. Each time
the process function is called, it should fill in the pels in the region
it was given. Note that, unlike \verb+vips_sink()+, the areas the process
function is asked to produce are not guaranteed to be either disjoint or
complete. Again, VIPS may start up many process functions if it sees fit.
Here is \verb+invert()+, rewritten to use PIO. This piece of code makes use
of a pair of standard start and stop functions provided by the VIPS library:
\verb+im_start_one()+ and \verb+im_stop_one()+. They assume that the first
of the two user arguments to \verb+im_generate()+ is the input image. They are
defined as:
\begin{verbatim}
REGION *
im_start_one( IMAGE *out, IMAGE *in )
{
return( im_region_create( in ) );
}
\end{verbatim}
\noindent
and:
\begin{verbatim}
int
im_stop_one( REGION *seq )
{
return( im_region_free( seq ) );
}
\end{verbatim}
They are useful for simple functions which expect only one input
image. See the manual page for \verb+im_start_many()+ for many-input
functions.
\begin{fig2}
\begin{verbatim}
#include <stdio.h>
#include <stdlib.h>
#include <vips/vips.h>
#include <vips/region.h>
/* Process function for invert(). Build the pixels in or
* from the appropriate pixels in ir.
*/
static int
invert_process( REGION *or, REGION *ir )
{
Rect *r = &or->valid;
int i, y;
/* Ask for the part of ir we need to make or. In this
* case, the two areas will be the same.
*/
if( im_prepare( ir, r ) )
return( -1 );
/* Loop over or writing pels calculated from ir.
*/
for( y = r->top; y < IM_RECT_BOTTOM( r ); y++ ) {
unsigned char *p = IM_REGION_ADDR( ir, r->left, y );
unsigned char *q = IM_REGION_ADDR( or, r->left, y );
for( i = 0; i < IM_REGION_N_ELEMENTS( or ); i++ )
q[i] = 255 - p[i];
}
/* Success!
*/
return( 0 );
}
\end{verbatim}
\caption{PIO invert}
\label{fg:p2invert}
\end{fig2}
\begin{fig2}
\begin{verbatim}
/* Invert an image.
*/
int
invert( IMAGE *in, IMAGE *out )
{
/* Check descriptors for PIO compatibility.
*/
if( im_piocheck( in, out ) )
return( -1 );
/* Check input image for compatibility with us.
*/
if( in->BandFmt != IM_BANDFMT_UCHAR || in->Coding != IM_CODING_NONE ) {
im_error( "invert", "uncoded uchar images only" );
return( -1 );
}
/* out inherits from in, as before.
*/
if( im_cp_desc( out, in ) )
return( -1 );
/* Set demand hints for out.
*/
if( im_demand_hint( out, IM_THINSTRIP, in, NULL ) )
return( -1 );
/* Build out in pieces, and possibly in parallel!
*/
if( im_generate( out,
im_start_one, invert_process, im_stop_one,
in, NULL ) )
return( -1 );
return( 0 );
}
\end{verbatim}
\caption{PIO invert (cont.)}
\end{fig2}
Functions have some choice about the way they write their output. Usually, they
should just write to the region they were given by \verb+im_generate()+. They
can, if they wish, set up the region for output to some other place. See
the manual page for \verb+im_region_region()+. See also the source for
\verb+im_copy()+ and \verb+im_extract()+ for examples of these tricks.
Note also the call to \verb+im_demand_hint()+. This function hints to the IO
system, suggesting the sorts of shapes of region this function is happiest
with. VIPS supports four basic shapes --- choosing the correct shape can
have a dramatic effect on the speed of your function. See the man page for
full details.
\subsection{Callbacks}
\label{sec:callback}
VIPS lets you attach callbacks to image descriptors. These are functions
you provide that VIPS will call when certain events occur. There are more
callbacks than are listed here: see the man page for full details.
\subsubsection{Close callbacks}
These callbacks are invoked just before an image is closed. They are useful
for freeing objects which are associated with the image. All callbacks are
triggered in the reverse order to the order in which they were attached. This
is sometimes important when freeing objects which contain pointers to
other objects. Close callbacks are guaranteed to be called, and to be called
exactly once.
Use \verb+im_add_close_callback()+ to add a close callback:
\begin{verbatim}
typedef int (*im_callback)( void *, void * )
int im_add_close_callback( IMAGE *,
im_callback_fn,
void *, void * )
\end{verbatim}
As with \verb+im_generate()+, the two \verb+void *+ pointers
are carried around for you by VIPS and may be used as your
function sees fit.
\subsubsection{Preclose callbacks}
Preclose callbacks are called before any shutdown has occured. Everything is
still alive and your callback can do anything to the image. Preclose callbacks
are guaranteed to be called, and to be called exactly once. See the manual
page for \verb+im_add_preclose_callback()+ for full details.
\subsubsection{Eval callbacks}
These are callbacks which are invoked periodically by VIPS during evaluation.
The callback has access to a struct containing information about the progress
of evaluation, useful for user-interfaces built on top of VIPS. See the
manual page for \verb+im_add_eval_callback()+ for full details.
\subsection{Memory allocation revisited}
When you are using PIO, memory allocation becomes rather more complicated than
it was before. There are essentially two types of memory which your function
might want to use for working space: memory which is associated with each
instance of your function (remember that two copies of you function may be
joined together in a pipeline and be running at the same time --- you can't
just use global variables), and memory which is local to each sequence
which VIPS starts on your argument image.
The first type, memory local to this function instance, typically holds
copies of any parameters passed to your image processing function, and links
to any read-only tables used by sequences which you run over the image. This
should be allocated in your main function.
The second type of memory, memory local to a sequence, should be allocated
in a start function. Because this space is private to a sequence, it may be
written to. Start and stop functions are guaranteed
to be single-threaded, so you may write to the function-local memory within
them.