1
0
mirror of https://github.com/opencv/opencv.git synced 2026-07-30 07:43:03 +04:00

Merge pull request #17502 from dmatveev:dm/infer2

* G-API: Introduce a new gapi::infer2 overload + gaze estimation sample

* G-API/infer2: Introduced static type checking for infer2

- Also added extra tests on the type check routine

* G-API/infer2: Addressed self-review comments in the sample app

- Also fix build on Linux;

* G-API/infer2: Remove incorrect SetLayout(HWC) + dead code

- Also fixed comments in the backend

* G-API/infer2: Continue with self-review

- Fix warnings/compile errors in gaze estimation
- Dropped the use of RTTI (VectorRef::holds()) from the giebackend
- Replaced it with a trait-based enums for GArray<T> and std::vector<T>
- The enums and traits are temporary and need to be unified with
  the S11N when it comes

* G-API/infer2: Final self-review items

- Refactored ROIList test to cover 70% for infer<> and infer2<>;
- Fixed the model data discovery routine to be compatible with new
  OpenVINO;
- Hopefully fixed the final issues (warnings) with the sample.

* G-API/infer2: address review problems

- Fixed typo in comments;
- Fixed public (Doxygen) comment on GArray<GMat> input case for infer2;
- Made model lookup more flexible to allow new & old OMZ dir layouts.

* G-API/infer2: Change the model paths again

* G-API/infer2: Change the lookup path for test data

* G-API/infer2: use randu instead of imread. CI war is over
This commit is contained in:
Dmitry Matveev
2020-07-14 11:06:49 +03:00
committed by GitHub
parent e5e767abc1
commit f0c411d8b5
14 changed files with 1066 additions and 85 deletions
+15
View File
@@ -187,4 +187,19 @@ TEST(GArray_VectorRef, TestMov)
EXPECT_EQ(V{}, vref.rref<I>());
EXPECT_EQ(V{}, vtest);
}
TEST(GArray_VectorRef, Spec)
{
cv::detail::VectorRef v1(std::vector<cv::Rect>{});
EXPECT_EQ(cv::detail::TypeSpec::RECT, v1.spec());
cv::detail::VectorRef v2(std::vector<cv::Mat>{});
EXPECT_EQ(cv::detail::TypeSpec::MAT, v2.spec());
cv::detail::VectorRef v3(std::vector<int>{});
EXPECT_EQ(cv::detail::TypeSpec::OPAQUE_SPEC, v3.spec());
cv::detail::VectorRef v4(std::vector<std::string>{});
EXPECT_EQ(cv::detail::TypeSpec::OPAQUE_SPEC, v4.spec());
}
} // namespace opencv_test
+135 -72
View File
@@ -75,6 +75,44 @@ void normAssert(cv::InputArray ref, cv::InputArray test,
EXPECT_LE(normInf, lInf) << comment;
}
std::vector<std::string> modelPathByName(const std::string &model_name) {
// Handle OMZ model layout changes among OpenVINO versions here
static const std::unordered_multimap<std::string, std::string> map = {
{"age-gender-recognition-retail-0013",
"2020.3.0/intel/age-gender-recognition-retail-0013/FP32"},
{"age-gender-recognition-retail-0013",
"Retail/object_attributes/age_gender/dldt"},
};
const auto range = map.equal_range(model_name);
std::vector<std::string> result;
for (auto it = range.first; it != range.second; ++it) {
result.emplace_back(it->second);
}
return result;
}
std::tuple<std::string, std::string> findModel(const std::string &model_name) {
const auto candidates = modelPathByName(model_name);
CV_Assert(!candidates.empty() && "No model path candidates found at all");
for (auto &&path : candidates) {
std::string model_xml, model_bin;
try {
model_xml = findDataFile(path + "/" + model_name + ".xml", false);
model_bin = findDataFile(path + "/" + model_name + ".bin", false);
// Return the first file which actually works
return std::make_tuple(model_xml, model_bin);
} catch (SkipTestException&) {
// This is quite ugly but it is a way for OpenCV to let us know
// this file wasn't found.
continue;
}
}
// Default behavior if reached here.
throw SkipTestException("Files for " + model_name + " were not found");
}
} // anonymous namespace
// TODO: Probably DNN/IE part can be further parametrized with a template
@@ -83,9 +121,8 @@ TEST(TestAgeGenderIE, InferBasicTensor)
{
initDLDTDataPath();
const std::string path = "Retail/object_attributes/age_gender/dldt/age-gender-recognition-retail-0013";
const auto topology_path = findDataFile(path + ".xml", false);
const auto weights_path = findDataFile(path + ".bin", false);
std::string topology_path, weights_path;
std::tie(topology_path, weights_path) = findModel("age-gender-recognition-retail-0013");
// Load IE network, initialize input data using that.
namespace IE = InferenceEngine;
@@ -138,9 +175,8 @@ TEST(TestAgeGenderIE, InferBasicImage)
{
initDLDTDataPath();
const std::string path = "Retail/object_attributes/age_gender/dldt/age-gender-recognition-retail-0013";
const auto topology_path = findDataFile(path + ".xml", false);
const auto weights_path = findDataFile(path + ".bin", false);
std::string topology_path, weights_path;
std::tie(topology_path, weights_path) = findModel("age-gender-recognition-retail-0013");
// FIXME: Ideally it should be an image from disk
// cv::Mat in_mat = cv::imread(findDataFile("grace_hopper_227.png"));
@@ -159,7 +195,6 @@ TEST(TestAgeGenderIE, InferBasicImage)
auto net = reader.getNetwork();
auto &ii = net.getInputsInfo().at("data");
ii->setPrecision(IE::Precision::U8);
ii->setLayout(IE::Layout::NHWC);
ii->getPreProcess().setResizeAlgorithm(IE::RESIZE_BILINEAR);
auto plugin = IE::PluginDispatcher().getPluginByDevice("CPU");
@@ -192,65 +227,86 @@ TEST(TestAgeGenderIE, InferBasicImage)
normAssert(cv::gapi::ie::util::to_ocv(ie_gender), gapi_gender, "Test gender output");
}
TEST(TestAgeGenderIE, InferROIList)
{
initDLDTDataPath();
struct ROIList: public ::testing::Test {
std::string m_model_path;
std::string m_weights_path;
const std::string path = "Retail/object_attributes/age_gender/dldt/age-gender-recognition-retail-0013";
const auto topology_path = findDataFile(path + ".xml", false);
const auto weights_path = findDataFile(path + ".bin", false);
cv::Mat m_in_mat;
std::vector<cv::Rect> m_roi_list;
// FIXME: Ideally it should be an image from disk
// cv::Mat in_mat = cv::imread(findDataFile("grace_hopper_227.png"));
cv::Mat in_mat(cv::Size(640, 480), CV_8UC3);
cv::randu(in_mat, 0, 255);
std::vector<cv::Mat> m_out_ie_ages;
std::vector<cv::Mat> m_out_ie_genders;
std::vector<cv::Rect> rois = {
cv::Rect(cv::Point{ 0, 0}, cv::Size{80, 120}),
cv::Rect(cv::Point{50, 100}, cv::Size{96, 160}),
};
std::vector<cv::Mat> m_out_gapi_ages;
std::vector<cv::Mat> m_out_gapi_genders;
std::vector<cv::Mat> gapi_age, gapi_gender;
// Load & run IE network
namespace IE = InferenceEngine;
std::vector<cv::Mat> ie_age, ie_gender;
{
IE::CNNNetReader reader;
reader.ReadNetwork(topology_path);
reader.ReadWeights(weights_path);
auto net = reader.getNetwork();
auto &ii = net.getInputsInfo().at("data");
ii->setPrecision(IE::Precision::U8);
ii->setLayout(IE::Layout::NHWC);
ii->getPreProcess().setResizeAlgorithm(IE::RESIZE_BILINEAR);
auto plugin = IE::PluginDispatcher().getPluginByDevice("CPU");
auto plugin_net = plugin.LoadNetwork(net, {});
auto infer_request = plugin_net.CreateInferRequest();
auto frame_blob = cv::gapi::ie::util::to_ie(in_mat);
for (auto &&rc : rois) {
const auto ie_rc = IE::ROI {
0u
, static_cast<std::size_t>(rc.x)
, static_cast<std::size_t>(rc.y)
, static_cast<std::size_t>(rc.width)
, static_cast<std::size_t>(rc.height)
};
infer_request.SetBlob("data", IE::make_shared_blob(frame_blob, ie_rc));
infer_request.Infer();
using namespace cv::gapi::ie::util;
ie_age.push_back(to_ocv(infer_request.GetBlob("age_conv3")).clone());
ie_gender.push_back(to_ocv(infer_request.GetBlob("prob")).clone());
}
}
// Configure & run G-API
using AGInfo = std::tuple<cv::GMat, cv::GMat>;
G_API_NET(AgeGender, <AGInfo(cv::GMat)>, "test-age-gender");
ROIList() {
initDLDTDataPath();
std::tie(m_model_path, m_weights_path) = findModel("age-gender-recognition-retail-0013");
// FIXME: it must be cv::imread(findDataFile("../dnn/grace_hopper_227.png", false));
m_in_mat = cv::Mat(cv::Size(320, 240), CV_8UC3);
cv::randu(m_in_mat, 0, 255);
// both ROIs point to the same face, with a slightly changed geometry
m_roi_list = {
cv::Rect(cv::Point{64, 60}, cv::Size{ 96, 96}),
cv::Rect(cv::Point{50, 32}, cv::Size{128, 160}),
};
// Load & run IE network
namespace IE = InferenceEngine;
{
IE::CNNNetReader reader;
reader.ReadNetwork(m_model_path);
reader.ReadWeights(m_weights_path);
auto net = reader.getNetwork();
auto &ii = net.getInputsInfo().at("data");
ii->setPrecision(IE::Precision::U8);
ii->getPreProcess().setResizeAlgorithm(IE::RESIZE_BILINEAR);
auto plugin = IE::PluginDispatcher().getPluginByDevice("CPU");
auto plugin_net = plugin.LoadNetwork(net, {});
auto infer_request = plugin_net.CreateInferRequest();
auto frame_blob = cv::gapi::ie::util::to_ie(m_in_mat);
for (auto &&rc : m_roi_list) {
const auto ie_rc = IE::ROI {
0u
, static_cast<std::size_t>(rc.x)
, static_cast<std::size_t>(rc.y)
, static_cast<std::size_t>(rc.width)
, static_cast<std::size_t>(rc.height)
};
infer_request.SetBlob("data", IE::make_shared_blob(frame_blob, ie_rc));
infer_request.Infer();
using namespace cv::gapi::ie::util;
m_out_ie_ages.push_back(to_ocv(infer_request.GetBlob("age_conv3")).clone());
m_out_ie_genders.push_back(to_ocv(infer_request.GetBlob("prob")).clone());
}
} // namespace IE = ..
} // ROIList()
void validate() {
// Validate with IE itself (avoid DNN module dependency here)
ASSERT_EQ(2u, m_out_ie_ages.size());
ASSERT_EQ(2u, m_out_ie_genders.size());
ASSERT_EQ(2u, m_out_gapi_ages.size());
ASSERT_EQ(2u, m_out_gapi_genders.size());
normAssert(m_out_ie_ages [0], m_out_gapi_ages [0], "0: Test age output");
normAssert(m_out_ie_genders[0], m_out_gapi_genders[0], "0: Test gender output");
normAssert(m_out_ie_ages [1], m_out_gapi_ages [1], "1: Test age output");
normAssert(m_out_ie_genders[1], m_out_gapi_genders[1], "1: Test gender output");
}
}; // ROIList
TEST_F(ROIList, TestInfer)
{
cv::GArray<cv::Rect> rr;
cv::GMat in;
cv::GArray<cv::GMat> age, gender;
@@ -258,23 +314,30 @@ TEST(TestAgeGenderIE, InferROIList)
cv::GComputation comp(cv::GIn(in, rr), cv::GOut(age, gender));
auto pp = cv::gapi::ie::Params<AgeGender> {
topology_path, weights_path, "CPU"
m_model_path, m_weights_path, "CPU"
}.cfgOutputLayers({ "age_conv3", "prob" });
comp.apply(cv::gin(in_mat, rois), cv::gout(gapi_age, gapi_gender),
comp.apply(cv::gin(m_in_mat, m_roi_list),
cv::gout(m_out_gapi_ages, m_out_gapi_genders),
cv::compile_args(cv::gapi::networks(pp)));
// Validate with IE itself (avoid DNN module dependency here)
ASSERT_EQ(2u, ie_age.size() );
ASSERT_EQ(2u, ie_gender.size());
ASSERT_EQ(2u, gapi_age.size() );
ASSERT_EQ(2u, gapi_gender.size());
normAssert(ie_age [0], gapi_age [0], "0: Test age output");
normAssert(ie_gender[0], gapi_gender[0], "0: Test gender output");
normAssert(ie_age [1], gapi_age [1], "1: Test age output");
normAssert(ie_gender[1], gapi_gender[1], "1: Test gender output");
validate();
}
TEST_F(ROIList, TestInfer2)
{
cv::GArray<cv::Rect> rr;
cv::GMat in;
cv::GArray<cv::GMat> age, gender;
std::tie(age, gender) = cv::gapi::infer2<AgeGender>(in, rr);
cv::GComputation comp(cv::GIn(in, rr), cv::GOut(age, gender));
auto pp = cv::gapi::ie::Params<AgeGender> {
m_model_path, m_weights_path, "CPU"
}.cfgOutputLayers({ "age_conv3", "prob" });
comp.apply(cv::gin(m_in_mat, m_roi_list),
cv::gout(m_out_gapi_ages, m_out_gapi_genders),
cv::compile_args(cv::gapi::networks(pp)));
validate();
}
} // namespace opencv_test
@@ -0,0 +1,79 @@
// 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) 2020 Intel Corporation
#include "../test_precomp.hpp"
// These tests verify some parts of cv::gapi::infer<> API
// regardless of the backend used
namespace opencv_test {
namespace {
template<class A, class B> using Check = cv::detail::valid_infer2_types<A, B>;
TEST(Infer, ValidInfer2Types)
{
// Compiled == passed!
// Argument block 1
static_assert(Check< std::tuple<cv::GMat> // Net
, std::tuple<cv::GMat> > // Call
::value == true, "Must work");
static_assert(Check< std::tuple<cv::GMat, cv::GMat> // Net
, std::tuple<cv::GMat, cv::GMat> > // Call
::value == true, "Must work");
// Argument block 2
static_assert(Check< std::tuple<cv::GMat> // Net
, std::tuple<cv::Rect> > // Call
::value == true, "Must work");
static_assert(Check< std::tuple<cv::GMat, cv::GMat> // Net
, std::tuple<cv::Rect, cv::Rect> > // Call
::value == true, "Must work");
// Argument block 3 (mixed cases)
static_assert(Check< std::tuple<cv::GMat, cv::GMat> // Net
, std::tuple<cv::GMat, cv::Rect> > // Call
::value == true, "Must work");
static_assert(Check< std::tuple<cv::GMat, cv::GMat> // Net
, std::tuple<cv::Rect, cv::GMat> > // Call
::value == true, "Must work");
// Argument block 4 (super-mixed)
static_assert(Check< std::tuple<cv::GMat, cv::GMat, cv::GMat> // Net
, std::tuple<cv::Rect, cv::GMat, cv::Rect> > // Call
::value == true, "Must work");
// Argument block 5 (mainly negative)
static_assert(Check< std::tuple<cv::GMat> // Net
, std::tuple<int> > // Call
::value == false, "This type(s) shouldn't pass");
static_assert(Check< std::tuple<cv::GMat, cv::GMat> // Net
, std::tuple<int, cv::Rect> > // Call
::value == false, "This type(s) shouldn't pass");
static_assert(Check< std::tuple<cv::GMat, cv::GMat> // Net
, std::tuple<cv::Rect, cv::Point> >// Call
::value == false, "This type(s) shouldn't pass");
// Argument block 5 (wrong args length)
static_assert(Check< std::tuple<cv::GMat, cv::GMat> // Net
, std::tuple<cv::GMat> > // Call
::value == false, "Should fail -- not enough args");
static_assert(Check< std::tuple<cv::GMat, cv::GMat> // Net
, std::tuple<cv::Rect> > // Call
::value == false, "Should fail -- not enough args");
static_assert(Check< std::tuple<cv::GMat, cv::GMat> // Net
, std::tuple<cv::Rect, cv::Rect, cv::GMat> > // Call
::value == false, "Should fail -- too much args");
}
} // anonymous namespace
} // namespace opencv_test
@@ -8,7 +8,7 @@
#include "../test_precomp.hpp"
namespace opencv_test {
// Tests on T/Kind matching ////////////////////////////////////////////////////
// Tests on T/Spec/Kind matching ///////////////////////////////////////////////
// {{
template<class T, cv::detail::ArgKind Exp>
@@ -76,6 +76,60 @@ TYPED_TEST(GArgKind, RValue)
EXPECT_EQ(TestFixture::Kind, arg.kind);
}
// Repeat the same for Spec
template<class T, cv::detail::ArgSpec Exp>
struct ExpectedS
{
using type = T;
static const constexpr cv::detail::ArgSpec spec = Exp;
};
template<typename T>
struct ArgSpec: public ::testing::Test
{
using Type = typename T::type;
const cv::detail::ArgSpec Spec = T::spec;
};
using Arg_Spec_Types = ::testing::Types
<
// G-API types
ExpectedS<cv::GMat, cv::detail::ArgSpec::OPAQUE_SPEC>
, ExpectedS<cv::GMatP, cv::detail::ArgSpec::OPAQUE_SPEC>
, ExpectedS<cv::GFrame, cv::detail::ArgSpec::OPAQUE_SPEC>
, ExpectedS<cv::GScalar, cv::detail::ArgSpec::OPAQUE_SPEC>
, ExpectedS<cv::GArray<int>, cv::detail::ArgSpec::OPAQUE_SPEC>
, ExpectedS<cv::GArray<float>, cv::detail::ArgSpec::OPAQUE_SPEC>
, ExpectedS<cv::GArray<cv::Point>, cv::detail::ArgSpec::OPAQUE_SPEC>
, ExpectedS<cv::GArray<cv::Rect>, cv::detail::ArgSpec::RECT>
, ExpectedS<cv::GArray<cv::GMat>, cv::detail::ArgSpec::GMAT>
, ExpectedS<cv::GOpaque<int>, cv::detail::ArgSpec::OPAQUE_SPEC>
, ExpectedS<cv::GOpaque<float>, cv::detail::ArgSpec::OPAQUE_SPEC>
, ExpectedS<cv::GOpaque<cv::Point>, cv::detail::ArgSpec::OPAQUE_SPEC>
, ExpectedS<cv::GOpaque<cv::Rect>, cv::detail::ArgSpec::RECT>
// FIXME: causes internal conflicts in GOpaque/descr_of
// , ExpectedS<cv::GOpaque<cv::Mat>, cv::detail::ArgSpec::GMAT>
// Built-in types
, ExpectedS<int, cv::detail::ArgSpec::OPAQUE_SPEC>
, ExpectedS<float, cv::detail::ArgSpec::OPAQUE_SPEC>
, ExpectedS<int*, cv::detail::ArgSpec::OPAQUE_SPEC>
, ExpectedS<cv::Point, cv::detail::ArgSpec::OPAQUE_SPEC>
, ExpectedS<std::string, cv::detail::ArgSpec::OPAQUE_SPEC>
, ExpectedS<cv::Mat, cv::detail::ArgSpec::OPAQUE_SPEC>
, ExpectedS<std::vector<int>, cv::detail::ArgSpec::OPAQUE_SPEC>
, ExpectedS<std::vector<cv::Point>, cv::detail::ArgSpec::OPAQUE_SPEC>
>;
TYPED_TEST_CASE(ArgSpec, Arg_Spec_Types);
TYPED_TEST(ArgSpec, Basic)
{
const auto this_spec = cv::detail::GTypeTraits<typename TestFixture::Type>::spec;
EXPECT_EQ(TestFixture::Spec, this_spec);
}
// }}
////////////////////////////////////////////////////////////////////////////////
@@ -21,14 +21,23 @@ namespace test
namespace
{
namespace D = cv::detail;
cv::GMat unaryOp(cv::GMat m)
{
return cv::GCall(cv::GKernel{"gapi.test.unaryop", "", nullptr, { GShape::GMAT } }).pass(m).yield(0);
return cv::GCall(cv::GKernel{ "gapi.test.unaryop"
, ""
, nullptr
, { D::ArgSpec::OPAQUE_SPEC }
, { GShape::GMAT } }).pass(m).yield(0);
}
cv::GMat binaryOp(cv::GMat m1, cv::GMat m2)
{
return cv::GCall(cv::GKernel{"gapi.test.binaryOp", "", nullptr, { GShape::GMAT } }).pass(m1, m2).yield(0);
return cv::GCall(cv::GKernel{ "gapi.test.binaryOp"
, ""
, nullptr
, { D::ArgSpec::OPAQUE_SPEC, D::ArgSpec::OPAQUE_SPEC }
, { GShape::GMAT } }).pass(m1, m2).yield(0);
}
std::vector<ade::NodeHandle> collectOperations(const cv::gimpl::GModel::Graph& gr)