github.com/kaydxh/golang@v0.0.131/pkg/gocv/cgo/third_path/opencv4/include/opencv2/gapi/cpu/gcpukernel.hpp (about)

     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-2020 Intel Corporation
     6  
     7  
     8  #ifndef OPENCV_GAPI_GCPUKERNEL_HPP
     9  #define OPENCV_GAPI_GCPUKERNEL_HPP
    10  
    11  #include <functional>
    12  #include <unordered_map>
    13  #include <utility>
    14  #include <vector>
    15  
    16  #include <opencv2/core/mat.hpp>
    17  #include <opencv2/gapi/gcommon.hpp>
    18  #include <opencv2/gapi/gkernel.hpp>
    19  #include <opencv2/gapi/garg.hpp>
    20  #include <opencv2/gapi/gmetaarg.hpp>
    21  #include <opencv2/gapi/util/compiler_hints.hpp> //suppress_unused_warning
    22  #include <opencv2/gapi/util/util.hpp>
    23  
    24  // FIXME: namespace scheme for backends?
    25  namespace cv {
    26  
    27  namespace gimpl
    28  {
    29      // Forward-declare an internal class
    30      class GCPUExecutable;
    31  
    32      namespace render
    33      {
    34      namespace ocv
    35      {
    36          class GRenderExecutable;
    37      }
    38      }
    39  } // namespace gimpl
    40  
    41  namespace gapi
    42  {
    43  /**
    44   * @brief This namespace contains G-API CPU backend functions,
    45   * structures, and symbols.
    46   */
    47  namespace cpu
    48  {
    49      /**
    50       * \addtogroup gapi_std_backends
    51       * @{
    52       *
    53       * @brief G-API backends available in this OpenCV version
    54       *
    55       * G-API backends play a corner stone role in G-API execution
    56       * stack. Every backend is hardware-oriented and thus can run its
    57       * kernels efficiently on the target platform.
    58       *
    59       * Backends are usually "black boxes" for G-API users -- on the API
    60       * side, all backends are represented as different objects of the
    61       * same class cv::gapi::GBackend.
    62       * User can manipulate with backends by specifying which kernels to use.
    63       *
    64       * @sa @ref gapi_hld
    65       */
    66  
    67      /**
    68       * @brief Get a reference to CPU (OpenCV) backend.
    69       *
    70       * This is the default backend in G-API at the moment, providing
    71       * broader functional coverage but losing some graph model
    72       * advantages. Provided mostly for reference and prototyping
    73       * purposes.
    74       *
    75       * @sa gapi_std_backends
    76       */
    77      GAPI_EXPORTS cv::gapi::GBackend backend();
    78      /** @} */
    79  
    80      class GOCVFunctor;
    81  
    82      //! @cond IGNORED
    83      template<typename K, typename Callable>
    84      GOCVFunctor ocv_kernel(const Callable& c);
    85  
    86      template<typename K, typename Callable>
    87      GOCVFunctor ocv_kernel(Callable& c);
    88      //! @endcond
    89  
    90  } // namespace cpu
    91  } // namespace gapi
    92  
    93  // Represents arguments which are passed to a wrapped CPU function
    94  // FIXME: put into detail?
    95  class GAPI_EXPORTS GCPUContext
    96  {
    97  public:
    98      // Generic accessor API
    99      template<typename T>
   100      const T& inArg(int input) { return m_args.at(input).get<T>(); }
   101  
   102      // Syntax sugar
   103      const cv::Mat&   inMat(int input);
   104      cv::Mat&         outMatR(int output); // FIXME: Avoid cv::Mat m = ctx.outMatR()
   105  
   106      const cv::Scalar& inVal(int input);
   107      cv::Scalar& outValR(int output); // FIXME: Avoid cv::Scalar s = ctx.outValR()
   108      cv::MediaFrame& outFrame(int output);
   109      template<typename T> std::vector<T>& outVecR(int output) // FIXME: the same issue
   110      {
   111          return outVecRef(output).wref<T>();
   112      }
   113      template<typename T> T& outOpaqueR(int output) // FIXME: the same issue
   114      {
   115          return outOpaqueRef(output).wref<T>();
   116      }
   117  
   118      GArg state()
   119      {
   120          return m_state;
   121      }
   122  
   123  protected:
   124      detail::VectorRef& outVecRef(int output);
   125      detail::OpaqueRef& outOpaqueRef(int output);
   126  
   127      std::vector<GArg> m_args;
   128      GArg m_state;
   129  
   130      //FIXME: avoid conversion of arguments from internal representation to OpenCV one on each call
   131      //to OCV kernel. (This can be achieved by a two single time conversions in GCPUExecutable::run,
   132      //once on enter for input and output arguments, and once before return for output arguments only
   133      std::unordered_map<std::size_t, GRunArgP> m_results;
   134  
   135      friend class gimpl::GCPUExecutable;
   136      friend class gimpl::render::ocv::GRenderExecutable;
   137  };
   138  
   139  class GAPI_EXPORTS GCPUKernel
   140  {
   141  public:
   142      // This function is a kernel's execution entry point (does the processing work)
   143      using RunF = std::function<void(GCPUContext &)>;
   144      // This function is a stateful kernel's setup routine (configures state)
   145      using SetupF = std::function<void(const GMetaArgs &, const GArgs &,
   146                                        GArg &, const GCompileArgs &)>;
   147  
   148      GCPUKernel();
   149      GCPUKernel(const RunF& runF, const SetupF& setupF = nullptr);
   150  
   151      RunF m_runF = nullptr;
   152      SetupF m_setupF = nullptr;
   153  
   154      bool m_isStateful = false;
   155  };
   156  
   157  // FIXME: This is an ugly ad-hoc implementation. TODO: refactor
   158  
   159  namespace detail
   160  {
   161  template<class T> struct get_in;
   162  template<> struct get_in<cv::GMat>
   163  {
   164      static cv::Mat    get(GCPUContext &ctx, int idx) { return ctx.inMat(idx); }
   165  };
   166  template<> struct get_in<cv::GMatP>
   167  {
   168      static cv::Mat    get(GCPUContext &ctx, int idx) { return get_in<cv::GMat>::get(ctx, idx); }
   169  };
   170  template<> struct get_in<cv::GFrame>
   171  {
   172      static cv::MediaFrame get(GCPUContext &ctx, int idx) { return ctx.inArg<cv::MediaFrame>(idx); }
   173  };
   174  template<> struct get_in<cv::GScalar>
   175  {
   176      static cv::Scalar get(GCPUContext &ctx, int idx) { return ctx.inVal(idx); }
   177  };
   178  template<typename U> struct get_in<cv::GArray<U> >
   179  {
   180      static const std::vector<U>& get(GCPUContext &ctx, int idx) { return ctx.inArg<VectorRef>(idx).rref<U>(); }
   181  };
   182  template<typename U> struct get_in<cv::GOpaque<U> >
   183  {
   184      static const U& get(GCPUContext &ctx, int idx) { return ctx.inArg<OpaqueRef>(idx).rref<U>(); }
   185  };
   186  
   187  //FIXME(dm): GArray<Mat>/GArray<GMat> conversion should be done more gracefully in the system
   188  template<> struct get_in<cv::GArray<cv::GMat> >: public get_in<cv::GArray<cv::Mat> >
   189  {
   190  };
   191  
   192  //FIXME(dm): GArray<Scalar>/GArray<GScalar> conversion should be done more gracefully in the system
   193  template<> struct get_in<cv::GArray<cv::GScalar> >: public get_in<cv::GArray<cv::Scalar> >
   194  {
   195  };
   196  
   197  // FIXME(dm): GArray<vector<U>>/GArray<GArray<U>> conversion should be done more gracefully in the system
   198  template<typename U> struct get_in<cv::GArray<cv::GArray<U>> >: public get_in<cv::GArray<std::vector<U>> >
   199  {
   200  };
   201  
   202  //FIXME(dm): GOpaque<Mat>/GOpaque<GMat> conversion should be done more gracefully in the system
   203  template<> struct get_in<cv::GOpaque<cv::GMat> >: public get_in<cv::GOpaque<cv::Mat> >
   204  {
   205  };
   206  
   207  //FIXME(dm): GOpaque<Scalar>/GOpaque<GScalar> conversion should be done more gracefully in the system
   208  template<> struct get_in<cv::GOpaque<cv::GScalar> >: public get_in<cv::GOpaque<cv::Mat> >
   209  {
   210  };
   211  
   212  template<class T> struct get_in
   213  {
   214      static T get(GCPUContext &ctx, int idx) { return ctx.inArg<T>(idx); }
   215  };
   216  
   217  struct tracked_cv_mat{
   218      tracked_cv_mat(cv::Mat& m) : r{m}, original_data{m.data} {}
   219      cv::Mat r;
   220      uchar* original_data;
   221  
   222      operator cv::Mat& (){ return r;}
   223      void validate() const{
   224          if (r.data != original_data)
   225          {
   226              util::throw_error
   227                  (std::logic_error
   228                   ("OpenCV kernel output parameter was reallocated. \n"
   229                    "Incorrect meta data was provided ?"));
   230          }
   231      }
   232  };
   233  
   234  template<typename... Outputs>
   235  void postprocess(Outputs&... outs)
   236  {
   237      struct
   238      {
   239          void operator()(tracked_cv_mat* bm) { bm->validate();  }
   240          void operator()(...)                {                  }
   241  
   242      } validate;
   243      //dummy array to unfold parameter pack
   244      int dummy[] = { 0, (validate(&outs), 0)... };
   245      cv::util::suppress_unused_warning(dummy);
   246  }
   247  
   248  template<class T> struct get_out;
   249  template<> struct get_out<cv::GMat>
   250  {
   251      static tracked_cv_mat get(GCPUContext &ctx, int idx)
   252      {
   253          auto& r = ctx.outMatR(idx);
   254          return {r};
   255      }
   256  };
   257  template<> struct get_out<cv::GMatP>
   258  {
   259      static tracked_cv_mat get(GCPUContext &ctx, int idx)
   260      {
   261          return get_out<cv::GMat>::get(ctx, idx);
   262      }
   263  };
   264  template<> struct get_out<cv::GScalar>
   265  {
   266      static cv::Scalar& get(GCPUContext &ctx, int idx)
   267      {
   268          return ctx.outValR(idx);
   269      }
   270  };
   271  template<> struct get_out<cv::GFrame>
   272  {
   273      static cv::MediaFrame& get(GCPUContext &ctx, int idx)
   274      {
   275          return ctx.outFrame(idx);
   276      }
   277  };
   278  template<typename U> struct get_out<cv::GArray<U>>
   279  {
   280      static std::vector<U>& get(GCPUContext &ctx, int idx)
   281      {
   282          return ctx.outVecR<U>(idx);
   283      }
   284  };
   285  
   286  //FIXME(dm): GArray<Mat>/GArray<GMat> conversion should be done more gracefully in the system
   287  template<> struct get_out<cv::GArray<cv::GMat> >: public get_out<cv::GArray<cv::Mat> >
   288  {
   289  };
   290  
   291  // FIXME(dm): GArray<vector<U>>/GArray<GArray<U>> conversion should be done more gracefully in the system
   292  template<typename U> struct get_out<cv::GArray<cv::GArray<U>> >: public get_out<cv::GArray<std::vector<U>> >
   293  {
   294  };
   295  
   296  template<typename U> struct get_out<cv::GOpaque<U>>
   297  {
   298      static U& get(GCPUContext &ctx, int idx)
   299      {
   300          return ctx.outOpaqueR<U>(idx);
   301      }
   302  };
   303  
   304  template<typename, typename>
   305  struct OCVSetupHelper;
   306  
   307  template<typename Impl, typename... Ins>
   308  struct OCVSetupHelper<Impl, std::tuple<Ins...>>
   309  {
   310      // Using 'auto' return type and 'decltype' specifier in both 'setup_impl' versions
   311      // to check existence of required 'Impl::setup' functions.
   312      // While 'decltype' specifier accepts expression we pass expression with 'comma-operator'
   313      // where first operand of comma-operator is call attempt to desired 'Impl::setup' and
   314      // the second operand is 'void()' expression.
   315      //
   316      // SFINAE for 'Impl::setup' which accepts compile arguments.
   317      template<int... IIs>
   318      static auto setup_impl(const GMetaArgs &metaArgs, const GArgs &args,
   319                             GArg &state, const GCompileArgs &compileArgs,
   320                             detail::Seq<IIs...>) ->
   321          decltype(Impl::setup(detail::get_in_meta<Ins>(metaArgs, args, IIs)...,
   322                               std::declval<typename std::add_lvalue_reference<
   323                                                std::shared_ptr<typename Impl::State>
   324                                                                              >::type
   325                                           >(),
   326                              compileArgs)
   327                   , void())
   328      {
   329          // TODO: unique_ptr <-> shared_ptr conversion ?
   330          // To check: Conversion is possible only if the state which should be passed to
   331          // 'setup' user callback isn't required to have previous value
   332          std::shared_ptr<typename Impl::State> stPtr;
   333          Impl::setup(detail::get_in_meta<Ins>(metaArgs, args, IIs)..., stPtr, compileArgs);
   334          state = GArg(stPtr);
   335      }
   336  
   337      // SFINAE for 'Impl::setup' which doesn't accept compile arguments.
   338      template<int... IIs>
   339      static auto setup_impl(const GMetaArgs &metaArgs, const GArgs &args,
   340                             GArg &state, const GCompileArgs &/* compileArgs */,
   341                             detail::Seq<IIs...>) ->
   342          decltype(Impl::setup(detail::get_in_meta<Ins>(metaArgs, args, IIs)...,
   343                               std::declval<typename std::add_lvalue_reference<
   344                                                std::shared_ptr<typename Impl::State>
   345                                                                              >::type
   346                                           >()
   347                              )
   348                   , void())
   349      {
   350          // The same comment as in 'setup' above.
   351          std::shared_ptr<typename Impl::State> stPtr;
   352          Impl::setup(detail::get_in_meta<Ins>(metaArgs, args, IIs)..., stPtr);
   353          state = GArg(stPtr);
   354      }
   355  
   356      static void setup(const GMetaArgs &metaArgs, const GArgs &args,
   357                        GArg& state, const GCompileArgs &compileArgs)
   358      {
   359          setup_impl(metaArgs, args, state, compileArgs,
   360                     typename detail::MkSeq<sizeof...(Ins)>::type());
   361      }
   362  };
   363  
   364  // OCVCallHelper is a helper class to call stateless OCV kernels and OCV kernel functors.
   365  template<typename, typename, typename>
   366  struct OCVCallHelper;
   367  
   368  // FIXME: probably can be simplified with std::apply or analogue.
   369  template<typename Impl, typename... Ins, typename... Outs>
   370  struct OCVCallHelper<Impl, std::tuple<Ins...>, std::tuple<Outs...>>
   371  {
   372      template<typename... Inputs>
   373      struct call_and_postprocess
   374      {
   375          template<typename... Outputs>
   376          static void call(Inputs&&... ins, Outputs&&... outs)
   377          {
   378              //not using a std::forward on outs is deliberate in order to
   379              //cause compilation error, by trying to bind rvalue references to lvalue references
   380              Impl::run(std::forward<Inputs>(ins)..., outs...);
   381              postprocess(outs...);
   382          }
   383  
   384          template<typename... Outputs>
   385          static void call(Impl& impl, Inputs&&... ins, Outputs&&... outs)
   386          {
   387              impl(std::forward<Inputs>(ins)..., outs...);
   388          }
   389      };
   390  
   391      template<int... IIs, int... OIs>
   392      static void call_impl(GCPUContext &ctx, detail::Seq<IIs...>, detail::Seq<OIs...>)
   393      {
   394          //Make sure that OpenCV kernels do not reallocate memory for output parameters
   395          //by comparing it's state (data ptr) before and after the call.
   396          //This is done by converting each output Mat into tracked_cv_mat object, and binding
   397          //them to parameters of ad-hoc function
   398          call_and_postprocess<decltype(get_in<Ins>::get(ctx, IIs))...>
   399              ::call(get_in<Ins>::get(ctx, IIs)..., get_out<Outs>::get(ctx, OIs)...);
   400      }
   401  
   402      template<int... IIs, int... OIs>
   403      static void call_impl(cv::GCPUContext &ctx, Impl& impl,
   404                            detail::Seq<IIs...>, detail::Seq<OIs...>)
   405      {
   406          call_and_postprocess<decltype(get_in<Ins>::get(ctx, IIs))...>
   407              ::call(impl, get_in<Ins>::get(ctx, IIs)..., get_out<Outs>::get(ctx, OIs)...);
   408      }
   409  
   410      static void call(GCPUContext &ctx)
   411      {
   412          call_impl(ctx,
   413                    typename detail::MkSeq<sizeof...(Ins)>::type(),
   414                    typename detail::MkSeq<sizeof...(Outs)>::type());
   415      }
   416  
   417      // NB: Same as call but calling the object
   418      // This necessary for kernel implementations that have a state
   419      // and are represented as an object
   420      static void callFunctor(cv::GCPUContext &ctx, Impl& impl)
   421      {
   422          call_impl(ctx, impl,
   423                    typename detail::MkSeq<sizeof...(Ins)>::type(),
   424                    typename detail::MkSeq<sizeof...(Outs)>::type());
   425      }
   426  };
   427  
   428  // OCVStCallHelper is a helper class to call stateful OCV kernels.
   429  template<typename, typename, typename>
   430  struct OCVStCallHelper;
   431  
   432  template<typename Impl, typename... Ins, typename... Outs>
   433  struct OCVStCallHelper<Impl, std::tuple<Ins...>, std::tuple<Outs...>> :
   434      OCVCallHelper<Impl, std::tuple<Ins...>, std::tuple<Outs...>>
   435  {
   436      template<typename... Inputs>
   437      struct call_and_postprocess
   438      {
   439          template<typename... Outputs>
   440          static void call(typename Impl::State& st, Inputs&&... ins, Outputs&&... outs)
   441          {
   442              Impl::run(std::forward<Inputs>(ins)..., outs..., st);
   443              postprocess(outs...);
   444          }
   445      };
   446  
   447      template<int... IIs, int... OIs>
   448      static void call_impl(GCPUContext &ctx, detail::Seq<IIs...>, detail::Seq<OIs...>)
   449      {
   450          auto& st = *ctx.state().get<std::shared_ptr<typename Impl::State>>();
   451          call_and_postprocess<decltype(get_in<Ins>::get(ctx, IIs))...>
   452              ::call(st, get_in<Ins>::get(ctx, IIs)..., get_out<Outs>::get(ctx, OIs)...);
   453      }
   454  
   455      static void call(GCPUContext &ctx)
   456      {
   457          call_impl(ctx,
   458                    typename detail::MkSeq<sizeof...(Ins)>::type(),
   459                    typename detail::MkSeq<sizeof...(Outs)>::type());
   460      }
   461  };
   462  
   463  } // namespace detail
   464  
   465  template<class Impl, class K>
   466  class GCPUKernelImpl: public cv::detail::KernelTag
   467  {
   468      using CallHelper = cv::detail::OCVCallHelper<Impl, typename K::InArgs, typename K::OutArgs>;
   469  
   470  public:
   471      using API = K;
   472  
   473      static cv::gapi::GBackend backend() { return cv::gapi::cpu::backend(); }
   474      static cv::GCPUKernel      kernel() { return GCPUKernel(&CallHelper::call); }
   475  };
   476  
   477  template<class Impl, class K, class S>
   478  class GCPUStKernelImpl: public cv::detail::KernelTag
   479  {
   480      using StSetupHelper = detail::OCVSetupHelper<Impl, typename K::InArgs>;
   481      using StCallHelper  = detail::OCVStCallHelper<Impl, typename K::InArgs, typename K::OutArgs>;
   482  
   483  public:
   484      using API = K;
   485      using State = S;
   486  
   487      static cv::gapi::GBackend backend() { return cv::gapi::cpu::backend(); }
   488      static cv::GCPUKernel     kernel()  { return GCPUKernel(&StCallHelper::call,
   489                                                              &StSetupHelper::setup); }
   490  };
   491  
   492  #define GAPI_OCV_KERNEL(Name, API) struct Name: public cv::GCPUKernelImpl<Name, API>
   493  
   494  // TODO: Reuse Anatoliy's logic for support of types with commas in macro.
   495  //       Retrieve the common part from Anatoliy's logic to the separate place.
   496  #define GAPI_OCV_KERNEL_ST(Name, API, State)                   \
   497      struct Name: public cv::GCPUStKernelImpl<Name, API, State> \
   498  
   499  /// @private
   500  class gapi::cpu::GOCVFunctor : public gapi::GFunctor
   501  {
   502  public:
   503      using Impl = std::function<void(GCPUContext &)>;
   504      using Meta = cv::GKernel::M;
   505  
   506      GOCVFunctor(const char* id, const Meta &meta, const Impl& impl)
   507          : gapi::GFunctor(id), impl_{GCPUKernel(impl), meta}
   508      {
   509      }
   510  
   511      GKernelImpl    impl()    const override { return impl_;                }
   512      gapi::GBackend backend() const override { return gapi::cpu::backend(); }
   513  
   514  private:
   515      GKernelImpl impl_;
   516  };
   517  
   518  //! @cond IGNORED
   519  template<typename K, typename Callable>
   520  gapi::cpu::GOCVFunctor gapi::cpu::ocv_kernel(Callable& c)
   521  {
   522      using P = cv::detail::OCVCallHelper<Callable, typename K::InArgs, typename K::OutArgs>;
   523      return GOCVFunctor{ K::id()
   524                        , &K::getOutMeta
   525                        , std::bind(&P::callFunctor, std::placeholders::_1, std::ref(c))
   526                        };
   527  }
   528  
   529  template<typename K, typename Callable>
   530  gapi::cpu::GOCVFunctor gapi::cpu::ocv_kernel(const Callable& c)
   531  {
   532      using P = cv::detail::OCVCallHelper<Callable, typename K::InArgs, typename K::OutArgs>;
   533      return GOCVFunctor{ K::id()
   534                        , &K::getOutMeta
   535                        , std::bind(&P::callFunctor, std::placeholders::_1, c)
   536                        };
   537  }
   538  //! @endcond
   539  
   540  } // namespace cv
   541  
   542  #endif // OPENCV_GAPI_GCPUKERNEL_HPP