dev-ros/cv_bridge: fix build with opencv 4
[gentoo.git] / dev-ros / cv_bridge / files / ocv4.patch
1 From b0281a5c844ea0b0d9e0104674474adf50810f49 Mon Sep 17 00:00:00 2001
2 From: BrutusTT <brutusthetschiepel@gmail.com>
3 Date: Wed, 4 Sep 2019 11:39:30 +0100
4 Subject: [PATCH 1/2] add OpenCV4 support addresses
5  ros-perception/vision_opencv#272
6
7 ---
8  cv_bridge/CMakeLists.txt     | 2 +-
9  cv_bridge/src/CMakeLists.txt | 8 +++++---
10  2 files changed, 6 insertions(+), 4 deletions(-)
11
12 diff --git a/cv_bridge/CMakeLists.txt b/cv_bridge/CMakeLists.txt
13 index 997bef3e..c203aad1 100644
14 --- a/cv_bridge/CMakeLists.txt
15 +++ b/cv_bridge/CMakeLists.txt
16 @@ -13,7 +13,7 @@ if(NOT ANDROID)
17  else()
18  find_package(Boost REQUIRED)
19  endif()
20 -find_package(OpenCV 3 REQUIRED
21 +find_package(OpenCV REQUIRED
22    COMPONENTS
23      opencv_core
24      opencv_imgproc
25 diff --git a/cv_bridge/src/CMakeLists.txt b/cv_bridge/src/CMakeLists.txt
26 index 37ba30ee..6d91003b 100644
27 --- a/cv_bridge/src/CMakeLists.txt
28 +++ b/cv_bridge/src/CMakeLists.txt
29 @@ -32,10 +32,12 @@ if (PYTHON_VERSION_MAJOR VERSION_EQUAL 3)
30    add_definitions(-DPYTHON3)
31  endif()
32  
33 -if (OpenCV_VERSION_MAJOR VERSION_EQUAL 3)
34 -add_library(${PROJECT_NAME}_boost module.cpp module_opencv3.cpp)
35 +if (OpenCV_VERSION_MAJOR VERSION_EQUAL 4)
36 +  add_library(${PROJECT_NAME}_boost module.cpp module_opencv4.cpp)
37 +elseif(OpenCV_VERSION_MAJOR VERSION_EQUAL 3)
38 +  add_library(${PROJECT_NAME}_boost module.cpp module_opencv3.cpp)
39  else()
40 -add_library(${PROJECT_NAME}_boost module.cpp module_opencv2.cpp)
41 +  add_library(${PROJECT_NAME}_boost module.cpp module_opencv2.cpp)
42  endif()
43  target_link_libraries(${PROJECT_NAME}_boost ${Boost_LIBRARIES}
44                                              ${catkin_LIBRARIES}
45
46 From 8e01b44c5c1c0003dc91273076f8ca7feb9a8025 Mon Sep 17 00:00:00 2001
47 From: BrutusTT <brutusthetschiepel@gmail.com>
48 Date: Thu, 17 Oct 2019 14:37:40 +0100
49 Subject: [PATCH 2/2] added missig file
50
51 ---
52  cv_bridge/src/module_opencv4.cpp | 371 +++++++++++++++++++++++++++++++
53  1 file changed, 371 insertions(+)
54  create mode 100644 cv_bridge/src/module_opencv4.cpp
55
56 diff --git a/cv_bridge/src/module_opencv4.cpp b/cv_bridge/src/module_opencv4.cpp
57 new file mode 100644
58 index 00000000..60a9d05d
59 --- /dev/null
60 +++ b/cv_bridge/src/module_opencv4.cpp
61 @@ -0,0 +1,371 @@
62 +// Taken from opencv/modules/python/src2/cv2.cpp
63 +
64 +#include "module.hpp"
65 +
66 +#include "opencv2/core/types_c.h"
67 +
68 +#include "opencv2/opencv_modules.hpp"
69 +
70 +#include "pycompat.hpp"
71 +
72 +static PyObject* opencv_error = 0;
73 +
74 +static int failmsg(const char *fmt, ...)
75 +{
76 +    char str[1000];
77 +
78 +    va_list ap;
79 +    va_start(ap, fmt);
80 +    vsnprintf(str, sizeof(str), fmt, ap);
81 +    va_end(ap);
82 +
83 +    PyErr_SetString(PyExc_TypeError, str);
84 +    return 0;
85 +}
86 +
87 +struct ArgInfo
88 +{
89 +    const char * name;
90 +    bool outputarg;
91 +    // more fields may be added if necessary
92 +
93 +    ArgInfo(const char * name_, bool outputarg_)
94 +        : name(name_)
95 +        , outputarg(outputarg_) {}
96 +
97 +    // to match with older pyopencv_to function signature
98 +    operator const char *() const { return name; }
99 +};
100 +
101 +class PyAllowThreads
102 +{
103 +public:
104 +    PyAllowThreads() : _state(PyEval_SaveThread()) {}
105 +    ~PyAllowThreads()
106 +    {
107 +        PyEval_RestoreThread(_state);
108 +    }
109 +private:
110 +    PyThreadState* _state;
111 +};
112 +
113 +class PyEnsureGIL
114 +{
115 +public:
116 +    PyEnsureGIL() : _state(PyGILState_Ensure()) {}
117 +    ~PyEnsureGIL()
118 +    {
119 +        PyGILState_Release(_state);
120 +    }
121 +private:
122 +    PyGILState_STATE _state;
123 +};
124 +
125 +#define ERRWRAP2(expr) \
126 +try \
127 +{ \
128 +    PyAllowThreads allowThreads; \
129 +    expr; \
130 +} \
131 +catch (const cv::Exception &e) \
132 +{ \
133 +    PyErr_SetString(opencv_error, e.what()); \
134 +    return 0; \
135 +}
136 +
137 +using namespace cv;
138 +
139 +static PyObject* failmsgp(const char *fmt, ...)
140 +{
141 +  char str[1000];
142 +
143 +  va_list ap;
144 +  va_start(ap, fmt);
145 +  vsnprintf(str, sizeof(str), fmt, ap);
146 +  va_end(ap);
147 +
148 +  PyErr_SetString(PyExc_TypeError, str);
149 +  return 0;
150 +}
151 +
152 +class NumpyAllocator : public MatAllocator
153 +{
154 +public:
155 +    NumpyAllocator() { stdAllocator = Mat::getStdAllocator(); }
156 +    ~NumpyAllocator() {}
157 +
158 +    UMatData* allocate(PyObject* o, int dims, const int* sizes, int type, size_t* step) const
159 +    {
160 +        UMatData* u = new UMatData(this);
161 +        u->data = u->origdata = (uchar*)PyArray_DATA((PyArrayObject*) o);
162 +        npy_intp* _strides = PyArray_STRIDES((PyArrayObject*) o);
163 +        for( int i = 0; i < dims - 1; i++ )
164 +            step[i] = (size_t)_strides[i];
165 +        step[dims-1] = CV_ELEM_SIZE(type);
166 +        u->size = sizes[0]*step[0];
167 +        u->userdata = o;
168 +        return u;
169 +    }
170 +
171 +    UMatData* allocate(int dims0, const int* sizes, int type, void* data, size_t* step, AccessFlag flags, UMatUsageFlags usageFlags) const
172 +    {
173 +        if( data != 0 )
174 +        {
175 +            CV_Error(Error::StsAssert, "The data should normally be NULL!");
176 +            // probably this is safe to do in such extreme case
177 +            return stdAllocator->allocate(dims0, sizes, type, data, step, flags, usageFlags);
178 +        }
179 +        PyEnsureGIL gil;
180 +
181 +        int depth = CV_MAT_DEPTH(type);
182 +        int cn = CV_MAT_CN(type);
183 +        const int f = (int)(sizeof(size_t)/8);
184 +        int typenum = depth == CV_8U ? NPY_UBYTE : depth == CV_8S ? NPY_BYTE :
185 +        depth == CV_16U ? NPY_USHORT : depth == CV_16S ? NPY_SHORT :
186 +        depth == CV_32S ? NPY_INT : depth == CV_32F ? NPY_FLOAT :
187 +        depth == CV_64F ? NPY_DOUBLE : f*NPY_ULONGLONG + (f^1)*NPY_UINT;
188 +        int i, dims = dims0;
189 +        cv::AutoBuffer<npy_intp> _sizes(dims + 1);
190 +        for( i = 0; i < dims; i++ )
191 +            _sizes[i] = sizes[i];
192 +        if( cn > 1 )
193 +            _sizes[dims++] = cn;
194 +        PyObject* o = PyArray_SimpleNew(dims, _sizes.data(), typenum);
195 +        if(!o)
196 +            CV_Error_(Error::StsError, ("The numpy array of typenum=%d, ndims=%d can not be created", typenum, dims));
197 +        return allocate(o, dims0, sizes, type, step);
198 +    }
199 +
200 +    bool allocate(UMatData* u, AccessFlag accessFlags, UMatUsageFlags usageFlags) const CV_OVERRIDE
201 +    {
202 +        return stdAllocator->allocate(u, accessFlags, usageFlags);
203 +    }
204 +
205 +    void deallocate(UMatData* u) const CV_OVERRIDE
206 +    {
207 +        if(!u)
208 +            return;
209 +        PyEnsureGIL gil;
210 +        CV_Assert(u->urefcount >= 0);
211 +        CV_Assert(u->refcount >= 0);
212 +        if(u->refcount == 0)
213 +        {
214 +            PyObject* o = (PyObject*)u->userdata;
215 +            Py_XDECREF(o);
216 +            delete u;
217 +        }                                                   
218 +    }
219 +
220 +    const MatAllocator* stdAllocator;
221 +};
222 +
223 +NumpyAllocator g_numpyAllocator;
224 +
225 +
226 +template<typename T> static
227 +bool pyopencv_to(PyObject* obj, T& p, const char* name = "<unknown>");
228 +
229 +template<typename T> static
230 +PyObject* pyopencv_from(const T& src);
231 +
232 +enum { ARG_NONE = 0, ARG_MAT = 1, ARG_SCALAR = 2 };
233 +
234 +// special case, when the convertor needs full ArgInfo structure
235 +static bool pyopencv_to(PyObject* o, Mat& m, const ArgInfo info)
236 +{
237 +      // to avoid PyArray_Check() to crash even with valid array
238 +    do_numpy_import( );
239 +
240 +
241 +    bool allowND = true;
242 +    if(!o || o == Py_None)
243 +    {
244 +        if( !m.data )
245 +            m.allocator = &g_numpyAllocator;
246 +        return true;
247 +    }
248 +
249 +    if( PyInt_Check(o) )
250 +    {
251 +        double v[] = {(double)PyInt_AsLong((PyObject*)o), 0., 0., 0.};
252 +        m = Mat(4, 1, CV_64F, v).clone();
253 +        return true;
254 +    }
255 +    if( PyFloat_Check(o) )
256 +    {
257 +        double v[] = {PyFloat_AsDouble((PyObject*)o), 0., 0., 0.};
258 +        m = Mat(4, 1, CV_64F, v).clone();
259 +        return true;
260 +    }
261 +    if( PyTuple_Check(o) )
262 +    {
263 +        int i, sz = (int)PyTuple_Size((PyObject*)o);
264 +        m = Mat(sz, 1, CV_64F);
265 +        for( i = 0; i < sz; i++ )
266 +        {
267 +            PyObject* oi = PyTuple_GET_ITEM(o, i);
268 +            if( PyInt_Check(oi) )
269 +                m.at<double>(i) = (double)PyInt_AsLong(oi);
270 +            else if( PyFloat_Check(oi) )
271 +                m.at<double>(i) = (double)PyFloat_AsDouble(oi);
272 +            else
273 +            {
274 +                failmsg("%s is not a numerical tuple", info.name);
275 +                m.release();
276 +                return false;
277 +            }
278 +        }
279 +        return true;
280 +    }
281 +
282 +    if( !PyArray_Check(o) )
283 +    {
284 +        failmsg("%s is not a numpy array, neither a scalar", info.name);
285 +        return false;
286 +    }
287 +
288 +    PyArrayObject* oarr = (PyArrayObject*) o;
289 +
290 +    bool needcopy = false, needcast = false;
291 +    int typenum = PyArray_TYPE(oarr), new_typenum = typenum;
292 +    int type = typenum == NPY_UBYTE ? CV_8U :
293 +               typenum == NPY_BYTE ? CV_8S :
294 +               typenum == NPY_USHORT ? CV_16U :
295 +               typenum == NPY_SHORT ? CV_16S :
296 +               typenum == NPY_INT ? CV_32S :
297 +               typenum == NPY_INT32 ? CV_32S :
298 +               typenum == NPY_FLOAT ? CV_32F :
299 +               typenum == NPY_DOUBLE ? CV_64F : -1;
300 +
301 +    if( type < 0 )
302 +    {
303 +        if( typenum == NPY_INT64 || typenum == NPY_UINT64 || type == NPY_LONG )
304 +        {
305 +            needcopy = needcast = true;
306 +            new_typenum = NPY_INT;
307 +            type = CV_32S;
308 +        }
309 +        else
310 +        {
311 +            failmsg("%s data type = %d is not supported", info.name, typenum);
312 +            return false;
313 +        }
314 +    }
315 +
316 +#ifndef CV_MAX_DIM
317 +    const int CV_MAX_DIM = 32;
318 +#endif
319 +
320 +    int ndims = PyArray_NDIM(oarr);
321 +    if(ndims >= CV_MAX_DIM)
322 +    {
323 +        failmsg("%s dimensionality (=%d) is too high", info.name, ndims);
324 +        return false;
325 +    }
326 +
327 +    int size[CV_MAX_DIM+1];
328 +    size_t step[CV_MAX_DIM+1];
329 +    size_t elemsize = CV_ELEM_SIZE1(type);
330 +    const npy_intp* _sizes = PyArray_DIMS(oarr);
331 +    const npy_intp* _strides = PyArray_STRIDES(oarr);
332 +    bool ismultichannel = ndims == 3 && _sizes[2] <= CV_CN_MAX;
333 +
334 +    for( int i = ndims-1; i >= 0 && !needcopy; i-- )
335 +    {
336 +        // these checks handle cases of
337 +        //  a) multi-dimensional (ndims > 2) arrays, as well as simpler 1- and 2-dimensional cases
338 +        //  b) transposed arrays, where _strides[] elements go in non-descending order
339 +        //  c) flipped arrays, where some of _strides[] elements are negative
340 +        if( (i == ndims-1 && (size_t)_strides[i] != elemsize) ||
341 +            (i < ndims-1 && _strides[i] < _strides[i+1]) )
342 +            needcopy = true;
343 +    }
344 +
345 +    if( ismultichannel && _strides[1] != (npy_intp)elemsize*_sizes[2] )
346 +        needcopy = true;
347 +
348 +    if (needcopy)
349 +    {
350 +        if (info.outputarg)
351 +        {
352 +            failmsg("Layout of the output array %s is incompatible with cv::Mat (step[ndims-1] != elemsize or step[1] != elemsize*nchannels)", info.name);
353 +            return false;
354 +        }
355 +
356 +        if( needcast ) {
357 +            o = PyArray_Cast(oarr, new_typenum);
358 +            oarr = (PyArrayObject*) o;
359 +        }
360 +        else {
361 +            oarr = PyArray_GETCONTIGUOUS(oarr);
362 +            o = (PyObject*) oarr;
363 +        }
364 +
365 +        _strides = PyArray_STRIDES(oarr);
366 +    }
367 +
368 +    for(int i = 0; i < ndims; i++)
369 +    {
370 +        size[i] = (int)_sizes[i];
371 +        step[i] = (size_t)_strides[i];
372 +    }
373 +
374 +    // handle degenerate case
375 +    if( ndims == 0) {
376 +        size[ndims] = 1;
377 +        step[ndims] = elemsize;
378 +        ndims++;
379 +    }
380 +
381 +    if( ismultichannel )
382 +    {
383 +        ndims--;
384 +        type |= CV_MAKETYPE(0, size[2]);
385 +    }
386 +
387 +    if( ndims > 2 && !allowND )
388 +    {
389 +        failmsg("%s has more than 2 dimensions", info.name);
390 +        return false;
391 +    }
392 +
393 +    m = Mat(ndims, size, type, PyArray_DATA(oarr), step);
394 +    m.u = g_numpyAllocator.allocate(o, ndims, size, type, step);
395 +    m.addref();
396 +
397 +    if( !needcopy )
398 +    {
399 +        Py_INCREF(o);
400 +    }
401 +    m.allocator = &g_numpyAllocator;
402 +
403 +    return true;
404 +}
405 +
406 +template<>
407 +bool pyopencv_to(PyObject* o, Mat& m, const char* name)
408 +{
409 +    return pyopencv_to(o, m, ArgInfo(name, 0));
410 +}
411 +
412 +PyObject* pyopencv_from(const Mat& m)
413 +{
414 +    if( !m.data )
415 +        Py_RETURN_NONE;
416 +    Mat temp, *p = (Mat*)&m;
417 +    if(!p->u || p->allocator != &g_numpyAllocator)
418 +    {
419 +        temp.allocator = &g_numpyAllocator;
420 +        ERRWRAP2(m.copyTo(temp));
421 +        p = &temp;
422 +    }
423 +    PyObject* o = (PyObject*)p->u->userdata;
424 +    Py_INCREF(o);
425 +    return o;
426 +}
427 +
428 +int convert_to_CvMat2(const PyObject* o, cv::Mat& m)
429 +{
430 +    pyopencv_to(const_cast<PyObject*>(o), m, "unknown");
431 +    return 0;
432 +}