Skip to content

Commit 6db6d29

Browse files
committed
Export C API in python module
- Allows converting Mat to NDArray and back
1 parent 70bbf17 commit 6db6d29

File tree

2 files changed

+194
-0
lines changed

2 files changed

+194
-0
lines changed
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,179 @@
1+
/**
2+
Utility API for converting Python types to/from OpenCV types
3+
4+
This is useful when you have a python module that links to a C++ library that
5+
uses OpenCV, and you need to convert things to/from python.
6+
7+
For now, this only supports converting Mat objects, but in the future
8+
other types of conversions may be supported.
9+
10+
This code is copied wholesafe from numpy's generate_numpy_api.py
11+
12+
Copyright (c) 2005-2016, NumPy Developers.
13+
All rights reserved.
14+
15+
Redistribution and use in source and binary forms, with or without
16+
modification, are permitted provided that the following conditions are
17+
met:
18+
19+
* Redistributions of source code must retain the above copyright
20+
notice, this list of conditions and the following disclaimer.
21+
22+
* Redistributions in binary form must reproduce the above
23+
copyright notice, this list of conditions and the following
24+
disclaimer in the documentation and/or other materials provided
25+
with the distribution.
26+
27+
* Neither the name of the NumPy Developers nor the names of any
28+
contributors may be used to endorse or promote products derived
29+
from this software without specific prior written permission.
30+
31+
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
32+
"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
33+
LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
34+
A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
35+
OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
36+
SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
37+
LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
38+
DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
39+
THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
40+
(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
41+
OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
42+
*/
43+
44+
#ifndef __PYCV2_API
45+
#define __PYCV2_API
46+
47+
#include <Python.h>
48+
#include <opencv2/core/core.hpp>
49+
50+
#if defined(PY_OPENCV_UNIQUE_SYMBOL)
51+
#define PyOpenCV_API PY_OPENCV_UNIQUE_SYMBOL
52+
#endif
53+
54+
#if defined(NO_IMPORT) || defined(NO_IMPORT_OPENCV)
55+
extern void **PyOpenCV_API;
56+
#else
57+
#if defined(PY_OPENCV_UNIQUE_SYMBOL)
58+
void **PyOpenCV_API;
59+
#else
60+
static void **PyOpenCV_API=NULL;
61+
#endif
62+
#endif
63+
64+
#if !defined(NO_IMPORT_OPENCV) && !defined(NO_IMPORT)
65+
static int
66+
_import_opencv(void)
67+
{
68+
//int st;
69+
PyObject *cv2 = PyImport_ImportModule("cv2");
70+
PyObject *c_api = NULL;
71+
72+
if (cv2 == NULL) {
73+
PyErr_SetString(PyExc_ImportError, "cv2 failed to import");
74+
return -1;
75+
}
76+
c_api = PyObject_GetAttrString(cv2, "_OPENCV_API");
77+
Py_DECREF(cv2);
78+
if (c_api == NULL) {
79+
PyErr_SetString(PyExc_AttributeError, "_OPENCV_API not found");
80+
return -1;
81+
}
82+
83+
#if PY_VERSION_HEX >= 0x03000000
84+
if (!PyCapsule_CheckExact(c_api)) {
85+
PyErr_SetString(PyExc_RuntimeError, "_OPENCV_API is not PyCapsule object");
86+
Py_DECREF(c_api);
87+
return -1;
88+
}
89+
PyOpenCV_API = (void **)PyCapsule_GetPointer(c_api, NULL);
90+
#else
91+
if (!PyCObject_Check(c_api)) {
92+
PyErr_SetString(PyExc_RuntimeError, "_OPENCV_API is not PyCObject object");
93+
Py_DECREF(c_api);
94+
return -1;
95+
}
96+
PyOpenCV_API = (void **)PyCObject_AsVoidPtr(c_api);
97+
#endif
98+
Py_DECREF(c_api);
99+
if (PyOpenCV_API == NULL) {
100+
PyErr_SetString(PyExc_RuntimeError, "_OPENCV_API is NULL pointer");
101+
return -1;
102+
}
103+
104+
/* TODO? Perform runtime check of C API version */
105+
/*
106+
if (NPY_VERSION != PyArray_GetNDArrayCVersion()) {
107+
PyErr_Format(PyExc_RuntimeError, "module compiled against "\
108+
"ABI version 0x%%x but this version of numpy is 0x%%x", \
109+
(int) NPY_VERSION, (int) PyArray_GetNDArrayCVersion());
110+
return -1;
111+
}
112+
if (NPY_FEATURE_VERSION > PyArray_GetNDArrayCFeatureVersion()) {
113+
PyErr_Format(PyExc_RuntimeError, "module compiled against "\
114+
"API version 0x%%x but this version of numpy is 0x%%x", \
115+
(int) NPY_FEATURE_VERSION, (int) PyArray_GetNDArrayCFeatureVersion());
116+
return -1;
117+
}
118+
*/
119+
120+
/*
121+
* TODO? Perform runtime check of endianness and check it matches the one set by
122+
* the headers (npy_endian.h) as a safeguard
123+
*/
124+
/*
125+
st = PyArray_GetEndianness();
126+
if (st == NPY_CPU_UNKNOWN_ENDIAN) {
127+
PyErr_Format(PyExc_RuntimeError, "FATAL: module compiled as unknown endian");
128+
return -1;
129+
}
130+
#if NPY_BYTE_ORDER == NPY_BIG_ENDIAN
131+
if (st != NPY_CPU_BIG) {
132+
PyErr_Format(PyExc_RuntimeError, "FATAL: module compiled as "\
133+
"big endian, but detected different endianness at runtime");
134+
return -1;
135+
}
136+
#elif NPY_BYTE_ORDER == NPY_LITTLE_ENDIAN
137+
if (st != NPY_CPU_LITTLE) {
138+
PyErr_Format(PyExc_RuntimeError, "FATAL: module compiled as "\
139+
"little endian, but detected different endianness at runtime");
140+
return -1;
141+
}
142+
#endif
143+
*/
144+
145+
return 0;
146+
}
147+
148+
#if PY_VERSION_HEX >= 0x03000000
149+
#define OPENCV_IMPORT_RETVAL NULL
150+
#else
151+
#define OPENCV_IMPORT_RETVAL
152+
#endif
153+
154+
#define import_opencv() {if (_import_opencv() < 0) {PyErr_Print(); PyErr_SetString(PyExc_ImportError, "cv2 failed to import"); return OPENCV_IMPORT_RETVAL; } }
155+
156+
#define import_opencv1(ret) {if (_import_opencv() < 0) {PyErr_Print(); PyErr_SetString(PyExc_ImportError, "cv2 failed to import"); return ret; } }
157+
158+
#define import_opencv2(msg, ret) {if (_import_opencv() < 0) {PyErr_Print(); PyErr_SetString(PyExc_ImportError, msg); return ret; } }
159+
160+
/**
161+
Exported API, must call import_opencv first!
162+
*/
163+
164+
#define PyOpenCV_MatToNdarray \
165+
(*(PyObject * (*)(const cv::Mat&)) \
166+
PyOpenCV_API[0])
167+
#define PyOpenCV_NdarrayToMat \
168+
(*(bool (*)(PyObject *, cv::Mat&, const char *)) \
169+
PyOpenCV_API[1])
170+
171+
#define PyOpenCV_UMatToNdarray \
172+
(*(PyObject * (*)(const cv::UMat&)) \
173+
PyOpenCV_API[2])
174+
#define PyOpenCV_NdarrayToUMat \
175+
(*(bool (*)(PyObject *, cv::UMat&, const char *)) \
176+
PyOpenCV_API[3])
177+
178+
#endif
179+
#endif

modules/python/src2/cv2.cpp

+15
Original file line numberDiff line numberDiff line change
@@ -1374,6 +1374,17 @@ static PyMethodDef special_methods[] = {
13741374
/************************************************************************/
13751375
/* Module init */
13761376

1377+
void * PyOpenCV_API[] = {
1378+
// 0: PyOpenCV_MatToNdarray
1379+
reinterpret_cast<void*>(static_cast<PyObject * (*)(const Mat&)>(&pyopencv_from)),
1380+
// 1: PyOpenCV_NdarrayToMat
1381+
reinterpret_cast<void*>(static_cast<bool (*)(PyObject *, Mat&, const char *)>(&pyopencv_to)),
1382+
// 2: PyOpenCV_UMatToNdarray
1383+
reinterpret_cast<void*>(static_cast<PyObject * (*)(const UMat&)>(&pyopencv_from)),
1384+
// 3: PyOpenCV_NdarrayToUMat
1385+
reinterpret_cast<void*>(static_cast<bool (*)(PyObject *, UMat&, const char *)>(&pyopencv_to)),
1386+
};
1387+
13771388
struct ConstDef
13781389
{
13791390
const char * name;
@@ -1486,6 +1497,10 @@ void initcv2()
14861497
#endif
14871498
PyModule_AddObject(m, "UMat", (PyObject *)&cv2_UMatWrapperType);
14881499

1500+
PyObject * c_api = PyCapsule_New(PyArray_API, NULL, NULL);
1501+
PyDict_SetItemString(d, "_OPENCV_API", c_api);
1502+
Py_DECREF(c_api);
1503+
14891504
#define PUBLISH(I) PyDict_SetItemString(d, #I, PyInt_FromLong(I))
14901505
//#define PUBLISHU(I) PyDict_SetItemString(d, #I, PyLong_FromUnsignedLong(I))
14911506
#define PUBLISH2(I, value) PyDict_SetItemString(d, #I, PyLong_FromLong(value))

0 commit comments

Comments
 (0)