From 1e2ddc30b10f948e733344919db247e7f665a7bc Mon Sep 17 00:00:00 2001 From: apavlenko Date: Fri, 25 Nov 2016 12:35:55 +0300 Subject: [PATCH 1/8] Canny via OpenVX, Node wrapper extended (query/set attribute), some naming fixes --- 3rdparty/openvx/include/ivx.hpp | 222 ++++++++++++++---- modules/core/include/opencv2/core.hpp | 1 + .../include/opencv2/core/openvx/ovx_defs.hpp | 33 +++ modules/core/include/opencv2/core/ovx.hpp | 28 +++ modules/core/src/ovx.cpp | 72 ++++++ modules/core/src/precomp.hpp | 11 +- modules/imgproc/src/canny.cpp | 73 ++++++ modules/imgproc/src/deriv.cpp | 8 +- 8 files changed, 389 insertions(+), 59 deletions(-) create mode 100644 modules/core/include/opencv2/core/openvx/ovx_defs.hpp create mode 100644 modules/core/include/opencv2/core/ovx.hpp create mode 100644 modules/core/src/ovx.cpp diff --git a/3rdparty/openvx/include/ivx.hpp b/3rdparty/openvx/include/ivx.hpp index 57eeda34a2..3d5102f5cf 100644 --- a/3rdparty/openvx/include/ivx.hpp +++ b/3rdparty/openvx/include/ivx.hpp @@ -100,6 +100,9 @@ Details: TBD namespace ivx { +inline vx_uint16 compiledWithVersion() +{ return VX_VERSION; } + /// Exception class for OpenVX runtime errors class RuntimeError : public std::runtime_error { @@ -715,8 +718,9 @@ protected: #endif // IVX_USE_EXTERNAL_REFCOUNT #ifndef VX_VERSION_1_1 -//TODO: provide wrapper for border mode -typedef vx_border_mode_t vx_border_t; +typedef vx_border_mode_t border_t; +#else +typedef vx_border_t border_t; #endif /// vx_context wrapper @@ -781,7 +785,7 @@ public: } /// vxQueryContext(VX_CONTEXT_UNIQUE_KERNELS) wrapper - vx_uint32 uniqueKernels() const + vx_uint32 uniqueKernelsNum() const { vx_uint32 v; query(VX_CONTEXT_UNIQUE_KERNELS, v); @@ -789,7 +793,7 @@ public: } /// vxQueryContext(VX_CONTEXT_MODULES) wrapper - vx_uint32 modules() const + vx_uint32 modulesNum() const { vx_uint32 v; query(VX_CONTEXT_MODULES, v); @@ -797,7 +801,7 @@ public: } /// vxQueryContext(VX_CONTEXT_REFERENCES) wrapper - vx_uint32 references() const + vx_uint32 refsNum() const { vx_uint32 v; query(VX_CONTEXT_REFERENCES, v); @@ -829,15 +833,15 @@ public: } /// vxQueryContext(VX_CONTEXT_IMMEDIATE_BORDER) wrapper - vx_border_t borderMode() const + border_t immediateBorder() const { - vx_border_t v; + border_t v; query(VX_CONTEXT_IMMEDIATE_BORDER, v); return v; } /// vxQueryContext(VX_CONTEXT_IMPLEMENTATION) wrapper - std::string implementation() const + std::string implName() const { std::vector v(VX_MAX_IMPLEMENTATION_NAME); IVX_CHECK_STATUS(vxQueryContext(ref, VX_CONTEXT_IMPLEMENTATION, &v[0], v.size() * sizeof(vx_char))); @@ -845,7 +849,7 @@ public: } /// vxQueryContext(VX_CONTEXT_EXTENSIONS) wrapper - std::string extensions() const + std::string extensionsStr() const { std::vector v(extensionsSize()); IVX_CHECK_STATUS(vxQueryContext(ref, VX_CONTEXT_EXTENSIONS, &v[0], v.size() * sizeof(vx_char))); @@ -855,14 +859,14 @@ public: /// vxQueryContext(VX_CONTEXT_UNIQUE_KERNEL_TABLE) wrapper std::vector kernelTable() const { - std::vector v(uniqueKernels()); + std::vector v(uniqueKernelsNum()); IVX_CHECK_STATUS(vxQueryContext(ref, VX_CONTEXT_UNIQUE_KERNEL_TABLE, &v[0], v.size() * sizeof(vx_kernel_info_t))); return v; } #ifdef VX_VERSION_1_1 /// vxQueryContext(VX_CONTEXT_IMMEDIATE_BORDER_POLICY) wrapper - vx_enum borderPolicy() const + vx_enum immediateBorderPolicy() const { vx_enum v; query(VX_CONTEXT_IMMEDIATE_BORDER_POLICY, v); @@ -878,10 +882,28 @@ public: } #endif - /// vxSetContextAttribute(VX_CONTEXT_IMMEDIATE_BORDER) wrapper - void setBorderMode(vx_border_t &border) - { IVX_CHECK_STATUS(vxSetContextAttribute(ref, VX_CONTEXT_IMMEDIATE_BORDER, &border, sizeof(border))); } + /// vxSetContextAttribute() wrapper + template + void setAttribute(vx_enum att, const T& value) + { IVX_CHECK_STATUS( vxSetContextAttribute(ref, att, &value, sizeof(value)) ); } + /// vxSetContextAttribute(BORDER) wrapper + void setImmediateBorder(const border_t& bm) + { setAttribute(VX_CONTEXT_IMMEDIATE_BORDER, bm); } + +#ifndef VX_VERSION_1_1 + /// vxSetContextAttribute(BORDER) wrapper + void setImmediateBorder(vx_enum mode, vx_uint32 val = 0) + { border_t bm = {mode, val}; setImmediateBorder(bm); } +#else + /// vxSetContextAttribute(BORDER) wrapper + void setImmediateBorder(vx_enum mode, const vx_pixel_value_t& val) + { border_t bm = {mode, val}; setImmediateBorder(bm); } + + /// vxSetContextAttribute(BORDER) wrapper + void setImmediateBorder(vx_enum mode) + { vx_pixel_value_t val = {}; setImmediateBorder(mode, val); } +#endif }; /// vx_graph wrapper @@ -926,7 +948,6 @@ public: { return Kernel(vxGetKernelByName(c, name.c_str())); } }; -#ifdef IVX_USE_CXX98 /// vx_node wrapper class Node : public RefWrapper @@ -952,6 +973,7 @@ public: static Node create(vx_graph graph, vx_enum kernelID, const std::vector& params) { return Node::create(graph, Kernel::getByEnum(Context::getFrom(graph), kernelID), params); } +#ifdef IVX_USE_CXX98 /// Create node for the kernel ID and set one parameter template static Node create(vx_graph g, vx_enum kernelID, @@ -1113,49 +1135,140 @@ public: return create(g, Kernel::getByEnum(Context::getFrom(g), kernelID), params); } - /// vxSetParameterByIndex() wrapper - void setParameterByIndex(vx_uint32 index, vx_reference value) - { IVX_CHECK_STATUS(vxSetParameterByIndex(ref, index, value)); } -}; - #else // not IVX_USE_CXX98 -/// vx_node wrapper -class Node : public RefWrapper -{ -public: - IVX_REF_STD_CTORS_AND_ASSIGNMENT(Node); - - /// vxCreateGenericNode() wrapper - static Node create(vx_graph g, vx_kernel k) - { return Node(vxCreateGenericNode(g, k)); } - - /// Create node for the kernel and set the parameters - static Node create(vx_graph graph, vx_kernel kernel, const std::vector& params) - { - Node node = Node::create(graph, kernel); - vx_uint32 i = 0; - for (const auto& p : params) - node.setParameterByIndex(i++, p); - return node; - } - - /// Create node for the kernel ID and set the parameters - static Node create(vx_graph graph, vx_enum kernelID, const std::vector& params) - { return Node::create(graph, Kernel::getByEnum(Context::getFrom(graph), kernelID), params); } - /// Create node for the kernel ID and set the specified parameters template static Node create(vx_graph g, vx_enum kernelID, const Ts&...args) { return create(g, Kernel::getByEnum(Context::getFrom(g), kernelID), { castToReference(args)... }); } +#endif // IVX_USE_CXX98 /// vxSetParameterByIndex() wrapper void setParameterByIndex(vx_uint32 index, vx_reference value) { IVX_CHECK_STATUS(vxSetParameterByIndex(ref, index, value)); } + + /// vxQueryNode() wrapper + template + void query(vx_enum att, T& value) const + { IVX_CHECK_STATUS( vxQueryNode(ref, att, &value, sizeof(value)) ); } + +#ifndef VX_VERSION_1_1 +static const vx_enum + VX_NODE_STATUS = VX_NODE_ATTRIBUTE_STATUS, + VX_NODE_PERFORMANCE = VX_NODE_ATTRIBUTE_PERFORMANCE, + VX_NODE_BORDER = VX_NODE_ATTRIBUTE_BORDER_MODE, + VX_NODE_LOCAL_DATA_SIZE = VX_NODE_ATTRIBUTE_LOCAL_DATA_SIZE, + VX_NODE_LOCAL_DATA_PTR = VX_NODE_ATTRIBUTE_LOCAL_DATA_PTR, + VX_BORDER_UNDEFINED = VX_BORDER_MODE_UNDEFINED; +#endif + + /// vxQueryNode(STATUS) wrapper + vx_status status() const + { + vx_status v; + query(VX_NODE_STATUS, v); + return v; + } + + /// vxQueryNode(PERFORMANCE) wrapper + vx_perf_t performance() const + { + vx_perf_t v; + query(VX_NODE_PERFORMANCE, v); + return v; + } + + /// vxQueryNode(BORDER) wrapper + border_t border() const + { + border_t v; + v.mode = VX_BORDER_UNDEFINED; + query(VX_NODE_BORDER, v); + return v; + } + + /// vxQueryNode(LOCAL_DATA_SIZE) wrapper + vx_size dataSize() const + { + vx_size v; + query(VX_NODE_LOCAL_DATA_SIZE, v); + return v; + } + + /// vxQueryNode(LOCAL_DATA_PTR) wrapper + void* dataPtr() const + { + void* v; + query(VX_NODE_LOCAL_DATA_PTR, v); + return v; + } + +#ifdef VX_VERSION_1_1 + /// vxQueryNode(PARAMETERS) wrapper + vx_uint32 paramsNum() const + { + vx_uint32 v; + query(VX_NODE_PARAMETERS, v); + return v; + } + + /// vxQueryNode(REPLICATED) wrapper + vx_bool isReplicated() const + { + vx_bool v; + query(VX_NODE_IS_REPLICATED, v); + return v; + } + + /// vxQueryNode(REPLICATE_FLAGS) wrapper + void replicateFlags(std::vector& flags) const + { + if(flags.empty()) flags.resize(paramsNum(), vx_false_e); + IVX_CHECK_STATUS( vxQueryNode(ref, VX_NODE_REPLICATE_FLAGS, &flags[0], flags.size()*sizeof(flags[0])) ); + } + + /// vxQueryNode(VX_NODE_VALID_RECT_RESET) wrapper + vx_bool resetValidRect() const + { + vx_bool v; + query(VX_NODE_VALID_RECT_RESET, v); + return v; + } +#endif // VX_VERSION_1_1 + + /// vxSetNodeAttribute() wrapper + template + void setAttribute(vx_enum att, const T& value) + { IVX_CHECK_STATUS( vxSetNodeAttribute(ref, att, &value, sizeof(value)) ); } + + /// vxSetNodeAttribute(BORDER) wrapper + void setBorder(const border_t& bm) + { setAttribute(VX_NODE_BORDER, bm); } + +#ifndef VX_VERSION_1_1 + /// vxSetNodeAttribute(BORDER) wrapper + void setBorder(vx_enum mode, vx_uint32 val = 0) + { vx_border_mode_t bm = {mode, val}; setBorder(bm); } +#else + /// vxSetNodeAttribute(BORDER) wrapper + void setBorder(vx_enum mode, const vx_pixel_value_t& val) + { vx_border_t bm = {mode, val}; setBorder(bm); } + + /// vxSetNodeAttribute(BORDER) wrapper + void setBorder(vx_enum mode) + { vx_pixel_value_t val = {}; setBorder(mode, val); } +#endif + + /// vxSetNodeAttribute(LOCAL_DATA_SIZE) wrapper + void setDataSize(vx_size size) + { setAttribute(VX_NODE_LOCAL_DATA_SIZE, size); } + + /// vxSetNodeAttribute(LOCAL_DATA_PTR) wrapper + void setDataPtr(void* ptr) + { setAttribute(VX_NODE_LOCAL_DATA_PTR, ptr); } }; -#endif // IVX_USE_CXX98 /// vx_image wrapper class Image : public RefWrapper @@ -1326,7 +1439,7 @@ static const vx_enum VX_IMAGE_SIZE = VX_IMAGE_ATTRIBUTE_SIZE; #endif -/// vxQueryImage(VX_IMAGE_WIDTH) wrapper + /// vxQueryImage(VX_IMAGE_WIDTH) wrapper vx_uint32 width() const { vx_uint32 v; @@ -1580,13 +1693,18 @@ static const vx_enum copyFrom(planeIdx, createAddressing((vx_uint32)m.cols, (vx_uint32)m.rows, (vx_int32)m.elemSize(), (vx_int32)m.step), m.ptr()); } - /* +/* +private: + cv::Mat _mat; // TODO: update copy/move-c-tors, operator=() and swapHandles() +public: static Image createFromHandle(vx_context context, const cv::Mat& mat) - { throw WrapperError(std::string(__func__)+"(): NYI"); } - - cv::Mat swapHandle(const cv::Mat& newMat) - { throw WrapperError(std::string(__func__)+"(): NYI"); } - */ + { + if(mat.empty()) throw WrapperError(std::string(__func__)+"(): empty cv::Mat"); + Image res = createFromHandle(context, matTypeToFormat(mat.type()), createAddressing(mat), mat.data ); + res._mat = mat; + return res; + } +*/ #endif //IVX_USE_OPENCV struct Patch; diff --git a/modules/core/include/opencv2/core.hpp b/modules/core/include/opencv2/core.hpp index cb2a8512fe..6b89c42efa 100644 --- a/modules/core/include/opencv2/core.hpp +++ b/modules/core/include/opencv2/core.hpp @@ -3215,5 +3215,6 @@ template<> struct ParamType #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*/ diff --git a/modules/core/include/opencv2/core/openvx/ovx_defs.hpp b/modules/core/include/opencv2/core/openvx/ovx_defs.hpp new file mode 100644 index 0000000000..a5cad4d99a --- /dev/null +++ b/modules/core/include/opencv2/core/openvx/ovx_defs.hpp @@ -0,0 +1,33 @@ +// 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 diff --git a/modules/core/include/opencv2/core/ovx.hpp b/modules/core/include/opencv2/core/ovx.hpp new file mode 100644 index 0000000000..8bb7d54911 --- /dev/null +++ b/modules/core/include/opencv2/core/ovx.hpp @@ -0,0 +1,28 @@ +// 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 diff --git a/modules/core/src/ovx.cpp b/modules/core/src/ovx.cpp new file mode 100644 index 0000000000..a53f5533aa --- /dev/null +++ b/modules/core/src/ovx.cpp @@ -0,0 +1,72 @@ +// 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 diff --git a/modules/core/src/precomp.hpp b/modules/core/src/precomp.hpp index df6c5b93ec..be30664acc 100644 --- a/modules/core/src/precomp.hpp +++ b/modules/core/src/precomp.hpp @@ -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& getCoreTlsData(); diff --git a/modules/imgproc/src/canny.cpp b/modules/imgproc/src/canny.cpp index 4636574b90..44ac37b1c1 100644 --- a/modules/imgproc/src/canny.cpp +++ b/modules/imgproc/src/canny.cpp @@ -45,6 +45,8 @@ #include "opencv2/core/hal/intrin.hpp" #include +#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(loVal), saturate_cast(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,19 @@ void Canny( InputArray _src, OutputArray _dst, Mat src = _src.getMat(), dst = _dst.getMat(); + CV_OVX_RUN( + 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; diff --git a/modules/imgproc/src/deriv.cpp b/modules/imgproc/src/deriv.cpp index c3c30cb354..fb3b2fff10 100644 --- a/modules/imgproc/src/deriv.cpp +++ b/modules/imgproc/src/deriv.cpp @@ -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) From ccd8031a333183bafce52dc22a9510b130117200 Mon Sep 17 00:00:00 2001 From: apavlenko Date: Fri, 2 Dec 2016 15:30:17 +0300 Subject: [PATCH 2/8] fixing compilation --- modules/imgproc/src/smooth.cpp | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/modules/imgproc/src/smooth.cpp b/modules/imgproc/src/smooth.cpp index 552ced0144..9651d13bea 100644 --- a/modules/imgproc/src/smooth.cpp +++ b/modules/imgproc/src/smooth.cpp @@ -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)); @@ -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) { @@ -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) { From 541d5b02d9ec4f6368b1b0b3ca250ee8aac2c7e6 Mon Sep 17 00:00:00 2001 From: apavlenko Date: Fri, 2 Dec 2016 15:59:09 +0300 Subject: [PATCH 3/8] disabling due to accuracy issues --- modules/imgproc/src/canny.cpp | 9 +++++---- modules/imgproc/src/smooth.cpp | 2 +- 2 files changed, 6 insertions(+), 5 deletions(-) diff --git a/modules/imgproc/src/canny.cpp b/modules/imgproc/src/canny.cpp index 44ac37b1c1..ee2dc339d9 100644 --- a/modules/imgproc/src/canny.cpp +++ b/modules/imgproc/src/canny.cpp @@ -866,10 +866,11 @@ void Canny( InputArray _src, OutputArray _dst, Mat src = _src.getMat(), dst = _dst.getMat(); CV_OVX_RUN( - src.type() == CV_8UC1 && - !src.isSubmatrix() && - src.cols >= aperture_size && - src.rows >= aperture_size, + false && /* disabling due to accuracy issues */ + src.type() == CV_8UC1 && + !src.isSubmatrix() && + src.cols >= aperture_size && + src.rows >= aperture_size, openvx_canny( src, dst, diff --git a/modules/imgproc/src/smooth.cpp b/modules/imgproc/src/smooth.cpp index 9651d13bea..e7da827783 100644 --- a/modules/imgproc/src/smooth.cpp +++ b/modules/imgproc/src/smooth.cpp @@ -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 From f3ec56fcee3c324ed61834f0795d53bb2986fc0c Mon Sep 17 00:00:00 2001 From: apavlenko Date: Mon, 5 Dec 2016 13:06:34 +0300 Subject: [PATCH 4/8] fixing build errors --- modules/imgproc/src/smooth.cpp | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/modules/imgproc/src/smooth.cpp b/modules/imgproc/src/smooth.cpp index e7da827783..afb434cf40 100644 --- a/modules/imgproc/src/smooth.cpp +++ b/modules/imgproc/src/smooth.cpp @@ -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) { From 76c38f0c809e46904a6d8c1a10ac90e75e413607 Mon Sep 17 00:00:00 2001 From: apavlenko Date: Tue, 6 Dec 2016 17:52:56 +0300 Subject: [PATCH 5/8] trying to enable canny_vx adding a new test comparing canny_cv vs canny_vx --- modules/core/src/convert.cpp | 16 ++--- modules/imgproc/src/canny.cpp | 2 +- modules/imgproc/test/ocl/test_canny.cpp | 88 ++++++++++++++++++++++++- modules/imgproc/test/test_canny.cpp | 5 +- 4 files changed, 96 insertions(+), 15 deletions(-) diff --git a/modules/core/src/convert.cpp b/modules/core/src/convert.cpp index d9b8857a89..4e6ed70ba4 100644 --- a/modules/core/src/convert.cpp +++ b/modules/core/src/convert.cpp @@ -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 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]); diff --git a/modules/imgproc/src/canny.cpp b/modules/imgproc/src/canny.cpp index ee2dc339d9..28141e8cbb 100644 --- a/modules/imgproc/src/canny.cpp +++ b/modules/imgproc/src/canny.cpp @@ -866,7 +866,7 @@ void Canny( InputArray _src, OutputArray _dst, Mat src = _src.getMat(), dst = _dst.getMat(); CV_OVX_RUN( - false && /* disabling due to accuracy issues */ + //false && /* disabling due to accuracy issues */ src.type() == CV_8UC1 && !src.isSubmatrix() && src.cols >= aperture_size && diff --git a/modules/imgproc/test/ocl/test_canny.cpp b/modules/imgproc/test/ocl/test_canny.cpp index 454198ac34..6798e2698c 100644 --- a/modules/imgproc/test/ocl/test_canny.cpp +++ b/modules/imgproc/test/ocl/test_canny.cpp @@ -133,6 +133,92 @@ OCL_INSTANTIATE_TEST_CASE_P(ImgProc, Canny, testing::Combine( testing::Values(L2gradient(false), L2gradient(true)), testing::Values(UseRoi(false), UseRoi(true)))); -} } // namespace cvtest::ocl + +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; + + TEST_DECLARE_INPUT_PARAMETER(src); + TEST_DECLARE_OUTPUT_PARAMETER(dst); + + virtual void SetUp() + { + imgPath = GET_PARAM(0); + kSize = GET_PARAM(1); + useL2 = GET_PARAM(2); + } + + void loadImage() + { + src = readImage(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); + + setUseOpenVX(false); + Mat diff, diff1; + absdiff(canny, cannyVX, diff); + boxFilter(diff, diff1, -1, Size(3,3)); + diff1 = diff1 > 255/9*3; + erode(diff1, diff1, Mat()); + double error = cv::norm(diff1, NORM_L1) / 255; + const int maxError = 10; + 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)) + ) + ); + +} // namespace ocl + +} // namespace cvtest #endif // HAVE_OPENCL diff --git a/modules/imgproc/test/test_canny.cpp b/modules/imgproc/test/test_canny.cpp index db4a82bf9d..4031839494 100644 --- a/modules/imgproc/test/test_canny.cpp +++ b/modules/imgproc/test/test_canny.cpp @@ -302,7 +302,8 @@ int CV_CannyTest::validate_test_results( int test_case_idx ) return code; } -TEST(Imgproc_Canny, accuracy) { CV_CannyTest test; test.safe_run(); } -TEST(Imgproc_Canny, accuracy_deriv) { CV_CannyTest test(true); test.safe_run(); } +// disabling, since testing on a white noise seems having too few sense... +TEST(DISABLED_Imgproc_Canny, accuracy) { CV_CannyTest test; test.safe_run(); } +TEST(DISABLED_Imgproc_Canny, accuracy_deriv) { CV_CannyTest test(true); test.safe_run(); } /* End of file. */ From 4246d3667ff44555a9bb265a0ca643e11f082cf8 Mon Sep 17 00:00:00 2001 From: apavlenko Date: Tue, 6 Dec 2016 18:55:09 +0300 Subject: [PATCH 6/8] disabling again --- modules/imgproc/src/canny.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/modules/imgproc/src/canny.cpp b/modules/imgproc/src/canny.cpp index 28141e8cbb..ee2dc339d9 100644 --- a/modules/imgproc/src/canny.cpp +++ b/modules/imgproc/src/canny.cpp @@ -866,7 +866,7 @@ void Canny( InputArray _src, OutputArray _dst, Mat src = _src.getMat(), dst = _dst.getMat(); CV_OVX_RUN( - //false && /* disabling due to accuracy issues */ + false && /* disabling due to accuracy issues */ src.type() == CV_8UC1 && !src.isSubmatrix() && src.cols >= aperture_size && From a99118c4c71c4552338d3410c3793eda3a836915 Mon Sep 17 00:00:00 2001 From: apavlenko Date: Fri, 9 Dec 2016 15:07:53 +0300 Subject: [PATCH 7/8] moving CannyVX test from ocl to cpp file --- modules/imgproc/test/ocl/test_canny.cpp | 84 ------------------- modules/imgproc/test/test_canny.cpp | 103 ++++++++++++++++++++++++ 2 files changed, 103 insertions(+), 84 deletions(-) diff --git a/modules/imgproc/test/ocl/test_canny.cpp b/modules/imgproc/test/ocl/test_canny.cpp index 6798e2698c..c5e0485ba7 100644 --- a/modules/imgproc/test/ocl/test_canny.cpp +++ b/modules/imgproc/test/ocl/test_canny.cpp @@ -133,90 +133,6 @@ OCL_INSTANTIATE_TEST_CASE_P(ImgProc, Canny, testing::Combine( testing::Values(L2gradient(false), L2gradient(true)), testing::Values(UseRoi(false), UseRoi(true)))); - -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; - - TEST_DECLARE_INPUT_PARAMETER(src); - TEST_DECLARE_OUTPUT_PARAMETER(dst); - - virtual void SetUp() - { - imgPath = GET_PARAM(0); - kSize = GET_PARAM(1); - useL2 = GET_PARAM(2); - } - - void loadImage() - { - src = readImage(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); - - setUseOpenVX(false); - Mat diff, diff1; - absdiff(canny, cannyVX, diff); - boxFilter(diff, diff1, -1, Size(3,3)); - diff1 = diff1 > 255/9*3; - erode(diff1, diff1, Mat()); - double error = cv::norm(diff1, NORM_L1) / 255; - const int maxError = 10; - 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)) - ) - ); - } // namespace ocl } // namespace cvtest diff --git a/modules/imgproc/test/test_canny.cpp b/modules/imgproc/test/test_canny.cpp index 4031839494..ea7f5637f8 100644 --- a/modules/imgproc/test/test_canny.cpp +++ b/modules/imgproc/test/test_canny.cpp @@ -306,4 +306,107 @@ int CV_CannyTest::validate_test_results( int test_case_idx ) TEST(DISABLED_Imgproc_Canny, accuracy) { CV_CannyTest test; test.safe_run(); } TEST(DISABLED_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); + + setUseOpenVX(false); + 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 = 10; // 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. */ From 3aedc134c205eb01109498270cdc738d772ec0a5 Mon Sep 17 00:00:00 2001 From: apavlenko Date: Fri, 9 Dec 2016 15:49:08 +0300 Subject: [PATCH 8/8] replacing white noise with fruits picture --- modules/imgproc/test/test_canny.cpp | 29 +++++++++++++++++++++++------ 1 file changed, 23 insertions(+), 6 deletions(-) diff --git a/modules/imgproc/test/test_canny.cpp b/modules/imgproc/test/test_canny.cpp index ea7f5637f8..cee069a3a8 100644 --- a/modules/imgproc/test/test_canny.cpp +++ b/modules/imgproc/test/test_canny.cpp @@ -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; @@ -302,9 +320,8 @@ int CV_CannyTest::validate_test_results( int test_case_idx ) return code; } -// disabling, since testing on a white noise seems having too few sense... -TEST(DISABLED_Imgproc_Canny, accuracy) { CV_CannyTest test; test.safe_run(); } -TEST(DISABLED_Imgproc_Canny, accuracy_deriv) { CV_CannyTest test(true); 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(); } /* @@ -366,7 +383,7 @@ TEST_P(CannyVX, Accuracy) Mat cannyVX; cv::Canny(src, cannyVX, 100, 150, 3); - setUseOpenVX(false); + // 'smart' diff check (excluding isolated pixels) Mat diff, diff1; absdiff(canny, cannyVX, diff); boxFilter(diff, diff1, -1, Size(3,3)); @@ -374,7 +391,7 @@ TEST_P(CannyVX, Accuracy) diff1 = diff1 > 255/9 * minPixelsAroud; erode(diff1, diff1, Mat()); double error = cv::norm(diff1, NORM_L1) / 255; - const int maxError = 10; // empirical number + const int maxError = std::min(10, diff.size().area()/100); // empirical number if(error > maxError) { string outPath =