File: python-cameramodel-converter.c

package info (click to toggle)
mrcal 2.5-2
  • links: PTS, VCS
  • area: main
  • in suites:
  • size: 8,560 kB
  • sloc: python: 40,604; ansic: 15,576; cpp: 1,754; perl: 303; makefile: 158; sh: 98; lisp: 84
file content (131 lines) | stat: -rw-r--r-- 5,578 bytes parent folder | download | duplicates (3)
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;
}