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:
committed by
Alexander Alekhin
parent
852f061b26
commit
29e88e50ff
@@ -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
|
||||
@@ -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
|
||||
Reference in New Issue
Block a user