1 // This file is part of OpenCV project.
2 // It is subject to the license terms in the LICENSE file found in the top-level directory
3 // of this distribution and at http://opencv.org/license.html.
4 //
5 // Copyright (C) 2021 Intel Corporation
6 
7 #include "../test_precomp.hpp"
8 
9 #include <opencv2/gapi/streaming/cap.hpp>
10 #include <opencv2/gapi/core.hpp>
11 #include <opencv2/gapi/fluid/imgproc.hpp>
12 #include <opencv2/gapi/streaming/cap.hpp>
13 #include <opencv2/gapi/streaming/sync.hpp>
14 
15 namespace opencv_test {
16 namespace {
17 
18 using ts_t = int64_t;
19 using ts_vec = std::vector<ts_t>;
20 using cv::gapi::streaming::sync_policy;
21 
calcLeastCommonMultiple(const ts_vec & values)22 ts_t calcLeastCommonMultiple(const ts_vec& values) {
23     ts_t res = *std::max_element(values.begin(), values.end());
24     auto isDivisor = [&](ts_t v) { return res % v == 0; };
25     while(!std::all_of(values.begin(), values.end(), isDivisor)) {
26         res++;
27     }
28     return res;
29 }
30 
31 struct TimestampGenerationParams {
32     const ts_vec frame_times;
33     sync_policy policy;
34     ts_t end_time;
TimestampGenerationParamsopencv_test::__anon4d6f978c0111::TimestampGenerationParams35     TimestampGenerationParams(const ts_vec& ft, sync_policy sp, ts_t et = 25)
36         : frame_times(ft), policy(sp), end_time(et) {
37     }
38 };
39 
40 class MultiFrameSource {
41     class SingleSource : public cv::gapi::wip::IStreamSource {
42         MultiFrameSource& m_source;
43         std::size_t m_idx;
44     public:
SingleSource(MultiFrameSource & s,std::size_t idx)45         SingleSource(MultiFrameSource& s, std::size_t idx)
46             : m_source(s)
47             , m_idx(idx)
48         {}
pull(cv::gapi::wip::Data & data)49         virtual bool pull(cv::gapi::wip::Data& data) {
50             return m_source.pull(data, m_idx);
51         }
descr_of() const52         virtual GMetaArg descr_of() const { return GMetaArg{m_source.desc()}; }
53     };
54 
55     TimestampGenerationParams p;
56     ts_vec m_current_times;
57     cv::Mat m_mat;
58 
59 public:
MultiFrameSource(const TimestampGenerationParams & params)60     MultiFrameSource(const TimestampGenerationParams& params)
61         : p(params)
62         , m_current_times(p.frame_times.size(), 0u)
63         , m_mat(8, 8, CV_8UC1) {
64     }
65 
pull(cv::gapi::wip::Data & data,std::size_t idx)66     bool pull(cv::gapi::wip::Data& data, std::size_t idx) {
67         cv::randn(m_mat, 127, 32);
68         GAPI_Assert(idx < p.frame_times.size());
69         m_current_times[idx] += p.frame_times[idx];
70         if (m_current_times[idx] >= p.end_time) {
71             return false;
72         }
73         data = m_mat.clone();
74         data.meta[cv::gapi::streaming::meta_tag::timestamp] = m_current_times[idx];
75         return true;
76     }
77 
getSource(std::size_t idx)78     cv::gapi::wip::IStreamSource::Ptr getSource(std::size_t idx) {
79         return cv::gapi::wip::IStreamSource::Ptr{new SingleSource(*this, idx)};
80     }
81 
desc() const82     GMatDesc desc() const { return cv::descr_of(m_mat); }
83 };
84 
85 class TimestampChecker {
86     TimestampGenerationParams p;
87     ts_t m_synced_time = 0u;
88     ts_t m_synced_frame_time = 0u;
89 public:
TimestampChecker(const TimestampGenerationParams & params)90     TimestampChecker(const TimestampGenerationParams& params)
91         : p(params)
92         , m_synced_frame_time(calcLeastCommonMultiple(p.frame_times)) {
93     }
94 
checkNext(const ts_vec & timestamps)95     void checkNext(const ts_vec& timestamps) {
96         if (p.policy == sync_policy::dont_sync) {
97             // don't check timestamps if the policy is dont_sync
98             return;
99         }
100         m_synced_time += m_synced_frame_time;
101         for (const auto& ts : timestamps) {
102             EXPECT_EQ(m_synced_time, ts);
103         }
104     }
105 
nFrames() const106     std::size_t nFrames() const {
107         auto frame_time = p.policy == sync_policy::dont_sync
108                           ? *std::max_element(p.frame_times.begin(), p.frame_times.end())
109                           : m_synced_frame_time;
110         auto n_frames = p.end_time / frame_time;
111         GAPI_Assert(n_frames > 0u);
112         return (std::size_t)n_frames;
113     }
114 };
115 
116 struct TimestampSyncTest : public ::testing::TestWithParam<sync_policy> {
runopencv_test::__anon4d6f978c0111::TimestampSyncTest117     void run(cv::GProtoInputArgs&& ins, cv::GProtoOutputArgs&& outs,
118              const ts_vec& frame_times) {
119         auto video_in_n = frame_times.size();
120         GAPI_Assert(video_in_n <= ins.m_args.size());
121         // Assume that all remaining inputs are const
122         auto const_in_n = ins.m_args.size() - video_in_n;
123         auto out_n = outs.m_args.size();
124         auto policy = GetParam();
125         TimestampGenerationParams ts_params(frame_times, policy);
126         MultiFrameSource source(ts_params);
127 
128         GRunArgs gins;
129         for (std::size_t i = 0; i < video_in_n; i++) {
130             gins += cv::gin(source.getSource(i));
131         }
132         auto desc = source.desc();
133         cv::Mat const_mat = cv::Mat::eye(desc.size.height,
134                                          desc.size.width,
135                                          CV_MAKE_TYPE(desc.depth, desc.chan));
136         for (std::size_t i = 0; i < const_in_n; i++) {
137             gins += cv::gin(const_mat);
138         }
139         ts_vec out_timestamps(out_n);
140         cv::GRunArgsP gouts{};
141         for (auto& t : out_timestamps) {
142             gouts += cv::gout(t);
143         }
144 
145         auto pipe = cv::GComputation(std::move(ins), std::move(outs))
146                     .compileStreaming(cv::compile_args(policy));
147 
148         pipe.setSource(std::move(gins));
149         pipe.start();
150 
151         std::size_t frames = 0u;
152         TimestampChecker checker(ts_params);
153         while(pipe.pull(std::move(gouts))) {
154             checker.checkNext(out_timestamps);
155             frames++;
156         }
157 
158         EXPECT_EQ(checker.nFrames(), frames);
159     }
160 };
161 
162 } // anonymous namespace
163 
TEST_P(TimestampSyncTest,Basic)164 TEST_P(TimestampSyncTest, Basic)
165 {
166     cv::GMat in1, in2;
167     auto out = cv::gapi::add(in1, in2);
168     auto ts = cv::gapi::streaming::timestamp(out);
169 
170     run(cv::GIn(in1, in2), cv::GOut(ts), {1,2});
171 }
172 
TEST_P(TimestampSyncTest,ThreeInputs)173 TEST_P(TimestampSyncTest, ThreeInputs)
174 {
175     cv::GMat in1, in2, in3;
176     auto tmp = cv::gapi::add(in1, in2);
177     auto out = cv::gapi::add(tmp, in3);
178     auto ts = cv::gapi::streaming::timestamp(out);
179 
180     run(cv::GIn(in1, in2, in3), cv::GOut(ts), {2,4,3});
181 }
182 
TEST_P(TimestampSyncTest,TwoOutputs)183 TEST_P(TimestampSyncTest, TwoOutputs)
184 {
185     cv::GMat in1, in2, in3;
186     auto out1 = cv::gapi::add(in1, in3);
187     auto out2 = cv::gapi::add(in2, in3);
188     auto ts1 = cv::gapi::streaming::timestamp(out1);
189     auto ts2 = cv::gapi::streaming::timestamp(out2);
190 
191     run(cv::GIn(in1, in2, in3), cv::GOut(ts1, ts2), {1,4,2});
192 }
193 
TEST_P(TimestampSyncTest,ConstInput)194 TEST_P(TimestampSyncTest, ConstInput)
195 {
196     cv::GMat in1, in2, in3;
197     auto out1 = cv::gapi::add(in1, in3);
198     auto out2 = cv::gapi::add(in2, in3);
199     auto ts1 = cv::gapi::streaming::timestamp(out1);
200     auto ts2 = cv::gapi::streaming::timestamp(out2);
201 
202     run(cv::GIn(in1, in2, in3), cv::GOut(ts1, ts2), {1,2});
203 }
204 
TEST_P(TimestampSyncTest,ChangeSource)205 TEST_P(TimestampSyncTest, ChangeSource)
206 {
207     cv::GMat in1, in2, in3;
208     auto out1 = cv::gapi::add(in1, in3);
209     auto out2 = cv::gapi::add(in2, in3);
210     auto ts1 = cv::gapi::streaming::timestamp(out1);
211     auto ts2 = cv::gapi::streaming::timestamp(out2);
212 
213     run(cv::GIn(in1, in2, in3), cv::GOut(ts1, ts2), {1,2});
214     run(cv::GIn(in1, in2, in3), cv::GOut(ts1, ts2), {1,2});
215 }
216 
217 INSTANTIATE_TEST_CASE_P(InputSynchronization, TimestampSyncTest,
218                         Values(sync_policy::dont_sync,
219                                sync_policy::drop));
220 } // namespace opencv_test
221