// This file is part of OpenCV project. // It is subject to the license terms in the LICENSE file found in the top-level directory // of this distribution and at http://opencv.org/license.html. // // Copyright (C) 2018-2019 Intel Corporation #ifndef OPENCV_GAPI_FLUID_KERNEL_HPP #define OPENCV_GAPI_FLUID_KERNEL_HPP #include <vector> #include <functional> #include <map> #include <unordered_map> #include <opencv2/gapi/opencv_includes.hpp> #include <opencv2/gapi/gcommon.hpp> #include <opencv2/gapi/gkernel.hpp> #include <opencv2/gapi/garg.hpp> #include <opencv2/gapi/fluid/gfluidbuffer.hpp> // FIXME: namespace scheme for backends? namespace cv { namespace gapi { /** * @brief This namespace contains G-API Fluid backend functions, structures, and symbols. */ namespace fluid { /** * \addtogroup gapi_std_backends G-API Standard Backends * @{ */ /** * @brief Get a reference to Fluid backend. * * @sa gapi_std_backends */ GAPI_EXPORTS cv::gapi::GBackend backend(); /** @} */ } // namespace fluid } // namespace gapi class GAPI_EXPORTS GFluidKernel { public: enum class Kind { Filter, Resize, YUV420toRGB //Color conversion of 4:2:0 chroma sub-sampling formats (NV12, I420 ..etc) to RGB }; // This function is a generic "doWork" callback using F = std::function<void(const cv::GArgs&, const std::vector<gapi::fluid::Buffer*> &)>; // This function is a generic "initScratch" callback using IS = std::function<void(const cv::GMetaArgs &, const cv::GArgs&, gapi::fluid::Buffer &)>; // This function is a generic "resetScratch" callback using RS = std::function<void(gapi::fluid::Buffer &)>; // This function describes kernel metadata inference rule. using M = std::function<GMetaArgs(const GMetaArgs &, const GArgs &)>; // This function is a generic "getBorder" callback (extracts border-related data from kernel's input parameters) using B = std::function<gapi::fluid::BorderOpt(const GMetaArgs&, const GArgs&)>; // This function is a generic "getWindow" callback (extracts window-related data from kernel's input parameters) using GW = std::function<int(const GMetaArgs&, const GArgs&)>; // FIXME: move implementations out of header file GFluidKernel() {} GFluidKernel(Kind k, int l, bool scratch, const F& f, const IS &is, const RS &rs, const B& b, const GW& win) : m_kind(k) , m_lpi(l) , m_scratch(scratch) , m_f(f) , m_is(is) , m_rs(rs) , m_b(b) , m_gw(win) {} Kind m_kind; const int m_lpi = -1; const bool m_scratch = false; const F m_f; const IS m_is; const RS m_rs; const B m_b; const GW m_gw; }; // FIXME!!! // This is the temporary and experimental API // which should be replaced by runtime roi-based scheduling /** \addtogroup gapi_compile_args * @{ */ /** * @brief This structure allows to control the output image region * which Fluid backend will produce in the graph. * * This feature is useful for external tiling and parallelism, but * will be deprecated in the future releases. */ struct GFluidOutputRois { std::vector<cv::Rect> rois; }; /** * @brief This structure forces Fluid backend to generate multiple * parallel output regions in the graph. These regions execute in parallel. * * This feature may be deprecated in the future releases. */ struct GFluidParallelOutputRois { std::vector<GFluidOutputRois> parallel_rois; }; /** * @brief This structure allows to customize the way how Fluid executes * parallel regions. * * For example, user can utilize his own threading runtime via this parameter. * The `parallel_for` member functor is called by the Fluid runtime with the * following arguments: * * @param size Size of the parallel range to process * @param f A function which should be called for every integer index * in this range by the specified parallel_for implementation. * * This feature may be deprecated in the future releases. */ struct GFluidParallelFor { //this function accepts: // - size of the "parallel" range as the first argument // - and a function to be called on the range items, designated by item index std::function<void(std::size_t size, std::function<void(std::size_t index)>)> parallel_for; }; /** @} gapi_compile_args */ namespace detail { template<> struct CompileArgTag<GFluidOutputRois> { static const char* tag() { return "gapi.fluid.outputRois"; } }; template<> struct CompileArgTag<GFluidParallelFor> { static const char* tag() { return "gapi.fluid.parallelFor"; } }; template<> struct CompileArgTag<GFluidParallelOutputRois> { static const char* tag() { return "gapi.fluid.parallelOutputRois"; } }; } // namespace detail namespace detail { template<class T> struct fluid_get_in; template<> struct fluid_get_in<cv::GMat> { static const cv::gapi::fluid::View& get(const cv::GArgs &in_args, int idx) { return *in_args[idx].unsafe_get<cv::gapi::fluid::View*>(); } }; template<> struct fluid_get_in<cv::GScalar> { // FIXME: change to return by reference when moved to own::Scalar static cv::Scalar get(const cv::GArgs &in_args, int idx) { return in_args[idx].unsafe_get<cv::Scalar>(); } }; template<typename U> struct fluid_get_in<cv::GArray<U>> { static const std::vector<U>& get(const cv::GArgs &in_args, int idx) { return in_args.at(idx).unsafe_get<cv::detail::VectorRef>().rref<U>(); } }; template<typename U> struct fluid_get_in<cv::GOpaque<U>> { static const U& get(const cv::GArgs &in_args, int idx) { return in_args.at(idx).unsafe_get<cv::detail::OpaqueRef>().rref<U>(); } }; template<class T> struct fluid_get_in { static const T& get(const cv::GArgs &in_args, int idx) { return in_args[idx].unsafe_get<T>(); } }; template<bool, typename Impl, typename... Ins> struct scratch_helper; template<typename Impl, typename... Ins> struct scratch_helper<true, Impl, Ins...> { // Init template<int... IIs> static void help_init_impl(const cv::GMetaArgs &metas, const cv::GArgs &in_args, gapi::fluid::Buffer &scratch_buf, detail::Seq<IIs...>) { Impl::initScratch(get_in_meta<Ins>(metas, in_args, IIs)..., scratch_buf); } static void help_init(const cv::GMetaArgs &metas, const cv::GArgs &in_args, gapi::fluid::Buffer &b) { help_init_impl(metas, in_args, b, typename detail::MkSeq<sizeof...(Ins)>::type()); } // Reset static void help_reset(gapi::fluid::Buffer &b) { Impl::resetScratch(b); } }; template<typename Impl, typename... Ins> struct scratch_helper<false, Impl, Ins...> { static void help_init(const cv::GMetaArgs &, const cv::GArgs &, gapi::fluid::Buffer &) { GAPI_Error("InternalError"); } static void help_reset(gapi::fluid::Buffer &) { GAPI_Error("InternalError"); } }; template<typename T> struct is_gmat_type { static const constexpr bool value = std::is_same<cv::GMat, T>::value; }; template<bool CallCustomGetBorder, typename Impl, typename... Ins> struct get_border_helper; template<typename Impl, typename... Ins> struct get_border_helper<true, Impl, Ins...> { template<int... IIs> static gapi::fluid::BorderOpt get_border_impl(const GMetaArgs &metas, const cv::GArgs &in_args, cv::detail::Seq<IIs...>) { return util::make_optional(Impl::getBorder(cv::detail::get_in_meta<Ins>(metas, in_args, IIs)...)); } static gapi::fluid::BorderOpt help(const GMetaArgs &metas, const cv::GArgs &in_args) { return get_border_impl(metas, in_args, typename detail::MkSeq<sizeof...(Ins)>::type()); } }; template<typename Impl, typename... Ins> struct get_border_helper<false, Impl, Ins...> { static gapi::fluid::BorderOpt help(const cv::GMetaArgs &, const cv::GArgs &) { return {}; } }; template<bool CallCustomGetWindow, typename, typename... Ins> struct get_window_helper; template<typename Impl, typename... Ins> struct get_window_helper<true, Impl, Ins...> { template<int... IIs> static int get_window_impl(const GMetaArgs &metas, const cv::GArgs &in_args, cv::detail::Seq<IIs...>) { return Impl::getWindow(cv::detail::get_in_meta<Ins>(metas, in_args, IIs)...); } static int help(const GMetaArgs &metas, const cv::GArgs &in_args) { return get_window_impl(metas, in_args, typename detail::MkSeq<sizeof...(Ins)>::type()); } }; template<typename Impl, typename... Ins> struct get_window_helper<false, Impl, Ins...> { static int help(const cv::GMetaArgs &, const cv::GArgs &) { return Impl::Window; } }; template<typename C, typename T> struct has_Window { private: template<class U> static constexpr auto Check(U*) -> typename std::is_same<decltype(U::Window), T>::type; template<typename> static constexpr std::false_type Check(...); typedef decltype(Check<C>(0)) Result; public: static constexpr bool value = Result::value; }; template<bool hasWindow, typename Impl> struct callCustomGetBorder; template<typename Impl> struct callCustomGetBorder<true, Impl> { static constexpr bool value = (Impl::Window != 1); }; template<typename Impl> struct callCustomGetBorder<false, Impl> { static constexpr bool value = true; }; template<typename, typename, typename, bool UseScratch> struct FluidCallHelper; template<typename Impl, typename... Ins, typename... Outs, bool UseScratch> struct FluidCallHelper<Impl, std::tuple<Ins...>, std::tuple<Outs...>, UseScratch> { static_assert(all_satisfy<is_gmat_type, Outs...>::value, "return type must be GMat"); static_assert(contains<GMat, Ins...>::value, "input must contain at least one GMat"); // Execution dispatcher //////////////////////////////////////////////////// template<int... IIs, int... OIs> static void call_impl(const cv::GArgs &in_args, const std::vector<gapi::fluid::Buffer*> &out_bufs, detail::Seq<IIs...>, detail::Seq<OIs...>) { Impl::run(fluid_get_in<Ins>::get(in_args, IIs)..., *out_bufs[OIs]...); } static void call(const cv::GArgs &in_args, const std::vector<gapi::fluid::Buffer*> &out_bufs) { constexpr int numOuts = (sizeof...(Outs)) + (UseScratch ? 1 : 0); call_impl(in_args, out_bufs, typename detail::MkSeq<sizeof...(Ins)>::type(), typename detail::MkSeq<numOuts>::type()); } // Scratch buffer initialization dispatcher //////////////////////////////// static void init_scratch(const GMetaArgs &metas, const cv::GArgs &in_args, gapi::fluid::Buffer &b) { scratch_helper<UseScratch, Impl, Ins...>::help_init(metas, in_args, b); } // Scratch buffer reset dispatcher ///////////////////////////////////////// static void reset_scratch(gapi::fluid::Buffer &scratch_buf) { scratch_helper<UseScratch, Impl, Ins...>::help_reset(scratch_buf); } static gapi::fluid::BorderOpt getBorder(const GMetaArgs &metas, const cv::GArgs &in_args) { constexpr bool hasWindow = has_Window<Impl, const int>::value; // User must provide "init" callback if Window != 1 // TODO: move to constexpr if when we enable C++17 return get_border_helper<callCustomGetBorder<hasWindow, Impl>::value, Impl, Ins...>::help(metas, in_args); } static int getWindow(const GMetaArgs &metas, const cv::GArgs &in_args) { constexpr bool callCustomGetWindow = !(has_Window<Impl, const int>::value); return get_window_helper<callCustomGetWindow, Impl, Ins...>::help(metas, in_args); } }; } // namespace detail template<class Impl, class K, bool UseScratch> class GFluidKernelImpl : public cv::detail::KernelTag { static const int LPI = 1; static const auto Kind = GFluidKernel::Kind::Filter; using P = detail::FluidCallHelper<Impl, typename K::InArgs, typename K::OutArgs, UseScratch>; public: using API = K; static GFluidKernel kernel() { // FIXME: call() and getOutMeta() needs to be renamed so it is clear these // functions are internal wrappers, not user API return GFluidKernel(Impl::Kind, Impl::LPI, UseScratch, &P::call, &P::init_scratch, &P::reset_scratch, &P::getBorder, &P::getWindow); } static cv::gapi::GBackend backend() { return cv::gapi::fluid::backend(); } }; #define GAPI_FLUID_KERNEL(Name, API, Scratch) struct Name: public cv::GFluidKernelImpl<Name, API, Scratch> } // namespace cv #endif // OPENCV_GAPI_GCPUKERNEL_HPP