Merge branch 4.x
@@ -697,7 +697,7 @@ __CV_ENUM_FLAGS_BITWISE_XOR_EQ (EnumType, EnumType)
|
||||
#endif
|
||||
|
||||
/****************************************************************************************\
|
||||
* Thread sanitizer *
|
||||
* Sanitizers *
|
||||
\****************************************************************************************/
|
||||
#ifndef CV_THREAD_SANITIZER
|
||||
# if defined(__has_feature)
|
||||
@@ -707,6 +707,12 @@ __CV_ENUM_FLAGS_BITWISE_XOR_EQ (EnumType, EnumType)
|
||||
# endif
|
||||
#endif
|
||||
|
||||
#if defined(__clang__) || defined(__GNUC__)
|
||||
#define CV_DISABLE_UBSAN __attribute__((no_sanitize("undefined")))
|
||||
#else
|
||||
#define CV_DISABLE_UBSAN
|
||||
#endif
|
||||
|
||||
/****************************************************************************************\
|
||||
* exchange-add operation for atomic operations on reference counters *
|
||||
\****************************************************************************************/
|
||||
|
||||
@@ -361,6 +361,7 @@ CV_INLINE int cvRound( int value )
|
||||
}
|
||||
|
||||
/** @overload */
|
||||
CV_DISABLE_UBSAN
|
||||
CV_INLINE int cvFloor( float value )
|
||||
{
|
||||
#if defined CV__FASTMATH_ENABLE_GCC_MATH_BUILTINS || \
|
||||
|
||||
@@ -680,6 +680,13 @@ public:
|
||||
double angle = 30, a = cos(angle*CV_PI/180), b = sin(angle*CV_PI/180);
|
||||
Mat R = (Mat_<double>(2,2) << a, -b, b, a);
|
||||
\endcode
|
||||
|
||||
\deprecated Use constructors with std::initializer_list instead:
|
||||
\code
|
||||
Mat_<int> m1({1, 2, 3, 4}); // 4x1 Mat
|
||||
Mat_<uchar> m2({2, 3}, {1, 2, 3, 4, 5, 6}); // 2x3 Mat
|
||||
|
||||
Mat_<double> R({2, 2}, {a, -b, b, a}); // from example
|
||||
*/
|
||||
template<typename _Tp> class MatCommaInitializer_
|
||||
{
|
||||
@@ -1261,7 +1268,7 @@ public:
|
||||
|
||||
/** @overload
|
||||
*/
|
||||
template<typename _Tp> explicit Mat(const MatCommaInitializer_<_Tp>& commaInitializer);
|
||||
template<typename _Tp> CV_DEPRECATED_EXTERNAL explicit Mat(const MatCommaInitializer_<_Tp>& commaInitializer);
|
||||
|
||||
//! download data from GpuMat
|
||||
explicit Mat(const cuda::GpuMat& m);
|
||||
@@ -2618,7 +2625,7 @@ public:
|
||||
template<int m, int n> explicit Mat_(const Matx<typename DataType<_Tp>::channel_type, m, n>& mtx, bool copyData=true);
|
||||
explicit Mat_(const Point_<typename DataType<_Tp>::channel_type>& pt, bool copyData=true);
|
||||
explicit Mat_(const Point3_<typename DataType<_Tp>::channel_type>& pt, bool copyData=true);
|
||||
explicit Mat_(const MatCommaInitializer_<_Tp>& commaInitializer);
|
||||
CV_DEPRECATED_EXTERNAL explicit Mat_(const MatCommaInitializer_<_Tp>& commaInitializer);
|
||||
|
||||
Mat_(std::initializer_list<_Tp> values);
|
||||
explicit Mat_(const std::initializer_list<int> sizes, const std::initializer_list<_Tp> values);
|
||||
|
||||
@@ -3207,7 +3207,7 @@ MatCommaInitializer_<_Tp>::operator Mat_<_Tp>() const
|
||||
}
|
||||
|
||||
|
||||
template<typename _Tp, typename T2> static inline
|
||||
template<typename _Tp, typename T2> CV_DEPRECATED_EXTERNAL static inline
|
||||
MatCommaInitializer_<_Tp> operator << (const Mat_<_Tp>& m, T2 val)
|
||||
{
|
||||
MatCommaInitializer_<_Tp> commaInitializer((Mat_<_Tp>*)&m);
|
||||
|
||||
@@ -115,7 +115,7 @@ public:
|
||||
int idx;
|
||||
};
|
||||
|
||||
template<typename _Tp, typename _T2, int m, int n> static inline
|
||||
template<typename _Tp, typename _T2, int m, int n> CV_DEPRECATED_EXTERNAL static inline
|
||||
MatxCommaInitializer<_Tp, m, n> operator << (const Matx<_Tp, m, n>& mtx, _T2 val)
|
||||
{
|
||||
MatxCommaInitializer<_Tp, m, n> commaInitializer((Matx<_Tp, m, n>*)&mtx);
|
||||
@@ -738,7 +738,7 @@ public:
|
||||
Vec<_Tp, m> operator *() const;
|
||||
};
|
||||
|
||||
template<typename _Tp, typename _T2, int cn> static inline
|
||||
template<typename _Tp, typename _T2, int cn> CV_DEPRECATED_EXTERNAL static inline
|
||||
VecCommaInitializer<_Tp, cn> operator << (const Vec<_Tp, cn>& vec, _T2 val)
|
||||
{
|
||||
VecCommaInitializer<_Tp, cn> commaInitializer((Vec<_Tp, cn>*)&vec);
|
||||
|
||||
@@ -307,8 +307,12 @@ public:
|
||||
before opening the file.
|
||||
@param filename Name of the file to open or the text string to read the data from.
|
||||
Extension of the file (.xml, .yml/.yaml or .json) determines its format (XML, YAML or JSON
|
||||
respectively). Also you can append .gz to work with compressed files, for example myHugeMatrix.xml.gz. If both
|
||||
FileStorage::WRITE and FileStorage::MEMORY flags are specified, source is used just to specify
|
||||
respectively). Also you can append .gz to work with compressed files, for example myHugeMatrix.xml.gz.
|
||||
You can also specify a compression level from 0 to 9 by appending it to the extension
|
||||
(e.g. ".gz0" for no compression, ".gz9" for high compression).
|
||||
The last digit will be truncated internally to write/read.
|
||||
(e.g. If "a.xml.gz9" is specified, "a.xml.gz" is used for the actual file name.)
|
||||
If both FileStorage::WRITE and FileStorage::MEMORY flags are specified, source is used just to specify
|
||||
the output file format (e.g. mydata.xml, .yml etc.). A file name can also contain parameters.
|
||||
You can use this format, "*?base64" (e.g. "file.json?base64" (case sensitive)), as an alternative to
|
||||
FileStorage::BASE64 flag.
|
||||
|
||||
@@ -1245,6 +1245,26 @@ _Tp Point_<_Tp>::dot(const Point_& pt) const
|
||||
return saturate_cast<_Tp>(x*pt.x + y*pt.y);
|
||||
}
|
||||
|
||||
template<> inline
|
||||
int Point_<int>::dot(const Point_<int>& pt) const
|
||||
{
|
||||
const int64_t xx = (int64_t)x * pt.x;
|
||||
const int64_t yy = (int64_t)y * pt.y;
|
||||
|
||||
// detect int64 overflow before adding
|
||||
if (yy > 0 && xx > INT64_MAX - yy)
|
||||
{
|
||||
return INT32_MAX;
|
||||
}
|
||||
if (yy < 0 && xx < INT64_MIN - yy)
|
||||
{
|
||||
return INT32_MIN;
|
||||
}
|
||||
|
||||
const int64_t v = xx + yy;
|
||||
return cv::saturate_cast<int>(v);
|
||||
}
|
||||
|
||||
template<typename _Tp> inline
|
||||
double Point_<_Tp>::ddot(const Point_& pt) const
|
||||
{
|
||||
@@ -1490,6 +1510,50 @@ _Tp Point3_<_Tp>::dot(const Point3_& pt) const
|
||||
return saturate_cast<_Tp>(x*pt.x + y*pt.y + z*pt.z);
|
||||
}
|
||||
|
||||
template<> inline
|
||||
int Point3_<int>::dot(const Point3_<int>& pt) const
|
||||
{
|
||||
const int64_t xx = (int64_t)x * pt.x;
|
||||
const int64_t yy = (int64_t)y * pt.y;
|
||||
const int64_t zz = (int64_t)z * pt.z;
|
||||
|
||||
// Sort the three products so that lo <= mid <= hi.
|
||||
// We add in the order (lo + hi) first, then add mid.
|
||||
// This minimizes the absolute value of the intermediate sum,
|
||||
// reducing the chance of int64 overflow during addition.
|
||||
int64_t lo = xx, mid = yy, hi = zz;
|
||||
if (lo > mid) std::swap(lo, mid);
|
||||
if (mid > hi) std::swap(mid, hi);
|
||||
if (lo > mid) std::swap(lo, mid);
|
||||
|
||||
// Step 1: lo + hi
|
||||
// If lo + hi overflows int64, the final result must saturate to INT32_MAX/INT32_MIN.
|
||||
// The middle value (mid) cannot bring the result back into the int32 range,
|
||||
// so we can return immediately without considering mid.
|
||||
if (hi > 0 && lo > INT64_MAX - hi)
|
||||
{
|
||||
return INT32_MAX;
|
||||
}
|
||||
if (hi < 0 && lo < INT64_MIN - hi)
|
||||
{
|
||||
return INT32_MIN;
|
||||
}
|
||||
int64_t sum = lo + hi;
|
||||
|
||||
// Step 2: sum + mid
|
||||
if (mid > 0 && sum > INT64_MAX - mid)
|
||||
{
|
||||
return INT32_MAX;
|
||||
}
|
||||
if (mid < 0 && sum < INT64_MIN - mid)
|
||||
{
|
||||
return INT32_MIN;
|
||||
}
|
||||
sum += mid;
|
||||
|
||||
return cv::saturate_cast<int>(sum);
|
||||
}
|
||||
|
||||
template<typename _Tp> inline
|
||||
double Point3_<_Tp>::ddot(const Point3_& pt) const
|
||||
{
|
||||
|
||||
@@ -102,11 +102,18 @@ template<typename _Tp, size_t fixed_size = 1024/sizeof(_Tp)+8> class AutoBuffer
|
||||
{
|
||||
public:
|
||||
typedef _Tp value_type;
|
||||
typedef std::size_t size_type;
|
||||
typedef std::ptrdiff_t difference_type;
|
||||
typedef const _Tp* const_iterator;
|
||||
typedef _Tp* iterator;
|
||||
typedef const _Tp& const_reference;
|
||||
typedef _Tp& reference;
|
||||
|
||||
//! the default constructor
|
||||
AutoBuffer();
|
||||
//! constructor taking the real buffer size
|
||||
explicit AutoBuffer(size_t _size);
|
||||
AutoBuffer(size_t _size, const _Tp& value);
|
||||
|
||||
//! the copy constructor
|
||||
AutoBuffer(const AutoBuffer<_Tp, fixed_size>& buf);
|
||||
@@ -140,7 +147,25 @@ public:
|
||||
//! returns a reference to the element at specified location. No bounds checking is performed in Release builds.
|
||||
inline const _Tp& operator[] (size_t i) const { CV_DbgCheckLT(i, sz, "out of range"); return ptr[i]; }
|
||||
#endif
|
||||
|
||||
public:
|
||||
inline iterator begin() { return data(); }
|
||||
inline const_iterator begin() const { return data(); }
|
||||
inline const_iterator cbegin() const { return begin(); }
|
||||
inline iterator end() { return data()+size(); }
|
||||
inline const_iterator end() const { return data()+size(); }
|
||||
inline const_iterator cend() const { return end(); }
|
||||
public:
|
||||
inline bool empty() const { return !size(); }
|
||||
inline void clear() {resize(0);}
|
||||
inline const_reference front() const { return (*this)[0] ;}
|
||||
inline reference front() { return (*this)[0] ;}
|
||||
inline const_reference back() const { CV_DbgCheckGT(sz, (size_t)0, "out of range"); return (*this)[size()-1] ;}
|
||||
inline reference back() { CV_DbgCheckGT(sz, (size_t)0, "out of range"); return (*this)[size()-1] ;}
|
||||
public:
|
||||
inline void push_back( const _Tp& value ) {resize(size()+1); back() = value;}
|
||||
inline void push_back( _Tp&& value ) {resize(size()+1); back() = std::move(value);}
|
||||
inline void emplace_back( _Tp&& value ) {push_back(value);}
|
||||
inline void pop_back() {CV_DbgCheckGT(sz, (size_t)0, "out of range"); resize(size()-1);}
|
||||
protected:
|
||||
//! pointer to the real buffer, can point to buf if the buffer is small enough
|
||||
_Tp* ptr;
|
||||
@@ -1054,6 +1079,13 @@ AutoBuffer<_Tp, fixed_size>::AutoBuffer(size_t _size)
|
||||
allocate(_size);
|
||||
}
|
||||
|
||||
template<typename _Tp, size_t fixed_size> inline
|
||||
AutoBuffer<_Tp, fixed_size>::AutoBuffer(size_t _size, const _Tp& value)
|
||||
:AutoBuffer<_Tp, fixed_size>(_size)
|
||||
{
|
||||
std::fill(begin(), end(), value);
|
||||
}
|
||||
|
||||
template<typename _Tp, size_t fixed_size> inline
|
||||
AutoBuffer<_Tp, fixed_size>::AutoBuffer(const AutoBuffer<_Tp, fixed_size>& abuf )
|
||||
{
|
||||
|
||||
@@ -103,7 +103,7 @@ static bool ocl_math_op(InputArray _src1, InputArray _src2, OutputArray _dst, in
|
||||
Fast cube root by Ken Turkowski
|
||||
(http://www.worldserver.com/turk/computergraphics/papers.html)
|
||||
\* ************************************************************************** */
|
||||
float cubeRoot( float value )
|
||||
CV_DISABLE_UBSAN float cubeRoot( float value )
|
||||
{
|
||||
CV_INSTRUMENT_REGION();
|
||||
|
||||
|
||||
@@ -1160,6 +1160,535 @@ struct NormDiffL2_SIMD<double, double> {
|
||||
|
||||
#endif
|
||||
|
||||
template <typename T, typename ST>
|
||||
struct MaskedNormInf_SIMD {
|
||||
inline ST operator() (const T* src, const uchar* mask, int len, int cn) const {
|
||||
ST s = 0;
|
||||
if (cn == 1) {
|
||||
for (int i = 0; i < len; i++) {
|
||||
if (mask[i]) {
|
||||
s = std::max(s, (ST)cv_abs(src[i]));
|
||||
}
|
||||
}
|
||||
}
|
||||
else {
|
||||
for (int i = 0; i < len; i++) {
|
||||
if (mask[i]) {
|
||||
const T* elem = src + i * cn;
|
||||
int k = 0;
|
||||
#if CV_ENABLE_UNROLLED
|
||||
for (; k <= cn - 4; k += 4) {
|
||||
s = std::max(s, (ST)cv_abs(elem[k]));
|
||||
s = std::max(s, (ST)cv_abs(elem[k + 1]));
|
||||
s = std::max(s, (ST)cv_abs(elem[k + 2]));
|
||||
s = std::max(s, (ST)cv_abs(elem[k + 3]));
|
||||
}
|
||||
#endif
|
||||
for (; k < cn; k++) {
|
||||
s = std::max(s, (ST)cv_abs(elem[k]));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
return s;
|
||||
}
|
||||
};
|
||||
|
||||
template <typename T, typename ST>
|
||||
struct MaskedNormL1_SIMD {
|
||||
inline ST operator() (const T* src, const uchar* mask, int len, int cn) const {
|
||||
ST s = 0;
|
||||
if (cn == 1) {
|
||||
for (int i = 0; i < len; i++) {
|
||||
if (mask[i]) {
|
||||
s += (ST)cv_abs(src[i]);
|
||||
}
|
||||
}
|
||||
}
|
||||
else {
|
||||
for (int i = 0; i < len; i++) {
|
||||
if (mask[i]) {
|
||||
const T* elem = src + i * cn;
|
||||
int k = 0;
|
||||
#if CV_ENABLE_UNROLLED
|
||||
for (; k <= cn - 4; k += 4) {
|
||||
s += (ST)cv_abs(elem[k]);
|
||||
s += (ST)cv_abs(elem[k + 1]);
|
||||
s += (ST)cv_abs(elem[k + 2]);
|
||||
s += (ST)cv_abs(elem[k + 3]);
|
||||
}
|
||||
#endif
|
||||
for (; k < cn; k++) {
|
||||
s += (ST)cv_abs(elem[k]);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
return s;
|
||||
}
|
||||
};
|
||||
|
||||
template <typename T, typename ST>
|
||||
struct MaskedNormL2_SIMD {
|
||||
inline ST operator() (const T* src, const uchar* mask, int len, int cn) const {
|
||||
ST s = 0;
|
||||
if (cn == 1) {
|
||||
int i = 0;
|
||||
#if CV_ENABLE_UNROLLED
|
||||
for (; i <= len - 4; i += 4) {
|
||||
if (mask[i]) { T v0 = src[i]; s += (ST)v0 * v0; }
|
||||
if (mask[i + 1]) { T v1 = src[i + 1]; s += (ST)v1 * v1; }
|
||||
if (mask[i + 2]) { T v2 = src[i + 2]; s += (ST)v2 * v2; }
|
||||
if (mask[i + 3]) { T v3 = src[i + 3]; s += (ST)v3 * v3; }
|
||||
}
|
||||
#endif
|
||||
for (; i < len; i++) {
|
||||
if (mask[i]) {
|
||||
T v = src[i];
|
||||
s += (ST)v * v;
|
||||
}
|
||||
}
|
||||
}
|
||||
else {
|
||||
for (int i = 0; i < len; i++) {
|
||||
if (mask[i]) {
|
||||
const T* elem = src + i * cn;
|
||||
int k = 0;
|
||||
#if CV_ENABLE_UNROLLED
|
||||
for (; k <= cn - 4; k += 4) {
|
||||
T v0 = elem[k]; s += (ST)v0 * v0;
|
||||
T v1 = elem[k + 1]; s += (ST)v1 * v1;
|
||||
T v2 = elem[k + 2]; s += (ST)v2 * v2;
|
||||
T v3 = elem[k + 3]; s += (ST)v3 * v3;
|
||||
}
|
||||
#endif
|
||||
for (; k < cn; k++) {
|
||||
T v = elem[k];
|
||||
s += (ST)v * v;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
return s;
|
||||
}
|
||||
};
|
||||
|
||||
template <>
|
||||
struct MaskedNormInf_SIMD<float, float> {
|
||||
inline float operator()(const float* src, const uchar* mask, int len, int cn) const {
|
||||
float result = 0.0f;
|
||||
if (cn == 1) {
|
||||
int i = 0;
|
||||
const int vstep = VTraits<v_float32>::vlanes();
|
||||
v_float32 acc = vx_setzero_f32();
|
||||
|
||||
for (; i <= len - vstep; i += vstep) {
|
||||
v_uint32 m = v_reinterpret_as_u32(vx_load_expand(mask + i));
|
||||
v_uint32 cmp = v_gt(m, vx_setzero_u32());
|
||||
v_float32 s = vx_load(src + i);
|
||||
s = v_abs(s);
|
||||
s = v_reinterpret_as_f32(v_and(v_reinterpret_as_u32(s), cmp));
|
||||
acc = v_max(acc, s);
|
||||
}
|
||||
result = v_reduce_max(acc);
|
||||
|
||||
for (; i < len; i++) {
|
||||
if (mask[i])
|
||||
result = std::max(result, std::abs(src[i]));
|
||||
}
|
||||
}
|
||||
else {
|
||||
for (int i = 0; i < len; i++) {
|
||||
if (mask[i]) {
|
||||
const float* elem = src + i * cn;
|
||||
int k = 0;
|
||||
const int vstep = VTraits<v_float32>::vlanes();
|
||||
v_float32 acc = vx_setzero_f32();
|
||||
|
||||
for (; k <= cn - vstep; k += vstep) {
|
||||
v_float32 s = vx_load(elem + k);
|
||||
acc = v_max(acc, v_abs(s));
|
||||
}
|
||||
|
||||
result = std::max(result, v_reduce_max(acc));
|
||||
|
||||
for (; k < cn; k++)
|
||||
result = std::max(result, std::abs(elem[k]));
|
||||
}
|
||||
}
|
||||
}
|
||||
return result;
|
||||
}
|
||||
};
|
||||
|
||||
#if CV_SIMD_64F
|
||||
template <>
|
||||
struct MaskedNormL1_SIMD<float, double> {
|
||||
inline double operator()(const float* src, const uchar* mask, int len, int cn) const {
|
||||
double result = 0.0;
|
||||
if (cn == 1) {
|
||||
int i = 0;
|
||||
const int vstep = VTraits<v_float32>::vlanes();
|
||||
v_float64 acc = vx_setzero_f64();
|
||||
|
||||
for (; i <= len - vstep; i += vstep) {
|
||||
v_uint32 cmp = v_gt(vx_load_expand_q(mask + i), vx_setzero_u32());
|
||||
v_float32 s = v_reinterpret_as_f32(v_and(v_reinterpret_as_u32(v_abs(vx_load(src + i))), cmp));
|
||||
acc = v_add(acc, v_cvt_f64(s));
|
||||
acc = v_add(acc, v_cvt_f64_high(s));
|
||||
}
|
||||
result = v_reduce_sum(acc);
|
||||
|
||||
for (; i < len; i++) {
|
||||
if (mask[i])
|
||||
result += std::abs(src[i]);
|
||||
}
|
||||
}
|
||||
else {
|
||||
for (int i = 0; i < len; i++) {
|
||||
if (mask[i]) {
|
||||
const float* elem = src + i * cn;
|
||||
int k = 0;
|
||||
const int vstep = VTraits<v_float32>::vlanes();
|
||||
v_float64 acc = vx_setzero_f64();
|
||||
|
||||
for (; k <= cn - vstep; k += vstep) {
|
||||
v_float32 s = v_abs(vx_load(elem + k));
|
||||
acc = v_add(acc, v_cvt_f64(s));
|
||||
acc = v_add(acc, v_cvt_f64_high(s));
|
||||
}
|
||||
|
||||
result += v_reduce_sum(acc);
|
||||
|
||||
for (; k < cn; k++)
|
||||
result += std::abs(elem[k]);
|
||||
}
|
||||
}
|
||||
}
|
||||
return result;
|
||||
}
|
||||
};
|
||||
|
||||
template <>
|
||||
struct MaskedNormL2_SIMD<float, double> {
|
||||
inline double operator()(const float* src, const uchar* mask, int len, int cn) const {
|
||||
double result = 0.0;
|
||||
if (cn == 1) {
|
||||
int i = 0;
|
||||
const int vstep = VTraits<v_float32>::vlanes();
|
||||
v_float32 facc = vx_setzero_f32();
|
||||
v_float64 dacc = vx_setzero_f64();
|
||||
int flush = 0;
|
||||
|
||||
for (; i <= len - vstep; i += vstep, flush += vstep) {
|
||||
if (flush >= 64) {
|
||||
dacc = v_add(dacc, v_cvt_f64(facc));
|
||||
dacc = v_add(dacc, v_cvt_f64_high(facc));
|
||||
facc = vx_setzero_f32();
|
||||
flush = 0;
|
||||
}
|
||||
v_uint32 cmp = v_gt(vx_load_expand_q(mask + i), vx_setzero_u32());
|
||||
v_float32 s = v_reinterpret_as_f32(v_and(v_reinterpret_as_u32(vx_load(src + i)), cmp));
|
||||
facc = v_add(facc, v_mul(s, s));
|
||||
}
|
||||
|
||||
dacc = v_add(dacc, v_cvt_f64(facc));
|
||||
dacc = v_add(dacc, v_cvt_f64_high(facc));
|
||||
result = v_reduce_sum(dacc);
|
||||
|
||||
for (; i < len; i++) {
|
||||
if (mask[i]) {
|
||||
double v = src[i];
|
||||
result += v * v;
|
||||
}
|
||||
}
|
||||
}
|
||||
else {
|
||||
for (int i = 0; i < len; i++) {
|
||||
if (mask[i]) {
|
||||
const float* elem = src + i * cn;
|
||||
int k = 0;
|
||||
const int vstep = VTraits<v_float32>::vlanes();
|
||||
v_float32 facc = vx_setzero_f32();
|
||||
|
||||
for (; k <= cn - vstep; k += vstep) {
|
||||
v_float32 s = vx_load(elem + k);
|
||||
facc = v_add(facc, v_mul(s, s));
|
||||
}
|
||||
|
||||
v_float64 dacc = v_add(v_cvt_f64(facc), v_cvt_f64_high(facc));
|
||||
result += v_reduce_sum(dacc);
|
||||
|
||||
for (; k < cn; k++) {
|
||||
double v = elem[k];
|
||||
result += v * v;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
return result;
|
||||
}
|
||||
};
|
||||
|
||||
#endif
|
||||
|
||||
template <>
|
||||
struct MaskedNormInf_SIMD<uchar, int> {
|
||||
inline int operator()(const uchar* src, const uchar* mask, int len, int cn) const {
|
||||
int result = 0;
|
||||
if (cn == 1) {
|
||||
int i = 0;
|
||||
const int vstep = VTraits<v_uint8>::vlanes();
|
||||
v_uint8 acc = vx_setzero_u8();
|
||||
|
||||
for (; i <= len - vstep; i += vstep) {
|
||||
v_uint8 m = vx_load(mask + i);
|
||||
v_uint8 s = vx_load(src + i);
|
||||
v_uint8 sel = v_and(s, v_gt(m, vx_setzero_u8()));
|
||||
acc = v_max(acc, sel);
|
||||
}
|
||||
|
||||
result = (int)v_reduce_max(acc);
|
||||
|
||||
for (; i < len; i++) {
|
||||
if (mask[i])
|
||||
result = std::max(result, (int)src[i]);
|
||||
}
|
||||
}
|
||||
else {
|
||||
for (int i = 0; i < len; i++) {
|
||||
if (mask[i]) {
|
||||
const uchar* elem = src + i * cn;
|
||||
int k = 0;
|
||||
const int vstep = VTraits<v_uint8>::vlanes();
|
||||
v_uint8 acc = vx_setzero_u8();
|
||||
|
||||
for (; k <= cn - vstep; k += vstep) {
|
||||
acc = v_max(acc, vx_load(elem + k));
|
||||
}
|
||||
|
||||
result = std::max(result, (int)v_reduce_max(acc));
|
||||
|
||||
for (; k < cn; k++)
|
||||
result = std::max(result, (int)elem[k]);
|
||||
}
|
||||
}
|
||||
}
|
||||
return result;
|
||||
}
|
||||
};
|
||||
|
||||
template <>
|
||||
struct MaskedNormL1_SIMD<uchar, int> {
|
||||
inline int operator()(const uchar* src, const uchar* mask, int len, int cn) const {
|
||||
int result = 0;
|
||||
if (cn == 1) {
|
||||
int i = 0;
|
||||
const int vstep = VTraits<v_uint8>::vlanes() / 4;
|
||||
v_uint32 acc = vx_setzero_u32();
|
||||
for (; i <= len - vstep; i += vstep) {
|
||||
v_uint32 m = vx_load_expand_q(mask + i);
|
||||
v_uint32 s = vx_load_expand_q(src + i);
|
||||
v_uint32 sel = v_and(s, v_gt(m, vx_setzero_u32()));
|
||||
acc = v_add(acc, sel);
|
||||
}
|
||||
|
||||
result = (int)v_reduce_sum(acc);
|
||||
|
||||
for (; i < len; i++) {
|
||||
if (mask[i])
|
||||
result += src[i];
|
||||
}
|
||||
}
|
||||
else {
|
||||
for (int i = 0; i < len; i++) {
|
||||
if (mask[i]) {
|
||||
const uchar* elem = src + i * cn;
|
||||
int k = 0;
|
||||
const int vstep = VTraits<v_uint8>::vlanes() / 4;
|
||||
v_uint32 acc = vx_setzero_u32();
|
||||
|
||||
for (; k <= cn - vstep; k += vstep) {
|
||||
v_uint32 s = vx_load_expand_q(elem + k);
|
||||
acc = v_add(acc, s);
|
||||
}
|
||||
|
||||
result += (int)v_reduce_sum(acc);
|
||||
|
||||
for (; k < cn; k++)
|
||||
result += elem[k];
|
||||
}
|
||||
}
|
||||
}
|
||||
return result;
|
||||
}
|
||||
};
|
||||
|
||||
template <>
|
||||
struct MaskedNormInf_SIMD<ushort, int> {
|
||||
inline int operator()(const ushort* src, const uchar* mask, int len, int cn) const {
|
||||
int result = 0;
|
||||
if (cn == 1) {
|
||||
int i = 0;
|
||||
const int vstep = VTraits<v_uint16>::vlanes();
|
||||
v_uint16 acc = vx_setzero_u16();
|
||||
|
||||
for (; i <= len - vstep; i += vstep) {
|
||||
v_uint16 m = vx_load_expand(mask + i);
|
||||
v_uint16 cmp = v_gt(m, vx_setzero_u16());
|
||||
v_uint16 s = vx_load(src + i);
|
||||
v_uint16 sel = v_and(s, cmp);
|
||||
acc = v_max(acc, sel);
|
||||
}
|
||||
|
||||
result = (int)v_reduce_max(acc);
|
||||
|
||||
for (; i < len; i++) {
|
||||
if (mask[i])
|
||||
result = std::max(result, (int)src[i]);
|
||||
}
|
||||
}
|
||||
else {
|
||||
for (int i = 0; i < len; i++) {
|
||||
if (mask[i]) {
|
||||
const ushort* elem = src + i * cn;
|
||||
int k = 0;
|
||||
const int vstep = VTraits<v_uint16>::vlanes();
|
||||
v_uint16 acc = vx_setzero_u16();
|
||||
|
||||
for (; k <= cn - vstep; k += vstep) {
|
||||
acc = v_max(acc, vx_load(elem + k));
|
||||
}
|
||||
|
||||
result = std::max(result, (int)v_reduce_max(acc));
|
||||
|
||||
for (; k < cn; k++)
|
||||
result = std::max(result, (int)elem[k]);
|
||||
}
|
||||
}
|
||||
}
|
||||
return result;
|
||||
}
|
||||
};
|
||||
|
||||
template <>
|
||||
struct MaskedNormL1_SIMD<ushort, int> {
|
||||
inline int operator()(const ushort* src, const uchar* mask, int len, int cn) const {
|
||||
int result = 0;
|
||||
if (cn == 1) {
|
||||
int i = 0;
|
||||
const int vstep = VTraits<v_uint16>::vlanes();
|
||||
v_uint32 acc32 = vx_setzero_u32();
|
||||
v_uint64 acc64 = vx_setzero_u64();
|
||||
int acc32_elems = 0;
|
||||
|
||||
for (; i <= len - vstep; i += vstep, acc32_elems += vstep) {
|
||||
if (acc32_elems >= 512) {
|
||||
v_uint64 lo64, hi64;
|
||||
v_expand(acc32, lo64, hi64);
|
||||
acc64 = v_add(acc64, v_add(lo64, hi64));
|
||||
acc32 = vx_setzero_u32();
|
||||
acc32_elems = 0;
|
||||
}
|
||||
v_uint16 m = vx_load_expand(mask + i);
|
||||
v_uint16 cmp = v_gt(m, vx_setzero_u16());
|
||||
v_uint16 s = v_and(vx_load(src + i), cmp);
|
||||
v_uint32 lo32, hi32;
|
||||
v_expand(s, lo32, hi32);
|
||||
acc32 = v_add(acc32, v_add(lo32, hi32));
|
||||
}
|
||||
|
||||
v_uint64 lo64, hi64;
|
||||
v_expand(acc32, lo64, hi64);
|
||||
acc64 = v_add(acc64, v_add(lo64, hi64));
|
||||
result = (int)v_reduce_sum(acc64);
|
||||
|
||||
for (; i < len; i++) {
|
||||
if (mask[i])
|
||||
result += src[i];
|
||||
}
|
||||
}
|
||||
else {
|
||||
for (int i = 0; i < len; i++) {
|
||||
if (mask[i]) {
|
||||
const ushort* elem = src + i * cn;
|
||||
int k = 0;
|
||||
const int vstep = VTraits<v_uint16>::vlanes();
|
||||
v_uint32 acc = vx_setzero_u32();
|
||||
|
||||
for (; k <= cn - vstep; k += vstep) {
|
||||
v_uint32 lo32, hi32;
|
||||
v_expand(vx_load(elem + k), lo32, hi32);
|
||||
acc = v_add(acc, v_add(lo32, hi32));
|
||||
}
|
||||
|
||||
result += (int)v_reduce_sum(acc);
|
||||
|
||||
for (; k < cn; k++)
|
||||
result += elem[k];
|
||||
}
|
||||
}
|
||||
}
|
||||
return result;
|
||||
}
|
||||
};
|
||||
|
||||
template <>
|
||||
struct MaskedNormL2_SIMD<ushort, double> {
|
||||
inline double operator()(const ushort* src, const uchar* mask, int len, int cn) const {
|
||||
double result = 0.0;
|
||||
if (cn == 1) {
|
||||
int i = 0;
|
||||
const int vstep = VTraits<v_uint16>::vlanes();
|
||||
v_uint64 acc = vx_setzero_u64();
|
||||
for (; i <= len - vstep; i += vstep) {
|
||||
v_uint16 m = vx_load_expand(mask + i);
|
||||
v_uint16 cmp = v_gt(m, vx_setzero_u16());
|
||||
v_uint16 s = v_and(vx_load(src + i), cmp);
|
||||
v_uint32 lo32, hi32;
|
||||
v_expand(s, lo32, hi32);
|
||||
v_uint64 lo64a, lo64b, hi64a, hi64b;
|
||||
v_expand(v_mul(lo32, lo32), lo64a, lo64b);
|
||||
v_expand(v_mul(hi32, hi32), hi64a, hi64b);
|
||||
acc = v_add(acc, v_add(v_add(lo64a, lo64b), v_add(hi64a, hi64b)));
|
||||
}
|
||||
result = (double)v_reduce_sum(acc);
|
||||
for (; i < len; i++) {
|
||||
if (mask[i]) {
|
||||
double v = src[i];
|
||||
result += v * v;
|
||||
}
|
||||
}
|
||||
}
|
||||
else {
|
||||
for (int i = 0; i < len; i++) {
|
||||
if (mask[i]) {
|
||||
const ushort* elem = src + i * cn;
|
||||
int k = 0;
|
||||
const int vstep = VTraits<v_uint16>::vlanes();
|
||||
v_uint64 acc = vx_setzero_u64();
|
||||
for (; k <= cn - vstep; k += vstep) {
|
||||
v_uint32 lo32, hi32;
|
||||
v_expand(vx_load(elem + k), lo32, hi32);
|
||||
v_uint64 lo64a, lo64b, hi64a, hi64b;
|
||||
v_expand(v_mul(lo32, lo32), lo64a, lo64b);
|
||||
v_expand(v_mul(hi32, hi32), hi64a, hi64b);
|
||||
acc = v_add(acc, v_add(v_add(lo64a, lo64b), v_add(hi64a, hi64b)));
|
||||
}
|
||||
|
||||
result += (double)v_reduce_sum(acc);
|
||||
|
||||
for (; k < cn; k++) {
|
||||
double v = elem[k];
|
||||
result += v * v;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
return result;
|
||||
}
|
||||
};
|
||||
|
||||
template<typename T, typename ST> int
|
||||
normInf_(const T* src, const uchar* mask, ST* _result, int len, int cn)
|
||||
{
|
||||
@@ -1168,15 +1697,9 @@ normInf_(const T* src, const uchar* mask, ST* _result, int len, int cn)
|
||||
{
|
||||
NormInf_SIMD<T, ST> op;
|
||||
result = std::max(result, op(src, len*cn));
|
||||
}
|
||||
else
|
||||
{
|
||||
for( int i = 0; i < len; i++, src += cn )
|
||||
if( mask[i] )
|
||||
{
|
||||
for( int k = 0; k < cn; k++ )
|
||||
result = std::max(result, (ST)cv_abs(src[k]));
|
||||
}
|
||||
} else {
|
||||
MaskedNormInf_SIMD<T, ST> op;
|
||||
result = std::max(result, op(src, mask, len, cn));
|
||||
}
|
||||
*_result = result;
|
||||
return 0;
|
||||
@@ -1190,15 +1713,9 @@ normL1_(const T* src, const uchar* mask, ST* _result, int len, int cn)
|
||||
{
|
||||
NormL1_SIMD<T, ST> op;
|
||||
result += op(src, len*cn);
|
||||
}
|
||||
else
|
||||
{
|
||||
for( int i = 0; i < len; i++, src += cn )
|
||||
if( mask[i] )
|
||||
{
|
||||
for( int k = 0; k < cn; k++ )
|
||||
result += cv_abs(src[k]);
|
||||
}
|
||||
} else {
|
||||
MaskedNormL1_SIMD<T, ST> op;
|
||||
result += op(src, mask, len, cn);
|
||||
}
|
||||
*_result = result;
|
||||
return 0;
|
||||
@@ -1215,15 +1732,8 @@ normL2_(const T* src, const uchar* mask, ST* _result, int len, int cn)
|
||||
}
|
||||
else
|
||||
{
|
||||
for( int i = 0; i < len; i++, src += cn )
|
||||
if( mask[i] )
|
||||
{
|
||||
for( int k = 0; k < cn; k++ )
|
||||
{
|
||||
ST v = (ST)src[k];
|
||||
result += v*v;
|
||||
}
|
||||
}
|
||||
MaskedNormL2_SIMD<T, ST> op;
|
||||
result += op(src, mask, len, cn);
|
||||
}
|
||||
*_result = result;
|
||||
return 0;
|
||||
|
||||
@@ -98,6 +98,50 @@ __kernel void copyToMask(__global const uchar * srcptr, int src_step, int src_of
|
||||
}
|
||||
}
|
||||
|
||||
#elif defined(SET_DIAG)
|
||||
|
||||
#ifdef cl_khr_fp64
|
||||
#pragma OPENCL EXTENSION cl_khr_fp64 : enable
|
||||
#endif
|
||||
|
||||
__kernel void setDiag(__global uchar* dst, int dst_step, int dst_offset,
|
||||
int dst_rows, int dst_cols,
|
||||
__global const uchar* src, int src_step, int src_offset,
|
||||
int len)
|
||||
{
|
||||
int x = get_global_id(0);
|
||||
int y = get_global_id(1);
|
||||
|
||||
if (x < dst_cols && y < dst_rows)
|
||||
{
|
||||
int elem_size = (int)sizeof(T1) * cn;
|
||||
int dst_idx = mad24(y, dst_step, mad24(x, elem_size, dst_offset));
|
||||
|
||||
if (x == y && x < len)
|
||||
{
|
||||
#if IS_ROW_VECTOR
|
||||
int src_idx = src_offset + x * elem_size;
|
||||
#else
|
||||
int src_idx = mad24(x, src_step, src_offset);
|
||||
#endif
|
||||
__global T1* dst_ptr = (__global T1*)(dst + dst_idx);
|
||||
__global const T1* src_ptr = (__global const T1*)(src + src_idx);
|
||||
|
||||
#pragma unroll
|
||||
for (int c = 0; c < cn; ++c)
|
||||
dst_ptr[c] = src_ptr[c];
|
||||
}
|
||||
else
|
||||
{
|
||||
__global T1* dst_ptr = (__global T1*)(dst + dst_idx);
|
||||
|
||||
#pragma unroll
|
||||
for (int c = 0; c < cn; ++c)
|
||||
dst_ptr[c] = (T1)0;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#else
|
||||
|
||||
#ifndef dstST
|
||||
|
||||
@@ -529,7 +529,6 @@ bool FileStorage::Impl::open(const char *filename_or_buf, int _flags, const char
|
||||
bool write_base64 = (write_mode || append) && (_flags & FileStorage::BASE64) != 0;
|
||||
|
||||
bool isGZ = false;
|
||||
size_t fnamelen = 0;
|
||||
|
||||
std::vector<std::string> params;
|
||||
//if ( !mem_mode )
|
||||
@@ -552,18 +551,30 @@ bool FileStorage::Impl::open(const char *filename_or_buf, int _flags, const char
|
||||
flags = _flags;
|
||||
|
||||
if (!mem_mode) {
|
||||
char *dot_pos = strrchr((char *) filename.c_str(), '.');
|
||||
size_t dot_idx = filename.find_last_of('.');
|
||||
char compression = '\0';
|
||||
|
||||
if (dot_pos && dot_pos[1] == 'g' && dot_pos[2] == 'z' &&
|
||||
(dot_pos[3] == '\0' || (cv_isdigit(dot_pos[3]) && dot_pos[4] == '\0'))) {
|
||||
if (append) {
|
||||
CV_Error(cv::Error::StsNotImplemented, "Appending data to compressed file is not implemented");
|
||||
if (dot_idx != std::string::npos)
|
||||
{
|
||||
std::string ext = filename.substr(dot_idx);
|
||||
|
||||
// Instead of `ext.starts_with(".gz")
|
||||
if (ext.size() >= 3 && (ext[1] == 'g') && (ext[2] == 'z') )
|
||||
{
|
||||
if (ext.size() == 3)
|
||||
{
|
||||
// ".gz"
|
||||
isGZ = true;
|
||||
}
|
||||
|
||||
else if(ext.size() == 4 && cv_isdigit(ext[3]))
|
||||
{
|
||||
// ".gz[0-9]"
|
||||
isGZ = true;
|
||||
compression = ext[3];
|
||||
filename.pop_back(); // Replace `gz[0-9]' to 'gz'.
|
||||
}
|
||||
}
|
||||
isGZ = true;
|
||||
compression = dot_pos[3];
|
||||
if (compression)
|
||||
dot_pos[3] = '\0', fnamelen--;
|
||||
}
|
||||
|
||||
if (!isGZ) {
|
||||
@@ -575,6 +586,9 @@ bool FileStorage::Impl::open(const char *filename_or_buf, int _flags, const char
|
||||
}
|
||||
} else {
|
||||
#if USE_ZLIB
|
||||
if (append) {
|
||||
CV_Error(cv::Error::StsNotImplemented, "Appending data to compressed file is not implemented");
|
||||
}
|
||||
char mode[] = {write_mode ? 'w' : 'r', 'b', compression ? compression : '3', '\0'};
|
||||
gzfile = gzopen(filename.c_str(), mode);
|
||||
if (!gzfile)
|
||||
|
||||
@@ -1029,10 +1029,54 @@ UMat UMat::reshape(int new_cn, int new_rows) const
|
||||
return hdr;
|
||||
}
|
||||
|
||||
#ifdef HAVE_OPENCL
|
||||
namespace {
|
||||
static bool ocl_setDiag(const UMat& d, UMat& m, int len)
|
||||
{
|
||||
int cn = d.channels();
|
||||
int depth = d.depth();
|
||||
|
||||
if (depth == CV_64F && !ocl::Device::getDefault().doubleFPConfig())
|
||||
return false;
|
||||
|
||||
String opts = format("-D SET_DIAG -D T1=%s -D cn=%d -D IS_ROW_VECTOR=%d",
|
||||
ocl::memopTypeToStr(depth),
|
||||
cn,
|
||||
(d.rows == 1) ? 1 : 0);
|
||||
|
||||
ocl::Kernel k("setDiag", ocl::core::copyset_oclsrc, opts);
|
||||
if (k.empty())
|
||||
return false;
|
||||
|
||||
k.args(ocl::KernelArg::WriteOnly(m),
|
||||
ocl::KernelArg::ReadOnlyNoSize(d),
|
||||
len);
|
||||
|
||||
size_t globalsize[2] = { (size_t)len, (size_t)len };
|
||||
return k.run(2, globalsize, NULL, false);
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
UMat UMat::diag(const UMat& d, UMatUsageFlags usageFlags)
|
||||
{
|
||||
CV_Assert( d.cols == 1 || d.rows == 1 );
|
||||
CV_INSTRUMENT_REGION();
|
||||
|
||||
CV_Assert(d.cols == 1 || d.rows == 1);
|
||||
int len = d.rows + d.cols - 1;
|
||||
|
||||
#ifdef HAVE_OPENCL
|
||||
if (ocl::useOpenCL())
|
||||
{
|
||||
UMat m(len, len, d.type(), usageFlags);
|
||||
if (ocl_setDiag(d, m, len))
|
||||
{
|
||||
CV_IMPL_ADD(CV_IMPL_OCL);
|
||||
return m;
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
UMat m(len, len, d.type(), Scalar(0), usageFlags);
|
||||
UMat md = m.diag();
|
||||
if( d.cols == 1 )
|
||||
|
||||
@@ -2995,6 +2995,14 @@ TEST(Core_Norm, NORM_L2_8UC4)
|
||||
EXPECT_EQ(kNorm, cv::norm(a, b, NORM_L2));
|
||||
}
|
||||
|
||||
TEST(Core_Norm, NORM_L2SQR_16SC4_large)
|
||||
{
|
||||
const int sizes[] = {1, 116, 40};
|
||||
Mat src(3, sizes, CV_16SC4, Scalar::all(16384));
|
||||
const double expected = static_cast<double>(src.total()) * src.channels() * 16384.0 * 16384.0;
|
||||
EXPECT_EQ(expected, cv::norm(src, NORM_L2SQR));
|
||||
}
|
||||
|
||||
TEST(Core_ConvertTo, regression_12121)
|
||||
{
|
||||
{
|
||||
|
||||
@@ -680,13 +680,26 @@ public:
|
||||
protected:
|
||||
void run_func();
|
||||
void prepare_to_validation( int test_case_idx );
|
||||
double get_success_error_level( int test_case_idx, int i, int j );
|
||||
};
|
||||
|
||||
|
||||
CxCore_DFTTest::CxCore_DFTTest() : CxCore_DXTBaseTest( true, true, false )
|
||||
{
|
||||
}
|
||||
|
||||
double CxCore_DFTTest::get_success_error_level( int test_case_idx, int i, int j )
|
||||
{
|
||||
CV_Assert(i == OUTPUT);
|
||||
CV_Assert(j == 0);
|
||||
|
||||
int depth = test_mat[i][j].depth();
|
||||
|
||||
// NOTE: non-default threshold intorduced for ARMPL integration
|
||||
if (depth == CV_32F)
|
||||
return 1.5e-4;
|
||||
|
||||
return CxCore_DXTBaseTest::get_success_error_level(test_case_idx, i, j);
|
||||
}
|
||||
|
||||
void CxCore_DFTTest::run_func()
|
||||
{
|
||||
@@ -745,6 +758,9 @@ public:
|
||||
protected:
|
||||
void run_func();
|
||||
void prepare_to_validation( int test_case_idx );
|
||||
#if defined(HAVE_ARMPL)
|
||||
double get_success_error_level( int test_case_idx, int i, int j ) CV_OVERRIDE;
|
||||
#endif
|
||||
};
|
||||
|
||||
|
||||
@@ -752,6 +768,20 @@ CxCore_DCTTest::CxCore_DCTTest() : CxCore_DXTBaseTest( false, false, false )
|
||||
{
|
||||
}
|
||||
|
||||
#if defined(HAVE_ARMPL)
|
||||
double CxCore_DCTTest::get_success_error_level(int, int i, int j)
|
||||
{
|
||||
CV_Assert(i == OUTPUT);
|
||||
CV_Assert(j == 0);
|
||||
|
||||
int depth = test_mat[i][j].depth();
|
||||
|
||||
if (depth == CV_32F)
|
||||
return 1.67e-5;
|
||||
|
||||
return 1e-12;
|
||||
}
|
||||
#endif
|
||||
|
||||
void CxCore_DCTTest::run_func()
|
||||
{
|
||||
|
||||
@@ -292,17 +292,17 @@ template<typename R> struct TheTest
|
||||
}
|
||||
|
||||
// reinterpret_as
|
||||
v_uint8 vu8 = v_reinterpret_as_u8(r1); out.a.clear(); v_store((uchar*)out.a.d, vu8); EXPECT_EQ(data.a, out.a);
|
||||
v_int8 vs8 = v_reinterpret_as_s8(r1); out.a.clear(); v_store((schar*)out.a.d, vs8); EXPECT_EQ(data.a, out.a);
|
||||
v_uint16 vu16 = v_reinterpret_as_u16(r1); out.a.clear(); v_store((ushort*)out.a.d, vu16); EXPECT_EQ(data.a, out.a);
|
||||
v_int16 vs16 = v_reinterpret_as_s16(r1); out.a.clear(); v_store((short*)out.a.d, vs16); EXPECT_EQ(data.a, out.a);
|
||||
v_uint32 vu32 = v_reinterpret_as_u32(r1); out.a.clear(); v_store((unsigned*)out.a.d, vu32); EXPECT_EQ(data.a, out.a);
|
||||
v_int32 vs32 = v_reinterpret_as_s32(r1); out.a.clear(); v_store((int*)out.a.d, vs32); EXPECT_EQ(data.a, out.a);
|
||||
v_uint64 vu64 = v_reinterpret_as_u64(r1); out.a.clear(); v_store((uint64*)out.a.d, vu64); EXPECT_EQ(data.a, out.a);
|
||||
v_int64 vs64 = v_reinterpret_as_s64(r1); out.a.clear(); v_store((int64*)out.a.d, vs64); EXPECT_EQ(data.a, out.a);
|
||||
v_float32 vf32 = v_reinterpret_as_f32(r1); out.a.clear(); v_store((float*)out.a.d, vf32); EXPECT_EQ(data.a, out.a);
|
||||
AlignedData<R> out_u8; v_uint8 vu8 = v_reinterpret_as_u8(r1); v_store((uchar*)out_u8.a.d, vu8); EXPECT_EQ(data.a, out_u8.a);
|
||||
AlignedData<R> out_s8; v_int8 vs8 = v_reinterpret_as_s8(r1); v_store((schar*)out_s8.a.d, vs8); EXPECT_EQ(data.a, out_s8.a);
|
||||
AlignedData<R> out_u16; v_uint16 vu16 = v_reinterpret_as_u16(r1); v_store((ushort*)out_u16.a.d, vu16); EXPECT_EQ(data.a, out_u16.a);
|
||||
AlignedData<R> out_s16; v_int16 vs16 = v_reinterpret_as_s16(r1); v_store((short*)out_s16.a.d, vs16); EXPECT_EQ(data.a, out_s16.a);
|
||||
AlignedData<R> out_u32; v_uint32 vu32 = v_reinterpret_as_u32(r1); v_store((unsigned*)out_u32.a.d, vu32); EXPECT_EQ(data.a, out_u32.a);
|
||||
AlignedData<R> out_s32; v_int32 vs32 = v_reinterpret_as_s32(r1); v_store((int*)out_s32.a.d, vs32); EXPECT_EQ(data.a, out_s32.a);
|
||||
AlignedData<R> out_u64; v_uint64 vu64 = v_reinterpret_as_u64(r1); v_store((uint64*)out_u64.a.d, vu64); EXPECT_EQ(data.a, out_u64.a);
|
||||
AlignedData<R> out_s64; v_int64 vs64 = v_reinterpret_as_s64(r1); v_store((int64*)out_s64.a.d, vs64); EXPECT_EQ(data.a, out_s64.a);
|
||||
AlignedData<R> out_f32; v_float32 vf32 = v_reinterpret_as_f32(r1); v_store((float*)out_f32.a.d, vf32); EXPECT_EQ(data.a, out_f32.a);
|
||||
#if (CV_SIMD_64F || CV_SIMD_SCALABLE_64F)
|
||||
v_float64 vf64 = v_reinterpret_as_f64(r1); out.a.clear(); v_store((double*)out.a.d, vf64); EXPECT_EQ(data.a, out.a);
|
||||
AlignedData<R> out_f64; v_float64 vf64 = v_reinterpret_as_f64(r1); v_store((double*)out_f64.a.d, vf64); EXPECT_EQ(data.a, out_f64.a);
|
||||
#endif
|
||||
|
||||
#if CV_SIMD_WIDTH == 16
|
||||
|
||||
@@ -2218,9 +2218,19 @@ T fsWriteRead(const T& expectedValue, const char* ext)
|
||||
fs_w.release();
|
||||
|
||||
FileStorage fs_r(fname, FileStorage::READ);
|
||||
|
||||
T value;
|
||||
fs_r["value"] >> value;
|
||||
fs_r.release();
|
||||
|
||||
// If ext is `.gz[0-9]`, fname on storage will end with `.gz`.
|
||||
// FileStorage::Impl::open() truncates the last digit internally.
|
||||
if (isdigit(fname.back()))
|
||||
{
|
||||
fname.pop_back();
|
||||
}
|
||||
|
||||
remove(fname.c_str());
|
||||
|
||||
return value;
|
||||
}
|
||||
|
||||
@@ -2275,7 +2285,7 @@ TEST_P(FileStorage_exact_type, long_int_mat)
|
||||
}
|
||||
|
||||
INSTANTIATE_TEST_CASE_P(Core_InputOutput,
|
||||
FileStorage_exact_type, Values(".yml", ".xml", ".json")
|
||||
FileStorage_exact_type, Values(".yml", ".xml", ".json", ".xml.gz", ".xml.gz0", ".xml.gz9")
|
||||
);
|
||||
|
||||
TEST(Core_InputOutput, YAML_Compatibility)
|
||||
|
||||
@@ -1370,6 +1370,8 @@ TEST(Core_Matx, from_initializer_list)
|
||||
Mat_<double> a = (Mat_<double>(2,2) << 10, 11, 12, 13);
|
||||
Matx22d b = {10, 11, 12, 13};
|
||||
ASSERT_EQ( cvtest::norm(a, b, NORM_INF), 0.);
|
||||
Mat_<double> c({2, 2}, {10, 11, 12, 13});
|
||||
ASSERT_EQ( cvtest::norm(c, b, NORM_INF), 0.);
|
||||
}
|
||||
|
||||
TEST(Core_Mat, regression_9507)
|
||||
@@ -1825,6 +1827,11 @@ TEST(Mat, from_initializer_list)
|
||||
auto D = Mat_<double>({2, 3}, {1, 2, 3, 4, 5, 6});
|
||||
EXPECT_EQ(2, D.rows);
|
||||
EXPECT_EQ(3, D.cols);
|
||||
|
||||
double angle = 30, a = cos(angle*CV_PI/180), b = sin(angle*CV_PI/180);
|
||||
Mat R({2, 2}, {a, -b, b, a});
|
||||
ASSERT_EQ(CV_64FC1, R.type());
|
||||
ASSERT_EQ(cv::Size(2, 2), R.size());
|
||||
}
|
||||
|
||||
TEST(Mat_, from_initializer_list)
|
||||
@@ -1837,6 +1844,11 @@ TEST(Mat_, from_initializer_list)
|
||||
ASSERT_DOUBLE_EQ(cvtest::norm(A, B, NORM_INF), 0.);
|
||||
ASSERT_DOUBLE_EQ(cvtest::norm(A, C, NORM_INF), 0.);
|
||||
ASSERT_DOUBLE_EQ(cvtest::norm(B, C, NORM_INF), 0.);
|
||||
|
||||
double angle = 30, a = cos(angle*CV_PI/180), b = sin(angle*CV_PI/180);
|
||||
Mat_<double> R({2, 2}, {a, -b, b, a});
|
||||
ASSERT_EQ(CV_64FC1, R.type());
|
||||
ASSERT_EQ(cv::Size(2, 2), R.size());
|
||||
}
|
||||
|
||||
|
||||
|
||||
@@ -3617,5 +3617,177 @@ TEST(Core_BFloat, convert)
|
||||
#endif
|
||||
}
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////
|
||||
// See https://github.com/opencv/opencv/issues/28930
|
||||
typedef testing::TestWithParam<std::tuple<int,int,int,int, int>> Core_Point_DotProduct_regression28930;
|
||||
|
||||
TEST_P(Core_Point_DotProduct_regression28930, basic)
|
||||
{
|
||||
const int x1 = std::get<0>(GetParam());
|
||||
const int y1 = std::get<1>(GetParam());
|
||||
const int x2 = std::get<2>(GetParam());
|
||||
const int y2 = std::get<3>(GetParam());
|
||||
const int expect = std::get<4>(GetParam());
|
||||
|
||||
cv::Point pt1(x1, y1);
|
||||
cv::Point pt2(x2, y2);
|
||||
|
||||
EXPECT_EQ(pt1.dot(pt2), expect) << "Failed for: (" << x1 << "," << y1 << ") dot (" << x2 << "," << y2 << ")";
|
||||
}
|
||||
|
||||
INSTANTIATE_TEST_CASE_P(/* */, Core_Point_DotProduct_regression28930,
|
||||
testing::Values(
|
||||
// 1. INT_MIN*INT_MIN + INT_MIN*INT_MIN = 2^62 + 2^62 => Saturates to MAX
|
||||
std::make_tuple( INT_MIN, INT_MIN,
|
||||
INT_MIN, INT_MIN,
|
||||
INT_MAX),
|
||||
|
||||
// 2. INT_MIN*INT_MAX + INT_MAX*INT_MAX = 4611686014132420609 + -4611686016279904256 = -2147483647
|
||||
std::make_tuple( INT_MAX, INT_MIN,
|
||||
INT_MAX, INT_MAX,
|
||||
-2147483647),
|
||||
|
||||
// 3. INT_MAX*INT_MAX + INT_MAX*INT_MIN = 4611686014132420609 + -4611686016279904256 = -2147483647
|
||||
std::make_tuple( INT_MAX, INT_MAX,
|
||||
INT_MAX, INT_MIN,
|
||||
-2147483647),
|
||||
|
||||
// 4. 46340^2 = 2147395600 (Under INT_MAX)
|
||||
std::make_tuple( 46340, 0,
|
||||
46340, 0,
|
||||
2147395600),
|
||||
|
||||
// 5. 46341^2 = 2147488281 (Over INT_MAX) => Saturates to MAX
|
||||
std::make_tuple( 46341, 0,
|
||||
46341, 0,
|
||||
INT_MAX),
|
||||
|
||||
// 6. -46340 * 46340 = -2147395600 (Over INT_MIN)
|
||||
std::make_tuple( -46340, 0,
|
||||
46340, 0,
|
||||
-2147395600),
|
||||
|
||||
// 7. -46341 * 46341 = -2147488281 (Under INT_MIN) => Saturates to MIN
|
||||
std::make_tuple( -46341, 0,
|
||||
46341, 0,
|
||||
INT_MIN),
|
||||
|
||||
// 8. Zero
|
||||
std::make_tuple( 0, 0,
|
||||
0, 0,
|
||||
0),
|
||||
|
||||
// 9. Simple max
|
||||
std::make_tuple( INT_MAX, 0,
|
||||
1, 0,
|
||||
INT_MAX)
|
||||
));
|
||||
|
||||
typedef testing::TestWithParam<std::tuple<int,int,int,int,int,int, int>> Core_Point3i_DotProduct_regression28930;
|
||||
|
||||
TEST_P(Core_Point3i_DotProduct_regression28930, basic)
|
||||
{
|
||||
const int x1 = std::get<0>(GetParam());
|
||||
const int y1 = std::get<1>(GetParam());
|
||||
const int z1 = std::get<2>(GetParam());
|
||||
const int x2 = std::get<3>(GetParam());
|
||||
const int y2 = std::get<4>(GetParam());
|
||||
const int z2 = std::get<5>(GetParam());
|
||||
const int expect = std::get<6>(GetParam());
|
||||
|
||||
cv::Point3i pt1(x1, y1, z1);
|
||||
cv::Point3i pt2(x2, y2, z2);
|
||||
|
||||
EXPECT_EQ(pt1.dot(pt2), expect) << "Failed for: (" << x1 << "," << y1 << "," << z1 << ") dot (" << x2 << "," << y2 << "," << z2 << ")";
|
||||
}
|
||||
|
||||
INSTANTIATE_TEST_CASE_P(/* */, Core_Point3i_DotProduct_regression28930,
|
||||
testing::Values(
|
||||
// 1. INT_MIN*INT_MIN + INT_MIN*INT_MIN = 2^62 + 2^62 => Saturates to MAX
|
||||
std::make_tuple( INT_MIN, INT_MIN, 0,
|
||||
INT_MIN, INT_MIN, 0,
|
||||
INT_MAX),
|
||||
|
||||
// 2. INT_MIN*INT_MAX + INT_MAX*INT_MAX = 4611686014132420609 + -4611686016279904256 = -2147483647
|
||||
std::make_tuple( INT_MAX, INT_MIN, 0,
|
||||
INT_MAX, INT_MAX, 0,
|
||||
-2147483647),
|
||||
|
||||
// 3. INT_MAX*INT_MAX + INT_MAX*INT_MIN = 4611686014132420609 + -4611686016279904256 = -2147483647
|
||||
std::make_tuple( INT_MAX, INT_MAX, 0,
|
||||
INT_MAX, INT_MIN, 0,
|
||||
-2147483647),
|
||||
|
||||
// 4. 46340^2 = 2147395600 (Under INT_MAX)
|
||||
std::make_tuple( 46340, 0, 0,
|
||||
46340, 0, 0,
|
||||
2147395600),
|
||||
|
||||
// 5. 46341^2 = 2147488281 (Over INT_MAX) => Saturates to MAX
|
||||
std::make_tuple( 46341, 0, 0,
|
||||
46341, 0, 0,
|
||||
INT_MAX),
|
||||
|
||||
// 6. -46340 * 46340 = -2147395600 (Over INT_MIN)
|
||||
std::make_tuple( -46340, 0, 0,
|
||||
46340, 0, 0,
|
||||
-2147395600),
|
||||
|
||||
// 7. -46341 * 46341 = -2147488281 (Under INT_MIN) => Saturates to MIN
|
||||
std::make_tuple( -46341, 0, 0,
|
||||
46341, 0, 0,
|
||||
INT_MIN),
|
||||
|
||||
// 8. Zero
|
||||
std::make_tuple( 0, 0, 0,
|
||||
0, 0, 0,
|
||||
0),
|
||||
|
||||
// 9. Simple max
|
||||
std::make_tuple( INT_MAX, 0, 0,
|
||||
1, 0, 0,
|
||||
INT_MAX),
|
||||
|
||||
// 10. All positive, no overflow
|
||||
std::make_tuple( 1, 2, 3,
|
||||
4, 5, 6,
|
||||
(1*4 + 2*5 + 3*6)),
|
||||
|
||||
// 11. All negative, no overflow
|
||||
std::make_tuple( -1, -2, -3,
|
||||
-4, -5, -6,
|
||||
(-1*-4 + -2*-5 + -3*-6)),
|
||||
|
||||
// 12. Three large positive products => saturate to MAX
|
||||
std::make_tuple( INT_MAX, INT_MAX, INT_MAX,
|
||||
INT_MAX, INT_MAX, INT_MAX,
|
||||
INT_MAX),
|
||||
|
||||
// 13. Three large positive products from INT_MIN*INT_MIN => saturate to MAX
|
||||
std::make_tuple( INT_MIN, INT_MIN, INT_MIN,
|
||||
INT_MIN, INT_MIN, INT_MIN,
|
||||
INT_MAX),
|
||||
|
||||
// 14. Three large negative products => saturate to MIN
|
||||
std::make_tuple( INT_MIN, INT_MIN, INT_MIN,
|
||||
INT_MAX, INT_MAX, INT_MAX,
|
||||
INT_MIN),
|
||||
|
||||
// 15. Mixed signs: + + -
|
||||
std::make_tuple( INT_MAX, INT_MAX, INT_MIN,
|
||||
INT_MAX, INT_MAX, INT_MAX,
|
||||
INT_MAX),
|
||||
|
||||
// 16. Mixed signs: + - +
|
||||
std::make_tuple( INT_MAX, INT_MIN, INT_MAX,
|
||||
INT_MAX, INT_MAX, INT_MAX,
|
||||
INT_MAX),
|
||||
|
||||
// 17. Mixed signs: - + -
|
||||
std::make_tuple( INT_MIN, INT_MAX, INT_MIN,
|
||||
INT_MAX, INT_MAX, INT_MAX,
|
||||
INT_MIN)
|
||||
));
|
||||
|
||||
}} // namespace
|
||||
/* End of file. */
|
||||
|
||||
@@ -302,9 +302,16 @@ public:
|
||||
ov::Shape(pads_begin), ov::Shape(pads_end), ov::Shape(kernel_size),
|
||||
rounding_type, pad_type);
|
||||
} else if (type == AVE) {
|
||||
#if defined(INF_ENGINE_RELEASE) && INF_ENGINE_VER_MAJOR_GE(2025030000)
|
||||
std::vector<size_t> dilations(kernel_size.size(), 1);
|
||||
pool = std::make_shared<ov::op::v16::AvgPool>(input, ov::Strides(strides), ov::Strides(dilations),
|
||||
ov::Shape(pads_begin), ov::Shape(pads_end), ov::Shape(kernel_size),
|
||||
!avePoolPaddedArea, rounding_type, pad_type);
|
||||
#else
|
||||
pool = std::make_shared<ov::op::v1::AvgPool>(input, ov::Strides(strides),
|
||||
ov::Shape(pads_begin), ov::Shape(pads_end), ov::Shape(kernel_size),
|
||||
!avePoolPaddedArea, rounding_type, pad_type);
|
||||
#endif
|
||||
} else if (type == SUM) {
|
||||
ov::Shape inpShape = input.get_shape();
|
||||
CV_Assert(inpShape.size() == 2 + kernel_size.size());
|
||||
|
||||
@@ -1510,9 +1510,15 @@ void runFastConv(InputArray _input, OutputArray _output, const Ptr<FastConv>& co
|
||||
|
||||
char *wptr = weights + (k0_block * DkHkWkCg + c0 * CONV_MR) * esz;
|
||||
float *cptr = cbuf_task + stripe * CONV_NR;
|
||||
#ifdef CONV_ARM_FP16
|
||||
hfloat* cptr_f16 = (hfloat*)cbuf_task + stripe*CONV_NR;
|
||||
#endif // CONV_ARM_FP16
|
||||
for (int k = k0_block; k < k1_block; k += CONV_MR,
|
||||
wptr += DkHkWkCg * CONV_MR * esz, cptr += CONV_MR * ldc, cptr_f16 += CONV_MR * ldc)
|
||||
wptr += DkHkWkCg * CONV_MR * esz,
|
||||
#ifdef CONV_ARM_FP16
|
||||
cptr_f16 += CONV_MR * ldc,
|
||||
#endif // CONV_ARM_FP16
|
||||
cptr += CONV_MR * ldc)
|
||||
{
|
||||
#if CV_TRY_AVX2
|
||||
if (conv->useAVX2)
|
||||
@@ -1546,12 +1552,18 @@ void runFastConv(InputArray _input, OutputArray _output, const Ptr<FastConv>& co
|
||||
|
||||
size_t outofs = ((n * ngroups + g) * Kg + k0_block) * out_planesize + zyx0;
|
||||
const float *cptr = cbuf_task;
|
||||
#ifdef CONV_ARM_FP16
|
||||
const hfloat *cptr_fp16 = (const hfloat *)cbuf_task;
|
||||
#endif // CONV_ARM_FP16
|
||||
float *outptr = out + outofs;
|
||||
const float *pbptr = fusedAddPtr0 ? fusedAddPtr0 + outofs : 0;
|
||||
|
||||
for (int k = k0_block; k < k1_block; k++,
|
||||
cptr += ldc, cptr_fp16 += ldc, outptr += out_planesize,
|
||||
cptr += ldc,
|
||||
#ifdef CONV_ARM_FP16
|
||||
cptr_fp16 += ldc,
|
||||
#endif // CONV_ARM_FP16
|
||||
outptr += out_planesize,
|
||||
pbptr += (pbptr ? out_planesize : 0))
|
||||
{
|
||||
float biasval = biasptr[k];
|
||||
|
||||
@@ -212,7 +212,10 @@ TEST_P(Test_Int8_layers, AvePooling)
|
||||
if (backend != DNN_BACKEND_INFERENCE_ENGINE_NGRAPH)
|
||||
testLayer("layer_pooling_ave", "Caffe", 0.0021, 0.0075);
|
||||
testLayer("ave_pool_same", "TensorFlow", 0.00153, 0.0041);
|
||||
testLayer("average_pooling_1d", "ONNX", 0.002, 0.0048);
|
||||
#if defined(INF_ENGINE_RELEASE) && INF_ENGINE_VER_MAJOR_LT(2025030000)
|
||||
if (backend != DNN_BACKEND_INFERENCE_ENGINE_NGRAPH)
|
||||
#endif
|
||||
testLayer("average_pooling_1d", "ONNX", 0.002, 0.0048);
|
||||
if (backend != DNN_BACKEND_INFERENCE_ENGINE_NGRAPH)
|
||||
testLayer("average_pooling", "ONNX", 0.0014, 0.0032);
|
||||
testLayer("average_pooling_dynamic_axes", "ONNX", 0.0014, 0.006);
|
||||
|
||||
@@ -230,7 +230,7 @@ static void filterEllipticKeyPointsByImageSize( std::vector<EllipticKeyPoint>& k
|
||||
std::vector<EllipticKeyPoint> filtered;
|
||||
filtered.reserve(keypoints.size());
|
||||
std::vector<EllipticKeyPoint>::const_iterator it = keypoints.begin();
|
||||
for( int i = 0; it != keypoints.end(); ++it, i++ )
|
||||
for( ; it != keypoints.end(); ++it )
|
||||
{
|
||||
if( it->center.x + it->boundingBox.width < imgSize.width &&
|
||||
it->center.x - it->boundingBox.width > 0 &&
|
||||
|
||||
@@ -663,33 +663,51 @@ int Index::radiusSearch(InputArray _query, OutputArray _indices,
|
||||
if( algo == FLANN_INDEX_LSH )
|
||||
CV_Error( Error::StsNotImplemented, "LSH index does not support radiusSearch operation" );
|
||||
|
||||
int rsearch_ret;
|
||||
switch( distType )
|
||||
{
|
||||
case FLANN_DIST_HAMMING:
|
||||
return runRadiusSearch< HammingDistance >(index, query, indices, dists, radius, params);
|
||||
|
||||
rsearch_ret = runRadiusSearch< HammingDistance >(index, query, indices, dists, radius, params);
|
||||
break;
|
||||
case FLANN_DIST_L2:
|
||||
return runRadiusSearch< ::cvflann::L2<float> >(index, query, indices, dists, radius, params);
|
||||
rsearch_ret = runRadiusSearch< ::cvflann::L2<float> >(index, query, indices, dists, radius, params);
|
||||
break;
|
||||
case FLANN_DIST_L1:
|
||||
return runRadiusSearch< ::cvflann::L1<float> >(index, query, indices, dists, radius, params);
|
||||
rsearch_ret = runRadiusSearch< ::cvflann::L1<float> >(index, query, indices, dists, radius, params);
|
||||
break;
|
||||
#if MINIFLANN_SUPPORT_EXOTIC_DISTANCE_TYPES
|
||||
case FLANN_DIST_DNAMMING:
|
||||
return runRadiusSearch< DNAmmingDistance >(index, query, indices, dists, radius, params);
|
||||
rsearch_ret = runRadiusSearch< DNAmmingDistance >(index, query, indices, dists, radius, params);
|
||||
break;
|
||||
case FLANN_DIST_MAX:
|
||||
return runRadiusSearch< ::cvflann::MaxDistance<float> >(index, query, indices, dists, radius, params);
|
||||
rsearch_ret = runRadiusSearch< ::cvflann::MaxDistance<float> >(index, query, indices, dists, radius, params);
|
||||
break;
|
||||
case FLANN_DIST_HIST_INTERSECT:
|
||||
return runRadiusSearch< ::cvflann::HistIntersectionDistance<float> >(index, query, indices, dists, radius, params);
|
||||
rsearch_ret = runRadiusSearch< ::cvflann::HistIntersectionDistance<float> >(index, query, indices, dists, radius, params);
|
||||
break;
|
||||
case FLANN_DIST_HELLINGER:
|
||||
return runRadiusSearch< ::cvflann::HellingerDistance<float> >(index, query, indices, dists, radius, params);
|
||||
rsearch_ret = runRadiusSearch< ::cvflann::HellingerDistance<float> >(index, query, indices, dists, radius, params);
|
||||
break;
|
||||
case FLANN_DIST_CHI_SQUARE:
|
||||
return runRadiusSearch< ::cvflann::ChiSquareDistance<float> >(index, query, indices, dists, radius, params);
|
||||
rsearch_ret = runRadiusSearch< ::cvflann::ChiSquareDistance<float> >(index, query, indices, dists, radius, params);
|
||||
break;
|
||||
case FLANN_DIST_KL:
|
||||
return runRadiusSearch< ::cvflann::KL_Divergence<float> >(index, query, indices, dists, radius, params);
|
||||
rsearch_ret = runRadiusSearch< ::cvflann::KL_Divergence<float> >(index, query, indices, dists, radius, params);
|
||||
break;
|
||||
#endif
|
||||
default:
|
||||
CV_Error(Error::StsBadArg, "Unknown/unsupported distance type");
|
||||
return -1;
|
||||
}
|
||||
return -1;
|
||||
if (rsearch_ret > maxResults)
|
||||
rsearch_ret = maxResults;
|
||||
if (rsearch_ret < maxResults) {
|
||||
if (_indices.needed())
|
||||
indices.colRange(0, rsearch_ret).copyTo(_indices);
|
||||
if (_dists.needed())
|
||||
dists.colRange(0, rsearch_ret).copyTo(_dists);
|
||||
}
|
||||
return rsearch_ret;
|
||||
}
|
||||
|
||||
flann_distance_t Index::getDistance() const
|
||||
|
||||
@@ -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 {
|
||||
|
||||
// Regression test: radiusSearch returned nn < maxResults but left the output
|
||||
// vectors sized to maxResults, so nn != indices.size() and nn > maxResults
|
||||
// could both occur for queries with fewer actual neighbors than maxResults.
|
||||
TEST(Flann_Index, radiusSearch_output_size_matches_returned_count)
|
||||
{
|
||||
std::vector<cv::Point2f> corners = {
|
||||
{3679.83f,1857.22f},{3324.43f,1850.67f},{3278.83f,1502.32f},{3621.32f,1508.69f},
|
||||
{3662.26f,1837.97f},{3336.06f,1839.28f},{3291.74f,1516.03f},{3608.94f,1518.72f},
|
||||
{2980.66f,1843.22f},{2628.11f,1837.14f},{2607.73f,1491.57f},{2948.22f,1496.76f},
|
||||
{2289.31f,1830.19f},{1943.99f,1824.17f},{1945.51f,1482.34f},{2280.68f,1487.39f},
|
||||
{1607.47f,1819.11f},{1260.04f,1813.26f},{1283.94f,1470.34f},{1620.03f,1475.85f},
|
||||
{923.063f,1808.08f},{576.193f,1802.4f},{624.713f,1459.78f},{959.715f,1464.93f},
|
||||
{3270.25f,1494.98f},{2952.95f,1492.19f},{2922.75f,1190.02f},{3231.05f,1194.81f},
|
||||
{3284.82f,1507.62f},{2942.74f,1502.59f},{2912.32f,1178.33f},{3242.98f,1183.19f},
|
||||
{2612.82f,1496.37f},{2276.44f,1491.63f},{2267.61f,1170.13f},{2593.64f,1174.43f},
|
||||
{1949.73f,1486.61f},{1614.92f,1480.64f},{1626.77f,1160.34f},{1952.0f,1165.51f},
|
||||
{1289.38f,1476.21f},{953.709f,1470.21f},{986.779f,1148.92f},{1311.98f,1154.89f},
|
||||
{3567.28f,1195.1f},{3237.51f,1189.03f},{3197.63f,884.15f},{3518.61f,888.76f},
|
||||
{2918.31f,1183.62f},{2588.69f,1179.37f},{2570.64f,875.672f},{2889.87f,878.902f},
|
||||
{2271.97f,1174.25f},{1947.62f,1169.61f},{1948.56f,868.197f},{2263.8f,872.533f},
|
||||
{1630.99f,1164.6f},{1306.74f,1159.52f},{1327.81f,858.358f},{1642.69f,862.765f},
|
||||
{992.896f,1155.52f},{666.107f,1149.96f},{705.246f,845.776f},{1023.25f,851.582f},
|
||||
{3204.48f,889.981f},{2883.49f,885.252f},{2859.31f,593.752f},{3175.43f,594.736f},
|
||||
{2575.86f,880.331f},{2259.44f,876.647f},{2252.33f,589.235f},{2560.47f,592.332f},
|
||||
{1954.36f,873.708f},{1636.7f,868.068f},{1646.93f,580.76f},{1956.14f,584.797f},
|
||||
{1332.02f,862.624f},{1017.11f,856.71f},{1045.32f,572.178f},{1350.95f,577.635f},
|
||||
{3481.36f,605.324f},{3169.05f,601.083f},{3138.84f,324.116f},{3446.12f,325.545f},
|
||||
{2866.14f,599.61f},{2555.38f,597.142f},{2541.51f,320.931f},{2845.5f,322.419f},
|
||||
{2256.79f,593.253f},{1950.17f,590.12f},{1951.8f,317.445f},{2250.88f,319.418f},
|
||||
{1654.16f,587.663f},{1344.83f,582.787f},{1362.8f,309.253f},{1663.89f,313.056f},
|
||||
{1050.94f,577.867f},{741.476f,571.648f},{776.433f,300.444f},{1076.97f,304.56f},
|
||||
{3327.05f,1847.65f},{2977.74f,1840.48f},{2945.48f,1499.68f},{3281.82f,1504.97f},
|
||||
{2630.85f,1834.22f},{2287.2f,1828.06f},{2278.56f,1489.51f},{2610.64f,1494.31f},
|
||||
{1946.11f,1822.05f},{1604.75f,1816.18f},{1617.11f,1478.59f},{1947.62f,1484.48f},
|
||||
{1262.96f,1810.53f},{920.46f,1805.05f},{956.712f,1467.57f},{1286.66f,1473.27f},
|
||||
{3618.7f,1511.71f},{3281.82f,1504.97f},{3240.24f,1186.11f},{3564.22f,1192.53f},
|
||||
{2945.48f,1499.68f},{2610.64f,1494.31f},{2591.52f,1176.55f},{2915.32f,1180.97f},
|
||||
{2278.56f,1489.51f},{1947.62f,1484.48f},{1949.81f,1167.56f},{2269.79f,1172.18f},
|
||||
{1617.11f,1478.59f},{1286.66f,1473.27f},{1308.98f,1157.53f},{1628.88f,1162.47f},
|
||||
{956.712f,1467.57f},{627.315f,1462.81f},{669.943f,1146.75f},{989.494f,1151.86f},
|
||||
{3240.25f,1186.11f},{2915.32f,1180.98f},{2887.03f,881.727f},{3200.69f,886.725f},
|
||||
{2591.52f,1176.55f},{2269.79f,1172.19f},{2261.62f,874.587f},{2573.63f,878.324f},
|
||||
{1949.81f,1167.55f},{1628.88f,1162.47f},{1640.44f,864.751f},{1950.74f,870.259f},
|
||||
{1308.98f,1157.53f},{989.494f,1151.86f},{1019.41f,854.787f},{1329.92f,860.49f},
|
||||
{3515.88f,891.68f},{3200.69f,886.725f},{3171.89f,598.263f},{3478.26f,602.785f},
|
||||
{2887.04f,881.725f},{2573.63f,878.324f},{2558.28f,594.392f},{2863.1f,597.005f},
|
||||
{2261.62f,874.587f},{1950.74f,870.259f},{1952.4f,588.114f},{2254.56f,591.241f},
|
||||
{1640.44f,864.751f},{1329.92f,860.49f},{1348.65f,579.559f},{1650.55f,584.209f},
|
||||
{1019.41f,854.787f},{708.649f,849.44f},{745.388f,568.534f},{1047.42f,574.313f},
|
||||
{3171.89f,598.263f},{2863.1f,597.005f},{2842.6f,325.169f},{3141.93f,326.654f},
|
||||
{2558.28f,594.392f},{2254.56f,591.241f},{2248.65f,321.425f},{2544.55f,323.537f},
|
||||
{1952.4f,588.113f},{1650.55f,584.209f},{1660.07f,316.284f},{1954.02f,319.457f},
|
||||
{1348.65f,579.559f},{1047.43f,574.312f},{1073.06f,307.674f},{1366.42f,312.707f}
|
||||
};
|
||||
|
||||
cv::flann::KDTreeIndexParams indexParams(1);
|
||||
cv::Mat data = cv::Mat(corners).reshape(1, static_cast<int>(corners.size()));
|
||||
cv::flann::Index index(data, indexParams);
|
||||
|
||||
const int maxResults = 4;
|
||||
|
||||
for (int i = 0; i < (int)corners.size(); i++)
|
||||
{
|
||||
SCOPED_TRACE(cv::format("Data row: %d", i));
|
||||
std::vector<int> indices(maxResults);
|
||||
std::vector<float> dists(maxResults);
|
||||
int nn = index.radiusSearch(data.row(i), indices, dists, 100, maxResults);
|
||||
EXPECT_EQ(nn, (int)indices.size());
|
||||
EXPECT_LE(nn, maxResults);
|
||||
}
|
||||
}
|
||||
|
||||
}} // namespace
|
||||
@@ -3960,9 +3960,14 @@ CV_EXPORTS_W int connectedComponentsWithStats(InputArray image, OutputArray labe
|
||||
|
||||
/** @brief Finds contours in a binary image.
|
||||
|
||||
The function retrieves contours from the binary image using the algorithm @cite Suzuki85 . The contours
|
||||
The function retrieves contours from the binary image. The contours
|
||||
are a useful tool for shape analysis and object detection and recognition. See squares.cpp in the
|
||||
OpenCV sample directory.
|
||||
|
||||
@note Since OpenCV 4.14, when mode is #RETR_LIST and no hierarchy is requested, this function
|
||||
automatically uses the TRUCO parallel algorithm @cite TRUCO2026, a scalable lock-free method for
|
||||
contour extraction. In all other cases, the sequential @cite Suzuki85 algorithm is used.
|
||||
|
||||
@note Since opencv 3.2 source image is not modified by this function.
|
||||
|
||||
@param image Source, an 8-bit single-channel image. Non-zero pixels are treated as 1's. Zero
|
||||
@@ -3992,6 +3997,7 @@ CV_EXPORTS_W void findContours( InputArray image, OutputArrayOfArrays contours,
|
||||
CV_EXPORTS void findContours( InputArray image, OutputArrayOfArrays contours,
|
||||
int mode, int method, Point offset = Point());
|
||||
|
||||
|
||||
//! @brief Find contours using link runs algorithm
|
||||
//!
|
||||
//! This function implements an algorithm different from cv::findContours:
|
||||
|
||||
@@ -141,4 +141,78 @@ PERF_TEST_P(TestMinEnclosingCircleWorstCase, minEnclosingCircle_sequential,
|
||||
SANITY_CHECK_NOTHING();
|
||||
}
|
||||
|
||||
// ============================================================
|
||||
// findTRUContours performance tests
|
||||
// ============================================================
|
||||
|
||||
typedef TestBaseWithParam< tuple<Size, int, int> > TestFindTRUContours;
|
||||
|
||||
PERF_TEST_P(TestFindTRUContours, findTRUContours,
|
||||
Combine(
|
||||
Values(sz1080p, sz2160p), // image size
|
||||
Values(128, 512, 2048), // circle count
|
||||
Values(1, 0) // nthreads: 1=single-thread baseline, 0=all available
|
||||
)
|
||||
)
|
||||
{
|
||||
Size img_size = get<0>(GetParam());
|
||||
int num_circles = get<1>(GetParam());
|
||||
int nthreads = get<2>(GetParam());
|
||||
|
||||
RNG rng(12345);
|
||||
Mat img = Mat::zeros(img_size, CV_8UC1);
|
||||
for (int i = 0; i < num_circles; ++i)
|
||||
{
|
||||
Point center(rng.uniform(50, img_size.width - 50),
|
||||
rng.uniform(50, img_size.height - 50));
|
||||
int radius = rng.uniform(10, 200);
|
||||
circle(img, center, radius, Scalar::all(255), FILLED);
|
||||
}
|
||||
|
||||
Mat binary;
|
||||
adaptiveThreshold(img, binary, 255, ADAPTIVE_THRESH_MEAN_C, THRESH_BINARY, 11, 0);
|
||||
|
||||
vector<vector<Point>> contours;
|
||||
int prev_nthreads=cv::getNumThreads();
|
||||
cv::setNumThreads(nthreads);
|
||||
TEST_CYCLE() findContours(binary, contours, RETR_LIST, CHAIN_APPROX_NONE);
|
||||
cv::setNumThreads(prev_nthreads);
|
||||
|
||||
SANITY_CHECK_NOTHING();
|
||||
}
|
||||
|
||||
// Baseline: same image, findContours(RETR_LIST, CHAIN_APPROX_NONE) for direct comparison
|
||||
typedef TestBaseWithParam< tuple<Size, int> > TestFindContoursBaseline;
|
||||
|
||||
PERF_TEST_P(TestFindContoursBaseline, findContours_baseline_for_TRUCO,
|
||||
Combine(
|
||||
Values(sz1080p, sz2160p),
|
||||
Values(128, 512, 2048)
|
||||
)
|
||||
)
|
||||
{
|
||||
Size img_size = get<0>(GetParam());
|
||||
int num_circles = get<1>(GetParam());
|
||||
|
||||
RNG rng(12345);
|
||||
Mat img = Mat::zeros(img_size, CV_8UC1);
|
||||
for (int i = 0; i < num_circles; ++i)
|
||||
{
|
||||
Point center(rng.uniform(50, img_size.width - 50),
|
||||
rng.uniform(50, img_size.height - 50));
|
||||
int radius = rng.uniform(10, 200);
|
||||
circle(img, center, radius, Scalar::all(255), FILLED);
|
||||
}
|
||||
|
||||
Mat binary;
|
||||
adaptiveThreshold(img, binary, 255, ADAPTIVE_THRESH_MEAN_C, THRESH_BINARY, 11, 0);
|
||||
|
||||
vector<vector<Point>> contours;
|
||||
vector<Vec4i> hierarchy;
|
||||
|
||||
TEST_CYCLE() findContours(binary, contours, hierarchy, RETR_LIST, CHAIN_APPROX_NONE);
|
||||
|
||||
SANITY_CHECK_NOTHING();
|
||||
}
|
||||
|
||||
} } // namespace
|
||||
|
||||
@@ -97,4 +97,46 @@ PERF_TEST_P( Image_KernelSize, GaborFilter2d,
|
||||
SANITY_CHECK(filteredImage, 1e-6, ERROR_RELATIVE);
|
||||
}
|
||||
|
||||
// Performance test for the tiled parallel FilterEngine path (images >= 1MP).
|
||||
// Exercises filter2D and sepFilter2D separately across common types and border modes.
|
||||
typedef TestBaseWithParam< tuple<Size, int, BorderMode, bool> > ImgProc_ParallelFilter_Perf;
|
||||
|
||||
PERF_TEST_P( ImgProc_ParallelFilter_Perf, filter2D_parallel,
|
||||
Combine(
|
||||
Values( Size(1280, 1024), sz1080p ),
|
||||
Values( CV_8UC1, CV_8UC3, CV_32FC1 ),
|
||||
Values( BORDER_DEFAULT, BORDER_CONSTANT ),
|
||||
Values( false, true ) // false = filter2D, true = sepFilter2D
|
||||
)
|
||||
)
|
||||
{
|
||||
const Size sz = get<0>(GetParam());
|
||||
const int type = get<1>(GetParam());
|
||||
const int borderMode = get<2>(GetParam());
|
||||
const bool isSep = get<3>(GetParam());
|
||||
|
||||
Mat src(sz, type);
|
||||
Mat dst(sz, type);
|
||||
declare.in(src, WARMUP_RNG).out(dst);
|
||||
|
||||
if (isSep)
|
||||
{
|
||||
Mat kx = (Mat_<float>(1, 3) << 0.25f, 0.5f, 0.25f);
|
||||
Mat ky = (Mat_<float>(3, 1) << 0.25f, 0.5f, 0.25f);
|
||||
TEST_CYCLE() cv::sepFilter2D(src, dst, -1, kx, ky,
|
||||
Point(-1, -1), 0, borderMode);
|
||||
}
|
||||
else
|
||||
{
|
||||
Mat kernel = (Mat_<float>(3, 3) <<
|
||||
1/16.f, 2/16.f, 1/16.f,
|
||||
2/16.f, 4/16.f, 2/16.f,
|
||||
1/16.f, 2/16.f, 1/16.f);
|
||||
TEST_CYCLE() cv::filter2D(src, dst, -1, kernel,
|
||||
Point(-1, -1), 0, borderMode);
|
||||
}
|
||||
|
||||
SANITY_CHECK_NOTHING();
|
||||
}
|
||||
|
||||
} // namespace
|
||||
|
||||
@@ -0,0 +1,40 @@
|
||||
// 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 "perf_precomp.hpp"
|
||||
|
||||
namespace opencv_test {
|
||||
|
||||
CV_ENUM(BorderMode, BORDER_CONSTANT, BORDER_REPLICATE, BORDER_REFLECT_101)
|
||||
CV_ENUM(TargetDepth, CV_8U, CV_16S)
|
||||
|
||||
typedef tuple<Size, int, TargetDepth, BorderMode> LaplacianParams;
|
||||
typedef perf::TestBaseWithParam<LaplacianParams> Perf_Laplacian;
|
||||
|
||||
PERF_TEST_P(Perf_Laplacian, Laplacian,
|
||||
testing::Combine(
|
||||
testing::Values(szVGA, sz720p, sz1080p),
|
||||
testing::Values(1, 3, 5), // ksize: 1, 3, 5
|
||||
TargetDepth::all(), // CV_8U and CV_16S
|
||||
BorderMode::all()
|
||||
))
|
||||
{
|
||||
Size sz = get<0>(GetParam());
|
||||
int ksize = get<1>(GetParam());
|
||||
int ddepth = get<2>(GetParam());
|
||||
int borderMode = get<3>(GetParam());
|
||||
|
||||
Mat src(sz, CV_8UC1);
|
||||
Mat dst(sz, ddepth == CV_16S ? CV_16SC1 : CV_8UC1);
|
||||
|
||||
declare.in(src, WARMUP_RNG).out(dst);
|
||||
|
||||
TEST_CYCLE()
|
||||
{
|
||||
cv::Laplacian(src, dst, ddepth, ksize, 1.0, 0.0, borderMode);
|
||||
}
|
||||
|
||||
SANITY_CHECK(dst);
|
||||
}
|
||||
|
||||
} // namespace opencv_test
|
||||
@@ -369,6 +369,7 @@ void boxFilter(InputArray _src, OutputArray _dst, int ddepth,
|
||||
CV_INSTRUMENT_REGION();
|
||||
|
||||
CV_Assert(!_src.empty());
|
||||
CV_Assert(ksize.width > 0 && ksize.height > 0);
|
||||
|
||||
CV_OCL_RUN(_dst.isUMat() &&
|
||||
(borderType == BORDER_REPLICATE || borderType == BORDER_CONSTANT ||
|
||||
|
||||
@@ -69,13 +69,14 @@ template<typename T, typename ST>
|
||||
struct RowSum :
|
||||
public BaseRowFilter
|
||||
{
|
||||
RowSum( int _ksize, int _anchor ) :
|
||||
BaseRowFilter()
|
||||
RowSum( int _ksize, int _anchor )
|
||||
{
|
||||
ksize = _ksize;
|
||||
anchor = _anchor;
|
||||
}
|
||||
|
||||
bool isStateless() const CV_OVERRIDE { return true; }
|
||||
|
||||
virtual void operator()(const uchar* src, uchar* dst, int width, int cn) CV_OVERRIDE
|
||||
{
|
||||
CV_INSTRUMENT_REGION();
|
||||
@@ -180,8 +181,7 @@ template<typename ST, typename T>
|
||||
struct ColumnSum :
|
||||
public BaseColumnFilter
|
||||
{
|
||||
ColumnSum( int _ksize, int _anchor, double _scale ) :
|
||||
BaseColumnFilter()
|
||||
ColumnSum( int _ksize, int _anchor, double _scale )
|
||||
{
|
||||
ksize = _ksize;
|
||||
anchor = _anchor;
|
||||
@@ -280,8 +280,7 @@ template<>
|
||||
struct ColumnSum<int, uchar> :
|
||||
public BaseColumnFilter
|
||||
{
|
||||
ColumnSum( int _ksize, int _anchor, double _scale ) :
|
||||
BaseColumnFilter()
|
||||
ColumnSum( int _ksize, int _anchor, double _scale )
|
||||
{
|
||||
ksize = _ksize;
|
||||
anchor = _anchor;
|
||||
@@ -436,8 +435,7 @@ public BaseColumnFilter
|
||||
{
|
||||
enum { SHIFT = 23 };
|
||||
|
||||
ColumnSum( int _ksize, int _anchor, double _scale ) :
|
||||
BaseColumnFilter()
|
||||
ColumnSum( int _ksize, int _anchor, double _scale )
|
||||
{
|
||||
ksize = _ksize;
|
||||
anchor = _anchor;
|
||||
@@ -613,8 +611,7 @@ template<>
|
||||
struct ColumnSum<int, short> :
|
||||
public BaseColumnFilter
|
||||
{
|
||||
ColumnSum( int _ksize, int _anchor, double _scale ) :
|
||||
BaseColumnFilter()
|
||||
ColumnSum( int _ksize, int _anchor, double _scale )
|
||||
{
|
||||
ksize = _ksize;
|
||||
anchor = _anchor;
|
||||
@@ -763,8 +760,7 @@ template<>
|
||||
struct ColumnSum<int, ushort> :
|
||||
public BaseColumnFilter
|
||||
{
|
||||
ColumnSum( int _ksize, int _anchor, double _scale ) :
|
||||
BaseColumnFilter()
|
||||
ColumnSum( int _ksize, int _anchor, double _scale )
|
||||
{
|
||||
ksize = _ksize;
|
||||
anchor = _anchor;
|
||||
@@ -910,8 +906,7 @@ template<>
|
||||
struct ColumnSum<int, int> :
|
||||
public BaseColumnFilter
|
||||
{
|
||||
ColumnSum( int _ksize, int _anchor, double _scale ) :
|
||||
BaseColumnFilter()
|
||||
ColumnSum( int _ksize, int _anchor, double _scale )
|
||||
{
|
||||
ksize = _ksize;
|
||||
anchor = _anchor;
|
||||
@@ -1044,8 +1039,7 @@ template<>
|
||||
struct ColumnSum<int, float> :
|
||||
public BaseColumnFilter
|
||||
{
|
||||
ColumnSum( int _ksize, int _anchor, double _scale ) :
|
||||
BaseColumnFilter()
|
||||
ColumnSum( int _ksize, int _anchor, double _scale )
|
||||
{
|
||||
ksize = _ksize;
|
||||
anchor = _anchor;
|
||||
@@ -1700,13 +1694,14 @@ template<typename T, typename ST>
|
||||
struct SqrRowSum :
|
||||
public BaseRowFilter
|
||||
{
|
||||
SqrRowSum( int _ksize, int _anchor ) :
|
||||
BaseRowFilter()
|
||||
SqrRowSum( int _ksize, int _anchor )
|
||||
{
|
||||
ksize = _ksize;
|
||||
anchor = _anchor;
|
||||
}
|
||||
|
||||
bool isStateless() const CV_OVERRIDE { return true; }
|
||||
|
||||
virtual void operator()(const uchar* src, uchar* dst, int width, int cn) CV_OVERRIDE
|
||||
{
|
||||
CV_INSTRUMENT_REGION();
|
||||
|
||||
@@ -303,6 +303,8 @@ void contourTreeToResults(CTree& tree,
|
||||
void approximateChainTC89(const ContourCodesStorage& chain, const Point& origin, const int method,
|
||||
ContourPointsStorage& output);
|
||||
|
||||
void findTRUContours(InputArray _src, OutputArrayOfArrays _contours, int minSize=0, bool binarize=false, int method=CHAIN_APPROX_NONE);
|
||||
|
||||
} // namespace cv
|
||||
|
||||
#endif // OPENCV_CONTOURS_COMMON_HPP
|
||||
|
||||
@@ -111,8 +111,7 @@ public:
|
||||
void LinkRunner::convertLinks(int& first, int& prev, bool isHole)
|
||||
{
|
||||
const vector<int>& contours = isHole ? int_rns : ext_rns;
|
||||
int count = 0;
|
||||
for (int j = 0; j < (int)contours.size(); j++, count++)
|
||||
for (int j = 0; j < (int)contours.size(); j++)
|
||||
{
|
||||
int start = contours[j];
|
||||
int cur = start;
|
||||
|
||||
@@ -648,6 +648,32 @@ void cv::findContours(InputArray _image,
|
||||
return;
|
||||
}
|
||||
|
||||
// Fast path: RETR_LIST without hierarchy → findTRUContours (parallel contour extraction)
|
||||
if (mode == RETR_LIST && !_hierarchy.needed())
|
||||
{
|
||||
// findTRUContours requires FOREGROUND=255; binarize=true thresholds the padded
|
||||
// image in-place, avoiding an extra allocation (findContours accepts any non-zero value)
|
||||
findTRUContours(_image, _contours, 0, true,method);
|
||||
if (offset != Point())
|
||||
{
|
||||
if (_contours.kind() == _InputArray::STD_VECTOR_VECTOR)
|
||||
{
|
||||
auto& vv = *reinterpret_cast<std::vector<std::vector<Point>>*>(_contours.getObj());
|
||||
for (auto& c : vv)
|
||||
for (auto& p : c)
|
||||
p += offset;
|
||||
}
|
||||
else
|
||||
{
|
||||
const Scalar shift(offset.x, offset.y);
|
||||
const int n = (int)_contours.size().height;
|
||||
for (int i = 0; i < n; i++)
|
||||
_contours.getMat(i) += shift;
|
||||
}
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
// TODO: need enum value, need way to return contour starting points with chain codes
|
||||
if (method == 0 /*CV_CHAIN_CODE*/)
|
||||
{
|
||||
|
||||
@@ -0,0 +1,649 @@
|
||||
|
||||
#include "precomp.hpp"
|
||||
#include "contours_common.hpp"
|
||||
#include "opencv2/core/hal/intrin.hpp"
|
||||
#include <map>
|
||||
|
||||
namespace{
|
||||
|
||||
// Tunable block size. 1024 points = 8KB (Fits easily in L1 Cache)
|
||||
template <size_t BLOCK_SIZE = 2048>
|
||||
class TRUCOPagedContour {
|
||||
public:
|
||||
struct Block {
|
||||
cv::Point data[BLOCK_SIZE];
|
||||
};
|
||||
|
||||
TRUCOPagedContour() {
|
||||
allocateBlock();
|
||||
// Initialize pointers to the start of the first block
|
||||
curr_ptr_ = all_blocks_[0]->data;
|
||||
end_ptr_ = curr_ptr_ + BLOCK_SIZE;
|
||||
}
|
||||
|
||||
~TRUCOPagedContour() {
|
||||
for (Block* b : all_blocks_) cv::fastFree(b);
|
||||
}
|
||||
|
||||
// --- HOT PATH: Minimal instructions ---
|
||||
// No counter updates, just raw pointer arithmetic.
|
||||
inline void push_back(const cv::Point& pt) {
|
||||
if (curr_ptr_ == end_ptr_) {
|
||||
current_block_idx_++;
|
||||
if (current_block_idx_ == all_blocks_.size()) {
|
||||
allocateBlock();
|
||||
}
|
||||
curr_ptr_ = all_blocks_[current_block_idx_]->data;
|
||||
end_ptr_ = curr_ptr_ + BLOCK_SIZE;
|
||||
}
|
||||
*curr_ptr_++ = pt;
|
||||
}
|
||||
|
||||
inline void pop_back() {
|
||||
// Safety check: do nothing if completely empty
|
||||
if (current_block_idx_ == 0 && curr_ptr_ == all_blocks_[0]->data) return;
|
||||
|
||||
// Check if we are at the start of the current block
|
||||
if (curr_ptr_ == all_blocks_[current_block_idx_]->data) {
|
||||
// Move to the previous block
|
||||
current_block_idx_--;
|
||||
// Point to the end of the previous block
|
||||
curr_ptr_ = all_blocks_[current_block_idx_]->data + BLOCK_SIZE;
|
||||
end_ptr_ = curr_ptr_;
|
||||
}
|
||||
curr_ptr_--;
|
||||
}
|
||||
|
||||
inline const cv::Point& back() const {
|
||||
// Handle case where back() crosses block boundary
|
||||
if (curr_ptr_ == all_blocks_[current_block_idx_]->data) {
|
||||
return all_blocks_[current_block_idx_ - 1]->data[BLOCK_SIZE - 1];
|
||||
}
|
||||
return *(curr_ptr_ - 1);
|
||||
}
|
||||
|
||||
inline const cv::Point& front() const {
|
||||
return all_blocks_[0]->data[0];
|
||||
}
|
||||
|
||||
// Calculated on demand (O(1) arithmetic, but slightly more math than reading a variable)
|
||||
size_t size() const {
|
||||
size_t elements_in_last = curr_ptr_ - all_blocks_[current_block_idx_]->data;
|
||||
return (current_block_idx_ * BLOCK_SIZE) + elements_in_last;
|
||||
}
|
||||
|
||||
void clear() {
|
||||
current_block_idx_ = 0;
|
||||
if (!all_blocks_.empty()) {
|
||||
curr_ptr_ = all_blocks_[0]->data;
|
||||
end_ptr_ = curr_ptr_ + BLOCK_SIZE;
|
||||
}
|
||||
}
|
||||
|
||||
// Optimized Copy: Uses block-wise memcpy
|
||||
void copyTo(std::vector<cv::Point>& out) const {
|
||||
size_t total = size();
|
||||
out.resize(total);
|
||||
if (total == 0) return;
|
||||
|
||||
cv::Point* dst = out.data();
|
||||
|
||||
// 1. Copy full blocks
|
||||
for (size_t i = 0; i < current_block_idx_; ++i) {
|
||||
std::memcpy(dst, all_blocks_[i]->data, BLOCK_SIZE * sizeof(cv::Point));
|
||||
dst += BLOCK_SIZE;
|
||||
}
|
||||
|
||||
// 2. Copy partial last block
|
||||
size_t last_block_count = curr_ptr_ - all_blocks_[current_block_idx_]->data;
|
||||
if (last_block_count > 0) {
|
||||
std::memcpy(dst, all_blocks_[current_block_idx_]->data, last_block_count * sizeof(cv::Point));
|
||||
}
|
||||
}
|
||||
|
||||
private:
|
||||
void grow() {
|
||||
current_block_idx_++;
|
||||
if (current_block_idx_ == all_blocks_.size()) {
|
||||
allocateBlock();
|
||||
}
|
||||
curr_ptr_ = all_blocks_[current_block_idx_]->data;
|
||||
end_ptr_ = curr_ptr_ + BLOCK_SIZE;
|
||||
}
|
||||
|
||||
void allocateBlock() {
|
||||
Block* b = (Block*)cv::fastMalloc(sizeof(Block));
|
||||
all_blocks_.push_back(b);
|
||||
}
|
||||
|
||||
std::vector<Block*> all_blocks_;
|
||||
size_t current_block_idx_ = 0;
|
||||
|
||||
// Fast pointers for the hot loop
|
||||
cv::Point* curr_ptr_ = nullptr;
|
||||
cv::Point* end_ptr_ = nullptr;
|
||||
};
|
||||
|
||||
|
||||
////IMPLEMENTATION
|
||||
|
||||
struct AccumulatorT:public std::vector<std::vector<cv::Point>>{
|
||||
std::vector<int> idx_internal_lastLine,idx_external_firstLine;
|
||||
};
|
||||
|
||||
|
||||
class TRUCOntourTracer : public cv::ParallelLoopBody
|
||||
{
|
||||
public:
|
||||
|
||||
// We use a pointer to the accumulator to avoid passing huge objects
|
||||
// Accumulator: Vector of (Vector of Contours), where Contour is Vector of Points
|
||||
using AccumulatorType = std::vector<AccumulatorT>;
|
||||
|
||||
TRUCOntourTracer(const cv::Mat& img,
|
||||
const std::vector<cv::Range>& stripRanges,
|
||||
AccumulatorType& accumulator,
|
||||
size_t minSize)
|
||||
: padded_(img), ranges_(stripRanges), accumulator_(accumulator), minSize_(minSize)
|
||||
{
|
||||
step_ = padded_.step;
|
||||
int istep = (int)step_;
|
||||
// 0: East (Right)
|
||||
offsets_[0] = 1;
|
||||
// 1: NE (Up-Right)
|
||||
offsets_[1] = -istep + 1;
|
||||
// 2: North (Up)
|
||||
offsets_[2] = -istep;
|
||||
// 3: NW (Up-Left)
|
||||
offsets_[3] = -istep - 1;
|
||||
// 4: West (Left)
|
||||
offsets_[4] = -1;
|
||||
// 5: SW (Down-Left)
|
||||
offsets_[5] = istep - 1;
|
||||
// 6: South (Down)
|
||||
offsets_[6] = istep;
|
||||
// 7: SE (Down-Right)
|
||||
offsets_[7] = istep + 1;
|
||||
|
||||
memcpy(offsets_ + 8, offsets_, 8 * sizeof(int));
|
||||
|
||||
}
|
||||
|
||||
//trace external contour marking EAST pixels (VISITED_OUTER_RIGHT) only so that later the analysis of the internal contour is exactly as expected
|
||||
void traceExternalContourMock( int r,int c,uchar *row_ptr, const cv::Range& rowRange)const{
|
||||
int curr_x = c , curr_y = r;
|
||||
int start_dir = -1 ;
|
||||
int search_idx = 5;
|
||||
uchar* curr_ptr = row_ptr + c , * start_ptr = curr_ptr;
|
||||
int dir=-1;
|
||||
bool is_first_move = true;
|
||||
// 3. TRACING LOOP
|
||||
while(true)
|
||||
{
|
||||
// Check neighbors
|
||||
for (int n = 0; n < 8; ++n)
|
||||
{
|
||||
int idx = search_idx + n;
|
||||
// Use offset cache
|
||||
uchar* neighbor = curr_ptr + offsets_[idx];
|
||||
if (*neighbor == BACKGROUND) continue;
|
||||
dir = idx & 7;
|
||||
if (((search_idx <= 1) || (dir <= search_idx - 2)) && (curr_x!=c && curr_y!=r))//do nt apply to first pixel in the way back
|
||||
*curr_ptr = VISITED_OUTER_RIGHT;
|
||||
// --- EXECUTE MOVE ---
|
||||
curr_y += dy_[dir];
|
||||
curr_x += dx_[dir];
|
||||
// Check bounds //we need to move out of the range //if first line, and internal contour, we let it go, but no further from this line
|
||||
if( curr_y < rowRange.start )
|
||||
return ;
|
||||
// Short-circuit Jacob's Check
|
||||
if (curr_ptr == start_ptr) {
|
||||
if (!is_first_move && dir == start_dir) {
|
||||
return ;//done
|
||||
}
|
||||
}
|
||||
curr_ptr = neighbor;//move ptr
|
||||
// Reset search index for Moore neighbor
|
||||
search_idx = (dir +6) & 7;
|
||||
break;
|
||||
|
||||
}
|
||||
if (is_first_move) {
|
||||
if(dir==-1){//single pixel
|
||||
break;//not moved
|
||||
}
|
||||
start_dir = dir;
|
||||
is_first_move = false;
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
bool traceContour( TRUCOPagedContour<4096>* buffer, int r,int c,uchar *row_ptr, const cv::Range& rowRange,bool isExternal)const{
|
||||
|
||||
buffer->clear();
|
||||
|
||||
int curr_x = c , curr_y = r;
|
||||
int start_dir = -1 ;
|
||||
int search_idx = isExternal ? 5 :1;
|
||||
uchar* curr_ptr = row_ptr + c , * start_ptr = curr_ptr;
|
||||
int dir=-1;
|
||||
// int sign=isExternal?1:-1;
|
||||
|
||||
bool is_first_move = true;
|
||||
|
||||
|
||||
// QUick test to find the first element of an internal contour. We know it should be NE
|
||||
if(!isExternal){
|
||||
int n=0;
|
||||
for ( n = 0; n < 8; ++n)
|
||||
{
|
||||
int idx = search_idx + n;
|
||||
if ( *(curr_ptr + offsets_[idx]) == BACKGROUND) continue;
|
||||
if(curr_x+ dx_[ idx & 7]!=c+1 || curr_y+ dy_[ idx & 7]!=r-1) return false;
|
||||
break;
|
||||
}
|
||||
if(n==8) return false;//isolated pixels must not be considered as internal
|
||||
}
|
||||
// 3. TRACING LOOP
|
||||
while(true)
|
||||
{
|
||||
buffer->push_back({ (curr_x - 1), (curr_y - 1)});
|
||||
// Check neighbors
|
||||
for (int n = 0; n < 8; ++n)
|
||||
{
|
||||
int idx = search_idx + n;
|
||||
// Use offset cache
|
||||
uchar* neighbor = curr_ptr + offsets_[idx];
|
||||
if (*neighbor == BACKGROUND) continue;
|
||||
|
||||
dir = idx & 7;
|
||||
// --- EXECUTE MOVE ---
|
||||
curr_y += dy_[dir];
|
||||
curr_x += dx_[dir];
|
||||
|
||||
|
||||
// Check bounds //we need to move out of the range //if first line, and internal contour, we let it go, but no further from this line
|
||||
if( curr_y < rowRange.start ){
|
||||
return false;
|
||||
}
|
||||
if ((search_idx <= 1) || (dir <= search_idx - 2))
|
||||
{
|
||||
*curr_ptr = VISITED_OUTER_RIGHT;
|
||||
}
|
||||
else if (*curr_ptr == FOREGROUND)
|
||||
{
|
||||
*curr_ptr = VISITED_;
|
||||
}
|
||||
|
||||
// Short-circuit Jacob's Check
|
||||
if (curr_ptr == start_ptr) {
|
||||
if (!is_first_move && dir == start_dir) {
|
||||
return true;//done
|
||||
}
|
||||
}
|
||||
|
||||
curr_ptr = neighbor;//move ptr
|
||||
|
||||
// Reset search index for Moore neighbor
|
||||
search_idx = (dir +6) & 7;
|
||||
break;
|
||||
|
||||
}
|
||||
if (is_first_move) {
|
||||
if(dir==-1){//single pixel
|
||||
*curr_ptr = VISITED_OUTER_RIGHT;
|
||||
break;//not moved
|
||||
}
|
||||
start_dir = dir;
|
||||
is_first_move = false;
|
||||
}
|
||||
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
void operator()(const cv::Range& range) const CV_OVERRIDE
|
||||
{
|
||||
// Pre-allocate buffer to avoid re-allocation during moves
|
||||
TRUCOPagedContour<4096> buffer;
|
||||
|
||||
int cols = padded_.cols;
|
||||
|
||||
for (int i = range.start; i < range.end; ++i)
|
||||
{
|
||||
const cv::Range& rowRange = ranges_[i];
|
||||
auto& local_contours = accumulator_[i];
|
||||
|
||||
// Hint for result vector size
|
||||
local_contours.reserve(2048);
|
||||
|
||||
for (int r = rowRange.start; r <= rowRange.end; ++r)
|
||||
{
|
||||
uchar* row_ptr = padded_.data + r * step_;
|
||||
|
||||
// "c" is updated by the find* functions
|
||||
for (int c = 1; c < cols - 1; )
|
||||
{
|
||||
// 1. FAST SCAN: Skip background pixels
|
||||
if ((c = findStartContourPoint(row_ptr, cols, c)) == cols) break;
|
||||
|
||||
// 2. CHECK: Only process if actually FOREGROUND (redundancy check)
|
||||
if (row_ptr[c] == FOREGROUND && r<rowRange.end )
|
||||
{
|
||||
if( traceContour(&buffer,r,c,row_ptr,rowRange,true)){
|
||||
// Post-processing
|
||||
if (buffer.size() > 1 && buffer.back() == buffer.front()) {
|
||||
buffer.pop_back();
|
||||
}
|
||||
if (buffer.size() >= minSize_) {
|
||||
// Instead of copying the vector, we move it.
|
||||
local_contours.emplace_back();
|
||||
buffer.copyTo(local_contours.back());
|
||||
if( r==rowRange.start){//register this so we can zip them as Suzuki&Abe method
|
||||
local_contours.idx_external_firstLine.push_back((int)(local_contours.size()-1) );
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
else if( row_ptr[c] == FOREGROUND && r==rowRange.end ){//extra step to mark east pixels only so that internal contours can be correctly extracted in this line
|
||||
traceExternalContourMock(r,c,row_ptr,rowRange);
|
||||
}
|
||||
|
||||
// 3. FAST SCAN: Find end of current component to skip processing it again
|
||||
c = findEndContourPoint(row_ptr, cols, c + 1);
|
||||
if(c>=cols)break;//end of row
|
||||
//internal contour
|
||||
if(row_ptr[c-1]>VISITED_OUTER_RIGHT && r>rowRange.start){//inner contours of first line are handled by the thread above
|
||||
|
||||
if(traceContour(&buffer,r,c-1,row_ptr,rowRange,false)){
|
||||
// Post-processing
|
||||
if (buffer.size() > 1 && buffer.back() == buffer.front()) {
|
||||
buffer.pop_back();
|
||||
}
|
||||
if (buffer.size() >= minSize_) {
|
||||
local_contours.emplace_back();
|
||||
buffer.copyTo(local_contours.back());
|
||||
if( r==rowRange.end){//register this so we can zip them as Suzuki&Abe method
|
||||
local_contours.idx_internal_lastLine.push_back((int)(local_contours.size() -1));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static inline int findStartContourPoint(uchar* src_data, int width, int j)
|
||||
{
|
||||
#if (CV_SIMD || CV_SIMD_SCALABLE)
|
||||
cv::v_uint8 v_zero = cv::vx_setzero_u8();
|
||||
for (; j <= width - cv::VTraits<cv::v_uint8>::vlanes(); j += cv::VTraits<cv::v_uint8>::vlanes())
|
||||
{
|
||||
cv::v_uint8 vmask = (cv::v_ne(cv::vx_load((uchar*)(src_data + j)), v_zero));
|
||||
if (cv::v_check_any(vmask))
|
||||
{
|
||||
j += cv::v_scan_forward(vmask);
|
||||
return j;
|
||||
}
|
||||
}
|
||||
#endif
|
||||
for (; j < width && !src_data[j]; ++j)
|
||||
;
|
||||
return j;
|
||||
}
|
||||
|
||||
inline static int findEndContourPoint(uchar* src_data,int width, int j)
|
||||
{
|
||||
#if (CV_SIMD || CV_SIMD_SCALABLE)
|
||||
if (j < width && !src_data[j])
|
||||
{
|
||||
return j;
|
||||
}
|
||||
else
|
||||
{
|
||||
cv::v_uint8 v_zero = cv::vx_setzero_u8();
|
||||
for (; j <= width - cv::VTraits<cv::v_uint8>::vlanes(); j += cv::VTraits<cv::v_uint8>::vlanes())
|
||||
{
|
||||
cv::v_uint8 vmask = (cv::v_eq(cv::vx_load((uchar*)(src_data + j)), v_zero));
|
||||
if (cv::v_check_any(vmask))
|
||||
{
|
||||
j += cv::v_scan_forward(vmask);
|
||||
return j;
|
||||
}
|
||||
}
|
||||
}
|
||||
#endif
|
||||
for (; j < width && src_data[j]; ++j)
|
||||
;
|
||||
|
||||
return j;
|
||||
}
|
||||
|
||||
|
||||
private:
|
||||
cv::Mat padded_;
|
||||
const std::vector<cv::Range>& ranges_;
|
||||
AccumulatorType& accumulator_;
|
||||
size_t minSize_;
|
||||
size_t step_;
|
||||
int offsets_[16];
|
||||
|
||||
// 0=E, 1=NE, 2=N, 3=NW, 4=W, 5=SW, 6=S, 7=SE (CCW Rotation)
|
||||
const int dx_[8] = { 1, 1, 0, -1, -1, -1, 0, 1 };
|
||||
const int dy_[8] = { 0, -1, -1, -1, 0, 1, 1, 1 };
|
||||
// Constants defined once
|
||||
const uchar FOREGROUND = 255;
|
||||
const uchar BACKGROUND = 0;
|
||||
const uchar VISITED_OUTER_RIGHT = 100;
|
||||
const uchar VISITED_ = 200;
|
||||
};
|
||||
|
||||
|
||||
void approxContour(std::vector<cv::Point> &inout,cv::ContourApproximationModes contApprox_) {
|
||||
size_t n = inout.size();
|
||||
if (n <= 1) return;
|
||||
|
||||
if (contApprox_ == cv::CHAIN_APPROX_SIMPLE) {
|
||||
std::vector<cv::Point> result;
|
||||
result.reserve(n);
|
||||
|
||||
for (size_t i = 0; i < n; ++i) {
|
||||
size_t prev_i = (i == 0) ? n - 1 : i - 1;
|
||||
size_t next_i = (i == n - 1) ? 0 : i + 1;
|
||||
|
||||
cv::Point v1 = inout[i] - inout[prev_i];
|
||||
cv::Point v2 = inout[next_i] - inout[i];
|
||||
|
||||
if (v1 != v2) {
|
||||
result.push_back(inout[i]);
|
||||
}
|
||||
}
|
||||
if (result.empty() && n > 0) result.push_back(inout[0]);
|
||||
inout = std::move(result);
|
||||
} else if (contApprox_ == cv::CHAIN_APPROX_TC89_L1 || contApprox_ == cv::CHAIN_APPROX_TC89_KCOS) {
|
||||
auto getCode = [](cv::Point d) -> schar {
|
||||
if (d.x == 1) {
|
||||
if (d.y == 0) return 0;
|
||||
if (d.y == -1) return 1;
|
||||
if (d.y == 1) return 7;
|
||||
} else if (d.x == 0) {
|
||||
if (d.y == -1) return 2;
|
||||
if (d.y == 1) return 6;
|
||||
} else if (d.x == -1) {
|
||||
if (d.y == -1) return 3;
|
||||
if (d.y == 0) return 4;
|
||||
if (d.y == 1) return 5;
|
||||
}
|
||||
return 0;
|
||||
};
|
||||
|
||||
cv::ContourCodesStorage::storage_t codesStorage;
|
||||
cv::ContourCodesStorage codes(&codesStorage);
|
||||
for (size_t i = 0; i < n; ++i) {
|
||||
cv::Point delta = inout[(i + 1) % n] - inout[i];
|
||||
codes.push_back(getCode(delta));
|
||||
}
|
||||
|
||||
cv::ContourPointsStorage::storage_t pointsStorage;
|
||||
cv::ContourPointsStorage points(&pointsStorage);
|
||||
cv::approximateChainTC89(codes, inout[0], contApprox_, points);
|
||||
|
||||
inout.clear();
|
||||
inout.reserve(points.size());
|
||||
for (size_t i = 0; i < points.size(); ++i) {
|
||||
inout.push_back(points.at(i));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// ==========================================================
|
||||
// 1. The Core Implementation (Operates on std::vector directly)
|
||||
// ==========================================================
|
||||
void findTRUContoursImpl(cv::Mat& padded,
|
||||
std::vector<std::vector<cv::Point>>& outContours,
|
||||
int minSize,int contApprox)
|
||||
{
|
||||
// Load Balancing Logic
|
||||
const int nstripes = cv::getNumThreads();
|
||||
std::vector<cv::Range> balancedRanges;
|
||||
if (nstripes > 1) {
|
||||
int rowsPerStripe = (padded.rows - 2) / nstripes;
|
||||
int remainingRows = (padded.rows - 2) % nstripes;
|
||||
int currentRow = 1;
|
||||
for (int t = 0; t < nstripes; ++t) {
|
||||
int startRow = currentRow;
|
||||
int endRow = startRow + rowsPerStripe + (t < remainingRows ? 1 : 0);
|
||||
balancedRanges.emplace_back(startRow, endRow);
|
||||
currentRow = endRow;
|
||||
}
|
||||
}
|
||||
else {
|
||||
balancedRanges.emplace_back(1, padded.rows - 1);
|
||||
}
|
||||
// Parallel Execution
|
||||
std::vector<AccumulatorT> threadAccumulators(balancedRanges.size());
|
||||
TRUCOntourTracer worker(padded, balancedRanges, threadAccumulators, minSize);
|
||||
cv::parallel_for_(cv::Range(0, (int)balancedRanges.size()), worker);
|
||||
|
||||
|
||||
// REORG To Match Suzuki & Abe's Contour Ordering.
|
||||
// Every adjacent strip pair (t, t+1) shares a boundary row. On that row:
|
||||
// * thread t recorded INTERNAL fragments -> idx_internal_lastLine (tail of accT)
|
||||
// * thread t+1 recorded EXTERNAL fragments -> idx_external_firstLine (head of accT1)
|
||||
// Both runs are already in X-ascending order (left-to-right raster scan),
|
||||
// so a two-pointer merge restores the sequential Suzuki & Abe ordering.
|
||||
for (size_t t = 0; t + 1 < threadAccumulators.size(); ++t) {
|
||||
auto& accT = threadAccumulators[t];
|
||||
auto& accT1 = threadAccumulators[t + 1];
|
||||
|
||||
const size_t kI = accT.idx_internal_lastLine.size();
|
||||
const size_t kE = accT1.idx_external_firstLine.size();
|
||||
if (kI == 0 && kE == 0) continue;
|
||||
|
||||
const size_t tailStart = accT.size() - kI;
|
||||
|
||||
// Merge by ascending X of each contour's first point. Contours are moved,
|
||||
// not copied — only std::vector handles (three pointers) change hands.
|
||||
std::vector<std::vector<cv::Point>> merged;
|
||||
merged.reserve(kI + kE);
|
||||
size_t i = tailStart, iEnd = accT.size();
|
||||
size_t j = 0, jEnd = kE;
|
||||
while (i < iEnd && j < jEnd) {
|
||||
if (accT[i].front().x <= accT1[j].front().x)
|
||||
merged.emplace_back(std::move(accT[i++]));
|
||||
else
|
||||
merged.emplace_back(std::move(accT1[j++]));
|
||||
}
|
||||
while (i < iEnd) merged.emplace_back(std::move(accT[i++]));
|
||||
while (j < jEnd) merged.emplace_back(std::move(accT1[j++]));
|
||||
|
||||
// Replace accT's tail with the merged run.
|
||||
accT.resize(tailStart);
|
||||
for (auto& c : merged) accT.emplace_back(std::move(c));
|
||||
|
||||
// Drop the consumed externals from the head of accT1.
|
||||
accT1.erase(accT1.begin(), accT1.begin() + kE);
|
||||
|
||||
// Any remaining bookkeeping in accT1 indexed absolute positions — fix them.
|
||||
for (auto& idx : accT1.idx_internal_lastLine)
|
||||
idx -= static_cast<int>(kE);
|
||||
|
||||
// This boundary's bookkeeping is now consumed.
|
||||
accT.idx_internal_lastLine.clear();
|
||||
accT1.idx_external_firstLine.clear();
|
||||
}
|
||||
|
||||
// ZERO-COPY MERGE
|
||||
size_t totalContours = 0;
|
||||
for (auto& tVec : threadAccumulators)
|
||||
totalContours += tVec.size();
|
||||
|
||||
|
||||
outContours.clear();
|
||||
outContours.reserve(totalContours);
|
||||
// move the contours from thread accumulators to output without copying pixel data
|
||||
for (auto& tVec : threadAccumulators) {
|
||||
// move_iterator moves the vector internals (pointers) without copying pixel data
|
||||
outContours.insert(outContours.end(),
|
||||
std::make_move_iterator(tVec.begin()),
|
||||
std::make_move_iterator(tVec.end()));
|
||||
}
|
||||
// reverse the order to match original findContours Suzuki&Abe
|
||||
std::reverse(outContours.begin(), outContours.end());
|
||||
|
||||
|
||||
//7. now, lets do the contour approximation if needed in parallel
|
||||
if(contApprox!=cv::CHAIN_APPROX_NONE){
|
||||
cv::parallel_for_(cv::Range(0, (int)outContours.size()), [&](const cv::Range& range){
|
||||
for(int i=range.start;i<range.end;i++){
|
||||
approxContour(outContours[i],(cv::ContourApproximationModes)contApprox);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
namespace cv{
|
||||
|
||||
// ==========================================================
|
||||
//
|
||||
// Public API: Handles OutputArray and dispatches to the core implementation
|
||||
// This is a modified version of the original TRUCO parallel algorithm to produce the exact same output
|
||||
// as original findContours with RETR_LIST mode (no hierarchy, all contours are external). It also supports contour approximation.
|
||||
//
|
||||
// ==========================================================
|
||||
void findTRUContours(InputArray _src, OutputArrayOfArrays _contours, int minSize, bool binarize,int method)
|
||||
{
|
||||
CV_INSTRUMENT_REGION();
|
||||
Mat src = _src.getMat();
|
||||
CV_Assert(!src.empty() && src.type() == CV_8UC1);
|
||||
|
||||
// Buffer handling
|
||||
cv::Mat padded;
|
||||
cv::copyMakeBorder(src, padded, 1, 1, 1, 1, cv::BORDER_CONSTANT, 0);
|
||||
if (binarize)
|
||||
cv::threshold(padded, padded, 0, 255, cv::THRESH_BINARY);
|
||||
|
||||
|
||||
// Fast path: caller passed std::vector<std::vector<cv::Point>> directly.
|
||||
// Write into it without any intermediate copy.
|
||||
if (_contours.kind() == _InputArray::STD_VECTOR_VECTOR) {
|
||||
auto* vec = reinterpret_cast<std::vector<std::vector<cv::Point>>*>(_contours.getObj());
|
||||
findTRUContoursImpl(padded, *vec, minSize,method);
|
||||
}
|
||||
else{ // Slow path: generic OutputArray — build in a temp vector then copy.
|
||||
std::vector<std::vector<cv::Point>> tempContours;
|
||||
findTRUContoursImpl(padded, tempContours, minSize,method);
|
||||
|
||||
_contours.create((int)tempContours.size(), 1, 0, -1, true);
|
||||
for (size_t i = 0; i < tempContours.size(); i++) {
|
||||
_contours.create((int)tempContours[i].size(), 1, CV_32SC2, (int)i, true);
|
||||
Mat m = _contours.getMat((int)i);
|
||||
std::memcpy(m.data, tempContours[i].data(), tempContours[i].size() * sizeof(cv::Point));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
@@ -750,6 +750,22 @@ void cv::Laplacian( InputArray _src, OutputArray _dst, int ddepth, int ksize,
|
||||
|
||||
if( ksize == 1 || ksize == 3 )
|
||||
{
|
||||
Mat src = _src.getMat();
|
||||
Mat dst = _dst.getMat();
|
||||
|
||||
Point ofs;
|
||||
Size wsz(src.cols, src.rows);
|
||||
if(!(borderType & BORDER_ISOLATED))
|
||||
src.locateROI(wsz, ofs);
|
||||
|
||||
CALL_HAL(laplacian, cv_hal_laplacian,
|
||||
src.ptr(), src.step,
|
||||
dst.ptr(), dst.step,
|
||||
src.cols, src.rows,
|
||||
sdepth, ddepth, cn,
|
||||
ksize, borderType & ~BORDER_ISOLATED,
|
||||
(uint8_t)0);
|
||||
|
||||
filter2D( _src, _dst, ddepth, kernel, Point(-1, -1), delta, borderType );
|
||||
}
|
||||
else
|
||||
|
||||
@@ -1495,6 +1495,7 @@ FillEdgeCollection( Mat& img, std::vector<PolyEdge>& edges, const void* color )
|
||||
|
||||
|
||||
/* draws simple or filled circle */
|
||||
CV_DISABLE_UBSAN
|
||||
static void
|
||||
Circle( Mat& img, Point center, int radius, const void* color, int fill )
|
||||
{
|
||||
|
||||
@@ -63,14 +63,14 @@
|
||||
|
||||
namespace cv {
|
||||
|
||||
BaseRowFilter::BaseRowFilter() { ksize = anchor = -1; }
|
||||
BaseRowFilter::BaseRowFilter() : ksize(-1), anchor(-1) {}
|
||||
BaseRowFilter::~BaseRowFilter() {}
|
||||
|
||||
BaseColumnFilter::BaseColumnFilter() { ksize = anchor = -1; }
|
||||
BaseColumnFilter::BaseColumnFilter() : ksize(-1), anchor(-1) {}
|
||||
BaseColumnFilter::~BaseColumnFilter() {}
|
||||
void BaseColumnFilter::reset() {}
|
||||
|
||||
BaseFilter::BaseFilter() { ksize = Size(-1,-1); anchor = Point(-1,-1); }
|
||||
BaseFilter::BaseFilter() : ksize(-1, -1), anchor(-1, -1) {}
|
||||
BaseFilter::~BaseFilter() {}
|
||||
void BaseFilter::reset() {}
|
||||
|
||||
@@ -207,6 +207,14 @@ int FilterEngine::proceed(const uchar* src, int srcstep, int count,
|
||||
CV_CPU_DISPATCH_MODES_ALL);
|
||||
}
|
||||
|
||||
bool FilterEngine::isStateless() const
|
||||
{
|
||||
bool s2d = !filter2D || filter2D->isStateless();
|
||||
bool sr = !rowFilter || rowFilter->isStateless();
|
||||
bool sc = !columnFilter || columnFilter->isStateless();
|
||||
return s2d && sr && sc;
|
||||
}
|
||||
|
||||
void FilterEngine::apply(const Mat& src, Mat& dst, const Size& wsz, const Point& ofs)
|
||||
{
|
||||
CV_INSTRUMENT_REGION();
|
||||
@@ -455,16 +463,18 @@ template<typename ST, class CastOp, class VecOp> struct Filter2D : public BaseFi
|
||||
vecOp = _vecOp;
|
||||
CV_Assert( _kernel.type() == DataType<KT>::type );
|
||||
preprocess2DKernel( _kernel, coords, coeffs );
|
||||
ptrs.resize( coords.size() );
|
||||
}
|
||||
|
||||
bool isStateless() const CV_OVERRIDE { return true; }
|
||||
|
||||
void operator()(const uchar** src, uchar* dst, int dststep, int count, int width, int cn) CV_OVERRIDE
|
||||
{
|
||||
KT _delta = delta;
|
||||
const Point* pt = &coords[0];
|
||||
const KT* kf = (const KT*)&coeffs[0];
|
||||
const ST** kp = (const ST**)&ptrs[0];
|
||||
int i, k, nz = (int)coords.size();
|
||||
AutoBuffer<const ST*> _kp(nz);
|
||||
const ST** kp = _kp.data();
|
||||
CastOp castOp = castOp0;
|
||||
|
||||
width *= cn;
|
||||
@@ -507,7 +517,6 @@ template<typename ST, class CastOp, class VecOp> struct Filter2D : public BaseFi
|
||||
|
||||
std::vector<Point> coords;
|
||||
std::vector<uchar> coeffs;
|
||||
std::vector<uchar*> ptrs;
|
||||
KT delta;
|
||||
CastOp castOp0;
|
||||
VecOp vecOp;
|
||||
|
||||
@@ -48,6 +48,8 @@
|
||||
|
||||
namespace cv
|
||||
{
|
||||
|
||||
|
||||
#ifdef HAVE_OPENCL
|
||||
bool ocl_sepFilter2D(
|
||||
InputArray _src, OutputArray _dst, int ddepth,
|
||||
|
||||
@@ -43,6 +43,7 @@
|
||||
#include "precomp.hpp"
|
||||
#include "opencv2/core/hal/intrin.hpp"
|
||||
#include "filter.hpp"
|
||||
#include "opencv2/core/utils/tls.hpp"
|
||||
|
||||
#include <cstddef>
|
||||
|
||||
@@ -296,12 +297,264 @@ int FilterEngine__proceed(FilterEngine& this_, const uchar* src, int srcstep, in
|
||||
return dy;
|
||||
}
|
||||
|
||||
// Lightweight tile border fill, templated on element size (bytes) so the
|
||||
// compiler can inline and optimize memcpy for common fixed sizes (1, 2, 4 …).
|
||||
// Avoids the full cv::copyMakeBorder() overhead: no OpenCL/IPP dispatch,
|
||||
// no Mat header re-allocation, and a single AutoBuffer for the border table.
|
||||
template<int esz>
|
||||
static void fillTileBorder(
|
||||
const uchar* src, size_t srcstep, int src_w, int src_h,
|
||||
uchar* dst, size_t dststep,
|
||||
int pad_top, int pad_bottom, int pad_left, int pad_right,
|
||||
int borderType, const uchar* constVal)
|
||||
{
|
||||
const int dst_w = src_w + pad_left + pad_right;
|
||||
|
||||
// ── Left/right column offset table (byte offsets into a source row) ────────
|
||||
AutoBuffer<int> _tab(pad_left + pad_right);
|
||||
int* tab = _tab.data();
|
||||
for (int i = 0; i < pad_left; i++)
|
||||
tab[i] = borderInterpolate(i - pad_left, src_w, borderType) * esz;
|
||||
for (int i = 0; i < pad_right; i++)
|
||||
tab[pad_left + i] = borderInterpolate(src_w + i, src_w, borderType) * esz;
|
||||
|
||||
// For BORDER_CONSTANT top/bottom rows, pre-build a full-width fill row.
|
||||
AutoBuffer<uchar> _constRow;
|
||||
uchar* constRow = nullptr;
|
||||
if (borderType == BORDER_CONSTANT && (pad_top > 0 || pad_bottom > 0))
|
||||
{
|
||||
_constRow.allocate(dst_w * esz);
|
||||
constRow = _constRow.data();
|
||||
for (int x = 0; x < dst_w; x++)
|
||||
memcpy(constRow + x * esz, constVal, esz);
|
||||
}
|
||||
|
||||
// ── Interior rows ─────────────────────────────────────────────────────────
|
||||
uchar* dstRow = dst + pad_top * dststep;
|
||||
for (int r = 0; r < src_h; r++, dstRow += dststep, src += srcstep)
|
||||
{
|
||||
// Copy source pixels into the interior portion of the row.
|
||||
memcpy(dstRow + pad_left * esz, src, src_w * esz);
|
||||
|
||||
if (borderType == BORDER_CONSTANT)
|
||||
{
|
||||
for (int i = 0; i < pad_left; i++)
|
||||
memcpy(dstRow + i * esz, constVal, esz);
|
||||
for (int i = 0; i < pad_right; i++)
|
||||
memcpy(dstRow + (pad_left + src_w + i) * esz, constVal, esz);
|
||||
}
|
||||
else
|
||||
{
|
||||
for (int i = 0; i < pad_left; i++)
|
||||
memcpy(dstRow + i * esz, src + tab[i], esz);
|
||||
for (int i = 0; i < pad_right; i++)
|
||||
memcpy(dstRow + (pad_left + src_w + i) * esz, src + tab[pad_left + i], esz);
|
||||
}
|
||||
}
|
||||
|
||||
// ── Top rows ──────────────────────────────────────────────────────────────
|
||||
for (int r = 0; r < pad_top; r++)
|
||||
{
|
||||
if (borderType == BORDER_CONSTANT)
|
||||
memcpy(dst + r * dststep, constRow, dst_w * esz);
|
||||
else
|
||||
{
|
||||
int j = borderInterpolate(r - pad_top, src_h, borderType);
|
||||
memcpy(dst + r * dststep, dst + (pad_top + j) * dststep, dst_w * esz);
|
||||
}
|
||||
}
|
||||
|
||||
// ── Bottom rows ───────────────────────────────────────────────────────────
|
||||
uchar* dstBot = dst + (pad_top + src_h) * dststep;
|
||||
for (int r = 0; r < pad_bottom; r++)
|
||||
{
|
||||
if (borderType == BORDER_CONSTANT)
|
||||
memcpy(dstBot + r * dststep, constRow, dst_w * esz);
|
||||
else
|
||||
{
|
||||
int j = borderInterpolate(src_h + r, src_h, borderType);
|
||||
memcpy(dstBot + r * dststep, dst + (pad_top + j) * dststep, dst_w * esz);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
class TiledFilterInvoker : public ParallelLoopBody
|
||||
{
|
||||
struct TiledFilterBuffers {
|
||||
Mat padded_tile;
|
||||
Mat hbuf;
|
||||
};
|
||||
|
||||
public:
|
||||
TiledFilterInvoker(FilterEngine& _fe, const Mat& _src, Mat& _dst, int _tileSize = 128)
|
||||
: fe(_fe), src(_src), dst(_dst), tileSize(_tileSize)
|
||||
{
|
||||
tilesX = (dst.cols + tileSize - 1) / tileSize;
|
||||
}
|
||||
|
||||
virtual void operator() (const Range& range) const CV_OVERRIDE
|
||||
{
|
||||
int ax = fe.anchor.x, kwidth = fe.ksize.width;
|
||||
int ay = fe.anchor.y, kheight = fe.ksize.height;
|
||||
int dx1 = ax, dx2 = kwidth - ax - 1;
|
||||
int dy1 = ay, dy2 = kheight - ay - 1;
|
||||
|
||||
int borderType = fe.rowBorderType;
|
||||
int cn = CV_MAT_CN(fe.srcType);
|
||||
bool isSep = fe.isSeparable();
|
||||
|
||||
TiledFilterBuffers& tls = tlsData.getRef();
|
||||
|
||||
for (int i = range.start; i < range.end; i++)
|
||||
{
|
||||
int ty = i / tilesX;
|
||||
int tx = i % tilesX;
|
||||
|
||||
int dst_x = tx * tileSize;
|
||||
int dst_y = ty * tileSize;
|
||||
int w = std::min(tileSize, dst.cols - dst_x);
|
||||
int h = std::min(tileSize, dst.rows - dst_y);
|
||||
|
||||
int src_x1 = dst_x - dx1, src_y1 = dst_y - dy1;
|
||||
int src_x2 = dst_x + w + dx2, src_y2 = dst_y + h + dy2;
|
||||
|
||||
int pad_top = std::max(0, -src_y1);
|
||||
int pad_bottom = std::max(0, src_y2 - src.rows);
|
||||
int pad_left = std::max(0, -src_x1);
|
||||
int pad_right = std::max(0, src_x2 - src.cols);
|
||||
|
||||
int clamped_x1 = std::max(0, src_x1);
|
||||
int clamped_y1 = std::max(0, src_y1);
|
||||
int clamped_x2 = std::min(src.cols, src_x2);
|
||||
int clamped_y2 = std::min(src.rows, src_y2);
|
||||
|
||||
Mat src_region = src(Rect(clamped_x1, clamped_y1,
|
||||
clamped_x2 - clamped_x1,
|
||||
clamped_y2 - clamped_y1));
|
||||
|
||||
Mat tile_mat;
|
||||
if (pad_top == 0 && pad_bottom == 0 && pad_left == 0 && pad_right == 0)
|
||||
{
|
||||
tile_mat = src_region;
|
||||
}
|
||||
else
|
||||
{
|
||||
int padded_w = w + dx1 + dx2;
|
||||
int padded_h = h + dy1 + dy2;
|
||||
if (tls.padded_tile.cols < padded_w || tls.padded_tile.rows < padded_h || tls.padded_tile.type() != fe.srcType)
|
||||
tls.padded_tile.create(padded_h, padded_w, fe.srcType);
|
||||
|
||||
tile_mat = tls.padded_tile(Rect(0, 0, padded_w, padded_h));
|
||||
|
||||
const int esz = (int)CV_ELEM_SIZE(fe.srcType);
|
||||
const uchar* cval = fe.constBorderValue.empty() ? nullptr : &fe.constBorderValue[0];
|
||||
|
||||
#define FILL_BORDER(E) fillTileBorder<E>(src_region.ptr(), (size_t)src_region.step, \
|
||||
src_region.cols, src_region.rows, tile_mat.ptr(), (size_t)tile_mat.step, \
|
||||
pad_top, pad_bottom, pad_left, pad_right, borderType, cval)
|
||||
switch (esz)
|
||||
{
|
||||
case 1: FILL_BORDER(1); break;
|
||||
case 2: FILL_BORDER(2); break;
|
||||
case 3: FILL_BORDER(3); break;
|
||||
case 4: FILL_BORDER(4); break;
|
||||
case 6: FILL_BORDER(6); break;
|
||||
case 8: FILL_BORDER(8); break;
|
||||
case 12: FILL_BORDER(12); break;
|
||||
case 16: FILL_BORDER(16); break;
|
||||
default:
|
||||
{
|
||||
// Generic fallback for exotic element sizes.
|
||||
Scalar bv = Scalar::all(0);
|
||||
if (!fe.constBorderValue.empty())
|
||||
{
|
||||
const uchar* bptr = &fe.constBorderValue[0];
|
||||
int depth = CV_MAT_DEPTH(fe.srcType);
|
||||
for (int k = 0; k < cn; k++)
|
||||
{
|
||||
switch(depth)
|
||||
{
|
||||
case CV_8U: bv[k] = bptr[k]; break;
|
||||
case CV_8S: bv[k] = ((const schar*)bptr)[k]; break;
|
||||
case CV_16U: bv[k] = ((const ushort*)bptr)[k]; break;
|
||||
case CV_16S: bv[k] = ((const short*)bptr)[k]; break;
|
||||
case CV_32S: bv[k] = ((const int*)bptr)[k]; break;
|
||||
case CV_32F: bv[k] = ((const float*)bptr)[k]; break;
|
||||
case CV_64F: bv[k] = ((const double*)bptr)[k]; break;
|
||||
default: bv[k] = bptr[k];
|
||||
}
|
||||
}
|
||||
}
|
||||
copyMakeBorder(src_region, tile_mat, pad_top, pad_bottom, pad_left, pad_right, borderType, bv);
|
||||
}
|
||||
}
|
||||
#undef FILL_BORDER
|
||||
}
|
||||
|
||||
uchar* dst_ptr = dst.ptr(dst_y) + dst_x * dst.elemSize();
|
||||
if (isSep)
|
||||
{
|
||||
int hstep = (int)alignSize(w * CV_ELEM_SIZE(fe.bufType), VEC_ALIGN);
|
||||
if (tls.hbuf.rows < tile_mat.rows || tls.hbuf.cols < hstep)
|
||||
tls.hbuf.create(tile_mat.rows, hstep, CV_8U);
|
||||
|
||||
uchar* hbuf = tls.hbuf.ptr();
|
||||
for (int r = 0; r < tile_mat.rows; r++)
|
||||
(*fe.rowFilter)(tile_mat.ptr(r), hbuf + r * hstep, w, cn);
|
||||
|
||||
AutoBuffer<const uchar*> _brows(h + kheight - 1);
|
||||
const uchar** brows = _brows.data();
|
||||
for (int m = 0; m < h + kheight - 1; m++)
|
||||
brows[m] = hbuf + m * hstep;
|
||||
|
||||
(*fe.columnFilter)(brows, dst_ptr, (int)dst.step, h, w * cn);
|
||||
}
|
||||
else
|
||||
{
|
||||
AutoBuffer<const uchar*> _brows(h + kheight - 1);
|
||||
const uchar** brows = _brows.data();
|
||||
for (int k = 0; k < h + kheight - 1; k++)
|
||||
brows[k] = tile_mat.ptr(k);
|
||||
|
||||
(*fe.filter2D)(brows, dst_ptr, (int)dst.step, h, w, cn);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private:
|
||||
FilterEngine& fe;
|
||||
const Mat& src;
|
||||
Mat& dst;
|
||||
int tileSize;
|
||||
int tilesX;
|
||||
mutable TLSData<TiledFilterBuffers> tlsData;
|
||||
};
|
||||
|
||||
void FilterEngine__apply(FilterEngine& this_, const Mat& src, Mat& dst, const Size& wsz, const Point& ofs)
|
||||
{
|
||||
CV_INSTRUMENT_REGION();
|
||||
|
||||
CV_DbgAssert(src.type() == this_.srcType && dst.type() == this_.dstType);
|
||||
|
||||
// Tiled Fast Path for stateless parallel filters on large images.
|
||||
int nthreads = cv::getNumThreads();
|
||||
if (this_.isStateless() && nthreads > 1 &&
|
||||
(size_t)src.total() >= std::max((size_t)1024 * 1024, (size_t)nthreads * 64 * 1024) &&
|
||||
this_.rowBorderType == this_.columnBorderType)
|
||||
{
|
||||
// For in-place operations (e.g. morphologyEx MORPH_OPEN), clone src so that
|
||||
// concurrent tiles read from an immutable snapshot rather than racing on writes.
|
||||
Mat src_copy = (src.data == dst.data) ? src.clone() : src;
|
||||
|
||||
// Heuristic: Balance L2 cache locality (128) vs parallel load balancing (64).
|
||||
int tileSize = (src.total() < (size_t)nthreads * 128 * 128 * 4) ? 64 : 128;
|
||||
int totalTiles = ((dst.cols + tileSize - 1) / tileSize) * ((dst.rows + tileSize - 1) / tileSize);
|
||||
|
||||
TiledFilterInvoker invoker(this_, src_copy, dst, tileSize);
|
||||
parallel_for_(Range(0, totalTiles), invoker);
|
||||
return;
|
||||
}
|
||||
|
||||
FilterEngine__start(this_, wsz, src.size(), ofs);
|
||||
int y = this_.startY - ofs.y;
|
||||
FilterEngine__proceed(this_,
|
||||
@@ -2398,6 +2651,8 @@ template<typename ST, typename DT, class VecOp> struct RowFilter : public BaseRo
|
||||
vecOp = _vecOp;
|
||||
}
|
||||
|
||||
bool isStateless() const CV_OVERRIDE { return true; }
|
||||
|
||||
void operator()(const uchar* src, uchar* dst, int width, int cn) CV_OVERRIDE
|
||||
{
|
||||
CV_INSTRUMENT_REGION();
|
||||
@@ -2599,6 +2854,8 @@ template<class CastOp, class VecOp> struct ColumnFilter : public BaseColumnFilte
|
||||
(kernel.rows == 1 || kernel.cols == 1));
|
||||
}
|
||||
|
||||
bool isStateless() const CV_OVERRIDE { return true; }
|
||||
|
||||
void operator()(const uchar** src, uchar* dst, int dststep, int count, int width) CV_OVERRIDE
|
||||
{
|
||||
CV_INSTRUMENT_REGION();
|
||||
@@ -3116,16 +3373,18 @@ template<typename ST, class CastOp, class VecOp> struct Filter2D : public BaseFi
|
||||
vecOp = _vecOp;
|
||||
CV_Assert( _kernel.type() == DataType<KT>::type );
|
||||
preprocess2DKernel( _kernel, coords, coeffs );
|
||||
ptrs.resize( coords.size() );
|
||||
}
|
||||
|
||||
bool isStateless() const CV_OVERRIDE { return true; }
|
||||
|
||||
void operator()(const uchar** src, uchar* dst, int dststep, int count, int width, int cn) CV_OVERRIDE
|
||||
{
|
||||
KT _delta = delta;
|
||||
const Point* pt = &coords[0];
|
||||
const KT* kf = (const KT*)&coeffs[0];
|
||||
const ST** kp = (const ST**)&ptrs[0];
|
||||
int i, k, nz = (int)coords.size();
|
||||
AutoBuffer<const ST*> _kp(nz);
|
||||
const ST** kp = _kp.data();
|
||||
CastOp castOp = castOp0;
|
||||
|
||||
width *= cn;
|
||||
@@ -3168,7 +3427,6 @@ template<typename ST, class CastOp, class VecOp> struct Filter2D : public BaseFi
|
||||
|
||||
std::vector<Point> coords;
|
||||
std::vector<uchar> coeffs;
|
||||
std::vector<uchar*> ptrs;
|
||||
KT delta;
|
||||
CastOp castOp0;
|
||||
VecOp vecOp;
|
||||
|
||||
@@ -74,6 +74,8 @@ public:
|
||||
virtual ~BaseRowFilter();
|
||||
//! the filtering operator. Must be overridden in the derived classes. The horizontal border interpolation is done outside of the class.
|
||||
virtual void operator()(const uchar* src, uchar* dst, int width, int cn) = 0;
|
||||
//! returns true if the filter holds no mutable state between calls (safe for concurrent tile-parallel invocation)
|
||||
virtual bool isStateless() const { return false; }
|
||||
|
||||
int ksize;
|
||||
int anchor;
|
||||
@@ -104,6 +106,8 @@ public:
|
||||
virtual void operator()(const uchar** src, uchar* dst, int dststep, int dstcount, int width) = 0;
|
||||
//! resets the internal buffers, if any
|
||||
virtual void reset();
|
||||
//! returns true if the filter holds no mutable state between calls (safe for concurrent tile-parallel invocation)
|
||||
virtual bool isStateless() const { return false; }
|
||||
|
||||
int ksize;
|
||||
int anchor;
|
||||
@@ -132,6 +136,8 @@ public:
|
||||
virtual void operator()(const uchar** src, uchar* dst, int dststep, int dstcount, int width, int cn) = 0;
|
||||
//! resets the internal buffers, if any
|
||||
virtual void reset();
|
||||
//! returns true if the filter holds no mutable state between calls (safe for concurrent tile-parallel invocation)
|
||||
virtual bool isStateless() const { return false; }
|
||||
|
||||
Size ksize;
|
||||
Point anchor;
|
||||
@@ -251,6 +257,9 @@ public:
|
||||
int remainingInputRows() const;
|
||||
int remainingOutputRows() const;
|
||||
|
||||
//! returns true if the engine's filters are entirely stateless and thus safe for 2D parallel block chunking
|
||||
bool isStateless() const;
|
||||
|
||||
int srcType;
|
||||
int dstType;
|
||||
int bufType;
|
||||
|
||||
@@ -1388,6 +1388,27 @@ inline int hal_ni_scharr(const uchar* src_data, size_t src_step, uchar* dst_data
|
||||
#define cv_hal_scharr hal_ni_scharr
|
||||
//! @endcond
|
||||
|
||||
/**
|
||||
@brief Computes Laplacian filter
|
||||
@param src_data Source image data
|
||||
@param src_step Source image step
|
||||
@param dst_data Destination image data
|
||||
@param dst_step Destination image step
|
||||
@param width Source image width
|
||||
@param height Source image height
|
||||
@param src_depth Depth of source image
|
||||
@param dst_depth Depth of destination image
|
||||
@param cn Number of channels
|
||||
@param ksize Kernel size (1, 3, or 5)
|
||||
@param border_type Border type
|
||||
@param border_value Border value for CONSTANT
|
||||
*/
|
||||
inline int hal_ni_laplacian(const uchar* src_data, size_t src_step, uchar* dst_data, size_t dst_step, int width, int height, int src_depth, int dst_depth, int cn, int ksize, int border_type, uchar border_value) { return CV_HAL_ERROR_NOT_IMPLEMENTED;}
|
||||
|
||||
//! @cond IGNORED
|
||||
#define cv_hal_laplacian hal_ni_laplacian
|
||||
//! @endcond
|
||||
|
||||
/**
|
||||
@brief Perform Gaussian Blur and downsampling for input tile.
|
||||
@param depth Depths of source and destination image
|
||||
|
||||
@@ -2352,6 +2352,7 @@ static void warpAffine(int src_type,
|
||||
parallel_for_(range, invoker, dst.total()/(double)(1<<16));
|
||||
}
|
||||
|
||||
CV_DISABLE_UBSAN
|
||||
void warpAffineBlocklineNN(int *adelta, int *bdelta, short* xy, int X0, int Y0, int bw)
|
||||
{
|
||||
CALL_HAL(warpAffineBlocklineNN, cv_hal_warpAffineBlocklineNN, adelta, bdelta, xy, X0, Y0, bw);
|
||||
@@ -2382,6 +2383,7 @@ void warpAffineBlocklineNN(int *adelta, int *bdelta, short* xy, int X0, int Y0,
|
||||
}
|
||||
}
|
||||
|
||||
CV_DISABLE_UBSAN
|
||||
void warpAffineBlockline(int *adelta, int *bdelta, short* xy, short* alpha, int X0, int Y0, int bw)
|
||||
{
|
||||
CALL_HAL(warpAffineBlockline, cv_hal_warpAffineBlockline, adelta, bdelta, xy, alpha, X0, Y0, bw);
|
||||
|
||||
@@ -977,7 +977,6 @@ double LineSegmentDetectorImpl::rect_nfa(const rect& rec) const
|
||||
double top_y = ordered_y[0].y, bottom_y = ordered_y[2].y;
|
||||
|
||||
// Loop around all points in the region and count those that are aligned.
|
||||
std::vector<cv::Point> points;
|
||||
double left_limit, right_limit;
|
||||
for(int y = (int) ceil(top_y); y <= (int) ceil(bottom_y); ++y)
|
||||
{
|
||||
|
||||
@@ -43,6 +43,7 @@
|
||||
#include "precomp.hpp"
|
||||
#include <limits.h>
|
||||
#include "opencv2/core/hal/intrin.hpp"
|
||||
#include "filter.hpp"
|
||||
|
||||
/****************************************************************************************\
|
||||
Basic Morphological Operations: Erosion & Dilation
|
||||
@@ -495,6 +496,8 @@ template<class Op, class VecOp> struct MorphRowFilter : public BaseRowFilter
|
||||
anchor = _anchor;
|
||||
}
|
||||
|
||||
bool isStateless() const CV_OVERRIDE { return true; }
|
||||
|
||||
void operator()(const uchar* src, uchar* dst, int width, int cn) CV_OVERRIDE
|
||||
{
|
||||
CV_INSTRUMENT_REGION();
|
||||
@@ -551,6 +554,8 @@ template<class Op, class VecOp> struct MorphColumnFilter : public BaseColumnFilt
|
||||
anchor = _anchor;
|
||||
}
|
||||
|
||||
bool isStateless() const CV_OVERRIDE { return true; }
|
||||
|
||||
void operator()(const uchar** _src, uchar* dst, int dststep, int count, int width) CV_OVERRIDE
|
||||
{
|
||||
CV_INSTRUMENT_REGION();
|
||||
@@ -638,7 +643,7 @@ template<class Op, class VecOp> struct MorphColumnFilter : public BaseColumnFilt
|
||||
};
|
||||
|
||||
|
||||
template<class Op, class VecOp> struct MorphFilter : BaseFilter
|
||||
template<class Op, class VecOp> struct MorphFilter : public BaseFilter
|
||||
{
|
||||
typedef typename Op::rtype T;
|
||||
|
||||
@@ -651,16 +656,18 @@ template<class Op, class VecOp> struct MorphFilter : BaseFilter
|
||||
std::vector<uchar> coeffs; // we do not really the values of non-zero
|
||||
// kernel elements, just their locations
|
||||
preprocess2DKernel( _kernel, coords, coeffs );
|
||||
ptrs.resize( coords.size() );
|
||||
}
|
||||
|
||||
bool isStateless() const CV_OVERRIDE { return true; }
|
||||
|
||||
void operator()(const uchar** src, uchar* dst, int dststep, int count, int width, int cn) CV_OVERRIDE
|
||||
{
|
||||
CV_INSTRUMENT_REGION();
|
||||
|
||||
const Point* pt = &coords[0];
|
||||
const T** kp = (const T**)&ptrs[0];
|
||||
int i, k, nz = (int)coords.size();
|
||||
AutoBuffer<const T*> _kp(nz);
|
||||
const T** kp = _kp.data();
|
||||
Op op;
|
||||
|
||||
width *= cn;
|
||||
@@ -671,7 +678,7 @@ template<class Op, class VecOp> struct MorphFilter : BaseFilter
|
||||
for( k = 0; k < nz; k++ )
|
||||
kp[k] = (const T*)src[pt[k].y] + pt[k].x*cn;
|
||||
|
||||
i = vecOp(&ptrs[0], nz, dst, width);
|
||||
i = vecOp((uchar**)kp, nz, dst, width);
|
||||
#if CV_ENABLE_UNROLLED
|
||||
for( ; i <= width - 4; i += 4 )
|
||||
{
|
||||
@@ -700,7 +707,6 @@ template<class Op, class VecOp> struct MorphFilter : BaseFilter
|
||||
}
|
||||
|
||||
std::vector<Point> coords;
|
||||
std::vector<uchar*> ptrs;
|
||||
VecOp vecOp;
|
||||
};
|
||||
|
||||
|
||||
@@ -240,8 +240,8 @@ TEST(Imgproc_ConnectedComponents, missing_background_pixels)
|
||||
|
||||
TEST(Imgproc_ConnectedComponents, spaghetti_bbdt_sauf_stats)
|
||||
{
|
||||
cv::Mat1b img(16, 16);
|
||||
img << 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
||||
cv::Mat1b img({16, 16}, {
|
||||
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
||||
0, 1, 1, 1, 0, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0,
|
||||
0, 1, 1, 1, 0, 1, 1, 1, 1, 0, 0, 1, 1, 1, 0, 0,
|
||||
0, 1, 1, 1, 0, 1, 1, 1, 1, 0, 1, 1, 1, 1, 1, 0,
|
||||
@@ -256,7 +256,8 @@ TEST(Imgproc_ConnectedComponents, spaghetti_bbdt_sauf_stats)
|
||||
0, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 1, 1, 1, 0, 1,
|
||||
0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 0, 0, 1,
|
||||
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1,
|
||||
1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1;
|
||||
1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1
|
||||
});
|
||||
|
||||
cv::Mat1i labels;
|
||||
cv::Mat1i stats;
|
||||
@@ -360,68 +361,64 @@ TEST(Imgproc_ConnectedComponents, spaghetti_bbdt_sauf_stats)
|
||||
|
||||
TEST(Imgproc_ConnectedComponents, chessboard_even)
|
||||
{
|
||||
cv::Size size(16, 16);
|
||||
cv::Mat1b input(size);
|
||||
cv::Mat1i output_8c(size);
|
||||
cv::Mat1i output_4c(size);
|
||||
const auto size = {16, 16};
|
||||
cv::Mat1b input(size, {
|
||||
1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0,
|
||||
0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1,
|
||||
1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0,
|
||||
0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1,
|
||||
1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0,
|
||||
0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1,
|
||||
1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0,
|
||||
0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1,
|
||||
1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0,
|
||||
0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1,
|
||||
1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0,
|
||||
0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1,
|
||||
1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0,
|
||||
0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1,
|
||||
1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0,
|
||||
0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1
|
||||
});
|
||||
cv::Mat1i output_8c(size, {
|
||||
1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0,
|
||||
0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1,
|
||||
1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0,
|
||||
0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1,
|
||||
1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0,
|
||||
0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1,
|
||||
1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0,
|
||||
0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1,
|
||||
1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0,
|
||||
0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1,
|
||||
1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0,
|
||||
0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1,
|
||||
1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0,
|
||||
0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1,
|
||||
1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0,
|
||||
0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1
|
||||
});
|
||||
cv::Mat1i output_4c(size, {
|
||||
1, 0, 2, 0, 3, 0, 4, 0, 5, 0, 6, 0, 7, 0, 8, 0,
|
||||
0, 9, 0, 10, 0, 11, 0, 12, 0, 13, 0, 14, 0, 15, 0, 16,
|
||||
17, 0, 18, 0, 19, 0, 20, 0, 21, 0, 22, 0, 23, 0, 24, 0,
|
||||
0, 25, 0, 26, 0, 27, 0, 28, 0, 29, 0, 30, 0, 31, 0, 32,
|
||||
33, 0, 34, 0, 35, 0, 36, 0, 37, 0, 38, 0, 39, 0, 40, 0,
|
||||
0, 41, 0, 42, 0, 43, 0, 44, 0, 45, 0, 46, 0, 47, 0, 48,
|
||||
49, 0, 50, 0, 51, 0, 52, 0, 53, 0, 54, 0, 55, 0, 56, 0,
|
||||
0, 57, 0, 58, 0, 59, 0, 60, 0, 61, 0, 62, 0, 63, 0, 64,
|
||||
65, 0, 66, 0, 67, 0, 68, 0, 69, 0, 70, 0, 71, 0, 72, 0,
|
||||
0, 73, 0, 74, 0, 75, 0, 76, 0, 77, 0, 78, 0, 79, 0, 80,
|
||||
81, 0, 82, 0, 83, 0, 84, 0, 85, 0, 86, 0, 87, 0, 88, 0,
|
||||
0, 89, 0, 90, 0, 91, 0, 92, 0, 93, 0, 94, 0, 95, 0, 96,
|
||||
97, 0, 98, 0, 99, 0, 100, 0, 101, 0, 102, 0, 103, 0, 104, 0,
|
||||
0, 105, 0, 106, 0, 107, 0, 108, 0, 109, 0, 110, 0, 111, 0, 112,
|
||||
113, 0, 114, 0, 115, 0, 116, 0, 117, 0, 118, 0, 119, 0, 120, 0,
|
||||
0, 121, 0, 122, 0, 123, 0, 124, 0, 125, 0, 126, 0, 127, 0, 128
|
||||
});
|
||||
|
||||
// Chessboard image with even number of rows and cols
|
||||
// Note that this is the maximum number of labels for 4-way connectivity
|
||||
{
|
||||
input <<
|
||||
1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0,
|
||||
0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1,
|
||||
1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0,
|
||||
0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1,
|
||||
1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0,
|
||||
0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1,
|
||||
1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0,
|
||||
0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1,
|
||||
1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0,
|
||||
0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1,
|
||||
1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0,
|
||||
0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1,
|
||||
1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0,
|
||||
0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1,
|
||||
1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0,
|
||||
0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1;
|
||||
|
||||
output_8c <<
|
||||
1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0,
|
||||
0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1,
|
||||
1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0,
|
||||
0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1,
|
||||
1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0,
|
||||
0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1,
|
||||
1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0,
|
||||
0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1,
|
||||
1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0,
|
||||
0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1,
|
||||
1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0,
|
||||
0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1,
|
||||
1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0,
|
||||
0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1,
|
||||
1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0,
|
||||
0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1;
|
||||
|
||||
output_4c <<
|
||||
1, 0, 2, 0, 3, 0, 4, 0, 5, 0, 6, 0, 7, 0, 8, 0,
|
||||
0, 9, 0, 10, 0, 11, 0, 12, 0, 13, 0, 14, 0, 15, 0, 16,
|
||||
17, 0, 18, 0, 19, 0, 20, 0, 21, 0, 22, 0, 23, 0, 24, 0,
|
||||
0, 25, 0, 26, 0, 27, 0, 28, 0, 29, 0, 30, 0, 31, 0, 32,
|
||||
33, 0, 34, 0, 35, 0, 36, 0, 37, 0, 38, 0, 39, 0, 40, 0,
|
||||
0, 41, 0, 42, 0, 43, 0, 44, 0, 45, 0, 46, 0, 47, 0, 48,
|
||||
49, 0, 50, 0, 51, 0, 52, 0, 53, 0, 54, 0, 55, 0, 56, 0,
|
||||
0, 57, 0, 58, 0, 59, 0, 60, 0, 61, 0, 62, 0, 63, 0, 64,
|
||||
65, 0, 66, 0, 67, 0, 68, 0, 69, 0, 70, 0, 71, 0, 72, 0,
|
||||
0, 73, 0, 74, 0, 75, 0, 76, 0, 77, 0, 78, 0, 79, 0, 80,
|
||||
81, 0, 82, 0, 83, 0, 84, 0, 85, 0, 86, 0, 87, 0, 88, 0,
|
||||
0, 89, 0, 90, 0, 91, 0, 92, 0, 93, 0, 94, 0, 95, 0, 96,
|
||||
97, 0, 98, 0, 99, 0, 100, 0, 101, 0, 102, 0, 103, 0, 104, 0,
|
||||
0, 105, 0, 106, 0, 107, 0, 108, 0, 109, 0, 110, 0, 111, 0, 112,
|
||||
113, 0, 114, 0, 115, 0, 116, 0, 117, 0, 118, 0, 119, 0, 120, 0,
|
||||
0, 121, 0, 122, 0, 123, 0, 124, 0, 125, 0, 126, 0, 127, 0, 128;
|
||||
}
|
||||
|
||||
int ccltype[] = { cv::CCL_DEFAULT, cv::CCL_WU, cv::CCL_GRANA, cv::CCL_BOLELLI, cv::CCL_SAUF, cv::CCL_BBDT, cv::CCL_SPAGHETTI };
|
||||
|
||||
@@ -448,65 +445,61 @@ TEST(Imgproc_ConnectedComponents, chessboard_even)
|
||||
|
||||
TEST(Imgproc_ConnectedComponents, chessboard_odd)
|
||||
{
|
||||
cv::Size size(15, 15);
|
||||
cv::Mat1b input(size);
|
||||
cv::Mat1i output_8c(size);
|
||||
cv::Mat1i output_4c(size);
|
||||
const auto size = {15, 15};
|
||||
cv::Mat1b input(size, {
|
||||
1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1,
|
||||
0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0,
|
||||
1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1,
|
||||
0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0,
|
||||
1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1,
|
||||
0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0,
|
||||
1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1,
|
||||
0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0,
|
||||
1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1,
|
||||
0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0,
|
||||
1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1,
|
||||
0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0,
|
||||
1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1,
|
||||
0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0,
|
||||
1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1
|
||||
});
|
||||
cv::Mat1i output_8c(size, {
|
||||
1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1,
|
||||
0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0,
|
||||
1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1,
|
||||
0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0,
|
||||
1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1,
|
||||
0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0,
|
||||
1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1,
|
||||
0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0,
|
||||
1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1,
|
||||
0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0,
|
||||
1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1,
|
||||
0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0,
|
||||
1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1,
|
||||
0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0,
|
||||
1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1
|
||||
});
|
||||
cv::Mat1i output_4c(size, {
|
||||
1, 0, 2, 0, 3, 0, 4, 0, 5, 0, 6, 0, 7, 0, 8,
|
||||
0, 9, 0, 10, 0, 11, 0, 12, 0, 13, 0, 14, 0, 15, 0,
|
||||
16, 0, 17, 0, 18, 0, 19, 0, 20, 0, 21, 0, 22, 0, 23,
|
||||
0, 24, 0, 25, 0, 26, 0, 27, 0, 28, 0, 29, 0, 30, 0,
|
||||
31, 0, 32, 0, 33, 0, 34, 0, 35, 0, 36, 0, 37, 0, 38,
|
||||
0, 39, 0, 40, 0, 41, 0, 42, 0, 43, 0, 44, 0, 45, 0,
|
||||
46, 0, 47, 0, 48, 0, 49, 0, 50, 0, 51, 0, 52, 0, 53,
|
||||
0, 54, 0, 55, 0, 56, 0, 57, 0, 58, 0, 59, 0, 60, 0,
|
||||
61, 0, 62, 0, 63, 0, 64, 0, 65, 0, 66, 0, 67, 0, 68,
|
||||
0, 69, 0, 70, 0, 71, 0, 72, 0, 73, 0, 74, 0, 75, 0,
|
||||
76, 0, 77, 0, 78, 0, 79, 0, 80, 0, 81, 0, 82, 0, 83,
|
||||
0, 84, 0, 85, 0, 86, 0, 87, 0, 88, 0, 89, 0, 90, 0,
|
||||
91, 0, 92, 0, 93, 0, 94, 0, 95, 0, 96, 0, 97, 0, 98,
|
||||
0, 99, 0, 100, 0, 101, 0, 102, 0, 103, 0, 104, 0, 105, 0,
|
||||
106, 0, 107, 0, 108, 0, 109, 0, 110, 0, 111, 0, 112, 0, 113
|
||||
});
|
||||
|
||||
// Chessboard image with odd number of rows and cols
|
||||
// Note that this is the maximum number of labels for 4-way connectivity
|
||||
{
|
||||
input <<
|
||||
1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1,
|
||||
0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0,
|
||||
1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1,
|
||||
0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0,
|
||||
1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1,
|
||||
0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0,
|
||||
1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1,
|
||||
0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0,
|
||||
1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1,
|
||||
0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0,
|
||||
1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1,
|
||||
0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0,
|
||||
1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1,
|
||||
0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0,
|
||||
1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1;
|
||||
|
||||
output_8c <<
|
||||
1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1,
|
||||
0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0,
|
||||
1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1,
|
||||
0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0,
|
||||
1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1,
|
||||
0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0,
|
||||
1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1,
|
||||
0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0,
|
||||
1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1,
|
||||
0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0,
|
||||
1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1,
|
||||
0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0,
|
||||
1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1,
|
||||
0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0,
|
||||
1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1;
|
||||
|
||||
output_4c <<
|
||||
1, 0, 2, 0, 3, 0, 4, 0, 5, 0, 6, 0, 7, 0, 8,
|
||||
0, 9, 0, 10, 0, 11, 0, 12, 0, 13, 0, 14, 0, 15, 0,
|
||||
16, 0, 17, 0, 18, 0, 19, 0, 20, 0, 21, 0, 22, 0, 23,
|
||||
0, 24, 0, 25, 0, 26, 0, 27, 0, 28, 0, 29, 0, 30, 0,
|
||||
31, 0, 32, 0, 33, 0, 34, 0, 35, 0, 36, 0, 37, 0, 38,
|
||||
0, 39, 0, 40, 0, 41, 0, 42, 0, 43, 0, 44, 0, 45, 0,
|
||||
46, 0, 47, 0, 48, 0, 49, 0, 50, 0, 51, 0, 52, 0, 53,
|
||||
0, 54, 0, 55, 0, 56, 0, 57, 0, 58, 0, 59, 0, 60, 0,
|
||||
61, 0, 62, 0, 63, 0, 64, 0, 65, 0, 66, 0, 67, 0, 68,
|
||||
0, 69, 0, 70, 0, 71, 0, 72, 0, 73, 0, 74, 0, 75, 0,
|
||||
76, 0, 77, 0, 78, 0, 79, 0, 80, 0, 81, 0, 82, 0, 83,
|
||||
0, 84, 0, 85, 0, 86, 0, 87, 0, 88, 0, 89, 0, 90, 0,
|
||||
91, 0, 92, 0, 93, 0, 94, 0, 95, 0, 96, 0, 97, 0, 98,
|
||||
0, 99, 0, 100, 0, 101, 0, 102, 0, 103, 0, 104, 0, 105, 0,
|
||||
106, 0, 107, 0, 108, 0, 109, 0, 110, 0, 111, 0, 112, 0, 113;
|
||||
}
|
||||
|
||||
int ccltype[] = { cv::CCL_DEFAULT, cv::CCL_WU, cv::CCL_GRANA, cv::CCL_BOLELLI, cv::CCL_SAUF, cv::CCL_BBDT, cv::CCL_SPAGHETTI };
|
||||
|
||||
@@ -533,66 +526,61 @@ TEST(Imgproc_ConnectedComponents, chessboard_odd)
|
||||
|
||||
TEST(Imgproc_ConnectedComponents, maxlabels_8conn_even)
|
||||
{
|
||||
cv::Size size(16, 16);
|
||||
cv::Mat1b input(size);
|
||||
cv::Mat1i output_8c(size);
|
||||
cv::Mat1i output_4c(size);
|
||||
|
||||
{
|
||||
input <<
|
||||
1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
||||
1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
||||
1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
||||
1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
||||
1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
||||
1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
||||
1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
||||
1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0;
|
||||
|
||||
output_8c <<
|
||||
1, 0, 2, 0, 3, 0, 4, 0, 5, 0, 6, 0, 7, 0, 8, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
||||
9, 0, 10, 0, 11, 0, 12, 0, 13, 0, 14, 0, 15, 0, 16, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
||||
17, 0, 18, 0, 19, 0, 20, 0, 21, 0, 22, 0, 23, 0, 24, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
||||
25, 0, 26, 0, 27, 0, 28, 0, 29, 0, 30, 0, 31, 0, 32, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
||||
33, 0, 34, 0, 35, 0, 36, 0, 37, 0, 38, 0, 39, 0, 40, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
||||
41, 0, 42, 0, 43, 0, 44, 0, 45, 0, 46, 0, 47, 0, 48, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
||||
49, 0, 50, 0, 51, 0, 52, 0, 53, 0, 54, 0, 55, 0, 56, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
||||
57, 0, 58, 0, 59, 0, 60, 0, 61, 0, 62, 0, 63, 0, 64, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0;
|
||||
|
||||
output_4c <<
|
||||
1, 0, 2, 0, 3, 0, 4, 0, 5, 0, 6, 0, 7, 0, 8, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
||||
9, 0, 10, 0, 11, 0, 12, 0, 13, 0, 14, 0, 15, 0, 16, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
||||
17, 0, 18, 0, 19, 0, 20, 0, 21, 0, 22, 0, 23, 0, 24, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
||||
25, 0, 26, 0, 27, 0, 28, 0, 29, 0, 30, 0, 31, 0, 32, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
||||
33, 0, 34, 0, 35, 0, 36, 0, 37, 0, 38, 0, 39, 0, 40, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
||||
41, 0, 42, 0, 43, 0, 44, 0, 45, 0, 46, 0, 47, 0, 48, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
||||
49, 0, 50, 0, 51, 0, 52, 0, 53, 0, 54, 0, 55, 0, 56, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
||||
57, 0, 58, 0, 59, 0, 60, 0, 61, 0, 62, 0, 63, 0, 64, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0;
|
||||
}
|
||||
const auto size = {16, 16};
|
||||
cv::Mat1b input(size, {
|
||||
1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
||||
1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
||||
1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
||||
1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
||||
1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
||||
1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
||||
1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
||||
1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0
|
||||
});
|
||||
cv::Mat1i output_8c(size, {
|
||||
1, 0, 2, 0, 3, 0, 4, 0, 5, 0, 6, 0, 7, 0, 8, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
||||
9, 0, 10, 0, 11, 0, 12, 0, 13, 0, 14, 0, 15, 0, 16, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
||||
17, 0, 18, 0, 19, 0, 20, 0, 21, 0, 22, 0, 23, 0, 24, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
||||
25, 0, 26, 0, 27, 0, 28, 0, 29, 0, 30, 0, 31, 0, 32, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
||||
33, 0, 34, 0, 35, 0, 36, 0, 37, 0, 38, 0, 39, 0, 40, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
||||
41, 0, 42, 0, 43, 0, 44, 0, 45, 0, 46, 0, 47, 0, 48, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
||||
49, 0, 50, 0, 51, 0, 52, 0, 53, 0, 54, 0, 55, 0, 56, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
||||
57, 0, 58, 0, 59, 0, 60, 0, 61, 0, 62, 0, 63, 0, 64, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0
|
||||
});
|
||||
cv::Mat1i output_4c(size, {
|
||||
1, 0, 2, 0, 3, 0, 4, 0, 5, 0, 6, 0, 7, 0, 8, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
||||
9, 0, 10, 0, 11, 0, 12, 0, 13, 0, 14, 0, 15, 0, 16, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
||||
17, 0, 18, 0, 19, 0, 20, 0, 21, 0, 22, 0, 23, 0, 24, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
||||
25, 0, 26, 0, 27, 0, 28, 0, 29, 0, 30, 0, 31, 0, 32, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
||||
33, 0, 34, 0, 35, 0, 36, 0, 37, 0, 38, 0, 39, 0, 40, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
||||
41, 0, 42, 0, 43, 0, 44, 0, 45, 0, 46, 0, 47, 0, 48, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
||||
49, 0, 50, 0, 51, 0, 52, 0, 53, 0, 54, 0, 55, 0, 56, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
||||
57, 0, 58, 0, 59, 0, 60, 0, 61, 0, 62, 0, 63, 0, 64, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0
|
||||
});
|
||||
|
||||
int ccltype[] = { cv::CCL_DEFAULT, cv::CCL_WU, cv::CCL_GRANA, cv::CCL_BOLELLI, cv::CCL_SAUF, cv::CCL_BBDT, cv::CCL_SPAGHETTI };
|
||||
|
||||
@@ -619,63 +607,58 @@ TEST(Imgproc_ConnectedComponents, maxlabels_8conn_even)
|
||||
|
||||
TEST(Imgproc_ConnectedComponents, maxlabels_8conn_odd)
|
||||
{
|
||||
cv::Size size(15, 15);
|
||||
cv::Mat1b input(size);
|
||||
cv::Mat1i output_8c(size);
|
||||
cv::Mat1i output_4c(size);
|
||||
|
||||
{
|
||||
input <<
|
||||
1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1,
|
||||
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
||||
1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1,
|
||||
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
||||
1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1,
|
||||
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
||||
1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1,
|
||||
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
||||
1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1,
|
||||
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
||||
1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1,
|
||||
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
||||
1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1,
|
||||
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
||||
1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1;
|
||||
|
||||
output_8c <<
|
||||
1, 0, 2, 0, 3, 0, 4, 0, 5, 0, 6, 0, 7, 0, 8,
|
||||
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
||||
9, 0, 10, 0, 11, 0, 12, 0, 13, 0, 14, 0, 15, 0, 16,
|
||||
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
||||
17, 0, 18, 0, 19, 0, 20, 0, 21, 0, 22, 0, 23, 0, 24,
|
||||
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
||||
25, 0, 26, 0, 27, 0, 28, 0, 29, 0, 30, 0, 31, 0, 32,
|
||||
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
||||
33, 0, 34, 0, 35, 0, 36, 0, 37, 0, 38, 0, 39, 0, 40,
|
||||
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
||||
41, 0, 42, 0, 43, 0, 44, 0, 45, 0, 46, 0, 47, 0, 48,
|
||||
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
||||
49, 0, 50, 0, 51, 0, 52, 0, 53, 0, 54, 0, 55, 0, 56,
|
||||
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
||||
57, 0, 58, 0, 59, 0, 60, 0, 61, 0, 62, 0, 63, 0, 64;
|
||||
|
||||
output_4c <<
|
||||
1, 0, 2, 0, 3, 0, 4, 0, 5, 0, 6, 0, 7, 0, 8,
|
||||
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
||||
9, 0, 10, 0, 11, 0, 12, 0, 13, 0, 14, 0, 15, 0, 16,
|
||||
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
||||
17, 0, 18, 0, 19, 0, 20, 0, 21, 0, 22, 0, 23, 0, 24,
|
||||
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
||||
25, 0, 26, 0, 27, 0, 28, 0, 29, 0, 30, 0, 31, 0, 32,
|
||||
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
||||
33, 0, 34, 0, 35, 0, 36, 0, 37, 0, 38, 0, 39, 0, 40,
|
||||
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
||||
41, 0, 42, 0, 43, 0, 44, 0, 45, 0, 46, 0, 47, 0, 48,
|
||||
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
||||
49, 0, 50, 0, 51, 0, 52, 0, 53, 0, 54, 0, 55, 0, 56,
|
||||
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
||||
57, 0, 58, 0, 59, 0, 60, 0, 61, 0, 62, 0, 63, 0, 64;
|
||||
}
|
||||
const auto size = {15, 15};
|
||||
cv::Mat1b input(size, {
|
||||
1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1,
|
||||
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
||||
1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1,
|
||||
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
||||
1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1,
|
||||
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
||||
1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1,
|
||||
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
||||
1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1,
|
||||
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
||||
1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1,
|
||||
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
||||
1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1,
|
||||
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
||||
1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1
|
||||
});
|
||||
cv::Mat1i output_8c(size, {
|
||||
1, 0, 2, 0, 3, 0, 4, 0, 5, 0, 6, 0, 7, 0, 8,
|
||||
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
||||
9, 0, 10, 0, 11, 0, 12, 0, 13, 0, 14, 0, 15, 0, 16,
|
||||
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
||||
17, 0, 18, 0, 19, 0, 20, 0, 21, 0, 22, 0, 23, 0, 24,
|
||||
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
||||
25, 0, 26, 0, 27, 0, 28, 0, 29, 0, 30, 0, 31, 0, 32,
|
||||
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
||||
33, 0, 34, 0, 35, 0, 36, 0, 37, 0, 38, 0, 39, 0, 40,
|
||||
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
||||
41, 0, 42, 0, 43, 0, 44, 0, 45, 0, 46, 0, 47, 0, 48,
|
||||
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
||||
49, 0, 50, 0, 51, 0, 52, 0, 53, 0, 54, 0, 55, 0, 56,
|
||||
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
||||
57, 0, 58, 0, 59, 0, 60, 0, 61, 0, 62, 0, 63, 0, 64
|
||||
});
|
||||
cv::Mat1i output_4c(size, {
|
||||
1, 0, 2, 0, 3, 0, 4, 0, 5, 0, 6, 0, 7, 0, 8,
|
||||
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
||||
9, 0, 10, 0, 11, 0, 12, 0, 13, 0, 14, 0, 15, 0, 16,
|
||||
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
||||
17, 0, 18, 0, 19, 0, 20, 0, 21, 0, 22, 0, 23, 0, 24,
|
||||
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
||||
25, 0, 26, 0, 27, 0, 28, 0, 29, 0, 30, 0, 31, 0, 32,
|
||||
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
||||
33, 0, 34, 0, 35, 0, 36, 0, 37, 0, 38, 0, 39, 0, 40,
|
||||
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
||||
41, 0, 42, 0, 43, 0, 44, 0, 45, 0, 46, 0, 47, 0, 48,
|
||||
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
||||
49, 0, 50, 0, 51, 0, 52, 0, 53, 0, 54, 0, 55, 0, 56,
|
||||
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
||||
57, 0, 58, 0, 59, 0, 60, 0, 61, 0, 62, 0, 63, 0, 64
|
||||
});
|
||||
|
||||
int ccltype[] = { cv::CCL_DEFAULT, cv::CCL_WU, cv::CCL_GRANA, cv::CCL_BOLELLI, cv::CCL_SAUF, cv::CCL_BBDT, cv::CCL_SPAGHETTI };
|
||||
|
||||
@@ -702,24 +685,10 @@ TEST(Imgproc_ConnectedComponents, maxlabels_8conn_odd)
|
||||
|
||||
TEST(Imgproc_ConnectedComponents, single_row)
|
||||
{
|
||||
cv::Size size(1, 15);
|
||||
cv::Mat1b input(size);
|
||||
cv::Mat1i output_8c(size);
|
||||
cv::Mat1i output_4c(size);
|
||||
|
||||
{
|
||||
input <<
|
||||
1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1;
|
||||
|
||||
|
||||
output_8c <<
|
||||
1, 0, 2, 0, 3, 0, 4, 0, 5, 0, 6, 0, 7, 0, 8;
|
||||
|
||||
|
||||
output_4c <<
|
||||
1, 0, 2, 0, 3, 0, 4, 0, 5, 0, 6, 0, 7, 0, 8;
|
||||
|
||||
}
|
||||
const auto size = {1, 15};
|
||||
cv::Mat1b input(size, {1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1});
|
||||
cv::Mat1i output_8c(size, {1, 0, 2, 0, 3, 0, 4, 0, 5, 0, 6, 0, 7, 0, 8});
|
||||
cv::Mat1i output_4c(size, {1, 0, 2, 0, 3, 0, 4, 0, 5, 0, 6, 0, 7, 0, 8});
|
||||
|
||||
int ccltype[] = { cv::CCL_DEFAULT, cv::CCL_WU, cv::CCL_GRANA, cv::CCL_BOLELLI, cv::CCL_SAUF, cv::CCL_BBDT, cv::CCL_SPAGHETTI };
|
||||
|
||||
@@ -746,24 +715,10 @@ TEST(Imgproc_ConnectedComponents, single_row)
|
||||
|
||||
TEST(Imgproc_ConnectedComponents, single_column)
|
||||
{
|
||||
cv::Size size(15, 1);
|
||||
cv::Mat1b input(size);
|
||||
cv::Mat1i output_8c(size);
|
||||
cv::Mat1i output_4c(size);
|
||||
|
||||
{
|
||||
input <<
|
||||
1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1;
|
||||
|
||||
|
||||
output_8c <<
|
||||
1, 0, 2, 0, 3, 0, 4, 0, 5, 0, 6, 0, 7, 0, 8;
|
||||
|
||||
|
||||
output_4c <<
|
||||
1, 0, 2, 0, 3, 0, 4, 0, 5, 0, 6, 0, 7, 0, 8;
|
||||
|
||||
}
|
||||
const auto size = {15, 1};
|
||||
cv::Mat1b input(size, {1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1});
|
||||
cv::Mat1i output_8c(size, {1, 0, 2, 0, 3, 0, 4, 0, 5, 0, 6, 0, 7, 0, 8});
|
||||
cv::Mat1i output_4c(size, {1, 0, 2, 0, 3, 0, 4, 0, 5, 0, 6, 0, 7, 0, 8});
|
||||
|
||||
int ccltype[] = { cv::CCL_DEFAULT, cv::CCL_WU, cv::CCL_GRANA, cv::CCL_BOLELLI, cv::CCL_SAUF, cv::CCL_BBDT, cv::CCL_SPAGHETTI };
|
||||
|
||||
|
||||
@@ -0,0 +1,254 @@
|
||||
// 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 {
|
||||
|
||||
//helps to temporarily change the number of threads and restore it back after the scope
|
||||
struct CvNThreadScope{
|
||||
int nprev;
|
||||
CvNThreadScope(int n){
|
||||
nprev=cv::getNumThreads();
|
||||
cv::setNumThreads(n);
|
||||
}
|
||||
~CvNThreadScope(){
|
||||
cv::setNumThreads(nprev);
|
||||
}
|
||||
};
|
||||
|
||||
// Order-independent contour-set comparison
|
||||
static bool trucoContoursMatch(const vector<vector<Point>>& cont1, const vector<vector<Point>>& cont2)
|
||||
{
|
||||
//order senstive hash
|
||||
auto Hash=[](const std::vector<cv::Point>& contour) {
|
||||
// FNV-1a 64-bit hash constants
|
||||
constexpr uint64_t FNV_OFFSET = 1469598103934665603ULL;
|
||||
constexpr uint64_t FNV_PRIME = 1099511628211ULL;
|
||||
|
||||
uint64_t hash = FNV_OFFSET;
|
||||
|
||||
// Mix in the size so that contours with different lengths
|
||||
// but same prefix produce different hashes
|
||||
uint64_t size = static_cast<uint64_t>(contour.size());
|
||||
for (int i = 0; i < 8; ++i) {
|
||||
hash ^= (size >> (i * 8)) & 0xFF;
|
||||
hash *= FNV_PRIME;
|
||||
}
|
||||
|
||||
// Mix in each point's x and y coordinates byte by byte
|
||||
for (const cv::Point& p : contour) {
|
||||
uint32_t x = static_cast<uint32_t>(p.x);
|
||||
uint32_t y = static_cast<uint32_t>(p.y);
|
||||
|
||||
for (int i = 0; i < 4; ++i) {
|
||||
hash ^= (x >> (i * 8)) & 0xFF;
|
||||
hash *= FNV_PRIME;
|
||||
}
|
||||
for (int i = 0; i < 4; ++i) {
|
||||
hash ^= (y >> (i * 8)) & 0xFF;
|
||||
hash *= FNV_PRIME;
|
||||
}
|
||||
}
|
||||
return hash;
|
||||
};
|
||||
std::set<uint64> hashes1,hashes2;
|
||||
for(auto &contour:cont1){
|
||||
hashes1.insert( Hash(contour));
|
||||
}
|
||||
for(auto &contour:cont2){
|
||||
hashes2.insert( Hash(contour));
|
||||
}
|
||||
|
||||
for(auto &h1:hashes1){//element in cont and not in cont2
|
||||
if( hashes2.find(h1) ==hashes2.end()) return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
typedef testing::TestWithParam<ContourApproximationModes> Imgproc_FindTRUContours;
|
||||
|
||||
TEST_P(Imgproc_FindTRUContours, nthreads_consistency)
|
||||
{
|
||||
ContourApproximationModes method = GetParam();
|
||||
const Size sz(1000, 1000);
|
||||
RNG& rng = TS::ptr()->get_rng();
|
||||
Mat noise(sz, CV_8UC1);
|
||||
cvtest::randUni(rng, noise, 0, 255);
|
||||
Mat blurred;
|
||||
boxFilter(noise, blurred, CV_8U, Size(5, 5));
|
||||
Mat img;
|
||||
cv::threshold(blurred, img, 128, 255, THRESH_BINARY);
|
||||
|
||||
vector<vector<Point>> ref_contours;
|
||||
vector<vector<Point>> ref_contours_m0;
|
||||
{
|
||||
CvNThreadScope nt(1);
|
||||
findContours(img, ref_contours, RETR_LIST, method);
|
||||
}
|
||||
|
||||
std::vector<int> thread_counts;
|
||||
for(int i=2;i<40;i++) thread_counts.push_back(i);
|
||||
for (int t : thread_counts)
|
||||
{
|
||||
SCOPED_TRACE(cv::format("nthreads=%d method=%d", t, (int)method));
|
||||
CvNThreadScope nt(t);
|
||||
vector<vector<Point>> contours;
|
||||
findContours(img, contours, RETR_LIST, method); //will use TRUCO because NOT using hierarchy AND RETR_LIST
|
||||
auto match=trucoContoursMatch(ref_contours, contours);
|
||||
EXPECT_TRUE(match);
|
||||
}
|
||||
}
|
||||
|
||||
TEST_P(Imgproc_FindTRUContours, circles_vs_standard)
|
||||
{
|
||||
ContourApproximationModes method = GetParam();
|
||||
const Size sz(4000, 4000);
|
||||
const int ITER = cvtest::debugLevel >= 10?100:10;
|
||||
const int NUM_CIRCLES = 250;
|
||||
RNG& rng = TS::ptr()->get_rng();
|
||||
|
||||
for (int iter = 0; iter < ITER; ++iter)
|
||||
{
|
||||
SCOPED_TRACE(cv::format("iter=%d method=%d", iter, (int)method));
|
||||
Mat img(sz, CV_8UC1, Scalar::all(0));
|
||||
for (int i = 0; i < NUM_CIRCLES; ++i)
|
||||
{
|
||||
Point center(rng.uniform(50, sz.width - 50),
|
||||
rng.uniform(50, sz.height - 50));
|
||||
int radius = rng.uniform(10, 150);
|
||||
circle(img, center, radius, Scalar::all(255), FILLED);
|
||||
}
|
||||
Mat binary;
|
||||
adaptiveThreshold(img, binary, 255, ADAPTIVE_THRESH_MEAN_C, THRESH_BINARY, 11, 0);
|
||||
|
||||
vector<vector<Point>> ref_contours;
|
||||
vector<Vec4i> hierarchy;
|
||||
findContours(binary, ref_contours, hierarchy, RETR_LIST, method); //will call suzuki abe because using hierarchy
|
||||
EXPECT_TRUE(!hierarchy.empty());
|
||||
vector<vector<Point>> truco_contours;
|
||||
findContours(binary, truco_contours, RETR_LIST, method);
|
||||
EXPECT_TRUE(trucoContoursMatch(ref_contours, truco_contours)); //will use TRUCO because NOT using hierarchy AND RETR_LIST
|
||||
}
|
||||
}
|
||||
|
||||
TEST_P(Imgproc_FindTRUContours, noise_threshold)
|
||||
{
|
||||
ContourApproximationModes method = GetParam();
|
||||
const Size sz(1500, 1500);
|
||||
RNG& rng = TS::ptr()->get_rng();
|
||||
const int levels[] = {86, 128, 170};
|
||||
const int ITER = 2;
|
||||
|
||||
std::vector<int> thread_counts;
|
||||
for(int i=2; i<40; i+=3) thread_counts.push_back(i);
|
||||
for(int i=0; i<ITER; i++)
|
||||
{
|
||||
for (int level : levels)
|
||||
{
|
||||
SCOPED_TRACE(cv::format("level=%d method=%d", level, (int)method));
|
||||
Mat noise(sz, CV_8UC1);
|
||||
cvtest::randUni(rng, noise, 0, 255);
|
||||
Mat blurred;
|
||||
boxFilter(noise, blurred, CV_8U, Size(5, 5));
|
||||
Mat binary;
|
||||
cv::threshold(blurred, binary, level, 255, THRESH_BINARY);
|
||||
|
||||
vector<vector<Point>> ref_contours;
|
||||
vector<Vec4i> hierarchy;
|
||||
findContours(binary, ref_contours, hierarchy, RETR_LIST, method);//will call suzuki&abe because using hierarchy
|
||||
EXPECT_TRUE(!hierarchy.empty());
|
||||
for(auto nt: thread_counts){
|
||||
CvNThreadScope ts(nt);
|
||||
vector<vector<Point>> truco_contours;
|
||||
findContours(binary, truco_contours, RETR_LIST, method);//will call TRUCO abe because NOT using hierarchy
|
||||
EXPECT_TRUE(trucoContoursMatch(ref_contours, truco_contours));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
TEST_P(Imgproc_FindTRUContours, nested_rectangles)
|
||||
{
|
||||
ContourApproximationModes method = GetParam();
|
||||
const int DIM = 1500;
|
||||
const Size sz(DIM, DIM);
|
||||
const int NUM = 25;
|
||||
Mat img(sz, CV_8UC1, Scalar::all(0));
|
||||
Rect rect(1, 1, DIM - 2, DIM - 2);
|
||||
for (int i = 0; i < NUM; ++i)
|
||||
{
|
||||
rectangle(img, rect, Scalar::all(255));
|
||||
rect.x += 10;
|
||||
rect.y += 10;
|
||||
rect.width -= 20;
|
||||
rect.height -= 20;
|
||||
if (rect.width <= 0 || rect.height <= 0)
|
||||
break;
|
||||
}
|
||||
|
||||
vector<vector<Point>> ref_contours;
|
||||
vector<Vec4i> hierarchy;
|
||||
findContours(img, ref_contours, hierarchy, RETR_LIST, method);//will call suzuki abe because using hierarchy
|
||||
EXPECT_TRUE(!hierarchy.empty());
|
||||
|
||||
vector<vector<Point>> truco_contours;
|
||||
findContours(img, truco_contours, RETR_LIST, method);//will use TRUCO because NOT using hierarchy AND RETR_LIST
|
||||
|
||||
EXPECT_TRUE(trucoContoursMatch(ref_contours, truco_contours));
|
||||
}
|
||||
|
||||
TEST_P(Imgproc_FindTRUContours, mixed_figures)
|
||||
{
|
||||
ContourApproximationModes method = GetParam();
|
||||
const Size sz(1800, 1600);
|
||||
RNG& rng = TS::ptr()->get_rng();
|
||||
const int ITER = cvtest::debugLevel >= 10?100:10;
|
||||
|
||||
|
||||
for (int iter = 0; iter < ITER; ++iter)
|
||||
{
|
||||
SCOPED_TRACE(cv::format("iter=%d method=%d", iter, (int)method));
|
||||
Mat img(sz, CV_8UC1, Scalar::all(0));
|
||||
for (int i = 0; i < 5; ++i)
|
||||
{
|
||||
Rect r(rng.uniform(10, sz.width / 2),
|
||||
rng.uniform(10, sz.height / 2),
|
||||
rng.uniform(20, 100),
|
||||
rng.uniform(20, 100));
|
||||
r &= Rect(0, 0, sz.width - 1, sz.height - 1);
|
||||
rectangle(img, r, Scalar::all(255), FILLED);
|
||||
}
|
||||
for (int i = 0; i < 5; ++i)
|
||||
{
|
||||
Point center(rng.uniform(50, sz.width - 50),
|
||||
rng.uniform(50, sz.height - 50));
|
||||
int radius = rng.uniform(10, 50);
|
||||
circle(img, center, radius, Scalar::all(255), FILLED);
|
||||
}
|
||||
for (int i = 0; i < 3; ++i)
|
||||
{
|
||||
Point pts[3];
|
||||
for (auto& p : pts)
|
||||
p = Point(rng.uniform(10, sz.width - 10),
|
||||
rng.uniform(10, sz.height - 10));
|
||||
const Point* ppts = pts;
|
||||
int npts = 3;
|
||||
fillPoly(img, &ppts, &npts, 1, Scalar::all(255));
|
||||
}
|
||||
vector<vector<Point>> ref_contours;
|
||||
vector<Vec4i> hierarchy;
|
||||
findContours(img, ref_contours, hierarchy, RETR_LIST, method);//will call suzuki abe because using hierarchy
|
||||
EXPECT_TRUE(!hierarchy.empty());
|
||||
vector<vector<Point>> truco_contours;
|
||||
findContours(img, truco_contours, RETR_LIST, method);//will use TRUCO because NOT using hierarchy AND RETR_LIST
|
||||
|
||||
EXPECT_TRUE(trucoContoursMatch(ref_contours, truco_contours));
|
||||
}
|
||||
}
|
||||
|
||||
INSTANTIATE_TEST_CASE_P(Imgproc, Imgproc_FindTRUContours,
|
||||
testing::Values(CHAIN_APPROX_NONE,CHAIN_APPROX_SIMPLE, CHAIN_APPROX_TC89_L1, CHAIN_APPROX_TC89_KCOS));
|
||||
|
||||
}} // namespace
|
||||
@@ -1247,4 +1247,207 @@ INSTANTIATE_TEST_CASE_P(/**/, Imgproc_sepFilter2D_types,
|
||||
testing::Values(CV_16S, CV_32F, CV_64F),
|
||||
);
|
||||
|
||||
// Verify that the tiled parallel FilterEngine path produces bit-exact results
|
||||
// compared to the sequential (single-threaded) path for large images.
|
||||
|
||||
typedef tuple<Size, int, int, int> ParallelFilterParams;
|
||||
typedef TestWithParam<ParallelFilterParams> ImgProc_ParallelFilter;
|
||||
|
||||
static void runFilter(const Mat& src, Mat& dst, int borderType, bool isSep)
|
||||
{
|
||||
if (isSep)
|
||||
{
|
||||
Mat kx = (Mat_<float>(1, 3) << 0.25f, 0.5f, 0.25f);
|
||||
Mat ky = (Mat_<float>(3, 1) << 0.25f, 0.5f, 0.25f);
|
||||
cv::sepFilter2D(src, dst, -1, kx, ky, Point(-1, -1), 0, borderType);
|
||||
}
|
||||
else
|
||||
{
|
||||
Mat kernel = (Mat_<float>(3, 3) <<
|
||||
1/16.f, 2/16.f, 1/16.f,
|
||||
2/16.f, 4/16.f, 2/16.f,
|
||||
1/16.f, 2/16.f, 1/16.f);
|
||||
cv::filter2D(src, dst, -1, kernel, Point(-1, -1), 0, borderType);
|
||||
}
|
||||
}
|
||||
|
||||
class ScopedThreadsGuard
|
||||
{
|
||||
public:
|
||||
ScopedThreadsGuard() : old_threads(getNumThreads()) {}
|
||||
~ScopedThreadsGuard() { setNumThreads(old_threads); }
|
||||
void set(int n) { setNumThreads(n); }
|
||||
private:
|
||||
int old_threads;
|
||||
};
|
||||
|
||||
TEST_P(ImgProc_ParallelFilter, accuracy)
|
||||
{
|
||||
const Size sz = get<0>(GetParam());
|
||||
const int type = get<1>(GetParam());
|
||||
const int borderType = get<2>(GetParam());
|
||||
const bool isSep = get<3>(GetParam()) != 0;
|
||||
|
||||
Mat src(sz, type);
|
||||
randu(src, 0, 256);
|
||||
|
||||
ScopedThreadsGuard threadsGuard;
|
||||
const int prev_threads = getNumThreads();
|
||||
|
||||
// Parallel run — use at least 2 threads to exercise the tiled path.
|
||||
threadsGuard.set(std::max(2, prev_threads));
|
||||
Mat dst_par;
|
||||
runFilter(src, dst_par, borderType, isSep);
|
||||
|
||||
// Sequential reference.
|
||||
threadsGuard.set(1);
|
||||
Mat dst_seq;
|
||||
runFilter(src, dst_seq, borderType, isSep);
|
||||
|
||||
Mat diff;
|
||||
double max_err = 0;
|
||||
absdiff(dst_par, dst_seq, diff);
|
||||
minMaxLoc(diff.reshape(1), nullptr, &max_err);
|
||||
EXPECT_EQ(0.0, max_err) << "Parallel and sequential filter results differ";
|
||||
}
|
||||
|
||||
INSTANTIATE_TEST_CASE_P(FullImage, ImgProc_ParallelFilter,
|
||||
Combine(
|
||||
Values(Size(1200, 1200), Size(2000, 1000)),
|
||||
Values(CV_8UC1, CV_8UC3, CV_32FC1),
|
||||
Values(BORDER_DEFAULT, BORDER_CONSTANT),
|
||||
Values(0, 1) // 0 = filter2D, 1 = sepFilter2D
|
||||
)
|
||||
);
|
||||
|
||||
// Verify compound morphological operations (in-place second pass) are bitexact.
|
||||
TEST(ImgProc_ParallelFilter, morphology_compound)
|
||||
{
|
||||
const Size sz(1920, 1080);
|
||||
const int types[] = { CV_8UC1, CV_8UC3 };
|
||||
const int morphOps[] = { MORPH_OPEN, MORPH_CLOSE, MORPH_TOPHAT, MORPH_BLACKHAT };
|
||||
|
||||
for (int ti = 0; ti < 2; ti++)
|
||||
{
|
||||
Mat src(sz, types[ti]);
|
||||
randu(src, 0, 256);
|
||||
|
||||
for (int oi = 0; oi < 4; oi++)
|
||||
{
|
||||
ScopedThreadsGuard threadsGuard;
|
||||
threadsGuard.set(std::max(2, getNumThreads()));
|
||||
Mat dst_par;
|
||||
cv::morphologyEx(src, dst_par, morphOps[oi], Mat());
|
||||
|
||||
threadsGuard.set(1);
|
||||
Mat dst_seq;
|
||||
cv::morphologyEx(src, dst_seq, morphOps[oi], Mat());
|
||||
|
||||
Mat diff;
|
||||
double max_err = 0;
|
||||
absdiff(dst_par, dst_seq, diff);
|
||||
minMaxLoc(diff.reshape(1), nullptr, &max_err);
|
||||
EXPECT_EQ(0.0, max_err)
|
||||
<< "morphOp=" << morphOps[oi]
|
||||
<< " type=" << types[ti]
|
||||
<< ": parallel vs sequential results differ";
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Regression test for ndsrvp HAL filter padding robustness:
|
||||
// Exercises extreme-but-valid anchor positions with small images and large kernels
|
||||
// to ensure HAL implementations handle boundary-dominated padding correctly.
|
||||
TEST(Imgproc_Filter2D, padding_bounds_extreme_anchor)
|
||||
{
|
||||
// Case 1: 1x1 image, large kernel, anchor at far right
|
||||
{
|
||||
Mat src = (Mat_<uchar>(1, 1) << 128);
|
||||
Mat kernel = Mat::ones(1, 7, CV_32F) / 7.0f;
|
||||
Mat dst;
|
||||
Point anchor(6, 0);
|
||||
EXPECT_NO_THROW(cv::filter2D(src, dst, -1, kernel, anchor, 0, BORDER_REPLICATE));
|
||||
EXPECT_EQ(dst.size(), src.size());
|
||||
EXPECT_NEAR(dst.at<uchar>(0, 0), 128, 1);
|
||||
}
|
||||
|
||||
// Case 2: 1x1 image, large kernel, anchor at far left
|
||||
{
|
||||
Mat src = (Mat_<uchar>(1, 1) << 200);
|
||||
Mat kernel = Mat::ones(1, 9, CV_32F) / 9.0f;
|
||||
Mat dst;
|
||||
Point anchor(0, 0);
|
||||
EXPECT_NO_THROW(cv::filter2D(src, dst, -1, kernel, anchor, 0, BORDER_REPLICATE));
|
||||
EXPECT_EQ(dst.size(), src.size());
|
||||
EXPECT_NEAR(dst.at<uchar>(0, 0), 200, 1);
|
||||
}
|
||||
|
||||
// Case 3: 2x2 image, 11x11 kernel, various anchors
|
||||
{
|
||||
Mat src = (Mat_<uchar>(2, 2) << 100, 150, 200, 250);
|
||||
Mat kernel = Mat::ones(11, 11, CV_32F) / 121.0f;
|
||||
Mat dst;
|
||||
for (int ax : {0, 5, 10}) {
|
||||
for (int ay : {0, 5, 10}) {
|
||||
Point anchor(ax, ay);
|
||||
EXPECT_NO_THROW(cv::filter2D(src, dst, -1, kernel, anchor, 0, BORDER_REPLICATE));
|
||||
EXPECT_EQ(dst.size(), src.size());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Case 4: ROI near edge of larger image (non-zero offset)
|
||||
{
|
||||
Mat full(10, 10, CV_8UC1, Scalar(100));
|
||||
Mat roi = full(Rect(8, 8, 2, 2));
|
||||
Mat kernel = Mat::ones(5, 5, CV_32F) / 25.0f;
|
||||
Mat dst;
|
||||
EXPECT_NO_THROW(cv::filter2D(roi, dst, -1, kernel, Point(4, 4), 0, BORDER_REPLICATE));
|
||||
EXPECT_EQ(dst.size(), roi.size());
|
||||
EXPECT_NO_THROW(cv::filter2D(roi, dst, -1, kernel, Point(0, 0), 0, BORDER_REPLICATE));
|
||||
EXPECT_EQ(dst.size(), roi.size());
|
||||
}
|
||||
|
||||
// Case 5: all border types with all valid anchors for wide kernel on narrow image
|
||||
{
|
||||
Mat src = (Mat_<uchar>(1, 3) << 10, 20, 30);
|
||||
Mat kernel = Mat::ones(1, 15, CV_32F) / 15.0f;
|
||||
Mat dst;
|
||||
int borderTypes[] = {BORDER_REPLICATE, BORDER_REFLECT, BORDER_REFLECT_101, BORDER_CONSTANT};
|
||||
for (int bt : borderTypes) {
|
||||
for (int ax = 0; ax < 15; ax++) {
|
||||
EXPECT_NO_THROW(cv::filter2D(src, dst, -1, kernel, Point(ax, 0), 0, bt))
|
||||
<< "borderType=" << bt << " anchor_x=" << ax;
|
||||
EXPECT_EQ(dst.size(), src.size());
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Regression test: small ROI with BORDER_ISOLATED and kernel larger than ROI width.
|
||||
// The HAL must handle the case where border regions dominate the center span.
|
||||
TEST(Imgproc_Filter2D, padding_bounds_roi_isolated)
|
||||
{
|
||||
Mat full(20, 20, CV_8UC1, Scalar(100));
|
||||
Mat roi = full(Rect(5, 5, 3, 3));
|
||||
roi.setTo(Scalar(200));
|
||||
|
||||
Mat kernel = Mat::ones(7, 7, CV_32F) / 49.0f;
|
||||
Mat dst;
|
||||
|
||||
for (int ax = 0; ax < 7; ax++) {
|
||||
for (int ay = 0; ay < 7; ay++) {
|
||||
EXPECT_NO_THROW(
|
||||
cv::filter2D(roi, dst, -1, kernel, Point(ax, ay), 0,
|
||||
BORDER_REPLICATE | BORDER_ISOLATED))
|
||||
<< "anchor=(" << ax << "," << ay << ")";
|
||||
EXPECT_EQ(dst.size(), roi.size());
|
||||
double minv, maxv;
|
||||
minMaxLoc(dst, &minv, &maxv);
|
||||
EXPECT_NEAR(minv, 200, 1) << "anchor=(" << ax << "," << ay << ")";
|
||||
EXPECT_NEAR(maxv, 200, 1) << "anchor=(" << ax << "," << ay << ")";
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}} // namespace
|
||||
|
||||
@@ -18,6 +18,7 @@ endforeach(m)
|
||||
|
||||
# header blacklist
|
||||
ocv_list_filterout(opencv_hdrs "modules/.*.h$")
|
||||
ocv_list_filterout(opencv_hdrs "modules/calib3d/include/opencv2/calib3d/private.hpp")
|
||||
ocv_list_filterout(opencv_hdrs "modules/core/include/opencv2/core/fast_math.hpp")
|
||||
ocv_list_filterout(opencv_hdrs "modules/core/.*/cuda")
|
||||
ocv_list_filterout(opencv_hdrs "modules/core/.*/opencl")
|
||||
|
||||
@@ -0,0 +1,69 @@
|
||||
|
||||
This is the documentation for the iOS OpenCV framework.
|
||||
|
||||
## Quick Start Tutorial
|
||||
|
||||
Follow the steps below to get a simple "Hello World" app running on iOS
|
||||
|
||||
* Open Xcode and create a new project from the **File** > **New** > **Project...** menu
|
||||
|
||||

|
||||
|
||||
* From template selection dialog select the `iOS` tab and then select `App`
|
||||
|
||||

|
||||
|
||||
* Enter a name for the project and set **Interface** to **Storyboard** and **Language** to **Swift**
|
||||
|
||||

|
||||
|
||||
* Choose a location to save the project
|
||||
|
||||

|
||||
|
||||
* Create a new folder `Framework`
|
||||
|
||||

|
||||
|
||||
* In a Finder window copy the framework file to the newly created folder. The framework should appear in the project navigator as below. (Note: in this case the framework has extension `.framework` but depending on how you built it or from where you obtained it your framework may have extension `.xcframework`)
|
||||
|
||||

|
||||
|
||||
* In the project navigator select the `ViewController.swift` file
|
||||
|
||||

|
||||
|
||||
* Add an `import` for the OpenCV framework below the existing import for `UIKit`
|
||||
|
||||
```swift, copy
|
||||
import opencv2
|
||||
```
|
||||
|
||||
* Replace the implementation of the `viewDidLoad` function with the following code
|
||||
|
||||
```swift, copy
|
||||
override func viewDidLoad() {
|
||||
super.viewDidLoad()
|
||||
let white = Scalar(255, 255, 255, 255)
|
||||
let black = Scalar(0, 0, 0, 255)
|
||||
let m = Mat(rows: 400, cols: Int32(view.frame.width), type: CvType.CV_8UC4, scalar: white)
|
||||
Imgproc.putText(img: m, text: "Hello World", org: Point(x: 10, y: 150), fontFace: HersheyFonts.FONT_HERSHEY_DUPLEX, fontScale: 2, color: black)
|
||||
let image = m.toUIImage()
|
||||
let imageView = UIImageView(image: image)
|
||||
self.view.addSubview(imageView)
|
||||
}
|
||||
```
|
||||
|
||||
* The `ViewController.swift` file should now look like this
|
||||
|
||||

|
||||
|
||||
* Launch the app
|
||||
|
||||

|
||||
|
||||
* The app home screen should look like this
|
||||
|
||||

|
||||
|
||||
If that has all worked well then congratulations. If not then feel free to reach out to the OpenCV community for help. Also feel free to contribute fixes and improvements.
|
||||
@@ -0,0 +1,85 @@
|
||||
|
||||
This is the documentation for the macOS OpenCV framework.
|
||||
|
||||
## Quick Start Tutorial
|
||||
|
||||
Follow the steps below to get a simple "Hello World" app running on macOS
|
||||
|
||||
* Open Xcode and create a new project from the **File** > **New** > **Project...** menu
|
||||
|
||||

|
||||
|
||||
* From the template selection dialog select the **macOS** tab and then select **App**
|
||||
|
||||

|
||||
|
||||
* Enter a name for the project and set **Interface** to **Storyboard** and **Language** to **Swift**
|
||||
|
||||

|
||||
|
||||
* Choose a location to save the project
|
||||
|
||||

|
||||
|
||||
* Create a new folder `Framework`
|
||||
|
||||

|
||||
|
||||
* In a Finder window copy the framework file to the newly created folder. The framework should appear in the project navigator as below. (Note: in this case the framework has extension `.framework` but depending on how you built it or from where you obtained it your framework may have extension `.xcframework`)
|
||||
|
||||

|
||||
|
||||
* Select the project root in the project navigator and on the **General** tab locate the **Frameworks, Libraries and Embedded Content** section
|
||||
|
||||

|
||||
|
||||
* In the **Choose frameworks and libraries to add** dialog add the following
|
||||
* OpenCL.framework
|
||||
* liblapack.tbd
|
||||
* libblas.tbd
|
||||
|
||||

|
||||
|
||||
* The **Frameworks, Libraries and Embedded Content** section should now look like this
|
||||
|
||||

|
||||
|
||||
* In the project navigator select the `ViewController.swift` file
|
||||
|
||||

|
||||
|
||||
* Add an `import` for the OpenCV framework below the existing import for `Cocoa`
|
||||
|
||||
```swift, copy
|
||||
import opencv2
|
||||
```
|
||||
|
||||
* Replace the implementation of the `viewDidLoad` function with the following code
|
||||
|
||||
```swift, copy
|
||||
override func viewDidLoad() {
|
||||
super.viewDidLoad()
|
||||
let white = Scalar(255, 255, 255, 255)
|
||||
let black = Scalar(0, 0, 0, 255)
|
||||
let m = Mat(rows: 400, cols: Int32(view.frame.width), type: CvType.CV_8UC4, scalar: white)
|
||||
Imgproc.putText(img: m, text: "Hello World", org: Point(x: 10, y: 250), fontFace: HersheyFonts.FONT_HERSHEY_DUPLEX, fontScale: 2, color: black)
|
||||
let image = m.toNSImage()
|
||||
let imageView = NSImageView(frame: NSRect(x: 0, y: 0, width: Int(m.cols()), height: Int(m.rows())))
|
||||
imageView.image = image
|
||||
self.view.addSubview(imageView)
|
||||
}
|
||||
```
|
||||
|
||||
* The `ViewController.swift` file should now look like this
|
||||
|
||||

|
||||
|
||||
* Launch the app
|
||||
|
||||

|
||||
|
||||
* The app home screen should look like this
|
||||
|
||||

|
||||
|
||||
If that has all worked well then congratulations. If not then feel free to reach out to the OpenCV community for help. Also feel free to contribute fixes and improvements.
|
||||
@@ -1,13 +0,0 @@
|
||||
## About
|
||||
|
||||
This is the documentation for the Objective-C/Swift OpenCV wrapper
|
||||
|
||||
To get started: add the OpenCV framework to your project and add the following to your source
|
||||
|
||||
###Objective-C
|
||||
|
||||
#import <OpenCV/OpenCV.h>
|
||||
|
||||
###Swift
|
||||
|
||||
import OpenCV
|
||||
|
After Width: | Height: | Size: 21 KiB |
|
After Width: | Height: | Size: 75 KiB |
|
After Width: | Height: | Size: 22 KiB |
|
After Width: | Height: | Size: 47 KiB |
|
After Width: | Height: | Size: 4.4 KiB |
|
After Width: | Height: | Size: 22 KiB |
|
After Width: | Height: | Size: 12 KiB |
|
After Width: | Height: | Size: 46 KiB |
|
After Width: | Height: | Size: 22 KiB |
|
After Width: | Height: | Size: 26 KiB |
|
After Width: | Height: | Size: 9.6 KiB |
|
After Width: | Height: | Size: 12 KiB |
|
After Width: | Height: | Size: 11 KiB |
|
After Width: | Height: | Size: 14 KiB |
|
After Width: | Height: | Size: 54 KiB |
|
After Width: | Height: | Size: 10 KiB |
|
After Width: | Height: | Size: 22 KiB |
|
After Width: | Height: | Size: 12 KiB |
|
After Width: | Height: | Size: 37 KiB |
|
After Width: | Height: | Size: 21 KiB |
|
After Width: | Height: | Size: 25 KiB |
@@ -100,6 +100,9 @@ method_dict = {
|
||||
("Mat", "dot") : "-dot:"
|
||||
}
|
||||
|
||||
enum_value_lookup = {}
|
||||
enums = set()
|
||||
|
||||
modules = []
|
||||
|
||||
|
||||
@@ -709,12 +712,25 @@ def see_lookup(objc_class, see):
|
||||
if (see_class, see_method) in method_dict:
|
||||
method = method_dict[(see_class, see_method)]
|
||||
if see_class == objc_class:
|
||||
return method
|
||||
return "``{}``".format(method[1:])
|
||||
else:
|
||||
return ("-" if method[0] == "-" else "") + "[" + see_class + " " + method[1:] + "]"
|
||||
return "``{}/{}``".format(see_class, method[1:])
|
||||
else:
|
||||
return see
|
||||
|
||||
def hash_link(link_text, objc_class, function_name):
|
||||
if link_text == function_name:
|
||||
return "*{}*".format(link_text) # make bold
|
||||
elif link_text in enums:
|
||||
return "``{}``".format(link_text)
|
||||
elif link_text in enum_value_lookup:
|
||||
return "``{}/{}``".format(enum_value_lookup[link_text], link_text)
|
||||
elif (objc_class, link_text) in method_dict:
|
||||
return "``{}/{}``".format(objc_class, link_text)
|
||||
elif ("Core", link_text) in method_dict:
|
||||
return "``Core/{}``".format(link_text)
|
||||
else:
|
||||
return "#{}".format(link_text) # leave as is
|
||||
|
||||
class ObjectiveCWrapperGenerator(object):
|
||||
def __init__(self):
|
||||
@@ -1116,13 +1132,16 @@ class ObjectiveCWrapperGenerator(object):
|
||||
if fi.docstring:
|
||||
lines = fi.docstring.splitlines()
|
||||
toWrite = []
|
||||
last_line_was_param = False
|
||||
for index, line in enumerate(lines):
|
||||
line = re.sub(r"([(\s])#(.+?)([).,\s]|$)", lambda x: x.group(1) + hash_link(x.group(2), ci.objc_name, fi.name) + x.group(3), line)
|
||||
p0 = line.find("@param")
|
||||
if p0 != -1:
|
||||
p0 += 7 # len("@param" + 1)
|
||||
p1 = line.find(' ', p0)
|
||||
p1 = len(line) if p1 == -1 else p1
|
||||
name = line[p0:p1]
|
||||
last_line_was_param = True
|
||||
for arg in args:
|
||||
if arg.name == name:
|
||||
toWrite.append(re.sub(r'\*\s*@param ', '* @param ', line))
|
||||
@@ -1130,10 +1149,14 @@ class ObjectiveCWrapperGenerator(object):
|
||||
else:
|
||||
s0 = line.find("@see")
|
||||
if s0 != -1:
|
||||
if last_line_was_param:
|
||||
# separate from @param lines
|
||||
toWrite.append(" *")
|
||||
sees = line[(s0 + 5):].split(",")
|
||||
toWrite.append(line[:(s0 + 5)] + ", ".join(["`" + see_lookup(ci.objc_name, see.strip()) + "`" for see in sees]))
|
||||
toWrite.append(line[:s0] + "- SeeAlso " + ", ".join([see_lookup(ci.objc_name, see.strip()) for see in sees]))
|
||||
else:
|
||||
toWrite.append(line)
|
||||
last_line_was_param = False
|
||||
|
||||
for line in toWrite:
|
||||
method_declarations.write(line + "\n")
|
||||
@@ -1318,6 +1341,10 @@ $unrefined_call$epilogue$ret
|
||||
if ci.cname in enum_fix:
|
||||
typeNameShort = enum_fix[ci.cname].get(typeNameShort, typeNameShort)
|
||||
|
||||
enums.add(typeNameShort)
|
||||
for c in consts:
|
||||
enum_value_lookup[c.name] = typeNameShort
|
||||
|
||||
ci.enum_declarations.write("""
|
||||
// C++: enum {1} ({2})
|
||||
typedef NS_ENUM(int, {1}) {{
|
||||
@@ -1463,16 +1490,37 @@ typedef NS_ENUM(int, {1}) {{
|
||||
self.save(opencv_modulemap_file, opencv_modulemap)
|
||||
available_modules = " ".join(["-DAVAILABLE_" + m['name'].upper() for m in config['modules']])
|
||||
cmakelist_template = read_contents(os.path.join(SCRIPT_DIR, 'templates/cmakelists.template'))
|
||||
cmakelist = Template(cmakelist_template).substitute(modules = ";".join(modules), framework = framework_name, objc_target=objc_target, module_availability_defines=available_modules)
|
||||
doc_catalog = output_objc_path + "/../Documentation.docc"
|
||||
doc_resources = os.path.join(doc_catalog, "Resources")
|
||||
cmakelist = Template(cmakelist_template).substitute(
|
||||
modules = ";".join(modules),
|
||||
framework = framework_name,
|
||||
objc_target = objc_target,
|
||||
module_availability_defines = available_modules,
|
||||
)
|
||||
self.save(os.path.join(dstdir, "CMakeLists.txt"), cmakelist)
|
||||
mkdir_p(doc_resources)
|
||||
mkdir_p(os.path.join(output_objc_build_path, "framework_build"))
|
||||
mkdir_p(os.path.join(output_objc_build_path, "test_build"))
|
||||
mkdir_p(os.path.join(output_objc_build_path, "doc_build"))
|
||||
with open(os.path.join(SCRIPT_DIR, '../doc/README.md')) as readme_in:
|
||||
readme_body = readme_in.read()
|
||||
readme_body += "\n\n\n##Modules\n\n" + ", ".join(["`" + m.capitalize() + "`" for m in modules])
|
||||
with open(os.path.join(output_objc_build_path, "doc_build/README.md"), "w") as readme_out:
|
||||
readme_out.write(readme_body)
|
||||
landing_page_body_in = ""
|
||||
if objc_target == "ios" or objc_target == "osx":
|
||||
with open(os.path.join(SCRIPT_DIR, '../doc/LandingPage_' + objc_target + '.md')) as landing_page_in:
|
||||
landing_page_body_in = landing_page_in.read()
|
||||
landing_page_body_out = "# ``" + framework_name + "`` Framework\n\n" + landing_page_body_in
|
||||
landing_page_body_out += "\n\n\n## Modules\n\n" + ", ".join(["``" + m.capitalize() + "``" for m in modules]) + "\n"
|
||||
with open(os.path.join(doc_catalog, "LandingPage.md"), "w") as landing_page_out:
|
||||
landing_page_out.write(landing_page_body_out)
|
||||
for m in config['modules']:
|
||||
pic_files = os.path.join(ROOT_DIR, m['location'], 'doc', 'pics')
|
||||
if os.path.exists(pic_files):
|
||||
copy_tree(pic_files, doc_resources)
|
||||
if objc_target == "ios":
|
||||
copy_tree(os.path.join(SCRIPT_DIR, '../doc/pics/ios'), doc_resources)
|
||||
if objc_target == "osx":
|
||||
copy_tree(os.path.join(SCRIPT_DIR, '../doc/pics/osx'), doc_resources)
|
||||
if objc_target == "ios" or objc_target == "osx":
|
||||
copy_tree(os.path.join(SCRIPT_DIR, '../doc/pics/common'), doc_resources)
|
||||
|
||||
if framework_name != "OpenCV":
|
||||
for dirname, dirs, files in os.walk(os.path.join(testdir, "test")):
|
||||
if dirname.endswith('/resources'):
|
||||
@@ -1542,10 +1590,11 @@ def sanitize_documentation_string(doc, type):
|
||||
doc = re.sub(re.compile('\\\\f\\$(.*?)\\\\f\\$', re.DOTALL), lambda x: '`$$' + fix_tex(x.group(1)) + '$$`', doc)
|
||||
doc = re.sub(re.compile('\\\\f\\[(.*?)\\\\f\\]', re.DOTALL), lambda x: '`$$' + fix_tex(x.group(1)) + '$$`', doc)
|
||||
doc = re.sub(re.compile('\\\\f\\{(.*?)\\\\f\\}', re.DOTALL), lambda x: '`$$' + fix_tex(x.group(1)) + '$$`', doc)
|
||||
doc = re.sub(r"!\[(.*)]\((?:.*/)*(.+)\.(?:png|jpg|svg)\)", "", doc)
|
||||
|
||||
doc = doc.replace("@anchor", "") \
|
||||
.replace("@brief ", "").replace("\\brief ", "") \
|
||||
.replace("@cite", "CITE:") \
|
||||
.replace("@cite", "*Cite:*") \
|
||||
.replace("@code{.cpp}", "<code>") \
|
||||
.replace("@code{.txt}", "<code>") \
|
||||
.replace("@code", "<code>") \
|
||||
@@ -1557,14 +1606,14 @@ def sanitize_documentation_string(doc, type):
|
||||
.replace("@endcode", "</code>") \
|
||||
.replace("@endinternal", "") \
|
||||
.replace("@file", "") \
|
||||
.replace("@include", "INCLUDE:") \
|
||||
.replace("@include", "Include:") \
|
||||
.replace("@ingroup", "") \
|
||||
.replace("@internal", "") \
|
||||
.replace("@overload", "") \
|
||||
.replace("@param[in]", "@param") \
|
||||
.replace("@param[out]", "@param") \
|
||||
.replace("@ref", "REF:") \
|
||||
.replace("@note", "NOTE:") \
|
||||
.replace("@ref", "*Ref:*") \
|
||||
.replace("@note", "*Note:*") \
|
||||
.replace("@returns", "@return") \
|
||||
.replace("@sa ", "@see ") \
|
||||
.replace("@snippet", "SNIPPET:") \
|
||||
|
||||
@@ -22,7 +22,14 @@ else()
|
||||
endif()
|
||||
file(GLOB_RECURSE objc_headers "*\.h")
|
||||
|
||||
add_library($framework STATIC $${objc_sources})
|
||||
set_source_files_properties(Documentation.docc PROPERTIES
|
||||
XCODE_LAST_KNOWN_FILE_TYPE folder.documentationcatalog
|
||||
)
|
||||
|
||||
add_library($framework STATIC
|
||||
$${objc_sources}
|
||||
Documentation.docc
|
||||
)
|
||||
|
||||
set_target_properties($framework PROPERTIES LINKER_LANGUAGE CXX)
|
||||
|
||||
|
||||
@@ -0,0 +1,11 @@
|
||||
# $framework Documentation - How To View
|
||||
|
||||
## View Locally
|
||||
|
||||
* Start a web server in the directory where this HowTo file is located. For example ``python3 -m http.server 8000``
|
||||
* In a browser navigate to URL http://127.0.0.1:8000/$hosting_base_path/documentation/$framework/
|
||||
|
||||
## Deploying to a server
|
||||
|
||||
* Upload the content of the directory where this HowTo file is located to the document root of the web server maintaining the same folder hierarchy
|
||||
* In a browser navigate to URL https://{server-domain}/$hosting_base_path/documentation/$framework/
|
||||
@@ -266,36 +266,42 @@ Mat Dictionary::getBitsFromByteList(const Mat &byteList, int markerSize, int rot
|
||||
|
||||
|
||||
Dictionary getPredefinedDictionary(PredefinedDictionaryType name) {
|
||||
// The maximum number of bits that can be corrected is theoretically (d-1)/2,
|
||||
// where d is the minimum Hamming distance between any two codes in the dictionary.
|
||||
// However, we use a more conservative limit (d/2)-1 to reduce the probability
|
||||
// of false positives during detection. This formula is equivalent to the
|
||||
// theoretical limit for even distances and stricter for odd distances.
|
||||
|
||||
// DictionaryData constructors calls
|
||||
// moved out of globals so construted on first use, which allows lazy-loading of opencv dll
|
||||
static const Dictionary DICT_ARUCO_DATA = Dictionary(Mat(1024, (5 * 5 + 7) / 8, CV_8UC4, (uchar*)DICT_ARUCO_BYTES), 5, 0);
|
||||
static const Dictionary DICT_ARUCO_DATA = Dictionary(Mat(1024, (5 * 5 + 7) / 8, CV_8UC4, (uchar*)DICT_ARUCO_BYTES), 5, (3-1)/2);
|
||||
|
||||
static const Dictionary DICT_4X4_50_DATA = Dictionary(Mat(50, (4 * 4 + 7) / 8, CV_8UC4, (uchar*)DICT_4X4_1000_BYTES), 4, 1);
|
||||
static const Dictionary DICT_4X4_100_DATA = Dictionary(Mat(100, (4 * 4 + 7) / 8, CV_8UC4, (uchar*)DICT_4X4_1000_BYTES), 4, 1);
|
||||
static const Dictionary DICT_4X4_250_DATA = Dictionary(Mat(250, (4 * 4 + 7) / 8, CV_8UC4, (uchar*)DICT_4X4_1000_BYTES), 4, 1);
|
||||
static const Dictionary DICT_4X4_1000_DATA = Dictionary(Mat(1000, (4 * 4 + 7) / 8, CV_8UC4, (uchar*)DICT_4X4_1000_BYTES), 4, 0);
|
||||
static const Dictionary DICT_4X4_50_DATA = Dictionary(Mat(50, (4 * 4 + 7) / 8, CV_8UC4, (uchar*)DICT_4X4_1000_BYTES), 4, (4-1)/2);
|
||||
static const Dictionary DICT_4X4_100_DATA = Dictionary(Mat(100, (4 * 4 + 7) / 8, CV_8UC4, (uchar*)DICT_4X4_1000_BYTES), 4, (3-1)/2);
|
||||
static const Dictionary DICT_4X4_250_DATA = Dictionary(Mat(250, (4 * 4 + 7) / 8, CV_8UC4, (uchar*)DICT_4X4_1000_BYTES), 4, (3-1)/2);
|
||||
static const Dictionary DICT_4X4_1000_DATA = Dictionary(Mat(1000, (4 * 4 + 7) / 8, CV_8UC4, (uchar*)DICT_4X4_1000_BYTES), 4, (2-1)/2);
|
||||
|
||||
static const Dictionary DICT_5X5_50_DATA = Dictionary(Mat(50, (5 * 5 + 7) / 8, CV_8UC4, (uchar*)DICT_5X5_1000_BYTES), 5, 3);
|
||||
static const Dictionary DICT_5X5_100_DATA = Dictionary(Mat(100, (5 * 5 + 7) / 8, CV_8UC4, (uchar*)DICT_5X5_1000_BYTES), 5, 3);
|
||||
static const Dictionary DICT_5X5_250_DATA = Dictionary(Mat(250, (5 * 5 + 7) / 8, CV_8UC4, (uchar*)DICT_5X5_1000_BYTES), 5, 2);
|
||||
static const Dictionary DICT_5X5_1000_DATA = Dictionary(Mat(1000, (5 * 5 + 7) / 8, CV_8UC4, (uchar*)DICT_5X5_1000_BYTES), 5, 2);
|
||||
static const Dictionary DICT_5X5_50_DATA = Dictionary(Mat(50, (5 * 5 + 7) / 8, CV_8UC4, (uchar*)DICT_5X5_1000_BYTES), 5, (8-1)/2);
|
||||
static const Dictionary DICT_5X5_100_DATA = Dictionary(Mat(100, (5 * 5 + 7) / 8, CV_8UC4, (uchar*)DICT_5X5_1000_BYTES), 5, (7-1)/2);
|
||||
static const Dictionary DICT_5X5_250_DATA = Dictionary(Mat(250, (5 * 5 + 7) / 8, CV_8UC4, (uchar*)DICT_5X5_1000_BYTES), 5, (6-1)/2);
|
||||
static const Dictionary DICT_5X5_1000_DATA = Dictionary(Mat(1000, (5 * 5 + 7) / 8, CV_8UC4, (uchar*)DICT_5X5_1000_BYTES), 5, (5-1)/2);
|
||||
|
||||
static const Dictionary DICT_6X6_50_DATA = Dictionary(Mat(50, (6 * 6 + 7) / 8, CV_8UC4, (uchar*)DICT_6X6_1000_BYTES), 6, 6);
|
||||
static const Dictionary DICT_6X6_100_DATA = Dictionary(Mat(100, (6 * 6 + 7) / 8, CV_8UC4, (uchar*)DICT_6X6_1000_BYTES), 6, 5);
|
||||
static const Dictionary DICT_6X6_250_DATA = Dictionary(Mat(250, (6 * 6 + 7) / 8, CV_8UC4, (uchar*)DICT_6X6_1000_BYTES), 6, 5);
|
||||
static const Dictionary DICT_6X6_1000_DATA = Dictionary(Mat(1000, (6 * 6 + 7) / 8, CV_8UC4, (uchar*)DICT_6X6_1000_BYTES), 6, 4);
|
||||
static const Dictionary DICT_6X6_50_DATA = Dictionary(Mat(50, (6 * 6 + 7) / 8, CV_8UC4, (uchar*)DICT_6X6_1000_BYTES), 6, (13-1)/2);
|
||||
static const Dictionary DICT_6X6_100_DATA = Dictionary(Mat(100, (6 * 6 + 7) / 8, CV_8UC4, (uchar*)DICT_6X6_1000_BYTES), 6, (12-1)/2);
|
||||
static const Dictionary DICT_6X6_250_DATA = Dictionary(Mat(250, (6 * 6 + 7) / 8, CV_8UC4, (uchar*)DICT_6X6_1000_BYTES), 6, (11-1)/2);
|
||||
static const Dictionary DICT_6X6_1000_DATA = Dictionary(Mat(1000, (6 * 6 + 7) / 8, CV_8UC4, (uchar*)DICT_6X6_1000_BYTES), 6, (9-1)/2);
|
||||
|
||||
static const Dictionary DICT_7X7_50_DATA = Dictionary(Mat(50, (7 * 7 + 7) / 8, CV_8UC4, (uchar*)DICT_7X7_1000_BYTES), 7, 9);
|
||||
static const Dictionary DICT_7X7_100_DATA = Dictionary(Mat(100, (7 * 7 + 7) / 8, CV_8UC4, (uchar*)DICT_7X7_1000_BYTES), 7, 8);
|
||||
static const Dictionary DICT_7X7_250_DATA = Dictionary(Mat(250, (7 * 7 + 7) / 8, CV_8UC4, (uchar*)DICT_7X7_1000_BYTES), 7, 8);
|
||||
static const Dictionary DICT_7X7_1000_DATA = Dictionary(Mat(1000, (7 * 7 + 7) / 8, CV_8UC4, (uchar*)DICT_7X7_1000_BYTES), 7, 6);
|
||||
static const Dictionary DICT_7X7_50_DATA = Dictionary(Mat(50, (7 * 7 + 7) / 8, CV_8UC4, (uchar*)DICT_7X7_1000_BYTES), 7, (19-1)/2);
|
||||
static const Dictionary DICT_7X7_100_DATA = Dictionary(Mat(100, (7 * 7 + 7) / 8, CV_8UC4, (uchar*)DICT_7X7_1000_BYTES), 7, (18-1)/2);
|
||||
static const Dictionary DICT_7X7_250_DATA = Dictionary(Mat(250, (7 * 7 + 7) / 8, CV_8UC4, (uchar*)DICT_7X7_1000_BYTES), 7, (17-1)/2);
|
||||
static const Dictionary DICT_7X7_1000_DATA = Dictionary(Mat(1000, (7 * 7 + 7) / 8, CV_8UC4, (uchar*)DICT_7X7_1000_BYTES), 7, (14-1)/2);
|
||||
|
||||
static const Dictionary DICT_APRILTAG_16h5_DATA = Dictionary(Mat(30, (4 * 4 + 7) / 8, CV_8UC4, (uchar*)DICT_APRILTAG_16h5_BYTES), 4, 0);
|
||||
static const Dictionary DICT_APRILTAG_25h9_DATA = Dictionary(Mat(35, (5 * 5 + 7) / 8, CV_8UC4, (uchar*)DICT_APRILTAG_25h9_BYTES), 5, 0);
|
||||
static const Dictionary DICT_APRILTAG_36h10_DATA = Dictionary(Mat(2320, (6 * 6 + 7) / 8, CV_8UC4, (uchar*)DICT_APRILTAG_36h10_BYTES), 6, 0);
|
||||
static const Dictionary DICT_APRILTAG_36h11_DATA = Dictionary(Mat(587, (6 * 6 + 7) / 8, CV_8UC4, (uchar*)DICT_APRILTAG_36h11_BYTES), 6, 0);
|
||||
static const Dictionary DICT_APRILTAG_16h5_DATA = Dictionary(Mat(30, (4 * 4 + 7) / 8, CV_8UC4, (uchar*)DICT_APRILTAG_16h5_BYTES), 4, (5-1)/2);
|
||||
static const Dictionary DICT_APRILTAG_25h9_DATA = Dictionary(Mat(35, (5 * 5 + 7) / 8, CV_8UC4, (uchar*)DICT_APRILTAG_25h9_BYTES), 5, (9-1)/2);
|
||||
static const Dictionary DICT_APRILTAG_36h10_DATA = Dictionary(Mat(2320, (6 * 6 + 7) / 8, CV_8UC4, (uchar*)DICT_APRILTAG_36h10_BYTES), 6, (10-1)/2);
|
||||
static const Dictionary DICT_APRILTAG_36h11_DATA = Dictionary(Mat(587, (6 * 6 + 7) / 8, CV_8UC4, (uchar*)DICT_APRILTAG_36h11_BYTES), 6, (11-1)/2);
|
||||
|
||||
static const Dictionary DICT_ARUCO_MIP_36h12_DATA = Dictionary(Mat(250, (6 * 6 + 7) / 8, CV_8UC4, (uchar*)DICT_ARUCO_MIP_36h12_BYTES), 6, 12);
|
||||
static const Dictionary DICT_ARUCO_MIP_36h12_DATA = Dictionary(Mat(250, (6 * 6 + 7) / 8, CV_8UC4, (uchar*)DICT_ARUCO_MIP_36h12_BYTES), 6, (12-1)/2);
|
||||
|
||||
switch(name) {
|
||||
|
||||
|
||||
@@ -141,4 +141,34 @@ bool pyopencv_to(PyObject* obj, Ptr<cv::IStreamReader>& p, const ArgInfo&)
|
||||
return false;
|
||||
}
|
||||
|
||||
static PyObject* pycvVideoEnter(PyObject* self, PyObject* /*args*/, PyObject* /*kw*/) {
|
||||
Py_INCREF(self);
|
||||
return self;
|
||||
}
|
||||
|
||||
static PyObject* pycvVideoCaptureExit(PyObject* self, PyObject* /*args*/, PyObject* /*kw*/) {
|
||||
Ptr<cv::VideoCapture>* obj_getp = nullptr;
|
||||
if (pyopencv_VideoCapture_getp(self, obj_getp) && obj_getp && *obj_getp) {
|
||||
(*obj_getp)->release();
|
||||
}
|
||||
Py_RETURN_NONE;
|
||||
}
|
||||
|
||||
static PyObject* pycvVideoWriterExit(PyObject* self, PyObject* /*args*/, PyObject* /*kw*/) {
|
||||
Ptr<cv::VideoWriter>* obj_getp = nullptr;
|
||||
if (pyopencv_VideoWriter_getp(self, obj_getp) && obj_getp && *obj_getp) {
|
||||
(*obj_getp)->release();
|
||||
}
|
||||
Py_RETURN_NONE;
|
||||
}
|
||||
|
||||
#define PYOPENCV_EXTRA_METHODS_VideoCapture \
|
||||
{"__enter__", CV_PY_FN_WITH_KW(pycvVideoEnter), "Context manager enter"}, \
|
||||
{"__exit__", CV_PY_FN_WITH_KW(pycvVideoCaptureExit), "Context manager exit"},
|
||||
|
||||
#define PYOPENCV_EXTRA_METHODS_VideoWriter \
|
||||
{"__enter__", CV_PY_FN_WITH_KW(pycvVideoEnter), "Context manager enter"}, \
|
||||
{"__exit__", CV_PY_FN_WITH_KW(pycvVideoWriterExit), "Context manager exit"},
|
||||
|
||||
|
||||
#endif // HAVE_OPENCV_VIDEOIO
|
||||
|
||||
@@ -5,6 +5,7 @@ import numpy as np
|
||||
import cv2 as cv
|
||||
import io
|
||||
import sys
|
||||
import tempfile
|
||||
|
||||
from tests_common import NewOpenCVTests
|
||||
|
||||
@@ -87,5 +88,22 @@ class Bindings(NewOpenCVTests):
|
||||
self.assertTrue(hasFrame)
|
||||
self.assertEqual(frame.shape, (576, 768, 3))
|
||||
|
||||
def test_context_manager(self):
|
||||
video_file = self.find_file("cv/video/768x576.avi")
|
||||
|
||||
with cv.VideoCapture(video_file) as cap:
|
||||
self.assertTrue(cap.isOpened(), "VideoCapture should be opened within context manager")
|
||||
|
||||
with tempfile.NamedTemporaryFile(suffix='.avi') as tmp:
|
||||
with cv.VideoWriter(tmp.name, cv.VideoWriter_fourcc(*'MJPG'), 25, (640, 480)) as writer:
|
||||
self.assertTrue(isinstance(writer, cv.VideoWriter))
|
||||
|
||||
try:
|
||||
with cv.VideoCapture(video_file) as cap:
|
||||
self.assertTrue(cap.isOpened())
|
||||
raise RuntimeError("Testing context manager exception safety")
|
||||
except RuntimeError:
|
||||
pass
|
||||
|
||||
if __name__ == '__main__':
|
||||
NewOpenCVTests.bootstrap()
|
||||
|
||||
@@ -2756,7 +2756,13 @@ int videoInput::start(int deviceID, videoDevice *VD){
|
||||
}
|
||||
|
||||
VIDEOINFOHEADER *pVih = reinterpret_cast<VIDEOINFOHEADER*>(VD->pAmMediaType->pbFormat);
|
||||
CV_Assert(pVih);
|
||||
// Some legacy or virtual cameras (e.g., Microsoft Ball filter) return S_OK
|
||||
// but leave pbFormat as NULL. We check for NULL here to avoid a crash.
|
||||
// https://github.com/opencv/opencv/issues/28904
|
||||
if (pVih == NULL) {
|
||||
DebugPrintOut("ERROR: pbFormat field is not set!\n");
|
||||
return false;
|
||||
}
|
||||
int currentWidth = HEADER(pVih)->biWidth;
|
||||
int currentHeight = HEADER(pVih)->biHeight;
|
||||
|
||||
|
||||
@@ -335,7 +335,10 @@ public:
|
||||
|
||||
// Workaround for some gstreamer pipelines
|
||||
if (apiPref == CAP_GSTREAMER)
|
||||
{
|
||||
expected_frame_count.start -= 1;
|
||||
expected_frame_count.end += 1;
|
||||
}
|
||||
|
||||
ASSERT_LE(expected_frame_count.start, actual);
|
||||
ASSERT_GE(expected_frame_count.end, actual);
|
||||
@@ -915,7 +918,7 @@ TEST_P(videowriter_acceleration, write)
|
||||
throw SkipTestException(cv::String("Backend is not available/disabled: ") + backend_name);
|
||||
#ifdef __linux__
|
||||
if (cvtest::skipUnstableTests && backend == CAP_GSTREAMER &&
|
||||
(extension == "mkv") && (codecid == "MPEG"))
|
||||
(extension == "mkv") && (codecid == "MPEG" || codecid == "H264"))
|
||||
{
|
||||
throw SkipTestException("Unstable GSTREAMER test");
|
||||
}
|
||||
|
||||
@@ -84,7 +84,7 @@ endif()
|
||||
ocv_target_compile_definitions(${the_module} PRIVATE OPENCV_MODULE_IS_PART_OF_WORLD=1)
|
||||
|
||||
# NOTE: https://github.com/opencv/opencv/issues/25543
|
||||
if (WITH_QT)
|
||||
if (WITH_QT AND QT_VERSION_MAJOR GREATER 5)
|
||||
qt_disable_unicode_defines(${the_module})
|
||||
endif()
|
||||
|
||||
|
||||