未验证 提交 5224d016 编写于 作者: R Ruslan Garnov 提交者: GitHub

Merge pull request #18339 from rgarnov:rg/rmat_integration

[GAPI] RMat integration into the framework

* RMat integration

* Added initialization of input mat in GArray initialization tests

* Fixed klocwork warnings in RMat tests, changed argument order in EXPECT_EQ
上级 199687a1
......@@ -78,6 +78,7 @@ set(gapi_srcs
src/api/ginfer.cpp
src/api/ft_render.cpp
src/api/media.cpp
src/api/rmat.cpp
# Compiler part
src/compiler/gmodel.cpp
......
......@@ -24,6 +24,7 @@
#include <opencv2/gapi/gtype_traits.hpp>
#include <opencv2/gapi/gmetaarg.hpp>
#include <opencv2/gapi/streaming/source.hpp>
#include <opencv2/gapi/rmat.hpp>
namespace cv {
......@@ -94,6 +95,7 @@ using GRunArg = util::variant<
#if !defined(GAPI_STANDALONE)
cv::UMat,
#endif // !defined(GAPI_STANDALONE)
cv::RMat,
cv::gapi::wip::IStreamSource::Ptr,
cv::Mat,
cv::Scalar,
......@@ -144,6 +146,7 @@ using GRunArgP = util::variant<
cv::UMat*,
#endif // !defined(GAPI_STANDALONE)
cv::Mat*,
cv::RMat*,
cv::Scalar*,
cv::detail::VectorRef,
cv::detail::OpaqueRef
......
......@@ -65,6 +65,8 @@ public:
using GMat::GMat;
};
class RMat;
/** @} */
/**
......@@ -113,6 +115,8 @@ struct GAPI_EXPORTS GMatDesc
// and as a 3-channel planar mat with height divided by 3)
bool canDescribe(const cv::Mat& mat) const;
bool canDescribe(const cv::RMat& mat) const;
// Meta combinator: return a new GMatDesc which differs in size by delta
// (all other fields are taken unchanged from this GMatDesc)
// FIXME: a better name?
......@@ -209,6 +213,8 @@ namespace gapi { namespace own {
GAPI_EXPORTS GMatDesc descr_of(const Mat &mat);
}}//gapi::own
GAPI_EXPORTS GMatDesc descr_of(const RMat &mat);
#if !defined(GAPI_STANDALONE)
GAPI_EXPORTS GMatDesc descr_of(const cv::Mat &mat);
#else
......
......@@ -121,6 +121,7 @@ namespace detail
template<> struct GTypeOf<cv::UMat> { using type = cv::GMat; };
#endif // !defined(GAPI_STANDALONE)
template<> struct GTypeOf<cv::Mat> { using type = cv::GMat; };
template<> struct GTypeOf<cv::RMat> { using type = cv::GMat; };
template<> struct GTypeOf<cv::Scalar> { using type = cv::GScalar; };
template<typename U> struct GTypeOf<std::vector<U> > { using type = cv::GArray<U>; };
template<typename U> struct GTypeOf { using type = cv::GOpaque<U>;};
......
......@@ -254,6 +254,18 @@ namespace cv { namespace gapi { namespace own {
*this = std::move(tmp);
}
/** @brief Creates a full copy of the matrix and the underlying data.
The method creates a full copy of the matrix. The original step[] is not taken into account.
So, the copy has a continuous buffer occupying total() * elemSize() bytes.
*/
Mat clone() const
{
Mat m;
copyTo(m);
return m;
}
/** @brief Copies the matrix to another one.
The method copies the matrix data to another matrix. Before copying the data, the method invokes :
......
......@@ -8,6 +8,7 @@
#define OPENCV_GAPI_RMAT_HPP
#include <opencv2/gapi/gmat.hpp>
#include <opencv2/gapi/own/exports.hpp>
namespace cv {
......@@ -31,7 +32,7 @@ namespace cv {
// performCalculations(in_view, out_view);
// // data from out_view is transferred to the device when out_view is destroyed
// }
class RMat
class GAPI_EXPORTS RMat
{
public:
// A lightweight wrapper on image data:
......@@ -39,20 +40,20 @@ public:
// - Doesn't implement copy semantics (it's assumed that a view is created each time
// wrapped data is being accessed);
// - Has an optional callback which is called when the view is destroyed.
class View
class GAPI_EXPORTS View
{
public:
using DestroyCallback = std::function<void()>;
View() = default;
View(const GMatDesc& desc, uchar* data, size_t step = 0u, DestroyCallback&& cb = nullptr)
: m_desc(desc), m_data(data), m_step(step == 0u ? elemSize()*cols() : step), m_cb(cb)
: m_desc(desc), m_data(data), m_step(step == 0u ? elemSize()*cols() : step), m_cb(std::move(cb))
{}
View(const View&) = delete;
View(View&&) = default;
View& operator=(const View&) = delete;
View& operator=(View&&) = default;
View(View&&) = default;
View& operator=(View&& v);
~View() { if (m_cb) m_cb(); }
cv::Size size() const { return m_desc.size; }
......@@ -88,7 +89,7 @@ public:
// Implementation is responsible for setting the appropriate callback to
// the view when accessed for writing, to ensure that the data from the view
// is transferred to the device when the view is destroyed
virtual View access(Access) const = 0;
virtual View access(Access) = 0;
};
using AdapterP = std::shared_ptr<Adapter>;
......
......@@ -2,7 +2,7 @@
// 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 Intel Corporation
// Copyright (C) 2018-2020 Intel Corporation
#include "precomp.hpp"
......@@ -103,38 +103,34 @@ namespace cv {
namespace gimpl {
namespace magazine {
// FIXME implement the below functions with visit()?
namespace {
// Utility function, used in both bindInArg and bindOutArg,
// implements default RMat bind behaviour (if backend doesn't handle RMats in specific way):
// view + wrapped cv::Mat are placed into the magazine
void bindRMat(Mag& mag, const RcDesc& rc, const cv::RMat& rmat, RMat::Access a)
{
auto& matv = mag.template slot<RMat::View>()[rc.id];
matv = rmat.access(a);
mag.template slot<cv::Mat>()[rc.id] = asMat(matv);
}
} // anonymous namespace
void bindInArg(Mag& mag, const RcDesc &rc, const GRunArg &arg, bool is_umat)
// FIXME implement the below functions with visit()?
void bindInArg(Mag& mag, const RcDesc &rc, const GRunArg &arg, HandleRMat handleRMat)
{
switch (rc.shape)
{
case GShape::GMAT:
{
switch (arg.index())
{
case GRunArg::index_of<cv::Mat>() :
if (is_umat)
{
#if !defined(GAPI_STANDALONE)
auto& mag_umat = mag.template slot<cv::UMat>()[rc.id];
mag_umat = util::get<cv::Mat>(arg).getUMat(ACCESS_READ);
#else
util::throw_error(std::logic_error("UMat is not supported in standalone build"));
#endif // !defined(GAPI_STANDALONE)
}
else
{
auto& mag_mat = mag.template slot<cv::Mat>()[rc.id];
mag_mat = util::get<cv::Mat>(arg);
}
break;
default: util::throw_error(std::logic_error("content type of the runtime argument does not match to resource description ?"));
}
// In case of handleRMat == SKIP
// We assume that backend can work with some device-specific RMats
// and will handle them in some specific way, so just return
if (handleRMat == HandleRMat::SKIP) return;
GAPI_Assert(arg.index() == GRunArg::index_of<cv::RMat>());
bindRMat(mag, rc, util::get<cv::RMat>(arg), RMat::Access::R);
break;
}
case GShape::GSCALAR:
{
auto& mag_scalar = mag.template slot<cv::Scalar>()[rc.id];
......@@ -159,32 +155,18 @@ void bindInArg(Mag& mag, const RcDesc &rc, const GRunArg &arg, bool is_umat)
}
}
void bindOutArg(Mag& mag, const RcDesc &rc, const GRunArgP &arg, bool is_umat)
void bindOutArg(Mag& mag, const RcDesc &rc, const GRunArgP &arg, HandleRMat handleRMat)
{
switch (rc.shape)
{
case GShape::GMAT:
{
switch (arg.index())
{
case GRunArgP::index_of<cv::Mat*>() :
if (is_umat)
{
#if !defined(GAPI_STANDALONE)
auto& mag_umat = mag.template slot<cv::UMat>()[rc.id];
mag_umat = util::get<cv::Mat*>(arg)->getUMat(ACCESS_RW);
#else
util::throw_error(std::logic_error("UMat is not supported in standalone build"));
#endif // !defined(GAPI_STANDALONE)
}
else
{
auto& mag_mat = mag.template slot<cv::Mat>()[rc.id];
mag_mat = *util::get<cv::Mat*>(arg);
}
break;
default: util::throw_error(std::logic_error("content type of the runtime argument does not match to resource description ?"));
}
// In case of handleRMat == SKIP
// We assume that backend can work with some device-specific RMats
// and will handle them in some specific way, so just return
if (handleRMat == HandleRMat::SKIP) return;
GAPI_Assert(arg.index() == GRunArgP::index_of<cv::RMat*>());
bindRMat(mag, rc, *util::get<cv::RMat*>(arg), RMat::Access::W);
break;
}
......@@ -248,7 +230,7 @@ cv::GRunArg getArg(const Mag& mag, const RcDesc &ref)
// Wrap associated CPU object (either host or an internal one)
switch (ref.shape)
{
case GShape::GMAT: return GRunArg(mag.template slot<cv::Mat>().at(ref.id));
case GShape::GMAT: return GRunArg(mag.template slot<cv::RMat>().at(ref.id));
case GShape::GSCALAR: return GRunArg(mag.template slot<cv::Scalar>().at(ref.id));
// Note: .at() is intentional for GArray and GOpaque as objects MUST be already there
// (and constructed by either bindIn/Out or resetInternal)
......@@ -300,46 +282,15 @@ cv::GRunArgP getObjPtr(Mag& mag, const RcDesc &rc, bool is_umat)
}
}
void writeBack(const Mag& mag, const RcDesc &rc, GRunArgP &g_arg, bool is_umat)
void writeBack(const Mag& mag, const RcDesc &rc, GRunArgP &g_arg)
{
switch (rc.shape)
{
case GShape::GARRAY:
// Do nothing - should we really do anything here?
break;
case GShape::GOPAQUE:
// Do nothing - should we really do anything here?
break;
case GShape::GMAT:
{
//simply check that memory was not reallocated, i.e.
//both instances of Mat pointing to the same memory
uchar* out_arg_data = nullptr;
switch (g_arg.index())
{
case GRunArgP::index_of<cv::Mat*>() : out_arg_data = util::get<cv::Mat*>(g_arg)->data; break;
#if !defined(GAPI_STANDALONE)
case GRunArgP::index_of<cv::UMat*>() : out_arg_data = (util::get<cv::UMat*>(g_arg))->getMat(ACCESS_RW).data; break;
#endif // !defined(GAPI_STANDALONE)
default: util::throw_error(std::logic_error("content type of the runtime argument does not match to resource description ?"));
}
if (is_umat)
{
#if !defined(GAPI_STANDALONE)
auto& in_mag = mag.template slot<cv::UMat>().at(rc.id);
GAPI_Assert((out_arg_data == (in_mag.getMat(ACCESS_RW).data)) && " data for output parameters was reallocated ?");
#else
util::throw_error(std::logic_error("UMat is not supported in standalone build"));
#endif // !defined(GAPI_STANDALONE)
}
else
{
auto& in_mag = mag.template slot<cv::Mat>().at(rc.id);
GAPI_Assert((out_arg_data == in_mag.data) && " data for output parameters was reallocated ?");
}
case GShape::GOPAQUE:
// Do nothing - should we really do anything here?
break;
}
case GShape::GSCALAR:
{
......@@ -357,6 +308,32 @@ void writeBack(const Mag& mag, const RcDesc &rc, GRunArgP &g_arg, bool is_umat)
}
}
void unbind(Mag& mag, const RcDesc &rc)
{
switch (rc.shape)
{
case GShape::GARRAY:
case GShape::GOPAQUE:
case GShape::GSCALAR:
// TODO: Do nothing - should we really do anything here?
break;
case GShape::GMAT:
// Clean-up everything - a cv::Mat, cv::RMat::View, a cv::UMat, and cv::RMat
// if applicable
mag.slot<cv::Mat>().erase(rc.id);
#if !defined(GAPI_STANDALONE)
mag.slot<cv::UMat>().erase(rc.id);
#endif
mag.slot<cv::RMat::View>().erase(rc.id);
mag.slot<cv::RMat>().erase(rc.id);
break;
default:
GAPI_Assert(false);
}
}
} // namespace magazine
void createMat(const cv::GMatDesc &desc, cv::Mat& mat)
......
......@@ -95,6 +95,11 @@ cv::GMetaArgs cv::gapi::own::descrs_of(const std::vector<Mat> &vec)
return vec_descr_of(vec);
}
cv::GMatDesc cv::descr_of(const cv::RMat &mat)
{
return mat.desc();
}
namespace cv {
std::ostream& operator<<(std::ostream& os, const cv::GMatDesc &desc)
{
......@@ -137,4 +142,9 @@ bool GMatDesc::canDescribe(const cv::Mat& mat) const
return canDescribeHelper(*this, mat);
}
bool GMatDesc::canDescribe(const cv::RMat& mat) const
{
return *this == mat.desc();
}
}// namespace cv
......@@ -119,6 +119,9 @@ cv::GMetaArg cv::descr_of(const cv::GRunArg &arg)
case GRunArg::index_of<cv::gapi::wip::IStreamSource::Ptr>():
return cv::util::get<cv::gapi::wip::IStreamSource::Ptr>(arg)->descr_of();
case GRunArg::index_of<cv::RMat>():
return cv::GMetaArg(cv::util::get<cv::RMat>(arg).desc());
default: util::throw_error(std::logic_error("Unsupported GRunArg type"));
}
}
......@@ -174,6 +177,8 @@ bool cv::can_describe(const GMetaArg& meta, const GRunArg& arg)
case GRunArg::index_of<cv::detail::VectorRef>(): return meta == cv::GMetaArg(util::get<cv::detail::VectorRef>(arg).descr_of());
case GRunArg::index_of<cv::detail::OpaqueRef>(): return meta == cv::GMetaArg(util::get<cv::detail::OpaqueRef>(arg).descr_of());
case GRunArg::index_of<cv::gapi::wip::IStreamSource::Ptr>(): return util::holds_alternative<GMatDesc>(meta); // FIXME(?) may be not the best option
case GRunArg::index_of<cv::RMat>(): return util::holds_alternative<GMatDesc>(meta) &&
util::get<GMatDesc>(meta).canDescribe(cv::util::get<cv::RMat>(arg));
default: util::throw_error(std::logic_error("Unsupported GRunArg type"));
}
}
......@@ -263,6 +268,8 @@ const void* cv::gimpl::proto::ptr(const GRunArgP &arg)
return static_cast<const void*>(cv::util::get<cv::Mat*>(arg));
case GRunArgP::index_of<cv::Scalar*>():
return static_cast<const void*>(cv::util::get<cv::Scalar*>(arg));
case GRunArgP::index_of<cv::RMat*>():
return static_cast<const void*>(cv::util::get<cv::RMat*>(arg));
case GRunArgP::index_of<cv::detail::VectorRef>():
return cv::util::get<cv::detail::VectorRef>(arg).ptr();
case GRunArgP::index_of<cv::detail::OpaqueRef>():
......
// 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 <opencv2/gapi/rmat.hpp>
using View = cv::RMat::View;
// There is an issue with default generated operator=(View&&) on Mac:
// it doesn't nullify m_cb of a moved object
View& View::operator=(View&& v) {
m_desc = v.m_desc;
m_data = v.m_data;
m_step = v.m_step;
m_cb = v.m_cb;
v.m_desc = {};
v.m_data = nullptr;
v.m_step = 0u;
v.m_cb = nullptr;
return *this;
}
......@@ -22,6 +22,23 @@
namespace cv {
namespace gimpl {
inline cv::Mat asMat(RMat::View& v) {
return v.dims().empty() ? cv::Mat(v.rows(), v.cols(), v.type(), v.ptr(), v.step())
: cv::Mat(v.dims(), v.type(), v.ptr());
}
inline RMat::View asView(const Mat& m, RMat::View::DestroyCallback&& cb = nullptr) {
return RMat::View(cv::descr_of(m), m.data, m.step, std::move(cb));
}
class RMatAdapter : public RMat::Adapter {
cv::Mat m_mat;
public:
const void* data() const { return m_mat.data; }
RMatAdapter(cv::Mat m) : m_mat(m) {}
virtual RMat::View access(RMat::Access) override { return asView(m_mat); }
virtual cv::GMatDesc desc() const override { return cv::descr_of(m_mat); }
};
// Forward declarations
struct Data;
struct RcDesc;
......@@ -44,20 +61,39 @@ namespace magazine {
} // namespace magazine
#if !defined(GAPI_STANDALONE)
using Mag = magazine::Class<cv::Mat, cv::UMat, cv::Scalar, cv::detail::VectorRef, cv::detail::OpaqueRef>;
using Mag = magazine::Class<cv::Mat, cv::UMat, cv::Scalar, cv::detail::VectorRef, cv::detail::OpaqueRef, cv::RMat, cv::RMat::View>;
#else
using Mag = magazine::Class<cv::Mat, cv::Scalar, cv::detail::VectorRef, cv::detail::OpaqueRef>;
using Mag = magazine::Class<cv::Mat, cv::Scalar, cv::detail::VectorRef, cv::detail::OpaqueRef, cv::RMat, cv::RMat::View>;
#endif
namespace magazine
{
void GAPI_EXPORTS bindInArg (Mag& mag, const RcDesc &rc, const GRunArg &arg, bool is_umat = false);
void GAPI_EXPORTS bindOutArg(Mag& mag, const RcDesc &rc, const GRunArgP &arg, bool is_umat = false);
enum class HandleRMat { BIND, SKIP };
// Extracts a memory object from GRunArg, stores it in appropriate slot in a magazine
// Note:
// Only RMats are expected here as a memory object for GMat shape.
// If handleRMat is BIND, RMat will be accessed, and RMat::View and wrapping cv::Mat
// will be placed into the magazine.
// If handleRMat is SKIP, this function skips'RMat handling assuming that backend will do it on its own.
// FIXME?
// handleRMat parameter might be redundant if all device specific backends implement own bind routines
// without utilizing magazine at all
void GAPI_EXPORTS bindInArg (Mag& mag, const RcDesc &rc, const GRunArg &arg, HandleRMat handleRMat = HandleRMat::BIND);
// Extracts a memory object reference fro GRunArgP, stores it in appropriate slot in a magazine
// Note on RMat handling from bindInArg above is also applied here
void GAPI_EXPORTS bindOutArg(Mag& mag, const RcDesc &rc, const GRunArgP &arg, HandleRMat handleRMat = HandleRMat::BIND);
void resetInternalData(Mag& mag, const Data &d);
cv::GRunArg getArg (const Mag& mag, const RcDesc &ref);
cv::GRunArgP getObjPtr ( Mag& mag, const RcDesc &rc, bool is_umat = false);
void writeBack (const Mag& mag, const RcDesc &rc, GRunArgP &g_arg, bool is_umat = false);
void writeBack (const Mag& mag, const RcDesc &rc, GRunArgP &g_arg);
// A mandatory clean-up procedure to force proper lifetime of wrappers (cv::Mat, cv::RMat::View)
// over not-owned data
// FIXME? Add an RAII wrapper for that?
// Or put objects which need to be cleaned-up into a separate stack allocated magazine?
void unbind(Mag &mag, const RcDesc &rc);
} // namespace magazine
namespace detail
......
......@@ -165,6 +165,14 @@ IOStream& operator<< (IOStream& os, const cv::Scalar &s) {
IIStream& operator>> (IIStream& is, cv::Scalar& s) {
return is >> s.val[0] >> s.val[1] >> s.val[2] >> s.val[3];
}
IOStream& operator<< (IOStream& os, const cv::RMat&) {
util::throw_error(std::logic_error("Serialization of RMat is not supported"));
return os;
}
IIStream& operator>> (IIStream& is, cv::RMat&) {
util::throw_error(std::logic_error("Serialization of RMat is not supported"));
return is;
}
namespace
{
......
......@@ -88,6 +88,9 @@ GAPI_EXPORTS IOStream& operator<< (IOStream& os, const cv::UMat &);
GAPI_EXPORTS IIStream& operator>> (IIStream& is, cv::UMat &);
#endif // !defined(GAPI_STANDALONE)
GAPI_EXPORTS IOStream& operator<< (IOStream& os, const cv::RMat &r);
GAPI_EXPORTS IIStream& operator>> (IIStream& is, cv::RMat &r);
GAPI_EXPORTS IOStream& operator<< (IOStream& os, const cv::gapi::wip::IStreamSource::Ptr &);
GAPI_EXPORTS IIStream& operator>> (IIStream& is, cv::gapi::wip::IStreamSource::Ptr &);
......
......@@ -276,4 +276,8 @@ void cv::gimpl::GCPUExecutable::run(std::vector<InObj> &&input_objs,
} // for(m_script)
for (auto &it : output_objs) magazine::writeBack(m_res, it.first, it.second);
// In/Out args clean-up is mandatory now with RMat
for (auto &it : input_objs) magazine::unbind(m_res, it.first);
for (auto &it : output_objs) magazine::unbind(m_res, it.first);
}
......@@ -1243,41 +1243,25 @@ void cv::gimpl::GFluidExecutable::reshape(ade::Graph &g, const GCompileArgs &arg
// FIXME: Document what it does
void cv::gimpl::GFluidExecutable::bindInArg(const cv::gimpl::RcDesc &rc, const GRunArg &arg)
{
switch (rc.shape)
{
case GShape::GMAT: m_buffers[m_id_map.at(rc.id)].priv().bindTo(util::get<cv::Mat>(arg), true); break;
case GShape::GSCALAR: m_res.slot<cv::Scalar>()[rc.id] = util::get<cv::Scalar>(arg); break;
case GShape::GARRAY: m_res.slot<cv::detail::VectorRef>()[rc.id] = util::get<cv::detail::VectorRef>(arg); break;
case GShape::GOPAQUE: m_res.slot<cv::detail::OpaqueRef>()[rc.id] = util::get<cv::detail::OpaqueRef>(arg); break;
default: util::throw_error(std::logic_error("Unsupported input GShape type"));
magazine::bindInArg(m_res, rc, arg);
if (rc.shape == GShape::GMAT) {
auto& mat = m_res.slot<cv::Mat>()[rc.id];
// fluid::Buffer::bindTo() is not connected to magazine::bindIn/OutArg and unbind() calls,
// it's simply called each run() without any requirement to call some fluid-specific
// unbind() at the end of run()
m_buffers[m_id_map.at(rc.id)].priv().bindTo(mat, true);
}
}
void cv::gimpl::GFluidExecutable::bindOutArg(const cv::gimpl::RcDesc &rc, const GRunArgP &arg)
{
// Only GMat is supported as return type
using T = GRunArgP;
switch (rc.shape)
{
case GShape::GMAT:
{
cv::GMatDesc desc = m_buffers[m_id_map.at(rc.id)].meta();
auto &bref = m_buffers[m_id_map.at(rc.id)].priv();
switch (arg.index()) {
// FIXME: See the bindInArg comment on Streaming-related changes
case T::index_of<cv::Mat*>(): {
auto &outMat = *util::get<cv::Mat*>(arg);
GAPI_Assert(outMat.data != nullptr);
GAPI_Assert(cv::descr_of(outMat) == desc && "Output argument was not preallocated as it should be ?");
bref.bindTo(outMat, false);
} break;
default: GAPI_Assert(false);
} // switch(arg.index())
break;
}
default: util::throw_error(std::logic_error("Unsupported return GShape type"));
if (rc.shape != GShape::GMAT) {
util::throw_error(std::logic_error("Unsupported return GShape type"));
}
magazine::bindOutArg(m_res, rc, arg);
auto& mat = m_res.slot<cv::Mat>()[rc.id];
m_buffers[m_id_map.at(rc.id)].priv().bindTo(mat, false);
}
void cv::gimpl::GFluidExecutable::packArg(cv::GArg &in_arg, const cv::GArg &op_arg)
......@@ -1383,6 +1367,10 @@ void cv::gimpl::GFluidExecutable::run(std::vector<InObj> &input_objs,
agent->doWork();
}
}
// In/Out args clean-up is mandatory now with RMat
for (auto &it : input_objs) magazine::unbind(m_res, it.first);
for (auto &it : output_objs) magazine::unbind(m_res, it.first);
}
cv::gimpl::GParallelFluidExecutable::GParallelFluidExecutable(const ade::Graph &g,
......
......@@ -128,8 +128,7 @@ class GFluidExecutable final: public GIslandExecutable
std::vector<FluidAgent*> m_script;
using Magazine = detail::magazine<cv::Scalar, cv::detail::VectorRef, cv::detail::OpaqueRef>;
Magazine m_res;
cv::gimpl::Mag m_res;
std::size_t m_num_int_buffers; // internal buffers counter (m_buffers - num_scratch)
std::vector<std::size_t> m_scratch_users;
......
......@@ -397,6 +397,10 @@ void cv::gimpl::ie::GIEExecutable::run(std::vector<InObj> &&input_objs,
kk.run(this_iec, uu, context);
for (auto &it : output_objs) magazine::writeBack(m_res, it.first, it.second);
// In/Out args clean-up is mandatory now with RMat
for (auto &it : input_objs) magazine::unbind(m_res, it.first);
for (auto &it : output_objs) magazine::unbind(m_res, it.first);
}
namespace cv {
......
......@@ -165,12 +165,31 @@ void cv::gimpl::GOCLExecutable::run(std::vector<InObj> &&input_objs,
// NB: avoid clearing the whole magazine, there's also pre-allocated internal data
for (auto& it : input_objs) umats.erase(it.first.id);
for (auto& it : output_objs) umats.erase(it.first.id);
// In/Out args clean-up is mandatory now with RMat
for (auto &it : input_objs) magazine::unbind(*p, it.first);
for (auto &it : output_objs) magazine::unbind(*p, it.first);
};
// RAII wrapper to clean-up m_res
std::unique_ptr<cv::gimpl::Mag, decltype(clean_up)> cleaner(&m_res, clean_up);
for (auto& it : input_objs) magazine::bindInArg (m_res, it.first, it.second, true);
for (auto& it : output_objs) magazine::bindOutArg(m_res, it.first, it.second, true);
const auto bindUMat = [this](const RcDesc& rc) {
auto& mag_umat = m_res.template slot<cv::UMat>()[rc.id];
mag_umat = m_res.template slot<cv::Mat>()[rc.id].getUMat(ACCESS_READ);
};
for (auto& it : input_objs) {
const auto& rc = it.first;
magazine::bindInArg (m_res, rc, it.second);
// There is already cv::Mat in the magazine after bindInArg call,
// extract UMat from it, put into the magazine
if (rc.shape == GShape::GMAT) bindUMat(rc);
}
for (auto& it : output_objs) {
const auto& rc = it.first;
magazine::bindOutArg(m_res, rc, it.second);
if (rc.shape == GShape::GMAT) bindUMat(rc);
}
// Initialize (reset) internal data nodes with user structures
// before processing a frame (no need to do it for external data structures)
......@@ -241,5 +260,16 @@ void cv::gimpl::GOCLExecutable::run(std::vector<InObj> &&input_objs,
}
} // for(m_script)
for (auto &it : output_objs) magazine::writeBack(m_res, it.first, it.second, true);
for (auto &it : output_objs)
{
auto& rc = it.first;
auto& g_arg = it.second;
magazine::writeBack(m_res, rc, g_arg);
if (rc.shape == GShape::GMAT)
{
uchar* out_arg_data = m_res.template slot<cv::Mat>()[rc.id].data;
auto& mag_mat = m_res.template slot<cv::UMat>().at(rc.id);
GAPI_Assert((out_arg_data == (mag_mat.getMat(ACCESS_RW).data)) && " data for output parameters was reallocated ?");
}
}
}
......@@ -198,6 +198,9 @@ void cv::gimpl::GPlaidMLExecutable::run(std::vector<InObj> &&input_objs,
exec_->run();
for (auto& it : output_objs) bindOutArg(it.first, it.second);
// FIXME:
// PlaidML backend haven't been updated with RMat support
}
void cv::gimpl::GPlaidMLExecutable::bindInArg(const RcDesc &rc, const GRunArg &arg)
......
......@@ -96,6 +96,10 @@ void cv::gimpl::render::ocv::GRenderExecutable::run(std::vector<InObj> &&input_
k.m_runF(context);
for (auto &it : output_objs) magazine::writeBack(m_res, it.first, it.second);
// In/Out args clean-up is mandatory now with RMat
for (auto &it : input_objs) magazine::unbind(m_res, it.first);
for (auto &it : output_objs) magazine::unbind(m_res, it.first);
}
cv::GArg cv::gimpl::render::ocv::GRenderExecutable::packArg(const cv::GArg &arg) {
......
......@@ -18,6 +18,7 @@
#include "compiler/gmodel.hpp"
#include "compiler/gislandmodel.hpp"
#include "compiler/gmodel.hpp"
#include "backends/common/gbackend.hpp" // RMatAdapter
#include "logger.hpp" // GAPI_LOG
......@@ -348,7 +349,7 @@ void GIslandExecutable::run(GIslandExecutable::IInput &in, GIslandExecutable::IO
switch (in_data_orig.index())
{
case cv::GRunArg::index_of<cv::Mat>():
in_data = cv::GRunArg{cv::util::get<cv::Mat>(in_data_orig)};
in_data = cv::GRunArg{cv::make_rmat<cv::gimpl::RMatAdapter>(cv::util::get<cv::Mat>(in_data_orig))};
break;
case cv::GRunArg::index_of<cv::Scalar>():
in_data = cv::GRunArg{(cv::util::get<cv::Scalar>(in_data_orig))};
......
......@@ -22,7 +22,6 @@
namespace cv { namespace gimpl {
// FIXME: GAPI_EXPORTS only because of tests!
class GAPI_EXPORTS GIsland
{
......@@ -122,6 +121,8 @@ public:
virtual bool canReshape() const = 0;
virtual void reshape(ade::Graph& g, const GCompileArgs& args) = 0;
virtual bool allocatesOutputs() const { return false; }
virtual cv::RMat allocate(const cv::GMatDesc&) const { GAPI_Assert(false && "should never be called"); }
// This method is called when the GStreamingCompiled gets a new
// input source to process. Normally this method is called once
......
......@@ -72,7 +72,7 @@ cv::gimpl::GExecutor::GExecutor(std::unique_ptr<ade::Graph> &&g_model)
const auto orig_data_nh
= m_gim.metadata(nh).get<DataSlot>().original_data_node;
// (1)
initResource(orig_data_nh);
initResource(nh, orig_data_nh);
m_slots.emplace_back(DataDesc{nh, orig_data_nh});
}
break;
......@@ -84,7 +84,82 @@ cv::gimpl::GExecutor::GExecutor(std::unique_ptr<ade::Graph> &&g_model)
} // for(gim nodes)
}
void cv::gimpl::GExecutor::initResource(const ade::NodeHandle &orig_nh)
namespace cv {
namespace gimpl {
namespace magazine {
namespace {
void bindInArgExec(Mag& mag, const RcDesc &rc, const GRunArg &arg)
{
if (rc.shape != GShape::GMAT)
{
bindInArg(mag, rc, arg);
return;
}
auto& mag_rmat = mag.template slot<cv::RMat>()[rc.id];
switch (arg.index())
{
case GRunArg::index_of<Mat>() :
mag_rmat = make_rmat<RMatAdapter>(util::get<Mat>(arg)); break;
case GRunArg::index_of<cv::RMat>() :
mag_rmat = util::get<cv::RMat>(arg); break;
default: util::throw_error(std::logic_error("content type of the runtime argument does not match to resource description ?"));
}
}
void bindOutArgExec(Mag& mag, const RcDesc &rc, const GRunArgP &arg)
{
if (rc.shape != GShape::GMAT)
{
bindOutArg(mag, rc, arg);
return;
}
auto& mag_rmat = mag.template slot<cv::RMat>()[rc.id];
switch (arg.index())
{
case GRunArgP::index_of<Mat*>() :
mag_rmat = make_rmat<RMatAdapter>(*util::get<Mat*>(arg)); break;
case GRunArgP::index_of<cv::RMat*>() :
mag_rmat = *util::get<cv::RMat*>(arg); break;
default: util::throw_error(std::logic_error("content type of the runtime argument does not match to resource description ?"));
}
}
cv::GRunArgP getObjPtrExec(Mag& mag, const RcDesc &rc)
{
if (rc.shape != GShape::GMAT)
{
return getObjPtr(mag, rc);
}
return GRunArgP(&mag.template slot<cv::RMat>()[rc.id]);
}
void writeBackExec(const Mag& mag, const RcDesc &rc, GRunArgP &g_arg)
{
if (rc.shape != GShape::GMAT)
{
writeBack(mag, rc, g_arg);
return;
}
auto checkOutArgData = [&](const uchar* out_arg_data) {
//simply check that memory was not reallocated, i.e.
//both Mat and View pointing to the same memory
auto mag_data = mag.template slot<cv::RMat>().at(rc.id).get<RMatAdapter>()->data();
GAPI_Assert((out_arg_data == mag_data) && " data for output parameters was reallocated ?");
};
switch (g_arg.index())
{
case GRunArgP::index_of<cv::Mat*>() : checkOutArgData(util::get<cv::Mat*>(g_arg)->data); break;
case GRunArgP::index_of<cv::RMat*>() : /* do nothing */ break;
default: util::throw_error(std::logic_error("content type of the runtime argument does not match to resource description ?"));
}
}
} // anonymous namespace
}}} // namespace cv::gimpl::magazine
void cv::gimpl::GExecutor::initResource(const ade::NodeHandle & nh, const ade::NodeHandle &orig_nh)
{
const Data &d = m_gm.metadata(orig_nh).get<Data>();
......@@ -99,9 +174,19 @@ void cv::gimpl::GExecutor::initResource(const ade::NodeHandle &orig_nh)
{
case GShape::GMAT:
{
// Let island allocate it's outputs if it can,
// allocate cv::Mat and wrap it with RMat otherwise
GAPI_Assert(!nh->inNodes().empty());
const auto desc = util::get<cv::GMatDesc>(d.meta);
auto& mat = m_res.slot<cv::Mat>()[d.rc];
createMat(desc, mat);
auto& exec = m_gim.metadata(nh->inNodes().front()).get<IslandExec>().object;
auto& rmat = m_res.slot<cv::RMat>()[d.rc];
if (exec->allocatesOutputs()) {
rmat = exec->allocate(desc);
} else {
Mat mat;
createMat(desc, mat);
rmat = make_rmat<RMatAdapter>(mat);
}
}
break;
......@@ -146,7 +231,7 @@ public:
class cv::gimpl::GExecutor::Output final: public cv::gimpl::GIslandExecutable::IOutput
{
cv::gimpl::Mag &mag;
virtual GRunArgP get(int idx) override { return magazine::getObjPtr(mag, desc()[idx]); }
virtual GRunArgP get(int idx) override { return magazine::getObjPtrExec(mag, desc()[idx]); }
virtual void post(GRunArgP&&) override { } // Do nothing here
virtual void post(EndOfStream&&) override {} // Do nothing here too
public:
......@@ -186,11 +271,10 @@ void cv::gimpl::GExecutor::run(cv::gimpl::GRuntimeArgs &&args)
using cv::util::get;
const auto desc = get<cv::GMatDesc>(d.meta);
auto check_own_mat = [&desc, &args, &index]()
auto check_rmat = [&desc, &args, &index]()
{
auto& out_mat = *get<cv::Mat*>(args.outObjs.at(index));
GAPI_Assert(out_mat.data != nullptr &&
desc.canDescribe(out_mat));
auto& out_mat = *get<cv::RMat*>(args.outObjs.at(index));
GAPI_Assert(desc.canDescribe(out_mat));
};
#if !defined(GAPI_STANDALONE)
......@@ -202,15 +286,25 @@ void cv::gimpl::GExecutor::run(cv::gimpl::GRuntimeArgs &&args)
auto& out_mat = *get<cv::Mat*>(args.outObjs.at(index));
createMat(desc, out_mat);
}
// In the case of own::Mat never reallocated, checked to perfectly fit required meta
// In the case of RMat check to fit required meta
else
{
check_own_mat();
check_rmat();
}
#else
// Building standalone - output buffer should always exist,
// and _exact_ match our inferred metadata
check_own_mat();
if (cv::util::holds_alternative<cv::Mat*>(args.outObjs.at(index)))
{
auto& out_mat = *get<cv::Mat*>(args.outObjs.at(index));
GAPI_Assert(out_mat.data != nullptr &&
desc.canDescribe(out_mat));
}
// In the case of RMat check to fit required meta
else
{
check_rmat();
}
#endif // !defined(GAPI_STANDALONE)
}
}
......@@ -218,12 +312,12 @@ void cv::gimpl::GExecutor::run(cv::gimpl::GRuntimeArgs &&args)
for (auto it : ade::util::zip(ade::util::toRange(proto.inputs),
ade::util::toRange(args.inObjs)))
{
magazine::bindInArg(m_res, std::get<0>(it), std::get<1>(it));
magazine::bindInArgExec(m_res, std::get<0>(it), std::get<1>(it));
}
for (auto it : ade::util::zip(ade::util::toRange(proto.outputs),
ade::util::toRange(args.outObjs)))
{
magazine::bindOutArg(m_res, std::get<0>(it), std::get<1>(it));
magazine::bindOutArgExec(m_res, std::get<0>(it), std::get<1>(it));
}
// Reset internal data
......@@ -246,7 +340,7 @@ void cv::gimpl::GExecutor::run(cv::gimpl::GRuntimeArgs &&args)
for (auto it : ade::util::zip(ade::util::toRange(proto.outputs),
ade::util::toRange(args.outObjs)))
{
magazine::writeBack(m_res, std::get<0>(it), std::get<1>(it));
magazine::writeBackExec(m_res, std::get<0>(it), std::get<1>(it));
}
}
......
......@@ -54,6 +54,7 @@ namespace gimpl {
class GExecutor
{
protected:
Mag m_res;
std::unique_ptr<ade::Graph> m_orig_graph;
std::shared_ptr<ade::Graph> m_island_graph;
......@@ -80,9 +81,7 @@ protected:
class Input;
class Output;
Mag m_res;
void initResource(const ade::NodeHandle &orig_nh); // FIXME: shouldn't it be RcDesc?
void initResource(const ade::NodeHandle &nh, const ade::NodeHandle &orig_nh); // FIXME: shouldn't it be RcDesc?
public:
explicit GExecutor(std::unique_ptr<ade::Graph> &&g_model);
......
......@@ -109,7 +109,13 @@ void sync_data(cv::GRunArgs &results, cv::GRunArgsP &outputs)
switch (out_obj.index())
{
case T::index_of<cv::Mat*>():
*cv::util::get<cv::Mat*>(out_obj) = std::move(cv::util::get<cv::Mat>(res_obj));
{
auto out_mat_p = cv::util::get<cv::Mat*>(out_obj);
auto view = cv::util::get<cv::RMat>(res_obj).access(cv::RMat::Access::R);
*out_mat_p = cv::gimpl::asMat(view).clone();
} break;
case T::index_of<cv::RMat*>():
*cv::util::get<cv::RMat*>(out_obj) = std::move(cv::util::get<cv::RMat>(res_obj));
break;
case T::index_of<cv::Scalar*>():
*cv::util::get<cv::Scalar*>(out_obj) = std::move(cv::util::get<cv::Scalar>(res_obj));
......@@ -408,6 +414,7 @@ class StreamingOutput final: public cv::gimpl::GIslandExecutable::IOutput
// These objects are owned externally
const cv::GMetaArgs &m_metas;
std::vector< std::vector<Q*> > &m_out_queues;
std::shared_ptr<cv::gimpl::GIslandExecutable> m_island;
// Allocate a new data object for output under idx
// Prepare this object for posting
......@@ -430,10 +437,19 @@ class StreamingOutput final: public cv::gimpl::GIslandExecutable::IOutput
// FIXME: This is absolutely ugly but seem to work perfectly for its purpose.
case cv::GShape::GMAT:
{
MatType newMat;
cv::gimpl::createMat(cv::util::get<cv::GMatDesc>(m_metas[idx]), newMat);
out_arg = cv::GRunArg(std::move(newMat));
ret_val = cv::GRunArgP(&cv::util::get<MatType>(out_arg));
auto desc = cv::util::get<cv::GMatDesc>(m_metas[idx]);
if (m_island->allocatesOutputs())
{
out_arg = cv::GRunArg(m_island->allocate(desc));
}
else
{
MatType newMat;
cv::gimpl::createMat(desc, newMat);
auto rmat = cv::make_rmat<cv::gimpl::RMatAdapter>(newMat);
out_arg = cv::GRunArg(std::move(rmat));
}
ret_val = cv::GRunArgP(&cv::util::get<cv::RMat>(out_arg));
}
break;
case cv::GShape::GSCALAR:
......@@ -538,9 +554,11 @@ class StreamingOutput final: public cv::gimpl::GIslandExecutable::IOutput
public:
explicit StreamingOutput(const cv::GMetaArgs &metas,
std::vector< std::vector<Q*> > &out_queues,
const std::vector<cv::gimpl::RcDesc> &out_descs)
const std::vector<cv::gimpl::RcDesc> &out_descs,
std::shared_ptr<cv::gimpl::GIslandExecutable> island)
: m_metas(metas)
, m_out_queues(out_queues)
, m_island(island)
{
set(out_descs);
m_postings.resize(out_descs.size());
......@@ -573,7 +591,7 @@ void islandActorThread(std::vector<cv::gimpl::RcDesc> in_rcs, //
GAPI_Assert(out_queues.size() == out_metas.size());
QueueReader qr;
StreamingInput input(qr, in_queues, in_constants, in_rcs);
StreamingOutput output(out_metas, out_queues, out_rcs);
StreamingOutput output(out_metas, out_queues, out_rcs, island);
while (!output.done())
{
island->run(input, output);
......
......@@ -182,7 +182,7 @@ TEST(GArray, GArrayConstValInitialization)
std::vector<cv::Point> initial_vec {Point(0,0), Point(1,1), Point(2,2)};
std::vector<cv::Point> ref_vec {Point(1,1), Point(2,2), Point(3,3)};
std::vector<cv::Point> out_vec;
cv::Mat in_mat;
cv::Mat in_mat = cv::Mat::eye(32, 32, CV_8UC1);
cv::GComputationT<ThisTest::GPointArray(cv::GMat)> c([&](cv::GMat in)
{
......@@ -201,7 +201,7 @@ TEST(GArray, GArrayRValInitialization)
{
std::vector<cv::Point> ref_vec {Point(1,1), Point(2,2), Point(3,3)};
std::vector<cv::Point> out_vec;
cv::Mat in_mat;
cv::Mat in_mat = cv::Mat::eye(32, 32, CV_8UC1);
cv::GComputationT<ThisTest::GPointArray(cv::GMat)> c([&](cv::GMat in)
{
......
......@@ -16,6 +16,7 @@ struct ProtoPtrTest : public ::testing::Test { using Type = T; };
using ProtoPtrTestTypes = ::testing::Types< cv::Mat
, cv::UMat
, cv::gapi::own::Mat
, cv::RMat
, cv::Scalar
, std::vector<int>
, int
......
// 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 "../test_precomp.hpp"
#include <opencv2/gapi/rmat.hpp>
#include "rmat_test_common.hpp"
#include <opencv2/gapi/fluid/imgproc.hpp>
#include <opencv2/gapi/cpu/imgproc.hpp>
namespace opencv_test
{
// This test set takes RMat type as a template parameter and launces simple
// blur(isl1) -> blur(isl2) computation passing RMat as input, as output
// and both input and output
template<typename RMatAdapterT>
struct RMatIntTestBase {
cv::Mat in_mat;
cv::Mat out_mat;
cv::Mat out_mat_ref;
cv::GComputation comp;
bool inCallbackCalled;
bool outCallbackCalled;
static constexpr int w = 8;
static constexpr int h = 8;
RMatIntTestBase()
: in_mat(h, w, CV_8UC1)
, out_mat(h, w, CV_8UC1)
, out_mat_ref(h, w, CV_8UC1)
, comp([](){
cv::GMat in;
auto tmp = cv::gapi::blur(in, {3,3});
auto out = cv::gapi::blur(tmp, {3,3});
cv::gapi::island("test", cv::GIn(in), cv::GOut(tmp));
return cv::GComputation(in, out);
})
, inCallbackCalled(false)
, outCallbackCalled(false) {
cv::randu(in_mat, cv::Scalar::all(127), cv::Scalar::all(40));
}
void check() {
comp.apply(in_mat, out_mat_ref);
EXPECT_EQ(0, cvtest::norm(out_mat_ref, out_mat, NORM_INF));
}
RMat createRMat(cv::Mat& mat, bool& callbackCalled) {
return {cv::make_rmat<RMatAdapterT>(mat, callbackCalled)};
}
};
template<typename RMatAdapterT>
struct RMatIntTest : public RMatIntTestBase<RMatAdapterT>
{
template<typename In, typename Out>
void run(const In& in, Out& out, cv::GCompileArgs&& compile_args) {
for (int i = 0; i < 2; i++) {
EXPECT_FALSE(this->inCallbackCalled);
EXPECT_FALSE(this->outCallbackCalled);
auto compile_args_copy = compile_args;
this->comp.apply(cv::gin(in), cv::gout(out), std::move(compile_args_copy));
EXPECT_FALSE(this->inCallbackCalled);
if (std::is_same<RMat,Out>::value) {
EXPECT_TRUE(this->outCallbackCalled);
} else {
EXPECT_FALSE(this->outCallbackCalled);
}
this->outCallbackCalled = false;
}
this->check();
}
};
template<typename RMatAdapterT>
struct RMatIntTestStreaming : public RMatIntTestBase<RMatAdapterT>
{
template <typename M>
cv::GMatDesc getDesc(const M& m) { return cv::descr_of(m); }
void checkOutput(const cv::Mat&) { this->check(); }
void checkOutput(const RMat& rm) {
auto view = rm.access(RMat::Access::R);
this->out_mat = cv::Mat(view.size(), view.type(), view.ptr());
this->check();
}
template<typename In, typename Out>
void run(const In& in, Out& out, cv::GCompileArgs&& compile_args) {
auto sc = this->comp.compileStreaming(getDesc(in), std::move(compile_args));
sc.setSource(cv::gin(in));
sc.start();
std::size_t frame = 0u;
constexpr std::size_t num_frames = 10u;
EXPECT_FALSE(this->inCallbackCalled);
EXPECT_FALSE(this->outCallbackCalled);
while (sc.pull(cv::gout(out)) && frame < num_frames) {
frame++;
this->checkOutput(out);
EXPECT_FALSE(this->inCallbackCalled);
EXPECT_FALSE(this->outCallbackCalled);
}
EXPECT_EQ(num_frames, frame);
}
};
struct OcvKernels {
cv::gapi::GKernelPackage kernels() { return cv::gapi::imgproc::cpu::kernels(); }
};
struct FluidKernels {
cv::gapi::GKernelPackage kernels() { return cv::gapi::imgproc::fluid::kernels(); }
};
struct RMatIntTestCpuRef : public
RMatIntTest<RMatAdapterRef>, OcvKernels {};
struct RMatIntTestCpuCopy : public
RMatIntTest<RMatAdapterCopy>, OcvKernels {};
struct RMatIntTestCpuRefStreaming : public
RMatIntTestStreaming<RMatAdapterRef>, OcvKernels {};
struct RMatIntTestCpuCopyStreaming : public
RMatIntTestStreaming<RMatAdapterCopy>, OcvKernels {};
struct RMatIntTestCpuRefFluid : public
RMatIntTest<RMatAdapterRef>, FluidKernels {};
struct RMatIntTestCpuCopyFluid : public
RMatIntTest<RMatAdapterCopy>, FluidKernels {};
struct RMatIntTestCpuRefStreamingFluid : public
RMatIntTestStreaming<RMatAdapterRef>, FluidKernels {};
struct RMatIntTestCpuCopyStreamingFluid : public
RMatIntTestStreaming<RMatAdapterCopy>, FluidKernels {};
template<typename T>
struct RMatIntTypedTest : public ::testing::Test, public T {};
using RMatIntTestTypes = ::testing::Types< RMatIntTestCpuRef
, RMatIntTestCpuCopy
, RMatIntTestCpuRefStreaming
, RMatIntTestCpuCopyStreaming
, RMatIntTestCpuRefFluid
, RMatIntTestCpuCopyFluid
, RMatIntTestCpuRefStreamingFluid
, RMatIntTestCpuCopyStreamingFluid
>;
TYPED_TEST_CASE(RMatIntTypedTest, RMatIntTestTypes);
TYPED_TEST(RMatIntTypedTest, In) {
auto in_rmat = this->createRMat(this->in_mat, this->inCallbackCalled);
this->run(in_rmat, this->out_mat, cv::compile_args(this->kernels()));
}
TYPED_TEST(RMatIntTypedTest, Out) {
auto out_rmat = this->createRMat(this->out_mat, this->outCallbackCalled);
this->run(this->in_mat, out_rmat, cv::compile_args(this->kernels()));
}
TYPED_TEST(RMatIntTypedTest, InOut) {
auto in_rmat = this->createRMat(this->in_mat, this->inCallbackCalled);
auto out_rmat = this->createRMat(this->out_mat, this->outCallbackCalled);
this->run(in_rmat, out_rmat, cv::compile_args(this->kernels()));
}
} // namespace opencv_test
// 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_RMAT_TESTS_COMMON_HPP
#define OPENCV_GAPI_RMAT_TESTS_COMMON_HPP
#include "../test_precomp.hpp"
#include <opencv2/gapi/rmat.hpp>
namespace opencv_test {
class RMatAdapterRef : public RMat::Adapter {
cv::Mat& m_mat;
bool& m_callbackCalled;
public:
RMatAdapterRef(cv::Mat& m, bool& callbackCalled)
: m_mat(m), m_callbackCalled(callbackCalled)
{}
virtual RMat::View access(RMat::Access access) override {
if (access == RMat::Access::W) {
return RMat::View(cv::descr_of(m_mat), m_mat.data, m_mat.step,
[this](){
EXPECT_FALSE(m_callbackCalled);
m_callbackCalled = true;
});
} else {
return RMat::View(cv::descr_of(m_mat), m_mat.data, m_mat.step);
}
}
virtual cv::GMatDesc desc() const override { return cv::descr_of(m_mat); }
};
class RMatAdapterCopy : public RMat::Adapter {
cv::Mat& m_deviceMat;
cv::Mat m_hostMat;
bool& m_callbackCalled;
public:
RMatAdapterCopy(cv::Mat& m, bool& callbackCalled)
: m_deviceMat(m), m_hostMat(m.clone()), m_callbackCalled(callbackCalled)
{}
virtual RMat::View access(RMat::Access access) override {
if (access == RMat::Access::W) {
return RMat::View(cv::descr_of(m_hostMat), m_hostMat.data, m_hostMat.step,
[this](){
EXPECT_FALSE(m_callbackCalled);
m_callbackCalled = true;
m_hostMat.copyTo(m_deviceMat);
});
} else {
m_deviceMat.copyTo(m_hostMat);
return RMat::View(cv::descr_of(m_hostMat), m_hostMat.data, m_hostMat.step);
}
}
virtual cv::GMatDesc desc() const override { return cv::descr_of(m_hostMat); }
};
} // namespace opencv_test
#endif // OPENCV_GAPI_RMAT_TESTS_COMMON_HPP
......@@ -6,55 +6,10 @@
#include "../test_precomp.hpp"
#include <opencv2/gapi/rmat.hpp>
#include "rmat_test_common.hpp"
namespace opencv_test {
namespace {
class RMatAdapterRef : public RMat::Adapter {
cv::Mat& m_mat;
bool& m_callbackCalled;
public:
RMatAdapterRef(cv::Mat& m, bool& callbackCalled)
: m_mat(m), m_callbackCalled(callbackCalled)
{}
virtual RMat::View access(RMat::Access access) const override {
if (access == RMat::Access::W) {
return RMat::View(cv::descr_of(m_mat), m_mat.data, m_mat.step,
[this](){
EXPECT_FALSE(m_callbackCalled);
m_callbackCalled = true;
});
} else {
return RMat::View(cv::descr_of(m_mat), m_mat.data, m_mat.step);
}
}
virtual cv::GMatDesc desc() const override { return cv::descr_of(m_mat); }
};
class RMatAdapterCopy : public RMat::Adapter {
cv::Mat& m_deviceMat;
cv::Mat m_hostMat;
bool& m_callbackCalled;
public:
RMatAdapterCopy(cv::Mat& m, bool& callbackCalled)
: m_deviceMat(m), m_hostMat(m.clone()), m_callbackCalled(callbackCalled)
{}
virtual RMat::View access(RMat::Access access) const override {
if (access == RMat::Access::W) {
return RMat::View(cv::descr_of(m_hostMat), m_hostMat.data, m_hostMat.step,
[this](){
EXPECT_FALSE(m_callbackCalled);
m_callbackCalled = true;
m_hostMat.copyTo(m_deviceMat);
});
} else {
m_deviceMat.copyTo(m_hostMat);
return RMat::View(cv::descr_of(m_hostMat), m_hostMat.data, m_hostMat.step);
}
}
virtual cv::GMatDesc desc() const override { return cv::descr_of(m_hostMat); }
};
void randomizeMat(cv::Mat& m) {
auto ref = m.clone();
while (cv::norm(m, ref, cv::NORM_INF) == 0) {
......@@ -66,7 +21,7 @@ template <typename RMatAdapterT>
struct RMatTest {
using AdapterT = RMatAdapterT;
RMatTest()
: m_deviceMat(8,8,CV_8UC1)
: m_deviceMat(cv::Mat::zeros(8,8,CV_8UC1))
, m_rmat(make_rmat<RMatAdapterT>(m_deviceMat, m_callbackCalled)) {
randomizeMat(m_deviceMat);
expectNoCallbackCalled();
......@@ -140,7 +95,7 @@ TYPED_TEST(RMatTypedTest, CorrectAdapterCast) {
}
class DummyAdapter : public RMat::Adapter {
virtual RMat::View access(RMat::Access) const override { return {}; }
virtual RMat::View access(RMat::Access) override { return {}; }
virtual cv::GMatDesc desc() const override { return {}; }
};
......@@ -152,7 +107,7 @@ class RMatAdapterForBackend : public RMat::Adapter {
int m_i;
public:
RMatAdapterForBackend(int i) : m_i(i) {}
virtual RMat::View access(RMat::Access) const override { return {}; }
virtual RMat::View access(RMat::Access) override { return {}; }
virtual GMatDesc desc() const override { return {}; }
int deviceSpecificData() const { return m_i; }
};
......
......@@ -17,22 +17,22 @@ using View = cv::RMat::View;
using cv::Mat;
using namespace ::testing;
static void expect_eq_desc(const View& view, const GMatDesc& desc) {
EXPECT_EQ(view.size(), desc.size);
EXPECT_EQ(view.dims(), desc.dims);
EXPECT_EQ(view.cols(), desc.size.width);
EXPECT_EQ(view.rows(), desc.size.height);
EXPECT_EQ(view.type(), CV_MAKE_TYPE(desc.depth,desc.chan));
EXPECT_EQ(view.depth(), desc.depth);
EXPECT_EQ(view.chan(), desc.chan);
static void expect_eq_desc(const GMatDesc& desc, const View& view) {
EXPECT_EQ(desc.size, view.size());
EXPECT_EQ(desc.dims, view.dims());
EXPECT_EQ(desc.size.width, view.cols());
EXPECT_EQ(desc.size.height, view.rows());
EXPECT_EQ(CV_MAKE_TYPE(desc.depth,desc.chan), view.type());
EXPECT_EQ(desc.depth, view.depth());
EXPECT_EQ(desc.chan, view.chan());
}
TEST(RMatView, TestDefaultConstruction) {
View view;
GMatDesc desc{};
expect_eq_desc(view, desc);
EXPECT_EQ(view.ptr(), nullptr);
EXPECT_EQ(view.step(), 0u);
expect_eq_desc(desc, view);
EXPECT_EQ(nullptr, view.ptr());
EXPECT_EQ(0u, view.step());
}
struct RMatViewTest : public TestWithParam<int /*dataType*/>{};
......@@ -41,9 +41,9 @@ TEST_P(RMatViewTest, ConstructionFromMat) {
Mat mat(8,8,type);
const auto desc = cv::descr_of(mat);
View view(cv::descr_of(mat), mat.ptr(), mat.step1());
expect_eq_desc(view, desc);
EXPECT_EQ(view.ptr(), mat.ptr());
EXPECT_EQ(view.step(), mat.step1());
expect_eq_desc(desc, view);
EXPECT_EQ(mat.ptr(), view.ptr());
EXPECT_EQ(mat.step1(), view.step());
}
TEST(RMatView, TestConstructionFromMatND) {
......@@ -51,8 +51,8 @@ TEST(RMatView, TestConstructionFromMatND) {
Mat mat(dims, CV_8UC1);
const auto desc = cv::descr_of(mat);
View view(cv::descr_of(mat), mat.ptr());
expect_eq_desc(view, desc);
EXPECT_EQ(view.ptr(), mat.ptr());
expect_eq_desc(desc, view);
EXPECT_EQ(mat.ptr(), view.ptr());
}
TEST_P(RMatViewTest, DefaultStep) {
......@@ -63,7 +63,7 @@ TEST_P(RMatViewTest, DefaultStep) {
desc.size = {8,8};
std::vector<unsigned char> data(desc.size.width*desc.size.height*CV_ELEM_SIZE(type));
View view(desc, data.data());
EXPECT_EQ(view.step(), static_cast<size_t>(desc.size.width)*CV_ELEM_SIZE(type));
EXPECT_EQ(static_cast<size_t>(desc.size.width)*CV_ELEM_SIZE(type), view.step());
}
static Mat asMat(View& view) {
......@@ -104,21 +104,31 @@ INSTANTIATE_TEST_CASE_P(Test, RMatViewTest,
struct RMatViewCallbackTest : public ::testing::Test {
RMatViewCallbackTest()
: mat(8,8,CV_8UC1), view(cv::descr_of(mat), mat.ptr(), mat.step1(), [this](){ callbackCalls++; }) {
: mat(8,8,CV_8UC1) {
cv::randn(mat, cv::Scalar::all(127), cv::Scalar::all(40));
}
View getView() { return {cv::descr_of(mat), mat.ptr(), mat.step1(), [this](){ callbackCalls++; }}; }
int callbackCalls = 0;
Mat mat;
View view;
};
TEST_F(RMatViewCallbackTest, MoveCtor) {
{
View copy(getView());
cv::util::suppress_unused_warning(copy);
EXPECT_EQ(0, callbackCalls);
}
EXPECT_EQ(1, callbackCalls);
}
TEST_F(RMatViewCallbackTest, MoveCopy) {
{
View copy(std::move(view));
View copy;
copy = getView();
cv::util::suppress_unused_warning(copy);
EXPECT_EQ(callbackCalls, 0);
EXPECT_EQ(0, callbackCalls);
}
EXPECT_EQ(callbackCalls, 1);
EXPECT_EQ(1, callbackCalls);
}
static int firstElement(const View& view) { return *view.ptr(); }
......@@ -128,20 +138,20 @@ TEST_F(RMatViewCallbackTest, MagazineInteraction) {
cv::gimpl::magazine::Class<View> mag;
constexpr int rc = 1;
constexpr uchar value = 11;
mag.slot<View>()[rc] = std::move(view);
mag.slot<View>()[rc] = getView();
{
auto& mag_view = mag.slot<View>()[rc];
setFirstElement(mag_view, value);
auto mag_el = firstElement(mag_view);
EXPECT_EQ(mag_el, value);
EXPECT_EQ(value, mag_el);
}
{
const auto& mag_view = mag.slot<View>()[rc];
auto mag_el = firstElement(mag_view);
EXPECT_EQ(mag_el, value);
EXPECT_EQ(value, mag_el);
}
EXPECT_EQ(callbackCalls, 0);
EXPECT_EQ(0, callbackCalls);
mag.slot<View>().erase(rc);
EXPECT_EQ(callbackCalls, 1);
EXPECT_EQ(1, callbackCalls);
}
} // namespace opencv_test
Markdown is supported
0% .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册