1
0
mirror of https://github.com/opencv/opencv.git synced 2026-07-31 08:13:04 +04:00

Merge branch 4.x

This commit is contained in:
Alexander Smorkalov
2026-02-11 13:54:39 +03:00
committed by Alexander Smorkalov
468 changed files with 16647 additions and 11284 deletions
+1 -1
View File
@@ -790,7 +790,7 @@ public:
Mat F(count == 7 ? 9 : 3, 3, CV_64F, f);
int n = count == 7 ? run7Point(m1, m2, F) : run8Point(m1, m2, F);
if( n == 0 )
if( n <= 0 )
_model.release();
else
F.rowRange(0, n*3).copyTo(_model);
+8
View File
@@ -44,6 +44,10 @@
#elif defined(HAVE_LAPACK)
#include "opencv_lapack.h"
#endif
#if defined(__APPLE__) && defined(__clang__)
#pragma clang diagnostic push
#pragma clang diagnostic ignored "-Wdeprecated-declarations"
#endif
namespace cv { namespace usac {
class DLSPnPImpl : public DLSPnP {
@@ -890,3 +894,7 @@ Ptr<DLSPnP> DLSPnP::create(const Mat &points_, const Mat &calib_norm_pts, const
return makePtr<DLSPnPImpl>(points_, calib_norm_pts, K);
}
}}
#if defined(__APPLE__) && defined(__clang__)
#pragma clang diagnostic pop
#endif
+8
View File
@@ -9,6 +9,10 @@
#elif defined(HAVE_LAPACK)
#include "opencv_lapack.h"
#endif
#if defined(__APPLE__) && defined(__clang__)
#pragma clang diagnostic push
#pragma clang diagnostic ignored "-Wdeprecated-declarations"
#endif
namespace cv { namespace usac {
/*
@@ -354,3 +358,7 @@ Ptr<EssentialMinimalSolver5pts> EssentialMinimalSolver5pts::create
return makePtr<EssentialMinimalSolver5ptsImpl>(points_, use_svd, is_nister);
}
}}
#if defined(__APPLE__) && defined(__clang__)
#pragma clang diagnostic pop
#endif
+8
View File
@@ -11,6 +11,10 @@
#elif defined(HAVE_LAPACK)
#include "opencv_lapack.h"
#endif
#if defined(__APPLE__) && defined(__clang__)
#pragma clang diagnostic push
#pragma clang diagnostic ignored "-Wdeprecated-declarations"
#endif
namespace cv { namespace usac {
class PnPMinimalSolver6PtsImpl : public PnPMinimalSolver6Pts {
@@ -408,3 +412,7 @@ Ptr<P3PSolver> P3PSolver::create(const Mat &points_, const Mat &calib_norm_pts,
return makePtr<P3PSolverImpl>(points_, calib_norm_pts, K);
}
}}
#if defined(__APPLE__) && defined(__clang__)
#pragma clang diagnostic pop
#endif
+2 -2
View File
@@ -388,12 +388,12 @@ void Utils::densitySort (const Mat &points, int knn, Mat &sorted_points, std::ve
sorted_mask[i] = i;
// get neighbors
FlannNeighborhoodGraph &graph = *FlannNeighborhoodGraph::create(points, points_size, knn,
cv::Ptr<FlannNeighborhoodGraph> graph = FlannNeighborhoodGraph::create(points, points_size, knn,
true /*get distances */, 6, 1);
std::vector<double> sum_knn_distances (points_size, 0);
for (int p = 0; p < points_size; p++) {
const std::vector<double> &dists = graph.getNeighborsDistances(p);
const std::vector<double> &dists = graph->getNeighborsDistances(p);
for (int k = 0; k < knn; k++)
sum_knn_distances[p] += dists[k];
}
+8
View File
@@ -420,5 +420,13 @@ TEST(Calib3d_FindFundamentalMat, correctMatches)
cout << np2 << endl;
}
TEST(Calib3d_FindFundamentalMat, Crash)
{
vector<Point2f> m1 = {{245, 128},{284, 226},{140, 60},{133, 127},{71, 218},{152, 138},{181, 106}};
vector<Point2f> m2 = m1;
vector<uchar> mask;
findFundamentalMat(m1, m2, mask, FM_LMEDS, 1, 0.99);
}
}} // namespace
/* End of file. */
+1 -1
View File
@@ -654,7 +654,7 @@ CV_EXPORTS_W bool checkChessboard(InputArray img, Size size);
- @ref CALIB_CB_LARGER The detected pattern is allowed to be larger than patternSize (see description).
- @ref CALIB_CB_MARKER The detected pattern must have a marker (see description).
This should be used if an accurate camera calibration is required.
@param meta Optional output arrray of detected corners (CV_8UC1 and size = cv::Size(columns,rows)).
@param meta Optional output array of detected corners (CV_8UC1 and size = cv::Size(columns,rows)).
Each entry stands for one corner of the pattern and can have one of the following values:
- 0 = no meta data attached
- 1 = left-top corner of a black cell
+1 -1
View File
@@ -2169,7 +2169,7 @@ cv::Point2f &Chessboard::Board::getCorner(int _row,int _col)
}
++count;
row_start = row_start->bottom;
}while(_row);
}while(row_start);
}
CV_Error(Error::StsInternal,"cannot find corner");
// return *top_left->top_left; // never reached
+1 -1
View File
@@ -149,7 +149,7 @@ class Chessboard: public cv::Feature2D
int max_tests; //!< maximal number of tested hypothesis
bool super_resolution; //!< use super-repsolution for chessboard detection
bool larger; //!< indicates if larger boards should be returned
bool marker; //!< indicates that valid boards must have a white and black cirlce marker used for orientation
bool marker; //!< indicates that valid boards must have a white and black circle marker used for orientation
Parameters()
{
@@ -239,6 +239,10 @@ struct VZeroUpperGuard {
#elif defined(__ARM_NEON)
# include <arm_neon.h>
# define CV_NEON 1
#ifdef __ARM_FEATURE_SVE
# include<arm_sve.h>
# define CV_SVE 1
#endif
#elif defined(__VSX__) && defined(__PPC64__) && defined(__LITTLE_ENDIAN__)
# include <altivec.h>
# undef vector
@@ -369,6 +373,10 @@ struct VZeroUpperGuard {
# define CV_NEON 0
#endif
#ifndef CV_SVE
# define CV_SVE 0
#endif
#ifndef CV_RVV071
# define CV_RVV071 0
#endif
@@ -399,6 +399,27 @@
#endif
#define __CV_CPU_DISPATCH_CHAIN_AVX512_ICL(fn, args, mode, ...) CV_CPU_CALL_AVX512_ICL(fn, args); __CV_EXPAND(__CV_CPU_DISPATCH_CHAIN_ ## mode(fn, args, __VA_ARGS__))
#if !defined CV_DISABLE_OPTIMIZATION && defined CV_ENABLE_INTRINSICS && defined CV_CPU_COMPILE_SVE
# define CV_TRY_SVE 1
# define CV_CPU_FORCE_SVE 1
# define CV_CPU_HAS_SUPPORT_SVE 1
# define CV_CPU_CALL_SVE(fn, args) return (cpu_baseline::fn args)
# define CV_CPU_CALL_SVE_(fn, args) return (opt_SVE::fn args)
#elif !defined CV_DISABLE_OPTIMIZATION && defined CV_ENABLE_INTRINSICS && defined CV_CPU_DISPATCH_COMPILE_SVE
# define CV_TRY_SVE 1
# define CV_CPU_FORCE_SVE 0
# define CV_CPU_HAS_SUPPORT_SVE (cv::checkHardwareSupport(CV_CPU_SVE))
# define CV_CPU_CALL_SVE(fn, args) if (CV_CPU_HAS_SUPPORT_SVE) return (opt_SVE::fn args)
# define CV_CPU_CALL_SVE_(fn, args) if (CV_CPU_HAS_SUPPORT_SVE) return (opt_SVE::fn args)
#else
# define CV_TRY_SVE 0
# define CV_CPU_FORCE_SVE 0
# define CV_CPU_HAS_SUPPORT_SVE 0
# define CV_CPU_CALL_SVE(fn, args)
# define CV_CPU_CALL_SVE_(fn, args)
#endif
#define __CV_CPU_DISPATCH_CHAIN_SVE(fn, args, mode, ...) CV_CPU_CALL_SVE(fn, args); __CV_EXPAND(__CV_CPU_DISPATCH_CHAIN_ ## mode(fn, args, __VA_ARGS__))
#if !defined CV_DISABLE_OPTIMIZATION && defined CV_ENABLE_INTRINSICS && defined CV_CPU_COMPILE_NEON
# define CV_TRY_NEON 1
# define CV_CPU_FORCE_NEON 1
@@ -279,6 +279,7 @@ namespace cv {
#define CV_CPU_NEON_DOTPROD 101
#define CV_CPU_NEON_FP16 102
#define CV_CPU_NEON_BF16 103
#define CV_CPU_SVE 104
#define CV_CPU_MSA 150
@@ -342,6 +343,7 @@ enum CpuFeatures {
CPU_NEON_DOTPROD = 101,
CPU_NEON_FP16 = 102,
CPU_NEON_BF16 = 103,
CPU_SVE = 104,
CPU_MSA = 150,
@@ -81,9 +81,26 @@ CV_CPU_OPTIMIZATION_HAL_NAMESPACE_BEGIN
"Universal intrinsics" is a types and functions set intended to simplify vectorization of code on
different platforms. Currently a few different SIMD extensions on different architectures are supported.
128 bit registers of various types support is implemented for a wide range of architectures
including x86(__SSE/SSE2/SSE4.2__), ARM(__NEON__), PowerPC(__VSX__), MIPS(__MSA__).
256 bit long registers are supported on x86(__AVX2__) and 512 bit long registers are supported on x86(__AVX512__).
OpenCV Universal Intrinsics support the following instruction sets:
- *128 bit* registers of various types support is implemented for a wide range of architectures including
- x86(SSE/SSE2/SSE4.2),
- ARM(NEON): 64-bit float (64F) requires AArch64,
- PowerPC(VSX),
- MIPS(MSA),
- LoongArch(LSX),
- RISC-V(RVV 0.7.1): Fixed-length implementation,
- WASM: 64-bit float (64F) is not supported,
- *256 bit* registers are supported on
- x86(AVX2),
- LoongArch (LASX),
- *512 bit* registers are supported on
- x86(AVX512),
- *Vector Length Agnostic (VLA)* registers are supported on
- RISC-V(RVV 1.0)
- ARM(SVE/SVE2): Powered by Arm KleidiCV integration (OpenCV 4.11+),
In case when there is no SIMD extension available during compilation, fallback C++ implementation of intrinsics
will be chosen and code will work as expected although it could be slower.
@@ -988,9 +988,10 @@ inline v_uint32x4 v_dotprod_expand_fast(const v_uint8x16& a, const v_uint8x16& b
inline v_int32x4 v_dotprod_expand_fast(const v_int8x16& a, const v_int8x16& b)
{
int16x8_t prod = vmull_s8(vget_low_s8(a.val), vget_low_s8(b.val));
prod = vmlal_s8(prod, vget_high_s8(a.val), vget_high_s8(b.val));
return v_int32x4(vaddl_s16(vget_low_s16(prod), vget_high_s16(prod)));
int16x8_t p0 = vmull_s8(vget_low_s8(a.val), vget_low_s8(b.val));
int16x8_t p1 = vmull_s8(vget_high_s8(a.val), vget_high_s8(b.val));
int32x4_t s0 = vaddl_s16(vget_low_s16(p0), vget_low_s16(p1));
return v_int32x4(vaddq_s32(s0, vaddl_s16(vget_high_s16(p0), vget_high_s16(p1))));
}
inline v_int32x4 v_dotprod_expand_fast(const v_int8x16& a, const v_int8x16& b, const v_int32x4& c)
{
+3 -3
View File
@@ -1549,15 +1549,15 @@ public:
/** @overload
* @param cn New number of channels. If the parameter is 0, the number of channels remains the same.
* @param newndims New number of dimentions.
* @param newsz Array with new matrix size by all dimentions. If some sizes are zero,
* @param newndims New number of dimensions.
* @param newsz Array with new matrix size by all dimensions. If some sizes are zero,
* the original sizes in those dimensions are presumed.
*/
Mat reshape(int cn, int newndims, const int* newsz) const;
/** @overload
* @param cn New number of channels. If the parameter is 0, the number of channels remains the same.
* @param newshape Vector with new matrix size by all dimentions. If some sizes are zero,
* @param newshape Vector with new matrix size by all dimensions. If some sizes are zero,
* the original sizes in those dimensions are presumed.
*/
Mat reshape(int cn, const std::vector<int>& newshape) const;
@@ -57,7 +57,7 @@ class QuatEnum
public:
/** @brief Enum of Euler angles type.
*
* Without considering the possibility of using two different convertions for the definition of the rotation axes ,
* Without considering the possibility of using two different conversions for the definition of the rotation axes ,
* there exists twelve possible sequences of rotation axes, divided into two groups:
* - Proper Euler angles (Z-X-Z, X-Y-X, Y-Z-Y, Z-Y-Z, X-Z-X, Y-X-Y)
* - TaitBryan angles (X-Y-Z, Y-Z-X, Z-X-Y, X-Z-Y, Z-Y-X, Y-X-Z).
@@ -273,7 +273,7 @@ public:
* where \f$ q_{X, \theta_1} \f$ is created from @ref createFromXRot, \f$ q_{Y, \theta_2} \f$ is created from @ref createFromYRot,
* \f$ q_{Z, \theta_3} \f$ is created from @ref createFromZRot.
* @param angles the Euler angles in a vector of length 3
* @param eulerAnglesType the convertion Euler angles type
* @param eulerAnglesType the conversion Euler angles type
*/
static Quat<_Tp> createFromEulerAngles(const Vec<_Tp, 3> &angles, QuatEnum::EulerAnglesType eulerAnglesType);
@@ -1610,7 +1610,7 @@ public:
* EXT_ZXZ| \f$ \theta_1 = \arctan2(m_{31},m_{32}) \\\theta_2 = \arccos(m_{33}) \\\theta_3 = \arctan2(-m_{13},m_{23})\f$| \f$ \theta_1=0\\ \theta_2=0\\ \theta_3=\arctan2(m_{21},m_{22}) \f$| \f$ \theta_1= 0\\ \theta_2=\pi\\ \theta_3=\arctan2(m_{21},m_{11}) \f$
* EXT_ZYZ| \f$ \theta_1 = \arctan2(m_{32},-m_{31})\\\theta_2 = \arccos(m_{33}) \\\theta_3 = \arctan2(m_{23},m_{13}) \f$| \f$ \theta_1=0\\ \theta_2=0\\ \theta_3=\arctan2(m_{21},m_{11}) \f$| \f$ \theta_1= 0\\ \theta_2=\pi\\ \theta_3=\arctan2(m_{21},m_{11}) \f$
*
* @param eulerAnglesType the convertion Euler angles type
* @param eulerAnglesType the conversion Euler angles type
*/
Vec<_Tp, 3> toEulerAngles(QuatEnum::EulerAnglesType eulerAnglesType);
+15 -28
View File
@@ -4,14 +4,10 @@ import java.nio.ByteBuffer;
// C++: class Mat
//javadoc: Mat
public class Mat {
public final long nativeObj;
public class Mat extends CleanableMat {
public Mat(long addr) {
if (addr == 0)
throw new UnsupportedOperationException("Native object address is NULL");
nativeObj = addr;
super(addr);
}
//
@@ -20,7 +16,7 @@ public class Mat {
// javadoc: Mat::Mat()
public Mat() {
nativeObj = n_Mat();
super(n_Mat());
}
//
@@ -29,7 +25,7 @@ public class Mat {
// javadoc: Mat::Mat(rows, cols, type)
public Mat(int rows, int cols, int type) {
nativeObj = n_Mat(rows, cols, type);
super(n_Mat(rows, cols, type));
}
//
@@ -38,7 +34,7 @@ public class Mat {
// javadoc: Mat::Mat(rows, cols, type, data)
public Mat(int rows, int cols, int type, ByteBuffer data) {
nativeObj = n_Mat(rows, cols, type, data);
super(n_Mat(rows, cols, type, data));
}
//
@@ -47,7 +43,7 @@ public class Mat {
// javadoc: Mat::Mat(rows, cols, type, data, step)
public Mat(int rows, int cols, int type, ByteBuffer data, long step) {
nativeObj = n_Mat(rows, cols, type, data, step);
super(n_Mat(rows, cols, type, data, step));
}
//
@@ -56,7 +52,7 @@ public class Mat {
// javadoc: Mat::Mat(size, type)
public Mat(Size size, int type) {
nativeObj = n_Mat(size.width, size.height, type);
super(n_Mat(size.width, size.height, type));
}
//
@@ -65,7 +61,7 @@ public class Mat {
// javadoc: Mat::Mat(sizes, type)
public Mat(int[] sizes, int type) {
nativeObj = n_Mat(sizes.length, sizes, type);
super(n_Mat(sizes.length, sizes, type));
}
//
@@ -74,7 +70,7 @@ public class Mat {
// javadoc: Mat::Mat(rows, cols, type, s)
public Mat(int rows, int cols, int type, Scalar s) {
nativeObj = n_Mat(rows, cols, type, s.val[0], s.val[1], s.val[2], s.val[3]);
super(n_Mat(rows, cols, type, s.val[0], s.val[1], s.val[2], s.val[3]));
}
//
@@ -83,7 +79,7 @@ public class Mat {
// javadoc: Mat::Mat(size, type, s)
public Mat(Size size, int type, Scalar s) {
nativeObj = n_Mat(size.width, size.height, type, s.val[0], s.val[1], s.val[2], s.val[3]);
super(n_Mat(size.width, size.height, type, s.val[0], s.val[1], s.val[2], s.val[3]));
}
//
@@ -92,7 +88,7 @@ public class Mat {
// javadoc: Mat::Mat(sizes, type, s)
public Mat(int[] sizes, int type, Scalar s) {
nativeObj = n_Mat(sizes.length, sizes, type, s.val[0], s.val[1], s.val[2], s.val[3]);
super(n_Mat(sizes.length, sizes, type, s.val[0], s.val[1], s.val[2], s.val[3]));
}
//
@@ -101,12 +97,12 @@ public class Mat {
// javadoc: Mat::Mat(m, rowRange, colRange)
public Mat(Mat m, Range rowRange, Range colRange) {
nativeObj = n_Mat(m.nativeObj, rowRange.start, rowRange.end, colRange.start, colRange.end);
super(n_Mat(m.nativeObj, rowRange.start, rowRange.end, colRange.start, colRange.end));
}
// javadoc: Mat::Mat(m, rowRange)
public Mat(Mat m, Range rowRange) {
nativeObj = n_Mat(m.nativeObj, rowRange.start, rowRange.end);
super(n_Mat(m.nativeObj, rowRange.start, rowRange.end));
}
//
@@ -115,7 +111,7 @@ public class Mat {
// javadoc: Mat::Mat(m, ranges)
public Mat(Mat m, Range[] ranges) {
nativeObj = n_Mat(m.nativeObj, ranges);
super(n_Mat(m.nativeObj, ranges));
}
//
@@ -124,7 +120,7 @@ public class Mat {
// javadoc: Mat::Mat(m, roi)
public Mat(Mat m, Rect roi) {
nativeObj = n_Mat(m.nativeObj, roi.y, roi.y + roi.height, roi.x, roi.x + roi.width);
super(n_Mat(m.nativeObj, roi.y, roi.y + roi.height, roi.x, roi.x + roi.width));
}
//
@@ -754,12 +750,6 @@ public class Mat {
return new Mat(n_zeros(sizes.length, sizes, type));
}
@Override
protected void finalize() throws Throwable {
n_delete(nativeObj);
super.finalize();
}
// javadoc:Mat::toString()
@Override
public String toString() {
@@ -1834,9 +1824,6 @@ public class Mat {
// C++: static Mat Mat::zeros(int ndims, const int* sizes, int type)
private static native long n_zeros(int ndims, int[] sizes, int type);
// native support for java finalize()
private static native void n_delete(long nativeObj);
private static native int nPutD(long self, int row, int col, int count, double[] data);
private static native int nPutDIdx(long self, int[] idx, int count, double[] data);
+16
View File
@@ -283,24 +283,40 @@ inline IppCmpOp arithm_ipp_convert_cmp(int cmpop)
inline int arithm_ipp_cmp8u(const uchar* src1, size_t step1, const uchar* src2, size_t step2,
uchar* dst, size_t step, int width, int height, int cmpop)
{
// perf regression with AVX512: https://github.com/opencv/opencv/issues/28251
if (ippCPUID_AVX512F&cv::ipp::getIppFeatures())
return 0;
ARITHM_IPP_CMP(ippiCompare_8u_C1R, src1, (int)step1, src2, (int)step2, dst, (int)step, ippiSize(width, height));
}
inline int arithm_ipp_cmp16u(const ushort* src1, size_t step1, const ushort* src2, size_t step2,
uchar* dst, size_t step, int width, int height, int cmpop)
{
// perf regression with AVX512: https://github.com/opencv/opencv/issues/28251
if (ippCPUID_AVX512F&cv::ipp::getIppFeatures())
return 0;
ARITHM_IPP_CMP(ippiCompare_16u_C1R, src1, (int)step1, src2, (int)step2, dst, (int)step, ippiSize(width, height));
}
inline int arithm_ipp_cmp16s(const short* src1, size_t step1, const short* src2, size_t step2,
uchar* dst, size_t step, int width, int height, int cmpop)
{
// perf regression with AVX512: https://github.com/opencv/opencv/issues/28251
if (ippCPUID_AVX512F&cv::ipp::getIppFeatures())
return 0;
ARITHM_IPP_CMP(ippiCompare_16s_C1R, src1, (int)step1, src2, (int)step2, dst, (int)step, ippiSize(width, height));
}
inline int arithm_ipp_cmp32f(const float* src1, size_t step1, const float* src2, size_t step2,
uchar* dst, size_t step, int width, int height, int cmpop)
{
// perf regression with AVX512: https://github.com/opencv/opencv/issues/28251
if (ippCPUID_AVX512F&cv::ipp::getIppFeatures())
return 0;
ARITHM_IPP_CMP(ippiCompare_32f_C1R, src1, (int)step1, src2, (int)step2, dst, (int)step, ippiSize(width, height));
}
+14 -2
View File
@@ -69,10 +69,17 @@
#include <sanitizer/msan_interface.h>
#define CV_ANNOTATE_MEMORY_IS_INITIALIZED(address, size) \
__msan_unpoison(address, size)
#define CV_ANNOTATE_NO_SANITIZE_MEMORY __attribute__((no_sanitize("memory")))
#endif
#endif
#ifndef CV_ANNOTATE_MEMORY_IS_INITIALIZED
#define CV_ANNOTATE_MEMORY_IS_INITIALIZED(address, size) do { } while(0)
#define CV_ANNOTATE_NO_SANITIZE_MEMORY
#endif
#if defined(__APPLE__) && defined(__clang__)
#pragma clang diagnostic push
#pragma clang diagnostic ignored "-Wdeprecated-declarations"
#endif
//lapack stores matrices in column-major order so transposing is needed everywhere
@@ -108,8 +115,9 @@ set_value(fptype *dst, size_t dst_ld, fptype value, size_t m, size_t n)
dst[i*dst_ld + j] = value;
}
// MSAN can't see that the fortran LAPACK functions initialize `info`
template <typename fptype> static inline int
lapack_LU(fptype* a, size_t a_step, int m, fptype* b, size_t b_step, int n, int* info)
CV_ANNOTATE_NO_SANITIZE_MEMORY lapack_LU(fptype* a, size_t a_step, int m, fptype* b, size_t b_step, int n, int* info)
{
#if defined (ACCELERATE_NEW_LAPACK) && defined (ACCELERATE_LAPACK_ILP64)
cv::AutoBuffer<long> piv_buff(m);
@@ -710,4 +718,8 @@ int lapack_gemm64fc(const double *src1, size_t src1_step, const double *src2, si
return lapack_gemm_c(src1, src1_step, src2, src2_step, alpha, src3, src3_step, beta, dst, dst_step, m, n, k, flags);
}
#endif //HAVE_LAPACK
#if defined(__APPLE__) && defined(__clang__)
#pragma clang diagnostic pop
#endif
#endif //HAVE_LAPACK
+24 -5
View File
@@ -46,6 +46,8 @@
#include <atomic>
#include <limits>
#include <iostream>
#include <algorithm>
#include <cmath>
#include "mathfuncs.hpp"
namespace cv
@@ -1423,12 +1425,21 @@ int cv::solveCubic( InputArray _coeffs, OutputArray _roots )
a3 = coeffs.at<double>(i+3);
}
if( a0 == 0 )
// Fix for Issue #27748: Normalize coefficients to avoid overflow/underflow
// and correctly detect negligible cubic terms.
double max_coeff = std::max({std::abs(a0), std::abs(a1), std::abs(a2), std::abs(a3)});
// If max_coeff is effectively zero, the equation is 0=0
if (max_coeff < std::numeric_limits<double>::epsilon())
return -1;
// Check if the cubic term is negligible relative to the largest coefficient
if( std::abs(a0) < std::numeric_limits<double>::epsilon() * max_coeff )
{
if( a1 == 0 )
if( std::abs(a1) < std::numeric_limits<double>::epsilon() * max_coeff )
{
if( a2 == 0 ) // constant
n = a3 == 0 ? -1 : 0;
if( std::abs(a2) < std::numeric_limits<double>::epsilon() * max_coeff )
n = std::abs(a3) < std::numeric_limits<double>::epsilon() * max_coeff ? -1 : 0;
else
{
// linear equation
@@ -1445,7 +1456,7 @@ int cv::solveCubic( InputArray _coeffs, OutputArray _roots )
d = std::sqrt(d);
double q1 = (-a2 + d) * 0.5;
double q2 = (a2 + d) * -0.5;
if( fabs(q1) > fabs(q2) )
if( std::abs(q1) > std::abs(q2) )
{
x0 = q1 / a1;
x1 = a3 / q1;
@@ -1462,6 +1473,13 @@ int cv::solveCubic( InputArray _coeffs, OutputArray _roots )
else
{
// cubic equation
// Normalize coefficients in-place to prevent overflow/instability
double scale = 1.0 / max_coeff;
a0 *= scale;
a1 *= scale;
a2 *= scale;
a3 *= scale;
a0 = 1./a0;
a1 *= a0;
a2 *= a0;
@@ -1470,6 +1488,7 @@ int cv::solveCubic( InputArray _coeffs, OutputArray _roots )
double Q = (a1 * a1 - 3 * a2) * (1./9);
double R = (a1 * (2 * a1 * a1 - 9 * a2) + 27 * a3) * (1./54);
double Qcubed = Q * Q * Q;
/*
Here we expand expression `Qcubed - R * R` for `d` variable
to reduce common terms `a1^6 / 729` and `-a1^4 * a2 / 81`
@@ -93,7 +93,7 @@ static void* AppleCLGetProcAddress(const char* name)
handle = dlopen(path.c_str(), RTLD_LAZY | RTLD_GLOBAL);
if (handle == NULL)
{
if (path != NULL && path != defaultPath)
if (!path.empty() && path != defaultPath)
fprintf(stderr, ERROR_MSG_CANT_LOAD);
}
else if (dlsym(handle, OPENCL_FUNC_TO_CHECK_1_1) == NULL)
-4
View File
@@ -465,10 +465,6 @@ namespace {
static inline int _initMaxThreads()
{
int maxThreads = omp_get_max_threads();
if (!utils::getConfigurationParameterBool("OPENCV_FOR_OPENMP_DYNAMIC_DISABLE", false))
{
omp_set_dynamic(1);
}
return maxThreads;
}
static int numThreadsMax = _initMaxThreads();
+3
View File
@@ -179,6 +179,7 @@ const uint64_t AT_HWCAP = NT_GNU_HWCAP;
#define _WIN32_WINNT 0x0400 // http://msdn.microsoft.com/en-us/library/ms686857(VS.85).aspx
#endif
#include <windows.h>
#include <combaseapi.h>
#if (_WIN32_WINNT >= 0x0602)
#include <synchapi.h>
#endif
@@ -478,6 +479,7 @@ struct HWFeatures
g_hwFeatureNames[CPU_NEON_DOTPROD] = "NEON_DOTPROD";
g_hwFeatureNames[CPU_NEON_FP16] = "NEON_FP16";
g_hwFeatureNames[CPU_NEON_BF16] = "NEON_BF16";
g_hwFeatureNames[CPU_SVE] = "SVE";
g_hwFeatureNames[CPU_VSX] = "VSX";
g_hwFeatureNames[CPU_VSX3] = "VSX3";
@@ -641,6 +643,7 @@ struct HWFeatures
{
have[CV_CPU_NEON_DOTPROD] = (auxv.a_un.a_val & (1 << 20)) != 0; // HWCAP_ASIMDDP
have[CV_CPU_NEON_FP16] = (auxv.a_un.a_val & (1 << 10)) != 0; // HWCAP_ASIMDHP
have[CV_CPU_SVE] = (auxv.a_un.a_val & (1 << 22)) != 0; // HWCAP_SVE
}
#if defined(AT_HWCAP2)
else if (auxv.a_type == AT_HWCAP2)
+39
View File
@@ -1037,6 +1037,14 @@ static void flip(const Mat& src, Mat& dst, int flipcode)
}
}
static void flip_inplace(Mat& dst, int flipcode)
{
Mat m;
m.create(dst.size(), dst.type());
reference::flip(dst, m, flipcode);
memcpy(dst.ptr<uchar>(), m.ptr<uchar>(), dst.total() * dst.elemSize());
}
static void rotate(const Mat& src, Mat& dst, int rotateMode)
{
Mat tmp;
@@ -1103,6 +1111,36 @@ struct FlipOp : public BaseElemWiseOp
int flipcode;
};
struct FlipInplaceOp : public BaseElemWiseOp
{
FlipInplaceOp() : BaseElemWiseOp(1, FIX_ALPHA+FIX_BETA+FIX_GAMMA, 1, 1, Scalar::all(0)) { flipcode = 0; }
void getRandomSize(RNG& rng, vector<int>& size)
{
cvtest::randomSize(rng, 2, 2, ARITHM_MAX_SIZE_LOG, size);
}
void op(const vector<Mat>& src, Mat& dst, const Mat&)
{
dst.create(src[0].size(), src[0].type());
memcpy(dst.ptr<uchar>(), src[0].ptr<uchar>(), src[0].total() * src[0].elemSize());
cv::flip(dst, dst, flipcode);
}
void refop(const vector<Mat>& src, Mat& dst, const Mat&)
{
dst.create(src[0].size(), src[0].type());
memcpy(dst.ptr<uchar>(), src[0].ptr<uchar>(), src[0].total() * src[0].elemSize());
reference::flip_inplace(dst, flipcode);
}
void generateScalars(int, RNG& rng)
{
flipcode = rng.uniform(0, 3) - 1;
}
double getMaxErr(int)
{
return 0;
}
int flipcode;
};
struct RotateOp : public BaseElemWiseOp
{
RotateOp() : BaseElemWiseOp(1, FIX_ALPHA+FIX_BETA+FIX_GAMMA, 1, 1, Scalar::all(0)) { rotatecode = 0; }
@@ -1789,6 +1827,7 @@ INSTANTIATE_TEST_CASE_P(Core_InRange, ElemWiseTest, ::testing::Values(ElemWiseOp
INSTANTIATE_TEST_CASE_P(Core_FiniteMask, ElemWiseTest, ::testing::Values(ElemWiseOpPtr(new FiniteMaskOp)));
INSTANTIATE_TEST_CASE_P(Core_Flip, ElemWiseTest, ::testing::Values(ElemWiseOpPtr(new FlipOp)));
INSTANTIATE_TEST_CASE_P(Core_FlipInplace, ElemWiseTest, ::testing::Values(ElemWiseOpPtr(new FlipInplaceOp)));
INSTANTIATE_TEST_CASE_P(Core_Rotate, ElemWiseTest, ::testing::Values(ElemWiseOpPtr(new RotateOp)));
INSTANTIATE_TEST_CASE_P(Core_Transpose, ElemWiseTest, ::testing::Values(ElemWiseOpPtr(new TransposeOp)));
INSTANTIATE_TEST_CASE_P(Core_SetIdentity, ElemWiseTest, ::testing::Values(ElemWiseOpPtr(new SetIdentityOp)));
+27
View File
@@ -1917,6 +1917,33 @@ TEST(Core_SolveCubic, regression_27323)
}
}
TEST(Core_SolveCubic, regression_27748)
{
// a is extremely small relative to others (approx 1.8e-19 ratio),
// causing instability in standard cubic formula.
double a = 1.56041e-17;
double b = 84.4504;
double c = -96.795;
double d = 13.6826;
Mat coeffs = (Mat_<double>(1, 4) << a, b, c, d);
Mat roots;
int n = solveCubic(coeffs, roots);
// Expecting quadratic behavior (2 roots)
EXPECT_GE(n, 2);
// Verify roots satisfy the FULL cubic equation
for(int i = 0; i < n; i++)
{
double x = roots.at<double>(i);
double val = a*x*x*x + b*x*x + c*x + d;
// Check residual is small
EXPECT_LE(fabs(val), 1e-6) << "Root " << x << " does not satisfy the equation";
}
}
TEST(Core_SolvePoly, regression_5599)
{
// x^4 - x^2 = 0, roots: 1, -1, 0, 0
+1 -1
View File
@@ -4,7 +4,7 @@ endif()
set(the_description "Deep neural network module. It allows to load models from different frameworks and to make forward pass")
ocv_add_dispatched_file_force_all("layers/layers_common" AVX AVX2 AVX512_SKX RVV LASX NEON)
ocv_add_dispatched_file_force_all("layers/layers_common" AVX AVX2 AVX512_SKX RVV LASX NEON SVE)
ocv_add_dispatched_file_force_all("int8layers/layers_common" AVX2 AVX512_SKX RVV LASX NEON)
ocv_add_dispatched_file_force_all("layers/cpu_kernels/conv_block" AVX AVX2 NEON NEON_FP16)
ocv_add_dispatched_file_force_all("layers/cpu_kernels/conv_depthwise" AVX AVX2 RVV LASX)
+1 -1
View File
@@ -6,7 +6,7 @@
#define OPENCV_DNN_VERSION_HPP
/// Use with major OpenCV version only.
#define OPENCV_DNN_API_VERSION 20250619
#define OPENCV_DNN_API_VERSION 20251223
#if !defined CV_DOXYGEN && !defined CV_STATIC_ANALYSIS && !defined CV_DNN_DONT_ADD_INLINE_NS
#define CV__DNN_INLINE_NS __CV_CAT(dnn5_v, OPENCV_DNN_API_VERSION)
+1 -1
View File
@@ -228,7 +228,7 @@ void blobFromImagesNCHW(const std::vector<Mat>& images, Mat& blob_, const Image2
template<typename Tout>
void blobFromImagesNCHW(const std::vector<UMat>& images, UMat& blob_, const Image2BlobParams& param)
{
CV_Error(Error::StsNotImplemented, "");
CV_Error(Error::StsNotImplemented, "blobFromImagesNCHW is not implemented for UMat inputs");
}
template<class Tmat>
+2 -2
View File
@@ -116,14 +116,14 @@ public:
{
case CV_32F: break;
case CV_32S: ge_dtype = ge::DT_INT32; break;
default: CV_Error(Error::StsNotImplemented, "Unsuppported data type");
default: CV_Error(Error::StsNotImplemented, "Unsupported data type");
}
auto size_of_type = sizeof(float);
switch (blobs[0].type())
{
case CV_32F: break;
case CV_32S: size_of_type = sizeof(int); break;
default: CV_Error(Error::StsNotImplemented, "Unsuppported data type");
default: CV_Error(Error::StsNotImplemented, "Unsupported data type");
}
auto desc = std::make_shared<ge::TensorDesc>(ge_shape, ge::FORMAT_NCHW, ge_dtype);
@@ -227,6 +227,7 @@ public:
p.useAVX512 = CV_CPU_HAS_SUPPORT_AVX512_SKX;
p.useRVV = checkHardwareSupport(CPU_RVV);
p.useLASX = checkHardwareSupport(CPU_LASX);
p.useSVE = checkHardwareSupport(CPU_SVE);
parallel_for_(Range(0, nstripes), p, nstripes);
}
@@ -276,6 +277,12 @@ public:
opt_AVX::fastGEMM1T( sptr, wptr, wstep, biasptr, dptr, nw, vecsize_aligned);
else
#endif
#if CV_TRY_SVE
if( useSVE ) {
opt_SVE::fastGEMM1T( sptr, wptr, wstep, biasptr, dptr, nw, vecsize_aligned);
}
else
#endif
#if CV_TRY_RVV && CV_RVV
if( useRVV )
opt_RVV::fastGEMM1T( sptr, wptr, wstep, biasptr, dptr, nw, vecsize);
@@ -341,6 +348,7 @@ public:
bool useAVX512;
bool useRVV;
bool useLASX;
bool useSVE;
};
#ifdef HAVE_OPENCL
@@ -221,7 +221,7 @@ public:
#ifdef HAVE_DNN_NGRAPH
virtual Ptr<BackendNode> initNgraph(const std::vector<Ptr<BackendWrapper> >& inputs,
const std::vector<Ptr<BackendNode> >& nodes) CV_OVERRIDE {
// onnx to openvino convertion: https://github.com/openvinotoolkit/openvino/blob/2023.1.0/src/frontends/onnx/frontend/src/op/instance_norm.cpp
// onnx to openvino conversion: https://github.com/openvinotoolkit/openvino/blob/2023.1.0/src/frontends/onnx/frontend/src/op/instance_norm.cpp
auto ieInpNode = nodes[0].dynamicCast<InfEngineNgraphNode>()->node;
const auto &input_shape = ieInpNode.get_shape();
+113 -1
View File
@@ -53,7 +53,119 @@ void fastGEMM( const float* aptr, size_t astep, const float* bptr,
size_t bstep, float* cptr, size_t cstep,
int ma, int na, int nb );
#if !defined(CV_CPU_OPTIMIZATION_DECLARATIONS_ONLY) && CV_NEON
#if !defined(CV_CPU_OPTIMIZATION_DECLARATIONS_ONLY) && defined(CV_CPU_COMPILE_SVE)
#include <arm_sve.h>
// dst = vec * weights^t + bias
void fastGEMM1T( const float* vec, const float* weights,
size_t wstep, const float* bias,
float* dst, int nvecs, int vecsize )
{
svbool_t pg_all = svptrue_b32();
int i = 0;
int vl = svcntw();
for( ; i <= nvecs - 15; i += 15 )
{
const float* wrow0 = weights + i * wstep; // base pointer for row i
// we will use wrow0 + k, wrow0 + wstep + k, etc
svfloat32_t vs0 = svdup_f32(0.0f), vs1 = svdup_f32(0.0f),
vs2 = svdup_f32(0.0f), vs3 = svdup_f32(0.0f),
vs4 = svdup_f32(0.0f), vs5 = svdup_f32(0.0f),
vs6 = svdup_f32(0.0f), vs7 = svdup_f32(0.0f),
vs8 = svdup_f32(0.0f), vs9 = svdup_f32(0.0f),
vs10 = svdup_f32(0.0f), vs11 = svdup_f32(0.0f),
vs12 = svdup_f32(0.0f), vs13 = svdup_f32(0.0f),
vs14 = svdup_f32(0.0f);
int k = 0;
for( ; k <= vecsize - vl; k += vl )
{
// load input chunk
const float* vecptr = reinterpret_cast<const float*>(vec) + k;
svfloat32_t v = svld1_f32(pg_all, vecptr);
// load weights from each of 15 rows at offset k
vs0 = svmla_f32_m(pg_all, vs0, svld1_f32(pg_all, wrow0 + k), v);
vs1 = svmla_f32_m(pg_all, vs1, svld1_f32(pg_all, wrow0 + wstep + k), v);
vs2 = svmla_f32_m(pg_all, vs2, svld1_f32(pg_all, wrow0 + wstep*2 + k), v);
vs3 = svmla_f32_m(pg_all, vs3, svld1_f32(pg_all, wrow0 + wstep*3 + k), v);
vs4 = svmla_f32_m(pg_all, vs4, svld1_f32(pg_all, wrow0 + wstep*4 + k), v);
vs5 = svmla_f32_m(pg_all, vs5, svld1_f32(pg_all, wrow0 + wstep*5 + k), v);
vs6 = svmla_f32_m(pg_all, vs6, svld1_f32(pg_all, wrow0 + wstep*6 + k), v);
vs7 = svmla_f32_m(pg_all, vs7, svld1_f32(pg_all, wrow0 + wstep*7 + k), v);
vs8 = svmla_f32_m(pg_all, vs8, svld1_f32(pg_all, wrow0 + wstep*8 + k), v);
vs9 = svmla_f32_m(pg_all, vs9, svld1_f32(pg_all, wrow0 + wstep*9 + k), v);
vs10 = svmla_f32_m(pg_all, vs10, svld1_f32(pg_all, wrow0 + wstep*10 + k), v);
vs11 = svmla_f32_m(pg_all, vs11, svld1_f32(pg_all, wrow0 + wstep*11 + k), v);
vs12 = svmla_f32_m(pg_all, vs12, svld1_f32(pg_all, wrow0 + wstep*12 + k), v);
vs13 = svmla_f32_m(pg_all, vs13, svld1_f32(pg_all, wrow0 + wstep*13 + k), v);
vs14 = svmla_f32_m(pg_all, vs14, svld1_f32(pg_all, wrow0 + wstep*14 + k), v);
}
if(k < vecsize){
svbool_t pg_tail = svwhilelt_b32(k, vecsize);
const float* vecptr = reinterpret_cast<const float*>(vec) + k;
svfloat32_t v = svld1_f32(pg_tail, vecptr);
const float* wptr = wrow0 + k;
vs0 = svmla_f32_m(pg_tail, vs0, svld1_f32(pg_tail, wptr), v);
vs1 = svmla_f32_m(pg_tail, vs1, svld1_f32(pg_tail, wptr + wstep), v);
vs2 = svmla_f32_m(pg_tail, vs2, svld1_f32(pg_tail, wptr + wstep*2), v);
vs3 = svmla_f32_m(pg_tail, vs3, svld1_f32(pg_tail, wptr + wstep*3), v);
vs4 = svmla_f32_m(pg_tail, vs4, svld1_f32(pg_tail, wptr + wstep*4), v);
vs5 = svmla_f32_m(pg_tail, vs5, svld1_f32(pg_tail, wptr + wstep*5), v);
vs6 = svmla_f32_m(pg_tail, vs6, svld1_f32(pg_tail, wptr + wstep*6), v);
vs7 = svmla_f32_m(pg_tail, vs7, svld1_f32(pg_tail, wptr + wstep*7), v);
vs8 = svmla_f32_m(pg_tail, vs8, svld1_f32(pg_tail, wptr + wstep*8), v);
vs9 = svmla_f32_m(pg_tail, vs9, svld1_f32(pg_tail, wptr + wstep*9), v);
vs10 = svmla_f32_m(pg_tail, vs10, svld1_f32(pg_tail, wptr + wstep*10), v);
vs11 = svmla_f32_m(pg_tail, vs11, svld1_f32(pg_tail, wptr + wstep*11), v);
vs12 = svmla_f32_m(pg_tail, vs12, svld1_f32(pg_tail, wptr + wstep*12), v);
vs13 = svmla_f32_m(pg_tail, vs13, svld1_f32(pg_tail, wptr + wstep*13), v);
vs14 = svmla_f32_m(pg_tail, vs14, svld1_f32(pg_tail, wptr + wstep*14), v);
}
float sum[15];
sum[0] = svaddv_f32(pg_all, vs0);
sum[1] = svaddv_f32(pg_all, vs1);
sum[2] = svaddv_f32(pg_all, vs2);
sum[3] = svaddv_f32(pg_all, vs3);
sum[4] = svaddv_f32(pg_all, vs4);
sum[5] = svaddv_f32(pg_all, vs5);
sum[6] = svaddv_f32(pg_all, vs6);
sum[7] = svaddv_f32(pg_all, vs7);
sum[8] = svaddv_f32(pg_all, vs8);
sum[9] = svaddv_f32(pg_all, vs9);
sum[10] = svaddv_f32(pg_all, vs10);
sum[11] = svaddv_f32(pg_all, vs11);
sum[12] = svaddv_f32(pg_all, vs12);
sum[13] = svaddv_f32(pg_all, vs13);
sum[14] = svaddv_f32(pg_all, vs14);
for (int j = 0; j < 15; j += vl) {
svbool_t pg = svwhilelt_b32(j, 15);
svfloat32_t v_sum = svld1_f32(pg, sum + j);
svfloat32_t v_bias = svld1_f32(pg, bias + i + j);
svst1_f32(pg, dst + i + j, svadd_f32_z(pg, v_sum, v_bias));
}
}
float temp = 0.f;
for( ; i < nvecs; i++ )
{
const float* wrow = weights + i * wstep;
svfloat32_t vs0 = svdup_f32(0.0f);
int k = 0;
for( ; k <= vecsize - vl; k += vl )
{
svfloat32_t v = svld1_f32(pg_all, reinterpret_cast<const float*>(vec) + k);
vs0 = svmla_f32_m(pg_all, vs0, svld1_f32(pg_all, wrow + k), v);
}
if (k != vecsize) {
svbool_t pg_tail = svwhilelt_b32(k, vecsize);
svfloat32_t v = svld1_f32(pg_tail, reinterpret_cast<const float*>(vec) + k);
vs0 = svmla_f32_m(pg_tail, vs0, svld1_f32(pg_tail, wrow + k), v);
}
temp = svaddv_f32(pg_all, vs0);
dst[i] = temp + bias[i];
}
}
#endif
#if !defined(CV_CPU_OPTIMIZATION_DECLARATIONS_ONLY) && CV_NEON && !defined(CV_CPU_COMPILE_SVE)
static const uint32_t tailMaskArray[7] = {
0u, 0u, 0u, 0u,
+1 -1
View File
@@ -423,7 +423,7 @@ class MatMulLayerImpl CV_FINAL : public MatMulLayer {
op->set_input_x1_by_name(*input_A_node, input_A_wrapper->name.c_str());
op->update_input_desc_x1(*input_A_desc);
// set inputs : x2
if (blobs.empty()) { // varaible input B
if (blobs.empty()) { // variable input B
auto input_B_wrapper = inputs[1].dynamicCast<CannBackendWrapper>();
auto input_B_desc = input_B_wrapper->getTensorDesc();
auto input_B_node = nodes[1].dynamicCast<CannBackendNode>()->getOp();
@@ -548,8 +548,8 @@ public:
int nplanes = std::accumulate(shape.begin(), shape.end() - 1, 1, std::multiplies<int>());
if (nplanes == 1) { // parallelize within the plane
AutoBuffer<char> buf_ptrs(steps.size());
auto ptrs = (char**)buf_ptrs.data();
AutoBuffer<char*> buf_ptrs(steps.size());
char** ptrs = buf_ptrs.data();
ptrs[0] = out;
for (int i = 0; i < ninputs; i++) {
ptrs[i+1] = (char*)inp[i];
@@ -594,8 +594,8 @@ public:
parallel_for_(Range(0, plane_size), worker, nstripes);
} else { // parallelize across the plane
auto worker = [&](const Range &r) {
AutoBuffer<char> buf_ptrs(steps.size());
auto ptrs = (char**)buf_ptrs.data();
AutoBuffer<char*> buf_ptrs(steps.size());
char** ptrs = buf_ptrs.data();
for (int plane_idx = r.start; plane_idx < r.end; plane_idx++) {
ptrs[0] = out;
for (int i = 0; i < ninputs; i++) ptrs[i+1] = (char*)inp[i];
@@ -1318,7 +1318,7 @@ public:
node = std::make_shared<ov::op::v1::Select>(inp0, inp1, inp2);
}
// Ideally we should do this but int32 internal blobs are converted to float32 data type in inference.
// TODO: Remove data type convertion when we have type inference.
// TODO: Remove data type conversion when we have type inference.
else if (op == OPERATION::MOD) {
auto inp0_i64 = std::make_shared<ov::op::v0::Convert>(inp0, ov::element::i64);
auto inp1_i64 = std::make_shared<ov::op::v0::Convert>(inp1, ov::element::i64);
+48 -1
View File
@@ -138,6 +138,9 @@ class LSTMLayerImpl CV_FINAL : public LSTMLayer
#if CV_TRY_AVX2
bool useAVX2;
#endif
#if CV_TRY_SVE
bool useSVE;
#endif
#if CV_TRY_NEON
bool useNEON;
#endif
@@ -156,6 +159,9 @@ public:
#if CV_TRY_AVX2
, useAVX2(checkHardwareSupport(CPU_AVX2))
#endif
#if CV_TRY_SVE
, useSVE(checkHardwareSupport(CPU_SVE))
#endif
#if CV_TRY_NEON
, useNEON(checkHardwareSupport(CPU_NEON))
#endif
@@ -410,7 +416,7 @@ public:
if (layout == BATCH_SEQ_HID){
//swap axis 0 and 1 input x
cv::Mat tmp;
// Since python input is 4 dimentional and C++ input 3 dimentinal
// Since python input is 4 dimensional and C++ input 3 dimensional
// we need to process each differently
if (input[0].dims == 4){
// here !!!
@@ -495,6 +501,13 @@ public:
&& Wh.depth() == CV_32F && hInternal.depth() == CV_32F && gates.depth() == CV_32F
&& Wh.cols >= 8;
#endif
#if CV_TRY_SVE
bool canUseSVE = gates.isContinuous() && bias.isContinuous()
&& Wx.depth() == CV_32F && gates.depth() == CV_32F
&& bias.depth() == CV_32F;
bool canUseSVE_hInternal = hInternal.isContinuous() && gates.isContinuous() && bias.isContinuous()
&& Wh.depth() == CV_32F && hInternal.depth() == CV_32F && gates.depth() == CV_32F;
#endif
#if CV_TRY_NEON
bool canUseNeon = gates.isContinuous() && bias.isContinuous()
&& Wx.depth() == CV_32F && gates.depth() == CV_32F
@@ -554,6 +567,23 @@ public:
}
else
#endif
#if CV_TRY_SVE
if (useSVE && canUseSVE && xCurr.isContinuous())
{
for (int n = 0; n < xCurr.rows; n++) {
opt_SVE::fastGEMM1T(
xCurr.ptr<float>(n),
Wx.ptr<float>(),
Wx.step1(),
bias.ptr<float>(),
gates.ptr<float>(n),
Wx.rows,
Wx.cols
);
}
}
else
#endif
#if CV_TRY_NEON
if (useNEON && canUseNeon && xCurr.isContinuous())
{
@@ -610,6 +640,23 @@ public:
}
else
#endif
#if CV_TRY_SVE
if (useSVE && canUseSVE_hInternal)
{
for (int n = 0; n < hInternal.rows; n++) {
opt_SVE::fastGEMM1T(
hInternal.ptr<float>(n),
Wh.ptr<float>(),
Wh.step1(),
gates.ptr<float>(n),
gates.ptr<float>(n),
Wh.rows,
Wh.cols
);
}
}
else
#endif
#if CV_TRY_NEON
if (useNEON && canUseNeon_hInternal)
{
+43
View File
@@ -194,6 +194,7 @@ private:
void parseElementWise (LayerParams& layerParams, const opencv_onnx::NodeProto& node_proto);
void parseDepthSpaceOps (LayerParams& layerParams, const opencv_onnx::NodeProto& node_proto);
void parseRange (LayerParams& layerParams, const opencv_onnx::NodeProto& node_proto);
void parseRandomNormalLike (LayerParams& layerParams, const opencv_onnx::NodeProto& node_proto);
void parseScatter (LayerParams& layerParams, const opencv_onnx::NodeProto& node_proto);
void parseTile (LayerParams& layerParams, const opencv_onnx::NodeProto& node_proto);
void parseLayerNorm (LayerParams& layerParams, const opencv_onnx::NodeProto& node_proto);
@@ -2046,6 +2047,25 @@ void ONNXImporter::parseConv(LayerParams& layerParams, const opencv_onnx::NodePr
layerParams.blobs.push_back(getBlob(node_proto, j));
}
}
// ONNX allows omitting 'kernel_shape' attribute for Conv. In that case, it should be inferred from weights.
// See: https://onnx.ai/onnx/operators/onnx__Conv.html
if (!layerParams.has("kernel_size"))
{
Mat weights;
if (!layerParams.blobs.empty())
weights = layerParams.blobs[0];
else if (constBlobs.find(node_proto.input(1)) != constBlobs.end())
weights = getBlob(node_proto, 1);
if (!weights.empty() && weights.dims >= 3)
{
const int kDims = weights.dims - 2;
std::vector<int32_t> kernel(kDims);
for (int i = 0; i < kDims; ++i)
kernel[i] = weights.size[2 + i];
layerParams.set("kernel_size", DictValue::arrayInt(kernel.data(), static_cast<int>(kernel.size())));
}
}
int outCn = layerParams.blobs.empty() ? outShapes[node_proto.input(1)][0] : layerParams.blobs[0].size[0];
layerParams.set("num_output", outCn);
@@ -2062,6 +2082,20 @@ void ONNXImporter::parseConvTranspose(LayerParams& layerParams, const opencv_onn
layerParams.set("num_output", layerParams.blobs[0].size[1] * layerParams.get<int>("group", 1));
layerParams.set("bias_term", node_proto.input_size() == 3);
// ONNX allows omitting 'kernel_shape' attribute for ConvTranspose. Infer it from weights if needed.
if (!layerParams.has("kernel_size"))
{
const Mat& weights = layerParams.blobs[0];
if (!weights.empty() && weights.dims >= 3)
{
const int kDims = weights.dims - 2;
std::vector<int32_t> kernel(kDims);
for (int i = 0; i < kDims; ++i)
kernel[i] = weights.size[2 + i];
layerParams.set("kernel_size", DictValue::arrayInt(kernel.data(), static_cast<int>(kernel.size())));
}
}
if (!layerParams.has("kernel_size"))
CV_Error(Error::StsNotImplemented,
"Required attribute 'kernel_size' is not present.");
@@ -3006,6 +3040,14 @@ void ONNXImporter::parseRange(LayerParams& layerParams, const opencv_onnx::NodeP
constBlobsExtraInfo.insert(std::make_pair(node_proto.output(0), TensorInfo(1)));
}
void ONNXImporter::parseRandomNormalLike(LayerParams& layerParams, const opencv_onnx::NodeProto& node_proto)
{
CV_CheckEQ(node_proto.input_size(), 1, "RandomNormalLike: one input is required");
layerParams.type = "RandomNormalLike";
addLayer(layerParams, node_proto);
}
void ONNXImporter::parseScatter(LayerParams& layerParams, const opencv_onnx::NodeProto& node_proto)
{
CV_CheckEQ(node_proto.input_size(), 3, "Scatter: three inputs are required.");
@@ -4022,6 +4064,7 @@ void ONNXImporter::buildDispatchMap_ONNX_AI()
dispatch["Sum"] = dispatch["Min"] = dispatch["Max"] = dispatch["Mean"] = &ONNXImporter::parseElementWise;
dispatch["Where"] = &ONNXImporter::parseElementWise;
dispatch["Range"] = &ONNXImporter::parseRange;
dispatch["RandomNormalLike"] = &ONNXImporter::parseRandomNormalLike;
dispatch["Einsum"] = &ONNXImporter::parseEinsum;
dispatch["Hardmax"] = &ONNXImporter::parseHardmax;
dispatch["GatherND"] = &ONNXImporter::parseGatherND;
+9 -2
View File
@@ -334,7 +334,8 @@ TFLiteImporter::DispatchMap TFLiteImporter::buildDispatchMap()
dispatch["DEPTHWISE_CONV_2D"] = &TFLiteImporter::parseDWConvolution;
dispatch["ADD"] = dispatch["MUL"] = dispatch["SUB"] =
dispatch["SQRT"] = dispatch["DIV"] = dispatch["NEG"] =
dispatch["RSQRT"] = dispatch["SQUARED_DIFFERENCE"] = &TFLiteImporter::parseEltwise;
dispatch["RSQRT"] = dispatch["SQUARED_DIFFERENCE"] =
dispatch["MAXIMUM"] = dispatch["MINIMUM"]= &TFLiteImporter::parseEltwise;
dispatch["RELU"] = dispatch["PRELU"] = dispatch["HARD_SWISH"] =
dispatch["LOGISTIC"] = dispatch["LEAKY_RELU"] = &TFLiteImporter::parseActivation;
dispatch["MAX_POOL_2D"] = dispatch["AVERAGE_POOL_2D"] = &TFLiteImporter::parsePooling;
@@ -681,7 +682,13 @@ void TFLiteImporter::parseEltwise(const Operator& op, const std::string& opcode,
}
else if (opcode == "SQRT" && !isOpInt8) {
layerParams.type = "Sqrt";
} else {
}
else if (opcode == "MAXIMUM" && !isOpInt8) {
layerParams.set("operation", "max");
}
else if (opcode == "MINIMUM" && !isOpInt8) {
layerParams.set("operation", "min");
}else {
CV_Error(Error::StsNotImplemented, cv::format("DNN/TFLite: Unknown opcode for %s Eltwise layer '%s'", isOpInt8 ? "INT8" : "FP32", opcode.c_str()));
}
+45
View File
@@ -3465,6 +3465,51 @@ TEST_P(Test_ONNX_layers, TopK) {
test("top_k_smallest");
}
// BUG: https://github.com/opencv/opencv/issues/28532
TEST_P(Test_ONNX_layers, DISABLED_RandomNormalLike_basic)
{
Net net = readNetFromONNX(findDataFile("dnn/onnx/models/random_normal_like.onnx", true));
Mat input(2, 3, CV_32F, Scalar(0));
net.setInput(input);
Mat out = net.forward();
EXPECT_EQ(out.rows, 2);
EXPECT_EQ(out.cols, 3);
EXPECT_EQ(out.type(), CV_32F);
double minVal, maxVal;
minMaxLoc(out, &minVal, &maxVal);
EXPECT_NE(minVal, 0.0);
EXPECT_NE(maxVal, 0.0);
EXPECT_NE(minVal, maxVal);
Mat out2 = net.forward();
EXPECT_EQ(countNonZero(out != out2), 0);
}
// BUG: https://github.com/opencv/opencv/issues/28532
TEST_P(Test_ONNX_layers, DISABLED_RandomNormalLike_complex)
{
Net net = readNetFromONNX(findDataFile("dnn/onnx/models/random_normal_like_complex.onnx", true));
Mat input(2, 3, CV_32F, Scalar(0));
net.setInput(input);
Mat out = net.forward();
EXPECT_EQ(out.rows, 2);
EXPECT_EQ(out.cols, 3);
EXPECT_EQ(out.type(), CV_32F);
double minVal, maxVal;
minMaxLoc(out, &minVal, &maxVal);
EXPECT_NE(minVal, maxVal);
net.setInput(input);
Mat out2 = net.forward();
EXPECT_EQ(countNonZero(out != out2), 0);
}
INSTANTIATE_TEST_CASE_P(/**/, Test_ONNX_nets, dnnBackendsAndTargets());
}} // namespace
+56
View File
@@ -289,6 +289,62 @@ TEST_P(Test_TFLite, face_blendshapes)
testModel("face_blendshapes", inp);
}
TEST_P(Test_TFLite, maximum)
{
Net net = readNetFromTFLite(findDataFile("dnn/tflite/maximum.tflite"));
net.setPreferableBackend(backend);
net.setPreferableTarget(target);
Mat input_x = blobFromNPY(findDataFile("dnn/tflite/maximum_input_x.npy"));
Mat input_y = blobFromNPY(findDataFile("dnn/tflite/maximum_input_y.npy"));
net.setInput(input_x, "x");
net.setInput(input_y, "y");
Mat out = net.forward();
Mat ref = blobFromNPY(findDataFile("dnn/tflite/maximum_output.npy"));
double l1 = 1e-5;
double lInf = 1e-4;
if (target == DNN_TARGET_CUDA_FP16 || target == DNN_TARGET_OPENCL_FP16)
{
l1 = 1e-3;
lInf = 1e-3;
}
normAssert(ref, out, "", l1, lInf);
}
TEST_P(Test_TFLite, minimum)
{
Net net = readNetFromTFLite(findDataFile("dnn/tflite/minimum.tflite"));
net.setPreferableBackend(backend);
net.setPreferableTarget(target);
Mat input_x = blobFromNPY(findDataFile("dnn/tflite/minimum_input_x.npy"));
Mat input_y = blobFromNPY(findDataFile("dnn/tflite/minimum_input_y.npy"));
net.setInput(input_x, "x");
net.setInput(input_y, "y");
Mat out = net.forward();
Mat ref = blobFromNPY(findDataFile("dnn/tflite/minimum_output.npy"));
double l1 = 1e-5;
double lInf = 1e-4;
if (target == DNN_TARGET_CUDA_FP16 || target == DNN_TARGET_OPENCL_FP16)
{
l1 = 1e-3;
lInf = 1e-3;
}
normAssert(ref, out, "", l1, lInf);
}
INSTANTIATE_TEST_CASE_P(/**/, Test_TFLite, dnnBackendsAndTargets());
}} // namespace
+11 -1
View File
@@ -563,7 +563,7 @@ public:
CV_WRAP static Ptr<GFTTDetector> create( int maxCorners=1000, double qualityLevel=0.01, double minDistance=1,
int blockSize=3, bool useHarrisDetector=false, double k=0.04 );
CV_WRAP static Ptr<GFTTDetector> create( int maxCorners, double qualityLevel, double minDistance,
int blockSize, int gradiantSize, bool useHarrisDetector=false, double k=0.04 );
int blockSize, int gradientSize, bool useHarrisDetector=false, double k=0.04 );
CV_WRAP virtual void setMaxFeatures(int maxFeatures) = 0;
CV_WRAP virtual int getMaxFeatures() const = 0;
@@ -645,6 +645,11 @@ public:
CV_PROP_RW bool filterByConvexity;
CV_PROP_RW float minConvexity, maxConvexity;
/** @brief Flag to enable contour collection.
If set to true, the detector will store the contours of the detected blobs in memory,
which can be retrieved after the detect() call using getBlobContours().
@note Default value is false.
*/
CV_PROP_RW bool collectContours;
void read( const FileNode& fn );
@@ -658,6 +663,11 @@ public:
CV_WRAP virtual SimpleBlobDetector::Params getParams() const = 0;
CV_WRAP virtual String getDefaultName() const CV_OVERRIDE;
/** @brief Returns the contours of the blobs detected during the last call to detect().
@note The @ref Params::collectContours parameter must be set to true before calling
detect() for this method to return any data.
*/
CV_WRAP virtual const std::vector<std::vector<cv::Point> >& getBlobContours() const = 0;
};
+6 -2
View File
@@ -1036,7 +1036,11 @@ void ORB_Impl::detectAndCompute( InputArray _image, InputArray _mask,
bool useOCL = false;
#endif
Mat image = _image.getMat(), mask = _mask.getMat();
Mat image = _image.getMat(), mask;
if (!_mask.empty())
{
threshold(_mask, mask, 0, 255, THRESH_BINARY);
}
if( image.type() != CV_8UC1 )
cvtColor(_image, image, COLOR_BGR2GRAY);
@@ -1134,7 +1138,7 @@ void ORB_Impl::detectAndCompute( InputArray _image, InputArray _mask,
}
copyMakeBorder(currImg, extImg, border, border, border, border,
BORDER_REFLECT_101+BORDER_ISOLATED);
BORDER_REFLECT_101 + BORDER_ISOLATED);
if (!mask.empty())
copyMakeBorder(currMask, extMask, border, border, border, border,
BORDER_CONSTANT+BORDER_ISOLATED);
+27
View File
@@ -168,4 +168,31 @@ BIGDATA_TEST(Features2D_ORB, regression_opencv_python_537) // memory usage: ~3
ASSERT_NO_THROW(orbPtr->detectAndCompute(img, noArray(), kps, fv));
}
TEST(Features2D_ORB, MaskValue)
{
Mat gray = imread(cvtest::findDataFile("features2d/tsukuba.png"), IMREAD_GRAYSCALE);
ASSERT_FALSE(gray.empty());
cv::Rect roi(gray.cols/4, gray.rows/4, gray.cols/2, gray.rows/2);
Mat mask255 = Mat::zeros(gray.size(), CV_8UC1);
Mat mask1 = Mat::zeros(gray.size(), CV_8UC1);
mask255(roi).setTo(255);
mask1(roi).setTo(1);
Ptr<ORB> orb = cv::ORB::create();
vector<KeyPoint> keypoints_mask255, keypoints_mask1;
Mat descriptors_mask255, descriptors_mask1;
orb->detectAndCompute(gray, mask255, keypoints_mask255, descriptors_mask255, false);
orb->detectAndCompute(gray, mask1, keypoints_mask1, descriptors_mask1, false);
ASSERT_EQ(keypoints_mask255.size(), keypoints_mask1.size())
<< "Number of keypoints differs between mask values 255 and 1";
Mat diff = descriptors_mask255 != descriptors_mask1;
ASSERT_EQ(countNonZero(diff), 0);
}
}} // namespace
+9 -6
View File
@@ -790,14 +790,17 @@ GuiReceiver::GuiReceiver() : bTimeOut(false), nb_windows(0)
void GuiReceiver::isLastWindow()
{
if (--nb_windows <= 0)
if (qApp->quitOnLastWindowClosed())
{
delete guiMainThread;//delete global_control_panel too
guiMainThread = NULL;
if (doesExternalQAppExist)
if (--nb_windows <= 0)
{
qApp->quit();
delete guiMainThread; // delete global_control_panel too
guiMainThread = NULL;
if (doesExternalQAppExist)
{
qApp->quit();
}
}
}
}
@@ -557,6 +557,7 @@ It also demonstrates how to save multiple images in a TIFF file:
@param filename Name of the file.
@param img (Mat or vector of Mat) Image or Images to be saved.
@param params Format-specific parameters encoded as pairs (paramId_1, paramValue_1, paramId_2, paramValue_2, ... .) see cv::ImwriteFlags
@return true if the image is successfully written to the specified file; false otherwise.
*/
CV_EXPORTS_W bool imwrite( const String& filename, InputArray img,
const std::vector<int>& params = std::vector<int>());
+16 -2
View File
@@ -211,6 +211,10 @@ bool AvifDecoder::readHeader() {
return true;
decoder_ = avifDecoderCreate();
if (!decoder_) {
CV_Error(Error::StsNoMem, "Failed to create AVIF decoder");
return false;
}
decoder_->strictFlags = AVIF_STRICT_DISABLED;
if (!m_buf.empty()) {
CV_Assert(m_buf.type() == CV_8UC1);
@@ -226,6 +230,11 @@ bool AvifDecoder::readHeader() {
decoder_);
OPENCV_AVIF_CHECK_STATUS(avifDecoderParse(decoder_), decoder_);
if (!decoder_->image) {
CV_Error(Error::StsParseError, "AVIF image is null after parsing");
return false;
}
m_width = decoder_->image->width;
m_height = decoder_->image->height;
m_frame_count = decoder_->imageCount;
@@ -380,7 +389,6 @@ bool AvifEncoder::writeanimation(const Animation& animation,
? AVIF_ADD_IMAGE_FLAG_SINGLE
: AVIF_ADD_IMAGE_FLAG_NONE;
std::vector<AvifImageUniquePtr> images;
std::vector<cv::Mat> imgs_scaled;
for (const cv::Mat &img : animation.frames) {
CV_CheckType(
img.type(),
@@ -388,11 +396,17 @@ bool AvifEncoder::writeanimation(const Animation& animation,
((bit_depth == 10 || bit_depth == 12) && img.depth() == CV_16U),
"AVIF only supports bit depth of 8 with CV_8U input or "
"bit depth of 10 or 12 with CV_16U input");
CV_Check(img.channels(),
img.channels() == 1 || img.channels() == 3 || img.channels() == 4,
"AVIF only supports 1, 3, 4 channels");
images.emplace_back(ConvertToAvif(img, do_lossless, bit_depth, m_metadata));
auto avifImg = ConvertToAvif(img, do_lossless, bit_depth, m_metadata);
if (!avifImg) {
CV_Error(Error::StsError, "Failed to convert Mat to AVIF image");
return false;
}
images.emplace_back(std::move(avifImg));
}
for (size_t i = 0; i < images.size(); i++)
-3
View File
@@ -130,9 +130,6 @@ bool ExrDecoder::readHeader()
m_file = new InputFile( m_filename.c_str() );
if( !m_file ) // probably paranoid
return false;
m_datawindow = m_file->header().dataWindow();
m_width = m_datawindow.max.x - m_datawindow.min.x + 1;
m_height = m_datawindow.max.y - m_datawindow.min.y + 1;
+4 -3
View File
@@ -42,6 +42,7 @@
#include "precomp.hpp"
#include <cstdint>
#include <memory>
#ifdef HAVE_PNG
@@ -364,7 +365,7 @@ bool PngDecoder::readHeader()
m_color_type = color_type;
m_bit_depth = bit_depth;
if (m_is_fcTL_loaded && ((long long int)x0 + w0 > m_width || (long long int)y0 + h0 > m_height || dop > 2 || bop > 1))
if (m_is_fcTL_loaded && ((int64_t)x0 + w0 > m_width || (int64_t)y0 + h0 > m_height || dop > 2 || bop > 1))
return false;
png_color_16p background_color;
@@ -456,7 +457,7 @@ bool PngDecoder::readData( Mat& img )
if (dop == 2)
memcpy(frameNext.getPixels(), frameCur.getPixels(), imagesize);
if (x0 + w0 > frameCur.getWidth() || y0 + h0 > frameCur.getHeight())
if ((uint64_t)x0 + w0 > frameCur.getWidth() || (uint64_t)y0 + h0 > frameCur.getHeight())
return false;
compose_frame(frameCur.getRows(), frameRaw.getRows(), bop, x0, y0, w0, h0, mat_cur);
@@ -508,7 +509,7 @@ bool PngDecoder::readData( Mat& img )
dop = chunk.p[32];
bop = chunk.p[33];
if (int(x0 + w0) > img.cols || int(y0 + h0) > img.rows || dop > 2 || bop > 1)
if ((int64_t)x0 + w0 > img.cols || (int64_t)y0 + h0 > img.rows || dop > 2 || bop > 1)
{
return false;
}
+1 -1
View File
@@ -995,7 +995,7 @@ bool TiffDecoder::readData( Mat& img )
break;
default:
CV_LOG_ONCE_ERROR(NULL, "OpenCV TIFF(line " << __LINE__ << "): Unsupported convertion :"
CV_LOG_ONCE_ERROR(NULL, "OpenCV TIFF(line " << __LINE__ << "): Unsupported conversion :"
<< " bpp = " << bpp << " ncn = " << (int)ncn
<< " wanted_channels =" << wanted_channels );
break;
+1 -1
View File
@@ -213,7 +213,7 @@ TEST(Imgcodecs_EXR, read_YA_unchanged)
ASSERT_FALSE(img.empty());
ASSERT_EQ(CV_32FC2, img.type());
// Cannot test writing, 2 channel writing not suppported by loadsave
// Cannot test writing, 2 channel writing not supported by loadsave
}
TEST(Imgcodecs_EXR, read_YC_changeDepth)
+248 -213
View File
@@ -532,149 +532,153 @@ enum HistCompMethods {
/** the color conversion codes
@see @ref imgproc_color_conversions
@note The source image (src) must be of an appropriate type for the desired color conversion.
- `[8U]` means to support `CV_8U` src type.
- `[16U]` means to support `CV_16U` src type.
- `[32F]` means to support `CV_32F` src type.
@ingroup imgproc_color_conversions
*/
enum ColorConversionCodes {
COLOR_BGR2BGRA = 0, //!< add alpha channel to RGB or BGR image
COLOR_RGB2RGBA = COLOR_BGR2BGRA,
COLOR_BGR2BGRA = 0, //!< [8U/16U/32F] add alpha channel to RGB or BGR image
COLOR_RGB2RGBA = COLOR_BGR2BGRA, //!< [8U/16U/32F]
COLOR_BGRA2BGR = 1, //!< remove alpha channel from RGB or BGR image
COLOR_RGBA2RGB = COLOR_BGRA2BGR,
COLOR_BGRA2BGR = 1, //!< [8U/16U/32F] remove alpha channel from RGB or BGR image
COLOR_RGBA2RGB = COLOR_BGRA2BGR, //!< [8U/16U/32F]
COLOR_BGR2RGBA = 2, //!< convert between RGB and BGR color spaces (with or without alpha channel)
COLOR_RGB2BGRA = COLOR_BGR2RGBA,
COLOR_BGR2RGBA = 2, //!< [8U/16U/32F] convert between RGB and BGR color spaces (with or without alpha channel)
COLOR_RGB2BGRA = COLOR_BGR2RGBA, //!< [8U/16U/32F]
COLOR_RGBA2BGR = 3,
COLOR_BGRA2RGB = COLOR_RGBA2BGR,
COLOR_RGBA2BGR = 3, //!< [8U/16U/32F]
COLOR_BGRA2RGB = COLOR_RGBA2BGR, //!< [8U/16U/32F]
COLOR_BGR2RGB = 4,
COLOR_RGB2BGR = COLOR_BGR2RGB,
COLOR_BGR2RGB = 4, //!< [8U/16U/32F]
COLOR_RGB2BGR = COLOR_BGR2RGB, //!< [8U/16U/32F]
COLOR_BGRA2RGBA = 5,
COLOR_RGBA2BGRA = COLOR_BGRA2RGBA,
COLOR_BGRA2RGBA = 5, //!< [8U/16U/32F]
COLOR_RGBA2BGRA = COLOR_BGRA2RGBA, //!< [8U/16U/32F]
COLOR_BGR2GRAY = 6, //!< convert between RGB/BGR and grayscale, @ref color_convert_rgb_gray "color conversions"
COLOR_RGB2GRAY = 7,
COLOR_GRAY2BGR = 8,
COLOR_GRAY2RGB = COLOR_GRAY2BGR,
COLOR_GRAY2BGRA = 9,
COLOR_GRAY2RGBA = COLOR_GRAY2BGRA,
COLOR_BGRA2GRAY = 10,
COLOR_RGBA2GRAY = 11,
COLOR_BGR2GRAY = 6, //!< [8U/16U/32F] convert between RGB/BGR and grayscale, @ref color_convert_rgb_gray "color conversions"
COLOR_RGB2GRAY = 7, //!< [8U/16U/32F]
COLOR_GRAY2BGR = 8, //!< [8U/16U/32F]
COLOR_GRAY2RGB = COLOR_GRAY2BGR, //!< [8U/16U/32F]
COLOR_GRAY2BGRA = 9, //!< [8U/16U/32F]
COLOR_GRAY2RGBA = COLOR_GRAY2BGRA, //!< [8U/16U/32F]
COLOR_BGRA2GRAY = 10, //!< [8U/16U/32F]
COLOR_RGBA2GRAY = 11, //!< [8U/16U/32F]
COLOR_BGR2BGR565 = 12, //!< convert between RGB/BGR and BGR565 (16-bit images)
COLOR_RGB2BGR565 = 13,
COLOR_BGR5652BGR = 14,
COLOR_BGR5652RGB = 15,
COLOR_BGRA2BGR565 = 16,
COLOR_RGBA2BGR565 = 17,
COLOR_BGR5652BGRA = 18,
COLOR_BGR5652RGBA = 19,
COLOR_BGR2BGR565 = 12, //!< [8U] convert between RGB/BGR and BGR565 (16-bit images)
COLOR_RGB2BGR565 = 13, //!< [8U]
COLOR_BGR5652BGR = 14, //!< [8U]
COLOR_BGR5652RGB = 15, //!< [8U]
COLOR_BGRA2BGR565 = 16, //!< [8U]
COLOR_RGBA2BGR565 = 17, //!< [8U]
COLOR_BGR5652BGRA = 18, //!< [8U]
COLOR_BGR5652RGBA = 19, //!< [8U]
COLOR_GRAY2BGR565 = 20, //!< convert between grayscale to BGR565 (16-bit images)
COLOR_BGR5652GRAY = 21,
COLOR_GRAY2BGR565 = 20, //!< [8U] convert between grayscale to BGR565 (16-bit images)
COLOR_BGR5652GRAY = 21, //!< [8U]
COLOR_BGR2BGR555 = 22, //!< convert between RGB/BGR and BGR555 (16-bit images)
COLOR_RGB2BGR555 = 23,
COLOR_BGR5552BGR = 24,
COLOR_BGR5552RGB = 25,
COLOR_BGRA2BGR555 = 26,
COLOR_RGBA2BGR555 = 27,
COLOR_BGR5552BGRA = 28,
COLOR_BGR5552RGBA = 29,
COLOR_BGR2BGR555 = 22, //!< [8U] convert between RGB/BGR and BGR555 (16-bit images)
COLOR_RGB2BGR555 = 23, //!< [8U]
COLOR_BGR5552BGR = 24, //!< [8U]
COLOR_BGR5552RGB = 25, //!< [8U]
COLOR_BGRA2BGR555 = 26, //!< [8U]
COLOR_RGBA2BGR555 = 27, //!< [8U]
COLOR_BGR5552BGRA = 28, //!< [8U]
COLOR_BGR5552RGBA = 29, //!< [8U]
COLOR_GRAY2BGR555 = 30, //!< convert between grayscale and BGR555 (16-bit images)
COLOR_BGR5552GRAY = 31,
COLOR_GRAY2BGR555 = 30, //!< [8U] convert between grayscale and BGR555 (16-bit images)
COLOR_BGR5552GRAY = 31, //!< [8U]
COLOR_BGR2XYZ = 32, //!< convert RGB/BGR to CIE XYZ, @ref color_convert_rgb_xyz "color conversions"
COLOR_RGB2XYZ = 33,
COLOR_XYZ2BGR = 34,
COLOR_XYZ2RGB = 35,
COLOR_BGR2XYZ = 32, //!< [8U/16U/32F] convert RGB/BGR to CIE XYZ, @ref color_convert_rgb_xyz "color conversions"
COLOR_RGB2XYZ = 33, //!< [8U/16U/32F]
COLOR_XYZ2BGR = 34, //!< [8U/16U/32F]
COLOR_XYZ2RGB = 35, //!< [8U/16U/32F]
COLOR_BGR2YCrCb = 36, //!< convert RGB/BGR to luma-chroma (aka YCC), @ref color_convert_rgb_ycrcb "color conversions"
COLOR_RGB2YCrCb = 37,
COLOR_YCrCb2BGR = 38,
COLOR_YCrCb2RGB = 39,
COLOR_BGR2YCrCb = 36, //!< [8U/16U/32F] convert RGB/BGR to luma-chroma (aka YCC), @ref color_convert_rgb_ycrcb "color conversions"
COLOR_RGB2YCrCb = 37, //!< [8U/16U/32F]
COLOR_YCrCb2BGR = 38, //!< [8U/16U/32F]
COLOR_YCrCb2RGB = 39, //!< [8U/16U/32F]
COLOR_BGR2HSV = 40, //!< convert RGB/BGR to HSV (hue saturation value) with H range 0..180 if 8 bit image, @ref color_convert_rgb_hsv "color conversions"
COLOR_RGB2HSV = 41,
COLOR_BGR2HSV = 40, //!< [8U/32F] convert RGB/BGR to HSV (hue saturation value) with H range 0..180 if 8 bit image, @ref color_convert_rgb_hsv "color conversions"
COLOR_RGB2HSV = 41, //!< [8U/32F]
COLOR_BGR2Lab = 44, //!< convert RGB/BGR to CIE Lab, @ref color_convert_rgb_lab "color conversions"
COLOR_RGB2Lab = 45,
COLOR_BGR2Lab = 44, //!< [8U/32F] convert RGB/BGR to CIE Lab, @ref color_convert_rgb_lab "color conversions"
COLOR_RGB2Lab = 45, //!< [8U/32F]
COLOR_BGR2Luv = 50, //!< convert RGB/BGR to CIE Luv, @ref color_convert_rgb_luv "color conversions"
COLOR_RGB2Luv = 51,
COLOR_BGR2HLS = 52, //!< convert RGB/BGR to HLS (hue lightness saturation) with H range 0..180 if 8 bit image, @ref color_convert_rgb_hls "color conversions"
COLOR_RGB2HLS = 53,
COLOR_BGR2Luv = 50, //!< [8U/32F] convert RGB/BGR to CIE Luv, @ref color_convert_rgb_luv "color conversions"
COLOR_RGB2Luv = 51, //!< [8U/32F]
COLOR_BGR2HLS = 52, //!< [8U/32F] convert RGB/BGR to HLS (hue lightness saturation) with H range 0..180 if 8 bit image, @ref color_convert_rgb_hls "color conversions"
COLOR_RGB2HLS = 53, //!< [8U/32F]
COLOR_HSV2BGR = 54, //!< backward conversions HSV to RGB/BGR with H range 0..180 if 8 bit image
COLOR_HSV2RGB = 55,
COLOR_HSV2BGR = 54, //!< [8U/32F] backward conversions HSV to RGB/BGR with H range 0..180 if 8 bit image
COLOR_HSV2RGB = 55, //!< [8U/32F]
COLOR_Lab2BGR = 56,
COLOR_Lab2RGB = 57,
COLOR_Luv2BGR = 58,
COLOR_Luv2RGB = 59,
COLOR_HLS2BGR = 60, //!< backward conversions HLS to RGB/BGR with H range 0..180 if 8 bit image
COLOR_HLS2RGB = 61,
COLOR_Lab2BGR = 56, //!< [8U/32F]
COLOR_Lab2RGB = 57, //!< [8U/32F]
COLOR_Luv2BGR = 58, //!< [8U/32F]
COLOR_Luv2RGB = 59, //!< [8U/32F]
COLOR_HLS2BGR = 60, //!< [8U/32F] backward conversions HLS to RGB/BGR with H range 0..180 if 8 bit image
COLOR_HLS2RGB = 61, //!< [8U/32F]
COLOR_BGR2HSV_FULL = 66, //!< convert RGB/BGR to HSV (hue saturation value) with H range 0..255 if 8 bit image, @ref color_convert_rgb_hsv "color conversions"
COLOR_RGB2HSV_FULL = 67,
COLOR_BGR2HLS_FULL = 68, //!< convert RGB/BGR to HLS (hue lightness saturation) with H range 0..255 if 8 bit image, @ref color_convert_rgb_hls "color conversions"
COLOR_RGB2HLS_FULL = 69,
COLOR_BGR2HSV_FULL = 66, //!< [8U/32F] convert RGB/BGR to HSV (hue saturation value) with H range 0..255 if 8 bit image, @ref color_convert_rgb_hsv "color conversions"
COLOR_RGB2HSV_FULL = 67, //!< [8U/32F]
COLOR_BGR2HLS_FULL = 68, //!< [8U/32F] convert RGB/BGR to HLS (hue lightness saturation) with H range 0..255 if 8 bit image, @ref color_convert_rgb_hls "color conversions"
COLOR_RGB2HLS_FULL = 69, //!< [8U/32F]
COLOR_HSV2BGR_FULL = 70, //!< backward conversions HSV to RGB/BGR with H range 0..255 if 8 bit image
COLOR_HSV2RGB_FULL = 71,
COLOR_HLS2BGR_FULL = 72, //!< backward conversions HLS to RGB/BGR with H range 0..255 if 8 bit image
COLOR_HLS2RGB_FULL = 73,
COLOR_HSV2BGR_FULL = 70, //!< [8U/32F] backward conversions HSV to RGB/BGR with H range 0..255 if 8 bit image
COLOR_HSV2RGB_FULL = 71, //!< [8U/32F]
COLOR_HLS2BGR_FULL = 72, //!< [8U/32F] backward conversions HLS to RGB/BGR with H range 0..255 if 8 bit image
COLOR_HLS2RGB_FULL = 73, //!< [8U/32F]
COLOR_LBGR2Lab = 74,
COLOR_LRGB2Lab = 75,
COLOR_LBGR2Luv = 76,
COLOR_LRGB2Luv = 77,
COLOR_LBGR2Lab = 74, //!< [8U/32F]
COLOR_LRGB2Lab = 75, //!< [8U/32F]
COLOR_LBGR2Luv = 76, //!< [8U/32F]
COLOR_LRGB2Luv = 77, //!< [8U/32F]
COLOR_Lab2LBGR = 78,
COLOR_Lab2LRGB = 79,
COLOR_Luv2LBGR = 80,
COLOR_Luv2LRGB = 81,
COLOR_Lab2LBGR = 78, //!< [8U/32F]
COLOR_Lab2LRGB = 79, //!< [8U/32F]
COLOR_Luv2LBGR = 80, //!< [8U/32F]
COLOR_Luv2LRGB = 81, //!< [8U/32F]
COLOR_BGR2YUV = 82, //!< convert between RGB/BGR and YUV
COLOR_RGB2YUV = 83,
COLOR_YUV2BGR = 84,
COLOR_YUV2RGB = 85,
COLOR_BGR2YUV = 82, //!< [8U/16U/32F] convert between RGB/BGR and YUV
COLOR_RGB2YUV = 83, //!< [8U/16U/32F]
COLOR_YUV2BGR = 84, //!< [8U/16U/32F]
COLOR_YUV2RGB = 85, //!< [8U/16U/32F]
COLOR_YUV2RGB_NV12 = 90, //!< convert between 4:2:0-subsampled YUV NV12 and RGB, two planes (in one or separate arrays): Y and U/V interleaved, see @ref color_convert_rgb_yuv_42x
COLOR_YUV2BGR_NV12 = 91, //!< convert between 4:2:0-subsampled YUV NV12 and BGR, two planes (in one or separate arrays): Y and U/V interleaved, see @ref color_convert_rgb_yuv_42x
COLOR_YUV2RGB_NV21 = 92, //!< convert between 4:2:0-subsampled YUV NV21 and RGB, two planes (in one or separate arrays): Y and V/U interleaved, see @ref color_convert_rgb_yuv_42x
COLOR_YUV2BGR_NV21 = 93, //!< convert between 4:2:0-subsampled YUV NV21 and BGR, two planes (in one or separate arrays): Y and V/U interleaved, see @ref color_convert_rgb_yuv_42x
COLOR_YUV2RGB_NV12 = 90, //!< [8U] convert between 4:2:0-subsampled YUV NV12 and RGB, two planes (in one or separate arrays): Y and U/V interleaved, see @ref color_convert_rgb_yuv_42x
COLOR_YUV2BGR_NV12 = 91, //!< [8U] convert between 4:2:0-subsampled YUV NV12 and BGR, two planes (in one or separate arrays): Y and U/V interleaved, see @ref color_convert_rgb_yuv_42x
COLOR_YUV2RGB_NV21 = 92, //!< [8U] convert between 4:2:0-subsampled YUV NV21 and RGB, two planes (in one or separate arrays): Y and V/U interleaved, see @ref color_convert_rgb_yuv_42x
COLOR_YUV2BGR_NV21 = 93, //!< [8U] convert between 4:2:0-subsampled YUV NV21 and BGR, two planes (in one or separate arrays): Y and V/U interleaved, see @ref color_convert_rgb_yuv_42x
COLOR_YUV420sp2RGB = COLOR_YUV2RGB_NV21, //!< synonym to NV21
COLOR_YUV420sp2BGR = COLOR_YUV2BGR_NV21, //!< synonym to NV21
COLOR_YUV2RGBA_NV12 = 94, //!< convert between 4:2:0-subsampled YUV NV12 and RGBA, two planes (in one or separate arrays): Y and U/V interleaved, see @ref color_convert_rgb_yuv_42x
COLOR_YUV2BGRA_NV12 = 95, //!< convert between 4:2:0-subsampled YUV NV12 and BGRA, two planes (in one or separate arrays): Y and U/V interleaved, see @ref color_convert_rgb_yuv_42x
COLOR_YUV2RGBA_NV21 = 96, //!< convert between 4:2:0-subsampled YUV NV21 and RGBA, two planes (in one or separate arrays): Y and V/U interleaved, see @ref color_convert_rgb_yuv_42x
COLOR_YUV2BGRA_NV21 = 97, //!< convert between 4:2:0-subsampled YUV NV21 and BGRA, two planes (in one or separate arrays): Y and V/U interleaved, see @ref color_convert_rgb_yuv_42x
COLOR_YUV2RGBA_NV12 = 94, //!< [8U] convert between 4:2:0-subsampled YUV NV12 and RGBA, two planes (in one or separate arrays): Y and U/V interleaved, see @ref color_convert_rgb_yuv_42x
COLOR_YUV2BGRA_NV12 = 95, //!< [8U] convert between 4:2:0-subsampled YUV NV12 and BGRA, two planes (in one or separate arrays): Y and U/V interleaved, see @ref color_convert_rgb_yuv_42x
COLOR_YUV2RGBA_NV21 = 96, //!< [8U] convert between 4:2:0-subsampled YUV NV21 and RGBA, two planes (in one or separate arrays): Y and V/U interleaved, see @ref color_convert_rgb_yuv_42x
COLOR_YUV2BGRA_NV21 = 97, //!< [8U] convert between 4:2:0-subsampled YUV NV21 and BGRA, two planes (in one or separate arrays): Y and V/U interleaved, see @ref color_convert_rgb_yuv_42x
COLOR_YUV420sp2RGBA = COLOR_YUV2RGBA_NV21, //!< synonym to NV21
COLOR_YUV420sp2BGRA = COLOR_YUV2BGRA_NV21, //!< synonym to NV21
COLOR_YUV2RGB_YV12 = 98, //!< convert between 4:2:0-subsampled YUV YV12 and RGB, three planes in one array: Y, V and U, see @ref color_convert_rgb_yuv_42x
COLOR_YUV2BGR_YV12 = 99, //!< convert between 4:2:0-subsampled YUV YV12 and BGR, three planes in one array: Y, V and U, see @ref color_convert_rgb_yuv_42x
COLOR_YUV2RGB_IYUV = 100, //!< convert between 4:2:0-subsampled YUV IYUV and RGB, three planes in one array: Y, U and V, see @ref color_convert_rgb_yuv_42x
COLOR_YUV2BGR_IYUV = 101, //!< convert between 4:2:0-subsampled YUV IYUV and BGR, three planes in one array: Y, U and V, see @ref color_convert_rgb_yuv_42x
COLOR_YUV2RGB_YV12 = 98, //!< [8U] convert between 4:2:0-subsampled YUV YV12 and RGB, three planes in one array: Y, V and U, see @ref color_convert_rgb_yuv_42x
COLOR_YUV2BGR_YV12 = 99, //!< [8U] convert between 4:2:0-subsampled YUV YV12 and BGR, three planes in one array: Y, V and U, see @ref color_convert_rgb_yuv_42x
COLOR_YUV2RGB_IYUV = 100, //!< [8U] convert between 4:2:0-subsampled YUV IYUV and RGB, three planes in one array: Y, U and V, see @ref color_convert_rgb_yuv_42x
COLOR_YUV2BGR_IYUV = 101, //!< [8U] convert between 4:2:0-subsampled YUV IYUV and BGR, three planes in one array: Y, U and V, see @ref color_convert_rgb_yuv_42x
COLOR_YUV2RGB_I420 = COLOR_YUV2RGB_IYUV, //!< synonym to IYUV
COLOR_YUV2BGR_I420 = COLOR_YUV2BGR_IYUV, //!< synonym to IYUV
COLOR_YUV420p2RGB = COLOR_YUV2RGB_YV12, //!< synonym to YV12
COLOR_YUV420p2BGR = COLOR_YUV2BGR_YV12, //!< synonym to YV12
COLOR_YUV2RGBA_YV12 = 102, //!< convert between 4:2:0-subsampled YUV YV12 and RGBA, three planes in one array: Y, V and U, see @ref color_convert_rgb_yuv_42x
COLOR_YUV2BGRA_YV12 = 103, //!< convert between 4:2:0-subsampled YUV YV12 and BGRA, three planes in one array: Y, V and U, see @ref color_convert_rgb_yuv_42x
COLOR_YUV2RGBA_IYUV = 104, //!< convert between 4:2:0-subsampled YUV YV12 and RGBA, three planes in one array: Y, U and V, see @ref color_convert_rgb_yuv_42x
COLOR_YUV2BGRA_IYUV = 105, //!< convert between 4:2:0-subsampled YUV YV12 and BGRA, three planes in one array: Y, U and V, see @ref color_convert_rgb_yuv_42x
COLOR_YUV2RGBA_YV12 = 102, //!< [8U] convert between 4:2:0-subsampled YUV YV12 and RGBA, three planes in one array: Y, V and U, see @ref color_convert_rgb_yuv_42x
COLOR_YUV2BGRA_YV12 = 103, //!< [8U] convert between 4:2:0-subsampled YUV YV12 and BGRA, three planes in one array: Y, V and U, see @ref color_convert_rgb_yuv_42x
COLOR_YUV2RGBA_IYUV = 104, //!< [8U] convert between 4:2:0-subsampled YUV YV12 and RGBA, three planes in one array: Y, U and V, see @ref color_convert_rgb_yuv_42x
COLOR_YUV2BGRA_IYUV = 105, //!< [8U] convert between 4:2:0-subsampled YUV YV12 and BGRA, three planes in one array: Y, U and V, see @ref color_convert_rgb_yuv_42x
COLOR_YUV2RGBA_I420 = COLOR_YUV2RGBA_IYUV, //!< synonym to IYUV
COLOR_YUV2BGRA_I420 = COLOR_YUV2BGRA_IYUV, //!< synonym to IYUV
COLOR_YUV420p2RGBA = COLOR_YUV2RGBA_YV12, //!< synonym to YV12
COLOR_YUV420p2BGRA = COLOR_YUV2BGRA_YV12, //!< synonym to YV12
COLOR_YUV2GRAY_420 = 106, //!< extract Y channel from YUV 4:2:0 image
COLOR_YUV2GRAY_420 = 106, //!< [8U] extract Y channel from YUV 4:2:0 image
COLOR_YUV2GRAY_NV21 = COLOR_YUV2GRAY_420, //!< synonym to COLOR_YUV2GRAY_420
COLOR_YUV2GRAY_NV12 = COLOR_YUV2GRAY_420, //!< synonym to COLOR_YUV2GRAY_420
COLOR_YUV2GRAY_YV12 = COLOR_YUV2GRAY_420, //!< synonym to COLOR_YUV2GRAY_420
@@ -683,8 +687,8 @@ enum ColorConversionCodes {
COLOR_YUV420sp2GRAY = COLOR_YUV2GRAY_420, //!< synonym to COLOR_YUV2GRAY_420
COLOR_YUV420p2GRAY = COLOR_YUV2GRAY_420, //!< synonym to COLOR_YUV2GRAY_420
COLOR_YUV2RGB_UYVY = 107, //!< convert between YUV UYVY and RGB, YUV is 4:2:2-subsampled and interleaved as U/Y1/V/Y2, see @ref color_convert_rgb_yuv_42x
COLOR_YUV2BGR_UYVY = 108, //!< convert between YUV UYVY and BGR, YUV is 4:2:2-subsampled and interleaved as U/Y1/V/Y2, see @ref color_convert_rgb_yuv_42x
COLOR_YUV2RGB_UYVY = 107, //!< [8U] convert between YUV UYVY and RGB, YUV is 4:2:2-subsampled and interleaved as U/Y1/V/Y2, see @ref color_convert_rgb_yuv_42x
COLOR_YUV2BGR_UYVY = 108, //!< [8U] convert between YUV UYVY and BGR, YUV is 4:2:2-subsampled and interleaved as U/Y1/V/Y2, see @ref color_convert_rgb_yuv_42x
//COLOR_YUV2RGB_VYUY = 109, //!< convert between YUV VYUY and RGB, YUV is 4:2:2-subsampled and interleaved as V/Y1/U/Y2, see @ref color_convert_rgb_yuv_42x
//COLOR_YUV2BGR_VYUY = 110, //!< convert between YUV VYUY and BGR, YUV is 4:2:2-subsampled and interleaved as V/Y1/U/Y2, see @ref color_convert_rgb_yuv_42x
COLOR_YUV2RGB_Y422 = COLOR_YUV2RGB_UYVY, //!< synonym to UYVY
@@ -692,180 +696,181 @@ enum ColorConversionCodes {
COLOR_YUV2RGB_UYNV = COLOR_YUV2RGB_UYVY, //!< synonym to UYVY
COLOR_YUV2BGR_UYNV = COLOR_YUV2BGR_UYVY, //!< synonym to UYVY
COLOR_YUV2RGBA_UYVY = 111, //!< convert between YUV UYVY and RGBA, YUV is 4:2:2-subsampled and interleaved as U/Y1/V/Y2, see @ref color_convert_rgb_yuv_42x
COLOR_YUV2BGRA_UYVY = 112, //!< convert between YUV UYVY and BGRA, YUV is 4:2:2-subsampled and interleaved as U/Y1/V/Y2, see @ref color_convert_rgb_yuv_42x
//COLOR_YUV2RGBA_VYUY = 113, //!< convert between YUV VYUY and RGBA, YUV is 4:2:2-subsampled and interleaved as V/Y1/U/Y2, see @ref color_convert_rgb_yuv_42x
//COLOR_YUV2BGRA_VYUY = 114, //!< convert between YUV VYUY and BGRA, YUV is 4:2:2-subsampled and interleaved as V/Y1/U/Y2, see @ref color_convert_rgb_yuv_42x
COLOR_YUV2RGBA_UYVY = 111, //!< [8U] convert between YUV UYVY and RGBA, YUV is 4:2:2-subsampled and interleaved as U/Y1/V/Y2, see @ref color_convert_rgb_yuv_42x
COLOR_YUV2BGRA_UYVY = 112, //!< [8U] convert between YUV UYVY and BGRA, YUV is 4:2:2-subsampled and interleaved as U/Y1/V/Y2, see @ref color_convert_rgb_yuv_42x
//COLOR_YUV2RGBA_VYUY = 113, //!< [8U] convert between YUV VYUY and RGBA, YUV is 4:2:2-subsampled and interleaved as V/Y1/U/Y2, see @ref color_convert_rgb_yuv_42x
//COLOR_YUV2BGRA_VYUY = 114, //!< [8U] convert between YUV VYUY and BGRA, YUV is 4:2:2-subsampled and interleaved as V/Y1/U/Y2, see @ref color_convert_rgb_yuv_42x
COLOR_YUV2RGBA_Y422 = COLOR_YUV2RGBA_UYVY, //!< synonym to UYVY
COLOR_YUV2BGRA_Y422 = COLOR_YUV2BGRA_UYVY, //!< synonym to UYVY
COLOR_YUV2RGBA_UYNV = COLOR_YUV2RGBA_UYVY, //!< synonym to UYVY
COLOR_YUV2BGRA_UYNV = COLOR_YUV2BGRA_UYVY, //!< synonym to UYVY
COLOR_YUV2RGB_YUY2 = 115, //!< convert between YUV YUY2 and RGB, YUV is 4:2:2-subsampled and interleaved as Y1/U/Y2/V, see @ref color_convert_rgb_yuv_42x
COLOR_YUV2BGR_YUY2 = 116, //!< convert between YUV YUY2 and BGR, YUV is 4:2:2-subsampled and interleaved as Y1/U/Y2/V, see @ref color_convert_rgb_yuv_42x
COLOR_YUV2RGB_YVYU = 117, //!< convert between YUV YVYU and RGB, YUV is 4:2:2-subsampled and interleaved as Y1/V/Y2/U, see @ref color_convert_rgb_yuv_42x
COLOR_YUV2BGR_YVYU = 118, //!< convert between YUV YVYU and BGR, YUV is 4:2:2-subsampled and interleaved as Y1/V/Y2/U, see @ref color_convert_rgb_yuv_42x
COLOR_YUV2RGB_YUY2 = 115, //!< [8U] convert between YUV YUY2 and RGB, YUV is 4:2:2-subsampled and interleaved as Y1/U/Y2/V, see @ref color_convert_rgb_yuv_42x
COLOR_YUV2BGR_YUY2 = 116, //!< [8U] convert between YUV YUY2 and BGR, YUV is 4:2:2-subsampled and interleaved as Y1/U/Y2/V, see @ref color_convert_rgb_yuv_42x
COLOR_YUV2RGB_YVYU = 117, //!< [8U] convert between YUV YVYU and RGB, YUV is 4:2:2-subsampled and interleaved as Y1/V/Y2/U, see @ref color_convert_rgb_yuv_42x
COLOR_YUV2BGR_YVYU = 118, //!< [8U] convert between YUV YVYU and BGR, YUV is 4:2:2-subsampled and interleaved as Y1/V/Y2/U, see @ref color_convert_rgb_yuv_42x
COLOR_YUV2RGB_YUYV = COLOR_YUV2RGB_YUY2, //!< synonym to YUY2
COLOR_YUV2BGR_YUYV = COLOR_YUV2BGR_YUY2, //!< synonym to YUY2
COLOR_YUV2RGB_YUNV = COLOR_YUV2RGB_YUY2, //!< synonym to YUY2
COLOR_YUV2BGR_YUNV = COLOR_YUV2BGR_YUY2, //!< synonym to YUY2
COLOR_YUV2RGBA_YUY2 = 119, //!< convert between YUV YUY2 and RGBA, YUV is 4:2:2-subsampled and interleaved as Y1/U/Y2/V, see @ref color_convert_rgb_yuv_42x
COLOR_YUV2BGRA_YUY2 = 120, //!< convert between YUV YUY2 and BGRA, YUV is 4:2:2-subsampled and interleaved as Y1/U/Y2/V, see @ref color_convert_rgb_yuv_42x
COLOR_YUV2RGBA_YVYU = 121, //!< convert between YUV YVYU and RGBA, YUV is 4:2:2-subsampled and interleaved as Y1/V/Y2/U, see @ref color_convert_rgb_yuv_42x
COLOR_YUV2BGRA_YVYU = 122, //!< convert between YUV YVYU and BGRA, YUV is 4:2:2-subsampled and interleaved as Y1/V/Y2/U, see @ref color_convert_rgb_yuv_42x
COLOR_YUV2RGBA_YUY2 = 119, //!< [8U] convert between YUV YUY2 and RGBA, YUV is 4:2:2-subsampled and interleaved as Y1/U/Y2/V, see @ref color_convert_rgb_yuv_42x
COLOR_YUV2BGRA_YUY2 = 120, //!< [8U] convert between YUV YUY2 and BGRA, YUV is 4:2:2-subsampled and interleaved as Y1/U/Y2/V, see @ref color_convert_rgb_yuv_42x
COLOR_YUV2RGBA_YVYU = 121, //!< [8U] convert between YUV YVYU and RGBA, YUV is 4:2:2-subsampled and interleaved as Y1/V/Y2/U, see @ref color_convert_rgb_yuv_42x
COLOR_YUV2BGRA_YVYU = 122, //!< [8U] convert between YUV YVYU and BGRA, YUV is 4:2:2-subsampled and interleaved as Y1/V/Y2/U, see @ref color_convert_rgb_yuv_42x
COLOR_YUV2RGBA_YUYV = COLOR_YUV2RGBA_YUY2, //!< synonym to YUY2
COLOR_YUV2BGRA_YUYV = COLOR_YUV2BGRA_YUY2, //!< synonym to YUY2
COLOR_YUV2RGBA_YUNV = COLOR_YUV2RGBA_YUY2, //!< synonym to YUY2
COLOR_YUV2BGRA_YUNV = COLOR_YUV2BGRA_YUY2, //!< synonym to YUY2
COLOR_YUV2GRAY_UYVY = 123, //!< extract Y channel from YUV 4:2:2 image
COLOR_YUV2GRAY_YUY2 = 124, //!< extract Y channel from YUV 4:2:2 image
//COLOR_YUV2GRAY_VYUY = COLOR_YUV2GRAY_UYVY,
COLOR_YUV2GRAY_UYVY = 123, //!< [8U] extract Y channel from YUV 4:2:2 image
COLOR_YUV2GRAY_YUY2 = 124, //!< [8U] extract Y channel from YUV 4:2:2 image
//CV_YUV2GRAY_VYUY = CV_YUV2GRAY_UYVY, //!< synonym to COLOR_YUV2GRAY_UYVY
COLOR_YUV2GRAY_Y422 = COLOR_YUV2GRAY_UYVY, //!< synonym to COLOR_YUV2GRAY_UYVY
COLOR_YUV2GRAY_UYNV = COLOR_YUV2GRAY_UYVY, //!< synonym to COLOR_YUV2GRAY_UYVY
COLOR_YUV2GRAY_YVYU = COLOR_YUV2GRAY_YUY2, //!< synonym to COLOR_YUV2GRAY_YUY2
COLOR_YUV2GRAY_YUYV = COLOR_YUV2GRAY_YUY2, //!< synonym to COLOR_YUV2GRAY_YUY2
COLOR_YUV2GRAY_YUNV = COLOR_YUV2GRAY_YUY2, //!< synonym to COLOR_YUV2GRAY_YUY2
//! alpha premultiplication
COLOR_RGBA2mRGBA = 125,
COLOR_mRGBA2RGBA = 126,
COLOR_RGBA2mRGBA = 125, //!< [8U]
COLOR_mRGBA2RGBA = 126, //!< [8U]
COLOR_RGB2YUV_I420 = 127, //!< convert between RGB and 4:2:0-subsampled YUV I420, three planes in one array: Y, U and V, see @ref color_convert_rgb_yuv_42x
COLOR_BGR2YUV_I420 = 128, //!< convert between BGR and 4:2:0-subsampled YUV I420, three planes in one array: Y, U and V, see @ref color_convert_rgb_yuv_42x
COLOR_RGB2YUV_I420 = 127, //!< [8U] convert between RGB and 4:2:0-subsampled YUV I420, three planes in one array: Y, U and V, see @ref color_convert_rgb_yuv_42x
COLOR_BGR2YUV_I420 = 128, //!< [8U] convert between BGR and 4:2:0-subsampled YUV I420, three planes in one array: Y, U and V, see @ref color_convert_rgb_yuv_42x
COLOR_RGB2YUV_IYUV = COLOR_RGB2YUV_I420, //!< synonym to I420
COLOR_BGR2YUV_IYUV = COLOR_BGR2YUV_I420, //!< synonym to I420
COLOR_RGBA2YUV_I420 = 129, //!< convert between RGBA and 4:2:0-subsampled YUV I420, three planes in one array: Y, U and V, see @ref color_convert_rgb_yuv_42x
COLOR_BGRA2YUV_I420 = 130, //!< convert between BGRA and 4:2:0-subsampled YUV I420, three planes in one array: Y, U and V, see @ref color_convert_rgb_yuv_42x
COLOR_RGBA2YUV_I420 = 129, //!< [8U] convert between RGBA and 4:2:0-subsampled YUV I420, three planes in one array: Y, U and V, see @ref color_convert_rgb_yuv_42x
COLOR_BGRA2YUV_I420 = 130, //!< [8U] convert between BGRA and 4:2:0-subsampled YUV I420, three planes in one array: Y, U and V, see @ref color_convert_rgb_yuv_42x
COLOR_RGBA2YUV_IYUV = COLOR_RGBA2YUV_I420, //!< synonym to I420
COLOR_BGRA2YUV_IYUV = COLOR_BGRA2YUV_I420, //!< synonym to I420
COLOR_RGB2YUV_YV12 = 131, //!< convert between RGB and 4:2:0-subsampled YUV YV12, three planes in one array: Y, V and U, see @ref color_convert_rgb_yuv_42x
COLOR_BGR2YUV_YV12 = 132, //!< convert between BGR and 4:2:0-subsampled YUV YV12, three planes in one array: Y, V and U, see @ref color_convert_rgb_yuv_42x
COLOR_RGBA2YUV_YV12 = 133, //!< convert between RGBA and 4:2:0-subsampled YUV YV12, three planes in one array: Y, V and U, see @ref color_convert_rgb_yuv_42x
COLOR_BGRA2YUV_YV12 = 134, //!< convert between BGRA and 4:2:0-subsampled YUV YV12, three planes in one array: Y, V and U, see @ref color_convert_rgb_yuv_42x
COLOR_RGB2YUV_YV12 = 131, //!< [8U] convert between RGB and 4:2:0-subsampled YUV YV12, three planes in one array: Y, V and U, see @ref color_convert_rgb_yuv_42x
COLOR_BGR2YUV_YV12 = 132, //!< [8U] convert between BGR and 4:2:0-subsampled YUV YV12, three planes in one array: Y, V and U, see @ref color_convert_rgb_yuv_42x
COLOR_RGBA2YUV_YV12 = 133, //!< [8U] convert between RGBA and 4:2:0-subsampled YUV YV12, three planes in one array: Y, V and U, see @ref color_convert_rgb_yuv_42x
COLOR_BGRA2YUV_YV12 = 134, //!< [8U] convert between BGRA and 4:2:0-subsampled YUV YV12, three planes in one array: Y, V and U, see @ref color_convert_rgb_yuv_42x
//! Demosaicing, see @ref color_convert_bayer "color conversions" for additional information
COLOR_BayerBG2BGR = 46, //!< equivalent to RGGB Bayer pattern
COLOR_BayerGB2BGR = 47, //!< equivalent to GRBG Bayer pattern
COLOR_BayerRG2BGR = 48, //!< equivalent to BGGR Bayer pattern
COLOR_BayerGR2BGR = 49, //!< equivalent to GBRG Bayer pattern
COLOR_BayerBG2BGR = 46, //!< [8U/16U] equivalent to RGGB Bayer pattern
COLOR_BayerGB2BGR = 47, //!< [8U/16U] equivalent to GRBG Bayer pattern
COLOR_BayerRG2BGR = 48, //!< [8U/16U] equivalent to BGGR Bayer pattern
COLOR_BayerGR2BGR = 49, //!< [8U/16U] equivalent to GBRG Bayer pattern
COLOR_BayerRGGB2BGR = COLOR_BayerBG2BGR,
COLOR_BayerGRBG2BGR = COLOR_BayerGB2BGR,
COLOR_BayerBGGR2BGR = COLOR_BayerRG2BGR,
COLOR_BayerGBRG2BGR = COLOR_BayerGR2BGR,
COLOR_BayerRGGB2BGR = COLOR_BayerBG2BGR, //!< [8U/16U]
COLOR_BayerGRBG2BGR = COLOR_BayerGB2BGR, //!< [8U/16U]
COLOR_BayerBGGR2BGR = COLOR_BayerRG2BGR, //!< [8U/16U]
COLOR_BayerGBRG2BGR = COLOR_BayerGR2BGR, //!< [8U/16U]
COLOR_BayerRGGB2RGB = COLOR_BayerBGGR2BGR,
COLOR_BayerGRBG2RGB = COLOR_BayerGBRG2BGR,
COLOR_BayerBGGR2RGB = COLOR_BayerRGGB2BGR,
COLOR_BayerGBRG2RGB = COLOR_BayerGRBG2BGR,
COLOR_BayerRGGB2RGB = COLOR_BayerBGGR2BGR, //!< [8U/16U]
COLOR_BayerGRBG2RGB = COLOR_BayerGBRG2BGR, //!< [8U/16U]
COLOR_BayerBGGR2RGB = COLOR_BayerRGGB2BGR, //!< [8U/16U]
COLOR_BayerGBRG2RGB = COLOR_BayerGRBG2BGR, //!< [8U/16U]
COLOR_BayerBG2RGB = COLOR_BayerRG2BGR, //!< equivalent to RGGB Bayer pattern
COLOR_BayerGB2RGB = COLOR_BayerGR2BGR, //!< equivalent to GRBG Bayer pattern
COLOR_BayerRG2RGB = COLOR_BayerBG2BGR, //!< equivalent to BGGR Bayer pattern
COLOR_BayerGR2RGB = COLOR_BayerGB2BGR, //!< equivalent to GBRG Bayer pattern
COLOR_BayerBG2RGB = COLOR_BayerRG2BGR, //!< [8U/16U] equivalent to RGGB Bayer pattern
COLOR_BayerGB2RGB = COLOR_BayerGR2BGR, //!< [8U/16U] equivalent to GRBG Bayer pattern
COLOR_BayerRG2RGB = COLOR_BayerBG2BGR, //!< [8U/16U] equivalent to BGGR Bayer pattern
COLOR_BayerGR2RGB = COLOR_BayerGB2BGR, //!< [8U/16U] equivalent to GBRG Bayer pattern
COLOR_BayerBG2GRAY = 86, //!< equivalent to RGGB Bayer pattern
COLOR_BayerGB2GRAY = 87, //!< equivalent to GRBG Bayer pattern
COLOR_BayerRG2GRAY = 88, //!< equivalent to BGGR Bayer pattern
COLOR_BayerGR2GRAY = 89, //!< equivalent to GBRG Bayer pattern
COLOR_BayerBG2GRAY = 86, //!< [8U/16U] equivalent to RGGB Bayer pattern
COLOR_BayerGB2GRAY = 87, //!< [8U/16U] equivalent to GRBG Bayer pattern
COLOR_BayerRG2GRAY = 88, //!< [8U/16U] equivalent to BGGR Bayer pattern
COLOR_BayerGR2GRAY = 89, //!< [8U/16U] equivalent to GBRG Bayer pattern
COLOR_BayerRGGB2GRAY = COLOR_BayerBG2GRAY,
COLOR_BayerGRBG2GRAY = COLOR_BayerGB2GRAY,
COLOR_BayerBGGR2GRAY = COLOR_BayerRG2GRAY,
COLOR_BayerGBRG2GRAY = COLOR_BayerGR2GRAY,
COLOR_BayerRGGB2GRAY = COLOR_BayerBG2GRAY, //!< [8U/16U]
COLOR_BayerGRBG2GRAY = COLOR_BayerGB2GRAY, //!< [8U/16U]
COLOR_BayerBGGR2GRAY = COLOR_BayerRG2GRAY, //!< [8U/16U]
COLOR_BayerGBRG2GRAY = COLOR_BayerGR2GRAY, //!< [8U/16U]
//! Demosaicing using Variable Number of Gradients
COLOR_BayerBG2BGR_VNG = 62, //!< equivalent to RGGB Bayer pattern
COLOR_BayerGB2BGR_VNG = 63, //!< equivalent to GRBG Bayer pattern
COLOR_BayerRG2BGR_VNG = 64, //!< equivalent to BGGR Bayer pattern
COLOR_BayerGR2BGR_VNG = 65, //!< equivalent to GBRG Bayer pattern
COLOR_BayerBG2BGR_VNG = 62, //!< [8U] equivalent to RGGB Bayer pattern
COLOR_BayerGB2BGR_VNG = 63, //!< [8U] equivalent to GRBG Bayer pattern
COLOR_BayerRG2BGR_VNG = 64, //!< [8U] equivalent to BGGR Bayer pattern
COLOR_BayerGR2BGR_VNG = 65, //!< [8U] equivalent to GBRG Bayer pattern
COLOR_BayerRGGB2BGR_VNG = COLOR_BayerBG2BGR_VNG,
COLOR_BayerGRBG2BGR_VNG = COLOR_BayerGB2BGR_VNG,
COLOR_BayerBGGR2BGR_VNG = COLOR_BayerRG2BGR_VNG,
COLOR_BayerGBRG2BGR_VNG = COLOR_BayerGR2BGR_VNG,
COLOR_BayerRGGB2BGR_VNG = COLOR_BayerBG2BGR_VNG, //!< [8U]
COLOR_BayerGRBG2BGR_VNG = COLOR_BayerGB2BGR_VNG, //!< [8U]
COLOR_BayerBGGR2BGR_VNG = COLOR_BayerRG2BGR_VNG, //!< [8U]
COLOR_BayerGBRG2BGR_VNG = COLOR_BayerGR2BGR_VNG, //!< [8U]
COLOR_BayerRGGB2RGB_VNG = COLOR_BayerBGGR2BGR_VNG,
COLOR_BayerGRBG2RGB_VNG = COLOR_BayerGBRG2BGR_VNG,
COLOR_BayerBGGR2RGB_VNG = COLOR_BayerRGGB2BGR_VNG,
COLOR_BayerGBRG2RGB_VNG = COLOR_BayerGRBG2BGR_VNG,
COLOR_BayerRGGB2RGB_VNG = COLOR_BayerBGGR2BGR_VNG, //!< [8U]
COLOR_BayerGRBG2RGB_VNG = COLOR_BayerGBRG2BGR_VNG, //!< [8U]
COLOR_BayerBGGR2RGB_VNG = COLOR_BayerRGGB2BGR_VNG, //!< [8U]
COLOR_BayerGBRG2RGB_VNG = COLOR_BayerGRBG2BGR_VNG, //!< [8U]
COLOR_BayerBG2RGB_VNG = COLOR_BayerRG2BGR_VNG, //!< equivalent to RGGB Bayer pattern
COLOR_BayerGB2RGB_VNG = COLOR_BayerGR2BGR_VNG, //!< equivalent to GRBG Bayer pattern
COLOR_BayerRG2RGB_VNG = COLOR_BayerBG2BGR_VNG, //!< equivalent to BGGR Bayer pattern
COLOR_BayerGR2RGB_VNG = COLOR_BayerGB2BGR_VNG, //!< equivalent to GBRG Bayer pattern
COLOR_BayerBG2RGB_VNG = COLOR_BayerRG2BGR_VNG, //!< [8U] equivalent to RGGB Bayer pattern
COLOR_BayerGB2RGB_VNG = COLOR_BayerGR2BGR_VNG, //!< [8U] equivalent to GRBG Bayer pattern
COLOR_BayerRG2RGB_VNG = COLOR_BayerBG2BGR_VNG, //!< [8U] equivalent to BGGR Bayer pattern
COLOR_BayerGR2RGB_VNG = COLOR_BayerGB2BGR_VNG, //!< [8U] equivalent to GBRG Bayer pattern
//! Edge-Aware Demosaicing
COLOR_BayerBG2BGR_EA = 135, //!< equivalent to RGGB Bayer pattern
COLOR_BayerGB2BGR_EA = 136, //!< equivalent to GRBG Bayer pattern
COLOR_BayerRG2BGR_EA = 137, //!< equivalent to BGGR Bayer pattern
COLOR_BayerGR2BGR_EA = 138, //!< equivalent to GBRG Bayer pattern
COLOR_BayerBG2BGR_EA = 135, //!< [8U/16U] equivalent to RGGB Bayer pattern
COLOR_BayerGB2BGR_EA = 136, //!< [8U/16U] equivalent to GRBG Bayer pattern
COLOR_BayerRG2BGR_EA = 137, //!< [8U/16U] equivalent to BGGR Bayer pattern
COLOR_BayerGR2BGR_EA = 138, //!< [8U/16U] equivalent to GBRG Bayer pattern
COLOR_BayerRGGB2BGR_EA = COLOR_BayerBG2BGR_EA,
COLOR_BayerGRBG2BGR_EA = COLOR_BayerGB2BGR_EA,
COLOR_BayerBGGR2BGR_EA = COLOR_BayerRG2BGR_EA,
COLOR_BayerGBRG2BGR_EA = COLOR_BayerGR2BGR_EA,
COLOR_BayerRGGB2BGR_EA = COLOR_BayerBG2BGR_EA, //!< [8U/16U]
COLOR_BayerGRBG2BGR_EA = COLOR_BayerGB2BGR_EA, //!< [8U/16U]
COLOR_BayerBGGR2BGR_EA = COLOR_BayerRG2BGR_EA, //!< [8U/16U]
COLOR_BayerGBRG2BGR_EA = COLOR_BayerGR2BGR_EA, //!< [8U/16U]
COLOR_BayerRGGB2RGB_EA = COLOR_BayerBGGR2BGR_EA,
COLOR_BayerGRBG2RGB_EA = COLOR_BayerGBRG2BGR_EA,
COLOR_BayerBGGR2RGB_EA = COLOR_BayerRGGB2BGR_EA,
COLOR_BayerGBRG2RGB_EA = COLOR_BayerGRBG2BGR_EA,
COLOR_BayerRGGB2RGB_EA = COLOR_BayerBGGR2BGR_EA, //!< [8U/16U]
COLOR_BayerGRBG2RGB_EA = COLOR_BayerGBRG2BGR_EA, //!< [8U/16U]
COLOR_BayerBGGR2RGB_EA = COLOR_BayerRGGB2BGR_EA, //!< [8U/16U]
COLOR_BayerGBRG2RGB_EA = COLOR_BayerGRBG2BGR_EA, //!< [8U/16U]
COLOR_BayerBG2RGB_EA = COLOR_BayerRG2BGR_EA, //!< equivalent to RGGB Bayer pattern
COLOR_BayerGB2RGB_EA = COLOR_BayerGR2BGR_EA, //!< equivalent to GRBG Bayer pattern
COLOR_BayerRG2RGB_EA = COLOR_BayerBG2BGR_EA, //!< equivalent to BGGR Bayer pattern
COLOR_BayerGR2RGB_EA = COLOR_BayerGB2BGR_EA, //!< equivalent to GBRG Bayer pattern
COLOR_BayerBG2RGB_EA = COLOR_BayerRG2BGR_EA, //!< [8U/16U] equivalent to RGGB Bayer pattern
COLOR_BayerGB2RGB_EA = COLOR_BayerGR2BGR_EA, //!< [8U/16U] equivalent to GRBG Bayer pattern
COLOR_BayerRG2RGB_EA = COLOR_BayerBG2BGR_EA, //!< [8U/16U] equivalent to BGGR Bayer pattern
COLOR_BayerGR2RGB_EA = COLOR_BayerGB2BGR_EA, //!< [8U/16U] equivalent to GBRG Bayer pattern
//! Demosaicing with alpha channel
COLOR_BayerBG2BGRA = 139, //!< equivalent to RGGB Bayer pattern
COLOR_BayerGB2BGRA = 140, //!< equivalent to GRBG Bayer pattern
COLOR_BayerRG2BGRA = 141, //!< equivalent to BGGR Bayer pattern
COLOR_BayerGR2BGRA = 142, //!< equivalent to GBRG Bayer pattern
COLOR_BayerBG2BGRA = 139, //!< [8U/16U] equivalent to RGGB Bayer pattern
COLOR_BayerGB2BGRA = 140, //!< [8U/16U] equivalent to GRBG Bayer pattern
COLOR_BayerRG2BGRA = 141, //!< [8U/16U] equivalent to BGGR Bayer pattern
COLOR_BayerGR2BGRA = 142, //!< [8U/16U] equivalent to GBRG Bayer pattern
COLOR_BayerRGGB2BGRA = COLOR_BayerBG2BGRA,
COLOR_BayerGRBG2BGRA = COLOR_BayerGB2BGRA,
COLOR_BayerBGGR2BGRA = COLOR_BayerRG2BGRA,
COLOR_BayerGBRG2BGRA = COLOR_BayerGR2BGRA,
COLOR_BayerRGGB2BGRA = COLOR_BayerBG2BGRA, //!< [8U/16U]
COLOR_BayerGRBG2BGRA = COLOR_BayerGB2BGRA, //!< [8U/16U]
COLOR_BayerBGGR2BGRA = COLOR_BayerRG2BGRA, //!< [8U/16U]
COLOR_BayerGBRG2BGRA = COLOR_BayerGR2BGRA, //!< [8U/16U]
COLOR_BayerRGGB2RGBA = COLOR_BayerBGGR2BGRA,
COLOR_BayerGRBG2RGBA = COLOR_BayerGBRG2BGRA,
COLOR_BayerBGGR2RGBA = COLOR_BayerRGGB2BGRA,
COLOR_BayerGBRG2RGBA = COLOR_BayerGRBG2BGRA,
COLOR_BayerRGGB2RGBA = COLOR_BayerBGGR2BGRA, //!< [8U/16U]
COLOR_BayerGRBG2RGBA = COLOR_BayerGBRG2BGRA, //!< [8U/16U]
COLOR_BayerBGGR2RGBA = COLOR_BayerRGGB2BGRA, //!< [8U/16U]
COLOR_BayerGBRG2RGBA = COLOR_BayerGRBG2BGRA, //!< [8U/16U]
COLOR_BayerBG2RGBA = COLOR_BayerRG2BGRA, //!< equivalent to RGGB Bayer pattern
COLOR_BayerGB2RGBA = COLOR_BayerGR2BGRA, //!< equivalent to GRBG Bayer pattern
COLOR_BayerRG2RGBA = COLOR_BayerBG2BGRA, //!< equivalent to BGGR Bayer pattern
COLOR_BayerGR2RGBA = COLOR_BayerGB2BGRA, //!< equivalent to GBRG Bayer pattern
COLOR_BayerBG2RGBA = COLOR_BayerRG2BGRA, //!< [8U/16U] equivalent to RGGB Bayer pattern
COLOR_BayerGB2RGBA = COLOR_BayerGR2BGRA, //!< [8U/16U] equivalent to GRBG Bayer pattern
COLOR_BayerRG2RGBA = COLOR_BayerBG2BGRA, //!< [8U/16U] equivalent to BGGR Bayer pattern
COLOR_BayerGR2RGBA = COLOR_BayerGB2BGRA, //!< [8U/16U] equivalent to GBRG Bayer pattern
COLOR_RGB2YUV_UYVY = 143, //!< convert between RGB and YUV UYVU, YUV is 4:2:2 and interleaved as U/Y1/V/Y2, see @ref color_convert_rgb_yuv_42x
COLOR_BGR2YUV_UYVY = 144, //!< convert between BGR and YUV UYVU, YUV is 4:2:2 and interleaved as U/Y1/V/Y2, see @ref color_convert_rgb_yuv_42x
COLOR_RGB2YUV_UYVY = 143, //!< [8U] convert between RGB and YUV UYVU, YUV is 4:2:2 and interleaved as U/Y1/V/Y2, see @ref color_convert_rgb_yuv_42x
COLOR_BGR2YUV_UYVY = 144, //!< [8U] convert between BGR and YUV UYVU, YUV is 4:2:2 and interleaved as U/Y1/V/Y2, see @ref color_convert_rgb_yuv_42x
COLOR_RGB2YUV_Y422 = COLOR_RGB2YUV_UYVY, //!< synonym to UYVY
COLOR_BGR2YUV_Y422 = COLOR_BGR2YUV_UYVY, //!< synonym to UYVY
COLOR_RGB2YUV_UYNV = COLOR_RGB2YUV_UYVY, //!< synonym to UYVY
COLOR_BGR2YUV_UYNV = COLOR_BGR2YUV_UYVY, //!< synonym to UYVY
COLOR_RGBA2YUV_UYVY = 145, //!< convert between RGBA and YUV UYVU, YUV is 4:2:2 and interleaved as U/Y1/V/Y2, see @ref color_convert_rgb_yuv_42x
COLOR_BGRA2YUV_UYVY = 146, //!< convert between BGRA and YUV UYVU, YUV is 4:2:2 and interleaved as U/Y1/V/Y2, see @ref color_convert_rgb_yuv_42x
COLOR_RGBA2YUV_UYVY = 145, //!< [8U] convert between RGBA and YUV UYVU, YUV is 4:2:2 and interleaved as U/Y1/V/Y2, see @ref color_convert_rgb_yuv_42x
COLOR_BGRA2YUV_UYVY = 146, //!< [8U] convert between BGRA and YUV UYVU, YUV is 4:2:2 and interleaved as U/Y1/V/Y2, see @ref color_convert_rgb_yuv_42x
COLOR_RGBA2YUV_Y422 = COLOR_RGBA2YUV_UYVY, //!< synonym to UYVY
COLOR_BGRA2YUV_Y422 = COLOR_BGRA2YUV_UYVY, //!< synonym to UYVY
COLOR_RGBA2YUV_UYNV = COLOR_RGBA2YUV_UYVY, //!< synonym to UYVY
COLOR_BGRA2YUV_UYNV = COLOR_BGRA2YUV_UYVY, //!< synonym to UYVY
COLOR_RGB2YUV_YUY2 = 147, //!< convert between RGB and YUV YUY2, YUV is 4:2:2 and interleaved as Y1/U/Y2/V, see @ref color_convert_rgb_yuv_42x
COLOR_BGR2YUV_YUY2 = 148, //!< convert between BGR and YUV YUY2, YUV is 4:2:2 and interleaved as Y1/U/Y2/V, see @ref color_convert_rgb_yuv_42x
COLOR_RGB2YUV_YVYU = 149, //!< convert between RGB and YUV YVYU, YUV is 4:2:2 and interleaved as Y1/V/Y2/U, see @ref color_convert_rgb_yuv_42x
COLOR_BGR2YUV_YVYU = 150, //!< convert between BGR and YUV YVYU, YUV is 4:2:2 and interleaved as Y1/V/Y2/U, see @ref color_convert_rgb_yuv_42x
COLOR_RGB2YUV_YUY2 = 147, //!< [8U] convert between RGB and YUV YUY2, YUV is 4:2:2 and interleaved as Y1/U/Y2/V, see @ref color_convert_rgb_yuv_42x
COLOR_BGR2YUV_YUY2 = 148, //!< [8U] convert between BGR and YUV YUY2, YUV is 4:2:2 and interleaved as Y1/U/Y2/V, see @ref color_convert_rgb_yuv_42x
COLOR_RGB2YUV_YVYU = 149, //!< [8U] convert between RGB and YUV YVYU, YUV is 4:2:2 and interleaved as Y1/V/Y2/U, see @ref color_convert_rgb_yuv_42x
COLOR_BGR2YUV_YVYU = 150, //!< [8U] convert between BGR and YUV YVYU, YUV is 4:2:2 and interleaved as Y1/V/Y2/U, see @ref color_convert_rgb_yuv_42x
COLOR_RGB2YUV_YUYV = COLOR_RGB2YUV_YUY2, //!< synonym to YUY2
COLOR_BGR2YUV_YUYV = COLOR_BGR2YUV_YUY2, //!< synonym to YUY2
COLOR_RGB2YUV_YUNV = COLOR_RGB2YUV_YUY2, //!< synonym to YUY2
COLOR_BGR2YUV_YUNV = COLOR_BGR2YUV_YUY2, //!< synonym to YUY2
COLOR_RGBA2YUV_YUY2 = 151, //!< convert between RGBA and YUV YUY2, YUV is 4:2:2 and interleaved as Y1/U/Y2/V, see @ref color_convert_rgb_yuv_42x
COLOR_BGRA2YUV_YUY2 = 152, //!< convert between BGRA and YUV YUY2, YUV is 4:2:2 and interleaved as Y1/U/Y2/V, see @ref color_convert_rgb_yuv_42x
COLOR_RGBA2YUV_YVYU = 153, //!< convert between RGBA and YUV YVYU, YUV is 4:2:2 and interleaved as Y1/V/Y2/U, see @ref color_convert_rgb_yuv_42x
COLOR_BGRA2YUV_YVYU = 154, //!< convert between BGRA and YUV YVYU, YUV is 4:2:2 and interleaved as Y1/V/Y2/U, see @ref color_convert_rgb_yuv_42x
COLOR_RGBA2YUV_YUY2 = 151, //!< [8U] convert between RGBA and YUV YUY2, YUV is 4:2:2 and interleaved as Y1/U/Y2/V, see @ref color_convert_rgb_yuv_42x
COLOR_BGRA2YUV_YUY2 = 152, //!< [8U] convert between BGRA and YUV YUY2, YUV is 4:2:2 and interleaved as Y1/U/Y2/V, see @ref color_convert_rgb_yuv_42x
COLOR_RGBA2YUV_YVYU = 153, //!< [8U] convert between RGBA and YUV YVYU, YUV is 4:2:2 and interleaved as Y1/V/Y2/U, see @ref color_convert_rgb_yuv_42x
COLOR_BGRA2YUV_YVYU = 154, //!< [8U] convert between BGRA and YUV YVYU, YUV is 4:2:2 and interleaved as Y1/V/Y2/U, see @ref color_convert_rgb_yuv_42x
COLOR_RGBA2YUV_YUYV = COLOR_RGBA2YUV_YUY2, //!< synonym to YUY2
COLOR_BGRA2YUV_YUYV = COLOR_BGRA2YUV_YUY2, //!< synonym to YUY2
COLOR_RGBA2YUV_YUNV = COLOR_RGBA2YUV_YUY2, //!< synonym to YUY2
@@ -2999,6 +3004,22 @@ peak) and will be smaller when there are multiple peaks.
CV_EXPORTS_W Point2d phaseCorrelate(InputArray src1, InputArray src2,
InputArray window = noArray(), CV_OUT double* response = 0);
/** @brief Detects translational shifts between two images.
This function extends the standard @ref phaseCorrelate method by improving sub-pixel accuracy
through iterative shift refinement in the phase-correlation space, as described in
@cite hrazdira2020iterative.
@param src1 Source floating point array (CV_32FC1 or CV_64FC1)
@param src2 Source floating point array (CV_32FC1 or CV_64FC1)
@param L2size The size of the correlation neighborhood used by the iterative shift refinement algorithm.
@param maxIters The maximum number of iterations the iterative refinement algorithm will run.
@returns detected sub-pixel shift between the two arrays.
@sa phaseCorrelate, dft, idft, createHanningWindow
*/
CV_EXPORTS_W Point2d phaseCorrelateIterative(InputArray src1, InputArray src2, int L2size = 7, int maxIters = 10);
/** @brief This function computes a Hanning window coefficients in two dimensions.
See (https://en.wikipedia.org/wiki/Hann_function) and (https://en.wikipedia.org/wiki/Window_function)
@@ -3736,6 +3757,7 @@ floating-point.
channels is derived automatically from src and code.
@param hint Implementation modification flags. See #AlgorithmHint
@note The source image (src) must be of an appropriate type for the desired color conversion. see ColorConversionCodes
@see @ref imgproc_color_conversions
*/
CV_EXPORTS_W void cvtColor( InputArray src, OutputArray dst, int code, int dstCn = 0, AlgorithmHint hint = cv::ALGO_HINT_DEFAULT );
@@ -3789,6 +3811,7 @@ The function can do the following transformations:
#COLOR_BayerBG2BGRA , #COLOR_BayerGB2BGRA , #COLOR_BayerRG2BGRA , #COLOR_BayerGR2BGRA
@note The source image (src) must be of an appropriate type for the desired color conversion. see ColorConversionCodes
@sa cvtColor
*/
CV_EXPORTS_W void demosaicing(InputArray src, OutputArray dst, int code, int dstCn = 0);
@@ -3812,6 +3835,18 @@ used for images only.
@note Only applicable to contour moments calculations from Python bindings: Note that the numpy
type for the input array should be either np.int32 or np.float32.
@note For contour-based moments, the zeroth-order moment \c m00 represents
the contour area.
If the input contour is degenerate (for example, a single point or all points
are collinear), the area is zero and therefore \c m00 == 0.
In this case, the centroid coordinates (\c m10/m00, \c m01/m00) are undefined
and must be handled explicitly by the caller.
A common workaround is to compute the center using cv::boundingRect() or by
averaging the input points.
@sa contourArea, arcLength
*/
CV_EXPORTS_W Moments moments( InputArray array, bool binaryImage = false );
@@ -4219,7 +4254,7 @@ area. It takes the set of points and the parameter k as input and returns the ar
enclosing polygon.
The Implementation is based on a paper by Aggarwal, Chang and Yap @cite Aggarwal1985. They
provide a \f$\theta(n²log(n)log(k))\f$ algorighm for finding the minimal convex polygon with k
provide a \f$\theta(n²log(n)log(k))\f$ algorithm for finding the minimal convex polygon with k
vertices enclosing a 2D convex polygon with n vertices (k < n). Since the #minEnclosingConvexPolygon
function takes a 2D point set as input, an additional preprocessing step of computing the convex hull
of the 2D point set is required. The complexity of the #convexHull function is \f$O(n log(n))\f$ which
+19 -5
View File
@@ -162,26 +162,40 @@ approxPolyDP_( const Point_<T>* src_contour, int count0, Point_<T>* dst_contour,
if( pos != slice.end )
{
double dx, dy, dist, max_dist = 0;
double dx, dy, max_dist_2_mul_segment_len_2 = 0;
dx = end_pt.x - start_pt.x;
dy = end_pt.y - start_pt.y;
double segment_len_2 = dx * dx + dy * dy;
CV_Assert( dx != 0 || dy != 0 );
while( pos != slice.end )
{
READ_PT(pt, pos);
dist = fabs((pt.y - start_pt.y) * dx - (pt.x - start_pt.x) * dy);
double projection = ((pt.x - start_pt.x) * dx + (pt.y - start_pt.y) * dy);
if( dist > max_dist )
double dist_2_mul_segment_len_2;
if ( projection < 0 )
{
max_dist = dist;
dist_2_mul_segment_len_2 = ((pt.x - start_pt.x) * (pt.x - start_pt.x) + (pt.y - start_pt.y) * (pt.y - start_pt.y)) * segment_len_2;
} else if ( projection > segment_len_2 )
{
dist_2_mul_segment_len_2 = ((pt.x - end_pt.x) * (pt.x - end_pt.x) + (pt.y - end_pt.y) * (pt.y - end_pt.y)) * segment_len_2;
} else
{
double dist = ((pt.y - start_pt.y) * dx - (pt.x - start_pt.x) * dy);
dist_2_mul_segment_len_2 = dist * dist;
}
if( dist_2_mul_segment_len_2 > max_dist_2_mul_segment_len_2 )
{
max_dist_2_mul_segment_len_2 = dist_2_mul_segment_len_2;
right_slice.start = (pos+count-1)%count;
}
}
le_eps = max_dist * max_dist <= eps * (dx * dx + dy * dy);
le_eps = max_dist_2_mul_segment_len_2 <= eps * segment_len_2;
}
else
{
@@ -321,7 +321,7 @@ bilateralFilter_32f( const Mat& src, Mat& dst, int d,
}
// parallel_for usage
CV_CPU_DISPATCH(bilateralFilterInvoker_32f, (cn, radius, maxk, space_ofs, temp, dst, scale_index, space_weight, expLUT),
CV_CPU_DISPATCH(bilateralFilterInvoker_32f, (cn, radius, maxk, space_ofs, temp, dst, scale_index, space_weight, expLUT, kExpNumBins),
CV_CPU_DISPATCH_MODES_ALL);
}
+15 -14
View File
@@ -58,7 +58,7 @@ void bilateralFilterInvoker_8u(
int* space_ofs, float *space_weight, float *color_weight);
void bilateralFilterInvoker_32f(
int cn, int radius, int maxk, int *space_ofs,
const Mat& temp, Mat& dst, float scale_index, float *space_weight, float *expLUT);
const Mat& temp, Mat& dst, float scale_index, float *space_weight, float *expLUT, int kExpNumBins);
#ifndef CV_CPU_OPTIMIZATION_DECLARATIONS_ONLY
@@ -495,9 +495,9 @@ class BilateralFilter_32f_Invoker :
public:
BilateralFilter_32f_Invoker(int _cn, int _radius, int _maxk, int *_space_ofs,
const Mat& _temp, Mat& _dest, float _scale_index, float *_space_weight, float *_expLUT) :
const Mat& _temp, Mat& _dest, float _scale_index, float *_space_weight, float *_expLUT, int _kExpNumBins) :
cn(_cn), radius(_radius), maxk(_maxk), space_ofs(_space_ofs),
temp(&_temp), dest(&_dest), scale_index(_scale_index), space_weight(_space_weight), expLUT(_expLUT)
temp(&_temp), dest(&_dest), scale_index(_scale_index), space_weight(_space_weight), expLUT(_expLUT), kExpNumBins(_kExpNumBins)
{
}
@@ -550,7 +550,7 @@ public:
v_float32 val0 = vx_load(ksptr);
v_float32 knan0 = v_not_nan(val0);
v_float32 alpha0 = v_and(v_and(v_mul(v_absdiff(val0, rval0), sindex), v_not_nan(rval0)), knan0);
v_int32 idx0 = v_trunc(alpha0);
v_int32 idx0 = v_min(v_trunc(alpha0), vx_setall_s32(kExpNumBins));
alpha0 = v_sub(alpha0, v_cvt_f32(idx0));
v_float32 w0 = v_and(v_mul(kweight, v_muladd(v_lut(this->expLUT + 1, idx0), alpha0, v_mul(v_lut(this->expLUT, idx0), v_sub(v_one, alpha0)))), knan0);
v_wsum0 = v_add(v_wsum0, w0);
@@ -560,7 +560,7 @@ public:
v_float32 val1 = vx_load(ksptr + nlanes);
v_float32 knan1 = v_not_nan(val1);
v_float32 alpha1 = v_and(v_and(v_mul(v_absdiff(val1, rval1), sindex), v_not_nan(rval1)), knan1);
v_int32 idx1 = v_trunc(alpha1);
v_int32 idx1 = v_min(v_trunc(alpha1), vx_setall_s32(kExpNumBins));
alpha1 = v_sub(alpha1, v_cvt_f32(idx1));
v_float32 w1 = v_and(v_mul(kweight, v_muladd(v_lut(this->expLUT + 1, idx1), alpha1, v_mul(v_lut(this->expLUT, idx1), v_sub(v_one, alpha1)))), knan1);
v_wsum1 = v_add(v_wsum1, w1);
@@ -570,7 +570,7 @@ public:
v_float32 val2 = vx_load(ksptr + nlanes_2);
v_float32 knan2 = v_not_nan(val2);
v_float32 alpha2 = v_and(v_and(v_mul(v_absdiff(val2, rval2), sindex), v_not_nan(rval2)), knan2);
v_int32 idx2 = v_trunc(alpha2);
v_int32 idx2 = v_min(v_trunc(alpha2), vx_setall_s32(kExpNumBins));
alpha2 = v_sub(alpha2, v_cvt_f32(idx2));
v_float32 w2 = v_and(v_mul(kweight, v_muladd(v_lut(this->expLUT + 1, idx2), alpha2, v_mul(v_lut(this->expLUT, idx2), v_sub(v_one, alpha2)))), knan2);
v_wsum2 = v_add(v_wsum2, w2);
@@ -580,7 +580,7 @@ public:
v_float32 val3 = vx_load(ksptr + nlanes_3);
v_float32 knan3 = v_not_nan(val3);
v_float32 alpha3 = v_and(v_and(v_mul(v_absdiff(val3, rval3), sindex), v_not_nan(rval3)), knan3);
v_int32 idx3 = v_trunc(alpha3);
v_int32 idx3 = v_min(v_trunc(alpha3), vx_setall_s32(kExpNumBins));
alpha3 = v_sub(alpha3, v_cvt_f32(idx3));
v_float32 w3 = v_and(v_mul(kweight, v_muladd(v_lut(this->expLUT + 1, idx3), alpha3, v_mul(v_lut(this->expLUT, idx3), v_sub(v_one, alpha3)))), knan3);
v_wsum3 = v_add(v_wsum3, w3);
@@ -611,7 +611,7 @@ public:
v_float32 val0 = vx_load(ksptr);
v_float32 knan0 = v_not_nan(val0);
v_float32 alpha0 = v_and(v_and(v_mul(v_absdiff(val0, rval0), sindex), rval0_not_nan), knan0);
v_int32 idx0 = v_trunc(alpha0);
v_int32 idx0 = v_min(v_trunc(alpha0), vx_setall_s32(kExpNumBins));
alpha0 = v_sub(alpha0, v_cvt_f32(idx0));
v_float32 w0 = v_and(v_mul(kweight, v_muladd(v_lut(this->expLUT + 1, idx0), alpha0, v_mul(v_lut(this->expLUT, idx0), v_sub(v_one, alpha0)))), knan0);
v_wsum0 = v_add(v_wsum0, w0);
@@ -621,7 +621,7 @@ public:
v_float32 val1 = vx_load(ksptr + nlanes);
v_float32 knan1 = v_not_nan(val1);
v_float32 alpha1 = v_and(v_and(v_mul(v_absdiff(val1, rval1), sindex), rval1_not_nan), knan1);
v_int32 idx1 = v_trunc(alpha1);
v_int32 idx1 = v_min(v_trunc(alpha1), vx_setall_s32(kExpNumBins));
alpha1 = v_sub(alpha1, v_cvt_f32(idx1));
v_float32 w1 = v_and(v_mul(kweight, v_muladd(v_lut(this->expLUT + 1, idx1), alpha1, v_mul(v_lut(this->expLUT, idx1), v_sub(v_one, alpha1)))), knan1);
v_wsum1 = v_add(v_wsum1, w1);
@@ -640,7 +640,7 @@ public:
{
const float* ksptr = sptr_j + space_ofs[k];
float val = *ksptr;
float alpha = std::abs(val - rval) * scale_index;
float alpha = std::min(std::abs(val - rval) * scale_index, (float)kExpNumBins);
int idx = cvFloor(alpha);
alpha -= idx;
if (!cvIsNaN(val))
@@ -681,7 +681,7 @@ public:
v_float32 knan = v_and(v_and(v_not_nan(kb), v_not_nan(kg)), v_not_nan(kr));
v_float32 alpha = v_and(v_and(v_and(v_and(v_mul(v_add(v_add(v_absdiff(kb, rb), v_absdiff(kg, rg)), v_absdiff(kr, rr)), sindex), v_not_nan(rb)), v_not_nan(rg)), v_not_nan(rr)), knan);
v_int32 idx = v_trunc(alpha);
v_int32 idx = v_min(v_trunc(alpha), vx_setall_s32(kExpNumBins));
alpha = v_sub(alpha, v_cvt_f32(idx));
v_float32 w = v_and(v_mul(kweight, v_muladd(v_lut(this->expLUT + 1, idx), alpha, v_mul(v_lut(this->expLUT, idx), v_sub(v_one, alpha)))), knan);
@@ -709,7 +709,7 @@ public:
bool v_NAN = cvIsNaN(b) || cvIsNaN(g) || cvIsNaN(r);
float rb = rsptr[0], rg = rsptr[1], rr = rsptr[2];
bool r_NAN = cvIsNaN(rb) || cvIsNaN(rg) || cvIsNaN(rr);
float alpha = (std::abs(b - rb) + std::abs(g - rg) + std::abs(r - rr)) * scale_index;
float alpha = std::min((std::abs(b - rb) + std::abs(g - rg) + std::abs(r - rr)) * scale_index, (float)kExpNumBins);
int idx = cvFloor(alpha);
alpha -= idx;
if (!v_NAN)
@@ -753,17 +753,18 @@ private:
const Mat* temp;
Mat *dest;
float scale_index, *space_weight, *expLUT;
int kExpNumBins;
};
} // namespace anon
void bilateralFilterInvoker_32f(
int cn, int radius, int maxk, int *space_ofs,
const Mat& temp, Mat& dst, float scale_index, float *space_weight, float *expLUT)
const Mat& temp, Mat& dst, float scale_index, float *space_weight, float *expLUT, int kExpNumBins)
{
CV_INSTRUMENT_REGION();
BilateralFilter_32f_Invoker body(cn, radius, maxk, space_ofs, temp, dst, scale_index, space_weight, expLUT);
BilateralFilter_32f_Invoker body(cn, radius, maxk, space_ofs, temp, dst, scale_index, space_weight, expLUT, kExpNumBins);
parallel_for_(Range(0, dst.rows), body, dst.total()/(double)(1<<16));
}
+38 -4
View File
@@ -219,9 +219,9 @@ void convexHull( InputArray _points, OutputArray _hull, bool clockwise, bool ret
}
for( i = 0; i < tl_count-1; i++ )
hullbuf[nout++] = int(pointer[tl_stack[i]] - data0);
hullbuf[nout++] = tl_stack[i];
for( i = tr_count - 1; i > 0; i-- )
hullbuf[nout++] = int(pointer[tr_stack[i]] - data0);
hullbuf[nout++] = tr_stack[i];
int stop_idx = tr_count > 2 ? tr_stack[1] : tl_count > 2 ? tl_stack[tl_count - 2] : -1;
// lower half
@@ -257,9 +257,43 @@ void convexHull( InputArray _points, OutputArray _hull, bool clockwise, bool ret
}
for( i = 0; i < bl_count-1; i++ )
hullbuf[nout++] = int(pointer[bl_stack[i]] - data0);
hullbuf[nout++] = bl_stack[i];
for( i = br_count-1; i > 0; i-- )
hullbuf[nout++] = int(pointer[br_stack[i]] - data0);
hullbuf[nout++] = br_stack[i];
if (!returnPoints)
{
// Try keep monotonous indices in case of self-intersection.
for (i = 0; i < nout; ++i)
{
auto prev = pointer[hullbuf[(i == 0 ? nout : i) - 1]];
auto next = pointer[hullbuf[(i + 1) % nout]];
auto cur = pointer[hullbuf[i]];
if ((prev < cur && cur < next) || (prev > cur && cur > next))
{
continue;
}
for (int j = hullbuf[i] + 1; j < total; ++j)
{
cur = pointer[j];
if (*pointer[hullbuf[i]] == *cur)
{
if ((prev < cur && cur < next) || (prev > cur && cur > next))
{
hullbuf[i] = j;
break;
}
}
else
break;
}
}
}
for (i = 0; i < nout; ++i)
{
hullbuf[i] = int(pointer[hullbuf[i]] - data0);
}
// try to make the convex hull indices form
// an ascending or descending sequence by the cyclic
+1 -1
View File
@@ -1668,7 +1668,7 @@ ThickLine( Mat& img, Point2l p0, Point2l p1, const void* color,
{
if( line_type < cv::LINE_AA )
{
if( line_type == 1 || line_type == 4 || shift == 0 )
if( line_type == 1 || line_type == 8 || shift == 0 )
{
p0.x = (p0.x + (XY_ONE>>1)) >> XY_SHIFT;
p0.y = (p0.y + (XY_ONE>>1)) >> XY_SHIFT;
+33 -2
View File
@@ -1171,8 +1171,24 @@ static bool replacementFilter2D(int stype, int dtype, int kernel_type,
int anchor_x, int anchor_y,
double delta, int borderType, bool isSubmatrix)
{
// Prioritize stateless implementation
int res = cv_hal_filter_stateless(src_data, src_step, stype,
dst_data, dst_step, dtype, width, height,
full_width, full_height, offset_x, offset_y,
kernel_data, kernel_step, kernel_type,
kernel_width, kernel_height, anchor_x, anchor_y,
delta, borderType, isSubmatrix, src_data == dst_data);
if (res == CV_HAL_ERROR_OK)
{
return true;
} else if (res != CV_HAL_ERROR_NOT_IMPLEMENTED)
{
CV_Error_(cv::Error::StsInternal,
("HAL implementation filter_stateless ==> " CVAUX_STR(cv_hal_filter_stateless) " returned %d (0x%08x)", res, res));
}
cvhalFilter2D* ctx;
int res = cv_hal_filterInit(&ctx, kernel_data, kernel_step, kernel_type, kernel_width, kernel_height, width, height,
res = cv_hal_filterInit(&ctx, kernel_data, kernel_step, kernel_type, kernel_width, kernel_height, width, height,
stype, dtype, borderType, delta, anchor_x, anchor_y, isSubmatrix, src_data == dst_data);
if (res == CV_HAL_ERROR_NOT_IMPLEMENTED)
{
@@ -1385,8 +1401,23 @@ static bool replacementSepFilter(int stype, int dtype, int ktype,
uchar * kernely_data, int kernely_len,
int anchor_x, int anchor_y, double delta, int borderType)
{
// Prioritize stateless implementation
int res = cv_hal_sepFilter_stateless(src_data, src_step, stype,
dst_data, dst_step, dtype,
width, height, full_width, full_height, offset_x, offset_y,
kernelx_data, kernelx_len, kernely_data, kernely_len, ktype,
anchor_x, anchor_y, delta, borderType);
if (res == CV_HAL_ERROR_OK)
{
return true;
} else if (res != CV_HAL_ERROR_NOT_IMPLEMENTED)
{
CV_Error_(cv::Error::StsInternal,
("HAL implementation sepFilter_stateless ==> " CVAUX_STR(cv_hal_sepFilter_stateless) " returned %d (0x%08x)", res, res));
}
cvhalFilter2D *ctx;
int res = cv_hal_sepFilterInit(&ctx, stype, dtype, ktype,
res = cv_hal_sepFilterInit(&ctx, stype, dtype, ktype,
kernelx_data, kernelx_len,
kernely_data, kernely_len,
anchor_x, anchor_y, delta, borderType);
+112
View File
@@ -86,6 +86,40 @@ int my_hal_filterFree(cvhalFilter2D *context) {
*/
struct cvhalFilter2D {};
/**
@brief 2D filtering in a stateless manner
@param src_data source image data
@param src_step source image step
@param src_type source image type
@param dst_data destination image data
@param dst_step destination image step
@param dst_type destination image type
@param width images width
@param height images height
@param full_width full width of source image (outside the ROI)
@param full_height full height of source image (outside the ROI)
@param offset_x source image ROI offset X
@param offset_y source image ROI offset Y
@param kernel_data pointer to kernel data
@param kernel_step kernel step
@param kernel_type kernel type (CV_8U, ...)
@param kernel_width kernel width
@param kernel_height kernel height
@param anchor_x relative X position of center point within the kernel
@param anchor_y relative Y position of center point within the kernel
@param delta added to pixel values
@param borderType border processing mode (CV_HAL_BORDER_REFLECT, ...)
@param isSubmatrix indicates whether the submatrices will be allowed as source image
@param allowInplace indicates whether the inplace operation will be possible
@sa cv::filter2D, cv::hal::Filter2D
*/
inline int hal_ni_filter_stateless(const uchar * src_data, size_t src_step, int src_type,
uchar * dst_data, size_t dst_step, int dst_type,
int width, int height, int full_width, int full_height, int offset_x, int offset_y,
const uchar * kernel_data, size_t kernel_step, int kernel_type, int kernel_width, int kernel_height,
int anchor_x, int anchor_y, double delta, int borderType, bool isSubmatrix, bool allowInplace)
{ return CV_HAL_ERROR_NOT_IMPLEMENTED; }
/**
@brief hal_filterInit
@param context double pointer to user-defined context
@@ -131,11 +165,45 @@ inline int hal_ni_filter(cvhalFilter2D *context, uchar *src_data, size_t src_ste
inline int hal_ni_filterFree(cvhalFilter2D *context) { return CV_HAL_ERROR_NOT_IMPLEMENTED; }
//! @cond IGNORED
#define cv_hal_filter_stateless hal_ni_filter_stateless
#define cv_hal_filterInit hal_ni_filterInit
#define cv_hal_filter hal_ni_filter
#define cv_hal_filterFree hal_ni_filterFree
//! @endcond
/**
@brief separable filtering in a stateless manner
@param src_data source image data
@param src_step source image step
@param src_type source image type
@param dst_data destination image data
@param dst_step destination image step
@param dst_type destination image type
@param width images width
@param height images height
@param full_width full width of source image (outside the ROI)
@param full_height full height of source image (outside the ROI)
@param offset_x source image ROI offset X
@param offset_y source image ROI offset Y
@param kernelx_data pointer to x-kernel data
@param kernelx_len x-kernel vector length
@param kernely_data pointer to y-kernel data
@param kernely_len y-kernel vector length
@param kernel_type kernel type (CV_8U, ...)
@param anchor_x relative X position of center point within the kernel
@param anchor_y relative Y position of center point within the kernel
@param delta added to pixel values
@param borderType border processing mode (CV_HAL_BORDER_REFLECT, ...)
@sa cv::sepFilter2D, cv::hal::SepFilter2D
*/
inline int hal_ni_sepFilter_stateless(const uchar * src_data, size_t src_step, int src_type,
uchar * dst_data, size_t dst_step, int dst_type,
int width, int height, int full_width, int full_height, int offset_x, int offset_y,
const uchar * kernelx_data, int kernelx_len,
const uchar * kernely_data, int kernely_len,
int kernel_type, int anchor_x, int anchor_y, double delta, int borderType)
{ return CV_HAL_ERROR_NOT_IMPLEMENTED; }
/**
@brief hal_sepFilterInit
@param context double pointer to user-defined context
@@ -177,11 +245,54 @@ inline int hal_ni_sepFilter(cvhalFilter2D *context, uchar *src_data, size_t src_
inline int hal_ni_sepFilterFree(cvhalFilter2D *context) { return CV_HAL_ERROR_NOT_IMPLEMENTED; }
//! @cond IGNORED
#define cv_hal_sepFilter_stateless hal_ni_sepFilter_stateless
#define cv_hal_sepFilterInit hal_ni_sepFilterInit
#define cv_hal_sepFilter hal_ni_sepFilter
#define cv_hal_sepFilterFree hal_ni_sepFilterFree
//! @endcond
/**
@brief morphology in a stateless manner
@param operation morphology operation CV_HAL_MORPH_ERODE or CV_HAL_MORPH_DILATE
@param src_data source image data
@param src_step source image step
@param src_type source image type
@param dst_data destination image data
@param dst_step destination image step
@param dst_type destination image type
@param width images width
@param height images height
@param src_full_width full width of source image (outside the ROI)
@param src_full_height full height of source image (outside the ROI)
@param src_roi_x source image ROI X offset
@param src_roi_y source image ROI Y offset
@param dst_full_width full width of destination image
@param dst_full_height full height of destination image
@param dst_roi_x destination image ROI X offset
@param dst_roi_y destination image ROI Y offset
@param kernel_data pointer to kernel data
@param kernel_step kernel step
@param kernel_type kernel type (CV_8U, ...)
@param kernel_width kernel width
@param kernel_height kernel height
@param anchor_x relative X position of center point within the kernel
@param anchor_y relative Y position of center point within the kernel
@param borderType border processing mode (CV_HAL_BORDER_REFLECT, ...)
@param borderValue values to use for CV_HAL_BORDER_CONSTANT mode
@param iterations number of iterations
@param allowSubmatrix indicates whether the submatrices will be allowed as source image
@param allowInplace indicates whether the inplace operation will be possible
@sa cv::erode, cv::dilate, cv::morphologyEx, cv::hal::Morph
*/
inline int hal_ni_morph_stateless(int operation, const uchar * src_data, size_t src_step, int src_type,
uchar * dst_data, size_t dst_step, int dst_type,
int width, int height, int src_full_width, int src_full_height, int src_roi_x, int src_roi_y,
int dst_full_width, int dst_full_height, int dst_roi_x, int dst_roi_y,
const uchar * kernel_data, size_t kernel_step, int kernel_type, int kernel_width, int kernel_height,
int anchor_x, int anchor_y, int borderType, const double borderValue[4],
int iterations, bool allowSubmatrix, bool allowInplace)
{ return CV_HAL_ERROR_NOT_IMPLEMENTED; }
/**
@brief hal_morphInit
@param context double pointer to user-defined context
@@ -233,6 +344,7 @@ inline int hal_ni_morph(cvhalFilter2D *context, uchar *src_data, size_t src_step
inline int hal_ni_morphFree(cvhalFilter2D *context) { return CV_HAL_ERROR_NOT_IMPLEMENTED; }
//! @cond IGNORED
#define cv_hal_morph_stateless hal_ni_morph_stateless
#define cv_hal_morphInit hal_ni_morphInit
#define cv_hal_morph hal_ni_morph
#define cv_hal_morphFree hal_ni_morphFree
+9 -9
View File
@@ -1024,7 +1024,7 @@ void HoughLinesPointSet( InputArray _point, OutputArray _lines, int lines_max, i
int r = idx - (n+1)*(numrho+2) - 1;
line.rho = static_cast<float>(min_rho) + r * (float)rho_step;
line.angle = static_cast<float>(min_theta) + n * (float)theta_step;
lines.push_back(Vec3d((double)accum[idx], (double)line.rho, (double)line.angle));
lines.emplace_back((double)accum[idx], (double)line.rho, (double)line.angle);
}
Mat(lines).copyTo(_lines);
@@ -1125,7 +1125,7 @@ public:
{
if (ptr[x])
{
list.push_back(Point(x, y));
list.emplace_back(x, y);
}
}
}
@@ -1489,7 +1489,7 @@ protected:
// Check if the circle has enough support
if(maxCount > accThreshold)
{
circlesLocal.push_back(EstimatedCircle(Vec3f(curCenter.x, curCenter.y, rBest), maxCount));
circlesLocal.emplace_back(Vec3f(curCenter.x, curCenter.y, rBest), maxCount);
}
}
@@ -1847,7 +1847,7 @@ static void HoughCirclesAlt( const Mat& img, std::vector<EstimatedCircle>& circl
continue;
mdata[y*mstep + x] = (uchar)1;
stack.push_back(Point(x, y));
stack.emplace_back(x, y);
bool backtrace_mode = false;
do
@@ -1858,7 +1858,7 @@ static void HoughCirclesAlt( const Mat& img, std::vector<EstimatedCircle>& circl
int vy = dyData[p.y*dxystep + p.x];
float mag = std::sqrt((float)vx*vx+(float)vy*vy);
nz.push_back(Vec4f((float)p.x, (float)p.y, (float)vx, (float)vy));
nz.emplace_back((float)p.x, (float)p.y, (float)vx, (float)vy);
CV_Assert(mdata[p.y*mstep + p.x] == 1);
int sx = cvRound(vx * RAY_FP_SCALE / mag);
@@ -1903,7 +1903,7 @@ static void HoughCirclesAlt( const Mat& img, std::vector<EstimatedCircle>& circl
if( mdata[y_*mstep + x_] || !edgeData[y_*estep + x_])
continue;
mdata[y_*mstep + x_] = (uchar)1;
stack.push_back(Point(x_, y_));
stack.emplace_back(x_, y_);
neighbors++;
}
@@ -1919,7 +1919,7 @@ static void HoughCirclesAlt( const Mat& img, std::vector<EstimatedCircle>& circl
// insert a special "stop marker" in the end of each
// connected component to make sure we
// finalize and analyze the arc segment
nz.push_back(Vec4f(0.f, 0.f, 0.f, 0.f));
nz.emplace_back(0.f, 0.f, 0.f, 0.f);
}
}
@@ -1951,7 +1951,7 @@ static void HoughCirclesAlt( const Mat& img, std::vector<EstimatedCircle>& circl
{
float cx = (float)((left + x - 1)*dp*0.5f);
float cy = (float)(y*dp);
centers.push_back(Point2f(cx, cy));
centers.emplace_back(cx, cy);
left = -1;
}
}
@@ -2223,7 +2223,7 @@ static void HoughCirclesAlt( const Mat& img, std::vector<EstimatedCircle>& circl
// (accepted ? '+' : '-'), cx, cy, rk, cjk.weight, count, max_runlen, cjk.mask);
if( accepted )
local_circles.push_back(EstimatedCircle(Vec3f(cx, cy, (float)rk), cjk.weight));
local_circles.emplace_back(Vec3f(cx, cy, (float)rk), cjk.weight);
}
}
}
+15 -1
View File
@@ -212,8 +212,22 @@ static bool halMorph(int op, int src_type, int dst_type,
int kernel_width, int kernel_height, int anchor_x, int anchor_y,
int borderType, const double borderValue[4], int iterations, bool isSubmatrix)
{
// Prioritize stateless implementation
int res = cv_hal_morph_stateless(op, src_data, src_step, src_type, dst_data, dst_step, dst_type, width, height,
roi_width, roi_height, roi_x, roi_y, roi_width2, roi_height2, roi_x2, roi_y2,
kernel_data, kernel_step, kernel_type, kernel_width, kernel_height, anchor_x, anchor_y,
borderType, borderValue, iterations, isSubmatrix, src_data == dst_data);
if (res == CV_HAL_ERROR_OK)
{
return true;
} else if (res != CV_HAL_ERROR_NOT_IMPLEMENTED)
{
CV_Error_(cv::Error::StsInternal,
("HAL implementation morph_stateless ==> " CVAUX_STR(cv_hal_morph_stateless) " returned %d (0x%08x)", res, res));
}
cvhalFilter2D * ctx;
int res = cv_hal_morphInit(&ctx, op, src_type, dst_type, width, height,
res = cv_hal_morphInit(&ctx, op, src_type, dst_type, width, height,
kernel_type, kernel_data, kernel_step, kernel_width, kernel_height,
anchor_x, anchor_y,
borderType, borderValue,
+191
View File
@@ -0,0 +1,191 @@
#include "precomp.hpp"
#include <cmath>
namespace {
template <typename T>
void calculateCrossPowerSpectrum(const cv::Mat& dft1, const cv::Mat& dft2, cv::Mat& cps)
{
for (int row = 0; row < dft1.rows; ++row)
{
auto* cpsp = cps.ptr<cv::Vec<T, 2>>(row);
const auto* dft1p = dft1.ptr<cv::Vec<T, 2>>(row);
const auto* dft2p = dft2.ptr<cv::Vec<T, 2>>(row);
for (int col = 0; col < dft1.cols; ++col)
{
const T re = dft1p[col][0] * dft2p[col][0] + dft1p[col][1] * dft2p[col][1];
const T im = dft1p[col][0] * dft2p[col][1] - dft1p[col][1] * dft2p[col][0];
const T mag = std::sqrt(re * re + im * im);
cpsp[col][0] = re / mag;
cpsp[col][1] = im / mag;
}
}
}
cv::Mat calculateCrossPowerSpectrum(const cv::Mat& dft1, const cv::Mat& dft2)
{
cv::Mat cps(dft1.rows, dft1.cols, dft1.type());
if (dft1.type() == CV_32FC2)
calculateCrossPowerSpectrum<float>(dft1, dft2, cps);
else if (dft1.type() == CV_64FC2)
calculateCrossPowerSpectrum<double>(dft1, dft2, cps);
else
CV_Error(cv::Error::StsNotImplemented, "Only CV_32FC2 and CV_64FC2 types are supported");
return cps;
}
void fftshift(cv::Mat& out)
{
int cx = out.cols / 2;
int cy = out.rows / 2;
cv::Mat q0(out, cv::Rect(0, 0, cx, cy));
cv::Mat q1(out, cv::Rect(cx, 0, cx, cy));
cv::Mat q2(out, cv::Rect(0, cy, cx, cy));
cv::Mat q3(out, cv::Rect(cx, cy, cx, cy));
cv::Mat tmp;
q0.copyTo(tmp);
q3.copyTo(q0);
tmp.copyTo(q3);
q1.copyTo(tmp);
q2.copyTo(q1);
tmp.copyTo(q2);
}
bool isOutOfBounds(const cv::Point2i& peak, const cv::Mat& mat, int size)
{
return peak.x - size / 2 < 0 || peak.y - size / 2 < 0 || peak.x + size / 2 >= mat.cols ||
peak.y + size / 2 >= mat.rows;
}
bool reduceL2size(int& L2size)
{
L2size -= 2;
return L2size >= 3;
}
int getL1size(int L2Usize, double L1ratio)
{
int L1size = static_cast<int>(std::floor(L1ratio * L2Usize));
return (L1size % 2) ? L1size : L1size + 1;
}
cv::Point2d getPeakSubpixel(const cv::Mat& mat)
{
const auto m = moments(mat);
return cv::Point2d(m.m10 / m.m00, m.m01 / m.m00);
}
bool accuracyReached(const cv::Point2d& L1peak, const cv::Point2d& L1mid)
{
return std::abs(L1peak.x - L1mid.x) < 0.5 && std::abs(L1peak.y - L1mid.y) < 0.5;
}
cv::Point2d getSubpixelShift(const cv::Mat& L3,
const cv::Point2d& L3peak,
const cv::Point2d& L3mid,
int L2size)
{
while (isOutOfBounds(L3peak, L3, L2size))
if (!reduceL2size(L2size))
return L3peak - L3mid;
cv::Mat L2 = L3(cv::Rect(static_cast<int>(L3peak.x - L2size / 2),
static_cast<int>(L3peak.y - L2size / 2),
L2size,
L2size));
cv::Point2d L2peak = getPeakSubpixel(L2);
cv::Point2d L2mid(L2.cols / 2, L2.rows / 2);
return L3peak - L3mid + L2peak - L2mid;
}
} // namespace
cv::Point2d
cv::phaseCorrelateIterative(InputArray _src1, InputArray _src2, int L2size, int maxIters)
{
CV_INSTRUMENT_REGION();
Mat src1 = _src1.getMat();
Mat src2 = _src2.getMat();
CV_Assert(src1.type() == src2.type());
CV_Assert(src1.size() == src2.size());
CV_Assert(src1.type() == CV_32FC1 || src1.type() == CV_64FC1);
// apply DFT window to input images
Mat window;
createHanningWindow(window, src1.size(), _src1.type());
Mat image1, image2;
multiply(_src1, window, image1);
multiply(_src2, window, image2);
// compute the DFTs of input images
dft(image1, image1, DFT_COMPLEX_OUTPUT);
dft(image2, image2, DFT_COMPLEX_OUTPUT);
// compute the phase correlation landscape L3
Mat L3 = calculateCrossPowerSpectrum(image1, image2);
dft(L3, L3, DFT_INVERSE | DFT_SCALE | DFT_REAL_OUTPUT);
fftshift(L3);
Point2d L3mid(L3.cols / 2, L3.rows / 2);
// calculate the maximum correlation location
Point2i L3peak;
minMaxLoc(L3, nullptr, nullptr, nullptr, &L3peak);
// reduce the L2size as long as L2 is out of bounds of L3
while (isOutOfBounds(L3peak, L3, L2size))
if (!reduceL2size(L2size))
return Point2d(L3peak) - L3mid;
// extract the L2 maximum correlation neighborhood from L3
Mat L2 = L3(Rect(L3peak.x - L2size / 2, L3peak.y - L2size / 2, L2size, L2size));
// upsample L2 maximum correlation neighborhood to get L2U
Mat L2U;
const int L2Usize = 223; // empirically determined optimal constant
resize(L2, L2U, {L2Usize, L2Usize}, 0, 0, INTER_LINEAR);
const Point2d L2Umid(L2U.cols / 2, L2U.rows / 2);
// run the iterative refinement algorithm using the specified L1 ratio
// gradually decrease L1 ratio if convergence is not achieved
const double L1ratioBase = 0.45; // empirically determined optimal constant
const double L1ratioStep = 0.025;
for (double L1ratio = L1ratioBase; getL1size(L2U.cols, L1ratio) > 0; L1ratio -= L1ratioStep)
{
Point2d L2Upeak = L2Umid; // reset the accumulated L2U peak position
const int L1size = getL1size(L2U.cols, L1ratio); // calculate the current L1 size
const Point2d L1mid(L1size / 2, L1size / 2); // update the L1 mid position
// perform the iterative refinement algorithm
for (int iter = 0; iter < maxIters; ++iter)
{
// verify that the L1 region is within the L2U region
if (isOutOfBounds(L2Upeak, L2U, L1size))
break;
// extract the L1 region from L2U
const Mat L1 = L2U(Rect(static_cast<int>(L2Upeak.x - L1size / 2),
static_cast<int>(L2Upeak.y - L1size / 2),
L1size,
L1size));
// calculate the centroid location
const Point2d L1peak = getPeakSubpixel(L1);
// add the contribution of the current iteration to the accumulated L2U peak location
L2Upeak += Point2d(std::round(L1peak.x - L1mid.x), std::round(L1peak.y - L1mid.y));
// check for convergence
if (accuracyReached(L1peak, L1mid))
// return the refined subpixel image shift
return Point2d(L3peak) - L3mid +
(L2Upeak - L2Umid + L1peak - L1mid) /
(static_cast<double>(L2Usize) / L2size);
}
}
// iterative refinement failed to converge, return non-iterative subpixel shift
return getSubpixelShift(L3, Point2d(L3peak), L3mid, L2size);
}
+5 -5
View File
@@ -365,7 +365,7 @@ cv::RotatedRect cv::minAreaRect( InputArray _points )
Mat hull;
Point2f out[3];
RotatedRect box;
box.angle = -(float)CV_PI / 2; // default angle for box without rotation and single point
double angle = -CV_PI / 2; // default angle for box without rotation and single point
static const bool clockwise = false;
convexHull(_points, hull, clockwise, true);
@@ -390,7 +390,7 @@ cv::RotatedRect cv::minAreaRect( InputArray _points )
if (out[1].x == 0.f && out[1].y > 0.f)
std::swap(box.size.width, box.size.height);
else
box.angle += (float)atan2( (double)out[1].y, (double)out[1].x );
angle = -atan2( (double)out[1].x, (double)out[1].y );
}
else if( n == 2 )
{
@@ -406,12 +406,12 @@ cv::RotatedRect cv::minAreaRect( InputArray _points )
}
else if (dy < 0)
{
box.angle = (float)atan2( dy, dx );
angle = atan2( dy, dx );
std::swap(box.size.width, box.size.height);
}
else if (dy > 0)
{
box.angle += (float)atan2( dy, dx );
angle = -atan2( dx, dy );
}
}
else
@@ -420,7 +420,7 @@ cv::RotatedRect cv::minAreaRect( InputArray _points )
box.center = hpoints[0];
}
box.angle = (float)(box.angle*180/CV_PI);
box.angle = (float)(angle*180/CV_PI);
CV_DbgCheckGE(box.angle, -90.0f, "");
CV_DbgCheckLT(box.angle, 0.0f, "");
return box;
+9 -3
View File
@@ -41,6 +41,7 @@ OTHER DEALINGS IN THE SOFTWARE.
#include "opencv2/core/hal/intrin.hpp"
#include <iostream>
#include <algorithm>
using namespace std;
@@ -1199,12 +1200,17 @@ void stackBlur(InputArray _src, OutputArray _dst, Size ksize)
CV_Assert( ksize.width > 0 && ksize.width % 2 == 1 &&
ksize.height > 0 && ksize.height % 2 == 1 );
int radiusH = ksize.height / 2;
int radiusW = ksize.width / 2;
int stype = _src.type(), sdepth = _src.depth();
Mat src = _src.getMat();
if (ksize.width > src.cols)
ksize.width = (src.cols % 2 == 0) ? std::max(1, src.cols - 1) : src.cols;
if (ksize.height > src.rows)
ksize.height = (src.rows % 2 == 0) ? std::max(1, src.rows - 1) : src.rows;
int radiusH = ksize.height / 2;
int radiusW = ksize.width / 2;
if (ksize.width == 1)
{
_src.copyTo(_dst);
+7 -7
View File
@@ -265,7 +265,7 @@ int Subdiv2D::newPoint(Point2f pt, bool isvirtual, int firstEdge)
{
if( freePoint == 0 )
{
vtx.push_back(Vertex());
vtx.emplace_back();
freePoint = (int)(vtx.size()-1);
}
int vidx = freePoint;
@@ -520,8 +520,8 @@ void Subdiv2D::initDelaunay( Rect rect )
Point2f ppB( rx, ry + big_coord );
Point2f ppC( rx - big_coord, ry - big_coord );
vtx.push_back(Vertex());
qedges.push_back(QuadEdge());
vtx.emplace_back();
qedges.emplace_back();
freeQEdge = 0;
freePoint = 0;
@@ -566,8 +566,8 @@ void Subdiv2D::initDelaunay( Rect2f rect )
Point2f ppB( rx, ry + big_coord );
Point2f ppC( rx - big_coord, ry - big_coord );
vtx.push_back(Vertex());
qedges.push_back(QuadEdge());
vtx.emplace_back();
qedges.emplace_back();
freeQEdge = 0;
freePoint = 0;
@@ -784,7 +784,7 @@ void Subdiv2D::getEdgeList(std::vector<Vec4f>& edgeList) const
{
Point2f org = vtx[qedges[i].pt[0]].pt;
Point2f dst = vtx[qedges[i].pt[2]].pt;
edgeList.push_back(Vec4f(org.x, org.y, dst.x, dst.y));
edgeList.emplace_back(org.x, org.y, dst.x, dst.y);
}
}
}
@@ -836,7 +836,7 @@ void Subdiv2D::getTriangleList(std::vector<Vec6f>& triangleList) const
edgemask[edge_a] = true;
edgemask[edge_b] = true;
edgemask[edge_c] = true;
triangleList.push_back(Vec6f(a.x, a.y, b.x, b.y, c.x, c.y));
triangleList.emplace_back(a.x, a.y, b.x, b.y, c.x, c.y);
}
}
+13
View File
@@ -76,6 +76,19 @@ TEST(Imgproc_ApproxPoly, bad_epsilon)
ASSERT_ANY_THROW(approxPolyDP(inputPoints, outputPoints, eps, false));
}
TEST(Imgproc_ApproxPoly, distace_between_point_and_segment)
{
vector<Point2f> inputPoints = {
{ {0.f, 0.f}, {4.f, 2.f}, {11.f, 1.f}, {8.f, 0.f} }
};
std::vector<Point2f> result;
approxPolyDP(inputPoints, result, 1.9, false);
vector<Point2f> expectedResult = {
{ {0.f, 0.f}, {11.f, 1.f}, {8.f, 0.f} }
};
ASSERT_EQ(result, expectedResult);
}
struct ApproxPolyN: public testing::Test
{
void SetUp()
@@ -291,4 +291,33 @@ namespace opencv_test { namespace {
test.safe_run();
}
// Regression test for issue #28254
// Out-of-bounds read in AVX2 bilateralFilter 32f path with BORDER_CONSTANT
TEST(Imgproc_BilateralFilter, regression_28254_oob_read)
{
// Create a 64x64 CV_32FC1 image with values in range [100, 200]
// Image must be large enough (width >= 32) to trigger SIMD/AVX2 code path.
// Values are set so BORDER_CONSTANT padding (default 0) is outside the range,
// which triggers the out-of-bounds condition in the LUT access.
cv::Mat src(64, 64, CV_32FC1);
cv::randu(src, 100.0f, 200.0f);
cv::Mat dst;
// Parameters that trigger the bug
int d = -1;
double sigmaColor = 2.7;
double sigmaSpace = 44.5;
int borderType = cv::BORDER_CONSTANT;
// This should not crash or trigger AddressSanitizer
EXPECT_NO_THROW(
cv::bilateralFilter(src, dst, d, sigmaColor, sigmaSpace, borderType)
);
// Verify output is valid
EXPECT_FALSE(dst.empty());
EXPECT_EQ(dst.size(), src.size());
EXPECT_EQ(dst.type(), src.type());
}
}} // namespace
+15
View File
@@ -233,5 +233,20 @@ TEST(Imgproc_DrawContours, MatListOfMatIntScalarInt)
EXPECT_EQ(nz, 0);
}
TEST(Imgproc_Moments, degenerateContours)
{
std::vector<cv::Point> c1;
c1.push_back(cv::Point(10,10));
cv::Moments m1 = cv::moments(c1, false);
EXPECT_EQ(m1.m00, 0);
std::vector<cv::Point> c2;
c2.push_back(cv::Point(0,0));
c2.push_back(cv::Point(5,5));
c2.push_back(cv::Point(10,10));
cv::Moments m2 = cv::moments(c2, false);
EXPECT_EQ(m2.m00, 0);
}
}} // namespace
/* End of file. */
+56 -2
View File
@@ -304,9 +304,11 @@ TEST(Imgproc_ConvexityDefects, ordering_4539)
vector<int> hull_ind;
vector<Vec4i> defects;
#if 0 // deprecated behavior
// first, check the original contour as-is, without intermediate fillPoly/drawContours.
convexHull(contour_, hull_ind, false, false);
EXPECT_THROW( convexityDefects(contour_, hull_ind, defects), cv::Exception );
#endif
int scale = 20;
contour_ *= (double)scale;
@@ -319,10 +321,12 @@ TEST(Imgproc_ConvexityDefects, ordering_4539)
findContours(canvas_gray, contours, noArray(), RETR_LIST, CHAIN_APPROX_SIMPLE);
convexHull(contours[0], hull_ind, false, false);
#if 0 // deprecated behavior
// the original contour contains self-intersections,
// therefore convexHull does not return a monotonous sequence of points
// and therefore convexityDefects throws an exception
EXPECT_THROW( convexityDefects(contours[0], hull_ind, defects), cv::Exception );
#endif
#if 1
// one way to eliminate the contour self-intersection in this particular case is to apply dilate(),
@@ -1049,7 +1053,7 @@ TEST_P(minEnclosingTriangle_Modes, accuracy)
const Mat midPoint = (cur + next) / 2;
EXPECT_TRUE(isPointOnHull(hull, midPoint));
// at least one of hull edges must be on tirangle edge
// at least one of hull edges must be on triangle edge
hasEdgeOnHull = hasEdgeOnHull || isEdgeOnHull(hull, cur, next);
}
EXPECT_TRUE(hasEdgeOnHull);
@@ -1279,10 +1283,60 @@ INSTANTIATE_TEST_CASE_P(Imgproc, minAreaRect_of_line,
testing::Values(
std::make_tuple(Point2f(10, 15), Point2f(10, 25), Point2f(10, 20), Size2f(10, 0), -90.f),
std::make_tuple(Point2f(450, 500), Point2f(508, 500), Point2f(479, 500), Size2f(0, 58), -90.f),
std::make_tuple(Point2f(10, 20), Point2f(13, 16), Point2f(11.5, 18), Size2f(5, 0), -53.1301002f),
std::make_tuple(Point2f(10, 20), Point2f(13, 16), Point2f(11.5, 18), Size2f(5, 0), -53.1301041f),
std::make_tuple(Point2f(9, 19), Point2f(4, 7), Point2f(6.5, 13), Size2f(0, 13), -22.6198654f)
));
typedef testing::TestWithParam<tuple<tuple<std::vector<Point>, Mat>, bool> > convexHull_monotonous;
TEST_P(convexHull_monotonous, self_intersecting_contour)
{
std::vector<Point> contour = get<0>(get<0>(GetParam()));
Mat ref = get<1>(get<0>(GetParam())).clone();
bool clockwise = get<1>(GetParam());
if (!clockwise)
{
std::reverse(ref.begin<int>(), ref.end<int>());
}
Mat indices;
convexHull(contour, indices, clockwise, false);
Point minLoc;
minMaxLoc(indices, nullptr, nullptr, &minLoc);
std::rotate(indices.begin<int>(), indices.begin<int>() + minLoc.y, indices.end<int>());
minMaxLoc(ref, nullptr, nullptr, &minLoc);
std::rotate(ref.begin<int>(), ref.begin<int>() + minLoc.y, ref.end<int>());
ASSERT_EQ( cvtest::norm(indices, ref, NORM_INF), 0) << indices;
}
INSTANTIATE_TEST_CASE_P(Imgproc, convexHull_monotonous,
testing::Combine(
testing::Values(
std::make_tuple(
std::vector<Point>{
Point(3, 2), Point(3, 4), Point(2, 5), Point(1, 5),
Point(2, 5), Point(3, 4), Point(6, 4), Point(6, 2)
},
(Mat_<int>(5, 1) << 0, 3, 4, 6, 7)
),
std::make_tuple(
std::vector<Point>{
Point(3, -2), Point(3, -4), Point(2, -5), Point(1, -5),
Point(2, -5), Point(3, -4), Point(6, -4), Point(6, -2)
},
(Mat_<int>(5, 1) << 3, 0, 7, 6, 4)
),
std::make_tuple(
std::vector<Point>{
Point(1, 1), Point(1, 0), Point(0, 0), Point(1, 0), Point(0, 1)
},
(Mat_<int>(4, 1) << 0, 1, 2, 4)
)
),
testing::Bool()
));
}} // namespace
/* End of file. */
+24
View File
@@ -1281,4 +1281,28 @@ TEST(Drawing, contours_filled)
}
}
// Test for LINE_4 vs LINE_8 connectivity behavior
// Regression test for issue #26413
TEST(Drawing, line_connectivity_regression_26413)
{
Mat img4(10, 10, CV_8UC1, Scalar(0));
Mat img8(10, 10, CV_8UC1, Scalar(0));
// Draw a diagonal line from (0,0) to (9,9)
// LINE_4 (4-connected) should produce staircase pattern (no diagonals)
// LINE_8 (8-connected) should produce diagonal steps
line(img4, Point(0, 0), Point(9, 9), Scalar(255), 1, LINE_4);
line(img8, Point(0, 0), Point(9, 9), Scalar(255), 1, LINE_8);
int count4 = countNonZero(img4);
int count8 = countNonZero(img8);
// LINE_8 for a 10-pixel diagonal should have exactly 10 pixels
EXPECT_EQ(10, count8) << "LINE_8 diagonal from (0,0) to (9,9) should have 10 pixels";
// LINE_4 for a 10-pixel diagonal should have approximately 19 pixels
// (needs both horizontal and vertical steps)
EXPECT_GT(count4, 15) << "LINE_4 diagonal should have significantly more pixels due to staircase";
}
}} // namespace
@@ -509,7 +509,8 @@ int CV_GoodFeatureToTTest::validate_test_results( int test_case_idx )
EXPECT_LE(e, eps); // never true
ts->set_failed_test_info(cvtest::TS::FAIL_BAD_ACCURACY);
for(int i = 0; i < (int)std::min((unsigned int)(cornersQuality.size()), (unsigned int)(cornersQuality.size())); i++) {
int min_size = (int)std::min(cornersQuality.size(), RefcornersQuality.size());
for(int i = 0; i < min_size; i++) {
if (std::abs(cornersQuality[i] - RefcornersQuality[i]) > eps * std::max(cornersQuality[i], RefcornersQuality[i]))
printf("i = %i Quality %2.6f Quality ref %2.6f\n", i, cornersQuality[i], RefcornersQuality[i]);
}
+117
View File
@@ -0,0 +1,117 @@
// 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"
#include <vector>
namespace opencv_test { namespace {
Mat CropMid(InputArray src, int w, int h)
{
Mat mat = src.getMat();
return mat(Rect(mat.cols / 2 - w / 2, mat.rows / 2 - h / 2, w, h));
}
Mat GenerateTestImage(Size size)
{
Mat image = Mat::zeros(size.height * 2, size.width * 2, CV_32F);
rectangle(image,
Point(static_cast<int>(size.width * 0.1), static_cast<int>(size.height * 0.1)),
Point(static_cast<int>(size.width * 0.9), static_cast<int>(size.height * 0.9)),
Scalar(1),
-1);
return image;
}
void TestPhaseCorrelationIterative(const Size& size, const double maxShift)
{
const auto iters = std::max(201., maxShift * 10 + 1);
const Point2d shiftOffset(-maxShift * 0.5, -maxShift * 0.5);
Mat image1 = GenerateTestImage(size);
Mat crop1 = CropMid(image1, size.width, size.height);
Mat image2 = image1.clone();
std::vector<double> pcErrors;
std::vector<double> ipcErrors;
for (int i = 0; i < iters; ++i)
{
const auto shift =
Point2d(maxShift * i / (iters - 1), maxShift * i / (iters - 1)) + shiftOffset;
const Mat Tmat = (Mat_<double>(2, 3) << 1., 0., shift.x, 0., 1., shift.y);
warpAffine(image1, image2, Tmat, image2.size());
Mat crop2 = CropMid(image2, size.width, size.height);
const auto ipcshift = phaseCorrelateIterative(crop1, crop2);
const auto pcshift = phaseCorrelate(crop1, crop2);
pcErrors.push_back(
0.5 * std::abs(pcshift.x - shift.y) + 0.5 * std::abs(pcshift.y - shift.x));
ipcErrors.push_back(
0.5 * std::abs(ipcshift.x - shift.y) + 0.5 * std::abs(ipcshift.y - shift.x));
// error should be low
EXPECT_NEAR(ipcshift.x - shift.x, 0.0, 0.1);
EXPECT_NEAR(ipcshift.y - shift.y, 0.0, 0.1);
}
cv::Scalar pcMean, pcStddev, ipcMean, ipcStddev;
meanStdDev(ipcErrors, ipcMean, ipcStddev);
meanStdDev(pcErrors, pcMean, pcStddev);
// average error should be low
ASSERT_LT(ipcMean[0], 0.03);
// average error should be less than non-iterative average error
ASSERT_LT(ipcMean[0], pcMean[0]);
// error stddev should be less than non-iterative error stddev
ASSERT_LT(ipcStddev[0], pcStddev[0]);
}
TEST(Imgproc_PhaseCorrelationIterative, 256x128_accuracy)
{
TestPhaseCorrelationIterative(Size(256, 128), 1);
}
TEST(Imgproc_PhaseCorrelationIterative, 64x64_accuracy_shift_1)
{
TestPhaseCorrelationIterative(Size(64, 64), 1);
}
TEST(Imgproc_PhaseCorrelationIterative, 64x64_accuracy_shift_16)
{
TestPhaseCorrelationIterative(Size(64, 64), 16);
}
TEST(Imgproc_PhaseCorrelationIterative, 0x0_image)
{
ASSERT_ANY_THROW(TestPhaseCorrelationIterative(Size(0, 0), 1));
}
TEST(Imgproc_PhaseCorrelationIterative, 1x1_image)
{
ASSERT_ANY_THROW(TestPhaseCorrelationIterative(Size(1, 1), 1));
}
TEST(Imgproc_PhaseCorrelationIterative, accuracy_real_img)
{
Mat img = imread(cvtest::TS::ptr()->get_data_path() + "shared/airplane.png", IMREAD_GRAYSCALE);
if (img.empty())
return;
img.convertTo(img, CV_64FC1);
const int xLen = 256;
const int yLen = 256;
const int xShift = 40;
const int yShift = 14;
Mat roi1 = img(Rect(xShift, yShift, xLen, yLen));
Mat roi2 = img(Rect(0, 0, xLen, yLen));
const Point2d ipcShift = phaseCorrelateIterative(roi1, roi2);
ASSERT_NEAR(ipcShift.x, (double)xShift, 1.);
ASSERT_NEAR(ipcShift.y, (double)yShift, 1.);
}
}} // namespace opencv_test
+13
View File
@@ -311,5 +311,18 @@ TEST_P(StackBlur_GaussianBlur, compare)
}
INSTANTIATE_TEST_CASE_P(Imgproc, StackBlur_GaussianBlur, testing::Values(CV_8U, CV_16S, CV_16U, CV_32F));
TEST(Imgproc_StackBlur, regression_28233)
{
Mat src1(1, 1, CV_8UC1, Scalar(123));
Mat dst1;
EXPECT_NO_THROW(stackBlur(src1, dst1, Size(9, 1)));
EXPECT_EQ(dst1.at<uchar>(0, 0), 123);
Mat src2(3, 3, CV_8UC1, Scalar(50));
Mat dst2;
EXPECT_NO_THROW(stackBlur(src2, dst2, Size(11, 11)));
EXPECT_EQ(dst2.at<uchar>(1, 1), 50);
}
}
}
+20 -1
View File
@@ -62,6 +62,24 @@ ocv_bindings_generator_populate_preprocessor_definitions(
opencv_preprocessor_defs
)
if(OPENCV_JAVA_CLEANING_API)
if(OPENCV_JAVA_CLEANING_API STREQUAL "finalize")
set(opencv_supported_cleaners "false")
elseif(OPENCV_JAVA_CLEANING_API STREQUAL "cleaner")
set(opencv_supported_cleaners "true")
else()
message(FATAL_ERROR "OPENCV_JAVA_CLEANING_API should be one of \"finalize\" or \"cleaner\"")
endif()
else()
if(ANDROID OR (Java_VERSION VERSION_LESS 9))
message(STATUS "Set Cleaners to False")
set(opencv_supported_cleaners "false")
else()
message(STATUS "Set Cleaners to True")
set(opencv_supported_cleaners "true")
endif()
endif(OPENCV_JAVA_CLEANING_API)
set(CONFIG_FILE "${CMAKE_CURRENT_BINARY_DIR}/gen_java.json")
set(__config_str
"{
@@ -74,7 +92,8 @@ ${opencv_preprocessor_defs}
},
\"files_remap\": [
${__remap_config}
]
],
\"support_cleaners\": ${opencv_supported_cleaners}
}
")
if(EXISTS "${CONFIG_FILE}")
+25 -4
View File
@@ -23,6 +23,7 @@ SCRIPT_DIR = os.path.dirname(os.path.abspath(__file__))
# list of modules + files remap
config = None
ROOT_DIR = None
USE_CLEANERS = True
FILES_REMAP = {}
def checkFileRemap(path):
path = os.path.realpath(path)
@@ -377,6 +378,7 @@ class ClassInfo(GeneralInfo):
jmodule = make_jmodule(m),
name = self.name,
jname = self.jname,
jcleaner = "long nativeObjCopy = nativeObj;\n org.opencv.core.Mat.cleaner.register(this, () -> delete(nativeObjCopy));" if USE_CLEANERS else "",
imports = "\n".join(self.getAllImports(M)),
docs = self.docstring,
annotation = "\n" + "\n".join(self.annotation) if self.annotation else "",
@@ -961,6 +963,7 @@ class JavaWrapperGenerator(object):
tail = ")"
else:
ret_val = "nativeObj = "
tail = ";\n long nativeObjCopy = nativeObj;\n org.opencv.core.Mat.cleaner.register(this, () -> delete(nativeObjCopy))" if USE_CLEANERS else ""
ret = ""
elif self.isWrapped(ret_type): # wrapped class
constructor = self.getClass(ret_type).jname + "("
@@ -1228,8 +1231,9 @@ JNIEXPORT $rtype JNICALL Java_org_opencv_${jmodule}_${clazz}_$fname
ci.cpp_code.write("\n".join(fn["cpp_code"]))
if ci.name != self.Module or ci.base:
# finalize()
ci.j_code.write(
# finalize() for old Java
if not USE_CLEANERS:
ci.j_code.write(
"""
@Override
protected void finalize() throws Throwable {
@@ -1239,7 +1243,7 @@ JNIEXPORT $rtype JNICALL Java_org_opencv_${jmodule}_${clazz}_$fname
ci.jn_code.write(
"""
// native support for java finalize()
// native support for java finalize() or cleaner
private static native void delete(long nativeObj);
""" )
@@ -1247,7 +1251,7 @@ JNIEXPORT $rtype JNICALL Java_org_opencv_${jmodule}_${clazz}_$fname
ci.cpp_code.write(
"""
//
// native support for java finalize()
// native support for java finalize() or cleaner
// static void %(cls)s::delete( __int64 self )
//
JNIEXPORT void JNICALL Java_org_opencv_%(module)s_%(j_cls)s_delete(JNIEnv*, jclass, jlong);
@@ -1468,6 +1472,12 @@ if __name__ == "__main__":
FILES_REMAP = { os.path.realpath(os.path.join(ROOT_DIR, f['src'])): f['target'] for f in config['files_remap'] }
logging.info("\nRemapped configured files (%d):\n%s", len(FILES_REMAP), pformat(FILES_REMAP))
USE_CLEANERS = config['support_cleaners']
if (USE_CLEANERS):
logging.info("\nUse Java 9+ cleaners\n")
else:
logging.info("\nUse old style Java finalize()\n")
dstdir = "./gen"
jni_path = os.path.join(dstdir, 'cpp'); mkdir_p(jni_path)
java_base_path = os.path.join(dstdir, 'java'); mkdir_p(java_base_path)
@@ -1557,6 +1567,17 @@ if __name__ == "__main__":
preprocessor_definitions)
else:
logging.info("No generated code for module: %s", module)
# Copy Cleaner / finalize() related files
if USE_CLEANERS:
cleaner_src = os.path.join(SCRIPT_DIR, "src", "java9", "CleanableMat.java")
else:
cleaner_src = os.path.join(SCRIPT_DIR, "src", "java_classic", "CleanableMat.java")
cleaner_dst = os.path.join(java_base_path, "org", "opencv", "core", "CleanableMat.java")
print("cleaner_dst: ", cleaner_dst)
copyfile(cleaner_src, cleaner_dst)
generator.finalize(jni_path)
print('Generated files: %d (updated %d)' % (total_files, updated_files))
@@ -0,0 +1,28 @@
// This file is part of OpenCV project.
// It is subject to the license terms in the LICENSE file found in the top-level directory
// of this distribution and at http://opencv.org/license.html
#include "opencv2/core.hpp"
#define LOG_TAG "org.opencv.core.CleanableMat"
#include "common.h"
#include <iostream>
using namespace cv;
extern "C" {
//
// native support for java finalize() or cleaners
// static void CleanableMat::n_delete( __int64 self )
//
JNIEXPORT void JNICALL Java_org_opencv_core_CleanableMat_n_1delete
(JNIEnv*, jclass, jlong self);
JNIEXPORT void JNICALL Java_org_opencv_core_CleanableMat_n_1delete
(JNIEnv*, jclass, jlong self)
{
// LOGD("CleanableMat.n_delete() called\n");
delete (Mat*) self;
}
}
-16
View File
@@ -2114,22 +2114,6 @@ JNIEXPORT jlong JNICALL Java_org_opencv_core_Mat_n_1zeros__I_3II
return 0;
}
//
// native support for java finalize()
// static void Mat::n_delete( __int64 self )
//
JNIEXPORT void JNICALL Java_org_opencv_core_Mat_n_1delete
(JNIEnv*, jclass, jlong self);
JNIEXPORT void JNICALL Java_org_opencv_core_Mat_n_1delete
(JNIEnv*, jclass, jlong self)
{
delete (Mat*) self;
}
} // extern "C"
namespace {
@@ -0,0 +1,23 @@
package org.opencv.core;
import java.lang.ref.Cleaner;
public abstract class CleanableMat {
// A native memory cleaner for the OpenCV library
public static Cleaner cleaner = Cleaner.create();
protected CleanableMat(long obj) {
if (obj == 0)
throw new UnsupportedOperationException("Native object address is NULL");
nativeObj = obj;
// The n_delete action must not refer to the object being registered. So, do not use nativeObj directly.
long nativeObjCopy = nativeObj;
cleaner.register(this, () -> n_delete(nativeObjCopy));
}
private static native void n_delete(long nativeObj);
public final long nativeObj;
}
@@ -0,0 +1,21 @@
package org.opencv.core;
public abstract class CleanableMat {
protected CleanableMat(long obj) {
if (obj == 0)
throw new UnsupportedOperationException("Native object address is NULL");
nativeObj = obj;
}
@Override
protected void finalize() throws Throwable {
n_delete(nativeObj);
super.finalize();
}
private static native void n_delete(long nativeObj);
public final long nativeObj;
}
@@ -9,7 +9,10 @@ $docs$annotation
public class $jname {
protected final long nativeObj;
protected $jname(long addr) { nativeObj = addr; }
protected $jname(long addr) {
nativeObj = addr;
$jcleaner
}
public long getNativeObjAddr() { return nativeObj; }
+12
View File
@@ -74,6 +74,18 @@ else()
endif()
endif()
# Embind requires C++17 or newer from Emscripten 4.0.20.
# See https://github.com/emscripten-core/emscripten/blob/main/ChangeLog.md#4020---111825
if(EMSCRIPTEN_VERSION VERSION_GREATER_EQUAL "4.0.20")
if(NOT CMAKE_CXX_STANDARD OR CMAKE_CXX_STANDARD STREQUAL "98"
OR CMAKE_CXX_STANDARD STREQUAL "11" OR CMAKE_CXX_STANDARD STREQUAL "14")
message(FATAL_ERROR "[OpenCV.js Build Error] "
"Emscripten ${EMSCRIPTEN_VERSION} requires C++17 or newer for Embind, "
"but CMAKE_CXX_STANDARD is set to '${CMAKE_CXX_STANDARD}'.\n"
"Please re-run emcmake with --cmake_option=\"-DCMAKE_CXX_STANDARD=17\"\n")
endif()
endif()
ocv_add_module(js BINDINGS PRIVATE_REQUIRED opencv_js_bindings_generator)
+1 -1
View File
@@ -50,7 +50,7 @@ if(DEFINED ENV{OPENCV_JS_WHITELIST})
else()
#generate white list from modules/<module_name>/misc/js/whitelist.json
set(OPENCV_JS_WHITELIST_FILE "${CMAKE_CURRENT_BINARY_DIR}/whitelist.json")
foreach(m in ${OPENCV_JS_MODULES})
foreach(m IN LISTS OPENCV_JS_MODULES)
set(js_whitelist "${OPENCV_MODULE_${m}_LOCATION}/misc/js/gen_dict.json")
if (EXISTS "${js_whitelist}")
file(READ "${js_whitelist}" whitelist_content)
+17 -4
View File
@@ -523,13 +523,20 @@ class JSWrapperGenerator(object):
# Return type
ret_type = 'void' if variant.rettype.strip() == '' else variant.rettype
if ret_type.startswith('Ptr'): #smart pointer
# FIX: Ensure namespaced smart-pointer return types in factory methods, e.g.:
# Ptr<EdgeDrawing> → Ptr<cv::ximgproc::EdgeDrawing>
if factory and class_info is not None and ret_type.startswith('Ptr<'):
inner = ret_type[len('Ptr<'):-1].strip()
if '::' not in inner and inner == class_info.name:
ret_type = 'Ptr<%s>' % class_info.cname
if ret_type.startswith('Ptr'): # smart pointer
ptr_type = ret_type.replace('Ptr<', '').replace('>', '')
if ptr_type in type_dict:
ret_type = type_dict[ptr_type]
for key in type_dict:
if key in ret_type:
ret_type = re.sub(r"\b" + key + r"\b", type_dict[key], ret_type)
for key in type_dict:
if key in ret_type:
ret_type = re.sub(r"\b" + key + r"\b", type_dict[key], ret_type)
arg_types = []
unwrapped_arg_types = []
for arg in variant.args:
@@ -708,6 +715,12 @@ class JSWrapperGenerator(object):
ret_type = 'void' if variant.rettype.strip() == '' else variant.rettype
ret_type = ret_type.strip()
# Same namespace fix for factory methods: Ptr<EdgeDrawing> -> Ptr<cv::ximgproc::EdgeDrawing>
if factory and class_info is not None and ret_type.startswith('Ptr<'):
inner = ret_type[len('Ptr<'):-1].strip()
if '::' not in inner and inner == class_info.name:
ret_type = 'Ptr<%s>' % class_info.cname
if ret_type.startswith('Ptr'): #smart pointer
ptr_type = ret_type.replace('Ptr<', '').replace('>', '')
if ptr_type in type_dict:
+16
View File
@@ -410,3 +410,19 @@ if (
if (cv.UMat) cv.UMat.prototype[Symbol.dispose] = cv.UMat.prototype.delete;
// Add more as OpenCV gains new manual-cleanup classes
}
// Override Emscripten's shallow clone() with OpenCV's deep copy mat_clone()
// This restores the expected behavior where clone() performs a deep copy.
// Background: Emscripten 3.1.71+ added ClassHandle.clone() which only does shallow copy.
// See: https://github.com/opencv/opencv/pull/26643
// See: https://github.com/opencv/opencv/issues/27572
var _opencv_onRuntimeInitialized_backup = Module['onRuntimeInitialized'];
Module['onRuntimeInitialized'] = function() {
if (_opencv_onRuntimeInitialized_backup) {
_opencv_onRuntimeInitialized_backup();
}
if (typeof cv !== 'undefined' && cv.Mat &&
typeof cv.Mat.prototype.mat_clone === 'function') {
cv.Mat.prototype.clone = cv.Mat.prototype.mat_clone;
}
};
+1 -1
View File
@@ -552,7 +552,7 @@ QUnit.test('test_filter', function(assert) {
cv.stackBlur(src, src, new cv.Size(3, 3));
// Verify result.
let expected = new Uint8Array([22,29,36,38,43,50]);
let expected = new Uint8Array([14,22,29,46,51,58]);
assert.deepEqual(src.data, expected);
src.delete();
+1 -1
View File
@@ -173,7 +173,7 @@ QUnit.test('test_mat_creation', function(assert) {
// clone
{
let mat = cv.Mat.ones(5, 5, cv.CV_8UC1);
let mat2 = mat.mat_clone();
let mat2 = mat.clone();
assert.equal(mat.channels, mat2.channels);
assert.equal(mat.size().height, mat2.size().height);
@@ -318,6 +318,33 @@ public:
CV_WRAP void detectMarkers(InputArray image, OutputArrayOfArrays corners, OutputArray ids,
OutputArrayOfArrays rejectedImgPoints = noArray()) const;
/** @brief Marker detection with confidence computation
*
* @param image input image
* @param corners vector of detected marker corners. For each marker, its four corners
* are provided, (e.g std::vector<std::vector<cv::Point2f> > ). For N detected markers,
* the dimensions of this array is Nx4. The order of the corners is clockwise.
* @param ids vector of identifiers of the detected markers. The identifier is of type int
* (e.g. std::vector<int>). For N detected markers, the size of ids is also N.
* The identifiers have the same order than the markers in the imgPoints array.
* @param markersConfidence contains the normalized confidence [0;1] of the markers' detection,
* defined as 1 minus the normalized uncertainty (percentage of incorrect pixel detections),
* with 1 describing a pixel perfect detection. The confidence values are of type float
* (e.g. std::vector<float>)
* @param rejectedImgPoints contains the imgPoints of those squares whose inner code has not a
* correct codification. Useful for debugging purposes.
*
* Performs marker detection in the input image. Only markers included in the first specified dictionary
* are searched. For each detected marker, it returns the 2D position of its corner in the image
* and its corresponding identifier.
* Note that this function does not perform pose estimation.
* @note The function does not correct lens distortion or takes it into account. It's recommended to undistort
* input image with corresponding camera model, if camera parameters are known
* @sa undistort, estimatePoseSingleMarkers, estimatePoseBoard
*/
CV_WRAP void detectMarkersWithConfidence(InputArray image, OutputArrayOfArrays corners, OutputArray ids, OutputArray markersConfidence,
OutputArrayOfArrays rejectedImgPoints = noArray()) const;
/** @brief Refine not detected markers based on the already detected and the board layout
*
* @param image input image
@@ -71,7 +71,6 @@ class CV_EXPORTS_W_SIMPLE Dictionary {
*/
CV_WRAP int getDistanceToId(InputArray bits, int id, bool allRotations = true) const;
/** @brief Generate a canonical marker image
*/
CV_WRAP void generateImageMarker(int id, int sidePixels, OutputArray _img, int borderBits = 1) const;
@@ -84,7 +83,7 @@ class CV_EXPORTS_W_SIMPLE Dictionary {
/** @brief Transform list of bytes to matrix of bits
*/
CV_WRAP static Mat getBitsFromByteList(const Mat &byteList, int markerSize);
CV_WRAP static Mat getBitsFromByteList(const Mat &byteList, int markerSize, int rotationId = 0);
};
@@ -62,7 +62,7 @@ public:
/**
* @brief detect aruco markers and interpolate position of ChArUco board corners
* @param image input image necesary for corner refinement. Note that markers are not detected and
* @param image input image necessary for corner refinement. Note that markers are not detected and
* should be sent in corners and ids parameters.
* @param charucoCorners interpolated chessboard corners.
* @param charucoIds interpolated chessboard corners identifiers.
@@ -18,6 +18,7 @@
#include "../../precomp.hpp"
#include "apriltag_quad_thresh.hpp"
#include "unionfind.hpp"
//#define APRIL_DEBUG
#ifdef APRIL_DEBUG
@@ -93,7 +94,7 @@ void ptsort_(struct pt *pts, int sz)
// a merge sort with temp storage.
// Use stack storage if it's not too big.
cv::AutoBuffer<struct pt, 1024> _tmp_stack(sz);
AutoBuffer<struct pt, 1024> _tmp_stack(sz);
memcpy(_tmp_stack.data(), pts, sizeof(struct pt) * sz);
int asz = sz/2;
@@ -571,31 +572,6 @@ int quad_segment_agg(int sz, struct line_fit_pt *lfps, int indices[4]){
return 1;
}
#define DO_UNIONFIND(dx, dy) if (im.data[y*s + dy*s + x + dx] == v) unionfind_connect(uf, y*w + x, y*w + dy*w + x + dx);
static void do_unionfind_line(unionfind_t *uf, Mat &im, int w, int s, int y){
CV_Assert(y+1 < im.rows);
CV_Assert(!im.empty());
for (int x = 1; x < w - 1; x++) {
uint8_t v = im.data[y*s + x];
if (v == 127)
continue;
// (dx,dy) pairs for 8 connectivity:
// (REFERENCE) (1, 0)
// (-1, 1) (0, 1) (1, 1)
//
DO_UNIONFIND(1, 0);
DO_UNIONFIND(0, 1);
if (v == 255) {
DO_UNIONFIND(-1, 1);
DO_UNIONFIND(1, 1);
}
}
}
#undef DO_UNIONFIND
/**
* return 1 if the quad looks okay, 0 if it should be discarded
* quad
@@ -1309,11 +1285,11 @@ zarray_t *apriltag_quad_thresh(const DetectorParameters &parameters, const Mat &
////////////////////////////////////////////////////////
// step 2. find connected components.
unionfind_t *uf = unionfind_create(w * h);
UnionFind uf(w * h);
// TODO PARALLELIZE
for (int y = 0; y < h - 1; y++) {
do_unionfind_line(uf, thold, w, ts, y);
uf.do_line(thold, w, ts, y);
}
// XXX sizing??
@@ -1329,7 +1305,7 @@ zarray_t *apriltag_quad_thresh(const DetectorParameters &parameters, const Mat &
continue;
// XXX don't query this until we know we need it?
uint64_t rep0 = unionfind_get_representative(uf, y*w + x);
uint64_t rep0 = uf.get_representative(y*w + x);
// whenever we find two adjacent pixels such that one is
// white and the other black, we add the point half-way
@@ -1356,7 +1332,7 @@ zarray_t *apriltag_quad_thresh(const DetectorParameters &parameters, const Mat &
uint8_t v1 = thold.data[y*ts + dy*ts + x + dx]; \
\
if (v0 + v1 == 255) { \
uint64_t rep1 = unionfind_get_representative(uf, y*w + dy*w + x + dx); \
uint64_t rep1 = uf.get_representative(y*w + dy*w + x + dx); \
uint64_t clusterid; \
if (rep0 < rep1) \
clusterid = (rep1 << 32) + rep0; \
@@ -1405,9 +1381,9 @@ uint32_t *colors = (uint32_t*) calloc(w*h, sizeof(*colors));
for (int y = 0; y < h; y++) {
for (int x = 0; x < w; x++) {
uint32_t v = unionfind_get_representative(uf, y*w+x);
uint32_t v = uf.get_representative(y*w+x);
if (unionfind_get_set_size(uf, v) < parameters->aprilTagMinClusterPixels)
if (uf.get_set_size(v) < parameters->aprilTagMinClusterPixels)
continue;
uint32_t color = colors[v];
@@ -1533,8 +1509,6 @@ for (int i = 0; i < _zarray_size(quads); i++) {
imwrite("2.5 debug_lines.pnm", out);
#endif
unionfind_destroy(uf);
for (int i = 0; i < _zarray_size(clusters); i++) {
zarray_t *cluster;
_zarray_get(clusters, i, &cluster);
@@ -19,7 +19,6 @@
#ifndef _OPENCV_APRIL_QUAD_THRESH_HPP_
#define _OPENCV_APRIL_QUAD_THRESH_HPP_
#include "unionfind.hpp"
#include "zmaxheap.hpp"
#include "zarray.hpp"
+107 -107
View File
@@ -17,115 +17,115 @@
namespace cv {
namespace aruco {
typedef struct unionfind unionfind_t;
struct unionfind{
struct UnionFind {
UnionFind(uint32_t _maxid)
{
maxid = _maxid;
data.resize(maxid+1);
for (unsigned int i = 0; i <= maxid; i++) {
data[i].size = 1;
data[i].parent = i;
}
};
inline uint32_t get_representative(uint32_t id) {
uint32_t root = id;
// chase down the root
while (data[root].parent != root) {
root = data[root].parent;
}
// go back and collapse the tree.
//
// XXX: on some of our workloads that have very shallow trees
// (e.g. image segmentation), we are actually faster not doing
// this...
while (data[id].parent != root) {
uint32_t tmp = data[id].parent;
data[id].parent = root;
id = tmp;
}
return root;
}
inline uint32_t get_set_size(uint32_t id) {
uint32_t repid = get_representative(id);
return data[repid].size;
}
inline uint32_t connect(uint32_t aid, uint32_t bid) {
uint32_t aroot = get_representative(aid);
uint32_t broot = get_representative(bid);
if (aroot == broot)
return aroot;
// we don't perform "union by rank", but we perform a similar
// operation (but probably without the same asymptotic guarantee):
// We join trees based on the number of *elements* (as opposed to
// rank) contained within each tree. I.e., we use size as a proxy
// for rank. In my testing, it's often *faster* to use size than
// rank, perhaps because the rank of the tree isn't that critical
// if there are very few nodes in it.
uint32_t asize = data[aroot].size;
uint32_t bsize = data[broot].size;
// optimization idea: We could shortcut some or all of the tree
// that is grafted onto the other tree. Pro: those nodes were just
// read and so are probably in cache. Con: it might end up being
// wasted effort -- the tree might be grafted onto another tree in
// a moment!
if (asize > bsize) {
data[broot].parent = aroot;
data[aroot].size += bsize;
return aroot;
} else {
data[aroot].parent = broot;
data[broot].size += asize;
return broot;
}
}
#define DO_UNIONFIND(dx, dy) if (im.data[y*s + dy*s + x + dx] == v) connect(y*w + x, y*w + dy*w + x + dx);
void do_line(Mat &im, int w, int s, int y) {
CV_Assert(y+1 < im.rows);
CV_Assert(!im.empty());
for (int x = 1; x < w - 1; x++) {
uint8_t v = im.data[y*s + x];
if (v == 127)
continue;
// (dx,dy) pairs for 8 connectivity:
// (REFERENCE) (1, 0)
// (-1, 1) (0, 1) (1, 1)
//
DO_UNIONFIND(1, 0);
DO_UNIONFIND(0, 1);
if (v == 255) {
DO_UNIONFIND(-1, 1);
DO_UNIONFIND(1, 1);
}
}
}
#undef DO_UNIONFIND
struct ufrec {
// the parent of this node. If a node's parent is its own index,
// then it is a root.
uint32_t parent;
// for the root of a connected component, the number of components
// connected to it. For intermediate values, it's not meaningful.
uint32_t size;
};
uint32_t maxid;
struct ufrec *data;
std::vector<ufrec> data;
};
struct ufrec{
// the parent of this node. If a node's parent is its own index,
// then it is a root.
uint32_t parent;
// for the root of a connected component, the number of components
// connected to it. For intermediate values, it's not meaningful.
uint32_t size;
};
static inline unionfind_t *unionfind_create(uint32_t maxid){
unionfind_t *uf = (unionfind_t*) calloc(1, sizeof(unionfind_t));
uf->maxid = maxid;
uf->data = (struct ufrec*) malloc((maxid+1) * sizeof(struct ufrec));
for (unsigned int i = 0; i <= maxid; i++) {
uf->data[i].size = 1;
uf->data[i].parent = i;
}
return uf;
}
static inline void unionfind_destroy(unionfind_t *uf){
free(uf->data);
free(uf);
}
/*
static inline uint32_t unionfind_get_representative(unionfind_t *uf, uint32_t id)
{
// base case: a node is its own parent
if (uf->data[id].parent == id)
return id;
// otherwise, recurse
uint32_t root = unionfind_get_representative(uf, uf->data[id].parent);
// short circuit the path. [XXX This write prevents tail recursion]
uf->data[id].parent = root;
return root;
}
*/
// this one seems to be every-so-slightly faster than the recursive
// version above.
static inline uint32_t unionfind_get_representative(unionfind_t *uf, uint32_t id){
uint32_t root = id;
// chase down the root
while (uf->data[root].parent != root) {
root = uf->data[root].parent;
}
// go back and collapse the tree.
//
// XXX: on some of our workloads that have very shallow trees
// (e.g. image segmentation), we are actually faster not doing
// this...
while (uf->data[id].parent != root) {
uint32_t tmp = uf->data[id].parent;
uf->data[id].parent = root;
id = tmp;
}
return root;
}
static inline uint32_t unionfind_get_set_size(unionfind_t *uf, uint32_t id){
uint32_t repid = unionfind_get_representative(uf, id);
return uf->data[repid].size;
}
static inline uint32_t unionfind_connect(unionfind_t *uf, uint32_t aid, uint32_t bid){
uint32_t aroot = unionfind_get_representative(uf, aid);
uint32_t broot = unionfind_get_representative(uf, bid);
if (aroot == broot)
return aroot;
// we don't perform "union by rank", but we perform a similar
// operation (but probably without the same asymptotic guarantee):
// We join trees based on the number of *elements* (as opposed to
// rank) contained within each tree. I.e., we use size as a proxy
// for rank. In my testing, it's often *faster* to use size than
// rank, perhaps because the rank of the tree isn't that critical
// if there are very few nodes in it.
uint32_t asize = uf->data[aroot].size;
uint32_t bsize = uf->data[broot].size;
// optimization idea: We could shortcut some or all of the tree
// that is grafted onto the other tree. Pro: those nodes were just
// read and so are probably in cache. Con: it might end up being
// wasted effort -- the tree might be grafted onto another tree in
// a moment!
if (asize > bsize) {
uf->data[broot].parent = aroot;
uf->data[aroot].size += bsize;
return aroot;
} else {
uf->data[aroot].parent = broot;
uf->data[broot].size += asize;
return broot;
}
}
}}
#endif
@@ -84,7 +84,6 @@ void zmaxheap_destroy(zmaxheap_t *heap)
{
free(heap->values);
free(heap->data);
memset(heap, 0, sizeof(zmaxheap_t));
free(heap);
}
+106 -19
View File
@@ -9,6 +9,7 @@
#include "opencv2/objdetect/aruco_board.hpp"
#include "apriltag/apriltag_quad_thresh.hpp"
#include "aruco_utils.hpp"
#include <algorithm>
#include <cmath>
#include <map>
@@ -124,7 +125,7 @@ static void _threshold(InputArray _in, OutputArray _out, int winSize, double con
/**
* @brief Given a tresholded image, find the contours, calculate their polygonal approximation
* @brief Given a thresholded image, find the contours, calculate their polygonal approximation
* and take those that accomplish some conditions
*/
static void _findMarkerContours(const Mat &in, vector<vector<Point2f> > &candidates,
@@ -313,10 +314,11 @@ static void _detectInitialCandidates(const Mat &grey, vector<vector<Point2f> > &
* the border bits
*/
static Mat _extractBits(InputArray _image, const vector<Point2f>& corners, int markerSize,
int markerBorderBits, int cellSize, double cellMarginRate, double minStdDevOtsu) {
int markerBorderBits, int cellSize, double cellMarginRate, double minStdDevOtsu,
OutputArray _cellPixelRatio = noArray()) {
CV_Assert(_image.getMat().channels() == 1);
CV_Assert(corners.size() == 4ull);
CV_Assert(markerBorderBits > 0 && cellSize > 0 && cellMarginRate >= 0 && cellMarginRate <= 1);
CV_Assert(markerBorderBits > 0 && cellSize > 0 && cellMarginRate >= 0 && cellMarginRate <= 0.5);
CV_Assert(minStdDevOtsu >= 0);
// number of bits in the marker
@@ -353,9 +355,16 @@ static Mat _extractBits(InputArray _image, const vector<Point2f>& corners, int m
bits.setTo(1);
else
bits.setTo(0);
if(_cellPixelRatio.needed()) bits.convertTo(_cellPixelRatio, CV_32F);
return bits;
}
Mat cellPixelRatio;
if (_cellPixelRatio.needed()) {
_cellPixelRatio.create(markerSizeWithBorders, markerSizeWithBorders, CV_32FC1);
cellPixelRatio = _cellPixelRatio.getMatRef();
}
// now extract code, first threshold using Otsu
threshold(resultImg, resultImg, 125, 255, THRESH_BINARY | THRESH_OTSU);
@@ -369,6 +378,9 @@ static Mat _extractBits(InputArray _image, const vector<Point2f>& corners, int m
// count white pixels on each cell to assign its value
size_t nZ = (size_t) countNonZero(square);
if(nZ > square.total() / 2) bits.at<unsigned char>(y, x) = 1;
// define the cell pixel ratio as the ratio of the white pixels. For inverted markers, the ratio will be inverted.
if(_cellPixelRatio.needed()) cellPixelRatio.at<float>(y, x) = (nZ / (float)square.total());
}
}
@@ -403,6 +415,52 @@ static int _getBorderErrors(const Mat &bits, int markerSize, int borderSize) {
}
/** @brief Given a matrix containing the percentage of white pixels in each marker cell, returns the normalized marker confidence [0;1].
* The confidence is defined as 1 - normalized uncertainty, where 1 describes a pixel perfect detection.
* The rotation is set to 0,1,2,3 for [0, 90, 180, 270] deg CCW rotations.
*/
static float _getMarkerConfidence(const Mat& groundTruthbits, const Mat &cellPixelRatio, const int markerSize, const int borderSize) {
CV_Assert(markerSize == groundTruthbits.cols && markerSize == groundTruthbits.rows);
const int sizeWithBorders = markerSize + 2 * borderSize;
CV_Assert(markerSize > 0 && cellPixelRatio.cols == sizeWithBorders && cellPixelRatio.rows == sizeWithBorders);
// Get border uncertainty. cellPixelRatio has the opposite color as the borders --> it is the uncertainty.
float tempBorderUnc = 0.f;
for(int y = 0; y < sizeWithBorders; y++) {
for(int k = 0; k < borderSize; k++) {
// Left and right vertical sides
tempBorderUnc += cellPixelRatio.ptr<float>(y)[k];
tempBorderUnc += cellPixelRatio.ptr<float>(y)[sizeWithBorders - 1 - k];
}
}
for(int x = borderSize; x < sizeWithBorders - borderSize; x++) {
for(int k = 0; k < borderSize; k++) {
// Top and bottom horizontal sides
tempBorderUnc += cellPixelRatio.ptr<float>(k)[x];
tempBorderUnc += cellPixelRatio.ptr<float>(sizeWithBorders - 1 - k)[x];
}
}
// Get the inner marker uncertainty. For a white or black cell, the uncertainty is the ratio of black or white pixels respectively.
float tempInnerUnc = 0.f;
for(int y = borderSize; y < markerSize + borderSize; y++) {
for(int x = borderSize; x < markerSize + borderSize; x++) {
tempInnerUnc += std::abs(groundTruthbits.ptr<float>(y - borderSize)[x - borderSize] - cellPixelRatio.ptr<float>(y)[x]);
}
}
// Compute the overall normalized marker uncertainty and convert it to confidence
const float area = static_cast<float>(sizeWithBorders) * sizeWithBorders;
const float normalizedMarkerUnc = (tempInnerUnc + tempBorderUnc) / area;
const float normalizedMarkerConfidence = 1.f - normalizedMarkerUnc;
return std::max(0.f, std::min(1.f, normalizedMarkerConfidence));
}
/**
* @brief Tries to identify one candidate given the dictionary
* @return candidate typ. zero if the candidate is not valid,
@@ -412,6 +470,7 @@ static int _getBorderErrors(const Mat &bits, int markerSize, int borderSize) {
static uint8_t _identifyOneCandidate(const Dictionary& dictionary, const Mat& _image,
const vector<Point2f>& _corners, int& idx,
const DetectorParameters& params, int& rotation,
float &markerConfidence, bool confidenceNeeded,
const float scale = 1.f) {
CV_DbgAssert(params.markerBorderBits > 0);
uint8_t typ=1;
@@ -423,10 +482,12 @@ static uint8_t _identifyOneCandidate(const Dictionary& dictionary, const Mat& _i
scaled_corners[i].y = _corners[i].y * scale;
}
Mat cellPixelRatio;
Mat candidateBits =
_extractBits(_image, scaled_corners, dictionary.markerSize, params.markerBorderBits,
params.perspectiveRemovePixelPerCell,
params.perspectiveRemoveIgnoredMarginPerCell, params.minOtsuStdDev);
params.perspectiveRemoveIgnoredMarginPerCell, params.minOtsuStdDev,
cellPixelRatio);
// analyze border bits
int maximumErrorsInBorder =
@@ -441,6 +502,7 @@ static uint8_t _identifyOneCandidate(const Dictionary& dictionary, const Mat& _i
int invBError = _getBorderErrors(invertedImg, dictionary.markerSize, params.markerBorderBits);
// white marker
if(invBError<borderErrors){
cellPixelRatio = 1.f - cellPixelRatio;
borderErrors = invBError;
invertedImg.copyTo(candidateBits);
typ=2;
@@ -458,6 +520,14 @@ static uint8_t _identifyOneCandidate(const Dictionary& dictionary, const Mat& _i
if(!dictionary.identify(onlyBits, idx, rotation, params.errorCorrectionRate))
return 0;
// compute the candidate's confidence
if(confidenceNeeded) {
Mat groundTruthbits;
Mat bitsUints = dictionary.getBitsFromByteList(dictionary.bytesList.rowRange(idx, idx + 1), dictionary.markerSize, rotation);
bitsUints.convertTo(groundTruthbits, CV_32F);
markerConfidence = _getMarkerConfidence(groundTruthbits, cellPixelRatio, dictionary.markerSize, params.markerBorderBits);
}
return typ;
}
@@ -657,7 +727,7 @@ struct ArucoDetector::ArucoDetectorImpl {
* @brief Detect markers either using multiple or just first dictionary
*/
void detectMarkers(InputArray _image, OutputArrayOfArrays _corners, OutputArray _ids,
OutputArrayOfArrays _rejectedImgPoints, OutputArray _dictIndices, DictionaryMode dictMode) {
OutputArrayOfArrays _rejectedImgPoints, OutputArray _dictIndices, OutputArray _markersConfidence, DictionaryMode dictMode) {
CV_Assert(!_image.empty());
CV_Assert(detectorParams.markerBorderBits > 0);
@@ -717,6 +787,7 @@ struct ArucoDetector::ArucoDetectorImpl {
vector<vector<Point2f> > candidates;
vector<vector<Point> > contours;
vector<int> ids;
vector<float> markersConfidence;
/// STEP 2.a Detect marker candidates :: using AprilTag
if(detectorParams.cornerRefinementMethod == (int)CORNER_REFINE_APRILTAG){
@@ -738,7 +809,7 @@ struct ArucoDetector::ArucoDetectorImpl {
/// STEP 2: Check candidate codification (identify markers)
identifyCandidates(grey, grey_pyramid, selectedCandidates, candidates, contours,
ids, dictionary, rejectedImgPoints);
ids, dictionary, rejectedImgPoints, markersConfidence, _markersConfidence.needed());
/// STEP 3: Corner refinement :: use corner subpix
if (detectorParams.cornerRefinementMethod == (int)CORNER_REFINE_SUBPIX) {
@@ -766,7 +837,7 @@ struct ArucoDetector::ArucoDetectorImpl {
// temporary variable to store the current candidates
vector<vector<Point2f>> currentCandidates;
identifyCandidates(grey, grey_pyramid, candidatesPerDictionarySize.at(currentDictionary.markerSize), currentCandidates, contours,
ids, currentDictionary, rejectedImgPoints);
ids, currentDictionary, rejectedImgPoints, markersConfidence, _markersConfidence.needed());
if (_dictIndices.needed()) {
dictIndices.insert(dictIndices.end(), currentCandidates.size(), dictIndex);
}
@@ -849,6 +920,9 @@ struct ArucoDetector::ArucoDetectorImpl {
if (_dictIndices.needed()) {
Mat(dictIndices).copyTo(_dictIndices);
}
if (_markersConfidence.needed()) {
Mat(markersConfidence).copyTo(_markersConfidence);
}
}
/**
@@ -982,9 +1056,10 @@ struct ArucoDetector::ArucoDetectorImpl {
*/
void identifyCandidates(const Mat& grey, const vector<Mat>& image_pyr, vector<MarkerCandidateTree>& selectedContours,
vector<vector<Point2f> >& accepted, vector<vector<Point> >& contours,
vector<int>& ids, const Dictionary& currentDictionary, vector<vector<Point2f>>& rejected) const {
vector<int>& ids, const Dictionary& currentDictionary, vector<vector<Point2f>>& rejected, vector<float>& markersConfidence, bool confidenceNeeded) const {
size_t ncandidates = selectedContours.size();
vector<float> markersConfidenceTmp(ncandidates, 0.f);
vector<int> idsTmp(ncandidates, -1);
vector<int> rotated(ncandidates, 0);
vector<uint8_t> validCandidates(ncandidates, 0);
@@ -1018,11 +1093,11 @@ struct ArucoDetector::ArucoDetectorImpl {
}
const float scale = detectorParams.useAruco3Detection ? img.cols / static_cast<float>(grey.cols) : 1.f;
validCandidates[v] = _identifyOneCandidate(currentDictionary, img, selectedContours[v].corners, idsTmp[v], detectorParams, rotated[v], scale);
validCandidates[v] = _identifyOneCandidate(currentDictionary, img, selectedContours[v].corners, idsTmp[v], detectorParams, rotated[v], markersConfidenceTmp[v], confidenceNeeded, scale);
if (validCandidates[v] == 0 && checkCloseContours) {
for (const MarkerCandidate& closeMarkerCandidate: selectedContours[v].closeContours) {
validCandidates[v] = _identifyOneCandidate(currentDictionary, img, closeMarkerCandidate.corners, idsTmp[v], detectorParams, rotated[v], scale);
validCandidates[v] = _identifyOneCandidate(currentDictionary, img, closeMarkerCandidate.corners, idsTmp[v], detectorParams, rotated[v], markersConfidenceTmp[v], confidenceNeeded, scale);
if (validCandidates[v] > 0) {
selectedContours[v].corners = closeMarkerCandidate.corners;
selectedContours[v].contour = closeMarkerCandidate.contour;
@@ -1052,17 +1127,24 @@ struct ArucoDetector::ArucoDetectorImpl {
for (size_t i = 0ull; i < selectedContours.size(); i++) {
if (validCandidates[i] > 0) {
// shift corner positions to the correct rotation
correctCornerPosition(selectedContours[i].corners, rotated[i]);
// shift corner positions to the correct rotation
correctCornerPosition(selectedContours[i].corners, rotated[i]);
accepted.push_back(selectedContours[i].corners);
contours.push_back(selectedContours[i].contour);
ids.push_back(idsTmp[i]);
}
else {
accepted.push_back(selectedContours[i].corners);
contours.push_back(selectedContours[i].contour);
ids.push_back(idsTmp[i]);
} else {
rejected.push_back(selectedContours[i].corners);
}
}
if(confidenceNeeded) {
for (size_t i = 0ull; i < selectedContours.size(); i++) {
if (validCandidates[i] > 0) {
markersConfidence.push_back(markersConfidenceTmp[i]);
}
}
}
}
void performCornerSubpixRefinement(const Mat& grey, const vector<Mat>& grey_pyramid, int closest_pyr_image_idx, const vector<vector<Point2f>>& candidates, const Dictionary& dictionary) const {
@@ -1103,14 +1185,19 @@ ArucoDetector::ArucoDetector(const vector<Dictionary> &_dictionaries,
arucoDetectorImpl = makePtr<ArucoDetectorImpl>(_dictionaries, _detectorParams, _refineParams);
}
void ArucoDetector::detectMarkersWithConfidence(InputArray _image, OutputArrayOfArrays _corners, OutputArray _ids, OutputArray _markersConfidence,
OutputArrayOfArrays _rejectedImgPoints) const {
arucoDetectorImpl->detectMarkers(_image, _corners, _ids, _rejectedImgPoints, noArray(), _markersConfidence, DictionaryMode::Single);
}
void ArucoDetector::detectMarkers(InputArray _image, OutputArrayOfArrays _corners, OutputArray _ids,
OutputArrayOfArrays _rejectedImgPoints) const {
arucoDetectorImpl->detectMarkers(_image, _corners, _ids, _rejectedImgPoints, noArray(), DictionaryMode::Single);
arucoDetectorImpl->detectMarkers(_image, _corners, _ids, _rejectedImgPoints, noArray(), noArray(), DictionaryMode::Single);
}
void ArucoDetector::detectMarkersMultiDict(InputArray _image, OutputArrayOfArrays _corners, OutputArray _ids,
OutputArrayOfArrays _rejectedImgPoints, OutputArray _dictIndices) const {
arucoDetectorImpl->detectMarkers(_image, _corners, _ids, _rejectedImgPoints, _dictIndices, DictionaryMode::Multi);
arucoDetectorImpl->detectMarkers(_image, _corners, _ids, _rejectedImgPoints, _dictIndices, noArray(), DictionaryMode::Multi);
}
/**
@@ -194,17 +194,24 @@ Mat Dictionary::getByteListFromBits(const Mat &bits) {
}
Mat Dictionary::getBitsFromByteList(const Mat &byteList, int markerSize) {
Mat Dictionary::getBitsFromByteList(const Mat &byteList, int markerSize, int rotationId) {
CV_Assert(byteList.total() > 0 &&
byteList.total() >= (unsigned int)markerSize * markerSize / 8 &&
byteList.total() <= (unsigned int)markerSize * markerSize / 8 + 1);
CV_Assert(rotationId >=0 && rotationId < 4);
Mat bits(markerSize, markerSize, CV_8UC1, Scalar::all(0));
unsigned char base2List[] = { 128, 64, 32, 16, 8, 4, 2, 1 };
// Use a base offset for the selected rotation
int nbytes = (bits.cols * bits.rows + 8 - 1) / 8; // integer ceil
int base = rotationId * nbytes;
int currentByteIdx = 0;
// we only need the bytes in normal rotation
unsigned char currentByte = byteList.ptr()[0];
unsigned char currentByte = byteList.ptr()[base + currentByteIdx];
int currentBit = 0;
for(int row = 0; row < bits.rows; row++) {
for(int col = 0; col < bits.cols; col++) {
if(currentByte >= base2List[currentBit]) {
@@ -214,7 +221,7 @@ Mat Dictionary::getBitsFromByteList(const Mat &byteList, int markerSize) {
currentBit++;
if(currentBit == 8) {
currentByteIdx++;
currentByte = byteList.ptr()[currentByteIdx];
currentByte = byteList.ptr()[base + currentByteIdx];
// if not enough bits for one more byte, we are in the end
// update bit position accordingly
if(8 * (currentByteIdx + 1) > (int)bits.total())
@@ -316,8 +316,8 @@ struct CharucoDetector::CharucoDetectorImpl {
CV_Assert((markerCorners.empty() && markerIds.empty() && !image.empty()) || (markerCorners.total() == markerIds.total()));
vector<vector<Point2f>> tmpMarkerCorners;
vector<int> tmpMarkerIds;
InputOutputArrayOfArrays _markerCorners = markerCorners.needed() ? markerCorners : tmpMarkerCorners;
InputOutputArray _markerIds = markerIds.needed() ? markerIds : tmpMarkerIds;
InputOutputArrayOfArrays _markerCorners = markerCorners.needed() ? markerCorners : _InputOutputArray(tmpMarkerCorners);
InputOutputArray _markerIds = markerIds.needed() ? markerIds : _InputOutputArray(tmpMarkerIds);
if (markerCorners.empty() && markerIds.empty()) {
vector<vector<Point2f> > rejectedMarkers;
@@ -341,8 +341,8 @@ struct CharucoDetector::CharucoDetectorImpl {
InputOutputArrayOfArrays markerCorners, InputOutputArray markerIds) {
vector<vector<Point2f>> tmpMarkerCorners;
vector<int> tmpMarkerIds;
InputOutputArrayOfArrays _markerCorners = markerCorners.needed() ? markerCorners : tmpMarkerCorners;
InputOutputArray _markerIds = markerIds.needed() ? markerIds : tmpMarkerIds;
InputOutputArrayOfArrays _markerCorners = markerCorners.needed() ? markerCorners : _InputOutputArray(tmpMarkerCorners);
InputOutputArray _markerIds = markerIds.needed() ? markerIds : _InputOutputArray(tmpMarkerIds);
detectBoard(image, charucoCorners, charucoIds, _markerCorners, _markerIds);
if (charucoParameters.checkMarkers && checkBoard(_markerCorners, _markerIds, charucoCorners, charucoIds) == false) {
CV_LOG_DEBUG(NULL, "ChArUco board is built incorrectly");
@@ -402,8 +402,8 @@ void CharucoDetector::detectDiamonds(InputArray image, OutputArrayOfArrays _diam
vector<vector<Point2f>> tmpMarkerCorners;
vector<int> tmpMarkerIds;
InputOutputArrayOfArrays _markerCorners = inMarkerCorners.needed() ? inMarkerCorners : tmpMarkerCorners;
InputOutputArray _markerIds = inMarkerIds.needed() ? inMarkerIds : tmpMarkerIds;
InputOutputArrayOfArrays _markerCorners = inMarkerCorners.needed() ? inMarkerCorners : _InputOutputArray(tmpMarkerCorners);
InputOutputArray _markerIds = inMarkerIds.needed() ? inMarkerIds : _InputOutputArray(tmpMarkerIds);
if (_markerCorners.empty() && _markerIds.empty()) {
charucoDetectorImpl->arucoDetector.detectMarkers(image, _markerCorners, _markerIds);
}
+1
View File
@@ -43,6 +43,7 @@
#ifndef __OPENCV_PRECOMP_H__
#define __OPENCV_PRECOMP_H__
#include "opencv2/core.hpp"
#include "opencv2/objdetect.hpp"
#include "opencv2/objdetect/barcode.hpp"
#include "opencv2/imgproc.hpp"
+30 -20
View File
@@ -80,8 +80,10 @@ static Point2f intersectionLines(Point2f a1, Point2f a2, Point2f b1, Point2f b2)
// / |
// a/ | c
static inline double getCosVectors(Point2f a, Point2f b, Point2f c)
static inline double getCosVectors(Point a, Point b, Point c)
{
CV_DbgCheckNE(a, b, "Angle between vector and point is undetermined");
CV_DbgCheckNE(b, c, "Angle between vector and point is undetermined");
return ((a - b).x * (c - b).x + (a - b).y * (c - b).y) / (norm(a - b) * norm(c - b));
}
@@ -407,9 +409,9 @@ void QRDetect::fixationPoints(vector<Point2f> &local_point)
list_area_pnt.push_back(current_point);
vector<LineIterator> list_line_iter;
list_line_iter.push_back(LineIterator(bin_barcode, current_point, left_point));
list_line_iter.push_back(LineIterator(bin_barcode, current_point, central_point));
list_line_iter.push_back(LineIterator(bin_barcode, current_point, right_point));
list_line_iter.emplace_back(bin_barcode, current_point, left_point);
list_line_iter.emplace_back(bin_barcode, current_point, central_point);
list_line_iter.emplace_back(bin_barcode, current_point, right_point);
for (size_t k = 0; k < list_line_iter.size(); k++)
{
@@ -752,7 +754,7 @@ vector<Point2f> QRDetect::getQuadrilateral(vector<Point2f> angle_list)
{
int x = cvRound(angle_list[i].x);
int y = cvRound(angle_list[i].y);
locations.push_back(Point(x, y));
locations.emplace_back(x,y);
}
vector<Point> integer_hull;
@@ -822,7 +824,11 @@ vector<Point2f> QRDetect::getQuadrilateral(vector<Point2f> angle_list)
Point intrsc_line_hull =
intersectionLines(hull[index_hull], hull[next_index_hull],
angle_list[1], angle_list[2]);
double temp_norm = getCosVectors(hull[index_hull], intrsc_line_hull, angle_closest_pnt);
double temp_norm = min_norm;
if (intrsc_line_hull != angle_closest_pnt)
{
temp_norm = getCosVectors(hull[index_hull], intrsc_line_hull, angle_closest_pnt);
}
if (min_norm > temp_norm &&
norm(hull[index_hull] - hull[next_index_hull]) >
norm(angle_list[1] - angle_list[2]) * 0.1)
@@ -860,7 +866,11 @@ vector<Point2f> QRDetect::getQuadrilateral(vector<Point2f> angle_list)
Point intrsc_line_hull =
intersectionLines(hull[index_hull], hull[next_index_hull],
angle_list[0], angle_list[1]);
double temp_norm = getCosVectors(hull[index_hull], intrsc_line_hull, angle_closest_pnt);
double temp_norm = min_norm;
if (intrsc_line_hull != angle_closest_pnt)
{
temp_norm = getCosVectors(hull[index_hull], intrsc_line_hull, angle_closest_pnt);
}
if (min_norm > temp_norm &&
norm(hull[index_hull] - hull[next_index_hull]) >
norm(angle_list[0] - angle_list[1]) * 0.05)
@@ -2204,13 +2214,13 @@ bool QRDecode::straightenQRCodeInParts()
vector<Point2f> perspective_points;
perspective_points.push_back(Point2f(0.0, start_cut));
perspective_points.push_back(Point2f(perspective_curved_size, start_cut));
perspective_points.emplace_back(0.0f, start_cut);
perspective_points.emplace_back(perspective_curved_size, start_cut);
perspective_points.push_back(Point2f(perspective_curved_size, start_cut + dist));
perspective_points.push_back(Point2f(0.0, start_cut+dist));
perspective_points.emplace_back(perspective_curved_size, start_cut + dist);
perspective_points.emplace_back(0.0f, start_cut+dist);
perspective_points.push_back(Point2f(perspective_curved_size * 0.5f, start_cut + dist * 0.5f));
perspective_points.emplace_back(perspective_curved_size * 0.5f, start_cut + dist * 0.5f);
if (i == 1)
{
@@ -2281,13 +2291,13 @@ bool QRDecode::straightenQRCodeInParts()
}
vector<Point2f> perspective_straight_points;
perspective_straight_points.push_back(Point2f(0.f, 0.f));
perspective_straight_points.push_back(Point2f(perspective_curved_size, 0.f));
perspective_straight_points.emplace_back(0.f, 0.f);
perspective_straight_points.emplace_back(perspective_curved_size, 0.f);
perspective_straight_points.push_back(Point2f(perspective_curved_size, perspective_curved_size));
perspective_straight_points.push_back(Point2f(0.f, perspective_curved_size));
perspective_straight_points.emplace_back(perspective_curved_size, perspective_curved_size);
perspective_straight_points.emplace_back(0.f, perspective_curved_size);
perspective_straight_points.push_back(Point2f(perspective_curved_size * 0.5f, perspective_curved_size * 0.5f));
perspective_straight_points.emplace_back(perspective_curved_size * 0.5f, perspective_curved_size * 0.5f);
Mat H = findHomography(original_curved_points, perspective_straight_points);
if (H.empty())
@@ -3272,9 +3282,9 @@ void QRDetectMulti::fixationPoints(vector<Point2f> &local_point)
list_area_pnt.push_back(current_point);
vector<LineIterator> list_line_iter;
list_line_iter.push_back(LineIterator(bin_barcode_temp, current_point, left_point));
list_line_iter.push_back(LineIterator(bin_barcode_temp, current_point, central_point));
list_line_iter.push_back(LineIterator(bin_barcode_temp, current_point, right_point));
list_line_iter.emplace_back(bin_barcode_temp, current_point, left_point);
list_line_iter.emplace_back(bin_barcode_temp, current_point, central_point);
list_line_iter.emplace_back(bin_barcode_temp, current_point, right_point);
for (size_t k = 0; k < list_line_iter.size(); k++)
{
@@ -321,6 +321,358 @@ void CV_ArucoDetectionPerspective::run(int) {
}
}
// Helper struct and functions for CV_ArucoDetectionConfidence
// Inverts a square subregion inside selected cells of a marker to simulate a confidence drop
enum class MarkerRegionToTemper {
BORDER, // Only invert cells within the marker border bits
INNER, // Only invert cells in the inner part of the marker (excluding borders)
ALL // Invert any cells
};
// Define the characteristics of cell inversions
struct MarkerTemperingConfig {
float cellRatioToTemper; // [0,1] ratio of the cell to invert
int numCellsToTemper; // Number of cells to invert
MarkerRegionToTemper markerRegionToTemper; // Which cells to invert (BORDER, INNER, ALL)
};
// Test configs for CV_ArucoDetectionConfidence
struct ArucoConfidenceTestConfig {
MarkerTemperingConfig markerTemperingConfig; // Configuration of cells to invert (percentage, number and markerRegionToTemper)
float perspectiveRemoveIgnoredMarginPerCell; // Width of the margin of pixels on each cell not considered for the marker identification
int markerBorderBits; // Number of bits of the marker border
float distortionRatio; // Percentage of offset used for perspective distortion, bigger means more distorted
};
enum class markerRot
{
NONE = 0,
ROT_90,
ROT_180,
ROT_270
};
struct markerDetectionGT {
int id; // Marker identification
double confidence; // Pixel-based confidence defined as 1 - (inverted area / total area)
bool expectDetection; // True if we expect to detect the marker
};
struct MarkerCreationConfig {
int id; // Marker identification
int markerSidePixels; // Marker size (in pixels)
markerRot rotation; // Rotation of the marker in degrees (0, 90, 180, 270)
};
void rotateMarker(Mat &marker, const markerRot rotation)
{
if(rotation == markerRot::NONE)
return;
if (rotation == markerRot::ROT_90) {
cv::transpose(marker, marker);
cv::flip(marker, marker, 0);
} else if (rotation == markerRot::ROT_180) {
cv::flip(marker, marker, -1);
} else if (rotation == markerRot::ROT_270) {
cv::transpose(marker, marker);
cv::flip(marker, marker, 1);
}
}
void distortMarker(Mat &marker, const float distortionRatio)
{
if (distortionRatio < FLT_EPSILON)
return;
// apply a distortion (a perspective warp) to simulate a non-ideal capture
vector<Point2f> src = { {0, 0},
{static_cast<float>(marker.cols), 0},
{static_cast<float>(marker.cols), static_cast<float>(marker.rows)},
{0, static_cast<float>(marker.rows)} };
float offset = marker.cols * distortionRatio; // distortionRatio % offset for distortion
vector<Point2f> dst = { {offset, offset},
{marker.cols - offset, 0},
{marker.cols - offset, marker.rows - offset},
{0, marker.rows - offset} };
Mat M = getPerspectiveTransform(src, dst);
warpPerspective(marker, marker, M, marker.size(), INTER_LINEAR, BORDER_CONSTANT, Scalar(255));
}
/**
* @brief Inverts a square subregion inside selected cells of a marker image to simulate confidence degradation.
*
* The function computes the marker grid parameters and then applies a bitwise inversion
* on a square markerRegionToTemper inside the chosen cells. The number of cells to be inverted is determined by
* the parameter 'numCellsToTemper'. The candidate cells can be filtered to only include border cells,
* inner cells, or all cells according to the parameter 'markerRegionToTemper'.
*
* @param marker The marker image
* @param markerSidePixels The total size of the marker in pixels (inner and border).
* @param markerId The id of the marker
* @param params The Aruco detector configuration (provides border bits, margin ratios, etc.).
* @param dictionary The Aruco marker dictionary (used to determine marker grid size).
* @param cellTempConfig Cell tempering config as defined in MarkerTemperingConfig
* @return Cell tempering ground truth as defined in markerDetectionGT
*/
markerDetectionGT applyTemperingToMarkerCells(cv::Mat &marker,
const int markerSidePixels,
const int markerId,
const aruco::DetectorParameters &params,
const aruco::Dictionary &dictionary,
const MarkerTemperingConfig &cellTempConfig)
{
// nothing to invert
if(cellTempConfig.numCellsToTemper <= 0 || cellTempConfig.cellRatioToTemper <= FLT_EPSILON)
return {markerId, 1.0, true};
// compute the overall grid dimensions.
const int markerSizeWithBorders = dictionary.markerSize + 2 * params.markerBorderBits;
const int cellSidePixelsSize = markerSidePixels / markerSizeWithBorders;
// compute the margin within each cell used for identification.
const int cellMarginPixels = static_cast<int>(params.perspectiveRemoveIgnoredMarginPerCell * cellSidePixelsSize);
const int innerCellSizePixels = cellSidePixelsSize - 2 * cellMarginPixels;
// determine the size of the square that will be inverted in each cell.
// (cellSidePixelsInvert / innerCellSizePixels)^2 should equal cellRatioToTemper.
const int cellSidePixelsInvert = min(cellSidePixelsSize, static_cast<int>(innerCellSizePixels * std::sqrt(cellTempConfig.cellRatioToTemper)));
const int inversionOffsetPixels = (cellSidePixelsSize - cellSidePixelsInvert) / 2;
// nothing to invert
if(cellSidePixelsInvert <= 0)
return {markerId, 1.0, true};
int cellsTempered = 0;
int borderErrors = 0;
int innerCellsErrors = 0;
// iterate over each cell in the grid.
for (int row = 0; row < markerSizeWithBorders; row++) {
for (int col = 0; col < markerSizeWithBorders; col++) {
// decide if this cell falls in the markerRegionToTemper to temper.
const bool isBorder = (row < params.markerBorderBits ||
col < params.markerBorderBits ||
row >= markerSizeWithBorders - params.markerBorderBits ||
col >= markerSizeWithBorders - params.markerBorderBits);
const bool inRegion = (cellTempConfig.markerRegionToTemper == MarkerRegionToTemper::ALL ||
(isBorder && cellTempConfig.markerRegionToTemper == MarkerRegionToTemper::BORDER) ||
(!isBorder && cellTempConfig.markerRegionToTemper == MarkerRegionToTemper::INNER));
// apply the inversion to simulate tempering.
if (inRegion && (cellsTempered < cellTempConfig.numCellsToTemper)) {
const int xStart = col * cellSidePixelsSize + inversionOffsetPixels;
const int yStart = row * cellSidePixelsSize + inversionOffsetPixels;
cv::Rect cellRect(xStart, yStart, cellSidePixelsInvert, cellSidePixelsInvert);
cv::Mat cellROI = marker(cellRect);
cv::bitwise_not(cellROI, cellROI);
++cellsTempered;
// cell too tempered, no detection expected
if(cellTempConfig.cellRatioToTemper > 0.5f) {
if(isBorder){
++borderErrors;
} else {
++innerCellsErrors;
}
}
}
if(cellsTempered >= cellTempConfig.numCellsToTemper)
break;
}
if(cellsTempered >= cellTempConfig.numCellsToTemper)
break;
}
// compute the ground-truth confidence
const double invertedArea = cellsTempered * cellSidePixelsInvert * cellSidePixelsInvert;
const double totalDetectionArea = markerSizeWithBorders * innerCellSizePixels * markerSizeWithBorders * innerCellSizePixels;
const double groundTruthConfidence = std::max(0.0, 1.0 - invertedArea / totalDetectionArea);
// check if marker is expected to be detected
const int maximumErrorsInBorder = static_cast<int>(dictionary.markerSize * dictionary.markerSize * params.maxErroneousBitsInBorderRate);
const int maxCorrectionRecalculed = static_cast<int>(dictionary.maxCorrectionBits * params.errorCorrectionRate);
const bool expectDetection = static_cast<bool>(borderErrors <= maximumErrorsInBorder && innerCellsErrors <= maxCorrectionRecalculed);
return {markerId, groundTruthConfidence, expectDetection};
}
/**
* @brief Create an image of a marker with inverted (tempered) regions to simulate detection confidence
*
* Applies an optional rotation and an optional perspective warp to simulate a distorted marker.
* Inverts a square subregion inside selected cells of a marker image to simulate a drop in confidence.
* Computes the ground-truth confidence as one minus the ratio of inverted area to the total marker area used for identification.
*
*/
markerDetectionGT generateTemperedMarkerImage(Mat &marker, const MarkerCreationConfig &markerConfig, const MarkerTemperingConfig &markerTemperingConfig,
const aruco::DetectorParameters &params, const aruco::Dictionary &dictionary, const float distortionRatio = 0.f)
{
// generate the synthetic marker image
aruco::generateImageMarker(dictionary, markerConfig.id, markerConfig.markerSidePixels,
marker, params.markerBorderBits);
// rotate marker if necessary
rotateMarker(marker, markerConfig.rotation);
// temper with cells to simulate detection confidence drops
markerDetectionGT groundTruth = applyTemperingToMarkerCells(marker, markerConfig.markerSidePixels, markerConfig.id, params, dictionary, markerTemperingConfig);
// apply a distortion (a perspective warp) to simulate a non-ideal capture
distortMarker(marker, distortionRatio);
return groundTruth;
}
/**
* @brief Copies a marker image into a larger image at the given top-left position.
*/
void placeMarker(Mat &img, const Mat &marker, const Point2f &topLeft)
{
Rect roi(Point(static_cast<int>(topLeft.x), static_cast<int>(topLeft.y)), marker.size());
marker.copyTo(img(roi));
}
/**
* @brief Test the marker confidence computations
*
* Loops over a set of detector configurations (e.g. expected confidence, distortion, DetectorParameters)
* For each configuration, it creates a synthetic image containing four markers arranged in a 2x2 grid.
* Each marker is generated with its own configuration (id, size, rotation).
* Finally, it runs the detector and checks that each marker is detected and
* that its computed confidence is close to the ground truth value.
*
*/
static void runArucoDetectionConfidence(ArucoAlgParams arucoAlgParam) {
aruco::DetectorParameters params;
// make sure there are no bits have any detection errors
params.maxErroneousBitsInBorderRate = 0.0;
params.errorCorrectionRate = 0.0;
params.perspectiveRemovePixelPerCell = 8; // esnsure that there is enough resolution to properly handle distortions
aruco::ArucoDetector detector(aruco::getPredefinedDictionary(aruco::DICT_6X6_250), params);
const bool detectInvertedMarker = (arucoAlgParam == ArucoAlgParams::DETECT_INVERTED_MARKER);
// define several detector configurations to test different settings
// {{MarkerTemperingConfig}, perspectiveRemoveIgnoredMarginPerCell, markerBorderBits, distortionRatio}
vector<ArucoConfidenceTestConfig> detectorConfigs = {
// No margins, No distortion
{{0.f, 64, MarkerRegionToTemper::ALL}, 0.0f, 1, 0.f},
{{0.01f, 64, MarkerRegionToTemper::ALL}, 0.0f, 1, 0.f},
{{0.05f, 100, MarkerRegionToTemper::ALL}, 0.0f, 2, 0.f},
{{0.1f, 64, MarkerRegionToTemper::ALL}, 0.0f, 1, 0.f},
{{0.15f, 30, MarkerRegionToTemper::ALL}, 0.0f, 1, 0.f},
{{0.20f, 55, MarkerRegionToTemper::ALL}, 0.0f, 2, 0.f},
// Margins, No distortion
{{0.f, 26, MarkerRegionToTemper::BORDER}, 0.05f, 1, 0.f},
{{0.01f, 56, MarkerRegionToTemper::BORDER}, 0.05f, 2, 0.f},
{{0.05f, 144, MarkerRegionToTemper::ALL}, 0.1f, 3, 0.f},
{{0.10f, 49, MarkerRegionToTemper::ALL}, 0.15f, 1, 0.f},
// No margins, distortion
{{0.f, 36, MarkerRegionToTemper::INNER}, 0.0f, 1, 0.01f},
{{0.01f, 36, MarkerRegionToTemper::INNER}, 0.0f, 1, 0.02f},
{{0.05f, 12, MarkerRegionToTemper::INNER}, 0.0f, 2, 0.05f},
{{0.1f, 64, MarkerRegionToTemper::ALL}, 0.0f, 1, 0.1f},
{{0.1f, 81, MarkerRegionToTemper::ALL}, 0.0f, 2, 0.2f},
// Margins, distortion
{{0.f, 81, MarkerRegionToTemper::ALL}, 0.05f, 2, 0.01f},
{{0.01f, 64, MarkerRegionToTemper::ALL}, 0.05f, 1, 0.02f},
{{0.05f, 81, MarkerRegionToTemper::ALL}, 0.1f, 2, 0.05f},
{{0.1f, 64, MarkerRegionToTemper::ALL}, 0.15f, 1, 0.1f},
{{0.1f, 64, MarkerRegionToTemper::ALL}, 0.0f, 1, 0.2f},
// no marker detection, too much tempering
{{0.9f, 1, MarkerRegionToTemper::ALL}, 0.05f, 2, 0.0f},
{{0.9f, 1, MarkerRegionToTemper::BORDER}, 0.05f, 2, 0.0f},
{{0.9f, 1, MarkerRegionToTemper::INNER}, 0.05f, 2, 0.0f},
};
// define marker configurations for the 4 markers in each image
const int markerSidePixels = 480; // To simplify the cell division, markerSidePixels is a multiple of 8. (6x6 dict + 2 border bits)
vector<MarkerCreationConfig> markerCreationConfig = {
{0, markerSidePixels, markerRot::ROT_90}, // {id, markerSidePixels, rotation}
{1, markerSidePixels, markerRot::ROT_270},
{2, markerSidePixels, markerRot::NONE},
{3, markerSidePixels, markerRot::ROT_180}
};
// loop over each detector configuration
for (size_t cfgIdx = 0; cfgIdx < detectorConfigs.size(); cfgIdx++) {
ArucoConfidenceTestConfig detCfg = detectorConfigs[cfgIdx];
SCOPED_TRACE(cv::format("detectorConfig=%zu", cfgIdx));
// update detector parameters
params.perspectiveRemoveIgnoredMarginPerCell = detCfg.perspectiveRemoveIgnoredMarginPerCell;
params.markerBorderBits = detCfg.markerBorderBits;
params.detectInvertedMarker = detectInvertedMarker;
detector.setDetectorParameters(params);
// create a blank image large enough to hold 4 markers in a 2x2 grid
const int margin = markerSidePixels / 2;
const int imageSize = (markerSidePixels * 2) + margin * 3;
Mat img(imageSize, imageSize, CV_8UC1, Scalar(255));
vector<markerDetectionGT> groundTruths;
const aruco::Dictionary &dictionary = detector.getDictionary();
// place each marker into the image
for (int row = 0; row < 2; row++) {
for (int col = 0; col < 2; col++) {
int index = row * 2 + col;
MarkerCreationConfig markerCfg = markerCreationConfig[index];
// adjust marker id to be unique for each detector configuration
markerCfg.id += static_cast<int>(cfgIdx * markerCreationConfig.size());
// generate img
Mat markerImg;
markerDetectionGT gt = generateTemperedMarkerImage(markerImg, markerCfg, detCfg.markerTemperingConfig, params, dictionary, detCfg.distortionRatio);
groundTruths.push_back(gt);
// place marker in the image
Point2f topLeft(static_cast<float>(margin + col * (markerSidePixels + margin)),
static_cast<float>(margin + row * (markerSidePixels + margin)));
placeMarker(img, markerImg, topLeft);
}
}
// if testing inverted markers globally, invert the whole image
if (detectInvertedMarker) {
bitwise_not(img, img);
}
// run detection.
vector<vector<Point2f>> corners, rejected;
vector<int> ids;
vector<float> markerConfidence;
detector.detectMarkersWithConfidence(img, corners, ids, markerConfidence, rejected);
ASSERT_EQ(ids.size(), corners.size());
ASSERT_EQ(ids.size(), markerConfidence.size());
std::map<int, float> confidenceById;
for (size_t i = 0; i < ids.size(); i++) {
confidenceById[ids[i]] = markerConfidence[i];
}
// verify that every marker is detected and its confidence is within tolerance
for (const auto& currentGT : groundTruths) {
const bool detected = confidenceById.find(currentGT.id) != confidenceById.end();
EXPECT_EQ(currentGT.expectDetection, detected) << "Marker id: " << currentGT.id;
if (currentGT.expectDetection && detected) {
EXPECT_NEAR(currentGT.confidence, confidenceById[currentGT.id], 0.05)
<< "Marker id: " << currentGT.id;
}
}
}
}
/**
* @brief Check max and min size in marker detection parameters
@@ -552,6 +904,83 @@ TEST(CV_ArucoBitCorrection, algorithmic) {
test.safe_run();
}
TEST(CV_ArucoDetectionConfidence, algorithmic) {
runArucoDetectionConfidence(ArucoAlgParams::USE_DEFAULT);
}
TEST(CV_InvertedArucoDetectionConfidence, algorithmic) {
runArucoDetectionConfidence(ArucoAlgParams::DETECT_INVERTED_MARKER);
}
TEST(CV_InvertedFlagArucoDetectionConfidence, algorithmic) {
aruco::DetectorParameters params;
params.maxErroneousBitsInBorderRate = 0.0;
params.errorCorrectionRate = 0.0;
params.perspectiveRemovePixelPerCell = 8;
params.detectInvertedMarker = false;
const aruco::Dictionary dictionary = aruco::getPredefinedDictionary(aruco::DICT_6X6_250);
// create a blank image large enough to hold 4 markers in a 2x2 grid
const int markerSidePixels = 480;
const int margin = markerSidePixels / 2;
const int imageSize = (markerSidePixels * 2) + margin * 3;
Mat img(imageSize, imageSize, CV_8UC1, Scalar(255));
// place 4 markers into the image
for (int row = 0; row < 2; row++) {
for (int col = 0; col < 2; col++) {
const int id = row * 2 + col;
Mat markerImg;
aruco::generateImageMarker(dictionary, id, markerSidePixels, markerImg, params.markerBorderBits);
Point2f topLeft(static_cast<float>(margin + col * (markerSidePixels + margin)),
static_cast<float>(margin + row * (markerSidePixels + margin)));
placeMarker(img, markerImg, topLeft);
}
}
// run detection with detectInvertedMarker = false (baseline)
aruco::ArucoDetector detector(dictionary, params);
vector<vector<Point2f>> corners, rejected;
vector<int> ids;
vector<float> confidenceDefault;
detector.detectMarkersWithConfidence(img, corners, ids, confidenceDefault, rejected);
ASSERT_EQ(ids.size(), corners.size());
ASSERT_EQ(ids.size(), confidenceDefault.size());
std::map<int, float> confidenceByIdDefault;
for (size_t i = 0; i < ids.size(); i++) {
confidenceByIdDefault[ids[i]] = confidenceDefault[i];
}
// run detection with detectInvertedMarker = true, without inverting the image
params.detectInvertedMarker = true;
aruco::ArucoDetector detectorInvertedFlag(dictionary, params);
vector<float> confidenceInvertedFlag;
detectorInvertedFlag.detectMarkersWithConfidence(img, corners, ids, confidenceInvertedFlag, rejected);
ASSERT_EQ(ids.size(), corners.size());
ASSERT_EQ(ids.size(), confidenceInvertedFlag.size());
std::map<int, float> confidenceByIdInvertedFlag;
for (size_t i = 0; i < ids.size(); i++) {
confidenceByIdInvertedFlag[ids[i]] = confidenceInvertedFlag[i];
}
// detectInvertedMarker should not invert/flip confidence for non-inverted markers.
for (int id = 0; id < 4; id++) {
ASSERT_NE(confidenceByIdDefault.find(id), confidenceByIdDefault.end()) << "Marker id: " << id;
ASSERT_NE(confidenceByIdInvertedFlag.find(id), confidenceByIdInvertedFlag.end()) << "Marker id: " << id;
const float confDefault = confidenceByIdDefault[id];
const float confInvertedFlag = confidenceByIdInvertedFlag[id];
EXPECT_GT(confDefault, 0.8f) << "Marker id: " << id;
EXPECT_GT(confInvertedFlag, 0.8f) << "Marker id: " << id;
EXPECT_NEAR(confDefault, confInvertedFlag, 0.2f) << "Marker id: " << id;
}
}
TEST(CV_ArucoDetectMarkers, regression_3192)
{
aruco::ArucoDetector detector(aruco::getPredefinedDictionary(aruco::DICT_4X4_50));
+33 -14
View File
@@ -59,16 +59,17 @@ void checkImageDimensions(const std::vector<Mat>& images)
}
}
Mat triangleWeights()
Mat triangleWeights(int length)
{
// hat function
Mat w(LDR_SIZE, 1, CV_32F);
int half = LDR_SIZE / 2;
int maxVal = LDR_SIZE - 1;
CV_Assert(length >= 2);
Mat w(length, 1, CV_32F);
int half = length / 2;
int maxVal = length - 1;
float epsilon = 1e-6f;
w.at<float>(0) = epsilon;
w.at<float>(LDR_SIZE-1) = epsilon;
for (int i = 1; i < LDR_SIZE-1; i++){
w.at<float>(length - 1) = epsilon;
for (int i = 1; i < length - 1; i++){
w.at<float>(i) = (i < half)
? static_cast<float>(i)
: static_cast<float>(maxVal - i);
@@ -76,15 +77,16 @@ Mat triangleWeights()
return w;
}
Mat RobertsonWeights()
Mat RobertsonWeights(int length)
{
Mat weight(LDR_SIZE, 1, CV_32FC3);
float q = (LDR_SIZE - 1) / 4.0f;
CV_Assert(length >= 1);
Mat weight(length, 1, CV_32FC3);
float q = (length - 1) / 4.0f;
float e4 = exp(4.f);
float scale = e4/(e4 - 1.f);
float shift = 1 / (1.f - e4);
for(int i = 0; i < LDR_SIZE; i++) {
for(int i = 0; i < length; i++) {
float value = i / q - 2.0f;
value = scale*exp(-value * value) + shift;
weight.at<Vec3f>(i) = Vec3f::all(value);
@@ -104,11 +106,28 @@ void mapLuminance(Mat src, Mat dst, Mat lum, Mat new_lum, float saturation)
merge(channels, dst);
}
Mat linearResponse(int channels)
Mat linearResponse(int channels, int length)
{
Mat response = Mat(LDR_SIZE, 1, CV_MAKETYPE(CV_32F, channels));
for(int i = 0; i < LDR_SIZE; i++) {
response.at<Vec3f>(i) = Vec3f::all(static_cast<float>(i));
CV_Assert(length >= 1);
Mat response = Mat(length, 1, CV_MAKETYPE(CV_32F, channels));
if (channels == 1)
{
for (int i = 0; i < length; i++)
{
response.at<float>(i) = static_cast<float>(i);
}
}
else if (channels == 3)
{
for (int i = 0; i < length; i++)
{
response.at<Vec3f>(i) = Vec3f::all(static_cast<float>(i));
}
}
else
{
CV_Error(Error::StsBadArg, "Unsupported number of channels in linearResponse");
}
return response;
}
+3 -3
View File
@@ -50,13 +50,13 @@ namespace cv
void checkImageDimensions(const std::vector<Mat>& images);
Mat triangleWeights();
Mat triangleWeights(int length = LDR_SIZE);
void mapLuminance(Mat src, Mat dst, Mat lum, Mat new_lum, float saturation);
Mat RobertsonWeights();
Mat RobertsonWeights(int length = LDR_SIZE);
Mat linearResponse(int channels);
Mat linearResponse(int channels, int length = LDR_SIZE);
}
#endif
+61 -12
View File
@@ -66,25 +66,46 @@ public:
CV_Assert(images.size() == times.total());
checkImageDimensions(images);
CV_Assert(images[0].depth() == CV_8U);
int depth = images[0].depth();
CV_Assert(depth == CV_8U || depth == CV_16U || depth == CV_32F);
int channels = images[0].channels();
Size size = images[0].size();
int CV_32FCC = CV_MAKETYPE(CV_32F, channels);
const bool use16bitLUT = (depth == CV_16U || depth == CV_32F);
const int lutLength = use16bitLUT ? 65536 : LDR_SIZE;
std::vector<Mat> lutImages(images.size());
if (depth == CV_8U || depth == CV_16U)
{
lutImages = images;
}
else
{
const double scale = static_cast<double>(lutLength - 1);
for (size_t i = 0; i < images.size(); ++i)
{
Mat clipped;
cv::max(images[i], 0.0, clipped);
cv::min(clipped, 1.0, clipped);
clipped.convertTo(lutImages[i], CV_16U, scale);
}
}
dst.create(images[0].size(), CV_32FCC);
Mat result = dst.getMat();
Mat response = input_response.getMat();
if(response.empty()) {
response = linearResponse(channels);
response = linearResponse(channels, lutLength);
response.at<Vec3f>(0) = response.at<Vec3f>(1);
}
Mat log_response;
log(response, log_response);
CV_Assert(log_response.rows == LDR_SIZE && log_response.cols == 1 &&
CV_Assert(log_response.rows == lutLength && log_response.cols == 1 &&
log_response.channels() == channels);
Mat exp_values(times.clone());
@@ -95,19 +116,23 @@ public:
split(result, result_split);
Mat weight_sum = Mat::zeros(size, CV_32F);
Mat weights_lut = use16bitLUT
? triangleWeights(lutLength)
: weights;
for(size_t i = 0; i < images.size(); i++) {
std::vector<Mat> splitted;
split(images[i], splitted);
split(lutImages[i], splitted);
Mat w = Mat::zeros(size, CV_32F);
for(int c = 0; c < channels; c++) {
LUT(splitted[c], weights, splitted[c]);
LUT(splitted[c], weights_lut, splitted[c]);
w += splitted[c];
}
w /= channels;
Mat response_img;
LUT(images[i], log_response, response_img);
LUT(lutImages[i], log_response, response_img);
split(response_img, splitted);
for(int c = 0; c < channels; c++) {
result_split[c] += w.mul(splitted[c] - exp_values.at<float>((int)i));
@@ -328,28 +353,52 @@ public:
CV_Assert(images.size() == times.total());
checkImageDimensions(images);
CV_Assert(images[0].depth() == CV_8U);
int depth = images[0].depth();
CV_Assert(depth == CV_8U || depth == CV_16U || depth == CV_32F);
int channels = images[0].channels();
int CV_32FCC = CV_MAKETYPE(CV_32F, channels);
const bool use16bitLUT = (depth == CV_16U || depth == CV_32F);
const int lutLength = use16bitLUT ? 65536 : LDR_SIZE;
// Build LUT index images (see MergeDebevecImpl for details).
std::vector<Mat> lutImages(images.size());
if (depth == CV_8U || depth == CV_16U)
{
lutImages = images;
}
else // CV_32F
{
const double scale = static_cast<double>(lutLength - 1);
for (size_t i = 0; i < images.size(); ++i)
{
images[i].convertTo(lutImages[i], CV_16U, scale);
}
}
dst.create(images[0].size(), CV_32FCC);
Mat result = dst.getMat();
Mat response = input_response.getMat();
if(response.empty()) {
float middle = static_cast<float>(LDR_SIZE) / 2.0f;
response = linearResponse(channels) / middle;
float middle = static_cast<float>(lutLength) / 2.0f;
response = linearResponse(channels, lutLength) / middle;
}
CV_Assert(response.rows == LDR_SIZE && response.cols == 1 &&
CV_Assert(response.rows == lutLength && response.cols == 1 &&
response.channels() == channels);
result = Mat::zeros(images[0].size(), CV_32FCC);
Mat wsum = Mat::zeros(images[0].size(), CV_32FCC);
Mat weight_lut = use16bitLUT
? RobertsonWeights(lutLength)
: weight;
for(size_t i = 0; i < images.size(); i++) {
Mat im, w;
LUT(images[i], weight, w);
LUT(images[i], response, im);
LUT(lutImages[i], weight_lut, w);
LUT(lutImages[i], response, im);
result += times.at<float>((int)i) * w.mul(im);
wsum += times.at<float>((int)i) * times.at<float>((int)i) * w;

Some files were not shown because too many files have changed in this diff Show More