#include "mplutils.h"
#include "_image_resample.h"
#include "_image.h"
#include "py_converters.h"
#ifndef NPY_1_7_API_VERSION
#define NPY_ARRAY_C_CONTIGUOUS NPY_C_CONTIGUOUS
#endif
/**********************************************************************
* Free functions
* */
const char* image_resample__doc__ =
"resample(input_array, output_array, matrix, interpolation=NEAREST, alpha=1.0, norm=0, radius=1)\n\n"
"Resample input_array, blending it in-place into output_array, using an\n"
"affine transformation.\n\n"
"Parameters\n"
"----------\n"
"input_array : 2-d or 3-d Numpy array of float, double or uint8\n"
" If 2-d, the image is grayscale. If 3-d, the image must be of size\n"
" 4 in the last dimension and represents RGBA data.\n\n"
"output_array : 2-d or 3-d Numpy array of float, double or uint8\n"
" The dtype and number of dimensions must match `input_array`.\n\n"
"transform : matplotlib.transforms.Transform instance\n"
" The transformation from the input array to the output\n"
" array.\n\n"
"interpolation : int, optional\n"
" The interpolation method. Must be one of the following constants\n"
" defined in this module:\n\n"
" NEAREST (default), BILINEAR, BICUBIC, SPLINE16, SPLINE36,\n"
" HANNING, HAMMING, HERMITE, KAISER, QUADRIC, CATROM, GAUSSIAN,\n"
" BESSEL, MITCHELL, SINC, LANCZOS, BLACKMAN\n\n"
"resample : bool, optional\n"
" When `True`, use a full resampling method. When `False`, only\n"
" resample when the output image is larger than the input image.\n\n"
"alpha : float, optional\n"
" The level of transparency to apply. 1.0 is completely opaque.\n"
" 0.0 is completely transparent.\n\n"
"norm : float, optional\n"
" The norm for the interpolation function. Default is 0.\n\n"
"radius: float, optional\n"
" The radius of the kernel, if method is SINC, LANCZOS or BLACKMAN.\n"
" Default is 1.\n";
static PyArrayObject *
_get_transform_mesh(PyObject *py_affine, npy_intp *dims)
{
/* TODO: Could we get away with float, rather than double, arrays here? */
/* Given a non-affine transform object, create a mesh that maps
every pixel in the output image to the input image. This is used
as a lookup table during the actual resampling. */
PyObject *py_inverse = NULL;
npy_intp out_dims[3];
out_dims[0] = dims[0] * dims[1];
out_dims[1] = 2;
py_inverse = PyObject_CallMethod(
py_affine, (char *)"inverted", (char *)"", NULL);
if (py_inverse == NULL) {
return NULL;
}
numpy::array_view<double, 2> input_mesh(out_dims);
double *p = (double *)input_mesh.data();
for (npy_intp y = 0; y < dims[0]; ++y) {
for (npy_intp x = 0; x < dims[1]; ++x) {
*p++ = (double)x;
*p++ = (double)y;
}
}
PyObject *output_mesh =
PyObject_CallMethod(
py_inverse, (char *)"transform", (char *)"O",
(char *)input_mesh.pyobj(), NULL);
Py_DECREF(py_inverse);
if (output_mesh == NULL) {
return NULL;
}
PyArrayObject *output_mesh_array =
(PyArrayObject *)PyArray_ContiguousFromAny(
output_mesh, NPY_DOUBLE, 2, 2);
Py_DECREF(output_mesh);
if (output_mesh_array == NULL) {
return NULL;
}
return output_mesh_array;
}
static PyObject *
image_resample(PyObject *self, PyObject* args, PyObject *kwargs)
{
PyObject *py_input_array = NULL;
PyObject *py_output_array = NULL;
PyObject *py_transform = NULL;
resample_params_t params;
int resample_;
PyArrayObject *input_array = NULL;
PyArrayObject *output_array = NULL;
PyArrayObject *transform_mesh_array = NULL;
params.transform_mesh = NULL;
const char *kwlist[] = {
"input_array", "output_array", "transform", "interpolation",
"resample", "alpha", "norm", "radius", NULL };
if (!PyArg_ParseTupleAndKeywords(
args, kwargs, "OOO|iiddd:resample", (char **)kwlist,
&py_input_array, &py_output_array, &py_transform,
¶ms.interpolation, &resample_, ¶ms.alpha, ¶ms.norm,
¶ms.radius)) {
return NULL;
}
if (params.interpolation < 0 || params.interpolation >= _n_interpolation) {
PyErr_Format(PyExc_ValueError, "invalid interpolation value %d",
params.interpolation);
goto error;
}
params.resample = (resample_ != 0);
input_array = (PyArrayObject *)PyArray_FromAny(
py_input_array, NULL, 2, 3, NPY_ARRAY_C_CONTIGUOUS, NULL);
if (input_array == NULL) {
goto error;
}
output_array = (PyArrayObject *)PyArray_FromAny(
py_output_array, NULL, 2, 3, NPY_ARRAY_C_CONTIGUOUS, NULL);
if (output_array == NULL) {
goto error;
}
if (py_transform == NULL || py_transform == Py_None) {
params.is_affine = true;
} else {
PyObject *py_is_affine;
int py_is_affine2;
py_is_affine = PyObject_GetAttrString(py_transform, "is_affine");
if (py_is_affine == NULL) {
goto error;
}
py_is_affine2 = PyObject_IsTrue(py_is_affine);
Py_DECREF(py_is_affine);
if (py_is_affine2 == -1) {
goto error;
} else if (py_is_affine2) {
if (!convert_trans_affine(py_transform, ¶ms.affine)) {
goto error;
}
params.is_affine = true;
} else {
transform_mesh_array = _get_transform_mesh(
py_transform, PyArray_DIMS(output_array));
if (transform_mesh_array == NULL) {
goto error;
}
params.transform_mesh = (double *)PyArray_DATA(transform_mesh_array);
params.is_affine = false;
}
}
if (PyArray_NDIM(input_array) != PyArray_NDIM(output_array)) {
PyErr_Format(
PyExc_ValueError,
"Mismatched number of dimensions. Got %d and %d.",
PyArray_NDIM(input_array), PyArray_NDIM(output_array));
goto error;
}
if (PyArray_TYPE(input_array) != PyArray_TYPE(output_array)) {
PyErr_SetString(PyExc_ValueError, "Mismatched types");
goto error;
}
if (PyArray_NDIM(input_array) == 3) {
if (PyArray_DIM(output_array, 2) != 4) {
PyErr_SetString(
PyExc_ValueError,
"Output array must be RGBA");
goto error;
}
if (PyArray_DIM(input_array, 2) == 4) {
switch(PyArray_TYPE(input_array)) {
case NPY_BYTE:
case NPY_UINT8:
Py_BEGIN_ALLOW_THREADS
resample(
(agg::rgba8 *)PyArray_DATA(input_array),
PyArray_DIM(input_array, 1),
PyArray_DIM(input_array, 0),
(agg::rgba8 *)PyArray_DATA(output_array),
PyArray_DIM(output_array, 1),
PyArray_DIM(output_array, 0),
params);
Py_END_ALLOW_THREADS
break;
case NPY_UINT16:
case NPY_INT16:
Py_BEGIN_ALLOW_THREADS
resample(
(agg::rgba16 *)PyArray_DATA(input_array),
PyArray_DIM(input_array, 1),
PyArray_DIM(input_array, 0),
(agg::rgba16 *)PyArray_DATA(output_array),
PyArray_DIM(output_array, 1),
PyArray_DIM(output_array, 0),
params);
Py_END_ALLOW_THREADS
break;
case NPY_FLOAT32:
Py_BEGIN_ALLOW_THREADS
resample(
(agg::rgba32 *)PyArray_DATA(input_array),
PyArray_DIM(input_array, 1),
PyArray_DIM(input_array, 0),
(agg::rgba32 *)PyArray_DATA(output_array),
PyArray_DIM(output_array, 1),
PyArray_DIM(output_array, 0),
params);
Py_END_ALLOW_THREADS
break;
case NPY_FLOAT64:
Py_BEGIN_ALLOW_THREADS
resample(
(agg::rgba64 *)PyArray_DATA(input_array),
PyArray_DIM(input_array, 1),
PyArray_DIM(input_array, 0),
(agg::rgba64 *)PyArray_DATA(output_array),
PyArray_DIM(output_array, 1),
PyArray_DIM(output_array, 0),
params);
Py_END_ALLOW_THREADS
break;
default:
PyErr_SetString(
PyExc_ValueError,
"3-dimensional arrays must be of dtype unsigned byte, "
"unsigned short, float32 or float64");
goto error;
}
} else {
PyErr_Format(
PyExc_ValueError,
"If 3-dimensional, array must be RGBA. Got %" NPY_INTP_FMT " planes.",
PyArray_DIM(input_array, 2));
goto error;
}
} else { // NDIM == 2
switch (PyArray_TYPE(input_array)) {
case NPY_DOUBLE:
Py_BEGIN_ALLOW_THREADS
resample(
(double *)PyArray_DATA(input_array),
PyArray_DIM(input_array, 1),
PyArray_DIM(input_array, 0),
(double *)PyArray_DATA(output_array),
PyArray_DIM(output_array, 1),
PyArray_DIM(output_array, 0),
params);
Py_END_ALLOW_THREADS
break;
case NPY_FLOAT:
Py_BEGIN_ALLOW_THREADS
resample(
(float *)PyArray_DATA(input_array),
PyArray_DIM(input_array, 1),
PyArray_DIM(input_array, 0),
(float *)PyArray_DATA(output_array),
PyArray_DIM(output_array, 1),
PyArray_DIM(output_array, 0),
params);
Py_END_ALLOW_THREADS
break;
case NPY_UINT8:
case NPY_BYTE:
Py_BEGIN_ALLOW_THREADS
resample(
(unsigned char *)PyArray_DATA(input_array),
PyArray_DIM(input_array, 1),
PyArray_DIM(input_array, 0),
(unsigned char *)PyArray_DATA(output_array),
PyArray_DIM(output_array, 1),
PyArray_DIM(output_array, 0),
params);
Py_END_ALLOW_THREADS
break;
case NPY_UINT16:
case NPY_INT16:
Py_BEGIN_ALLOW_THREADS
resample(
(unsigned short *)PyArray_DATA(input_array),
PyArray_DIM(input_array, 1),
PyArray_DIM(input_array, 0),
(unsigned short *)PyArray_DATA(output_array),
PyArray_DIM(output_array, 1),
PyArray_DIM(output_array, 0),
params);
Py_END_ALLOW_THREADS
break;
default:
PyErr_SetString(PyExc_ValueError, "Unsupported dtype");
goto error;
}
}
Py_DECREF(input_array);
Py_XDECREF(transform_mesh_array);
return (PyObject *)output_array;
error:
Py_XDECREF(input_array);
Py_XDECREF(output_array);
Py_XDECREF(transform_mesh_array);
return NULL;
}
const char *image_pcolor__doc__ =
"pcolor(x, y, data, rows, cols, bounds)\n"
"\n"
"Generate a pseudo-color image from data on a non-uniform grid using\n"
"nearest neighbour or linear interpolation.\n"
"bounds = (x_min, x_max, y_min, y_max)\n"
"interpolation = NEAREST or BILINEAR \n";
static PyObject *image_pcolor(PyObject *self, PyObject *args, PyObject *kwds)
{
numpy::array_view<const float, 1> x;
numpy::array_view<const float, 1> y;
numpy::array_view<const agg::int8u, 3> d;
npy_intp rows, cols;
float bounds[4];
int interpolation;
if (!PyArg_ParseTuple(args,
"O&O&O&nn(ffff)i:pcolor",
&x.converter,
&x,
&y.converter,
&y,
&d.converter_contiguous,
&d,
&rows,
&cols,
&bounds[0],
&bounds[1],
&bounds[2],
&bounds[3],
&interpolation)) {
return NULL;
}
npy_intp dim[3] = {rows, cols, 4};
numpy::array_view<const agg::int8u, 3> output(dim);
CALL_CPP("pcolor", (pcolor(x, y, d, rows, cols, bounds, interpolation, output)));
return output.pyobj();
}
const char *image_pcolor2__doc__ =
"pcolor2(x, y, data, rows, cols, bounds, bg)\n"
"\n"
"Generate a pseudo-color image from data on a non-uniform grid\n"
"specified by its cell boundaries.\n"
"bounds = (x_left, x_right, y_bot, y_top)\n"
"bg = ndarray of 4 uint8 representing background rgba\n";
static PyObject *image_pcolor2(PyObject *self, PyObject *args, PyObject *kwds)
{
numpy::array_view<const double, 1> x;
numpy::array_view<const double, 1> y;
numpy::array_view<const agg::int8u, 3> d;
npy_intp rows, cols;
float bounds[4];
numpy::array_view<const agg::int8u, 1> bg;
if (!PyArg_ParseTuple(args,
"O&O&O&nn(ffff)O&:pcolor2",
&x.converter_contiguous,
&x,
&y.converter_contiguous,
&y,
&d.converter_contiguous,
&d,
&rows,
&cols,
&bounds[0],
&bounds[1],
&bounds[2],
&bounds[3],
&bg.converter,
&bg)) {
return NULL;
}
npy_intp dim[3] = {rows, cols, 4};
numpy::array_view<const agg::int8u, 3> output(dim);
CALL_CPP("pcolor2", (pcolor2(x, y, d, rows, cols, bounds, bg, output)));
return output.pyobj();
}
static PyMethodDef module_functions[] = {
{"resample", (PyCFunction)image_resample, METH_VARARGS|METH_KEYWORDS, image_resample__doc__},
{"pcolor", (PyCFunction)image_pcolor, METH_VARARGS, image_pcolor__doc__},
{"pcolor2", (PyCFunction)image_pcolor2, METH_VARARGS, image_pcolor2__doc__},
{NULL}
};
extern "C" {
#if PY3K
static struct PyModuleDef moduledef = {
PyModuleDef_HEAD_INIT,
"_image",
NULL,
0,
module_functions,
NULL,
NULL,
NULL,
NULL
};
#define INITERROR return NULL
PyMODINIT_FUNC PyInit__image(void)
#else
#define INITERROR return
PyMODINIT_FUNC init_image(void)
#endif
{
PyObject *m;
#if PY3K
m = PyModule_Create(&moduledef);
#else
m = Py_InitModule3("_image", module_functions, NULL);
#endif
if (m == NULL) {
INITERROR;
}
if (PyModule_AddIntConstant(m, "NEAREST", NEAREST) ||
PyModule_AddIntConstant(m, "BILINEAR", BILINEAR) ||
PyModule_AddIntConstant(m, "BICUBIC", BICUBIC) ||
PyModule_AddIntConstant(m, "SPLINE16", SPLINE16) ||
PyModule_AddIntConstant(m, "SPLINE36", SPLINE36) ||
PyModule_AddIntConstant(m, "HANNING", HANNING) ||
PyModule_AddIntConstant(m, "HAMMING", HAMMING) ||
PyModule_AddIntConstant(m, "HERMITE", HERMITE) ||
PyModule_AddIntConstant(m, "KAISER", KAISER) ||
PyModule_AddIntConstant(m, "QUADRIC", QUADRIC) ||
PyModule_AddIntConstant(m, "CATROM", CATROM) ||
PyModule_AddIntConstant(m, "GAUSSIAN", GAUSSIAN) ||
PyModule_AddIntConstant(m, "BESSEL", BESSEL) ||
PyModule_AddIntConstant(m, "MITCHELL", MITCHELL) ||
PyModule_AddIntConstant(m, "SINC", SINC) ||
PyModule_AddIntConstant(m, "LANCZOS", LANCZOS) ||
PyModule_AddIntConstant(m, "BLACKMAN", BLACKMAN) ||
PyModule_AddIntConstant(m, "_n_interpolation", _n_interpolation)) {
INITERROR;
}
import_array();
#if PY3K
return m;
#endif
}
} // extern "C"