Merge pull request #16213 from TolyaTalamanov:at/lambdas-for-kernels

G-API: Using functors as kernel implementation

* Implement ability to create kernel impls from functors

* Clean up

* Replace make_ocv_functor to ocv_kernel

* Clean up

* Replace GCPUFunctor -> GOCVFunctor
* Move GOCVFunctor to cv::gapi::cpu namespace

* Implement override for rvalue and lvalue cases

* Fix comments to review

* Remove GAPI_EXPORT for template functions

* Fix indentation
pull/16619/head
Anatoliy Talamanov 5 years ago committed by GitHub
parent f3237fdc6e
commit a6ef9b4584
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
  1. 72
      modules/gapi/include/opencv2/gapi/cpu/gcpukernel.hpp
  2. 27
      modules/gapi/include/opencv2/gapi/gkernel.hpp
  3. 110
      modules/gapi/test/gapi_sample_pipelines.cpp

@ -72,6 +72,17 @@ namespace cpu
*/
GAPI_EXPORTS cv::gapi::GBackend backend();
/** @} */
class GOCVFunctor;
//! @cond IGNORED
template<typename K, typename Callable>
GOCVFunctor ocv_kernel(const Callable& c);
template<typename K, typename Callable>
GOCVFunctor ocv_kernel(Callable& c);
//! @endcond
} // namespace cpu
} // namespace gapi
@ -279,6 +290,12 @@ struct OCVCallHelper<Impl, std::tuple<Ins...>, std::tuple<Outs...> >
Impl::run(std::forward<Inputs>(ins)..., outs...);
postprocess(outs...);
}
template<typename... Outputs>
static void call(Impl& impl, Inputs&&... ins, Outputs&&... outs)
{
impl(std::forward<Inputs>(ins)..., outs...);
}
};
template<int... IIs, int... OIs>
@ -290,7 +307,17 @@ struct OCVCallHelper<Impl, std::tuple<Ins...>, std::tuple<Outs...> >
//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_and_postprocess<decltype(get_in<Ins>::get(ctx, IIs))...>
::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...>)
{
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)...);
}
static void call(GCPUContext &ctx)
@ -299,6 +326,16 @@ struct OCVCallHelper<Impl, std::tuple<Ins...>, std::tuple<Outs...> >
typename detail::MkSeq<sizeof...(Ins)>::type(),
typename detail::MkSeq<sizeof...(Outs)>::type());
}
// NB: Same as call but calling the object
// This necessary for kernel implementations that have a state
// and are represented as an object
static void callFunctor(cv::GCPUContext &ctx, Impl& impl)
{
call_impl(ctx, impl,
typename detail::MkSeq<sizeof...(Ins)>::type(),
typename detail::MkSeq<sizeof...(Outs)>::type());
}
};
} // namespace detail
@ -318,6 +355,39 @@ public:
#define GAPI_OCV_KERNEL(Name, API) struct Name: public cv::GCPUKernelImpl<Name, API>
class gapi::cpu::GOCVFunctor : public gapi::GFunctor
{
public:
using Impl = std::function<void(GCPUContext &)>;
GOCVFunctor(const char* id, const Impl& impl)
: gapi::GFunctor(id), impl_{GCPUKernel(impl)}
{
}
GKernelImpl impl() const override { return impl_; }
gapi::GBackend backend() const override { return gapi::cpu::backend(); }
private:
GKernelImpl impl_;
};
//! @cond IGNORED
template<typename K, typename Callable>
gapi::cpu::GOCVFunctor gapi::cpu::ocv_kernel(Callable& c)
{
using P = detail::OCVCallHelper<Callable, typename K::InArgs, typename K::OutArgs>;
return GOCVFunctor(K::id(), std::bind(&P::callFunctor, std::placeholders::_1, std::ref(c)));
}
template<typename K, typename Callable>
gapi::cpu::GOCVFunctor gapi::cpu::ocv_kernel(const Callable& c)
{
using P = detail::OCVCallHelper<Callable, typename K::InArgs, typename K::OutArgs>;
return GOCVFunctor(K::id(), std::bind(&P::callFunctor, std::placeholders::_1, c));
}
//! @endcond
} // namespace cv
#endif // OPENCV_GAPI_GCPUKERNEL_HPP

@ -383,6 +383,20 @@ namespace std
namespace cv {
namespace gapi {
class GFunctor
{
public:
virtual cv::GKernelImpl impl() const = 0;
virtual cv::gapi::GBackend backend() const = 0;
const char* id() const { return m_id; }
virtual ~GFunctor() = default;
protected:
GFunctor(const char* id) : m_id(id) { };
private:
const char* m_id;
};
/** \addtogroup gapi_compile_args
* @{
*/
@ -460,6 +474,10 @@ namespace gapi {
}
public:
void include(const GFunctor& functor)
{
m_id_kernels[functor.id()] = std::make_pair(functor.backend(), functor.impl());
}
/**
* @brief Returns total number of kernels
* in the package (across all backends included)
@ -618,6 +636,15 @@ namespace gapi {
return pkg;
};
template<typename... FF>
GKernelPackage kernels(FF&... functors)
{
GKernelPackage pkg;
int unused[] = { 0, (pkg.include(functors), 0)... };
cv::util::suppress_unused_warning(unused);
return pkg;
};
/** @} */
// FYI - this function is already commented above

@ -11,6 +11,8 @@
#include <ade/util/iota_range.hpp>
#include "logger.hpp"
#include <opencv2/gapi/core.hpp>
namespace opencv_test
{
@ -42,6 +44,11 @@ namespace
}
};
G_TYPED_KERNEL(GCustom, <GMat(GMat)>, "org.opencv.test.custom")
{
static GMatDesc outMeta(GMatDesc in) { return in; }
};
// These definitons test the correct macro work if the kernel has multiple output values
G_TYPED_KERNEL(GRetGArrayTupleOfGMat2Kernel, <GArray<std::tuple<GMat, GMat>>(GMat, Scalar)>, "org.opencv.test.retarrayoftupleofgmat2kernel") {};
G_TYPED_KERNEL(GRetGArraTupleyOfGMat3Kernel, <GArray<std::tuple<GMat, GMat, GMat>>(GMat)>, "org.opencv.test.retarrayoftupleofgmat3kernel") {};
@ -335,4 +342,107 @@ TEST(GAPI_Pipeline, CanUseOwnMatAsOutput)
EXPECT_NO_THROW(comp.apply({in_own_mat}, {out_own_mat}));
}
TEST(GAPI_Pipeline, CreateKernelImplFromLambda)
{
cv::Size size(300, 300);
int type = CV_8UC3;
cv::Mat in_mat(size, type);
cv::randu(in_mat, cv::Scalar::all(0), cv::Scalar::all(255));
int value = 5;
cv::GMat in;
cv::GMat out = GCustom::on(in);
cv::GComputation comp(in, out);
// OpenCV //////////////////////////////////////////////////////////////////////////
auto ref_mat = in_mat + value;
// G-API //////////////////////////////////////////////////////////////////////////
auto impl = cv::gapi::cpu::ocv_kernel<GCustom>([&value](const cv::Mat& src, cv::Mat& dst)
{
dst = src + value;
});
cv::Mat out_mat;
auto pkg = cv::gapi::kernels(impl);
comp.apply(in_mat, out_mat, cv::compile_args(pkg));
EXPECT_EQ(0, cv::norm(out_mat, ref_mat));
}
TEST(GAPI_Pipeline, ReplaceDefaultByLambda)
{
cv::Size size(300, 300);
int type = CV_8UC3;
cv::Mat in_mat1(size, type);
cv::Mat in_mat2(size, type);
cv::randu(in_mat2, cv::Scalar::all(0), cv::Scalar::all(255));
cv::randu(in_mat1, cv::Scalar::all(0), cv::Scalar::all(255));
cv::GMat in1, in2;
cv::GMat out = cv::gapi::add(in1, in2);
cv::GComputation comp(cv::GIn(in1, in2), cv::GOut(out));
// OpenCV //////////////////////////////////////////////////////////////////////////
cv::Mat ref_mat = in_mat1 + in_mat2;
// G-API //////////////////////////////////////////////////////////////////////////
bool is_called = false;
auto impl = cv::gapi::cpu::ocv_kernel<cv::gapi::core::GAdd>([&is_called]
(const cv::Mat& src1, const cv::Mat& src2, int, cv::Mat& dst)
{
is_called = true;
dst = src1 + src2;
});
cv::Mat out_mat;
auto pkg = cv::gapi::kernels(impl);
comp.apply(cv::gin(in_mat1, in_mat2), cv::gout(out_mat), cv::compile_args(pkg));
EXPECT_EQ(0, cv::norm(out_mat, ref_mat));
EXPECT_TRUE(is_called);
}
struct AddImpl
{
void operator()(const cv::Mat& in1, const cv::Mat& in2, int, cv::Mat& out)
{
out = in1 + in2;
is_called = true;
}
bool is_called = false;
};
TEST(GAPI_Pipeline, ReplaceDefaultByFunctor)
{
cv::Size size(300, 300);
int type = CV_8UC3;
cv::Mat in_mat1(size, type);
cv::Mat in_mat2(size, type);
cv::randu(in_mat2, cv::Scalar::all(0), cv::Scalar::all(255));
cv::randu(in_mat1, cv::Scalar::all(0), cv::Scalar::all(255));
cv::GMat in1, in2;
cv::GMat out = cv::gapi::add(in1, in2);
cv::GComputation comp(cv::GIn(in1, in2), cv::GOut(out));
// OpenCV //////////////////////////////////////////////////////////////////////////
cv::Mat ref_mat = in_mat1 + in_mat2;
// G-API ///////////////////////////////////////////////////////////////////////////
AddImpl f;
EXPECT_FALSE(f.is_called);
auto impl = cv::gapi::cpu::ocv_kernel<cv::gapi::core::GAdd>(f);
cv::Mat out_mat;
auto pkg = cv::gapi::kernels(impl);
comp.apply(cv::gin(in_mat1, in_mat2), cv::gout(out_mat), cv::compile_args(pkg));
EXPECT_EQ(0, cv::norm(out_mat, ref_mat));
EXPECT_TRUE(f.is_called);
}
} // namespace opencv_test

Loading…
Cancel
Save