vendor: OpenCV 5.0.0 snapshot at 755e50675d97db9b7d449d8bd6b09888646f6c6e

This commit is contained in:
Gitea Mirror Bot
2026-08-22 00:11:13 +08:00
commit 12022378a3
3872 changed files with 2513409 additions and 0 deletions
@@ -0,0 +1,179 @@
// 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"
namespace opencv_test {
Ptr<AdaptiveManifoldFilter> createAMFilterRefImpl(double sigma_s, double sigma_r, bool adjust_outliers = false);
namespace {
#ifndef SQR
#define SQR(x) ((x)*(x))
#endif
static string getOpenCVExtraDir()
{
return cvtest::TS::ptr()->get_data_path();
}
static void checkSimilarity(InputArray res, InputArray ref, double maxNormInf = 1, double maxNormL2 = 1.0 / 64)
{
double normInf = cvtest::norm(res, ref, NORM_INF);
double normL2 = cvtest::norm(res, ref, NORM_L2) / res.total();
if (maxNormInf >= 0) { EXPECT_LE(normInf, maxNormInf); }
if (maxNormL2 >= 0) { EXPECT_LE(normL2, maxNormL2); }
}
TEST(AdaptiveManifoldTest, SplatSurfaceAccuracy)
{
RNG rnd(0);
for (int i = 0; i < 5; i++)
{
Size sz(rnd.uniform(512, 1024), rnd.uniform(512, 1024));
int guideCn = rnd.uniform(1, 8);
Mat guide(sz, CV_MAKE_TYPE(CV_32F, guideCn));
randu(guide, 0, 1);
Scalar surfaceValue;
int srcCn = rnd.uniform(1, 4);
rnd.fill(surfaceValue, RNG::UNIFORM, 0, 255);
Mat src(sz, CV_MAKE_TYPE(CV_8U, srcCn), surfaceValue);
double sigma_s = rnd.uniform(1.0, 50.0);
double sigma_r = rnd.uniform(0.1, 0.9);
Mat res;
amFilter(guide, src, res, sigma_s, sigma_r, false);
double normInf = cvtest::norm(src, res, NORM_INF);
EXPECT_EQ(normInf, 0);
}
}
TEST(AdaptiveManifoldTest, AuthorsReferenceAccuracy)
{
String srcImgPath = "cv/edgefilter/kodim23.png";
String refPaths[] =
{
"cv/edgefilter/amf/kodim23_amf_ss5_sr0.3_ref.png",
"cv/edgefilter/amf/kodim23_amf_ss30_sr0.1_ref.png",
"cv/edgefilter/amf/kodim23_amf_ss50_sr0.3_ref.png"
};
pair<double, double> refParams[] =
{
make_pair(5.0, 0.3),
make_pair(30.0, 0.1),
make_pair(50.0, 0.3)
};
String refOutliersPaths[] =
{
"cv/edgefilter/amf/kodim23_amf_ss5_sr0.1_outliers_ref.png",
"cv/edgefilter/amf/kodim23_amf_ss15_sr0.3_outliers_ref.png",
"cv/edgefilter/amf/kodim23_amf_ss50_sr0.5_outliers_ref.png"
};
pair<double, double> refOutliersParams[] =
{
make_pair(5.0, 0.1),
make_pair(15.0, 0.3),
make_pair(50.0, 0.5),
};
Mat srcImg = imread(getOpenCVExtraDir() + srcImgPath);
ASSERT_TRUE(!srcImg.empty());
for (int i = 0; i < 3; i++)
{
Mat refRes = imread(getOpenCVExtraDir() + refPaths[i]);
double sigma_s = refParams[i].first;
double sigma_r = refParams[i].second;
ASSERT_TRUE(!refRes.empty());
Mat res;
Ptr<AdaptiveManifoldFilter> amf = createAMFilter(sigma_s, sigma_r, false);
amf->setUseRNG(false);
amf->filter(srcImg, res, srcImg);
amf->collectGarbage();
checkSimilarity(res, refRes);
}
for (int i = 0; i < 3; i++)
{
Mat refRes = imread(getOpenCVExtraDir() + refOutliersPaths[i]);
double sigma_s = refOutliersParams[i].first;
double sigma_r = refOutliersParams[i].second;
ASSERT_TRUE(!refRes.empty());
Mat res;
Ptr<AdaptiveManifoldFilter> amf = createAMFilter(sigma_s, sigma_r, true);
amf->setUseRNG(false);
amf->filter(srcImg, res, srcImg);
amf->collectGarbage();
checkSimilarity(res, refRes);
}
}
typedef tuple<string, string> AMRefTestParams;
typedef TestWithParam<AMRefTestParams> AdaptiveManifoldRefImplTest;
TEST_P(AdaptiveManifoldRefImplTest, RefImplAccuracy)
{
AMRefTestParams params = GetParam();
string guideFileName = get<0>(params);
string srcFileName = get<1>(params);
Mat guide = imread(getOpenCVExtraDir() + guideFileName);
Mat src = imread(getOpenCVExtraDir() + srcFileName);
ASSERT_TRUE(!guide.empty() && !src.empty());
int seed = 10 * (int)guideFileName.length() + (int)srcFileName.length();
RNG rnd(seed);
//inconsistent downsample/upsample operations in reference implementation
Size dstSize((guide.cols + 15) & ~15, (guide.rows + 15) & ~15);
resize(guide, guide, dstSize, 0, 0, INTER_LINEAR_EXACT);
resize(src, src, dstSize, 0, 0, INTER_LINEAR_EXACT);
int nThreads = cv::getNumThreads();
if (nThreads == 1)
throw SkipTestException("Single thread environment");
for (int iter = 0; iter < 4; iter++)
{
double sigma_s = rnd.uniform(1.0, 50.0);
double sigma_r = rnd.uniform(0.1, 0.9);
bool adjust_outliers = (iter % 2 == 0);
cv::setNumThreads(nThreads);
Mat res;
amFilter(guide, src, res, sigma_s, sigma_r, adjust_outliers);
cv::setNumThreads(1);
Mat resRef;
Ptr<AdaptiveManifoldFilter> amf = createAMFilterRefImpl(sigma_s, sigma_r, adjust_outliers);
amf->filter(src, resRef, guide);
//results of reference implementation may differ on small sigma_s into small isolated region
//due to low single-precision floating point numbers accuracy
//therefore the threshold of inf norm was increased
checkSimilarity(res, resRef, 25);
}
}
INSTANTIATE_TEST_CASE_P(TypicalSet, AdaptiveManifoldRefImplTest,
Combine(
Values("cv/edgefilter/kodim23.png", "cv/npr/test4.png"),
Values("cv/edgefilter/kodim23.png", "cv/npr/test4.png")
));
}} // namespace
@@ -0,0 +1,947 @@
// 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.
/*
* The MIT License(MIT)
*
* Copyright(c) 2013 Vladislav Vinogradov
*
* Permission is hereby granted, free of charge, to any person obtaining a copy of
* this software and associated documentation files(the "Software"), to deal in
* the Software without restriction, including without limitation the rights to
* use, copy, modify, merge, publish, distribute, sublicense, and / or sell copies of
* the Software, and to permit persons to whom the Software is furnished to do so,
* subject to the following conditions :
*
* The above copyright notice and this permission notice shall be included in all
* copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
* FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.IN NO EVENT SHALL THE AUTHORS OR
* COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
* IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
* CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
*/
#include "test_precomp.hpp"
#include <opencv2/core/private.hpp>
namespace opencv_test { namespace {
using namespace cv::ximgproc;
struct Buf
{
Mat_<Point3f> eta_1;
Mat_<uchar> cluster_1;
Mat_<Point3f> tilde_dst;
Mat_<float> alpha;
Mat_<Point3f> diff;
Mat_<Point3f> dst;
Mat_<float> V;
Mat_<Point3f> dIcdx;
Mat_<Point3f> dIcdy;
Mat_<float> dIdx;
Mat_<float> dIdy;
Mat_<float> dHdx;
Mat_<float> dVdy;
Mat_<float> t;
Mat_<float> theta_masked;
Mat_<Point3f> mul;
Mat_<Point3f> numerator;
Mat_<float> denominator;
Mat_<Point3f> numerator_filtered;
Mat_<float> denominator_filtered;
Mat_<Point3f> X;
Mat_<Point3f> eta_k_small;
Mat_<Point3f> eta_k_big;
Mat_<Point3f> X_squared;
Mat_<float> pixel_dist_to_manifold_squared;
Mat_<float> gaussian_distance_weights;
Mat_<Point3f> Psi_splat;
Mat_<Vec4f> Psi_splat_joined;
Mat_<Vec4f> Psi_splat_joined_resized;
Mat_<Vec4f> blurred_projected_values;
Mat_<Point3f> w_ki_Psi_blur;
Mat_<float> w_ki_Psi_blur_0;
Mat_<Point3f> w_ki_Psi_blur_resized;
Mat_<float> w_ki_Psi_blur_0_resized;
Mat_<float> rand_vec;
Mat_<float> v1;
Mat_<float> Nx_v1_mult;
Mat_<float> theta;
std::vector<Mat_<Point3f> > eta_minus;
std::vector<Mat_<uchar> > cluster_minus;
std::vector<Mat_<Point3f> > eta_plus;
std::vector<Mat_<uchar> > cluster_plus;
void release();
};
void Buf::release()
{
eta_1.release();
cluster_1.release();
tilde_dst.release();
alpha.release();
diff.release();
dst.release();
V.release();
dIcdx.release();
dIcdy.release();
dIdx.release();
dIdy.release();
dHdx.release();
dVdy.release();
t.release();
theta_masked.release();
mul.release();
numerator.release();
denominator.release();
numerator_filtered.release();
denominator_filtered.release();
X.release();
eta_k_small.release();
eta_k_big.release();
X_squared.release();
pixel_dist_to_manifold_squared.release();
gaussian_distance_weights.release();
Psi_splat.release();
Psi_splat_joined.release();
Psi_splat_joined_resized.release();
blurred_projected_values.release();
w_ki_Psi_blur.release();
w_ki_Psi_blur_0.release();
w_ki_Psi_blur_resized.release();
w_ki_Psi_blur_0_resized.release();
rand_vec.release();
v1.release();
Nx_v1_mult.release();
theta.release();
eta_minus.clear();
cluster_minus.clear();
eta_plus.clear();
cluster_plus.clear();
}
class AdaptiveManifoldFilterRefImpl : public AdaptiveManifoldFilter
{
public:
AdaptiveManifoldFilterRefImpl();
void filter(InputArray src, OutputArray dst, InputArray joint) CV_OVERRIDE;
void collectGarbage() CV_OVERRIDE;
inline double getSigmaS() const CV_OVERRIDE { return sigma_s_; }
inline void setSigmaS(double val) CV_OVERRIDE { sigma_s_ = val; }
inline double getSigmaR() const CV_OVERRIDE { return sigma_r_; }
inline void setSigmaR(double val) CV_OVERRIDE { sigma_r_ = val; }
inline int getTreeHeight() const CV_OVERRIDE { return tree_height_; }
inline void setTreeHeight(int val) CV_OVERRIDE { tree_height_ = val; }
inline int getPCAIterations() const CV_OVERRIDE { return num_pca_iterations_; }
inline void setPCAIterations(int val) CV_OVERRIDE { num_pca_iterations_ = val; }
inline bool getAdjustOutliers() const CV_OVERRIDE { return adjust_outliers_; }
inline void setAdjustOutliers(bool val) CV_OVERRIDE { adjust_outliers_ = val; }
inline bool getUseRNG() const CV_OVERRIDE { return useRNG; }
inline void setUseRNG(bool val) CV_OVERRIDE { useRNG = val; }
protected:
bool adjust_outliers_;
double sigma_s_;
double sigma_r_;
int tree_height_;
int num_pca_iterations_;
bool useRNG;
private:
void buildManifoldsAndPerformFiltering(const Mat_<Point3f>& eta_k, const Mat_<uchar>& cluster_k, int current_tree_level);
Buf buf_;
Mat_<Point3f> src_f_;
Mat_<Point3f> src_joint_f_;
Mat_<Point3f> sum_w_ki_Psi_blur_;
Mat_<float> sum_w_ki_Psi_blur_0_;
Mat_<float> min_pixel_dist_to_manifold_squared_;
RNG rng_;
int cur_tree_height_;
float sigma_r_over_sqrt_2_;
};
AdaptiveManifoldFilterRefImpl::AdaptiveManifoldFilterRefImpl()
{
sigma_s_ = 16.0;
sigma_r_ = 0.2;
tree_height_ = -1;
num_pca_iterations_ = 1;
adjust_outliers_ = false;
useRNG = true;
}
void AdaptiveManifoldFilterRefImpl::collectGarbage()
{
buf_.release();
src_f_.release();
src_joint_f_.release();
sum_w_ki_Psi_blur_.release();
sum_w_ki_Psi_blur_0_.release();
min_pixel_dist_to_manifold_squared_.release();
}
inline double Log2(double n)
{
return log(n) / log(2.0);
}
inline int computeManifoldTreeHeight(double sigma_s, double sigma_r)
{
const double Hs = floor(Log2(sigma_s)) - 1.0;
const double Lr = 1.0 - sigma_r;
return max(2, static_cast<int>(ceil(Hs * Lr)));
}
/*
void ensureSizeIsEnough(int rows, int cols, int type, Mat& m)
{
if (m.empty() || m.type() != type || m.data != m.datastart)
m.create(rows, cols, type);
else
{
const size_t esz = m.elemSize();
const ptrdiff_t delta2 = m.dataend - m.datastart;
const size_t minstep = m.cols * esz;
Size wholeSize;
wholeSize.height = std::max(static_cast<int>((delta2 - minstep) / m.step + 1), m.rows);
wholeSize.width = std::max(static_cast<int>((delta2 - m.step * (wholeSize.height - 1)) / esz), m.cols);
if (wholeSize.height < rows || wholeSize.width < cols)
m.create(rows, cols, type);
else
{
m.cols = cols;
m.rows = rows;
}
}
}
inline void ensureSizeIsEnough(Size size, int type, Mat& m)
{
ensureSizeIsEnough(size.height, size.width, type, m);
}
*/
template <typename T>
inline void ensureSizeIsEnough(int rows, int cols, Mat_<T>& m)
{
if (m.empty() || m.data != m.datastart)
m.create(rows, cols);
else
{
const size_t esz = m.elemSize();
const ptrdiff_t delta2 = m.dataend - m.datastart;
const size_t minstep = m.cols * esz;
Size wholeSize;
wholeSize.height = std::max(static_cast<int>((delta2 - minstep) / m.step + 1), m.rows);
wholeSize.width = std::max(static_cast<int>((delta2 - m.step * (wholeSize.height - 1)) / esz), m.cols);
if (wholeSize.height < rows || wholeSize.width < cols)
m.create(rows, cols);
else
{
m.cols = cols;
m.rows = rows;
}
}
}
template <typename T>
inline void ensureSizeIsEnough(Size size, Mat_<T>& m)
{
ensureSizeIsEnough(size.height, size.width, m);
}
template <typename T>
void h_filter(const Mat_<T>& src, Mat_<T>& dst, float sigma)
{
CV_DbgAssert( src.depth() == CV_32F );
const float a = exp(-sqrt(2.0f) / sigma);
ensureSizeIsEnough(src.size(), dst);
for (int y = 0; y < src.rows; ++y)
{
const T* src_row = src[y];
T* dst_row = dst[y];
dst_row[0] = src_row[0];
for (int x = 1; x < src.cols; ++x)
{
//dst_row[x] = src_row[x] + a * (src_row[x - 1] - src_row[x]);
dst_row[x] = src_row[x] + a * (dst_row[x - 1] - src_row[x]); //!!!
}
for (int x = src.cols - 2; x >= 0; --x)
{
dst_row[x] = dst_row[x] + a * (dst_row[x + 1] - dst_row[x]);
}
}
for (int y = 1; y < src.rows; ++y)
{
T* dst_cur_row = dst[y];
T* dst_prev_row = dst[y - 1];
for (int x = 0; x < src.cols; ++x)
{
dst_cur_row[x] = dst_cur_row[x] + a * (dst_prev_row[x] - dst_cur_row[x]);
}
}
for (int y = src.rows - 2; y >= 0; --y)
{
T* dst_cur_row = dst[y];
T* dst_prev_row = dst[y + 1];
for (int x = 0; x < src.cols; ++x)
{
dst_cur_row[x] = dst_cur_row[x] + a * (dst_prev_row[x] - dst_cur_row[x]);
}
}
}
template <typename T>
void rdivide(const Mat_<T>& a, const Mat_<float>& b, Mat_<T>& dst)
{
CV_DbgAssert( a.depth() == CV_32F );
CV_DbgAssert( a.size() == b.size() );
ensureSizeIsEnough(a.size(), dst);
dst.setTo(0);
for (int y = 0; y < a.rows; ++y)
{
const T* a_row = a[y];
const float* b_row = b[y];
T* dst_row = dst[y];
for (int x = 0; x < a.cols; ++x)
{
//if (b_row[x] > numeric_limits<float>::epsilon())
dst_row[x] = a_row[x] * (1.0f / b_row[x]);
}
}
}
template <typename T>
void times(const Mat_<T>& a, const Mat_<float>& b, Mat_<T>& dst)
{
CV_DbgAssert( a.depth() == CV_32F );
CV_DbgAssert( a.size() == b.size() );
ensureSizeIsEnough(a.size(), dst);
for (int y = 0; y < a.rows; ++y)
{
const T* a_row = a[y];
const float* b_row = b[y];
T* dst_row = dst[y];
for (int x = 0; x < a.cols; ++x)
{
dst_row[x] = a_row[x] * b_row[x];
}
}
}
void AdaptiveManifoldFilterRefImpl::filter(InputArray _src, OutputArray _dst, InputArray _joint)
{
const Mat src = _src.getMat();
const Mat src_joint = _joint.getMat();
const Size srcSize = src.size();
CV_Assert( src.type() == CV_8UC3 );
CV_Assert( src_joint.empty() || (src_joint.type() == src.type() && src_joint.size() == srcSize) );
ensureSizeIsEnough(srcSize, src_f_);
src.convertTo(src_f_, src_f_.type(), 1.0 / 255.0);
ensureSizeIsEnough(srcSize, sum_w_ki_Psi_blur_);
sum_w_ki_Psi_blur_.setTo(Scalar::all(0));
ensureSizeIsEnough(srcSize, sum_w_ki_Psi_blur_0_);
sum_w_ki_Psi_blur_0_.setTo(Scalar::all(0));
ensureSizeIsEnough(srcSize, min_pixel_dist_to_manifold_squared_);
min_pixel_dist_to_manifold_squared_.setTo(Scalar::all(numeric_limits<float>::max()));
// If the tree_height was not specified, compute it using Eq. (10) of our paper.
cur_tree_height_ = tree_height_ > 0 ? tree_height_ : computeManifoldTreeHeight(sigma_s_, sigma_r_);
// If no joint signal was specified, use the original signal
ensureSizeIsEnough(srcSize, src_joint_f_);
if (src_joint.empty())
src_f_.copyTo(src_joint_f_);
else
src_joint.convertTo(src_joint_f_, src_joint_f_.type(), 1.0 / 255.0);
// Use the center pixel as seed to random number generation.
const double seedCoef = src_joint_f_(src_joint_f_.rows / 2, src_joint_f_.cols / 2).x;
const uint64 baseCoef = numeric_limits<uint64>::max() / 0xFFFF;
rng_.state = static_cast<uint64>(baseCoef*seedCoef);
// Dividing the covariance matrix by 2 is equivalent to dividing the standard deviations by sqrt(2).
sigma_r_over_sqrt_2_ = static_cast<float>(sigma_r_ / sqrt(2.0));
// Algorithm 1, Step 1: compute the first manifold by low-pass filtering.
h_filter(src_joint_f_, buf_.eta_1, static_cast<float>(sigma_s_));
ensureSizeIsEnough(srcSize, buf_.cluster_1);
buf_.cluster_1.setTo(Scalar::all(1));
buf_.eta_minus.resize(cur_tree_height_);
buf_.cluster_minus.resize(cur_tree_height_);
buf_.eta_plus.resize(cur_tree_height_);
buf_.cluster_plus.resize(cur_tree_height_);
buildManifoldsAndPerformFiltering(buf_.eta_1, buf_.cluster_1, 1);
// Compute the filter response by normalized convolution -- Eq. (4)
rdivide(sum_w_ki_Psi_blur_, sum_w_ki_Psi_blur_0_, buf_.tilde_dst);
if (!adjust_outliers_)
{
buf_.tilde_dst.convertTo(_dst, CV_8U, 255.0);
}
else
{
// Adjust the filter response for outlier pixels -- Eq. (10)
ensureSizeIsEnough(srcSize, buf_.alpha);
exp(min_pixel_dist_to_manifold_squared_ * (-0.5 / sigma_r_ / sigma_r_), buf_.alpha);
ensureSizeIsEnough(srcSize, buf_.diff);
subtract(buf_.tilde_dst, src_f_, buf_.diff);
times(buf_.diff, buf_.alpha, buf_.diff);
ensureSizeIsEnough(srcSize, buf_.dst);
cv::add(src_f_, buf_.diff, buf_.dst); // TODO cvtest
buf_.dst.convertTo(_dst, CV_8U, 255.0);
}
}
inline double floor_to_power_of_two(double r)
{
return pow(2.0, floor(Log2(r)));
}
void channelsSum(const Mat_<Point3f>& src, Mat_<float>& dst)
{
ensureSizeIsEnough(src.size(), dst);
for (int y = 0; y < src.rows; ++y)
{
const Point3f* src_row = src[y];
float* dst_row = dst[y];
for (int x = 0; x < src.cols; ++x)
{
const Point3f src_val = src_row[x];
dst_row[x] = src_val.x + src_val.y + src_val.z;
}
}
}
void phi(const Mat_<float>& src, Mat_<float>& dst, float sigma)
{
ensureSizeIsEnough(src.size(), dst);
for (int y = 0; y < dst.rows; ++y)
{
const float* src_row = src[y];
float* dst_row = dst[y];
for (int x = 0; x < dst.cols; ++x)
{
dst_row[x] = exp(-0.5f * src_row[x] / sigma / sigma);
}
}
}
void catCn(const Mat_<Point3f>& a, const Mat_<float>& b, Mat_<Vec4f>& dst)
{
ensureSizeIsEnough(a.size(), dst);
for (int y = 0; y < a.rows; ++y)
{
const Point3f* a_row = a[y];
const float* b_row = b[y];
Vec4f* dst_row = dst[y];
for (int x = 0; x < a.cols; ++x)
{
const Point3f a_val = a_row[x];
const float b_val = b_row[x];
dst_row[x] = Vec4f(a_val.x, a_val.y, a_val.z, b_val);
}
}
}
void diffY(const Mat_<Point3f>& src, Mat_<Point3f>& dst)
{
ensureSizeIsEnough(src.rows - 1, src.cols, dst);
for (int y = 0; y < src.rows - 1; ++y)
{
const Point3f* src_cur_row = src[y];
const Point3f* src_next_row = src[y + 1];
Point3f* dst_row = dst[y];
for (int x = 0; x < src.cols; ++x)
{
dst_row[x] = src_next_row[x] - src_cur_row[x];
}
}
}
void diffX(const Mat_<Point3f>& src, Mat_<Point3f>& dst)
{
ensureSizeIsEnough(src.rows, src.cols - 1, dst);
for (int y = 0; y < src.rows; ++y)
{
const Point3f* src_row = src[y];
Point3f* dst_row = dst[y];
for (int x = 0; x < src.cols - 1; ++x)
{
dst_row[x] = src_row[x + 1] - src_row[x];
}
}
}
void TransformedDomainRecursiveFilter(const Mat_<Vec4f>& I, const Mat_<float>& DH, const Mat_<float>& DV, Mat_<Vec4f>& dst, float sigma, Buf& buf)
{
CV_DbgAssert( I.size() == DH.size() );
const float a = exp(-sqrt(2.0f) / sigma);
ensureSizeIsEnough(I.size(), dst);
I.copyTo(dst);
ensureSizeIsEnough(DH.size(), buf.V);
for (int y = 0; y < DH.rows; ++y)
{
const float* D_row = DH[y];
float* V_row = buf.V[y];
for (int x = 0; x < DH.cols; ++x)
{
V_row[x] = pow(a, D_row[x]);
}
}
for (int y = 0; y < I.rows; ++y)
{
const float* V_row = buf.V[y];
Vec4f* dst_row = dst[y];
for (int x = 1; x < I.cols; ++x)
{
Vec4f dst_cur_val = dst_row[x];
const Vec4f dst_prev_val = dst_row[x - 1];
const float V_val = V_row[x];
dst_cur_val[0] += V_val * (dst_prev_val[0] - dst_cur_val[0]);
dst_cur_val[1] += V_val * (dst_prev_val[1] - dst_cur_val[1]);
dst_cur_val[2] += V_val * (dst_prev_val[2] - dst_cur_val[2]);
dst_cur_val[3] += V_val * (dst_prev_val[3] - dst_cur_val[3]);
dst_row[x] = dst_cur_val;
}
for (int x = I.cols - 2; x >= 0; --x)
{
Vec4f dst_cur_val = dst_row[x];
const Vec4f dst_prev_val = dst_row[x + 1];
//const float V_val = V_row[x];
const float V_val = V_row[x+1];
dst_cur_val[0] += V_val * (dst_prev_val[0] - dst_cur_val[0]);
dst_cur_val[1] += V_val * (dst_prev_val[1] - dst_cur_val[1]);
dst_cur_val[2] += V_val * (dst_prev_val[2] - dst_cur_val[2]);
dst_cur_val[3] += V_val * (dst_prev_val[3] - dst_cur_val[3]);
dst_row[x] = dst_cur_val;
}
}
for (int y = 0; y < DV.rows; ++y)
{
const float* D_row = DV[y];
float* V_row = buf.V[y];
for (int x = 0; x < DV.cols; ++x)
{
V_row[x] = pow(a, D_row[x]);
}
}
for (int y = 1; y < I.rows; ++y)
{
const float* V_row = buf.V[y];
Vec4f* dst_cur_row = dst[y];
Vec4f* dst_prev_row = dst[y - 1];
for (int x = 0; x < I.cols; ++x)
{
Vec4f dst_cur_val = dst_cur_row[x];
const Vec4f dst_prev_val = dst_prev_row[x];
const float V_val = V_row[x];
dst_cur_val[0] += V_val * (dst_prev_val[0] - dst_cur_val[0]);
dst_cur_val[1] += V_val * (dst_prev_val[1] - dst_cur_val[1]);
dst_cur_val[2] += V_val * (dst_prev_val[2] - dst_cur_val[2]);
dst_cur_val[3] += V_val * (dst_prev_val[3] - dst_cur_val[3]);
dst_cur_row[x] = dst_cur_val;
}
}
for (int y = I.rows - 2; y >= 0; --y)
{
//const float* V_row = buf.V[y];
const float* V_row = buf.V[y + 1];
Vec4f* dst_cur_row = dst[y];
Vec4f* dst_prev_row = dst[y + 1];
for (int x = 0; x < I.cols; ++x)
{
Vec4f dst_cur_val = dst_cur_row[x];
const Vec4f dst_prev_val = dst_prev_row[x];
const float V_val = V_row[x];
dst_cur_val[0] += V_val * (dst_prev_val[0] - dst_cur_val[0]);
dst_cur_val[1] += V_val * (dst_prev_val[1] - dst_cur_val[1]);
dst_cur_val[2] += V_val * (dst_prev_val[2] - dst_cur_val[2]);
dst_cur_val[3] += V_val * (dst_prev_val[3] - dst_cur_val[3]);
dst_cur_row[x] = dst_cur_val;
}
}
}
void RF_filter(const Mat_<Vec4f>& src, const Mat_<Point3f>& src_joint, Mat_<Vec4f>& dst, float sigma_s, float sigma_r, Buf& buf)
{
CV_DbgAssert( src_joint.size() == src.size() );
diffX(src_joint, buf.dIcdx);
diffY(src_joint, buf.dIcdy);
ensureSizeIsEnough(src.size(), buf.dIdx);
buf.dIdx.setTo(Scalar::all(0));
for (int y = 0; y < src.rows; ++y)
{
const Point3f* dIcdx_row = buf.dIcdx[y];
float* dIdx_row = buf.dIdx[y];
for (int x = 1; x < src.cols; ++x)
{
const Point3f val = dIcdx_row[x - 1];
dIdx_row[x] = val.dot(val);
}
}
ensureSizeIsEnough(src.size(), buf.dIdy);
buf.dIdy.setTo(Scalar::all(0));
for (int y = 1; y < src.rows; ++y)
{
const Point3f* dIcdy_row = buf.dIcdy[y - 1];
float* dIdy_row = buf.dIdy[y];
for (int x = 0; x < src.cols; ++x)
{
const Point3f val = dIcdy_row[x];
dIdy_row[x] = val.dot(val);
}
}
ensureSizeIsEnough(buf.dIdx.size(), buf.dHdx);
buf.dIdx.convertTo(buf.dHdx, buf.dHdx.type(), (sigma_s / sigma_r) * (sigma_s / sigma_r), (sigma_s / sigma_s) * (sigma_s / sigma_s));
sqrt(buf.dHdx, buf.dHdx);
ensureSizeIsEnough(buf.dIdy.size(), buf.dVdy);
buf.dIdy.convertTo(buf.dVdy, buf.dVdy.type(), (sigma_s / sigma_r) * (sigma_s / sigma_r), (sigma_s / sigma_s) * (sigma_s / sigma_s));
sqrt(buf.dVdy, buf.dVdy);
ensureSizeIsEnough(src.size(), dst);
src.copyTo(dst);
TransformedDomainRecursiveFilter(src, buf.dHdx, buf.dVdy, dst, sigma_s, buf);
}
void split_3_1(const Mat_<Vec4f>& src, Mat_<Point3f>& dst1, Mat_<float>& dst2)
{
ensureSizeIsEnough(src.size(), dst1);
ensureSizeIsEnough(src.size(), dst2);
for (int y = 0; y < src.rows; ++y)
{
const Vec4f* src_row = src[y];
Point3f* dst1_row = dst1[y];
float* dst2_row = dst2[y];
for (int x = 0; x < src.cols; ++x)
{
Vec4f val = src_row[x];
dst1_row[x] = Point3f(val[0], val[1], val[2]);
dst2_row[x] = val[3];
}
}
}
void computeEigenVector(const Mat_<float>& X, const Mat_<uchar>& mask, Mat_<float>& dst, int num_pca_iterations, const Mat_<float>& rand_vec, Buf& buf)
{
CV_DbgAssert( X.cols == rand_vec.cols );
CV_DbgAssert( X.rows == mask.size().area() );
CV_DbgAssert( rand_vec.rows == 1 );
ensureSizeIsEnough(rand_vec.size(), dst);
rand_vec.copyTo(dst);
ensureSizeIsEnough(X.size(), buf.t);
float* dst_row = dst[0];
for (int i = 0; i < num_pca_iterations; ++i)
{
buf.t.setTo(Scalar::all(0));
for (int y = 0, ind = 0; y < mask.rows; ++y)
{
const uchar* mask_row = mask[y];
for (int x = 0; x < mask.cols; ++x, ++ind)
{
if (mask_row[x])
{
const float* X_row = X[ind];
float* t_row = buf.t[ind];
float dots = 0.0;
for (int c = 0; c < X.cols; ++c)
dots += dst_row[c] * X_row[c];
for (int c = 0; c < X.cols; ++c)
t_row[c] = dots * X_row[c];
}
}
}
dst.setTo(0.0);
for (int k = 0; k < X.rows; ++k)
{
const float* t_row = buf.t[k];
for (int c = 0; c < X.cols; ++c)
{
dst_row[c] += t_row[c];
}
}
}
double n = cvtest::norm(dst, NORM_L2);
cv::divide(dst, n, dst); // TODO cvtest
}
void calcEta(const Mat_<Point3f>& src_joint_f, const Mat_<float>& theta, const Mat_<uchar>& cluster, Mat_<Point3f>& dst, float sigma_s, float df, Buf& buf)
{
ensureSizeIsEnough(theta.size(), buf.theta_masked);
buf.theta_masked.setTo(Scalar::all(0));
theta.copyTo(buf.theta_masked, cluster);
times(src_joint_f, buf.theta_masked, buf.mul);
const Size nsz = Size(saturate_cast<int>(buf.mul.cols * (1.0 / df)), saturate_cast<int>(buf.mul.rows * (1.0 / df)));
ensureSizeIsEnough(nsz, buf.numerator);
resize(buf.mul, buf.numerator, Size(), 1.0 / df, 1.0 / df);
ensureSizeIsEnough(nsz, buf.denominator);
resize(buf.theta_masked, buf.denominator, Size(), 1.0 / df, 1.0 / df);
h_filter(buf.numerator, buf.numerator_filtered, sigma_s / df);
h_filter(buf.denominator, buf.denominator_filtered, sigma_s / df);
rdivide(buf.numerator_filtered, buf.denominator_filtered, dst);
}
void AdaptiveManifoldFilterRefImpl::buildManifoldsAndPerformFiltering(const Mat_<Point3f>& eta_k, const Mat_<uchar>& cluster_k, int current_tree_level)
{
// Compute downsampling factor
double df = min(sigma_s_ / 4.0, 256.0 * sigma_r_);
df = floor_to_power_of_two(df);
df = max(1.0, df);
// Splatting: project the pixel values onto the current manifold eta_k
if (eta_k.rows == src_joint_f_.rows)
{
ensureSizeIsEnough(src_joint_f_.size(), buf_.X);
subtract(src_joint_f_, eta_k, buf_.X);
const Size nsz = Size(saturate_cast<int>(eta_k.cols * (1.0 / df)), saturate_cast<int>(eta_k.rows * (1.0 / df)));
ensureSizeIsEnough(nsz, buf_.eta_k_small);
resize(eta_k, buf_.eta_k_small, Size(), 1.0 / df, 1.0 / df);
}
else
{
ensureSizeIsEnough(eta_k.size(), buf_.eta_k_small);
eta_k.copyTo(buf_.eta_k_small);
ensureSizeIsEnough(src_joint_f_.size(), buf_.eta_k_big);
resize(eta_k, buf_.eta_k_big, src_joint_f_.size());
ensureSizeIsEnough(src_joint_f_.size(), buf_.X);
subtract(src_joint_f_, buf_.eta_k_big, buf_.X);
}
// Project pixel colors onto the manifold -- Eq. (3), Eq. (5)
ensureSizeIsEnough(buf_.X.size(), buf_.X_squared);
cv::multiply(buf_.X, buf_.X, buf_.X_squared); // TODO cvtest
channelsSum(buf_.X_squared, buf_.pixel_dist_to_manifold_squared);
phi(buf_.pixel_dist_to_manifold_squared, buf_.gaussian_distance_weights, sigma_r_over_sqrt_2_);
times(src_f_, buf_.gaussian_distance_weights, buf_.Psi_splat);
const Mat_<float>& Psi_splat_0 = buf_.gaussian_distance_weights;
// Save min distance to later perform adjustment of outliers -- Eq. (10)
if (adjust_outliers_)
{
cv::min(_InputArray(min_pixel_dist_to_manifold_squared_), _InputArray(buf_.pixel_dist_to_manifold_squared), _OutputArray(min_pixel_dist_to_manifold_squared_));
}
// Blurring: perform filtering over the current manifold eta_k
catCn(buf_.Psi_splat, Psi_splat_0, buf_.Psi_splat_joined);
ensureSizeIsEnough(buf_.eta_k_small.size(), buf_.Psi_splat_joined_resized);
resize(buf_.Psi_splat_joined, buf_.Psi_splat_joined_resized, buf_.eta_k_small.size());
RF_filter(buf_.Psi_splat_joined_resized, buf_.eta_k_small, buf_.blurred_projected_values, static_cast<float>(sigma_s_ / df), sigma_r_over_sqrt_2_, buf_);
split_3_1(buf_.blurred_projected_values, buf_.w_ki_Psi_blur, buf_.w_ki_Psi_blur_0);
// Slicing: gather blurred values from the manifold
// Since we perform splatting and slicing at the same points over the manifolds,
// the interpolation weights are equal to the gaussian weights used for splatting.
const Mat_<float>& w_ki = buf_.gaussian_distance_weights;
ensureSizeIsEnough(src_f_.size(), buf_.w_ki_Psi_blur_resized);
resize(buf_.w_ki_Psi_blur, buf_.w_ki_Psi_blur_resized, src_f_.size());
times(buf_.w_ki_Psi_blur_resized, w_ki, buf_.w_ki_Psi_blur_resized);
cv::add(sum_w_ki_Psi_blur_, buf_.w_ki_Psi_blur_resized, sum_w_ki_Psi_blur_); // TODO cvtest
ensureSizeIsEnough(src_f_.size(), buf_.w_ki_Psi_blur_0_resized);
resize(buf_.w_ki_Psi_blur_0, buf_.w_ki_Psi_blur_0_resized, src_f_.size());
times(buf_.w_ki_Psi_blur_0_resized, w_ki, buf_.w_ki_Psi_blur_0_resized);
cv::add(sum_w_ki_Psi_blur_0_, buf_.w_ki_Psi_blur_0_resized, sum_w_ki_Psi_blur_0_); // TODO cvtest
// Compute two new manifolds eta_minus and eta_plus
if (current_tree_level < cur_tree_height_)
{
// Algorithm 1, Step 2: compute the eigenvector v1
const Mat_<float> nX(src_joint_f_.size().area(), 3, (float*) buf_.X.data);
ensureSizeIsEnough(1, nX.cols, buf_.rand_vec);
if (useRNG)
{
rng_.fill(buf_.rand_vec, RNG::UNIFORM, -0.5, 0.5);
}
else
{
for (int i = 0; i < (int)buf_.rand_vec.total(); i++)
buf_.rand_vec(0, i) = (i % 2 == 0) ? 0.5f : -0.5f;
}
computeEigenVector(nX, cluster_k, buf_.v1, num_pca_iterations_, buf_.rand_vec, buf_);
// Algorithm 1, Step 3: Segment pixels into two clusters -- Eq. (6)
ensureSizeIsEnough(nX.rows, buf_.v1.rows, buf_.Nx_v1_mult);
gemm(nX, buf_.v1, 1.0, noArray(), 0.0, buf_.Nx_v1_mult, GEMM_2_T);
const Mat_<float> dot(src_joint_f_.rows, src_joint_f_.cols, (float*) buf_.Nx_v1_mult.data);
Mat_<uchar>& cluster_minus = buf_.cluster_minus[current_tree_level];
ensureSizeIsEnough(dot.size(), cluster_minus);
cvtest::compare(dot, 0, cluster_minus, CMP_LT);
bitwise_and(cluster_minus, cluster_k, cluster_minus);
Mat_<uchar>& cluster_plus = buf_.cluster_plus[current_tree_level];
ensureSizeIsEnough(dot.size(), cluster_plus);
//compare(dot, 0, cluster_plus, CMP_GT);
cvtest::compare(dot, 0, cluster_plus, CMP_GE);
bitwise_and(cluster_plus, cluster_k, cluster_plus);
// Algorithm 1, Step 4: Compute new manifolds by weighted low-pass filtering -- Eq. (7-8)
ensureSizeIsEnough(w_ki.size(), buf_.theta);
buf_.theta.setTo(Scalar::all(1.0));
subtract(buf_.theta, w_ki, buf_.theta);
Mat_<Point3f>& eta_minus = buf_.eta_minus[current_tree_level];
calcEta(src_joint_f_, buf_.theta, cluster_minus, eta_minus, (float)sigma_s_, (float)df, buf_);
Mat_<Point3f>& eta_plus = buf_.eta_plus[current_tree_level];
calcEta(src_joint_f_, buf_.theta, cluster_plus, eta_plus, (float)sigma_s_, (float)df, buf_);
// Algorithm 1, Step 5: recursively build more manifolds.
buildManifoldsAndPerformFiltering(eta_minus, cluster_minus, current_tree_level + 1);
buildManifoldsAndPerformFiltering(eta_plus, cluster_plus, current_tree_level + 1);
}
}
} // namespace
Ptr<AdaptiveManifoldFilter> createAMFilterRefImpl(double sigma_s, double sigma_r, bool adjust_outliers)
{
Ptr<AdaptiveManifoldFilter> amf(new AdaptiveManifoldFilterRefImpl());
amf->setSigmaS(sigma_s);
amf->setSigmaR(sigma_r);
amf->setAdjustOutliers(adjust_outliers);
return amf;
}
} // namespace
+29
View File
@@ -0,0 +1,29 @@
// 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"
namespace opencv_test { namespace {
TEST(ximgproc_AnisotropicDiffusion, regression)
{
string folder = string(cvtest::TS::ptr()->get_data_path()) + "cv/shared/";
string original_path = folder + "fruits.png";
Mat original = imread(original_path, IMREAD_COLOR);
ASSERT_FALSE(original.empty()) << "Could not load input image " << original_path;
ASSERT_EQ(3, original.channels()) << "Load color input image " << original_path;
Mat result;
float alpha = 1.0f;
float K = 0.02f;
int niters = 10;
ximgproc::anisotropicDiffusion(original, result, alpha, K, niters);
double adiff_psnr = cvtest::PSNR(original, result);
//printf("psnr=%.2f\n", adiff_psnr);
ASSERT_GT(adiff_psnr, 25.0);
}
}} // namespace
@@ -0,0 +1,102 @@
// 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"
namespace opencv_test { namespace {
typedef tuple<int, double, double, MatType, int> BTFParams;
typedef TestWithParam<BTFParams> BilateralTextureFilterTest;
TEST_P(BilateralTextureFilterTest, SplatSurfaceAccuracy)
{
BTFParams params = GetParam();
int fr = get<0>(params);
double sigmaAlpha = get<1>(params);
double sigmaAvg = get<2>(params);
int depth = get<3>(params);
int srcCn = get<4>(params);
RNG rnd(0);
Size sz(rnd.uniform(256,512), rnd.uniform(256,512));
for (int i = 0; i < 5; i++)
{
Scalar surfaceValue;
if(depth == CV_8U)
rnd.fill(surfaceValue, RNG::UNIFORM, 0, 255);
else
rnd.fill(surfaceValue, RNG::UNIFORM, 0.0f, 1.0f);
Mat src(sz, CV_MAKE_TYPE(depth, srcCn), surfaceValue);
Mat res;
bilateralTextureFilter(src, res, fr, 1, sigmaAlpha, sigmaAvg);
double normL1 = cvtest::norm(src, res, NORM_L1)/src.total()/src.channels();
EXPECT_LE(normL1, 1.0/64.0);
}
}
TEST_P(BilateralTextureFilterTest, MultiThreadReproducibility)
{
if (cv::getNumberOfCPUs() == 1)
return;
BTFParams params = GetParam();
int fr = get<0>(params);
double sigmaAlpha = get<1>(params);
double sigmaAvg = get<2>(params);
int depth = get<3>(params);
int srcCn = get<4>(params);
double MAX_DIF = 1.0;
double MAX_MEAN_DIF = 1.0 / 64.0;
int loopsCount = 2;
RNG rnd(1);
Size sz(rnd.uniform(256,512), rnd.uniform(256,512));
Mat src(sz,CV_MAKE_TYPE(depth, srcCn));
if(src.depth()==CV_8U)
randu(src, 0, 255);
else if(src.depth()==CV_16S)
randu(src, -32767, 32767);
else
randu(src, 0.0f, 1.0f);
int nThreads = cv::getNumThreads();
if (nThreads == 1)
throw SkipTestException("Single thread environment");
for (int iter = 0; iter <= loopsCount; iter++)
{
cv::setNumThreads(nThreads);
Mat resMultiThread;
bilateralTextureFilter(src, resMultiThread, fr, 1, sigmaAlpha, sigmaAvg);
cv::setNumThreads(1);
Mat resSingleThread;
bilateralTextureFilter(src, resSingleThread, fr, 1, sigmaAlpha, sigmaAvg);
EXPECT_LE(cv::norm(resSingleThread, resMultiThread, NORM_INF), MAX_DIF);
EXPECT_LE(
cv::norm(resSingleThread, resMultiThread, NORM_L1),
MAX_MEAN_DIF * src.total() * src.channels());
}
}
INSTANTIATE_TEST_CASE_P(
TypicalSet1,
BilateralTextureFilterTest,
Combine(
Values(2),
Values(0.5),
Values(0.5),
Values(CV_8U, CV_32F),
Values(1, 3)
)
);
}} // namespace
@@ -0,0 +1,41 @@
// 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"
namespace opencv_test { namespace {
TEST(ximgproc_DericheFilter, regression)
{
Mat img = Mat::zeros(64, 64, CV_8UC3);
Mat res = Mat::zeros(64, 64, CV_32FC3);
img.at<Vec3b>(31, 31) = Vec3b(1, 2, 4);
double a = 0.5;
double w = 0.0005;
Mat dst;
ximgproc::GradientDericheX(img, dst, a, w);
double c = pow(1 - exp(-a), 2.0) * exp(a);
double k = pow(a*(1 - exp(-a)), 2.0) / (1 + 2 * a*exp(-a) - exp(-2 * a));
for (int i = 0; i < img.rows; i++)
{
double n = -31 + i;
for (int j = 0; j < img.cols; j++)
{
double m = -31 + j;
double x = -c * exp(-a * fabs(m))*sin(w*m);
x = x * (k*(a*sin(w*fabs(n)) + w * cos(w*fabs(n)))*exp(-a * fabs(n))) / (a*a + w * w);
x = x / (w*w);
float xx=static_cast<float>(x);
res.at<Vec3f>(i, j) = Vec3f(xx, 2 * xx, 4 * xx);
}
}
EXPECT_LE(cv::norm(res, dst, NORM_INF), 1e-5);
Mat dst2;
ximgproc::GradientDericheY(img, dst2, a, w);
cv::transpose(dst2, dst2);
EXPECT_LE(cv::norm(dst2, dst, NORM_INF), 1e-5);
}
}
} // namespace
@@ -0,0 +1,117 @@
// 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/ximgproc/disparity_filter.hpp"
namespace opencv_test { namespace {
static string getDataDir()
{
return cvtest::TS::ptr()->get_data_path();
}
CV_ENUM(SrcTypes, CV_16S);
CV_ENUM(GuideTypes, CV_8UC1, CV_8UC3)
typedef tuple<Size, SrcTypes, GuideTypes, bool, bool> DisparityWLSParams;
typedef TestWithParam<DisparityWLSParams> DisparityWLSFilterTest;
TEST(DisparityWLSFilterTest, ReferenceAccuracy)
{
string dir = getDataDir() + "cv/disparityfilter";
Mat left = imread(dir + "/left_view.png",IMREAD_COLOR);
ASSERT_FALSE(left.empty());
Mat left_disp = imread(dir + "/disparity_left_raw.png",IMREAD_GRAYSCALE);
ASSERT_FALSE(left_disp.empty());
left_disp.convertTo(left_disp,CV_16S,16);
Mat right_disp = imread(dir + "/disparity_right_raw.png",IMREAD_GRAYSCALE);
ASSERT_FALSE(right_disp.empty());
right_disp.convertTo(right_disp,CV_16S,-16);
Mat GT;
ASSERT_FALSE(readGT(dir + "/GT.png",GT));
FileStorage ROI_storage( dir + "/ROI.xml", FileStorage::READ );
Rect ROI((int)ROI_storage["x"],(int)ROI_storage["y"],(int)ROI_storage["width"],(int)ROI_storage["height"]);
FileStorage reference_res( dir + "/reference_accuracy.xml", FileStorage::READ );
double ref_MSE = (double)reference_res["MSE_after"];
double ref_BadPercent = (double)reference_res["BadPercent_after"];
Mat res;
Ptr<DisparityWLSFilter> wls_filter = createDisparityWLSFilterGeneric(true);
wls_filter->setLambda(8000.0);
wls_filter->setSigmaColor(0.5);
wls_filter->filter(left_disp,left,res,right_disp,ROI);
double MSE = computeMSE(GT,res,ROI);
double BadPercent = computeBadPixelPercent(GT,res,ROI);
double eps = 0.01;
EXPECT_LE(MSE,ref_MSE+eps*ref_MSE);
EXPECT_LE(BadPercent,ref_BadPercent+eps*ref_BadPercent);
}
TEST_P(DisparityWLSFilterTest, MultiThreadReproducibility)
{
if (cv::getNumberOfCPUs() == 1)
return;
double MAX_DIF = 1.0;
double MAX_MEAN_DIF = 1.0 / 256.0;
int loopsCount = 2;
RNG rng(0);
DisparityWLSParams params = GetParam();
Size size = get<0>(params);
int srcType = get<1>(params);
int guideType = get<2>(params);
bool use_conf = get<3>(params);
bool use_downscale = get<4>(params);
Mat left(size, guideType);
randu(left, 0, 255);
Mat left_disp(size,srcType);
int max_disp = (int)(size.width*0.1);
randu(left_disp, 0, max_disp-1);
Mat right_disp(size,srcType);
randu(left_disp, -max_disp+1, 0);
Rect ROI(max_disp,0,size.width-max_disp,size.height);
if(use_downscale)
{
resize(left_disp,left_disp,Size(),0.5,0.5, INTER_LINEAR_EXACT);
resize(right_disp,right_disp,Size(),0.5,0.5, INTER_LINEAR_EXACT);
ROI = Rect(ROI.x/2,ROI.y/2,ROI.width/2,ROI.height/2);
}
int nThreads = cv::getNumThreads();
if (nThreads == 1)
throw SkipTestException("Single thread environment");
for (int iter = 0; iter <= loopsCount; iter++)
{
double lambda = rng.uniform(100.0, 10000.0);
double sigma = rng.uniform(1.0, 100.0);
Ptr<DisparityWLSFilter> wls_filter = createDisparityWLSFilterGeneric(use_conf);
wls_filter->setLambda(lambda);
wls_filter->setSigmaColor(sigma);
cv::setNumThreads(nThreads);
Mat resMultiThread;
wls_filter->filter(left_disp,left,resMultiThread,right_disp,ROI);
cv::setNumThreads(1);
Mat resSingleThread;
wls_filter->filter(left_disp,left,resSingleThread,right_disp,ROI);
EXPECT_LE(cv::norm(resSingleThread, resMultiThread, NORM_INF), MAX_DIF);
EXPECT_LE(cv::norm(resSingleThread, resMultiThread, NORM_L1), MAX_MEAN_DIF*left.total());
}
}
INSTANTIATE_TEST_CASE_P(FullSet,DisparityWLSFilterTest,Combine(Values(szODD, szQVGA), SrcTypes::all(), GuideTypes::all(),Values(true,false),Values(true,false)));
}} // namespace
@@ -0,0 +1,217 @@
// 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"
namespace opencv_test { namespace {
static string getOpenCVExtraDir()
{
return cvtest::TS::ptr()->get_data_path();
}
CV_ENUM(SupportedTypes, CV_8UC1, CV_8UC3, CV_32FC1); // reduced set
CV_ENUM(ModeType, DTF_NC, DTF_IC, DTF_RF)
typedef tuple<Size, ModeType, SupportedTypes, SupportedTypes> DTParams;
Mat convertTypeAndSize(Mat src, int dstType, Size dstSize)
{
Mat dst;
CV_Assert(src.channels() == 3);
int dstChannels = CV_MAT_CN(dstType);
if (dstChannels == 1)
{
cvtColor(src, dst, COLOR_BGR2GRAY);
}
else if (dstChannels == 2)
{
Mat srcCn[3];
split(src, srcCn);
merge(srcCn, 2, dst);
}
else if (dstChannels == 3)
{
dst = src.clone();
}
else if (dstChannels == 4)
{
Mat srcCn[4];
split(src, srcCn);
srcCn[3] = srcCn[0].clone();
merge(srcCn, 4, dst);
}
dst.convertTo(dst, dstType);
resize(dst, dst, dstSize, 0, 0, dstType == CV_32FC1 ? INTER_LINEAR : INTER_LINEAR_EXACT);
return dst;
}
TEST(DomainTransformTest, SplatSurfaceAccuracy)
{
static int dtModes[] = {DTF_NC, DTF_RF, DTF_IC};
RNG rnd(0);
for (int i = 0; i < 15; i++)
{
Size sz(rnd.uniform(512, 1024), rnd.uniform(512, 1024));
int guideCn = rnd.uniform(1, 4);
Mat guide(sz, CV_MAKE_TYPE(CV_32F, guideCn));
randu(guide, 0, 255);
Scalar surfaceValue;
int srcCn = rnd.uniform(1, 4);
rnd.fill(surfaceValue, RNG::UNIFORM, 0, 255);
Mat src(sz, CV_MAKE_TYPE(CV_8U, srcCn), surfaceValue);
double sigma_s = rnd.uniform(1.0, 100.0);
double sigma_r = rnd.uniform(1.0, 100.0);
int mode = dtModes[i%3];
Mat res;
dtFilter(guide, src, res, sigma_s, sigma_r, mode, 1);
double normL1 = cvtest::norm(src, res, NORM_L1)/src.total()/src.channels();
EXPECT_LE(normL1, 1.0/64);
}
}
typedef TestWithParam<DTParams> DomainTransformTest;
TEST_P(DomainTransformTest, MultiThreadReproducibility)
{
if (cv::getNumberOfCPUs() == 1)
return;
double MAX_DIF = 1.0;
double MAX_MEAN_DIF = 1.0 / 256.0;
int loopsCount = 2;
RNG rng(0);
DTParams params = GetParam();
Size size = get<0>(params);
int mode = get<1>(params);
int guideType = get<2>(params);
int srcType = get<3>(params);
Mat original = imread(getOpenCVExtraDir() + "cv/edgefilter/statue.png");
Mat guide = convertTypeAndSize(original, guideType, size);
Mat src = convertTypeAndSize(original, srcType, size);
int nThreads = cv::getNumThreads();
if (nThreads == 1)
throw SkipTestException("Single thread environment");
for (int iter = 0; iter <= loopsCount; iter++)
{
double ss = rng.uniform(0.0, 100.0);
double sc = rng.uniform(0.0, 100.0);
cv::setNumThreads(nThreads);
Mat resMultithread;
dtFilter(guide, src, resMultithread, ss, sc, mode);
cv::setNumThreads(1);
Mat resSingleThread;
dtFilter(guide, src, resSingleThread, ss, sc, mode);
EXPECT_LE(cv::norm(resSingleThread, resMultithread, NORM_INF), MAX_DIF);
EXPECT_LE(cv::norm(resSingleThread, resMultithread, NORM_L1), MAX_MEAN_DIF*src.total());
}
}
INSTANTIATE_TEST_CASE_P(FullSet, DomainTransformTest,
Combine(Values(szODD, szQVGA), ModeType::all(), SupportedTypes::all(), SupportedTypes::all())
);
template<typename SrcVec>
Mat getChessMat1px(Size sz, double whiteIntensity = 255)
{
typedef typename DataType<SrcVec>::channel_type SrcType;
Mat dst(sz, traits::Type<SrcVec>::value);
SrcVec black = SrcVec::all(0);
SrcVec white = SrcVec::all((SrcType)whiteIntensity);
for (int i = 0; i < dst.rows; i++)
for (int j = 0; j < dst.cols; j++)
dst.at<SrcVec>(i, j) = ((i + j) % 2) ? white : black;
return dst;
}
TEST(DomainTransformTest, ChessBoard_NC_accuracy)
{
RNG rng(0);
double MAX_DIF = 1;
Size sz = szVGA;
double ss = 80;
double sc = 60;
Mat srcb = randomMat(rng, sz, CV_8UC4, 0, 255, true);
Mat srcf = randomMat(rng, sz, CV_32FC4, 0, 255, true);
Mat chessb = getChessMat1px<Vec3b>(sz);
Mat dstb, dstf;
dtFilter(chessb, srcb.clone(), dstb, ss, sc, DTF_NC);
dtFilter(chessb, srcf.clone(), dstf, ss, sc, DTF_NC);
EXPECT_LE(cv::norm(srcb, dstb, NORM_INF), MAX_DIF);
EXPECT_LE(cv::norm(srcf, dstf, NORM_INF), MAX_DIF);
}
TEST(DomainTransformTest, BoxFilter_NC_accuracy)
{
double MAX_DIF = 1;
int radius = 5;
double sc = 1.0;
double ss = 1.01*radius / sqrt(3.0);
Mat src = imread(getOpenCVExtraDir() + "cv/edgefilter/statue.png");
ASSERT_TRUE(!src.empty());
Mat1b guide(src.size(), 200);
Mat res_dt, res_box;
blur(src, res_box, Size(2 * radius + 1, 2 * radius + 1));
dtFilter(guide, src, res_dt, ss, sc, DTF_NC, 1);
EXPECT_LE(cv::norm(res_dt, res_box, NORM_L2), MAX_DIF*src.total());
}
TEST(DomainTransformTest, AuthorReferenceAccuracy)
{
string dir = getOpenCVExtraDir() + "cv/edgefilter";
double ss = 30;
double sc = 0.2 * 255;
Mat src = imread(dir + "/statue.png");
Mat ref_NC = imread(dir + "/dt/authors_statue_NC_ss30_sc0.2.png");
Mat ref_IC = imread(dir + "/dt/authors_statue_IC_ss30_sc0.2.png");
Mat ref_RF = imread(dir + "/dt/authors_statue_RF_ss30_sc0.2.png");
ASSERT_FALSE(src.empty());
ASSERT_FALSE(ref_NC.empty());
ASSERT_FALSE(ref_IC.empty());
ASSERT_FALSE(ref_RF.empty());
Mat res_NC, res_IC, res_RF;
dtFilter(src, src, res_NC, ss, sc, DTF_NC);
dtFilter(src, src, res_IC, ss, sc, DTF_IC);
dtFilter(src, src, res_RF, ss, sc, DTF_RF);
double totalMaxError = 1.0/64.0*src.total();
EXPECT_LE(cvtest::norm(res_NC, ref_NC, NORM_L2), totalMaxError);
EXPECT_LE(cvtest::norm(res_NC, ref_NC, NORM_INF), 1);
EXPECT_LE(cvtest::norm(res_IC, ref_IC, NORM_L2), totalMaxError);
EXPECT_LE(cvtest::norm(res_IC, ref_IC, NORM_INF), 1);
EXPECT_LE(cvtest::norm(res_RF, ref_RF, NORM_L2), totalMaxError);
EXPECT_LE(cvtest::norm(res_IC, ref_IC, NORM_INF), 1);
}
}} // namespace
+48
View File
@@ -0,0 +1,48 @@
// 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"
namespace opencv_test { namespace {
TEST(ximgproc_Edgeboxes, regression)
{
//Testing Edgeboxes implementation by asking for one proposal
//on a simple test image from the PASCAL VOC 2012 dataset.
std::vector<Rect> boxes;
std::vector<float> scores;
float expectedScore = 0.48742563f;
Rect expectedProposal(158, 69, 125, 154);
//Using sample model file, compute orientations map for use with edge detection.
cv::String testImagePath = cvtest::TS::ptr()->get_data_path() + "cv/ximgproc/" + "pascal_voc_bird.png";
Mat testImg = imread(testImagePath);
ASSERT_FALSE(testImg.empty()) << "Could not load input image " << testImagePath;
cvtColor(testImg, testImg, COLOR_BGR2RGB);
testImg.convertTo(testImg, CV_32F, 1.0 / 255.0f);
//Use the model for structured edge detection that is already provided in opencv_extra.
cv::String model_path = cvtest::TS::ptr()->get_data_path() + "cv/ximgproc/" + "model.yml.gz";
Ptr<StructuredEdgeDetection> sed = createStructuredEdgeDetection(model_path);
Mat edgeImage, edgeOrientations;
sed->detectEdges(testImg, edgeImage);
sed->computeOrientation(edgeImage, edgeOrientations);
//Obtain one proposal and its score from Edgeboxes.
Ptr<EdgeBoxes> edgeboxes = createEdgeBoxes();
edgeboxes->setMaxBoxes(1);
edgeboxes->getBoundingBoxes(edgeImage, edgeOrientations, boxes, scores);
//We asked for one proposal and thus one score, we better get one back only.
ASSERT_TRUE(boxes.size() == 1);
ASSERT_TRUE(scores.size() == 1);
//Check the proposal and its score.
EXPECT_NEAR(scores[0], expectedScore, 1e-8);
EXPECT_EQ(expectedProposal.x, boxes[0].x);
EXPECT_EQ(expectedProposal.y, boxes[0].y);
EXPECT_EQ(expectedProposal.height, boxes[0].height);
EXPECT_EQ(expectedProposal.width, boxes[0].width);
}
}} // namespace
@@ -0,0 +1,35 @@
// 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.
//
// Created by Simon Reich
//
#include "test_precomp.hpp"
namespace opencv_test { namespace {
TEST(ximgproc_EdgepreservingFilter, regression)
{
// Load original image
std::string filename = string(cvtest::TS::ptr()->get_data_path()) + "perf/320x260.png";
cv::Mat src, dst, noise, original = imread(filename, 1);
ASSERT_FALSE(original.empty()) << "Could not load input image " << filename;
ASSERT_EQ(3, original.channels()) << "Load color input image " << filename;
// add noise
noise = Mat(original.size(), original.type());
cv::randn(noise, 0, 5);
src = original + noise;
// Filter
int kernel = 9;
double threshold = 20;
ximgproc::edgePreservingFilter(src, dst, kernel, threshold);
double psnr = cvtest::PSNR(original, dst);
//printf("psnr=%.2f\n", psnr);
ASSERT_LT(psnr, 25.0);
}
}} // namespace
@@ -0,0 +1,462 @@
/*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) 2015, Smart Engines Ltd, all rights reserved.
// Copyright (C) 2015, Institute for Information Transmission Problems of the Russian Academy of Sciences (Kharkevich Institute), all rights reserved.
// Copyright (C) 2015, Dmitry Nikolaev, Simon Karpenko, Michail Aliev, Elena Kuznetsova, 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 {
//----------------------utils---------------------------------------------------
template <typename T> struct Eps
{
static T get() { return 1; }
};
template <> struct Eps<float> { static float get() { return float(1e-3); } };
template <> struct Eps<double> { static double get() { return 1e-6; } };
template <typename T> struct MinPos
{
static T get() { return Eps<T>::get(); }
};
template <typename T> struct Max { static T get()
{
return saturate_cast<T>(numeric_limits<T>::max()); }
};
template <typename T> struct Rand
{
static T get(T _min = MinPos<T>::get(), T _max = Max<T>::get())
{
RNG& rng = TS::ptr()->get_rng();
return saturate_cast<T>(rng.uniform(int(std::max(MinPos<T>::get(),
_min)),
int(std::min(Max<T>::get(),
_max))));
}
};
template <> struct Rand <float>
{
static float get(float _min = MinPos<float>::get(),
float _max = Max<float>::get())
{
RNG& rng = TS::ptr()->get_rng();
return rng.uniform(std::max(MinPos<float>::get(), _min),
std::min(Max<float>::get(), _max));
}
};
template <> struct Rand <double>
{
static double get(double _min = MinPos<double>::get(),
double _max = Max<double>::get())
{
RNG& rng = TS::ptr()->get_rng();
return rng.uniform(std::max(MinPos<double>::get(), _min),
std::min(Max<double>::get(), _max));
}
};
template <typename T> struct Eq
{
static bool get(T a, T b)
{
return a < b ? b - a < Eps<T>::get() : a - b < Eps<T>::get();
}
};
//----------------------TestFHT-------------------------------------------------
class TestFHT
{
public:
TestFHT() : ts(TS::ptr()) {}
void run_n_tests(int depth,
int channels,
int pts_count,
int n_per_test);
private:
template <typename T>
int run_n_tests_t(int depth,
int channels,
int pts_count,
int n_per_test);
template <typename T>
int run_test(int depth,
int channels,
int pts_count);
template <typename T>
int put_random_points(Mat &img,
int count,
vector<Point> &pts);
int run_func(Mat const&src,
Mat& fht);
template <typename T>
int validate_test_results(Mat const &fht,
Mat const &src,
vector<Point> const& pts);
template <typename T> int validate_sum(Mat const& src, Mat const& fht);
int validate_point(Mat const& fht, vector<Point> const &pts);
int validate_line(Mat const& fht, Mat const& src, vector<Point> const& pts);
private:
TS *ts;
};
template <typename T>
int TestFHT::put_random_points(Mat &img, int count, vector<Point> &pts)
{
int code = TS::OK;
pts.resize(count, Point(-1, -1));
for (int i = 0; i < count; ++i)
{
RNG rng = ts->get_rng();
Point const pt(rng.uniform(0, img.cols),
rng.uniform(0, img.rows));
pts[i] = pt;
for (int c = 0; c < img.channels(); ++c)
{
T color = Rand<T>::get(MinPos<T>::get(),
T(Max<T>::get() / count));
T *img_line = (T*)(img.data + img.step * pt.y);
img_line[pt.x * img.channels() + c] = color;
}
}
return code;
}
template <typename T>
int TestFHT::validate_sum(Mat const& src, Mat const& fht)
{
int const channels = src.channels();
if (fht.channels() != channels)
return TS::FAIL_BAD_ARG_CHECK;
vector<Mat> src_channels(channels);
split(src, src_channels);
vector<Mat> fht_channels(channels);
split(fht, fht_channels);
for (int c = 0; c < channels; ++c)
{
T const src_sum = saturate_cast<T>(sum(src_channels[c]).val[0]);
for (int y = 0; y < fht.rows; ++y)
{
T const fht_sum = saturate_cast<T>(sum(fht_channels[c].row(y)).val[0]);
if (!Eq<T>::get(src_sum, fht_sum))
{
ts->printf(TS::LOG,
"The sum of column #%d of channel #%d of the fast "
"hough transform result and the sum of source image"
" mismatch (=%g, should be =%g)\n",
y, c, (float)fht_sum, (float)src_sum);
return TS::FAIL_BAD_ACCURACY;
}
}
}
return TS::OK;
}
int TestFHT::validate_point(Mat const& fht,
vector<Point> const &pts)
{
if (pts.empty())
return TS::OK;
for (size_t i = 1; i < pts.size(); ++i)
{
if (pts[0] != pts[i])
return TS::OK;
}
int const channels = fht.channels();
vector<Mat> fht_channels(channels);
split(fht, fht_channels);
for (int c = 0; c < channels; ++c)
{
for (int y = 0; y < fht.rows; ++y)
{
int cnt = countNonZero(fht_channels[c].row(y));
if (cnt != 1)
{
ts->printf(TS::LOG,
"The incorrect count of non-zero values in column "
"#%d, channel #%d of FastHoughTransform result "
"image (=%d, should be %d)\n",
y, c, cnt, 1);
return TS::FAIL_BAD_ACCURACY;
}
}
}
return TS::OK;
}
static const double MAX_LDIST = 2.0;
int TestFHT::validate_line(Mat const& fht,
Mat const& src,
vector<Point> const& pts)
{
size_t const size = (int)pts.size();
if (size < 2)
return TS::OK;
size_t first_pt_i = 0, second_pt_i = 1;
for (size_t i = first_pt_i + 1; i < size; ++i)
{
if (pts[i] != pts[first_pt_i])
{
second_pt_i = first_pt_i;
break;
}
}
if (pts[second_pt_i] == pts[first_pt_i])
return TS::OK;
for (size_t i = second_pt_i + 1; i < size; ++i)
{
if (pts[i] != pts[second_pt_i])
return TS::OK;
}
const Point &f = pts[first_pt_i];
const Point &s = pts[second_pt_i];
int const channels = fht.channels();
vector<Mat> fht_channels(channels);
split(fht, fht_channels);
for (int ch = 0; ch < channels; ++ch)
{
Point fht_max(-1, -1);
minMaxLoc(fht_channels[ch], 0, 0, 0, &fht_max);
Vec4i src_line = HoughPoint2Line(fht_max, src,
ARO_315_135, HDO_DESKEW, RO_STRICT);
double const a = src_line[1] - src_line[3];
double const b = src_line[2] - src_line[0];
double const c = - (a * src_line[0] + b * src_line[1]);
double const fd = abs(f.x * a + f.y * b + c) / sqrt(a * a + b * b);
double const sd = abs(s.x * a + s.y * b + c) / sqrt(a * a + b * b);
double const dist = std::max(fd, sd);
if (dist > MAX_LDIST)
{
ts->printf(TS::LOG,
"Failed to detect max line in channels %d (distance "
"between point and line correspoinding of maximum in "
"FastHoughTransform space is #%g)\n", ch, dist);
return TS::FAIL_BAD_ACCURACY;
}
}
return TS::OK;
}
template <typename T>
int TestFHT::validate_test_results(Mat const &fht,
Mat const &src,
vector<Point> const& pts)
{
int code = validate_sum<T>(src, fht);
if (code == TS::OK)
code = validate_point(fht, pts);
if (code == TS::OK)
code = validate_line(fht, src, pts);
return code;
}
int TestFHT::run_func(Mat const&src,
Mat& fht)
{
int code = TS::OK;
FastHoughTransform(src, fht, src.depth());
return code;
}
static Size random_size(int const max_size_log,
int const elem_size)
{
RNG& rng = TS::ptr()->get_rng();
return randomSize(rng, std::max(1,
max_size_log - cvRound(log(double(elem_size)))));
}
static const int FHT_MAX_SIZE_LOG = 9;
template <typename T>
int TestFHT::run_test(int depth,
int channels,
int pts_count)
{
int code = TS::OK;
Size size = random_size(FHT_MAX_SIZE_LOG,
CV_ELEM_SIZE(CV_MAKE_TYPE(depth, channels)));
Mat src = Mat::zeros(size, CV_MAKETYPE(depth, channels));
vector<Point> pts;
code = put_random_points<T>(src, pts_count, pts);
if (code != TS::OK)
return code;
Mat fht;
code = run_func(src, fht);
if (code != TS::OK)
return code;
code = validate_test_results<T>(fht, src, pts);
return code;
}
void TestFHT::run_n_tests(int depth,
int channels,
int pts_count,
int n)
{
try
{
int code = TS::OK;
switch (depth)
{
case CV_8U:
code = run_n_tests_t<uchar>(depth, channels, pts_count, n);
break;
case CV_8S:
code = run_n_tests_t<schar>(depth, channels, pts_count, n);
break;
case CV_16U:
code = run_n_tests_t<ushort>(depth, channels, pts_count, n);
break;
case CV_16S:
code = run_n_tests_t<short>(depth, channels, pts_count, n);
break;
case CV_32S:
code = run_n_tests_t<int>(depth, channels, pts_count, n);
break;
case CV_32F:
code = run_n_tests_t<float>(depth, channels, pts_count, n);
break;
case CV_64F:
code = run_n_tests_t<double>(depth, channels, pts_count, n);
break;
default:
code = TS::FAIL_BAD_ARG_CHECK;
ts->printf(TS::LOG, "Unknown depth %d\n", depth);
break;
}
if (code != TS::OK)
throw TS::FailureCode(code);
}
catch (const TS::FailureCode& fc)
{
std::string errorStr = TS::str_from_code(fc);
ts->printf(TS::LOG,
"General failure:\n\t%s (%d)\n", errorStr.c_str(), fc);
ts->set_failed_test_info(fc);
}
catch(...)
{
ts->printf(TS::LOG, "Unknown failure\n");
ts->set_failed_test_info(TS::FAIL_EXCEPTION);
}
}
template <typename T>
int TestFHT::run_n_tests_t(int depth,
int channels,
int pts_count,
int n)
{
int code = TS::OK;
for (int iTest = 0; iTest < n; ++iTest)
{
code = run_test<T>(depth, channels, pts_count);
if (code != TS::OK)
{
ts->printf(TS::LOG, "Test %d failed with code %d\n", iTest, code);
break;
}
}
return code;
}
//----------------------TEST_P--------------------------------------------------
typedef tuple<int, int, int, int> Depth_Channels_PtsC_nPerTest;
typedef TestWithParam<Depth_Channels_PtsC_nPerTest> FastHoughTransformTest;
TEST_P(FastHoughTransformTest, accuracy)
{
int const depth = get<0>(GetParam());
int const channels = get<1>(GetParam());
int const pts_count = get<2>(GetParam());
int const n_per_test = get<3>(GetParam());
TestFHT testFht;
testFht.run_n_tests(depth, channels, pts_count, n_per_test);
}
#define FHT_ALL_DEPTHS CV_8U, CV_16U, CV_32S, CV_32F, CV_64F
#define FHT_ALL_CHANNELS 1, 3, 4
INSTANTIATE_TEST_CASE_P(FullSet, FastHoughTransformTest,
Combine(Values(FHT_ALL_DEPTHS),
Values(FHT_ALL_CHANNELS),
Values(1, 2),
Values(5)));
#undef FHT_ALL_DEPTHS
#undef FHT_ALL_CHANNELS
}} // namespace
+131
View File
@@ -0,0 +1,131 @@
/*
* 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
* (3 - clause BSD License)
*
* Redistribution and use in source and binary forms, with or without modification,
* are permitted provided that the following conditions are met :
*
* * Redistributions of source code must retain the above copyright notice,
* this list of conditions and the following disclaimer.
*
* * Redistributions 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.
*
* * Neither the names of the copyright holders nor the names of the contributors
* may 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 copyright holders 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.
*/
#include "test_precomp.hpp"
#ifdef HAVE_EIGEN
namespace opencv_test { namespace {
using namespace std;
using namespace cv;
using namespace cv::ximgproc;
static string getDataDir()
{
return cvtest::TS::ptr()->get_data_path();
}
CV_ENUM(SrcTypes, CV_8UC1, CV_8UC3, CV_8UC4, CV_16SC1, CV_16SC3, CV_32FC1);
CV_ENUM(GuideTypes, CV_8UC1, CV_8UC3)
typedef tuple<Size, SrcTypes, GuideTypes> FBSParams;
typedef TestWithParam<FBSParams> FastBilateralSolverTest;
TEST(FastBilateralSolverTest, SplatSurfaceAccuracy)
{
RNG rnd(0);
int chanLut[] = {1,3,4};
for (int i = 0; i < 5; i++)
{
Size sz(rnd.uniform(512, 1024), rnd.uniform(512, 1024));
int guideCn = rnd.uniform(0, 2); // 1 or 3 channels
Mat guide(sz, CV_MAKE_TYPE(CV_8U, chanLut[guideCn]));
randu(guide, 0, 255);
Scalar surfaceValue;
int srcCn = rnd.uniform(0, 3); // 1, 3 or 4 channels
rnd.fill(surfaceValue, RNG::UNIFORM, 0, 255);
Mat src(sz, CV_MAKE_TYPE(CV_16S, chanLut[srcCn]), surfaceValue);
Mat confidence(sz, CV_MAKE_TYPE(CV_8U, 1), 255);
double sigma_spatial = rnd.uniform(4.0, 40.0);
double sigma_luma = rnd.uniform(4.0, 40.0);
double sigma_chroma = rnd.uniform(4.0, 40.0);
Mat res;
fastBilateralSolverFilter(guide, src, confidence, res, sigma_spatial, sigma_luma, sigma_chroma);
// When filtering a constant image we should get the same image:
double normL1 = cvtest::norm(src, res, NORM_L1)/src.total()/src.channels();
EXPECT_LE(normL1, 1.0/64);
}
}
#define COUNT_EXCEED(MAT1, MAT2, THRESHOLD, PIXEL_COUNT) \
{ \
Mat diff, count; \
absdiff(MAT1.reshape(1), MAT2.reshape(1), diff); \
cvtest::compare(diff, THRESHOLD, count, CMP_GT); \
PIXEL_COUNT = countNonZero(count.reshape(1)); \
PIXEL_COUNT /= MAT1.channels(); \
}
TEST(FastBilateralSolverTest, ReferenceAccuracy)
{
string dir = getDataDir() + "cv/edgefilter";
Mat src = imread(dir + "/kodim23.png");
Mat ref = imread(dir + "/fbs/kodim23_spatial=16_luma=16_chroma=16.png");
Mat confidence(src.size(), CV_MAKE_TYPE(CV_8U, 1), 255);
ASSERT_FALSE(src.empty());
ASSERT_FALSE(ref.empty());
Mat res;
fastBilateralSolverFilter(src,src,confidence,res, 16.0, 16.0, 16.0);
double totalMaxError = 1.0/64.0*src.total()*src.channels();
EXPECT_LE(cvtest::norm(res, ref, NORM_L2), totalMaxError);
#if defined (__x86_64__) || defined (_M_X64)
EXPECT_LE(cvtest::norm(res, ref, NORM_INF), 1);
#else
// fastBilateralSolverFilter is not bit-exact
int pixelCount = 0;
COUNT_EXCEED(res, ref, 2, pixelCount);
EXPECT_LE(pixelCount, (int)(res.cols*res.rows*1/100));
#endif
}
INSTANTIATE_TEST_CASE_P(FullSet, FastBilateralSolverTest,Combine(Values(szODD, szQVGA), SrcTypes::all(), GuideTypes::all()));
}
}
#endif //HAVE_EIGEN
+115
View File
@@ -0,0 +1,115 @@
// 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"
namespace opencv_test { namespace {
static string getDataDir()
{
return cvtest::TS::ptr()->get_data_path();
}
CV_ENUM(SrcTypes, CV_8UC1, CV_8UC3, CV_8UC4, CV_16SC1, CV_16SC3, CV_32FC1);
CV_ENUM(GuideTypes, CV_8UC1, CV_8UC3)
typedef tuple<Size, SrcTypes, GuideTypes> FGSParams;
typedef TestWithParam<FGSParams> FastGlobalSmootherTest;
TEST(FastGlobalSmootherTest, SplatSurfaceAccuracy)
{
RNG rnd(0);
for (int i = 0; i < 5; i++)
{
Size sz(rnd.uniform(512, 1024), rnd.uniform(512, 1024));
int guideCn = rnd.uniform(1, 2);
if(guideCn==2) guideCn++; //1 or 3 channels
Mat guide(sz, CV_MAKE_TYPE(CV_8U, guideCn));
randu(guide, 0, 255);
Scalar surfaceValue;
int srcCn = rnd.uniform(1, 4);
rnd.fill(surfaceValue, RNG::UNIFORM, 0, 255);
Mat src(sz, CV_MAKE_TYPE(CV_16S, srcCn), surfaceValue);
double lambda = rnd.uniform(100, 10000);
double sigma = rnd.uniform(1.0, 100.0);
Mat res;
fastGlobalSmootherFilter(guide, src, res, lambda, sigma);
// When filtering a constant image we should get the same image:
double normL1 = cvtest::norm(src, res, NORM_L1)/src.total()/src.channels();
EXPECT_LE(normL1, 1.0/64);
}
}
TEST(FastGlobalSmootherTest, ReferenceAccuracy)
{
string dir = getDataDir() + "cv/edgefilter";
Mat src = imread(dir + "/kodim23.png");
Mat ref = imread(dir + "/fgs/kodim23_lambda=1000_sigma=10.png");
ASSERT_FALSE(src.empty());
ASSERT_FALSE(ref.empty());
Mat res;
fastGlobalSmootherFilter(src,src,res,1000.0,10.0);
double totalMaxError = 1.0/64.0*src.total()*src.channels();
EXPECT_LE(cvtest::norm(res, ref, NORM_L2), totalMaxError);
EXPECT_LE(cvtest::norm(res, ref, NORM_INF), 1);
}
TEST_P(FastGlobalSmootherTest, MultiThreadReproducibility)
{
if (cv::getNumberOfCPUs() == 1)
return;
double MAX_DIF = 1.0;
double MAX_MEAN_DIF = 1.0 / 64.0;
int loopsCount = 2;
RNG rng(0);
FGSParams params = GetParam();
Size size = get<0>(params);
int srcType = get<1>(params);
int guideType = get<2>(params);
Mat guide(size, guideType);
randu(guide, 0, 255);
Mat src(size,srcType);
if(src.depth()==CV_8U)
randu(src, 0, 255);
else if(src.depth()==CV_16S)
randu(src, -32767, 32767);
else
randu(src, -100000.0f, 100000.0f);
int nThreads = cv::getNumThreads();
if (nThreads == 1)
throw SkipTestException("Single thread environment");
for (int iter = 0; iter <= loopsCount; iter++)
{
double lambda = rng.uniform(100.0, 10000.0);
double sigma = rng.uniform(1.0, 100.0);
cv::setNumThreads(nThreads);
Mat resMultiThread;
fastGlobalSmootherFilter(guide, src, resMultiThread, lambda, sigma);
cv::setNumThreads(1);
Mat resSingleThread;
fastGlobalSmootherFilter(guide, src, resSingleThread, lambda, sigma);
EXPECT_LE(cv::norm(resSingleThread, resMultiThread, NORM_INF), MAX_DIF);
EXPECT_LE(cv::norm(resSingleThread, resMultiThread, NORM_L1), MAX_MEAN_DIF*src.total()*src.channels());
}
}
INSTANTIATE_TEST_CASE_P(FullSet, FastGlobalSmootherTest,Combine(Values(szODD, szQVGA), SrcTypes::all(), GuideTypes::all()));
}} // namespace
@@ -0,0 +1,43 @@
// 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"
using namespace cv;
namespace opencv_test { namespace {
TEST(FindEllipsesTest, EllipsesOnly)
{
std::string picture_name = "cv/imgproc/stuff.jpg";
std::string filename = cvtest::TS::ptr()->get_data_path() + picture_name;
Mat src = imread(filename, IMREAD_GRAYSCALE);
EXPECT_FALSE(src.empty()) << "Invalid test image: " << filename;
std::vector<Vec6f> ells;
ximgproc::findEllipses(src, ells, 0.7f, 0.75f, 0.02f);
// number check
EXPECT_EQ(ells.size(), size_t(3)) << "Should find 3 ellipses";
// position check
// target centers
Point2f center_1(226.9f, 57.2f);
Point2f center_2(393.1f, 187.0f);
Point2f center_3(208.5f, 307.5f);
// matching
for (auto ell: ells) {
bool has_match = false;
for (auto c: {center_1, center_2, center_3}) {
Point2f diff = c - Point2f(ell[0], ell[1]);
float distance = sqrt(diff.x * diff.x + diff.y * diff.y);
if (distance < 5.0) {
has_match = true;
break;
}
}
EXPECT_TRUE(has_match) << "Wrong ellipse center:" << Point2f(ell[0], ell[1]);
}
}
}}
+331
View File
@@ -0,0 +1,331 @@
// 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"
namespace opencv_test { namespace {
const Size img_size(320, 240);
const int FLD_TEST_SEED = 0x134679;
const int EPOCHS = 5;
class FLDBase : public testing::Test
{
public:
FLDBase() { }
protected:
Mat test_image;
vector<Vec4f> lines;
RNG rng;
int passedtests;
void GenerateWhiteNoise(Mat& image);
void GenerateConstColor(Mat& image);
void GenerateLines(Mat& image, const unsigned int numLines);
void GenerateEdgeLines(Mat& image, const unsigned int numLines);
void GenerateBrokenLines(Mat& image, const unsigned int numLines);
void GenerateRotatedRect(Mat& image);
virtual void SetUp();
};
class ximgproc_FLD: public FLDBase
{
public:
ximgproc_FLD() { }
protected:
};
class ximgproc_ED: public FLDBase
{
public:
ximgproc_ED()
{
detector = createEdgeDrawing();
}
string filename = cvtest::TS::ptr()->get_data_path() + "cv/imgproc/beads.jpg";
protected:
Ptr<EdgeDrawing> detector;
};
void FLDBase::GenerateWhiteNoise(Mat& image)
{
image = Mat(img_size, CV_8UC1);
rng.fill(image, RNG::UNIFORM, 0, 256);
}
void FLDBase::GenerateConstColor(Mat& image)
{
image = Mat(img_size, CV_8UC1, Scalar::all(rng.uniform(0, 256)));
}
void FLDBase::GenerateLines(Mat& image, const unsigned int numLines)
{
image = Mat(img_size, CV_8UC1, Scalar::all(rng.uniform(0, 128)));
for(unsigned int i = 0; i < numLines; ++i)
{
int y = rng.uniform(10, img_size.width - 10);
Point p1(y, 10);
Point p2(y, img_size.height - 10);
line(image, p1, p2, Scalar(255), 2);
}
}
void FLDBase::GenerateEdgeLines(Mat& image, const unsigned int numLines)
{
image = Mat(img_size, CV_8UC1, Scalar::all(0));
for(unsigned int i = 0; i < numLines; ++i)
{
int y = rng.uniform(10, img_size.width - 10);
Point p1(y, 10);
Point p2(y, img_size.height - 10);
line(image, p1, p2, Scalar(255), 1);
}
}
void FLDBase::GenerateBrokenLines(Mat& image, const unsigned int numLines)
{
image = Mat(img_size, CV_8UC1, Scalar::all(rng.uniform(0, 128)));
for(unsigned int i = 0; i < numLines; ++i)
{
int y = rng.uniform(10, img_size.width - 10);
Point p1(y, 10);
Point p2(y, img_size.height/2);
line(image, p1, p2, Scalar(255), 2);
p1 = Point2i(y, img_size.height/2 + 3);
p2 = Point2i(y, img_size.height - 10);
line(image, p1, p2, Scalar(255), 2);
}
}
void FLDBase::GenerateRotatedRect(Mat& image)
{
image = Mat::zeros(img_size, CV_8UC1);
Point center(rng.uniform(img_size.width/4, img_size.width*3/4),
rng.uniform(img_size.height/4, img_size.height*3/4));
Size rect_size(rng.uniform(img_size.width/8, img_size.width/6),
rng.uniform(img_size.height/8, img_size.height/6));
float angle = rng.uniform(0.f, 360.f);
Point2f vertices[4];
RotatedRect rRect = RotatedRect(center, rect_size, angle);
rRect.points(vertices);
for (int i = 0; i < 4; i++)
{
line(image, vertices[i], vertices[(i + 1) % 4], Scalar(255), 3);
}
}
void FLDBase::SetUp()
{
lines.clear();
test_image = Mat();
rng = RNG(FLD_TEST_SEED);
passedtests = 0;
}
TEST_F(ximgproc_FLD, whiteNoise)
{
for (int i = 0; i < EPOCHS; ++i)
{
GenerateWhiteNoise(test_image);
Ptr<FastLineDetector> detector = createFastLineDetector(20);
detector->detect(test_image, lines);
if(40u >= lines.size()) ++passedtests;
}
ASSERT_EQ(EPOCHS, passedtests);
}
TEST_F(ximgproc_FLD, constColor)
{
for (int i = 0; i < EPOCHS; ++i)
{
GenerateConstColor(test_image);
Ptr<FastLineDetector> detector = createFastLineDetector();
detector->detect(test_image, lines);
if(0u == lines.size()) ++passedtests;
}
ASSERT_EQ(EPOCHS, passedtests);
}
TEST_F(ximgproc_FLD, lines)
{
for (int i = 0; i < EPOCHS; ++i)
{
const unsigned int numOfLines = 1;
GenerateLines(test_image, numOfLines);
Ptr<FastLineDetector> detector = createFastLineDetector();
detector->detect(test_image, lines);
if(numOfLines * 2 == lines.size()) ++passedtests; // * 2 because of Gibbs effect
}
ASSERT_EQ(EPOCHS, passedtests);
}
TEST_F(ximgproc_FLD, edgeLines)
{
for (int i = 0; i < EPOCHS; ++i)
{
const unsigned int numOfLines = 1;
GenerateEdgeLines(test_image, numOfLines);
Ptr<FastLineDetector> detector = createFastLineDetector(10, 1.414213562f, 50, 50, 0);
detector->detect(test_image, lines);
if(numOfLines == lines.size()) ++passedtests;
}
ASSERT_EQ(EPOCHS, passedtests);
}
TEST_F(ximgproc_FLD, mergeLines)
{
for (int i = 0; i < EPOCHS; ++i)
{
const unsigned int numOfLines = 1;
GenerateBrokenLines(test_image, numOfLines);
Ptr<FastLineDetector> detector = createFastLineDetector(10, 1.414213562f, true);
detector->detect(test_image, lines);
if(numOfLines * 2 == lines.size()) ++passedtests; // * 2 because of Gibbs effect
}
ASSERT_EQ(EPOCHS, passedtests);
}
TEST_F(ximgproc_FLD, rotatedRect)
{
for (int i = 0; i < EPOCHS; ++i)
{
GenerateRotatedRect(test_image);
Ptr<FastLineDetector> detector = createFastLineDetector();
detector->detect(test_image, lines);
if(2u <= lines.size()) ++passedtests;
}
ASSERT_EQ(EPOCHS, passedtests);
}
//************** EDGE DRAWING *******************
TEST_F(ximgproc_ED, whiteNoise)
{
for (int i = 0; i < EPOCHS; ++i)
{
GenerateWhiteNoise(test_image);
detector->detectEdges(test_image);
detector->detectLines(lines);
if(2u >= lines.size()) ++passedtests;
}
ASSERT_EQ(EPOCHS, passedtests);
}
TEST_F(ximgproc_ED, constColor)
{
for (int i = 0; i < EPOCHS; ++i)
{
GenerateConstColor(test_image);
detector->detectEdges(test_image);
if(0u == detector->getSegments().size()) ++passedtests;
}
ASSERT_EQ(EPOCHS, passedtests);
}
TEST_F(ximgproc_ED, lines)
{
for (int i = 0; i < EPOCHS; ++i)
{
const unsigned int numOfLines = 1;
GenerateLines(test_image, numOfLines);
detector->detectEdges(test_image);
detector->detectLines(lines);
if(numOfLines * 2 == lines.size()) ++passedtests; // * 2 because of Gibbs effect
}
ASSERT_EQ(EPOCHS, passedtests);
}
TEST_F(ximgproc_ED, mergeLines)
{
for (int i = 0; i < EPOCHS; ++i)
{
const unsigned int numOfLines = 1;
GenerateBrokenLines(test_image, numOfLines);
detector->detectEdges(test_image);
detector->detectLines(lines);
if(numOfLines * 2 == lines.size()) ++passedtests; // * 2 because of Gibbs effect
}
ASSERT_EQ(EPOCHS, passedtests);
}
TEST_F(ximgproc_ED, rotatedRect)
{
for (int i = 0; i < EPOCHS; ++i)
{
GenerateRotatedRect(test_image);
detector->detectEdges(test_image);
detector->detectLines(lines);
if(6u <= lines.size()) ++passedtests;
}
ASSERT_EQ(EPOCHS, passedtests);
}
TEST_F(ximgproc_ED, detectLinesAndEllipses)
{
Mat gray_image;
vector<Vec6d> ellipses;
test_image = imread(filename);
EXPECT_FALSE(test_image.empty()) << "Invalid test image: " << filename;
cvtColor(test_image, test_image, COLOR_BGR2BGRA);
cvtColor(test_image, gray_image, COLOR_BGR2GRAY);
detector->detectEdges(gray_image);
detector->detectEllipses(ellipses);
detector->detectLines(lines);
size_t segments_size = 6458;
size_t lines_size = 6264;
size_t ellipses_size = 2449;
EXPECT_EQ(detector->getSegments().size(), segments_size);
EXPECT_GE(lines.size(), lines_size);
EXPECT_LE(lines.size(), lines_size + 2);
EXPECT_EQ(ellipses.size(), ellipses_size);
detector->params.PFmode = true;
detector->detectEdges(gray_image);
detector->detectEllipses(ellipses);
detector->detectLines(lines);
segments_size = 2717;
lines_size = 6197;
ellipses_size = 2446;
EXPECT_EQ(detector->getSegments().size(), segments_size);
EXPECT_GE(lines.size(), lines_size);
EXPECT_LE(lines.size(), lines_size + 2);
EXPECT_EQ(ellipses.size(), ellipses_size);
detector->params.MinLineLength = 10;
detector->detectEdges(test_image);
detector->detectEllipses(ellipses);
detector->detectLines(lines);
detector->detectEllipses(ellipses);
segments_size = 6230;
lines_size = 11133;
ellipses_size = 2431;
EXPECT_EQ(detector->getSegments().size(), segments_size);
EXPECT_GE(lines.size(), lines_size);
EXPECT_LE(lines.size(), lines_size + 2);
EXPECT_GE(ellipses.size(), ellipses_size);
EXPECT_LE(ellipses.size(), ellipses_size + 2);
}
}} // namespace
@@ -0,0 +1,90 @@
// 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"
namespace opencv_test { namespace {
TEST(ximgproc_fourierdescriptors,test_FD_AND_FIT)
{
Mat fd;
vector<Point2f> ctr(16);
float Rx = 100, Ry = 100;
Point2f g(0, 0);
float angleOri = 0;
for (int i = 0; i < static_cast<int>(ctr.size()); i++)
{
float theta = static_cast<float>(2 * CV_PI / static_cast<int>(ctr.size()) * i + angleOri);
ctr[i] = Point2f(Rx * cos(theta) + g.x, Ry * sin(theta) + g.y);
}
ximgproc::fourierDescriptor(ctr, fd);
CV_Assert(cv::norm(fd.at<Vec2f>(0, 0)) < ctr.size() * FLT_EPSILON && cv::norm(fd.at<Vec2f>(0, 1) - Vec2f(Rx, 0)) < ctr.size() * FLT_EPSILON);
Rx = 100, Ry = 50;
g = Point2f(50, 20);
for (int i = 0; i < static_cast<int>(ctr.size()); i++)
{
float theta = static_cast<float>(2 * CV_PI / static_cast<int>(ctr.size()) * i + angleOri);
ctr[i] = Point2f(Rx * cos(theta) + g.x, Ry * sin(theta) + g.y);
}
ximgproc::fourierDescriptor(ctr, fd);
CV_Assert(cv::norm(fd.at<Vec2f>(0, 0) - Vec2f(g)) < 1 &&
fabs(fd.at<Vec2f>(0, 1)[0] + fd.at<Vec2f>(0, static_cast<int>(ctr.size()) - 1)[0] - Rx) < 1 &&
fabs(fd.at<Vec2f>(0, 1)[0] - fd.at<Vec2f>(0, static_cast<int>(ctr.size()) - 1)[0] - Ry) < 1);
Rx = 70, Ry = 100;
g = Point2f(30, 100);
angleOri = static_cast<float>(CV_PI / 4);
for (int i = 0; i < static_cast<int>(ctr.size()); i++)
{
float theta = static_cast<float>(2 * CV_PI / static_cast<int>(ctr.size()) * i + CV_PI / 4);
ctr[i] = Point2f(Rx * cos(theta) + g.x, Ry * sin(theta) + g.y);
}
ximgproc::fourierDescriptor(ctr, fd);
CV_Assert(cv::norm(fd.at<Vec2f>(0, 0) - Vec2f(g)) < 1);
CV_Assert(cv::norm(Vec2f((Rx + Ry)*cos(angleOri) / 2, (Rx + Ry)*sin(angleOri) / 2) - fd.at<Vec2f>(0, 1)) < 1);
CV_Assert(cv::norm(Vec2f((Rx - Ry)*cos(angleOri) / 2, -(Rx - Ry)*sin(angleOri) / 2) - fd.at<Vec2f>(0, static_cast<int>(ctr.size()) - 1)) < 1);
RNG rAlea;
g.x = 0; g.y = 0;
ctr.resize(256);
for (int i = 0; i < static_cast<int>(ctr.size()); i++)
{
ctr[i] = Point2f(rAlea.uniform(0.0F, 1.0F), rAlea.uniform(0.0F, 1.0F));
g += ctr[i];
}
g.x = g.x / ctr.size();
g.y = g.y / ctr.size();
double rotAngle = 35;
double s = 0.1515;
Mat r = getRotationMatrix2D(g, rotAngle, 0.1515);
vector<Point2f> unknownCtr;
vector<Point2f> ctrShift;
int valShift = 170;
for (int i = 0; i < static_cast<int>(ctr.size()); i++)
ctrShift.push_back(ctr[(i + valShift) % ctr.size()]);
cv::transform(ctrShift, unknownCtr, r);
ximgproc::ContourFitting fit;
fit.setFDSize(16);
Mat t;
double dist;
fit.estimateTransformation(unknownCtr, ctr, t, &dist, false);
CV_Assert(fabs(t.at<double>(0, 0)*ctr.size() + valShift) < 10 || fabs((1 - t.at<double>(0, 0))*ctr.size() - valShift) < 10);
CV_Assert(fabs(t.at<double>(0, 1) - rotAngle / 180.*CV_PI) < 0.1);
CV_Assert(fabs(t.at<double>(0, 2) - 1 / s) < 0.1);
ctr.resize(4);
ctr[0] = Point2f(0, 0);
ctr[1] = Point2f(16, 0);
ctr[2] = Point2f(16, 16);
ctr[3] = Point2f(0, 16);
double squareArea = contourArea(ctr), lengthSquare = arcLength(ctr, true);
Mat ctrs;
ximgproc::contourSampling(ctr, ctrs, 64);
CV_Assert(fabs(squareArea - contourArea(ctrs)) < FLT_EPSILON);
CV_Assert(fabs(lengthSquare - arcLength(ctrs, true)) < FLT_EPSILON);
}
}} // namespace
@@ -0,0 +1,452 @@
// 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"
namespace opencv_test { namespace {
#ifndef SQR
#define SQR(x) ((x)*(x))
#endif
static string getOpenCVExtraDir()
{
return cvtest::TS::ptr()->get_data_path();
}
static Mat convertTypeAndSize(Mat src, int dstType, Size dstSize)
{
Mat dst;
int srcCnNum = src.channels();
int dstCnNum = CV_MAT_CN(dstType);
CV_Assert(srcCnNum == 3);
if (srcCnNum == dstCnNum)
{
src.copyTo(dst);
}
else if (dstCnNum == 1 && srcCnNum == 3)
{
cvtColor(src, dst, COLOR_BGR2GRAY);
}
else if (dstCnNum == 1 && srcCnNum == 4)
{
cvtColor(src, dst, COLOR_BGRA2GRAY);
}
else
{
vector<Mat> srcCn;
split(src, srcCn);
srcCn.resize(dstCnNum);
uint64 seed = 10000 * src.rows + 1000 * src.cols + 100 * dstSize.height + 10 * dstSize.width + dstType;
RNG rnd(seed);
for (int i = srcCnNum; i < dstCnNum; i++)
{
Mat& donor = srcCn[i % srcCnNum];
double minVal, maxVal;
minMaxLoc(donor, &minVal, &maxVal);
Mat randItem(src.size(), CV_MAKE_TYPE(src.depth(), 1));
randn(randItem, 0, (maxVal - minVal) / 100);
cv::add(donor, randItem, srcCn[i]); // TODO cvtest
}
merge(srcCn, dst);
}
dst.convertTo(dst, dstType);
resize(dst, dst, dstSize, 0, 0, INTER_LINEAR_EXACT);
return dst;
}
static double laplacianVariance(Mat src)
{
Mat laplacian;
Laplacian(src, laplacian, CV_64F);
Scalar mean, stddev;
meanStdDev(laplacian, mean, stddev);
double variance = stddev.val[0] * stddev.val[0];
return variance;
}
class GuidedFilterRefImpl : public GuidedFilter
{
int height, width, rad, chNum;
Mat det;
Mat *channels, *exps, **vars, **A;
double eps;
void meanFilter(const Mat &src, Mat & dst);
void computeCovGuide();
void computeCovGuideInv();
void applyTransform(int cNum, Mat *Ichannels, Mat *beta, Mat **alpha, int dDepth);
void computeCovGuideAndSrc(int cNum, Mat **vars_I, Mat *Ichannels, Mat *exp_I);
void computeBeta(int cNum, Mat *beta, Mat *exp_I, Mat **alpha);
void computeAlpha(int cNum, Mat **alpha, Mat **vars_I);
public:
GuidedFilterRefImpl(InputArray guide_, int rad, double eps);
void filter(InputArray src, OutputArray dst, int dDepth = -1);
~GuidedFilterRefImpl();
};
void GuidedFilterRefImpl::meanFilter(const Mat &src, Mat & dst)
{
boxFilter(src, dst, CV_32F, Size(2 * rad + 1, 2 * rad + 1), Point(-1, -1), true, BORDER_REFLECT);
}
GuidedFilterRefImpl::GuidedFilterRefImpl(InputArray _guide, int _rad, double _eps) :
height(_guide.rows()), width(_guide.cols()), rad(_rad), chNum(_guide.channels()), eps(_eps)
{
Mat guide = _guide.getMat();
CV_Assert(chNum > 0 && chNum <= 3);
channels = new Mat[chNum];
exps = new Mat[chNum];
A = new Mat *[chNum];
vars = new Mat *[chNum];
for (int i = 0; i < chNum; ++i)
{
A[i] = new Mat[chNum];
vars[i] = new Mat[chNum];
}
split(guide, channels);
for (int i = 0; i < chNum; ++i)
{
channels[i].convertTo(channels[i], CV_32F);
meanFilter(channels[i], exps[i]);
}
computeCovGuide();
computeCovGuideInv();
}
void GuidedFilterRefImpl::computeCovGuide()
{
static const int pY[] = { 0, 0, 1, 0, 1, 2 };
static const int pX[] = { 0, 1, 1, 2, 2, 2 };
int numOfIterations = (SQR(chNum) - chNum) / 2 + chNum;
for (int k = 0; k < numOfIterations; ++k)
{
int i = pY[k], j = pX[k];
vars[i][j] = channels[i].mul(channels[j]);
meanFilter(vars[i][j], vars[i][j]);
vars[i][j] -= exps[i].mul(exps[j]);
if (i == j)
vars[i][j] += eps * Mat::ones(height, width, CV_32F);
else
vars[j][i] = vars[i][j];
}
}
void GuidedFilterRefImpl::computeCovGuideInv()
{
static const int pY[] = { 0, 0, 1, 0, 1, 2 };
static const int pX[] = { 0, 1, 1, 2, 2, 2 };
int numOfIterations = (SQR(chNum) - chNum) / 2 + chNum;
if (chNum == 3)
{
for (int k = 0; k < numOfIterations; ++k){
int i = pY[k], i1 = (pY[k] + 1) % 3, i2 = (pY[k] + 2) % 3;
int j = pX[k], j1 = (pX[k] + 1) % 3, j2 = (pX[k] + 2) % 3;
A[i][j] = vars[i1][j1].mul(vars[i2][j2])
- vars[i1][j2].mul(vars[i2][j1]);
}
}
else if (chNum == 2)
{
A[0][0] = vars[1][1];
A[1][1] = vars[0][0];
A[0][1] = -vars[0][1];
}
else if (chNum == 1)
A[0][0] = Mat::ones(height, width, CV_32F);
for (int i = 0; i < chNum; ++i)
for (int j = 0; j < i; ++j)
A[i][j] = A[j][i];
det = vars[0][0].mul(A[0][0]);
for (int k = 0; k < chNum - 1; ++k)
det += vars[0][k + 1].mul(A[0][k + 1]);
}
GuidedFilterRefImpl::~GuidedFilterRefImpl(){
delete [] channels;
delete [] exps;
for (int i = 0; i < chNum; ++i)
{
delete [] A[i];
delete [] vars[i];
}
delete [] A;
delete [] vars;
}
void GuidedFilterRefImpl::filter(InputArray src_, OutputArray dst_, int dDepth)
{
if (dDepth == -1) dDepth = src_.depth();
dst_.create(height, width, src_.type());
Mat src = src_.getMat();
Mat dst = dst_.getMat();
int cNum = src.channels();
CV_Assert(height == src.rows && width == src.cols);
Mat *Ichannels, *exp_I, **vars_I, **alpha, *beta;
Ichannels = new Mat[cNum];
exp_I = new Mat[cNum];
beta = new Mat[cNum];
vars_I = new Mat *[chNum];
alpha = new Mat *[chNum];
for (int i = 0; i < chNum; ++i){
vars_I[i] = new Mat[cNum];
alpha[i] = new Mat[cNum];
}
split(src, Ichannels);
for (int i = 0; i < cNum; ++i)
{
Ichannels[i].convertTo(Ichannels[i], CV_32F);
meanFilter(Ichannels[i], exp_I[i]);
}
computeCovGuideAndSrc(cNum, vars_I, Ichannels, exp_I);
computeAlpha(cNum, alpha, vars_I);
computeBeta(cNum, beta, exp_I, alpha);
for (int i = 0; i < chNum + 1; ++i)
for (int j = 0; j < cNum; ++j)
if (i < chNum)
meanFilter(alpha[i][j], alpha[i][j]);
else
meanFilter(beta[j], beta[j]);
applyTransform(cNum, Ichannels, beta, alpha, dDepth);
merge(Ichannels, cNum, dst);
delete [] Ichannels;
delete [] exp_I;
delete [] beta;
for (int i = 0; i < chNum; ++i)
{
delete [] vars_I[i];
delete [] alpha[i];
}
delete [] vars_I;
delete [] alpha;
}
void GuidedFilterRefImpl::computeAlpha(int cNum, Mat **alpha, Mat **vars_I)
{
for (int i = 0; i < chNum; ++i)
for (int j = 0; j < cNum; ++j)
{
alpha[i][j] = vars_I[0][j].mul(A[i][0]);
for (int k = 1; k < chNum; ++k)
alpha[i][j] += vars_I[k][j].mul(A[i][k]);
alpha[i][j] /= det;
}
}
void GuidedFilterRefImpl::computeBeta(int cNum, Mat *beta, Mat *exp_I, Mat **alpha)
{
for (int i = 0; i < cNum; ++i)
{
beta[i] = exp_I[i];
for (int j = 0; j < chNum; ++j)
beta[i] -= alpha[j][i].mul(exps[j]);
}
}
void GuidedFilterRefImpl::computeCovGuideAndSrc(int cNum, Mat **vars_I, Mat *Ichannels, Mat *exp_I)
{
for (int i = 0; i < chNum; ++i)
for (int j = 0; j < cNum; ++j)
{
vars_I[i][j] = channels[i].mul(Ichannels[j]);
meanFilter(vars_I[i][j], vars_I[i][j]);
vars_I[i][j] -= exp_I[j].mul(exps[i]);
}
}
void GuidedFilterRefImpl::applyTransform(int cNum, Mat *Ichannels, Mat *beta, Mat **alpha, int dDepth)
{
for (int i = 0; i < cNum; ++i)
{
Ichannels[i] = beta[i];
for (int j = 0; j < chNum; ++j)
Ichannels[i] += alpha[j][i].mul(channels[j]);
Ichannels[i].convertTo(Ichannels[i], dDepth);
}
}
typedef tuple<int, string, string> GFParams;
typedef TestWithParam<GFParams> GuidedFilterTest;
TEST_P(GuidedFilterTest, accuracy)
{
GFParams params = GetParam();
int guideCnNum = 3;
int srcCnNum = get<0>(params);
string guideFileName = get<1>(params);
string srcFileName = get<2>(params);
int seed = 100 * guideCnNum + 50 * srcCnNum + 5*(int)guideFileName.length() + (int)srcFileName.length();
RNG rng(seed);
Mat guide = imread(getOpenCVExtraDir() + guideFileName);
Mat src = imread(getOpenCVExtraDir() + srcFileName);
ASSERT_TRUE(!guide.empty() && !src.empty());
Size dstSize(guide.cols + 1 + rng.uniform(0, 3), guide.rows);
guide = convertTypeAndSize(guide, CV_MAKE_TYPE(guide.depth(), guideCnNum), dstSize);
src = convertTypeAndSize(src, CV_MAKE_TYPE(src.depth(), srcCnNum), dstSize);
int nThreads = cv::getNumThreads();
if (nThreads == 1)
throw SkipTestException("Single thread environment");
for (int iter = 0; iter < 2; iter++)
{
int radius = rng.uniform(0, 50);
double eps = rng.uniform(0.0, SQR(255.0));
cv::setNumThreads(nThreads);
Mat res;
Ptr<GuidedFilter> gf = createGuidedFilter(guide, radius, eps);
gf->filter(src, res);
cv::setNumThreads(1);
Mat resRef;
Ptr<GuidedFilter> gfRef(new GuidedFilterRefImpl(guide, radius, eps));
gfRef->filter(src, resRef);
double normInf = cv::norm(res, resRef, NORM_INF);
double normL2 = cv::norm(res, resRef, NORM_L2) / guide.total();
EXPECT_LE(normInf, 1.0);
EXPECT_LE(normL2, 1.0/64.0);
}
}
TEST_P(GuidedFilterTest, accuracyFastGuidedFilter)
{
int radius = 8;
double eps = 1;
GFParams params = GetParam();
string guideFileName = get<1>(params);
string srcFileName = get<2>(params);
int guideCnNum = 3;
int srcCnNum = get<0>(params);
Mat guide = imread(getOpenCVExtraDir() + guideFileName);
Mat src = imread(getOpenCVExtraDir() + srcFileName);
ASSERT_TRUE(!guide.empty() && !src.empty());
Size dstSize(guide.cols, guide.rows);
guide = convertTypeAndSize(guide, CV_MAKE_TYPE(guide.depth(), guideCnNum), dstSize);
src = convertTypeAndSize(src, CV_MAKE_TYPE(src.depth(), srcCnNum), dstSize);
Mat outputRef;
ximgproc::guidedFilter(guide, src, outputRef, radius, eps);
for (double scale : {1./2, 1./3, 1./4}) {
Mat outputFastGuidedFilter;
ximgproc::guidedFilter(guide, src, outputFastGuidedFilter, radius, eps, -1, scale);
Mat guideNaiveDownsampled, srcNaiveDownsampled, outputNaiveDownsampled;
resize(guide, guideNaiveDownsampled, {}, scale, scale, INTER_LINEAR);
resize(src, srcNaiveDownsampled, {}, scale, scale, INTER_LINEAR);
ximgproc::guidedFilter(guideNaiveDownsampled, srcNaiveDownsampled, outputNaiveDownsampled, radius, eps);
resize(outputNaiveDownsampled, outputNaiveDownsampled, dstSize, 0, 0, INTER_LINEAR);
double laplacianVarianceFastGuidedFilter = laplacianVariance(outputFastGuidedFilter);
double laplacianVarianceNaiveDownsampled = laplacianVariance(outputNaiveDownsampled);
EXPECT_GT(laplacianVarianceFastGuidedFilter, laplacianVarianceNaiveDownsampled);
double normL2 = cv::norm(outputFastGuidedFilter, outputRef, NORM_L2) / guide.total();
EXPECT_LE(normL2, 1.0/48.0/scale);
}
}
TEST_P(GuidedFilterTest, smallParamsIssue)
{
GFParams params = GetParam();
string guideFileName = get<1>(params);
string srcFileName = get<2>(params);
int guideCnNum = 3;
int srcCnNum = get<0>(params);
Mat guide = imread(getOpenCVExtraDir() + guideFileName);
Mat src = imread(getOpenCVExtraDir() + srcFileName);
ASSERT_TRUE(!guide.empty() && !src.empty());
Size dstSize(guide.cols, guide.rows);
guide = convertTypeAndSize(guide, CV_MAKE_TYPE(guide.depth(), guideCnNum), dstSize);
src = convertTypeAndSize(src, CV_MAKE_TYPE(src.depth(), srcCnNum), dstSize);
Mat output;
ximgproc::guidedFilter(guide, src, output, 3, 1e-6);
size_t whitePixels = 0;
for(int i = 0; i < output.cols; i++)
{
for(int j = 0; j < output.rows; j++)
{
if(output.channels() == 1)
{
if(output.ptr<uchar>(i)[j] == 255)
whitePixels++;
}
else if(output.channels() == 3)
{
Vec3b currentPixel = output.ptr<Vec3b>(i)[j];
if(currentPixel == Vec3b(255, 255, 255))
whitePixels++;
}
}
}
double whiteRate = whitePixels / (double) output.total();
EXPECT_LE(whiteRate, 0.1);
}
INSTANTIATE_TEST_CASE_P(TypicalSet, GuidedFilterTest,
Combine(
Values(1, 3),
Values("cv/shared/lena.png", "cv/shared/baboon.png"),
Values("cv/shared/lena.png", "cv/shared/baboon.png")
));
}} // namespace
@@ -0,0 +1,244 @@
// 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"
namespace opencv_test { namespace {
static std::string getOpenCVExtraDir()
{
return cvtest::TS::ptr()->get_data_path();
}
static void checkSimilarity(InputArray src, InputArray ref)
{
double normInf = cvtest::norm(src, ref, NORM_INF);
double normL2 = cvtest::norm(src, ref, NORM_L2) / (src.total()*src.channels());
EXPECT_LE(normInf, 1.0);
EXPECT_LE(normL2, 1.0 / 16);
}
static Mat convertTypeAndSize(Mat src, int dstType, Size dstSize)
{
Mat dst;
int srcCnNum = src.channels();
int dstCnNum = CV_MAT_CN(dstType);
if (srcCnNum == dstCnNum)
{
src.copyTo(dst);
}
else if (srcCnNum == 3 && dstCnNum == 1)
{
cvtColor(src, dst, COLOR_BGR2GRAY);
}
else if (srcCnNum == 1 && dstCnNum == 3)
{
cvtColor(src, dst, COLOR_GRAY2BGR);
}
else
{
CV_Error(Error::BadNumChannels, "Bad num channels in src");
}
dst.convertTo(dst, dstType);
resize(dst, dst, dstSize, 0, 0, INTER_LINEAR_EXACT);
return dst;
}
//////////////////////////////////////////////////////////////////////////
//////////////////////////////////////////////////////////////////////////
void jointBilateralFilterNaive(InputArray joint, InputArray src, OutputArray dst, int d, double sigmaColor, double sigmaSpace, int borderType = BORDER_DEFAULT);
typedef Vec<float, 1> Vec1f;
typedef Vec<uchar, 1> Vec1b;
#ifndef SQR
#define SQR(a) ((a)*(a))
#endif
template<typename T, int cn>
float normL1Sqr(const Vec<T, cn>& a, const Vec<T, cn>& b)
{
float res = 0.0f;
for (int i = 0; i < cn; i++)
res += std::abs((float)a[i] - (float)b[i]);
return res*res;
}
template<typename JointVec, typename SrcVec>
void jointBilateralFilterNaive_(InputArray joint_, InputArray src_, OutputArray dst_, int d, double sigmaColor, double sigmaSpace, int borderType)
{
CV_Assert(joint_.size() == src_.size());
CV_Assert(joint_.type() == traits::Type<JointVec>::value && src_.type() == traits::Type<SrcVec>::value);
typedef Vec<float, SrcVec::channels> SrcVecf;
if (sigmaColor <= 0)
sigmaColor = 1;
if (sigmaSpace <= 0)
sigmaSpace = 1;
int radius;
if (d <= 0)
radius = cvRound(sigmaSpace*1.5);
else
radius = d / 2;
radius = std::max(radius, 1);
d = 2 * radius + 1;
dst_.create(src_.size(), src_.type());
Mat_<SrcVec> dst = dst_.getMat();
Mat_<JointVec> jointExt;
Mat_<SrcVec> srcExt;
cv::copyMakeBorder(src_, srcExt, radius, radius, radius, radius, borderType); // TODO cvtest
cv::copyMakeBorder(joint_, jointExt, radius, radius, radius, radius, borderType); // TODO cvtest
float colorGaussCoef = (float)(-0.5 / (sigmaColor*sigmaColor));
float spaceGaussCoef = (float)(-0.5 / (sigmaSpace*sigmaSpace));
for (int i = radius; i < srcExt.rows - radius; i++)
{
for (int j = radius; j < srcExt.cols - radius; j++)
{
JointVec joint0 = jointExt(i, j);
SrcVecf sum = SrcVecf::all(0.0f);
float sumWeights = 0.0f;
for (int k = -radius; k <= radius; k++)
{
for (int l = -radius; l <= radius; l++)
{
float spatDistSqr = (float)(k*k + l*l);
if (spatDistSqr > SQR(radius)) continue;
float colorDistSqr = normL1Sqr(joint0, jointExt(i + k, j + l));
float weight = std::exp(spatDistSqr*spaceGaussCoef + colorDistSqr*colorGaussCoef);
sum += weight*SrcVecf(srcExt(i + k, j + l));
sumWeights += weight;
}
}
dst(i - radius, j - radius) = sum / sumWeights;
}
}
}
void jointBilateralFilterNaive(InputArray joint, InputArray src, OutputArray dst, int d, double sigmaColor, double sigmaSpace, int borderType)
{
CV_Assert(src.size() == joint.size() && src.depth() == joint.depth());
CV_Assert(src.type() == CV_32FC1 || src.type() == CV_32FC3 || src.type() == CV_8UC1 || src.type() == CV_8UC3);
CV_Assert(joint.type() == CV_32FC1 || joint.type() == CV_32FC3 || joint.type() == CV_8UC1 || joint.type() == CV_8UC3);
int jointType = joint.type();
int srcType = src.type();
#define JBF_naive(VecJoint, VecSrc) jointBilateralFilterNaive_<VecJoint, VecSrc>(joint, src, dst, d, sigmaColor, sigmaSpace, borderType);
if (jointType == CV_8UC1)
{
if (srcType == CV_8UC1) JBF_naive(Vec1b, Vec1b);
if (srcType == CV_8UC3) JBF_naive(Vec1b, Vec3b);
}
if (jointType == CV_8UC3)
{
if (srcType == CV_8UC1) JBF_naive(Vec3b, Vec1b);
if (srcType == CV_8UC3) JBF_naive(Vec3b, Vec3b);
}
if (jointType == CV_32FC1)
{
if (srcType == CV_32FC1) JBF_naive(Vec1f, Vec1f);
if (srcType == CV_32FC3) JBF_naive(Vec1f, Vec3f);
}
if (jointType == CV_32FC3)
{
if (srcType == CV_32FC1) JBF_naive(Vec3f, Vec1f);
if (srcType == CV_32FC3) JBF_naive(Vec3f, Vec3f);
}
#undef JBF_naive
}
//////////////////////////////////////////////////////////////////////////
//////////////////////////////////////////////////////////////////////////
typedef tuple<string, string> JBFTestParam;
typedef TestWithParam<JBFTestParam> JointBilateralFilterTest_NaiveRef;
TEST_P(JointBilateralFilterTest_NaiveRef, Accuracy)
{
JBFTestParam param = GetParam();
double sigmaS = 8.0;
string jointPath = get<0>(param);
string srcPath = get<1>(param);
int depth = CV_8U;
int jCn = 3;
int srcCn = 1;
int jointType = CV_MAKE_TYPE(depth, jCn);
int srcType = CV_MAKE_TYPE(depth, srcCn);
Mat joint = imread(getOpenCVExtraDir() + jointPath);
Mat src = imread(getOpenCVExtraDir() + srcPath);
ASSERT_TRUE(!joint.empty() && !src.empty());
joint = convertTypeAndSize(joint, jointType, joint.size());
src = convertTypeAndSize(src, srcType, joint.size());
RNG rnd(cvRound(10*sigmaS) + jointType + srcType + jointPath.length() + srcPath.length() + joint.rows + joint.cols);
double sigmaC = rnd.uniform(0, 255);
Mat resNaive;
jointBilateralFilterNaive(joint, src, resNaive, 0, sigmaC, sigmaS);
Mat res;
jointBilateralFilter(joint, src, res, 0, sigmaC, sigmaS);
checkSimilarity(res, resNaive);
}
INSTANTIATE_TEST_CASE_P(Set2, JointBilateralFilterTest_NaiveRef,
Combine(
Values("/cv/shared/airplane.png", "/cv/shared/fruits.png"),
Values("/cv/shared/airplane.png", "/cv/shared/fruits.png"))
);
//////////////////////////////////////////////////////////////////////////
//////////////////////////////////////////////////////////////////////////
typedef tuple<string, int> BFTestParam;
typedef TestWithParam<BFTestParam> JointBilateralFilterTest_BilateralRef;
TEST_P(JointBilateralFilterTest_BilateralRef, Accuracy)
{
BFTestParam param = GetParam();
double sigmaS = 4.0;
string srcPath = get<0>(param);
int srcType = get<1>(param);
Mat src = imread(getOpenCVExtraDir() + srcPath);
ASSERT_TRUE(!src.empty());
src = convertTypeAndSize(src, srcType, src.size());
RNG rnd(cvRound(10*sigmaS) + srcPath.length() + srcType + src.rows);
double sigmaC = rnd.uniform(0.0, 255.0);
Mat resRef;
bilateralFilter(src, resRef, 0, sigmaC, sigmaS);
Mat res, joint = src.clone();
jointBilateralFilter(joint, src, res, 0, sigmaC, sigmaS);
checkSimilarity(res, resRef);
}
INSTANTIATE_TEST_CASE_P(Set1, JointBilateralFilterTest_BilateralRef,
Combine(
Values("/cv/shared/lena.png", "cv/shared/box_in_scene.png"),
Values(CV_8UC3, CV_32FC1)
)
);
}} // namespace
+83
View File
@@ -0,0 +1,83 @@
// 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"
namespace opencv_test { namespace {
CV_ENUM(SrcTypes, CV_8UC1, CV_8UC3, CV_16UC1, CV_16UC3);
typedef tuple<Size, SrcTypes> L0SmoothParams;
typedef TestWithParam<L0SmoothParams> L0SmoothTest;
TEST(L0SmoothTest, SplatSurfaceAccuracy)
{
RNG rnd(0);
for (int i = 0; i < 3; i++)
{
Size sz(rnd.uniform(512, 1024), rnd.uniform(512, 1024));
Scalar surfaceValue;
int srcCn = 3;
rnd.fill(surfaceValue, RNG::UNIFORM, 0, 255);
Mat src(sz, CV_MAKE_TYPE(CV_8U, srcCn), surfaceValue);
double lambda = rnd.uniform(0.01, 0.05);
double kappa = rnd.uniform(1.5, 5.0);
Mat res;
l0Smooth(src, res, lambda, kappa);
// When filtering a constant image we should get the same image:
double normL1 = cvtest::norm(src, res, NORM_L1)/src.total()/src.channels();
EXPECT_LE(normL1, 1.0/64);
}
}
TEST_P(L0SmoothTest, MultiThreadReproducibility)
{
if (cv::getNumberOfCPUs() == 1)
return;
double MAX_DIF = 10.0;
double MAX_MEAN_DIF = 1.0 / 8.0;
int loopsCount = 2;
RNG rng(0);
L0SmoothParams params = GetParam();
Size size = get<0>(params);
int srcType = get<1>(params);
Mat src(size,srcType);
if(src.depth()==CV_8U)
randu(src, 0, 255);
else if(src.depth()==CV_16U)
randu(src, 0, 65535);
else
randu(src, -100000.0f, 100000.0f);
int nThreads = cv::getNumThreads();
if (nThreads == 1)
throw SkipTestException("Single thread environment");
for (int iter = 0; iter <= loopsCount; iter++)
{
double lambda = rng.uniform(0.01, 0.05);
double kappa = rng.uniform(1.5, 5.0);
cv::setNumThreads(nThreads);
Mat resMultiThread;
l0Smooth(src, resMultiThread, lambda, kappa);
cv::setNumThreads(1);
Mat resSingleThread;
l0Smooth(src, resSingleThread, lambda, kappa);
EXPECT_LE(cv::norm(resSingleThread, resMultiThread, NORM_INF), MAX_DIF);
EXPECT_LE(cv::norm(resSingleThread, resMultiThread, NORM_L1), MAX_MEAN_DIF*src.total()*src.channels());
}
}
INSTANTIATE_TEST_CASE_P(FullSet, L0SmoothTest,Combine(Values(szODD, szQVGA), SrcTypes::all()));
}} // namespace
+6
View File
@@ -0,0 +1,6 @@
// 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"
CV_TEST_MAIN("")
@@ -0,0 +1,81 @@
// 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"
namespace opencv_test { namespace {
TEST(ximgproc_matchcolortemplate,test_QFFT)
{
String openCVExtraDir = cvtest::TS::ptr()->get_data_path();
String dataPath = openCVExtraDir;
#ifdef GENERATE_TESTDATA
FileStorage fs;
dataPath += "cv/ximgproc/sources/07.png";
Mat imgTest = imread(dataPath, IMREAD_COLOR);
resize(imgTest, imgTest, Size(), 0.0625, 0.0625);
Mat qimgTest, qdftimgTest;
ximgproc::createQuaternionImage(imgTest, qimgTest);
ximgproc::qdft(qimgTest, qdftimgTest, 0, true);
fs.open(openCVExtraDir + "cv/ximgproc/qdftData.yml.gz", FileStorage::WRITE);
fs << "image" << imgTest;
fs << "qdftleft" << qdftimgTest;
ximgproc::qdft(qimgTest, qdftimgTest, 0, false);
fs << "qdftright" << qdftimgTest;
ximgproc::qdft(qimgTest, qdftimgTest, DFT_INVERSE, true);
fs << "qidftleft" << qdftimgTest;
ximgproc::qdft(qimgTest, qdftimgTest, DFT_INVERSE, false);
fs << "qidftright" << qdftimgTest;
fs.release();
#endif
dataPath = openCVExtraDir + "cv/ximgproc/qdftData.yml.gz";
FileStorage f;
f.open(dataPath, FileStorage::READ);
Mat img;
f["image"] >> img;
Mat qTest;
vector<String> nodeName = { "qdftleft","qdftright","qidftleft","qidftright" };
vector<int> flag = { 0,0,DFT_INVERSE,DFT_INVERSE };
vector<bool> leftSize = {true,false,true,false};
ximgproc::createQuaternionImage(img, img);
for (int i=0;i<static_cast<int>(nodeName.size());i++)
{
Mat test, dd;
f[nodeName[i]] >> qTest;
ximgproc::qdft(img, test, flag[i], leftSize[i]);
absdiff(test, qTest, dd);
vector<Mat> plane;
split(dd, plane);
for (auto p : plane)
{
double maxVal;
Point pIdx;
minMaxLoc(p, NULL, &maxVal, NULL, &pIdx);
ASSERT_LE(p.at<double>(pIdx), 1e-5);
}
}
}
TEST(ximgproc_matchcolortemplate, test_COLORMATCHTEMPLATE)
{
String openCVExtraDir = cvtest::TS::ptr()->get_data_path();
String dataPath = openCVExtraDir + "cv/ximgproc/corr.yml.gz";
Mat img, logo;
Mat corrRef,corr;
img = imread(openCVExtraDir + "cv/ximgproc/image.png", IMREAD_COLOR);
logo = imread(openCVExtraDir + "cv/ximgproc/opencv_logo.png", IMREAD_COLOR);
ximgproc::colorMatchTemplate(img, logo, corr);
#ifdef GENERATE_TESTDATA
FileStorage fs;
fs.open(dataPath, FileStorage::WRITE);
fs << "corr" << imgcorr;
fs.release();
#endif
FileStorage f;
f.open(dataPath, FileStorage::READ);
f["corr"] >> corrRef;
EXPECT_LE(cv::norm(corr, corrRef, NORM_INF), 1e-5);
}
}} // namespace
@@ -0,0 +1,29 @@
// 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"
namespace opencv_test { namespace {
TEST(ximgproc_niBlackThreshold, sauvola)
{
Mat src = (Mat_<uchar>(3, 3) << 1, 1, 1, 2, 2, 2, 3, 3, 3);
Mat dst;
cv::ximgproc::niBlackThreshold(src, dst, 255, THRESH_BINARY, 3, 1, BINARIZATION_SAUVOLA, 1);
EXPECT_EQ(CV_8U, dst.type());
EXPECT_EQ(3, dst.rows);
EXPECT_EQ(3, dst.cols);
EXPECT_EQ(0, dst.at<uchar>(0, 0));
EXPECT_EQ(0, dst.at<uchar>(0, 1));
EXPECT_EQ(0, dst.at<uchar>(0, 2));
EXPECT_EQ(0, dst.at<uchar>(1, 0));
EXPECT_EQ(0, dst.at<uchar>(1, 1));
EXPECT_EQ(0, dst.at<uchar>(1, 2));
EXPECT_EQ(255, dst.at<uchar>(2, 0));
EXPECT_EQ(255, dst.at<uchar>(2, 1));
EXPECT_EQ(255, dst.at<uchar>(2, 2));
}
}} // namespace
+20
View File
@@ -0,0 +1,20 @@
// 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.
#ifndef __OPENCV_TEST_PRECOMP_HPP__
#define __OPENCV_TEST_PRECOMP_HPP__
#include "opencv2/ximgproc.hpp"
#include "opencv2/ts.hpp"
#include <opencv2/ts/ts_perf.hpp>
#include <opencv2/core/utility.hpp>
#include <opencv2/geometry.hpp>
namespace opencv_test {
using namespace cv::ximgproc;
using namespace perf; // szODD
Ptr<AdaptiveManifoldFilter> createAMFilterRefImpl(double sigma_s, double sigma_r, bool adjust_outliers);
}
#endif
@@ -0,0 +1,81 @@
// 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"
namespace opencv_test {namespace {
TEST(RadonTransformTest, output_size)
{
Mat src(Size(256, 256), CV_8U, Scalar(0));
circle(src, Point(128, 128), 64, Scalar(255), FILLED);
Mat radon;
cv::ximgproc::RadonTransform(src, radon);
EXPECT_EQ(363, radon.rows);
EXPECT_EQ(180, radon.cols);
cv::ximgproc::RadonTransform(src, radon, 1, 0, 180, true);
EXPECT_EQ(256, radon.rows);
EXPECT_EQ(180, radon.cols);
}
TEST(RadonTransformTest, output_type)
{
Mat src_int(Size(256, 256), CV_8U, Scalar(0));
circle(src_int, Point(128, 128), 64, Scalar(255), FILLED);
Mat radon, radon_norm;
cv::ximgproc::RadonTransform(src_int, radon);
cv::ximgproc::RadonTransform(src_int, radon_norm, 1, 0, 180, false, true);
EXPECT_EQ(CV_32SC1, radon.type());
EXPECT_EQ(CV_8U, radon_norm.type());
Mat src_float(Size(256, 256), CV_32FC1, Scalar(0));
Mat src_double(Size(256, 256), CV_32FC1, Scalar(0));
cv::ximgproc::RadonTransform(src_float, radon);
cv::ximgproc::RadonTransform(src_float, radon_norm, 1, 0, 180, false, true);
EXPECT_EQ(CV_64FC1, radon.type());
EXPECT_EQ(CV_8U, radon_norm.type());
cv::ximgproc::RadonTransform(src_double, radon);
EXPECT_EQ(CV_64FC1, radon.type());
EXPECT_EQ(CV_8U, radon_norm.type());
}
TEST(RadonTransformTest, accuracy_by_pixel)
{
Mat src(Size(256, 256), CV_8U, Scalar(0));
circle(src, Point(128, 128), 64, Scalar(255), FILLED);
Mat radon;
cv::ximgproc::RadonTransform(src, radon);
ASSERT_EQ(CV_32SC1, radon.type());
EXPECT_EQ(0, radon.at<int>(0, 0));
EXPECT_LT(18000, radon.at<int>(128, 128));
EXPECT_GT(19000, radon.at<int>(128, 128));
}
TEST(RadonTransformTest, accuracy_uchar)
{
Mat src(Size(10, 10), CV_8UC1, Scalar(1));
cv::Mat radon;
ximgproc::RadonTransform(src, radon, 45, 0, 180, false, false);
EXPECT_EQ(100, sum(radon.col(0))[0]);
}
TEST(RadonTransformTest, accuracy_float)
{
Mat src(Size(10, 10), CV_32FC1, Scalar(1.1));
cv::Mat radon;
ximgproc::RadonTransform(src, radon, 45, 0, 180, false, false);
EXPECT_LT(109, sum(radon.col(0))[0]);
EXPECT_GT(111, sum(radon.col(0))[0]);
}
} }
@@ -0,0 +1,27 @@
// 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"
namespace opencv_test { namespace {
TEST(ximgproc_ridgedetectionfilter, ReferenceAccuracy)
{
String openCVExtraDir = cvtest::TS::ptr()->get_data_path();
String srcImgPath = "cv/ximgproc/sources/04.png";
String refPath = "cv/ximgproc/results/ridge_filter_test_ref/04.png";
Mat src = imread(openCVExtraDir + srcImgPath);
Mat ref = imread(openCVExtraDir + refPath, 0);
Mat n_ref;
ref.convertTo(n_ref, CV_8UC1);
Ptr<RidgeDetectionFilter> rdf = RidgeDetectionFilter::create();
Mat out;
rdf->getRidgeFilteredImage(src, out);
Mat out_cmp;
out.convertTo(out_cmp, CV_8UC1);
EXPECT_LE(cvtest::norm(out, ref, NORM_INF), 0.0f);
EXPECT_LE(cvtest::norm(out, ref, NORM_L2 | NORM_RELATIVE), .0f);
}
}} // namespace
@@ -0,0 +1,174 @@
// 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"
namespace opencv_test { namespace {
static std::string getOpenCVExtraDir()
{
return cvtest::TS::ptr()->get_data_path();
}
static void checkSimilarity(InputArray src, InputArray ref)
{
// Doesn't work with bilateral filter: EXPECT_LE(cvtest::norm(src, ref, NORM_INF), 1.0);
EXPECT_LE(cvtest::norm(src, ref, NORM_L2 | NORM_RELATIVE), 1e-3);
}
static Mat convertTypeAndSize(Mat src, int dstType, Size dstSize)
{
Mat dst;
int srcCnNum = src.channels();
int dstCnNum = CV_MAT_CN(dstType);
if (srcCnNum == dstCnNum)
{
src.copyTo(dst);
}
else if (srcCnNum == 3 && dstCnNum == 1)
{
cvtColor(src, dst, COLOR_BGR2GRAY);
}
else if (srcCnNum == 1 && dstCnNum == 3)
{
cvtColor(src, dst, COLOR_GRAY2BGR);
}
else
{
CV_Error(Error::BadNumChannels, "Bad num channels in src");
}
dst.convertTo(dst, dstType);
resize(dst, dst, dstSize, 0, 0, INTER_LINEAR_EXACT);
return dst;
}
//////////////////////////////////////////////////////////////////////////
//////////////////////////////////////////////////////////////////////////
typedef tuple<double, MatType, int> RGFParams;
typedef TestWithParam<RGFParams> RollingGuidanceFilterTest;
TEST_P(RollingGuidanceFilterTest, SplatSurfaceAccuracy)
{
RGFParams params = GetParam();
double sigmaS = get<0>(params);
int depth = get<1>(params);
int srcCn = get<2>(params);
RNG rnd(0);
Size sz(rnd.uniform(512,1024), rnd.uniform(512,1024));
for (int i = 0; i < 5; i++)
{
Scalar surfaceValue;
rnd.fill(surfaceValue, RNG::UNIFORM, 0, 255);
Mat src(sz, CV_MAKE_TYPE(depth, srcCn), surfaceValue);
double sigmaC = rnd.uniform(1.0, 255.0);
int iterNum = int(rnd.uniform(1.0, 5.0));
Mat res;
rollingGuidanceFilter(src, res, -1, sigmaC, sigmaS, iterNum);
double normL1 = cvtest::norm(src, res, NORM_L1)/src.total()/src.channels();
EXPECT_LE(normL1, 1.0/64);
}
}
TEST_P(RollingGuidanceFilterTest, MultiThreadReproducibility)
{
if (cv::getNumberOfCPUs() == 1)
return;
RGFParams params = GetParam();
double sigmaS = get<0>(params);
int depth = get<1>(params);
int srcCn = get<2>(params);
double MAX_DIF = 1.0;
double MAX_MEAN_DIF = 1.0 / 64.0;
int loopsCount = 2;
RNG rnd(1);
Size sz(rnd.uniform(512,1024), rnd.uniform(512,1024));
Mat src(sz,CV_MAKE_TYPE(depth, srcCn));
if(src.depth()==CV_8U)
randu(src, 0, 255);
else if(src.depth()==CV_16S)
randu(src, -32767, 32767);
else
randu(src, -100000.0f, 100000.0f);
int nThreads = cv::getNumThreads();
if (nThreads == 1)
throw SkipTestException("Single thread environment");
for (int iter = 0; iter <= loopsCount; iter++)
{
int iterNum = int(rnd.uniform(1.0, 5.0));
double sigmaC = rnd.uniform(1.0, 255.0);
cv::setNumThreads(nThreads);
Mat resMultiThread;
rollingGuidanceFilter(src, resMultiThread, -1, sigmaC, sigmaS, iterNum);
cv::setNumThreads(1);
Mat resSingleThread;
rollingGuidanceFilter(src, resSingleThread, -1, sigmaC, sigmaS, iterNum);
EXPECT_LE(cv::norm(resSingleThread, resMultiThread, NORM_INF), MAX_DIF);
EXPECT_LE(cv::norm(resSingleThread, resMultiThread, NORM_L1), MAX_MEAN_DIF*src.total()*src.channels());
}
}
INSTANTIATE_TEST_CASE_P(TypicalSet1, RollingGuidanceFilterTest,
Combine(
Values(2.0, 5.0),
Values(CV_8U, CV_32F),
Values(1, 3)
)
);
//////////////////////////////////////////////////////////////////////////
//////////////////////////////////////////////////////////////////////////
typedef tuple<double, string, int> RGFBFParam;
typedef TestWithParam<RGFBFParam> RollingGuidanceFilterTest_BilateralRef;
TEST_P(RollingGuidanceFilterTest_BilateralRef, Accuracy)
{
RGFBFParam params = GetParam();
double sigmaS = get<0>(params);
string srcPath = get<1>(params);
int srcType = get<2>(params);
Mat src = imread(getOpenCVExtraDir() + srcPath);
ASSERT_TRUE(!src.empty());
src = convertTypeAndSize(src, srcType, src.size());
RNG rnd(0);
double sigmaC = rnd.uniform(0.0, 255.0);
Mat resRef;
bilateralFilter(src, resRef, 0, sigmaC, sigmaS);
Mat res, joint = src.clone();
rollingGuidanceFilter(src, res, 0, sigmaC, sigmaS, 1);
checkSimilarity(res, resRef);
}
INSTANTIATE_TEST_CASE_P(TypicalSet2, RollingGuidanceFilterTest_BilateralRef,
Combine(
Values(4.0, 6.0, 8.0),
Values("/cv/shared/pic2.png", "/cv/shared/lena.png", "cv/shared/box_in_scene.png"),
Values(CV_8UC1, CV_8UC3, CV_32FC1, CV_32FC3)
)
);
}} // namespace
@@ -0,0 +1,249 @@
// 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/ximgproc/run_length_morphology.hpp"
#include "opencv2/imgproc.hpp"
namespace opencv_test {
namespace {
const Size img_size(640, 480);
const int tile_size(20);
typedef tuple<cv::MorphTypes, int, int> RLMParams;
typedef tuple<cv::MorphTypes, int, int, int> RLMSParams;
class RLTestBase
{
public:
RLTestBase() { }
protected:
std::vector<Mat> test_image;
std::vector<Mat> test_image_rle;
void generateCheckerBoard(Mat& image);
void generateRandomImage(Mat& image);
bool areImagesIdentical(Mat& pixelImage, Mat& rleImage);
bool arePixelImagesIdentical(Mat& image1, Mat& image2);
void setUp_impl();
};
void RLTestBase::generateCheckerBoard(Mat& image)
{
image.create(img_size, CV_8UC1);
for (int iy = 0; iy < img_size.height; iy += tile_size)
{
Range rowRange(iy, std::min(iy + tile_size, img_size.height));
for (int ix = 0; ix < img_size.width; ix += tile_size)
{
Range colRange(ix, std::min(ix + tile_size, img_size.width));
Mat tile(image, rowRange, colRange);
bool bBright = ((iy + ix) % (2 * tile_size) == 0);
tile = (bBright ? Scalar(255) : Scalar(0));
}
}
}
void RLTestBase::generateRandomImage(Mat& image)
{
image.create(img_size, CV_8UC1);
randu(image, Scalar::all(0), Scalar::all(255));
}
void RLTestBase::setUp_impl()
{
test_image.resize(2);
test_image_rle.resize(2);
generateCheckerBoard(test_image[0]);
rl::threshold(test_image[0], test_image_rle[0], 100.0, THRESH_BINARY);
cv::Mat theRandom;
generateRandomImage(theRandom);
double dThreshold = 254.0;
cv::threshold(theRandom, test_image[1], dThreshold, 255.0, THRESH_BINARY);
rl::threshold(theRandom, test_image_rle[1], dThreshold, THRESH_BINARY);
}
bool RLTestBase::areImagesIdentical(Mat& pixelImage, Mat& rleImage)
{
cv::Mat rleConverted;
rleConverted = cv::Mat::zeros(pixelImage.rows, pixelImage.cols, CV_8UC1);
rl::paint(rleConverted, rleImage, Scalar(255.0));
return arePixelImagesIdentical(pixelImage, rleConverted);
}
bool RLTestBase::arePixelImagesIdentical(Mat& image1, Mat& image2)
{
cv::Mat diff;
cv::absdiff(image1, image2, diff);
int nDiff = cv::countNonZero(diff);
return (nDiff == 0);
}
class RL_Identical_Result_Simple : public RLTestBase, public ::testing::TestWithParam<RLMSParams>
{
public:
RL_Identical_Result_Simple() { }
protected:
virtual void SetUp() { setUp_impl(); }
};
TEST_P(RL_Identical_Result_Simple, simple)
{
Mat resPix, resRLE;
RLMSParams param = GetParam();
cv::MorphTypes elementType = get<0>(param);
int nSize = get<1>(param);
int image = get<2>(param);
int op = get<3>(param);
Mat element = getStructuringElement(elementType, Size(nSize * 2 + 1, nSize * 2 + 1),
Point(nSize, nSize));
morphologyEx(test_image[image], resPix, op, element);
Mat elementRLE = rl::getStructuringElement(elementType, Size(nSize * 2 + 1, nSize * 2 + 1));
rl::morphologyEx(test_image_rle[image], resRLE, op, elementRLE);
ASSERT_TRUE(areImagesIdentical(resPix, resRLE));
}
INSTANTIATE_TEST_CASE_P(TypicalSET, RL_Identical_Result_Simple, Combine(Values(MORPH_RECT, MORPH_CROSS, MORPH_ELLIPSE),
Values(1, 5, 11), Values(0, 1), Values(MORPH_ERODE, MORPH_DILATE, MORPH_OPEN, MORPH_CLOSE, MORPH_GRADIENT, MORPH_TOPHAT, MORPH_BLACKHAT)));
class RL_Identical_Result : public RLTestBase, public ::testing::TestWithParam<RLMParams>
{
public:
RL_Identical_Result() { }
protected:
virtual void SetUp() { setUp_impl(); }
};
TEST_P(RL_Identical_Result, erosion_no_boundary)
{
Mat resPix, resRLE;
RLMParams param = GetParam();
cv::MorphTypes elementType = get<0>(param);
int nSize = get<1>(param);
int image = get<2>(param);
Mat element = getStructuringElement(elementType, Size(nSize * 2 + 1, nSize * 2 + 1),
Point(nSize, nSize));
erode(test_image[image], resPix, element, cv::Point(-1,-1), 1, BORDER_CONSTANT, cv::Scalar(0));
Mat elementRLE = rl::getStructuringElement(elementType, Size(nSize * 2 + 1, nSize * 2 + 1));
rl::erode(test_image_rle[image], resRLE, elementRLE, false);
ASSERT_TRUE(areImagesIdentical(resPix, resRLE));
}
TEST_P(RL_Identical_Result, erosion_with_offset)
{
Mat resPix, resRLE;
RLMParams param = GetParam();
cv::MorphTypes elementType = get<0>(param);
int nSize = get<1>(param);
int image = get<2>(param);
int nOffset = nSize - 1;
Mat element = getStructuringElement(elementType, Size(nSize * 2 + 1, nSize * 2 + 1),
Point(nSize, nSize));
erode(test_image[image], resPix, element, cv::Point(nSize + nOffset, nSize + nOffset));
Mat elementRLE = rl::getStructuringElement(elementType, Size(nSize * 2 + 1, nSize * 2 + 1));
rl::erode(test_image_rle[image], resRLE, elementRLE, true, Point(nOffset, nOffset));
ASSERT_TRUE(areImagesIdentical(resPix, resRLE));
}
TEST_P(RL_Identical_Result, dilation_with_offset)
{
Mat resPix, resRLE;
RLMParams param = GetParam();
cv::MorphTypes elementType = get<0>(param);
int nSize = get<1>(param);
int image = get<2>(param);
int nOffset = nSize - 1;
Mat element = getStructuringElement(elementType, Size(nSize * 2 + 1, nSize * 2 + 1),
Point(nSize, nSize));
dilate(test_image[image], resPix, element, cv::Point(nSize + nOffset, nSize + nOffset));
Mat elementRLE = rl::getStructuringElement(elementType, Size(nSize * 2 + 1, nSize * 2 + 1));
rl::dilate(test_image_rle[image], resRLE, elementRLE, Point(nOffset, nOffset));
ASSERT_TRUE(areImagesIdentical(resPix, resRLE));
}
INSTANTIATE_TEST_CASE_P(TypicalSET, RL_Identical_Result, Combine(Values(MORPH_RECT, MORPH_CROSS, MORPH_ELLIPSE), Values(1,5,11), Values(0,1)));
class RL_CreateCustomKernel : public RLTestBase, public testing::Test
{
public:
RL_CreateCustomKernel() { }
protected:
virtual void SetUp() { setUp_impl(); }
};
TEST_F(RL_CreateCustomKernel, check_valid)
{
// create a diamond
int nSize = 21;
std::vector<Point3i> runs;
for (int i = 0; i < nSize; ++i)
{
runs.emplace_back(Point3i(-i, i, -nSize + i));
runs.emplace_back(Point3i(-i, i, nSize - i));
}
runs.emplace_back(Point3i(-nSize, nSize, 0));
Mat kernel, dest;
rl::createRLEImage(runs, kernel);
ASSERT_TRUE(rl::isRLMorphologyPossible(kernel));
rl::erode(test_image_rle[0], dest, kernel);
//only one row means: no runs, all pixels off
ASSERT_TRUE(dest.rows == 1);
}
typedef tuple<int> RLPParams;
class RL_Paint : public RLTestBase, public ::testing::TestWithParam<RLPParams>
{
public:
RL_Paint() { }
protected:
virtual void SetUp() { setUp_impl(); }
};
TEST_P(RL_Paint, same_result)
{
Mat converted, pixBinary, painted;
RLPParams param = GetParam();
int rType = get<0>(param);
double dThreshold = 100.0;
double dMaxValue = 105.0;
test_image[1].convertTo(converted, rType);
cv::threshold(converted, pixBinary, dThreshold, dMaxValue, THRESH_BINARY);
painted.create(test_image[1].rows, test_image[1].cols, rType);
painted = cv::Scalar(0.0);
rl::paint(painted, test_image_rle[1], Scalar(dMaxValue));
ASSERT_TRUE(arePixelImagesIdentical(pixBinary, painted));
}
INSTANTIATE_TEST_CASE_P(TypicalSET, RL_Paint, Values(CV_8U, CV_16U, CV_16S, CV_32F, CV_64F));
}
}
@@ -0,0 +1,35 @@
// 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"
namespace opencv_test { namespace {
static void runScanSegment(int slices)
{
Mat img = imread(cvtest::findDataFile("cv/shared/lena.png"), IMREAD_COLOR);
Mat labImg;
cvtColor(img, labImg, COLOR_BGR2Lab);
Ptr<ScanSegment> ss = createScanSegment(labImg.cols, labImg.rows, 500, slices, true);
ss->iterate(labImg);
int numSuperpixels = ss->getNumberOfSuperpixels();
EXPECT_GT(numSuperpixels, 100);
EXPECT_LE(numSuperpixels, 500);
Mat res;
ss->getLabelContourMask(res, false);
EXPECT_GE(cvtest::norm(res, NORM_L1), 1000000);
if (cvtest::debugLevel >= 10)
{
imshow("ScanSegment", res);
waitKey();
}
}
TEST(ximgproc_ScanSegment, smoke) { runScanSegment(1); }
TEST(ximgproc_ScanSegment, smoke4) { runScanSegment(4); }
TEST(ximgproc_ScanSegment, smoke8) { runScanSegment(8); }
}} // namespace
+22
View File
@@ -0,0 +1,22 @@
// 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"
namespace opencv_test { namespace {
TEST(ximgproc_SuperpixelSLIC, smoke)
{
Mat img = imread(cvtest::findDataFile("cv/shared/lena.png"), IMREAD_COLOR);
Mat labImg;
cvtColor(img, labImg, COLOR_BGR2Lab);
Ptr< SuperpixelSLIC> slic = createSuperpixelSLIC(labImg);
slic->iterate(5);
Mat outLabels;
slic->getLabels(outLabels);
EXPECT_FALSE(outLabels.empty());
int numSuperpixels = slic->getNumberOfSuperpixels();
EXPECT_GT(numSuperpixels, 0);
}
}} // namespace
@@ -0,0 +1,220 @@
// 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/ximgproc/sparse_match_interpolator.hpp"
namespace opencv_test { namespace {
static string getDataDir()
{
return cvtest::TS::ptr()->get_data_path();
}
const float FLOW_TAG_FLOAT = 202021.25f;
Mat readOpticalFlow( const String& path )
{
// CV_Assert(sizeof(float) == 4);
//FIXME: ensure right sizes of int and float - here and in writeOpticalFlow()
Mat flow;
std::ifstream file(path.c_str(), std::ios_base::binary);
if ( !file.good() )
return flow; // no file - return empty matrix
float tag;
file.read((char*) &tag, sizeof(float));
if ( tag != FLOW_TAG_FLOAT )
return flow;
int width, height;
file.read((char*) &width, 4);
file.read((char*) &height, 4);
flow.create(height, width, CV_32FC2);
for ( int i = 0; i < flow.rows; ++i )
{
for ( int j = 0; j < flow.cols; ++j )
{
Point2f u;
file.read((char*) &u.x, sizeof(float));
file.read((char*) &u.y, sizeof(float));
if ( !file.good() )
{
flow.release();
return flow;
}
flow.at<Point2f>(i, j) = u;
}
}
file.close();
return flow;
}
CV_ENUM(GuideTypes, CV_8UC1, CV_8UC3)
typedef tuple<Size, GuideTypes> InterpolatorParams;
typedef TestWithParam<InterpolatorParams> InterpolatorTest;
TEST(InterpolatorTest, ReferenceAccuracy)
{
double MAX_DIF = 1.0;
double MAX_MEAN_DIF = 1.0 / 256.0;
string dir = getDataDir() + "cv/sparse_match_interpolator";
Mat src = imread(getDataDir() + "cv/optflow/RubberWhale1.png",IMREAD_COLOR);
ASSERT_FALSE(src.empty());
Mat ref_flow = readOpticalFlow(dir + "/RubberWhale_reference_result.flo");
ASSERT_FALSE(ref_flow.empty());
std::ifstream file((dir + "/RubberWhale_sparse_matches.txt").c_str());
float from_x,from_y,to_x,to_y;
vector<Point2f> from_points;
vector<Point2f> to_points;
while(file >> from_x >> from_y >> to_x >> to_y)
{
from_points.push_back(Point2f(from_x,from_y));
to_points.push_back(Point2f(to_x,to_y));
}
Mat res_flow;
Ptr<EdgeAwareInterpolator> interpolator = createEdgeAwareInterpolator();
interpolator->setK(128);
interpolator->setSigma(0.05f);
interpolator->setUsePostProcessing(true);
interpolator->setFGSLambda(500.0f);
interpolator->setFGSSigma(1.5f);
interpolator->interpolate(src,from_points,Mat(),to_points,res_flow);
EXPECT_LE(cv::norm(res_flow, ref_flow, NORM_INF), MAX_DIF);
EXPECT_LE(cv::norm(res_flow, ref_flow, NORM_L1) , MAX_MEAN_DIF*res_flow.total());
Mat from_point_mat(from_points);
Mat to_points_mat(to_points);
interpolator->interpolate(src,from_point_mat,Mat(),to_points_mat,res_flow);
EXPECT_LE(cv::norm(res_flow, ref_flow, NORM_INF), MAX_DIF);
EXPECT_LE(cv::norm(res_flow, ref_flow, NORM_L1) , MAX_MEAN_DIF*res_flow.total());
}
TEST(InterpolatorTest, RICReferenceAccuracy)
{
double MAX_DIF = 6.0;
double MAX_MEAN_DIF = 60.0 / 256.0;
string dir = getDataDir() + "cv/sparse_match_interpolator";
Mat src = imread(getDataDir() + "cv/optflow/RubberWhale1.png", IMREAD_COLOR);
ASSERT_FALSE(src.empty());
Mat ref_flow = readOpticalFlow(dir + "/RubberWhale_reference_result.flo");
ASSERT_FALSE(ref_flow.empty());
Mat src1 = imread(getDataDir() + "cv/optflow/RubberWhale2.png", IMREAD_COLOR);
ASSERT_FALSE(src.empty());
std::ifstream file((dir + "/RubberWhale_sparse_matches.txt").c_str());
float from_x, from_y, to_x, to_y;
vector<Point2f> from_points;
vector<Point2f> to_points;
while (file >> from_x >> from_y >> to_x >> to_y)
{
from_points.push_back(Point2f(from_x, from_y));
to_points.push_back(Point2f(to_x, to_y));
}
Mat res_flow;
Ptr<RICInterpolator> interpolator = createRICInterpolator();
interpolator->setK(32);
interpolator->setSuperpixelSize(15);
interpolator->setSuperpixelNNCnt(150);
interpolator->setSuperpixelRuler(15.f);
interpolator->setSuperpixelMode(ximgproc::SLIC);
interpolator->setAlpha(0.7f);
interpolator->setModelIter(4);
interpolator->setRefineModels(true);
interpolator->setMaxFlow(250.f);
interpolator->setUseVariationalRefinement(true);
interpolator->setUseGlobalSmootherFilter(true);
interpolator->setFGSLambda(500.f);
interpolator->setFGSSigma(1.5f);
interpolator->interpolate(src, from_points, src1, to_points, res_flow);
EXPECT_LE(cv::norm(res_flow, ref_flow, NORM_INF), MAX_DIF);
EXPECT_LE(cv::norm(res_flow, ref_flow, NORM_L1), MAX_MEAN_DIF*res_flow.total());
Mat from_point_mat(from_points);
Mat to_points_mat(to_points);
interpolator->interpolate(src, from_point_mat, src1, to_points_mat, res_flow);
EXPECT_LE(cv::norm(res_flow, ref_flow, NORM_INF), MAX_DIF);
EXPECT_LE(cv::norm(res_flow, ref_flow, NORM_L1) , MAX_MEAN_DIF*res_flow.total());
}
TEST_P(InterpolatorTest, MultiThreadReproducibility)
{
if (cv::getNumberOfCPUs() == 1)
return;
double MAX_DIF = 1.0;
double MAX_MEAN_DIF = 1.0 / 256.0;
int loopsCount = 2;
RNG rng(0);
InterpolatorParams params = GetParam();
Size size = get<0>(params);
int guideType = get<1>(params);
Mat from(size, guideType);
randu(from, 0, 255);
int num_matches = rng.uniform(5,SHRT_MAX-1);
vector<Point2f> from_points;
vector<Point2f> to_points;
for(int i=0;i<num_matches;i++)
{
from_points.push_back(Point2f(rng.uniform(0.01f,(float)size.width-1.01f),rng.uniform(0.01f,(float)size.height-1.01f)));
to_points.push_back(Point2f(rng.uniform(0.01f,(float)size.width-1.01f),rng.uniform(0.01f,(float)size.height-1.01f)));
}
int nThreads = cv::getNumThreads();
if (nThreads == 1)
throw SkipTestException("Single thread environment");
for (int iter = 0; iter <= loopsCount; iter++)
{
int K = rng.uniform(4,512);
float sigma = rng.uniform(0.01f,0.5f);
float FGSlambda = rng.uniform(100.0f, 10000.0f);
float FGSsigma = rng.uniform(0.5f, 100.0f);
Ptr<EdgeAwareInterpolator> interpolator = createEdgeAwareInterpolator();
interpolator->setK(K);
interpolator->setSigma(sigma);
interpolator->setUsePostProcessing(true);
interpolator->setFGSLambda(FGSlambda);
interpolator->setFGSSigma(FGSsigma);
cv::setNumThreads(nThreads);
Mat resMultiThread;
interpolator->interpolate(from,from_points,Mat(),to_points,resMultiThread);
cv::setNumThreads(1);
Mat resSingleThread;
interpolator->interpolate(from,from_points,Mat(),to_points,resSingleThread);
EXPECT_LE(cv::norm(resSingleThread, resMultiThread, NORM_INF), MAX_DIF);
EXPECT_LE(cv::norm(resSingleThread, resMultiThread, NORM_L1) , MAX_MEAN_DIF*resMultiThread.total());
}
}
INSTANTIATE_TEST_CASE_P(FullSet,InterpolatorTest, Combine(Values(szODD,szVGA), GuideTypes::all()));
}} // namespace
@@ -0,0 +1,42 @@
// 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"
namespace opencv_test { namespace {
TEST(ximgproc_StructuredEdgeDetection, regression)
{
cv::String subfolder = "cv/ximgproc/";
cv::String dir = cvtest::TS::ptr()->get_data_path() + subfolder;
int nTests = 12;
float threshold = 0.01f;
cv::String modelName = dir + "model.yml.gz";
cv::Ptr<cv::ximgproc::StructuredEdgeDetection> pDollar =
cv::ximgproc::createStructuredEdgeDetection(modelName);
for (int i = 0; i < nTests; ++i)
{
cv::String srcName = dir + cv::format( "sources/%02d.png", i + 1);
cv::Mat src = cv::imread( srcName, 1 );
ASSERT_TRUE(!src.empty());
cv::String previousResultName = dir + cv::format( "results/%02d.png", i + 1 );
cv::Mat previousResult = cv::imread( previousResultName, 0 );
previousResult.convertTo( previousResult, CV_32F, 1/255.0 );
src.convertTo( src, CV_32F, 1/255.0 );
cv::Mat currentResult( src.size(), src.type() );
pDollar->detectEdges( src, currentResult );
cv::Mat sqrError = ( currentResult - previousResult )
.mul( currentResult - previousResult );
cv::Scalar mse = cv::sum(sqrError) / cv::Scalar::all( double( sqrError.total() ) );
EXPECT_LE( mse[0], threshold );
}
}
}} // namespace
+57
View File
@@ -0,0 +1,57 @@
// 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"
namespace opencv_test { namespace {
static int createTestImage(Mat1b& src)
{
src = Mat1b::zeros(Size(256, 256));
// Create a corner point that should not be affected.
src(0, 0) = 255;
for (int x = 50; x < src.cols - 50; x += 50)
{
cv::circle(src, Point(x, x/2), 30 + x/2, Scalar(255), 5);
}
int src_pixels = countNonZero(src);
EXPECT_GT(src_pixels, 0);
return src_pixels;
}
TEST(ximgproc_Thinning, simple_ZHANGSUEN)
{
Mat1b src;
int src_pixels = createTestImage(src);
Mat1b dst;
thinning(src, dst, THINNING_ZHANGSUEN);
int dst_pixels = countNonZero(dst);
EXPECT_LE(dst_pixels, src_pixels);
EXPECT_EQ(dst(0, 0), 255);
#if 0
imshow("src", src); imshow("dst", dst); waitKey();
#endif
}
TEST(ximgproc_Thinning, simple_GUOHALL)
{
Mat1b src;
int src_pixels = createTestImage(src);
Mat1b dst;
thinning(src, dst, THINNING_GUOHALL);
int dst_pixels = countNonZero(dst);
EXPECT_LE(dst_pixels, src_pixels);
EXPECT_EQ(dst(0, 0), 255);
#if 0
imshow("src", src); imshow("dst", dst); waitKey();
#endif
}
}} // namespace
@@ -0,0 +1,76 @@
// 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"
namespace opencv_test { namespace {
static string getDataDir()
{
return cvtest::TS::ptr()->get_data_path();
}
typedef tuple<Size, WMFWeightType> WMFParams;
typedef TestWithParam<WMFParams> WeightedMedianFilterTest;
TEST_P(WeightedMedianFilterTest, SplatSurfaceAccuracy)
{
WMFParams params = GetParam();
Size size = get<0>(params);
WMFWeightType weightType = get<1>(params);
RNG rnd(0);
int guideCn = rnd.uniform(1, 2);
if(guideCn==2) guideCn++; //1 or 3 channels
Mat guide(size, CV_MAKE_TYPE(CV_8U, guideCn));
randu(guide, 0, 255);
Scalar surfaceValue;
int srcCn = rnd.uniform(1, 4);
rnd.fill(surfaceValue, RNG::UNIFORM, 0, 255);
Mat src(size, CV_MAKE_TYPE(CV_8U, srcCn), surfaceValue);
int r = int(rnd.uniform(3, 11));
double sigma = rnd.uniform(9.0, 100.0);
Mat res;
weightedMedianFilter(guide, src, res, r, sigma, weightType);
double normL1 = cvtest::norm(src, res, NORM_L1)/src.total()/src.channels();
EXPECT_LE(normL1, 1.0/64);
}
TEST(WeightedMedianFilterTest, ReferenceAccuracy)
{
string dir = getDataDir() + "cv/edgefilter";
Mat src = imread(dir + "/kodim23.png");
Mat ref = imread(dir + "/fgs/kodim23_lambda=1000_sigma=10.png");
ASSERT_FALSE(src.empty());
ASSERT_FALSE(ref.empty());
Mat res;
weightedMedianFilter(src, src, res, 7);
double totalMaxError = 1.0/32.0*src.total()*src.channels();
EXPECT_LE(cvtest::norm(res, ref, NORM_L2), totalMaxError);
}
TEST(WeightedMedianFilterTest, mask_zeros_no_crash)
{
Mat img = imread(getDataDir() + "cv/ximgproc/sources/01.png");
Mat mask = Mat::zeros(img.size(), CV_8U);
Mat filtered;
weightedMedianFilter(img, img, filtered, 3, 20, WMF_EXP, mask);
EXPECT_EQ(cv::norm(img, filtered, NORM_INF), 0.0);
}
INSTANTIATE_TEST_CASE_P(TypicalSET, WeightedMedianFilterTest, Combine(Values(szODD, szQVGA), Values(WMF_EXP, WMF_IV2, WMF_OFF)));
}} // namespace