1 // This file is part of OpenCV project.
2 // It is subject to the license terms in the LICENSE file found in the top-level directory
3 // of this distribution and at http://opencv.org/license.html.
5 // Copyright (C) 2018-2019 Intel Corporation
9 #include <algorithm> // remove_if
10 #include <cctype> // isspace (non-locale version)
11 #include <ade/util/algorithm.hpp>
13 #include "logger.hpp" // GAPI_LOG
15 #include "opencv2/gapi/gcomputation.hpp"
16 #include "opencv2/gapi/gkernel.hpp"
18 #include "api/gcomputation_priv.hpp"
19 #include "api/gcall_priv.hpp"
20 #include "api/gnode_priv.hpp"
22 #include "compiler/gmodelbuilder.hpp"
23 #include "compiler/gcompiler.hpp"
25 // cv::GComputation private implementation /////////////////////////////////////
28 // cv::GComputation public implementation //////////////////////////////////////
29 cv::GComputation::GComputation(const Generator& gen)
30 : m_priv(gen().m_priv)
34 cv::GComputation::GComputation(GMat in, GMat out)
35 : cv::GComputation(cv::GIn(in), cv::GOut(out))
40 cv::GComputation::GComputation(GMat in, GScalar out)
41 : cv::GComputation(cv::GIn(in), cv::GOut(out))
45 cv::GComputation::GComputation(GMat in1, GMat in2, GMat out)
46 : cv::GComputation(cv::GIn(in1, in2), cv::GOut(out))
50 cv::GComputation::GComputation(GMat in1, GMat in2, GScalar out)
51 : cv::GComputation(cv::GIn(in1, in2), cv::GOut(out))
55 cv::GComputation::GComputation(const std::vector<GMat> &ins,
56 const std::vector<GMat> &outs)
59 const auto wrap = [](cv::GMat m) { return GProtoArg(m); };
60 ade::util::transform(ins, std::back_inserter(m_priv->m_ins), wrap);
61 ade::util::transform(outs, std::back_inserter(m_priv->m_outs), wrap);
64 cv::GComputation::GComputation(cv::GProtoInputArgs &&ins,
65 cv::GProtoOutputArgs &&outs)
68 m_priv->m_ins = std::move(ins.m_args);
69 m_priv->m_outs = std::move(outs.m_args);
72 cv::GCompiled cv::GComputation::compile(GMetaArgs &&metas, GCompileArgs &&args)
74 // FIXME: Cache gcompiled per parameters here?
75 cv::gimpl::GCompiler comp(*this, std::move(metas), std::move(args));
76 return comp.compile();
79 // FIXME: Introduce similar query/test method for GMetaArgs as a building block
80 // for functions like this?
81 static bool formats_are_same(const cv::GMetaArgs& metas1, const cv::GMetaArgs& metas2)
83 return std::equal(metas1.cbegin(), metas1.cend(), metas2.cbegin(),
84 [](const cv::GMetaArg& meta1, const cv::GMetaArg& meta2) {
85 if (meta1.index() == meta2.index() && meta1.index() == cv::GMetaArg::index_of<cv::GMatDesc>())
87 const auto& desc1 = cv::util::get<cv::GMatDesc>(meta1);
88 const auto& desc2 = cv::util::get<cv::GMatDesc>(meta2);
90 // comparison by size is omitted
91 return (desc1.chan == desc2.chan &&
92 desc1.depth == desc2.depth);
96 return meta1 == meta2;
101 void cv::GComputation::apply(GRunArgs &&ins, GRunArgsP &&outs, GCompileArgs &&args)
103 const auto in_metas = descr_of(ins);
104 // FIXME Graph should be recompiled when GCompileArgs have changed
105 if (m_priv->m_lastMetas != in_metas)
107 if (m_priv->m_lastCompiled &&
108 m_priv->m_lastCompiled.canReshape() &&
109 formats_are_same(m_priv->m_lastMetas, in_metas))
111 m_priv->m_lastCompiled.reshape(in_metas, args);
115 // FIXME: Had to construct temporary object as compile() takes && (r-value)
116 m_priv->m_lastCompiled = compile(GMetaArgs(in_metas), std::move(args));
118 m_priv->m_lastMetas = in_metas;
120 m_priv->m_lastCompiled(std::move(ins), std::move(outs));
123 void cv::GComputation::apply(const std::vector<cv::gapi::own::Mat> &ins,
124 const std::vector<cv::gapi::own::Mat> &outs,
131 for (const cv::gapi::own::Mat &m : ins) { call_ins.emplace_back(m); }
132 for ( cv::gapi::own::Mat &m : tmp) { call_outs.emplace_back(&m); }
134 apply(std::move(call_ins), std::move(call_outs), std::move(args));
137 #if !defined(GAPI_STANDALONE)
138 void cv::GComputation::apply(cv::Mat in, cv::Mat &out, GCompileArgs &&args)
140 apply(cv::gin(in), cv::gout(out), std::move(args));
141 // FIXME: The following doesn't work!
142 // Operation result is not replicated into user's object
143 // apply({GRunArg(in)}, {GRunArg(out)});
146 void cv::GComputation::apply(cv::Mat in, cv::Scalar &out, GCompileArgs &&args)
148 apply(cv::gin(in), cv::gout(out), std::move(args));
151 void cv::GComputation::apply(cv::Mat in1, cv::Mat in2, cv::Mat &out, GCompileArgs &&args)
153 apply(cv::gin(in1, in2), cv::gout(out), std::move(args));
156 void cv::GComputation::apply(cv::Mat in1, cv::Mat in2, cv::Scalar &out, GCompileArgs &&args)
158 apply(cv::gin(in1, in2), cv::gout(out), std::move(args));
161 void cv::GComputation::apply(const std::vector<cv::Mat> &ins,
162 const std::vector<cv::Mat> &outs,
168 // Make a temporary copy of vector outs - cv::Mats are copies anyway
170 for (const cv::Mat &m : ins) { call_ins.emplace_back(m); }
171 for ( cv::Mat &m : tmp) { call_outs.emplace_back(&m); }
173 apply(std::move(call_ins), std::move(call_outs), std::move(args));
175 #endif // !defined(GAPI_STANDALONE)
177 cv::GComputation::Priv& cv::GComputation::priv()
182 const cv::GComputation::Priv& cv::GComputation::priv() const
187 // Islands /////////////////////////////////////////////////////////////////////
189 void cv::gapi::island(const std::string &name,
190 GProtoInputArgs &&ins,
191 GProtoOutputArgs &&outs)
194 // Island must have a printable name.
195 // Forbid names which contain only spaces.
196 GAPI_Assert(!name.empty());
197 const auto first_printable_it = std::find_if_not(name.begin(), name.end(), isspace);
198 const bool likely_printable = first_printable_it != name.end();
199 GAPI_Assert(likely_printable);
201 // Even if the name contains spaces, keep it unmodified as user will
202 // then use this string to assign affinity, etc.
204 // First, set island tags on all operations from `ins` to `outs`
205 auto island = cv::gimpl::unrollExpr(ins.m_args, outs.m_args);
206 if (island.all_ops.empty())
208 util::throw_error(std::logic_error("Operation range is empty"));
210 for (auto &op_expr_node : island.all_ops)
212 auto &op_expr_node_p = op_expr_node.priv();
214 GAPI_Assert(op_expr_node.shape() == GNode::NodeShape::CALL);
215 const GCall& call = op_expr_node.call();
216 const GCall::Priv& call_p = call.priv();
218 if (!op_expr_node_p.m_island.empty())
220 util::throw_error(std::logic_error
221 ( "Operation " + call_p.m_k.name
222 + " is already assigned to island \""
223 + op_expr_node_p.m_island + "\""));
227 op_expr_node_p.m_island = name;
229 "Assigned " << call_p.m_k.name << "_" << &call_p <<
230 " to island \"" << name << "\"");
234 // Note - this function only sets islands to all operations in
235 // expression tree, it is just a first step.
236 // The second step is assigning intermediate data objects to Islands,
237 // see passes::initIslands for details.