Unverified Commit f4d55d51 authored by Alexander Alekhin's avatar Alexander Alekhin Committed by GitHub

imgproc: fix bit-exact GaussianBlur() / sepFilter2D() (#15855)

* imgproc: fix bit-exact GaussianBlur() / sepFilter2D()

- avoid kernels with bad approximation
- GaussiabBlur - apply error-diffusion approximation for kernel (8-bit fraction)

* java(test): update features2d ref data

* test: update test_facedetect
parent 955b2023
......@@ -85,11 +85,11 @@ public class BruteForceDescriptorMatcherTest extends OpenCVTestCase {
matSize = 100;
truth = new DMatch[] {
new DMatch(0, 0, 0, 0.6211397f),
new DMatch(0, 0, 0, 0.6159003f),
new DMatch(1, 1, 0, 0.9177120f),
new DMatch(2, 1, 0, 0.3112163f),
new DMatch(3, 1, 0, 0.2925074f),
new DMatch(4, 1, 0, 0.9309178f)
new DMatch(4, 1, 0, 0.26520672f)
};
}
......
......@@ -85,11 +85,11 @@ public class BruteForceL1DescriptorMatcherTest extends OpenCVTestCase {
matSize = 100;
truth = new DMatch[] {
new DMatch(0, 0, 0, 3.0975165f),
new DMatch(1, 1, 0, 3.5680308f),
new DMatch(2, 1, 0, 1.3722466f),
new DMatch(3, 1, 0, 1.3041023f),
new DMatch(4, 1, 0, 3.5970376f)
new DMatch(0, 0, 0, 3.0710702f),
new DMatch(1, 1, 0, 3.562016f),
new DMatch(2, 1, 0, 1.3682679f),
new DMatch(3, 1, 0, 1.3012862f),
new DMatch(4, 1, 0, 1.1852086f)
};
}
......
......@@ -90,11 +90,11 @@ public class BruteForceSL2DescriptorMatcherTest extends OpenCVTestCase {
matSize = 100;
truth = new DMatch[] {
new DMatch(0, 0, 0, 0.3858146f),
new DMatch(0, 0, 0, 0.37933317f),
new DMatch(1, 1, 0, 0.8421953f),
new DMatch(2, 1, 0, 0.0968556f),
new DMatch(3, 1, 0, 0.0855606f),
new DMatch(4, 1, 0, 0.8666080f)
new DMatch(4, 1, 0, 0.07033461f)
};
}
......
......@@ -160,11 +160,11 @@ public class FlannBasedDescriptorMatcherTest extends OpenCVTestCase {
matcher = DescriptorMatcher.create(DescriptorMatcher.FLANNBASED);
matSize = 100;
truth = new DMatch[] {
new DMatch(0, 0, 0, 0.6211397f),
new DMatch(0, 0, 0, 0.6159003f),
new DMatch(1, 1, 0, 0.9177120f),
new DMatch(2, 1, 0, 0.3112163f),
new DMatch(3, 1, 0, 0.2925075f),
new DMatch(4, 1, 0, 0.9309179f)
new DMatch(4, 1, 0, 0.26520672f)
};
}
......
......@@ -53,7 +53,7 @@ public class ORBDescriptorExtractorTest extends OpenCVTestCase {
Mat truth = new Mat(1, 32, CvType.CV_8UC1) {
{
put(0, 0,
6, 74, 6, 129, 2, 130, 56, 0, 36, 132, 66, 165, 172, 6, 3, 72, 102, 61, 163, 214, 0, 144, 65, 232, 4, 32, 138, 129, 4, 21, 37, 88);
6, 74, 6, 129, 2, 130, 56, 0, 44, 132, 66, 165, 172, 6, 3, 72, 102, 61, 171, 214, 0, 144, 65, 232, 4, 32, 138, 131, 4, 21, 37, 217);
}
};
assertDescriptorsClose(truth, descriptors, 1);
......@@ -92,7 +92,7 @@ public class ORBDescriptorExtractorTest extends OpenCVTestCase {
Mat truth = new Mat(1, 32, CvType.CV_8UC1) {
{
put(0, 0,
6, 10, 22, 5, 2, 130, 56, 0, 44, 164, 66, 165, 140, 6, 1, 72, 38, 61, 163, 210, 0, 208, 1, 104, 4, 32, 10, 131, 0, 37, 37, 67);
6, 10, 22, 5, 2, 130, 56, 0, 44, 164, 66, 165, 140, 6, 1, 72, 38, 61, 163, 210, 0, 208, 1, 104, 4, 32, 74, 131, 0, 37, 37, 67);
}
};
assertDescriptorsClose(truth, descriptors, 1);
......
......@@ -41,6 +41,12 @@
//M*/
#include "precomp.hpp"
#include <opencv2/core/utils/logger.defines.hpp>
#undef CV_LOG_STRIP_LEVEL
#define CV_LOG_STRIP_LEVEL CV_LOG_LEVEL_DEBUG + 1
#include <opencv2/core/utils/logger.hpp>
#include "opencv2/core/opencl/ocl_defs.hpp"
#include "opencl_kernels_imgproc.hpp"
#include "hal_replacement.hpp"
......@@ -273,6 +279,22 @@ Ptr<BaseColumnFilter> getLinearColumnFilter(
CV_CPU_DISPATCH_MODES_ALL);
}
static bool createBitExactKernel_32S(const Mat& kernel, Mat& kernel_dst, int bits)
{
kernel.convertTo(kernel_dst, CV_32S, (1 << bits));
Mat_<double> kernel_64f;
kernel.convertTo(kernel_64f, CV_64F, (1 << bits));
int ksize = (int)kernel.total();
const double eps = 10 * FLT_EPSILON * (1 << bits);
for (int i = 0; i < ksize; i++)
{
int bitExactValue = kernel_dst.at<int>(i);
double approxValue = kernel_64f.at<double>(i);
if (fabs(approxValue - bitExactValue) > eps)
return false;
}
return true;
}
Ptr<FilterEngine> createSeparableLinearFilter(
int _srcType, int _dstType,
......@@ -299,6 +321,7 @@ Ptr<FilterEngine> createSeparableLinearFilter(
_columnKernel.rows == 1 ? Point(_anchor.y, 0) : Point(0, _anchor.y));
Mat rowKernel, columnKernel;
bool isBitExactMode = false;
int bdepth = std::max(CV_32F,std::max(sdepth, ddepth));
int bits = 0;
......@@ -311,14 +334,27 @@ Ptr<FilterEngine> createSeparableLinearFilter(
(rtype & ctype & KERNEL_INTEGER) &&
ddepth == CV_16S)) )
{
bdepth = CV_32S;
bits = ddepth == CV_8U ? 8 : 0;
_rowKernel.convertTo( rowKernel, CV_32S, 1 << bits );
_columnKernel.convertTo( columnKernel, CV_32S, 1 << bits );
bits *= 2;
_delta *= (1 << bits);
int bits_ = ddepth == CV_8U ? 8 : 0;
bool isValidBitExactRowKernel = createBitExactKernel_32S(_rowKernel, rowKernel, bits_);
bool isValidBitExactColumnKernel = createBitExactKernel_32S(_columnKernel, columnKernel, bits_);
if (!isValidBitExactRowKernel)
{
CV_LOG_DEBUG(NULL, "createSeparableLinearFilter: bit-exact row-kernel can't be applied: ksize=" << _rowKernel.total());
}
else if (!isValidBitExactColumnKernel)
{
CV_LOG_DEBUG(NULL, "createSeparableLinearFilter: bit-exact column-kernel can't be applied: ksize=" << _columnKernel.total());
}
else
{
bdepth = CV_32S;
bits = bits_;
bits *= 2;
_delta *= (1 << bits);
isBitExactMode = true;
}
}
else
if (!isBitExactMode)
{
if( _rowKernel.type() != bdepth )
_rowKernel.convertTo( rowKernel, bdepth );
......
......@@ -355,6 +355,9 @@ public:
CV_ALWAYS_INLINE bool isZero() { return val == 0; }
static CV_ALWAYS_INLINE ufixedpoint16 zero() { return ufixedpoint16(); }
static CV_ALWAYS_INLINE ufixedpoint16 one() { return ufixedpoint16((uint16_t)(1 << fixedShift)); }
static CV_ALWAYS_INLINE ufixedpoint16 fromRaw(uint16_t v) { return ufixedpoint16(v); }
CV_ALWAYS_INLINE ufixedpoint16 raw() { return val; }
};
}
......
This diff is collapsed.
......@@ -59,6 +59,13 @@ protected:
bool fp_kernel;
bool inplace;
int border;
void dump_test_case(int test_case_idx, std::ostream* out) CV_OVERRIDE
{
ArrayTest::dump_test_case(test_case_idx, out);
*out << "border=" << border << std::endl;
}
};
......@@ -685,6 +692,12 @@ protected:
void get_test_array_types_and_sizes( int test_case_idx, vector<vector<Size> >& sizes, vector<vector<int> >& types );
double get_success_error_level( int test_case_idx, int i, int j );
const char* smooth_type;
void dump_test_case(int test_case_idx, std::ostream* out) CV_OVERRIDE
{
CV_FilterBaseTest::dump_test_case(test_case_idx, out);
*out << "smooth_type=" << smooth_type << std::endl;
}
};
......@@ -795,6 +808,12 @@ protected:
double get_success_error_level( int /*test_case_idx*/, int /*i*/, int /*j*/ );
double sigma;
int param1, param2;
void dump_test_case(int test_case_idx, std::ostream* out) CV_OVERRIDE
{
CV_SmoothBaseTest::dump_test_case(test_case_idx, out);
*out << "kernel=(" << param1 << ", " << param2 << ") sigma=" << sigma << std::endl;
}
};
......@@ -838,7 +857,7 @@ void CV_GaussianBlurTest::run_func()
// !!! Copied from cvSmooth, if the code is changed in cvSmooth,
// make sure to update this one too.
#define SMALL_GAUSSIAN_SIZE 7
#define SMALL_GAUSSIAN_SIZE 9
static void
calcGaussianKernel( int n, double sigma, vector<float>& kernel )
{
......@@ -847,14 +866,15 @@ calcGaussianKernel( int n, double sigma, vector<float>& kernel )
{1.f},
{0.25f, 0.5f, 0.25f},
{0.0625f, 0.25f, 0.375f, 0.25f, 0.0625f},
{0.03125, 0.109375, 0.21875, 0.28125, 0.21875, 0.109375, 0.03125}
{0.03125, 0.109375, 0.21875, 0.28125, 0.21875, 0.109375, 0.03125},
{4.0 / 256, 13.0 / 256, 30.0 / 256, 51.0 / 256, 60.0 / 256, 51.0 / 256, 30.0 / 256, 13.0 / 256, 4.0 / 256}
};
kernel.resize(n);
if( n <= SMALL_GAUSSIAN_SIZE && sigma <= 0 )
{
assert( n%2 == 1 );
memcpy( &kernel[0], small_gaussian_tab[n>>1], n*sizeof(kernel[0]));
CV_Assert(n%2 == 1);
memcpy(&kernel[0], small_gaussian_tab[n / 2], n*sizeof(kernel[0]));
}
else
{
......
......@@ -14,11 +14,14 @@ namespace opencv_test { namespace {
{ fixedOne >> 2, fixedOne >> 1, fixedOne >> 2 }, // size 3, sigma 0
{ fixedOne >> 4, fixedOne >> 2, 6 * (fixedOne >> 4), fixedOne >> 2, fixedOne >> 4 }, // size 5, sigma 0
{ fixedOne >> 5, 7 * (fixedOne >> 6), 7 * (fixedOne >> 5), 9 * (fixedOne >> 5), 7 * (fixedOne >> 5), 7 * (fixedOne >> 6), fixedOne >> 5 }, // size 7, sigma 0
{ 4, 13, 30, 51, 61, 51, 30, 13, 4 }, // size 9, sigma 0
{ 81, 95, 81 }, // size 3, sigma 1.75
{ 65, 125, 65 }, // size 3, sigma 0.875
{ 4, 13, 30, 51, 60, 51, 30, 13, 4 }, // size 9, sigma 0
#if 1
#define CV_TEST_INACCURATE_GAUSSIAN_BLUR
{ 81, 94, 81 }, // size 3, sigma 1.75
{ 65, 126, 65 }, // size 3, sigma 0.875
{ 0, 7, 242, 7, 0 }, // size 5, sigma 0.375
{ 4, 56, 136, 56, 4 } // size 5, sigma 0.75
#endif
};
template <typename T, int fixedShift>
......@@ -68,11 +71,13 @@ TEST(GaussianBlur_Bitexact, Linear8U)
{ CV_8UC1, Size( 256, 128), Size(5, 5), 0, 0, vector<int64_t>(v[2], v[2]+5), vector<int64_t>(v[2], v[2]+5) },
{ CV_8UC1, Size( 256, 128), Size(7, 7), 0, 0, vector<int64_t>(v[3], v[3]+7), vector<int64_t>(v[3], v[3]+7) },
{ CV_8UC1, Size( 256, 128), Size(9, 9), 0, 0, vector<int64_t>(v[4], v[4]+9), vector<int64_t>(v[4], v[4]+9) },
#ifdef CV_TEST_INACCURATE_GAUSSIAN_BLUR
{ CV_8UC1, Size( 256, 128), Size(3, 3), 1.75, 0.875, vector<int64_t>(v[5], v[5]+3), vector<int64_t>(v[6], v[6]+3) },
{ CV_8UC2, Size( 256, 128), Size(3, 3), 1.75, 0.875, vector<int64_t>(v[5], v[5]+3), vector<int64_t>(v[6], v[6]+3) },
{ CV_8UC3, Size( 256, 128), Size(3, 3), 1.75, 0.875, vector<int64_t>(v[5], v[5]+3), vector<int64_t>(v[6], v[6]+3) },
{ CV_8UC4, Size( 256, 128), Size(3, 3), 1.75, 0.875, vector<int64_t>(v[5], v[5]+3), vector<int64_t>(v[6], v[6]+3) },
{ CV_8UC1, Size( 256, 128), Size(5, 5), 0.375, 0.75, vector<int64_t>(v[7], v[7]+5), vector<int64_t>(v[8], v[8]+5) }
#endif
};
int bordermodes[] = {
......@@ -162,8 +167,28 @@ TEST(GaussianBlur_Bitexact, regression_15015)
{
Mat src(100,100,CV_8UC3,Scalar(255,255,255));
Mat dst;
GaussianBlur(src, dst, Size(5, 5), 9);
GaussianBlur(src, dst, Size(5, 5), 0);
ASSERT_EQ(0.0, cvtest::norm(dst, src, NORM_INF));
}
static void checkGaussianBlur_8Uvs32F(const Mat& src8u, const Mat& src32f, int N, double sigma)
{
Mat dst8u; GaussianBlur(src8u, dst8u, Size(N, N), sigma); // through bit-exact path
Mat dst8u_32f; dst8u.convertTo(dst8u_32f, CV_32F);
Mat dst32f; GaussianBlur(src32f, dst32f, Size(N, N), sigma); // without bit-exact computations
double normINF_32f = cv::norm(dst8u_32f, dst32f, NORM_INF);
EXPECT_LE(normINF_32f, 1.0);
}
TEST(GaussianBlur_Bitexact, regression_9863)
{
Mat src8u = imread(cvtest::findDataFile("shared/lena.png"));
Mat src32f; src8u.convertTo(src32f, CV_32F);
checkGaussianBlur_8Uvs32F(src8u, src32f, 151, 30);
}
}} // namespace
......@@ -50,7 +50,7 @@ class facedetect_test(NewOpenCVTests):
img = self.get_sample( sample)
gray = cv.cvtColor(img, cv.COLOR_BGR2GRAY)
gray = cv.GaussianBlur(gray, (5, 5), 5.1)
gray = cv.GaussianBlur(gray, (5, 5), 0)
rects = detect(gray, cascade)
faces.append(rects)
......
......@@ -428,6 +428,9 @@ protected:
// updates progress bar
virtual int update_progress( int progress, int test_case_idx, int count, double dt );
// dump test case input parameters
virtual void dump_test_case(int test_case_idx, std::ostream* out);
// finds test parameter
const CvFileNode* find_param( CvFileStorage* fs, const char* param_name );
......
......@@ -350,7 +350,13 @@ void BaseTest::run( int start_from )
return;
if( validate_test_results( test_case_idx ) < 0 || ts->get_err_code() < 0 )
{
std::stringstream ss;
dump_test_case(test_case_idx, &ss);
std::string s = ss.str();
ts->printf( TS::LOG, "%s", s.c_str());
return;
}
}
}
......@@ -401,6 +407,12 @@ int BaseTest::update_progress( int progress, int test_case_idx, int count, doubl
}
void BaseTest::dump_test_case(int test_case_idx, std::ostream* out)
{
*out << "test_case_idx = " << test_case_idx << std::endl;
}
BadArgTest::BadArgTest()
{
test_case_idx = -1;
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment