mirror of https://github.com/opencv/opencv.git
parent
358d69956a
commit
e06efd5329
6 changed files with 1632 additions and 1 deletions
@ -0,0 +1,582 @@ |
||||
// 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
|
||||
|
||||
#include <unordered_set> |
||||
|
||||
#include "pattern_matching.hpp" |
||||
|
||||
namespace { |
||||
using Graph = cv::gimpl::GModel::Graph; |
||||
using Metadata = typename Graph::CMetadataT; |
||||
using VisitedMatchings = std::list<std::pair<ade::NodeHandle, ade::NodeHandle>>; |
||||
|
||||
using LabeledNodes = std::unordered_map |
||||
< // reader node
|
||||
ade::NodeHandle |
||||
// if the reader node above is:
|
||||
// - DATA node: then vector is 1-element vector containing port number of
|
||||
// the input edge
|
||||
// - OP node: then vector is ports' vector of current connections between
|
||||
// this node and an parent active DATA node
|
||||
, std::vector<std::size_t> |
||||
, ade::HandleHasher<ade::Node> |
||||
>; |
||||
|
||||
using MultipleMatchings = std::unordered_map |
||||
// pattern OP node
|
||||
< ade::NodeHandle |
||||
// nodes in the test graph which match to the pattern OP node above
|
||||
, std::vector<ade::NodeHandle> |
||||
, ade::HandleHasher<ade::Node> |
||||
>; |
||||
|
||||
// Returns true if two DATA nodes are semantically and structurally identical:
|
||||
// - both nodes have the same GShape
|
||||
// - both nodes are produced by the same port numbers
|
||||
// - both nodes have the same number of output edges
|
||||
// (output edges' ports are not checked here)
|
||||
//
|
||||
// @param first - first node to compare
|
||||
// @param firstPorts - a single element vector with first DATA node's producer output port
|
||||
// @param firstMeta - metadata of first
|
||||
// @param second - second node to compare
|
||||
// @param secondPorts - a single element vector with second DATA node's producer output port
|
||||
// @param secondMeta - metadata of second
|
||||
bool compareDataNodes(const ade::NodeHandle& first, const std::vector<std::size_t>& firstPorts, |
||||
const Metadata& firstMeta, |
||||
const ade::NodeHandle& second, const std::vector<std::size_t>& secondPorts, |
||||
const Metadata& secondMeta) { |
||||
if (secondMeta.get<cv::gimpl::NodeType>().t != cv::gimpl::NodeType::DATA) { |
||||
throw std::logic_error("NodeType of passed node as second argument" |
||||
"shall be NodeType::DATA!"); |
||||
} |
||||
|
||||
if (firstMeta.get<cv::gimpl::Data>().shape != |
||||
secondMeta.get<cv::gimpl::Data>().shape) { |
||||
return false; |
||||
} |
||||
|
||||
if (*firstPorts.begin() != *secondPorts.begin()) { |
||||
return false; |
||||
} |
||||
|
||||
const auto& firstOutputEdges = first->outEdges(); |
||||
const auto& secondOutputEdges = second->outEdges(); |
||||
|
||||
if (firstOutputEdges.size() != secondOutputEdges.size()) { |
||||
return false; |
||||
} |
||||
|
||||
// FIXME: Because of new changes which introduce existence of unused DATA nodes
|
||||
// check that first and second nodes have the same type of DATA::Storage.
|
||||
|
||||
return true; |
||||
}; |
||||
|
||||
// Returns true if two OP nodes semantically and structurally identical:
|
||||
// - both nodes have the same kernel name
|
||||
// - both nodes are produced by the same port numbers
|
||||
// - if any of the nodes are in the array with visited matchings, then:
|
||||
// first node is equal to found matching first argument and
|
||||
// second node is equal to found matching second argument
|
||||
//
|
||||
// @param first - first node to compare
|
||||
// @param firstPorts - ports' vector of current connections between first node and an parent active
|
||||
// DATA node
|
||||
// @param firstMeta - metadata of first
|
||||
// @param second - second node to compare
|
||||
// @param secondPorts - ports' vector of current connections between second node and an parent
|
||||
// active DATA node
|
||||
// @param secondMeta - metadata of second
|
||||
// @param [out] isAlreadyVisited - set to true if first and second nodes have been already visited
|
||||
bool compareOpNodes(const VisitedMatchings& matchedVisitedNodes, |
||||
const ade::NodeHandle& first, std::vector<std::size_t> firstPorts, |
||||
const Metadata& firstMeta, |
||||
const ade::NodeHandle& second, std::vector<std::size_t> secondPorts, |
||||
const Metadata& secondMeta, |
||||
bool& isAlreadyVisited) { |
||||
if (secondMeta.get<cv::gimpl::NodeType>().t != cv::gimpl::NodeType::OP) { |
||||
throw std::logic_error("NodeType of passed node as second argument shall be NodeType::OP!"); |
||||
} |
||||
|
||||
// Assuming that if kernels names are the same then
|
||||
// output DATA nodes counts from kernels are the same.
|
||||
// Assuming that if kernels names are the same then
|
||||
// input DATA nodes counts to kernels are the same.
|
||||
if (firstMeta.get<cv::gimpl::Op>().k.name != secondMeta.get<cv::gimpl::Op>().k.name) { |
||||
return false; |
||||
} |
||||
|
||||
std::sort(firstPorts.begin(), firstPorts.end()); |
||||
std::sort(secondPorts.begin(), secondPorts.end()); |
||||
if (firstPorts != secondPorts) { |
||||
return false; |
||||
} |
||||
|
||||
// Shall work, but it is good to test on the cases where multiple start pattern OP nodes
|
||||
// maps to the test's one.
|
||||
auto foundIt = std::find_if(matchedVisitedNodes.begin(), matchedVisitedNodes.end(), |
||||
[&first, &second](const std::pair<ade::NodeHandle, |
||||
ade::NodeHandle>& match) |
||||
{return first == match.first || second == match.second; }); |
||||
if (foundIt != matchedVisitedNodes.end()) { |
||||
if (first != foundIt->first || second != foundIt->second) { |
||||
return false; |
||||
} |
||||
|
||||
isAlreadyVisited = true; |
||||
} |
||||
|
||||
return true; |
||||
}; |
||||
|
||||
// Retrieves and return sample from the cartesian product of candidates sets
|
||||
VisitedMatchings sampleFromProduct(std::size_t sampleIdx, // index of the sample in the product
|
||||
const MultipleMatchings& candidatesSets) // map of nodes to sets
|
||||
// of candidates
|
||||
{ |
||||
VisitedMatchings matchingsSample; |
||||
|
||||
std::size_t quo = sampleIdx; |
||||
for (const auto& setForNode : candidatesSets) { |
||||
// TODO: order is not determined: for ex., for last node.
|
||||
// May be use ordered set and map to ensure order?
|
||||
auto size = setForNode.second.size(); |
||||
|
||||
// The below code block decodes sampleIdx into a particular sample from cartesian product
|
||||
// of candidates sets.
|
||||
std::size_t index = quo % size; |
||||
quo = quo / size; |
||||
const auto& candidate = setForNode.second[index]; |
||||
matchingsSample.push_back({ setForNode.first, candidate }); |
||||
} |
||||
|
||||
return matchingsSample; |
||||
} |
||||
|
||||
// Depending on type of the node retrieve port number (IN/OUT) of the edge entering this node.
|
||||
std::size_t labelOf (const ade::NodeHandle& node, // reader node
|
||||
const ade::EdgeHandle& edge, // edge entering the reader node
|
||||
const Graph& graph) // graph containing node and edge
|
||||
{ |
||||
|
||||
if (graph.metadata(node).get<cv::gimpl::NodeType>().t == cv::gimpl::NodeType::OP) { |
||||
return graph.metadata(edge).get<cv::gimpl::Input>().port; |
||||
} |
||||
else { |
||||
return graph.metadata(edge).get<cv::gimpl::Output>().port; |
||||
} |
||||
}; |
||||
|
||||
inline bool IS_STARTPOINT(const ade::NodeHandle& nh){ |
||||
return nh->inEdges().empty(); |
||||
} |
||||
|
||||
inline bool IS_ENDPOINT(const ade::NodeHandle& nh){ |
||||
// FIXME: Because of new changes which introduce existence of unused DATA nodes
|
||||
// Try to rely on the nh Data::Storage::OUTPUT
|
||||
return nh->outEdges().empty(); |
||||
} |
||||
} |
||||
|
||||
// Routine relies on the logic that 1 DATA node may have only 1 input edge.
|
||||
cv::gimpl::SubgraphMatch |
||||
cv::gimpl::findMatches(const cv::gimpl::GModel::Graph& patternGraph, |
||||
const cv::gimpl::GModel::Graph& testGraph) { |
||||
|
||||
//TODO: Possibly, we may add N^2 check whether this graph may match or not at all.
|
||||
// Check that all pattern OP nodes exist in computational graph.
|
||||
|
||||
//---------------------------------------------------------------
|
||||
// Identify operations which start and end our pattern
|
||||
SubgraphMatch::S patternStartOpNodes, patternEndOpNodes; |
||||
|
||||
const auto& patternInputDataNodes = patternGraph.metadata().get<cv::gimpl::Protocol>().in_nhs; |
||||
const auto& patternOutputDataNodes = patternGraph.metadata().get<cv::gimpl::Protocol>().out_nhs; |
||||
|
||||
for (const auto& node : patternInputDataNodes) { |
||||
auto opNodes = node->outNodes(); |
||||
patternStartOpNodes.insert(opNodes.begin(), opNodes.end()); |
||||
} |
||||
|
||||
for (const auto& node : patternOutputDataNodes) { |
||||
auto opNodes = node->inNodes(); |
||||
// May be switched to patternEndOpNodes.insert(*opNodes.begin());
|
||||
patternEndOpNodes.insert(opNodes.begin(), opNodes.end()); |
||||
} |
||||
|
||||
std::unordered_map<ade::NodeHandle, // pattern OP node
|
||||
std::vector<ade::NodeHandle>, // nodes in the test graph which match
|
||||
// to the pattern OP node
|
||||
ade::HandleHasher<ade::Node>> allMatchingsForStartOpNodes; |
||||
|
||||
//Filling of allMatchingsForStartOpNodes
|
||||
std::size_t possibleStartPointsCount = 1; |
||||
|
||||
// For every starting OP node of pattern identify matching candidates(there may be many)
|
||||
// in test graph.
|
||||
auto testOpNodes = ade::util::filter(testGraph.nodes(), |
||||
[&](const ade::NodeHandle& node) { |
||||
return testGraph.metadata(node). |
||||
get<cv::gimpl::NodeType>().t |
||||
== cv::gimpl::NodeType::OP; |
||||
}); |
||||
for (const auto& patternStartOpNode : patternStartOpNodes) { |
||||
const auto& patternOpMeta = patternGraph.metadata(patternStartOpNode); |
||||
|
||||
auto& possibleMatchings = allMatchingsForStartOpNodes[patternStartOpNode]; |
||||
std::copy_if(testOpNodes.begin(), testOpNodes.end(), std::back_inserter(possibleMatchings), |
||||
[&](const ade::NodeHandle& testOpNode) { |
||||
const auto& testOpMeta = testGraph.metadata(testOpNode); |
||||
|
||||
bool stub = false; |
||||
return compareOpNodes({ }, |
||||
patternStartOpNode, { }, patternOpMeta, |
||||
testOpNode, { }, testOpMeta, |
||||
stub); |
||||
}); |
||||
|
||||
if (possibleMatchings.size() == 0) { |
||||
// Pattern graph is not matched
|
||||
return SubgraphMatch { }; |
||||
} |
||||
|
||||
possibleStartPointsCount *= possibleMatchings.size(); |
||||
} |
||||
|
||||
SubgraphMatch::M subgraphStartOps; |
||||
SubgraphMatch::M subgraphEndOps; |
||||
// FIXME: consider moving to S
|
||||
std::list<ade::NodeHandle> subgraphInternals; |
||||
|
||||
|
||||
// Structural matching first, semantic matching second.
|
||||
|
||||
// 'patternFound' means pattern is matched.
|
||||
bool patternFound = false; |
||||
std::size_t i = 0; |
||||
while (!patternFound && (i < possibleStartPointsCount)) { |
||||
subgraphStartOps.clear(); |
||||
subgraphEndOps.clear(); |
||||
subgraphInternals.clear(); |
||||
|
||||
// List of the pairs representing matchings of pattern node to the test node.
|
||||
VisitedMatchings matchedVisitedNodes; |
||||
|
||||
// Cartesian product of candidate sets for start OP nodes gives set of samples
|
||||
// as possible matchings for start OP nodes.
|
||||
// Let allMatchingsForStartOpNodes looks like: x1 : [ y1 ]
|
||||
// x2 : [ y2, y3 ]
|
||||
// Cartesian product of two these candidates sets (for x1 and x2 pattern nodes
|
||||
// correspondingly) produces two samples of matchings for x1, x2:
|
||||
// [ (x1, y1), (x2, y2) ]
|
||||
// [ (x1, y1), (x2, y3) ]
|
||||
//
|
||||
|
||||
// Here we fill matchedVisitedNodes list with the next sample from the cartesian product
|
||||
// of candidates sets.
|
||||
// i is traversing full cartesian product of candidates sets.
|
||||
matchedVisitedNodes = sampleFromProduct(i, allMatchingsForStartOpNodes); |
||||
|
||||
bool stop = false; |
||||
|
||||
// matchIt is an iterator to a pair of pattern ade::NodeHandle to test's ade::nodeHandle.
|
||||
auto matchIt = matchedVisitedNodes.begin(); |
||||
std::size_t size = matchedVisitedNodes.size(); |
||||
|
||||
while (!stop) { |
||||
// The following loop traverses through the current level of matchings.
|
||||
// Every iteration we consider only one certain pair of matched nodes.
|
||||
for (std::size_t index = 0u; index < size && !stop; ++index, ++matchIt) { |
||||
|
||||
// Check if a given matchIt->first node is an pattern-ending OP node.
|
||||
// If it is just remember it in a special map.
|
||||
bool cond1 = std::find(patternEndOpNodes.begin(), |
||||
patternEndOpNodes.end(), |
||||
matchIt->first) |
||||
!= patternEndOpNodes.end(); |
||||
if (cond1) { |
||||
subgraphEndOps[matchIt->first] = matchIt->second; |
||||
} |
||||
|
||||
// Check if a given matchIt->first node is an pattern-starting OP node.
|
||||
// If it is just remember it in a special map.
|
||||
bool cond2 = std::find(patternStartOpNodes.begin(), |
||||
patternStartOpNodes.end(), |
||||
matchIt->first) |
||||
!= patternStartOpNodes.end(); |
||||
if (cond2) { |
||||
subgraphStartOps[matchIt->first] = matchIt->second; |
||||
} |
||||
|
||||
// If neither of conditions are true mark the test node as an internal one.
|
||||
if (!cond1 && !cond2) { |
||||
subgraphInternals.push_back(matchIt->second); |
||||
} |
||||
|
||||
//-------------------------------------------------------------------------------
|
||||
// Given the current pattern/test matching of nodes, traverse their descendants.
|
||||
// For every descendant store the port of the edge connecting to it.
|
||||
// NOTE: the nature of port number may vary: it may be either IN for OP nodes
|
||||
// or OUT for DATA ones
|
||||
LabeledNodes patternOutputNodesLabeled; |
||||
LabeledNodes testOutputNodesLabeled; |
||||
|
||||
auto patternOutputEdges = matchIt->first->outEdges(); |
||||
auto testOutputEdges = matchIt->second->outEdges(); |
||||
|
||||
for (const auto& patternOutputEdge : patternOutputEdges) { |
||||
const auto& dstNh = patternOutputEdge->dstNode(); |
||||
if (!IS_ENDPOINT(dstNh)) { |
||||
//Assuming that there is no case for the op node without output data nodes.
|
||||
patternOutputNodesLabeled[dstNh]. |
||||
push_back(labelOf(dstNh, patternOutputEdge, patternGraph)); |
||||
} |
||||
} |
||||
|
||||
for (const auto& testOutputEdge : testOutputEdges) { |
||||
const auto& dstNh = testOutputEdge->dstNode(); |
||||
testOutputNodesLabeled[dstNh]. |
||||
push_back(labelOf(dstNh, testOutputEdge, testGraph)); |
||||
} |
||||
|
||||
//---------------------------------------------------------------------------------
|
||||
// Traverse through labeled descendants of pattern node and for every descedant
|
||||
// find a matching in labeled descendants of corresponding test node
|
||||
for (const auto& patternNode : patternOutputNodesLabeled) { |
||||
bool isAlreadyVisited = false; |
||||
const auto& patternNodeMeta = patternGraph.metadata(patternNode.first); |
||||
|
||||
auto testIt = std::find_if(testOutputNodesLabeled.begin(), |
||||
testOutputNodesLabeled.end(), |
||||
[&](const std::pair<const ade::NodeHandle, |
||||
std::vector<std::size_t>>& testNode) { |
||||
const auto& testNodeMeta = testGraph.metadata(testNode.first); |
||||
|
||||
auto patternNodeType = patternNodeMeta.get<cv::gimpl::NodeType>().t; |
||||
|
||||
switch(patternNodeType) { |
||||
case cv::gimpl::NodeType::DATA: |
||||
return compareDataNodes(patternNode.first, patternNode.second, |
||||
patternNodeMeta, |
||||
testNode.first, testNode.second, |
||||
testNodeMeta); |
||||
case cv::gimpl::NodeType::OP: |
||||
return compareOpNodes(matchedVisitedNodes, |
||||
patternNode.first, patternNode.second, |
||||
patternNodeMeta, |
||||
testNode.first, testNode.second, |
||||
testNodeMeta, |
||||
isAlreadyVisited); |
||||
default: |
||||
GAPI_Assert(false && "Unsupported Node type!"); |
||||
} |
||||
|
||||
return false; |
||||
}); |
||||
|
||||
if (testIt == testOutputNodesLabeled.end()) { |
||||
stop = true; |
||||
break; |
||||
} |
||||
|
||||
// Update matchedVisitedNodes list with found pair of nodes if the pair
|
||||
// has not been visited before.
|
||||
if (!isAlreadyVisited) { |
||||
matchedVisitedNodes.push_back({ patternNode.first, testIt->first }); |
||||
} |
||||
} // Loop traversed patternOutputNodesLabeled
|
||||
} // Loop traversed matchedVisitedNodes
|
||||
|
||||
// Suppose, pattern and test graphs' structures without input DATA nodes look like:
|
||||
// Pattern graph Test graph
|
||||
// op1 op2 t_op1 t_op2
|
||||
// +-----+ +-----+ +-----+ +-----+
|
||||
// v v v v v v v v
|
||||
// d1 d2 d3 d4 t_d1 t_d2 t_d3 t_d4
|
||||
// v v v v v v v v
|
||||
// ... ... ... ... ... ... ... ...
|
||||
|
||||
// matchedVisitedNodes content before previous loop execution:
|
||||
// op1 <--> t_op1, op2 <--> t_op2
|
||||
// matchedVisitedNodes content after previous loop execution (extended with the next
|
||||
// level of matchings):
|
||||
// op1 <--> t_op1, op2 <--> t_op2 | d1 <--> t_d1, d2 <--> t_d2, d3 <--> t_d3, d4 <--> t_d4
|
||||
// ^
|
||||
// |
|
||||
// matchIt
|
||||
//
|
||||
// matchIt iterator points to the first matching in next level if the next level exists.
|
||||
// If there is no next level, matchIt == matchedVisitedNodes.end() and all pattern
|
||||
// levels (except ones for IN/OUT data nodes) have been already processed, so,
|
||||
// pattern subgraph is found.
|
||||
|
||||
if (!stop) { |
||||
// Check if pattetn subgraph is found
|
||||
if (matchIt == matchedVisitedNodes.end()) { |
||||
// Found
|
||||
stop = true; |
||||
patternFound = true; |
||||
} |
||||
|
||||
// Update 'size' with the size of the new level of matchings
|
||||
size = static_cast<std::size_t>(std::distance(matchIt, matchedVisitedNodes.end())); |
||||
} |
||||
} |
||||
|
||||
if (!patternFound){ |
||||
// Pattern subgraph is not matched.
|
||||
// Switch to the next combination of starting points
|
||||
++i; |
||||
continue; |
||||
} |
||||
|
||||
SubgraphMatch::M inputApiMatch; |
||||
SubgraphMatch::M outputApiMatch; |
||||
|
||||
// Traversing current result for starting OPs
|
||||
for (auto it = subgraphStartOps.begin(); |
||||
it != subgraphStartOps.end() && patternFound; ++it) { |
||||
const auto& match = *it; |
||||
auto patternInputEdges = match.first->inEdges(); |
||||
auto testInputEdges = match.second->inEdges(); |
||||
|
||||
SubgraphMatch::S patternUniqInNodes(match.first->inNodes().begin(), |
||||
match.first->inNodes().end()); |
||||
SubgraphMatch::S testUniqInNodes(match.second->inNodes().begin(), |
||||
match.second->inNodes().end()); |
||||
|
||||
if (patternUniqInNodes.size() < testUniqInNodes.size()) { |
||||
inputApiMatch.clear(); |
||||
patternFound = false; |
||||
break; |
||||
} |
||||
// Else, patternInNodes.size() > testInNodes.size() is considered as valid case.
|
||||
|
||||
// Match pattern input DATA nodes with boundary matched test DATA nodes.
|
||||
for (const auto& patternInEdge : patternInputEdges) { |
||||
|
||||
// Not all start OP nodes are located in the beginning of the pattern graph
|
||||
// Start OP may have one input DATA node as an Protocol IN node and other
|
||||
// input DATA nodes produced from another operations
|
||||
if (!IS_STARTPOINT(patternInEdge->srcNode())) { |
||||
continue; |
||||
} |
||||
|
||||
auto patternInputPort = |
||||
patternGraph.metadata(patternInEdge).get<cv::gimpl::Input>().port; |
||||
|
||||
auto matchedIt = std::find_if(testInputEdges.begin(), testInputEdges.end(), |
||||
[&](const ade::EdgeHandle& testInEdge) -> bool { |
||||
auto testInputPort = |
||||
testGraph.metadata(testInEdge).get<cv::gimpl::Input>().port; |
||||
|
||||
if (patternInputPort != testInputPort) { |
||||
return false; |
||||
} |
||||
|
||||
auto foundIt = inputApiMatch.find(patternInEdge->srcNode()); |
||||
if (foundIt != inputApiMatch.end()) { |
||||
if (testInEdge->srcNode() != foundIt->second) { |
||||
return false; |
||||
} |
||||
return true; |
||||
} |
||||
|
||||
// Update inputApiMatch map only if the pair of nodes isn't in the map already
|
||||
inputApiMatch[patternInEdge->srcNode()] = testInEdge->srcNode(); |
||||
return true; |
||||
}); |
||||
|
||||
if (matchedIt == testInputEdges.end()) { |
||||
inputApiMatch.clear(); |
||||
patternFound = false; |
||||
break; |
||||
} |
||||
} // Loop traversed patternInputEdges
|
||||
} // Loop traversed sugraphStartOps
|
||||
|
||||
if (!patternFound) { |
||||
// Pattern IN data nodes can not be matched.
|
||||
// Switch to the next combination of starting points
|
||||
++i; |
||||
continue; |
||||
} |
||||
|
||||
// Create vector with the correctly ordered IN data nodes in the test subgraph
|
||||
std::vector<ade::NodeHandle> inputTestDataNodes; |
||||
for (const auto& patternInNode : patternInputDataNodes) { |
||||
inputTestDataNodes.push_back(inputApiMatch[patternInNode]); |
||||
} |
||||
|
||||
// Traversing current result for ending OPs
|
||||
// There is an assumption that if the pattern subgraph is matched, then
|
||||
// OUT data nodes shall be definitely matched
|
||||
for (const auto& match : subgraphEndOps) { |
||||
auto patternOutputEdges = match.first->outEdges(); |
||||
auto testOutputEdges = match.second->outEdges(); |
||||
|
||||
GAPI_Assert(patternOutputEdges.size() == testOutputEdges.size() |
||||
&& |
||||
"Ending OP nodes are matched, so OPs' outputs count shall be the same!"); |
||||
|
||||
// Match pattern output DATA nodes with boundary matched test DATA nodes.
|
||||
for (const auto& patternOutEdge : patternOutputEdges) { |
||||
|
||||
// Not all end OP nodes are located in the ending of the pattern graph
|
||||
// End OP node may have one output DATA node as an Protocol OUT node and other
|
||||
// output DATA nodes as input for another operations
|
||||
if (!IS_ENDPOINT(patternOutEdge->dstNode())) { |
||||
continue; |
||||
} |
||||
|
||||
auto patternOutputPort = |
||||
patternGraph.metadata(patternOutEdge).get<cv::gimpl::Output>().port; |
||||
|
||||
auto matchedIt = std::find_if(testOutputEdges.begin(), testOutputEdges.end(), |
||||
[&](const ade::EdgeHandle& testOutEdge) -> bool { |
||||
auto testOutputPort = |
||||
testGraph.metadata(testOutEdge).get<cv::gimpl::Output>().port; |
||||
|
||||
if (patternOutputPort != testOutputPort) { |
||||
return false; |
||||
} |
||||
|
||||
outputApiMatch[patternOutEdge->dstNode()] = testOutEdge->dstNode(); |
||||
return true; |
||||
}); |
||||
|
||||
GAPI_Assert(matchedIt != testOutputEdges.end() |
||||
&& |
||||
"There shall be a match for every OUT data node from ending OP node," |
||||
"if ending OP node matches"); |
||||
} |
||||
|
||||
} |
||||
|
||||
// Create vector with the correctly ordered OUT data nodes in the test subgraph
|
||||
std::vector<ade::NodeHandle> outputTestDataNodes; |
||||
for (const auto& patternOutNode : patternOutputDataNodes) { |
||||
outputTestDataNodes.push_back(outputApiMatch[patternOutNode]); |
||||
} |
||||
|
||||
SubgraphMatch subgraph; |
||||
|
||||
subgraph.inputDataNodes = std::move(inputApiMatch); |
||||
subgraph.startOpNodes = std::move(subgraphStartOps); |
||||
subgraph.internalLayers = std::move(subgraphInternals); |
||||
subgraph.finishOpNodes = std::move(subgraphEndOps); |
||||
subgraph.outputDataNodes = std::move(outputApiMatch); |
||||
|
||||
subgraph.inputTestDataNodes = std::move(inputTestDataNodes); |
||||
subgraph.outputTestDataNodes = std::move(outputTestDataNodes); |
||||
|
||||
return subgraph; |
||||
|
||||
} |
||||
|
||||
return SubgraphMatch { }; |
||||
} |
@ -0,0 +1,98 @@ |
||||
// 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_PATTERN_MATCHING_HPP |
||||
#define OPENCV_GAPI_PATTERN_MATCHING_HPP |
||||
|
||||
#include <unordered_map> |
||||
#include <unordered_set> |
||||
#include <vector> |
||||
#include <list> |
||||
|
||||
#include "compiler/gmodel.hpp" |
||||
|
||||
namespace cv { |
||||
namespace gimpl { |
||||
|
||||
struct SubgraphMatch { |
||||
using M = std::unordered_map< ade::NodeHandle // Pattern graph node
|
||||
, ade::NodeHandle // Test graph node
|
||||
, ade::HandleHasher<ade::Node> |
||||
>; |
||||
using S = std::unordered_set< ade::NodeHandle |
||||
, ade::HandleHasher<ade::Node> |
||||
>; |
||||
M inputDataNodes; |
||||
M startOpNodes; |
||||
M finishOpNodes; |
||||
M outputDataNodes; |
||||
|
||||
std::vector<ade::NodeHandle> inputTestDataNodes; |
||||
std::vector<ade::NodeHandle> outputTestDataNodes; |
||||
|
||||
std::list<ade::NodeHandle> internalLayers; |
||||
|
||||
// FIXME: switch to operator bool() instead
|
||||
bool ok() const { |
||||
return !inputDataNodes.empty() && !startOpNodes.empty() |
||||
&& !finishOpNodes.empty() && !outputDataNodes.empty() |
||||
&& !inputTestDataNodes.empty() && !outputTestDataNodes.empty(); |
||||
|
||||
} |
||||
|
||||
S nodes() const { |
||||
S allNodes {}; |
||||
|
||||
allNodes.insert(inputTestDataNodes.begin(), inputTestDataNodes.end()); |
||||
|
||||
for (const auto& startOpMatch : startOpNodes) { |
||||
allNodes.insert(startOpMatch.second); |
||||
} |
||||
|
||||
for (const auto& finishOpMatch : finishOpNodes) { |
||||
allNodes.insert(finishOpMatch.second); |
||||
} |
||||
|
||||
allNodes.insert(outputTestDataNodes.begin(), outputTestDataNodes.end()); |
||||
|
||||
allNodes.insert(internalLayers.begin(), internalLayers.end()); |
||||
|
||||
return allNodes; |
||||
} |
||||
|
||||
S startOps() { |
||||
S sOps; |
||||
for (const auto& opMatch : startOpNodes) { |
||||
sOps.insert(opMatch.second); |
||||
} |
||||
return sOps; |
||||
} |
||||
|
||||
S finishOps() { |
||||
S fOps; |
||||
for (const auto& opMatch : finishOpNodes) { |
||||
fOps.insert(opMatch.second); |
||||
} |
||||
return fOps; |
||||
} |
||||
|
||||
std::vector<ade::NodeHandle> protoIns() { |
||||
return inputTestDataNodes; |
||||
} |
||||
|
||||
|
||||
std::vector<ade::NodeHandle> protoOuts() { |
||||
return outputTestDataNodes; |
||||
} |
||||
}; |
||||
|
||||
GAPI_EXPORTS SubgraphMatch findMatches(const cv::gimpl::GModel::Graph& patternGraph, |
||||
const cv::gimpl::GModel::Graph& compGraph); |
||||
|
||||
} //namespace gimpl
|
||||
} //namespace cv
|
||||
#endif // OPENCV_GAPI_PATTERN_MATCHING_HPP
|
@ -0,0 +1,942 @@ |
||||
// 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
|
||||
|
||||
|
||||
#include "../test_precomp.hpp" |
||||
|
||||
#include <stdexcept> |
||||
|
||||
#include "compiler/gmodel.hpp" |
||||
#include "compiler/gmodel_priv.hpp" |
||||
|
||||
#include "api/gcomputation_priv.hpp" |
||||
#include "compiler/gcompiler.hpp" |
||||
#include "compiler/gmodelbuilder.hpp" |
||||
#include "compiler/passes/passes.hpp" |
||||
|
||||
#include "compiler/passes/pattern_matching.hpp" |
||||
|
||||
#include "../common/gapi_tests_common.hpp" |
||||
|
||||
#include "logger.hpp" |
||||
|
||||
namespace opencv_test |
||||
{ |
||||
|
||||
namespace matching_test { |
||||
namespace { |
||||
using V = std::vector<ade::NodeHandle>; |
||||
using S = std::unordered_set< ade::NodeHandle |
||||
, ade::HandleHasher<ade::Node> |
||||
>; |
||||
|
||||
void initGModel(ade::Graph& gr, |
||||
cv::GProtoInputArgs&& in, |
||||
cv::GProtoOutputArgs&& out) { |
||||
|
||||
cv::gimpl::GModel::Graph gm(gr); |
||||
cv::gimpl::GModel::init(gm); |
||||
auto proto_slots = cv::gimpl::GModelBuilder(gr) |
||||
.put(in.m_args, out.m_args); |
||||
|
||||
cv::gimpl::Protocol p; |
||||
std::tie(p.inputs, p.outputs, p.in_nhs, p.out_nhs) = proto_slots; |
||||
gm.metadata().set(p); |
||||
} |
||||
|
||||
bool isConsumedBy(cv::gimpl::GModel::Graph gm, ade::NodeHandle data_nh, ade::NodeHandle op_nh) { |
||||
auto oi = cv::gimpl::GModel::orderedInputs(gm, op_nh); |
||||
return std::find(oi.begin(), oi.end(), data_nh) != oi.end(); |
||||
} |
||||
|
||||
std::string opName(cv::gimpl::GModel::Graph gm, ade::NodeHandle op_nh) { |
||||
return gm.metadata(op_nh).get<cv::gimpl::Op>().k.name; |
||||
} |
||||
|
||||
} |
||||
} // matching_test
|
||||
|
||||
TEST(PatternMatching, TestFuncDoesNotChangeTestGraph) |
||||
{ |
||||
// Pattern
|
||||
ade::Graph pg; |
||||
{ |
||||
GMat in; |
||||
GMat out = cv::gapi::bitwise_not(in); |
||||
matching_test::initGModel(pg, cv::GIn(in), cv::GOut(out)); |
||||
} |
||||
|
||||
// Test
|
||||
ade::Graph tg; |
||||
GMat in; |
||||
GMat out = cv::gapi::bitwise_not(in); |
||||
matching_test::initGModel(tg, cv::GIn(in), cv::GOut(out)); |
||||
|
||||
// Pattern Matching
|
||||
cv::gimpl::GModel::Graph pgm(pg); |
||||
cv::gimpl::GModel::Graph tgm(tg); |
||||
cv::gimpl::findMatches(pg, tg); |
||||
|
||||
// Inspecting results:
|
||||
matching_test::S nodes{ tgm.nodes().begin(), tgm.nodes().end() }; |
||||
|
||||
const auto in_nh = cv::gimpl::GModel::dataNodeOf(tgm, in); |
||||
const auto out_nh = cv::gimpl::GModel::dataNodeOf(tgm, out); |
||||
|
||||
auto input_data_nhs = tgm.metadata().get<cv::gimpl::Protocol>().in_nhs; |
||||
auto output_data_nhs = tgm.metadata().get<cv::gimpl::Protocol>().out_nhs; |
||||
|
||||
EXPECT_EQ(1u, input_data_nhs.size()); |
||||
EXPECT_EQ(1u, output_data_nhs.size()); |
||||
EXPECT_EQ(in_nh, *input_data_nhs.begin()); |
||||
EXPECT_EQ(out_nh, *output_data_nhs.begin()); |
||||
EXPECT_EQ(0u, in_nh->inEdges().size()); |
||||
EXPECT_EQ(0u, out_nh->outEdges().size()); |
||||
EXPECT_EQ(1u, in_nh->outEdges().size()); |
||||
EXPECT_EQ(1u, out_nh->inEdges().size()); |
||||
|
||||
const auto op_nh = cv::gimpl::GModel::producerOf(tgm, out_nh); //bitwise_not
|
||||
EXPECT_EQ(cv::gapi::core::GNot::id(), matching_test::opName(tgm, op_nh)); |
||||
EXPECT_EQ(1u, op_nh->inEdges().size()); |
||||
EXPECT_TRUE(matching_test::isConsumedBy(tgm, in_nh, op_nh)); |
||||
EXPECT_EQ(1u, op_nh->outEdges().size()); |
||||
} |
||||
|
||||
TEST(PatternMatching, TestSimple1) |
||||
{ |
||||
// Pattern
|
||||
ade::Graph pg; |
||||
{ |
||||
GMat in; |
||||
GMat out = cv::gapi::bitwise_not(in); |
||||
matching_test::initGModel(pg, cv::GIn(in), cv::GOut(out)); |
||||
} |
||||
|
||||
// Test
|
||||
ade::Graph tg; |
||||
GMat in; |
||||
GMat out = cv::gapi::bitwise_not(in); |
||||
matching_test::initGModel(tg, cv::GIn(in), cv::GOut(out)); |
||||
|
||||
// Pattern Matching
|
||||
cv::gimpl::GModel::Graph pgm(pg); |
||||
cv::gimpl::GModel::Graph tgm(tg); |
||||
cv::gimpl::SubgraphMatch match = cv::gimpl::findMatches(pg, tg); |
||||
|
||||
// Inspecting results:
|
||||
EXPECT_TRUE(match.ok()); |
||||
|
||||
auto nodes = match.nodes(); |
||||
EXPECT_EQ(3u, nodes.size()); |
||||
|
||||
const auto in_nh = cv::gimpl::GModel::dataNodeOf(tgm, in); |
||||
const auto out_nh = cv::gimpl::GModel::dataNodeOf(tgm, out); |
||||
const auto op_nh = cv::gimpl::GModel::producerOf(tgm, out_nh); |
||||
|
||||
EXPECT_EQ(matching_test::S({in_nh, out_nh, op_nh}), nodes); |
||||
EXPECT_EQ(cv::gapi::core::GNot::id(), matching_test::opName(tgm, op_nh)); |
||||
EXPECT_TRUE(matching_test::isConsumedBy(tgm, in_nh, op_nh)); |
||||
EXPECT_EQ(matching_test::S{op_nh}, match.startOps()); |
||||
EXPECT_EQ(matching_test::S{op_nh}, match.finishOps()); |
||||
EXPECT_EQ(matching_test::V{in_nh}, match.protoIns()); |
||||
EXPECT_EQ(matching_test::V{out_nh}, match.protoOuts()); |
||||
} |
||||
|
||||
TEST(PatternMatching, TestSimple2) |
||||
{ |
||||
// Pattern
|
||||
ade::Graph pg; |
||||
{ |
||||
GMat in; |
||||
GMat out = cv::gapi::bitwise_not(in); |
||||
matching_test::initGModel(pg, cv::GIn(in), cv::GOut(out)); |
||||
} |
||||
|
||||
// Test
|
||||
ade::Graph tg; |
||||
GMat in; |
||||
GMat tmp = cv::gapi::bitwise_not(in); |
||||
GMat out = cv::gapi::blur(tmp, cv::Size(3, 3)); |
||||
matching_test::initGModel(tg, cv::GIn(in), cv::GOut(out)); |
||||
|
||||
// Pattern Matching
|
||||
cv::gimpl::GModel::Graph pgm(pg); |
||||
cv::gimpl::GModel::Graph tgm(tg); |
||||
cv::gimpl::SubgraphMatch match = cv::gimpl::findMatches(pg, tg); |
||||
|
||||
// Inspecting results:
|
||||
EXPECT_TRUE(match.ok()); |
||||
|
||||
auto nodes = match.nodes(); |
||||
EXPECT_EQ(3u, nodes.size()); |
||||
|
||||
const auto in_nh = cv::gimpl::GModel::dataNodeOf(tgm, in); |
||||
const auto tmp_nh = cv::gimpl::GModel::dataNodeOf(tgm, tmp); |
||||
const auto op_nh = cv::gimpl::GModel::producerOf(tgm, tmp_nh); |
||||
|
||||
EXPECT_EQ(matching_test::S({in_nh, tmp_nh, op_nh}), nodes); |
||||
EXPECT_EQ(cv::gapi::core::GNot::id(), matching_test::opName(tgm, op_nh)); |
||||
EXPECT_TRUE(matching_test::isConsumedBy(tgm, in_nh, op_nh)); |
||||
EXPECT_EQ(matching_test::S{op_nh}, match.startOps()); |
||||
EXPECT_EQ(matching_test::S{op_nh}, match.finishOps()); |
||||
EXPECT_EQ(matching_test::V{in_nh}, match.protoIns()); |
||||
EXPECT_EQ(matching_test::V{tmp_nh}, match.protoOuts()); |
||||
} |
||||
|
||||
TEST(PatternMatching, TestSimple3) |
||||
{ |
||||
// Pattern
|
||||
ade::Graph pg; |
||||
{ |
||||
GMat in; |
||||
GMat out = cv::gapi::bitwise_not(in); |
||||
matching_test::initGModel(pg, cv::GIn(in), cv::GOut(out)); |
||||
} |
||||
|
||||
// Test
|
||||
ade::Graph tg; |
||||
GMat in; |
||||
GMat tmp = cv::gapi::blur(in, cv::Size(3, 3)); |
||||
GMat out = cv::gapi::bitwise_not(tmp); |
||||
matching_test::initGModel(tg, cv::GIn(in), cv::GOut(out)); |
||||
|
||||
// Pattern Matching
|
||||
cv::gimpl::GModel::Graph pgm(pg); |
||||
cv::gimpl::GModel::Graph tgm(tg); |
||||
cv::gimpl::SubgraphMatch match = cv::gimpl::findMatches(pg, tg); |
||||
|
||||
// Inspecting results:
|
||||
EXPECT_TRUE(match.ok()); |
||||
|
||||
auto nodes = match.nodes(); |
||||
EXPECT_EQ(3u, nodes.size()); |
||||
|
||||
const auto tmp_nh = cv::gimpl::GModel::dataNodeOf(tgm, tmp); |
||||
const auto out_nh = cv::gimpl::GModel::dataNodeOf(tgm, out); |
||||
const auto op_nh = cv::gimpl::GModel::producerOf(tgm, out_nh); |
||||
|
||||
EXPECT_EQ(matching_test::S({tmp_nh, out_nh, op_nh}), nodes); |
||||
EXPECT_EQ(cv::gapi::core::GNot::id(), matching_test::opName(tgm, op_nh)); |
||||
EXPECT_TRUE(matching_test::isConsumedBy(tgm, tmp_nh, op_nh)); |
||||
EXPECT_EQ(matching_test::S{op_nh}, match.startOps()); |
||||
EXPECT_EQ(matching_test::S{op_nh}, match.finishOps()); |
||||
EXPECT_EQ(matching_test::V{tmp_nh}, match.protoIns()); |
||||
EXPECT_EQ(matching_test::V{out_nh}, match.protoOuts()); |
||||
} |
||||
|
||||
TEST(PatternMatching, TestMultiplePatternOuts) |
||||
{ |
||||
// Pattern
|
||||
ade::Graph pg; |
||||
{ |
||||
GMat in; |
||||
GMat dx, dy; |
||||
std::tie(dx, dy) = cv::gapi::SobelXY(in, -1, 1); |
||||
matching_test::initGModel(pg, cv::GIn(in), cv::GOut(dx, dy)); |
||||
} |
||||
|
||||
// Test
|
||||
ade::Graph tg; |
||||
GMat in; |
||||
GMat dx, dy; |
||||
std::tie(dx, dy) = cv::gapi::SobelXY(in, -1, 1); |
||||
matching_test::initGModel(tg, cv::GIn(in), cv::GOut(dx, dy)); |
||||
|
||||
// Pattern Matching
|
||||
cv::gimpl::GModel::Graph pgm(pg); |
||||
cv::gimpl::GModel::Graph tgm(tg); |
||||
cv::gimpl::SubgraphMatch match = cv::gimpl::findMatches(pg, tg); |
||||
|
||||
// Inspecting results:
|
||||
EXPECT_TRUE(match.ok()); |
||||
|
||||
auto nodes = match.nodes(); |
||||
EXPECT_EQ(4u, nodes.size()); |
||||
|
||||
const auto in_nh = cv::gimpl::GModel::dataNodeOf(tgm, in); |
||||
const auto dx_nh = cv::gimpl::GModel::dataNodeOf(tgm, dx); |
||||
const auto dy_nh = cv::gimpl::GModel::dataNodeOf(tgm, dy); |
||||
const auto op_nh = cv::gimpl::GModel::producerOf(tgm, dx_nh); |
||||
EXPECT_EQ(op_nh, cv::gimpl::GModel::producerOf(tgm, dy_nh)); |
||||
|
||||
EXPECT_EQ(matching_test::S({in_nh, dx_nh, dy_nh, op_nh}), nodes); |
||||
EXPECT_EQ(cv::gapi::imgproc::GSobelXY::id(), matching_test::opName(tgm, op_nh)); |
||||
EXPECT_TRUE(matching_test::isConsumedBy(tgm, in_nh, op_nh)); |
||||
EXPECT_EQ(matching_test::S{op_nh}, match.startOps()); |
||||
EXPECT_EQ(matching_test::S{op_nh}, match.finishOps()); |
||||
EXPECT_EQ(matching_test::V{in_nh}, match.protoIns()); |
||||
EXPECT_EQ(matching_test::V({dx_nh, dy_nh}), match.protoOuts()); |
||||
} |
||||
|
||||
TEST(PatternMatching, TestPreprocSplit3) |
||||
{ |
||||
// Pattern
|
||||
ade::Graph pg; |
||||
{ |
||||
GMat in; |
||||
GMat tmp = cv::gapi::resize(in, cv::Size{224, 224}); |
||||
GMat b, g, r; |
||||
std::tie(b, g, r) = cv::gapi::split3(tmp); |
||||
matching_test::initGModel(pg, cv::GIn(in), cv::GOut(b, g, r)); |
||||
} |
||||
|
||||
// Test
|
||||
ade::Graph tg; |
||||
GMat y, uv; |
||||
GMat bgr = cv::gapi::NV12toBGR(y, uv); |
||||
GMat tmp = cv::gapi::resize(bgr, cv::Size{224, 224}); |
||||
GMat b, g, r; |
||||
std::tie(b, g, r) = cv::gapi::split3(tmp); |
||||
matching_test::initGModel(tg, cv::GIn(y, uv), cv::GOut(b, g, r)); |
||||
|
||||
// Pattern Matching
|
||||
cv::gimpl::GModel::Graph pgm(pg); |
||||
cv::gimpl::GModel::Graph tgm(tg); |
||||
cv::gimpl::SubgraphMatch match = cv::gimpl::findMatches(pg, tg); |
||||
|
||||
// Inspecting results:
|
||||
EXPECT_TRUE(match.ok()); |
||||
|
||||
auto nodes = match.nodes(); |
||||
EXPECT_EQ(7u, nodes.size()); |
||||
|
||||
const auto bgr_nh = cv::gimpl::GModel::dataNodeOf(tgm, bgr); |
||||
const auto tmp_nh = cv::gimpl::GModel::dataNodeOf(tgm, tmp); |
||||
const auto b_nh = cv::gimpl::GModel::dataNodeOf(tgm, b); |
||||
const auto g_nh = cv::gimpl::GModel::dataNodeOf(tgm, g); |
||||
const auto r_nh = cv::gimpl::GModel::dataNodeOf(tgm, r); |
||||
|
||||
const auto op1_nh = cv::gimpl::GModel::producerOf(tgm, tmp_nh); // 1st resize
|
||||
const auto op2_nh = cv::gimpl::GModel::producerOf(tgm, b_nh); // 2nd split3
|
||||
EXPECT_EQ(op2_nh, cv::gimpl::GModel::producerOf(tgm, g_nh)); |
||||
EXPECT_EQ(op2_nh, cv::gimpl::GModel::producerOf(tgm, r_nh)); |
||||
|
||||
EXPECT_EQ(matching_test::S({bgr_nh, tmp_nh, b_nh, g_nh, |
||||
r_nh, op1_nh, op2_nh}), |
||||
nodes); |
||||
|
||||
EXPECT_EQ(cv::gapi::core::GResize::id(), matching_test::opName(tgm, op1_nh)); |
||||
EXPECT_EQ(cv::gapi::core::GSplit3::id(), matching_test::opName(tgm, op2_nh)); |
||||
|
||||
EXPECT_EQ(1u, tmp_nh->outEdges().size()); |
||||
EXPECT_TRUE(matching_test::isConsumedBy(tgm, bgr_nh, op1_nh)); |
||||
EXPECT_TRUE(matching_test::isConsumedBy(tgm, tmp_nh, op2_nh)); |
||||
|
||||
EXPECT_EQ(matching_test::S{ op1_nh }, match.startOps()); |
||||
EXPECT_EQ(matching_test::S{ op2_nh }, match.finishOps()); |
||||
EXPECT_EQ(matching_test::V{ bgr_nh }, match.protoIns()); |
||||
EXPECT_EQ(matching_test::V({ b_nh, g_nh, r_nh }), match.protoOuts()); |
||||
} |
||||
|
||||
G_TYPED_KERNEL(GToNCHW, <GMatP(GMat)>, "test.toNCHW") { |
||||
static GMatDesc outMeta(GMatDesc in) { |
||||
GAPI_Assert(in.depth == CV_8U); |
||||
GAPI_Assert(in.chan == 3); |
||||
GAPI_Assert(in.planar == false); |
||||
return in.asPlanar(); |
||||
} |
||||
}; |
||||
|
||||
static GMat toNCHW(const GMat& src) |
||||
{ |
||||
return GToNCHW::on(src); |
||||
} |
||||
|
||||
TEST(PatternMatching, TestPreprocToNCHW) |
||||
{ |
||||
// Pattern
|
||||
ade::Graph pg; |
||||
{ |
||||
GMat in; |
||||
GMat tmp = cv::gapi::resize(in, cv::Size{224, 224}); |
||||
GMat plr = toNCHW(tmp); |
||||
matching_test::initGModel(pg, cv::GIn(in), cv::GOut(plr)); |
||||
} |
||||
|
||||
// Test
|
||||
ade::Graph tg; |
||||
GMat y, uv; |
||||
GMat bgr = cv::gapi::NV12toBGR(y, uv); |
||||
GMat tmp = cv::gapi::resize(bgr, cv::Size{224, 224}); |
||||
GMat plr = toNCHW(tmp); |
||||
matching_test::initGModel(tg, cv::GIn(y, uv), cv::GOut(plr)); |
||||
|
||||
// Pattern Matching
|
||||
cv::gimpl::GModel::Graph pgm(pg); |
||||
cv::gimpl::GModel::Graph tgm(tg); |
||||
cv::gimpl::SubgraphMatch match = cv::gimpl::findMatches(pg, tg); |
||||
|
||||
// Inspecting results:
|
||||
EXPECT_TRUE(match.ok()); |
||||
|
||||
auto nodes = match.nodes(); |
||||
EXPECT_EQ(5u, nodes.size()); |
||||
|
||||
const auto bgr_nh = cv::gimpl::GModel::dataNodeOf(tgm, bgr); |
||||
const auto tmp_nh = cv::gimpl::GModel::dataNodeOf(tgm, tmp); |
||||
const auto plr_nh = cv::gimpl::GModel::dataNodeOf(tgm, plr); |
||||
|
||||
const auto op1_nh = cv::gimpl::GModel::producerOf(tgm, tmp_nh); // 1st resize
|
||||
const auto op2_nh = cv::gimpl::GModel::producerOf(tgm, plr_nh); // 2nd toNCHW
|
||||
|
||||
EXPECT_EQ(matching_test::S({bgr_nh, tmp_nh, plr_nh, op1_nh, op2_nh}), |
||||
nodes); |
||||
|
||||
EXPECT_EQ(cv::gapi::core::GResize::id(), matching_test::opName(tgm, op1_nh)); |
||||
EXPECT_EQ(GToNCHW::id(), matching_test::opName(tgm, op2_nh)); |
||||
|
||||
EXPECT_EQ(1u, tmp_nh->outEdges().size()); |
||||
EXPECT_TRUE(matching_test::isConsumedBy(tgm, bgr_nh, op1_nh)); |
||||
EXPECT_TRUE(matching_test::isConsumedBy(tgm, tmp_nh, op2_nh)); |
||||
|
||||
EXPECT_EQ(matching_test::S{ op1_nh }, match.startOps()); |
||||
EXPECT_EQ(matching_test::S{ op2_nh }, match.finishOps()); |
||||
EXPECT_EQ(matching_test::V{ bgr_nh }, match.protoIns()); |
||||
EXPECT_EQ(matching_test::V{ plr_nh }, match.protoOuts()); |
||||
} |
||||
|
||||
//FIXME: To switch from filter2d kernel (which shall be matched by params too) to another one
|
||||
TEST(PatternMatching, MatchChainInTheMiddle) |
||||
{ |
||||
// Pattern
|
||||
ade::Graph pg; |
||||
{ |
||||
GMat in; |
||||
GMat tmp = cv::gapi::filter2D(in, -1, {}); |
||||
GMat out = cv::gapi::filter2D(tmp, -1, {}); |
||||
matching_test::initGModel(pg, cv::GIn(in), cv::GOut(out)); |
||||
} |
||||
|
||||
// Test
|
||||
ade::Graph tg; |
||||
GMat in; |
||||
GMat tmp1 = cv::gapi::erode3x3(in); |
||||
GMat tmp2 = cv::gapi::filter2D(tmp1, -1, {}); |
||||
GMat tmp3 = cv::gapi::filter2D(tmp2, -1, {}); |
||||
GMat out = cv::gapi::dilate3x3(tmp3); |
||||
matching_test::initGModel(tg, cv::GIn(in), cv::GOut(out)); |
||||
|
||||
// Pattern Matching
|
||||
cv::gimpl::GModel::Graph pgm(pg); |
||||
cv::gimpl::GModel::Graph tgm(tg); |
||||
cv::gimpl::SubgraphMatch match = cv::gimpl::findMatches(pg, tg); |
||||
|
||||
// Inspecting results:
|
||||
EXPECT_TRUE(match.ok()); |
||||
|
||||
auto nodes = match.nodes(); |
||||
EXPECT_EQ(5u, nodes.size()); |
||||
|
||||
const auto tmp1_nh = cv::gimpl::GModel::dataNodeOf(tgm, tmp1); |
||||
const auto tmp2_nh = cv::gimpl::GModel::dataNodeOf(tgm, tmp2); |
||||
const auto tmp3_nh = cv::gimpl::GModel::dataNodeOf(tgm, tmp3); |
||||
const auto op1_nh = cv::gimpl::GModel::producerOf(tgm, tmp2_nh); // 1st filter2D
|
||||
const auto op2_nh = cv::gimpl::GModel::producerOf(tgm, tmp3_nh); // 2nd filter2D
|
||||
|
||||
EXPECT_EQ(matching_test::S({tmp1_nh, tmp2_nh, tmp3_nh, op1_nh, op2_nh}), nodes); |
||||
|
||||
EXPECT_EQ(cv::gapi::imgproc::GFilter2D::id(), matching_test::opName(tgm, op1_nh)); |
||||
EXPECT_EQ(cv::gapi::imgproc::GFilter2D::id(), matching_test::opName(tgm, op2_nh)); |
||||
|
||||
EXPECT_EQ(1u, tmp2_nh->outEdges().size()); |
||||
EXPECT_TRUE(matching_test::isConsumedBy(tgm, tmp1_nh, op1_nh)); |
||||
EXPECT_TRUE(matching_test::isConsumedBy(tgm, tmp2_nh, op2_nh)); |
||||
|
||||
EXPECT_EQ(matching_test::S({op1_nh}), match.startOps()); |
||||
EXPECT_EQ(matching_test::S({op2_nh}), match.finishOps()); |
||||
EXPECT_EQ(matching_test::V{ tmp1_nh }, match.protoIns()); |
||||
EXPECT_EQ(matching_test::V{ tmp3_nh }, match.protoOuts()); |
||||
} |
||||
|
||||
TEST(PatternMatching, TestMultipleStartOps1) |
||||
{ |
||||
// Pattern
|
||||
ade::Graph pg; |
||||
{ |
||||
GMat in1, in2; |
||||
GMat er = cv::gapi::erode3x3(in1); |
||||
GMat dil = cv::gapi::dilate3x3(in2); |
||||
GMat out = cv::gapi::add(er, dil); |
||||
matching_test::initGModel(pg, cv::GIn(in1, in2), cv::GOut(out)); |
||||
} |
||||
|
||||
// Test
|
||||
ade::Graph tg; |
||||
|
||||
GMat in1, in2, in3, in4, in5, in6; |
||||
GMat er1 = cv::gapi::erode3x3(in1); |
||||
GMat er2 = cv::gapi::erode3x3(in2); |
||||
GMat er3 = cv::gapi::erode3x3(in3); |
||||
GMat er4 = cv::gapi::erode3x3(in4); |
||||
GMat dil1 = cv::gapi::dilate3x3(in5); |
||||
GMat dil2 = cv::gapi::dilate3x3(in6); |
||||
GMat out1 = cv::gapi::add(er1, er2); |
||||
GMat out2 = cv::gapi::add(er3, dil2); |
||||
matching_test::initGModel(tg, cv::GIn(in1, in2, in3, in4, in5, in6), cv::GOut(out1, out2, er4, dil1)); |
||||
|
||||
// Pattern Matching
|
||||
cv::gimpl::GModel::Graph pgm(pg); |
||||
cv::gimpl::GModel::Graph tgm(tg); |
||||
cv::gimpl::SubgraphMatch match = cv::gimpl::findMatches(pg, tg); |
||||
|
||||
// Inspecting results:
|
||||
EXPECT_TRUE(match.ok()); |
||||
|
||||
auto nodes = match.nodes(); |
||||
EXPECT_EQ(8u, nodes.size()); |
||||
|
||||
const auto in3_nh = cv::gimpl::GModel::dataNodeOf(tgm, in3); |
||||
const auto in6_nh = cv::gimpl::GModel::dataNodeOf(tgm, in6); |
||||
const auto er3_nh = cv::gimpl::GModel::dataNodeOf(tgm, er3); |
||||
const auto dil2_nh = cv::gimpl::GModel::dataNodeOf(tgm, dil2); |
||||
const auto out2_nh = cv::gimpl::GModel::dataNodeOf(tgm, out2); |
||||
|
||||
const auto er_op_nh = cv::gimpl::GModel::producerOf(tgm, er3_nh); |
||||
const auto dil_op_nh = cv::gimpl::GModel::producerOf(tgm, dil2_nh); |
||||
const auto add_op_nh = cv::gimpl::GModel::producerOf(tgm, out2_nh); |
||||
|
||||
EXPECT_EQ(matching_test::S({in3_nh, in6_nh, er3_nh, dil2_nh, out2_nh, |
||||
er_op_nh, dil_op_nh, add_op_nh}), |
||||
nodes); |
||||
|
||||
EXPECT_EQ(cv::gapi::imgproc::GErode::id(), matching_test::opName(tgm, er_op_nh)); |
||||
EXPECT_EQ(cv::gapi::imgproc::GDilate::id(), matching_test::opName(tgm, dil_op_nh)); |
||||
EXPECT_EQ(cv::gapi::core::GAdd::id(), matching_test::opName(tgm, add_op_nh)); |
||||
|
||||
EXPECT_EQ(1u, er3_nh->outEdges().size()); |
||||
EXPECT_EQ(1u, dil2_nh->outEdges().size()); |
||||
EXPECT_TRUE(matching_test::isConsumedBy(tgm, in3_nh, er_op_nh)); |
||||
EXPECT_TRUE(matching_test::isConsumedBy(tgm, in6_nh, dil_op_nh)); |
||||
EXPECT_TRUE(matching_test::isConsumedBy(tgm, er3_nh, add_op_nh)); |
||||
EXPECT_TRUE(matching_test::isConsumedBy(tgm, dil2_nh, add_op_nh)); |
||||
|
||||
EXPECT_EQ(matching_test::S({ er_op_nh, dil_op_nh }), match.startOps()); |
||||
EXPECT_EQ(matching_test::S{ add_op_nh }, match.finishOps()); |
||||
EXPECT_EQ(matching_test::V({ in3_nh, in6_nh }), match.protoIns()); |
||||
EXPECT_EQ(matching_test::V{ out2_nh }, match.protoOuts()); |
||||
} |
||||
|
||||
TEST(PatternMatching, TestMultipleStartOps2) |
||||
{ |
||||
// Pattern
|
||||
ade::Graph pg; |
||||
{ |
||||
GMat in1, in2; |
||||
GMat er = cv::gapi::erode3x3(in1); |
||||
GMat dil = cv::gapi::dilate3x3(in2); |
||||
GMat out = cv::gapi::add(er, dil); |
||||
matching_test::initGModel(pg, cv::GIn(in1, in2), cv::GOut(out)); |
||||
} |
||||
|
||||
// Test
|
||||
ade::Graph tg; |
||||
|
||||
GMat in1, in2; |
||||
GMat er = cv::gapi::erode3x3(in1); |
||||
GMat dil1 = cv::gapi::dilate3x3(in2); |
||||
GMat dil2 = cv::gapi::dilate3x3(dil1); |
||||
GMat out = cv::gapi::add(er, dil2); |
||||
matching_test::initGModel(tg, cv::GIn(in1, in2), cv::GOut(out)); |
||||
|
||||
// Pattern Matching
|
||||
cv::gimpl::GModel::Graph pgm(pg); |
||||
cv::gimpl::GModel::Graph tgm(tg); |
||||
cv::gimpl::SubgraphMatch match = cv::gimpl::findMatches(pg, tg); |
||||
|
||||
// Inspecting results:
|
||||
EXPECT_TRUE(match.ok()); |
||||
|
||||
auto nodes = match.nodes(); |
||||
EXPECT_EQ(8u, nodes.size()); |
||||
|
||||
const auto in1_nh = cv::gimpl::GModel::dataNodeOf(tgm, in1); |
||||
const auto dil1_nh = cv::gimpl::GModel::dataNodeOf(tgm, dil1); |
||||
const auto er_nh = cv::gimpl::GModel::dataNodeOf(tgm, er); |
||||
const auto dil2_nh = cv::gimpl::GModel::dataNodeOf(tgm, dil2); |
||||
const auto out_nh = cv::gimpl::GModel::dataNodeOf(tgm, out); |
||||
|
||||
const auto er_op_nh = cv::gimpl::GModel::producerOf(tgm, er_nh); |
||||
const auto dil_op_nh = cv::gimpl::GModel::producerOf(tgm, dil2_nh); |
||||
const auto add_op_nh = cv::gimpl::GModel::producerOf(tgm, out_nh); |
||||
|
||||
EXPECT_EQ(matching_test::S({in1_nh, dil1_nh, er_nh, dil2_nh, out_nh, |
||||
er_op_nh, dil_op_nh, add_op_nh}), |
||||
nodes); |
||||
|
||||
EXPECT_EQ(cv::gapi::imgproc::GErode::id(), matching_test::opName(tgm, er_op_nh)); |
||||
EXPECT_EQ(cv::gapi::imgproc::GDilate::id(), matching_test::opName(tgm, dil_op_nh)); |
||||
EXPECT_EQ(cv::gapi::core::GAdd::id(), matching_test::opName(tgm, add_op_nh)); |
||||
|
||||
EXPECT_EQ(1u, er_nh->outEdges().size()); |
||||
EXPECT_EQ(1u, dil2_nh->outEdges().size()); |
||||
EXPECT_TRUE(matching_test::isConsumedBy(tgm, in1_nh, er_op_nh)); |
||||
EXPECT_TRUE(matching_test::isConsumedBy(tgm, dil1_nh, dil_op_nh)); |
||||
EXPECT_TRUE(matching_test::isConsumedBy(tgm, er_nh, add_op_nh)); |
||||
EXPECT_TRUE(matching_test::isConsumedBy(tgm, dil2_nh, add_op_nh)); |
||||
|
||||
EXPECT_EQ(matching_test::S({ er_op_nh, dil_op_nh }), match.startOps()); |
||||
EXPECT_EQ(matching_test::S{ add_op_nh }, match.finishOps()); |
||||
EXPECT_EQ(matching_test::V({ in1_nh, dil1_nh }), match.protoIns()); |
||||
EXPECT_EQ(matching_test::V{ out_nh }, match.protoOuts()); |
||||
} |
||||
|
||||
TEST(PatternMatching, TestInexactMatchOfInOutData) |
||||
{ |
||||
// Pattern
|
||||
ade::Graph pg; |
||||
{ |
||||
GMat in; |
||||
GMat out = cv::gapi::dilate3x3(in); |
||||
matching_test::initGModel(pg, cv::GIn(in), cv::GOut(out)); |
||||
} |
||||
|
||||
// Test
|
||||
ade::Graph tg; |
||||
GMat in; |
||||
GMat out1 = cv::gapi::erode3x3(in); |
||||
GMat out2 = cv::gapi::boxFilter(in, -1, cv::Size(3, 3)); |
||||
GMat tmp = cv::gapi::dilate3x3(in); |
||||
GScalar out3 = cv::gapi::sum(tmp); |
||||
GScalar out4 = cv::gapi::mean(tmp); |
||||
matching_test::initGModel(tg, cv::GIn(in), cv::GOut(out1, out2, out3, out4)); |
||||
|
||||
// Pattern Matching
|
||||
cv::gimpl::GModel::Graph pgm(pg); |
||||
cv::gimpl::GModel::Graph tgm(tg); |
||||
cv::gimpl::SubgraphMatch match = cv::gimpl::findMatches(pg, tg); |
||||
|
||||
// Inspecting results:
|
||||
EXPECT_TRUE(match.ok()); |
||||
|
||||
auto nodes = match.nodes(); |
||||
EXPECT_EQ(3u, nodes.size()); |
||||
|
||||
const auto in_nh = cv::gimpl::GModel::dataNodeOf(tgm, in); |
||||
const auto tmp_nh = cv::gimpl::GModel::dataNodeOf(tgm, tmp); |
||||
|
||||
const auto op_nh = cv::gimpl::GModel::producerOf(tgm, tmp_nh); // dilate3x3
|
||||
|
||||
EXPECT_EQ(matching_test::S({in_nh, tmp_nh, op_nh}), |
||||
nodes); |
||||
|
||||
EXPECT_EQ(cv::gapi::imgproc::GDilate::id(), matching_test::opName(tgm, op_nh)); |
||||
|
||||
EXPECT_TRUE(matching_test::isConsumedBy(tgm, in_nh, op_nh)); |
||||
|
||||
|
||||
EXPECT_EQ(matching_test::S{ op_nh }, match.startOps()); |
||||
EXPECT_EQ(matching_test::S{ op_nh }, match.finishOps()); |
||||
EXPECT_EQ(matching_test::V{ in_nh }, match.protoIns()); |
||||
EXPECT_EQ(matching_test::V{ tmp_nh }, match.protoOuts()); |
||||
|
||||
EXPECT_GT(in_nh->outEdges().size(), 1u); |
||||
EXPECT_GT(tmp_nh->outEdges().size(), 1u); |
||||
} |
||||
|
||||
//FIXME: The start ops matching shall be reworked to more smarter way.
|
||||
// Start ops matching shall get rid of non valid matchings sample,
|
||||
// where two identical start ops in the pattern refer to the only one in the test.
|
||||
TEST(PatternMatching, TestManySameStartOpsAndHinge) |
||||
{ |
||||
// Pattern
|
||||
ade::Graph pg; |
||||
{ |
||||
GMat in1, in2, in3; |
||||
GMat er1 = cv::gapi::erode3x3(in1); |
||||
GMat er2 = cv::gapi::erode3x3(in2); |
||||
GMat er3 = cv::gapi::erode3x3(in3); |
||||
GMat mrg = cv::gapi::merge3(er1, er2, er3); |
||||
matching_test::initGModel(pg, cv::GIn(in1, in2, in3), cv::GOut(mrg)); |
||||
} |
||||
|
||||
// Test
|
||||
ade::Graph tg; |
||||
GMat in1, in2, in3; |
||||
GMat er1 = cv::gapi::erode3x3(in1); |
||||
GMat er2 = cv::gapi::erode3x3(in2); |
||||
GMat er3 = cv::gapi::erode3x3(in3); |
||||
GMat mrg = cv::gapi::merge3(er1, er2, er3); |
||||
matching_test::initGModel(tg, cv::GIn(in1, in2, in3), cv::GOut(mrg)); |
||||
|
||||
// Pattern Matching
|
||||
cv::gimpl::GModel::Graph pgm(pg); |
||||
cv::gimpl::GModel::Graph tgm(tg); |
||||
cv::gimpl::SubgraphMatch match = cv::gimpl::findMatches(pg, tg); |
||||
|
||||
// Inspecting results:
|
||||
EXPECT_TRUE(match.ok()); |
||||
|
||||
auto nodes = match.nodes(); |
||||
EXPECT_EQ(11u, nodes.size()); |
||||
EXPECT_EQ(matching_test::S(tgm.nodes().begin(), tgm.nodes().end()), |
||||
nodes); |
||||
} |
||||
|
||||
//FIXME: The start ops matching shall be reworked to more smarter way.
|
||||
// Start ops matching shall get rid of non valid matchings sample,
|
||||
// where two identical start ops in the pattern refer to the only one in the test.
|
||||
TEST(PatternMatching, TestManySameStartOpsAndHinge2) |
||||
{ |
||||
// Pattern
|
||||
ade::Graph pg; |
||||
{ |
||||
GMat in1, in2, in3; |
||||
GMat er1 = cv::gapi::erode3x3(in1); |
||||
GMat er2 = cv::gapi::erode3x3(in2); |
||||
GMat er3 = cv::gapi::erode3x3(in3); |
||||
GMat dil1 = cv::gapi::dilate3x3(er1); |
||||
GMat dil2 = cv::gapi::dilate3x3(er2); |
||||
GMat dil3 = cv::gapi::dilate3x3(er3); |
||||
GMat mrg = cv::gapi::merge3(dil1, dil2, dil3); |
||||
matching_test::initGModel(pg, cv::GIn(in1, in2, in3), cv::GOut(mrg)); |
||||
} |
||||
|
||||
// Test
|
||||
ade::Graph tg; |
||||
GMat in1, in2, in3; |
||||
GMat er1 = cv::gapi::erode3x3(in1); |
||||
GMat er2 = cv::gapi::erode3x3(in2); |
||||
GMat er3 = cv::gapi::erode3x3(in3); |
||||
GMat dil1 = cv::gapi::dilate3x3(er1); |
||||
GMat dil2 = cv::gapi::dilate3x3(er2); |
||||
GMat dil3 = cv::gapi::dilate3x3(er3); |
||||
GMat mrg = cv::gapi::merge3(dil1, dil2, dil3); |
||||
matching_test::initGModel(tg, cv::GIn(in1, in2, in3), cv::GOut(mrg)); |
||||
|
||||
// Pattern Matching
|
||||
cv::gimpl::GModel::Graph pgm(pg); |
||||
cv::gimpl::GModel::Graph tgm(tg); |
||||
cv::gimpl::SubgraphMatch match = cv::gimpl::findMatches(pg, tg); |
||||
|
||||
// Inspecting results:
|
||||
EXPECT_TRUE(match.ok()); |
||||
|
||||
auto nodes = match.nodes(); |
||||
EXPECT_EQ(17u, nodes.size()); |
||||
EXPECT_EQ(matching_test::S(tgm.nodes().begin(), tgm.nodes().end()), |
||||
nodes); |
||||
} |
||||
|
||||
//FIXME: The start ops matching shall be reworked to more smarter way.
|
||||
// Start ops matching shall get rid of non valid matchings sample,
|
||||
// where two identical start ops in the pattern refer to the only one in the test.
|
||||
TEST(PatternMatching, TestTwoChainsOnTheHingeIsomorphism) |
||||
{ |
||||
// Pattern
|
||||
ade::Graph pg; |
||||
{ |
||||
GMat in1, in2; |
||||
GMat er1 = cv::gapi::erode3x3(in1); |
||||
GMat er2 = cv::gapi::erode3x3(in2); |
||||
GMat mdb = cv::gapi::medianBlur(er1, 3); |
||||
GMat gb = cv::gapi::gaussianBlur(er2, cv::Size(5, 5), 0.12); |
||||
GMat conc = cv::gapi::concatVert(mdb, gb); |
||||
matching_test::initGModel(pg, cv::GIn(in1, in2), cv::GOut(conc)); |
||||
} |
||||
|
||||
// Test
|
||||
ade::Graph tg; |
||||
GMat in1, in2; |
||||
GMat er1 = cv::gapi::erode3x3(in1); |
||||
GMat er2 = cv::gapi::erode3x3(in2); |
||||
GMat gb = cv::gapi::gaussianBlur(er1, cv::Size(5, 5), 0.12); |
||||
GMat mdb = cv::gapi::medianBlur(er2, 3); |
||||
GMat conc = cv::gapi::concatVert(mdb, gb); |
||||
matching_test::initGModel(tg, cv::GIn(in1, in2), cv::GOut(conc)); |
||||
|
||||
// Pattern Matching
|
||||
cv::gimpl::GModel::Graph pgm(pg); |
||||
cv::gimpl::GModel::Graph tgm(tg); |
||||
cv::gimpl::SubgraphMatch match = cv::gimpl::findMatches(pg, tg); |
||||
|
||||
// Inspecting results:
|
||||
EXPECT_TRUE(match.ok()); |
||||
|
||||
auto nodes = match.nodes(); |
||||
EXPECT_EQ(12u, nodes.size()); |
||||
EXPECT_EQ(matching_test::S(tgm.nodes().begin(), tgm.nodes().end()), |
||||
nodes); |
||||
|
||||
const auto in1_nh = cv::gimpl::GModel::dataNodeOf(tgm, in1); |
||||
const auto in2_nh = cv::gimpl::GModel::dataNodeOf(tgm, in2); |
||||
|
||||
EXPECT_EQ(matching_test::V({ in2_nh, in1_nh }), match.protoIns()); |
||||
} |
||||
|
||||
TEST(PatternMatching, TestPatternHasMoreInDataNodes) |
||||
{ |
||||
// Pattern
|
||||
ade::Graph pg; |
||||
{ |
||||
GMat in1, in2, in3; |
||||
GMat out = cv::gapi::merge3(in1, in2, in3); |
||||
matching_test::initGModel(pg, cv::GIn(in1, in2, in3), cv::GOut(out)); |
||||
} |
||||
|
||||
// Test
|
||||
ade::Graph tg; |
||||
GMat in; |
||||
GMat out = cv::gapi::merge3(in, in, in); |
||||
matching_test::initGModel(tg, cv::GIn(in), cv::GOut(out)); |
||||
|
||||
// Pattern Matching
|
||||
cv::gimpl::GModel::Graph pgm(pg); |
||||
cv::gimpl::GModel::Graph tgm(tg); |
||||
cv::gimpl::SubgraphMatch match = cv::gimpl::findMatches(pg, tg); |
||||
|
||||
// Inspecting results:
|
||||
EXPECT_TRUE(match.ok()); |
||||
|
||||
auto nodes = match.nodes(); |
||||
EXPECT_EQ(3u, nodes.size()); |
||||
EXPECT_EQ(matching_test::S(tgm.nodes().begin(), tgm.nodes().end()), |
||||
nodes); |
||||
|
||||
const auto in_nh = cv::gimpl::GModel::dataNodeOf(tgm, in); |
||||
|
||||
EXPECT_EQ(matching_test::V({ in_nh, in_nh, in_nh }), match.protoIns()); |
||||
} |
||||
|
||||
TEST(PatternMatching, TestPatternHasFewerInDataNodes) |
||||
{ |
||||
// Pattern
|
||||
ade::Graph pg; |
||||
{ |
||||
GMat in; |
||||
GMat out = cv::gapi::merge3(in, in, in); |
||||
matching_test::initGModel(pg, cv::GIn(in), cv::GOut(out)); |
||||
} |
||||
|
||||
// Test
|
||||
ade::Graph tg; |
||||
GMat in1, in2, in3; |
||||
GMat out = cv::gapi::merge3(in1, in2, in3); |
||||
matching_test::initGModel(tg, cv::GIn(in1, in2, in3), cv::GOut(out)); |
||||
|
||||
// Pattern Matching
|
||||
cv::gimpl::GModel::Graph pgm(pg); |
||||
cv::gimpl::GModel::Graph tgm(tg); |
||||
cv::gimpl::SubgraphMatch match = cv::gimpl::findMatches(pg, tg); |
||||
|
||||
// Inspecting results:
|
||||
EXPECT_FALSE(match.ok()); |
||||
} |
||||
|
||||
TEST(PatternMatching, TestTwoMatchingsOneCorrect) |
||||
{ |
||||
// Pattern
|
||||
ade::Graph pg; |
||||
{ |
||||
GMat in1, in2; |
||||
GMat n = cv::gapi::bitwise_not(in1); |
||||
GMat e = cv::gapi::erode3x3(in1); |
||||
GMat d = cv::gapi::dilate3x3(in2); |
||||
GMat out = cv::gapi::merge3(n, e, d); |
||||
matching_test::initGModel(pg, cv::GIn(in1, in2), cv::GOut(out)); |
||||
} |
||||
|
||||
// Test
|
||||
ade::Graph tg; |
||||
GMat in1, in2; |
||||
GMat n = cv::gapi::bitwise_not(in1); |
||||
GMat e = cv::gapi::erode3x3(in2); |
||||
GMat d = cv::gapi::dilate3x3(in2); |
||||
GMat mrg = cv::gapi::merge3(n, e, d); |
||||
GMat i, sqi; |
||||
std::tie(i, sqi) = cv::gapi::integral(mrg); |
||||
GMat n1 = cv::gapi::bitwise_not(i); |
||||
GMat e1 = cv::gapi::erode3x3(i); |
||||
GMat d1 = cv::gapi::dilate3x3(sqi); |
||||
GMat out = cv::gapi::merge3(n1, e1, d1); |
||||
matching_test::initGModel(tg, cv::GIn(in1, in2), cv::GOut(out)); |
||||
|
||||
// Pattern Matching
|
||||
cv::gimpl::GModel::Graph pgm(pg); |
||||
cv::gimpl::GModel::Graph tgm(tg); |
||||
cv::gimpl::SubgraphMatch match = cv::gimpl::findMatches(pg, tg); |
||||
|
||||
// Inspecting results:
|
||||
EXPECT_TRUE(match.ok()); |
||||
|
||||
auto nodes = match.nodes(); |
||||
EXPECT_EQ(10u, nodes.size()); |
||||
|
||||
const auto i_nh = cv::gimpl::GModel::dataNodeOf(tgm, i); |
||||
const auto sqi_nh = cv::gimpl::GModel::dataNodeOf(tgm, sqi); |
||||
const auto n1_nh = cv::gimpl::GModel::dataNodeOf(tgm, n1); |
||||
const auto e1_nh = cv::gimpl::GModel::dataNodeOf(tgm, e1); |
||||
const auto d1_nh = cv::gimpl::GModel::dataNodeOf(tgm, d1); |
||||
const auto out_nh = cv::gimpl::GModel::dataNodeOf(tgm, out); |
||||
|
||||
const auto n_op_nh = cv::gimpl::GModel::producerOf(tgm, n1_nh); |
||||
const auto e_op_nh = cv::gimpl::GModel::producerOf(tgm, e1_nh); |
||||
const auto d_op_nh = cv::gimpl::GModel::producerOf(tgm, d1_nh); |
||||
const auto m_op_nh = cv::gimpl::GModel::producerOf(tgm, out_nh); |
||||
|
||||
EXPECT_EQ(matching_test::S({i_nh, sqi_nh, n1_nh, e1_nh, d1_nh, out_nh, |
||||
n_op_nh, e_op_nh, d_op_nh, m_op_nh}), nodes); |
||||
|
||||
EXPECT_EQ(cv::gapi::core::GNot::id(), matching_test::opName(tgm, n_op_nh)); |
||||
EXPECT_EQ(cv::gapi::imgproc::GErode::id(), matching_test::opName(tgm, e_op_nh)); |
||||
EXPECT_EQ(cv::gapi::imgproc::GDilate::id(), matching_test::opName(tgm, d_op_nh)); |
||||
EXPECT_EQ(cv::gapi::core::GMerge3::id(), matching_test::opName(tgm, m_op_nh)); |
||||
|
||||
EXPECT_TRUE(matching_test::isConsumedBy(tgm, i_nh, n_op_nh)); |
||||
EXPECT_TRUE(matching_test::isConsumedBy(tgm, i_nh, e_op_nh)); |
||||
EXPECT_TRUE(matching_test::isConsumedBy(tgm, sqi_nh, d_op_nh)); |
||||
EXPECT_TRUE(matching_test::isConsumedBy(tgm, n1_nh, m_op_nh)); |
||||
EXPECT_TRUE(matching_test::isConsumedBy(tgm, e1_nh, m_op_nh)); |
||||
EXPECT_TRUE(matching_test::isConsumedBy(tgm, d1_nh, m_op_nh)); |
||||
EXPECT_EQ(1u, n1_nh->outEdges().size()); |
||||
EXPECT_EQ(1u, e1_nh->outEdges().size()); |
||||
EXPECT_EQ(1u, d1_nh->outEdges().size()); |
||||
|
||||
EXPECT_EQ(matching_test::S({n_op_nh, e_op_nh, d_op_nh}), match.startOps()); |
||||
EXPECT_EQ(matching_test::S{m_op_nh}, match.finishOps()); |
||||
EXPECT_EQ(matching_test::V({i_nh, sqi_nh}), match.protoIns()); |
||||
EXPECT_EQ(matching_test::V{out_nh}, match.protoOuts());} |
||||
|
||||
TEST(PatternMatching, CheckNoMatch) |
||||
{ |
||||
// Pattern
|
||||
ade::Graph pg; |
||||
{ |
||||
GMat in; |
||||
GMat tmp = cv::gapi::filter2D(in, -1, {}); |
||||
GMat out = cv::gapi::filter2D(tmp, -1, {}); |
||||
matching_test::initGModel(pg, cv::GIn(in), cv::GOut(out)); |
||||
} |
||||
|
||||
// Test
|
||||
ade::Graph tg; |
||||
{ |
||||
GMat in; |
||||
GMat tmp1 = cv::gapi::erode3x3(in); |
||||
GMat out = cv::gapi::dilate3x3(tmp1); |
||||
matching_test::initGModel(tg, cv::GIn(in), cv::GOut(out)); |
||||
} |
||||
|
||||
// Pattern Matching
|
||||
cv::gimpl::GModel::Graph pgm(pg); |
||||
cv::gimpl::GModel::Graph tgm(tg); |
||||
cv::gimpl::SubgraphMatch match = cv::gimpl::findMatches(pg, tg); |
||||
|
||||
// Inspecting results:
|
||||
EXPECT_FALSE(match.ok()); |
||||
} |
||||
|
||||
TEST(PatternMatching, adeSmokeTest) |
||||
{ |
||||
ade::Graph g; |
||||
ade::NodeHandle src = g.createNode(); |
||||
ade::NodeHandle dst = g.createNode(); |
||||
g.link(src, dst); |
||||
g.link(src, dst); |
||||
|
||||
EXPECT_EQ(2u, dst->inNodes().size()); |
||||
} |
||||
|
||||
} // namespace opencv_test
|
Loading…
Reference in new issue