Skip to content

Instantly share code, notes, and snippets.

@denis-trofimov
Forked from tmattio/cv2.cpp
Last active April 21, 2017 14:40
Show Gist options
  • Save denis-trofimov/cd4c29dac34cf44d6c69f5802554802c to your computer and use it in GitHub Desktop.
Save denis-trofimov/cd4c29dac34cf44d6c69f5802554802c to your computer and use it in GitHub Desktop.
Cyphon OpenCV
#include <Python.h>
#include <opencv2/core/core.hpp>
#include <numpy/ndarrayobject.h>
#include <cstdio>
using namespace cv;
static PyObject* opencv_error = 0;
#define ERRWRAP2(expr) \
try { \
PyAllowThreads allowThreads; \
expr; \
} catch (const cv::Exception& e) { \
PyErr_SetString(opencv_error, e.what()); \
return 0; \
}
static int failmsg(const char* fmt, ...) {
char str[1000];
va_list ap;
va_start(ap, fmt);
vsnprintf(str, sizeof(str), fmt, ap);
va_end(ap);
PyErr_SetString(PyExc_TypeError, str);
return 0;
}
static PyObject* failmsgp(const char* fmt, ...) {
char str[1000];
va_list ap;
va_start(ap, fmt);
vsnprintf(str, sizeof(str), fmt, ap);
va_end(ap);
PyErr_SetString(PyExc_TypeError, str);
return 0;
}
class PyAllowThreads {
public:
PyAllowThreads() : _state(PyEval_SaveThread()) {}
~PyAllowThreads() { PyEval_RestoreThread(_state); }
private:
PyThreadState* _state;
};
class PyEnsureGIL {
public:
PyEnsureGIL() : _state(PyGILState_Ensure()) {}
~PyEnsureGIL() { PyGILState_Release(_state); }
private:
PyGILState_STATE _state;
};
static size_t REFCOUNT_OFFSET =
(size_t) &
(((PyObject*)0)->ob_refcnt) +
(0x12345678 != *(const size_t*)"\x78\x56\x34\x12\0\0\0\0\0") *
sizeof(int);
static inline PyObject* pyObjectFromRefcount(const int* refcount) {
return (PyObject*)((size_t)refcount - REFCOUNT_OFFSET);
}
static inline int* refcountFromPyObject(const PyObject* obj) {
return (int*)((size_t)obj + REFCOUNT_OFFSET);
}
enum { ARG_NONE = 0, ARG_MAT = 1, ARG_SCALAR = 2 };
class NumpyAllocator : public MatAllocator {
public:
NumpyAllocator() { stdAllocator = Mat::getStdAllocator(); }
~NumpyAllocator() {}
UMatData* allocate(PyObject* o, int dims, const int* sizes, int type,
size_t* step) const {
UMatData* u = new UMatData(this);
u->data = u->origdata = (uchar*)PyArray_DATA((PyArrayObject*)o);
npy_intp* _strides = PyArray_STRIDES((PyArrayObject*)o);
for (int i = 0; i < dims - 1; i++) step[i] = (size_t)_strides[i];
step[dims - 1] = CV_ELEM_SIZE(type);
u->size = sizes[0] * step[0];
u->userdata = o;
return u;
}
UMatData* allocate(int dims0, const int* sizes, int type, void* data,
size_t* step, int flags, UMatUsageFlags usageFlags) const {
if (data != 0) {
CV_Error(Error::StsAssert, "The data should normally be NULL!");
// probably this is safe to do in such extreme case
return stdAllocator->allocate(dims0, sizes, type, data, step, flags,
usageFlags);
}
PyEnsureGIL gil;
int depth = CV_MAT_DEPTH(type);
int cn = CV_MAT_CN(type);
const int f = (int)(sizeof(size_t) / 8);
int typenum =
depth == CV_8U
? NPY_UBYTE
: depth == CV_8S
? NPY_BYTE
: depth == CV_16U
? NPY_USHORT
: depth == CV_16S
? NPY_SHORT
: depth == CV_32S
? NPY_INT
: depth == CV_32F
? NPY_FLOAT
: depth == CV_64F
? NPY_DOUBLE
: f * NPY_ULONGLONG +
(f ^ 1) * NPY_UINT;
int i, dims = dims0;
cv::AutoBuffer<npy_intp> _sizes(dims + 1);
for (i = 0; i < dims; i++) _sizes[i] = sizes[i];
if (cn > 1) _sizes[dims++] = cn;
PyObject* o = PyArray_SimpleNew(dims, _sizes, typenum);
if (!o)
CV_Error_(Error::StsError,
("The numpy array of typenum=%d, ndims=%d can not be created",
typenum, dims));
return allocate(o, dims0, sizes, type, step);
}
bool allocate(UMatData* u, int accessFlags, UMatUsageFlags usageFlags) const {
return stdAllocator->allocate(u, accessFlags, usageFlags);
}
void deallocate(UMatData* u) const {
if (u) {
PyEnsureGIL gil;
PyObject* o = (PyObject*)u->userdata;
Py_XDECREF(o);
delete u;
}
}
const MatAllocator* stdAllocator;
};
NumpyAllocator g_numpyAllocator;
PyObject* fromMatToNDArray(const Mat& m) {
if (!m.data) Py_RETURN_NONE;
Mat temp, *p = (Mat*)&m;
if (!p->u || p->allocator != &g_numpyAllocator) {
temp.allocator = &g_numpyAllocator;
ERRWRAP2(m.copyTo(temp));
p = &temp;
}
PyObject* o = (PyObject*)p->u->userdata;
Py_INCREF(o);
return o;
}
Mat fromNDArrayToMat(PyObject* o) {
cv::Mat m;
bool allowND = true;
if (!PyArray_Check(o)) {
failmsg("argument is not a numpy array");
if (!m.data) m.allocator = &g_numpyAllocator;
} else {
PyArrayObject* oarr = (PyArrayObject*)o;
bool needcopy = false, needcast = false;
int typenum = PyArray_TYPE(oarr), new_typenum = typenum;
int type = typenum == NPY_UBYTE
? CV_8U
: typenum == NPY_BYTE
? CV_8S
: typenum == NPY_USHORT
? CV_16U
: typenum == NPY_SHORT
? CV_16S
: typenum == NPY_INT
? CV_32S
: typenum == NPY_INT32
? CV_32S
: typenum == NPY_FLOAT
? CV_32F
: typenum == NPY_DOUBLE
? CV_64F
: -1;
if (type < 0) {
if (typenum == NPY_INT64 || typenum == NPY_UINT64 || type == NPY_LONG) {
needcopy = needcast = true;
new_typenum = NPY_INT;
type = CV_32S;
} else {
failmsg("Argument data type is not supported");
m.allocator = &g_numpyAllocator;
return m;
}
}
#ifndef CV_MAX_DIM
const int CV_MAX_DIM = 32;
#endif
int ndims = PyArray_NDIM(oarr);
if (ndims >= CV_MAX_DIM) {
failmsg("Dimensionality of argument is too high");
if (!m.data) m.allocator = &g_numpyAllocator;
return m;
}
int size[CV_MAX_DIM + 1];
size_t step[CV_MAX_DIM + 1];
size_t elemsize = CV_ELEM_SIZE1(type);
const npy_intp* _sizes = PyArray_DIMS(oarr);
const npy_intp* _strides = PyArray_STRIDES(oarr);
bool ismultichannel = ndims == 3 && _sizes[2] <= CV_CN_MAX;
for (int i = ndims - 1; i >= 0 && !needcopy; i--) {
// these checks handle cases of
// a) multi-dimensional (ndims > 2) arrays, as well as simpler 1- and
// 2-dimensional cases
// b) transposed arrays, where _strides[] elements go in non-descending
// order
// c) flipped arrays, where some of _strides[] elements are negative
if ((i == ndims - 1 && (size_t)_strides[i] != elemsize) ||
(i < ndims - 1 && _strides[i] < _strides[i + 1]))
needcopy = true;
}
if (ismultichannel && _strides[1] != (npy_intp)elemsize * _sizes[2])
needcopy = true;
if (needcopy) {
if (needcast) {
o = PyArray_Cast(oarr, new_typenum);
oarr = (PyArrayObject*)o;
} else {
oarr = PyArray_GETCONTIGUOUS(oarr);
o = (PyObject*)oarr;
}
_strides = PyArray_STRIDES(oarr);
}
for (int i = 0; i < ndims; i++) {
size[i] = (int)_sizes[i];
step[i] = (size_t)_strides[i];
}
// handle degenerate case
if (ndims == 0) {
size[ndims] = 1;
step[ndims] = elemsize;
ndims++;
}
if (ismultichannel) {
ndims--;
type |= CV_MAKETYPE(0, size[2]);
}
if (ndims > 2 && !allowND) {
failmsg("%s has more than 2 dimensions");
} else {
m = Mat(ndims, size, type, PyArray_DATA(oarr), step);
m.u = g_numpyAllocator.allocate(o, ndims, size, type, step);
m.addref();
if (!needcopy) {
Py_INCREF(o);
}
}
m.allocator = &g_numpyAllocator;
}
return m;
}
#pragma once
#include "opencv2/core.hpp"
#include "opencv2/highgui.hpp"
using namespace cv;
void display_image(const Mat &img) {
cv::imshow("", img);
cv::waitKey(0);
}
import numpy as np
cimport numpy as np
from cpython.ref cimport PyObject
# Declares OpenCV's cv::Mat class
cdef extern from "opencv2/core/core.hpp":
cdef cppclass Mat:
pass
# Declares the official wrapper conversion functions + NumPy's import_array() function
cdef extern from "cv2.cpp":
void import_array()
PyObject* fromMatToNDArray(const Mat&)
Mat fromNDArrayToMat(PyObject* o)
# Function to be called at initialization
cdef void *init():
import_array()
# Python to C++ conversion
cdef Mat array2cvmat(object array):
cdef PyObject* pyobject = <PyObject*> array
cdef Mat mat = fromNDArrayToMat(pyobject)
return <Mat> mat
cdef extern from "object_detection.h":
void display_image(const Mat &)
def show_me(image):
init()
display_image(array2cvmat(image))
#======== setup.py ===========
# Usage: python3 setup.py build_ext --inplace
from distutils.core import setup
from distutils.extension import Extension
from Cython.Distutils import build_ext
from Cython.Build import cythonize
import numpy
import subprocess
proc_libs = subprocess.check_output("pkg-config --libs opencv".split())
proc_incs = subprocess.check_output("pkg-config --cflags opencv".split())
libs = [lib for lib in str(proc_libs, "utf-8").split()]
incs = [numpy.get_include()]
incs.extend([inc for inc in str(proc_incs, "utf-8").split()])
setup(
cmdclass = {'build_ext': build_ext},
ext_modules = cythonize(Extension("module",
sources = ["module.pyx"],
language = "c++",
include_dirs=incs,
extra_link_args=libs
)
)
)
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment