#include "api/gbackend_priv.hpp" // FIXME: Make it part of Backend SDK!
#include "utils/itt.hpp"
+#include "logger.hpp"
// FIXME: Is there a way to take a typed graph (our GModel),
// and create a new typed graph _ATOP_ of that (by extending with a couple of
}
}
makeReshape();
- // For each stateful kernel call 'setup' user callback to initialize state.
- setupKernelStates();
}
// FIXME: Document what it does
void cv::gimpl::GCPUExecutable::reshape(ade::Graph&, const GCompileArgs& args) {
m_compileArgs = args;
makeReshape();
- // Signal to reset stateful kernels` state.
- // There can be no handleNewStream() call to set this flag
- // if user didn't call GCompiled`s prepareForNewStream()
- m_newStreamStarted = true;
+ // TODO: Add an input meta sensitivity flag to stateful kernels.
+ // When reshape() happens, reset state for meta-sensitive kernels only
+ if (!m_nodesToStates.empty()) {
+ std::call_once(m_warnFlag,
+ [](){
+ GAPI_LOG_WARNING(NULL,
+ "\nGCPUExecutable::reshape was called. Resetting states of stateful kernels.");
+ });
+ setupKernelStates();
+ }
}
void cv::gimpl::GCPUExecutable::handleNewStream()
{
- // Signal to reset stateful kernels` state.
- // No need to call reshape() here since it'll
- // be called automatically if input meta was changed
- m_newStreamStarted = true;
+ // In case if new video-stream happens - for each stateful kernel
+ // call 'setup' user callback to re-initialize state.
+ setupKernelStates();
}
void cv::gimpl::GCPUExecutable::run(std::vector<InObj> &&input_objs,
}
}
- // In case if new video-stream happens - for each stateful kernel
- // call 'setup' user callback to re-initialize state.
- if (m_newStreamStarted)
- {
- setupKernelStates();
- m_newStreamStarted = false;
- }
-
// OpenCV backend execution is not a rocket science at all.
// Simply invoke our kernels in the proper order.
GConstGCPUModel gcm(m_g);
#include <opencv2/video.hpp>
#endif
+#include <memory> // required by std::shared_ptr
namespace opencv_test
{
{
std::string method;
};
+
+ struct CountStateSetupsParams
+ {
+ std::shared_ptr<int> pSetupsCount;
+ };
} // namespace opencv_test
namespace cv
return "org.opencv.test.background_substractor_state_params";
}
};
+
+ template<> struct CompileArgTag<opencv_test::CountStateSetupsParams>
+ {
+ static const char* tag()
+ {
+ return "org.opencv.test.count_state_setups_params";
+ }
+ };
} // namespace detail
} // namespace cv
}
};
#endif
+
+ G_TYPED_KERNEL(GCountStateSetups, <cv::GOpaque<bool>(GMat)>,
+ "org.opencv.test.count_state_setups")
+ {
+ static GOpaqueDesc outMeta(GMatDesc /* in */) { return empty_gopaque_desc(); }
+ };
+
+ GAPI_OCV_KERNEL_ST(GOCVCountStateSetups, GCountStateSetups, int)
+ {
+ static void setup(const cv::GMatDesc &, std::shared_ptr<int> &,
+ const cv::GCompileArgs &compileArgs)
+ {
+ auto params = cv::gapi::getCompileArg<CountStateSetupsParams>(compileArgs)
+ .value_or(CountStateSetupsParams { });
+ if (params.pSetupsCount != nullptr) {
+ (*params.pSetupsCount)++;
+ }
+ }
+
+ static void run(const cv::Mat & , bool &out, int &)
+ {
+ out = true;
+ }
+ };
+};
+
+TEST(StatefulKernel, StateInitOnceInRegularMode)
+{
+ cv::GMat in;
+ cv::GOpaque<bool> out = GCountStateSetups::on(in);
+ cv::GComputation c(cv::GIn(in), cv::GOut(out));
+
+ // Input mat:
+ cv::Mat inputData(1080, 1920, CV_8UC1);
+ cv::randu(inputData, cv::Scalar::all(1), cv::Scalar::all(128));
+
+ // variable to update when state is initialized in the kernel
+ CountStateSetupsParams params;
+ params.pSetupsCount.reset(new int(0));
+
+ // Testing for 100 frames
+ bool result { };
+ for (int i = 0; i < 100; ++i) {
+ c.apply(cv::gin(inputData), cv::gout(result),
+ cv::compile_args(cv::gapi::kernels<GOCVCountStateSetups>(), params));
+ EXPECT_TRUE(result);
+ EXPECT_TRUE(params.pSetupsCount != nullptr);
+ EXPECT_EQ(1, *params.pSetupsCount);
+ }
};
+struct StateInitOnce : public ::testing::TestWithParam<bool>{};
+TEST_P(StateInitOnce, StreamingCompiledWithMeta)
+{
+ bool compileWithMeta = GetParam();
+ cv::GMat in;
+ cv::GOpaque<bool> out = GCountStateSetups::on(in);
+ cv::GComputation c(cv::GIn(in), cv::GOut(out));
+
+ // Input mat:
+ cv::Mat inputData(1080, 1920, CV_8UC1);
+ cv::randu(inputData, cv::Scalar::all(1), cv::Scalar::all(128));
+
+ // variable to update when state is initialized in the kernel
+ CountStateSetupsParams params;
+ params.pSetupsCount.reset(new int(0));
+
+ // Compilation & testing
+ auto ccomp = (compileWithMeta)
+ ? c.compileStreaming(cv::descr_of(inputData),
+ cv::compile_args(cv::gapi::kernels<GOCVCountStateSetups>(),
+ params))
+ : c.compileStreaming(
+ cv::compile_args(cv::gapi::kernels<GOCVCountStateSetups>(),
+ params));
+
+ ccomp.setSource(cv::gin(inputData));
+
+ ccomp.start();
+ EXPECT_TRUE(ccomp.running());
+
+ int counter { };
+ bool result;
+ // Process mat 100 times
+ while (ccomp.pull(cv::gout(result)) && (counter++ < 100)) {
+ EXPECT_TRUE(params.pSetupsCount != nullptr);
+ EXPECT_EQ(1, *params.pSetupsCount);
+ }
+
+ ccomp.stop();
+ EXPECT_FALSE(ccomp.running());
+}
+
+INSTANTIATE_TEST_CASE_P(StatefulKernel, StateInitOnce, ::testing::Bool());
+
TEST(StatefulKernel, StateIsMutableInRuntime)
{
constexpr int expectedCallsCount = 10;
}
-TEST(StatefulKernel, StateIsAutoResetForNewStream)
+TEST(StateIsResetOnNewStream, RegularMode)
+{
+ cv::GMat in;
+ cv::GOpaque<bool> out = GCountStateSetups::on(in);
+ cv::GComputation c(cv::GIn(in), cv::GOut(out));
+
+ // Input mat:
+ cv::Mat inputData(1080, 1920, CV_8UC1);
+ cv::randu(inputData, cv::Scalar::all(1), cv::Scalar::all(128));
+
+ // variable to update when state is initialized in the kernel
+ CountStateSetupsParams params;
+ params.pSetupsCount.reset(new int(0));
+
+ auto setupsCounter = c.compile(cv::descr_of(inputData),
+ cv::compile_args(cv::gapi::kernels<GOCVCountStateSetups>(),
+ params));
+
+ bool result { };
+ for (int i = 0; i < 2; ++i) {
+ setupsCounter(cv::gin(inputData), cv::gout(result));
+ EXPECT_TRUE(params.pSetupsCount != nullptr);
+ EXPECT_EQ(1, *params.pSetupsCount);
+ }
+
+ EXPECT_TRUE(params.pSetupsCount != nullptr);
+ EXPECT_EQ(1, *params.pSetupsCount);
+ setupsCounter.prepareForNewStream();
+
+ for (int i = 0; i < 2; ++i) {
+ setupsCounter(cv::gin(inputData), cv::gout(result));
+ EXPECT_TRUE(params.pSetupsCount != nullptr);
+ EXPECT_EQ(2, *params.pSetupsCount);
+ }
+}
+
+TEST(StateIsResetOnNewStream, StreamingMode)
{
cv::GMat in;
cv::GOpaque<bool> out = GIsStateUpToDate::on(in);
run("cv/video/768x576.avi", "knn");
run("cv/video/1920x1080.avi", "mog2");
}
+
+TEST(StatefulKernel, StateIsResetOnceOnReshapeInStreaming)
+{
+ cv::GMat in;
+ cv::GOpaque<bool> out = GCountStateSetups::on(in);
+ cv::GComputation c(cv::GIn(in), cv::GOut(out));
+
+ // variable to update when state is initialized in the kernel
+ CountStateSetupsParams params;
+ params.pSetupsCount.reset(new int(0));
+
+ auto ccomp = c.compileStreaming(
+ cv::compile_args(cv::gapi::kernels<GOCVCountStateSetups>(), params));
+
+ auto run = [&ccomp, ¶ms](const std::string& videoPath, int expectedSetupsCount) {
+ auto path = findDataFile(videoPath);
+ try {
+ ccomp.setSource<cv::gapi::wip::GCaptureSource>(path);
+ } catch(...) {
+ throw SkipTestException("Video file can not be opened");
+ }
+ ccomp.start();
+
+ int frames = 0;
+ bool result = false;
+ while (ccomp.pull(cv::gout(result)) && (frames++ < 10)) {
+ EXPECT_TRUE(result);
+ EXPECT_TRUE(params.pSetupsCount != nullptr);
+ EXPECT_EQ(expectedSetupsCount, *params.pSetupsCount);
+ }
+ ccomp.stop();
+ };
+
+ run("cv/video/768x576.avi", 1);
+ // FIXME: it should be 2, not 3 for expectedSetupsCount here.
+ // With current implemention both GCPUExecutable reshape() and
+ // handleNewStream() call setupKernelStates()
+ run("cv/video/1920x1080.avi", 3);
+}
#endif
TEST(StatefulKernel, StateIsAutoResetOnReshape)