gcpukernel.hpp 7.9 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267
  1. // This file is part of OpenCV project.
  2. // It is subject to the license terms in the LICENSE file found in the top-level directory
  3. // of this distribution and at http://opencv.org/license.html.
  4. //
  5. // Copyright (C) 2018 Intel Corporation
  6. #ifndef OPENCV_GAPI_GCPUKERNEL_HPP
  7. #define OPENCV_GAPI_GCPUKERNEL_HPP
  8. #include <functional>
  9. #include <unordered_map>
  10. #include <utility>
  11. #include <vector>
  12. #include <opencv2/core/mat.hpp>
  13. #include <opencv2/gapi/gcommon.hpp>
  14. #include <opencv2/gapi/gkernel.hpp>
  15. #include <opencv2/gapi/garg.hpp>
  16. #include <opencv2/gapi/own/convert.hpp> //to_ocv
  17. #include <opencv2/gapi/util/compiler_hints.hpp> //suppress_unused_warning
  18. // FIXME: namespace scheme for backends?
  19. namespace cv {
  20. namespace gimpl
  21. {
  22. // Forward-declare an internal class
  23. class GCPUExecutable;
  24. } // namespace gimpl
  25. namespace gapi
  26. {
  27. namespace cpu
  28. {
  29. /**
  30. * \addtogroup gapi_std_backends
  31. * @{
  32. *
  33. * @brief G-API backends available in this OpenCV version
  34. *
  35. * G-API backends play a corner stone role in G-API execution
  36. * stack. Every backend is hardware-oriented and thus can run its
  37. * kernels efficiently on the target platform.
  38. *
  39. * Backends are usually "back boxes" for G-API users -- on the API
  40. * side, all backends are represented as different objects of the
  41. * same class cv::gapi::GBackend. User can manipulate with backends
  42. * mainly by specifying which kernels to use or where to look up
  43. * for kernels first.
  44. *
  45. * @sa @ref gapi_hld, cv::gapi::lookup_order()
  46. */
  47. /**
  48. * @brief Get a reference to CPU (OpenCV) backend.
  49. *
  50. * This is the default backend in G-API at the moment, providing
  51. * broader functional coverage but losing some graph model
  52. * advantages. Provided mostly for reference and prototyping
  53. * purposes.
  54. *
  55. * @sa gapi_std_backends
  56. */
  57. GAPI_EXPORTS cv::gapi::GBackend backend();
  58. /** @} */
  59. } // namespace cpu
  60. } // namespace gapi
  61. // Represents arguments which are passed to a wrapped CPU function
  62. // FIXME: put into detail?
  63. class GAPI_EXPORTS GCPUContext
  64. {
  65. public:
  66. // Generic accessor API
  67. template<typename T>
  68. const T& inArg(int input) { return m_args.at(input).get<T>(); }
  69. // Syntax sugar
  70. const cv::gapi::own::Mat& inMat(int input);
  71. cv::gapi::own::Mat& outMatR(int output); // FIXME: Avoid cv::gapi::own::Mat m = ctx.outMatR()
  72. const cv::gapi::own::Scalar& inVal(int input);
  73. cv::gapi::own::Scalar& outValR(int output); // FIXME: Avoid cv::gapi::own::Scalar s = ctx.outValR()
  74. template<typename T> std::vector<T>& outVecR(int output) // FIXME: the same issue
  75. {
  76. return outVecRef(output).wref<T>();
  77. }
  78. protected:
  79. detail::VectorRef& outVecRef(int output);
  80. std::vector<GArg> m_args;
  81. //FIXME: avoid conversion of arguments from internal representaion to OpenCV one on each call
  82. //to OCV kernel. (This can be achieved by a two single time conversions in GCPUExecutable::run,
  83. //once on enter for input and output arguments, and once before return for output arguments only
  84. std::unordered_map<std::size_t, GRunArgP> m_results;
  85. friend class gimpl::GCPUExecutable;
  86. };
  87. class GAPI_EXPORTS GCPUKernel
  88. {
  89. public:
  90. // This function is kernel's execution entry point (does the processing work)
  91. using F = std::function<void(GCPUContext &)>;
  92. GCPUKernel();
  93. explicit GCPUKernel(const F& f);
  94. void apply(GCPUContext &ctx);
  95. protected:
  96. F m_f;
  97. };
  98. // FIXME: This is an ugly ad-hoc imlpementation. TODO: refactor
  99. namespace detail
  100. {
  101. template<class T> struct get_in;
  102. template<> struct get_in<cv::GMat>
  103. {
  104. static cv::Mat get(GCPUContext &ctx, int idx) { return to_ocv(ctx.inMat(idx)); }
  105. };
  106. template<> struct get_in<cv::GScalar>
  107. {
  108. static cv::Scalar get(GCPUContext &ctx, int idx) { return to_ocv(ctx.inVal(idx)); }
  109. };
  110. template<typename U> struct get_in<cv::GArray<U> >
  111. {
  112. static const std::vector<U>& get(GCPUContext &ctx, int idx) { return ctx.inArg<VectorRef>(idx).rref<U>(); }
  113. };
  114. template<class T> struct get_in
  115. {
  116. static T get(GCPUContext &ctx, int idx) { return ctx.inArg<T>(idx); }
  117. };
  118. struct tracked_cv_mat{
  119. tracked_cv_mat(cv::gapi::own::Mat& m) : r{to_ocv(m)}, original_data{m.data} {}
  120. cv::Mat r;
  121. uchar* original_data;
  122. operator cv::Mat& (){ return r;}
  123. void validate() const{
  124. if (r.data != original_data)
  125. {
  126. util::throw_error
  127. (std::logic_error
  128. ("OpenCV kernel output parameter was reallocated. \n"
  129. "Incorrect meta data was provided ?"));
  130. }
  131. }
  132. };
  133. struct scalar_wrapper
  134. {
  135. scalar_wrapper(cv::gapi::own::Scalar& s) : m_s{cv::gapi::own::to_ocv(s)}, m_org_s(s) {};
  136. operator cv::Scalar& () { return m_s; }
  137. void writeBack() const { m_org_s = to_own(m_s); }
  138. cv::Scalar m_s;
  139. cv::gapi::own::Scalar& m_org_s;
  140. };
  141. template<typename... Outputs>
  142. void postprocess(Outputs&... outs)
  143. {
  144. struct
  145. {
  146. void operator()(tracked_cv_mat* bm) { bm->validate(); }
  147. void operator()(scalar_wrapper* sw) { sw->writeBack(); }
  148. void operator()(...) { }
  149. } validate;
  150. //dummy array to unfold parameter pack
  151. int dummy[] = { 0, (validate(&outs), 0)... };
  152. cv::util::suppress_unused_warning(dummy);
  153. }
  154. template<class T> struct get_out;
  155. template<> struct get_out<cv::GMat>
  156. {
  157. static tracked_cv_mat get(GCPUContext &ctx, int idx)
  158. {
  159. auto& r = ctx.outMatR(idx);
  160. return {r};
  161. }
  162. };
  163. template<> struct get_out<cv::GScalar>
  164. {
  165. static scalar_wrapper get(GCPUContext &ctx, int idx)
  166. {
  167. auto& s = ctx.outValR(idx);
  168. return {s};
  169. }
  170. };
  171. template<typename U> struct get_out<cv::GArray<U>>
  172. {
  173. static std::vector<U>& get(GCPUContext &ctx, int idx)
  174. {
  175. return ctx.outVecR<U>(idx);
  176. }
  177. };
  178. template<typename, typename, typename>
  179. struct OCVCallHelper;
  180. // FIXME: probably can be simplified with std::apply or analogue.
  181. template<typename Impl, typename... Ins, typename... Outs>
  182. struct OCVCallHelper<Impl, std::tuple<Ins...>, std::tuple<Outs...> >
  183. {
  184. template<typename... Inputs>
  185. struct call_and_postprocess
  186. {
  187. template<typename... Outputs>
  188. static void call(Inputs&&... ins, Outputs&&... outs)
  189. {
  190. //not using a std::forward on outs is deliberate in order to
  191. //cause compilation error, by tring to bind rvalue references to lvalue references
  192. Impl::run(std::forward<Inputs>(ins)..., outs...);
  193. postprocess(outs...);
  194. }
  195. };
  196. template<int... IIs, int... OIs>
  197. static void call_impl(GCPUContext &ctx, detail::Seq<IIs...>, detail::Seq<OIs...>)
  198. {
  199. //Make sure that OpenCV kernels do not reallocate memory for output parameters
  200. //by comparing it's state (data ptr) before and after the call.
  201. //This is done by converting each output Mat into tracked_cv_mat object, and binding
  202. //them to parameters of ad-hoc function
  203. //Convert own::Scalar to cv::Scalar before call kernel and run kernel
  204. //convert cv::Scalar to own::Scalar after call kernel and write back results
  205. call_and_postprocess<decltype(get_in<Ins>::get(ctx, IIs))...>::call(get_in<Ins>::get(ctx, IIs)..., get_out<Outs>::get(ctx, OIs)...);
  206. }
  207. static void call(GCPUContext &ctx)
  208. {
  209. call_impl(ctx,
  210. typename detail::MkSeq<sizeof...(Ins)>::type(),
  211. typename detail::MkSeq<sizeof...(Outs)>::type());
  212. }
  213. };
  214. } // namespace detail
  215. template<class Impl, class K>
  216. class GCPUKernelImpl: public detail::OCVCallHelper<Impl, typename K::InArgs, typename K::OutArgs>
  217. {
  218. using P = detail::OCVCallHelper<Impl, typename K::InArgs, typename K::OutArgs>;
  219. public:
  220. using API = K;
  221. static cv::gapi::GBackend backend() { return cv::gapi::cpu::backend(); }
  222. static cv::GCPUKernel kernel() { return GCPUKernel(&P::call); }
  223. };
  224. #define GAPI_OCV_KERNEL(Name, API) struct Name: public cv::GCPUKernelImpl<Name, API>
  225. } // namespace cv
  226. #endif // OPENCV_GAPI_GCPUKERNEL_HPP