Diplib Programmers Guide
Diplib Programmers Guide
2 Conventions 2
2.1 Include files . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 2
2.2 ANSI-C source code conventions . . . . . . . . . . . . . . . . . . . . . . . . . 2
3 Initialisation 4
3.1 Introduction . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 4
3.2 Clean up . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 4
4 Error handling 5
4.1 Writing a DIPlib function . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 5
4.2 Error macros . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 7
5 Resource management 9
5.1 The scheme . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 9
5.2 Cleanup operations . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 11
5.3 Tracking your own resources . . . . . . . . . . . . . . . . . . . . . . . . . . . . 11
6 Memory allocation 14
8 Data types 20
8.1 Introduction . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 20
8.1.1 The complex data types . . . . . . . . . . . . . . . . . . . . . . . . . . 20
8.1.2 The binary data types . . . . . . . . . . . . . . . . . . . . . . . . . . . 20
8.2 Dynamic versus static data types . . . . . . . . . . . . . . . . . . . . . . . . . 21
8.2.1 Data type information from dip DataType . . . . . . . . . . . . . . . 22
8.2.2 Overloading . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 22
Overloading scheme #1 . . . . . . . . . . . . . . . . . . . . . . . . . . 22
Overloading scheme #2 . . . . . . . . . . . . . . . . . . . . . . . . . . 23
8.2.3 Type iterators . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 24
9 Image infrastructure 27
9.1 The dip Image structure . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 27
9.2 Allocating and de-allocating images . . . . . . . . . . . . . . . . . . . . . . . 28
9.3 Writing image processing operations . . . . . . . . . . . . . . . . . . . . . . . 29
9.3.1 The general structure . . . . . . . . . . . . . . . . . . . . . . . . . . . 29
III
IV Contents
10 Boundary conditions 34
11 Frameworks 36
11.1 Introduction . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 36
11.2 The separable filter framework . . . . . . . . . . . . . . . . . . . . . . . . . . 36
11.2.1 Introduction . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 36
11.2.2 Usage . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 37
11.3 The monadic and single output frameworks . . . . . . . . . . . . . . . . . . . 38
11.4 The scan framework . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 38
11.5 The pixel table framework . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 38
11.6 Framework convenience functions . . . . . . . . . . . . . . . . . . . . . . . . . 38
Chapter 1
Introduction
DIPlib is a scientific image processing library written in C. It consists of a large number
of functions for processing and analysing multi-dimensional image data. The library pro-
vides functions for performing transforms, filter operations, object generation, local structure
analysis, object measurements and statistical analysis of images.
The current release contains over five hundred (500) documented functions, and more than
three hundred (300) of these functions provide image processing functionality. The remaining
functions provide access to DIPlib’s data structures and other support functionality.
In the current release, only scalar images of standard C data types, binary data types and
complex (floating point only) data types are supported. The data type is a property of an
image. Our philosophy is that an image processing algorithm should not be tied too closely to
the data type or dimensionality. In practice this means that a single function accepts images
of various data types and dimensionality. We have attempted to deal sensibly with this.
Two examples: the Gaussian filter accepts both integer and floating point images, but always
returns a floating point image. A grey value dilation does not introduce new grey-values.
The dilation function also accepts both integer and floating point images, but the output
data type is the same as the input data type in this case. Both filter functions accept input
images of arbitrary dimensionality. Some functions are tied to a particular dimensionality,
for instance most skeletonisation algorithms, and these only operate on an image with the
correct dimensionality.
The library does not contain any I/O or display functionality. A separate library, dipIO, is
available for I/O functionality, with support for ICS, TIFF, JPEG, GIF and a variety of other
file types.
We use MATLAB as our image processing environment and as frontend to DIPlib. The
frontend, DIPimage, is available as a MATLAB toolbox. Together MATLAB and DIPimage
yield a powerful workbench for working with scalar and vector images in any number of
dimensions.
More information about DIPlib and DIPimage can be found at their web page:
http://www.diplib.org/.
1
Chapter 2
Conventions
This chapter describes the file and C statements conventions used in the DIPlib and dipIO
source libraries. We will use the term public to specify that something (like a function) is
documented, supported, and available on the DIPlib library level. Something is called private
when it is undocumented and unsupported.
2
DIPlib Programmers Guide 3
#include "diplib.h"
/* dip_error.h is included through diplib.h */
dip_Error dip_MyFunction
(
dip_Image in
)
{
DIP_FN_DECLARE("dip_MyFunction");
dip_int ii;
dip_error:
DIP_FN_EXIT;
}
Chapter 3
Initialisation
DIPlib and dipIO have to be initialised before use and also require some clean up operations
after use.
3.1 Introduction
Before DIPlib functions can be used, the library needs to be initialised. This is achieved by
calling the following function:
dip_Initialise();
It is safe to call dip Initialise more than one time, only the first call will be effective.
The equivalent function for dipIO is:
dipio_Initialise();
3.2 Clean up
Before exiting a program linked with DIPlib, the following function should be called, allowing
some clean up operations to be performed (preventing memory leaks):
dip_Exit();
dipio_Exit();
4
Chapter 4
Error handling
In order to use or write DIPlib functions it is necessary to know how errors are dealt with.
DIPlib’s error mechanism has the following features:
• consistent error handling
• errors are handled using functions’ return values
• a function call tree is maintained by the error functions and macros to facilitate the
debugging of DIPlib code.
• each function in the call tree can have an arbitrary message associated with it.
Consider the following schematic piece of code:
main()
{
DoSomethingClever();
DoSomethingDumb();
}
DoSomethingClever()
{
DoSomethingStupid();
}
5
6 Chapter 4. Error handling
Main
Figure 4.1: A DIPlib call tree. Each entry shows the function name and (optionally) a
message.
A line containing the label dip error should precede the DIP FN EXIT macro. This label is
used by the error macros to jump to the end of the function if an error condition occurs.
Only if your routine does not use one of these macros, can this label be omitted. Any
cleanup operations, such as freeing memory, should be done after this label, but before the
DIP FN EXIT macro.
The return type of the function should be dip Error, which is a typedef for a pointer to the
actual error structure dip Error. If a function executes without generating an error, the zero
pointer is returned (use the supplied macros to deal with errors, rather than manipulating
the error structure yourself).
There is also a set of macros starting with DIP FNR, which are similar to the DIP FN macros.
The introduction of these is deferred to chapter 5.
Summarising, a DIPlib function should:
• have a return type of dip Error
• precede its declarations with the macro DIP FN DECLARE
• end with the label dip error, followed by cleanup actions, and finally the DIP FN EXIT
macro.
The template for a DIPlib function is as follows:
DIPlib Programmers Guide 7
dip_Error
dip_MyFunction
(
/* Your arguments */
)
{
DIP_FN_DECLARE("dip_MyFunction");
/* Your declarations here */
/*
* Example of how to generate an error condition and set the
* error message.
*/
DIPSJ( "Your error message" );
dip_error:
/* Your clean up code */
DIP_FN_EXIT;
}
predefined static error messages, which you can use. A complete list is given in the reference
manual.
It is also possible to pass dynamically allocated (using dip MemoryNew) strings to DIP FN EXIT.
These need to be explicitly freed by dip MemoryFree after DIP FN EXIT has finished its job.
To achieve this the string should be passed by the DIPJF macro, short for ”set, Jump and
Free”. DIP FN EXIT will now take care of freeing the string.
DIPTS, short for ”Test, Set and jump”, takes two arguments. The first is a test, the second
the error message that should be passed to DIP FN EXIT if the test result is true. It is in fact
just short hand for:
Table 4.1 gives an overview of all the macros described in this chapter. It includes the DIP FNR
macros, which are discussed in chapter 5.
Resource management
dip_Resources resources;
dip_Image image, anotherImage;
dip_int *iptr;
void *vptr;
dip_ResourcesNew( &resources, 0 );
dip_ImageNew( &image, resources );
dip_ImageNew( &anotherImage, resources );
dip_MemoryNew( &vptr, 5000 * sizeof( dip_int ), resources );
iptr = vptr;
9
10 Chapter 5. Resource management
image = 0;
anotherImage = 0;
iptr = 0;
dip_ImageNew( &image, 0 );
dip_ImageNew( &anotherImage, 0 );
dip_MemoryNew( &vptr, 5000 * sizeof( dip_int ), 0 );
iptr = vptr;
The order in which resources are freed using dip ResourcesFree is unspecified. The order
may even change between different releases of DIPlib.
The majority of the DIPlib functions starts by allocating a resources structure at the start
and freeing it at the end of the routine. We have therefore made variants of the DIP FN error
macros that automate this process. DIP FNR DECLARE is identical to DIP FN DECLARE, but
also declares a resources structure. The name of the resources structure is ”rg”. It should
be followed by your own declarations. The resources structure is initialised by invoking the
DIP FNR INITIALISE macro at the start of your code. Finally, the routine should exit using
the DIP FNR EXIT macro. This macro first frees the resources associated with ”rg”, and
subsequently performs the same tasks as DIP FN EXIT. The layout of a typical DIPlib routine
using the resource management scheme is given below:
DIPlib Programmers Guide 11
dip_Error
dip_MyFunction( void )
{
DIP_FNR_DECLARE("dip_MyFunction");
dip_Image image;
DIP_FNR_INITIALISE;
dip_error:
/* Frees the dip_Resources structure and the image */
DIP_FNR_EXIT;
}
/* Other code */
dip_ResourcesFree( &resources, 0 );
register two things in a dip Resources structure: a void pointer and a handler that will be
called by dip ResourcesFree when your resource is to be freed. dip ResourceUnsubscribe
can be used to stop tracking a particular resource. The resource itself will not be freed by
dip ResourceUnsubscribe. It should only be used on resources that have been registered
directly by dip ResourceSubscribe, not on indirectly registered resources such as images
allocated by dip ImageNew.
When writing a function that allocates a structure that requires the allocation of many
substructures, dip ResourcesMerge may come in handy. It is best demonstrated by the
example given in figure 5.1:
dip_AllocateMyThing
(
myThing *thing,
dip_Resources resources
)
{
DIP_FNR_DECLARE("dip_AllocateMyThing");
DIP_FNR_INITIALISE;
/*
* Allocate your stuff and register it in "rg", the local
* resources structure allocated and initialised by
* the DIP_FNR macros.
*/
/*
* If none of the allocations failed, the following will merge
* the local resources into the resources. It will then free the
* local resources structure (not the resources it was tracking).
*/
dip_error:
/*
* If anything went wrong before the call to dip_ResourcesMerge,
* DIP_FNR_EXIT will free all resources allocated by our
* function.
*/
DIP_FNR_EXIT;
}
13
14 Chapter 6. Memory allocation
Chapter 6
Memory allocation
The DIPlib library offers its own memory allocation functions. Use them instead of the
standard malloc functions to allocate memory. In the future, the use of DIPlib’s own memory
allocation functions could improve memory use and could simplify the portability of the
library. Most importantly, the DIPlib allocation routines support the DIPlib error mechanism
and resource management.
Memory can be allocated using the dip MemoryNew function. It takes a pointer to a void
pointer as its first argument. If the allocation is succesful the void pointer will point to the
allocated memory. The second argument is the amount of bytes to allocate. The memory can
be registered in a resources structure by passing it as the third parameter.
Memory can be freed using the dip MemoryFree function. It takes a pointer to the mem-
ory to be freed. Usually memory is tracked using the resource management scheme, so
dip MemoryFree is not often used explicitly. It is allowed to pass a zero pointer to
dip MemoryFree, in which case it does nothing.
Finally, memory chunks can be resized using dip MemoryReallocate. The arguments are the
same as those for dip MemoryNew, except that the pointer to a void pointer must be a valid
address previously returned by dip MemoryNew. If the reallocation fails, the pointer will not
be overwritten.
If the developent environment provides its own memory management functions,
dip MemoryFunctionsSet can be used to tell DIPlib to use those functions instead of the
default malloc, realloc, and free.
The following example shows how to allocate ten integers, please read the comment in the
code carefully:
DIPlib Programmers Guide 15
dip_int *mypointer;
void *voidpointer;
/*
* One might be tempted to write:
* dip_MemoryNew( &mypointer, 10 * sizeof( dip_int ), 0 ));
* but "C" does not guarantee that different pointers have the
* same representation. A void pointer can represent any other
* pointer though, although a conversion may be required. In the
* code above this conversion is done by the assignment of the
* void pointer to the integer pointer (implicit cast).
*/
7.1 Arrays
In many places an array is required to store some data. For example, the Gaussian filter in
DIPlib may have a different sigma in every dimension. To pass these sigma values to the
Gaussian filter an array is employed. Array’s in C do not have an explicit size field. For
example, it cannot be verified that the number of elements and the dimensionality of the
image that is to be filtered match. In many other places it would be useful to know the size
of the array as well.
To solve these problems, DIPlib defines a set of array types. One of these is the
dip IntegerArray type. It is defined as follows:
typedef struct
{
dip_int size;
dip_int *array;
} dip__IntegerArray, *dip_IntegerArray;
The basic set of these array types consists of: dip IntegerArray, dip FloatArray,
dip ComplexArray, dip BooleanArray and dip VoidPointerArray. We’ll refer to these kind
of array types as Arrays as opposed to normal arrays.
A dip IntegerArray can be allocated using dip NewIntegerArray. There are corresponding
allocation routines for the other Arrays as well.
There are two typedefs pertaining to each Array. One for the actual structure itself and one
for a pointer to the structure. The reason for this is explained in section 7.2. Usually Arrays
are handles through the pointer types, such as dip IntegerArray.
The Array type definitions are public. This means that their definition is fixed. Although
using dip ResourcesFree is usually much more convenient, it is also ”legal” to free an Array
by hand as follows:
16
DIPlib Programmers Guide 17
dip_IntegerArray myArray;
dip_Resources resources;
dip_IntegerArray myArray;
An Array may have size zero. It is also allowed to set the size field to a smaller value without
reallocating the array pointed to by the array element.
Using an Array is almost as simple as using a normal C array. The following code shows the
content of an Array:
dip_IntegerArray myArray;
dip_int ii;
typedef struct
{
dip_DataType dataType;
dip_IntegerArray dimensions;
void *data;
} dip__TheImage;
The dip DataType type is explained in chapter 8. It simply states whether the pixels are
represented by integers, floating point numbers etc. The dimensions Array contains the
dimensionality and the size of each dimension. The prototype for a copy function looks like
this:
typedef struct
{
dip_DataType dataType;
dip_IntegerArray dimensions;
void *data;
} dip__TheImage, *dip_TheImage;
Here we have employed a design policy to hide pointer definitions in a typedef. Source code
becomes a little more pleasant to read, although the programmer must remain aware of the
pointer nature of these types.
Now our second objective of hiding the definition is achieved by putting the definition of
dip TheImage in a private include file, that is not included in the distribution and defining
dip TheImage as a void pointer. The result looks like this:
DIPlib Programmers Guide 19
The definition of dip TheImage as a void pointer has some undesirable consequences. When
a pointer of the wrong type is passed to a function expecting a dip TheImage, it will be
silently converted to a void pointer by the compiler. To make sure that the compiler can
check dip TheImage parameters, we have to add an extra indirection to our definition:
This is the approach we have used with both the dip Resources and dip Image structure.
The latter is introduced in chapter 9.
Because dip IntegerArray, dip Resources and dip Image are in fact pointers, we can assign
zero to a variable of one of these types. It is also possible to pass zero to a routine expecting
a variable of one of these types. Many routines do accept zero as a valid argument, usually
meaning that this argument must be ignored by the routine.
Chapter 8
Data types
8.1 Introduction
Pixel values can be represented by different types. Such types will be referred to as data
types. DIPlib uses five sets of data types: unsigned integers, signed integers, floating point
numbers, complex numbers and binary numbers. All of these come in different sizes, each able
to represent a different range of values. The complete set of data types is given in table 8.1.
There are also generic data types: dip binary, dip int, dip float and dip complex. These
can be used when the exact range doesn’t matter (non pixel data).
The complex and binary data types are discussed in the following two sections.
20
DIPlib Programmers Guide 21
/*
* dip_bin8 is identical to dip_uint8, but shows our intent to use it
* as a container for binary values.
*/
dip_bin8 binaryData;
dip_int bit2;
/*
* read bit 2 from binaryData and set the variable bit2 accordingly
*/
bit2 = ( binaryData & ( 1 << 2 )) >> 2;
Figure 8.2: Data types, their corresponding dip DataType flags, bitwise flags, and suffixes.
8.2.2 Overloading
The term overloading is used to describe the scheme that is used by DIPlib to call type
specific routines from a type independent routine. Which routine is called is determined by
a dip DataType. There are actually two overloading schemes. One more closely resembles
a function call, while the other is more flexible. For both schemes the user has to provide
a base name for the function to be called. The overload scheme attaches a type dependent
suffix to this base name, as given by table 8.2, and calls the corresponding function.
Overloading scheme #1
The first scheme defines a macro called DIP OVERLOAD FUNC which allows you to invoke a type-
dependent function almost like an ordinary function. The following steps must be undertaken
to use the macro:
• At the top of your code, do the following:
#define DIP OVL ALLOW <list of allowed data types>
#include "dip overload.h"
• At the spot where the type specific code should be called:
DIP OVERLOAD FUNC( <base name of the function> ( <argument list> ),
<data type> )
The data types for which a function is available are specified by defining DIP OVL ALLOW. This
define must be followed by a bitwise OR of the flags specified in table 8.2. Besides these flags,
DIPlib Programmers Guide 23
which each specify a single data type, there is also a set of flags that specify entire groups of
data types. These flags are given in table 8.3. If DIP OVL ALLOW is not defined, all data types
will be overloaded.
Since DIP OVL ALLOW is defined at the start of the source file, DIP OVERLOAD FUNC can not be
used for calling type specific functions that are available for different sets of data types. This
is possible with the second scheme though.
Overloading scheme #2
The second scheme also uses the C preprocessor to do the overloading. To perform the function
call to the type specific routine, the following recipe must be inserted into the source code at
the place the function call is supposed to be executed:
• #define DIP OVL FUNC <base name of the function>
• #define DIP OVL ARGS <argument list>
• #define DIP OVL ALLOW <list of allowed data types>
• #include "dip ovl.h"
The data type is assumed to be in a variable called ovlDataType. If DIP OVL ALLOW is not de-
fined, all data types are overloaded. The following example shows how a function dip Filter
calls the appropriate low-level routine for the two data types it supports, DIP DT SFLOAT and
DIP DT DFLOAT.
24 Chapter 8. Data types
dip_Filter
(
dip_Image in,
dip_Image out
)
{
dip_DataType ovlDataType;
/*
* Some code, which we assume initialises ovlDataType, as well
* as the two data pointers inData and outData.
*/
/*
* if ovlDataType = DIP_DT_SFLOAT the code above will execute:
* DIPXJ( dip_Filter_sfl( inData, outData ));
* if ovlDataType = DIP_DT_DFLOAT the code above will execute:
* DIPXJ( dip_Filter_dfl( inData, outData ));
* for all other data types :
* DIPSJ( dip_errorDataTypeNotSupported );
*/
}
In addition to DIP OVL ARGS it is also possible to set DIP OVL BINARY ARGS. If defined, it
will be used instead of DIP OVL ARGS for the binary data types. Often the parameter list for
binary data types includes extra parameters, such as plane numbers.
The default action undertaken by dip ovl.h is to call a type specific routine using DIPXJ().
Sometimes it is necessary to set a function pointer to a type specific routine. This can be
accomplished by defining DIP OVL ASSIGN as ”<filter pointer> =”. Again the binary
data types can be treated separately using DIP OVL BINARY ASSIGN.
for the set of data types. The following recipe shows how to use the scheme:
• #define DIP TPI FILE <name of file containing type specific code>
• #define DIP TPI ALLOW <list of allowed data types>
• #include "dip tpi.h"
The include file iterates over all speficied data types and during each iteration assigns the
current data type to DIP TPI. In the type specific code DIP TPI must be used to refer to the
current data type. Besides DIP TPI a number of other symbols are also defined by dip tpi.h.
For example DIP TPI DATA TYPE is the dip DataType corresponding to DIP TPI. The other
symbols are explained in the reference manual.
The most elegant way to use this scheme is by putting the type independent code and the
type specific code in one file and letting this file #include itself using dip tpi.h. This works
as demonstrated by the piece of pseudo code shown in figure 8.4.
The scheme can also be used in include files to generate prototypes for type specific functions.
26 Chapter 8. Data types
filter.c:
/* When the compiler starts processing this program, DIP_TPI will be
* undefined and process the code directly following the next
* #ifndef... */
#ifndef DIP_TPI
/* This is where the type specific code is stored. The compiler will
* reach this code only through including "dip_tpi.h". It will be
* included for each realization of DIP_TPI, in this case both
* dip_sfloat and dip_dfloat. */
#else
in = inData;
out = outData;
/* Execute algorithm */
}
Figure 8.4: Example of self including code for multiple data types.
Chapter 9
Image infrastructure
D−1
X
address = origin + cor[i] ∗ stride→array[i]
i=0
Where origin is the address of the pixel at the origin of the image and stride[] is an Array
stored in the dip Image structure. For each dimension it holds the interleave between two
neighbouring pixels in memory.
For binary images the plane field holds the number of the bit in which the binary data is
stored.
The data pointer, plane and stride fields are all volatile. They can be changed by most
27
28 Chapter 9. Image infrastructure
Figure 9.2: The dip Image fields and their access functions.
functions. It is only safe to use the information in these fields during the time you access the
pixel data of the image. See section 9.3.5 on how to safely access the pixel data.
dip_Image image;
dip_IntegerArray dimensions;
dip_IntegerArrayNew( &dimensions, 2, 0, 0 );
dimensions->array[ 0 ] = 156;
dimensions->array[ 1 ] = 111;
dip_ImageNew( &image, 0 );
dip_ImageSetType( image, DIP_IMTP_SCALAR );
dip_ImageSetDataType( image, DIP_DT_SFLOAT );
dip_ImageSetDimensions( image, dimensions );
dip_ImageForge( image );
An image is said to be raw before the call to dip ImageForge and forged afterwards. The
DIPlib Programmers Guide 29
dip ImageSet functions can only be used on a raw image. While this scheme may seem
complex, it is very flexible. It will also simplify the integration of any future image types.
There are also simpler ways to allocate an image, see section 9.4.
The dip ImageStrip function deallocates the image data if present and resets all image fields
to their initial value, thus returning the image to its raw state. dip ImageFree first calls
dip ImageStrip and then frees the dip Image structure itself. It is almost never necessary
to call dip ImageFree directly because of the resource tracking scheme.
dip_ImagesCompare(), dip_ImagesCompareTwo(),
dip_ImageCheck(),
dip_ImagesCheck(), dip_ImagesCheckTwo(),
dip_IsScalar(),
dip_DataTypeAllowed(),
30 Chapter 9. Image infrastructure
As an example we show how to check whether two images have the same size ( dimensionality
and individual dimensions):
/*
* The last parameter is 0. This will cause dip_ImagesCompareTwo
* to return an error when the images do not have the same size.
*/
dip_ImagesCompareTwo( image1, image2, DIP_CPIM_SIZE_MATCH, 0 );
accepts an array of flags (one for each input image) with which you can indicate that the
input data was copied to a safe place. This prevents unnecessary allocation of temporary
output images.
if "out is forged"
{
dip_ImageStrip( out );
}
Set properties of out to what they are supposed to be;
dip_ImageForge( out );
}
If the output should have the same size as the input and should always have data type
DIP DT SFLOAT, this can be achieved by the following code:
32 Chapter 9. Image infrastructure
dip_ImageNew( &dummy, 0 );
dip_ImageCopyProperties( in, dummy );
dip_ImageSetDataType( dummy, DIP_DT_SFLOAT );
dip_ImageAssimilate( dummy, out );
It is also possible to use dip ImageAssimilate to allocate a temporary image that has the
same properties as some existing image:
dip_ImageNew( &tmpImage, 0 );
dip_ImageAssimilate( image, tmpImage );
The function dip ImageClone is merely short hand for these two calls. Note that the image
data is not copied. If this is desired, substitute dip Copy for dip ImageAssimilate (dip Copy
will call dip ImageAssimilate and then copy the data).
Boundary conditions
One of the design features of the DIPlib library is to standardise the way filters deal with the
borders of an image. This is done by defining a set of boundary conditions describing how an
image should be extended beyond the borders of that image.
Most of the filters that are supplied by the DIPlib library accept an array of boundary
conditions. This array specifies what the boundary condition is for each dimension of the
image that has to be filtered. These functions also accept NULL for this parameter, causing
the default boundary condition to be used. This default is set to DIP BC SYM MIRROR by
dip Initialise(), and can be changed through dip GlobalBoundaryConditionSet(). It
is possible to specify a different boundary condition for each image dimension.
The boundary conditions supported by the DIPlib library are specified in the dip Boundary
enumeration type (defined in dip support.h). The current inplemetation of the library
supports the boundary conditions specified in table 10.1 (see 10.2)
Please note that using mirroring as implemented by DIPlib, the border pixels are duplicated.
Thus, if an image 123 is extended by mirroring, it will become 123321 and not 12321.
If a filter can not support a certain boundary condition, it should return the
DIP E BOUNDARY CONDITION NOT SUPPORTED error code.
The DIPlib library supplies two functions to facilitate the processing of the boundary con-
ditions: The function dip FillBoundary() extends a dip Image according to the boundary
condition. The function dip FillBoundaryArray() extends a one dimensional array.
The FrameWork functions (see chapter 11) process the boundary conditions for the filters
that use one of these FrameWork functions. Therefore these filters do not have to handle the
boundary conditions themselves, but only have to pass on the boundary conditions to one of
the frameworks.
34
Name Description
DIP BC SYM MIRROR Symmetric mirroring
DIP BC ASYM MIRROR Asymmetric mirroring
DIP BC PERIODIC Periodic copying
DIP BC ASYM PERIODIC Asymmetric periodic copying
DIP BC ADD ZEROS Extending the image with zeros
DIP BC ADD MAX VALUE Extending the image with + infinity
DIP BC ADD MIN VALUE Extending the image with − infinity
...
...
...
...
...
...
Figure 10.2: Illustration of the main boundary extensions. From left to right: the original
image, symmetric mirror, asymmetric mirror, periodic, and asymmetric periodic. The images
with a black background represent an image multiplied by -1.
35
36 Chapter 11. Frameworks
Chapter 11
Frameworks
11.1 Introduction
To quicken the development and the execution speed of image processing filters, the DIPlib
library supplies several framework functions. The following frameworks are available in the
current release,
• dip SeparableFrameWork
• dip MonadicFrameWork
• dip SingleOutputFrameWork
• dip PixelTableFrameWork
• dip ScanFrameWork
The following sections describe the funtionality and use of each of these frameworks. For a
complete explaination of their function arguments and behaviour, we refer to the reference
manual.
After processing all lines in one dimensions, dip SeparableFrameWork will repeat this for all
the dimensions in the image.
Besides copying the images lines to 1-D arrays, the dip SeparableFrameWork also extends the
arrays by adding extra pixels to both sides of the line. This done to facilitate the processing
of the borders of the image. By enlarging the arrays on both sides with an user specified
number of pixels, the user-supplied filter function can savely processing all the image line
pixels without the need to worry about the edges. The values of the pixels added to the
arrays is determined by a user-supplied array of boundary conditions.
11.2.2 Usage
The definition of the dip SeparableFrameWork function is:
The first argument is the input image, the second the output image. The third argument is an
array (its size equal to dimensionality of the input image) specifying the boundary condition
of the image in each dimension. This argument can be set to zero, in which can the global
default boundary conditions are used. The fourth argument specifies the border extension,
i.e. how many pixels dip SeparableFrameWork should add to the input and output array.
(again this array has a siz equal to the dimensionality of the input image). If the input and
output arrays do not require extension, this argument can be set to zero. The final argument
is an array of dip FrameWorkProcess structures.
The definition of the user-supplied 1-D filter function is:
The first two arguments are pointers to the input and output arrays. These arguments are
followed by the size (in number of pixels) of these arrays. The final argument is a structure
containing additional information about the input and output arrays. This structure will be
discussed later. We name this filter function the SeparableFilterFunction from now on.
dip SeparableFrameWork creates this array of pixels by copying it from the input image and
extendeding it with a border. The size of this border is specified by the function that calls
dip SeparableFrameWork. dip SeparableFrameWork will give the filterfunction a pointer to
the first pixel of the line of the input to be processed, allowing it to access pixels on both
sides of the line. Therefore no special border processing code needs to be written for the
filterfunction, reducing coding time and code complexity.
Having created a filterfunction, or different filterfunctions for processing some di-
mensions of the image in a different manner, one needs to create an array of
dip FrameWorkProcess structures. with the number of structures determines the number
of times dip SeparableFrameWork has to process the image (this number can be less, equal
or larger than the dimensionality of the image). If just one process structure is provided and
38 Chapter 11. Frameworks
the number of structures is set to zero, that structure is used to process all dimensions of the
image.
#include "diplib.h"
#include "dip_framework.h"
dip_Error dip_Uniform3x3
(
dip_Image in,
dip_Image out,
dip_BoundaryArray boundary
)
{
DIP_FNR_DECLARE("dip_Uniform3x3");
dip_int ii, dim;
dip_IntegerArray border;
dip_FrameWorkProcess process;
DIP_FNR_INITIALISE;
dip_error:
DIP_FNR_EXIT;
}
40 Chapter 11. Frameworks
#include "diplib.h"
#include "dip_framework.h"
dip_Error dip__Uniform3x3
(
void *input,
void *output,
dip_int size,
dip_SeparableFilterParameters params
)
{
DIP_FN_DECLARE("dip__Uniform3x3");
dip_int ii;
dip_float *in, *out;
in = input;
out = output;
DIP_FN_EXIT;
}