diff --git a/modules/gapi/perf/common/gapi_video_perf_tests.hpp b/modules/gapi/perf/common/gapi_video_perf_tests.hpp index 59e1555df0..a4796fab89 100644 --- a/modules/gapi/perf/common/gapi_video_perf_tests.hpp +++ b/modules/gapi/perf/common/gapi_video_perf_tests.hpp @@ -29,6 +29,12 @@ class BuildPyr_CalcOptFlow_PipelinePerfTest : public TestPerfParams> {}; + +class KalmanFilterControlPerfTest : + public TestPerfParams> {}; +class KalmanFilterNoControlPerfTest : + public TestPerfParams> {}; + } // opencv_test #endif // OPENCV_GAPI_VIDEO_PERF_TESTS_HPP diff --git a/modules/gapi/perf/common/gapi_video_perf_tests_inl.hpp b/modules/gapi/perf/common/gapi_video_perf_tests_inl.hpp index 68a96b0442..700d2f6e7e 100644 --- a/modules/gapi/perf/common/gapi_video_perf_tests_inl.hpp +++ b/modules/gapi/perf/common/gapi_video_perf_tests_inl.hpp @@ -241,6 +241,156 @@ PERF_TEST_P_(BackgroundSubtractorPerfTest, TestPerformance) //------------------------------------------------------------------------------ +inline void generateInputKalman(const int mDim, const MatType2& type, + const size_t testNumMeasurements, const bool receiveRandMeas, + std::vector& haveMeasurements, + std::vector& measurements) +{ + cv::RNG& rng = cv::theRNG(); + measurements.clear(); + haveMeasurements = std::vector(testNumMeasurements, true); + for (size_t i = 0; i < testNumMeasurements; i++) + { + if (receiveRandMeas) + { + haveMeasurements[i] = rng(2u) == 1; // returns 0 or 1 - whether we have measurement + // at this iteration or not + } // if not - testing the slowest case in which we have measurements at every iteration + + cv::Mat measurement = cv::Mat::zeros(mDim, 1, type); + if (haveMeasurements[i]) + { + cv::randu(measurement, cv::Scalar::all(-1), cv::Scalar::all(1)); + } + measurements.push_back(measurement.clone()); + } +} + +inline void generateInputKalman(const int mDim, const int cDim, const MatType2& type, + const size_t testNumMeasurements, const bool receiveRandMeas, + std::vector& haveMeasurements, + std::vector& measurements, + std::vector& ctrls) +{ + generateInputKalman(mDim, type, testNumMeasurements, receiveRandMeas, + haveMeasurements, measurements); + ctrls.clear(); + cv::Mat ctrl(cDim, 1, type); + for (size_t i = 0; i < testNumMeasurements; i++) + { + cv::randu(ctrl, cv::Scalar::all(-1), cv::Scalar::all(1)); + ctrls.push_back(ctrl.clone()); + } +} + +PERF_TEST_P_(KalmanFilterControlPerfTest, TestPerformance) +{ + MatType2 type = -1; + int dDim = -1, mDim = -1; + size_t testNumMeasurements = 0; + bool receiveRandMeas = true; + cv::GCompileArgs compileArgs; + std::tie(type, dDim, mDim, testNumMeasurements, receiveRandMeas, compileArgs) = GetParam(); + + const int cDim = 2; + cv::gapi::KalmanParams kp; + initKalmanParams(type, dDim, mDim, cDim, kp); + + // Generating input + std::vector haveMeasurements; + std::vector measurements, ctrls; + generateInputKalman(mDim, cDim, type, testNumMeasurements, receiveRandMeas, + haveMeasurements, measurements, ctrls); + + // G-API graph declaration + cv::GMat m, ctrl; + cv::GOpaque have_m; + cv::GMat out = cv::gapi::KalmanFilter(m, have_m, ctrl, kp); + cv::GComputation c(cv::GIn(m, have_m, ctrl), cv::GOut(out)); + auto cc = c.compile( + cv::descr_of(cv::gin(cv::Mat(mDim, 1, type), true, cv::Mat(cDim, 1, type))), + std::move(compileArgs)); + + cv::Mat gapiKState(dDim, 1, type); + TEST_CYCLE() + { + cc.prepareForNewStream(); + for (size_t i = 0; i < testNumMeasurements; i++) + { + bool hvMeas = haveMeasurements[i]; + cc(cv::gin(measurements[i], hvMeas, ctrls[i]), cv::gout(gapiKState)); + } + } + + // OpenCV reference KalmanFilter initialization + cv::KalmanFilter ocvKalman(dDim, mDim, cDim, type); + initKalmanFilter(kp, true, ocvKalman); + + cv::Mat ocvKState(dDim, 1, type); + for (size_t i = 0; i < testNumMeasurements; i++) + { + ocvKState = ocvKalman.predict(ctrls[i]); + if (haveMeasurements[i]) + ocvKState = ocvKalman.correct(measurements[i]); + } + // Validation + EXPECT_TRUE(AbsExact().to_compare_f()(gapiKState, ocvKState)); + SANITY_CHECK_NOTHING(); +} + +PERF_TEST_P_(KalmanFilterNoControlPerfTest, TestPerformance) +{ + MatType2 type = -1; + int dDim = -1, mDim = -1; + size_t testNumMeasurements = 0; + bool receiveRandMeas = true; + cv::GCompileArgs compileArgs; + std::tie(type, dDim, mDim, testNumMeasurements, receiveRandMeas, compileArgs) = GetParam(); + + const int cDim = 0; + cv::gapi::KalmanParams kp; + initKalmanParams(type, dDim, mDim, cDim, kp); + + // Generating input + std::vector haveMeasurements; + std::vector measurements; + generateInputKalman(mDim, type, testNumMeasurements, receiveRandMeas, + haveMeasurements, measurements); + + // G-API graph declaration + cv::GMat m; + cv::GOpaque have_m; + cv::GMat out = cv::gapi::KalmanFilter(m, have_m, kp); + cv::GComputation c(cv::GIn(m, have_m), cv::GOut(out)); + auto cc = c.compile(cv::descr_of(cv::gin(cv::Mat(mDim, 1, type), true)), + std::move(compileArgs)); + + cv::Mat gapiKState(dDim, 1, type); + TEST_CYCLE() + { + cc.prepareForNewStream(); + for (size_t i = 0; i < testNumMeasurements; i++) + { + bool hvMeas = haveMeasurements[i]; + cc(cv::gin(measurements[i], hvMeas), cv::gout(gapiKState)); + } + } + + // OpenCV reference KalmanFilter declaration + cv::KalmanFilter ocvKalman(dDim, mDim, cDim, type); + initKalmanFilter(kp, false, ocvKalman); + + cv::Mat ocvKState(dDim, 1, type); + for (size_t i = 0; i < testNumMeasurements; i++) + { + ocvKState = ocvKalman.predict(); + if (haveMeasurements[i]) + ocvKState = ocvKalman.correct(measurements[i]); + } + // Validation + EXPECT_TRUE(AbsExact().to_compare_f()(gapiKState, ocvKState)); + SANITY_CHECK_NOTHING(); +} #endif // HAVE_OPENCV_VIDEO } // opencv_test diff --git a/modules/gapi/perf/cpu/gapi_video_perf_tests_cpu.cpp b/modules/gapi/perf/cpu/gapi_video_perf_tests_cpu.cpp index 9a1a47adca..0da776a3ff 100644 --- a/modules/gapi/perf/cpu/gapi_video_perf_tests_cpu.cpp +++ b/modules/gapi/perf/cpu/gapi_video_perf_tests_cpu.cpp @@ -111,4 +111,22 @@ INSTANTIATE_TEST_CASE_MACRO_P(WITH_VIDEO(BackgroundSubtractorPerfTestCPU), Values(5), Values(cv::compile_args(VIDEO_CPU)), Values(AbsExact().to_compare_obj()))); + +INSTANTIATE_TEST_CASE_MACRO_P(WITH_VIDEO(KalmanFilterControlPerfTestCPU), + KalmanFilterControlPerfTest, + Combine(Values(CV_32FC1, CV_64FC1), + Values(2, 5), + Values(2, 5), + Values(5), + testing::Bool(), + Values(cv::compile_args(VIDEO_CPU)))); + +INSTANTIATE_TEST_CASE_MACRO_P(WITH_VIDEO(KalmanFilterNoControlPerfTestCPU), + KalmanFilterNoControlPerfTest, + Combine(Values(CV_32FC1, CV_64FC1), + Values(2, 5), + Values(2, 5), + Values(5), + testing::Bool(), + Values(cv::compile_args(VIDEO_CPU)))); } // opencv_test diff --git a/modules/gapi/src/backends/cpu/gcpuvideo.cpp b/modules/gapi/src/backends/cpu/gcpuvideo.cpp index 075b5f9ad5..cf64c240d0 100644 --- a/modules/gapi/src/backends/cpu/gcpuvideo.cpp +++ b/modules/gapi/src/backends/cpu/gcpuvideo.cpp @@ -117,15 +117,15 @@ GAPI_OCV_KERNEL_ST(GCPUKalmanFilter, cv::gapi::video::GKalmanFilter, cv::KalmanF kfParams.controlMatrix.cols, kfParams.transitionMatrix.type()); // initial state - state->statePost = kfParams.state; - state->errorCovPost = kfParams.errorCov; + kfParams.state.copyTo(state->statePost); + kfParams.errorCov.copyTo(state->errorCovPost); // dynamic system initialization - state->controlMatrix = kfParams.controlMatrix; - state->measurementMatrix = kfParams.measurementMatrix; - state->transitionMatrix = kfParams.transitionMatrix; - state->processNoiseCov = kfParams.processNoiseCov; - state->measurementNoiseCov = kfParams.measurementNoiseCov; + kfParams.controlMatrix.copyTo(state->controlMatrix); + kfParams.measurementMatrix.copyTo(state->measurementMatrix); + kfParams.transitionMatrix.copyTo(state->transitionMatrix); + kfParams.processNoiseCov.copyTo(state->processNoiseCov); + kfParams.measurementNoiseCov.copyTo(state->measurementNoiseCov); } static void run(const cv::Mat& measurements, bool haveMeasurement, @@ -151,14 +151,14 @@ GAPI_OCV_KERNEL_ST(GCPUKalmanFilterNoControl, cv::gapi::video::GKalmanFilterNoCo state = std::make_shared(kfParams.transitionMatrix.rows, kfParams.measurementMatrix.rows, 0, kfParams.transitionMatrix.type()); // initial state - state->statePost = kfParams.state; - state->errorCovPost = kfParams.errorCov; + kfParams.state.copyTo(state->statePost); + kfParams.errorCov.copyTo(state->errorCovPost); // dynamic system initialization - state->measurementMatrix = kfParams.measurementMatrix; - state->transitionMatrix = kfParams.transitionMatrix; - state->processNoiseCov = kfParams.processNoiseCov; - state->measurementNoiseCov = kfParams.measurementNoiseCov; + kfParams.measurementMatrix.copyTo(state->measurementMatrix); + kfParams.transitionMatrix.copyTo(state->transitionMatrix); + kfParams.processNoiseCov.copyTo(state->processNoiseCov); + kfParams.measurementNoiseCov.copyTo(state->measurementNoiseCov); } static void run(const cv::Mat& measurements, bool haveMeasurement, diff --git a/modules/gapi/test/common/gapi_video_tests_common.hpp b/modules/gapi/test/common/gapi_video_tests_common.hpp index d6a99ce8aa..f3e7e5aa6c 100644 --- a/modules/gapi/test/common/gapi_video_tests_common.hpp +++ b/modules/gapi/test/common/gapi_video_tests_common.hpp @@ -397,6 +397,37 @@ inline void testBackgroundSubtractorStreaming(cv::GStreamingCompiled& gapiBackSu EXPECT_FALSE(gapiBackSub.running()); } +inline void initKalmanParams(const int type, const int dDim, const int mDim, const int cDim, + cv::gapi::KalmanParams& kp) +{ + kp.state = Mat::zeros(dDim, 1, type); + cv::randu(kp.state, Scalar::all(0), Scalar::all(0.1)); + kp.errorCov = Mat::eye(dDim, dDim, type); + + kp.transitionMatrix = Mat::ones(dDim, dDim, type) * 2; + kp.processNoiseCov = Mat::eye(dDim, dDim, type) * (1e-5); + kp.measurementMatrix = Mat::eye(mDim, dDim, type) * 2; + kp.measurementNoiseCov = Mat::eye(mDim, mDim, type) * (1e-5); + + if (cDim > 0) + kp.controlMatrix = Mat::eye(dDim, cDim, type) * (1e-3); +} + +inline void initKalmanFilter(const cv::gapi::KalmanParams& kp, const bool control, + cv::KalmanFilter& ocvKalman) +{ + kp.state.copyTo(ocvKalman.statePost); + kp.errorCov.copyTo(ocvKalman.errorCovPost); + + kp.transitionMatrix.copyTo(ocvKalman.transitionMatrix); + kp.measurementMatrix.copyTo(ocvKalman.measurementMatrix); + kp.measurementNoiseCov.copyTo(ocvKalman.measurementNoiseCov); + kp.processNoiseCov.copyTo(ocvKalman.processNoiseCov); + + if (control) + kp.controlMatrix.copyTo(ocvKalman.controlMatrix); +} + #else // !HAVE_OPENCV_VIDEO inline cv::GComputation runOCVnGAPIBuildOptFlowPyramid(TestFunctional&, diff --git a/modules/gapi/test/common/gapi_video_tests_inl.hpp b/modules/gapi/test/common/gapi_video_tests_inl.hpp index 75fd50b208..7827680561 100644 --- a/modules/gapi/test/common/gapi_video_tests_inl.hpp +++ b/modules/gapi/test/common/gapi_video_tests_inl.hpp @@ -132,49 +132,19 @@ TEST_P(BackgroundSubtractorTest, AccuracyTest) testBackgroundSubtractorStreaming(gapiBackSub, pOCVBackSub, 1, 1, learningRate, testNumFrames); } -inline void initKalmanParams(cv::gapi::KalmanParams& kp, int type, int dDim, int mDim, int cDim) -{ - kp.state = Mat::zeros(dDim, 1, type); - cv::randu(kp.state, Scalar::all(0), Scalar::all(0.1)); - kp.errorCov = Mat::eye(dDim, dDim, type); - - kp.transitionMatrix = Mat::ones(dDim, dDim, type) * 2; - kp.processNoiseCov = Mat::eye(dDim, dDim, type)*(1e-5); - kp.measurementMatrix = Mat::eye(mDim, dDim, type) * 2; - kp.measurementNoiseCov = Mat::eye(mDim, mDim, type)*(1e-5); - - if (cDim > 0) - kp.controlMatrix = Mat::eye(dDim, cDim, type)* (1e-3); -} - -inline void initKalmanFilter(const cv::gapi::KalmanParams& kp, - cv::KalmanFilter& ocvKalman, bool control) -{ - kp.state.copyTo(ocvKalman.statePost); - kp.errorCov.copyTo(ocvKalman.errorCovPost); - - kp.transitionMatrix.copyTo(ocvKalman.transitionMatrix); - kp.measurementMatrix.copyTo(ocvKalman.measurementMatrix); - kp.measurementNoiseCov.copyTo(ocvKalman.measurementNoiseCov); - kp.processNoiseCov.copyTo(ocvKalman.processNoiseCov); - - if (control) - kp.controlMatrix.copyTo(ocvKalman.controlMatrix); -} - TEST_P(KalmanFilterTest, AccuracyTest) { cv::gapi::KalmanParams kp; - initKalmanParams(kp, type, dDim, mDim, cDim); + initKalmanParams(type, dDim, mDim, cDim, kp); // OpenCV reference KalmanFilter initialization cv::KalmanFilter ocvKalman(dDim, mDim, cDim, type); - initKalmanFilter(kp, ocvKalman, true); + initKalmanFilter(kp, true, ocvKalman); - //measurement vector + // measurement vector cv::Mat measure_vec(mDim, 1, type); - //control vector + // control vector cv::Mat ctrl_vec = Mat::zeros(cDim > 0 ? cDim : 2, 1, type); // G-API Kalman's output state @@ -193,7 +163,7 @@ TEST_P(KalmanFilterTest, AccuracyTest) for (int i = 0; i < numIter; i++) { - haveMeasure = (rng(2u) == 1) ? true : false; // returns 0 or 1 - whether we have measurement at this iteration or not + haveMeasure = (rng(2u) == 1); // returns 0 or 1 - whether we have measurement at this iteration or not if (haveMeasure) cv::randu(measure_vec, Scalar::all(-1), Scalar::all(1)); @@ -210,22 +180,20 @@ TEST_P(KalmanFilterTest, AccuracyTest) // Comparison ////////////////////////////////////////////////////////////// { - double diff = 0; - vector idx; - EXPECT_TRUE(cmpEps(gapiKState, ocvKState, &diff, 1.0, &idx, false) >= 0); + EXPECT_TRUE(AbsExact().to_compare_f()(gapiKState, ocvKState)); } } TEST_P(KalmanFilterNoControlTest, AccuracyTest) { cv::gapi::KalmanParams kp; - initKalmanParams(kp, type, dDim, mDim, 0); + initKalmanParams(type, dDim, mDim, 0, kp); // OpenCV reference KalmanFilter initialization cv::KalmanFilter ocvKalman(dDim, mDim, 0, type); - initKalmanFilter(kp, ocvKalman, false); + initKalmanFilter(kp, false, ocvKalman); - //measurement vector + // measurement vector cv::Mat measure_vec(mDim, 1, type); // G-API Kalman's output state @@ -244,7 +212,7 @@ TEST_P(KalmanFilterNoControlTest, AccuracyTest) for (int i = 0; i < numIter; i++) { - haveMeasure = (rng(2u) == 1) ? true : false; // returns 0 or 1 - whether we have measurement at this iteration or not + haveMeasure = (rng(2u) == 1); // returns 0 or 1 - whether we have measurement at this iteration or not if (haveMeasure) cv::randu(measure_vec, Scalar::all(-1), Scalar::all(1)); @@ -260,9 +228,7 @@ TEST_P(KalmanFilterNoControlTest, AccuracyTest) // Comparison ////////////////////////////////////////////////////////////// { - double diff = 0; - vector idx; - EXPECT_TRUE(cmpEps(gapiKState, ocvKState, &diff, 1.0, &idx, false) >= 0); + EXPECT_TRUE(AbsExact().to_compare_f()(gapiKState, ocvKState)); } } @@ -270,11 +236,8 @@ TEST_P(KalmanFilterCircleSampleTest, AccuracyTest) { // auxiliary variables cv::Mat processNoise(2, 1, type); - // For comparison - double diff = 0; - vector idx; - // Input mesurement + // Input measurement cv::Mat measurement = Mat::zeros(1, 1, type); // Angle and it's delta(phi, delta_phi) cv::Mat state(2, 1, type); @@ -303,7 +266,7 @@ TEST_P(KalmanFilterCircleSampleTest, AccuracyTest) // OCV Kalman initialization cv::KalmanFilter KF(2, 1, 0); - initKalmanFilter(kp, KF, false); + initKalmanFilter(kp, false, KF); cv::randn(state, Scalar::all(0), Scalar::all(0.1)); @@ -334,14 +297,14 @@ TEST_P(KalmanFilterCircleSampleTest, AccuracyTest) haveMeasure = true; ocvCorrState = KF.correct(measurement); comp.apply(cv::gin(measurement, haveMeasure), cv::gout(gapiState)); - EXPECT_TRUE(cmpEps(gapiState, ocvCorrState, &diff, 1.0, &idx, false) >= 0); + EXPECT_TRUE(AbsExact().to_compare_f()(gapiState, ocvCorrState)); } else { // Get GAPI Prediction haveMeasure = false; comp.apply(cv::gin(measurement, haveMeasure), cv::gout(gapiState)); - EXPECT_TRUE(cmpEps(gapiState, ocvPreState, &diff, 1.0, &idx, false) >= 0); + EXPECT_TRUE(AbsExact().to_compare_f()(gapiState, ocvPreState)); } GAPI_DbgAssert(cv::norm(kp.processNoiseCov, KF.processNoiseCov, cv::NORM_INF) == 0);