mirror of https://github.com/opencv/opencv.git
Open Source Computer Vision Library
https://opencv.org/
You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
500 lines
16 KiB
500 lines
16 KiB
/*M/////////////////////////////////////////////////////////////////////////////////////// |
|
// |
|
// IMPORTANT: READ BEFORE DOWNLOADING, COPYING, INSTALLING OR USING. |
|
// |
|
// By downloading, copying, installing or using the software you agree to this license. |
|
// If you do not agree to this license, do not download, install, |
|
// copy or use the software. |
|
// |
|
// |
|
// Intel License Agreement |
|
// For Open Source Computer Vision Library |
|
// |
|
// Copyright (C) 2000, Intel Corporation, all rights reserved. |
|
// Third party copyrights are property of their respective owners. |
|
// |
|
// Redistribution and use in source and binary forms, with or without modification, |
|
// are permitted provided that the following conditions are met: |
|
// |
|
// * Redistribution's of source code must retain the above copyright notice, |
|
// this list of conditions and the following disclaimer. |
|
// |
|
// * Redistribution's in binary form must reproduce the above copyright notice, |
|
// this list of conditions and the following disclaimer in the documentation |
|
// and/or other materials provided with the distribution. |
|
// |
|
// * The name of Intel Corporation may not be used to endorse or promote products |
|
// derived from this software without specific prior written permission. |
|
// |
|
// This software is provided by the copyright holders and contributors "as is" and |
|
// any express or implied warranties, including, but not limited to, the implied |
|
// warranties of merchantability and fitness for a particular purpose are disclaimed. |
|
// In no event shall the Intel Corporation or contributors be liable for any direct, |
|
// indirect, incidental, special, exemplary, or consequential damages |
|
// (including, but not limited to, procurement of substitute goods or services; |
|
// loss of use, data, or profits; or business interruption) however caused |
|
// and on any theory of liability, whether in contract, strict liability, |
|
// or tort (including negligence or otherwise) arising in any way out of |
|
// the use of this software, even if advised of the possibility of such damage. |
|
// |
|
//M*/ |
|
|
|
#include "test_precomp.hpp" |
|
|
|
using namespace cv; |
|
using namespace std; |
|
|
|
///////////////////// base MHI class /////////////////////// |
|
class CV_MHIBaseTest : public cvtest::ArrayTest |
|
{ |
|
public: |
|
CV_MHIBaseTest(); |
|
|
|
protected: |
|
void get_test_array_types_and_sizes( int test_case_idx, vector<vector<Size> >& sizes, vector<vector<int> >& types ); |
|
void get_minmax_bounds( int i, int j, int type, Scalar& low, Scalar& high ); |
|
int prepare_test_case( int test_case_idx ); |
|
double timestamp, duration, max_log_duration; |
|
int mhi_i, mhi_ref_i; |
|
double silh_ratio; |
|
}; |
|
|
|
|
|
CV_MHIBaseTest::CV_MHIBaseTest() |
|
{ |
|
timestamp = duration = 0; |
|
max_log_duration = 9; |
|
mhi_i = mhi_ref_i = -1; |
|
|
|
silh_ratio = 0.25; |
|
} |
|
|
|
|
|
void CV_MHIBaseTest::get_minmax_bounds( int i, int j, int type, Scalar& low, Scalar& high ) |
|
{ |
|
cvtest::ArrayTest::get_minmax_bounds( i, j, type, low, high ); |
|
if( i == INPUT && CV_MAT_DEPTH(type) == CV_8U ) |
|
{ |
|
low = Scalar::all(cvRound(-1./silh_ratio)+2.); |
|
high = Scalar::all(2); |
|
} |
|
else if( i == mhi_i || i == mhi_ref_i ) |
|
{ |
|
low = Scalar::all(-exp(max_log_duration)); |
|
high = Scalar::all(0.); |
|
} |
|
} |
|
|
|
|
|
void CV_MHIBaseTest::get_test_array_types_and_sizes( int test_case_idx, |
|
vector<vector<Size> >& sizes, vector<vector<int> >& types ) |
|
{ |
|
RNG& rng = ts->get_rng(); |
|
cvtest::ArrayTest::get_test_array_types_and_sizes( test_case_idx, sizes, types ); |
|
|
|
types[INPUT][0] = CV_8UC1; |
|
types[mhi_i][0] = types[mhi_ref_i][0] = CV_32FC1; |
|
duration = exp(cvtest::randReal(rng)*max_log_duration); |
|
timestamp = duration + cvtest::randReal(rng)*30.-10.; |
|
} |
|
|
|
|
|
int CV_MHIBaseTest::prepare_test_case( int test_case_idx ) |
|
{ |
|
int code = cvtest::ArrayTest::prepare_test_case( test_case_idx ); |
|
if( code > 0 ) |
|
{ |
|
Mat& mat = test_mat[mhi_i][0]; |
|
mat += Scalar::all(duration); |
|
cv::max(mat, 0, mat); |
|
if( mhi_i != mhi_ref_i ) |
|
{ |
|
Mat& mat0 = test_mat[mhi_ref_i][0]; |
|
cvtest::copy( mat, mat0 ); |
|
} |
|
} |
|
|
|
return code; |
|
} |
|
|
|
|
|
///////////////////// update motion history //////////////////////////// |
|
|
|
static void test_updateMHI( const Mat& silh, Mat& mhi, double timestamp, double duration ) |
|
{ |
|
int i, j; |
|
float delbound = (float)(timestamp - duration); |
|
for( i = 0; i < mhi.rows; i++ ) |
|
{ |
|
const uchar* silh_row = silh.ptr(i); |
|
float* mhi_row = mhi.ptr<float>(i); |
|
|
|
for( j = 0; j < mhi.cols; j++ ) |
|
{ |
|
if( silh_row[j] ) |
|
mhi_row[j] = (float)timestamp; |
|
else if( mhi_row[j] < delbound ) |
|
mhi_row[j] = 0.f; |
|
} |
|
} |
|
} |
|
|
|
|
|
class CV_UpdateMHITest : public CV_MHIBaseTest |
|
{ |
|
public: |
|
CV_UpdateMHITest(); |
|
|
|
protected: |
|
double get_success_error_level( int test_case_idx, int i, int j ); |
|
void run_func(); |
|
void prepare_to_validation( int ); |
|
}; |
|
|
|
|
|
CV_UpdateMHITest::CV_UpdateMHITest() |
|
{ |
|
test_array[INPUT].push_back(NULL); |
|
test_array[INPUT_OUTPUT].push_back(NULL); |
|
test_array[REF_INPUT_OUTPUT].push_back(NULL); |
|
mhi_i = INPUT_OUTPUT; mhi_ref_i = REF_INPUT_OUTPUT; |
|
} |
|
|
|
|
|
double CV_UpdateMHITest::get_success_error_level( int /*test_case_idx*/, int /*i*/, int /*j*/ ) |
|
{ |
|
return 0; |
|
} |
|
|
|
|
|
void CV_UpdateMHITest::run_func() |
|
{ |
|
cv::updateMotionHistory( test_mat[INPUT][0], test_mat[INPUT_OUTPUT][0], timestamp, duration); |
|
} |
|
|
|
|
|
void CV_UpdateMHITest::prepare_to_validation( int /*test_case_idx*/ ) |
|
{ |
|
//CvMat m0 = test_mat[REF_INPUT_OUTPUT][0]; |
|
test_updateMHI( test_mat[INPUT][0], test_mat[REF_INPUT_OUTPUT][0], timestamp, duration ); |
|
} |
|
|
|
|
|
///////////////////// calc motion gradient //////////////////////////// |
|
|
|
static void test_MHIGradient( const Mat& mhi, Mat& mask, Mat& orientation, |
|
double delta1, double delta2, int aperture_size ) |
|
{ |
|
Point anchor( aperture_size/2, aperture_size/2 ); |
|
double limit = 1e-4*aperture_size*aperture_size; |
|
|
|
Mat dx, dy, min_mhi, max_mhi; |
|
|
|
Mat kernel = cvtest::calcSobelKernel2D( 1, 0, aperture_size ); |
|
cvtest::filter2D( mhi, dx, CV_32F, kernel, anchor, 0, BORDER_REPLICATE ); |
|
kernel = cvtest::calcSobelKernel2D( 0, 1, aperture_size ); |
|
cvtest::filter2D( mhi, dy, CV_32F, kernel, anchor, 0, BORDER_REPLICATE ); |
|
|
|
kernel = Mat::ones(aperture_size, aperture_size, CV_8U); |
|
cvtest::erode(mhi, min_mhi, kernel, anchor, 0, BORDER_REPLICATE); |
|
cvtest::dilate(mhi, max_mhi, kernel, anchor, 0, BORDER_REPLICATE); |
|
|
|
if( delta1 > delta2 ) |
|
{ |
|
std::swap( delta1, delta2 ); |
|
} |
|
|
|
for( int i = 0; i < mhi.rows; i++ ) |
|
{ |
|
uchar* mask_row = mask.ptr(i); |
|
float* orient_row = orientation.ptr<float>(i); |
|
const float* dx_row = dx.ptr<float>(i); |
|
const float* dy_row = dy.ptr<float>(i); |
|
const float* min_row = min_mhi.ptr<float>(i); |
|
const float* max_row = max_mhi.ptr<float>(i); |
|
|
|
for( int j = 0; j < mhi.cols; j++ ) |
|
{ |
|
double delta = max_row[j] - min_row[j]; |
|
double _dx = dx_row[j], _dy = dy_row[j]; |
|
|
|
if( delta1 <= delta && delta <= delta2 && |
|
(fabs(_dx) > limit || fabs(_dy) > limit) ) |
|
{ |
|
mask_row[j] = 1; |
|
double angle = atan2( _dy, _dx ) * (180/CV_PI); |
|
if( angle < 0 ) |
|
angle += 360.; |
|
orient_row[j] = (float)angle; |
|
} |
|
else |
|
{ |
|
mask_row[j] = 0; |
|
orient_row[j] = 0.f; |
|
} |
|
} |
|
} |
|
} |
|
|
|
|
|
class CV_MHIGradientTest : public CV_MHIBaseTest |
|
{ |
|
public: |
|
CV_MHIGradientTest(); |
|
|
|
protected: |
|
void get_test_array_types_and_sizes( int test_case_idx, vector<vector<Size> >& sizes, vector<vector<int> >& types ); |
|
double get_success_error_level( int test_case_idx, int i, int j ); |
|
void run_func(); |
|
void prepare_to_validation( int ); |
|
|
|
double delta1, delta2, delta_range_log; |
|
int aperture_size; |
|
}; |
|
|
|
|
|
CV_MHIGradientTest::CV_MHIGradientTest() |
|
{ |
|
mhi_i = mhi_ref_i = INPUT; |
|
test_array[INPUT].push_back(NULL); |
|
test_array[OUTPUT].push_back(NULL); |
|
test_array[OUTPUT].push_back(NULL); |
|
test_array[REF_OUTPUT].push_back(NULL); |
|
test_array[REF_OUTPUT].push_back(NULL); |
|
delta1 = delta2 = 0; |
|
aperture_size = 0; |
|
delta_range_log = 4; |
|
} |
|
|
|
|
|
void CV_MHIGradientTest::get_test_array_types_and_sizes( int test_case_idx, vector<vector<Size> >& sizes, vector<vector<int> >& types ) |
|
{ |
|
RNG& rng = ts->get_rng(); |
|
CV_MHIBaseTest::get_test_array_types_and_sizes( test_case_idx, sizes, types ); |
|
|
|
types[OUTPUT][0] = types[REF_OUTPUT][0] = CV_8UC1; |
|
types[OUTPUT][1] = types[REF_OUTPUT][1] = CV_32FC1; |
|
delta1 = exp(cvtest::randReal(rng)*delta_range_log + 1.); |
|
delta2 = exp(cvtest::randReal(rng)*delta_range_log + 1.); |
|
aperture_size = (cvtest::randInt(rng)%3)*2+3; |
|
//duration = exp(cvtest::randReal(rng)*max_log_duration); |
|
//timestamp = duration + cvtest::randReal(rng)*30.-10.; |
|
} |
|
|
|
|
|
double CV_MHIGradientTest::get_success_error_level( int /*test_case_idx*/, int /*i*/, int j ) |
|
{ |
|
return j == 0 ? 0 : 2e-1; |
|
} |
|
|
|
|
|
void CV_MHIGradientTest::run_func() |
|
{ |
|
cv::calcMotionGradient(test_mat[INPUT][0], test_mat[OUTPUT][0], |
|
test_mat[OUTPUT][1], delta1, delta2, aperture_size ); |
|
//cvCalcMotionGradient( test_array[INPUT][0], test_array[OUTPUT][0], |
|
// test_array[OUTPUT][1], delta1, delta2, aperture_size ); |
|
} |
|
|
|
|
|
void CV_MHIGradientTest::prepare_to_validation( int /*test_case_idx*/ ) |
|
{ |
|
test_MHIGradient( test_mat[INPUT][0], test_mat[REF_OUTPUT][0], |
|
test_mat[REF_OUTPUT][1], delta1, delta2, aperture_size ); |
|
test_mat[REF_OUTPUT][0] += Scalar::all(1); |
|
test_mat[OUTPUT][0] += Scalar::all(1); |
|
} |
|
|
|
|
|
////////////////////// calc global orientation ///////////////////////// |
|
|
|
static double test_calcGlobalOrientation( const Mat& orient, const Mat& mask, |
|
const Mat& mhi, double timestamp, double duration ) |
|
{ |
|
const int HIST_SIZE = 12; |
|
int y, x; |
|
int histogram[HIST_SIZE]; |
|
int max_bin = 0; |
|
|
|
double base_orientation = 0, delta_orientation = 0, weight = 0; |
|
double low_time, global_orientation; |
|
|
|
memset( histogram, 0, sizeof( histogram )); |
|
timestamp = 0; |
|
|
|
for( y = 0; y < orient.rows; y++ ) |
|
{ |
|
const float* orient_data = orient.ptr<float>(y); |
|
const uchar* mask_data = mask.ptr(y); |
|
const float* mhi_data = mhi.ptr<float>(y); |
|
for( x = 0; x < orient.cols; x++ ) |
|
if( mask_data[x] ) |
|
{ |
|
int bin = cvFloor( (orient_data[x]*HIST_SIZE)/360 ); |
|
histogram[bin < 0 ? 0 : bin >= HIST_SIZE ? HIST_SIZE-1 : bin]++; |
|
if( mhi_data[x] > timestamp ) |
|
timestamp = mhi_data[x]; |
|
} |
|
} |
|
|
|
low_time = timestamp - duration; |
|
|
|
for( x = 1; x < HIST_SIZE; x++ ) |
|
{ |
|
if( histogram[x] > histogram[max_bin] ) |
|
max_bin = x; |
|
} |
|
|
|
base_orientation = ((double)max_bin*360)/HIST_SIZE; |
|
|
|
for( y = 0; y < orient.rows; y++ ) |
|
{ |
|
const float* orient_data = orient.ptr<float>(y); |
|
const float* mhi_data = mhi.ptr<float>(y); |
|
const uchar* mask_data = mask.ptr(y); |
|
|
|
for( x = 0; x < orient.cols; x++ ) |
|
{ |
|
if( mask_data[x] && mhi_data[x] > low_time ) |
|
{ |
|
double diff = orient_data[x] - base_orientation; |
|
double delta_weight = (((mhi_data[x] - low_time)/duration)*254 + 1)/255; |
|
|
|
if( diff < -180 ) diff += 360; |
|
if( diff > 180 ) diff -= 360; |
|
|
|
if( delta_weight > 0 && fabs(diff) < 45 ) |
|
{ |
|
delta_orientation += diff*delta_weight; |
|
weight += delta_weight; |
|
} |
|
} |
|
} |
|
} |
|
|
|
if( weight == 0 ) |
|
global_orientation = base_orientation; |
|
else |
|
{ |
|
global_orientation = base_orientation + delta_orientation/weight; |
|
if( global_orientation < 0 ) global_orientation += 360; |
|
if( global_orientation > 360 ) global_orientation -= 360; |
|
} |
|
|
|
return global_orientation; |
|
} |
|
|
|
|
|
class CV_MHIGlobalOrientTest : public CV_MHIBaseTest |
|
{ |
|
public: |
|
CV_MHIGlobalOrientTest(); |
|
|
|
protected: |
|
void get_test_array_types_and_sizes( int test_case_idx, vector<vector<Size> >& sizes, vector<vector<int> >& types ); |
|
void get_minmax_bounds( int i, int j, int type, Scalar& low, Scalar& high ); |
|
double get_success_error_level( int test_case_idx, int i, int j ); |
|
int validate_test_results( int test_case_idx ); |
|
void run_func(); |
|
double angle, min_angle, max_angle; |
|
}; |
|
|
|
|
|
CV_MHIGlobalOrientTest::CV_MHIGlobalOrientTest() |
|
{ |
|
mhi_i = mhi_ref_i = INPUT; |
|
test_array[INPUT].push_back(NULL); |
|
test_array[INPUT].push_back(NULL); |
|
test_array[INPUT].push_back(NULL); |
|
min_angle = max_angle = 0; |
|
} |
|
|
|
|
|
void CV_MHIGlobalOrientTest::get_test_array_types_and_sizes( int test_case_idx, vector<vector<Size> >& sizes, vector<vector<int> >& types ) |
|
{ |
|
RNG& rng = ts->get_rng(); |
|
CV_MHIBaseTest::get_test_array_types_and_sizes( test_case_idx, sizes, types ); |
|
Size size = sizes[INPUT][0]; |
|
|
|
size.width = MAX( size.width, 16 ); |
|
size.height = MAX( size.height, 16 ); |
|
sizes[INPUT][0] = sizes[INPUT][1] = sizes[INPUT][2] = size; |
|
|
|
types[INPUT][1] = CV_8UC1; // mask |
|
types[INPUT][2] = CV_32FC1; // orientation |
|
|
|
min_angle = cvtest::randReal(rng)*359.9; |
|
max_angle = cvtest::randReal(rng)*359.9; |
|
if( min_angle >= max_angle ) |
|
{ |
|
std::swap( min_angle, max_angle); |
|
} |
|
max_angle += 0.1; |
|
duration = exp(cvtest::randReal(rng)*max_log_duration); |
|
timestamp = duration + cvtest::randReal(rng)*30.-10.; |
|
} |
|
|
|
|
|
void CV_MHIGlobalOrientTest::get_minmax_bounds( int i, int j, int type, Scalar& low, Scalar& high ) |
|
{ |
|
CV_MHIBaseTest::get_minmax_bounds( i, j, type, low, high ); |
|
if( i == INPUT && j == 2 ) |
|
{ |
|
low = Scalar::all(min_angle); |
|
high = Scalar::all(max_angle); |
|
} |
|
} |
|
|
|
|
|
double CV_MHIGlobalOrientTest::get_success_error_level( int /*test_case_idx*/, int /*i*/, int /*j*/ ) |
|
{ |
|
return 15; |
|
} |
|
|
|
|
|
void CV_MHIGlobalOrientTest::run_func() |
|
{ |
|
//angle = cvCalcGlobalOrientation( test_array[INPUT][2], test_array[INPUT][1], |
|
// test_array[INPUT][0], timestamp, duration ); |
|
angle = cv::calcGlobalOrientation(test_mat[INPUT][2], test_mat[INPUT][1], |
|
test_mat[INPUT][0], timestamp, duration ); |
|
} |
|
|
|
|
|
int CV_MHIGlobalOrientTest::validate_test_results( int test_case_idx ) |
|
{ |
|
//printf("%d. rows=%d, cols=%d, nzmask=%d\n", test_case_idx, test_mat[INPUT][1].rows, test_mat[INPUT][1].cols, |
|
// cvCountNonZero(test_array[INPUT][1])); |
|
|
|
double ref_angle = test_calcGlobalOrientation( test_mat[INPUT][2], test_mat[INPUT][1], |
|
test_mat[INPUT][0], timestamp, duration ); |
|
double err_level = get_success_error_level( test_case_idx, 0, 0 ); |
|
int code = cvtest::TS::OK; |
|
int nz = countNonZero( test_mat[INPUT][1] ); |
|
|
|
if( nz > 32 && !(min_angle - err_level <= angle && |
|
max_angle + err_level >= angle) && |
|
!(min_angle - err_level <= angle+360 && |
|
max_angle + err_level >= angle+360) ) |
|
{ |
|
ts->printf( cvtest::TS::LOG, "The angle=%g is outside (%g,%g) range\n", |
|
angle, min_angle - err_level, max_angle + err_level ); |
|
code = cvtest::TS::FAIL_BAD_ACCURACY; |
|
} |
|
else if( fabs(angle - ref_angle) > err_level && |
|
fabs(360 - fabs(angle - ref_angle)) > err_level ) |
|
{ |
|
ts->printf( cvtest::TS::LOG, "The angle=%g differs too much from reference value=%g\n", |
|
angle, ref_angle ); |
|
code = cvtest::TS::FAIL_BAD_ACCURACY; |
|
} |
|
|
|
if( code < 0 ) |
|
ts->set_failed_test_info( code ); |
|
return code; |
|
} |
|
|
|
|
|
TEST(Video_MHIUpdate, accuracy) { CV_UpdateMHITest test; test.safe_run(); } |
|
TEST(Video_MHIGradient, accuracy) { CV_MHIGradientTest test; test.safe_run(); } |
|
TEST(Video_MHIGlobalOrient, accuracy) { CV_MHIGlobalOrientTest test; test.safe_run(); }
|
|
|