Skip to content

Commit 413c056

Browse files
Merge pull request opencv#19533 from TolyaTalamanov:at/async-requests-hotfix
[G-API] Async infer request hotfix * Fix hanging on empty roi list * Prevent possible data race * Clean up
1 parent 20d5d1c commit 413c056

File tree

2 files changed

+141
-26
lines changed

2 files changed

+141
-26
lines changed

modules/gapi/src/backends/ie/giebackend.cpp

Lines changed: 29 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -293,12 +293,12 @@ class IECallContext
293293
}
294294

295295
// Syntax sugar
296-
cv::GShape inShape(int i) const;
297-
const cv::Mat& inMat(std::size_t input) const;
296+
cv::GShape inShape(std::size_t input) const;
297+
const cv::Mat& inMat (std::size_t input) const;
298298
const cv::MediaFrame& inFrame(std::size_t input) const;
299299

300300
cv::Mat& outMatR(std::size_t idx);
301-
cv::GRunArgP output(int idx);
301+
cv::GRunArgP output (std::size_t idx);
302302

303303
const IEUnit &uu;
304304
cv::gimpl::GIslandExecutable::IOutput &out;
@@ -367,7 +367,7 @@ const cv::GArgs& IECallContext::inArgs() const {
367367
return m_args;
368368
}
369369

370-
cv::GShape IECallContext::inShape(int i) const {
370+
cv::GShape IECallContext::inShape(std::size_t i) const {
371371
return m_in_shapes[i];
372372
}
373373

@@ -383,7 +383,7 @@ cv::Mat& IECallContext::outMatR(std::size_t idx) {
383383
return *cv::util::get<cv::Mat*>(m_results.at(idx));
384384
}
385385

386-
cv::GRunArgP IECallContext::output(int idx) {
386+
cv::GRunArgP IECallContext::output(std::size_t idx) {
387387
return m_output_objs[idx].second;
388388
};
389389

@@ -518,16 +518,17 @@ cv::gimpl::ie::GIEExecutable::GIEExecutable(const ade::Graph &g,
518518
void cv::gimpl::ie::GIEExecutable::run(cv::gimpl::GIslandExecutable::IInput &in,
519519
cv::gimpl::GIslandExecutable::IOutput &out) {
520520
// General alghoritm:
521-
// 1. Get input message from IInput
521+
// 1. Since only single async request is supported
522+
// wait until it is over and start collecting new data.
522523
// 2. Collect island inputs/outputs.
523524
// 3. Create kernel context. (Every kernel has his own context.)
524-
// 4. Since only single async request is supported
525-
// wait until it is over and run kernel.
526-
// (At this point, an asynchronous request will be started.)
527-
// 5. Without waiting for the completion of the asynchronous request
528-
// started by kernel go to the next frame (1)
525+
// 4. Go to the next frame without waiting until the async request is over (1)
529526
//
530-
// 6. If graph is compiled in non-streaming mode, wait until request is over.
527+
// 5. If graph is compiled in non-streaming mode, wait until request is over.
528+
529+
// (1) To prevent data race on the IOutput object, need to wait
530+
// for async request callback, which post outputs and only after that get new data.
531+
m_sync.wait();
531532

532533
std::vector<InObj> input_objs;
533534
std::vector<OutObj> output_objs;
@@ -538,9 +539,6 @@ void cv::gimpl::ie::GIEExecutable::run(cv::gimpl::GIslandExecutable::IInput &in
538539

539540
if (cv::util::holds_alternative<cv::gimpl::EndOfStream>(in_msg))
540541
{
541-
// (1) Since kernel is executing asynchronously
542-
// need to wait until the previous is over
543-
m_sync.wait();
544542
out.post(cv::gimpl::EndOfStream{});
545543
return;
546544
}
@@ -570,9 +568,6 @@ void cv::gimpl::ie::GIEExecutable::run(cv::gimpl::GIslandExecutable::IInput &in
570568
std::move(input_objs), std::move(output_objs));
571569

572570

573-
// (4) Only single async request is supported now,
574-
// so need to wait until the previous is over.
575-
m_sync.wait();
576571
// (5) Run the kernel and start handle next frame.
577572
const auto &kk = giem.metadata(this_nh).get<IECallable>();
578573
// FIXME: Running just a single node now.
@@ -859,6 +854,14 @@ struct InferList: public cv::detail::KernelTag {
859854
// by some resetInternalData(), etc? (Probably at the GExecutor level)
860855
}
861856

857+
// NB: If list of roi is empty need to post output data anyway.
858+
if (in_roi_vec.empty()) {
859+
for (auto i : ade::util::iota(ctx->uu.params.num_out)) {
860+
ctx->out.post(ctx->output(i));
861+
}
862+
return;
863+
}
864+
862865
for (auto&& rc : in_roi_vec) {
863866
// NB: Only single async request is supported now,
864867
// so need to wait until previos iteration is over.
@@ -984,6 +987,14 @@ struct InferList2: public cv::detail::KernelTag {
984987
// by some resetInternalData(), etc? (Probably at the GExecutor level)
985988
}
986989

990+
// NB: If list of roi is empty need to post output data anyway.
991+
if (list_size == 0u) {
992+
for (auto i : ade::util::iota(ctx->uu.params.num_out)) {
993+
ctx->out.post(ctx->output(i));
994+
}
995+
return;
996+
}
997+
987998
for (const auto &list_idx : ade::util::iota(list_size)) {
988999
// NB: Only single async request is supported now,
9891000
// so need to wait until previos iteration is over.

modules/gapi/test/infer/gapi_infer_ie_test.cpp

Lines changed: 112 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -1112,10 +1112,7 @@ TEST(InferList, TestStreamingInfer)
11121112

11131113
// Load IE network, initialize input data using that.
11141114
cv::Mat in_mat;
1115-
std::vector<cv::Mat> ie_ages;
1116-
std::vector<cv::Mat> ie_genders;
1117-
std::vector<cv::Mat> gapi_ages;
1118-
std::vector<cv::Mat> gapi_genders;
1115+
std::vector<cv::Mat> ie_ages, ie_genders, gapi_ages, gapi_genders;
11191116

11201117
std::vector<cv::Rect> roi_list = {
11211118
cv::Rect(cv::Point{64, 60}, cv::Size{ 96, 96}),
@@ -1206,10 +1203,7 @@ TEST(Infer2, TestStreamingInfer)
12061203

12071204
// Load IE network, initialize input data using that.
12081205
cv::Mat in_mat;
1209-
std::vector<cv::Mat> ie_ages;
1210-
std::vector<cv::Mat> ie_genders;
1211-
std::vector<cv::Mat> gapi_ages;
1212-
std::vector<cv::Mat> gapi_genders;
1206+
std::vector<cv::Mat> ie_ages, ie_genders, gapi_ages, gapi_genders;
12131207

12141208
std::vector<cv::Rect> roi_list = {
12151209
cv::Rect(cv::Point{64, 60}, cv::Size{ 96, 96}),
@@ -1286,6 +1280,116 @@ TEST(Infer2, TestStreamingInfer)
12861280
pipeline.stop();
12871281
}
12881282

1283+
TEST(InferEmptyList, TestStreamingInfer)
1284+
{
1285+
initTestDataPath();
1286+
initDLDTDataPath();
1287+
1288+
std::string filepath = findDataFile("cv/video/768x576.avi");
1289+
1290+
cv::gapi::ie::detail::ParamDesc params;
1291+
params.model_path = findDataFile(SUBDIR + "age-gender-recognition-retail-0013.xml");
1292+
params.weights_path = findDataFile(SUBDIR + "age-gender-recognition-retail-0013.bin");
1293+
params.device_id = "CPU";
1294+
1295+
// Load IE network, initialize input data using that.
1296+
cv::Mat in_mat;
1297+
std::vector<cv::Mat> ie_ages, ie_genders, gapi_ages, gapi_genders;
1298+
1299+
// NB: Empty list of roi
1300+
std::vector<cv::Rect> roi_list;
1301+
1302+
using AGInfo = std::tuple<cv::GMat, cv::GMat>;
1303+
G_API_NET(AgeGender, <AGInfo(cv::GMat)>, "test-age-gender");
1304+
1305+
cv::GMat in;
1306+
cv::GArray<cv::Rect> roi;
1307+
cv::GArray<GMat> age, gender;
1308+
1309+
std::tie(age, gender) = cv::gapi::infer<AgeGender>(roi, in);
1310+
cv::GComputation comp(cv::GIn(in, roi), cv::GOut(age, gender));
1311+
1312+
auto pp = cv::gapi::ie::Params<AgeGender> {
1313+
params.model_path, params.weights_path, params.device_id
1314+
}.cfgOutputLayers({ "age_conv3", "prob" });
1315+
1316+
1317+
std::size_t num_frames = 0u;
1318+
std::size_t max_frames = 1u;
1319+
1320+
cv::VideoCapture cap;
1321+
cap.open(filepath);
1322+
if (!cap.isOpened())
1323+
throw SkipTestException("Video file can not be opened");
1324+
1325+
cap >> in_mat;
1326+
auto pipeline = comp.compileStreaming(cv::compile_args(cv::gapi::networks(pp)));
1327+
pipeline.setSource(
1328+
cv::gin(cv::gapi::wip::make_src<cv::gapi::wip::GCaptureSource>(filepath), roi_list));
1329+
1330+
pipeline.start();
1331+
while (num_frames < max_frames && pipeline.pull(cv::gout(gapi_ages, gapi_genders)))
1332+
{
1333+
EXPECT_TRUE(gapi_ages.empty());
1334+
EXPECT_TRUE(gapi_genders.empty());
1335+
}
1336+
}
1337+
1338+
TEST(Infer2EmptyList, TestStreamingInfer)
1339+
{
1340+
initTestDataPath();
1341+
initDLDTDataPath();
1342+
1343+
std::string filepath = findDataFile("cv/video/768x576.avi");
1344+
1345+
cv::gapi::ie::detail::ParamDesc params;
1346+
params.model_path = findDataFile(SUBDIR + "age-gender-recognition-retail-0013.xml");
1347+
params.weights_path = findDataFile(SUBDIR + "age-gender-recognition-retail-0013.bin");
1348+
params.device_id = "CPU";
1349+
1350+
// Load IE network, initialize input data using that.
1351+
cv::Mat in_mat;
1352+
std::vector<cv::Mat> ie_ages, ie_genders, gapi_ages, gapi_genders;
1353+
1354+
// NB: Empty list of roi
1355+
std::vector<cv::Rect> roi_list;
1356+
1357+
using AGInfo = std::tuple<cv::GMat, cv::GMat>;
1358+
G_API_NET(AgeGender, <AGInfo(cv::GMat)>, "test-age-gender");
1359+
1360+
cv::GArray<cv::Rect> rr;
1361+
cv::GMat in;
1362+
cv::GArray<cv::GMat> age, gender;
1363+
std::tie(age, gender) = cv::gapi::infer2<AgeGender>(in, rr);
1364+
1365+
cv::GComputation comp(cv::GIn(in, rr), cv::GOut(age, gender));
1366+
1367+
auto pp = cv::gapi::ie::Params<AgeGender> {
1368+
params.model_path, params.weights_path, params.device_id
1369+
}.cfgOutputLayers({ "age_conv3", "prob" });
1370+
1371+
1372+
std::size_t num_frames = 0u;
1373+
std::size_t max_frames = 1u;
1374+
1375+
cv::VideoCapture cap;
1376+
cap.open(filepath);
1377+
if (!cap.isOpened())
1378+
throw SkipTestException("Video file can not be opened");
1379+
1380+
cap >> in_mat;
1381+
auto pipeline = comp.compileStreaming(cv::compile_args(cv::gapi::networks(pp)));
1382+
pipeline.setSource(
1383+
cv::gin(cv::gapi::wip::make_src<cv::gapi::wip::GCaptureSource>(filepath), roi_list));
1384+
1385+
pipeline.start();
1386+
while (num_frames < max_frames && pipeline.pull(cv::gout(gapi_ages, gapi_genders)))
1387+
{
1388+
EXPECT_TRUE(gapi_ages.empty());
1389+
EXPECT_TRUE(gapi_genders.empty());
1390+
}
1391+
}
1392+
12891393
} // namespace opencv_test
12901394

12911395
#endif // HAVE_INF_ENGINE

0 commit comments

Comments
 (0)