1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131
|
// mrcal_cameramodel_converter is a "converter" function that can be used with
// "O&" conversions in PyArg_ParseTupleAndKeywords() calls. Can interpret either
// path strings or mrcal.cameramodel objects as mrcal_cameramodel_VOID_t structures
//
// This isn't a part of the mrcal Python wrapping, but helps other python
// wrapping programs work with mrcal_cameramodel_VOID_t. I link this into
// libmrcal.so, but libmrcal.so does NOT link with libpython. 99% of the usage
// of libmrcal.so will not use this, so it should work without libpython. People
// using this function will be doing so as part of
// PyArg_ParseTupleAndKeywords(), so they will be linking to libpython anyway.
// Thus I weaken all the references to libpython AFTER this is compiled. This is
// done in the Makefile, see the comment there for all sorts of gory details
#define NPY_NO_DEPRECATED_API NPY_API_VERSION
#include <Python.h>
#include <numpy/arrayobject.h>
#ifdef WEAKEN_PY_REFS
#include "python-cameramodel-converter-py-symbol-refs.h"
#define WEAKEN(f) extern __typeof__(f) f __attribute__((weak));
PY_REFS(WEAKEN)
#endif
#include "mrcal.h"
#include "python-wrapping-utilities.h"
#define CHECK_LAYOUT3(name, npy_type, dims_ref) \
CHECK_LAYOUT(name,xxx,xxx,xxx,xxx,name,npy_type,dims_ref)
#define BARF_AND_GOTO_DONE(fmt, ...) do { BARF(fmt, ##__VA_ARGS__); goto done; } while(0)
int mrcal_cameramodel_converter(PyObject* py_model,
mrcal_cameramodel_VOID_t** model)
{
// Define the PyArray_API. See here:
// https://numpy.org/doc/stable/reference/c-api/array.html#c.import_array
// PyArray_ImportNumPyAPI() has this if() logic, but that's only available
// in numpy >= 2.0. Without this, the PyArray_API function table will be
// NULL. It is static in each compile unit.
if(PyArray_API == NULL)
import_array();
int result = 0; // failure by default
PyObject* call_result = NULL;
if(py_model == Py_None)
*model = NULL;
else if(PyUnicode_Check(py_model))
{
// This is a string. Assume it's a filename.
const char* filename = PyUnicode_AsUTF8(py_model);
if(filename == NULL)
BARF_AND_GOTO_DONE("The model argument claims to be a string, but I could not get this string out of it");
*model = mrcal_read_cameramodel_file(filename);
if(*model == NULL)
BARF_AND_GOTO_DONE("Couldn't read mrcal_cameramodel_VOID_t from '%s'", filename);
}
else
{
call_result = PyObject_CallMethod(py_model, "intrinsics", NULL);
if(call_result == NULL)
BARF_AND_GOTO_DONE("Couldn't call cameramodel.intrinsics()");
if(!PyTuple_Check(call_result))
BARF_AND_GOTO_DONE("cameramodel.intrinsics() result isn't a tuple");
if(2 != PyTuple_Size(call_result))
BARF_AND_GOTO_DONE("cameramodel.intrinsics() result must be a tuple of length 2");
PyObject* py_lensmodel = PyTuple_GET_ITEM(call_result,0);
PyObject* intrinsics = PyTuple_GET_ITEM(call_result,1);
if(!(PyUnicode_Check(py_lensmodel) && PyArray_Check(intrinsics)))
BARF_AND_GOTO_DONE("cameramodel.intrinsics() result must contain (string,array)");
const char* lensmodel = PyUnicode_AsUTF8(py_lensmodel);
if(lensmodel == NULL)
BARF_AND_GOTO_DONE("The lensmodel claims to be a string, but I could not get this string out of it");
CHECK_LAYOUT3(intrinsics, NPY_DOUBLE, {-1});
int Nparams = PyArray_SIZE((PyArrayObject*)intrinsics);
*model = malloc(sizeof(mrcal_cameramodel_VOID_t) +
Nparams*sizeof(double));
if(NULL == *model)
BARF_AND_GOTO_DONE("Couldn't allocate cameramodel with %d intrinsics", Nparams);
if(!mrcal_lensmodel_from_name(&((*model)->lensmodel),
lensmodel))
BARF_AND_GOTO_DONE("Couldn't parsse lensmodel from '%s'", lensmodel);
memcpy(&(*model)->intrinsics[0],
PyArray_DATA((PyArrayObject*)intrinsics),
Nparams*sizeof(double));
Py_DECREF(call_result);
call_result = NULL;
call_result = PyObject_CallMethod(py_model, "imagersize", NULL);
if(call_result == NULL)
BARF_AND_GOTO_DONE("Couldn't call cameramodel.imagersize()");
if(!PyArray_Check((PyArrayObject*)call_result))
BARF_AND_GOTO_DONE("cameramodel.imagersize() result must be a numpy array");
CHECK_LAYOUT3(call_result, NPY_INT32, {2});
(*model)->imagersize[0] = ((int32_t*)PyArray_DATA((PyArrayObject*)call_result))[0];
(*model)->imagersize[1] = ((int32_t*)PyArray_DATA((PyArrayObject*)call_result))[1];
Py_DECREF(call_result);
call_result = NULL;
call_result = PyObject_CallMethod(py_model, "rt_cam_ref", NULL);
if(call_result == NULL)
BARF_AND_GOTO_DONE("Couldn't call cameramodel.rt_cam_ref()");
if(!PyArray_Check((PyArrayObject*)call_result))
BARF_AND_GOTO_DONE("cameramodel.rt_cam_ref() result must be a numpy array");
CHECK_LAYOUT3(call_result, NPY_DOUBLE, {6});
memcpy(&(*model)->rt_cam_ref[0],
PyArray_DATA((PyArrayObject*)call_result),
6*sizeof(double));
Py_DECREF(call_result);
call_result = NULL;
}
// success!
result = 1;
done:
Py_XDECREF(call_result);
if(result == 0)
{
free(*model);
*model = NULL;
}
return result;
}
|