Merge pull request #6878 from alalek:canny_custom_gradient

This commit is contained in:
Vadim Pisarevsky 2016-07-20 13:16:53 +00:00
commit a455858dd9
5 changed files with 209 additions and 68 deletions

View File

@ -1664,6 +1664,19 @@ CV_EXPORTS_W void Canny( InputArray image, OutputArray edges,
double threshold1, double threshold2, double threshold1, double threshold2,
int apertureSize = 3, bool L2gradient = false ); int apertureSize = 3, bool L2gradient = false );
/** \overload
Finds edges in an image using the Canny algorithm with custom image gradient.
@param dx 16-bit x derivative of input image (CV_16SC1 or CV_16SC3).
@param dy 16-bit y derivative of input image (same type as dx).
@param edges,threshold1,threshold2,L2gradient See cv::Canny
*/
CV_EXPORTS_W void Canny( InputArray dx, InputArray dy,
OutputArray edges,
double threshold1, double threshold2,
bool L2gradient = false );
/** @brief Calculates the minimal eigenvalue of gradient matrices for corner detection. /** @brief Calculates the minimal eigenvalue of gradient matrices for corner detection.
The function is similar to cornerEigenValsAndVecs but it calculates and stores only the minimal The function is similar to cornerEigenValsAndVecs but it calculates and stores only the minimal

View File

@ -43,6 +43,10 @@
#include "precomp.hpp" #include "precomp.hpp"
#include "opencl_kernels_imgproc.hpp" #include "opencl_kernels_imgproc.hpp"
#ifdef _MSC_VER
#pragma warning( disable: 4127 ) // conditional expression is constant
#endif
#if defined (HAVE_IPP) && (IPP_VERSION_X100 >= 700) #if defined (HAVE_IPP) && (IPP_VERSION_X100 >= 700)
#define USE_IPP_CANNY 1 #define USE_IPP_CANNY 1
@ -53,53 +57,73 @@
namespace cv namespace cv
{ {
static void CannyImpl(Mat& dx_, Mat& dy_, Mat& _dst, double low_thresh, double high_thresh, bool L2gradient);
#ifdef HAVE_IPP #ifdef HAVE_IPP
static bool ippCanny(const Mat& _src, Mat& _dst, float low, float high) template <bool useCustomDeriv>
static bool ippCanny(const Mat& _src, const Mat& dx_, const Mat& dy_, Mat& _dst, float low, float high)
{ {
#if USE_IPP_CANNY #if USE_IPP_CANNY
int size = 0, size1 = 0; int size = 0, size1 = 0;
IppiSize roi = { _src.cols, _src.rows }; IppiSize roi = { _src.cols, _src.rows };
if (ippiCannyGetSize(roi, &size) < 0)
return false;
if (!useCustomDeriv)
{
#if IPP_VERSION_X100 < 900 #if IPP_VERSION_X100 < 900
if (ippiFilterSobelNegVertGetBufferSize_8u16s_C1R(roi, ippMskSize3x3, &size) < 0) if (ippiFilterSobelNegVertGetBufferSize_8u16s_C1R(roi, ippMskSize3x3, &size1) < 0)
return false; return false;
if (ippiFilterSobelHorizGetBufferSize_8u16s_C1R(roi, ippMskSize3x3, &size1) < 0) size = std::max(size, size1);
return false; if (ippiFilterSobelHorizGetBufferSize_8u16s_C1R(roi, ippMskSize3x3, &size1) < 0)
return false;
#else #else
if (ippiFilterSobelNegVertBorderGetBufferSize(roi, ippMskSize3x3, ipp8u, ipp16s, 1, &size) < 0) if (ippiFilterSobelNegVertBorderGetBufferSize(roi, ippMskSize3x3, ipp8u, ipp16s, 1, &size1) < 0)
return false; return false;
if (ippiFilterSobelHorizBorderGetBufferSize(roi, ippMskSize3x3, ipp8u, ipp16s, 1, &size1) < 0) size = std::max(size, size1);
return false; if (ippiFilterSobelHorizBorderGetBufferSize(roi, ippMskSize3x3, ipp8u, ipp16s, 1, &size1) < 0)
return false;
#endif #endif
size = std::max(size, size1);
size = std::max(size, size1); }
if (ippiCannyGetSize(roi, &size1) < 0)
return false;
size = std::max(size, size1);
AutoBuffer<uchar> buf(size + 64); AutoBuffer<uchar> buf(size + 64);
uchar* buffer = alignPtr((uchar*)buf, 32); uchar* buffer = alignPtr((uchar*)buf, 32);
Mat _dx(_src.rows, _src.cols, CV_16S); Mat dx, dy;
if( ippiFilterSobelNegVertBorder_8u16s_C1R(_src.ptr(), (int)_src.step, if (!useCustomDeriv)
_dx.ptr<short>(), (int)_dx.step, roi, {
ippMskSize3x3, ippBorderRepl, 0, buffer) < 0 ) Mat _dx(_src.rows, _src.cols, CV_16S);
return false; if( ippiFilterSobelNegVertBorder_8u16s_C1R(_src.ptr(), (int)_src.step,
_dx.ptr<short>(), (int)_dx.step, roi,
ippMskSize3x3, ippBorderRepl, 0, buffer) < 0 )
return false;
Mat _dy(_src.rows, _src.cols, CV_16S); Mat _dy(_src.rows, _src.cols, CV_16S);
if( ippiFilterSobelHorizBorder_8u16s_C1R(_src.ptr(), (int)_src.step, if( ippiFilterSobelHorizBorder_8u16s_C1R(_src.ptr(), (int)_src.step,
_dy.ptr<short>(), (int)_dy.step, roi, _dy.ptr<short>(), (int)_dy.step, roi,
ippMskSize3x3, ippBorderRepl, 0, buffer) < 0 ) ippMskSize3x3, ippBorderRepl, 0, buffer) < 0 )
return false; return false;
if( ippiCanny_16s8u_C1R(_dx.ptr<short>(), (int)_dx.step, swap(dx, _dx);
_dy.ptr<short>(), (int)_dy.step, swap(dy, _dy);
}
else
{
dx = dx_;
dy = dy_;
}
if( ippiCanny_16s8u_C1R(dx.ptr<short>(), (int)dx.step,
dy.ptr<short>(), (int)dy.step,
_dst.ptr(), (int)_dst.step, roi, low, high, buffer) < 0 ) _dst.ptr(), (int)_dst.step, roi, low, high, buffer) < 0 )
return false; return false;
return true; return true;
#else #else
CV_UNUSED(_src); CV_UNUSED(_dst); CV_UNUSED(low); CV_UNUSED(high); CV_UNUSED(_src); CV_UNUSED(dx_); CV_UNUSED(dy_); CV_UNUSED(_dst); CV_UNUSED(low); CV_UNUSED(high);
return false; return false;
#endif #endif
} }
@ -107,7 +131,8 @@ static bool ippCanny(const Mat& _src, Mat& _dst, float low, float high)
#ifdef HAVE_OPENCL #ifdef HAVE_OPENCL
static bool ocl_Canny(InputArray _src, OutputArray _dst, float low_thresh, float high_thresh, template <bool useCustomDeriv>
static bool ocl_Canny(InputArray _src, const UMat& dx_, const UMat& dy_, OutputArray _dst, float low_thresh, float high_thresh,
int aperture_size, bool L2gradient, int cn, const Size & size) int aperture_size, bool L2gradient, int cn, const Size & size)
{ {
UMat map; UMat map;
@ -140,7 +165,8 @@ static bool ocl_Canny(InputArray _src, OutputArray _dst, float low_thresh, float
} }
int low = cvFloor(low_thresh), high = cvFloor(high_thresh); int low = cvFloor(low_thresh), high = cvFloor(high_thresh);
if (aperture_size == 3 && !_src.isSubmatrix()) if (!useCustomDeriv &&
aperture_size == 3 && !_src.isSubmatrix())
{ {
/* /*
stage1_with_sobel: stage1_with_sobel:
@ -181,8 +207,16 @@ static bool ocl_Canny(InputArray _src, OutputArray _dst, float low_thresh, float
Double thresholding Double thresholding
*/ */
UMat dx, dy; UMat dx, dy;
Sobel(_src, dx, CV_16S, 1, 0, aperture_size, 1, 0, BORDER_REPLICATE); if (!useCustomDeriv)
Sobel(_src, dy, CV_16S, 0, 1, aperture_size, 1, 0, BORDER_REPLICATE); {
Sobel(_src, dx, CV_16S, 1, 0, aperture_size, 1, 0, BORDER_REPLICATE);
Sobel(_src, dy, CV_16S, 0, 1, aperture_size, 1, 0, BORDER_REPLICATE);
}
else
{
dx = dx_;
dy = dy_;
}
ocl::Kernel without_sobel("stage1_without_sobel", ocl::imgproc::canny_oclsrc, ocl::Kernel without_sobel("stage1_without_sobel", ocl::imgproc::canny_oclsrc,
format("-D WITHOUT_SOBEL -D cn=%d -D GRP_SIZEX=%d -D GRP_SIZEY=%d%s", format("-D WITHOUT_SOBEL -D cn=%d -D GRP_SIZEX=%d -D GRP_SIZEY=%d%s",
@ -585,9 +619,7 @@ private:
#endif #endif
} // namespace cv void Canny( InputArray _src, OutputArray _dst,
void cv::Canny( InputArray _src, OutputArray _dst,
double low_thresh, double high_thresh, double low_thresh, double high_thresh,
int aperture_size, bool L2gradient ) int aperture_size, bool L2gradient )
{ {
@ -611,7 +643,7 @@ void cv::Canny( InputArray _src, OutputArray _dst,
std::swap(low_thresh, high_thresh); std::swap(low_thresh, high_thresh);
CV_OCL_RUN(_dst.isUMat() && (cn == 1 || cn == 3), CV_OCL_RUN(_dst.isUMat() && (cn == 1 || cn == 3),
ocl_Canny(_src, _dst, (float)low_thresh, (float)high_thresh, aperture_size, L2gradient, cn, size)) ocl_Canny<false>(_src, UMat(), UMat(), _dst, (float)low_thresh, (float)high_thresh, aperture_size, L2gradient, cn, size))
Mat src = _src.getMat(), dst = _dst.getMat(); Mat src = _src.getMat(), dst = _dst.getMat();
@ -620,7 +652,7 @@ void cv::Canny( InputArray _src, OutputArray _dst,
return; return;
#endif #endif
CV_IPP_RUN(USE_IPP_CANNY && (aperture_size == 3 && !L2gradient && 1 == cn), ippCanny(src, dst, (float)low_thresh, (float)high_thresh)) CV_IPP_RUN(USE_IPP_CANNY && (aperture_size == 3 && !L2gradient && 1 == cn), ippCanny<false>(src, Mat(), Mat(), dst, (float)low_thresh, (float)high_thresh))
#ifdef HAVE_TBB #ifdef HAVE_TBB
@ -683,14 +715,66 @@ while (borderPeaks.try_pop(m))
if (!m[mapstep+1]) CANNY_PUSH_SERIAL(m + mapstep + 1); if (!m[mapstep+1]) CANNY_PUSH_SERIAL(m + mapstep + 1);
} }
#else // the final pass, form the final image
const uchar* pmap = map + mapstep + 1;
uchar* pdst = dst.ptr();
for (int i = 0; i < src.rows; i++, pmap += mapstep, pdst += dst.step)
{
for (int j = 0; j < src.cols; j++)
pdst[j] = (uchar)-(pmap[j] >> 1);
}
#else
Mat dx(src.rows, src.cols, CV_16SC(cn)); Mat dx(src.rows, src.cols, CV_16SC(cn));
Mat dy(src.rows, src.cols, CV_16SC(cn)); Mat dy(src.rows, src.cols, CV_16SC(cn));
Sobel(src, dx, CV_16S, 1, 0, aperture_size, 1, 0, BORDER_REPLICATE); Sobel(src, dx, CV_16S, 1, 0, aperture_size, 1, 0, BORDER_REPLICATE);
Sobel(src, dy, CV_16S, 0, 1, aperture_size, 1, 0, BORDER_REPLICATE); Sobel(src, dy, CV_16S, 0, 1, aperture_size, 1, 0, BORDER_REPLICATE);
CannyImpl(dx, dy, dst, low_thresh, high_thresh, L2gradient);
#endif
}
void Canny( InputArray _dx, InputArray _dy, OutputArray _dst,
double low_thresh, double high_thresh,
bool L2gradient )
{
CV_Assert(_dx.dims() == 2);
CV_Assert(_dx.type() == CV_16SC1 || _dx.type() == CV_16SC3);
CV_Assert(_dy.type() == _dx.type());
CV_Assert(_dx.sameSize(_dy));
if (low_thresh > high_thresh)
std::swap(low_thresh, high_thresh);
const int cn = _dx.channels();
const Size size = _dx.size();
CV_OCL_RUN(_dst.isUMat(),
ocl_Canny<true>(UMat(), _dx.getUMat(), _dy.getUMat(), _dst, (float)low_thresh, (float)high_thresh, 0, L2gradient, cn, size))
_dst.create(size, CV_8U);
Mat dst = _dst.getMat();
Mat dx = _dx.getMat();
Mat dy = _dy.getMat();
CV_IPP_RUN(USE_IPP_CANNY && (!L2gradient && 1 == cn), ippCanny<true>(Mat(), dx, dy, dst, (float)low_thresh, (float)high_thresh))
if (cn > 1)
{
dx = dx.clone();
dy = dy.clone();
}
CannyImpl(dx, dy, dst, low_thresh, high_thresh, L2gradient);
}
static void CannyImpl(Mat& dx, Mat& dy, Mat& dst,
double low_thresh, double high_thresh, bool L2gradient)
{
const int cn = dx.channels();
const int cols = dx.cols, rows = dx.rows;
if (L2gradient) if (L2gradient)
{ {
low_thresh = std::min(32767.0, low_thresh); low_thresh = std::min(32767.0, low_thresh);
@ -702,8 +786,8 @@ while (borderPeaks.try_pop(m))
int low = cvFloor(low_thresh); int low = cvFloor(low_thresh);
int high = cvFloor(high_thresh); int high = cvFloor(high_thresh);
ptrdiff_t mapstep = src.cols + 2; ptrdiff_t mapstep = cols + 2;
AutoBuffer<uchar> buffer((src.cols+2)*(src.rows+2) + cn * mapstep * 3 * sizeof(int)); AutoBuffer<uchar> buffer((cols+2)*(rows+2) + cn * mapstep * 3 * sizeof(int));
int* mag_buf[3]; int* mag_buf[3];
mag_buf[0] = (int*)(uchar*)buffer; mag_buf[0] = (int*)(uchar*)buffer;
@ -713,9 +797,9 @@ while (borderPeaks.try_pop(m))
uchar* map = (uchar*)(mag_buf[2] + mapstep*cn); uchar* map = (uchar*)(mag_buf[2] + mapstep*cn);
memset(map, 1, mapstep); memset(map, 1, mapstep);
memset(map + mapstep*(src.rows + 1), 1, mapstep); memset(map + mapstep*(rows + 1), 1, mapstep);
int maxsize = std::max(1 << 10, src.cols * src.rows / 10); int maxsize = std::max(1 << 10, cols * rows / 10);
std::vector<uchar*> stack(maxsize); std::vector<uchar*> stack(maxsize);
uchar **stack_top = &stack[0]; uchar **stack_top = &stack[0];
uchar **stack_bottom = &stack[0]; uchar **stack_bottom = &stack[0];
@ -744,17 +828,17 @@ while (borderPeaks.try_pop(m))
// 0 - the pixel might belong to an edge // 0 - the pixel might belong to an edge
// 1 - the pixel can not belong to an edge // 1 - the pixel can not belong to an edge
// 2 - the pixel does belong to an edge // 2 - the pixel does belong to an edge
for (int i = 0; i <= src.rows; i++) for (int i = 0; i <= rows; i++)
{ {
int* _norm = mag_buf[(i > 0) + 1] + 1; int* _norm = mag_buf[(i > 0) + 1] + 1;
if (i < src.rows) if (i < rows)
{ {
short* _dx = dx.ptr<short>(i); short* _dx = dx.ptr<short>(i);
short* _dy = dy.ptr<short>(i); short* _dy = dy.ptr<short>(i);
if (!L2gradient) if (!L2gradient)
{ {
int j = 0, width = src.cols * cn; int j = 0, width = cols * cn;
#if CV_SSE2 #if CV_SSE2
if (haveSSE2) if (haveSSE2)
{ {
@ -788,7 +872,7 @@ while (borderPeaks.try_pop(m))
} }
else else
{ {
int j = 0, width = src.cols * cn; int j = 0, width = cols * cn;
#if CV_SSE2 #if CV_SSE2
if (haveSSE2) if (haveSSE2)
{ {
@ -824,7 +908,7 @@ while (borderPeaks.try_pop(m))
if (cn > 1) if (cn > 1)
{ {
for(int j = 0, jn = 0; j < src.cols; ++j, jn += cn) for(int j = 0, jn = 0; j < cols; ++j, jn += cn)
{ {
int maxIdx = jn; int maxIdx = jn;
for(int k = 1; k < cn; ++k) for(int k = 1; k < cn; ++k)
@ -834,7 +918,7 @@ while (borderPeaks.try_pop(m))
_dy[j] = _dy[maxIdx]; _dy[j] = _dy[maxIdx];
} }
} }
_norm[-1] = _norm[src.cols] = 0; _norm[-1] = _norm[cols] = 0;
} }
else else
memset(_norm-1, 0, /* cn* */mapstep*sizeof(int)); memset(_norm-1, 0, /* cn* */mapstep*sizeof(int));
@ -845,7 +929,7 @@ while (borderPeaks.try_pop(m))
continue; continue;
uchar* _map = map + mapstep*i + 1; uchar* _map = map + mapstep*i + 1;
_map[-1] = _map[src.cols] = 1; _map[-1] = _map[cols] = 1;
int* _mag = mag_buf[1] + 1; // take the central row int* _mag = mag_buf[1] + 1; // take the central row
ptrdiff_t magstep1 = mag_buf[2] - mag_buf[1]; ptrdiff_t magstep1 = mag_buf[2] - mag_buf[1];
@ -854,17 +938,17 @@ while (borderPeaks.try_pop(m))
const short* _x = dx.ptr<short>(i-1); const short* _x = dx.ptr<short>(i-1);
const short* _y = dy.ptr<short>(i-1); const short* _y = dy.ptr<short>(i-1);
if ((stack_top - stack_bottom) + src.cols > maxsize) if ((stack_top - stack_bottom) + cols > maxsize)
{ {
int sz = (int)(stack_top - stack_bottom); int sz = (int)(stack_top - stack_bottom);
maxsize = std::max(maxsize * 3/2, sz + src.cols); maxsize = std::max(maxsize * 3/2, sz + cols);
stack.resize(maxsize); stack.resize(maxsize);
stack_bottom = &stack[0]; stack_bottom = &stack[0];
stack_top = stack_bottom + sz; stack_top = stack_bottom + sz;
} }
int prev_flag = 0; int prev_flag = 0;
for (int j = 0; j < src.cols; j++) for (int j = 0; j < cols; j++)
{ {
#define CANNY_SHIFT 15 #define CANNY_SHIFT 15
const int TG22 = (int)(0.4142135623730950488016887242097*(1<<CANNY_SHIFT) + 0.5); const int TG22 = (int)(0.4142135623730950488016887242097*(1<<CANNY_SHIFT) + 0.5);
@ -943,18 +1027,18 @@ __ocv_canny_push:
if (!m[mapstep+1]) CANNY_PUSH(m + mapstep + 1); if (!m[mapstep+1]) CANNY_PUSH(m + mapstep + 1);
} }
#endif
// the final pass, form the final image // the final pass, form the final image
const uchar* pmap = map + mapstep + 1; const uchar* pmap = map + mapstep + 1;
uchar* pdst = dst.ptr(); uchar* pdst = dst.ptr();
for (int i = 0; i < src.rows; i++, pmap += mapstep, pdst += dst.step) for (int i = 0; i < rows; i++, pmap += mapstep, pdst += dst.step)
{ {
for (int j = 0; j < src.cols; j++) for (int j = 0; j < cols; j++)
pdst[j] = (uchar)-(pmap[j] >> 1); pdst[j] = (uchar)-(pmap[j] >> 1);
} }
} }
} // namespace cv
void cvCanny( const CvArr* image, CvArr* edges, double threshold1, void cvCanny( const CvArr* image, CvArr* edges, double threshold1,
double threshold2, int aperture_size ) double threshold2, int aperture_size )
{ {

View File

@ -54,13 +54,13 @@ namespace ocl {
//////////////////////////////////////////////////////// ////////////////////////////////////////////////////////
// Canny // Canny
IMPLEMENT_PARAM_CLASS(AppertureSize, int) IMPLEMENT_PARAM_CLASS(ApertureSize, int)
IMPLEMENT_PARAM_CLASS(L2gradient, bool) IMPLEMENT_PARAM_CLASS(L2gradient, bool)
IMPLEMENT_PARAM_CLASS(UseRoi, bool) IMPLEMENT_PARAM_CLASS(UseRoi, bool)
PARAM_TEST_CASE(Canny, Channels, AppertureSize, L2gradient, UseRoi) PARAM_TEST_CASE(Canny, Channels, ApertureSize, L2gradient, UseRoi)
{ {
int cn, apperture_size; int cn, aperture_size;
bool useL2gradient, use_roi; bool useL2gradient, use_roi;
TEST_DECLARE_INPUT_PARAMETER(src); TEST_DECLARE_INPUT_PARAMETER(src);
@ -69,7 +69,7 @@ PARAM_TEST_CASE(Canny, Channels, AppertureSize, L2gradient, UseRoi)
virtual void SetUp() virtual void SetUp()
{ {
cn = GET_PARAM(0); cn = GET_PARAM(0);
apperture_size = GET_PARAM(1); aperture_size = GET_PARAM(1);
useL2gradient = GET_PARAM(2); useL2gradient = GET_PARAM(2);
use_roi = GET_PARAM(3); use_roi = GET_PARAM(3);
} }
@ -105,8 +105,31 @@ OCL_TEST_P(Canny, Accuracy)
eps = 12e-3; eps = 12e-3;
#endif #endif
OCL_OFF(cv::Canny(src_roi, dst_roi, low_thresh, high_thresh, apperture_size, useL2gradient)); OCL_OFF(cv::Canny(src_roi, dst_roi, low_thresh, high_thresh, aperture_size, useL2gradient));
OCL_ON(cv::Canny(usrc_roi, udst_roi, low_thresh, high_thresh, apperture_size, useL2gradient)); OCL_ON(cv::Canny(usrc_roi, udst_roi, low_thresh, high_thresh, aperture_size, useL2gradient));
EXPECT_MAT_SIMILAR(dst_roi, udst_roi, eps);
EXPECT_MAT_SIMILAR(dst, udst, eps);
}
OCL_TEST_P(Canny, AccuracyCustomGradient)
{
generateTestData();
const double low_thresh = 50.0, high_thresh = 100.0;
double eps = 1e-2;
#ifdef ANDROID
if (cv::ocl::Device::getDefault().isNVidia())
eps = 12e-3;
#endif
OCL_OFF(cv::Canny(src_roi, dst_roi, low_thresh, high_thresh, aperture_size, useL2gradient));
OCL_ON(
UMat dx, dy;
Sobel(usrc_roi, dx, CV_16S, 1, 0, aperture_size, 1, 0, BORDER_REPLICATE);
Sobel(usrc_roi, dy, CV_16S, 0, 1, aperture_size, 1, 0, BORDER_REPLICATE);
cv::Canny(dx, dy, udst_roi, low_thresh, high_thresh, useL2gradient);
);
EXPECT_MAT_SIMILAR(dst_roi, udst_roi, eps); EXPECT_MAT_SIMILAR(dst_roi, udst_roi, eps);
EXPECT_MAT_SIMILAR(dst, udst, eps); EXPECT_MAT_SIMILAR(dst, udst, eps);
@ -114,7 +137,7 @@ OCL_TEST_P(Canny, Accuracy)
OCL_INSTANTIATE_TEST_CASE_P(ImgProc, Canny, testing::Combine( OCL_INSTANTIATE_TEST_CASE_P(ImgProc, Canny, testing::Combine(
testing::Values(1, 3), testing::Values(1, 3),
testing::Values(AppertureSize(3), AppertureSize(5)), testing::Values(ApertureSize(3), ApertureSize(5)),
testing::Values(L2gradient(false), L2gradient(true)), testing::Values(L2gradient(false), L2gradient(true)),
testing::Values(UseRoi(false), UseRoi(true)))); testing::Values(UseRoi(false), UseRoi(true))));

View File

@ -47,7 +47,7 @@ using namespace std;
class CV_CannyTest : public cvtest::ArrayTest class CV_CannyTest : public cvtest::ArrayTest
{ {
public: public:
CV_CannyTest(); CV_CannyTest(bool custom_deriv = false);
protected: protected:
void get_test_array_types_and_sizes( int test_case_idx, vector<vector<Size> >& sizes, vector<vector<int> >& types ); void get_test_array_types_and_sizes( int test_case_idx, vector<vector<Size> >& sizes, vector<vector<int> >& types );
@ -61,10 +61,11 @@ protected:
bool use_true_gradient; bool use_true_gradient;
double threshold1, threshold2; double threshold1, threshold2;
bool test_cpp; bool test_cpp;
bool test_custom_deriv;
}; };
CV_CannyTest::CV_CannyTest() CV_CannyTest::CV_CannyTest(bool custom_deriv)
{ {
test_array[INPUT].push_back(NULL); test_array[INPUT].push_back(NULL);
test_array[OUTPUT].push_back(NULL); test_array[OUTPUT].push_back(NULL);
@ -75,6 +76,7 @@ CV_CannyTest::CV_CannyTest()
threshold1 = threshold2 = 0; threshold1 = threshold2 = 0;
test_cpp = false; test_cpp = false;
test_custom_deriv = custom_deriv;
} }
@ -99,6 +101,9 @@ void CV_CannyTest::get_test_array_types_and_sizes( int test_case_idx,
use_true_gradient = cvtest::randInt(rng) % 2 != 0; use_true_gradient = cvtest::randInt(rng) % 2 != 0;
test_cpp = (cvtest::randInt(rng) & 256) == 0; test_cpp = (cvtest::randInt(rng) & 256) == 0;
ts->printf(cvtest::TS::LOG, "Canny(size = %d x %d, aperture_size = %d, threshold1 = %g, threshold2 = %g, L2 = %s) test_cpp = %s (test case #%d)\n",
sizes[0][0].width, sizes[0][0].height, aperture_size, threshold1, threshold2, use_true_gradient ? "TRUE" : "FALSE", test_cpp ? "TRUE" : "FALSE", test_case_idx);
} }
@ -123,9 +128,24 @@ double CV_CannyTest::get_success_error_level( int /*test_case_idx*/, int /*i*/,
void CV_CannyTest::run_func() void CV_CannyTest::run_func()
{ {
if(!test_cpp) if (test_custom_deriv)
{
cv::Mat _out = cv::cvarrToMat(test_array[OUTPUT][0]);
cv::Mat src = cv::cvarrToMat(test_array[INPUT][0]);
cv::Mat dx, dy;
int m = aperture_size;
Point anchor(m/2, m/2);
Mat dxkernel = cvtest::calcSobelKernel2D( 1, 0, m, 0 );
Mat dykernel = cvtest::calcSobelKernel2D( 0, 1, m, 0 );
cvtest::filter2D(src, dx, CV_16S, dxkernel, anchor, 0, BORDER_REPLICATE);
cvtest::filter2D(src, dy, CV_16S, dykernel, anchor, 0, BORDER_REPLICATE);
cv::Canny(dx, dy, _out, threshold1, threshold2, use_true_gradient);
}
else if(!test_cpp)
{
cvCanny( test_array[INPUT][0], test_array[OUTPUT][0], threshold1, threshold2, cvCanny( test_array[INPUT][0], test_array[OUTPUT][0], threshold1, threshold2,
aperture_size + (use_true_gradient ? CV_CANNY_L2_GRADIENT : 0)); aperture_size + (use_true_gradient ? CV_CANNY_L2_GRADIENT : 0));
}
else else
{ {
cv::Mat _out = cv::cvarrToMat(test_array[OUTPUT][0]); cv::Mat _out = cv::cvarrToMat(test_array[OUTPUT][0]);
@ -283,5 +303,6 @@ int CV_CannyTest::validate_test_results( int test_case_idx )
} }
TEST(Imgproc_Canny, accuracy) { CV_CannyTest test; test.safe_run(); } TEST(Imgproc_Canny, accuracy) { CV_CannyTest test; test.safe_run(); }
TEST(Imgproc_Canny, accuracy_deriv) { CV_CannyTest test(true); test.safe_run(); }
/* End of file. */ /* End of file. */

View File

@ -349,8 +349,8 @@ IMPLEMENT_PARAM_CLASS(Channels, int)
#define OCL_TEST_F(name, ...) typedef name OCL_##name; TEST_F(OCL_##name, __VA_ARGS__) #define OCL_TEST_F(name, ...) typedef name OCL_##name; TEST_F(OCL_##name, __VA_ARGS__)
#define OCL_TEST(name, ...) TEST(OCL_##name, __VA_ARGS__) #define OCL_TEST(name, ...) TEST(OCL_##name, __VA_ARGS__)
#define OCL_OFF(fn) cv::ocl::setUseOpenCL(false); fn #define OCL_OFF(...) cv::ocl::setUseOpenCL(false); __VA_ARGS__ ;
#define OCL_ON(fn) cv::ocl::setUseOpenCL(true); fn #define OCL_ON(...) cv::ocl::setUseOpenCL(true); __VA_ARGS__ ;
#define OCL_ALL_DEPTHS Values(CV_8U, CV_8S, CV_16U, CV_16S, CV_32S, CV_32F, CV_64F) #define OCL_ALL_DEPTHS Values(CV_8U, CV_8S, CV_16U, CV_16S, CV_32S, CV_32F, CV_64F)
#define OCL_ALL_CHANNELS Values(1, 2, 3, 4) #define OCL_ALL_CHANNELS Values(1, 2, 3, 4)