Merge pull request #19731 from rgarnov:rg/basic_frame_drop
This commit is contained in:
@@ -0,0 +1,220 @@
|
||||
// This file is part of OpenCV project.
|
||||
// It is subject to the license terms in the LICENSE file found in the top-level directory
|
||||
// of this distribution and at http://opencv.org/license.html.
|
||||
//
|
||||
// Copyright (C) 2021 Intel Corporation
|
||||
|
||||
#include "../test_precomp.hpp"
|
||||
|
||||
#include <opencv2/gapi/streaming/cap.hpp>
|
||||
#include <opencv2/gapi/core.hpp>
|
||||
#include <opencv2/gapi/fluid/imgproc.hpp>
|
||||
#include <opencv2/gapi/streaming/cap.hpp>
|
||||
#include <opencv2/gapi/streaming/sync.hpp>
|
||||
|
||||
namespace opencv_test {
|
||||
namespace {
|
||||
|
||||
using ts_t = int64_t;
|
||||
using ts_vec = std::vector<ts_t>;
|
||||
using cv::gapi::streaming::sync_policy;
|
||||
|
||||
ts_t calcLeastCommonMultiple(const ts_vec& values) {
|
||||
ts_t res = *std::max_element(values.begin(), values.end());
|
||||
auto isDivisor = [&](ts_t v) { return res % v == 0; };
|
||||
while(!std::all_of(values.begin(), values.end(), isDivisor)) {
|
||||
res++;
|
||||
}
|
||||
return res;
|
||||
}
|
||||
|
||||
struct TimestampGenerationParams {
|
||||
const ts_vec frame_times;
|
||||
sync_policy policy;
|
||||
ts_t end_time;
|
||||
TimestampGenerationParams(const ts_vec& ft, sync_policy sp, ts_t et = 25)
|
||||
: frame_times(ft), policy(sp), end_time(et) {
|
||||
}
|
||||
};
|
||||
|
||||
class MultiFrameSource {
|
||||
class SingleSource : public cv::gapi::wip::IStreamSource {
|
||||
MultiFrameSource& m_source;
|
||||
std::size_t m_idx;
|
||||
public:
|
||||
SingleSource(MultiFrameSource& s, std::size_t idx)
|
||||
: m_source(s)
|
||||
, m_idx(idx)
|
||||
{}
|
||||
virtual bool pull(cv::gapi::wip::Data& data) {
|
||||
return m_source.pull(data, m_idx);
|
||||
}
|
||||
virtual GMetaArg descr_of() const { return GMetaArg{m_source.desc()}; }
|
||||
};
|
||||
|
||||
TimestampGenerationParams p;
|
||||
ts_vec m_current_times;
|
||||
cv::Mat m_mat;
|
||||
|
||||
public:
|
||||
MultiFrameSource(const TimestampGenerationParams& params)
|
||||
: p(params)
|
||||
, m_current_times(p.frame_times.size(), 0u)
|
||||
, m_mat(8, 8, CV_8UC1) {
|
||||
}
|
||||
|
||||
bool pull(cv::gapi::wip::Data& data, std::size_t idx) {
|
||||
cv::randn(m_mat, 127, 32);
|
||||
GAPI_Assert(idx < p.frame_times.size());
|
||||
m_current_times[idx] += p.frame_times[idx];
|
||||
if (m_current_times[idx] >= p.end_time) {
|
||||
return false;
|
||||
}
|
||||
data = m_mat.clone();
|
||||
data.meta[cv::gapi::streaming::meta_tag::timestamp] = m_current_times[idx];
|
||||
return true;
|
||||
}
|
||||
|
||||
cv::gapi::wip::IStreamSource::Ptr getSource(std::size_t idx) {
|
||||
return cv::gapi::wip::IStreamSource::Ptr{new SingleSource(*this, idx)};
|
||||
}
|
||||
|
||||
GMatDesc desc() const { return cv::descr_of(m_mat); }
|
||||
};
|
||||
|
||||
class TimestampChecker {
|
||||
TimestampGenerationParams p;
|
||||
ts_t m_synced_time = 0u;
|
||||
ts_t m_synced_frame_time = 0u;
|
||||
public:
|
||||
TimestampChecker(const TimestampGenerationParams& params)
|
||||
: p(params)
|
||||
, m_synced_frame_time(calcLeastCommonMultiple(p.frame_times)) {
|
||||
}
|
||||
|
||||
void checkNext(const ts_vec& timestamps) {
|
||||
if (p.policy == sync_policy::dont_sync) {
|
||||
// don't check timestamps if the policy is dont_sync
|
||||
return;
|
||||
}
|
||||
m_synced_time += m_synced_frame_time;
|
||||
for (const auto& ts : timestamps) {
|
||||
EXPECT_EQ(m_synced_time, ts);
|
||||
}
|
||||
}
|
||||
|
||||
std::size_t nFrames() const {
|
||||
auto frame_time = p.policy == sync_policy::dont_sync
|
||||
? *std::max_element(p.frame_times.begin(), p.frame_times.end())
|
||||
: m_synced_frame_time;
|
||||
auto n_frames = p.end_time / frame_time;
|
||||
GAPI_Assert(n_frames > 0u);
|
||||
return n_frames;
|
||||
}
|
||||
};
|
||||
|
||||
struct TimestampSyncTest : public ::testing::TestWithParam<sync_policy> {
|
||||
void run(cv::GProtoInputArgs&& ins, cv::GProtoOutputArgs&& outs,
|
||||
const ts_vec& frame_times) {
|
||||
auto video_in_n = frame_times.size();
|
||||
GAPI_Assert(video_in_n <= ins.m_args.size());
|
||||
// Assume that all remaining inputs are const
|
||||
auto const_in_n = ins.m_args.size() - video_in_n;
|
||||
auto out_n = outs.m_args.size();
|
||||
auto policy = GetParam();
|
||||
TimestampGenerationParams ts_params(frame_times, policy);
|
||||
MultiFrameSource source(ts_params);
|
||||
|
||||
GRunArgs gins;
|
||||
for (std::size_t i = 0; i < video_in_n; i++) {
|
||||
gins += cv::gin(source.getSource(i));
|
||||
}
|
||||
auto desc = source.desc();
|
||||
cv::Mat const_mat = cv::Mat::eye(desc.size.height,
|
||||
desc.size.width,
|
||||
CV_MAKE_TYPE(desc.depth, desc.chan));
|
||||
for (std::size_t i = 0; i < const_in_n; i++) {
|
||||
gins += cv::gin(const_mat);
|
||||
}
|
||||
ts_vec out_timestamps(out_n);
|
||||
cv::GRunArgsP gouts{};
|
||||
for (auto& t : out_timestamps) {
|
||||
gouts += cv::gout(t);
|
||||
}
|
||||
|
||||
auto pipe = cv::GComputation(std::move(ins), std::move(outs))
|
||||
.compileStreaming(cv::compile_args(policy));
|
||||
|
||||
pipe.setSource(std::move(gins));
|
||||
pipe.start();
|
||||
|
||||
std::size_t frames = 0u;
|
||||
TimestampChecker checker(ts_params);
|
||||
while(pipe.pull(std::move(gouts))) {
|
||||
checker.checkNext(out_timestamps);
|
||||
frames++;
|
||||
}
|
||||
|
||||
EXPECT_EQ(checker.nFrames(), frames);
|
||||
}
|
||||
};
|
||||
|
||||
} // anonymous namespace
|
||||
|
||||
TEST_P(TimestampSyncTest, Basic)
|
||||
{
|
||||
cv::GMat in1, in2;
|
||||
auto out = cv::gapi::add(in1, in2);
|
||||
auto ts = cv::gapi::streaming::timestamp(out);
|
||||
|
||||
run(cv::GIn(in1, in2), cv::GOut(ts), {1,2});
|
||||
}
|
||||
|
||||
TEST_P(TimestampSyncTest, ThreeInputs)
|
||||
{
|
||||
cv::GMat in1, in2, in3;
|
||||
auto tmp = cv::gapi::add(in1, in2);
|
||||
auto out = cv::gapi::add(tmp, in3);
|
||||
auto ts = cv::gapi::streaming::timestamp(out);
|
||||
|
||||
run(cv::GIn(in1, in2, in3), cv::GOut(ts), {2,4,3});
|
||||
}
|
||||
|
||||
TEST_P(TimestampSyncTest, TwoOutputs)
|
||||
{
|
||||
cv::GMat in1, in2, in3;
|
||||
auto out1 = cv::gapi::add(in1, in3);
|
||||
auto out2 = cv::gapi::add(in2, in3);
|
||||
auto ts1 = cv::gapi::streaming::timestamp(out1);
|
||||
auto ts2 = cv::gapi::streaming::timestamp(out2);
|
||||
|
||||
run(cv::GIn(in1, in2, in3), cv::GOut(ts1, ts2), {1,4,2});
|
||||
}
|
||||
|
||||
TEST_P(TimestampSyncTest, ConstInput)
|
||||
{
|
||||
cv::GMat in1, in2, in3;
|
||||
auto out1 = cv::gapi::add(in1, in3);
|
||||
auto out2 = cv::gapi::add(in2, in3);
|
||||
auto ts1 = cv::gapi::streaming::timestamp(out1);
|
||||
auto ts2 = cv::gapi::streaming::timestamp(out2);
|
||||
|
||||
run(cv::GIn(in1, in2, in3), cv::GOut(ts1, ts2), {1,2});
|
||||
}
|
||||
|
||||
TEST_P(TimestampSyncTest, ChangeSource)
|
||||
{
|
||||
cv::GMat in1, in2, in3;
|
||||
auto out1 = cv::gapi::add(in1, in3);
|
||||
auto out2 = cv::gapi::add(in2, in3);
|
||||
auto ts1 = cv::gapi::streaming::timestamp(out1);
|
||||
auto ts2 = cv::gapi::streaming::timestamp(out2);
|
||||
|
||||
run(cv::GIn(in1, in2, in3), cv::GOut(ts1, ts2), {1,2});
|
||||
run(cv::GIn(in1, in2, in3), cv::GOut(ts1, ts2), {1,2});
|
||||
}
|
||||
|
||||
INSTANTIATE_TEST_CASE_P(InputSynchronization, TimestampSyncTest,
|
||||
Values(sync_policy::dont_sync,
|
||||
sync_policy::drop));
|
||||
} // namespace opencv_test
|
||||
@@ -1104,25 +1104,37 @@ struct GAPI_Streaming_Unit: public ::testing::Test {
|
||||
// FIXME: (GAPI_Streaming_Types, XChangeOpaque) test is missing here!
|
||||
// FIXME: (GAPI_Streaming_Types, OutputOpaque) test is missing here!
|
||||
|
||||
TEST_F(GAPI_Streaming_Unit, TestTwoVideoSourcesFail)
|
||||
TEST(GAPI_Streaming, TestTwoVideosDifferentLength)
|
||||
{
|
||||
auto c_desc = cv::GMatDesc{CV_8U,3,{768,576}};
|
||||
auto m_desc = cv::descr_of(m);
|
||||
auto path = findDataFile("cv/video/768x576.avi");
|
||||
initTestDataPath();
|
||||
auto desc = cv::GMatDesc{CV_8U,3,{768,576}};
|
||||
std::string path1, path2;
|
||||
try {
|
||||
sc = cc.compileStreaming(c_desc, m_desc);
|
||||
// FIXME: it should be EXPECT_NO_THROW()
|
||||
sc.setSource(cv::gin(gapi::wip::make_src<cv::gapi::wip::GCaptureSource>(path), m));
|
||||
sc = cc.compileStreaming(m_desc, c_desc);
|
||||
// FIXME: it should be EXPECT_NO_THROW()
|
||||
sc.setSource(cv::gin(m, gapi::wip::make_src<cv::gapi::wip::GCaptureSource>(path)));
|
||||
path1 = findDataFile("cv/video/768x576.avi");
|
||||
path2 = findDataFile("highgui/video/big_buck_bunny.avi");
|
||||
} catch(...) {
|
||||
throw SkipTestException("Video file can not be opened");
|
||||
throw SkipTestException("Video file can not be found");
|
||||
}
|
||||
|
||||
sc = cc.compileStreaming(c_desc, c_desc);
|
||||
auto c_ptr = gapi::wip::make_src<cv::gapi::wip::GCaptureSource>(path);
|
||||
EXPECT_ANY_THROW(sc.setSource(cv::gin(c_ptr, c_ptr)));
|
||||
cv::GMat in1, in2;
|
||||
auto out = in1 + cv::gapi::resize(in2, desc.size);
|
||||
|
||||
cv::GComputation cc(cv::GIn(in1, in2), cv::GOut(out));
|
||||
auto sc = cc.compileStreaming();
|
||||
|
||||
sc.setSource(cv::gin(gapi::wip::make_src<cv::gapi::wip::GCaptureSource>(path1),
|
||||
gapi::wip::make_src<cv::gapi::wip::GCaptureSource>(path2)));
|
||||
sc.start();
|
||||
|
||||
cv::Mat out_mat;
|
||||
std::size_t frames = 0u;
|
||||
while(sc.pull(cv::gout(out_mat))) {
|
||||
frames++;
|
||||
}
|
||||
|
||||
// big_buck_bunny.avi has 125 frames, 768x576.avi - 100 frames,
|
||||
// expect framework to stop after 100 frames
|
||||
EXPECT_EQ(100u, frames);
|
||||
}
|
||||
|
||||
TEST_F(GAPI_Streaming_Unit, TestStartWithoutnSetSource)
|
||||
|
||||
Reference in New Issue
Block a user