Enable stateful kernels in G-API OCV Backend
This commit is contained in:
@@ -17,6 +17,7 @@
|
||||
#include <opencv2/gapi/gcommon.hpp>
|
||||
#include <opencv2/gapi/gkernel.hpp>
|
||||
#include <opencv2/gapi/garg.hpp>
|
||||
#include <opencv2/gapi/gmetaarg.hpp>
|
||||
#include <opencv2/gapi/util/compiler_hints.hpp> //suppress_unused_warning
|
||||
#include <opencv2/gapi/util/util.hpp>
|
||||
|
||||
@@ -109,11 +110,17 @@ public:
|
||||
return outOpaqueRef(output).wref<T>();
|
||||
}
|
||||
|
||||
GArg state()
|
||||
{
|
||||
return m_state;
|
||||
}
|
||||
|
||||
protected:
|
||||
detail::VectorRef& outVecRef(int output);
|
||||
detail::OpaqueRef& outOpaqueRef(int output);
|
||||
|
||||
std::vector<GArg> m_args;
|
||||
GArg m_state;
|
||||
|
||||
//FIXME: avoid conversion of arguments from internal representation to OpenCV one on each call
|
||||
//to OCV kernel. (This can be achieved by a two single time conversions in GCPUExecutable::run,
|
||||
@@ -127,16 +134,18 @@ protected:
|
||||
class GAPI_EXPORTS GCPUKernel
|
||||
{
|
||||
public:
|
||||
// This function is kernel's execution entry point (does the processing work)
|
||||
using F = std::function<void(GCPUContext &)>;
|
||||
// This function is a kernel's execution entry point (does the processing work)
|
||||
using RunF = std::function<void(GCPUContext &)>;
|
||||
// This function is a stateful kernel's setup routine (configures state)
|
||||
using SetupF = std::function<void(const GMetaArgs &, const GArgs &, GArg &)>;
|
||||
|
||||
GCPUKernel();
|
||||
explicit GCPUKernel(const F& f);
|
||||
GCPUKernel(const RunF& runF, const SetupF& setupF = nullptr);
|
||||
|
||||
void apply(GCPUContext &ctx);
|
||||
RunF m_runF = nullptr;
|
||||
SetupF m_setupF = nullptr;
|
||||
|
||||
protected:
|
||||
F m_f;
|
||||
bool m_isStateful = false;
|
||||
};
|
||||
|
||||
// FIXME: This is an ugly ad-hoc implementation. TODO: refactor
|
||||
@@ -269,12 +278,38 @@ template<typename U> struct get_out<cv::GOpaque<U>>
|
||||
}
|
||||
};
|
||||
|
||||
template<typename, typename>
|
||||
struct OCVSetupHelper;
|
||||
|
||||
template<typename Impl, typename... Ins>
|
||||
struct OCVSetupHelper<Impl, std::tuple<Ins...>>
|
||||
{
|
||||
template<int... IIs>
|
||||
static void setup_impl(const GMetaArgs &metaArgs, const GArgs &args, GArg &state,
|
||||
detail::Seq<IIs...>)
|
||||
{
|
||||
// TODO: unique_ptr <-> shared_ptr conversion ?
|
||||
// To check: Conversion is possible only if the state which should be passed to
|
||||
// 'setup' user callback isn't required to have previous value
|
||||
std::shared_ptr<typename Impl::State> stPtr;
|
||||
Impl::setup(detail::get_in_meta<Ins>(metaArgs, args, IIs)..., stPtr);
|
||||
state = GArg(stPtr);
|
||||
}
|
||||
|
||||
static void setup(const GMetaArgs &metaArgs, const GArgs &args, GArg& state)
|
||||
{
|
||||
setup_impl(metaArgs, args, state,
|
||||
typename detail::MkSeq<sizeof...(Ins)>::type());
|
||||
}
|
||||
};
|
||||
|
||||
// OCVCallHelper is a helper class to call stateless OCV kernels and OCV kernel functors.
|
||||
template<typename, typename, typename>
|
||||
struct OCVCallHelper;
|
||||
|
||||
// FIXME: probably can be simplified with std::apply or analogue.
|
||||
template<typename Impl, typename... Ins, typename... Outs>
|
||||
struct OCVCallHelper<Impl, std::tuple<Ins...>, std::tuple<Outs...> >
|
||||
struct OCVCallHelper<Impl, std::tuple<Ins...>, std::tuple<Outs...>>
|
||||
{
|
||||
template<typename... Inputs>
|
||||
struct call_and_postprocess
|
||||
@@ -302,19 +337,16 @@ struct OCVCallHelper<Impl, std::tuple<Ins...>, std::tuple<Outs...> >
|
||||
//by comparing it's state (data ptr) before and after the call.
|
||||
//This is done by converting each output Mat into tracked_cv_mat object, and binding
|
||||
//them to parameters of ad-hoc function
|
||||
//Convert own::Scalar to cv::Scalar before call kernel and run kernel
|
||||
//convert cv::Scalar to own::Scalar after call kernel and write back results
|
||||
call_and_postprocess<decltype(get_in<Ins>::get(ctx, IIs))...>
|
||||
::call(get_in<Ins>::get(ctx, IIs)...,
|
||||
get_out<Outs>::get(ctx, OIs)...);
|
||||
::call(get_in<Ins>::get(ctx, IIs)..., get_out<Outs>::get(ctx, OIs)...);
|
||||
}
|
||||
|
||||
template<int... IIs, int... OIs>
|
||||
static void call_impl(cv::GCPUContext &ctx, Impl& impl, detail::Seq<IIs...>, detail::Seq<OIs...>)
|
||||
static void call_impl(cv::GCPUContext &ctx, Impl& impl,
|
||||
detail::Seq<IIs...>, detail::Seq<OIs...>)
|
||||
{
|
||||
call_and_postprocess<decltype(cv::detail::get_in<Ins>::get(ctx, IIs))...>
|
||||
::call(impl, cv::detail::get_in<Ins>::get(ctx, IIs)...,
|
||||
cv::detail::get_out<Outs>::get(ctx, OIs)...);
|
||||
call_and_postprocess<decltype(get_in<Ins>::get(ctx, IIs))...>
|
||||
::call(impl, get_in<Ins>::get(ctx, IIs)..., get_out<Outs>::get(ctx, OIs)...);
|
||||
}
|
||||
|
||||
static void call(GCPUContext &ctx)
|
||||
@@ -335,23 +367,78 @@ struct OCVCallHelper<Impl, std::tuple<Ins...>, std::tuple<Outs...> >
|
||||
}
|
||||
};
|
||||
|
||||
// OCVStCallHelper is a helper class to call stateful OCV kernels.
|
||||
template<typename, typename, typename>
|
||||
struct OCVStCallHelper;
|
||||
|
||||
template<typename Impl, typename... Ins, typename... Outs>
|
||||
struct OCVStCallHelper<Impl, std::tuple<Ins...>, std::tuple<Outs...>> :
|
||||
OCVCallHelper<Impl, std::tuple<Ins...>, std::tuple<Outs...>>
|
||||
{
|
||||
template<typename... Inputs>
|
||||
struct call_and_postprocess
|
||||
{
|
||||
template<typename... Outputs>
|
||||
static void call(typename Impl::State& st, Inputs&&... ins, Outputs&&... outs)
|
||||
{
|
||||
Impl::run(std::forward<Inputs>(ins)..., outs..., st);
|
||||
postprocess(outs...);
|
||||
}
|
||||
};
|
||||
|
||||
template<int... IIs, int... OIs>
|
||||
static void call_impl(GCPUContext &ctx, detail::Seq<IIs...>, detail::Seq<OIs...>)
|
||||
{
|
||||
auto& st = *ctx.state().get<std::shared_ptr<typename Impl::State>>();
|
||||
call_and_postprocess<decltype(get_in<Ins>::get(ctx, IIs))...>
|
||||
::call(st, get_in<Ins>::get(ctx, IIs)..., get_out<Outs>::get(ctx, OIs)...);
|
||||
}
|
||||
|
||||
static void call(GCPUContext &ctx)
|
||||
{
|
||||
call_impl(ctx,
|
||||
typename detail::MkSeq<sizeof...(Ins)>::type(),
|
||||
typename detail::MkSeq<sizeof...(Outs)>::type());
|
||||
}
|
||||
};
|
||||
|
||||
} // namespace detail
|
||||
|
||||
template<class Impl, class K>
|
||||
class GCPUKernelImpl: public cv::detail::OCVCallHelper<Impl, typename K::InArgs, typename K::OutArgs>,
|
||||
public cv::detail::KernelTag
|
||||
class GCPUKernelImpl: public cv::detail::KernelTag
|
||||
{
|
||||
using P = detail::OCVCallHelper<Impl, typename K::InArgs, typename K::OutArgs>;
|
||||
using CallHelper = detail::OCVCallHelper<Impl, typename K::InArgs, typename K::OutArgs>;
|
||||
|
||||
public:
|
||||
using API = K;
|
||||
|
||||
static cv::gapi::GBackend backend() { return cv::gapi::cpu::backend(); }
|
||||
static cv::GCPUKernel kernel() { return GCPUKernel(&P::call); }
|
||||
static cv::gapi::GBackend backend() { return cv::gapi::cpu::backend(); }
|
||||
static cv::GCPUKernel kernel() { return GCPUKernel(&CallHelper::call); }
|
||||
};
|
||||
|
||||
template<class Impl, class K, class S>
|
||||
class GCPUStKernelImpl: public cv::detail::KernelTag
|
||||
{
|
||||
using StSetupHelper = detail::OCVSetupHelper<Impl, typename K::InArgs>;
|
||||
using StCallHelper = detail::OCVStCallHelper<Impl, typename K::InArgs, typename K::OutArgs>;
|
||||
|
||||
public:
|
||||
using API = K;
|
||||
using State = S;
|
||||
|
||||
static cv::gapi::GBackend backend() { return cv::gapi::cpu::backend(); }
|
||||
static cv::GCPUKernel kernel() { return GCPUKernel(&StCallHelper::call,
|
||||
&StSetupHelper::setup); }
|
||||
};
|
||||
|
||||
#define GAPI_OCV_KERNEL(Name, API) struct Name: public cv::GCPUKernelImpl<Name, API>
|
||||
|
||||
// TODO: Reuse Anatoliy's logic for support of types with commas in macro.
|
||||
// Retrieve the common part from Anatoliy's logic to the separate place.
|
||||
#define GAPI_OCV_KERNEL_ST(Name, API, State) \
|
||||
struct Name:public cv::GCPUStKernelImpl<Name, API, State> \
|
||||
|
||||
|
||||
class gapi::cpu::GOCVFunctor : public gapi::GFunctor
|
||||
{
|
||||
public:
|
||||
|
||||
@@ -208,6 +208,19 @@ public:
|
||||
// FIXME: Why it requires compile args?
|
||||
void reshape(const GMetaArgs& inMetas, const GCompileArgs& args);
|
||||
|
||||
/**
|
||||
* @brief Prepare inner kernels states for a new video-stream.
|
||||
*
|
||||
* GCompiled objects may be used to process video streams frame by frame.
|
||||
* In this case, a GCompiled is called on every image frame individually.
|
||||
* Starting OpenCV 4.4, some kernels in the graph may have their internal
|
||||
* states (see GAPI_OCV_KERNEL_ST for the OpenCV backend).
|
||||
* In this case, if user starts processing another video stream with
|
||||
* this GCompiled, this method needs to be called to let kernels re-initialize
|
||||
* their internal states to a new video stream.
|
||||
*/
|
||||
void prepareForNewStream();
|
||||
|
||||
protected:
|
||||
/// @private
|
||||
std::shared_ptr<Priv> m_priv;
|
||||
|
||||
Reference in New Issue
Block a user