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.
397 lines
13 KiB
397 lines
13 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. |
|
// |
|
// |
|
// License Agreement |
|
// For Open Source Computer Vision Library |
|
// |
|
// Copyright (C) 2000-2008, Intel Corporation, all rights reserved. |
|
// Copyright (C) 2009, Willow Garage Inc., 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 the copyright holders 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" |
|
|
|
namespace opencv_test { namespace { |
|
|
|
/// phase correlation |
|
class CV_PhaseCorrelatorTest : public cvtest::ArrayTest |
|
{ |
|
public: |
|
CV_PhaseCorrelatorTest(); |
|
protected: |
|
void run( int ); |
|
}; |
|
|
|
CV_PhaseCorrelatorTest::CV_PhaseCorrelatorTest() {} |
|
|
|
void CV_PhaseCorrelatorTest::run( int ) |
|
{ |
|
ts->set_failed_test_info(cvtest::TS::OK); |
|
|
|
Mat r1 = Mat::ones(Size(129, 128), CV_64F); |
|
Mat r2 = Mat::ones(Size(129, 128), CV_64F); |
|
|
|
double expectedShiftX = -10.0; |
|
double expectedShiftY = -20.0; |
|
|
|
// draw 10x10 rectangles @ (100, 100) and (90, 80) should see ~(-10, -20) shift here... |
|
cv::rectangle(r1, Point(100, 100), Point(110, 110), Scalar(0, 0, 0), cv::FILLED); |
|
cv::rectangle(r2, Point(90, 80), Point(100, 90), Scalar(0, 0, 0), cv::FILLED); |
|
|
|
Mat hann; |
|
createHanningWindow(hann, r1.size(), CV_64F); |
|
Point2d phaseShift = phaseCorrelate(r1, r2, hann); |
|
|
|
// test accuracy should be less than 1 pixel... |
|
if(std::abs(expectedShiftX - phaseShift.x) >= 1 || std::abs(expectedShiftY - phaseShift.y) >= 1) |
|
{ |
|
ts->set_failed_test_info( cvtest::TS::FAIL_BAD_ACCURACY ); |
|
} |
|
} |
|
|
|
TEST(Imgproc_PhaseCorrelatorTest, accuracy) { CV_PhaseCorrelatorTest test; test.safe_run(); } |
|
|
|
TEST(Imgproc_PhaseCorrelatorTest, accuracy_real_img) |
|
{ |
|
Mat img = imread(cvtest::TS::ptr()->get_data_path() + "shared/airplane.png", IMREAD_GRAYSCALE); |
|
img.convertTo(img, CV_64FC1); |
|
|
|
const int xLen = 129; |
|
const int yLen = 129; |
|
const int xShift = 40; |
|
const int yShift = 14; |
|
|
|
Mat roi1 = img(Rect(xShift, yShift, xLen, yLen)); |
|
Mat roi2 = img(Rect(0, 0, xLen, yLen)); |
|
|
|
Mat hann; |
|
createHanningWindow(hann, roi1.size(), CV_64F); |
|
Point2d phaseShift = phaseCorrelate(roi1, roi2, hann); |
|
|
|
ASSERT_NEAR(phaseShift.x, (double)xShift, 1.); |
|
ASSERT_NEAR(phaseShift.y, (double)yShift, 1.); |
|
} |
|
|
|
TEST(Imgproc_PhaseCorrelatorTest, accuracy_1d_odd_fft) { |
|
Mat r1 = Mat::ones(Size(129, 1), CV_64F)*255; // 129 will be completed to 135 before FFT |
|
Mat r2 = Mat::ones(Size(129, 1), CV_64F)*255; |
|
|
|
const int xShift = 10; |
|
|
|
for(int i = 6; i < 20; i++) |
|
{ |
|
r1.at<double>(i) = 1; |
|
r2.at<double>(i + xShift) = 1; |
|
} |
|
|
|
Point2d phaseShift = phaseCorrelate(r1, r2); |
|
|
|
ASSERT_NEAR(phaseShift.x, (double)xShift, 1.); |
|
} |
|
|
|
////////////////////// DivSpectrums //////////////////////// |
|
class CV_DivSpectrumsTest : public cvtest::ArrayTest |
|
{ |
|
public: |
|
CV_DivSpectrumsTest(); |
|
protected: |
|
void run_func(); |
|
void get_test_array_types_and_sizes( int, vector<vector<Size> >& sizes, vector<vector<int> >& types ); |
|
void prepare_to_validation( int test_case_idx ); |
|
int flags; |
|
}; |
|
|
|
|
|
CV_DivSpectrumsTest::CV_DivSpectrumsTest() : flags(0) |
|
{ |
|
// Allocate test matrices. |
|
test_array[INPUT].push_back(NULL); // first input DFT as a CCS-packed array or complex matrix. |
|
test_array[INPUT].push_back(NULL); // second input DFT as a CCS-packed array or complex matrix. |
|
test_array[OUTPUT].push_back(NULL); // output DFT as a complex matrix. |
|
test_array[REF_OUTPUT].push_back(NULL); // reference output DFT as a complex matrix. |
|
test_array[TEMP].push_back(NULL); // first input DFT converted to a complex matrix. |
|
test_array[TEMP].push_back(NULL); // second input DFT converted to a complex matrix. |
|
test_array[TEMP].push_back(NULL); // output DFT as a CCV-packed array. |
|
} |
|
|
|
void CV_DivSpectrumsTest::get_test_array_types_and_sizes( int test_case_idx, vector<vector<Size> >& sizes, vector<vector<int> >& types ) |
|
{ |
|
cvtest::ArrayTest::get_test_array_types_and_sizes(test_case_idx, sizes, types); |
|
RNG& rng = ts->get_rng(); |
|
|
|
// Get the flag of the input. |
|
const int rand_int_flags = cvtest::randInt(rng); |
|
flags = rand_int_flags & (CV_DXT_MUL_CONJ | CV_DXT_ROWS); |
|
|
|
// Get input type. |
|
const int rand_int_type = cvtest::randInt(rng); |
|
int type; |
|
|
|
if (rand_int_type % 4) |
|
{ |
|
type = CV_32FC1; |
|
} |
|
else if (rand_int_type % 4 == 1) |
|
{ |
|
type = CV_32FC2; |
|
} |
|
else if (rand_int_type % 4 == 2) |
|
{ |
|
type = CV_64FC1; |
|
} |
|
else |
|
{ |
|
type = CV_64FC2; |
|
} |
|
|
|
for( size_t i = 0; i < types.size(); i++ ) |
|
{ |
|
for( size_t j = 0; j < types[i].size(); j++ ) |
|
{ |
|
types[i][j] = type; |
|
} |
|
} |
|
|
|
// Inputs are CCS-packed arrays. Prepare outputs and temporary inputs as complex matrices. |
|
if( type == CV_32FC1 || type == CV_64FC1 ) |
|
{ |
|
types[OUTPUT][0] += 8; |
|
types[REF_OUTPUT][0] += 8; |
|
types[TEMP][0] += 8; |
|
types[TEMP][1] += 8; |
|
} |
|
} |
|
|
|
/// Helper function to convert a ccs array of depth_t into a complex matrix. |
|
template<typename depth_t> |
|
static void convert_from_ccs_helper( const Mat& src0, const Mat& src1, Mat& dst ) |
|
{ |
|
const int cn = src0.channels(); |
|
int srcstep = cn; |
|
int dststep = 1; |
|
|
|
if( !dst.isContinuous() ) |
|
dststep = (int)(dst.step/dst.elemSize()); |
|
|
|
if( !src0.isContinuous() ) |
|
srcstep = (int)(src0.step/src0.elemSize1()); |
|
|
|
Complex<depth_t> *dst_data = dst.ptr<Complex<depth_t> >(); |
|
const depth_t* src0_data = src0.ptr<depth_t>(); |
|
const depth_t* src1_data = src1.ptr<depth_t>(); |
|
dst_data->re = src0_data[0]; |
|
dst_data->im = 0; |
|
const int n = dst.cols + dst.rows - 1; |
|
const int n2 = (n+1) >> 1; |
|
|
|
if( (n & 1) == 0 ) |
|
{ |
|
dst_data[n2*dststep].re = src0_data[(cn == 1 ? n-1 : n2)*srcstep]; |
|
dst_data[n2*dststep].im = 0; |
|
} |
|
|
|
int delta0 = srcstep; |
|
int delta1 = delta0 + (cn == 1 ? srcstep : 1); |
|
|
|
if( cn == 1 ) |
|
srcstep *= 2; |
|
|
|
for( int i = 1; i < n2; i++, delta0 += srcstep, delta1 += srcstep ) |
|
{ |
|
depth_t t0 = src0_data[delta0]; |
|
depth_t t1 = src0_data[delta1]; |
|
|
|
dst_data[i*dststep].re = t0; |
|
dst_data[i*dststep].im = t1; |
|
|
|
t0 = src1_data[delta0]; |
|
t1 = -src1_data[delta1]; |
|
|
|
dst_data[(n-i)*dststep].re = t0; |
|
dst_data[(n-i)*dststep].im = t1; |
|
} |
|
} |
|
|
|
/// Helper function to convert a ccs array into a complex matrix. |
|
static void convert_from_ccs( const Mat& src0, const Mat& src1, Mat& dst, const int flags ) |
|
{ |
|
if( dst.rows > 1 && (dst.cols > 1 || (flags & DFT_ROWS)) ) |
|
{ |
|
const int count = dst.rows; |
|
const int len = dst.cols; |
|
const bool is2d = (flags & DFT_ROWS) == 0; |
|
for( int i = 0; i < count; i++ ) |
|
{ |
|
const int j = !is2d || i == 0 ? i : count - i; |
|
const Mat& src0row = src0.row(i); |
|
const Mat& src1row = src1.row(j); |
|
Mat dstrow = dst.row(i); |
|
convert_from_ccs( src0row, src1row, dstrow, 0 ); |
|
} |
|
|
|
if( is2d ) |
|
{ |
|
const Mat& src0row = src0.col(0); |
|
Mat dstrow = dst.col(0); |
|
convert_from_ccs( src0row, src0row, dstrow, 0 ); |
|
|
|
if( (len & 1) == 0 ) |
|
{ |
|
const Mat& src0row_even = src0.col(src0.cols - 1); |
|
Mat dstrow_even = dst.col(len/2); |
|
convert_from_ccs( src0row_even, src0row_even, dstrow_even, 0 ); |
|
} |
|
} |
|
} |
|
else |
|
{ |
|
if( dst.depth() == CV_32F ) |
|
{ |
|
convert_from_ccs_helper<float>( src0, src1, dst ); |
|
} |
|
else |
|
{ |
|
convert_from_ccs_helper<double>( src0, src1, dst ); |
|
} |
|
} |
|
} |
|
|
|
/// Helper function to compute complex number (nu_re + nu_im * i) / (de_re + de_im * i). |
|
static std::pair<double, double> divide_complex_numbers( const double nu_re, const double nu_im, |
|
const double de_re, const double de_im, |
|
const bool conj_de ) |
|
{ |
|
if ( conj_de ) |
|
{ |
|
return divide_complex_numbers( nu_re, nu_im, de_re, -de_im, false /* conj_de */ ); |
|
} |
|
|
|
const double result_de = de_re * de_re + de_im * de_im + DBL_EPSILON; |
|
const double result_re = nu_re * de_re + nu_im * de_im; |
|
const double result_im = nu_re * (-de_im) + nu_im * de_re; |
|
return std::pair<double, double>(result_re / result_de, result_im / result_de); |
|
} |
|
|
|
/// Helper function to divide a DFT in src1 by a DFT in src2 with depths depth_t. The DFTs are |
|
/// complex matrices. |
|
template <typename depth_t> |
|
static void div_complex_helper( const Mat& src1, const Mat& src2, Mat& dst, int flags ) |
|
{ |
|
CV_Assert( src1.size == src2.size && src1.type() == src2.type() ); |
|
dst.create( src1.rows, src1.cols, src1.type() ); |
|
const int cn = src1.channels(); |
|
int cols = src1.cols * cn; |
|
|
|
for( int i = 0; i < dst.rows; i++ ) |
|
{ |
|
const depth_t *src1_data = src1.ptr<depth_t>(i); |
|
const depth_t *src2_data = src2.ptr<depth_t>(i); |
|
depth_t *dst_data = dst.ptr<depth_t>(i); |
|
for( int j = 0; j < cols; j += 2 ) |
|
{ |
|
std::pair<double, double> result = |
|
divide_complex_numbers( src1_data[j], src1_data[j + 1], |
|
src2_data[j], src2_data[j + 1], |
|
(flags & CV_DXT_MUL_CONJ) != 0 ); |
|
dst_data[j] = (depth_t)result.first; |
|
dst_data[j + 1] = (depth_t)result.second; |
|
} |
|
} |
|
} |
|
|
|
/// Helper function to divide a DFT in src1 by a DFT in src2. The DFTs are complex matrices. |
|
static void div_complex( const Mat& src1, const Mat& src2, Mat& dst, const int flags ) |
|
{ |
|
const int type = src1.type(); |
|
CV_Assert( type == CV_32FC2 || type == CV_64FC2 ); |
|
|
|
if ( src1.depth() == CV_32F ) |
|
{ |
|
return div_complex_helper<float>( src1, src2, dst, flags ); |
|
} |
|
else |
|
{ |
|
return div_complex_helper<double>( src1, src2, dst, flags ); |
|
} |
|
} |
|
|
|
void CV_DivSpectrumsTest::prepare_to_validation( int /* test_case_idx */ ) |
|
{ |
|
Mat &src1 = test_mat[INPUT][0]; |
|
Mat &src2 = test_mat[INPUT][1]; |
|
Mat &ref_dst = test_mat[REF_OUTPUT][0]; |
|
const int cn = src1.channels(); |
|
// Inputs are CCS-packed arrays. Convert them to complex matrices and get the expected output |
|
// as a complex matrix. |
|
if( cn == 1 ) |
|
{ |
|
Mat &converted_src1 = test_mat[TEMP][0]; |
|
Mat &converted_src2 = test_mat[TEMP][1]; |
|
convert_from_ccs( src1, src1, converted_src1, flags ); |
|
convert_from_ccs( src2, src2, converted_src2, flags ); |
|
div_complex( converted_src1, converted_src2, ref_dst, flags ); |
|
} |
|
// Inputs are complex matrices. Get the expected output as a complex matrix. |
|
else |
|
{ |
|
div_complex( src1, src2, ref_dst, flags ); |
|
} |
|
} |
|
|
|
void CV_DivSpectrumsTest::run_func() |
|
{ |
|
const Mat &src1 = test_mat[INPUT][0]; |
|
const Mat &src2 = test_mat[INPUT][1]; |
|
const int cn = src1.channels(); |
|
|
|
// Inputs are CCS-packed arrays. Get the output as a CCS-packed array and convert it to a |
|
// complex matrix. |
|
if ( cn == 1 ) |
|
{ |
|
Mat &dst = test_mat[TEMP][2]; |
|
cv::divSpectrums( src1, src2, dst, flags, (flags & CV_DXT_MUL_CONJ) != 0 ); |
|
Mat &converted_dst = test_mat[OUTPUT][0]; |
|
convert_from_ccs( dst, dst, converted_dst, flags ); |
|
} |
|
// Inputs are complex matrices. Get the output as a complex matrix. |
|
else |
|
{ |
|
Mat &dst = test_mat[OUTPUT][0]; |
|
cv::divSpectrums( src1, src2, dst, flags, (flags & CV_DXT_MUL_CONJ) != 0 ); |
|
} |
|
} |
|
|
|
TEST(Imgproc_DivSpectrums, accuracy) { CV_DivSpectrumsTest test; test.safe_run(); } |
|
|
|
}} // namespace
|
|
|