Merge pull request #20298 from mpashchenkov:mp/python-desync
[platform/upstream/opencv.git] / modules / gapi / misc / python / pyopencv_gapi.hpp
1 #ifndef OPENCV_GAPI_PYOPENCV_GAPI_HPP
2 #define OPENCV_GAPI_PYOPENCV_GAPI_HPP
3
4 #ifdef HAVE_OPENCV_GAPI
5
6 #ifdef _MSC_VER
7 #pragma warning(disable: 4503)  // "decorated name length exceeded"
8 #endif
9
10 #include <opencv2/gapi/cpu/gcpukernel.hpp>
11 #include <opencv2/gapi/python/python.hpp>
12
13 // NB: Python wrapper replaces :: with _ for classes
14 using gapi_GKernelPackage        = cv::gapi::GKernelPackage;
15 using gapi_GNetPackage           = cv::gapi::GNetPackage;
16 using gapi_ie_PyParams           = cv::gapi::ie::PyParams;
17 using gapi_wip_IStreamSource_Ptr = cv::Ptr<cv::gapi::wip::IStreamSource>;
18 using detail_ExtractArgsCallback = cv::detail::ExtractArgsCallback;
19 using detail_ExtractMetaCallback = cv::detail::ExtractMetaCallback;
20 using vector_GNetParam           = std::vector<cv::gapi::GNetParam>;
21
22 // NB: Python wrapper generate T_U for T<U>
23 // This behavior is only observed for inputs
24 using GOpaque_bool    = cv::GOpaque<bool>;
25 using GOpaque_int     = cv::GOpaque<int>;
26 using GOpaque_double  = cv::GOpaque<double>;
27 using GOpaque_float   = cv::GOpaque<double>;
28 using GOpaque_string  = cv::GOpaque<std::string>;
29 using GOpaque_Point2i = cv::GOpaque<cv::Point>;
30 using GOpaque_Point2f = cv::GOpaque<cv::Point2f>;
31 using GOpaque_Size    = cv::GOpaque<cv::Size>;
32 using GOpaque_Rect    = cv::GOpaque<cv::Rect>;
33
34 using GArray_bool    = cv::GArray<bool>;
35 using GArray_int     = cv::GArray<int>;
36 using GArray_double  = cv::GArray<double>;
37 using GArray_float   = cv::GArray<double>;
38 using GArray_string  = cv::GArray<std::string>;
39 using GArray_Point2i = cv::GArray<cv::Point>;
40 using GArray_Point2f = cv::GArray<cv::Point2f>;
41 using GArray_Size    = cv::GArray<cv::Size>;
42 using GArray_Rect    = cv::GArray<cv::Rect>;
43 using GArray_Scalar  = cv::GArray<cv::Scalar>;
44 using GArray_Mat     = cv::GArray<cv::Mat>;
45 using GArray_GMat    = cv::GArray<cv::GMat>;
46 using GArray_Prim    = cv::GArray<cv::gapi::wip::draw::Prim>;
47
48 // FIXME: Python wrapper generate code without namespace std,
49 // so it cause error: "string wasn't declared"
50 // WA: Create using
51 using std::string;
52
53 namespace cv
54 {
55 namespace detail
56 {
57
58 class PyObjectHolder
59 {
60 public:
61     PyObjectHolder(PyObject* o, bool owner = true);
62     PyObject* get() const;
63
64 private:
65     class Impl;
66     std::shared_ptr<Impl> m_impl;
67 };
68
69 } // namespace detail
70 } // namespace cv
71
72 class cv::detail::PyObjectHolder::Impl
73 {
74 public:
75     Impl(PyObject* object, bool owner);
76     PyObject* get() const;
77     ~Impl();
78
79 private:
80     PyObject* m_object;
81 };
82
83 cv::detail::PyObjectHolder::Impl::Impl(PyObject* object, bool owner)
84     : m_object(object)
85 {
86     // NB: Become an owner of that PyObject.
87     // Need to store this and get access
88     // after the caller which provide the object is out of range.
89     if (owner)
90     {
91         // NB: Impossible take ownership if object is NULL.
92         GAPI_Assert(object);
93         Py_INCREF(m_object);
94     }
95 }
96
97 cv::detail::PyObjectHolder::Impl::~Impl()
98 {
99     // NB: If NULL was set, don't decrease counter.
100     if (m_object)
101     {
102         Py_DECREF(m_object);
103     }
104 }
105
106 PyObject* cv::detail::PyObjectHolder::Impl::get() const
107 {
108     return m_object;
109 }
110
111 cv::detail::PyObjectHolder::PyObjectHolder(PyObject* object, bool owner)
112         : m_impl(new cv::detail::PyObjectHolder::Impl{object, owner})
113 {
114 }
115
116 PyObject* cv::detail::PyObjectHolder::get() const
117 {
118     return m_impl->get();
119 }
120
121 template<>
122 PyObject* pyopencv_from(const cv::detail::PyObjectHolder& v)
123 {
124     PyObject* o = cv::util::any_cast<cv::detail::PyObjectHolder>(v).get();
125     Py_INCREF(o);
126     return o;
127 }
128
129 // #FIXME: Is it possible to implement pyopencv_from/pyopencv_to for generic
130 // cv::variant<Types...> ?
131 template <>
132 PyObject* pyopencv_from(const cv::gapi::wip::draw::Prim& prim)
133 {
134     switch (prim.index())
135     {
136         case cv::gapi::wip::draw::Prim::index_of<cv::gapi::wip::draw::Rect>():
137             return pyopencv_from(cv::util::get<cv::gapi::wip::draw::Rect>(prim));
138         case cv::gapi::wip::draw::Prim::index_of<cv::gapi::wip::draw::Text>():
139             return pyopencv_from(cv::util::get<cv::gapi::wip::draw::Text>(prim));
140         case cv::gapi::wip::draw::Prim::index_of<cv::gapi::wip::draw::Circle>():
141             return pyopencv_from(cv::util::get<cv::gapi::wip::draw::Circle>(prim));
142         case cv::gapi::wip::draw::Prim::index_of<cv::gapi::wip::draw::Line>():
143             return pyopencv_from(cv::util::get<cv::gapi::wip::draw::Line>(prim));
144         case cv::gapi::wip::draw::Prim::index_of<cv::gapi::wip::draw::Poly>():
145             return pyopencv_from(cv::util::get<cv::gapi::wip::draw::Poly>(prim));
146         case cv::gapi::wip::draw::Prim::index_of<cv::gapi::wip::draw::Mosaic>():
147             return pyopencv_from(cv::util::get<cv::gapi::wip::draw::Mosaic>(prim));
148         case cv::gapi::wip::draw::Prim::index_of<cv::gapi::wip::draw::Image>():
149             return pyopencv_from(cv::util::get<cv::gapi::wip::draw::Image>(prim));
150     }
151
152     util::throw_error(std::logic_error("Unsupported draw primitive type"));
153 }
154
155 template <>
156 PyObject* pyopencv_from(const cv::gapi::wip::draw::Prims& value)
157 {
158     return pyopencv_from_generic_vec(value);
159 }
160
161 template<>
162 bool pyopencv_to(PyObject* obj, cv::gapi::wip::draw::Prim& value, const ArgInfo& info)
163 {
164 #define TRY_EXTRACT(Prim)                                                                                  \
165     if (PyObject_TypeCheck(obj, reinterpret_cast<PyTypeObject*>(pyopencv_gapi_wip_draw_##Prim##_TypePtr))) \
166     {                                                                                                      \
167         value = reinterpret_cast<pyopencv_gapi_wip_draw_##Prim##_t*>(obj)->v;                              \
168         return true;                                                                                       \
169     }                                                                                                      \
170
171     TRY_EXTRACT(Rect)
172     TRY_EXTRACT(Text)
173     TRY_EXTRACT(Circle)
174     TRY_EXTRACT(Line)
175     TRY_EXTRACT(Mosaic)
176     TRY_EXTRACT(Image)
177     TRY_EXTRACT(Poly)
178
179     failmsg("Unsupported primitive type");
180     return false;
181 }
182
183 template <>
184 bool pyopencv_to(PyObject* obj, cv::gapi::wip::draw::Prims& value, const ArgInfo& info)
185 {
186     return pyopencv_to_generic_vec(obj, value, info);
187 }
188
189 template<>
190 PyObject* pyopencv_from(const cv::GArg& value)
191 {
192     GAPI_Assert(value.kind != cv::detail::ArgKind::GOBJREF);
193 #define HANDLE_CASE(T, O) case cv::detail::OpaqueKind::CV_##T:  \
194     {                                                           \
195         return pyopencv_from(value.get<O>());                   \
196     }
197
198 #define UNSUPPORTED(T) case cv::detail::OpaqueKind::CV_##T: break
199     switch (value.opaque_kind)
200     {
201         HANDLE_CASE(BOOL,      bool);
202         HANDLE_CASE(INT,       int);
203         HANDLE_CASE(INT64,   int64_t);
204         HANDLE_CASE(DOUBLE,    double);
205         HANDLE_CASE(FLOAT,     float);
206         HANDLE_CASE(STRING,    std::string);
207         HANDLE_CASE(POINT,     cv::Point);
208         HANDLE_CASE(POINT2F,   cv::Point2f);
209         HANDLE_CASE(SIZE,      cv::Size);
210         HANDLE_CASE(RECT,      cv::Rect);
211         HANDLE_CASE(SCALAR,    cv::Scalar);
212         HANDLE_CASE(MAT,       cv::Mat);
213         HANDLE_CASE(UNKNOWN,   cv::detail::PyObjectHolder);
214         HANDLE_CASE(DRAW_PRIM, cv::gapi::wip::draw::Prim);
215         UNSUPPORTED(UINT64);
216 #undef HANDLE_CASE
217 #undef UNSUPPORTED
218     }
219     util::throw_error(std::logic_error("Unsupported kernel input type"));
220 }
221
222 template<>
223 bool pyopencv_to(PyObject* obj, cv::GArg& value, const ArgInfo& info)
224 {
225     value = cv::GArg(cv::detail::PyObjectHolder(obj));
226     return true;
227 }
228
229 template <>
230 bool pyopencv_to(PyObject* obj, std::vector<cv::gapi::GNetParam>& value, const ArgInfo& info)
231 {
232     return pyopencv_to_generic_vec(obj, value, info);
233 }
234
235 template <>
236 PyObject* pyopencv_from(const std::vector<cv::gapi::GNetParam>& value)
237 {
238     return pyopencv_from_generic_vec(value);
239 }
240
241 template <>
242 bool pyopencv_to(PyObject* obj, std::vector<GCompileArg>& value, const ArgInfo& info)
243 {
244     return pyopencv_to_generic_vec(obj, value, info);
245 }
246
247 template <>
248 PyObject* pyopencv_from(const std::vector<GCompileArg>& value)
249 {
250     return pyopencv_from_generic_vec(value);
251 }
252
253 template<>
254 PyObject* pyopencv_from(const cv::detail::OpaqueRef& o)
255 {
256     switch (o.getKind())
257     {
258         case cv::detail::OpaqueKind::CV_BOOL      : return pyopencv_from(o.rref<bool>());
259         case cv::detail::OpaqueKind::CV_INT       : return pyopencv_from(o.rref<int>());
260         case cv::detail::OpaqueKind::CV_INT64     : return pyopencv_from(o.rref<int64_t>());
261         case cv::detail::OpaqueKind::CV_DOUBLE    : return pyopencv_from(o.rref<double>());
262         case cv::detail::OpaqueKind::CV_FLOAT     : return pyopencv_from(o.rref<float>());
263         case cv::detail::OpaqueKind::CV_STRING    : return pyopencv_from(o.rref<std::string>());
264         case cv::detail::OpaqueKind::CV_POINT     : return pyopencv_from(o.rref<cv::Point>());
265         case cv::detail::OpaqueKind::CV_POINT2F   : return pyopencv_from(o.rref<cv::Point2f>());
266         case cv::detail::OpaqueKind::CV_SIZE      : return pyopencv_from(o.rref<cv::Size>());
267         case cv::detail::OpaqueKind::CV_RECT      : return pyopencv_from(o.rref<cv::Rect>());
268         case cv::detail::OpaqueKind::CV_UNKNOWN   : return pyopencv_from(o.rref<cv::GArg>());
269         case cv::detail::OpaqueKind::CV_DRAW_PRIM : return pyopencv_from(o.rref<cv::gapi::wip::draw::Prim>());
270         case cv::detail::OpaqueKind::CV_UINT64    : break;
271         case cv::detail::OpaqueKind::CV_SCALAR    : break;
272         case cv::detail::OpaqueKind::CV_MAT       : break;
273     }
274
275     PyErr_SetString(PyExc_TypeError, "Unsupported GOpaque type");
276     return NULL;
277 };
278
279 template <>
280 PyObject* pyopencv_from(const cv::detail::VectorRef& v)
281 {
282     switch (v.getKind())
283     {
284         case cv::detail::OpaqueKind::CV_BOOL      : return pyopencv_from_generic_vec(v.rref<bool>());
285         case cv::detail::OpaqueKind::CV_INT       : return pyopencv_from_generic_vec(v.rref<int>());
286         case cv::detail::OpaqueKind::CV_INT64     : return pyopencv_from_generic_vec(v.rref<int64_t>());
287         case cv::detail::OpaqueKind::CV_DOUBLE    : return pyopencv_from_generic_vec(v.rref<double>());
288         case cv::detail::OpaqueKind::CV_FLOAT     : return pyopencv_from_generic_vec(v.rref<float>());
289         case cv::detail::OpaqueKind::CV_STRING    : return pyopencv_from_generic_vec(v.rref<std::string>());
290         case cv::detail::OpaqueKind::CV_POINT     : return pyopencv_from_generic_vec(v.rref<cv::Point>());
291         case cv::detail::OpaqueKind::CV_POINT2F   : return pyopencv_from_generic_vec(v.rref<cv::Point2f>());
292         case cv::detail::OpaqueKind::CV_SIZE      : return pyopencv_from_generic_vec(v.rref<cv::Size>());
293         case cv::detail::OpaqueKind::CV_RECT      : return pyopencv_from_generic_vec(v.rref<cv::Rect>());
294         case cv::detail::OpaqueKind::CV_SCALAR    : return pyopencv_from_generic_vec(v.rref<cv::Scalar>());
295         case cv::detail::OpaqueKind::CV_MAT       : return pyopencv_from_generic_vec(v.rref<cv::Mat>());
296         case cv::detail::OpaqueKind::CV_UNKNOWN   : return pyopencv_from_generic_vec(v.rref<cv::GArg>());
297         case cv::detail::OpaqueKind::CV_DRAW_PRIM : return pyopencv_from_generic_vec(v.rref<cv::gapi::wip::draw::Prim>());
298         case cv::detail::OpaqueKind::CV_UINT64    : break;
299     }
300
301     PyErr_SetString(PyExc_TypeError, "Unsupported GArray type");
302     return NULL;
303 }
304
305 template <>
306 PyObject* pyopencv_from(const GRunArg& v)
307 {
308     switch (v.index())
309     {
310         case GRunArg::index_of<cv::Mat>():
311             return pyopencv_from(util::get<cv::Mat>(v));
312
313         case GRunArg::index_of<cv::Scalar>():
314             return pyopencv_from(util::get<cv::Scalar>(v));
315
316         case GRunArg::index_of<cv::detail::VectorRef>():
317             return pyopencv_from(util::get<cv::detail::VectorRef>(v));
318
319         case GRunArg::index_of<cv::detail::OpaqueRef>():
320             return pyopencv_from(util::get<cv::detail::OpaqueRef>(v));
321     }
322
323     PyErr_SetString(PyExc_TypeError, "Failed to unpack GRunArgs. Index of variant is unknown");
324     return NULL;
325 }
326
327 template <typename T>
328 PyObject* pyopencv_from(const cv::optional<T>& opt)
329 {
330     if (!opt.has_value())
331     {
332         Py_RETURN_NONE;
333     }
334     return pyopencv_from(*opt);
335 }
336
337 template <>
338 PyObject* pyopencv_from(const GOptRunArg& v)
339 {
340     switch (v.index())
341     {
342         case GOptRunArg::index_of<cv::optional<cv::Mat>>():
343             return pyopencv_from(util::get<cv::optional<cv::Mat>>(v));
344
345         case GOptRunArg::index_of<cv::optional<cv::Scalar>>():
346             return pyopencv_from(util::get<cv::optional<cv::Scalar>>(v));
347
348         case GOptRunArg::index_of<optional<cv::detail::VectorRef>>():
349             return pyopencv_from(util::get<optional<cv::detail::VectorRef>>(v));
350
351         case GOptRunArg::index_of<optional<cv::detail::OpaqueRef>>():
352             return pyopencv_from(util::get<optional<cv::detail::OpaqueRef>>(v));
353     }
354
355     PyErr_SetString(PyExc_TypeError, "Failed to unpack GOptRunArg. Index of variant is unknown");
356     return NULL;
357 }
358
359 template<>
360 PyObject* pyopencv_from(const GRunArgs& value)
361 {
362      return value.size() == 1 ? pyopencv_from(value[0]) : pyopencv_from_generic_vec(value);
363 }
364
365 template<>
366 PyObject* pyopencv_from(const GOptRunArgs& value)
367 {
368     return value.size() == 1 ? pyopencv_from(value[0]) : pyopencv_from_generic_vec(value);
369 }
370
371 // FIXME: cv::variant should be wrapped once for all types.
372 template <>
373 PyObject* pyopencv_from(const cv::util::variant<cv::GRunArgs, cv::GOptRunArgs>& v)
374 {
375     using RunArgs = cv::util::variant<cv::GRunArgs, cv::GOptRunArgs>;
376     switch (v.index())
377     {
378         case RunArgs::index_of<cv::GRunArgs>():
379             return pyopencv_from(util::get<cv::GRunArgs>(v));
380         case RunArgs::index_of<cv::GOptRunArgs>():
381             return pyopencv_from(util::get<cv::GOptRunArgs>(v));
382     }
383
384     PyErr_SetString(PyExc_TypeError, "Failed to recognize kind of RunArgs. Index of variant is unknown");
385     return NULL;
386 }
387
388 template <typename T>
389 void pyopencv_to_with_check(PyObject* from, T& to, const std::string& msg = "")
390 {
391     if (!pyopencv_to(from, to, ArgInfo("", false)))
392     {
393         cv::util::throw_error(std::logic_error(msg));
394     }
395 }
396
397 template <typename T>
398 void pyopencv_to_generic_vec_with_check(PyObject* from,
399                                         std::vector<T>& to,
400                                         const std::string& msg = "")
401 {
402     if (!pyopencv_to_generic_vec(from, to, ArgInfo("", false)))
403     {
404         cv::util::throw_error(std::logic_error(msg));
405     }
406 }
407
408 template <typename T>
409 static T extract_proto_args(PyObject* py_args)
410 {
411     using namespace cv;
412
413     GProtoArgs args;
414     Py_ssize_t size = PyList_Size(py_args);
415     args.reserve(size);
416     for (int i = 0; i < size; ++i)
417     {
418         PyObject* item = PyList_GetItem(py_args, i);
419         if (PyObject_TypeCheck(item, reinterpret_cast<PyTypeObject*>(pyopencv_GScalar_TypePtr)))
420         {
421             args.emplace_back(reinterpret_cast<pyopencv_GScalar_t*>(item)->v);
422         }
423         else if (PyObject_TypeCheck(item, reinterpret_cast<PyTypeObject*>(pyopencv_GMat_TypePtr)))
424         {
425             args.emplace_back(reinterpret_cast<pyopencv_GMat_t*>(item)->v);
426         }
427         else if (PyObject_TypeCheck(item, reinterpret_cast<PyTypeObject*>(pyopencv_GOpaqueT_TypePtr)))
428         {
429             args.emplace_back(reinterpret_cast<pyopencv_GOpaqueT_t*>(item)->v.strip());
430         }
431         else if (PyObject_TypeCheck(item, reinterpret_cast<PyTypeObject*>(pyopencv_GArrayT_TypePtr)))
432         {
433             args.emplace_back(reinterpret_cast<pyopencv_GArrayT_t*>(item)->v.strip());
434         }
435         else
436         {
437             util::throw_error(std::logic_error("Unsupported type for GProtoArgs"));
438         }
439     }
440
441     return T(std::move(args));
442 }
443
444 static cv::detail::OpaqueRef extract_opaque_ref(PyObject* from, cv::detail::OpaqueKind kind)
445 {
446 #define HANDLE_CASE(T, O) case cv::detail::OpaqueKind::CV_##T:  \
447 {                                                               \
448     O obj{};                                                    \
449     pyopencv_to_with_check(from, obj, "Failed to obtain " # O); \
450     return cv::detail::OpaqueRef{std::move(obj)};               \
451 }
452 #define UNSUPPORTED(T) case cv::detail::OpaqueKind::CV_##T: break
453     switch (kind)
454     {
455         HANDLE_CASE(BOOL,    bool);
456         HANDLE_CASE(INT,     int);
457         HANDLE_CASE(DOUBLE,  double);
458         HANDLE_CASE(FLOAT,   float);
459         HANDLE_CASE(STRING,  std::string);
460         HANDLE_CASE(POINT,   cv::Point);
461         HANDLE_CASE(POINT2F, cv::Point2f);
462         HANDLE_CASE(SIZE,    cv::Size);
463         HANDLE_CASE(RECT,    cv::Rect);
464         HANDLE_CASE(UNKNOWN, cv::GArg);
465         UNSUPPORTED(UINT64);
466         UNSUPPORTED(INT64);
467         UNSUPPORTED(SCALAR);
468         UNSUPPORTED(MAT);
469         UNSUPPORTED(DRAW_PRIM);
470 #undef HANDLE_CASE
471 #undef UNSUPPORTED
472     }
473     util::throw_error(std::logic_error("Unsupported type for GOpaqueT"));
474 }
475
476 static cv::detail::VectorRef extract_vector_ref(PyObject* from, cv::detail::OpaqueKind kind)
477 {
478 #define HANDLE_CASE(T, O) case cv::detail::OpaqueKind::CV_##T:                        \
479 {                                                                                     \
480     std::vector<O> obj;                                                               \
481     pyopencv_to_generic_vec_with_check(from, obj, "Failed to obtain vector of " # O); \
482     return cv::detail::VectorRef{std::move(obj)};                                     \
483 }
484 #define UNSUPPORTED(T) case cv::detail::OpaqueKind::CV_##T: break
485     switch (kind)
486     {
487         HANDLE_CASE(BOOL,      bool);
488         HANDLE_CASE(INT,       int);
489         HANDLE_CASE(DOUBLE,    double);
490         HANDLE_CASE(FLOAT,     float);
491         HANDLE_CASE(STRING,    std::string);
492         HANDLE_CASE(POINT,     cv::Point);
493         HANDLE_CASE(POINT2F,   cv::Point2f);
494         HANDLE_CASE(SIZE,      cv::Size);
495         HANDLE_CASE(RECT,      cv::Rect);
496         HANDLE_CASE(SCALAR,    cv::Scalar);
497         HANDLE_CASE(MAT,       cv::Mat);
498         HANDLE_CASE(UNKNOWN,   cv::GArg);
499         HANDLE_CASE(DRAW_PRIM, cv::gapi::wip::draw::Prim);
500         UNSUPPORTED(UINT64);
501         UNSUPPORTED(INT64);
502 #undef HANDLE_CASE
503 #undef UNSUPPORTED
504     }
505     util::throw_error(std::logic_error("Unsupported type for GArrayT"));
506 }
507
508 static cv::GRunArg extract_run_arg(const cv::GTypeInfo& info, PyObject* item)
509 {
510     switch (info.shape)
511     {
512         case cv::GShape::GMAT:
513         {
514             // NB: In case streaming it can be IStreamSource or cv::Mat
515             if (PyObject_TypeCheck(item,
516                         reinterpret_cast<PyTypeObject*>(pyopencv_gapi_wip_IStreamSource_TypePtr)))
517             {
518                 cv::gapi::wip::IStreamSource::Ptr source =
519                     reinterpret_cast<pyopencv_gapi_wip_IStreamSource_t*>(item)->v;
520                 return source;
521             }
522             cv::Mat obj;
523             pyopencv_to_with_check(item, obj, "Failed to obtain cv::Mat");
524             return obj;
525         }
526         case cv::GShape::GSCALAR:
527         {
528             cv::Scalar obj;
529             pyopencv_to_with_check(item, obj, "Failed to obtain cv::Scalar");
530             return obj;
531         }
532         case cv::GShape::GOPAQUE:
533         {
534             return extract_opaque_ref(item, info.kind);
535         }
536         case cv::GShape::GARRAY:
537         {
538             return extract_vector_ref(item, info.kind);
539         }
540         case cv::GShape::GFRAME:
541         {
542             // NB: Isn't supported yet.
543             break;
544         }
545     }
546
547     util::throw_error(std::logic_error("Unsupported output shape"));
548 }
549
550 static cv::GRunArgs extract_run_args(const cv::GTypesInfo& info, PyObject* py_args)
551 {
552     GAPI_Assert(PyList_Check(py_args));
553
554     cv::GRunArgs args;
555     Py_ssize_t list_size = PyList_Size(py_args);
556     args.reserve(list_size);
557
558     for (int i = 0; i < list_size; ++i)
559     {
560         args.push_back(extract_run_arg(info[i], PyList_GetItem(py_args, i)));
561     }
562
563     return args;
564 }
565
566 static cv::GMetaArg extract_meta_arg(const cv::GTypeInfo& info, PyObject* item)
567 {
568     switch (info.shape)
569     {
570         case cv::GShape::GMAT:
571         {
572             cv::Mat obj;
573             pyopencv_to_with_check(item, obj, "Failed to obtain cv::Mat");
574             return cv::GMetaArg{cv::descr_of(obj)};
575         }
576         case cv::GShape::GSCALAR:
577         {
578             cv::Scalar obj;
579             pyopencv_to_with_check(item, obj, "Failed to obtain cv::Scalar");
580             return cv::GMetaArg{cv::descr_of(obj)};
581         }
582         case cv::GShape::GARRAY:
583         {
584             return cv::GMetaArg{cv::empty_array_desc()};
585         }
586         case cv::GShape::GOPAQUE:
587         {
588             return cv::GMetaArg{cv::empty_gopaque_desc()};
589         }
590         case cv::GShape::GFRAME:
591         {
592             // NB: Isn't supported yet.
593             break;
594         }
595     }
596     util::throw_error(std::logic_error("Unsupported output shape"));
597 }
598
599 static cv::GMetaArgs extract_meta_args(const cv::GTypesInfo& info, PyObject* py_args)
600 {
601     GAPI_Assert(PyList_Check(py_args));
602
603     cv::GMetaArgs metas;
604     Py_ssize_t list_size = PyList_Size(py_args);
605     metas.reserve(list_size);
606
607     for (int i = 0; i < list_size; ++i)
608     {
609         metas.push_back(extract_meta_arg(info[i], PyList_GetItem(py_args, i)));
610     }
611
612     return metas;
613 }
614
615 static cv::GRunArgs run_py_kernel(cv::detail::PyObjectHolder kernel,
616                                   const cv::gapi::python::GPythonContext &ctx)
617 {
618     const auto& ins      = ctx.ins;
619     const auto& in_metas = ctx.in_metas;
620     const auto& out_info = ctx.out_info;
621
622     PyGILState_STATE gstate;
623     gstate = PyGILState_Ensure();
624
625     cv::GRunArgs outs;
626     try
627     {
628         int in_idx = 0;
629         // NB: Doesn't increase reference counter (false),
630         // because PyObject already have ownership.
631         // In case exception decrement reference counter.
632         cv::detail::PyObjectHolder args(PyTuple_New(ins.size()), false);
633         for (size_t i = 0; i < ins.size(); ++i)
634         {
635             // NB: If meta is monostate then object isn't associated with G-TYPE.
636             if (cv::util::holds_alternative<cv::util::monostate>(in_metas[i]))
637             {
638                 PyTuple_SetItem(args.get(), i, pyopencv_from(ins[i]));
639                 continue;
640             }
641
642             switch (in_metas[i].index())
643             {
644                 case cv::GMetaArg::index_of<cv::GMatDesc>():
645                     PyTuple_SetItem(args.get(), i, pyopencv_from(ins[i].get<cv::Mat>()));
646                     break;
647                 case cv::GMetaArg::index_of<cv::GScalarDesc>():
648                     PyTuple_SetItem(args.get(), i, pyopencv_from(ins[i].get<cv::Scalar>()));
649                     break;
650                 case cv::GMetaArg::index_of<cv::GOpaqueDesc>():
651                     PyTuple_SetItem(args.get(), i, pyopencv_from(ins[i].get<cv::detail::OpaqueRef>()));
652                     break;
653                 case cv::GMetaArg::index_of<cv::GArrayDesc>():
654                     PyTuple_SetItem(args.get(), i, pyopencv_from(ins[i].get<cv::detail::VectorRef>()));
655                     break;
656                 case cv::GMetaArg::index_of<cv::GFrameDesc>():
657                     util::throw_error(std::logic_error("GFrame isn't supported for custom operation"));
658                     break;
659             }
660             ++in_idx;
661         }
662         // NB: Doesn't increase reference counter (false).
663         // In case PyObject_CallObject return NULL, do nothing in destructor.
664         cv::detail::PyObjectHolder result(
665                 PyObject_CallObject(kernel.get(), args.get()), false);
666
667         if (PyErr_Occurred())
668         {
669             PyErr_PrintEx(0);
670             PyErr_Clear();
671             throw std::logic_error("Python kernel failed with error!");
672         }
673         // NB: In fact it's impossible situation, becase errors were handled above.
674         GAPI_Assert(result.get() && "Python kernel returned NULL!");
675
676         if (out_info.size() == 1)
677         {
678             outs = cv::GRunArgs{extract_run_arg(out_info[0], result.get())};
679         }
680         else if (out_info.size() > 1)
681         {
682             GAPI_Assert(PyTuple_Check(result.get()));
683
684             Py_ssize_t tuple_size = PyTuple_Size(result.get());
685             outs.reserve(tuple_size);
686
687             for (int i = 0; i < tuple_size; ++i)
688             {
689                 outs.push_back(extract_run_arg(out_info[i], PyTuple_GetItem(result.get(), i)));
690             }
691         }
692         else
693         {
694             // Seems to be impossible case.
695             GAPI_Assert(false);
696         }
697     }
698     catch (...)
699     {
700         PyGILState_Release(gstate);
701         throw;
702     }
703     PyGILState_Release(gstate);
704
705     return outs;
706 }
707
708 static GMetaArg get_meta_arg(PyObject* obj)
709 {
710     if (PyObject_TypeCheck(obj,
711                 reinterpret_cast<PyTypeObject*>(pyopencv_GMatDesc_TypePtr)))
712     {
713         return cv::GMetaArg{reinterpret_cast<pyopencv_GMatDesc_t*>(obj)->v};
714     }
715     else if (PyObject_TypeCheck(obj,
716                 reinterpret_cast<PyTypeObject*>(pyopencv_GScalarDesc_TypePtr)))
717     {
718         return cv::GMetaArg{reinterpret_cast<pyopencv_GScalarDesc_t*>(obj)->v};
719     }
720     else if (PyObject_TypeCheck(obj,
721                 reinterpret_cast<PyTypeObject*>(pyopencv_GArrayDesc_TypePtr)))
722     {
723         return cv::GMetaArg{reinterpret_cast<pyopencv_GArrayDesc_t*>(obj)->v};
724     }
725     else if (PyObject_TypeCheck(obj,
726                 reinterpret_cast<PyTypeObject*>(pyopencv_GOpaqueDesc_TypePtr)))
727     {
728         return cv::GMetaArg{reinterpret_cast<pyopencv_GOpaqueDesc_t*>(obj)->v};
729     }
730     else
731     {
732         util::throw_error(std::logic_error("Unsupported output meta type"));
733     }
734 }
735
736 static cv::GMetaArgs get_meta_args(PyObject* tuple)
737 {
738     size_t size = PyTuple_Size(tuple);
739
740     cv::GMetaArgs metas;
741     metas.reserve(size);
742     for (size_t i = 0; i < size; ++i)
743     {
744         metas.push_back(get_meta_arg(PyTuple_GetItem(tuple, i)));
745     }
746
747     return metas;
748 }
749
750 static GMetaArgs run_py_meta(cv::detail::PyObjectHolder out_meta,
751                              const cv::GMetaArgs         &meta,
752                              const cv::GArgs             &gargs)
753 {
754     PyGILState_STATE gstate;
755     gstate = PyGILState_Ensure();
756
757     cv::GMetaArgs out_metas;
758     try
759     {
760         // NB: Doesn't increase reference counter (false),
761         // because PyObject already have ownership.
762         // In case exception decrement reference counter.
763         cv::detail::PyObjectHolder args(PyTuple_New(meta.size()), false);
764         size_t idx = 0;
765         for (auto&& m : meta)
766         {
767             switch (m.index())
768             {
769                 case cv::GMetaArg::index_of<cv::GMatDesc>():
770                     PyTuple_SetItem(args.get(), idx, pyopencv_from(cv::util::get<cv::GMatDesc>(m)));
771                     break;
772                 case cv::GMetaArg::index_of<cv::GScalarDesc>():
773                     PyTuple_SetItem(args.get(), idx, pyopencv_from(cv::util::get<cv::GScalarDesc>(m)));
774                     break;
775                 case cv::GMetaArg::index_of<cv::GArrayDesc>():
776                     PyTuple_SetItem(args.get(), idx, pyopencv_from(cv::util::get<cv::GArrayDesc>(m)));
777                     break;
778                 case cv::GMetaArg::index_of<cv::GOpaqueDesc>():
779                     PyTuple_SetItem(args.get(), idx, pyopencv_from(cv::util::get<cv::GOpaqueDesc>(m)));
780                     break;
781                 case cv::GMetaArg::index_of<cv::util::monostate>():
782                     PyTuple_SetItem(args.get(), idx, pyopencv_from(gargs[idx]));
783                     break;
784                 case cv::GMetaArg::index_of<cv::GFrameDesc>():
785                     util::throw_error(std::logic_error("GFrame isn't supported for custom operation"));
786                     break;
787             }
788             ++idx;
789         }
790         // NB: Doesn't increase reference counter (false).
791         // In case PyObject_CallObject return NULL, do nothing in destructor.
792         cv::detail::PyObjectHolder result(
793                 PyObject_CallObject(out_meta.get(), args.get()), false);
794
795         if (PyErr_Occurred())
796         {
797             PyErr_PrintEx(0);
798             PyErr_Clear();
799             throw std::logic_error("Python outMeta failed with error!");
800         }
801         // NB: In fact it's impossible situation, becase errors were handled above.
802         GAPI_Assert(result.get() && "Python outMeta returned NULL!");
803
804         out_metas = PyTuple_Check(result.get()) ? get_meta_args(result.get())
805                                                 : cv::GMetaArgs{get_meta_arg(result.get())};
806     }
807     catch (...)
808     {
809         PyGILState_Release(gstate);
810         throw;
811     }
812     PyGILState_Release(gstate);
813
814     return out_metas;
815 }
816
817 static PyObject* pyopencv_cv_gapi_kernels(PyObject* , PyObject* py_args, PyObject*)
818 {
819     using namespace cv;
820     gapi::GKernelPackage pkg;
821     Py_ssize_t size = PyTuple_Size(py_args);
822
823     for (int i = 0; i < size; ++i)
824     {
825         PyObject* user_kernel = PyTuple_GetItem(py_args, i);
826
827         PyObject* id_obj = PyObject_GetAttrString(user_kernel, "id");
828         if (!id_obj)
829         {
830             PyErr_SetString(PyExc_TypeError,
831                     "Python kernel should contain id, please use cv.gapi.kernel to define kernel");
832             return NULL;
833         }
834
835         PyObject* out_meta = PyObject_GetAttrString(user_kernel, "outMeta");
836         if (!out_meta)
837         {
838             PyErr_SetString(PyExc_TypeError,
839                     "Python kernel should contain outMeta, please use cv.gapi.kernel to define kernel");
840             return NULL;
841         }
842
843         PyObject* run  = PyObject_GetAttrString(user_kernel, "run");
844         if (!run)
845         {
846             PyErr_SetString(PyExc_TypeError,
847                     "Python kernel should contain run, please use cv.gapi.kernel to define kernel");
848             return NULL;
849         }
850
851         std::string id;
852         if (!pyopencv_to(id_obj, id, ArgInfo("id", false)))
853         {
854             PyErr_SetString(PyExc_TypeError, "Failed to obtain string");
855             return NULL;
856         }
857
858         using namespace std::placeholders;
859         gapi::python::GPythonFunctor f(id.c_str(),
860                 std::bind(run_py_meta  , cv::detail::PyObjectHolder{out_meta}, _1, _2),
861                 std::bind(run_py_kernel, cv::detail::PyObjectHolder{run}    , _1));
862         pkg.include(f);
863     }
864     return pyopencv_from(pkg);
865 }
866
867 static PyObject* pyopencv_cv_gapi_op(PyObject* , PyObject* py_args, PyObject*)
868 {
869     using namespace cv;
870     Py_ssize_t size = PyTuple_Size(py_args);
871     std::string id;
872     if (!pyopencv_to(PyTuple_GetItem(py_args, 0), id, ArgInfo("id", false)))
873     {
874         PyErr_SetString(PyExc_TypeError, "Failed to obtain: operation id must be a string");
875         return NULL;
876     }
877     PyObject* outMeta = PyTuple_GetItem(py_args, 1);
878
879     cv::GArgs args;
880     for (int i = 2; i < size; i++)
881     {
882         PyObject* item = PyTuple_GetItem(py_args, i);
883         if (PyObject_TypeCheck(item,
884                     reinterpret_cast<PyTypeObject*>(pyopencv_GMat_TypePtr)))
885         {
886             args.emplace_back(reinterpret_cast<pyopencv_GMat_t*>(item)->v);
887         }
888         else if (PyObject_TypeCheck(item,
889                            reinterpret_cast<PyTypeObject*>(pyopencv_GScalar_TypePtr)))
890         {
891             args.emplace_back(reinterpret_cast<pyopencv_GScalar_t*>(item)->v);
892         }
893         else if (PyObject_TypeCheck(item,
894                            reinterpret_cast<PyTypeObject*>(pyopencv_GOpaqueT_TypePtr)))
895         {
896             auto&& arg = reinterpret_cast<pyopencv_GOpaqueT_t*>(item)->v.arg();
897 #define HC(T, K) case cv::GOpaqueT::Storage:: index_of<cv::GOpaque<T>>(): \
898             args.emplace_back(cv::util::get<cv::GOpaque<T>>(arg));        \
899             break;                                                        \
900
901             SWITCH(arg.index(), GOPAQUE_TYPE_LIST_G, HC)
902 #undef HC
903         }
904         else if (PyObject_TypeCheck(item,
905                            reinterpret_cast<PyTypeObject*>(pyopencv_GArrayT_TypePtr)))
906         {
907             auto&& arg = reinterpret_cast<pyopencv_GArrayT_t*>(item)->v.arg();
908 #define HC(T, K) case cv::GArrayT::Storage:: index_of<cv::GArray<T>>(): \
909             args.emplace_back(cv::util::get<cv::GArray<T>>(arg));       \
910             break;                                                      \
911
912             SWITCH(arg.index(), GARRAY_TYPE_LIST_G, HC)
913 #undef HC
914         }
915         else
916         {
917             args.emplace_back(cv::GArg(cv::detail::PyObjectHolder{item}));
918         }
919     }
920
921     cv::GKernel::M outMetaWrapper = std::bind(run_py_meta,
922                                               cv::detail::PyObjectHolder{outMeta},
923                                               std::placeholders::_1,
924                                               std::placeholders::_2);
925     return pyopencv_from(cv::gapi::wip::op(id, outMetaWrapper, std::move(args)));
926 }
927
928 template<>
929 bool pyopencv_to(PyObject* obj, cv::detail::ExtractArgsCallback& value, const ArgInfo&)
930 {
931     cv::detail::PyObjectHolder holder{obj};
932     value = cv::detail::ExtractArgsCallback{[=](const cv::GTypesInfo& info)
933     {
934         PyGILState_STATE gstate;
935         gstate = PyGILState_Ensure();
936
937         cv::GRunArgs args;
938         try
939         {
940             args = extract_run_args(info, holder.get());
941         }
942         catch (...)
943         {
944             PyGILState_Release(gstate);
945             throw;
946         }
947         PyGILState_Release(gstate);
948         return args;
949     }};
950     return true;
951 }
952
953 template<>
954 bool pyopencv_to(PyObject* obj, cv::detail::ExtractMetaCallback& value, const ArgInfo&)
955 {
956     cv::detail::PyObjectHolder holder{obj};
957     value = cv::detail::ExtractMetaCallback{[=](const cv::GTypesInfo& info)
958     {
959         PyGILState_STATE gstate;
960         gstate = PyGILState_Ensure();
961
962         cv::GMetaArgs args;
963         try
964         {
965             args = extract_meta_args(info, holder.get());
966         }
967         catch (...)
968         {
969             PyGILState_Release(gstate);
970             throw;
971         }
972         PyGILState_Release(gstate);
973         return args;
974     }};
975     return true;
976 }
977
978 template<typename T>
979 struct PyOpenCV_Converter<cv::GArray<T>>
980 {
981     static PyObject* from(const cv::GArray<T>& p)
982     {
983         return pyopencv_from(cv::GArrayT(p));
984     }
985     static bool to(PyObject *obj, cv::GArray<T>& value, const ArgInfo& info)
986     {
987         if (PyObject_TypeCheck(obj, reinterpret_cast<PyTypeObject*>(pyopencv_GArrayT_TypePtr)))
988         {
989             auto& array = reinterpret_cast<pyopencv_GArrayT_t*>(obj)->v;
990             try
991             {
992                 value = cv::util::get<cv::GArray<T>>(array.arg());
993             }
994             catch (...)
995             {
996                 return false;
997             }
998             return true;
999         }
1000         return false;
1001     }
1002 };
1003
1004 template<typename T>
1005 struct PyOpenCV_Converter<cv::GOpaque<T>>
1006 {
1007     static PyObject* from(const cv::GOpaque<T>& p)
1008     {
1009         return pyopencv_from(cv::GOpaqueT(p));
1010     }
1011     static bool to(PyObject *obj, cv::GOpaque<T>& value, const ArgInfo& info)
1012     {
1013         if (PyObject_TypeCheck(obj, reinterpret_cast<PyTypeObject*>(pyopencv_GOpaqueT_TypePtr)))
1014         {
1015             auto& opaque = reinterpret_cast<pyopencv_GOpaqueT_t*>(obj)->v;
1016             try
1017             {
1018                 value = cv::util::get<cv::GOpaque<T>>(opaque.arg());
1019             }
1020             catch (...)
1021             {
1022                 return false;
1023             }
1024             return true;
1025         }
1026         return false;
1027     }
1028 };
1029
1030 template<>
1031 bool pyopencv_to(PyObject* obj, cv::GProtoInputArgs& value, const ArgInfo& info)
1032 {
1033     try
1034     {
1035         value = extract_proto_args<cv::GProtoInputArgs>(obj);
1036         return true;
1037     }
1038     catch (...)
1039     {
1040         failmsg("Can't parse cv::GProtoInputArgs");
1041         return false;
1042     }
1043 }
1044
1045 template<>
1046 bool pyopencv_to(PyObject* obj, cv::GProtoOutputArgs& value, const ArgInfo& info)
1047 {
1048     try
1049     {
1050         value = extract_proto_args<cv::GProtoOutputArgs>(obj);
1051         return true;
1052     }
1053     catch (...)
1054     {
1055         failmsg("Can't parse cv::GProtoOutputArgs");
1056         return false;
1057     }
1058 }
1059
1060 // extend cv.gapi methods
1061 #define PYOPENCV_EXTRA_METHODS_GAPI \
1062   {"kernels", CV_PY_FN_WITH_KW(pyopencv_cv_gapi_kernels), "kernels(...) -> GKernelPackage"}, \
1063   {"__op", CV_PY_FN_WITH_KW(pyopencv_cv_gapi_op), "__op(...) -> retval\n"},
1064
1065
1066 #endif  // HAVE_OPENCV_GAPI
1067 #endif  // OPENCV_GAPI_PYOPENCV_GAPI_HPP