A few functions in the numarray.nd_image take a call-back
argument. This can be a python function, but also a CObject containing a
pointer to a C function. To use this feature, you must write your own C
extension that defines the function, and define a python function that
returns a CObject containing a pointer to this function.
An example of a function that supports this is
geometric_transform (see section ). You can pass it a python callable object that defines a
mapping from all output coordinates to corresponding coordinates in the
input array. This mapping function can also be a C function, which
generally will be much more efficient, since the overhead of calling a
python function at each element is avoided.
For example to implement a simple shift function we define the following
function:
static int
_shift_function(int *output_coordinates, double* input_coordinates,
int output_rank, int input_rank, void *callback_data)
{
int ii;
/* get the shift from the callback data pointer: */
double shift = *(double*)callback_data;
/* calculate the coordinates: */
for(ii = 0; ii < irank; ii++)
icoor[ii] = ocoor[ii] - shift;
/* return OK status: */
return 1;
}
This function is called at every element of the output array, passing the
current coordinates in the output_coordinates array. On return, the
input_coordinates array must contain the coordinates at which the
input is interpolated. The ranks of the input and output array are passed
through output_rank and input_rank. The value of the shift is
passed through the callback_data argument, which is a pointer to
void. The function returns an error status, in this case always 1, since no
error can occur.
A pointer to this function and a pointer to the shift value must be passed
to geometric_transform. Both are passed by a single CObject
which is created by the following python extension function:
static PyObject *
py_shift_function(PyObject *obj, PyObject *args)
{
double shift = 0.0;
if (!PyArg_ParseTuple(args, "d", &shift)) {
PyErr_SetString(PyExc_RuntimeError, "invalid parameters");
return NULL;
} else {
/* assign the shift to a dynamically allocated location: */
double *cdata = (double*)malloc(sizeof(double));
*cdata = shift;
/* wrap function and callback_data in a CObject: */
return PyCObject_FromVoidPtrAndDesc(_shift_function, cdata,
_destructor);
}
}
The value of the shift is obtained and then assigned to a dynamically
allocated memory location. Both this data pointer and the function pointer
are then wrapped in a CObject, which is returned. Additionally, a pointer
to a destructor function is given, that will free the memory we allocated
for the shift value when the CObject is destroyed. This destructor is very
simple:
C Callback functions for use with nd_image functions must all be
written according to this scheme. The next section lists the
nd_image functions that acccept a C callback function and gives
the prototype of the callback function.
The nd_image functions that support C callback functions are
described here. Obviously, the prototype of the function that is provided
to these functions must match exactly that what they expect. Therefore we
give here the prototypes of the callback functions. All these callback
functions accept a void callback_data pointer that must be wrapped in
a CObject using the Python PyCObject_FromVoidPtrAndDesc
function, which can also accept a pointer to a destructor function to free
any memory allocated for callback_data. If callback_data is not
needed, PyCObject_FromVoidPtr may be used instead. The callback
functions must return an integer error status that is equal to zero if
something went wrong, or 1 otherwise. If an error occurs, you should
normally set the python error status with an informative message before
returning, otherwise, a default error message is set by the calling
function.
The function generic_filter (see section
21.3.5) accepts a callback function with the
following prototype:
int FilterFunction(
double *buffer, int filter_size,
double *return_value, void *callback_data)
The calling function iterates
over the elements of the input and output arrays, calling the callback
function at each element. The elements within the footprint of the filter
at the current element are passed through the buffer parameter, and
the number of elements within the footprint through filter_size. The
calculated valued should be returned in the return_value argument.
The function generic_filter1d (see section
21.3.5) accepts a callback function with the
following prototype:
int FilterFunction1D(
double *input_line, int
input_length, double *output_line, int output_length, void *callback_data)
The calling function iterates over the lines of the input and output
arrays, calling the callback function at each line. The current line is
extended according to the border conditions set by the calling function,
and the result is copied into the array that is passed through the
input_line array. The length of the input line (after extension) is
passed through input_length. The callback function should apply the
1D filter and store the result in the array passed through
output_line. The length of the output line is passed through
output_length.
The function geometric_transform (see section
21.5.2) expects a function with the following
prototype:
int MapCoordinates(
int *output_coordinates,
double* input_coordinates, int output_rank, int input_rank,
void *callback_data)
The calling function iterates over the elements of the
output array, calling the callback function at each element. The
coordinates of the current output element are passed through
output_coordinates. The callback function must return the coordinates
at which the input must be interpolated in input_coordinates. The
rank of the input and output arrays are given by input_rank and
output_rank respectively.