#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>
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,
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
}
};
+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
//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)
}
};
+// 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 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(&CallHelper::call); }
+};
+
+template<class Impl, class K, class S>
+class GCPUStKernelImpl: public cv::detail::KernelTag
{
- using P = detail::OCVCallHelper<Impl, typename K::InArgs, typename K::OutArgs>;
+ 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(&P::call); }
+ 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:
// 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;
//
// If not, we need to introduce that!
using GCPUModel = ade::TypedGraph
- < cv::gimpl::Unit
+ < cv::gimpl::CPUUnit
, cv::gimpl::Protocol
>;
// FIXME: Same issue with Typed and ConstTyped
using GConstGCPUModel = ade::ConstTypedGraph
- < cv::gimpl::Unit
+ < cv::gimpl::CPUUnit
, cv::gimpl::Protocol
>;
{
GCPUModel gm(graph);
auto cpu_impl = cv::util::any_cast<cv::GCPUKernel>(impl.opaque);
- gm.metadata(op_node).set(cv::gimpl::Unit{cpu_impl});
+ gm.metadata(op_node).set(cv::gimpl::CPUUnit{cpu_impl});
}
virtual EPtr compile(const ade::Graph &graph,
{
// Convert list of operations (which is topologically sorted already)
// into an execution script.
+ GConstGCPUModel gcm(m_g);
for (auto &nh : nodes)
{
switch (m_gm.metadata(nh).get<NodeType>().t)
{
- case NodeType::OP: m_script.push_back({nh, GModel::collectOutputMeta(m_gm, nh)}); break;
+ case NodeType::OP:
+ {
+ m_script.push_back({nh, GModel::collectOutputMeta(m_gm, nh)});
+
+ // If kernel is stateful then prepare storage for its state.
+ GCPUKernel k = gcm.metadata(nh).get<CPUUnit>().k;
+ if (k.m_isStateful)
+ {
+ m_nodesToStates[nh] = GArg{ };
+ }
+ break;
+ }
case NodeType::DATA:
{
m_dataNodes.push_back(nh);
default: util::throw_error(std::logic_error("Unsupported NodeType type"));
}
}
+
+ // For each stateful kernel call 'setup' user callback to initialize state.
+ setupKernelStates();
}
// FIXME: Document what it does
}
}
+void cv::gimpl::GCPUExecutable::setupKernelStates()
+{
+ GConstGCPUModel gcm(m_g);
+ for (auto& nodeToState : m_nodesToStates)
+ {
+ auto& kernelNode = nodeToState.first;
+ auto& kernelState = nodeToState.second;
+
+ const GCPUKernel& kernel = gcm.metadata(kernelNode).get<CPUUnit>().k;
+ kernel.m_setupF(GModel::collectInputMeta(m_gm, kernelNode),
+ m_gm.metadata(kernelNode).get<Op>().args,
+ kernelState);
+ }
+}
+
+void cv::gimpl::GCPUExecutable::handleNewStream()
+{
+ m_newStreamStarted = true;
+}
+
void cv::gimpl::GCPUExecutable::run(std::vector<InObj> &&input_objs,
std::vector<OutObj> &&output_objs)
{
}
}
+ // In case if new video-stream happens - for each stateful kernel
+ // call 'setup' user callback to re-initialize state.
+ if (m_newStreamStarted)
+ {
+ setupKernelStates();
+ m_newStreamStarted = false;
+ }
+
// OpenCV backend execution is not a rocket science at all.
// Simply invoke our kernels in the proper order.
GConstGCPUModel gcm(m_g);
// Obtain our real execution unit
// TODO: Should kernels be copyable?
- GCPUKernel k = gcm.metadata(op_info.nh).get<Unit>().k;
+ GCPUKernel k = gcm.metadata(op_info.nh).get<CPUUnit>().k;
// Initialize kernel's execution context:
// - Input parameters
using namespace std::placeholders;
ade::util::transform(op.args,
- std::back_inserter(context.m_args),
- std::bind(&GCPUExecutable::packArg, this, _1));
+ std::back_inserter(context.m_args),
+ std::bind(&GCPUExecutable::packArg, this, _1));
// - Output parameters.
// FIXME: pre-allocate internal Mats, etc, according to the known meta
context.m_results[out_port] = magazine::getObjPtr(m_res, out_desc);
}
+ // For stateful kernel add state to its execution context
+ if (k.m_isStateful)
+ {
+ context.m_state = m_nodesToStates.at(op_info.nh);
+ }
+
// Now trigger the executable unit
- k.apply(context);
+ k.m_runF(context);
//As Kernels are forbidden to allocate memory for (Mat) outputs,
//this code seems redundant, at least for Mats
namespace cv { namespace gimpl {
-struct Unit
+struct CPUUnit
{
static const char *name() { return "HostKernel"; }
GCPUKernel k;
// Actual data of all resources in graph (both internal and external)
Mag m_res;
GArg packArg(const GArg &arg);
+ void setupKernelStates();
+
+ // TODO: Check that it is thread-safe
+ std::unordered_map<ade::NodeHandle, GArg,
+ ade::HandleHasher<ade::Node>> m_nodesToStates;
+
+ bool m_newStreamStarted = false;
public:
GCPUExecutable(const ade::Graph &graph,
util::throw_error(std::logic_error("GCPUExecutable::reshape() should never be called"));
}
+ virtual void handleNewStream() override;
+
virtual void run(std::vector<InObj> &&input_objs,
std::vector<OutObj> &&output_objs) override;
};
{
}
-cv::GCPUKernel::GCPUKernel(const GCPUKernel::F &f)
- : m_f(f)
+cv::GCPUKernel::GCPUKernel(const GCPUKernel::RunF &runF, const GCPUKernel::SetupF &setupF)
+ : m_runF(runF), m_setupF(setupF), m_isStateful(m_setupF != nullptr)
{
}
-
-void cv::GCPUKernel::apply(GCPUContext &ctx)
-{
- GAPI_Assert(m_f);
- m_f(ctx);
-}
//
// If not, we need to introduce that!
using GOCLModel = ade::TypedGraph
- < cv::gimpl::Unit
+ < cv::gimpl::OCLUnit
, cv::gimpl::Protocol
>;
// FIXME: Same issue with Typed and ConstTyped
using GConstGOCLModel = ade::ConstTypedGraph
- < cv::gimpl::Unit
+ < cv::gimpl::OCLUnit
, cv::gimpl::Protocol
>;
{
GOCLModel gm(graph);
auto ocl_impl = cv::util::any_cast<cv::GOCLKernel>(impl.opaque);
- gm.metadata(op_node).set(cv::gimpl::Unit{ocl_impl});
+ gm.metadata(op_node).set(cv::gimpl::OCLUnit{ocl_impl});
}
virtual EPtr compile(const ade::Graph &graph,
// Obtain our real execution unit
// TODO: Should kernels be copyable?
- GOCLKernel k = gcm.metadata(op_info.nh).get<Unit>().k;
+ GOCLKernel k = gcm.metadata(op_info.nh).get<OCLUnit>().k;
// Initialize kernel's execution context:
// - Input parameters
namespace cv { namespace gimpl {
-struct Unit
+struct OCLUnit
{
static const char *name() { return "OCLKernel"; }
GOCLKernel k;
context.m_args.emplace_back(m_ftpr.get());
- k.apply(context);
+ k.m_runF(context);
for (auto &it : output_objs) magazine::writeBack(m_res, it.first, it.second);
}
m_metas = inMetas;
}
+void cv::GCompiled::Priv::prepareForNewStream()
+{
+ GAPI_Assert(m_exec);
+ m_exec->prepareForNewStream();
+}
+
const cv::gimpl::GModel::Graph& cv::GCompiled::Priv::model() const
{
GAPI_Assert(nullptr != m_exec);
{
m_priv->reshape(inMetas, args);
}
+
+void cv::GCompiled::prepareForNewStream()
+{
+ m_priv->prepareForNewStream();
+}
bool canReshape() const;
void reshape(const GMetaArgs& inMetas, const GCompileArgs &args);
+ void prepareForNewStream();
void run(cv::gimpl::GRuntimeArgs &&args);
const GMetaArgs& metas() const;
passes::inferMeta(ctx, true);
m_ops[0].isl_exec->reshape(g, args);
}
+
+void cv::gimpl::GExecutor::prepareForNewStream()
+{
+ for (auto &op : m_ops)
+ {
+ op.isl_exec->handleNewStream();
+ }
+}
bool canReshape() const;
void reshape(const GMetaArgs& inMetas, const GCompileArgs& args);
+ void prepareForNewStream();
+
const GModel::Graph& model() const; // FIXME: make it ConstGraph?
};
}
}
};
+ bool islandsRecompiled = false;
const auto new_meta = cv::descr_of(ins); // 0
if (gm.metadata().contains<OriginalInputMeta>()) // (1)
{
}
update_int_metas(); // (7)
m_reshapable = util::make_optional(is_reshapable);
+
+ islandsRecompiled = true;
}
else // (8)
{
for (auto &&out_eh : op.nh->outNodes()) {
out_queues.push_back(reader_queues(*m_island_graph, out_eh));
}
- op.isl_exec->handleNewStream();
+
+ // If Island Executable is recompiled, all its stuff including internal kernel states
+ // are recreated and re-initialized automatically.
+ // But if not, we should notify Island Executable about new started stream to let it update
+ // its internal variables.
+ if (!islandsRecompiled)
+ {
+ op.isl_exec->handleNewStream();
+ }
m_threads.emplace_back(islandActorThread,
op.in_objects,
--- /dev/null
+// 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) 2020 Intel Corporation
+
+#ifndef OPENCV_GAPI_OCV_STATEFUL_KERNEL_TESTS_UTILS_HPP
+#define OPENCV_GAPI_OCV_STATEFUL_KERNEL_TESTS_UTILS_HPP
+
+#include "../test_precomp.hpp"
+
+// 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 DEFINE_INITIALIZER(Name, StateType, ...) \
+struct Name \
+{ \
+ static StateType value() \
+ { \
+ return __VA_ARGS__; \
+ } \
+} \
+
+namespace opencv_test
+{
+namespace
+{
+struct UserStruct
+{
+ UserStruct() = default;
+ UserStruct(short myShortVal, float myFloatVal):
+ _myShortVal(myShortVal),
+ _myFloatVal(myFloatVal) { }
+
+ bool operator==(const UserStruct& rhs) const
+ {
+ return ((_myShortVal == rhs._myShortVal) &&
+ (_myFloatVal == rhs._myFloatVal));
+ }
+
+private:
+ short _myShortVal;
+ float _myFloatVal;
+};
+} // anonymous namespace
+} // opencv_test
+
+#endif // OPENCV_GAPI_OCV_STATEFUL_KERNEL_TESTS_UTILS_HPP
--- /dev/null
+// 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) 2020 Intel Corporation
+
+#include "gapi_ocv_stateful_kernel_test_utils.hpp"
+#include <opencv2/gapi/cpu/core.hpp>
+#include <opencv2/gapi/streaming/cap.hpp>
+
+namespace opencv_test
+{
+//TODO: test OT, Background Subtractor, Kalman with 3rd version of API
+//----------------------------------------------- Simple tests ------------------------------------------------
+namespace
+{
+ inline void initTestDataPath()
+ {
+#ifndef WINRT
+ static bool initialized = false;
+ if (!initialized)
+ {
+ // Since G-API has no own test data (yet), it is taken from the common space
+ const char* testDataPath = getenv("OPENCV_TEST_DATA_PATH");
+ GAPI_Assert(testDataPath != nullptr);
+
+ cvtest::addDataSearchPath(testDataPath);
+ initialized = true;
+ }
+#endif // WINRT
+ }
+
+ G_TYPED_KERNEL(GCountCalls, <cv::GOpaque<int>(GMat)>, "org.opencv.test.count_calls")
+ {
+ static GOpaqueDesc outMeta(GMatDesc /* in */) { return empty_gopaque_desc(); }
+ };
+
+ GAPI_OCV_KERNEL_ST(GOCVCountCalls, GCountCalls, int)
+ {
+ static void setup(const cv::GMatDesc &/* in */, std::shared_ptr<int> &state)
+ {
+ state.reset(new int{ });
+ }
+
+ static void run(const cv::Mat &/* in */, int &out, int& state)
+ {
+ out = ++state;
+ }
+ };
+
+ G_TYPED_KERNEL(GIsStateUpToDate, <cv::GOpaque<bool>(GMat)>,
+ "org.opencv.test.is_state_up-to-date")
+ {
+ static GOpaqueDesc outMeta(GMatDesc /* in */) { return empty_gopaque_desc(); }
+ };
+
+ GAPI_OCV_KERNEL_ST(GOCVIsStateUpToDate, GIsStateUpToDate, cv::Size)
+ {
+ static void setup(const cv::GMatDesc &in,
+ std::shared_ptr<cv::Size> &state)
+ {
+ state.reset(new cv::Size(in.size));
+ }
+
+ static void run(const cv::Mat &in , bool &out, cv::Size& state)
+ {
+ out = in.size() == state;
+ }
+ };
+
+ G_TYPED_KERNEL(GStInvalidResize, <GMat(GMat,Size,double,double,int)>, "org.opencv.test.st_invalid_resize")
+ {
+ static GMatDesc outMeta(GMatDesc in, Size, double, double, int) { return in; }
+ };
+
+ GAPI_OCV_KERNEL_ST(GOCVStInvalidResize, GStInvalidResize, int)
+ {
+ static void setup(const cv::GMatDesc, cv::Size, double, double, int,
+ std::shared_ptr<int> &/* state */)
+ { }
+
+ static void run(const cv::Mat& in, cv::Size sz, double fx, double fy, int interp,
+ cv::Mat &out, int& /* state */)
+ {
+ cv::resize(in, out, sz, fx, fy, interp);
+ }
+ };
+};
+
+TEST(StatefulKernel, StateIsMutableInRuntime)
+{
+ constexpr int expectedCallsCount = 10;
+
+ cv::Mat dummyIn { 1, 1, CV_8UC1 };
+ int actualCallsCount = 0;
+
+ // Declaration of G-API expression
+ GMat in;
+ GOpaque<int> out = GCountCalls::on(in);
+ cv::GComputation comp(cv::GIn(in), cv::GOut(out));
+
+ const auto pkg = cv::gapi::kernels<GOCVCountCalls>();
+
+ // Compilation of G-API expression
+ auto callsCounter = comp.compile(cv::descr_of(dummyIn), cv::compile_args(pkg));
+
+ // Simulating video stream: call GCompiled multiple times
+ for (int i = 0; i < expectedCallsCount; i++)
+ {
+ callsCounter(cv::gin(dummyIn), cv::gout(actualCallsCount));
+ EXPECT_EQ(i + 1, actualCallsCount);
+ }
+
+ // End of "video stream"
+ EXPECT_EQ(expectedCallsCount, actualCallsCount);
+
+ // User asks G-API to prepare for a new stream
+ callsCounter.prepareForNewStream();
+ callsCounter(cv::gin(dummyIn), cv::gout(actualCallsCount));
+ EXPECT_EQ(1, actualCallsCount);
+
+}
+
+TEST(StatefulKernel, StateIsAutoResetForNewStream)
+{
+ initTestDataPath();
+
+ cv::GMat in;
+ GOpaque<bool> out = GIsStateUpToDate::on(in);
+ cv::GComputation c(cv::GIn(in), cv::GOut(out));
+
+ const auto pkg = cv::gapi::kernels<GOCVIsStateUpToDate>();
+
+ // Compilation & testing
+ auto ccomp = c.compileStreaming(cv::compile_args(pkg));
+
+ ccomp.setSource(gapi::wip::make_src<cv::gapi::wip::GCaptureSource>
+ (findDataFile("cv/video/768x576.avi")));
+ ccomp.start();
+ EXPECT_TRUE(ccomp.running());
+
+ // Process the full video
+ bool isStateUpToDate = false;
+ while (ccomp.pull(cv::gout(isStateUpToDate))) {
+ EXPECT_TRUE(isStateUpToDate);
+ }
+ EXPECT_FALSE(ccomp.running());
+
+ ccomp.setSource(gapi::wip::make_src<cv::gapi::wip::GCaptureSource>
+ (findDataFile("cv/video/1920x1080.avi")));
+ ccomp.start();
+ EXPECT_TRUE(ccomp.running());
+
+ while (ccomp.pull(cv::gout(isStateUpToDate))) {
+ EXPECT_TRUE(isStateUpToDate);
+ }
+ EXPECT_FALSE(ccomp.running());
+}
+
+TEST(StatefulKernel, InvalidReallocatingKernel)
+{
+ cv::GMat in, out;
+ cv::Mat in_mat(500, 500, CV_8UC1), out_mat;
+ out = GStInvalidResize::on(in, cv::Size(300, 300), 0.0, 0.0, cv::INTER_LINEAR);
+
+ const auto pkg = cv::gapi::kernels<GOCVStInvalidResize>();
+ cv::GComputation comp(cv::GIn(in), cv::GOut(out));
+
+ EXPECT_THROW(comp.apply(in_mat, out_mat, cv::compile_args(pkg)), std::logic_error);
+
+}
+//-------------------------------------------------------------------------------------------------------------
+
+
+//------------------------------------------- Typed tests on setup() ------------------------------------------
+namespace
+{
+template<typename Tuple>
+struct SetupStateTypedTest : public ::testing::Test
+{
+ using StateT = typename std::tuple_element<0, Tuple>::type;
+ using SetupT = typename std::tuple_element<1, Tuple>::type;
+
+ G_TYPED_KERNEL(GReturnState, <cv::GOpaque<StateT>(GMat)>, "org.opencv.test.return_state")
+ {
+ static GOpaqueDesc outMeta(GMatDesc /* in */) { return empty_gopaque_desc(); }
+ };
+
+ GAPI_OCV_KERNEL_ST(GOCVReturnState, GReturnState, StateT)
+ {
+ static void setup(const cv::GMatDesc &/* in */, std::shared_ptr<StateT> &state)
+ {
+ // Don't use input cv::GMatDesc intentionally
+ state.reset(new StateT(SetupT::value()));
+ }
+
+ static void run(const cv::Mat &/* in */, StateT &out, StateT& state)
+ {
+ out = state;
+ }
+ };
+};
+
+TYPED_TEST_CASE_P(SetupStateTypedTest);
+} // namespace
+
+
+TYPED_TEST_P(SetupStateTypedTest, ReturnInitializedState)
+{
+ using StateType = typename TestFixture::StateT;
+ using SetupType = typename TestFixture::SetupT;
+
+ cv::Mat dummyIn { 1, 1, CV_8UC1 };
+ StateType retState { };
+
+ GMat in;
+ auto out = TestFixture::GReturnState::on(in);
+ cv::GComputation comp(cv::GIn(in), cv::GOut(out));
+
+ const auto pkg = cv::gapi::kernels<typename TestFixture::GOCVReturnState>();
+ comp.apply(cv::gin(dummyIn), cv::gout(retState), cv::compile_args(pkg));
+
+ EXPECT_EQ(SetupType::value(), retState);
+}
+
+REGISTER_TYPED_TEST_CASE_P(SetupStateTypedTest,
+ ReturnInitializedState);
+
+
+DEFINE_INITIALIZER(CharValue, char, 'z');
+DEFINE_INITIALIZER(IntValue, int, 7);
+DEFINE_INITIALIZER(FloatValue, float, 42.f);
+DEFINE_INITIALIZER(UcharPtrValue, uchar*, nullptr);
+namespace
+{
+using Std3IntArray = std::array<int, 3>;
+}
+DEFINE_INITIALIZER(StdArrayValue, Std3IntArray, { 1, 2, 3 });
+DEFINE_INITIALIZER(UserValue, UserStruct, { 5, 7.f });
+
+using TypesToVerify = ::testing::Types<std::tuple<char, CharValue>,
+ std::tuple<int, IntValue>,
+ std::tuple<float, FloatValue>,
+ std::tuple<uchar*, UcharPtrValue>,
+ std::tuple<std::array<int, 3>, StdArrayValue>,
+ std::tuple<UserStruct, UserValue>>;
+
+INSTANTIATE_TYPED_TEST_CASE_P(SetupStateTypedInst, SetupStateTypedTest, TypesToVerify);
+//-------------------------------------------------------------------------------------------------------------
+
+} // opencv_test