mirror of
https://github.com/opencv/opencv.git
synced 2025-08-05 22:19:14 +08:00
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
This commit is contained in:
parent
f3237fdc6e
commit
a6ef9b4584
@ -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…
Reference in New Issue
Block a user