Commit 34c07f6a authored by Alexander Alekhin's avatar Alexander Alekhin

Merge pull request #7729 from apavlenko:vx-canny

parents 2fded5d8 3aedc134
This diff is collapsed.
......@@ -3215,5 +3215,6 @@ template<> struct ParamType<uchar>
#include "opencv2/core/cvstd.inl.hpp"
#include "opencv2/core/utility.hpp"
#include "opencv2/core/optim.hpp"
#include "opencv2/core/ovx.hpp"
#endif /*OPENCV_CORE_HPP*/
// 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.
// Copyright (C) 2016, Intel Corporation, all rights reserved.
// Third party copyrights are property of their respective owners.
// OpenVX related definitions and declarations
#pragma once
#ifndef OPENCV_OVX_DEFS_HPP
#define OPENCV_OVX_DEFS_HPP
#include "cvconfig.h"
// utility macro for running OpenVX-based implementations
#ifdef HAVE_OPENVX
#define IVX_HIDE_INFO_WARNINGS
#define IVX_USE_OPENCV
#include "ivx.hpp"
#define CV_OVX_RUN(condition, func, ...) \
if (cv::useOpenVX() && (condition) && func) \
{ \
return __VA_ARGS__; \
}
#else
#define CV_OVX_RUN(condition, func, ...)
#endif // HAVE_OPENVX
#endif // OPENCV_OVX_DEFS_HPP
// 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.
// Copyright (C) 2016, Intel Corporation, all rights reserved.
// Third party copyrights are property of their respective owners.
// OpenVX related definitions and declarations
#pragma once
#ifndef OPENCV_OVX_HPP
#define OPENCV_OVX_HPP
#include "cvdef.h"
namespace cv
{
/// Check if use of OpenVX is possible
CV_EXPORTS_W bool haveOpenVX();
/// Check if use of OpenVX is enabled
CV_EXPORTS_W bool useOpenVX();
/// Enable/disable use of OpenVX
CV_EXPORTS_W void setUseOpenVX(bool flag);
} // namespace cv
#endif // OPENCV_OVX_HPP
......@@ -46,11 +46,7 @@
#include "opencl_kernels_core.hpp"
#include "opencv2/core/hal/intrin.hpp"
#ifdef HAVE_OPENVX
#define IVX_USE_OPENCV
#define IVX_HIDE_INFO_WARNINGS
#include "ivx.hpp"
#endif
#include "opencv2/core/openvx/ovx_defs.hpp"
#ifdef __APPLE__
#undef CV_NEON
......@@ -4735,12 +4731,10 @@ template<typename T, typename DT> static void
cvt_( const T* src, size_t sstep,
DT* dst, size_t dstep, Size size )
{
#ifdef HAVE_OPENVX
if(openvx_cvt(src, sstep, dst, dstep, size))
{
return;
}
#endif
CV_OVX_RUN(
false,
openvx_cvt(src, sstep, dst, dstep, size)
);
sstep /= sizeof(src[0]);
dstep /= sizeof(dst[0]);
......
// 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.
// Copyright (C) 2016, Intel Corporation, all rights reserved.
// Third party copyrights are property of their respective owners.
// OpenVX related functions
#include "precomp.hpp"
#include "opencv2/core/ovx.hpp"
#include "opencv2/core/openvx/ovx_defs.hpp"
namespace cv
{
bool haveOpenVX()
{
#ifdef HAVE_OPENVX
static int g_haveOpenVX = -1;
if(g_haveOpenVX < 0)
{
try
{
ivx::Context context = ivx::Context::create();
vx_uint16 vComp = ivx::compiledWithVersion();
vx_uint16 vCurr = context.version();
g_haveOpenVX =
VX_VERSION_MAJOR(vComp) == VX_VERSION_MAJOR(vCurr) &&
VX_VERSION_MINOR(vComp) == VX_VERSION_MINOR(vCurr)
? 1 : 0;
}
catch(const ivx::WrapperError&)
{ g_haveOpenVX = 0; }
catch(const ivx::RuntimeError&)
{ g_haveOpenVX = 0; }
}
return g_haveOpenVX == 1;
#else
return false;
#endif
}
bool useOpenVX()
{
#ifdef HAVE_OPENVX
CoreTLSData* data = getCoreTlsData().get();
if( data->useOpenVX < 0 )
{
// enabled (if available) by default
data->useOpenVX = haveOpenVX() ? 1 : 0;
}
return data->useOpenVX > 0;
#else
return false;
#endif
}
void setUseOpenVX(bool flag)
{
#ifdef HAVE_OPENVX
if( haveOpenVX() )
{
CoreTLSData* data = getCoreTlsData().get();
data->useOpenVX = flag ? 1 : 0;
}
#else
CV_Assert(!flag && "OpenVX support isn't enabled at compile time");
#endif
}
} // namespace cv
......@@ -262,11 +262,13 @@ struct CoreTLSData
device(0), useOpenCL(-1),
//#endif
useIPP(-1)
{
#ifdef HAVE_TEGRA_OPTIMIZATION
useTegra = -1;
,useTegra(-1)
#endif
}
#ifdef HAVE_OPENVX
,useOpenVX(-1)
#endif
{}
RNG rng;
//#ifdef HAVE_OPENCL
......@@ -278,6 +280,9 @@ struct CoreTLSData
#ifdef HAVE_TEGRA_OPTIMIZATION
int useTegra; // 1 - use, 0 - do not use, -1 - auto/not initialized
#endif
#ifdef HAVE_OPENVX
int useOpenVX; // 1 - use, 0 - do not use, -1 - auto/not initialized
#endif
};
TLSData<CoreTLSData>& getCoreTlsData();
......
......@@ -45,6 +45,8 @@
#include "opencv2/core/hal/intrin.hpp"
#include <queue>
#include "opencv2/core/openvx/ovx_defs.hpp"
#ifdef _MSC_VER
#pragma warning( disable: 4127 ) // conditional expression is constant
#endif
......@@ -775,6 +777,64 @@ private:
ptrdiff_t mapstep;
};
#ifdef HAVE_OPENVX
static bool openvx_canny(const Mat& src, Mat& dst, int loVal, int hiVal, int kSize, bool useL2)
{
using namespace ivx;
Context context = Context::create();
try
{
Image _src = Image::createFromHandle(
context,
Image::matTypeToFormat(src.type()),
Image::createAddressing(src),
src.data );
Image _dst = Image::createFromHandle(
context,
Image::matTypeToFormat(dst.type()),
Image::createAddressing(dst),
dst.data );
Threshold threshold = Threshold::createRange(context, VX_TYPE_UINT8, saturate_cast<uchar>(loVal), saturate_cast<uchar>(hiVal));
#if 0
// the code below is disabled because vxuCannyEdgeDetector()
// ignores context attribute VX_CONTEXT_IMMEDIATE_BORDER
// FIXME: may fail in multithread case
border_t prevBorder = context.immediateBorder();
context.setImmediateBorder(VX_BORDER_REPLICATE);
IVX_CHECK_STATUS( vxuCannyEdgeDetector(context, _src, threshold, kSize, (useL2 ? VX_NORM_L2 : VX_NORM_L1), _dst) );
context.setImmediateBorder(prevBorder);
#else
// alternative code without vxuCannyEdgeDetector()
Graph graph = Graph::create(context);
ivx::Node node = ivx::Node(vxCannyEdgeDetectorNode(graph, _src, threshold, kSize, (useL2 ? VX_NORM_L2 : VX_NORM_L1), _dst) );
node.setBorder(VX_BORDER_REPLICATE);
graph.verify();
graph.process();
#endif
#ifdef VX_VERSION_1_1
_src.swapHandle();
_dst.swapHandle();
#endif
}
catch(const WrapperError& e)
{
//CV_DbgAssert(!"openvx_canny - WrapperError");
return false;
}
catch(const RuntimeError& e)
{
//CV_DbgAssert(!"openvx_canny - RuntimeError");
return false;
}
return true;
}
#endif // HAVE_OPENVX
void Canny( InputArray _src, OutputArray _dst,
double low_thresh, double high_thresh,
int aperture_size, bool L2gradient )
......@@ -805,6 +865,20 @@ void Canny( InputArray _src, OutputArray _dst,
Mat src = _src.getMat(), dst = _dst.getMat();
CV_OVX_RUN(
false && /* disabling due to accuracy issues */
src.type() == CV_8UC1 &&
!src.isSubmatrix() &&
src.cols >= aperture_size &&
src.rows >= aperture_size,
openvx_canny(
src,
dst,
cvFloor(low_thresh),
cvFloor(high_thresh),
aperture_size,
L2gradient ) );
#ifdef HAVE_TEGRA_OPTIMIZATION
if (tegra::useTegra() && tegra::canny(src, dst, low_thresh, high_thresh, aperture_size, L2gradient))
return;
......
......@@ -263,8 +263,8 @@ namespace cv
//ATTENTION: VX_CONTEXT_IMMEDIATE_BORDER attribute change could lead to strange issues in multi-threaded environments
//since OpenVX standart says nothing about thread-safety for now
vx_border_t prevBorder = ctx.borderMode();
ctx.setBorderMode(border);
vx_border_t prevBorder = ctx.immediateBorder();
ctx.setImmediateBorder(border);
if (dtype == CV_16SC1 && ksize == 3 && ((dx | dy) == 1) && (dx + dy) == 1)
{
if(dx)
......@@ -277,7 +277,7 @@ namespace cv
#if VX_VERSION <= VX_VERSION_1_0
if (ctx.vendorID() == VX_ID_KHRONOS && ((vx_size)(src.cols) <= ctx.convolutionMaxDimension() || (vx_size)(src.rows) <= ctx.convolutionMaxDimension()))
{
ctx.setBorderMode(prevBorder);
ctx.setImmediateBorder(prevBorder);
return false;
}
#endif
......@@ -292,7 +292,7 @@ namespace cv
cnv.setScale(cscale);
ivx::IVX_CHECK_STATUS(vxuConvolve(ctx, ia, cnv, ib));
}
ctx.setBorderMode(prevBorder);
ctx.setImmediateBorder(prevBorder);
return true;
}
catch (ivx::RuntimeError & e)
......
......@@ -1703,8 +1703,8 @@ namespace cv
//ATTENTION: VX_CONTEXT_IMMEDIATE_BORDER attribute change could lead to strange issues in multi-threaded environments
//since OpenVX standart says nothing about thread-safety for now
vx_border_t prevBorder = ctx.borderMode();
ctx.setBorderMode(border);
ivx::border_t prevBorder = ctx.immediateBorder();
ctx.setImmediateBorder(border);
if (ddepth == CV_8U && ksize.width == 3 && ksize.height == 3 && normalize)
{
ivx::IVX_CHECK_STATUS(vxuBox3x3(ctx, ia, ib));
......@@ -1714,7 +1714,7 @@ namespace cv
#if VX_VERSION <= VX_VERSION_1_0
if (ctx.vendorID() == VX_ID_KHRONOS && ((vx_size)(src.cols) <= ctx.convolutionMaxDimension() || (vx_size)(src.rows) <= ctx.convolutionMaxDimension()))
{
ctx.setBorderMode(prevBorder);
ctx.setImmediateBorder(prevBorder);
return false;
}
#endif
......@@ -1726,7 +1726,7 @@ namespace cv
cnv.setScale(1 << 15);
ivx::IVX_CHECK_STATUS(vxuConvolve(ctx, ia, cnv, ib));
}
ctx.setBorderMode(prevBorder);
ctx.setImmediateBorder(prevBorder);
}
catch (ivx::RuntimeError & e)
{
......@@ -2274,8 +2274,8 @@ static bool openvx_gaussianBlur(InputArray _src, OutputArray _dst, Size ksize,
//ATTENTION: VX_CONTEXT_IMMEDIATE_BORDER attribute change could lead to strange issues in multi-threaded environments
//since OpenVX standart says nothing about thread-safety for now
vx_border_t prevBorder = ctx.borderMode();
ctx.setBorderMode(border);
ivx::border_t prevBorder = ctx.immediateBorder();
ctx.setImmediateBorder(border);
if (ksize.width == 3 && ksize.height == 3 && (sigma1 == 0.0 || (sigma1 - 0.8) < DBL_EPSILON) && (sigma2 == 0.0 || (sigma2 - 0.8) < DBL_EPSILON))
{
ivx::IVX_CHECK_STATUS(vxuGaussian3x3(ctx, ia, ib));
......@@ -2285,7 +2285,7 @@ static bool openvx_gaussianBlur(InputArray _src, OutputArray _dst, Size ksize,
#if VX_VERSION <= VX_VERSION_1_0
if (ctx.vendorID() == VX_ID_KHRONOS && ((vx_size)(a.cols) <= ctx.convolutionMaxDimension() || (vx_size)(a.rows) <= ctx.convolutionMaxDimension()))
{
ctx.setBorderMode(prevBorder);
ctx.setImmediateBorder(prevBorder);
return false;
}
#endif
......@@ -2296,7 +2296,7 @@ static bool openvx_gaussianBlur(InputArray _src, OutputArray _dst, Size ksize,
cnv.setScale(1 << 15);
ivx::IVX_CHECK_STATUS(vxuConvolve(ctx, ia, cnv, ib));
}
ctx.setBorderMode(prevBorder);
ctx.setImmediateBorder(prevBorder);
}
catch (ivx::RuntimeError & e)
{
......@@ -3405,8 +3405,8 @@ namespace cv
//ATTENTION: VX_CONTEXT_IMMEDIATE_BORDER attribute change could lead to strange issues in multi-threaded environments
//since OpenVX standart says nothing about thread-safety for now
vx_border_t prevBorder = ctx.borderMode();
ctx.setBorderMode(border);
ivx::border_t prevBorder = ctx.immediateBorder();
ctx.setImmediateBorder(border);
#ifdef VX_VERSION_1_1
if (ksize == 3)
#endif
......@@ -3425,7 +3425,7 @@ namespace cv
ivx::IVX_CHECK_STATUS(vxQueryContext(ctx, VX_CONTEXT_NONLINEAR_MAX_DIMENSION, &supportedSize, sizeof(supportedSize)));
if ((vx_size)ksize > supportedSize)
{
ctx.setBorderMode(prevBorder);
ctx.setImmediateBorder(prevBorder);
return false;
}
Mat mask(ksize, ksize, CV_8UC1, Scalar(255));
......@@ -3435,7 +3435,7 @@ namespace cv
ivx::IVX_CHECK_STATUS(vxuNonLinearFilter(ctx, VX_NONLINEAR_FILTER_MEDIAN, ia, mtx, ib));
}
#endif
ctx.setBorderMode(prevBorder);
ctx.setImmediateBorder(prevBorder);
}
catch (ivx::RuntimeError & e)
{
......
......@@ -133,6 +133,8 @@ OCL_INSTANTIATE_TEST_CASE_P(ImgProc, Canny, testing::Combine(
testing::Values(L2gradient(false), L2gradient(true)),
testing::Values(UseRoi(false), UseRoi(true))));
} } // namespace cvtest::ocl
} // namespace ocl
} // namespace cvtest
#endif // HAVE_OPENCL
......@@ -62,6 +62,8 @@ protected:
double threshold1, threshold2;
bool test_cpp;
bool test_custom_deriv;
Mat img;
};
......@@ -77,6 +79,9 @@ CV_CannyTest::CV_CannyTest(bool custom_deriv)
test_cpp = false;
test_custom_deriv = custom_deriv;
const char imgPath[] = "shared/fruits.png";
img = cv::imread(cvtest::TS::ptr()->get_data_path() + imgPath, IMREAD_GRAYSCALE);
}
......@@ -112,8 +117,21 @@ int CV_CannyTest::prepare_test_case( int test_case_idx )
int code = cvtest::ArrayTest::prepare_test_case( test_case_idx );
if( code > 0 )
{
RNG& rng = ts->get_rng();
Mat& src = test_mat[INPUT][0];
GaussianBlur(src, src, Size(11, 11), 5, 5);
//GaussianBlur(src, src, Size(11, 11), 5, 5);
if(src.cols > img.cols || src.rows > img.rows)
resize(img, src, src.size());
else
img(
Rect(
cvtest::randInt(rng) % (img.cols-src.cols),
cvtest::randInt(rng) % (img.rows-src.rows),
src.cols,
src.rows
)
).copyTo(src);
GaussianBlur(src, src, Size(5, 5), 0);
}
return code;
......@@ -305,4 +323,107 @@ int CV_CannyTest::validate_test_results( int test_case_idx )
TEST(Imgproc_Canny, accuracy) { CV_CannyTest test; test.safe_run(); }
TEST(Imgproc_Canny, accuracy_deriv) { CV_CannyTest test(true); test.safe_run(); }
/*
* Comparing OpenVX based implementation with the main one
*/
#ifndef IMPLEMENT_PARAM_CLASS
#define IMPLEMENT_PARAM_CLASS(name, type) \
class name \
{ \
public: \
name ( type arg = type ()) : val_(arg) {} \
operator type () const {return val_;} \
private: \
type val_; \
}; \
inline void PrintTo( name param, std::ostream* os) \
{ \
*os << #name << "(" << testing::PrintToString(static_cast< type >(param)) << ")"; \
}
#endif // IMPLEMENT_PARAM_CLASS
IMPLEMENT_PARAM_CLASS(ImagePath, string)
IMPLEMENT_PARAM_CLASS(ApertureSize, int)
IMPLEMENT_PARAM_CLASS(L2gradient, bool)
PARAM_TEST_CASE(CannyVX, ImagePath, ApertureSize, L2gradient)
{
string imgPath;
int kSize;
bool useL2;
Mat src, dst;
virtual void SetUp()
{
imgPath = GET_PARAM(0);
kSize = GET_PARAM(1);
useL2 = GET_PARAM(2);
}
void loadImage()
{
src = cv::imread(cvtest::TS::ptr()->get_data_path() + imgPath, IMREAD_GRAYSCALE);
ASSERT_FALSE(src.empty()) << "cann't load image: " << imgPath;
}
};
TEST_P(CannyVX, Accuracy)
{
if(haveOpenVX())
{
loadImage();
setUseOpenVX(false);
Mat canny;
cv::Canny(src, canny, 100, 150, 3);
setUseOpenVX(true);
Mat cannyVX;
cv::Canny(src, cannyVX, 100, 150, 3);
// 'smart' diff check (excluding isolated pixels)
Mat diff, diff1;
absdiff(canny, cannyVX, diff);
boxFilter(diff, diff1, -1, Size(3,3));
const int minPixelsAroud = 3; // empirical number
diff1 = diff1 > 255/9 * minPixelsAroud;
erode(diff1, diff1, Mat());
double error = cv::norm(diff1, NORM_L1) / 255;
const int maxError = std::min(10, diff.size().area()/100); // empirical number
if(error > maxError)
{
string outPath =
string("CannyVX-diff-") +
imgPath + '-' +
'k' + char(kSize+'0') + '-' +
(useL2 ? "l2" : "l1");
std::replace(outPath.begin(), outPath.end(), '/', '_');
std::replace(outPath.begin(), outPath.end(), '\\', '_');
std::replace(outPath.begin(), outPath.end(), '.', '_');
imwrite(outPath+".png", diff);
}
ASSERT_LE(error, maxError);
}
}
INSTANTIATE_TEST_CASE_P(
ImgProc, CannyVX,
testing::Combine(
testing::Values(
string("shared/baboon.png"),
string("shared/fruits.png"),
string("shared/lena.png"),
string("shared/pic1.png"),
string("shared/pic3.png"),
string("shared/pic5.png"),
string("shared/pic6.png")
),
testing::Values(ApertureSize(3), ApertureSize(5)),
testing::Values(L2gradient(false), L2gradient(true))
)
);
/* End of file. */
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