Skip to content

HTTPS clone URL

Subversion checkout URL

You can clone with
or
.
Download ZIP
Fetching contributors…

Cannot retrieve contributors at this time

1996 lines (1728 sloc) 57.132 kB
/* -*- mode: c++; c-basic-offset: 4 -*- */
/* Python API mandates Python.h is included *first* */
#include "Python.h"
#include <string>
#include <iostream>
#include <fstream>
#include <cmath>
#include <cstdio>
#include "numpy/arrayobject.h"
#include "agg_color_rgba.h"
#include "agg_conv_transform.h"
#include "agg_image_accessors.h"
#include "agg_path_storage.h"
#include "agg_pixfmt_rgb.h"
#include "agg_pixfmt_rgba.h"
#include "agg_rasterizer_scanline_aa.h"
#include "agg_renderer_scanline.h"
#include "agg_rendering_buffer.h"
#include "agg_scanline_bin.h"
#include "agg_scanline_bin.h"
#include "agg_scanline_u.h"
#include "agg_span_allocator.h"
#include "agg_span_image_filter_rgb.h"
#include "agg_span_image_filter_rgba.h"
#include "agg_span_interpolator_linear.h"
#include "agg_rasterizer_sl_clip.h"
#include "util/agg_color_conv_rgb8.h"
#include "_image.h"
#include "mplutils.h"
typedef agg::pixfmt_rgba32 pixfmt;
typedef agg::pixfmt_rgba32_pre pixfmt_pre;
typedef agg::renderer_base<pixfmt> renderer_base;
typedef agg::span_interpolator_linear<> interpolator_type;
typedef agg::rasterizer_scanline_aa<agg::rasterizer_sl_clip_dbl> rasterizer;
Image::Image() :
bufferIn(NULL), rbufIn(NULL), colsIn(0), rowsIn(0),
bufferOut(NULL), rbufOut(NULL), colsOut(0), rowsOut(0), BPP(4),
interpolation(BILINEAR), aspect(ASPECT_FREE), bg(1, 1, 1, 0), resample(true)
{
_VERBOSE("Image::Image");
}
Image::~Image()
{
_VERBOSE("Image::~Image");
delete [] bufferIn;
bufferIn = NULL;
delete rbufIn;
rbufIn = NULL;
delete rbufOut;
rbufOut = NULL;
delete [] bufferOut;
bufferOut = NULL;
}
int
Image::setattr(const char * name, const Py::Object & value)
{
_VERBOSE("Image::setattr");
__dict__[name] = value;
return 0;
}
Py::Object
Image::getattr(const char * name)
{
_VERBOSE("Image::getattro");
if (__dict__.hasKey(name)) return __dict__[name];
else return getattr_default(name);
}
char Image::apply_rotation__doc__[] =
"apply_rotation(angle)\n"
"\n"
"Apply the rotation (degrees) to image"
;
Py::Object
Image::apply_rotation(const Py::Tuple& args)
{
_VERBOSE("Image::apply_rotation");
args.verify_length(1);
double r = Py::Float(args[0]);
agg::trans_affine M = agg::trans_affine_rotation(r * agg::pi / 180.0);
srcMatrix *= M;
imageMatrix *= M;
return Py::Object();
}
char Image::flipud_out__doc__[] =
"flipud()\n"
"\n"
"Flip the output image upside down"
;
char Image::flipud_in__doc__[] =
"flipud()\n"
"\n"
"Flip the input image upside down"
;
Py::Object
Image::flipud_in(const Py::Tuple& args)
{
_VERBOSE("Image::flipud_in");
args.verify_length(0);
int stride = rbufIn->stride();
rbufIn->attach(bufferIn, colsIn, rowsIn, -stride);
return Py::Object();
}
char Image::set_bg__doc__[] =
"set_bg(r,g,b,a)\n"
"\n"
"Set the background color"
;
Py::Object
Image::set_bg(const Py::Tuple& args)
{
_VERBOSE("Image::set_bg");
args.verify_length(4);
bg.r = Py::Float(args[0]);
bg.g = Py::Float(args[1]);
bg.b = Py::Float(args[2]);
bg.a = Py::Float(args[3]);
return Py::Object();
}
char Image::apply_scaling__doc__[] =
"apply_scaling(sx, sy)\n"
"\n"
"Apply the scale factors sx, sy to the transform matrix"
;
Py::Object
Image::apply_scaling(const Py::Tuple& args)
{
_VERBOSE("Image::apply_scaling");
args.verify_length(2);
double sx = Py::Float(args[0]);
double sy = Py::Float(args[1]);
//printf("applying scaling %1.2f, %1.2f\n", sx, sy);
agg::trans_affine M = agg::trans_affine_scaling(sx, sy);
srcMatrix *= M;
imageMatrix *= M;
return Py::Object();
}
char Image::apply_translation__doc__[] =
"apply_translation(tx, ty)\n"
"\n"
"Apply the translation tx, ty to the transform matrix"
;
Py::Object
Image::apply_translation(const Py::Tuple& args)
{
_VERBOSE("Image::apply_translation");
args.verify_length(2);
double tx = Py::Float(args[0]);
double ty = Py::Float(args[1]);
//printf("applying translation %1.2f, %1.2f\n", tx, ty);
agg::trans_affine M = agg::trans_affine_translation(tx, ty);
srcMatrix *= M;
imageMatrix *= M;
return Py::Object();
}
char Image::as_rgba_str__doc__[] =
"numrows, numcols, s = as_rgba_str()"
"\n"
"Call this function after resize to get the data as string\n"
"The string is a numrows by numcols x 4 (RGBA) unsigned char buffer\n"
;
Py::Object
Image::as_rgba_str(const Py::Tuple& args, const Py::Dict& kwargs)
{
_VERBOSE("Image::as_rgba_str");
args.verify_length(0);
std::pair<agg::int8u*, bool> bufpair = _get_output_buffer();
#if PY3K
Py::Object ret = Py::asObject(Py_BuildValue("lly#", rowsOut, colsOut,
bufpair.first, colsOut * rowsOut * 4));
#else
Py::Object ret = Py::asObject(Py_BuildValue("lls#", rowsOut, colsOut,
bufpair.first, colsOut * rowsOut * 4));
#endif
if (bufpair.second) delete [] bufpair.first;
return ret;
}
char Image::color_conv__doc__[] =
"numrows, numcols, buffer = color_conv(format)"
"\n"
"format 0(BGRA) or 1(ARGB)\n"
"Convert image to format and return in a writable buffer\n"
;
Py::Object
Image::color_conv(const Py::Tuple& args)
{
_VERBOSE("Image::color_conv");
args.verify_length(1);
int format = Py::Int(args[0]);
PyObject* py_buffer = NULL;
int row_len = colsOut * 4;
#if PY3K
unsigned char* buf = (unsigned char *)malloc(row_len * rowsOut);
if (buf == NULL)
throw Py::MemoryError("Image::color_conv could not allocate memory");
#else
py_buffer = PyBuffer_New(row_len * rowsOut);
if (py_buffer == NULL)
throw Py::MemoryError("Image::color_conv could not allocate memory");
void* buf;
Py_ssize_t buffer_len;
int ret = PyObject_AsWriteBuffer(py_buffer, &buf, &buffer_len);
if (ret != 0)
{
Py_XDECREF(py_buffer);
throw Py::MemoryError("Image::color_conv could not allocate memory");
}
#endif
agg::rendering_buffer rtmp;
rtmp.attach(reinterpret_cast<unsigned char*>(buf), colsOut, rowsOut,
row_len);
switch (format)
{
case 0:
agg::color_conv(&rtmp, rbufOut, agg::color_conv_rgba32_to_bgra32());
break;
case 1:
agg::color_conv(&rtmp, rbufOut, agg::color_conv_rgba32_to_argb32());
break;
default:
Py_XDECREF(py_buffer);
throw Py::ValueError("Image::color_conv unknown format");
}
#if PY3K
py_buffer = PyByteArray_FromStringAndSize((char *)buf, row_len * rowsOut);
if (py_buffer == NULL) {
free(buf);
}
#endif
PyObject* o = Py_BuildValue("llN", rowsOut, colsOut, py_buffer);
return Py::asObject(o);
}
char Image::buffer_rgba__doc__[] =
"buffer = buffer_rgba()"
"\n"
"Return the image buffer as rgba32\n"
;
Py::Object
Image::buffer_rgba(const Py::Tuple& args)
{
//"Return the image object as rgba";
_VERBOSE("RendererAgg::buffer_rgba");
args.verify_length(0);
int row_len = colsOut * 4;
PyObject* o = Py_BuildValue("lls#", rowsOut, colsOut,
rbufOut, row_len * rowsOut);
return Py::asObject(o);
}
char Image::reset_matrix__doc__[] =
"reset_matrix()"
"\n"
"Reset the transformation matrix"
;
Py::Object
Image::reset_matrix(const Py::Tuple& args)
{
_VERBOSE("Image::reset_matrix");
args.verify_length(0);
srcMatrix.reset();
imageMatrix.reset();
return Py::Object();
}
char Image::get_matrix__doc__[] =
"(m11,m21,m12,m22,m13,m23) = get_matrix()\n"
"\n"
"Get the affine transformation matrix\n"
" /m11,m12,m13\\\n"
" /m21,m22,m23|\n"
" \\ 0 , 0 , 1 /"
;
Py::Object
Image::get_matrix(const Py::Tuple& args)
{
_VERBOSE("Image::get_matrix");
args.verify_length(0);
double m[6];
srcMatrix.store_to(m);
Py::Tuple ret(6);
for (int i = 0;i < 6;i++)
{
ret[i] = Py::Float(m[i]);
}
return ret;
}
char Image::resize__doc__[] =
"resize(width, height, norm=1, radius=4.0)\n"
"\n"
"Resize the image to width, height using interpolation\n"
"norm and radius are optional args for some of the filters and must be\n"
"passed as kwargs\n"
;
Py::Object
Image::resize(const Py::Tuple& args, const Py::Dict& kwargs)
{
_VERBOSE("Image::resize");
args.verify_length(2);
int norm = 1;
if (kwargs.hasKey("norm"))
{
norm = Py::Int(kwargs["norm"]);
}
double radius = 4.0;
if (kwargs.hasKey("radius"))
{
radius = Py::Float(kwargs["radius"]);
}
if (bufferIn == NULL)
{
throw Py::RuntimeError("You must first load the image");
}
int numcols = Py::Int(args[0]);
int numrows = Py::Int(args[1]);
colsOut = numcols;
rowsOut = numrows;
size_t NUMBYTES(numrows * numcols * BPP);
delete [] bufferOut;
bufferOut = new agg::int8u[NUMBYTES];
if (bufferOut == NULL) //todo: also handle allocation throw
{
throw Py::MemoryError("Image::resize could not allocate memory");
}
delete rbufOut;
rbufOut = new agg::rendering_buffer;
rbufOut->attach(bufferOut, numcols, numrows, numcols * BPP);
// init the output rendering/rasterizing stuff
pixfmt pixf(*rbufOut);
renderer_base rb(pixf);
rb.clear(bg);
rasterizer ras;
agg::scanline_u8 sl;
ras.clip_box(0, 0, numcols, numrows);
//srcMatrix *= resizingMatrix;
//imageMatrix *= resizingMatrix;
imageMatrix.invert();
interpolator_type interpolator(imageMatrix);
typedef agg::span_allocator<agg::rgba8> span_alloc_type;
span_alloc_type sa;
agg::rgba8 background(agg::rgba8(int(255*bg.r),
int(255*bg.g),
int(255*bg.b),
int(255*bg.a)));
// the image path
agg::path_storage path;
agg::rendering_buffer rbufPad;
double x0, y0, x1, y1;
x0 = 0.0;
x1 = colsIn;
y0 = 0.0;
y1 = rowsIn;
path.move_to(x0, y0);
path.line_to(x1, y0);
path.line_to(x1, y1);
path.line_to(x0, y1);
path.close_polygon();
agg::conv_transform<agg::path_storage> imageBox(path, srcMatrix);
ras.add_path(imageBox);
typedef agg::wrap_mode_reflect reflect_type;
typedef agg::image_accessor_wrap<pixfmt_pre, reflect_type, reflect_type> img_accessor_type;
pixfmt_pre pixfmtin(*rbufIn);
img_accessor_type ia(pixfmtin);
switch (interpolation)
{
case NEAREST:
{
if (colsIn == numcols && rowsIn == numrows) {
memcpy(bufferOut, bufferIn, colsIn * rowsIn * 4);
} else {
typedef agg::span_image_filter_rgba_nn<img_accessor_type, interpolator_type> span_gen_type;
typedef agg::renderer_scanline_aa<renderer_base, span_alloc_type, span_gen_type> renderer_type;
span_gen_type sg(ia, interpolator);
renderer_type ri(rb, sa, sg);
agg::render_scanlines(ras, sl, ri);
}
}
break;
case HANNING:
case HAMMING:
case HERMITE:
{
agg::image_filter_lut filter;
switch (interpolation)
{
case HANNING:
filter.calculate(agg::image_filter_hanning(), norm);
break;
case HAMMING:
filter.calculate(agg::image_filter_hamming(), norm);
break;
case HERMITE:
filter.calculate(agg::image_filter_hermite(), norm);
break;
}
if (resample)
{
typedef agg::span_image_resample_rgba_affine<img_accessor_type> span_gen_type;
typedef agg::renderer_scanline_aa<renderer_base, span_alloc_type, span_gen_type> renderer_type;
span_gen_type sg(ia, interpolator, filter);
renderer_type ri(rb, sa, sg);
agg::render_scanlines(ras, sl, ri);
}
else
{
typedef agg::span_image_filter_rgba_2x2<img_accessor_type, interpolator_type> span_gen_type;
typedef agg::renderer_scanline_aa<renderer_base, span_alloc_type, span_gen_type> renderer_type;
span_gen_type sg(ia, interpolator, filter);
renderer_type ri(rb, sa, sg);
agg::render_scanlines(ras, sl, ri);
}
}
break;
case BILINEAR:
case BICUBIC:
case SPLINE16:
case SPLINE36:
case KAISER:
case QUADRIC:
case CATROM:
case GAUSSIAN:
case BESSEL:
case MITCHELL:
case SINC:
case LANCZOS:
case BLACKMAN:
{
agg::image_filter_lut filter;
switch (interpolation)
{
case BILINEAR:
filter.calculate(agg::image_filter_bilinear(), norm);
break;
case BICUBIC:
filter.calculate(agg::image_filter_bicubic(), norm);
break;
case SPLINE16:
filter.calculate(agg::image_filter_spline16(), norm);
break;
case SPLINE36:
filter.calculate(agg::image_filter_spline36(), norm);
break;
case KAISER:
filter.calculate(agg::image_filter_kaiser(), norm);
break;
case QUADRIC:
filter.calculate(agg::image_filter_quadric(), norm);
break;
case CATROM:
filter.calculate(agg::image_filter_catrom(), norm);
break;
case GAUSSIAN:
filter.calculate(agg::image_filter_gaussian(), norm);
break;
case BESSEL:
filter.calculate(agg::image_filter_bessel(), norm);
break;
case MITCHELL:
filter.calculate(agg::image_filter_mitchell(), norm);
break;
case SINC:
filter.calculate(agg::image_filter_sinc(radius), norm);
break;
case LANCZOS:
filter.calculate(agg::image_filter_lanczos(radius), norm);
break;
case BLACKMAN:
filter.calculate(agg::image_filter_blackman(radius), norm);
break;
}
if (resample)
{
typedef agg::span_image_resample_rgba_affine<img_accessor_type> span_gen_type;
typedef agg::renderer_scanline_aa<renderer_base, span_alloc_type, span_gen_type> renderer_type;
span_gen_type sg(ia, interpolator, filter);
renderer_type ri(rb, sa, sg);
agg::render_scanlines(ras, sl, ri);
}
else
{
typedef agg::span_image_filter_rgba<img_accessor_type, interpolator_type> span_gen_type;
typedef agg::renderer_scanline_aa<renderer_base, span_alloc_type, span_gen_type> renderer_type;
span_gen_type sg(ia, interpolator, filter);
renderer_type ri(rb, sa, sg);
agg::render_scanlines(ras, sl, ri);
}
}
break;
}
return Py::Object();
}
char Image::get_interpolation__doc__[] =
"get_interpolation()\n"
"\n"
"Get the interpolation scheme to one of the module constants, "
"one of image.NEAREST, image.BILINEAR, etc..."
;
Py::Object
Image::get_interpolation(const Py::Tuple& args)
{
_VERBOSE("Image::get_interpolation");
args.verify_length(0);
return Py::Int((int)interpolation);
}
char Image::get_aspect__doc__[] =
"get_aspect()\n"
"\n"
"Get the aspect constraint constants"
;
Py::Object
Image::get_aspect(const Py::Tuple& args)
{
_VERBOSE("Image::get_aspect");
args.verify_length(0);
return Py::Int((int)aspect);
}
char Image::get_size__doc__[] =
"numrows, numcols = get_size()\n"
"\n"
"Get the number or rows and columns of the input image"
;
Py::Object
Image::get_size(const Py::Tuple& args)
{
_VERBOSE("Image::get_size");
args.verify_length(0);
Py::Tuple ret(2);
ret[0] = Py::Int((long)rowsIn);
ret[1] = Py::Int((long)colsIn);
return ret;
}
char Image::get_resample__doc__[] =
"get_resample()\n"
"\n"
"Get the resample flag."
;
Py::Object
Image::get_resample(const Py::Tuple& args)
{
_VERBOSE("Image::get_resample");
args.verify_length(0);
return Py::Int((int)resample);
}
char Image::get_size_out__doc__[] =
"numrows, numcols = get_size()\n"
"\n"
"Get the number or rows and columns of the output image"
;
Py::Object
Image::get_size_out(const Py::Tuple& args)
{
_VERBOSE("Image::get_size_out");
args.verify_length(0);
Py::Tuple ret(2);
ret[0] = Py::Int((long)rowsOut);
ret[1] = Py::Int((long)colsOut);
return ret;
}
//get the output buffer, flipped if necessary. The second element of
//the pair is a bool that indicates whether you need to free the
//memory
std::pair<agg::int8u*, bool>
Image::_get_output_buffer()
{
_VERBOSE("Image::_get_output_buffer");
std::pair<agg::int8u*, bool> ret;
bool flipy = rbufOut->stride() < 0;
if (flipy)
{
agg::int8u* buffer = new agg::int8u[rowsOut*colsOut*4];
agg::rendering_buffer rb;
rb.attach(buffer, colsOut, rowsOut, colsOut*4);
rb.copy_from(*rbufOut);
ret.first = buffer;
ret.second = true;
}
else
{
ret.first = bufferOut;
ret.second = false;
}
return ret;
}
char Image::set_interpolation__doc__[] =
"set_interpolation(scheme)\n"
"\n"
"Set the interpolation scheme to one of the module constants, "
"eg, image.NEAREST, image.BILINEAR, etc..."
;
Py::Object
Image::set_interpolation(const Py::Tuple& args)
{
_VERBOSE("Image::set_interpolation");
args.verify_length(1);
size_t method = (long)Py::Int(args[0]);
interpolation = (unsigned)method;
return Py::Object();
}
char Image::set_resample__doc__[] =
"set_resample(boolean)\n"
"\n"
"Set the resample flag."
;
Py::Object
Image::set_resample(const Py::Tuple& args)
{
_VERBOSE("Image::set_resample");
args.verify_length(1);
int flag = Py::Int(args[0]);
resample = (bool)flag;
return Py::Object();
}
char Image::set_aspect__doc__[] =
"set_aspect(scheme)\n"
"\n"
"Set the aspect ration to one of the image module constant."
"eg, one of image.ASPECT_PRESERVE, image.ASPECT_FREE"
;
Py::Object
Image::set_aspect(const Py::Tuple& args)
{
_VERBOSE("Image::set_aspect");
args.verify_length(1);
size_t method = (long)Py::Int(args[0]);
aspect = (unsigned)method;
return Py::Object();
}
void
Image::init_type()
{
_VERBOSE("Image::init_type");
behaviors().name("Image");
behaviors().doc("Image");
behaviors().supportGetattr();
behaviors().supportSetattr();
add_varargs_method("apply_rotation", &Image::apply_rotation, Image::apply_rotation__doc__);
add_varargs_method("apply_scaling", &Image::apply_scaling, Image::apply_scaling__doc__);
add_varargs_method("apply_translation", &Image::apply_translation, Image::apply_translation__doc__);
add_keyword_method("as_rgba_str", &Image::as_rgba_str, Image::as_rgba_str__doc__);
add_varargs_method("color_conv", &Image::color_conv, Image::color_conv__doc__);
add_varargs_method("buffer_rgba", &Image::buffer_rgba, Image::buffer_rgba__doc__);
add_varargs_method("get_aspect", &Image::get_aspect, Image::get_aspect__doc__);
add_varargs_method("get_interpolation", &Image::get_interpolation, Image::get_interpolation__doc__);
add_varargs_method("get_resample", &Image::get_resample, Image::get_resample__doc__);
add_varargs_method("get_size", &Image::get_size, Image::get_size__doc__);
add_varargs_method("get_size_out", &Image::get_size_out, Image::get_size_out__doc__);
add_varargs_method("reset_matrix", &Image::reset_matrix, Image::reset_matrix__doc__);
add_varargs_method("get_matrix", &Image::get_matrix, Image::get_matrix__doc__);
add_keyword_method("resize", &Image::resize, Image::resize__doc__);
add_varargs_method("set_interpolation", &Image::set_interpolation, Image::set_interpolation__doc__);
add_varargs_method("set_resample", &Image::set_resample, Image::set_resample__doc__);
add_varargs_method("set_aspect", &Image::set_aspect, Image::set_aspect__doc__);
add_varargs_method("set_bg", &Image::set_bg, Image::set_bg__doc__);
add_varargs_method("flipud_out", &Image::flipud_out, Image::flipud_out__doc__);
add_varargs_method("flipud_in", &Image::flipud_in, Image::flipud_in__doc__);
}
char _image_module_from_images__doc__[] =
"from_images(numrows, numcols, seq)\n"
"\n"
"return an image instance with numrows, numcols from a seq of image\n"
"instances using alpha blending. seq is a list of (Image, ox, oy)"
;
Py::Object
_image_module::from_images(const Py::Tuple& args)
{
_VERBOSE("_image_module::from_images");
args.verify_length(3);
size_t numrows = (long)Py::Int(args[0]);
size_t numcols = (long)Py::Int(args[1]);
if (numrows >= 32768 || numcols >= 32768)
{
throw Py::RuntimeError("numrows and numcols must both be less than 32768");
}
Py::SeqBase<Py::Object> tups = args[2];
size_t N = tups.length();
if (N == 0)
{
throw Py::RuntimeError("Empty list of images");
}
Py::Tuple tup;
size_t ox(0), oy(0), thisx(0), thisy(0);
//copy image 0 output buffer into return images output buffer
Image* imo = new Image;
imo->rowsOut = numrows;
imo->colsOut = numcols;
size_t NUMBYTES(numrows * numcols * imo->BPP);
imo->bufferOut = new agg::int8u[NUMBYTES];
if (imo->bufferOut == NULL) //todo: also handle allocation throw
{
throw Py::MemoryError("_image_module::from_images could not allocate memory");
}
delete imo->rbufOut;
imo->rbufOut = new agg::rendering_buffer;
imo->rbufOut->attach(imo->bufferOut, imo->colsOut, imo->rowsOut, imo->colsOut * imo->BPP);
pixfmt pixf(*imo->rbufOut);
renderer_base rb(pixf);
rb.clear(agg::rgba(1, 1, 1, 1));
for (size_t imnum = 0; imnum < N; imnum++)
{
tup = Py::Tuple(tups[imnum]);
Image* thisim = static_cast<Image*>(tup[0].ptr());
ox = (long)Py::Int(tup[1]);
oy = (long)Py::Int(tup[2]);
bool isflip = (thisim->rbufOut->stride()) < 0;
//std::cout << "from images " << isflip << "; stride=" << thisim->rbufOut->stride() << std::endl;
size_t ind = 0;
for (size_t j = 0; j < thisim->rowsOut; j++)
{
for (size_t i = 0; i < thisim->colsOut; i++)
{
thisx = i + ox;
if (isflip)
{
thisy = thisim->rowsOut - j + oy;
}
else
{
thisy = j + oy;
}
if (thisx >= numcols || thisy >= numrows)
{
ind += 4;
continue;
}
pixfmt::color_type p;
p.r = *(thisim->bufferOut + ind++);
p.g = *(thisim->bufferOut + ind++);
p.b = *(thisim->bufferOut + ind++);
p.a = *(thisim->bufferOut + ind++);
pixf.blend_pixel(thisx, thisy, p, 255);
}
}
}
return Py::asObject(imo);
}
char _image_module_fromarray__doc__[] =
"fromarray(A, isoutput)\n"
"\n"
"Load the image from a numpy array\n"
"By default this function fills the input buffer, which can subsequently\n"
"be resampled using resize. If isoutput=1, fill the output buffer.\n"
"This is used to support raw pixel images w/o resampling"
;
Py::Object
_image_module::fromarray(const Py::Tuple& args)
{
_VERBOSE("_image_module::fromarray");
args.verify_length(2);
Py::Object x = args[0];
int isoutput = Py::Int(args[1]);
PyArrayObject *A = (PyArrayObject *) PyArray_FromObject(x.ptr(), PyArray_DOUBLE, 2, 3);
if (A == NULL)
{
throw Py::ValueError("Array must be rank 2 or 3 of doubles");
}
Py::Object A_obj((PyObject *)A, true);
Image* imo = new Image;
imo->rowsIn = A->dimensions[0];
imo->colsIn = A->dimensions[1];
size_t NUMBYTES(imo->colsIn * imo->rowsIn * imo->BPP);
agg::int8u *buffer = new agg::int8u[NUMBYTES];
if (buffer == NULL) //todo: also handle allocation throw
{
throw Py::MemoryError("_image_module::fromarray could not allocate memory");
}
if (isoutput)
{
// make the output buffer point to the input buffer
imo->rowsOut = imo->rowsIn;
imo->colsOut = imo->colsIn;
imo->rbufOut = new agg::rendering_buffer;
imo->bufferOut = buffer;
imo->rbufOut->attach(imo->bufferOut, imo->colsOut, imo->rowsOut, imo->colsOut * imo->BPP);
}
else
{
imo->bufferIn = buffer;
imo->rbufIn = new agg::rendering_buffer;
imo->rbufIn->attach(buffer, imo->colsIn, imo->rowsIn, imo->colsIn*imo->BPP);
}
if (A->nd == 2) //assume luminance for now;
{
agg::int8u gray;
for (size_t rownum = 0; rownum < imo->rowsIn; rownum++)
{
for (size_t colnum = 0; colnum < imo->colsIn; colnum++)
{
double val = *(double *)(A->data + rownum * A->strides[0] + colnum * A->strides[1]);
gray = int(255 * val);
*buffer++ = gray; // red
*buffer++ = gray; // green
*buffer++ = gray; // blue
*buffer++ = 255; // alpha
}
}
}
else if (A->nd == 3) // assume RGB
{
if (A->dimensions[2] != 3 && A->dimensions[2] != 4)
{
throw Py::ValueError(Printf("3rd dimension must be length 3 (RGB) or 4 (RGBA); found %d", A->dimensions[2]).str());
}
int rgba = A->dimensions[2] == 4;
double r, g, b, alpha;
size_t offset = 0;
for (size_t rownum = 0; rownum < imo->rowsIn; rownum++)
{
for (size_t colnum = 0; colnum < imo->colsIn; colnum++)
{
offset = rownum * A->strides[0] + colnum * A->strides[1];
r = *(double *)(A->data + offset);
g = *(double *)(A->data + offset + A->strides[2]);
b = *(double *)(A->data + offset + 2 * A->strides[2]);
if (rgba)
{
alpha = *(double *)(A->data + offset + 3 * A->strides[2]);
}
else
{
alpha = 1.0;
}
*buffer++ = int(255 * r); // red
*buffer++ = int(255 * g); // green
*buffer++ = int(255 * b); // blue
*buffer++ = int(255 * alpha); // alpha
}
}
}
else // error
{
throw Py::ValueError("Illegal array rank; must be rank; must 2 or 3");
}
buffer -= NUMBYTES;
return Py::asObject(imo);
}
char _image_module_fromarray2__doc__[] =
"fromarray2(A, isoutput)\n"
"\n"
"Load the image from a numpy array\n"
"By default this function fills the input buffer, which can subsequently\n"
"be resampled using resize. If isoutput=1, fill the output buffer.\n"
"This is used to support raw pixel images w/o resampling"
;
Py::Object
_image_module::fromarray2(const Py::Tuple& args)
{
_VERBOSE("_image_module::fromarray2");
args.verify_length(2);
Py::Object x = args[0];
int isoutput = Py::Int(args[1]);
PyArrayObject *A = (PyArrayObject *) PyArray_ContiguousFromObject(x.ptr(), PyArray_DOUBLE, 2, 3);
if (A == NULL)
{
throw Py::ValueError("Array must be rank 2 or 3 of doubles");
}
Py::Object A_obj((PyObject*)A, true);
Image* imo = new Image;
imo->rowsIn = A->dimensions[0];
imo->colsIn = A->dimensions[1];
size_t NUMBYTES(imo->colsIn * imo->rowsIn * imo->BPP);
agg::int8u *buffer = new agg::int8u[NUMBYTES];
if (buffer == NULL) //todo: also handle allocation throw
{
throw Py::MemoryError("_image_module::fromarray could not allocate memory");
}
if (isoutput)
{
// make the output buffer point to the input buffer
imo->rowsOut = imo->rowsIn;
imo->colsOut = imo->colsIn;
imo->rbufOut = new agg::rendering_buffer;
imo->bufferOut = buffer;
imo->rbufOut->attach(imo->bufferOut, imo->colsOut, imo->rowsOut, imo->colsOut * imo->BPP);
}
else
{
imo->bufferIn = buffer;
imo->rbufIn = new agg::rendering_buffer;
imo->rbufIn->attach(buffer, imo->colsIn, imo->rowsIn, imo->colsIn*imo->BPP);
}
if (A->nd == 2) //assume luminance for now;
{
agg::int8u gray;
const size_t N = imo->rowsIn * imo->colsIn;
size_t i = 0;
while (i++ < N)
{
double val = *(double *)(A->data++);
gray = int(255 * val);
*buffer++ = gray; // red
*buffer++ = gray; // green
*buffer++ = gray; // blue
*buffer++ = 255; // alpha
}
}
else if (A->nd == 3) // assume RGB
{
if (A->dimensions[2] != 3 && A->dimensions[2] != 4)
{
throw Py::ValueError(Printf("3rd dimension must be length 3 (RGB) or 4 (RGBA); found %d", A->dimensions[2]).str());
}
int rgba = A->dimensions[2] == 4;
double r, g, b, alpha;
const size_t N = imo->rowsIn * imo->colsIn;
for (size_t i = 0; i < N; ++i)
{
r = *(double *)(A->data++);
g = *(double *)(A->data++);
b = *(double *)(A->data++);
if (rgba)
alpha = *(double *)(A->data++);
else
alpha = 1.0;
*buffer++ = int(255 * r); // red
*buffer++ = int(255 * g); // green
*buffer++ = int(255 * b); // blue
*buffer++ = int(255 * alpha); // alpha
}
}
else // error
{
throw Py::ValueError("Illegal array rank; must be rank; must 2 or 3");
}
buffer -= NUMBYTES;
return Py::asObject(imo);
}
char _image_module_frombyte__doc__[] =
"frombyte(A, isoutput)\n"
"\n"
"Load the image from a byte array.\n"
"By default this function fills the input buffer, which can subsequently\n"
"be resampled using resize. If isoutput=1, fill the output buffer.\n"
"This is used to support raw pixel images w/o resampling."
;
Py::Object
_image_module::frombyte(const Py::Tuple& args)
{
_VERBOSE("_image_module::frombyte");
args.verify_length(2);
Py::Object x = args[0];
int isoutput = Py::Int(args[1]);
PyArrayObject *A = (PyArrayObject *) PyArray_FromObject(x.ptr(), PyArray_UBYTE, 3, 3);
if (A == NULL)
{
throw Py::ValueError("Array must have 3 dimensions");
}
Py::Object A_obj((PyObject*)A, true);
if (A->dimensions[2] < 3 || A->dimensions[2] > 4)
{
throw Py::ValueError("Array dimension 3 must have size 3 or 4");
}
Image* imo = new Image;
imo->rowsIn = A->dimensions[0];
imo->colsIn = A->dimensions[1];
agg::int8u *arrbuf;
agg::int8u *buffer;
agg::int8u *dstbuf;
arrbuf = reinterpret_cast<agg::int8u *>(A->data);
size_t NUMBYTES(imo->colsIn * imo->rowsIn * imo->BPP);
buffer = dstbuf = new agg::int8u[NUMBYTES];
if (buffer == NULL) //todo: also handle allocation throw
{
throw Py::MemoryError("_image_module::frombyte could not allocate memory");
}
if PyArray_ISCONTIGUOUS(A)
{
if (A->dimensions[2] == 4)
{
memmove(dstbuf, arrbuf, imo->rowsIn * imo->colsIn * 4);
}
else
{
size_t i = imo->rowsIn * imo->colsIn;
while (i--)
{
*dstbuf++ = *arrbuf++;
*dstbuf++ = *arrbuf++;
*dstbuf++ = *arrbuf++;
*dstbuf++ = 255;
}
}
}
else if ((A->strides[1] == 4) && (A->strides[2] == 1))
{
const size_t N = imo->colsIn * 4;
const size_t stride = A->strides[0];
for (size_t rownum = 0; rownum < imo->rowsIn; rownum++)
{
memmove(dstbuf, arrbuf, N);
arrbuf += stride;
dstbuf += N;
}
}
else if ((A->strides[1] == 3) && (A->strides[2] == 1))
{
const size_t stride = A->strides[0] - imo->colsIn * 3;
for (size_t rownum = 0; rownum < imo->rowsIn; rownum++)
{
for (size_t colnum = 0; colnum < imo->colsIn; colnum++)
{
*dstbuf++ = *arrbuf++;
*dstbuf++ = *arrbuf++;
*dstbuf++ = *arrbuf++;
*dstbuf++ = 255;
}
arrbuf += stride;
}
}
else
{
PyArrayIterObject *iter;
iter = (PyArrayIterObject *)PyArray_IterNew((PyObject *)A);
if (A->dimensions[2] == 4)
{
while (iter->index < iter->size) {
*dstbuf++ = *((unsigned char *)iter->dataptr);
PyArray_ITER_NEXT(iter);
}
}
else
{
while (iter->index < iter->size) {
*dstbuf++ = *((unsigned char *)iter->dataptr);
PyArray_ITER_NEXT(iter);
*dstbuf++ = *((unsigned char *)iter->dataptr);
PyArray_ITER_NEXT(iter);
*dstbuf++ = *((unsigned char *)iter->dataptr);
PyArray_ITER_NEXT(iter);
*dstbuf++ = 255;
}
}
Py_DECREF(iter);
}
if (isoutput)
{
// make the output buffer point to the input buffer
imo->rowsOut = imo->rowsIn;
imo->colsOut = imo->colsIn;
imo->rbufOut = new agg::rendering_buffer;
imo->bufferOut = buffer;
imo->rbufOut->attach(imo->bufferOut, imo->colsOut, imo->rowsOut, imo->colsOut * imo->BPP);
}
else
{
imo->bufferIn = buffer;
imo->rbufIn = new agg::rendering_buffer;
imo->rbufIn->attach(buffer, imo->colsIn, imo->rowsIn, imo->colsIn*imo->BPP);
}
return Py::asObject(imo);
}
char _image_module_frombuffer__doc__[] =
"frombuffer(buffer, width, height, isoutput)\n"
"\n"
"Load the image from a character buffer\n"
"By default this function fills the input buffer, which can subsequently\n"
"be resampled using resize. If isoutput=1, fill the output buffer.\n"
"This is used to support raw pixel images w/o resampling."
;
Py::Object
_image_module::frombuffer(const Py::Tuple& args)
{
_VERBOSE("_image_module::frombuffer");
args.verify_length(4);
PyObject *bufin = new_reference_to(args[0]);
size_t x = (long)Py::Int(args[1]);
size_t y = (long)Py::Int(args[2]);
if (x >= 32768 || y >= 32768)
{
throw Py::ValueError("x and y must both be less than 32768");
}
int isoutput = Py::Int(args[3]);
if (PyObject_CheckReadBuffer(bufin) != 1)
throw Py::ValueError("First argument must be a buffer.");
Image* imo = new Image;
imo->rowsIn = y;
imo->colsIn = x;
Py_ssize_t NUMBYTES(imo->colsIn * imo->rowsIn * imo->BPP);
Py_ssize_t buflen;
const agg::int8u *rawbuf;
if (PyObject_AsReadBuffer(bufin, reinterpret_cast<const void**>(&rawbuf), &buflen) != 0)
{
throw Py::ValueError("Cannot get buffer from object.");
}
// Check buffer is required size.
if (buflen != NUMBYTES)
{
throw Py::ValueError("Buffer length must be width * height * 4.");
}
// Copy from input buffer to new buffer for agg.
agg::int8u* buffer = new agg::int8u[NUMBYTES];
if (buffer == NULL) //todo: also handle allocation throw
{
throw Py::MemoryError("_image_module::frombuffer could not allocate memory");
}
memmove(buffer, rawbuf, NUMBYTES);
if (isoutput)
{
// make the output buffer point to the input buffer
imo->rowsOut = imo->rowsIn;
imo->colsOut = imo->colsIn;
imo->rbufOut = new agg::rendering_buffer;
imo->bufferOut = buffer;
imo->rbufOut->attach(imo->bufferOut, imo->colsOut, imo->rowsOut, imo->colsOut * imo->BPP);
}
else
{
imo->bufferIn = buffer;
imo->rbufIn = new agg::rendering_buffer;
imo->rbufIn->attach(buffer, imo->colsIn, imo->rowsIn, imo->colsIn*imo->BPP);
}
return Py::asObject(imo);
}
// utilities for irregular grids
void _bin_indices_middle(unsigned int *irows, int nrows, float *ys1, int ny, float dy, float y_min)
{
int i, j, j_last;
unsigned int * rowstart = irows;
float *ys2 = ys1 + 1;
float *yl = ys1 + ny ;
float yo = y_min + dy / 2.0;
float ym = 0.5f * (*ys1 + *ys2);
// y/rows
j = 0;
j_last = j;
for (i = 0;i < nrows;i++, yo += dy, rowstart++)
{
while (ys2 != yl && yo > ym)
{
ys1 = ys2;
ys2 = ys1 + 1;
ym = 0.5f * (*ys1 + *ys2);
j++;
}
*rowstart = j - j_last;
j_last = j;
}
}
void _bin_indices_middle_linear(float *arows, unsigned int *irows, int nrows, float *y, int ny, float dy, float y_min)
{
int i;
int ii = 0;
int iilast = ny - 1;
float sc = 1 / dy;
int iy0 = (int)floor(sc * (y[ii] - y_min));
int iy1 = (int)floor(sc * (y[ii+1] - y_min));
float invgap = 1.0f / (iy1 - iy0);
for (i = 0; i < nrows && i <= iy0; i++)
{
irows[i] = 0;
arows[i] = 1.0;
//std::cerr<<"i="<<i<<" ii="<<0<<" a="<< arows[i]<< std::endl;
}
for (; i < nrows; i++)
{
while (i > iy1 && ii < iilast)
{
ii++;
iy0 = iy1;
iy1 = (int)floor(sc * (y[ii+1] - y_min));
invgap = 1.0f / (iy1 - iy0);
}
if (i >= iy0 && i <= iy1)
{
irows[i] = ii;
arows[i] = (iy1 - i) * invgap;
//std::cerr<<"i="<<i<<" ii="<<ii<<" a="<< arows[i]<< std::endl;
}
else break;
}
for (; i < nrows; i++)
{
irows[i] = iilast - 1;
arows[i] = 0.0;
//std::cerr<<"i="<<i<<" ii="<<iilast-1<<" a="<< arows[i]<< std::endl;
}
}
void _bin_indices(int *irows, int nrows, double *y, int ny,
double sc, double offs)
{
int i;
if (sc*(y[ny-1] - y[0]) > 0)
{
int ii = 0;
int iilast = ny - 1;
int iy0 = (int)floor(sc * (y[ii] - offs));
int iy1 = (int)floor(sc * (y[ii+1] - offs));
for (i = 0; i < nrows && i < iy0; i++)
{
irows[i] = -1;
}
for (; i < nrows; i++)
{
while (i > iy1 && ii < iilast)
{
ii++;
iy0 = iy1;
iy1 = (int)floor(sc * (y[ii+1] - offs));
}
if (i >= iy0 && i <= iy1) irows[i] = ii;
else break;
}
for (; i < nrows; i++)
{
irows[i] = -1;
}
}
else
{
int iilast = ny - 1;
int ii = iilast;
int iy0 = (int)floor(sc * (y[ii] - offs));
int iy1 = (int)floor(sc * (y[ii-1] - offs));
for (i = 0; i < nrows && i < iy0; i++)
{
irows[i] = -1;
}
for (; i < nrows; i++)
{
while (i > iy1 && ii > 1)
{
ii--;
iy0 = iy1;
iy1 = (int)floor(sc * (y[ii-1] - offs));
}
if (i >= iy0 && i <= iy1) irows[i] = ii - 1;
else break;
}
for (; i < nrows; i++)
{
irows[i] = -1;
}
}
}
void _bin_indices_linear(float *arows, int *irows, int nrows, double *y, int ny,
double sc, double offs)
{
int i;
if (sc*(y[ny-1] - y[0]) > 0)
{
int ii = 0;
int iilast = ny - 1;
int iy0 = (int)floor(sc * (y[ii] - offs));
int iy1 = (int)floor(sc * (y[ii+1] - offs));
float invgap = 1.0 / (iy1 - iy0);
for (i = 0; i < nrows && i < iy0; i++)
{
irows[i] = -1;
}
for (; i < nrows; i++)
{
while (i > iy1 && ii < iilast)
{
ii++;
iy0 = iy1;
iy1 = (int)floor(sc * (y[ii+1] - offs));
invgap = 1.0 / (iy1 - iy0);
}
if (i >= iy0 && i <= iy1)
{
irows[i] = ii;
arows[i] = (iy1 - i) * invgap;
}
else break;
}
for (; i < nrows; i++)
{
irows[i] = -1;
}
}
else
{
int iilast = ny - 1;
int ii = iilast;
int iy0 = (int)floor(sc * (y[ii] - offs));
int iy1 = (int)floor(sc * (y[ii-1] - offs));
float invgap = 1.0 / (iy1 - iy0);
for (i = 0; i < nrows && i < iy0; i++)
{
irows[i] = -1;
}
for (; i < nrows; i++)
{
while (i > iy1 && ii > 1)
{
ii--;
iy0 = iy1;
iy1 = (int)floor(sc * (y[ii-1] - offs));
invgap = 1.0 / (iy1 - iy0);
}
if (i >= iy0 && i <= iy1)
{
irows[i] = ii - 1;
arows[i] = (i - iy0) * invgap;
}
else break;
}
for (; i < nrows; i++)
{
irows[i] = -1;
}
}
}
char __image_module_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"
;
void _pcolor_cleanup(PyArrayObject* x, PyArrayObject* y, PyArrayObject *d,
unsigned int * rowstarts , unsigned int*colstarts ,
float *acols , float *arows)
{
Py_XDECREF(x);
Py_XDECREF(y);
Py_XDECREF(d);
if (rowstarts)
{
PyMem_Free(rowstarts);
}
if (colstarts)
{
PyMem_Free(colstarts);
}
if (acols)
{
PyMem_Free(acols);
}
if (arows)
{
PyMem_Free(arows);
}
return;
}
Py::Object
_image_module::pcolor(const Py::Tuple& args)
{
_VERBOSE("_image_module::pcolor");
if (args.length() != 7)
{
throw Py::TypeError("Incorrect number of arguments (7 expected)");
}
Py::Object xp = args[0];
Py::Object yp = args[1];
Py::Object dp = args[2];
unsigned int rows = (unsigned long)Py::Int(args[3]);
unsigned int cols = (unsigned long)Py::Int(args[4]);
Py::Tuple bounds = args[5];
unsigned int interpolation = (unsigned long)Py::Int(args[6]);
if (rows >= 32768 || cols >= 32768)
{
throw Py::ValueError("rows and cols must both be less than 32768");
}
if (bounds.length() != 4)
{
throw Py::TypeError("Incorrect number of bounds (4 expected)");
}
float x_min = Py::Float(bounds[0]);
float x_max = Py::Float(bounds[1]);
float y_min = Py::Float(bounds[2]);
float y_max = Py::Float(bounds[3]);
float width = x_max - x_min;
float height = y_max - y_min;
float dx = width / ((float) cols);
float dy = height / ((float) rows);
// Check we have something to output to
if (rows == 0 || cols == 0)
{
throw Py::ValueError("Cannot scale to zero size");
}
PyArrayObject *x = NULL;
PyArrayObject *y = NULL;
PyArrayObject *d = NULL;
unsigned int *rowstarts = NULL;
unsigned int *colstarts = NULL;
float *acols = NULL;
float *arows = NULL;
// Get numpy arrays
x = (PyArrayObject *) PyArray_ContiguousFromObject(xp.ptr(), PyArray_FLOAT, 1, 1);
if (x == NULL)
{
_pcolor_cleanup(x, y, d, rowstarts, colstarts, acols, arows);
throw Py::ValueError("x is of incorrect type (wanted 1D float)");
}
y = (PyArrayObject *) PyArray_ContiguousFromObject(yp.ptr(), PyArray_FLOAT, 1, 1);
if (y == NULL)
{
_pcolor_cleanup(x, y, d, rowstarts, colstarts, acols, arows);
throw Py::ValueError("y is of incorrect type (wanted 1D float)");
}
d = (PyArrayObject *) PyArray_ContiguousFromObject(dp.ptr(), PyArray_UBYTE, 3, 3);
if (d == NULL)
{
_pcolor_cleanup(x, y, d, rowstarts, colstarts, acols, arows);
throw Py::ValueError("data is of incorrect type (wanted 3D UInt8)");
}
if (d->dimensions[2] != 4)
{
_pcolor_cleanup(x, y, d, rowstarts, colstarts, acols, arows);
throw Py::ValueError("data must be in RGBA format");
}
// Check dimensions match
int nx = x->dimensions[0];
int ny = y->dimensions[0];
if (nx != d->dimensions[1] || ny != d->dimensions[0])
{
_pcolor_cleanup(x, y, d, rowstarts, colstarts, acols, arows);
throw Py::ValueError("data and axis dimensions do not match");
}
// Allocate memory for pointer arrays
rowstarts = reinterpret_cast<unsigned int*>(PyMem_Malloc(sizeof(unsigned int) * rows));
if (rowstarts == NULL)
{
_pcolor_cleanup(x, y, d, rowstarts, colstarts, acols, arows);
throw Py::MemoryError("Cannot allocate memory for lookup table");
}
colstarts = reinterpret_cast<unsigned int*>(PyMem_Malloc(sizeof(unsigned int) * cols));
if (colstarts == NULL)
{
_pcolor_cleanup(x, y, d, rowstarts, colstarts, acols, arows);
throw Py::MemoryError("Cannot allocate memory for lookup table");
}
// Create output
Image* imo = new Image;
imo->rowsIn = rows;
imo->colsIn = cols;
imo->rowsOut = rows;
imo->colsOut = cols;
size_t NUMBYTES(rows * cols * 4);
agg::int8u *buffer = new agg::int8u[NUMBYTES];
if (buffer == NULL)
{
_pcolor_cleanup(x, y, d, rowstarts, colstarts, acols, arows);
throw Py::MemoryError("Could not allocate memory for image");
}
// Calculate the pointer arrays to map input x to output x
unsigned int i, j;
unsigned int * colstart = colstarts;
unsigned int * rowstart = rowstarts;
float *xs1 = reinterpret_cast<float*>(x->data);
float *ys1 = reinterpret_cast<float*>(y->data);
// Copy data to output buffer
unsigned char *start;
unsigned char *inposition;
size_t inrowsize(nx*4);
size_t rowsize(cols*4);
rowstart = rowstarts;
agg::int8u * position = buffer;
agg::int8u * oldposition = NULL;
start = reinterpret_cast<unsigned char*>(d->data);
int s0 = d->strides[0];
int s1 = d->strides[1];
if (interpolation == Image::NEAREST)
{
_bin_indices_middle(colstart, cols, xs1, nx, dx, x_min);
_bin_indices_middle(rowstart, rows, ys1, ny, dy, y_min);
for (i = 0;i < rows;i++, rowstart++)
{
if (i > 0 && *rowstart == 0)
{
memcpy(position, oldposition, rowsize*sizeof(agg::int8u));
oldposition = position;
position += rowsize;
}
else
{
oldposition = position;
start += *rowstart * inrowsize;
inposition = start;
for (j = 0, colstart = colstarts;j < cols;j++, position += 4, colstart++)
{
inposition += *colstart * 4;
memcpy(position, inposition, 4*sizeof(agg::int8u));
}
}
}
}
else if (interpolation == Image::BILINEAR)
{
arows = reinterpret_cast<float *>(PyMem_Malloc(sizeof(float) * rows));
if (arows == NULL)
{
_pcolor_cleanup(x, y, d, rowstarts, colstarts, acols, arows);
throw Py::MemoryError("Cannot allocate memory for lookup table");
}
acols = reinterpret_cast<float*>(PyMem_Malloc(sizeof(float) * cols));
if (acols == NULL)
{
_pcolor_cleanup(x, y, d, rowstarts, colstarts, acols, arows);
throw Py::MemoryError("Cannot allocate memory for lookup table");
}
_bin_indices_middle_linear(acols, colstart, cols, xs1, nx, dx, x_min);
_bin_indices_middle_linear(arows, rowstart, rows, ys1, ny, dy, y_min);
double a00, a01, a10, a11, alpha, beta;
agg::int8u * start00;
agg::int8u * start01;
agg::int8u * start10;
agg::int8u * start11;
// Copy data to output buffer
for (i = 0; i < rows; i++)
{
for (j = 0; j < cols; j++)
{
alpha = arows[i];
beta = acols[j];
a00 = alpha * beta;
a01 = alpha * (1.0 - beta);
a10 = (1.0 - alpha) * beta;
a11 = 1.0 - a00 - a01 - a10;
start00 = (agg::int8u *)(start + s0 * rowstart[i] + s1 * colstart[j]);
start01 = start00 + s1;
start10 = start00 + s0;
start11 = start10 + s1;
position[0] = (agg::int8u)(start00[0] * a00 + start01[0] * a01 + start10[0] * a10 + start11[0] * a11);
position[1] = (agg::int8u)(start00[1] * a00 + start01[1] * a01 + start10[1] * a10 + start11[1] * a11);
position[2] = (agg::int8u)(start00[2] * a00 + start01[2] * a01 + start10[2] * a10 + start11[2] * a11);
position[3] = (agg::int8u)(start00[3] * a00 + start01[3] * a01 + start10[3] * a10 + start11[3] * a11);
position += 4;
}
}
}
// Attach output buffer to output buffer
imo->rbufOut = new agg::rendering_buffer;
imo->bufferOut = buffer;
imo->rbufOut->attach(imo->bufferOut, imo->colsOut, imo->rowsOut, imo->colsOut * imo->BPP);
_pcolor_cleanup(x, y, d, rowstarts, colstarts, acols, arows);
return Py::asObject(imo);
}
void _pcolor2_cleanup(PyArrayObject* x, PyArrayObject* y, PyArrayObject *d,
PyArrayObject* bg, int *irows, int*jcols)
{
Py_XDECREF(x);
Py_XDECREF(y);
Py_XDECREF(d);
Py_XDECREF(bg);
if (irows)
{
PyMem_Free(irows);
}
if (jcols)
{
PyMem_Free(jcols);
}
}
char __image_module_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"
;
Py::Object
_image_module::pcolor2(const Py::Tuple& args)
{
_VERBOSE("_image_module::pcolor2");
if (args.length() != 7)
{
throw Py::TypeError("Incorrect number of arguments (6 expected)");
}
Py::Object xp = args[0];
Py::Object yp = args[1];
Py::Object dp = args[2];
int rows = Py::Int(args[3]);
int cols = Py::Int(args[4]);
Py::Tuple bounds = args[5];
Py::Object bgp = args[6];
if (rows >= 32768 || cols >= 32768)
{
throw Py::ValueError("rows and cols must both be less than 32768");
}
if (bounds.length() != 4)
{
throw Py::TypeError("Incorrect number of bounds (4 expected)");
}
double x_left = Py::Float(bounds[0]);
double x_right = Py::Float(bounds[1]);
double y_bot = Py::Float(bounds[2]);
double y_top = Py::Float(bounds[3]);
// Check we have something to output to
if (rows == 0 || cols == 0)
{
throw Py::ValueError("rows or cols is zero; there are no pixels");
}
PyArrayObject* x = NULL;
PyArrayObject* y = NULL;
PyArrayObject* d = NULL;
PyArrayObject* bg = NULL;
int* irows = NULL;
int* jcols = NULL;
// Get numpy arrays
x = (PyArrayObject *) PyArray_ContiguousFromObject(xp.ptr(), PyArray_DOUBLE, 1, 1);
if (x == NULL)
{
_pcolor2_cleanup(x, y, d, bg, irows, jcols);
throw Py::ValueError("x is of incorrect type (wanted 1D double)");
}
y = (PyArrayObject *) PyArray_ContiguousFromObject(yp.ptr(), PyArray_DOUBLE, 1, 1);
if (y == NULL)
{
_pcolor2_cleanup(x, y, d, bg, irows, jcols);
throw Py::ValueError("y is of incorrect type (wanted 1D double)");
}
d = (PyArrayObject *) PyArray_ContiguousFromObject(dp.ptr(), PyArray_UBYTE, 3, 3);
if (d == NULL)
{
_pcolor2_cleanup(x, y, d, bg, irows, jcols);
throw Py::ValueError("data is of incorrect type (wanted 3D uint8)");
}
if (d->dimensions[2] != 4)
{
_pcolor2_cleanup(x, y, d, bg, irows, jcols);
throw Py::ValueError("data must be in RGBA format");
}
// Check dimensions match
int nx = x->dimensions[0];
int ny = y->dimensions[0];
if (nx != d->dimensions[1] + 1 || ny != d->dimensions[0] + 1)
{
_pcolor2_cleanup(x, y, d, bg, irows, jcols);
throw Py::ValueError("data and axis bin boundary dimensions are incompatible");
}
bg = (PyArrayObject *) PyArray_ContiguousFromObject(bgp.ptr(), PyArray_UBYTE, 1, 1);
if (bg == NULL)
{
_pcolor2_cleanup(x, y, d, bg, irows, jcols);
throw Py::ValueError("bg is of incorrect type (wanted 1D uint8)");
}
if (bg->dimensions[0] != 4)
{
_pcolor2_cleanup(x, y, d, bg, irows, jcols);
throw Py::ValueError("bg must be in RGBA format");
}
// Allocate memory for pointer arrays
irows = reinterpret_cast<int*>(PyMem_Malloc(sizeof(int) * rows));
if (irows == NULL)
{
_pcolor2_cleanup(x, y, d, bg, irows, jcols);
throw Py::MemoryError("Cannot allocate memory for lookup table");
}
jcols = reinterpret_cast<int*>(PyMem_Malloc(sizeof(int) * cols));
if (jcols == NULL)
{
_pcolor2_cleanup(x, y, d, bg, irows, jcols);
throw Py::MemoryError("Cannot allocate memory for lookup table");
}
// Create output
Image* imo = new Image;
imo->rowsIn = rows;
imo->rowsOut = rows;
imo->colsIn = cols;
imo->colsOut = cols;
size_t NUMBYTES(rows * cols * 4);
agg::int8u *buffer = new agg::int8u[NUMBYTES];
if (buffer == NULL)
{
_pcolor2_cleanup(x, y, d, bg, irows, jcols);
throw Py::MemoryError("Could not allocate memory for image");
}
// Calculate the pointer arrays to map input x to output x
int i, j;
double *x0 = reinterpret_cast<double*>(x->data);
double *y0 = reinterpret_cast<double*>(y->data);
double sx = cols / (x_right - x_left);
double sy = rows / (y_top - y_bot);
_bin_indices(jcols, cols, x0, nx, sx, x_left);
_bin_indices(irows, rows, y0, ny, sy, y_bot);
// Copy data to output buffer
agg::int8u * position = buffer;
unsigned char *start = reinterpret_cast<unsigned char*>(d->data);
unsigned char *bgptr = reinterpret_cast<unsigned char*>(bg->data);
int s0 = d->strides[0];
int s1 = d->strides[1];
for (i = 0; i < rows; i++)
{
for (j = 0; j < cols; j++)
{
if (irows[i] == -1 || jcols[j] == -1)
{
memcpy(position, bgptr, 4*sizeof(agg::int8u));
}
else
{
memcpy(position, (start + s0*irows[i] + s1*jcols[j]),
4*sizeof(agg::int8u));
}
position += 4;
}
}
// Attach output buffer to output buffer
imo->rbufOut = new agg::rendering_buffer;
imo->bufferOut = buffer;
imo->rbufOut->attach(imo->bufferOut, imo->colsOut, imo->rowsOut, imo->colsOut * imo->BPP);
_pcolor2_cleanup(x, y, d, bg, irows, jcols);
return Py::asObject(imo);
}
#if PY3K
PyMODINIT_FUNC
PyInit__image(void)
#else
PyMODINIT_FUNC
init_image(void)
#endif
{
_VERBOSE("init_image");
static _image_module* _image = new _image_module;
import_array();
Py::Dict d = _image->moduleDictionary();
d["NEAREST"] = Py::Int(Image::NEAREST);
d["BILINEAR"] = Py::Int(Image::BILINEAR);
d["BICUBIC"] = Py::Int(Image::BICUBIC);
d["SPLINE16"] = Py::Int(Image::SPLINE16);
d["SPLINE36"] = Py::Int(Image::SPLINE36);
d["HANNING"] = Py::Int(Image::HANNING);
d["HAMMING"] = Py::Int(Image::HAMMING);
d["HERMITE"] = Py::Int(Image::HERMITE);
d["KAISER"] = Py::Int(Image::KAISER);
d["QUADRIC"] = Py::Int(Image::QUADRIC);
d["CATROM"] = Py::Int(Image::CATROM);
d["GAUSSIAN"] = Py::Int(Image::GAUSSIAN);
d["BESSEL"] = Py::Int(Image::BESSEL);
d["MITCHELL"] = Py::Int(Image::MITCHELL);
d["SINC"] = Py::Int(Image::SINC);
d["LANCZOS"] = Py::Int(Image::LANCZOS);
d["BLACKMAN"] = Py::Int(Image::BLACKMAN);
d["ASPECT_FREE"] = Py::Int(Image::ASPECT_FREE);
d["ASPECT_PRESERVE"] = Py::Int(Image::ASPECT_PRESERVE);
#if PY3K
return _image->module().ptr();
#endif
}
Jump to Line
Something went wrong with that request. Please try again.