/
Alx89
/
OpenCV
Обзор
Документация
Войти
/
Alx89
/
OpenCV
Код
Запросы
0
Задачи
Вики
Пакеты
0
Релизы
0
Аналитика
Безопасность
4.x
modules/gapi/src/backends/python/gpythonbackend.cpp
283 строки
9 KB
Alexander Alekhin
Merge pull request #22935 from alalek:gapi_error
19 дек 2022, 09:05
Не верифицирован
19 дек 2022, 09:05
91998d6
Код
Авторство
О чём код?
// 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) 2021 Intel Corporation #include <ade/util/zip_range.hpp> // zip_range, indexed #include "compiler/gmodel.hpp" #include <opencv2/gapi/garg.hpp> #include <opencv2/gapi/util/throw.hpp> // throw_error #include <opencv2/gapi/python/python.hpp> #include "api/gbackend_priv.hpp" #include "backends/common/gbackend.hpp" cv::gapi::python::GPythonKernel::GPythonKernel(cv::gapi::python::Impl runf, cv::gapi::python::Setup setupf) : run(runf), setup(setupf), is_stateful(setup != nullptr) { } cv::gapi::python::GPythonFunctor::GPythonFunctor(const char* id, const cv::gapi::python::GPythonFunctor::Meta& meta, const cv::gapi::python::Impl& impl, const cv::gapi::python::Setup& setup) : gapi::GFunctor(id), impl_{GPythonKernel{impl, setup}, meta} { } cv::GKernelImpl cv::gapi::python::GPythonFunctor::impl() const { return impl_; } cv::gapi::GBackend cv::gapi::python::GPythonFunctor::backend() const { return cv::gapi::python::backend(); } namespace { struct PythonUnit { static const char *name() { return "PythonUnit"; } cv::gapi::python::GPythonKernel kernel; }; using PythonModel = ade::TypedGraph < cv::gimpl::Op , PythonUnit >; using ConstPythonModel = ade::ConstTypedGraph < cv::gimpl::Op , PythonUnit >; class GPythonExecutable final: public cv::gimpl::GIslandExecutable { virtual void run(std::vector<InObj> &&, std::vector<OutObj> &&) override; virtual bool allocatesOutputs() const override { return true; } // Return an empty RMat since we will reuse the input. // There is no need to allocate and copy 4k image here. virtual cv::RMat allocate(const cv::GMatDesc&) const override { return {}; } virtual bool canReshape() const override { return true; } virtual void handleNewStream() override; virtual void reshape(ade::Graph&, const cv::GCompileArgs&) override { // Do nothing here } public: GPythonExecutable(const ade::Graph &, const std::vector<ade::NodeHandle> &); const ade::Graph& m_g; cv::gimpl::GModel::ConstGraph m_gm; cv::gapi::python::GPythonKernel m_kernel; ade::NodeHandle m_op; cv::GArg m_node_state; cv::GTypesInfo m_out_info; cv::GMetaArgs m_in_metas; cv::gimpl::Mag m_res; }; static cv::GArg packArg(cv::gimpl::Mag& m_res, const cv::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 && arg.kind != cv::detail::ArgKind::GOPAQUE && arg.kind != cv::detail::ArgKind::GFRAME); 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 cv::GShape::GMAT: return cv::GArg(m_res.slot<cv::Mat>() [ref.id]); case cv::GShape::GSCALAR: return cv::GArg(m_res.slot<cv::Scalar>()[ref.id]); // Note: .at() is intentional for GArray and GOpaque as objects MUST be already there // (and constructed by either bindIn/Out or resetInternal) case cv::GShape::GARRAY: return cv::GArg(m_res.slot<cv::detail::VectorRef>().at(ref.id)); case cv::GShape::GOPAQUE: return cv::GArg(m_res.slot<cv::detail::OpaqueRef>().at(ref.id)); case cv::GShape::GFRAME: return cv::GArg(m_res.slot<cv::MediaFrame>().at(ref.id)); default: cv::util::throw_error(std::logic_error("Unsupported GShape type")); break; } } static void writeBack(cv::GRunArg& arg, cv::GRunArgP& out) { switch (arg.index()) { case cv::GRunArg::index_of<cv::Mat>(): { auto& rmat = *cv::util::get<cv::RMat*>(out); rmat = cv::make_rmat<cv::gimpl::RMatOnMat>(cv::util::get<cv::Mat>(arg)); break; } case cv::GRunArg::index_of<cv::Scalar>(): { *cv::util::get<cv::Scalar*>(out) = cv::util::get<cv::Scalar>(arg); break; } case cv::GRunArg::index_of<cv::detail::OpaqueRef>(): { auto& oref = cv::util::get<cv::detail::OpaqueRef>(arg); cv::util::get<cv::detail::OpaqueRef>(out).mov(oref); break; } case cv::GRunArg::index_of<cv::detail::VectorRef>(): { auto& vref = cv::util::get<cv::detail::VectorRef>(arg); cv::util::get<cv::detail::VectorRef>(out).mov(vref); break; } default: GAPI_Error("Unsupported output type"); } } void GPythonExecutable::handleNewStream() { if (!m_kernel.is_stateful) return; m_node_state = m_kernel.setup(cv::gimpl::GModel::collectInputMeta(m_gm, m_op), m_gm.metadata(m_op).get<cv::gimpl::Op>().args); } void GPythonExecutable::run(std::vector<InObj> &&input_objs, std::vector<OutObj> &&output_objs) { const auto &op = m_gm.metadata(m_op).get<cv::gimpl::Op>(); for (auto& it : input_objs) cv::gimpl::magazine::bindInArg(m_res, it.first, it.second); using namespace std::placeholders; cv::GArgs inputs; ade::util::transform(op.args, std::back_inserter(inputs), std::bind(&packArg, std::ref(m_res), _1)); cv::gapi::python::GPythonContext ctx{inputs, m_in_metas, m_out_info, /*state*/{}}; // NB: For stateful kernel add state to its execution context if (m_kernel.is_stateful) { ctx.m_state = cv::optional<cv::GArg>(m_node_state); } auto outs = m_kernel.run(ctx); for (auto&& it : ade::util::zip(outs, output_objs)) { writeBack(std::get<0>(it), std::get<1>(it).second); } } class GPythonBackendImpl final: public cv::gapi::GBackend::Priv { virtual void unpackKernel(ade::Graph &graph, const ade::NodeHandle &op_node, const cv::GKernelImpl &impl) override { PythonModel gm(graph); const auto &kernel = cv::util::any_cast<cv::gapi::python::GPythonKernel>(impl.opaque); gm.metadata(op_node).set(PythonUnit{kernel}); } virtual EPtr compile(const ade::Graph &graph, const cv::GCompileArgs &, const std::vector<ade::NodeHandle> &nodes) const override { return EPtr{new GPythonExecutable(graph, nodes)}; } virtual bool controlsMerge() const override { return true; } virtual bool allowsMerge(const cv::gimpl::GIslandModel::Graph &, const ade::NodeHandle &, const ade::NodeHandle &, const ade::NodeHandle &) const override { return false; } }; GPythonExecutable::GPythonExecutable(const ade::Graph& g, const std::vector<ade::NodeHandle>& nodes) : m_g(g), m_gm(m_g) { using namespace cv::gimpl; const auto is_op = [this](const ade::NodeHandle &nh) { return m_gm.metadata(nh).get<NodeType>().t == NodeType::OP; }; auto it = std::find_if(nodes.begin(), nodes.end(), is_op); GAPI_Assert(it != nodes.end() && "No operators found for this island?!"); ConstPythonModel cag(m_g); m_op = *it; m_kernel = cag.metadata(m_op).get<PythonUnit>().kernel; // If kernel is stateful then prepare storage for its state. if (m_kernel.is_stateful) { m_node_state = cv::GArg{ }; } // Ensure this the only op in the graph if (std::any_of(it+1, nodes.end(), is_op)) { cv::util::throw_error (std::logic_error ("Internal error: Python subgraph has multiple operations")); } m_out_info.reserve(m_op->outEdges().size()); for (const auto &e : m_op->outEdges()) { const auto& out_data = m_gm.metadata(e->dstNode()).get<cv::gimpl::Data>(); m_out_info.push_back(cv::GTypeInfo{out_data.shape, out_data.kind, out_data.ctor}); } const auto& op = m_gm.metadata(m_op).get<cv::gimpl::Op>(); m_in_metas.resize(op.args.size()); GAPI_Assert(m_op->inEdges().size() > 0); for (const auto &in_eh : m_op->inEdges()) { const auto& input_port = m_gm.metadata(in_eh).get<Input>().port; const auto& input_nh = in_eh->srcNode(); const auto& input_meta = m_gm.metadata(input_nh).get<Data>().meta; m_in_metas.at(input_port) = input_meta; } } } // anonymous namespace cv::gapi::GBackend cv::gapi::python::backend() { static cv::gapi::GBackend this_backend(std::make_shared<GPythonBackendImpl>()); return this_backend; }