"${CMAKE_CURRENT_LIST_DIR}/include/opencv2/${name}/own/*.hpp"
"${CMAKE_CURRENT_LIST_DIR}/include/opencv2/${name}/render/*.hpp"
"${CMAKE_CURRENT_LIST_DIR}/include/opencv2/${name}/streaming/*.hpp"
+ "${CMAKE_CURRENT_LIST_DIR}/include/opencv2/${name}/plaidml/*.hpp"
"${CMAKE_CURRENT_LIST_DIR}/include/opencv2/${name}/util/*.hpp"
)
src/backends/render/grenderocvbackend.cpp
src/backends/render/grenderocv.cpp
+ #PlaidML Backend
+ src/backends/plaidml/gplaidmlcore.cpp
+ src/backends/plaidml/gplaidmlbackend.cpp
+
# Compound
src/backends/common/gcompoundbackend.cpp
src/backends/common/gcompoundkernel.cpp
target_link_libraries(opencv_test_gapi PRIVATE ade)
endif()
+if(HAVE_PLAIDML)
+ ocv_target_compile_definitions(opencv_gapi PRIVATE -DHAVE_PLAIDML)
+ ocv_target_compile_definitions(opencv_test_gapi PRIVATE -DHAVE_PLAIDML)
+ ocv_target_link_libraries(opencv_gapi LINK_PRIVATE ${PLAIDML_LIBRARIES})
+ ocv_target_include_directories(opencv_gapi SYSTEM PRIVATE ${PLAIDML_INCLUDE_DIRS})
+endif()
+
ocv_add_perf_tests()
ocv_add_samples()
OCV_OPTION(WITH_ADE "Enable ADE framework (required for Graph API module)" ON)
+OCV_OPTION(WITH_PLAIDML "Include PlaidML2 support" OFF)
if(NOT WITH_ADE)
return()
endif()
-if (ade_DIR)
+if(ade_DIR)
# if ade_DIR is set, use ADE-supplied CMake script
# to set up variables to the prebuilt ADE
find_package(ade 0.1.0)
# downloaded one (if there any)
include("${CMAKE_CURRENT_LIST_DIR}/DownloadADE.cmake")
endif()
+
+if(WITH_PLAIDML)
+ find_package(PlaidML2 CONFIG QUIET)
+ if (PLAIDML_FOUND)
+ set(HAVE_PLAIDML TRUE)
+ endif()
+endif()
--- /dev/null
+// 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) 2019 Intel Corporation
+
+
+#ifndef OPENCV_GAPI_PLAIDML_CORE_HPP
+#define OPENCV_GAPI_PLAIDML_CORE_HPP
+
+#include <opencv2/gapi/gkernel.hpp> // GKernelPackage
+#include <opencv2/gapi/own/exports.hpp> // GAPI_EXPORTS
+
+namespace cv { namespace gapi { namespace core { namespace plaidml {
+
+GAPI_EXPORTS GKernelPackage kernels();
+
+}}}}
+
+#endif // OPENCV_GAPI_PLAIDML_CORE_HPP
--- /dev/null
+// 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) 2019 Intel Corporation
+//
+
+
+#ifndef OPENCV_GAPI_GPLAIDMLKERNEL_HPP
+#define OPENCV_GAPI_GPLAIDMLKERNEL_HPP
+
+#include <opencv2/gapi/gkernel.hpp>
+#include <opencv2/gapi/garg.hpp>
+
+namespace plaidml
+{
+namespace edsl
+{
+ class Tensor;
+} // namespace edsl
+} // namespace plaidml
+
+namespace cv
+{
+namespace gapi
+{
+namespace plaidml
+{
+
+GAPI_EXPORTS cv::gapi::GBackend backend();
+
+} // namespace plaidml
+} // namespace gapi
+
+struct GPlaidMLContext
+{
+ // Generic accessor API
+ template<typename T>
+ const T& inArg(int input) { return m_args.at(input).get<T>(); }
+
+ // Syntax sugar
+ const plaidml::edsl::Tensor& inTensor(int input)
+ {
+ return inArg<plaidml::edsl::Tensor>(input);
+ }
+
+ plaidml::edsl::Tensor& outTensor(int output)
+ {
+ return *(m_results.at(output).get<plaidml::edsl::Tensor*>());
+ }
+
+ std::vector<GArg> m_args;
+ std::unordered_map<std::size_t, GArg> m_results;
+};
+
+class GAPI_EXPORTS GPlaidMLKernel
+{
+public:
+ using F = std::function<void(GPlaidMLContext &)>;
+
+ GPlaidMLKernel() = default;
+ explicit GPlaidMLKernel(const F& f) : m_f(f) {};
+
+ void apply(GPlaidMLContext &ctx) const
+ {
+ GAPI_Assert(m_f);
+ m_f(ctx);
+ }
+
+protected:
+ F m_f;
+};
+
+
+namespace detail
+{
+
+template<class T> struct plaidml_get_in;
+template<> struct plaidml_get_in<cv::GMat>
+{
+ static const plaidml::edsl::Tensor& get(GPlaidMLContext& ctx, int idx)
+ {
+ return ctx.inTensor(idx);
+ }
+};
+
+template<class T> struct plaidml_get_in
+{
+ static T get(GPlaidMLContext &ctx, int idx) { return ctx.inArg<T>(idx); }
+};
+
+template<class T> struct plaidml_get_out;
+template<> struct plaidml_get_out<cv::GMat>
+{
+ static plaidml::edsl::Tensor& get(GPlaidMLContext& ctx, int idx)
+ {
+ return ctx.outTensor(idx);
+ }
+};
+
+template<typename, typename, typename>
+struct PlaidMLCallHelper;
+
+template<typename Impl, typename... Ins, typename... Outs>
+struct PlaidMLCallHelper<Impl, std::tuple<Ins...>, std::tuple<Outs...> >
+{
+ template<int... IIs, int... OIs>
+ static void call_impl(GPlaidMLContext &ctx, detail::Seq<IIs...>, detail::Seq<OIs...>)
+ {
+ Impl::run(plaidml_get_in<Ins>::get(ctx, IIs)..., plaidml_get_out<Outs>::get(ctx, OIs)...);
+ }
+
+ static void call(GPlaidMLContext& ctx)
+ {
+ call_impl(ctx,
+ typename detail::MkSeq<sizeof...(Ins)>::type(),
+ typename detail::MkSeq<sizeof...(Outs)>::type());
+ }
+};
+
+} // namespace detail
+
+template<class Impl, class K>
+class GPlaidMLKernelImpl: public cv::detail::PlaidMLCallHelper<Impl, typename K::InArgs, typename K::OutArgs>,
+ public cv::detail::KernelTag
+{
+ using P = detail::PlaidMLCallHelper<Impl, typename K::InArgs, typename K::OutArgs>;
+
+public:
+ using API = K;
+
+ static cv::gapi::GBackend backend() { return cv::gapi::plaidml::backend(); }
+ static cv::GPlaidMLKernel kernel() { return GPlaidMLKernel(&P::call); }
+};
+
+#define GAPI_PLAIDML_KERNEL(Name, API) struct Name: public cv::GPlaidMLKernelImpl<Name, API>
+
+} // namespace cv
+
+#endif // OPENCV_GAPI_GPLAIDMLKERNEL_HPP
--- /dev/null
+// 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) 2019 Intel Corporation
+
+
+#ifndef OPENCV_GAPI_PLAIDML_PLAIDML_HPP
+#define OPENCV_GAPI_PLAIDML_PLAIDML_HPP
+
+#include <string>
+#include <opencv2/gapi/gcommon.hpp> // CompileArgTag
+
+namespace cv
+{
+namespace gapi
+{
+namespace plaidml
+{
+
+struct config
+{
+ std::string dev_id;
+ std::string trg_id;
+};
+
+} // namespace plaidml
+} // namespace gapi
+
+namespace detail
+{
+ template<> struct CompileArgTag<cv::gapi::plaidml::config>
+ {
+ static const char* tag() { return "gapi.plaidml.config"; }
+ };
+} // namespace detail
+
+} // namespace cv
+
+#endif // OPENCV_GAPI_PLAIDML_PLAIDML_HPP
return {};
}
+std::unique_ptr<cv::gimpl::GIslandExecutable>
+cv::gapi::GBackend::Priv::compile(const ade::Graph& graph,
+ const GCompileArgs& args,
+ const std::vector<ade::NodeHandle>& nodes,
+ const std::vector<cv::gimpl::Data>&,
+ const std::vector<cv::gimpl::Data>&) const
+{
+ return compile(graph, args, nodes);
+}
+
void cv::gapi::GBackend::Priv::addBackendPasses(ade::ExecutionEngineSetupContext &)
{
// Do nothing by default, plugins may override this to
#include "opencv2/gapi/gcommon.hpp"
#include "opencv2/gapi/gkernel.hpp"
+#include "compiler/gmodel.hpp"
+
+
namespace cv
{
namespace gimpl
// there's no need in having both cv::gimpl::GBackend
// and cv::gapi::GBackend - these two things can be unified
// NOTE - nodes are guaranteed to be topologically sorted.
+
+ // NB: This method is deprecated
virtual EPtr compile(const ade::Graph &graph,
const GCompileArgs &args,
const std::vector<ade::NodeHandle> &nodes) const;
+ virtual EPtr compile(const ade::Graph &graph,
+ const GCompileArgs &args,
+ const std::vector<ade::NodeHandle> &nodes,
+ const std::vector<cv::gimpl::Data>& ins_data,
+ const std::vector<cv::gimpl::Data>& outs_data) const;
+
virtual void addBackendPasses(ade::ExecutionEngineSetupContext &);
virtual cv::gapi::GKernelPackage auxiliaryKernels() const;
--- /dev/null
+// 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) 2019 Intel Corporation
+
+
+#ifdef HAVE_PLAIDML
+
+#include "precomp.hpp"
+
+#include <ade/util/algorithm.hpp>
+#include <ade/util/range.hpp>
+#include <ade/util/zip_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 <opencv2/gapi/plaidml/plaidml.hpp>
+
+#include "compiler/gobjref.hpp"
+#include "compiler/gmodel.hpp"
+
+#include "backends/plaidml/gplaidmlbackend.hpp"
+#include "backends/plaidml/plaidml_util.hpp"
+
+#include "api/gbackend_priv.hpp" // FIXME: Make it part of Backend SDK!
+
+using GPlaidMLModel = ade::TypedGraph
+ < cv::gimpl::PlaidMLUnit
+ , cv::gimpl::Protocol
+ >;
+
+// FIXME: Same issue with Typed and ConstTyped
+using GConstGPlaidMLModel = ade::ConstTypedGraph
+ < cv::gimpl::PlaidMLUnit
+ , cv::gimpl::Protocol
+ >;
+
+namespace
+{
+ class GPlaidMLBackendImpl final: public cv::gapi::GBackend::Priv
+ {
+ virtual void unpackKernel(ade::Graph &graph,
+ const ade::NodeHandle &op_node,
+ const cv::GKernelImpl &impl) override
+ {
+ GPlaidMLModel gm(graph);
+ auto plaidml_impl = cv::util::any_cast<cv::GPlaidMLKernel>(impl.opaque);
+ gm.metadata(op_node).set(cv::gimpl::PlaidMLUnit{plaidml_impl});
+ }
+
+ virtual EPtr compile(const ade::Graph& graph,
+ const cv::GCompileArgs& args,
+ const std::vector<ade::NodeHandle>& nodes,
+ const std::vector<cv::gimpl::Data>& ins_data,
+ const std::vector<cv::gimpl::Data>& outs_data) const override
+ {
+ auto has_config = cv::gimpl::getCompileArg<cv::gapi::plaidml::config>(args);
+
+ if (!has_config)
+ {
+ cv::util::throw_error(std::runtime_error("Config not found!\n"
+ "You must pass cv::gapi::plaidml::config to the graph compile arguments"));
+ }
+
+ const auto& arg = has_config.value();
+ return EPtr{new cv::gimpl::GPlaidMLExecutable(cv::gimpl::GPlaidMLExecutable::Config{arg.dev_id, arg.trg_id},
+ graph, nodes, ins_data, outs_data)};
+ }
+ };
+}
+
+cv::gapi::GBackend cv::gapi::plaidml::backend()
+{
+ static cv::gapi::GBackend this_backend(std::make_shared<GPlaidMLBackendImpl>());
+ return this_backend;
+}
+
+void cv::gimpl::GPlaidMLExecutable::initBuffers(const std::vector<cv::gimpl::Data>& data,
+ std::vector<plaidml::exec::Binding>& bindings)
+{
+
+ // NB: This is necessary because we keep a pointer to bindings elements to buffer_map
+ // In order to them to remain valid it's required to prevant reallocation
+ bindings.reserve(data.size());
+ for (const auto& d : data)
+ {
+ GAPI_Assert(d.shape == GShape::GMAT &&
+ "Now PlaidML backend supports only cv::GMat's");
+
+ const auto& desc = cv::util::get<cv::GMatDesc>(d.meta);
+
+ auto placeholder = plaidml::edsl::Placeholder(
+ cv::util::plaidml::depth_from_ocv(desc.depth),
+ {desc.size.width, desc.size.height, desc.chan});
+
+ const auto& shape = placeholder.shape();
+ plaidml::TensorShape tshape(shape.dtype(), shape.int_dims());
+ plaidml::Buffer buffer(m_cfg.dev_id, tshape);
+
+ bindings.push_back(plaidml::exec::Binding{std::move(placeholder),
+ std::move(buffer)});
+
+ auto& tensor_map = m_res.slot<plaidml::edsl::Tensor>();
+ // FIXME Avoid Copy here !!!
+ tensor_map.emplace(d.rc, bindings.back().tensor);
+
+ auto& buffer_map = m_res.slot<plaidml::Buffer*>();
+ buffer_map.emplace(d.rc, &(bindings.back().buffer));
+ }
+}
+
+void cv::gimpl::GPlaidMLExecutable::compile(const std::vector<cv::gimpl::Data>& ins_data,
+ const std::vector<cv::gimpl::Data>& outs_data)
+{
+ initBuffers(ins_data, input_bindings_);
+ initBuffers(outs_data, output_bindings_);
+
+ ade::util::transform(outs_data, std::back_inserter(output_ids_),
+ [](const cv::gimpl::Data& d) { return d.rc; });
+
+ GConstGPlaidMLModel gcm(m_g);
+ for (const auto& nh : m_all_ops)
+ {
+ const auto& k = gcm.metadata(nh).get<PlaidMLUnit>().k;
+ GPlaidMLContext ctx;
+
+ const auto &op = m_gm.metadata(nh).get<Op>();
+ ctx.m_args.reserve(op.args.size());
+
+ using namespace std::placeholders;
+ ade::util::transform(op.args,
+ std::back_inserter(ctx.m_args),
+ std::bind(&GPlaidMLExecutable::packArg, this, _1));
+
+ for (const auto &out_it : ade::util::indexed(op.outs))
+ {
+ const auto out_port = ade::util::index(out_it);
+ const auto out_desc = ade::util::value(out_it);
+
+ auto& tensor_map = m_res.slot<plaidml::edsl::Tensor>();
+
+ // NB: Create tensor if need
+ auto& tensor = tensor_map[out_desc.id];
+ ctx.m_results[out_port] = GArg(&(tensor));
+ }
+
+ k.apply(ctx);
+ }
+
+ std::vector<plaidml::edsl::Tensor> output_tensors;
+ for (const auto& out_id : output_ids_)
+ {
+ auto& tensor_map = m_res.slot<plaidml::edsl::Tensor>();
+ // FIXME Avoid copy here !!!
+ output_tensors.emplace_back(tensor_map[out_id]);
+ }
+
+ plaidml::edsl::Program program("Program", output_tensors);
+ binder_.reset(new plaidml::exec::Binder(program));
+
+ for (const auto& binding : input_bindings_)
+ {
+ binder_->set_input(binding.tensor, binding.buffer);
+ }
+
+ for (const auto& binding : output_bindings_)
+ {
+ binder_->set_output(binding.tensor, binding.buffer);
+ }
+
+ exec_ = binder_->compile();
+}
+
+cv::gimpl::GPlaidMLExecutable::GPlaidMLExecutable(cv::gimpl::GPlaidMLExecutable::Config cfg,
+ const ade::Graph& g,
+ const std::vector<ade::NodeHandle>& nodes,
+ const std::vector<cv::gimpl::Data>& ins_data,
+ const std::vector<cv::gimpl::Data>& outs_data)
+ : m_cfg(std::move(cfg)), m_g(g), m_gm(m_g)
+{
+ auto is_op = [&](ade::NodeHandle nh) {
+ return m_gm.metadata(nh).get<NodeType>().t == NodeType::OP;
+ };
+
+ std::copy_if(nodes.begin(), nodes.end(), std::back_inserter(m_all_ops), is_op);
+
+ compile(ins_data, outs_data);
+}
+
+void cv::gimpl::GPlaidMLExecutable::run(std::vector<InObj> &&input_objs,
+ std::vector<OutObj> &&output_objs)
+{
+ for (auto& it : input_objs) bindInArg (it.first, it.second);
+
+ exec_->run();
+
+ for (auto& it : output_objs) bindOutArg(it.first, it.second);
+}
+
+void cv::gimpl::GPlaidMLExecutable::bindInArg(const RcDesc &rc, const GRunArg &arg)
+{
+ switch (rc.shape)
+ {
+ case GShape::GMAT:
+ {
+ auto& tensor_map = m_res.slot<plaidml::edsl::Tensor>();
+ auto it = tensor_map.find(rc.id);
+ GAPI_Assert(it != tensor_map.end());
+
+ switch (arg.index())
+ {
+ case GRunArg::index_of<cv::gapi::own::Mat>():
+ {
+ auto& arg_mat = util::get<cv::gapi::own::Mat>(arg);
+ binder_->input(it->second).copy_from(arg_mat.data);
+ }
+ break;
+#if !defined(GAPI_STANDALONE)
+ case GRunArg::index_of<cv::Mat>() :
+ {
+ auto& arg_mat = util::get<cv::Mat>(arg);
+ binder_->input(it->second).copy_from(arg_mat.data);
+ }
+ break;
+#endif // !defined(GAPI_STANDALONE)
+ default: util::throw_error(std::logic_error("content type of the runtime argument does not match to resource description ?"));
+ }
+ }
+ break;
+
+ default:
+ util::throw_error(std::logic_error("Unsupported GShape type"));
+ }
+}
+
+void cv::gimpl::GPlaidMLExecutable::bindOutArg(const RcDesc &rc, const GRunArgP &arg)
+{
+ switch (rc.shape)
+ {
+ case GShape::GMAT:
+ {
+ auto& tensor_map = m_res.slot<plaidml::edsl::Tensor>();
+ auto it = tensor_map.find(rc.id);
+ GAPI_Assert(it != tensor_map.end());
+
+ switch (arg.index())
+ {
+ case GRunArgP::index_of<cv::gapi::own::Mat*>():
+ {
+ auto& arg_mat = *util::get<cv::gapi::own::Mat*>(arg);
+ binder_->output(it->second).copy_into(arg_mat.data);
+ }
+ break;
+#if !defined(GAPI_STANDALONE)
+ case GRunArgP::index_of<cv::Mat*>() :
+ {
+ auto& arg_mat = *util::get<cv::Mat*>(arg);
+ binder_->output(it->second).copy_into(arg_mat.data);
+ }
+ break;
+#endif // !defined(GAPI_STANDALONE)
+ default: util::throw_error(std::logic_error("content type of the runtime argument does not match to resource description ?"));
+ }
+ }
+ break;
+
+ default:
+ util::throw_error(std::logic_error("Unsupported GShape type"));
+ }
+}
+
+cv::GArg cv::gimpl::GPlaidMLExecutable::packArg(const GArg &arg)
+{
+ 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);
+
+ const cv::gimpl::RcDesc &ref = arg.get<cv::gimpl::RcDesc>();
+ switch (ref.shape)
+ {
+ case GShape::GMAT:
+ {
+ auto& tensor_map = m_res.slot<plaidml::edsl::Tensor>();
+ auto it = tensor_map.find(ref.id);
+ GAPI_Assert(it != tensor_map.end());
+ return GArg(it->second);
+ }
+ break;
+ default:
+ util::throw_error(std::logic_error("Unsupported GShape type"));
+ break;
+ }
+}
+
+#endif // HAVE_PLAIDML
--- /dev/null
+// 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) 2019 Intel Corporation
+
+#ifdef HAVE_PLAIDML
+
+#ifndef OPENCV_GAPI_GPLAIDMLBACKEND_HPP
+#define OPENCV_GAPI_GPLAIDMLBACKEND_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/plaidml/gplaidmlkernel.hpp>
+
+#include "api/gorigin.hpp"
+#include "backends/common/gbackend.hpp"
+
+#include "compiler/gislandmodel.hpp"
+
+#include <plaidml2/exec/exec.h>
+#include <plaidml2/core/core.h>
+
+namespace cv { namespace gimpl {
+
+struct PlaidMLUnit
+{
+ static const char *name() { return "PlaidMLKernel"; }
+ GPlaidMLKernel k;
+};
+
+class GPlaidMLExecutable final: public GIslandExecutable
+{
+public:
+ struct Config
+ {
+ std::string dev_id;
+ std::string trg_id;
+ };
+
+ GPlaidMLExecutable(Config cfg,
+ const ade::Graph& graph,
+ const std::vector<ade::NodeHandle>& nodes,
+ const std::vector<cv::gimpl::Data>& ins_data,
+ const std::vector<cv::gimpl::Data>& outs_data);
+
+ virtual inline bool canReshape() const override { return false; }
+
+ virtual inline void reshape(ade::Graph&, const GCompileArgs&) override
+ {
+ util::throw_error(std::logic_error("GPlaidMLExecutable::reshape() should never be called"));
+ }
+
+ virtual void run(std::vector<InObj> &&input_objs,
+ std::vector<OutObj> &&output_objs) override;
+
+private:
+ void initBuffers(const std::vector<cv::gimpl::Data>& ins_data,
+ std::vector<plaidml::exec::Binding>& bindings);
+
+ void bindInArg (const RcDesc &rc, const GRunArg &arg);
+ void bindOutArg (const RcDesc &rc, const GRunArgP &arg);
+
+ void compile(const std::vector<cv::gimpl::Data>& ins_data,
+ const std::vector<cv::gimpl::Data>& outs_data);
+
+ // FIXME User also can pass config via compile args ?
+ void initConfig();
+
+ GArg packArg(const GArg &arg);
+
+ Config m_cfg;
+
+ const ade::Graph &m_g;
+ GModel::ConstGraph m_gm;
+
+ std::vector<ade::NodeHandle> m_all_ops;
+
+ std::vector<size_t> output_ids_;
+
+ std::unique_ptr<plaidml::exec::Binder> binder_;
+ std::shared_ptr<plaidml::exec::Executable> exec_;
+
+ std::vector<plaidml::exec::Binding> input_bindings_;
+ std::vector<plaidml::exec::Binding> output_bindings_;
+
+ using Mag = detail::magazine<plaidml::edsl::Tensor, plaidml::Buffer*>;
+ Mag m_res;
+};
+
+}}
+
+#endif // OPENCV_GAPI_GPLAIDMLBACKEND_HPP
+
+#endif // HAVE_PLAIDML
--- /dev/null
+// 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) 2019 Intel Corporation
+
+
+#ifdef HAVE_PLAIDML
+
+#include "precomp.hpp"
+
+#include <opencv2/gapi/core.hpp>
+#include <opencv2/gapi/plaidml/core.hpp>
+#include <opencv2/gapi/plaidml/gplaidmlkernel.hpp>
+
+#include <plaidml2/edsl/edsl.h>
+
+#define GAPI_PLAIDML_LOGICAL_OP(Name, API, Op) \
+GAPI_PLAIDML_KERNEL(Name, API) \
+{ \
+ static void run(const plaidml::edsl::Tensor& src1, \
+ const plaidml::edsl::Tensor& src2, \
+ plaidml::edsl::Tensor& dst) \
+ { \
+ dst = src1 Op src2; \
+ }; \
+}; \
+
+#define GAPI_PLAIDML_ARITHMETIC_OP(Name, API, Op) \
+GAPI_PLAIDML_KERNEL(Name, API) \
+{ \
+ static void run(const plaidml::edsl::Tensor& src1, \
+ const plaidml::edsl::Tensor& src2, \
+ int, /* dtype */ \
+ plaidml::edsl::Tensor& dst) \
+ { \
+ dst = src1 Op src2; \
+ }; \
+}; \
+
+GAPI_PLAIDML_LOGICAL_OP(GPlaidMLAnd, cv::gapi::core::GAnd, &);
+GAPI_PLAIDML_LOGICAL_OP(GPlaidMLXor, cv::gapi::core::GXor, ^);
+GAPI_PLAIDML_LOGICAL_OP(GPlaidMLOr , cv::gapi::core::GOr , |)
+
+GAPI_PLAIDML_ARITHMETIC_OP(GPlaidMLAdd, cv::gapi::core::GAdd, +);
+GAPI_PLAIDML_ARITHMETIC_OP(GPlaidMLSub, cv::gapi::core::GSub, -);
+
+cv::gapi::GKernelPackage cv::gapi::core::plaidml::kernels()
+{
+ static auto pkg = cv::gapi::kernels<GPlaidMLAdd, GPlaidMLSub, GPlaidMLAnd, GPlaidMLXor, GPlaidMLOr>();
+ return pkg;
+}
+
+#endif // HACE_PLAIDML
--- /dev/null
+// 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) 2019 Intel Corporation
+
+
+#ifdef HAVE_PLAIDML
+
+#ifndef OPENCV_GAPI_PLAIDML_UTIL_HPP
+#define OPENCV_GAPI_PLAIDML_UTIL_HPP
+
+#include <plaidml2/core/ffi.h>
+
+namespace cv
+{
+namespace util
+{
+namespace plaidml
+{
+
+inline plaidml_datatype depth_from_ocv(int depth)
+{
+ switch(depth)
+ {
+ case CV_8U : return PLAIDML_DATA_UINT8;
+ case CV_8S : return PLAIDML_DATA_INT8;
+ case CV_16U : return PLAIDML_DATA_UINT16;
+ case CV_16S : return PLAIDML_DATA_INT16;
+ case CV_32S : return PLAIDML_DATA_INT32;
+ case CV_32F : return PLAIDML_DATA_FLOAT32;
+ case CV_64F : return PLAIDML_DATA_FLOAT64;
+ default: util::throw_error("Unrecognized OpenCV depth");
+ }
+};
+
+}
+}
+}
+#endif // OPENCV_GAPI_PLAIDML_UTIL_HPP
+
+#endif // HAVE_PLAIDML
#include "api/gbackend_priv.hpp" // GBackend::Priv().compile()
#include "compiler/gmodel.hpp"
#include "compiler/gislandmodel.hpp"
+#include "compiler/gmodel.hpp"
+
#include "logger.hpp" // GAPI_LOG
namespace cv { namespace gimpl {
{
if (NodeKind::ISLAND == g.metadata(nh).get<NodeKind>().k)
{
+ auto nodes_to_data = [&](const ade::NodeHandle& dnh)
+ {
+ GAPI_Assert(g.metadata(dnh).get<NodeKind>().k == NodeKind::SLOT);
+ const auto& orig_data_nh = g.metadata(dnh).get<DataSlot>().original_data_node;
+ const auto& data = gm.metadata(orig_data_nh).get<Data>();
+ return data;
+ };
+
+ std::vector<cv::gimpl::Data> ins_data;
+ ade::util::transform(nh->inNodes(), std::back_inserter(ins_data), nodes_to_data);
+
+ std::vector<cv::gimpl::Data> outs_data;
+ ade::util::transform(nh->outNodes(), std::back_inserter(outs_data), nodes_to_data);
+
auto island_obj = g.metadata(nh).get<FusedIsland>().object;
auto island_ops = island_obj->contents();
});
auto island_exe = island_obj->backend().priv()
- .compile(orig_g, args, topo_sorted_list);
+ .compile(orig_g, args, topo_sorted_list, ins_data, outs_data);
GAPI_Assert(nullptr != island_exe);
+
g.metadata(nh).set(IslandExec{std::move(island_exe)});
}
}
// * Is instantiated by the last step of the Islands fusion procedure;
// * Is orchestrated by a GExecutor instance.
//
+
class GIslandExecutable
{
public:
m_ops.emplace_back(OpDesc{ std::move(input_rcs)
, std::move(output_rcs)
- , m_gim.metadata(nh).get<IslandExec>().object});
+ , m_gim.metadata(nh).get<IslandExec>().object
+ });
}
break;
--- /dev/null
+// 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) 2019 Intel Corporation
+
+
+#ifdef HAVE_PLAIDML
+
+#include "test_precomp.hpp"
+
+#include <stdexcept>
+#include <ade/util/iota_range.hpp>
+#include "logger.hpp"
+
+#include <opencv2/gapi/plaidml/core.hpp>
+#include <opencv2/gapi/plaidml/plaidml.hpp>
+
+namespace opencv_test
+{
+
+inline cv::gapi::plaidml::config getConfig()
+{
+ auto read_var_from_env = [](const char* env)
+ {
+ const char* raw = std::getenv(env);
+ if (!raw)
+ {
+ cv::util::throw_error(std::runtime_error(std::string(env) + " is't set"));
+ }
+
+ return std::string(raw);
+ };
+
+ auto dev_id = read_var_from_env("PLAIDML_DEVICE");
+ auto trg_id = read_var_from_env("PLAIDML_TARGET");
+
+ return cv::gapi::plaidml::config{std::move(dev_id),
+ std::move(trg_id)};
+}
+
+TEST(GAPI_PlaidML_Pipelines, SimpleArithmetic)
+{
+ cv::Size size(1920, 1080);
+ int type = CV_8UC1;
+
+ cv::Mat in_mat1(size, type);
+ cv::Mat in_mat2(size, type);
+
+ // NB: What about overflow ? PlaidML doesn't handle it
+ cv::randu(in_mat1, cv::Scalar::all(0), cv::Scalar::all(127));
+ cv::randu(in_mat2, cv::Scalar::all(0), cv::Scalar::all(127));
+
+ cv::Mat out_mat(size, type, cv::Scalar::all(0));
+ cv::Mat ref_mat(size, type, cv::Scalar::all(0));
+
+ ////////////////////////////// G-API //////////////////////////////////////
+ cv::GMat in1, in2;
+ auto out = in1 + in2;
+
+ cv::GComputation comp(cv::GIn(in1, in2), cv::GOut(out));
+ comp.apply(cv::gin(in_mat1, in_mat2), cv::gout(out_mat),
+ cv::compile_args(getConfig(),
+ cv::gapi::use_only{cv::gapi::core::plaidml::kernels()}));
+
+ ////////////////////////////// OpenCV /////////////////////////////////////
+ cv::add(in_mat1, in_mat2, ref_mat, cv::noArray(), type);
+
+ EXPECT_EQ(0, cv::norm(out_mat, ref_mat));
+}
+
+// FIXME PlaidML cpu backend does't support bitwise operations
+TEST(GAPI_PlaidML_Pipelines, DISABLED_ComplexArithmetic)
+{
+ cv::Size size(1920, 1080);
+ int type = CV_8UC1;
+
+ cv::Mat in_mat1(size, type);
+ cv::Mat in_mat2(size, type);
+
+ cv::randu(in_mat1, cv::Scalar::all(0), cv::Scalar::all(255));
+ cv::randu(in_mat2, cv::Scalar::all(0), cv::Scalar::all(255));
+
+ cv::Mat out_mat(size, type, cv::Scalar::all(0));
+ cv::Mat ref_mat(size, type, cv::Scalar::all(0));
+
+ ////////////////////////////// G-API //////////////////////////////////////
+ cv::GMat in1, in2;
+ auto out = in1 | (in2 ^ (in1 & (in2 + (in1 - in2))));
+
+ cv::GComputation comp(cv::GIn(in1, in2), cv::GOut(out));
+ comp.apply(cv::gin(in_mat1, in_mat2), cv::gout(out_mat),
+ cv::compile_args(getConfig(),
+ cv::gapi::use_only{cv::gapi::core::plaidml::kernels()}));
+
+ ////////////////////////////// OpenCV /////////////////////////////////////
+ cv::subtract(in_mat1, in_mat2, ref_mat, cv::noArray(), type);
+ cv::add(in_mat2, ref_mat, ref_mat, cv::noArray(), type);
+ cv::bitwise_and(in_mat1, ref_mat, ref_mat);
+ cv::bitwise_xor(in_mat2, ref_mat, ref_mat);
+ cv::bitwise_or(in_mat1, ref_mat, ref_mat);
+
+ EXPECT_EQ(0, cv::norm(out_mat, ref_mat));
+}
+
+TEST(GAPI_PlaidML_Pipelines, TwoInputOperations)
+{
+ cv::Size size(1920, 1080);
+ int type = CV_8UC1;
+
+ constexpr int kNumInputs = 4;
+ std::vector<cv::Mat> in_mat(kNumInputs, cv::Mat(size, type));
+ for (int i = 0; i < kNumInputs; ++i)
+ {
+ cv::randu(in_mat[i], cv::Scalar::all(0), cv::Scalar::all(60));
+ }
+
+ cv::Mat out_mat(size, type, cv::Scalar::all(0));
+ cv::Mat ref_mat(size, type, cv::Scalar::all(0));
+
+ ////////////////////////////// G-API //////////////////////////////////////
+ cv::GMat in[4];
+ auto out = (in[3] - in[0]) + (in[2] - in[1]);
+
+ cv::GComputation comp(cv::GIn(in[0], in[1], in[2], in[3]), cv::GOut(out));
+
+ // FIXME Doesn't work just apply(in_mat, out_mat, ...)
+ comp.apply(cv::gin(in_mat[0], in_mat[1], in_mat[2], in_mat[3]), cv::gout(out_mat),
+ cv::compile_args(getConfig(),
+ cv::gapi::use_only{cv::gapi::core::plaidml::kernels()}));
+
+ ////////////////////////////// OpenCV /////////////////////////////////////
+ cv::subtract(in_mat[3], in_mat[0], ref_mat, cv::noArray(), type);
+ cv::add(ref_mat, in_mat[2], ref_mat, cv::noArray(), type);
+ cv::subtract(ref_mat, in_mat[1], ref_mat, cv::noArray(), type);
+
+ EXPECT_EQ(0, cv::norm(out_mat, ref_mat));
+}
+
+TEST(GAPI_PlaidML_Pipelines, TwoOutputOperations)
+{
+ cv::Size size(1920, 1080);
+ int type = CV_8UC1;
+
+ constexpr int kNumInputs = 4;
+ std::vector<cv::Mat> in_mat(kNumInputs, cv::Mat(size, type));
+ for (int i = 0; i < kNumInputs; ++i)
+ {
+ cv::randu(in_mat[i], cv::Scalar::all(0), cv::Scalar::all(60));
+ }
+
+ std::vector<cv::Mat> out_mat(kNumInputs, cv::Mat(size, type, cv::Scalar::all(0)));
+ std::vector<cv::Mat> ref_mat(kNumInputs, cv::Mat(size, type, cv::Scalar::all(0)));
+
+ ////////////////////////////// G-API //////////////////////////////////////
+ cv::GMat in[4], out[2];
+ out[0] = in[0] + in[3];
+ out[1] = in[1] + in[2];
+
+ cv::GComputation comp(cv::GIn(in[0], in[1], in[2], in[3]), cv::GOut(out[0], out[1]));
+
+ // FIXME Doesn't work just apply(in_mat, out_mat, ...)
+ comp.apply(cv::gin(in_mat[0], in_mat[1], in_mat[2], in_mat[3]),
+ cv::gout(out_mat[0], out_mat[1]),
+ cv::compile_args(getConfig(),
+ cv::gapi::use_only{cv::gapi::core::plaidml::kernels()}));
+
+ ////////////////////////////// OpenCV /////////////////////////////////////
+ cv::add(in_mat[0], in_mat[3], ref_mat[0], cv::noArray(), type);
+ cv::add(in_mat[1], in_mat[2], ref_mat[1], cv::noArray(), type);
+
+ EXPECT_EQ(0, cv::norm(out_mat[0], ref_mat[0]));
+ EXPECT_EQ(0, cv::norm(out_mat[1], ref_mat[1]));
+}
+
+} // namespace opencv_test
+
+#endif // HAVE_PLAIDML
#include <ade/util/iota_range.hpp>
#include "logger.hpp"
-#include <opencv2/gapi/render/render.hpp>
+#include <opencv2/gapi/plaidml/core.hpp>
+
namespace opencv_test
{
// FIXME add overload for apply(cv::gapi::own::Mat in, cv::gapi::own::Mat& out)
EXPECT_NO_THROW(comp.apply({in_own_mat}, {out_own_mat}));
}
+
} // namespace opencv_test