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.
180 lines
6.1 KiB
180 lines
6.1 KiB
// This file is part of OpenCV project. |
|
// It is subject to the license terms in the LICENSE file found in the top-level directory |
|
// of this distribution and at http://opencv.org/license.html. |
|
|
|
#include "test_precomp.hpp" |
|
#include "opencv2/core/mat.hpp" |
|
#include "opencv2/signal.hpp" |
|
|
|
#include <vector> |
|
#include <numeric> |
|
#include <cmath> |
|
#include <string> |
|
#include <random> |
|
#include <ctime> |
|
#include <algorithm> |
|
|
|
|
|
namespace opencv_test { namespace { |
|
|
|
using namespace cv; |
|
using namespace cv::signal; |
|
|
|
float MSE(const Mat1f &outSignal, const Mat1f &refSignal) |
|
{ |
|
float mse = 0.f; |
|
for (int i = 0; i < refSignal.cols; ++i) |
|
{ |
|
mse += powf(outSignal.at<float>(0,i) - refSignal.at<float>(0,i), 2.f); |
|
} |
|
mse /= refSignal.cols; |
|
return mse; |
|
} |
|
|
|
// RRMSE = sqrt( MSE / SUM(sqr(refSignal(i))) ) * 100% |
|
float RRMSE(const Mat1f &outSignal, const Mat1f &refSignal) |
|
{ |
|
float rrmse = 0.f; |
|
float div = 0.f; |
|
rrmse = MSE(outSignal, refSignal); |
|
for (int i = 0; i < refSignal.cols; ++i) |
|
{ |
|
div += powf(refSignal.at<float>(0,i), 2.f); |
|
} |
|
rrmse /= div; |
|
rrmse = sqrt(rrmse) * 100; |
|
return rrmse; |
|
} |
|
|
|
TEST(ResampleTest, simple_resample_test_up) |
|
{ |
|
Mat1f sample_signal(Size(1000U,1U)); |
|
Mat1f outSignal; |
|
std::iota(sample_signal.begin(), sample_signal.end(), 1.f); |
|
resampleSignal(sample_signal, outSignal, 16000U, 32000U); |
|
vector<float> ref(outSignal.cols, 0.f); |
|
for (uint32_t i = 0U; i < 2000U; ++i) |
|
{ |
|
ref[i] = static_cast<float>(i) / 2.f; |
|
} |
|
EXPECT_NEAR(cvtest::norm(ref, NORM_L2) / cvtest::norm(outSignal, NORM_L2), 1.0f, 0.05f) |
|
<< "\nL2_norm(refSignal) = " << cvtest::norm(ref, NORM_L2) |
|
<< "\nL2_norm(outSignal) = " << cvtest::norm(outSignal, NORM_L2); |
|
} |
|
|
|
TEST(ResampleTest, resample_sin_signal_up_2) |
|
{ |
|
Mat1f sample_signal(Size(1000U,1U)); |
|
Mat1f outSignal; |
|
for (uint32_t i = 0U; i < (uint32_t)sample_signal.cols; ++i) |
|
{ |
|
sample_signal.at<float>(0, i) = sinf(float(i)); |
|
} |
|
resampleSignal(sample_signal, outSignal, 16000U, 32000U); |
|
vector<float> ref(outSignal.cols, 0.f); |
|
for (uint32_t i = 0U; i < 2000U; ++i) |
|
{ |
|
ref[i] = sin(static_cast<float>(i) / 2.f); |
|
} |
|
EXPECT_NEAR(cvtest::norm(ref, NORM_L2) / cvtest::norm(outSignal, NORM_L2), 1.0f, 0.05f) |
|
<< "\nL2_norm(refSignal) = " << cvtest::norm(ref, NORM_L2) |
|
<< "\nL2_norm(outSignal) = " << cvtest::norm(outSignal, NORM_L2); |
|
} |
|
|
|
TEST(ResampleTest, simple_resample_test_dn) |
|
{ |
|
Mat1f sample_signal(Size(1000U,1U)); |
|
Mat1f outSignal; |
|
std::iota(sample_signal.begin(), sample_signal.end(), 1.f); |
|
resampleSignal(sample_signal, outSignal, 32000U, 16000U); |
|
vector<float> ref(outSignal.cols, 0.f); |
|
for (uint32_t i = 0U; i < 500U; ++i) |
|
{ |
|
ref[i] = static_cast<float>(i) * 2.f; |
|
} |
|
EXPECT_NEAR(cvtest::norm(ref, NORM_L2) / cvtest::norm(outSignal, NORM_L2), 1.0f, 0.05f) |
|
<< "\nL2_norm(refSignal) = " << cvtest::norm(ref, NORM_L2) |
|
<< "\nL2_norm(outSignal) = " << cvtest::norm(outSignal, NORM_L2); |
|
} |
|
|
|
TEST(ResampleTest, resample_sin_signal_dn_2) |
|
{ |
|
Mat1f sample_signal(Size(1000U,1U)); |
|
Mat1f outSignal; |
|
for (uint32_t i = 0U; i < (uint32_t)sample_signal.cols; ++i) |
|
{ |
|
sample_signal.at<float>(0, i) = sinf(float(i)); |
|
} |
|
resampleSignal(sample_signal, outSignal, 32000U, 16000U); |
|
std::vector<float> ref(outSignal.cols, 0.f); |
|
for (uint32_t i = 0U; i < 500U; ++i) |
|
{ |
|
ref[i] = sin(static_cast<float>(i) * 2.f); |
|
} |
|
EXPECT_NEAR(cvtest::norm(ref, NORM_L2) / cvtest::norm(outSignal, NORM_L2), 1.0f, 0.05f) |
|
<< "\nL2_norm(refSignal) = " << cvtest::norm(ref, NORM_L2) |
|
<< "\nL2_norm(outSignal) = " << cvtest::norm(outSignal, NORM_L2); |
|
} |
|
|
|
// produce 1s of signal @ freq hz |
|
void fillSignal(uint32_t freq, Mat1f &inSignal) |
|
{ |
|
static std::default_random_engine e((unsigned int)(time(NULL))); |
|
static std::uniform_real_distribution<> dis(0, 1); // range [0, 1) |
|
static auto a = dis(e), b = dis(e), c = dis(e); |
|
uint32_t idx = 0; |
|
std::generate(inSignal.begin(), inSignal.end(), [&]() |
|
{ |
|
float ret = static_cast<float>(sin(idx/(float)freq + a) + 3 * sin(CV_PI / 4 * (idx/(float)freq + b)) |
|
+ 5 * sin(CV_PI/12 * idx/(float)freq + c) + 20*cos(idx/(float)freq*4000)); |
|
idx++; |
|
return ret; |
|
}); |
|
} |
|
|
|
class ResampleTestClass : public testing::TestWithParam<std::tuple<int, int>> |
|
{ |
|
}; |
|
|
|
TEST_P(ResampleTestClass, func_test) { |
|
auto params1 = GetParam(); |
|
uint32_t inFreq = std::get<0>(params1); |
|
uint32_t outFreq = std::get<1>(params1); |
|
// 1 second @ inFreq hz |
|
Mat1f inSignal(Size(inFreq, 1U)); |
|
Mat1f outSignal; |
|
// generating testing function as a sum of different sinusoids |
|
fillSignal(inFreq, inSignal); |
|
resampleSignal(inSignal, outSignal, inFreq, outFreq); |
|
// reference signal |
|
// 1 second @ outFreq hz |
|
Mat1f refSignal(Size(outFreq, 1U)); |
|
fillSignal(outFreq, refSignal); |
|
// calculating maxDiff |
|
float maxDiff = 0.f; |
|
// exclude 2 elements and last 2 elements from testing |
|
for (uint32_t i = 2; i < (uint32_t)refSignal.cols - 2; ++i) |
|
{ |
|
if(maxDiff < abs(refSignal.at<float>(0,i) - outSignal.at<float>(0,i))) |
|
{ |
|
maxDiff = abs(refSignal.at<float>(0,i) - outSignal.at<float>(0,i)); |
|
} |
|
} |
|
auto max = std::max_element(outSignal.begin(), outSignal.end()); |
|
float maxDiffRel = maxDiff / (*max); |
|
EXPECT_LE(maxDiffRel, 0.35f); |
|
// calculating relative error of L2 norms |
|
EXPECT_NEAR(abs(cvtest::norm(outSignal, NORM_L2) - cvtest::norm(refSignal, NORM_L2)) / |
|
cvtest::norm(refSignal, NORM_L2), 0.0f, 0.05f); |
|
// calculating relative mean squared error |
|
float rrmse = RRMSE(outSignal, refSignal); |
|
// 1% error |
|
EXPECT_LE(rrmse, 1.f); |
|
} |
|
|
|
INSTANTIATE_TEST_CASE_P(RefSignalTestingCase, |
|
ResampleTestClass, |
|
::testing::Combine(testing::Values(16000, 32000, 44100, 48000), |
|
testing::Values(16000, 32000, 44100, 48000))); |
|
|
|
}} // namespace
|
|
|