vendor: OpenCV 5.0.0 snapshot at 755e50675d97db9b7d449d8bd6b09888646f6c6e
This commit is contained in:
@@ -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
|
||||
@@ -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
|
||||
@@ -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
|
||||
@@ -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
|
||||
@@ -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]);
|
||||
}
|
||||
}
|
||||
}}
|
||||
@@ -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
|
||||
@@ -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
|
||||
@@ -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
|
||||
@@ -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
|
||||
@@ -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
|
||||
@@ -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
|
||||
Reference in New Issue
Block a user