1
0
mirror of https://github.com/opencv/opencv.git synced 2026-07-30 07:43:03 +04:00

Merge pull request #12608 from dmatveev:gapi

* G-API Initial code upload

* Update G-API code base to Sep-24-2018

* The majority of OpenCV buildbot problems was addressed

* Update G-API code base to 24-Sep-18 EOD

* G-API code base update 25-Sep-2018

* Linux warnings should be resolved
* Documentation build should become green
* Number of Windows warnings should be reduced

* Update G-API code base to 25-Sep-18 EOD

* ARMv7 build issue should be resolved
* ADE is bumped to latest version and should fix Clang builds for macOS/iOS
* Remaining Windows warnings should be resolved
* New Linux32 / ARMv7 warnings should be resolved

* G-API code base update 25-Sep-2018-EOD2

* Final Windows warnings should be resolved now

* G-API code base update 26-Sep-2018

* Fixed issues with precompiled headers in module and its tests
This commit is contained in:
Dmitry Matveev
2018-09-26 21:50:39 +03:00
committed by Alexander Alekhin
parent 852f061b26
commit 29e88e50ff
166 changed files with 35254 additions and 0 deletions
+2
View File
@@ -0,0 +1,2 @@
This directory contains various G-API backends, which provide scheduling
logic and kernel implementations for specific targets.
@@ -0,0 +1,103 @@
// 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) 2018 Intel Corporation
#ifndef OPENCV_GAPI_GBACKEND_HPP
#define OPENCV_GAPI_GBACKEND_HPP
#include <string>
#include <memory>
#include <ade/node.hpp>
#include "opencv2/gapi/garg.hpp"
#include "opencv2/gapi/own/mat.hpp"
#include "opencv2/gapi/util/optional.hpp"
#include "opencv2/gapi/own/scalar.hpp"
#include "compiler/gmodel.hpp"
namespace cv {
namespace gimpl {
// Forward declarations
struct Data;
struct RcDesc;
namespace magazine {
template<typename... Ts> struct Class
{
template<typename T> using MapT = std::unordered_map<int, T>;
template<typename T> MapT<T>& slot()
{
return std::get<ade::util::type_list_index<T, Ts...>::value>(slots);
}
template<typename T> const MapT<T>& slot() const
{
return std::get<ade::util::type_list_index<T, Ts...>::value>(slots);
}
private:
std::tuple<MapT<Ts>...> slots;
};
} // namespace magazine
using Mag = magazine::Class<cv::gapi::own::Mat, cv::gapi::own::Scalar, cv::detail::VectorRef>;
namespace magazine
{
void bindInArg (Mag& mag, const RcDesc &rc, const GRunArg &arg);
void bindOutArg(Mag& mag, const RcDesc &rc, const GRunArgP &arg);
void resetInternalData(Mag& mag, const Data &d);
cv::GRunArg getArg (const Mag& mag, const RcDesc &ref);
cv::GRunArgP getObjPtr ( Mag& mag, const RcDesc &rc);
void writeBack (const Mag& mag, const RcDesc &rc, GRunArgP &g_arg);
} // namespace magazine
namespace detail
{
template<typename... Ts> struct magazine
{
template<typename T> using MapT = std::unordered_map<int, T>;
template<typename T> MapT<T>& slot()
{
return std::get<util::type_list_index<T, Ts...>::value>(slots);
}
template<typename T> const MapT<T>& slot() const
{
return std::get<util::type_list_index<T, Ts...>::value>(slots);
}
private:
std::tuple<MapT<Ts>...> slots;
};
} // namespace detail
struct GRuntimeArgs
{
GRunArgs inObjs;
GRunArgsP outObjs;
};
template<typename T>
inline cv::util::optional<T> getCompileArg(const cv::GCompileArgs &args)
{
for (auto &compile_arg : args)
{
if (compile_arg.tag == cv::detail::CompileArgTag<T>::tag())
{
return cv::util::optional<T>(compile_arg.get<T>());
}
}
return cv::util::optional<T>();
}
}} // cv::gimpl
#endif // OPENCV_GAPI_GBACKEND_HPP
@@ -0,0 +1,18 @@
// 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) 2018 Intel Corporation
#include "opencv2/gapi/gcompoundkernel.hpp" // compound::backend()
#include "api/gbackend_priv.hpp"
#include "compiler/gislandmodel.hpp" // GIslandExecutable
cv::gapi::GBackend cv::gapi::compound::backend()
{
// A pointer to dummy Priv is used to uniquely identify backends
static cv::gapi::GBackend this_backend(std::make_shared<cv::gapi::GBackend::Priv>());
return this_backend;
}
@@ -0,0 +1,45 @@
// 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) 2018 Intel Corporation
#include <ade/util/zip_range.hpp> // util::indexed
#include "opencv2/gapi/gcompoundkernel.hpp"
#include "compiler/gobjref.hpp"
// FIXME move to backends
cv::detail::GCompoundContext::GCompoundContext(const cv::GArgs& in_args)
{
m_args.resize(in_args.size());
for (const auto& it : ade::util::indexed(in_args))
{
const auto& i = ade::util::index(it);
const auto& in_arg = ade::util::value(it);
if (in_arg.kind != cv::detail::ArgKind::GOBJREF)
{
m_args[i] = in_arg;
}
else
{
const cv::gimpl::RcDesc &ref = in_arg.get<cv::gimpl::RcDesc>();
switch (ref.shape)
{
case GShape::GMAT : m_args[i] = GArg(GMat()); break;
case GShape::GSCALAR: m_args[i] = GArg(GScalar()); break;
case GShape::GARRAY :/* do nothing - as handled in a special way, see gcompoundkernel.hpp for details */; break;
default: GAPI_Assert(false);
}
}
}
GAPI_Assert(m_args.size() == in_args.size());
}
cv::detail::GCompoundKernel::GCompoundKernel(const F& f) : m_f(f)
{
}
void cv::detail::GCompoundKernel::apply(cv::detail::GCompoundContext& ctx) { m_f(ctx); }
@@ -0,0 +1,227 @@
// 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) 2018 Intel Corporation
#include <functional>
#include <unordered_set>
#include <ade/util/algorithm.hpp>
#include <ade/util/range.hpp>
#include <ade/util/zip_range.hpp>
#include <ade/util/chain_range.hpp>
#include <ade/typed_graph.hpp>
#include "opencv2/gapi/gcommon.hpp"
#include "opencv2/gapi/util/any.hpp"
#include "opencv2/gapi/gtype_traits.hpp"
#include "compiler/gobjref.hpp"
#include "compiler/gmodel.hpp"
#include "backends/cpu/gcpubackend.hpp"
#include "backends/cpu/gcpuimgproc.hpp"
#include "backends/cpu/gcpucore.hpp"
#include "api/gbackend_priv.hpp" // FIXME: Make it part of Backend SDK!
// FIXME: Is there a way to take a typed graph (our GModel),
// and create a new typed graph _ATOP_ of that (by extending with a couple of
// new types?).
// Alternatively, is there a way to compose types graphs?
//
// If not, we need to introduce that!
using GCPUModel = ade::TypedGraph
< cv::gimpl::Unit
, cv::gimpl::Protocol
>;
// FIXME: Same issue with Typed and ConstTyped
using GConstGCPUModel = ade::ConstTypedGraph
< cv::gimpl::Unit
, cv::gimpl::Protocol
>;
namespace
{
class GCPUBackendImpl final: public cv::gapi::GBackend::Priv
{
virtual void unpackKernel(ade::Graph &graph,
const ade::NodeHandle &op_node,
const cv::GKernelImpl &impl) override
{
GCPUModel gm(graph);
auto cpu_impl = cv::util::any_cast<cv::GCPUKernel>(impl.opaque);
gm.metadata(op_node).set(cv::gimpl::Unit{cpu_impl});
}
virtual EPtr compile(const ade::Graph &graph,
const cv::GCompileArgs &,
const std::vector<ade::NodeHandle> &nodes) const override
{
return EPtr{new cv::gimpl::GCPUExecutable(graph, nodes)};
}
};
}
cv::gapi::GBackend cv::gapi::cpu::backend()
{
static cv::gapi::GBackend this_backend(std::make_shared<GCPUBackendImpl>());
return this_backend;
}
// GCPUExcecutable implementation //////////////////////////////////////////////
cv::gimpl::GCPUExecutable::GCPUExecutable(const ade::Graph &g,
const std::vector<ade::NodeHandle> &nodes)
: m_g(g), m_gm(m_g)
{
// Convert list of operations (which is topologically sorted already)
// into an execution script.
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::DATA:
{
m_dataNodes.push_back(nh);
const auto &desc = m_gm.metadata(nh).get<Data>();
if (desc.storage == Data::Storage::CONST)
{
auto rc = RcDesc{desc.rc, desc.shape, desc.ctor};
magazine::bindInArg(m_res, rc, m_gm.metadata(nh).get<ConstValue>().arg);
}
//preallocate internal Mats in advance
if (desc.storage == Data::Storage::INTERNAL && desc.shape == GShape::GMAT)
{
const auto mat_desc = util::get<cv::GMatDesc>(desc.meta);
const auto type = CV_MAKETYPE(mat_desc.depth, mat_desc.chan);
m_res.slot<cv::gapi::own::Mat>()[desc.rc].create(mat_desc.size, type);
}
break;
}
default: util::throw_error(std::logic_error("Unsupported NodeType type"));
}
}
}
// FIXME: Document what it does
cv::GArg cv::gimpl::GCPUExecutable::packArg(const GArg &arg)
{
// No API placeholders allowed at this point
// FIXME: this check has to be done somewhere in compilation stage.
GAPI_Assert( arg.kind != cv::detail::ArgKind::GMAT
&& arg.kind != cv::detail::ArgKind::GSCALAR
&& arg.kind != cv::detail::ArgKind::GARRAY);
if (arg.kind != cv::detail::ArgKind::GOBJREF)
{
// All other cases - pass as-is, with no transformations to GArg contents.
return arg;
}
GAPI_Assert(arg.kind == cv::detail::ArgKind::GOBJREF);
// Wrap associated CPU object (either host or an internal one)
// FIXME: object can be moved out!!! GExecutor faced that.
const cv::gimpl::RcDesc &ref = arg.get<cv::gimpl::RcDesc>();
switch (ref.shape)
{
case GShape::GMAT: return GArg(m_res.slot<cv::gapi::own::Mat>() [ref.id]);
case GShape::GSCALAR: return GArg(m_res.slot<cv::gapi::own::Scalar>()[ref.id]);
// Note: .at() is intentional for GArray as object MUST be already there
// (and constructed by either bindIn/Out or resetInternal)
case GShape::GARRAY: return GArg(m_res.slot<cv::detail::VectorRef>().at(ref.id));
default:
util::throw_error(std::logic_error("Unsupported GShape type"));
break;
}
}
void cv::gimpl::GCPUExecutable::run(std::vector<InObj> &&input_objs,
std::vector<OutObj> &&output_objs)
{
// Update resources with run-time information - what this Island
// has received from user (or from another Island, or mix...)
// FIXME: Check input/output objects against GIsland protocol
for (auto& it : input_objs) magazine::bindInArg (m_res, it.first, it.second);
for (auto& it : output_objs) magazine::bindOutArg(m_res, it.first, it.second);
// Initialize (reset) internal data nodes with user structures
// before processing a frame (no need to do it for external data structures)
GModel::ConstGraph gm(m_g);
for (auto nh : m_dataNodes)
{
const auto &desc = gm.metadata(nh).get<Data>();
if ( desc.storage == Data::Storage::INTERNAL
&& !util::holds_alternative<util::monostate>(desc.ctor))
{
// FIXME: Note that compile-time constant data objects (like
// a value-initialized GArray<T>) also satisfy this condition
// and should be excluded, but now we just don't support it
magazine::resetInternalData(m_res, desc);
}
}
// OpenCV backend execution is not a rocket science at all.
// Simply invoke our kernels in the proper order.
GConstGCPUModel gcm(m_g);
for (auto &op_info : m_script)
{
const auto &op = m_gm.metadata(op_info.nh).get<Op>();
// Obtain our real execution unit
// TODO: Should kernels be copyable?
GCPUKernel k = gcm.metadata(op_info.nh).get<Unit>().k;
// Initialize kernel's execution context:
// - Input parameters
GCPUContext context;
context.m_args.reserve(op.args.size());
using namespace std::placeholders;
ade::util::transform(op.args,
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
for (const auto &out_it : ade::util::indexed(op.outs))
{
// FIXME: Can the same GArg type resolution mechanism be reused here?
const auto out_port = ade::util::index(out_it);
const auto out_desc = ade::util::value(out_it);
context.m_results[out_port] = magazine::getObjPtr(m_res, out_desc);
}
// Now trigger the executable unit
k.apply(context);
//As Kernels are forbidden to allocate memory for (Mat) outputs,
//this code seems redundant, at least for Mats
//FIXME: unify with cv::detail::ensure_out_mats_not_reallocated
for (const auto &out_it : ade::util::indexed(op_info.expected_out_metas))
{
const auto out_index = ade::util::index(out_it);
const auto expected_meta = ade::util::value(out_it);
const auto out_meta = descr_of(context.m_results[out_index]);
if (expected_meta != out_meta)
{
util::throw_error
(std::logic_error
("Output meta doesn't "
"coincide with the generated meta\n"
"Expected: " + ade::util::to_string(expected_meta) + "\n"
"Actual : " + ade::util::to_string(out_meta)));
}
}
} // for(m_script)
for (auto &it : output_objs) magazine::writeBack(m_res, it.first, it.second);
}
@@ -0,0 +1,63 @@
// 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) 2018 Intel Corporation
#ifndef OPENCV_GAPI_GCPUBACKEND_HPP
#define OPENCV_GAPI_GCPUBACKEND_HPP
#include <map> // map
#include <unordered_map> // unordered_map
#include <tuple> // tuple
#include <ade/util/algorithm.hpp> // type_list_index
#include "opencv2/gapi/garg.hpp"
#include "opencv2/gapi/gproto.hpp"
#include "opencv2/gapi/cpu/gcpukernel.hpp"
#include "api/gapi_priv.hpp"
#include "backends/common/gbackend.hpp"
#include "compiler/gislandmodel.hpp"
namespace cv { namespace gimpl {
struct Unit
{
static const char *name() { return "HostKernel"; }
GCPUKernel k;
};
class GCPUExecutable final: public GIslandExecutable
{
const ade::Graph &m_g;
GModel::ConstGraph m_gm;
struct OperationInfo
{
ade::NodeHandle nh;
GMetaArgs expected_out_metas;
};
// Execution script, currently absolutely naive
std::vector<OperationInfo> m_script;
// List of all resources in graph (both internal and external)
std::vector<ade::NodeHandle> m_dataNodes;
// Actual data of all resources in graph (both internal and external)
Mag m_res;
GArg packArg(const GArg &arg);
public:
GCPUExecutable(const ade::Graph &graph,
const std::vector<ade::NodeHandle> &nodes);
virtual void run(std::vector<InObj> &&input_objs,
std::vector<OutObj> &&output_objs) override;
};
}}
#endif // OPENCV_GAPI_GBACKEND_HPP
+577
View File
@@ -0,0 +1,577 @@
// 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) 2018 Intel Corporation
#include "precomp.hpp"
#include "opencv2/gapi/core.hpp"
#include "opencv2/gapi/cpu/core.hpp"
#include "backends/cpu/gcpucore.hpp"
GAPI_OCV_KERNEL(GCPUAdd, cv::gapi::core::GAdd)
{
static void run(const cv::Mat& a, const cv::Mat& b, int dtype, cv::Mat& out)
{
cv::add(a, b, out, cv::noArray(), dtype);
}
};
GAPI_OCV_KERNEL(GCPUAddC, cv::gapi::core::GAddC)
{
static void run(const cv::Mat& a, const cv::Scalar& b, int dtype, cv::Mat& out)
{
cv::add(a, b, out, cv::noArray(), dtype);
}
};
GAPI_OCV_KERNEL(GCPUSub, cv::gapi::core::GSub)
{
static void run(const cv::Mat& a, const cv::Mat& b, int dtype, cv::Mat& out)
{
cv::subtract(a, b, out, cv::noArray(), dtype);
}
};
GAPI_OCV_KERNEL(GCPUSubC, cv::gapi::core::GSubC)
{
static void run(const cv::Mat& a, const cv::Scalar& b, int dtype, cv::Mat& out)
{
cv::subtract(a, b, out, cv::noArray(), dtype);
}
};
GAPI_OCV_KERNEL(GCPUSubRC, cv::gapi::core::GSubRC)
{
static void run(const cv::Scalar& a, const cv::Mat& b, int dtype, cv::Mat& out)
{
cv::subtract(a, b, out, cv::noArray(), dtype);
}
};
GAPI_OCV_KERNEL(GCPUMul, cv::gapi::core::GMul)
{
static void run(const cv::Mat& a, const cv::Mat& b, double scale, int dtype, cv::Mat& out)
{
cv::multiply(a, b, out, scale, dtype);
}
};
GAPI_OCV_KERNEL(GCPUMulCOld, cv::gapi::core::GMulCOld)
{
static void run(const cv::Mat& a, double b, int dtype, cv::Mat& out)
{
cv::multiply(a, b, out, 1, dtype);
}
};
GAPI_OCV_KERNEL(GCPUMulC, cv::gapi::core::GMulC)
{
static void run(const cv::Mat& a, const cv::Scalar& b, int dtype, cv::Mat& out)
{
cv::multiply(a, b, out, 1, dtype);
}
};
GAPI_OCV_KERNEL(GCPUDiv, cv::gapi::core::GDiv)
{
static void run(const cv::Mat& a, const cv::Mat& b, double scale, int dtype, cv::Mat& out)
{
cv::divide(a, b, out, scale, dtype);
}
};
GAPI_OCV_KERNEL(GCPUDivC, cv::gapi::core::GDivC)
{
static void run(const cv::Mat& a, const cv::Scalar& b, double scale, int dtype, cv::Mat& out)
{
cv::divide(a, b, out, scale, dtype);
}
};
GAPI_OCV_KERNEL(GCPUDivRC, cv::gapi::core::GDivRC)
{
static void run(const cv::Scalar& a, const cv::Mat& b, double scale, int dtype, cv::Mat& out)
{
cv::divide(a, b, out, scale, dtype);
}
};
GAPI_OCV_KERNEL(GCPUMask, cv::gapi::core::GMask)
{
static void run(const cv::Mat& in, const cv::Mat& mask, cv::Mat& out)
{
out = cv::Mat::zeros(in.size(), in.type());
in.copyTo(out, mask);
}
};
GAPI_OCV_KERNEL(GCPUMean, cv::gapi::core::GMean)
{
static void run(const cv::Mat& in, cv::Scalar& out)
{
out = cv::mean(in);
}
};
GAPI_OCV_KERNEL(GCPUPolarToCart, cv::gapi::core::GPolarToCart)
{
static void run(const cv::Mat& magn, const cv::Mat& angle, bool angleInDegrees, cv::Mat& outx, cv::Mat& outy)
{
cv::polarToCart(magn, angle, outx, outy, angleInDegrees);
}
};
GAPI_OCV_KERNEL(GCPUCartToPolar, cv::gapi::core::GCartToPolar)
{
static void run(const cv::Mat& x, const cv::Mat& y, bool angleInDegrees, cv::Mat& outmagn, cv::Mat& outangle)
{
cv::cartToPolar(x, y, outmagn, outangle, angleInDegrees);
}
};
GAPI_OCV_KERNEL(GCPUCmpGT, cv::gapi::core::GCmpGT)
{
static void run(const cv::Mat& a, const cv::Mat& b, cv::Mat& out)
{
cv::compare(a, b, out, cv::CMP_GT);
}
};
GAPI_OCV_KERNEL(GCPUCmpGE, cv::gapi::core::GCmpGE)
{
static void run(const cv::Mat& a, const cv::Mat& b, cv::Mat& out)
{
cv::compare(a, b, out, cv::CMP_GE);
}
};
GAPI_OCV_KERNEL(GCPUCmpLE, cv::gapi::core::GCmpLE)
{
static void run(const cv::Mat& a, const cv::Mat& b, cv::Mat& out)
{
cv::compare(a, b, out, cv::CMP_LE);
}
};
GAPI_OCV_KERNEL(GCPUCmpLT, cv::gapi::core::GCmpLT)
{
static void run(const cv::Mat& a, const cv::Mat& b, cv::Mat& out)
{
cv::compare(a, b, out, cv::CMP_LT);
}
};
GAPI_OCV_KERNEL(GCPUCmpEQ, cv::gapi::core::GCmpEQ)
{
static void run(const cv::Mat& a, const cv::Mat& b, cv::Mat& out)
{
cv::compare(a, b, out, cv::CMP_EQ);
}
};
GAPI_OCV_KERNEL(GCPUCmpNE, cv::gapi::core::GCmpNE)
{
static void run(const cv::Mat& a, const cv::Mat& b, cv::Mat& out)
{
cv::compare(a, b, out, cv::CMP_NE);
}
};
GAPI_OCV_KERNEL(GCPUCmpGTScalar, cv::gapi::core::GCmpGTScalar)
{
static void run(const cv::Mat& a, const cv::Scalar& b, cv::Mat& out)
{
cv::compare(a, b, out, cv::CMP_GT);
}
};
GAPI_OCV_KERNEL(GCPUCmpGEScalar, cv::gapi::core::GCmpGEScalar)
{
static void run(const cv::Mat& a, const cv::Scalar& b, cv::Mat& out)
{
cv::compare(a, b, out, cv::CMP_GE);
}
};
GAPI_OCV_KERNEL(GCPUCmpLEScalar, cv::gapi::core::GCmpLEScalar)
{
static void run(const cv::Mat& a, const cv::Scalar& b, cv::Mat& out)
{
cv::compare(a, b, out, cv::CMP_LE);
}
};
GAPI_OCV_KERNEL(GCPUCmpLTScalar, cv::gapi::core::GCmpLTScalar)
{
static void run(const cv::Mat& a, const cv::Scalar& b, cv::Mat& out)
{
cv::compare(a, b, out, cv::CMP_LT);
}
};
GAPI_OCV_KERNEL(GCPUCmpEQScalar, cv::gapi::core::GCmpEQScalar)
{
static void run(const cv::Mat& a, const cv::Scalar& b, cv::Mat& out)
{
cv::compare(a, b, out, cv::CMP_EQ);
}
};
GAPI_OCV_KERNEL(GCPUCmpNEScalar, cv::gapi::core::GCmpNEScalar)
{
static void run(const cv::Mat& a, const cv::Scalar& b, cv::Mat& out)
{
cv::compare(a, b, out, cv::CMP_NE);
}
};
GAPI_OCV_KERNEL(GCPUAnd, cv::gapi::core::GAnd)
{
static void run(const cv::Mat& a, const cv::Mat& b, cv::Mat& out)
{
cv::bitwise_and(a, b, out);
}
};
GAPI_OCV_KERNEL(GCPUAndS, cv::gapi::core::GAndS)
{
static void run(const cv::Mat& a, const cv::Scalar& b, cv::Mat& out)
{
cv::bitwise_and(a, b, out);
}
};
GAPI_OCV_KERNEL(GCPUOr, cv::gapi::core::GOr)
{
static void run(const cv::Mat& a, const cv::Mat& b, cv::Mat& out)
{
cv::bitwise_or(a, b, out);
}
};
GAPI_OCV_KERNEL(GCPUOrS, cv::gapi::core::GOrS)
{
static void run(const cv::Mat& a, const cv::Scalar& b, cv::Mat& out)
{
cv::bitwise_or(a, b, out);
}
};
GAPI_OCV_KERNEL(GCPUXor, cv::gapi::core::GXor)
{
static void run(const cv::Mat& a, const cv::Mat& b, cv::Mat& out)
{
cv::bitwise_xor(a, b, out);
}
};
GAPI_OCV_KERNEL(GCPUXorS, cv::gapi::core::GXorS)
{
static void run(const cv::Mat& a, const cv::Scalar& b, cv::Mat& out)
{
cv::bitwise_xor(a, b, out);
}
};
GAPI_OCV_KERNEL(GCPUNot, cv::gapi::core::GNot)
{
static void run(const cv::Mat& a, cv::Mat& out)
{
cv::bitwise_not(a, out);
}
};
GAPI_OCV_KERNEL(GCPUSelect, cv::gapi::core::GSelect)
{
static void run(const cv::Mat& src1, const cv::Mat& src2, const cv::Mat& mask, cv::Mat& out)
{
src2.copyTo(out);
src1.copyTo(out, mask);
}
};
GAPI_OCV_KERNEL(GCPUMin, cv::gapi::core::GMin)
{
static void run(const cv::Mat& in1, const cv::Mat& in2, cv::Mat& out)
{
out = cv::min(in1, in2);
}
};
GAPI_OCV_KERNEL(GCPUMax, cv::gapi::core::GMax)
{
static void run(const cv::Mat& in1, const cv::Mat& in2, cv::Mat& out)
{
out = cv::max(in1, in2);
}
};
GAPI_OCV_KERNEL(GCPUAbsDiff, cv::gapi::core::GAbsDiff)
{
static void run(const cv::Mat& in1, const cv::Mat& in2, cv::Mat& out)
{
cv::absdiff(in1, in2, out);
}
};
GAPI_OCV_KERNEL(GCPUAbsDiffC, cv::gapi::core::GAbsDiffC)
{
static void run(const cv::Mat& in1, const cv::Scalar& in2, cv::Mat& out)
{
cv::absdiff(in1, in2, out);
}
};
GAPI_OCV_KERNEL(GCPUSum, cv::gapi::core::GSum)
{
static void run(const cv::Mat& in, cv::Scalar& out)
{
out = cv::sum(in);
}
};
GAPI_OCV_KERNEL(GCPUAddW, cv::gapi::core::GAddW)
{
static void run(const cv::Mat& in1, double alpha, const cv::Mat& in2, double beta, double gamma, int dtype, cv::Mat& out)
{
cv::addWeighted(in1, alpha, in2, beta, gamma, out, dtype);
}
};
GAPI_OCV_KERNEL(GCPUNormL1, cv::gapi::core::GNormL1)
{
static void run(const cv::Mat& in, cv::Scalar& out)
{
out = cv::norm(in, cv::NORM_L1);
}
};
GAPI_OCV_KERNEL(GCPUNormL2, cv::gapi::core::GNormL2)
{
static void run(const cv::Mat& in, cv::Scalar& out)
{
out = cv::norm(in, cv::NORM_L2);
}
};
GAPI_OCV_KERNEL(GCPUNormInf, cv::gapi::core::GNormInf)
{
static void run(const cv::Mat& in, cv::Scalar& out)
{
out = cv::norm(in, cv::NORM_INF);
}
};
GAPI_OCV_KERNEL(GCPUIntegral, cv::gapi::core::GIntegral)
{
static void run(const cv::Mat& in, int sdepth, int sqdepth, cv::Mat& out, cv::Mat& outSq)
{
cv::integral(in, out, outSq, sdepth, sqdepth);
}
};
GAPI_OCV_KERNEL(GCPUThreshold, cv::gapi::core::GThreshold)
{
static void run(const cv::Mat& in, const cv::Scalar& a, const cv::Scalar& b, int type, cv::Mat& out)
{
cv::threshold(in, out, a.val[0], b.val[0], type);
}
};
GAPI_OCV_KERNEL(GCPUThresholdOT, cv::gapi::core::GThresholdOT)
{
static void run(const cv::Mat& in, const cv::Scalar& b, int type, cv::Mat& out, cv::Scalar& outScalar)
{
outScalar = cv::threshold(in, out, b.val[0], b.val[0], type);
}
};
GAPI_OCV_KERNEL(GCPUInRange, cv::gapi::core::GInRange)
{
static void run(const cv::Mat& in, const cv::Scalar& low, const cv::Scalar& up, cv::Mat& out)
{
cv::inRange(in, low, up, out);
}
};
GAPI_OCV_KERNEL(GCPUSplit3, cv::gapi::core::GSplit3)
{
static void run(const cv::Mat& in, cv::Mat &m1, cv::Mat &m2, cv::Mat &m3)
{
std::vector<cv::Mat> outMats = {m1, m2, m3};
cv::split(in, outMats);
// Write back FIXME: Write a helper or avoid this nonsence completely!
m1 = outMats[0];
m2 = outMats[1];
m3 = outMats[2];
}
};
GAPI_OCV_KERNEL(GCPUSplit4, cv::gapi::core::GSplit4)
{
static void run(const cv::Mat& in, cv::Mat &m1, cv::Mat &m2, cv::Mat &m3, cv::Mat &m4)
{
std::vector<cv::Mat> outMats = {m1, m2, m3, m4};
cv::split(in, outMats);
// Write back FIXME: Write a helper or avoid this nonsence completely!
m1 = outMats[0];
m2 = outMats[1];
m3 = outMats[2];
m4 = outMats[3];
}
};
GAPI_OCV_KERNEL(GCPUMerge3, cv::gapi::core::GMerge3)
{
static void run(const cv::Mat& in1, const cv::Mat& in2, const cv::Mat& in3, cv::Mat &out)
{
std::vector<cv::Mat> inMats = {in1, in2, in3};
cv::merge(inMats, out);
}
};
GAPI_OCV_KERNEL(GCPUMerge4, cv::gapi::core::GMerge4)
{
static void run(const cv::Mat& in1, const cv::Mat& in2, const cv::Mat& in3, const cv::Mat& in4, cv::Mat &out)
{
std::vector<cv::Mat> inMats = {in1, in2, in3, in4};
cv::merge(inMats, out);
}
};
GAPI_OCV_KERNEL(GCPUResize, cv::gapi::core::GResize)
{
static void run(const cv::Mat& in, cv::Size sz, double fx, double fy, int interp, cv::Mat &out)
{
cv::resize(in, out, sz, fx, fy, interp);
}
};
GAPI_OCV_KERNEL(GCPURemap, cv::gapi::core::GRemap)
{
static void run(const cv::Mat& in, const cv::Mat& x, const cv::Mat& y, int a, int b, cv::Scalar s, cv::Mat& out)
{
cv::remap(in, out, x, y, a, b, s);
}
};
GAPI_OCV_KERNEL(GCPUFlip, cv::gapi::core::GFlip)
{
static void run(const cv::Mat& in, int code, cv::Mat& out)
{
cv::flip(in, out, code);
}
};
GAPI_OCV_KERNEL(GCPUCrop, cv::gapi::core::GCrop)
{
static void run(const cv::Mat& in, cv::Rect rect, cv::Mat& out)
{
cv::Mat(in, rect).copyTo(out);
}
};
GAPI_OCV_KERNEL(GCPUConcatHor, cv::gapi::core::GConcatHor)
{
static void run(const cv::Mat& in1, const cv::Mat& in2, cv::Mat& out)
{
cv::hconcat(in1, in2, out);
}
};
GAPI_OCV_KERNEL(GCPUConcatVert, cv::gapi::core::GConcatVert)
{
static void run(const cv::Mat& in1, const cv::Mat& in2, cv::Mat& out)
{
cv::vconcat(in1, in2, out);
}
};
GAPI_OCV_KERNEL(GCPULUT, cv::gapi::core::GLUT)
{
static void run(const cv::Mat& in, const cv::Mat& lut, cv::Mat& out)
{
cv::LUT(in, lut, out);
}
};
GAPI_OCV_KERNEL(GCPUConvertTo, cv::gapi::core::GConvertTo)
{
static void run(const cv::Mat& in, int rtype, double alpha, double beta, cv::Mat& out)
{
in.convertTo(out, rtype, alpha, beta);
}
};
cv::gapi::GKernelPackage cv::gapi::core::cpu::kernels()
{
static auto pkg = cv::gapi::kernels
< GCPUAdd
, GCPUAddC
, GCPUSub
, GCPUSubC
, GCPUSubRC
, GCPUMul
, GCPUMulC
, GCPUMulCOld
, GCPUDiv
, GCPUDivC
, GCPUDivRC
, GCPUMean
, GCPUMask
, GCPUPolarToCart
, GCPUCartToPolar
, GCPUCmpGT
, GCPUCmpGE
, GCPUCmpLE
, GCPUCmpLT
, GCPUCmpEQ
, GCPUCmpNE
, GCPUCmpGTScalar
, GCPUCmpGEScalar
, GCPUCmpLEScalar
, GCPUCmpLTScalar
, GCPUCmpEQScalar
, GCPUCmpNEScalar
, GCPUAnd
, GCPUAndS
, GCPUOr
, GCPUOrS
, GCPUXor
, GCPUXorS
, GCPUNot
, GCPUSelect
, GCPUMin
, GCPUMax
, GCPUAbsDiff
, GCPUAbsDiffC
, GCPUSum
, GCPUAddW
, GCPUNormL1
, GCPUNormL2
, GCPUNormInf
, GCPUIntegral
, GCPUThreshold
, GCPUThresholdOT
, GCPUInRange
, GCPUSplit3
, GCPUSplit4
, GCPUResize
, GCPUMerge3
, GCPUMerge4
, GCPURemap
, GCPUFlip
, GCPUCrop
, GCPUConcatHor
, GCPUConcatVert
, GCPULUT
, GCPUConvertTo
>();
return pkg;
}
@@ -0,0 +1,24 @@
// 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) 2018 Intel Corporation
#ifndef OPENCV_GAPI_GCPUCORE_HPP
#define OPENCV_GAPI_GCPUCORE_HPP
#include <map>
#include <string>
#include "opencv2/gapi/cpu/gcpukernel.hpp"
namespace cv { namespace gimpl {
// NB: This is what a "Kernel Package" from the original Wiki doc should be.
void loadCPUCore(std::map<std::string, cv::GCPUKernel> &kmap);
}
}
#endif // OPENCV_GAPI_GCPUCORE_HPP
@@ -0,0 +1,273 @@
// 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) 2018 Intel Corporation
#include "precomp.hpp"
#include "opencv2/gapi/imgproc.hpp"
#include "opencv2/gapi/cpu/imgproc.hpp"
#include "backends/cpu/gcpuimgproc.hpp"
GAPI_OCV_KERNEL(GCPUSepFilter, cv::gapi::imgproc::GSepFilter)
{
static void run(const cv::Mat& in, int ddepth, const cv::Mat& kernX, const cv::Mat& kernY, const cv::Point& anchor, const cv::Scalar& delta,
int border, const cv::Scalar& bordVal, cv::Mat &out)
{
if( border == cv::BORDER_CONSTANT )
{
cv::Mat temp_in;
int width_add = (kernY.cols - 1) / 2;
int height_add = (kernX.rows - 1) / 2;
cv::copyMakeBorder(in, temp_in, height_add, height_add, width_add, width_add, border, bordVal);
cv::Rect rect = cv::Rect(height_add, width_add, in.cols, in.rows);
cv::sepFilter2D(temp_in(rect), out, ddepth, kernX, kernY, anchor, delta.val[0], border);
}
else
cv::sepFilter2D(in, out, ddepth, kernX, kernY, anchor, delta.val[0], border);
}
};
GAPI_OCV_KERNEL(GCPUBoxFilter, cv::gapi::imgproc::GBoxFilter)
{
static void run(const cv::Mat& in, int ddepth, const cv::Size& ksize, const cv::Point& anchor, bool normalize, int borderType, const cv::Scalar& bordVal, cv::Mat &out)
{
if( borderType == cv::BORDER_CONSTANT )
{
cv::Mat temp_in;
int width_add = (ksize.width - 1) / 2;
int height_add = (ksize.height - 1) / 2;
cv::copyMakeBorder(in, temp_in, height_add, height_add, width_add, width_add, borderType, bordVal);
cv::Rect rect = cv::Rect(height_add, width_add, in.cols, in.rows);
cv::boxFilter(temp_in(rect), out, ddepth, ksize, anchor, normalize, borderType);
}
else
cv::boxFilter(in, out, ddepth, ksize, anchor, normalize, borderType);
}
};
GAPI_OCV_KERNEL(GCPUBlur, cv::gapi::imgproc::GBlur)
{
static void run(const cv::Mat& in, const cv::Size& ksize, const cv::Point& anchor, int borderType, const cv::Scalar& bordVal, cv::Mat &out)
{
if( borderType == cv::BORDER_CONSTANT )
{
cv::Mat temp_in;
int width_add = (ksize.width - 1) / 2;
int height_add = (ksize.height - 1) / 2;
cv::copyMakeBorder(in, temp_in, height_add, height_add, width_add, width_add, borderType, bordVal);
cv::Rect rect = cv::Rect(height_add, width_add, in.cols, in.rows);
cv::blur(temp_in(rect), out, ksize, anchor, borderType);
}
else
cv::blur(in, out, ksize, anchor, borderType);
}
};
GAPI_OCV_KERNEL(GCPUFilter2D, cv::gapi::imgproc::GFilter2D)
{
static void run(const cv::Mat& in, int ddepth, const cv::Mat& k, const cv::Point& anchor, const cv::Scalar& delta, int border,
const cv::Scalar& bordVal, cv::Mat &out)
{
if( border == cv::BORDER_CONSTANT )
{
cv::Mat temp_in;
int width_add = (k.cols - 1) / 2;
int height_add = (k.rows - 1) / 2;
cv::copyMakeBorder(in, temp_in, height_add, height_add, width_add, width_add, border, bordVal );
cv::Rect rect = cv::Rect(height_add, width_add, in.cols, in.rows);
cv::filter2D(temp_in(rect), out, ddepth, k, anchor, delta.val[0], border);
}
else
cv::filter2D(in, out, ddepth, k, anchor, delta.val[0], border);
}
};
GAPI_OCV_KERNEL(GCPUGaussBlur, cv::gapi::imgproc::GGaussBlur)
{
static void run(const cv::Mat& in, const cv::Size& ksize, double sigmaX, double sigmaY, int borderType, const cv::Scalar& bordVal, cv::Mat &out)
{
if( borderType == cv::BORDER_CONSTANT )
{
cv::Mat temp_in;
int width_add = (ksize.width - 1) / 2;
int height_add = (ksize.height - 1) / 2;
cv::copyMakeBorder(in, temp_in, height_add, height_add, width_add, width_add, borderType, bordVal );
cv::Rect rect = cv::Rect(height_add, width_add, in.cols, in.rows);
cv::GaussianBlur(temp_in(rect), out, ksize, sigmaX, sigmaY, borderType);
}
else
cv::GaussianBlur(in, out, ksize, sigmaX, sigmaY, borderType);
}
};
GAPI_OCV_KERNEL(GCPUMedianBlur, cv::gapi::imgproc::GMedianBlur)
{
static void run(const cv::Mat& in, int ksize, cv::Mat &out)
{
cv::medianBlur(in, out, ksize);
}
};
GAPI_OCV_KERNEL(GCPUErode, cv::gapi::imgproc::GErode)
{
static void run(const cv::Mat& in, const cv::Mat& kernel, const cv::Point& anchor, int iterations, int borderType, const cv::Scalar& borderValue, cv::Mat &out)
{
cv::erode(in, out, kernel, anchor, iterations, borderType, borderValue);
}
};
GAPI_OCV_KERNEL(GCPUDilate, cv::gapi::imgproc::GDilate)
{
static void run(const cv::Mat& in, const cv::Mat& kernel, const cv::Point& anchor, int iterations, int borderType, const cv::Scalar& borderValue, cv::Mat &out)
{
cv::dilate(in, out, kernel, anchor, iterations, borderType, borderValue);
}
};
GAPI_OCV_KERNEL(GCPUSobel, cv::gapi::imgproc::GSobel)
{
static void run(const cv::Mat& in, int ddepth, int dx, int dy, int ksize, double scale, double delta, int borderType,
const cv::Scalar& bordVal, cv::Mat &out)
{
if( borderType == cv::BORDER_CONSTANT )
{
cv::Mat temp_in;
int add = (ksize - 1) / 2;
cv::copyMakeBorder(in, temp_in, add, add, add, add, borderType, bordVal );
cv::Rect rect = cv::Rect(add, add, in.cols, in.rows);
cv::Sobel(temp_in(rect), out, ddepth, dx, dy, ksize, scale, delta, borderType);
}
else
cv::Sobel(in, out, ddepth, dx, dy, ksize, scale, delta, borderType);
}
};
GAPI_OCV_KERNEL(GCPUEqualizeHist, cv::gapi::imgproc::GEqHist)
{
static void run(const cv::Mat& in, cv::Mat &out)
{
cv::equalizeHist(in, out);
}
};
GAPI_OCV_KERNEL(GCPUCanny, cv::gapi::imgproc::GCanny)
{
static void run(const cv::Mat& in, double thr1, double thr2, int apSize, bool l2gradient, cv::Mat &out)
{
cv::Canny(in, out, thr1, thr2, apSize, l2gradient);
}
};
GAPI_OCV_KERNEL(GCPURGB2YUV, cv::gapi::imgproc::GRGB2YUV)
{
static void run(const cv::Mat& in, cv::Mat &out)
{
cv::cvtColor(in, out, cv::COLOR_RGB2YUV);
}
};
GAPI_OCV_KERNEL(GCPUYUV2RGB, cv::gapi::imgproc::GYUV2RGB)
{
static void run(const cv::Mat& in, cv::Mat &out)
{
cv::cvtColor(in, out, cv::COLOR_YUV2RGB);
}
};
GAPI_OCV_KERNEL(GCPURGB2Lab, cv::gapi::imgproc::GRGB2Lab)
{
static void run(const cv::Mat& in, cv::Mat &out)
{
cv::cvtColor(in, out, cv::COLOR_RGB2Lab);
}
};
GAPI_OCV_KERNEL(GCPUBGR2LUV, cv::gapi::imgproc::GBGR2LUV)
{
static void run(const cv::Mat& in, cv::Mat &out)
{
cv::cvtColor(in, out, cv::COLOR_BGR2Luv);
}
};
GAPI_OCV_KERNEL(GCPUBGR2YUV, cv::gapi::imgproc::GBGR2YUV)
{
static void run(const cv::Mat& in, cv::Mat &out)
{
cv::cvtColor(in, out, cv::COLOR_BGR2YUV);
}
};
GAPI_OCV_KERNEL(GCPULUV2BGR, cv::gapi::imgproc::GLUV2BGR)
{
static void run(const cv::Mat& in, cv::Mat &out)
{
cv::cvtColor(in, out, cv::COLOR_Luv2BGR);
}
};
GAPI_OCV_KERNEL(GCPUYUV2BGR, cv::gapi::imgproc::GYUV2BGR)
{
static void run(const cv::Mat& in, cv::Mat &out)
{
cv::cvtColor(in, out, cv::COLOR_YUV2BGR);
}
};
GAPI_OCV_KERNEL(GCPURGB2Gray, cv::gapi::imgproc::GRGB2Gray)
{
static void run(const cv::Mat& in, cv::Mat &out)
{
cv::cvtColor(in, out, cv::COLOR_RGB2GRAY);
}
};
GAPI_OCV_KERNEL(GCPUBGR2Gray, cv::gapi::imgproc::GBGR2Gray)
{
static void run(const cv::Mat& in, cv::Mat &out)
{
cv::cvtColor(in, out, cv::COLOR_BGR2GRAY);
}
};
GAPI_OCV_KERNEL(GCPURGB2GrayCustom, cv::gapi::imgproc::GRGB2GrayCustom)
{
static void run(const cv::Mat& in, float rY, float bY, float gY, cv::Mat &out)
{
cv::Mat planes[3];
cv::split(in, planes);
out = planes[0]*rY + planes[1]*bY + planes[2]*gY;
}
};
cv::gapi::GKernelPackage cv::gapi::imgproc::cpu::kernels()
{
static auto pkg = cv::gapi::kernels
< GCPUFilter2D
, GCPUSepFilter
, GCPUBoxFilter
, GCPUBlur
, GCPUGaussBlur
, GCPUMedianBlur
, GCPUErode
, GCPUDilate
, GCPUSobel
, GCPUCanny
, GCPUEqualizeHist
, GCPURGB2YUV
, GCPUYUV2RGB
, GCPURGB2Lab
, GCPUBGR2LUV
, GCPUBGR2YUV
, GCPUYUV2BGR
, GCPULUV2BGR
, GCPUBGR2Gray
, GCPURGB2Gray
, GCPURGB2GrayCustom
>();
return pkg;
}
@@ -0,0 +1,23 @@
// 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) 2018 Intel Corporation
#ifndef OPENCV_GAPI_GCPUIMGPROC_HPP
#define OPENCV_GAPI_GCPUIMGPROC_HPP
#include <map>
#include <string>
#include "opencv2/gapi/cpu/gcpukernel.hpp"
namespace cv { namespace gimpl {
// NB: This is what a "Kernel Package" from the origianl Wiki doc should be.
void loadCPUImgProc(std::map<std::string, cv::GCPUKernel> &kmap);
}}
#endif // OPENCV_GAPI_GCPUIMGPROC_HPP
@@ -0,0 +1,50 @@
// 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) 2018 Intel Corporation
#include <cassert>
#include "opencv2/gapi/cpu/gcpukernel.hpp"
const cv::gapi::own::Mat& cv::GCPUContext::inMat(int input)
{
return inArg<cv::gapi::own::Mat>(input);
}
cv::gapi::own::Mat& cv::GCPUContext::outMatR(int output)
{
return *util::get<cv::gapi::own::Mat*>(m_results.at(output));
}
const cv::gapi::own::Scalar& cv::GCPUContext::inVal(int input)
{
return inArg<cv::gapi::own::Scalar>(input);
}
cv::gapi::own::Scalar& cv::GCPUContext::outValR(int output)
{
return *util::get<cv::gapi::own::Scalar*>(m_results.at(output));
}
cv::detail::VectorRef& cv::GCPUContext::outVecRef(int output)
{
return util::get<cv::detail::VectorRef>(m_results.at(output));
}
cv::GCPUKernel::GCPUKernel()
{
}
cv::GCPUKernel::GCPUKernel(const GCPUKernel::F &f)
: m_f(f)
{
}
void cv::GCPUKernel::apply(GCPUContext &ctx)
{
GAPI_Assert(m_f);
m_f(ctx);
}
File diff suppressed because it is too large Load Diff
@@ -0,0 +1,128 @@
// 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) 2018 Intel Corporation
#ifndef OPENCV_GAPI_FLUID_BACKEND_HPP
#define OPENCV_GAPI_FLUID_BACKEND_HPP
#include "opencv2/gapi/garg.hpp"
#include "opencv2/gapi/gproto.hpp"
#include "opencv2/gapi/fluid/gfluidkernel.hpp"
#include "opencv2/gapi/fluid/gfluidbuffer.hpp"
// PRIVATE STUFF!
#include "backends/common/gbackend.hpp"
#include "compiler/gislandmodel.hpp"
namespace cv { namespace gimpl {
struct FluidUnit
{
static const char *name() { return "FluidKernel"; }
GFluidKernel k;
gapi::fluid::BorderOpt border;
int border_size;
int line_consumption;
double ratio;
};
struct FluidUseOwnBorderBuffer
{
static const char *name() { return "FluidUseOwnBorderBuffer"; }
bool use;
};
struct FluidData
{
static const char *name() { return "FluidData"; }
// FIXME: This structure starts looking like "FluidBuffer" meta
int latency = 0;
int skew = 0;
int max_consumption = 1;
int border_size = 0;
int lpi_write = 1;
gapi::fluid::BorderOpt border;
};
struct FluidAgent
{
public:
virtual ~FluidAgent() = default;
FluidAgent(const ade::Graph &g, ade::NodeHandle nh);
GFluidKernel k;
ade::NodeHandle op_handle; // FIXME: why it is here??//
std::string op_name;
// < 0 - not a buffer
// >= 0 - a buffer with RcID
std::vector<int> in_buffer_ids;
std::vector<int> out_buffer_ids;
cv::GArgs in_args;
std::vector<cv::gapi::fluid::View> in_views; // sparce list of IN views
std::vector<cv::gapi::fluid::Buffer*> out_buffers;
// FIXME Current assumption is that outputs have EQUAL SIZES
int m_outputLines = 0;
int m_producedLines = 0;
double m_ratio = 0.0f;
// Execution methods
void reset();
bool canWork() const;
bool canRead() const;
bool canWrite() const;
void doWork();
bool done() const;
void debug(std::ostream& os);
private:
// FIXME!!!
// move to another class
virtual int firstWindow() const = 0;
virtual int nextWindow() const = 0;
virtual int linesRead() const = 0;
};
class GFluidExecutable final: public GIslandExecutable
{
const ade::Graph &m_g;
GModel::ConstGraph m_gm;
std::vector<std::unique_ptr<FluidAgent>> m_agents;
std::vector<cv::gapi::fluid::Buffer> m_buffers;
using Magazine = detail::magazine<cv::gapi::own::Scalar>;
Magazine m_res;
std::size_t m_num_int_buffers; // internal buffers counter (m_buffers - num_scratch)
std::vector<std::size_t> m_scratch_users;
std::vector<cv::gapi::fluid::View> m_views;
std::vector<cv::gapi::own::Rect> m_outputRois;
std::unordered_map<int, std::size_t> m_id_map; // GMat id -> buffer idx map
void bindInArg (const RcDesc &rc, const GRunArg &arg);
void bindOutArg(const RcDesc &rc, const GRunArgP &arg);
void packArg (GArg &in_arg, const GArg &op_arg);
public:
GFluidExecutable(const ade::Graph &g,
const std::vector<ade::NodeHandle> &nodes,
const std::vector<cv::gapi::own::Rect> &outputRois);
virtual void run(std::vector<InObj> &&input_objs,
std::vector<OutObj> &&output_objs) override;
};
}} // cv::gimpl
#endif // OPENCV_GAPI_FLUID_BACKEND_HPP
@@ -0,0 +1,746 @@
// 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) 2018 Intel Corporation
#include <iomanip> // hex, dec (debug)
#include "opencv2/gapi/own/convert.hpp"
#include "opencv2/gapi/fluid/gfluidbuffer.hpp"
#include "backends/fluid/gfluidbuffer_priv.hpp"
#include "opencv2/gapi/opencv_includes.hpp"
#include "backends/fluid/gfluidutils.hpp" // saturate
namespace cv {
namespace gapi {
namespace fluid {
bool operator == (const fluid::Border& b1, const fluid::Border& b2)
{
return b1.type == b2.type && b1.value == b2.value;
}
} // namespace fluid
// Fluid BorderHandler implementation /////////////////////////////////////////////////
namespace {
template<typename T>
// Expected inputs:
// row - row buffer allocated with border in mind (have memory for both image and border pixels)
// length - size of the buffer with left and right borders included
void fillBorderReplicateRow(uint8_t* row, int length, int chan, int borderSize)
{
auto leftBorder = reinterpret_cast<T*>(row);
auto rightBorder = leftBorder + (length - borderSize) * chan;
for (int b = 0; b < borderSize; b++)
{
for (int c = 0; c < chan; c++)
{
leftBorder [b*chan + c] = leftBorder [borderSize*chan + c];
rightBorder[b*chan + c] = rightBorder[-chan + c];
}
}
}
template<typename T>
void fillBorderReflectRow(uint8_t* row, int length, int chan, int borderSize)
{
auto leftBorder = reinterpret_cast<T*>(row);
auto rightBorder = leftBorder + (length - borderSize) * chan;
for (int b = 0; b < borderSize; b++)
{
for (int c = 0; c < chan; c++)
{
leftBorder [b*chan + c] = leftBorder [(2*borderSize - b)*chan + c];
rightBorder[b*chan + c] = rightBorder[(-b - 2)*chan + c];
}
}
}
template<typename T>
void fillConstBorderRow(uint8_t* row, int length, int chan, int borderSize, cv::gapi::own::Scalar borderValue)
{
GAPI_DbgAssert(chan > 0 && chan <= 4);
auto leftBorder = reinterpret_cast<T*>(row);
auto rightBorder = leftBorder + (length - borderSize) * chan;
for (int b = 0; b < borderSize; b++)
{
for (int c = 0; c < chan; c++)
{
leftBorder [b*chan + c] = fluid::saturate<T>(borderValue[c], fluid::roundd);
rightBorder[b*chan + c] = fluid::saturate<T>(borderValue[c], fluid::roundd);
}
}
}
// Fills const border pixels in the whole mat
void fillBorderConstant(int borderSize, cv::gapi::own::Scalar borderValue, cv::Mat& mat)
{
// cv::Scalar can contain maximum 4 chan
GAPI_Assert(mat.channels() > 0 && mat.channels() <= 4);
auto getFillBorderRowFunc = [&](int type) {
switch(type)
{
case CV_8U: return &fillConstBorderRow< uint8_t>; break;
case CV_16S: return &fillConstBorderRow< int16_t>; break;
case CV_16U: return &fillConstBorderRow<uint16_t>; break;
case CV_32F: return &fillConstBorderRow< float >; break;
default: CV_Assert(false); return &fillConstBorderRow<uint8_t>;
}
};
auto fillBorderRow = getFillBorderRowFunc(mat.depth());
for (int y = 0; y < mat.rows; y++)
{
fillBorderRow(mat.ptr(y), mat.cols, mat.channels(), borderSize, borderValue);
}
}
} // anonymous namespace
fluid::BorderHandler::BorderHandler(int border_size)
{
CV_Assert(border_size > 0);
m_border_size = border_size;
}
template <int BorderType>
fluid::BorderHandlerT<BorderType>::BorderHandlerT(int border_size, int data_type)
: BorderHandler(border_size)
{
auto getFillBorderRowFunc = [&](int border, int dataType) {
if (border == cv::BORDER_REPLICATE)
{
switch(dataType)
{
case CV_8U: return &fillBorderReplicateRow< uint8_t>; break;
case CV_16S: return &fillBorderReplicateRow< int16_t>; break;
case CV_16U: return &fillBorderReplicateRow<uint16_t>; break;
case CV_32F: return &fillBorderReplicateRow< float >; break;
default: CV_Assert(!"Unsupported data type"); return &fillBorderReplicateRow<uint8_t>;
}
}
else if (border == cv::BORDER_REFLECT_101)
{
switch(dataType)
{
case CV_8U: return &fillBorderReflectRow< uint8_t>; break;
case CV_16S: return &fillBorderReflectRow< int16_t>; break;
case CV_16U: return &fillBorderReflectRow<uint16_t>; break;
case CV_32F: return &fillBorderReflectRow< float >; break;
default: CV_Assert(!"Unsupported data type"); return &fillBorderReflectRow<uint8_t>;
}
}
else
{
CV_Assert(!"Unsupported border type");
return &fillBorderReflectRow<uint8_t>;
}
};
m_fill_border_row = getFillBorderRowFunc(BorderType, data_type);
}
namespace {
template <int BorderType> int getBorderIdx(int log_idx, int desc_height);
template<> int getBorderIdx<cv::BORDER_REPLICATE>(int log_idx, int desc_height)
{
return log_idx < 0 ? 0 : desc_height - 1;
}
template<> int getBorderIdx<cv::BORDER_REFLECT_101>(int log_idx, int desc_height)
{
return log_idx < 0 ? -log_idx : 2*(desc_height - 1) - log_idx;
}
} // namespace
template <int BorderType>
const uint8_t* fluid::BorderHandlerT<BorderType>::inLineB(int log_idx, const BufferStorageWithBorder& data, int desc_height) const
{
auto idx = getBorderIdx<BorderType>(log_idx, desc_height);
return data.ptr(idx);
}
fluid::BorderHandlerT<cv::BORDER_CONSTANT>::BorderHandlerT(int border_size, cv::gapi::own::Scalar border_value, int data_type, int desc_width)
: BorderHandler(border_size), m_border_value(border_value)
{
m_const_border.create(1, desc_width + 2*m_border_size, data_type);
m_const_border = cv::gapi::own::to_ocv(border_value);
}
const uint8_t* fluid::BorderHandlerT<cv::BORDER_CONSTANT>::inLineB(int /*log_idx*/, const BufferStorageWithBorder& /*data*/, int /*desc_height*/) const
{
return m_const_border.ptr(0, m_border_size);
}
void fluid::BorderHandlerT<cv::BORDER_CONSTANT>::fillCompileTimeBorder(BufferStorageWithBorder& data) const
{
cv::gapi::fillBorderConstant(m_border_size, m_border_value, data.data());
}
template <int BorderType>
void fluid::BorderHandlerT<BorderType>::updateBorderPixels(BufferStorageWithBorder &data, int startLine, int nLines) const
{
auto& mat = data.data();
auto length = mat.cols;
auto chan = mat.channels();
for (int l = startLine; l < startLine + nLines; l++)
{
auto row = mat.ptr(data.physIdx(l));
m_fill_border_row(row, length, chan, m_border_size);
}
}
std::size_t fluid::BorderHandlerT<cv::BORDER_CONSTANT>::size() const
{
return m_const_border.total() * m_const_border.elemSize();
}
// Fluid BufferStorage implementation //////////////////////////////////////////
void fluid::BufferStorageWithBorder::create(int capacity, int desc_width, int dtype, int border_size, Border border)
{
auto width = (desc_width + 2*border_size);
m_data.create(capacity, width, dtype);
switch(border.type)
{
case cv::BORDER_CONSTANT:
m_borderHandler.reset(new BorderHandlerT<cv::BORDER_CONSTANT>(border_size, border.value, dtype, desc_width)); break;
case cv::BORDER_REPLICATE:
m_borderHandler.reset(new BorderHandlerT<cv::BORDER_REPLICATE>(border_size, dtype)); break;
case cv::BORDER_REFLECT_101:
m_borderHandler.reset(new BorderHandlerT<cv::BORDER_REFLECT_101>(border_size, dtype)); break;
default:
CV_Assert(false);
}
m_borderHandler->fillCompileTimeBorder(*this);
}
void fluid::BufferStorageWithoutBorder::create(int capacity, int desc_width, int dtype)
{
auto width = desc_width;
m_data.create(capacity, width, dtype);
m_is_virtual = true;
}
const uint8_t* fluid::BufferStorageWithBorder::inLineB(int log_idx, int desc_height) const
{
if (log_idx < 0 || log_idx >= desc_height)
{
return m_borderHandler->inLineB(log_idx, *this, desc_height);
}
else
{
return ptr(log_idx);
}
}
const uint8_t* fluid::BufferStorageWithoutBorder::inLineB(int log_idx, int /*desc_height*/) const
{
return ptr(log_idx);
}
static void copyWithoutBorder(const cv::Mat& src, int src_border_size, cv::Mat& dst, int dst_border_size, int startSrcLine, int startDstLine, int lpi)
{
// FIXME use cv::gapi::own::Rect when implement cv::gapi::own::Mat
auto subSrc = src(cv::Rect{src_border_size, startSrcLine, src.cols - 2*src_border_size, lpi});
auto subDst = dst(cv::Rect{dst_border_size, startDstLine, dst.cols - 2*dst_border_size, lpi});
subSrc.copyTo(subDst);
}
void fluid::BufferStorageWithoutBorder::copyTo(BufferStorageWithBorder &dst, int startLine, int nLines) const
{
for (int l = startLine; l < startLine + nLines; l++)
{
copyWithoutBorder(m_data, 0, dst.data(), dst.borderSize(), physIdx(l), dst.physIdx(l), 1);
}
}
void fluid::BufferStorageWithBorder::copyTo(BufferStorageWithBorder &dst, int startLine, int nLines) const
{
// Copy required lpi lines line by line (to avoid wrap if invoked for multiple lines)
for (int l = startLine; l < startLine + nLines; l++)
{
copyWithoutBorder(m_data, borderSize(), dst.data(), dst.borderSize(), physIdx(l), dst.physIdx(l), 1);
}
}
// FIXME? remember parent and remove src parameter?
void fluid::BufferStorageWithBorder::updateBeforeRead(int startLine, int nLines, const BufferStorage& src)
{
// TODO:
// Cover with tests!!
// (Ensure that there are no redundant copies done
// and only required (not fetched before) lines are copied)
GAPI_DbgAssert(startLine >= 0);
src.copyTo(*this, startLine, nLines);
m_borderHandler->updateBorderPixels(*this, startLine, nLines);
}
void fluid::BufferStorageWithoutBorder::updateBeforeRead(int /*startLine*/, int /*lpi*/, const BufferStorage& /*src*/)
{
/* nothing */
}
void fluid::BufferStorageWithBorder::updateAfterWrite(int startLine, int nLines)
{
// FIXME?
// Actually startLine + nLines can be > logical height so
// redundant end lines which will never be read
// can be filled in the ring buffer
m_borderHandler->updateBorderPixels(*this, startLine, nLines);
}
void fluid::BufferStorageWithoutBorder::updateAfterWrite(int /*startLine*/, int /*lpi*/)
{
/* nothing */
}
size_t fluid::BufferStorageWithBorder::size() const
{
return m_data.total()*m_data.elemSize() + m_borderHandler->size();
}
size_t fluid::BufferStorageWithoutBorder::size() const
{
return m_data.total()*m_data.elemSize();
}
namespace fluid {
namespace {
std::unique_ptr<fluid::BufferStorage> createStorage(int capacity, int desc_width, int type,
int border_size, fluid::BorderOpt border);
std::unique_ptr<fluid::BufferStorage> createStorage(int capacity, int desc_width, int type,
int border_size, fluid::BorderOpt border)
{
if (border)
{
std::unique_ptr<fluid::BufferStorageWithBorder> storage(new BufferStorageWithBorder);
storage->create(capacity, desc_width, type, border_size, border.value());
return std::move(storage);
}
std::unique_ptr<BufferStorageWithoutBorder> storage(new BufferStorageWithoutBorder);
storage->create(capacity, desc_width, type);
return std::move(storage);
}
std::unique_ptr<BufferStorage> createStorage(const cv::Mat& data, cv::gapi::own::Rect roi);
std::unique_ptr<BufferStorage> createStorage(const cv::Mat& data, cv::gapi::own::Rect roi)
{
std::unique_ptr<BufferStorageWithoutBorder> storage(new BufferStorageWithoutBorder);
storage->attach(data, roi);
return std::move(storage);
}
} // namespace
} // namespace fluid
// Fluid View implementation ///////////////////////////////////////////////////
void fluid::View::Priv::reset(int linesForFirstIteration)
{
GAPI_DbgAssert(m_p);
m_lines_next_iter = linesForFirstIteration;
m_read_caret = m_p->priv().readStart();
}
void fluid::View::Priv::readDone(int linesRead, int linesForNextIteration)
{
CV_DbgAssert(m_p);
m_read_caret += linesRead;
m_read_caret %= m_p->meta().size.height;
m_lines_next_iter = linesForNextIteration;
}
bool fluid::View::Priv::ready() const
{
auto lastWrittenLine = m_p->priv().writeStart() + m_p->linesReady();
// + bottom border
if (lastWrittenLine == m_p->meta().size.height) lastWrittenLine += m_border_size;
// + top border
lastWrittenLine += m_border_size;
auto lastRequiredLine = m_read_caret + m_lines_next_iter;
return lastWrittenLine >= lastRequiredLine;
}
fluid::ViewPrivWithoutOwnBorder::ViewPrivWithoutOwnBorder(const Buffer *parent, int borderSize)
{
CV_Assert(parent);
m_p = parent;
m_border_size = borderSize;
}
const uint8_t* fluid::ViewPrivWithoutOwnBorder::InLineB(int index) const
{
GAPI_DbgAssert(m_p);
const auto &p_priv = m_p->priv();
CV_Assert( index >= -m_border_size
&& index < -m_border_size + m_lines_next_iter);
const int log_idx = m_read_caret + index;
return p_priv.storage().inLineB(log_idx, m_p->meta().size.height);
}
fluid::ViewPrivWithOwnBorder::ViewPrivWithOwnBorder(const Buffer *parent, int lineConsumption, int borderSize, Border border)
{
GAPI_Assert(parent);
m_p = parent;
m_border_size = borderSize;
auto desc = m_p->meta();
int type = CV_MAKETYPE(desc.depth, desc.chan);
m_own_storage.create(lineConsumption, desc.size.width, type, borderSize, border);
}
void fluid::ViewPrivWithOwnBorder::prepareToRead()
{
int startLine = 0;
int nLines = 0;
if (m_read_caret == m_p->priv().readStart())
{
// Need to fetch full window on the first iteration
startLine = (m_read_caret > m_border_size) ? m_read_caret - m_border_size : 0;
nLines = m_lines_next_iter;
}
else
{
startLine = m_read_caret + m_border_size;
nLines = m_lines_next_iter - 2*m_border_size;
}
m_own_storage.updateBeforeRead(startLine, nLines, m_p->priv().storage());
}
std::size_t fluid::ViewPrivWithOwnBorder::size() const
{
GAPI_DbgAssert(m_p);
return m_own_storage.size();
}
const uint8_t* fluid::ViewPrivWithOwnBorder::InLineB(int index) const
{
GAPI_DbgAssert(m_p);
GAPI_Assert( index >= -m_border_size
&& index < -m_border_size + m_lines_next_iter);
const int log_idx = m_read_caret + index;
return m_own_storage.inLineB(log_idx, m_p->meta().size.height);
}
const uint8_t* fluid::View::InLineB(int index) const
{
return m_priv->InLineB(index);
}
fluid::View::operator bool() const
{
return m_priv != nullptr && m_priv->m_p != nullptr;
}
int fluid::View::length() const
{
return m_priv->m_p->length();
}
bool fluid::View::ready() const
{
return m_priv->ready();
}
int fluid::View::y() const
{
return m_priv->m_read_caret - m_priv->m_border_size;
}
cv::GMatDesc fluid::View::meta() const
{
// FIXME: cover with test!
return m_priv->m_p->meta();
}
fluid::View::Priv& fluid::View::priv()
{
return *m_priv;
}
const fluid::View::Priv& fluid::View::priv() const
{
return *m_priv;
}
// Fluid Buffer implementation /////////////////////////////////////////////////
fluid::Buffer::Priv::Priv(int read_start, cv::gapi::own::Rect roi)
: m_readStart(read_start)
, m_roi(roi)
{}
void fluid::Buffer::Priv::init(const cv::GMatDesc &desc,
int line_consumption,
int border_size,
int skew,
int wlpi,
int readStartPos,
cv::gapi::own::Rect roi)
{
GAPI_Assert(m_line_consumption == -1);
GAPI_Assert(line_consumption > 0);
m_line_consumption = line_consumption;
m_border_size = border_size;
m_skew = skew;
m_writer_lpi = wlpi;
m_desc = desc;
m_readStart = readStartPos;
m_roi = roi;
}
void fluid::Buffer::Priv::allocate(BorderOpt border)
{
GAPI_Assert(!m_storage);
// Init physical buffer
// FIXME? combine with skew?
auto maxRead = m_line_consumption + m_skew;
auto maxWritten = m_writer_lpi;
auto max = std::max(maxRead, maxWritten);
auto min = std::min(maxRead, maxWritten);
// FIXME:
// Fix the deadlock (completely)!!!
auto data_height = static_cast<int>(std::ceil((double)max / min) * min);
m_storage = createStorage(data_height,
m_desc.size.width,
CV_MAKETYPE(m_desc.depth, m_desc.chan),
m_border_size,
border);
// Finally, initialize carets
m_write_caret = 0;
}
void fluid::Buffer::Priv::bindTo(const cv::Mat &data, bool is_input)
{
// FIXME: move all these fields into a separate structure
GAPI_Assert(m_skew == 0);
GAPI_Assert(m_desc == cv::descr_of(data));
if ( is_input) CV_Assert(m_writer_lpi == 1);
m_storage = createStorage(data, m_roi);
m_is_input = is_input;
m_write_caret = is_input ? writeEnd(): writeStart();
// NB: views remain the same!
}
bool fluid::Buffer::Priv::full() const
{
int slowest_y = writeEnd();
if (!m_views.empty())
{
// reset with maximum possible value and then find minimum
slowest_y = m_desc.size.height;
for (const auto &v : m_views) slowest_y = std::min(slowest_y, v.y());
}
return m_write_caret + lpi() - slowest_y > m_storage->rows();
}
void fluid::Buffer::Priv::writeDone()
{
// There are possible optimizations which can be done to fill a border values
// in compile time of the graph (for example border is const),
// so there is no need to update border values after each write.
// If such optimizations weren't applied, fill border for lines
// which have been just written
m_storage->updateAfterWrite(m_write_caret, m_writer_lpi);
// Final write may produce less LPI, so
// write caret may exceed logical buffer size
m_write_caret += m_writer_lpi;
// FIXME: add consistency check!
}
void fluid::Buffer::Priv::reset()
{
m_write_caret = m_is_input ? writeEnd() : writeStart();
}
int fluid::Buffer::Priv::size() const
{
std::size_t view_sz = 0;
for (const auto &v : m_views) view_sz += v.priv().size();
auto total = view_sz;
if (m_storage) total += m_storage->size();
// FIXME: Change API to return size_t!!!
return static_cast<int>(total);
}
int fluid::Buffer::Priv::linesReady() const
{
if (m_is_input)
{
return m_storage->rows();
}
else
{
const int writes = std::min(m_write_caret - writeStart(), outputLines());
return writes;
}
}
uint8_t* fluid::Buffer::Priv::OutLineB(int index)
{
GAPI_Assert(index >= 0 && index < m_writer_lpi);
return m_storage->ptr(m_write_caret + index);
}
int fluid::Buffer::Priv::lpi() const
{
// FIXME:
// m_write_caret can be greater than m_writeRoi.y + m_writeRoi.height, so return value can be negative !!!
return std::min(writeEnd() - m_write_caret, m_writer_lpi);
}
fluid::Buffer::Buffer()
: m_priv(new Priv())
{
}
fluid::Buffer::Buffer(const cv::GMatDesc &desc)
: m_priv(new Priv())
{
int lineConsumption = 1;
int border = 0, skew = 0, wlpi = 1, readStart = 0;
cv::gapi::own::Rect roi = {0, 0, desc.size.width, desc.size.height};
m_priv->init(desc, lineConsumption, border, skew, wlpi, readStart, roi);
m_priv->allocate({});
}
fluid::Buffer::Buffer(const cv::GMatDesc &desc,
int max_line_consumption,
int border_size,
int skew,
int wlpi,
BorderOpt border)
: m_priv(new Priv())
{
int readStart = 0;
cv::gapi::own::Rect roi = {0, 0, desc.size.width, desc.size.height};
m_priv->init(desc, max_line_consumption, border_size, skew, wlpi, readStart, roi);
m_priv->allocate(border);
}
fluid::Buffer::Buffer(const cv::Mat &data, bool is_input)
: m_priv(new Priv())
{
int lineConsumption = 1;
int border = 0, skew = 0, wlpi = 1, readStart = 0;
cv::gapi::own::Rect roi{0, 0, data.cols, data.rows};
m_priv->init(descr_of(data), lineConsumption, border, skew, wlpi, readStart, roi);
m_priv->bindTo(data, is_input);
}
uint8_t* fluid::Buffer::Buffer::OutLineB(int index)
{
return m_priv->OutLineB(index);
}
int fluid::Buffer::linesReady() const
{
return m_priv->linesReady();
}
int fluid::Buffer::length() const
{
return meta().size.width;
}
int fluid::Buffer::lpi() const
{
return m_priv->lpi();
}
cv::GMatDesc fluid::Buffer::meta() const
{
return m_priv->meta();
}
fluid::View::View(Priv* p)
: m_priv(p)
{ /* nothing */ }
fluid::View fluid::Buffer::mkView(int lineConsumption, int borderSize, BorderOpt border, bool ownStorage)
{
// FIXME: logic outside of Priv (because View takes pointer to Buffer)
auto view = ownStorage ? View(new ViewPrivWithOwnBorder(this, lineConsumption, borderSize, border.value()))
: View(new ViewPrivWithoutOwnBorder(this, borderSize));
m_priv->addView(view);
return view;
}
void fluid::debugBufferPriv(const fluid::Buffer& buffer, std::ostream &os)
{
// FIXME Use cv::gapi::own Size and Rect with operator<<, when merged ADE-285
const auto& p = buffer.priv();
os << "Fluid buffer " << std::hex << &buffer << std::dec
<< " " << p.m_desc.size.width << " x " << p.m_desc.size.height << "]"
<< " readStart:" << p.m_readStart
<< " roi:" << "[" << p.m_roi.width << " x " << p.m_roi.height << " from (" << p.m_roi.x << ", " << p.m_roi.y << ")]"
<<" (phys " << "[" << p.storage().cols() << " x " << p.storage().rows() << "]" << ") :"
<< " w: " << p.m_write_caret
<< ", r: [";
for (const auto &v : p.m_views) { os << &v.priv() << ":" << v.y() << " "; }
os << "], avail: " << buffer.linesReady()
<< std::endl;
}
void fluid::Buffer::debug(std::ostream &os) const
{
debugBufferPriv(*this, os);
}
fluid::Buffer::Priv& fluid::Buffer::priv()
{
return *m_priv;
}
const fluid::Buffer::Priv& fluid::Buffer::priv() const
{
return *m_priv;
}
int fluid::Buffer::y() const
{
return m_priv->y();
}
} // namespace cv::gapi
} // namespace cv
@@ -0,0 +1,298 @@
// 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) 2018 Intel Corporation
#ifndef OPENCV_GAPI_FLUID_BUFFER_PRIV_HPP
#define OPENCV_GAPI_FLUID_BUFFER_PRIV_HPP
#include <vector>
#include "opencv2/gapi/fluid/gfluidbuffer.hpp"
#include "opencv2/gapi/own/convert.hpp" // cv::gapi::own::to_ocv
namespace cv {
namespace gapi {
namespace fluid {
class BufferStorageWithBorder;
class BorderHandler
{
protected:
int m_border_size;
public:
BorderHandler(int border_size);
virtual ~BorderHandler() = default;
virtual const uint8_t* inLineB(int log_idx, const BufferStorageWithBorder &data, int desc_height) const = 0;
// Fills border pixels after buffer allocation (if possible (for const border))
virtual void fillCompileTimeBorder(BufferStorageWithBorder &) const { /* nothing */ }
// Fills required border lines
virtual void updateBorderPixels(BufferStorageWithBorder& /*data*/, int /*startLine*/, int /*lpi*/) const { /* nothing */ }
inline int borderSize() const { return m_border_size; }
virtual std::size_t size() const { return 0; }
};
template<int BorderType>
class BorderHandlerT : public BorderHandler
{
std::function<void(uint8_t*,int,int,int)> m_fill_border_row;
public:
BorderHandlerT(int border_size, int data_type);
virtual void updateBorderPixels(BufferStorageWithBorder& data, int startLine, int lpi) const override;
virtual const uint8_t* inLineB(int log_idx, const BufferStorageWithBorder &data, int desc_height) const override;
};
template<>
class BorderHandlerT<cv::BORDER_CONSTANT> : public BorderHandler
{
cv::gapi::own::Scalar m_border_value;
cv::Mat m_const_border;
public:
BorderHandlerT(int border_size, cv::gapi::own::Scalar border_value, int data_type, int desc_width);
virtual const uint8_t* inLineB(int log_idx, const BufferStorageWithBorder &data, int desc_height) const override;
virtual void fillCompileTimeBorder(BufferStorageWithBorder &) const override;
virtual std::size_t size() const override;
};
class BufferStorage
{
protected:
cv::Mat m_data;
public:
virtual void copyTo(BufferStorageWithBorder &dst, int startLine, int nLines) const = 0;
virtual ~BufferStorage() = default;
virtual const uint8_t* ptr(int idx) const = 0;
virtual uint8_t* ptr(int idx) = 0;
inline bool empty() const { return m_data.empty(); }
inline const cv::Mat& data() const { return m_data; }
inline cv::Mat& data() { return m_data; }
inline int rows() const { return m_data.rows; }
inline int cols() const { return m_data.cols; }
inline int type() const { return m_data.type(); }
virtual const uint8_t* inLineB(int log_idx, int desc_height) const = 0;
// FIXME? remember parent and remove src parameter?
virtual void updateBeforeRead(int startLine, int nLines, const BufferStorage& src) = 0;
virtual void updateAfterWrite(int startLine, int nLines) = 0;
virtual int physIdx(int logIdx) const = 0;
virtual size_t size() const = 0;
};
class BufferStorageWithoutBorder final : public BufferStorage
{
bool m_is_virtual = true;
cv::gapi::own::Rect m_roi;
public:
virtual void copyTo(BufferStorageWithBorder &dst, int startLine, int nLines) const override;
inline virtual const uint8_t* ptr(int idx) const override
{
GAPI_DbgAssert((m_is_virtual && m_roi == cv::gapi::own::Rect{}) || (!m_is_virtual && m_roi != cv::gapi::own::Rect{}));
return m_data.ptr(physIdx(idx), 0);
}
inline virtual uint8_t* ptr(int idx) override
{
GAPI_DbgAssert((m_is_virtual && m_roi == cv::gapi::own::Rect{}) || (!m_is_virtual && m_roi != cv::gapi::own::Rect{}));
return m_data.ptr(physIdx(idx), 0);
}
inline void attach(const cv::Mat& _data, const cv::gapi::own::Rect& _roi)
{
m_data = _data(cv::gapi::own::to_ocv(_roi));
m_roi = _roi;
m_is_virtual = false;
}
void create(int capacity, int desc_width, int type);
inline virtual const uint8_t* inLineB(int log_idx, int desc_height) const override;
virtual void updateBeforeRead(int startLine, int nLines, const BufferStorage& src) override;
virtual void updateAfterWrite(int startLine, int nLines) override;
inline virtual int physIdx(int logIdx) const override { return (logIdx - m_roi.y) % m_data.rows; }
virtual size_t size() const override;
};
class BufferStorageWithBorder final: public BufferStorage
{
std::unique_ptr<BorderHandler> m_borderHandler;
public:
inline int borderSize() const { return m_borderHandler->borderSize(); }
virtual void copyTo(BufferStorageWithBorder &dst, int startLine, int nLines) const override;
inline virtual const uint8_t* ptr(int idx) const override
{
return m_data.ptr(physIdx(idx), borderSize());
}
inline virtual uint8_t* ptr(int idx) override
{
return m_data.ptr(physIdx(idx), borderSize());
}
void create(int capacity, int desc_width, int type, int border_size, Border border);
virtual const uint8_t* inLineB(int log_idx, int desc_height) const override;
virtual void updateBeforeRead(int startLine, int nLines, const BufferStorage &src) override;
virtual void updateAfterWrite(int startLine, int nLines) override;
inline virtual int physIdx(int logIdx) const override { return logIdx % m_data.rows; }
virtual size_t size() const override;
};
// FIXME: GAPI_EXPORTS is used here only to access internal methods
// like readDone/writeDone in low-level tests
class GAPI_EXPORTS View::Priv
{
friend class View;
protected:
const Buffer *m_p = nullptr; // FIXME replace with weak_ptr
int m_read_caret = -1;
int m_lines_next_iter = -1;
int m_border_size = -1;
public:
virtual ~Priv() = default;
// API used by actors/backend
virtual void prepareToRead() = 0;
void readDone(int linesRead, int linesForNextIteration);
void reset(int linesForFirstIteration);
virtual std::size_t size() const = 0;
// Does the view have enough unread lines for next iteration
bool ready() const;
// API used (indirectly) by user code
virtual const uint8_t* InLineB(int index) const = 0;
};
class ViewPrivWithoutOwnBorder final : public View::Priv
{
public:
// API used by actors/backend
ViewPrivWithoutOwnBorder(const Buffer *p, int borderSize);
virtual void prepareToRead() override { /* nothing */ }
virtual std::size_t size() const override { return 0; }
// API used (indirectly) by user code
virtual const uint8_t* InLineB(int index) const override;
};
class ViewPrivWithOwnBorder final : public View::Priv
{
BufferStorageWithBorder m_own_storage;
public:
// API used by actors/backend
ViewPrivWithOwnBorder(const Buffer *p, int lineCapacity, int borderSize, Border border);
virtual void prepareToRead() override;
virtual std::size_t size() const override;
// API used (indirectly) by user code
virtual const uint8_t* InLineB(int index) const override;
};
void debugBufferPriv(const Buffer& buffer, std::ostream &os);
// FIXME: GAPI_EXPORTS is used here only to access internal methods
// like readDone/writeDone in low-level tests
class GAPI_EXPORTS Buffer::Priv
{
int m_line_consumption = -1;
int m_border_size = -1;
int m_skew = -1;
int m_writer_lpi = 1;
cv::GMatDesc m_desc = cv::GMatDesc{-1,-1,{-1,-1}};
bool m_is_input = false;
int m_write_caret = -1;
std::vector<View> m_views;
std::unique_ptr<BufferStorage> m_storage;
// Coordinate starting from which this buffer is assumed
// to be read (with border not being taken into account)
int m_readStart;
cv::gapi::own::Rect m_roi;
friend void debugBufferPriv(const Buffer& p, std::ostream &os);
public:
Priv() = default;
Priv(int read_start, cv::gapi::own::Rect roi);
inline const BufferStorage& storage() const { return *m_storage.get(); }
// API used by actors/backend
void init(const cv::GMatDesc &desc,
int line_consumption,
int border_size,
int skew,
int wlpi,
int readStart,
cv::gapi::own::Rect roi);
void allocate(BorderOpt border);
void bindTo(const cv::Mat &data, bool is_input);
void addView(const View& view) { m_views.push_back(view); }
const GMatDesc meta() const { return m_desc; }
bool full() const;
void writeDone();
void reset();
int size() const;
int linesReady() const;
inline int y() const { return m_write_caret; }
inline int writer_lpi() const { return m_writer_lpi; }
// API used (indirectly) by user code
uint8_t* OutLineB(int index = 0);
int lpi() const;
inline int readStart() const { return m_readStart; }
inline int writeStart() const { return m_roi.y; }
inline int writeEnd() const { return m_roi.y + m_roi.height; }
inline int outputLines() const { return m_roi.height; }
};
} // namespace cv::gapi::fluid
} // namespace cv::gapi
} // namespace cv
#endif // OPENCV_GAPI_FLUID_BUFFER_PRIV_HPP
File diff suppressed because it is too large Load Diff
@@ -0,0 +1,19 @@
// 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) 2018 Intel Corporation
#ifndef OPENCV_GAPI_GFLUIDCORE_HPP
#define OPENCV_GAPI_GFLUIDCORE_HPP
#include "opencv2/gapi/fluid/gfluidkernel.hpp"
namespace cv { namespace gapi { namespace core { namespace fluid {
GAPI_EXPORTS GKernelPackage kernels();
}}}}
#endif // OPENCV_GAPI_GFLUIDCORE_HPP
File diff suppressed because it is too large Load Diff
@@ -0,0 +1,19 @@
// 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) 2018 Intel Corporation
#ifndef OPENCV_GAPI_GFLUIDIMGPROC_HPP
#define OPENCV_GAPI_GFLUIDIMGPROC_HPP
#include "opencv2/gapi/fluid/gfluidkernel.hpp"
namespace cv { namespace gapi { namespace imgproc { namespace fluid {
GAPI_EXPORTS GKernelPackage kernels();
}}}}
#endif // OPENCV_GAPI_GFLUIDIMGPROC_HPP
@@ -0,0 +1,154 @@
// 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) 2018 Intel Corporation
#ifndef GFLUIDUTILS_HPP
#define GFLUIDUTILS_HPP
#include <limits>
#include <type_traits>
#include <opencv2/gapi/util/compiler_hints.hpp> //UNUSED
namespace cv {
namespace gapi {
namespace fluid {
//-----------------------------
//
// Numeric cast with saturation
//
//-----------------------------
template<typename DST, typename SRC>
static inline DST saturate(SRC x)
{
// only integral types please!
GAPI_DbgAssert(std::is_integral<DST>::value &&
std::is_integral<SRC>::value);
if (std::is_same<DST, SRC>::value)
return static_cast<DST>(x);
if (sizeof(DST) > sizeof(SRC))
return static_cast<DST>(x);
// compiler must recognize this saturation,
// so compile saturate<s16>(a + b) with adds
// instruction (e.g.: _mm_adds_epi16 if x86)
return x < std::numeric_limits<DST>::min()?
std::numeric_limits<DST>::min():
x > std::numeric_limits<DST>::max()?
std::numeric_limits<DST>::max():
static_cast<DST>(x);
}
// Note, that OpenCV rounds differently:
// - like std::round() for add, subtract
// - like std::rint() for multiply, divide
template<typename DST, typename SRC, typename R>
static inline DST saturate(SRC x, R round)
{
if (std::is_floating_point<DST>::value)
{
return static_cast<DST>(x);
}
else if (std::is_integral<SRC>::value)
{
GAPI_DbgAssert(std::is_integral<DST>::value &&
std::is_integral<SRC>::value);
return saturate<DST>(x);
}
else
{
GAPI_DbgAssert(std::is_integral<DST>::value &&
std::is_floating_point<SRC>::value);
#ifdef _WIN32
// Suppress warning about convering x to floating-point
// Note that x is already floating-point at this point
#pragma warning(disable: 4244)
#endif
int ix = static_cast<int>(round(x));
#ifdef _WIN32
#pragma warning(default: 4244)
#endif
return saturate<DST>(ix);
}
}
// explicit suffix 'd' for double type
static inline double ceild(double x) { return std::ceil(x); }
static inline double floord(double x) { return std::floor(x); }
static inline double roundd(double x) { return std::round(x); }
static inline double rintd(double x) { return std::rint(x); }
//--------------------------------
//
// Macros for mappig of data types
//
//--------------------------------
#define UNARY_(DST, SRC, OP, ...) \
if (cv::DataType<DST>::depth == dst.meta().depth && \
cv::DataType<SRC>::depth == src.meta().depth) \
{ \
GAPI_DbgAssert(dst.length() == src.length()); \
GAPI_DbgAssert(dst.meta().chan == src.meta().chan); \
\
OP<DST, SRC>(__VA_ARGS__); \
return; \
}
// especial unary operation: dst is always 8UC1 image
#define INRANGE_(DST, SRC, OP, ...) \
if (cv::DataType<DST>::depth == dst.meta().depth && \
cv::DataType<SRC>::depth == src.meta().depth) \
{ \
GAPI_DbgAssert(dst.length() == src.length()); \
GAPI_DbgAssert(dst.meta().chan == 1); \
\
OP<DST, SRC>(__VA_ARGS__); \
return; \
}
#define BINARY_(DST, SRC1, SRC2, OP, ...) \
if (cv::DataType<DST>::depth == dst.meta().depth && \
cv::DataType<SRC1>::depth == src1.meta().depth && \
cv::DataType<SRC2>::depth == src2.meta().depth) \
{ \
GAPI_DbgAssert(dst.length() == src1.length()); \
GAPI_DbgAssert(dst.length() == src2.length()); \
\
GAPI_DbgAssert(dst.meta().chan == src1.meta().chan); \
GAPI_DbgAssert(dst.meta().chan == src2.meta().chan); \
\
OP<DST, SRC1, SRC2>(__VA_ARGS__); \
return; \
}
// especial ternary operation: src3 has only one channel
#define SELECT_(DST, SRC1, SRC2, SRC3, OP, ...) \
if (cv::DataType<DST>::depth == dst.meta().depth && \
cv::DataType<SRC1>::depth == src1.meta().depth && \
cv::DataType<SRC2>::depth == src2.meta().depth && \
cv::DataType<SRC3>::depth == src3.meta().depth) \
{ \
GAPI_DbgAssert(dst.length() == src1.length()); \
GAPI_DbgAssert(dst.length() == src2.length()); \
GAPI_DbgAssert(dst.length() == src3.length()); \
\
GAPI_DbgAssert(dst.meta().chan == src1.meta().chan); \
GAPI_DbgAssert(dst.meta().chan == src2.meta().chan); \
GAPI_DbgAssert( 1 == src3.meta().chan); \
\
OP<DST, SRC1, SRC2, SRC3>(__VA_ARGS__); \
return; \
}
} // namespace fluid
} // namespace gapi
} // namespace cv
#endif // GFLUIDUTILS_HPP