diff --git a/3rdparty/carotene/CMakeLists.txt b/3rdparty/carotene/CMakeLists.txt index ebcdf1a9f6..aa95956e7f 100644 --- a/3rdparty/carotene/CMakeLists.txt +++ b/3rdparty/carotene/CMakeLists.txt @@ -42,6 +42,14 @@ endif() if(WITH_NEON) target_compile_definitions(carotene_objs PRIVATE "-DWITH_NEON") + if(NOT DEFINED CAROTENE_NEON_ARCH ) + elseif(CAROTENE_NEON_ARCH EQUAL 8) + target_compile_definitions(carotene_objs PRIVATE "-DCAROTENE_NEON_ARCH=8") + elseif(CAROTENE_NEON_ARCH EQUAL 7) + target_compile_definitions(carotene_objs PRIVATE "-DCAROTENE_NEON_ARCH=7") + else() + target_compile_definitions(carotene_objs PRIVATE "-DCAROTENE_NEON_ARCH=0") + endif() endif() # we add dummy file to fix XCode build diff --git a/3rdparty/carotene/src/add_weighted.cpp b/3rdparty/carotene/src/add_weighted.cpp index 6559b9fe53..7e2945e88c 100644 --- a/3rdparty/carotene/src/add_weighted.cpp +++ b/3rdparty/carotene/src/add_weighted.cpp @@ -39,6 +39,7 @@ #include "common.hpp" #include "vtransform.hpp" +#include "vround_helper.hpp" namespace CAROTENE_NS { @@ -106,7 +107,7 @@ template <> struct wAdd { valpha = vdupq_n_f32(_alpha); vbeta = vdupq_n_f32(_beta); - vgamma = vdupq_n_f32(_gamma + 0.5); + vgamma = vdupq_n_f32(_gamma); } void operator() (const VecTraits::vec128 & v_src0, @@ -118,7 +119,7 @@ template <> struct wAdd vs1 = vmlaq_f32(vgamma, vs1, valpha); vs1 = vmlaq_f32(vs1, vs2, vbeta); - v_dst = vcvtq_s32_f32(vs1); + v_dst = vroundq_s32_f32(vs1); } void operator() (const VecTraits::vec64 & v_src0, @@ -130,7 +131,7 @@ template <> struct wAdd vs1 = vmla_f32(vget_low(vgamma), vs1, vget_low(valpha)); vs1 = vmla_f32(vs1, vs2, vget_low(vbeta)); - v_dst = vcvt_s32_f32(vs1); + v_dst = vround_s32_f32(vs1); } void operator() (const s32 * src0, const s32 * src1, s32 * dst) const @@ -150,7 +151,7 @@ template <> struct wAdd { valpha = vdupq_n_f32(_alpha); vbeta = vdupq_n_f32(_beta); - vgamma = vdupq_n_f32(_gamma + 0.5); + vgamma = vdupq_n_f32(_gamma); } void operator() (const VecTraits::vec128 & v_src0, @@ -162,7 +163,7 @@ template <> struct wAdd vs1 = vmlaq_f32(vgamma, vs1, valpha); vs1 = vmlaq_f32(vs1, vs2, vbeta); - v_dst = vcvtq_u32_f32(vs1); + v_dst = vroundq_u32_f32(vs1); } void operator() (const VecTraits::vec64 & v_src0, @@ -174,7 +175,7 @@ template <> struct wAdd vs1 = vmla_f32(vget_low(vgamma), vs1, vget_low(valpha)); vs1 = vmla_f32(vs1, vs2, vget_low(vbeta)); - v_dst = vcvt_u32_f32(vs1); + v_dst = vround_u32_f32(vs1); } void operator() (const u32 * src0, const u32 * src1, u32 * dst) const diff --git a/3rdparty/carotene/src/blur.cpp b/3rdparty/carotene/src/blur.cpp index 21689a2bd3..30c1f8a729 100644 --- a/3rdparty/carotene/src/blur.cpp +++ b/3rdparty/carotene/src/blur.cpp @@ -41,6 +41,7 @@ #include "common.hpp" #include "saturate_cast.hpp" +#include "vround_helper.hpp" namespace CAROTENE_NS { @@ -198,7 +199,6 @@ void blur3x3(const Size2D &size, s32 cn, //#define FLOAT_VARIANT_1_9 #ifdef FLOAT_VARIANT_1_9 float32x4_t v1_9 = vdupq_n_f32 (1.0/9.0); - float32x4_t v0_5 = vdupq_n_f32 (.5); #else const int16x8_t vScale = vmovq_n_s16(3640); #endif @@ -283,8 +283,8 @@ void blur3x3(const Size2D &size, s32 cn, uint32x4_t tres2 = vmovl_u16(vget_high_u16(t0)); float32x4_t vf1 = vmulq_f32(v1_9, vcvtq_f32_u32(tres1)); float32x4_t vf2 = vmulq_f32(v1_9, vcvtq_f32_u32(tres2)); - tres1 = vcvtq_u32_f32(vaddq_f32(vf1, v0_5)); - tres2 = vcvtq_u32_f32(vaddq_f32(vf2, v0_5)); + tres1 = internal::vroundq_u32_f32(vf1); + tres2 = internal::vroundq_u32_f32(vf2); t0 = vcombine_u16(vmovn_u32(tres1),vmovn_u32(tres2)); vst1_u8(drow + x - 8, vmovn_u16(t0)); #else @@ -445,8 +445,8 @@ void blur3x3(const Size2D &size, s32 cn, uint32x4_t tres2 = vmovl_u16(vget_high_u16(t0)); float32x4_t vf1 = vmulq_f32(v1_9, vcvtq_f32_u32(tres1)); float32x4_t vf2 = vmulq_f32(v1_9, vcvtq_f32_u32(tres2)); - tres1 = vcvtq_u32_f32(vaddq_f32(vf1, v0_5)); - tres2 = vcvtq_u32_f32(vaddq_f32(vf2, v0_5)); + tres1 = internal::vroundq_u32_f32(vf1); + tres2 = internal::vroundq_u32_f32(vf2); t0 = vcombine_u16(vmovn_u32(tres1),vmovn_u32(tres2)); vst1_u8(drow + x - 8, vmovn_u16(t0)); #else @@ -508,7 +508,6 @@ void blur5x5(const Size2D &size, s32 cn, #define FLOAT_VARIANT_1_25 #ifdef FLOAT_VARIANT_1_25 float32x4_t v1_25 = vdupq_n_f32 (1.0f/25.0f); - float32x4_t v0_5 = vdupq_n_f32 (.5f); #else const int16x8_t vScale = vmovq_n_s16(1310); #endif @@ -752,8 +751,8 @@ void blur5x5(const Size2D &size, s32 cn, uint32x4_t tres2 = vmovl_u16(vget_high_u16(t0)); float32x4_t vf1 = vmulq_f32(v1_25, vcvtq_f32_u32(tres1)); float32x4_t vf2 = vmulq_f32(v1_25, vcvtq_f32_u32(tres2)); - tres1 = vcvtq_u32_f32(vaddq_f32(vf1, v0_5)); - tres2 = vcvtq_u32_f32(vaddq_f32(vf2, v0_5)); + tres1 = internal::vroundq_u32_f32(vf1); + tres2 = internal::vroundq_u32_f32(vf2); t0 = vcombine_u16(vmovn_u32(tres1),vmovn_u32(tres2)); vst1_u8(drow + x - 8, vmovn_u16(t0)); #else diff --git a/3rdparty/carotene/src/colorconvert.cpp b/3rdparty/carotene/src/colorconvert.cpp index a8aef9b722..752c65146a 100644 --- a/3rdparty/carotene/src/colorconvert.cpp +++ b/3rdparty/carotene/src/colorconvert.cpp @@ -40,6 +40,7 @@ #include "common.hpp" #include "saturate_cast.hpp" +#include "vround_helper.hpp" namespace CAROTENE_NS { @@ -1166,17 +1167,10 @@ inline uint8x8x3_t convertToHSV(const uint8x8_t vR, const uint8x8_t vG, const ui vSt3 = vmulq_f32(vHF1, vDivTab); vSt4 = vmulq_f32(vHF2, vDivTab); - float32x4_t bias = vdupq_n_f32(0.5f); - - vSt1 = vaddq_f32(vSt1, bias); - vSt2 = vaddq_f32(vSt2, bias); - vSt3 = vaddq_f32(vSt3, bias); - vSt4 = vaddq_f32(vSt4, bias); - - uint32x4_t vRes1 = vcvtq_u32_f32(vSt1); - uint32x4_t vRes2 = vcvtq_u32_f32(vSt2); - uint32x4_t vRes3 = vcvtq_u32_f32(vSt3); - uint32x4_t vRes4 = vcvtq_u32_f32(vSt4); + uint32x4_t vRes1 = internal::vroundq_u32_f32(vSt1); + uint32x4_t vRes2 = internal::vroundq_u32_f32(vSt2); + uint32x4_t vRes3 = internal::vroundq_u32_f32(vSt3); + uint32x4_t vRes4 = internal::vroundq_u32_f32(vSt4); int32x4_t vH_L = vmovl_s16(vget_low_s16(vDiff4)); int32x4_t vH_H = vmovl_s16(vget_high_s16(vDiff4)); diff --git a/3rdparty/carotene/src/common.hpp b/3rdparty/carotene/src/common.hpp index 823ddf1ccf..b9de371a6a 100644 --- a/3rdparty/carotene/src/common.hpp +++ b/3rdparty/carotene/src/common.hpp @@ -58,6 +58,17 @@ namespace CAROTENE_NS { namespace internal { +#ifndef CAROTENE_NEON_ARCH +# if defined(__aarch64__) || defined(__aarch32__) +# define CAROTENE_NEON_ARCH 8 +# else +# define CAROTENE_NEON_ARCH 7 +# endif +#endif +#if ( !defined(__aarch64__) && !defined(__aarch32__) ) && (CAROTENE_NEON_ARCH == 8 ) +# error("ARMv7 doen't support A32/A64 Neon instructions") +#endif + inline void prefetch(const void *ptr, size_t offset = 32*10) { #if defined __GNUC__ diff --git a/3rdparty/carotene/src/convert_scale.cpp b/3rdparty/carotene/src/convert_scale.cpp index d599d24c1e..f88dbea182 100644 --- a/3rdparty/carotene/src/convert_scale.cpp +++ b/3rdparty/carotene/src/convert_scale.cpp @@ -38,6 +38,7 @@ */ #include "common.hpp" +#include "vround_helper.hpp" namespace CAROTENE_NS { @@ -185,7 +186,7 @@ CVTS_FUNC1(u8, 16, #else CVTS_FUNC1(u8, 16, float32x4_t vscale = vdupq_n_f32((f32)alpha); - float32x4_t vshift = vdupq_n_f32((f32)beta + 0.5f);, + float32x4_t vshift = vdupq_n_f32((f32)beta);, { for (size_t i = 0; i < w; i += 16) { @@ -209,10 +210,10 @@ CVTS_FUNC1(u8, 16, vline2_f32 = vaddq_f32(vline2_f32, vshift); vline3_f32 = vaddq_f32(vline3_f32, vshift); vline4_f32 = vaddq_f32(vline4_f32, vshift); - int32x4_t vline1_s32 = vcvtq_s32_f32(vline1_f32); - int32x4_t vline2_s32 = vcvtq_s32_f32(vline2_f32); - int32x4_t vline3_s32 = vcvtq_s32_f32(vline3_f32); - int32x4_t vline4_s32 = vcvtq_s32_f32(vline4_f32); + int32x4_t vline1_s32 = internal::vroundq_s32_f32(vline1_f32); + int32x4_t vline2_s32 = internal::vroundq_s32_f32(vline2_f32); + int32x4_t vline3_s32 = internal::vroundq_s32_f32(vline3_f32); + int32x4_t vline4_s32 = internal::vroundq_s32_f32(vline4_f32); uint16x8_t vRes1_u16 = vcombine_u16(vqmovun_s32(vline1_s32), vqmovun_s32(vline2_s32)); uint16x8_t vRes2_u16 = vcombine_u16(vqmovun_s32(vline3_s32), vqmovun_s32(vline4_s32)); vst1q_u8(_dst + i, vcombine_u8(vqmovn_u16(vRes1_u16), vqmovn_u16(vRes2_u16))); @@ -270,7 +271,7 @@ CVTS_FUNC(u8, s8, 16, #else CVTS_FUNC(u8, s8, 16, float32x4_t vscale = vdupq_n_f32((f32)alpha); - float32x4_t vshift = vdupq_n_f32((f32)beta + 0.5f);, + float32x4_t vshift = vdupq_n_f32((f32)beta);, { for (size_t i = 0; i < w; i += 16) { @@ -294,10 +295,10 @@ CVTS_FUNC(u8, s8, 16, vline2_f32 = vaddq_f32(vline2_f32, vshift); vline3_f32 = vaddq_f32(vline3_f32, vshift); vline4_f32 = vaddq_f32(vline4_f32, vshift); - int32x4_t vline1_s32 = vcvtq_s32_f32(vline1_f32); - int32x4_t vline2_s32 = vcvtq_s32_f32(vline2_f32); - int32x4_t vline3_s32 = vcvtq_s32_f32(vline3_f32); - int32x4_t vline4_s32 = vcvtq_s32_f32(vline4_f32); + int32x4_t vline1_s32 = internal::vroundq_s32_f32(vline1_f32); + int32x4_t vline2_s32 = internal::vroundq_s32_f32(vline2_f32); + int32x4_t vline3_s32 = internal::vroundq_s32_f32(vline3_f32); + int32x4_t vline4_s32 = internal::vroundq_s32_f32(vline4_f32); int16x8_t vRes1_u16 = vcombine_s16(vqmovn_s32(vline1_s32), vqmovn_s32(vline2_s32)); int16x8_t vRes2_u16 = vcombine_s16(vqmovn_s32(vline3_s32), vqmovn_s32(vline4_s32)); vst1q_s8(_dst + i, vcombine_s8(vqmovn_s16(vRes1_u16), vqmovn_s16(vRes2_u16))); @@ -355,7 +356,7 @@ CVTS_FUNC(u8, u16, 16, #else CVTS_FUNC(u8, u16, 16, float32x4_t vscale = vdupq_n_f32((f32)alpha); - float32x4_t vshift = vdupq_n_f32((f32)beta + 0.5f);, + float32x4_t vshift = vdupq_n_f32((f32)beta);, { for (size_t i = 0; i < w; i += 16) { @@ -379,10 +380,10 @@ CVTS_FUNC(u8, u16, 16, vline2_f32 = vaddq_f32(vline2_f32, vshift); vline3_f32 = vaddq_f32(vline3_f32, vshift); vline4_f32 = vaddq_f32(vline4_f32, vshift); - int32x4_t vline1_s32 = vcvtq_s32_f32(vline1_f32); - int32x4_t vline2_s32 = vcvtq_s32_f32(vline2_f32); - int32x4_t vline3_s32 = vcvtq_s32_f32(vline3_f32); - int32x4_t vline4_s32 = vcvtq_s32_f32(vline4_f32); + int32x4_t vline1_s32 = internal::vroundq_s32_f32(vline1_f32); + int32x4_t vline2_s32 = internal::vroundq_s32_f32(vline2_f32); + int32x4_t vline3_s32 = internal::vroundq_s32_f32(vline3_f32); + int32x4_t vline4_s32 = internal::vroundq_s32_f32(vline4_f32); vst1q_u16(_dst + i + 0, vcombine_u16(vqmovun_s32(vline1_s32), vqmovun_s32(vline2_s32))); vst1q_u16(_dst + i + 8, vcombine_u16(vqmovun_s32(vline3_s32), vqmovun_s32(vline4_s32))); } @@ -439,7 +440,7 @@ CVTS_FUNC(u8, s16, 16, #else CVTS_FUNC(u8, s16, 16, float32x4_t vscale = vdupq_n_f32((f32)alpha); - float32x4_t vshift = vdupq_n_f32((f32)beta + 0.5f);, + float32x4_t vshift = vdupq_n_f32((f32)beta);, { for (size_t i = 0; i < w; i += 16) { @@ -463,10 +464,10 @@ CVTS_FUNC(u8, s16, 16, vline2_f32 = vaddq_f32(vline2_f32, vshift); vline3_f32 = vaddq_f32(vline3_f32, vshift); vline4_f32 = vaddq_f32(vline4_f32, vshift); - int32x4_t vline1_s32 = vcvtq_s32_f32(vline1_f32); - int32x4_t vline2_s32 = vcvtq_s32_f32(vline2_f32); - int32x4_t vline3_s32 = vcvtq_s32_f32(vline3_f32); - int32x4_t vline4_s32 = vcvtq_s32_f32(vline4_f32); + int32x4_t vline1_s32 = internal::vroundq_s32_f32(vline1_f32); + int32x4_t vline2_s32 = internal::vroundq_s32_f32(vline2_f32); + int32x4_t vline3_s32 = internal::vroundq_s32_f32(vline3_f32); + int32x4_t vline4_s32 = internal::vroundq_s32_f32(vline4_f32); vst1q_s16(_dst + i + 0, vcombine_s16(vqmovn_s32(vline1_s32), vqmovn_s32(vline2_s32))); vst1q_s16(_dst + i + 8, vcombine_s16(vqmovn_s32(vline3_s32), vqmovn_s32(vline4_s32))); } @@ -526,7 +527,7 @@ CVTS_FUNC(u8, s32, 16, #else CVTS_FUNC(u8, s32, 16, float32x4_t vscale = vdupq_n_f32((f32)alpha); - float32x4_t vshift = vdupq_n_f32((f32)beta + 0.5f);, + float32x4_t vshift = vdupq_n_f32((f32)beta);, { for (size_t i = 0; i < w; i += 16) { @@ -550,10 +551,10 @@ CVTS_FUNC(u8, s32, 16, vline2_f32 = vaddq_f32(vline2_f32, vshift); vline3_f32 = vaddq_f32(vline3_f32, vshift); vline4_f32 = vaddq_f32(vline4_f32, vshift); - int32x4_t vline1_s32 = vcvtq_s32_f32(vline1_f32); - int32x4_t vline2_s32 = vcvtq_s32_f32(vline2_f32); - int32x4_t vline3_s32 = vcvtq_s32_f32(vline3_f32); - int32x4_t vline4_s32 = vcvtq_s32_f32(vline4_f32); + int32x4_t vline1_s32 = internal::vroundq_s32_f32(vline1_f32); + int32x4_t vline2_s32 = internal::vroundq_s32_f32(vline2_f32); + int32x4_t vline3_s32 = internal::vroundq_s32_f32(vline3_f32); + int32x4_t vline4_s32 = internal::vroundq_s32_f32(vline4_f32); vst1q_s32(_dst + i + 0, vline1_s32); vst1q_s32(_dst + i + 4, vline2_s32); vst1q_s32(_dst + i + 8, vline3_s32); @@ -693,7 +694,7 @@ CVTS_FUNC(s8, u8, 16, #else CVTS_FUNC(s8, u8, 16, float32x4_t vscale = vdupq_n_f32((f32)alpha); - float32x4_t vshift = vdupq_n_f32((f32)beta + 0.5f);, + float32x4_t vshift = vdupq_n_f32((f32)beta);, { for (size_t i = 0; i < w; i += 16) { @@ -717,10 +718,10 @@ CVTS_FUNC(s8, u8, 16, vline2_f32 = vaddq_f32(vline2_f32, vshift); vline3_f32 = vaddq_f32(vline3_f32, vshift); vline4_f32 = vaddq_f32(vline4_f32, vshift); - vline1_s32 = vcvtq_s32_f32(vline1_f32); - vline2_s32 = vcvtq_s32_f32(vline2_f32); - vline3_s32 = vcvtq_s32_f32(vline3_f32); - vline4_s32 = vcvtq_s32_f32(vline4_f32); + vline1_s32 = internal::vroundq_s32_f32(vline1_f32); + vline2_s32 = internal::vroundq_s32_f32(vline2_f32); + vline3_s32 = internal::vroundq_s32_f32(vline3_f32); + vline4_s32 = internal::vroundq_s32_f32(vline4_f32); uint16x8_t vRes1_u16 = vcombine_u16(vqmovun_s32(vline1_s32), vqmovun_s32(vline2_s32)); uint16x8_t vRes2_u16 = vcombine_u16(vqmovun_s32(vline3_s32), vqmovun_s32(vline4_s32)); vst1q_u8(_dst + i, vcombine_u8(vqmovn_u16(vRes1_u16), vqmovn_u16(vRes2_u16))); @@ -778,7 +779,7 @@ CVTS_FUNC1(s8, 16, #else CVTS_FUNC1(s8, 16, float32x4_t vscale = vdupq_n_f32((f32)alpha); - float32x4_t vshift = vdupq_n_f32((f32)beta + 0.5f);, + float32x4_t vshift = vdupq_n_f32((f32)beta);, { for (size_t i = 0; i < w; i += 16) { @@ -802,10 +803,10 @@ CVTS_FUNC1(s8, 16, vline2_f32 = vaddq_f32(vline2_f32, vshift); vline3_f32 = vaddq_f32(vline3_f32, vshift); vline4_f32 = vaddq_f32(vline4_f32, vshift); - vline1_s32 = vcvtq_s32_f32(vline1_f32); - vline2_s32 = vcvtq_s32_f32(vline2_f32); - vline3_s32 = vcvtq_s32_f32(vline3_f32); - vline4_s32 = vcvtq_s32_f32(vline4_f32); + vline1_s32 = internal::vroundq_s32_f32(vline1_f32); + vline2_s32 = internal::vroundq_s32_f32(vline2_f32); + vline3_s32 = internal::vroundq_s32_f32(vline3_f32); + vline4_s32 = internal::vroundq_s32_f32(vline4_f32); int16x8_t vRes1_s16 = vcombine_s16(vqmovn_s32(vline1_s32), vqmovn_s32(vline2_s32)); int16x8_t vRes2_s16 = vcombine_s16(vqmovn_s32(vline3_s32), vqmovn_s32(vline4_s32)); vst1q_s8(_dst + i, vcombine_s8(vqmovn_s16(vRes1_s16), vqmovn_s16(vRes2_s16))); @@ -863,7 +864,7 @@ CVTS_FUNC(s8, u16, 16, #else CVTS_FUNC(s8, u16, 16, float32x4_t vscale = vdupq_n_f32((f32)alpha); - float32x4_t vshift = vdupq_n_f32((f32)beta + 0.5f);, + float32x4_t vshift = vdupq_n_f32((f32)beta);, { for (size_t i = 0; i < w; i += 16) { @@ -887,10 +888,10 @@ CVTS_FUNC(s8, u16, 16, vline2_f32 = vaddq_f32(vline2_f32, vshift); vline3_f32 = vaddq_f32(vline3_f32, vshift); vline4_f32 = vaddq_f32(vline4_f32, vshift); - vline1_s32 = vcvtq_s32_f32(vline1_f32); - vline2_s32 = vcvtq_s32_f32(vline2_f32); - vline3_s32 = vcvtq_s32_f32(vline3_f32); - vline4_s32 = vcvtq_s32_f32(vline4_f32); + vline1_s32 = internal::vroundq_s32_f32(vline1_f32); + vline2_s32 = internal::vroundq_s32_f32(vline2_f32); + vline3_s32 = internal::vroundq_s32_f32(vline3_f32); + vline4_s32 = internal::vroundq_s32_f32(vline4_f32); uint16x8_t vRes1_u16 = vcombine_u16(vqmovun_s32(vline1_s32), vqmovun_s32(vline2_s32)); uint16x8_t vRes2_u16 = vcombine_u16(vqmovun_s32(vline3_s32), vqmovun_s32(vline4_s32)); vst1q_u16(_dst + i + 0, vRes1_u16); @@ -949,7 +950,7 @@ CVTS_FUNC(s8, s16, 16, #else CVTS_FUNC(s8, s16, 16, float32x4_t vscale = vdupq_n_f32((f32)alpha); - float32x4_t vshift = vdupq_n_f32((f32)beta + 0.5f);, + float32x4_t vshift = vdupq_n_f32((f32)beta);, { for (size_t i = 0; i < w; i += 16) { @@ -973,10 +974,10 @@ CVTS_FUNC(s8, s16, 16, vline2_f32 = vaddq_f32(vline2_f32, vshift); vline3_f32 = vaddq_f32(vline3_f32, vshift); vline4_f32 = vaddq_f32(vline4_f32, vshift); - vline1_s32 = vcvtq_s32_f32(vline1_f32); - vline2_s32 = vcvtq_s32_f32(vline2_f32); - vline3_s32 = vcvtq_s32_f32(vline3_f32); - vline4_s32 = vcvtq_s32_f32(vline4_f32); + vline1_s32 = internal::vroundq_s32_f32(vline1_f32); + vline2_s32 = internal::vroundq_s32_f32(vline2_f32); + vline3_s32 = internal::vroundq_s32_f32(vline3_f32); + vline4_s32 = internal::vroundq_s32_f32(vline4_f32); int16x8_t vRes1_s16 = vcombine_s16(vqmovn_s32(vline1_s32), vqmovn_s32(vline2_s32)); int16x8_t vRes2_s16 = vcombine_s16(vqmovn_s32(vline3_s32), vqmovn_s32(vline4_s32)); vst1q_s16(_dst + i + 0, vRes1_s16); @@ -1038,7 +1039,7 @@ CVTS_FUNC(s8, s32, 16, #else CVTS_FUNC(s8, s32, 16, float32x4_t vscale = vdupq_n_f32((f32)alpha); - float32x4_t vshift = vdupq_n_f32((f32)beta + 0.5f);, + float32x4_t vshift = vdupq_n_f32((f32)beta);, { for (size_t i = 0; i < w; i += 16) { @@ -1062,10 +1063,10 @@ CVTS_FUNC(s8, s32, 16, vline2_f32 = vaddq_f32(vline2_f32, vshift); vline3_f32 = vaddq_f32(vline3_f32, vshift); vline4_f32 = vaddq_f32(vline4_f32, vshift); - vline1_s32 = vcvtq_s32_f32(vline1_f32); - vline2_s32 = vcvtq_s32_f32(vline2_f32); - vline3_s32 = vcvtq_s32_f32(vline3_f32); - vline4_s32 = vcvtq_s32_f32(vline4_f32); + vline1_s32 = internal::vroundq_s32_f32(vline1_f32); + vline2_s32 = internal::vroundq_s32_f32(vline2_f32); + vline3_s32 = internal::vroundq_s32_f32(vline3_f32); + vline4_s32 = internal::vroundq_s32_f32(vline4_f32); vst1q_s32(_dst + i + 0, vline1_s32); vst1q_s32(_dst + i + 4, vline2_s32); vst1q_s32(_dst + i + 8, vline3_s32); @@ -1190,7 +1191,7 @@ CVTS_FUNC(u16, u8, 16, #else CVTS_FUNC(u16, u8, 16, float32x4_t vscale = vdupq_n_f32((f32)alpha); - float32x4_t vshift = vdupq_n_f32((f32)beta + 0.5f);, + float32x4_t vshift = vdupq_n_f32((f32)beta);, { for (size_t i = 0; i < w; i += 8) { @@ -1204,8 +1205,8 @@ CVTS_FUNC(u16, u8, 16, vline2_f32 = vmulq_f32(vline2_f32, vscale); vline1_f32 = vaddq_f32(vline1_f32, vshift); vline2_f32 = vaddq_f32(vline2_f32, vshift); - int32x4_t vline1_s32 = vcvtq_s32_f32(vline1_f32); - int32x4_t vline2_s32 = vcvtq_s32_f32(vline2_f32); + int32x4_t vline1_s32 = internal::vroundq_s32_f32(vline1_f32); + int32x4_t vline2_s32 = internal::vroundq_s32_f32(vline2_f32); int16x4_t vRes1 = vqmovn_s32(vline1_s32); int16x4_t vRes2 = vqmovn_s32(vline2_s32); uint8x8_t vRes = vqmovun_s16(vcombine_s16(vRes1, vRes2)); @@ -1249,7 +1250,7 @@ CVTS_FUNC(u16, s8, 16, #else CVTS_FUNC(u16, s8, 16, float32x4_t vscale = vdupq_n_f32((f32)alpha); - float32x4_t vshift = vdupq_n_f32((f32)beta + 0.5f);, + float32x4_t vshift = vdupq_n_f32((f32)beta);, { for (size_t i = 0; i < w; i += 8) { @@ -1263,8 +1264,8 @@ CVTS_FUNC(u16, s8, 16, vline2_f32 = vmulq_f32(vline2_f32, vscale); vline1_f32 = vaddq_f32(vline1_f32, vshift); vline2_f32 = vaddq_f32(vline2_f32, vshift); - int32x4_t vline1_s32 = vcvtq_s32_f32(vline1_f32); - int32x4_t vline2_s32 = vcvtq_s32_f32(vline2_f32); + int32x4_t vline1_s32 = internal::vroundq_s32_f32(vline1_f32); + int32x4_t vline2_s32 = internal::vroundq_s32_f32(vline2_f32); int16x4_t vRes1 = vqmovn_s32(vline1_s32); int16x4_t vRes2 = vqmovn_s32(vline2_s32); int8x8_t vRes = vqmovn_s16(vcombine_s16(vRes1, vRes2)); @@ -1307,7 +1308,7 @@ CVTS_FUNC1(u16, 16, #else CVTS_FUNC1(u16, 16, float32x4_t vscale = vdupq_n_f32((f32)alpha); - float32x4_t vshift = vdupq_n_f32((f32)beta + 0.5f);, + float32x4_t vshift = vdupq_n_f32((f32)beta);, { for (size_t i = 0; i < w; i += 8) { @@ -1321,8 +1322,8 @@ CVTS_FUNC1(u16, 16, vline2_f32 = vmulq_f32(vline2_f32, vscale); vline1_f32 = vaddq_f32(vline1_f32, vshift); vline2_f32 = vaddq_f32(vline2_f32, vshift); - int32x4_t vline1_s32 = vcvtq_s32_f32(vline1_f32); - int32x4_t vline2_s32 = vcvtq_s32_f32(vline2_f32); + int32x4_t vline1_s32 = internal::vroundq_s32_f32(vline1_f32); + int32x4_t vline2_s32 = internal::vroundq_s32_f32(vline2_f32); uint16x4_t vRes1 = vqmovun_s32(vline1_s32); uint16x4_t vRes2 = vqmovun_s32(vline2_s32); vst1q_u16(_dst + i, vcombine_u16(vRes1, vRes2)); @@ -1364,7 +1365,7 @@ CVTS_FUNC(u16, s16, 8, #else CVTS_FUNC(u16, s16, 8, float32x4_t vscale = vdupq_n_f32((f32)alpha); - float32x4_t vshift = vdupq_n_f32((f32)beta + 0.5f);, + float32x4_t vshift = vdupq_n_f32((f32)beta);, { for (size_t i = 0; i < w; i += 8) { @@ -1378,8 +1379,8 @@ CVTS_FUNC(u16, s16, 8, vline2_f32 = vmulq_f32(vline2_f32, vscale); vline1_f32 = vaddq_f32(vline1_f32, vshift); vline2_f32 = vaddq_f32(vline2_f32, vshift); - int32x4_t vline1_s32 = vcvtq_s32_f32(vline1_f32); - int32x4_t vline2_s32 = vcvtq_s32_f32(vline2_f32); + int32x4_t vline1_s32 = internal::vroundq_s32_f32(vline1_f32); + int32x4_t vline2_s32 = internal::vroundq_s32_f32(vline2_f32); int16x4_t vRes1 = vqmovn_s32(vline1_s32); int16x4_t vRes2 = vqmovn_s32(vline2_s32); vst1q_s16(_dst + i, vcombine_s16(vRes1, vRes2)); @@ -1421,7 +1422,7 @@ CVTS_FUNC(u16, s32, 8, #else CVTS_FUNC(u16, s32, 8, float32x4_t vscale = vdupq_n_f32((f32)alpha); - float32x4_t vshift = vdupq_n_f32((f32)beta + 0.5f);, + float32x4_t vshift = vdupq_n_f32((f32)beta);, { for (size_t i = 0; i < w; i += 8) { @@ -1435,8 +1436,8 @@ CVTS_FUNC(u16, s32, 8, vline2_f32 = vmulq_f32(vline2_f32, vscale); vline1_f32 = vaddq_f32(vline1_f32, vshift); vline2_f32 = vaddq_f32(vline2_f32, vshift); - int32x4_t vline1_s32 = vcvtq_s32_f32(vline1_f32); - int32x4_t vline2_s32 = vcvtq_s32_f32(vline2_f32); + int32x4_t vline1_s32 = internal::vroundq_s32_f32(vline1_f32); + int32x4_t vline2_s32 = internal::vroundq_s32_f32(vline2_f32); vst1q_s32(_dst + i + 0, vline1_s32); vst1q_s32(_dst + i + 4, vline2_s32); } @@ -1530,7 +1531,7 @@ CVTS_FUNC(s16, u8, 16, #else CVTS_FUNC(s16, u8, 16, float32x4_t vscale = vdupq_n_f32((f32)alpha); - float32x4_t vshift = vdupq_n_f32((f32)beta + 0.5f);, + float32x4_t vshift = vdupq_n_f32((f32)beta);, { for (size_t i = 0; i < w; i += 8) { @@ -1544,8 +1545,8 @@ CVTS_FUNC(s16, u8, 16, vline2_f32 = vmulq_f32(vline2_f32, vscale); vline1_f32 = vaddq_f32(vline1_f32, vshift); vline2_f32 = vaddq_f32(vline2_f32, vshift); - vline1_s32 = vcvtq_s32_f32(vline1_f32); - vline2_s32 = vcvtq_s32_f32(vline2_f32); + vline1_s32 = internal::vroundq_s32_f32(vline1_f32); + vline2_s32 = internal::vroundq_s32_f32(vline2_f32); int16x4_t vRes1 = vqmovn_s32(vline1_s32); int16x4_t vRes2 = vqmovn_s32(vline2_s32); uint8x8_t vRes = vqmovun_s16(vcombine_s16(vRes1, vRes2)); @@ -1589,7 +1590,7 @@ CVTS_FUNC(s16, s8, 16, #else CVTS_FUNC(s16, s8, 16, float32x4_t vscale = vdupq_n_f32((f32)alpha); - float32x4_t vshift = vdupq_n_f32((f32)beta + 0.5f);, + float32x4_t vshift = vdupq_n_f32((f32)beta);, { for (size_t i = 0; i < w; i += 8) { @@ -1603,8 +1604,8 @@ CVTS_FUNC(s16, s8, 16, vline2_f32 = vmulq_f32(vline2_f32, vscale); vline1_f32 = vaddq_f32(vline1_f32, vshift); vline2_f32 = vaddq_f32(vline2_f32, vshift); - vline1_s32 = vcvtq_s32_f32(vline1_f32); - vline2_s32 = vcvtq_s32_f32(vline2_f32); + vline1_s32 = internal::vroundq_s32_f32(vline1_f32); + vline2_s32 = internal::vroundq_s32_f32(vline2_f32); int16x4_t vRes1 = vqmovn_s32(vline1_s32); int16x4_t vRes2 = vqmovn_s32(vline2_s32); int8x8_t vRes = vqmovn_s16(vcombine_s16(vRes1, vRes2)); @@ -1647,7 +1648,7 @@ CVTS_FUNC(s16, u16, 8, #else CVTS_FUNC(s16, u16, 8, float32x4_t vscale = vdupq_n_f32((f32)alpha); - float32x4_t vshift = vdupq_n_f32((f32)beta + 0.5f);, + float32x4_t vshift = vdupq_n_f32((f32)beta);, { for (size_t i = 0; i < w; i += 8) { @@ -1661,8 +1662,8 @@ CVTS_FUNC(s16, u16, 8, vline2_f32 = vmulq_f32(vline2_f32, vscale); vline1_f32 = vaddq_f32(vline1_f32, vshift); vline2_f32 = vaddq_f32(vline2_f32, vshift); - vline1_s32 = vcvtq_s32_f32(vline1_f32); - vline2_s32 = vcvtq_s32_f32(vline2_f32); + vline1_s32 = internal::vroundq_s32_f32(vline1_f32); + vline2_s32 = internal::vroundq_s32_f32(vline2_f32); uint16x4_t vRes1 = vqmovun_s32(vline1_s32); uint16x4_t vRes2 = vqmovun_s32(vline2_s32); vst1q_u16(_dst + i, vcombine_u16(vRes1, vRes2)); @@ -1704,7 +1705,7 @@ CVTS_FUNC1(s16, 16, #else CVTS_FUNC1(s16, 16, float32x4_t vscale = vdupq_n_f32((f32)alpha); - float32x4_t vshift = vdupq_n_f32((f32)beta + 0.5f);, + float32x4_t vshift = vdupq_n_f32((f32)beta);, { for (size_t i = 0; i < w; i += 8) { @@ -1718,8 +1719,8 @@ CVTS_FUNC1(s16, 16, vline2_f32 = vmulq_f32(vline2_f32, vscale); vline1_f32 = vaddq_f32(vline1_f32, vshift); vline2_f32 = vaddq_f32(vline2_f32, vshift); - vline1_s32 = vcvtq_s32_f32(vline1_f32); - vline2_s32 = vcvtq_s32_f32(vline2_f32); + vline1_s32 = internal::vroundq_s32_f32(vline1_f32); + vline2_s32 = internal::vroundq_s32_f32(vline2_f32); int16x4_t vRes1 = vqmovn_s32(vline1_s32); int16x4_t vRes2 = vqmovn_s32(vline2_s32); vst1q_s16(_dst + i, vcombine_s16(vRes1, vRes2)); @@ -1761,7 +1762,7 @@ CVTS_FUNC(s16, s32, 8, #else CVTS_FUNC(s16, s32, 8, float32x4_t vscale = vdupq_n_f32((f32)alpha); - float32x4_t vshift = vdupq_n_f32((f32)beta + 0.5f);, + float32x4_t vshift = vdupq_n_f32((f32)beta);, { for (size_t i = 0; i < w; i += 8) { @@ -1775,8 +1776,8 @@ CVTS_FUNC(s16, s32, 8, vline2_f32 = vmulq_f32(vline2_f32, vscale); vline1_f32 = vaddq_f32(vline1_f32, vshift); vline2_f32 = vaddq_f32(vline2_f32, vshift); - vline1_s32 = vcvtq_s32_f32(vline1_f32); - vline2_s32 = vcvtq_s32_f32(vline2_f32); + vline1_s32 = internal::vroundq_s32_f32(vline1_f32); + vline2_s32 = internal::vroundq_s32_f32(vline2_f32); vst1q_s32(_dst + i + 0, vline1_s32); vst1q_s32(_dst + i + 4, vline2_s32); } @@ -1870,7 +1871,7 @@ CVTS_FUNC(s32, u8, 8, #else CVTS_FUNC(s32, u8, 8, float32x4_t vscale = vdupq_n_f32((f32)alpha); - float32x4_t vshift = vdupq_n_f32((f32)beta + 0.5f);, + float32x4_t vshift = vdupq_n_f32((f32)beta);, { for (size_t i = 0; i < w; i += 8) { @@ -1883,8 +1884,8 @@ CVTS_FUNC(s32, u8, 8, vline2_f32 = vmulq_f32(vline2_f32, vscale); vline1_f32 = vaddq_f32(vline1_f32, vshift); vline2_f32 = vaddq_f32(vline2_f32, vshift); - vline1_s32 = vcvtq_s32_f32(vline1_f32); - vline2_s32 = vcvtq_s32_f32(vline2_f32); + vline1_s32 = internal::vroundq_s32_f32(vline1_f32); + vline2_s32 = internal::vroundq_s32_f32(vline2_f32); uint16x4_t vRes1 = vqmovun_s32(vline1_s32); uint16x4_t vRes2 = vqmovun_s32(vline2_s32); uint8x8_t vRes = vqmovn_u16(vcombine_u16(vRes1, vRes2)); @@ -1928,7 +1929,7 @@ CVTS_FUNC(s32, s8, 8, #else CVTS_FUNC(s32, s8, 8, float32x4_t vscale = vdupq_n_f32((f32)alpha); - float32x4_t vshift = vdupq_n_f32((f32)beta + 0.5f);, + float32x4_t vshift = vdupq_n_f32((f32)beta);, { for (size_t i = 0; i < w; i += 8) { @@ -1941,8 +1942,8 @@ CVTS_FUNC(s32, s8, 8, vline2_f32 = vmulq_f32(vline2_f32, vscale); vline1_f32 = vaddq_f32(vline1_f32, vshift); vline2_f32 = vaddq_f32(vline2_f32, vshift); - vline1_s32 = vcvtq_s32_f32(vline1_f32); - vline2_s32 = vcvtq_s32_f32(vline2_f32); + vline1_s32 = internal::vroundq_s32_f32(vline1_f32); + vline2_s32 = internal::vroundq_s32_f32(vline2_f32); int16x4_t vRes1 = vqmovn_s32(vline1_s32); int16x4_t vRes2 = vqmovn_s32(vline2_s32); int8x8_t vRes = vqmovn_s16(vcombine_s16(vRes1, vRes2)); @@ -1985,7 +1986,7 @@ CVTS_FUNC(s32, u16, 8, #else CVTS_FUNC(s32, u16, 8, float32x4_t vscale = vdupq_n_f32((f32)alpha); - float32x4_t vshift = vdupq_n_f32((f32)beta + 0.5f);, + float32x4_t vshift = vdupq_n_f32((f32)beta);, { for (size_t i = 0; i < w; i += 8) { @@ -1998,8 +1999,8 @@ CVTS_FUNC(s32, u16, 8, vline2_f32 = vmulq_f32(vline2_f32, vscale); vline1_f32 = vaddq_f32(vline1_f32, vshift); vline2_f32 = vaddq_f32(vline2_f32, vshift); - vline1_s32 = vcvtq_s32_f32(vline1_f32); - vline2_s32 = vcvtq_s32_f32(vline2_f32); + vline1_s32 = internal::vroundq_s32_f32(vline1_f32); + vline2_s32 = internal::vroundq_s32_f32(vline2_f32); uint16x4_t vRes1 = vqmovun_s32(vline1_s32); uint16x4_t vRes2 = vqmovun_s32(vline2_s32); vst1q_u16(_dst + i, vcombine_u16(vRes1, vRes2)); @@ -2041,7 +2042,7 @@ CVTS_FUNC(s32, s16, 8, #else CVTS_FUNC(s32, s16, 8, float32x4_t vscale = vdupq_n_f32((f32)alpha); - float32x4_t vshift = vdupq_n_f32((f32)beta + 0.5f);, + float32x4_t vshift = vdupq_n_f32((f32)beta);, { for (size_t i = 0; i < w; i += 8) { @@ -2054,8 +2055,8 @@ CVTS_FUNC(s32, s16, 8, vline2_f32 = vmulq_f32(vline2_f32, vscale); vline1_f32 = vaddq_f32(vline1_f32, vshift); vline2_f32 = vaddq_f32(vline2_f32, vshift); - vline1_s32 = vcvtq_s32_f32(vline1_f32); - vline2_s32 = vcvtq_s32_f32(vline2_f32); + vline1_s32 = internal::vroundq_s32_f32(vline1_f32); + vline2_s32 = internal::vroundq_s32_f32(vline2_f32); int16x4_t vRes1 = vqmovn_s32(vline1_s32); int16x4_t vRes2 = vqmovn_s32(vline2_s32); vst1q_s16(_dst + i, vcombine_s16(vRes1, vRes2)); @@ -2097,7 +2098,7 @@ CVTS_FUNC1(s32, 8, #else CVTS_FUNC1(s32, 8, float32x4_t vscale = vdupq_n_f32((f32)alpha); - float32x4_t vshift = vdupq_n_f32((f32)beta + 0.5f);, + float32x4_t vshift = vdupq_n_f32((f32)beta);, { for (size_t i = 0; i < w; i += 8) { @@ -2110,8 +2111,8 @@ CVTS_FUNC1(s32, 8, vline2_f32 = vmulq_f32(vline2_f32, vscale); vline1_f32 = vaddq_f32(vline1_f32, vshift); vline2_f32 = vaddq_f32(vline2_f32, vshift); - vline1_s32 = vcvtq_s32_f32(vline1_f32); - vline2_s32 = vcvtq_s32_f32(vline2_f32); + vline1_s32 = internal::vroundq_s32_f32(vline1_f32); + vline2_s32 = internal::vroundq_s32_f32(vline2_f32); vst1q_s32(_dst + i + 0, vline1_s32); vst1q_s32(_dst + i + 4, vline2_s32); } @@ -2272,7 +2273,7 @@ CVTS_FUNC(f32, s8, 8, #else CVTS_FUNC(f32, s8, 8, float32x4_t vscale = vdupq_n_f32((f32)alpha); - float32x4_t vshift = vdupq_n_f32((f32)beta + 0.5f);, + float32x4_t vshift = vdupq_n_f32((f32)beta);, { for (size_t i = 0; i < w; i += 8) { @@ -2283,8 +2284,8 @@ CVTS_FUNC(f32, s8, 8, vline2_f32 = vmulq_f32(vline2_f32, vscale); vline1_f32 = vaddq_f32(vline1_f32, vshift); vline2_f32 = vaddq_f32(vline2_f32, vshift); - int32x4_t vline1_s32 = vcvtq_s32_f32(vline1_f32); - int32x4_t vline2_s32 = vcvtq_s32_f32(vline2_f32); + int32x4_t vline1_s32 = internal::vroundq_s32_f32(vline1_f32); + int32x4_t vline2_s32 = internal::vroundq_s32_f32(vline2_f32); int16x4_t vRes1 = vqmovn_s32(vline1_s32); int16x4_t vRes2 = vqmovn_s32(vline2_s32); int8x8_t vRes = vqmovn_s16(vcombine_s16(vRes1, vRes2)); @@ -2325,7 +2326,7 @@ CVTS_FUNC(f32, u16, 8, #else CVTS_FUNC(f32, u16, 8, float32x4_t vscale = vdupq_n_f32((f32)alpha); - float32x4_t vshift = vdupq_n_f32((f32)beta + 0.5f);, + float32x4_t vshift = vdupq_n_f32((f32)beta);, { for (size_t i = 0; i < w; i += 8) { @@ -2336,8 +2337,8 @@ CVTS_FUNC(f32, u16, 8, vline2_f32 = vmulq_f32(vline2_f32, vscale); vline1_f32 = vaddq_f32(vline1_f32, vshift); vline2_f32 = vaddq_f32(vline2_f32, vshift); - uint32x4_t vline1_u32 = vcvtq_u32_f32(vline1_f32); - uint32x4_t vline2_u32 = vcvtq_u32_f32(vline2_f32); + uint32x4_t vline1_u32 = internal::vroundq_u32_f32(vline1_f32); + uint32x4_t vline2_u32 = internal::vroundq_u32_f32(vline2_f32); uint16x4_t vRes1 = vqmovn_u32(vline1_u32); uint16x4_t vRes2 = vqmovn_u32(vline2_u32); vst1q_u16(_dst + i, vcombine_u16(vRes1, vRes2)); @@ -2377,7 +2378,7 @@ CVTS_FUNC(f32, s16, 8, #else CVTS_FUNC(f32, s16, 8, float32x4_t vscale = vdupq_n_f32((f32)alpha); - float32x4_t vshift = vdupq_n_f32((f32)beta + 0.5f);, + float32x4_t vshift = vdupq_n_f32((f32)beta);, { for (size_t i = 0; i < w; i += 8) { @@ -2388,8 +2389,8 @@ CVTS_FUNC(f32, s16, 8, vline2_f32 = vmulq_f32(vline2_f32, vscale); vline1_f32 = vaddq_f32(vline1_f32, vshift); vline2_f32 = vaddq_f32(vline2_f32, vshift); - int32x4_t vline1_s32 = vcvtq_s32_f32(vline1_f32); - int32x4_t vline2_s32 = vcvtq_s32_f32(vline2_f32); + int32x4_t vline1_s32 = internal::vroundq_s32_f32(vline1_f32); + int32x4_t vline2_s32 = internal::vroundq_s32_f32(vline2_f32); int16x4_t vRes1 = vqmovn_s32(vline1_s32); int16x4_t vRes2 = vqmovn_s32(vline2_s32); vst1q_s16(_dst + i, vcombine_s16(vRes1, vRes2)); @@ -2429,7 +2430,7 @@ CVTS_FUNC(f32, s32, 8, #else CVTS_FUNC(f32, s32, 8, float32x4_t vscale = vdupq_n_f32((f32)alpha); - float32x4_t vshift = vdupq_n_f32((f32)beta + 0.5f);, + float32x4_t vshift = vdupq_n_f32((f32)beta);, { for (size_t i = 0; i < w; i += 8) { @@ -2440,8 +2441,8 @@ CVTS_FUNC(f32, s32, 8, vline2_f32 = vmulq_f32(vline2_f32, vscale); vline1_f32 = vaddq_f32(vline1_f32, vshift); vline2_f32 = vaddq_f32(vline2_f32, vshift); - int32x4_t vline1_s32 = vcvtq_s32_f32(vline1_f32); - int32x4_t vline2_s32 = vcvtq_s32_f32(vline2_f32); + int32x4_t vline1_s32 = internal::vroundq_s32_f32(vline1_f32); + int32x4_t vline2_s32 = internal::vroundq_s32_f32(vline2_f32); vst1q_s32(_dst + i + 0, vline1_s32); vst1q_s32(_dst + i + 4, vline2_s32); } diff --git a/3rdparty/carotene/src/div.cpp b/3rdparty/carotene/src/div.cpp index 38892acab3..75502c736b 100644 --- a/3rdparty/carotene/src/div.cpp +++ b/3rdparty/carotene/src/div.cpp @@ -39,6 +39,7 @@ #include "common.hpp" #include "vtransform.hpp" +#include "vround_helper.hpp" #include #include @@ -51,13 +52,6 @@ namespace { #ifdef CAROTENE_NEON -inline float32x4_t vroundq(const float32x4_t& v) -{ - const int32x4_t signMask = vdupq_n_s32(1 << 31), half = vreinterpretq_s32_f32(vdupq_n_f32(0.5f)); - float32x4_t v_addition = vreinterpretq_f32_s32(vorrq_s32(half, vandq_s32(signMask, vreinterpretq_s32_f32(v)))); - return vaddq_f32(v, v_addition); -} - template inline T divSaturateQ(const T &v1, const T &v2, const float scale) { @@ -69,17 +63,10 @@ inline T divSaturateQ(const T &v1, const T &v2, const float scale) } template <> inline int32x4_t divSaturateQ(const int32x4_t &v1, const int32x4_t &v2, const float scale) -{ return vcvtq_s32_f32(vroundq(vmulq_f32(vmulq_n_f32(vcvtq_f32_s32(v1), scale), internal::vrecpq_f32(vcvtq_f32_s32(v2))))); } +{ return internal::vroundq_s32_f32(vmulq_f32(vmulq_n_f32(vcvtq_f32_s32(v1), scale), internal::vrecpq_f32(vcvtq_f32_s32(v2)))); } template <> inline uint32x4_t divSaturateQ(const uint32x4_t &v1, const uint32x4_t &v2, const float scale) -{ return vcvtq_u32_f32(vroundq(vmulq_f32(vmulq_n_f32(vcvtq_f32_u32(v1), scale), internal::vrecpq_f32(vcvtq_f32_u32(v2))))); } - -inline float32x2_t vround(const float32x2_t& v) -{ - const int32x2_t signMask = vdup_n_s32(1 << 31), half = vreinterpret_s32_f32(vdup_n_f32(0.5f)); - float32x2_t v_addition = vreinterpret_f32_s32(vorr_s32(half, vand_s32(signMask, vreinterpret_s32_f32(v)))); - return vadd_f32(v, v_addition); -} +{ return internal::vroundq_u32_f32(vmulq_f32(vmulq_n_f32(vcvtq_f32_u32(v1), scale), internal::vrecpq_f32(vcvtq_f32_u32(v2)))); } template inline T divSaturate(const T &v1, const T &v2, const float scale) @@ -88,10 +75,10 @@ inline T divSaturate(const T &v1, const T &v2, const float scale) } template <> inline int32x2_t divSaturate(const int32x2_t &v1, const int32x2_t &v2, const float scale) -{ return vcvt_s32_f32(vround(vmul_f32(vmul_n_f32(vcvt_f32_s32(v1), scale), internal::vrecp_f32(vcvt_f32_s32(v2))))); } +{ return internal::vround_s32_f32(vmul_f32(vmul_n_f32(vcvt_f32_s32(v1), scale), internal::vrecp_f32(vcvt_f32_s32(v2)))); } template <> inline uint32x2_t divSaturate(const uint32x2_t &v1, const uint32x2_t &v2, const float scale) -{ return vcvt_u32_f32(vround(vmul_f32(vmul_n_f32(vcvt_f32_u32(v1), scale), internal::vrecp_f32(vcvt_f32_u32(v2))))); } +{ return internal::vround_u32_f32(vmul_f32(vmul_n_f32(vcvt_f32_u32(v1), scale), internal::vrecp_f32(vcvt_f32_u32(v2)))); } template @@ -157,8 +144,8 @@ void div(const Size2D &size, if (scale == 0.0f || (std::numeric_limits::is_integer && - (scale * std::numeric_limits::max()) < 1.0f && - (scale * std::numeric_limits::max()) > -1.0f)) + (scale * static_cast(std::numeric_limits::max())) < 1.0f && + (scale * static_cast(std::numeric_limits::max())) > -1.0f)) { for (size_t y = 0; y < size.height; ++y) { diff --git a/3rdparty/carotene/src/phase.cpp b/3rdparty/carotene/src/phase.cpp index 141b1e864a..48dea2a860 100644 --- a/3rdparty/carotene/src/phase.cpp +++ b/3rdparty/carotene/src/phase.cpp @@ -41,6 +41,7 @@ #include #include "common.hpp" +#include "vround_helper.hpp" namespace CAROTENE_NS { @@ -121,8 +122,6 @@ void phase(const Size2D &size, size_t roiw16 = size.width >= 15 ? size.width - 15 : 0; size_t roiw8 = size.width >= 7 ? size.width - 7 : 0; - float32x4_t v_05 = vdupq_n_f32(0.5f); - for (size_t i = 0; i < size.height; ++i) { const s16 * src0 = internal::getRowPtr(src0Base, src0Stride, i); @@ -149,8 +148,8 @@ void phase(const Size2D &size, float32x4_t v_dst32f1; FASTATAN2VECTOR(v_src1_p, v_src0_p, v_dst32f1) - uint16x8_t v_dst16s0 = vcombine_u16(vmovn_u32(vcvtq_u32_f32(vaddq_f32(v_dst32f0, v_05))), - vmovn_u32(vcvtq_u32_f32(vaddq_f32(v_dst32f1, v_05)))); + uint16x8_t v_dst16s0 = vcombine_u16(vmovn_u32(internal::vroundq_u32_f32(v_dst32f0)), + vmovn_u32(internal::vroundq_u32_f32(v_dst32f1))); // 1 v_src0_p = vcvtq_f32_s32(vmovl_s16(vget_low_s16(v_src01))); @@ -161,8 +160,8 @@ void phase(const Size2D &size, v_src1_p = vcvtq_f32_s32(vmovl_s16(vget_high_s16(v_src11))); FASTATAN2VECTOR(v_src1_p, v_src0_p, v_dst32f1) - uint16x8_t v_dst16s1 = vcombine_u16(vmovn_u32(vcvtq_u32_f32(vaddq_f32(v_dst32f0, v_05))), - vmovn_u32(vcvtq_u32_f32(vaddq_f32(v_dst32f1, v_05)))); + uint16x8_t v_dst16s1 = vcombine_u16(vmovn_u32(internal::vroundq_u32_f32(v_dst32f0)), + vmovn_u32(internal::vroundq_u32_f32(v_dst32f1))); vst1q_u8(dst + j, vcombine_u8(vmovn_u16(v_dst16s0), vmovn_u16(v_dst16s1))); @@ -182,8 +181,8 @@ void phase(const Size2D &size, float32x4_t v_dst32f1; FASTATAN2VECTOR(v_src1_p, v_src0_p, v_dst32f1) - uint16x8_t v_dst = vcombine_u16(vmovn_u32(vcvtq_u32_f32(vaddq_f32(v_dst32f0, v_05))), - vmovn_u32(vcvtq_u32_f32(vaddq_f32(v_dst32f1, v_05)))); + uint16x8_t v_dst = vcombine_u16(vmovn_u32(internal::vroundq_u32_f32(v_dst32f0)), + vmovn_u32(internal::vroundq_u32_f32(v_dst32f1))); vst1_u8(dst + j, vmovn_u16(v_dst)); } diff --git a/3rdparty/carotene/src/vround_helper.hpp b/3rdparty/carotene/src/vround_helper.hpp new file mode 100644 index 0000000000..89a6254510 --- /dev/null +++ b/3rdparty/carotene/src/vround_helper.hpp @@ -0,0 +1,102 @@ +/* + * By downloading, copying, installing or using the software you agree to this license. + * If you do not agree to this license, do not download, install, + * copy or use the software. + * + * + * License Agreement + * For Open Source Computer Vision Library + * (3-clause BSD License) + * + * Copyright (C) 2014-2015, NVIDIA Corporation, all rights reserved. + * Third party copyrights are property of their respective owners. + * + * Redistribution and use in source and binary forms, with or without modification, + * are permitted provided that the following conditions are met: + * + * * Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * + * * Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * + * * Neither the names of the copyright holders nor the names of the contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * This software is provided by the copyright holders and contributors "as is" and + * any express or implied warranties, including, but not limited to, the implied + * warranties of merchantability and fitness for a particular purpose are disclaimed. + * In no event shall copyright holders or contributors be liable for any direct, + * indirect, incidental, special, exemplary, or consequential damages + * (including, but not limited to, procurement of substitute goods or services; + * loss of use, data, or profits; or business interruption) however caused + * and on any theory of liability, whether in contract, strict liability, + * or tort (including negligence or otherwise) arising in any way out of + * the use of this software, even if advised of the possibility of such damage. + */ + +#ifndef CAROTENE_SRC_VROUND_HELPER_HPP +#define CAROTENE_SRC_VROUND_HELPER_HPP + +#include "common.hpp" +#include "vtransform.hpp" + +#ifdef CAROTENE_NEON + +/** + * This helper header is for rounding from float32xN to uin32xN or int32xN to nearest, ties to even. + * See https://en.wikipedia.org/wiki/Rounding#Rounding_half_to_even + */ + +// See https://github.com/opencv/opencv/pull/24271#issuecomment-1867318007 +#define CAROTENE_ROUND_DELTA (12582912.0f) + +namespace CAROTENE_NS { namespace internal { + +inline uint32x4_t vroundq_u32_f32(const float32x4_t val) +{ +#if CAROTENE_NEON_ARCH >= 8 /* get ready for ARMv9 */ + return vcvtnq_u32_f32(val); +#else + const float32x4_t delta = vdupq_n_f32(CAROTENE_ROUND_DELTA); + return vcvtq_u32_f32(vsubq_f32(vaddq_f32(val, delta), delta)); +#endif +} + +inline uint32x2_t vround_u32_f32(const float32x2_t val) +{ +#if CAROTENE_NEON_ARCH >= 8 /* get ready for ARMv9 */ + return vcvtn_u32_f32(val); +#else + const float32x2_t delta = vdup_n_f32(CAROTENE_ROUND_DELTA); + return vcvt_u32_f32(vsub_f32(vadd_f32(val, delta), delta)); +#endif +} + +inline int32x4_t vroundq_s32_f32(const float32x4_t val) +{ +#if CAROTENE_NEON_ARCH >= 8 /* get ready for ARMv9 */ + return vcvtnq_s32_f32(val); +#else + const float32x4_t delta = vdupq_n_f32(CAROTENE_ROUND_DELTA); + return vcvtq_s32_f32(vsubq_f32(vaddq_f32(val, delta), delta)); +#endif +} + +inline int32x2_t vround_s32_f32(const float32x2_t val) +{ +#if CAROTENE_NEON_ARCH >= 8 /* get ready for ARMv9 */ + return vcvtn_s32_f32(val); +#else + const float32x2_t delta = vdup_n_f32(CAROTENE_ROUND_DELTA); + return vcvt_s32_f32(vsub_f32(vadd_f32(val, delta), delta)); +#endif +} + +} } + +#endif // CAROTENE_NEON + +#endif diff --git a/3rdparty/ffmpeg/ffmpeg.cmake b/3rdparty/ffmpeg/ffmpeg.cmake index da75e3d2ca..fa6299751c 100644 --- a/3rdparty/ffmpeg/ffmpeg.cmake +++ b/3rdparty/ffmpeg/ffmpeg.cmake @@ -1,8 +1,8 @@ -# Binaries branch name: ffmpeg/4.x_20230622 -# Binaries were created for OpenCV: 61d48dd0f8d1cc1a115d26998705a61478f64a3c -ocv_update(FFMPEG_BINARIES_COMMIT "7da61f0695eabf8972a2c302bf1632a3d99fb0d5") -ocv_update(FFMPEG_FILE_HASH_BIN32 "4aaef1456e282e5ef665d65555f47f56") -ocv_update(FFMPEG_FILE_HASH_BIN64 "38a638851e064c591ce812e27ed43f1f") +# Binaries branch name: ffmpeg/4.x_20231225 +# Binaries were created for OpenCV: 62f1a7410d5e5e03d6cee5c95549bf61d5ee98db +ocv_update(FFMPEG_BINARIES_COMMIT "fbac408a47977ee4265f39e7659d33f1dfef5216") +ocv_update(FFMPEG_FILE_HASH_BIN32 "9b755ecbbade0a5b78332e9b4ef2dd1b") +ocv_update(FFMPEG_FILE_HASH_BIN64 "cb4db51ee9a423e6168b9d08bee61efc") ocv_update(FFMPEG_FILE_HASH_CMAKE "8862c87496e2e8c375965e1277dee1c7") function(download_win_ffmpeg script_var) diff --git a/3rdparty/libspng/CMakeLists.txt b/3rdparty/libspng/CMakeLists.txt index afd6d5fe40..ab0a41a5c6 100644 --- a/3rdparty/libspng/CMakeLists.txt +++ b/3rdparty/libspng/CMakeLists.txt @@ -23,7 +23,6 @@ if(MSVC) endif(MSVC) add_library(${SPNG_LIBRARY} STATIC ${OPENCV_3RDPARTY_EXCLUDE_FROM_ALL} ${spng_headers} ${spng_sources}) -ocv_warnings_disable(CMAKE_C_FLAGS -Wunused-variable) target_link_libraries(${SPNG_LIBRARY} ${ZLIB_LIBRARIES}) set_target_properties(${SPNG_LIBRARY} diff --git a/3rdparty/libspng/LICENSE b/3rdparty/libspng/LICENSE index f96574b80d..a29d5a2fa8 100644 --- a/3rdparty/libspng/LICENSE +++ b/3rdparty/libspng/LICENSE @@ -1,6 +1,6 @@ BSD 2-Clause License -Copyright (c) 2018-2022, Randy +Copyright (c) 2018-2023, Randy All rights reserved. Redistribution and use in source and binary forms, with or without diff --git a/3rdparty/libspng/spng.c b/3rdparty/libspng/spng.c index 6ed60f2d6c..b22b7110a1 100644 --- a/3rdparty/libspng/spng.c +++ b/3rdparty/libspng/spng.c @@ -2691,6 +2691,7 @@ static int read_non_idat_chunks(spng_ctx *ctx) if(!memcmp(chunk.type, type_exif, 4)) { if(ctx->file.exif) return SPNG_EDUP_EXIF; + if(!chunk.length) return SPNG_EEXIF; ctx->file.exif = 1; @@ -4999,11 +5000,11 @@ void spng_ctx_free(spng_ctx *ctx) spng__free(ctx, ctx->prev_scanline_buf); spng__free(ctx, ctx->filtered_scanline_buf); - spng_free_fn *free_func = ctx->alloc.free_fn; + spng_free_fn *free_fn = ctx->alloc.free_fn; memset(ctx, 0, sizeof(spng_ctx)); - free_func(ctx); + free_fn(ctx); } static int buffer_read_fn(spng_ctx *ctx, void *user, void *data, size_t n) @@ -5743,7 +5744,8 @@ int spng_set_iccp(spng_ctx *ctx, struct spng_iccp *iccp) SPNG_SET_CHUNK_BOILERPLATE(iccp); if(check_png_keyword(iccp->profile_name)) return SPNG_EICCP_NAME; - if(!iccp->profile_len || iccp->profile_len > UINT_MAX) return 1; + if(!iccp->profile_len) return SPNG_ECHUNK_SIZE; + if(iccp->profile_len > spng_u32max) return SPNG_ECHUNK_STDLEN; if(ctx->iccp.profile && !ctx->user.iccp) spng__free(ctx, ctx->iccp.profile); diff --git a/3rdparty/libspng/spng.h b/3rdparty/libspng/spng.h index 5937d6c15d..8f946337bf 100644 --- a/3rdparty/libspng/spng.h +++ b/3rdparty/libspng/spng.h @@ -1,4 +1,4 @@ -/* SPDX-License-Identifier: (BSD-2-Clause AND libpng-2.0) */ +/* SPDX-License-Identifier: BSD-2-Clause */ #ifndef SPNG_H #define SPNG_H @@ -28,7 +28,7 @@ extern "C" { #define SPNG_VERSION_MAJOR 0 #define SPNG_VERSION_MINOR 7 -#define SPNG_VERSION_PATCH 3 +#define SPNG_VERSION_PATCH 4 enum spng_errno { diff --git a/3rdparty/openexr/IlmImf/ImfConvert.cpp b/3rdparty/openexr/IlmImf/ImfConvert.cpp index cce7163c19..62c0305935 100644 --- a/3rdparty/openexr/IlmImf/ImfConvert.cpp +++ b/3rdparty/openexr/IlmImf/ImfConvert.cpp @@ -107,7 +107,7 @@ floatToUint (float f) if (isNegative (f) || isNan (f)) return 0; - if (isInfinity (f) || f > UINT_MAX) + if (isInfinity (f) || f > (float)UINT_MAX) return UINT_MAX; return (unsigned int) f; diff --git a/3rdparty/openjpeg/openjp2/ht_dec.c b/3rdparty/openjpeg/openjp2/ht_dec.c index e2f3afd6a3..85e7266919 100644 --- a/3rdparty/openjpeg/openjp2/ht_dec.c +++ b/3rdparty/openjpeg/openjp2/ht_dec.c @@ -55,6 +55,10 @@ #define OPJ_COMPILER_GNUC #endif +#if defined(OPJ_COMPILER_MSVC) && defined(_M_ARM64) +#include +#endif + //************************************************************************/ /** @brief Displays the error message for disabling the decoding of SPP and * MRP passes @@ -71,6 +75,9 @@ OPJ_UINT32 population_count(OPJ_UINT32 val) { #if defined(OPJ_COMPILER_MSVC) && (defined(_M_IX86) || defined(_M_AMD64)) return (OPJ_UINT32)__popcnt(val); +#elif defined(OPJ_COMPILER_MSVC) && defined(_M_ARM64) + const __n64 temp = neon_cnt(__uint64ToN64_v(val)); + return neon_addv8(temp).n8_i8[0]; #elif (defined OPJ_COMPILER_GNUC) return (OPJ_UINT32)__builtin_popcount(val); #else diff --git a/3rdparty/openjpeg/openjp2/j2k.c b/3rdparty/openjpeg/openjp2/j2k.c index bcce316568..d3b2258dfc 100644 --- a/3rdparty/openjpeg/openjp2/j2k.c +++ b/3rdparty/openjpeg/openjp2/j2k.c @@ -7796,7 +7796,7 @@ OPJ_BOOL opj_j2k_setup_encoder(opj_j2k_t *p_j2k, image->comps[0].h * image->comps[0].prec) / ((double)parameters->tcp_rates[parameters->tcp_numlayers - 1] * 8 * image->comps[0].dx * image->comps[0].dy)); - if (temp_size > INT_MAX) { + if (temp_size > (OPJ_FLOAT32)INT_MAX) { parameters->max_cs_size = INT_MAX; } else { parameters->max_cs_size = (int) floor(temp_size); diff --git a/3rdparty/openjpeg/openjp2/tcd.c b/3rdparty/openjpeg/openjp2/tcd.c index 6442669d60..8f0aac0aa0 100644 --- a/3rdparty/openjpeg/openjp2/tcd.c +++ b/3rdparty/openjpeg/openjp2/tcd.c @@ -2262,7 +2262,7 @@ static OPJ_BOOL opj_tcd_dc_level_shift_decode(opj_tcd_t *p_tcd) for (j = 0; j < l_height; ++j) { for (i = 0; i < l_width; ++i) { OPJ_FLOAT32 l_value = *((OPJ_FLOAT32 *) l_current_ptr); - if (l_value > INT_MAX) { + if (l_value > (OPJ_FLOAT32)INT_MAX) { *l_current_ptr = l_max; } else if (l_value < INT_MIN) { *l_current_ptr = l_min; diff --git a/3rdparty/readme.txt b/3rdparty/readme.txt index 0e6ce1e05e..02875ece28 100644 --- a/3rdparty/readme.txt +++ b/3rdparty/readme.txt @@ -31,7 +31,7 @@ libpng Portable Network Graphics library. libspng Portable Network Graphics library. The license and copyright notes can be found in libspng/LICENSE. - See libspng home page https://www.libspng.org + See libspng home page https://libspng.org for details and links to the source code WITH_SPNG CMake option must be ON to add libspng support to imgcodecs @@ -41,7 +41,6 @@ libtiff Tag Image File Format (TIFF) Software Copyright (c) 1991-1997 Silicon Graphics, Inc. See libtiff home page #1 http://www.simplesystems.org/libtiff/ #2 https://libtiff.gitlab.io/libtiff/ - #3 http://libtiff.maptools.org/ for details and links to the source code WITH_TIFF CMake option must be ON to add libtiff & zlib support to imgcodecs. diff --git a/3rdparty/zlib/ChangeLog b/3rdparty/zlib/ChangeLog index 457526bc6a..8707988ac1 100644 --- a/3rdparty/zlib/ChangeLog +++ b/3rdparty/zlib/ChangeLog @@ -1,6 +1,24 @@ ChangeLog file for zlib +Changes in 1.3 (18 Aug 2023) +- Remove K&R function definitions and zlib2ansi +- Fix bug in deflateBound() for level 0 and memLevel 9 +- Fix bug when gzungetc() is used immediately after gzopen() +- Fix bug when using gzflush() with a very small buffer +- Fix crash when gzsetparams() attempted for transparent write +- Fix test/example.c to work with FORCE_STORED +- Rewrite of zran in examples (see zran.c version history) +- Fix minizip to allow it to open an empty zip file +- Fix reading disk number start on zip64 files in minizip +- Fix logic error in minizip argument processing +- Add minizip testing to Makefile +- Read multiple bytes instead of byte-by-byte in minizip unzip.c +- Add memory sanitizer to configure (--memory) +- Various portability improvements +- Various documentation improvements +- Various spelling and typo corrections + Changes in 1.2.13 (13 Oct 2022) - Fix configure issue that discarded provided CC definition - Correct incorrect inputs provided to the CRC functions @@ -1445,7 +1463,7 @@ Changes in 0.99 (27 Jan 96) - fix typo in Make_vms.com (f$trnlnm -> f$getsyi) - in fcalloc, normalize pointer if size > 65520 bytes - don't use special fcalloc for 32 bit Borland C++ -- use STDC instead of __GO32__ to avoid redeclaring exit, calloc, etc... +- use STDC instead of __GO32__ to avoid redeclaring exit, calloc, etc. - use Z_BINARY instead of BINARY - document that gzclose after gzdopen will close the file - allow "a" as mode in gzopen diff --git a/3rdparty/zlib/README b/3rdparty/zlib/README index ba34d1894a..e02fc5aa20 100644 --- a/3rdparty/zlib/README +++ b/3rdparty/zlib/README @@ -1,6 +1,6 @@ ZLIB DATA COMPRESSION LIBRARY -zlib 1.2.13 is a general purpose data compression library. All the code is +zlib 1.3 is a general purpose data compression library. All the code is thread safe. The data format used by the zlib library is described by RFCs (Request for Comments) 1950 to 1952 in the files http://tools.ietf.org/html/rfc1950 (zlib format), rfc1951 (deflate format) and @@ -29,18 +29,17 @@ PLEASE read the zlib FAQ http://zlib.net/zlib_faq.html before asking for help. Mark Nelson wrote an article about zlib for the Jan. 1997 issue of Dr. Dobb's Journal; a copy of the article is available at -http://marknelson.us/1997/01/01/zlib-engine/ . +https://marknelson.us/posts/1997/01/01/zlib-engine.html . -The changes made in version 1.2.13 are documented in the file ChangeLog. +The changes made in version 1.3 are documented in the file ChangeLog. Unsupported third party contributions are provided in directory contrib/ . -zlib is available in Java using the java.util.zip package, documented at -http://java.sun.com/developer/technicalArticles/Programming/compression/ . +zlib is available in Java using the java.util.zip package. Follow the API +Documentation link at: https://docs.oracle.com/search/?q=java.util.zip . -A Perl interface to zlib written by Paul Marquess is available -at CPAN (Comprehensive Perl Archive Network) sites, including -http://search.cpan.org/~pmqs/IO-Compress-Zlib/ . +A Perl interface to zlib and bzip2 written by Paul Marquess +can be found at https://github.com/pmqs/IO-Compress . A Python interface to zlib written by A.M. Kuchling is available in Python 1.5 and later versions, see @@ -64,7 +63,7 @@ Notes for some targets: - zlib doesn't work with gcc 2.6.3 on a DEC 3000/300LX under OSF/1 2.1 it works when compiled with cc. -- On Digital Unix 4.0D (formely OSF/1) on AlphaServer, the cc option -std1 is +- On Digital Unix 4.0D (formerly OSF/1) on AlphaServer, the cc option -std1 is necessary to get gzprintf working correctly. This is done by configure. - zlib doesn't work on HP-UX 9.05 with some versions of /bin/cc. It works with @@ -84,7 +83,7 @@ Acknowledgments: Copyright notice: - (C) 1995-2022 Jean-loup Gailly and Mark Adler + (C) 1995-2023 Jean-loup Gailly and Mark Adler This software is provided 'as-is', without any express or implied warranty. In no event will the authors be held liable for any damages diff --git a/3rdparty/zlib/adler32.c b/3rdparty/zlib/adler32.c index d0be4380a3..04b81d29ba 100644 --- a/3rdparty/zlib/adler32.c +++ b/3rdparty/zlib/adler32.c @@ -7,8 +7,6 @@ #include "zutil.h" -local uLong adler32_combine_ OF((uLong adler1, uLong adler2, z_off64_t len2)); - #define BASE 65521U /* largest prime smaller than 65536 */ #define NMAX 5552 /* NMAX is the largest n such that 255n(n+1)/2 + (n+1)(BASE-1) <= 2^32-1 */ @@ -60,11 +58,7 @@ local uLong adler32_combine_ OF((uLong adler1, uLong adler2, z_off64_t len2)); #endif /* ========================================================================= */ -uLong ZEXPORT adler32_z(adler, buf, len) - uLong adler; - const Bytef *buf; - z_size_t len; -{ +uLong ZEXPORT adler32_z(uLong adler, const Bytef *buf, z_size_t len) { unsigned long sum2; unsigned n; @@ -131,20 +125,12 @@ uLong ZEXPORT adler32_z(adler, buf, len) } /* ========================================================================= */ -uLong ZEXPORT adler32(adler, buf, len) - uLong adler; - const Bytef *buf; - uInt len; -{ +uLong ZEXPORT adler32(uLong adler, const Bytef *buf, uInt len) { return adler32_z(adler, buf, len); } /* ========================================================================= */ -local uLong adler32_combine_(adler1, adler2, len2) - uLong adler1; - uLong adler2; - z_off64_t len2; -{ +local uLong adler32_combine_(uLong adler1, uLong adler2, z_off64_t len2) { unsigned long sum1; unsigned long sum2; unsigned rem; @@ -169,18 +155,10 @@ local uLong adler32_combine_(adler1, adler2, len2) } /* ========================================================================= */ -uLong ZEXPORT adler32_combine(adler1, adler2, len2) - uLong adler1; - uLong adler2; - z_off_t len2; -{ +uLong ZEXPORT adler32_combine(uLong adler1, uLong adler2, z_off_t len2) { return adler32_combine_(adler1, adler2, len2); } -uLong ZEXPORT adler32_combine64(adler1, adler2, len2) - uLong adler1; - uLong adler2; - z_off64_t len2; -{ +uLong ZEXPORT adler32_combine64(uLong adler1, uLong adler2, z_off64_t len2) { return adler32_combine_(adler1, adler2, len2); } diff --git a/3rdparty/zlib/compress.c b/3rdparty/zlib/compress.c index 2ad5326c14..f43bacf7ab 100644 --- a/3rdparty/zlib/compress.c +++ b/3rdparty/zlib/compress.c @@ -19,13 +19,8 @@ memory, Z_BUF_ERROR if there was not enough room in the output buffer, Z_STREAM_ERROR if the level parameter is invalid. */ -int ZEXPORT compress2(dest, destLen, source, sourceLen, level) - Bytef *dest; - uLongf *destLen; - const Bytef *source; - uLong sourceLen; - int level; -{ +int ZEXPORT compress2(Bytef *dest, uLongf *destLen, const Bytef *source, + uLong sourceLen, int level) { z_stream stream; int err; const uInt max = (uInt)-1; @@ -65,12 +60,8 @@ int ZEXPORT compress2(dest, destLen, source, sourceLen, level) /* =========================================================================== */ -int ZEXPORT compress(dest, destLen, source, sourceLen) - Bytef *dest; - uLongf *destLen; - const Bytef *source; - uLong sourceLen; -{ +int ZEXPORT compress(Bytef *dest, uLongf *destLen, const Bytef *source, + uLong sourceLen) { return compress2(dest, destLen, source, sourceLen, Z_DEFAULT_COMPRESSION); } @@ -78,9 +69,7 @@ int ZEXPORT compress(dest, destLen, source, sourceLen) If the default memLevel or windowBits for deflateInit() is changed, then this function needs to be updated. */ -uLong ZEXPORT compressBound(sourceLen) - uLong sourceLen; -{ +uLong ZEXPORT compressBound(uLong sourceLen) { return sourceLen + (sourceLen >> 12) + (sourceLen >> 14) + (sourceLen >> 25) + 13; } diff --git a/3rdparty/zlib/crc32.c b/3rdparty/zlib/crc32.c index f8357b083f..6c38f5c04c 100644 --- a/3rdparty/zlib/crc32.c +++ b/3rdparty/zlib/crc32.c @@ -103,19 +103,6 @@ # define ARMCRC32 #endif -/* Local functions. */ -local z_crc_t multmodp OF((z_crc_t a, z_crc_t b)); -local z_crc_t x2nmodp OF((z_off64_t n, unsigned k)); - -#if defined(W) && (!defined(ARMCRC32) || defined(DYNAMIC_CRC_TABLE)) - local z_word_t byte_swap OF((z_word_t word)); -#endif - -#if defined(W) && !defined(ARMCRC32) - local z_crc_t crc_word OF((z_word_t data)); - local z_word_t crc_word_big OF((z_word_t data)); -#endif - #if defined(W) && (!defined(ARMCRC32) || defined(DYNAMIC_CRC_TABLE)) /* Swap the bytes in a z_word_t to convert between little and big endian. Any @@ -123,9 +110,7 @@ local z_crc_t x2nmodp OF((z_off64_t n, unsigned k)); instruction, if one is available. This assumes that word_t is either 32 bits or 64 bits. */ -local z_word_t byte_swap(word) - z_word_t word; -{ +local z_word_t byte_swap(z_word_t word) { # if W == 8 return (word & 0xff00000000000000) >> 56 | @@ -146,24 +131,77 @@ local z_word_t byte_swap(word) } #endif +#ifdef DYNAMIC_CRC_TABLE +/* ========================================================================= + * Table of powers of x for combining CRC-32s, filled in by make_crc_table() + * below. + */ + local z_crc_t FAR x2n_table[32]; +#else +/* ========================================================================= + * Tables for byte-wise and braided CRC-32 calculations, and a table of powers + * of x for combining CRC-32s, all made by make_crc_table(). + */ +# include "crc32.h" +#endif + /* CRC polynomial. */ #define POLY 0xedb88320 /* p(x) reflected, with x^32 implied */ -#ifdef DYNAMIC_CRC_TABLE +/* + Return a(x) multiplied by b(x) modulo p(x), where p(x) is the CRC polynomial, + reflected. For speed, this requires that a not be zero. + */ +local z_crc_t multmodp(z_crc_t a, z_crc_t b) { + z_crc_t m, p; + m = (z_crc_t)1 << 31; + p = 0; + for (;;) { + if (a & m) { + p ^= b; + if ((a & (m - 1)) == 0) + break; + } + m >>= 1; + b = b & 1 ? (b >> 1) ^ POLY : b >> 1; + } + return p; +} + +/* + Return x^(n * 2^k) modulo p(x). Requires that x2n_table[] has been + initialized. + */ +local z_crc_t x2nmodp(z_off64_t n, unsigned k) { + z_crc_t p; + + p = (z_crc_t)1 << 31; /* x^0 == 1 */ + while (n) { + if (n & 1) + p = multmodp(x2n_table[k & 31], p); + n >>= 1; + k++; + } + return p; +} + +#ifdef DYNAMIC_CRC_TABLE +/* ========================================================================= + * Build the tables for byte-wise and braided CRC-32 calculations, and a table + * of powers of x for combining CRC-32s. + */ local z_crc_t FAR crc_table[256]; -local z_crc_t FAR x2n_table[32]; -local void make_crc_table OF((void)); #ifdef W local z_word_t FAR crc_big_table[256]; local z_crc_t FAR crc_braid_table[W][256]; local z_word_t FAR crc_braid_big_table[W][256]; - local void braid OF((z_crc_t [][256], z_word_t [][256], int, int)); + local void braid(z_crc_t [][256], z_word_t [][256], int, int); #endif #ifdef MAKECRCH - local void write_table OF((FILE *, const z_crc_t FAR *, int)); - local void write_table32hi OF((FILE *, const z_word_t FAR *, int)); - local void write_table64 OF((FILE *, const z_word_t FAR *, int)); + local void write_table(FILE *, const z_crc_t FAR *, int); + local void write_table32hi(FILE *, const z_word_t FAR *, int); + local void write_table64(FILE *, const z_word_t FAR *, int); #endif /* MAKECRCH */ /* @@ -176,7 +214,6 @@ local void make_crc_table OF((void)); /* Definition of once functionality. */ typedef struct once_s once_t; -local void once OF((once_t *, void (*)(void))); /* Check for the availability of atomics. */ #if defined(__STDC__) && __STDC_VERSION__ >= 201112L && \ @@ -196,10 +233,7 @@ struct once_s { invoke once() at the same time. The state must be a once_t initialized with ONCE_INIT. */ -local void once(state, init) - once_t *state; - void (*init)(void); -{ +local void once(once_t *state, void (*init)(void)) { if (!atomic_load(&state->done)) { if (atomic_flag_test_and_set(&state->begun)) while (!atomic_load(&state->done)) @@ -222,10 +256,7 @@ struct once_s { /* Test and set. Alas, not atomic, but tries to minimize the period of vulnerability. */ -local int test_and_set OF((int volatile *)); -local int test_and_set(flag) - int volatile *flag; -{ +local int test_and_set(int volatile *flag) { int was; was = *flag; @@ -234,10 +265,7 @@ local int test_and_set(flag) } /* Run the provided init() function once. This is not thread-safe. */ -local void once(state, init) - once_t *state; - void (*init)(void); -{ +local void once(once_t *state, void (*init)(void)) { if (!state->done) { if (test_and_set(&state->begun)) while (!state->done) @@ -279,8 +307,7 @@ local once_t made = ONCE_INIT; combinations of CRC register values and incoming bytes. */ -local void make_crc_table() -{ +local void make_crc_table(void) { unsigned i, j, n; z_crc_t p; @@ -447,11 +474,7 @@ local void make_crc_table() Write the 32-bit values in table[0..k-1] to out, five per line in hexadecimal separated by commas. */ -local void write_table(out, table, k) - FILE *out; - const z_crc_t FAR *table; - int k; -{ +local void write_table(FILE *out, const z_crc_t FAR *table, int k) { int n; for (n = 0; n < k; n++) @@ -464,11 +487,7 @@ local void write_table(out, table, k) Write the high 32-bits of each value in table[0..k-1] to out, five per line in hexadecimal separated by commas. */ -local void write_table32hi(out, table, k) -FILE *out; -const z_word_t FAR *table; -int k; -{ +local void write_table32hi(FILE *out, const z_word_t FAR *table, int k) { int n; for (n = 0; n < k; n++) @@ -484,11 +503,7 @@ int k; bits. If not, then the type cast and format string can be adjusted accordingly. */ -local void write_table64(out, table, k) - FILE *out; - const z_word_t FAR *table; - int k; -{ +local void write_table64(FILE *out, const z_word_t FAR *table, int k) { int n; for (n = 0; n < k; n++) @@ -498,8 +513,7 @@ local void write_table64(out, table, k) } /* Actually do the deed. */ -int main() -{ +int main(void) { make_crc_table(); return 0; } @@ -511,12 +525,7 @@ int main() Generate the little and big-endian braid tables for the given n and z_word_t size w. Each array must have room for w blocks of 256 elements. */ -local void braid(ltl, big, n, w) - z_crc_t ltl[][256]; - z_word_t big[][256]; - int n; - int w; -{ +local void braid(z_crc_t ltl[][256], z_word_t big[][256], int n, int w) { int k; z_crc_t i, p, q; for (k = 0; k < w; k++) { @@ -531,69 +540,13 @@ local void braid(ltl, big, n, w) } #endif -#else /* !DYNAMIC_CRC_TABLE */ -/* ======================================================================== - * Tables for byte-wise and braided CRC-32 calculations, and a table of powers - * of x for combining CRC-32s, all made by make_crc_table(). - */ -#include "crc32.h" #endif /* DYNAMIC_CRC_TABLE */ -/* ======================================================================== - * Routines used for CRC calculation. Some are also required for the table - * generation above. - */ - -/* - Return a(x) multiplied by b(x) modulo p(x), where p(x) is the CRC polynomial, - reflected. For speed, this requires that a not be zero. - */ -local z_crc_t multmodp(a, b) - z_crc_t a; - z_crc_t b; -{ - z_crc_t m, p; - - m = (z_crc_t)1 << 31; - p = 0; - for (;;) { - if (a & m) { - p ^= b; - if ((a & (m - 1)) == 0) - break; - } - m >>= 1; - b = b & 1 ? (b >> 1) ^ POLY : b >> 1; - } - return p; -} - -/* - Return x^(n * 2^k) modulo p(x). Requires that x2n_table[] has been - initialized. - */ -local z_crc_t x2nmodp(n, k) - z_off64_t n; - unsigned k; -{ - z_crc_t p; - - p = (z_crc_t)1 << 31; /* x^0 == 1 */ - while (n) { - if (n & 1) - p = multmodp(x2n_table[k & 31], p); - n >>= 1; - k++; - } - return p; -} - /* ========================================================================= * This function can be used by asm versions of crc32(), and to force the * generation of the CRC tables in a threaded application. */ -const z_crc_t FAR * ZEXPORT get_crc_table() -{ +const z_crc_t FAR * ZEXPORT get_crc_table(void) { #ifdef DYNAMIC_CRC_TABLE once(&made, make_crc_table); #endif /* DYNAMIC_CRC_TABLE */ @@ -619,11 +572,8 @@ const z_crc_t FAR * ZEXPORT get_crc_table() #define Z_BATCH_ZEROS 0xa10d3d0c /* computed from Z_BATCH = 3990 */ #define Z_BATCH_MIN 800 /* fewest words in a final batch */ -unsigned long ZEXPORT crc32_z(crc, buf, len) - unsigned long crc; - const unsigned char FAR *buf; - z_size_t len; -{ +unsigned long ZEXPORT crc32_z(unsigned long crc, const unsigned char FAR *buf, + z_size_t len) { z_crc_t val; z_word_t crc1, crc2; const z_word_t *word; @@ -723,18 +673,14 @@ unsigned long ZEXPORT crc32_z(crc, buf, len) least-significant byte of the word as the first byte of data, without any pre or post conditioning. This is used to combine the CRCs of each braid. */ -local z_crc_t crc_word(data) - z_word_t data; -{ +local z_crc_t crc_word(z_word_t data) { int k; for (k = 0; k < W; k++) data = (data >> 8) ^ crc_table[data & 0xff]; return (z_crc_t)data; } -local z_word_t crc_word_big(data) - z_word_t data; -{ +local z_word_t crc_word_big(z_word_t data) { int k; for (k = 0; k < W; k++) data = (data << 8) ^ @@ -745,11 +691,8 @@ local z_word_t crc_word_big(data) #endif /* ========================================================================= */ -unsigned long ZEXPORT crc32_z(crc, buf, len) - unsigned long crc; - const unsigned char FAR *buf; - z_size_t len; -{ +unsigned long ZEXPORT crc32_z(unsigned long crc, const unsigned char FAR *buf, + z_size_t len) { /* Return initial CRC, if requested. */ if (buf == Z_NULL) return 0; @@ -781,8 +724,8 @@ unsigned long ZEXPORT crc32_z(crc, buf, len) words = (z_word_t const *)buf; /* Do endian check at execution time instead of compile time, since ARM - processors can change the endianess at execution time. If the - compiler knows what the endianess will be, it can optimize out the + processors can change the endianness at execution time. If the + compiler knows what the endianness will be, it can optimize out the check and the unused branch. */ endian = 1; if (*(unsigned char *)&endian) { @@ -1069,20 +1012,13 @@ unsigned long ZEXPORT crc32_z(crc, buf, len) #endif /* ========================================================================= */ -unsigned long ZEXPORT crc32(crc, buf, len) - unsigned long crc; - const unsigned char FAR *buf; - uInt len; -{ +unsigned long ZEXPORT crc32(unsigned long crc, const unsigned char FAR *buf, + uInt len) { return crc32_z(crc, buf, len); } /* ========================================================================= */ -uLong ZEXPORT crc32_combine64(crc1, crc2, len2) - uLong crc1; - uLong crc2; - z_off64_t len2; -{ +uLong ZEXPORT crc32_combine64(uLong crc1, uLong crc2, z_off64_t len2) { #ifdef DYNAMIC_CRC_TABLE once(&made, make_crc_table); #endif /* DYNAMIC_CRC_TABLE */ @@ -1090,18 +1026,12 @@ uLong ZEXPORT crc32_combine64(crc1, crc2, len2) } /* ========================================================================= */ -uLong ZEXPORT crc32_combine(crc1, crc2, len2) - uLong crc1; - uLong crc2; - z_off_t len2; -{ +uLong ZEXPORT crc32_combine(uLong crc1, uLong crc2, z_off_t len2) { return crc32_combine64(crc1, crc2, (z_off64_t)len2); } /* ========================================================================= */ -uLong ZEXPORT crc32_combine_gen64(len2) - z_off64_t len2; -{ +uLong ZEXPORT crc32_combine_gen64(z_off64_t len2) { #ifdef DYNAMIC_CRC_TABLE once(&made, make_crc_table); #endif /* DYNAMIC_CRC_TABLE */ @@ -1109,17 +1039,11 @@ uLong ZEXPORT crc32_combine_gen64(len2) } /* ========================================================================= */ -uLong ZEXPORT crc32_combine_gen(len2) - z_off_t len2; -{ +uLong ZEXPORT crc32_combine_gen(z_off_t len2) { return crc32_combine_gen64((z_off64_t)len2); } /* ========================================================================= */ -uLong ZEXPORT crc32_combine_op(crc1, crc2, op) - uLong crc1; - uLong crc2; - uLong op; -{ +uLong ZEXPORT crc32_combine_op(uLong crc1, uLong crc2, uLong op) { return multmodp(op, crc1) ^ (crc2 & 0xffffffff); } diff --git a/3rdparty/zlib/deflate.c b/3rdparty/zlib/deflate.c index 4a689db359..bd01175192 100644 --- a/3rdparty/zlib/deflate.c +++ b/3rdparty/zlib/deflate.c @@ -1,5 +1,5 @@ /* deflate.c -- compress data using the deflation algorithm - * Copyright (C) 1995-2022 Jean-loup Gailly and Mark Adler + * Copyright (C) 1995-2023 Jean-loup Gailly and Mark Adler * For conditions of distribution and use, see copyright notice in zlib.h */ @@ -52,7 +52,7 @@ #include "deflate.h" const char deflate_copyright[] = - " deflate 1.2.13 Copyright 1995-2022 Jean-loup Gailly and Mark Adler "; + " deflate 1.3 Copyright 1995-2023 Jean-loup Gailly and Mark Adler "; /* If you use the zlib library in a product, an acknowledgment is welcome in the documentation of your product. If for some reason you cannot @@ -60,9 +60,6 @@ const char deflate_copyright[] = copyright string in the executable of your product. */ -/* =========================================================================== - * Function prototypes. - */ typedef enum { need_more, /* block not completed, need more input or more output */ block_done, /* block flush performed */ @@ -70,29 +67,16 @@ typedef enum { finish_done /* finish done, accept no more input or output */ } block_state; -typedef block_state (*compress_func) OF((deflate_state *s, int flush)); +typedef block_state (*compress_func)(deflate_state *s, int flush); /* Compression function. Returns the block state after the call. */ -local int deflateStateCheck OF((z_streamp strm)); -local void slide_hash OF((deflate_state *s)); -local void fill_window OF((deflate_state *s)); -local block_state deflate_stored OF((deflate_state *s, int flush)); -local block_state deflate_fast OF((deflate_state *s, int flush)); +local block_state deflate_stored(deflate_state *s, int flush); +local block_state deflate_fast(deflate_state *s, int flush); #ifndef FASTEST -local block_state deflate_slow OF((deflate_state *s, int flush)); -#endif -local block_state deflate_rle OF((deflate_state *s, int flush)); -local block_state deflate_huff OF((deflate_state *s, int flush)); -local void lm_init OF((deflate_state *s)); -local void putShortMSB OF((deflate_state *s, uInt b)); -local void flush_pending OF((z_streamp strm)); -local unsigned read_buf OF((z_streamp strm, Bytef *buf, unsigned size)); -local uInt longest_match OF((deflate_state *s, IPos cur_match)); - -#ifdef ZLIB_DEBUG -local void check_match OF((deflate_state *s, IPos start, IPos match, - int length)); +local block_state deflate_slow(deflate_state *s, int flush); #endif +local block_state deflate_rle(deflate_state *s, int flush); +local block_state deflate_huff(deflate_state *s, int flush); /* =========================================================================== * Local data @@ -195,9 +179,12 @@ local const config configuration_table[10] = { * bit values at the expense of memory usage). We slide even when level == 0 to * keep the hash table consistent if we switch back to level > 0 later. */ -local void slide_hash(s) - deflate_state *s; -{ +#if defined(__has_feature) +# if __has_feature(memory_sanitizer) + __attribute__((no_sanitize("memory"))) +# endif +#endif +local void slide_hash(deflate_state *s) { unsigned n, m; Posf *p; uInt wsize = s->w_size; @@ -221,30 +208,177 @@ local void slide_hash(s) #endif } +/* =========================================================================== + * Read a new buffer from the current input stream, update the adler32 + * and total number of bytes read. All deflate() input goes through + * this function so some applications may wish to modify it to avoid + * allocating a large strm->next_in buffer and copying from it. + * (See also flush_pending()). + */ +local unsigned read_buf(z_streamp strm, Bytef *buf, unsigned size) { + unsigned len = strm->avail_in; + + if (len > size) len = size; + if (len == 0) return 0; + + strm->avail_in -= len; + + zmemcpy(buf, strm->next_in, len); + if (strm->state->wrap == 1) { + strm->adler = adler32(strm->adler, buf, len); + } +#ifdef GZIP + else if (strm->state->wrap == 2) { + strm->adler = crc32(strm->adler, buf, len); + } +#endif + strm->next_in += len; + strm->total_in += len; + + return len; +} + +/* =========================================================================== + * Fill the window when the lookahead becomes insufficient. + * Updates strstart and lookahead. + * + * IN assertion: lookahead < MIN_LOOKAHEAD + * OUT assertions: strstart <= window_size-MIN_LOOKAHEAD + * At least one byte has been read, or avail_in == 0; reads are + * performed for at least two bytes (required for the zip translate_eol + * option -- not supported here). + */ +local void fill_window(deflate_state *s) { + unsigned n; + unsigned more; /* Amount of free space at the end of the window. */ + uInt wsize = s->w_size; + + Assert(s->lookahead < MIN_LOOKAHEAD, "already enough lookahead"); + + do { + more = (unsigned)(s->window_size -(ulg)s->lookahead -(ulg)s->strstart); + + /* Deal with !@#$% 64K limit: */ + if (sizeof(int) <= 2) { + if (more == 0 && s->strstart == 0 && s->lookahead == 0) { + more = wsize; + + } else if (more == (unsigned)(-1)) { + /* Very unlikely, but possible on 16 bit machine if + * strstart == 0 && lookahead == 1 (input done a byte at time) + */ + more--; + } + } + + /* If the window is almost full and there is insufficient lookahead, + * move the upper half to the lower one to make room in the upper half. + */ + if (s->strstart >= wsize + MAX_DIST(s)) { + + zmemcpy(s->window, s->window + wsize, (unsigned)wsize - more); + s->match_start -= wsize; + s->strstart -= wsize; /* we now have strstart >= MAX_DIST */ + s->block_start -= (long) wsize; + if (s->insert > s->strstart) + s->insert = s->strstart; + slide_hash(s); + more += wsize; + } + if (s->strm->avail_in == 0) break; + + /* If there was no sliding: + * strstart <= WSIZE+MAX_DIST-1 && lookahead <= MIN_LOOKAHEAD - 1 && + * more == window_size - lookahead - strstart + * => more >= window_size - (MIN_LOOKAHEAD-1 + WSIZE + MAX_DIST-1) + * => more >= window_size - 2*WSIZE + 2 + * In the BIG_MEM or MMAP case (not yet supported), + * window_size == input_size + MIN_LOOKAHEAD && + * strstart + s->lookahead <= input_size => more >= MIN_LOOKAHEAD. + * Otherwise, window_size == 2*WSIZE so more >= 2. + * If there was sliding, more >= WSIZE. So in all cases, more >= 2. + */ + Assert(more >= 2, "more < 2"); + + n = read_buf(s->strm, s->window + s->strstart + s->lookahead, more); + s->lookahead += n; + + /* Initialize the hash value now that we have some input: */ + if (s->lookahead + s->insert >= MIN_MATCH) { + uInt str = s->strstart - s->insert; + s->ins_h = s->window[str]; + UPDATE_HASH(s, s->ins_h, s->window[str + 1]); +#if MIN_MATCH != 3 + Call UPDATE_HASH() MIN_MATCH-3 more times +#endif + while (s->insert) { + UPDATE_HASH(s, s->ins_h, s->window[str + MIN_MATCH-1]); +#ifndef FASTEST + s->prev[str & s->w_mask] = s->head[s->ins_h]; +#endif + s->head[s->ins_h] = (Pos)str; + str++; + s->insert--; + if (s->lookahead + s->insert < MIN_MATCH) + break; + } + } + /* If the whole input has less than MIN_MATCH bytes, ins_h is garbage, + * but this is not important since only literal bytes will be emitted. + */ + + } while (s->lookahead < MIN_LOOKAHEAD && s->strm->avail_in != 0); + + /* If the WIN_INIT bytes after the end of the current data have never been + * written, then zero those bytes in order to avoid memory check reports of + * the use of uninitialized (or uninitialised as Julian writes) bytes by + * the longest match routines. Update the high water mark for the next + * time through here. WIN_INIT is set to MAX_MATCH since the longest match + * routines allow scanning to strstart + MAX_MATCH, ignoring lookahead. + */ + if (s->high_water < s->window_size) { + ulg curr = s->strstart + (ulg)(s->lookahead); + ulg init; + + if (s->high_water < curr) { + /* Previous high water mark below current data -- zero WIN_INIT + * bytes or up to end of window, whichever is less. + */ + init = s->window_size - curr; + if (init > WIN_INIT) + init = WIN_INIT; + zmemzero(s->window + curr, (unsigned)init); + s->high_water = curr + init; + } + else if (s->high_water < (ulg)curr + WIN_INIT) { + /* High water mark at or above current data, but below current data + * plus WIN_INIT -- zero out to current data plus WIN_INIT, or up + * to end of window, whichever is less. + */ + init = (ulg)curr + WIN_INIT - s->high_water; + if (init > s->window_size - s->high_water) + init = s->window_size - s->high_water; + zmemzero(s->window + s->high_water, (unsigned)init); + s->high_water += init; + } + } + + Assert((ulg)s->strstart <= s->window_size - MIN_LOOKAHEAD, + "not enough room for search"); +} + /* ========================================================================= */ -int ZEXPORT deflateInit_(strm, level, version, stream_size) - z_streamp strm; - int level; - const char *version; - int stream_size; -{ +int ZEXPORT deflateInit_(z_streamp strm, int level, const char *version, + int stream_size) { return deflateInit2_(strm, level, Z_DEFLATED, MAX_WBITS, DEF_MEM_LEVEL, Z_DEFAULT_STRATEGY, version, stream_size); /* To do: ignore strm->next_in if we use it as window */ } /* ========================================================================= */ -int ZEXPORT deflateInit2_(strm, level, method, windowBits, memLevel, strategy, - version, stream_size) - z_streamp strm; - int level; - int method; - int windowBits; - int memLevel; - int strategy; - const char *version; - int stream_size; -{ +int ZEXPORT deflateInit2_(z_streamp strm, int level, int method, + int windowBits, int memLevel, int strategy, + const char *version, int stream_size) { deflate_state *s; int wrap = 1; static const char my_version[] = ZLIB_VERSION; @@ -386,9 +520,7 @@ int ZEXPORT deflateInit2_(strm, level, method, windowBits, memLevel, strategy, /* ========================================================================= * Check for a valid deflate stream state. Return 0 if ok, 1 if not. */ -local int deflateStateCheck(strm) - z_streamp strm; -{ +local int deflateStateCheck(z_streamp strm) { deflate_state *s; if (strm == Z_NULL || strm->zalloc == (alloc_func)0 || strm->zfree == (free_func)0) @@ -409,11 +541,8 @@ local int deflateStateCheck(strm) } /* ========================================================================= */ -int ZEXPORT deflateSetDictionary(strm, dictionary, dictLength) - z_streamp strm; - const Bytef *dictionary; - uInt dictLength; -{ +int ZEXPORT deflateSetDictionary(z_streamp strm, const Bytef *dictionary, + uInt dictLength) { deflate_state *s; uInt str, n; int wrap; @@ -478,11 +607,8 @@ int ZEXPORT deflateSetDictionary(strm, dictionary, dictLength) } /* ========================================================================= */ -int ZEXPORT deflateGetDictionary(strm, dictionary, dictLength) - z_streamp strm; - Bytef *dictionary; - uInt *dictLength; -{ +int ZEXPORT deflateGetDictionary(z_streamp strm, Bytef *dictionary, + uInt *dictLength) { deflate_state *s; uInt len; @@ -500,9 +626,7 @@ int ZEXPORT deflateGetDictionary(strm, dictionary, dictLength) } /* ========================================================================= */ -int ZEXPORT deflateResetKeep(strm) - z_streamp strm; -{ +int ZEXPORT deflateResetKeep(z_streamp strm) { deflate_state *s; if (deflateStateCheck(strm)) { @@ -537,10 +661,32 @@ int ZEXPORT deflateResetKeep(strm) return Z_OK; } +/* =========================================================================== + * Initialize the "longest match" routines for a new zlib stream + */ +local void lm_init(deflate_state *s) { + s->window_size = (ulg)2L*s->w_size; + + CLEAR_HASH(s); + + /* Set the default configuration parameters: + */ + s->max_lazy_match = configuration_table[s->level].max_lazy; + s->good_match = configuration_table[s->level].good_length; + s->nice_match = configuration_table[s->level].nice_length; + s->max_chain_length = configuration_table[s->level].max_chain; + + s->strstart = 0; + s->block_start = 0L; + s->lookahead = 0; + s->insert = 0; + s->match_length = s->prev_length = MIN_MATCH-1; + s->match_available = 0; + s->ins_h = 0; +} + /* ========================================================================= */ -int ZEXPORT deflateReset(strm) - z_streamp strm; -{ +int ZEXPORT deflateReset(z_streamp strm) { int ret; ret = deflateResetKeep(strm); @@ -550,10 +696,7 @@ int ZEXPORT deflateReset(strm) } /* ========================================================================= */ -int ZEXPORT deflateSetHeader(strm, head) - z_streamp strm; - gz_headerp head; -{ +int ZEXPORT deflateSetHeader(z_streamp strm, gz_headerp head) { if (deflateStateCheck(strm) || strm->state->wrap != 2) return Z_STREAM_ERROR; strm->state->gzhead = head; @@ -561,11 +704,7 @@ int ZEXPORT deflateSetHeader(strm, head) } /* ========================================================================= */ -int ZEXPORT deflatePending(strm, pending, bits) - unsigned *pending; - int *bits; - z_streamp strm; -{ +int ZEXPORT deflatePending(z_streamp strm, unsigned *pending, int *bits) { if (deflateStateCheck(strm)) return Z_STREAM_ERROR; if (pending != Z_NULL) *pending = strm->state->pending; @@ -575,11 +714,7 @@ int ZEXPORT deflatePending(strm, pending, bits) } /* ========================================================================= */ -int ZEXPORT deflatePrime(strm, bits, value) - z_streamp strm; - int bits; - int value; -{ +int ZEXPORT deflatePrime(z_streamp strm, int bits, int value) { deflate_state *s; int put; @@ -602,11 +737,7 @@ int ZEXPORT deflatePrime(strm, bits, value) } /* ========================================================================= */ -int ZEXPORT deflateParams(strm, level, strategy) - z_streamp strm; - int level; - int strategy; -{ +int ZEXPORT deflateParams(z_streamp strm, int level, int strategy) { deflate_state *s; compress_func func; @@ -651,13 +782,8 @@ int ZEXPORT deflateParams(strm, level, strategy) } /* ========================================================================= */ -int ZEXPORT deflateTune(strm, good_length, max_lazy, nice_length, max_chain) - z_streamp strm; - int good_length; - int max_lazy; - int nice_length; - int max_chain; -{ +int ZEXPORT deflateTune(z_streamp strm, int good_length, int max_lazy, + int nice_length, int max_chain) { deflate_state *s; if (deflateStateCheck(strm)) return Z_STREAM_ERROR; @@ -693,10 +819,7 @@ int ZEXPORT deflateTune(strm, good_length, max_lazy, nice_length, max_chain) * * Shifts are used to approximate divisions, for speed. */ -uLong ZEXPORT deflateBound(strm, sourceLen) - z_streamp strm; - uLong sourceLen; -{ +uLong ZEXPORT deflateBound(z_streamp strm, uLong sourceLen) { deflate_state *s; uLong fixedlen, storelen, wraplen; @@ -752,7 +875,8 @@ uLong ZEXPORT deflateBound(strm, sourceLen) /* if not default parameters, return one of the conservative bounds */ if (s->w_bits != 15 || s->hash_bits != 8 + 7) - return (s->w_bits <= s->hash_bits ? fixedlen : storelen) + wraplen; + return (s->w_bits <= s->hash_bits && s->level ? fixedlen : storelen) + + wraplen; /* default settings: return tight bound for that case -- ~0.03% overhead plus a small constant */ @@ -765,10 +889,7 @@ uLong ZEXPORT deflateBound(strm, sourceLen) * IN assertion: the stream state is correct and there is enough room in * pending_buf. */ -local void putShortMSB(s, b) - deflate_state *s; - uInt b; -{ +local void putShortMSB(deflate_state *s, uInt b) { put_byte(s, (Byte)(b >> 8)); put_byte(s, (Byte)(b & 0xff)); } @@ -779,9 +900,7 @@ local void putShortMSB(s, b) * applications may wish to modify it to avoid allocating a large * strm->next_out buffer and copying into it. (See also read_buf()). */ -local void flush_pending(strm) - z_streamp strm; -{ +local void flush_pending(z_streamp strm) { unsigned len; deflate_state *s = strm->state; @@ -812,10 +931,7 @@ local void flush_pending(strm) } while (0) /* ========================================================================= */ -int ZEXPORT deflate(strm, flush) - z_streamp strm; - int flush; -{ +int ZEXPORT deflate(z_streamp strm, int flush) { int old_flush; /* value of flush param for previous deflate call */ deflate_state *s; @@ -1127,9 +1243,7 @@ int ZEXPORT deflate(strm, flush) } /* ========================================================================= */ -int ZEXPORT deflateEnd(strm) - z_streamp strm; -{ +int ZEXPORT deflateEnd(z_streamp strm) { int status; if (deflateStateCheck(strm)) return Z_STREAM_ERROR; @@ -1153,11 +1267,10 @@ int ZEXPORT deflateEnd(strm) * To simplify the source, this is not supported for 16-bit MSDOS (which * doesn't have enough memory anyway to duplicate compression states). */ -int ZEXPORT deflateCopy(dest, source) - z_streamp dest; - z_streamp source; -{ +int ZEXPORT deflateCopy(z_streamp dest, z_streamp source) { #ifdef MAXSEG_64K + (void)dest; + (void)source; return Z_STREAM_ERROR; #else deflate_state *ds; @@ -1205,66 +1318,6 @@ int ZEXPORT deflateCopy(dest, source) #endif /* MAXSEG_64K */ } -/* =========================================================================== - * Read a new buffer from the current input stream, update the adler32 - * and total number of bytes read. All deflate() input goes through - * this function so some applications may wish to modify it to avoid - * allocating a large strm->next_in buffer and copying from it. - * (See also flush_pending()). - */ -local unsigned read_buf(strm, buf, size) - z_streamp strm; - Bytef *buf; - unsigned size; -{ - unsigned len = strm->avail_in; - - if (len > size) len = size; - if (len == 0) return 0; - - strm->avail_in -= len; - - zmemcpy(buf, strm->next_in, len); - if (strm->state->wrap == 1) { - strm->adler = adler32(strm->adler, buf, len); - } -#ifdef GZIP - else if (strm->state->wrap == 2) { - strm->adler = crc32(strm->adler, buf, len); - } -#endif - strm->next_in += len; - strm->total_in += len; - - return len; -} - -/* =========================================================================== - * Initialize the "longest match" routines for a new zlib stream - */ -local void lm_init(s) - deflate_state *s; -{ - s->window_size = (ulg)2L*s->w_size; - - CLEAR_HASH(s); - - /* Set the default configuration parameters: - */ - s->max_lazy_match = configuration_table[s->level].max_lazy; - s->good_match = configuration_table[s->level].good_length; - s->nice_match = configuration_table[s->level].nice_length; - s->max_chain_length = configuration_table[s->level].max_chain; - - s->strstart = 0; - s->block_start = 0L; - s->lookahead = 0; - s->insert = 0; - s->match_length = s->prev_length = MIN_MATCH-1; - s->match_available = 0; - s->ins_h = 0; -} - #ifndef FASTEST /* =========================================================================== * Set match_start to the longest match starting at the given string and @@ -1275,10 +1328,7 @@ local void lm_init(s) * string (strstart) and its distance is <= MAX_DIST, and prev_length >= 1 * OUT assertion: the match length is not greater than s->lookahead. */ -local uInt longest_match(s, cur_match) - deflate_state *s; - IPos cur_match; /* current match */ -{ +local uInt longest_match(deflate_state *s, IPos cur_match) { unsigned chain_length = s->max_chain_length;/* max hash chain length */ register Bytef *scan = s->window + s->strstart; /* current string */ register Bytef *match; /* matched string */ @@ -1426,10 +1476,7 @@ local uInt longest_match(s, cur_match) /* --------------------------------------------------------------------------- * Optimized version for FASTEST only */ -local uInt longest_match(s, cur_match) - deflate_state *s; - IPos cur_match; /* current match */ -{ +local uInt longest_match(deflate_state *s, IPos cur_match) { register Bytef *scan = s->window + s->strstart; /* current string */ register Bytef *match; /* matched string */ register int len; /* length of current match */ @@ -1490,11 +1537,7 @@ local uInt longest_match(s, cur_match) /* =========================================================================== * Check that the match at match_start is indeed a match. */ -local void check_match(s, start, match, length) - deflate_state *s; - IPos start, match; - int length; -{ +local void check_match(deflate_state *s, IPos start, IPos match, int length) { /* check that the match is indeed a match */ if (zmemcmp(s->window + match, s->window + start, length) != EQUAL) { @@ -1514,137 +1557,6 @@ local void check_match(s, start, match, length) # define check_match(s, start, match, length) #endif /* ZLIB_DEBUG */ -/* =========================================================================== - * Fill the window when the lookahead becomes insufficient. - * Updates strstart and lookahead. - * - * IN assertion: lookahead < MIN_LOOKAHEAD - * OUT assertions: strstart <= window_size-MIN_LOOKAHEAD - * At least one byte has been read, or avail_in == 0; reads are - * performed for at least two bytes (required for the zip translate_eol - * option -- not supported here). - */ -local void fill_window(s) - deflate_state *s; -{ - unsigned n; - unsigned more; /* Amount of free space at the end of the window. */ - uInt wsize = s->w_size; - - Assert(s->lookahead < MIN_LOOKAHEAD, "already enough lookahead"); - - do { - more = (unsigned)(s->window_size -(ulg)s->lookahead -(ulg)s->strstart); - - /* Deal with !@#$% 64K limit: */ - if (sizeof(int) <= 2) { - if (more == 0 && s->strstart == 0 && s->lookahead == 0) { - more = wsize; - - } else if (more == (unsigned)(-1)) { - /* Very unlikely, but possible on 16 bit machine if - * strstart == 0 && lookahead == 1 (input done a byte at time) - */ - more--; - } - } - - /* If the window is almost full and there is insufficient lookahead, - * move the upper half to the lower one to make room in the upper half. - */ - if (s->strstart >= wsize + MAX_DIST(s)) { - - zmemcpy(s->window, s->window + wsize, (unsigned)wsize - more); - s->match_start -= wsize; - s->strstart -= wsize; /* we now have strstart >= MAX_DIST */ - s->block_start -= (long) wsize; - if (s->insert > s->strstart) - s->insert = s->strstart; - slide_hash(s); - more += wsize; - } - if (s->strm->avail_in == 0) break; - - /* If there was no sliding: - * strstart <= WSIZE+MAX_DIST-1 && lookahead <= MIN_LOOKAHEAD - 1 && - * more == window_size - lookahead - strstart - * => more >= window_size - (MIN_LOOKAHEAD-1 + WSIZE + MAX_DIST-1) - * => more >= window_size - 2*WSIZE + 2 - * In the BIG_MEM or MMAP case (not yet supported), - * window_size == input_size + MIN_LOOKAHEAD && - * strstart + s->lookahead <= input_size => more >= MIN_LOOKAHEAD. - * Otherwise, window_size == 2*WSIZE so more >= 2. - * If there was sliding, more >= WSIZE. So in all cases, more >= 2. - */ - Assert(more >= 2, "more < 2"); - - n = read_buf(s->strm, s->window + s->strstart + s->lookahead, more); - s->lookahead += n; - - /* Initialize the hash value now that we have some input: */ - if (s->lookahead + s->insert >= MIN_MATCH) { - uInt str = s->strstart - s->insert; - s->ins_h = s->window[str]; - UPDATE_HASH(s, s->ins_h, s->window[str + 1]); -#if MIN_MATCH != 3 - Call UPDATE_HASH() MIN_MATCH-3 more times -#endif - while (s->insert) { - UPDATE_HASH(s, s->ins_h, s->window[str + MIN_MATCH-1]); -#ifndef FASTEST - s->prev[str & s->w_mask] = s->head[s->ins_h]; -#endif - s->head[s->ins_h] = (Pos)str; - str++; - s->insert--; - if (s->lookahead + s->insert < MIN_MATCH) - break; - } - } - /* If the whole input has less than MIN_MATCH bytes, ins_h is garbage, - * but this is not important since only literal bytes will be emitted. - */ - - } while (s->lookahead < MIN_LOOKAHEAD && s->strm->avail_in != 0); - - /* If the WIN_INIT bytes after the end of the current data have never been - * written, then zero those bytes in order to avoid memory check reports of - * the use of uninitialized (or uninitialised as Julian writes) bytes by - * the longest match routines. Update the high water mark for the next - * time through here. WIN_INIT is set to MAX_MATCH since the longest match - * routines allow scanning to strstart + MAX_MATCH, ignoring lookahead. - */ - if (s->high_water < s->window_size) { - ulg curr = s->strstart + (ulg)(s->lookahead); - ulg init; - - if (s->high_water < curr) { - /* Previous high water mark below current data -- zero WIN_INIT - * bytes or up to end of window, whichever is less. - */ - init = s->window_size - curr; - if (init > WIN_INIT) - init = WIN_INIT; - zmemzero(s->window + curr, (unsigned)init); - s->high_water = curr + init; - } - else if (s->high_water < (ulg)curr + WIN_INIT) { - /* High water mark at or above current data, but below current data - * plus WIN_INIT -- zero out to current data plus WIN_INIT, or up - * to end of window, whichever is less. - */ - init = (ulg)curr + WIN_INIT - s->high_water; - if (init > s->window_size - s->high_water) - init = s->window_size - s->high_water; - zmemzero(s->window + s->high_water, (unsigned)init); - s->high_water += init; - } - } - - Assert((ulg)s->strstart <= s->window_size - MIN_LOOKAHEAD, - "not enough room for search"); -} - /* =========================================================================== * Flush the current block, with given end-of-file flag. * IN assertion: strstart is set to the end of the current match. @@ -1687,10 +1599,7 @@ local void fill_window(s) * copied. It is most efficient with large input and output buffers, which * maximizes the opportunities to have a single copy from next_in to next_out. */ -local block_state deflate_stored(s, flush) - deflate_state *s; - int flush; -{ +local block_state deflate_stored(deflate_state *s, int flush) { /* Smallest worthy block size when not flushing or finishing. By default * this is 32K. This can be as small as 507 bytes for memLevel == 1. For * large input and output buffers, the stored block size will be larger. @@ -1874,10 +1783,7 @@ local block_state deflate_stored(s, flush) * new strings in the dictionary only for unmatched strings or for short * matches. It is used only for the fast compression options. */ -local block_state deflate_fast(s, flush) - deflate_state *s; - int flush; -{ +local block_state deflate_fast(deflate_state *s, int flush) { IPos hash_head; /* head of the hash chain */ int bflush; /* set if current block must be flushed */ @@ -1976,10 +1882,7 @@ local block_state deflate_fast(s, flush) * evaluation for matches: a match is finally adopted only if there is * no better match at the next window position. */ -local block_state deflate_slow(s, flush) - deflate_state *s; - int flush; -{ +local block_state deflate_slow(deflate_state *s, int flush) { IPos hash_head; /* head of hash chain */ int bflush; /* set if current block must be flushed */ @@ -2107,10 +2010,7 @@ local block_state deflate_slow(s, flush) * one. Do not maintain a hash table. (It will be regenerated if this run of * deflate switches away from Z_RLE.) */ -local block_state deflate_rle(s, flush) - deflate_state *s; - int flush; -{ +local block_state deflate_rle(deflate_state *s, int flush) { int bflush; /* set if current block must be flushed */ uInt prev; /* byte at distance one to match */ Bytef *scan, *strend; /* scan goes up to strend for length of run */ @@ -2181,10 +2081,7 @@ local block_state deflate_rle(s, flush) * For Z_HUFFMAN_ONLY, do not look for matches. Do not maintain a hash table. * (It will be regenerated if this run of deflate switches away from Huffman.) */ -local block_state deflate_huff(s, flush) - deflate_state *s; - int flush; -{ +local block_state deflate_huff(deflate_state *s, int flush) { int bflush; /* set if current block must be flushed */ for (;;) { diff --git a/3rdparty/zlib/deflate.h b/3rdparty/zlib/deflate.h index 1a06cd5f25..8696791429 100644 --- a/3rdparty/zlib/deflate.h +++ b/3rdparty/zlib/deflate.h @@ -291,14 +291,14 @@ typedef struct internal_state { memory checker errors from longest match routines */ /* in trees.c */ -void ZLIB_INTERNAL _tr_init OF((deflate_state *s)); -int ZLIB_INTERNAL _tr_tally OF((deflate_state *s, unsigned dist, unsigned lc)); -void ZLIB_INTERNAL _tr_flush_block OF((deflate_state *s, charf *buf, - ulg stored_len, int last)); -void ZLIB_INTERNAL _tr_flush_bits OF((deflate_state *s)); -void ZLIB_INTERNAL _tr_align OF((deflate_state *s)); -void ZLIB_INTERNAL _tr_stored_block OF((deflate_state *s, charf *buf, - ulg stored_len, int last)); +void ZLIB_INTERNAL _tr_init(deflate_state *s); +int ZLIB_INTERNAL _tr_tally(deflate_state *s, unsigned dist, unsigned lc); +void ZLIB_INTERNAL _tr_flush_block(deflate_state *s, charf *buf, + ulg stored_len, int last); +void ZLIB_INTERNAL _tr_flush_bits(deflate_state *s); +void ZLIB_INTERNAL _tr_align(deflate_state *s); +void ZLIB_INTERNAL _tr_stored_block(deflate_state *s, charf *buf, + ulg stored_len, int last); #define d_code(dist) \ ((dist) < 256 ? _dist_code[dist] : _dist_code[256+((dist)>>7)]) diff --git a/3rdparty/zlib/gzclose.c b/3rdparty/zlib/gzclose.c index caeb99a317..48d6a86f04 100644 --- a/3rdparty/zlib/gzclose.c +++ b/3rdparty/zlib/gzclose.c @@ -8,9 +8,7 @@ /* gzclose() is in a separate file so that it is linked in only if it is used. That way the other gzclose functions can be used instead to avoid linking in unneeded compression or decompression routines. */ -int ZEXPORT gzclose(file) - gzFile file; -{ +int ZEXPORT gzclose(gzFile file) { #ifndef NO_GZCOMPRESS gz_statep state; diff --git a/3rdparty/zlib/gzguts.h b/3rdparty/zlib/gzguts.h index 57faf37165..f9375047e8 100644 --- a/3rdparty/zlib/gzguts.h +++ b/3rdparty/zlib/gzguts.h @@ -7,9 +7,8 @@ # ifndef _LARGEFILE_SOURCE # define _LARGEFILE_SOURCE 1 # endif -# ifdef _FILE_OFFSET_BITS -# undef _FILE_OFFSET_BITS -# endif +# undef _FILE_OFFSET_BITS +# undef _TIME_BITS #endif #ifdef HAVE_HIDDEN @@ -119,8 +118,8 @@ /* gz* functions always use library allocation functions */ #ifndef STDC - extern voidp malloc OF((uInt size)); - extern void free OF((voidpf ptr)); + extern voidp malloc(uInt size); + extern void free(voidpf ptr); #endif /* get errno and strerror definition */ @@ -138,10 +137,10 @@ /* provide prototypes for these when building zlib without LFS */ #if !defined(_LARGEFILE64_SOURCE) || _LFS64_LARGEFILE-0 == 0 - ZEXTERN gzFile ZEXPORT gzopen64 OF((const char *, const char *)); - ZEXTERN z_off64_t ZEXPORT gzseek64 OF((gzFile, z_off64_t, int)); - ZEXTERN z_off64_t ZEXPORT gztell64 OF((gzFile)); - ZEXTERN z_off64_t ZEXPORT gzoffset64 OF((gzFile)); + ZEXTERN gzFile ZEXPORT gzopen64(const char *, const char *); + ZEXTERN z_off64_t ZEXPORT gzseek64(gzFile, z_off64_t, int); + ZEXTERN z_off64_t ZEXPORT gztell64(gzFile); + ZEXTERN z_off64_t ZEXPORT gzoffset64(gzFile); #endif /* default memLevel */ @@ -203,9 +202,9 @@ typedef struct { typedef gz_state FAR *gz_statep; /* shared functions */ -void ZLIB_INTERNAL gz_error OF((gz_statep, int, const char *)); +void ZLIB_INTERNAL gz_error(gz_statep, int, const char *); #if defined UNDER_CE -char ZLIB_INTERNAL *gz_strwinerror OF((DWORD error)); +char ZLIB_INTERNAL *gz_strwinerror(DWORD error); #endif /* GT_OFF(x), where x is an unsigned value, is true if x > maximum z_off64_t @@ -214,6 +213,6 @@ char ZLIB_INTERNAL *gz_strwinerror OF((DWORD error)); #ifdef INT_MAX # define GT_OFF(x) (sizeof(int) == sizeof(z_off64_t) && (x) > INT_MAX) #else -unsigned ZLIB_INTERNAL gz_intmax OF((void)); +unsigned ZLIB_INTERNAL gz_intmax(void); # define GT_OFF(x) (sizeof(int) == sizeof(z_off64_t) && (x) > gz_intmax()) #endif diff --git a/3rdparty/zlib/gzlib.c b/3rdparty/zlib/gzlib.c index 55da46a453..29fc4486fb 100644 --- a/3rdparty/zlib/gzlib.c +++ b/3rdparty/zlib/gzlib.c @@ -15,10 +15,6 @@ #endif #endif -/* Local functions */ -local void gz_reset OF((gz_statep)); -local gzFile gz_open OF((const void *, int, const char *)); - #if defined UNDER_CE /* Map the Windows error number in ERROR to a locale-dependent error message @@ -30,9 +26,7 @@ local gzFile gz_open OF((const void *, int, const char *)); The gz_strwinerror function does not change the current setting of GetLastError. */ -char ZLIB_INTERNAL *gz_strwinerror(error) - DWORD error; -{ +char ZLIB_INTERNAL *gz_strwinerror(DWORD error) { static char buf[1024]; wchar_t *msgbuf; @@ -72,9 +66,7 @@ char ZLIB_INTERNAL *gz_strwinerror(error) #endif /* UNDER_CE */ /* Reset gzip file state */ -local void gz_reset(state) - gz_statep state; -{ +local void gz_reset(gz_statep state) { state->x.have = 0; /* no output data available */ if (state->mode == GZ_READ) { /* for reading ... */ state->eof = 0; /* not at end of file */ @@ -90,11 +82,7 @@ local void gz_reset(state) } /* Open a gzip file either by name or file descriptor. */ -local gzFile gz_open(path, fd, mode) - const void *path; - int fd; - const char *mode; -{ +local gzFile gz_open(const void *path, int fd, const char *mode) { gz_statep state; z_size_t len; int oflag; @@ -269,26 +257,17 @@ local gzFile gz_open(path, fd, mode) } /* -- see zlib.h -- */ -gzFile ZEXPORT gzopen(path, mode) - const char *path; - const char *mode; -{ +gzFile ZEXPORT gzopen(const char *path, const char *mode) { return gz_open(path, -1, mode); } /* -- see zlib.h -- */ -gzFile ZEXPORT gzopen64(path, mode) - const char *path; - const char *mode; -{ +gzFile ZEXPORT gzopen64(const char *path, const char *mode) { return gz_open(path, -1, mode); } /* -- see zlib.h -- */ -gzFile ZEXPORT gzdopen(fd, mode) - int fd; - const char *mode; -{ +gzFile ZEXPORT gzdopen(int fd, const char *mode) { char *path; /* identifier for error messages */ gzFile gz; @@ -306,19 +285,13 @@ gzFile ZEXPORT gzdopen(fd, mode) /* -- see zlib.h -- */ #ifdef WIDECHAR -gzFile ZEXPORT gzopen_w(path, mode) - const wchar_t *path; - const char *mode; -{ +gzFile ZEXPORT gzopen_w(const wchar_t *path, const char *mode) { return gz_open(path, -2, mode); } #endif /* -- see zlib.h -- */ -int ZEXPORT gzbuffer(file, size) - gzFile file; - unsigned size; -{ +int ZEXPORT gzbuffer(gzFile file, unsigned size) { gz_statep state; /* get internal structure and check integrity */ @@ -335,16 +308,14 @@ int ZEXPORT gzbuffer(file, size) /* check and set requested size */ if ((size << 1) < size) return -1; /* need to be able to double it */ - if (size < 2) - size = 2; /* need two bytes to check magic header */ + if (size < 8) + size = 8; /* needed to behave well with flushing */ state->want = size; return 0; } /* -- see zlib.h -- */ -int ZEXPORT gzrewind(file) - gzFile file; -{ +int ZEXPORT gzrewind(gzFile file) { gz_statep state; /* get internal structure */ @@ -365,11 +336,7 @@ int ZEXPORT gzrewind(file) } /* -- see zlib.h -- */ -z_off64_t ZEXPORT gzseek64(file, offset, whence) - gzFile file; - z_off64_t offset; - int whence; -{ +z_off64_t ZEXPORT gzseek64(gzFile file, z_off64_t offset, int whence) { unsigned n; z_off64_t ret; gz_statep state; @@ -442,11 +409,7 @@ z_off64_t ZEXPORT gzseek64(file, offset, whence) } /* -- see zlib.h -- */ -z_off_t ZEXPORT gzseek(file, offset, whence) - gzFile file; - z_off_t offset; - int whence; -{ +z_off_t ZEXPORT gzseek(gzFile file, z_off_t offset, int whence) { z_off64_t ret; ret = gzseek64(file, (z_off64_t)offset, whence); @@ -454,9 +417,7 @@ z_off_t ZEXPORT gzseek(file, offset, whence) } /* -- see zlib.h -- */ -z_off64_t ZEXPORT gztell64(file) - gzFile file; -{ +z_off64_t ZEXPORT gztell64(gzFile file) { gz_statep state; /* get internal structure and check integrity */ @@ -471,9 +432,7 @@ z_off64_t ZEXPORT gztell64(file) } /* -- see zlib.h -- */ -z_off_t ZEXPORT gztell(file) - gzFile file; -{ +z_off_t ZEXPORT gztell(gzFile file) { z_off64_t ret; ret = gztell64(file); @@ -481,9 +440,7 @@ z_off_t ZEXPORT gztell(file) } /* -- see zlib.h -- */ -z_off64_t ZEXPORT gzoffset64(file) - gzFile file; -{ +z_off64_t ZEXPORT gzoffset64(gzFile file) { z_off64_t offset; gz_statep state; @@ -504,9 +461,7 @@ z_off64_t ZEXPORT gzoffset64(file) } /* -- see zlib.h -- */ -z_off_t ZEXPORT gzoffset(file) - gzFile file; -{ +z_off_t ZEXPORT gzoffset(gzFile file) { z_off64_t ret; ret = gzoffset64(file); @@ -514,9 +469,7 @@ z_off_t ZEXPORT gzoffset(file) } /* -- see zlib.h -- */ -int ZEXPORT gzeof(file) - gzFile file; -{ +int ZEXPORT gzeof(gzFile file) { gz_statep state; /* get internal structure and check integrity */ @@ -531,10 +484,7 @@ int ZEXPORT gzeof(file) } /* -- see zlib.h -- */ -const char * ZEXPORT gzerror(file, errnum) - gzFile file; - int *errnum; -{ +const char * ZEXPORT gzerror(gzFile file, int *errnum) { gz_statep state; /* get internal structure and check integrity */ @@ -552,9 +502,7 @@ const char * ZEXPORT gzerror(file, errnum) } /* -- see zlib.h -- */ -void ZEXPORT gzclearerr(file) - gzFile file; -{ +void ZEXPORT gzclearerr(gzFile file) { gz_statep state; /* get internal structure and check integrity */ @@ -578,11 +526,7 @@ void ZEXPORT gzclearerr(file) memory). Simply save the error message as a static string. If there is an allocation failure constructing the error message, then convert the error to out of memory. */ -void ZLIB_INTERNAL gz_error(state, err, msg) - gz_statep state; - int err; - const char *msg; -{ +void ZLIB_INTERNAL gz_error(gz_statep state, int err, const char *msg) { /* free previously allocated message and clear */ if (state->msg != NULL) { if (state->err != Z_MEM_ERROR) @@ -624,8 +568,7 @@ void ZLIB_INTERNAL gz_error(state, err, msg) available) -- we need to do this to cover cases where 2's complement not used, since C standard permits 1's complement and sign-bit representations, otherwise we could just use ((unsigned)-1) >> 1 */ -unsigned ZLIB_INTERNAL gz_intmax() -{ +unsigned ZLIB_INTERNAL gz_intmax(void) { unsigned p, q; p = 1; diff --git a/3rdparty/zlib/gzread.c b/3rdparty/zlib/gzread.c index dd77381596..4168cbc887 100644 --- a/3rdparty/zlib/gzread.c +++ b/3rdparty/zlib/gzread.c @@ -5,25 +5,12 @@ #include "gzguts.h" -/* Local functions */ -local int gz_load OF((gz_statep, unsigned char *, unsigned, unsigned *)); -local int gz_avail OF((gz_statep)); -local int gz_look OF((gz_statep)); -local int gz_decomp OF((gz_statep)); -local int gz_fetch OF((gz_statep)); -local int gz_skip OF((gz_statep, z_off64_t)); -local z_size_t gz_read OF((gz_statep, voidp, z_size_t)); - /* Use read() to load a buffer -- return -1 on error, otherwise 0. Read from state->fd, and update state->eof, state->err, and state->msg as appropriate. This function needs to loop on read(), since read() is not guaranteed to read the number of bytes requested, depending on the type of descriptor. */ -local int gz_load(state, buf, len, have) - gz_statep state; - unsigned char *buf; - unsigned len; - unsigned *have; -{ +local int gz_load(gz_statep state, unsigned char *buf, unsigned len, + unsigned *have) { int ret; unsigned get, max = ((unsigned)-1 >> 2) + 1; @@ -53,9 +40,7 @@ local int gz_load(state, buf, len, have) If strm->avail_in != 0, then the current data is moved to the beginning of the input buffer, and then the remainder of the buffer is loaded with the available data from the input file. */ -local int gz_avail(state) - gz_statep state; -{ +local int gz_avail(gz_statep state) { unsigned got; z_streamp strm = &(state->strm); @@ -88,9 +73,7 @@ local int gz_avail(state) case, all further file reads will be directly to either the output buffer or a user buffer. If decompressing, the inflate state will be initialized. gz_look() will return 0 on success or -1 on failure. */ -local int gz_look(state) - gz_statep state; -{ +local int gz_look(gz_statep state) { z_streamp strm = &(state->strm); /* allocate read buffers and inflate memory */ @@ -170,9 +153,7 @@ local int gz_look(state) data. If the gzip stream completes, state->how is reset to LOOK to look for the next gzip stream or raw data, once state->x.have is depleted. Returns 0 on success, -1 on failure. */ -local int gz_decomp(state) - gz_statep state; -{ +local int gz_decomp(gz_statep state) { int ret = Z_OK; unsigned had; z_streamp strm = &(state->strm); @@ -224,9 +205,7 @@ local int gz_decomp(state) looked for to determine whether to copy or decompress. Returns -1 on error, otherwise 0. gz_fetch() will leave state->how as COPY or GZIP unless the end of the input file has been reached and all data has been processed. */ -local int gz_fetch(state) - gz_statep state; -{ +local int gz_fetch(gz_statep state) { z_streamp strm = &(state->strm); do { @@ -254,10 +233,7 @@ local int gz_fetch(state) } /* Skip len uncompressed bytes of output. Return -1 on error, 0 on success. */ -local int gz_skip(state, len) - gz_statep state; - z_off64_t len; -{ +local int gz_skip(gz_statep state, z_off64_t len) { unsigned n; /* skip over len bytes or reach end-of-file, whichever comes first */ @@ -289,11 +265,7 @@ local int gz_skip(state, len) input. Return the number of bytes read. If zero is returned, either the end of file was reached, or there was an error. state->err must be consulted in that case to determine which. */ -local z_size_t gz_read(state, buf, len) - gz_statep state; - voidp buf; - z_size_t len; -{ +local z_size_t gz_read(gz_statep state, voidp buf, z_size_t len) { z_size_t got; unsigned n; @@ -370,11 +342,7 @@ local z_size_t gz_read(state, buf, len) } /* -- see zlib.h -- */ -int ZEXPORT gzread(file, buf, len) - gzFile file; - voidp buf; - unsigned len; -{ +int ZEXPORT gzread(gzFile file, voidp buf, unsigned len) { gz_statep state; /* get internal structure */ @@ -406,12 +374,7 @@ int ZEXPORT gzread(file, buf, len) } /* -- see zlib.h -- */ -z_size_t ZEXPORT gzfread(buf, size, nitems, file) - voidp buf; - z_size_t size; - z_size_t nitems; - gzFile file; -{ +z_size_t ZEXPORT gzfread(voidp buf, z_size_t size, z_size_t nitems, gzFile file) { z_size_t len; gz_statep state; @@ -442,9 +405,7 @@ z_size_t ZEXPORT gzfread(buf, size, nitems, file) #else # undef gzgetc #endif -int ZEXPORT gzgetc(file) - gzFile file; -{ +int ZEXPORT gzgetc(gzFile file) { unsigned char buf[1]; gz_statep state; @@ -469,17 +430,12 @@ int ZEXPORT gzgetc(file) return gz_read(state, buf, 1) < 1 ? -1 : buf[0]; } -int ZEXPORT gzgetc_(file) -gzFile file; -{ +int ZEXPORT gzgetc_(gzFile file) { return gzgetc(file); } /* -- see zlib.h -- */ -int ZEXPORT gzungetc(c, file) - int c; - gzFile file; -{ +int ZEXPORT gzungetc(int c, gzFile file) { gz_statep state; /* get internal structure */ @@ -487,6 +443,10 @@ int ZEXPORT gzungetc(c, file) return -1; state = (gz_statep)file; + /* in case this was just opened, set up the input buffer */ + if (state->mode == GZ_READ && state->how == LOOK && state->x.have == 0) + (void)gz_look(state); + /* check that we're reading and that there's no (serious) error */ if (state->mode != GZ_READ || (state->err != Z_OK && state->err != Z_BUF_ERROR)) @@ -536,11 +496,7 @@ int ZEXPORT gzungetc(c, file) } /* -- see zlib.h -- */ -char * ZEXPORT gzgets(file, buf, len) - gzFile file; - char *buf; - int len; -{ +char * ZEXPORT gzgets(gzFile file, char *buf, int len) { unsigned left, n; char *str; unsigned char *eol; @@ -600,9 +556,7 @@ char * ZEXPORT gzgets(file, buf, len) } /* -- see zlib.h -- */ -int ZEXPORT gzdirect(file) - gzFile file; -{ +int ZEXPORT gzdirect(gzFile file) { gz_statep state; /* get internal structure */ @@ -620,9 +574,7 @@ int ZEXPORT gzdirect(file) } /* -- see zlib.h -- */ -int ZEXPORT gzclose_r(file) - gzFile file; -{ +int ZEXPORT gzclose_r(gzFile file) { int ret, err; gz_statep state; diff --git a/3rdparty/zlib/gzwrite.c b/3rdparty/zlib/gzwrite.c index eb8a0e5893..435b4621b5 100644 --- a/3rdparty/zlib/gzwrite.c +++ b/3rdparty/zlib/gzwrite.c @@ -5,18 +5,10 @@ #include "gzguts.h" -/* Local functions */ -local int gz_init OF((gz_statep)); -local int gz_comp OF((gz_statep, int)); -local int gz_zero OF((gz_statep, z_off64_t)); -local z_size_t gz_write OF((gz_statep, voidpc, z_size_t)); - /* Initialize state for writing a gzip file. Mark initialization by setting state->size to non-zero. Return -1 on a memory allocation failure, or 0 on success. */ -local int gz_init(state) - gz_statep state; -{ +local int gz_init(gz_statep state) { int ret; z_streamp strm = &(state->strm); @@ -70,10 +62,7 @@ local int gz_init(state) deflate() flush value. If flush is Z_FINISH, then the deflate() state is reset to start a new gzip stream. If gz->direct is true, then simply write to the output file without compressing, and ignore flush. */ -local int gz_comp(state, flush) - gz_statep state; - int flush; -{ +local int gz_comp(gz_statep state, int flush) { int ret, writ; unsigned have, put, max = ((unsigned)-1 >> 2) + 1; z_streamp strm = &(state->strm); @@ -151,10 +140,7 @@ local int gz_comp(state, flush) /* Compress len zeros to output. Return -1 on a write error or memory allocation failure by gz_comp(), or 0 on success. */ -local int gz_zero(state, len) - gz_statep state; - z_off64_t len; -{ +local int gz_zero(gz_statep state, z_off64_t len) { int first; unsigned n; z_streamp strm = &(state->strm); @@ -184,11 +170,7 @@ local int gz_zero(state, len) /* Write len bytes from buf to file. Return the number of bytes written. If the returned value is less than len, then there was an error. */ -local z_size_t gz_write(state, buf, len) - gz_statep state; - voidpc buf; - z_size_t len; -{ +local z_size_t gz_write(gz_statep state, voidpc buf, z_size_t len) { z_size_t put = len; /* if len is zero, avoid unnecessary operations */ @@ -252,11 +234,7 @@ local z_size_t gz_write(state, buf, len) } /* -- see zlib.h -- */ -int ZEXPORT gzwrite(file, buf, len) - gzFile file; - voidpc buf; - unsigned len; -{ +int ZEXPORT gzwrite(gzFile file, voidpc buf, unsigned len) { gz_statep state; /* get internal structure */ @@ -280,12 +258,8 @@ int ZEXPORT gzwrite(file, buf, len) } /* -- see zlib.h -- */ -z_size_t ZEXPORT gzfwrite(buf, size, nitems, file) - voidpc buf; - z_size_t size; - z_size_t nitems; - gzFile file; -{ +z_size_t ZEXPORT gzfwrite(voidpc buf, z_size_t size, z_size_t nitems, + gzFile file) { z_size_t len; gz_statep state; @@ -310,10 +284,7 @@ z_size_t ZEXPORT gzfwrite(buf, size, nitems, file) } /* -- see zlib.h -- */ -int ZEXPORT gzputc(file, c) - gzFile file; - int c; -{ +int ZEXPORT gzputc(gzFile file, int c) { unsigned have; unsigned char buf[1]; gz_statep state; @@ -358,10 +329,7 @@ int ZEXPORT gzputc(file, c) } /* -- see zlib.h -- */ -int ZEXPORT gzputs(file, s) - gzFile file; - const char *s; -{ +int ZEXPORT gzputs(gzFile file, const char *s) { z_size_t len, put; gz_statep state; @@ -388,8 +356,7 @@ int ZEXPORT gzputs(file, s) #include /* -- see zlib.h -- */ -int ZEXPORTVA gzvprintf(gzFile file, const char *format, va_list va) -{ +int ZEXPORTVA gzvprintf(gzFile file, const char *format, va_list va) { int len; unsigned left; char *next; @@ -460,8 +427,7 @@ int ZEXPORTVA gzvprintf(gzFile file, const char *format, va_list va) return len; } -int ZEXPORTVA gzprintf(gzFile file, const char *format, ...) -{ +int ZEXPORTVA gzprintf(gzFile file, const char *format, ...) { va_list va; int ret; @@ -474,13 +440,10 @@ int ZEXPORTVA gzprintf(gzFile file, const char *format, ...) #else /* !STDC && !Z_HAVE_STDARG_H */ /* -- see zlib.h -- */ -int ZEXPORTVA gzprintf(file, format, a1, a2, a3, a4, a5, a6, a7, a8, a9, a10, - a11, a12, a13, a14, a15, a16, a17, a18, a19, a20) - gzFile file; - const char *format; - int a1, a2, a3, a4, a5, a6, a7, a8, a9, a10, - a11, a12, a13, a14, a15, a16, a17, a18, a19, a20; -{ +int ZEXPORTVA gzprintf(gzFile file, const char *format, int a1, int a2, int a3, + int a4, int a5, int a6, int a7, int a8, int a9, int a10, + int a11, int a12, int a13, int a14, int a15, int a16, + int a17, int a18, int a19, int a20) { unsigned len, left; char *next; gz_statep state; @@ -562,10 +525,7 @@ int ZEXPORTVA gzprintf(file, format, a1, a2, a3, a4, a5, a6, a7, a8, a9, a10, #endif /* -- see zlib.h -- */ -int ZEXPORT gzflush(file, flush) - gzFile file; - int flush; -{ +int ZEXPORT gzflush(gzFile file, int flush) { gz_statep state; /* get internal structure */ @@ -594,11 +554,7 @@ int ZEXPORT gzflush(file, flush) } /* -- see zlib.h -- */ -int ZEXPORT gzsetparams(file, level, strategy) - gzFile file; - int level; - int strategy; -{ +int ZEXPORT gzsetparams(gzFile file, int level, int strategy) { gz_statep state; z_streamp strm; @@ -609,7 +565,7 @@ int ZEXPORT gzsetparams(file, level, strategy) strm = &(state->strm); /* check that we're writing and that there's no error */ - if (state->mode != GZ_WRITE || state->err != Z_OK) + if (state->mode != GZ_WRITE || state->err != Z_OK || state->direct) return Z_STREAM_ERROR; /* if no change is requested, then do nothing */ @@ -636,9 +592,7 @@ int ZEXPORT gzsetparams(file, level, strategy) } /* -- see zlib.h -- */ -int ZEXPORT gzclose_w(file) - gzFile file; -{ +int ZEXPORT gzclose_w(gzFile file) { int ret = Z_OK; gz_statep state; diff --git a/3rdparty/zlib/infback.c b/3rdparty/zlib/infback.c index babeaf1806..e7b25b307a 100644 --- a/3rdparty/zlib/infback.c +++ b/3rdparty/zlib/infback.c @@ -15,9 +15,6 @@ #include "inflate.h" #include "inffast.h" -/* function prototypes */ -local void fixedtables OF((struct inflate_state FAR *state)); - /* strm provides memory allocation functions in zalloc and zfree, or Z_NULL to use the library memory allocation functions. @@ -25,13 +22,9 @@ local void fixedtables OF((struct inflate_state FAR *state)); windowBits is in the range 8..15, and window is a user-supplied window and output buffer that is 2**windowBits bytes. */ -int ZEXPORT inflateBackInit_(strm, windowBits, window, version, stream_size) -z_streamp strm; -int windowBits; -unsigned char FAR *window; -const char *version; -int stream_size; -{ +int ZEXPORT inflateBackInit_(z_streamp strm, int windowBits, + unsigned char FAR *window, const char *version, + int stream_size) { struct inflate_state FAR *state; if (version == Z_NULL || version[0] != ZLIB_VERSION[0] || @@ -80,9 +73,7 @@ int stream_size; used for threaded applications, since the rewriting of the tables and virgin may not be thread-safe. */ -local void fixedtables(state) -struct inflate_state FAR *state; -{ +local void fixedtables(struct inflate_state FAR *state) { #ifdef BUILDFIXED static int virgin = 1; static code *lenfix, *distfix; @@ -248,13 +239,8 @@ struct inflate_state FAR *state; inflateBack() can also return Z_STREAM_ERROR if the input parameters are not correct, i.e. strm is Z_NULL or the state was not initialized. */ -int ZEXPORT inflateBack(strm, in, in_desc, out, out_desc) -z_streamp strm; -in_func in; -void FAR *in_desc; -out_func out; -void FAR *out_desc; -{ +int ZEXPORT inflateBack(z_streamp strm, in_func in, void FAR *in_desc, + out_func out, void FAR *out_desc) { struct inflate_state FAR *state; z_const unsigned char FAR *next; /* next input */ unsigned char FAR *put; /* next output */ @@ -632,9 +618,7 @@ void FAR *out_desc; return ret; } -int ZEXPORT inflateBackEnd(strm) -z_streamp strm; -{ +int ZEXPORT inflateBackEnd(z_streamp strm) { if (strm == Z_NULL || strm->state == Z_NULL || strm->zfree == (free_func)0) return Z_STREAM_ERROR; ZFREE(strm, strm->state); diff --git a/3rdparty/zlib/inffast.c b/3rdparty/zlib/inffast.c index 1fec7f363f..9354676e78 100644 --- a/3rdparty/zlib/inffast.c +++ b/3rdparty/zlib/inffast.c @@ -47,10 +47,7 @@ requires strm->avail_out >= 258 for each loop to avoid checking for output space. */ -void ZLIB_INTERNAL inflate_fast(strm, start) -z_streamp strm; -unsigned start; /* inflate()'s starting value for strm->avail_out */ -{ +void ZLIB_INTERNAL inflate_fast(z_streamp strm, unsigned start) { struct inflate_state FAR *state; z_const unsigned char FAR *in; /* local strm->next_in */ z_const unsigned char FAR *last; /* have enough input while in < last */ diff --git a/3rdparty/zlib/inffast.h b/3rdparty/zlib/inffast.h index e5c1aa4ca8..49c6d156c5 100644 --- a/3rdparty/zlib/inffast.h +++ b/3rdparty/zlib/inffast.h @@ -8,4 +8,4 @@ subject to change. Applications should only use zlib.h. */ -void ZLIB_INTERNAL inflate_fast OF((z_streamp strm, unsigned start)); +void ZLIB_INTERNAL inflate_fast(z_streamp strm, unsigned start); diff --git a/3rdparty/zlib/inflate.c b/3rdparty/zlib/inflate.c index c84f52507c..bce123b363 100644 --- a/3rdparty/zlib/inflate.c +++ b/3rdparty/zlib/inflate.c @@ -91,20 +91,7 @@ # endif #endif -/* function prototypes */ -local int inflateStateCheck OF((z_streamp strm)); -local void fixedtables OF((struct inflate_state FAR *state)); -local int updatewindow OF((z_streamp strm, const unsigned char FAR *end, - unsigned copy)); -#ifdef BUILDFIXED - void makefixed OF((void)); -#endif -local unsigned syncsearch OF((unsigned FAR *have, const unsigned char FAR *buf, - unsigned len)); - -local int inflateStateCheck(strm) -z_streamp strm; -{ +local int inflateStateCheck(z_streamp strm) { struct inflate_state FAR *state; if (strm == Z_NULL || strm->zalloc == (alloc_func)0 || strm->zfree == (free_func)0) @@ -116,9 +103,7 @@ z_streamp strm; return 0; } -int ZEXPORT inflateResetKeep(strm) -z_streamp strm; -{ +int ZEXPORT inflateResetKeep(z_streamp strm) { struct inflate_state FAR *state; if (inflateStateCheck(strm)) return Z_STREAM_ERROR; @@ -142,9 +127,7 @@ z_streamp strm; return Z_OK; } -int ZEXPORT inflateReset(strm) -z_streamp strm; -{ +int ZEXPORT inflateReset(z_streamp strm) { struct inflate_state FAR *state; if (inflateStateCheck(strm)) return Z_STREAM_ERROR; @@ -155,10 +138,7 @@ z_streamp strm; return inflateResetKeep(strm); } -int ZEXPORT inflateReset2(strm, windowBits) -z_streamp strm; -int windowBits; -{ +int ZEXPORT inflateReset2(z_streamp strm, int windowBits) { int wrap; struct inflate_state FAR *state; @@ -195,12 +175,8 @@ int windowBits; return inflateReset(strm); } -int ZEXPORT inflateInit2_(strm, windowBits, version, stream_size) -z_streamp strm; -int windowBits; -const char *version; -int stream_size; -{ +int ZEXPORT inflateInit2_(z_streamp strm, int windowBits, + const char *version, int stream_size) { int ret; struct inflate_state FAR *state; @@ -240,22 +216,17 @@ int stream_size; return ret; } -int ZEXPORT inflateInit_(strm, version, stream_size) -z_streamp strm; -const char *version; -int stream_size; -{ +int ZEXPORT inflateInit_(z_streamp strm, const char *version, + int stream_size) { return inflateInit2_(strm, DEF_WBITS, version, stream_size); } -int ZEXPORT inflatePrime(strm, bits, value) -z_streamp strm; -int bits; -int value; -{ +int ZEXPORT inflatePrime(z_streamp strm, int bits, int value) { struct inflate_state FAR *state; if (inflateStateCheck(strm)) return Z_STREAM_ERROR; + if (bits == 0) + return Z_OK; state = (struct inflate_state FAR *)strm->state; if (bits < 0) { state->hold = 0; @@ -279,9 +250,7 @@ int value; used for threaded applications, since the rewriting of the tables and virgin may not be thread-safe. */ -local void fixedtables(state) -struct inflate_state FAR *state; -{ +local void fixedtables(struct inflate_state FAR *state) { #ifdef BUILDFIXED static int virgin = 1; static code *lenfix, *distfix; @@ -343,7 +312,7 @@ struct inflate_state FAR *state; a.out > inffixed.h */ -void makefixed() +void makefixed(void) { unsigned low, size; struct inflate_state state; @@ -397,11 +366,7 @@ void makefixed() output will fall in the output data, making match copies simpler and faster. The advantage may be dependent on the size of the processor's data caches. */ -local int updatewindow(strm, end, copy) -z_streamp strm; -const Bytef *end; -unsigned copy; -{ +local int updatewindow(z_streamp strm, const Bytef *end, unsigned copy) { struct inflate_state FAR *state; unsigned dist; @@ -623,10 +588,7 @@ unsigned copy; will return Z_BUF_ERROR if it has not reached the end of the stream. */ -int ZEXPORT inflate(strm, flush) -z_streamp strm; -int flush; -{ +int ZEXPORT inflate(z_streamp strm, int flush) { struct inflate_state FAR *state; z_const unsigned char FAR *next; /* next input */ unsigned char FAR *put; /* next output */ @@ -1302,9 +1264,7 @@ int flush; return ret; } -int ZEXPORT inflateEnd(strm) -z_streamp strm; -{ +int ZEXPORT inflateEnd(z_streamp strm) { struct inflate_state FAR *state; if (inflateStateCheck(strm)) return Z_STREAM_ERROR; @@ -1316,11 +1276,8 @@ z_streamp strm; return Z_OK; } -int ZEXPORT inflateGetDictionary(strm, dictionary, dictLength) -z_streamp strm; -Bytef *dictionary; -uInt *dictLength; -{ +int ZEXPORT inflateGetDictionary(z_streamp strm, Bytef *dictionary, + uInt *dictLength) { struct inflate_state FAR *state; /* check state */ @@ -1339,11 +1296,8 @@ uInt *dictLength; return Z_OK; } -int ZEXPORT inflateSetDictionary(strm, dictionary, dictLength) -z_streamp strm; -const Bytef *dictionary; -uInt dictLength; -{ +int ZEXPORT inflateSetDictionary(z_streamp strm, const Bytef *dictionary, + uInt dictLength) { struct inflate_state FAR *state; unsigned long dictid; int ret; @@ -1374,10 +1328,7 @@ uInt dictLength; return Z_OK; } -int ZEXPORT inflateGetHeader(strm, head) -z_streamp strm; -gz_headerp head; -{ +int ZEXPORT inflateGetHeader(z_streamp strm, gz_headerp head) { struct inflate_state FAR *state; /* check state */ @@ -1402,11 +1353,8 @@ gz_headerp head; called again with more data and the *have state. *have is initialized to zero for the first call. */ -local unsigned syncsearch(have, buf, len) -unsigned FAR *have; -const unsigned char FAR *buf; -unsigned len; -{ +local unsigned syncsearch(unsigned FAR *have, const unsigned char FAR *buf, + unsigned len) { unsigned got; unsigned next; @@ -1425,9 +1373,7 @@ unsigned len; return next; } -int ZEXPORT inflateSync(strm) -z_streamp strm; -{ +int ZEXPORT inflateSync(z_streamp strm) { unsigned len; /* number of bytes to look at or looked at */ int flags; /* temporary to save header status */ unsigned long in, out; /* temporary to save total_in and total_out */ @@ -1483,9 +1429,7 @@ z_streamp strm; block. When decompressing, PPP checks that at the end of input packet, inflate is waiting for these length bytes. */ -int ZEXPORT inflateSyncPoint(strm) -z_streamp strm; -{ +int ZEXPORT inflateSyncPoint(z_streamp strm) { struct inflate_state FAR *state; if (inflateStateCheck(strm)) return Z_STREAM_ERROR; @@ -1493,10 +1437,7 @@ z_streamp strm; return state->mode == STORED && state->bits == 0; } -int ZEXPORT inflateCopy(dest, source) -z_streamp dest; -z_streamp source; -{ +int ZEXPORT inflateCopy(z_streamp dest, z_streamp source) { struct inflate_state FAR *state; struct inflate_state FAR *copy; unsigned char FAR *window; @@ -1540,10 +1481,7 @@ z_streamp source; return Z_OK; } -int ZEXPORT inflateUndermine(strm, subvert) -z_streamp strm; -int subvert; -{ +int ZEXPORT inflateUndermine(z_streamp strm, int subvert) { struct inflate_state FAR *state; if (inflateStateCheck(strm)) return Z_STREAM_ERROR; @@ -1558,10 +1496,7 @@ int subvert; #endif } -int ZEXPORT inflateValidate(strm, check) -z_streamp strm; -int check; -{ +int ZEXPORT inflateValidate(z_streamp strm, int check) { struct inflate_state FAR *state; if (inflateStateCheck(strm)) return Z_STREAM_ERROR; @@ -1573,9 +1508,7 @@ int check; return Z_OK; } -long ZEXPORT inflateMark(strm) -z_streamp strm; -{ +long ZEXPORT inflateMark(z_streamp strm) { struct inflate_state FAR *state; if (inflateStateCheck(strm)) @@ -1586,9 +1519,7 @@ z_streamp strm; (state->mode == MATCH ? state->was - state->length : 0)); } -unsigned long ZEXPORT inflateCodesUsed(strm) -z_streamp strm; -{ +unsigned long ZEXPORT inflateCodesUsed(z_streamp strm) { struct inflate_state FAR *state; if (inflateStateCheck(strm)) return (unsigned long)-1; state = (struct inflate_state FAR *)strm->state; diff --git a/3rdparty/zlib/inftrees.c b/3rdparty/zlib/inftrees.c index 57d2793bec..8a208c2daa 100644 --- a/3rdparty/zlib/inftrees.c +++ b/3rdparty/zlib/inftrees.c @@ -1,5 +1,5 @@ /* inftrees.c -- generate Huffman trees for efficient decoding - * Copyright (C) 1995-2022 Mark Adler + * Copyright (C) 1995-2023 Mark Adler * For conditions of distribution and use, see copyright notice in zlib.h */ @@ -9,7 +9,7 @@ #define MAXBITS 15 const char inflate_copyright[] = - " inflate 1.2.13 Copyright 1995-2022 Mark Adler "; + " inflate 1.3 Copyright 1995-2023 Mark Adler "; /* If you use the zlib library in a product, an acknowledgment is welcome in the documentation of your product. If for some reason you cannot @@ -29,14 +29,9 @@ const char inflate_copyright[] = table index bits. It will differ if the request is greater than the longest code or if it is less than the shortest code. */ -int ZLIB_INTERNAL inflate_table(type, lens, codes, table, bits, work) -codetype type; -unsigned short FAR *lens; -unsigned codes; -code FAR * FAR *table; -unsigned FAR *bits; -unsigned short FAR *work; -{ +int ZLIB_INTERNAL inflate_table(codetype type, unsigned short FAR *lens, + unsigned codes, code FAR * FAR *table, + unsigned FAR *bits, unsigned short FAR *work) { unsigned len; /* a code's length in bits */ unsigned sym; /* index of code symbols */ unsigned min, max; /* minimum and maximum code lengths */ @@ -62,7 +57,7 @@ unsigned short FAR *work; 35, 43, 51, 59, 67, 83, 99, 115, 131, 163, 195, 227, 258, 0, 0}; static const unsigned short lext[31] = { /* Length codes 257..285 extra */ 16, 16, 16, 16, 16, 16, 16, 16, 17, 17, 17, 17, 18, 18, 18, 18, - 19, 19, 19, 19, 20, 20, 20, 20, 21, 21, 21, 21, 16, 194, 65}; + 19, 19, 19, 19, 20, 20, 20, 20, 21, 21, 21, 21, 16, 198, 203}; static const unsigned short dbase[32] = { /* Distance codes 0..29 base */ 1, 2, 3, 4, 5, 7, 9, 13, 17, 25, 33, 49, 65, 97, 129, 193, 257, 385, 513, 769, 1025, 1537, 2049, 3073, 4097, 6145, diff --git a/3rdparty/zlib/inftrees.h b/3rdparty/zlib/inftrees.h index f53665311c..a10712d8cb 100644 --- a/3rdparty/zlib/inftrees.h +++ b/3rdparty/zlib/inftrees.h @@ -57,6 +57,6 @@ typedef enum { DISTS } codetype; -int ZLIB_INTERNAL inflate_table OF((codetype type, unsigned short FAR *lens, - unsigned codes, code FAR * FAR *table, - unsigned FAR *bits, unsigned short FAR *work)); +int ZLIB_INTERNAL inflate_table(codetype type, unsigned short FAR *lens, + unsigned codes, code FAR * FAR *table, + unsigned FAR *bits, unsigned short FAR *work); diff --git a/3rdparty/zlib/trees.c b/3rdparty/zlib/trees.c index 5f305c4722..8dbdc40bac 100644 --- a/3rdparty/zlib/trees.c +++ b/3rdparty/zlib/trees.c @@ -122,39 +122,116 @@ struct static_tree_desc_s { int max_length; /* max bit length for the codes */ }; -local const static_tree_desc static_l_desc = +#ifdef NO_INIT_GLOBAL_POINTERS +# define TCONST +#else +# define TCONST const +#endif + +local TCONST static_tree_desc static_l_desc = {static_ltree, extra_lbits, LITERALS+1, L_CODES, MAX_BITS}; -local const static_tree_desc static_d_desc = +local TCONST static_tree_desc static_d_desc = {static_dtree, extra_dbits, 0, D_CODES, MAX_BITS}; -local const static_tree_desc static_bl_desc = +local TCONST static_tree_desc static_bl_desc = {(const ct_data *)0, extra_blbits, 0, BL_CODES, MAX_BL_BITS}; /* =========================================================================== - * Local (static) routines in this file. + * Output a short LSB first on the stream. + * IN assertion: there is enough room in pendingBuf. */ +#define put_short(s, w) { \ + put_byte(s, (uch)((w) & 0xff)); \ + put_byte(s, (uch)((ush)(w) >> 8)); \ +} -local void tr_static_init OF((void)); -local void init_block OF((deflate_state *s)); -local void pqdownheap OF((deflate_state *s, ct_data *tree, int k)); -local void gen_bitlen OF((deflate_state *s, tree_desc *desc)); -local void gen_codes OF((ct_data *tree, int max_code, ushf *bl_count)); -local void build_tree OF((deflate_state *s, tree_desc *desc)); -local void scan_tree OF((deflate_state *s, ct_data *tree, int max_code)); -local void send_tree OF((deflate_state *s, ct_data *tree, int max_code)); -local int build_bl_tree OF((deflate_state *s)); -local void send_all_trees OF((deflate_state *s, int lcodes, int dcodes, - int blcodes)); -local void compress_block OF((deflate_state *s, const ct_data *ltree, - const ct_data *dtree)); -local int detect_data_type OF((deflate_state *s)); -local unsigned bi_reverse OF((unsigned code, int len)); -local void bi_windup OF((deflate_state *s)); -local void bi_flush OF((deflate_state *s)); +/* =========================================================================== + * Reverse the first len bits of a code, using straightforward code (a faster + * method would use a table) + * IN assertion: 1 <= len <= 15 + */ +local unsigned bi_reverse(unsigned code, int len) { + register unsigned res = 0; + do { + res |= code & 1; + code >>= 1, res <<= 1; + } while (--len > 0); + return res >> 1; +} + +/* =========================================================================== + * Flush the bit buffer, keeping at most 7 bits in it. + */ +local void bi_flush(deflate_state *s) { + if (s->bi_valid == 16) { + put_short(s, s->bi_buf); + s->bi_buf = 0; + s->bi_valid = 0; + } else if (s->bi_valid >= 8) { + put_byte(s, (Byte)s->bi_buf); + s->bi_buf >>= 8; + s->bi_valid -= 8; + } +} + +/* =========================================================================== + * Flush the bit buffer and align the output on a byte boundary + */ +local void bi_windup(deflate_state *s) { + if (s->bi_valid > 8) { + put_short(s, s->bi_buf); + } else if (s->bi_valid > 0) { + put_byte(s, (Byte)s->bi_buf); + } + s->bi_buf = 0; + s->bi_valid = 0; +#ifdef ZLIB_DEBUG + s->bits_sent = (s->bits_sent + 7) & ~7; +#endif +} + +/* =========================================================================== + * Generate the codes for a given tree and bit counts (which need not be + * optimal). + * IN assertion: the array bl_count contains the bit length statistics for + * the given tree and the field len is set for all tree elements. + * OUT assertion: the field code is set for all tree elements of non + * zero code length. + */ +local void gen_codes(ct_data *tree, int max_code, ushf *bl_count) { + ush next_code[MAX_BITS+1]; /* next code value for each bit length */ + unsigned code = 0; /* running code value */ + int bits; /* bit index */ + int n; /* code index */ + + /* The distribution counts are first used to generate the code values + * without bit reversal. + */ + for (bits = 1; bits <= MAX_BITS; bits++) { + code = (code + bl_count[bits - 1]) << 1; + next_code[bits] = (ush)code; + } + /* Check that the bit counts in bl_count are consistent. The last code + * must be all ones. + */ + Assert (code + bl_count[MAX_BITS] - 1 == (1 << MAX_BITS) - 1, + "inconsistent bit counts"); + Tracev((stderr,"\ngen_codes: max_code %d ", max_code)); + + for (n = 0; n <= max_code; n++) { + int len = tree[n].Len; + if (len == 0) continue; + /* Now reverse the bits */ + tree[n].Code = (ush)bi_reverse(next_code[len]++, len); + + Tracecv(tree != static_ltree, (stderr,"\nn %3d %c l %2d c %4x (%x) ", + n, (isgraph(n) ? n : ' '), len, tree[n].Code, next_code[len] - 1)); + } +} #ifdef GEN_TREES_H -local void gen_trees_header OF((void)); +local void gen_trees_header(void); #endif #ifndef ZLIB_DEBUG @@ -167,27 +244,12 @@ local void gen_trees_header OF((void)); send_bits(s, tree[c].Code, tree[c].Len); } #endif -/* =========================================================================== - * Output a short LSB first on the stream. - * IN assertion: there is enough room in pendingBuf. - */ -#define put_short(s, w) { \ - put_byte(s, (uch)((w) & 0xff)); \ - put_byte(s, (uch)((ush)(w) >> 8)); \ -} - /* =========================================================================== * Send a value on a given number of bits. * IN assertion: length <= 16 and value fits in length bits. */ #ifdef ZLIB_DEBUG -local void send_bits OF((deflate_state *s, int value, int length)); - -local void send_bits(s, value, length) - deflate_state *s; - int value; /* value to send */ - int length; /* number of bits */ -{ +local void send_bits(deflate_state *s, int value, int length) { Tracevv((stderr," l %2d v %4x ", length, value)); Assert(length > 0 && length <= 15, "invalid length"); s->bits_sent += (ulg)length; @@ -229,8 +291,7 @@ local void send_bits(s, value, length) /* =========================================================================== * Initialize the various 'constant' tables. */ -local void tr_static_init() -{ +local void tr_static_init(void) { #if defined(GEN_TREES_H) || !defined(STDC) static int static_init_done = 0; int n; /* iterates over tree elements */ @@ -323,8 +384,7 @@ local void tr_static_init() ((i) == (last)? "\n};\n\n" : \ ((i) % (width) == (width) - 1 ? ",\n" : ", ")) -void gen_trees_header() -{ +void gen_trees_header(void) { FILE *header = fopen("trees.h", "w"); int i; @@ -373,12 +433,26 @@ void gen_trees_header() } #endif /* GEN_TREES_H */ +/* =========================================================================== + * Initialize a new block. + */ +local void init_block(deflate_state *s) { + int n; /* iterates over tree elements */ + + /* Initialize the trees. */ + for (n = 0; n < L_CODES; n++) s->dyn_ltree[n].Freq = 0; + for (n = 0; n < D_CODES; n++) s->dyn_dtree[n].Freq = 0; + for (n = 0; n < BL_CODES; n++) s->bl_tree[n].Freq = 0; + + s->dyn_ltree[END_BLOCK].Freq = 1; + s->opt_len = s->static_len = 0L; + s->sym_next = s->matches = 0; +} + /* =========================================================================== * Initialize the tree data structures for a new zlib stream. */ -void ZLIB_INTERNAL _tr_init(s) - deflate_state *s; -{ +void ZLIB_INTERNAL _tr_init(deflate_state *s) { tr_static_init(); s->l_desc.dyn_tree = s->dyn_ltree; @@ -401,24 +475,6 @@ void ZLIB_INTERNAL _tr_init(s) init_block(s); } -/* =========================================================================== - * Initialize a new block. - */ -local void init_block(s) - deflate_state *s; -{ - int n; /* iterates over tree elements */ - - /* Initialize the trees. */ - for (n = 0; n < L_CODES; n++) s->dyn_ltree[n].Freq = 0; - for (n = 0; n < D_CODES; n++) s->dyn_dtree[n].Freq = 0; - for (n = 0; n < BL_CODES; n++) s->bl_tree[n].Freq = 0; - - s->dyn_ltree[END_BLOCK].Freq = 1; - s->opt_len = s->static_len = 0L; - s->sym_next = s->matches = 0; -} - #define SMALLEST 1 /* Index within the heap array of least frequent node in the Huffman tree */ @@ -448,11 +504,7 @@ local void init_block(s) * when the heap property is re-established (each father smaller than its * two sons). */ -local void pqdownheap(s, tree, k) - deflate_state *s; - ct_data *tree; /* the tree to restore */ - int k; /* node to move down */ -{ +local void pqdownheap(deflate_state *s, ct_data *tree, int k) { int v = s->heap[k]; int j = k << 1; /* left son of k */ while (j <= s->heap_len) { @@ -483,10 +535,7 @@ local void pqdownheap(s, tree, k) * The length opt_len is updated; static_len is also updated if stree is * not null. */ -local void gen_bitlen(s, desc) - deflate_state *s; - tree_desc *desc; /* the tree descriptor */ -{ +local void gen_bitlen(deflate_state *s, tree_desc *desc) { ct_data *tree = desc->dyn_tree; int max_code = desc->max_code; const ct_data *stree = desc->stat_desc->static_tree; @@ -561,48 +610,9 @@ local void gen_bitlen(s, desc) } } -/* =========================================================================== - * Generate the codes for a given tree and bit counts (which need not be - * optimal). - * IN assertion: the array bl_count contains the bit length statistics for - * the given tree and the field len is set for all tree elements. - * OUT assertion: the field code is set for all tree elements of non - * zero code length. - */ -local void gen_codes(tree, max_code, bl_count) - ct_data *tree; /* the tree to decorate */ - int max_code; /* largest code with non zero frequency */ - ushf *bl_count; /* number of codes at each bit length */ -{ - ush next_code[MAX_BITS+1]; /* next code value for each bit length */ - unsigned code = 0; /* running code value */ - int bits; /* bit index */ - int n; /* code index */ - - /* The distribution counts are first used to generate the code values - * without bit reversal. - */ - for (bits = 1; bits <= MAX_BITS; bits++) { - code = (code + bl_count[bits - 1]) << 1; - next_code[bits] = (ush)code; - } - /* Check that the bit counts in bl_count are consistent. The last code - * must be all ones. - */ - Assert (code + bl_count[MAX_BITS] - 1 == (1 << MAX_BITS) - 1, - "inconsistent bit counts"); - Tracev((stderr,"\ngen_codes: max_code %d ", max_code)); - - for (n = 0; n <= max_code; n++) { - int len = tree[n].Len; - if (len == 0) continue; - /* Now reverse the bits */ - tree[n].Code = (ush)bi_reverse(next_code[len]++, len); - - Tracecv(tree != static_ltree, (stderr,"\nn %3d %c l %2d c %4x (%x) ", - n, (isgraph(n) ? n : ' '), len, tree[n].Code, next_code[len] - 1)); - } -} +#ifdef DUMP_BL_TREE +# include +#endif /* =========================================================================== * Construct one Huffman tree and assigns the code bit strings and lengths. @@ -612,10 +622,7 @@ local void gen_codes(tree, max_code, bl_count) * and corresponding code. The length opt_len is updated; static_len is * also updated if stree is not null. The field max_code is set. */ -local void build_tree(s, desc) - deflate_state *s; - tree_desc *desc; /* the tree descriptor */ -{ +local void build_tree(deflate_state *s, tree_desc *desc) { ct_data *tree = desc->dyn_tree; const ct_data *stree = desc->stat_desc->static_tree; int elems = desc->stat_desc->elems; @@ -700,11 +707,7 @@ local void build_tree(s, desc) * Scan a literal or distance tree to determine the frequencies of the codes * in the bit length tree. */ -local void scan_tree(s, tree, max_code) - deflate_state *s; - ct_data *tree; /* the tree to be scanned */ - int max_code; /* and its largest code of non zero frequency */ -{ +local void scan_tree(deflate_state *s, ct_data *tree, int max_code) { int n; /* iterates over all tree elements */ int prevlen = -1; /* last emitted length */ int curlen; /* length of current code */ @@ -745,11 +748,7 @@ local void scan_tree(s, tree, max_code) * Send a literal or distance tree in compressed form, using the codes in * bl_tree. */ -local void send_tree(s, tree, max_code) - deflate_state *s; - ct_data *tree; /* the tree to be scanned */ - int max_code; /* and its largest code of non zero frequency */ -{ +local void send_tree(deflate_state *s, ct_data *tree, int max_code) { int n; /* iterates over all tree elements */ int prevlen = -1; /* last emitted length */ int curlen; /* length of current code */ @@ -796,9 +795,7 @@ local void send_tree(s, tree, max_code) * Construct the Huffman tree for the bit lengths and return the index in * bl_order of the last bit length code to send. */ -local int build_bl_tree(s) - deflate_state *s; -{ +local int build_bl_tree(deflate_state *s) { int max_blindex; /* index of last bit length code of non zero freq */ /* Determine the bit length frequencies for literal and distance trees */ @@ -831,10 +828,8 @@ local int build_bl_tree(s) * lengths of the bit length codes, the literal tree and the distance tree. * IN assertion: lcodes >= 257, dcodes >= 1, blcodes >= 4. */ -local void send_all_trees(s, lcodes, dcodes, blcodes) - deflate_state *s; - int lcodes, dcodes, blcodes; /* number of codes for each tree */ -{ +local void send_all_trees(deflate_state *s, int lcodes, int dcodes, + int blcodes) { int rank; /* index in bl_order */ Assert (lcodes >= 257 && dcodes >= 1 && blcodes >= 4, "not enough codes"); @@ -860,12 +855,8 @@ local void send_all_trees(s, lcodes, dcodes, blcodes) /* =========================================================================== * Send a stored block */ -void ZLIB_INTERNAL _tr_stored_block(s, buf, stored_len, last) - deflate_state *s; - charf *buf; /* input block */ - ulg stored_len; /* length of input block */ - int last; /* one if this is the last block for a file */ -{ +void ZLIB_INTERNAL _tr_stored_block(deflate_state *s, charf *buf, + ulg stored_len, int last) { send_bits(s, (STORED_BLOCK<<1) + last, 3); /* send block type */ bi_windup(s); /* align on byte boundary */ put_short(s, (ush)stored_len); @@ -884,9 +875,7 @@ void ZLIB_INTERNAL _tr_stored_block(s, buf, stored_len, last) /* =========================================================================== * Flush the bits in the bit buffer to pending output (leaves at most 7 bits) */ -void ZLIB_INTERNAL _tr_flush_bits(s) - deflate_state *s; -{ +void ZLIB_INTERNAL _tr_flush_bits(deflate_state *s) { bi_flush(s); } @@ -894,9 +883,7 @@ void ZLIB_INTERNAL _tr_flush_bits(s) * Send one empty static block to give enough lookahead for inflate. * This takes 10 bits, of which 7 may remain in the bit buffer. */ -void ZLIB_INTERNAL _tr_align(s) - deflate_state *s; -{ +void ZLIB_INTERNAL _tr_align(deflate_state *s) { send_bits(s, STATIC_TREES<<1, 3); send_code(s, END_BLOCK, static_ltree); #ifdef ZLIB_DEBUG @@ -905,16 +892,99 @@ void ZLIB_INTERNAL _tr_align(s) bi_flush(s); } +/* =========================================================================== + * Send the block data compressed using the given Huffman trees + */ +local void compress_block(deflate_state *s, const ct_data *ltree, + const ct_data *dtree) { + unsigned dist; /* distance of matched string */ + int lc; /* match length or unmatched char (if dist == 0) */ + unsigned sx = 0; /* running index in sym_buf */ + unsigned code; /* the code to send */ + int extra; /* number of extra bits to send */ + + if (s->sym_next != 0) do { + dist = s->sym_buf[sx++] & 0xff; + dist += (unsigned)(s->sym_buf[sx++] & 0xff) << 8; + lc = s->sym_buf[sx++]; + if (dist == 0) { + send_code(s, lc, ltree); /* send a literal byte */ + Tracecv(isgraph(lc), (stderr," '%c' ", lc)); + } else { + /* Here, lc is the match length - MIN_MATCH */ + code = _length_code[lc]; + send_code(s, code + LITERALS + 1, ltree); /* send length code */ + extra = extra_lbits[code]; + if (extra != 0) { + lc -= base_length[code]; + send_bits(s, lc, extra); /* send the extra length bits */ + } + dist--; /* dist is now the match distance - 1 */ + code = d_code(dist); + Assert (code < D_CODES, "bad d_code"); + + send_code(s, code, dtree); /* send the distance code */ + extra = extra_dbits[code]; + if (extra != 0) { + dist -= (unsigned)base_dist[code]; + send_bits(s, dist, extra); /* send the extra distance bits */ + } + } /* literal or match pair ? */ + + /* Check that the overlay between pending_buf and sym_buf is ok: */ + Assert(s->pending < s->lit_bufsize + sx, "pendingBuf overflow"); + + } while (sx < s->sym_next); + + send_code(s, END_BLOCK, ltree); +} + +/* =========================================================================== + * Check if the data type is TEXT or BINARY, using the following algorithm: + * - TEXT if the two conditions below are satisfied: + * a) There are no non-portable control characters belonging to the + * "block list" (0..6, 14..25, 28..31). + * b) There is at least one printable character belonging to the + * "allow list" (9 {TAB}, 10 {LF}, 13 {CR}, 32..255). + * - BINARY otherwise. + * - The following partially-portable control characters form a + * "gray list" that is ignored in this detection algorithm: + * (7 {BEL}, 8 {BS}, 11 {VT}, 12 {FF}, 26 {SUB}, 27 {ESC}). + * IN assertion: the fields Freq of dyn_ltree are set. + */ +local int detect_data_type(deflate_state *s) { + /* block_mask is the bit mask of block-listed bytes + * set bits 0..6, 14..25, and 28..31 + * 0xf3ffc07f = binary 11110011111111111100000001111111 + */ + unsigned long block_mask = 0xf3ffc07fUL; + int n; + + /* Check for non-textual ("block-listed") bytes. */ + for (n = 0; n <= 31; n++, block_mask >>= 1) + if ((block_mask & 1) && (s->dyn_ltree[n].Freq != 0)) + return Z_BINARY; + + /* Check for textual ("allow-listed") bytes. */ + if (s->dyn_ltree[9].Freq != 0 || s->dyn_ltree[10].Freq != 0 + || s->dyn_ltree[13].Freq != 0) + return Z_TEXT; + for (n = 32; n < LITERALS; n++) + if (s->dyn_ltree[n].Freq != 0) + return Z_TEXT; + + /* There are no "block-listed" or "allow-listed" bytes: + * this stream either is empty or has tolerated ("gray-listed") bytes only. + */ + return Z_BINARY; +} + /* =========================================================================== * Determine the best encoding for the current block: dynamic trees, static * trees or store, and write out the encoded block. */ -void ZLIB_INTERNAL _tr_flush_block(s, buf, stored_len, last) - deflate_state *s; - charf *buf; /* input block, or NULL if too old */ - ulg stored_len; /* length of input block */ - int last; /* one if this is the last block for a file */ -{ +void ZLIB_INTERNAL _tr_flush_block(deflate_state *s, charf *buf, + ulg stored_len, int last) { ulg opt_lenb, static_lenb; /* opt_len and static_len in bytes */ int max_blindex = 0; /* index of last bit length code of non zero freq */ @@ -1011,11 +1081,7 @@ void ZLIB_INTERNAL _tr_flush_block(s, buf, stored_len, last) * Save the match info and tally the frequency counts. Return true if * the current block must be flushed. */ -int ZLIB_INTERNAL _tr_tally(s, dist, lc) - deflate_state *s; - unsigned dist; /* distance of matched string */ - unsigned lc; /* match length - MIN_MATCH or unmatched char (dist==0) */ -{ +int ZLIB_INTERNAL _tr_tally(deflate_state *s, unsigned dist, unsigned lc) { s->sym_buf[s->sym_next++] = (uch)dist; s->sym_buf[s->sym_next++] = (uch)(dist >> 8); s->sym_buf[s->sym_next++] = (uch)lc; @@ -1035,147 +1101,3 @@ int ZLIB_INTERNAL _tr_tally(s, dist, lc) } return (s->sym_next == s->sym_end); } - -/* =========================================================================== - * Send the block data compressed using the given Huffman trees - */ -local void compress_block(s, ltree, dtree) - deflate_state *s; - const ct_data *ltree; /* literal tree */ - const ct_data *dtree; /* distance tree */ -{ - unsigned dist; /* distance of matched string */ - int lc; /* match length or unmatched char (if dist == 0) */ - unsigned sx = 0; /* running index in sym_buf */ - unsigned code; /* the code to send */ - int extra; /* number of extra bits to send */ - - if (s->sym_next != 0) do { - dist = s->sym_buf[sx++] & 0xff; - dist += (unsigned)(s->sym_buf[sx++] & 0xff) << 8; - lc = s->sym_buf[sx++]; - if (dist == 0) { - send_code(s, lc, ltree); /* send a literal byte */ - Tracecv(isgraph(lc), (stderr," '%c' ", lc)); - } else { - /* Here, lc is the match length - MIN_MATCH */ - code = _length_code[lc]; - send_code(s, code + LITERALS + 1, ltree); /* send length code */ - extra = extra_lbits[code]; - if (extra != 0) { - lc -= base_length[code]; - send_bits(s, lc, extra); /* send the extra length bits */ - } - dist--; /* dist is now the match distance - 1 */ - code = d_code(dist); - Assert (code < D_CODES, "bad d_code"); - - send_code(s, code, dtree); /* send the distance code */ - extra = extra_dbits[code]; - if (extra != 0) { - dist -= (unsigned)base_dist[code]; - send_bits(s, dist, extra); /* send the extra distance bits */ - } - } /* literal or match pair ? */ - - /* Check that the overlay between pending_buf and sym_buf is ok: */ - Assert(s->pending < s->lit_bufsize + sx, "pendingBuf overflow"); - - } while (sx < s->sym_next); - - send_code(s, END_BLOCK, ltree); -} - -/* =========================================================================== - * Check if the data type is TEXT or BINARY, using the following algorithm: - * - TEXT if the two conditions below are satisfied: - * a) There are no non-portable control characters belonging to the - * "block list" (0..6, 14..25, 28..31). - * b) There is at least one printable character belonging to the - * "allow list" (9 {TAB}, 10 {LF}, 13 {CR}, 32..255). - * - BINARY otherwise. - * - The following partially-portable control characters form a - * "gray list" that is ignored in this detection algorithm: - * (7 {BEL}, 8 {BS}, 11 {VT}, 12 {FF}, 26 {SUB}, 27 {ESC}). - * IN assertion: the fields Freq of dyn_ltree are set. - */ -local int detect_data_type(s) - deflate_state *s; -{ - /* block_mask is the bit mask of block-listed bytes - * set bits 0..6, 14..25, and 28..31 - * 0xf3ffc07f = binary 11110011111111111100000001111111 - */ - unsigned long block_mask = 0xf3ffc07fUL; - int n; - - /* Check for non-textual ("block-listed") bytes. */ - for (n = 0; n <= 31; n++, block_mask >>= 1) - if ((block_mask & 1) && (s->dyn_ltree[n].Freq != 0)) - return Z_BINARY; - - /* Check for textual ("allow-listed") bytes. */ - if (s->dyn_ltree[9].Freq != 0 || s->dyn_ltree[10].Freq != 0 - || s->dyn_ltree[13].Freq != 0) - return Z_TEXT; - for (n = 32; n < LITERALS; n++) - if (s->dyn_ltree[n].Freq != 0) - return Z_TEXT; - - /* There are no "block-listed" or "allow-listed" bytes: - * this stream either is empty or has tolerated ("gray-listed") bytes only. - */ - return Z_BINARY; -} - -/* =========================================================================== - * Reverse the first len bits of a code, using straightforward code (a faster - * method would use a table) - * IN assertion: 1 <= len <= 15 - */ -local unsigned bi_reverse(code, len) - unsigned code; /* the value to invert */ - int len; /* its bit length */ -{ - register unsigned res = 0; - do { - res |= code & 1; - code >>= 1, res <<= 1; - } while (--len > 0); - return res >> 1; -} - -/* =========================================================================== - * Flush the bit buffer, keeping at most 7 bits in it. - */ -local void bi_flush(s) - deflate_state *s; -{ - if (s->bi_valid == 16) { - put_short(s, s->bi_buf); - s->bi_buf = 0; - s->bi_valid = 0; - } else if (s->bi_valid >= 8) { - put_byte(s, (Byte)s->bi_buf); - s->bi_buf >>= 8; - s->bi_valid -= 8; - } -} - -/* =========================================================================== - * Flush the bit buffer and align the output on a byte boundary - */ -local void bi_windup(s) - deflate_state *s; -{ - if (s->bi_valid > 8) { - put_short(s, s->bi_buf); - } else if (s->bi_valid > 0) { - put_byte(s, (Byte)s->bi_buf); - } - s->bi_buf = 0; - s->bi_valid = 0; -#ifdef ZLIB_DEBUG - s->bits_sent = (s->bits_sent + 7) & ~7; -#endif -} diff --git a/3rdparty/zlib/uncompr.c b/3rdparty/zlib/uncompr.c index f9532f46c1..5e256663b4 100644 --- a/3rdparty/zlib/uncompr.c +++ b/3rdparty/zlib/uncompr.c @@ -24,12 +24,8 @@ Z_DATA_ERROR if the input data was corrupted, including if the input data is an incomplete zlib stream. */ -int ZEXPORT uncompress2(dest, destLen, source, sourceLen) - Bytef *dest; - uLongf *destLen; - const Bytef *source; - uLong *sourceLen; -{ +int ZEXPORT uncompress2(Bytef *dest, uLongf *destLen, const Bytef *source, + uLong *sourceLen) { z_stream stream; int err; const uInt max = (uInt)-1; @@ -83,11 +79,7 @@ int ZEXPORT uncompress2(dest, destLen, source, sourceLen) err; } -int ZEXPORT uncompress(dest, destLen, source, sourceLen) - Bytef *dest; - uLongf *destLen; - const Bytef *source; - uLong sourceLen; -{ +int ZEXPORT uncompress(Bytef *dest, uLongf *destLen, const Bytef *source, + uLong sourceLen) { return uncompress2(dest, destLen, source, &sourceLen); } diff --git a/3rdparty/zlib/zconf.h b/3rdparty/zlib/zconf.h index bf977d3e70..fb76ffe312 100644 --- a/3rdparty/zlib/zconf.h +++ b/3rdparty/zlib/zconf.h @@ -241,7 +241,11 @@ #endif #ifdef Z_SOLO - typedef unsigned long z_size_t; +# ifdef _WIN64 + typedef unsigned long long z_size_t; +# else + typedef unsigned long z_size_t; +# endif #else # define z_longlong long long # if defined(NO_SIZE_T) @@ -520,7 +524,7 @@ typedef uLong FAR uLongf; #if !defined(_WIN32) && defined(Z_LARGE64) # define z_off64_t off64_t #else -# if defined(_WIN32) && !defined(__GNUC__) && !defined(Z_SOLO) +# if defined(_WIN32) && !defined(__GNUC__) # define z_off64_t __int64 # else # define z_off64_t z_off_t diff --git a/3rdparty/zlib/zlib.h b/3rdparty/zlib/zlib.h index 953cb5012d..6b7244f994 100644 --- a/3rdparty/zlib/zlib.h +++ b/3rdparty/zlib/zlib.h @@ -1,7 +1,7 @@ /* zlib.h -- interface of the 'zlib' general purpose compression library - version 1.2.13, October 13th, 2022 + version 1.3, August 18th, 2023 - Copyright (C) 1995-2022 Jean-loup Gailly and Mark Adler + Copyright (C) 1995-2023 Jean-loup Gailly and Mark Adler This software is provided 'as-is', without any express or implied warranty. In no event will the authors be held liable for any damages @@ -37,11 +37,11 @@ extern "C" { #endif -#define ZLIB_VERSION "1.2.13" -#define ZLIB_VERNUM 0x12d0 +#define ZLIB_VERSION "1.3" +#define ZLIB_VERNUM 0x1300 #define ZLIB_VER_MAJOR 1 -#define ZLIB_VER_MINOR 2 -#define ZLIB_VER_REVISION 13 +#define ZLIB_VER_MINOR 3 +#define ZLIB_VER_REVISION 0 #define ZLIB_VER_SUBREVISION 0 /* @@ -78,8 +78,8 @@ extern "C" { even in the case of corrupted input. */ -typedef voidpf (*alloc_func) OF((voidpf opaque, uInt items, uInt size)); -typedef void (*free_func) OF((voidpf opaque, voidpf address)); +typedef voidpf (*alloc_func)(voidpf opaque, uInt items, uInt size); +typedef void (*free_func)(voidpf opaque, voidpf address); struct internal_state; @@ -217,7 +217,7 @@ typedef gz_header FAR *gz_headerp; /* basic functions */ -ZEXTERN const char * ZEXPORT zlibVersion OF((void)); +ZEXTERN const char * ZEXPORT zlibVersion(void); /* The application can compare zlibVersion and ZLIB_VERSION for consistency. If the first character differs, the library code actually used is not compatible with the zlib.h header file used by the application. This check @@ -225,12 +225,12 @@ ZEXTERN const char * ZEXPORT zlibVersion OF((void)); */ /* -ZEXTERN int ZEXPORT deflateInit OF((z_streamp strm, int level)); +ZEXTERN int ZEXPORT deflateInit(z_streamp strm, int level); Initializes the internal stream state for compression. The fields zalloc, zfree and opaque must be initialized before by the caller. If zalloc and zfree are set to Z_NULL, deflateInit updates them to use default - allocation functions. + allocation functions. total_in, total_out, adler, and msg are initialized. The compression level must be Z_DEFAULT_COMPRESSION, or between 0 and 9: 1 gives best speed, 9 gives best compression, 0 gives no compression at all @@ -247,7 +247,7 @@ ZEXTERN int ZEXPORT deflateInit OF((z_streamp strm, int level)); */ -ZEXTERN int ZEXPORT deflate OF((z_streamp strm, int flush)); +ZEXTERN int ZEXPORT deflate(z_streamp strm, int flush); /* deflate compresses as much data as possible, and stops when the input buffer becomes empty or the output buffer becomes full. It may introduce @@ -320,8 +320,8 @@ ZEXTERN int ZEXPORT deflate OF((z_streamp strm, int flush)); with the same value of the flush parameter and more output space (updated avail_out), until the flush is complete (deflate returns with non-zero avail_out). In the case of a Z_FULL_FLUSH or Z_SYNC_FLUSH, make sure that - avail_out is greater than six to avoid repeated flush markers due to - avail_out == 0 on return. + avail_out is greater than six when the flush marker begins, in order to avoid + repeated flush markers upon calling deflate() again when avail_out == 0. If the parameter flush is set to Z_FINISH, pending input is processed, pending output is flushed and deflate returns with Z_STREAM_END if there was @@ -360,7 +360,7 @@ ZEXTERN int ZEXPORT deflate OF((z_streamp strm, int flush)); */ -ZEXTERN int ZEXPORT deflateEnd OF((z_streamp strm)); +ZEXTERN int ZEXPORT deflateEnd(z_streamp strm); /* All dynamically allocated data structures for this stream are freed. This function discards any unprocessed input and does not flush any pending @@ -375,7 +375,7 @@ ZEXTERN int ZEXPORT deflateEnd OF((z_streamp strm)); /* -ZEXTERN int ZEXPORT inflateInit OF((z_streamp strm)); +ZEXTERN int ZEXPORT inflateInit(z_streamp strm); Initializes the internal stream state for decompression. The fields next_in, avail_in, zalloc, zfree and opaque must be initialized before by @@ -383,7 +383,8 @@ ZEXTERN int ZEXPORT inflateInit OF((z_streamp strm)); read or consumed. The allocation of a sliding window will be deferred to the first call of inflate (if the decompression does not complete on the first call). If zalloc and zfree are set to Z_NULL, inflateInit updates - them to use default allocation functions. + them to use default allocation functions. total_in, total_out, adler, and + msg are initialized. inflateInit returns Z_OK if success, Z_MEM_ERROR if there was not enough memory, Z_VERSION_ERROR if the zlib library version is incompatible with the @@ -397,7 +398,7 @@ ZEXTERN int ZEXPORT inflateInit OF((z_streamp strm)); */ -ZEXTERN int ZEXPORT inflate OF((z_streamp strm, int flush)); +ZEXTERN int ZEXPORT inflate(z_streamp strm, int flush); /* inflate decompresses as much data as possible, and stops when the input buffer becomes empty or the output buffer becomes full. It may introduce @@ -517,7 +518,7 @@ ZEXTERN int ZEXPORT inflate OF((z_streamp strm, int flush)); */ -ZEXTERN int ZEXPORT inflateEnd OF((z_streamp strm)); +ZEXTERN int ZEXPORT inflateEnd(z_streamp strm); /* All dynamically allocated data structures for this stream are freed. This function discards any unprocessed input and does not flush any pending @@ -535,12 +536,12 @@ ZEXTERN int ZEXPORT inflateEnd OF((z_streamp strm)); */ /* -ZEXTERN int ZEXPORT deflateInit2 OF((z_streamp strm, - int level, - int method, - int windowBits, - int memLevel, - int strategy)); +ZEXTERN int ZEXPORT deflateInit2(z_streamp strm, + int level, + int method, + int windowBits, + int memLevel, + int strategy); This is another version of deflateInit with more compression options. The fields zalloc, zfree and opaque must be initialized before by the caller. @@ -607,9 +608,9 @@ ZEXTERN int ZEXPORT deflateInit2 OF((z_streamp strm, compression: this will be done by deflate(). */ -ZEXTERN int ZEXPORT deflateSetDictionary OF((z_streamp strm, - const Bytef *dictionary, - uInt dictLength)); +ZEXTERN int ZEXPORT deflateSetDictionary(z_streamp strm, + const Bytef *dictionary, + uInt dictLength); /* Initializes the compression dictionary from the given byte sequence without producing any compressed output. When using the zlib format, this @@ -651,9 +652,9 @@ ZEXTERN int ZEXPORT deflateSetDictionary OF((z_streamp strm, not perform any compression: this will be done by deflate(). */ -ZEXTERN int ZEXPORT deflateGetDictionary OF((z_streamp strm, - Bytef *dictionary, - uInt *dictLength)); +ZEXTERN int ZEXPORT deflateGetDictionary(z_streamp strm, + Bytef *dictionary, + uInt *dictLength); /* Returns the sliding dictionary being maintained by deflate. dictLength is set to the number of bytes in the dictionary, and that many bytes are copied @@ -673,8 +674,8 @@ ZEXTERN int ZEXPORT deflateGetDictionary OF((z_streamp strm, stream state is inconsistent. */ -ZEXTERN int ZEXPORT deflateCopy OF((z_streamp dest, - z_streamp source)); +ZEXTERN int ZEXPORT deflateCopy(z_streamp dest, + z_streamp source); /* Sets the destination stream as a complete copy of the source stream. @@ -691,20 +692,20 @@ ZEXTERN int ZEXPORT deflateCopy OF((z_streamp dest, destination. */ -ZEXTERN int ZEXPORT deflateReset OF((z_streamp strm)); +ZEXTERN int ZEXPORT deflateReset(z_streamp strm); /* This function is equivalent to deflateEnd followed by deflateInit, but does not free and reallocate the internal compression state. The stream will leave the compression level and any other attributes that may have been - set unchanged. + set unchanged. total_in, total_out, adler, and msg are initialized. deflateReset returns Z_OK if success, or Z_STREAM_ERROR if the source stream state was inconsistent (such as zalloc or state being Z_NULL). */ -ZEXTERN int ZEXPORT deflateParams OF((z_streamp strm, - int level, - int strategy)); +ZEXTERN int ZEXPORT deflateParams(z_streamp strm, + int level, + int strategy); /* Dynamically update the compression level and compression strategy. The interpretation of level and strategy is as in deflateInit2(). This can be @@ -729,7 +730,7 @@ ZEXTERN int ZEXPORT deflateParams OF((z_streamp strm, Then no more input data should be provided before the deflateParams() call. If this is done, the old level and strategy will be applied to the data compressed before deflateParams(), and the new level and strategy will be - applied to the the data compressed after deflateParams(). + applied to the data compressed after deflateParams(). deflateParams returns Z_OK on success, Z_STREAM_ERROR if the source stream state was inconsistent or if a parameter was invalid, or Z_BUF_ERROR if @@ -740,11 +741,11 @@ ZEXTERN int ZEXPORT deflateParams OF((z_streamp strm, retried with more output space. */ -ZEXTERN int ZEXPORT deflateTune OF((z_streamp strm, - int good_length, - int max_lazy, - int nice_length, - int max_chain)); +ZEXTERN int ZEXPORT deflateTune(z_streamp strm, + int good_length, + int max_lazy, + int nice_length, + int max_chain); /* Fine tune deflate's internal compression parameters. This should only be used by someone who understands the algorithm used by zlib's deflate for @@ -757,8 +758,8 @@ ZEXTERN int ZEXPORT deflateTune OF((z_streamp strm, returns Z_OK on success, or Z_STREAM_ERROR for an invalid deflate stream. */ -ZEXTERN uLong ZEXPORT deflateBound OF((z_streamp strm, - uLong sourceLen)); +ZEXTERN uLong ZEXPORT deflateBound(z_streamp strm, + uLong sourceLen); /* deflateBound() returns an upper bound on the compressed size after deflation of sourceLen bytes. It must be called after deflateInit() or @@ -772,9 +773,9 @@ ZEXTERN uLong ZEXPORT deflateBound OF((z_streamp strm, than Z_FINISH or Z_NO_FLUSH are used. */ -ZEXTERN int ZEXPORT deflatePending OF((z_streamp strm, - unsigned *pending, - int *bits)); +ZEXTERN int ZEXPORT deflatePending(z_streamp strm, + unsigned *pending, + int *bits); /* deflatePending() returns the number of bytes and bits of output that have been generated, but not yet provided in the available output. The bytes not @@ -787,9 +788,9 @@ ZEXTERN int ZEXPORT deflatePending OF((z_streamp strm, stream state was inconsistent. */ -ZEXTERN int ZEXPORT deflatePrime OF((z_streamp strm, - int bits, - int value)); +ZEXTERN int ZEXPORT deflatePrime(z_streamp strm, + int bits, + int value); /* deflatePrime() inserts bits in the deflate output stream. The intent is that this function is used to start off the deflate output with the bits @@ -804,8 +805,8 @@ ZEXTERN int ZEXPORT deflatePrime OF((z_streamp strm, source stream state was inconsistent. */ -ZEXTERN int ZEXPORT deflateSetHeader OF((z_streamp strm, - gz_headerp head)); +ZEXTERN int ZEXPORT deflateSetHeader(z_streamp strm, + gz_headerp head); /* deflateSetHeader() provides gzip header information for when a gzip stream is requested by deflateInit2(). deflateSetHeader() may be called @@ -821,16 +822,17 @@ ZEXTERN int ZEXPORT deflateSetHeader OF((z_streamp strm, gzip file" and give up. If deflateSetHeader is not used, the default gzip header has text false, - the time set to zero, and os set to 255, with no extra, name, or comment - fields. The gzip header is returned to the default state by deflateReset(). + the time set to zero, and os set to the current operating system, with no + extra, name, or comment fields. The gzip header is returned to the default + state by deflateReset(). deflateSetHeader returns Z_OK if success, or Z_STREAM_ERROR if the source stream state was inconsistent. */ /* -ZEXTERN int ZEXPORT inflateInit2 OF((z_streamp strm, - int windowBits)); +ZEXTERN int ZEXPORT inflateInit2(z_streamp strm, + int windowBits); This is another version of inflateInit with an extra parameter. The fields next_in, avail_in, zalloc, zfree and opaque must be initialized @@ -883,9 +885,9 @@ ZEXTERN int ZEXPORT inflateInit2 OF((z_streamp strm, deferred until inflate() is called. */ -ZEXTERN int ZEXPORT inflateSetDictionary OF((z_streamp strm, - const Bytef *dictionary, - uInt dictLength)); +ZEXTERN int ZEXPORT inflateSetDictionary(z_streamp strm, + const Bytef *dictionary, + uInt dictLength); /* Initializes the decompression dictionary from the given uncompressed byte sequence. This function must be called immediately after a call of inflate, @@ -906,9 +908,9 @@ ZEXTERN int ZEXPORT inflateSetDictionary OF((z_streamp strm, inflate(). */ -ZEXTERN int ZEXPORT inflateGetDictionary OF((z_streamp strm, - Bytef *dictionary, - uInt *dictLength)); +ZEXTERN int ZEXPORT inflateGetDictionary(z_streamp strm, + Bytef *dictionary, + uInt *dictLength); /* Returns the sliding dictionary being maintained by inflate. dictLength is set to the number of bytes in the dictionary, and that many bytes are copied @@ -921,7 +923,7 @@ ZEXTERN int ZEXPORT inflateGetDictionary OF((z_streamp strm, stream state is inconsistent. */ -ZEXTERN int ZEXPORT inflateSync OF((z_streamp strm)); +ZEXTERN int ZEXPORT inflateSync(z_streamp strm); /* Skips invalid compressed data until a possible full flush point (see above for the description of deflate with Z_FULL_FLUSH) can be found, or until all @@ -940,8 +942,8 @@ ZEXTERN int ZEXPORT inflateSync OF((z_streamp strm)); input each time, until success or end of the input data. */ -ZEXTERN int ZEXPORT inflateCopy OF((z_streamp dest, - z_streamp source)); +ZEXTERN int ZEXPORT inflateCopy(z_streamp dest, + z_streamp source); /* Sets the destination stream as a complete copy of the source stream. @@ -956,18 +958,19 @@ ZEXTERN int ZEXPORT inflateCopy OF((z_streamp dest, destination. */ -ZEXTERN int ZEXPORT inflateReset OF((z_streamp strm)); +ZEXTERN int ZEXPORT inflateReset(z_streamp strm); /* This function is equivalent to inflateEnd followed by inflateInit, but does not free and reallocate the internal decompression state. The stream will keep attributes that may have been set by inflateInit2. + total_in, total_out, adler, and msg are initialized. inflateReset returns Z_OK if success, or Z_STREAM_ERROR if the source stream state was inconsistent (such as zalloc or state being Z_NULL). */ -ZEXTERN int ZEXPORT inflateReset2 OF((z_streamp strm, - int windowBits)); +ZEXTERN int ZEXPORT inflateReset2(z_streamp strm, + int windowBits); /* This function is the same as inflateReset, but it also permits changing the wrap and window size requests. The windowBits parameter is interpreted @@ -980,9 +983,9 @@ ZEXTERN int ZEXPORT inflateReset2 OF((z_streamp strm, the windowBits parameter is invalid. */ -ZEXTERN int ZEXPORT inflatePrime OF((z_streamp strm, - int bits, - int value)); +ZEXTERN int ZEXPORT inflatePrime(z_streamp strm, + int bits, + int value); /* This function inserts bits in the inflate input stream. The intent is that this function is used to start inflating at a bit position in the @@ -1001,7 +1004,7 @@ ZEXTERN int ZEXPORT inflatePrime OF((z_streamp strm, stream state was inconsistent. */ -ZEXTERN long ZEXPORT inflateMark OF((z_streamp strm)); +ZEXTERN long ZEXPORT inflateMark(z_streamp strm); /* This function returns two values, one in the lower 16 bits of the return value, and the other in the remaining upper bits, obtained by shifting the @@ -1029,8 +1032,8 @@ ZEXTERN long ZEXPORT inflateMark OF((z_streamp strm)); source stream state was inconsistent. */ -ZEXTERN int ZEXPORT inflateGetHeader OF((z_streamp strm, - gz_headerp head)); +ZEXTERN int ZEXPORT inflateGetHeader(z_streamp strm, + gz_headerp head); /* inflateGetHeader() requests that gzip header information be stored in the provided gz_header structure. inflateGetHeader() may be called after @@ -1070,8 +1073,8 @@ ZEXTERN int ZEXPORT inflateGetHeader OF((z_streamp strm, */ /* -ZEXTERN int ZEXPORT inflateBackInit OF((z_streamp strm, int windowBits, - unsigned char FAR *window)); +ZEXTERN int ZEXPORT inflateBackInit(z_streamp strm, int windowBits, + unsigned char FAR *window); Initialize the internal stream state for decompression using inflateBack() calls. The fields zalloc, zfree and opaque in strm must be initialized @@ -1091,13 +1094,13 @@ ZEXTERN int ZEXPORT inflateBackInit OF((z_streamp strm, int windowBits, the version of the header file. */ -typedef unsigned (*in_func) OF((void FAR *, - z_const unsigned char FAR * FAR *)); -typedef int (*out_func) OF((void FAR *, unsigned char FAR *, unsigned)); +typedef unsigned (*in_func)(void FAR *, + z_const unsigned char FAR * FAR *); +typedef int (*out_func)(void FAR *, unsigned char FAR *, unsigned); -ZEXTERN int ZEXPORT inflateBack OF((z_streamp strm, - in_func in, void FAR *in_desc, - out_func out, void FAR *out_desc)); +ZEXTERN int ZEXPORT inflateBack(z_streamp strm, + in_func in, void FAR *in_desc, + out_func out, void FAR *out_desc); /* inflateBack() does a raw inflate with a single call using a call-back interface for input and output. This is potentially more efficient than @@ -1165,7 +1168,7 @@ ZEXTERN int ZEXPORT inflateBack OF((z_streamp strm, cannot return Z_OK. */ -ZEXTERN int ZEXPORT inflateBackEnd OF((z_streamp strm)); +ZEXTERN int ZEXPORT inflateBackEnd(z_streamp strm); /* All memory allocated by inflateBackInit() is freed. @@ -1173,7 +1176,7 @@ ZEXTERN int ZEXPORT inflateBackEnd OF((z_streamp strm)); state was inconsistent. */ -ZEXTERN uLong ZEXPORT zlibCompileFlags OF((void)); +ZEXTERN uLong ZEXPORT zlibCompileFlags(void); /* Return flags indicating compile-time options. Type sizes, two bits each, 00 = 16 bits, 01 = 32, 10 = 64, 11 = other: @@ -1226,8 +1229,8 @@ ZEXTERN uLong ZEXPORT zlibCompileFlags OF((void)); you need special options. */ -ZEXTERN int ZEXPORT compress OF((Bytef *dest, uLongf *destLen, - const Bytef *source, uLong sourceLen)); +ZEXTERN int ZEXPORT compress(Bytef *dest, uLongf *destLen, + const Bytef *source, uLong sourceLen); /* Compresses the source buffer into the destination buffer. sourceLen is the byte length of the source buffer. Upon entry, destLen is the total size @@ -1241,9 +1244,9 @@ ZEXTERN int ZEXPORT compress OF((Bytef *dest, uLongf *destLen, buffer. */ -ZEXTERN int ZEXPORT compress2 OF((Bytef *dest, uLongf *destLen, - const Bytef *source, uLong sourceLen, - int level)); +ZEXTERN int ZEXPORT compress2(Bytef *dest, uLongf *destLen, + const Bytef *source, uLong sourceLen, + int level); /* Compresses the source buffer into the destination buffer. The level parameter has the same meaning as in deflateInit. sourceLen is the byte @@ -1257,15 +1260,15 @@ ZEXTERN int ZEXPORT compress2 OF((Bytef *dest, uLongf *destLen, Z_STREAM_ERROR if the level parameter is invalid. */ -ZEXTERN uLong ZEXPORT compressBound OF((uLong sourceLen)); +ZEXTERN uLong ZEXPORT compressBound(uLong sourceLen); /* compressBound() returns an upper bound on the compressed size after compress() or compress2() on sourceLen bytes. It would be used before a compress() or compress2() call to allocate the destination buffer. */ -ZEXTERN int ZEXPORT uncompress OF((Bytef *dest, uLongf *destLen, - const Bytef *source, uLong sourceLen)); +ZEXTERN int ZEXPORT uncompress(Bytef *dest, uLongf *destLen, + const Bytef *source, uLong sourceLen); /* Decompresses the source buffer into the destination buffer. sourceLen is the byte length of the source buffer. Upon entry, destLen is the total size @@ -1282,8 +1285,8 @@ ZEXTERN int ZEXPORT uncompress OF((Bytef *dest, uLongf *destLen, buffer with the uncompressed data up to that point. */ -ZEXTERN int ZEXPORT uncompress2 OF((Bytef *dest, uLongf *destLen, - const Bytef *source, uLong *sourceLen)); +ZEXTERN int ZEXPORT uncompress2(Bytef *dest, uLongf *destLen, + const Bytef *source, uLong *sourceLen); /* Same as uncompress, except that sourceLen is a pointer, where the length of the source is *sourceLen. On return, *sourceLen is the number of @@ -1302,7 +1305,7 @@ ZEXTERN int ZEXPORT uncompress2 OF((Bytef *dest, uLongf *destLen, typedef struct gzFile_s *gzFile; /* semi-opaque gzip file descriptor */ /* -ZEXTERN gzFile ZEXPORT gzopen OF((const char *path, const char *mode)); +ZEXTERN gzFile ZEXPORT gzopen(const char *path, const char *mode); Open the gzip (.gz) file at path for reading and decompressing, or compressing and writing. The mode parameter is as in fopen ("rb" or "wb") @@ -1339,7 +1342,7 @@ ZEXTERN gzFile ZEXPORT gzopen OF((const char *path, const char *mode)); file could not be opened. */ -ZEXTERN gzFile ZEXPORT gzdopen OF((int fd, const char *mode)); +ZEXTERN gzFile ZEXPORT gzdopen(int fd, const char *mode); /* Associate a gzFile with the file descriptor fd. File descriptors are obtained from calls like open, dup, creat, pipe or fileno (if the file has @@ -1362,7 +1365,7 @@ ZEXTERN gzFile ZEXPORT gzdopen OF((int fd, const char *mode)); will not detect if fd is invalid (unless fd is -1). */ -ZEXTERN int ZEXPORT gzbuffer OF((gzFile file, unsigned size)); +ZEXTERN int ZEXPORT gzbuffer(gzFile file, unsigned size); /* Set the internal buffer size used by this library's functions for file to size. The default buffer size is 8192 bytes. This function must be called @@ -1378,7 +1381,7 @@ ZEXTERN int ZEXPORT gzbuffer OF((gzFile file, unsigned size)); too late. */ -ZEXTERN int ZEXPORT gzsetparams OF((gzFile file, int level, int strategy)); +ZEXTERN int ZEXPORT gzsetparams(gzFile file, int level, int strategy); /* Dynamically update the compression level and strategy for file. See the description of deflateInit2 for the meaning of these parameters. Previously @@ -1389,7 +1392,7 @@ ZEXTERN int ZEXPORT gzsetparams OF((gzFile file, int level, int strategy)); or Z_MEM_ERROR if there is a memory allocation error. */ -ZEXTERN int ZEXPORT gzread OF((gzFile file, voidp buf, unsigned len)); +ZEXTERN int ZEXPORT gzread(gzFile file, voidp buf, unsigned len); /* Read and decompress up to len uncompressed bytes from file into buf. If the input file is not in gzip format, gzread copies the given number of @@ -1419,8 +1422,8 @@ ZEXTERN int ZEXPORT gzread OF((gzFile file, voidp buf, unsigned len)); Z_STREAM_ERROR. */ -ZEXTERN z_size_t ZEXPORT gzfread OF((voidp buf, z_size_t size, z_size_t nitems, - gzFile file)); +ZEXTERN z_size_t ZEXPORT gzfread(voidp buf, z_size_t size, z_size_t nitems, + gzFile file); /* Read and decompress up to nitems items of size size from file into buf, otherwise operating as gzread() does. This duplicates the interface of @@ -1445,14 +1448,14 @@ ZEXTERN z_size_t ZEXPORT gzfread OF((voidp buf, z_size_t size, z_size_t nitems, file, resetting and retrying on end-of-file, when size is not 1. */ -ZEXTERN int ZEXPORT gzwrite OF((gzFile file, voidpc buf, unsigned len)); +ZEXTERN int ZEXPORT gzwrite(gzFile file, voidpc buf, unsigned len); /* Compress and write the len uncompressed bytes at buf to file. gzwrite returns the number of uncompressed bytes written or 0 in case of error. */ -ZEXTERN z_size_t ZEXPORT gzfwrite OF((voidpc buf, z_size_t size, - z_size_t nitems, gzFile file)); +ZEXTERN z_size_t ZEXPORT gzfwrite(voidpc buf, z_size_t size, + z_size_t nitems, gzFile file); /* Compress and write nitems items of size size from buf to file, duplicating the interface of stdio's fwrite(), with size_t request and return types. If @@ -1465,7 +1468,7 @@ ZEXTERN z_size_t ZEXPORT gzfwrite OF((voidpc buf, z_size_t size, is returned, and the error state is set to Z_STREAM_ERROR. */ -ZEXTERN int ZEXPORTVA gzprintf Z_ARG((gzFile file, const char *format, ...)); +ZEXTERN int ZEXPORTVA gzprintf(gzFile file, const char *format, ...); /* Convert, format, compress, and write the arguments (...) to file under control of the string format, as in fprintf. gzprintf returns the number of @@ -1480,7 +1483,7 @@ ZEXTERN int ZEXPORTVA gzprintf Z_ARG((gzFile file, const char *format, ...)); This can be determined using zlibCompileFlags(). */ -ZEXTERN int ZEXPORT gzputs OF((gzFile file, const char *s)); +ZEXTERN int ZEXPORT gzputs(gzFile file, const char *s); /* Compress and write the given null-terminated string s to file, excluding the terminating null character. @@ -1488,7 +1491,7 @@ ZEXTERN int ZEXPORT gzputs OF((gzFile file, const char *s)); gzputs returns the number of characters written, or -1 in case of error. */ -ZEXTERN char * ZEXPORT gzgets OF((gzFile file, char *buf, int len)); +ZEXTERN char * ZEXPORT gzgets(gzFile file, char *buf, int len); /* Read and decompress bytes from file into buf, until len-1 characters are read, or until a newline character is read and transferred to buf, or an @@ -1502,13 +1505,13 @@ ZEXTERN char * ZEXPORT gzgets OF((gzFile file, char *buf, int len)); buf are indeterminate. */ -ZEXTERN int ZEXPORT gzputc OF((gzFile file, int c)); +ZEXTERN int ZEXPORT gzputc(gzFile file, int c); /* Compress and write c, converted to an unsigned char, into file. gzputc returns the value that was written, or -1 in case of error. */ -ZEXTERN int ZEXPORT gzgetc OF((gzFile file)); +ZEXTERN int ZEXPORT gzgetc(gzFile file); /* Read and decompress one byte from file. gzgetc returns this byte or -1 in case of end of file or error. This is implemented as a macro for speed. @@ -1517,7 +1520,7 @@ ZEXTERN int ZEXPORT gzgetc OF((gzFile file)); points to has been clobbered or not. */ -ZEXTERN int ZEXPORT gzungetc OF((int c, gzFile file)); +ZEXTERN int ZEXPORT gzungetc(int c, gzFile file); /* Push c back onto the stream for file to be read as the first character on the next read. At least one character of push-back is always allowed. @@ -1529,7 +1532,7 @@ ZEXTERN int ZEXPORT gzungetc OF((int c, gzFile file)); gzseek() or gzrewind(). */ -ZEXTERN int ZEXPORT gzflush OF((gzFile file, int flush)); +ZEXTERN int ZEXPORT gzflush(gzFile file, int flush); /* Flush all pending output to file. The parameter flush is as in the deflate() function. The return value is the zlib error number (see function @@ -1545,8 +1548,8 @@ ZEXTERN int ZEXPORT gzflush OF((gzFile file, int flush)); */ /* -ZEXTERN z_off_t ZEXPORT gzseek OF((gzFile file, - z_off_t offset, int whence)); +ZEXTERN z_off_t ZEXPORT gzseek(gzFile file, + z_off_t offset, int whence); Set the starting position to offset relative to whence for the next gzread or gzwrite on file. The offset represents a number of bytes in the @@ -1564,7 +1567,7 @@ ZEXTERN z_off_t ZEXPORT gzseek OF((gzFile file, would be before the current position. */ -ZEXTERN int ZEXPORT gzrewind OF((gzFile file)); +ZEXTERN int ZEXPORT gzrewind(gzFile file); /* Rewind file. This function is supported only for reading. @@ -1572,7 +1575,7 @@ ZEXTERN int ZEXPORT gzrewind OF((gzFile file)); */ /* -ZEXTERN z_off_t ZEXPORT gztell OF((gzFile file)); +ZEXTERN z_off_t ZEXPORT gztell(gzFile file); Return the starting position for the next gzread or gzwrite on file. This position represents a number of bytes in the uncompressed data stream, @@ -1583,7 +1586,7 @@ ZEXTERN z_off_t ZEXPORT gztell OF((gzFile file)); */ /* -ZEXTERN z_off_t ZEXPORT gzoffset OF((gzFile file)); +ZEXTERN z_off_t ZEXPORT gzoffset(gzFile file); Return the current compressed (actual) read or write offset of file. This offset includes the count of bytes that precede the gzip stream, for example @@ -1592,7 +1595,7 @@ ZEXTERN z_off_t ZEXPORT gzoffset OF((gzFile file)); be used for a progress indicator. On error, gzoffset() returns -1. */ -ZEXTERN int ZEXPORT gzeof OF((gzFile file)); +ZEXTERN int ZEXPORT gzeof(gzFile file); /* Return true (1) if the end-of-file indicator for file has been set while reading, false (0) otherwise. Note that the end-of-file indicator is set @@ -1607,7 +1610,7 @@ ZEXTERN int ZEXPORT gzeof OF((gzFile file)); has grown since the previous end of file was detected. */ -ZEXTERN int ZEXPORT gzdirect OF((gzFile file)); +ZEXTERN int ZEXPORT gzdirect(gzFile file); /* Return true (1) if file is being copied directly while reading, or false (0) if file is a gzip stream being decompressed. @@ -1628,7 +1631,7 @@ ZEXTERN int ZEXPORT gzdirect OF((gzFile file)); gzip file reading and decompression, which may not be desired.) */ -ZEXTERN int ZEXPORT gzclose OF((gzFile file)); +ZEXTERN int ZEXPORT gzclose(gzFile file); /* Flush all pending output for file, if necessary, close file and deallocate the (de)compression state. Note that once file is closed, you @@ -1641,8 +1644,8 @@ ZEXTERN int ZEXPORT gzclose OF((gzFile file)); last read ended in the middle of a gzip stream, or Z_OK on success. */ -ZEXTERN int ZEXPORT gzclose_r OF((gzFile file)); -ZEXTERN int ZEXPORT gzclose_w OF((gzFile file)); +ZEXTERN int ZEXPORT gzclose_r(gzFile file); +ZEXTERN int ZEXPORT gzclose_w(gzFile file); /* Same as gzclose(), but gzclose_r() is only for use when reading, and gzclose_w() is only for use when writing or appending. The advantage to @@ -1653,7 +1656,7 @@ ZEXTERN int ZEXPORT gzclose_w OF((gzFile file)); zlib library. */ -ZEXTERN const char * ZEXPORT gzerror OF((gzFile file, int *errnum)); +ZEXTERN const char * ZEXPORT gzerror(gzFile file, int *errnum); /* Return the error message for the last error which occurred on file. errnum is set to zlib error number. If an error occurred in the file system @@ -1669,7 +1672,7 @@ ZEXTERN const char * ZEXPORT gzerror OF((gzFile file, int *errnum)); functions above that do not distinguish those cases in their return values. */ -ZEXTERN void ZEXPORT gzclearerr OF((gzFile file)); +ZEXTERN void ZEXPORT gzclearerr(gzFile file); /* Clear the error and end-of-file flags for file. This is analogous to the clearerr() function in stdio. This is useful for continuing to read a gzip @@ -1686,7 +1689,7 @@ ZEXTERN void ZEXPORT gzclearerr OF((gzFile file)); library. */ -ZEXTERN uLong ZEXPORT adler32 OF((uLong adler, const Bytef *buf, uInt len)); +ZEXTERN uLong ZEXPORT adler32(uLong adler, const Bytef *buf, uInt len); /* Update a running Adler-32 checksum with the bytes buf[0..len-1] and return the updated checksum. An Adler-32 value is in the range of a 32-bit @@ -1706,15 +1709,15 @@ ZEXTERN uLong ZEXPORT adler32 OF((uLong adler, const Bytef *buf, uInt len)); if (adler != original_adler) error(); */ -ZEXTERN uLong ZEXPORT adler32_z OF((uLong adler, const Bytef *buf, - z_size_t len)); +ZEXTERN uLong ZEXPORT adler32_z(uLong adler, const Bytef *buf, + z_size_t len); /* Same as adler32(), but with a size_t length. */ /* -ZEXTERN uLong ZEXPORT adler32_combine OF((uLong adler1, uLong adler2, - z_off_t len2)); +ZEXTERN uLong ZEXPORT adler32_combine(uLong adler1, uLong adler2, + z_off_t len2); Combine two Adler-32 checksums into one. For two sequences of bytes, seq1 and seq2 with lengths len1 and len2, Adler-32 checksums were calculated for @@ -1724,7 +1727,7 @@ ZEXTERN uLong ZEXPORT adler32_combine OF((uLong adler1, uLong adler2, negative, the result has no meaning or utility. */ -ZEXTERN uLong ZEXPORT crc32 OF((uLong crc, const Bytef *buf, uInt len)); +ZEXTERN uLong ZEXPORT crc32(uLong crc, const Bytef *buf, uInt len); /* Update a running CRC-32 with the bytes buf[0..len-1] and return the updated CRC-32. A CRC-32 value is in the range of a 32-bit unsigned integer. @@ -1742,14 +1745,14 @@ ZEXTERN uLong ZEXPORT crc32 OF((uLong crc, const Bytef *buf, uInt len)); if (crc != original_crc) error(); */ -ZEXTERN uLong ZEXPORT crc32_z OF((uLong crc, const Bytef *buf, - z_size_t len)); +ZEXTERN uLong ZEXPORT crc32_z(uLong crc, const Bytef *buf, + z_size_t len); /* Same as crc32(), but with a size_t length. */ /* -ZEXTERN uLong ZEXPORT crc32_combine OF((uLong crc1, uLong crc2, z_off_t len2)); +ZEXTERN uLong ZEXPORT crc32_combine(uLong crc1, uLong crc2, z_off_t len2); Combine two CRC-32 check values into one. For two sequences of bytes, seq1 and seq2 with lengths len1 and len2, CRC-32 check values were @@ -1759,13 +1762,13 @@ ZEXTERN uLong ZEXPORT crc32_combine OF((uLong crc1, uLong crc2, z_off_t len2)); */ /* -ZEXTERN uLong ZEXPORT crc32_combine_gen OF((z_off_t len2)); +ZEXTERN uLong ZEXPORT crc32_combine_gen(z_off_t len2); Return the operator corresponding to length len2, to be used with crc32_combine_op(). */ -ZEXTERN uLong ZEXPORT crc32_combine_op OF((uLong crc1, uLong crc2, uLong op)); +ZEXTERN uLong ZEXPORT crc32_combine_op(uLong crc1, uLong crc2, uLong op); /* Give the same result as crc32_combine(), using op in place of len2. op is is generated from len2 by crc32_combine_gen(). This will be faster than @@ -1778,20 +1781,20 @@ ZEXTERN uLong ZEXPORT crc32_combine_op OF((uLong crc1, uLong crc2, uLong op)); /* deflateInit and inflateInit are macros to allow checking the zlib version * and the compiler's view of z_stream: */ -ZEXTERN int ZEXPORT deflateInit_ OF((z_streamp strm, int level, - const char *version, int stream_size)); -ZEXTERN int ZEXPORT inflateInit_ OF((z_streamp strm, - const char *version, int stream_size)); -ZEXTERN int ZEXPORT deflateInit2_ OF((z_streamp strm, int level, int method, - int windowBits, int memLevel, - int strategy, const char *version, - int stream_size)); -ZEXTERN int ZEXPORT inflateInit2_ OF((z_streamp strm, int windowBits, - const char *version, int stream_size)); -ZEXTERN int ZEXPORT inflateBackInit_ OF((z_streamp strm, int windowBits, - unsigned char FAR *window, - const char *version, - int stream_size)); +ZEXTERN int ZEXPORT deflateInit_(z_streamp strm, int level, + const char *version, int stream_size); +ZEXTERN int ZEXPORT inflateInit_(z_streamp strm, + const char *version, int stream_size); +ZEXTERN int ZEXPORT deflateInit2_(z_streamp strm, int level, int method, + int windowBits, int memLevel, + int strategy, const char *version, + int stream_size); +ZEXTERN int ZEXPORT inflateInit2_(z_streamp strm, int windowBits, + const char *version, int stream_size); +ZEXTERN int ZEXPORT inflateBackInit_(z_streamp strm, int windowBits, + unsigned char FAR *window, + const char *version, + int stream_size); #ifdef Z_PREFIX_SET # define z_deflateInit(strm, level) \ deflateInit_((strm), (level), ZLIB_VERSION, (int)sizeof(z_stream)) @@ -1836,7 +1839,7 @@ struct gzFile_s { unsigned char *next; z_off64_t pos; }; -ZEXTERN int ZEXPORT gzgetc_ OF((gzFile file)); /* backward compatibility */ +ZEXTERN int ZEXPORT gzgetc_(gzFile file); /* backward compatibility */ #ifdef Z_PREFIX_SET # undef z_gzgetc # define z_gzgetc(g) \ @@ -1853,13 +1856,13 @@ ZEXTERN int ZEXPORT gzgetc_ OF((gzFile file)); /* backward compatibility */ * without large file support, _LFS64_LARGEFILE must also be true */ #ifdef Z_LARGE64 - ZEXTERN gzFile ZEXPORT gzopen64 OF((const char *, const char *)); - ZEXTERN z_off64_t ZEXPORT gzseek64 OF((gzFile, z_off64_t, int)); - ZEXTERN z_off64_t ZEXPORT gztell64 OF((gzFile)); - ZEXTERN z_off64_t ZEXPORT gzoffset64 OF((gzFile)); - ZEXTERN uLong ZEXPORT adler32_combine64 OF((uLong, uLong, z_off64_t)); - ZEXTERN uLong ZEXPORT crc32_combine64 OF((uLong, uLong, z_off64_t)); - ZEXTERN uLong ZEXPORT crc32_combine_gen64 OF((z_off64_t)); + ZEXTERN gzFile ZEXPORT gzopen64(const char *, const char *); + ZEXTERN z_off64_t ZEXPORT gzseek64(gzFile, z_off64_t, int); + ZEXTERN z_off64_t ZEXPORT gztell64(gzFile); + ZEXTERN z_off64_t ZEXPORT gzoffset64(gzFile); + ZEXTERN uLong ZEXPORT adler32_combine64(uLong, uLong, z_off64_t); + ZEXTERN uLong ZEXPORT crc32_combine64(uLong, uLong, z_off64_t); + ZEXTERN uLong ZEXPORT crc32_combine_gen64(z_off64_t); #endif #if !defined(ZLIB_INTERNAL) && defined(Z_WANT64) @@ -1881,50 +1884,50 @@ ZEXTERN int ZEXPORT gzgetc_ OF((gzFile file)); /* backward compatibility */ # define crc32_combine_gen crc32_combine_gen64 # endif # ifndef Z_LARGE64 - ZEXTERN gzFile ZEXPORT gzopen64 OF((const char *, const char *)); - ZEXTERN z_off_t ZEXPORT gzseek64 OF((gzFile, z_off_t, int)); - ZEXTERN z_off_t ZEXPORT gztell64 OF((gzFile)); - ZEXTERN z_off_t ZEXPORT gzoffset64 OF((gzFile)); - ZEXTERN uLong ZEXPORT adler32_combine64 OF((uLong, uLong, z_off_t)); - ZEXTERN uLong ZEXPORT crc32_combine64 OF((uLong, uLong, z_off_t)); - ZEXTERN uLong ZEXPORT crc32_combine_gen64 OF((z_off_t)); + ZEXTERN gzFile ZEXPORT gzopen64(const char *, const char *); + ZEXTERN z_off_t ZEXPORT gzseek64(gzFile, z_off_t, int); + ZEXTERN z_off_t ZEXPORT gztell64(gzFile); + ZEXTERN z_off_t ZEXPORT gzoffset64(gzFile); + ZEXTERN uLong ZEXPORT adler32_combine64(uLong, uLong, z_off_t); + ZEXTERN uLong ZEXPORT crc32_combine64(uLong, uLong, z_off_t); + ZEXTERN uLong ZEXPORT crc32_combine_gen64(z_off_t); # endif #else - ZEXTERN gzFile ZEXPORT gzopen OF((const char *, const char *)); - ZEXTERN z_off_t ZEXPORT gzseek OF((gzFile, z_off_t, int)); - ZEXTERN z_off_t ZEXPORT gztell OF((gzFile)); - ZEXTERN z_off_t ZEXPORT gzoffset OF((gzFile)); - ZEXTERN uLong ZEXPORT adler32_combine OF((uLong, uLong, z_off_t)); - ZEXTERN uLong ZEXPORT crc32_combine OF((uLong, uLong, z_off_t)); - ZEXTERN uLong ZEXPORT crc32_combine_gen OF((z_off_t)); + ZEXTERN gzFile ZEXPORT gzopen(const char *, const char *); + ZEXTERN z_off_t ZEXPORT gzseek(gzFile, z_off_t, int); + ZEXTERN z_off_t ZEXPORT gztell(gzFile); + ZEXTERN z_off_t ZEXPORT gzoffset(gzFile); + ZEXTERN uLong ZEXPORT adler32_combine(uLong, uLong, z_off_t); + ZEXTERN uLong ZEXPORT crc32_combine(uLong, uLong, z_off_t); + ZEXTERN uLong ZEXPORT crc32_combine_gen(z_off_t); #endif #else /* Z_SOLO */ - ZEXTERN uLong ZEXPORT adler32_combine OF((uLong, uLong, z_off_t)); - ZEXTERN uLong ZEXPORT crc32_combine OF((uLong, uLong, z_off_t)); - ZEXTERN uLong ZEXPORT crc32_combine_gen OF((z_off_t)); + ZEXTERN uLong ZEXPORT adler32_combine(uLong, uLong, z_off_t); + ZEXTERN uLong ZEXPORT crc32_combine(uLong, uLong, z_off_t); + ZEXTERN uLong ZEXPORT crc32_combine_gen(z_off_t); #endif /* !Z_SOLO */ /* undocumented functions */ -ZEXTERN const char * ZEXPORT zError OF((int)); -ZEXTERN int ZEXPORT inflateSyncPoint OF((z_streamp)); -ZEXTERN const z_crc_t FAR * ZEXPORT get_crc_table OF((void)); -ZEXTERN int ZEXPORT inflateUndermine OF((z_streamp, int)); -ZEXTERN int ZEXPORT inflateValidate OF((z_streamp, int)); -ZEXTERN unsigned long ZEXPORT inflateCodesUsed OF((z_streamp)); -ZEXTERN int ZEXPORT inflateResetKeep OF((z_streamp)); -ZEXTERN int ZEXPORT deflateResetKeep OF((z_streamp)); +ZEXTERN const char * ZEXPORT zError(int); +ZEXTERN int ZEXPORT inflateSyncPoint(z_streamp); +ZEXTERN const z_crc_t FAR * ZEXPORT get_crc_table(void); +ZEXTERN int ZEXPORT inflateUndermine(z_streamp, int); +ZEXTERN int ZEXPORT inflateValidate(z_streamp, int); +ZEXTERN unsigned long ZEXPORT inflateCodesUsed(z_streamp); +ZEXTERN int ZEXPORT inflateResetKeep(z_streamp); +ZEXTERN int ZEXPORT deflateResetKeep(z_streamp); #if defined(_WIN32) && !defined(Z_SOLO) -ZEXTERN gzFile ZEXPORT gzopen_w OF((const wchar_t *path, - const char *mode)); +ZEXTERN gzFile ZEXPORT gzopen_w(const wchar_t *path, + const char *mode); #endif #if defined(STDC) || defined(Z_HAVE_STDARG_H) # ifndef Z_SOLO -ZEXTERN int ZEXPORTVA gzvprintf Z_ARG((gzFile file, - const char *format, - va_list va)); +ZEXTERN int ZEXPORTVA gzvprintf(gzFile file, + const char *format, + va_list va); # endif #endif diff --git a/3rdparty/zlib/zutil.c b/3rdparty/zlib/zutil.c index 9543ae825e..b1c5d2d3c6 100644 --- a/3rdparty/zlib/zutil.c +++ b/3rdparty/zlib/zutil.c @@ -24,13 +24,11 @@ z_const char * const z_errmsg[10] = { }; -const char * ZEXPORT zlibVersion() -{ +const char * ZEXPORT zlibVersion(void) { return ZLIB_VERSION; } -uLong ZEXPORT zlibCompileFlags() -{ +uLong ZEXPORT zlibCompileFlags(void) { uLong flags; flags = 0; @@ -121,9 +119,7 @@ uLong ZEXPORT zlibCompileFlags() # endif int ZLIB_INTERNAL z_verbose = verbose; -void ZLIB_INTERNAL z_error(m) - char *m; -{ +void ZLIB_INTERNAL z_error(char *m) { fprintf(stderr, "%s\n", m); exit(1); } @@ -132,9 +128,7 @@ void ZLIB_INTERNAL z_error(m) /* exported to allow conversion of error code to string for compress() and * uncompress() */ -const char * ZEXPORT zError(err) - int err; -{ +const char * ZEXPORT zError(int err) { return ERR_MSG(err); } @@ -148,22 +142,14 @@ const char * ZEXPORT zError(err) #ifndef HAVE_MEMCPY -void ZLIB_INTERNAL zmemcpy(dest, source, len) - Bytef* dest; - const Bytef* source; - uInt len; -{ +void ZLIB_INTERNAL zmemcpy(Bytef* dest, const Bytef* source, uInt len) { if (len == 0) return; do { *dest++ = *source++; /* ??? to be unrolled */ } while (--len != 0); } -int ZLIB_INTERNAL zmemcmp(s1, s2, len) - const Bytef* s1; - const Bytef* s2; - uInt len; -{ +int ZLIB_INTERNAL zmemcmp(const Bytef* s1, const Bytef* s2, uInt len) { uInt j; for (j = 0; j < len; j++) { @@ -172,10 +158,7 @@ int ZLIB_INTERNAL zmemcmp(s1, s2, len) return 0; } -void ZLIB_INTERNAL zmemzero(dest, len) - Bytef* dest; - uInt len; -{ +void ZLIB_INTERNAL zmemzero(Bytef* dest, uInt len) { if (len == 0) return; do { *dest++ = 0; /* ??? to be unrolled */ @@ -216,8 +199,7 @@ local ptr_table table[MAX_PTR]; * a protected system like OS/2. Use Microsoft C instead. */ -voidpf ZLIB_INTERNAL zcalloc(voidpf opaque, unsigned items, unsigned size) -{ +voidpf ZLIB_INTERNAL zcalloc(voidpf opaque, unsigned items, unsigned size) { voidpf buf; ulg bsize = (ulg)items*size; @@ -242,8 +224,7 @@ voidpf ZLIB_INTERNAL zcalloc(voidpf opaque, unsigned items, unsigned size) return buf; } -void ZLIB_INTERNAL zcfree(voidpf opaque, voidpf ptr) -{ +void ZLIB_INTERNAL zcfree(voidpf opaque, voidpf ptr) { int n; (void)opaque; @@ -279,14 +260,12 @@ void ZLIB_INTERNAL zcfree(voidpf opaque, voidpf ptr) # define _hfree hfree #endif -voidpf ZLIB_INTERNAL zcalloc(voidpf opaque, uInt items, uInt size) -{ +voidpf ZLIB_INTERNAL zcalloc(voidpf opaque, uInt items, uInt size) { (void)opaque; return _halloc((long)items, size); } -void ZLIB_INTERNAL zcfree(voidpf opaque, voidpf ptr) -{ +void ZLIB_INTERNAL zcfree(voidpf opaque, voidpf ptr) { (void)opaque; _hfree(ptr); } @@ -299,25 +278,18 @@ void ZLIB_INTERNAL zcfree(voidpf opaque, voidpf ptr) #ifndef MY_ZCALLOC /* Any system without a special alloc function */ #ifndef STDC -extern voidp malloc OF((uInt size)); -extern voidp calloc OF((uInt items, uInt size)); -extern void free OF((voidpf ptr)); +extern voidp malloc(uInt size); +extern voidp calloc(uInt items, uInt size); +extern void free(voidpf ptr); #endif -voidpf ZLIB_INTERNAL zcalloc(opaque, items, size) - voidpf opaque; - unsigned items; - unsigned size; -{ +voidpf ZLIB_INTERNAL zcalloc(voidpf opaque, unsigned items, unsigned size) { (void)opaque; return sizeof(uInt) > 2 ? (voidpf)malloc(items * size) : (voidpf)calloc(items, size); } -void ZLIB_INTERNAL zcfree(opaque, ptr) - voidpf opaque; - voidpf ptr; -{ +void ZLIB_INTERNAL zcfree(voidpf opaque, voidpf ptr) { (void)opaque; free(ptr); } diff --git a/3rdparty/zlib/zutil.h b/3rdparty/zlib/zutil.h index 0bc7f4ecd1..902a304cc2 100644 --- a/3rdparty/zlib/zutil.h +++ b/3rdparty/zlib/zutil.h @@ -191,9 +191,9 @@ extern z_const char * const z_errmsg[10]; /* indexed by 2-zlib_error */ /* provide prototypes for these when building zlib without LFS */ #if !defined(_WIN32) && \ (!defined(_LARGEFILE64_SOURCE) || _LFS64_LARGEFILE-0 == 0) - ZEXTERN uLong ZEXPORT adler32_combine64 OF((uLong, uLong, z_off_t)); - ZEXTERN uLong ZEXPORT crc32_combine64 OF((uLong, uLong, z_off_t)); - ZEXTERN uLong ZEXPORT crc32_combine_gen64 OF((z_off_t)); + ZEXTERN uLong ZEXPORT adler32_combine64(uLong, uLong, z_off_t); + ZEXTERN uLong ZEXPORT crc32_combine64(uLong, uLong, z_off_t); + ZEXTERN uLong ZEXPORT crc32_combine_gen64(z_off_t); #endif /* common defaults */ @@ -232,16 +232,16 @@ extern z_const char * const z_errmsg[10]; /* indexed by 2-zlib_error */ # define zmemzero(dest, len) memset(dest, 0, len) # endif #else - void ZLIB_INTERNAL zmemcpy OF((Bytef* dest, const Bytef* source, uInt len)); - int ZLIB_INTERNAL zmemcmp OF((const Bytef* s1, const Bytef* s2, uInt len)); - void ZLIB_INTERNAL zmemzero OF((Bytef* dest, uInt len)); + void ZLIB_INTERNAL zmemcpy(Bytef* dest, const Bytef* source, uInt len); + int ZLIB_INTERNAL zmemcmp(const Bytef* s1, const Bytef* s2, uInt len); + void ZLIB_INTERNAL zmemzero(Bytef* dest, uInt len); #endif /* Diagnostic functions */ #ifdef ZLIB_DEBUG # include extern int ZLIB_INTERNAL z_verbose; - extern void ZLIB_INTERNAL z_error OF((char *m)); + extern void ZLIB_INTERNAL z_error(char *m); # define Assert(cond,msg) {if(!(cond)) z_error(msg);} # define Trace(x) {if (z_verbose>=0) fprintf x ;} # define Tracev(x) {if (z_verbose>0) fprintf x ;} @@ -258,9 +258,9 @@ extern z_const char * const z_errmsg[10]; /* indexed by 2-zlib_error */ #endif #ifndef Z_SOLO - voidpf ZLIB_INTERNAL zcalloc OF((voidpf opaque, unsigned items, - unsigned size)); - void ZLIB_INTERNAL zcfree OF((voidpf opaque, voidpf ptr)); + voidpf ZLIB_INTERNAL zcalloc(voidpf opaque, unsigned items, + unsigned size); + void ZLIB_INTERNAL zcfree(voidpf opaque, voidpf ptr); #endif #define ZALLOC(strm, items, size) \ diff --git a/CMakeLists.txt b/CMakeLists.txt index e10a04cd63..5f5e1a8128 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -5,7 +5,6 @@ # $ cmake # # ---------------------------------------------------------------------------- - # Disable in-source builds to prevent source tree corruption. if(" ${CMAKE_SOURCE_DIR}" STREQUAL " ${CMAKE_BINARY_DIR}") message(FATAL_ERROR " @@ -74,6 +73,10 @@ if(POLICY CMP0077) cmake_policy(SET CMP0077 NEW) # CMake 3.13+: option() honors normal variables. endif() +if(POLICY CMP0091) + cmake_policy(SET CMP0091 NEW) # CMake 3.15+: leave MSVC runtime selection out of default CMAKE__FLAGS_ flags +endif() + if(POLICY CMP0146) cmake_policy(SET CMP0146 OLD) # CMake 3.27+: use CMake FindCUDA if available. endif() @@ -240,9 +243,9 @@ OCV_OPTION(BUILD_CLAPACK "Build CLapack from source" (((WIN3 # Optional 3rd party components # =================================================== OCV_OPTION(WITH_1394 "Include IEEE1394 support" ON - VISIBLE_IF NOT ANDROID AND NOT IOS AND NOT WINRT + VISIBLE_IF NOT ANDROID AND NOT IOS AND NOT XROS AND NOT WINRT VERIFY HAVE_DC1394_2) -OCV_OPTION(WITH_AVFOUNDATION "Use AVFoundation for Video I/O (iOS/Mac)" ON +OCV_OPTION(WITH_AVFOUNDATION "Use AVFoundation for Video I/O (iOS/visionOS/Mac)" ON VISIBLE_IF APPLE VERIFY HAVE_AVFOUNDATION) OCV_OPTION(WITH_AVIF "Enable AVIF support" OFF @@ -251,15 +254,15 @@ OCV_OPTION(WITH_CAP_IOS "Enable iOS video capture" ON VISIBLE_IF IOS VERIFY HAVE_CAP_IOS) OCV_OPTION(WITH_CAROTENE "Use NVidia carotene acceleration library for ARM platform" (NOT CV_DISABLE_OPTIMIZATION) - VISIBLE_IF (ARM OR AARCH64) AND NOT IOS) + VISIBLE_IF (ARM OR AARCH64) AND NOT IOS AND NOT XROS) OCV_OPTION(WITH_CPUFEATURES "Use cpufeatures Android library" ON VISIBLE_IF ANDROID VERIFY HAVE_CPUFEATURES) OCV_OPTION(WITH_VTK "Include VTK library support (and build opencv_viz module eiher)" ON - VISIBLE_IF NOT ANDROID AND NOT IOS AND NOT WINRT AND NOT CMAKE_CROSSCOMPILING + VISIBLE_IF NOT ANDROID AND NOT IOS AND NOT XROS AND NOT WINRT AND NOT CMAKE_CROSSCOMPILING VERIFY HAVE_VTK) OCV_OPTION(WITH_CUDA "Include NVidia Cuda Runtime support" OFF - VISIBLE_IF NOT IOS AND NOT WINRT + VISIBLE_IF NOT IOS AND NOT XROS AND NOT WINRT VERIFY HAVE_CUDA) OCV_OPTION(WITH_CUFFT "Include NVidia Cuda Fast Fourier Transform (FFT) library support" WITH_CUDA VISIBLE_IF WITH_CUDA @@ -280,10 +283,10 @@ OCV_OPTION(WITH_EIGEN "Include Eigen2/Eigen3 support" (NOT CV_DISABLE_OPTIMIZATI VISIBLE_IF NOT WINRT VERIFY HAVE_EIGEN) OCV_OPTION(WITH_FFMPEG "Include FFMPEG support" (NOT ANDROID) - VISIBLE_IF NOT IOS AND NOT WINRT + VISIBLE_IF NOT IOS AND NOT XROS AND NOT WINRT VERIFY HAVE_FFMPEG) OCV_OPTION(WITH_GSTREAMER "Include Gstreamer support" ON - VISIBLE_IF NOT ANDROID AND NOT IOS AND NOT WINRT + VISIBLE_IF NOT ANDROID AND NOT IOS AND NOT XROS AND NOT WINRT VERIFY HAVE_GSTREAMER AND GSTREAMER_VERSION VERSION_GREATER "0.99") OCV_OPTION(WITH_GTK "Include GTK support" ON VISIBLE_IF UNIX AND NOT APPLE AND NOT ANDROID @@ -295,7 +298,7 @@ OCV_OPTION(WITH_WAYLAND "Include Wayland support" OFF VISIBLE_IF UNIX AND NOT APPLE AND NOT ANDROID VERIFY HAVE_WAYLAND) OCV_OPTION(WITH_IPP "Include Intel IPP support" (NOT MINGW AND NOT CV_DISABLE_OPTIMIZATION) - VISIBLE_IF (X86_64 OR X86) AND NOT WINRT AND NOT IOS + VISIBLE_IF (X86_64 OR X86) AND NOT WINRT AND NOT IOS AND NOT XROS VERIFY HAVE_IPP) OCV_OPTION(WITH_VULKAN "Include Vulkan support" OFF VISIBLE_IF TRUE @@ -308,10 +311,10 @@ OCV_OPTION(WITH_WEBNN "Include WebNN support" OFF VISIBLE_IF TRUE VERIFY HAVE_WEBNN) OCV_OPTION(WITH_JASPER "Include JPEG2K support (Jasper)" ON - VISIBLE_IF NOT IOS + VISIBLE_IF NOT IOS AND NOT XROS VERIFY HAVE_JASPER) OCV_OPTION(WITH_OPENJPEG "Include JPEG2K support (OpenJPEG)" ON - VISIBLE_IF NOT IOS + VISIBLE_IF NOT IOS AND NOT XROS VERIFY HAVE_OPENJPEG) OCV_OPTION(WITH_JPEG "Include JPEG support" ON VISIBLE_IF TRUE @@ -329,10 +332,10 @@ OCV_OPTION(WITH_OPENVX "Include OpenVX support" OFF VISIBLE_IF TRUE VERIFY HAVE_OPENVX) OCV_OPTION(WITH_OPENNI "Include OpenNI support" OFF - VISIBLE_IF NOT ANDROID AND NOT IOS AND NOT WINRT + VISIBLE_IF NOT ANDROID AND NOT IOS AND NOT XROS AND NOT WINRT VERIFY HAVE_OPENNI) OCV_OPTION(WITH_OPENNI2 "Include OpenNI2 support" OFF - VISIBLE_IF NOT ANDROID AND NOT IOS AND NOT WINRT + VISIBLE_IF NOT ANDROID AND NOT IOS AND NOT XROS AND NOT WINRT VERIFY HAVE_OPENNI2) OCV_OPTION(WITH_PNG "Include PNG support" ON VISIBLE_IF TRUE @@ -344,19 +347,19 @@ OCV_OPTION(WITH_GDCM "Include DICOM support" OFF VISIBLE_IF TRUE VERIFY HAVE_GDCM) OCV_OPTION(WITH_PVAPI "Include Prosilica GigE support" OFF - VISIBLE_IF NOT ANDROID AND NOT IOS AND NOT WINRT + VISIBLE_IF NOT ANDROID AND NOT IOS AND NOT XROS AND NOT WINRT VERIFY HAVE_PVAPI) OCV_OPTION(WITH_ARAVIS "Include Aravis GigE support" OFF - VISIBLE_IF NOT ANDROID AND NOT IOS AND NOT WINRT AND NOT WIN32 + VISIBLE_IF NOT ANDROID AND NOT IOS AND NOT XROS AND NOT WINRT AND NOT WIN32 VERIFY HAVE_ARAVIS_API) OCV_OPTION(WITH_QT "Build with Qt Backend support" OFF - VISIBLE_IF NOT ANDROID AND NOT IOS AND NOT WINRT + VISIBLE_IF NOT ANDROID AND NOT IOS AND NOT XROS AND NOT WINRT VERIFY HAVE_QT) OCV_OPTION(WITH_WIN32UI "Build with Win32 UI Backend support" ON VISIBLE_IF WIN32 AND NOT WINRT VERIFY HAVE_WIN32UI) OCV_OPTION(WITH_TBB "Include Intel TBB support" OFF - VISIBLE_IF NOT IOS AND NOT WINRT + VISIBLE_IF NOT IOS AND NOT XROS AND NOT WINRT VERIFY HAVE_TBB) OCV_OPTION(WITH_HPX "Include Ste||ar Group HPX support" OFF VISIBLE_IF TRUE @@ -368,7 +371,7 @@ OCV_OPTION(WITH_PTHREADS_PF "Use pthreads-based parallel_for" ON VISIBLE_IF NOT WIN32 OR MINGW VERIFY HAVE_PTHREADS_PF) OCV_OPTION(WITH_TIFF "Include TIFF support" ON - VISIBLE_IF NOT IOS + VISIBLE_IF NOT IOS AND NOT XROS VERIFY HAVE_TIFF) OCV_OPTION(WITH_V4L "Include Video 4 Linux support" ON VISIBLE_IF UNIX AND NOT ANDROID AND NOT APPLE @@ -395,20 +398,23 @@ OCV_OPTION(WITH_CLP "Include Clp support (EPL)" OFF VISIBLE_IF TRUE VERIFY HAVE_CLP) OCV_OPTION(WITH_OPENCL "Include OpenCL Runtime support" (NOT ANDROID AND NOT CV_DISABLE_OPTIMIZATION) - VISIBLE_IF NOT IOS AND NOT WINRT + VISIBLE_IF NOT IOS AND NOT XROS AND NOT WINRT VERIFY HAVE_OPENCL) OCV_OPTION(WITH_OPENCL_SVM "Include OpenCL Shared Virtual Memory support" OFF VISIBLE_IF TRUE VERIFY HAVE_OPENCL_SVM) # experimental OCV_OPTION(WITH_OPENCLAMDFFT "Include AMD OpenCL FFT library support" ON - VISIBLE_IF NOT ANDROID AND NOT IOS AND NOT WINRT + VISIBLE_IF NOT ANDROID AND NOT IOS AND NOT XROS AND NOT WINRT VERIFY HAVE_CLAMDFFT) OCV_OPTION(WITH_OPENCLAMDBLAS "Include AMD OpenCL BLAS library support" ON - VISIBLE_IF NOT ANDROID AND NOT IOS AND NOT WINRT + VISIBLE_IF NOT ANDROID AND NOT IOS AND NOT XROS AND NOT WINRT VERIFY HAVE_CLAMDBLAS) OCV_OPTION(WITH_DIRECTX "Include DirectX support" ON VISIBLE_IF WIN32 AND NOT WINRT VERIFY HAVE_DIRECTX) +OCV_OPTION(WITH_DIRECTML "Include DirectML support" ON + VISIBLE_IF WIN32 AND NOT WINRT + VERIFY HAVE_DIRECTML) OCV_OPTION(WITH_OPENCL_D3D11_NV "Include NVIDIA OpenCL D3D11 support" WITH_DIRECTX VISIBLE_IF WIN32 AND NOT WINRT VERIFY HAVE_OPENCL_D3D11_NV) @@ -425,13 +431,13 @@ OCV_OPTION(WITH_MFX "Include Intel Media SDK support" OFF VISIBLE_IF (UNIX AND NOT ANDROID) OR (WIN32 AND NOT WINRT AND NOT MINGW) VERIFY HAVE_MFX) OCV_OPTION(WITH_GDAL "Include GDAL Support" OFF - VISIBLE_IF NOT ANDROID AND NOT IOS AND NOT WINRT + VISIBLE_IF NOT ANDROID AND NOT IOS AND NOT XROS AND NOT WINRT VERIFY HAVE_GDAL) OCV_OPTION(WITH_GPHOTO2 "Include gPhoto2 library support" OFF - VISIBLE_IF UNIX AND NOT ANDROID AND NOT IOS + VISIBLE_IF UNIX AND NOT ANDROID AND NOT IOS AND NOT XROS VERIFY HAVE_GPHOTO2) OCV_OPTION(WITH_LAPACK "Include Lapack library support" (NOT CV_DISABLE_OPTIMIZATION) - VISIBLE_IF NOT ANDROID AND NOT IOS + VISIBLE_IF NOT ANDROID AND NOT IOS AND NOT XROS VERIFY HAVE_LAPACK) OCV_OPTION(WITH_ITT "Include Intel ITT support" ON VISIBLE_IF NOT APPLE_FRAMEWORK @@ -451,7 +457,7 @@ OCV_OPTION(WITH_IMGCODEC_PXM "Include PNM (PBM,PGM,PPM) and PAM formats support" OCV_OPTION(WITH_IMGCODEC_PFM "Include PFM formats support" ON VISIBLE_IF TRUE VERIFY HAVE_IMGCODEC_PFM) -OCV_OPTION(WITH_QUIRC "Include library QR-code decoding" ON +OCV_OPTION(WITH_QUIRC "Include library QR-code decoding" OFF VISIBLE_IF TRUE VERIFY HAVE_QUIRC) OCV_OPTION(WITH_ANDROID_MEDIANDK "Use Android Media NDK for Video I/O (Android)" (ANDROID_NATIVE_API_LEVEL GREATER 20) @@ -466,6 +472,7 @@ OCV_OPTION(WITH_ONNX "Include Microsoft ONNX Runtime support" OFF OCV_OPTION(WITH_TIMVX "Include Tim-VX support" OFF VISIBLE_IF TRUE VERIFY HAVE_TIMVX) +# attention: Astra2, Gemini2, and Gemini2L cameras currently only support Windows and Linux kernel versions no higher than 4.15, and higher versions of Linux kernel may have exceptions. OCV_OPTION(WITH_OBSENSOR "Include obsensor support (Orbbec RGB-D modules: Astra+/Femto)" ON VISIBLE_IF (WIN32 AND NOT ARM AND NOT WINRT AND NOT MINGW) OR ( UNIX AND NOT APPLE AND NOT ANDROID) VERIFY HAVE_OBSENSOR) @@ -511,7 +518,7 @@ OCV_OPTION(INSTALL_TESTS "Install accuracy and performance test binar # OpenCV build options # =================================================== OCV_OPTION(ENABLE_CCACHE "Use ccache" (UNIX AND (CMAKE_GENERATOR MATCHES "Makefile" OR CMAKE_GENERATOR MATCHES "Ninja" OR CMAKE_GENERATOR MATCHES "Xcode")) ) -OCV_OPTION(ENABLE_PRECOMPILED_HEADERS "Use precompiled headers" MSVC IF (MSVC OR (NOT IOS AND NOT CMAKE_CROSSCOMPILING) ) ) +OCV_OPTION(ENABLE_PRECOMPILED_HEADERS "Use precompiled headers" MSVC IF (MSVC OR (NOT IOS AND NOT XROS AND NOT CMAKE_CROSSCOMPILING) ) ) OCV_OPTION(ENABLE_DELAYLOAD "Enable delayed loading of OpenCV DLLs" OFF VISIBLE_IF MSVC AND BUILD_SHARED_LIBS) OCV_OPTION(ENABLE_SOLUTION_FOLDERS "Solution folder in Visual Studio or in other IDEs" (MSVC_IDE OR CMAKE_GENERATOR MATCHES Xcode) ) OCV_OPTION(ENABLE_PROFILING "Enable profiling in the GCC compiler (Add flags: -g -pg)" OFF IF CV_GCC ) @@ -521,8 +528,8 @@ OCV_OPTION(ENABLE_OMIT_FRAME_POINTER "Enable -fomit-frame-pointer for GCC" OCV_OPTION(ENABLE_POWERPC "Enable PowerPC for GCC" ON IF (CV_GCC AND CMAKE_SYSTEM_PROCESSOR MATCHES powerpc.*) ) OCV_OPTION(ENABLE_FAST_MATH "Enable compiler options for fast math optimizations on FP computations (not recommended)" OFF) if(NOT IOS AND (NOT ANDROID OR OPENCV_ANDROID_USE_LEGACY_FLAGS) AND CMAKE_CROSSCOMPILING) # Use CPU_BASELINE instead -OCV_OPTION(ENABLE_NEON "Enable NEON instructions" (NEON OR ANDROID_ARM_NEON OR AARCH64) IF (CV_GCC OR CV_CLANG) AND (ARM OR AARCH64 OR IOS) ) -OCV_OPTION(ENABLE_VFPV3 "Enable VFPv3-D32 instructions" OFF IF (CV_GCC OR CV_CLANG) AND (ARM OR AARCH64 OR IOS) ) +OCV_OPTION(ENABLE_NEON "Enable NEON instructions" (NEON OR ANDROID_ARM_NEON OR AARCH64) IF (CV_GCC OR CV_CLANG) AND (ARM OR AARCH64 OR IOS OR XROS) ) +OCV_OPTION(ENABLE_VFPV3 "Enable VFPv3-D32 instructions" OFF IF (CV_GCC OR CV_CLANG) AND (ARM OR AARCH64 OR IOS OR XROS) ) endif() OCV_OPTION(ENABLE_NOISY_WARNINGS "Show all warnings even if they are too noisy" OFF ) OCV_OPTION(OPENCV_WARNINGS_ARE_ERRORS "Treat warnings as errors" OFF ) @@ -544,6 +551,9 @@ OCV_OPTION(OPENCV_ENABLE_MEMALIGN "Enable posix_memalign or memalign usage" OCV_OPTION(OPENCV_DISABLE_FILESYSTEM_SUPPORT "Disable filesystem support" OFF) OCV_OPTION(OPENCV_DISABLE_THREAD_SUPPORT "Build the library without multi-threaded code." OFF) OCV_OPTION(OPENCV_SEMIHOSTING "Build the library for semihosting target (Arm). See https://developer.arm.com/documentation/100863/latest." OFF) +OCV_OPTION(ENABLE_CUDA_FIRST_CLASS_LANGUAGE "Enable CUDA as a first class language, if enabled dependant projects will need to use CMake >= 3.18" OFF + VISIBLE_IF (WITH_CUDA AND NOT CMAKE_VERSION VERSION_LESS 3.18) + VERIFY HAVE_CUDA) OCV_OPTION(ENABLE_PYLINT "Add target with Pylint checks" (BUILD_DOCS OR BUILD_EXAMPLES) IF (NOT CMAKE_CROSSCOMPILING AND NOT APPLE_FRAMEWORK) ) OCV_OPTION(ENABLE_FLAKE8 "Add target with Python flake8 checker" (BUILD_DOCS OR BUILD_EXAMPLES) IF (NOT CMAKE_CROSSCOMPILING AND NOT APPLE_FRAMEWORK) ) @@ -647,7 +657,7 @@ endif() ocv_cmake_hook(POST_CMAKE_BUILD_OPTIONS) # --- Python Support --- -if(NOT IOS) +if(NOT IOS AND NOT XROS) include(cmake/OpenCVDetectPython.cmake) endif() @@ -655,6 +665,51 @@ include(cmake/OpenCVCompilerOptions.cmake) ocv_cmake_hook(POST_COMPILER_OPTIONS) +# --- CUDA Support --- +if(ENABLE_CUDA_FIRST_CLASS_LANGUAGE) + if(CMAKE_VERSION VERSION_LESS 3.18) + message(WARNING "CUDA: First class language only supported for CMake versions >= 3.18, falling back to FindCUDA!") + set(ENABLE_CUDA_FIRST_CLASS_LANGUAGE OFF CACHE BOOL "Enable CUDA as a first class language, if enabled dependant projects will need to use CMake >= 3.18" FORCE) + else() + + # Check CUDA_PATH if supplied + if(UNIX AND CUDA_PATH AND NOT ENV{CUDA_PATH}) + set(ENV{CUDA_PATH} ${CUDA_PATH}) + elseif(WIN32 AND CUDA_PATH) + set(ENV{PATH} "${CUDA_PATH}\\bin\;$ENV{PATH}") + endif() + include(CheckLanguage) + check_language(CUDA) + + # Fallback to checking default locations + if(NOT CMAKE_CUDA_COMPILER) + # Checking windows default search location isn't possible because the CUDA Toolkit is installed to C:/Program Files/NVIDIA GPU Computing Toolkit/CUDA/vXX.X + if(WIN32) + if(CMAKE_GENERATOR MATCHES "Visual Studio") + message(STATUS "CUDA: Not detected, when using stand alone installations with the Visual Studio generator the path to the CUDA toolkit should be manually specified with -Tcuda=. e.g. -Tcuda=\"C:/Program Files/NVIDIA GPU Computing Toolkit/CUDA/vXX.X\"") + else() + message(STATUS "CUDA: Not detected, for stand alone installations the path to the CUDA toolkit should be manually specified with -DCUDA_PATH=. e.g. -DCUDA_PATH=\"C:/Program Files/NVIDIA GPU Computing Toolkit/CUDA/vXX.X\"") + endif() + elseif(UNIX) + message(STATUS "CUDA: Not detected, make sure you have performed the mandatory Post-installation actions described in the CUDA installation guide.\n For stand alone installations you can set the CUDA_PATH environmental or CMake variable. e.g. export CUDA_PATH=/usr/local/cuda-XX.X or -DCUDA_PATH=/usr/local/cuda-XX.X.") + message(STATUS "CUDA: Falling back to searching for the CUDA compiler in its default location (/usr/local/cuda)") + set(CUDA_PATH "/usr/local/cuda" CACHE INTERNAL "") + set(ENV{CUDA_PATH} ${CUDA_PATH}) + unset(CMAKE_CUDA_COMPILER CACHE) + unset(CMAKE_CUDA_COMPILER) + check_language(CUDA) + endif() + endif() + + cmake_policy(SET CMP0092 NEW) # CMake 3.15+: leave warning flags out of default CMAKE__FLAGS flags. + if(CMAKE_CUDA_COMPILER) + enable_language(CUDA) + elseif(UNIX) + message(WARNING "CUDA: Not detected! If you are not using the default host compiler (g++) then you need to specify both CMAKE_CUDA_HOST_COMPILER and CMAKE_CUDA_COMPILER. e.g. -DCMAKE_CUDA_HOST_COMPILER=/usr/bin/clang++ -DCMAKE_CUDA_COMPILER=/usr/local/cuda/bin/nvcc.") + endif() + endif() +endif() + # ---------------------------------------------------------------------------- # CHECK FOR SYSTEM LIBRARIES, OPTIONS, ETC.. # ---------------------------------------------------------------------------- @@ -732,7 +787,7 @@ include(cmake/OpenCVModule.cmake) # Detect endianness of build platform # ---------------------------------------------------------------------------- -if(IOS) +if(IOS OR XROS) # test_big_endian needs try_compile, which doesn't work for iOS # http://public.kitware.com/Bug/view.php?id=12288 set(WORDS_BIGENDIAN 0) @@ -847,6 +902,10 @@ endif() if(WITH_DIRECTX) include(cmake/OpenCVDetectDirectX.cmake) endif() +# --- DirectML --- +if(WITH_DIRECTML) + include(cmake/OpenCVDetectDirectML.cmake) +endif() if(WITH_VTK) include(cmake/OpenCVDetectVTK.cmake) @@ -907,7 +966,15 @@ foreach(hal ${OpenCV_HAL}) if(";${CPU_BASELINE_FINAL};" MATCHES ";NEON;") add_subdirectory(3rdparty/carotene/hal) ocv_hal_register(CAROTENE_HAL_LIBRARIES CAROTENE_HAL_HEADERS CAROTENE_HAL_INCLUDE_DIRS) - list(APPEND OpenCV_USED_HAL "carotene (ver ${CAROTENE_HAL_VERSION})") + + if( NOT DEFINED CAROTENE_NEON_ARCH) + set(CAROTENE_NEON_MSG "Auto detected") + elseif( CAROTENE_NEON_ARCH GREATER 7) + set(CAROTENE_NEON_MSG "Force ARMv8+") + else() + set(CAROTENE_NEON_MSG "Force ARMv7") + endif() + list(APPEND OpenCV_USED_HAL "carotene (ver ${CAROTENE_HAL_VERSION}, ${CAROTENE_NEON_MSG})") else() message(STATUS "Carotene: NEON is not available, disabling carotene...") endif() @@ -979,10 +1046,6 @@ if(BUILD_EXAMPLES OR BUILD_ANDROID_EXAMPLES OR INSTALL_ANDROID_EXAMPLES OR INSTA add_subdirectory(samples) endif() -if(ANDROID) - add_subdirectory(platforms/android/service) -endif() - # ---------------------------------------------------------------------------- # Finalization: generate configuration-based files # ---------------------------------------------------------------------------- @@ -1003,7 +1066,7 @@ include(cmake/OpenCVGenAndroidMK.cmake) # Generate OpenCVConfig.cmake and OpenCVConfig-version.cmake for cmake projects include(cmake/OpenCVGenConfig.cmake) -# Generate Info.plist for the IOS framework +# Generate Info.plist for the iOS/visionOS framework if(APPLE_FRAMEWORK) include(cmake/OpenCVGenInfoPlist.cmake) endif() @@ -1399,12 +1462,14 @@ if(WITH_AVIF OR HAVE_AVIF) endif() endif() -if(WITH_PNG OR HAVE_PNG OR WITH_SPNG) - if(WITH_SPNG) +if(WITH_SPNG) + if(BUILD_SPNG) status(" PNG:" "build-${SPNG_LIBRARY} (ver ${SPNG_VERSION})") - else() - status(" PNG:" PNG_FOUND THEN "${PNG_LIBRARY} (ver ${PNG_VERSION})" ELSE "build (ver ${PNG_VERSION})") + elseif(HAVE_SPNG) + status(" PNG:" "${SPNG_LIBRARY} (ver ${SPNG_VERSION})") endif() +elseif(WITH_PNG OR HAVE_PNG) + status(" PNG:" PNG_FOUND THEN "${PNG_LIBRARY} (ver ${PNG_VERSION_STRING})" ELSE "build (ver ${PNG_VERSION_STRING})") endif() if(WITH_TIFF OR HAVE_TIFF) @@ -1413,7 +1478,7 @@ endif() if(HAVE_OPENJPEG) status(" JPEG 2000:" OpenJPEG_FOUND - THEN "OpenJPEG (ver ${OPENJPEG_MAJOR_VERSION}.${OPENJPEG_MINOR_VERSION}.${OPENJPEG_BUILD_VERSION})" + THEN "OpenJPEG (ver ${OPENJPEG_VERSION})" ELSE "build (ver ${OPENJPEG_VERSION})" ) elseif(HAVE_JASPER) @@ -1554,6 +1619,11 @@ if(WITH_GPHOTO2 OR HAVE_GPHOTO2) status(" gPhoto2:" HAVE_GPHOTO2 THEN "YES" ELSE NO) endif() +if(ANDROID) + status(" MEDIANDK:" HAVE_ANDROID_MEDIANDK THEN "YES" ELSE NO) + status(" NDK Camera:" HAVE_ANDROID_NATIVE_CAMERA THEN "YES" ELSE NO) +endif() + # Order is similar to CV_PARALLEL_FRAMEWORK in core/src/parallel.cpp ocv_build_features_string(parallel_status EXCLUSIVE IF HAVE_TBB THEN "TBB (ver ${TBB_VERSION_MAJOR}.${TBB_VERSION_MINOR} interface ${TBB_INTERFACE_VERSION})" diff --git a/README.md b/README.md index a1a973d022..25c984e1f2 100644 --- a/README.md +++ b/README.md @@ -1,5 +1,9 @@ ## OpenCV: Open Source Computer Vision Library +### Keep OpenCV Free + +OpenCV is raising funds to keep the library free for everyone, and we need the support of the entire community to do it. [Donate to OpenCV on IndieGoGo](http://igg.me/at/opencv5) before the campaign ends on December 16 to show your support. + ### Resources * Homepage: @@ -8,7 +12,7 @@ * Q&A forum: * previous forum (read only): * Issue tracking: -* Additional OpenCV functionality: +* Additional OpenCV functionality: ### Contributing @@ -22,3 +26,13 @@ Please read the [contribution guidelines](https://github.com/opencv/opencv/wiki/ * Include tests and documentation; * Clean up "oops" commits before submitting; * Follow the [coding style guide](https://github.com/opencv/opencv/wiki/Coding_Style_Guide). + +### Additional Resources + +* [Submit your OpenCV-based project](https://form.jotform.com/233105358823151) for inclusion in Community Friday on opencv.org +* [Subscribe to the OpenCV YouTube Channel](http://youtube.com/@opencvofficial) featuring OpenCV Live, an hour-long streaming show +* [Follow OpenCV on LinkedIn](http://linkedin.com/company/opencv/) for daily posts showing the state-of-the-art in computer vision &AI +* [Apply to be an OpenCV Volunteer](https://form.jotform.com/232745316792159) to help organize events and online campaigns as well as amplify them +* [Follow OpenCV on Mastodon](http://mastodon.social/@opencv) in the Fediverse +* [Follow OpenCV on Twitter](https://twitter.com/opencvlive) +* [OpenCV.ai](https://opencv.ai): Computer Vision and AI development services from the OpenCV team. diff --git a/cmake/FindONNX.cmake b/cmake/FindONNX.cmake index b2c79a9031..0b61185824 100644 --- a/cmake/FindONNX.cmake +++ b/cmake/FindONNX.cmake @@ -11,7 +11,9 @@ if(ONNXRT_ROOT_DIR) find_library(ORT_LIB onnxruntime ${ONNXRT_ROOT_DIR}/lib CMAKE_FIND_ROOT_PATH_BOTH) + # The location of headers varies across different versions of ONNX Runtime find_path(ORT_INCLUDE onnxruntime_cxx_api.h + ${ONNXRT_ROOT_DIR}/include/onnxruntime/ ${ONNXRT_ROOT_DIR}/include/onnxruntime/core/session CMAKE_FIND_ROOT_PATH_BOTH) endif() @@ -32,6 +34,14 @@ if(ORT_LIB AND ORT_INCLUDE) HAVE_ONNX_DML ) + # Check CoreML Execution Provider availability + get_filename_component(coreml_dir ${ONNXRT_ROOT_DIR}/include/onnxruntime/core/providers/coreml ABSOLUTE) + detect_onxxrt_ep( + coreml_provider_factory.h + ${coreml_dir} + HAVE_ONNX_COREML + ) + set(HAVE_ONNX TRUE) # For CMake output only set(ONNX_LIBRARIES "${ORT_LIB}" CACHE STRING "ONNX Runtime libraries") diff --git a/cmake/OpenCVCRTLinkage.cmake b/cmake/OpenCVCRTLinkage.cmake index 0e0a54ecf9..c29daad0d1 100644 --- a/cmake/OpenCVCRTLinkage.cmake +++ b/cmake/OpenCVCRTLinkage.cmake @@ -33,34 +33,44 @@ endif() # Ignore warning: This object file does not define any previously undefined public symbols, ... set(CMAKE_STATIC_LINKER_FLAGS "${CMAKE_STATIC_LINKER_FLAGS} /IGNORE:4221") +if(POLICY CMP0091) + cmake_policy(GET CMP0091 MSVC_RUNTIME_SET_BY_ABSTRACTION) +endif() + if(NOT BUILD_SHARED_LIBS AND BUILD_WITH_STATIC_CRT) - foreach(flag_var - CMAKE_C_FLAGS CMAKE_C_FLAGS_DEBUG CMAKE_C_FLAGS_RELEASE - CMAKE_C_FLAGS_MINSIZEREL CMAKE_C_FLAGS_RELWITHDEBINFO - CMAKE_CXX_FLAGS CMAKE_CXX_FLAGS_DEBUG CMAKE_CXX_FLAGS_RELEASE - CMAKE_CXX_FLAGS_MINSIZEREL CMAKE_CXX_FLAGS_RELWITHDEBINFO) - if(${flag_var} MATCHES "/MD") - string(REGEX REPLACE "/MD" "/MT" ${flag_var} "${${flag_var}}") - endif() - if(${flag_var} MATCHES "/MDd") - string(REGEX REPLACE "/MDd" "/MTd" ${flag_var} "${${flag_var}}") - endif() - endforeach(flag_var) + if(MSVC_RUNTIME_SET_BY_ABSTRACTION STREQUAL "NEW") + set(CMAKE_MSVC_RUNTIME_LIBRARY "MultiThreaded$<$:Debug>") + else() + foreach(flag_var + CMAKE_C_FLAGS CMAKE_C_FLAGS_DEBUG CMAKE_C_FLAGS_RELEASE + CMAKE_C_FLAGS_MINSIZEREL CMAKE_C_FLAGS_RELWITHDEBINFO + CMAKE_CXX_FLAGS CMAKE_CXX_FLAGS_DEBUG CMAKE_CXX_FLAGS_RELEASE + CMAKE_CXX_FLAGS_MINSIZEREL CMAKE_CXX_FLAGS_RELWITHDEBINFO) + if(${flag_var} MATCHES "/MD") + string(REGEX REPLACE "/MD" "/MT" ${flag_var} "${${flag_var}}") + endif() + if(${flag_var} MATCHES "/MDd") + string(REGEX REPLACE "/MDd" "/MTd" ${flag_var} "${${flag_var}}") + endif() + endforeach(flag_var) + endif() set(CMAKE_EXE_LINKER_FLAGS "${CMAKE_EXE_LINKER_FLAGS} /NODEFAULTLIB:atlthunk.lib") set(CMAKE_EXE_LINKER_FLAGS_DEBUG "${CMAKE_EXE_LINKER_FLAGS_DEBUG} /NODEFAULTLIB:libcmt.lib /NODEFAULTLIB:libcpmt.lib /NODEFAULTLIB:msvcrt.lib") set(CMAKE_EXE_LINKER_FLAGS_RELEASE "${CMAKE_EXE_LINKER_FLAGS_RELEASE} /NODEFAULTLIB:libcmtd.lib /NODEFAULTLIB:libcpmtd.lib /NODEFAULTLIB:msvcrtd.lib") else() - foreach(flag_var - CMAKE_C_FLAGS CMAKE_C_FLAGS_DEBUG CMAKE_C_FLAGS_RELEASE - CMAKE_C_FLAGS_MINSIZEREL CMAKE_C_FLAGS_RELWITHDEBINFO - CMAKE_CXX_FLAGS CMAKE_CXX_FLAGS_DEBUG CMAKE_CXX_FLAGS_RELEASE - CMAKE_CXX_FLAGS_MINSIZEREL CMAKE_CXX_FLAGS_RELWITHDEBINFO) - if(${flag_var} MATCHES "/MT") - string(REGEX REPLACE "/MT" "/MD" ${flag_var} "${${flag_var}}") - endif() - if(${flag_var} MATCHES "/MTd") - string(REGEX REPLACE "/MTd" "/MDd" ${flag_var} "${${flag_var}}") - endif() - endforeach(flag_var) + if(NOT MSVC_RUNTIME_SET_BY_ABSTRACTION STREQUAL "NEW") + foreach(flag_var + CMAKE_C_FLAGS CMAKE_C_FLAGS_DEBUG CMAKE_C_FLAGS_RELEASE + CMAKE_C_FLAGS_MINSIZEREL CMAKE_C_FLAGS_RELWITHDEBINFO + CMAKE_CXX_FLAGS CMAKE_CXX_FLAGS_DEBUG CMAKE_CXX_FLAGS_RELEASE + CMAKE_CXX_FLAGS_MINSIZEREL CMAKE_CXX_FLAGS_RELWITHDEBINFO) + if(${flag_var} MATCHES "/MT") + string(REGEX REPLACE "/MT" "/MD" ${flag_var} "${${flag_var}}") + endif() + if(${flag_var} MATCHES "/MTd") + string(REGEX REPLACE "/MTd" "/MDd" ${flag_var} "${${flag_var}}") + endif() + endforeach(flag_var) + endif() endif() diff --git a/cmake/OpenCVCompilerOptimizations.cmake b/cmake/OpenCVCompilerOptimizations.cmake index d45f327beb..ea577e4a1c 100644 --- a/cmake/OpenCVCompilerOptimizations.cmake +++ b/cmake/OpenCVCompilerOptimizations.cmake @@ -49,7 +49,7 @@ set(CPU_ALL_OPTIMIZATIONS "SSE;SSE2;SSE3;SSSE3;SSE4_1;SSE4_2;POPCNT;AVX;FP16;AVX2;FMA3;AVX_512F") list(APPEND CPU_ALL_OPTIMIZATIONS "AVX512_COMMON;AVX512_KNL;AVX512_KNM;AVX512_SKX;AVX512_CNL;AVX512_CLX;AVX512_ICL") -list(APPEND CPU_ALL_OPTIMIZATIONS NEON VFPV3 FP16 NEON_DOTPROD) +list(APPEND CPU_ALL_OPTIMIZATIONS NEON VFPV3 FP16 NEON_DOTPROD NEON_FP16 NEON_BF16) list(APPEND CPU_ALL_OPTIMIZATIONS MSA) list(APPEND CPU_ALL_OPTIMIZATIONS VSX VSX3) list(APPEND CPU_ALL_OPTIMIZATIONS RVV) @@ -349,12 +349,18 @@ elseif(ARM OR AARCH64) ocv_update(CPU_KNOWN_OPTIMIZATIONS "NEON;FP16;NEON_DOTPROD;NEON_FP16;NEON_BF16") ocv_update(CPU_NEON_FLAGS_ON "") ocv_update(CPU_FP16_IMPLIES "NEON") - ocv_update(CPU_NEON_DOTPROD_FLAGS_ON "-march=armv8.2-a+dotprod") ocv_update(CPU_NEON_DOTPROD_IMPLIES "NEON") - ocv_update(CPU_NEON_FP16_FLAGS_ON "-march=armv8.2-a+fp16") ocv_update(CPU_NEON_FP16_IMPLIES "NEON") - ocv_update(CPU_NEON_BF16_FLAGS_ON "-march=armv8.2-a+fp16+bf16") ocv_update(CPU_NEON_BF16_IMPLIES "NEON") + if(MSVC) + ocv_update(CPU_NEON_DOTPROD_FLAGS_ON "") + ocv_update(CPU_NEON_FP16_FLAGS_ON "") + ocv_update(CPU_NEON_BF16_FLAGS_ON "") + else() + ocv_update(CPU_NEON_DOTPROD_FLAGS_ON "-march=armv8.2-a+dotprod") + ocv_update(CPU_NEON_FP16_FLAGS_ON "-march=armv8.2-a+fp16") + ocv_update(CPU_NEON_BF16_FLAGS_ON "-march=armv8.2-a+bf16") + endif() set(CPU_BASELINE "NEON;FP16" CACHE STRING "${HELP_CPU_BASELINE}") set(CPU_DISPATCH "NEON_FP16;NEON_BF16;NEON_DOTPROD" CACHE STRING "${HELP_CPU_DISPATCH}") endif() @@ -403,11 +409,8 @@ elseif(LOONGARCH64) ocv_update(CPU_KNOWN_OPTIMIZATIONS "LSX;LASX") ocv_update(CPU_LSX_FLAGS_ON "-mlsx") ocv_update(CPU_LASX_FLAGS_ON "-mlasx") - if("${CPU_BASELINE_DISABLE}" STREQUAL "LASX") - set(CPU_BASELINE "LSX" CACHE STRING "${HELP_CPU_BASELINE}") - else() - set(CPU_BASELINE "LASX" CACHE STRING "${HELP_CPU_BASELINE}") - endif() + set(CPU_BASELINE "LSX" CACHE STRING "${HELP_CPU_BASELINE}") + set(CPU_DISPATCH "LASX" CACHE STRING "${HELP_CPU_DISPATCH}") endif() @@ -480,6 +483,25 @@ macro(ocv_check_compiler_optimization OPT) endif() endmacro() +macro(ocv_cpu_aarch64_baseline_merge_feature_options FEATURE_NAME_LIST FLAG_STRING COMMON_OPTION) + if(NOT MSVC) + unset(_POSTFIX) + # Check each feature option + foreach(OPT IN LISTS ${FEATURE_NAME_LIST}) + string(FIND "${${FLAG_STRING}}" "${CPU_${OPT}_FLAGS_ON}" OPT_FOUND) + if(NOT ${OPT_FOUND} EQUAL -1) + string(REPLACE "${COMMON_OPTION}" "" TRAILING_PART "${CPU_${OPT}_FLAGS_ON}") + string(APPEND _POSTFIX "${TRAILING_PART}") + string(REPLACE " ${CPU_${OPT}_FLAGS_ON}" "" ${FLAG_STRING} ${${FLAG_STRING}}) + endif() + endforeach() + # If more than one option found, merge them + if(NOT "x${_POSTFIX}" STREQUAL "x") + set(${FLAG_STRING} "${${FLAG_STRING}} ${COMMON_OPTION}${_POSTFIX}") + endif() + endif() +endmacro() + foreach(OPT ${CPU_KNOWN_OPTIMIZATIONS}) set(CPU_${OPT}_USAGE_COUNT 0 CACHE INTERNAL "") if("${CPU_${OPT}_FLAGS_ON}" STREQUAL "disabled") @@ -573,6 +595,13 @@ foreach(OPT ${CPU_KNOWN_OPTIMIZATIONS}) endif() endforeach() +if(AARCH64) + # Define the list of NEON options to check + set(NEON_OPTIONS_LIST NEON_DOTPROD NEON_FP16 NEON_BF16) + set(BASE_ARCHITECTURE "-march=armv8.2-a") + ocv_cpu_aarch64_baseline_merge_feature_options(NEON_OPTIONS_LIST CPU_BASELINE_FLAGS ${BASE_ARCHITECTURE}) +endif() + foreach(OPT ${CPU_BASELINE_REQUIRE}) if(NOT ";${CPU_BASELINE_FINAL};" MATCHES ";${OPT};") message(SEND_ERROR "Required baseline optimization is not supported: ${OPT} (CPU_BASELINE_REQUIRE=${CPU_BASELINE_REQUIRE})") diff --git a/cmake/OpenCVDetectCUDA.cmake b/cmake/OpenCVDetectCUDA.cmake index 7887c798e4..e0c539b90a 100644 --- a/cmake/OpenCVDetectCUDA.cmake +++ b/cmake/OpenCVDetectCUDA.cmake @@ -1,10 +1,10 @@ if((WIN32 AND NOT MSVC) OR OPENCV_CMAKE_FORCE_CUDA) - message(STATUS "CUDA compilation is disabled (due to only Visual Studio compiler supported on your platform).") + message(STATUS "CUDA: Compilation is disabled (due to only Visual Studio compiler supported on your platform).") return() endif() if((NOT UNIX AND CV_CLANG) OR OPENCV_CMAKE_FORCE_CUDA) - message(STATUS "CUDA compilation is disabled (due to Clang unsupported on your platform).") + message(STATUS "CUDA: Compilation is disabled (due to Clang unsupported on your platform).") return() endif() @@ -31,444 +31,144 @@ else() list(REMOVE_AT CMAKE_MODULE_PATH 0) endif() -if(CUDA_FOUND) - unset(CUDA_nvcuvenc_LIBRARY CACHE) - set(HAVE_CUDA 1) - if(NOT CUDA_VERSION VERSION_LESS 11.0) - # CUDA 11.0 removes nppicom - ocv_list_filterout(CUDA_nppi_LIBRARY "nppicom") - ocv_list_filterout(CUDA_npp_LIBRARY "nppicom") - endif() - - if(WITH_CUFFT) - set(HAVE_CUFFT 1) - endif() - - if(WITH_CUBLAS) - set(HAVE_CUBLAS 1) - endif() - - if(WITH_CUDNN) - set(CMAKE_MODULE_PATH "${OpenCV_SOURCE_DIR}/cmake" ${CMAKE_MODULE_PATH}) - find_host_package(CUDNN "${MIN_VER_CUDNN}") - list(REMOVE_AT CMAKE_MODULE_PATH 0) - - if(CUDNN_FOUND) - set(HAVE_CUDNN 1) - endif() - endif() - - if(WITH_NVCUVID OR WITH_NVCUVENC) - macro(ocv_cuda_SEARCH_NVCUVID_HEADER _filename _result) - # place header file under CUDA_TOOLKIT_TARGET_DIR or CUDA_TOOLKIT_ROOT_DIR - find_path(_header_result - ${_filename} - PATHS "${CUDA_TOOLKIT_TARGET_DIR}" "${CUDA_TOOLKIT_ROOT_DIR}" - ENV CUDA_PATH - ENV CUDA_INC_PATH - PATH_SUFFIXES include - NO_DEFAULT_PATH - ) - if("x${_header_result}" STREQUAL "x_header_result-NOTFOUND") - set(${_result} 0) - else() - set(${_result} 1) - endif() - unset(_header_result CACHE) - endmacro() - if(WITH_NVCUVID) - ocv_cuda_SEARCH_NVCUVID_HEADER("nvcuvid.h" HAVE_NVCUVID_HEADER) - ocv_cuda_SEARCH_NVCUVID_HEADER("dynlink_nvcuvid.h" HAVE_DYNLINK_NVCUVID_HEADER) - find_cuda_helper_libs(nvcuvid) - if(CUDA_nvcuvid_LIBRARY AND (${HAVE_NVCUVID_HEADER} OR ${HAVE_DYNLINK_NVCUVID_HEADER})) - # make sure to have both header and library before enabling - set(HAVE_NVCUVID 1) - endif() - endif() - if(WITH_NVCUVENC) - ocv_cuda_SEARCH_NVCUVID_HEADER("nvEncodeAPI.h" HAVE_NVCUVENC_HEADER) - if(WIN32) - find_cuda_helper_libs(nvencodeapi) - else() - find_cuda_helper_libs(nvidia-encode) - endif() - if((CUDA_nvencodeapi_LIBRARY OR CUDA_nvidia-encode_LIBRARY) AND ${HAVE_NVCUVENC_HEADER}) - set(HAVE_NVCUVENC 1) - endif() - endif() - endif() - - message(STATUS "CUDA detected: " ${CUDA_VERSION}) - - OCV_OPTION(CUDA_ENABLE_DEPRECATED_GENERATION "Enable deprecated generations in the list" OFF) - set(_generations "Maxwell" "Pascal" "Volta" "Turing" "Ampere" "Lovelace" "Hopper") - if(CUDA_ENABLE_DEPRECATED_GENERATION) - set(_generations "Fermi" "${_generations}") - set(_generations "Kepler" "${_generations}") - endif() - set(_arch_fermi "2.0") - set(_arch_kepler "3.0;3.5;3.7") - set(_arch_maxwell "5.0;5.2") - set(_arch_pascal "6.0;6.1") - set(_arch_volta "7.0") - set(_arch_turing "7.5") - set(_arch_ampere "8.0;8.6") - set(_arch_lovelace "8.9") - set(_arch_hopper "9.0") - if(NOT CMAKE_CROSSCOMPILING) - list(APPEND _generations "Auto") - endif() - set(CUDA_GENERATION "" CACHE STRING "Build CUDA device code only for specific GPU architecture. Leave empty to build for all architectures.") - if( CMAKE_VERSION VERSION_GREATER "2.8" ) - set_property( CACHE CUDA_GENERATION PROPERTY STRINGS "" ${_generations} ) - endif() - - if(CUDA_GENERATION) - if(NOT ";${_generations};" MATCHES ";${CUDA_GENERATION};") - string(REPLACE ";" ", " _generations "${_generations}") - message(FATAL_ERROR "ERROR: ${_generations} Generations are supported.") - endif() - unset(CUDA_ARCH_BIN CACHE) - unset(CUDA_ARCH_PTX CACHE) - endif() - - if(OPENCV_CUDA_DETECTION_NVCC_FLAGS MATCHES "-ccbin") - # already specified by user - elseif(CUDA_HOST_COMPILER AND EXISTS "${CUDA_HOST_COMPILER}") - get_filename_component(c_compiler_realpath "${CMAKE_C_COMPILER}" REALPATH) - # C compiler doesn't work with --run option, forcing C++ compiler instead - if(CUDA_HOST_COMPILER STREQUAL c_compiler_realpath OR CUDA_HOST_COMPILER STREQUAL CMAKE_C_COMPILER) - if(DEFINED CMAKE_CXX_COMPILER) - get_filename_component(cxx_compiler_realpath "${CMAKE_CXX_COMPILER}" REALPATH) - LIST(APPEND OPENCV_CUDA_DETECTION_NVCC_FLAGS -ccbin "${cxx_compiler_realpath}") - else() - message(STATUS "CUDA: CMAKE_CXX_COMPILER is not available. You may need to specify CUDA_HOST_COMPILER.") - endif() - else() - LIST(APPEND OPENCV_CUDA_DETECTION_NVCC_FLAGS -ccbin "${CUDA_HOST_COMPILER}") - endif() - elseif(WIN32 AND CMAKE_LINKER) # Workaround for VS cl.exe not being in the env. path - get_filename_component(host_compiler_bindir ${CMAKE_LINKER} DIRECTORY) - LIST(APPEND OPENCV_CUDA_DETECTION_NVCC_FLAGS -ccbin "${host_compiler_bindir}") - else() - if(CUDA_HOST_COMPILER) - message(STATUS "CUDA: CUDA_HOST_COMPILER='${CUDA_HOST_COMPILER}' is not valid, autodetection may not work. Specify OPENCV_CUDA_DETECTION_NVCC_FLAGS with -ccbin option for fix that") - endif() - endif() - - macro(ocv_filter_available_architecture result_list) - set(__cache_key_check "${ARGN} : ${CUDA_NVCC_EXECUTABLE} ${OPENCV_CUDA_DETECTION_NVCC_FLAGS}") - if(DEFINED OPENCV_CACHE_CUDA_SUPPORTED_CC AND OPENCV_CACHE_CUDA_SUPPORTED_CC_check STREQUAL __cache_key_check) - set(${result_list} "${OPENCV_CACHE_CUDA_SUPPORTED_CC}") - else() - set(CC_LIST ${ARGN}) - foreach(target_arch ${CC_LIST}) - string(REPLACE "." "" target_arch_short "${target_arch}") - set(NVCC_OPTION "-gencode;arch=compute_${target_arch_short},code=sm_${target_arch_short}") - set(_cmd "${CUDA_NVCC_EXECUTABLE}" ${OPENCV_CUDA_DETECTION_NVCC_FLAGS} ${NVCC_OPTION} "${OpenCV_SOURCE_DIR}/cmake/checks/OpenCVDetectCudaArch.cu" --compile) - execute_process( - COMMAND ${_cmd} - WORKING_DIRECTORY "${CMAKE_BINARY_DIR}${CMAKE_FILES_DIRECTORY}/CMakeTmp/" - RESULT_VARIABLE _nvcc_res - OUTPUT_VARIABLE _nvcc_out - ERROR_VARIABLE _nvcc_err - #ERROR_QUIET - OUTPUT_STRIP_TRAILING_WHITESPACE - ) - if(OPENCV_CMAKE_CUDA_DEBUG) - message(WARNING "COMMAND: ${_cmd}") - message(STATUS "Result: ${_nvcc_res}") - message(STATUS "Out: ${_nvcc_out}") - message(STATUS "Err: ${_nvcc_err}") - endif() - if(_nvcc_res EQUAL 0) - LIST(APPEND ${result_list} "${target_arch}") - endif() - endforeach() - string(STRIP "${${result_list}}" ${result_list}) - if(" ${${result_list}}" STREQUAL " ") - message(WARNING "CUDA: Autodetection arch list is empty. Please enable OPENCV_CMAKE_CUDA_DEBUG=1 and check/specify OPENCV_CUDA_DETECTION_NVCC_FLAGS variable") - endif() - - # cache detected values - set(OPENCV_CACHE_CUDA_SUPPORTED_CC ${${result_list}} CACHE INTERNAL "") - set(OPENCV_CACHE_CUDA_SUPPORTED_CC_check "${__cache_key_check}" CACHE INTERNAL "") - endif() - endmacro() - - macro(ocv_detect_native_cuda_arch status output) - set(OPENCV_CUDA_DETECT_ARCHS_COMMAND "${CUDA_NVCC_EXECUTABLE}" ${OPENCV_CUDA_DETECTION_NVCC_FLAGS} "${OpenCV_SOURCE_DIR}/cmake/checks/OpenCVDetectCudaArch.cu" "--run") - set(__cache_key_check "${OPENCV_CUDA_DETECT_ARCHS_COMMAND}") - if(DEFINED OPENCV_CACHE_CUDA_ACTIVE_CC AND OPENCV_CACHE_CUDA_ACTIVE_CC_check STREQUAL __cache_key_check) - set(${output} "${OPENCV_CACHE_CUDA_ACTIVE_CC}") - set(${status} 0) - else() - execute_process( - COMMAND ${OPENCV_CUDA_DETECT_ARCHS_COMMAND} - WORKING_DIRECTORY "${CMAKE_BINARY_DIR}${CMAKE_FILES_DIRECTORY}/CMakeTmp/" - RESULT_VARIABLE ${status} - OUTPUT_VARIABLE _nvcc_out - ERROR_VARIABLE _nvcc_err - ERROR_QUIET - OUTPUT_STRIP_TRAILING_WHITESPACE - ) - if(OPENCV_CMAKE_CUDA_DEBUG) - message(WARNING "COMMAND: ${OPENCV_CUDA_DETECT_ARCHS_COMMAND}") - message(STATUS "Result: ${${status}}") - message(STATUS "Out: ${_nvcc_out}") - message(STATUS "Err: ${_nvcc_err}") - endif() - string(REGEX REPLACE ".*\n" "" ${output} "${_nvcc_out}") #Strip leading warning messages, if any - - if(${status} EQUAL 0) - # cache detected values - set(OPENCV_CACHE_CUDA_ACTIVE_CC ${${output}} CACHE INTERNAL "") - set(OPENCV_CACHE_CUDA_ACTIVE_CC_check "${__cache_key_check}" CACHE INTERNAL "") - endif() - endif() - endmacro() - - set(__cuda_arch_ptx ${CUDA_ARCH_PTX}) - if(CUDA_GENERATION STREQUAL "Fermi") - set(__cuda_arch_bin ${_arch_fermi}) - elseif(CUDA_GENERATION STREQUAL "Kepler") - set(__cuda_arch_bin ${_arch_kepler}) - elseif(CUDA_GENERATION STREQUAL "Maxwell") - set(__cuda_arch_bin ${_arch_maxwell}) - elseif(CUDA_GENERATION STREQUAL "Pascal") - set(__cuda_arch_bin ${_arch_pascal}) - elseif(CUDA_GENERATION STREQUAL "Volta") - set(__cuda_arch_bin ${_arch_volta}) - elseif(CUDA_GENERATION STREQUAL "Turing") - set(__cuda_arch_bin ${_arch_turing}) - elseif(CUDA_GENERATION STREQUAL "Ampere") - set(__cuda_arch_bin ${_arch_ampere}) - elseif(CUDA_GENERATION STREQUAL "Lovelace") - set(__cuda_arch_bin ${_arch_lovelace}) - elseif(CUDA_GENERATION STREQUAL "Hopper") - set(__cuda_arch_bin ${_arch_hopper}) - elseif(CUDA_GENERATION STREQUAL "Auto") - ocv_detect_native_cuda_arch(_nvcc_res _nvcc_out) - if(NOT _nvcc_res EQUAL 0) - message(STATUS "Automatic detection of CUDA generation failed. Going to build for all known architectures.") - else() - string(REGEX MATCHALL "[0-9]+\\.[0-9]" __cuda_arch_bin "${_nvcc_out}") - endif() - elseif(CUDA_ARCH_BIN) - message(STATUS "CUDA: Using CUDA_ARCH_BIN=${CUDA_ARCH_BIN}") - set(__cuda_arch_bin ${CUDA_ARCH_BIN}) - endif() - - if(NOT DEFINED __cuda_arch_bin AND NOT DEFINED __cuda_arch_ptx) - if(ARM) - set(__cuda_arch_bin "3.2") - set(__cuda_arch_ptx "") - elseif(AARCH64) - if(NOT CMAKE_CROSSCOMPILING) - ocv_detect_native_cuda_arch(_nvcc_res _nvcc_out) - else() - set(_nvcc_res -1) # emulate error, see below - endif() - if(NOT _nvcc_res EQUAL 0) - message(STATUS "Automatic detection of CUDA generation failed. Going to build for all known architectures.") - # TX1 (5.3) TX2 (6.2) Xavier (7.2) V100 (7.0) Orin (8.7) - ocv_filter_available_architecture(__cuda_arch_bin - 5.3 - 6.2 - 7.2 - 7.0 - 8.7 - ) - else() - set(__cuda_arch_bin "${_nvcc_out}") - endif() - set(__cuda_arch_ptx "") - else() - ocv_filter_available_architecture(__cuda_arch_bin - ${_arch_fermi} - ${_arch_kepler} - ${_arch_maxwell} - ${_arch_pascal} - ${_arch_volta} - ${_arch_turing} - ${_arch_ampere} - ${_arch_lovelace} - ${_arch_hopper} - ) - list(GET __cuda_arch_bin -1 __cuda_arch_ptx) - endif() - endif() - - set(CUDA_ARCH_BIN ${__cuda_arch_bin} CACHE STRING "Specify 'real' GPU architectures to build binaries for, BIN(PTX) format is supported") - set(CUDA_ARCH_PTX ${__cuda_arch_ptx} CACHE STRING "Specify 'virtual' PTX architectures to build PTX intermediate code for") - - string(REGEX REPLACE "\\." "" ARCH_BIN_NO_POINTS "${CUDA_ARCH_BIN}") - string(REGEX REPLACE "\\." "" ARCH_PTX_NO_POINTS "${CUDA_ARCH_PTX}") - - # Check if user specified 1.0/2.1 compute capability: we don't support it - macro(ocv_wipeout_deprecated_cc target_cc) - if(" ${CUDA_ARCH_BIN} ${CUDA_ARCH_PTX}" MATCHES " ${target_cc}") - message(SEND_ERROR "CUDA: ${target_cc} compute capability is not supported - exclude it from ARCH/PTX list and re-run CMake") - endif() - endmacro() - ocv_wipeout_deprecated_cc("1.0") - ocv_wipeout_deprecated_cc("2.1") - - # NVCC flags to be set - set(NVCC_FLAGS_EXTRA "") - - # These vars will be passed into the templates - set(OPENCV_CUDA_ARCH_BIN "") - set(OPENCV_CUDA_ARCH_PTX "") - set(OPENCV_CUDA_ARCH_FEATURES "") - - # Tell NVCC to add binaries for the specified GPUs - string(REGEX MATCHALL "[0-9()]+" ARCH_LIST "${ARCH_BIN_NO_POINTS}") - foreach(ARCH IN LISTS ARCH_LIST) - if(ARCH MATCHES "([0-9]+)\\(([0-9]+)\\)") - # User explicitly specified PTX for the concrete BIN - set(NVCC_FLAGS_EXTRA ${NVCC_FLAGS_EXTRA} -gencode arch=compute_${CMAKE_MATCH_2},code=sm_${CMAKE_MATCH_1}) - set(OPENCV_CUDA_ARCH_BIN "${OPENCV_CUDA_ARCH_BIN} ${CMAKE_MATCH_1}") - set(OPENCV_CUDA_ARCH_FEATURES "${OPENCV_CUDA_ARCH_FEATURES} ${CMAKE_MATCH_2}") - else() - # User didn't explicitly specify PTX for the concrete BIN, we assume PTX=BIN - set(NVCC_FLAGS_EXTRA ${NVCC_FLAGS_EXTRA} -gencode arch=compute_${ARCH},code=sm_${ARCH}) - set(OPENCV_CUDA_ARCH_BIN "${OPENCV_CUDA_ARCH_BIN} ${ARCH}") - set(OPENCV_CUDA_ARCH_FEATURES "${OPENCV_CUDA_ARCH_FEATURES} ${ARCH}") - endif() - endforeach() - set(NVCC_FLAGS_EXTRA ${NVCC_FLAGS_EXTRA} -D_FORCE_INLINES) - - # Tell NVCC to add PTX intermediate code for the specified architectures - string(REGEX MATCHALL "[0-9]+" ARCH_LIST "${ARCH_PTX_NO_POINTS}") - foreach(ARCH IN LISTS ARCH_LIST) - set(NVCC_FLAGS_EXTRA ${NVCC_FLAGS_EXTRA} -gencode arch=compute_${ARCH},code=compute_${ARCH}) - set(OPENCV_CUDA_ARCH_PTX "${OPENCV_CUDA_ARCH_PTX} ${ARCH}") - set(OPENCV_CUDA_ARCH_FEATURES "${OPENCV_CUDA_ARCH_FEATURES} ${ARCH}") - endforeach() - - # These vars will be processed in other scripts - set(CUDA_NVCC_FLAGS ${CUDA_NVCC_FLAGS} ${NVCC_FLAGS_EXTRA}) - set(OpenCV_CUDA_CC "${NVCC_FLAGS_EXTRA}") - - if(ANDROID) - set(CUDA_NVCC_FLAGS ${CUDA_NVCC_FLAGS} "-Xptxas;-dlcm=ca") - endif() - - # Tell NVCC the maximum number of threads to be used to execute the compilation steps in parallel - # (option --threads was introduced in version 11.2) - if(NOT CUDA_VERSION VERSION_LESS "11.2") - if(CMAKE_GENERATOR MATCHES "Visual Studio" AND NOT $ENV{CMAKE_BUILD_PARALLEL_LEVEL} STREQUAL "") - set(CUDA_NVCC_FLAGS ${CUDA_NVCC_FLAGS} "--threads=$ENV{CMAKE_BUILD_PARALLEL_LEVEL}") - endif() - endif() - - message(STATUS "CUDA NVCC target flags: ${CUDA_NVCC_FLAGS}") - - OCV_OPTION(CUDA_FAST_MATH "Enable --use_fast_math for CUDA compiler " OFF) - - if(CUDA_FAST_MATH) - set(CUDA_NVCC_FLAGS ${CUDA_NVCC_FLAGS} --use_fast_math) - endif() - - OCV_OPTION(CUDA_ENABLE_DELAYLOAD "Enable delayed loading of CUDA DLLs" OFF VISIBLE_IF MSVC) - - mark_as_advanced(CUDA_BUILD_CUBIN CUDA_BUILD_EMULATION CUDA_VERBOSE_BUILD CUDA_SDK_ROOT_DIR) - - macro(ocv_cuda_filter_options) - foreach(var CMAKE_CXX_FLAGS CMAKE_CXX_FLAGS_RELEASE CMAKE_CXX_FLAGS_DEBUG) - set(${var}_backup_in_cuda_compile_ "${${var}}") - - if (CV_CLANG) - # we remove -Winconsistent-missing-override and -Qunused-arguments - # just in case we are compiling CUDA with gcc but OpenCV with clang - string(REPLACE "-Winconsistent-missing-override" "" ${var} "${${var}}") - string(REPLACE "-Qunused-arguments" "" ${var} "${${var}}") - endif() - - # we remove /EHa as it generates warnings under windows - string(REPLACE "/EHa" "" ${var} "${${var}}") - - # we remove -ggdb3 flag as it leads to preprocessor errors when compiling CUDA files (CUDA 4.1) - string(REPLACE "-ggdb3" "" ${var} "${${var}}") - - # we remove -Wsign-promo as it generates warnings under linux - string(REPLACE "-Wsign-promo" "" ${var} "${${var}}") - - # we remove -Wno-sign-promo as it generates warnings under linux - string(REPLACE "-Wno-sign-promo" "" ${var} "${${var}}") - - # we remove -Wno-delete-non-virtual-dtor because it's used for C++ compiler - # but NVCC uses C compiler by default - string(REPLACE "-Wno-delete-non-virtual-dtor" "" ${var} "${${var}}") - - # we remove -frtti because it's used for C++ compiler - # but NVCC uses C compiler by default - string(REPLACE "-frtti" "" ${var} "${${var}}") - - string(REPLACE "-fvisibility-inlines-hidden" "" ${var} "${${var}}") - - # cc1: warning: command line option '-Wsuggest-override' is valid for C++/ObjC++ but not for C - string(REPLACE "-Wsuggest-override" "" ${var} "${${var}}") - - # issue: #11552 (from OpenCVCompilerOptions.cmake) - string(REGEX REPLACE "-Wimplicit-fallthrough(=[0-9]+)? " "" ${var} "${${var}}") - - # removal of custom specified options - if(OPENCV_CUDA_NVCC_FILTEROUT_OPTIONS) - foreach(__flag ${OPENCV_CUDA_NVCC_FILTEROUT_OPTIONS}) - string(REPLACE "${__flag}" "" ${var} "${${var}}") - endforeach() - endif() - endforeach() - endmacro() - - macro(ocv_cuda_compile VAR) - ocv_cuda_filter_options() - - if(BUILD_SHARED_LIBS) - set(CUDA_NVCC_FLAGS ${CUDA_NVCC_FLAGS} -Xcompiler -DCVAPI_EXPORTS) - endif() - - if(UNIX OR APPLE) - set(CUDA_NVCC_FLAGS ${CUDA_NVCC_FLAGS} -Xcompiler -fPIC) - if(NOT " ${CMAKE_CXX_FLAGS} ${CMAKE_CXX_FLAGS_RELEASE} ${CMAKE_CXX_FLAGS_DEBUG} ${CUDA_NVCC_FLAGS}" MATCHES "-std=") - if(CUDA_VERSION VERSION_LESS "11.0") - list(APPEND CUDA_NVCC_FLAGS "--std=c++11") - else() - list(APPEND CUDA_NVCC_FLAGS "--std=c++14") - endif() - endif() - endif() - if(APPLE) - set(CUDA_NVCC_FLAGS ${CUDA_NVCC_FLAGS} -Xcompiler -fno-finite-math-only) - endif() - - if(WIN32 AND NOT (CUDA_VERSION VERSION_LESS "11.2")) - set(CUDA_NVCC_FLAGS ${CUDA_NVCC_FLAGS} -Xcudafe --display_error_number --diag-suppress 1394,1388) - endif() - - if(CMAKE_CROSSCOMPILING AND (ARM OR AARCH64)) - set(CUDA_NVCC_FLAGS ${CUDA_NVCC_FLAGS} -Xlinker --unresolved-symbols=ignore-in-shared-libs) - endif() - - # disabled because of multiple warnings during building nvcc auto generated files - if(CV_GCC AND CMAKE_CXX_COMPILER_VERSION VERSION_GREATER "4.6.0") - ocv_warnings_disable(CMAKE_CXX_FLAGS -Wunused-but-set-variable) - endif() - - CUDA_COMPILE(${VAR} ${ARGN}) - - foreach(var CMAKE_CXX_FLAGS CMAKE_CXX_FLAGS_RELEASE CMAKE_CXX_FLAGS_DEBUG) - set(${var} "${${var}_backup_in_cuda_compile_}") - unset(${var}_backup_in_cuda_compile_) - endforeach() - endmacro() -else() +if(NOT CUDA_FOUND) unset(CUDA_ARCH_BIN CACHE) unset(CUDA_ARCH_PTX CACHE) + return() endif() +unset(CUDA_nvcuvenc_LIBRARY CACHE) +set(HAVE_CUDA 1) +if(NOT CUDA_VERSION VERSION_LESS 11.0) + # CUDA 11.0 removes nppicom + ocv_list_filterout(CUDA_nppi_LIBRARY "nppicom") + ocv_list_filterout(CUDA_npp_LIBRARY "nppicom") +endif() + +if(WITH_CUFFT) + set(HAVE_CUFFT 1) +endif() + +if(WITH_CUBLAS) + set(HAVE_CUBLAS 1) +endif() + +if(WITH_CUDNN) + set(CMAKE_MODULE_PATH "${OpenCV_SOURCE_DIR}/cmake" ${CMAKE_MODULE_PATH}) + find_host_package(CUDNN "${MIN_VER_CUDNN}") + list(REMOVE_AT CMAKE_MODULE_PATH 0) + + if(CUDNN_FOUND) + set(HAVE_CUDNN 1) + endif() +endif() + +include(cmake/OpenCVDetectCUDAUtils.cmake) + +if(WITH_NVCUVID OR WITH_NVCUVENC) + set(cuda_toolkit_dirs "${CUDA_TOOLKIT_TARGET_DIR}" "${CUDA_TOOLKIT_ROOT_DIR}") + ocv_check_for_nvidia_video_codec_sdk("${cuda_toolkit_dirs}") +endif() + +message(STATUS "CUDA detected: " ${CUDA_VERSION}) + +ocv_set_cuda_detection_nvcc_flags(CUDA_HOST_COMPILER) +ocv_set_cuda_arch_bin_and_ptx(${CUDA_NVCC_EXECUTABLE}) + +# NVCC flags to be set +set(NVCC_FLAGS_EXTRA "") + +# These vars will be passed into the templates +set(OPENCV_CUDA_ARCH_BIN "") +set(OPENCV_CUDA_ARCH_PTX "") +set(OPENCV_CUDA_ARCH_FEATURES "") + +# Tell NVCC to add binaries for the specified GPUs +string(REGEX MATCHALL "[0-9()]+" ARCH_LIST "${ARCH_BIN_NO_POINTS}") +foreach(ARCH IN LISTS ARCH_LIST) + if(ARCH MATCHES "([0-9]+)\\(([0-9]+)\\)") + # User explicitly specified PTX for the concrete BIN + set(NVCC_FLAGS_EXTRA ${NVCC_FLAGS_EXTRA} -gencode arch=compute_${CMAKE_MATCH_2},code=sm_${CMAKE_MATCH_1}) + set(OPENCV_CUDA_ARCH_BIN "${OPENCV_CUDA_ARCH_BIN} ${CMAKE_MATCH_1}") + set(OPENCV_CUDA_ARCH_FEATURES "${OPENCV_CUDA_ARCH_FEATURES} ${CMAKE_MATCH_2}") + else() + # User didn't explicitly specify PTX for the concrete BIN, we assume PTX=BIN + set(NVCC_FLAGS_EXTRA ${NVCC_FLAGS_EXTRA} -gencode arch=compute_${ARCH},code=sm_${ARCH}) + set(OPENCV_CUDA_ARCH_BIN "${OPENCV_CUDA_ARCH_BIN} ${ARCH}") + set(OPENCV_CUDA_ARCH_FEATURES "${OPENCV_CUDA_ARCH_FEATURES} ${ARCH}") + endif() +endforeach() +set(NVCC_FLAGS_EXTRA ${NVCC_FLAGS_EXTRA} -D_FORCE_INLINES) + +# Tell NVCC to add PTX intermediate code for the specified architectures +string(REGEX MATCHALL "[0-9]+" ARCH_LIST "${ARCH_PTX_NO_POINTS}") +foreach(ARCH IN LISTS ARCH_LIST) + set(NVCC_FLAGS_EXTRA ${NVCC_FLAGS_EXTRA} -gencode arch=compute_${ARCH},code=compute_${ARCH}) + set(OPENCV_CUDA_ARCH_PTX "${OPENCV_CUDA_ARCH_PTX} ${ARCH}") + set(OPENCV_CUDA_ARCH_FEATURES "${OPENCV_CUDA_ARCH_FEATURES} ${ARCH}") +endforeach() + +# These vars will be processed in other scripts +set(CUDA_NVCC_FLAGS ${CUDA_NVCC_FLAGS} ${NVCC_FLAGS_EXTRA}) +set(OpenCV_CUDA_CC "${NVCC_FLAGS_EXTRA}") + +if(ANDROID) + set(CUDA_NVCC_FLAGS ${CUDA_NVCC_FLAGS} "-Xptxas;-dlcm=ca") +endif() + +ocv_set_nvcc_threads_for_vs() + +message(STATUS "CUDA: NVCC target flags ${CUDA_NVCC_FLAGS}") + +OCV_OPTION(CUDA_FAST_MATH "Enable --use_fast_math for CUDA compiler " OFF) + +if(CUDA_FAST_MATH) + set(CUDA_NVCC_FLAGS ${CUDA_NVCC_FLAGS} --use_fast_math) +endif() + +OCV_OPTION(CUDA_ENABLE_DELAYLOAD "Enable delayed loading of CUDA DLLs" OFF VISIBLE_IF MSVC) + +mark_as_advanced(CUDA_BUILD_CUBIN CUDA_BUILD_EMULATION CUDA_VERBOSE_BUILD CUDA_SDK_ROOT_DIR) + +macro(ocv_check_windows_crt_linkage) + # The new MSVC runtime abstraction is only useable if CUDA is a first class language + if(WIN32 AND POLICY CMP0091) + cmake_policy(GET CMP0091 MSVC_RUNTIME_SET_BY_ABSTRACTION) + if(MSVC_RUNTIME_SET_BY_ABSTRACTION STREQUAL "NEW") + if(NOT BUILD_SHARED_LIBS AND BUILD_WITH_STATIC_CRT) + set(CMAKE_CXX_FLAGS_RELEASE ${CMAKE_CXX_FLAGS_RELEASE} " /MT") + set(CMAKE_CXX_FLAGS_DEBUG ${CMAKE_CXX_FLAGS_DEBUG} " /MTd") + else() + set(CMAKE_CXX_FLAGS_RELEASE ${CMAKE_CXX_FLAGS_RELEASE} " /MD") + set(CMAKE_CXX_FLAGS_DEBUG ${CMAKE_CXX_FLAGS_DEBUG} " /MDd") + endif() + endif() + endif() +endmacro() + +macro(ocv_cuda_compile VAR) + ocv_cuda_filter_options() + ocv_check_windows_crt_linkage() + ocv_nvcc_flags() + + if(UNIX OR APPLE) + if(NOT " ${CMAKE_CXX_FLAGS} ${CMAKE_CXX_FLAGS_RELEASE} ${CMAKE_CXX_FLAGS_DEBUG} ${CUDA_NVCC_FLAGS}" MATCHES "-std=") + if(CUDA_VERSION VERSION_LESS "11.0") + list(APPEND CUDA_NVCC_FLAGS "--std=c++11") + else() + list(APPEND CUDA_NVCC_FLAGS "--std=c++14") + endif() + endif() + endif() + + CUDA_COMPILE(${VAR} ${ARGN}) + + foreach(var CMAKE_CXX_FLAGS CMAKE_CXX_FLAGS_RELEASE CMAKE_CXX_FLAGS_DEBUG) + set(${var} "${${var}_backup_in_cuda_compile_}") + unset(${var}_backup_in_cuda_compile_) + endforeach() +endmacro() + if(HAVE_CUDA) set(CUDA_LIBS_PATH "") foreach(p ${CUDA_LIBRARIES} ${CUDA_npp_LIBRARY}) @@ -525,36 +225,13 @@ if(HAVE_CUDA) endif() endif() - -# ---------------------------------------------------------------------------- -# Add CUDA libraries (needed for apps/tools, samples) -# ---------------------------------------------------------------------------- if(HAVE_CUDA) - # details: https://github.com/NVIDIA/nvidia-docker/issues/775 - if(" ${CUDA_CUDA_LIBRARY}" MATCHES "/stubs/libcuda.so" AND NOT OPENCV_SKIP_CUDA_STUB_WORKAROUND) - set(CUDA_STUB_ENABLED_LINK_WORKAROUND 1) - if(EXISTS "${CUDA_CUDA_LIBRARY}" AND NOT OPENCV_SKIP_CUDA_STUB_WORKAROUND_RPATH_LINK) - set(CUDA_STUB_TARGET_PATH "${CMAKE_BINARY_DIR}${CMAKE_FILES_DIRECTORY}/") - execute_process(COMMAND ${CMAKE_COMMAND} -E create_symlink "${CUDA_CUDA_LIBRARY}" "${CUDA_STUB_TARGET_PATH}/libcuda.so.1" - RESULT_VARIABLE CUDA_STUB_SYMLINK_RESULT) - if(NOT CUDA_STUB_SYMLINK_RESULT EQUAL 0) - execute_process(COMMAND ${CMAKE_COMMAND} -E copy_if_different "${CUDA_CUDA_LIBRARY}" "${CUDA_STUB_TARGET_PATH}/libcuda.so.1" - RESULT_VARIABLE CUDA_STUB_COPY_RESULT) - if(NOT CUDA_STUB_COPY_RESULT EQUAL 0) - set(CUDA_STUB_ENABLED_LINK_WORKAROUND 0) - endif() - endif() - if(CUDA_STUB_ENABLED_LINK_WORKAROUND) - set(CMAKE_EXE_LINKER_FLAGS "${CMAKE_EXE_LINKER_FLAGS} -Wl,-rpath-link,\"${CUDA_STUB_TARGET_PATH}\"") - endif() - else() - set(CMAKE_EXE_LINKER_FLAGS "${CMAKE_EXE_LINKER_FLAGS} -Wl,--allow-shlib-undefined") - endif() - if(NOT CUDA_STUB_ENABLED_LINK_WORKAROUND) - message(WARNING "CUDA: workaround for stubs/libcuda.so.1 is not applied") - endif() - endif() + ocv_apply_cuda_stub_workaround("${CUDA_CUDA_LIBRARY}") + ocv_check_cuda_delayed_load("${CUDA_TOOLKIT_ROOT_DIR}") + # ---------------------------------------------------------------------------- + # Add CUDA libraries (needed for apps/tools, samples) + # ---------------------------------------------------------------------------- set(OPENCV_LINKER_LIBS ${OPENCV_LINKER_LIBS} ${CUDA_LIBRARIES} ${CUDA_npp_LIBRARY}) if(HAVE_CUBLAS) set(OPENCV_LINKER_LIBS ${OPENCV_LINKER_LIBS} ${CUDA_cublas_LIBRARY}) @@ -572,19 +249,4 @@ if(HAVE_CUDA) set(OPENCV_LINKER_LIBS ${OPENCV_LINKER_LIBS} ${CMAKE_LIBRARY_PATH_FLAG}${p}) endif() endforeach() - - if(MSVC AND CUDA_ENABLE_DELAYLOAD) - set(DELAYFLAGS "delayimp.lib") - file(GLOB CUDA_DLLS "${CUDA_TOOLKIT_ROOT_DIR}/bin/*.dll") - foreach(d ${CUDA_DLLS}) - cmake_path(GET "d" FILENAME DLL_NAME) - if(NOT ${DLL_NAME} MATCHES "cudart") - set(DELAYFLAGS "${DELAYFLAGS} /DELAYLOAD:${DLL_NAME}") - endif() - endforeach() - set(DELAYFLAGS "${DELAYFLAGS} /DELAYLOAD:nvcuda.dll /DELAYLOAD:nvml.dll /IGNORE:4199") - set(CMAKE_EXE_LINKER_FLAGS "${CMAKE_EXE_LINKER_FLAGS} ${DELAYFLAGS}") - set(CMAKE_MODULE_LINKER_FLAGS "${CMAKE_MODULE_LINKER_FLAGS} ${DELAYFLAGS}") - set(CMAKE_SHARED_LINKER_FLAGS "${CMAKE_SHARED_LINKER_FLAGS} ${DELAYFLAGS}") - endif() endif() diff --git a/cmake/OpenCVDetectCUDALanguage.cmake b/cmake/OpenCVDetectCUDALanguage.cmake new file mode 100644 index 0000000000..0eeea77f2c --- /dev/null +++ b/cmake/OpenCVDetectCUDALanguage.cmake @@ -0,0 +1,154 @@ +####################### +# Previously in FindCUDA and still required for FindCUDNN +macro(FIND_CUDA_HELPER_LIBS _name) + if(CMAKE_CROSSCOMPILING AND (ARM OR AARCH64)) + set(_cuda_cross_arm_lib_dir "lib/stubs") + endif() + find_library(CUDA_${_name}_LIBRARY ${_name} + NAMES ${_name} + PATHS "${CUDAToolkit_LIBRARY_ROOT}" + PATH_SUFFIXES "lib/x64" "lib64" ${_cuda_cross_arm_lib_dir} "lib/Win32" "lib" + DOC "\"${_name}\" library" + ) + mark_as_advanced(CUDA_${_name}_LIBRARY) +endmacro() +####################### +include(cmake/OpenCVDetectCUDAUtils.cmake) + +if((WIN32 AND NOT MSVC) OR OPENCV_CMAKE_FORCE_CUDA) + message(STATUS "CUDA: Compilation is disabled (due to only Visual Studio compiler supported on your platform).") + return() +endif() + +if((NOT UNIX AND CV_CLANG) OR OPENCV_CMAKE_FORCE_CUDA) + message(STATUS "CUDA: Compilation is disabled (due to Clang unsupported on your platform).") + return() +endif() + +#set(OPENCV_CMAKE_CUDA_DEBUG 1) + +find_package(CUDAToolkit) +if(CMAKE_CUDA_COMPILER AND CUDAToolkit_FOUND) + set(CUDA_FOUND TRUE) + set(CUDA_TOOLKIT_INCLUDE ${CUDAToolkit_INCLUDE_DIRS}) + set(CUDA_VERSION_STRING ${CUDAToolkit_VERSION}) + set(CUDA_VERSION ${CUDAToolkit_VERSION}) + if(NOT CUDA_VERSION VERSION_LESS 11.0) + set(CMAKE_CUDA_STANDARD 14) + else() + set(CMAKE_CUDA_STANDARD 11) + endif() + if(UNIX AND NOT BUILD_SHARED_LIBS) + set(CUDA_LIB_EXT "_static") + endif() +endif() + +if(NOT CUDA_FOUND) + unset(CUDA_ARCH_BIN CACHE) + unset(CUDA_ARCH_PTX CACHE) + return() +endif() + +set(HAVE_CUDA 1) + +if(WITH_CUFFT) + set(HAVE_CUFFT 1) +endif() + +if(WITH_CUBLAS) + set(HAVE_CUBLAS 1) +endif() + +if(WITH_CUDNN) + set(CMAKE_MODULE_PATH "${OpenCV_SOURCE_DIR}/cmake" ${CMAKE_MODULE_PATH}) + find_host_package(CUDNN "${MIN_VER_CUDNN}") + list(REMOVE_AT CMAKE_MODULE_PATH 0) + + if(CUDNN_FOUND) + set(HAVE_CUDNN 1) + endif() +endif() + +if(WITH_NVCUVID OR WITH_NVCUVENC) + ocv_check_for_nvidia_video_codec_sdk("${CUDAToolkit_LIBRARY_ROOT}") +endif() + +ocv_check_for_cmake_cuda_architectures() +ocv_set_cuda_detection_nvcc_flags(CMAKE_CUDA_HOST_COMPILER) +ocv_set_cuda_arch_bin_and_ptx(${CUDAToolkit_NVCC_EXECUTABLE}) + +# NVCC flags to be set +set(NVCC_FLAGS_EXTRA "") + +# These vars will be passed into the templates +set(OPENCV_CUDA_ARCH_BIN "") +set(OPENCV_CUDA_ARCH_PTX "") +set(OPENCV_CUDA_ARCH_FEATURES "") + +# Tell NVCC to add binaries for the specified GPUs +string(REGEX MATCHALL "[0-9()]+" ARCH_LIST "${ARCH_BIN_NO_POINTS}") +foreach(ARCH IN LISTS ARCH_LIST) + if(ARCH MATCHES "([0-9]+)\\(([0-9]+)\\)") + # User explicitly specified PTX for the concrete BIN + set(CMAKE_CUDA_ARCHITECTURES ${CMAKE_CUDA_ARCHITECTURES} ${CMAKE_MATCH_2}-virtual;${CMAKE_MATCH_1}-real;) + set(OPENCV_CUDA_ARCH_BIN "${OPENCV_CUDA_ARCH_BIN} ${CMAKE_MATCH_1}") + set(OPENCV_CUDA_ARCH_FEATURES "${OPENCV_CUDA_ARCH_FEATURES} ${CMAKE_MATCH_2}") + else() + # User didn't explicitly specify PTX for the concrete BIN, we assume PTX=BIN + set(CMAKE_CUDA_ARCHITECTURES ${CMAKE_CUDA_ARCHITECTURES} ${ARCH}-real;) + set(OPENCV_CUDA_ARCH_BIN "${OPENCV_CUDA_ARCH_BIN} ${ARCH}") + set(OPENCV_CUDA_ARCH_FEATURES "${OPENCV_CUDA_ARCH_FEATURES} ${ARCH}") + endif() +endforeach() +set(NVCC_FLAGS_EXTRA ${NVCC_FLAGS_EXTRA} -D_FORCE_INLINES) + +# Tell NVCC to add PTX intermediate code for the specified architectures +string(REGEX MATCHALL "[0-9]+" ARCH_LIST "${ARCH_PTX_NO_POINTS}") +foreach(ARCH IN LISTS ARCH_LIST) + set(CMAKE_CUDA_ARCHITECTURES ${CMAKE_CUDA_ARCHITECTURES} ${ARCH}-virtual;) + set(OPENCV_CUDA_ARCH_PTX "${OPENCV_CUDA_ARCH_PTX} ${ARCH}") + set(OPENCV_CUDA_ARCH_FEATURES "${OPENCV_CUDA_ARCH_FEATURES} ${ARCH}") +endforeach() + +ocv_set_nvcc_threads_for_vs() + +# These vars will be processed in other scripts +set(CUDA_NVCC_FLAGS ${CUDA_NVCC_FLAGS} ${NVCC_FLAGS_EXTRA}) +set(OpenCV_CUDA_CC "${CMAKE_CUDA_ARCHITECTURES}") + +if(ANDROID) + set(CUDA_NVCC_FLAGS ${CUDA_NVCC_FLAGS} "-Xptxas;-dlcm=ca") +endif() + +message(STATUS "CUDA: NVCC target flags ${CUDA_NVCC_FLAGS}") + +OCV_OPTION(CUDA_FAST_MATH "Enable --use_fast_math for CUDA compiler " OFF) + +if(CUDA_FAST_MATH) + set(CUDA_NVCC_FLAGS ${CUDA_NVCC_FLAGS} --use_fast_math) +endif() + +OCV_OPTION(CUDA_ENABLE_DELAYLOAD "Enable delayed loading of CUDA DLLs" OFF VISIBLE_IF MSVC) + +mark_as_advanced(CUDA_BUILD_CUBIN CUDA_BUILD_EMULATION CUDA_VERBOSE_BUILD CUDA_SDK_ROOT_DIR) + +macro(ocv_cuda_unfilter_options) + foreach(var CMAKE_CXX_FLAGS CMAKE_CXX_FLAGS_RELEASE CMAKE_CXX_FLAGS_DEBUG) + set(${var} "${${var}_backup_in_cuda_compile_}") + unset(${var}_backup_in_cuda_compile_) + endforeach() +endmacro() + +macro(ocv_cuda_compile_flags) + ocv_cuda_filter_options() + ocv_nvcc_flags() + set(CMAKE_CXX_FLAGS_CUDA ${CMAKE_CXX_FLAGS}) + set(CMAKE_CXX_FLAGS_RELEASE_CUDA ${CMAKE_CXX_FLAGS_RELEASE}) + set(CMAKE_CXX_FLAGS_DEBUG_CUDA ${CMAKE_CXX_FLAGS_DEBUG}) + ocv_cuda_unfilter_options() +endmacro() + +if(HAVE_CUDA) + ocv_apply_cuda_stub_workaround("${CUDA_cuda_driver_LIBRARY}") + ocv_check_cuda_delayed_load("${cuda_toolkit_root_dir}") +endif() \ No newline at end of file diff --git a/cmake/OpenCVDetectCUDAUtils.cmake b/cmake/OpenCVDetectCUDAUtils.cmake new file mode 100644 index 0000000000..69d093efca --- /dev/null +++ b/cmake/OpenCVDetectCUDAUtils.cmake @@ -0,0 +1,442 @@ +macro(ocv_check_for_nvidia_video_codec_sdk cuda_toolkit_dirs) + macro(ocv_cuda_SEARCH_NVCUVID_HEADER _filename _result) + # place header file under CUDAToolkit_LIBRARY_ROOT + find_path(_header_result + ${_filename} + PATHS ${cuda_toolkit_dirs} + PATH_SUFFIXES include + NO_DEFAULT_PATH + ) + if("x${_header_result}" STREQUAL "x_header_result-NOTFOUND") + set(${_result} 0) + else() + set(${_result} 1) + endif() + unset(_header_result CACHE) + endmacro() + if(WITH_NVCUVID) + ocv_cuda_SEARCH_NVCUVID_HEADER("nvcuvid.h" HAVE_NVCUVID_HEADER) + # make sure to have both header and library before enabling + if(${HAVE_NVCUVID_HEADER}) + find_cuda_helper_libs(nvcuvid) + if(CUDA_nvcuvid_LIBRARY) + set(HAVE_NVCUVID 1) + message(STATUS "Found NVCUVID: ${CUDA_nvcuvid_LIBRARY}") + else() + if(WIN32) + message(STATUS "NVCUVID: Library not found, WITH_NVCUVID requires Nvidia decoding library nvcuvid.lib to either be inside ${cuda_toolkit_dirs}/lib or its location manually set with CUDA_nvcuvid_LIBRARY, i.e. CUDA_nvcuvid_LIBRARY=${cuda_toolkit_dirs}/lib/nvcuvid.lib") + else() + message(STATUS "NVCUVID: Library not found, WITH_NVCUVID requires the Nvidia decoding shared library nvcuvid.so from the driver installation or the location of the stub library to be manually set with CUDA_nvcuvid_LIBRARY i.e. CUDA_nvcuvid_LIBRARY=/home/user/Video_Codec_SDK_X.X.X/Lib/linux/stubs/x86_64/nvcuvid.so") + endif() + endif() + else() + message(STATUS "NVCUVID: Header not found, WITH_NVCUVID requires Nvidia decoding library header ${cuda_toolkit_dirs}/include/nvcuvid.h") + endif() + endif() + + if(WITH_NVCUVENC) + ocv_cuda_SEARCH_NVCUVID_HEADER("nvEncodeAPI.h" HAVE_NVCUVENC_HEADER) + if(${HAVE_NVCUVENC_HEADER}) + if(WIN32) + find_cuda_helper_libs(nvencodeapi) + else() + find_cuda_helper_libs(nvidia-encode) + endif() + if(CUDA_nvencodeapi_LIBRARY OR CUDA_nvidia-encode_LIBRARY) + set(HAVE_NVCUVENC 1) + message(STATUS "Found NVCUVENC: ${CUDA_nvencodeapi_LIBRARY} ${CUDA_nvidia-encode_LIBRARY}") + else() + if(WIN32) + message(STATUS "NVCUVENC: Library not found, WITH_NVCUVENC requires Nvidia encoding library nvencodeapi.lib to either be inside ${cuda_toolkit_dirs}/lib or its location manually set with CUDA_nvencodeapi_LIBRARY, i.e. CUDA_nvencodeapi_LIBRARY=${cuda_toolkit_dirs}/lib/nvencodeapi.lib") + else() + message(STATUS "NVCUVENC: Library not found, WITH_NVCUVENC requires the Nvidia encoding shared library libnvidia-encode.so from the driver installation or the location of the stub library to be manually set with CUDA_nvidia-encode_LIBRARY i.e. CUDA_nvidia-encode_LIBRARY=/home/user/Video_Codec_SDK_X.X.X/Lib/linux/stubs/x86_64/libnvidia-encode.so") + endif() + endif() + else() + message(STATUS "NVCUVENC: Header not found, WITH_NVCUVENC requires Nvidia encoding library header ${cuda_toolkit_dirs}/include/nvEncodeAPI.h") + endif() + endif() +endmacro() + +# Use CMAKE_CUDA_ARCHITECTURES if provided: order of preference CMAKE_CUDA_ARCHITECTURES > CUDA_GENERATION > CUDA_ARCH_BIN and/or CUDA_ARCH_PTX +function(ocv_check_for_cmake_cuda_architectures) + if(NOT CMAKE_CUDA_ARCHITECTURES) + return() + endif() + if(CMAKE_CUDA_ARCHITECTURES STREQUAL "all" OR CMAKE_CUDA_ARCHITECTURES STREQUAL "all-major" OR CMAKE_CUDA_ARCHITECTURES STREQUAL "native") + message(WARNING "CUDA: CMAKE_CUDA_ARCHITECTURES=${CMAKE_CUDA_ARCHITECTURES}, special values all, all-major and native are not supported by OpenCV, specify only CUDA real and/or virtual architectures or use combinations of CUDA_ARCH_BIN and CUDA_ARCH_PTX or specify the CUDA_GENERATION where -DCUDA_GENERATION=Auto is equivalent to native!") + return() + endif() + set(internal_ptx "") + set(internal_bin "") + foreach(ARCH IN LISTS CMAKE_CUDA_ARCHITECTURES) + if(ARCH MATCHES "([0-9]+)\-real") + set(internal_bin ${internal_bin} ${CMAKE_MATCH_1};) + elseif(ARCH MATCHES "([0-9]+)\-virtual") + set(internal_ptx ${internal_ptx} ${CMAKE_MATCH_1};) + elseif(ARCH MATCHES "([0-9]+)") + set(internal_bin ${internal_bin} ${CMAKE_MATCH_1};) + set(internal_ptx ${internal_ptx} ${CMAKE_MATCH_1};) + endif() + endforeach() + if(internal_bin OR internal_ptx) + unset(CUDA_ARCH_BIN CACHE) + unset(CUDA_ARCH_PTX CACHE) + endif() + if(internal_ptx) + set(CUDA_ARCH_PTX ${internal_ptx} CACHE STRING "Specify 'virtual' PTX architectures to build PTX intermediate code for (see https://docs.opencv.org/5.x/d2/dbc/cuda_intro.html)") + endif() + if(internal_bin) + set(CUDA_ARCH_BIN ${internal_bin} CACHE STRING "Specify 'real' GPU architectures to build binaries for, BIN(PTX) format is supported (see https://docs.opencv.org/5.x/d2/dbc/cuda_intro.html)") + endif() + set(CMAKE_CUDA_ARCHITECTURES "" PARENT) + unset(CUDA_GENERATION CACHE) +endfunction() + +macro(ocv_initialize_nvidia_device_generations) + OCV_OPTION(CUDA_ENABLE_DEPRECATED_GENERATION "Enable deprecated generations in the list" OFF) + set(_generations "Maxwell" "Pascal" "Volta" "Turing" "Ampere" "Lovelace" "Hopper") + if(CUDA_ENABLE_DEPRECATED_GENERATION) + set(_generations "Fermi" "${_generations}") + set(_generations "Kepler" "${_generations}") + endif() + set(_arch_fermi "2.0") + set(_arch_kepler "3.0;3.5;3.7") + set(_arch_maxwell "5.0;5.2") + set(_arch_pascal "6.0;6.1") + set(_arch_volta "7.0") + set(_arch_turing "7.5") + set(_arch_ampere "8.0;8.6") + set(_arch_lovelace "8.9") + set(_arch_hopper "9.0") + if(NOT CMAKE_CROSSCOMPILING) + list(APPEND _generations "Auto") + endif() + set(CUDA_GENERATION "" CACHE STRING "Build CUDA device code only for specific GPU architecture. Leave empty to build for all architectures (see https://docs.opencv.org/5.x/d2/dbc/cuda_intro.html).") + if( CMAKE_VERSION VERSION_GREATER "2.8" ) + set_property( CACHE CUDA_GENERATION PROPERTY STRINGS "" ${_generations} ) + endif() + + if(CUDA_GENERATION) + if(NOT ";${_generations};" MATCHES ";${CUDA_GENERATION};") + string(REPLACE ";" ", " _generations "${_generations}") + message(FATAL_ERROR "ERROR: ${_generations} Generations are supported.") + endif() + unset(CUDA_ARCH_BIN CACHE) + unset(CUDA_ARCH_PTX CACHE) + endif() +endmacro() + +macro(ocv_set_cuda_detection_nvcc_flags cuda_host_compiler_var) + if(OPENCV_CUDA_DETECTION_NVCC_FLAGS MATCHES "-ccbin") + # already specified by user + elseif(${cuda_host_compiler_var} AND EXISTS "${${cuda_host_compiler_var}}") + get_filename_component(c_compiler_realpath "${CMAKE_C_COMPILER}" REALPATH) + # C compiler doesn't work with --run option, forcing C++ compiler instead + if(${cuda_host_compiler_var} STREQUAL c_compiler_realpath OR ${cuda_host_compiler_var} STREQUAL CMAKE_C_COMPILER) + if(DEFINED CMAKE_CXX_COMPILER) + get_filename_component(cxx_compiler_realpath "${CMAKE_CXX_COMPILER}" REALPATH) + LIST(APPEND OPENCV_CUDA_DETECTION_NVCC_FLAGS -ccbin "${cxx_compiler_realpath}") + else() + message(STATUS "CUDA: CMAKE_CXX_COMPILER is not available. You may need to specify ${cuda_host_compiler_var}.") + endif() + else() + LIST(APPEND OPENCV_CUDA_DETECTION_NVCC_FLAGS -ccbin "${${cuda_host_compiler_var}}") + endif() + elseif(WIN32 AND CMAKE_LINKER) # Workaround for VS cl.exe not being in the env. path + get_filename_component(host_compiler_bindir ${CMAKE_LINKER} DIRECTORY) + LIST(APPEND OPENCV_CUDA_DETECTION_NVCC_FLAGS -ccbin "${host_compiler_bindir}") + else() + if(${cuda_host_compiler_var}) + message(STATUS "CUDA: ${cuda_host_compiler_var}='${cuda_host_compiler}' is not valid, autodetection may not work. Specify OPENCV_CUDA_DETECTION_NVCC_FLAGS with -ccbin option for fix that") + endif() + endif() +endmacro() + +macro(ocv_filter_available_architecture nvcc_executable result_list) + set(__cache_key_check "${ARGN} : ${nvcc_executable} ${OPENCV_CUDA_DETECTION_NVCC_FLAGS}") + if(DEFINED OPENCV_CACHE_CUDA_SUPPORTED_CC AND OPENCV_CACHE_CUDA_SUPPORTED_CC_check STREQUAL __cache_key_check) + set(${result_list} "${OPENCV_CACHE_CUDA_SUPPORTED_CC}") + else() + set(CC_LIST ${ARGN}) + foreach(target_arch ${CC_LIST}) + string(REPLACE "." "" target_arch_short "${target_arch}") + set(NVCC_OPTION "-gencode;arch=compute_${target_arch_short},code=sm_${target_arch_short}") + set(_cmd "${nvcc_executable}" ${OPENCV_CUDA_DETECTION_NVCC_FLAGS} ${NVCC_OPTION} "${OpenCV_SOURCE_DIR}/cmake/checks/OpenCVDetectCudaArch.cu" --compile) + execute_process( + COMMAND ${_cmd} + WORKING_DIRECTORY "${CMAKE_BINARY_DIR}${CMAKE_FILES_DIRECTORY}/CMakeTmp/" + RESULT_VARIABLE _nvcc_res + OUTPUT_VARIABLE _nvcc_out + ERROR_VARIABLE _nvcc_err + #ERROR_QUIET + OUTPUT_STRIP_TRAILING_WHITESPACE + ) + if(OPENCV_CMAKE_CUDA_DEBUG) + message(WARNING "COMMAND: ${_cmd}") + message(STATUS "Result: ${_nvcc_res}") + message(STATUS "Out: ${_nvcc_out}") + message(STATUS "Err: ${_nvcc_err}") + endif() + if(_nvcc_res EQUAL 0) + LIST(APPEND ${result_list} "${target_arch}") + endif() + endforeach() + string(STRIP "${${result_list}}" ${result_list}) + if(" ${${result_list}}" STREQUAL " ") + message(WARNING "CUDA: Autodetection arch list is empty. Please enable OPENCV_CMAKE_CUDA_DEBUG=1 and check/specify OPENCV_CUDA_DETECTION_NVCC_FLAGS variable") + endif() + + # cache detected values + set(OPENCV_CACHE_CUDA_SUPPORTED_CC ${${result_list}} CACHE INTERNAL "") + set(OPENCV_CACHE_CUDA_SUPPORTED_CC_check "${__cache_key_check}" CACHE INTERNAL "") + endif() +endmacro() + +macro(ocv_detect_native_cuda_arch nvcc_executable status output) + set(OPENCV_CUDA_DETECT_ARCHS_COMMAND "${nvcc_executable}" ${OPENCV_CUDA_DETECTION_NVCC_FLAGS} "${OpenCV_SOURCE_DIR}/cmake/checks/OpenCVDetectCudaArch.cu" "--run") + set(__cache_key_check "${OPENCV_CUDA_DETECT_ARCHS_COMMAND}") + if(DEFINED OPENCV_CACHE_CUDA_ACTIVE_CC AND OPENCV_CACHE_CUDA_ACTIVE_CC_check STREQUAL __cache_key_check) + set(${output} "${OPENCV_CACHE_CUDA_ACTIVE_CC}") + set(${status} 0) + else() + execute_process( + COMMAND ${OPENCV_CUDA_DETECT_ARCHS_COMMAND} + WORKING_DIRECTORY "${CMAKE_BINARY_DIR}${CMAKE_FILES_DIRECTORY}/CMakeTmp/" + RESULT_VARIABLE ${status} + OUTPUT_VARIABLE _nvcc_out + ERROR_VARIABLE _nvcc_err + ERROR_QUIET + OUTPUT_STRIP_TRAILING_WHITESPACE + ) + if(OPENCV_CMAKE_CUDA_DEBUG) + message(WARNING "COMMAND: ${OPENCV_CUDA_DETECT_ARCHS_COMMAND}") + message(STATUS "Result: ${${status}}") + message(STATUS "Out: ${_nvcc_out}") + message(STATUS "Err: ${_nvcc_err}") + endif() + string(REGEX REPLACE ".*\n" "" ${output} "${_nvcc_out}") #Strip leading warning messages, if any + + if(${status} EQUAL 0) + # cache detected values + set(OPENCV_CACHE_CUDA_ACTIVE_CC ${${output}} CACHE INTERNAL "") + set(OPENCV_CACHE_CUDA_ACTIVE_CC_check "${__cache_key_check}" CACHE INTERNAL "") + endif() + endif() +endmacro() + +macro(ocv_set_cuda_arch_bin_and_ptx nvcc_executable) + ocv_initialize_nvidia_device_generations() + set(__cuda_arch_ptx ${CUDA_ARCH_PTX}) + if(CUDA_GENERATION STREQUAL "Fermi") + set(__cuda_arch_bin ${_arch_fermi}) + elseif(CUDA_GENERATION STREQUAL "Kepler") + set(__cuda_arch_bin ${_arch_kepler}) + elseif(CUDA_GENERATION STREQUAL "Maxwell") + set(__cuda_arch_bin ${_arch_maxwell}) + elseif(CUDA_GENERATION STREQUAL "Pascal") + set(__cuda_arch_bin ${_arch_pascal}) + elseif(CUDA_GENERATION STREQUAL "Volta") + set(__cuda_arch_bin ${_arch_volta}) + elseif(CUDA_GENERATION STREQUAL "Turing") + set(__cuda_arch_bin ${_arch_turing}) + elseif(CUDA_GENERATION STREQUAL "Ampere") + set(__cuda_arch_bin ${_arch_ampere}) + elseif(CUDA_GENERATION STREQUAL "Lovelace") + set(__cuda_arch_bin ${_arch_lovelace}) + elseif(CUDA_GENERATION STREQUAL "Hopper") + set(__cuda_arch_bin ${_arch_hopper}) + elseif(CUDA_GENERATION STREQUAL "Auto") + ocv_detect_native_cuda_arch(${nvcc_executable} _nvcc_res _nvcc_out) + if(NOT _nvcc_res EQUAL 0) + message(STATUS "CUDA: Automatic detection of CUDA generation failed. Going to build for all known architectures") + else() + string(REGEX MATCHALL "[0-9]+\\.[0-9]" __cuda_arch_bin "${_nvcc_out}") + endif() + elseif(CUDA_ARCH_BIN) + message(STATUS "CUDA: Using CUDA_ARCH_BIN=${CUDA_ARCH_BIN}") + set(__cuda_arch_bin ${CUDA_ARCH_BIN}) + endif() + + if(NOT DEFINED __cuda_arch_bin AND NOT DEFINED __cuda_arch_ptx) + if(ARM) + set(__cuda_arch_bin "3.2") + set(__cuda_arch_ptx "") + elseif(AARCH64) + if(NOT CMAKE_CROSSCOMPILING) + ocv_detect_native_cuda_arch(${nvcc_executable} _nvcc_res _nvcc_out) + else() + set(_nvcc_res -1) # emulate error, see below + endif() + if(NOT _nvcc_res EQUAL 0) + message(STATUS "CUDA: Automatic detection of CUDA generation failed. Going to build for all known architectures") + # TX1 (5.3) TX2 (6.2) Xavier (7.2) V100 (7.0) Orin (8.7) + ocv_filter_available_architecture(${nvcc_executable} __cuda_arch_bin + 5.3 + 6.2 + 7.2 + 7.0 + 8.7 + ) + else() + set(__cuda_arch_bin "${_nvcc_out}") + endif() + set(__cuda_arch_ptx "") + else() + ocv_filter_available_architecture(${nvcc_executable} __cuda_arch_bin + ${_arch_fermi} + ${_arch_kepler} + ${_arch_maxwell} + ${_arch_pascal} + ${_arch_volta} + ${_arch_turing} + ${_arch_ampere} + ${_arch_lovelace} + ${_arch_hopper} + ) + list(GET __cuda_arch_bin -1 __cuda_arch_ptx) + endif() + endif() + + set(CUDA_ARCH_BIN ${__cuda_arch_bin} CACHE STRING "Specify 'real' GPU architectures to build binaries for, BIN(PTX) format is supported (see https://docs.opencv.org/5.x/d2/dbc/cuda_intro.html)") + set(CUDA_ARCH_PTX ${__cuda_arch_ptx} CACHE STRING "Specify 'virtual' PTX architectures to build PTX intermediate code for (see https://docs.opencv.org/5.x/d2/dbc/cuda_intro.html)") + string(REGEX REPLACE "\\." "" ARCH_BIN_NO_POINTS "${CUDA_ARCH_BIN}") + string(REGEX REPLACE "\\." "" ARCH_PTX_NO_POINTS "${CUDA_ARCH_PTX}") + + # Check if user specified 1.0/2.1 compute capability: we don't support it + macro(ocv_wipeout_deprecated_cc target_cc) + if(" ${CUDA_ARCH_BIN} ${CUDA_ARCH_PTX}" MATCHES " ${target_cc}") + message(SEND_ERROR "CUDA: ${target_cc} compute capability is not supported - exclude it from ARCH/PTX list and re-run CMake") + endif() + endmacro() + ocv_wipeout_deprecated_cc("1.0") + ocv_wipeout_deprecated_cc("2.1") +endmacro() + +macro(ocv_set_nvcc_threads_for_vs) + # Tell NVCC the maximum number of threads to be used to execute the compilation steps in parallel + # (option --threads was introduced in version 11.2) + if(NOT CUDA_VERSION VERSION_LESS "11.2") + if(CMAKE_GENERATOR MATCHES "Visual Studio" AND NOT $ENV{CMAKE_BUILD_PARALLEL_LEVEL} STREQUAL "") + set(CUDA_NVCC_FLAGS ${CUDA_NVCC_FLAGS} "--threads=$ENV{CMAKE_BUILD_PARALLEL_LEVEL}") + endif() + endif() +endmacro() + +macro(ocv_cuda_filter_options) + foreach(var CMAKE_CXX_FLAGS CMAKE_CXX_FLAGS_RELEASE CMAKE_CXX_FLAGS_DEBUG) + set(${var}_backup_in_cuda_compile_ "${${var}}") + + if (CV_CLANG) + # we remove -Winconsistent-missing-override and -Qunused-arguments + # just in case we are compiling CUDA with gcc but OpenCV with clang + string(REPLACE "-Winconsistent-missing-override" "" ${var} "${${var}}") + string(REPLACE "-Qunused-arguments" "" ${var} "${${var}}") + endif() + + # we remove /EHa as it generates warnings under windows + string(REPLACE "/EHa" "" ${var} "${${var}}") + + # we remove -ggdb3 flag as it leads to preprocessor errors when compiling CUDA files (CUDA 4.1) + string(REPLACE "-ggdb3" "" ${var} "${${var}}") + + # we remove -Wsign-promo as it generates warnings under linux + string(REPLACE "-Wsign-promo" "" ${var} "${${var}}") + + # we remove -Wno-sign-promo as it generates warnings under linux + string(REPLACE "-Wno-sign-promo" "" ${var} "${${var}}") + + # we remove -Wno-delete-non-virtual-dtor because it's used for C++ compiler + # but NVCC uses C compiler by default + string(REPLACE "-Wno-delete-non-virtual-dtor" "" ${var} "${${var}}") + + # we remove -frtti because it's used for C++ compiler + # but NVCC uses C compiler by default + string(REPLACE "-frtti" "" ${var} "${${var}}") + + string(REPLACE "-fvisibility-inlines-hidden" "" ${var} "${${var}}") + + # cc1: warning: command line option '-Wsuggest-override' is valid for C++/ObjC++ but not for C + string(REPLACE "-Wsuggest-override" "" ${var} "${${var}}") + + # issue: #11552 (from OpenCVCompilerOptions.cmake) + string(REGEX REPLACE "-Wimplicit-fallthrough(=[0-9]+)? " "" ${var} "${${var}}") + + # removal of custom specified options + if(OPENCV_CUDA_NVCC_FILTEROUT_OPTIONS) + foreach(__flag ${OPENCV_CUDA_NVCC_FILTEROUT_OPTIONS}) + string(REPLACE "${__flag}" "" ${var} "${${var}}") + endforeach() + endif() + endforeach() +endmacro() + +macro(ocv_nvcc_flags) + if(BUILD_SHARED_LIBS) + set(CUDA_NVCC_FLAGS ${CUDA_NVCC_FLAGS} -Xcompiler=-DCVAPI_EXPORTS) + endif() + + if(UNIX OR APPLE) + set(CUDA_NVCC_FLAGS ${CUDA_NVCC_FLAGS} -Xcompiler=-fPIC) + endif() + if(APPLE) + set(CUDA_NVCC_FLAGS ${CUDA_NVCC_FLAGS} -Xcompiler=-fno-finite-math-only) + endif() + + if(WIN32 AND NOT (CUDA_VERSION VERSION_LESS "11.2")) + set(CUDA_NVCC_FLAGS ${CUDA_NVCC_FLAGS} -Xcudafe --display_error_number --diag-suppress 1394,1388) + endif() + + if(CMAKE_CROSSCOMPILING AND (ARM OR AARCH64)) + set(CUDA_NVCC_FLAGS ${CUDA_NVCC_FLAGS} -Xlinker --unresolved-symbols=ignore-in-shared-libs) + endif() + + # disabled because of multiple warnings during building nvcc auto generated files + if(CV_GCC AND CMAKE_CXX_COMPILER_VERSION VERSION_GREATER "4.6.0") + ocv_warnings_disable(CMAKE_CXX_FLAGS -Wunused-but-set-variable) + endif() +endmacro() + +macro(ocv_apply_cuda_stub_workaround cuda_driver_library_path) + # details: https://github.com/NVIDIA/nvidia-docker/issues/775 + if(" ${cuda_driver_library_path}" MATCHES "/stubs/libcuda.so" AND NOT OPENCV_SKIP_CUDA_STUB_WORKAROUND) + set(CUDA_STUB_ENABLED_LINK_WORKAROUND 1) + if(EXISTS "${cuda_driver_library_path}" AND NOT OPENCV_SKIP_CUDA_STUB_WORKAROUND_RPATH_LINK) + set(CUDA_STUB_TARGET_PATH "${CMAKE_BINARY_DIR}${CMAKE_FILES_DIRECTORY}/") + execute_process(COMMAND ${CMAKE_COMMAND} -E create_symlink "${cuda_driver_library_path}" "${CUDA_STUB_TARGET_PATH}/libcuda.so.1" + RESULT_VARIABLE CUDA_STUB_SYMLINK_RESULT) + if(NOT CUDA_STUB_SYMLINK_RESULT EQUAL 0) + execute_process(COMMAND ${CMAKE_COMMAND} -E copy_if_different "${cuda_driver_library_path}" "${CUDA_STUB_TARGET_PATH}/libcuda.so.1" + RESULT_VARIABLE CUDA_STUB_COPY_RESULT) + if(NOT CUDA_STUB_COPY_RESULT EQUAL 0) + set(CUDA_STUB_ENABLED_LINK_WORKAROUND 0) + endif() + endif() + if(CUDA_STUB_ENABLED_LINK_WORKAROUND) + set(CMAKE_EXE_LINKER_FLAGS "${CMAKE_EXE_LINKER_FLAGS} -Wl,-rpath-link,\"${CUDA_STUB_TARGET_PATH}\"") + endif() + else() + set(CMAKE_EXE_LINKER_FLAGS "${CMAKE_EXE_LINKER_FLAGS} -Wl,--allow-shlib-undefined") + endif() + if(NOT CUDA_STUB_ENABLED_LINK_WORKAROUND) + message(WARNING "CUDA: Workaround for stubs/libcuda.so.1 is not applied") + endif() + endif() +endmacro() + +macro(ocv_check_cuda_delayed_load cuda_toolkit_root_dir) + if(MSVC AND CUDA_ENABLE_DELAYLOAD) + set(DELAYFLAGS "delayimp.lib") + file(GLOB CUDA_DLLS "${cuda_toolkit_root_dir}/bin/*.dll") + foreach(d ${CUDA_DLLS}) + cmake_path(GET "d" FILENAME DLL_NAME) + if(NOT ${DLL_NAME} MATCHES "cudart") + set(DELAYFLAGS "${DELAYFLAGS} /DELAYLOAD:${DLL_NAME}") + endif() + endforeach() + set(DELAYFLAGS "${DELAYFLAGS} /DELAYLOAD:nvcuda.dll /DELAYLOAD:nvml.dll /IGNORE:4199") + set(CMAKE_EXE_LINKER_FLAGS "${CMAKE_EXE_LINKER_FLAGS} ${DELAYFLAGS}") + set(CMAKE_MODULE_LINKER_FLAGS "${CMAKE_MODULE_LINKER_FLAGS} ${DELAYFLAGS}") + set(CMAKE_SHARED_LINKER_FLAGS "${CMAKE_SHARED_LINKER_FLAGS} ${DELAYFLAGS}") + endif() +endmacro() diff --git a/cmake/OpenCVDetectCXXCompiler.cmake b/cmake/OpenCVDetectCXXCompiler.cmake index 4295f96568..1743aca11f 100644 --- a/cmake/OpenCVDetectCXXCompiler.cmake +++ b/cmake/OpenCVDetectCXXCompiler.cmake @@ -28,22 +28,8 @@ if(NOT DEFINED CV_GCC AND CMAKE_CXX_COMPILER_ID MATCHES "GNU") endif() if(NOT DEFINED CV_CLANG AND CMAKE_CXX_COMPILER_ID MATCHES "Clang") # Clang or AppleClang (see CMP0025) set(CV_CLANG 1) - set(CMAKE_COMPILER_IS_CLANGCXX 1) # TODO next release: remove this - set(CMAKE_COMPILER_IS_CLANGCC 1) # TODO next release: remove this endif() -function(access_CMAKE_COMPILER_IS_CLANGCXX) - if(NOT OPENCV_SUPPRESS_DEPRECATIONS) - message(WARNING "DEPRECATED: CMAKE_COMPILER_IS_CLANGCXX support is deprecated in OpenCV. - Consider using: - - CV_GCC # GCC - - CV_CLANG # Clang or AppleClang (see CMP0025) -") - endif() -endfunction() -variable_watch(CMAKE_COMPILER_IS_CLANGCXX access_CMAKE_COMPILER_IS_CLANGCXX) -variable_watch(CMAKE_COMPILER_IS_CLANGCC access_CMAKE_COMPILER_IS_CLANGCXX) - # ---------------------------------------------------------------------------- # Detect Intel ICC compiler diff --git a/cmake/OpenCVDetectDirectML.cmake b/cmake/OpenCVDetectDirectML.cmake new file mode 100644 index 0000000000..0fc71eca03 --- /dev/null +++ b/cmake/OpenCVDetectDirectML.cmake @@ -0,0 +1,13 @@ +if(WIN32) + try_compile(__VALID_DIRECTML + "${OpenCV_BINARY_DIR}" + "${OpenCV_SOURCE_DIR}/cmake/checks/directml.cpp" + LINK_LIBRARIES d3d12 dxcore directml + OUTPUT_VARIABLE TRY_OUT + ) + if(NOT __VALID_DIRECTML) + message(STATUS "No support for DirectML (d3d12, dxcore, directml libs are required)") + return() + endif() + set(HAVE_DIRECTML ON) +endif() diff --git a/cmake/OpenCVDetectPython.cmake b/cmake/OpenCVDetectPython.cmake index d9bb5bb9a6..e798789b51 100644 --- a/cmake/OpenCVDetectPython.cmake +++ b/cmake/OpenCVDetectPython.cmake @@ -171,7 +171,7 @@ if(NOT ${found}) endif() endif() - if(NOT ANDROID AND NOT IOS) + if(NOT ANDROID AND NOT IOS AND NOT XROS) if(CMAKE_HOST_UNIX) execute_process(COMMAND ${_executable} -c "from sysconfig import *; print(get_path('purelib'))" RESULT_VARIABLE _cvpy_process @@ -233,7 +233,7 @@ if(NOT ${found}) OUTPUT_STRIP_TRAILING_WHITESPACE) endif() endif() - endif(NOT ANDROID AND NOT IOS) + endif(NOT ANDROID AND NOT IOS AND NOT XROS) endif() # Export return values diff --git a/cmake/OpenCVFindCANN.cmake b/cmake/OpenCVFindCANN.cmake index e1cd054a37..913c1887e7 100644 --- a/cmake/OpenCVFindCANN.cmake +++ b/cmake/OpenCVFindCANN.cmake @@ -46,6 +46,17 @@ if(CANN_INSTALL_DIR) set(HAVE_CANN OFF) return() endif() + # * libacl_op_compiler.so + set(lib_acl_op_compiler "${CANN_INSTALL_DIR}/lib64") + find_library(found_lib_acl_op_compiler NAMES acl_op_compiler PATHS ${lib_acl_op_compiler} NO_DEFAULT_PATH) + if(found_lib_acl_op_compiler) + set(lib_acl_op_compiler ${found_lib_acl_op_compiler}) + message(STATUS "CANN: libacl_op_compiler.so is found at ${lib_acl_op_compiler}") + else() + message(STATUS "CANN: Missing libacl_op_compiler.so. Turning off HAVE_CANN") + set(HAVE_CANN OFF) + return() + endif() # * libgraph.so set(lib_graph "${CANN_INSTALL_DIR}/compiler/lib64") find_library(found_lib_graph NAMES graph PATHS ${lib_graph} NO_DEFAULT_PATH) @@ -90,6 +101,7 @@ if(CANN_INSTALL_DIR) set(libs_cann "") list(APPEND libs_cann ${lib_ascendcl}) + list(APPEND libs_cann ${lib_acl_op_compiler}) list(APPEND libs_cann ${lib_opsproto}) list(APPEND libs_cann ${lib_graph}) list(APPEND libs_cann ${lib_ge_compiler}) diff --git a/cmake/OpenCVFindLibsGUI.cmake b/cmake/OpenCVFindLibsGUI.cmake index 79758fa813..31c19b1da7 100644 --- a/cmake/OpenCVFindLibsGUI.cmake +++ b/cmake/OpenCVFindLibsGUI.cmake @@ -77,7 +77,7 @@ endif(WITH_OPENGL) # --- Cocoa --- if(APPLE) - if(NOT IOS AND CV_CLANG) + if(NOT IOS AND NOT XROS AND CV_CLANG) set(HAVE_COCOA YES) endif() endif() diff --git a/cmake/OpenCVFindLibsGrfmt.cmake b/cmake/OpenCVFindLibsGrfmt.cmake index e544f78eaa..a76a9ed44a 100644 --- a/cmake/OpenCVFindLibsGrfmt.cmake +++ b/cmake/OpenCVFindLibsGrfmt.cmake @@ -34,7 +34,7 @@ if(NOT ZLIB_FOUND) set(ZLIB_INCLUDE_DIRS ${ZLIB_INCLUDE_DIR}) set(ZLIB_LIBRARIES ${ZLIB_LIBRARY}) - ocv_parse_header2(ZLIB "${${ZLIB_LIBRARY}_SOURCE_DIR}/zlib.h" ZLIB_VERSION) + ocv_parse_header_version(ZLIB "${${ZLIB_LIBRARY}_SOURCE_DIR}/zlib.h" ZLIB_VERSION) endif() # --- libavif (optional) --- @@ -202,6 +202,7 @@ if(WITH_OPENJPEG) endif() else() set(HAVE_OPENJPEG YES) + set(OPENJPEG_VERSION "${OPENJPEG_MAJOR_VERSION}.${OPENJPEG_MINOR_VERSION}.${OPENJPEG_BUILD_VERSION}") message(STATUS "Found system OpenJPEG: ${OPENJPEG_LIBRARIES} " "(found version \"${OPENJPEG_VERSION}\")") endif() @@ -232,16 +233,38 @@ if(WITH_JASPER AND NOT HAVE_OPENJPEG) endif() if(WITH_SPNG) - set(SPNG_LIBRARY libspng CACHE INTERNAL "") - set(SPNG_LIBRARIES ${SPNG_LIBRARY}) - add_subdirectory("${OpenCV_SOURCE_DIR}/3rdparty/libspng") - set(SPNG_INCLUDE_DIR "${${SPNG_LIBRARY}_SOURCE_DIR}" CACHE INTERNAL "") - set(SPNG_DEFINITIONS "") - ocv_parse_header("${SPNG_INCLUDE_DIR}/spng.h" SPNG_VERSION_LINES SPNG_VERSION_MAJOR SPNG_VERSION_MINOR SPNG_VERSION_PATCH) + if(BUILD_SPNG) + ocv_clear_vars(PNG_FOUND) + else() + # CMakeConfig bug in SPNG, include is missing there in version 0.7.4 and older + # See https://github.com/randy408/libspng/pull/264 + include(CMakeFindDependencyMacro) + find_package(SPNG QUIET) + if(SPNG_FOUND) + set(SPNG_LIBRARY "spng::spng" CACHE INTERNAL "") + set(SPNG_LIBRARIES ${SPNG_LIBRARY}) + else() + if(PkgConfig_FOUND) + pkg_check_modules(SPNG QUIET spng) + endif() + endif() + if(SPNG_FOUND) + set(HAVE_SPNG YES) + message(STATUS "imgcodecs: PNG codec will use SPNG, version: ${SPNG_VERSION}") + endif() + endif() + if(NOT SPNG_FOUND) + set(SPNG_LIBRARY libspng CACHE INTERNAL "") + set(SPNG_LIBRARIES ${SPNG_LIBRARY}) + add_subdirectory("${OpenCV_SOURCE_DIR}/3rdparty/libspng") + set(SPNG_INCLUDE_DIR "${${SPNG_LIBRARY}_SOURCE_DIR}" CACHE INTERNAL "") + set(SPNG_DEFINITIONS "") + ocv_parse_header("${SPNG_INCLUDE_DIR}/spng.h" SPNG_VERSION_LINES SPNG_VERSION_MAJOR SPNG_VERSION_MINOR SPNG_VERSION_PATCH) - set(HAVE_SPNG YES) - set(SPNG_VERSION "${SPNG_VERSION_MAJOR}.${SPNG_VERSION_MINOR}.${SPNG_VERSION_PATCH}") - message(STATUS "imgcodecs: PNG codec will use SPNG, version: ${SPNG_VERSION} ") + set(HAVE_SPNG YES) + set(SPNG_VERSION "${SPNG_VERSION_MAJOR}.${SPNG_VERSION_MINOR}.${SPNG_VERSION_PATCH}") + message(STATUS "imgcodecs: PNG codec will use SPNG, version: ${SPNG_VERSION} ") + endif() endif() # --- libpng (optional, should be searched after zlib) --- @@ -250,31 +273,21 @@ if(NOT HAVE_SPNG AND WITH_PNG) ocv_clear_vars(PNG_FOUND) else() ocv_clear_internal_cache_vars(PNG_LIBRARY PNG_INCLUDE_DIR) - include(FindPNG) - if(PNG_FOUND) - include(CheckIncludeFile) - check_include_file("${PNG_PNG_INCLUDE_DIR}/libpng/png.h" HAVE_LIBPNG_PNG_H) - if(HAVE_LIBPNG_PNG_H) - ocv_parse_header("${PNG_PNG_INCLUDE_DIR}/libpng/png.h" PNG_VERSION_LINES PNG_LIBPNG_VER_MAJOR PNG_LIBPNG_VER_MINOR PNG_LIBPNG_VER_RELEASE) - else() - ocv_parse_header("${PNG_PNG_INCLUDE_DIR}/png.h" PNG_VERSION_LINES PNG_LIBPNG_VER_MAJOR PNG_LIBPNG_VER_MINOR PNG_LIBPNG_VER_RELEASE) - endif() - endif() + find_package(PNG QUIET) endif() if(NOT PNG_FOUND) - ocv_clear_vars(PNG_LIBRARY PNG_LIBRARIES PNG_INCLUDE_DIR PNG_PNG_INCLUDE_DIR HAVE_LIBPNG_PNG_H PNG_DEFINITIONS) + ocv_clear_vars(PNG_LIBRARY PNG_LIBRARIES PNG_INCLUDE_DIR PNG_DEFINITIONS) set(PNG_LIBRARY libpng CACHE INTERNAL "") set(PNG_LIBRARIES ${PNG_LIBRARY}) add_subdirectory("${OpenCV_SOURCE_DIR}/3rdparty/libpng") set(PNG_INCLUDE_DIR "${${PNG_LIBRARY}_SOURCE_DIR}" CACHE INTERNAL "") set(PNG_DEFINITIONS "") - ocv_parse_header("${PNG_INCLUDE_DIR}/png.h" PNG_VERSION_LINES PNG_LIBPNG_VER_MAJOR PNG_LIBPNG_VER_MINOR PNG_LIBPNG_VER_RELEASE) + ocv_parse_header_version(PNG "${PNG_INCLUDE_DIR}/png.h" PNG_LIBPNG_VER_STRING) endif() set(HAVE_PNG YES) - set(PNG_VERSION "${PNG_LIBPNG_VER_MAJOR}.${PNG_LIBPNG_VER_MINOR}.${PNG_LIBPNG_VER_RELEASE}") endif() diff --git a/cmake/OpenCVFindLibsPerf.cmake b/cmake/OpenCVFindLibsPerf.cmake index a191afde58..5f72a67d89 100644 --- a/cmake/OpenCVFindLibsPerf.cmake +++ b/cmake/OpenCVFindLibsPerf.cmake @@ -40,7 +40,11 @@ endif() # --- CUDA --- if(WITH_CUDA) - include("${OpenCV_SOURCE_DIR}/cmake/OpenCVDetectCUDA.cmake") + if(ENABLE_CUDA_FIRST_CLASS_LANGUAGE) + include("${OpenCV_SOURCE_DIR}/cmake/OpenCVDetectCUDALanguage.cmake") + else() + include("${OpenCV_SOURCE_DIR}/cmake/OpenCVDetectCUDA.cmake") + endif() if(NOT HAVE_CUDA) message(WARNING "OpenCV is not able to find/configure CUDA SDK (required by WITH_CUDA). CUDA support will be disabled in OpenCV build. diff --git a/cmake/OpenCVGenAndroidMK.cmake b/cmake/OpenCVGenAndroidMK.cmake index ebf25d72a6..bd17cc7fea 100644 --- a/cmake/OpenCVGenAndroidMK.cmake +++ b/cmake/OpenCVGenAndroidMK.cmake @@ -48,7 +48,7 @@ if(ANDROID) string(REPLACE "opencv_" "" OPENCV_MODULES_CONFIGMAKE "${OPENCV_MODULES_CONFIGMAKE}") if(BUILD_FAT_JAVA_LIB) - set(OPENCV_LIBS_CONFIGMAKE java4) + set(OPENCV_LIBS_CONFIGMAKE java5) else() set(OPENCV_LIBS_CONFIGMAKE "${OPENCV_MODULES_CONFIGMAKE}") endif() diff --git a/cmake/OpenCVGenConfig.cmake b/cmake/OpenCVGenConfig.cmake index 838852c4e7..df48ae0848 100644 --- a/cmake/OpenCVGenConfig.cmake +++ b/cmake/OpenCVGenConfig.cmake @@ -12,7 +12,11 @@ else() endif() if(HAVE_CUDA) - ocv_cmake_configure("${CMAKE_CURRENT_LIST_DIR}/templates/OpenCVConfig-CUDA.cmake.in" CUDA_CONFIGCMAKE @ONLY) + if(ENABLE_CUDA_FIRST_CLASS_LANGUAGE) + ocv_cmake_configure("${CMAKE_CURRENT_LIST_DIR}/templates/OpenCVConfig-CUDALanguage.cmake.in" CUDA_CONFIGCMAKE @ONLY) + else() + ocv_cmake_configure("${CMAKE_CURRENT_LIST_DIR}/templates/OpenCVConfig-CUDA.cmake.in" CUDA_CONFIGCMAKE @ONLY) + endif() endif() if(ANDROID) @@ -120,7 +124,6 @@ endif() if(ANDROID) ocv_gen_config("${CMAKE_BINARY_DIR}/unix-install" "abi-${ANDROID_NDK_ABI_NAME}" "OpenCVConfig.root-ANDROID.cmake.in") - install(FILES "${OpenCV_SOURCE_DIR}/platforms/android/android.toolchain.cmake" DESTINATION "${OPENCV_CONFIG_INSTALL_PATH}" COMPONENT dev) endif() # -------------------------------------------------------------------------------------------- diff --git a/cmake/OpenCVGenInfoPlist.cmake b/cmake/OpenCVGenInfoPlist.cmake index 105087907f..f1a6926d07 100644 --- a/cmake/OpenCVGenInfoPlist.cmake +++ b/cmake/OpenCVGenInfoPlist.cmake @@ -13,6 +13,14 @@ if(IOS) configure_file("${OpenCV_SOURCE_DIR}/platforms/ios/Info.plist.in" "${CMAKE_BINARY_DIR}/ios/Info.plist") endif() +elseif(XROS) + if(APPLE_FRAMEWORK AND DYNAMIC_PLIST) + configure_file("${OpenCV_SOURCE_DIR}/platforms/ios/Info.Dynamic.plist.in" + "${CMAKE_BINARY_DIR}/visionos/Info.plist") + else() + configure_file("${OpenCV_SOURCE_DIR}/platforms/ios/Info.plist.in" + "${CMAKE_BINARY_DIR}/visionos/Info.plist") + endif() elseif(APPLE) configure_file("${OpenCV_SOURCE_DIR}/platforms/osx/Info.plist.in" "${CMAKE_BINARY_DIR}/osx/Info.plist") diff --git a/cmake/OpenCVGenPkgconfig.cmake b/cmake/OpenCVGenPkgconfig.cmake index e4c05ed3d9..52e18d8fc3 100644 --- a/cmake/OpenCVGenPkgconfig.cmake +++ b/cmake/OpenCVGenPkgconfig.cmake @@ -1,4 +1,4 @@ -if(MSVC OR IOS) +if(MSVC OR IOS OR XROS) return() endif() diff --git a/cmake/OpenCVModule.cmake b/cmake/OpenCVModule.cmake index 2085877cbc..7542b069fa 100644 --- a/cmake/OpenCVModule.cmake +++ b/cmake/OpenCVModule.cmake @@ -1002,7 +1002,7 @@ macro(_ocv_create_module) INTERFACE ${OPENCV_MODULE_${the_module}_DEPS_EXT} ) ocv_target_link_libraries(${the_module} PRIVATE ${OPENCV_LINKER_LIBS} ${OPENCV_HAL_LINKER_LIBS} ${IPP_LIBS} ${ARGN}) - if (HAVE_CUDA) + if (NOT ENABLE_CUDA_FIRST_CLASS_LANGUAGE AND HAVE_CUDA) ocv_target_link_libraries(${the_module} PRIVATE ${CUDA_LIBRARIES} ${CUDA_npp_LIBRARY}) endif() diff --git a/cmake/OpenCVUtils.cmake b/cmake/OpenCVUtils.cmake index 04a4441c92..91a12c1565 100644 --- a/cmake/OpenCVUtils.cmake +++ b/cmake/OpenCVUtils.cmake @@ -1430,6 +1430,18 @@ macro(ocv_parse_header2 LIBNAME HDR_PATH VARNAME) endif() endmacro() +# set ${LIBNAME}_VERSION_STRING to ${LIBVER} without quotes +macro(ocv_parse_header_version LIBNAME HDR_PATH LIBVER) + ocv_clear_vars(${LIBNAME}_VERSION_STRING) + set(${LIBNAME}_H "") + if(EXISTS "${HDR_PATH}") + file(STRINGS "${HDR_PATH}" ${LIBNAME}_H REGEX "^#define[ \t]+${LIBVER}[ \t]+\"[^\"]*\".*$" LIMIT_COUNT 1) + endif() + if(${LIBNAME}_H) + string(REGEX REPLACE "^.*[ \t]${LIBVER}[ \t]+\"([0-9\.]+)\"$" "\\1" ${LIBNAME}_VERSION_STRING "${${LIBNAME}_H}") + endif() +endmacro() + ################################################################################################ # short command to setup source group function(ocv_source_group group) @@ -1545,13 +1557,23 @@ function(_ocv_append_target_includes target) endif() endfunction() +macro(ocv_add_cuda_compile_flags) + ocv_cuda_compile_flags() + target_compile_options(${target} PRIVATE $<$: ${CUDA_NVCC_FLAGS} + "-Xcompiler=${CMAKE_CXX_FLAGS_CUDA} $<$:${CMAKE_CXX_FLAGS_DEBUG_CUDA}> \ + $<$:${CMAKE_CXX_FLAGS_RELEASE_CUDA}>" >) +endmacro() + function(ocv_add_executable target) add_executable(${target} ${ARGN}) + if(ENABLE_CUDA_FIRST_CLASS_LANGUAGE AND HAVE_CUDA) + ocv_add_cuda_compile_flags() + endif() _ocv_append_target_includes(${target}) endfunction() function(ocv_add_library target) - if(HAVE_CUDA AND ARGN MATCHES "\\.cu") + if(NOT ENABLE_CUDA_FIRST_CLASS_LANGUAGE AND HAVE_CUDA AND ARGN MATCHES "\\.cu") ocv_include_directories(${CUDA_INCLUDE_DIRS}) ocv_cuda_compile(cuda_objs ${ARGN}) set(OPENCV_MODULE_${target}_CUDA_OBJECTS ${cuda_objs} CACHE INTERNAL "Compiled CUDA object files") @@ -1559,12 +1581,16 @@ function(ocv_add_library target) add_library(${target} ${ARGN} ${cuda_objs}) + if(ENABLE_CUDA_FIRST_CLASS_LANGUAGE AND HAVE_CUDA) + ocv_add_cuda_compile_flags() + endif() + if(APPLE_FRAMEWORK AND BUILD_SHARED_LIBS) message(STATUS "Setting Apple target properties for ${target}") set(CMAKE_SHARED_LIBRARY_RUNTIME_C_FLAG 1) - if(IOS AND NOT MAC_CATALYST) + if((IOS OR XROS) AND NOT MAC_CATALYST) set(OPENCV_APPLE_INFO_PLIST "${CMAKE_BINARY_DIR}/ios/Info.plist") else() set(OPENCV_APPLE_INFO_PLIST "${CMAKE_BINARY_DIR}/osx/Info.plist") diff --git a/cmake/android/android_gradle_projects.cmake b/cmake/android/android_gradle_projects.cmake index 0a8b9f4b60..7c80d5777e 100644 --- a/cmake/android/android_gradle_projects.cmake +++ b/cmake/android/android_gradle_projects.cmake @@ -1,8 +1,8 @@ # https://developer.android.com/studio/releases/gradle-plugin -set(ANDROID_GRADLE_PLUGIN_VERSION "3.2.1" CACHE STRING "Android Gradle Plugin version") +set(ANDROID_GRADLE_PLUGIN_VERSION "7.3.1" CACHE STRING "Android Gradle Plugin version") message(STATUS "Android Gradle Plugin version: ${ANDROID_GRADLE_PLUGIN_VERSION}") -set(KOTLIN_PLUGIN_VERSION "1.4.10" CACHE STRING "Kotlin Plugin version") +set(KOTLIN_PLUGIN_VERSION "1.5.20" CACHE STRING "Kotlin Plugin version") message(STATUS "Kotlin Plugin version: ${KOTLIN_PLUGIN_VERSION}") if(BUILD_KOTLIN_EXTENSIONS) @@ -13,16 +13,16 @@ else() set(KOTLIN_STD_LIB "" CACHE STRING "Kotlin Standard Library dependency") endif() -set(GRADLE_VERSION "5.6.4" CACHE STRING "Gradle version") +set(GRADLE_VERSION "7.6.3" CACHE STRING "Gradle version") message(STATUS "Gradle version: ${GRADLE_VERSION}") -set(ANDROID_COMPILE_SDK_VERSION "26" CACHE STRING "Android compileSdkVersion") +set(ANDROID_COMPILE_SDK_VERSION "31" CACHE STRING "Android compileSdkVersion") if(ANDROID_NATIVE_API_LEVEL GREATER 21) set(ANDROID_MIN_SDK_VERSION "${ANDROID_NATIVE_API_LEVEL}" CACHE STRING "Android minSdkVersion") else() set(ANDROID_MIN_SDK_VERSION "21" CACHE STRING "Android minSdkVersion") endif() -set(ANDROID_TARGET_SDK_VERSION "26" CACHE STRING "Android minSdkVersion") +set(ANDROID_TARGET_SDK_VERSION "31" CACHE STRING "Android minSdkVersion") set(ANDROID_BUILD_BASE_DIR "${OpenCV_BINARY_DIR}/opencv_android" CACHE INTERNAL "") set(ANDROID_TMP_INSTALL_BASE_DIR "${CMAKE_BINARY_DIR}${CMAKE_FILES_DIRECTORY}/install/opencv_android") @@ -109,21 +109,39 @@ if(NOT OPENCV_SKIP_ANDROID_FORCE_CMAKE) get_filename_component(_CMAKE_INSTALL_DIR "${CMAKE_ROOT}" PATH) get_filename_component(_CMAKE_INSTALL_DIR "${_CMAKE_INSTALL_DIR}" PATH) endif() - ocv_update_file("${ANDROID_BUILD_BASE_DIR}/local.properties" "cmake.dir=${_CMAKE_INSTALL_DIR}") + ocv_update_file("${ANDROID_BUILD_BASE_DIR}/local.properties" "cmake.dir=${_CMAKE_INSTALL_DIR}\nndk.dir=${ANDROID_NDK}") endif() file(WRITE "${ANDROID_BUILD_BASE_DIR}/settings.gradle" " +gradle.ext { + // possible options: 'maven_central', 'maven_local', 'sdk_path' + opencv_source = 'sdk_path' +} + include ':opencv' ") file(WRITE "${ANDROID_TMP_INSTALL_BASE_DIR}/settings.gradle" " rootProject.name = 'opencv_samples' -def opencvsdk='../' -//def opencvsdk='/' -//println opencvsdk -include ':opencv' -project(':opencv').projectDir = new File(opencvsdk + '/sdk') +gradle.ext { + // possible options: 'maven_central', 'maven_local', 'sdk_path' + opencv_source = 'sdk_path' +} + +if (gradle.opencv_source == 'maven_local') { + gradle.ext { + opencv_maven_path = '' + } +} + +if (gradle.opencv_source == 'sdk_path') { + def opencvsdk = '../' + //def opencvsdk='/' + //println opencvsdk + include ':opencv' + project(':opencv').projectDir = new File(opencvsdk + '/sdk') +} ") ocv_check_environment_variables(OPENCV_GRADLE_VERBOSE_OPTIONS) @@ -207,9 +225,20 @@ include ':${__dir}' configure_file("${path}/build.gradle.in" "${ANDROID_TMP_INSTALL_BASE_DIR}/${__dir}/build.gradle" @ONLY) install(FILES "${ANDROID_TMP_INSTALL_BASE_DIR}/${__dir}/build.gradle" DESTINATION "${ANDROID_INSTALL_SAMPLES_DIR}/${__dir}" COMPONENT samples) - file(APPEND "${ANDROID_TMP_INSTALL_BASE_DIR}/settings.gradle" " + # HACK: AAR packages generated from current OpenCV project has incomple prefab part + # and cannot be used for native linkage against OpenCV. + # Alternative way to build AAR: https://github.com/opencv/opencv/blob/5.x/platforms/android/build_java_shared_aar.py + if("${__dir}" STREQUAL "tutorial-2-mixedprocessing" OR "${__dir}" STREQUAL "tutorial-4-opencl") + file(APPEND "${ANDROID_TMP_INSTALL_BASE_DIR}/settings.gradle" " +if (gradle.opencv_source == 'sdk_path') { + include ':${__dir}' +} +") + else() + file(APPEND "${ANDROID_TMP_INSTALL_BASE_DIR}/settings.gradle" " include ':${__dir}' ") + endif() endmacro() diff --git a/cmake/checks/cpu_fp16.cpp b/cmake/checks/cpu_fp16.cpp index f12cb10f4d..6bffc4792a 100644 --- a/cmake/checks/cpu_fp16.cpp +++ b/cmake/checks/cpu_fp16.cpp @@ -11,7 +11,7 @@ int test() _mm_storel_epi64((__m128i*)dst, v_dst); return (int)dst[0]; } -#elif defined __GNUC__ && (defined __arm__ || defined __aarch64__) +#elif (defined __GNUC__ && (defined __arm__ || defined __aarch64__)) || (defined _MSC_VER && defined _M_ARM64) #include "arm_neon.h" int test() { diff --git a/cmake/checks/cpu_neon_bf16.cpp b/cmake/checks/cpu_neon_bf16.cpp index c18d63c579..a9045d7117 100644 --- a/cmake/checks/cpu_neon_bf16.cpp +++ b/cmake/checks/cpu_neon_bf16.cpp @@ -1,4 +1,4 @@ -#if defined __GNUC__ && (defined __arm__ || defined __aarch64__) +#if (defined __GNUC__ && (defined __arm__ || defined __aarch64__)) || (defined _MSC_VER && defined _M_ARM64) #include #include "arm_neon.h" diff --git a/cmake/checks/cpu_neon_dotprod.cpp b/cmake/checks/cpu_neon_dotprod.cpp index 4f39c50659..74f44a1832 100644 --- a/cmake/checks/cpu_neon_dotprod.cpp +++ b/cmake/checks/cpu_neon_dotprod.cpp @@ -1,6 +1,6 @@ #include -#if defined __GNUC__ && (defined __arm__ || defined __aarch64__) +#if (defined __GNUC__ && (defined __arm__ || defined __aarch64__)) || (defined _MSC_VER && defined _M_ARM64) #include "arm_neon.h" int test() { diff --git a/cmake/checks/cpu_neon_fp16.cpp b/cmake/checks/cpu_neon_fp16.cpp index 20fbab25d5..bba5b97026 100644 --- a/cmake/checks/cpu_neon_fp16.cpp +++ b/cmake/checks/cpu_neon_fp16.cpp @@ -1,6 +1,6 @@ #include -#if defined __GNUC__ && (defined __arm__ || defined __aarch64__) +#if (defined __GNUC__ && (defined __arm__ || defined __aarch64__)) || (defined _MSC_VER && defined _M_ARM64) #include "arm_neon.h" float16x8_t vld1q_as_f16(const float* src) diff --git a/cmake/checks/directml.cpp b/cmake/checks/directml.cpp new file mode 100644 index 0000000000..1cf62b8fad --- /dev/null +++ b/cmake/checks/directml.cpp @@ -0,0 +1,38 @@ +#include + +#include +#include +#include +#include +#include +#include +#include +#include + +int main(int /*argc*/, char** /*argv*/) +{ + IDXCoreAdapterFactory* factory; + DXCoreCreateAdapterFactory(__uuidof(IDXCoreAdapterFactory), (void**)&factory); + + IDXCoreAdapterList* adapterList; + const GUID dxGUIDs[] = { DXCORE_ADAPTER_ATTRIBUTE_D3D12_CORE_COMPUTE }; + factory->CreateAdapterList(ARRAYSIZE(dxGUIDs), dxGUIDs, __uuidof(IDXCoreAdapterList), (void**)&adapterList); + + IDXCoreAdapter* adapter; + adapterList->GetAdapter(0u, __uuidof(IDXCoreAdapter), (void**)&adapter); + + D3D_FEATURE_LEVEL d3dFeatureLevel = D3D_FEATURE_LEVEL_1_0_CORE; + ID3D12Device* d3d12Device = NULL; + D3D12CreateDevice((IUnknown*)adapter, d3dFeatureLevel, __uuidof(ID3D11Device), (void**)&d3d12Device); + + D3D12_COMMAND_LIST_TYPE commandQueueType = D3D12_COMMAND_LIST_TYPE_COMPUTE; + ID3D12CommandQueue* cmdQueue; + D3D12_COMMAND_QUEUE_DESC commandQueueDesc = {}; + commandQueueDesc.Type = commandQueueType; + + d3d12Device->CreateCommandQueue(&commandQueueDesc, __uuidof(ID3D12CommandQueue), (void**)&cmdQueue); + IDMLDevice* dmlDevice; + DMLCreateDevice(d3d12Device, DML_CREATE_DEVICE_FLAG_NONE, IID_PPV_ARGS(&dmlDevice)); + + return 0; +} \ No newline at end of file diff --git a/cmake/templates/OpenCVConfig-CUDALanguage.cmake.in b/cmake/templates/OpenCVConfig-CUDALanguage.cmake.in new file mode 100644 index 0000000000..259141006a --- /dev/null +++ b/cmake/templates/OpenCVConfig-CUDALanguage.cmake.in @@ -0,0 +1,31 @@ +# Version Compute Capability from which OpenCV has been compiled is remembered +set(OpenCV_COMPUTE_CAPABILITIES "@OpenCV_CUDA_CC@") + +set(OpenCV_CUDA_VERSION "@CUDA_VERSION_STRING@") +set(OpenCV_USE_CUBLAS "@HAVE_CUBLAS@") +set(OpenCV_USE_CUFFT "@HAVE_CUFFT@") +set(OpenCV_USE_NVCUVID "@HAVE_NVCUVID@") +set(OpenCV_USE_NVCUVENC "@HAVE_NVCUVENC@") +set(OpenCV_CUDNN_VERSION "@CUDNN_VERSION@") +set(OpenCV_USE_CUDNN "@HAVE_CUDNN@") +set(ENABLE_CUDA_FIRST_CLASS_LANGUAGE ON) + +if(NOT CUDAToolkit_FOUND) + if(NOT CMAKE_VERSION VERSION_LESS 3.18) + if(UNIX AND NOT CMAKE_CUDA_COMPILER AND NOT CUDAToolkit_ROOT) + message(STATUS "Checking for CUDAToolkit in default location (/usr/local/cuda)") + set(CUDA_PATH "/usr/local/cuda" CACHE INTERNAL "") + set(ENV{CUDA_PATH} ${CUDA_PATH}) + endif() + find_package(CUDAToolkit ${OpenCV_CUDA_VERSION} EXACT REQUIRED) + else() + message(FATAL_ERROR "Using OpenCV compiled with CUDA as first class language requires CMake \>= 3.18.") + endif() +else() + if(CUDAToolkit_FOUND) + set(CUDA_VERSION_STRING ${CUDAToolkit_VERSION}) + endif() + if(NOT CUDA_VERSION_STRING VERSION_EQUAL OpenCV_CUDA_VERSION) + message(FATAL_ERROR "OpenCV library was compiled with CUDA ${OpenCV_CUDA_VERSION} support. Please, use the same version or rebuild OpenCV with CUDA ${CUDA_VERSION_STRING}") + endif() +endif() diff --git a/cmake/templates/cvconfig.h.in b/cmake/templates/cvconfig.h.in index 30b40547ee..057a92843e 100644 --- a/cmake/templates/cvconfig.h.in +++ b/cmake/templates/cvconfig.h.in @@ -75,9 +75,6 @@ /* IJG JPEG codec */ #cmakedefine HAVE_JPEG -/* libpng/png.h needs to be included */ -#cmakedefine HAVE_LIBPNG_PNG_H - /* GDCM DICOM codec */ #cmakedefine HAVE_GDCM diff --git a/doc/LICENSE_CHANGE_NOTICE.txt b/doc/LICENSE_CHANGE_NOTICE.txt index 0df3eca671..c2711d4671 100644 --- a/doc/LICENSE_CHANGE_NOTICE.txt +++ b/doc/LICENSE_CHANGE_NOTICE.txt @@ -1,4 +1,4 @@ -Starting from OpenCV 4.5-pre (2020 August) OpenCV has changed the license from BSD to Apache 2. See https://opencv.org/opencv-is-to-change-the-license-to-apache-2/ and https://github.com/opencv/opencv/wiki/OE-32.--Change-OpenCV-License-to-Apache-2 for details. +Starting from OpenCV 4.5-pre (2020 August) OpenCV has changed the license from BSD to Apache 2. See https://opencv.org/blog/opencv-is-to-change-the-license-to-apache-2/ and https://github.com/opencv/opencv/wiki/OE-32.--Change-OpenCV-License-to-Apache-2 for details. Here is the original OpenCV license: ------------------------------------------------------------------------------------ diff --git a/doc/js_tutorials/js_setup/js_setup/js_setup.markdown b/doc/js_tutorials/js_setup/js_setup/js_setup.markdown index 5b0e65b250..87a32a78cb 100644 --- a/doc/js_tutorials/js_setup/js_setup/js_setup.markdown +++ b/doc/js_tutorials/js_setup/js_setup/js_setup.markdown @@ -54,7 +54,7 @@ repository](https://github.com/opencv/opencv.git). ### Obtaining the Latest Stable OpenCV Version -- Go to our [releases page](http://opencv.org/releases.html). +- Go to our [releases page](https://opencv.org/releases). - Download the source archive and unpack it. ### Obtaining the Cutting-edge OpenCV from the Git Repository diff --git a/doc/opencv.bib b/doc/opencv.bib index 62c038de08..ada690497d 100644 --- a/doc/opencv.bib +++ b/doc/opencv.bib @@ -1391,3 +1391,85 @@ YEAR = {2016}, MONTH = {October}, } +@inproceedings{BarathGCRANSAC, + author = {Barath, Daniel and Matas, Jiri}, + title = {Graph-Cut RANSAC}, + booktitle = {Proceedings of the IEEE Conference on Computer Vision and Pattern Recognition (CVPR)}, + month = {June}, + year = {2018} +} +@misc{barath2019progressive, + title={Progressive NAPSAC: sampling from gradually growing neighborhoods}, + author={Barath, Daniel and Ivashechkin, Maksym and Matas, Jiri}, + year={2019}, + eprint={1906.02295}, + archivePrefix={arXiv}, + primaryClass={cs.CV} +} +@inproceedings{BarathMAGSAC, + author = {Barath, Daniel and Noskova, Jana and Ivashechkin, Maksym and Matas, Jiri}, + title = {MAGSAC++, a Fast, Reliable and Accurate Robust Estimator}, + booktitle = {Proceedings of the IEEE/CVF Conference on Computer Vision and Pattern Recognition (CVPR)}, + month = {June}, + year = {2020} +} +@inproceedings{ChumPROSAC, + title = {Matching with {PROSAC} - Progressive Sampling Consensus}, + author = {Chum, Ondrej and Matas, Jiri}, + booktitle = {Proceedings of the IEEE/CVF Conference on Computer Vision and Pattern Recognition (CVPR)}, + year = {2005} +} +@inproceedings{ChumLORANSAC, + title = {Locally Optimized {RANSAC}}, + author = {Chum, Ondrej and Matas, Jiri and Kittler, Josef}, + booktitle = {DAGM}, + year = {2003} +} +@inproceedings{ChumEpipolar, + author={Chum, Ondrej and Werner, Tomas and Matas, Jiri}, + booktitle={Proceedings of the 17th International Conference on Pattern Recognition. ICPR 2004}, + title={Epipolar geometry estimation via RANSAC benefits from the oriented epipolar constraint}, + year={2004}, + volume={1}, + pages={112-115 Vol.1} +} +@inproceedings{ChumDominant, + title = {Epipolar Geometry Estimation Unaffected by the Dominant Plane}, + author = {Chum, Ondrej and Werner, Tomas and Matas, Jiri.}, + booktitle = {Proceedings of the IEEE/CVF Conference on Computer Vision and Pattern Recognition (CVPR)}, + year = {2005} +} +@article{FischlerRANSAC, + author = {Fischler, Martin A. and Bolles, Robert C.}, + title = {Random Sample Consensus: A Paradigm for Model Fitting with Applications to Image Analysis and Automated Cartography}, + year = {1981}, + publisher = {Association for Computing Machinery}, + volume = {24}, + number = {6}, + month = {jun}, + pages = {381–395}, + numpages = {15} +} +@article{Matas2005RandomizedRW, + title={Randomized RANSAC with sequential probability ratio test}, + author={Matas, Jiri and Chum, Ondrej}, + journal={Tenth IEEE International Conference on Computer Vision (ICCV) Volume 1}, + year={2005}, + volume={2}, + pages={1727-1732 Vol. 2} +} +@inproceedings{MyattNAPSAC, + author = {Myatt, D. and Torr, Philip and Nasuto, Slawomir and Bishop, John and Craddock, R.}, + year = {2002}, + booktitle = {Proceedings of the British Machine Vision Conference (BMVC)}, + title = {NAPSAC: High Noise, High Dimensional Robust Estimation - it's in the Bag} +} +@article{SteweniusRecent, + author = {Stewenius, Henrik and Engels, Christopher and Nister, David}, + year = {2006}, + month = {06}, + pages = {284-294}, + title = {Recent developments on direct relative orientation}, + volume = {60}, + journal = {ISPRS Journal of Photogrammetry and Remote Sensing} +} diff --git a/doc/pattern_tools/test_charuco_board.py b/doc/pattern_tools/test_charuco_board.py index 93a3e68d3c..9eca7b1480 100644 --- a/doc/pattern_tools/test_charuco_board.py +++ b/doc/pattern_tools/test_charuco_board.py @@ -32,7 +32,7 @@ class aruco_objdetect_test(NewOpenCVTests): aruco_dict = cv.aruco.getPredefinedDictionary(aruco_type[aruco_type_i]) board = cv.aruco.CharucoBoard((cols, rows), square_size, marker_size, aruco_dict) charuco_detector = cv.aruco.CharucoDetector(board) - from_cv_img = board.generateImage((cols*square_size*10, rows*square_size*10)) + from_cv_img = board.generateImage((cols*square_size, rows*square_size)) #draw desk using svg fd1, filesvg = tempfile.mkstemp(prefix="out", suffix=".svg") @@ -48,15 +48,20 @@ class aruco_objdetect_test(NewOpenCVTests): pm.make_charuco_board() pm.save() drawing = svg2rlg(filesvg) - renderPM.drawToFile(drawing, filepng, fmt='PNG', dpi=720) + renderPM.drawToFile(drawing, filepng, fmt='PNG', dpi=72) from_svg_img = cv.imread(filepng) + _charucoCorners, _charuco_ids_svg, marker_corners_svg, marker_ids_svg = charuco_detector.detectBoard(from_svg_img) + _charucoCorners, _charuco_ids_cv, marker_corners_cv, marker_ids_cv = charuco_detector.detectBoard(from_cv_img) + marker_corners_svg_map, marker_corners_cv_map = {}, {} + for i in range(len(marker_ids_svg)): + marker_corners_svg_map[int(marker_ids_svg[i])] = marker_corners_svg[i] + for i in range(len(marker_ids_cv)): + marker_corners_cv_map[int(marker_ids_cv[i])] = marker_corners_cv[i] - #test - _charucoCorners, _charucoIds, markerCorners_svg, markerIds_svg = charuco_detector.detectBoard(from_svg_img) - _charucoCorners, _charucoIds, markerCorners_cv, markerIds_cv = charuco_detector.detectBoard(from_cv_img) - - np.testing.assert_allclose(markerCorners_svg, markerCorners_cv, 0.1, 0.1) - np.testing.assert_allclose(markerIds_svg, markerIds_cv, 0.1, 0.1) + for key_svg in marker_corners_svg_map.keys(): + marker_svg = marker_corners_svg_map[key_svg] + marker_cv = marker_corners_cv_map[key_svg] + np.testing.assert_allclose(marker_svg, marker_cv, 0.1, 0.1) finally: if os.path.exists(filesvg): os.remove(filesvg) @@ -85,7 +90,7 @@ class aruco_objdetect_test(NewOpenCVTests): aruco_dict = cv.aruco.getPredefinedDictionary(aruco_type) board = cv.aruco.CharucoBoard((cols, rows), square_size, marker_size, aruco_dict) charuco_detector = cv.aruco.CharucoDetector(board) - from_cv_img = board.generateImage((cols*square_size*10, rows*square_size*10)) + from_cv_img = board.generateImage((cols*square_size, rows*square_size)) #draw desk using svg fd1, filesvg = tempfile.mkstemp(prefix="out", suffix=".svg") @@ -100,15 +105,22 @@ class aruco_objdetect_test(NewOpenCVTests): pm.make_charuco_board() pm.save() drawing = svg2rlg(filesvg) - renderPM.drawToFile(drawing, filepng, fmt='PNG', dpi=720) + renderPM.drawToFile(drawing, filepng, fmt='PNG', dpi=72) from_svg_img = cv.imread(filepng) #test - _charucoCorners, _charucoIds, markerCorners_svg, markerIds_svg = charuco_detector.detectBoard(from_svg_img) - _charucoCorners, _charucoIds, markerCorners_cv, markerIds_cv = charuco_detector.detectBoard(from_cv_img) + _charucoCorners, _charuco_ids_svg, marker_corners_svg, marker_ids_svg = charuco_detector.detectBoard(from_svg_img) + _charucoCorners, _charuco_ids_cv, marker_corners_cv, marker_ids_cv = charuco_detector.detectBoard(from_cv_img) + marker_corners_svg_map, marker_corners_cv_map = {}, {} + for i in range(len(marker_ids_svg)): + marker_corners_svg_map[int(marker_ids_svg[i])] = marker_corners_svg[i] + for i in range(len(marker_ids_cv)): + marker_corners_cv_map[int(marker_ids_cv[i])] = marker_corners_cv[i] - np.testing.assert_allclose(markerCorners_svg, markerCorners_cv, 0.1, 0.1) - np.testing.assert_allclose(markerIds_svg, markerIds_cv, 0.1, 0.1) + for key_svg in marker_corners_svg_map.keys(): + marker_svg = marker_corners_svg_map[key_svg] + marker_cv = marker_corners_cv_map[key_svg] + np.testing.assert_allclose(marker_svg, marker_cv, 0.1, 0.1) finally: if os.path.exists(filesvg): os.remove(filesvg) diff --git a/doc/py_tutorials/py_bindings/py_bindings_basics/py_bindings_basics.markdown b/doc/py_tutorials/py_bindings/py_bindings_basics/py_bindings_basics.markdown index 001952deca..f7a29b4805 100644 --- a/doc/py_tutorials/py_bindings/py_bindings_basics/py_bindings_basics.markdown +++ b/doc/py_tutorials/py_bindings/py_bindings_basics/py_bindings_basics.markdown @@ -79,9 +79,12 @@ Functions are extended using `CV_EXPORTS_W` macro. An example is shown below. @code{.cpp} CV_EXPORTS_W void equalizeHist( InputArray src, OutputArray dst ); @endcode -Header parser can understand the input and output arguments from keywords like -InputArray, OutputArray etc. But sometimes, we may need to hardcode inputs and outputs. For that, -macros like `CV_OUT`, `CV_IN_OUT` etc. are used. +Header parser can understand the input and output arguments from keywords like InputArray, +OutputArray etc. The arguments semantics are kept in Python: anything that is modified in C++ +will be modified in Python. And vice-versa read-only Python objects cannot be modified by OpenCV, +if they are used as output. Such situation will cause Python exception. Sometimes, the parameters +that are passed by reference in C++ may be used as input, output or both. +Macros `CV_OUT`, `CV_IN_OUT` allow to solve ambiguity and generate correct bindings. @code{.cpp} CV_EXPORTS_W void minEnclosingCircle( InputArray points, CV_OUT Point2f& center, CV_OUT float& radius ); diff --git a/doc/py_tutorials/py_gui/py_video_display/py_video_display.markdown b/doc/py_tutorials/py_gui/py_video_display/py_video_display.markdown index d60b846245..5819653fa0 100644 --- a/doc/py_tutorials/py_gui/py_video_display/py_video_display.markdown +++ b/doc/py_tutorials/py_gui/py_video_display/py_video_display.markdown @@ -111,7 +111,7 @@ frames per second (fps) and frame size should be passed. And the last one is the `True`, the encoder expect color frame, otherwise it works with grayscale frame. [FourCC](http://en.wikipedia.org/wiki/FourCC) is a 4-byte code used to specify the video codec. The -list of available codes can be found in [fourcc.org](http://www.fourcc.org/codecs.php). It is +list of available codes can be found in [fourcc.org](https://fourcc.org/codecs.php). It is platform dependent. The following codecs work fine for me. - In Fedora: DIVX, XVID, MJPG, X264, WMV1, WMV2. (XVID is more preferable. MJPG results in high diff --git a/doc/py_tutorials/py_ml/py_knn/py_knn_understanding/py_knn_understanding.markdown b/doc/py_tutorials/py_ml/py_knn/py_knn_understanding/py_knn_understanding.markdown index 9f76e0f808..5985cdd559 100644 --- a/doc/py_tutorials/py_ml/py_knn/py_knn_understanding/py_knn_understanding.markdown +++ b/doc/py_tutorials/py_ml/py_knn/py_knn_understanding/py_knn_understanding.markdown @@ -141,7 +141,7 @@ Additional Resources -------------------- 1. [NPTEL notes on Pattern Recognition, Chapter - 11](https://nptel.ac.in/courses/106/108/106108057/) + 11](https://nptel.ac.in/courses/106108057) 2. [Wikipedia article on Nearest neighbor search](https://en.wikipedia.org/wiki/Nearest_neighbor_search) 3. [Wikipedia article on k-d tree](https://en.wikipedia.org/wiki/K-d_tree) diff --git a/doc/py_tutorials/py_ml/py_svm/py_svm_basics/py_svm_basics.markdown b/doc/py_tutorials/py_ml/py_svm/py_svm_basics/py_svm_basics.markdown index c8dbe39920..55f74237e9 100644 --- a/doc/py_tutorials/py_ml/py_svm/py_svm_basics/py_svm_basics.markdown +++ b/doc/py_tutorials/py_ml/py_svm/py_svm_basics/py_svm_basics.markdown @@ -129,7 +129,6 @@ Additional Resources -------------------- -# [NPTEL notes on Statistical Pattern Recognition, Chapters - 25-29](http://www.nptel.ac.in/courses/106108057/26). - + 25-29](https://nptel.ac.in/courses/117108048) Exercises --------- diff --git a/doc/tutorials/calib3d/camera_multiview_calibration/multiview_calibration.markdown b/doc/tutorials/calib3d/camera_multiview_calibration/multiview_calibration.markdown index c9278cd4c1..287b78b97e 100644 --- a/doc/tutorials/calib3d/camera_multiview_calibration/multiview_calibration.markdown +++ b/doc/tutorials/calib3d/camera_multiview_calibration/multiview_calibration.markdown @@ -4,6 +4,7 @@ Multi-view Camera Calibration Tutorial {#tutorial_multiview_camera_calibration} @tableofcontents @prev_tutorial{tutorial_interactive_calibration} +@next_tutorial{tutorial_usac} | | | | -: | :- | diff --git a/doc/tutorials/calib3d/table_of_content_calib3d.markdown b/doc/tutorials/calib3d/table_of_content_calib3d.markdown index 00799dde80..3085456923 100644 --- a/doc/tutorials/calib3d/table_of_content_calib3d.markdown +++ b/doc/tutorials/calib3d/table_of_content_calib3d.markdown @@ -6,4 +6,5 @@ Camera calibration and 3D reconstruction (calib3d module) {#tutorial_table_of_co - @subpage tutorial_camera_calibration - @subpage tutorial_real_time_pose - @subpage tutorial_interactive_calibration -- @subpage tutorial_multiview_camera_calibration \ No newline at end of file +- @subpage tutorial_multiview_camera_calibration +- @subpage tutorial_usac diff --git a/doc/tutorials/calib3d/usac.markdown b/doc/tutorials/calib3d/usac.markdown index 300add042f..5ed87a59bb 100644 --- a/doc/tutorials/calib3d/usac.markdown +++ b/doc/tutorials/calib3d/usac.markdown @@ -1,14 +1,19 @@ ---- -author: -- Maksym Ivashechkin -bibliography: 'bibs.bib' -csl: 'acm-sigchi-proceedings.csl' -date: August 2020 -title: 'Google Summer of Code: Improvement of Random Sample Consensus in OpenCV' -... +USAC: Improvement of Random Sample Consensus in OpenCV {#tutorial_usac} +============================== + +@tableofcontents + +@prev_tutorial{tutorial_multiview_camera_calibration} + +| | | +| -: | :- | +| Original author | Maksym Ivashechkin | +| Compatibility | OpenCV >= 4.0 | + +This work was integrated as part of the Google Summer of Code (August 2020). Contribution -============ +------ The integrated part to OpenCV `3d` module is RANSAC-based universal framework USAC (`namespace usac`) written in C++. The framework includes @@ -20,25 +25,25 @@ components: 1. Sampling method: - 1. Uniform – standard RANSAC sampling proposed in \[8\] which draw + 1. Uniform – standard RANSAC sampling proposed in @cite FischlerRANSAC which draw minimal subset independently uniformly at random. *The default option in proposed framework*. - 2. PROSAC – method \[4\] that assumes input data points sorted by + 2. PROSAC – method @cite ChumPROSAC that assumes input data points sorted by quality so sampling can start from the most promising points. Correspondences for this method can be sorted e.g., by ratio of descriptor distances of the best to second match obtained from SIFT detector. *This is method is recommended to use because it can find good model and terminate much earlier*. - 3. NAPSAC – sampling method \[10\] which takes initial point + 3. NAPSAC – sampling method @cite MyattNAPSAC which takes initial point uniformly at random and the rest of points for minimal sample in the neighborhood of initial point. This is method can be potentially useful when models are localized. For example, for plane fitting. However, in practise struggles from degenerate issues and defining optimal neighborhood size. - 4. Progressive-NAPSAC – sampler \[2\] which is similar to NAPSAC, + 4. Progressive-NAPSAC – sampler @cite barath2019progressive which is similar to NAPSAC, although it starts from local and gradually converges to global sampling. This method can be quite useful if local models are expected but distribution of data can be arbitrary. The @@ -56,7 +61,7 @@ components: default option in framework*. The model might not have as many inliers as using RANSAC score, however will be more accurate. - 3. MAGSAC – threshold-free method \[3\] to compute score. Using, + 3. MAGSAC – threshold-free method @cite BarathMAGSAC to compute score. Using, although, maximum sigma (standard deviation of noise) level to marginalize residual of point over sigma. Score of the point represents likelihood of point being inlier. *Recommended option @@ -86,7 +91,7 @@ components: 4. Degeneracy: - 1. DEGENSAC – method \[7\] which for Fundamental matrix estimation + 1. DEGENSAC – method @cite ChumDominant which for Fundamental matrix estimation efficiently verifies and recovers model which has at least 5 points in minimal sample lying on the dominant plane. @@ -96,11 +101,11 @@ components: in minimal sample lie on the same side w.r.t. to any line crossing any two points in sample (does not assume reflection). - 3. Oriented epipolar constraint – method \[6\] for epipolar + 3. Oriented epipolar constraint – method @cite ChumEpipolar for epipolar geometry which verifies model (fundamental and essential matrix) to have points visible in the front of the camera. -5. SPRT verification – method \[9\] which verifies model by its +5. SPRT verification – method @cite Matas2005RandomizedRW which verifies model by its evaluation on randomly shuffled points using statistical properties given by probability of inlier, relative time for estimation, average number of output models etc. Significantly speeding up @@ -109,17 +114,17 @@ components: 6. Local Optimization: - 1. Locally Optimized RANSAC – method \[5\] that iteratively + 1. Locally Optimized RANSAC – method @cite ChumLORANSAC that iteratively improves so-far-the-best model by non-minimal estimation. *The default option in framework. This procedure is the fastest and not worse than others local optimization methods.* - 2. Graph-Cut RANSAC – method \[1\] that refine so-far-the-best + 2. Graph-Cut RANSAC – method @cite BarathGCRANSAC that refine so-far-the-best model, however, it exploits spatial coherence of the data points. *This procedure is quite precise however computationally slower.* - 3. Sigma Consensus – method \[3\] which improves model by applying + 3. Sigma Consensus – method @cite BarathMAGSAC which improves model by applying non-minimal weighted estimation, where weights are computed with the same logic as in MAGSAC score. This method is better to use together with MAGSAC score. @@ -152,7 +157,7 @@ components: 4. Essential matrix – 4 null vectors are found using Gaussian elimination. Then the solver based on Gröbner basis - described in \[11\] is used. Essential matrix can be computed + described in @cite SteweniusRecent is used. Essential matrix can be computed only if LAPACK or Eigen are installed as it requires eigen decomposition with complex @@ -180,12 +185,12 @@ sequentially. However, using default options of framework parallel RANSAC is not deterministic since it depends on how often each thread is running. The easiest way to make it deterministic is using PROSAC sampler without SPRT and Local Optimization and not for Fundamental -matrix, because they internally use random generators.\ -\ +matrix, because they internally use random generators. + For NAPSAC, Progressive NAPSAC or Graph-Cut methods is required to build a neighborhood graph. In framework there are 3 options to do it: -1. `NEIGH_FLANN_KNN` – estimate neighborhood graph using OpenCV FLANN +1. NEIGH_FLANN_KNN – estimate neighborhood graph using OpenCV FLANN K nearest-neighbors. The default value for KNN is 7. KNN method may work good for sampling but not good for GC-RANSAC. @@ -193,14 +198,14 @@ a neighborhood graph. In framework there are 3 options to do it: points which distance is less than 20 pixels. 3. `NEIGH_GRID` – for finding points’ neighborhood tiles points in - cells using hash-table. The method is described in \[2\]. Less + cells using hash-table. The method is described in @cite barath2019progressive. Less accurate than `NEIGH_FLANN_RADIUS`, although significantly faster. Note, `NEIGH_FLANN_RADIUS` and `NEIGH_FLANN_RADIUS` are not able to PnP -solver, since there are 3D object points.\ -\ -New flags: +solver, since there are 3D object points. +New flags: +------ 1. `USAC_DEFAULT` – has standard LO-RANSAC. 2. `USAC_PARALLEL` – has LO-RANSAC and RANSACs run in parallel. @@ -220,9 +225,10 @@ New flags: Every flag uses SPRT verification. And in the end the final so-far-the-best model is polished by non minimal estimation of all found -inliers.\ -\ +inliers. + A few other important parameters: +------ 1. `randomGeneratorState` – since every USAC solver is deterministic in OpenCV (i.e., for the same points and parameters returns the @@ -240,6 +246,7 @@ A few other important parameters: estimation on low number of points is faster and more robust. Samples: +------ There are three new sample files in opencv/samples directory. @@ -260,48 +267,3 @@ There are three new sample files in opencv/samples directory. 3. `essential_mat_reconstr.py` – the same functionality as in .cpp file, however instead of clustering points to plane the 3D map of object points is plot. - -References: - -1\. Daniel Barath and Jiří Matas. 2018. Graph-Cut RANSAC. In *Proceedings -of the iEEE conference on computer vision and pattern recognition*, -6733–6741. - -2\. Daniel Barath, Maksym Ivashechkin, and Jiri Matas. 2019. Progressive -NAPSAC: Sampling from gradually growing neighborhoods. *arXiv preprint -arXiv:1906.02295*. - -3\. Daniel Barath, Jana Noskova, Maksym Ivashechkin, and Jiri Matas. -2020. MAGSAC++, a fast, reliable and accurate robust estimator. In -*Proceedings of the iEEE/CVF conference on computer vision and pattern -recognition (cVPR)*. - -4\. O. Chum and J. Matas. 2005. Matching with PROSAC-progressive sample -consensus. In *Computer vision and pattern recognition*. - -5\. O. Chum, J. Matas, and J. Kittler. 2003. Locally optimized RANSAC. In -*Joint pattern recognition symposium*. - -6\. O. Chum, T. Werner, and J. Matas. 2004. Epipolar geometry estimation -via RANSAC benefits from the oriented epipolar constraint. In -*International conference on pattern recognition*. - -7\. Ondrej Chum, Tomas Werner, and Jiri Matas. 2005. Two-view geometry -estimation unaffected by a dominant plane. In *2005 iEEE computer -society conference on computer vision and pattern recognition -(cVPR’05)*, 772–779. - -8\. M. A. Fischler and R. C. Bolles. 1981. Random sample consensus: A -paradigm for model fitting with applications to image analysis and -automated cartography. *Communications of the ACM*. - -9\. Jiri Matas and Ondrej Chum. 2005. Randomized RANSAC with sequential -probability ratio test. In *Tenth iEEE international conference on -computer vision (iCCV’05) volume 1*, 1727–1732. - -10\. D. R. Myatt, P. H. S. Torr, S. J. Nasuto, J. M. Bishop, and R. -Craddock. 2002. NAPSAC: High noise, high dimensional robust estimation. -In *In bMVC02*, 458–467. - -11\. Henrik Stewénius, Christopher Engels, and David Nistér. 2006. Recent -developments on direct relative orientation. diff --git a/doc/tutorials/core/univ_intrin/univ_intrin.markdown b/doc/tutorials/core/univ_intrin/univ_intrin.markdown index a94c0126a0..0fc610ba9a 100644 --- a/doc/tutorials/core/univ_intrin/univ_intrin.markdown +++ b/doc/tutorials/core/univ_intrin/univ_intrin.markdown @@ -245,7 +245,7 @@ In the following section, we will vectorize a simple convolution function for si You may learn more about convolution from the previous tutorial. We use the same naive implementation from the previous tutorial and compare it to the vectorized version. -The full tutorial code is [here](https://github.com/opencv/opencv/tree/5.x/samples/cpp/tutorial_code/univ_intrin/univ_intrin.cpp). +The full tutorial code is [here](https://github.com/opencv/opencv/tree/5.x/samples/cpp/tutorial_code/core/univ_intrin/univ_intrin.cpp). ### Vectorizing Convolution diff --git a/doc/tutorials/dnn/dnn_android/10_opencv_dependency.png b/doc/tutorials/dnn/dnn_android/10_opencv_dependency.png deleted file mode 100644 index 03b0b597ec..0000000000 Binary files a/doc/tutorials/dnn/dnn_android/10_opencv_dependency.png and /dev/null differ diff --git a/doc/tutorials/dnn/dnn_android/1_start_new_project.png b/doc/tutorials/dnn/dnn_android/1_start_new_project.png deleted file mode 100644 index 37f1dd7182..0000000000 Binary files a/doc/tutorials/dnn/dnn_android/1_start_new_project.png and /dev/null differ diff --git a/doc/tutorials/dnn/dnn_android/2_start_new_project.png b/doc/tutorials/dnn/dnn_android/2_start_new_project.png deleted file mode 100644 index 4eeeb8ae11..0000000000 Binary files a/doc/tutorials/dnn/dnn_android/2_start_new_project.png and /dev/null differ diff --git a/doc/tutorials/dnn/dnn_android/3_start_new_project.png b/doc/tutorials/dnn/dnn_android/3_start_new_project.png deleted file mode 100644 index 8a2cd4b454..0000000000 Binary files a/doc/tutorials/dnn/dnn_android/3_start_new_project.png and /dev/null differ diff --git a/doc/tutorials/dnn/dnn_android/4_start_new_project.png b/doc/tutorials/dnn/dnn_android/4_start_new_project.png deleted file mode 100644 index d1b63f94c2..0000000000 Binary files a/doc/tutorials/dnn/dnn_android/4_start_new_project.png and /dev/null differ diff --git a/doc/tutorials/dnn/dnn_android/5_setup.png b/doc/tutorials/dnn/dnn_android/5_setup.png deleted file mode 100644 index 81b88a9f49..0000000000 Binary files a/doc/tutorials/dnn/dnn_android/5_setup.png and /dev/null differ diff --git a/doc/tutorials/dnn/dnn_android/6_run_empty_project.png b/doc/tutorials/dnn/dnn_android/6_run_empty_project.png deleted file mode 100644 index 88dc30ce99..0000000000 Binary files a/doc/tutorials/dnn/dnn_android/6_run_empty_project.png and /dev/null differ diff --git a/doc/tutorials/dnn/dnn_android/7_import_module.png b/doc/tutorials/dnn/dnn_android/7_import_module.png deleted file mode 100644 index c258f5ad25..0000000000 Binary files a/doc/tutorials/dnn/dnn_android/7_import_module.png and /dev/null differ diff --git a/doc/tutorials/dnn/dnn_android/8_import_module.png b/doc/tutorials/dnn/dnn_android/8_import_module.png deleted file mode 100644 index e2e0d3e358..0000000000 Binary files a/doc/tutorials/dnn/dnn_android/8_import_module.png and /dev/null differ diff --git a/doc/tutorials/dnn/dnn_android/9_opencv_dependency.png b/doc/tutorials/dnn/dnn_android/9_opencv_dependency.png deleted file mode 100644 index 107f4d210b..0000000000 Binary files a/doc/tutorials/dnn/dnn_android/9_opencv_dependency.png and /dev/null differ diff --git a/doc/tutorials/dnn/dnn_android/dnn_android.markdown b/doc/tutorials/dnn/dnn_android/dnn_android.markdown index 42b81e44a9..39d5e19b58 100644 --- a/doc/tutorials/dnn/dnn_android/dnn_android.markdown +++ b/doc/tutorials/dnn/dnn_android/dnn_android.markdown @@ -1,107 +1 @@ -# How to run deep networks on Android device {#tutorial_dnn_android} - -@tableofcontents - -@prev_tutorial{tutorial_dnn_openvino} -@next_tutorial{tutorial_dnn_yolo} - -| | | -| -: | :- | -| Original author | Dmitry Kurtaev | -| Compatibility | OpenCV >= 3.3 | - -## Introduction -In this tutorial you'll know how to run deep learning networks on Android device -using OpenCV deep learning module. - -Tutorial was written for the following versions of corresponding software: -- Android Studio 2.3.3 -- OpenCV 3.3.0+ - -## Requirements - -- Download and install Android Studio from https://developer.android.com/studio. - -- Get the latest pre-built OpenCV for Android release from https://github.com/opencv/opencv/releases and unpack it (for example, `opencv-5.X.Y-android-sdk.zip`). - -- Download MobileNet object detection model from https://github.com/chuanqi305/MobileNet-SSD. We need a configuration file `MobileNetSSD_deploy.prototxt` and weights `MobileNetSSD_deploy.caffemodel`. - -## Create an empty Android Studio project -- Open Android Studio. Start a new project. Let's call it `opencv_mobilenet`. -![](1_start_new_project.png) - -- Keep default target settings. -![](2_start_new_project.png) - -- Use "Empty Activity" template. Name activity as `MainActivity` with a -corresponding layout `activity_main`. -![](3_start_new_project.png) - - ![](4_start_new_project.png) - -- Wait until a project was created. Go to `Run->Edit Configurations`. -Choose `USB Device` as target device for runs. -![](5_setup.png) -Plug in your device and run the project. It should be installed and launched -successfully before we'll go next. -@note Read @ref tutorial_android_dev_intro in case of problems. - -![](6_run_empty_project.png) - -## Add OpenCV dependency - -- Go to `File->New->Import module` and provide a path to `unpacked_OpenCV_package/sdk/java`. The name of module detects automatically. -Disable all features that Android Studio will suggest you on the next window. -![](7_import_module.png) - - ![](8_import_module.png) - -- Open two files: - - 1. `AndroidStudioProjects/opencv_mobilenet/app/build.gradle` - - 2. `AndroidStudioProjects/opencv_mobilenet/openCVLibrary330/build.gradle` - - Copy both `compileSdkVersion` and `buildToolsVersion` from the first file to - the second one. - - `compileSdkVersion 14` -> `compileSdkVersion 26` - - `buildToolsVersion "25.0.0"` -> `buildToolsVersion "26.0.1"` - -- Make the project. There is no errors should be at this point. - -- Go to `File->Project Structure`. Add OpenCV module dependency. -![](9_opencv_dependency.png) - - ![](10_opencv_dependency.png) - -- Install once an appropriate OpenCV manager from `unpacked_OpenCV_package/apk` -to target device. -@code -adb install OpenCV_3.3.0_Manager_3.30_armeabi-v7a.apk -@endcode - -- Congratulations! We're ready now to make a sample using OpenCV. - -## Make a sample -Our sample will takes pictures from a camera, forwards it into a deep network and -receives a set of rectangles, class identifiers and confidence values in `[0, 1]` -range. - -- First of all, we need to add a necessary widget which displays processed -frames. Modify `app/src/main/res/layout/activity_main.xml`: -@include android/mobilenet-objdetect/res/layout/activity_main.xml - -- Put downloaded `MobileNetSSD_deploy.prototxt` and `MobileNetSSD_deploy.caffemodel` -into `app/build/intermediates/assets/debug` folder. - -- Modify `/app/src/main/AndroidManifest.xml` to enable full-screen mode, set up -a correct screen orientation and allow to use a camera. -@include android/mobilenet-objdetect/gradle/AndroidManifest.xml - -- Replace content of `app/src/main/java/org/opencv/samples/opencv_mobilenet/MainActivity.java`: -@include android/mobilenet-objdetect/src/org/opencv/samples/opencv_mobilenet/MainActivity.java - -- Launch an application and make a fun! -![](11_demo.jpg) +The page was moved to @ref tutorial_android_dnn_intro diff --git a/doc/tutorials/dnn/dnn_openvino/dnn_openvino.markdown b/doc/tutorials/dnn/dnn_openvino/dnn_openvino.markdown index 6ad78bd0d8..d8672325d4 100644 --- a/doc/tutorials/dnn/dnn_openvino/dnn_openvino.markdown +++ b/doc/tutorials/dnn/dnn_openvino/dnn_openvino.markdown @@ -2,7 +2,7 @@ OpenCV usage with OpenVINO {#tutorial_dnn_openvino} ===================== @prev_tutorial{tutorial_dnn_googlenet} -@next_tutorial{tutorial_dnn_android} +@next_tutorial{tutorial_dnn_yolo} | | | | -: | :- | diff --git a/doc/tutorials/dnn/dnn_yolo/dnn_yolo.markdown b/doc/tutorials/dnn/dnn_yolo/dnn_yolo.markdown index cf608ca18e..ab2cdd00de 100644 --- a/doc/tutorials/dnn/dnn_yolo/dnn_yolo.markdown +++ b/doc/tutorials/dnn/dnn_yolo/dnn_yolo.markdown @@ -3,7 +3,7 @@ YOLO DNNs {#tutorial_dnn_yolo} @tableofcontents -@prev_tutorial{tutorial_dnn_android} +@prev_tutorial{tutorial_dnn_openvino} @next_tutorial{tutorial_dnn_javascript} | | | diff --git a/doc/tutorials/dnn/table_of_content_dnn.markdown b/doc/tutorials/dnn/table_of_content_dnn.markdown index 08c6fc5054..bacd0e3d5f 100644 --- a/doc/tutorials/dnn/table_of_content_dnn.markdown +++ b/doc/tutorials/dnn/table_of_content_dnn.markdown @@ -3,7 +3,6 @@ Deep Neural Networks (dnn module) {#tutorial_table_of_content_dnn} - @subpage tutorial_dnn_googlenet - @subpage tutorial_dnn_openvino -- @subpage tutorial_dnn_android - @subpage tutorial_dnn_yolo - @subpage tutorial_dnn_javascript - @subpage tutorial_dnn_custom_layers diff --git a/doc/tutorials/imgproc/out_of_focus_deblur_filter/out_of_focus_deblur_filter.markdown b/doc/tutorials/imgproc/out_of_focus_deblur_filter/out_of_focus_deblur_filter.markdown index 13db710b32..d2dc68bc90 100644 --- a/doc/tutorials/imgproc/out_of_focus_deblur_filter/out_of_focus_deblur_filter.markdown +++ b/doc/tutorials/imgproc/out_of_focus_deblur_filter/out_of_focus_deblur_filter.markdown @@ -117,6 +117,6 @@ References - [SmartDeblur] - SmartDeblur site -[Digital Image Processing]: http://web.ipac.caltech.edu/staff/fmasci/home/astro_refs/Digital_Image_Processing_2ndEd.pdf +[Digital Image Processing]: http://web.ipac.caltech.edu/staff/fmasci/home/RefMaterial/ImageProc/Book_DigitalImageProcessing.pdf [Image Deblurring in Matlab]: https://www.mathworks.com/help/images/image-deblurring.html [SmartDeblur]: http://yuzhikov.com/articles/BlurredImagesRestoration1.htm diff --git a/doc/tutorials/introduction/android_binary_package/O4A_SDK.markdown b/doc/tutorials/introduction/android_binary_package/O4A_SDK.markdown deleted file mode 100644 index 2f748a23be..0000000000 --- a/doc/tutorials/introduction/android_binary_package/O4A_SDK.markdown +++ /dev/null @@ -1,255 +0,0 @@ -OpenCV4Android SDK {#tutorial_O4A_SDK} -================== - -@prev_tutorial{tutorial_android_dev_intro} -@next_tutorial{tutorial_dev_with_OCV_on_Android} - -| | | -| -: | :- | -| Original author | Vsevolod Glumov | -| Compatibility | OpenCV >= 3.0 | - -@warning -This tutorial is deprecated. - -This tutorial was designed to help you with installation and configuration of OpenCV4Android SDK. - -This guide was written with MS Windows 7 in mind, though it should work with GNU Linux and Apple Mac -OS as well. - -This tutorial assumes you have the following software installed and configured: - -- JDK -- Android SDK and NDK -- Eclipse IDE -- ADT and CDT plugins for Eclipse - -If you need help with anything of the above, you may refer to our @ref tutorial_android_dev_intro guide. - -If you encounter any error after thoroughly following these steps, feel free to contact us via -[OpenCV4Android](https://groups.google.com/group/android-opencv/) discussion group or OpenCV [Q&A -forum](https://forum.opencv.org). We'll do our best to help you out. - -General info ------------- - -OpenCV4Android SDK package enables development of Android applications with use of OpenCV library. - -The structure of package contents looks as follows: - - OpenCV-2.4.9-android-sdk - |_ apk - | |_ OpenCV_2.4.9_binary_pack_armv7a.apk - | |_ OpenCV_2.4.9_Manager_2.18_XXX.apk - | - |_ doc - |_ samples - |_ sdk - | |_ etc - | |_ java - | |_ native - | |_ 3rdparty - | |_ jni - | |_ libs - | |_ armeabi - | |_ armeabi-v7a - | |_ x86 - | - |_ LICENSE - |_ README.android - -- `sdk` folder contains OpenCV API and libraries for Android: -- `sdk/java` folder contains an Android library Eclipse project providing OpenCV Java API that can - be imported into developer's workspace; -- `sdk/native` folder contains OpenCV C++ headers (for JNI code) and native Android libraries - (\*.so and \*.a) for ARM-v5, ARM-v7a and x86 architectures; -- `sdk/etc` folder contains Haar and LBP cascades distributed with OpenCV. -- `apk` folder contains Android packages that should be installed on the target Android device to - enable OpenCV library access via OpenCV Manager API (see details below). - - On production devices that have access to Google Play Market (and Internet) these packages will - be installed from Market on the first start of an application using OpenCV Manager API. But - devkits without Market or Internet connection require this packages to be installed manually. - Install the Manager.apk and optional binary_pack.apk if it needed. See `Manager Selection` - for details. - - @note Installation from Internet is the preferable way since OpenCV team may publish updated - versions of this packages on the Market. - -- `samples` folder contains sample applications projects - and their prebuilt packages (APK). Import them into Eclipse workspace (like described below) and - browse the code to learn possible ways of OpenCV use on Android. - -- `doc` folder contains various OpenCV documentation in PDF format. It's also available online at - . - @note The most recent docs (nightly build) are at . Generally, it's more - up-to-date, but can refer to not-yet-released functionality. - @todo I'm not sure that this is the best place to talk about OpenCV Manager - -Starting from version 2.4.3 OpenCV4Android SDK uses OpenCV Manager API for library initialization. -OpenCV Manager is an Android service based solution providing the following benefits for OpenCV -applications developers: - -- Compact apk-size, since all applications use the same binaries from Manager and do not store - native libs within themselves; -- Hardware specific optimizations are automatically enabled on all supported platforms; -- Automatic updates and bug fixes; -- Trusted OpenCV library source. All packages with OpenCV are published on Google Play; - - -Manual OpenCV4Android SDK setup -------------------------------- - -### Get the OpenCV4Android SDK - --# Go to the [OpenCV download page on - SourceForge](http://sourceforge.net/projects/opencvlibrary/files/) and download - the latest available version. This tutorial is based on this package: [OpenCV-2.4.9-android-sdk.zip](http://sourceforge.net/projects/opencvlibrary/files/opencv-android/2.4.9/OpenCV-2.4.9-android-sdk.zip/download). --# Create a new folder for Android with OpenCV development. For this tutorial we have unpacked - OpenCV SDK to the `C:\Work\OpenCV4Android\` directory. - - @note Better to use a path without spaces in it. Otherwise you may have problems with ndk-build. - --# Unpack the SDK archive into the chosen directory. - - You can unpack it using any popular archiver (e.g with 7-Zip): - - ![](images/android_package_7zip.png) - - On Unix you can use the following command: - @code{.bash} - unzip ~/Downloads/OpenCV-2.4.9-android-sdk.zip - @endcode - -### Import OpenCV library and samples to the Eclipse - --# Start Eclipse and choose your workspace location. - - We recommend to start working with OpenCV for Android from a new clean workspace. A new Eclipse - workspace can for example be created in the folder where you have unpacked OpenCV4Android SDK - package: - - ![](images/eclipse_1_choose_workspace.png) - --# Import OpenCV library and samples into workspace. - - OpenCV library is packed as a ready-for-use [Android Library - Project](http://developer.android.com/guide/developing/projects/index.html#LibraryProjects). You - can simply reference it in your projects. - - Each sample included into the `OpenCV-2.4.9-android-sdk.zip` is a regular Android project that - already references OpenCV library. Follow the steps below to import OpenCV and samples into the - workspace: - - - Right click on the Package Explorer window and choose Import... option from the context - menu: - - ![](images/eclipse_5_import_command.png) - - - In the main panel select General --\> Existing Projects into Workspace and press Next - button: - - ![](images/eclipse_6_import_existing_projects.png) - - - In the Select root directory field locate your OpenCV package folder. Eclipse should - automatically locate OpenCV library and samples: - - ![](images/eclipse_7_select_projects.png) - - - Click Finish button to complete the import operation. - - @note OpenCV samples are indeed **dependent** on OpenCV library project so don't forget to import it to your workspace as well. - - After clicking Finish button Eclipse will load all selected projects into workspace, and you - have to wait some time while it is building OpenCV samples. Just give a minute to Eclipse to - complete initialization. - - ![](images/eclipse_cdt_cfg4.png) - - Once Eclipse completes build you will have the clean workspace without any build errors: - - ![](images/eclipse_10_crystal_clean.png) - -@anchor tutorial_O4A_SDK_samples -### Running OpenCV Samples - -At this point you should be able to build and run the samples. Keep in mind, that face-detection and -Tutorial 2 - Mixed Processing include some native code and require Android NDK and NDK/CDT plugin -for Eclipse to build working applications. If you haven't installed these tools, see the -corresponding section of @ref tutorial_android_dev_intro. - -**warning** - -Please consider that some samples use Android Java Camera API, which is accessible with an AVD. - -@note Recent *Android SDK tools, revision 19+* can run ARM v7a OS images but they available not for -all Android versions. - -Well, running samples from Eclipse is very simple: - -- Connect your device with adb tool from Android SDK or create an emulator with camera support. - - See [Managing Virtual Devices](http://developer.android.com/guide/developing/devices/index.html) document for help - with Android Emulator. - - See [Using Hardware Devices](http://developer.android.com/guide/developing/device.html) for - help with real devices (not emulators). - -- Select project you want to start in Package Explorer and just press Ctrl + F11 or select option - Run --\> Run from the main menu, or click Run button on the toolbar. - - @note Android Emulator can take several minutes to start. So, please, be patient. \* On the first - run Eclipse will ask you about the running mode for your application: - - ![](images/eclipse_11_run_as.png) - -- Select the Android Application option and click OK button. Eclipse will install and run the - sample. - - Chances are that on the first launch you will not have the [OpenCV - Manager](https://docs.google.com/a/itseez.com/presentation/d/1EO_1kijgBg_BsjNp2ymk-aarg-0K279_1VZRcPplSuk/present#slide=id.p) - package installed. In this case you will see the following message: - - ![](images/android_emulator_opencv_manager_fail.png) - - To get rid of the message you will need to install OpenCV Manager and the appropriate - OpenCV binary pack. Simply tap Yes if you have *Google Play Market* installed on your - device/emulator. It will redirect you to the corresponding page on *Google Play Market*. - - If you have no access to the *Market*, which is often the case with emulators - you will need to - install the packages from OpenCV4Android SDK folder manually. See `Manager Selection` for - details. - @code{.sh} - /platform-tools/adb install /apk/OpenCV_2.4.9_Manager_2.18_armv7a-neon.apk - @endcode - - @note armeabi, armv7a-neon, arm7a-neon-android8, mips and x86 stand for platform targets: - - armeabi is for ARM v5 and ARM v6 architectures with Android API 8+, - - armv7a-neon is for NEON-optimized ARM v7 with Android API 9+, - - arm7a-neon-android8 is for NEON-optimized ARM v7 with Android API 8, - - mips is for MIPS architecture with Android API 9+, - - x86 is for Intel x86 CPUs with Android API 9+. - - @note - If using hardware device for testing/debugging, run the following command to learn its CPU - architecture: - @code{.sh} - adb shell getprop ro.product.cpu.abi - @endcode - If you're using an AVD emulator, go Window \> AVD Manager to see the list of available devices. - Click Edit in the context menu of the selected device. In the window, which then pop-ups, find - the CPU field. - - @note - You may also see section `Manager Selection` for details. - - When done, you will be able to run OpenCV samples on your device/emulator seamlessly. - -- Here is Sample - image-manipulations sample, running on top of stock camera-preview of the - emulator. - - ![](images/emulator_canny.png) - -What's next ------------ - -Now, when you have your instance of OpenCV4Adroid SDK set up and configured, you may want to proceed -to using OpenCV in your own application. You can learn how to do that in a separate @ref tutorial_dev_with_OCV_on_Android tutorial. diff --git a/doc/tutorials/introduction/android_binary_package/android_dev_intro.markdown b/doc/tutorials/introduction/android_binary_package/android_dev_intro.markdown index 584f9a2f6c..ce247279fd 100644 --- a/doc/tutorials/introduction/android_binary_package/android_dev_intro.markdown +++ b/doc/tutorials/introduction/android_binary_package/android_dev_intro.markdown @@ -2,24 +2,18 @@ Introduction into Android Development {#tutorial_android_dev_intro} ===================================== @prev_tutorial{tutorial_clojure_dev_intro} -@next_tutorial{tutorial_O4A_SDK} +@next_tutorial{tutorial_dev_with_OCV_on_Android} | | | | -: | :- | -| Original author | Vsevolod Glumov | -| Compatibility | OpenCV >= 3.0 | - -@warning -This tutorial is deprecated. - +| Original author | Rostislav Vasilikhin | +| Compatibility | OpenCV >= 4.0 | This guide was designed to help you in learning Android development basics and setting up your -working environment quickly. It was written with Windows 7 in mind, though it would work with Linux -(Ubuntu), Mac OS X and any other OS supported by Android SDK. +working environment quickly. It was tested with Ubuntu 22.04 and Windows 10. If you encounter any error after thoroughly following these steps, feel free to contact us via -[OpenCV4Android](https://groups.google.com/group/android-opencv/) discussion group or OpenCV [Q&A -forum](https://forum.opencv.org). We'll do our best to help you out. +OpenCV [Forum](https://forum.opencv.org). We'll do our best to help you out. Preface ------- @@ -29,7 +23,7 @@ by Google. See the [Android home site](http://www.android.com/about/) for genera Development for Android significantly differs from development for other platforms. So before starting programming for Android we recommend you make sure that you are familiar with the following -key topis: +key topics: -# [Java](http://en.wikipedia.org/wiki/Java_(programming_language)) programming language that is the primary development technology for Android OS. Also, you can find [Oracle docs on @@ -39,369 +33,63 @@ key topis: JNI](http://docs.oracle.com/javase/7/docs/technotes/guides/jni/) useful. -# [Android Activity](http://developer.android.com/training/basics/activity-lifecycle/starting.html) and its - lifecycle, that is an essential Android API class. + life-cycle, that is an essential Android API class. -# OpenCV development will certainly require some knowledge of the [Android Camera](http://developer.android.com/guide/topics/media/camera.html) specifics. Manual environment setup for Android development ------------------------------------------------ -### Development in Java - -You need the following software to be installed in order to develop for Android in Java: - --# **Sun JDK 6** (Sun JDK 7 is also possible) - - Visit [Java SE Downloads page](http://www.oracle.com/technetwork/java/javase/downloads/) and - download an installer for your OS. - - Here is a detailed JDK (Java Development Kit) [installation - guide](http://source.android.com/source/initializing.html#installing-the-jdk) for Ubuntu and Mac - OS (only JDK sections are applicable for OpenCV) - - @note OpenJDK is not suitable for Android development, since Android SDK supports only Sun JDK. If you use Ubuntu, after installation of Sun JDK you should run the following command to set Sun java environment: - @code{.bash} - sudo update-java-alternatives --set java-6-sun - @endcode - --# **Android SDK** - - Get the latest Android SDK from - - Here is Google's [install guide](http://developer.android.com/sdk/installing.html) for the SDK. - - @note You can choose downloading **ADT Bundle package** that in addition to Android SDK Tools - includes Eclipse + ADT + NDK/CDT plugins, Android Platform-tools, the latest Android platform and - the latest Android system image for the emulator - this is the best choice for those who is setting - up Android development environment the first time! - - @note If you are running x64 version of Ubuntu Linux, then you need ia32 shared libraries for use on amd64 and ia64 systems to be installed. You can install them with the following command: - @code{.bash} - sudo apt-get install ia32-libs - @endcode - For Red Hat based systems the following command might be helpful: - @code{.bash} - sudo yum install libXtst.i386 - @endcode - --# **Android SDK components** - - You need the following SDK components to be installed: - - - *Android SDK Tools, revision 20* or newer. - - Older revisions should also work, but they are not recommended. - - - *SDK Platform Android 3.0* (API 11). - - The minimal platform supported by OpenCV Java API is **Android 2.2** (API 8). This is also - the minimum API Level required for the provided samples to run. See the - \ tag in their **AndroidManifest.xml** files. But for - successful compilation the **target** platform should be set to Android 3.0 (API 11) or - higher. It will not prevent them from running on Android 2.2. - - ![](images/android_sdk_and_avd_manager.png) - - See [Adding Platforms and - Packages](http://developer.android.com/sdk/installing/adding-packages.html) for help with - installing/updating SDK components. - --# **Eclipse IDE** - - Check the [Android SDK System Requirements](http://developer.android.com/sdk/requirements.html) - document for a list of Eclipse versions that are compatible with the Android SDK. For OpenCV - 2.4.x we recommend **Eclipse 3.7 (Indigo)** or **Eclipse 4.2 (Juno)**. They work well for OpenCV - under both Windows and Linux. - - If you have no Eclipse installed, you can get it from the [official - site](http://www.eclipse.org/downloads/). - --# **ADT plugin for Eclipse** - - These instructions are copied from [Android Developers - site](http://developer.android.com/sdk/installing/installing-adt.html), check it out in case of - any ADT-related problem. - - Assuming that you have Eclipse IDE installed, as described above, follow these steps to download - and install the ADT plugin: - - -# Start Eclipse, then select Help --\> Install New Software... - -# Click Add (in the top-right corner). - -# In the Add Repository dialog that appears, enter "ADT Plugin" for the Name and the following - URL for the Location: - - -# Click OK - - @note If you have trouble acquiring the plugin, try using "http" in the Location URL, instead of "https" (https is preferred for security reasons). - - -# In the Available Software dialog, select the checkbox next to Developer Tools and click Next. - - -# In the next window, you'll see a list of the tools to be downloaded. Click Next. - - @note If you also plan to develop native C++ code with Android NDK don't forget to enable NDK Plugins installations as well. - - ![](images/eclipse_inst_adt.png) - - -# Read and accept the license agreements, then click Finish. - - @note If you get a security warning saying that the authenticity or validity of the software can't be established, click OK. - - -# When the installation completes, restart Eclipse. - -### Native development in C++ - -You need the following software to be installed in order to develop for Android in C++: - --# **Android NDK** - - To compile C++ code for Android platform you need Android Native Development Kit (*NDK*). - - You can get the latest version of NDK from the [download - page](http://developer.android.com/tools/sdk/ndk/index.html). To install Android NDK just - extract the archive to some folder on your computer. Here are [installation - instructions](http://developer.android.com/tools/sdk/ndk/index.html#Installing). - - @note Before start you can read official Android NDK documentation which is in the Android NDK - archive, in the folder `docs/`. The main article about using Android NDK build system is in the - `ANDROID-MK.html` file. Some additional information you can find in the `APPLICATION-MK.html`, - `NDK-BUILD.html` files, and `CPU-ARM-NEON.html`, `CPLUSPLUS-SUPPORT.html`, `PREBUILTS.html`. - --# **CDT plugin for Eclipse** - - If you selected for installation the NDK plugins component of Eclipse ADT plugin (see the picture - above) your Eclipse IDE should already have CDT plugin (that means C/C++ Development Tooling). - There are several possible ways to integrate compilation of C++ code by Android NDK into Eclipse - compilation process. We recommend the approach based on Eclipse CDT(C/C++ Development Tooling) - Builder. - -Android application structure ------------------------------ - -Usually source code of an Android application has the following structure: - -- `root folder of the project/` - - `jni/` - - `libs/` - - `res/` - - `src/` - - `AndroidManifest.xml` - - `project.properties` - - `... other files ...` - -Where: - -- the `src` folder contains Java code of the application, -- the `res` folder contains resources of the application (images, xml files describing UI layout, - etc), -- the `libs` folder will contain native libraries after a successful build, -- and the `jni` folder contains C/C++ application source code and NDK's build scripts `Android.mk` - and `Application.mk` producing the native libraries, -- `AndroidManifest.xml` file presents essential information about application to the Android - system (name of the Application, name of main application's package, components of the - application, required permissions, etc). - - It can be created using Eclipse wizard or android tool from Android SDK. - -- `project.properties` is a text file containing information about target Android platform and - other build details. This file is generated by Eclipse or can be created with android tool - included in Android SDK. - -@note Both `AndroidManifest.xml` and `project.properties` files are required to compile the C++ part -of the application, since Android NDK build system relies on them. If any of these files does not -exist, compile the Java part of the project before the C++ part. - -`Android.mk` and `Application.mk` scripts ------------------------------------------ - -The script `Android.mk` usually has the following structure: -@code{.make} -LOCAL_PATH := \f$(call my-dir) - -include \f$(CLEAR_VARS) -LOCAL_MODULE := -LOCAL_SRC_FILES := - := -... - := - -include \f$(BUILD_SHARED_LIBRARY) -@endcode -This is the minimal file `Android.mk`, which builds C++ source code of an Android application. Note -that the first two lines and the last line are mandatory for any `Android.mk`. - -Usually the file `Application.mk` is optional, but in case of project using OpenCV, when STL and -exceptions are used in C++, it also should be created. Example of the file `Application.mk`: -@code{.make} -APP_STL := gnustl_static -APP_CPPFLAGS := -frtti -fexceptions -APP_ABI := all -@endcode - -@note We recommend setting APP_ABI := all for all targets. If you want to specify the target -explicitly, use armeabi for ARMv5/ARMv6, armeabi-v7a for ARMv7, x86 for Intel Atom or mips for MIPS. - -@anchor tutorial_android_dev_intro_ndk -Building application native part from command line --------------------------------------------------- - -Here is the standard way to compile C++ part of an Android application: - -**warning** - -We strongly recommend using cmd.exe (standard Windows console) instead of Cygwin on - **Windows**. Use the latter if only you're absolutely sure about, what you're doing. Cygwin is - not really supported and we are unlikely to help you in case you encounter some problems with - it. So, use it only if you're capable of handling the consequences yourself. - --# Open console and go to the root folder of an Android application - @code{.bash} - cd / - @endcode --# Run the following command - @code{.bash} - /ndk-build - @endcode - @note On Windows we recommend to use ndk-build.cmd in standard Windows console (cmd.exe) rather than the similar bash script in Cygwin shell. - ![](images/ndk_build.png) - --# After executing this command the C++ part of the source code is compiled. - -After that the Java part of the application can be (re)compiled (using either *Eclipse* or *Ant* -build tool). - -@note Some parameters can be set for the ndk-build: - **Example 1**: Verbose compilation - @code{.bash} - /ndk-build V=1 - @endcode - **Example 2**: Rebuild all - @code{.bash} - /ndk-build -B - @endcode - -@anchor tutorial_android_dev_intro_eclipse -Building application native part from *Eclipse* (CDT Builder) -------------------------------------------------------------- - -There are several possible ways to integrate compilation of native C++ code by Android NDK into -Eclipse build process. We recommend the approach based on Eclipse CDT(C/C++ Development Tooling) -Builder. - -**important** - -OpenCV for Android package since version 2.4.2 contains sample projects - pre-configured CDT Builders. For your own projects follow the steps below. - --# Define the NDKROOT environment variable containing the path to Android NDK in your system (e.g. - "X:\\Apps\\android-ndk-r8" or "/opt/android-ndk-r8"). - - **On Windows** an environment variable can be set via - My Computer -\> Properties -\> Advanced -\> Environment variables. On Windows 7 it's also - possible to use [setx](http://ss64.com/nt/setx.html) command in a console session. - - **On Linux** and **MacOS** an environment variable can be set via appending a - "export VAR_NAME=VAR_VALUE" line to the `"~/.bashrc"` file and logging off and then on. - - @note It's also possible to define the NDKROOT environment variable within Eclipse IDE, but it - should be done for every new workspace you create. If you prefer this option better than setting - system environment variable, open Eclipse menu - Window -\> Preferences -\> C/C++ -\> Build -\> Environment, press the Add... button and set variable - name to NDKROOT and value to local Android NDK path. \#. After that you need to **restart Eclipse** - to apply the changes. - --# Open Eclipse and load the Android app project to configure. - --# Add C/C++ Nature to the project via Eclipse menu - New -\> Other -\> C/C++ -\> Convert to a C/C++ Project. - ![](images/eclipse_cdt_cfg1.png) - And: - ![](images/eclipse_cdt_cfg2.png) - --# Select the project(s) to convert. Specify "Project type" = Makefile project, "Toolchains" = - Other Toolchain. - ![](images/eclipse_cdt_cfg3.png) - --# Open Project Properties -\> C/C++ Build, uncheck Use default build command, replace "Build - command" text from "make" to - - "${NDKROOT}/ndk-build.cmd" on Windows, - - "${NDKROOT}/ndk-build" on Linux and MacOS. - - ![](images/eclipse_cdt_cfg4.png) - --# Go to Behaviour tab and change "Workbench build type" section like shown below: - - ![](images/eclipse_cdt_cfg5.png) - --# Press OK and make sure the ndk-build is successfully invoked when building the project. - - ![](images/eclipse_cdt_cfg6.png) - --# If you open your C++ source file in Eclipse editor, you'll see syntax error notifications. They - are not real errors, but additional CDT configuring is required. - - ![](images/eclipse_cdt_cfg7.png) - --# Open Project Properties -\> C/C++ General -\> Paths and Symbols and add the following - **Include** paths for **C++**: - @code - # for NDK r8 and prior: - ${NDKROOT}/platforms/android-9/arch-arm/usr/include - ${NDKROOT}/sources/cxx-stl/gnu-libstdc++/include - ${NDKROOT}/sources/cxx-stl/gnu-libstdc++/libs/armeabi-v7a/include - ${ProjDirPath}/../../sdk/native/jni/include - - # for NDK r8b and later: - ${NDKROOT}/platforms/android-9/arch-arm/usr/include - ${NDKROOT}/sources/cxx-stl/gnu-libstdc++/4.6/include - ${NDKROOT}/sources/cxx-stl/gnu-libstdc++/4.6/libs/armeabi-v7a/include - ${ProjDirPath}/../../sdk/native/jni/include - @endcode - The last path should be changed to the correct absolute or relative path to OpenCV4Android SDK - location. - - This should clear the syntax error notifications in Eclipse C++ editor. - - ![](images/eclipse_cdt_cfg8.png) - -Debugging and Testing ---------------------- - -In this section we will give you some easy-to-follow instructions on how to set up an emulator or -hardware device for testing and debugging an Android project. - -### AVD - -AVD (*Android Virtual Device*) is not probably the most convenient way to test an OpenCV-dependent -application, but sure the most uncomplicated one to configure. - --# Assuming you already have *Android SDK* and *Eclipse IDE* installed, in Eclipse go - Window -\> AVD Manager. --# Press the New button in AVD Manager window. --# Create new Android Virtual Device window will let you select some properties for your new - device, like target API level, size of SD-card and other. - - ![](images/AVD_create.png) - --# When you click the Create AVD button, your new AVD will be available in AVD Manager. --# Press Start to launch the device. Be aware that any AVD (a.k.a. Emulator) is usually much slower - than a hardware Android device, so it may take up to several minutes to start. --# Go Run -\> Run/Debug in Eclipse IDE to run your application in regular or debugging mode. - Device Chooser will let you choose among the running devices or to start a new one. - -### Hardware Device - -If you have an Android device, you can use it to test and debug your applications. This way is more -authentic, though a little bit harder to set up. You need to make some actions for Windows and Linux -operating systems to be able to work with Android devices. No extra actions are required for Mac OS. -See detailed information on configuring hardware devices in subsections below. - -You may also consult the official [Android Developers site -instructions](http://developer.android.com/tools/device.html) for more information. +In this tutorial we're gonna use an official Android Studio IDE and a set of other freely available tools. + +### Get tools and dependencies + +Here's how to get a ready to work environment: + +1. Download and install Android Studio: + * Ubuntu: + 1. Download Android Studio: https://developer.android.com/studio + 2. Extract the tar.gz archive + 3. Follow the instructions in `Install-Linux-tar.txt`: open `android-studio/bin` folder in terminal and run `./studio.sh` + 4. Perform standard installation through GUI + 5. Optionally you can add a shortcut on a desktop for a quick access by clicking menu ***Tools -> Create desktop entry***. The menu appears after any project is created or opened. + * Windows: + Just download Android Studio from the official site and run installer. + +2. Install fresh Android SDK and NDK: + 1. Open SDK manager in Android Studio (***Customize -> All Settings -> Languages & Frameworks -> Android SDK***) + 2. Enable "Show Package Details" checkbox + ![](images/sdk_ndk_manager.png) + 3. Check SDK and NDK of the latest versions and press OK + 4. Make sure that your device support the chosen SDK versions + +3. Install all the necessary packages for the build: + - `sudo apt install git cmake ninja-build openjdk-17-jdk openjdk-17-jre` + - the rest required packages are dependencies and should be installed automatically + +### Check OpenCV examples + +1. Download OpenCV from Android SDK from official [release page on Github](https://github.com/opencv/opencv/releases) +or [SourceForge](https://sourceforge.net/projects/opencvlibrary/). +2. Extract zip archive with your OS tools. +3. Open the project `/OpenCV-android-sdk/samples` in Android Studio. +4. Connect your device + * Debugging should be enabled on a device, you can find an instruction about it across the web + * Alternatively you can use a virtual device that comes with the Android studio + ![](images/choose_device.png) +5. Choose a sample from the drop-down menu (for example, `15-puzzle`) and run it. + +Setup Device for Testing and Debugging +-------------------------------------- + +Usually the recipe above works as expected, but in some cases there are additional actions that must +be performed. In this section we'll cover some cases. #### Windows host computer +If you have Windows 10 or higher then you don't have to do additional actions to connect +a phone and run samples on it. However, earlier Windows versions require a longer procedure: + -# Enable USB debugging on the Android device (via Settings menu). -# Attach the Android device to your PC with a USB cable. -# Go to Start Menu and **right-click** on Computer. Select Manage in the context menu. You may be @@ -477,8 +165,8 @@ instructions](http://developer.android.com/tools/device.html) for more informati #### Linux host computer -By default Linux doesn't recognize Android devices, but it's easy to fix this issue. On Ubuntu Linux -you have to create a new **/etc/udev/rules.d/51-android.rules** configuration file that contains +While the latest Ubuntu versions work well with connected Android devices, there can be issues on older versions. +However, most of them can be fixed easily. You have to create a new **/etc/udev/rules.d/51-android.rules** configuration file that contains information about your Android device. You may find some Vendor ID's [here](http://developer.android.com/tools/device.html#VendorIds) or execute lsusb command to view VendorID of plugged Android device. Here is an example of such file for LG device: @@ -488,7 +176,13 @@ SUBSYSTEM=="usb", ATTR{idVendor}=="1004", MODE="0666", GROUP="plugdev" Then restart your adb server (even better to restart the system), plug in your Android device and execute adb devices command. You will see the list of attached devices: -![](images/usb_device_connect_ubuntu.png) +``` +savuor@rostislav-laptop:~/Android/Sdk/platform-tools$ ./adb devices +List of devices attached +R58MB40Q3VP device + +savuor@rostislav-laptop:~/Android/Sdk/platform-tools$ +``` #### Mac OS host computer @@ -497,5 +191,5 @@ No actions are required, just connect your device via USB and run adb devices to What's next ----------- -Now, when you have your development environment set up and configured, you may want to proceed to -installing OpenCV4Android SDK. You can learn how to do that in a separate @ref tutorial_O4A_SDK tutorial. +Now, when you have your instance of OpenCV4Adroid SDK set up and configured, you may want to proceed +to using OpenCV in your own application. You can learn how to do that in a separate @ref tutorial_dev_with_OCV_on_Android tutorial. diff --git a/doc/tutorials/introduction/android_binary_package/android_dnn_intro.markdown b/doc/tutorials/introduction/android_binary_package/android_dnn_intro.markdown new file mode 100644 index 0000000000..871ac5e588 --- /dev/null +++ b/doc/tutorials/introduction/android_binary_package/android_dnn_intro.markdown @@ -0,0 +1,85 @@ +# How to run deep networks on Android device {#tutorial_android_dnn_intro} + +@tableofcontents + +@prev_tutorial{tutorial_dev_with_OCV_on_Android} +@next_tutorial{tutorial_android_ocl_intro} + +@see @ref tutorial_table_of_content_dnn + +| | | +| -: | :- | +| Original author | Dmitry Kurtaev | +| Compatibility | OpenCV >= 4.9 | + +## Introduction +In this tutorial you'll know how to run deep learning networks on Android device +using OpenCV deep learning module. +Tutorial was written for Android Studio 2022.2.1. + +## Requirements + +- Download and install Android Studio from https://developer.android.com/studio. + +- Get the latest pre-built OpenCV for Android release from https://github.com/opencv/opencv/releases +and unpack it (for example, `opencv-4.X.Y-android-sdk.zip`, minimum version 4.9 is required). + +- Download MobileNet object detection model from https://github.com/chuanqi305/MobileNet-SSD. +Configuration file `MobileNetSSD_deploy.prototxt` and model weights `MobileNetSSD_deploy.caffemodel` +are required. + +## Create an empty Android Studio project and add OpenCV dependency + +Use @ref tutorial_dev_with_OCV_on_Android tutorial to initialize your project and add OpenCV. + +## Make an app + +Our sample will takes pictures from a camera, forwards it into a deep network and +receives a set of rectangles, class identifiers and confidence values in range [0, 1]. + +- First of all, we need to add a necessary widget which displays processed +frames. Modify `app/src/main/res/layout/activity_main.xml`: +@include android/mobilenet-objdetect/res/layout/activity_main.xml + +- Modify `/app/src/main/AndroidManifest.xml` to enable full-screen mode, set up +a correct screen orientation and allow to use a camera. +@code{.xml} + + + + +@endcode +@snippet android/mobilenet-objdetect/gradle/AndroidManifest.xml mobilenet_tutorial + +- Replace content of `app/src/main/java/com/example/myapplication/MainActivity.java` and set a custom package name if necessary: + +@snippet android/mobilenet-objdetect/src/org/opencv/samples/opencv_mobilenet/MainActivity.java mobilenet_tutorial_package +@snippet android/mobilenet-objdetect/src/org/opencv/samples/opencv_mobilenet/MainActivity.java mobilenet_tutorial + +- Put downloaded `deploy.prototxt` and `mobilenet_iter_73000.caffemodel` +into `app/src/main/res/raw` folder. OpenCV DNN model is mainly designed to load ML and DNN models +from file. Modern Android does not allow it without extra permissions, but provides Java API to load +bytes from resources. The sample uses alternative DNN API that initializes a model from in-memory +buffer rather than a file. The following function reads model file from resources and converts it to +`MatOfBytes` (analog of `std::vector` in C++ world) object suitable for OpenCV Java API: + +@snippet android/mobilenet-objdetect/src/org/opencv/samples/opencv_mobilenet/MainActivity.java mobilenet_tutorial_resource + +And then the network initialization is done with the following lines: + +@snippet android/mobilenet-objdetect/src/org/opencv/samples/opencv_mobilenet/MainActivity.java init_model_from_memory + +See also [Android documentation on resources](https://developer.android.com/guide/topics/resources/providing-resources.html) + +- Take a look how DNN model input is prepared and inference result is interpreted: + +@snippet android/mobilenet-objdetect/src/org/opencv/samples/opencv_mobilenet/MainActivity.java mobilenet_handle_frame + +`Dnn.blobFromImage` converts camera frame to neural network input tensor. Resize and statistical +normalization are applied. Each line of network output tensor contains information on one detected +object in the following order: confidence in range [0, 1], class id, left, top, right, bottom box +coordinates. All coordinates are in range [0, 1] and should be scaled to image size before rendering. + +- Launch an application and make a fun! +![](images/11_demo.jpg) diff --git a/doc/tutorials/introduction/android_binary_package/android_ocl_intro.markdown b/doc/tutorials/introduction/android_binary_package/android_ocl_intro.markdown index 12701c0cf7..5d98fb732d 100644 --- a/doc/tutorials/introduction/android_binary_package/android_ocl_intro.markdown +++ b/doc/tutorials/introduction/android_binary_package/android_ocl_intro.markdown @@ -1,26 +1,24 @@ Use OpenCL in Android camera preview based CV application {#tutorial_android_ocl_intro} ===================================== -@prev_tutorial{tutorial_dev_with_OCV_on_Android} +@prev_tutorial{tutorial_android_dnn_intro} @next_tutorial{tutorial_macos_install} | | | | -: | :- | -| Original author | Andrey Pavlenko | -| Compatibility | OpenCV >= 3.0 | - -@warning -This tutorial is deprecated. +| Original author | Andrey Pavlenko, Alexander Panov | +| Compatibility | OpenCV >= 4.9 | This guide was designed to help you in use of [OpenCL ™](https://www.khronos.org/opencl/) in Android camera preview based CV application. -It was written for [Eclipse-based ADT tools](http://developer.android.com/tools/help/adt.html) -(deprecated by Google now), but it easily can be reproduced with [Android Studio](http://developer.android.com/tools/studio/index.html). +Tutorial was written for [Android Studio](http://developer.android.com/tools/studio/index.html) 2022.2.1. It was tested with Ubuntu 22.04. This tutorial assumes you have the following installed and configured: -- JDK -- Android SDK and NDK -- Eclipse IDE with ADT and CDT plugins +- Android Studio (2022.2.1.+) +- JDK 17 +- Android SDK +- Android NDK (25.2.9519653+) +- download OpenCV source code from [github](git@github.com:opencv/opencv.git) or from [releases](https://opencv.org/releases/) and build by [instruction on wiki](https://github.com/opencv/opencv/wiki/Custom-OpenCV-Android-SDK-and-AAR-package-build). It also assumes that you are familiar with Android Java and JNI programming basics. If you need help with anything of the above, you may refer to our @ref tutorial_android_dev_intro guide. @@ -30,6 +28,56 @@ This tutorial also assumes you have an Android operated device with OpenCL enabl The related source code is located within OpenCV samples at [opencv/samples/android/tutorial-4-opencl](https://github.com/opencv/opencv/tree/5.x/samples/android/tutorial-4-opencl/) directory. +How to build custom OpenCV Android SDK with OpenCL +-------------------------------------------------- + +1. __Assemble and configure Android OpenCL SDK.__ +The JNI part of the sample depends on standard Khornos OpenCL headers, and C++ wrapper for OpenCL and libOpenCL.so. +The standard OpenCL headers may be copied from 3rdparty directory in OpenCV repository or you Linux distribution package. +C++ wrapper is available in [official Khronos reposiotry on Github](https://github.com/KhronosGroup/OpenCL-CLHPP). +Copy the header files to didicated directory in the following way: +@code{.bash} +cd your_path/ && mkdir ANDROID_OPENCL_SDK && mkdir ANDROID_OPENCL_SDK/include && cd ANDROID_OPENCL_SDK/include +cp -r path_to_opencv/opencv/3rdparty/include/opencl/1.2/CL . && cd CL +wget https://github.com/KhronosGroup/OpenCL-CLHPP/raw/main/include/CL/opencl.hpp +wget https://github.com/KhronosGroup/OpenCL-CLHPP/raw/main/include/CL/cl2.hpp +@endcode +libOpenCL.so may be provided with BSP or just downloaded from any OpenCL-cabaple Android device with relevant arhitecture. +@code{.bash} +cd your_path/ANDROID_OPENCL_SDK && mkdir lib && cd lib +adb pull /system/vendor/lib64/libOpenCL.so +@endcode +System verison of libOpenCL.so may have a lot of platform specific dependencies. `-Wl,--allow-shlib-undefined` flag allows +to ignore 3rdparty symbols if they are not used during the build. +The following CMake line allows to link the JNI part against standard OpenCL, but not include the loadLibrary into +application package. System OpenCL API is used in run-time. +@code +target_link_libraries(${target} -lOpenCL) +@endcode + + +2. __Build custom OpenCV Android SDK with OpenCL.__ +OpenCL support (T-API) is disabled in OpenCV builds for Android OS by default. +but it's possible to rebuild locally OpenCV for Android with OpenCL/T-API enabled: use `-DWITH_OPENCL=ON` option for CMake. +You also need to specify the path to the Android OpenCL SDK: use `-DANDROID_OPENCL_SDK=path_to_your_Android_OpenCL_SDK` option for CMake. +If you are building OpenCV using `build_sdk.py` please follow [instruction on wiki](https://github.com/opencv/opencv/wiki/Custom-OpenCV-Android-SDK-and-AAR-package-build). +Set these CMake parameters in your `.config.py`, e.g. `ndk-18-api-level-21.config.py`: +@code{.py} +ABI("3", "arm64-v8a", None, 21, cmake_vars=dict('WITH_OPENCL': 'ON', 'ANDROID_OPENCL_SDK': 'path_to_your_Android_OpenCL_SDK')) +@endcode +If you are building OpenCV using cmake/ninja, use this bash script (set your NDK_VERSION and your paths instead of examples of paths): +@code{.bash} +cd path_to_opencv && mkdir build && cd build +export NDK_VERSION=25.2.9519653 +export ANDROID_SDK=/home/user/Android/Sdk/ +export ANDROID_OPENCL_SDK=/path_to_ANDROID_OPENCL_SDK/ +export ANDROID_HOME=$ANDROID_SDK +export ANDROID_NDK_HOME=$ANDROID_SDK/ndk/$NDK_VERSION/ +cmake -GNinja -DCMAKE_TOOLCHAIN_FILE=$ANDROID_NDK_HOME/build/cmake/android.toolchain.cmake -DANDROID_STL=c++_shared -DANDROID_NATIVE_API_LEVEL=24 +-DANDROID_SDK=$ANDROID_SDK -DANDROID_NDK=$ANDROID_NDK_HOME -DBUILD_JAVA=ON -DANDROID_HOME=$ANDROID_SDK -DBUILD_ANDROID_EXAMPLES=ON +-DINSTALL_ANDROID_EXAMPLES=ON -DANDROID_ABI=arm64-v8a -DWITH_OPENCL=ON -DANDROID_OPENCL_SDK=$ANDROID_OPENCL_SDK .. +@endcode + Preface ------- @@ -97,74 +145,16 @@ public class Tutorial4Activity extends Activity { And a minimal `View` class respectively: -@code{.java} -public class MyGLSurfaceView extends GLSurfaceView { +@snippet samples/android/tutorial-4-opencl/src/org/opencv/samples/tutorial4/MyGLSurfaceView.java minimal_surface_view - MyGLRendererBase mRenderer; - - public MyGLSurfaceView(Context context) { - super(context); - - if(android.os.Build.VERSION.SDK_INT >= 21) - mRenderer = new Camera2Renderer(this); - else - mRenderer = new CameraRenderer(this); - - setEGLContextClientVersion(2); - setRenderer(mRenderer); - setRenderMode(GLSurfaceView.RENDERMODE_WHEN_DIRTY); - } - - @Override - public void surfaceCreated(SurfaceHolder holder) { - super.surfaceCreated(holder); - } - - @Override - public void surfaceDestroyed(SurfaceHolder holder) { - super.surfaceDestroyed(holder); - } - - @Override - public void surfaceChanged(SurfaceHolder holder, int format, int w, int h) { - super.surfaceChanged(holder, format, w, h); - } - - @Override - public void onResume() { - super.onResume(); - mRenderer.onResume(); - } - - @Override - public void onPause() { - mRenderer.onPause(); - super.onPause(); - } -} -@endcode - -__Note__: we use two renderer classes: one for legacy [Camera](http://developer.android.com/reference/android/hardware/Camera.html) API +@note we use two renderer classes: one for legacy [Camera](http://developer.android.com/reference/android/hardware/Camera.html) API and another for modern [Camera2](http://developer.android.com/reference/android/hardware/camera2/package-summary.html). A minimal `Renderer` class can be implemented in Java (OpenGL ES 2.0 [available](http://developer.android.com/reference/android/opengl/GLES20.html) in Java), but since we are going to modify the preview texture with OpenCL let's move OpenGL stuff to JNI. Here is a simple Java wrapper for our JNI stuff: -@code{.java} -public class NativeGLRenderer { - static - { - System.loadLibrary("opencv_java4"); // comment this when using OpenCV Manager - System.loadLibrary("JNIrender"); - } - - public static native int initGL(); - public static native void closeGL(); - public static native void drawFrame(); - public static native void changeSize(int width, int height); -} -@endcode +@snippet samples/android/tutorial-4-opencl/src/org/opencv/samples/tutorial4/NativePart.java native_part Since `Camera` and `Camera2` APIs differ significantly in camera setup and control, let's create a base class for the two corresponding renderers: @@ -275,126 +265,21 @@ After that we can read (_copy_) pixel data from C/C++ via `glReadPixels()` and w Also that `GL_TEXTURE_2D` texture can be shared with OpenCL without copying, but we have to create OpenCL context with special way for that: -@code{.cpp} -void initCL() -{ - EGLDisplay mEglDisplay = eglGetCurrentDisplay(); - if (mEglDisplay == EGL_NO_DISPLAY) - LOGE("initCL: eglGetCurrentDisplay() returned 'EGL_NO_DISPLAY', error = %x", eglGetError()); - - EGLContext mEglContext = eglGetCurrentContext(); - if (mEglContext == EGL_NO_CONTEXT) - LOGE("initCL: eglGetCurrentContext() returned 'EGL_NO_CONTEXT', error = %x", eglGetError()); - - cl_context_properties props[] = - { CL_GL_CONTEXT_KHR, (cl_context_properties) mEglContext, - CL_EGL_DISPLAY_KHR, (cl_context_properties) mEglDisplay, - CL_CONTEXT_PLATFORM, 0, - 0 }; - - try - { - cl::Platform p = cl::Platform::getDefault(); - std::string ext = p.getInfo(); - if(ext.find("cl_khr_gl_sharing") == std::string::npos) - LOGE("Warning: CL-GL sharing isn't supported by PLATFORM"); - props[5] = (cl_context_properties) p(); - - theContext = cl::Context(CL_DEVICE_TYPE_GPU, props); - std::vector devs = theContext.getInfo(); - LOGD("Context returned %d devices, taking the 1st one", devs.size()); - ext = devs[0].getInfo(); - if(ext.find("cl_khr_gl_sharing") == std::string::npos) - LOGE("Warning: CL-GL sharing isn't supported by DEVICE"); - - theQueue = cl::CommandQueue(theContext, devs[0]); - - // ... - } - catch(cl::Error& e) - { - LOGE("cl::Error: %s (%d)", e.what(), e.err()); - } - catch(std::exception& e) - { - LOGE("std::exception: %s", e.what()); - } - catch(...) - { - LOGE( "OpenCL info: unknown error while initializing OpenCL stuff" ); - } - LOGD("initCL completed"); -} -@endcode - -@note To build this JNI code you need __OpenCL 1.2__ headers from [Khronos web site](https://www.khronos.org/registry/cl/api/1.2/) and -the __libOpenCL.so__ downloaded from the device you'll run the application. +@snippet samples/android/tutorial-4-opencl/jni/CLprocessor.cpp init_opencl Then the texture can be wrapped by a `cl::ImageGL` object and processed via OpenCL calls: -@code{.cpp} - cl::ImageGL imgIn (theContext, CL_MEM_READ_ONLY, GL_TEXTURE_2D, 0, texIn); - cl::ImageGL imgOut(theContext, CL_MEM_WRITE_ONLY, GL_TEXTURE_2D, 0, texOut); - std::vector < cl::Memory > images; - images.push_back(imgIn); - images.push_back(imgOut); - theQueue.enqueueAcquireGLObjects(&images); - theQueue.finish(); - - cl::Kernel Laplacian = ... - Laplacian.setArg(0, imgIn); - Laplacian.setArg(1, imgOut); - theQueue.finish(); - - theQueue.enqueueNDRangeKernel(Laplacian, cl::NullRange, cl::NDRange(w, h), cl::NullRange); - theQueue.finish(); - - theQueue.enqueueReleaseGLObjects(&images); - theQueue.finish(); -@endcode +@snippet samples/android/tutorial-4-opencl/jni/CLprocessor.cpp process_pure_opencl ### OpenCV T-API But instead of writing OpenCL code by yourselves you may want to use __OpenCV T-API__ that calls OpenCL implicitly. All that you need is to pass the created OpenCL context to OpenCV (via `cv::ocl::attachContext()`) and somehow wrap OpenGL texture with `cv::UMat`. Unfortunately `UMat` keeps OpenCL _buffer_ internally, that can't be wrapped over either OpenGL _texture_ or OpenCL _image_ - so we have to copy image data here: -@code{.cpp} - cl::ImageGL imgIn (theContext, CL_MEM_READ_ONLY, GL_TEXTURE_2D, 0, tex); - std::vector < cl::Memory > images(1, imgIn); - theQueue.enqueueAcquireGLObjects(&images); - theQueue.finish(); - cv::UMat uIn, uOut, uTmp; - cv::ocl::convertFromImage(imgIn(), uIn); - theQueue.enqueueReleaseGLObjects(&images); +@snippet samples/android/tutorial-4-opencl/jni/CLprocessor.cpp process_tapi - cv::Laplacian(uIn, uTmp, CV_8U); - cv:multiply(uTmp, 10, uOut); - cv::ocl::finish(); - - cl::ImageGL imgOut(theContext, CL_MEM_WRITE_ONLY, GL_TEXTURE_2D, 0, tex); - images.clear(); - images.push_back(imgOut); - theQueue.enqueueAcquireGLObjects(&images); - cl_mem clBuffer = (cl_mem)uOut.handle(cv::ACCESS_READ); - cl_command_queue q = (cl_command_queue)cv::ocl::Queue::getDefault().ptr(); - size_t offset = 0; - size_t origin[3] = { 0, 0, 0 }; - size_t region[3] = { w, h, 1 }; - CV_Assert(clEnqueueCopyBufferToImage (q, clBuffer, imgOut(), offset, origin, region, 0, NULL, NULL) == CL_SUCCESS); - theQueue.enqueueReleaseGLObjects(&images); - cv::ocl::finish(); -@endcode - -- @note We have to make one more image data copy when placing back the modified image to the original OpenGL texture via OpenCL image wrapper. -- @note By default the OpenCL support (T-API) is disabled in OpenCV builds for Android OS (so it's absent in official packages as of version 3.0), - but it's possible to rebuild locally OpenCV for Android with OpenCL/T-API enabled: use `-DWITH_OPENCL=YES` option for CMake. - @code{.cmd} - cd opencv-build-android - path/to/cmake.exe -GNinja -DCMAKE_MAKE_PROGRAM="path/to/ninja.exe" -DCMAKE_TOOLCHAIN_FILE=path/to/opencv/platforms/android/android.toolchain.cmake -DANDROID_ABI="armeabi-v7a with NEON" -DCMAKE_BUILD_WITH_INSTALL_RPATH=ON path/to/opencv - path/to/ninja.exe install/strip - @endcode - To use your own modified `libopencv_java4.so` you have to keep inside your APK, not to use OpenCV Manager and load it manually via `System.loadLibrary("opencv_java4")`. +@note We have to make one more image data copy when placing back the modified image to the original OpenGL texture via OpenCL image wrapper. Performance notes ----------------- diff --git a/doc/tutorials/introduction/android_binary_package/dev_with_OCV_on_Android.markdown b/doc/tutorials/introduction/android_binary_package/dev_with_OCV_on_Android.markdown index d37721a188..68d7ab3644 100644 --- a/doc/tutorials/introduction/android_binary_package/dev_with_OCV_on_Android.markdown +++ b/doc/tutorials/introduction/android_binary_package/dev_with_OCV_on_Android.markdown @@ -1,384 +1,197 @@ Android Development with OpenCV {#tutorial_dev_with_OCV_on_Android} =============================== -@prev_tutorial{tutorial_O4A_SDK} -@next_tutorial{tutorial_android_ocl_intro} +@prev_tutorial{tutorial_android_dev_intro} +@next_tutorial{tutorial_android_dnn_intro} | | | | -: | :- | -| Original author | Vsevolod Glumov | -| Compatibility | OpenCV >= 3.0 | - -@warning -This tutorial is deprecated. +| Original authors | Alexander Panov, Rostislav Vasilikhin | +| Compatibility | OpenCV >= 4.9.0 | This tutorial has been created to help you use OpenCV library within your Android project. -This guide was written with Windows 7 in mind, though it should work with any other OS supported by -OpenCV4Android SDK. +This guide was checked on Ubuntu but contains no platform-dependent parts, therefore should be compatible with any OS supported by Android Studio and OpenCV4Android SDK. This tutorial assumes you have the following installed and configured: +- Android Studio - JDK - Android SDK and NDK -- Eclipse IDE -- ADT and CDT plugins for Eclipse +- OpenCV for Android SDK from official [release page on Github](https://github.com/opencv/opencv/releases) + or [SourceForge](https://sourceforge.net/projects/opencvlibrary/). Advanced: as alternative the SDK may be + built from source code by [instruction on wiki](https://github.com/opencv/opencv/wiki/Custom-OpenCV-Android-SDK-and-AAR-package-build). If you need help with anything of the above, you may refer to our @ref tutorial_android_dev_intro guide. -This tutorial also assumes you have OpenCV4Android SDK already installed on your development machine -and OpenCV Manager on your testing device correspondingly. If you need help with any of these, you -may consult our @ref tutorial_O4A_SDK tutorial. +If you encounter any error after thoroughly following these steps, feel free to contact us via OpenCV [forum](https://forum.opencv.org). We'll do our best to help you out. -If you encounter any error after thoroughly following these steps, feel free to contact us via -[OpenCV4Android](https://groups.google.com/group/android-opencv/) discussion group or OpenCV [Q&A -forum](https://forum.opencv.org) . We'll do our best to help you out. - -Using OpenCV Library Within Your Android Project ------------------------------------------------- - -In this section we will explain how to make some existing project to use OpenCV. Starting with 2.4.2 -release for Android, *OpenCV Manager* is used to provide apps with the best available version of -OpenCV. You can get more information here: `Android OpenCV Manager` and in these -[slides](https://docs.google.com/a/itseez.com/presentation/d/1EO_1kijgBg_BsjNp2ymk-aarg-0K279_1VZRcPplSuk/present#slide=id.p). - -### Java - -#### Application Development with Async Initialization - -Using async initialization is a **recommended** way for application development. It uses the OpenCV -Manager to access OpenCV libraries externally installed in the target system. - --# Add OpenCV library project to your workspace. Use menu - File -\> Import -\> Existing project in your workspace. - - Press Browse button and locate OpenCV4Android SDK (`OpenCV-2.4.9-android-sdk/sdk`). - - ![](images/eclipse_opencv_dependency0.png) - --# In application project add a reference to the OpenCV Java SDK in - Project -\> Properties -\> Android -\> Library -\> Add select OpenCV Library - 2.4.9. - - ![](images/eclipse_opencv_dependency1.png) - -In most cases OpenCV Manager may be installed automatically from Google Play. For the case, when -Google Play is not available, i.e. emulator, developer board, etc, you can install it manually using -adb tool. See `Manager Selection` for details. - -There is a very base code snippet implementing the async initialization. It shows basic principles. -See the "15-puzzle" OpenCV sample for details. -@code{.java} -public class Sample1Java extends Activity implements CvCameraViewListener { - - private BaseLoaderCallback mLoaderCallback = new BaseLoaderCallback(this) { - @Override - public void onManagerConnected(int status) { - switch (status) { - case LoaderCallbackInterface.SUCCESS: - { - Log.i(TAG, "OpenCV loaded successfully"); - mOpenCvCameraView.enableView(); - } break; - default: - { - super.onManagerConnected(status); - } break; - } - } - }; - - @Override - public void onResume() - { - super.onResume(); - OpenCVLoader.initAsync(OpenCVLoader.OPENCV_VERSION_2_4_6, this, mLoaderCallback); - } - - ... -} -@endcode -It this case application works with OpenCV Manager in asynchronous fashion. OnManagerConnected -callback will be called in UI thread, when initialization finishes. Please note, that it is not -allowed to use OpenCV calls or load OpenCV-dependent native libs before invoking this callback. Load -your own native libraries that depend on OpenCV after the successful OpenCV initialization. Default -BaseLoaderCallback implementation treat application context as Activity and calls Activity.finish() -method to exit in case of initialization failure. To override this behavior you need to override -finish() method of BaseLoaderCallback class and implement your own finalization method. - -#### Application Development with Static Initialization - -According to this approach all OpenCV binaries are included into your application package. It is -designed mostly for development purposes. This approach is deprecated for the production code, -release package is recommended to communicate with OpenCV Manager via the async initialization -described above. - --# Add the OpenCV library project to your workspace the same way as for the async initialization - above. Use menu File -\> Import -\> Existing project in your workspace, press Browse button and - select OpenCV SDK path (`OpenCV-2.4.9-android-sdk/sdk`). - - ![](images/eclipse_opencv_dependency0.png) - --# In the application project add a reference to the OpenCV4Android SDK in - Project -\> Properties -\> Android -\> Library -\> Add select OpenCV Library - 2.4.9; - - ![](images/eclipse_opencv_dependency1.png) - --# If your application project **doesn't have a JNI part**, just copy the corresponding OpenCV - native libs from `/sdk/native/libs/` to your project - directory to folder `libs/`. - - In case of the application project **with a JNI part**, instead of manual libraries copying you - need to modify your Android.mk file: add the following two code lines after the - "include $(CLEAR_VARS)" and before - "include path_to_OpenCV-2.4.9-android-sdk/sdk/native/jni/OpenCV.mk" - @code{.make} - OPENCV_CAMERA_MODULES:=on - OPENCV_INSTALL_MODULES:=on - @endcode - The result should look like the following: - @code{.make} - include $(CLEAR_VARS) - - # OpenCV - OPENCV_CAMERA_MODULES:=on - OPENCV_INSTALL_MODULES:=on - include ../../sdk/native/jni/OpenCV.mk - @endcode - After that the OpenCV libraries will be copied to your application `libs` folder during the JNI - build.v - - Eclipse will automatically include all the libraries from the `libs` folder to the application - package (APK). - --# The last step of enabling OpenCV in your application is Java initialization code before calling - OpenCV API. It can be done, for example, in the static section of the Activity class: - @code{.java} - static { - if (!OpenCVLoader.initDebug()) { - // Handle initialization error - } - } - @endcode - If you application includes other OpenCV-dependent native libraries you should load them - **after** OpenCV initialization: - @code{.java} - static { - if (!OpenCVLoader.initDebug()) { - // Handle initialization error - } else { - System.loadLibrary("my_jni_lib1"); - System.loadLibrary("my_jni_lib2"); - } - } - @endcode - -### Native/C++ - -To build your own Android application, using OpenCV as native part, the following steps should be -taken: - --# You can use an environment variable to specify the location of OpenCV package or just hardcode - absolute or relative path in the `jni/Android.mk` of your projects. --# The file `jni/Android.mk` should be written for the current application using the common rules - for this file. - - For detailed information see the Android NDK documentation from the Android NDK archive, in the - file `/docs/ANDROID-MK.html`. - --# The following line: - @code{.make} - include C:\Work\OpenCV4Android\OpenCV-2.4.9-android-sdk\sdk\native\jni\OpenCV.mk - @endcode - Should be inserted into the `jni/Android.mk` file **after** this line: - @code{.make} - include $(CLEAR_VARS) - @endcode --# Several variables can be used to customize OpenCV stuff, but you **don't need** to use them when - your application uses the async initialization via the OpenCV Manager API. - - @note These variables should be set **before** the "include .../OpenCV.mk" line: - @code{.make} - OPENCV_INSTALL_MODULES:=on - @endcode - - Copies necessary OpenCV dynamic libs to the project libs folder in order to include them - into the APK. - @code{.make} - OPENCV_CAMERA_MODULES:=off - @endcode - Skip native OpenCV camera related libs copying to the project libs folder. - @code{.make} - OPENCV_LIB_TYPE:=STATIC - @endcode - Perform static linking with OpenCV. By default dynamic link is used and the project JNI lib - depends on libopencv_java.so. - --# The file `Application.mk` should exist and should contain lines: - @code{.make} - APP_STL := gnustl_static - APP_CPPFLAGS := -frtti -fexceptions - @endcode - Also, the line like this one: - @code{.make} - APP_ABI := armeabi-v7a - @endcode - Should specify the application target platforms. - - In some cases a linkage error (like - `"In function 'cv::toUtf16(std::basic_string<...>... undefined reference to 'mbstowcs'"`) - happens when building an application JNI library, depending on OpenCV. The following line in the - `Application.mk` usually fixes it: - @code{.make} - APP_PLATFORM := android-9 - @endcode - --# Either use @ref tutorial_android_dev_intro_ndk "manual" ndk-build invocation or - @ref tutorial_android_dev_intro_eclipse "setup Eclipse CDT Builder" to build native JNI lib - before (re)building the Java part and creating - an APK. - -Hello OpenCV Sample +Hello OpenCV sample ------------------- -Here are basic steps to guide you through the process of creating a simple OpenCV-centric -application. It will be capable of accessing camera output, processing it and displaying the result. +In this section we're gonna create a simple app that does nothing but OpenCV loading. In next section we'll extend it to support camera. --# Open Eclipse IDE, create a new clean workspace, create a new Android project - File --\> New --\> Android Project --# Set name, target, package and minSDKVersion accordingly. The minimal SDK version for build with - OpenCV4Android SDK is 11. Minimal device API Level (for application manifest) is 8. --# Allow Eclipse to create default activity. Lets name the activity HelloOpenCvActivity. --# Choose Blank Activity with full screen layout. Lets name the layout HelloOpenCvLayout. --# Import OpenCV library project to your workspace. --# Reference OpenCV library within your project properties. +In addition to this instruction you can use some video guide, for example [this one](https://www.youtube.com/watch?v=bR7lL886-uc&ab_channel=ProgrammingHut) - ![](images/dev_OCV_reference.png) +1. Open Android Studio and create empty project by choosing ***Empty Views Activity*** --# Edit your layout file as xml file and pass the following layout there: + ![](images/create_empty_project.png) + +2. Setup the project: + - Choose ***Java*** language + - Choose ***Groovy DSL*** build configuration language + - Choose ***Minumum SDK*** with the version number not less than was used during OpenCV 4 Android build + - If you don't know it, you can find it in file `OpenCV-android-sdk/sdk/build.gradle` at `android -> defaultConfig -> minSdkVersion` + + ![](images/setup_project.png) + + +3. Click ***File -> New -> Import module...*** and select OpenCV SDK path + + ![](images/sdk_path.png) + +4. Set module name as `OpenCV` and press `Finish` + + ![](images/module_name.png) + +5. OpenCV also provides experiemental Kotlin support. Please add Android Kotlin plugin to `MyApplication/OpenCV/build.gradle` file: + @code{.gradle} + plugins { + id 'org.jetbrains.kotlin.android' version '1.7.10' #version may differ for your setup + } + @endcode + Like this: + ![](images/gradle_ocv_fix.png) + If you don't do this, you may get an error: + @code + Task failed with an exception. + ----------- + * Where: + Build file '/home/alexander/AndroidStudioProjects/MyApplication/opencv/build.gradle' line: 4 + + * What went wrong: + A problem occurred evaluating project ':opencv'. + > Plugin with id 'kotlin-android' not found. + @endcode + The fix was found [here](https://stackoverflow.com/questions/73225714/import-opencv-sdk-to-android-studio-chipmunk) + +6. OpenCV project uses `aidl` and `buildConfig` features. Please enable them in + `MyApplication/OpenCV/build.gradle` file to `android` block: + @code{.gradle} + buildFeatures{ + aidl true + buildConfig true + } + + @endcode + Like this: + ![](images/module_gradle_fix.png) + If you don't do this, you may get an error: + @code + JavaCameraView.java:15: error: cannot find symbol import org.opencv.BuildConfig; ^ symbol: class BuildConfig location: package org.opencv + @endcode + The fix was found [here](https://stackoverflow.com/questions/76374886/error-cannot-find-symbol-import-org-opencv-buildconfig-android-studio) and [here](https://forum.opencv.org/t/task-compiledebugjavawithjavac-failed/13667/4) + +7. Add the module to the project: + - Click ***File -> Project structure... -> Dependencies -> All modules -> + (Add Dependency button) -> Module dependency*** + + ![](images/add_module_1.png) + + - Choose `app` + + ![](images/add_module_2.png) + + - Select `OpenCV` + + ![](images/add_module_3.png) + +8. Before using any OpenCV function you have to load the library first. If you application includes other OpenCV-dependent native libraries you should load them ***after*** OpenCV initialization. + Add the folowing code to load the library at app start: + @snippet samples/android/tutorial-1-camerapreview/src/org/opencv/samples/tutorial1/Tutorial1Activity.java ocv_loader_init + Like this: + ![](images/sample_code.png) + +9. Choose a device to check the sample on and run the code by pressing `run` button + + ![](images/run_app.png) + +Camera view sample +------------------ + +In this section we'll extend our empty OpenCV app created in the previous section to support camera. We'll take camera frames and display them on the screen. + +1. Tell a system that we need camera permissions. + Add the following code to the file `MyApplication/app/src/main/AndroidManifest.xml`: + @snippet samples/android/tutorial-1-camerapreview/gradle/AndroidManifest.xml camera_permissions + Like this: + ![](images/camera_permissions.png) + +2. Go to `activity_main.xml` layout and delete TextView with text "Hello World!" + + ![](images/delete_text.png) + + This can also be done in Code or Split mode by removing the `TextView` block from XML file. + +3. Add camera view to the layout: + 1. Add a scheme into layout description: @code{.xml} - - - - - + xmlns:opencv="http://schemas.android.com/apk/res-auto" @endcode --# Add the following permissions to the `AndroidManifest.xml` file: - @code{.xml} - - + 2. Replace `TextView` with `org.opencv.android.JavaCameraView` widget: + @snippet /samples/android/tutorial-1-camerapreview/res/layout/tutorial1_surface_view.xml camera_view - - - - - @endcode --# Set application theme in AndroidManifest.xml to hide title and system buttons. - @code{.xml} - - @endcode --# Add OpenCV library initialization to your activity. Fix errors by adding required imports. - @code{.java} - private BaseLoaderCallback mLoaderCallback = new BaseLoaderCallback(this) { - @Override - public void onManagerConnected(int status) { - switch (status) { - case LoaderCallbackInterface.SUCCESS: - { - Log.i(TAG, "OpenCV loaded successfully"); - mOpenCvCameraView.enableView(); - } break; - default: - { - super.onManagerConnected(status); - } break; - } - } - }; + 3. If you get a layout warning replace `fill_parent` values by `match_parent` for `android:layout_width` and `android:layout_height` properties - @Override - public void onResume() - { - super.onResume(); - OpenCVLoader.initAsync(OpenCVLoader.OPENCV_VERSION_2_4_6, this, mLoaderCallback); - } - @endcode --# Defines that your activity implements CvCameraViewListener2 interface and fix activity related - errors by defining missed methods. For this activity define onCreate, onDestroy and onPause and - implement them according to the code snippet below. Fix errors by adding required imports. - @code{.java} - private CameraBridgeViewBase mOpenCvCameraView; + You'll get a code like this: - @Override - public void onCreate(Bundle savedInstanceState) { - Log.i(TAG, "called onCreate"); - super.onCreate(savedInstanceState); - getWindow().addFlags(WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON); - setContentView(R.layout.HelloOpenCvLayout); - mOpenCvCameraView = (CameraBridgeViewBase) findViewById(R.id.HelloOpenCvView); - mOpenCvCameraView.setVisibility(SurfaceView.VISIBLE); - mOpenCvCameraView.setCvCameraViewListener(this); - } + @include /samples/android/tutorial-1-camerapreview/res/layout/tutorial1_surface_view.xml - @Override - public void onPause() - { - super.onPause(); - if (mOpenCvCameraView != null) - mOpenCvCameraView.disableView(); - } +4. Inherit the main class from `org.opencv.android.CameraActivity`. CameraActivity implements + camera perimission requiest and some other utilities needed for CV application. Methods we're + interested in to override are `onCreate`, `onDestroy`, `onPause`, `onResume` and `getCameraViewList` - public void onDestroy() { - super.onDestroy(); - if (mOpenCvCameraView != null) - mOpenCvCameraView.disableView(); - } +5. Implement the interface `org.opencv.android.CameraBridgeViewBase.CvCameraViewListener2` + `onCameraFrame` method should return the `Mat` object with content for render. + The sample just returns camera frame for preview: `return inputFrame.rgba();` - public void onCameraViewStarted(int width, int height) { - } +6. Allocate `org.opencv.android.CameraBridgeViewBase` object: + - It should be created at app start (`onCreate` method) and this class should be set as a listener + - At pause/resume (`onPause`, `onResume` methods) it should be disabled/enabled + - Should be disabled at app finish (`onDestroy` method) + - Should be returned in `getCameraViewList` - public void onCameraViewStopped() { - } +7. Optionally you can forbid the phone to dim screen or lock: - public Mat onCameraFrame(CvCameraViewFrame inputFrame) { - return inputFrame.rgba(); - } - @endcode --# Run your application on device or emulator. + @snippet samples/android/tutorial-1-camerapreview/src/org/opencv/samples/tutorial1/Tutorial1Activity.java keep_screen -Lets discuss some most important steps. Every Android application with UI must implement Activity -and View. By the first steps we create blank activity and default view layout. The simplest -OpenCV-centric application must implement OpenCV initialization, create its own view to show preview -from camera and implements CvCameraViewListener2 interface to get frames from camera and process it. +Finally you'll get source code similar to this: -First of all we create our application view using xml layout. Our layout consists of the only one -full screen component of class org.opencv.android.JavaCameraView. This class is implemented inside -OpenCV library. It is inherited from CameraBridgeViewBase, that extends SurfaceView and uses -standard Android camera API. +@include samples/android/tutorial-1-camerapreview/src/org/opencv/samples/tutorial1/Tutorial1Activity.java -After creating layout we need to implement Activity class. OpenCV initialization process has been -already discussed above. In this sample we use asynchronous initialization. Implementation of -CvCameraViewListener interface allows you to add processing steps after frame grabbing from camera -and before its rendering on screen. The most important function is onCameraFrame. It is callback -function and it is called on retrieving frame from camera. The callback input is object of -CvCameraViewFrame class that represents frame from camera. +This is it! Now you can run the code on your device to check it. -@note Do not save or use CvCameraViewFrame object out of onCameraFrame callback. This object does -not have its own state and its behavior out of callback is unpredictable! -It has rgba() and gray() -methods that allows to get frame as RGBA and one channel gray scale Mat respectively. It expects -that onCameraFrame function returns RGBA frame that will be drawn on the screen. +Let's discuss some most important steps +--------------------------------------- + +Every Android application with UI must implement Activity and View. By the first steps we create blank +activity and default view layout. The simplest OpenCV-centric application must perform OpenCV +initialization, create a view to show preview from camera and implement `CvCameraViewListener2` interface +to get frames from camera and process them. + +First of all we create our application view using XML layout. Our layout consists of the only one +full screen component of class `org.opencv.android.JavaCameraView`. This OpenCV class is inherited from + `CameraBridgeViewBase` that extends `SurfaceView` and under the hood uses standard Android camera API. + +The `CvCameraViewListener2` interface lets you add some processing steps after the frame is grabbed from +the camera and before it's rendered on the screen. The most important method is `onCameraFrame`. This is +a callback function and it's called on retrieving frame from camera. It expects that `onCameraFrame` +function returns RGBA frame that will be drawn on the screen. + +The callback passes a frame from camera to our class as an object of `CvCameraViewFrame` class. +This object has `rgba()` and `gray()` methods that let a user get colored or one-channel grayscale +frame as a `Mat` class object. + +@note Do not save or use `CvCameraViewFrame` object out of `onCameraFrame` callback. This object does +not have its own state and its behavior outside the callback is unpredictable! diff --git a/doc/tutorials/dnn/dnn_android/11_demo.jpg b/doc/tutorials/introduction/android_binary_package/images/11_demo.jpg similarity index 100% rename from doc/tutorials/dnn/dnn_android/11_demo.jpg rename to doc/tutorials/introduction/android_binary_package/images/11_demo.jpg diff --git a/doc/tutorials/introduction/android_binary_package/images/AVD_create.png b/doc/tutorials/introduction/android_binary_package/images/AVD_create.png deleted file mode 100644 index f55ea51d73..0000000000 Binary files a/doc/tutorials/introduction/android_binary_package/images/AVD_create.png and /dev/null differ diff --git a/doc/tutorials/introduction/android_binary_package/images/AVD_empty.png b/doc/tutorials/introduction/android_binary_package/images/AVD_empty.png deleted file mode 100644 index 6989f7e167..0000000000 Binary files a/doc/tutorials/introduction/android_binary_package/images/AVD_empty.png and /dev/null differ diff --git a/doc/tutorials/introduction/android_binary_package/images/add_module_1.png b/doc/tutorials/introduction/android_binary_package/images/add_module_1.png new file mode 100644 index 0000000000..2653c433bb Binary files /dev/null and b/doc/tutorials/introduction/android_binary_package/images/add_module_1.png differ diff --git a/doc/tutorials/introduction/android_binary_package/images/add_module_2.png b/doc/tutorials/introduction/android_binary_package/images/add_module_2.png new file mode 100644 index 0000000000..55ac7bcdfc Binary files /dev/null and b/doc/tutorials/introduction/android_binary_package/images/add_module_2.png differ diff --git a/doc/tutorials/introduction/android_binary_package/images/add_module_3.png b/doc/tutorials/introduction/android_binary_package/images/add_module_3.png new file mode 100644 index 0000000000..b9be8e8bb6 Binary files /dev/null and b/doc/tutorials/introduction/android_binary_package/images/add_module_3.png differ diff --git a/doc/tutorials/introduction/android_binary_package/images/android_emulator_opencv_manager_fail.png b/doc/tutorials/introduction/android_binary_package/images/android_emulator_opencv_manager_fail.png deleted file mode 100644 index 04839636ec..0000000000 Binary files a/doc/tutorials/introduction/android_binary_package/images/android_emulator_opencv_manager_fail.png and /dev/null differ diff --git a/doc/tutorials/introduction/android_binary_package/images/android_package_7zip.png b/doc/tutorials/introduction/android_binary_package/images/android_package_7zip.png deleted file mode 100644 index 3bd6aa3457..0000000000 Binary files a/doc/tutorials/introduction/android_binary_package/images/android_package_7zip.png and /dev/null differ diff --git a/doc/tutorials/introduction/android_binary_package/images/android_sdk_and_avd_manager.png b/doc/tutorials/introduction/android_binary_package/images/android_sdk_and_avd_manager.png deleted file mode 100644 index cf5eb7d1ab..0000000000 Binary files a/doc/tutorials/introduction/android_binary_package/images/android_sdk_and_avd_manager.png and /dev/null differ diff --git a/doc/tutorials/introduction/android_binary_package/images/camera_permissions.png b/doc/tutorials/introduction/android_binary_package/images/camera_permissions.png new file mode 100644 index 0000000000..f7622dee88 Binary files /dev/null and b/doc/tutorials/introduction/android_binary_package/images/camera_permissions.png differ diff --git a/doc/tutorials/introduction/android_binary_package/images/choose_device.png b/doc/tutorials/introduction/android_binary_package/images/choose_device.png new file mode 100644 index 0000000000..76f3c3060f Binary files /dev/null and b/doc/tutorials/introduction/android_binary_package/images/choose_device.png differ diff --git a/doc/tutorials/introduction/android_binary_package/images/cmd_adb_devices.png b/doc/tutorials/introduction/android_binary_package/images/cmd_adb_devices.png deleted file mode 100644 index e0e4853dec..0000000000 Binary files a/doc/tutorials/introduction/android_binary_package/images/cmd_adb_devices.png and /dev/null differ diff --git a/doc/tutorials/introduction/android_binary_package/images/create_empty_project.png b/doc/tutorials/introduction/android_binary_package/images/create_empty_project.png new file mode 100644 index 0000000000..609f258d45 Binary files /dev/null and b/doc/tutorials/introduction/android_binary_package/images/create_empty_project.png differ diff --git a/doc/tutorials/introduction/android_binary_package/images/delete_text.png b/doc/tutorials/introduction/android_binary_package/images/delete_text.png new file mode 100644 index 0000000000..d865ff92cc Binary files /dev/null and b/doc/tutorials/introduction/android_binary_package/images/delete_text.png differ diff --git a/doc/tutorials/introduction/android_binary_package/images/dev_OCV_new_class.png b/doc/tutorials/introduction/android_binary_package/images/dev_OCV_new_class.png deleted file mode 100644 index 3a75b11081..0000000000 Binary files a/doc/tutorials/introduction/android_binary_package/images/dev_OCV_new_class.png and /dev/null differ diff --git a/doc/tutorials/introduction/android_binary_package/images/dev_OCV_reference.png b/doc/tutorials/introduction/android_binary_package/images/dev_OCV_reference.png deleted file mode 100644 index 5179b23430..0000000000 Binary files a/doc/tutorials/introduction/android_binary_package/images/dev_OCV_reference.png and /dev/null differ diff --git a/doc/tutorials/introduction/android_binary_package/images/device_details.png b/doc/tutorials/introduction/android_binary_package/images/device_details.png deleted file mode 100644 index 9c0a94000b..0000000000 Binary files a/doc/tutorials/introduction/android_binary_package/images/device_details.png and /dev/null differ diff --git a/doc/tutorials/introduction/android_binary_package/images/eclipse_10_crystal_clean.png b/doc/tutorials/introduction/android_binary_package/images/eclipse_10_crystal_clean.png deleted file mode 100644 index 499247a482..0000000000 Binary files a/doc/tutorials/introduction/android_binary_package/images/eclipse_10_crystal_clean.png and /dev/null differ diff --git a/doc/tutorials/introduction/android_binary_package/images/eclipse_11_run_as.png b/doc/tutorials/introduction/android_binary_package/images/eclipse_11_run_as.png deleted file mode 100644 index 46c584d43b..0000000000 Binary files a/doc/tutorials/introduction/android_binary_package/images/eclipse_11_run_as.png and /dev/null differ diff --git a/doc/tutorials/introduction/android_binary_package/images/eclipse_1_choose_workspace.png b/doc/tutorials/introduction/android_binary_package/images/eclipse_1_choose_workspace.png deleted file mode 100644 index af06bd9b0d..0000000000 Binary files a/doc/tutorials/introduction/android_binary_package/images/eclipse_1_choose_workspace.png and /dev/null differ diff --git a/doc/tutorials/introduction/android_binary_package/images/eclipse_1a_locate_sdk.png b/doc/tutorials/introduction/android_binary_package/images/eclipse_1a_locate_sdk.png deleted file mode 100644 index 95bb41e4bd..0000000000 Binary files a/doc/tutorials/introduction/android_binary_package/images/eclipse_1a_locate_sdk.png and /dev/null differ diff --git a/doc/tutorials/introduction/android_binary_package/images/eclipse_2_window_preferences.png b/doc/tutorials/introduction/android_binary_package/images/eclipse_2_window_preferences.png deleted file mode 100644 index 414673547a..0000000000 Binary files a/doc/tutorials/introduction/android_binary_package/images/eclipse_2_window_preferences.png and /dev/null differ diff --git a/doc/tutorials/introduction/android_binary_package/images/eclipse_3_preferences_android.png b/doc/tutorials/introduction/android_binary_package/images/eclipse_3_preferences_android.png deleted file mode 100644 index 76a0589d71..0000000000 Binary files a/doc/tutorials/introduction/android_binary_package/images/eclipse_3_preferences_android.png and /dev/null differ diff --git a/doc/tutorials/introduction/android_binary_package/images/eclipse_4_locate_sdk.png b/doc/tutorials/introduction/android_binary_package/images/eclipse_4_locate_sdk.png deleted file mode 100644 index 9657cc5c4b..0000000000 Binary files a/doc/tutorials/introduction/android_binary_package/images/eclipse_4_locate_sdk.png and /dev/null differ diff --git a/doc/tutorials/introduction/android_binary_package/images/eclipse_5_import_command.png b/doc/tutorials/introduction/android_binary_package/images/eclipse_5_import_command.png deleted file mode 100644 index f111795014..0000000000 Binary files a/doc/tutorials/introduction/android_binary_package/images/eclipse_5_import_command.png and /dev/null differ diff --git a/doc/tutorials/introduction/android_binary_package/images/eclipse_6_import_existing_projects.png b/doc/tutorials/introduction/android_binary_package/images/eclipse_6_import_existing_projects.png deleted file mode 100644 index c397a834c6..0000000000 Binary files a/doc/tutorials/introduction/android_binary_package/images/eclipse_6_import_existing_projects.png and /dev/null differ diff --git a/doc/tutorials/introduction/android_binary_package/images/eclipse_7_select_projects.png b/doc/tutorials/introduction/android_binary_package/images/eclipse_7_select_projects.png deleted file mode 100644 index e152bc4ed2..0000000000 Binary files a/doc/tutorials/introduction/android_binary_package/images/eclipse_7_select_projects.png and /dev/null differ diff --git a/doc/tutorials/introduction/android_binary_package/images/eclipse_8_false_alarm.png b/doc/tutorials/introduction/android_binary_package/images/eclipse_8_false_alarm.png deleted file mode 100644 index 43295daf8b..0000000000 Binary files a/doc/tutorials/introduction/android_binary_package/images/eclipse_8_false_alarm.png and /dev/null differ diff --git a/doc/tutorials/introduction/android_binary_package/images/eclipse_8a_target.png b/doc/tutorials/introduction/android_binary_package/images/eclipse_8a_target.png deleted file mode 100644 index cee04cdfde..0000000000 Binary files a/doc/tutorials/introduction/android_binary_package/images/eclipse_8a_target.png and /dev/null differ diff --git a/doc/tutorials/introduction/android_binary_package/images/eclipse_8b_fix_props.png b/doc/tutorials/introduction/android_binary_package/images/eclipse_8b_fix_props.png deleted file mode 100644 index 061be3dc61..0000000000 Binary files a/doc/tutorials/introduction/android_binary_package/images/eclipse_8b_fix_props.png and /dev/null differ diff --git a/doc/tutorials/introduction/android_binary_package/images/eclipse_9_errors_dissapearing.png b/doc/tutorials/introduction/android_binary_package/images/eclipse_9_errors_dissapearing.png deleted file mode 100644 index 5a9157237b..0000000000 Binary files a/doc/tutorials/introduction/android_binary_package/images/eclipse_9_errors_dissapearing.png and /dev/null differ diff --git a/doc/tutorials/introduction/android_binary_package/images/eclipse_NDK_build_success.png b/doc/tutorials/introduction/android_binary_package/images/eclipse_NDK_build_success.png deleted file mode 100644 index 0bf2a85287..0000000000 Binary files a/doc/tutorials/introduction/android_binary_package/images/eclipse_NDK_build_success.png and /dev/null differ diff --git a/doc/tutorials/introduction/android_binary_package/images/eclipse_builder_types.png b/doc/tutorials/introduction/android_binary_package/images/eclipse_builder_types.png deleted file mode 100644 index 61673a3699..0000000000 Binary files a/doc/tutorials/introduction/android_binary_package/images/eclipse_builder_types.png and /dev/null differ diff --git a/doc/tutorials/introduction/android_binary_package/images/eclipse_builders.png b/doc/tutorials/introduction/android_binary_package/images/eclipse_builders.png deleted file mode 100644 index 69d90d839d..0000000000 Binary files a/doc/tutorials/introduction/android_binary_package/images/eclipse_builders.png and /dev/null differ diff --git a/doc/tutorials/introduction/android_binary_package/images/eclipse_cdt_cfg1.png b/doc/tutorials/introduction/android_binary_package/images/eclipse_cdt_cfg1.png deleted file mode 100644 index 41e489f974..0000000000 Binary files a/doc/tutorials/introduction/android_binary_package/images/eclipse_cdt_cfg1.png and /dev/null differ diff --git a/doc/tutorials/introduction/android_binary_package/images/eclipse_cdt_cfg2.png b/doc/tutorials/introduction/android_binary_package/images/eclipse_cdt_cfg2.png deleted file mode 100644 index e216b58fe1..0000000000 Binary files a/doc/tutorials/introduction/android_binary_package/images/eclipse_cdt_cfg2.png and /dev/null differ diff --git a/doc/tutorials/introduction/android_binary_package/images/eclipse_cdt_cfg3.png b/doc/tutorials/introduction/android_binary_package/images/eclipse_cdt_cfg3.png deleted file mode 100644 index fd73103b28..0000000000 Binary files a/doc/tutorials/introduction/android_binary_package/images/eclipse_cdt_cfg3.png and /dev/null differ diff --git a/doc/tutorials/introduction/android_binary_package/images/eclipse_cdt_cfg4.png b/doc/tutorials/introduction/android_binary_package/images/eclipse_cdt_cfg4.png deleted file mode 100644 index f8126b5b56..0000000000 Binary files a/doc/tutorials/introduction/android_binary_package/images/eclipse_cdt_cfg4.png and /dev/null differ diff --git a/doc/tutorials/introduction/android_binary_package/images/eclipse_cdt_cfg5.png b/doc/tutorials/introduction/android_binary_package/images/eclipse_cdt_cfg5.png deleted file mode 100644 index e4e6f4fc56..0000000000 Binary files a/doc/tutorials/introduction/android_binary_package/images/eclipse_cdt_cfg5.png and /dev/null differ diff --git a/doc/tutorials/introduction/android_binary_package/images/eclipse_cdt_cfg6.png b/doc/tutorials/introduction/android_binary_package/images/eclipse_cdt_cfg6.png deleted file mode 100644 index 8212ae700e..0000000000 Binary files a/doc/tutorials/introduction/android_binary_package/images/eclipse_cdt_cfg6.png and /dev/null differ diff --git a/doc/tutorials/introduction/android_binary_package/images/eclipse_cdt_cfg7.png b/doc/tutorials/introduction/android_binary_package/images/eclipse_cdt_cfg7.png deleted file mode 100644 index 205a06593c..0000000000 Binary files a/doc/tutorials/introduction/android_binary_package/images/eclipse_cdt_cfg7.png and /dev/null differ diff --git a/doc/tutorials/introduction/android_binary_package/images/eclipse_cdt_cfg8.png b/doc/tutorials/introduction/android_binary_package/images/eclipse_cdt_cfg8.png deleted file mode 100644 index d5253bd9b5..0000000000 Binary files a/doc/tutorials/introduction/android_binary_package/images/eclipse_cdt_cfg8.png and /dev/null differ diff --git a/doc/tutorials/introduction/android_binary_package/images/eclipse_edit_configuration_build_options.png b/doc/tutorials/introduction/android_binary_package/images/eclipse_edit_configuration_build_options.png deleted file mode 100644 index 9a4683d5bf..0000000000 Binary files a/doc/tutorials/introduction/android_binary_package/images/eclipse_edit_configuration_build_options.png and /dev/null differ diff --git a/doc/tutorials/introduction/android_binary_package/images/eclipse_edit_configuration_build_resources.png b/doc/tutorials/introduction/android_binary_package/images/eclipse_edit_configuration_build_resources.png deleted file mode 100644 index 9c3480ea76..0000000000 Binary files a/doc/tutorials/introduction/android_binary_package/images/eclipse_edit_configuration_build_resources.png and /dev/null differ diff --git a/doc/tutorials/introduction/android_binary_package/images/eclipse_edit_configuration_main.png b/doc/tutorials/introduction/android_binary_package/images/eclipse_edit_configuration_main.png deleted file mode 100644 index 4c3bea0c86..0000000000 Binary files a/doc/tutorials/introduction/android_binary_package/images/eclipse_edit_configuration_main.png and /dev/null differ diff --git a/doc/tutorials/introduction/android_binary_package/images/eclipse_edit_configuration_refresh.png b/doc/tutorials/introduction/android_binary_package/images/eclipse_edit_configuration_refresh.png deleted file mode 100644 index 4e36593b98..0000000000 Binary files a/doc/tutorials/introduction/android_binary_package/images/eclipse_edit_configuration_refresh.png and /dev/null differ diff --git a/doc/tutorials/introduction/android_binary_package/images/eclipse_edit_configuration_specify_resources.png b/doc/tutorials/introduction/android_binary_package/images/eclipse_edit_configuration_specify_resources.png deleted file mode 100644 index bce08a8831..0000000000 Binary files a/doc/tutorials/introduction/android_binary_package/images/eclipse_edit_configuration_specify_resources.png and /dev/null differ diff --git a/doc/tutorials/introduction/android_binary_package/images/eclipse_inst_adt.png b/doc/tutorials/introduction/android_binary_package/images/eclipse_inst_adt.png deleted file mode 100644 index 94491e0a61..0000000000 Binary files a/doc/tutorials/introduction/android_binary_package/images/eclipse_inst_adt.png and /dev/null differ diff --git a/doc/tutorials/introduction/android_binary_package/images/eclipse_inst_cdt.png b/doc/tutorials/introduction/android_binary_package/images/eclipse_inst_cdt.png deleted file mode 100644 index ab34a6783f..0000000000 Binary files a/doc/tutorials/introduction/android_binary_package/images/eclipse_inst_cdt.png and /dev/null differ diff --git a/doc/tutorials/introduction/android_binary_package/images/eclipse_inst_cdt_2.png b/doc/tutorials/introduction/android_binary_package/images/eclipse_inst_cdt_2.png deleted file mode 100644 index 3cb773e79b..0000000000 Binary files a/doc/tutorials/introduction/android_binary_package/images/eclipse_inst_cdt_2.png and /dev/null differ diff --git a/doc/tutorials/introduction/android_binary_package/images/eclipse_ndk_build.png b/doc/tutorials/introduction/android_binary_package/images/eclipse_ndk_build.png deleted file mode 100644 index 0fdc59a9fc..0000000000 Binary files a/doc/tutorials/introduction/android_binary_package/images/eclipse_ndk_build.png and /dev/null differ diff --git a/doc/tutorials/introduction/android_binary_package/images/eclipse_opencv_dependency0.png b/doc/tutorials/introduction/android_binary_package/images/eclipse_opencv_dependency0.png deleted file mode 100644 index 5c5673b481..0000000000 Binary files a/doc/tutorials/introduction/android_binary_package/images/eclipse_opencv_dependency0.png and /dev/null differ diff --git a/doc/tutorials/introduction/android_binary_package/images/eclipse_opencv_dependency1.png b/doc/tutorials/introduction/android_binary_package/images/eclipse_opencv_dependency1.png deleted file mode 100644 index 5fc63da9ba..0000000000 Binary files a/doc/tutorials/introduction/android_binary_package/images/eclipse_opencv_dependency1.png and /dev/null differ diff --git a/doc/tutorials/introduction/android_binary_package/images/eclipse_windows_environment.png b/doc/tutorials/introduction/android_binary_package/images/eclipse_windows_environment.png deleted file mode 100644 index 5deced55c9..0000000000 Binary files a/doc/tutorials/introduction/android_binary_package/images/eclipse_windows_environment.png and /dev/null differ diff --git a/doc/tutorials/introduction/android_binary_package/images/emulator_canny.png b/doc/tutorials/introduction/android_binary_package/images/emulator_canny.png deleted file mode 100644 index d08340be9c..0000000000 Binary files a/doc/tutorials/introduction/android_binary_package/images/emulator_canny.png and /dev/null differ diff --git a/doc/tutorials/introduction/android_binary_package/images/gradle_ocv_fix.png b/doc/tutorials/introduction/android_binary_package/images/gradle_ocv_fix.png new file mode 100644 index 0000000000..abaddce00f Binary files /dev/null and b/doc/tutorials/introduction/android_binary_package/images/gradle_ocv_fix.png differ diff --git a/doc/tutorials/introduction/android_binary_package/images/module_gradle_fix.png b/doc/tutorials/introduction/android_binary_package/images/module_gradle_fix.png new file mode 100644 index 0000000000..235f012e3c Binary files /dev/null and b/doc/tutorials/introduction/android_binary_package/images/module_gradle_fix.png differ diff --git a/doc/tutorials/introduction/android_binary_package/images/module_name.png b/doc/tutorials/introduction/android_binary_package/images/module_name.png new file mode 100644 index 0000000000..e0c85b0ec7 Binary files /dev/null and b/doc/tutorials/introduction/android_binary_package/images/module_name.png differ diff --git a/doc/tutorials/introduction/android_binary_package/images/ndk_build.png b/doc/tutorials/introduction/android_binary_package/images/ndk_build.png deleted file mode 100644 index 633d0684e9..0000000000 Binary files a/doc/tutorials/introduction/android_binary_package/images/ndk_build.png and /dev/null differ diff --git a/doc/tutorials/introduction/android_binary_package/images/run_app.png b/doc/tutorials/introduction/android_binary_package/images/run_app.png new file mode 100644 index 0000000000..97529779b7 Binary files /dev/null and b/doc/tutorials/introduction/android_binary_package/images/run_app.png differ diff --git a/doc/tutorials/introduction/android_binary_package/images/sample_code.png b/doc/tutorials/introduction/android_binary_package/images/sample_code.png new file mode 100644 index 0000000000..e800184ae0 Binary files /dev/null and b/doc/tutorials/introduction/android_binary_package/images/sample_code.png differ diff --git a/doc/tutorials/introduction/android_binary_package/images/sdk_ndk_manager.png b/doc/tutorials/introduction/android_binary_package/images/sdk_ndk_manager.png new file mode 100644 index 0000000000..fcd1ad0060 Binary files /dev/null and b/doc/tutorials/introduction/android_binary_package/images/sdk_ndk_manager.png differ diff --git a/doc/tutorials/introduction/android_binary_package/images/sdk_path.png b/doc/tutorials/introduction/android_binary_package/images/sdk_path.png new file mode 100644 index 0000000000..0f8fbd7703 Binary files /dev/null and b/doc/tutorials/introduction/android_binary_package/images/sdk_path.png differ diff --git a/doc/tutorials/introduction/android_binary_package/images/setup_project.png b/doc/tutorials/introduction/android_binary_package/images/setup_project.png new file mode 100644 index 0000000000..5586b1786f Binary files /dev/null and b/doc/tutorials/introduction/android_binary_package/images/setup_project.png differ diff --git a/doc/tutorials/introduction/android_binary_package/images/usb_device_connect_13.png b/doc/tutorials/introduction/android_binary_package/images/usb_device_connect_13.png deleted file mode 100644 index 4d7c84fbc7..0000000000 Binary files a/doc/tutorials/introduction/android_binary_package/images/usb_device_connect_13.png and /dev/null differ diff --git a/doc/tutorials/introduction/android_binary_package/images/usb_device_connect_ubuntu.png b/doc/tutorials/introduction/android_binary_package/images/usb_device_connect_ubuntu.png deleted file mode 100644 index fb9f72a45f..0000000000 Binary files a/doc/tutorials/introduction/android_binary_package/images/usb_device_connect_ubuntu.png and /dev/null differ diff --git a/doc/tutorials/introduction/building_tegra_cuda/building_tegra_cuda.markdown b/doc/tutorials/introduction/building_tegra_cuda/building_tegra_cuda.markdown index a56c0fa17f..753b3efe58 100644 --- a/doc/tutorials/introduction/building_tegra_cuda/building_tegra_cuda.markdown +++ b/doc/tutorials/introduction/building_tegra_cuda/building_tegra_cuda.markdown @@ -40,7 +40,7 @@ Getting the Source Code {#tutorial_building_tegra_cuda_getting_the_code} There are two (2) ways to get the OpenCV source code: -* Direct download from the [OpenCV downloads](http://opencv.org/releases.html) page +* Direct download from the [OpenCV downloads](https://opencv.org/releases) page * Cloning the git repositories hosted on [GitHub](https://github.com/opencv) For this guide, the focus is on using the git repositories. This is because the 3.1.0 version of OpenCV will not build with CUDA 8.0 without applying a few small upstream changes from the git repository. diff --git a/doc/tutorials/introduction/config_reference/config_reference.markdown b/doc/tutorials/introduction/config_reference/config_reference.markdown index 258b1b8c16..09742ca9ba 100644 --- a/doc/tutorials/introduction/config_reference/config_reference.markdown +++ b/doc/tutorials/introduction/config_reference/config_reference.markdown @@ -583,6 +583,7 @@ Following options can be used to change installation layout for common scenarios | `BUILD_JAVA` | _ON_ | Enable Java wrappers build. Java SDK and Ant must be installed. | | `BUILD_FAT_JAVA_LIB` | _ON_ (for static Android builds) | Build single _opencv_java_ dynamic library containing all library functionality bundled with Java bindings. | | `BUILD_opencv_python3` | _ON_ | Build python3 bindings. Python with development files and numpy must be installed. | +| `CAROTENE_NEON_ARCH` | '(auto)' | Switch NEON Arch for Carotene. If it sets nothing, it will be auto-detected. If it sets 8, ARMv8(and later) is used. Otherwise, ARMv7 is used. | TODO: need separate tutorials covering bindings builds diff --git a/doc/tutorials/introduction/crosscompilation/arm_crosscompile_with_cmake.markdown b/doc/tutorials/introduction/crosscompilation/arm_crosscompile_with_cmake.markdown index 058b5c92f2..33704d5bca 100644 --- a/doc/tutorials/introduction/crosscompilation/arm_crosscompile_with_cmake.markdown +++ b/doc/tutorials/introduction/crosscompilation/arm_crosscompile_with_cmake.markdown @@ -2,7 +2,7 @@ Cross compilation for ARM based Linux systems {#tutorial_arm_crosscompile_with_c ============================================= @prev_tutorial{tutorial_macos_install} -@next_tutorial{tutorial_building_tegra_cuda} +@next_tutorial{tutorial_crosscompile_with_multiarch} | | | | -: | :- | diff --git a/doc/tutorials/introduction/crosscompilation/crosscompile_with_multiarch.markdown b/doc/tutorials/introduction/crosscompilation/crosscompile_with_multiarch.markdown new file mode 100644 index 0000000000..2ae3e24e38 --- /dev/null +++ b/doc/tutorials/introduction/crosscompilation/crosscompile_with_multiarch.markdown @@ -0,0 +1,595 @@ +# MultiArch cross-compilation with Ubuntu/Debian{#tutorial_crosscompile_with_multiarch} + +@prev_tutorial{tutorial_arm_crosscompile_with_cmake} +@next_tutorial{tutorial_building_tegra_cuda} + +[TOC] + +| | | +| -: | :- | +| Original author | Kumataro | +| Compatibility | Ubuntu >=23.04 | +|^ | OpenCV >=4.8.0 | + +@warning +This tutorial may contain obsolete information. + +## What is "MultiArch" + +OpenCV may use a lot of 3rdparty libraries for video and image decoding, rendering, acceleration +and complex math algorithms. The 3rd party components are found by CMake on the build host +cross-compilation allows to build OpenCV for foreign architecture or OS, but we loose that large +world of components and have to cross-compile each dependency separately and point to it during +OpenCV build. + +Debian/Ubuntu MultiArch helps to fix this. It allows to install several foreign architecture +libraries on host system and use them during OpenCV dependencies resolution. + +@warning +- Following these steps will make your Linux environment a little dirty. + If possible, it is better to use VMs or Container(e.g. Docker). +- This tutorial expects host and target uses same Ubuntu version. + Do not use/mix different versions for external library dependency. + - Good: Host and Target are 23.04. + - Good: Host and Target are 23.10. + - Not Good: Host is 23.04, and Target is 23.10. + - Not Good: Host is 23.10, and Target is 23.04. +- This tutorial may be used for Debian and its derivatives like Raspberry Pi OS. Please make any +necessary changes. + +## Download tools + +Install necessary tools and toolchains for cross-compilation. + +- git, cmake, pkgconf and build-essential are required basically. +- ninja-build is to reduce compilation time(option). +- crossbuild-essential-armhf is toolchain package for armv7 target. +- crossbuild-essential-arm64 is toolchain package for aarch64 target. + +@code{.bash} +sudo apt update -y +sudo apt install -y \ + git \ + cmake \ + pkgconf \ + build-essential \ + ninja-build \ + crossbuild-essential-armhf \ + crossbuild-essential-arm64 +@endcode + +If you want to enable Python 3 wrapper, install these packages too. + +@code{.bash} +sudo apt install -y \ + python3-minimal \ + python3-numpy +@endcode + +## Working folder structure + +In this tutorial, following working folder structure are used. + +@code{.unparsed} +/home + + kmtr - please replace your account name. + + work + + opencv - source, cloned from github + + opencv_contrib - source, cloned from github + + build4-full_arm64 - artifact(for aarch64 target), created by cmake + + build4-full_armhf - artifact(for armhf target), created by cmake +@endcode + +1. Create working folder under your home directory. +2. Clone OpenCV and OpenCV Contrib from repository to work directory. + +@code{.bash} +cd ~ +mkdir work +cd work +git clone --depth=1 https://github.com/opencv/opencv.git +git clone --depth=1 https://github.com/opencv/opencv_contrib.git +@endcode + +## Update apt and dpkg settings + +These steps are on host. + +`apt` and `dpkg` are package management systems used in Ubuntu and Debian. + +Following are setup steps to use MultiArch. + +### Step 1. Add apt source for arm64 and armhf + +Execute `sudo apt edit-sources` to add foreign arch libraries at end of file. + +Example 1: arm64 and armv7 for Ubuntu 23.04 + +@code{.unparsed} +deb [arch=arm64,armhf] http://ports.ubuntu.com/ubuntu-ports lunar main restricted +deb [arch=arm64,armhf] http://ports.ubuntu.com/ubuntu-ports lunar-updates main restricted +deb [arch=arm64,armhf] http://ports.ubuntu.com/ubuntu-ports lunar universe +deb [arch=arm64,armhf] http://ports.ubuntu.com/ubuntu-ports lunar-updates universe +deb [arch=arm64,armhf] http://ports.ubuntu.com/ubuntu-ports lunar multiverse +deb [arch=arm64,armhf] http://ports.ubuntu.com/ubuntu-ports lunar-updates multiverse +deb [arch=arm64,armhf] http://ports.ubuntu.com/ubuntu-ports lunar-backports main restricted universe multiverse +deb [arch=arm64,armhf] http://ports.ubuntu.com/ubuntu-ports lunar-security main restricted +deb [arch=arm64,armhf] http://ports.ubuntu.com/ubuntu-ports lunar-security universe +deb [arch=arm64,armhf] http://ports.ubuntu.com/ubuntu-ports lunar-security multiverse +@endcode + +Example 2: arm64 and armv7 for Ubuntu 23.10 + +@code{.unparsed} +deb [arch=arm64,armhf] http://ports.ubuntu.com/ubuntu-ports mantic main restricted +deb [arch=arm64,armhf] http://ports.ubuntu.com/ubuntu-ports mantic-updates main restricted +deb [arch=arm64,armhf] http://ports.ubuntu.com/ubuntu-ports mantic universe +deb [arch=arm64,armhf] http://ports.ubuntu.com/ubuntu-ports mantic-updates universe +deb [arch=arm64,armhf] http://ports.ubuntu.com/ubuntu-ports mantic multiverse +deb [arch=arm64,armhf] http://ports.ubuntu.com/ubuntu-ports mantic-updates multiverse +deb [arch=arm64,armhf] http://ports.ubuntu.com/ubuntu-ports mantic-backports main restricted universe multiverse +deb [arch=arm64,armhf] http://ports.ubuntu.com/ubuntu-ports mantic-security main restricted +deb [arch=arm64,armhf] http://ports.ubuntu.com/ubuntu-ports mantic-security universe +deb [arch=arm64,armhf] http://ports.ubuntu.com/ubuntu-ports mantic-security multiverse +@endcode + +### Step 2. Update apt database + +Update apt database to apply new apt sources. + +Execute `sudo apt update`. + +@code{.bash} +sudo apt update +@endcode + +### Step 3. Update dpkg settings + +Update dpkg settings to support foreign architectures. + +Execute `sudo dpkg --add-architecture arm64` and/or `sudo dpkg --add-architecture armhf`. + +@code{.bash} +sudo dpkg --add-architecture arm64 +sudo dpkg --add-architecture armhf +@endcode + +`sudo dpkg --print-architecture` shows what is host architecture. + +@code{.bash} +sudo dpkg --print-architecture +amd64 +@endcode + +And `sudo dpkg --print-foreign-architectures` shows what foreign architectures are supported. + +@code{.bash} +sudo dpkg --print-foreign-architectures +arm64 +armhf +@endcode + +### Confirm working pkg-config + +With MultiArch, several shared libraries and pkg-config information for each architectures are stored into /usr/lib. + +@code{.unparsed} +/usr + + lib + + aarch64-linux-gnu - shared libraries for arm64 + + pkgconfig - pkg-config files for arm64 libraries + + arm-linux-gnueabihf - shared libraries for armhf + + pkgconfig - pkg-config files for armhf libraries + + share + + pkgconfig - pkg-config files(for header files) +@endcode + +Confirm to work `pkg-config` using `PKG_CONFIG_PATH`, `PKG_CONFIG_LIBDIR` and `PKG_CONFIG_SYSROOT_DIR` options. + +for aarch64: + +@code{.bash} +PKG_CONFIG_PATH=/usr/lib/aarch64-linux-gnu/pkgconfig:/usr/share/pkgconfig \ + PKG_CONFIG_LIBDIR=/usr/lib/aarch64-linux-gnu \ + PKG_CONFIG_SYSROOT_DIR=/ \ + pkg-config --list-all +@endcode + +for armv7: + +@code{.bash} +PKG_CONFIG_PATH=/usr/lib/arm-linux-gnueabihf/pkgconfig:/usr/share/pkgconfig \ + PKG_CONFIG_LIBDIR=/usr/lib/arm-linux-gnueabihf \ + PKG_CONFIG_SYSROOT_DIR=/ \ + pkg-config --list-all +@endcode + +## Cross-compile for aarch64 + +Following is to compile for target (aarch64) at host (x86-64). + +### Step 1. Install external libraries for target into host + +This step is on host. + +Install libfreetype-dev, libharfbuzz-dev and FFmpeg packages for target (arm64) into host (x86-64). + +@code{.bash} +sudo apt install -y \ + libavcodec-dev:arm64 \ + libavformat-dev:arm64 \ + libavutil-dev:arm64 \ + libswscale-dev:arm64 \ + libfreetype-dev:arm64 \ + libharfbuzz-dev:arm64 +@endcode + +If you want to enable Python 3 wrapper, install these packages too. + +@code{.bash} +sudo apt install -y \ + libpython3-dev:arm64 +@endcode + +If succeed, pkg-config can show information about these packages. + +For Freetype2 and Harfbuzz: + +@code{.bash} +PKG_CONFIG_PATH=/usr/lib/aarch64-linux-gnu/pkgconfig:/usr/share/pkgconfig \ + PKG_CONFIG_LIBDIR=/usr/lib/aarch64-linux-gnu \ + PKG_CONFIG_SYSROOT_DIR=/ \ + pkg-config freetype2 harfbuzz --cflags --libs +-I/usr/include/freetype2 -I/usr/include/libpng16 -I/usr/include/harfbuzz -I/usr/include/glib-2.0 -I/usr/lib/aarch64-linux-gnu/glib-2.0/include -L/usr/lib/aarch64-linux-gnu -lfreetype -lharfbuzz +@endcode + +For FFmpeg: + +@code{.bash} +PKG_CONFIG_PATH=/usr/lib/aarch64-linux-gnu/pkgconfig:/usr/share/pkgconfig \ + PKG_CONFIG_LIBDIR=/usr/lib/aarch64-linux-gnu \ + PKG_CONFIG_SYSROOT_DIR=/ \ + pkg-config libavcodec libavformat libavutil libswscale --cflags --libs +-I/usr/include/aarch64-linux-gnu -L/usr/lib/aarch64-linux-gnu -lavcodec -lavformat -lavutil -lswscale +@endcode + +### Step 2. Configure OpenCV Settings +This step is on host. + +Execute `cmake` to make cross-compile configuration for aarch64. + +@note `-DCMAKE_TOOLCHAIN_FILE` should be absolute/real file path, not relative path. + +@code{.bash} +PKG_CONFIG_PATH=/usr/lib/aarch64-linux-gnu/pkgconfig:/usr/share/pkgconfig \ + PKG_CONFIG_LIBDIR=/usr/lib/aarch64-linux-gnu \ + PKG_CONFIG_SYSROOT_DIR=/ \ + cmake -S opencv \ + -B build4-full_arm64 \ + -DCMAKE_TOOLCHAIN_FILE=/home/kmtr/work/opencv/platforms/linux/aarch64-gnu.toolchain.cmake \ + -DOPENCV_EXTRA_MODULES_PATH=opencv_contrib/modules \ + -GNinja +@endcode + +If you want to enable Python 3 wrapper, extra options are needed. + +@code{.bash} +PYTHON3_REALPATH=`realpath /usr/bin/python3` +PYTHON3_BASENAME=`basename ${PYTHON3_REALPATH}` +PKG_CONFIG_PATH=/usr/lib/aarch64-linux-gnu/pkgconfig:/usr/share/pkgconfig \ + PKG_CONFIG_LIBDIR=/usr/lib/aarch64-linux-gnu \ + PKG_CONFIG_SYSROOT_DIR=/ \ + cmake -S opencv \ + -B build4-full_arm64 \ + -DCMAKE_TOOLCHAIN_FILE=/home/kmtr/work/opencv/platforms/linux/aarch64-gnu.toolchain.cmake \ + -DOPENCV_EXTRA_MODULES_PATH=opencv_contrib/modules \ + -DPYTHON3_NUMPY_INCLUDE_DIRS="/usr/local/lib/${PYTHON3_BASENAME}/dist-packages/numpy/core/include/" \ + -DPYTHON3_INCLUDE_PATH="/usr/include/${PYTHON3_BASENAME};/usr/include/" \ + -DPYTHON3_LIBRARIES=`find /usr/lib/aarch64-linux-gnu/ -name libpython*.so` \ + -DPYTHON3_EXECUTABLE="/usr/bin/${PYTHON3_BASENAME}" \ + -DPYTHON3_CVPY_SUFFIX=".so" \ + -GNinja +@endcode + +@note +@parblock +Lastly, "python3.XX" string is needed. So this script generate it. +- Get real path from "/usr/bin/python3" to "/usr/bin/python3.xx". +- Get base name from "/usr/bin/python3.xx" to "pyhton3.xx". +@endparblock + +Following is cmake outputs. +- `Host` is `Linux x86_64`. +- `Target` is `Linux aarch64`. +- FFmpeg is available. + +@code{.unparsed} +-- General configuration for OpenCV 4.8.0-dev ===================================== +-- Version control: 408730b +-- +-- Extra modules: +-- Location (extra): /home/kmtr/work/opencv_contrib/modules +-- Version control (extra): faa5468 +-- +-- Platform: +-- Timestamp: 2023-12-01T22:02:14Z +-- Host: Linux 6.5.0-13-generic x86_64 +-- Target: Linux 1 aarch64 +-- CMake: 3.27.4 +-- CMake generator: Ninja +-- CMake build tool: /usr/bin/ninja +-- Configuration: Release +-- +-- CPU/HW features: +-- Baseline: NEON FP16 +-- required: NEON +-- disabled: VFPV3 +-- Dispatched code generation: NEON_DOTPROD NEON_FP16 NEON_BF16 +-- requested: NEON_FP16 NEON_BF16 NEON_DOTPROD +-- NEON_DOTPROD (1 files): + NEON_DOTPROD +-- NEON_FP16 (2 files): + NEON_FP16 +-- NEON_BF16 (0 files): + NEON_BF16 +-- +-- C/C++: +-- Built as dynamic libs?: YES +-- C++ standard: 11 +-- C++ Compiler: /usr/bin/aarch64-linux-gnu-g++ (ver 13.2.0) + +: +: + +-- +-- Video I/O: +-- DC1394: NO +-- FFMPEG: YES +-- avcodec: YES (60.3.100) +-- avformat: YES (60.3.100) +-- avutil: YES (58.2.100) +-- swscale: YES (7.1.100) +-- avresample: NO +-- GStreamer: NO +-- v4l/v4l2: YES (linux/videodev2.h) +-- +@endcode + +If enabling Python 3 wrapper is succeeded, `Python 3:` section shows more. + +@code{.unparsed} +-- +-- Python 3: +-- Interpreter: /usr/bin/python3.11 (ver 3.11.6) +-- Libraries: /usr/lib/aarch64-linux-gnu/libpython3.11.so +-- numpy: /usr/local/lib/python3.11/dist-packages/numpy/core/include/ (ver undefined - cannot be probed because of the cross-compilation) +-- install path: lib/python3.11/dist-packages/cv2/python-3.11 +-- +-- Python (for build): /usr/bin/python3.11 +-- +@endcode + +### Step 3. Build and archive OpenCV libraries and headers + +This step in in host. + +Build and install. +(This `install` means only that copying artifacts to `install` folder.) + +@code{.bash} + cmake --build build4-full_arm64 +sudo cmake --install build4-full_arm64 +@endcode + +Archive artifacts(built libraries and headers) to `opencv_arm64.tgz` with tar command. + +@code{.bash} +tar czvf opencv_arm64.tgz -C build4-full_arm64/install . +@endcode + +And send `opencv_arm64.tgz` to target. + +### Step 4. Install dependency libraries at target + +This step is executed on the target system. + +Install dependency run-time libraries for OpenCV/OpenCV contrib libraries at target. + +@code{.bash} +sudo apt install -y \ + libavcodec60 \ + libavformat60 \ + libavutil58 \ + libswscale7 \ + libfreetype6 \ + libharfbuzz0b + +sudo ldconfig +@endcode + +If you want to enable Python 3 wrapper, install these packages too. + +@code{.bash} +sudo apt install -y \ + python3-minimal \ + python3-numpy +@endcode + +@warning +@parblock +If version of runtime libraries and/or programs are incremented, apt package names may be changed +(e.g. `libswscale6` is used for Ubuntu 23.04, but `libswscale7` is used for Ubuntu 23.10). +Looking for it with `apt search` command or https://packages.ubuntu.com/ . +@endparblock + +@warning +@parblock +External library version between host and target should be same. +Please update to the latest version libraries at the same time as possible. + +Even if the OS versions are the same between the Host and Target, +the versions may differ due to additional updates to the libraries. +This will cause unexpected problems. + +For example) +- On Host, OpenCV has been build with external libA (v1.0) for target. +- libA (v1.1) may be updated. +- On Target, libA (v1.1) is installed to use OpenCV. +- In this case, versions of libA is difference between compiling and running. +@endparblock + +@warning +@parblock +If you forget/mismatch to install some necessary libraries, OpenCV will not works well. + +`ldd` command can detect dependency. If there are any "not found", please install necessary libraries. + +@code{.bash} +ldd /usr/local/lib/libopencv_freetype.so +@endcode + +(Not Good) `freetype module` requires `libharfbuzz.so.0`, but it has not been installed. +@code{.unparsed} + linux-vdso.so.1 (0xABCDEFG01234567) + libopencv_imgproc.so.408 => /usr/local/lib/libopencv_imgproc.so.408 (0xABCDEF001234567) + libfreetype.so.6 => /lib/aarch64-linux-gnu/libfreetype.so.6 (0xABCDEF001234567) + libharfbuzz.so.0 => not found + libopencv_core.so.408 => /usr/local/lib/libopencv_core.so.408 (0xABCDEF001234567) + : +@endcode + +(Good) All libraries which are required from `freetype modules` are installed. +@code{.unparsed} + linux-vdso.so.1 (0xABCDEFG01234567) + libopencv_imgproc.so.408 => /usr/local/lib/libopencv_imgproc.so.408 (0xABCDEF001234567) + libfreetype.so.6 => /lib/aarch64-linux-gnu/libfreetype.so.6 (0xABCDEF001234567) + libharfbuzz.so.0 => /lib/aarch64-linux-gnu/libharfbuzz.so.0 (0xABCDEF001234567) + libopencv_core.so.408 => /usr/local/lib/libopencv_core.so.408 (0xABCDEF001234567) + : +@endcode +@endparblock + +### Step 5. Install OpenCV libraries to target + +This step is on target. + +Receive `opencv_arm64.tgz` from host (generated at Step3), and extract to `/usr/local`. + +@code{.bash} +sudo tar zxvf opencv_arm64.tgz -C /usr/local +sudo ldconfig +@endcode + +You can use OpenCV libraries same as self-compiling. Following is OpenCV sample code. Compile and +run it on target. + +Makefile +@code{.make} +a.out : main.cpp + g++ main.cpp -o a.out \ + -I/usr/local/include/opencv4 \ + -lopencv_core +@endcode + +main.cpp +@code{.cpp} +#include +#include +int main(void) +{ + std::cout << cv::getBuildInformation() << std::endl; + return 0; +} +@endcode + +Execute `make` and run it. +@code{.bash} +make a.out +./a.out +@endcode + +If you want to enable Python 3 wrapper, execute following command to confirm. + +@code{.bash} +python3 -c "import cv2; print(cv2.getBuildInformation())" +@endcode + +## Cross-compile for armv7 + +Following is to compile for target (armhf) at host (x86-64). + +- To resolve dependencies, `linux-libc-dev:armhf` is required. +- To optimize with neon, `-DENABLE_NEON=ON` is needed. + +@code{.bash} +sudo apt install -y \ + linux-libc-dev:armhf \ + libavcodec-dev:armhf \ + libavformat-dev:armhf \ + libavutil-dev:armhf \ + libswscale-dev:armhf \ + libfreetype-dev:armhf \ + libharfbuzz-dev:armhf + +PKG_CONFIG_PATH=/usr/lib/arm-linux-gnueabihf/pkgconfig:/usr/share/pkgconfig \ + PKG_CONFIG_LIBDIR=/usr/lib/arm-linux-gnueabihf \ + PKG_CONFIG_SYSROOT_DIR=/ \ + cmake -S opencv \ + -B build4-full_armhf \ + -DENABLE_NEON=ON \ + -DCMAKE_TOOLCHAIN_FILE=/home/kmtr/work/opencv/platforms/linux/arm-gnueabi.toolchain.cmake \ + -DOPENCV_EXTRA_MODULES_PATH=opencv_contrib/modules \ + -GNinja + +cmake --build build4-full_armhf +sudo cmake --install build4-full_armhf +tar czvf opencv_armhf.tgz -C build4-full_armhf/install . +@endcode + +Following is cmake outputs. +- `Host` is `Linux x86_64`. +- `Target` is `Linux arm`. +- FFmpeg is available. + +@code{.unparsed} +-- General configuration for OpenCV 4.8.0-dev ===================================== +-- Version control: 408730b +-- +-- Extra modules: +-- Location (extra): /home/kmtr/work/opencv_contrib/modules +-- Version control (extra): faa5468 +-- +-- Platform: +-- Timestamp: 2023-12-02T03:39:58Z +-- Host: Linux 6.5.0-13-generic x86_64 +-- Target: Linux 1 arm +-- CMake: 3.27.4 +-- CMake generator: Ninja +-- CMake build tool: /usr/bin/ninja +-- Configuration: Release +-- +-- CPU/HW features: +-- Baseline: NEON +-- requested: DETECT +-- required: NEON +-- disabled: VFPV3 +-- +-- C/C++: +-- Built as dynamic libs?: YES +-- C++ standard: 11 +-- C++ Compiler: /usr/bin/arm-linux-gnueabihf-g++ (ver 13.2.0) + +: +: + +-- +-- Video I/O: +-- DC1394: NO +-- FFMPEG: YES +-- avcodec: YES (60.3.100) +-- avformat: YES (60.3.100) +-- avutil: YES (58.2.100) +-- swscale: YES (7.1.100) +-- avresample: NO +-- GStreamer: NO +-- v4l/v4l2: YES (linux/videodev2.h) +-- + +@endcode diff --git a/doc/tutorials/introduction/java_eclipse/java_eclipse.markdown b/doc/tutorials/introduction/java_eclipse/java_eclipse.markdown index 9d9434adf7..46e2aac5d1 100644 --- a/doc/tutorials/introduction/java_eclipse/java_eclipse.markdown +++ b/doc/tutorials/introduction/java_eclipse/java_eclipse.markdown @@ -21,7 +21,7 @@ less mistakes. Here we go. Configuring Eclipse ------------------- -First, obtain a fresh release of OpenCV [from download page](http://opencv.org/releases.html) and +First, obtain a fresh release of OpenCV [from download page](https://opencv.org/releases) and extract it under a simple location like `C:\OpenCV-2.4.6\`. I am using version 2.4.6, but the steps are more or less the same for other versions. diff --git a/doc/tutorials/introduction/linux_install/linux_install.markdown b/doc/tutorials/introduction/linux_install/linux_install.markdown index f3a60145d0..6419a4817e 100644 --- a/doc/tutorials/introduction/linux_install/linux_install.markdown +++ b/doc/tutorials/introduction/linux_install/linux_install.markdown @@ -65,7 +65,7 @@ There are two methods of getting OpenCV sources: @note -Snapshots of other branches, releases or commits can be found on the [GitHub](https://github.com/opencv/opencv) and the [official download page](https://opencv.org/releases.html). +Snapshots of other branches, releases or commits can be found on the [GitHub](https://github.com/opencv/opencv) and the [official download page](https://opencv.org/releases). ## Configure and build {#tutorial_linux_install_detailed_basic_build} diff --git a/doc/tutorials/introduction/macos_install/macos_install.markdown b/doc/tutorials/introduction/macos_install/macos_install.markdown index f170b47fec..80ce08ef6d 100644 --- a/doc/tutorials/introduction/macos_install/macos_install.markdown +++ b/doc/tutorials/introduction/macos_install/macos_install.markdown @@ -53,7 +53,7 @@ You can use the latest stable OpenCV version or you can grab the latest snapshot ### Getting the Latest Stable OpenCV Version -- Go to our [downloads page](http://opencv.org/releases.html). +- Go to our [downloads page](https://opencv.org/releases). - Download the source archive and unpack it. ### Getting the Cutting-edge OpenCV from the Git Repository diff --git a/doc/tutorials/introduction/table_of_content_introduction.markdown b/doc/tutorials/introduction/table_of_content_introduction.markdown index 4df7ad2c78..22d9688291 100644 --- a/doc/tutorials/introduction/table_of_content_introduction.markdown +++ b/doc/tutorials/introduction/table_of_content_introduction.markdown @@ -21,13 +21,14 @@ Introduction to OpenCV {#tutorial_table_of_content_introduction} - @subpage tutorial_java_eclipse - @subpage tutorial_clojure_dev_intro - @subpage tutorial_android_dev_intro -- @subpage tutorial_O4A_SDK - @subpage tutorial_dev_with_OCV_on_Android +- @subpage tutorial_android_dnn_intro - @subpage tutorial_android_ocl_intro ##### Other platforms - @subpage tutorial_macos_install - @subpage tutorial_arm_crosscompile_with_cmake +- @subpage tutorial_crosscompile_with_multiarch - @subpage tutorial_building_tegra_cuda - @ref tutorial_ios_install diff --git a/doc/tutorials/introduction/transition_guide/transition_guide.markdown b/doc/tutorials/introduction/transition_guide/transition_guide.markdown index 454d3ca051..43bc5dd958 100644 --- a/doc/tutorials/introduction/transition_guide/transition_guide.markdown +++ b/doc/tutorials/introduction/transition_guide/transition_guide.markdown @@ -201,7 +201,7 @@ All specialized `ocl` implementations has been hidden behind general C++ algorit New class cv::UMat is intended to hide data exchange with OpenCL device in a convenient way. -Following example illustrate API modifications (from [OpenCV site](http://opencv.org/platforms/opencl.html)): +Following example illustrate API modifications (from [OpenCV site](https://opencv.org/opencl)): - OpenCL-aware code OpenCV-2.x @code{.cpp} diff --git a/modules/3d/include/opencv2/3d.hpp b/modules/3d/include/opencv2/3d.hpp index 3e1dcd72f5..ceef7f615b 100644 --- a/modules/3d/include/opencv2/3d.hpp +++ b/modules/3d/include/opencv2/3d.hpp @@ -828,7 +828,7 @@ and a rotation matrix. It optionally returns three rotation matrices, one for each axis, and the three Euler angles in degrees (as the return value) that could be used in OpenGL. Note, there is always more than one sequence of rotations about the three principal axes that results in the same orientation of an -object, e.g. see @cite Slabaugh . Returned tree rotation matrices and corresponding three Euler angles +object, e.g. see @cite Slabaugh . Returned three rotation matrices and corresponding three Euler angles are only one of the possible solutions. */ CV_EXPORTS_W Vec3d RQDecomp3x3( InputArray src, OutputArray mtxR, OutputArray mtxQ, @@ -854,7 +854,7 @@ matrix and the position of a camera. It optionally returns three rotation matrices, one for each axis, and three Euler angles that could be used in OpenGL. Note, there is always more than one sequence of rotations about the three principal axes that results in the same orientation of an object, e.g. see @cite Slabaugh . Returned -tree rotation matrices and corresponding three Euler angles are only one of the possible solutions. +three rotation matrices and corresponding three Euler angles are only one of the possible solutions. The function is based on #RQDecomp3x3 . */ @@ -2322,7 +2322,6 @@ void initInverseRectificationMap( InputArray cameraMatrix, InputArray distCoeffs InputArray R, InputArray newCameraMatrix, const Size& size, int m1type, OutputArray map1, OutputArray map2 ); - //! initializes maps for #remap for wide-angle CV_EXPORTS float initWideAngleProjMap(InputArray cameraMatrix, InputArray distCoeffs, diff --git a/modules/calib/include/opencv2/calib.hpp b/modules/calib/include/opencv2/calib.hpp index cd0ca88559..edf7c2779d 100644 --- a/modules/calib/include/opencv2/calib.hpp +++ b/modules/calib/include/opencv2/calib.hpp @@ -411,7 +411,8 @@ enum { CALIB_CB_ADAPTIVE_THRESH = 1, CALIB_CB_EXHAUSTIVE = 16, CALIB_CB_ACCURACY = 32, CALIB_CB_LARGER = 64, - CALIB_CB_MARKER = 128 + CALIB_CB_MARKER = 128, + CALIB_CB_PLAIN = 256 }; enum { CALIB_CB_SYMMETRIC_GRID = 1, @@ -502,6 +503,10 @@ square-like shape) to filter out false quads extracted at the contour retrieval - @ref CALIB_CB_FAST_CHECK Run a fast check on the image that looks for chessboard corners, and shortcut the call if none is found. This can drastically speed up the call in the degenerate condition when no chessboard is observed. +- @ref CALIB_CB_PLAIN All other flags are ignored. The input image is taken as is. +No image processing is done to improve to find the checkerboard. This has the effect of speeding up the +execution of the function but could lead to not recognizing the checkerboard if the image +is not previously binarized in the appropriate manner. The function attempts to determine whether the input image is a view of the chessboard pattern and locate the internal chessboard corners. The function returns a non-zero value if all of the corners @@ -843,6 +848,10 @@ The algorithm performs the following steps: \f$f_y\f$ (ratios of 10:1 or more)), then you are probably using patternSize=cvSize(rows,cols) instead of using patternSize=cvSize(cols,rows) in @ref findChessboardCorners. +@note + The function may throw exceptions, if unsupported combination of parameters is provided or + the system is underconstrained. + @sa calibrateCameraRO, findChessboardCorners, solvePnP, initCameraMatrix2D, stereoCalibrate, undistort diff --git a/modules/calib/src/calibinit.cpp b/modules/calib/src/calibinit.cpp index e49b5c2a66..60806bf689 100644 --- a/modules/calib/src/calibinit.cpp +++ b/modules/calib/src/calibinit.cpp @@ -46,7 +46,7 @@ Here is the copyright notice from the original Vladimir's code: =============================================================== - The algorithms developed and implemented by Vezhnevets Vldimir + The algorithms developed and implemented by Vezhnevets Vladimir aka Dead Moroz (vvp@graphics.cs.msu.ru) See http://graphics.cs.msu.su/en/research/calibration/opencv.html for detailed information. @@ -54,7 +54,7 @@ Reliability additions and modifications made by Philip Gruebele. pgruebele@cox.net - Some further improvements for detection of partially ocluded boards at non-ideal + Some further improvements for detection of partially occluded boards at non-ideal lighting conditions have been made by Alex Bovyrin and Kurt Kolonige \************************************************************************************/ @@ -72,6 +72,7 @@ #include "precomp.hpp" #include "circlesgrid.hpp" #include "opencv2/imgproc/imgproc_c.h" +#include "opencv2/flann.hpp" #include @@ -240,7 +241,7 @@ public: void findConnectedQuads(std::vector& out_group, int group_idx); - int checkQuadGroup(std::vector& quad_group, std::vector& out_corners); + int checkQuadGroup(const std::vector& quad_group, std::vector& out_corners); int cleanFoundConnectedQuads(std::vector& quad_group); @@ -297,7 +298,7 @@ static void icvSmoothHistogram256(const ArrayContainer& piHist, ArrayContainer& CV_DbgAssert(iIdx >= 0 && iIdx < 256); iSmooth += piHist[iIdx]; } - piHistSmooth[i] = iSmooth/(2*iWidth+1); + piHistSmooth[i] = iSmooth/(iIdx_max-iIdx_min+1); } } /***************************************************************************************************/ @@ -325,7 +326,7 @@ static void icvGradientOfHistogram256(const ArrayContainer& piHist, ArrayContain piHistGrad[255] = 0; } /***************************************************************************************************/ -//PERFORM SMART IMAGE THRESHOLDING BASED ON ANALYSIS OF INTENSTY HISTOGRAM +//PERFORM SMART IMAGE THRESHOLDING BASED ON ANALYSIS OF INTENSITY HISTOGRAM static void icvBinarizationHistogramBased(Mat & img) { CV_Assert(img.channels() == 1 && img.depth() == CV_8U); @@ -479,8 +480,7 @@ bool findChessboardCorners(InputArray image_, Size pattern_size, bool found = false; - const int min_dilations = 0; - const int max_dilations = 7; + const bool is_plain = (flags & CALIB_CB_PLAIN) != 0; int type = image_.type(), depth = CV_MAT_DEPTH(type), cn = CV_MAT_CN(type); Mat img = image_.getMat(); @@ -496,6 +496,9 @@ bool findChessboardCorners(InputArray image_, Size pattern_size, std::vector out_corners; + if (is_plain) + CV_CheckType(type, depth == CV_8U && cn == 1, "Only 8-bit grayscale images are supported whith CALIB_CB_PLAIN flag enable"); + if (img.channels() != 1) { cvtColor(img, img, COLOR_BGR2GRAY); @@ -504,10 +507,11 @@ bool findChessboardCorners(InputArray image_, Size pattern_size, int prev_sqr_size = 0; Mat thresh_img_new = img.clone(); - icvBinarizationHistogramBased(thresh_img_new); // process image in-place + if(!is_plain) + icvBinarizationHistogramBased(thresh_img_new); // process image in-place SHOW("New binarization", thresh_img_new); - if (flags & CALIB_CB_FAST_CHECK) + if (flags & CALIB_CB_FAST_CHECK && !is_plain) { //perform new method for checking chessboard using a binary image. //image is binarised using a threshold dependent on the image histogram @@ -523,6 +527,9 @@ bool findChessboardCorners(InputArray image_, Size pattern_size, ChessBoardDetector detector(pattern_size); + const int min_dilations = 0; + const int max_dilations = is_plain ? 0 : 7; + // Try our standard "1" dilation, but if the pattern is not found, iterate the whole procedure with higher dilations. // This is necessary because some squares simply do not separate properly with a single dilation. However, // we want to use the minimum number of dilations possible since dilations cause the squares to become smaller, @@ -530,7 +537,8 @@ bool findChessboardCorners(InputArray image_, Size pattern_size, for (int dilations = min_dilations; dilations <= max_dilations; dilations++) { //USE BINARY IMAGE COMPUTED USING icvBinarizationHistogramBased METHOD - dilate( thresh_img_new, thresh_img_new, Mat(), Point(-1, -1), 1 ); + if(!is_plain) + dilate( thresh_img_new, thresh_img_new, Mat(), Point(-1, -1), 1 ); // So we can find rectangles that go to the edge, we draw a white line around the image edge. // Otherwise FindContours will miss those clipped rectangle contours. @@ -551,7 +559,7 @@ bool findChessboardCorners(InputArray image_, Size pattern_size, DPRINTF("Chessboard detection result 0: %d", (int)found); // revert to old, slower, method if detection failed - if (!found) + if (!found && !is_plain) { if (flags & CALIB_CB_NORMALIZE_IMAGE) { @@ -662,7 +670,6 @@ bool findChessboardCorners(InputArray image_, Size pattern_size, return found; } - // // Checks that each board row and column is pretty much monotonous curve: // It analyzes each row and each column of the chessboard as following: @@ -671,7 +678,7 @@ bool findChessboardCorners(InputArray image_, Size pattern_size, // of the neighbor corners in the same row/column. // // This function has been created as temporary workaround for the bug in current implementation -// of cvFindChessboardCornes that produces absolutely unordered sets of corners. +// of cvFindChessboardCorners that produces absolutely unordered sets of corners. // bool ChessBoardDetector::checkBoardMonotony(const std::vector& corners) { @@ -1213,9 +1220,9 @@ int ChessBoardDetector::cleanFoundConnectedQuads(std::vector& q // We iteratively remove the point which reduces the size of // the bounding box of the blobs the most // (since we want the rectangle to be as small as possible) - // remove the quadrange that causes the biggest reduction + // remove the quadrangle that causes the biggest reduction // in pattern size until we have the correct number - for (; quad_count > count; quad_count--) + while (quad_count > count) { double min_box_area = DBL_MAX; int min_box_area_index = -1; @@ -1321,7 +1328,7 @@ void ChessBoardDetector::findConnectedQuads(std::vector& out_gr } -int ChessBoardDetector::checkQuadGroup(std::vector& quad_group, std::vector& out_corners) +int ChessBoardDetector::checkQuadGroup(const std::vector& quad_group, std::vector& out_corners) { const int ROW1 = 1000000; const int ROW2 = 2000000; @@ -1419,6 +1426,7 @@ int ChessBoardDetector::checkQuadGroup(std::vector& quad_group, ChessBoardCorner* cur = first; ChessBoardCorner* right = NULL; ChessBoardCorner* below = NULL; + out_corners.clear(); out_corners.push_back(cur); for (int k = 0; k < 4; ++k) @@ -1588,7 +1596,24 @@ finalize: void ChessBoardDetector::findQuadNeighbors() { const float thresh_scale = 1.f; + + const int all_corners_count = all_quads_count * 4; + + std::vector all_quads_pts; + all_quads_pts.reserve(all_corners_count); + for (int idx = 0; idx < all_quads_count; idx++) + { + const ChessBoardQuad& cur_quad = (const ChessBoardQuad&)all_quads[idx]; + for (int i = 0; i < 4; i++) + all_quads_pts.push_back(cur_quad.corners[i]->pt); + } + + const cvflann::KDTreeSingleIndexParams index_params; + flann::GenericIndex> all_quads_pts_index(Mat(all_quads_pts).reshape(1, all_corners_count), index_params); + // find quad neighbors + std::vector neighbors_indices(all_corners_count); + std::vector neighbors_dists(all_corners_count); for (int idx = 0; idx < all_quads_count; idx++) { ChessBoardQuad& cur_quad = (ChessBoardQuad&)all_quads[idx]; @@ -1611,36 +1636,41 @@ void ChessBoardDetector::findQuadNeighbors() Point2f pt = cur_quad.corners[i]->pt; // find the closest corner in all other quadrangles - for (int k = 0; k < all_quads_count; k++) + std::vector query = Mat(pt); + float radius = cur_quad.edge_len * thresh_scale + 1; + const cvflann::SearchParams search_params(-1); + int neighbors_count = all_quads_pts_index.radiusSearch(query, neighbors_indices, neighbors_dists, radius, search_params); + + for (int neighbor_idx_idx = 0; neighbor_idx_idx < neighbors_count; neighbor_idx_idx++) { + const int neighbor_idx = neighbors_indices[neighbor_idx_idx]; + const int k = neighbor_idx >> 2; if (k == idx) continue; ChessBoardQuad& q_k = all_quads[k]; + const int j = neighbor_idx & 3; + if (q_k.neighbors[j]) + continue; - for (int j = 0; j < 4; j++) + const float dist = normL2Sqr(pt - q_k.corners[j]->pt); + if (dist < min_dist && + dist <= cur_quad.edge_len * thresh_scale && + dist <= q_k.edge_len * thresh_scale) { - if (q_k.neighbors[j]) - continue; - - float dist = normL2Sqr(pt - q_k.corners[j]->pt); - if (dist < min_dist && - dist <= cur_quad.edge_len*thresh_scale && - dist <= q_k.edge_len*thresh_scale ) + // check edge lengths, make sure they're compatible + // edges that are different by more than 1:4 are rejected. + // edge_len is squared edge length, so we compare them + // with squared constant 16 = 4^2 + if (q_k.edge_len > 16 * cur_quad.edge_len || + cur_quad.edge_len > 16 * q_k.edge_len) { - // check edge lengths, make sure they're compatible - // edges that are different by more than 1:4 are rejected - float ediff = cur_quad.edge_len - q_k.edge_len; - if (ediff > 32*cur_quad.edge_len || - ediff > 32*q_k.edge_len) - { - DPRINTF("Incompatible edge lengths"); - continue; - } - closest_corner_idx = j; - closest_quad = &q_k; - min_dist = dist; + DPRINTF("Incompatible edge lengths"); + continue; } + closest_corner_idx = j; + closest_quad = &q_k; + min_dist = dist; } } @@ -1681,26 +1711,29 @@ void ChessBoardDetector::findQuadNeighbors() // check whether the closest corner to closest_corner // is different from cur_quad->corners[i]->pt - for (j = 0; j < all_quads_count; j++ ) + query = Mat(closest_corner.pt); + radius = min_dist + 1; + neighbors_count = all_quads_pts_index.radiusSearch(query, neighbors_indices, neighbors_dists, radius, search_params); + + int neighbor_idx_idx = 0; + for (; neighbor_idx_idx < neighbors_count; neighbor_idx_idx++) { + const int neighbor_idx = neighbors_indices[neighbor_idx_idx]; + j = neighbor_idx >> 2; + ChessBoardQuad* q = &const_cast(all_quads[j]); if (j == idx || q == closest_quad) continue; - int k = 0; - for (; k < 4; k++ ) + const int k = neighbor_idx & 3; + CV_DbgAssert(q); + if (!q->neighbors[k]) { - CV_DbgAssert(q); - if (!q->neighbors[k]) - { - if (normL2Sqr(closest_corner.pt - q->corners[k]->pt) < min_dist) - break; - } + if (normL2Sqr(closest_corner.pt - q->corners[k]->pt) < min_dist) + break; } - if (k < 4) - break; } - if (j < all_quads_count) + if (neighbor_idx_idx < neighbors_count) continue; closest_corner.pt = (pt + closest_corner.pt) * 0.5f; @@ -1730,8 +1763,8 @@ void ChessBoardDetector::generateQuads(const Mat& image_, int flags) all_quads.deallocate(); all_corners.deallocate(); - // empiric bound for minimal allowed perimeter for squares - int min_size = 25; //cvRound( image->cols * image->rows * .03 * 0.01 * 0.92 ); + // empiric bound for minimal allowed area for squares + const int min_area = 25; //cvRound( image->cols * image->rows * .03 * 0.01 * 0.92 ); bool filterQuads = (flags & CALIB_CB_FILTER_QUADS) != 0; @@ -1759,7 +1792,7 @@ void ChessBoardDetector::generateQuads(const Mat& image_, int flags) const std::vector& contour = contours[idx]; Rect contour_rect = boundingRect(contour); - if (contour_rect.area() < min_size) + if (contour_rect.area() < min_area) continue; std::vector approx_contour; @@ -1803,7 +1836,7 @@ void ChessBoardDetector::generateQuads(const Mat& image_, int flags) // than rectangular and which are big enough double d3 = sqrt(normL2Sqr(pt[0] - pt[1])); double d4 = sqrt(normL2Sqr(pt[1] - pt[2])); - if (!(d3*4 > d4 && d4*4 > d3 && d3*d4 < area*1.5 && area > min_size && + if (!(d3*4 > d4 && d4*4 > d3 && d3*d4 < area*1.5 && area > min_area && d1 >= 0.15 * p && d2 >= 0.15 * p)) continue; } @@ -1912,6 +1945,7 @@ bool ChessBoardDetector::processQuads(std::vector& out_corners, int &pr if (count > 0 || (-count > (int)out_corners.size())) { // copy corners to output array + out_corners.clear(); out_corners.reserve(n); for (int i = 0; i < n; ++i) out_corners.push_back(corner_group[i]->pt); diff --git a/modules/calib/test/test_chesscorners.cpp b/modules/calib/test/test_chesscorners.cpp index 7226da999a..56cb81e4ab 100644 --- a/modules/calib/test/test_chesscorners.cpp +++ b/modules/calib/test/test_chesscorners.cpp @@ -73,7 +73,7 @@ void show_points( const Mat& gray, const Mat& expected, const vector& a #define show_points(...) #endif -enum Pattern { CHESSBOARD,CHESSBOARD_SB,CIRCLES_GRID, ASYMMETRIC_CIRCLES_GRID}; +enum Pattern { CHESSBOARD, CHESSBOARD_SB, CHESSBOARD_PLAIN, CIRCLES_GRID, ASYMMETRIC_CIRCLES_GRID}; class CV_ChessboardDetectorTest : public cvtest::BaseTest { @@ -149,6 +149,25 @@ void CV_ChessboardDetectorTest::run( int /*start_from */) case CHESSBOARD_SB: checkByGeneratorHighAccuracy(); // not supported by CHESSBOARD /* fallthrough */ + case CHESSBOARD_PLAIN: + checkByGenerator(); + if (ts->get_err_code() != cvtest::TS::OK) + { + break; + } + + run_batch("negative_list.dat"); + if (ts->get_err_code() != cvtest::TS::OK) + { + break; + } + + run_batch("chessboard_list.dat"); + if (ts->get_err_code() != cvtest::TS::OK) + { + break; + } + break; case CHESSBOARD: checkByGenerator(); if (ts->get_err_code() != cvtest::TS::OK) @@ -191,6 +210,7 @@ void CV_ChessboardDetectorTest::run_batch( const string& filename ) { case CHESSBOARD: case CHESSBOARD_SB: + case CHESSBOARD_PLAIN: folder = string(ts->get_data_path()) + "cv/cameracalibration/"; break; case CIRCLES_GRID: @@ -215,6 +235,9 @@ void CV_ChessboardDetectorTest::run_batch( const string& filename ) int progress = 0; int max_idx = (int)board_list.size()/2; + if(filename.compare("chessboard_list.dat") == 0 && pattern == CHESSBOARD_PLAIN) + max_idx = 7; + double sum_error = 0.0; int count = 0; @@ -247,6 +270,7 @@ void CV_ChessboardDetectorTest::run_batch( const string& filename ) size_t count_exp = static_cast(expected.cols * expected.rows); Size pattern_size = expected.size(); + Mat ori; vector v; int flags = 0; switch( pattern ) @@ -254,14 +278,30 @@ void CV_ChessboardDetectorTest::run_batch( const string& filename ) case CHESSBOARD: flags = CALIB_CB_ADAPTIVE_THRESH | CALIB_CB_NORMALIZE_IMAGE; break; + case CHESSBOARD_PLAIN: { + flags = CALIB_CB_PLAIN; + ori = gray.clone(); + int min_size = cvRound((gray.cols * gray.rows * 0.05) / ((pattern_size.width+1) * (pattern_size.height+1))); + if(min_size%2==0) min_size += 1; + adaptiveThreshold(gray, gray, 255, ADAPTIVE_THRESH_MEAN_C, THRESH_BINARY, min_size, 0); + dilate(gray, gray, Mat(), Point(-1, -1), 1); + break; + } case CIRCLES_GRID: case CHESSBOARD_SB: case ASYMMETRIC_CIRCLES_GRID: default: flags = 0; } + bool result = findChessboardCornersWrapper(gray, pattern_size,v,flags); - if(result && sharpness && (pattern == CHESSBOARD_SB || pattern == CHESSBOARD)) + + if(result && pattern == CHESSBOARD_PLAIN) { + gray = ori; + cornerSubPix(gray, v, Size(6,6), Size(-1,-1), TermCriteria(TermCriteria::EPS + TermCriteria::COUNT, 30, 0.1)); + } + + if(result && sharpness && (pattern == CHESSBOARD_SB || pattern == CHESSBOARD || pattern == CHESSBOARD_PLAIN)) { Scalar s= estimateChessboardSharpness(gray,pattern_size,v); if(fabs(s[0] - sharpness) > 0.1) @@ -287,7 +327,7 @@ void CV_ChessboardDetectorTest::run_batch( const string& filename ) double err = calcError(v, expected); max_rough_error = MAX( max_rough_error, err ); #endif - if( pattern == CHESSBOARD ) + if( pattern == CHESSBOARD || pattern == CHESSBOARD_PLAIN ) cornerSubPix( gray, v, Size(5, 5), Size(-1,-1), TermCriteria(TermCriteria::EPS|TermCriteria::MAX_ITER, 30, 0.1)); //find4QuadCornerSubpix(gray, v, Size(5, 5)); show_points( gray, expected, v, result ); @@ -381,6 +421,7 @@ bool CV_ChessboardDetectorTest::findChessboardCornersWrapper(InputArray image, S switch(pattern) { case CHESSBOARD: + case CHESSBOARD_PLAIN: return findChessboardCorners(image,patternSize,corners,flags); case CHESSBOARD_SB: // check default settings until flags have been specified @@ -631,6 +672,7 @@ bool CV_ChessboardDetectorTest::checkByGeneratorHighAccuracy() TEST(Calib3d_ChessboardDetector, accuracy) { CV_ChessboardDetectorTest test( CHESSBOARD ); test.safe_run(); } TEST(Calib3d_ChessboardDetector2, accuracy) { CV_ChessboardDetectorTest test( CHESSBOARD_SB ); test.safe_run(); } +TEST(Calib3d_ChessboardDetector3, accuracy) { CV_ChessboardDetectorTest test( CHESSBOARD_PLAIN ); test.safe_run(); } TEST(Calib3d_CirclesPatternDetector, accuracy) { CV_ChessboardDetectorTest test( CIRCLES_GRID ); test.safe_run(); } TEST(Calib3d_AsymmetricCirclesPatternDetector, accuracy) { CV_ChessboardDetectorTest test( ASYMMETRIC_CIRCLES_GRID ); test.safe_run(); } #ifdef HAVE_OPENCV_FLANN diff --git a/modules/calib/test/test_chesscorners_timing.cpp b/modules/calib/test/test_chesscorners_timing.cpp index 78dbffa184..6c8ea6f0bf 100644 --- a/modules/calib/test/test_chesscorners_timing.cpp +++ b/modules/calib/test/test_chesscorners_timing.cpp @@ -138,13 +138,13 @@ void CV_ChessboardDetectorTimingTest::run( int start_from ) } int num_pixels = gray.cols*gray.rows; - float check_chessboard_time = float(_time01 - _time0)/(float)cv::getTickFrequency(); // in us + float check_chessboard_time = float(_time01 - _time0)/(float)cv::getTickFrequency(); // in s ts->printf(cvtest::TS::LOG, " cvCheckChessboard time s: %f, us per pixel: %f\n", - check_chessboard_time*1e-6, check_chessboard_time/num_pixels); + check_chessboard_time, check_chessboard_time*1e6/num_pixels); float find_chessboard_time = float(_time1 - _time01)/(float)cv::getTickFrequency(); ts->printf(cvtest::TS::LOG, " cvFindChessboard time s: %f, us per pixel: %f\n", - find_chessboard_time*1e-6, find_chessboard_time/num_pixels); + find_chessboard_time, find_chessboard_time*1e6/num_pixels); progress = update_progress( progress, idx-1, max_idx, 0 ); } diff --git a/modules/core/CMakeLists.txt b/modules/core/CMakeLists.txt index a1611dfcf6..6f35e98eca 100644 --- a/modules/core/CMakeLists.txt +++ b/modules/core/CMakeLists.txt @@ -1,27 +1,27 @@ set(the_description "The Core Functionality") -ocv_add_dispatched_file(mathfuncs_core SSE2 AVX AVX2) -ocv_add_dispatched_file(stat SSE4_2 AVX2) -ocv_add_dispatched_file(arithm SSE2 SSE4_1 AVX2 VSX3) -ocv_add_dispatched_file(convert SSE2 AVX2 VSX3) -ocv_add_dispatched_file(convert_scale SSE2 AVX2) -ocv_add_dispatched_file(count_non_zero SSE2 AVX2) -ocv_add_dispatched_file(has_non_zero SSE2 AVX2) -ocv_add_dispatched_file(matmul SSE2 SSE4_1 AVX2 AVX512_SKX NEON_DOTPROD) -ocv_add_dispatched_file(mean SSE2 AVX2) -ocv_add_dispatched_file(merge SSE2 AVX2) -ocv_add_dispatched_file(nan_mask SSE2 AVX2) -ocv_add_dispatched_file(split SSE2 AVX2) -ocv_add_dispatched_file(sum SSE2 AVX2) +ocv_add_dispatched_file(mathfuncs_core SSE2 AVX AVX2 LASX) +ocv_add_dispatched_file(stat SSE4_2 AVX2 LASX) +ocv_add_dispatched_file(arithm SSE2 SSE4_1 AVX2 VSX3 LASX) +ocv_add_dispatched_file(convert SSE2 AVX2 VSX3 LASX) +ocv_add_dispatched_file(convert_scale SSE2 AVX2 LASX) +ocv_add_dispatched_file(count_non_zero SSE2 AVX2 LASX) +ocv_add_dispatched_file(has_non_zero SSE2 AVX2 LASX ) +ocv_add_dispatched_file(matmul SSE2 SSE4_1 AVX2 AVX512_SKX NEON_DOTPROD LASX) +ocv_add_dispatched_file(mean SSE2 AVX2 LASX) +ocv_add_dispatched_file(merge SSE2 AVX2 LASX) +ocv_add_dispatched_file(nan_mask SSE2 AVX2 LASX) +ocv_add_dispatched_file(split SSE2 AVX2 LASX) +ocv_add_dispatched_file(sum SSE2 AVX2 LASX) # dispatching for accuracy tests ocv_add_dispatched_file_force_all(test_intrin128 TEST SSE2 SSE3 SSSE3 SSE4_1 SSE4_2 AVX FP16 AVX2 AVX512_SKX) -ocv_add_dispatched_file_force_all(test_intrin256 TEST AVX2 AVX512_SKX) +ocv_add_dispatched_file_force_all(test_intrin256 TEST AVX2 AVX512_SKX LASX) ocv_add_dispatched_file_force_all(test_intrin512 TEST AVX512_SKX) set(PARALLEL_ENABLE_PLUGINS_DEFAULT ON) -if(EMSCRIPTEN OR IOS OR WINRT) +if(EMSCRIPTEN OR IOS OR XROS OR WINRT) set(PARALLEL_ENABLE_PLUGINS_DEFAULT OFF) endif() # parallel backends configuration @@ -50,13 +50,6 @@ if(DEFINED WINRT AND NOT DEFINED ENABLE_WINRT_MODE_NATIVE) set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} /ZW") endif() -if(HAVE_CUDA) - if(NOT HAVE_opencv_cudev) - message(FATAL_ERROR "CUDA: OpenCV requires enabled 'cudev' module from 'opencv_contrib' repository: https://github.com/opencv/opencv_contrib") - endif() - ocv_warnings_disable(CMAKE_CXX_FLAGS -Wundef -Wenum-compare -Wunused-function -Wshadow) -endif() - if(CV_TRACE AND HAVE_ITT) add_definitions(-DOPENCV_WITH_ITT=1) endif() @@ -154,7 +147,6 @@ elseif(HAVE_CXX11 OR DEFINED OPENCV_ALLOCATOR_STATS_COUNTER_TYPE) endif() endif() - if(PARALLEL_ENABLE_PLUGINS) ocv_append_source_file_compile_definitions(${CMAKE_CURRENT_SOURCE_DIR}/src/parallel/parallel.cpp "PARALLEL_ENABLE_PLUGINS=1") if(OPENCV_DEBUG_POSTFIX) @@ -162,6 +154,15 @@ if(PARALLEL_ENABLE_PLUGINS) endif() endif() +if(HAVE_CUDA) + if(NOT HAVE_opencv_cudev) + message(FATAL_ERROR "CUDA: OpenCV requires enabled 'cudev' module from 'opencv_contrib' repository: https://github.com/opencv/opencv_contrib") + endif() + if(ENABLE_CUDA_FIRST_CLASS_LANGUAGE) + ocv_module_include_directories(${CUDAToolkit_INCLUDE_DIRS}) + endif() + ocv_warnings_disable(CMAKE_CXX_FLAGS -Wundef -Wenum-compare -Wunused-function -Wshadow) +endif() ocv_create_module(${extra_libs}) diff --git a/modules/core/include/opencv2/core.hpp b/modules/core/include/opencv2/core.hpp index 9e69e58620..c648c0d30f 100644 --- a/modules/core/include/opencv2/core.hpp +++ b/modules/core/include/opencv2/core.hpp @@ -3201,6 +3201,10 @@ public: * @overload */ CV_WRAP void write(FileStorage& fs, const String& name) const; +#if CV_VERSION_MAJOR < 5 + /** @deprecated */ + void write(const Ptr& fs, const String& name = String()) const; +#endif /** @brief Reads algorithm parameters from a file storage */ diff --git a/modules/core/include/opencv2/core/bindings_utils.hpp b/modules/core/include/opencv2/core/bindings_utils.hpp index 64f346570a..9c8f9e0f2b 100644 --- a/modules/core/include/opencv2/core/bindings_utils.hpp +++ b/modules/core/include/opencv2/core/bindings_utils.hpp @@ -75,20 +75,6 @@ String dumpString(const String& argument) return cv::format("String: %s", argument.c_str()); } -CV_WRAP static inline -String testOverloadResolution(int value, const Point& point = Point(42, 24)) -{ - return format("overload (int=%d, point=(x=%d, y=%d))", value, point.x, - point.y); -} - -CV_WRAP static inline -String testOverloadResolution(const Rect& rect) -{ - return format("overload (rect=(x=%d, y=%d, w=%d, h=%d))", rect.x, rect.y, - rect.width, rect.height); -} - CV_WRAP static inline String dumpRect(const Rect& argument) { @@ -111,6 +97,42 @@ String dumpRotatedRect(const RotatedRect& argument) argument.size.height, argument.angle); } +CV_WRAP static inline +String dumpRange(const Range& argument) +{ + if (argument == Range::all()) + { + return "range: all"; + } + else + { + return format("range: (s=%d, e=%d)", argument.start, argument.end); + } +} + +CV_EXPORTS_W String dumpVectorOfInt(const std::vector& vec); + +CV_EXPORTS_W String dumpVectorOfDouble(const std::vector& vec); + +CV_EXPORTS_W String dumpVectorOfRect(const std::vector& vec); + + +//! @cond IGNORED + +CV_WRAP static inline +String testOverloadResolution(int value, const Point& point = Point(42, 24)) +{ + return format("overload (int=%d, point=(x=%d, y=%d))", value, point.x, + point.y); +} + +CV_WRAP static inline +String testOverloadResolution(const Rect& rect) +{ + return format("overload (rect=(x=%d, y=%d, w=%d, h=%d))", rect.x, rect.y, + rect.width, rect.height); +} + CV_WRAP static inline RotatedRect testRotatedRect(float x, float y, float w, float h, float angle) { @@ -126,19 +148,6 @@ std::vector testRotatedRectVector(float x, float y, float w, float return result; } -CV_WRAP static inline -String dumpRange(const Range& argument) -{ - if (argument == Range::all()) - { - return "range: all"; - } - else - { - return format("range: (s=%d, e=%d)", argument.start, argument.end); - } -} - CV_WRAP static inline int testOverwriteNativeMethod(int argument) { @@ -151,12 +160,6 @@ String testReservedKeywordConversion(int positional_argument, int lambda = 2, in return format("arg=%d, lambda=%d, from=%d", positional_argument, lambda, from); } -CV_EXPORTS_W String dumpVectorOfInt(const std::vector& vec); - -CV_EXPORTS_W String dumpVectorOfDouble(const std::vector& vec); - -CV_EXPORTS_W String dumpVectorOfRect(const std::vector& vec); - CV_WRAP static inline void generateVectorOfRect(size_t len, CV_OUT std::vector& vec) { @@ -323,6 +326,8 @@ private: typedef OriginalClassName::Params OriginalClassName_Params; } // namespace nested +//! @endcond IGNORED + namespace fs { CV_EXPORTS_W cv::String getCacheDirectoryForDownloads(); } // namespace fs diff --git a/modules/core/include/opencv2/core/cv_cpu_helper.h b/modules/core/include/opencv2/core/cv_cpu_helper.h index daca592e65..04b00d2024 100644 --- a/modules/core/include/opencv2/core/cv_cpu_helper.h +++ b/modules/core/include/opencv2/core/cv_cpu_helper.h @@ -441,6 +441,48 @@ #endif #define __CV_CPU_DISPATCH_CHAIN_NEON_DOTPROD(fn, args, mode, ...) CV_CPU_CALL_NEON_DOTPROD(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_FP16 +# define CV_TRY_NEON_FP16 1 +# define CV_CPU_FORCE_NEON_FP16 1 +# define CV_CPU_HAS_SUPPORT_NEON_FP16 1 +# define CV_CPU_CALL_NEON_FP16(fn, args) return (cpu_baseline::fn args) +# define CV_CPU_CALL_NEON_FP16_(fn, args) return (opt_NEON_FP16::fn args) +#elif !defined CV_DISABLE_OPTIMIZATION && defined CV_ENABLE_INTRINSICS && defined CV_CPU_DISPATCH_COMPILE_NEON_FP16 +# define CV_TRY_NEON_FP16 1 +# define CV_CPU_FORCE_NEON_FP16 0 +# define CV_CPU_HAS_SUPPORT_NEON_FP16 (cv::checkHardwareSupport(CV_CPU_NEON_FP16)) +# define CV_CPU_CALL_NEON_FP16(fn, args) if (CV_CPU_HAS_SUPPORT_NEON_FP16) return (opt_NEON_FP16::fn args) +# define CV_CPU_CALL_NEON_FP16_(fn, args) if (CV_CPU_HAS_SUPPORT_NEON_FP16) return (opt_NEON_FP16::fn args) +#else +# define CV_TRY_NEON_FP16 0 +# define CV_CPU_FORCE_NEON_FP16 0 +# define CV_CPU_HAS_SUPPORT_NEON_FP16 0 +# define CV_CPU_CALL_NEON_FP16(fn, args) +# define CV_CPU_CALL_NEON_FP16_(fn, args) +#endif +#define __CV_CPU_DISPATCH_CHAIN_NEON_FP16(fn, args, mode, ...) CV_CPU_CALL_NEON_FP16(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_BF16 +# define CV_TRY_NEON_BF16 1 +# define CV_CPU_FORCE_NEON_BF16 1 +# define CV_CPU_HAS_SUPPORT_NEON_BF16 1 +# define CV_CPU_CALL_NEON_BF16(fn, args) return (cpu_baseline::fn args) +# define CV_CPU_CALL_NEON_BF16_(fn, args) return (opt_NEON_BF16::fn args) +#elif !defined CV_DISABLE_OPTIMIZATION && defined CV_ENABLE_INTRINSICS && defined CV_CPU_DISPATCH_COMPILE_NEON_BF16 +# define CV_TRY_NEON_BF16 1 +# define CV_CPU_FORCE_NEON_BF16 0 +# define CV_CPU_HAS_SUPPORT_NEON_BF16 (cv::checkHardwareSupport(CV_CPU_NEON_BF16)) +# define CV_CPU_CALL_NEON_BF16(fn, args) if (CV_CPU_HAS_SUPPORT_NEON_BF16) return (opt_NEON_BF16::fn args) +# define CV_CPU_CALL_NEON_BF16_(fn, args) if (CV_CPU_HAS_SUPPORT_NEON_BF16) return (opt_NEON_BF16::fn args) +#else +# define CV_TRY_NEON_BF16 0 +# define CV_CPU_FORCE_NEON_BF16 0 +# define CV_CPU_HAS_SUPPORT_NEON_BF16 0 +# define CV_CPU_CALL_NEON_BF16(fn, args) +# define CV_CPU_CALL_NEON_BF16_(fn, args) +#endif +#define __CV_CPU_DISPATCH_CHAIN_NEON_BF16(fn, args, mode, ...) CV_CPU_CALL_NEON_BF16(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_MSA # define CV_TRY_MSA 1 # define CV_CPU_FORCE_MSA 1 diff --git a/modules/core/include/opencv2/core/fast_math.hpp b/modules/core/include/opencv2/core/fast_math.hpp index 08401afbb8..ff9ee46af6 100644 --- a/modules/core/include/opencv2/core/fast_math.hpp +++ b/modules/core/include/opencv2/core/fast_math.hpp @@ -68,7 +68,7 @@ // nothing, intrinsics/asm code is not supported #else #if ((defined _MSC_VER && defined _M_X64) \ - || (defined __GNUC__ && defined __x86_64__ && defined __SSE2__)) \ + || (defined __GNUC__ && defined __SSE2__)) \ && !defined(OPENCV_SKIP_INCLUDE_EMMINTRIN_H) #include #endif diff --git a/modules/core/include/opencv2/core/hal/intrin.hpp b/modules/core/include/opencv2/core/hal/intrin.hpp index cf0711fa61..3c76b6288c 100644 --- a/modules/core/include/opencv2/core/hal/intrin.hpp +++ b/modules/core/include/opencv2/core/hal/intrin.hpp @@ -246,12 +246,6 @@ using namespace CV_CPU_OPTIMIZATION_HAL_NAMESPACE; #include "opencv2/core/hal/intrin_lsx.hpp" -#elif CV_LASX - #if !defined(CV_FORCE_SIMD128_CPP) - #define CV_FORCE_SIMD128_CPP 1 - #endif -#include "opencv2/core/hal/intrin_cpp.hpp" - #else #include "opencv2/core/hal/intrin_cpp.hpp" diff --git a/modules/core/include/opencv2/core/hal/intrin_lasx.hpp b/modules/core/include/opencv2/core/hal/intrin_lasx.hpp index 5214e80743..6546d6db7d 100644 --- a/modules/core/include/opencv2/core/hal/intrin_lasx.hpp +++ b/modules/core/include/opencv2/core/hal/intrin_lasx.hpp @@ -1419,20 +1419,6 @@ inline v_uint32x8 v_popcount(const v_int32x8& a) inline v_uint64x4 v_popcount(const v_int64x4& a) { return v_popcount(v_reinterpret_as_u64(a)); } -/** Mask **/ -#define OPENCV_HAL_IMPL_REINTERPRET_INT(ft, tt) \ -inline tt reinterpret_int(ft x) { union { ft l; tt i; } v; v.l = x; return v.i; } -OPENCV_HAL_IMPL_REINTERPRET_INT(uchar, schar) -OPENCV_HAL_IMPL_REINTERPRET_INT(schar, schar) -OPENCV_HAL_IMPL_REINTERPRET_INT(ushort, short) -OPENCV_HAL_IMPL_REINTERPRET_INT(short, short) -OPENCV_HAL_IMPL_REINTERPRET_INT(unsigned, int) -OPENCV_HAL_IMPL_REINTERPRET_INT(int, int) -OPENCV_HAL_IMPL_REINTERPRET_INT(float, int) -OPENCV_HAL_IMPL_REINTERPRET_INT(uint64, int64) -OPENCV_HAL_IMPL_REINTERPRET_INT(int64, int64) -OPENCV_HAL_IMPL_REINTERPRET_INT(double, int64) - inline int v_signmask(const v_int8x32& a) { __m256i result = __lasx_xvmskltz_b(a.val); @@ -2151,7 +2137,8 @@ template inline void v_rshr_pack_store(uchar* ptr, const v_uint16x16& a) { __m256i res = __lasx_xvssrlrni_bu_h(a.val, a.val, n); - __lsx_vst(_v256_extract_low(_v256_shuffle_odd_64(res)), ptr, 0); + __lasx_xvstelm_d(res, ptr, 0, 0); + __lasx_xvstelm_d(res, ptr, 8, 2); } template inline @@ -2165,7 +2152,8 @@ template inline void v_rshr_pack_u_store(uchar* ptr, const v_int16x16& a) { __m256i res = __lasx_xvssrarni_bu_h(a.val, a.val, n); - __lsx_vst(_v256_extract_low(_v256_shuffle_odd_64(res)), ptr, 0); + __lasx_xvstelm_d(res, ptr, 0, 0); + __lasx_xvstelm_d(res, ptr, 8, 2); } template inline @@ -2179,7 +2167,8 @@ template inline void v_rshr_pack_store(schar* ptr, const v_int16x16& a) { __m256i res = __lasx_xvssrarni_b_h(a.val, a.val, n); - __lsx_vst(_v256_extract_low(_v256_shuffle_odd_64(res)), ptr, 0); + __lasx_xvstelm_d(res, ptr, 0, 0); + __lasx_xvstelm_d(res, ptr, 8, 2); } // 32 @@ -2198,7 +2187,8 @@ inline void v_pack_store(short* ptr, const v_int32x8& a) inline void v_pack_store(ushort* ptr, const v_uint32x8& a) { __m256i res = __lasx_xvssrlrni_hu_w(a.val, a.val, 0); - __lsx_vst(_v256_extract_low(_v256_shuffle_odd_64(res)), ptr, 0); + __lasx_xvstelm_d(res, ptr, 0, 0); + __lasx_xvstelm_d(res, ptr, 8, 2); } inline void v_pack_u_store(ushort* ptr, const v_int32x8& a) @@ -2212,7 +2202,8 @@ template inline void v_rshr_pack_store(ushort* ptr, const v_uint32x8& a) { __m256i res = __lasx_xvssrlrni_hu_w(a.val, a.val, n); - __lsx_vst(_v256_extract_low(_v256_shuffle_odd_64(res)), ptr, 0); + __lasx_xvstelm_d(res, ptr, 0, 0); + __lasx_xvstelm_d(res, ptr, 8, 2); } template inline @@ -2223,7 +2214,8 @@ template inline void v_rshr_pack_u_store(ushort* ptr, const v_int32x8& a) { __m256i res = __lasx_xvssrarni_hu_w(a.val, a.val, n); - __lsx_vst(_v256_extract_low(_v256_shuffle_odd_64(res)), ptr, 0); + __lasx_xvstelm_d(res, ptr, 0, 0); + __lasx_xvstelm_d(res, ptr, 8, 2); } template inline @@ -2234,7 +2226,8 @@ template inline void v_rshr_pack_store(short* ptr, const v_int32x8& a) { __m256i res = __lasx_xvssrarni_h_w(a.val, a.val, n); - __lsx_vst(_v256_extract_low(_v256_shuffle_odd_64(res)), ptr, 0); + __lasx_xvstelm_d(res, ptr, 0, 0); + __lasx_xvstelm_d(res, ptr, 8, 2); } // 64 @@ -2263,7 +2256,11 @@ v_uint32x8 v_rshr_pack(const v_uint64x4& a, const v_uint64x4& b) template inline void v_rshr_pack_store(unsigned* ptr, const v_uint64x4& a) -{ __lsx_vst(_v256_shuffle_odd_64(__lasx_xvsrlrni_w_d(a.val, a.val, n)), ptr, 0); } +{ + __m256i res = __lasx_xvsrlrni_w_d(a.val, a.val, n); + __lasx_xvstelm_d(res, ptr, 0, 0); + __lasx_xvstelm_d(res, ptr, 8, 2); +} template inline v_int32x8 v_rshr_pack(const v_int64x4& a, const v_int64x4& b) @@ -2271,7 +2268,11 @@ v_int32x8 v_rshr_pack(const v_int64x4& a, const v_int64x4& b) template inline void v_rshr_pack_store(int* ptr, const v_int64x4& a) -{ __lsx_vst(_v256_shuffle_odd_64(__lasx_xvsrarni_w_d(a.val, a.val, n)), ptr, 0); } +{ + __m256i res = __lasx_xvsrarni_w_d(a.val, a.val, n); + __lasx_xvstelm_d(res, ptr, 0, 0); + __lasx_xvstelm_d(res, ptr, 8, 2); +} // pack boolean inline v_uint8x32 v_pack_b(const v_uint16x16& a, const v_uint16x16& b) diff --git a/modules/core/include/opencv2/core/hal/intrin_neon.hpp b/modules/core/include/opencv2/core/hal/intrin_neon.hpp index b4ed6dec0f..c2d49c3de1 100644 --- a/modules/core/include/opencv2/core/hal/intrin_neon.hpp +++ b/modules/core/include/opencv2/core/hal/intrin_neon.hpp @@ -2007,11 +2007,9 @@ inline v_int32x4 v_round(const v_float32x4& a) #else inline v_int32x4 v_round(const v_float32x4& a) { - static const int32x4_t v_sign = vdupq_n_s32(1 << 31), - v_05 = vreinterpretq_s32_f32(vdupq_n_f32(0.5f)); - - int32x4_t v_addition = vorrq_s32(v_05, vandq_s32(v_sign, vreinterpretq_s32_f32(a.val))); - return v_int32x4(vcvtq_s32_f32(vaddq_f32(a.val, vreinterpretq_f32_s32(v_addition)))); + // See https://github.com/opencv/opencv/pull/24271#issuecomment-1867318007 + float32x4_t delta = vdupq_n_f32(12582912.0f); + return v_int32x4(vcvtq_s32_f32(vsubq_f32(vaddq_f32(a.val, delta), delta))); } #endif inline v_int32x4 v_floor(const v_float32x4& a) diff --git a/modules/core/include/opencv2/core/hal/intrin_rvv.hpp b/modules/core/include/opencv2/core/hal/intrin_rvv.hpp index 04e3c0e140..d70660249c 100644 --- a/modules/core/include/opencv2/core/hal/intrin_rvv.hpp +++ b/modules/core/include/opencv2/core/hal/intrin_rvv.hpp @@ -32,6 +32,8 @@ namespace cv { +//! @cond IGNORED + CV_CPU_OPTIMIZATION_HAL_NAMESPACE_BEGIN #define CV_SIMD128 1 @@ -3336,7 +3338,8 @@ inline void v_cleanup() {} CV_CPU_OPTIMIZATION_HAL_NAMESPACE_END +//! @endcond -} +} // namespace cv #endif diff --git a/modules/core/include/opencv2/core/hal/intrin_rvv071.hpp b/modules/core/include/opencv2/core/hal/intrin_rvv071.hpp index 9faefd97b7..26f478feda 100644 --- a/modules/core/include/opencv2/core/hal/intrin_rvv071.hpp +++ b/modules/core/include/opencv2/core/hal/intrin_rvv071.hpp @@ -2536,5 +2536,5 @@ CV_CPU_OPTIMIZATION_HAL_NAMESPACE_END //! @endcond -} +} // namespace cv #endif diff --git a/modules/core/include/opencv2/core/hal/intrin_rvv_scalable.hpp b/modules/core/include/opencv2/core/hal/intrin_rvv_scalable.hpp index 14988fc03c..e2475e0e7d 100644 --- a/modules/core/include/opencv2/core/hal/intrin_rvv_scalable.hpp +++ b/modules/core/include/opencv2/core/hal/intrin_rvv_scalable.hpp @@ -38,6 +38,9 @@ namespace cv { + +//! @cond IGNORED + CV_CPU_OPTIMIZATION_HAL_NAMESPACE_BEGIN #define CV_SIMD_SCALABLE 1 @@ -445,29 +448,7 @@ OPENCV_HAL_IMPL_RVV_LOADSTORE_OP(v_float64, vfloat64m1_t, double, VTraits::vlanes())), sizeof(_Tp), VTraits<_Tpvec>::vlanes()); \ - return vloxei32(tab, vidx, VTraits<_Tpvec>::vlanes()); \ -} \ -inline _Tpvec v_lut_pairs(const _Tp* tab, const int* idx) \ -{ \ - std::vector idx_; \ - for (int i = 0; i < VTraits::vlanes(); ++i) { \ - idx_.push_back(idx[i]); \ - idx_.push_back(idx[i]+1); \ - } \ - vuint32##suffix##_t vidx = vmul(vle32_v_u32##suffix(idx_.data(), VTraits<_Tpvec>::vlanes()), sizeof(_Tp), VTraits<_Tpvec>::vlanes()); \ - return vloxei32(tab, vidx, VTraits<_Tpvec>::vlanes()); \ -} \ -inline _Tpvec v_lut_quads(const _Tp* tab, const int* idx) \ -{ \ - std::vector idx_; \ - for (int i = 0; i < VTraits::vlanes(); ++i) { \ - idx_.push_back(idx[i]); \ - idx_.push_back(idx[i]+1); \ - idx_.push_back(idx[i]+2); \ - idx_.push_back(idx[i]+3); \ - } \ - vuint32##suffix##_t vidx = vmul(vle32_v_u32##suffix(idx_.data(), VTraits<_Tpvec>::vlanes()), sizeof(_Tp), VTraits<_Tpvec>::vlanes()); \ + auto vidx = vmul(vreinterpret_u32##suffix(vle32_v_i32##suffix(idx, VTraits<_Tpvec>::vlanes())), sizeof(_Tp), VTraits<_Tpvec>::vlanes()); \ return vloxei32(tab, vidx, VTraits<_Tpvec>::vlanes()); \ } OPENCV_HAL_IMPL_RVV_LUT(v_int8, schar, m4) @@ -479,6 +460,55 @@ OPENCV_HAL_IMPL_RVV_LUT(v_float32, float, m1) OPENCV_HAL_IMPL_RVV_LUT(v_float64, double, mf2) #endif +#define OPENCV_HAL_IMPL_RVV_LUT_PAIRS(_Tpvec, _Tp, suffix1, suffix2, v_trunc) \ +inline _Tpvec v_lut_pairs(const _Tp* tab, const int* idx) \ +{ \ + auto v0 = vle32_v_u32##suffix1((unsigned*)idx, VTraits<_Tpvec>::vlanes()/2); \ + auto v1 = vadd(v0, 1, VTraits<_Tpvec>::vlanes()/2); \ + auto w0 = vwcvtu_x(v0, VTraits<_Tpvec>::vlanes()/2); \ + auto w1 = vwcvtu_x(v1, VTraits<_Tpvec>::vlanes()/2); \ + auto sh1 = vslide1up(v_trunc(vreinterpret_u32##suffix2(w1)),0, VTraits<_Tpvec>::vlanes()); \ + auto vid = vor(sh1, v_trunc(vreinterpret_u32##suffix2(w0)), VTraits<_Tpvec>::vlanes()); \ + auto vidx = vmul(vid, sizeof(_Tp), VTraits<_Tpvec>::vlanes()); \ + return vloxei32(tab, vidx, VTraits<_Tpvec>::vlanes()); \ +} +OPENCV_HAL_IMPL_RVV_LUT_PAIRS(v_int8, schar, m2, m4, OPENCV_HAL_NOP) +OPENCV_HAL_IMPL_RVV_LUT_PAIRS(v_int16, short, m1, m2, OPENCV_HAL_NOP) +OPENCV_HAL_IMPL_RVV_LUT_PAIRS(v_int32, int, mf2, m1, OPENCV_HAL_NOP) +OPENCV_HAL_IMPL_RVV_LUT_PAIRS(v_float32, float, mf2, m1, OPENCV_HAL_NOP) +OPENCV_HAL_IMPL_RVV_LUT_PAIRS(v_int64, int64_t, mf2, m1, vlmul_trunc_u32mf2) +#if CV_SIMD_SCALABLE_64F +OPENCV_HAL_IMPL_RVV_LUT_PAIRS(v_float64, double, mf2, m1, vlmul_trunc_u32mf2) +#endif + + +#define OPENCV_HAL_IMPL_RVV_LUT_QUADS(_Tpvec, _Tp, suffix0, suffix1, suffix2, v_trunc) \ +inline _Tpvec v_lut_quads(const _Tp* tab, const int* idx) \ +{ \ + auto v0 = vle32_v_u32##suffix0((unsigned*)idx, VTraits<_Tpvec>::vlanes()/4); \ + auto v1 = vadd(v0, 1, VTraits<_Tpvec>::vlanes()/4); \ + auto v2 = vadd(v0, 2, VTraits<_Tpvec>::vlanes()/4); \ + auto v3 = vadd(v0, 3, VTraits<_Tpvec>::vlanes()/4); \ + auto w0 = vwcvtu_x(v0, VTraits<_Tpvec>::vlanes()/4); \ + auto w1 = vwcvtu_x(v1, VTraits<_Tpvec>::vlanes()/4); \ + auto w2 = vwcvtu_x(v2, VTraits<_Tpvec>::vlanes()/4); \ + auto w3 = vwcvtu_x(v3, VTraits<_Tpvec>::vlanes()/4); \ + auto sh2 = vslide1up(vreinterpret_u32##suffix1(w2),0, VTraits<_Tpvec>::vlanes()/2); \ + auto sh3 = vslide1up(vreinterpret_u32##suffix1(w3),0, VTraits<_Tpvec>::vlanes()/2); \ + auto vid0 = vor(sh2, vreinterpret_u32##suffix1(w0), VTraits<_Tpvec>::vlanes()/2); \ + auto vid1 = vor(sh3, vreinterpret_u32##suffix1(w1), VTraits<_Tpvec>::vlanes()/2); \ + auto wid0 = vwcvtu_x(v_trunc(vid0), VTraits<_Tpvec>::vlanes()/2); \ + auto wid1 = vwcvtu_x(v_trunc(vid1), VTraits<_Tpvec>::vlanes()/2); \ + auto shwid1 = vslide1up(vreinterpret_u32##suffix2(wid1),0, VTraits<_Tpvec>::vlanes()); \ + auto vid = vor(shwid1, vreinterpret_u32##suffix2(wid0), VTraits<_Tpvec>::vlanes()); \ + auto vidx = vmul(vid, sizeof(_Tp), VTraits<_Tpvec>::vlanes()); \ + return vloxei32(tab, vidx, VTraits<_Tpvec>::vlanes()); \ +} +OPENCV_HAL_IMPL_RVV_LUT_QUADS(v_int8, schar, m1, m2, m4, OPENCV_HAL_NOP) +OPENCV_HAL_IMPL_RVV_LUT_QUADS(v_int16, short, mf2 , m1, m2, OPENCV_HAL_NOP) +OPENCV_HAL_IMPL_RVV_LUT_QUADS(v_int32, int, mf2, m1, m1, vlmul_trunc_u32mf2) +OPENCV_HAL_IMPL_RVV_LUT_QUADS(v_float32, float, mf2, m1, m1, vlmul_trunc_u32mf2) + #define OPENCV_HAL_IMPL_RVV_LUT_VEC(_Tpvec, _Tp) \ inline _Tpvec v_lut(const _Tp* tab, const v_int32& vidx) \ { \ @@ -509,7 +539,6 @@ inline v_uint32 v_lut_pairs(const unsigned* tab, const int* idx) { return v_rein inline v_uint32 v_lut_quads(const unsigned* tab, const int* idx) { return v_reinterpret_as_u32(v_lut_quads((int*)tab, idx)); } inline v_uint64 v_lut(const uint64* tab, const int* idx) { return v_reinterpret_as_u64(v_lut((const int64_t *)tab, idx)); } inline v_uint64 v_lut_pairs(const uint64* tab, const int* idx) { return v_reinterpret_as_u64(v_lut_pairs((const int64_t *)tab, idx)); } -inline v_uint64 v_lut_quads(const uint64* tab, const int* idx) { return v_reinterpret_as_u64(v_lut_quads((const int64_t*)tab, idx)); } ////////////// Pack boolean //////////////////// inline v_uint8 v_pack_b(const v_uint16& a, const v_uint16& b) @@ -1390,23 +1419,23 @@ OPENCV_HAL_IMPL_RVV_REVERSE(v_float64, 64) #define OPENCV_HAL_IMPL_RVV_EXPAND(_Tp, _Tpwvec, _Tpwvec_m2, _Tpvec, width, suffix, suffix2, cvt) \ inline void v_expand(const _Tpvec& a, _Tpwvec& b0, _Tpwvec& b1) \ { \ - _Tpwvec_m2 temp = cvt(a, vsetvlmax_e##width##m1()); \ + _Tpwvec_m2 temp = cvt(a, VTraits<_Tpvec>::vlanes()); \ b0 = vget_##suffix##m1(temp, 0); \ b1 = vget_##suffix##m1(temp, 1); \ } \ inline _Tpwvec v_expand_low(const _Tpvec& a) \ { \ - _Tpwvec_m2 temp = cvt(a, vsetvlmax_e##width##m1()); \ + _Tpwvec_m2 temp = cvt(a, VTraits<_Tpvec>::vlanes()); \ return vget_##suffix##m1(temp, 0); \ } \ inline _Tpwvec v_expand_high(const _Tpvec& a) \ { \ - _Tpwvec_m2 temp = cvt(a, vsetvlmax_e##width##m1()); \ + _Tpwvec_m2 temp = cvt(a, VTraits<_Tpvec>::vlanes()); \ return vget_##suffix##m1(temp, 1); \ } \ inline _Tpwvec v_load_expand(const _Tp* ptr) \ { \ - return cvt(vle##width##_v_##suffix2##mf2(ptr, vsetvlmax_e##width##m1()), vsetvlmax_e##width##m1()); \ + return cvt(vle##width##_v_##suffix2##mf2(ptr, VTraits<_Tpvec>::vlanes()), VTraits<_Tpvec>::vlanes()); \ } OPENCV_HAL_IMPL_RVV_EXPAND(uchar, v_uint16, vuint16m2_t, v_uint8, 8, u16, u8, vwcvtu_x) @@ -1759,8 +1788,8 @@ inline int v_scan_forward(const v_float64& a) // mask: {0,0,0,1, ...} -> {T,T,T,F, ...} #define OPENCV_HAL_IMPL_RVV_PACK_TRIPLETS(_Tpvec, v_trunc) \ inline _Tpvec v_pack_triplets(const _Tpvec& vec) { \ - size_t vl = vsetvlmax_e8m1(); \ - vuint32m1_t one = vmv_v_x_u32m1(1, vl/4); \ + size_t vl = __cv_rvv_e8m1_nlanes; \ + vuint32m1_t one = vmv_v_x_u32m1(1, __cv_rvv_e32m1_nlanes); \ vuint8m1_t zero = vmv_v_x_u8m1(0, vl); \ vuint8m1_t mask = vreinterpret_u8m1(one); \ return vcompress(vmseq(v_trunc(vslideup(zero, mask, 3, vl)), 0, vl), vec, vec, VTraits<_Tpvec>::vlanes()); \ @@ -2126,6 +2155,8 @@ inline void v_cleanup() {} CV_CPU_OPTIMIZATION_HAL_NAMESPACE_END +//! @endcond + } //namespace cv #endif //OPENCV_HAL_INTRIN_RVV_SCALABLE_HPP diff --git a/modules/core/include/opencv2/core/matx.hpp b/modules/core/include/opencv2/core/matx.hpp index fae4526584..8d5c2e49f8 100644 --- a/modules/core/include/opencv2/core/matx.hpp +++ b/modules/core/include/opencv2/core/matx.hpp @@ -781,7 +781,7 @@ inline Matx<_Tp, m, n>::operator Matx() const template template inline Matx<_Tp, m1, n1> Matx<_Tp, m, n>::reshape() const { - CV_StaticAssert(m1*n1 == m*n, "Input and destnarion matrices must have the same number of elements"); + CV_StaticAssert(m1*n1 == m*n, "Input and destination matrices must have the same number of elements"); return (const Matx<_Tp, m1, n1>&)*this; } diff --git a/modules/core/perf/perf_arithm.cpp b/modules/core/perf/perf_arithm.cpp index c48e23a8f6..009260ddbb 100644 --- a/modules/core/perf/perf_arithm.cpp +++ b/modules/core/perf/perf_arithm.cpp @@ -1,6 +1,6 @@ #include "perf_precomp.hpp" -#include "opencv2/core/softfloat.hpp" #include +#include "opencv2/core/softfloat.hpp" namespace opencv_test { @@ -452,7 +452,6 @@ INSTANTIATE_TEST_CASE_P(/*nothing*/ , BinaryOpTest, ) ); - ///////////// PatchNaNs //////////////////////// template diff --git a/modules/core/src/arithm.cpp b/modules/core/src/arithm.cpp index 44e9c6324b..73683ac235 100644 --- a/modules/core/src/arithm.cpp +++ b/modules/core/src/arithm.cpp @@ -1646,7 +1646,7 @@ static bool ocl_inRange( InputArray _src, InputArray _lowerb, if (kercn % cn != 0) kercn = cn; int colsPerWI = kercn / cn; - String opts = format("%s-D cn=%d -D srcT=%s -D srcT1=%s -D dstT=%s -D kercn=%d -D depth=%d%s -D colsPerWI=%d", + String opts = format("%s-D CN=%d -D SRC_T=%s -D SRC_T1=%s -D DST_T=%s -D KERCN=%d -D DEPTH=%d%s -D COLS_PER_WI=%d", haveScalar ? "-D HAVE_SCALAR " : "", cn, ocl::typeToStr(CV_MAKE_TYPE(sdepth, kercn)), ocl::typeToStr(sdepth), ocl::typeToStr(CV_8UC(colsPerWI)), kercn, sdepth, doubleSupport ? " -D DOUBLE_SUPPORT" : "", colsPerWI); diff --git a/modules/core/src/arithm.simd.hpp b/modules/core/src/arithm.simd.hpp index 3f2f3f10a1..1745a8517d 100644 --- a/modules/core/src/arithm.simd.hpp +++ b/modules/core/src/arithm.simd.hpp @@ -861,7 +861,7 @@ static void cmp_loop_nosimd(const double* src1, size_t step1, const double* src2 } // todo: try to avoid define dispatcher functions using macros with these such cases -DEFINE_SIMD_ALL(cmp) +DEFINE_SIMD_ALL(cmp, void) //========================================================================= // scaling helpers for single and dual source diff --git a/modules/core/src/opencl/inrange.cl b/modules/core/src/opencl/inrange.cl index 538259539a..a5efd38bcf 100644 --- a/modules/core/src/opencl/inrange.cl +++ b/modules/core/src/opencl/inrange.cl @@ -52,7 +52,7 @@ __kernel void inrange(__global const uchar * src1ptr, int src1_step, int src1_offset, __global uchar * dstptr, int dst_step, int dst_offset, int dst_rows, int dst_cols, #ifdef HAVE_SCALAR - __global const srcT1 * src2, __global const srcT1 * src3, + __global const SRC_T1 * src2, __global const SRC_T1 * src3, #else __global const uchar * src2ptr, int src2_step, int src2_offset, __global const uchar * src3ptr, int src3_step, int src3_offset, @@ -64,56 +64,56 @@ __kernel void inrange(__global const uchar * src1ptr, int src1_step, int src1_of if (x < dst_cols) { - int src1_index = mad24(y0, src1_step, mad24(x, (int)sizeof(srcT1) * kercn, src1_offset)); - int dst_index = mad24(y0, dst_step, mad24(x, colsPerWI, dst_offset)); + int src1_index = mad24(y0, src1_step, mad24(x, (int)sizeof(SRC_T1) * KERCN, src1_offset)); + int dst_index = mad24(y0, dst_step, mad24(x, COLS_PER_WI, dst_offset)); #ifndef HAVE_SCALAR - int src2_index = mad24(y0, src2_step, mad24(x, (int)sizeof(srcT1) * kercn, src2_offset)); - int src3_index = mad24(y0, src3_step, mad24(x, (int)sizeof(srcT1) * kercn, src3_offset)); + int src2_index = mad24(y0, src2_step, mad24(x, (int)sizeof(SRC_T1) * KERCN, src2_offset)); + int src3_index = mad24(y0, src3_step, mad24(x, (int)sizeof(SRC_T1) * KERCN, src3_offset)); #endif for (int y = y0, y1 = min(dst_rows, y0 + rowsPerWI); y < y1; ++y, src1_index += src1_step, dst_index += dst_step) { -#if kercn >= cn && kercn == 4 && depth <= 4 && !defined HAVE_SCALAR - srcT src1 = *(__global const srcT *)(src1ptr + src1_index); - srcT src2 = *(__global const srcT *)(src2ptr + src2_index); - srcT src3 = *(__global const srcT *)(src3ptr + src3_index); - __global dstT * dst = (__global dstT *)(dstptr + dst_index); -#if cn == 1 - dst[0] = src2 > src1 || src3 < src1 ? (dstT)(0) : (dstT)(255); -#elif cn == 2 - dst[0] = (dstT)(src2.xy > src1.xy || src3.xy < src1.xy || - src2.zw > src1.zw || src3.zw < src1.zw ? (dstT)(0) : (dstT)(255); -#elif cn == 4 - dst[0] = (dstT)(src2.x > src1.x || src3.x < src1.x || +#if KERCN >= CN && KERCN == 4 && DEPTH <= 4 && !defined HAVE_SCALAR + SRC_T src1 = *(__global const SRC_T *)(src1ptr + src1_index); + SRC_T src2 = *(__global const SRC_T *)(src2ptr + src2_index); + SRC_T src3 = *(__global const SRC_T *)(src3ptr + src3_index); + __global DST_T * dst = (__global DST_T *)(dstptr + dst_index); +#if CN == 1 + dst[0] = src2 > src1 || src3 < src1 ? (DST_T)(0) : (DST_T)(255); +#elif CN == 2 + dst[0] = (DST_T)(src2.xy > src1.xy || src3.xy < src1.xy || + src2.zw > src1.zw || src3.zw < src1.zw ? (DST_T)(0) : (DST_T)(255); +#elif CN == 4 + dst[0] = (DST_T)(src2.x > src1.x || src3.x < src1.x || src2.y > src1.y || src3.y < src1.y || src2.z > src1.z || src3.z < src1.z || src2.w > src1.w || src3.w < src1.w ? 0 : 255); #endif #else - __global const srcT1 * src1 = (__global const srcT1 *)(src1ptr + src1_index); + __global const SRC_T1 * src1 = (__global const SRC_T1 *)(src1ptr + src1_index); __global uchar * dst = dstptr + dst_index; #ifndef HAVE_SCALAR - __global const srcT1 * src2 = (__global const srcT1 *)(src2ptr + src2_index); - __global const srcT1 * src3 = (__global const srcT1 *)(src3ptr + src3_index); + __global const SRC_T1 * src2 = (__global const SRC_T1 *)(src2ptr + src2_index); + __global const SRC_T1 * src3 = (__global const SRC_T1 *)(src3ptr + src3_index); #endif #pragma unroll - for (int px = 0; px < colsPerWI; ++px, src1 += cn + for (int px = 0; px < COLS_PER_WI; ++px, src1 += CN #ifndef HAVE_SCALAR - , src2 += cn, src3 += cn + , src2 += CN, src3 += CN #endif ) { dst[px] = 255; - for (int c = 0; c < cn; ++c) + for (int c = 0; c < CN; ++c) if (src2[c] > src1[c] || src3[c] < src1[c]) { dst[px] = 0; break; } } -#endif // kercn >= cn +#endif // KERCN >= CN #ifndef HAVE_SCALAR src2_index += src2_step; src3_index += src3_step; diff --git a/modules/core/src/system.cpp b/modules/core/src/system.cpp index 0316ab375c..b4fb466bb0 100644 --- a/modules/core/src/system.cpp +++ b/modules/core/src/system.cpp @@ -154,6 +154,12 @@ void* allocSingletonNewBuffer(size_t size) { return malloc(size); } # endif #endif +#if defined __loongarch64 +#include "sys/auxv.h" +#define LA_HWCAP_LSX (1<<4) +#define LA_HWCAP_LASX (1<<5) +#endif + #if defined _WIN32 || defined WINCE #ifndef _WIN32_WINNT // This is needed for the declaration of TryEnterCriticalSection in winbase.h with Visual Studio 2005 (and older?) #define _WIN32_WINNT 0x0400 // http://msdn.microsoft.com/en-us/library/ms686857(VS.85).aspx @@ -704,12 +710,11 @@ struct HWFeatures have[CV_CPU_RVV] = true; #endif - #if defined __loongarch_sx - have[CV_CPU_LSX] = true; - #endif + #if defined __loongarch64 && defined __linux__ + int flag = (int)getauxval(AT_HWCAP); - #if defined __loongarch_asx - have[CV_CPU_LASX] = true; + have[CV_CPU_LSX] = (flag & LA_HWCAP_LSX) != 0; + have[CV_CPU_LASX] = (flag & LA_HWCAP_LASX) != 0; #endif bool skip_baseline_check = false; diff --git a/modules/core/test/ocl/test_image2d.cpp b/modules/core/test/ocl/test_image2d.cpp index 86561bddcf..07652e5c9d 100644 --- a/modules/core/test/ocl/test_image2d.cpp +++ b/modules/core/test/ocl/test_image2d.cpp @@ -83,7 +83,7 @@ TEST(Image2D, turnOffOpenCL) } else std::cout << "CV_8UC1 is not supported for OpenCL images. Test skipped." << std::endl; - + // reset state to the previous one cv::ocl::setUseOpenCL(useOCL); } diff --git a/modules/core/test/test_operations.cpp b/modules/core/test/test_operations.cpp index 430f285438..b5bf70d43a 100644 --- a/modules/core/test/test_operations.cpp +++ b/modules/core/test/test_operations.cpp @@ -1571,4 +1571,54 @@ TEST(Core_Arithm, scalar_handling_19599) // https://github.com/opencv/opencv/is EXPECT_EQ(1, c.rows); } +// https://github.com/opencv/opencv/issues/24163 +typedef tuple Arith_Regression24163Param; +typedef testing::TestWithParam Core_Arith_Regression24163; + +#if defined __riscv +TEST_P(Core_Arith_Regression24163, DISABLED_test_for_ties_to_even) +#else +TEST_P(Core_Arith_Regression24163, test_for_ties_to_even) +#endif +{ + const int matDepth = get<0>(GetParam()); + const int matHeight= get<1>(GetParam()); + const int matWidth = 3; // Fixed + const int alpha = get<2>(GetParam()); + const int beta = get<3>(GetParam()); + + // If alpha and/or beta are negative, and matDepth is unsigned, test is passed. + if( ( (alpha < 0) || (beta < 0) ) + && + ( (matDepth != CV_8S) && (matDepth != CV_16S) && (matDepth != CV_32S) ) ) + { + throw SkipTestException( cv::format("Test is skipped(matDepth is not signed, alpha = %d, beta = %d)", alpha, beta) ); + } + + const int matType = CV_MAKE_TYPE(matDepth, 1); + const Size matSize(matWidth, matHeight); + const Mat src1(matSize, matType, Scalar(alpha,alpha,alpha,alpha)); + const Mat src2(matSize, matType, Scalar(beta, beta, beta, beta)); + const Mat result = ( src1 + src2 ) / 2; + + // Expected that default is FE_TONEAREST(Ties to Even). + const int mean = lrint( static_cast(alpha + beta) / 2.0 ); + const Mat expected(matSize, matType, Scalar(mean,mean,mean,mean)); + + // Compare result and extected. + ASSERT_EQ(expected.size(), result.size()); + EXPECT_EQ(0, cvtest::norm(expected, result, NORM_INF)) << + "result=" << std::endl << result << std::endl << + "expected=" << std::endl << expected; +} + +INSTANTIATE_TEST_CASE_P(/* */, Core_Arith_Regression24163, + testing::Combine( + testing::Values(perf::MatDepth(CV_8U), CV_8S, CV_16U, CV_16S, CV_32S), // MatType + testing::Values( 3, 4, 5, 6), // MatHeight + testing::Values(-2,-1, 0, 1, 2), // src1 + testing::Values( -1, 0, 1 ) // src2 + ) +); + }} // namespace diff --git a/modules/dnn/CMakeLists.txt b/modules/dnn/CMakeLists.txt index 9122168c65..2832052822 100644 --- a/modules/dnn/CMakeLists.txt +++ b/modules/dnn/CMakeLists.txt @@ -6,9 +6,9 @@ set(the_description "Deep neural network module. It allows to load models from d ocv_add_dispatched_file_force_all("layers/layers_common" AVX AVX2 AVX512_SKX RVV LASX) ocv_add_dispatched_file_force_all("int8layers/layers_common" AVX2 AVX512_SKX LASX) -ocv_add_dispatched_file_force_all("layers/cpu_kernels/conv_block" AVX AVX2) +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) -ocv_add_dispatched_file_force_all("layers/cpu_kernels/conv_winograd_f63" AVX AVX2) +ocv_add_dispatched_file_force_all("layers/cpu_kernels/conv_winograd_f63" AVX AVX2 NEON_FP16) ocv_add_dispatched_file_force_all("layers/cpu_kernels/fast_gemm_kernels" AVX AVX2 NEON LASX) ocv_add_module(dnn opencv_core opencv_imgproc WRAP python java objc js) @@ -164,6 +164,12 @@ if(OPENCV_DNN_CUDA AND HAVE_CUDA AND HAVE_CUBLAS AND HAVE_CUDNN) endif() endforeach() unset(CC_LIST) + if(ENABLE_CUDA_FIRST_CLASS_LANGUAGE) + list(APPEND libs ${CUDNN_LIBRARIES} CUDA::cublas${CUDA_LIB_EXT}) + if(NOT CUDA_VERSION VERSION_LESS 10.1) + list(APPEND libs CUDA::cublasLt${CUDA_LIB_EXT}) + endif() + endif() else() set(sources_options ${sources_options} EXCLUDE_CUDA) endif() diff --git a/modules/dnn/include/opencv2/dnn/all_layers.hpp b/modules/dnn/include/opencv2/dnn/all_layers.hpp index fc48607f31..95982f6fb0 100644 --- a/modules/dnn/include/opencv2/dnn/all_layers.hpp +++ b/modules/dnn/include/opencv2/dnn/all_layers.hpp @@ -291,7 +291,7 @@ CV__DNN_INLINE_NS_BEGIN static Ptr create(const LayerParams& params); bool fusedActivation = false; bool fusedAdd = false; - bool useWinograd = false; // Flag whether to use Winograd to speed up 3x3 convolution. + bool useWinograd = true; // Flag whether to use Winograd to speed up 3x3 convolution. }; class CV_EXPORTS ConvolutionLayerInt8 : public BaseConvolutionLayer @@ -303,7 +303,7 @@ CV__DNN_INLINE_NS_BEGIN // quantization type flag. The perChannel default is true, that means it contains the parameters // of per-Channel quantization. Otherwise, that means this layer contains per-Tensor quantized parameters. bool per_channel; - bool useWinograd = true; // Flag whether to use Winograd to speed up 3x3 convolution. + bool useWinograd = false; // Flag whether to use Winograd to speed up 3x3 convolution. static Ptr create(const LayerParams& params); }; @@ -1160,12 +1160,29 @@ CV__DNN_INLINE_NS_BEGIN static Ptr create(const LayerParams& params); }; + class CV_EXPORTS MatMulLayer : public Layer { + public: + static Ptr create(const LayerParams ¶ms); + }; + class CV_EXPORTS ExpandLayer : public Layer { public: static Ptr create(const LayerParams ¶ms); }; + class CV_EXPORTS InstanceNormLayer : public Layer { + public: + float epsilon; + + static Ptr create(const LayerParams ¶ms); + }; + + class CV_EXPORTS AttentionLayer : public Layer { + public: + static Ptr create(const LayerParams ¶ms); + }; + //! @} //! @} CV__DNN_INLINE_NS_END diff --git a/modules/dnn/include/opencv2/dnn/dnn.hpp b/modules/dnn/include/opencv2/dnn/dnn.hpp index 88ba31410b..cf034db6f0 100644 --- a/modules/dnn/include/opencv2/dnn/dnn.hpp +++ b/modules/dnn/include/opencv2/dnn/dnn.hpp @@ -1127,7 +1127,7 @@ CV__DNN_INLINE_NS_BEGIN CV_WRAP Image2BlobParams(); CV_WRAP Image2BlobParams(const Scalar& scalefactor, const Size& size = Size(), const Scalar& mean = Scalar(), bool swapRB = false, int ddepth = CV_32F, DataLayout datalayout = DNN_LAYOUT_NCHW, - ImagePaddingMode mode = DNN_PMODE_NULL); + ImagePaddingMode mode = DNN_PMODE_NULL, Scalar borderValue = 0.0); CV_PROP_RW Scalar scalefactor; //!< scalefactor multiplier for input image values. CV_PROP_RW Size size; //!< Spatial size for output image. @@ -1136,6 +1136,21 @@ CV__DNN_INLINE_NS_BEGIN CV_PROP_RW int ddepth; //!< Depth of output blob. Choose CV_32F or CV_8U. CV_PROP_RW DataLayout datalayout; //!< Order of output dimensions. Choose DNN_LAYOUT_NCHW or DNN_LAYOUT_NHWC. CV_PROP_RW ImagePaddingMode paddingmode; //!< Image padding mode. @see ImagePaddingMode. + CV_PROP_RW Scalar borderValue; //!< Value used in padding mode for padding. + + /** @brief Get rectangle coordinates in original image system from rectangle in blob coordinates. + * @param rBlob rect in blob coordinates. + * @param size original input image size. + * @returns rectangle in original image coordinates. + */ + CV_WRAP Rect blobRectToImageRect(const Rect &rBlob, const Size &size); + + /** @brief Get rectangle coordinates in original image system from rectangle in blob coordinates. + * @param rBlob rect in blob coordinates. + * @param rImg result rect in image coordinates. + * @param size original input image size. + */ + CV_WRAP void blobRectsToImageRects(const std::vector &rBlob, CV_OUT std::vector& rImg, const Size& size); }; /** @brief Creates 4-dimensional blob from image with given params. @@ -1373,6 +1388,9 @@ CV__DNN_INLINE_NS_BEGIN /// @sa Net::setPreferableTarget CV_WRAP Model& setPreferableTarget(dnn::Target targetId); + /// @sa Net::enableWinograd + CV_WRAP Model& enableWinograd(bool useWinograd); + CV_DEPRECATED_EXTERNAL operator Net&() const { return getNetwork_(); } diff --git a/modules/dnn/include/opencv2/dnn/version.hpp b/modules/dnn/include/opencv2/dnn/version.hpp index a1cfca0e1d..ed6fc5b4c5 100644 --- a/modules/dnn/include/opencv2/dnn/version.hpp +++ b/modules/dnn/include/opencv2/dnn/version.hpp @@ -6,7 +6,7 @@ #define OPENCV_DNN_VERSION_HPP /// Use with major OpenCV version only. -#define OPENCV_DNN_API_VERSION 20230620 +#define OPENCV_DNN_API_VERSION 20231225 #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) diff --git a/modules/dnn/misc/python/test/test_dnn.py b/modules/dnn/misc/python/test/test_dnn.py index a06c02ad2d..da3409c3b3 100644 --- a/modules/dnn/misc/python/test/test_dnn.py +++ b/modules/dnn/misc/python/test/test_dnn.py @@ -127,6 +127,34 @@ class dnn_test(NewOpenCVTests): targets = cv.dnn.getAvailableTargets(cv.dnn.DNN_BACKEND_OPENCV) self.assertTrue(cv.dnn.DNN_TARGET_CPU in targets) + def test_blobRectsToImageRects(self): + paramNet = cv.dnn.Image2BlobParams() + paramNet.size = (226, 226) + paramNet.ddepth = cv.CV_32F + paramNet.mean = [0.485, 0.456, 0.406] + paramNet.scalefactor = [0.229, 0.224, 0.225] + paramNet.swapRB = False + paramNet.datalayout = cv.dnn.DNN_LAYOUT_NCHW + paramNet.paddingmode = cv.dnn.DNN_PMODE_LETTERBOX + rBlob = np.zeros(shape=(20, 4), dtype=np.int32) + rImg = paramNet.blobRectsToImageRects(rBlob, (356, 356)) + self.assertTrue(type(rImg[0, 0])==np.int32) + self.assertTrue(rImg.shape==(20, 4)) + + def test_blobRectToImageRect(self): + paramNet = cv.dnn.Image2BlobParams() + paramNet.size = (226, 226) + paramNet.ddepth = cv.CV_32F + paramNet.mean = [0.485, 0.456, 0.406] + paramNet.scalefactor = [0.229, 0.224, 0.225] + paramNet.swapRB = False + paramNet.datalayout = cv.dnn.DNN_LAYOUT_NCHW + paramNet.paddingmode = cv.dnn.DNN_PMODE_LETTERBOX + rBlob = np.zeros(shape=(20, 4), dtype=np.int32) + rImg = paramNet.blobRectToImageRect((0, 0, 0, 0), (356, 356)) + self.assertTrue(type(rImg[0])==int) + + def test_blobFromImage(self): np.random.seed(324) diff --git a/modules/dnn/perf/perf_convolution.cpp b/modules/dnn/perf/perf_convolution.cpp index 1ecd29227c..2c33969a76 100644 --- a/modules/dnn/perf/perf_convolution.cpp +++ b/modules/dnn/perf/perf_convolution.cpp @@ -4,6 +4,7 @@ #include "perf_precomp.hpp" #include +#include namespace opencv_test { @@ -26,753 +27,765 @@ struct ConvParam_t { double declared_flops; }; // Details: #12142 -// Last update: 2021-09 -static const ConvParam_t testConvolutionConfigs[] = { +// Last update: 2023-11 +// Extended and classified: #24547 +static const ConvParam_t testConvolution_Configs[] = { /* GFLOPS 3.398 x 20 = 67.956 */ {{7, 7}, {{1, 128, 46, 46}}, 128, 1, {1, 1}, {1, 1}, {3, 3}, {0, 0}, "", true, 3397788160.}, /* GFLOPS 16.987 x 3 = 50.962 */ {{5, 5}, {{1, 1152, 16, 16}}, 1152, 1, {1, 1}, {1, 1}, {0, 0}, {0, 0}, "SAME", false, 16987226112.}, /* GFLOPS 23.122 x 2 = 46.244 */ {{5, 5}, {{1, 672, 32, 32}}, 672, 1, {1, 1}, {1, 1}, {0, 0}, {0, 0}, "SAME", false, 23121788928.}, - /* GFLOPS 9.987 x 3 = 29.960 */ {{3, 3}, {{1, 256, 92, 92}}, 256, 1, {1, 1}, {1, 1}, {1, 1}, {0, 0}, "", true, 9986707456.}, - /* GFLOPS 1.595 x 16 = 25.524 */ {{3, 3}, {{1, 256, 26, 26}}, 512, 1, {1, 1}, {1, 1}, {1, 1}, {0, 0}, "", false, 1595230208.}, /* GFLOPS 4.566 x 5 = 22.828 */ {{7, 7}, {{1, 172, 46, 46}}, 128, 1, {1, 1}, {1, 1}, {3, 3}, {0, 0}, "", true, 4565684736.}, - /* GFLOPS 1.596 x 14 = 22.338 */ {{3, 3}, {{1, 128, 52, 52}}, 256, 1, {1, 1}, {1, 1}, {1, 1}, {0, 0}, "", false, 1595576320.}, - /* GFLOPS 1.595 x 12 = 19.141 */ {{3, 3}, {{1, 512, 13, 13}}, 1024, 1, {1, 1}, {1, 1}, {1, 1}, {0, 0}, "", false, 1595057152.}, - /* GFLOPS 6.814 x 2 = 13.629 */ {{3, 3}, {{1, 512, 38, 38}}, 512, 1, {1, 1}, {1, 1}, {1, 1}, {0, 0}, "", true, 6814386176.}, - /* GFLOPS 6.637 x 2 = 13.274 */ {{3, 3}, {{1, 256, 75, 75}}, 256, 1, {1, 1}, {1, 1}, {1, 1}, {0, 0}, "", true, 6636960000.}, /* GFLOPS 11.797 x 1 = 11.797 */ {{5, 5}, {{1, 240, 64, 64}}, 240, 1, {1, 1}, {1, 1}, {0, 0}, {0, 0}, "SAME", false, 11797463040.}, /* GFLOPS 11.797 x 1 = 11.797 */ {{5, 5}, {{1, 480, 32, 32}}, 480, 1, {1, 1}, {1, 1}, {0, 0}, {0, 0}, "SAME", false, 11796971520.}, - /* GFLOPS 10.701 x 1 = 10.701 */ {{3, 3}, {{1, 512, 38, 38}}, 804, 1, {1, 1}, {1, 1}, {1, 1}, {0, 0}, "", true, 10700715792.}, - /* GFLOPS 10.087 x 1 = 10.087 */ {{3, 3}, {{1, 576, 38, 50}}, 512, 1, {1, 1}, {1, 1}, {0, 0}, {0, 0}, "SAME", true, 10086963200.}, - /* GFLOPS 9.993 x 1 = 9.993 */ {{3, 3}, {{1, 64, 368, 368}}, 64, 1, {1, 1}, {1, 1}, {1, 1}, {0, 0}, "", true, 9993207808.}, - /* GFLOPS 9.989 x 1 = 9.989 */ {{3, 3}, {{1, 128, 184, 184}}, 128, 1, {1, 1}, {1, 1}, {1, 1}, {0, 0}, "", true, 9988874240.}, - /* GFLOPS 9.986 x 1 = 9.986 */ {{3, 3}, {{1, 512, 46, 46}}, 512, 1, {1, 1}, {1, 1}, {1, 1}, {0, 0}, "", true, 9985624064.}, - /* GFLOPS 1.704 x 5 = 8.518 */ {{3, 3}, {{1, 512, 19, 19}}, 512, 512, {1, 1}, {1, 1}, {1, 1}, {0, 0}, "", true, 1703596544.}, - /* GFLOPS 1.704 x 5 = 8.518 */ {{3, 3}, {{1, 512, 19, 19}}, 512, 1, {1, 1}, {1, 1}, {0, 0}, {0, 0}, "SAME", false, 1703596544.}, - /* GFLOPS 4.247 x 2 = 8.494 */ {{3, 3}, {{1, 480, 32, 32}}, 480, 1, {1, 1}, {1, 1}, {0, 0}, {0, 0}, "SAME", false, 4247224320.}, - /* GFLOPS 8.025 x 1 = 8.025 */ {{3, 3}, {{1, 1024, 19, 19}}, 1206, 1, {1, 1}, {1, 1}, {1, 1}, {0, 0}, "", true, 8025101478.}, - /* GFLOPS 0.798 x 9 = 7.180 */ {{3, 3}, {{1, 128, 52, 52}}, 128, 1, {1, 1}, {1, 1}, {1, 1}, {0, 0}, "", false, 797788160.}, - /* GFLOPS 0.798 x 9 = 7.179 */ {{3, 3}, {{1, 256, 26, 26}}, 256, 1, {1, 1}, {1, 1}, {1, 1}, {0, 0}, "", false, 797615104.}, - /* GFLOPS 6.641 x 1 = 6.641 */ {{3, 3}, {{1, 64, 300, 300}}, 64, 1, {1, 1}, {1, 1}, {1, 1}, {0, 0}, "", true, 6641280000.}, - /* GFLOPS 6.641 x 1 = 6.641 */ {{3, 3}, {{1, 64, 150, 200}}, 192, 1, {1, 1}, {1, 1}, {0, 0}, {0, 0}, "SAME", true, 6641280000.}, - /* GFLOPS 6.638 x 1 = 6.638 */ {{3, 3}, {{1, 128, 150, 150}}, 128, 1, {1, 1}, {1, 1}, {1, 1}, {0, 0}, "", true, 6638400000.}, - /* GFLOPS 6.118 x 1 = 6.118 */ {{3, 3}, {{1, 144, 128, 128}}, 144, 1, {1, 1}, {1, 1}, {0, 0}, {0, 0}, "SAME", false, 6117654528.}, - /* GFLOPS 6.116 x 1 = 6.116 */ {{3, 3}, {{1, 1152, 16, 16}}, 1152, 1, {1, 1}, {1, 1}, {0, 0}, {0, 0}, "SAME", false, 6115590144.}, /* GFLOPS 5.780 x 1 = 5.780 */ {{5, 5}, {{1, 672, 32, 32}}, 672, 1, {2, 2}, {1, 1}, {0, 0}, {0, 0}, "SAME", false, 5780447232.}, - /* GFLOPS 1.704 x 3 = 5.111 */ {{3, 3}, {{1, 512, 19, 19}}, 512, 1, {1, 1}, {1, 1}, {1, 1}, {0, 0}, "", true, 1703596544.}, - /* GFLOPS 4.997 x 1 = 4.997 */ {{3, 3}, {{1, 64, 184, 184}}, 128, 1, {1, 1}, {1, 1}, {1, 1}, {0, 0}, "", true, 4996603904.}, - /* GFLOPS 4.994 x 1 = 4.994 */ {{3, 3}, {{1, 128, 92, 92}}, 256, 1, {1, 1}, {1, 1}, {1, 1}, {0, 0}, "", true, 4994437120.}, - /* GFLOPS 4.993 x 1 = 4.993 */ {{3, 3}, {{1, 256, 46, 46}}, 512, 1, {1, 1}, {1, 1}, {1, 1}, {0, 0}, "", true, 4993353728.}, - /* GFLOPS 4.993 x 1 = 4.993 */ {{3, 3}, {{1, 512, 46, 46}}, 256, 1, {1, 1}, {1, 1}, {1, 1}, {0, 0}, "", true, 4992812032.}, - /* GFLOPS 1.659 x 3 = 4.977 */ {{3, 3}, {{1, 960, 10, 10}}, 960, 1, {1, 1}, {1, 1}, {0, 0}, {0, 0}, "SAME", false, 1658976000.}, - /* GFLOPS 2.156 x 2 = 4.312 */ {{3, 3}, {{1, 576, 19, 19}}, 576, 1, {1, 1}, {1, 1}, {0, 0}, {0, 0}, "SAME", false, 2156088384.}, /* GFLOPS 4.247 x 1 = 4.247 */ {{5, 5}, {{1, 144, 128, 128}}, 144, 1, {2, 2}, {1, 1}, {0, 0}, {0, 0}, "SAME", false, 4247322624.}, - /* GFLOPS 0.798 x 5 = 3.988 */ {{3, 3}, {{1, 512, 13, 13}}, 512, 1, {1, 1}, {1, 1}, {1, 1}, {0, 0}, "", false, 797528576.}, - /* GFLOPS 0.958 x 4 = 3.833 */ {{3, 3}, {{1, 384, 19, 19}}, 384, 1, {1, 1}, {1, 1}, {0, 0}, {0, 0}, "SAME", false, 958307712.}, - /* GFLOPS 0.624 x 6 = 3.746 */ {{3, 3}, {{1, 128, 46, 46}}, 128, 1, {1, 1}, {1, 1}, {1, 1}, {0, 0}, "", true, 624304640.}, - /* GFLOPS 3.408 x 1 = 3.408 */ {{3, 3}, {{1, 256, 38, 38}}, 512, 1, {1, 1}, {1, 1}, {1, 1}, {0, 0}, "", true, 3407562752.}, /* GFLOPS 3.407 x 1 = 3.407 */ {{3, 3}, {{1, 512, 19, 19}}, 1024, 1, {1, 1}, {6, 6}, {6, 6}, {0, 0}, "", true, 3407193088.}, - /* GFLOPS 0.177 x 19 = 3.370 */ {{1, 1}, {{1, 512, 26, 26}}, 256, 1, {1, 1}, {1, 1}, {0, 0}, {0, 0}, "", false, 177382400.}, - /* GFLOPS 0.302 x 11 = 3.325 */ {{3, 3}, {{1, 64, 64, 64}}, 64, 1, {1, 1}, {1, 1}, {0, 0}, {0, 0}, "SAME", false, 302252032.}, - /* GFLOPS 3.321 x 1 = 3.321 */ {{3, 3}, {{1, 64, 150, 150}}, 128, 1, {1, 1}, {1, 1}, {1, 1}, {0, 0}, "", true, 3320640000.}, - /* GFLOPS 0.830 x 4 = 3.321 */ {{3, 3}, {{1, 64, 75, 100}}, 96, 1, {1, 1}, {1, 1}, {0, 0}, {0, 0}, "SAME", true, 830160000.}, - /* GFLOPS 3.319 x 1 = 3.319 */ {{3, 3}, {{1, 128, 75, 75}}, 256, 1, {1, 1}, {1, 1}, {1, 1}, {0, 0}, "", true, 3319200000.}, /* GFLOPS 1.598 x 2 = 3.195 */ {{3, 3}, {{1, 32, 416, 416}}, 64, 1, {2, 2}, {1, 1}, {1, 1}, {0, 0}, "", false, 1597652992.}, - /* GFLOPS 1.598 x 2 = 3.195 */ {{3, 3}, {{1, 32, 208, 208}}, 64, 1, {1, 1}, {1, 1}, {1, 1}, {0, 0}, "", false, 1597652992.}, /* GFLOPS 1.596 x 2 = 3.193 */ {{3, 3}, {{1, 64, 208, 208}}, 128, 1, {2, 2}, {1, 1}, {1, 1}, {0, 0}, "", false, 1596268544.}, - /* GFLOPS 1.596 x 2 = 3.193 */ {{3, 3}, {{1, 64, 104, 104}}, 128, 1, {1, 1}, {1, 1}, {1, 1}, {0, 0}, "", false, 1596268544.}, /* GFLOPS 1.596 x 2 = 3.191 */ {{3, 3}, {{1, 128, 104, 104}}, 256, 1, {2, 2}, {1, 1}, {1, 1}, {0, 0}, "", false, 1595576320.}, - /* GFLOPS 1.595 x 2 = 3.190 */ {{3, 3}, {{1, 256, 52, 52}}, 512, 1, {2, 2}, {1, 1}, {1, 1}, {0, 0}, "", false, 1595230208.}, /* GFLOPS 1.595 x 2 = 3.190 */ {{3, 3}, {{1, 512, 26, 26}}, 1024, 1, {2, 2}, {1, 1}, {1, 1}, {0, 0}, "", false, 1595057152.}, - /* GFLOPS 0.178 x 16 = 2.841 */ {{1, 1}, {{1, 256, 52, 52}}, 128, 1, {1, 1}, {1, 1}, {0, 0}, {0, 0}, "", false, 177555456.}, /* GFLOPS 2.719 x 1 = 2.719 */ {{3, 3}, {{1, 96, 256, 256}}, 96, 1, {2, 2}, {1, 1}, {0, 0}, {0, 0}, "SAME", false, 2719481856.}, - /* GFLOPS 0.177 x 15 = 2.659 */ {{1, 1}, {{1, 1024, 13, 13}}, 512, 1, {1, 1}, {1, 1}, {0, 0}, {0, 0}, "", false, 177295872.}, - /* GFLOPS 1.245 x 2 = 2.490 */ {{3, 3}, {{1, 96, 75, 100}}, 96, 1, {1, 1}, {1, 1}, {0, 0}, {0, 0}, "SAME", true, 1244880000.}, - /* GFLOPS 0.798 x 3 = 2.394 */ {{3, 3}, {{1, 64, 104, 104}}, 64, 1, {1, 1}, {1, 1}, {1, 1}, {0, 0}, "", false, 798134272.}, - /* GFLOPS 0.472 x 5 = 2.360 */ {{3, 3}, {{1, 256, 20, 20}}, 256, 1, {1, 1}, {1, 1}, {0, 0}, {0, 0}, "SAME", false, 471961600.}, - /* GFLOPS 2.255 x 1 = 2.255 */ {{3, 3}, {{1, 128, 80, 100}}, 128, 1, {1, 1}, {1, 1}, {0, 0}, {0, 0}, "", true, 2255285760.}, - /* GFLOPS 2.153 x 1 = 2.153 */ {{3, 3}, {{1, 128, 78, 98}}, 128, 1, {1, 1}, {1, 1}, {0, 0}, {0, 0}, "", true, 2152611840.}, - /* GFLOPS 2.100 x 1 = 2.100 */ {{3, 3}, {{1, 144, 75, 75}}, 144, 1, {1, 1}, {1, 1}, {0, 0}, {0, 0}, "SAME", false, 2100330000.}, - /* GFLOPS 2.052 x 1 = 2.052 */ {{3, 3}, {{1, 128, 76, 96}}, 128, 1, {1, 1}, {1, 1}, {0, 0}, {0, 0}, "", true, 2052298240.}, - /* GFLOPS 1.022 x 2 = 2.044 */ {{3, 3}, {{1, 576, 19, 19}}, 273, 1, {1, 1}, {1, 1}, {0, 0}, {0, 0}, "SAME", true, 1021896057.}, /* GFLOPS 1.995 x 1 = 1.995 */ {{9, 9}, {{1, 3, 320, 400}}, 32, 1, {1, 1}, {1, 1}, {4, 4}, {0, 0}, "", true, 1994752000.}, - /* GFLOPS 1.954 x 1 = 1.954 */ {{3, 3}, {{1, 128, 74, 94}}, 128, 1, {1, 1}, {1, 1}, {0, 0}, {0, 0}, "", true, 1954344960.}, - /* GFLOPS 0.958 x 2 = 1.917 */ {{3, 3}, {{1, 192, 38, 38}}, 192, 1, {1, 1}, {1, 1}, {0, 0}, {0, 0}, "SAME", false, 958446336.}, - /* GFLOPS 1.888 x 1 = 1.888 */ {{3, 3}, {{1, 1024, 10, 10}}, 1024, 1, {1, 1}, {1, 1}, {0, 0}, {0, 0}, "SAME", false, 1887539200.}, - /* GFLOPS 1.888 x 1 = 1.888 */ {{3, 3}, {{1, 1024, 10, 10}}, 1024, 1024, {1, 1}, {1, 1}, {1, 1}, {0, 0}, "", true, 1887539200.}, - /* GFLOPS 1.859 x 1 = 1.859 */ {{3, 3}, {{1, 128, 72, 92}}, 128, 1, {1, 1}, {1, 1}, {0, 0}, {0, 0}, "", true, 1858752000.}, - /* GFLOPS 1.766 x 1 = 1.766 */ {{3, 3}, {{1, 128, 70, 90}}, 128, 1, {1, 1}, {1, 1}, {0, 0}, {0, 0}, "", true, 1765519360.}, - /* GFLOPS 1.704 x 1 = 1.704 */ {{3, 3}, {{1, 256, 38, 38}}, 256, 256, {1, 1}, {1, 1}, {1, 1}, {0, 0}, "", true, 1703781376.}, - /* GFLOPS 1.704 x 1 = 1.704 */ {{3, 3}, {{1, 256, 38, 38}}, 256, 1, {1, 1}, {1, 1}, {0, 0}, {0, 0}, "SAME", false, 1703781376.}, - /* GFLOPS 1.675 x 1 = 1.675 */ {{3, 3}, {{1, 128, 68, 88}}, 128, 1, {1, 1}, {1, 1}, {0, 0}, {0, 0}, "", true, 1674647040.}, - /* GFLOPS 1.660 x 1 = 1.660 */ {{3, 3}, {{1, 128, 75, 75}}, 128, 128, {1, 1}, {1, 1}, {1, 1}, {0, 0}, "", true, 1659600000.}, - /* GFLOPS 1.660 x 1 = 1.660 */ {{3, 3}, {{1, 128, 75, 75}}, 128, 1, {1, 1}, {1, 1}, {0, 0}, {0, 0}, "SAME", false, 1659600000.}, - /* GFLOPS 1.586 x 1 = 1.586 */ {{3, 3}, {{1, 128, 66, 86}}, 128, 1, {1, 1}, {1, 1}, {0, 0}, {0, 0}, "", true, 1586135040.}, - /* GFLOPS 1.500 x 1 = 1.500 */ {{3, 3}, {{1, 128, 64, 84}}, 128, 1, {1, 1}, {1, 1}, {0, 0}, {0, 0}, "", true, 1499983360.}, - /* GFLOPS 1.416 x 1 = 1.416 */ {{3, 3}, {{1, 128, 62, 82}}, 128, 1, {1, 1}, {1, 1}, {0, 0}, {0, 0}, "", true, 1416192000.}, - /* GFLOPS 0.472 x 3 = 1.416 */ {{3, 3}, {{1, 128, 40, 40}}, 128, 1, {1, 1}, {1, 1}, {0, 0}, {0, 0}, "SAME", false, 472064000.}, - /* GFLOPS 0.472 x 3 = 1.416 */ {{3, 3}, {{1, 512, 10, 10}}, 512, 1, {1, 1}, {1, 1}, {0, 0}, {0, 0}, "SAME", false, 471910400.}, - /* GFLOPS 0.280 x 5 = 1.402 */ {{1, 1}, {{1, 576, 38, 50}}, 128, 1, {1, 1}, {1, 1}, {0, 0}, {0, 0}, "SAME", true, 280409600.}, - /* GFLOPS 0.701 x 2 = 1.401 */ {{3, 3}, {{1, 128, 38, 50}}, 160, 1, {1, 1}, {1, 1}, {0, 0}, {0, 0}, "SAME", true, 700720000.}, - /* GFLOPS 0.231 x 6 = 1.388 */ {{3, 3}, {{1, 128, 56, 56}}, 32, 1, {1, 1}, {1, 1}, {1, 1}, {0, 0}, "", false, 231311360.}, - /* GFLOPS 0.231 x 6 = 1.388 */ {{3, 3}, {{1, 256, 14, 14}}, 256, 1, {1, 1}, {1, 1}, {1, 1}, {0, 0}, "", false, 231261184.}, - /* GFLOPS 0.210 x 6 = 1.262 */ {{1, 1}, {{1, 576, 38, 50}}, 96, 1, {1, 1}, {1, 1}, {0, 0}, {0, 0}, "SAME", true, 210307200.}, - /* GFLOPS 0.420 x 3 = 1.261 */ {{3, 3}, {{1, 96, 38, 50}}, 128, 1, {1, 1}, {1, 1}, {0, 0}, {0, 0}, "SAME", true, 420492800.}, - /* GFLOPS 1.261 x 1 = 1.261 */ {{3, 3}, {{1, 192, 38, 50}}, 192, 1, {1, 1}, {1, 1}, {0, 0}, {0, 0}, "SAME", true, 1261113600.}, - /* GFLOPS 1.258 x 1 = 1.258 */ {{3, 3}, {{1, 1280, 10, 10}}, 546, 1, {1, 1}, {1, 1}, {0, 0}, {0, 0}, "SAME", true, 1258038600.}, - /* GFLOPS 1.248 x 1 = 1.248 */ {{3, 3}, {{1, 256, 46, 46}}, 128, 1, {1, 1}, {1, 1}, {1, 1}, {0, 0}, "", true, 1248338432.}, - /* GFLOPS 1.245 x 1 = 1.245 */ {{3, 3}, {{1, 64, 75, 75}}, 192, 1, {1, 1}, {1, 1}, {0, 0}, {0, 0}, "SAME", true, 1245240000.}, - /* GFLOPS 1.210 x 1 = 1.210 */ {{3, 3}, {{1, 32, 256, 256}}, 32, 1, {1, 1}, {1, 1}, {0, 0}, {0, 0}, "SAME", false, 1210056704.}, - /* GFLOPS 1.196 x 1 = 1.196 */ {{3, 3}, {{1, 384, 26, 26}}, 256, 1, {1, 1}, {1, 1}, {1, 1}, {0, 0}, "", false, 1196336128.}, + /* GFLOPS 0.945 x 2 = 1.891 */ {{3, 3}, {{1, 32, 320, 320}}, 64, 1, {2, 2}, {1, 1}, {1, 1}, {0, 0}, "", true, 945356800.}, + /* GFLOPS 0.945 x 2 = 1.889 */ {{3, 3}, {{1, 64, 160, 160}}, 128, 1, {2, 2}, {1, 1}, {1, 1}, {0, 0}, "", true, 944537600.}, + /* GFLOPS 0.944 x 2 = 1.888 */ {{3, 3}, {{1, 128, 80, 80}}, 256, 1, {2, 2}, {1, 1}, {1, 1}, {0, 0}, "", true, 944128000.}, + /* GFLOPS 0.944 x 2 = 1.888 */ {{3, 3}, {{1, 256, 40, 40}}, 512, 1, {2, 2}, {1, 1}, {1, 1}, {0, 0}, "", true, 943923200.}, /* GFLOPS 1.195 x 1 = 1.195 */ {{9, 9}, {{1, 32, 240, 320}}, 3, 1, {1, 1}, {1, 1}, {4, 4}, {0, 0}, "", true, 1194624000.}, /* GFLOPS 1.182 x 1 = 1.182 */ {{3, 3}, {{1, 32, 320, 400}}, 64, 1, {2, 2}, {1, 1}, {1, 1}, {0, 0}, "", true, 1181696000.}, /* GFLOPS 1.181 x 1 = 1.181 */ {{3, 3}, {{1, 64, 160, 200}}, 128, 1, {2, 2}, {1, 1}, {1, 1}, {0, 0}, "", true, 1180672000.}, - /* GFLOPS 0.561 x 2 = 1.121 */ {{3, 3}, {{1, 128, 38, 50}}, 128, 1, {1, 1}, {1, 1}, {0, 0}, {0, 0}, "SAME", true, 560576000.}, - /* GFLOPS 1.112 x 1 = 1.112 */ {{3, 3}, {{1, 512, 10, 10}}, 1206, 1, {1, 1}, {1, 1}, {1, 1}, {0, 0}, "", true, 1111570200.}, - /* GFLOPS 0.357 x 3 = 1.072 */ {{1, 1}, {{1, 64, 208, 208}}, 64, 1, {1, 1}, {1, 1}, {0, 0}, {0, 0}, "", false, 357187584.}, /* GFLOPS 1.062 x 1 = 1.062 */ {{3, 3}, {{1, 240, 64, 64}}, 240, 1, {2, 2}, {1, 1}, {0, 0}, {0, 0}, "SAME", false, 1061928960.}, - /* GFLOPS 0.076 x 14 = 1.058 */ {{3, 3}, {{1, 64, 32, 32}}, 64, 1, {1, 1}, {1, 1}, {0, 0}, {0, 0}, "SAME", false, 75563008.}, - /* GFLOPS 1.051 x 1 = 1.051 */ {{3, 3}, {{1, 160, 38, 50}}, 192, 1, {1, 1}, {1, 1}, {0, 0}, {0, 0}, "SAME", true, 1050988800.}, - /* GFLOPS 0.210 x 5 = 1.051 */ {{1, 1}, {{1, 256, 20, 20}}, 1024, 1, {1, 1}, {1, 1}, {0, 0}, {0, 0}, "SAME", false, 210124800.}, - /* GFLOPS 0.210 x 5 = 1.049 */ {{1, 1}, {{1, 1024, 20, 20}}, 256, 1, {1, 1}, {1, 1}, {0, 0}, {0, 0}, "SAME", false, 209817600.}, - /* GFLOPS 1.006 x 1 = 1.006 */ {{3, 3}, {{1, 1024, 10, 10}}, 546, 1, {1, 1}, {1, 1}, {0, 0}, {0, 0}, "SAME", true, 1006441800.}, - /* GFLOPS 0.246 x 4 = 0.985 */ {{1, 1}, {{1, 256, 75, 100}}, 64, 1, {1, 1}, {1, 1}, {0, 0}, {0, 0}, "SAME", true, 246240000.}, - /* GFLOPS 0.189 x 5 = 0.947 */ {{1, 1}, {{1, 512, 19, 19}}, 512, 1, {1, 1}, {1, 1}, {0, 0}, {0, 0}, "", true, 189452800.}, - /* GFLOPS 0.189 x 5 = 0.947 */ {{1, 1}, {{1, 512, 19, 19}}, 512, 1, {1, 1}, {1, 1}, {0, 0}, {0, 0}, "SAME", false, 189452800.}, - /* GFLOPS 0.472 x 2 = 0.945 */ {{3, 3}, {{1, 64, 80, 80}}, 64, 1, {1, 1}, {1, 1}, {0, 0}, {0, 0}, "SAME", false, 472268800.}, - /* GFLOPS 0.934 x 1 = 0.934 */ {{3, 3}, {{1, 96, 150, 150}}, 96, 1, {2, 2}, {1, 1}, {0, 0}, {0, 0}, "SAME", false, 933660000.}, - /* GFLOPS 0.231 x 4 = 0.925 */ {{3, 3}, {{1, 128, 28, 28}}, 128, 1, {1, 1}, {1, 1}, {1, 1}, {0, 0}, "", false, 231311360.}, + /* GFLOPS 0.237 x 4 = 0.947 */ {{3, 3}, {{1, 16, 320, 320}}, 32, 1, {2, 2}, {1, 1}, {1, 1}, {0, 0}, "", true, 236748800.}, + /* GFLOPS 0.236 x 4 = 0.945 */ {{3, 3}, {{1, 32, 160, 160}}, 64, 1, {2, 2}, {1, 1}, {1, 1}, {0, 0}, "", true, 236339200.}, + /* GFLOPS 0.236 x 4 = 0.945 */ {{3, 3}, {{1, 64, 80, 80}}, 128, 1, {2, 2}, {1, 1}, {1, 1}, {0, 0}, "", true, 236134400.}, /* GFLOPS 0.896 x 1 = 0.896 */ {{5, 5}, {{1, 96, 27, 27}}, 256, 2, {1, 1}, {1, 1}, {2, 2}, {0, 0}, "", true, 895981824.}, - /* GFLOPS 0.089 x 10 = 0.890 */ {{1, 1}, {{1, 128, 52, 52}}, 128, 1, {1, 1}, {1, 1}, {0, 0}, {0, 0}, "", false, 88950784.}, - /* GFLOPS 0.089 x 10 = 0.888 */ {{1, 1}, {{1, 256, 26, 26}}, 256, 1, {1, 1}, {1, 1}, {0, 0}, {0, 0}, "", false, 88777728.}, - /* GFLOPS 0.876 x 1 = 0.876 */ {{3, 3}, {{1, 160, 38, 50}}, 160, 1, {1, 1}, {1, 1}, {0, 0}, {0, 0}, "SAME", true, 875824000.}, /* GFLOPS 0.850 x 1 = 0.850 */ {{7, 7}, {{1, 3, 600, 800}}, 24, 1, {2, 2}, {1, 1}, {0, 0}, {0, 0}, "SAME", false, 849600000.}, - /* GFLOPS 0.841 x 1 = 0.841 */ {{3, 3}, {{1, 128, 38, 50}}, 192, 1, {1, 1}, {1, 1}, {0, 0}, {0, 0}, "SAME", true, 840864000.}, - /* GFLOPS 0.415 x 2 = 0.831 */ {{3, 3}, {{1, 32, 150, 150}}, 32, 1, {1, 1}, {1, 1}, {0, 0}, {0, 0}, "SAME", false, 415440000.}, - /* GFLOPS 0.757 x 1 = 0.757 */ {{1, 1}, {{1, 1024, 19, 19}}, 1024, 1, {1, 1}, {1, 1}, {0, 0}, {0, 0}, "", true, 757441536.}, - /* GFLOPS 0.712 x 1 = 0.712 */ {{1, 1}, {{1, 128, 208, 208}}, 64, 1, {1, 1}, {1, 1}, {0, 0}, {0, 0}, "", false, 711606272.}, - /* GFLOPS 0.178 x 4 = 0.712 */ {{1, 1}, {{1, 128, 104, 104}}, 64, 1, {1, 1}, {1, 1}, {0, 0}, {0, 0}, "", false, 177901568.}, - /* GFLOPS 0.354 x 2 = 0.707 */ {{1, 1}, {{1, 256, 52, 52}}, 255, 1, {1, 1}, {1, 1}, {0, 0}, {0, 0}, "", true, 353723760.}, - /* GFLOPS 0.351 x 2 = 0.701 */ {{1, 1}, {{1, 576, 38, 50}}, 160, 1, {1, 1}, {1, 1}, {0, 0}, {0, 0}, "SAME", true, 350512000.}, + /* GFLOPS 0.356 x 2 = 0.711 */ {{6, 6}, {{1, 3, 640, 640}}, 16, 1, {2, 2}, {1, 1}, {2, 2}, {0, 0}, "", true, 355532800.}, /* GFLOPS 0.701 x 1 = 0.701 */ {{3, 3}, {{1, 128, 75, 100}}, 160, 1, {2, 2}, {1, 1}, {0, 0}, {0, 0}, "SAME", true, 700720000.}, - /* GFLOPS 0.694 x 1 = 0.694 */ {{3, 3}, {{1, 64, 56, 56}}, 192, 1, {1, 1}, {1, 1}, {1, 1}, {0, 0}, "", true, 694235136.}, - /* GFLOPS 0.694 x 1 = 0.694 */ {{3, 3}, {{1, 64, 56, 56}}, 192, 1, {1, 1}, {1, 1}, {0, 0}, {0, 0}, "SAME", true, 694235136.}, - /* GFLOPS 0.231 x 3 = 0.694 */ {{3, 3}, {{1, 64, 56, 56}}, 64, 1, {1, 1}, {1, 1}, {1, 1}, {0, 0}, "", false, 231411712.}, - /* GFLOPS 0.058 x 12 = 0.694 */ {{3, 3}, {{1, 128, 28, 28}}, 32, 1, {1, 1}, {1, 1}, {1, 1}, {0, 0}, "", false, 57827840.}, - /* GFLOPS 0.231 x 3 = 0.694 */ {{3, 3}, {{1, 512, 7, 7}}, 512, 1, {1, 1}, {1, 1}, {1, 1}, {0, 0}, "", false, 231236096.}, - /* GFLOPS 0.160 x 4 = 0.639 */ {{3, 3}, {{1, 64, 38, 38}}, 96, 1, {1, 1}, {1, 1}, {0, 0}, {0, 0}, "SAME", true, 159833472.}, - /* GFLOPS 0.211 x 3 = 0.634 */ {{1, 1}, {{1, 64, 80, 80}}, 256, 1, {1, 1}, {1, 1}, {0, 0}, {0, 0}, "SAME", false, 211353600.}, - /* GFLOPS 0.211 x 3 = 0.632 */ {{1, 1}, {{1, 128, 40, 40}}, 512, 1, {1, 1}, {1, 1}, {0, 0}, {0, 0}, "SAME", false, 210534400.}, - /* GFLOPS 0.210 x 3 = 0.630 */ {{1, 1}, {{1, 512, 40, 40}}, 128, 1, {1, 1}, {1, 1}, {0, 0}, {0, 0}, "SAME", false, 209920000.}, - /* GFLOPS 0.210 x 3 = 0.630 */ {{1, 1}, {{1, 512, 10, 10}}, 2048, 1, {1, 1}, {1, 1}, {0, 0}, {0, 0}, "SAME", false, 209920000.}, - /* GFLOPS 0.103 x 6 = 0.618 */ {{1, 1}, {{1, 256, 14, 14}}, 1024, 1, {1, 1}, {1, 1}, {0, 0}, {0, 0}, "", false, 102961152.}, - /* GFLOPS 0.615 x 1 = 0.615 */ {{1, 1}, {{1, 320, 75, 100}}, 128, 1, {1, 1}, {1, 1}, {0, 0}, {0, 0}, "SAME", true, 615360000.}, - /* GFLOPS 0.305 x 2 = 0.609 */ {{3, 3}, {{1, 3, 416, 416}}, 32, 1, {1, 1}, {1, 1}, {1, 1}, {0, 0}, "", false, 304578560.}, - /* GFLOPS 0.597 x 1 = 0.597 */ {{3, 3}, {{1, 576, 19, 19}}, 576, 1, {2, 2}, {1, 1}, {0, 0}, {0, 0}, "SAME", false, 597254400.}, - /* GFLOPS 0.278 x 2 = 0.557 */ {{1, 1}, {{1, 128, 46, 46}}, 512, 1, {1, 1}, {1, 1}, {0, 0}, {0, 0}, "", true, 278431744.}, - /* GFLOPS 0.185 x 3 = 0.554 */ {{1, 1}, {{1, 192, 75, 100}}, 64, 1, {1, 1}, {1, 1}, {0, 0}, {0, 0}, "SAME", true, 184800000.}, - /* GFLOPS 0.553 x 1 = 0.553 */ {{3, 3}, {{1, 64, 75, 100}}, 64, 1, {1, 1}, {1, 1}, {0, 0}, {0, 0}, "SAME", true, 553440000.}, - /* GFLOPS 0.539 x 1 = 0.539 */ {{3, 3}, {{1, 144, 75, 75}}, 144, 1, {2, 2}, {1, 1}, {0, 0}, {0, 0}, "SAME", false, 539178048.}, - /* GFLOPS 0.103 x 5 = 0.514 */ {{1, 1}, {{1, 1024, 14, 14}}, 256, 1, {1, 1}, {1, 1}, {0, 0}, {0, 0}, "", false, 102810624.}, - /* GFLOPS 0.491 x 1 = 0.491 */ {{1, 1}, {{1, 576, 38, 50}}, 224, 1, {1, 1}, {1, 1}, {0, 0}, {0, 0}, "SAME", true, 490716800.}, /* GFLOPS 0.483 x 1 = 0.483 */ {{7, 7}, {{1, 3, 320, 320}}, 64, 1, {2, 2}, {1, 1}, {3, 3}, {0, 0}, "", false, 483328000.}, - /* GFLOPS 0.240 x 2 = 0.479 */ {{3, 3}, {{1, 96, 38, 38}}, 96, 1, {1, 1}, {1, 1}, {0, 0}, {0, 0}, "SAME", true, 239680896.}, - /* GFLOPS 0.477 x 1 = 0.477 */ {{3, 3}, {{1, 3, 368, 368}}, 64, 1, {1, 1}, {1, 1}, {1, 1}, {0, 0}, "", true, 476692480.}, - /* GFLOPS 0.237 x 2 = 0.474 */ {{7, 7}, {{1, 3, 224, 224}}, 64, 1, {2, 2}, {1, 1}, {3, 3}, {0, 0}, "", true, 236830720.}, - /* GFLOPS 0.472 x 1 = 0.472 */ {{3, 3}, {{1, 512, 19, 19}}, 512, 512, {2, 2}, {1, 1}, {1, 1}, {0, 0}, "", true, 471910400.}, /* GFLOPS 0.472 x 1 = 0.472 */ {{3, 3}, {{1, 512, 19, 19}}, 512, 1, {2, 2}, {1, 1}, {0, 0}, {0, 0}, "SAME", false, 471910400.}, - /* GFLOPS 0.155 x 3 = 0.464 */ {{1, 1}, {{1, 112, 32, 32}}, 672, 1, {1, 1}, {1, 1}, {0, 0}, {0, 0}, "SAME", false, 154828800.}, - /* GFLOPS 0.114 x 4 = 0.454 */ {{1, 1}, {{1, 192, 16, 16}}, 1152, 1, {1, 1}, {1, 1}, {0, 0}, {0, 0}, "SAME", false, 113541120.}, - /* GFLOPS 0.449 x 1 = 0.449 */ {{3, 3}, {{1, 384, 13, 13}}, 384, 2, {1, 1}, {1, 1}, {1, 1}, {0, 0}, "", true, 448626048.}, - /* GFLOPS 0.089 x 5 = 0.443 */ {{1, 1}, {{1, 512, 13, 13}}, 512, 1, {1, 1}, {1, 1}, {0, 0}, {0, 0}, "", false, 88691200.}, - /* GFLOPS 0.428 x 1 = 0.428 */ {{1, 1}, {{1, 64, 64, 64}}, 810, 1, {1, 1}, {1, 1}, {0, 0}, {0, 0}, "VALID", true, 427991040.}, - /* GFLOPS 0.426 x 1 = 0.426 */ {{3, 3}, {{1, 128, 75, 75}}, 128, 128, {2, 2}, {1, 1}, {1, 1}, {0, 0}, "", true, 426037760.}, /* GFLOPS 0.426 x 1 = 0.426 */ {{3, 3}, {{1, 128, 75, 75}}, 128, 1, {2, 2}, {1, 1}, {0, 0}, {0, 0}, "SAME", false, 426037760.}, - /* GFLOPS 0.426 x 1 = 0.426 */ {{3, 3}, {{1, 128, 38, 38}}, 128, 1, {1, 1}, {1, 1}, {1, 1}, {0, 0}, "", false, 426037760.}, - /* GFLOPS 0.426 x 1 = 0.426 */ {{3, 3}, {{1, 256, 38, 38}}, 256, 256, {2, 2}, {1, 1}, {1, 1}, {0, 0}, "", true, 425945344.}, /* GFLOPS 0.426 x 1 = 0.426 */ {{3, 3}, {{1, 256, 38, 38}}, 256, 1, {2, 2}, {1, 1}, {0, 0}, {0, 0}, "SAME", false, 425945344.}, - /* GFLOPS 0.426 x 1 = 0.426 */ {{3, 3}, {{1, 256, 19, 19}}, 256, 1, {1, 1}, {1, 1}, {1, 1}, {0, 0}, "", false, 425945344.}, - /* GFLOPS 0.421 x 1 = 0.421 */ {{1, 1}, {{1, 576, 38, 50}}, 192, 1, {1, 1}, {1, 1}, {0, 0}, {0, 0}, "SAME", true, 420614400.}, - /* GFLOPS 0.420 x 1 = 0.420 */ {{1, 1}, {{1, 256, 40, 40}}, 512, 1, {1, 1}, {1, 1}, {0, 0}, {0, 0}, "SAME", false, 420249600.}, - /* GFLOPS 0.210 x 2 = 0.420 */ {{1, 1}, {{1, 256, 80, 80}}, 64, 1, {1, 1}, {1, 1}, {0, 0}, {0, 0}, "SAME", false, 210124800.}, - /* GFLOPS 0.420 x 1 = 0.420 */ {{1, 1}, {{1, 512, 20, 20}}, 1024, 1, {1, 1}, {1, 1}, {0, 0}, {0, 0}, "SAME", false, 419840000.}, - /* GFLOPS 0.420 x 1 = 0.420 */ {{1, 1}, {{1, 1024, 10, 10}}, 2048, 1, {1, 1}, {1, 1}, {0, 0}, {0, 0}, "SAME", false, 419635200.}, - /* GFLOPS 0.210 x 2 = 0.420 */ {{1, 1}, {{1, 2048, 10, 10}}, 512, 1, {1, 1}, {1, 1}, {0, 0}, {0, 0}, "SAME", false, 209766400.}, - /* GFLOPS 0.415 x 1 = 0.415 */ {{3, 3}, {{1, 32, 150, 150}}, 32, 32, {1, 1}, {1, 1}, {1, 1}, {0, 0}, "", true, 415440000.}, - /* GFLOPS 0.415 x 1 = 0.415 */ {{3, 3}, {{1, 64, 150, 150}}, 64, 64, {2, 2}, {1, 1}, {1, 1}, {0, 0}, "", true, 415080000.}, /* GFLOPS 0.415 x 1 = 0.415 */ {{3, 3}, {{1, 64, 150, 150}}, 64, 1, {2, 2}, {1, 1}, {0, 0}, {0, 0}, "SAME", false, 415080000.}, - /* GFLOPS 0.104 x 4 = 0.414 */ {{1, 1}, {{1, 64, 56, 56}}, 256, 1, {1, 1}, {1, 1}, {0, 0}, {0, 0}, "", false, 103563264.}, - /* GFLOPS 0.103 x 4 = 0.413 */ {{1, 1}, {{1, 128, 28, 28}}, 512, 1, {1, 1}, {1, 1}, {0, 0}, {0, 0}, "", false, 103161856.}, /* GFLOPS 0.399 x 1 = 0.399 */ {{3, 3}, {{1, 32, 208, 208}}, 64, 1, {2, 2}, {1, 1}, {1, 1}, {0, 0}, "", false, 399413248.}, - /* GFLOPS 0.200 x 2 = 0.399 */ {{3, 3}, {{1, 32, 104, 104}}, 32, 1, {1, 1}, {1, 1}, {1, 1}, {0, 0}, "", false, 199706624.}, - /* GFLOPS 0.200 x 2 = 0.399 */ {{3, 3}, {{1, 64, 52, 52}}, 64, 1, {1, 1}, {1, 1}, {1, 1}, {0, 0}, "", false, 199533568.}, - /* GFLOPS 0.399 x 1 = 0.399 */ {{3, 3}, {{1, 128, 52, 52}}, 256, 1, {2, 2}, {1, 1}, {1, 1}, {0, 0}, "", false, 398894080.}, - /* GFLOPS 0.199 x 2 = 0.399 */ {{3, 3}, {{1, 128, 26, 26}}, 128, 1, {1, 1}, {1, 1}, {1, 1}, {0, 0}, "", false, 199447040.}, - /* GFLOPS 0.399 x 1 = 0.399 */ {{3, 3}, {{1, 256, 26, 26}}, 512, 1, {2, 2}, {1, 1}, {1, 1}, {0, 0}, "", false, 398807552.}, - /* GFLOPS 0.399 x 1 = 0.399 */ {{3, 3}, {{1, 256, 13, 13}}, 512, 1, {1, 1}, {1, 1}, {1, 1}, {0, 0}, "", false, 398807552.}, - /* GFLOPS 0.376 x 1 = 0.376 */ {{1, 1}, {{1, 24, 300, 400}}, 64, 1, {1, 1}, {1, 1}, {0, 0}, {0, 0}, "VALID", true, 376320000.}, - /* GFLOPS 0.179 x 2 = 0.357 */ {{1, 1}, {{1, 64, 208, 208}}, 32, 1, {1, 1}, {1, 1}, {0, 0}, {0, 0}, "", false, 178593792.}, - /* GFLOPS 0.089 x 4 = 0.357 */ {{1, 1}, {{1, 64, 104, 104}}, 64, 1, {1, 1}, {1, 1}, {0, 0}, {0, 0}, "", false, 89296896.}, - /* GFLOPS 0.356 x 1 = 0.356 */ {{1, 1}, {{1, 128, 104, 104}}, 128, 1, {1, 1}, {1, 1}, {0, 0}, {0, 0}, "", false, 355803136.}, - /* GFLOPS 0.355 x 1 = 0.355 */ {{1, 1}, {{1, 256, 52, 52}}, 256, 1, {1, 1}, {1, 1}, {0, 0}, {0, 0}, "", false, 355110912.}, - /* GFLOPS 0.355 x 1 = 0.355 */ {{1, 1}, {{1, 512, 26, 26}}, 512, 1, {1, 1}, {1, 1}, {0, 0}, {0, 0}, "", false, 354764800.}, - /* GFLOPS 0.355 x 1 = 0.355 */ {{1, 1}, {{1, 1024, 13, 13}}, 1024, 1, {1, 1}, {1, 1}, {0, 0}, {0, 0}, "", false, 354591744.}, - /* GFLOPS 0.355 x 1 = 0.355 */ {{1, 1}, {{1, 2048, 13, 13}}, 512, 1, {1, 1}, {1, 1}, {0, 0}, {0, 0}, "", false, 354505216.}, - /* GFLOPS 0.177 x 2 = 0.353 */ {{1, 1}, {{1, 512, 26, 26}}, 255, 1, {1, 1}, {1, 1}, {0, 0}, {0, 0}, "", true, 176689500.}, - /* GFLOPS 0.070 x 5 = 0.348 */ {{1, 1}, {{1, 128, 46, 46}}, 128, 1, {1, 1}, {1, 1}, {0, 0}, {0, 0}, "", true, 69607936.}, - /* GFLOPS 0.347 x 1 = 0.347 */ {{3, 3}, {{1, 128, 28, 28}}, 192, 1, {1, 1}, {1, 1}, {1, 1}, {0, 0}, "", true, 346967040.}, - /* GFLOPS 0.347 x 1 = 0.347 */ {{3, 3}, {{1, 128, 28, 28}}, 192, 1, {1, 1}, {1, 1}, {0, 0}, {0, 0}, "SAME", true, 346967040.}, - /* GFLOPS 0.014 x 24 = 0.347 */ {{3, 3}, {{1, 128, 14, 14}}, 32, 1, {1, 1}, {1, 1}, {1, 1}, {0, 0}, "", false, 14456960.}, - /* GFLOPS 0.113 x 3 = 0.340 */ {{1, 1}, {{1, 1152, 16, 16}}, 192, 1, {1, 1}, {1, 1}, {0, 0}, {0, 0}, "SAME", false, 113295360.}, - /* GFLOPS 0.053 x 6 = 0.320 */ {{1, 1}, {{1, 576, 19, 19}}, 128, 1, {1, 1}, {1, 1}, {0, 0}, {0, 0}, "SAME", true, 53277824.}, - /* GFLOPS 0.319 x 1 = 0.319 */ {{3, 3}, {{1, 192, 19, 19}}, 256, 1, {1, 1}, {1, 1}, {0, 0}, {0, 0}, "SAME", true, 319482112.}, - /* GFLOPS 0.317 x 1 = 0.317 */ {{3, 3}, {{1, 3, 300, 300}}, 64, 1, {1, 1}, {1, 1}, {1, 1}, {0, 0}, "", true, 316800000.}, + /* GFLOPS 0.090 x 4 = 0.360 */ {{3, 3}, {{1, 3, 640, 640}}, 16, 1, {2, 2}, {1, 1}, {1, 1}, {0, 0}, "", true, 90112000.}, + /* GFLOPS 0.170 x 2 = 0.340 */ {{3, 3}, {{1, 64, 96, 96}}, 64, 1, {2, 2}, {1, 1}, {0, 0}, {0, 0}, "SAME", true, 170016768.}, /* GFLOPS 0.315 x 1 = 0.315 */ {{3, 3}, {{1, 96, 75, 100}}, 96, 1, {2, 2}, {1, 1}, {0, 0}, {0, 0}, "SAME", true, 315369600.}, - /* GFLOPS 0.103 x 3 = 0.309 */ {{1, 1}, {{1, 512, 7, 7}}, 2048, 1, {1, 1}, {1, 1}, {0, 0}, {0, 0}, "", false, 102860800.}, - /* GFLOPS 0.103 x 3 = 0.309 */ {{1, 1}, {{1, 512, 28, 28}}, 128, 1, {1, 1}, {1, 1}, {0, 0}, {0, 0}, "", false, 102860800.}, - /* GFLOPS 0.154 x 2 = 0.309 */ {{1, 1}, {{1, 672, 32, 32}}, 112, 1, {1, 1}, {1, 1}, {0, 0}, {0, 0}, "SAME", false, 154255360.}, - /* GFLOPS 0.308 x 1 = 0.308 */ {{1, 1}, {{1, 320, 75, 100}}, 64, 1, {1, 1}, {1, 1}, {0, 0}, {0, 0}, "SAME", true, 307680000.}, - /* GFLOPS 0.034 x 9 = 0.304 */ {{1, 1}, {{1, 64, 64, 64}}, 64, 1, {1, 1}, {1, 1}, {0, 0}, {0, 0}, "VALID", true, 33816576.}, - /* GFLOPS 0.299 x 1 = 0.299 */ {{3, 3}, {{1, 256, 13, 13}}, 384, 1, {1, 1}, {1, 1}, {1, 1}, {0, 0}, "", true, 299105664.}, - /* GFLOPS 0.299 x 1 = 0.299 */ {{3, 3}, {{1, 384, 13, 13}}, 256, 2, {1, 1}, {1, 1}, {1, 1}, {0, 0}, "", true, 299084032.}, - /* GFLOPS 0.017 x 17 = 0.290 */ {{1, 1}, {{1, 32, 32, 64}}, 128, 1, {1, 1}, {1, 1}, {0, 0}, {0, 0}, "", false, 17039360.}, - /* GFLOPS 0.017 x 16 = 0.269 */ {{1, 1}, {{1, 128, 32, 64}}, 32, 1, {1, 1}, {1, 1}, {0, 0}, {0, 0}, "", false, 16842752.}, - /* GFLOPS 0.133 x 2 = 0.266 */ {{3, 3}, {{1, 128, 19, 19}}, 160, 1, {1, 1}, {1, 1}, {0, 0}, {0, 0}, "SAME", true, 133136800.}, - /* GFLOPS 0.266 x 1 = 0.266 */ {{1, 1}, {{1, 384, 52, 52}}, 128, 1, {1, 1}, {1, 1}, {0, 0}, {0, 0}, "", false, 266160128.}, - /* GFLOPS 0.266 x 1 = 0.266 */ {{1, 1}, {{1, 768, 26, 26}}, 256, 1, {1, 1}, {1, 1}, {0, 0}, {0, 0}, "", false, 265987072.}, - /* GFLOPS 0.038 x 7 = 0.265 */ {{3, 3}, {{1, 16, 64, 128}}, 16, 1, {1, 1}, {1, 1}, {1, 1}, {0, 0}, "", true, 37879808.}, - /* GFLOPS 0.019 x 14 = 0.264 */ {{3, 3}, {{1, 64, 16, 16}}, 64, 1, {1, 1}, {1, 1}, {0, 0}, {0, 0}, "SAME", false, 18890752.}, - /* GFLOPS 0.262 x 1 = 0.262 */ {{1, 1}, {{1, 2560, 20, 20}}, 128, 1, {1, 1}, {1, 1}, {0, 0}, {0, 0}, "SAME", false, 262195200.}, - /* GFLOPS 0.126 x 2 = 0.252 */ {{3, 3}, {{1, 512, 5, 5}}, 546, 1, {1, 1}, {1, 1}, {0, 0}, {0, 0}, "SAME", true, 125812050.}, - /* GFLOPS 0.248 x 1 = 0.248 */ {{1, 1}, {{1, 64, 150, 200}}, 64, 1, {1, 1}, {1, 1}, {0, 0}, {0, 0}, "SAME", true, 247680000.}, - /* GFLOPS 0.040 x 6 = 0.240 */ {{1, 1}, {{1, 576, 19, 19}}, 96, 1, {1, 1}, {1, 1}, {0, 0}, {0, 0}, "SAME", true, 39958368.}, - /* GFLOPS 0.080 x 3 = 0.240 */ {{3, 3}, {{1, 96, 19, 19}}, 128, 1, {1, 1}, {1, 1}, {0, 0}, {0, 0}, "SAME", true, 79893632.}, /* GFLOPS 0.240 x 1 = 0.240 */ {{3, 3}, {{1, 192, 38, 38}}, 192, 1, {2, 2}, {1, 1}, {0, 0}, {0, 0}, "SAME", false, 239611584.}, - /* GFLOPS 0.240 x 1 = 0.240 */ {{3, 3}, {{1, 192, 19, 19}}, 192, 1, {1, 1}, {1, 1}, {0, 0}, {0, 0}, "SAME", true, 239611584.}, - /* GFLOPS 0.079 x 3 = 0.237 */ {{1, 1}, {{1, 80, 32, 32}}, 480, 1, {1, 1}, {1, 1}, {0, 0}, {0, 0}, "SAME", false, 79134720.}, /* GFLOPS 0.237 x 1 = 0.237 */ {{7, 7}, {{1, 3, 224, 224}}, 64, 1, {2, 2}, {1, 1}, {3, 3}, {0, 0}, "", false, 236830720.}, - /* GFLOPS 0.237 x 1 = 0.237 */ {{7, 7}, {{1, 3, 224, 224}}, 64, 1, {2, 2}, {1, 1}, {0, 0}, {0, 0}, "SAME", true, 236830720.}, - /* GFLOPS 0.118 x 2 = 0.236 */ {{3, 3}, {{1, 32, 80, 80}}, 32, 1, {1, 1}, {1, 1}, {0, 0}, {0, 0}, "SAME", false, 118169600.}, - /* GFLOPS 0.236 x 1 = 0.236 */ {{3, 3}, {{1, 256, 19, 19}}, 512, 1, {2, 2}, {1, 1}, {1, 1}, {0, 0}, "", true, 235980800.}, - /* GFLOPS 0.116 x 2 = 0.231 */ {{1, 1}, {{1, 24, 128, 128}}, 144, 1, {1, 1}, {1, 1}, {0, 0}, {0, 0}, "SAME", false, 115605504.}, - /* GFLOPS 0.111 x 2 = 0.221 */ {{3, 3}, {{1, 192, 10, 10}}, 320, 1, {1, 1}, {1, 1}, {0, 0}, {0, 0}, "SAME", true, 110624000.}, /* GFLOPS 0.213 x 1 = 0.213 */ {{3, 3}, {{1, 128, 38, 38}}, 256, 1, {2, 2}, {1, 1}, {1, 1}, {0, 0}, "", false, 213018880.}, /* GFLOPS 0.213 x 1 = 0.213 */ {{3, 3}, {{1, 128, 19, 19}}, 256, 1, {1, 1}, {2, 2}, {2, 2}, {0, 0}, "", false, 213018880.}, - /* GFLOPS 0.107 x 2 = 0.213 */ {{3, 3}, {{1, 128, 19, 19}}, 128, 1, {1, 1}, {1, 1}, {0, 0}, {0, 0}, "SAME", true, 106509440.}, - /* GFLOPS 0.213 x 1 = 0.213 */ {{3, 3}, {{1, 256, 19, 19}}, 128, 1, {1, 1}, {1, 1}, {1, 1}, {0, 0}, "", false, 212972672.}, - /* GFLOPS 0.213 x 1 = 0.213 */ {{3, 3}, {{1, 512, 38, 38}}, 16, 1, {1, 1}, {1, 1}, {1, 1}, {0, 0}, "", true, 212949568.}, /* GFLOPS 0.212 x 1 = 0.212 */ {{7, 7}, {{1, 3, 300, 300}}, 32, 1, {2, 2}, {1, 1}, {3, 3}, {0, 0}, "", true, 212400000.}, /* GFLOPS 0.211 x 1 = 0.211 */ {{11, 11}, {{1, 3, 227, 227}}, 96, 1, {4, 4}, {1, 1}, {0, 0}, {0, 0}, "", true, 211120800.}, - /* GFLOPS 0.210 x 1 = 0.210 */ {{3, 3}, {{1, 64, 38, 50}}, 96, 1, {1, 1}, {1, 1}, {0, 0}, {0, 0}, "SAME", true, 210307200.}, - /* GFLOPS 0.210 x 1 = 0.210 */ {{1, 1}, {{1, 1024, 10, 10}}, 1024, 1, {1, 1}, {1, 1}, {0, 0}, {0, 0}, "", true, 209817600.}, - /* GFLOPS 0.210 x 1 = 0.210 */ {{1, 1}, {{1, 1024, 10, 10}}, 1024, 1, {1, 1}, {1, 1}, {0, 0}, {0, 0}, "SAME", false, 209817600.}, - /* GFLOPS 0.104 x 2 = 0.208 */ {{3, 3}, {{1, 32, 75, 75}}, 32, 1, {1, 1}, {1, 1}, {1, 1}, {0, 0}, "", false, 103860000.}, - /* GFLOPS 0.208 x 1 = 0.208 */ {{1, 1}, {{1, 16, 256, 256}}, 96, 1, {1, 1}, {1, 1}, {0, 0}, {0, 0}, "SAME", false, 207618048.}, - /* GFLOPS 0.206 x 1 = 0.206 */ {{1, 1}, {{1, 256, 56, 56}}, 512, 1, {2, 2}, {1, 1}, {0, 0}, {0, 0}, "", false, 205922304.}, - /* GFLOPS 0.206 x 1 = 0.206 */ {{1, 1}, {{1, 256, 56, 56}}, 128, 1, {1, 1}, {1, 1}, {0, 0}, {0, 0}, "", false, 205922304.}, - /* GFLOPS 0.103 x 2 = 0.206 */ {{1, 1}, {{1, 256, 56, 56}}, 64, 1, {1, 1}, {1, 1}, {0, 0}, {0, 0}, "", false, 102961152.}, - /* GFLOPS 0.206 x 1 = 0.206 */ {{1, 1}, {{1, 512, 28, 28}}, 256, 1, {1, 1}, {1, 1}, {0, 0}, {0, 0}, "", false, 205721600.}, - /* GFLOPS 0.206 x 1 = 0.206 */ {{1, 1}, {{1, 512, 28, 28}}, 1024, 1, {2, 2}, {1, 1}, {0, 0}, {0, 0}, "", false, 205721600.}, - /* GFLOPS 0.206 x 1 = 0.206 */ {{1, 1}, {{1, 1024, 14, 14}}, 512, 1, {1, 1}, {1, 1}, {0, 0}, {0, 0}, "", false, 205621248.}, - /* GFLOPS 0.206 x 1 = 0.206 */ {{1, 1}, {{1, 1024, 14, 14}}, 2048, 1, {2, 2}, {1, 1}, {0, 0}, {0, 0}, "", false, 205621248.}, - /* GFLOPS 0.103 x 2 = 0.206 */ {{1, 1}, {{1, 2048, 7, 7}}, 512, 1, {1, 1}, {1, 1}, {0, 0}, {0, 0}, "", false, 102785536.}, - /* GFLOPS 0.201 x 1 = 0.201 */ {{1, 1}, {{1, 512, 14, 14}}, 1000, 1, {1, 1}, {1, 1}, {0, 0}, {0, 0}, "", true, 200900000.}, - /* GFLOPS 0.200 x 1 = 0.200 */ {{3, 3}, {{1, 160, 19, 19}}, 192, 1, {1, 1}, {1, 1}, {0, 0}, {0, 0}, "SAME", true, 199687872.}, - /* GFLOPS 0.190 x 1 = 0.190 */ {{1, 1}, {{1, 256, 38, 38}}, 256, 1, {1, 1}, {1, 1}, {0, 0}, {0, 0}, "", true, 189637632.}, - /* GFLOPS 0.190 x 1 = 0.190 */ {{1, 1}, {{1, 256, 38, 38}}, 256, 1, {1, 1}, {1, 1}, {0, 0}, {0, 0}, "SAME", false, 189637632.}, - /* GFLOPS 0.047 x 4 = 0.190 */ {{1, 1}, {{1, 256, 38, 38}}, 64, 1, {1, 1}, {1, 1}, {0, 0}, {0, 0}, "SAME", true, 47409408.}, - /* GFLOPS 0.189 x 1 = 0.189 */ {{1, 1}, {{1, 1024, 19, 19}}, 256, 1, {1, 1}, {1, 1}, {0, 0}, {0, 0}, "", true, 189360384.}, - /* GFLOPS 0.038 x 5 = 0.189 */ {{3, 3}, {{1, 32, 32, 64}}, 32, 1, {1, 1}, {1, 1}, {1, 1}, {0, 0}, "", true, 37814272.}, - /* GFLOPS 0.189 x 1 = 0.189 */ {{1, 1}, {{1, 1152, 16, 16}}, 320, 1, {1, 1}, {1, 1}, {0, 0}, {0, 0}, "SAME", false, 188825600.}, - /* GFLOPS 0.185 x 1 = 0.185 */ {{1, 1}, {{1, 128, 75, 75}}, 128, 1, {1, 1}, {1, 1}, {0, 0}, {0, 0}, "", true, 185040000.}, - /* GFLOPS 0.185 x 1 = 0.185 */ {{1, 1}, {{1, 128, 75, 75}}, 128, 1, {1, 1}, {1, 1}, {0, 0}, {0, 0}, "SAME", false, 185040000.}, - /* GFLOPS 0.181 x 1 = 0.181 */ {{3, 3}, {{1, 160, 14, 14}}, 320, 1, {1, 1}, {1, 1}, {1, 1}, {0, 0}, "", true, 180696320.}, - /* GFLOPS 0.181 x 1 = 0.181 */ {{3, 3}, {{1, 160, 14, 14}}, 320, 1, {1, 1}, {1, 1}, {0, 0}, {0, 0}, "SAME", true, 180696320.}, - /* GFLOPS 0.090 x 2 = 0.181 */ {{3, 3}, {{1, 224, 10, 10}}, 224, 1, {1, 1}, {1, 1}, {0, 0}, {0, 0}, "SAME", true, 90339200.}, - /* GFLOPS 0.180 x 1 = 0.180 */ {{1, 1}, {{1, 224, 56, 56}}, 128, 1, {1, 1}, {1, 1}, {0, 0}, {0, 0}, "", false, 180232192.}, - /* GFLOPS 0.088 x 2 = 0.177 */ {{1, 1}, {{1, 1024, 13, 13}}, 255, 1, {1, 1}, {1, 1}, {0, 0}, {0, 0}, "", true, 88301655.}, - /* GFLOPS 0.174 x 1 = 0.174 */ {{3, 3}, {{1, 96, 28, 28}}, 128, 1, {1, 1}, {1, 1}, {1, 1}, {0, 0}, "", true, 173508608.}, - /* GFLOPS 0.174 x 1 = 0.174 */ {{3, 3}, {{1, 96, 28, 28}}, 128, 1, {1, 1}, {1, 1}, {0, 0}, {0, 0}, "SAME", true, 173508608.}, - /* GFLOPS 0.166 x 1 = 0.166 */ {{3, 3}, {{1, 160, 19, 19}}, 160, 1, {1, 1}, {1, 1}, {0, 0}, {0, 0}, "SAME", true, 166406560.}, - /* GFLOPS 0.080 x 2 = 0.160 */ {{1, 1}, {{1, 576, 19, 19}}, 192, 1, {1, 1}, {1, 1}, {0, 0}, {0, 0}, "SAME", true, 79916736.}, - /* GFLOPS 0.160 x 1 = 0.160 */ {{3, 3}, {{1, 128, 19, 19}}, 192, 1, {1, 1}, {1, 1}, {0, 0}, {0, 0}, "SAME", true, 159764160.}, - /* GFLOPS 0.160 x 1 = 0.160 */ {{3, 3}, {{1, 1024, 19, 19}}, 24, 1, {1, 1}, {1, 1}, {1, 1}, {0, 0}, "", true, 159703512.}, /* GFLOPS 0.159 x 1 = 0.159 */ {{7, 7}, {{1, 3, 300, 300}}, 24, 1, {2, 2}, {1, 1}, {0, 0}, {0, 0}, "SAME", false, 159300000.}, - /* GFLOPS 0.080 x 2 = 0.159 */ {{1, 1}, {{1, 40, 64, 64}}, 240, 1, {1, 1}, {1, 1}, {0, 0}, {0, 0}, "SAME", false, 79626240.}, - /* GFLOPS 0.079 x 2 = 0.157 */ {{1, 1}, {{1, 480, 32, 32}}, 80, 1, {1, 1}, {1, 1}, {0, 0}, {0, 0}, "SAME", false, 78725120.}, - /* GFLOPS 0.155 x 1 = 0.155 */ {{1, 1}, {{1, 192, 56, 56}}, 128, 1, {1, 1}, {1, 1}, {0, 0}, {0, 0}, "", false, 154542080.}, - /* GFLOPS 0.146 x 1 = 0.146 */ {{3, 3}, {{1, 144, 14, 14}}, 288, 1, {1, 1}, {1, 1}, {1, 1}, {0, 0}, "", true, 146369664.}, - /* GFLOPS 0.146 x 1 = 0.146 */ {{3, 3}, {{1, 144, 14, 14}}, 288, 1, {1, 1}, {1, 1}, {0, 0}, {0, 0}, "SAME", true, 146369664.}, - /* GFLOPS 0.072 x 2 = 0.144 */ {{1, 1}, {{1, 1024, 10, 10}}, 352, 1, {1, 1}, {1, 1}, {0, 0}, {0, 0}, "SAME", true, 72124800.}, - /* GFLOPS 0.140 x 1 = 0.140 */ {{1, 1}, {{1, 576, 38, 50}}, 64, 1, {1, 1}, {1, 1}, {0, 0}, {0, 0}, "SAME", true, 140204800.}, - /* GFLOPS 0.139 x 1 = 0.139 */ {{3, 3}, {{1, 256, 5, 5}}, 1206, 1, {1, 1}, {1, 1}, {1, 1}, {0, 0}, "", true, 138961350.}, - /* GFLOPS 0.017 x 8 = 0.138 */ {{1, 1}, {{1, 16, 64, 128}}, 64, 1, {1, 1}, {1, 1}, {0, 0}, {0, 0}, "", false, 17301504.}, - /* GFLOPS 0.067 x 2 = 0.133 */ {{1, 1}, {{1, 576, 19, 19}}, 160, 1, {1, 1}, {1, 1}, {0, 0}, {0, 0}, "SAME", true, 66597280.}, /* GFLOPS 0.133 x 1 = 0.133 */ {{3, 3}, {{1, 128, 38, 38}}, 160, 1, {2, 2}, {1, 1}, {0, 0}, {0, 0}, "SAME", true, 133136800.}, - /* GFLOPS 0.044 x 3 = 0.133 */ {{1, 1}, {{1, 512, 13, 13}}, 256, 1, {1, 1}, {1, 1}, {0, 0}, {0, 0}, "", false, 44345600.}, - /* GFLOPS 0.129 x 1 = 0.129 */ {{1, 1}, {{1, 160, 56, 56}}, 128, 1, {1, 1}, {1, 1}, {0, 0}, {0, 0}, "", false, 128851968.}, - /* GFLOPS 0.128 x 1 = 0.128 */ {{3, 3}, {{1, 64, 24, 24}}, 192, 1, {1, 1}, {1, 1}, {1, 1}, {0, 0}, "", true, 127512576.}, /* GFLOPS 0.120 x 1 = 0.120 */ {{5, 5}, {{1, 32, 28, 28}}, 96, 1, {1, 1}, {1, 1}, {2, 2}, {0, 0}, "", true, 120497664.}, - /* GFLOPS 0.120 x 1 = 0.120 */ {{5, 5}, {{1, 32, 28, 28}}, 96, 1, {1, 1}, {1, 1}, {0, 0}, {0, 0}, "SAME", true, 120497664.}, - /* GFLOPS 0.040 x 3 = 0.120 */ {{1, 1}, {{1, 96, 19, 19}}, 576, 1, {1, 1}, {1, 1}, {0, 0}, {0, 0}, "SAME", false, 40131648.}, - /* GFLOPS 0.118 x 1 = 0.118 */ {{1, 1}, {{1, 320, 38, 38}}, 128, 1, {1, 1}, {1, 1}, {0, 0}, {0, 0}, "SAME", true, 118477312.}, - /* GFLOPS 0.017 x 7 = 0.118 */ {{1, 1}, {{1, 64, 64, 128}}, 16, 1, {1, 1}, {1, 1}, {0, 0}, {0, 0}, "", false, 16908288.}, + /* GFLOPS 0.060 x 2 = 0.119 */ {{3, 3}, {{1, 3, 736, 736}}, 8, 1, {2, 2}, {1, 1}, {1, 1}, {0, 0}, "", true, 59586560.}, /* GFLOPS 0.118 x 1 = 0.118 */ {{3, 3}, {{1, 64, 80, 80}}, 64, 1, {2, 2}, {1, 1}, {1, 1}, {0, 0}, "", false, 118067200.}, - /* GFLOPS 0.118 x 1 = 0.118 */ {{3, 3}, {{1, 64, 40, 40}}, 64, 1, {1, 1}, {1, 1}, {0, 0}, {0, 0}, "SAME", false, 118067200.}, - /* GFLOPS 0.039 x 3 = 0.118 */ {{1, 1}, {{1, 1024, 10, 10}}, 192, 1, {1, 1}, {1, 1}, {0, 0}, {0, 0}, "SAME", true, 39340800.}, /* GFLOPS 0.118 x 1 = 0.118 */ {{3, 3}, {{1, 128, 40, 40}}, 128, 1, {2, 2}, {1, 1}, {1, 1}, {0, 0}, "", false, 118016000.}, - /* GFLOPS 0.118 x 1 = 0.118 */ {{3, 3}, {{1, 128, 20, 20}}, 128, 1, {1, 1}, {1, 1}, {0, 0}, {0, 0}, "SAME", false, 118016000.}, - /* GFLOPS 0.118 x 1 = 0.118 */ {{3, 3}, {{1, 256, 20, 20}}, 256, 1, {2, 2}, {1, 1}, {1, 1}, {0, 0}, "", false, 117990400.}, - /* GFLOPS 0.118 x 1 = 0.118 */ {{3, 3}, {{1, 256, 19, 19}}, 256, 1, {2, 2}, {1, 1}, {0, 0}, {0, 0}, "SAME", true, 117990400.}, - /* GFLOPS 0.058 x 2 = 0.116 */ {{3, 3}, {{1, 16, 56, 56}}, 64, 1, {1, 1}, {1, 1}, {1, 1}, {0, 0}, "", true, 58003456.}, - /* GFLOPS 0.058 x 2 = 0.116 */ {{3, 3}, {{1, 32, 28, 28}}, 128, 1, {1, 1}, {1, 1}, {1, 1}, {0, 0}, "", true, 57903104.}, - /* GFLOPS 0.058 x 2 = 0.116 */ {{3, 3}, {{1, 64, 14, 14}}, 256, 1, {1, 1}, {1, 1}, {1, 1}, {0, 0}, "", true, 57852928.}, - /* GFLOPS 0.116 x 1 = 0.116 */ {{3, 3}, {{1, 128, 14, 14}}, 256, 1, {1, 1}, {1, 1}, {1, 1}, {0, 0}, "", true, 115655680.}, - /* GFLOPS 0.116 x 1 = 0.116 */ {{3, 3}, {{1, 128, 14, 14}}, 256, 1, {1, 1}, {1, 1}, {0, 0}, {0, 0}, "SAME", true, 115655680.}, /* GFLOPS 0.115 x 1 = 0.115 */ {{3, 3}, {{1, 3, 512, 512}}, 32, 1, {2, 2}, {1, 1}, {0, 0}, {0, 0}, "SAME", false, 115343360.}, - /* GFLOPS 0.114 x 1 = 0.114 */ {{1, 1}, {{1, 144, 128, 128}}, 24, 1, {1, 1}, {1, 1}, {0, 0}, {0, 0}, "SAME", false, 113639424.}, - /* GFLOPS 0.112 x 1 = 0.112 */ {{1, 1}, {{1, 1024, 10, 10}}, 546, 1, {1, 1}, {1, 1}, {0, 0}, {0, 0}, "SAME", true, 111875400.}, - /* GFLOPS 0.110 x 1 = 0.110 */ {{1, 1}, {{1, 480, 32, 32}}, 112, 1, {1, 1}, {1, 1}, {0, 0}, {0, 0}, "SAME", false, 110215168.}, - /* GFLOPS 0.107 x 1 = 0.107 */ {{1, 1}, {{1, 64, 32, 32}}, 810, 1, {1, 1}, {1, 1}, {0, 0}, {0, 0}, "VALID", true, 106997760.}, - /* GFLOPS 0.036 x 3 = 0.107 */ {{1, 1}, {{1, 192, 38, 38}}, 64, 1, {1, 1}, {1, 1}, {0, 0}, {0, 0}, "SAME", true, 35580160.}, /* GFLOPS 0.107 x 1 = 0.107 */ {{3, 3}, {{1, 32, 75, 75}}, 128, 1, {2, 2}, {1, 1}, {1, 1}, {0, 0}, "", false, 106648064.}, - /* GFLOPS 0.107 x 1 = 0.107 */ {{3, 3}, {{1, 64, 38, 38}}, 64, 1, {1, 1}, {1, 1}, {0, 0}, {0, 0}, "SAME", true, 106555648.}, - /* GFLOPS 0.105 x 1 = 0.105 */ {{1, 1}, {{1, 256, 40, 40}}, 128, 1, {1, 1}, {1, 1}, {0, 0}, {0, 0}, "SAME", false, 105062400.}, - /* GFLOPS 0.105 x 1 = 0.105 */ {{1, 1}, {{1, 512, 20, 20}}, 256, 1, {1, 1}, {1, 1}, {0, 0}, {0, 0}, "SAME", false, 104960000.}, - /* GFLOPS 0.105 x 1 = 0.105 */ {{1, 1}, {{1, 512, 10, 10}}, 1024, 1, {1, 1}, {1, 1}, {0, 0}, {0, 0}, "", true, 104960000.}, - /* GFLOPS 0.105 x 1 = 0.105 */ {{1, 1}, {{1, 512, 10, 10}}, 1024, 1, {1, 1}, {1, 1}, {0, 0}, {0, 0}, "SAME", false, 104960000.}, - /* GFLOPS 0.105 x 1 = 0.105 */ {{1, 1}, {{1, 1024, 10, 10}}, 512, 1, {1, 1}, {1, 1}, {0, 0}, {0, 0}, "SAME", false, 104908800.}, - /* GFLOPS 0.103 x 1 = 0.103 */ {{1, 1}, {{1, 128, 56, 56}}, 128, 1, {1, 1}, {1, 1}, {0, 0}, {0, 0}, "", false, 103161856.}, - /* GFLOPS 0.051 x 2 = 0.103 */ {{1, 1}, {{1, 256, 28, 28}}, 128, 1, {1, 1}, {1, 1}, {0, 0}, {0, 0}, "", true, 51480576.}, - /* GFLOPS 0.051 x 2 = 0.103 */ {{1, 1}, {{1, 256, 28, 28}}, 128, 1, {1, 1}, {1, 1}, {0, 0}, {0, 0}, "SAME", true, 51480576.}, - /* GFLOPS 0.008 x 12 = 0.101 */ {{1, 1}, {{1, 64, 32, 32}}, 64, 1, {1, 1}, {1, 1}, {0, 0}, {0, 0}, "VALID", true, 8454144.}, - /* GFLOPS 0.101 x 1 = 0.101 */ {{1, 1}, {{1, 512, 19, 19}}, 273, 1, {1, 1}, {1, 1}, {0, 0}, {0, 0}, "SAME", true, 101016825.}, - /* GFLOPS 0.096 x 1 = 0.096 */ {{1, 1}, {{1, 480, 28, 28}}, 128, 1, {1, 1}, {1, 1}, {0, 0}, {0, 0}, "", false, 96438272.}, - /* GFLOPS 0.095 x 1 = 0.095 */ {{1, 1}, {{1, 128, 38, 38}}, 256, 1, {1, 1}, {1, 1}, {0, 0}, {0, 0}, "", true, 95003648.}, - /* GFLOPS 0.095 x 1 = 0.095 */ {{1, 1}, {{1, 128, 38, 38}}, 256, 1, {1, 1}, {1, 1}, {0, 0}, {0, 0}, "SAME", false, 95003648.}, - /* GFLOPS 0.095 x 1 = 0.095 */ {{1, 1}, {{1, 256, 19, 19}}, 512, 1, {1, 1}, {1, 1}, {0, 0}, {0, 0}, "", true, 94818816.}, - /* GFLOPS 0.095 x 1 = 0.095 */ {{1, 1}, {{1, 256, 19, 19}}, 512, 1, {1, 1}, {1, 1}, {0, 0}, {0, 0}, "SAME", false, 94818816.}, - /* GFLOPS 0.094 x 1 = 0.094 */ {{1, 1}, {{1, 32, 150, 150}}, 64, 1, {1, 1}, {1, 1}, {0, 0}, {0, 0}, "", true, 93600000.}, - /* GFLOPS 0.094 x 1 = 0.094 */ {{1, 1}, {{1, 32, 150, 150}}, 64, 1, {1, 1}, {1, 1}, {0, 0}, {0, 0}, "SAME", false, 93600000.}, - /* GFLOPS 0.093 x 1 = 0.093 */ {{1, 1}, {{1, 512, 38, 50}}, 48, 1, {1, 1}, {1, 1}, {0, 0}, {0, 0}, "SAME", true, 93480000.}, - /* GFLOPS 0.093 x 1 = 0.093 */ {{1, 1}, {{1, 576, 19, 19}}, 224, 1, {1, 1}, {1, 1}, {0, 0}, {0, 0}, "SAME", true, 93236192.}, - /* GFLOPS 0.093 x 1 = 0.093 */ {{1, 1}, {{1, 64, 75, 75}}, 128, 1, {1, 1}, {1, 1}, {0, 0}, {0, 0}, "", true, 92880000.}, - /* GFLOPS 0.093 x 1 = 0.093 */ {{1, 1}, {{1, 64, 75, 75}}, 128, 1, {1, 1}, {1, 1}, {0, 0}, {0, 0}, "SAME", false, 92880000.}, - /* GFLOPS 0.031 x 3 = 0.092 */ {{1, 1}, {{1, 160, 10, 10}}, 960, 1, {1, 1}, {1, 1}, {0, 0}, {0, 0}, "SAME", false, 30816000.}, - /* GFLOPS 0.092 x 1 = 0.092 */ {{1, 1}, {{1, 192, 75, 100}}, 32, 1, {1, 1}, {1, 1}, {0, 0}, {0, 0}, "SAME", true, 92400000.}, - /* GFLOPS 0.090 x 1 = 0.090 */ {{1, 1}, {{1, 448, 28, 28}}, 128, 1, {1, 1}, {1, 1}, {0, 0}, {0, 0}, "", false, 90015744.}, - /* GFLOPS 0.045 x 2 = 0.090 */ {{3, 3}, {{1, 576, 19, 19}}, 12, 1, {1, 1}, {1, 1}, {0, 0}, {0, 0}, "SAME", true, 44918508.}, - /* GFLOPS 0.044 x 2 = 0.089 */ {{1, 1}, {{1, 256, 26, 26}}, 128, 1, {1, 1}, {1, 1}, {0, 0}, {0, 0}, "", false, 44388864.}, - /* GFLOPS 0.089 x 1 = 0.089 */ {{3, 3}, {{1, 112, 14, 14}}, 224, 1, {1, 1}, {1, 1}, {1, 1}, {0, 0}, "", true, 88554368.}, - /* GFLOPS 0.089 x 1 = 0.089 */ {{3, 3}, {{1, 112, 14, 14}}, 224, 1, {1, 1}, {1, 1}, {0, 0}, {0, 0}, "SAME", true, 88554368.}, - /* GFLOPS 0.088 x 1 = 0.088 */ {{1, 1}, {{1, 256, 26, 26}}, 255, 1, {1, 1}, {1, 1}, {0, 0}, {0, 0}, "", true, 88430940.}, + /* GFLOPS 0.050 x 2 = 0.101 */ {{2, 2}, {{1, 512, 2, 25}}, 512, 1, {1, 1}, {1, 1}, {0, 0}, {0, 0}, "", true, 50343936.}, + /* GFLOPS 0.044 x 2 = 0.087 */ {{5, 5}, {{1, 3, 192, 192}}, 32, 1, {2, 2}, {1, 1}, {1, 1}, {0, 0}, "", true, 43608800.}, + /* GFLOPS 0.042 x 2 = 0.085 */ {{3, 3}, {{1, 128, 48, 48}}, 32, 1, {2, 2}, {1, 1}, {0, 0}, {0, 0}, "SAME", true, 42485760.}, /* GFLOPS 0.021 x 4 = 0.084 */ {{5, 1}, {{1, 32, 32, 64}}, 32, 1, {1, 1}, {1, 1}, {2, 0}, {0, 0}, "", false, 21037056.}, /* GFLOPS 0.021 x 4 = 0.084 */ {{1, 5}, {{1, 32, 32, 64}}, 32, 1, {1, 1}, {1, 1}, {0, 2}, {0, 0}, "", true, 21037056.}, - /* GFLOPS 0.084 x 1 = 0.084 */ {{1, 1}, {{1, 416, 28, 28}}, 128, 1, {1, 1}, {1, 1}, {0, 0}, {0, 0}, "", false, 83593216.}, - /* GFLOPS 0.082 x 1 = 0.082 */ {{1, 1}, {{1, 320, 10, 10}}, 1280, 1, {1, 1}, {1, 1}, {0, 0}, {0, 0}, "SAME", false, 82048000.}, - /* GFLOPS 0.040 x 2 = 0.080 */ {{1, 1}, {{1, 576, 19, 19}}, 96, 1, {1, 1}, {1, 1}, {0, 0}, {0, 0}, "SAME", false, 39958368.}, - /* GFLOPS 0.040 x 2 = 0.079 */ {{1, 1}, {{1, 24, 75, 75}}, 144, 1, {1, 1}, {1, 1}, {0, 0}, {0, 0}, "SAME", false, 39690000.}, - /* GFLOPS 0.040 x 2 = 0.079 */ {{3, 3}, {{1, 3, 300, 300}}, 32, 1, {2, 2}, {1, 1}, {0, 0}, {0, 0}, "SAME", false, 39600000.}, - /* GFLOPS 0.079 x 1 = 0.079 */ {{1, 1}, {{1, 240, 64, 64}}, 40, 1, {1, 1}, {1, 1}, {0, 0}, {0, 0}, "SAME", false, 78807040.}, - /* GFLOPS 0.079 x 1 = 0.079 */ {{1, 1}, {{1, 384, 40, 40}}, 64, 1, {1, 1}, {1, 1}, {0, 0}, {0, 0}, "SAME", false, 78745600.}, - /* GFLOPS 0.077 x 1 = 0.077 */ {{1, 1}, {{1, 96, 56, 56}}, 128, 1, {1, 1}, {1, 1}, {0, 0}, {0, 0}, "", false, 77471744.}, - /* GFLOPS 0.077 x 1 = 0.077 */ {{3, 3}, {{1, 192, 10, 10}}, 224, 1, {1, 1}, {1, 1}, {0, 0}, {0, 0}, "SAME", true, 77436800.}, - /* GFLOPS 0.077 x 1 = 0.077 */ {{1, 1}, {{1, 384, 28, 28}}, 128, 1, {1, 1}, {1, 1}, {0, 0}, {0, 0}, "", false, 77170688.}, /* GFLOPS 0.076 x 1 = 0.076 */ {{3, 3}, {{1, 3, 416, 416}}, 32, 1, {2, 2}, {1, 1}, {1, 1}, {0, 0}, "", false, 76144640.}, - /* GFLOPS 0.076 x 1 = 0.076 */ {{1, 1}, {{1, 96, 128, 128}}, 24, 1, {1, 1}, {1, 1}, {0, 0}, {0, 0}, "SAME", false, 75890688.}, /* GFLOPS 0.038 x 2 = 0.076 */ {{3, 3}, {{1, 32, 32, 64}}, 32, 1, {1, 1}, {8, 8}, {8, 8}, {0, 0}, "", true, 37814272.}, /* GFLOPS 0.038 x 2 = 0.076 */ {{3, 3}, {{1, 32, 32, 64}}, 32, 1, {1, 1}, {4, 4}, {4, 4}, {0, 0}, "", true, 37814272.}, /* GFLOPS 0.038 x 2 = 0.076 */ {{3, 3}, {{1, 32, 32, 64}}, 32, 1, {1, 1}, {2, 2}, {2, 2}, {0, 0}, "", true, 37814272.}, /* GFLOPS 0.038 x 2 = 0.076 */ {{3, 3}, {{1, 32, 32, 64}}, 32, 1, {1, 1}, {16, 16}, {16, 16}, {0, 0}, "", true, 37814272.}, - /* GFLOPS 0.018 x 4 = 0.072 */ {{1, 1}, {{1, 64, 19, 19}}, 384, 1, {1, 1}, {1, 1}, {0, 0}, {0, 0}, "SAME", false, 17882496.}, + /* GFLOPS 0.032 x 2 = 0.065 */ {{3, 3}, {{1, 3, 192, 192}}, 64, 1, {2, 2}, {1, 1}, {0, 0}, {0, 0}, "SAME", true, 32440320.}, + /* GFLOPS 0.060 x 1 = 0.060 */ {{3, 3}, {{1, 96, 38, 38}}, 96, 1, {2, 2}, {1, 1}, {0, 0}, {0, 0}, "SAME", true, 59920224.}, + /* GFLOPS 0.059 x 1 = 0.059 */ {{3, 3}, {{1, 256, 10, 10}}, 512, 1, {2, 2}, {1, 1}, {1, 1}, {0, 0}, "", true, 58995200.}, + /* GFLOPS 0.045 x 1 = 0.045 */ {{3, 3}, {{1, 3, 227, 227}}, 64, 1, {2, 2}, {1, 1}, {0, 0}, {0, 0}, "", true, 44946880.}, + /* GFLOPS 0.044 x 1 = 0.044 */ {{3, 3}, {{1, 128, 19, 19}}, 192, 1, {2, 2}, {1, 1}, {0, 0}, {0, 0}, "SAME", true, 44256000.}, + /* GFLOPS 0.043 x 1 = 0.043 */ {{7, 7}, {{1, 3, 96, 96}}, 64, 1, {2, 2}, {1, 1}, {3, 3}, {0, 0}, "", true, 43499520.}, + /* GFLOPS 0.022 x 2 = 0.043 */ {{3, 3}, {{1, 3, 224, 224}}, 32, 1, {2, 2}, {1, 1}, {0, 0}, {0, 0}, "", true, 21684960.}, + /* GFLOPS 0.022 x 2 = 0.043 */ {{3, 3}, {{1, 3, 258, 258}}, 24, 1, {2, 2}, {1, 1}, {0, 0}, {0, 0}, "", true, 21626880.}, + /* GFLOPS 0.040 x 1 = 0.040 */ {{3, 3}, {{1, 3, 300, 300}}, 32, 1, {2, 2}, {1, 1}, {1, 1}, {0, 0}, "", true, 39600000.}, + /* GFLOPS 0.034 x 1 = 0.034 */ {{2, 2}, {{1, 64, 64, 128}}, 32, 1, {2, 2}, {1, 1}, {0, 0}, {0, 0}, "", false, 33619968.}, + /* GFLOPS 0.016 x 2 = 0.033 */ {{3, 3}, {{1, 3, 224, 224}}, 24, 1, {2, 2}, {1, 1}, {0, 0}, {0, 0}, "", true, 16263720.}, + /* GFLOPS 0.005 x 6 = 0.032 */ {{3, 3}, {{1, 16, 48, 48}}, 32, 1, {2, 2}, {1, 1}, {0, 0}, {0, 0}, "SAME", true, 5326848.}, + /* GFLOPS 0.005 x 6 = 0.032 */ {{3, 3}, {{1, 32, 24, 24}}, 64, 1, {2, 2}, {1, 1}, {0, 0}, {0, 0}, "SAME", true, 5317632.}, + /* GFLOPS 0.015 x 2 = 0.030 */ {{5, 5}, {{1, 24, 14, 14}}, 64, 1, {1, 1}, {1, 1}, {2, 2}, {0, 0}, "", true, 15065344.}, + /* GFLOPS 0.029 x 1 = 0.029 */ {{3, 3}, {{1, 256, 10, 10}}, 256, 1, {2, 2}, {1, 1}, {0, 0}, {0, 0}, "SAME", false, 29497600.}, + /* GFLOPS 0.023 x 1 = 0.023 */ {{3, 3}, {{1, 3, 256, 512}}, 13, 1, {2, 2}, {1, 1}, {1, 1}, {0, 0}, "", true, 23429120.}, + /* GFLOPS 0.017 x 1 = 0.017 */ {{2, 2}, {{1, 16, 128, 256}}, 16, 1, {2, 2}, {1, 1}, {0, 0}, {0, 0}, "", false, 16908288.}, + /* GFLOPS 0.003 x 6 = 0.016 */ {{3, 3}, {{1, 16, 48, 48}}, 16, 1, {2, 2}, {1, 1}, {0, 0}, {0, 0}, "SAME", true, 2663424.}, + /* GFLOPS 0.015 x 1 = 0.015 */ {{5, 5}, {{1, 48, 7, 7}}, 128, 1, {1, 1}, {1, 1}, {2, 2}, {0, 0}, "", true, 15059072.}, + /* GFLOPS 0.005 x 2 = 0.011 */ {{3, 3}, {{1, 3, 256, 256}}, 6, 1, {2, 2}, {1, 1}, {1, 1}, {0, 0}, "", true, 5406720.}, + /* GFLOPS 0.005 x 2 = 0.011 */ {{3, 3}, {{1, 6, 128, 128}}, 12, 1, {2, 2}, {1, 1}, {1, 1}, {0, 0}, "", true, 5357568.}, + /* GFLOPS 0.005 x 2 = 0.011 */ {{3, 3}, {{1, 12, 64, 64}}, 24, 1, {2, 2}, {1, 1}, {1, 1}, {0, 0}, "", true, 5332992.}, + /* GFLOPS 0.005 x 2 = 0.011 */ {{3, 3}, {{1, 24, 32, 32}}, 48, 1, {2, 2}, {1, 1}, {1, 1}, {0, 0}, "", true, 5320704.}, + /* GFLOPS 0.003 x 4 = 0.011 */ {{3, 3}, {{1, 16, 24, 24}}, 64, 1, {2, 2}, {1, 1}, {0, 0}, {0, 0}, "SAME", true, 2663424.}, + /* GFLOPS 0.010 x 1 = 0.010 */ {{5, 5}, {{1, 32, 7, 7}}, 128, 1, {1, 1}, {1, 1}, {2, 2}, {0, 0}, "", true, 10041472.}, + /* GFLOPS 0.008 x 1 = 0.008 */ {{5, 5}, {{1, 16, 14, 14}}, 48, 1, {1, 1}, {1, 1}, {2, 2}, {0, 0}, "", true, 7535808.}, + /* GFLOPS 0.007 x 1 = 0.007 */ {{3, 3}, {{1, 160, 6, 6}}, 256, 1, {2, 2}, {1, 1}, {1, 1}, {0, 0}, "", true, 6637824.}, + /* GFLOPS 0.003 x 2 = 0.005 */ {{3, 3}, {{1, 32, 24, 24}}, 32, 1, {2, 2}, {1, 1}, {0, 0}, {0, 0}, "SAME", true, 2658816.}, + /* GFLOPS 0.003 x 2 = 0.005 */ {{3, 3}, {{1, 32, 12, 12}}, 128, 1, {2, 2}, {1, 1}, {0, 0}, {0, 0}, "SAME", true, 2658816.}, + /* GFLOPS 0.004 x 1 = 0.004 */ {{5, 5}, {{1, 16, 12, 12}}, 32, 1, {1, 1}, {1, 1}, {2, 2}, {0, 0}, "", true, 3691008.}, + /* GFLOPS 0.004 x 1 = 0.004 */ {{5, 5}, {{1, 32, 6, 6}}, 64, 1, {1, 1}, {1, 1}, {2, 2}, {0, 0}, "", true, 3688704.}, + /* GFLOPS 0.004 x 1 = 0.004 */ {{5, 5}, {{1, 32, 12, 12}}, 64, 1, {2, 2}, {1, 1}, {2, 2}, {0, 0}, "", true, 3688704.}, + /* GFLOPS 0.004 x 1 = 0.004 */ {{5, 5}, {{1, 64, 6, 6}}, 128, 1, {2, 2}, {1, 1}, {2, 2}, {0, 0}, "", true, 3687552.}, + /* GFLOPS 0.001 x 2 = 0.003 */ {{3, 3}, {{1, 3, 128, 128}}, 6, 1, {2, 2}, {1, 1}, {1, 1}, {0, 0}, "", true, 1351680.}, + /* GFLOPS 0.001 x 2 = 0.003 */ {{3, 3}, {{1, 6, 64, 64}}, 12, 1, {2, 2}, {1, 1}, {1, 1}, {0, 0}, "", true, 1339392.}, + /* GFLOPS 0.001 x 2 = 0.003 */ {{3, 3}, {{1, 12, 32, 32}}, 24, 1, {2, 2}, {1, 1}, {1, 1}, {0, 0}, "", true, 1333248.}, + /* GFLOPS 0.001 x 2 = 0.003 */ {{3, 3}, {{1, 16, 12, 12}}, 128, 1, {2, 2}, {1, 1}, {0, 0}, {0, 0}, "SAME", true, 1331712.}, + /* GFLOPS 0.001 x 2 = 0.003 */ {{3, 3}, {{1, 24, 16, 16}}, 48, 1, {2, 2}, {1, 1}, {1, 1}, {0, 0}, "", true, 1330176.}, + /* GFLOPS 0.002 x 1 = 0.002 */ {{3, 3}, {{1, 128, 3, 3}}, 256, 1, {2, 2}, {1, 1}, {1, 1}, {0, 0}, "", true, 2360320.}, + /* GFLOPS 0.001 x 1 = 0.001 */ {{3, 3}, {{1, 128, 3, 3}}, 128, 1, {2, 2}, {1, 1}, {0, 0}, {0, 0}, "SAME", false, 1180160.}, + /* GFLOPS 0.001 x 2 = 0.001 */ {{3, 3}, {{1, 16, 24, 24}}, 16, 1, {2, 2}, {1, 1}, {0, 0}, {0, 0}, "SAME", true, 665856.}, + /* GFLOPS 0.000 x 2 = 0.001 */ {{2, 2}, {{1, 192, 2, 2}}, 195, 1, {1, 1}, {1, 1}, {0, 0}, {0, 0}, "", true, 299715.}, + /* GFLOPS 0.000 x 2 = 0.000 */ {{2, 2}, {{1, 192, 2, 2}}, 117, 1, {1, 1}, {1, 1}, {0, 0}, {0, 0}, "", true, 179829.}, + /* GFLOPS 0.000 x 1 = 0.000 */ {{3, 3}, {{1, 64, 2, 2}}, 128, 1, {2, 2}, {1, 1}, {1, 1}, {0, 0}, "", true, 147584.}, + /* GFLOPS 0.000 x 1 = 0.000 */ {{3, 3}, {{1, 64, 2, 2}}, 64, 1, {2, 2}, {1, 1}, {0, 0}, {0, 0}, "SAME", false, 73792.}, + /* GFLOPS 0.000 x 2 = 0.000 */ {{2, 2}, {{1, 192, 2, 2}}, 1, 1, {1, 1}, {1, 1}, {0, 0}, {0, 0}, "", true, 1537.}, +}; + +static const ConvParam_t testConvolution_1x1_Configs[] = { + /* GFLOPS 0.280 x 5 = 1.402 */ {{1, 1}, {{1, 576, 38, 50}}, 128, 1, {1, 1}, {1, 1}, {0, 0}, {0, 0}, "SAME", true, 280409600.}, + /* GFLOPS 0.210 x 6 = 1.262 */ {{1, 1}, {{1, 576, 38, 50}}, 96, 1, {1, 1}, {1, 1}, {0, 0}, {0, 0}, "SAME", true, 210307200.}, + /* GFLOPS 0.357 x 3 = 1.072 */ {{1, 1}, {{1, 64, 208, 208}}, 64, 1, {1, 1}, {1, 1}, {0, 0}, {0, 0}, "", false, 357187584.}, + /* GFLOPS 0.246 x 4 = 0.985 */ {{1, 1}, {{1, 256, 75, 100}}, 64, 1, {1, 1}, {1, 1}, {0, 0}, {0, 0}, "SAME", true, 246240000.}, + /* GFLOPS 0.053 x 18 = 0.947 */ {{1, 1}, {{1, 128, 40, 40}}, 128, 1, {1, 1}, {1, 1}, {0, 0}, {0, 0}, "", true, 52633600.}, + /* GFLOPS 0.712 x 1 = 0.712 */ {{1, 1}, {{1, 128, 208, 208}}, 64, 1, {1, 1}, {1, 1}, {0, 0}, {0, 0}, "", false, 711606272.}, + /* GFLOPS 0.178 x 4 = 0.712 */ {{1, 1}, {{1, 128, 104, 104}}, 64, 1, {1, 1}, {1, 1}, {0, 0}, {0, 0}, "", false, 177901568.}, + /* GFLOPS 0.354 x 2 = 0.707 */ {{1, 1}, {{1, 256, 52, 52}}, 255, 1, {1, 1}, {1, 1}, {0, 0}, {0, 0}, "", true, 353723760.}, + /* GFLOPS 0.351 x 2 = 0.701 */ {{1, 1}, {{1, 576, 38, 50}}, 160, 1, {1, 1}, {1, 1}, {0, 0}, {0, 0}, "SAME", true, 350512000.}, + /* GFLOPS 0.211 x 3 = 0.634 */ {{1, 1}, {{1, 64, 80, 80}}, 256, 1, {1, 1}, {1, 1}, {0, 0}, {0, 0}, "SAME", false, 211353600.}, + /* GFLOPS 0.211 x 3 = 0.632 */ {{1, 1}, {{1, 128, 40, 40}}, 512, 1, {1, 1}, {1, 1}, {0, 0}, {0, 0}, "SAME", false, 210534400.}, + /* GFLOPS 0.105 x 6 = 0.632 */ {{1, 1}, {{1, 128, 80, 80}}, 64, 1, {1, 1}, {1, 1}, {0, 0}, {0, 0}, "", true, 105267200.}, + /* GFLOPS 0.210 x 3 = 0.630 */ {{1, 1}, {{1, 512, 40, 40}}, 128, 1, {1, 1}, {1, 1}, {0, 0}, {0, 0}, "SAME", false, 209920000.}, + /* GFLOPS 0.615 x 1 = 0.615 */ {{1, 1}, {{1, 320, 75, 100}}, 128, 1, {1, 1}, {1, 1}, {0, 0}, {0, 0}, "SAME", true, 615360000.}, + /* GFLOPS 0.044 x 14 = 0.609 */ {{1, 1}, {{1, 1632, 7, 7}}, 272, 1, {1, 1}, {1, 1}, {0, 0}, {0, 0}, "", true, 43515920.}, + /* GFLOPS 0.185 x 3 = 0.554 */ {{1, 1}, {{1, 192, 75, 100}}, 64, 1, {1, 1}, {1, 1}, {0, 0}, {0, 0}, "SAME", true, 184800000.}, + /* GFLOPS 0.266 x 2 = 0.532 */ {{1, 1}, {{1, 240, 48, 48}}, 240, 1, {1, 1}, {1, 1}, {0, 0}, {0, 0}, "SAME", true, 265973760.}, + /* GFLOPS 0.491 x 1 = 0.491 */ {{1, 1}, {{1, 576, 38, 50}}, 224, 1, {1, 1}, {1, 1}, {0, 0}, {0, 0}, "SAME", true, 490716800.}, + /* GFLOPS 0.079 x 6 = 0.473 */ {{1, 1}, {{1, 192, 40, 40}}, 128, 1, {1, 1}, {1, 1}, {0, 0}, {0, 0}, "", true, 78848000.}, + /* GFLOPS 0.079 x 6 = 0.472 */ {{1, 1}, {{1, 384, 20, 20}}, 256, 1, {1, 1}, {1, 1}, {0, 0}, {0, 0}, "", true, 78745600.}, + /* GFLOPS 0.155 x 3 = 0.464 */ {{1, 1}, {{1, 112, 32, 32}}, 672, 1, {1, 1}, {1, 1}, {0, 0}, {0, 0}, "SAME", false, 154828800.}, + /* GFLOPS 0.114 x 4 = 0.454 */ {{1, 1}, {{1, 192, 16, 16}}, 1152, 1, {1, 1}, {1, 1}, {0, 0}, {0, 0}, "SAME", false, 113541120.}, + /* GFLOPS 0.089 x 5 = 0.443 */ {{1, 1}, {{1, 512, 13, 13}}, 512, 1, {1, 1}, {1, 1}, {0, 0}, {0, 0}, "", false, 88691200.}, + /* GFLOPS 0.428 x 1 = 0.428 */ {{1, 1}, {{1, 64, 64, 64}}, 810, 1, {1, 1}, {1, 1}, {0, 0}, {0, 0}, "VALID", true, 427991040.}, + /* GFLOPS 0.053 x 8 = 0.426 */ {{1, 1}, {{1, 32, 160, 160}}, 32, 1, {1, 1}, {1, 1}, {0, 0}, {0, 0}, "", true, 53248000.}, + /* GFLOPS 0.211 x 2 = 0.423 */ {{1, 1}, {{1, 64, 160, 160}}, 64, 1, {1, 1}, {1, 1}, {0, 0}, {0, 0}, "", true, 211353600.}, + /* GFLOPS 0.106 x 4 = 0.423 */ {{1, 1}, {{1, 64, 160, 160}}, 32, 1, {1, 1}, {1, 1}, {0, 0}, {0, 0}, "", true, 105676800.}, + /* GFLOPS 0.421 x 1 = 0.421 */ {{1, 1}, {{1, 576, 38, 50}}, 192, 1, {1, 1}, {1, 1}, {0, 0}, {0, 0}, "SAME", true, 420614400.}, + /* GFLOPS 0.211 x 2 = 0.421 */ {{1, 1}, {{1, 64, 80, 80}}, 255, 1, {1, 1}, {1, 1}, {0, 0}, {0, 0}, "", true, 210528000.}, + /* GFLOPS 0.420 x 1 = 0.420 */ {{1, 1}, {{1, 256, 40, 40}}, 512, 1, {1, 1}, {1, 1}, {0, 0}, {0, 0}, "SAME", false, 420249600.}, + /* GFLOPS 0.420 x 1 = 0.420 */ {{1, 1}, {{1, 1024, 10, 10}}, 2048, 1, {1, 1}, {1, 1}, {0, 0}, {0, 0}, "SAME", false, 419635200.}, + /* GFLOPS 0.210 x 2 = 0.420 */ {{1, 1}, {{1, 256, 80, 80}}, 64, 1, {1, 1}, {1, 1}, {0, 0}, {0, 0}, "SAME", false, 210124800.}, + /* GFLOPS 0.376 x 1 = 0.376 */ {{1, 1}, {{1, 24, 300, 400}}, 64, 1, {1, 1}, {1, 1}, {0, 0}, {0, 0}, "VALID", true, 376320000.}, + /* GFLOPS 0.179 x 2 = 0.357 */ {{1, 1}, {{1, 64, 208, 208}}, 32, 1, {1, 1}, {1, 1}, {0, 0}, {0, 0}, "", false, 178593792.}, + /* GFLOPS 0.089 x 4 = 0.357 */ {{1, 1}, {{1, 64, 104, 104}}, 64, 1, {1, 1}, {1, 1}, {0, 0}, {0, 0}, "", false, 89296896.}, + /* GFLOPS 0.356 x 1 = 0.356 */ {{1, 1}, {{1, 128, 104, 104}}, 128, 1, {1, 1}, {1, 1}, {0, 0}, {0, 0}, "", false, 355803136.}, + /* GFLOPS 0.113 x 3 = 0.340 */ {{1, 1}, {{1, 1152, 16, 16}}, 192, 1, {1, 1}, {1, 1}, {0, 0}, {0, 0}, "SAME", false, 113295360.}, + /* GFLOPS 0.080 x 4 = 0.321 */ {{1, 1}, {{1, 56, 46, 46}}, 336, 1, {1, 1}, {1, 1}, {0, 0}, {0, 0}, "", true, 80340288.}, + /* GFLOPS 0.158 x 2 = 0.315 */ {{1, 1}, {{1, 192, 80, 80}}, 64, 1, {1, 1}, {1, 1}, {0, 0}, {0, 0}, "", true, 157696000.}, + /* GFLOPS 0.157 x 2 = 0.315 */ {{1, 1}, {{1, 384, 40, 40}}, 128, 1, {1, 1}, {1, 1}, {0, 0}, {0, 0}, "", true, 157491200.}, + /* GFLOPS 0.154 x 2 = 0.309 */ {{1, 1}, {{1, 672, 32, 32}}, 112, 1, {1, 1}, {1, 1}, {0, 0}, {0, 0}, "SAME", false, 154255360.}, + /* GFLOPS 0.103 x 3 = 0.309 */ {{1, 1}, {{1, 512, 7, 7}}, 2048, 1, {1, 1}, {1, 1}, {0, 0}, {0, 0}, "", false, 102860800.}, + /* GFLOPS 0.308 x 1 = 0.308 */ {{1, 1}, {{1, 320, 75, 100}}, 64, 1, {1, 1}, {1, 1}, {0, 0}, {0, 0}, "SAME", true, 307680000.}, + /* GFLOPS 0.034 x 9 = 0.304 */ {{1, 1}, {{1, 64, 64, 64}}, 64, 1, {1, 1}, {1, 1}, {0, 0}, {0, 0}, "VALID", true, 33816576.}, + /* GFLOPS 0.017 x 17 = 0.290 */ {{1, 1}, {{1, 32, 32, 64}}, 128, 1, {1, 1}, {1, 1}, {0, 0}, {0, 0}, "", false, 17039360.}, + /* GFLOPS 0.017 x 16 = 0.269 */ {{1, 1}, {{1, 128, 32, 64}}, 32, 1, {1, 1}, {1, 1}, {0, 0}, {0, 0}, "", false, 16842752.}, + /* GFLOPS 0.266 x 1 = 0.266 */ {{1, 1}, {{1, 768, 26, 26}}, 256, 1, {1, 1}, {1, 1}, {0, 0}, {0, 0}, "", false, 265987072.}, + /* GFLOPS 0.132 x 2 = 0.263 */ {{1, 1}, {{1, 128, 80, 80}}, 80, 1, {1, 1}, {1, 1}, {0, 0}, {0, 0}, "", true, 131584000.}, + /* GFLOPS 0.026 x 10 = 0.263 */ {{1, 1}, {{1, 128, 40, 40}}, 64, 1, {1, 1}, {1, 1}, {0, 0}, {0, 0}, "", true, 26316800.}, + /* GFLOPS 0.262 x 1 = 0.262 */ {{1, 1}, {{1, 2560, 20, 20}}, 128, 1, {1, 1}, {1, 1}, {0, 0}, {0, 0}, "SAME", false, 262195200.}, + /* GFLOPS 0.248 x 1 = 0.248 */ {{1, 1}, {{1, 64, 150, 200}}, 64, 1, {1, 1}, {1, 1}, {0, 0}, {0, 0}, "SAME", true, 247680000.}, + /* GFLOPS 0.041 x 6 = 0.245 */ {{1, 1}, {{1, 80, 23, 23}}, 480, 1, {1, 1}, {1, 1}, {0, 0}, {0, 0}, "", true, 40881120.}, + /* GFLOPS 0.079 x 3 = 0.237 */ {{1, 1}, {{1, 80, 32, 32}}, 480, 1, {1, 1}, {1, 1}, {0, 0}, {0, 0}, "SAME", false, 79134720.}, + /* GFLOPS 0.116 x 2 = 0.231 */ {{1, 1}, {{1, 24, 128, 128}}, 144, 1, {1, 1}, {1, 1}, {0, 0}, {0, 0}, "SAME", false, 115605504.}, + /* GFLOPS 0.107 x 2 = 0.215 */ {{1, 1}, {{1, 16, 184, 184}}, 96, 1, {1, 1}, {1, 1}, {0, 0}, {0, 0}, "", false, 107255808.}, + /* GFLOPS 0.106 x 2 = 0.213 */ {{1, 1}, {{1, 32, 160, 160}}, 64, 1, {1, 1}, {1, 1}, {0, 0}, {0, 0}, "", true, 106496000.}, + /* GFLOPS 0.105 x 2 = 0.210 */ {{1, 1}, {{1, 128, 40, 40}}, 255, 1, {1, 1}, {1, 1}, {0, 0}, {0, 0}, "", true, 104856000.}, + /* GFLOPS 0.208 x 1 = 0.208 */ {{1, 1}, {{1, 16, 256, 256}}, 96, 1, {1, 1}, {1, 1}, {0, 0}, {0, 0}, "SAME", false, 207618048.}, + /* GFLOPS 0.206 x 1 = 0.206 */ {{1, 1}, {{1, 256, 56, 56}}, 512, 1, {2, 2}, {1, 1}, {0, 0}, {0, 0}, "", false, 205922304.}, + /* GFLOPS 0.206 x 1 = 0.206 */ {{1, 1}, {{1, 512, 28, 28}}, 1024, 1, {2, 2}, {1, 1}, {0, 0}, {0, 0}, "", false, 205721600.}, + /* GFLOPS 0.206 x 1 = 0.206 */ {{1, 1}, {{1, 1024, 14, 14}}, 2048, 1, {2, 2}, {1, 1}, {0, 0}, {0, 0}, "", false, 205621248.}, + /* GFLOPS 0.103 x 2 = 0.206 */ {{1, 1}, {{1, 1024, 7, 7}}, 1024, 1, {1, 1}, {1, 1}, {0, 0}, {0, 0}, "", false, 102810624.}, + /* GFLOPS 0.103 x 2 = 0.206 */ {{1, 1}, {{1, 2048, 7, 7}}, 512, 1, {1, 1}, {1, 1}, {0, 0}, {0, 0}, "", false, 102785536.}, + /* GFLOPS 0.201 x 1 = 0.201 */ {{1, 1}, {{1, 512, 14, 14}}, 1000, 1, {1, 1}, {1, 1}, {0, 0}, {0, 0}, "", true, 200900000.}, + /* GFLOPS 0.190 x 1 = 0.190 */ {{1, 1}, {{1, 256, 38, 38}}, 256, 1, {1, 1}, {1, 1}, {0, 0}, {0, 0}, "", true, 189637632.}, + /* GFLOPS 0.047 x 4 = 0.190 */ {{1, 1}, {{1, 256, 38, 38}}, 64, 1, {1, 1}, {1, 1}, {0, 0}, {0, 0}, "SAME", true, 47409408.}, + /* GFLOPS 0.189 x 1 = 0.189 */ {{1, 1}, {{1, 1152, 16, 16}}, 320, 1, {1, 1}, {1, 1}, {0, 0}, {0, 0}, "SAME", false, 188825600.}, + /* GFLOPS 0.185 x 1 = 0.185 */ {{1, 1}, {{1, 128, 75, 75}}, 128, 1, {1, 1}, {1, 1}, {0, 0}, {0, 0}, "", true, 185040000.}, + /* GFLOPS 0.180 x 1 = 0.180 */ {{1, 1}, {{1, 224, 56, 56}}, 128, 1, {1, 1}, {1, 1}, {0, 0}, {0, 0}, "", false, 180232192.}, + /* GFLOPS 0.045 x 4 = 0.179 */ {{1, 1}, {{1, 16, 184, 184}}, 40, 1, {1, 1}, {1, 1}, {0, 0}, {0, 0}, "", true, 44689920.}, + /* GFLOPS 0.089 x 2 = 0.177 */ {{1, 1}, {{1, 24, 112, 112}}, 144, 1, {1, 1}, {1, 1}, {0, 0}, {0, 0}, "", false, 88510464.}, + /* GFLOPS 0.088 x 2 = 0.177 */ {{1, 1}, {{1, 1024, 13, 13}}, 255, 1, {1, 1}, {1, 1}, {0, 0}, {0, 0}, "", true, 88301655.}, + /* GFLOPS 0.041 x 4 = 0.163 */ {{1, 1}, {{1, 480, 23, 23}}, 80, 1, {1, 1}, {1, 1}, {0, 0}, {0, 0}, "", true, 40669520.}, + /* GFLOPS 0.080 x 2 = 0.159 */ {{1, 1}, {{1, 336, 46, 46}}, 56, 1, {1, 1}, {1, 1}, {0, 0}, {0, 0}, "", true, 79747808.}, + /* GFLOPS 0.080 x 2 = 0.159 */ {{1, 1}, {{1, 40, 64, 64}}, 240, 1, {1, 1}, {1, 1}, {0, 0}, {0, 0}, "SAME", false, 79626240.}, + /* GFLOPS 0.079 x 2 = 0.159 */ {{1, 1}, {{1, 48, 160, 160}}, 32, 1, {1, 1}, {1, 1}, {0, 0}, {0, 0}, "", true, 79462400.}, + /* GFLOPS 0.079 x 2 = 0.158 */ {{1, 1}, {{1, 96, 80, 80}}, 64, 1, {1, 1}, {1, 1}, {0, 0}, {0, 0}, "", true, 79052800.}, + /* GFLOPS 0.079 x 2 = 0.157 */ {{1, 1}, {{1, 480, 32, 32}}, 80, 1, {1, 1}, {1, 1}, {0, 0}, {0, 0}, "SAME", false, 78725120.}, + /* GFLOPS 0.074 x 2 = 0.147 */ {{1, 1}, {{1, 8, 368, 368}}, 32, 1, {1, 1}, {1, 1}, {0, 0}, {0, 0}, "", true, 73670656.}, + /* GFLOPS 0.072 x 2 = 0.144 */ {{1, 1}, {{1, 1024, 10, 10}}, 352, 1, {1, 1}, {1, 1}, {0, 0}, {0, 0}, "SAME", true, 72124800.}, + /* GFLOPS 0.072 x 2 = 0.143 */ {{1, 1}, {{1, 1632, 7, 7}}, 448, 1, {1, 1}, {1, 1}, {0, 0}, {0, 0}, "", true, 71673280.}, + /* GFLOPS 0.140 x 1 = 0.140 */ {{1, 1}, {{1, 576, 38, 50}}, 64, 1, {1, 1}, {1, 1}, {0, 0}, {0, 0}, "SAME", true, 140204800.}, + /* GFLOPS 0.017 x 8 = 0.138 */ {{1, 1}, {{1, 16, 64, 128}}, 64, 1, {1, 1}, {1, 1}, {0, 0}, {0, 0}, "", false, 17301504.}, + /* GFLOPS 0.044 x 3 = 0.133 */ {{1, 1}, {{1, 512, 13, 13}}, 256, 1, {1, 1}, {1, 1}, {0, 0}, {0, 0}, "", false, 44345600.}, + /* GFLOPS 0.129 x 1 = 0.129 */ {{1, 1}, {{1, 160, 56, 56}}, 128, 1, {1, 1}, {1, 1}, {0, 0}, {0, 0}, "", false, 128851968.}, + /* GFLOPS 0.118 x 1 = 0.118 */ {{1, 1}, {{1, 320, 38, 38}}, 128, 1, {1, 1}, {1, 1}, {0, 0}, {0, 0}, "SAME", true, 118477312.}, + /* GFLOPS 0.039 x 3 = 0.118 */ {{1, 1}, {{1, 1024, 10, 10}}, 192, 1, {1, 1}, {1, 1}, {0, 0}, {0, 0}, "SAME", true, 39340800.}, + /* GFLOPS 0.017 x 7 = 0.118 */ {{1, 1}, {{1, 64, 64, 128}}, 16, 1, {1, 1}, {1, 1}, {0, 0}, {0, 0}, "", false, 16908288.}, + /* GFLOPS 0.019 x 6 = 0.115 */ {{1, 1}, {{1, 32, 96, 96}}, 32, 1, {1, 1}, {1, 1}, {0, 0}, {0, 0}, "", true, 19169280.}, + /* GFLOPS 0.114 x 1 = 0.114 */ {{1, 1}, {{1, 144, 128, 128}}, 24, 1, {1, 1}, {1, 1}, {0, 0}, {0, 0}, "SAME", false, 113639424.}, + /* GFLOPS 0.057 x 2 = 0.114 */ {{1, 1}, {{1, 240, 46, 46}}, 56, 1, {1, 1}, {1, 1}, {0, 0}, {0, 0}, "", true, 56996576.}, + /* GFLOPS 0.056 x 2 = 0.113 */ {{1, 1}, {{1, 448, 7, 7}}, 1280, 1, {1, 1}, {1, 1}, {0, 0}, {0, 0}, "", false, 56259840.}, + /* GFLOPS 0.112 x 1 = 0.112 */ {{1, 1}, {{1, 1024, 10, 10}}, 546, 1, {1, 1}, {1, 1}, {0, 0}, {0, 0}, "SAME", true, 111875400.}, + /* GFLOPS 0.110 x 1 = 0.110 */ {{1, 1}, {{1, 480, 32, 32}}, 112, 1, {1, 1}, {1, 1}, {0, 0}, {0, 0}, "SAME", false, 110215168.}, + /* GFLOPS 0.054 x 2 = 0.108 */ {{1, 1}, {{1, 16, 320, 320}}, 16, 1, {1, 1}, {1, 1}, {0, 0}, {0, 0}, "", true, 54067200.}, + /* GFLOPS 0.107 x 1 = 0.107 */ {{1, 1}, {{1, 64, 32, 32}}, 810, 1, {1, 1}, {1, 1}, {0, 0}, {0, 0}, "VALID", true, 106997760.}, + /* GFLOPS 0.036 x 3 = 0.107 */ {{1, 1}, {{1, 192, 38, 38}}, 64, 1, {1, 1}, {1, 1}, {0, 0}, {0, 0}, "SAME", true, 35580160.}, + /* GFLOPS 0.027 x 4 = 0.106 */ {{1, 1}, {{1, 32, 160, 160}}, 16, 1, {1, 1}, {1, 1}, {0, 0}, {0, 0}, "", true, 26624000.}, + /* GFLOPS 0.027 x 4 = 0.106 */ {{1, 1}, {{1, 24, 92, 92}}, 64, 1, {1, 1}, {1, 1}, {0, 0}, {0, 0}, "", true, 26543104.}, + /* GFLOPS 0.026 x 4 = 0.106 */ {{1, 1}, {{1, 64, 80, 80}}, 32, 1, {1, 1}, {1, 1}, {0, 0}, {0, 0}, "", true, 26419200.}, + /* GFLOPS 0.105 x 1 = 0.105 */ {{1, 1}, {{1, 256, 40, 40}}, 128, 1, {1, 1}, {1, 1}, {0, 0}, {0, 0}, "SAME", false, 105062400.}, + /* GFLOPS 0.105 x 1 = 0.105 */ {{1, 1}, {{1, 1024, 10, 10}}, 512, 1, {1, 1}, {1, 1}, {0, 0}, {0, 0}, "SAME", false, 104908800.}, + /* GFLOPS 0.052 x 2 = 0.105 */ {{1, 1}, {{1, 256, 20, 20}}, 255, 1, {1, 1}, {1, 1}, {0, 0}, {0, 0}, "", true, 52326000.}, + /* GFLOPS 0.026 x 4 = 0.105 */ {{1, 1}, {{1, 64, 92, 92}}, 24, 1, {1, 1}, {1, 1}, {0, 0}, {0, 0}, "", true, 26204544.}, + /* GFLOPS 0.052 x 2 = 0.104 */ {{1, 1}, {{1, 32, 112, 112}}, 64, 1, {1, 1}, {1, 1}, {0, 0}, {0, 0}, "", false, 52183040.}, + /* GFLOPS 0.051 x 2 = 0.103 */ {{1, 1}, {{1, 512, 7, 7}}, 1024, 1, {1, 1}, {1, 1}, {0, 0}, {0, 0}, "", false, 51430400.}, + /* GFLOPS 0.101 x 1 = 0.101 */ {{1, 1}, {{1, 512, 19, 19}}, 273, 1, {1, 1}, {1, 1}, {0, 0}, {0, 0}, "SAME", true, 101016825.}, + /* GFLOPS 0.008 x 12 = 0.101 */ {{1, 1}, {{1, 64, 32, 32}}, 64, 1, {1, 1}, {1, 1}, {0, 0}, {0, 0}, "VALID", true, 8454144.}, + /* GFLOPS 0.050 x 2 = 0.100 */ {{1, 1}, {{1, 24, 92, 92}}, 120, 1, {1, 1}, {1, 1}, {0, 0}, {0, 0}, "", true, 49768320.}, + /* GFLOPS 0.095 x 1 = 0.095 */ {{1, 1}, {{1, 128, 38, 38}}, 256, 1, {1, 1}, {1, 1}, {0, 0}, {0, 0}, "", true, 95003648.}, + /* GFLOPS 0.094 x 1 = 0.094 */ {{1, 1}, {{1, 32, 150, 150}}, 64, 1, {1, 1}, {1, 1}, {0, 0}, {0, 0}, "", true, 93600000.}, + /* GFLOPS 0.093 x 1 = 0.093 */ {{1, 1}, {{1, 512, 38, 50}}, 48, 1, {1, 1}, {1, 1}, {0, 0}, {0, 0}, "SAME", true, 93480000.}, + /* GFLOPS 0.093 x 1 = 0.093 */ {{1, 1}, {{1, 576, 19, 19}}, 224, 1, {1, 1}, {1, 1}, {0, 0}, {0, 0}, "SAME", true, 93236192.}, + /* GFLOPS 0.093 x 1 = 0.093 */ {{1, 1}, {{1, 64, 75, 75}}, 128, 1, {1, 1}, {1, 1}, {0, 0}, {0, 0}, "", true, 92880000.}, + /* GFLOPS 0.092 x 1 = 0.092 */ {{1, 1}, {{1, 192, 75, 100}}, 32, 1, {1, 1}, {1, 1}, {0, 0}, {0, 0}, "SAME", true, 92400000.}, + /* GFLOPS 0.031 x 3 = 0.092 */ {{1, 1}, {{1, 160, 10, 10}}, 960, 1, {1, 1}, {1, 1}, {0, 0}, {0, 0}, "SAME", false, 30816000.}, + /* GFLOPS 0.044 x 2 = 0.088 */ {{1, 1}, {{1, 40, 184, 184}}, 16, 1, {1, 1}, {1, 1}, {0, 0}, {0, 0}, "", true, 43877376.}, + /* GFLOPS 0.044 x 2 = 0.087 */ {{1, 1}, {{1, 272, 7, 7}}, 1632, 1, {1, 1}, {1, 1}, {0, 0}, {0, 0}, "", false, 43582560.}, + /* GFLOPS 0.042 x 2 = 0.084 */ {{1, 1}, {{1, 672, 14, 14}}, 160, 1, {1, 1}, {1, 1}, {0, 0}, {0, 0}, "", true, 42179200.}, + /* GFLOPS 0.082 x 1 = 0.082 */ {{1, 1}, {{1, 320, 10, 10}}, 1280, 1, {1, 1}, {1, 1}, {0, 0}, {0, 0}, "SAME", false, 82048000.}, + /* GFLOPS 0.041 x 2 = 0.082 */ {{1, 1}, {{1, 40, 46, 46}}, 240, 1, {1, 1}, {1, 1}, {0, 0}, {0, 0}, "", true, 41135040.}, + /* GFLOPS 0.040 x 2 = 0.080 */ {{1, 1}, {{1, 24, 92, 92}}, 96, 1, {1, 1}, {1, 1}, {0, 0}, {0, 0}, "", false, 39814656.}, + /* GFLOPS 0.013 x 6 = 0.080 */ {{1, 1}, {{1, 32, 80, 80}}, 32, 1, {1, 1}, {1, 1}, {0, 0}, {0, 0}, "", true, 13312000.}, + /* GFLOPS 0.079 x 1 = 0.079 */ {{1, 1}, {{1, 240, 64, 64}}, 40, 1, {1, 1}, {1, 1}, {0, 0}, {0, 0}, "SAME", false, 78807040.}, + /* GFLOPS 0.079 x 1 = 0.079 */ {{1, 1}, {{1, 384, 40, 40}}, 64, 1, {1, 1}, {1, 1}, {0, 0}, {0, 0}, "SAME", false, 78745600.}, + /* GFLOPS 0.040 x 2 = 0.079 */ {{1, 1}, {{1, 24, 75, 75}}, 144, 1, {1, 1}, {1, 1}, {0, 0}, {0, 0}, "SAME", false, 39690000.}, + /* GFLOPS 0.077 x 1 = 0.077 */ {{1, 1}, {{1, 96, 56, 56}}, 128, 1, {1, 1}, {1, 1}, {0, 0}, {0, 0}, "", false, 77471744.}, + /* GFLOPS 0.076 x 1 = 0.076 */ {{1, 1}, {{1, 96, 128, 128}}, 24, 1, {1, 1}, {1, 1}, {0, 0}, {0, 0}, "SAME", false, 75890688.}, + /* GFLOPS 0.038 x 2 = 0.076 */ {{1, 1}, {{1, 64, 48, 48}}, 128, 1, {1, 1}, {1, 1}, {0, 0}, {0, 0}, "SAME", true, 38043648.}, + /* GFLOPS 0.018 x 4 = 0.074 */ {{1, 1}, {{1, 8, 368, 368}}, 8, 1, {1, 1}, {1, 1}, {0, 0}, {0, 0}, "", true, 18417664.}, /* GFLOPS 0.071 x 1 = 0.071 */ {{1, 1}, {{1, 16, 150, 150}}, 96, 1, {1, 1}, {1, 1}, {0, 0}, {0, 0}, "SAME", false, 71280000.}, - /* GFLOPS 0.071 x 1 = 0.071 */ {{1, 1}, {{1, 352, 28, 28}}, 128, 1, {1, 1}, {1, 1}, {0, 0}, {0, 0}, "", false, 70748160.}, /* GFLOPS 0.071 x 1 = 0.071 */ {{1, 1}, {{1, 24, 150, 150}}, 64, 1, {1, 1}, {1, 1}, {0, 0}, {0, 0}, "VALID", true, 70560000.}, - /* GFLOPS 0.070 x 1 = 0.070 */ {{3, 3}, {{1, 96, 14, 14}}, 208, 1, {1, 1}, {1, 1}, {1, 1}, {0, 0}, "", true, 70487872.}, - /* GFLOPS 0.069 x 1 = 0.069 */ {{3, 3}, {{1, 96, 14, 14}}, 204, 1, {1, 1}, {1, 1}, {0, 0}, {0, 0}, "SAME", true, 69132336.}, /* GFLOPS 0.068 x 1 = 0.068 */ {{1, 1}, {{1, 32, 256, 256}}, 16, 1, {1, 1}, {1, 1}, {0, 0}, {0, 0}, "SAME", false, 68157440.}, - /* GFLOPS 0.005 x 14 = 0.066 */ {{3, 3}, {{1, 64, 8, 8}}, 64, 1, {1, 1}, {1, 1}, {0, 0}, {0, 0}, "SAME", false, 4722688.}, /* GFLOPS 0.066 x 1 = 0.066 */ {{1, 1}, {{1, 672, 16, 16}}, 192, 1, {1, 1}, {1, 1}, {0, 0}, {0, 0}, "SAME", false, 66109440.}, /* GFLOPS 0.066 x 1 = 0.066 */ {{1, 1}, {{1, 1280, 10, 10}}, 256, 1, {1, 1}, {1, 1}, {0, 0}, {0, 0}, "SAME", false, 65561600.}, - /* GFLOPS 0.033 x 2 = 0.065 */ {{3, 3}, {{1, 48, 14, 14}}, 192, 1, {1, 1}, {1, 1}, {1, 1}, {0, 0}, "", true, 32551680.}, - /* GFLOPS 0.065 x 1 = 0.065 */ {{3, 3}, {{1, 192, 7, 7}}, 384, 1, {1, 1}, {1, 1}, {1, 1}, {0, 0}, "", true, 65046912.}, - /* GFLOPS 0.065 x 1 = 0.065 */ {{3, 3}, {{1, 192, 7, 7}}, 384, 1, {1, 1}, {1, 1}, {0, 0}, {0, 0}, "SAME", true, 65046912.}, - /* GFLOPS 0.065 x 1 = 0.065 */ {{3, 3}, {{1, 160, 10, 10}}, 224, 1, {1, 1}, {1, 1}, {0, 0}, {0, 0}, "SAME", true, 64534400.}, - /* GFLOPS 0.064 x 1 = 0.064 */ {{1, 1}, {{1, 320, 28, 28}}, 128, 1, {1, 1}, {1, 1}, {0, 0}, {0, 0}, "", false, 64325632.}, - /* GFLOPS 0.032 x 2 = 0.064 */ {{3, 3}, {{1, 96, 12, 12}}, 128, 1, {1, 1}, {1, 1}, {1, 1}, {0, 0}, "", true, 31868928.}, + /* GFLOPS 0.033 x 2 = 0.066 */ {{1, 1}, {{1, 128, 40, 40}}, 80, 1, {1, 1}, {1, 1}, {0, 0}, {0, 0}, "", true, 32896000.}, + /* GFLOPS 0.016 x 4 = 0.066 */ {{1, 1}, {{1, 40, 46, 46}}, 96, 1, {1, 1}, {1, 1}, {0, 0}, {0, 0}, "", true, 16454016.}, + /* GFLOPS 0.016 x 4 = 0.065 */ {{1, 1}, {{1, 96, 46, 46}}, 40, 1, {1, 1}, {1, 1}, {0, 0}, {0, 0}, "", true, 16335520.}, /* GFLOPS 0.061 x 1 = 0.061 */ {{1, 1}, {{1, 960, 10, 10}}, 320, 1, {1, 1}, {1, 1}, {0, 0}, {0, 0}, "SAME", false, 61472000.}, - /* GFLOPS 0.031 x 2 = 0.061 */ {{1, 1}, {{1, 960, 10, 10}}, 160, 1, {1, 1}, {1, 1}, {0, 0}, {0, 0}, "SAME", false, 30736000.}, /* GFLOPS 0.061 x 1 = 0.061 */ {{1, 1}, {{1, 512, 46, 46}}, 28, 1, {1, 1}, {1, 1}, {0, 0}, {0, 0}, "", true, 60729200.}, - /* GFLOPS 0.060 x 1 = 0.060 */ {{3, 3}, {{1, 96, 38, 38}}, 96, 1, {2, 2}, {1, 1}, {0, 0}, {0, 0}, "SAME", true, 59920224.}, + /* GFLOPS 0.031 x 2 = 0.061 */ {{1, 1}, {{1, 960, 10, 10}}, 160, 1, {1, 1}, {1, 1}, {0, 0}, {0, 0}, "SAME", false, 30736000.}, /* GFLOPS 0.059 x 1 = 0.059 */ {{1, 1}, {{1, 320, 38, 38}}, 64, 1, {1, 1}, {1, 1}, {0, 0}, {0, 0}, "SAME", true, 59238656.}, - /* GFLOPS 0.059 x 1 = 0.059 */ {{3, 3}, {{1, 128, 19, 19}}, 256, 1, {2, 2}, {1, 1}, {1, 1}, {0, 0}, "", true, 59008000.}, - /* GFLOPS 0.059 x 1 = 0.059 */ {{3, 3}, {{1, 256, 10, 10}}, 512, 1, {2, 2}, {1, 1}, {1, 1}, {0, 0}, "", true, 58995200.}, - /* GFLOPS 0.059 x 1 = 0.059 */ {{3, 3}, {{1, 256, 10, 10}}, 512, 1, {2, 2}, {1, 1}, {0, 0}, {0, 0}, "SAME", true, 58995200.}, - /* GFLOPS 0.059 x 1 = 0.059 */ {{3, 3}, {{1, 256, 10, 10}}, 512, 1, {2, 2}, {1, 1}, {0, 0}, {0, 0}, "SAME", false, 58995200.}, - /* GFLOPS 0.058 x 1 = 0.058 */ {{1, 1}, {{1, 288, 28, 28}}, 128, 1, {1, 1}, {1, 1}, {0, 0}, {0, 0}, "", false, 57903104.}, - /* GFLOPS 0.004 x 16 = 0.058 */ {{3, 3}, {{1, 128, 7, 7}}, 32, 1, {1, 1}, {1, 1}, {1, 1}, {0, 0}, "", false, 3614240.}, - /* GFLOPS 0.055 x 1 = 0.055 */ {{3, 3}, {{1, 1280, 10, 10}}, 24, 1, {1, 1}, {1, 1}, {0, 0}, {0, 0}, "SAME", true, 55298400.}, + /* GFLOPS 0.007 x 8 = 0.059 */ {{1, 1}, {{1, 112, 7, 7}}, 672, 1, {1, 1}, {1, 1}, {0, 0}, {0, 0}, "", true, 7408800.}, + /* GFLOPS 0.010 x 6 = 0.058 */ {{1, 1}, {{1, 56, 16, 16}}, 336, 1, {1, 1}, {1, 1}, {0, 0}, {0, 0}, "", true, 9719808.}, + /* GFLOPS 0.010 x 6 = 0.058 */ {{1, 1}, {{1, 64, 14, 14}}, 384, 1, {1, 1}, {1, 1}, {0, 0}, {0, 0}, "", true, 9709056.}, + /* GFLOPS 0.028 x 2 = 0.057 */ {{1, 1}, {{1, 336, 23, 23}}, 80, 1, {1, 1}, {1, 1}, {0, 0}, {0, 0}, "", true, 28481360.}, + /* GFLOPS 0.007 x 8 = 0.057 */ {{1, 1}, {{1, 96, 8, 8}}, 576, 1, {1, 1}, {1, 1}, {0, 0}, {0, 0}, "", true, 7114752.}, + /* GFLOPS 0.027 x 2 = 0.054 */ {{1, 1}, {{1, 16, 160, 160}}, 32, 1, {1, 1}, {1, 1}, {0, 0}, {0, 0}, "", true, 27033600.}, /* GFLOPS 0.018 x 3 = 0.054 */ {{1, 1}, {{1, 32, 38, 38}}, 192, 1, {1, 1}, {1, 1}, {0, 0}, {0, 0}, "SAME", false, 18021120.}, - /* GFLOPS 0.018 x 3 = 0.053 */ {{1, 1}, {{1, 384, 19, 19}}, 64, 1, {1, 1}, {1, 1}, {0, 0}, {0, 0}, "SAME", false, 17766976.}, - /* GFLOPS 0.053 x 1 = 0.053 */ {{3, 3}, {{1, 128, 38, 38}}, 16, 1, {1, 1}, {1, 1}, {1, 1}, {0, 0}, "", true, 53254720.}, + /* GFLOPS 0.014 x 4 = 0.054 */ {{1, 1}, {{1, 16, 160, 160}}, 16, 1, {1, 1}, {1, 1}, {0, 0}, {0, 0}, "", true, 13516800.}, /* GFLOPS 0.053 x 1 = 0.053 */ {{1, 1}, {{1, 528, 14, 14}}, 256, 1, {1, 1}, {1, 1}, {0, 0}, {0, 0}, "", true, 53036032.}, - /* GFLOPS 0.053 x 1 = 0.053 */ {{1, 1}, {{1, 528, 14, 14}}, 256, 1, {1, 1}, {1, 1}, {0, 0}, {0, 0}, "SAME", true, 53036032.}, - /* GFLOPS 0.053 x 1 = 0.053 */ {{1, 1}, {{1, 64, 80, 80}}, 64, 1, {1, 1}, {1, 1}, {0, 0}, {0, 0}, "SAME", false, 52838400.}, /* GFLOPS 0.053 x 1 = 0.053 */ {{1, 1}, {{1, 64, 40, 40}}, 256, 1, {1, 1}, {1, 1}, {0, 0}, {0, 0}, "SAME", false, 52838400.}, /* GFLOPS 0.053 x 1 = 0.053 */ {{1, 1}, {{1, 128, 80, 80}}, 32, 1, {1, 1}, {1, 1}, {0, 0}, {0, 0}, "SAME", false, 52633600.}, /* GFLOPS 0.053 x 1 = 0.053 */ {{1, 1}, {{1, 128, 20, 20}}, 512, 1, {1, 1}, {1, 1}, {0, 0}, {0, 0}, "SAME", false, 52633600.}, /* GFLOPS 0.053 x 1 = 0.053 */ {{1, 1}, {{1, 256, 10, 10}}, 1024, 1, {1, 1}, {1, 1}, {0, 0}, {0, 0}, "SAME", false, 52531200.}, - /* GFLOPS 0.052 x 1 = 0.052 */ {{1, 1}, {{1, 1024, 10, 10}}, 256, 1, {1, 1}, {1, 1}, {0, 0}, {0, 0}, "", true, 52454400.}, - /* GFLOPS 0.052 x 1 = 0.052 */ {{1, 1}, {{1, 1024, 10, 10}}, 256, 1, {1, 1}, {1, 1}, {0, 0}, {0, 0}, "SAME", true, 52454400.}, - /* GFLOPS 0.052 x 1 = 0.052 */ {{1, 1}, {{1, 1024, 10, 10}}, 256, 1, {1, 1}, {1, 1}, {0, 0}, {0, 0}, "SAME", false, 52454400.}, + /* GFLOPS 0.026 x 2 = 0.053 */ {{1, 1}, {{1, 16, 112, 112}}, 64, 1, {1, 1}, {1, 1}, {0, 0}, {0, 0}, "", true, 26492928.}, + /* GFLOPS 0.013 x 4 = 0.053 */ {{1, 1}, {{1, 128, 20, 20}}, 128, 1, {1, 1}, {1, 1}, {0, 0}, {0, 0}, "", true, 13158400.}, /* GFLOPS 0.026 x 2 = 0.052 */ {{1, 1}, {{1, 1024, 10, 10}}, 128, 1, {1, 1}, {1, 1}, {0, 0}, {0, 0}, "SAME", true, 26227200.}, - /* GFLOPS 0.052 x 1 = 0.052 */ {{1, 1}, {{1, 64, 56, 56}}, 128, 1, {1, 1}, {1, 1}, {0, 0}, {0, 0}, "", false, 51781632.}, + /* GFLOPS 0.013 x 4 = 0.052 */ {{1, 1}, {{1, 16, 64, 64}}, 96, 1, {1, 1}, {1, 1}, {0, 0}, {0, 0}, "", true, 12976128.}, /* GFLOPS 0.051 x 1 = 0.051 */ {{1, 1}, {{1, 256, 56, 56}}, 128, 1, {2, 2}, {1, 1}, {0, 0}, {0, 0}, "", false, 51480576.}, - /* GFLOPS 0.051 x 1 = 0.051 */ {{1, 1}, {{1, 256, 28, 28}}, 128, 1, {1, 1}, {1, 1}, {0, 0}, {0, 0}, "", false, 51480576.}, /* GFLOPS 0.051 x 1 = 0.051 */ {{1, 1}, {{1, 512, 28, 28}}, 256, 1, {2, 2}, {1, 1}, {0, 0}, {0, 0}, "", false, 51430400.}, - /* GFLOPS 0.026 x 2 = 0.051 */ {{1, 1}, {{1, 512, 14, 14}}, 128, 1, {1, 1}, {1, 1}, {0, 0}, {0, 0}, "", true, 25715200.}, - /* GFLOPS 0.026 x 2 = 0.051 */ {{1, 1}, {{1, 512, 14, 14}}, 128, 1, {1, 1}, {1, 1}, {0, 0}, {0, 0}, "SAME", true, 25715200.}, - /* GFLOPS 0.013 x 4 = 0.051 */ {{1, 1}, {{1, 512, 14, 14}}, 64, 1, {1, 1}, {1, 1}, {0, 0}, {0, 0}, "", true, 12857600.}, /* GFLOPS 0.051 x 1 = 0.051 */ {{1, 1}, {{1, 1024, 14, 14}}, 512, 1, {2, 2}, {1, 1}, {0, 0}, {0, 0}, "", false, 51405312.}, - /* GFLOPS 0.050 x 1 = 0.050 */ {{1, 1}, {{1, 992, 14, 14}}, 128, 1, {1, 1}, {1, 1}, {0, 0}, {0, 0}, "", false, 49799680.}, - /* GFLOPS 0.048 x 1 = 0.048 */ {{1, 1}, {{1, 960, 14, 14}}, 128, 1, {1, 1}, {1, 1}, {0, 0}, {0, 0}, "", false, 48194048.}, - /* GFLOPS 0.047 x 1 = 0.047 */ {{1, 1}, {{1, 256, 19, 19}}, 256, 1, {1, 1}, {1, 1}, {0, 0}, {0, 0}, "", false, 47409408.}, + /* GFLOPS 0.026 x 2 = 0.051 */ {{1, 1}, {{1, 960, 7, 7}}, 272, 1, {1, 1}, {1, 1}, {0, 0}, {0, 0}, "", true, 25603088.}, /* GFLOPS 0.047 x 1 = 0.047 */ {{1, 1}, {{1, 144, 64, 64}}, 40, 1, {1, 1}, {1, 1}, {0, 0}, {0, 0}, "SAME", false, 47349760.}, /* GFLOPS 0.047 x 1 = 0.047 */ {{1, 1}, {{1, 512, 38, 50}}, 24, 1, {1, 1}, {1, 1}, {0, 0}, {0, 0}, "SAME", true, 46740000.}, - /* GFLOPS 0.047 x 1 = 0.047 */ {{1, 1}, {{1, 928, 14, 14}}, 128, 1, {1, 1}, {1, 1}, {0, 0}, {0, 0}, "", false, 46588416.}, - /* GFLOPS 0.046 x 1 = 0.046 */ {{1, 1}, {{1, 64, 75, 75}}, 64, 1, {1, 1}, {1, 1}, {0, 0}, {0, 0}, "SAME", true, 46440000.}, - /* GFLOPS 0.023 x 2 = 0.045 */ {{3, 3}, {{1, 256, 3, 3}}, 546, 1, {1, 1}, {1, 1}, {0, 0}, {0, 0}, "SAME", true, 22648626.}, - /* GFLOPS 0.045 x 1 = 0.045 */ {{3, 3}, {{1, 160, 7, 7}}, 320, 1, {1, 1}, {1, 1}, {1, 1}, {0, 0}, "", true, 45174080.}, - /* GFLOPS 0.045 x 1 = 0.045 */ {{3, 3}, {{1, 160, 7, 7}}, 320, 1, {1, 1}, {1, 1}, {0, 0}, {0, 0}, "SAME", true, 45174080.}, + /* GFLOPS 0.023 x 2 = 0.046 */ {{1, 1}, {{1, 56, 46, 46}}, 96, 1, {1, 1}, {1, 1}, {0, 0}, {0, 0}, "", false, 22954368.}, /* GFLOPS 0.045 x 1 = 0.045 */ {{1, 1}, {{1, 224, 28, 28}}, 128, 1, {1, 1}, {1, 1}, {0, 0}, {0, 0}, "", false, 45058048.}, - /* GFLOPS 0.023 x 2 = 0.045 */ {{1, 1}, {{1, 512, 14, 14}}, 112, 1, {1, 1}, {1, 1}, {0, 0}, {0, 0}, "", true, 22500800.}, - /* GFLOPS 0.045 x 1 = 0.045 */ {{1, 1}, {{1, 896, 14, 14}}, 128, 1, {1, 1}, {1, 1}, {0, 0}, {0, 0}, "", false, 44982784.}, - /* GFLOPS 0.045 x 1 = 0.045 */ {{3, 3}, {{1, 3, 227, 227}}, 64, 1, {2, 2}, {1, 1}, {0, 0}, {0, 0}, "", true, 44946880.}, - /* GFLOPS 0.044 x 1 = 0.044 */ {{3, 3}, {{1, 128, 19, 19}}, 192, 1, {2, 2}, {1, 1}, {0, 0}, {0, 0}, "SAME", true, 44256000.}, - /* GFLOPS 0.044 x 1 = 0.044 */ {{3, 3}, {{1, 1024, 10, 10}}, 24, 1, {1, 1}, {1, 1}, {0, 0}, {0, 0}, "SAME", true, 44239200.}, /* GFLOPS 0.044 x 1 = 0.044 */ {{1, 1}, {{1, 512, 13, 13}}, 255, 1, {1, 1}, {1, 1}, {0, 0}, {0, 0}, "", true, 44172375.}, - /* GFLOPS 0.043 x 1 = 0.043 */ {{7, 7}, {{1, 3, 96, 96}}, 64, 1, {2, 2}, {1, 1}, {3, 3}, {0, 0}, "", true, 43499520.}, - /* GFLOPS 0.043 x 1 = 0.043 */ {{1, 1}, {{1, 864, 14, 14}}, 128, 1, {1, 1}, {1, 1}, {0, 0}, {0, 0}, "", false, 43377152.}, - /* GFLOPS 0.042 x 1 = 0.042 */ {{1, 1}, {{1, 832, 14, 14}}, 128, 1, {1, 1}, {1, 1}, {0, 0}, {0, 0}, "", false, 41771520.}, - /* GFLOPS 0.040 x 1 = 0.040 */ {{5, 5}, {{1, 32, 14, 14}}, 128, 1, {1, 1}, {1, 1}, {2, 2}, {0, 0}, "", true, 40165888.}, - /* GFLOPS 0.040 x 1 = 0.040 */ {{5, 5}, {{1, 32, 14, 14}}, 128, 1, {1, 1}, {1, 1}, {0, 0}, {0, 0}, "SAME", true, 40165888.}, - /* GFLOPS 0.040 x 1 = 0.040 */ {{1, 1}, {{1, 800, 14, 14}}, 128, 1, {1, 1}, {1, 1}, {0, 0}, {0, 0}, "", false, 40165888.}, - /* GFLOPS 0.040 x 1 = 0.040 */ {{3, 3}, {{1, 64, 19, 19}}, 96, 1, {1, 1}, {1, 1}, {0, 0}, {0, 0}, "SAME", true, 39958368.}, - /* GFLOPS 0.040 x 1 = 0.040 */ {{3, 3}, {{1, 256, 19, 19}}, 24, 1, {1, 1}, {1, 1}, {1, 1}, {0, 0}, "", true, 39932376.}, - /* GFLOPS 0.040 x 1 = 0.040 */ {{3, 3}, {{1, 3, 300, 300}}, 32, 1, {2, 2}, {1, 1}, {1, 1}, {0, 0}, "", true, 39600000.}, + /* GFLOPS 0.007 x 6 = 0.044 */ {{1, 1}, {{1, 672, 7, 7}}, 112, 1, {1, 1}, {1, 1}, {0, 0}, {0, 0}, "", true, 7381360.}, + /* GFLOPS 0.007 x 6 = 0.043 */ {{1, 1}, {{1, 576, 8, 8}}, 96, 1, {1, 1}, {1, 1}, {0, 0}, {0, 0}, "", true, 7084032.}, + /* GFLOPS 0.020 x 2 = 0.041 */ {{1, 1}, {{1, 120, 46, 46}}, 40, 1, {1, 1}, {1, 1}, {0, 0}, {0, 0}, "", true, 20398240.}, + /* GFLOPS 0.010 x 4 = 0.040 */ {{1, 1}, {{1, 16, 56, 56}}, 96, 1, {1, 1}, {1, 1}, {0, 0}, {0, 0}, "", true, 9934848.}, /* GFLOPS 0.039 x 1 = 0.039 */ {{1, 1}, {{1, 240, 32, 32}}, 80, 1, {1, 1}, {1, 1}, {0, 0}, {0, 0}, "SAME", false, 39403520.}, /* GFLOPS 0.039 x 1 = 0.039 */ {{1, 1}, {{1, 144, 75, 75}}, 24, 1, {1, 1}, {1, 1}, {0, 0}, {0, 0}, "SAME", false, 39015000.}, /* GFLOPS 0.039 x 1 = 0.039 */ {{1, 1}, {{1, 192, 28, 28}}, 128, 1, {1, 1}, {1, 1}, {0, 0}, {0, 0}, "", false, 38635520.}, - /* GFLOPS 0.039 x 1 = 0.039 */ {{1, 1}, {{1, 768, 14, 14}}, 128, 1, {1, 1}, {1, 1}, {0, 0}, {0, 0}, "", false, 38560256.}, - /* GFLOPS 0.037 x 1 = 0.037 */ {{1, 1}, {{1, 736, 14, 14}}, 128, 1, {1, 1}, {1, 1}, {0, 0}, {0, 0}, "", false, 36954624.}, + /* GFLOPS 0.020 x 2 = 0.039 */ {{1, 1}, {{1, 32, 112, 112}}, 24, 1, {1, 1}, {1, 1}, {0, 0}, {0, 0}, "", true, 19568640.}, + /* GFLOPS 0.010 x 4 = 0.039 */ {{1, 1}, {{1, 336, 16, 16}}, 56, 1, {1, 1}, {1, 1}, {0, 0}, {0, 0}, "", true, 9648128.}, + /* GFLOPS 0.019 x 2 = 0.038 */ {{1, 1}, {{1, 32, 48, 48}}, 128, 1, {1, 1}, {1, 1}, {0, 0}, {0, 0}, "SAME", true, 19169280.}, + /* GFLOPS 0.005 x 8 = 0.038 */ {{1, 1}, {{1, 256, 6, 6}}, 256, 1, {1, 1}, {1, 1}, {0, 0}, {0, 0}, "", true, 4727808.}, /* GFLOPS 0.036 x 1 = 0.036 */ {{1, 1}, {{1, 480, 14, 14}}, 192, 1, {1, 1}, {1, 1}, {0, 0}, {0, 0}, "", true, 36164352.}, - /* GFLOPS 0.036 x 1 = 0.036 */ {{1, 1}, {{1, 480, 14, 14}}, 192, 1, {1, 1}, {1, 1}, {0, 0}, {0, 0}, "SAME", true, 36164352.}, - /* GFLOPS 0.018 x 2 = 0.036 */ {{1, 1}, {{1, 192, 38, 38}}, 32, 1, {1, 1}, {1, 1}, {0, 0}, {0, 0}, "SAME", false, 17790080.}, - /* GFLOPS 0.035 x 1 = 0.035 */ {{1, 1}, {{1, 704, 14, 14}}, 128, 1, {1, 1}, {1, 1}, {0, 0}, {0, 0}, "", false, 35348992.}, + /* GFLOPS 0.018 x 2 = 0.036 */ {{1, 1}, {{1, 40, 46, 46}}, 104, 1, {1, 1}, {1, 1}, {0, 0}, {0, 0}, "", true, 17825184.}, + /* GFLOPS 0.009 x 4 = 0.036 */ {{1, 1}, {{1, 8, 256, 256}}, 8, 1, {1, 1}, {1, 1}, {0, 0}, {0, 0}, "", true, 8912896.}, /* GFLOPS 0.035 x 1 = 0.035 */ {{1, 1}, {{1, 512, 46, 46}}, 16, 1, {1, 1}, {1, 1}, {0, 0}, {0, 0}, "", true, 34702400.}, - /* GFLOPS 0.034 x 1 = 0.034 */ {{1, 1}, {{1, 672, 14, 14}}, 128, 1, {1, 1}, {1, 1}, {0, 0}, {0, 0}, "", false, 33743360.}, + /* GFLOPS 0.018 x 2 = 0.035 */ {{1, 1}, {{1, 104, 46, 46}}, 40, 1, {1, 1}, {1, 1}, {0, 0}, {0, 0}, "", true, 17689760.}, /* GFLOPS 0.034 x 1 = 0.034 */ {{1, 1}, {{1, 128, 32, 64}}, 64, 1, {1, 1}, {1, 1}, {0, 0}, {0, 0}, "", false, 33685504.}, - /* GFLOPS 0.034 x 1 = 0.034 */ {{2, 2}, {{1, 64, 64, 128}}, 32, 1, {2, 2}, {1, 1}, {0, 0}, {0, 0}, "", false, 33619968.}, - /* GFLOPS 0.033 x 1 = 0.033 */ {{3, 3}, {{1, 256, 3, 3}}, 804, 1, {1, 1}, {1, 1}, {1, 1}, {0, 0}, "", true, 33350724.}, + /* GFLOPS 0.017 x 2 = 0.034 */ {{1, 1}, {{1, 192, 28, 28}}, 56, 1, {1, 1}, {1, 1}, {0, 0}, {0, 0}, "", true, 16903040.}, /* GFLOPS 0.033 x 1 = 0.033 */ {{1, 1}, {{1, 528, 14, 14}}, 160, 1, {1, 1}, {1, 1}, {0, 0}, {0, 0}, "", true, 33147520.}, - /* GFLOPS 0.033 x 1 = 0.033 */ {{1, 1}, {{1, 528, 14, 14}}, 160, 1, {1, 1}, {1, 1}, {0, 0}, {0, 0}, "SAME", true, 33147520.}, /* GFLOPS 0.033 x 1 = 0.033 */ {{1, 1}, {{1, 1024, 10, 10}}, 160, 1, {1, 1}, {1, 1}, {0, 0}, {0, 0}, "SAME", true, 32784000.}, + /* GFLOPS 0.016 x 2 = 0.033 */ {{1, 1}, {{1, 40, 92, 92}}, 24, 1, {1, 1}, {1, 1}, {0, 0}, {0, 0}, "", true, 16454016.}, + /* GFLOPS 0.005 x 6 = 0.033 */ {{1, 1}, {{1, 48, 14, 14}}, 288, 1, {1, 1}, {1, 1}, {0, 0}, {0, 0}, "", true, 5475456.}, /* GFLOPS 0.032 x 1 = 0.032 */ {{1, 1}, {{1, 160, 28, 28}}, 128, 1, {1, 1}, {1, 1}, {0, 0}, {0, 0}, "", false, 32212992.}, /* GFLOPS 0.032 x 1 = 0.032 */ {{1, 1}, {{1, 512, 14, 14}}, 160, 1, {1, 1}, {1, 1}, {0, 0}, {0, 0}, "", true, 32144000.}, - /* GFLOPS 0.032 x 1 = 0.032 */ {{1, 1}, {{1, 640, 14, 14}}, 128, 1, {1, 1}, {1, 1}, {0, 0}, {0, 0}, "", false, 32137728.}, /* GFLOPS 0.032 x 1 = 0.032 */ {{1, 1}, {{1, 508, 14, 14}}, 160, 1, {1, 1}, {1, 1}, {0, 0}, {0, 0}, "SAME", true, 31893120.}, /* GFLOPS 0.011 x 3 = 0.032 */ {{1, 1}, {{1, 320, 16, 16}}, 64, 1, {1, 1}, {1, 1}, {0, 0}, {0, 0}, "SAME", true, 10502144.}, /* GFLOPS 0.031 x 1 = 0.031 */ {{1, 1}, {{1, 832, 7, 7}}, 384, 1, {1, 1}, {1, 1}, {0, 0}, {0, 0}, "", true, 31328640.}, - /* GFLOPS 0.031 x 1 = 0.031 */ {{1, 1}, {{1, 832, 7, 7}}, 384, 1, {1, 1}, {1, 1}, {0, 0}, {0, 0}, "SAME", true, 31328640.}, - /* GFLOPS 0.031 x 1 = 0.031 */ {{1, 1}, {{1, 608, 14, 14}}, 128, 1, {1, 1}, {1, 1}, {0, 0}, {0, 0}, "", false, 30532096.}, /* GFLOPS 0.015 x 2 = 0.030 */ {{1, 1}, {{1, 128, 46, 46}}, 28, 1, {1, 1}, {1, 1}, {0, 0}, {0, 0}, "", true, 15226736.}, - /* GFLOPS 0.015 x 2 = 0.030 */ {{5, 5}, {{1, 24, 14, 14}}, 64, 1, {1, 1}, {1, 1}, {2, 2}, {0, 0}, "", true, 15065344.}, - /* GFLOPS 0.015 x 2 = 0.030 */ {{5, 5}, {{1, 24, 14, 14}}, 64, 1, {1, 1}, {1, 1}, {0, 0}, {0, 0}, "SAME", true, 15065344.}, - /* GFLOPS 0.015 x 2 = 0.030 */ {{5, 5}, {{1, 48, 7, 7}}, 128, 1, {1, 1}, {1, 1}, {0, 0}, {0, 0}, "SAME", true, 15059072.}, - /* GFLOPS 0.029 x 1 = 0.029 */ {{3, 3}, {{1, 256, 10, 10}}, 256, 1, {2, 2}, {1, 1}, {0, 0}, {0, 0}, "SAME", false, 29497600.}, - /* GFLOPS 0.015 x 2 = 0.029 */ {{1, 1}, {{1, 112, 32, 32}}, 64, 1, {1, 1}, {1, 1}, {0, 0}, {0, 0}, "SAME", true, 14745600.}, - /* GFLOPS 0.029 x 1 = 0.029 */ {{1, 1}, {{1, 192, 28, 28}}, 96, 1, {1, 1}, {1, 1}, {0, 0}, {0, 0}, "", true, 28976640.}, - /* GFLOPS 0.029 x 1 = 0.029 */ {{1, 1}, {{1, 192, 28, 28}}, 96, 1, {1, 1}, {1, 1}, {0, 0}, {0, 0}, "SAME", true, 28976640.}, + /* GFLOPS 0.015 x 2 = 0.030 */ {{1, 1}, {{1, 336, 14, 14}}, 112, 1, {1, 1}, {1, 1}, {0, 0}, {0, 0}, "", true, 14773696.}, + /* GFLOPS 0.005 x 6 = 0.030 */ {{1, 1}, {{1, 40, 16, 16}}, 240, 1, {1, 1}, {1, 1}, {0, 0}, {0, 0}, "", true, 4976640.}, /* GFLOPS 0.029 x 1 = 0.029 */ {{1, 1}, {{1, 512, 14, 14}}, 144, 1, {1, 1}, {1, 1}, {0, 0}, {0, 0}, "", true, 28929600.}, - /* GFLOPS 0.029 x 1 = 0.029 */ {{1, 1}, {{1, 512, 14, 14}}, 144, 1, {1, 1}, {1, 1}, {0, 0}, {0, 0}, "SAME", true, 28929600.}, - /* GFLOPS 0.029 x 1 = 0.029 */ {{1, 1}, {{1, 576, 14, 14}}, 128, 1, {1, 1}, {1, 1}, {0, 0}, {0, 0}, "", false, 28926464.}, - /* GFLOPS 0.027 x 1 = 0.027 */ {{1, 1}, {{1, 544, 14, 14}}, 128, 1, {1, 1}, {1, 1}, {0, 0}, {0, 0}, "", false, 27320832.}, - /* GFLOPS 0.027 x 1 = 0.027 */ {{1, 1}, {{1, 64, 16, 16}}, 810, 1, {1, 1}, {1, 1}, {0, 0}, {0, 0}, "VALID", true, 26749440.}, + /* GFLOPS 0.015 x 2 = 0.029 */ {{1, 1}, {{1, 112, 32, 32}}, 64, 1, {1, 1}, {1, 1}, {0, 0}, {0, 0}, "SAME", true, 14745600.}, + /* GFLOPS 0.007 x 4 = 0.029 */ {{1, 1}, {{1, 24, 32, 32}}, 144, 1, {1, 1}, {1, 1}, {0, 0}, {0, 0}, "", true, 7225344.}, + /* GFLOPS 0.014 x 2 = 0.028 */ {{1, 1}, {{1, 576, 8, 8}}, 192, 1, {1, 1}, {1, 1}, {0, 0}, {0, 0}, "", true, 14168064.}, /* GFLOPS 0.027 x 1 = 0.027 */ {{1, 1}, {{1, 384, 19, 19}}, 96, 1, {1, 1}, {1, 1}, {0, 0}, {0, 0}, "SAME", false, 26650464.}, /* GFLOPS 0.027 x 1 = 0.027 */ {{1, 1}, {{1, 576, 19, 19}}, 64, 1, {1, 1}, {1, 1}, {0, 0}, {0, 0}, "SAME", true, 26638912.}, - /* GFLOPS 0.027 x 1 = 0.027 */ {{3, 3}, {{1, 128, 38, 38}}, 8, 1, {1, 1}, {1, 1}, {1, 1}, {0, 0}, "", true, 26627360.}, - /* GFLOPS 0.027 x 1 = 0.027 */ {{1, 1}, {{1, 528, 14, 14}}, 128, 1, {1, 1}, {1, 1}, {0, 0}, {0, 0}, "", true, 26518016.}, - /* GFLOPS 0.027 x 1 = 0.027 */ {{1, 1}, {{1, 528, 14, 14}}, 128, 1, {1, 1}, {1, 1}, {0, 0}, {0, 0}, "SAME", true, 26518016.}, - /* GFLOPS 0.009 x 3 = 0.026 */ {{1, 1}, {{1, 128, 46, 46}}, 16, 1, {1, 1}, {1, 1}, {0, 0}, {0, 0}, "", true, 8700992.}, /* GFLOPS 0.026 x 1 = 0.026 */ {{1, 1}, {{1, 96, 75, 75}}, 24, 1, {1, 1}, {1, 1}, {0, 0}, {0, 0}, "SAME", false, 26055000.}, - /* GFLOPS 0.026 x 1 = 0.026 */ {{1, 1}, {{1, 64, 56, 56}}, 64, 1, {1, 1}, {1, 1}, {0, 0}, {0, 0}, "VALID", true, 25890816.}, - /* GFLOPS 0.026 x 1 = 0.026 */ {{1, 1}, {{1, 64, 56, 56}}, 64, 1, {1, 1}, {1, 1}, {0, 0}, {0, 0}, "", true, 25890816.}, - /* GFLOPS 0.026 x 1 = 0.026 */ {{1, 1}, {{1, 64, 56, 56}}, 64, 1, {1, 1}, {1, 1}, {0, 0}, {0, 0}, "", false, 25890816.}, /* GFLOPS 0.026 x 1 = 0.026 */ {{1, 1}, {{1, 1024, 10, 10}}, 126, 1, {1, 1}, {1, 1}, {0, 0}, {0, 0}, "", true, 25817400.}, - /* GFLOPS 0.026 x 1 = 0.026 */ {{1, 1}, {{1, 128, 28, 28}}, 128, 1, {1, 1}, {1, 1}, {0, 0}, {0, 0}, "", false, 25790464.}, - /* GFLOPS 0.026 x 1 = 0.026 */ {{1, 1}, {{1, 256, 28, 28}}, 64, 1, {1, 1}, {1, 1}, {0, 0}, {0, 0}, "", true, 25740288.}, - /* GFLOPS 0.026 x 1 = 0.026 */ {{1, 1}, {{1, 256, 28, 28}}, 64, 1, {1, 1}, {1, 1}, {0, 0}, {0, 0}, "SAME", true, 25740288.}, - /* GFLOPS 0.013 x 2 = 0.026 */ {{1, 1}, {{1, 256, 28, 28}}, 32, 1, {1, 1}, {1, 1}, {0, 0}, {0, 0}, "", true, 12870144.}, - /* GFLOPS 0.026 x 1 = 0.026 */ {{1, 1}, {{1, 512, 14, 14}}, 128, 1, {1, 1}, {1, 1}, {0, 0}, {0, 0}, "", false, 25715200.}, /* GFLOPS 0.013 x 2 = 0.026 */ {{1, 1}, {{1, 512, 14, 14}}, 64, 1, {1, 1}, {1, 1}, {0, 0}, {0, 0}, "SAME", true, 12857600.}, - /* GFLOPS 0.002 x 12 = 0.025 */ {{1, 1}, {{1, 64, 16, 16}}, 64, 1, {1, 1}, {1, 1}, {0, 0}, {0, 0}, "VALID", true, 2113536.}, + /* GFLOPS 0.009 x 3 = 0.026 */ {{1, 1}, {{1, 128, 46, 46}}, 16, 1, {1, 1}, {1, 1}, {0, 0}, {0, 0}, "", true, 8700992.}, + /* GFLOPS 0.013 x 2 = 0.025 */ {{1, 1}, {{1, 96, 64, 64}}, 16, 1, {1, 1}, {1, 1}, {0, 0}, {0, 0}, "", true, 12648448.}, /* GFLOPS 0.024 x 1 = 0.024 */ {{1, 1}, {{1, 480, 14, 14}}, 128, 1, {1, 1}, {1, 1}, {0, 0}, {0, 0}, "", false, 24109568.}, /* GFLOPS 0.024 x 1 = 0.024 */ {{1, 1}, {{1, 128, 38, 38}}, 256, 1, {2, 2}, {1, 1}, {0, 0}, {0, 0}, "", false, 23750912.}, - /* GFLOPS 0.024 x 1 = 0.024 */ {{1, 1}, {{1, 256, 19, 19}}, 128, 1, {1, 1}, {1, 1}, {0, 0}, {0, 0}, "", true, 23704704.}, - /* GFLOPS 0.023 x 1 = 0.023 */ {{3, 3}, {{1, 3, 256, 512}}, 13, 1, {2, 2}, {1, 1}, {1, 1}, {0, 0}, "", true, 23429120.}, /* GFLOPS 0.023 x 1 = 0.023 */ {{1, 1}, {{1, 32, 150, 150}}, 16, 1, {1, 1}, {1, 1}, {0, 0}, {0, 0}, "SAME", false, 23400000.}, /* GFLOPS 0.023 x 1 = 0.023 */ {{1, 1}, {{1, 512, 19, 19}}, 63, 1, {1, 1}, {1, 1}, {0, 0}, {0, 0}, "", true, 23311575.}, /* GFLOPS 0.023 x 1 = 0.023 */ {{1, 1}, {{1, 448, 14, 14}}, 128, 1, {1, 1}, {1, 1}, {0, 0}, {0, 0}, "", false, 22503936.}, /* GFLOPS 0.023 x 1 = 0.023 */ {{1, 1}, {{1, 512, 14, 14}}, 112, 1, {1, 1}, {1, 1}, {0, 0}, {0, 0}, "SAME", true, 22500800.}, /* GFLOPS 0.022 x 1 = 0.022 */ {{1, 1}, {{1, 508, 14, 14}}, 112, 1, {1, 1}, {1, 1}, {0, 0}, {0, 0}, "SAME", true, 22325184.}, - /* GFLOPS 0.022 x 1 = 0.022 */ {{3, 3}, {{1, 512, 10, 10}}, 24, 1, {1, 1}, {1, 1}, {1, 1}, {0, 0}, "", true, 22120800.}, - /* GFLOPS 0.021 x 1 = 0.021 */ {{3, 3}, {{1, 128, 12, 12}}, 256, 1, {2, 2}, {1, 1}, {1, 1}, {0, 0}, "", true, 21242880.}, + /* GFLOPS 0.006 x 4 = 0.022 */ {{1, 1}, {{1, 24, 28, 28}}, 144, 1, {1, 1}, {1, 1}, {0, 0}, {0, 0}, "", true, 5531904.}, + /* GFLOPS 0.005 x 4 = 0.022 */ {{1, 1}, {{1, 288, 14, 14}}, 48, 1, {1, 1}, {1, 1}, {0, 0}, {0, 0}, "", true, 5428416.}, /* GFLOPS 0.021 x 1 = 0.021 */ {{1, 1}, {{1, 40, 64, 64}}, 64, 1, {1, 1}, {1, 1}, {0, 0}, {0, 0}, "SAME", true, 21233664.}, /* GFLOPS 0.021 x 1 = 0.021 */ {{1, 1}, {{1, 416, 14, 14}}, 128, 1, {1, 1}, {1, 1}, {0, 0}, {0, 0}, "", false, 20898304.}, /* GFLOPS 0.021 x 1 = 0.021 */ {{1, 1}, {{1, 832, 7, 7}}, 256, 1, {1, 1}, {1, 1}, {0, 0}, {0, 0}, "", true, 20885760.}, - /* GFLOPS 0.021 x 1 = 0.021 */ {{1, 1}, {{1, 832, 7, 7}}, 256, 1, {1, 1}, {1, 1}, {0, 0}, {0, 0}, "SAME", true, 20885760.}, - /* GFLOPS 0.010 x 2 = 0.021 */ {{1, 1}, {{1, 832, 7, 7}}, 128, 1, {1, 1}, {1, 1}, {0, 0}, {0, 0}, "", true, 10442880.}, - /* GFLOPS 0.010 x 2 = 0.021 */ {{1, 1}, {{1, 832, 7, 7}}, 128, 1, {1, 1}, {1, 1}, {0, 0}, {0, 0}, "SAME", true, 10442880.}, - /* GFLOPS 0.010 x 2 = 0.020 */ {{3, 3}, {{1, 256, 2, 2}}, 546, 1, {1, 1}, {1, 1}, {0, 0}, {0, 0}, "SAME", true, 10066056.}, - /* GFLOPS 0.020 x 1 = 0.020 */ {{5, 5}, {{1, 16, 28, 28}}, 32, 1, {1, 1}, {1, 1}, {2, 2}, {0, 0}, "", true, 20095488.}, - /* GFLOPS 0.020 x 1 = 0.020 */ {{5, 5}, {{1, 16, 28, 28}}, 32, 1, {1, 1}, {1, 1}, {0, 0}, {0, 0}, "SAME", true, 20095488.}, - /* GFLOPS 0.020 x 1 = 0.020 */ {{5, 5}, {{1, 32, 14, 14}}, 64, 1, {1, 1}, {1, 1}, {2, 2}, {0, 0}, "", true, 20082944.}, - /* GFLOPS 0.020 x 1 = 0.020 */ {{5, 5}, {{1, 32, 14, 14}}, 64, 1, {1, 1}, {1, 1}, {0, 0}, {0, 0}, "SAME", true, 20082944.}, - /* GFLOPS 0.020 x 1 = 0.020 */ {{3, 3}, {{1, 256, 19, 19}}, 12, 1, {1, 1}, {1, 1}, {1, 1}, {0, 0}, "", true, 19966188.}, - /* GFLOPS 0.019 x 1 = 0.019 */ {{1, 1}, {{1, 192, 28, 28}}, 64, 1, {1, 1}, {1, 1}, {0, 0}, {0, 0}, "", true, 19317760.}, - /* GFLOPS 0.019 x 1 = 0.019 */ {{1, 1}, {{1, 192, 28, 28}}, 64, 1, {1, 1}, {1, 1}, {0, 0}, {0, 0}, "SAME", true, 19317760.}, + /* GFLOPS 0.010 x 2 = 0.021 */ {{1, 1}, {{1, 32, 64, 64}}, 39, 1, {1, 1}, {1, 1}, {0, 0}, {0, 0}, "", true, 10383360.}, + /* GFLOPS 0.010 x 2 = 0.020 */ {{1, 1}, {{1, 24, 112, 112}}, 16, 1, {1, 1}, {1, 1}, {0, 0}, {0, 0}, "", true, 9834496.}, + /* GFLOPS 0.005 x 4 = 0.020 */ {{1, 1}, {{1, 240, 16, 16}}, 40, 1, {1, 1}, {1, 1}, {0, 0}, {0, 0}, "", true, 4925440.}, /* GFLOPS 0.019 x 1 = 0.019 */ {{1, 1}, {{1, 384, 14, 14}}, 128, 1, {1, 1}, {1, 1}, {0, 0}, {0, 0}, "", false, 19292672.}, /* GFLOPS 0.019 x 1 = 0.019 */ {{1, 1}, {{1, 64, 64, 64}}, 36, 1, {1, 1}, {1, 1}, {0, 0}, {0, 0}, "VALID", true, 19021824.}, + /* GFLOPS 0.010 x 2 = 0.019 */ {{1, 1}, {{1, 96, 56, 56}}, 16, 1, {1, 1}, {1, 1}, {0, 0}, {0, 0}, "", true, 9683968.}, + /* GFLOPS 0.010 x 2 = 0.019 */ {{1, 1}, {{1, 32, 48, 48}}, 64, 1, {1, 1}, {1, 1}, {0, 0}, {0, 0}, "", true, 9584640.}, + /* GFLOPS 0.010 x 2 = 0.019 */ {{1, 1}, {{1, 64, 48, 48}}, 32, 1, {1, 1}, {1, 1}, {0, 0}, {0, 0}, "SAME", true, 9510912.}, /* GFLOPS 0.018 x 1 = 0.018 */ {{1, 1}, {{1, 576, 10, 10}}, 160, 1, {1, 1}, {1, 1}, {0, 0}, {0, 0}, "SAME", false, 18448000.}, /* GFLOPS 0.018 x 1 = 0.018 */ {{1, 1}, {{1, 480, 14, 14}}, 96, 1, {1, 1}, {1, 1}, {0, 0}, {0, 0}, "", true, 18082176.}, - /* GFLOPS 0.018 x 1 = 0.018 */ {{1, 1}, {{1, 480, 14, 14}}, 96, 1, {1, 1}, {1, 1}, {0, 0}, {0, 0}, "SAME", true, 18082176.}, /* GFLOPS 0.018 x 1 = 0.018 */ {{1, 1}, {{1, 192, 38, 38}}, 32, 1, {1, 1}, {1, 1}, {0, 0}, {0, 0}, "SAME", true, 17790080.}, /* GFLOPS 0.018 x 1 = 0.018 */ {{1, 1}, {{1, 352, 14, 14}}, 128, 1, {1, 1}, {1, 1}, {0, 0}, {0, 0}, "", false, 17687040.}, - /* GFLOPS 0.017 x 1 = 0.017 */ {{2, 2}, {{1, 16, 128, 256}}, 16, 1, {2, 2}, {1, 1}, {0, 0}, {0, 0}, "", false, 16908288.}, - /* GFLOPS 0.016 x 1 = 0.016 */ {{1, 1}, {{1, 320, 14, 14}}, 128, 1, {1, 1}, {1, 1}, {0, 0}, {0, 0}, "", false, 16081408.}, + /* GFLOPS 0.009 x 2 = 0.018 */ {{1, 1}, {{1, 8, 128, 128}}, 32, 1, {1, 1}, {1, 1}, {0, 0}, {0, 0}, "", true, 8912896.}, + /* GFLOPS 0.008 x 2 = 0.017 */ {{1, 1}, {{1, 64, 80, 80}}, 10, 1, {1, 1}, {1, 1}, {0, 0}, {0, 0}, "", true, 8256000.}, /* GFLOPS 0.016 x 1 = 0.016 */ {{1, 1}, {{1, 832, 7, 7}}, 192, 1, {1, 1}, {1, 1}, {0, 0}, {0, 0}, "", true, 15664320.}, - /* GFLOPS 0.016 x 1 = 0.016 */ {{1, 1}, {{1, 832, 7, 7}}, 192, 1, {1, 1}, {1, 1}, {0, 0}, {0, 0}, "SAME", true, 15664320.}, - /* GFLOPS 0.015 x 1 = 0.015 */ {{5, 5}, {{1, 48, 7, 7}}, 128, 1, {1, 1}, {1, 1}, {2, 2}, {0, 0}, "", true, 15059072.}, - /* GFLOPS 0.015 x 1 = 0.015 */ {{5, 5}, {{1, 32, 12, 12}}, 64, 1, {1, 1}, {1, 1}, {2, 2}, {0, 0}, "", true, 14754816.}, - /* GFLOPS 0.015 x 1 = 0.015 */ {{3, 3}, {{1, 128, 10, 10}}, 256, 1, {2, 2}, {1, 1}, {1, 1}, {0, 0}, "", true, 14752000.}, + /* GFLOPS 0.008 x 2 = 0.016 */ {{1, 1}, {{1, 128, 20, 20}}, 80, 1, {1, 1}, {1, 1}, {0, 0}, {0, 0}, "", true, 8224000.}, + /* GFLOPS 0.008 x 2 = 0.016 */ {{1, 1}, {{1, 256, 12, 12}}, 108, 1, {1, 1}, {1, 1}, {0, 0}, {0, 0}, "", true, 7978176.}, /* GFLOPS 0.014 x 1 = 0.014 */ {{1, 1}, {{1, 288, 14, 14}}, 128, 1, {1, 1}, {1, 1}, {0, 0}, {0, 0}, "", false, 14475776.}, /* GFLOPS 0.014 x 1 = 0.014 */ {{1, 1}, {{1, 512, 5, 5}}, 546, 1, {1, 1}, {1, 1}, {0, 0}, {0, 0}, "SAME", true, 13991250.}, + /* GFLOPS 0.007 x 2 = 0.014 */ {{1, 1}, {{1, 288, 14, 14}}, 64, 1, {1, 1}, {1, 1}, {0, 0}, {0, 0}, "", true, 7237888.}, + /* GFLOPS 0.007 x 2 = 0.014 */ {{1, 1}, {{1, 144, 32, 32}}, 24, 1, {1, 1}, {1, 1}, {0, 0}, {0, 0}, "", true, 7102464.}, + /* GFLOPS 0.007 x 2 = 0.014 */ {{1, 1}, {{1, 240, 16, 16}}, 56, 1, {1, 1}, {1, 1}, {0, 0}, {0, 0}, "", true, 6895616.}, /* GFLOPS 0.013 x 1 = 0.013 */ {{1, 1}, {{1, 144, 38, 38}}, 32, 1, {1, 1}, {1, 1}, {0, 0}, {0, 0}, "SAME", false, 13354112.}, - /* GFLOPS 0.007 x 2 = 0.013 */ {{1, 1}, {{1, 16, 56, 56}}, 64, 1, {1, 1}, {1, 1}, {0, 0}, {0, 0}, "", true, 6623232.}, - /* GFLOPS 0.013 x 1 = 0.013 */ {{1, 1}, {{1, 512, 10, 10}}, 128, 1, {1, 1}, {1, 1}, {0, 0}, {0, 0}, "", true, 13120000.}, /* GFLOPS 0.013 x 1 = 0.013 */ {{1, 1}, {{1, 832, 7, 7}}, 160, 1, {1, 1}, {1, 1}, {0, 0}, {0, 0}, "", true, 13053600.}, - /* GFLOPS 0.013 x 1 = 0.013 */ {{1, 1}, {{1, 832, 7, 7}}, 160, 1, {1, 1}, {1, 1}, {0, 0}, {0, 0}, "SAME", true, 13053600.}, - /* GFLOPS 0.007 x 2 = 0.013 */ {{1, 1}, {{1, 32, 28, 28}}, 128, 1, {1, 1}, {1, 1}, {0, 0}, {0, 0}, "", true, 6522880.}, - /* GFLOPS 0.001 x 11 = 0.013 */ {{3, 3}, {{1, 64, 4, 4}}, 64, 1, {1, 1}, {1, 1}, {0, 0}, {0, 0}, "SAME", false, 1180672.}, - /* GFLOPS 0.006 x 2 = 0.013 */ {{1, 1}, {{1, 64, 14, 14}}, 256, 1, {1, 1}, {1, 1}, {0, 0}, {0, 0}, "", true, 6472704.}, - /* GFLOPS 0.013 x 1 = 0.013 */ {{1, 1}, {{1, 128, 56, 56}}, 16, 1, {1, 1}, {1, 1}, {0, 0}, {0, 0}, "", true, 12895232.}, - /* GFLOPS 0.013 x 1 = 0.013 */ {{1, 1}, {{1, 256, 28, 28}}, 32, 1, {1, 1}, {1, 1}, {0, 0}, {0, 0}, "SAME", true, 12870144.}, - /* GFLOPS 0.013 x 1 = 0.013 */ {{1, 1}, {{1, 256, 14, 14}}, 128, 1, {1, 1}, {1, 1}, {0, 0}, {0, 0}, "", false, 12870144.}, /* GFLOPS 0.013 x 1 = 0.013 */ {{1, 1}, {{1, 508, 14, 14}}, 64, 1, {1, 1}, {1, 1}, {0, 0}, {0, 0}, "SAME", true, 12757248.}, + /* GFLOPS 0.007 x 2 = 0.013 */ {{1, 1}, {{1, 16, 56, 56}}, 64, 1, {1, 1}, {1, 1}, {0, 0}, {0, 0}, "", true, 6623232.}, + /* GFLOPS 0.007 x 2 = 0.013 */ {{1, 1}, {{1, 128, 80, 80}}, 4, 1, {1, 1}, {1, 1}, {0, 0}, {0, 0}, "", true, 6579200.}, + /* GFLOPS 0.007 x 2 = 0.013 */ {{1, 1}, {{1, 32, 28, 28}}, 128, 1, {1, 1}, {1, 1}, {0, 0}, {0, 0}, "", true, 6522880.}, + /* GFLOPS 0.006 x 2 = 0.013 */ {{1, 1}, {{1, 64, 14, 14}}, 256, 1, {1, 1}, {1, 1}, {0, 0}, {0, 0}, "", true, 6472704.}, + /* GFLOPS 0.006 x 2 = 0.013 */ {{1, 1}, {{1, 24, 128, 128}}, 8, 1, {1, 1}, {1, 1}, {0, 0}, {0, 0}, "", true, 6422528.}, + /* GFLOPS 0.002 x 6 = 0.013 */ {{1, 1}, {{1, 8, 128, 128}}, 8, 1, {1, 1}, {1, 1}, {0, 0}, {0, 0}, "", true, 2228224.}, /* GFLOPS 0.012 x 1 = 0.012 */ {{1, 1}, {{1, 992, 7, 7}}, 128, 1, {1, 1}, {1, 1}, {0, 0}, {0, 0}, "", false, 12449920.}, /* GFLOPS 0.012 x 1 = 0.012 */ {{1, 1}, {{1, 480, 14, 14}}, 64, 1, {1, 1}, {1, 1}, {0, 0}, {0, 0}, "", true, 12054784.}, - /* GFLOPS 0.012 x 1 = 0.012 */ {{1, 1}, {{1, 480, 14, 14}}, 64, 1, {1, 1}, {1, 1}, {0, 0}, {0, 0}, "SAME", true, 12054784.}, /* GFLOPS 0.012 x 1 = 0.012 */ {{1, 1}, {{1, 960, 7, 7}}, 128, 1, {1, 1}, {1, 1}, {0, 0}, {0, 0}, "", false, 12048512.}, /* GFLOPS 0.012 x 1 = 0.012 */ {{1, 1}, {{1, 32, 75, 75}}, 128, 1, {2, 2}, {1, 1}, {0, 0}, {0, 0}, "", false, 12014080.}, - /* GFLOPS 0.012 x 1 = 0.012 */ {{3, 3}, {{1, 96, 6, 6}}, 192, 1, {1, 1}, {1, 1}, {1, 1}, {0, 0}, "", true, 11950848.}, - /* GFLOPS 0.006 x 2 = 0.012 */ {{3, 3}, {{1, 96, 3, 3}}, 384, 1, {1, 1}, {1, 1}, {1, 1}, {0, 0}, "", true, 5975424.}, /* GFLOPS 0.012 x 1 = 0.012 */ {{1, 1}, {{1, 320, 12, 12}}, 128, 1, {1, 1}, {1, 1}, {0, 0}, {0, 0}, "", true, 11814912.}, /* GFLOPS 0.012 x 1 = 0.012 */ {{1, 1}, {{1, 640, 6, 6}}, 256, 1, {1, 1}, {1, 1}, {0, 0}, {0, 0}, "", true, 11805696.}, /* GFLOPS 0.012 x 1 = 0.012 */ {{1, 1}, {{1, 928, 7, 7}}, 128, 1, {1, 1}, {1, 1}, {0, 0}, {0, 0}, "", false, 11647104.}, /* GFLOPS 0.011 x 1 = 0.011 */ {{1, 1}, {{1, 896, 7, 7}}, 128, 1, {1, 1}, {1, 1}, {0, 0}, {0, 0}, "", false, 11245696.}, - /* GFLOPS 0.011 x 1 = 0.011 */ {{1, 1}, {{1, 256, 13, 13}}, 128, 1, {1, 1}, {1, 1}, {0, 0}, {0, 0}, "", false, 11097216.}, - /* GFLOPS 0.011 x 1 = 0.011 */ {{3, 3}, {{1, 256, 10, 10}}, 24, 1, {1, 1}, {1, 1}, {1, 1}, {0, 0}, "", true, 11061600.}, - /* GFLOPS 0.006 x 2 = 0.011 */ {{3, 3}, {{1, 512, 5, 5}}, 24, 1, {1, 1}, {1, 1}, {0, 0}, {0, 0}, "SAME", true, 5530200.}, /* GFLOPS 0.011 x 1 = 0.011 */ {{1, 1}, {{1, 864, 7, 7}}, 128, 1, {1, 1}, {1, 1}, {0, 0}, {0, 0}, "", false, 10844288.}, + /* GFLOPS 0.005 x 2 = 0.011 */ {{1, 1}, {{1, 144, 28, 28}}, 24, 1, {1, 1}, {1, 1}, {0, 0}, {0, 0}, "", true, 5437824.}, + /* GFLOPS 0.005 x 2 = 0.011 */ {{1, 1}, {{1, 128, 24, 24}}, 36, 1, {1, 1}, {1, 1}, {0, 0}, {0, 0}, "", true, 5329152.}, /* GFLOPS 0.010 x 1 = 0.010 */ {{1, 1}, {{1, 832, 7, 7}}, 128, 1, {1, 1}, {1, 1}, {0, 0}, {0, 0}, "", false, 10442880.}, - /* GFLOPS 0.010 x 1 = 0.010 */ {{5, 5}, {{1, 32, 7, 7}}, 128, 1, {1, 1}, {1, 1}, {2, 2}, {0, 0}, "", true, 10041472.}, /* GFLOPS 0.010 x 1 = 0.010 */ {{1, 1}, {{1, 800, 7, 7}}, 128, 1, {1, 1}, {1, 1}, {0, 0}, {0, 0}, "", false, 10041472.}, - /* GFLOPS 0.010 x 1 = 0.010 */ {{1, 1}, {{1, 192, 28, 28}}, 32, 1, {1, 1}, {1, 1}, {0, 0}, {0, 0}, "", true, 9658880.}, - /* GFLOPS 0.010 x 1 = 0.010 */ {{1, 1}, {{1, 192, 28, 28}}, 32, 1, {1, 1}, {1, 1}, {0, 0}, {0, 0}, "SAME", true, 9658880.}, /* GFLOPS 0.010 x 1 = 0.010 */ {{1, 1}, {{1, 384, 14, 14}}, 64, 1, {1, 1}, {1, 1}, {0, 0}, {0, 0}, "", true, 9646336.}, - /* GFLOPS 0.005 x 2 = 0.010 */ {{1, 1}, {{1, 512, 14, 14}}, 24, 1, {1, 1}, {1, 1}, {0, 0}, {0, 0}, "", true, 4821600.}, /* GFLOPS 0.010 x 1 = 0.010 */ {{1, 1}, {{1, 768, 7, 7}}, 128, 1, {1, 1}, {1, 1}, {0, 0}, {0, 0}, "", false, 9640064.}, - /* GFLOPS 0.010 x 1 = 0.010 */ {{3, 3}, {{1, 4, 128, 256}}, 4, 1, {1, 1}, {1, 1}, {1, 1}, {0, 0}, "", true, 9568256.}, - /* GFLOPS 0.005 x 2 = 0.009 */ {{1, 1}, {{1, 4, 128, 256}}, 16, 1, {1, 1}, {1, 1}, {0, 0}, {0, 0}, "", false, 4718592.}, /* GFLOPS 0.009 x 1 = 0.009 */ {{1, 1}, {{1, 736, 7, 7}}, 128, 1, {1, 1}, {1, 1}, {0, 0}, {0, 0}, "", false, 9238656.}, - /* GFLOPS 0.009 x 1 = 0.009 */ {{1, 1}, {{1, 192, 19, 19}}, 64, 1, {1, 1}, {1, 1}, {0, 0}, {0, 0}, "SAME", false, 8895040.}, /* GFLOPS 0.009 x 1 = 0.009 */ {{1, 1}, {{1, 704, 7, 7}}, 128, 1, {1, 1}, {1, 1}, {0, 0}, {0, 0}, "", false, 8837248.}, + /* GFLOPS 0.005 x 2 = 0.009 */ {{1, 1}, {{1, 96, 32, 32}}, 24, 1, {1, 1}, {1, 1}, {0, 0}, {0, 0}, "", true, 4743168.}, + /* GFLOPS 0.005 x 2 = 0.009 */ {{1, 1}, {{1, 4, 128, 256}}, 16, 1, {1, 1}, {1, 1}, {0, 0}, {0, 0}, "", false, 4718592.}, + /* GFLOPS 0.004 x 2 = 0.009 */ {{1, 1}, {{1, 16, 64, 64}}, 32, 1, {1, 1}, {1, 1}, {0, 0}, {0, 0}, "", true, 4325376.}, + /* GFLOPS 0.004 x 2 = 0.009 */ {{1, 1}, {{1, 32, 64, 64}}, 16, 1, {1, 1}, {1, 1}, {0, 0}, {0, 0}, "", true, 4259840.}, /* GFLOPS 0.008 x 1 = 0.008 */ {{1, 1}, {{1, 672, 7, 7}}, 128, 1, {1, 1}, {1, 1}, {0, 0}, {0, 0}, "", false, 8435840.}, /* GFLOPS 0.008 x 1 = 0.008 */ {{1, 1}, {{1, 128, 32, 64}}, 16, 1, {1, 1}, {1, 1}, {0, 0}, {0, 0}, "", false, 8421376.}, - /* GFLOPS 0.008 x 1 = 0.008 */ {{1, 1}, {{1, 640, 7, 7}}, 128, 1, {1, 1}, {1, 1}, {0, 0}, {0, 0}, "", false, 8034432.}, - /* GFLOPS 0.004 x 2 = 0.008 */ {{1, 1}, {{1, 832, 7, 7}}, 48, 1, {1, 1}, {1, 1}, {0, 0}, {0, 0}, "SAME", true, 3916080.}, /* GFLOPS 0.008 x 1 = 0.008 */ {{1, 1}, {{1, 608, 7, 7}}, 128, 1, {1, 1}, {1, 1}, {0, 0}, {0, 0}, "", false, 7633024.}, - /* GFLOPS 0.008 x 1 = 0.008 */ {{5, 5}, {{1, 16, 14, 14}}, 48, 1, {1, 1}, {1, 1}, {2, 2}, {0, 0}, "", true, 7535808.}, - /* GFLOPS 0.008 x 1 = 0.008 */ {{5, 5}, {{1, 16, 14, 14}}, 48, 1, {1, 1}, {1, 1}, {0, 0}, {0, 0}, "SAME", true, 7535808.}, + /* GFLOPS 0.004 x 2 = 0.008 */ {{1, 1}, {{1, 384, 7, 7}}, 112, 1, {1, 1}, {1, 1}, {0, 0}, {0, 0}, "", true, 4220272.}, + /* GFLOPS 0.004 x 2 = 0.008 */ {{1, 1}, {{1, 336, 8, 8}}, 96, 1, {1, 1}, {1, 1}, {0, 0}, {0, 0}, "", true, 4134912.}, /* GFLOPS 0.007 x 1 = 0.007 */ {{1, 1}, {{1, 640, 6, 6}}, 160, 1, {1, 1}, {1, 1}, {0, 0}, {0, 0}, "", true, 7378560.}, - /* GFLOPS 0.004 x 2 = 0.007 */ {{1, 1}, {{1, 48, 14, 14}}, 192, 1, {1, 1}, {1, 1}, {0, 0}, {0, 0}, "", true, 3650304.}, /* GFLOPS 0.007 x 1 = 0.007 */ {{1, 1}, {{1, 384, 14, 14}}, 48, 1, {1, 1}, {1, 1}, {0, 0}, {0, 0}, "", true, 7234752.}, /* GFLOPS 0.007 x 1 = 0.007 */ {{1, 1}, {{1, 576, 7, 7}}, 128, 1, {1, 1}, {1, 1}, {0, 0}, {0, 0}, "", false, 7231616.}, /* GFLOPS 0.007 x 1 = 0.007 */ {{1, 1}, {{1, 256, 12, 12}}, 96, 1, {1, 1}, {1, 1}, {0, 0}, {0, 0}, "", true, 7091712.}, /* GFLOPS 0.007 x 1 = 0.007 */ {{1, 1}, {{1, 544, 7, 7}}, 128, 1, {1, 1}, {1, 1}, {0, 0}, {0, 0}, "", false, 6830208.}, - /* GFLOPS 0.007 x 1 = 0.007 */ {{1, 1}, {{1, 64, 8, 8}}, 810, 1, {1, 1}, {1, 1}, {0, 0}, {0, 0}, "VALID", true, 6687360.}, - /* GFLOPS 0.007 x 1 = 0.007 */ {{3, 3}, {{1, 160, 6, 6}}, 256, 1, {2, 2}, {1, 1}, {1, 1}, {0, 0}, "", true, 6637824.}, /* GFLOPS 0.007 x 1 = 0.007 */ {{1, 1}, {{1, 528, 14, 14}}, 32, 1, {1, 1}, {1, 1}, {0, 0}, {0, 0}, "", true, 6629504.}, - /* GFLOPS 0.007 x 1 = 0.007 */ {{1, 1}, {{1, 528, 14, 14}}, 32, 1, {1, 1}, {1, 1}, {0, 0}, {0, 0}, "SAME", true, 6629504.}, /* GFLOPS 0.007 x 1 = 0.007 */ {{1, 1}, {{1, 256, 5, 5}}, 512, 1, {1, 1}, {1, 1}, {0, 0}, {0, 0}, "SAME", false, 6566400.}, - /* GFLOPS 0.003 x 2 = 0.007 */ {{1, 1}, {{1, 512, 5, 5}}, 128, 1, {1, 1}, {1, 1}, {0, 0}, {0, 0}, "SAME", false, 3280000.}, + /* GFLOPS 0.004 x 2 = 0.007 */ {{1, 1}, {{1, 48, 14, 14}}, 192, 1, {1, 1}, {1, 1}, {0, 0}, {0, 0}, "", true, 3650304.}, + /* GFLOPS 0.003 x 2 = 0.007 */ {{1, 1}, {{1, 64, 80, 80}}, 4, 1, {1, 1}, {1, 1}, {0, 0}, {0, 0}, "", true, 3302400.}, /* GFLOPS 0.006 x 1 = 0.006 */ {{1, 1}, {{1, 64, 56, 56}}, 16, 1, {1, 1}, {1, 1}, {0, 0}, {0, 0}, "", true, 6472704.}, - /* GFLOPS 0.006 x 1 = 0.006 */ {{1, 1}, {{1, 128, 28, 28}}, 32, 1, {1, 1}, {1, 1}, {0, 0}, {0, 0}, "", true, 6447616.}, - /* GFLOPS 0.006 x 1 = 0.006 */ {{1, 1}, {{1, 512, 7, 7}}, 128, 1, {1, 1}, {1, 1}, {0, 0}, {0, 0}, "", false, 6428800.}, /* GFLOPS 0.006 x 1 = 0.006 */ {{1, 1}, {{1, 512, 14, 14}}, 32, 1, {1, 1}, {1, 1}, {0, 0}, {0, 0}, "", true, 6428800.}, - /* GFLOPS 0.006 x 1 = 0.006 */ {{1, 1}, {{1, 512, 14, 14}}, 32, 1, {1, 1}, {1, 1}, {0, 0}, {0, 0}, "SAME", true, 6428800.}, - /* GFLOPS 0.001 x 12 = 0.006 */ {{1, 1}, {{1, 64, 8, 8}}, 64, 1, {1, 1}, {1, 1}, {0, 0}, {0, 0}, "VALID", true, 528384.}, - /* GFLOPS 0.006 x 1 = 0.006 */ {{3, 3}, {{1, 256, 10, 10}}, 12, 1, {1, 1}, {1, 1}, {1, 1}, {0, 0}, "", true, 5530800.}, + /* GFLOPS 0.003 x 2 = 0.006 */ {{1, 1}, {{1, 144, 16, 16}}, 40, 1, {1, 1}, {1, 1}, {0, 0}, {0, 0}, "", true, 2959360.}, /* GFLOPS 0.005 x 1 = 0.005 */ {{1, 1}, {{1, 192, 12, 12}}, 96, 1, {1, 1}, {1, 1}, {0, 0}, {0, 0}, "", true, 5322240.}, - /* GFLOPS 0.005 x 1 = 0.005 */ {{3, 3}, {{1, 128, 5, 5}}, 256, 1, {2, 2}, {1, 1}, {1, 1}, {0, 0}, "", true, 5310720.}, - /* GFLOPS 0.005 x 1 = 0.005 */ {{3, 3}, {{1, 128, 5, 5}}, 256, 1, {2, 2}, {1, 1}, {0, 0}, {0, 0}, "SAME", true, 5310720.}, - /* GFLOPS 0.005 x 1 = 0.005 */ {{3, 3}, {{1, 128, 5, 5}}, 256, 1, {2, 2}, {1, 1}, {0, 0}, {0, 0}, "SAME", false, 5310720.}, - /* GFLOPS 0.005 x 1 = 0.005 */ {{3, 3}, {{1, 128, 5, 5}}, 256, 1, {1, 1}, {1, 1}, {0, 0}, {0, 0}, "", true, 5310720.}, /* GFLOPS 0.005 x 1 = 0.005 */ {{1, 1}, {{1, 1024, 10, 10}}, 24, 1, {1, 1}, {1, 1}, {0, 0}, {0, 0}, "", true, 4917600.}, - /* GFLOPS 0.005 x 1 = 0.005 */ {{1, 1}, {{1, 1024, 10, 10}}, 24, 1, {1, 1}, {1, 1}, {0, 0}, {0, 0}, "SAME", true, 4917600.}, - /* GFLOPS 0.005 x 1 = 0.005 */ {{1, 1}, {{1, 192, 28, 28}}, 16, 1, {1, 1}, {1, 1}, {0, 0}, {0, 0}, "", true, 4829440.}, - /* GFLOPS 0.005 x 1 = 0.005 */ {{1, 1}, {{1, 192, 28, 28}}, 16, 1, {1, 1}, {1, 1}, {0, 0}, {0, 0}, "SAME", true, 4829440.}, /* GFLOPS 0.005 x 1 = 0.005 */ {{1, 1}, {{1, 256, 14, 14}}, 48, 1, {1, 1}, {1, 1}, {0, 0}, {0, 0}, "", true, 4826304.}, - /* GFLOPS 0.005 x 1 = 0.005 */ {{1, 1}, {{1, 512, 14, 14}}, 24, 1, {1, 1}, {1, 1}, {0, 0}, {0, 0}, "SAME", true, 4821600.}, /* GFLOPS 0.005 x 1 = 0.005 */ {{1, 1}, {{1, 508, 14, 14}}, 24, 1, {1, 1}, {1, 1}, {0, 0}, {0, 0}, "SAME", true, 4783968.}, /* GFLOPS 0.005 x 1 = 0.005 */ {{1, 1}, {{1, 64, 32, 32}}, 36, 1, {1, 1}, {1, 1}, {0, 0}, {0, 0}, "VALID", true, 4755456.}, - /* GFLOPS 0.005 x 1 = 0.005 */ {{1, 1}, {{1, 64, 24, 24}}, 64, 1, {1, 1}, {1, 1}, {0, 0}, {0, 0}, "", true, 4755456.}, - /* GFLOPS 0.005 x 1 = 0.005 */ {{1, 1}, {{1, 256, 12, 12}}, 64, 1, {1, 1}, {1, 1}, {0, 0}, {0, 0}, "", true, 4727808.}, /* GFLOPS 0.005 x 1 = 0.005 */ {{1, 1}, {{1, 1024, 3, 3}}, 256, 1, {1, 1}, {1, 1}, {0, 0}, {0, 0}, "", true, 4720896.}, + /* GFLOPS 0.003 x 2 = 0.005 */ {{1, 1}, {{1, 144, 14, 14}}, 48, 1, {1, 1}, {1, 1}, {0, 0}, {0, 0}, "", true, 2718912.}, + /* GFLOPS 0.002 x 2 = 0.005 */ {{1, 1}, {{1, 576, 8, 8}}, 32, 1, {1, 1}, {1, 1}, {0, 0}, {0, 0}, "", true, 2361344.}, /* GFLOPS 0.004 x 1 = 0.004 */ {{1, 1}, {{1, 512, 19, 19}}, 12, 1, {1, 1}, {1, 1}, {0, 0}, {0, 0}, "", true, 4440300.}, - /* GFLOPS 0.004 x 1 = 0.004 */ {{1, 1}, {{1, 512, 19, 19}}, 12, 1, {1, 1}, {1, 1}, {0, 0}, {0, 0}, "SAME", true, 4440300.}, /* GFLOPS 0.004 x 1 = 0.004 */ {{1, 1}, {{1, 640, 6, 6}}, 96, 1, {1, 1}, {1, 1}, {0, 0}, {0, 0}, "", true, 4427136.}, /* GFLOPS 0.004 x 1 = 0.004 */ {{1, 1}, {{1, 16, 128, 256}}, 4, 1, {1, 1}, {1, 1}, {0, 0}, {0, 0}, "", false, 4325376.}, /* GFLOPS 0.004 x 1 = 0.004 */ {{1, 1}, {{1, 64, 64, 128}}, 4, 1, {1, 1}, {1, 1}, {0, 0}, {0, 0}, "", false, 4227072.}, /* GFLOPS 0.004 x 1 = 0.004 */ {{1, 1}, {{1, 832, 7, 7}}, 48, 1, {1, 1}, {1, 1}, {0, 0}, {0, 0}, "", true, 3916080.}, - /* GFLOPS 0.004 x 1 = 0.004 */ {{3, 3}, {{1, 256, 1, 1}}, 804, 1, {1, 1}, {1, 1}, {1, 1}, {0, 0}, "", true, 3705636.}, - /* GFLOPS 0.004 x 1 = 0.004 */ {{5, 5}, {{1, 16, 12, 12}}, 32, 1, {1, 1}, {1, 1}, {2, 2}, {0, 0}, "", true, 3691008.}, - /* GFLOPS 0.004 x 1 = 0.004 */ {{3, 3}, {{1, 64, 10, 10}}, 128, 1, {2, 2}, {1, 1}, {1, 1}, {0, 0}, "", true, 3689600.}, - /* GFLOPS 0.004 x 1 = 0.004 */ {{5, 5}, {{1, 32, 6, 6}}, 64, 1, {1, 1}, {1, 1}, {2, 2}, {0, 0}, "", true, 3688704.}, - /* GFLOPS 0.004 x 1 = 0.004 */ {{5, 5}, {{1, 32, 12, 12}}, 64, 1, {2, 2}, {1, 1}, {2, 2}, {0, 0}, "", true, 3688704.}, - /* GFLOPS 0.004 x 1 = 0.004 */ {{5, 5}, {{1, 64, 6, 6}}, 128, 1, {2, 2}, {1, 1}, {2, 2}, {0, 0}, "", true, 3687552.}, /* GFLOPS 0.004 x 1 = 0.004 */ {{1, 1}, {{1, 192, 12, 12}}, 64, 1, {1, 1}, {1, 1}, {0, 0}, {0, 0}, "", true, 3548160.}, + /* GFLOPS 0.002 x 2 = 0.004 */ {{1, 1}, {{1, 240, 48, 48}}, 2, 1, {1, 1}, {1, 1}, {0, 0}, {0, 0}, "", true, 2216448.}, + /* GFLOPS 0.002 x 2 = 0.004 */ {{1, 1}, {{1, 32, 64, 64}}, 8, 1, {1, 1}, {1, 1}, {0, 0}, {0, 0}, "", true, 2129920.}, + /* GFLOPS 0.002 x 2 = 0.004 */ {{1, 1}, {{1, 64, 40, 40}}, 10, 1, {1, 1}, {1, 1}, {0, 0}, {0, 0}, "", true, 2064000.}, + /* GFLOPS 0.001 x 6 = 0.004 */ {{1, 1}, {{1, 32, 24, 24}}, 16, 1, {1, 1}, {1, 1}, {0, 0}, {0, 0}, "SAME", true, 599040.}, /* GFLOPS 0.003 x 1 = 0.003 */ {{1, 1}, {{1, 736, 3, 3}}, 256, 1, {1, 1}, {1, 1}, {0, 0}, {0, 0}, "", true, 3393792.}, - /* GFLOPS 0.003 x 1 = 0.003 */ {{1, 1}, {{1, 256, 10, 10}}, 64, 1, {1, 1}, {1, 1}, {0, 0}, {0, 0}, "", true, 3283200.}, /* GFLOPS 0.003 x 1 = 0.003 */ {{1, 1}, {{1, 512, 5, 5}}, 128, 1, {1, 1}, {1, 1}, {0, 0}, {0, 0}, "", true, 3280000.}, - /* GFLOPS 0.003 x 1 = 0.003 */ {{1, 1}, {{1, 512, 5, 5}}, 128, 1, {1, 1}, {1, 1}, {0, 0}, {0, 0}, "SAME", true, 3280000.}, /* GFLOPS 0.003 x 1 = 0.003 */ {{1, 1}, {{1, 512, 5, 5}}, 126, 1, {1, 1}, {1, 1}, {0, 0}, {0, 0}, "", true, 3228750.}, /* GFLOPS 0.003 x 1 = 0.003 */ {{1, 1}, {{1, 480, 14, 14}}, 16, 1, {1, 1}, {1, 1}, {0, 0}, {0, 0}, "", true, 3013696.}, - /* GFLOPS 0.003 x 1 = 0.003 */ {{1, 1}, {{1, 480, 14, 14}}, 16, 1, {1, 1}, {1, 1}, {0, 0}, {0, 0}, "SAME", true, 3013696.}, /* GFLOPS 0.003 x 1 = 0.003 */ {{1, 1}, {{1, 320, 12, 12}}, 32, 1, {1, 1}, {1, 1}, {0, 0}, {0, 0}, "", true, 2953728.}, /* GFLOPS 0.003 x 1 = 0.003 */ {{1, 1}, {{1, 640, 6, 6}}, 64, 1, {1, 1}, {1, 1}, {0, 0}, {0, 0}, "", true, 2951424.}, - /* GFLOPS 0.003 x 1 = 0.003 */ {{3, 3}, {{1, 256, 5, 5}}, 24, 1, {1, 1}, {1, 1}, {1, 1}, {0, 0}, "", true, 2765400.}, - /* GFLOPS 0.003 x 1 = 0.003 */ {{3, 3}, {{1, 128, 5, 5}}, 128, 1, {2, 2}, {1, 1}, {0, 0}, {0, 0}, "SAME", false, 2655360.}, /* GFLOPS 0.003 x 1 = 0.003 */ {{1, 1}, {{1, 832, 7, 7}}, 32, 1, {1, 1}, {1, 1}, {0, 0}, {0, 0}, "", true, 2610720.}, - /* GFLOPS 0.003 x 1 = 0.003 */ {{1, 1}, {{1, 256, 3, 3}}, 546, 1, {1, 1}, {1, 1}, {0, 0}, {0, 0}, "SAME", true, 2520882.}, - /* GFLOPS 0.001 x 2 = 0.003 */ {{3, 3}, {{1, 128, 1, 1}}, 546, 1, {1, 1}, {1, 1}, {0, 0}, {0, 0}, "SAME", true, 1258530.}, + /* GFLOPS 0.002 x 2 = 0.003 */ {{1, 1}, {{1, 128, 80, 80}}, 1, 1, {1, 1}, {1, 1}, {0, 0}, {0, 0}, "", true, 1644800.}, + /* GFLOPS 0.002 x 2 = 0.003 */ {{1, 1}, {{1, 128, 40, 40}}, 4, 1, {1, 1}, {1, 1}, {0, 0}, {0, 0}, "", true, 1644800.}, + /* GFLOPS 0.002 x 2 = 0.003 */ {{1, 1}, {{1, 24, 32, 32}}, 32, 1, {1, 1}, {1, 1}, {0, 0}, {0, 0}, "", true, 1605632.}, + /* GFLOPS 0.001 x 4 = 0.003 */ {{1, 1}, {{1, 64, 80, 80}}, 1, 1, {1, 1}, {1, 1}, {0, 0}, {0, 0}, "", true, 825600.}, /* GFLOPS 0.002 x 1 = 0.002 */ {{1, 1}, {{1, 256, 12, 12}}, 32, 1, {1, 1}, {1, 1}, {0, 0}, {0, 0}, "", true, 2363904.}, - /* GFLOPS 0.002 x 1 = 0.002 */ {{3, 3}, {{1, 128, 3, 3}}, 256, 1, {2, 2}, {1, 1}, {1, 1}, {0, 0}, "", true, 2360320.}, - /* GFLOPS 0.002 x 1 = 0.002 */ {{3, 3}, {{1, 128, 3, 3}}, 256, 1, {2, 2}, {1, 1}, {0, 0}, {0, 0}, "SAME", true, 2360320.}, - /* GFLOPS 0.002 x 1 = 0.002 */ {{3, 3}, {{1, 128, 3, 3}}, 256, 1, {2, 2}, {1, 1}, {0, 0}, {0, 0}, "SAME", false, 2360320.}, /* GFLOPS 0.002 x 1 = 0.002 */ {{1, 1}, {{1, 528, 4, 4}}, 128, 1, {1, 1}, {1, 1}, {0, 0}, {0, 0}, "SAME", true, 2164736.}, /* GFLOPS 0.002 x 1 = 0.002 */ {{1, 1}, {{1, 508, 4, 4}}, 128, 1, {1, 1}, {1, 1}, {0, 0}, {0, 0}, "SAME", true, 2082816.}, /* GFLOPS 0.002 x 1 = 0.002 */ {{1, 1}, {{1, 1024, 1, 1}}, 1000, 1, {1, 1}, {1, 1}, {0, 0}, {0, 0}, "", true, 2049000.}, - /* GFLOPS 0.001 x 2 = 0.002 */ {{3, 3}, {{1, 256, 3, 3}}, 24, 1, {1, 1}, {1, 1}, {0, 0}, {0, 0}, "SAME", true, 995544.}, - /* GFLOPS 0.002 x 1 = 0.002 */ {{1, 1}, {{1, 1024, 3, 3}}, 96, 1, {1, 1}, {1, 1}, {0, 0}, {0, 0}, "", true, 1770336.}, /* GFLOPS 0.002 x 1 = 0.002 */ {{1, 1}, {{1, 64, 4, 4}}, 810, 1, {1, 1}, {1, 1}, {0, 0}, {0, 0}, "VALID", true, 1671840.}, /* GFLOPS 0.002 x 1 = 0.002 */ {{1, 1}, {{1, 32, 80, 80}}, 4, 1, {1, 1}, {1, 1}, {0, 0}, {0, 0}, "SAME", true, 1664000.}, - /* GFLOPS 0.002 x 1 = 0.002 */ {{1, 1}, {{1, 256, 5, 5}}, 128, 1, {1, 1}, {1, 1}, {0, 0}, {0, 0}, "", true, 1641600.}, + /* GFLOPS 0.001 x 2 = 0.002 */ {{1, 1}, {{1, 16, 4, 8400}}, 1, 1, {1, 1}, {1, 1}, {0, 0}, {0, 0}, "", false, 1108800.}, + /* GFLOPS 0.001 x 2 = 0.002 */ {{1, 1}, {{1, 56, 16, 16}}, 32, 1, {1, 1}, {1, 1}, {0, 0}, {0, 0}, "", true, 925696.}, + /* GFLOPS 0.001 x 2 = 0.002 */ {{1, 1}, {{1, 64, 40, 40}}, 4, 1, {1, 1}, {1, 1}, {0, 0}, {0, 0}, "", true, 825600.}, + /* GFLOPS 0.001 x 4 = 0.002 */ {{1, 1}, {{1, 64, 12, 12}}, 32, 1, {1, 1}, {1, 1}, {0, 0}, {0, 0}, "SAME", true, 594432.}, + /* GFLOPS 0.000 x 8 = 0.002 */ {{1, 1}, {{1, 192, 2, 2}}, 192, 1, {1, 1}, {1, 1}, {0, 0}, {0, 0}, "", true, 295680.}, /* GFLOPS 0.001 x 1 = 0.001 */ {{1, 1}, {{1, 640, 6, 6}}, 32, 1, {1, 1}, {1, 1}, {0, 0}, {0, 0}, "", true, 1475712.}, - /* GFLOPS 0.001 x 1 = 0.001 */ {{3, 3}, {{1, 128, 5, 5}}, 24, 1, {1, 1}, {1, 1}, {1, 1}, {0, 0}, "", true, 1383000.}, - /* GFLOPS 0.001 x 1 = 0.001 */ {{3, 3}, {{1, 64, 5, 5}}, 128, 1, {1, 1}, {1, 1}, {0, 0}, {0, 0}, "", true, 1328256.}, - /* GFLOPS 0.001 x 1 = 0.001 */ {{1, 1}, {{1, 736, 3, 3}}, 96, 1, {1, 1}, {1, 1}, {0, 0}, {0, 0}, "", true, 1272672.}, - /* GFLOPS 0.001 x 1 = 0.001 */ {{1, 1}, {{1, 64, 16, 16}}, 36, 1, {1, 1}, {1, 1}, {0, 0}, {0, 0}, "VALID", true, 1188864.}, - /* GFLOPS 0.000 x 9 = 0.001 */ {{1, 1}, {{1, 64, 4, 4}}, 64, 1, {1, 1}, {1, 1}, {0, 0}, {0, 0}, "VALID", true, 132096.}, - /* GFLOPS 0.001 x 2 = 0.001 */ {{1, 1}, {{1, 256, 3, 3}}, 128, 1, {1, 1}, {1, 1}, {0, 0}, {0, 0}, "", true, 590976.}, - /* GFLOPS 0.001 x 2 = 0.001 */ {{1, 1}, {{1, 256, 3, 3}}, 128, 1, {1, 1}, {1, 1}, {0, 0}, {0, 0}, "SAME", false, 590976.}, - /* GFLOPS 0.001 x 1 = 0.001 */ {{3, 3}, {{1, 128, 3, 3}}, 128, 1, {2, 2}, {1, 1}, {0, 0}, {0, 0}, "SAME", false, 1180160.}, /* GFLOPS 0.001 x 1 = 0.001 */ {{1, 1}, {{1, 256, 2, 2}}, 546, 1, {1, 1}, {1, 1}, {0, 0}, {0, 0}, "SAME", true, 1120392.}, /* GFLOPS 0.001 x 1 = 0.001 */ {{1, 1}, {{1, 192, 12, 12}}, 16, 1, {1, 1}, {1, 1}, {0, 0}, {0, 0}, "", true, 887040.}, - /* GFLOPS 0.000 x 2 = 0.001 */ {{3, 3}, {{1, 256, 2, 2}}, 24, 1, {1, 1}, {1, 1}, {0, 0}, {0, 0}, "SAME", true, 442464.}, - /* GFLOPS 0.000 x 2 = 0.001 */ {{1, 1}, {{1, 32, 80, 80}}, 1, 1, {1, 1}, {1, 1}, {0, 0}, {0, 0}, "SAME", true, 416000.}, - /* GFLOPS 0.001 x 1 = 0.001 */ {{3, 3}, {{1, 128, 5, 5}}, 12, 1, {1, 1}, {1, 1}, {1, 1}, {0, 0}, "", true, 691500.}, - /* GFLOPS 0.001 x 1 = 0.001 */ {{3, 3}, {{1, 256, 3, 3}}, 16, 1, {1, 1}, {1, 1}, {1, 1}, {0, 0}, "", true, 663696.}, /* GFLOPS 0.001 x 1 = 0.001 */ {{1, 1}, {{1, 640, 2, 2}}, 128, 1, {1, 1}, {1, 1}, {0, 0}, {0, 0}, "", true, 655872.}, /* GFLOPS 0.001 x 1 = 0.001 */ {{1, 1}, {{1, 512, 5, 5}}, 24, 1, {1, 1}, {1, 1}, {0, 0}, {0, 0}, "", true, 615000.}, - /* GFLOPS 0.001 x 1 = 0.001 */ {{1, 1}, {{1, 512, 5, 5}}, 24, 1, {1, 1}, {1, 1}, {0, 0}, {0, 0}, "SAME", true, 615000.}, - /* GFLOPS 0.001 x 1 = 0.001 */ {{1, 1}, {{1, 128, 3, 3}}, 256, 1, {1, 1}, {1, 1}, {0, 0}, {0, 0}, "SAME", false, 592128.}, - /* GFLOPS 0.001 x 1 = 0.001 */ {{1, 1}, {{1, 256, 3, 3}}, 128, 1, {1, 1}, {1, 1}, {0, 0}, {0, 0}, "SAME", true, 590976.}, - /* GFLOPS 0.001 x 1 = 0.001 */ {{3, 3}, {{1, 128, 3, 3}}, 256, 1, {1, 1}, {1, 1}, {0, 0}, {0, 0}, "", true, 590080.}, - /* GFLOPS 0.001 x 1 = 0.001 */ {{1, 1}, {{1, 256, 3, 3}}, 126, 1, {1, 1}, {1, 1}, {0, 0}, {0, 0}, "", true, 581742.}, - /* GFLOPS 0.001 x 1 = 0.001 */ {{1, 1}, {{1, 256, 4, 4}}, 64, 1, {1, 1}, {1, 1}, {0, 0}, {0, 0}, "", true, 525312.}, - /* GFLOPS 0.000 x 4 = 0.000 */ {{1, 1}, {{1, 48, 1, 1}}, 1152, 1, {1, 1}, {1, 1}, {0, 0}, {0, 0}, "SAME", true, 111744.}, - /* GFLOPS 0.000 x 4 = 0.000 */ {{1, 1}, {{1, 1152, 1, 1}}, 48, 1, {1, 1}, {1, 1}, {0, 0}, {0, 0}, "SAME", true, 110640.}, - /* GFLOPS 0.000 x 1 = 0.000 */ {{1, 1}, {{1, 128, 5, 5}}, 64, 1, {1, 1}, {1, 1}, {0, 0}, {0, 0}, "", true, 411200.}, - /* GFLOPS 0.000 x 1 = 0.000 */ {{3, 3}, {{1, 128, 3, 3}}, 16, 1, {1, 1}, {1, 1}, {1, 1}, {0, 0}, "", true, 331920.}, + /* GFLOPS 0.001 x 2 = 0.001 */ {{1, 1}, {{1, 256, 3, 3}}, 128, 1, {1, 1}, {1, 1}, {0, 0}, {0, 0}, "", true, 590976.}, + /* GFLOPS 0.001 x 2 = 0.001 */ {{1, 1}, {{1, 64, 20, 20}}, 10, 1, {1, 1}, {1, 1}, {0, 0}, {0, 0}, "", true, 516000.}, + /* GFLOPS 0.000 x 2 = 0.001 */ {{1, 1}, {{1, 256, 12, 12}}, 6, 1, {1, 1}, {1, 1}, {0, 0}, {0, 0}, "", true, 443232.}, + /* GFLOPS 0.000 x 2 = 0.001 */ {{1, 1}, {{1, 32, 80, 80}}, 1, 1, {1, 1}, {1, 1}, {0, 0}, {0, 0}, "SAME", true, 416000.}, + /* GFLOPS 0.000 x 2 = 0.001 */ {{1, 1}, {{1, 128, 40, 40}}, 1, 1, {1, 1}, {1, 1}, {0, 0}, {0, 0}, "", true, 411200.}, + /* GFLOPS 0.000 x 2 = 0.001 */ {{1, 1}, {{1, 128, 20, 20}}, 4, 1, {1, 1}, {1, 1}, {0, 0}, {0, 0}, "", true, 411200.}, + /* GFLOPS 0.000 x 4 = 0.001 */ {{1, 1}, {{1, 64, 12, 12}}, 16, 1, {1, 1}, {1, 1}, {0, 0}, {0, 0}, "SAME", true, 297216.}, + /* GFLOPS 0.000 x 2 = 0.001 */ {{1, 1}, {{1, 128, 6, 6}}, 32, 1, {1, 1}, {1, 1}, {0, 0}, {0, 0}, "SAME", true, 296064.}, + /* GFLOPS 0.000 x 2 = 0.001 */ {{1, 1}, {{1, 128, 24, 24}}, 2, 1, {1, 1}, {1, 1}, {0, 0}, {0, 0}, "", true, 296064.}, + /* GFLOPS 0.000 x 4 = 0.001 */ {{1, 1}, {{1, 64, 40, 40}}, 1, 1, {1, 1}, {1, 1}, {0, 0}, {0, 0}, "", true, 206400.}, + /* GFLOPS 0.000 x 9 = 0.001 */ {{1, 1}, {{1, 64, 4, 4}}, 64, 1, {1, 1}, {1, 1}, {0, 0}, {0, 0}, "VALID", true, 132096.}, /* GFLOPS 0.000 x 1 = 0.000 */ {{1, 1}, {{1, 192, 5, 5}}, 32, 1, {1, 1}, {1, 1}, {0, 0}, {0, 0}, "", true, 308000.}, - /* GFLOPS 0.000 x 1 = 0.000 */ {{1, 1}, {{1, 64, 8, 8}}, 36, 1, {1, 1}, {1, 1}, {0, 0}, {0, 0}, "VALID", true, 297216.}, /* GFLOPS 0.000 x 1 = 0.000 */ {{1, 1}, {{1, 128, 2, 2}}, 256, 1, {1, 1}, {1, 1}, {0, 0}, {0, 0}, "SAME", false, 263168.}, - /* GFLOPS 0.000 x 2 = 0.000 */ {{1, 1}, {{1, 256, 2, 2}}, 64, 1, {1, 1}, {1, 1}, {0, 0}, {0, 0}, "SAME", false, 131328.}, /* GFLOPS 0.000 x 1 = 0.000 */ {{1, 1}, {{1, 256, 2, 2}}, 126, 1, {1, 1}, {1, 1}, {0, 0}, {0, 0}, "", true, 258552.}, + /* GFLOPS 0.000 x 2 = 0.000 */ {{1, 1}, {{1, 64, 20, 20}}, 4, 1, {1, 1}, {1, 1}, {0, 0}, {0, 0}, "", true, 206400.}, /* GFLOPS 0.000 x 1 = 0.000 */ {{1, 1}, {{1, 1024, 1, 1}}, 96, 1, {1, 1}, {1, 1}, {0, 0}, {0, 0}, "", true, 196704.}, - /* GFLOPS 0.000 x 1 = 0.000 */ {{3, 3}, {{1, 128, 3, 3}}, 8, 1, {1, 1}, {1, 1}, {1, 1}, {0, 0}, "", true, 165960.}, /* GFLOPS 0.000 x 1 = 0.000 */ {{1, 1}, {{1, 128, 3, 3}}, 64, 1, {1, 1}, {1, 1}, {0, 0}, {0, 0}, "", true, 148032.}, - /* GFLOPS 0.000 x 1 = 0.000 */ {{3, 3}, {{1, 64, 3, 3}}, 128, 1, {1, 1}, {1, 1}, {0, 0}, {0, 0}, "", true, 147584.}, - /* GFLOPS 0.000 x 1 = 0.000 */ {{3, 3}, {{1, 64, 2, 2}}, 128, 1, {2, 2}, {1, 1}, {1, 1}, {0, 0}, "", true, 147584.}, - /* GFLOPS 0.000 x 1 = 0.000 */ {{3, 3}, {{1, 64, 2, 2}}, 128, 1, {2, 2}, {1, 1}, {0, 0}, {0, 0}, "SAME", true, 147584.}, - /* GFLOPS 0.000 x 1 = 0.000 */ {{3, 3}, {{1, 64, 2, 2}}, 128, 1, {2, 2}, {1, 1}, {0, 0}, {0, 0}, "SAME", false, 147584.}, + /* GFLOPS 0.000 x 2 = 0.000 */ {{1, 1}, {{1, 128, 6, 6}}, 16, 1, {1, 1}, {1, 1}, {0, 0}, {0, 0}, "SAME", true, 148032.}, /* GFLOPS 0.000 x 1 = 0.000 */ {{1, 1}, {{1, 736, 1, 1}}, 96, 1, {1, 1}, {1, 1}, {0, 0}, {0, 0}, "", true, 141408.}, /* GFLOPS 0.000 x 1 = 0.000 */ {{1, 1}, {{1, 128, 1, 1}}, 546, 1, {1, 1}, {1, 1}, {0, 0}, {0, 0}, "SAME", true, 140322.}, - /* GFLOPS 0.000 x 1 = 0.000 */ {{1, 1}, {{1, 256, 2, 2}}, 64, 1, {1, 1}, {1, 1}, {0, 0}, {0, 0}, "", true, 131328.}, - /* GFLOPS 0.000 x 1 = 0.000 */ {{1, 1}, {{1, 256, 2, 2}}, 64, 1, {1, 1}, {1, 1}, {0, 0}, {0, 0}, "SAME", true, 131328.}, + /* GFLOPS 0.000 x 2 = 0.000 */ {{1, 1}, {{1, 256, 2, 2}}, 64, 1, {1, 1}, {1, 1}, {0, 0}, {0, 0}, "SAME", false, 131328.}, + /* GFLOPS 0.000 x 4 = 0.000 */ {{1, 1}, {{1, 48, 1, 1}}, 1152, 1, {1, 1}, {1, 1}, {0, 0}, {0, 0}, "SAME", true, 111744.}, + /* GFLOPS 0.000 x 4 = 0.000 */ {{1, 1}, {{1, 1152, 1, 1}}, 48, 1, {1, 1}, {1, 1}, {0, 0}, {0, 0}, "SAME", true, 110640.}, + /* GFLOPS 0.000 x 2 = 0.000 */ {{1, 1}, {{1, 128, 20, 20}}, 1, 1, {1, 1}, {1, 1}, {0, 0}, {0, 0}, "", true, 102800.}, + /* GFLOPS 0.000 x 1 = 0.000 */ {{1, 1}, {{1, 64, 4, 4}}, 36, 1, {1, 1}, {1, 1}, {0, 0}, {0, 0}, "VALID", true, 74304.}, + /* GFLOPS 0.000 x 4 = 0.000 */ {{1, 1}, {{1, 64, 20, 20}}, 1, 1, {1, 1}, {1, 1}, {0, 0}, {0, 0}, "", true, 51600.}, + /* GFLOPS 0.000 x 1 = 0.000 */ {{1, 1}, {{1, 256, 2, 2}}, 24, 1, {1, 1}, {1, 1}, {0, 0}, {0, 0}, "", true, 49248.}, /* GFLOPS 0.000 x 3 = 0.000 */ {{1, 1}, {{1, 28, 1, 1}}, 672, 1, {1, 1}, {1, 1}, {0, 0}, {0, 0}, "SAME", true, 38304.}, /* GFLOPS 0.000 x 3 = 0.000 */ {{1, 1}, {{1, 672, 1, 1}}, 28, 1, {1, 1}, {1, 1}, {0, 0}, {0, 0}, "SAME", true, 37660.}, - /* GFLOPS 0.000 x 1 = 0.000 */ {{1, 1}, {{1, 256, 3, 3}}, 24, 1, {1, 1}, {1, 1}, {0, 0}, {0, 0}, "", true, 110808.}, - /* GFLOPS 0.000 x 1 = 0.000 */ {{1, 1}, {{1, 256, 3, 3}}, 24, 1, {1, 1}, {1, 1}, {0, 0}, {0, 0}, "SAME", true, 110808.}, - /* GFLOPS 0.000 x 2 = 0.000 */ {{3, 3}, {{1, 128, 1, 1}}, 24, 1, {1, 1}, {1, 1}, {0, 0}, {0, 0}, "SAME", true, 55320.}, - /* GFLOPS 0.000 x 1 = 0.000 */ {{1, 1}, {{1, 64, 4, 4}}, 36, 1, {1, 1}, {1, 1}, {0, 0}, {0, 0}, "VALID", true, 74304.}, - /* GFLOPS 0.000 x 1 = 0.000 */ {{3, 3}, {{1, 64, 2, 2}}, 64, 1, {2, 2}, {1, 1}, {0, 0}, {0, 0}, "SAME", false, 73792.}, - /* GFLOPS 0.000 x 1 = 0.000 */ {{3, 3}, {{1, 256, 1, 1}}, 16, 1, {1, 1}, {1, 1}, {1, 1}, {0, 0}, "", true, 73744.}, + /* GFLOPS 0.000 x 1 = 0.000 */ {{1, 1}, {{1, 128, 1, 1}}, 126, 1, {1, 1}, {1, 1}, {0, 0}, {0, 0}, "", true, 32382.}, /* GFLOPS 0.000 x 3 = 0.000 */ {{1, 1}, {{1, 20, 1, 1}}, 480, 1, {1, 1}, {1, 1}, {0, 0}, {0, 0}, "SAME", true, 19680.}, /* GFLOPS 0.000 x 3 = 0.000 */ {{1, 1}, {{1, 480, 1, 1}}, 20, 1, {1, 1}, {1, 1}, {0, 0}, {0, 0}, "SAME", true, 19220.}, - /* GFLOPS 0.000 x 1 = 0.000 */ {{1, 1}, {{1, 256, 2, 2}}, 24, 1, {1, 1}, {1, 1}, {0, 0}, {0, 0}, "", true, 49248.}, - /* GFLOPS 0.000 x 1 = 0.000 */ {{1, 1}, {{1, 256, 2, 2}}, 24, 1, {1, 1}, {1, 1}, {0, 0}, {0, 0}, "SAME", true, 49248.}, - /* GFLOPS 0.000 x 1 = 0.000 */ {{3, 3}, {{1, 128, 1, 1}}, 16, 1, {1, 1}, {1, 1}, {1, 1}, {0, 0}, "", true, 36880.}, - /* GFLOPS 0.000 x 1 = 0.000 */ {{1, 1}, {{1, 128, 1, 1}}, 126, 1, {1, 1}, {1, 1}, {0, 0}, {0, 0}, "", true, 32382.}, - /* GFLOPS 0.000 x 1 = 0.000 */ {{3, 3}, {{1, 128, 1, 1}}, 8, 1, {1, 1}, {1, 1}, {1, 1}, {0, 0}, "", true, 18440.}, /* GFLOPS 0.000 x 1 = 0.000 */ {{1, 1}, {{1, 64, 1, 1}}, 128, 1, {1, 1}, {1, 1}, {0, 0}, {0, 0}, "SAME", false, 16512.}, + /* GFLOPS 0.000 x 1 = 0.000 */ {{1, 1}, {{1, 128, 1, 1}}, 24, 1, {1, 1}, {1, 1}, {0, 0}, {0, 0}, "", true, 6168.}, /* GFLOPS 0.000 x 2 = 0.000 */ {{1, 1}, {{1, 10, 1, 1}}, 240, 1, {1, 1}, {1, 1}, {0, 0}, {0, 0}, "SAME", true, 5040.}, /* GFLOPS 0.000 x 2 = 0.000 */ {{1, 1}, {{1, 240, 1, 1}}, 10, 1, {1, 1}, {1, 1}, {0, 0}, {0, 0}, "SAME", true, 4810.}, - /* GFLOPS 0.000 x 1 = 0.000 */ {{1, 1}, {{1, 128, 1, 1}}, 24, 1, {1, 1}, {1, 1}, {0, 0}, {0, 0}, "", true, 6168.}, - /* GFLOPS 0.000 x 1 = 0.000 */ {{1, 1}, {{1, 128, 1, 1}}, 24, 1, {1, 1}, {1, 1}, {0, 0}, {0, 0}, "SAME", true, 6168.}, + /* GFLOPS 0.000 x 8 = 0.000 */ {{1, 1}, {{1, 24, 1, 1}}, 96, 1, {1, 1}, {1, 1}, {0, 0}, {0, 0}, "", true, 4704.}, + /* GFLOPS 0.000 x 8 = 0.000 */ {{1, 1}, {{1, 96, 1, 1}}, 24, 1, {1, 1}, {1, 1}, {0, 0}, {0, 0}, "", true, 4632.}, + /* GFLOPS 0.000 x 4 = 0.000 */ {{1, 1}, {{1, 4, 16, 16}}, 2, 1, {1, 1}, {1, 1}, {0, 0}, {0, 0}, "", true, 4608.}, + /* GFLOPS 0.000 x 2 = 0.000 */ {{1, 1}, {{1, 4, 16, 16}}, 1, 1, {1, 1}, {1, 1}, {0, 0}, {0, 0}, "", true, 2304.}, /* GFLOPS 0.000 x 2 = 0.000 */ {{1, 1}, {{1, 6, 1, 1}}, 144, 1, {1, 1}, {1, 1}, {0, 0}, {0, 0}, "SAME", true, 1872.}, /* GFLOPS 0.000 x 2 = 0.000 */ {{1, 1}, {{1, 144, 1, 1}}, 6, 1, {1, 1}, {1, 1}, {0, 0}, {0, 0}, "SAME", true, 1734.}, /* GFLOPS 0.000 x 1 = 0.000 */ {{1, 1}, {{1, 4, 1, 1}}, 96, 1, {1, 1}, {1, 1}, {0, 0}, {0, 0}, "SAME", true, 864.}, /* GFLOPS 0.000 x 1 = 0.000 */ {{1, 1}, {{1, 96, 1, 1}}, 4, 1, {1, 1}, {1, 1}, {0, 0}, {0, 0}, "SAME", true, 772.}, /* GFLOPS 0.000 x 1 = 0.000 */ {{1, 1}, {{1, 8, 1, 1}}, 32, 1, {1, 1}, {1, 1}, {0, 0}, {0, 0}, "SAME", true, 544.}, /* GFLOPS 0.000 x 1 = 0.000 */ {{1, 1}, {{1, 32, 1, 1}}, 8, 1, {1, 1}, {1, 1}, {0, 0}, {0, 0}, "SAME", true, 520.}, - /* GFLOPS 0.472 x 1 = 0.472 */ {{5, 5}, {{1, 32, 96, 96}}, 32, 32, {1, 1}, {1, 1}, {2, 2}, {0, 0}, "", true, 472154112.} + /* GFLOPS 0.000 x 8 = 0.000 */ {{1, 1}, {{1, 6, 1, 1}}, 24, 1, {1, 1}, {1, 1}, {0, 0}, {0, 0}, "", true, 312.}, + /* GFLOPS 0.000 x 8 = 0.000 */ {{1, 1}, {{1, 24, 1, 1}}, 6, 1, {1, 1}, {1, 1}, {0, 0}, {0, 0}, "", true, 294.}, }; -struct ConvParamID + +static const ConvParam_t testConvolution_3x3S1D1_Configs[] = { + /* GFLOPS 1.596 x 14 = 22.338 */ {{3, 3}, {{1, 128, 52, 52}}, 256, 1, {1, 1}, {1, 1}, {1, 1}, {0, 0}, "", false, 1595576320.}, + /* GFLOPS 1.595 x 12 = 19.141 */ {{3, 3}, {{1, 512, 13, 13}}, 1024, 1, {1, 1}, {1, 1}, {1, 1}, {0, 0}, "", false, 1595057152.}, + /* GFLOPS 6.814 x 2 = 13.629 */ {{3, 3}, {{1, 512, 38, 38}}, 512, 1, {1, 1}, {1, 1}, {1, 1}, {0, 0}, "", true, 6814386176.}, + /* GFLOPS 6.637 x 2 = 13.274 */ {{3, 3}, {{1, 256, 75, 75}}, 256, 1, {1, 1}, {1, 1}, {1, 1}, {0, 0}, "", true, 6636960000.}, + /* GFLOPS 10.701 x 1 = 10.701 */ {{3, 3}, {{1, 512, 38, 38}}, 804, 1, {1, 1}, {1, 1}, {1, 1}, {0, 0}, "", true, 10700715792.}, + /* GFLOPS 10.087 x 1 = 10.087 */ {{3, 3}, {{1, 576, 38, 50}}, 512, 1, {1, 1}, {1, 1}, {0, 0}, {0, 0}, "SAME", true, 10086963200.}, + /* GFLOPS 9.993 x 1 = 9.993 */ {{3, 3}, {{1, 64, 368, 368}}, 64, 1, {1, 1}, {1, 1}, {1, 1}, {0, 0}, "", true, 9993207808.}, + /* GFLOPS 9.989 x 1 = 9.989 */ {{3, 3}, {{1, 128, 184, 184}}, 128, 1, {1, 1}, {1, 1}, {1, 1}, {0, 0}, "", true, 9988874240.}, + /* GFLOPS 4.247 x 2 = 8.494 */ {{3, 3}, {{1, 480, 32, 32}}, 480, 1, {1, 1}, {1, 1}, {0, 0}, {0, 0}, "SAME", false, 4247224320.}, + /* GFLOPS 8.025 x 1 = 8.025 */ {{3, 3}, {{1, 1024, 19, 19}}, 1206, 1, {1, 1}, {1, 1}, {1, 1}, {0, 0}, "", true, 8025101478.}, + /* GFLOPS 6.641 x 1 = 6.641 */ {{3, 3}, {{1, 64, 300, 300}}, 64, 1, {1, 1}, {1, 1}, {1, 1}, {0, 0}, "", true, 6641280000.}, + /* GFLOPS 6.641 x 1 = 6.641 */ {{3, 3}, {{1, 64, 150, 200}}, 192, 1, {1, 1}, {1, 1}, {0, 0}, {0, 0}, "SAME", true, 6641280000.}, + /* GFLOPS 6.638 x 1 = 6.638 */ {{3, 3}, {{1, 128, 150, 150}}, 128, 1, {1, 1}, {1, 1}, {1, 1}, {0, 0}, "", true, 6638400000.}, + /* GFLOPS 6.118 x 1 = 6.118 */ {{3, 3}, {{1, 144, 128, 128}}, 144, 1, {1, 1}, {1, 1}, {0, 0}, {0, 0}, "SAME", false, 6117654528.}, + /* GFLOPS 6.116 x 1 = 6.116 */ {{3, 3}, {{1, 1152, 16, 16}}, 1152, 1, {1, 1}, {1, 1}, {0, 0}, {0, 0}, "SAME", false, 6115590144.}, + /* GFLOPS 4.997 x 1 = 4.997 */ {{3, 3}, {{1, 64, 184, 184}}, 128, 1, {1, 1}, {1, 1}, {1, 1}, {0, 0}, "", true, 4996603904.}, + /* GFLOPS 4.993 x 1 = 4.993 */ {{3, 3}, {{1, 512, 46, 46}}, 256, 1, {1, 1}, {1, 1}, {1, 1}, {0, 0}, "", true, 4992812032.}, + /* GFLOPS 3.408 x 1 = 3.408 */ {{3, 3}, {{1, 256, 38, 38}}, 512, 1, {1, 1}, {1, 1}, {1, 1}, {0, 0}, "", true, 3407562752.}, + /* GFLOPS 0.302 x 11 = 3.325 */ {{3, 3}, {{1, 64, 64, 64}}, 64, 1, {1, 1}, {1, 1}, {0, 0}, {0, 0}, "SAME", false, 302252032.}, + /* GFLOPS 3.321 x 1 = 3.321 */ {{3, 3}, {{1, 64, 150, 150}}, 128, 1, {1, 1}, {1, 1}, {1, 1}, {0, 0}, "", true, 3320640000.}, + /* GFLOPS 0.830 x 4 = 3.321 */ {{3, 3}, {{1, 64, 75, 100}}, 96, 1, {1, 1}, {1, 1}, {0, 0}, {0, 0}, "SAME", true, 830160000.}, + /* GFLOPS 3.319 x 1 = 3.319 */ {{3, 3}, {{1, 128, 75, 75}}, 256, 1, {1, 1}, {1, 1}, {1, 1}, {0, 0}, "", true, 3319200000.}, + /* GFLOPS 1.598 x 2 = 3.195 */ {{3, 3}, {{1, 32, 208, 208}}, 64, 1, {1, 1}, {1, 1}, {1, 1}, {0, 0}, "", false, 1597652992.}, + /* GFLOPS 1.596 x 2 = 3.193 */ {{3, 3}, {{1, 64, 104, 104}}, 128, 1, {1, 1}, {1, 1}, {1, 1}, {0, 0}, "", false, 1596268544.}, + /* GFLOPS 1.405 x 2 = 2.810 */ {{3, 3}, {{1, 96, 184, 184}}, 24, 1, {1, 1}, {1, 1}, {1, 1}, {0, 0}, "", true, 1404888576.}, + /* GFLOPS 0.798 x 3 = 2.394 */ {{3, 3}, {{1, 64, 104, 104}}, 64, 1, {1, 1}, {1, 1}, {1, 1}, {0, 0}, "", false, 798134272.}, + /* GFLOPS 2.255 x 1 = 2.255 */ {{3, 3}, {{1, 128, 80, 100}}, 128, 1, {1, 1}, {1, 1}, {0, 0}, {0, 0}, "", true, 2255285760.}, + /* GFLOPS 2.153 x 1 = 2.153 */ {{3, 3}, {{1, 128, 78, 98}}, 128, 1, {1, 1}, {1, 1}, {0, 0}, {0, 0}, "", true, 2152611840.}, + /* GFLOPS 2.052 x 1 = 2.052 */ {{3, 3}, {{1, 128, 76, 96}}, 128, 1, {1, 1}, {1, 1}, {0, 0}, {0, 0}, "", true, 2052298240.}, + /* GFLOPS 1.022 x 2 = 2.044 */ {{3, 3}, {{1, 576, 19, 19}}, 273, 1, {1, 1}, {1, 1}, {0, 0}, {0, 0}, "SAME", true, 1021896057.}, + /* GFLOPS 1.954 x 1 = 1.954 */ {{3, 3}, {{1, 128, 74, 94}}, 128, 1, {1, 1}, {1, 1}, {0, 0}, {0, 0}, "", true, 1954344960.}, + /* GFLOPS 1.888 x 1 = 1.888 */ {{3, 3}, {{1, 1024, 10, 10}}, 1024, 1, {1, 1}, {1, 1}, {0, 0}, {0, 0}, "SAME", false, 1887539200.}, + /* GFLOPS 1.859 x 1 = 1.859 */ {{3, 3}, {{1, 128, 72, 92}}, 128, 1, {1, 1}, {1, 1}, {0, 0}, {0, 0}, "", true, 1858752000.}, + /* GFLOPS 1.766 x 1 = 1.766 */ {{3, 3}, {{1, 128, 70, 90}}, 128, 1, {1, 1}, {1, 1}, {0, 0}, {0, 0}, "", true, 1765519360.}, + /* GFLOPS 1.704 x 1 = 1.704 */ {{3, 3}, {{1, 256, 38, 38}}, 256, 1, {1, 1}, {1, 1}, {0, 0}, {0, 0}, "SAME", false, 1703781376.}, + /* GFLOPS 1.675 x 1 = 1.675 */ {{3, 3}, {{1, 128, 68, 88}}, 128, 1, {1, 1}, {1, 1}, {0, 0}, {0, 0}, "", true, 1674647040.}, + /* GFLOPS 1.660 x 1 = 1.660 */ {{3, 3}, {{1, 128, 75, 75}}, 128, 1, {1, 1}, {1, 1}, {0, 0}, {0, 0}, "SAME", false, 1659600000.}, + /* GFLOPS 1.586 x 1 = 1.586 */ {{3, 3}, {{1, 128, 66, 86}}, 128, 1, {1, 1}, {1, 1}, {0, 0}, {0, 0}, "", true, 1586135040.}, + /* GFLOPS 1.500 x 1 = 1.500 */ {{3, 3}, {{1, 128, 64, 84}}, 128, 1, {1, 1}, {1, 1}, {0, 0}, {0, 0}, "", true, 1499983360.}, + /* GFLOPS 0.711 x 2 = 1.422 */ {{3, 3}, {{1, 12, 320, 320}}, 32, 1, {1, 1}, {1, 1}, {1, 1}, {0, 0}, "", true, 711065600.}, + /* GFLOPS 1.416 x 1 = 1.416 */ {{3, 3}, {{1, 128, 62, 82}}, 128, 1, {1, 1}, {1, 1}, {0, 0}, {0, 0}, "", true, 1416192000.}, + /* GFLOPS 0.701 x 2 = 1.401 */ {{3, 3}, {{1, 128, 38, 50}}, 160, 1, {1, 1}, {1, 1}, {0, 0}, {0, 0}, "SAME", true, 700720000.}, + /* GFLOPS 0.231 x 6 = 1.388 */ {{3, 3}, {{1, 128, 56, 56}}, 32, 1, {1, 1}, {1, 1}, {1, 1}, {0, 0}, "", false, 231311360.}, + /* GFLOPS 0.231 x 6 = 1.388 */ {{3, 3}, {{1, 256, 14, 14}}, 256, 1, {1, 1}, {1, 1}, {1, 1}, {0, 0}, "", false, 231261184.}, + /* GFLOPS 0.420 x 3 = 1.261 */ {{3, 3}, {{1, 96, 38, 50}}, 128, 1, {1, 1}, {1, 1}, {0, 0}, {0, 0}, "SAME", true, 420492800.}, + /* GFLOPS 1.258 x 1 = 1.258 */ {{3, 3}, {{1, 1280, 10, 10}}, 546, 1, {1, 1}, {1, 1}, {0, 0}, {0, 0}, "SAME", true, 1258038600.}, + /* GFLOPS 1.248 x 1 = 1.248 */ {{3, 3}, {{1, 256, 46, 46}}, 128, 1, {1, 1}, {1, 1}, {1, 1}, {0, 0}, "", true, 1248338432.}, + /* GFLOPS 1.245 x 1 = 1.245 */ {{3, 3}, {{1, 64, 75, 75}}, 192, 1, {1, 1}, {1, 1}, {0, 0}, {0, 0}, "SAME", true, 1245240000.}, + /* GFLOPS 1.210 x 1 = 1.210 */ {{3, 3}, {{1, 32, 256, 256}}, 32, 1, {1, 1}, {1, 1}, {0, 0}, {0, 0}, "SAME", false, 1210056704.}, + /* GFLOPS 1.196 x 1 = 1.196 */ {{3, 3}, {{1, 384, 26, 26}}, 256, 1, {1, 1}, {1, 1}, {1, 1}, {0, 0}, "", false, 1196336128.}, + /* GFLOPS 0.590 x 2 = 1.181 */ {{3, 3}, {{1, 64, 80, 80}}, 80, 1, {1, 1}, {1, 1}, {1, 1}, {0, 0}, "", true, 590336000.}, + /* GFLOPS 0.561 x 2 = 1.121 */ {{3, 3}, {{1, 128, 38, 50}}, 128, 1, {1, 1}, {1, 1}, {0, 0}, {0, 0}, "SAME", true, 560576000.}, + /* GFLOPS 1.112 x 1 = 1.112 */ {{3, 3}, {{1, 512, 10, 10}}, 1206, 1, {1, 1}, {1, 1}, {1, 1}, {0, 0}, "", true, 1111570200.}, + /* GFLOPS 0.076 x 14 = 1.058 */ {{3, 3}, {{1, 64, 32, 32}}, 64, 1, {1, 1}, {1, 1}, {0, 0}, {0, 0}, "SAME", false, 75563008.}, + /* GFLOPS 1.051 x 1 = 1.051 */ {{3, 3}, {{1, 160, 38, 50}}, 192, 1, {1, 1}, {1, 1}, {0, 0}, {0, 0}, "SAME", true, 1050988800.}, + /* GFLOPS 1.006 x 1 = 1.006 */ {{3, 3}, {{1, 1024, 10, 10}}, 546, 1, {1, 1}, {1, 1}, {0, 0}, {0, 0}, "SAME", true, 1006441800.}, + /* GFLOPS 0.473 x 2 = 0.945 */ {{3, 3}, {{1, 32, 160, 160}}, 32, 1, {1, 1}, {1, 1}, {1, 1}, {0, 0}, "", true, 472678400.}, + /* GFLOPS 0.472 x 2 = 0.944 */ {{3, 3}, {{1, 512, 4, 25}}, 512, 1, {1, 1}, {1, 1}, {1, 1}, {0, 0}, "", false, 471910400.}, + /* GFLOPS 0.841 x 1 = 0.841 */ {{3, 3}, {{1, 128, 38, 50}}, 192, 1, {1, 1}, {1, 1}, {0, 0}, {0, 0}, "SAME", true, 840864000.}, + /* GFLOPS 0.415 x 2 = 0.831 */ {{3, 3}, {{1, 32, 150, 150}}, 32, 1, {1, 1}, {1, 1}, {0, 0}, {0, 0}, "SAME", false, 415440000.}, + /* GFLOPS 0.118 x 6 = 0.710 */ {{3, 3}, {{1, 16, 160, 160}}, 16, 1, {1, 1}, {1, 1}, {1, 1}, {0, 0}, "", true, 118374400.}, + /* GFLOPS 0.351 x 2 = 0.702 */ {{3, 3}, {{1, 96, 92, 92}}, 24, 1, {1, 1}, {1, 1}, {1, 1}, {0, 0}, "", false, 351222144.}, + /* GFLOPS 0.694 x 1 = 0.694 */ {{3, 3}, {{1, 64, 56, 56}}, 192, 1, {1, 1}, {1, 1}, {1, 1}, {0, 0}, "", true, 694235136.}, + /* GFLOPS 0.231 x 3 = 0.694 */ {{3, 3}, {{1, 512, 7, 7}}, 512, 1, {1, 1}, {1, 1}, {1, 1}, {0, 0}, "", false, 231236096.}, + /* GFLOPS 0.160 x 4 = 0.639 */ {{3, 3}, {{1, 64, 38, 38}}, 96, 1, {1, 1}, {1, 1}, {0, 0}, {0, 0}, "SAME", true, 159833472.}, + /* GFLOPS 0.305 x 2 = 0.609 */ {{3, 3}, {{1, 3, 416, 416}}, 32, 1, {1, 1}, {1, 1}, {1, 1}, {0, 0}, "", false, 304578560.}, + /* GFLOPS 0.295 x 2 = 0.590 */ {{3, 3}, {{1, 128, 40, 40}}, 80, 1, {1, 1}, {1, 1}, {1, 1}, {0, 0}, "", true, 295040000.}, + /* GFLOPS 0.553 x 1 = 0.553 */ {{3, 3}, {{1, 64, 75, 100}}, 64, 1, {1, 1}, {1, 1}, {0, 0}, {0, 0}, "SAME", true, 553440000.}, + /* GFLOPS 0.477 x 1 = 0.477 */ {{3, 3}, {{1, 3, 368, 368}}, 64, 1, {1, 1}, {1, 1}, {1, 1}, {0, 0}, "", true, 476692480.}, + /* GFLOPS 0.236 x 2 = 0.472 */ {{3, 3}, {{1, 128, 40, 40}}, 64, 1, {1, 1}, {1, 1}, {1, 1}, {0, 0}, "", true, 236032000.}, + /* GFLOPS 0.236 x 2 = 0.472 */ {{3, 3}, {{1, 256, 8, 25}}, 256, 1, {1, 1}, {1, 1}, {1, 1}, {0, 0}, "", true, 235980800.}, + /* GFLOPS 0.236 x 2 = 0.472 */ {{3, 3}, {{1, 256, 4, 25}}, 512, 1, {1, 1}, {1, 1}, {1, 1}, {0, 0}, "", false, 235980800.}, + /* GFLOPS 0.449 x 1 = 0.449 */ {{3, 3}, {{1, 384, 13, 13}}, 384, 2, {1, 1}, {1, 1}, {1, 1}, {0, 0}, "", true, 448626048.}, + /* GFLOPS 0.426 x 1 = 0.426 */ {{3, 3}, {{1, 128, 38, 38}}, 128, 1, {1, 1}, {1, 1}, {1, 1}, {0, 0}, "", false, 426037760.}, + /* GFLOPS 0.399 x 1 = 0.399 */ {{3, 3}, {{1, 256, 13, 13}}, 512, 1, {1, 1}, {1, 1}, {1, 1}, {0, 0}, "", false, 398807552.}, + /* GFLOPS 0.200 x 2 = 0.399 */ {{3, 3}, {{1, 32, 104, 104}}, 32, 1, {1, 1}, {1, 1}, {1, 1}, {0, 0}, "", false, 199706624.}, + /* GFLOPS 0.319 x 1 = 0.319 */ {{3, 3}, {{1, 192, 19, 19}}, 256, 1, {1, 1}, {1, 1}, {0, 0}, {0, 0}, "SAME", true, 319482112.}, + /* GFLOPS 0.317 x 1 = 0.317 */ {{3, 3}, {{1, 3, 300, 300}}, 64, 1, {1, 1}, {1, 1}, {1, 1}, {0, 0}, "", true, 316800000.}, + /* GFLOPS 0.299 x 1 = 0.299 */ {{3, 3}, {{1, 256, 13, 13}}, 384, 1, {1, 1}, {1, 1}, {1, 1}, {0, 0}, "", true, 299105664.}, + /* GFLOPS 0.299 x 1 = 0.299 */ {{3, 3}, {{1, 384, 13, 13}}, 256, 2, {1, 1}, {1, 1}, {1, 1}, {0, 0}, "", true, 299084032.}, + /* GFLOPS 0.147 x 2 = 0.295 */ {{3, 3}, {{1, 256, 20, 20}}, 80, 1, {1, 1}, {1, 1}, {1, 1}, {0, 0}, "", true, 147488000.}, + /* GFLOPS 0.133 x 2 = 0.266 */ {{3, 3}, {{1, 128, 19, 19}}, 160, 1, {1, 1}, {1, 1}, {0, 0}, {0, 0}, "SAME", true, 133136800.}, + /* GFLOPS 0.038 x 7 = 0.265 */ {{3, 3}, {{1, 16, 64, 128}}, 16, 1, {1, 1}, {1, 1}, {1, 1}, {0, 0}, "", true, 37879808.}, + /* GFLOPS 0.011 x 24 = 0.256 */ {{3, 3}, {{1, 16, 48, 48}}, 16, 1, {1, 1}, {1, 1}, {1, 1}, {0, 0}, "SAME", true, 10653696.}, + /* GFLOPS 0.011 x 24 = 0.255 */ {{3, 3}, {{1, 32, 24, 24}}, 32, 1, {1, 1}, {1, 1}, {1, 1}, {0, 0}, "SAME", true, 10635264.}, + /* GFLOPS 0.126 x 2 = 0.252 */ {{3, 3}, {{1, 512, 5, 5}}, 546, 1, {1, 1}, {1, 1}, {0, 0}, {0, 0}, "SAME", true, 125812050.}, + /* GFLOPS 0.118 x 2 = 0.236 */ {{3, 3}, {{1, 64, 16, 50}}, 128, 1, {1, 1}, {1, 1}, {1, 1}, {0, 0}, "", true, 118067200.}, + /* GFLOPS 0.118 x 2 = 0.236 */ {{3, 3}, {{1, 128, 8, 25}}, 256, 1, {1, 1}, {1, 1}, {1, 1}, {0, 0}, "", true, 118016000.}, + /* GFLOPS 0.118 x 2 = 0.236 */ {{3, 3}, {{1, 256, 20, 20}}, 64, 1, {1, 1}, {1, 1}, {1, 1}, {0, 0}, "", true, 117990400.}, + /* GFLOPS 0.111 x 2 = 0.221 */ {{3, 3}, {{1, 192, 10, 10}}, 320, 1, {1, 1}, {1, 1}, {0, 0}, {0, 0}, "SAME", true, 110624000.}, + /* GFLOPS 0.213 x 1 = 0.213 */ {{3, 3}, {{1, 256, 19, 19}}, 128, 1, {1, 1}, {1, 1}, {1, 1}, {0, 0}, "", false, 212972672.}, + /* GFLOPS 0.213 x 1 = 0.213 */ {{3, 3}, {{1, 512, 38, 38}}, 16, 1, {1, 1}, {1, 1}, {1, 1}, {0, 0}, "", true, 212949568.}, + /* GFLOPS 0.210 x 1 = 0.210 */ {{3, 3}, {{1, 64, 38, 50}}, 96, 1, {1, 1}, {1, 1}, {0, 0}, {0, 0}, "SAME", true, 210307200.}, + /* GFLOPS 0.104 x 2 = 0.208 */ {{3, 3}, {{1, 32, 75, 75}}, 32, 1, {1, 1}, {1, 1}, {1, 1}, {0, 0}, "", false, 103860000.}, + /* GFLOPS 0.200 x 1 = 0.200 */ {{3, 3}, {{1, 160, 19, 19}}, 192, 1, {1, 1}, {1, 1}, {0, 0}, {0, 0}, "SAME", true, 199687872.}, + /* GFLOPS 0.038 x 5 = 0.189 */ {{3, 3}, {{1, 32, 32, 64}}, 32, 1, {1, 1}, {1, 1}, {1, 1}, {0, 0}, "", true, 37814272.}, + /* GFLOPS 0.090 x 2 = 0.181 */ {{3, 3}, {{1, 224, 10, 10}}, 224, 1, {1, 1}, {1, 1}, {0, 0}, {0, 0}, "SAME", true, 90339200.}, + /* GFLOPS 0.088 x 2 = 0.176 */ {{3, 3}, {{1, 96, 46, 46}}, 24, 1, {1, 1}, {1, 1}, {1, 1}, {0, 0}, "", false, 87805536.}, + /* GFLOPS 0.160 x 1 = 0.160 */ {{3, 3}, {{1, 128, 19, 19}}, 192, 1, {1, 1}, {1, 1}, {0, 0}, {0, 0}, "SAME", true, 159764160.}, + /* GFLOPS 0.146 x 1 = 0.146 */ {{3, 3}, {{1, 144, 14, 14}}, 288, 1, {1, 1}, {1, 1}, {1, 1}, {0, 0}, "", true, 146369664.}, + /* GFLOPS 0.139 x 1 = 0.139 */ {{3, 3}, {{1, 256, 5, 5}}, 1206, 1, {1, 1}, {1, 1}, {1, 1}, {0, 0}, "", true, 138961350.}, + /* GFLOPS 0.128 x 1 = 0.128 */ {{3, 3}, {{1, 64, 24, 24}}, 192, 1, {1, 1}, {1, 1}, {1, 1}, {0, 0}, "", true, 127512576.}, + /* GFLOPS 0.058 x 2 = 0.116 */ {{3, 3}, {{1, 16, 56, 56}}, 64, 1, {1, 1}, {1, 1}, {1, 1}, {0, 0}, "", true, 58003456.}, + /* GFLOPS 0.058 x 2 = 0.116 */ {{3, 3}, {{1, 32, 28, 28}}, 128, 1, {1, 1}, {1, 1}, {1, 1}, {0, 0}, "", true, 57903104.}, + /* GFLOPS 0.058 x 2 = 0.116 */ {{3, 3}, {{1, 64, 14, 14}}, 256, 1, {1, 1}, {1, 1}, {1, 1}, {0, 0}, "", true, 57852928.}, + /* GFLOPS 0.045 x 2 = 0.090 */ {{3, 3}, {{1, 576, 19, 19}}, 12, 1, {1, 1}, {1, 1}, {0, 0}, {0, 0}, "SAME", true, 44918508.}, + /* GFLOPS 0.089 x 1 = 0.089 */ {{3, 3}, {{1, 112, 14, 14}}, 224, 1, {1, 1}, {1, 1}, {1, 1}, {0, 0}, "", true, 88554368.}, + /* GFLOPS 0.043 x 2 = 0.085 */ {{3, 3}, {{1, 32, 48, 48}}, 32, 1, {1, 1}, {1, 1}, {1, 1}, {0, 0}, "SAME", true, 42541056.}, + /* GFLOPS 0.011 x 8 = 0.085 */ {{3, 3}, {{1, 128, 6, 6}}, 128, 1, {1, 1}, {1, 1}, {1, 1}, {0, 0}, "SAME", true, 10621440.}, + /* GFLOPS 0.077 x 1 = 0.077 */ {{3, 3}, {{1, 192, 10, 10}}, 224, 1, {1, 1}, {1, 1}, {0, 0}, {0, 0}, "SAME", true, 77436800.}, + /* GFLOPS 0.070 x 1 = 0.070 */ {{3, 3}, {{1, 96, 14, 14}}, 208, 1, {1, 1}, {1, 1}, {1, 1}, {0, 0}, "", true, 70487872.}, + /* GFLOPS 0.069 x 1 = 0.069 */ {{3, 3}, {{1, 96, 14, 14}}, 204, 1, {1, 1}, {1, 1}, {0, 0}, {0, 0}, "SAME", true, 69132336.}, + /* GFLOPS 0.065 x 1 = 0.065 */ {{3, 3}, {{1, 192, 7, 7}}, 384, 1, {1, 1}, {1, 1}, {1, 1}, {0, 0}, "", true, 65046912.}, + /* GFLOPS 0.065 x 1 = 0.065 */ {{3, 3}, {{1, 160, 10, 10}}, 224, 1, {1, 1}, {1, 1}, {0, 0}, {0, 0}, "SAME", true, 64534400.}, + /* GFLOPS 0.033 x 2 = 0.065 */ {{3, 3}, {{1, 48, 14, 14}}, 192, 1, {1, 1}, {1, 1}, {1, 1}, {0, 0}, "", true, 32551680.}, + /* GFLOPS 0.032 x 2 = 0.064 */ {{3, 3}, {{1, 96, 12, 12}}, 128, 1, {1, 1}, {1, 1}, {1, 1}, {0, 0}, "", true, 31868928.}, + /* GFLOPS 0.004 x 16 = 0.058 */ {{3, 3}, {{1, 128, 7, 7}}, 32, 1, {1, 1}, {1, 1}, {1, 1}, {0, 0}, "", false, 3614240.}, + /* GFLOPS 0.055 x 1 = 0.055 */ {{3, 3}, {{1, 1280, 10, 10}}, 24, 1, {1, 1}, {1, 1}, {0, 0}, {0, 0}, "SAME", true, 55298400.}, + /* GFLOPS 0.053 x 1 = 0.053 */ {{3, 3}, {{1, 128, 38, 38}}, 16, 1, {1, 1}, {1, 1}, {1, 1}, {0, 0}, "", true, 53254720.}, + /* GFLOPS 0.045 x 1 = 0.045 */ {{3, 3}, {{1, 160, 7, 7}}, 320, 1, {1, 1}, {1, 1}, {1, 1}, {0, 0}, "", true, 45174080.}, + /* GFLOPS 0.044 x 1 = 0.044 */ {{3, 3}, {{1, 1024, 10, 10}}, 24, 1, {1, 1}, {1, 1}, {0, 0}, {0, 0}, "SAME", true, 44239200.}, + /* GFLOPS 0.022 x 2 = 0.044 */ {{3, 3}, {{1, 3, 112, 112}}, 32, 1, {1, 1}, {1, 1}, {1, 1}, {0, 0}, "", false, 22077440.}, + /* GFLOPS 0.022 x 2 = 0.044 */ {{3, 3}, {{1, 96, 23, 23}}, 24, 1, {1, 1}, {1, 1}, {1, 1}, {0, 0}, "", false, 21951384.}, + /* GFLOPS 0.007 x 6 = 0.043 */ {{3, 3}, {{1, 48, 16, 16}}, 32, 1, {1, 1}, {1, 1}, {1, 1}, {0, 0}, "", true, 7086080.}, + /* GFLOPS 0.040 x 1 = 0.040 */ {{3, 3}, {{1, 64, 19, 19}}, 96, 1, {1, 1}, {1, 1}, {0, 0}, {0, 0}, "SAME", true, 39958368.}, + /* GFLOPS 0.027 x 1 = 0.027 */ {{3, 3}, {{1, 128, 38, 38}}, 8, 1, {1, 1}, {1, 1}, {1, 1}, {0, 0}, "", true, 26627360.}, + /* GFLOPS 0.010 x 2 = 0.020 */ {{3, 3}, {{1, 256, 2, 2}}, 546, 1, {1, 1}, {1, 1}, {0, 0}, {0, 0}, "SAME", true, 10066056.}, + /* GFLOPS 0.010 x 2 = 0.019 */ {{3, 3}, {{1, 8, 256, 256}}, 1, 1, {1, 1}, {1, 1}, {1, 1}, {0, 0}, "", true, 9502720.}, + /* GFLOPS 0.002 x 6 = 0.014 */ {{3, 3}, {{1, 32, 16, 16}}, 16, 1, {1, 1}, {1, 1}, {1, 1}, {0, 0}, "", true, 2363392.}, + /* GFLOPS 0.001 x 11 = 0.013 */ {{3, 3}, {{1, 64, 4, 4}}, 64, 1, {1, 1}, {1, 1}, {0, 0}, {0, 0}, "SAME", false, 1180672.}, + /* GFLOPS 0.012 x 1 = 0.012 */ {{3, 3}, {{1, 96, 6, 6}}, 192, 1, {1, 1}, {1, 1}, {1, 1}, {0, 0}, "", true, 11950848.}, + /* GFLOPS 0.006 x 2 = 0.012 */ {{3, 3}, {{1, 96, 3, 3}}, 384, 1, {1, 1}, {1, 1}, {1, 1}, {0, 0}, "", true, 5975424.}, + /* GFLOPS 0.006 x 2 = 0.011 */ {{3, 3}, {{1, 512, 5, 5}}, 24, 1, {1, 1}, {1, 1}, {0, 0}, {0, 0}, "SAME", true, 5530200.}, + /* GFLOPS 0.010 x 1 = 0.010 */ {{3, 3}, {{1, 4, 128, 256}}, 4, 1, {1, 1}, {1, 1}, {1, 1}, {0, 0}, "", true, 9568256.}, + /* GFLOPS 0.006 x 1 = 0.006 */ {{3, 3}, {{1, 256, 10, 10}}, 12, 1, {1, 1}, {1, 1}, {1, 1}, {0, 0}, "", true, 5530800.}, + /* GFLOPS 0.004 x 1 = 0.004 */ {{3, 3}, {{1, 256, 1, 1}}, 804, 1, {1, 1}, {1, 1}, {1, 1}, {0, 0}, "", true, 3705636.}, + /* GFLOPS 0.001 x 6 = 0.004 */ {{3, 3}, {{1, 16, 16, 16}}, 8, 1, {1, 1}, {1, 1}, {1, 1}, {0, 0}, "", true, 591872.}, + /* GFLOPS 0.001 x 2 = 0.003 */ {{3, 3}, {{1, 128, 1, 1}}, 546, 1, {1, 1}, {1, 1}, {0, 0}, {0, 0}, "SAME", true, 1258530.}, + /* GFLOPS 0.001 x 1 = 0.001 */ {{3, 3}, {{1, 128, 5, 5}}, 12, 1, {1, 1}, {1, 1}, {1, 1}, {0, 0}, "", true, 691500.}, + /* GFLOPS 0.001 x 1 = 0.001 */ {{3, 3}, {{1, 128, 3, 3}}, 256, 1, {1, 1}, {1, 1}, {0, 0}, {0, 0}, "", true, 590080.}, + /* GFLOPS 0.000 x 2 = 0.001 */ {{3, 3}, {{1, 256, 2, 2}}, 24, 1, {1, 1}, {1, 1}, {0, 0}, {0, 0}, "SAME", true, 442464.}, + /* GFLOPS 0.000 x 6 = 0.001 */ {{3, 3}, {{1, 8, 16, 16}}, 4, 1, {1, 1}, {1, 1}, {1, 1}, {0, 0}, "", true, 148480.}, + /* GFLOPS 0.000 x 1 = 0.000 */ {{3, 3}, {{1, 64, 3, 3}}, 128, 1, {1, 1}, {1, 1}, {0, 0}, {0, 0}, "", true, 147584.}, + /* GFLOPS 0.000 x 1 = 0.000 */ {{3, 3}, {{1, 256, 1, 1}}, 16, 1, {1, 1}, {1, 1}, {1, 1}, {0, 0}, "", true, 73744.}, + /* GFLOPS 0.000 x 2 = 0.000 */ {{3, 3}, {{1, 128, 1, 1}}, 24, 1, {1, 1}, {1, 1}, {0, 0}, {0, 0}, "SAME", true, 55320.}, + /* GFLOPS 0.000 x 1 = 0.000 */ {{3, 3}, {{1, 128, 1, 1}}, 16, 1, {1, 1}, {1, 1}, {1, 1}, {0, 0}, "", true, 36880.}, + /* GFLOPS 0.000 x 1 = 0.000 */ {{3, 3}, {{1, 128, 1, 1}}, 8, 1, {1, 1}, {1, 1}, {1, 1}, {0, 0}, "", true, 18440.}, +}; + +static const ConvParam_t testConvolution_Depthwise_Configs[] = { + /* GFLOPS 6.525 x 14 = 91.357 */ {{5, 5}, {{1, 1632, 7, 7}}, 1632, 1632, {1, 1}, {1, 1}, {2, 2}, {0, 0}, "", true, 6525468768.}, + /* GFLOPS 6.094 x 4 = 24.377 */ {{5, 5}, {{1, 480, 23, 23}}, 480, 480, {1, 1}, {1, 1}, {2, 2}, {0, 0}, "", true, 6094333920.}, + /* GFLOPS 0.925 x 10 = 9.249 */ {{3, 3}, {{1, 512, 14, 14}}, 512, 512, {1, 1}, {1, 1}, {1, 1}, {0, 0}, "", false, 924944384.}, + /* GFLOPS 4.301 x 2 = 8.601 */ {{3, 3}, {{1, 336, 46, 46}}, 336, 336, {1, 1}, {1, 1}, {1, 1}, {0, 0}, "", true, 4300693824.}, + /* GFLOPS 1.734 x 4 = 6.936 */ {{5, 5}, {{1, 64, 92, 92}}, 64, 64, {1, 1}, {1, 1}, {2, 2}, {0, 0}, "", true, 1733968896.}, + /* GFLOPS 1.106 x 6 = 6.638 */ {{5, 5}, {{1, 672, 7, 7}}, 672, 672, {1, 1}, {1, 1}, {2, 2}, {0, 0}, "", true, 1106413728.}, + /* GFLOPS 1.062 x 6 = 6.370 */ {{5, 5}, {{1, 576, 8, 8}}, 576, 576, {1, 1}, {1, 1}, {2, 2}, {0, 0}, "", true, 1061720064.}, + /* GFLOPS 2.986 x 2 = 5.973 */ {{5, 5}, {{1, 336, 46, 46}}, 336, 336, {2, 2}, {1, 1}, {2, 2}, {0, 0}, "", true, 2986276944.}, + /* GFLOPS 1.445 x 4 = 5.781 */ {{5, 5}, {{1, 336, 16, 16}}, 336, 336, {1, 1}, {1, 1}, {2, 2}, {0, 0}, "", true, 1445154816.}, + /* GFLOPS 0.472 x 10 = 4.719 */ {{5, 5}, {{1, 128, 24, 24}}, 128, 128, {1, 1}, {1, 1}, {2, 2}, {0, 0}, "", true, 471932928.}, + /* GFLOPS 2.194 x 2 = 4.389 */ {{3, 3}, {{1, 240, 46, 46}}, 240, 240, {1, 1}, {1, 1}, {1, 1}, {0, 0}, "", true, 2194376640.}, + /* GFLOPS 1.889 x 2 = 3.778 */ {{3, 3}, {{1, 64, 160, 160}}, 64, 64, {1, 1}, {1, 1}, {1, 1}, {0, 0}, "", true, 1889075200.}, + /* GFLOPS 1.659 x 2 = 3.318 */ {{5, 5}, {{1, 960, 14, 14}}, 960, 960, {2, 2}, {1, 1}, {1, 1}, {0, 0}, "", true, 1658914560.}, + /* GFLOPS 0.472 x 6 = 2.834 */ {{3, 3}, {{1, 64, 80, 80}}, 64, 64, {1, 1}, {1, 1}, {1, 1}, {0, 0}, "", true, 472268800.}, + /* GFLOPS 0.472 x 6 = 2.832 */ {{5, 5}, {{1, 64, 48, 48}}, 64, 64, {1, 1}, {1, 1}, {2, 2}, {0, 0}, "", true, 472006656.}, + /* GFLOPS 1.344 x 2 = 2.688 */ {{5, 5}, {{1, 192, 56, 56}}, 192, 192, {2, 2}, {1, 1}, {1, 1}, {0, 0}, "", true, 1343832768.}, + /* GFLOPS 0.382 x 6 = 2.293 */ {{3, 3}, {{1, 576, 8, 8}}, 576, 576, {1, 1}, {1, 1}, {1, 1}, {0, 0}, "", true, 382242816.}, + /* GFLOPS 1.130 x 2 = 2.259 */ {{3, 3}, {{1, 144, 112, 112}}, 144, 144, {2, 2}, {1, 1}, {0, 0}, {0, 0}, "", true, 1129510800.}, + /* GFLOPS 1.062 x 2 = 2.124 */ {{5, 5}, {{1, 144, 32, 32}}, 144, 144, {1, 1}, {1, 1}, {2, 2}, {0, 0}, "", true, 1061830656.}, + /* GFLOPS 0.976 x 2 = 1.953 */ {{3, 3}, {{1, 40, 184, 184}}, 40, 40, {1, 1}, {1, 1}, {1, 1}, {0, 0}, "", true, 976407040.}, + /* GFLOPS 0.473 x 4 = 1.891 */ {{3, 3}, {{1, 32, 160, 160}}, 32, 32, {1, 1}, {1, 1}, {1, 1}, {0, 0}, "", true, 472678400.}, + /* GFLOPS 0.925 x 2 = 1.850 */ {{3, 3}, {{1, 128, 56, 56}}, 128, 128, {1, 1}, {1, 1}, {1, 1}, {0, 0}, "", false, 925245440.}, + /* GFLOPS 0.925 x 2 = 1.850 */ {{3, 3}, {{1, 256, 28, 28}}, 256, 256, {1, 1}, {1, 1}, {1, 1}, {0, 0}, "", false, 925044736.}, + /* GFLOPS 0.925 x 2 = 1.850 */ {{3, 3}, {{1, 1024, 7, 7}}, 1024, 1024, {1, 1}, {1, 1}, {1, 1}, {0, 0}, "", false, 924894208.}, + /* GFLOPS 1.704 x 1 = 1.704 */ {{3, 3}, {{1, 256, 38, 38}}, 256, 256, {1, 1}, {1, 1}, {1, 1}, {0, 0}, "", true, 1703781376.}, + /* GFLOPS 1.660 x 1 = 1.660 */ {{3, 3}, {{1, 128, 75, 75}}, 128, 128, {1, 1}, {1, 1}, {1, 1}, {0, 0}, "", true, 1659600000.}, + /* GFLOPS 0.813 x 2 = 1.626 */ {{5, 5}, {{1, 144, 28, 28}}, 144, 144, {1, 1}, {1, 1}, {2, 2}, {0, 0}, "", true, 812964096.}, + /* GFLOPS 0.813 x 2 = 1.626 */ {{5, 5}, {{1, 288, 14, 14}}, 288, 288, {1, 1}, {1, 1}, {2, 2}, {0, 0}, "", true, 812907648.}, + /* GFLOPS 0.737 x 2 = 1.475 */ {{5, 5}, {{1, 240, 16, 16}}, 240, 240, {1, 1}, {1, 1}, {2, 2}, {0, 0}, "", true, 737341440.}, + /* GFLOPS 0.351 x 4 = 1.405 */ {{3, 3}, {{1, 96, 46, 46}}, 96, 96, {1, 1}, {1, 1}, {1, 1}, {0, 0}, "", true, 351222144.}, + /* GFLOPS 0.680 x 2 = 1.360 */ {{3, 3}, {{1, 96, 64, 64}}, 96, 96, {1, 1}, {1, 1}, {1, 1}, {0, 0}, "", true, 679870464.}, + /* GFLOPS 0.677 x 2 = 1.355 */ {{5, 5}, {{1, 40, 184, 184}}, 40, 40, {2, 2}, {1, 1}, {2, 2}, {0, 0}, "", true, 677458560.}, + /* GFLOPS 0.625 x 2 = 1.250 */ {{3, 3}, {{1, 32, 368, 368}}, 32, 32, {2, 2}, {1, 1}, {1, 1}, {0, 0}, "", true, 625117184.}, + /* GFLOPS 0.293 x 4 = 1.171 */ {{3, 3}, {{1, 288, 14, 14}}, 288, 288, {1, 1}, {1, 1}, {1, 1}, {0, 0}, "", true, 292682880.}, + /* GFLOPS 0.549 x 2 = 1.097 */ {{3, 3}, {{1, 120, 92, 92}}, 120, 120, {2, 2}, {1, 1}, {1, 1}, {0, 0}, "", true, 548721120.}, + /* GFLOPS 0.265 x 4 = 1.062 */ {{3, 3}, {{1, 240, 16, 16}}, 240, 240, {1, 1}, {1, 1}, {1, 1}, {0, 0}, "", true, 265482240.}, + /* GFLOPS 0.473 x 2 = 0.947 */ {{3, 3}, {{1, 16, 320, 320}}, 16, 16, {1, 1}, {1, 1}, {1, 1}, {0, 0}, "", true, 473497600.}, + /* GFLOPS 0.472 x 2 = 0.944 */ {{5, 5}, {{1, 96, 64, 64}}, 96, 96, {2, 2}, {1, 1}, {2, 2}, {0, 0}, "", true, 471957504.}, + /* GFLOPS 0.398 x 2 = 0.797 */ {{3, 3}, {{1, 672, 7, 7}}, 672, 672, {1, 1}, {1, 1}, {1, 1}, {0, 0}, "", true, 398330016.}, + /* GFLOPS 0.361 x 2 = 0.723 */ {{5, 5}, {{1, 336, 16, 16}}, 336, 336, {2, 2}, {1, 1}, {2, 2}, {0, 0}, "", true, 361288704.}, + /* GFLOPS 0.118 x 6 = 0.708 */ {{3, 3}, {{1, 64, 40, 40}}, 64, 64, {1, 1}, {1, 1}, {1, 1}, {0, 0}, "", true, 118067200.}, + /* GFLOPS 0.118 x 6 = 0.708 */ {{5, 5}, {{1, 256, 6, 6}}, 256, 256, {1, 1}, {1, 1}, {2, 2}, {0, 0}, "", true, 117974016.}, + /* GFLOPS 0.336 x 2 = 0.672 */ {{5, 5}, {{1, 96, 56, 56}}, 96, 96, {2, 2}, {1, 1}, {1, 1}, {0, 0}, "", true, 335993184.}, + /* GFLOPS 0.265 x 2 = 0.531 */ {{5, 5}, {{1, 384, 14, 14}}, 384, 384, {2, 2}, {1, 1}, {1, 1}, {0, 0}, "", true, 265434624.}, + /* GFLOPS 0.472 x 1 = 0.472 */ {{5, 5}, {{1, 32, 96, 96}}, 32, 32, {1, 1}, {1, 1}, {2, 2}, {0, 0}, "", true, 472154112.}, + /* GFLOPS 0.232 x 2 = 0.463 */ {{3, 3}, {{1, 32, 112, 112}}, 32, 32, {1, 1}, {1, 1}, {1, 1}, {0, 0}, "", true, 231612416.}, + /* GFLOPS 0.231 x 2 = 0.463 */ {{3, 3}, {{1, 64, 112, 112}}, 64, 64, {2, 2}, {1, 1}, {1, 1}, {0, 0}, "", false, 231411712.}, + /* GFLOPS 0.231 x 2 = 0.463 */ {{3, 3}, {{1, 128, 56, 56}}, 128, 128, {2, 2}, {1, 1}, {1, 1}, {0, 0}, "", false, 231311360.}, + /* GFLOPS 0.231 x 2 = 0.463 */ {{3, 3}, {{1, 256, 28, 28}}, 256, 256, {2, 2}, {1, 1}, {1, 1}, {0, 0}, "", false, 231261184.}, + /* GFLOPS 0.231 x 2 = 0.462 */ {{3, 3}, {{1, 512, 14, 14}}, 512, 512, {2, 2}, {1, 1}, {1, 1}, {0, 0}, "", false, 231236096.}, + /* GFLOPS 0.426 x 1 = 0.426 */ {{3, 3}, {{1, 128, 75, 75}}, 128, 128, {2, 2}, {1, 1}, {1, 1}, {0, 0}, "", true, 426037760.}, + /* GFLOPS 0.426 x 1 = 0.426 */ {{3, 3}, {{1, 256, 38, 38}}, 256, 256, {2, 2}, {1, 1}, {1, 1}, {0, 0}, "", true, 425945344.}, + /* GFLOPS 0.415 x 1 = 0.415 */ {{3, 3}, {{1, 32, 150, 150}}, 32, 32, {1, 1}, {1, 1}, {1, 1}, {0, 0}, "", true, 415440000.}, + /* GFLOPS 0.415 x 1 = 0.415 */ {{3, 3}, {{1, 64, 150, 150}}, 64, 64, {2, 2}, {1, 1}, {1, 1}, {0, 0}, "", true, 415080000.}, + /* GFLOPS 0.170 x 2 = 0.341 */ {{3, 3}, {{1, 24, 128, 128}}, 24, 24, {1, 1}, {1, 1}, {1, 1}, {0, 0}, "", true, 170262528.}, + /* GFLOPS 0.157 x 2 = 0.314 */ {{3, 3}, {{1, 8, 368, 368}}, 8, 8, {1, 1}, {1, 1}, {1, 1}, {0, 0}, "", true, 157091840.}, + /* GFLOPS 0.076 x 4 = 0.304 */ {{3, 3}, {{1, 8, 256, 256}}, 8, 8, {1, 1}, {1, 1}, {1, 1}, {0, 0}, "", true, 76021760.}, + /* GFLOPS 0.130 x 2 = 0.261 */ {{3, 3}, {{1, 24, 112, 112}}, 24, 24, {1, 1}, {1, 1}, {1, 1}, {0, 0}, "", true, 130357248.}, + /* GFLOPS 0.118 x 2 = 0.237 */ {{3, 3}, {{1, 16, 160, 160}}, 16, 16, {1, 1}, {1, 1}, {1, 1}, {0, 0}, "", true, 118374400.}, + /* GFLOPS 0.113 x 2 = 0.226 */ {{5, 5}, {{1, 32, 96, 96}}, 32, 32, {2, 2}, {1, 1}, {1, 1}, {0, 0}, "", true, 113171488.}, + /* GFLOPS 0.108 x 2 = 0.217 */ {{5, 5}, {{1, 64, 48, 48}}, 64, 64, {2, 2}, {1, 1}, {1, 1}, {0, 0}, "", true, 108373056.}, + /* GFLOPS 0.099 x 2 = 0.198 */ {{5, 5}, {{1, 128, 24, 24}}, 128, 128, {2, 2}, {1, 1}, {1, 1}, {0, 0}, "", true, 99138688.}, + /* GFLOPS 0.096 x 2 = 0.191 */ {{3, 3}, {{1, 144, 32, 32}}, 144, 144, {2, 2}, {1, 1}, {1, 1}, {0, 0}, "", true, 95588352.}, + /* GFLOPS 0.030 x 6 = 0.177 */ {{3, 3}, {{1, 64, 20, 20}}, 64, 64, {1, 1}, {1, 1}, {1, 1}, {0, 0}, "", true, 29516800.}, + /* GFLOPS 0.082 x 2 = 0.164 */ {{5, 5}, {{1, 256, 12, 12}}, 256, 256, {2, 2}, {1, 1}, {1, 1}, {0, 0}, "", true, 81926400.}, + /* GFLOPS 0.076 x 2 = 0.151 */ {{3, 3}, {{1, 32, 64, 64}}, 32, 32, {1, 1}, {1, 1}, {1, 1}, {0, 0}, "", true, 75628544.}, + /* GFLOPS 0.076 x 2 = 0.151 */ {{3, 3}, {{1, 32, 128, 128}}, 32, 32, {2, 2}, {1, 1}, {1, 1}, {0, 0}, "", true, 75628544.}, + /* GFLOPS 0.063 x 2 = 0.126 */ {{3, 3}, {{1, 144, 28, 28}}, 144, 144, {2, 2}, {1, 1}, {0, 0}, {0, 0}, "", true, 63103248.}, + /* GFLOPS 0.019 x 6 = 0.114 */ {{3, 3}, {{1, 8, 128, 128}}, 8, 8, {1, 1}, {1, 1}, {1, 1}, {0, 0}, "", true, 19005440.}, + /* GFLOPS 0.019 x 2 = 0.038 */ {{3, 3}, {{1, 16, 64, 64}}, 16, 16, {1, 1}, {1, 1}, {1, 1}, {0, 0}, "", true, 18939904.}, + /* GFLOPS 0.014 x 2 = 0.029 */ {{3, 3}, {{1, 56, 16, 16}}, 56, 56, {1, 1}, {1, 1}, {1, 1}, {0, 0}, "", true, 14465024.}, + /* GFLOPS 0.012 x 2 = 0.023 */ {{3, 3}, {{1, 10, 80, 80}}, 10, 10, {1, 1}, {1, 1}, {1, 1}, {0, 0}, "", true, 11584000.}, + /* GFLOPS 0.011 x 2 = 0.021 */ {{3, 3}, {{1, 24, 32, 32}}, 24, 24, {1, 1}, {1, 1}, {1, 1}, {0, 0}, "", true, 10641408.}, + /* GFLOPS 0.003 x 6 = 0.016 */ {{3, 3}, {{1, 192, 2, 2}}, 192, 192, {1, 1}, {1, 1}, {1, 1}, {0, 0}, "", true, 2654976.}, + /* GFLOPS 0.004 x 2 = 0.008 */ {{3, 3}, {{1, 1, 32, 100}}, 64, 1, {1, 1}, {1, 1}, {1, 1}, {0, 0}, "", true, 3891200.}, + /* GFLOPS 0.003 x 2 = 0.006 */ {{3, 3}, {{1, 10, 40, 40}}, 10, 10, {1, 1}, {1, 1}, {1, 1}, {0, 0}, "", true, 2896000.}, + /* GFLOPS 0.002 x 2 = 0.004 */ {{3, 3}, {{1, 4, 80, 80}}, 4, 4, {1, 1}, {1, 1}, {1, 1}, {0, 0}, "", true, 1868800.}, + /* GFLOPS 0.001 x 2 = 0.001 */ {{3, 3}, {{1, 10, 20, 20}}, 10, 10, {1, 1}, {1, 1}, {1, 1}, {0, 0}, "", true, 724000.}, + /* GFLOPS 0.000 x 2 = 0.001 */ {{3, 3}, {{1, 192, 4, 4}}, 192, 192, {2, 2}, {1, 1}, {0, 0}, {0, 0}, "", true, 663744.}, + /* GFLOPS 0.000 x 2 = 0.001 */ {{3, 3}, {{1, 4, 40, 40}}, 4, 4, {1, 1}, {1, 1}, {1, 1}, {0, 0}, "", true, 467200.}, + /* GFLOPS 0.000 x 4 = 0.000 */ {{3, 3}, {{1, 1, 80, 80}}, 1, 1, {1, 1}, {1, 1}, {1, 1}, {0, 0}, "", true, 121600.}, + /* GFLOPS 0.000 x 2 = 0.000 */ {{3, 3}, {{1, 4, 20, 20}}, 4, 4, {1, 1}, {1, 1}, {1, 1}, {0, 0}, "", true, 116800.}, + /* GFLOPS 0.000 x 4 = 0.000 */ {{3, 3}, {{1, 1, 40, 40}}, 1, 1, {1, 1}, {1, 1}, {1, 1}, {0, 0}, "", true, 30400.}, + /* GFLOPS 0.000 x 4 = 0.000 */ {{3, 3}, {{1, 1, 20, 20}}, 1, 1, {1, 1}, {1, 1}, {1, 1}, {0, 0}, "", true, 7600.}, +}; + +struct ConvParamGenerator { - enum { - CONV_0 = 0, - CONV_100 = 100, - CONV_LAST = sizeof(testConvolutionConfigs) / sizeof(testConvolutionConfigs[0]) - }; - int val_; - ConvParamID(int val = 0) : val_(val) {} - operator int() const { return val_; } - static ::testing::internal::ParamGenerator all() + ConvParamGenerator(const ConvParam_t* testConfigs, const int size): testConfigs(testConfigs), size(size) + {} + + const ConvParam_t* testConfigs; + const int size; + + ::testing::internal::ParamGenerator all() const { -#if 0 - enum { NUM = (int)CONV_LAST }; -#else - enum { NUM = (int)CONV_100 }; -#endif - ConvParamID v_[NUM]; for (int i = 0; i < NUM; ++i) { v_[i] = ConvParamID(i); } // reduce generated code size - return ::testing::ValuesIn(v_, v_ + NUM); + int NUM = size; + static size_t DNN_LIMIT_CONV = utils::getConfigurationParameterSizeT("OPENCV_TEST_DNN_LIMIT_CONV", 0); + if (DNN_LIMIT_CONV > 0) + NUM = std::min(NUM, (int)DNN_LIMIT_CONV); + + std::vector v_(NUM); + for (int i = 0; i < NUM; ++i) { v_[i] = testConfigs[i]; } // reduce generated code size + return ::testing::ValuesIn(v_); } }; -static inline void PrintTo(const ConvParamID& v, std::ostream* os) +static inline void PrintTo(const ConvParam_t& p, std::ostream* os) { - CV_Assert((int)v >= 0); CV_Assert((int)v < ConvParamID::CONV_LAST); - const ConvParam_t& p = testConvolutionConfigs[(int)v]; - *os << "GFLOPS=" << cv::format("%.3f", p.declared_flops * 1e-9) << ", K=" << (Size)p.kernel << ", IN={" << p.shapeIn.dims[0] << ", " << p.shapeIn.dims[1] << ", " << p.shapeIn.dims[2] << ", " << p.shapeIn.dims[3] << "}" @@ -793,17 +806,20 @@ static inline void PrintTo(const ConvParamID& v, std::ostream* os) *os << ", BIAS"; } - - -typedef tuple > ConvTestParam_t; -typedef TestBaseWithParam Conv; - -PERF_TEST_P_(Conv, conv) +static +Net build_net( + const ConvParam_t& params, Backend backendId, Target targetId, + const std::function& configure_network_cb = std::function(), + double flops_limit_debug_long = 2e9, double flops_limit_debug_verylong = 6e9 +) { - int test_id = (int)get<0>(GetParam()); - ASSERT_GE(test_id, 0); ASSERT_LT(test_id, ConvParamID::CONV_LAST); - const ConvParam_t& params = testConvolutionConfigs[test_id]; double declared_flops = params.declared_flops; + + if (flops_limit_debug_verylong > 0 && declared_flops >= flops_limit_debug_verylong) + applyTestTag(CV_TEST_TAG_DEBUG_VERYLONG); + if (flops_limit_debug_long > 0 && declared_flops >= flops_limit_debug_long) + applyTestTag(CV_TEST_TAG_DEBUG_LONG); + Size kernel = params.kernel; MatShape inputShape = MatShape(params.shapeIn.dims, params.shapeIn.dims + 4); int outChannels = params.outCN; @@ -814,8 +830,6 @@ PERF_TEST_P_(Conv, conv) Size padAdjust = params.padAdjust; std::string padMode(params.padMode); bool hasBias = params.hasBias; - Backend backendId = get<0>(get<1>(GetParam())); - Target targetId = get<1>(get<1>(GetParam())); int inChannels = inputShape[1]; Size inSize(inputShape[3], inputShape[2]); @@ -859,9 +873,14 @@ PERF_TEST_P_(Conv, conv) Net net; net.addLayerToPrev(lp.name, lp.type, lp); - net.setInput(input); net.setPreferableBackend(backendId); net.setPreferableTarget(targetId); + if (configure_network_cb) + { + configure_network_cb(net); + } + + net.setInput(input); // warmup Mat output = net.forward(); @@ -878,18 +897,104 @@ PERF_TEST_P_(Conv, conv) << " Weights(parameters): " << divUp(weightsMemory, 1u<<10) << " Kb" << " MFLOPS=" << flops * 1e-6 << std::endl; + EXPECT_NEAR(flops, declared_flops, declared_flops * 1e-6); + + return net; +} + +typedef tuple > ConvTestParam_t; +typedef tuple, bool> Conv3x3S1D1TestParam_t; +typedef TestBaseWithParam Conv; +typedef TestBaseWithParam Conv_1x1; +typedef TestBaseWithParam Conv_3x3S1D1; +typedef TestBaseWithParam Conv_Depthwise; + +PERF_TEST_P_(Conv, conv) +{ + const ConvParam_t& params = get<0>(GetParam()); + Backend backendId = get<0>(get<1>(GetParam())); + Target targetId = get<1>(get<1>(GetParam())); + Net net = build_net(params, backendId, targetId); + TEST_CYCLE() { Mat res = net.forward(); } - - EXPECT_NEAR(flops, declared_flops, declared_flops * 1e-6); SANITY_CHECK_NOTHING(); } +PERF_TEST_P_(Conv_1x1, conv) +{ + const ConvParam_t& params = get<0>(GetParam()); + Backend backendId = get<0>(get<1>(GetParam())); + Target targetId = get<1>(get<1>(GetParam())); + Net net = build_net(params, backendId, targetId); + + TEST_CYCLE() + { + Mat res = net.forward(); + } + SANITY_CHECK_NOTHING(); +} + +PERF_TEST_P_(Conv_3x3S1D1, conv) +{ + const ConvParam_t& params = get<0>(GetParam()); + Backend backendId = get<0>(get<1>(GetParam())); + Target targetId = get<1>(get<1>(GetParam())); + bool winograd = get<2>(GetParam()); + Net net = build_net(params, backendId, targetId, + [=](Net& net) + { + net.enableWinograd(winograd); + } + ); + + TEST_CYCLE() + { + Mat res = net.forward(); + } + SANITY_CHECK_NOTHING(); +} + +PERF_TEST_P_(Conv_Depthwise, conv) +{ + const ConvParam_t& params = get<0>(GetParam()); + Backend backendId = get<0>(get<1>(GetParam())); + Target targetId = get<1>(get<1>(GetParam())); + Net net = build_net(params, backendId, targetId, std::function(), + 0/*flops_limit_debug_long*/, 0/*flops_limit_debug_verylong*/); + + TEST_CYCLE() + { + Mat res = net.forward(); + } + SANITY_CHECK_NOTHING(); +} + +ConvParamGenerator conv_params(testConvolution_Configs, sizeof(testConvolution_Configs) / sizeof(testConvolution_Configs[0])); INSTANTIATE_TEST_CASE_P(/**/, Conv, Combine( - ConvParamID::all(), - dnnBackendsAndTargets(/* withInferenceEngine = */false, /* obsolete_withHalide = */false) // defined in ../test/test_common.hpp + conv_params.all(), + dnnBackendsAndTargets(false, false) // defined in ../test/test_common.hpp +)); + +ConvParamGenerator conv_1x1_params(testConvolution_1x1_Configs, sizeof(testConvolution_1x1_Configs) / sizeof(testConvolution_1x1_Configs[0])); +INSTANTIATE_TEST_CASE_P(/**/, Conv_1x1, Combine( + conv_1x1_params.all(), + dnnBackendsAndTargets(false, false) // defined in ../test/test_common.hpp +)); + +ConvParamGenerator conv_3x3S1D1_params(testConvolution_3x3S1D1_Configs, sizeof(testConvolution_3x3S1D1_Configs) / sizeof(testConvolution_3x3S1D1_Configs[0])); +INSTANTIATE_TEST_CASE_P(/**/, Conv_3x3S1D1, Combine( + conv_3x3S1D1_params.all(), + dnnBackendsAndTargets(false, false), // defined in ../test/test_common.hpp + testing::Values(true, false) // enable Winograd or not +)); + +ConvParamGenerator conv_depthwise_params(testConvolution_Depthwise_Configs, sizeof(testConvolution_Depthwise_Configs) / sizeof(testConvolution_Depthwise_Configs[0])); +INSTANTIATE_TEST_CASE_P(/**/, Conv_Depthwise, Combine( + conv_depthwise_params.all(), + dnnBackendsAndTargets(false, false) // defined in ../test/test_common.hpp )); } // namespace diff --git a/modules/dnn/perf/perf_einsum.cpp b/modules/dnn/perf/perf_einsum.cpp new file mode 100644 index 0000000000..bad9d956be --- /dev/null +++ b/modules/dnn/perf/perf_einsum.cpp @@ -0,0 +1,109 @@ +// This file is part of OpenCV project. +// It is subject to the license terms in the LICENSE file found in the top-level directory +// of this distribution and at http://opencv.org/license.html. + +#include "perf_precomp.hpp" + +namespace opencv_test { + +struct EinsumParams { + int inputSize; + int outputSize; + std::string equation; + std::vector einsumInpShapes; + EinsumParams(std::string equation_, std::vector einsumInpShapes_ = std::vector()) + { + inputSize = einsumInpShapes_.size(); + equation = equation_; + einsumInpShapes = einsumInpShapes_; + } +}; + +static inline void PrintTo(const EinsumParams& params, ::std::ostream* os) { + (*os) << "Equation=" << params.equation << " "; + + (*os) << "InputShape={"; + for(int i = 0; i < params.einsumInpShapes.size(); i++) + { + (*os) << "{"; + for(int j = 0; j < params.einsumInpShapes[i].size(); j++) + { + (*os) << params.einsumInpShapes[i][j] << ((j < params.einsumInpShapes[i].size() - 1) ? ", " : ""); + } + (*os) << ((i < params.einsumInpShapes.size() - 1) ? "}, " : "}"); + } + (*os) << "}"; +} + +// test cases +static const EinsumParams testEinsumConfigs[] = { + // TODO: Add tests with one input after ellips merge + {"ij, jk -> ik", {{2, 3}, {3, 2}}}, + {"ij, jk -> ik", {{20, 30}, {30, 20}}}, + {"ij, jk -> ik", {{113, 127}, {127, 113}}}, + + {"imkj, injs -> imnks", {{1, 4, 7, 9}, {1, 5, 9, 8}}}, + {"imkj, injs -> imnks", {{1, 4, 70, 90}, {1, 5, 90, 80}}}, + {"imkj, injs -> imnks", {{1, 4, 73, 91}, {1, 5, 91, 57}}}, + + {"ij -> i", {{30, 40}}}, + {"ij -> i", {{113, 374}}}, + + {"...ij -> ...i", {{30, 40}}}, + {"...ij -> ...i", {{113, 374}}}, + + {"...ij, ...jk -> ...ik", {{40, 50}, {50, 80}}}, + {"...ij, ...jk -> ...ik", {{47, 51}, {51, 83}}}, +}; + +class Layer_Einsum: public TestBaseWithParam {}; + +PERF_TEST_P_(Layer_Einsum, einsum) { + const EinsumParams& params = GetParam(); + LayerParams lp; + lp.type = "Einsum"; + lp.name = "testEinsum"; + lp.set("equation", params.equation); + lp.set("inputSize", params.inputSize); + lp.set("outputSize", 1); + + CV_CheckFalse(params.einsumInpShapes.empty(), "ERROR no inputs shapes provided"); + + for (int i = 0; i < params.einsumInpShapes.size(); i++) { + lp.set("inputShapes" + cv::format("%d", i), DictValue::arrayInt(params.einsumInpShapes[i].begin(), params.einsumInpShapes[i].size())); + } + + Net net; + std::vector inputs; + std::vector input_names; + int id = net.addLayer(lp.name, lp.type, lp); + + for (int i = 0; i < params.inputSize; ++i) { + // create inputs + inputs.emplace_back(Mat(params.einsumInpShapes[i].size(), params.einsumInpShapes[i].data(), CV_32FC1)); + + // connect each input to the layer + net.connect(0, i, id, i); + + // create input names dynamically, assuming input naming follows a consistent pattern + input_names.emplace_back("input" + std::to_string(i + 1)); + } + + //warm up + std::vector outputs; + net.setInputsNames(input_names); + for (int i = 0; i < input_names.size(); i++){ + net.setInput(inputs[i], input_names[i]); + } + net.forward(outputs, "testEinsum"); + + TEST_CYCLE() + { + net.forward(outputs, "testEinsum"); + } + SANITY_CHECK_NOTHING(); +} + +INSTANTIATE_TEST_CASE_P(/**/, Layer_Einsum, testing::ValuesIn(testEinsumConfigs)); + +}; //namespace diff --git a/modules/dnn/perf/perf_gemm.cpp b/modules/dnn/perf/perf_gemm.cpp index 8051cc273e..40fd66865b 100644 --- a/modules/dnn/perf/perf_gemm.cpp +++ b/modules/dnn/perf/perf_gemm.cpp @@ -5,6 +5,8 @@ #include "perf_precomp.hpp" #include +#include + namespace opencv_test { struct GemmParam_t { @@ -71,6 +73,18 @@ static const GemmParam_t test_gemm_configs[] = { */ }; +static const GemmParam_t test_matmul_configs[] = { + // vision transformer cases + { {12, 197, 197}, {12, 197, 64} }, + { {12, 197, 64 }, {12, 64, 197} }, + { {12, 50, 64}, {12, 64, 50} }, + { {12, 50, 50}, {12, 50, 64} }, + { {16, 197, 197}, {16, 197, 64} }, + { {16, 197, 64 }, {16, 64, 197} }, + { {16, 50, 64}, {16, 64, 50} }, + { {16, 50, 50}, {16, 50, 64} }, +}; + struct GemmParamId { enum { @@ -88,6 +102,21 @@ struct GemmParamId } }; +struct MatMulParamId { + enum { + MATMUL_0 = 0, + MATMUL_LAST = sizeof(test_matmul_configs) / sizeof(test_matmul_configs[0]) + }; + int val_; + MatMulParamId(int val = 0) : val_(val) {} + operator int() const { return val_; } + static ::testing::internal::ParamGenerator all() { + enum { NUM = (int)MATMUL_LAST }; + MatMulParamId v_[NUM]; for (int i = 0; i < NUM; i++) { v_[i] = MatMulParamId(i); } + return ::testing::ValuesIn(v_, v_ + NUM); + } +}; + static inline void PrintTo(const GemmParamId& v, std::ostream* os) { CV_Assert((int)v >= 0); CV_Assert((int)v < GemmParamId::GEMM_LAST); @@ -138,7 +167,7 @@ PERF_TEST_P_(Gemm, gemm) Mat A(static_cast(a_shape.size()), a_shape.data(), CV_32F); randu(A, -1.0f, 1.0f); Mat B(static_cast(b_shape.size()), b_shape.data(), CV_32F); - randu(A, -1.0f, 1.0f); + randu(B, -1.0f, 1.0f); LayerParams lp; lp.type = "Gemm"; @@ -197,7 +226,7 @@ PERF_TEST_P_(Gemm, innerproduct) Mat A(static_cast(a_shape.size()), a_shape.data(), CV_32F); randu(A, -1.0f, 1.0f); Mat B(static_cast(b_shape.size()), b_shape.data(), CV_32F); - randu(A, -1.0f, 1.0f); + randu(B, -1.0f, 1.0f); LayerParams lp; lp.type = "InnerProduct"; @@ -241,9 +270,146 @@ PERF_TEST_P_(Gemm, innerproduct) SANITY_CHECK_NOTHING(); } +static inline void PrintTo(const MatMulParamId& v, std::ostream* os) +{ + CV_Assert((int)v >= 0); CV_Assert((int)v < MatMulParamId::MATMUL_LAST); + const GemmParam_t& p = test_matmul_configs[(int)v]; + + auto print_shape = [os](const std::vector& shape, const std::string tag) { + if (shape.empty()) { + return ; + } + + *os << tag << "=["; + for (size_t i = 0; i < shape.size(); ++i) { + if (i == shape.size() - 1) { + *os << shape[i] << "]"; + break; + } + *os << shape[i] << ", "; + } + }; + + print_shape(p.a_shape, "A"); + print_shape(p.b_shape, ", B"); + print_shape(p.c_shape, ", C"); + *os << ", trans_a=" << p.trans_a << ", trans_b=" << p.trans_b; +} + +using MatMulTestParam_t = tuple>; +using MatMul = TestBaseWithParam; + +PERF_TEST_P_(MatMul, matmul) +{ + int test_id = (int)get<0>(GetParam()); + ASSERT_GE(test_id, 0); ASSERT_LT(test_id, MatMulParamId::MATMUL_LAST); + const GemmParam_t& params = test_matmul_configs[test_id]; + auto a_shape = params.a_shape; + auto b_shape = params.b_shape; + auto trans_a = params.trans_a; + auto trans_b = params.trans_b; + float alpha = 1.f; + float beta = 1.f; + + Backend backend_id = get<0>(get<1>(GetParam())); + Target target_id = get<1>(get<1>(GetParam())); + + Mat A(a_shape, CV_32F); + randu(A, -1.0f, 1.0f); + Mat B(b_shape, CV_32F); + randu(B, -1.0f, 1.0f); + + LayerParams lp; + lp.type = "MatMul"; + lp.name = "testLayer"; + lp.set("transA", trans_a); + lp.set("transB", trans_b); + lp.set("alpha", alpha); + lp.set("beta", beta); + lp.blobs.push_back(B); + + Net net; + net.addLayerToPrev(lp.name, lp.type, lp); + net.setPreferableBackend(backend_id); + net.setPreferableTarget(target_id); + + // warmup + { + std::vector input_names{"A"}; + net.setInputsNames(input_names); + net.setInput(A, input_names[0]); + Mat out = net.forward(); + } + + TEST_CYCLE() + { + Mat res = net.forward(); + } + + SANITY_CHECK_NOTHING(); +} + +PERF_TEST_P_(MatMul, innerproduct) +{ + int test_id = (int)get<0>(GetParam()); + ASSERT_GE(test_id, 0); ASSERT_LT(test_id, MatMulParamId::MATMUL_LAST); + const GemmParam_t& params = test_matmul_configs[test_id]; + auto a_shape = params.a_shape; + auto b_shape = params.b_shape; + + Backend backend_id = get<0>(get<1>(GetParam())); + Target target_id = get<1>(get<1>(GetParam())); + + Mat A(a_shape, CV_32F); + randu(A, -1.0f, 1.0f); + Mat B(b_shape, CV_32F); + randu(B, -1.0f, 1.0f); + + LayerParams lp; + lp.type = "InnerProduct"; + lp.name = "testLayer"; + lp.set("axis", (int)(a_shape.size() - 1)); + lp.set("bias_term", false); + + // pre-transpose + std::vector order(b_shape.size()); + std::iota(order.begin(), order.end(), 0); + std::swap(order.back(), order[b_shape.size() - 2]); + Mat B_transposed; + transposeND(B, order, B_transposed); + lp.blobs.push_back(B_transposed); + lp.set("num_output", int(B_transposed.total(0, b_shape.size() - 1))); + lp.set("is_matmul", true); + + Net net; + net.addLayerToPrev(lp.name, lp.type, lp); + net.setPreferableBackend(backend_id); + net.setPreferableTarget(target_id); + + // warmup + { + std::vector input_names{"A"}; + net.setInputsNames(input_names); + net.setInput(A, input_names[0]); + Mat out = net.forward(); + } + + TEST_CYCLE() + { + Mat res = net.forward(); + } + + SANITY_CHECK_NOTHING(); +} + INSTANTIATE_TEST_CASE_P(/**/, Gemm, Combine( GemmParamId::all(), dnnBackendsAndTargets(false, false) // defined in ../test/test_common.hpp )); +INSTANTIATE_TEST_CASE_P(/**/, MatMul, Combine( + MatMulParamId::all(), + dnnBackendsAndTargets(false, false) // defined in ../test/test_common.hpp +)); + } // namespace diff --git a/modules/dnn/perf/perf_layer.cpp b/modules/dnn/perf/perf_layer.cpp index 38015a0103..66b5ad62c2 100644 --- a/modules/dnn/perf/perf_layer.cpp +++ b/modules/dnn/perf/perf_layer.cpp @@ -683,7 +683,119 @@ PERF_TEST_P_(Layer_GatherElements, GatherElements) test_layer({2700, 1, 2914}, {2700, 1, 81}, 2); } -INSTANTIATE_TEST_CASE_P(/**/, Layer_Slice, dnnBackendsAndTargets(/* withInferenceEngine = */false, /* obsolete_withHalide = */false)); +struct Layer_InstanceNorm : public TestBaseWithParam > +{ + void test_layer(const std::vector& x_shape) + { + int backendId = get<0>(GetParam()); + int targetId = get<1>(GetParam()); + + Mat x(x_shape, CV_32FC1); + Mat scale(x_shape[1], 1, CV_32FC1); + Mat b(x_shape[1], 1, CV_32FC1); + + randu(x, 0.f, 1.f); + randu(scale, 0.f, 1.f); + randu(b, 0.f, 1.f); + + Net net; + LayerParams lp; + lp.type = "InstanceNormalization"; + lp.name = "testLayer"; + int id = net.addLayerToPrev(lp.name, lp.type, lp); + net.connect(0, 0, id, 0); + net.connect(0, 1, id, 1); + net.connect(0, 2, id, 2); + + // warmup + { + std::vector inpNames{"x", "scale", "b"}; + net.setInputsNames(inpNames); + net.setInput(x, inpNames[0]); + net.setInput(scale, inpNames[1]); + net.setInput(b, inpNames[2]); + + net.setPreferableBackend(backendId); + net.setPreferableTarget(targetId); + Mat out = net.forward(); + } + + TEST_CYCLE() + { + Mat res = net.forward(); + } + + SANITY_CHECK_NOTHING(); + } + + int N = 2; + int C = 64; + int H = 180; + int W = 240; +}; + +PERF_TEST_P_(Layer_InstanceNorm, InstanceNorm) +{ + test_layer({N, C, H, W}); +} + +struct Layer_Attention : public TestBaseWithParam> { + void test_layer(const std::vector x_shape, const std::vector qkv_hidden_sizes, const int num_heads) { + int backendId = get<0>(GetParam()); + int targetId = get<1>(GetParam()); + + auto qk_hidden_size = qkv_hidden_sizes[0]; + auto v_hidden_size = qkv_hidden_sizes[2]; + + auto input_hidden_size = x_shape[2]; + auto hidden_size = qk_hidden_size + qk_hidden_size + v_hidden_size; + + Mat x(x_shape, CV_32F); + Mat weight(std::vector{input_hidden_size, hidden_size}, CV_32F); + Mat bias(std::vector{hidden_size}, CV_32F); + + randu(x, 0.f, 1.f); + randu(weight, 0.f, 1.f); + randu(bias, 0.f, 1.f); + + LayerParams lp; + lp.type = "Attention"; + lp.name = "testLayer"; + lp.set("num_heads", num_heads); + lp.set("qkv_hidden_sizes", DictValue::arrayInt(qkv_hidden_sizes.data(), qkv_hidden_sizes.size())); + + Net net; + int id = net.addLayerToPrev(lp.name, lp.type, lp); + net.connect(0, 0, id, 0); + net.connect(0, 1, id, 1); + net.connect(0, 2, id, 2); + + { + std::vector input_names{"x", "weight", "bias"}; + net.setInputsNames(input_names); + net.setInput(x, input_names[0]); + net.setInput(weight, input_names[1]); + net.setInput(bias, input_names[2]); + + net.setPreferableBackend(backendId); + net.setPreferableTarget(targetId); + Mat out = net.forward(); + } + + TEST_CYCLE() + { + Mat out = net.forward(); + } + + SANITY_CHECK_NOTHING(); + } +}; + +PERF_TEST_P_(Layer_Attention, VisionTransformer) { + test_layer({1, 197, 768}, {768, 768, 768}, 12); +} + +INSTANTIATE_TEST_CASE_P(/**/, Layer_Slice, dnnBackendsAndTargets(false, false)); INSTANTIATE_TEST_CASE_P(/**/, Layer_NaryEltwise, testing::Values(std::make_tuple(DNN_BACKEND_OPENCV, DNN_TARGET_CPU))); #ifdef HAVE_CUDA INSTANTIATE_TEST_CASE_P(CUDA, Layer_NaryEltwise, testing::Values(std::make_tuple(DNN_BACKEND_CUDA, DNN_TARGET_CUDA))); @@ -693,6 +805,8 @@ INSTANTIATE_TEST_CASE_P(/**/, Layer_ScatterND, testing::Values(std::make_tuple(D INSTANTIATE_TEST_CASE_P(/**/, Layer_LayerNorm, testing::Values(std::make_tuple(DNN_BACKEND_OPENCV, DNN_TARGET_CPU))); INSTANTIATE_TEST_CASE_P(/**/, Layer_LayerNormExpanded, testing::Values(std::make_tuple(DNN_BACKEND_OPENCV, DNN_TARGET_CPU))); INSTANTIATE_TEST_CASE_P(/**/, Layer_GatherElements, testing::Values(std::make_tuple(DNN_BACKEND_OPENCV, DNN_TARGET_CPU))); +INSTANTIATE_TEST_CASE_P(/**/, Layer_InstanceNorm, testing::Values(std::make_tuple(DNN_BACKEND_OPENCV, DNN_TARGET_CPU))); +INSTANTIATE_TEST_CASE_P(/**/, Layer_Attention, testing::Values(std::make_tuple(DNN_BACKEND_OPENCV, DNN_TARGET_CPU))); typedef TestBaseWithParam > > Layer_FullyConnected; @@ -715,6 +829,9 @@ PERF_TEST_P_(Layer_FullyConnected, fc) int backendId = get<0>(get<3>(GetParam())); int targetId = get<1>(get<3>(GetParam())); + if (inpShape.size() == 4 && inpShape[0] == 5 && inpShape[1] == 16 && inpShape[2] == 512 && inpShape[3] == 128 && outDims >= 512) + applyTestTag(CV_TEST_TAG_DEBUG_VERYLONG); + std::vector weightShape; if (isMatMul) { weightShape = inpShape; @@ -758,4 +875,55 @@ INSTANTIATE_TEST_CASE_P(/**/, Layer_FullyConnected, Combine( dnnBackendsAndTargets() )); +typedef TestBaseWithParam, int, tuple > > Layer_Softmax; +PERF_TEST_P_(Layer_Softmax, softmax_3d) { + std::vector shape = get<0>(GetParam()); + int axis = get<1>(GetParam()); + int backendId = get<0>(get<2>(GetParam())); + int targetId = get<1>(get<2>(GetParam())); + + Mat data(shape, CV_32FC1); + Scalar mean = 0.f; + Scalar std = 1.f; + randn(data, mean, std); + + Net net; + LayerParams lp; + lp.type = "Softmax"; + lp.name = "testLayer"; + lp.set("axis", axis); + + net.addLayerToPrev(lp.name, lp.type, lp); + // warmup + { + net.setInput(data); + net.setPreferableBackend(backendId); + net.setPreferableTarget(targetId); + Mat out = net.forward(); + } + + TEST_CYCLE() { + Mat res = net.forward(); + } + + SANITY_CHECK_NOTHING(); +} + +INSTANTIATE_TEST_CASE_P(/**/, Layer_Softmax, Combine( + Values( // input size + std::vector({16, 50, 50}), + std::vector({16, 197, 197}), + std::vector({16, 1024, 1024}) + ), + Values(0, 1, 2), // axis + dnnBackendsAndTargets(/* withInferenceEngine= */ false, + /* withHalide= */ false, + /* withCpuOCV= */ true, + /* withVkCom= */ false, + /* withCUDA= */ false, + /* withNgraph= */ false, + /* withWebnn= */ false, + /* withCann= */ false) // only test on CPU +)); + } // namespace diff --git a/modules/dnn/perf/perf_net.cpp b/modules/dnn/perf/perf_net.cpp index 96c4aea124..b43613cc64 100644 --- a/modules/dnn/perf/perf_net.cpp +++ b/modules/dnn/perf/perf_net.cpp @@ -82,7 +82,6 @@ public: } }; - PERF_TEST_P_(DNNTestNetwork, AlexNet) { processNet("dnn/bvlc_alexnet.caffemodel", "dnn/bvlc_alexnet.prototxt", cv::Size(227, 227)); @@ -111,6 +110,8 @@ PERF_TEST_P_(DNNTestNetwork, Inception_5h) PERF_TEST_P_(DNNTestNetwork, SSD) { + applyTestTag(CV_TEST_TAG_DEBUG_VERYLONG); + processNet("dnn/VGG_ILSVRC2016_SSD_300x300_iter_440000.caffemodel", "dnn/ssd_vgg16.prototxt", cv::Size(300, 300)); } @@ -136,6 +137,8 @@ PERF_TEST_P_(DNNTestNetwork, DenseNet_121) PERF_TEST_P_(DNNTestNetwork, OpenPose_pose_mpi_faster_4_stages) { + applyTestTag(CV_TEST_TAG_DEBUG_VERYLONG); + if (backend == DNN_BACKEND_INFERENCE_ENGINE_NN_BUILDER_2019 && (target == DNN_TARGET_MYRIAD || target == DNN_TARGET_HDDL)) throw SkipTestException(""); // The same .caffemodel but modified .prototxt @@ -150,12 +153,17 @@ PERF_TEST_P_(DNNTestNetwork, opencv_face_detector) PERF_TEST_P_(DNNTestNetwork, Inception_v2_SSD_TensorFlow) { + applyTestTag(CV_TEST_TAG_DEBUG_VERYLONG); + processNet("dnn/ssd_inception_v2_coco_2017_11_17.pb", "ssd_inception_v2_coco_2017_11_17.pbtxt", cv::Size(300, 300)); } PERF_TEST_P_(DNNTestNetwork, YOLOv3) { - applyTestTag(CV_TEST_TAG_MEMORY_2GB); + applyTestTag( + CV_TEST_TAG_MEMORY_2GB, + CV_TEST_TAG_DEBUG_VERYLONG + ); #if defined(INF_ENGINE_RELEASE) && INF_ENGINE_VER_MAJOR_EQ(2020040000) // nGraph compilation failure if (backend == DNN_BACKEND_INFERENCE_ENGINE_NGRAPH && target == DNN_TARGET_OPENCL) throw SkipTestException("Test is disabled in OpenVINO 2020.4"); @@ -174,7 +182,10 @@ PERF_TEST_P_(DNNTestNetwork, YOLOv3) PERF_TEST_P_(DNNTestNetwork, YOLOv4) { - applyTestTag(CV_TEST_TAG_MEMORY_2GB); + applyTestTag( + CV_TEST_TAG_MEMORY_2GB, + CV_TEST_TAG_DEBUG_VERYLONG + ); if (target == DNN_TARGET_MYRIAD) // not enough resources throw SkipTestException(""); #if defined(INF_ENGINE_RELEASE) && INF_ENGINE_VER_MAJOR_EQ(2020040000) // nGraph compilation failure @@ -206,15 +217,23 @@ PERF_TEST_P_(DNNTestNetwork, YOLOv5) { processNet("", "dnn/yolov5n.onnx", inp); } -PERF_TEST_P_(DNNTestNetwork, YOLOv8) { - applyTestTag(CV_TEST_TAG_MEMORY_512MB); +PERF_TEST_P_(DNNTestNetwork, YOLOv8) +{ + applyTestTag( + CV_TEST_TAG_MEMORY_512MB, + CV_TEST_TAG_DEBUG_LONG + ); + Mat sample = imread(findDataFile("dnn/dog416.png")); Mat inp = blobFromImage(sample, 1.0 / 255.0, Size(640, 640), Scalar(), true); processNet("", "dnn/yolov8n.onnx", inp); } PERF_TEST_P_(DNNTestNetwork, YOLOX) { - applyTestTag(CV_TEST_TAG_MEMORY_512MB); + applyTestTag( + CV_TEST_TAG_MEMORY_512MB, + CV_TEST_TAG_DEBUG_VERYLONG + ); Mat sample = imread(findDataFile("dnn/dog416.png")); Mat inp = blobFromImage(sample, 1.0 / 255.0, Size(640, 640), Scalar(), true); processNet("", "dnn/yolox_s.onnx", inp); @@ -222,16 +241,22 @@ PERF_TEST_P_(DNNTestNetwork, YOLOX) { PERF_TEST_P_(DNNTestNetwork, EAST_text_detection) { + applyTestTag(CV_TEST_TAG_DEBUG_VERYLONG); + processNet("dnn/frozen_east_text_detection.pb", "", cv::Size(320, 320)); } PERF_TEST_P_(DNNTestNetwork, FastNeuralStyle_eccv16) { + applyTestTag(CV_TEST_TAG_DEBUG_VERYLONG); + processNet("", "dnn/mosaic-9.onnx", cv::Size(224, 224)); } PERF_TEST_P_(DNNTestNetwork, Inception_v2_Faster_RCNN) { + applyTestTag(CV_TEST_TAG_DEBUG_VERYLONG); + #if defined(INF_ENGINE_RELEASE) && INF_ENGINE_VER_MAJOR_EQ(2019010000) if (backend == DNN_BACKEND_INFERENCE_ENGINE_NN_BUILDER_2019) throw SkipTestException("Test is disabled in OpenVINO 2019R1"); @@ -317,17 +342,16 @@ PERF_TEST_P_(DNNTestNetwork, CRNN) { processNet("", "dnn/text_recognition_CRNN_EN_2021sep.onnx", inp); } -PERF_TEST_P_(DNNTestNetwork, ViTTrack) { +PERF_TEST_P_(DNNTestNetwork, VitTrack) { Mat inp1(cv::Size(128, 128), CV_32FC3); Mat inp2(cv::Size(256, 256), CV_32FC3); randu(inp1, 0.0f, 1.0f); randu(inp2, 0.0f, 1.0f); inp1 = blobFromImage(inp1, 1.0, Size(), Scalar(), false); inp2 = blobFromImage(inp2, 1.0, Size(), Scalar(), false); - processNet("", "dnn/onnx/models/vitTracker.onnx", {std::make_tuple(inp1, "template"), std::make_tuple(inp2, "search")}); + processNet("", "dnn/onnx/models/object_tracking_vittrack_2023sep.onnx", {std::make_tuple(inp1, "template"), std::make_tuple(inp2, "search")}); } - PERF_TEST_P_(DNNTestNetwork, EfficientDet_int8) { if (target != DNN_TARGET_CPU || (backend != DNN_BACKEND_OPENCV && @@ -339,6 +363,13 @@ PERF_TEST_P_(DNNTestNetwork, EfficientDet_int8) processNet("", "dnn/tflite/coco_efficientdet_lite0_v1_1.0_quant_2021_09_06.tflite", inp); } +PERF_TEST_P_(DNNTestNetwork, VIT_B_32) +{ + applyTestTag(CV_TEST_TAG_DEBUG_VERYLONG); + + processNet("", "dnn/onnx/models/vit_b_32.onnx", cv::Size(224, 224)); +} + INSTANTIATE_TEST_CASE_P(/*nothing*/, DNNTestNetwork, dnnBackendsAndTargets()); } // namespace diff --git a/modules/dnn/src/caffe/caffe_importer.cpp b/modules/dnn/src/caffe/caffe_importer.cpp index a5ee074ef8..6606fc301b 100644 --- a/modules/dnn/src/caffe/caffe_importer.cpp +++ b/modules/dnn/src/caffe/caffe_importer.cpp @@ -499,6 +499,11 @@ public: { type = "Convolution"; } + else if (type == "Softmax"){ + // set default axis to 1 + if(!layerParams.has("axis")) + layerParams.set("axis", 1); + } int id = dstNet.addLayer(name, type, layerParams); diff --git a/modules/dnn/src/cuda/eltwise_ops.cu b/modules/dnn/src/cuda/eltwise_ops.cu index f94bdb8113..16f6cccf6b 100644 --- a/modules/dnn/src/cuda/eltwise_ops.cu +++ b/modules/dnn/src/cuda/eltwise_ops.cu @@ -319,7 +319,13 @@ void eltwise_div_2(const Stream& stream, TensorSpan output, TensorView x, eltwise_op>(stream, output, x, y); } +template +void eltwise_sub_2(const Stream& stream, TensorSpan output, TensorView x, TensorView y) { + eltwise_op>(stream, output, x, y); +} + #if !defined(__CUDA_ARCH__) || (__CUDA_ARCH__ >= 530) + template void eltwise_sub_2(const Stream& stream, TensorSpan<__half> output, TensorView<__half> x, TensorView<__half> y); template void eltwise_div_2(const Stream& stream, TensorSpan<__half> output, TensorView<__half> x, TensorView<__half> y); template void eltwise_prod_2(const Stream& stream, TensorSpan<__half> output, TensorView<__half> x, TensorView<__half> y); template void eltwise_sum_coeff_2(const Stream&, TensorSpan<__half>, __half, TensorView<__half>, __half, TensorView<__half>); @@ -327,6 +333,7 @@ void eltwise_div_2(const Stream& stream, TensorSpan output, TensorView x, template void eltwise_max_2(const Stream& stream, TensorSpan<__half> output, TensorView<__half> x, TensorView<__half> y); template void eltwise_min_2(const Stream& stream, TensorSpan<__half> output, TensorView<__half> x, TensorView<__half> y); #endif + template void eltwise_sub_2(const Stream& stream, TensorSpan output, TensorView x, TensorView y); template void eltwise_div_2(const Stream& stream, TensorSpan output, TensorView x, TensorView y); template void eltwise_prod_2(const Stream& stream, TensorSpan output, TensorView x, TensorView y); template void eltwise_sum_coeff_2(const Stream&, TensorSpan, float, TensorView, float, TensorView); diff --git a/modules/dnn/src/cuda/functors.hpp b/modules/dnn/src/cuda/functors.hpp index 3e487cd98a..2df32030f0 100644 --- a/modules/dnn/src/cuda/functors.hpp +++ b/modules/dnn/src/cuda/functors.hpp @@ -741,6 +741,18 @@ struct DivFunctor { CUDA4DNN_DEVICE T operator()(T x, T y) { return x / y; } }; +template +struct SubFunctor { + struct Params { + CUDA4DNN_HOST_DEVICE Params() { } + }; + + CUDA4DNN_DEVICE SubFunctor() { } + CUDA4DNN_DEVICE SubFunctor(const Params& params) { } + + CUDA4DNN_DEVICE T operator()(T x, T y) { return x - y; } +}; + template struct SignFunctor { struct Params { diff --git a/modules/dnn/src/cuda/mvn.cu b/modules/dnn/src/cuda/mvn.cu index adf997c0b0..0accc499a2 100644 --- a/modules/dnn/src/cuda/mvn.cu +++ b/modules/dnn/src/cuda/mvn.cu @@ -66,6 +66,38 @@ namespace raw { output[idx] = (static_cast(input[idx]) - means[outer_idx]) * scale[outer_idx]; } } + + template + __global__ void normalize_mean_variance_channelwise(Span output, View input, View scale, View bias, View means, View inv_stddev, size_type inner_size, size_type C) { + for (auto idx : grid_stride_range(output.size())) { + const index_type outer_idx = idx / inner_size; + const index_type c = outer_idx % C; + auto s = static_cast(scale[c]) * inv_stddev[outer_idx]; + auto b = static_cast(bias[c]); + output[idx] = (static_cast(input[idx]) - means[outer_idx]) * s + b; + } + } + + template + __global__ void normalize_mean_variance_layernorm(Span output, View input, View scale, View means, View inv_stddev, size_type inner_size) { + for (auto idx : grid_stride_range(output.size())) { + const index_type outer_idx = idx / inner_size; + const index_type inner_idx = idx % inner_size; + auto s = static_cast(scale[inner_idx]) * inv_stddev[outer_idx]; + output[idx] = (static_cast(input[idx]) - means[outer_idx]) * s; + } + } + + template + __global__ void normalize_mean_variance_layernorm_with_bias(Span output, View input, View scale, View bias, View means, View inv_stddev, size_type inner_size) { + for (auto idx : grid_stride_range(output.size())) { + const index_type outer_idx = idx / inner_size; + const index_type inner_idx = idx % inner_size; + auto s = static_cast(scale[inner_idx]) * inv_stddev[outer_idx]; + auto b = static_cast(bias[inner_idx]); + output[idx] = (static_cast(input[idx]) - means[outer_idx]) * s + b; + } + } } template @@ -142,4 +174,55 @@ template void normalize_mean_variance(const Stream&, Span<__half>, View<__half>, #endif template void normalize_mean_variance(const Stream&, Span, View, View, View, std::size_t); +template +void normalize_mean_variance_channelwise(const Stream& stream, Span output, View input, View scale, View bias, View means, View inv_stddev, std::size_t inner_size, std::size_t C) +{ + CV_Assert(input.size() == output.size()); + CV_Assert(input.size() / inner_size == means.size()); + CV_Assert(means.size() == inv_stddev.size()); + + auto kernel = raw::normalize_mean_variance_channelwise; + auto policy = make_policy(kernel, output.size(), 0, stream); + launch_kernel(kernel, policy, output, input, scale, bias, means, inv_stddev, inner_size, C); +} + +#if !defined(__CUDA_ARCH__) || (__CUDA_ARCH__ >= 530) +template void normalize_mean_variance_channelwise(const Stream&, Span<__half> /*output*/, View<__half> /*input*/, View<__half> /*scale*/, View<__half> /*bias*/, View /*means*/, View /*inv_stddev*/, std::size_t, std::size_t); +#endif +template void normalize_mean_variance_channelwise(const Stream&, Span /*output*/, View /*input*/, View /*scale*/, View /*bias*/, View /*means*/, View /*inv_stddev*/, std::size_t, std::size_t); + +template +void normalize_mean_variance_layernorm(const Stream& stream, Span output, View input, View scale, View means, View inv_stddev, std::size_t inner_size) +{ + CV_Assert(input.size() == output.size()); + CV_Assert(input.size() / inner_size == means.size()); + CV_Assert(means.size() == inv_stddev.size()); + + auto kernel = raw::normalize_mean_variance_layernorm; + auto policy = make_policy(kernel, output.size(), 0, stream); + launch_kernel(kernel, policy, output, input, scale, means, inv_stddev, inner_size); +} + +#if !defined(__CUDA_ARCH__) || (__CUDA_ARCH__ >= 530) +template void normalize_mean_variance_layernorm(const Stream&, Span<__half> /*output*/, View<__half> /*input*/, View<__half> /*scale*/, View /*means*/, View /*inv_stddev*/, std::size_t); +#endif +template void normalize_mean_variance_layernorm(const Stream&, Span /*output*/, View /*input*/, View /*scale*/, View /*means*/, View /*inv_stddev*/, std::size_t); + +template +void normalize_mean_variance_layernorm(const Stream& stream, Span output, View input, View scale, View bias, View means, View inv_stddev, std::size_t inner_size) +{ + CV_Assert(input.size() == output.size()); + CV_Assert(input.size() / inner_size == means.size()); + CV_Assert(means.size() == inv_stddev.size()); + + auto kernel = raw::normalize_mean_variance_layernorm_with_bias; + auto policy = make_policy(kernel, output.size(), 0, stream); + launch_kernel(kernel, policy, output, input, scale, bias, means, inv_stddev, inner_size); +} + +#if !defined(__CUDA_ARCH__) || (__CUDA_ARCH__ >= 530) +template void normalize_mean_variance_layernorm(const Stream&, Span<__half> /*output*/, View<__half> /*input*/, View<__half> /*scale*/, View<__half> /*bias*/, View /*means*/, View /*inv_stddev*/, std::size_t); +#endif +template void normalize_mean_variance_layernorm(const Stream&, Span /*output*/, View /*input*/, View /*scale*/, View /*bias*/, View /*means*/, View /*inv_stddev*/, std::size_t); + }}}} /* namespace cv::dnn::cuda4dnn::kernels */ diff --git a/modules/dnn/src/cuda4dnn/csl/cublas.hpp b/modules/dnn/src/cuda4dnn/csl/cublas.hpp index 760e3824fd..96cf70fab9 100644 --- a/modules/dnn/src/cuda4dnn/csl/cublas.hpp +++ b/modules/dnn/src/cuda4dnn/csl/cublas.hpp @@ -8,6 +8,7 @@ #include "error.hpp" #include "stream.hpp" #include "pointer.hpp" +#include "memory.hpp" #include @@ -363,6 +364,145 @@ namespace cv { namespace dnn { namespace cuda4dnn { namespace csl { namespace cu ); } + /** @brief Strided batched GEMM for colummn-major matrices + * + * \f$ C_i = \alpha A_i B_i + \beta C_i \f$ for a stack of matrices A, B and C indexed by i + * + * @tparam T matrix element type (must be `half` or `float`) + * + * @param handle valid cuBLAS Handle + * @param trans_a use transposed matrix of A_i for computation + * @param trans_b use transposed matrix of B_i for computation + * @param M number of rows in C + * @param N number of columns in C + * @param K common dimension of A (or trans A) and B (or trans B) + * @param alpha scale factor for A B + * @param[in] A pointer to stack of column-major matrices A in device memory + * @param lda leading dimension of matrix A + * @param A_offsets offsets to get A slices + * @param[in] B pointer to stack of column-major matrices B in device memory + * @param ldb leading dimension of matrix B + * @param B_offsets offsets to get B slices + * @param beta scale factor for C + * @param[in,out] C pointer to stack of column-major matrices C in device memory + * @param ldc leading dimension of matrix C + * @param C_offsets offsets to get C slices + * @param batchCount number of matrices in the batch + * + * Exception Guarantee: Basic + */ + template + void gemmBatched(const Handle &handle, + bool trans_a, bool trans_b, + std::size_t M, std::size_t N, std::size_t K, + T alpha, + const DevicePtr A, std::size_t lda, std::vector A_offsets, + const DevicePtr B, std::size_t ldb, std::vector B_offsets, + T beta, + const DevicePtr C, std::size_t ldc, std::vector C_offsets, + std::size_t batchCount); + + template <> inline + void gemmBatched(const Handle &handle, + bool trans_a, bool trans_b, + std::size_t M, std::size_t N, std::size_t K, + half alpha, + const DevicePtr A, std::size_t lda, std::vector A_offsets, + const DevicePtr B, std::size_t ldb, std::vector B_offsets, + half beta, + const DevicePtr C, std::size_t ldc, std::vector C_offsets, + std::size_t batchCount) { + CV_Assert(handle); + + const auto opa = trans_a ? CUBLAS_OP_T : CUBLAS_OP_N, + opb = trans_b ? CUBLAS_OP_T : CUBLAS_OP_N; + const auto iM = static_cast(M), + iN = static_cast(N), + iK = static_cast(K), + ilda = static_cast(lda), + ildb = static_cast(ldb), + ildc = static_cast(ldc); + + const auto batch_count = static_cast(batchCount); + + AutoBuffer buffer(3 * batch_count); + auto A_slices = (half**)(buffer.data()); + auto B_slices = A_slices + batch_count; + auto C_slices = B_slices + batch_count; + // collect A, B and C slices + for (int i = 0; i < batch_count; i++) { + A_slices[i] = (half*)(A.get()) + A_offsets[i]; + B_slices[i] = (half*)(B.get()) + B_offsets[i]; + C_slices[i] = (half*)(C.get()) + C_offsets[i]; + } + + const half **dev_A_slices = 0, **dev_B_slices = 0; + half **dev_C_slices = 0; + cudaMalloc((void**)&dev_A_slices, batch_count * sizeof(half*)); + cudaMalloc((void**)&dev_B_slices, batch_count * sizeof(half*)); + cudaMalloc((void**)&dev_C_slices, batch_count * sizeof(half*)); + cudaMemcpy(dev_A_slices, A_slices, batch_count * sizeof(half*), cudaMemcpyHostToDevice); + cudaMemcpy(dev_B_slices, B_slices, batch_count * sizeof(half*), cudaMemcpyHostToDevice); + cudaMemcpy(dev_C_slices, C_slices, batch_count * sizeof(half*), cudaMemcpyHostToDevice); + + CUDA4DNN_CHECK_CUBLAS(cublasHgemmBatched(handle.get(), opa, opb, iM, iN, iK, &alpha, dev_A_slices, ilda, dev_B_slices, ildb, &beta, dev_C_slices, ildc, batch_count)); + + cudaFree(dev_A_slices); + cudaFree(dev_B_slices); + cudaFree(dev_C_slices); + } + + template <> inline + void gemmBatched(const Handle &handle, + bool trans_a, bool trans_b, + std::size_t M, std::size_t N, std::size_t K, + float alpha, + const DevicePtr A, std::size_t lda, std::vector A_offsets, + const DevicePtr B, std::size_t ldb, std::vector B_offsets, + float beta, + const DevicePtr C, std::size_t ldc, std::vector C_offsets, + std::size_t batchCount) { + CV_Assert(handle); + + const auto opa = trans_a ? CUBLAS_OP_T : CUBLAS_OP_N, + opb = trans_b ? CUBLAS_OP_T : CUBLAS_OP_N; + const auto iM = static_cast(M), + iN = static_cast(N), + iK = static_cast(K), + ilda = static_cast(lda), + ildb = static_cast(ldb), + ildc = static_cast(ldc); + + const auto batch_count = static_cast(batchCount); + + AutoBuffer buffer(3 * batch_count); + auto A_slices = (float**)(buffer.data()); + auto B_slices = A_slices + batch_count; + auto C_slices = B_slices + batch_count; + // collect A, B and C slices + for (int i = 0; i < batch_count; i++) { + A_slices[i] = (float*)(A.get()) + A_offsets[i]; + B_slices[i] = (float*)(B.get()) + B_offsets[i]; + C_slices[i] = (float*)(C.get()) + C_offsets[i]; + } + + const float **dev_A_slices = 0, **dev_B_slices = 0; + float **dev_C_slices = 0; + cudaMalloc((void**)&dev_A_slices, batch_count * sizeof(float*)); + cudaMalloc((void**)&dev_B_slices, batch_count * sizeof(float*)); + cudaMalloc((void**)&dev_C_slices, batch_count * sizeof(float*)); + cudaMemcpy(dev_A_slices, A_slices, batch_count * sizeof(float*), cudaMemcpyHostToDevice); + cudaMemcpy(dev_B_slices, B_slices, batch_count * sizeof(float*), cudaMemcpyHostToDevice); + cudaMemcpy(dev_C_slices, C_slices, batch_count * sizeof(float*), cudaMemcpyHostToDevice); + + // cuBLAS is column-major + CUDA4DNN_CHECK_CUBLAS(cublasSgemmBatched(handle.get(), opa, opb, iM, iN, iK, &alpha, dev_A_slices, ilda, dev_B_slices, ildb, &beta, dev_C_slices, ildc, batch_count)); + + cudaFree(dev_A_slices); + cudaFree(dev_B_slices); + cudaFree(dev_C_slices); + } + }}}}} /* namespace cv::dnn::cuda4dnn::csl::cublas */ #endif /* OPENCV_DNN_SRC_CUDA4DNN_CSL_CUBLAS_HPP */ diff --git a/modules/dnn/src/cuda4dnn/csl/tensor_ops.hpp b/modules/dnn/src/cuda4dnn/csl/tensor_ops.hpp index 27f8306bf3..868b0c9284 100644 --- a/modules/dnn/src/cuda4dnn/csl/tensor_ops.hpp +++ b/modules/dnn/src/cuda4dnn/csl/tensor_ops.hpp @@ -152,6 +152,31 @@ namespace cv { namespace dnn { namespace cuda4dnn { namespace csl { batch_size); } + /** @brief performs generalized matrix-multiplication for a strided batch of matrices + * + * Pre-conditions: + * - A, B and C must be rank three tensors with dimensions (batch, rows, cols) + * - the last two axes of \p A and \p B must meet the mathematical requirements for matrix multiplication + * - \p C must be large enough to hold the result and the matrices must not overlap in memory + * + * Exception Guarantee: Basic + */ + template inline + void gemmBatched(const cublas::Handle& handle, std::size_t batch, + T beta, TensorSpan C, const std::vector C_offsets, T alpha, + bool trans_a, TensorView A, const std::vector A_offsets, + bool trans_b, TensorView B, const std::vector B_offsets) { + const auto M = C.get_axis_size(-2), + N = C.get_axis_size(-1), + K = A.get_axis_size(trans_a ? -2 : -1); + const auto lda = A.get_axis_size(-1), + ldb = B.get_axis_size(-1), + ldc = N; + + // collect pointers and run cublasSgemmBatched / cublasHgemmBatched + csl::cublas::gemmBatched(handle, trans_b, trans_a, N, M, K, 1.f, B.get(), ldb, B_offsets, A.get(), lda, A_offsets, 0.f, C.get(), ldc, C_offsets, batch); + } + /** @brief performs element-wise addition with broadcasting * * Pre-conditions: diff --git a/modules/dnn/src/cuda4dnn/kernels/eltwise_ops.hpp b/modules/dnn/src/cuda4dnn/kernels/eltwise_ops.hpp index 0e44372fee..3dc3355b3b 100644 --- a/modules/dnn/src/cuda4dnn/kernels/eltwise_ops.hpp +++ b/modules/dnn/src/cuda4dnn/kernels/eltwise_ops.hpp @@ -30,6 +30,9 @@ namespace cv { namespace dnn { namespace cuda4dnn { namespace kernels { template void eltwise_div_2(const csl::Stream& stream, csl::TensorSpan output, csl::TensorView x, csl::TensorView y); + template + void eltwise_sub_2(const csl::Stream& stream, csl::TensorSpan output, csl::TensorView x, csl::TensorView y); + }}}} /* namespace cv::dnn::cuda4dnn::kernels */ #endif /* OPENCV_DNN_SRC_CUDA4DNN_KERNELS_ELTWISE_OPS_HPP */ diff --git a/modules/dnn/src/cuda4dnn/kernels/mvn.hpp b/modules/dnn/src/cuda4dnn/kernels/mvn.hpp index b5a573e921..6cddeb22bb 100644 --- a/modules/dnn/src/cuda4dnn/kernels/mvn.hpp +++ b/modules/dnn/src/cuda4dnn/kernels/mvn.hpp @@ -26,6 +26,15 @@ void normalize_mean(const csl::Stream& stream, csl::Span output, csl::View template void normalize_mean_variance(const csl::Stream& stream, csl::Span output, csl::View input, csl::View means, csl::View scale, std::size_t inner_size); +template +void normalize_mean_variance_channelwise(const csl::Stream &stream, csl::Span output, csl::View input, csl::View scale, csl::View bias, csl::View means, csl::View inv_stddev, std::size_t inner_size, std::size_t C); + +template +void normalize_mean_variance_layernorm(const csl::Stream &stream, csl::Span output, csl::View input, csl::View scale, csl::View means, csl::View inv_stddev, std::size_t inner_size); + +template +void normalize_mean_variance_layernorm(const csl::Stream &stream, csl::Span output, csl::View input, csl::View scale, csl::View bias, csl::View means, csl::View inv_stddev, std::size_t inner_size); + }}}} /* namespace cv::dnn::cuda4dnn::kernels */ #endif /* OPENCV_DNN_SRC_CUDA4DNN_KERNELS_MVN_HPP */ diff --git a/modules/dnn/src/cuda4dnn/primitives/eltwise.hpp b/modules/dnn/src/cuda4dnn/primitives/eltwise.hpp index b46f0d870f..05bca83820 100644 --- a/modules/dnn/src/cuda4dnn/primitives/eltwise.hpp +++ b/modules/dnn/src/cuda4dnn/primitives/eltwise.hpp @@ -27,6 +27,7 @@ namespace cv { namespace dnn { namespace cuda4dnn { PRODUCT, DIV, MIN, + SUB, }; class EltwiseOpBase : public CUDABackendNode { @@ -88,6 +89,7 @@ namespace cv { namespace dnn { namespace cuda4dnn { else kernels::eltwise_sum_coeff_2(stream, output, coeffs[0], input_x, coeffs[1], input_y); break; + case EltwiseOpType::SUB: kernels::eltwise_sub_2(stream, output, input_x, input_y); break; } } else @@ -119,6 +121,7 @@ namespace cv { namespace dnn { namespace cuda4dnn { kernels::eltwise_sum_coeff_2(stream, output, coeff_x, output, coeffs[i], input); } break; + case EltwiseOpType::SUB: kernels::eltwise_sub_2(stream, output, output, input); break; } } } diff --git a/modules/dnn/src/cuda4dnn/primitives/instance_norm.hpp b/modules/dnn/src/cuda4dnn/primitives/instance_norm.hpp new file mode 100644 index 0000000000..0a32e40fc0 --- /dev/null +++ b/modules/dnn/src/cuda4dnn/primitives/instance_norm.hpp @@ -0,0 +1,86 @@ +// 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. + +#ifndef OPENCV_DNN_SRC_CUDA4DNN_PRIMITIVES_INSTANCE_NORM_HPP +#define OPENCV_DNN_SRC_CUDA4DNN_PRIMITIVES_INSTANCE_NORM_HPP + +#include "../../op_cuda.hpp" + +#include "../csl/stream.hpp" +#include "../csl/span.hpp" +#include "../csl/tensor.hpp" +#include "../csl/workspace.hpp" + +#include "../kernels/fill_copy.hpp" +#include "../kernels/mvn.hpp" + +#include + +#include +#include +#include + +namespace cv { namespace dnn { namespace cuda4dnn { + + template + class InstanceNormOp final : public CUDABackendNode { + public: + using wrapper_type = GetCUDABackendWrapperType; + + InstanceNormOp(csl::Stream stream_, float epsilon_, size_t loops) + : stream(std::move(stream_)), epsilon(epsilon_) { + csl::WorkspaceBuilder builder; + builder.require(loops); + builder.require(loops); + scratch_mem_in_bytes = builder.required_workspace_size(); + } + + void forward(const std::vector>& inputs, + const std::vector>& outputs, + csl::Workspace& workspace) override { + auto input_wrapper = inputs[0].dynamicCast(); + auto scale_wrapper = inputs[1].dynamicCast(); + auto bias_wrapper = inputs[2].dynamicCast(); + + auto input = input_wrapper->getView(); + auto scale = scale_wrapper->getView(); + auto bias = bias_wrapper->getView(); + + auto output_wrapper = outputs[0].dynamicCast(); + auto output = output_wrapper->getSpan(); + + auto C = input.get_axis_size(1); + auto loops = input.size_range(0, 2); + auto norm_size = input.size_range(2, input.rank()); + if (norm_size == 1) { + kernels::fill(stream, output, 0.f); + return; + } else { + auto ws_allocator = csl::WorkspaceAllocator(workspace); + + auto mean = ws_allocator.get_span(loops); + kernels::fill(stream, mean, 0.f); + + auto stdev = ws_allocator.get_span(loops); + kernels::fill(stream, stdev, 0.f); + + kernels::reduce_mean_sqr_sum(stream, mean, stdev, input, norm_size); + kernels::compute_normalization_scale(stream, stdev, mean, stdev, norm_size, epsilon); + kernels::normalize_mean_variance_channelwise(stream, output, input, scale, bias, mean, stdev, norm_size, C); + } + } + + std::size_t get_workspace_memory_in_bytes() const noexcept override { return scratch_mem_in_bytes; } + + private: + csl::Stream stream; + + float epsilon; + + std::size_t scratch_mem_in_bytes; + }; + +}}} // cv::dnn::cuda4dnn + +#endif // OPENCV_DNN_SRC_CUDA4DNN_PRIMITIVES_INSTANCE_NORM_HPP diff --git a/modules/dnn/src/cuda4dnn/primitives/layer_norm.hpp b/modules/dnn/src/cuda4dnn/primitives/layer_norm.hpp new file mode 100644 index 0000000000..7f4658a50a --- /dev/null +++ b/modules/dnn/src/cuda4dnn/primitives/layer_norm.hpp @@ -0,0 +1,93 @@ +// 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. + +#ifndef OPENCV_DNN_SRC_CUDA4DNN_PRIMITIVES_LAYER_NORM_HPP +#define OPENCV_DNN_SRC_CUDA4DNN_PRIMITIVES_LAYER_NORM_HPP + +#include "../../op_cuda.hpp" + +#include "../csl/stream.hpp" +#include "../csl/span.hpp" +#include "../csl/tensor.hpp" +#include "../csl/workspace.hpp" + +#include "../kernels/fill_copy.hpp" +#include "../kernels/mvn.hpp" + +#include + +#include +#include +#include + +namespace cv { namespace dnn { namespace cuda4dnn { + + template + class LayerNormOp final : public CUDABackendNode { + public: + using wrapper_type = GetCUDABackendWrapperType; + + LayerNormOp(csl::Stream stream_, int normalized_axis, float epsilon_, size_t loops) + : stream(std::move(stream_)), epsilon(epsilon_) { + CV_CheckGE(normalized_axis, 0, "LayerNorm/CUDA: axis needs to be normalized"); + axis = static_cast(normalized_axis); + + csl::WorkspaceBuilder builder; + builder.require(loops); + builder.require(loops); + scratch_mem_in_bytes = builder.required_workspace_size(); + } + + void forward(const std::vector>& inputs, + const std::vector>& outputs, + csl::Workspace& workspace) override { + auto input_wrapper = inputs[0].dynamicCast(); + auto scale_wrapper = inputs[1].dynamicCast(); + + auto input = input_wrapper->getView(); + auto scale = scale_wrapper->getView(); + + auto output_wrapper = outputs[0].dynamicCast(); + auto output = output_wrapper->getSpan(); + + auto loops = input.size_range(0, axis); + auto norm_size = input.size_range(axis, input.rank()); + if (norm_size == 1) { + kernels::fill(stream, output, 0.f); + return; + } else { + auto ws_allocator = csl::WorkspaceAllocator(workspace); + + auto mean = ws_allocator.get_span(loops); + kernels::fill(stream, mean, 0.f); + + auto inv_stddev = ws_allocator.get_span(loops); + kernels::fill(stream, inv_stddev, 0.f); + + kernels::reduce_mean_sqr_sum(stream, mean, inv_stddev, input, norm_size); + kernels::compute_normalization_scale(stream, inv_stddev, mean, inv_stddev, norm_size, epsilon); + if (inputs.size() == 3) { + auto bias_wrapper = inputs[2].dynamicCast(); + auto bias = bias_wrapper->getView(); + kernels::normalize_mean_variance_layernorm(stream, output, input, scale, bias, mean, inv_stddev, norm_size); + } else { + kernels::normalize_mean_variance_layernorm(stream, output, input, scale, mean, inv_stddev, norm_size); + } + } + } + + std::size_t get_workspace_memory_in_bytes() const noexcept override { return scratch_mem_in_bytes; } + + private: + csl::Stream stream; + + float epsilon; + size_t axis; + + std::size_t scratch_mem_in_bytes; + }; + +}}} // cv::dnn::cuda4dnn + +#endif // OPENCV_DNN_SRC_CUDA4DNN_PRIMITIVES_LAYER_NORM_HPP diff --git a/modules/dnn/src/cuda4dnn/primitives/matmul_broadcast.hpp b/modules/dnn/src/cuda4dnn/primitives/matmul_broadcast.hpp new file mode 100644 index 0000000000..824d917382 --- /dev/null +++ b/modules/dnn/src/cuda4dnn/primitives/matmul_broadcast.hpp @@ -0,0 +1,79 @@ +// 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. + +#ifndef OPENCV_DNN_SRC_CUDA4DNN_PRIMITIVES_MATMUL_BROADCAST_HPP +#define OPENCV_DNN_SRC_CUDA4DNN_PRIMITIVES_MATMUL_BROADCAST_HPP + +#include "../../op_cuda.hpp" + +#include "../csl/stream.hpp" +#include "../csl/cublas.hpp" +#include "../csl/tensor.hpp" +#include "../csl/tensor_ops.hpp" + +#include + +#include + +namespace cv { namespace dnn { namespace cuda4dnn { + + template + class MatMulBroadcastOp final : public CUDABackendNode { + public: + using wrapper_type = GetCUDABackendWrapperType; + + MatMulBroadcastOp(csl::Stream stream_, csl::cublas::Handle handle, const Mat &B, bool _transA, bool _transB, + const std::vector &A_offsets_, const std::vector &B_offsets_, std::vector &C_offsets_, + size_t batch_) + : stream(std::move(stream_)), cublasHandle(std::move(handle)), A_offsets(A_offsets_), B_offsets(B_offsets_), C_offsets(C_offsets_), batch(batch_) + { + if (!B.empty()) { + input_B_tensor = csl::makeTensorHeader(B); + csl::copyMatToTensor(B, input_B_tensor, stream); + } + + transA = _transA; + transB = _transB; + } + + void forward( + const std::vector>& inputs, + const std::vector>& outputs, + csl::Workspace& workspace) override + { + CV_Assert(((inputs.size() == 2 && input_B_tensor.empty()) || + (inputs.size() == 1 && !input_B_tensor.empty())) && outputs.size() == 1); + + auto input_A_wrapper = inputs[0].dynamicCast(); + auto input_A = input_A_wrapper->getView(); + + csl::TensorView input_B; + if (input_B_tensor.empty()) { + auto input_B_wrapper = inputs[1].dynamicCast(); + input_B = input_B_wrapper->getView(); + } else { + input_B = csl::TensorView(input_B_tensor); + } + + auto output_wrapper = outputs[0].dynamicCast(); + auto output = output_wrapper->getSpan(); + + csl::tensor_ops::gemmBatched(cublasHandle, batch, 0.f, output, C_offsets, 1.f, transA, input_A, A_offsets, transB, input_B, B_offsets); + } + + private: + csl::Stream stream; + csl::cublas::Handle cublasHandle; + csl::Tensor input_B_tensor; + bool transA, transB; + + std::vector A_offsets; + std::vector B_offsets; + std::vector C_offsets; + size_t batch; + }; + +}}} /* namespace cv::dnn::cuda4dnn */ + +#endif /* OPENCV_DNN_SRC_CUDA4DNN_PRIMITIVES_MATMUL_BROADCAST_HPP */ diff --git a/modules/dnn/src/cuda4dnn/primitives/scale_shift.hpp b/modules/dnn/src/cuda4dnn/primitives/scale_shift.hpp index 9da7ec3326..abe2615590 100644 --- a/modules/dnn/src/cuda4dnn/primitives/scale_shift.hpp +++ b/modules/dnn/src/cuda4dnn/primitives/scale_shift.hpp @@ -128,6 +128,9 @@ namespace cv { namespace dnn { namespace cuda4dnn { /* the scale shift operation might require broadcasting */ const int end_axis = [&] { + if (num_parameters == 1) { + return static_cast(axis + 1); + } for (int endAxis = axis + 1; endAxis <= input.rank(); endAxis++) { if (input.size_range(axis, endAxis) == mid_size) return endAxis; diff --git a/modules/dnn/src/darknet/darknet_io.cpp b/modules/dnn/src/darknet/darknet_io.cpp index 54140f8dc0..a3c7b37a73 100644 --- a/modules/dnn/src/darknet/darknet_io.cpp +++ b/modules/dnn/src/darknet/darknet_io.cpp @@ -309,11 +309,56 @@ namespace cv { fused_layer_names.push_back(last_layer); } + void setCrop(int crop_height, int crop_width, int inp_height, int inp_width, bool noadjust) + { + cv::dnn::LayerParams crop_param; + crop_param.name = "CropLayer-name"; + std::vector begin = {0, 0, (inp_height - crop_height) / 2, (inp_width - crop_width) / 2}; + std::vector sizes = {-1, -1, crop_height, crop_width}; + crop_param.set("begin", DictValue::arrayInt(&begin[0], begin.size())); + crop_param.set("size", DictValue::arrayInt(&sizes[0], sizes.size())); + crop_param.type = "Slice"; + + darknet::LayerParameter lp; + std::string layer_name = cv::format("crop_%d", layer_id); + lp.layer_name = layer_name; + lp.layer_type = crop_param.type; + lp.layerParams = crop_param; + lp.bottom_indexes.push_back(last_layer); + last_layer = layer_name; + net->layers.push_back(lp); + layer_id++; + + if (!noadjust) + { + cv::dnn::LayerParams params; + params.set("bias_term", true); + params.blobs = { + Mat(1, 1, CV_32F, Scalar(2)), + Mat(1, 1, CV_32F, Scalar(-1)) + }; + + darknet::LayerParameter lp; + std::string layer_name = cv::format("adjust_crop_%d", layer_id); + lp.layer_name = layer_name; + lp.layer_type = "Scale"; + lp.layerParams = params; + lp.bottom_indexes.push_back(last_layer); + last_layer = layer_name; + net->layers.push_back(lp); + layer_id++; + } + fused_layer_names.push_back(last_layer); + } + void setSoftmax() { cv::dnn::LayerParams softmax_param; softmax_param.name = "Softmax-name"; softmax_param.type = "Softmax"; + // set default axis to 1 + if(!softmax_param.has("axis")) + softmax_param.set("axis", 1); darknet::LayerParameter lp; std::string layer_name = cv::format("softmax_%d", layer_id); @@ -682,8 +727,8 @@ namespace cv { MatShape tensor_shape(3); tensor_shape[0] = net->channels; - tensor_shape[1] = net->width; - tensor_shape[2] = net->height; + tensor_shape[1] = net->height; + tensor_shape[2] = net->width; net->out_channels_vec.resize(net->layers_cfg.size()); layers_counter = -1; @@ -760,6 +805,19 @@ namespace cv { tensor_shape[1] = 1; tensor_shape[2] = 1; } + else if (layer_type == "crop") + { + int crop_height = getParam(layer_params, "crop_height", 0); + int crop_width = getParam(layer_params, "crop_width", 0); + bool noadjust = getParam(layer_params, "noadjust", false); + CV_CheckGT(crop_height, 0, ""); + CV_CheckGT(crop_width, 0, ""); + + setParams.setCrop(crop_height, crop_width, tensor_shape[1], tensor_shape[2], noadjust); + + tensor_shape[1] = crop_height; + tensor_shape[2] = crop_width; + } else if (layer_type == "softmax") { int groups = getParam(layer_params, "groups", 1); @@ -934,8 +992,8 @@ namespace cv { MatShape tensor_shape(3); tensor_shape[0] = net->channels; - tensor_shape[1] = net->width; - tensor_shape[2] = net->height; + tensor_shape[1] = net->height; + tensor_shape[2] = net->width; int cv_layers_counter = -1; int darknet_layers_counter = -1; diff --git a/modules/dnn/src/dnn_read.cpp b/modules/dnn/src/dnn_read.cpp index 89aef857d5..bb9fedb29a 100644 --- a/modules/dnn/src/dnn_read.cpp +++ b/modules/dnn/src/dnn_read.cpp @@ -58,7 +58,9 @@ Net readNet(const String& _framework, const std::vector& bufferModel, const std::vector& bufferConfig) { String framework = toLowerCase(_framework); - if (framework == "caffe") + if (framework == "onnx") + return readNetFromONNX(bufferModel); + else if (framework == "caffe") return readNetFromCaffe(bufferConfig, bufferModel); else if (framework == "tensorflow") return readNetFromTensorflow(bufferModel, bufferConfig); diff --git a/modules/dnn/src/dnn_utils.cpp b/modules/dnn/src/dnn_utils.cpp index f25da7458a..99b4e77e11 100644 --- a/modules/dnn/src/dnn_utils.cpp +++ b/modules/dnn/src/dnn_utils.cpp @@ -17,9 +17,9 @@ Image2BlobParams::Image2BlobParams():scalefactor(Scalar::all(1.0)), size(Size()) {} Image2BlobParams::Image2BlobParams(const Scalar& scalefactor_, const Size& size_, const Scalar& mean_, bool swapRB_, - int ddepth_, DataLayout datalayout_, ImagePaddingMode mode_): - scalefactor(scalefactor_), size(size_), mean(mean_), swapRB(swapRB_), ddepth(ddepth_), - datalayout(datalayout_), paddingmode(mode_) + int ddepth_, DataLayout datalayout_, ImagePaddingMode mode_, Scalar borderValue_): + scalefactor(scalefactor_), size(size_), mean(mean_), swapRB(swapRB_), ddepth(ddepth_), + datalayout(datalayout_), paddingmode(mode_), borderValue(borderValue_) {} void getVector(InputArrayOfArrays images_, std::vector& images) { @@ -196,7 +196,7 @@ void blobFromImagesWithParamsImpl(InputArrayOfArrays images_, Tmat& blob_, const int bottom = size.height - top - rh; int left = (size.width - rw)/2; int right = size.width - left - rw; - copyMakeBorder(images[i], images[i], top, bottom, left, right, BORDER_CONSTANT); + copyMakeBorder(images[i], images[i], top, bottom, left, right, BORDER_CONSTANT, param.borderValue); } else { @@ -382,6 +382,66 @@ void imagesFromBlob(const cv::Mat& blob_, OutputArrayOfArrays images_) } } +Rect Image2BlobParams::blobRectToImageRect(const Rect &r, const Size &oriImage) +{ + CV_Assert(!oriImage.empty()); + std::vector rImg, rBlob; + rBlob.push_back(Rect(r)); + rImg.resize(1); + this->blobRectsToImageRects(rBlob, rImg, oriImage); + return Rect(rImg[0]); +} + +void Image2BlobParams::blobRectsToImageRects(const std::vector &rBlob, std::vector& rImg, const Size& imgSize) +{ + Size size = this->size; + rImg.resize(rBlob.size()); + if (size != imgSize) + { + if (this->paddingmode == DNN_PMODE_CROP_CENTER) + { + float resizeFactor = std::max(size.width / (float)imgSize.width, + size.height / (float)imgSize.height); + for (int i = 0; i < rBlob.size(); i++) + { + rImg[i] = Rect((rBlob[i].x + 0.5 * (imgSize.width * resizeFactor - size.width)) / resizeFactor, + (rBlob[i].y + 0.5 * (imgSize.height * resizeFactor - size.height)) / resizeFactor, + rBlob[i].width / resizeFactor, + rBlob[i].height / resizeFactor); + } + } + else if (this->paddingmode == DNN_PMODE_LETTERBOX) + { + float resizeFactor = std::min(size.width / (float)imgSize.width, + size.height / (float)imgSize.height); + int rh = int(imgSize.height * resizeFactor); + int rw = int(imgSize.width * resizeFactor); + + int top = (size.height - rh) / 2; + int left = (size.width - rw) / 2; + for (int i = 0; i < rBlob.size(); i++) + { + rImg[i] = Rect((rBlob[i].x - left) / resizeFactor, + (rBlob[i].y - top) / resizeFactor, + rBlob[i].width / resizeFactor, + rBlob[i].height / resizeFactor); + } + } + else if (this->paddingmode == DNN_PMODE_NULL) + { + for (int i = 0; i < rBlob.size(); i++) + { + rImg[i] = Rect(rBlob[i].x * (float)imgSize.width / size.width, + rBlob[i].y * (float)imgSize.height / size.height, + rBlob[i].width * (float)imgSize.width / size.width, + rBlob[i].height * (float)imgSize.height / size.height); + } + } + else + CV_Error(CV_StsBadArg, "Unknown padding mode"); + } +} + CV__DNN_INLINE_NS_END }} // namespace cv::dnn diff --git a/modules/dnn/src/graph_simplifier.cpp b/modules/dnn/src/graph_simplifier.cpp index e58e0e38e8..2e1dc400be 100644 --- a/modules/dnn/src/graph_simplifier.cpp +++ b/modules/dnn/src/graph_simplifier.cpp @@ -77,68 +77,107 @@ int Subgraph::getInputNodeId(const Ptr& net, } bool Subgraph::match(const Ptr& net, int nodeId, - std::vector& matchedNodesIds, - std::vector& targetNodesIds) + std::vector& matchedNodesIds) { matchedNodesIds.clear(); - targetNodesIds.clear(); - std::queue nodesToMatch; - std::queue targetNodes; - nodesToMatch.push(nodeId); - targetNodes.push(nodes.size() - 1); - while (!nodesToMatch.empty()) + // Collection of all matchings states across branching. + // If there is no commutative ops in the subgraph - there would be just a single map. + std::vector>> matchCandidates; + matchCandidates.push_back(makePtr>()); + + struct State { - int nodeToMatch = nodesToMatch.front(); - int targetNodeId = targetNodes.front(); - nodesToMatch.pop(); - targetNodes.pop(); + int nodeToMatch; + int targetNodeId; + // Every state refers to current matchings pairs as well as + // matchings from parent branches produced by commutative ops. + std::vector>> matchings; - if (std::find(matchedNodesIds.begin(), matchedNodesIds.end(), nodeToMatch) != - matchedNodesIds.end()) + // When we register a matching pair we should register it in every parent branch. + // This is actual for branching in case of commutative ops only. + void addMatch(std::pair match) + { + for (auto& m : matchings) + m->insert(match); + } + }; + + std::queue states; + states.push({nodeId, (int)nodes.size() - 1, matchCandidates}); + + while (!states.empty()) + { + auto state = states.front(); + states.pop(); + int nodeToMatch = state.nodeToMatch; + int targetNodeId = state.targetNodeId; + auto matchings = state.matchings.back(); + + if (matchings->find(targetNodeId) != matchings->end()) continue; + // Empty placeholder matches with any input type + if (nodes[targetNodeId].empty()) { + state.addMatch({targetNodeId, nodeToMatch}); + continue; + } + const Ptr node = net->getNode(nodeToMatch); if (node->getType() != nodes[targetNodeId]) - return false; + continue; std::vector& inputNodes = inputs[targetNodeId]; if (inputNodes.size() != node->getNumInputs()) - return false; + continue; - for (int j = 0; j < inputNodes.size(); ++j) + state.addMatch({targetNodeId, nodeToMatch}); + + bool isCommutative = net->isCommutativeOp(node->getType()); + if (isCommutative) { - if (nodes[inputNodes[j]].empty() || node->getInputName(j).empty()) // Unknown input node type. - continue; - nodeId = getInputNodeId(net, node, j); - const Ptr inpNode = net->getNode(nodeId); - if (inpNode->getType() != "Const" && inpNode->getType() != "Constant") - { - nodesToMatch.push(nodeId); - targetNodes.push(inputNodes[j]); - } - else if (nodes[inputNodes[j]] != "Const" && nodes[inputNodes[j]] != "Constant") - return false; - } - matchedNodesIds.push_back(nodeToMatch); - targetNodesIds.push_back(targetNodeId); - } + if (inputNodes.size() != 2) + CV_Error(Error::StsNotImplemented, "Commutative op fusion with more than 2 inputs"); - const int n = matchedNodesIds.size(); - std::vector > elements(n); - for (int i = 0; i < n; ++i) - elements[i] = std::make_pair(matchedNodesIds[i], targetNodesIds[i]); - std::sort(elements.begin(), elements.end()); - for (int i = 0; i < n; ++i) - { - matchedNodesIds[i] = elements[i].first; - targetNodesIds[i] = elements[i].second; + auto newMatchings = makePtr>(*matchings); + matchCandidates.push_back(newMatchings); + state.matchings.push_back(newMatchings); + states.push({getInputNodeId(net, node, 0), inputNodes[0], state.matchings}); + states.push({getInputNodeId(net, node, 1), inputNodes[1], state.matchings}); + state.matchings.pop_back(); + + newMatchings = makePtr>(*matchings); + matchCandidates.push_back(newMatchings); + state.matchings.push_back(newMatchings); + states.push({getInputNodeId(net, node, 0), inputNodes[1], state.matchings}); + states.push({getInputNodeId(net, node, 1), inputNodes[0], state.matchings}); + state.matchings.pop_back(); + } + else + { + for (int j = 0; j < inputNodes.size(); ++j) + { + nodeId = getInputNodeId(net, node, j); + states.push({nodeId, inputNodes[j], state.matchings}); + } + } } - return true; + for (auto& matchings : matchCandidates) + { + if (matchings->size() != nodes.size()) + continue; + matchedNodesIds.resize(matchings->size()); + for (int i = 0; i < matchings->size(); ++i) + { + CV_Assert(matchings->find(i) != matchings->end()); + matchedNodesIds[i] = matchings->at(i); + } + return true; + } + return false; } -void Subgraph::replace(const Ptr& net, const std::vector& matchedNodesIds, - const std::vector& targetNodesIds) +void Subgraph::replace(const Ptr& net, const std::vector& matchedNodesIds) { // Extract names of input nodes. std::vector inputsNames(fusedNodeInputs.size()); @@ -149,9 +188,9 @@ void Subgraph::replace(const Ptr& net, const std::vector node = net->getNode(matchedNodesIds[j]); - std::vector& inpIndices = inputs[targetNodesIds[j]]; + std::vector& inpIndices = inputs[j]; - CV_Assert(node->getNumInputs() == inpIndices.size()); + CV_Assert(inpIndices.empty() || node->getNumInputs() == inpIndices.size()); for (int k = 0; k < inpIndices.size(); ++k) { if (inpIndices[k] == fusedNodeInputs[i]) @@ -165,10 +204,7 @@ void Subgraph::replace(const Ptr& net, const std::vector node = net->getNode(matchedNodesIds.back()); - for (int i = matchedNodesIds.size() - 2; i >= 0; --i) - net->removeNode(matchedNodesIds[i]); // Modify the last node to be a fused one. node->setType(fusedNodeOp); @@ -190,18 +226,63 @@ void simplifySubgraphs(const Ptr& net, const std::vector >& patterns) { int numNodes = net->getNumNodes(); - std::vector matchedNodesIds, targetNodesIds; + std::vector matchedNodesIds; + std::vector nodesToRemove; for (int j = 0; j < patterns.size(); ++j) { for (int i = 0; i < numNodes; ++i) { - if (patterns[j]->match(net, i, matchedNodesIds, targetNodesIds)) + if (patterns[j]->match(net, i, matchedNodesIds)) { - patterns[j]->replace(net, matchedNodesIds, targetNodesIds); - numNodes -= matchedNodesIds.size() - 1; // #matchedNodes removed and one added. + patterns[j]->replace(net, matchedNodesIds); + // Remove matched nodes except the last one. + nodesToRemove.insert(nodesToRemove.end(), matchedNodesIds.begin(), matchedNodesIds.end() - 1); } } } + + if (nodesToRemove.empty()) + return; + + // Collect reference counts for every node + std::vector refcounts(net->getNumNodes(), 0); + std::map nodeIds; + + // Register node outputs. + // Every usage of one of the node's outputs should be counted. + for (int nodeId = 0; nodeId < refcounts.size(); ++nodeId) { + for (int i = 0; i < net->getNumOutputs(nodeId); ++i) { + std::string name = net->getOutputName(nodeId, i); + nodeIds[name] = nodeId; + } + } + + for (int nodeId = 0; nodeId < refcounts.size(); ++nodeId) { + // Increase counters for node's inputs + auto node = net->getNode(nodeId); + for (int i = 0; i < node->getNumInputs(); ++i) { + std::string inpName = node->getInputName(i); + if (inpName.empty()) + continue; + CV_Assert(nodeIds.find(inpName) != nodeIds.end()); + refcounts[nodeIds[inpName]] += 1; + } + } + + // Remove all fused nodes. Indices expected to be in descending order. + std::sort(nodesToRemove.begin(), nodesToRemove.end(), [](int a, int b) { return a > b; }); + for (int nodeId : nodesToRemove) { + if (refcounts[nodeId] == 0) { + // Decrease references to node's inputs and remove node itself + auto node = net->getNode(nodeId); + for (int i = 0; i < node->getNumInputs(); ++i) { + std::string inpName = node->getInputName(i); + refcounts[nodeIds[inpName]] -= 1; + } + net->removeNode(nodeId); + refcounts[nodeId] = -1; // Same node cannot be removed twice + } + } } }} // namespace cv::dnn diff --git a/modules/dnn/src/graph_simplifier.hpp b/modules/dnn/src/graph_simplifier.hpp index 22bc50e3e5..aa9be32a91 100644 --- a/modules/dnn/src/graph_simplifier.hpp +++ b/modules/dnn/src/graph_simplifier.hpp @@ -44,6 +44,8 @@ public: virtual std::string getOutputName(int nodeId, int outId) const = 0; virtual void removeNode(int idx) = 0; + + virtual bool isCommutativeOp(const std::string& type) const = 0; }; class Subgraph // Interface to match and replace subgraphs. @@ -75,12 +77,10 @@ public: // Match TensorFlow subgraph starting from with a set of nodes to be fused. // Const nodes are skipped during matching. Returns true if nodes are matched and can be fused. virtual bool match(const Ptr& net, int nodeId, - std::vector& matchedNodesIds, - std::vector& targetNodesIds); + std::vector& matchedNodesIds); // Fuse matched subgraph. - void replace(const Ptr& net, const std::vector& matchedNodesIds, - const std::vector& targetNodesIds); + void replace(const Ptr& net, const std::vector& matchedNodesIds); virtual void finalize(const Ptr& net, const Ptr& fusedNode, diff --git a/modules/dnn/src/init.cpp b/modules/dnn/src/init.cpp index e70d5dad47..9b433dac50 100644 --- a/modules/dnn/src/init.cpp +++ b/modules/dnn/src/init.cpp @@ -102,6 +102,7 @@ void initializeLayerFactory() CV_DNN_REGISTER_LAYER_CLASS(LRN, LRNLayer); CV_DNN_REGISTER_LAYER_CLASS(InnerProduct, InnerProductLayer); CV_DNN_REGISTER_LAYER_CLASS(Gemm, GemmLayer); + CV_DNN_REGISTER_LAYER_CLASS(MatMul, MatMulLayer); CV_DNN_REGISTER_LAYER_CLASS(Softmax, SoftmaxLayer); CV_DNN_REGISTER_LAYER_CLASS(SoftMax, SoftmaxLayer); // For compatibility. See https://github.com/opencv/opencv/issues/16877 CV_DNN_REGISTER_LAYER_CLASS(MVN, MVNLayer); @@ -160,6 +161,8 @@ void initializeLayerFactory() CV_DNN_REGISTER_LAYER_CLASS(GatherElements, GatherElementsLayer); CV_DNN_REGISTER_LAYER_CLASS(LayerNormalization, LayerNormLayer); CV_DNN_REGISTER_LAYER_CLASS(Expand, ExpandLayer); + CV_DNN_REGISTER_LAYER_CLASS(InstanceNormalization, InstanceNormLayer); + CV_DNN_REGISTER_LAYER_CLASS(Attention, AttentionLayer); CV_DNN_REGISTER_LAYER_CLASS(Crop, CropLayer); CV_DNN_REGISTER_LAYER_CLASS(Eltwise, EltwiseLayer); diff --git a/modules/dnn/src/layers/attention_layer.cpp b/modules/dnn/src/layers/attention_layer.cpp new file mode 100644 index 0000000000..64b39297f5 --- /dev/null +++ b/modules/dnn/src/layers/attention_layer.cpp @@ -0,0 +1,272 @@ +// 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 "../precomp.hpp" +#include "cpu_kernels/fast_gemm.hpp" +#include "cpu_kernels/softmax.hpp" + +#include + +namespace cv { namespace dnn { + +static void packWeight(size_t num_heads, size_t head_size, size_t input_hidden_size, + const float *weight_data, size_t hidden_size, std::vector &packed_weight, const FastGemmOpt &opt) { + // num_heads * pack(head_size, input_hidden_size) + size_t pack_size = fastGemmPackBSize(head_size, input_hidden_size, opt); + size_t packed_weight_size = num_heads * pack_size; + packed_weight.resize(packed_weight_size, 0.f); + auto *packed_weight_data = packed_weight.data(); + for (size_t i = 0; i < num_heads; i++) { + fastGemmPackB(false, head_size, input_hidden_size, weight_data, hidden_size, packed_weight_data, opt); + packed_weight_data += pack_size; + weight_data += head_size; + } +} + +// Operator spec: https://github.com/microsoft/onnxruntime/blob/v1.16.1/docs/ContribOperators.md#com.microsoft.Attention +class AttentionLayerImpl CV_FINAL : public AttentionLayer { + public: + AttentionLayerImpl(const LayerParams ¶ms) { + setParamsFrom(params); + + CV_CheckTrue(params.has("num_heads"), "DNN/Attention: num_heads is required but missing"); + num_heads = params.get("num_heads"); // required, no default value + + CV_CheckTrue(params.has("qkv_hidden_sizes"), "DNN/Attention: qkv_hidden_sizes is required but missing"); + auto param_qkv_hidden_sizes = params.get("qkv_hidden_sizes"); + CV_CheckEQ(param_qkv_hidden_sizes.size(), 3, "DNN/Attention: qkv_hidden_sizes must and only have three elements"); + + qkv_hidden_sizes.clear(); + qkv_hidden_sizes.resize(3); + qkv_hidden_sizes[0] = static_cast(param_qkv_hidden_sizes.get(0)); + qkv_hidden_sizes[1] = static_cast(param_qkv_hidden_sizes.get(1)); + /* v_hidden_size needs to be initialized in finalize in case v_slice_end=INT_MAX */ + + qkv_head_sizes.clear(); + qkv_head_sizes.resize(3); + qkv_head_sizes[0] = static_cast(qkv_hidden_sizes[0] / num_heads); + qkv_head_sizes[1] = static_cast(qkv_hidden_sizes[1] / num_heads); + + scale = 1.f / params.get("scale", sqrt(qkv_head_sizes[0])); + + output_ndims = params.get("output_ndims", 3); + + is_prepacked = false; + } + + virtual bool supportBackend(int backendId) CV_OVERRIDE { + return backendId == DNN_BACKEND_OPENCV; + } + + virtual bool getMemoryShapes(const std::vector &inputs, + const int requiredOutputs, + std::vector &outputs, + std::vector &internals) const CV_OVERRIDE { + CV_CheckEQ(inputs.size(), static_cast(3), "DNN/Attention: three inputs are required"); + const auto &input_shape = inputs[0]; + const auto &weight_shape = inputs[1]; + const auto &bias_shape = inputs[2]; + + CV_CheckEQ(input_shape.size(), static_cast(3), "DNN/Attention: invalid input dimension"); + CV_CheckEQ(weight_shape.size(), static_cast(2), "DNN/Attention: invalid weight dimension"); + + CV_CheckEQ(input_shape[2], weight_shape[0], "DNN/Attention: invalid input shape"); + CV_CheckEQ(weight_shape[1], bias_shape[0], "DNN/Attention: invalid weight or bias shape"); + + if (output_ndims == 3) { + outputs.assign(1, inputs[0]); + } else if (output_ndims == 2) { + int batch = input_shape[0], seq_len = input_shape[1], input_hidden_size = input_shape[2]; + MatShape output_shape{batch * seq_len, input_hidden_size}; + outputs.assign(1, output_shape); + } else { + CV_Error(Error::StsBadArg, format("DNN/Attention: invalid output dimension %zu, valid value is 2 or 3", output_ndims)); + } + return false; + } + + virtual void finalize(InputArrayOfArrays inputs_arr, OutputArrayOfArrays outputs_arr) CV_OVERRIDE { + opt.init(); + + std::vector inputs; + inputs_arr.getMatVector(inputs); + const auto input_shape = shape(inputs[0]); + batch_size = static_cast(input_shape[0]); + seq_len = static_cast(input_shape[1]); + input_hidden_size = static_cast(input_shape[2]); + + const auto weight_shape = shape(inputs[1]); + hidden_size = weight_shape[1]; + qkv_hidden_sizes[2] = hidden_size - qkv_hidden_sizes[0] - qkv_hidden_sizes[1]; + qkv_head_sizes[2] = static_cast(qkv_hidden_sizes[2] / num_heads); + } + + void forward(InputArrayOfArrays inputs_arr, OutputArrayOfArrays outputs_arr, OutputArrayOfArrays internals_arr) CV_OVERRIDE { + CV_TRACE_FUNCTION(); + CV_TRACE_ARG_VALUE(name, "name", name.c_str()); + + if (inputs_arr.depth() == CV_16S) + { + forward_fallback(inputs_arr, outputs_arr, internals_arr); + return; + } + + std::vector inputs, outputs; + inputs_arr.getMatVector(inputs); + outputs_arr.getMatVector(outputs); + + // prepack weights + if (!is_prepacked) { + // prepack + const auto &weight = inputs[1]; + const auto *weight_data = weight.ptr(); + packWeight(num_heads, qkv_head_sizes[0], input_hidden_size, weight_data, hidden_size, packed_weight_q, opt); + packWeight(num_heads, qkv_head_sizes[1], input_hidden_size, weight_data + qkv_hidden_sizes[0], hidden_size, packed_weight_k, opt); + packWeight(num_heads, qkv_head_sizes[2], input_hidden_size, weight_data + qkv_hidden_sizes[0] + qkv_hidden_sizes[1], hidden_size, packed_weight_v, opt); + + is_prepacked = true; + } + + float *packed_weights[3] = {packed_weight_q.data(), packed_weight_k.data(), packed_weight_v.data()}; + size_t packed_weights_size[3] = {packed_weight_q.size() / num_heads, packed_weight_k.size() / num_heads, packed_weight_v.size() / num_heads}; + + Mat gemm_buffer = Mat::zeros(1, int(batch_size * seq_len * hidden_size), CV_32F); + auto *Q = gemm_buffer.ptr(); + auto *K = Q + batch_size * seq_len * qkv_hidden_sizes[0]; + auto *V = K + batch_size * seq_len * qkv_hidden_sizes[1]; + float *QKV[3] = {Q, K, V}; // Q, K, V: [B, N, S, H] + { + const auto &input = inputs[0]; + const auto &bias = inputs[2]; + const auto *input_data = input.ptr(); + const auto *bias_data = bias.ptr(); + + opt.multi_thread = false; + auto fn = [&](const Range &r) { + for (int i = r.start; i < r.end; i++) { + const int batch_index = static_cast((i / 3) / num_heads); + const int head_index = static_cast((i / 3) % num_heads); + const int qkv_index = static_cast(i % 3); + + auto *dst = QKV[qkv_index]; + size_t head_size = qkv_head_sizes[qkv_index]; + + int input_offset = batch_index * seq_len * input_hidden_size; + int bias_offset = qkv_index * qkv_hidden_sizes[0] + head_index * head_size; + int dst_offset = (batch_index * num_heads + head_index) * (seq_len * head_size); + + // broadcast bias ([NH] -> [BN, SH]) and make copy to dst + const auto *bias_data_src = bias_data + bias_offset; + auto *dst_data = dst + dst_offset; + for (size_t seq_len_idx = 0; seq_len_idx < seq_len; seq_len_idx++) { + std::memcpy(dst_data, bias_data_src, head_size * sizeof(float)); + dst_data += head_size; + } + + auto *packed_weight = packed_weights[qkv_index] + packed_weights_size[qkv_index] * head_index; + // single-thread gemm kernel + fastGemm(false, seq_len, head_size, input_hidden_size, + 1.f, input_data + input_offset, input_hidden_size, + packed_weight, 1.f, dst + dst_offset, head_size, opt); + } + }; + + size_t loops = 3 * batch_size * num_heads; + double nstripes = loops * seq_len * qkv_head_sizes[0] * input_hidden_size * (1 / 1024.0); + parallel_for_(Range(0, loops), fn, nstripes); + } + + // Compute softmax(scale * matmul(Q, K)) + std::vector attention_prob_shape{int(batch_size * num_heads), int(seq_len), int(seq_len)}; + Mat attention_prob = Mat::zeros(attention_prob_shape.size(), attention_prob_shape.data(), CV_32F); + { + auto *output = attention_prob.ptr(); + + auto loops = batch_size * num_heads; + auto seq_len_square = seq_len * seq_len; + auto qk_head_size = qkv_head_sizes[0]; + auto qk_inner_size = seq_len * qk_head_size; + + // Compute scale * matmul(Q, K) + opt.multi_thread = false; + parallel_for_(Range(0, loops), [&] (const Range r) { + for (int i = r.start; i < r.end; i++) { + const int output_offset = i * seq_len_square; + + const auto *q = Q + qk_inner_size * i, *k = K + qk_inner_size * i; + fastGemm(false, true, seq_len, qk_head_size, seq_len, qk_head_size, + scale, q, qk_head_size, 1, + k, qk_head_size, 1, 0.f, + output + output_offset, seq_len, opt); + } + }, loops * seq_len * qk_head_size * seq_len * (1 / 1024.0)); + + // Compute softmax + softmax(attention_prob, attention_prob, attention_prob_shape.size() - 1); + } + + // Compute np.matmul(attention_prob, V) + Mat output_buffer = Mat::zeros(1, int(batch_size * num_heads * seq_len * qkv_head_sizes[2]), CV_32F); + { + auto *output = outputs[0].ptr(); + auto *output_buff = output_buffer.ptr(); + const auto *prob = attention_prob.ptr(); + + auto loops = batch_size * num_heads; + auto prob_inner_size = seq_len * seq_len; + auto v_head_size = qkv_head_sizes[2]; + auto v_inner_size = seq_len * v_head_size; + + opt.multi_thread = false; + parallel_for_(Range(0, loops), [&] (const Range &r) { + for (int i = r.start; i < r.end; i++) { + const int output_offset = i * v_inner_size; + + const auto *p = prob + i * prob_inner_size, *v = V + i * v_inner_size; + fastGemm(false, false, seq_len, seq_len, seq_len, v_head_size, + 1.f, p, seq_len, 1, + v, v_head_size, 1, 0.f, + output_buff + output_offset, v_head_size, opt); + + // tranpose on the fly + const int batch_index = static_cast(i / num_heads); + const int head_index = static_cast(i % num_heads); + auto *src = output_buff + output_offset; + auto *dst = output + (batch_index * seq_len * num_heads + head_index) * v_head_size; + for (int j = 0; j < seq_len; j++) { + std::memcpy(dst, src, v_head_size * sizeof(float)); + src += v_head_size; + dst += qkv_hidden_sizes[2]; + } + } + }, loops * seq_len * seq_len * v_head_size * (1 / 1024.0)); + } + } + + private: + size_t num_heads; + std::vector qkv_hidden_sizes; // order: {qk_hidden_size, qk_hidden_size, v_hidden_size} + float scale; + size_t output_ndims; + + std::vector qkv_head_sizes; // order: {qk_head_size, qk_head_size, v_head_size} + + size_t batch_size; + size_t seq_len; + size_t input_hidden_size; + size_t hidden_size; + + bool is_prepacked; + std::vector packed_weight_q; + std::vector packed_weight_k; + std::vector packed_weight_v; + + FastGemmOpt opt; +}; + +Ptr AttentionLayer::create(const LayerParams ¶ms) { + return makePtr(params); +} + +}} // cv::dnn diff --git a/modules/dnn/src/layers/cpu_kernels/conv_block.simd.hpp b/modules/dnn/src/layers/cpu_kernels/conv_block.simd.hpp index 3310a91b6b..22d7d5194a 100644 --- a/modules/dnn/src/layers/cpu_kernels/conv_block.simd.hpp +++ b/modules/dnn/src/layers/cpu_kernels/conv_block.simd.hpp @@ -8,16 +8,26 @@ namespace cv { namespace dnn { CV_CPU_OPTIMIZATION_NAMESPACE_BEGIN -void convBlock(int np, const float* a, const float* b, float* c, int ldc, bool init_c, int width, const int convMR, const int convNR); +void convBlock_F32(int np, const float* a, const float* b, float* c, int ldc, bool init_c, int width, const int convMR, const int convNR); -#if !defined(CV_CPU_OPTIMIZATION_DECLARATIONS_ONLY) && CV_AVX + +// FP 16 branch. +void convBlock_F16(int np, const char * _a, const char * _b, char * _c, int ldc, bool init_c, int width, + const int convMR_fp16, const int convNR_fp16); + +void convBlockMR1_F16(int np, const char* _a, const char* _b, float *c, const float _bias, bool init_c, + const float minval, const float maxval, bool ifMinMaxAct, const int width, const int convNR_FP16); + +#if !defined(CV_CPU_OPTIMIZATION_DECLARATIONS_ONLY) + +#if CV_AVX #if !CV_FMA3 // AVX workaround #undef _mm256_fmadd_ps #define _mm256_fmadd_ps(a, b, c) _mm256_add_ps(c, _mm256_mul_ps(a, b)) #endif -void convBlock(int np, const float* a, const float* b, float* c, int ldc, bool init_c, int width, const int convMR, const int convNR) +void convBlock_F32(int np, const float* a, const float* b, float* c, int ldc, bool init_c, int width, const int convMR, const int convNR) { CV_Assert(convMR == 4 && convNR == 24); __m256 c00 = _mm256_set1_ps(0.f), c01 = c00, c02 = c00; @@ -121,16 +131,11 @@ void convBlock(int np, const float* a, const float* b, float* c, int ldc, bool i _mm256_zeroupper(); } -#endif // CV_CPU_OPTIMIZATION_DECLARATIONS_ONLY +#endif -CV_CPU_OPTIMIZATION_NAMESPACE_END +#if CV_NEON -// NEON code work around. -namespace opt_NEON -{ -#if !defined(CV_CPU_OPTIMIZATION_DECLARATIONS_ONLY) && CV_NEON - -void convBlock(int np, const float* a, const float* b, float* c, int ldc, bool init_c, int width, const int convMR, const int convNR) +void convBlock_F32(int np, const float* a, const float* b, float* c, int ldc, bool init_c, int width, const int convMR, const int convNR) { #if CV_NEON_AARCH64 if (convMR == 4 && convNR == 28) // AARCH64 @@ -298,104 +303,104 @@ void convBlock(int np, const float* a, const float* b, float* c, int ldc, bool i } else #endif - if (convMR == 4 && convNR == 12) // ARMv7 - { - float32x4_t c0 = vdupq_n_f32(0.f), c1 = c0, c2 = c0; - float32x4_t c3 = vdupq_n_f32(0.f), c4 = c3, c5 = c3; - float32x4_t c6 = vdupq_n_f32(0.f), c7 = c6, c8 = c6; - float32x4_t c9 = vdupq_n_f32(0.f), c10 = c9, c11 = c9; - - float32x2_t a0 = vdup_n_f32(0.0f), a1 = a0; - float32x4_t b0 = vdupq_n_f32(0.0f), b1 = vdupq_n_f32(0.0f), b2 = vdupq_n_f32(0.0f); - - if (width > 8) + if (convMR == 4 && convNR == 12) // ARMv7 { - for (int p = 0; p < np; p++, a += convMR, b += convNR) + float32x4_t c0 = vdupq_n_f32(0.f), c1 = c0, c2 = c0; + float32x4_t c3 = vdupq_n_f32(0.f), c4 = c3, c5 = c3; + float32x4_t c6 = vdupq_n_f32(0.f), c7 = c6, c8 = c6; + float32x4_t c9 = vdupq_n_f32(0.f), c10 = c9, c11 = c9; + + float32x2_t a0 = vdup_n_f32(0.0f), a1 = a0; + float32x4_t b0 = vdupq_n_f32(0.0f), b1 = vdupq_n_f32(0.0f), b2 = vdupq_n_f32(0.0f); + + if (width > 8) { - a0 = vld1_f32(a), a1 = vld1_f32(a+2); - b0 = vld1q_f32(b), b1 = vld1q_f32(b + 4), b2 = vld1q_f32(b + 8); + for (int p = 0; p < np; p++, a += convMR, b += convNR) + { + a0 = vld1_f32(a), a1 = vld1_f32(a+2); + b0 = vld1q_f32(b), b1 = vld1q_f32(b + 4), b2 = vld1q_f32(b + 8); - c0 = vmlaq_lane_f32(c0, b0, a0, 0); - c1 = vmlaq_lane_f32(c1, b1, a0, 0); - c2 = vmlaq_lane_f32(c2, b2, a0, 0); + c0 = vmlaq_lane_f32(c0, b0, a0, 0); + c1 = vmlaq_lane_f32(c1, b1, a0, 0); + c2 = vmlaq_lane_f32(c2, b2, a0, 0); - c3 = vmlaq_lane_f32(c3, b0, a0, 1); - c4 = vmlaq_lane_f32(c4, b1, a0, 1); - c5 = vmlaq_lane_f32(c5, b2, a0, 1); + c3 = vmlaq_lane_f32(c3, b0, a0, 1); + c4 = vmlaq_lane_f32(c4, b1, a0, 1); + c5 = vmlaq_lane_f32(c5, b2, a0, 1); - c6 = vmlaq_lane_f32(c6, b0, a1, 0); - c7 = vmlaq_lane_f32(c7, b1, a1, 0); - c8 = vmlaq_lane_f32(c8, b2, a1, 0); + c6 = vmlaq_lane_f32(c6, b0, a1, 0); + c7 = vmlaq_lane_f32(c7, b1, a1, 0); + c8 = vmlaq_lane_f32(c8, b2, a1, 0); - c9 = vmlaq_lane_f32(c9 , b0, a1, 1); - c10 = vmlaq_lane_f32(c10, b1, a1, 1); - c11 = vmlaq_lane_f32(c11, b2, a1, 1); + c9 = vmlaq_lane_f32(c9 , b0, a1, 1); + c10 = vmlaq_lane_f32(c10, b1, a1, 1); + c11 = vmlaq_lane_f32(c11, b2, a1, 1); + } } - } - else if (width > 4) - { - for (int p = 0; p < np; p++, a += convMR, b += convNR) + else if (width > 4) { - a0 = vld1_f32(a), a1 = vld1_f32(a+2); - b0 = vld1q_f32(b), b1 = vld1q_f32(b + 4); + for (int p = 0; p < np; p++, a += convMR, b += convNR) + { + a0 = vld1_f32(a), a1 = vld1_f32(a+2); + b0 = vld1q_f32(b), b1 = vld1q_f32(b + 4); - c0 = vmlaq_lane_f32(c0, b0, a0, 0); - c1 = vmlaq_lane_f32(c1, b1, a0, 0); + c0 = vmlaq_lane_f32(c0, b0, a0, 0); + c1 = vmlaq_lane_f32(c1, b1, a0, 0); - c3 = vmlaq_lane_f32(c3, b0, a0, 1); - c4 = vmlaq_lane_f32(c4, b1, a0, 1); + c3 = vmlaq_lane_f32(c3, b0, a0, 1); + c4 = vmlaq_lane_f32(c4, b1, a0, 1); - c6 = vmlaq_lane_f32(c6, b0, a1, 0); - c7 = vmlaq_lane_f32(c7, b1, a1, 0); + c6 = vmlaq_lane_f32(c6, b0, a1, 0); + c7 = vmlaq_lane_f32(c7, b1, a1, 0); - c9 = vmlaq_lane_f32(c9 , b0, a1, 1); - c10 = vmlaq_lane_f32(c10, b1, a1, 1); + c9 = vmlaq_lane_f32(c9 , b0, a1, 1); + c10 = vmlaq_lane_f32(c10, b1, a1, 1); + } } + else + { + for (int p = 0; p < np; p++, a += convMR, b += convNR) + { + a0 = vld1_f32(a), a1 = vld1_f32(a+2); + b0 = vld1q_f32(b); + + c0 = vmlaq_lane_f32(c0, b0, a0, 0); + c3 = vmlaq_lane_f32(c3, b0, a0, 1); + c6 = vmlaq_lane_f32(c6, b0, a1, 0); + c9 = vmlaq_lane_f32(c9 , b0, a1, 1); + } + } + + if (!init_c) + { + c0 = vaddq_f32(c0, vld1q_f32(c)); + c1 = vaddq_f32(c1, vld1q_f32(c + 4)); + c2 = vaddq_f32(c2, vld1q_f32(c + 8)); + + c3 = vaddq_f32(c3, vld1q_f32(c + ldc)); + c4 = vaddq_f32(c4, vld1q_f32(c + ldc + 4)); + c5 = vaddq_f32(c5, vld1q_f32(c + ldc + 8)); + + c6 = vaddq_f32(c6, vld1q_f32(c + ldc * 2)); + c7 = vaddq_f32(c7, vld1q_f32(c + ldc * 2 + 4)); + c8 = vaddq_f32(c8, vld1q_f32(c + ldc * 2 + 8)); + + c9 = vaddq_f32(c9 , vld1q_f32(c + ldc * 3)); + c10 = vaddq_f32(c10, vld1q_f32(c + ldc * 3 + 4)); + c11 = vaddq_f32(c11, vld1q_f32(c + ldc * 3 + 8)); + } + + vst1q_f32(c, c0), vst1q_f32(c+4, c1), vst1q_f32(c+8, c2); + vst1q_f32(c + ldc, c3), vst1q_f32(c + ldc + 4, c4), vst1q_f32(c + ldc + 8, c5); + vst1q_f32(c + ldc*2, c6), vst1q_f32(c + ldc*2 + 4, c7), vst1q_f32(c + ldc*2 + 8, c8); + vst1q_f32(c + ldc*3, c9), vst1q_f32(c + ldc*3 + 4, c10), vst1q_f32(c + ldc*3 + 8, c11); } else - { - for (int p = 0; p < np; p++, a += convMR, b += convNR) - { - a0 = vld1_f32(a), a1 = vld1_f32(a+2); - b0 = vld1q_f32(b); - - c0 = vmlaq_lane_f32(c0, b0, a0, 0); - c3 = vmlaq_lane_f32(c3, b0, a0, 1); - c6 = vmlaq_lane_f32(c6, b0, a1, 0); - c9 = vmlaq_lane_f32(c9 , b0, a1, 1); - } - } - - if (!init_c) - { - c0 = vaddq_f32(c0, vld1q_f32(c)); - c1 = vaddq_f32(c1, vld1q_f32(c + 4)); - c2 = vaddq_f32(c2, vld1q_f32(c + 8)); - - c3 = vaddq_f32(c3, vld1q_f32(c + ldc)); - c4 = vaddq_f32(c4, vld1q_f32(c + ldc + 4)); - c5 = vaddq_f32(c5, vld1q_f32(c + ldc + 8)); - - c6 = vaddq_f32(c6, vld1q_f32(c + ldc * 2)); - c7 = vaddq_f32(c7, vld1q_f32(c + ldc * 2 + 4)); - c8 = vaddq_f32(c8, vld1q_f32(c + ldc * 2 + 8)); - - c9 = vaddq_f32(c9 , vld1q_f32(c + ldc * 3)); - c10 = vaddq_f32(c10, vld1q_f32(c + ldc * 3 + 4)); - c11 = vaddq_f32(c11, vld1q_f32(c + ldc * 3 + 8)); - } - - vst1q_f32(c, c0), vst1q_f32(c+4, c1), vst1q_f32(c+8, c2); - vst1q_f32(c + ldc, c3), vst1q_f32(c + ldc + 4, c4), vst1q_f32(c + ldc + 8, c5); - vst1q_f32(c + ldc*2, c6), vst1q_f32(c + ldc*2 + 4, c7), vst1q_f32(c + ldc*2 + 8, c8); - vst1q_f32(c + ldc*3, c9), vst1q_f32(c + ldc*3 + 4, c10), vst1q_f32(c + ldc*3 + 8, c11); - } - else - CV_Error(Error::StsNotImplemented, "Unsupported convMR and/or convNR in opt_NEON::convBlock"); + CV_Error(Error::StsNotImplemented, "Unsupported convMR and/or convNR in opt_NEON::convBlock"); } void convBlockMR1_F32(int np, const float * a, const float * b, float *c, const float bias, bool init_c, - const float minval, const float maxval, bool ifMinMaxAct, const int width, const int convNR) + const float minval, const float maxval, bool ifMinMaxAct, const int width, const int convNR) { CV_Assert(convNR == 28); float32x4_t c0 = vdupq_n_f32(bias), c1 = c0, c2 = c0; @@ -482,22 +487,17 @@ void convBlockMR1_F32(int np, const float * a, const float * b, float *c, const vst1q_f32(c + 20, c5); vst1q_f32(c + 24, c6); } - -#if CV_NEON_AARCH64 && defined(__ARM_FEATURE_FP16_VECTOR_ARITHMETIC) -// Fix conflict between float16_t in arm_neon.h and float16_t in cvdef.h. -typedef __fp16 float16_t; - -#ifndef __ARM_FEATURE_FMA // Work around without FMA support. -#define vfmaq_f16(a, b, c) (a + b * c) #endif -void convBlock_FP16(int np, const char * _a, const char * _b, char * _c, int ldc, bool init_c, int width, + +#if defined(CV_NEON_AARCH64) && CV_NEON_AARCH64 && defined(__ARM_FEATURE_FP16_VECTOR_ARITHMETIC) + +void convBlock_F16(int np, const char * _a, const char * _b, char * _c, int ldc, bool init_c, int width, const int convMR_fp16, const int convNR_fp16) { -#if 1 + typedef __fp16 float16_t; const float16_t* a = (const float16_t*)_a; const float16_t* b = (const float16_t*)_b; float16_t* c = (float16_t*)_c; - CV_Assert(convMR_fp16 == 8 && convNR_fp16 == 24); float16x8_t c00 = vdupq_n_f16(0), c01 = c00, c02 = c00; @@ -603,8 +603,8 @@ void convBlock_FP16(int np, const char * _a, const char * _b, char * _c, int ldc if (!init_c) { -#undef _FX_UPDATE_CBUF_ROW -#define _FX_UPDATE_CBUF_ROW(row) \ + #undef _FX_UPDATE_CBUF_ROW + #define _FX_UPDATE_CBUF_ROW(row) \ c##row##0 = c##row##0 + vld1q_f16(c + row*ldc); \ c##row##1 = c##row##1 + vld1q_f16(c + row*ldc + 8); \ c##row##2 = c##row##2 + vld1q_f16(c + row*ldc + 16) @@ -619,8 +619,8 @@ void convBlock_FP16(int np, const char * _a, const char * _b, char * _c, int ldc _FX_UPDATE_CBUF_ROW(7); } -#undef _FX_STORE_CBUF_ROW -#define _FX_STORE_CBUF_ROW(row) \ + #undef _FX_STORE_CBUF_ROW + #define _FX_STORE_CBUF_ROW(row) \ vst1q_f16(c + row*ldc, c##row##0); \ vst1q_f16(c + row*ldc + 8, c##row##1); \ vst1q_f16(c + row*ldc + 16, c##row##2) @@ -633,46 +633,12 @@ void convBlock_FP16(int np, const char * _a, const char * _b, char * _c, int ldc _FX_STORE_CBUF_ROW(5); _FX_STORE_CBUF_ROW(6); _FX_STORE_CBUF_ROW(7); -#else - // reference only. - const float16_t* a = (const float16_t*)_a; - const float16_t* b = (const float16_t*)_b; - float16_t* c = (float16_t*)_c; - float cbuf[convMR_fp16*convNR_fp16]; - memset(cbuf, 0, sizeof(cbuf)); - - for( int p = 0; p < np; p++ ) - { - for( int i = 0; i < convMR_fp16; i++ ) - { - float ai = float(a[convMR_fp16*p + i]); - for( int j = 0; j < convNR_fp16; j++ ) - cbuf[i*convNR_fp16+j] += float(b[convNR_fp16*p + j]) * ai; - } - } - - if (!init_c) - { - for(int i = 0; i < convMR_fp16; i++) - { - for(int j = 0; j < convNR_fp16; j++) - c[i*ldc + j] = float16_t(float(c[i*ldc + j]) + cbuf[i*convNR_fp16 + j]); - } - } - else - { - for(int i = 0; i < convMR_fp16; i++) - { - for(int j = 0; j < convNR_fp16; j++) - c[i*ldc + j] = (float16_t)(cbuf[i*convNR_fp16 + j]); - } - } -#endif } -void convBlockMR1_FP16(int np, const char* _a, const char* _b, float *c, const float _bias, bool init_c, - const float minval, const float maxval, bool ifMinMaxAct, const int width, const int convNR_FP16) +void convBlockMR1_F16(int np, const char* _a, const char* _b, float *c, const float _bias, bool init_c, + const float minval, const float maxval, bool ifMinMaxAct, const int width, const int convNR_FP16) { + typedef __fp16 float16_t; CV_Assert(convNR_FP16 == 24); // CONV_NR_FP16 = 24 const float16_t* a = (const float16_t*)_a; const float16_t* b = (const float16_t*)_b; @@ -685,7 +651,7 @@ void convBlockMR1_FP16(int np, const char* _a, const char* _b, float *c, const f { for (int p = 0; p < np; p++, a++, b += convNR_FP16) { - float16x8_t a0= vdupq_n_f16(a[0]); + float16x8_t a0 = vdupq_n_f16(a[0]); float16x8_t b0 = vld1q_f16(b), b1 = vld1q_f16(b + 8), b2 = vld1q_f16(b + 16); c0 = vfmaq_f16(c0, a0, b0); @@ -754,6 +720,7 @@ void convBlockMR1_FP16(int np, const char* _a, const char* _b, float *c, const f } #endif -#endif -} +#endif // CV_CPU_OPTIMIZATION_DECLARATIONS_ONLY + +CV_CPU_OPTIMIZATION_NAMESPACE_END }} // namespace cv::dnn diff --git a/modules/dnn/src/layers/cpu_kernels/conv_depthwise.cpp b/modules/dnn/src/layers/cpu_kernels/conv_depthwise.cpp index 59f069eeaa..8c1c643abe 100644 --- a/modules/dnn/src/layers/cpu_kernels/conv_depthwise.cpp +++ b/modules/dnn/src/layers/cpu_kernels/conv_depthwise.cpp @@ -92,7 +92,7 @@ void runDepthwise(InputArray _input, OutputArray _output, const Ptr& c ofstab[k] = dy * Wi + dx; } - const float *weights0 = conv->weightsBufPtr, *bias = conv->biasBuf.data(); + const float *weights0 = conv->getWeights(), *bias = conv->biasBuf.data(); const float* relu = reluslope.data(); CV_Assert(ksize > 1 || (pad_left == 0 && pad_right == 0 && pad_top == 0 && pad_bottom == 0)); diff --git a/modules/dnn/src/layers/cpu_kernels/conv_winograd_f63.cpp b/modules/dnn/src/layers/cpu_kernels/conv_winograd_f63.cpp index 605cf37949..6cf066576b 100644 --- a/modules/dnn/src/layers/cpu_kernels/conv_winograd_f63.cpp +++ b/modules/dnn/src/layers/cpu_kernels/conv_winograd_f63.cpp @@ -20,15 +20,15 @@ namespace cv { namespace dnn { #if CV_NEON || CV_SIMD128 || CV_TRY_AVX2 enum { VEC_ALIGN = 32, DFT_TYPE = CV_32F }; // Memory alignment. -void winofunc_accum_f32(const float* inwptr, const float* wptr, float* outbuf, int Cg, int iblock, +void winofunc_accum_F32(const float* inwptr, const float* wptr, float* outbuf, int Cg, int iblock, const int winoIblock, const int winoKblock, const int winoAtomF32, const int winoNatomF32); /*Input transform*/ -void winofunc_BtXB_8x8_f32(const float* inptr, int inpstep, +void winofunc_BtXB_8x8_F32(const float* inptr, int inpstep, float* outptr, int Cg, const int winoIblock, const int winoAtomF32); /*Output transform*/ -void winofunc_AtXA_8x8_f32(const float* inptr, int inpstep, float* bpptr, int bpstep, float* outptr, int outstep, +void winofunc_AtXA_8x8_F32(const float* inptr, int inpstep, float* bpptr, int bpstep, float* outptr, int outstep, float bias, float minval, float maxval, bool ifMinMaxAct); int runWinograd63(InputArray _input, InputArray _fusedAddMat, OutputArray _output, const Ptr& conv, @@ -67,6 +67,28 @@ int runWinograd63(InputArray _input, InputArray _fusedAddMat, OutputArray _outpu #endif const int CONV_WINO_NATOMS_F32 = CONV_WINO_AREA / CONV_WINO_ATOM_F32; // for AVX2, it is 8, otherwise, it's 16. + int CONV_WINO_ATOM = CONV_WINO_ATOM_F32; + int CONV_WINO_NATOMS = CONV_WINO_NATOMS_F32; + +#ifdef CONV_ARM_FP16 + // FP 16 + const int CONV_WINO_ATOM_F16 = CONV_WINO_ATOM_F32 * 2; + const int CONV_WINO_NATOMS_F16 = CONV_WINO_AREA / CONV_WINO_ATOM_F16; +#endif + + int esz = sizeof(float ); + +#ifdef CONV_ARM_FP16 + const bool useFP16 = conv->useFP16; + if (useFP16) + { + // works at FP 16. + CONV_WINO_ATOM = CONV_WINO_ATOM_F16; + CONV_WINO_NATOMS = CONV_WINO_NATOMS_F16; + esz = sizeof(float16_t); + } +#endif + int Kg_nblocks = (Kg + CONV_WINO_KBLOCK - 1)/CONV_WINO_KBLOCK; const size_t inp_planesize = (size_t)Hi*Wi; const size_t out_planesize = (size_t)H0*W0; @@ -78,9 +100,9 @@ int runWinograd63(InputArray _input, InputArray _fusedAddMat, OutputArray _outpu size_t totalbufsize = (size_t)N*C*blocks_per_plane_aligned*CONV_WINO_AREA; - AutoBuffer _buf; - _buf.allocate(totalbufsize + VEC_ALIGN); - float* wbuf_all = alignPtr(_buf.data(), VEC_ALIGN); + AutoBuffer _buf; + _buf.allocate((totalbufsize + VEC_ALIGN) * esz); + char* wbuf_all = alignPtr(_buf.data(), VEC_ALIGN * esz); float* inp = input.ptr(); float* out = output.ptr(); @@ -104,14 +126,15 @@ int runWinograd63(InputArray _input, InputArray _fusedAddMat, OutputArray _outpu int c = nc0 - n*C; int g = c / Cg; c -= g*Cg; + for (int block_id = 0; block_id < blocks_per_plane; block_id += CONV_WINO_IBLOCK) { for (int db = 0; db < CONV_WINO_IBLOCK; db++) { size_t inwofs = ((n*ngroups + g)*blocks_per_plane_aligned + block_id)*Cg*CONV_WINO_AREA + - (c*CONV_WINO_IBLOCK + db)*CONV_WINO_ATOM_F32; - float* inwptr = (float*)wbuf_all + inwofs; + (c*CONV_WINO_IBLOCK + db) * CONV_WINO_ATOM; + char* inwptr = wbuf_all + inwofs * esz; if (block_id + db < blocks_per_plane) { @@ -152,27 +175,40 @@ int runWinograd63(InputArray _input, InputArray _fusedAddMat, OutputArray _outpu inptr = inpbuf; inpstep = CONV_WINO_SIZE; } + #if CV_TRY_AVX2 if (conv->useAVX2) - opt_AVX2::winofunc_BtXB_8x8_f32(inptr, inpstep, inwptr, Cg, CONV_WINO_IBLOCK, CONV_WINO_ATOM_F32); + opt_AVX2::winofunc_BtXB_8x8_F32(inptr, inpstep, (float *)inwptr, Cg, CONV_WINO_IBLOCK, CONV_WINO_ATOM); else #endif #if CV_TRY_AVX if (conv->useAVX) - opt_AVX::winofunc_BtXB_8x8_f32(inptr, inpstep, inwptr, Cg, CONV_WINO_IBLOCK, CONV_WINO_ATOM_F32); + opt_AVX::winofunc_BtXB_8x8_F32(inptr, inpstep, (float *)inwptr, Cg, CONV_WINO_IBLOCK, CONV_WINO_ATOM); else #endif #if CV_NEON && CV_NEON_AARCH64 if (conv->useNEON) - opt_NEON::winofunc_BtXB_8x8_f32(inptr, inpstep, inwptr, Cg, CONV_WINO_IBLOCK, CONV_WINO_ATOM_F32); + { +#ifdef CONV_ARM_FP16 + if (useFP16) + { + opt_NEON_FP16::winofunc_BtXB_8x8_F16(inptr, inpstep, inwptr, Cg, CONV_WINO_IBLOCK, + CONV_WINO_ATOM); + } + else +#endif + opt_NEON::winofunc_BtXB_8x8_F32(inptr, inpstep, (float *)inwptr, Cg, CONV_WINO_IBLOCK, + CONV_WINO_ATOM); + } else #endif - winofunc_BtXB_8x8_f32(inptr, inpstep, inwptr, Cg, CONV_WINO_IBLOCK, CONV_WINO_ATOM_F32); + winofunc_BtXB_8x8_F32(inptr, inpstep, (float *)inwptr, Cg, CONV_WINO_IBLOCK, CONV_WINO_ATOM); + } else { - for (int i = 0; i < CONV_WINO_NATOMS_F32; i++, inwptr += CONV_WINO_IBLOCK*CONV_WINO_ATOM_F32) - memset(inwptr, 0, CONV_WINO_ATOM_F32*sizeof(inwptr[0])); + for (int i = 0; i < CONV_WINO_NATOMS; i++, inwptr += CONV_WINO_IBLOCK * CONV_WINO_ATOM * esz) + memset(inwptr, 0, CONV_WINO_ATOM * esz); } } } @@ -182,19 +218,37 @@ int runWinograd63(InputArray _input, InputArray _fusedAddMat, OutputArray _outpu // Phase 2. compute elemwise-weighted sums of transformed blocks, // apply inverse Winograd transforms to the sums, // add bias, apply activation function if any and store the results. + char* wptr0 = nullptr; +#ifdef CONV_ARM_FP16 + if (useFP16) + { + CV_Assert(!conv->weightsWinoBuf_FP16.empty()); + wptr0 = (char *)conv->getWeightsWinoFP16(); + } + else +#endif + { + CV_Assert(!conv->weightsWinoBuf.empty()); + wptr0 = (char *)conv->getWeightsWino(); + } + parallel_for_(Range(0, ntasks), [&](const Range& r0) { for (int task_id = r0.start; task_id < r0.end; task_id++) { - size_t out_wbuf_size = CONV_WINO_AREA*CONV_WINO_KBLOCK*CONV_WINO_IBLOCK; + size_t out_wbuf_size = CONV_WINO_AREA * CONV_WINO_KBLOCK * CONV_WINO_IBLOCK; size_t outbuf_size = CONV_WINO_AREA; - AutoBuffer out_wbuf_, outbuf_; - out_wbuf_.allocate(out_wbuf_size + VEC_ALIGN); - float* out_wbuf = alignPtr(out_wbuf_.data(), VEC_ALIGN); + + // For saving the accumulation output. + AutoBuffer out_wbuf_; + out_wbuf_.allocate((out_wbuf_size + VEC_ALIGN) * esz); + char* out_wbuf = alignPtr(out_wbuf_.data(), VEC_ALIGN * esz); + memset(out_wbuf, 0, out_wbuf_size * esz); + + // For saving the fuse_Add data. + AutoBuffer outbuf_; outbuf_.allocate(outbuf_size + VEC_ALIGN); float* outbuf = alignPtr(outbuf_.data(), VEC_ALIGN); - - memset(out_wbuf, 0, out_wbuf_size * sizeof(float)); - memset(outbuf, 0, outbuf_size * sizeof(float)); + memset(outbuf, 0, outbuf_size * sizeof(outbuf[0])); int ngk0 = (int)(((int64_t)N*Kg_nblocks*ngroups)*task_id/ntasks); int ngk1 = (int)(((int64_t)N*Kg_nblocks*ngroups)*(task_id+1)/ntasks); @@ -214,30 +268,40 @@ int runWinograd63(InputArray _input, InputArray _fusedAddMat, OutputArray _outpu size_t inwofs = ((n*ngroups + g)*blocks_per_plane_aligned + block_id0)*Cg*CONV_WINO_AREA; size_t wofs = (g*Kg_nblocks*CONV_WINO_KBLOCK + k0)*Cg*CONV_WINO_AREA; - float* inwptr = wbuf_all + inwofs; - const float* wptr = conv->weightsWinoBufPtr + wofs; + char* inwptr = wbuf_all + inwofs * esz; + char* wptr = wptr0 + wofs * esz; #if CV_TRY_AVX2 if (conv->useAVX2) - opt_AVX2::winofunc_accum_f32(inwptr, wptr, out_wbuf, Cg, block_id1 - block_id0, CONV_WINO_IBLOCK, - CONV_WINO_KBLOCK, CONV_WINO_ATOM_F32, CONV_WINO_NATOMS_F32); + opt_AVX2::winofunc_accum_F32((float *)inwptr, (float *)wptr, (float *)out_wbuf, Cg, block_id1 - block_id0, CONV_WINO_IBLOCK, + CONV_WINO_KBLOCK, CONV_WINO_ATOM, CONV_WINO_NATOMS); else #endif #if CV_TRY_AVX if (conv->useAVX) - opt_AVX::winofunc_accum_f32(inwptr, wptr, out_wbuf, Cg, block_id1 - block_id0, CONV_WINO_IBLOCK, - CONV_WINO_KBLOCK, CONV_WINO_ATOM_F32, CONV_WINO_NATOMS_F32); + opt_AVX::winofunc_accum_F32((float *)inwptr, (float *)wptr, (float *)out_wbuf, Cg, block_id1 - block_id0, CONV_WINO_IBLOCK, + CONV_WINO_KBLOCK, CONV_WINO_ATOM, CONV_WINO_NATOMS); else #endif #if CV_NEON && CV_NEON_AARCH64 if (conv->useNEON) - opt_NEON::winofunc_accum_f32(inwptr, wptr, out_wbuf, Cg, block_id1 - block_id0, CONV_WINO_IBLOCK, - CONV_WINO_KBLOCK, CONV_WINO_ATOM_F32, CONV_WINO_NATOMS_F32); + { +#ifdef CONV_ARM_FP16 + if (useFP16) + { + opt_NEON_FP16::winofunc_accum_F16(inwptr, wptr, out_wbuf, Cg, block_id1 - block_id0, CONV_WINO_IBLOCK, + CONV_WINO_KBLOCK, CONV_WINO_ATOM, CONV_WINO_NATOMS); + } + else +#endif + opt_NEON::winofunc_accum_F32((float *)inwptr, (float *)wptr, (float *)out_wbuf, Cg, block_id1 - block_id0, CONV_WINO_IBLOCK, + CONV_WINO_KBLOCK, CONV_WINO_ATOM, CONV_WINO_NATOMS); + } else #endif + winofunc_accum_F32((float *)inwptr, (float *)wptr, (float *)out_wbuf, Cg, block_id1 - block_id0, CONV_WINO_IBLOCK, + CONV_WINO_KBLOCK, CONV_WINO_ATOM, CONV_WINO_NATOMS); - winofunc_accum_f32(inwptr, wptr, out_wbuf, Cg, block_id1 - block_id0, CONV_WINO_IBLOCK, - CONV_WINO_KBLOCK, CONV_WINO_ATOM_F32, CONV_WINO_NATOMS_F32); for (int k = k0; k < k1; k++) { float biasv = conv->biasBuf[g*Kg + k]; @@ -274,31 +338,42 @@ int runWinograd63(InputArray _input, InputArray _fusedAddMat, OutputArray _outpu } #if CV_TRY_AVX2 if (conv->useAVX2) - opt_AVX::winofunc_AtXA_8x8_f32(out_wbuf + ((k - k0)*CONV_WINO_IBLOCK + (block_id - block_id0))*CONV_WINO_AREA, CONV_WINO_SIZE, + opt_AVX::winofunc_AtXA_8x8_F32((float *)out_wbuf + ((k - k0)*CONV_WINO_IBLOCK + (block_id - block_id0))*CONV_WINO_AREA, CONV_WINO_SIZE, bpptr, outstep, outptr, outstep, biasv, minval, maxval, ifMinMaxAct); else #endif #if CV_TRY_AVX if (conv->useAVX) - opt_AVX::winofunc_AtXA_8x8_f32(out_wbuf + ((k - k0)*CONV_WINO_IBLOCK + (block_id - block_id0))*CONV_WINO_AREA, CONV_WINO_SIZE, + opt_AVX::winofunc_AtXA_8x8_F32((float *)out_wbuf + ((k - k0)*CONV_WINO_IBLOCK + (block_id - block_id0))*CONV_WINO_AREA, CONV_WINO_SIZE, bpptr, outstep, outptr, outstep, biasv, minval, maxval, ifMinMaxAct); else #endif #if CV_NEON && CV_NEON_AARCH64 + // NEON optimization is only for ARMv8 device, and for ARMv7 device, we use the Universal intrinsics. if (conv->useNEON) - // NEON optimization is only for ARMv8 device, and for ARMv7 device, we use the Universal intrinsics. - opt_NEON::winofunc_AtXA_8x8_f32(out_wbuf + ((k - k0)*CONV_WINO_IBLOCK + (block_id - block_id0))*CONV_WINO_AREA, CONV_WINO_SIZE, + { +#ifdef CONV_ARM_FP16 + if (useFP16) + { + opt_NEON_FP16::winofunc_AtXA_8x8_F16(out_wbuf + ((k - k0)*CONV_WINO_IBLOCK + (block_id - block_id0))*CONV_WINO_AREA * esz, CONV_WINO_SIZE, bpptr, outstep, outptr, outstep, biasv, minval, maxval, ifMinMaxAct); + } + else +#endif + opt_NEON::winofunc_AtXA_8x8_F32((float *)out_wbuf + ((k - k0)*CONV_WINO_IBLOCK + (block_id - block_id0))*CONV_WINO_AREA, CONV_WINO_SIZE, + bpptr, outstep, outptr, outstep, biasv, minval, maxval, ifMinMaxAct); + } else #endif - winofunc_AtXA_8x8_f32(out_wbuf + ((k - k0)*CONV_WINO_IBLOCK + (block_id - block_id0))*CONV_WINO_AREA, CONV_WINO_SIZE, + winofunc_AtXA_8x8_F32((float *)out_wbuf + ((k - k0)*CONV_WINO_IBLOCK + (block_id - block_id0))*CONV_WINO_AREA, CONV_WINO_SIZE, bpptr, outstep, outptr, outstep, biasv, minval, maxval, ifMinMaxAct); + if (partial) { if (activ) activ->forwardSlice(outptr, outptr, CONV_WINO_SIZE*CONV_WINO_STEP, 0, g*Kg + k, g*Kg + k + 1); for (int y = 0; y < dy1; y++) - memcpy(outptr0 + y*W0, outptr + y*CONV_WINO_SIZE,dx1*sizeof(outptr0[0])); + memcpy(outptr0 + y*W0, outptr + y*CONV_WINO_SIZE, dx1*sizeof(outptr0[0])); } } } @@ -314,7 +389,7 @@ int runWinograd63(InputArray _input, InputArray _fusedAddMat, OutputArray _outpu #if CV_SIMD128 -void winofunc_accum_f32(const float* inwptr, const float* wptr, float* outbuf, int Cg, int iblock, +void winofunc_accum_F32(const float* inwptr, const float* wptr, float* outbuf, int Cg, int iblock, const int winoIblock, const int winoKblock, const int winoAtomF32, const int winoNatomF32) { #if 1 @@ -411,7 +486,7 @@ void winofunc_accum_f32(const float* inwptr, const float* wptr, float* outbuf, i } /*Input transform*/ -void winofunc_BtXB_8x8_f32(const float* inptr, int inpstep, +void winofunc_BtXB_8x8_F32(const float* inptr, int inpstep, float* outptr, int Cg, const int winoIblock, const int winoAtomF32) { CV_Assert(winoIblock == 3 && winoAtomF32 == 4); @@ -585,7 +660,7 @@ void winofunc_BtXB_8x8_f32(const float* inptr, int inpstep, the Winograd-transformed weights should also be transposed. init_conv() (see OpConv.fx) takes care of that. */ -void winofunc_AtXA_8x8_f32(const float* inptr, int inpstep, +void winofunc_AtXA_8x8_F32(const float* inptr, int inpstep, float* bpptr, int bpstep, float* outptr, int outstep, float bias, float minval, float maxval, bool ifMinMaxAct) { diff --git a/modules/dnn/src/layers/cpu_kernels/conv_winograd_f63.neon.cpp b/modules/dnn/src/layers/cpu_kernels/conv_winograd_f63.neon.cpp new file mode 100644 index 0000000000..70b716f9c7 --- /dev/null +++ b/modules/dnn/src/layers/cpu_kernels/conv_winograd_f63.neon.cpp @@ -0,0 +1,476 @@ +// 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 "../../precomp.hpp" +#include "convolution.hpp" +#include "opencv2/core/hal/intrin.hpp" + +namespace cv { +namespace dnn { + +// NEON code work around. +namespace opt_NEON +{ + +#if CV_NEON && CV_NEON_AARCH64 + +/* Accumulate */ +void winofunc_accum_F32(const float* inwptr, const float* wptr, float* outbuf, int Cg, int iblock, + const int winoIblock, const int winoKblock, const int winoAtomF32, const int winoNatomF32) +{ + CV_Assert(winoIblock == 6 && winoKblock == 4 && winoAtomF32 == 4); + if (iblock > 3) + { + for (int atom_id = 0; atom_id < winoNatomF32; atom_id++, + outbuf += winoAtomF32) + { + float32x4_t s00 = vdupq_n_f32(0.f), s01 = s00, s02 = s00, s03 = s00, s04 = s00, s05 = s00; + float32x4_t s10 = vdupq_n_f32(0.f), s11 = s00, s12 = s00, s13 = s00, s14 = s00, s15 = s00; + float32x4_t s20 = vdupq_n_f32(0.f), s21 = s00, s22 = s00, s23 = s00, s24 = s00, s25 = s00; + float32x4_t s30 = vdupq_n_f32(0.f), s31 = s00, s32 = s00, s33 = s00, s34 = s00, s35 = s00; + for (int c = 0; c < Cg; c++, inwptr += winoIblock*winoAtomF32, + wptr += winoKblock*winoAtomF32) { + float32x4_t w0 = vld1q_f32(wptr), w1 = vld1q_f32(wptr + 4); + float32x4_t w2 = vld1q_f32(wptr + 8), w3 = vld1q_f32(wptr + 12); + float32x4_t x0, x1; + x0 = vld1q_f32(inwptr); + x1 = vld1q_f32(inwptr + 4); + s00 = vfmaq_f32(s00, w0, x0); + s01 = vfmaq_f32(s01, w0, x1); + s10 = vfmaq_f32(s10, w1, x0); + s11 = vfmaq_f32(s11, w1, x1); + s20 = vfmaq_f32(s20, w2, x0); + s21 = vfmaq_f32(s21, w2, x1); + s30 = vfmaq_f32(s30, w3, x0); + s31 = vfmaq_f32(s31, w3, x1); + x0 = vld1q_f32(inwptr + 8); + x1 = vld1q_f32(inwptr + 12); + s02 = vfmaq_f32(s02, w0, x0); + s03 = vfmaq_f32(s03, w0, x1); + s12 = vfmaq_f32(s12, w1, x0); + s13 = vfmaq_f32(s13, w1, x1); + s22 = vfmaq_f32(s22, w2, x0); + s23 = vfmaq_f32(s23, w2, x1); + s32 = vfmaq_f32(s32, w3, x0); + s33 = vfmaq_f32(s33, w3, x1); + x0 = vld1q_f32(inwptr + 16); + x1 = vld1q_f32(inwptr + 20); + s04 = vfmaq_f32(s04, w0, x0); + s05 = vfmaq_f32(s05, w0, x1); + s14 = vfmaq_f32(s14, w1, x0); + s15 = vfmaq_f32(s15, w1, x1); + s24 = vfmaq_f32(s24, w2, x0); + s25 = vfmaq_f32(s25, w2, x1); + s34 = vfmaq_f32(s34, w3, x0); + s35 = vfmaq_f32(s35, w3, x1); + } + + vst1q_f32(outbuf, s00); + vst1q_f32(outbuf + 1*64, s01); + vst1q_f32(outbuf + 2*64, s02); + vst1q_f32(outbuf + 3*64, s03); + vst1q_f32(outbuf + 4*64, s04); + vst1q_f32(outbuf + 5*64, s05); + + vst1q_f32(outbuf + 6*64, s10); + vst1q_f32(outbuf + 7*64, s11); + vst1q_f32(outbuf + 8*64, s12); + vst1q_f32(outbuf + 9*64, s13); + vst1q_f32(outbuf + 10*64, s14); + vst1q_f32(outbuf + 11*64, s15); + + vst1q_f32(outbuf + 12*64, s20); + vst1q_f32(outbuf + 13*64, s21); + vst1q_f32(outbuf + 14*64, s22); + vst1q_f32(outbuf + 15*64, s23); + vst1q_f32(outbuf + 16*64, s24); + vst1q_f32(outbuf + 17*64, s25); + + vst1q_f32(outbuf + 18*64, s30); + vst1q_f32(outbuf + 19*64, s31); + vst1q_f32(outbuf + 20*64, s32); + vst1q_f32(outbuf + 21*64, s33); + vst1q_f32(outbuf + 22*64, s34); + vst1q_f32(outbuf + 23*64, s35); + } + } + else + { + for (int atom_id = 0; atom_id < winoNatomF32; atom_id++, + outbuf += winoAtomF32) + { + float32x4_t s00 = vdupq_n_f32(0.f), s01 = s00, s02 = s00; + float32x4_t s10 = vdupq_n_f32(0.f), s11 = s00, s12 = s00; + float32x4_t s20 = vdupq_n_f32(0.f), s21 = s00, s22 = s00; + float32x4_t s30 = vdupq_n_f32(0.f), s31 = s00, s32 = s00; + for (int c = 0; c < Cg; c++, inwptr += winoIblock*winoAtomF32, + wptr += winoKblock*winoAtomF32) { + float32x4_t w0 = vld1q_f32(wptr), w1 = vld1q_f32(wptr + 4); + float32x4_t w2 = vld1q_f32(wptr + 8), w3 = vld1q_f32(wptr + 12); + float32x4_t x0, x1, x2; + x0 = vld1q_f32(inwptr); + x1 = vld1q_f32(inwptr + 4); + x2 = vld1q_f32(inwptr + 8); + s00 = vfmaq_f32(s00, w0, x0); + s01 = vfmaq_f32(s01, w0, x1); + s02 = vfmaq_f32(s02, w0, x2); + s10 = vfmaq_f32(s10, w1, x0); + s11 = vfmaq_f32(s11, w1, x1); + s12 = vfmaq_f32(s12, w1, x2); + s20 = vfmaq_f32(s20, w2, x0); + s21 = vfmaq_f32(s21, w2, x1); + s22 = vfmaq_f32(s22, w2, x2); + s30 = vfmaq_f32(s30, w3, x0); + s31 = vfmaq_f32(s31, w3, x1); + s32 = vfmaq_f32(s32, w3, x2); + } + + vst1q_f32(outbuf, s00); + vst1q_f32(outbuf + 1*64, s01); + vst1q_f32(outbuf + 2*64, s02); + vst1q_f32(outbuf + 6*64, s10); + vst1q_f32(outbuf + 7*64, s11); + vst1q_f32(outbuf + 8*64, s12); + vst1q_f32(outbuf + 12*64, s20); + vst1q_f32(outbuf + 13*64, s21); + vst1q_f32(outbuf + 14*64, s22); + vst1q_f32(outbuf + 18*64, s30); + vst1q_f32(outbuf + 19*64, s31); + vst1q_f32(outbuf + 20*64, s32); + } + } +} + +#undef T4x4 +#define T4x4(a, b, c, d, tr0, tr1) \ + tr0 = vtrnq_f32(a, b); \ + tr1 = vtrnq_f32(c, d); \ + a = vcombine_f32(vget_low_f32(tr0.val[0]), vget_low_f32(tr1.val[0])); \ + b = vcombine_f32(vget_low_f32(tr0.val[1]), vget_low_f32(tr1.val[1])); \ + c = vcombine_f32(vget_high_f32(tr0.val[0]), vget_high_f32(tr1.val[0])); \ + d = vcombine_f32(vget_high_f32(tr0.val[1]), vget_high_f32(tr1.val[1])) + +/*Input transform*/ +void winofunc_BtXB_8x8_F32(const float* inptr, int inpstep, + float* outptr, int Cg, const int winoIblock, const int winoAtomF32) +{ + float32x4_t x00 = vld1q_f32(inptr), x01 = vld1q_f32(inptr + 4); + float32x4_t x10 = vld1q_f32(inptr + inpstep), x11 = vld1q_f32(inptr + inpstep + 4); + float32x4_t x20 = vld1q_f32(inptr + inpstep*2), x21 = vld1q_f32(inptr + inpstep*2 + 4); + float32x4_t x30 = vld1q_f32(inptr + inpstep*3), x31 = vld1q_f32(inptr + inpstep*3 + 4); + float32x4_t x40 = vld1q_f32(inptr + inpstep*4), x41 = vld1q_f32(inptr + inpstep*4 + 4); + float32x4_t x50 = vld1q_f32(inptr + inpstep*5), x51 = vld1q_f32(inptr + inpstep*5 + 4); + float32x4_t x60 = vld1q_f32(inptr + inpstep*6), x61 = vld1q_f32(inptr + inpstep*6 + 4); + float32x4_t x70 = vld1q_f32(inptr + inpstep*7), x71 = vld1q_f32(inptr + inpstep*7 + 4); + + float32x4_t z00, z01, z10, z11, z20, z21, z30, z31, z40, z41, z50, z51, z60, z61, z70, z71; + + { + /* Y[0] = [1.f, 0.f, -5.25f, 0.f, 5.25f, 0.f, -1.f, 0.f]*X */ + /* Y[7] = [0.f, -1.f, 0.f, 5.25f, 0.f, -5.25f, 0.f, 1.f]*X */ + float32x4_t q5_25 = vdupq_n_f32(5.25f), t00, t01, t10, t11; + t00 = vsubq_f32(x40, x20); + t01 = vsubq_f32(x41, x21); + t10 = vsubq_f32(x30, x50); + t11 = vsubq_f32(x31, x51); + float32x4_t y00 = vfmaq_f32(vsubq_f32(x00, x60), t00, q5_25); + float32x4_t y01 = vfmaq_f32(vsubq_f32(x01, x61), t01, q5_25); + float32x4_t y70 = vfmaq_f32(vsubq_f32(x70, x10), t10, q5_25); + float32x4_t y71 = vfmaq_f32(vsubq_f32(x71, x11), t11, q5_25); + + /* Y[1] = [0.f, 1.f, 1.f, -4.25f, -4.25f, 1.f, 1.f, 0.f]*X */ + /* Y[2] = [0.f, -1.f, 1.f, 4.25f, -4.25f, -1.f, 1.f, 0.f]*X */ + float32x4_t qm4_25 = vdupq_n_f32(-4.25f); + t00 = vfmaq_f32(vaddq_f32(x10, x50), x30, qm4_25); + t01 = vfmaq_f32(vaddq_f32(x11, x51), x31, qm4_25); + t10 = vfmaq_f32(vaddq_f32(x20, x60), x40, qm4_25); + t11 = vfmaq_f32(vaddq_f32(x21, x61), x41, qm4_25); + + float32x4_t y10 = vaddq_f32(t00, t10), y11 = vaddq_f32(t01, t11); + float32x4_t y20 = vsubq_f32(t10, t00), y21 = vsubq_f32(t11, t01); + + /* Y[3] = [0.f, 0.5f, 0.25f, -2.5f, -1.25f, 2.f, 1.f, 0.f]*X */ + /* Y[4] = [0.f, -0.5f, 0.25f, 2.5f, -1.25f, -2.f, 1.f, 0.f]*X */ + float32x4_t q0_5 = vdupq_n_f32(0.5f), q0_25 = vdupq_n_f32(0.25f); + float32x4_t qm2_5 = vdupq_n_f32(-2.5f), qm1_25 = vdupq_n_f32(-1.25f); + t00 = vfmaq_f32(vaddq_f32(x50, x50), x10, q0_5); + t01 = vfmaq_f32(vaddq_f32(x51, x51), x11, q0_5); + t10 = vfmaq_f32(x60, x20, q0_25); + t11 = vfmaq_f32(x61, x21, q0_25); + t00 = vfmaq_f32(t00, x30, qm2_5); + t01 = vfmaq_f32(t01, x31, qm2_5); + t10 = vfmaq_f32(t10, x40, qm1_25); + t11 = vfmaq_f32(t11, x41, qm1_25); + + float32x4_t y30 = vaddq_f32(t00, t10), y31 = vaddq_f32(t01, t11); + float32x4_t y40 = vsubq_f32(t10, t00), y41 = vsubq_f32(t11, t01); + + /* Y[5] = [0.f, 2.f, 4.f, -2.5f, -5.f, 0.5f, 1.f, 0.f]*X */ + /* Y[6] = [0.f, -2.f, 4.f, 2.5f, -5.f, -0.5f, 1.f, 0.f]*X */ + float32x4_t q4 = vdupq_n_f32(4.f), qm5 = vdupq_n_f32(-5.f); + t00 = vfmaq_f32(vaddq_f32(x10, x10), x50, q0_5); + t01 = vfmaq_f32(vaddq_f32(x11, x11), x51, q0_5); + t10 = vfmaq_f32(x60, x20, q4); + t11 = vfmaq_f32(x61, x21, q4); + t00 = vfmaq_f32(t00, x30, qm2_5); + t01 = vfmaq_f32(t01, x31, qm2_5); + t10 = vfmaq_f32(t10, x40, qm5); + t11 = vfmaq_f32(t11, x41, qm5); + + float32x4_t y50 = vaddq_f32(t00, t10), y51 = vaddq_f32(t01, t11); + float32x4_t y60 = vsubq_f32(t10, t00), y61 = vsubq_f32(t11, t01); + + /* transpose 8x8 matrix in-place with some renumeration of the elements: */ + /* Y: */ + /* y00 y01 */ + /* y10 y11 */ + /* ... */ + /* y70 y71 */ + /* Y': */ + /* y00 y40 */ + /* y10 y50 */ + /* y20 y60 */ + /* y30 y70 */ + /* y01 y41 */ + /* y11 y51 */ + /* y21 y61 */ + /* y31 y71 */ + /* in other words, y40 <-> y01, y50 <-> y11, y60 <-> y21, y70 <-> y31 */ + float32x4x2_t tr0, tr1; + + T4x4(y00, y10, y20, y30, tr0, tr1); + T4x4(y01, y11, y21, y31, tr0, tr1); + T4x4(y40, y50, y60, y70, tr0, tr1); + T4x4(y41, y51, y61, y71, tr0, tr1); + + /* Z[0] = [1.f, 0.f, -5.25f, 0.f, 5.25f, 0.f, -1.f, 0.f]*Y */ + /* Z[7] = [0.f, -1.f, 0.f, 5.25f, 0.f, -5.25f, 0.f, 1.f]*Y */ + t00 = vsubq_f32(y01, y20); + t01 = vsubq_f32(y41, y60); + t10 = vsubq_f32(y30, y11); + t11 = vsubq_f32(y70, y51); + z00 = vfmaq_f32(vsubq_f32(y00, y21), t00, q5_25); + z01 = vfmaq_f32(vsubq_f32(y40, y61), t01, q5_25); + z70 = vfmaq_f32(vsubq_f32(y31, y10), t10, q5_25); + z71 = vfmaq_f32(vsubq_f32(y71, y50), t11, q5_25); + + /* Z[1] = [0.f, 1.f, 1.f, -4.25f, -4.25f, 1.f, 1.f, 0.f]*Y */ + /* Z[2] = [0.f, -1.f, 1.f, 4.25f, -4.25f, -1.f, 1.f, 0.f]*Y */ + t00 = vfmaq_f32(vaddq_f32(y10, y11), y30, qm4_25); + t01 = vfmaq_f32(vaddq_f32(y50, y51), y70, qm4_25); + t10 = vfmaq_f32(vaddq_f32(y20, y21), y01, qm4_25); + t11 = vfmaq_f32(vaddq_f32(y60, y61), y41, qm4_25); + + z10 = vaddq_f32(t00, t10); z11 = vaddq_f32(t01, t11); + z20 = vsubq_f32(t10, t00); z21 = vsubq_f32(t11, t01); + + /* Z[3] = [0.f, 0.5f, 0.25f, -2.5f, -1.25f, 2.f, 1.f, 0.f]*Y */ + /* Z[4] = [0.f, -0.5f, 0.25f, 2.5f, -1.25f, -2.f, 1.f, 0.f]*Y */ + t00 = vfmaq_f32(vaddq_f32(y11, y11), y10, q0_5); + t01 = vfmaq_f32(vaddq_f32(y51, y51), y50, q0_5); + t10 = vfmaq_f32(y21, y20, q0_25); + t11 = vfmaq_f32(y61, y60, q0_25); + t00 = vfmaq_f32(t00, y30, qm2_5); + t01 = vfmaq_f32(t01, y70, qm2_5); + t10 = vfmaq_f32(t10, y01, qm1_25); + t11 = vfmaq_f32(t11, y41, qm1_25); + + z30 = vaddq_f32(t00, t10); z31 = vaddq_f32(t01, t11); + z40 = vsubq_f32(t10, t00); z41 = vsubq_f32(t11, t01); + + /* Z[5] = [0.f, 2.f, 4.f, -2.5f, -5.f, 0.5f, 1.f, 0.f]*Y */ + /* Z[6] = [0.f, -2.f, 4.f, 2.5f, -5.f, -0.5f, 1.f, 0.f]*Y */ + t00 = vfmaq_f32(vaddq_f32(y10, y10), y11, q0_5); + t01 = vfmaq_f32(vaddq_f32(y50, y50), y51, q0_5); + t10 = vfmaq_f32(y21, y20, q4); + t11 = vfmaq_f32(y61, y60, q4); + t00 = vfmaq_f32(t00, y30, qm2_5); + t01 = vfmaq_f32(t01, y70, qm2_5); + t10 = vfmaq_f32(t10, y01, qm5); + t11 = vfmaq_f32(t11, y41, qm5); + + z50 = vaddq_f32(t00, t10); z51 = vaddq_f32(t01, t11); + z60 = vsubq_f32(t10, t00); z61 = vsubq_f32(t11, t01); + } + + const int outstep = winoIblock*winoAtomF32*Cg; + + vst1q_f32(outptr, z00); + vst1q_f32(outptr + outstep, z01); + vst1q_f32(outptr + outstep*2, z10); + vst1q_f32(outptr + outstep*3, z11); + vst1q_f32(outptr + outstep*4, z20); + vst1q_f32(outptr + outstep*5, z21); + vst1q_f32(outptr + outstep*6, z30); + vst1q_f32(outptr + outstep*7, z31); + vst1q_f32(outptr + outstep*8, z40); + vst1q_f32(outptr + outstep*9, z41); + vst1q_f32(outptr + outstep*10, z50); + vst1q_f32(outptr + outstep*11, z51); + vst1q_f32(outptr + outstep*12, z60); + vst1q_f32(outptr + outstep*13, z61); + vst1q_f32(outptr + outstep*14, z70); + vst1q_f32(outptr + outstep*15, z71); +} + +/*Output transform*/ +void winofunc_AtXA_8x8_F32(const float* inptr, int inpstep, + float* bpptr, int bpstep, float* outptr, int outstep, + float bias, float minval, float maxval, bool ifMinMaxAct) +{ + float32x4_t x00 = vld1q_f32(inptr), x01 = vld1q_f32(inptr + 4); + float32x4_t x10 = vld1q_f32(inptr + inpstep), x11 = vld1q_f32(inptr + inpstep + 4); + float32x4_t x20 = vld1q_f32(inptr + inpstep*2), x21 = vld1q_f32(inptr + inpstep*2 + 4); + float32x4_t x30 = vld1q_f32(inptr + inpstep*3), x31 = vld1q_f32(inptr + inpstep*3 + 4); + float32x4_t x40 = vld1q_f32(inptr + inpstep*4), x41 = vld1q_f32(inptr + inpstep*4 + 4); + float32x4_t x50 = vld1q_f32(inptr + inpstep*5), x51 = vld1q_f32(inptr + inpstep*5 + 4); + float32x4_t x60 = vld1q_f32(inptr + inpstep*6), x61 = vld1q_f32(inptr + inpstep*6 + 4); + float32x4_t x70 = vld1q_f32(inptr + inpstep*7), x71 = vld1q_f32(inptr + inpstep*7 + 4); + float32x4_t z00, z01, z10, z11, z20, z21, z30, z31, z40, z41, z50, z51; + + { + float32x4_t s12_0, s12_1, s34_0, s34_1, s56_0, s56_1; + s12_0 = vaddq_f32(x10, x20); s12_1 = vaddq_f32(x11, x21); + s34_0 = vaddq_f32(x30, x40); s34_1 = vaddq_f32(x31, x41); + s56_0 = vaddq_f32(x50, x60); s56_1 = vaddq_f32(x51, x61); + + float32x4_t y00 = vaddq_f32(vaddq_f32(vaddq_f32(x00, s12_0), s34_0), s56_0); + float32x4_t y01 = vaddq_f32(vaddq_f32(vaddq_f32(x01, s12_1), s34_1), s56_1); + float32x4_t y20 = vfmaq_n_f32(vfmaq_n_f32(s12_0, s34_0, 4.0f), s56_0, 0.25f); + float32x4_t y21 = vfmaq_n_f32(vfmaq_n_f32(s12_1, s34_1, 4.0f), s56_1, 0.25f); + float32x4_t y40 = vfmaq_n_f32(vfmaq_n_f32(s12_0, s34_0, 16.0f), s56_0, 1.f/16); + float32x4_t y41 = vfmaq_n_f32(vfmaq_n_f32(s12_1, s34_1, 16.0f), s56_1, 1.f/16); + + s12_0 = vsubq_f32(x10, x20); s12_1 = vsubq_f32(x11, x21); + s34_0 = vsubq_f32(x30, x40); s34_1 = vsubq_f32(x31, x41); + s56_0 = vsubq_f32(x50, x60); s56_1 = vsubq_f32(x51, x61); + + float32x4_t y50 = vfmaq_n_f32(vfmaq_n_f32(vaddq_f32(x70, s12_0), + s34_0, 32.f), s56_0, 1.f/32); + float32x4_t y51 = vfmaq_n_f32(vfmaq_n_f32(vaddq_f32(x71, s12_1), + s34_1, 32.f), s56_1, 1.f/32); + float32x4_t y10 = vfmaq_n_f32(vfmaq_n_f32(s12_0, s34_0, 2.0f), s56_0, 0.5f); + float32x4_t y11 = vfmaq_n_f32(vfmaq_n_f32(s12_1, s34_1, 2.0f), s56_1, 0.5f); + float32x4_t y30 = vfmaq_n_f32(vfmaq_n_f32(s12_0, s34_0, 8.0f), s56_0, 0.125f); + float32x4_t y31 = vfmaq_n_f32(vfmaq_n_f32(s12_1, s34_1, 8.0f), s56_1, 0.125f); + float32x4_t y60 = vdupq_n_f32(0.f), y61 = y60, y70 = y60, y71 = y60; + + /* transpose 8x8 matrix in-place with some renumeration of the elements: */ + /* Y: */ + /* y00 y01 */ + /* y10 y11 */ + /* ... */ + /* y50 y51 */ + /* 0 0 */ + /* 0 0 */ + /* Y': */ + /* y00 y40 */ + /* y10 y50 */ + /* y20 y60 */ + /* y30 y70 */ + /* y01 y41 */ + /* y11 y51 */ + /* y21 y61 */ + /* y31 y71 */ + /* in other words, y40 <-> y01, y50 <-> y11, y60 <-> y21, y70 <-> y31 */ + float32x4x2_t tr0, tr1; + + T4x4(y00, y10, y20, y30, tr0, tr1); + T4x4(y01, y11, y21, y31, tr0, tr1); + T4x4(y40, y50, y60, y70, tr0, tr1); + T4x4(y41, y51, y61, y71, tr0, tr1); + + s12_0 = vaddq_f32(y10, y20); s12_1 = vaddq_f32(y50, y60); + s34_0 = vaddq_f32(y30, y01); s34_1 = vaddq_f32(y70, y41); + s56_0 = vaddq_f32(y11, y21); s56_1 = vaddq_f32(y51, y61); + + z00 = vaddq_f32(vaddq_f32(vaddq_f32(y00, s12_0), s34_0), s56_0); + z01 = vaddq_f32(vaddq_f32(vaddq_f32(y40, s12_1), s34_1), s56_1); + z20 = vfmaq_n_f32(vfmaq_n_f32(s12_0, s34_0, 4.0f), s56_0, 0.25f); + z21 = vfmaq_n_f32(vfmaq_n_f32(s12_1, s34_1, 4.0f), s56_1, 0.25f); + z40 = vfmaq_n_f32(vfmaq_n_f32(s12_0, s34_0, 16.0f), s56_0, 1.f/16); + z41 = vfmaq_n_f32(vfmaq_n_f32(s12_1, s34_1, 16.0f), s56_1, 1.f/16); + + s12_0 = vsubq_f32(y10, y20); s12_1 = vsubq_f32(y50, y60); + s34_0 = vsubq_f32(y30, y01); s34_1 = vsubq_f32(y70, y41); + s56_0 = vsubq_f32(y11, y21); s56_1 = vsubq_f32(y51, y61); + + z50 = vfmaq_n_f32(vfmaq_n_f32(vaddq_f32(y31, s12_0), + s34_0, 32.f), s56_0, 1.f/32); + z51 = vfmaq_n_f32(vfmaq_n_f32(vaddq_f32(y71, s12_1), + s34_1, 32.f), s56_1, 1.f/32); + z10 = vfmaq_n_f32(vfmaq_n_f32(s12_0, s34_0, 2.0f), s56_0, 0.5f); + z11 = vfmaq_n_f32(vfmaq_n_f32(s12_1, s34_1, 2.0f), s56_1, 0.5f); + z30 = vfmaq_n_f32(vfmaq_n_f32(s12_0, s34_0, 8.0f), s56_0, 0.125f); + z31 = vfmaq_n_f32(vfmaq_n_f32(s12_1, s34_1, 8.0f), s56_1, 0.125f); + float32x4_t vbias = vdupq_n_f32(bias); + + z00 = vaddq_f32(z00, vbias); + z01 = vaddq_f32(z01, vbias); + z10 = vaddq_f32(z10, vbias); + z11 = vaddq_f32(z11, vbias); + z20 = vaddq_f32(z20, vbias); + z21 = vaddq_f32(z21, vbias); + z30 = vaddq_f32(z30, vbias); + z31 = vaddq_f32(z31, vbias); + z40 = vaddq_f32(z40, vbias); + z41 = vaddq_f32(z41, vbias); + z50 = vaddq_f32(z50, vbias); + z51 = vaddq_f32(z51, vbias); + } + + if (bpptr) + { + float32x2_t zhalf = vdup_n_f32(0.f); + z00 = vaddq_f32(z00, vld1q_f32(bpptr)); + z01 = vaddq_f32(z01, vcombine_f32(vld1_f32(bpptr + 4), zhalf)); + z10 = vaddq_f32(z10, vld1q_f32(bpptr + bpstep)); + z11 = vaddq_f32(z11, vcombine_f32(vld1_f32(bpptr + bpstep + 4), zhalf)); + z20 = vaddq_f32(z20, vld1q_f32(bpptr + bpstep*2)); + z21 = vaddq_f32(z21, vcombine_f32(vld1_f32(bpptr + bpstep*2 + 4), zhalf)); + z30 = vaddq_f32(z30, vld1q_f32(bpptr + bpstep*3)); + z31 = vaddq_f32(z31, vcombine_f32(vld1_f32(bpptr + bpstep*3 + 4), zhalf)); + z40 = vaddq_f32(z40, vld1q_f32(bpptr + bpstep*4)); + z41 = vaddq_f32(z41, vcombine_f32(vld1_f32(bpptr + bpstep*4 + 4), zhalf)); + z50 = vaddq_f32(z50, vld1q_f32(bpptr + bpstep*5)); + z51 = vaddq_f32(z51, vcombine_f32(vld1_f32(bpptr + bpstep*5 + 4), zhalf)); + } + + if (ifMinMaxAct) + { + float32x4_t vmax = vdupq_n_f32(maxval); + float32x4_t vmin = vdupq_n_f32(minval); + + z00 = vminq_f32(vmaxq_f32(z00, vmin), vmax); + z01 = vminq_f32(vmaxq_f32(z01, vmin), vmax); + z10 = vminq_f32(vmaxq_f32(z10, vmin), vmax); + z11 = vminq_f32(vmaxq_f32(z11, vmin), vmax); + z20 = vminq_f32(vmaxq_f32(z20, vmin), vmax); + z21 = vminq_f32(vmaxq_f32(z21, vmin), vmax); + z30 = vminq_f32(vmaxq_f32(z30, vmin), vmax); + z31 = vminq_f32(vmaxq_f32(z31, vmin), vmax); + z40 = vminq_f32(vmaxq_f32(z40, vmin), vmax); + z41 = vminq_f32(vmaxq_f32(z41, vmin), vmax); + z50 = vminq_f32(vmaxq_f32(z50, vmin), vmax); + z51 = vminq_f32(vmaxq_f32(z51, vmin), vmax); + } + + vst1q_f32(outptr, z00); + vst1_f32(outptr + 4, vget_low_f32(z01)); + vst1q_f32(outptr + outstep, z10); + vst1_f32(outptr + outstep + 4, vget_low_f32(z11)); + vst1q_f32(outptr + outstep*2, z20); + vst1_f32(outptr + outstep*2 + 4, vget_low_f32(z21)); + vst1q_f32(outptr + outstep*3, z30); + vst1_f32(outptr + outstep*3 + 4, vget_low_f32(z31)); + vst1q_f32(outptr + outstep*4, z40); + vst1_f32(outptr + outstep*4 + 4, vget_low_f32(z41)); + vst1q_f32(outptr + outstep*5, z50); + vst1_f32(outptr + outstep*5 + 4, vget_low_f32(z51)); +} + +#endif +} + +}} // namespace diff --git a/modules/dnn/src/layers/cpu_kernels/conv_winograd_f63.simd.hpp b/modules/dnn/src/layers/cpu_kernels/conv_winograd_f63.simd.hpp index 2688c75785..d1f1610280 100644 --- a/modules/dnn/src/layers/cpu_kernels/conv_winograd_f63.simd.hpp +++ b/modules/dnn/src/layers/cpu_kernels/conv_winograd_f63.simd.hpp @@ -9,26 +9,37 @@ namespace dnn { CV_CPU_OPTIMIZATION_NAMESPACE_BEGIN /* Accumulate */ -void winofunc_accum_f32(const float* inwptr, const float* wptr, float* outbuf, int Cg, int iblock, +void winofunc_accum_F32(const float* inwptr, const float* wptr, float* outbuf, int Cg, int iblock, const int winoIblock, const int winoKblock, const int winoAtomF32, const int winoNatomF32); /*Input transform*/ -void winofunc_BtXB_8x8_f32(const float* inptr, int inpstep, +void winofunc_BtXB_8x8_F32(const float* inptr, int inpstep, float* outptr, int Cg, const int winoIblock, const int winoAtomF32); /*Output transform*/ -void winofunc_AtXA_8x8_f32(const float* inptr, int inpstep, +void winofunc_AtXA_8x8_F32(const float* inptr, int inpstep, float* bpptr, int bpstep, float* outptr, int outstep, float bias, float minval, float maxval, bool ifMinMaxAct); -#if !defined(CV_CPU_OPTIMIZATION_DECLARATIONS_ONLY) && CV_AVX +// FP 16 branch, only ARMv8 supports. +void winofunc_accum_F16(const char* _inwptr, const char* _wptr, char* _outbuf, int Cg, int iblock, + const int winoIblock, const int winoKblock, const int winoAtomF16, const int winoNatomF16); +void winofunc_BtXB_8x8_F16(const float * inptr, int inpstep, + char * _outptr, int Cg, const int winoIblock, const int winoAtomF16); +void winofunc_AtXA_8x8_F16(const char* inptr, int inpstep, + float * bpptr, int bpstep, float* outptr, int outstep, + float bias, float minval, float maxval, bool ifMinMaxAct); + +#if !defined(CV_CPU_OPTIMIZATION_DECLARATIONS_ONLY) + +#if CV_AVX #if !CV_FMA3 // AVX workaround #undef _mm256_fmadd_ps #define _mm256_fmadd_ps(a, b, c) _mm256_add_ps(c, _mm256_mul_ps(a, b)) #endif -void winofunc_accum_f32(const float* inwptr, const float* wptr, float* outbuf, int Cg, int iblock, +void winofunc_accum_F32(const float* inwptr, const float* wptr, float* outbuf, int Cg, int iblock, const int winoIblock, const int winoKblock, const int winoAtomF32, const int winoNatomF32) { CV_Assert(winoIblock == 6 && winoKblock == 4 && winoAtomF32 == 8); @@ -187,7 +198,7 @@ void transpose8_ps(__m256 &row0, __m256 &row1, __m256 &row2, __m256 &row3, __m25 } /*Input transform*/ -void winofunc_BtXB_8x8_f32(const float* inptr, int inpstep, +void winofunc_BtXB_8x8_F32(const float* inptr, int inpstep, float* outptr, int Cg, const int winoIblock, const int winoAtomF32) { __m256 x00 = _mm256_loadu_ps(inptr); @@ -311,7 +322,7 @@ void winofunc_BtXB_8x8_f32(const float* inptr, int inpstep, 0.f, 1.f, 1.f, 16.f, 16.f, 1.f/16, 1.f/16, 0.f, 0.f, 1.f, -1.f, 32.f, -32.f, 1.f/32, -1.f/32, 1.f] */ -void winofunc_AtXA_8x8_f32(const float* inptr, int inpstep, +void winofunc_AtXA_8x8_F32(const float* inptr, int inpstep, float* bpptr, int bpstep, float* outptr, int outstep, float bias, float minval, float maxval, bool ifMinMaxAct) { @@ -405,154 +416,13 @@ void winofunc_AtXA_8x8_f32(const float* inptr, int inpstep, STORE6_ELE_FROM_16(outptr + outstep * 5, z50, lowM, highM); _mm256_zeroupper(); } -#endif // CV_CPU_OPTIMIZATION_DECLARATIONS_ONLY -CV_CPU_OPTIMIZATION_NAMESPACE_END +#endif // CV_AVX -// NEON code work around. -namespace opt_NEON -{ - -#if !defined(CV_CPU_OPTIMIZATION_DECLARATIONS_ONLY) && CV_NEON && CV_NEON_AARCH64 -/* Accumulate */ -void winofunc_accum_f32(const float* inwptr, const float* wptr, float* outbuf, int Cg, int iblock, - const int winoIblock, const int winoKblock, const int winoAtomF32, const int winoNatomF32); - -/*Input transform*/ -void winofunc_BtXB_8x8_f32(const float* inptr, int inpstep, - float* outptr, int Cg, const int winoIblock, const int winoAtomF32); - -/*Output transform*/ -void winofunc_AtXA_8x8_f32(const float* inptr, int inpstep, - float* bpptr, int bpstep, float* outptr, int outstep, - float bias, float minval, float maxval, bool ifMinMaxAct); - -void winofunc_accum_f32(const float* inwptr, const float* wptr, float* outbuf, int Cg, int iblock, - const int winoIblock, const int winoKblock, const int winoAtomF32, const int winoNatomF32) -{ - CV_Assert(winoIblock == 6 && winoKblock == 4 && winoAtomF32 == 4); - if (iblock > 3) - { - for (int atom_id = 0; atom_id < winoNatomF32; atom_id++, - outbuf += winoAtomF32) - { - float32x4_t s00 = vdupq_n_f32(0.f), s01 = s00, s02 = s00, s03 = s00, s04 = s00, s05 = s00; - float32x4_t s10 = vdupq_n_f32(0.f), s11 = s00, s12 = s00, s13 = s00, s14 = s00, s15 = s00; - float32x4_t s20 = vdupq_n_f32(0.f), s21 = s00, s22 = s00, s23 = s00, s24 = s00, s25 = s00; - float32x4_t s30 = vdupq_n_f32(0.f), s31 = s00, s32 = s00, s33 = s00, s34 = s00, s35 = s00; - for (int c = 0; c < Cg; c++, inwptr += winoIblock*winoAtomF32, - wptr += winoKblock*winoAtomF32) { - float32x4_t w0 = vld1q_f32(wptr), w1 = vld1q_f32(wptr + 4); - float32x4_t w2 = vld1q_f32(wptr + 8), w3 = vld1q_f32(wptr + 12); - float32x4_t x0, x1; - x0 = vld1q_f32(inwptr); - x1 = vld1q_f32(inwptr + 4); - s00 = vfmaq_f32(s00, w0, x0); - s01 = vfmaq_f32(s01, w0, x1); - s10 = vfmaq_f32(s10, w1, x0); - s11 = vfmaq_f32(s11, w1, x1); - s20 = vfmaq_f32(s20, w2, x0); - s21 = vfmaq_f32(s21, w2, x1); - s30 = vfmaq_f32(s30, w3, x0); - s31 = vfmaq_f32(s31, w3, x1); - x0 = vld1q_f32(inwptr + 8); - x1 = vld1q_f32(inwptr + 12); - s02 = vfmaq_f32(s02, w0, x0); - s03 = vfmaq_f32(s03, w0, x1); - s12 = vfmaq_f32(s12, w1, x0); - s13 = vfmaq_f32(s13, w1, x1); - s22 = vfmaq_f32(s22, w2, x0); - s23 = vfmaq_f32(s23, w2, x1); - s32 = vfmaq_f32(s32, w3, x0); - s33 = vfmaq_f32(s33, w3, x1); - x0 = vld1q_f32(inwptr + 16); - x1 = vld1q_f32(inwptr + 20); - s04 = vfmaq_f32(s04, w0, x0); - s05 = vfmaq_f32(s05, w0, x1); - s14 = vfmaq_f32(s14, w1, x0); - s15 = vfmaq_f32(s15, w1, x1); - s24 = vfmaq_f32(s24, w2, x0); - s25 = vfmaq_f32(s25, w2, x1); - s34 = vfmaq_f32(s34, w3, x0); - s35 = vfmaq_f32(s35, w3, x1); - } - - vst1q_f32(outbuf, s00); - vst1q_f32(outbuf + 1*64, s01); - vst1q_f32(outbuf + 2*64, s02); - vst1q_f32(outbuf + 3*64, s03); - vst1q_f32(outbuf + 4*64, s04); - vst1q_f32(outbuf + 5*64, s05); - - vst1q_f32(outbuf + 6*64, s10); - vst1q_f32(outbuf + 7*64, s11); - vst1q_f32(outbuf + 8*64, s12); - vst1q_f32(outbuf + 9*64, s13); - vst1q_f32(outbuf + 10*64, s14); - vst1q_f32(outbuf + 11*64, s15); - - vst1q_f32(outbuf + 12*64, s20); - vst1q_f32(outbuf + 13*64, s21); - vst1q_f32(outbuf + 14*64, s22); - vst1q_f32(outbuf + 15*64, s23); - vst1q_f32(outbuf + 16*64, s24); - vst1q_f32(outbuf + 17*64, s25); - - vst1q_f32(outbuf + 18*64, s30); - vst1q_f32(outbuf + 19*64, s31); - vst1q_f32(outbuf + 20*64, s32); - vst1q_f32(outbuf + 21*64, s33); - vst1q_f32(outbuf + 22*64, s34); - vst1q_f32(outbuf + 23*64, s35); - } - } - else - { - for (int atom_id = 0; atom_id < winoNatomF32; atom_id++, - outbuf += winoAtomF32) - { - float32x4_t s00 = vdupq_n_f32(0.f), s01 = s00, s02 = s00; - float32x4_t s10 = vdupq_n_f32(0.f), s11 = s00, s12 = s00; - float32x4_t s20 = vdupq_n_f32(0.f), s21 = s00, s22 = s00; - float32x4_t s30 = vdupq_n_f32(0.f), s31 = s00, s32 = s00; - for (int c = 0; c < Cg; c++, inwptr += winoIblock*winoAtomF32, - wptr += winoKblock*winoAtomF32) { - float32x4_t w0 = vld1q_f32(wptr), w1 = vld1q_f32(wptr + 4); - float32x4_t w2 = vld1q_f32(wptr + 8), w3 = vld1q_f32(wptr + 12); - float32x4_t x0, x1, x2; - x0 = vld1q_f32(inwptr); - x1 = vld1q_f32(inwptr + 4); - x2 = vld1q_f32(inwptr + 8); - s00 = vfmaq_f32(s00, w0, x0); - s01 = vfmaq_f32(s01, w0, x1); - s02 = vfmaq_f32(s02, w0, x2); - s10 = vfmaq_f32(s10, w1, x0); - s11 = vfmaq_f32(s11, w1, x1); - s12 = vfmaq_f32(s12, w1, x2); - s20 = vfmaq_f32(s20, w2, x0); - s21 = vfmaq_f32(s21, w2, x1); - s22 = vfmaq_f32(s22, w2, x2); - s30 = vfmaq_f32(s30, w3, x0); - s31 = vfmaq_f32(s31, w3, x1); - s32 = vfmaq_f32(s32, w3, x2); - } - - vst1q_f32(outbuf, s00); - vst1q_f32(outbuf + 1*64, s01); - vst1q_f32(outbuf + 2*64, s02); - vst1q_f32(outbuf + 6*64, s10); - vst1q_f32(outbuf + 7*64, s11); - vst1q_f32(outbuf + 8*64, s12); - vst1q_f32(outbuf + 12*64, s20); - vst1q_f32(outbuf + 13*64, s21); - vst1q_f32(outbuf + 14*64, s22); - vst1q_f32(outbuf + 18*64, s30); - vst1q_f32(outbuf + 19*64, s31); - vst1q_f32(outbuf + 20*64, s32); - } - } -} +// FP16, currently, only ARMv8 may support it +#if defined(CV_NEON_AARCH64) && CV_NEON_AARCH64 && defined(__ARM_FEATURE_FP16_VECTOR_ARITHMETIC) +#undef T4x4 #define T4x4(a, b, c, d, tr0, tr1) \ tr0 = vtrnq_f32(a, b); \ tr1 = vtrnq_f32(c, d); \ @@ -561,10 +431,168 @@ void winofunc_accum_f32(const float* inwptr, const float* wptr, float* outbuf, i c = vcombine_f32(vget_high_f32(tr0.val[0]), vget_high_f32(tr1.val[0])); \ d = vcombine_f32(vget_high_f32(tr0.val[1]), vget_high_f32(tr1.val[1])) -/*Input transform*/ -void winofunc_BtXB_8x8_f32(const float* inptr, int inpstep, - float* outptr, int Cg, const int winoIblock, const int winoAtomF32) +/* Accumulate */ +void winofunc_accum_F16(const char* _inwptr, const char* _wptr, char* _outbuf, int Cg, int iblock, + const int winoIblock, const int winoKblock, const int winoAtomF16, const int winoNatomF16) { + typedef __fp16 float16_t; + const float16_t* inwptr = (const float16_t*)_inwptr; + const float16_t* wptr = (const float16_t*)_wptr; + float16_t* outbuf = (float16_t*)_outbuf; + + CV_Assert(winoIblock == 6 && winoKblock == 4 && winoAtomF16 == 8); + + if (iblock > 3) + { + for (int atom_id = 0; atom_id < winoNatomF16; atom_id++, outbuf += winoAtomF16) + { + float16x8_t s00 = vdupq_n_f16(0.f), s01 = s00, s02 = s00, s03 = s00, s04 = s00, s05 = s00; + float16x8_t s10 = vdupq_n_f16(0.f), s11 = s00, s12 = s00, s13 = s00, s14 = s00, s15 = s00; + float16x8_t s20 = vdupq_n_f16(0.f), s21 = s00, s22 = s00, s23 = s00, s24 = s00, s25 = s00; + float16x8_t s30 = vdupq_n_f16(0.f), s31 = s00, s32 = s00, s33 = s00, s34 = s00, s35 = s00; + + for (int c = 0; c < Cg; c++, inwptr += winoIblock*winoAtomF16, + wptr += winoKblock*winoAtomF16) + { + float16x8_t w0 = vld1q_f16(wptr), w1 = vld1q_f16(wptr + 8); + float16x8_t w2 = vld1q_f16(wptr + 16), w3 = vld1q_f16(wptr + 24); + + float16x8_t x0, x1, x2; + x0 = vld1q_f16(inwptr); + x1 = vld1q_f16(inwptr + 8); + x2 = vld1q_f16(inwptr + 16); + + s00 = vfmaq_f16(s00, w0, x0); + s01 = vfmaq_f16(s01, w0, x1); + s02 = vfmaq_f16(s02, w0, x2); + + s10 = vfmaq_f16(s10, w1, x0); + s11 = vfmaq_f16(s11, w1, x1); + s12 = vfmaq_f16(s12, w1, x2); + + s20 = vfmaq_f16(s20, w2, x0); + s21 = vfmaq_f16(s21, w2, x1); + s22 = vfmaq_f16(s22, w2, x2); + + s30 = vfmaq_f16(s30, w3, x0); + s31 = vfmaq_f16(s31, w3, x1); + s32 = vfmaq_f16(s32, w3, x2); + + x0 = vld1q_f16(inwptr + 24); + x1 = vld1q_f16(inwptr + 32); + x2 = vld1q_f16(inwptr + 40); + + s03 = vfmaq_f16(s03, w0, x0); + s04 = vfmaq_f16(s04, w0, x1); + s05 = vfmaq_f16(s05, w0, x2); + + s13 = vfmaq_f16(s13, w1, x0); + s14 = vfmaq_f16(s14, w1, x1); + s15 = vfmaq_f16(s15, w1, x2); + + s23 = vfmaq_f16(s23, w2, x0); + s24 = vfmaq_f16(s24, w2, x1); + s25 = vfmaq_f16(s25, w2, x2); + + s33 = vfmaq_f16(s33, w3, x0); + s34 = vfmaq_f16(s34, w3, x1); + s35 = vfmaq_f16(s35, w3, x2); + } + + vst1q_f16(outbuf, s00); + vst1q_f16(outbuf + 1*64, s01); + vst1q_f16(outbuf + 2*64, s02); + vst1q_f16(outbuf + 3*64, s03); + vst1q_f16(outbuf + 4*64, s04); + vst1q_f16(outbuf + 5*64, s05); + + vst1q_f16(outbuf + 6*64, s10); + vst1q_f16(outbuf + 7*64, s11); + vst1q_f16(outbuf + 8*64, s12); + vst1q_f16(outbuf + 9*64, s13); + vst1q_f16(outbuf + 10*64, s14); + vst1q_f16(outbuf + 11*64, s15); + + vst1q_f16(outbuf + 12*64, s20); + vst1q_f16(outbuf + 13*64, s21); + vst1q_f16(outbuf + 14*64, s22); + vst1q_f16(outbuf + 15*64, s23); + vst1q_f16(outbuf + 16*64, s24); + vst1q_f16(outbuf + 17*64, s25); + + vst1q_f16(outbuf + 18*64, s30); + vst1q_f16(outbuf + 19*64, s31); + vst1q_f16(outbuf + 20*64, s32); + vst1q_f16(outbuf + 21*64, s33); + vst1q_f16(outbuf + 22*64, s34); + vst1q_f16(outbuf + 23*64, s35); + } + } + else + { + for (int atom_id = 0; atom_id < winoNatomF16; atom_id++, + outbuf += winoAtomF16) + { + float16x8_t s00 = vdupq_n_f16(0.f), s01 = s00, s02 = s00; + float16x8_t s10 = vdupq_n_f16(0.f), s11 = s00, s12 = s00; + float16x8_t s20 = vdupq_n_f16(0.f), s21 = s00, s22 = s00; + float16x8_t s30 = vdupq_n_f16(0.f), s31 = s00, s32 = s00; + + for (int c = 0; c < Cg; c++, inwptr += winoIblock*winoAtomF16, + wptr += winoKblock*winoAtomF16) + { + float16x8_t w0 = vld1q_f16(wptr), w1 = vld1q_f16(wptr + 8); + float16x8_t w2 = vld1q_f16(wptr + 16), w3 = vld1q_f16(wptr + 24); + float16x8_t x0, x1, x2; + + x0 = vld1q_f16(inwptr); + x1 = vld1q_f16(inwptr + 8); + x2 = vld1q_f16(inwptr + 16); + + s00 = vfmaq_f16(s00, w0, x0); + s01 = vfmaq_f16(s01, w0, x1); + s02 = vfmaq_f16(s02, w0, x2); + + s10 = vfmaq_f16(s10, w1, x0); + s11 = vfmaq_f16(s11, w1, x1); + s12 = vfmaq_f16(s12, w1, x2); + + s20 = vfmaq_f16(s20, w2, x0); + s21 = vfmaq_f16(s21, w2, x1); + s22 = vfmaq_f16(s22, w2, x2); + + s30 = vfmaq_f16(s30, w3, x0); + s31 = vfmaq_f16(s31, w3, x1); + s32 = vfmaq_f16(s32, w3, x2); + } + + vst1q_f16(outbuf, s00); + vst1q_f16(outbuf + 1*64, s01); + vst1q_f16(outbuf + 2*64, s02); + + vst1q_f16(outbuf + 6*64, s10); + vst1q_f16(outbuf + 7*64, s11); + vst1q_f16(outbuf + 8*64, s12); + + vst1q_f16(outbuf + 12*64, s20); + vst1q_f16(outbuf + 13*64, s21); + vst1q_f16(outbuf + 14*64, s22); + + vst1q_f16(outbuf + 18*64, s30); + vst1q_f16(outbuf + 19*64, s31); + vst1q_f16(outbuf + 20*64, s32); + } + } +} + +/*Input transform*/ +//NOTE: Since we don't have the fully fp16 support. Current work around is that we need packing the data and +// convert it to FP16 in input transform stage. And at output transform stage we will convert it back to FP32. +void winofunc_BtXB_8x8_F16(const float * inptr, int inpstep, + char * _outptr, int Cg, const int winoIblock, const int winoAtomF16) +{ + typedef __fp16 float16_t; + float16_t* outptr = (float16_t*)_outptr; float32x4_t x00 = vld1q_f32(inptr), x01 = vld1q_f32(inptr + 4); float32x4_t x10 = vld1q_f32(inptr + inpstep), x11 = vld1q_f32(inptr + inpstep + 4); float32x4_t x20 = vld1q_f32(inptr + inpstep*2), x21 = vld1q_f32(inptr + inpstep*2 + 4); @@ -577,8 +605,8 @@ void winofunc_BtXB_8x8_f32(const float* inptr, int inpstep, float32x4_t z00, z01, z10, z11, z20, z21, z30, z31, z40, z41, z50, z51, z60, z61, z70, z71; { - /* Y[0] = [1.f, 0.f, -5.25f, 0.f, 5.25f, 0.f, -1.f, 0.f]*X */ - /* Y[7] = [0.f, -1.f, 0.f, 5.25f, 0.f, -5.25f, 0.f, 1.f]*X */ + // Y[0] = [1.f, 0.f, -5.25f, 0.f, 5.25f, 0.f, -1.f, 0.f]*X + // Y[7] = [0.f, -1.f, 0.f, 5.25f, 0.f, -5.25f, 0.f, 1.f]*X float32x4_t q5_25 = vdupq_n_f32(5.25f), t00, t01, t10, t11; t00 = vsubq_f32(x40, x20); t01 = vsubq_f32(x41, x21); @@ -589,8 +617,8 @@ void winofunc_BtXB_8x8_f32(const float* inptr, int inpstep, float32x4_t y70 = vfmaq_f32(vsubq_f32(x70, x10), t10, q5_25); float32x4_t y71 = vfmaq_f32(vsubq_f32(x71, x11), t11, q5_25); - /* Y[1] = [0.f, 1.f, 1.f, -4.25f, -4.25f, 1.f, 1.f, 0.f]*X */ - /* Y[2] = [0.f, -1.f, 1.f, 4.25f, -4.25f, -1.f, 1.f, 0.f]*X */ + // Y[1] = [0.f, 1.f, 1.f, -4.25f, -4.25f, 1.f, 1.f, 0.f]*X + // Y[2] = [0.f, -1.f, 1.f, 4.25f, -4.25f, -1.f, 1.f, 0.f]*X float32x4_t qm4_25 = vdupq_n_f32(-4.25f); t00 = vfmaq_f32(vaddq_f32(x10, x50), x30, qm4_25); t01 = vfmaq_f32(vaddq_f32(x11, x51), x31, qm4_25); @@ -600,8 +628,8 @@ void winofunc_BtXB_8x8_f32(const float* inptr, int inpstep, float32x4_t y10 = vaddq_f32(t00, t10), y11 = vaddq_f32(t01, t11); float32x4_t y20 = vsubq_f32(t10, t00), y21 = vsubq_f32(t11, t01); - /* Y[3] = [0.f, 0.5f, 0.25f, -2.5f, -1.25f, 2.f, 1.f, 0.f]*X */ - /* Y[4] = [0.f, -0.5f, 0.25f, 2.5f, -1.25f, -2.f, 1.f, 0.f]*X */ + // Y[3] = [0.f, 0.5f, 0.25f, -2.5f, -1.25f, 2.f, 1.f, 0.f]*X + // Y[4] = [0.f, -0.5f, 0.25f, 2.5f, -1.25f, -2.f, 1.f, 0.f]*X float32x4_t q0_5 = vdupq_n_f32(0.5f), q0_25 = vdupq_n_f32(0.25f); float32x4_t qm2_5 = vdupq_n_f32(-2.5f), qm1_25 = vdupq_n_f32(-1.25f); t00 = vfmaq_f32(vaddq_f32(x50, x50), x10, q0_5); @@ -616,8 +644,8 @@ void winofunc_BtXB_8x8_f32(const float* inptr, int inpstep, float32x4_t y30 = vaddq_f32(t00, t10), y31 = vaddq_f32(t01, t11); float32x4_t y40 = vsubq_f32(t10, t00), y41 = vsubq_f32(t11, t01); - /* Y[5] = [0.f, 2.f, 4.f, -2.5f, -5.f, 0.5f, 1.f, 0.f]*X */ - /* Y[6] = [0.f, -2.f, 4.f, 2.5f, -5.f, -0.5f, 1.f, 0.f]*X */ + // Y[5] = [0.f, 2.f, 4.f, -2.5f, -5.f, 0.5f, 1.f, 0.f]*X + // Y[6] = [0.f, -2.f, 4.f, 2.5f, -5.f, -0.5f, 1.f, 0.f]*X float32x4_t q4 = vdupq_n_f32(4.f), qm5 = vdupq_n_f32(-5.f); t00 = vfmaq_f32(vaddq_f32(x10, x10), x50, q0_5); t01 = vfmaq_f32(vaddq_f32(x11, x11), x51, q0_5); @@ -631,22 +659,22 @@ void winofunc_BtXB_8x8_f32(const float* inptr, int inpstep, float32x4_t y50 = vaddq_f32(t00, t10), y51 = vaddq_f32(t01, t11); float32x4_t y60 = vsubq_f32(t10, t00), y61 = vsubq_f32(t11, t01); - /* transpose 8x8 matrix in-place with some renumeration of the elements: */ - /* Y: */ - /* y00 y01 */ - /* y10 y11 */ - /* ... */ - /* y70 y71 */ - /* Y': */ - /* y00 y40 */ - /* y10 y50 */ - /* y20 y60 */ - /* y30 y70 */ - /* y01 y41 */ - /* y11 y51 */ - /* y21 y61 */ - /* y31 y71 */ - /* in other words, y40 <-> y01, y50 <-> y11, y60 <-> y21, y70 <-> y31 */ + // transpose 8x8 matrix in-place with some renumeration of the elements: + // Y: + // y00 y01 + // y10 y11 + // ... + // y70 y71 + // Y': + // y00 y40 + // y10 y50 + // y20 y60 + // y30 y70 + // y01 y41 + // y11 y51 + // y21 y61 + // y31 y71 + // in other words, y40 <-> y01, y50 <-> y11, y60 <-> y21, y70 <-> y31 float32x4x2_t tr0, tr1; T4x4(y00, y10, y20, y30, tr0, tr1); @@ -654,8 +682,8 @@ void winofunc_BtXB_8x8_f32(const float* inptr, int inpstep, T4x4(y40, y50, y60, y70, tr0, tr1); T4x4(y41, y51, y61, y71, tr0, tr1); - /* Z[0] = [1.f, 0.f, -5.25f, 0.f, 5.25f, 0.f, -1.f, 0.f]*Y */ - /* Z[7] = [0.f, -1.f, 0.f, 5.25f, 0.f, -5.25f, 0.f, 1.f]*Y */ + // Z[0] = [1.f, 0.f, -5.25f, 0.f, 5.25f, 0.f, -1.f, 0.f]*Y + // Z[7] = [0.f, -1.f, 0.f, 5.25f, 0.f, -5.25f, 0.f, 1.f]*Y t00 = vsubq_f32(y01, y20); t01 = vsubq_f32(y41, y60); t10 = vsubq_f32(y30, y11); @@ -665,8 +693,8 @@ void winofunc_BtXB_8x8_f32(const float* inptr, int inpstep, z70 = vfmaq_f32(vsubq_f32(y31, y10), t10, q5_25); z71 = vfmaq_f32(vsubq_f32(y71, y50), t11, q5_25); - /* Z[1] = [0.f, 1.f, 1.f, -4.25f, -4.25f, 1.f, 1.f, 0.f]*Y */ - /* Z[2] = [0.f, -1.f, 1.f, 4.25f, -4.25f, -1.f, 1.f, 0.f]*Y */ + // Z[1] = [0.f, 1.f, 1.f, -4.25f, -4.25f, 1.f, 1.f, 0.f]*Y + // Z[2] = [0.f, -1.f, 1.f, 4.25f, -4.25f, -1.f, 1.f, 0.f]*Y t00 = vfmaq_f32(vaddq_f32(y10, y11), y30, qm4_25); t01 = vfmaq_f32(vaddq_f32(y50, y51), y70, qm4_25); t10 = vfmaq_f32(vaddq_f32(y20, y21), y01, qm4_25); @@ -675,8 +703,8 @@ void winofunc_BtXB_8x8_f32(const float* inptr, int inpstep, z10 = vaddq_f32(t00, t10); z11 = vaddq_f32(t01, t11); z20 = vsubq_f32(t10, t00); z21 = vsubq_f32(t11, t01); - /* Z[3] = [0.f, 0.5f, 0.25f, -2.5f, -1.25f, 2.f, 1.f, 0.f]*Y */ - /* Z[4] = [0.f, -0.5f, 0.25f, 2.5f, -1.25f, -2.f, 1.f, 0.f]*Y */ + // Z[3] = [0.f, 0.5f, 0.25f, -2.5f, -1.25f, 2.f, 1.f, 0.f]*Y + // Z[4] = [0.f, -0.5f, 0.25f, 2.5f, -1.25f, -2.f, 1.f, 0.f]*Y t00 = vfmaq_f32(vaddq_f32(y11, y11), y10, q0_5); t01 = vfmaq_f32(vaddq_f32(y51, y51), y50, q0_5); t10 = vfmaq_f32(y21, y20, q0_25); @@ -689,8 +717,8 @@ void winofunc_BtXB_8x8_f32(const float* inptr, int inpstep, z30 = vaddq_f32(t00, t10); z31 = vaddq_f32(t01, t11); z40 = vsubq_f32(t10, t00); z41 = vsubq_f32(t11, t01); - /* Z[5] = [0.f, 2.f, 4.f, -2.5f, -5.f, 0.5f, 1.f, 0.f]*Y */ - /* Z[6] = [0.f, -2.f, 4.f, 2.5f, -5.f, -0.5f, 1.f, 0.f]*Y */ + // Z[5] = [0.f, 2.f, 4.f, -2.5f, -5.f, 0.5f, 1.f, 0.f]*Y + // Z[6] = [0.f, -2.f, 4.f, 2.5f, -5.f, -0.5f, 1.f, 0.f]*Y t00 = vfmaq_f32(vaddq_f32(y10, y10), y11, q0_5); t01 = vfmaq_f32(vaddq_f32(y50, y50), y51, q0_5); t10 = vfmaq_f32(y21, y20, q4); @@ -704,39 +732,42 @@ void winofunc_BtXB_8x8_f32(const float* inptr, int inpstep, z60 = vsubq_f32(t10, t00); z61 = vsubq_f32(t11, t01); } - const int outstep = winoIblock*winoAtomF32*Cg; + const int outstep = winoIblock*winoAtomF16*Cg; - vst1q_f32(outptr, z00); - vst1q_f32(outptr + outstep, z01); - vst1q_f32(outptr + outstep*2, z10); - vst1q_f32(outptr + outstep*3, z11); - vst1q_f32(outptr + outstep*4, z20); - vst1q_f32(outptr + outstep*5, z21); - vst1q_f32(outptr + outstep*6, z30); - vst1q_f32(outptr + outstep*7, z31); - vst1q_f32(outptr + outstep*8, z40); - vst1q_f32(outptr + outstep*9, z41); - vst1q_f32(outptr + outstep*10, z50); - vst1q_f32(outptr + outstep*11, z51); - vst1q_f32(outptr + outstep*12, z60); - vst1q_f32(outptr + outstep*13, z61); - vst1q_f32(outptr + outstep*14, z70); - vst1q_f32(outptr + outstep*15, z71); + vst1_f16(outptr, vcvt_f16_f32(z00)); + vst1_f16(outptr + 4, vcvt_f16_f32(z01)); + vst1_f16(outptr + outstep, vcvt_f16_f32(z10)); + vst1_f16(outptr + outstep + 4, vcvt_f16_f32(z11)); + vst1_f16(outptr + outstep*2, vcvt_f16_f32(z20)); + vst1_f16(outptr + outstep*2 + 4, vcvt_f16_f32(z21)); + vst1_f16(outptr + outstep*3, vcvt_f16_f32(z30)); + vst1_f16(outptr + outstep*3 + 4, vcvt_f16_f32(z31)); + vst1_f16(outptr + outstep*4, vcvt_f16_f32(z40)); + vst1_f16(outptr + outstep*4 + 4, vcvt_f16_f32(z41)); + vst1_f16(outptr + outstep*5, vcvt_f16_f32(z50)); + vst1_f16(outptr + outstep*5 + 4, vcvt_f16_f32(z51)); + vst1_f16(outptr + outstep*6, vcvt_f16_f32(z60)); + vst1_f16(outptr + outstep*6 + 4, vcvt_f16_f32(z61)); + vst1_f16(outptr + outstep*7, vcvt_f16_f32(z70)); + vst1_f16(outptr + outstep*7 + 4, vcvt_f16_f32(z71)); } -/*Output transform*/ -void winofunc_AtXA_8x8_f32(const float* inptr, int inpstep, - float* bpptr, int bpstep, float* outptr, int outstep, - float bias, float minval, float maxval, bool ifMinMaxAct) +// Output transform +void winofunc_AtXA_8x8_F16(const char* _inptr, int inpstep, + float * bpptr, int bpstep, float* outptr, int outstep, + float bias, float minval, float maxval, bool ifMinMaxAct) { - float32x4_t x00 = vld1q_f32(inptr), x01 = vld1q_f32(inptr + 4); - float32x4_t x10 = vld1q_f32(inptr + inpstep), x11 = vld1q_f32(inptr + inpstep + 4); - float32x4_t x20 = vld1q_f32(inptr + inpstep*2), x21 = vld1q_f32(inptr + inpstep*2 + 4); - float32x4_t x30 = vld1q_f32(inptr + inpstep*3), x31 = vld1q_f32(inptr + inpstep*3 + 4); - float32x4_t x40 = vld1q_f32(inptr + inpstep*4), x41 = vld1q_f32(inptr + inpstep*4 + 4); - float32x4_t x50 = vld1q_f32(inptr + inpstep*5), x51 = vld1q_f32(inptr + inpstep*5 + 4); - float32x4_t x60 = vld1q_f32(inptr + inpstep*6), x61 = vld1q_f32(inptr + inpstep*6 + 4); - float32x4_t x70 = vld1q_f32(inptr + inpstep*7), x71 = vld1q_f32(inptr + inpstep*7 + 4); + typedef __fp16 float16_t; + const float16_t* inptr = (const float16_t*)_inptr; + + float32x4_t x00 = vcvt_f32_f16(vld1_f16(inptr)), x01 = vcvt_f32_f16(vld1_f16(inptr + 4)); + float32x4_t x10 = vcvt_f32_f16(vld1_f16(inptr + inpstep)), x11 = vcvt_f32_f16(vld1_f16(inptr + inpstep + 4)); + float32x4_t x20 = vcvt_f32_f16(vld1_f16(inptr + inpstep*2)), x21 = vcvt_f32_f16(vld1_f16(inptr + inpstep*2 + 4)); + float32x4_t x30 = vcvt_f32_f16(vld1_f16(inptr + inpstep*3)), x31 = vcvt_f32_f16(vld1_f16(inptr + inpstep*3 + 4)); + float32x4_t x40 = vcvt_f32_f16(vld1_f16(inptr + inpstep*4)), x41 = vcvt_f32_f16(vld1_f16(inptr + inpstep*4 + 4)); + float32x4_t x50 = vcvt_f32_f16(vld1_f16(inptr + inpstep*5)), x51 = vcvt_f32_f16(vld1_f16(inptr + inpstep*5 + 4)); + float32x4_t x60 = vcvt_f32_f16(vld1_f16(inptr + inpstep*6)), x61 = vcvt_f32_f16(vld1_f16(inptr + inpstep*6 + 4)); + float32x4_t x70 = vcvt_f32_f16(vld1_f16(inptr + inpstep*7)), x71 = vcvt_f32_f16(vld1_f16(inptr + inpstep*7 + 4)); float32x4_t z00, z01, z10, z11, z20, z21, z30, z31, z40, z41, z50, z51; { @@ -757,33 +788,33 @@ void winofunc_AtXA_8x8_f32(const float* inptr, int inpstep, s56_0 = vsubq_f32(x50, x60); s56_1 = vsubq_f32(x51, x61); float32x4_t y50 = vfmaq_n_f32(vfmaq_n_f32(vaddq_f32(x70, s12_0), - s34_0, 32.f), s56_0, 1.f/32); + s34_0, 32.f), s56_0, 1.f/32); float32x4_t y51 = vfmaq_n_f32(vfmaq_n_f32(vaddq_f32(x71, s12_1), - s34_1, 32.f), s56_1, 1.f/32); + s34_1, 32.f), s56_1, 1.f/32); float32x4_t y10 = vfmaq_n_f32(vfmaq_n_f32(s12_0, s34_0, 2.0f), s56_0, 0.5f); float32x4_t y11 = vfmaq_n_f32(vfmaq_n_f32(s12_1, s34_1, 2.0f), s56_1, 0.5f); float32x4_t y30 = vfmaq_n_f32(vfmaq_n_f32(s12_0, s34_0, 8.0f), s56_0, 0.125f); float32x4_t y31 = vfmaq_n_f32(vfmaq_n_f32(s12_1, s34_1, 8.0f), s56_1, 0.125f); float32x4_t y60 = vdupq_n_f32(0.f), y61 = y60, y70 = y60, y71 = y60; - /* transpose 8x8 matrix in-place with some renumeration of the elements: */ - /* Y: */ - /* y00 y01 */ - /* y10 y11 */ - /* ... */ - /* y50 y51 */ - /* 0 0 */ - /* 0 0 */ - /* Y': */ - /* y00 y40 */ - /* y10 y50 */ - /* y20 y60 */ - /* y30 y70 */ - /* y01 y41 */ - /* y11 y51 */ - /* y21 y61 */ - /* y31 y71 */ - /* in other words, y40 <-> y01, y50 <-> y11, y60 <-> y21, y70 <-> y31 */ + // transpose 8x8 matrix in-place with some renumeration of the elements: + // Y: + // y00 y01 + // y10 y11 + // ... + // y50 y51 + // 0 0 + // 0 0 + // Y': + // y00 y40 + // y10 y50 + // y20 y60 + // y30 y70 + // y01 y41 + // y11 y51 + // y21 y61 + // y31 y71 + // in other words, y40 <-> y01, y50 <-> y11, y60 <-> y21, y70 <-> y31 float32x4x2_t tr0, tr1; T4x4(y00, y10, y20, y30, tr0, tr1); @@ -807,9 +838,9 @@ void winofunc_AtXA_8x8_f32(const float* inptr, int inpstep, s56_0 = vsubq_f32(y11, y21); s56_1 = vsubq_f32(y51, y61); z50 = vfmaq_n_f32(vfmaq_n_f32(vaddq_f32(y31, s12_0), - s34_0, 32.f), s56_0, 1.f/32); + s34_0, 32.f), s56_0, 1.f/32); z51 = vfmaq_n_f32(vfmaq_n_f32(vaddq_f32(y71, s12_1), - s34_1, 32.f), s56_1, 1.f/32); + s34_1, 32.f), s56_1, 1.f/32); z10 = vfmaq_n_f32(vfmaq_n_f32(s12_0, s34_0, 2.0f), s56_0, 0.5f); z11 = vfmaq_n_f32(vfmaq_n_f32(s12_1, s34_1, 2.0f), s56_1, 0.5f); z30 = vfmaq_n_f32(vfmaq_n_f32(s12_0, s34_0, 8.0f), s56_0, 0.125f); @@ -879,8 +910,8 @@ void winofunc_AtXA_8x8_f32(const float* inptr, int inpstep, vst1q_f32(outptr + outstep*5, z50); vst1_f32(outptr + outstep*5 + 4, vget_low_f32(z51)); } - #endif -} +#endif +CV_CPU_OPTIMIZATION_NAMESPACE_END }} // namespace diff --git a/modules/dnn/src/layers/cpu_kernels/convolution.cpp b/modules/dnn/src/layers/cpu_kernels/convolution.cpp index 68777ff65e..c5258ccd91 100644 --- a/modules/dnn/src/layers/cpu_kernels/convolution.cpp +++ b/modules/dnn/src/layers/cpu_kernels/convolution.cpp @@ -14,15 +14,76 @@ #include "conv_block.simd.hpp" #include "layers/cpu_kernels/conv_block.simd_declarations.hpp" // defines CV_CPU_DISPATCH_MODES_ALL=AVX2,...,BASELINE based on CMakeLists.txt content +#include namespace cv { namespace dnn { -enum { VEC_ALIGN = 32, DFT_TYPE = CV_32F }; // Memory alignment. +enum { VEC_ALIGN = 32}; // Memory alignment. -void convBlock(int np, const float* a, const float* b, float* c, int ldc, bool init_c, const int outLen, +void convBlock_F32(int np, const float* a, const float* b, float* c, int ldc, bool init_c, const int outLen, const int convMR, const int convNR); -void convBlockMR1(int np, const float* a, const float* b, float *c, const float bias, bool init_c, +void convBlockMR1_F32(int np, const float* a, const float* b, float *c, const float bias, bool init_c, const float minval, const float maxval, bool ifMinMaxAct, const int outLen, const int convNR); +#ifdef CONV_ARM_FP16 +// Fast convert float 32 to float16 +static inline void _cvt32f16f(const float* src, float16_t* dst, int len) +{ + int j = 0; + const int VECSZ = 4; + __fp16* dst_FP16 = (__fp16 *)dst; + if (len > VECSZ * 4) + { + const int VECSZ4 = 4 * VECSZ; + for( ; j + VECSZ4 < len; j += VECSZ4) + { + + float32x4_t v0 = vld1q_f32(src + j); + float32x4_t v1 = vld1q_f32(src + j + 4); + float32x4_t v2 = vld1q_f32(src + j + 8); + float32x4_t v3 = vld1q_f32(src + j + 12); + + vst1q_f16(dst_FP16 + j, vcombine_f16(vcvt_f16_f32(v0), vcvt_f16_f32(v1))); + vst1q_f16(dst_FP16 + j + 8, vcombine_f16(vcvt_f16_f32(v2), vcvt_f16_f32(v3))); + } + } + + for( ; j < len; j += VECSZ ) + { + if( j > len - VECSZ ) + { + if( j == 0 ) + break; + j = len - VECSZ; + } + + float16x4_t hv = vcvt_f16_f32(vld1q_f32(src + j)); + vst1_f16(dst_FP16 + j, hv); + } + for( ; j < len; j++ ) + dst[j] = float16_t(src[j]); +} +#endif + +float* FastConv::getWeights() +{ + return alignPtr(weightsBuf.data(), VEC_ALIGN); +} + +float* FastConv::getWeightsWino() +{ + return alignPtr(weightsWinoBuf.data(), VEC_ALIGN); +} + +float16_t* FastConv::getWeightsFP16() +{ + return alignPtr(weightsBuf_FP16.data(), VEC_ALIGN); +} + +float16_t* FastConv::getWeightsWinoFP16() +{ + return alignPtr(weightsWinoBuf_FP16.data(), VEC_ALIGN); +} + Ptr initFastConv( InputArray _weightsMat, float* srcBias, @@ -119,9 +180,16 @@ Ptr initFastConv( conv->useFP16 = false; #ifdef CONV_ARM_FP16 - // TODO: add FP16 support for Winograd. - if (_useFP16 && (conv->conv_type == CONV_TYPE_GENERIC || conv->conv_type == CONV_TYPE_DEPTHWISE_REMAIN)) + if (_useFP16 && (conv->conv_type == CONV_TYPE_GENERIC || conv->conv_type == CONV_TYPE_DEPTHWISE_REMAIN + || conv->conv_type == CONV_TYPE_WINOGRAD3X3)) conv->useFP16 = true; + + // Runtime FP16 check. + if (conv->useFP16 && !checkHardwareSupport(CPU_NEON_FP16)) + { + conv->useFP16 = false; + CV_LOG_ONCE_WARNING(NULL, "DNN: the CPU does not support the instruction set required by FP16, fallback to FP32."); + } #endif float *srcWeights = (float *)weightsMat.data; @@ -141,31 +209,25 @@ Ptr initFastConv( if (conv->useFP16) { conv->weightsBuf_FP16.resize(nweights + VEC_ALIGN); - conv->weightsBufPtr_FP16 = alignPtr(conv->weightsBuf_FP16.data(), VEC_ALIGN * sizeof(float16_t )); - memset(conv->weightsBufPtr_FP16, 0, nweights * sizeof(float16_t )); - auto weightsBufPtr_FP16 = conv->weightsBufPtr_FP16; + auto weightsPtr_FP16 = conv->getWeightsFP16(); + memset(reinterpret_cast(weightsPtr_FP16), 0, nweights * sizeof(weightsPtr_FP16[0])); parallel_for_(Range(0, C), [&](const Range& r0){ - for(int c = r0.start; c < r0.end; c++) - { - for (int k = 0; k < ksize; k++) - weightsBufPtr_FP16[c*padded_ksize + k] = (float16_t)srcWeights[c*wstep + k]; - }}); + for(int c = r0.start; c < r0.end; c++) + _cvt32f16f(srcWeights + c*wstep, weightsPtr_FP16 + c*padded_ksize, ksize); + }); } else #endif { conv->weightsBuf.resize(nweights + VEC_ALIGN); - conv->weightsBufPtr = alignPtr(conv->weightsBuf.data(), VEC_ALIGN * sizeof(float )); - memset(conv->weightsBufPtr, 0, nweights*sizeof(float )); - auto weightsBufPtr = conv->weightsBufPtr; + auto weightsPtr = conv->getWeights(); + memset(weightsPtr, 0, nweights*sizeof(weightsPtr[0])); - parallel_for_(Range(0, C), [&](const Range& r0){ - for(int c = r0.start; c < r0.end; c++) - { - for (int k = 0; k < ksize; k++) - weightsBufPtr[c*padded_ksize + k] = srcWeights[c*wstep + k]; - }}); + parallel_for_(Range(0, C), [&](const Range& r0) { + for(int c = r0.start; c < r0.end; c++) + memcpy(weightsPtr + c*padded_ksize, srcWeights + c*wstep, ksize*sizeof(weightsPtr[0])); + }); } } else if(conv->conv_type == CONV_TYPE_WINOGRAD3X3) // winograd @@ -213,16 +275,14 @@ Ptr initFastConv( if (conv->useFP16) { conv->weightsWinoBuf_FP16.resize(nweights + VEC_ALIGN); - conv->weightsWinoBufPtr_FP16 = alignPtr(conv->weightsWinoBuf_FP16.data(), VEC_ALIGN); - wptrWino_FP16 = conv->weightsWinoBufPtr_FP16; - memset(wptrWino_FP16, 0, nweights * sizeof(wptrWino_FP16[0])); + wptrWino_FP16 = conv->getWeightsWinoFP16(); + memset(reinterpret_cast(wptrWino_FP16), 0, nweights * sizeof(wptrWino_FP16[0])); } else #endif { conv->weightsWinoBuf.resize(nweights + VEC_ALIGN); - conv->weightsWinoBufPtr = alignPtr(conv->weightsWinoBuf.data(), VEC_ALIGN); - wptrWino = conv->weightsWinoBufPtr; + wptrWino = conv->getWeightsWino(); memset(wptrWino, 0, nweights * sizeof(wptrWino[0])); } @@ -272,7 +332,7 @@ Ptr initFastConv( for (int i = 0; i < CONV_WINO_NATOMS_F16; i++, wptr += Cg * CONV_WINO_KBLOCK * CONV_WINO_ATOM_F16) { - CV_Assert(conv->weightsWinoBufPtr_FP16 <= wptr && wptr + CONV_WINO_ATOM_F16 <= conv->weightsWinoBufPtr_FP16 + nweights); + CV_Assert(wptrWino_FP16 <= wptr && wptr + CONV_WINO_ATOM_F16 <= wptrWino_FP16 + nweights); for (int j = 0; j < CONV_WINO_ATOM_F16; j++) { wptr[j] = (float16_t)kernelTm[i * CONV_WINO_ATOM_F16 + j]; @@ -287,7 +347,7 @@ Ptr initFastConv( for (int i = 0; i < CONV_WINO_NATOMS_F32; i++, wptr += Cg * CONV_WINO_KBLOCK * CONV_WINO_ATOM_F32) { - CV_Assert(conv->weightsWinoBufPtr <= wptr && wptr + CONV_WINO_ATOM_F32 <= conv->weightsWinoBufPtr + nweights); + CV_Assert(wptrWino <= wptr && wptr + CONV_WINO_ATOM_F32 <= wptrWino + nweights); memcpy(wptr, kernelTm + i * CONV_WINO_ATOM_F32, CONV_WINO_ATOM_F32*sizeof (wptr[0])); } } @@ -305,29 +365,26 @@ Ptr initFastConv( int numStripsMR = (Kg + CONV_MR_FP32 - 1) / CONV_MR_FP32; int Kg_aligned = numStripsMR * CONV_MR_FP32; size_t nweights = ngroups*Kg_aligned*DkHkWkCg; - - float* weightsBufPtr = nullptr; + float* weightsPtr = nullptr; #ifdef CONV_ARM_FP16 int numStripsMR_FP16 = (Kg + CONV_MR_FP16 - 1) / CONV_MR_FP16; int Kg_aligned_FP16 = numStripsMR_FP16 * CONV_MR_FP16; size_t nweights_FP16 = ngroups * Kg_aligned_FP16 * DkHkWkCg; + float16_t* weightsPtr_FP16 = nullptr; - float16_t* weightsBufPtr_FP16 = nullptr; if (conv->useFP16) { conv->weightsBuf_FP16.resize(nweights_FP16 + VEC_ALIGN); - conv->weightsBufPtr_FP16 = alignPtr(conv->weightsBuf_FP16.data(), VEC_ALIGN); - weightsBufPtr_FP16 = conv->weightsBufPtr_FP16; - memset(weightsBufPtr_FP16, 0, nweights_FP16*sizeof(weightsBufPtr_FP16[0])); + weightsPtr_FP16 = conv->getWeightsFP16(); + memset(reinterpret_cast(weightsPtr_FP16), 0, nweights_FP16*sizeof(weightsPtr_FP16[0])); } else #endif { conv->weightsBuf.resize(nweights + VEC_ALIGN); - conv->weightsBufPtr = alignPtr(conv->weightsBuf.data(), VEC_ALIGN); - weightsBufPtr = conv->weightsBufPtr; - memset(weightsBufPtr, 0, nweights*sizeof(weightsBufPtr[0])); + weightsPtr = conv->getWeights(); + memset(weightsPtr, 0, nweights*sizeof(weightsPtr[0])); } // Pack the weight. @@ -343,7 +400,7 @@ Ptr initFastConv( int startK = si * CONV_MR_FP16; CV_Assert(startK < Kg_aligned_FP16); - float16_t* packed_wptr = weightsBufPtr_FP16 + DkHkWkCg * (startK + g * Kg_aligned_FP16); + float16_t* packed_wptr = weightsPtr_FP16 + DkHkWkCg * (startK + g * Kg_aligned_FP16); int dk = Kg - startK < CONV_MR_FP16 ? Kg - startK : CONV_MR_FP16; // check if we need zero padding. int k_idx = g*Kg + startK; @@ -373,7 +430,7 @@ Ptr initFastConv( int startK = si * CONV_MR_FP32; CV_Assert(startK < Kg_aligned); - float* packed_wptr = weightsBufPtr + DkHkWkCg * (startK + g * Kg_aligned); + float* packed_wptr = weightsPtr + DkHkWkCg * (startK + g * Kg_aligned); int dk = Kg - startK < CONV_MR_FP32 ? Kg - startK : CONV_MR_FP32; // check if we need zero padding. int k_idx = g*Kg + startK; @@ -410,7 +467,7 @@ Ptr initFastConv( } static inline void packData8(char*& inpbuf, float*& inptrIn, int& in_w, int& x0, int& s0, const int* ofstab, - const int stride_w, const int ksize, const int esz) + const int stride_w, const int ksize, const int esz) { char * inpbufC = inpbuf + s0 * esz; float* inptrInC = (float* )inptrIn; @@ -435,16 +492,8 @@ static inline void packData8(char*& inpbuf, float*& inptrIn, int& in_w, int& x0, for (int k = 0; k < ksize; k++) { int k1 = ofstab[k]; - float32x4_t v0, v1; - - v0[0] = inptrInC[k1]; - v0[1] = inptrInC[k1 + stride_w]; - v0[2] = inptrInC[k1 + 2*stride_w]; - v0[3] = inptrInC[k1 + 3*stride_w]; - v1[0] = inptrInC[k1 + 4*stride_w]; - v1[1] = inptrInC[k1 + 5*stride_w]; - v1[2] = inptrInC[k1 + 6*stride_w]; - v1[3] = inptrInC[k1 + 7*stride_w]; + float32x4_t v0 = {inptrInC[k1], inptrInC[k1 + stride_w], inptrInC[k1 + 2*stride_w], inptrInC[k1 + 3*stride_w]}; + float32x4_t v1 = {inptrInC[k1 + 4*stride_w], inptrInC[k1 + 5*stride_w], inptrInC[k1 + 6*stride_w], inptrInC[k1 + 7*stride_w]}; vst1q_f16((__fp16*)inpbufC_FP16 + k * CONV_NR_FP16, vcombine_f16(vcvt_f16_f32(v0), vcvt_f16_f32(v1))); } @@ -516,7 +565,7 @@ static inline void packData8(char*& inpbuf, float*& inptrIn, int& in_w, int& x0, } static inline void packData2(char *& inpbuf, float*& inptrIn, int& in_w, int& x0, int& s0, const int* ofstab, - const int stride_w, const int ksize, const int esz) + const int stride_w, const int ksize, const int esz) { char* inpbufC = inpbuf + s0 * esz; float* inptrInC = inptrIn; @@ -553,46 +602,6 @@ static inline void packData2(char *& inpbuf, float*& inptrIn, int& in_w, int& x0 in_w += stride_w; } -#ifdef CONV_ARM_FP16 -// Fast convert float 32 to float16 -static inline void _cvt32f16f( const float* src, float16_t* dst, int len) -{ - int j = 0; - const int VECSZ = 4; - __fp16* dst_FP16 = (__fp16 *)dst; - if (len > VECSZ * 4) - { - const int VECSZ4 = 4 * VECSZ; - for( ; j + VECSZ4 < len; j += VECSZ4) - { - - float32x4_t v0 = vld1q_f32(src + j); - float32x4_t v1 = vld1q_f32(src + j + 4); - float32x4_t v2 = vld1q_f32(src + j + 8); - float32x4_t v3 = vld1q_f32(src + j + 12); - - vst1q_f16(dst_FP16 + j, vcombine_f16(vcvt_f16_f32(v0), vcvt_f16_f32(v1))); - vst1q_f16(dst_FP16 + j + 8, vcombine_f16(vcvt_f16_f32(v2), vcvt_f16_f32(v3))); - } - } - - for( ; j < len; j += VECSZ ) - { - if( j > len - VECSZ ) - { - if( j == 0 ) - break; - j = len - VECSZ; - } - - float16x4_t hv = vcvt_f16_f32(vld1q_f32(src + j)); - vst1_f16(dst_FP16 + j, hv); - } - for( ; j < len; j++ ) - dst[j] = float16_t(src[j]); -} -#endif - static inline void packInputData(char* inpbuf_task, float* inp, const int* ofstab, const int* dhwTab, int zyx0, int zyx_limit, int ksize, int stride_d, int stride_h, int stride_w, int pad_front, int pad_top, int pad_left, int Dk, int Hk, int Wk, int dilation_d, int dilation_h, int dilation_w, int Di, int Hi, int Wi, @@ -939,11 +948,8 @@ static inline void packInputData(char* inpbuf_task, float* inp, const int* ofsta { for (int c = 0; c < Cg; c++, inpbuf_ki_FP16 += CONV_NR, inptr_ki += inp_planesize) { - float32x4_t v0, v1; - v0[0] = inptr_ki[0], v0[1] = inptr_ki[2]; - v0[2] = inptr_ki[4], v0[3] = inptr_ki[6]; - v1[0] = inptr_ki[8], v1[1] = inptr_ki[10]; - v1[2] = inptr_ki[12], v1[3] = inptr_ki[14]; + float32x4_t v0 = {inptr_ki[0], inptr_ki[2], inptr_ki[4], inptr_ki[6]}; + float32x4_t v1 = {inptr_ki[8], inptr_ki[10], inptr_ki[12], inptr_ki[14]}; vst1q_f16((__fp16* )inpbuf_ki_FP16, vcombine_f16(vcvt_f16_f32(v0), vcvt_f16_f32(v1))); } } @@ -972,12 +978,8 @@ static inline void packInputData(char* inpbuf_task, float* inp, const int* ofsta { for (int c = 0; c < Cg; c++, inpbuf_ki_FP16 += CONV_NR, inptr_ki += inp_planesize) { - float32x4_t v0, v1; - - v0[0] = inptr_ki[0], v0[1] = inptr_ki[stride_w]; - v0[2] = inptr_ki[stride_w * 2], v0[3] = inptr_ki[stride_w * 3]; - v1[0] = inptr_ki[stride_w * 4], v1[1] = inptr_ki[stride_w * 5]; - v1[2] = inptr_ki[stride_w * 6], v1[3] = inptr_ki[stride_w * 7]; + float32x4_t v0 = {inptr_ki[0], inptr_ki[stride_w], inptr_ki[stride_w * 2], inptr_ki[stride_w * 3]}; + float32x4_t v1 = {inptr_ki[stride_w * 4], inptr_ki[stride_w * 5], inptr_ki[stride_w * 6], inptr_ki[stride_w * 7]}; vst1q_f16((__fp16* )inpbuf_ki_FP16, vcombine_f16(vcvt_f16_f32(v0), vcvt_f16_f32(v1))); } } @@ -1034,9 +1036,7 @@ static inline void packInputData(char* inpbuf_task, float* inp, const int* ofsta { for (int c = 0; c < Cg; c++, inpbuf_ki_FP16 += CONV_NR, inptr_ki += inp_planesize) { - float32x4_t v0; - v0[0] = inptr_ki[0], v0[1] = inptr_ki[stride_w]; - v0[2] = inptr_ki[stride_w * 2], v0[3] = inptr_ki[stride_w * 3]; + float32x4_t v0 = {inptr_ki[0], inptr_ki[stride_w], inptr_ki[stride_w * 2], inptr_ki[stride_w * 3]}; vst1_f16((__fp16* )inpbuf_ki_FP16, vcvt_f16_f32(v0)); } } @@ -1174,10 +1174,9 @@ void runFastConv(InputArray _input, OutputArray _output, const Ptr& co else activ = nullptr; - // TODO: support FP16 for winograd. if (conv->conv_type == CONV_TYPE_WINOGRAD3X3) // winograd { - CV_Assert(conv->weightsWinoBufPtr && input.dims == 4 && conv_dim == CONV_2D && !useFP16); + CV_Assert((!conv->weightsWinoBuf.empty() || !conv->weightsWinoBuf_FP16.empty()) && input.dims == 4 && conv_dim == CONV_2D); if (runWinograd63(input, fusedAddMat, output, conv, ntasks, minval, maxval, activ, ifMinMaxAct)) return; } @@ -1437,13 +1436,13 @@ void runFastConv(InputArray _input, OutputArray _output, const Ptr& co if (useFP16) { CV_Assert(!conv->weightsBuf_FP16.empty()); - weights = (char *)conv->weightsBufPtr_FP16; + weights = (char *)conv->getWeightsFP16(); } else #endif { CV_Assert(!conv->weightsBuf.empty()); - weights = (char *)conv->weightsBufPtr; + weights = (char *)conv->getWeights(); } // optional branch, only for depth-wise convolution which was implemented by generic convolution. // In this case, CONV_MR is 1, and CONV_NR remains the same. @@ -1477,7 +1476,7 @@ void runFastConv(InputArray _input, OutputArray _output, const Ptr& co #ifdef CONV_ARM_FP16 if (useFP16) { - opt_NEON::convBlockMR1_FP16(DkHkWkCg, weights, inptr, cptr, biasVal, fusedAdd, minval, maxval, ifMinMaxAct, outLen, CONV_NR); + opt_NEON_FP16::convBlockMR1_F16(DkHkWkCg, weights, inptr, cptr, biasVal, fusedAdd, minval, maxval, ifMinMaxAct, outLen, CONV_NR); } else #endif @@ -1485,7 +1484,7 @@ void runFastConv(InputArray _input, OutputArray _output, const Ptr& co } else #endif - convBlockMR1(DkHkWkCg, (const float *)weights, (const float *)inptr, cptr, biasVal, fusedAdd, minval, maxval, ifMinMaxAct, outLen, CONV_NR); + convBlockMR1_F32(DkHkWkCg, (const float *)weights, (const float *)inptr, cptr, biasVal, fusedAdd, minval, maxval, ifMinMaxAct, outLen, CONV_NR); if (ifBuffer) { @@ -1526,12 +1525,12 @@ void runFastConv(InputArray _input, OutputArray _output, const Ptr& co { #if CV_TRY_AVX2 if (conv->useAVX2) - opt_AVX2::convBlock(c1 - c0, (const float *)wptr, (const float *)inptr, cptr, ldc, c0 == 0, outLen, CONV_MR, CONV_NR); + opt_AVX2::convBlock_F32(c1 - c0, (const float *)wptr, (const float *)inptr, cptr, ldc, c0 == 0, outLen, CONV_MR, CONV_NR); else #endif #if CV_TRY_AVX if (conv->useAVX) - opt_AVX::convBlock(c1 - c0, (const float *)wptr, (const float *)inptr, cptr, ldc, c0 == 0, outLen, CONV_MR, CONV_NR); + opt_AVX::convBlock_F32(c1 - c0, (const float *)wptr, (const float *)inptr, cptr, ldc, c0 == 0, outLen, CONV_MR, CONV_NR); else #endif #if CV_NEON @@ -1540,16 +1539,16 @@ void runFastConv(InputArray _input, OutputArray _output, const Ptr& co #ifdef CONV_ARM_FP16 if (useFP16) { - opt_NEON::convBlock_FP16(c1 - c0, wptr, inptr, (char *)cptr_f16, ldc, c0 == 0, outLen, CONV_MR, CONV_NR); + opt_NEON_FP16::convBlock_F16(c1 - c0, wptr, inptr, (char *)cptr_f16, ldc, c0 == 0, outLen, CONV_MR, CONV_NR); } else #endif - opt_NEON::convBlock(c1 - c0, (const float *)wptr, (const float *)inptr, cptr, ldc, c0 == 0, outLen, CONV_MR, CONV_NR); + opt_NEON::convBlock_F32(c1 - c0, (const float *)wptr, (const float *)inptr, cptr, ldc, c0 == 0, outLen, CONV_MR, CONV_NR); } else #endif // The possible outLen range is 24 or 8~1. - convBlock(c1 - c0, (const float *)wptr, (const float *)inptr, cptr, ldc, c0 == 0, outLen, CONV_MR, CONV_NR); + convBlock_F32(c1 - c0, (const float *)wptr, (const float *)inptr, cptr, ldc, c0 == 0, outLen, CONV_MR, CONV_NR); } } } @@ -1838,7 +1837,7 @@ static inline void convBlockMR1x12(int np, const float* a, const float* b, float } #endif -void convBlockMR1(int np, const float* a, const float* b, float *c, const float bias, bool init_c, +void convBlockMR1_F32(int np, const float* a, const float* b, float *c, const float bias, bool init_c, const float minval, const float maxval, bool ifMinMaxAct, const int outLen, const int convNR) { #if CV_SIMD128 @@ -2088,7 +2087,7 @@ static inline void convBlockNoSIMD(int np, const float* a, const float* b, float } } -void convBlock(int np, const float* a, const float* b, float* c, int ldc, bool init_c, const int outLen, +void convBlock_F32(int np, const float* a, const float* b, float* c, int ldc, bool init_c, const int outLen, const int convMR, const int convNR) { // The possible outLen range is [24, 8~1]. diff --git a/modules/dnn/src/layers/cpu_kernels/convolution.hpp b/modules/dnn/src/layers/cpu_kernels/convolution.hpp index 5effdc2d0c..e9f169bbaf 100644 --- a/modules/dnn/src/layers/cpu_kernels/convolution.hpp +++ b/modules/dnn/src/layers/cpu_kernels/convolution.hpp @@ -14,7 +14,7 @@ #define CONV_NR_FP32 28 // The FP16 can only be supported by ARM64 and with FP16 FMA supported. -#if defined(__ARM_FEATURE_FP16_VECTOR_ARITHMETIC) && CV_FP16 // check FP16 FMA. +#if CV_FP16 && CV_TRY_NEON_FP16 // check FP16 FMA. #define CONV_ARM_FP16 1 #endif @@ -22,7 +22,6 @@ // Currently, only ARM 64 support FP16. #define CONV_MR_FP16 8 #define CONV_NR_FP16 24 -typedef __fp16 float16_t; // Fix conflict between float16_t in arm_neon.h and float16_t in cvdef.h. #endif #elif CV_NEON // 16 registers. @@ -58,17 +57,15 @@ struct FastConv int pad_top, pad_bottom, pad_left, pad_right, pad_front, pad_behind; std::vector weightsBuf; // For generic Conv 2D - float* weightsBufPtr; std::vector weightsWinoBuf; // For Winograd F(6x6, 3x3). - float* weightsWinoBufPtr; std::vector biasBuf; + float* getWeights(); + float* getWeightsWino(); -#if CV_NEON && CV_NEON_AARCH64 && CV_FP16 std::vector weightsBuf_FP16; - float16_t* weightsBufPtr_FP16; std::vector weightsWinoBuf_FP16; - float16_t* weightsWinoBufPtr_FP16; -#endif + float16_t* getWeightsFP16(); + float16_t* getWeightsWinoFP16(); int conv_type; int conv_dim; // Flag for conv1d, conv2d, or conv3d. @@ -115,6 +112,32 @@ void runDepthwise(InputArray _input, OutputArray _output, const Ptr& c int runWinograd63(InputArray _input, InputArray _fusedAddMat, OutputArray _output, const Ptr& conv, int ntasks, float minval, float maxval, ActivationLayer* activ, bool ifMinMaxAct); +// Work around of NEON, the following functions are only used internally. +namespace opt_NEON { +#if CV_NEON +void convBlock_F32(int np, const float* a, const float* b, float* c, int ldc, bool init_c, int width, const int convMR, const int convNR); + +void convBlockMR1_F32(int np, const float* a, const float* b, float* c, const float bias, bool init_c, + const float minval, const float maxval, bool ifMinMaxAct, const int width, const int convNR); + +#if CV_NEON_AARCH64 +/* Accumulate */ +void winofunc_accum_F32(const float* inwptr, const float* wptr, float* outbuf, int Cg, int iblock, + const int winoIblock, const int winoKblock, const int winoAtom, const int winoNatom); + +/*Input transform*/ +void winofunc_BtXB_8x8_F32(const float* inptr, int inpstep, + float* outptr, int Cg, const int winoIblock, const int winoAtom); + +/*Output transform*/ +void winofunc_AtXA_8x8_F32(const float* inptr, int inpstep, + float* bpptr, int bpstep, float* outptr, int outstep, + float bias, float minval, float maxval, bool ifMinMaxAct); +#endif // CV_NEON_AARCH64 +#endif // CV_NEON +} // namespace opt_NEON. + + } // namespace dnn } // namespace cv diff --git a/modules/dnn/src/layers/cpu_kernels/fast_gemm.cpp b/modules/dnn/src/layers/cpu_kernels/fast_gemm.cpp index b7aa18d486..a8972aba4e 100644 --- a/modules/dnn/src/layers/cpu_kernels/fast_gemm.cpp +++ b/modules/dnn/src/layers/cpu_kernels/fast_gemm.cpp @@ -20,56 +20,145 @@ namespace cv { namespace dnn { +size_t fastGemmPackBSize(size_t N, size_t K, const FastGemmOpt &opt) { +#if CV_TRY_NEON + if (opt.use_neon) { + return static_cast(opt_NEON::fastGemmPackBSize(N, K)); + } else +#endif +#if CV_TRY_AVX2 + if (opt.use_avx2) { + return static_cast(opt_AVX2::fastGemmPackBSize(N, K)); + } else +#endif +#if CV_TRY_AVX + if (opt.use_avx) { + return static_cast(opt_AVX::fastGemmPackBSize(N, K)); + } else +#endif +#if CV_TRY_LASX + if (opt.use_lasx) { + return static_cast(opt_LASX::fastGemmPackBSize(N, K)); + } else +#endif + { + return static_cast(cpu_baseline::fastGemmPackBSize(N, K)); + } +} + void fastGemmPackB(const Mat &B, std::vector &packed_B, bool trans, FastGemmOpt &opt) { - CV_CheckEQ(B.dims, 2, "fastGemmPackB: input mat should be two-dimensional"); CV_CheckTypeEQ(B.type(), CV_32F, "fastGemmPackB: only float32 is supported for now"); auto B_shape = shape(B); - int K = B_shape[0], N = B_shape[1], ldb0 = N, ldb1 = 1; + int batch = total(B_shape, 0, B_shape.size() - 2), + K = B_shape[B_shape.size() - 2], N = B_shape.back(), ldb0 = N, ldb1 = 1; if (trans) { std::swap(K, N); std::swap(ldb0, ldb1); } + const auto *b = B.ptr(); + int esz = B.elemSize(); + #if CV_TRY_NEON if (opt.use_neon) { int size_packed_B = opt_NEON::fastGemmPackBSize(N, K); - packed_B.resize(size_packed_B); - opt_NEON::fastGemmPackBKernel(B.ptr(), (char *)packed_B.data(), N, K, ldb0, ldb1, B.elemSize()); + packed_B.resize(size_packed_B * batch); + auto *packed_b = (char*)packed_B.data(); + for (int i = 0; i < batch; i++) { + opt_NEON::fastGemmPackBKernel(b, packed_b, N, K, ldb0, ldb1, esz); + b += N * K * esz; + packed_b += size_packed_B * esz; + } } else #endif #if CV_TRY_AVX2 if (opt.use_avx2) { int size_packed_B = opt_AVX2::fastGemmPackBSize(N, K); - packed_B.resize(size_packed_B); - opt_AVX2::fastGemmPackBKernel(B.ptr(), (char *)packed_B.data(), N, K, ldb0, ldb1, B.elemSize()); + packed_B.resize(size_packed_B * batch); + auto *packed_b = (char*)packed_B.data(); + for (int i = 0; i < batch; i++) { + opt_AVX2::fastGemmPackBKernel(b, packed_b, N, K, ldb0, ldb1, esz); + b += N * K * esz; + packed_b += size_packed_B * esz; + } } else #endif #if CV_TRY_AVX if (opt.use_avx) { int size_packed_B = opt_AVX::fastGemmPackBSize(N, K); - packed_B.resize(size_packed_B); - opt_AVX::fastGemmPackBKernel(B.ptr(), (char *)packed_B.data(), N, K, ldb0, ldb1, B.elemSize()); + packed_B.resize(size_packed_B * batch); + auto *packed_b = (char*)packed_B.data(); + for (int i = 0; i < batch; i++) { + opt_AVX::fastGemmPackBKernel(b, packed_b, N, K, ldb0, ldb1, esz); + b += N * K * esz; + packed_b += size_packed_B * esz; + } } else #endif #if CV_TRY_LASX if (opt.use_lasx) { int size_packed_B = opt_LASX::fastGemmPackBSize(N, K); - packed_B.resize(size_packed_B); - opt_LASX::fastGemmPackBKernel(B.ptr(), (char *)packed_B.data(), N, K, ldb0, ldb1, B.elemSize()); + packed_B.resize(size_packed_B * batch); + auto *packed_b = (char*)packed_B.data(); + for (int i = 0; i < batch; i++) { + opt_LASX::fastGemmPackBKernel(b, packed_b, N, K, ldb0, ldb1, esz); + b += N * K * esz; + packed_b += size_packed_B * esz; + } } else #endif { int size_packed_B = cpu_baseline::fastGemmPackBSize(N, K); - packed_B.resize(size_packed_B); - cpu_baseline::fastGemmPackBKernel(B.ptr(), (char *)packed_B.data(), N, K, ldb0, ldb1, B.elemSize()); + packed_B.resize(size_packed_B * batch); + auto *packed_b = (char*)packed_B.data(); + for (int i = 0; i < batch; i++) { + cpu_baseline::fastGemmPackBKernel(b, packed_b, N, K, ldb0, ldb1, esz); + b += N * K * esz; + packed_b += size_packed_B * esz; + } + } +} + +void fastGemmPackB(bool trans, size_t N, size_t K, const float *B, size_t ldb, float *packed_B, const FastGemmOpt &opt) { + size_t ldb0 = ldb, ldb1 = 1; + if (trans) { + std::swap(K, N); + std::swap(ldb0, ldb1); + } + + const auto &b = (const char *)B; + auto *packed_b = (char *)packed_B; + +#if CV_TRY_NEON + if (opt.use_neon) { + opt_NEON::fastGemmPackBKernel(b, packed_b, N, K, ldb0, ldb1, sizeof(float)); + } else +#endif +#if CV_TRY_AVX2 + if (opt.use_avx2) { + opt_AVX2::fastGemmPackBKernel(b, packed_b, N, K, ldb0, ldb1, sizeof(float)); + } else +#endif +#if CV_TRY_AVX + if (opt.use_avx) { + opt_AVX::fastGemmPackBKernel(b, packed_b, N, K, ldb0, ldb1, sizeof(float)); + } else +#endif +#if CV_TRY_LASX + if (opt.use_lasx) { + opt_LASX::fastGemmPackBKernel(b, packed_b, N, K, ldb0, ldb1, sizeof(float)); + } else +#endif + { + cpu_baseline::fastGemmPackBKernel(b, packed_b, N, K, ldb0, ldb1, sizeof(float)); } } static void fast_gemm_thin(float alpha, float beta, int M, int N, int K, const char *a_, int lda0, int lda1, const char *b_, int ldb, - char *c_, int ldc) { + char *c_, int ldc, bool multi_thread) { const float* a = (const float*)a_; auto fn = [&](const Range &r) { @@ -88,16 +177,24 @@ static void fast_gemm_thin(float alpha, float beta, int M, int N, int K, } }; - int total = M; // outer loops - int cost_per_thread = static_cast(K * N); // inner loops - double nstripes = (size_t)total * cost_per_thread * (1 / 1024.0); - parallel_for_(Range(0, total), fn, nstripes); + if (multi_thread) { + int total = M; // outer loops + int cost_per_thread = static_cast(K * N); // inner loops + double nstripes = (size_t)total * cost_per_thread * (1 / 1024.0); + parallel_for_(Range(0, total), fn, nstripes); + } else { + fn(Range(0, M)); + } } void fastGemm(bool trans_a, int M, int N, int K, float alpha, const float *A, int lda, const float *packed_B, float beta, float *C, int ldc, FastGemmOpt &opt) { + const char *a = (const char *)A; + const char *packed_b = (const char *)packed_B; + char *c = (char *)C; + int lda0 = lda, lda1 = 1; if (trans_a) { std::swap(lda0, lda1); @@ -105,33 +202,32 @@ void fastGemm(bool trans_a, int M, int N, int K, #if CV_TRY_NEON if (opt.use_neon) { - opt_NEON::fastGemmKernel(M, N, K, alpha, (const char *)A, lda0, lda1, (const char *)packed_B, beta, (char *)C, ldc, sizeof(float)); + opt_NEON::fastGemmKernel(M, N, K, alpha, a, lda0, lda1, packed_b, beta, c, ldc, sizeof(float), opt.multi_thread); } else #endif #if CV_TRY_AVX2 if (opt.use_avx2) { - opt_AVX2::fastGemmKernel(M, N, K, alpha, (const char *)A, lda0, lda1, (const char *)packed_B, beta, (char *)C, ldc, sizeof(float)); + opt_AVX2::fastGemmKernel(M, N, K, alpha, a, lda0, lda1, packed_b, beta, c, ldc, sizeof(float), opt.multi_thread); } else #endif #if CV_TRY_AVX if (opt.use_avx) { - opt_AVX::fastGemmKernel(M, N, K, alpha, (const char *)A, lda0, lda1, (const char *)packed_B, beta, (char *)C, ldc, sizeof(float)); + opt_AVX::fastGemmKernel(M, N, K, alpha, a, lda0, lda1, packed_b, beta, c, ldc, sizeof(float), opt.multi_thread); } else #endif #if CV_TRY_LASX if (opt.use_lasx) { - opt_LASX::fastGemmKernel(M, N, K, alpha, (const char *)A, lda0, lda1, (const char *)packed_B, beta, (char *)C, ldc, sizeof(float)); + opt_LASX::fastGemmKernel(M, N, K, alpha, a, lda0, lda1, packed_b, beta, c, ldc, sizeof(float), opt.multi_thread); } else #endif { - cpu_baseline::fastGemmKernel(M, N, K, alpha, (const char *)A, lda0, lda1, (const char *)packed_B, beta, (char *)C, ldc, sizeof(float)); + cpu_baseline::fastGemmKernel(M, N, K, alpha, a, lda0, lda1, packed_b, beta, c, ldc, sizeof(float), opt.multi_thread); } } void fastGemm(bool trans_a, bool trans_b, int ma, int na, int mb, int nb, float alpha, const float *A, int lda0, int lda1, const float *B, int ldb0, int ldb1, float beta, float *C, int ldc, FastGemmOpt &opt) { - const char *a = (const char *)A; const char *b = (const char *)B; char *c = (char *)C; @@ -148,36 +244,41 @@ void fastGemm(bool trans_a, bool trans_b, int ma, int na, int mb, int nb, } if (!trans_b && ldb1 == 1 && (M <= 4 || (uint64_t)M * N * K <= 10000)) { - return fast_gemm_thin(alpha, beta, M, N, K, a, lda0, lda1, b, ldb0, c, ldc); + return fast_gemm_thin(alpha, beta, M, N, K, a, lda0, lda1, b, ldb0, c, ldc, opt.multi_thread); } #if CV_TRY_NEON if (opt.use_neon) { - opt_NEON::fastGemmKernel(M, N, K, alpha, (const char *)A, lda0, lda1, - (const char *)B, ldb0, ldb1, beta, (char *)C, ldc, sizeof(float)); + opt_NEON::fastGemmKernel(M, N, K, alpha, a, lda0, lda1, + b, ldb0, ldb1, beta, + c, ldc, sizeof(float), opt.multi_thread); } else #endif #if CV_TRY_AVX2 if (opt.use_avx2) { - opt_AVX2::fastGemmKernel(M, N, K, alpha, (const char *)A, lda0, lda1, - (const char *)B, ldb0, ldb1, beta, (char *)C, ldc, sizeof(float)); + opt_AVX2::fastGemmKernel(M, N, K, alpha, a, lda0, lda1, + b, ldb0, ldb1, beta, + c, ldc, sizeof(float), opt.multi_thread); } else #endif #if CV_TRY_AVX if (opt.use_avx) { - opt_AVX::fastGemmKernel(M, N, K, alpha, (const char *)A, lda0, lda1, - (const char *)B, ldb0, ldb1, beta, (char *)C, ldc, sizeof(float)); + opt_AVX::fastGemmKernel(M, N, K, alpha, a, lda0, lda1, + b, ldb0, ldb1, beta, + c, ldc, sizeof(float), opt.multi_thread); } else #endif #if CV_TRY_LASX if (opt.use_lasx) { - opt_LASX::fastGemmKernel(M, N, K, alpha, (const char *)A, lda0, lda1, - (const char *)B, ldb0, ldb1, beta, (char *)C, ldc, sizeof(float)); + opt_LASX::fastGemmKernel(M, N, K, alpha, a, lda0, lda1, + b, ldb0, ldb1, beta, + c, ldc, sizeof(float), opt.multi_thread); } else #endif { - cpu_baseline::fastGemmKernel(M, N, K, alpha, (const char *)A, lda0, lda1, - (const char *)B, ldb0, ldb1, beta, (char *)C, ldc, sizeof(float)); + cpu_baseline::fastGemmKernel(M, N, K, alpha, a, lda0, lda1, + b, ldb0, ldb1, beta, + c, ldc, sizeof(float), opt.multi_thread); } } @@ -209,54 +310,93 @@ void fastGemm(bool trans_a, bool trans_b, beta, c, ldc, opt); } -void fastGemmBatched(bool trans_a, bool trans_b, - float alpha, const Mat &A, const Mat &B, - float beta, Mat &C, FastGemmOpt &opt) { - CV_CheckTypeEQ(A.type(), B.type(), "DNN/fastGemmBatched: A and B should have the same type"); - CV_CheckTypeEQ(B.type(), C.type(), "DNN/fastGemmBatched: B and C should have the same type"); - CV_CheckTypeEQ(A.type(), CV_32F, "DNN/fastGemmBatched: only support float32 for now"); +void fastGemmBatch(size_t batch, const size_t *A_offsets, const size_t *B_offsets, const size_t *C_offsets, + int M, int N, int K, float alpha, const float *A, int lda0, int lda1, + const float *B, int ldb0, int ldb1, float beta, float *C, int ldc, FastGemmOpt &opt) { + const char *a = (const char *)A; + const char *b = (const char *)B; + char *c = (char *)C; - const auto shape_a = shape(A); - size_t dims_A = shape_a.size(); - CV_CheckGE(dims_A, static_cast(2), "DNN/fastGemmBatched: A must be n-dimensional (n >= 2)"); - const auto shape_b = shape(B); - CV_CheckEQ(shape_b.size(), static_cast(2), "DNN/fastGemmBatched: B must be 2-dimensional"); - const auto shape_c = shape(C); - size_t dims_C = shape_c.size(); - CV_CheckGE(dims_C, static_cast(2), "DNN/fastGemmBatched: C must be n-dimensional (n >= 2)"); - - if (trans_a) { - int ma = shape_a[dims_A - 2], na = shape_a[dims_A - 1]; - int mb = shape_b[0], nb = shape_b[1]; - - int lda0 = na, lda1 = 1, ldb0 = nb, ldb1 = 1, ldc = shape_c[1]; - - const float *a = A.ptr(); - const float *b = B.ptr(); - float *c = C.ptr(); - - int batches = std::accumulate(shape_a.begin(), shape_a.end() - 2, 1, std::multiplies()); - int step_a = ma * na, step_c = na * nb; - for (int i = 0; i < batches; i++) { - fastGemm(true, trans_b, ma, na, mb, nb, - alpha, a + i * step_a, lda0, lda1, b, ldb0, ldb1, - beta, c + i * step_c, ldc, opt); - } - } else { - int ma = std::accumulate(shape_a.begin(), shape_a.end() - 1, 1, std::multiplies()), - na = shape_a[dims_A - 1]; - int mb = shape_b[0], nb = shape_b[1]; - - int lda0 = na, lda1 = 1, ldb0 = nb, ldb1 = 1, ldc = shape_c[1]; - - const float *a = A.ptr(); - const float *b = B.ptr(); - float *c = C.ptr(); - - fastGemm(false, trans_b, ma, na, mb, nb, - alpha, a, lda0, lda1, b, ldb0, ldb1, - beta, c, ldc, opt); +#if CV_TRY_NEON + if (opt.use_neon) { + opt_NEON::fastGemmBatchKernel(batch, A_offsets, B_offsets, C_offsets, M, N, K, alpha, a, lda0, lda1, b, ldb0, ldb1, beta, c, ldc, sizeof(float)); + } else +#endif +#if CV_TRY_AVX2 + if (opt.use_avx2) { + opt_AVX2::fastGemmBatchKernel(batch, A_offsets, B_offsets, C_offsets, M, N, K, alpha, a, lda0, lda1, b, ldb0, ldb1, beta, c, ldc, sizeof(float)); + } else +#endif +#if CV_TRY_AVX + if (opt.use_avx) { + opt_AVX::fastGemmBatchKernel(batch, A_offsets, B_offsets, C_offsets, M, N, K, alpha, a, lda0, lda1, b, ldb0, ldb1, beta, c, ldc, sizeof(float)); + } else +#endif +#if CV_TRY_LASX + if (opt.use_lasx) { + opt_LASX::fastGemmBatchKernel(batch, A_offsets, B_offsets, C_offsets, M, N, K, alpha, a, lda0, lda1, b, ldb0, ldb1, beta, c, ldc, sizeof(float)); + } else +#endif + { + cpu_baseline::fastGemmBatchKernel(batch, A_offsets, B_offsets, C_offsets, M, N, K, alpha, a, lda0, lda1, b, ldb0, ldb1, beta, c, ldc, sizeof(float)); } } +void fastGemmBatch(size_t batch, const size_t *A_offsets, const size_t *packed_B_offsets, const size_t *C_offsets, + int M, int N, int K, float alpha, const float *A, int lda0, int lda1, + const float *packed_B, float beta, float *C, int ldc, FastGemmOpt &opt) { + const char *a = (const char *)A; + const char *b = (const char *)packed_B; + char *c = (char *)C; + +#if CV_TRY_NEON + if (opt.use_neon) { + opt_NEON::fastGemmBatchKernel(batch, A_offsets, packed_B_offsets, C_offsets, M, N, K, alpha, a, lda0, lda1, b, beta, c, ldc, sizeof(float)); + } else +#endif +#if CV_TRY_AVX2 + if (opt.use_avx2) { + opt_AVX2::fastGemmBatchKernel(batch, A_offsets, packed_B_offsets, C_offsets, M, N, K, alpha, a, lda0, lda1, b, beta, c, ldc, sizeof(float)); + } else +#endif +#if CV_TRY_AVX + if (opt.use_avx) { + opt_AVX::fastGemmBatchKernel(batch, A_offsets, packed_B_offsets, C_offsets, M, N, K, alpha, a, lda0, lda1, b, beta, c, ldc, sizeof(float)); + } else +#endif +#if CV_TRY_LASX + if (opt.use_lasx) { + opt_LASX::fastGemmBatchKernel(batch, A_offsets, packed_B_offsets, C_offsets, M, N, K, alpha, a, lda0, lda1, b, beta, c, ldc, sizeof(float)); + } else +#endif + { + cpu_baseline::fastGemmBatchKernel(batch, A_offsets, packed_B_offsets, C_offsets, M, N, K, alpha, a, lda0, lda1, b, beta, c, ldc, sizeof(float)); + } +} + +void fastGemmBatch(bool trans_a, bool trans_b, + float alpha, const Mat &A, const Mat &B, + float beta, Mat &C, FastGemmOpt &opt) { + CV_CheckTypeEQ(A.type(), B.type(), "DNN/fastGemmBatch: A and B should have the same type"); + CV_CheckTypeEQ(B.type(), C.type(), "DNN/fastGemmBatch: B and C should have the same type"); + CV_CheckTypeEQ(A.type(), CV_32F, "DNN/fastGemmBatch: only support float32 for now"); + + const auto shape_a = shape(A); + const auto shape_b = shape(B); + const auto shape_c = shape(C); + CV_CheckGE(shape_a.size(), static_cast(2), "DNN/fastGemmBatch: A must be n-dimensional (n >= 2)"); + CV_CheckEQ(shape_b.size(), static_cast(2), "DNN/fastGemmBatch: B must be n-dimensional (n >= 2)"); + + const float *a = A.ptr(); + const float *b = B.ptr(); + float *c = C.ptr(); + + MatMulHelper helper; + helper.compute(trans_a, trans_b, shape_a, shape_b, shape_c); + + fastGemmBatch(helper.batch, helper.A_offsets.data(), helper.B_offsets.data(), helper.C_offsets.data(), + helper.M, helper.N, helper.K, alpha, a, helper.lda0, helper.lda1, b, helper.ldb0, + helper.ldb1, beta, c, helper.ldc, opt); +} + }} // cv::dnn diff --git a/modules/dnn/src/layers/cpu_kernels/fast_gemm.hpp b/modules/dnn/src/layers/cpu_kernels/fast_gemm.hpp index 7f9e5c3017..a207c63c3c 100644 --- a/modules/dnn/src/layers/cpu_kernels/fast_gemm.hpp +++ b/modules/dnn/src/layers/cpu_kernels/fast_gemm.hpp @@ -22,12 +22,14 @@ struct FastGemmOpt { bool use_avx2; bool use_neon; bool use_lasx; + bool multi_thread; FastGemmOpt() { use_avx = false; use_avx2 = false; use_neon = false; use_lasx = false; + multi_thread = false; } void init() { @@ -35,6 +37,7 @@ struct FastGemmOpt { use_avx2 = checkHardwareSupport(CPU_AVX2); use_neon = checkHardwareSupport(CPU_NEON); use_lasx = checkHardwareSupport(CPU_LASX); + multi_thread = true; } bool all() { @@ -42,7 +45,116 @@ struct FastGemmOpt { } }; +struct MatMulHelper { + std::vector A_offsets; + std::vector B_offsets; + std::vector packed_B_offsets; + std::vector C_offsets; + std::vector A_rows; + std::vector B_rows; + std::vector C_rows; + size_t batch; + + int lda0, lda1; + int ldb0, ldb1; + int ldc; + + int M, N, K; + + MatMulHelper() { + A_offsets = {0}; + B_offsets = {0}; + packed_B_offsets = {0}; + C_offsets = {0}; + A_rows = {0}; + B_rows = {0}; + C_rows = {0}; + + batch = 0; + } + + bool empty() const { + return batch == 0; + } + + void compute(bool trans_a, bool trans_b, MatShape A_shape, MatShape B_shape, MatShape C_shape) { + auto A_ndims = A_shape.size(), B_ndims = B_shape.size(), C_ndims = C_shape.size(); + int ma = A_shape[A_ndims - 2], na = A_shape.back(); + int mb = B_shape[B_ndims - 2], nb = B_shape.back(); + lda0 = na, lda1 = 1; + ldb0 = nb, ldb1 = 1; + ldc = C_shape.back(); + + M = trans_a ? na : ma; + N = trans_b ? mb : nb; + K = trans_a ? ma : na; + + if (trans_a) { + std::swap(lda0, lda1); + } + if (trans_b) { + std::swap(ldb0, ldb1); + } + + // compute offsets + auto batch_ndims = C_ndims - 2; + + batch = total(C_shape, 0, batch_ndims); + + A_offsets.resize(batch, 0); + B_offsets.resize(batch, 0); + C_offsets.resize(batch, 0); + A_rows.resize(batch, 0); + B_rows.resize(batch, 0); + C_rows.resize(batch, 0); + + // build C_offsets + size_t C_step = total(C_shape, C_ndims - 2); + + MatShape A_broadcast_shape(C_ndims, 1); + std::memcpy(A_broadcast_shape.data() + (C_ndims - A_ndims), A_shape.data(), A_ndims * sizeof(int)); + MatShape B_broadcast_shape(C_shape.size(), 1); + std::memcpy(B_broadcast_shape.data() + (C_ndims - B_ndims), B_shape.data(), B_shape.size() * sizeof(int)); + std::vector A_steps(C_ndims, 1), B_steps(C_ndims, 1); + for (int i = C_ndims - 2; i >= 0; i--) { + A_steps[i] = A_steps[i + 1] * A_broadcast_shape[i + 1]; + B_steps[i] = B_steps[i + 1] * B_broadcast_shape[i + 1]; + } + size_t t, idx; + for (size_t i = 0; i < batch; i++) { + C_offsets[i] = i * C_step; + C_rows[i] = i; + + size_t A_offset = 0, B_offset = 0; + t = i; + for (int j = batch_ndims - 1; j >= 0; j--) { + idx = t / C_shape[j]; + int idx_offset = (int)(t - idx * C_shape[j]); + A_offset += A_broadcast_shape[j] == 1 ? 0 : idx_offset * A_steps[j]; + B_offset += B_broadcast_shape[j] == 1 ? 0 : idx_offset * B_steps[j]; + t = idx; + } + A_offsets[i] = A_offset; + B_offsets[i] = B_offset; + A_rows[i] = A_offset / (M * K); + B_rows[i] = B_offset / (N * K); + } + } + + // only run after compute + void updatePackedBOffsets(size_t packed_B_size) { + size_t packed_B_inner_size = packed_B_size / batch; + packed_B_offsets.resize(B_offsets.size()); + for (size_t i = 0; i < packed_B_offsets.size(); i++) { + packed_B_offsets[i] = (B_offsets[i] / (N * K)) * packed_B_inner_size; + } + } +}; + +size_t fastGemmPackBSize(size_t N, size_t K, const FastGemmOpt &opt); + void fastGemmPackB(const Mat &m, std::vector &packed_B, bool trans, FastGemmOpt &opt); +void fastGemmPackB(bool trans, size_t N, size_t K, const float *B, size_t ldb, float *packed_B, const FastGemmOpt &opt); void fastGemm(bool trans_a, int M, int N, int K, float alpha, const float *A, int lda, @@ -55,10 +167,14 @@ void fastGemm(bool trans_a, bool trans_b, float alpha, const Mat &A, const Mat &B, float beta, Mat &C, FastGemmOpt &opt); -// FIXME: B needs to 2d for now. Support nd (n>=2) B in the future. -void fastGemmBatched(bool trans_a, bool trans_b, - float alpha, const Mat &A, const Mat &B, - float beta, Mat &C, FastGemmOpt &opt); +void fastGemmBatch(size_t batch, const size_t *A_offsets, const size_t *B_offsets, const size_t *C_offsets, + int M, int N, int K, float alpha, const float *A, int lda0, int lda1, + const float *B, int ldb0, int ldb1, float beta, float *C, int ldc, FastGemmOpt &opt); +void fastGemmBatch(size_t batch, const size_t *A_offsets, const size_t *B_offsets, const size_t *C_offsets, + int M, int N, int K, float alpha, const float *A, int lda0, int lda1, + const float *packed_B, float beta, float *C, int ldc, FastGemmOpt &opt); +void fastGemmBatch(bool trans_a, bool trans_b, float alpha, const Mat &A, + const Mat &B, float beta, Mat &C, FastGemmOpt &opt); }} // cv::dnn diff --git a/modules/dnn/src/layers/cpu_kernels/fast_gemm_kernels.default.hpp b/modules/dnn/src/layers/cpu_kernels/fast_gemm_kernels.default.hpp index b9362bb4d5..f6bd7317a2 100644 --- a/modules/dnn/src/layers/cpu_kernels/fast_gemm_kernels.default.hpp +++ b/modules/dnn/src/layers/cpu_kernels/fast_gemm_kernels.default.hpp @@ -83,10 +83,17 @@ void fastGemmPackBKernel(const char *B, char *packed_B, int N, int K, int ldb0, void fastGemmKernel(int M, int N, int K, float alpha, const char *A, int lda0, int lda1, const char *B, int ldb0, int ldb1, - float beta, char *C, int ldc, int esz); + float beta, char *C, int ldc, int esz, bool multi_thread); void fastGemmKernel(int M, int N, int K, float alpha, const char *A, int lda0, int lda1, - const char *packed_B, float beta, char *C, int ldc, int esz); + const char *packed_B, float beta, char *C, int ldc, int esz, bool multi_thread); + +void fastGemmBatchKernel(size_t batch, const size_t *A_offsets, const size_t *B_offsets, const size_t *C_offsets, + int M, int N, int K, float alpha, const char *A, int lda0, int lda1, + const char *B, int ldb0, int ldb1, float beta, char *C, int ldc, int esz); +void fastGemmBatchKernel(size_t batch, const size_t *A_offsets, const size_t *B_offsets, const size_t *C_offsets, + int M, int N, int K, float alpha, const char *A, int lda0, int lda1, + const char *packed_B, float beta, char *C, int ldc, int esz); FAST_GEMM_IMPLEMENT_PACK(8, _f32, float, float) FAST_GEMM_IMPLEMENT_PACK(12, _f32, float, float) @@ -172,7 +179,7 @@ static void fast_gemm_macro_kernel(int m, int n, int k, void fastGemmKernel(int M, int N, int K, float alpha, const char *A, int lda0, int lda1, const char *B, int ldb0, int ldb1, - float beta, char *C, int ldc, int esz) { + float beta, char *C, int ldc, int esz, bool multi_thread) { int GEMM_MC = FAST_GEMM_F32_MC, GEMM_NC = FAST_GEMM_F32_NC, GEMM_MR = FAST_GEMM_F32_MR, @@ -229,15 +236,18 @@ void fastGemmKernel(int M, int N, int K, } }; - int total = total_tiles; - int cost_per_thread = static_cast((K / KC) * (MC / GEMM_MR) * (NC / GEMM_NR)); - double nstripes = (size_t)total * cost_per_thread * (1 / 1024.0); - parallel_for_(Range(0, total), fn, nstripes); + if (multi_thread) { + int cost_per_thread = static_cast((K / KC) * (MC / GEMM_MR) * (NC / GEMM_NR)); + double nstripes = (size_t)total_tiles * cost_per_thread * (1 / 1024.0); + parallel_for_(Range(0, total_tiles), fn, nstripes); + } else { + fn(Range(0, total_tiles)); + } } void fastGemmKernel(int M, int N, int K, float alpha, const char *A, int lda0, int lda1, - const char *packed_B, float beta, char *C, int ldc, int esz) { + const char *packed_B, float beta, char *C, int ldc, int esz, bool multi_thread) { int GEMM_MC = FAST_GEMM_F32_MC, GEMM_NC = FAST_GEMM_F32_NC, GEMM_MR = FAST_GEMM_F32_MR, @@ -294,7 +304,157 @@ void fastGemmKernel(int M, int N, int K, } }; - int total = total_tiles; + if (multi_thread) { + int cost_per_thread = static_cast((K / KC) * (MC / GEMM_MR) * (NC / GEMM_NR)); + double nstripes = (size_t)total_tiles * cost_per_thread * (1 / 1024.0); + parallel_for_(Range(0, total_tiles), fn, nstripes); + } else { + fn(Range(0, total_tiles)); + } +} + +void fastGemmBatchKernel(size_t batch, const size_t *A_offsets, const size_t *B_offsets, const size_t *C_offsets, + int M, int N, int K, float alpha, const char *A, int lda0, int lda1, + const char *B, int ldb0, int ldb1, float beta, char *C, int ldc, int esz) { + int GEMM_MC = FAST_GEMM_F32_MC, + GEMM_NC = FAST_GEMM_F32_NC, + GEMM_MR = FAST_GEMM_F32_MR, + GEMM_NR = FAST_GEMM_F32_NR; + + int MC = (((GEMM_MC < M ? GEMM_MC : M) + GEMM_MR - 1) / GEMM_MR) * GEMM_MR; + int NC = (((GEMM_NC < N ? GEMM_NC : N) + GEMM_NR - 1) / GEMM_NR) * GEMM_NR; + int KC = std::min(FAST_GEMM_F32_PACKED_STRIDE_K, K); + + size_t buff_size = KC * (MC + NC) * esz; + bool use_stackbuff = buff_size <= FAST_GEMM_MAX_STACKBUF; + int m_tiles = (M + MC - 1) / MC; + int n_tiles = (N + NC - 1) / NC; + int total_tiles = m_tiles * n_tiles; + + auto fn = [&](const Range &r) { + char* packed_a = (char*)(use_stackbuff ? alloca(buff_size) : malloc(buff_size)); + char* packed_b = packed_a + KC * MC * esz; + int start = r.start; + int end = r.end; + + for (int tile_idx = start; tile_idx < end; tile_idx++) { + const int batch_index = static_cast(tile_idx / total_tiles); + const int m_tiles_index = static_cast((tile_idx - batch_index * total_tiles) / n_tiles); + const int n_tiles_index = static_cast(tile_idx % n_tiles); + + int i0 = m_tiles_index * MC; + int j0 = n_tiles_index * NC; + int mc = M - i0 < MC ? M - i0 : MC; + int nc = N - j0 < NC ? N - j0 : NC; + int ldc_block = ldc; + const char *a_block = A + A_offsets[batch_index] * esz; + const char *b_block = B + B_offsets[batch_index] * esz; + char* c_block = C + C_offsets[batch_index] * esz + (i0 * ldc + j0) * esz; + + if (beta == 0.f) { + for(int i = 0; i < mc; i++) + memset(c_block + i * ldc_block * esz, 0, nc * esz); + } else if (beta != 1.f) { + for(int i = 0; i < mc; i++) { + float* c_i = (float*)c_block + i * ldc_block; + for(int j = 0; j < nc; j++) + c_i[j] *= beta; + } + } + + for(int k0 = 0; k0 < K; k0 += KC) + { + int kc = K - k0 < KC ? K - k0 : KC; + // pack a + fast_gemm_pack8_f32(mc, kc, a_block + (i0 * lda0 + k0 * lda1) * esz, lda0, lda1, packed_a); + + // pack b + fast_gemm_pack12_f32(nc, kc, b_block + (k0 * ldb0 + j0 * ldb1) * esz, ldb1, ldb0, packed_b); + + // run kernel + fast_gemm_macro_kernel(mc, nc, kc, packed_a, packed_b, alpha, c_block, ldc_block, esz); + } + } + + if (!use_stackbuff) { + free(packed_a); + } + }; + + int total = batch * total_tiles; + int cost_per_thread = static_cast((K / KC) * (MC / GEMM_MR) * (NC / GEMM_NR)); + double nstripes = (size_t)total * cost_per_thread * (1 / 1024.0); + parallel_for_(Range(0, total), fn, nstripes); +} + +void fastGemmBatchKernel(size_t batch, const size_t *A_offsets, const size_t *B_offsets, const size_t *C_offsets, + int M, int N, int K, float alpha, const char *A, int lda0, int lda1, + const char *packed_B, float beta, char *C, int ldc, int esz) { + int GEMM_MC = FAST_GEMM_F32_MC, + GEMM_NC = FAST_GEMM_F32_NC, + GEMM_MR = FAST_GEMM_F32_MR, + GEMM_NR = FAST_GEMM_F32_NR; + + int MC = (((GEMM_MC < M ? GEMM_MC : M) + GEMM_MR - 1) / GEMM_MR) * GEMM_MR; + int NC = (((GEMM_NC < N ? GEMM_NC : N) + GEMM_NR - 1) / GEMM_NR) * GEMM_NR; + int KC = std::min(FAST_GEMM_F32_PACKED_STRIDE_K, K); + + size_t buff_size = KC * MC * esz; + bool use_stackbuff = buff_size <= FAST_GEMM_MAX_STACKBUF; + int m_tiles = (M + MC - 1) / MC; + int n_tiles = (N + NC - 1) / NC; + int total_tiles = m_tiles * n_tiles; + + auto fn = [&](const Range &r) { + char* packed_a = (char*)(use_stackbuff ? alloca(buff_size) : malloc(buff_size)); + const char *packed_b = packed_B; + int start = r.start; + int end = r.end; + + for (int tile_idx = start; tile_idx < end; tile_idx++) { + const int batch_index = static_cast(tile_idx / total_tiles); + const int m_tiles_index = static_cast((tile_idx - batch_index * total_tiles) / n_tiles); + const int n_tiles_index = static_cast(tile_idx % n_tiles); + + int i0 = m_tiles_index * MC; + int j0 = n_tiles_index * NC; + int mc = M - i0 < MC ? M - i0 : MC; + int nc = N - j0 < NC ? N - j0 : NC; + int ldc_block = ldc; + const char *a_block = A + A_offsets[batch_index] * esz; + packed_b = packed_B + B_offsets[batch_index] * esz + j0 * K * esz; + char* c_block = C + C_offsets[batch_index] * esz + (i0 * ldc + j0) * esz; + + if (beta == 0.f) { + for(int i = 0; i < mc; i++) + memset(c_block + i * ldc_block * esz, 0, nc * esz); + } else if (beta != 1.f) { + for(int i = 0; i < mc; i++) { + float* c_i = (float*)c_block + i * ldc_block; + for(int j = 0; j < nc; j++) + c_i[j] *= beta; + } + } + + int _nc = static_cast((nc + GEMM_NR - 1) / GEMM_NR) * GEMM_NR * esz; + for(int k0 = 0; k0 < K; k0 += KC) + { + int kc = K - k0 < KC ? K - k0 : KC; + // pack a + fast_gemm_pack8_f32(mc, kc, a_block + (i0 * lda0 + k0 * lda1) * esz, lda0, lda1, packed_a); + + // run kernel + fast_gemm_macro_kernel(mc, nc, kc, packed_a, packed_b, alpha, c_block, ldc_block, esz); + packed_b += _nc * kc; + } + } + + if (!use_stackbuff) { + free(packed_a); + } + }; + + int total = batch * total_tiles; int cost_per_thread = static_cast((K / KC) * (MC / GEMM_MR) * (NC / GEMM_NR)); double nstripes = (size_t)total * cost_per_thread * (1 / 1024.0); parallel_for_(Range(0, total), fn, nstripes); diff --git a/modules/dnn/src/layers/cpu_kernels/fast_gemm_kernels.simd.hpp b/modules/dnn/src/layers/cpu_kernels/fast_gemm_kernels.simd.hpp index 7d123ed9b5..8e63d15137 100644 --- a/modules/dnn/src/layers/cpu_kernels/fast_gemm_kernels.simd.hpp +++ b/modules/dnn/src/layers/cpu_kernels/fast_gemm_kernels.simd.hpp @@ -22,8 +22,8 @@ #define FAST_GEMM_F32_MC 48 #define FAST_GEMM_F32_NC 128 #else // CV_NEON_AARCH64, SIMD128 -#define FAST_GEMM_F32_MC 64 -#define FAST_GEMM_F32_NC 240 +#define FAST_GEMM_F32_MC 144 +#define FAST_GEMM_F32_NC 72 #endif #if CV_AVX @@ -122,10 +122,17 @@ void fastGemmPackBKernel(const char *B, char *packed_B, int N, int K, int ldb0, void fastGemmKernel(int M, int N, int K, float alpha, const char *A, int lda0, int lda1, const char *B, int ldb0, int ldb1, - float beta, char *C, int ldc, int esz); + float beta, char *C, int ldc, int esz, bool multi_thread); void fastGemmKernel(int M, int N, int K, float alpha, const char *A, int lda0, int lda1, - const char *packed_B, float beta, char *C, int ldc, int esz); + const char *packed_B, float beta, char *C, int ldc, int esz, bool multi_thread); + +void fastGemmBatchKernel(size_t batch, const size_t *A_offsets, const size_t *B_offsets, const size_t *C_offsets, + int M, int N, int K, float alpha, const char *A, int lda0, int lda1, + const char *B, int ldb0, int ldb1, float beta, char *C, int ldc, int esz); +void fastGemmBatchKernel(size_t batch, const size_t *A_offsets, const size_t *B_offsets, const size_t *C_offsets, + int M, int N, int K, float alpha, const char *A, int lda0, int lda1, + const char *packed_B, float beta, char *C, int ldc, int esz); #ifndef CV_CPU_OPTIMIZATION_DECLARATIONS_ONLY @@ -561,7 +568,7 @@ void fastGemmPackBKernel(const char *B, char *packed_B, int N, int K, int ldb0, void fastGemmKernel(int M, int N, int K, float alpha, const char *A, int lda0, int lda1, const char *B, int ldb0, int ldb1, - float beta, char *C, int ldc, int esz) { + float beta, char *C, int ldc, int esz, bool multi_thread) { int GEMM_MC = FAST_GEMM_F32_MC, GEMM_NC = FAST_GEMM_F32_NC, GEMM_MR = FAST_GEMM_F32_MR, @@ -639,15 +646,19 @@ void fastGemmKernel(int M, int N, int K, } }; - int total = total_tiles; - int cost_per_thread = static_cast((K / KC) * (MC / GEMM_MR) * (NC / GEMM_NR)); - double nstripes = (size_t)total * cost_per_thread * (1 / 1024.0); - parallel_for_(Range(0, total), fn, nstripes); + if (multi_thread) { + int cost_per_thread = static_cast((K / KC) * (MC / GEMM_MR) * (NC / GEMM_NR)); + double nstripes = (size_t)total_tiles * cost_per_thread * (1 / 1024.0); + parallel_for_(Range(0, total_tiles), fn, nstripes); + } else { + fn(Range(0, total_tiles)); + } + } void fastGemmKernel(int M, int N, int K, float alpha, const char *A, int lda0, int lda1, - const char *packed_B, float beta, char *C, int ldc, int esz) { + const char *packed_B, float beta, char *C, int ldc, int esz, bool multi_thread) { int GEMM_MC = FAST_GEMM_F32_MC, GEMM_NC = FAST_GEMM_F32_NC, GEMM_MR = FAST_GEMM_F32_MR, @@ -715,7 +726,181 @@ void fastGemmKernel(int M, int N, int K, } }; - int total = total_tiles; + if (multi_thread) { + int cost_per_thread = static_cast((K / KC) * (MC / GEMM_MR) * (NC / GEMM_NR)); + double nstripes = (size_t)total_tiles * cost_per_thread * (1 / 1024.0); + parallel_for_(Range(0, total_tiles), fn, nstripes); + } else { + fn(Range(0, total_tiles)); + } +} + +void fastGemmBatchKernel(size_t batch, const size_t *A_offsets, const size_t *B_offsets, const size_t *C_offsets, + int M, int N, int K, float alpha, const char *A, int lda0, int lda1, + const char *B, int ldb0, int ldb1, float beta, char *C, int ldc, int esz) { + int GEMM_MC = FAST_GEMM_F32_MC, + GEMM_NC = FAST_GEMM_F32_NC, + GEMM_MR = FAST_GEMM_F32_MR, + GEMM_NR = FAST_GEMM_F32_NR; + + int MC = (((GEMM_MC < M ? GEMM_MC : M) + GEMM_MR - 1) / GEMM_MR) * GEMM_MR; + int NC = (((GEMM_NC < N ? GEMM_NC : N) + GEMM_NR - 1) / GEMM_NR) * GEMM_NR; + int KC = std::min(FAST_GEMM_F32_PACKED_STRIDE_K, K); + + size_t buff_size = KC * (MC + NC) * esz; + bool use_stackbuff = buff_size <= FAST_GEMM_MAX_STACKBUF; + int m_tiles = (M + MC - 1) / MC; + int n_tiles = (N + NC - 1) / NC; + int total_tiles = m_tiles * n_tiles; + + auto fn = [&](const Range &r) { + char* packed_a = (char*)(use_stackbuff ? alloca(buff_size) : malloc(buff_size)); + char* packed_b = packed_a + KC * MC * esz; + int start = r.start; + int end = r.end; + + for (int tile_idx = start; tile_idx < end; tile_idx++) { + const int batch_index = static_cast(tile_idx / total_tiles); + const int m_tiles_index = static_cast((tile_idx - batch_index * total_tiles) / n_tiles); + const int n_tiles_index = static_cast(tile_idx % n_tiles); + + int i0 = m_tiles_index * MC; + int j0 = n_tiles_index * NC; + int mc = M - i0 < MC ? M - i0 : MC; + int nc = N - j0 < NC ? N - j0 : NC; + int ldc_block = ldc; + const char *a_block = A + A_offsets[batch_index] * esz; + const char *b_block = B + B_offsets[batch_index] * esz; + char* c_block = C + C_offsets[batch_index] * esz + (i0 * ldc + j0) * esz; + + if (beta == 0.f) { + for(int i = 0; i < mc; i++) + memset(c_block + i * ldc_block * esz, 0, nc * esz); + } else if (beta != 1.f) { + for(int i = 0; i < mc; i++) { + float* c_i = (float*)c_block + i * ldc_block; + for(int j = 0; j < nc; j++) + c_i[j] *= beta; + } + } + + for(int k0 = 0; k0 < K; k0 += KC) + { + int kc = K - k0 < KC ? K - k0 : KC; + // pack a +#if CV_NEON && CV_NEON_AARCH64 + fast_gemm_pack8_f32(mc, kc, a_block + (i0 * lda0 + k0 * lda1) * esz, lda0, lda1, packed_a); +#elif CV_AVX + fast_gemm_pack12_f32(mc, kc, a_block + (i0 * lda0 + k0 * lda1) * esz, lda0, lda1, packed_a); +#elif CV_LASX + fast_gemm_pack12_f32(mc, kc, a_block + (i0 * lda0 + k0 * lda1) * esz, lda0, lda1, packed_a); +#elif CV_SIMD128 + fast_gemm_pack8_f32(mc, kc, a_block + (i0 * lda0 + k0 * lda1) * esz, lda0, lda1, packed_a); +#endif + + // pack b +#if CV_NEON && CV_NEON_AARCH64 + fast_gemm_pack12_f32(nc, kc, b_block + (k0 * ldb0 + j0 * ldb1) * esz, ldb1, ldb0, packed_b); +#elif CV_AVX + fast_gemm_pack8_f32(nc, kc, b_block + (k0 * ldb0 + j0 * ldb1) * esz, ldb1, ldb0, packed_b); +#elif CV_LASX + fast_gemm_pack16_f32(nc, kc, b_block + (k0 * ldb0 + j0 * ldb1) * esz, ldb1, ldb0, packed_b); +#elif CV_SIMD128 + fast_gemm_pack12_f32(nc, kc, b_block + (k0 * ldb0 + j0 * ldb1) * esz, ldb1, ldb0, packed_b); +#endif + + // run kernel + fast_gemm_macro_kernel(mc, nc, kc, packed_a, packed_b, alpha, c_block, ldc_block, esz); + } + } + + if (!use_stackbuff) { + free(packed_a); + } + }; + + int total = batch * total_tiles; + int cost_per_thread = static_cast((K / KC) * (MC / GEMM_MR) * (NC / GEMM_NR)); + double nstripes = (size_t)total * cost_per_thread * (1 / 1024.0); + parallel_for_(Range(0, total), fn, nstripes); +} + +void fastGemmBatchKernel(size_t batch, const size_t *A_offsets, const size_t *B_offsets, const size_t *C_offsets, + int M, int N, int K, float alpha, const char *A, int lda0, int lda1, + const char *packed_B, float beta, char *C, int ldc, int esz) { + int GEMM_MC = FAST_GEMM_F32_MC, + GEMM_NC = FAST_GEMM_F32_NC, + GEMM_MR = FAST_GEMM_F32_MR, + GEMM_NR = FAST_GEMM_F32_NR; + + int MC = (((GEMM_MC < M ? GEMM_MC : M) + GEMM_MR - 1) / GEMM_MR) * GEMM_MR; + int NC = (((GEMM_NC < N ? GEMM_NC : N) + GEMM_NR - 1) / GEMM_NR) * GEMM_NR; + int KC = std::min(FAST_GEMM_F32_PACKED_STRIDE_K, K); + + size_t buff_size = KC * MC * esz; + bool use_stackbuff = buff_size <= FAST_GEMM_MAX_STACKBUF; + int m_tiles = (M + MC - 1) / MC; + int n_tiles = (N + NC - 1) / NC; + int total_tiles = m_tiles * n_tiles; + + auto fn = [&](const Range &r) { + char* packed_a = (char*)(use_stackbuff ? alloca(buff_size) : malloc(buff_size)); + const char *packed_b = packed_B; + int start = r.start; + int end = r.end; + + for (int tile_idx = start; tile_idx < end; tile_idx++) { + const int batch_index = static_cast(tile_idx / total_tiles); + const int m_tiles_index = static_cast((tile_idx - batch_index * total_tiles) / n_tiles); + const int n_tiles_index = static_cast(tile_idx % n_tiles); + + int i0 = m_tiles_index * MC; + int j0 = n_tiles_index * NC; + int mc = M - i0 < MC ? M - i0 : MC; + int nc = N - j0 < NC ? N - j0 : NC; + int ldc_block = ldc; + const char *a_block = A + A_offsets[batch_index] * esz; + packed_b = packed_B + B_offsets[batch_index] * esz + j0 * K * esz; + char* c_block = C + C_offsets[batch_index] * esz + (i0 * ldc + j0) * esz; + + if (beta == 0.f) { + for(int i = 0; i < mc; i++) + memset(c_block + i * ldc_block * esz, 0, nc * esz); + } else if (beta != 1.f) { + for(int i = 0; i < mc; i++) { + float* c_i = (float*)c_block + i * ldc_block; + for(int j = 0; j < nc; j++) + c_i[j] *= beta; + } + } + + int _nc = static_cast((nc + GEMM_NR - 1) / GEMM_NR) * GEMM_NR * esz; + for(int k0 = 0; k0 < K; k0 += KC) + { + int kc = K - k0 < KC ? K - k0 : KC; + // pack a +#if CV_NEON && CV_NEON_AARCH64 + fast_gemm_pack8_f32(mc, kc, a_block + (i0 * lda0 + k0 * lda1) * esz, lda0, lda1, packed_a); +#elif CV_AVX + fast_gemm_pack12_f32(mc, kc, a_block + (i0 * lda0 + k0 * lda1) * esz, lda0, lda1, packed_a); +#elif CV_LASX + fast_gemm_pack12_f32(mc, kc, a_block + (i0 * lda0 + k0 * lda1) * esz, lda0, lda1, packed_a); +#elif CV_SIMD128 + fast_gemm_pack8_f32(mc, kc, a_block + (i0 * lda0 + k0 * lda1) * esz, lda0, lda1, packed_a); +#endif + + // run kernel + fast_gemm_macro_kernel(mc, nc, kc, packed_a, packed_b, alpha, c_block, ldc_block, esz); + packed_b += _nc * kc; + } + } + + if (!use_stackbuff) { + free(packed_a); + } + }; + + int total = batch * total_tiles; int cost_per_thread = static_cast((K / KC) * (MC / GEMM_MR) * (NC / GEMM_NR)); double nstripes = (size_t)total * cost_per_thread * (1 / 1024.0); parallel_for_(Range(0, total), fn, nstripes); diff --git a/modules/dnn/src/layers/cpu_kernels/fast_norm.cpp b/modules/dnn/src/layers/cpu_kernels/fast_norm.cpp index 60b503513f..ab9d8ee0af 100644 --- a/modules/dnn/src/layers/cpu_kernels/fast_norm.cpp +++ b/modules/dnn/src/layers/cpu_kernels/fast_norm.cpp @@ -118,10 +118,11 @@ void fastNorm(const Mat &input, const Mat &scale, const Mat &bias, Mat &output, void fastNormChannel(const Mat &input, const Mat &scale, const Mat &bias, Mat &output, float epsilon) { const auto input_shape = shape(input); + size_t N = input_shape[0], C = input_shape[1]; CV_CheckEQ(scale.total(), bias.total(), "fastNormChannel: scale and bias should have the same shape"); + CV_CheckEQ(scale.total(), C, "fastNormChannel: scale should be a 1d tensor and match the channel of input"); CV_CheckGE(input.dims, 3, "fastNormChannel: input dimension >= 3"); - size_t N = input_shape[0], C = input_shape[1]; size_t loops = N * C, norm_size = static_cast(total(input_shape, 2)); float inv_norm_size = 1.0 / norm_size; @@ -147,9 +148,9 @@ void fastNormChannel(const Mat &input, const Mat &scale, const Mat &bias, Mat &o float inv_stdev = 1.f / mean_square; size_t c = i % C; - float s = scale_data[c], b = bias_data[c]; + float s = scale_data[c] * inv_stdev, b = bias_data[c]; for (size_t j = 0; j < norm_size; j++) { - y[j] = s * (x[j] - mean) * inv_stdev + b; + y[j] = s * (x[j] - mean) + b; } } }; diff --git a/modules/dnn/src/layers/cpu_kernels/softmax.cpp b/modules/dnn/src/layers/cpu_kernels/softmax.cpp new file mode 100644 index 0000000000..eb258ecfa2 --- /dev/null +++ b/modules/dnn/src/layers/cpu_kernels/softmax.cpp @@ -0,0 +1,157 @@ +// 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. + +// This file is modified from the ficus (https://github.com/vpisarev/ficus/blob/master/lib/NN/OpNN.fx). +// Here is the original license: +/* + This file is a part of ficus language project. + See ficus/LICENSE for the licensing terms +*/ + +#include "../../precomp.hpp" +#include "softmax.hpp" + +namespace cv { namespace dnn { + +void softmax(Mat &dst, const Mat &src, int axis, int axisBias, int axisStep){ + CV_Assert(src.type() == CV_32F); + CV_Assert(src.isContinuous() && dst.isContinuous()); + CV_Assert(src.size == dst.size); + axis = normalize_axis(axis, src.dims); + + size_t outerSize = src.total(0, axis), + innerSize = src.total(axis + 1); + + const float *srcPtr = src.ptr(); + float *dstPtr = dst.ptr(); + + size_t outerStep = src.total(axis); + size_t cnStep = src.total(axis + 1); + + // multi-threads + size_t totalTasks = outerSize * innerSize; + double nstripes = (double) totalTasks / 1024.0; + // make the channel axis to be multiple of 8 + size_t channelAxis = (axisStep + 7) & -8; + +#if (CV_SIMD || CV_SIMD_SCALABLE) + const int nlanes = VTraits::vlanes(); + // the number of redundant dimension + size_t redundantDim = nlanes - axisStep % nlanes; +#endif + + parallel_for_(Range(0, (int) totalTasks), [&](const Range &range) { + AutoBuffer axisBuf_(channelAxis); + float *axisBuf = axisBuf_.data(); + + for (size_t i = range.start; i < range.end; i++) { + size_t outerDim = i / innerSize; + size_t innerDim = i % innerSize; + size_t srcOffset = outerDim * outerStep + innerDim; + // copy data from src to buf along axis, since the data may not be continuous + for (size_t cnDim = 0; cnDim < axisStep; cnDim++) + axisBuf[cnDim] = srcPtr[srcOffset + (cnDim + axisBias) * cnStep]; + + float s = 0.f; +#if (CV_SIMD || CV_SIMD_SCALABLE) + // make the value of the redundant dimension to be -FLT_MAX + if (redundantDim != nlanes) { + for (size_t j = axisStep; j < axisStep + redundantDim; j++) + axisBuf[j] = -FLT_MAX; + } + // calculate the max value along the axis + v_float32 vmax = vx_load(axisBuf); + for (size_t cnDim = nlanes; cnDim < axisStep; cnDim += nlanes) { + v_float32 val = vx_load(axisBuf + cnDim); + vmax = v_max(vmax, val); + } + float maxVal = v_reduce_max(vmax); + + // calculate the exp value along the axis + v_float32 vs = vx_setzero_f32(); + vmax = vx_setall_f32(maxVal); + // initialize vexp constant + v_float32 _vexp_lo = vx_setall_f32(-88.3762626647949f); + v_float32 _vexp_hi = vx_setall_f32(88.3762626647949f); + v_float32 _vexp_half = vx_setall_f32(0.5f); + v_float32 _vexp_one = vx_setall_f32(1.f); + v_float32 _vexp_LOG2EF = vx_setall_f32(1.44269504088896341f); + v_float32 _vexp_C1 = vx_setall_f32(-0.693359375f); + v_float32 _vexp_C2 = vx_setall_f32(2.12194440e-4f); + v_float32 _vexp_p0 = vx_setall_f32(1.9875691500E-4f); + v_float32 _vexp_p1 = vx_setall_f32(1.3981999507E-3f); + v_float32 _vexp_p2 = vx_setall_f32(8.3334519073E-3f); + v_float32 _vexp_p3 = vx_setall_f32(4.1665795894E-2f); + v_float32 _vexp_p4 = vx_setall_f32(1.6666665459E-1f); + v_float32 _vexp_p5 = vx_setall_f32(5.0000001201E-1f); + // initialize temp vectors for vexp + v_float32 val, _vexp_, _vexp_x, _vexp_y, _vexp_z; + v_int32 _vexp_mm; + + // calculate and sum all data along axis + for (size_t cnDim = 0; cnDim < axisStep; cnDim += nlanes) { + val = vx_load(axisBuf + cnDim); + val = v_sub(val, vmax); + + // compute vexp of val + _vexp_x = v_min(val, _vexp_hi); + _vexp_x = v_max(_vexp_x, _vexp_lo); + _vexp_ = v_fma(_vexp_x, _vexp_LOG2EF, _vexp_half); + _vexp_mm = v_floor(_vexp_); + _vexp_ = v_cvt_f32(_vexp_mm); + _vexp_mm = v_add(_vexp_mm, vx_setall_s32(0x7f)); + _vexp_mm = v_shl(_vexp_mm, 23); + _vexp_x = v_fma(_vexp_, _vexp_C1, _vexp_x); + _vexp_x = v_fma(_vexp_, _vexp_C2, _vexp_x); + _vexp_z = v_mul(_vexp_x, _vexp_x); + _vexp_y = v_fma(_vexp_x, _vexp_p0, _vexp_p1); + _vexp_y = v_fma(_vexp_y, _vexp_x, _vexp_p2); + _vexp_y = v_fma(_vexp_y, _vexp_x, _vexp_p3); + _vexp_y = v_fma(_vexp_y, _vexp_x, _vexp_p4); + _vexp_y = v_fma(_vexp_y, _vexp_x, _vexp_p5); + _vexp_y = v_fma(_vexp_y, _vexp_z, _vexp_x); + _vexp_y = v_add(_vexp_y, _vexp_one); + val = v_mul(_vexp_y, v_reinterpret_as_f32(_vexp_mm)); + + vs = v_add(vs, val); + v_store(axisBuf + cnDim, val); + } + + s = v_reduce_sum(vs); + // subtract the value of the redundant dimension + if (redundantDim != nlanes) { + float _val[VTraits::max_nlanes]; + v_store(_val, val); + for (size_t j = nlanes - redundantDim; j < nlanes; j++) + s -= _val[j]; + } +#else + float maxVal = axisBuf[0]; + for (size_t cnDim = 1; cnDim < axisStep; cnDim++) { + maxVal = std::max(maxVal, axisBuf[cnDim]); + } + for (size_t j = 0; j < axisStep; j++) { + axisBuf[j] = expf(axisBuf[j] - maxVal); + s += axisBuf[j]; + } +#endif + s = 1.f / s; + + // copy back the result to src + for (size_t cnDim = 0; cnDim < axisStep; cnDim++) + dstPtr[srcOffset + (cnDim + axisBias) * cnStep] = axisBuf[cnDim] * s; + } + }, nstripes); +} + +void softmax(Mat &dst, const Mat &src, int axis) { + softmax(dst, src, axis, 0, src.size[axis]); +} + +void logSoftmax(Mat &dst, const Mat &src, int axis) { + softmax(dst, src, axis); + log(dst, dst); +} + +}} // cv::dnn diff --git a/modules/dnn/src/layers/cpu_kernels/softmax.hpp b/modules/dnn/src/layers/cpu_kernels/softmax.hpp new file mode 100644 index 0000000000..19a89fa878 --- /dev/null +++ b/modules/dnn/src/layers/cpu_kernels/softmax.hpp @@ -0,0 +1,28 @@ +// This file is part of OpenCV project. +// It is subject to the license terms in the LICENSE file found in the top-level directory +// of this distribution and at http://opencv.org/license.html. + +// This file is modified from the ficus (https://github.com/vpisarev/ficus/blob/master/lib/NN/OpNN.fx). +// Here is the original license: +/* + This file is a part of ficus language project. + See ficus/LICENSE for the licensing terms +*/ + +#ifndef OPENCV_DNN_SOFTMAX_HPP +#define OPENCV_DNN_SOFTMAX_HPP + +#include "opencv2/core/hal/intrin.hpp" +#include + +namespace cv { namespace dnn { + +void softmax(Mat &dst, const Mat &src, int axis, int axisBias, int axisStep); + +void softmax(Mat &dst, const Mat &src, int axis); + +void logSoftmax(Mat &dst, const Mat &src, int axis); + +}} // cv::dnn + +#endif // OPENCV_DNN_SOFTMAX_HPP diff --git a/modules/dnn/src/layers/einsum_layer.cpp b/modules/dnn/src/layers/einsum_layer.cpp index 3dee2057ab..c7f9aaca06 100644 --- a/modules/dnn/src/layers/einsum_layer.cpp +++ b/modules/dnn/src/layers/einsum_layer.cpp @@ -6,6 +6,7 @@ #include #include "../precomp.hpp" #include "layers_common.hpp" +#include "cpu_kernels/fast_gemm.hpp" namespace cv { @@ -32,111 +33,6 @@ static bool IsTransposeReshapeForEinsum(const std::vector& perm, return true; } -static Mat batchwiseMatMul( - const Mat& input1, - const MatShape& input1ShapeOverride, - const Mat& input2, - const MatShape& input2ShapeOverride) -{ - // Sanity checks before the actual MatMul - CV_CheckType(input1.type(), input2.type(), "Data types of the inputs must match for MatMul"); - CV_CheckEQ(input1ShapeOverride.size(), (size_t) 3, "Only 1 batch dimension is allowed for MatMul"); - CV_CheckEQ(input2ShapeOverride.size(), (size_t) 3, "Only 1 batch dimension is allowed for MatMul"); - CV_CheckEQ((size_t) input1ShapeOverride[0], (size_t) input2ShapeOverride[0], "Batch dimension should match for MatMul;"); - CV_CheckEQ((size_t) input1ShapeOverride[2], (size_t) input2ShapeOverride[1], "Incompatible matrix dimensions for matMul"); - - size_t batches = input1ShapeOverride[0]; - size_t M = input1ShapeOverride[1]; - size_t K = input1ShapeOverride[2]; - size_t N = input2ShapeOverride[2]; - - std::vector output; - if (batches > 1) - { - Mat reshapedInput1 = input1; - Mat reshapedInput2 = input2; - - // input1 should of size MxK - // check if input1 needs reshape, if need reshape - if (input1.size[0] != M || input1.size[1] != K) - { - int shape[] = {static_cast(batches), static_cast(M), static_cast(K)}; - reshapedInput1 = input1.reshape(1, 3, shape); - } - - // input2 should be of size KxN - // check if input2 needs reshape, if needs reshape - if (input2.size[0] != K || input2.size[1] != N) - { - int shape[] = {static_cast(batches), static_cast(K), static_cast(N)}; - reshapedInput2 = input2.reshape(1, 3, shape); - } - - for (size_t i=0; i < batches; i++) - { - std::vector ranges1 = {cv::Range(i, i+1)}; - for (int j = 1; j < reshapedInput1.dims; j++) - ranges1.emplace_back(cv::Range::all()); - - Mat part1 = reshapedInput1(ranges1); - int shape[] = {static_cast(M), static_cast(K)}; - part1 = part1.reshape(1, sizeof(shape)/sizeof(shape[0]), shape); - - std::vector ranges2 = {cv::Range(i, i+1)}; - for (int j = 1; j < reshapedInput2.dims; j++) - ranges2.emplace_back(cv::Range::all()); - - Mat part2 = reshapedInput2(ranges2); - int shape2[] = {static_cast(K), static_cast(N)}; - part2 = part2.reshape(1, sizeof(shape2)/sizeof(shape2[0]), shape2); - - Mat tmp_output; - cv::gemm(part1, part2, 1.0, cv::Mat(), 1.0, tmp_output); - int newShape[] = {1, static_cast(M), static_cast(N)}; - tmp_output = tmp_output.reshape(1, sizeof(newShape)/sizeof(newShape[0]), newShape); - - output.emplace_back(tmp_output); - } - - } else { - - Mat reshapedInput1 = input1; - Mat reshapedInput2 = input2; - - // input1 should of size MxK - // check if input1 needs reshape, if need reshape - if (input1.dims > 2 || input1.size[0] != M || (input1.dims > 1 && input1.size[1] != K) || input1.dims == 1) - { - int shape[] = {static_cast(M), static_cast(K)}; - reshapedInput1 = input1.reshape(1, 2, shape); - } - - // input2 should be of size KxN - // check if input2 needs reshape, if needs reshape - if (input2.dims > 2 || input2.size[0] != K || (input2.dims > 1 && input2.size[1] != N) || input2.dims == 1) - { - int shape2[] = {static_cast(K), static_cast(N)}; - reshapedInput2 = input2.reshape(1, 2, shape2); - } - - Mat tmp_output; - cv::gemm(reshapedInput1, reshapedInput2, 1.0, cv::Mat(), 1.0, tmp_output); - - int newShape[] = {1, static_cast(M), static_cast(N)}; - tmp_output = tmp_output.reshape(1, sizeof(newShape)/sizeof(newShape[0]), newShape); - output.emplace_back(tmp_output); - - } - - int outputDim[] = {static_cast(output.size()), static_cast(M), static_cast(N)}; - Mat output_buffer = Mat::zeros(3, outputDim, CV_32F); - - for (size_t i = 0; i < output.size(); i++) { - Mat output_slice = output_buffer.row(i); - output[i].copyTo(output_slice); - } - return output_buffer; -}; static Mat Transpose( const Mat& input, @@ -452,6 +348,8 @@ public: // The number of dimensions that are encompassed by an "ellipsis" - "...". size_t numOfEllipsisDims = 0; + // Backend for fastgemm + FastGemmOpt opt; void parseEquation(String equation); void processEquation(const std::vector& inputs); @@ -469,7 +367,12 @@ public: const MatShape& reduceDims, bool isFinalPair ); - + Mat batchwiseMatMul( + const Mat& input1, + const MatShape& input1ShapeOverride, + const Mat& input2, + const MatShape& input2ShapeOverride + ); // constructor LayerEinsumImpl(const LayerParams& params) @@ -491,6 +394,7 @@ public: einsumInpShapes.emplace_back(shape); } + opt.init(); // Maintains a mapping between input indices and their corresponding subscript labels for each input inputSubscriptIndices.reserve(numInputs); @@ -1389,6 +1293,112 @@ Mat LayerEinsumImpl::pairwiseOperandProcess( return output; }; +Mat LayerEinsumImpl::batchwiseMatMul( + const Mat& input1, + const MatShape& input1ShapeOverride, + const Mat& input2, + const MatShape& input2ShapeOverride) +{ + + // Sanity checks before the actual MatMul + CV_CheckType(input1.type(), input2.type(), "Data types of the inputs must match for MatMul"); + CV_CheckEQ(input1ShapeOverride.size(), (size_t) 3, "Only 1 batch dimension is allowed for MatMul"); + CV_CheckEQ(input2ShapeOverride.size(), (size_t) 3, "Only 1 batch dimension is allowed for MatMul"); + CV_CheckEQ((size_t) input1ShapeOverride[0], (size_t) input2ShapeOverride[0], "Batch dimension should match for MatMul;"); + CV_CheckEQ((size_t) input1ShapeOverride[2], (size_t) input2ShapeOverride[1], "Incompatible matrix dimensions for matMul"); + + int batches = input1ShapeOverride[0]; + int M = input1ShapeOverride[1]; + int K = input1ShapeOverride[2]; + int N = input2ShapeOverride[2]; + + std::vector output; + if (batches > 1) + { + Mat reshapedInput1 = input1; + Mat reshapedInput2 = input2; + + // input1 should of size MxK + // check if input1 needs reshape, if need reshape + if (input1.size[0] != M || input1.size[1] != K) + { + int shape[] = {batches, M, K}; + reshapedInput1 = input1.reshape(1, 3, shape); + } + + // input2 should be of size KxN + // check if input2 needs reshape, if needs reshape + if (input2.size[0] != K || input2.size[1] != N) + { + int shape[] = {batches, K, N}; + reshapedInput2 = input2.reshape(1, 3, shape); + } + + for (size_t i=0; i < batches; i++) + { + std::vector ranges1 = {cv::Range(i, i+1)}; + for (int j = 1; j < reshapedInput1.dims; j++) + ranges1.emplace_back(cv::Range::all()); + + Mat part1 = reshapedInput1(ranges1); + int shape[] = {M, K}; + part1 = part1.reshape(1, sizeof(shape)/sizeof(shape[0]), shape); + + std::vector ranges2 = {cv::Range(i, i+1)}; + for (int j = 1; j < reshapedInput2.dims; j++) + ranges2.emplace_back(cv::Range::all()); + + Mat part2 = reshapedInput2(ranges2); + int shape2[] = {K, N}; + part2 = part2.reshape(1, sizeof(shape2)/sizeof(shape2[0]), shape2); + + Mat tmp_output(M, N, part1.type()); + fastGemm(false, false, 1.0, part1, part2, 0.0, tmp_output, opt); + int newShape[] = {1, M, N}; + tmp_output = tmp_output.reshape(1, sizeof(newShape)/sizeof(newShape[0]), newShape); + + output.emplace_back(tmp_output); + } + + } else { + + Mat reshapedInput1 = input1; + Mat reshapedInput2 = input2; + + // input1 should of size MxK + // check if input1 needs reshape, if need reshape + if (input1.dims > 2 || input1.size[0] != M || input1.size[1] != K) + { + int shape[] = {M, K}; + reshapedInput1 = input1.reshape(1, 2, shape); + } + + // input2 should be of size KxN + // check if input2 needs reshape, if needs reshape + if (input2.dims > 2 || input2.size[0] != K || input2.size[1] != N) + { + int shape2[] = {K, N}; + reshapedInput2 = input2.reshape(1, 2, shape2); + } + + Mat tmp_output(M, N, reshapedInput1.type()); + fastGemm(false, false, 1.0, reshapedInput1, reshapedInput2, 0.0, tmp_output, opt); + + int newShape[] = {1, M, N}; + tmp_output = tmp_output.reshape(1, sizeof(newShape)/sizeof(newShape[0]), newShape); + output.emplace_back(tmp_output); + + } + + int outputDim[] = {static_cast(output.size()), M, N}; + Mat output_buffer = Mat::zeros(3, outputDim, CV_32F); + + for (size_t i = 0; i < output.size(); i++) { + Mat output_slice = output_buffer.row(i); + output[i].copyTo(output_slice); + } + return output_buffer; +}; Ptr EinsumLayer::create(const LayerParams& params) { return makePtr(params); diff --git a/modules/dnn/src/layers/elementwise_layers.cpp b/modules/dnn/src/layers/elementwise_layers.cpp index 3d96a7bfc6..642d78e101 100644 --- a/modules/dnn/src/layers/elementwise_layers.cpp +++ b/modules/dnn/src/layers/elementwise_layers.cpp @@ -1712,7 +1712,9 @@ struct HardSwishFunctor : public BaseDefaultFunctor bool supportBackend(int backendId, int) { - return backendId == DNN_BACKEND_OPENCV || backendId == DNN_BACKEND_CUDA; + return backendId == DNN_BACKEND_OPENCV || + backendId == DNN_BACKEND_CUDA || + backendId == DNN_BACKEND_CANN; } inline float calculate(float x) const @@ -1727,6 +1729,27 @@ struct HardSwishFunctor : public BaseDefaultFunctor } #endif +#ifdef HAVE_CANN + Ptr initCannOp(const std::string& name, + const std::vector > &inputs, + const std::vector >& nodes) + { + auto x = inputs[0].dynamicCast(); + + auto op = std::make_shared(name); + + auto op_x = nodes[0].dynamicCast()->getOp(); + op->set_input_x_by_name(*op_x, x->name.c_str()); + auto x_desc = x->getTensorDesc(); + op->update_input_desc_x(*x_desc); + + auto output_desc = std::make_shared(ge::Shape(), ge::FORMAT_NCHW, ge::DT_FLOAT); + op->update_output_desc_y(*output_desc); + + return Ptr(new CannBackendNode(op)); + } +#endif + int64 getFLOPSPerElement() const { return 1; } }; diff --git a/modules/dnn/src/layers/gemm_layer.cpp b/modules/dnn/src/layers/gemm_layer.cpp index a553f97568..821700c83e 100644 --- a/modules/dnn/src/layers/gemm_layer.cpp +++ b/modules/dnn/src/layers/gemm_layer.cpp @@ -211,7 +211,7 @@ public: CV_CheckGT(packed_B.size(), static_cast(0), "DNN/Gemm: constant B is not pre-packed"); fastGemm(trans_a, M, N, K, alpha, A.ptr(), na, packed_B.data(), 1.f, Y.ptr(), N, opt); } else { - fastGemmBatched(trans_a, trans_b, alpha, A, inputs[1], 1.f, Y, opt); + fastGemmBatch(trans_a, trans_b, alpha, A, inputs[1], 1.f, Y, opt); } } @@ -274,6 +274,7 @@ public: op->update_input_desc_bias(*(op_const_C->getTensorDesc())); // set outputs + auto output_desc = std::make_shared(ge::Shape(), ge::FORMAT_NCHW, ge::DT_FLOAT); op->update_output_desc_y(*output_desc); return Ptr(new CannBackendNode(op)); } diff --git a/modules/dnn/src/layers/instance_norm_layer.cpp b/modules/dnn/src/layers/instance_norm_layer.cpp new file mode 100644 index 0000000000..b43e9bbb7a --- /dev/null +++ b/modules/dnn/src/layers/instance_norm_layer.cpp @@ -0,0 +1,280 @@ +// 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 "../precomp.hpp" +#include +#include "./cpu_kernels/fast_norm.hpp" + +// CANN backend +#include "../op_cann.hpp" + +// OpenVINO backend +#include "../op_inf_engine.hpp" +#include "../ie_ngraph.hpp" + +// CUDA backend +#include "../op_cuda.hpp" +#ifdef HAVE_CUDA +#include "../cuda4dnn/primitives/instance_norm.hpp" +using namespace cv::dnn::cuda4dnn; +#endif + +// OpenCL backend +#ifdef HAVE_OPENCL +#include "../ocl4dnn/include/math_functions.hpp" +#include "opencl_kernels_dnn.hpp" +#endif + +namespace cv { namespace dnn { + +// https://github.com/onnx/onnx/blob/main/docs/Operators.md#InstanceNormalization +class InstanceNormLayerImpl CV_FINAL : public InstanceNormLayer { +public: + InstanceNormLayerImpl(const LayerParams ¶ms) { + setParamsFrom(params); + + epsilon = params.get("epsilon", 1e-5); + } + + virtual bool supportBackend(int backendId) CV_OVERRIDE { +#ifdef HAVE_INF_ENGINE + if (backendId == DNN_BACKEND_INFERENCE_ENGINE_NGRAPH) + return true; +#endif + return backendId == DNN_BACKEND_OPENCV || + backendId == DNN_BACKEND_CUDA; + // backendId == DNN_BACKEND_CANN; // not supported due to 1d mat shape issue + } + + bool getMemoryShapes(const std::vector &inputs, + const int requiredOutputs, + std::vector &outputs, + std::vector &internals) const CV_OVERRIDE { + const auto &input = inputs[0]; + const auto &scale = inputs[1]; + const auto &bias = inputs[2]; + CV_CheckGE(input.size(), static_cast(3), "DNN/InstanceNorm: input dimension >= 3 is required"); + + int C = input[1]; + int scale_dim = std::accumulate(scale.begin(), scale.end(), 1, std::multiplies()); + CV_CheckEQ(scale_dim, C, "DNN/InstanceNorm: scale must be a 1d tensor and match the channel of input"); + int bias_dim = std::accumulate(bias.begin(), bias.end(), 1, std::multiplies()); + CV_CheckEQ(bias_dim, C, "DNN/InstanceNorm: bias must be a 1d tensor and match the channel of input"); + + outputs.assign(1, inputs[0]); + return false; + } + + void forward(InputArrayOfArrays inputs_arr, OutputArrayOfArrays outputs_arr, OutputArrayOfArrays internals_arr) CV_OVERRIDE { + CV_TRACE_FUNCTION(); + CV_TRACE_ARG_VALUE(name, "name", name.c_str()); + + CV_OCL_RUN(IS_DNN_OPENCL_TARGET(preferableTarget), + forward_ocl(inputs_arr, outputs_arr, internals_arr)) + + if (inputs_arr.depth() == CV_16S) + { + forward_fallback(inputs_arr, outputs_arr, internals_arr); + return; + } + + std::vector inputs, outputs; + inputs_arr.getMatVector(inputs); + outputs_arr.getMatVector(outputs); + + const auto &input = inputs[0]; + const auto &scale = inputs[1]; + const auto &bias = inputs[2]; + + fastNormChannel(input, scale, bias, outputs[0], epsilon); + } + +#ifdef HAVE_OPENCL + bool forward_ocl(InputArrayOfArrays inputs_, OutputArrayOfArrays outputs_, OutputArrayOfArrays internals_) { + std::vector inputs; + std::vector outputs; + + inputs_.getUMatVector(inputs); + outputs_.getUMatVector(outputs); + + const auto &input = inputs[0], &scale = inputs[1], &bias = inputs[2]; + auto &output = outputs[0]; + + const auto input_shape = shape(input); + size_t N = input_shape[0], C = input_shape[1], + loops = N * C, norm_size = static_cast(total(input_shape, 2)); + float inv_norm_size = 1.f / norm_size; + + // no fp16 support + if (input.depth() == CV_16S) { + return false; + } + + String base_opts = format(" -DT=float -DT4=float4 -Dconvert_T=convert_float4"); + + // Calculate mean + UMat one = UMat::ones(norm_size, 1, CV_32F); + UMat mean = UMat(loops, 1, CV_32F); + UMat mean_square = UMat(loops, 1, CV_32F); + UMat tmp = UMat(loops, norm_size, CV_32F); + bool ret = ocl4dnn::ocl4dnnGEMV(ocl4dnn::CblasNoTrans, loops, norm_size, inv_norm_size, + input, 0, one, 0, 0.f, mean, 0); + if (!ret) { + return false; + } + // Calculate mean_square + int num_vector = (norm_size % 8 == 0) ? 8 : ((norm_size % 4 == 0) ? 4 : 1); + size_t global[] = {loops, static_cast(norm_size / num_vector)}; + String build_opt = format(" -DNUM=%d", num_vector) + base_opts; + String mean_square_kernel_name = format("calc_mean%d", num_vector); + ocl::Kernel mean_square_kernel(mean_square_kernel_name.c_str(), ocl::dnn::mvn_oclsrc, build_opt + " -DKERNEL_MEAN"); + if (mean_square_kernel.empty()) { + return false; + } + mean_square_kernel.set(0, ocl::KernelArg::PtrReadOnly(input)); + mean_square_kernel.set(1, (int)loops); + mean_square_kernel.set(2, (int)norm_size); + mean_square_kernel.set(3, ocl::KernelArg::PtrReadOnly(mean)); + mean_square_kernel.set(4, ocl::KernelArg::PtrWriteOnly(tmp)); + ret = mean_square_kernel.run(2, global, NULL, false); + if (!ret) { + return false; + } + ret = ocl4dnn::ocl4dnnGEMV(ocl4dnn::CblasNoTrans, loops, norm_size, inv_norm_size, + tmp, 0, one, 0, 0.f, mean_square, 0); + if (!ret) { + return false; + } + // Calculate instance norm: output = scale * (x - mean) / sqrt(var + eps) + bias + String mvn_kernel_name = format("mvn%d", num_vector); + build_opt += " -DNORM_VARIANCE -DFUSE_BATCH_NORM -DKERNEL_MVN"; + ocl::Kernel mvn_kernel(mvn_kernel_name.c_str(), ocl::dnn::mvn_oclsrc, build_opt); + if (mvn_kernel.empty()) { + return false; + } + mvn_kernel.set(0, ocl::KernelArg::PtrReadOnly(input)); + mvn_kernel.set(1, (int)loops); + mvn_kernel.set(2, (int)norm_size); + mvn_kernel.set(3, (float)epsilon); + mvn_kernel.set(4, ocl::KernelArg::PtrReadOnly(mean)); + mvn_kernel.set(5, ocl::KernelArg::PtrReadOnly(mean_square)); + mvn_kernel.set(6, ocl::KernelArg::PtrReadOnly(scale)); + mvn_kernel.set(7, ocl::KernelArg::PtrReadOnly(bias)); + mvn_kernel.set(8, (int)C); + mvn_kernel.set(9, (float)0.f); + mvn_kernel.set(10, ocl::KernelArg::PtrWriteOnly(output)); + ret = mvn_kernel.run(2, global, NULL, false); + if (!ret) { + return false; + } + + return true; + } +#endif + +#ifdef HAVE_CANN + virtual Ptr initCann(const std::vector > &inputs, + const std::vector > &outputs, + const std::vector >& nodes) CV_OVERRIDE { + auto input_tensor_wrapper = inputs[0].dynamicCast(); + auto input_tensor_desc = input_tensor_wrapper->getTensorDesc(); + + auto scale_tensor_wrapper = inputs[1].dynamicCast(); + auto scale_tensor_desc = scale_tensor_wrapper->getTensorDesc(); + + auto bias_tensor_wrapper = inputs[2].dynamicCast(); + auto bias_tensor_desc = bias_tensor_wrapper->getTensorDesc(); + + auto last_node = nodes[0].dynamicCast()->getOp(); + auto scale_node = nodes[1].dynamicCast()->getOp(); + auto bias_node = nodes[2].dynamicCast()->getOp(); + + auto op = std::make_shared(name); + + // set attrs + op->set_attr_epsilon(epsilon); + + // set inputs + // set inputs : x + op->set_input_x_by_name(*last_node, input_tensor_wrapper->name.c_str()); + op->update_input_desc_x(*input_tensor_desc); + // set inputs : gamma + op->set_input_gamma_by_name((*scale_node), scale_tensor_wrapper->name.c_str()); + op->update_input_desc_gamma(*scale_tensor_desc); + // set inputs : beta + op->set_input_beta_by_name(*bias_node, bias_tensor_wrapper->name.c_str()); + op->update_input_desc_beta(*bias_tensor_desc); + + // set outputs + auto output_desc_y = std::make_shared(ge::Shape(), ge::FORMAT_NCHW, ge::DT_FLOAT); + op->update_output_desc_y(*output_desc_y); + auto output_desc_mean = std::make_shared(ge::Shape(), ge::FORMAT_NCHW, ge::DT_FLOAT); + op->update_output_desc_mean(*output_desc_mean); + auto output_desc_var = std::make_shared(ge::Shape(), ge::FORMAT_NCHW, ge::DT_FLOAT); + op->update_output_desc_variance(*output_desc_var); + + return Ptr(new CannBackendNode(op)); + } +#endif // HAVE_CANN + +#ifdef HAVE_DNN_NGRAPH + virtual Ptr initNgraph(const std::vector >& inputs, + const std::vector >& 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 + + auto ieInpNode = nodes[0].dynamicCast()->node; + const auto &input_shape = ieInpNode.get_shape(); + std::shared_ptr mvn, result; + + // mvn +#if INF_ENGINE_VER_MAJOR_LE(INF_ENGINE_RELEASE_2021_2) + // https://docs.openvino.ai/2021.4/api/ngraph_python_api/_autosummary/ngraph.opset3.mvn.html?highlight=mvn#ngraph.opset3.mvn + bool across_channels = false; + bool normalize_variance = true; + mvn = std::make_shared(ieInpNode, across_channels, normalize_variance, epsilon); +#else + // https://docs.openvino.ai/2023.1/openvino_docs_ops_normalization_MVN_6.html + std::vector axes_v(input_shape.size() - 2); + std::iota(axes_v.begin(), axes_v.end(), 2); // {2, 3, ...} for nd input tensor, n>=3 + auto axes = std::make_shared(ngraph::element::i64, ngraph::Shape{axes_v.size()}, axes_v.data()); + bool normalize_variance = true; + mvn = std::make_shared(ieInpNode, axes, normalize_variance, epsilon, ngraph::op::MVNEpsMode::INSIDE_SQRT); +#endif + + // instance norm = scale * mvn + bias + auto scale = nodes[1].dynamicCast()->node; + std::vector shared_shape_v(input_shape.size(), 1); + shared_shape_v[1] = -1; + auto shared_shape = std::make_shared(ngraph::element::i64, ngraph::Shape{shared_shape_v.size()}, shared_shape_v.data()); + scale = std::make_shared(scale, shared_shape, true); + result = std::make_shared(mvn, scale); + auto bias = nodes[2].dynamicCast()->node; + bias = std::make_shared(bias, shared_shape, true); + result = std::make_shared(result, bias); + + return Ptr(new InfEngineNgraphNode(result)); + } +#endif // HAVE_DNN_NGRAPH + +#ifdef HAVE_CUDA + Ptr initCUDA(void *context_, + const std::vector>& inputs, + const std::vector>& outputs) override { + auto context = reinterpret_cast(context_); + + auto input_wrapper = inputs[0].dynamicCast(); + auto input_shape = input_wrapper->getShape(); + size_t loops = static_cast(total(input_shape, 0, 2)); + + return make_cuda_node(preferableTarget, std::move(context->stream), epsilon, loops); + } +#endif // HAVE_CUDA + +}; + +Ptr InstanceNormLayer::create(const LayerParams ¶ms) { + return Ptr(new InstanceNormLayerImpl(params)); +} + +}} // cv::dnn diff --git a/modules/dnn/src/layers/layer_norm.cpp b/modules/dnn/src/layers/layer_norm.cpp index 9c16d19e41..f3d2667a0a 100644 --- a/modules/dnn/src/layers/layer_norm.cpp +++ b/modules/dnn/src/layers/layer_norm.cpp @@ -6,8 +6,29 @@ #include "layers_common.hpp" #include "cpu_kernels/fast_norm.hpp" +// CANN backend +#include "../op_cann.hpp" + +// OpenVINO backend +#include "../op_inf_engine.hpp" +#include "../ie_ngraph.hpp" + +// CUDA backend +#include "../op_cuda.hpp" +#ifdef HAVE_CUDA +#include "../cuda4dnn/primitives/layer_norm.hpp" +using namespace cv::dnn::cuda4dnn; +#endif + +// OpenCL backend +#ifdef HAVE_OPENCL +#include "../ocl4dnn/include/math_functions.hpp" +#include "opencl_kernels_dnn.hpp" +#endif + namespace cv { namespace dnn { +// https://github.com/onnx/onnx/blob/main/docs/Operators.md#LayerNormalization class LayerNormLayerImpl CV_FINAL : public LayerNormLayer { public: @@ -22,7 +43,13 @@ public: virtual bool supportBackend(int backendId) CV_OVERRIDE { - return backendId == DNN_BACKEND_OPENCV; +#ifdef HAVE_INF_ENGINE + if (backendId == DNN_BACKEND_INFERENCE_ENGINE_NGRAPH) + return true; +#endif + return backendId == DNN_BACKEND_OPENCV || + backendId == DNN_BACKEND_CUDA || + (backendId == DNN_BACKEND_CANN && axis != -1); // axis=-1 not supported due to 1d mat shape problem } virtual bool getMemoryShapes(const std::vector &inputs, @@ -69,6 +96,9 @@ public: CV_TRACE_FUNCTION(); CV_TRACE_ARG_VALUE(name, "name", name.c_str()); + CV_OCL_RUN(IS_DNN_OPENCL_TARGET(preferableTarget), + forward_ocl(inputs_arr, outputs_arr, internals_arr)) + if (inputs_arr.depth() == CV_16S) { forward_fallback(inputs_arr, outputs_arr, internals_arr); @@ -90,6 +120,205 @@ public: fastNorm(input, scale, output, epsilon, static_cast(axis)); } } + +#ifdef HAVE_OPENCL + bool forward_ocl(InputArrayOfArrays inputs_, OutputArrayOfArrays outputs_, OutputArrayOfArrays internals_) { + std::vector inputs; + std::vector outputs; + + inputs_.getUMatVector(inputs); + outputs_.getUMatVector(outputs); + + const auto &input = inputs[0], &scale = inputs[1]; // &bias = inputs[2]; // bias is optional + auto &output = outputs[0]; + + const auto input_shape = shape(input); + size_t loops = static_cast(total(input_shape, 0, axis)), + norm_size = static_cast(total(input_shape, axis)); + float inv_norm_size = 1.f / norm_size; + + const auto &bias = inputs.size() == 3 ? inputs[2] : UMat::zeros(norm_size, 1, CV_32F); + + // no fp16 support + if (input.depth() == CV_16S) { + return false; + } + + String base_opts = format(" -DT=float -DT4=float4 -Dconvert_T=convert_float4"); + + // Calculate mean + UMat one = UMat::ones(norm_size, 1, CV_32F); + UMat mean = UMat(loops, 1, CV_32F); + UMat mean_square = UMat(loops, 1, CV_32F); + UMat tmp = UMat(loops, norm_size, CV_32F); + bool ret = ocl4dnn::ocl4dnnGEMV(ocl4dnn::CblasNoTrans, loops, norm_size, inv_norm_size, + input, 0, one, 0, 0.f, mean, 0); + if (!ret) { + return false; + } + // Calculate mean_square + int num_vector = (norm_size % 8 == 0) ? 8 : ((norm_size % 4 == 0) ? 4 : 1); + size_t global[] = {loops, static_cast(norm_size / num_vector)}; + String build_opt = format(" -DNUM=%d", num_vector) + base_opts; + String mean_square_kernel_name = format("calc_mean%d", num_vector); + ocl::Kernel mean_square_kernel(mean_square_kernel_name.c_str(), ocl::dnn::mvn_oclsrc, build_opt + " -DKERNEL_MEAN"); + if (mean_square_kernel.empty()) { + return false; + } + mean_square_kernel.set(0, ocl::KernelArg::PtrReadOnly(input)); + mean_square_kernel.set(1, (int)loops); + mean_square_kernel.set(2, (int)norm_size); + mean_square_kernel.set(3, ocl::KernelArg::PtrReadOnly(mean)); + mean_square_kernel.set(4, ocl::KernelArg::PtrWriteOnly(tmp)); + ret = mean_square_kernel.run(2, global, NULL, false); + if (!ret) { + return false; + } + ret = ocl4dnn::ocl4dnnGEMV(ocl4dnn::CblasNoTrans, loops, norm_size, inv_norm_size, + tmp, 0, one, 0, 0.f, mean_square, 0); + if (!ret) { + return false; + } + // Calculate instance norm: output = scale * (x - mean) / sqrt(var + eps) + bias + String mvn_kernel_name = format("mvn%d", num_vector); + build_opt += " -DNORM_VARIANCE -DLAYER_NORM -DKERNEL_MVN"; + ocl::Kernel mvn_kernel(mvn_kernel_name.c_str(), ocl::dnn::mvn_oclsrc, build_opt); + if (mvn_kernel.empty()) { + return false; + } + mvn_kernel.set(0, ocl::KernelArg::PtrReadOnly(input)); + mvn_kernel.set(1, (int)loops); + mvn_kernel.set(2, (int)norm_size); + mvn_kernel.set(3, (float)epsilon); + mvn_kernel.set(4, ocl::KernelArg::PtrReadOnly(mean)); + mvn_kernel.set(5, ocl::KernelArg::PtrReadOnly(mean_square)); + mvn_kernel.set(6, ocl::KernelArg::PtrReadOnly(scale)); + mvn_kernel.set(7, ocl::KernelArg::PtrReadOnly(bias)); + mvn_kernel.set(8, (int)1); + mvn_kernel.set(9, (float)0.f); + mvn_kernel.set(10, ocl::KernelArg::PtrWriteOnly(output)); + ret = mvn_kernel.run(2, global, NULL, false); + if (!ret) { + return false; + } + + return true; + } +#endif + +#ifdef HAVE_CANN + virtual Ptr initCann(const std::vector > &inputs, + const std::vector > &outputs, + const std::vector >& nodes) CV_OVERRIDE { + CV_CheckEQ(inputs.size(), static_cast(3), "LayerNorm/CANN: requires three input wrappers"); + CV_CheckEQ(nodes.size(), static_cast(3), "LayerNorm/CANN: requires three input nodes"); + + auto input_tensor_wrapper = inputs[0].dynamicCast(); + auto input_tensor_desc = input_tensor_wrapper->getTensorDesc(); + + CV_CheckNE(axis, static_cast(input_tensor_desc->GetShape().GetDimNum() - 1), "LayerNorm: CANN does not support axis set as last axis due to 1D mat compatibility issue"); + + auto scale_tensor_wrapper = inputs[1].dynamicCast(); + auto scale_tensor_desc = scale_tensor_wrapper->getTensorDesc(); + + auto bias_tensor_wrapper = inputs[2].dynamicCast(); + auto bias_tensor_desc = bias_tensor_wrapper->getTensorDesc(); + + auto last_node = nodes[0].dynamicCast()->getOp(); + auto scale_node = nodes[1].dynamicCast()->getOp(); + auto bias_node = nodes[2].dynamicCast()->getOp(); + + auto op = std::make_shared(name); + + // set attrs + op->set_attr_begin_norm_axis(axis); + op->set_attr_begin_params_axis(axis); + op->set_attr_epsilon(epsilon); + + // set inputs + // set inputs : x + op->set_input_x_by_name(*last_node, input_tensor_wrapper->name.c_str()); + op->update_input_desc_x(*input_tensor_desc); + // set inputs : gamma + op->set_input_gamma_by_name(*scale_node, scale_tensor_wrapper->name.c_str()); + op->update_input_desc_gamma(*scale_tensor_desc); + // set inputs : beta + op->set_input_beta_by_name(*bias_node, bias_tensor_wrapper->name.c_str()); + op->update_input_desc_beta(*bias_tensor_desc); + + // set outputs + auto output_desc_y = std::make_shared(ge::Shape(), ge::FORMAT_NCHW, ge::DT_FLOAT); + op->update_output_desc_y(*output_desc_y); + auto output_desc_mean = std::make_shared(ge::Shape(), ge::FORMAT_NCHW, ge::DT_FLOAT); + op->update_output_desc_mean(*output_desc_mean); + auto output_desc_var = std::make_shared(ge::Shape(), ge::FORMAT_NCHW, ge::DT_FLOAT); + op->update_output_desc_variance(*output_desc_var); + + return Ptr(new CannBackendNode(op)); + } +#endif // HAVE_CANN + +#ifdef HAVE_DNN_NGRAPH + virtual Ptr initNgraph(const std::vector >& inputs, + const std::vector >& nodes) CV_OVERRIDE { + auto ieInpNode = nodes[0].dynamicCast()->node; + const auto &input_shape = ieInpNode.get_shape(); + std::shared_ptr mvn, result; + + // mvn +#if INF_ENGINE_VER_MAJOR_LE(INF_ENGINE_RELEASE_2021_2) + // https://docs.openvino.ai/2021.4/api/ngraph_python_api/_autosummary/ngraph.opset3.mvn.html?highlight=mvn#ngraph.opset3.mvn + bool across_channels = false; + bool normalize_variance = true; + mvn = std::make_shared(ieInpNode, across_channels, normalize_variance, epsilon); +#else + // https://docs.openvino.ai/2023.1/openvino_docs_ops_normalization_MVN_6.html + std::vector axes_v(input_shape.size() - axis); + std::iota(axes_v.begin(), axes_v.end(), axis); + auto axes = std::make_shared(ngraph::element::i64, ngraph::Shape{axes_v.size()}, axes_v.data()); + bool normalize_variance = true; + mvn = std::make_shared(ieInpNode, axes, normalize_variance, epsilon, ngraph::op::MVNEpsMode::INSIDE_SQRT); +#endif + + // layer norm = scale * mvn + bias + auto scale = nodes[1].dynamicCast()->node; + ngraph::Output bias; + if (nodes.size() == 3) { + bias = nodes[2].dynamicCast()->node; + } + if (axis == -1 || axis == input_shape.size() - 1) { // special case for 1D tensor (2D mat) + std::vector shared_shape_v(input_shape.size(), 1); + shared_shape_v.back() = -1; + auto shared_shape = std::make_shared(ngraph::element::i64, ngraph::Shape{shared_shape_v.size()}, shared_shape_v.data()); + scale = std::make_shared(scale, shared_shape, true); + if (nodes.size() == 3) { + bias = std::make_shared(bias, shared_shape, true); + } + } + + result = std::make_shared(mvn, scale); + if (nodes.size() == 3) { + result = std::make_shared(result, bias); + } + + return Ptr(new InfEngineNgraphNode(result)); + } +#endif // HAVE_DNN_NGRAPH + +#ifdef HAVE_CUDA + Ptr initCUDA(void *context_, + const std::vector>& inputs, + const std::vector>& outputs) override { + auto context = reinterpret_cast(context_); + + auto input_wrapper = inputs[0].dynamicCast(); + auto input_shape = input_wrapper->getShape(); + size_t loops = static_cast(total(input_shape, 0, axis)); + + return make_cuda_node(preferableTarget, std::move(context->stream), axis, epsilon, loops); + } +#endif // HAVE_CUDA + }; Ptr LayerNormLayer::create(const LayerParams& params) diff --git a/modules/dnn/src/layers/layers_common.cpp b/modules/dnn/src/layers/layers_common.cpp index 48401893f0..3b3a007b06 100644 --- a/modules/dnn/src/layers/layers_common.cpp +++ b/modules/dnn/src/layers/layers_common.cpp @@ -195,7 +195,7 @@ void getConvolutionKernelParams(const LayerParams ¶ms, std::vector& util::getStrideAndPadding(params, pads_begin, pads_end, strides, padMode, kernel.size()); util::getParameter(params, "dilation", "dilation", dilations, true, std::vector(kernel.size(), 1)); util::getParameter(params, "adj", "adj", adjust_pads, true, std::vector(kernel.size(), 0)); - useWinograd = params.get("use_winograd", true); + useWinograd = params.get("use_winograd", useWinograd); for (int i = 0; i < dilations.size(); i++) CV_Assert(dilations[i] > 0); diff --git a/modules/dnn/src/layers/matmul_layer.cpp b/modules/dnn/src/layers/matmul_layer.cpp new file mode 100644 index 0000000000..c6cea65d87 --- /dev/null +++ b/modules/dnn/src/layers/matmul_layer.cpp @@ -0,0 +1,326 @@ +// 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 "../precomp.hpp" + +#include +#include "cpu_kernels/fast_gemm.hpp" + +// OpenVINO backend +#include "../op_inf_engine.hpp" +#include "../ie_ngraph.hpp" + +// Vulkan backend +#include "../op_vkcom.hpp" + +// CUDA backend +#ifdef HAVE_CUDA +#include "../cuda4dnn/primitives/matmul_broadcast.hpp" +using namespace cv::dnn::cuda4dnn; +#endif + +// CANN backend +#include "../op_cann.hpp" + +namespace cv { namespace dnn { + +class MatMulLayerImpl CV_FINAL : public MatMulLayer { + public: + MatMulLayerImpl(const LayerParams& params) { + setParamsFrom(params); + + trans_a = params.get("transA", false); + trans_b = params.get("transB", false); + alpha = params.get("alpha", 1.f); + beta = params.get("beta", 1.f); + } + + virtual bool supportBackend(int backendId) CV_OVERRIDE { + return backendId == DNN_BACKEND_OPENCV || + backendId == DNN_BACKEND_INFERENCE_ENGINE_NGRAPH || + (backendId == DNN_BACKEND_VKCOM && haveVulkan() && !trans_a && !trans_b) || + backendId == DNN_BACKEND_CUDA || + backendId == DNN_BACKEND_CANN; + } + + virtual bool getMemoryShapes(const std::vector &inputs, + const int requiredOutputs, + std::vector &outputs, + std::vector &internals) const CV_OVERRIDE { + CV_CheckGE(inputs.size(), static_cast(1), "DNN/MatMul: one varible input at least"); + CV_CheckLE(inputs.size(), static_cast(2), "DNN/MatMul: two variable inputs at most"); + + const auto shape_A = inputs[0], shape_B = blobs.empty() ? inputs[1] : shape(blobs[0]); + CV_CheckGE(shape_A.size(), static_cast(2), "DNN/MatMul: invalid shape of input A"); + CV_CheckGE(shape_B.size(), static_cast(2), "DNN/MatMul: invalid shape of input B"); + + // Check legal matrix multiplication + int mA = shape_A[shape_A.size() - 2], nA = shape_A.back(); + int mB = shape_B[shape_B.size() - 2], nB = shape_B.back(); + int M = trans_a ? nA : mA; + int N = trans_b ? mB : nB; + int K_A = trans_a ? mA : nA; + int K_B = trans_b ? nB : mB; + CV_CheckEQ(K_A, K_B, "DNN/MatMul: invalid dimension K"); + + // Check legal broadcast. It is legal for sure if A and B are 2d, or one of them is 2d. + MatShape common_shape; + if (shape_A.size() != 2 || shape_B.size() != 2) { + const auto &shape_more_dims = shape_A.size() > shape_B.size() ? shape_A : shape_B; + const auto &shape_less_dims = shape_A.size() > shape_B.size() ? shape_B : shape_A; + size_t diff_dims = shape_more_dims.size() - shape_less_dims.size(); + common_shape = shape_more_dims; + for (size_t i = 0; i < shape_less_dims.size() - 2; i++) { + const auto dl = shape_less_dims[i], dm = shape_more_dims[i + diff_dims]; + if (dl != 1 && dm != 1 && dl != dm) { + CV_Error(Error::StsBadSize, format("DNN/MatMul: invalid shape for broadcasting, shape_A[%zu]=%d, shape_B[%zu]=%d\n", i, shape_less_dims[i], i, shape_more_dims[i + diff_dims])); + } + + if (dm == 1) { + common_shape[i + diff_dims] = dl; + } + } + common_shape[common_shape.size() - 2] = M; + common_shape[common_shape.size() - 1] = N; + } else { + common_shape.resize(2); + common_shape[0] = M; + common_shape[1] = N; + } + + outputs.assign(1, common_shape); + return false; + } + + virtual void finalize(InputArrayOfArrays inputs_arr, OutputArrayOfArrays outputs_arr) CV_OVERRIDE { + opt.init(); + + std::vector inputs, outputs; + inputs_arr.getMatVector(inputs); + outputs_arr.getMatVector(outputs); + + const auto A_shape = shape(inputs[0]), + B_shape = blobs.empty() ? shape(inputs[1]) : shape(blobs[0]), + C_shape = shape(outputs[0]); + helper.compute(trans_a, trans_b, A_shape, B_shape, C_shape); + + if (!blobs.empty()) { + fastGemmPackB(blobs[0], packed_input_B, trans_b, opt); + helper.updatePackedBOffsets(packed_input_B.size()); + } + } + + // works like Y = numpy.matmul(A, B) + void forward(InputArrayOfArrays inputs_arr, OutputArrayOfArrays outputs_arr, OutputArrayOfArrays internals_arr) CV_OVERRIDE { + CV_TRACE_FUNCTION(); + CV_TRACE_ARG_VALUE(name, "name", name.c_str()); + + CV_OCL_RUN(IS_DNN_OPENCL_TARGET(preferableTarget), + forward_ocl(inputs_arr, outputs_arr, internals_arr)) + + if (inputs_arr.depth() == CV_16S) + { + forward_fallback(inputs_arr, outputs_arr, internals_arr); + return; + } + + std::vector inputs, outputs; + inputs_arr.getMatVector(inputs); + outputs_arr.getMatVector(outputs); + + const auto &A = inputs[0]; + auto &Y = outputs[0]; + + const auto *a = A.ptr(); + auto *y = Y.ptr(); + std::memset(y, 0, Y.total() * sizeof(float)); + + if (blobs.empty()) { + const auto &B = inputs[1]; + const auto *b = B.ptr(); + fastGemmBatch(helper.batch, helper.A_offsets.data(), helper.B_offsets.data(), helper.C_offsets.data(), + helper.M, helper.N, helper.K, alpha, a, helper.lda0, helper.lda1, + b, helper.ldb0, helper.ldb1, beta, y, helper.ldc, opt); + } else { + fastGemmBatch(helper.batch, helper.A_offsets.data(), helper.packed_B_offsets.data(), helper.C_offsets.data(), + helper.M, helper.N, helper.K, alpha, a, helper.lda0, helper.lda1, + packed_input_B.data(), beta, y, helper.ldc, opt); + } + } + +#ifdef HAVE_OPENCL + bool forward_ocl(InputArrayOfArrays inputs_arr, OutputArrayOfArrays outputs_arr, InputArrayOfArrays internals) { + std::vector inputs; + std::vector outputs; + + bool use_half = (inputs_arr.depth() == CV_16S); + inputs_arr.getUMatVector(inputs); + outputs_arr.getUMatVector(outputs); + + const auto &input_A = inputs[0]; + UMat input_B; + if (blobs.empty()) { + input_B = inputs[1]; + } else { + blobs[0].copyTo(input_B); + } + auto &output = outputs[0]; + + int M = static_cast(helper.M), + N = static_cast(helper.N), + K = static_cast(helper.K), + batch = static_cast(helper.batch); + int batch_A = total(shape(input_A)) / (M * K), + batch_B = total(shape(input_B)) / (N * K); + MatShape new_shape_A{batch_A, M * K}, new_shape_B{batch_B, N * K}, new_shape_output{batch, M * N}; + + const auto input_A_2d = input_A.reshape(1, new_shape_A.size(), &new_shape_A[0]), + input_B_2d = input_B.reshape(1, new_shape_B.size(), &new_shape_B[0]); + auto output_2d = output.reshape(1, new_shape_output.size(), &new_shape_output[0]); + UMat A, B, C, A_fp32, B_fp32, C_fp32; + for (int i = 0; i < batch; i++) { + A = input_A_2d.row(helper.A_rows[i]).reshape(1, trans_a ? K : M); + B = input_B_2d.row(helper.B_rows[i]).reshape(1, trans_b ? K : N); + C = output_2d.row(helper.C_rows[i]).reshape(1, M); + + if (trans_a) { + A = A.t(); + } + if (trans_b) { + B = B.t(); + } + + if (use_half) { + convertFp16(A, A_fp32); + convertFp16(B, B_fp32); + convertFp16(C, C_fp32); + } else { + A_fp32 = A; + B_fp32 = B; + C_fp32 = C; + } + + cv::gemm(A_fp32, B_fp32, 1.f, noArray(), 0.f, C_fp32); + if (use_half) { + convertFp16(A_fp32, A); + convertFp16(B_fp32, B); + convertFp16(C_fp32, C); + } + } + return true; + } +#endif // HAVE_OPENCL + +#ifdef HAVE_DNN_NGRAPH + virtual Ptr initNgraph(const std::vector >& inputs, + const std::vector >& nodes) CV_OVERRIDE { + auto& input_A_node = nodes[0].dynamicCast()->node; + std::shared_ptr matmul; + + if (nodes.size() == 2) { + auto &input_B_node = nodes[1].dynamicCast()->node; + matmul = std::make_shared(input_A_node, input_B_node, trans_a, trans_b); + } else { + auto input_B_shape = getShape(blobs[0]); + auto input_B_node = std::make_shared(ngraph::element::f32, input_B_shape, blobs[0].data); + matmul = std::make_shared(input_A_node, input_B_node, trans_a, trans_b); + } + + return Ptr(new InfEngineNgraphNode(matmul)); + } +#endif // HAVE_DNN_NGRAPH + +#ifdef HAVE_VULKAN + virtual Ptr initVkCom(const std::vector > &inputs, + std::vector > &outputs) CV_OVERRIDE { + auto input_A_wrapper = inputs[0].dynamicCast(); + auto output_wrapper = outputs[0].dynamicCast(); + + const auto input_A_shape = shape(*input_A_wrapper->getMat()); + const auto output_shape = shape(*output_wrapper->getMat()); + if (output_shape.size() != 2) { + return Ptr(); + } + + std::vector constants; + + if (!blobs.empty()) { + constants.push_back(blobs[0]); + } + + Ptr op = new vkcom::OpMatMul(constants, input_A_shape[0], input_A_shape[1], output_shape[1]); + return Ptr(new VkComBackendNode(inputs, op, outputs)); + } +#endif + +#ifdef HAVE_CUDA + Ptr initCUDA(void *context_, + const std::vector>& inputs, + const std::vector>& outputs) override { + auto context = reinterpret_cast(context_); + auto input_B = blobs.empty() ? Mat() : blobs[0]; + + CV_CheckFalse(helper.empty(), "DNN/MatMul/CUDA: MatMulHelper is not initialized"); + + return make_cuda_node(preferableTarget, std::move(context->stream), std::move(context->cublas_handle), input_B, trans_a, trans_b, helper.A_offsets, helper.B_offsets, helper.C_offsets, helper.batch); + } +#endif // HAVE_CUDA + +#ifdef HAVE_CANN + virtual Ptr initCann(const std::vector > &inputs, + const std::vector > &outputs, + const std::vector >& nodes) CV_OVERRIDE { + auto input_A_wrapper = inputs[0].dynamicCast(); + auto input_A_desc = input_A_wrapper->getTensorDesc(); + auto input_A_node = nodes[0].dynamicCast()->getOp(); + + auto op = std::make_shared(name); + + // set attributes + op->set_attr_adj_x1(trans_a); + op->set_attr_adj_x2(trans_b); + + // set inputs + // set inputs : x1 + 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 + auto input_B_wrapper = inputs[1].dynamicCast(); + auto input_B_desc = input_B_wrapper->getTensorDesc(); + auto input_B_node = nodes[1].dynamicCast()->getOp(); + op->set_input_x2_by_name(*input_B_node, "y"); + op->update_input_desc_x2(*input_B_desc); + } else { // constant input B + auto B = blobs[0]; + auto const_B_node = std::make_shared(B.data, B.type(), shape(B), cv::format("%s_B", name.c_str())); + op->set_input_x2_by_name(*(const_B_node->getOp()), "y"); + op->update_input_desc_x2(*(const_B_node->getTensorDesc())); + } + + // set outputs + auto output_desc = std::make_shared(ge::Shape(), ge::FORMAT_NCHW, ge::DT_FLOAT); + op->update_output_desc_y(*output_desc); + return Ptr(new CannBackendNode(op)); + } +#endif // HAVE_CANN + + private: + bool trans_a; + bool trans_b; + float alpha; + float beta; + + std::vector packed_input_B; + + FastGemmOpt opt; + MatMulHelper helper; +}; + +Ptr MatMulLayer::create(const LayerParams& params) +{ + return makePtr(params); +} + +}} // cv::dnn diff --git a/modules/dnn/src/layers/nary_eltwise_layers.cpp b/modules/dnn/src/layers/nary_eltwise_layers.cpp index 67de1ff3df..2e9416fe25 100644 --- a/modules/dnn/src/layers/nary_eltwise_layers.cpp +++ b/modules/dnn/src/layers/nary_eltwise_layers.cpp @@ -114,9 +114,11 @@ public: op == OPERATION::GREATER_EQUAL || op == OPERATION::LESS_EQUAL ); - if (op == OPERATION::MAX || op == OPERATION::MIN || op == OPERATION::SUM || - op == OPERATION::PROD || op == OPERATION::DIV || op == OPERATION::ADD) - return backendId == DNN_BACKEND_OPENCV || backendId == DNN_BACKEND_CUDA; + if (backendId == DNN_BACKEND_CUDA) { + return op == OPERATION::MAX || op == OPERATION::MIN || op == OPERATION::SUM || + op == OPERATION::PROD || op == OPERATION::DIV || op == OPERATION::ADD || + op == OPERATION::SUB; + } return backendId == DNN_BACKEND_OPENCV; } @@ -832,6 +834,9 @@ public: case OPERATION::ADD: op_ = cuda4dnn::EltwiseOpType::SUM; break; + case OPERATION::SUB: + op_ = cuda4dnn::EltwiseOpType::SUB; + break; default: return Ptr(); // return empty cuda_node if the EltwiseOpType is unsupported type. }; diff --git a/modules/dnn/src/layers/region_layer.cpp b/modules/dnn/src/layers/region_layer.cpp index 49952b4c83..38b809e0f9 100644 --- a/modules/dnn/src/layers/region_layer.cpp +++ b/modules/dnn/src/layers/region_layer.cpp @@ -45,6 +45,7 @@ #include #include #include "../nms.inl.hpp" +#include "cpu_kernels/softmax.hpp" #ifdef HAVE_OPENCL #include "opencl_kernels_dnn.hpp" @@ -280,10 +281,8 @@ public: } if (useSoftmax) { // Yolo v2 - for (int i = 0; i < batch_size*rows*cols*anchors; ++i) { - int index = cell_size*i; - softmax_activate(srcData + index + 5, classes, 1, dstData + index + 5); - } + Mat _inpBlob = inpBlob.reshape(0, outBlob.dims, outBlob.size); + softmax(outBlob, _inpBlob, -1, 5, classes); } else if (useLogistic) { // Yolo v3 for (int i = 0; i < batch_size*rows*cols*anchors; ++i){ diff --git a/modules/dnn/src/layers/softmax_layer.cpp b/modules/dnn/src/layers/softmax_layer.cpp index 41077eda47..6ca715eefe 100644 --- a/modules/dnn/src/layers/softmax_layer.cpp +++ b/modules/dnn/src/layers/softmax_layer.cpp @@ -51,6 +51,7 @@ #include #include #include +#include "cpu_kernels/softmax.hpp" using std::max; #ifdef HAVE_OPENCL @@ -74,7 +75,7 @@ public: SoftMaxLayerImpl(const LayerParams& params) { - axisRaw = params.get("axis", 1); + axisRaw = params.get("axis", -1); logSoftMax = params.get("log_softmax", false); setParamsFrom(params); } @@ -223,89 +224,15 @@ public: std::vector inputs, outputs, internals; inputs_arr.getMatVector(inputs); outputs_arr.getMatVector(outputs); - internals_arr.getMatVector(internals); const Mat &src = inputs[0]; Mat &dst = outputs[0]; - int axis = normalize_axis(axisRaw, src.dims); - size_t outerSize = src.total(0, axis), channels = src.size[axis], - innerSize = src.total(axis + 1); - CV_Assert(src.type() == CV_32F); - CV_Assert(src.isContinuous() && dst.isContinuous()); - - const float *srcPtr = src.ptr(); - float *dstPtr = dst.ptr(); - float *bufPtr = internals[0].ptr(); - - size_t outerStep = src.total(axis); - size_t cnStep = src.total(axis + 1); - - //compute max along axis - for (size_t outerDim = 0; outerDim < outerSize; outerDim++) - { - size_t srcOffset = outerDim * outerStep; - size_t bufOffset = outerDim * cnStep; - - memcpy(bufPtr + bufOffset, srcPtr + srcOffset, innerSize * sizeof(float)); - - for (size_t cnDim = 1; cnDim < channels; cnDim++) - { - for (size_t i = 0; i < innerSize; i++) - bufPtr[bufOffset + i] = std::max(bufPtr[bufOffset + i], srcPtr[srcOffset + cnDim * cnStep + i]); - } - } - - //subtract max - for (size_t outerDim = 0; outerDim < outerSize; outerDim++) - { - size_t srcOffset = outerDim * outerStep; - size_t bufOffset = outerDim * cnStep; - - for (size_t cnDim = 0; cnDim < channels; cnDim++) - { - const int offset = srcOffset + cnDim * cnStep; - for (size_t i = 0; i < innerSize; i++) - dstPtr[offset + i] = srcPtr[offset + i] - bufPtr[bufOffset + i]; - } - } - - cv::exp(dst, dst); - - for (size_t outerDim = 0; outerDim < outerSize; outerDim++) - { - size_t srcOffset = outerDim * outerStep; - size_t bufOffset = outerDim * cnStep; - - //sum exp along axis - for (size_t i = 0; i < innerSize; i++) - bufPtr[bufOffset + i] = 0.f; - - for (size_t cnDim = 0; cnDim < channels; cnDim++) - { - const int offset = srcOffset + cnDim * cnStep; - for (size_t i = 0; i < innerSize; i++) - bufPtr[bufOffset + i] += dstPtr[offset + i]; - } - - //divide by computed sum - for (size_t cnDim = 0; cnDim < channels; cnDim++) - { - const int offset = srcOffset + cnDim * cnStep; - for (size_t i = 0; i < innerSize; i++) - dstPtr[offset + i] /= bufPtr[bufOffset + i]; - } - if (logSoftMax) - { - for (size_t cnDim = 0; cnDim < channels; cnDim++) - { - const int offset = srcOffset + cnDim * cnStep; - for (size_t i = 0; i < innerSize; i++) - dstPtr[offset + i] = log(dstPtr[offset + i]); - } - } - } + if(logSoftMax) + logSoftmax(dst, src, axis); + else + softmax(dst, src, axis); } #ifdef HAVE_CUDA diff --git a/modules/dnn/src/model.cpp b/modules/dnn/src/model.cpp index 64b2706d38..bc8e2ebe33 100644 --- a/modules/dnn/src/model.cpp +++ b/modules/dnn/src/model.cpp @@ -37,6 +37,7 @@ public: virtual void setPreferableBackend(Backend backendId) { net.setPreferableBackend(backendId); } virtual void setPreferableTarget(Target targetId) { net.setPreferableTarget(targetId); } + virtual void enableWinograd(bool useWinograd) { net.enableWinograd(useWinograd); } virtual void initNet(const Net& network) @@ -151,6 +152,7 @@ Model& Model::setPreferableBackend(Backend backendId) impl->setPreferableBackend(backendId); return *this; } + Model& Model::setPreferableTarget(Target targetId) { CV_DbgAssert(impl); @@ -158,6 +160,13 @@ Model& Model::setPreferableTarget(Target targetId) return *this; } +Model& Model::enableWinograd(bool useWinograd) +{ + CV_DbgAssert(impl); + impl->enableWinograd(useWinograd); + return *this; +} + Model& Model::setInputSize(const Size& size) { CV_DbgAssert(impl); diff --git a/modules/dnn/src/net_impl_backend.cpp b/modules/dnn/src/net_impl_backend.cpp index cd12e6a21b..2751a41782 100644 --- a/modules/dnn/src/net_impl_backend.cpp +++ b/modules/dnn/src/net_impl_backend.cpp @@ -236,6 +236,14 @@ void Net::Impl::setPreferableTarget(int targetId) #endif clear(); + + if (targetId == DNN_TARGET_CPU_FP16) + { + if (useWinograd) { + CV_LOG_INFO(NULL, "DNN: DNN_TARGET_CPU_FP16 is set => Winograd convolution is disabled by default to preserve accuracy. If needed, enable it explicitly using enableWinograd(true)."); + enableWinograd(false); + } + } } } diff --git a/modules/dnn/src/ocl4dnn/src/ocl4dnn_conv_spatial.cpp b/modules/dnn/src/ocl4dnn/src/ocl4dnn_conv_spatial.cpp index 90cc2108d6..283a0b88e9 100644 --- a/modules/dnn/src/ocl4dnn/src/ocl4dnn_conv_spatial.cpp +++ b/modules/dnn/src/ocl4dnn/src/ocl4dnn_conv_spatial.cpp @@ -1462,6 +1462,16 @@ void OCL4DNNConvSpatial::generate_gemmlike_tuneritems(std::vector< cv::Pt return; } + // issue #24734 + // OpenCL 1.2: https://registry.khronos.org/OpenCL/specs/opencl-1.2.pdf + // section 6.1.2 page 200: "Supported values of n are 2, 3, 4, 8, and 16 for all vector data types." + // besides of builtin types, kernel code defines extra types up to float15 (see float15 definition) + if (kernel_w_ > 16) + { + CV_LOG_DEBUG(NULL, "DNN/OCL: skip KERNEL_TYPE_GEMM_LIKE with blockMKN=[" << blockM << ", " << blockK << ", " << blockN << "] kernel=" << kernel_w_ << " x " << kernel_h_); + return; + } + tunerItems.push_back(makePtr(KERNEL_TYPE_GEMM_LIKE, blockM, blockK, blockN)); } diff --git a/modules/dnn/src/onnx/onnx_graph_simplifier.cpp b/modules/dnn/src/onnx/onnx_graph_simplifier.cpp index a43815dbe4..77dc1c52df 100644 --- a/modules/dnn/src/onnx/onnx_graph_simplifier.cpp +++ b/modules/dnn/src/onnx/onnx_graph_simplifier.cpp @@ -13,6 +13,7 @@ #include #include +#include namespace cv { namespace dnn { CV__DNN_INLINE_NS_BEGIN @@ -64,6 +65,12 @@ class ONNXGraphWrapper : public ImportGraphWrapper public: ONNXGraphWrapper(opencv_onnx::GraphProto& _net) : net(_net) { + // Add a fake initializer with empty name. + // Some ONNX models skip their inputs. For example, + // Resize which has 4 inputs but 2 of them have empty names. + // So we add a fake empty node to which such ops may refer as input. + net.add_initializer(); + numInputs = net.input_size(); numInitializers = net.initializer_size(); } @@ -76,6 +83,23 @@ public: return makePtr(node); } + int getTensorShapeSize(int node_id, int node_input_id) { + const auto node = getNode(node_id); + const auto &input_name = node->getInputName(node_input_id); + for (int i = 0; i < net.value_info_size(); i++) { + const auto value_info = net.value_info(i); + if (value_info.name() == input_name) { + if (value_info.has_type() && value_info.type().has_tensor_type() && + value_info.type().tensor_type().has_shape()) { + return value_info.type().tensor_type().shape().dim_size(); + } else { + return -1; + } + } + } + return -1; + } + int getInputInitializerId(int node_id, int node_input_id) { auto node = getNode(node_id); @@ -125,8 +149,13 @@ public: virtual void removeNode(int idx) CV_OVERRIDE { - CV_Assert(idx >= numInputs + numInitializers); - net.mutable_node()->DeleteSubrange(idx - numInputs - numInitializers, 1); + if (idx >= numInputs + numInitializers) + net.mutable_node()->DeleteSubrange(idx - numInputs - numInitializers, 1); + } + + virtual inline bool isCommutativeOp(const std::string& type) const CV_OVERRIDE + { + return type == "Add" || type == "Mul" || type == "Equal" || type == "Max"; } private: @@ -134,6 +163,369 @@ private: opencv_onnx::GraphProto& net; }; +static Mat extractConstant(const Ptr& net, int node_id, int input_id) +{ + auto onnx_net = net.dynamicCast(); + int initializer_id = onnx_net->getInputInitializerId(node_id, input_id); + if (initializer_id != -1) + { + return onnx_net->getMatFromInitializer(initializer_id); + } + else + { + const Ptr node = net->getNode(node_id); + int constant_id = Subgraph::getInputNodeId(net, node, input_id); + Ptr constant_ptr = net->getNode(constant_id); + opencv_onnx::NodeProto* constant_node = constant_ptr.dynamicCast()->node; + opencv_onnx::TensorProto constant_proto = constant_node->attribute(0).t(); + return getMatFromTensor(constant_proto); + } +} + +static std::string getInputName(const Ptr& net, int node_id, int input_id) { + auto onnx_net = net.dynamicCast(); + int initializer_id = onnx_net->getInputInitializerId(node_id, input_id); + if (initializer_id != -1) { + return onnx_net->getNameOfInitializer(initializer_id); + } else { + const auto node = net->getNode(node_id); + return node->getInputName(input_id); + } +} + +/* Slice operator has two optional inputs "axes" and "steps". Some models may be set to have + Slice with optional inputs of default values, some of them don't. This Subgraph adjusts + all optional inputs of Slice up to 5. +*/ +class AdjustSliceAllOptionalInputsSubgraph : public Subgraph { + public: + AdjustSliceAllOptionalInputsSubgraph(size_t num_inputs = 4) { + num_inputs_ = num_inputs; + + int input = addNodeToMatch(""); + int starts = addNodeToMatch(""); + int ends = addNodeToMatch(""); + std::vector inputs{input, starts, ends}; + for (size_t i = 3; i < num_inputs_; i++) { // axes and steps + inputs.push_back(addNodeToMatch("")); + } + + slice_id = addNodeToMatch("Slice", inputs); + + setFusedNode("Slice", inputs); + } + + virtual void finalize(const Ptr&, + const Ptr& fusedNode, + std::vector >&) CV_OVERRIDE + { + opencv_onnx::NodeProto* node = fusedNode.dynamicCast()->node; + for (int i = num_inputs_; i < 5; ++i) { + node->add_input(""); + } + } + private: + int slice_id; + size_t num_inputs_; +}; + +/* The fusion for the multi-head attention from vision transformer. + + Abbreviations: + B - batch_size, symbolic; + S - sequence_length, symbolic; + W - hidden_size, W = N * H; + N - num_heads; + H - head_size; + + Graph before fusion: + [Input](BxSxW) + | + LayerNorm + | + Transpose(perm=[1, 0, 2]) + | + | (SxBxW) + | + Matmul[Weight(Wx3W)] + | + Add[Bias(3W)] + / | \ + q_Slice k_Slice v_Slice (output(SxBxW)) + | | | + q_Reshape k_Reshape v_Reshape (output(Sx(BxN)xH), could be optional if N=1) + | | | + q_Transpose k_Transpose v_Transpose + (1,0,2) (1,2,0) (perm=1,0,2) + |((BxN)xSxH) |((BxN)xHxS) | + q_Div / / + \ / / + qk_MatMul / + | / + qk_Softmax / + | ((BxN)xSxS) / ((BxN)xSxH) + \ / + qkv_MatMul (output((BxN)xSxH)) + | + Transpose(perm=1,2,0) + | + Reshape (output(SxH)) + | + MatMul + | + Add + | + [Output](BxSxW) + + + Attributes: + num_heads - number of attention heads + qkv_hidden_sizes - hidden size of qkv respectively, [qk_hidden_size, qk_hidden_size, v_hidden_size], + assume qk_hidden_size = v_hidden_size for now. TODO: support qk_hidden_size != v_hidden_size + scale - scale factor of q, defaults to sqrt(1/num_heads) + Inputs: + weight - merged Q, K, V weights of shape [input_hidden_size, qk_hidden_size + qk_hidden_size + v_hidden_size] + bias - bias of shape [qk_hidden_size + qk_hidden_size + v_hidden_size] + + Graph after fusion: + [Input](BxSxW) + | + LayerNorm + | + Transpose + | + Attention[weight, bias] + | + MatMul + | + Add + | + [Output](BxSxW) + + More details see See https://github.com/microsoft/onnxruntime/blob/v1.16.1/docs/ContribOperators.md#com.microsoft.Attention. +*/ +class AttentionSubGraph : public Subgraph { + public: + AttentionSubGraph() { + int input = addNodeToMatch(""); + int transpose = addNodeToMatch("Transpose", input); // tranpose does not make any differences to the accuracy here in this subgraph + att_matmul = addNodeToMatch("MatMul", transpose, addNodeToMatch("")); + att_add = addNodeToMatch("Add", addNodeToMatch(""), att_matmul); + + // v_path + slice_v = addNodeToMatch("Slice", std::vector{att_add, addNodeToMatch(""), addNodeToMatch(""), addNodeToMatch(""), addNodeToMatch("")}); + int reshape_v = addNodeToMatch("Reshape", slice_v, addNodeToMatch("")); + int transpose_v = addNodeToMatch("Transpose", reshape_v); + + // q_path + slice_q = addNodeToMatch("Slice", std::vector{att_add, addNodeToMatch(""), addNodeToMatch(""), addNodeToMatch(""), addNodeToMatch("")}); + reshape_q = addNodeToMatch("Reshape", slice_q, addNodeToMatch("")); + int transpose_q = addNodeToMatch("Transpose", reshape_q); + div_q = addNodeToMatch("Div", transpose_q, addNodeToMatch("")); + + // k_path + slice_k = addNodeToMatch("Slice", std::vector{att_add, addNodeToMatch(""), addNodeToMatch(""), addNodeToMatch(""), addNodeToMatch("")}); + int reshape_k = addNodeToMatch("Reshape", slice_k, addNodeToMatch("")); + int transpose_k = addNodeToMatch("Transpose", reshape_k); + + // qk + int matmul_qk = addNodeToMatch("MatMul", div_q, transpose_k); + int softmax_qk = addNodeToMatch("Softmax", matmul_qk); + + // qkv + int matmul_qkv = addNodeToMatch("MatMul", softmax_qk, transpose_v); + int transpose_qkv = addNodeToMatch("Transpose", matmul_qkv); + last_reshape = addNodeToMatch("Reshape", transpose_qkv, addNodeToMatch("")); + + setFusedNode("Attention", input); + } + + virtual bool match(const Ptr& net, int nodeId, + std::vector& matchedNodesIds) CV_OVERRIDE { + if (Subgraph::match(net, nodeId, matchedNodesIds)) { + // get attrs - qkv_hidden_sizes + qkv_hidden_sizes.clear(); + auto fill_qkv_hidden_sizes = [&] (const int slice_node_id) { + int slice_start = extractConstant(net, matchedNodesIds[slice_node_id], 1).at(0); + int slice_end = extractConstant(net, matchedNodesIds[slice_node_id], 2).at(0); + if (slice_end == std::numeric_limits::max()) { + qkv_hidden_sizes.push_back(0); // workaround for Slice with end=INT_MAX + } else { + int64_t hidden_size = static_cast(slice_end - slice_start); + qkv_hidden_sizes.push_back(hidden_size); + } + }; + fill_qkv_hidden_sizes(slice_q); + fill_qkv_hidden_sizes(slice_k); + fill_qkv_hidden_sizes(slice_v); // TODO: take care of INT64_MAX + CV_CheckEQ(qkv_hidden_sizes.size(), static_cast(3), "ONNXSimplifier/Attention: invalid qkv hidden sizes"); + CV_CheckEQ(int(qkv_hidden_sizes[0]), int(qkv_hidden_sizes[1]), "ONNXSimplifier/Attention: invalid qkv hidden sizes, q_hidden_size == v_hidden_size is required"); + // get attrs - num_heads, scale + num_heads = extractConstant(net, matchedNodesIds[reshape_q], 1).at(1); + scale = extractConstant(net, matchedNodesIds[div_q], 1).at(0); + output_ndims = extractConstant(net, matchedNodesIds[last_reshape], 1).size[0]; + + // get names + weight_name = getInputName(net, matchedNodesIds[att_matmul], 1); + bias_name = getInputName(net, matchedNodesIds[att_add], 0); + return true; + } + return false; + } + + virtual void finalize(const Ptr& net, + const Ptr& fusedNode, + std::vector >&) CV_OVERRIDE { + // add attrs + opencv_onnx::NodeProto* node = fusedNode.dynamicCast()->node; + opencv_onnx::AttributeProto* attr_num_heads = node->add_attribute(); + attr_num_heads->set_name("num_heads"); + attr_num_heads->set_i(num_heads); + opencv_onnx::AttributeProto* attr_qkv_hidden_sizes = node->add_attribute(); + attr_qkv_hidden_sizes->set_name("qkv_hidden_sizes"); + attr_qkv_hidden_sizes->add_ints(qkv_hidden_sizes[0]); + attr_qkv_hidden_sizes->add_ints(qkv_hidden_sizes[1]); + attr_qkv_hidden_sizes->add_ints(qkv_hidden_sizes[2]); + opencv_onnx::AttributeProto* attr_scale = node->add_attribute(); + attr_scale->set_name("scale"); + attr_scale->set_f(scale); + + // add customized attrs + opencv_onnx::AttributeProto* attr_output_ndims = node->add_attribute(); + attr_output_ndims->set_name("output_ndims"); + attr_output_ndims->set_i(output_ndims); + + // add inputs + node->add_input(weight_name); + node->add_input(bias_name); + } + + private: + int att_matmul, att_add; + int slice_q, slice_k, slice_v; + int reshape_q, div_q, last_reshape; + + std::vector qkv_hidden_sizes; // order: [qk_hidden_size, qk_hidden_size, v_hidden_size] + int64_t num_heads; + float scale; + + int64_t output_ndims; + + std::string weight_name; + std::string bias_name; +}; + +/* Attention subgraph with single head. + No Reshape operator is appended after each Slice operator. +*/ +class AttentionSingleHeadSubGraph : public Subgraph { + public: + AttentionSingleHeadSubGraph() { + int input = addNodeToMatch(""); + int transpose = addNodeToMatch("Transpose", input); // tranpose does not make any differences to the accuracy here in this subgraph + att_matmul = addNodeToMatch("MatMul", transpose, addNodeToMatch("")); + att_add = addNodeToMatch("Add", addNodeToMatch(""), att_matmul); + + // v_path + slice_v = addNodeToMatch("Slice", std::vector{att_add, addNodeToMatch(""), addNodeToMatch(""), addNodeToMatch(""), addNodeToMatch("")}); + int transpose_v = addNodeToMatch("Transpose", slice_v); + + // q_path + slice_q = addNodeToMatch("Slice", std::vector{att_add, addNodeToMatch(""), addNodeToMatch(""), addNodeToMatch(""), addNodeToMatch("")}); + int transpose_q = addNodeToMatch("Transpose", slice_q); + div_q = addNodeToMatch("Div", transpose_q, addNodeToMatch("")); + + // k_path + slice_k = addNodeToMatch("Slice", std::vector{att_add, addNodeToMatch(""), addNodeToMatch(""), addNodeToMatch(""), addNodeToMatch("")}); + int transpose_k = addNodeToMatch("Transpose", slice_k); + + // qk + int matmul_qk = addNodeToMatch("MatMul", div_q, transpose_k); + int softmax_qk = addNodeToMatch("Softmax", matmul_qk); + + // qkv + int matmul_qkv = addNodeToMatch("MatMul", softmax_qk, transpose_v); + int transpose_qkv = addNodeToMatch("Transpose", matmul_qkv); + last_reshape = addNodeToMatch("Reshape", transpose_qkv, addNodeToMatch("")); + + setFusedNode("Attention", input); + } + + virtual bool match(const Ptr& net, int nodeId, + std::vector& matchedNodesIds) CV_OVERRIDE { + if (Subgraph::match(net, nodeId, matchedNodesIds)) { + // get attrs - qkv_hidden_sizes + qkv_hidden_sizes.clear(); + auto fill_qkv_hidden_sizes = [&] (const int slice_node_id) { + int slice_start = extractConstant(net, matchedNodesIds[slice_node_id], 1).at(0); + int slice_end = extractConstant(net, matchedNodesIds[slice_node_id], 2).at(0); + if (slice_end == std::numeric_limits::max()) { + qkv_hidden_sizes.push_back(0); // workaround for Slice with end=INT_MAX + } else { + int64_t hidden_size = static_cast(slice_end - slice_start); + qkv_hidden_sizes.push_back(hidden_size); + } + }; + fill_qkv_hidden_sizes(slice_q); + fill_qkv_hidden_sizes(slice_k); + fill_qkv_hidden_sizes(slice_v); + CV_CheckEQ(qkv_hidden_sizes.size(), static_cast(3), "ONNXSimplifier/Attention: invalid qkv hidden sizes"); + CV_CheckEQ(int(qkv_hidden_sizes[0]), int(qkv_hidden_sizes[1]), "ONNXSimplifier/Attention: invalid qkv hidden sizes, q_hidden_size == v_hidden_size is required"); + // get attrs - num_heads, scale + num_heads = 1; + scale = extractConstant(net, matchedNodesIds[div_q], 1).at(0); + output_ndims = extractConstant(net, matchedNodesIds[last_reshape], 1).size[0]; + + // get names + weight_name = getInputName(net, matchedNodesIds[att_matmul], 1); + bias_name = getInputName(net, matchedNodesIds[att_add], 0); + return true; + } + return false; + } + + virtual void finalize(const Ptr& net, + const Ptr& fusedNode, + std::vector >&) CV_OVERRIDE { + // add attrs + opencv_onnx::NodeProto* node = fusedNode.dynamicCast()->node; + opencv_onnx::AttributeProto* attr_num_heads = node->add_attribute(); + attr_num_heads->set_name("num_heads"); + attr_num_heads->set_i(num_heads); + opencv_onnx::AttributeProto* attr_qkv_hidden_sizes = node->add_attribute(); + attr_qkv_hidden_sizes->set_name("qkv_hidden_sizes"); + attr_qkv_hidden_sizes->add_ints(qkv_hidden_sizes[0]); + attr_qkv_hidden_sizes->add_ints(qkv_hidden_sizes[1]); + attr_qkv_hidden_sizes->add_ints(qkv_hidden_sizes[2]); + opencv_onnx::AttributeProto* attr_scale = node->add_attribute(); + attr_scale->set_name("scale"); + attr_scale->set_f(scale); + + // add customized attrs + opencv_onnx::AttributeProto* attr_output_ndims = node->add_attribute(); + attr_output_ndims->set_name("output_ndims"); + attr_output_ndims->set_i(output_ndims); + + // add inputs + node->add_input(weight_name); + node->add_input(bias_name); + } + + protected: + int att_matmul, att_add; + int slice_q, slice_k, slice_v; + int div_q, last_reshape; + + std::vector qkv_hidden_sizes; // order: [qk_hidden_size, qk_hidden_size, v_hidden_size] + int64_t num_heads; + float scale; + + int64_t output_ndims; + + std::string weight_name; + std::string bias_name; +}; + /* Fusion for Gelu. Graph before fusion: @@ -151,54 +543,32 @@ public: GeluSubGraph() { int input = addNodeToMatch(""); - int div = addNodeToMatch("Div", input, addNodeToMatch("") /* B=sqrt(2) */ ); + div = addNodeToMatch("Div", input, addNodeToMatch("") /* B=sqrt(2) */ ); int erf = addNodeToMatch("Erf", div); - int add = addNodeToMatch("Add", erf, addNodeToMatch("") /* B=1 */ ); + add = addNodeToMatch("Add", erf, addNodeToMatch("") /* B=1 */ ); int mul = addNodeToMatch("Mul", input, add); - addNodeToMatch("Mul", mul, addNodeToMatch("") /* B=0.5 */) ; + mul2 = addNodeToMatch("Mul", mul, addNodeToMatch("") /* B=0.5 */) ; setFusedNode("Gelu", input); } - static float extractConstant(const Ptr& net, int node_id, int input_id) - { - auto onnx_net = net.dynamicCast(); - int initializer_id = onnx_net->getInputInitializerId(node_id, input_id); - if (initializer_id != -1) - { - Mat const_mat = onnx_net->getMatFromInitializer(initializer_id); - return *const_mat.ptr(); - } - else - { - const Ptr node = net->getNode(node_id); - int constant_id = getInputNodeId(net, node, input_id); - Ptr constant_ptr = net->getNode(constant_id); - opencv_onnx::NodeProto* constant_node = constant_ptr.dynamicCast()->node; - opencv_onnx::TensorProto constant_proto = constant_node->attribute(0).t(); - Mat constant_mat = getMatFromTensor(constant_proto); - return *constant_mat.ptr(); - } - } - virtual bool match(const Ptr& net, int nodeId, - std::vector& matchedNodesIds, - std::vector& targetNodesIds) CV_OVERRIDE + std::vector& matchedNodesIds) CV_OVERRIDE { - if (Subgraph::match(net, nodeId, matchedNodesIds, targetNodesIds)) + if (Subgraph::match(net, nodeId, matchedNodesIds)) { // Check Div[B=sqrt(2)] - float divisor = extractConstant(net, matchedNodesIds[0], 1); + float divisor = extractConstant(net, matchedNodesIds[div], 1).at(0); if (std::fabs(divisor - M_SQRT2) >= std::numeric_limits::epsilon()) return false; // Check Add[B=1] - float add_const = extractConstant(net, matchedNodesIds[2], 1); + float add_const = extractConstant(net, matchedNodesIds[add], 1).at(0); if (std::fabs(add_const - 1.f) >= std::numeric_limits::epsilon()) return false; // Check Mul[B=0.5] - float mul_const = extractConstant(net, matchedNodesIds[4], 1); + float mul_const = extractConstant(net, matchedNodesIds[mul2], 1).at(0); if (std::fabs(mul_const - 0.5f) >= std::numeric_limits::epsilon()) return false; @@ -206,6 +576,9 @@ public: } return false; } + +private: + int div, add, mul2; }; /* Fusion for GeluApproximation. @@ -229,61 +602,39 @@ public: int input = addNodeToMatch(""); int mul0 = addNodeToMatch("Mul", input, input); int mul1 = addNodeToMatch("Mul", input, mul0); - int mul2 = addNodeToMatch("Mul", addNodeToMatch("") /* A=0.044714998453855515 */, mul1); + mul2 = addNodeToMatch("Mul", addNodeToMatch("") /* A=0.044714998453855515 */, mul1); int add0 = addNodeToMatch("Add", input, mul2); - int mul3 = addNodeToMatch("Mul", addNodeToMatch("") /* A=sqrt(2/pie) */, add0); + mul3 = addNodeToMatch("Mul", addNodeToMatch("") /* A=sqrt(2/pie) */, add0); int tanh = addNodeToMatch("Tanh", mul3); - int add1 = addNodeToMatch("Add", addNodeToMatch("") /* A=1 */, tanh); + add1 = addNodeToMatch("Add", addNodeToMatch("") /* A=1 */, tanh); int mul4 = addNodeToMatch("Mul", input, add1); - addNodeToMatch("Mul", addNodeToMatch("") /* A=0.5 */, mul4); + mul5 = addNodeToMatch("Mul", addNodeToMatch("") /* A=0.5 */, mul4); setFusedNode("GeluApproximation", input); } - static float extractConstant(const Ptr& net, int node_id, int input_id) - { - auto onnx_net = net.dynamicCast(); - int initializer_id = onnx_net->getInputInitializerId(node_id, input_id); - if (initializer_id != -1) - { - Mat const_mat = onnx_net->getMatFromInitializer(initializer_id); - return *const_mat.ptr(); - } - else - { - const Ptr node = net->getNode(node_id); - int constant_id = getInputNodeId(net, node, input_id); - Ptr constant_ptr = net->getNode(constant_id); - opencv_onnx::NodeProto* constant_node = constant_ptr.dynamicCast()->node; - opencv_onnx::TensorProto constant_proto = constant_node->attribute(0).t(); - Mat constant_mat = getMatFromTensor(constant_proto); - return *constant_mat.ptr(); - } - } - virtual bool match(const Ptr& net, int nodeId, - std::vector& matchedNodesIds, - std::vector& targetNodesIds) CV_OVERRIDE + std::vector& matchedNodesIds) CV_OVERRIDE { - if (Subgraph::match(net, nodeId, matchedNodesIds, targetNodesIds)) + if (Subgraph::match(net, nodeId, matchedNodesIds)) { // Check Mul[A=0.044714998453855515] - float coef = extractConstant(net, matchedNodesIds[2], 0); + float coef = extractConstant(net, matchedNodesIds[mul2], 0).at(0); if (coef - 0.044714998453855515 >= 1e-6) return false; // Check Mul[A=sqrt(2/pie)] - float sqrt_2_pie = extractConstant(net, matchedNodesIds[4], 0); + float sqrt_2_pie = extractConstant(net, matchedNodesIds[mul3], 0).at(0); if (sqrt_2_pie - 0.7978845834732056 >= 1e-6) return false; // Check Add[A=1] - float add_const = extractConstant(net, matchedNodesIds[6], 0); + float add_const = extractConstant(net, matchedNodesIds[add1], 0).at(0); if (add_const - 1.f >= 1e-6) return false; // Check Mul[A=0.5] - float mul_const = extractConstant(net, matchedNodesIds[8], 0); + float mul_const = extractConstant(net, matchedNodesIds[mul5], 0).at(0); if (mul_const - 0.5f >= 1e-6) return false; @@ -291,6 +642,9 @@ public: } return false; } + +private: + int mul2, mul3, add1, mul5; }; /* Fusion for LayerNormalization. @@ -313,43 +667,22 @@ public: LayerNormSubGraph() : axis(-1), epsilon(1e-5) { int input = addNodeToMatch(""); - int mean = addNodeToMatch("ReduceMean", input); + mean = addNodeToMatch("ReduceMean", input); int sub = addNodeToMatch("Sub", input, mean); - int pow = addNodeToMatch("Pow", sub, addNodeToMatch("")); - int mean1 = addNodeToMatch("ReduceMean", pow); - int add = addNodeToMatch("Add", mean1, addNodeToMatch("")); + pow = addNodeToMatch("Pow", sub, addNodeToMatch("")); + mean1 = addNodeToMatch("ReduceMean", pow); + add = addNodeToMatch("Add", mean1, addNodeToMatch("")); int sqrt = addNodeToMatch("Sqrt", add); int div = addNodeToMatch("Div", sub, sqrt); - int mul = addNodeToMatch("Mul", div, addNodeToMatch("")); - addNodeToMatch("Add", mul, addNodeToMatch("")); + mul = addNodeToMatch("Mul", div, addNodeToMatch("")); + bias = addNodeToMatch("Add", mul, addNodeToMatch("")); setFusedNode("LayerNormalization", input); } - static float extractConstant(const Ptr& net, int node_id, int input_id) - { - auto onnx_net = net.dynamicCast(); - int initializer_id = onnx_net->getInputInitializerId(node_id, input_id); - if (initializer_id != -1) // initializer - { - Mat const_mat = onnx_net->getMatFromInitializer(initializer_id); - return *const_mat.ptr(); - } - else - { - const Ptr node = net->getNode(node_id); - int constant_id = getInputNodeId(net, node, input_id); - Ptr constant_ptr = net->getNode(constant_id); - opencv_onnx::NodeProto* constant_node = constant_ptr.dynamicCast()->node; - opencv_onnx::TensorProto constant_proto = constant_node->attribute(0).t(); - Mat constant_mat = getMatFromTensor(constant_proto); - return *constant_mat.ptr(); - } - } - static float extractAxis(const Ptr& net, int node_id) { Ptr mean_ptr = net->getNode(node_id); @@ -365,41 +698,25 @@ public: return axis_; } - static std::string getInputName(const Ptr& net, int node_id, int input_id) - { - auto onnx_net = net.dynamicCast(); - int initializer_id = onnx_net->getInputInitializerId(node_id, input_id); - if (initializer_id != -1) - { - return onnx_net->getNameOfInitializer(initializer_id); - } - else - { - const auto node = net->getNode(node_id); - return node->getInputName(input_id); - } - } - virtual bool match(const Ptr& net, int nodeId, - std::vector& matchedNodesIds, - std::vector& targetNodesIds) CV_OVERRIDE + std::vector& matchedNodesIds) CV_OVERRIDE { - if (Subgraph::match(net, nodeId, matchedNodesIds, targetNodesIds)) + if (Subgraph::match(net, nodeId, matchedNodesIds)) { - float pow_exp = extractConstant(net, matchedNodesIds[2], 1); + float pow_exp = extractConstant(net, matchedNodesIds[pow], 1).at(0); if (pow_exp - 2 > 1e-5) // not pow(2) return false; - int axis_mean1 = extractAxis(net, matchedNodesIds[0]); - int axis_mean2 = extractAxis(net, matchedNodesIds[3]); + int axis_mean1 = extractAxis(net, matchedNodesIds[mean]); + int axis_mean2 = extractAxis(net, matchedNodesIds[mean1]); if (axis_mean1 != axis_mean2) return false; axis = axis_mean1; - epsilon = extractConstant(net, matchedNodesIds[4], 1); + epsilon = extractConstant(net, matchedNodesIds[add], 1).at(0); - weight_name = getInputName(net, matchedNodesIds[7], 1); - bias_name = getInputName(net, matchedNodesIds[8], 1); + weight_name = getInputName(net, matchedNodesIds[mul], 1); + bias_name = getInputName(net, matchedNodesIds[bias], 1); return true; } @@ -429,6 +746,7 @@ protected: float epsilon; std::string weight_name; std::string bias_name; + int pow, mean, mean1, add, mul, bias; }; class SoftMaxSubgraphBase : public Subgraph @@ -437,10 +755,9 @@ public: SoftMaxSubgraphBase() : axis(1), id(-1) {} virtual bool match(const Ptr& net, int nodeId, - std::vector& matchedNodesIds, - std::vector& targetNodesIds) CV_OVERRIDE + std::vector& matchedNodesIds) CV_OVERRIDE { - if (Subgraph::match(net, nodeId, matchedNodesIds, targetNodesIds)) + if (Subgraph::match(net, nodeId, matchedNodesIds)) { CV_Assert(id >= 0 && id < matchedNodesIds.size()); Ptr sum = net->getNode(matchedNodesIds[id]); @@ -485,7 +802,7 @@ public: int inpExp = addNodeToMatch("Exp", input); int sum = addNodeToMatch("ReduceSum", inpExp); - id = 1; + id = sum; addNodeToMatch("Div", inpExp, sum); setFusedNode("Softmax", input); @@ -498,7 +815,7 @@ public: int input = addNodeToMatch(""); int reducemax = addNodeToMatch("ReduceMax", input); - id = 0; + id = reducemax; int sub = addNodeToMatch("Sub", input, reducemax); int exp = addNodeToMatch("Exp", sub); @@ -516,7 +833,7 @@ public: int input = addNodeToMatch(""); int reducemax = addNodeToMatch("ReduceMax", input); - id = 0; + id = reducemax; int sub_1 = addNodeToMatch("Sub", input, reducemax); int exp = addNodeToMatch("Exp", sub_1); @@ -533,18 +850,17 @@ public: HardSwishSubgraph() { int input = addNodeToMatch(""); - int hardSigmoid = addNodeToMatch("HardSigmoid", input); - addNodeToMatch("Mul", input, hardSigmoid); + hardSigmoidId = addNodeToMatch("HardSigmoid", input); + addNodeToMatch("Mul", input, hardSigmoidId); setFusedNode("HardSwish", input); } virtual bool match(const Ptr& net, int nodeId, - std::vector& matchedNodesIds, - std::vector& targetNodesIds) CV_OVERRIDE + std::vector& matchedNodesIds) CV_OVERRIDE { - if (Subgraph::match(net, nodeId, matchedNodesIds, targetNodesIds)) + if (Subgraph::match(net, nodeId, matchedNodesIds)) { - Ptr hardSigmoid = net->getNode(matchedNodesIds[0]); + Ptr hardSigmoid = net->getNode(matchedNodesIds[hardSigmoidId]); opencv_onnx::NodeProto* node = hardSigmoid.dynamicCast()->node; uint8_t matched = 0; @@ -561,6 +877,9 @@ public: } return false; } + +private: + int hardSigmoidId; }; class CeluSubgraph : public Subgraph @@ -569,9 +888,9 @@ public: CeluSubgraph() : alpha(1.f) { int input = addNodeToMatch(""); - int div = addNodeToMatch("Div", input, addNodeToMatch("")); - int elu = addNodeToMatch("Elu", div); - addNodeToMatch("Mul", addNodeToMatch(""), elu); + div = addNodeToMatch("Div", input, addNodeToMatch("")); + elu = addNodeToMatch("Elu", div); + mul = addNodeToMatch("Mul", addNodeToMatch(""), elu); setFusedNode("Celu", input); } @@ -587,16 +906,15 @@ public: } virtual bool match(const Ptr& net, int nodeId, - std::vector& matchedNodesIds, - std::vector& targetNodesIds) CV_OVERRIDE + std::vector& matchedNodesIds) CV_OVERRIDE { - if (Subgraph::match(net, nodeId, matchedNodesIds, targetNodesIds)) + if (Subgraph::match(net, nodeId, matchedNodesIds)) { - float alpha_div = extractAlpha(net, matchedNodesIds[0], 1); - float alpha_mul = extractAlpha(net, matchedNodesIds[2], 0); + float alpha_div = extractAlpha(net, matchedNodesIds[div], 1); + float alpha_mul = extractAlpha(net, matchedNodesIds[mul], 0); float alpha_elu = 1.f; - Ptr elu_ptr = net->getNode(matchedNodesIds[1]); + Ptr elu_ptr = net->getNode(matchedNodesIds[elu]); opencv_onnx::NodeProto* elu_node = elu_ptr.dynamicCast()->node; for (int i = 0; i < elu_node->attribute_size(); i++) @@ -625,18 +943,18 @@ public: protected: float alpha; + int div, mul, elu; }; class NormalizeSubgraphBase : public Subgraph { public: - NormalizeSubgraphBase(int _normNodeOrder = 0) : axis(1), normNodeOrder(_normNodeOrder) {} + NormalizeSubgraphBase(int _normNodeOrder = 1) : axis(1), normNodeOrder(_normNodeOrder) {} virtual bool match(const Ptr& net, int nodeId, - std::vector& matchedNodesIds, - std::vector& targetNodesIds) CV_OVERRIDE + std::vector& matchedNodesIds) CV_OVERRIDE { - if (Subgraph::match(net, nodeId, matchedNodesIds, targetNodesIds)) + if (Subgraph::match(net, nodeId, matchedNodesIds)) { Ptr norm = net->getNode(matchedNodesIds[normNodeOrder]); opencv_onnx::NodeProto* node = norm.dynamicCast()->node; @@ -725,7 +1043,7 @@ public: class NormalizeSubgraph3 : public NormalizeSubgraphBase { public: - NormalizeSubgraph3() : NormalizeSubgraphBase(1) + NormalizeSubgraph3() : NormalizeSubgraphBase(3) { int input = addNodeToMatch(""); int power = addNodeToMatch("Constant"); @@ -743,7 +1061,7 @@ public: class NormalizeSubgraph4 : public NormalizeSubgraphBase { public: - NormalizeSubgraph4() : NormalizeSubgraphBase(1) + NormalizeSubgraph4() : NormalizeSubgraphBase(2) { int input = addNodeToMatch(""); int mul = addNodeToMatch("Mul", input, input); @@ -760,7 +1078,7 @@ public: class NormalizeSubgraph5 : public NormalizeSubgraphBase { public: - NormalizeSubgraph5() : NormalizeSubgraphBase(1) + NormalizeSubgraph5() : NormalizeSubgraphBase(2) { int input = addNodeToMatch(""); int mul = addNodeToMatch("Mul", input, input); @@ -781,25 +1099,24 @@ public: { int input = addNodeToMatch(""); int index = addNodeToMatch("Constant"); - int gather = addNodeToMatch("Gather", input, index); - addNodeToMatch("Cast", gather); + gather = addNodeToMatch("Gather", input, index); + cast = addNodeToMatch("Cast", gather); setFusedNode("Gather", input, index); } virtual bool match(const Ptr& net, int nodeId, - std::vector& matchedNodesIds, - std::vector& targetNodesIds) CV_OVERRIDE + std::vector& matchedNodesIds) CV_OVERRIDE { - bool retVal = Subgraph::match(net, nodeId, matchedNodesIds, targetNodesIds); + bool retVal = Subgraph::match(net, nodeId, matchedNodesIds); size_t matchedNodesNum = matchedNodesIds.size(); // Now we check if merging can be made for these Gather and Cast nodes if (!retVal || matchedNodesNum < 2) return retVal; else { - int nodeToMatch = matchedNodesIds[matchedNodesNum - 1]; + int nodeToMatch = matchedNodesIds[cast]; const Ptr node = net->getNode(nodeToMatch); if (node->getType() == "Cast") { - int inpNodeId = matchedNodesIds[matchedNodesNum - 2]; + int inpNodeId = matchedNodesIds[gather]; const Ptr inpNode = net->getNode(inpNodeId); if (inpNode->getType() == "Gather") { int numNodes = net->getNumNodes(); @@ -819,6 +1136,9 @@ public: } return retVal; } + +private: + int cast, gather; }; /* Constant folding shape for Expand. @@ -838,12 +1158,12 @@ public: { int input = addNodeToMatch(""); int values = addNodeToMatch(""); - int init = addNodeToMatch("ConstantOfShape", values); + init = addNodeToMatch("ConstantOfShape", values); int coeff = addNodeToMatch("Constant"); - int mul = addNodeToMatch("Mul", init, coeff); + mul = addNodeToMatch("Mul", init, coeff); int shape = addNodeToMatch("Constant"); - int condition = addNodeToMatch("Equal", shape, mul); - int where = addNodeToMatch("Where", condition, init, addNodeToMatch("Constant")); + condition = addNodeToMatch("Equal", shape, mul); + where = addNodeToMatch("Where", condition, init, addNodeToMatch("Constant")); addNodeToMatch("Expand", input, where); setFusedNode("Expand", input, shape); } @@ -872,53 +1192,28 @@ public: return 0; } - static std::vector extractConstant(const Ptr& net, int node_id, int input_id) - { - auto onnx_net = net.dynamicCast(); - int initializer_id = onnx_net->getInputInitializerId(node_id, input_id); - Mat mat_constant; - if (initializer_id != -1) // initializer - { - mat_constant = onnx_net->getMatFromInitializer(initializer_id); - } - else - { - const Ptr node = net->getNode(node_id); - int constant_id = getInputNodeId(net, node, input_id); - Ptr constant_ptr = net->getNode(constant_id); - opencv_onnx::NodeProto* constant_node = constant_ptr.dynamicCast()->node; - opencv_onnx::TensorProto constant_proto = constant_node->attribute(0).t(); - mat_constant = getMatFromTensor(constant_proto); - } - - std::vector retvals{mat_constant.begin(), mat_constant.end()}; - return retvals; - } - virtual bool match(const Ptr& net, int nodeId, - std::vector& matchedNodesIds, - std::vector& targetNodesIds) CV_OVERRIDE { - if (Subgraph::match(net, nodeId, matchedNodesIds, targetNodesIds)) { + std::vector& matchedNodesIds) CV_OVERRIDE { + if (Subgraph::match(net, nodeId, matchedNodesIds)) { int64_t value_ConstantOfShape; - if (!extractValue(net, matchedNodesIds[0], value_ConstantOfShape)) { + if (!extractValue(net, matchedNodesIds[init], value_ConstantOfShape)) { return false; } - std::vector input_ConstantOfShape = extractConstant(net, matchedNodesIds[0], 0); + std::vector input_ConstantOfShape = extractConstant(net, matchedNodesIds[init], 0); if (input_ConstantOfShape.size() != static_cast(1)) { return false; } - - auto B_Mul = extractConstant(net, matchedNodesIds[1], 1); + std::vector B_Mul = extractConstant(net, matchedNodesIds[mul], 1); if (B_Mul.size() != static_cast(1)) { return false; } - auto A_Equal = extractConstant(net, matchedNodesIds[2], 0); + std::vector A_Equal = extractConstant(net, matchedNodesIds[condition], 0); if (A_Equal.size() != static_cast(input_ConstantOfShape[0])) { return false; } - auto Y_Where = extractConstant(net, matchedNodesIds[3], 2); + std::vector Y_Where = extractConstant(net, matchedNodesIds[where], 2); if (Y_Where.size() != A_Equal.size()) { return false; } @@ -969,6 +1264,9 @@ public: protected: std::vector shape; + +private: + int init, mul, condition, where; }; class MishSubgraph : public Subgraph @@ -979,7 +1277,7 @@ public: int input = addNodeToMatch(""); int softplus = addNodeToMatch("Softplus", input); int tanh = addNodeToMatch("Tanh", softplus); - addNodeToMatch("Mul", input, tanh); + addNodeToMatch("Mul", tanh, input); setFusedNode("Mish", input); } }; @@ -999,20 +1297,6 @@ public: } }; -class SoftplusSubgraph2: public Subgraph -{ -public: - SoftplusSubgraph2() - { - int input = addNodeToMatch(""); - int exp = addNodeToMatch("Exp", input); - int addVal = addNodeToMatch(""); - int add = addNodeToMatch("Add", exp, addVal); - addNodeToMatch("Log", add); - setFusedNode("Softplus", input); - } -}; - class MulCastSubgraph : public Subgraph { public: @@ -1110,7 +1394,11 @@ public: ResizeSubgraph1() : ExtractScalesSubgraph() { int shape = addNodeToMatch("Shape", input); - int slice = addNodeToMatch("Slice", shape, addNodeToMatch("Constant"), addNodeToMatch("Constant"), addNodeToMatch("Constant")); + int slice = addNodeToMatch("Slice", {shape, + addNodeToMatch(""), + addNodeToMatch(""), + addNodeToMatch(""), + addNodeToMatch("")}); int castConcat = addNodeToMatch("Cast", concatId); int concat = addNodeToMatch("Concat", slice, castConcat); @@ -1136,6 +1424,37 @@ public: } }; +class ResizeSubgraph3 : public Subgraph +{ +public: + ResizeSubgraph3() : Subgraph() + { + int shapeSrc = addNodeToMatch(""); + int input = addNodeToMatch(""); + + int shape_h = addNodeToMatch("Shape", shapeSrc); + int shape_w = addNodeToMatch("Shape", shapeSrc); + int gather_h = addNodeToMatch("Gather", shape_h, addNodeToMatch("Constant")); + int gather_w = addNodeToMatch("Gather", shape_w, addNodeToMatch("Constant")); + int unsqueeze_h = addNodeToMatch("Unsqueeze", gather_h); + int unsqueeze_w = addNodeToMatch("Unsqueeze", gather_w); + int concat1 = addNodeToMatch("Concat", unsqueeze_h, unsqueeze_w); + int cast = addNodeToMatch("Cast", concat1); + + int shape2 = addNodeToMatch("Shape", input); + int slice = addNodeToMatch("Slice", {shape2, + addNodeToMatch(""), + addNodeToMatch(""), + addNodeToMatch(""), + addNodeToMatch("")}); + int concat2 = addNodeToMatch("Concat", slice, cast); + addNodeToMatch("Resize", input, addNodeToMatch("Constant"), addNodeToMatch("Constant"), concat2); + + setFusedNode("Upsample", input, shapeSrc); + } +}; + + class BatchNormalizationSubgraphBase : public Subgraph { public: @@ -1199,6 +1518,8 @@ public: void simplifySubgraphs(opencv_onnx::GraphProto& net) { std::vector > subgraphs; + subgraphs.push_back(makePtr(3)); + subgraphs.push_back(makePtr(4)); subgraphs.push_back(makePtr()); subgraphs.push_back(makePtr()); subgraphs.push_back(makePtr()); @@ -1207,6 +1528,7 @@ void simplifySubgraphs(opencv_onnx::GraphProto& net) subgraphs.push_back(makePtr()); subgraphs.push_back(makePtr()); subgraphs.push_back(makePtr()); + subgraphs.push_back(makePtr()); subgraphs.push_back(makePtr()); subgraphs.push_back(makePtr()); subgraphs.push_back(makePtr()); @@ -1220,10 +1542,13 @@ void simplifySubgraphs(opencv_onnx::GraphProto& net) subgraphs.push_back(makePtr()); subgraphs.push_back(makePtr()); subgraphs.push_back(makePtr()); - subgraphs.push_back(makePtr()); subgraphs.push_back(makePtr()); subgraphs.push_back(makePtr()); subgraphs.push_back(makePtr()); + if (getParam_DNN_BACKEND_DEFAULT() == DNN_BACKEND_OPENCV) { + subgraphs.push_back(makePtr()); + subgraphs.push_back(makePtr()); + } simplifySubgraphs(Ptr(new ONNXGraphWrapper(net)), subgraphs); } diff --git a/modules/dnn/src/onnx/onnx_importer.cpp b/modules/dnn/src/onnx/onnx_importer.cpp index 3031c599b1..09ebce7476 100644 --- a/modules/dnn/src/onnx/onnx_importer.cpp +++ b/modules/dnn/src/onnx/onnx_importer.cpp @@ -207,6 +207,7 @@ private: void parseQConcat (LayerParams& layerParams, const opencv_onnx::NodeProto& node_proto); void parseQGemm (LayerParams& layerParams, const opencv_onnx::NodeProto& node_proto); void parseQSoftmax (LayerParams& layerParams, const opencv_onnx::NodeProto& node_proto); + void parseAttention (LayerParams& layerParams, const opencv_onnx::NodeProto& node_proto); // '???' domain or '???' layer type void parseCustomLayer (LayerParams& layerParams, const opencv_onnx::NodeProto& node_proto); @@ -1235,7 +1236,7 @@ void ONNXImporter::parseSlice(LayerParams& layerParams, const opencv_onnx::NodeP starts_ = DictValue::arrayInt(start_blob.begin(), start_blob.total()); ends_ = DictValue::arrayInt(end_blob.begin(), end_blob.total()); - if (inp_size > 3) + if (inp_size > 3 && !getBlob(node_proto, 3).empty()) { Mat axes_blob = getBlob(node_proto, 3); CV_Assert(axes_blob.total() == start_blob.total()); @@ -1244,7 +1245,7 @@ void ONNXImporter::parseSlice(LayerParams& layerParams, const opencv_onnx::NodeP has_axes = true; } - if (inp_size == 5) + if (inp_size == 5 && !getBlob(node_proto, 4).empty()) { Mat step_blob = getBlob(node_proto, 4); CV_Assert(step_blob.total() == start_blob.total()); @@ -1848,44 +1849,43 @@ void ONNXImporter::parseLRN(LayerParams& layerParams, const opencv_onnx::NodePro addLayer(layerParams, node_proto); } -void ONNXImporter::parseInstanceNormalization(LayerParams& layerParams, const opencv_onnx::NodeProto& node_proto_) -{ - opencv_onnx::NodeProto node_proto = node_proto_; - if (node_proto.input_size() != 3) - CV_Error(Error::StsNotImplemented, - "Expected input, scale, bias"); +void ONNXImporter::parseInstanceNormalization(LayerParams& layerParams, const opencv_onnx::NodeProto& node_proto) { + int num_inputs = node_proto.input_size(); + CV_CheckEQ(num_inputs, 3, "DNN/ONNXImporter - InstanceNorm: three inputs are required"); - layerParams.blobs.resize(4); - layerParams.blobs[2] = getBlob(node_proto, 1); // weightData - layerParams.blobs[3] = getBlob(node_proto, 2); // biasData - layerParams.set("has_bias", true); - layerParams.set("has_weight", true); + bool found_input = constBlobs.find(node_proto.input(0)) != constBlobs.end(); + bool found_scale = constBlobs.find(node_proto.input(1)) != constBlobs.end(); + bool found_bias = constBlobs.find(node_proto.input(2)) != constBlobs.end(); - // Get number of channels in input - int size = layerParams.blobs[2].total(); - layerParams.blobs[0] = Mat::zeros(size, 1, CV_32F); // mean - layerParams.blobs[1] = Mat::ones(size, 1, CV_32F); // std + if (found_input && found_scale && found_bias) { + std::vector inputs, output; - LayerParams mvnParams; - mvnParams.name = layerParams.name + "/MVN"; - mvnParams.type = "MVN"; - mvnParams.set("eps", layerParams.get("epsilon")); - layerParams.erase("epsilon"); + Mat input = getBlob(node_proto, 0); + Mat scale = getBlob(node_proto, 1); + Mat bias = getBlob(node_proto, 2); + inputs.push_back(input); + inputs.push_back(scale); + inputs.push_back(bias); - //Create MVN layer - int id = dstNet.addLayer(mvnParams.name, mvnParams.type, mvnParams); - //Connect to input - IterLayerId_t layerId = layer_id.find(node_proto.input(0)); - CV_Assert(layerId != layer_id.end()); - dstNet.connect(layerId->second.layerId, layerId->second.outputId, id, 0); - //Add shape - layer_id.insert(std::make_pair(mvnParams.name, LayerInfo(id, 0))); - outShapes[mvnParams.name] = outShapes[node_proto.input(0)]; + runLayer(layerParams, inputs, output); + addConstant(node_proto.output(0), output[0]); + } else { + auto add_const_node = [&] (int i) { + LayerParams const_params; + const_params.name = node_proto.input(i); + const_params.type = "Const"; + Mat blob = getBlob(node_proto, i); + const_params.blobs.push_back(blob); - //Replace Batch Norm's input to MVN - node_proto.set_input(0, mvnParams.name); - layerParams.type = "BatchNorm"; - addLayer(layerParams, node_proto); + opencv_onnx::NodeProto proto; + proto.add_output(const_params.name); + addLayer(const_params, proto); + }; + if (found_input && layer_id.find(node_proto.input(0)) == layer_id.end()) { add_const_node(0); } + if (found_scale && layer_id.find(node_proto.input(1)) == layer_id.end()) { add_const_node(1); } + if (found_bias && layer_id.find(node_proto.input(2)) == layer_id.end()) { add_const_node(2); } + addLayer(layerParams, node_proto); + } } void ONNXImporter::parseBatchNormalization(LayerParams& layerParams, const opencv_onnx::NodeProto& node_proto) @@ -1962,50 +1962,33 @@ void ONNXImporter::parseGemm(LayerParams& layerParams, const opencv_onnx::NodePr addLayer(layerParams, node_proto); } -void ONNXImporter::parseMatMul(LayerParams& layerParams, const opencv_onnx::NodeProto& node_proto_) -{ - opencv_onnx::NodeProto node_proto = node_proto_; - CV_Assert(node_proto.input_size() == 2); - layerParams.type = "InnerProduct"; - layerParams.set("bias_term", false); - int firstInpDims, secondInpDims; +void ONNXImporter::parseMatMul(LayerParams& layerParams, const opencv_onnx::NodeProto& node_proto_) { + auto node_proto = node_proto_; + CV_CheckEQ(node_proto.input_size(), 2, "ONNXImporter/MatMul: two inputs required"); - if (constBlobs.find(node_proto.input(0)) != constBlobs.end()) - { - Mat blob = getBlob(node_proto, 0); - firstInpDims = blob.dims; - LayerParams constParams; - constParams.name = layerParams.name + "/const_0"; - constParams.type = "Const"; - constParams.blobs.push_back(blob); + for (int i = 0; i < node_proto.input_size(); i++) { + if (constBlobs.find(node_proto.input(i)) == constBlobs.end()) { + continue; + } - opencv_onnx::NodeProto tmpProto; - tmpProto.add_output(constParams.name); - addLayer(constParams, tmpProto); + Mat blob = getBlob(node_proto, i); - node_proto.set_input(0, constParams.name); + if (i == 1) { + layerParams.blobs.push_back(blob); + } else { + LayerParams const_params; + const_params.name = node_proto.input(i); + const_params.type = "Const"; + const_params.blobs.push_back(blob); + + opencv_onnx::NodeProto const_node_proto; + const_node_proto.add_output(const_params.name); + addLayer(const_params, const_node_proto); + + node_proto.set_input(i, const_params.name); + } } - else - firstInpDims = outShapes[node_proto.input(0)].size(); - if (constBlobs.find(node_proto.input(1)) != constBlobs.end()) - { - Mat blob = getBlob(node_proto, 1); - Mat transBlob; - secondInpDims = blob.dims; - // create order transposing last 2 dimensions - std::vector order(secondInpDims); - std::iota(order.begin(), order.end(), 0); - std::swap(order[secondInpDims - 2], order[secondInpDims - 1]); - transposeND(blob, order, transBlob); - layerParams.blobs.push_back(transBlob); - int numOutput = layerParams.blobs[0].total(0, secondInpDims - 1); - layerParams.set("num_output", numOutput); - layerParams.set("is_matmul", secondInpDims > 2); - } else - secondInpDims = outShapes[node_proto.input(1)].size(); - - layerParams.set("axis", firstInpDims - 1); addLayer(layerParams, node_proto); } @@ -2789,6 +2772,13 @@ void ONNXImporter::parseUpsample(LayerParams& layerParams, const opencv_onnx::No void ONNXImporter::parseSoftMax(LayerParams& layerParams, const opencv_onnx::NodeProto& node_proto) { const std::string& layer_type = node_proto.op_type(); + int axis; + if (onnx_opset != 0 && onnx_opset <= 11) { + axis = layerParams.get("axis", 1); + } else { + axis = layerParams.get("axis", -1); + } + layerParams.set("axis", axis); layerParams.type = "Softmax"; layerParams.set("log_softmax", layer_type == "LogSoftmax"); addLayer(layerParams, node_proto); @@ -3182,10 +3172,19 @@ void ONNXImporter::parseLayerNorm(LayerParams& layerParams, const opencv_onnx::N // Remove additional outputs (Mean, InvStdDev) if (node_proto.output_size() > 1) { + // remove from graph proto + for (size_t i = 1; i < node_proto.output_size(); i++) { + for (int j = graph_proto.output_size() - 1; j >= 0; j--) { + if (graph_proto.output(j).name() == node_proto.output(i)) { + graph_proto.mutable_output()->DeleteSubrange(j, 1); + break; + } + } + } + // remove from node proto auto outputName = node_proto.output(0); opencv_onnx::NodeProto node_proto_ = node_proto; - node_proto_.clear_output(); - node_proto_.add_output(outputName); + node_proto_.mutable_output()->DeleteSubrange(1, node_proto_.output_size() - 1); addLayer(layerParams, node_proto_); } else @@ -3222,19 +3221,44 @@ void ONNXImporter::parseSimpleLayers(LayerParams& layerParams, const opencv_onnx addLayer(layerParams, node_proto); } - void ONNXImporter::parseEinsum(LayerParams& layerParams, const opencv_onnx::NodeProto& node_proto) { std::vector einsumInpShapes; for (int j = 0; j < node_proto.input_size(); j++) { - const auto& inputLayerName = node_proto.input(j); - auto it = outShapes.find(inputLayerName); - if (it != outShapes.end()) - { - einsumInpShapes.emplace_back(it->second); + // create Const layer for constants and mark its shape + std::vector input_shape; + if (layer_id.find(node_proto.input(j)) == layer_id.end()) { + Mat blob = getBlob(node_proto, j); + + LayerParams const_params; + const_params.name = node_proto.input(j); + const_params.type = "Const"; + const_params.blobs.push_back(blob); + + opencv_onnx::NodeProto proto; + proto.add_output(const_params.name); + addLayer(const_params, proto); + + input_shape.resize(blob.dims); + for (size_t i = 0; i < input_shape.size(); i++) { + input_shape[i] = blob.size[i]; + } + } + + // also try getting shape from inferred shapes + if (input_shape.empty()) { + const auto& inputLayerName = node_proto.input(j); + auto it = outShapes.find(inputLayerName); + if (it != outShapes.end()) { + input_shape = it->second; + } + } + + if (input_shape.empty()) { + CV_Error(Error::StsAssert, format("ERROR input shape of %s not found", node_proto.input(j).c_str())); } else { - CV_Error(Error::StsAssert, "ERROR input shape not found"); + einsumInpShapes.emplace_back(input_shape); } } @@ -3525,7 +3549,7 @@ void ONNXImporter::parseQGemm(LayerParams& layerParams, const opencv_onnx::NodeP Mat bias; if (constBlobs.find(node_proto.input(6)) != constBlobs.end()) bias = getBlob(node_proto, 6); - else + if (bias.empty()) bias = Mat::zeros(1, outCn, CV_32S); Mat biasFused(1, outCn, CV_32S); @@ -3897,6 +3921,31 @@ void ONNXImporter::parseQSoftmax(LayerParams& layerParams, const opencv_onnx::No addLayer(layerParams, node_proto); } +void ONNXImporter::parseAttention(LayerParams& params, const opencv_onnx::NodeProto& node_proto) { + CV_CheckTrue(params.has("num_heads"), "ONNXImporter/parseAttention: num_heads is required but missing"); + CV_CheckTrue(params.has("qkv_hidden_sizes"), "ONNXImporter/parseAttention: qkv_hidden_sizes is required but missing"); + + auto param_qkv_hidden_sizes = params.get("qkv_hidden_sizes"); + CV_CheckEQ(param_qkv_hidden_sizes.size(), 3, "ONNXImporter/parseAttention: qkv_hidden_sizes is must and only have three elements"); + + for (size_t i = 1; i < node_proto.input_size(); i++) { + if (layer_id.find(node_proto.input(i)) == layer_id.end()) { + Mat tensor = getBlob(node_proto, i); + + LayerParams const_params; + const_params.name = node_proto.input(i); + const_params.type = "Const"; + const_params.blobs.push_back(tensor); + + opencv_onnx::NodeProto proto; + proto.add_output(const_params.name); + addLayer(const_params, proto); + } + } + + addLayer(params, node_proto); +} + // Domain: ai.onnx (default) // URL: https://github.com/onnx/onnx/blob/master/docs/Operators.md void ONNXImporter::buildDispatchMap_ONNX_AI(int opset_version) @@ -3948,7 +3997,7 @@ void ONNXImporter::buildDispatchMap_ONNX_AI(int opset_version) dispatch["Concat"] = &ONNXImporter::parseConcat; dispatch["Resize"] = &ONNXImporter::parseResize; dispatch["Upsample"] = &ONNXImporter::parseUpsample; - dispatch["SoftMax"] = dispatch["LogSoftmax"] = &ONNXImporter::parseSoftMax; + dispatch["SoftMax"] = dispatch["Softmax"] = dispatch["LogSoftmax"] = &ONNXImporter::parseSoftMax; dispatch["DetectionOutput"] = &ONNXImporter::parseDetectionOutput; dispatch["CumSum"] = &ONNXImporter::parseCumSum; dispatch["SpaceToDepth"] = dispatch["DepthToSpace"] = &ONNXImporter::parseDepthToSpace; @@ -3967,7 +4016,7 @@ void ONNXImporter::buildDispatchMap_ONNX_AI(int opset_version) std::vector simpleLayers{"Acos", "Acosh", "Asin", "Asinh", "Atan", "Atanh", "Ceil", "Celu", "Cos", "Cosh", "Dropout", "Erf", "Exp", "Floor", "HardSigmoid", "HardSwish", - "Identity", "Log", "Round", "Reciprocal", "Selu", "Sign", "Sigmoid", "Sin", "Sinh", "Softmax", + "Identity", "Log", "Round", "Reciprocal", "Selu", "Sign", "Sigmoid", "Sin", "Sinh", "Softplus", "Softsign", "Shrink", "Sqrt", "Tan", "ThresholdedRelu", "Gelu", "GeluApproximation"}; for (const auto& name : simpleLayers) @@ -3980,6 +4029,11 @@ void ONNXImporter::buildDispatchMap_ONNX_AI(int opset_version) dispatch["QLinearConv"] = &ONNXImporter::parseQConv; dispatch["QLinearMatMul"] = &ONNXImporter::parseQMatMul; + // com.microsft: This operator is added for compatibility via onnx graph simplifier. + // Opset domain cannot be modified from onnx_graph_simplifier.cpp so this + // operator cannot be parsed if only added in buildDispatchMap_COM_MICROSOFT + dispatch["Attention"] = &ONNXImporter::parseAttention; + domain_dispatch_map[str_domain_ai_onnx] = dispatch; } @@ -3997,6 +4051,7 @@ void ONNXImporter::buildDispatchMap_COM_MICROSOFT(int opset_version) dispatch["QLinearConcat"] = &ONNXImporter::parseQConcat; dispatch["QGemm"] = &ONNXImporter::parseQGemm; dispatch["QLinearSoftmax"] = &ONNXImporter::parseQSoftmax; + dispatch["Attention"] = &ONNXImporter::parseAttention; domain_dispatch_map["com.microsoft"] = dispatch; } diff --git a/modules/dnn/src/opencl/conv_layer_spatial.cl b/modules/dnn/src/opencl/conv_layer_spatial.cl index c9ddacfb8e..0ac074b26b 100644 --- a/modules/dnn/src/opencl/conv_layer_spatial.cl +++ b/modules/dnn/src/opencl/conv_layer_spatial.cl @@ -95,23 +95,23 @@ #define __CAT(x, y) x##y #define CAT(x, y) __CAT(x, y) #define LOOP0(VAR, STMT) -#define LOOP1(VAR, STMT) (STMT); (VAR)++; -#define LOOP2(VAR, STMT) LOOP1(VAR, STMT); (STMT); (VAR)++; -#define LOOP3(VAR, STMT) LOOP2(VAR, STMT); (STMT); (VAR)++; -#define LOOP4(VAR, STMT) LOOP3(VAR, STMT); (STMT); (VAR)++; -#define LOOP5(VAR, STMT) LOOP4(VAR, STMT); (STMT); (VAR)++; -#define LOOP6(VAR, STMT) LOOP5(VAR, STMT); (STMT); (VAR)++; -#define LOOP7(VAR, STMT) LOOP6(VAR, STMT); (STMT); (VAR)++; -#define LOOP8(VAR, STMT) LOOP7(VAR, STMT); (STMT); (VAR)++; -#define LOOP9(VAR, STMT) LOOP8(VAR, STMT); (STMT); (VAR)++; -#define LOOP10(VAR, STMT) LOOP9(VAR, STMT); (STMT); (VAR)++; -#define LOOP11(VAR, STMT) LOOP10(VAR, STMT); (STMT); (VAR)++; -#define LOOP12(VAR, STMT) LOOP11(VAR, STMT); (STMT); (VAR)++; -#define LOOP13(VAR, STMT) LOOP12(VAR, STMT); (STMT); (VAR)++; -#define LOOP14(VAR, STMT) LOOP13(VAR, STMT); (STMT); (VAR)++; -#define LOOP15(VAR, STMT) LOOP14(VAR, STMT); (STMT); (VAR)++; -#define LOOP16(VAR, STMT) LOOP15(VAR, STMT); (STMT); (VAR)++; -#define LOOP(N, VAR, STMT) CAT(LOOP, N)((VAR), (STMT)) +#define LOOP1(VAR, STMT) STMT; (VAR)++; +#define LOOP2(VAR, STMT) LOOP1(VAR, STMT); STMT; (VAR)++; +#define LOOP3(VAR, STMT) LOOP2(VAR, STMT); STMT; (VAR)++; +#define LOOP4(VAR, STMT) LOOP3(VAR, STMT); STMT; (VAR)++; +#define LOOP5(VAR, STMT) LOOP4(VAR, STMT); STMT; (VAR)++; +#define LOOP6(VAR, STMT) LOOP5(VAR, STMT); STMT; (VAR)++; +#define LOOP7(VAR, STMT) LOOP6(VAR, STMT); STMT; (VAR)++; +#define LOOP8(VAR, STMT) LOOP7(VAR, STMT); STMT; (VAR)++; +#define LOOP9(VAR, STMT) LOOP8(VAR, STMT); STMT; (VAR)++; +#define LOOP10(VAR, STMT) LOOP9(VAR, STMT); STMT; (VAR)++; +#define LOOP11(VAR, STMT) LOOP10(VAR, STMT); STMT; (VAR)++; +#define LOOP12(VAR, STMT) LOOP11(VAR, STMT); STMT; (VAR)++; +#define LOOP13(VAR, STMT) LOOP12(VAR, STMT); STMT; (VAR)++; +#define LOOP14(VAR, STMT) LOOP13(VAR, STMT); STMT; (VAR)++; +#define LOOP15(VAR, STMT) LOOP14(VAR, STMT); STMT; (VAR)++; +#define LOOP16(VAR, STMT) LOOP15(VAR, STMT); STMT; (VAR)++; +#define LOOP(N, VAR, STMT) CAT(LOOP, N)(VAR, STMT) #if defined(convolve_simd) || defined(Conv_Interleaved) #if TYPE == TYPE_HALF diff --git a/modules/dnn/src/opencl/mvn.cl b/modules/dnn/src/opencl/mvn.cl index f84d04502c..7353ed8b82 100644 --- a/modules/dnn/src/opencl/mvn.cl +++ b/modules/dnn/src/opencl/mvn.cl @@ -126,12 +126,18 @@ __kernel void MVN(__global const Dtype* src, alpha = 1; #endif +#ifdef LAYER_NORM + vec_type w = load(bnorm_weight, y), b = load(bnorm_bias, y); +#else + Dtype w = 1.f, b = 0.f; #ifdef FUSE_BATCH_NORM w = bnorm_weight[x % channels]; b = bnorm_bias[x % channels]; #endif +#endif // LAYER_NORM + vec_type src_vec = load(src, index) - (vec_type)mean_val; vec_type dst_vec = src_vec * alpha; dst_vec = dst_vec * w + (vec_type)b; diff --git a/modules/dnn/src/tensorflow/tf_graph_simplifier.cpp b/modules/dnn/src/tensorflow/tf_graph_simplifier.cpp index 5531b28111..8ba1963512 100644 --- a/modules/dnn/src/tensorflow/tf_graph_simplifier.cpp +++ b/modules/dnn/src/tensorflow/tf_graph_simplifier.cpp @@ -98,6 +98,14 @@ public: net.mutable_node()->DeleteSubrange(idx, 1); } + virtual inline bool isCommutativeOp(const std::string& type) const CV_OVERRIDE + { + return type == "Add" || type == "Sum" || + type == "Mul" || type == "Prod" || + type == "Max" || type == "Maximum" || type == "Minimum" || + type == "Mean" || type == "SquaredDifference"; + } + tensorflow::GraphDef& net; }; @@ -282,24 +290,26 @@ public: { int input = addNodeToMatch(""); int relu = addNodeToMatch("Relu", input); - int maxValue = addNodeToMatch("Const"); + maxValueId = addNodeToMatch("Const"); int clipValue = addNodeToMatch("Const"); - int minimum = addNodeToMatch("Minimum", relu, maxValue); + int minimum = addNodeToMatch("Minimum", relu, maxValueId); addNodeToMatch("Maximum", minimum, clipValue); setFusedNode("Relu6", input); } virtual bool match(const Ptr& net, int nodeId, - std::vector& matchedNodesIds, - std::vector& targetNodesIds) CV_OVERRIDE + std::vector& matchedNodesIds) CV_OVERRIDE { - if (!Subgraph::match(net, nodeId, matchedNodesIds, targetNodesIds)) + if (!Subgraph::match(net, nodeId, matchedNodesIds)) return false; - tensorflow::NodeDef* node = net->getNode(matchedNodesIds.front() + 1).dynamicCast()->node; + tensorflow::NodeDef* node = net->getNode(matchedNodesIds[maxValueId]).dynamicCast()->node; Mat maxValue = getTensorContent(node->attr().at("value").tensor()); return maxValue.type() == CV_32FC1 && maxValue.total() == 1 && maxValue.at(0) == 6; } + +private: + int maxValueId; }; // Keras' reshape stores output shape in separate Const nodes by one value. @@ -328,15 +338,14 @@ public: } virtual bool match(const Ptr& net, int nodeId, - std::vector& matchedNodesIds, - std::vector& targetNodesIds) CV_OVERRIDE + std::vector& matchedNodesIds) CV_OVERRIDE { Ptr node = net->getNode(nodeId); if (node->getNumInputs() == 0) return false; inpName = node->getInputName(0); - return Subgraph::match(net, nodeId, matchedNodesIds, targetNodesIds); + return Subgraph::match(net, nodeId, matchedNodesIds); } diff --git a/modules/dnn/src/tensorflow/tf_importer.cpp b/modules/dnn/src/tensorflow/tf_importer.cpp index 9ebd2a6b1d..bd7ed3e02c 100644 --- a/modules/dnn/src/tensorflow/tf_importer.cpp +++ b/modules/dnn/src/tensorflow/tf_importer.cpp @@ -2300,6 +2300,12 @@ void TFImporter::parseSoftmax(tensorflow::GraphDef& net, const tensorflow::NodeD CV_CheckGT(num_inputs, 0, ""); if (hasLayerAttr(layer, "axis")) layerParams.set("axis", getLayerAttr(layer, "axis").i()); + // if tf version is 2.x, use axis -1 as default + else if(netBin.has_versions() && (int)netBin.versions().producer() >= 2) + layerParams.set("axis", -1); + // else use axis 1 as default + else + layerParams.set("axis", 1); int id = dstNet.addLayer(name, "Softmax", layerParams); layer_id[name] = id; diff --git a/modules/dnn/test/test_backends.cpp b/modules/dnn/test/test_backends.cpp index f07b0c0e1b..c57faac1a9 100644 --- a/modules/dnn/test/test_backends.cpp +++ b/modules/dnn/test/test_backends.cpp @@ -47,7 +47,10 @@ public: net.setInput(inp); net.setPreferableBackend(backend); net.setPreferableTarget(target); - net.enableWinograd(useWinograd); + + if (target == DNN_TARGET_CPU_FP16) + net.enableWinograd(false); + Mat out = net.forward(outputLayer).clone(); check(outDefault, out, outputLayer, l1, lInf, detectionConfThresh, "First run"); @@ -105,7 +108,7 @@ TEST_P(DNNTestNetwork, ResNet_50) { applyTestTag( (target == DNN_TARGET_CPU ? CV_TEST_TAG_MEMORY_512MB : CV_TEST_TAG_MEMORY_1GB), - CV_TEST_TAG_DEBUG_LONG + CV_TEST_TAG_DEBUG_VERYLONG ); processNet("dnn/ResNet-50-model.caffemodel", "dnn/ResNet-50-deploy.prototxt", @@ -278,8 +281,11 @@ TEST_P(DNNTestNetwork, MobileNet_SSD_v2_TensorFlow) TEST_P(DNNTestNetwork, SSD_VGG16) { - applyTestTag(CV_TEST_TAG_LONG, (target == DNN_TARGET_CPU ? CV_TEST_TAG_MEMORY_1GB : CV_TEST_TAG_MEMORY_2GB), - CV_TEST_TAG_DEBUG_VERYLONG); + applyTestTag( + CV_TEST_TAG_MEMORY_2GB, + CV_TEST_TAG_LONG, + CV_TEST_TAG_DEBUG_VERYLONG + ); Mat sample = imread(findDataFile("dnn/street.png")); Mat inp = blobFromImage(sample, 1.0f, Size(300, 300), Scalar(), false); @@ -373,7 +379,7 @@ TEST_P(DNNTestNetwork, Inception_v2_SSD_TensorFlow) { applyTestTag( (target == DNN_TARGET_CPU ? CV_TEST_TAG_MEMORY_512MB : CV_TEST_TAG_MEMORY_1GB), - CV_TEST_TAG_DEBUG_LONG + CV_TEST_TAG_DEBUG_VERYLONG ); #if defined(INF_ENGINE_RELEASE) if (backend == DNN_BACKEND_INFERENCE_ENGINE_NN_BUILDER_2019 && target == DNN_TARGET_MYRIAD diff --git a/modules/dnn/test/test_caffe_importer.cpp b/modules/dnn/test/test_caffe_importer.cpp index 14c3ffc6d4..4db7796e58 100644 --- a/modules/dnn/test/test_caffe_importer.cpp +++ b/modules/dnn/test/test_caffe_importer.cpp @@ -62,6 +62,10 @@ public: findDataFile("dnn/" + model, false)); net.setPreferableBackend(backend); net.setPreferableTarget(target); + + if (target == DNN_TARGET_CPU_FP16) + net.enableWinograd(false); + Mat img = imread(findDataFile("dnn/dog416.png")); resize(img, img, Size(800, 600)); Mat blob = blobFromImage(img, 1.0, Size(), Scalar(102.9801, 115.9465, 122.7717), false, false); @@ -219,6 +223,9 @@ TEST_P(Reproducibility_AlexNet, Accuracy) net.setPreferableBackend(DNN_BACKEND_OPENCV); net.setPreferableTarget(targetId); + if (targetId == DNN_TARGET_CPU_FP16) + net.enableWinograd(false); + Mat sample = imread(_tf("grace_hopper_227.png")); ASSERT_TRUE(!sample.empty()); @@ -263,7 +270,11 @@ TEST(Reproducibility_FCN, Accuracy) TEST(Reproducibility_SSD, Accuracy) { - applyTestTag(CV_TEST_TAG_MEMORY_512MB, CV_TEST_TAG_DEBUG_LONG); + applyTestTag( + CV_TEST_TAG_MEMORY_512MB, + CV_TEST_TAG_DEBUG_VERYLONG + ); + Net net; { const string proto = findDataFile("dnn/ssd_vgg16.prototxt"); @@ -383,6 +394,9 @@ TEST_P(Reproducibility_ResNet50, Accuracy) net.setPreferableBackend(DNN_BACKEND_OPENCV); net.setPreferableTarget(targetId); + if (targetId == DNN_TARGET_CPU_FP16) + net.enableWinograd(false); + float l1 = (targetId == DNN_TARGET_OPENCL_FP16 || targetId == DNN_TARGET_CPU_FP16) ? 3e-5 : 1e-5; float lInf = (targetId == DNN_TARGET_OPENCL_FP16 || targetId == DNN_TARGET_CPU_FP16) ? 6e-3 : 1e-4; @@ -490,7 +504,10 @@ TEST(Reproducibility_GoogLeNet_fp16, Accuracy) // https://github.com/richzhang/colorization TEST_P(Test_Caffe_nets, Colorization) { - applyTestTag(target == DNN_TARGET_CPU ? CV_TEST_TAG_MEMORY_512MB : CV_TEST_TAG_MEMORY_1GB); + applyTestTag( + target == DNN_TARGET_CPU ? CV_TEST_TAG_MEMORY_512MB : CV_TEST_TAG_MEMORY_1GB, + CV_TEST_TAG_DEBUG_VERYLONG + ); checkBackend(); Mat inp = blobFromNPY(_tf("colorization_inp.npy")); @@ -503,6 +520,10 @@ TEST_P(Test_Caffe_nets, Colorization) net.setPreferableBackend(backend); net.setPreferableTarget(target); + // This model has bad accuracy when the FP16 and Winograd are enable at same time. + if (target == DNN_TARGET_CPU_FP16) + net.enableWinograd(false); + net.getLayer(net.getLayerId("class8_ab"))->blobs.push_back(kernel); net.getLayer(net.getLayerId("conv8_313_rh"))->blobs.push_back(Mat(1, 313, CV_32F, 2.606)); @@ -568,10 +589,15 @@ TEST_P(Test_Caffe_nets, DenseNet_121) { l1 = 0.11; lInf = 0.5; } - else if (target == DNN_TARGET_CUDA_FP16 || target == DNN_TARGET_CPU_FP16) + else if (target == DNN_TARGET_CUDA_FP16) { l1 = 0.04; lInf = 0.2; } + else if (target == DNN_TARGET_CPU_FP16) + { + l1 = 0.06; lInf = 0.3; + } + normAssert(outs[0], ref, "", l1, lInf); if (target != DNN_TARGET_MYRIAD || getInferenceEngineVPUType() != CV_DNN_INFERENCE_ENGINE_VPU_TYPE_MYRIAD_X) expectNoFallbacksFromIE(model.getNetwork_()); @@ -758,7 +784,7 @@ TEST_P(Test_Caffe_nets, FasterRCNN_zf) #else (target == DNN_TARGET_CPU ? CV_TEST_TAG_MEMORY_512MB : CV_TEST_TAG_MEMORY_1GB), #endif - CV_TEST_TAG_DEBUG_LONG + CV_TEST_TAG_DEBUG_VERYLONG ); #if defined(INF_ENGINE_RELEASE) && INF_ENGINE_VER_MAJOR_EQ(2021040000) // IE exception: Ngraph operation Reshape with name rpn_cls_score_reshape has dynamic output shape on 0 port, but CPU plug-in supports only static shape diff --git a/modules/dnn/test/test_darknet_importer.cpp b/modules/dnn/test/test_darknet_importer.cpp index 2c734b4492..ba2a7f14c6 100644 --- a/modules/dnn/test/test_darknet_importer.cpp +++ b/modules/dnn/test/test_darknet_importer.cpp @@ -357,7 +357,8 @@ TEST_P(Test_Darknet_nets, YoloVoc) #else CV_TEST_TAG_MEMORY_1GB, #endif - CV_TEST_TAG_LONG + CV_TEST_TAG_LONG, + CV_TEST_TAG_DEBUG_VERYLONG ); #if defined(INF_ENGINE_RELEASE) && INF_ENGINE_VER_MAJOR_EQ(2020040000) // nGraph compilation failure @@ -473,7 +474,8 @@ TEST_P(Test_Darknet_nets, TinyYoloVoc) 1, 6, 0.928758f, 0.651024f, 0.463539f, 0.823784f, 0.654998f); // a car double scoreDiff = 8e-5, iouDiff = 3e-4; - if (target == DNN_TARGET_OPENCL_FP16 || target == DNN_TARGET_MYRIAD || target == DNN_TARGET_CPU_FP16) + bool useWinograd = true; + if (target == DNN_TARGET_OPENCL_FP16 || target == DNN_TARGET_MYRIAD) { scoreDiff = 8e-3; iouDiff = 0.018; @@ -483,18 +485,24 @@ TEST_P(Test_Darknet_nets, TinyYoloVoc) scoreDiff = 0.008; iouDiff = 0.02; } + else if (target == DNN_TARGET_CPU_FP16) + { + useWinograd = false; + scoreDiff = 8e-3; + iouDiff = 0.018; + } std::string config_file = "tiny-yolo-voc.cfg"; std::string weights_file = "tiny-yolo-voc.weights"; { SCOPED_TRACE("batch size 1"); - testDarknetModel(config_file, weights_file, ref.rowRange(0, 2), scoreDiff, iouDiff); + testDarknetModel(config_file, weights_file, ref.rowRange(0, 2), scoreDiff, iouDiff, 0.24, 0.4, useWinograd); } { SCOPED_TRACE("batch size 2"); - testDarknetModel(config_file, weights_file, ref, scoreDiff, iouDiff); + testDarknetModel(config_file, weights_file, ref, scoreDiff, iouDiff, 0.24, 0.4, useWinograd); } } @@ -890,12 +898,12 @@ TEST_P(Test_Darknet_nets, YOLOv4_tiny) { SCOPED_TRACE("batch size 1"); - testDarknetModel(config_file, weights_file, ref.rowRange(0, N0), scoreDiff, iouDiff, confThreshold); + testDarknetModel(config_file, weights_file, ref.rowRange(0, N0), scoreDiff, iouDiff, confThreshold, 0.4, false); } { SCOPED_TRACE("batch size 2"); - testDarknetModel(config_file, weights_file, ref, scoreDiff, iouDiff, confThreshold); + testDarknetModel(config_file, weights_file, ref, scoreDiff, iouDiff, confThreshold, 0.4, false); } #if defined(INF_ENGINE_RELEASE) @@ -912,10 +920,10 @@ TEST_P(Test_Darknet_nets, YOLOv4_tiny) TEST_P(Test_Darknet_nets, YOLOv4x_mish) { applyTestTag( - CV_TEST_TAG_LONG, - CV_TEST_TAG_MEMORY_2GB, - CV_TEST_TAG_DEBUG_VERYLONG - ); + CV_TEST_TAG_MEMORY_2GB, + CV_TEST_TAG_LONG, + CV_TEST_TAG_DEBUG_VERYLONG + ); #if defined(INF_ENGINE_RELEASE) && INF_ENGINE_VER_MAJOR_EQ(2021040000) // IE exception: Ngraph operation Transpose with name permute_168 has dynamic output shape on 0 port, but CPU plug-in supports only static shape @@ -1042,6 +1050,11 @@ TEST_P(Test_Darknet_layers, avgpool_softmax) testDarknetLayer("avgpool_softmax"); } +TEST_P(Test_Darknet_layers, crop) +{ + testDarknetLayer("crop"); +} + TEST_P(Test_Darknet_layers, region) { #if defined(INF_ENGINE_RELEASE) && INF_ENGINE_VER_MAJOR_LT(2021040000) diff --git a/modules/dnn/test/test_graph_simplifier.cpp b/modules/dnn/test/test_graph_simplifier.cpp new file mode 100644 index 0000000000..e09a68c158 --- /dev/null +++ b/modules/dnn/test/test_graph_simplifier.cpp @@ -0,0 +1,142 @@ +// This file is part of OpenCV project. +// It is subject to the license terms in the LICENSE file found in the top-level directory +// of this distribution and at http://opencv.org/license.html. + +#include "test_precomp.hpp" + +namespace opencv_test { namespace { + +class Test_Graph_Simplifier : public ::testing::Test { + public: + bool required; + + Test_Graph_Simplifier() : required(true) {} + + void test_conformance(const std::string &basename, const std::string &expected_layer) { + test(basename + std::string("/model"), std::vector{expected_layer}, std::string("dnn/onnx/conformance/node/")); + } + + void test(const std::string &basename, const std::string &expected_layer) { + test(basename, std::vector{expected_layer}); + } + + void test(const std::string &basename, const std::vector &expected_layers, const std::string &model_path_prefix = std::string("dnn/onnx/models/")) { + std::string model_path = findDataFile(model_path_prefix + basename + std::string(".onnx"), required); + auto net = readNet(model_path); + std::vector layers; + net.getLayerTypes(layers); + + // remove Const, Identity (output layer), __NetInputLayer__ (input layer) + layers.erase(std::remove_if(layers.begin(), layers.end(), [] (const std::string l) { return l == "Const" || l == "Identity" || l == "__NetInputLayer__"; }), layers.end()); + + EXPECT_EQ(layers, expected_layers); + } +}; + +TEST_F(Test_Graph_Simplifier, GeluSubGraph) { + test("gelu", "Gelu"); + test("bias_gelu", std::vector{"Gelu", "NaryEltwise"}); +} + +TEST_F(Test_Graph_Simplifier, GeluApproximationSubGraph) { + test("gelu_approximation", "GeluApproximation"); +} + +TEST_F(Test_Graph_Simplifier, LayerNormSubGraph) { + test("layer_norm_expanded", "LayerNormalization"); + test("layer_norm_expanded_with_initializers", "LayerNormalization"); +} + +TEST_F(Test_Graph_Simplifier, ResizeSubgraph) { + /* Test for 6 subgraphs: + - GatherCastSubgraph + - MulCastSubgraph + - UpsampleSubgraph + - ResizeSubgraph1 + - ResizeSubgraph2 + - ResizeSubgraph3 + */ + test("upsample_unfused_torch1.2", std::vector{"BatchNorm", "Resize"}); + test("resize_nearest_unfused_opset11_torch1.3", std::vector{"BatchNorm", "Convolution", "Resize"}); + test("resize_nearest_unfused_opset11_torch1.4", std::vector{"BatchNorm", "Convolution", "Resize"}); + test("upsample_unfused_opset9_torch1.4", std::vector{"BatchNorm", "Convolution", "Resize"}); + test("two_resizes_with_shared_subgraphs", std::vector{"NaryEltwise", "Resize"}); +} + +TEST_F(Test_Graph_Simplifier, SoftmaxSubgraph) { + /* Test for 3 subgraphs + - SoftMaxSubgraph + - SoftMaxSubgraph2 (conformance) + - LogSoftMaxSubgraph (conformance) + */ + test("softmax_unfused", "Softmax"); + test_conformance("test_softmax_example_expanded", "Softmax"); + test_conformance("test_softmax_axis_2_expanded", "Softmax"); + test_conformance("test_softmax_default_axis_expanded", "Softmax"); + test_conformance("test_softmax_axis_0_expanded", "Softmax"); + test_conformance("test_softmax_axis_1_expanded", "Softmax"); + test_conformance("test_softmax_large_number_expanded", "Softmax"); + test_conformance("test_softmax_negative_axis_expanded", "Softmax"); + test_conformance("test_logsoftmax_axis_2_expanded", "Softmax"); + test_conformance("test_logsoftmax_example_1_expanded", "Softmax"); + test_conformance("test_logsoftmax_negative_axis_expanded", "Softmax"); + test_conformance("test_logsoftmax_axis_0_expanded", "Softmax"); + test_conformance("test_logsoftmax_axis_1_expanded", "Softmax"); + test_conformance("test_logsoftmax_large_number_expanded", "Softmax"); + test_conformance("test_logsoftmax_default_axis_expanded", "Softmax"); +} + +TEST_F(Test_Graph_Simplifier, HardSwishSubgraph) { + test_conformance("test_hardswish_expanded", "HardSwish"); +} + +TEST_F(Test_Graph_Simplifier, CeluSubgraph) { + test_conformance("test_celu_expanded", "Celu"); +} + +TEST_F(Test_Graph_Simplifier, NormalizeSubgraph) { + /* Test for 6 subgraphs + - NormalizeSubgraph1 + - NormalizeSubgraph2 + - NormalizeSubgraph2_2 + - NormalizeSubgraph3 + - NormalizeSubgraph4 + - NormalizeSubgraph5 + */ + test("reduceL2_subgraph_2", "Normalize"); + test("reduceL2_subgraph", "Normalize"); + test("normalize_fusion", "Normalize"); +} + +TEST_F(Test_Graph_Simplifier, BatchNormalizationSubgraph) { + /* Test for 2 subgraphs + - BatchNormalizationSubgraph1 + - BatchNormalizationSubgraph2 + */ + test("frozenBatchNorm2d", "BatchNorm"); + test("batch_norm_subgraph", "BatchNorm"); +} + +TEST_F(Test_Graph_Simplifier, ExpandSubgraph) { + test("expand_neg_batch", "Expand"); +} + +TEST_F(Test_Graph_Simplifier, MishSubgraph) { + /* Test for 2 subgraphs + - SoftplusSubgraph + - MishSubgraph + */ + test("mish_no_softplus", "Mish"); + test("mish", "Mish"); +} + +TEST_F(Test_Graph_Simplifier, AttentionSubgraph) { + /* Test for 2 subgraphs + - AttentionSubgraph + - AttentionSingleHeadSubgraph + */ + test("attention", "Attention"); + test("attention_single_head", "Attention"); +} + +}} diff --git a/modules/dnn/test/test_int8_layers.cpp b/modules/dnn/test/test_int8_layers.cpp index 075cc4c069..949c354066 100644 --- a/modules/dnn/test/test_int8_layers.cpp +++ b/modules/dnn/test/test_int8_layers.cpp @@ -752,7 +752,10 @@ TEST_P(Test_Int8_nets, GoogLeNet) TEST_P(Test_Int8_nets, ResNet50) { - applyTestTag(target == DNN_TARGET_CPU ? CV_TEST_TAG_MEMORY_512MB : CV_TEST_TAG_MEMORY_1GB); + applyTestTag( + target == DNN_TARGET_CPU ? CV_TEST_TAG_MEMORY_512MB : CV_TEST_TAG_MEMORY_1GB, + CV_TEST_TAG_DEBUG_VERYLONG + ); if (target == DNN_TARGET_OPENCL_FP16 && !ocl::Device::getDefault().isIntel()) applyTestTag(CV_TEST_TAG_DNN_SKIP_OPENCL_FP16); @@ -1204,7 +1207,10 @@ TEST_P(Test_Int8_nets, YoloVoc) TEST_P(Test_Int8_nets, TinyYoloVoc) { - applyTestTag(CV_TEST_TAG_MEMORY_512MB); + applyTestTag( + CV_TEST_TAG_MEMORY_512MB, + CV_TEST_TAG_DEBUG_VERYLONG + ); if (target == DNN_TARGET_OPENCL_FP16 && !ocl::Device::getDefault().isIntel()) applyTestTag(CV_TEST_TAG_DNN_SKIP_OPENCL_FP16); diff --git a/modules/dnn/test/test_misc.cpp b/modules/dnn/test/test_misc.cpp index 2ac683ad55..6b7973db7e 100644 --- a/modules/dnn/test/test_misc.cpp +++ b/modules/dnn/test/test_misc.cpp @@ -13,17 +13,83 @@ namespace opencv_test { namespace { +TEST(blobRectToImageRect, DNN_PMODE_NULL) +{ + Size inputSize(50 + (rand() % 100) / 4 * 4, 50 + (rand() % 100) / 4 * 4); + Size imgSize(200 + (rand() % 100) / 4 * 4, 200 + (rand() % 100) / 4 * 4); + Rect rBlob(inputSize.width / 2 - inputSize.width / 4, inputSize.height / 2 - inputSize.height / 4, inputSize.width / 2, inputSize.height / 2); + Image2BlobParams paramNet; + paramNet.scalefactor = Scalar::all(1.f); + paramNet.size = inputSize; + paramNet.ddepth = CV_32F; + paramNet.mean = Scalar(); + paramNet.swapRB = false; + paramNet.datalayout = DNN_LAYOUT_NHWC; + paramNet.paddingmode = DNN_PMODE_NULL; + Rect rOri = paramNet.blobRectToImageRect(rBlob, imgSize); + Rect rImg = Rect(rBlob.x * (float)imgSize.width / inputSize.width, rBlob.y * (float)imgSize.height / inputSize.height, + rBlob.width * (float)imgSize.width / inputSize.width, rBlob.height * (float)imgSize.height / inputSize.height); + ASSERT_EQ(rImg, rOri); +} + +TEST(blobRectToImageRect, DNN_PMODE_CROP_CENTER) +{ + Size inputSize(50 + (rand() % 100) / 4 * 4, 50 + (rand() % 100) / 4 * 4); + Size imgSize(200 + (rand() % 100) / 4 * 4, 200 + (rand() % 100) / 4 * 4); + Rect rBlob(inputSize.width / 2 - inputSize.width / 4, inputSize.height / 2 - inputSize.height / 4, inputSize.width / 2, inputSize.height / 2); + Image2BlobParams paramNet; + paramNet.scalefactor = Scalar::all(1.f); + paramNet.size = inputSize; + paramNet.ddepth = CV_32F; + paramNet.mean = Scalar(); + paramNet.swapRB = false; + paramNet.datalayout = DNN_LAYOUT_NHWC; + paramNet.paddingmode = DNN_PMODE_CROP_CENTER; + Rect rOri = paramNet.blobRectToImageRect(rBlob, imgSize); + float resizeFactor = std::max(inputSize.width / (float)imgSize.width, + inputSize.height / (float)imgSize.height); + Rect rImg = Rect((rBlob.x + 0.5 * (imgSize.width * resizeFactor - inputSize.width)) / resizeFactor, (rBlob.y + 0.5 * (imgSize.height * resizeFactor - inputSize.height)) / resizeFactor, + rBlob.width / resizeFactor, rBlob.height / resizeFactor); + ASSERT_EQ(rImg, rOri); +} + +TEST(blobRectToImageRect, DNN_PMODE_LETTERBOX) +{ + Size inputSize(50 + (rand() % 100) / 4 * 4, 50 + (rand() % 100) / 4 * 4); + Size imgSize(200 + (rand() % 100) / 4 * 4, 200 + (rand() % 100) / 4 * 4); + Rect rBlob(inputSize.width / 2 - inputSize.width / 4, inputSize.height / 2 - inputSize.height / 4, inputSize.width / 2, inputSize.height / 2); + Image2BlobParams paramNet; + paramNet.scalefactor = Scalar::all(1.f); + paramNet.size = inputSize; + paramNet.ddepth = CV_32F; + paramNet.mean = Scalar(); + paramNet.swapRB = false; + paramNet.datalayout = DNN_LAYOUT_NHWC; + paramNet.paddingmode = DNN_PMODE_LETTERBOX; + Rect rOri = paramNet.blobRectToImageRect(rBlob, imgSize); + float resizeFactor = std::min(inputSize.width / (float)imgSize.width, + inputSize.height / (float)imgSize.height); + int rh = int(imgSize.height * resizeFactor); + int rw = int(imgSize.width * resizeFactor); + + int top = (inputSize.height - rh) / 2; + int left = (inputSize.width - rw) / 2; + Rect rImg = Rect((rBlob.x - left) / resizeFactor, (rBlob.y - top) / resizeFactor, rBlob.width / resizeFactor, rBlob.height / resizeFactor); + ASSERT_EQ(rImg, rOri); +} + + TEST(blobFromImage_4ch, Regression) { Mat ch[4]; - for(int i = 0; i < 4; i++) - ch[i] = Mat::ones(10, 10, CV_8U)*i; + for (int i = 0; i < 4; i++) + ch[i] = Mat::ones(10, 10, CV_8U) * i; Mat img; merge(ch, 4, img); Mat blob = dnn::blobFromImage(img, 1., Size(), Scalar(), false, false); - for(int i = 0; i < 4; i++) + for (int i = 0; i < 4; i++) { ch[i] = Mat(img.rows, img.cols, CV_32F, blob.ptr(0, i)); ASSERT_DOUBLE_EQ(cvtest::norm(ch[i], cv::NORM_INF), i); @@ -32,7 +98,7 @@ TEST(blobFromImage_4ch, Regression) TEST(blobFromImage, allocated) { - int size[] = {1, 3, 4, 5}; + int size[] = { 1, 3, 4, 5 }; Mat img(size[2], size[3], CV_32FC(size[1])); Mat blob(4, size, CV_32F); void* blobData = blob.data; @@ -66,8 +132,8 @@ TEST(imagesFromBlob, Regression) TEST(blobFromImageWithParams_4ch, NHWC_scalar_scale) { - Mat img(10, 10, CV_8UC4, cv::Scalar(0,1,2,3)); - std::vector factorVec = {0.1, 0.2, 0.3, 0.4}; + Mat img(10, 10, CV_8UC4, cv::Scalar(0, 1, 2, 3)); + std::vector factorVec = { 0.1, 0.2, 0.3, 0.4 }; Scalar scalefactor(factorVec[0], factorVec[1], factorVec[2], factorVec[3]); @@ -77,7 +143,7 @@ TEST(blobFromImageWithParams_4ch, NHWC_scalar_scale) Mat blob = dnn::blobFromImageWithParams(img, param); // [1, 10, 10, 4] float* blobPtr = blob.ptr(0); - std::vector targetVec = {(float )factorVec[0] * 0, (float )factorVec[1] * 1, (float )factorVec[2] * 2, (float )factorVec[3] * 3}; // Target Value. + std::vector targetVec = { (float)factorVec[0] * 0, (float)factorVec[1] * 1, (float)factorVec[2] * 2, (float)factorVec[3] * 3 }; // Target Value. for (int hi = 0; hi < 10; hi++) { for (int wi = 0; wi < 10; wi++) @@ -93,18 +159,51 @@ TEST(blobFromImageWithParams_4ch, NHWC_scalar_scale) } } +TEST(blobFromImageWithParams_CustomPadding, letter_box) +{ + Mat img(40, 20, CV_8UC4, Scalar(0, 1, 2, 3)); + + // Custom padding value that you have added + Scalar customPaddingValue(5, 6, 7, 8); // Example padding value + + Size targetSize(20, 20); + + Mat targetImg = img.clone(); + + cv::copyMakeBorder( + targetImg, targetImg, 0, 0, + targetSize.width / 2, + targetSize.width / 2, + BORDER_CONSTANT, + customPaddingValue); + + // Set up Image2BlobParams with your new functionality + Image2BlobParams param; + param.size = targetSize; + param.paddingmode = DNN_PMODE_LETTERBOX; + param.borderValue = customPaddingValue; // Use your new feature here + + // Create blob with custom padding + Mat blob = dnn::blobFromImageWithParams(img, param); + + // Create target blob for comparison + Mat targetBlob = dnn::blobFromImage(targetImg, 1.0, targetSize); + + EXPECT_EQ(0, cvtest::norm(targetBlob, blob, NORM_INF)); +} + TEST(blobFromImageWithParams_4ch, letter_box) { - Mat img(40, 20, CV_8UC4, cv::Scalar(0,1,2,3)); + Mat img(40, 20, CV_8UC4, cv::Scalar(0, 1, 2, 3)); // Construct target mat. Mat targetCh[4]; // The letterbox will add zero at the left and right of output blob. // After the letterbox, every row data would have same value showing as valVec. - std::vector valVec = {0,0,0,0,0, 1,1,1,1,1,1,1,1,1,1, 0,0,0,0,0}; + std::vector valVec = { 0,0,0,0,0, 1,1,1,1,1,1,1,1,1,1, 0,0,0,0,0 }; Mat rowM(1, 20, CV_8UC1, valVec.data()); - for(int i = 0; i < 4; i++) + for (int i = 0; i < 4; i++) { targetCh[i] = rowM * i; } @@ -130,7 +229,7 @@ TEST(blobFromImagesWithParams_4ch, multi_image) param.scalefactor = scalefactor; param.datalayout = DNN_LAYOUT_NHWC; - Mat blobs = blobFromImagesWithParams(std::vector { img, 2*img }, param); + Mat blobs = blobFromImagesWithParams(std::vector { img, 2 * img }, param); vector ranges; ranges.push_back(Range(0, 1)); ranges.push_back(Range(0, blobs.size[1])); @@ -140,7 +239,7 @@ TEST(blobFromImagesWithParams_4ch, multi_image) ranges[0] = Range(1, 2); Mat blob1 = blobs(ranges); - EXPECT_EQ(0, cvtest::norm(2*blob0, blob1, NORM_INF)); + EXPECT_EQ(0, cvtest::norm(2 * blob0, blob1, NORM_INF)); } TEST(readNet, Regression) diff --git a/modules/dnn/test/test_model.cpp b/modules/dnn/test/test_model.cpp index 3805f31afb..1875d832b0 100644 --- a/modules/dnn/test/test_model.cpp +++ b/modules/dnn/test/test_model.cpp @@ -40,6 +40,8 @@ public: model.setPreferableTarget(target); model.setNmsAcrossClasses(nmsAcrossClasses); + if (target == DNN_TARGET_CPU_FP16) + model.enableWinograd(false); std::vector classIds; std::vector confidences; @@ -286,8 +288,9 @@ TEST_P(Test_Model, Classify) TEST_P(Test_Model, DetectRegion) { applyTestTag( + CV_TEST_TAG_MEMORY_2GB, CV_TEST_TAG_LONG, - CV_TEST_TAG_MEMORY_2GB + CV_TEST_TAG_DEBUG_VERYLONG ); #if defined(INF_ENGINE_RELEASE) && INF_ENGINE_VER_MAJOR_EQ(2022010000) @@ -346,8 +349,9 @@ TEST_P(Test_Model, DetectRegion) TEST_P(Test_Model, DetectRegionWithNmsAcrossClasses) { applyTestTag( + CV_TEST_TAG_MEMORY_2GB, CV_TEST_TAG_LONG, - CV_TEST_TAG_MEMORY_2GB + CV_TEST_TAG_DEBUG_VERYLONG ); #if defined(INF_ENGINE_RELEASE) && INF_ENGINE_VER_MAJOR_EQ(2022010000) @@ -406,6 +410,8 @@ TEST_P(Test_Model, DetectRegionWithNmsAcrossClasses) TEST_P(Test_Model, DetectionOutput) { + applyTestTag(CV_TEST_TAG_DEBUG_VERYLONG); + #if defined(INF_ENGINE_RELEASE) && INF_ENGINE_VER_MAJOR_EQ(2022010000) // Check 'backward_compatible_check || in_out_elements_equal' failed at core/src/op/reshape.cpp:427: // While validating node 'v1::Reshape bbox_pred_reshape (ave_bbox_pred_rois[0]:f32{1,8,1,1}, Constant_388[0]:i64{4}) -> (f32{?,?,?,?})' with friendly_name 'bbox_pred_reshape': @@ -629,7 +635,8 @@ TEST_P(Test_Model, Detection_normalized) TEST_P(Test_Model, Segmentation) { applyTestTag( - CV_TEST_TAG_MEMORY_2GB + CV_TEST_TAG_MEMORY_2GB, + CV_TEST_TAG_DEBUG_VERYLONG ); float norm = 0; @@ -744,6 +751,8 @@ TEST_P(Test_Model, TextRecognitionWithCTCPrefixBeamSearch) TEST_P(Test_Model, TextDetectionByDB) { + applyTestTag(CV_TEST_TAG_DEBUG_VERYLONG); + if (target == DNN_TARGET_OPENCL_FP16) applyTestTag(CV_TEST_TAG_DNN_SKIP_OPENCL_FP16); if (target == DNN_TARGET_CPU_FP16) @@ -786,6 +795,8 @@ TEST_P(Test_Model, TextDetectionByDB) TEST_P(Test_Model, TextDetectionByEAST) { + applyTestTag(CV_TEST_TAG_DEBUG_VERYLONG); + std::string imgPath = _tf("text_det_test2.jpg"); std::string weightPath = _tf("frozen_east_text_detection.pb", false); diff --git a/modules/dnn/test/test_onnx_conformance.cpp b/modules/dnn/test/test_onnx_conformance.cpp index 5fb9133b93..dfb8c4d1e5 100644 --- a/modules/dnn/test/test_onnx_conformance.cpp +++ b/modules/dnn/test/test_onnx_conformance.cpp @@ -339,6 +339,25 @@ static const TestCase testConformanceConfig[] = { {"test_isinf_negative", 1, 1}, {"test_isinf_positive", 1, 1}, {"test_isnan", 1, 1}, + {"test_layer_normalization_2d_axis0", 3, 1}, + {"test_layer_normalization_2d_axis1", 3, 1}, + {"test_layer_normalization_2d_axis_negative_1", 3, 1}, + {"test_layer_normalization_2d_axis_negative_2", 3, 1}, + {"test_layer_normalization_3d_axis0_epsilon", 3, 1}, + {"test_layer_normalization_3d_axis1_epsilon", 3, 1}, + {"test_layer_normalization_3d_axis2_epsilon", 3, 1}, + {"test_layer_normalization_3d_axis_negative_1_epsilon", 3, 1}, + {"test_layer_normalization_3d_axis_negative_2_epsilon", 3, 1}, + {"test_layer_normalization_3d_axis_negative_3_epsilon", 3, 1}, + {"test_layer_normalization_4d_axis0", 3, 1}, + {"test_layer_normalization_4d_axis1", 3, 1}, + {"test_layer_normalization_4d_axis2", 3, 1}, + {"test_layer_normalization_4d_axis3", 3, 1}, + {"test_layer_normalization_4d_axis_negative_1", 3, 1}, + {"test_layer_normalization_4d_axis_negative_2", 3, 1}, + {"test_layer_normalization_4d_axis_negative_3", 3, 1}, + {"test_layer_normalization_4d_axis_negative_4", 3, 1}, + {"test_layer_normalization_default_axis", 3, 1}, {"test_leakyrelu", 1, 1}, {"test_leakyrelu_default", 1, 1}, {"test_leakyrelu_example", 1, 1}, diff --git a/modules/dnn/test/test_onnx_conformance_layer_filter__openvino.inl.hpp b/modules/dnn/test/test_onnx_conformance_layer_filter__openvino.inl.hpp index 339746f5f2..17d561d64b 100644 --- a/modules/dnn/test/test_onnx_conformance_layer_filter__openvino.inl.hpp +++ b/modules/dnn/test/test_onnx_conformance_layer_filter__openvino.inl.hpp @@ -792,6 +792,44 @@ CASE(test_isinf_positive) // no filter CASE(test_isnan) // no filter +CASE(test_layer_normalization_2d_axis0) + // no filter +CASE(test_layer_normalization_2d_axis1) + // no filter +CASE(test_layer_normalization_2d_axis_negative_1) + // no filter +CASE(test_layer_normalization_2d_axis_negative_2) + // no filter +CASE(test_layer_normalization_3d_axis0_epsilon) + // no filter +CASE(test_layer_normalization_3d_axis1_epsilon) + // no filter +CASE(test_layer_normalization_3d_axis2_epsilon) + // no filter +CASE(test_layer_normalization_3d_axis_negative_1_epsilon) + // no filter +CASE(test_layer_normalization_3d_axis_negative_2_epsilon) + // no filter +CASE(test_layer_normalization_3d_axis_negative_3_epsilon) + // no filter +CASE(test_layer_normalization_4d_axis0) + // no filter +CASE(test_layer_normalization_4d_axis1) + // no filter +CASE(test_layer_normalization_4d_axis2) + // no filter +CASE(test_layer_normalization_4d_axis3) + // no filter +CASE(test_layer_normalization_4d_axis_negative_1) + // no filter +CASE(test_layer_normalization_4d_axis_negative_2) + // no filter +CASE(test_layer_normalization_4d_axis_negative_3) + // no filter +CASE(test_layer_normalization_4d_axis_negative_4) + // no filter +CASE(test_layer_normalization_default_axis) + // no filter CASE(test_leakyrelu) // no filter CASE(test_leakyrelu_default) diff --git a/modules/dnn/test/test_onnx_conformance_layer_filter_opencv_all_denylist.inl.hpp b/modules/dnn/test/test_onnx_conformance_layer_filter_opencv_all_denylist.inl.hpp index 292cd2a066..e2ea428939 100644 --- a/modules/dnn/test/test_onnx_conformance_layer_filter_opencv_all_denylist.inl.hpp +++ b/modules/dnn/test/test_onnx_conformance_layer_filter_opencv_all_denylist.inl.hpp @@ -43,7 +43,6 @@ "test_castlike_STRING_to_FLOAT_expanded", "test_concat_1d_axis_negative_1", "test_div_uint8", // output type mismatch -"test_logsoftmax_default_axis", "test_maxpool_2d_dilations", "test_maxpool_2d_same_lower", "test_maxpool_2d_uint8", // output type mismatch @@ -51,7 +50,6 @@ "test_maxpool_with_argmax_2d_precomputed_strides", "test_maxunpool_export_with_output_shape", // exception during net.forward() call "test_mul_uint8", // output type mismatch -"test_softmax_default_axis", "test_sub_bcast", "test_sub_uint8", // output type mismatch "test_upsample_nearest", diff --git a/modules/dnn/test/test_onnx_conformance_layer_parser_denylist.inl.hpp b/modules/dnn/test/test_onnx_conformance_layer_parser_denylist.inl.hpp index 52e709ebeb..29b5ac46cc 100644 --- a/modules/dnn/test/test_onnx_conformance_layer_parser_denylist.inl.hpp +++ b/modules/dnn/test/test_onnx_conformance_layer_parser_denylist.inl.hpp @@ -159,8 +159,6 @@ "test_if", "test_if_opt", "test_if_seq", -"test_instancenorm_epsilon", -"test_instancenorm_example", "test_isinf", "test_isinf_negative", "test_isinf_positive", diff --git a/modules/dnn/test/test_onnx_importer.cpp b/modules/dnn/test/test_onnx_importer.cpp index c304a897d7..fd8aa71a8b 100644 --- a/modules/dnn/test/test_onnx_importer.cpp +++ b/modules/dnn/test/test_onnx_importer.cpp @@ -12,6 +12,15 @@ #include namespace opencv_test { namespace { +void yoloPostProcessing( + std::vector& outs, + std::vector& keep_classIds, + std::vector& keep_confidences, + std::vector& keep_boxes, + float conf_threshold, + float iou_threshold, + const std::string& test_name); + template static std::string _tf(TString filename, bool required = true) { @@ -54,7 +63,8 @@ public: void testONNXModels(const String& basename, const Extension ext = npy, double l1 = 0, double lInf = 0, const bool useSoftmax = false, - bool checkNoFallbacks = true, int numInps = 1) + bool checkNoFallbacks = true, int numInps = 1, + bool testShapes = true, bool useWinograd = true) { String onnxmodel = _tf("models/" + basename + ".onnx", required); std::vector inps(numInps); @@ -76,10 +86,12 @@ public: Net net = readNetFromONNX(onnxmodel); ASSERT_FALSE(net.empty()); - testInputShapes(net, inps); + if (testShapes) + testInputShapes(net, inps); net.setPreferableBackend(backend); net.setPreferableTarget(target); + net.enableWinograd(useWinograd); std::vector inputNames; for (int i = 0; i < numInps; ++i) @@ -122,9 +134,6 @@ public: TEST_P(Test_ONNX_layers, InstanceNorm) { - if(backend == DNN_BACKEND_CUDA) - applyTestTag(CV_TEST_TAG_DNN_SKIP_CUDA); /* MVN is not supported */ - if (target == DNN_TARGET_MYRIAD) testONNXModels("instancenorm", npy, 0, 0, false, false); else @@ -254,6 +263,10 @@ TEST_P(Test_ONNX_layers, Gather_shared_indices) { testONNXModels("gather_shared_indices", npy, 0, 0, false, false, 1); } +TEST_P(Test_ONNX_layers, Two_resizes_with_shared_subgraphs) { + testONNXModels("two_resizes_with_shared_subgraphs", npy, 0, 0, false, false, 3, /*testShapes*/ false); +} + TEST_P(Test_ONNX_layers, Convolution3D) { if (backend == DNN_BACKEND_CUDA && target == DNN_TARGET_CUDA_FP16) @@ -1482,7 +1495,8 @@ TEST_P(Test_ONNX_layers, Einsum_5D) testONNXModels("einsum_5d", npy, 0, 0, false, false, 2); } -TEST_P(Test_ONNX_layers, Einsum_InnerProduct) +// https://github.com/opencv/opencv/issues/24883 +TEST_P(Test_ONNX_layers, DISABLED_Einsum_InnerProduct) { testONNXModels("einsum_inner", npy, 0, 0, false, false, 2); } @@ -1507,6 +1521,10 @@ TEST_P(Test_ONNX_layers, Einsum_transpose) testONNXModels("einsum_transpose", npy, 0, 0, false, false, 1); } +TEST_P(Test_ONNX_layers, Einsum_const_inputs) { + testONNXModels("einsum_const_inputs", npy, 0, 0, false, false, 1); +} + TEST_P(Test_ONNX_layers, Pad2d_Unfused) { testONNXModels("ReflectionPad2d"); @@ -1932,7 +1950,9 @@ TEST_P(Test_ONNX_layers, ConvResizePool1d) #endif } #endif - testONNXModels("conv_resize_pool_1d"); + + const double lInf = (target == DNN_TARGET_CPU_FP16) ? 0.024 : default_lInf; + testONNXModels("conv_resize_pool_1d", npy, default_l1, lInf); } TEST_P(Test_ONNX_layers, DepthWiseAdd) @@ -2133,6 +2153,7 @@ TEST_P(Test_ONNX_nets, Alexnet) net.setPreferableBackend(backend); net.setPreferableTarget(target); + net.enableWinograd(false); Mat inp = imread(_tf("../grace_hopper_227.png")); Mat ref = blobFromNPY(_tf("../caffe_alexnet_prob.npy")); @@ -2205,6 +2226,9 @@ TEST_P(Test_ONNX_nets, Googlenet) net.setPreferableBackend(backend); net.setPreferableTarget(target); + if (target == DNN_TARGET_CPU_FP16) + net.enableWinograd(false); + std::vector images; images.push_back( imread(_tf("../googlenet_0.png")) ); images.push_back( imread(_tf("../googlenet_1.png")) ); @@ -2349,7 +2373,7 @@ TEST_P(Test_ONNX_nets, TinyYolov2) } #endif - testONNXModels("tiny_yolo2", pb, l1, lInf); + testONNXModels("tiny_yolo2", pb, l1, lInf, false, true, 1, true, false); } TEST_P(Test_ONNX_nets, CNN_MNIST) @@ -2377,7 +2401,7 @@ TEST_P(Test_ONNX_nets, LResNet100E_IR) #else (target == DNN_TARGET_CPU ? CV_TEST_TAG_MEMORY_512MB : CV_TEST_TAG_MEMORY_1GB), #endif - CV_TEST_TAG_DEBUG_LONG + CV_TEST_TAG_DEBUG_VERYLONG ); if (backend == DNN_BACKEND_INFERENCE_ENGINE_NN_BUILDER_2019) { @@ -2394,6 +2418,7 @@ TEST_P(Test_ONNX_nets, LResNet100E_IR) double l1 = default_l1, lInf = default_lInf; // output range: [-3; 3] + bool useWinograd = true; if (backend == DNN_BACKEND_OPENCV && target == DNN_TARGET_OPENCL_FP16) { l1 = 0.009; @@ -2409,7 +2434,14 @@ TEST_P(Test_ONNX_nets, LResNet100E_IR) l1 = 0.009; lInf = 0.04; } - testONNXModels("LResNet100E_IR", pb, l1, lInf); + else if (target == DNN_TARGET_CPU_FP16) + { + useWinograd = false; + l1 = 0.009; + lInf = 0.035; + } + + testONNXModels("LResNet100E_IR", pb, l1, lInf, false, true, 1, true, useWinograd); } TEST_P(Test_ONNX_nets, Emotion_ferplus) @@ -2424,7 +2456,7 @@ TEST_P(Test_ONNX_nets, Emotion_ferplus) double l1 = default_l1; double lInf = default_lInf; - + bool useWinograd = true; // Output values are in range [-2.011, 2.111] if ((backend == DNN_BACKEND_OPENCV && target == DNN_TARGET_OPENCL_FP16) || (target == DNN_TARGET_CUDA_FP16)) l1 = 0.007; @@ -2437,6 +2469,11 @@ TEST_P(Test_ONNX_nets, Emotion_ferplus) l1 = 2.4e-4; lInf = 6e-4; } + else if (backend == DNN_BACKEND_OPENCV && target == DNN_TARGET_CPU_FP16) + { + useWinograd = false; + l1 = 0.007; + } #if defined(INF_ENGINE_RELEASE) && INF_ENGINE_VER_MAJOR_GE(2020040000) if (backend == DNN_BACKEND_INFERENCE_ENGINE_NGRAPH && target == DNN_TARGET_OPENCL_FP16) { @@ -2444,7 +2481,7 @@ TEST_P(Test_ONNX_nets, Emotion_ferplus) } #endif - testONNXModels("emotion_ferplus", pb, l1, lInf); + testONNXModels("emotion_ferplus", pb, l1, lInf, false, true, 1, true, useWinograd); } TEST_P(Test_ONNX_nets, Inception_v2) @@ -2588,30 +2625,16 @@ TEST_P(Test_ONNX_layers, CumSum) testONNXModels("cumsum_3d_dim_2"); } -// This test is mainly to test: -// 1. identity node with constant input -// 2. limited support to range operator (all inputs are constant) -// 3. parseExpand with multiple broadcast axes -// 4. 1D mat dimension issue with the output of range operator -TEST_P(Test_ONNX_layers, YOLOv7) +static void testYOLO(const std::string& weightPath, const std::vector& refClassIds, + const std::vector& refScores, const std::vector& refBoxes, + Image2BlobParams imgParams, float conf_threshold = 0.3, float iou_threshold = 0.5, + double scores_diff = 1e-5, double boxes_iou_diff = 1e-4, const std::string test_name = "") { - std::string weightPath = _tf("models/yolov7_not_simplified.onnx", false); std::string imgPath = _tf("../dog_orig_size.png"); - Size targetSize{640, 640}; - float conf_threshold = 0.3; - float iou_threshold = 0.5; - - // Reference, which is collected with input size of 640x640 - std::vector refClassIds{1, 16, 7}; - std::vector refScores{0.9614331f, 0.9589417f, 0.8679074f}; - // [x1, y1, x2, y2] x 3 - std::vector refBoxes{Rect2d(105.973236f, 150.16716f, 472.59012f, 466.48834f), - Rect2d(109.97953f, 246.17862f, 259.83676f, 600.76624f), - Rect2d(385.96185f, 83.02809f, 576.07355f, 189.82793f)}; - Mat img = imread(imgPath); - Mat inp = blobFromImage(img, 1/255.0, targetSize, Scalar(0, 0, 0), true, false); + + Mat inp = blobFromImageWithParams(img, imgParams); Net net = readNet(weightPath); @@ -2619,57 +2642,253 @@ TEST_P(Test_ONNX_layers, YOLOv7) std::vector outs; net.forward(outs, net.getUnconnectedOutLayersNames()); - Mat preds = outs[3].reshape(1, outs[3].size[1]); // [1, 25200, 85] + // Retrieve + std::vector keep_classIds; + std::vector keep_confidences; + std::vector keep_boxes; + yoloPostProcessing(outs, keep_classIds, keep_confidences, keep_boxes, conf_threshold, iou_threshold, test_name); + + normAssertDetections( + refClassIds, refScores, refBoxes, + keep_classIds, keep_confidences, keep_boxes, + "", 0.0, scores_diff, boxes_iou_diff); +} + +void yoloPostProcessing( + std::vector& outs, + std::vector& keep_classIds, + std::vector& keep_confidences, + std::vector& keep_boxes, + float conf_threshold, + float iou_threshold, + const std::string& test_name +){ // Retrieve std::vector classIds; std::vector confidences; std::vector boxes; + + if (test_name == "yolov8"){ + cv::transposeND(outs[0], {0, 2, 1}, outs[0]); + } + // each row is [cx, cy, w, h, conf_obj, conf_class1, ..., conf_class80] - for (int i = 0; i < preds.rows; ++i) - { - // filter out non objects - float obj_conf = preds.row(i).at(4); - if (obj_conf < conf_threshold) - continue; + for (auto preds : outs){ - // get class id and conf - Mat scores = preds.row(i).colRange(5, preds.cols); - double conf; - Point maxLoc; - minMaxLoc(scores, 0, &conf, 0, &maxLoc); - conf *= obj_conf; - if (conf < conf_threshold) - continue; + preds = preds.reshape(1, preds.size[1]); // [1, 8400, 85] -> [8400, 85] - // get bbox coords - float* det = preds.ptr(i); - double cx = det[0]; - double cy = det[1]; - double w = det[2]; - double h = det[3]; - // [x1, y1, x2, y2] - boxes.push_back(Rect2d(cx - 0.5 * w, cy - 0.5 * h, - cx + 0.5 * w, cy + 0.5 * h)); - classIds.push_back(maxLoc.x); - confidences.push_back(conf); + for (int i = 0; i < preds.rows; ++i) + { + // filter out non objects + float obj_conf = (test_name != "yolov8") ? preds.at(i, 4) : 1.0f; + if (obj_conf < conf_threshold) + continue; + + Mat scores = preds.row(i).colRange((test_name != "yolov8") ? 5 : 4, preds.cols); + double conf; + Point maxLoc; + minMaxLoc(scores, 0, &conf, 0, &maxLoc); + + conf = (test_name != "yolov8") ? conf * obj_conf : conf; + if (conf < conf_threshold) + continue; + + // get bbox coords + float* det = preds.ptr(i); + double cx = det[0]; + double cy = det[1]; + double w = det[2]; + double h = det[3]; + + // [x1, y1, x2, y2] + boxes.push_back(Rect2d(cx - 0.5 * w, cy - 0.5 * h, + cx + 0.5 * w, cy + 0.5 * h)); + classIds.push_back(maxLoc.x); + confidences.push_back(conf); + } } // NMS std::vector keep_idx; NMSBoxes(boxes, confidences, conf_threshold, iou_threshold, keep_idx); - std::vector keep_classIds; - std::vector keep_confidences; - std::vector keep_boxes; for (auto i : keep_idx) { keep_classIds.push_back(classIds[i]); keep_confidences.push_back(confidences[i]); keep_boxes.push_back(boxes[i]); } +} - normAssertDetections(refClassIds, refScores, refBoxes, keep_classIds, keep_confidences, keep_boxes); + +TEST_P(Test_ONNX_nets, YOLOX) +{ + applyTestTag(CV_TEST_TAG_DEBUG_VERYLONG); + + std::string weightPath = _tf("models/yolox_s_inf_decoder.onnx", false); + + Size targetSize{640, 640}; + float conf_threshold = 0.50; + float iou_threshold = 0.50; + + std::vector refClassIds{1, 16, 7}; + std::vector refScores{0.9649f, 0.9163f, 0.6879f}; + + std::vector refBoxes{ + Rect2d(105.5384, 179.4100, 470.6339, 428.5553), + Rect2d(111.4482, 263.4098, 258.7438, 526.1140), + Rect2d(389.1421, 143.9286, 577.9495, 222.0294) + }; + + Image2BlobParams imgParams( + Scalar::all(1), + targetSize, + Scalar::all(0), + true, + CV_32F, + DNN_LAYOUT_NCHW, + DNN_PMODE_LETTERBOX, + Scalar::all(114) + ); + + testYOLO( + weightPath, refClassIds, refScores, refBoxes, + imgParams, conf_threshold, iou_threshold, + 1.0e-4, 1.0e-4); +} + +TEST_P(Test_ONNX_nets, YOLOv8) +{ + std::string weightPath = _tf("models/yolov8n.onnx", false); + + Size targetSize{640, 640}; + float conf_threshold = 0.25; + float iou_threshold = 0.50; + + std::vector refClassIds{16, 1, 2}; + std::vector refScores{0.9332f, 0.8959f, 0.6157f}; + // [x1, y1, x2, y2] + std::vector refBoxes{ + Rect2d(108.8965, 261.9094, 257.1633, 530.3049), + Rect2d(110.4020, 192.9843, 473.4418, 429.5965), + Rect2d(389.1603, 143.2506, 577.3542, 223.0615), + }; + + Image2BlobParams imgParams( + Scalar::all(1/255.0), + targetSize, + Scalar::all(0), + true, + CV_32F, + DNN_LAYOUT_NCHW, + DNN_PMODE_LETTERBOX, + Scalar::all(114) + ); + + testYOLO( + weightPath, refClassIds, refScores, refBoxes, + imgParams, conf_threshold, iou_threshold, + 1.0e-4, 1.0e-4, "yolov8"); +} + +// This test is mainly to test: +// 1. identity node with constant input +// 2. limited support to range operator (all inputs are constant) +// 3. parseExpand with multiple broadcast axes +// 4. 1D mat dimension issue with the output of range operator +TEST_P(Test_ONNX_nets, YOLOv7) +{ + applyTestTag( + CV_TEST_TAG_MEMORY_2GB, + CV_TEST_TAG_DEBUG_VERYLONG + ); + + std::string weightPath = _tf("models/yolov7_not_simplified.onnx", false); + // Reference, which is collected with input size of 640x640 + std::vector refClassIds{1, 16, 7}; + std::vector refScores{0.9614331f, 0.9589417f, 0.8679074f}; + // [x1, y1, x2, y2] x 3 + std::vector refBoxes{Rect2d(105.973236f, 150.16716f, 472.59012f, 466.48834f), + Rect2d(109.97953f, 246.17862f, 259.83676f, 600.76624f), + Rect2d(385.96185f, 83.02809f, 576.07355f, 189.82793f)}; + + Size targetSize{640, 640}; + + Image2BlobParams imgParams( + Scalar::all(1/255.0), + targetSize, + Scalar::all(0), + true, + CV_32F, + DNN_LAYOUT_NCHW, + DNN_PMODE_NULL, + Scalar::all(0) + ); + + testYOLO(weightPath, refClassIds, refScores, refBoxes, imgParams); +} + +TEST_P(Test_ONNX_nets, YOLOv6) +{ + std::string weightPath = _tf("models/yolov6n.onnx", false); + + Size targetSize{640, 640}; + float conf_threshold = 0.30; + float iou_threshold = 0.50; + + std::vector refClassIds{1, 16, 7, 1}; + std::vector refScores{0.95031f, 0.87123f, 0.65453f, 0.34142f}; + // [x1, y1, x2, y2] x 3 + std::vector refBoxes{Rect2d(98.84, 177.91, 473.29, 431.19), + Rect2d(109.80, 265.50, 258.86, 531.97), + Rect2d(387.79, 141.61, 576.98, 223.52), + Rect2d(105.62, 199.24, 218.37, 389.84), + }; + + Image2BlobParams imgParams( + Scalar::all(1/255.0), + targetSize, + Scalar::all(0), + true, + CV_32F, + DNN_LAYOUT_NCHW, + DNN_PMODE_LETTERBOX, + Scalar::all(114) + ); + + testYOLO( + weightPath, refClassIds, refScores, refBoxes, + imgParams, conf_threshold, iou_threshold, + 1.0e-4, 1.0e-3); +} + +TEST_P(Test_ONNX_nets, YOLOv5n) +{ + std::string weightPath = findDataFile("dnn/yolov5n.onnx", false); + // Reference, which is collected with input size of 640x640 + std::vector refClassIds{16, 2, 1}; + std::vector refScores{0.749053f, 0.616853f, 0.32506f}; + // [x1, y1, x2, y2] x 4 + + std::vector refBoxes{Rect2d(108.088f, 239.293f, 266.196f, 607.658f), + Rect2d(392.028f, 89.9233f, 579.152f, 190.447f), + Rect2d(120.278f, 159.76, 214.481f, 241.473f)}; + + Size targetSize{640, 640}; + + Image2BlobParams imgParams( + Scalar::all(1/255.0), + targetSize, + Scalar::all(0), + true, + CV_32F, + DNN_LAYOUT_NCHW, + DNN_PMODE_NULL, + Scalar::all(0) + ); + + testYOLO(weightPath, refClassIds, refScores, refBoxes, imgParams); } TEST_P(Test_ONNX_layers, Tile) @@ -2677,36 +2896,6 @@ TEST_P(Test_ONNX_layers, Tile) testONNXModels("tile", pb); } -TEST_P(Test_ONNX_layers, LayerNorm) -{ - testONNXModels("test_layer_normalization_2d_axis0", pb, 0, 0, false, true, 3); - testONNXModels("test_layer_normalization_2d_axis1", pb, 0, 0, false, true, 3); - testONNXModels("test_layer_normalization_2d_axis_negative_1", pb, 0, 0, false, true, 3); - testONNXModels("test_layer_normalization_2d_axis_negative_2", pb, 0, 0, false, true, 3); - testONNXModels("test_layer_normalization_3d_axis0_epsilon", pb, 0, 0, false, true, 3); - testONNXModels("test_layer_normalization_3d_axis1_epsilon", pb, 0, 0, false, true, 3); - testONNXModels("test_layer_normalization_3d_axis2_epsilon", pb, 0, 0, false, true, 3); - testONNXModels("test_layer_normalization_3d_axis_negative_1_epsilon", pb, 0, 0, false, true, 3); - testONNXModels("test_layer_normalization_3d_axis_negative_2_epsilon", pb, 0, 0, false, true, 3); - testONNXModels("test_layer_normalization_3d_axis_negative_3_epsilon", pb, 0, 0, false, true, 3); - testONNXModels("test_layer_normalization_4d_axis0", pb, 0, 0, false, true, 3); - testONNXModels("test_layer_normalization_4d_axis1", pb, 0, 0, false, true, 3); - testONNXModels("test_layer_normalization_4d_axis2", pb, 0, 0, false, true, 3); - testONNXModels("test_layer_normalization_4d_axis3", pb, 0, 0, false, true, 3); - testONNXModels("test_layer_normalization_4d_axis_negative_1", pb, 0, 0, false, true, 3); - testONNXModels("test_layer_normalization_4d_axis_negative_2", pb, 0, 0, false, true, 3); - testONNXModels("test_layer_normalization_4d_axis_negative_3", pb, 0, 0, false, true, 3); - testONNXModels("test_layer_normalization_4d_axis_negative_4", pb, 0, 0, false, true, 3); - testONNXModels("test_layer_normalization_default_axis", pb, 0, 0, false, true, 3); -} - -// for testing graph simplification -TEST_P(Test_ONNX_layers, LayerNormExpanded) -{ - testONNXModels("layer_norm_expanded"); - testONNXModels("layer_norm_expanded_with_initializers"); -} - TEST_P(Test_ONNX_layers, Gelu) { testONNXModels("gelu"); @@ -2778,6 +2967,70 @@ TEST_P(Test_ONNX_layers, Expand_shape_model4) { testONNXModels("test_expand_shape_model4", pb, 0, 0, false, true, 1); } +TEST_P(Test_ONNX_layers, Attention) { + testONNXModels("attention"); +} +TEST_P(Test_ONNX_layers, AttentionSingleHead) { + testONNXModels("attention_single_head"); +} + +TEST_P(Test_ONNX_nets, ViT_B_32) { + applyTestTag(CV_TEST_TAG_LONG, CV_TEST_TAG_DEBUG_LONG); + + const std::string model_path = _tf("models/vit_b_32.onnx", false); + + auto net = readNet(model_path); + ASSERT_FALSE(net.empty()); + + net.setPreferableBackend(backend); + net.setPreferableTarget(target); + + auto image = imread(_tf("../googlenet_0.png")); + auto blob = blobFromImage(image, 1.f, Size(224, 224)); + auto ref = blobFromNPY(_tf("data/output_vit_b_32.npy")); + checkBackend(&blob, &ref); + + net.setInput(blob); + auto out = net.forward(); + + double l1 = default_l1; + double lInf = default_lInf; + if (target == DNN_TARGET_CUDA_FP16) + { + l1 = 0.01; + lInf = 0.06; + } + if (target == DNN_TARGET_OPENCL_FP16) + { + l1 = 0.008; + lInf = 0.04; + } + + normAssert(ref, out, "ViTB_32", l1, lInf); +} + +TEST_P(Test_ONNX_nets, VitTrack) { + auto image = imread(_tf("../dog_orig_size.png")); + auto input0 = blobFromImage(image, 1.f, Size(128, 128)); + auto input1 = blobFromImage(image, 1.f, Size(256, 256)); + + auto net = readNet(_tf("models/object_tracking_vittrack_2023sep.onnx", false)); + net.setInput(input0, "template"); + net.setInput(input1, "search"); + + std::vector output_names{"output1", "output2", "output3"}; + std::vector outputs; + net.forward(outputs, output_names); + + auto ref_output1 = blobFromNPY(_tf("data/output_object_tracking_vittrack_2023sep_0.npy")); + auto ref_output2 = blobFromNPY(_tf("data/output_object_tracking_vittrack_2023sep_1.npy")); + auto ref_output3 = blobFromNPY(_tf("data/output_object_tracking_vittrack_2023sep_2.npy")); + + normAssert(ref_output1, outputs[0], "VitTrack output1"); + normAssert(ref_output2, outputs[1], "VitTrack output2"); + normAssert(ref_output3, outputs[2], "VitTrack output3"); +} + INSTANTIATE_TEST_CASE_P(/**/, Test_ONNX_nets, dnnBackendsAndTargets()); }} // namespace diff --git a/modules/dnn/test/test_tf_importer.cpp b/modules/dnn/test/test_tf_importer.cpp index 1d36cddc23..964fcbbbe8 100644 --- a/modules/dnn/test/test_tf_importer.cpp +++ b/modules/dnn/test/test_tf_importer.cpp @@ -974,6 +974,9 @@ TEST_P(Test_TensorFlow_nets, Inception_v2_SSD) net.setPreferableBackend(backend); net.setPreferableTarget(target); + if (target == DNN_TARGET_CPU_FP16) + net.enableWinograd(false); + net.setInput(blob); // Output has shape 1x1xNx7 where N - number of detections. // An every detection is a vector of values [id, classId, confidence, left, top, right, bottom] @@ -1279,7 +1282,7 @@ TEST_P(Test_TensorFlow_nets, EAST_text_detection) { applyTestTag( (target == DNN_TARGET_CPU ? CV_TEST_TAG_MEMORY_512MB : CV_TEST_TAG_MEMORY_1GB), - CV_TEST_TAG_DEBUG_LONG + CV_TEST_TAG_DEBUG_VERYLONG ); #if defined(INF_ENGINE_RELEASE) @@ -1307,6 +1310,8 @@ TEST_P(Test_TensorFlow_nets, EAST_text_detection) net.setPreferableBackend(backend); net.setPreferableTarget(target); + if (target == DNN_TARGET_CPU_FP16) + net.enableWinograd(false); Mat img = imread(imgPath); Mat inp = blobFromImage(img, 1.0, Size(), Scalar(123.68, 116.78, 103.94), true, false); @@ -1341,8 +1346,9 @@ TEST_P(Test_TensorFlow_nets, EAST_text_detection) } else if (target == DNN_TARGET_CPU_FP16) { - lInf_scores = 0.1; - l1_geometry = 0.28; lInf_geometry = 5.94; + lInf_scores = 0.17; + l1_geometry = 0.28; + lInf_geometry = 5.94; } else { @@ -1798,7 +1804,10 @@ TEST_P(Test_TensorFlow_nets, Mask_RCNN) if (target == DNN_TARGET_CUDA_FP16) applyTestTag(CV_TEST_TAG_DNN_SKIP_CUDA_FP16); - applyTestTag(CV_TEST_TAG_MEMORY_1GB, CV_TEST_TAG_DEBUG_VERYLONG); + applyTestTag( + CV_TEST_TAG_MEMORY_2GB, + CV_TEST_TAG_DEBUG_VERYLONG + ); Mat img = imread(findDataFile("dnn/street.png")); std::string proto = findDataFile("dnn/mask_rcnn_inception_v2_coco_2018_01_28.pbtxt"); std::string model = findDataFile("dnn/mask_rcnn_inception_v2_coco_2018_01_28.pb", false); @@ -1810,6 +1819,8 @@ TEST_P(Test_TensorFlow_nets, Mask_RCNN) net.setPreferableBackend(backend); net.setPreferableTarget(target); + if (target == DNN_TARGET_CPU_FP16) + net.enableWinograd(false); net.setInput(blob); diff --git a/modules/features2d/include/opencv2/features2d.hpp b/modules/features2d/include/opencv2/features2d.hpp index fab90c6b3d..f8c631f233 100644 --- a/modules/features2d/include/opencv2/features2d.hpp +++ b/modules/features2d/include/opencv2/features2d.hpp @@ -1527,8 +1527,8 @@ public: @param dmatcher Descriptor matcher that is used to find the nearest word of the trained vocabulary for each keypoint descriptor of the image. */ - CV_WRAP BOWImgDescriptorExtractor( const Ptr& dextractor, - const Ptr& dmatcher ); + CV_WRAP BOWImgDescriptorExtractor( const Ptr& dextractor, + const Ptr& dmatcher ); /** @overload */ BOWImgDescriptorExtractor( const Ptr& dmatcher ); virtual ~BOWImgDescriptorExtractor(); diff --git a/modules/features2d/misc/java/test/BOWImgDescriptorExtractorTest.java b/modules/features2d/misc/java/test/BOWImgDescriptorExtractorTest.java new file mode 100644 index 0000000000..a8ff001e85 --- /dev/null +++ b/modules/features2d/misc/java/test/BOWImgDescriptorExtractorTest.java @@ -0,0 +1,48 @@ +package org.opencv.test.features2d; + +import org.opencv.core.Core; +import org.opencv.core.CvType; +import org.opencv.core.Mat; +import org.opencv.core.MatOfKeyPoint; +import org.opencv.core.Point; +import org.opencv.core.Scalar; +import org.opencv.core.KeyPoint; +import org.opencv.features2d.ORB; +import org.opencv.features2d.DescriptorMatcher; +import org.opencv.features2d.BOWImgDescriptorExtractor; +import org.opencv.test.OpenCVTestCase; +import org.opencv.test.OpenCVTestRunner; +import org.opencv.imgproc.Imgproc; + +public class BOWImgDescriptorExtractorTest extends OpenCVTestCase { + + ORB extractor; + DescriptorMatcher matcher; + int matSize; + + public static void assertDescriptorsClose(Mat expected, Mat actual, int allowedDistance) { + double distance = Core.norm(expected, actual, Core.NORM_HAMMING); + assertTrue("expected:<" + allowedDistance + "> but was:<" + distance + ">", distance <= allowedDistance); + } + + private Mat getTestImg() { + Mat cross = new Mat(matSize, matSize, CvType.CV_8U, new Scalar(255)); + Imgproc.line(cross, new Point(20, matSize / 2), new Point(matSize - 21, matSize / 2), new Scalar(100), 2); + Imgproc.line(cross, new Point(matSize / 2, 20), new Point(matSize / 2, matSize - 21), new Scalar(100), 2); + + return cross; + } + + @Override + protected void setUp() throws Exception { + super.setUp(); + extractor = ORB.create(); + matcher = DescriptorMatcher.create(DescriptorMatcher.BRUTEFORCE); + matSize = 100; + } + + public void testCreate() { + BOWImgDescriptorExtractor bow = new BOWImgDescriptorExtractor(extractor, matcher); + } + +} diff --git a/modules/gapi/CMakeLists.txt b/modules/gapi/CMakeLists.txt index 46ea208221..6a4563a3c6 100644 --- a/modules/gapi/CMakeLists.txt +++ b/modules/gapi/CMakeLists.txt @@ -88,6 +88,7 @@ set(gapi_srcs src/api/kernels_imgproc.cpp src/api/kernels_video.cpp src/api/kernels_nnparsers.cpp + src/api/kernels_ot.cpp src/api/kernels_streaming.cpp src/api/kernels_stereo.cpp src/api/render.cpp @@ -130,6 +131,7 @@ set(gapi_srcs src/backends/cpu/gcpustereo.cpp src/backends/cpu/gcpuvideo.cpp src/backends/cpu/gcpucore.cpp + src/backends/cpu/gcpuot.cpp src/backends/cpu/gnnparsers.cpp # Fluid Backend (also built-in, FIXME:move away) @@ -163,6 +165,7 @@ set(gapi_srcs # ONNX backend src/backends/onnx/gonnxbackend.cpp src/backends/onnx/dml_ep.cpp + src/backends/onnx/coreml_ep.cpp # Render backend src/backends/render/grenderocv.cpp @@ -240,18 +243,25 @@ set(gapi_srcs src/utils/itt.cpp ) +file(GLOB_RECURSE gapi_3rdparty_srcs + "${CMAKE_CURRENT_LIST_DIR}/src/3rdparty/vasot/src/*.cpp" +) + ocv_add_dispatched_file(backends/fluid/gfluidimgproc_func SSE4_1 AVX2) ocv_add_dispatched_file(backends/fluid/gfluidcore_func SSE4_1 AVX2) ocv_list_add_prefix(gapi_srcs "${CMAKE_CURRENT_LIST_DIR}/") # For IDE users -ocv_source_group("Src" FILES ${gapi_srcs}) +ocv_source_group("Src" FILES ${gapi_srcs} ${gapi_3rdparty_srcs}) ocv_source_group("Include" FILES ${gapi_ext_hdrs}) -ocv_set_module_sources(HEADERS ${gapi_ext_hdrs} SOURCES ${gapi_srcs}) +ocv_set_module_sources(HEADERS ${gapi_ext_hdrs} SOURCES ${gapi_srcs} ${gapi_3rdparty_srcs}) ocv_module_include_directories("${CMAKE_CURRENT_LIST_DIR}/src") +# VAS Object Tracking includes +ocv_module_include_directories(${CMAKE_CURRENT_LIST_DIR}/src/3rdparty/vasot/include) + ocv_create_module() ocv_target_link_libraries(${the_module} PRIVATE ade) @@ -367,6 +377,10 @@ if(WIN32) ocv_target_link_libraries(${the_module} PRIVATE wsock32 ws2_32) endif() +if(HAVE_DIRECTML) + ocv_target_compile_definitions(${the_module} PRIVATE HAVE_DIRECTML=1) +endif() + if(HAVE_ONNX) ocv_target_link_libraries(${the_module} PRIVATE ${ONNX_LIBRARY}) ocv_target_compile_definitions(${the_module} PRIVATE HAVE_ONNX=1) @@ -379,10 +393,11 @@ if(HAVE_ONNX) endif() endif() +ocv_install_3rdparty_licenses(vasot "${CMAKE_CURRENT_SOURCE_DIR}/src/3rdparty/vasot/LICENSE.txt") + ocv_add_perf_tests() ocv_add_samples() - # Required for sample with inference on host if(TARGET example_gapi_onevpl_infer_with_advanced_device_selection) if(TARGET ocv.3rdparty.openvino AND OPENCV_GAPI_WITH_OPENVINO) diff --git a/modules/gapi/include/opencv2/gapi/cpu/ot.hpp b/modules/gapi/include/opencv2/gapi/cpu/ot.hpp new file mode 100644 index 0000000000..03dbe904cc --- /dev/null +++ b/modules/gapi/include/opencv2/gapi/cpu/ot.hpp @@ -0,0 +1,29 @@ +// This file is part of OpenCV project. +// It is subject to the license terms in the LICENSE file found in the top-level directory +// of this distribution and at http://opencv.org/license.html. +// +// Copyright (C) 2018 Intel Corporation + + +#ifndef OPENCV_GAPI_CPU_OT_API_HPP +#define OPENCV_GAPI_CPU_OT_API_HPP + +#include // GAPI_EXPORTS +#include // GKernelPackage + +namespace cv { +namespace gapi { +/** + * @brief This namespace contains G-API Operation Types for + * VAS Object Tracking module functionality. + */ +namespace ot { +namespace cpu { +GAPI_EXPORTS_W GKernelPackage kernels(); +} // namespace cpu +} // namespace ot +} // namespace gapi +} // namespace cv + + +#endif // OPENCV_GAPI_CPU_OT_API_HPP diff --git a/modules/gapi/include/opencv2/gapi/infer/bindings_onnx.hpp b/modules/gapi/include/opencv2/gapi/infer/bindings_onnx.hpp index c418c0d496..fb2376ece8 100644 --- a/modules/gapi/include/opencv2/gapi/infer/bindings_onnx.hpp +++ b/modules/gapi/include/opencv2/gapi/infer/bindings_onnx.hpp @@ -39,6 +39,9 @@ public: GAPI_WRAP PyParams& cfgAddExecutionProvider(ep::DirectML ep); + GAPI_WRAP + PyParams& cfgAddExecutionProvider(ep::CoreML ep); + GAPI_WRAP PyParams& cfgAddExecutionProvider(ep::CUDA ep); diff --git a/modules/gapi/include/opencv2/gapi/infer/onnx.hpp b/modules/gapi/include/opencv2/gapi/infer/onnx.hpp index 4efb750439..ec5950718e 100644 --- a/modules/gapi/include/opencv2/gapi/infer/onnx.hpp +++ b/modules/gapi/include/opencv2/gapi/infer/onnx.hpp @@ -32,6 +32,65 @@ namespace onnx { */ namespace ep { +/** + * @brief This structure provides functions + * that fill inference options for ONNX CoreML Execution Provider. + * Please follow https://onnxruntime.ai/docs/execution-providers/CoreML-ExecutionProvider.html#coreml-execution-provider + */ +struct GAPI_EXPORTS_W_SIMPLE CoreML { + /** @brief Class constructor. + + Constructs CoreML parameters. + + */ + GAPI_WRAP + CoreML() = default; + + /** @brief Limit CoreML Execution Provider to run on CPU only. + + This function is used to limit CoreML to run on CPU only. + Please follow: https://onnxruntime.ai/docs/execution-providers/CoreML-ExecutionProvider.html#coreml_flag_use_cpu_only + + @return reference to this parameter structure. + */ + GAPI_WRAP + CoreML& cfgUseCPUOnly() { + use_cpu_only = true; + return *this; + } + + /** @brief Enable CoreML EP to run on a subgraph in the body of a control flow ONNX operator (i.e. a Loop, Scan or If operator). + + This function is used to enable CoreML EP to run on + a subgraph of a control flow of ONNX operation. + Please follow: https://onnxruntime.ai/docs/execution-providers/CoreML-ExecutionProvider.html#coreml_flag_enable_on_subgraph + + @return reference to this parameter structure. + */ + GAPI_WRAP + CoreML& cfgEnableOnSubgraph() { + enable_on_subgraph = true; + return *this; + } + + /** @brief Enable CoreML EP to run only on Apple Neural Engine. + + This function is used to enable CoreML EP to run only on Apple Neural Engine. + Please follow: https://onnxruntime.ai/docs/execution-providers/CoreML-ExecutionProvider.html#coreml_flag_only_enable_device_with_ane + + @return reference to this parameter structure. + */ + GAPI_WRAP + CoreML& cfgEnableOnlyNeuralEngine() { + enable_only_ane = true; + return *this; + } + + bool use_cpu_only = false; + bool enable_on_subgraph = false; + bool enable_only_ane = false; +}; + /** * @brief This structure provides functions * that fill inference options for CUDA Execution Provider. @@ -189,13 +248,23 @@ public: GAPI_WRAP explicit DirectML(const int device_id) : ddesc(device_id) { }; - using DeviceDesc = cv::util::variant; + /** @brief Class constructor. + + Constructs DirectML parameters based on adapter name. + + @param adapter_name Target adapter_name to use. + */ + GAPI_WRAP + explicit DirectML(const std::string &adapter_name) : ddesc(adapter_name) { }; + + using DeviceDesc = cv::util::variant; DeviceDesc ddesc; }; using EP = cv::util::variant< cv::util::monostate , OpenVINO , DirectML + , CoreML , CUDA , TensorRT>; @@ -487,6 +556,20 @@ public: /** @brief Adds execution provider for runtime. + The function is used to add ONNX Runtime CoreML Execution Provider options. + + @param ep CoreML Execution Provider options. + @see cv::gapi::onnx::ep::CoreML. + + @return the reference on modified object. + */ + Params& cfgAddExecutionProvider(ep::CoreML&& ep) { + desc.execution_providers.emplace_back(std::move(ep)); + return *this; + } + + /** @brief Adds execution provider for runtime. + The function is used to add ONNX Runtime CUDA Execution Provider options. @param ep CUDA Execution Provider options. @@ -573,6 +656,11 @@ public: desc.execution_providers.emplace_back(std::move(ep)); } + /** @see onnx::Params::cfgAddExecutionProvider. */ + void cfgAddExecutionProvider(ep::CoreML&& ep) { + desc.execution_providers.emplace_back(std::move(ep)); + } + /** @see onnx::Params::cfgAddExecutionProvider. */ void cfgAddExecutionProvider(ep::CUDA&& ep) { desc.execution_providers.emplace_back(std::move(ep)); diff --git a/modules/gapi/include/opencv2/gapi/infer/ov.hpp b/modules/gapi/include/opencv2/gapi/infer/ov.hpp index 99d701f937..02d6225c7a 100644 --- a/modules/gapi/include/opencv2/gapi/infer/ov.hpp +++ b/modules/gapi/include/opencv2/gapi/infer/ov.hpp @@ -679,7 +679,32 @@ protected: }; } // namespace ov + +namespace wip { namespace ov { +/** + * @brief Ask G-API OpenVINO backend to run only inference of model provided. + * + * G-API OpenVINO backend will perform only the inference of the model provided + * without populating input and copying back output data. + * This mode is used to evaluate the pure inference performance of the model without + * taking into account the i/o data transfer. + */ +struct benchmark_mode { }; +/** @} */ + +} // namespace ov +} // namespace wip + } // namespace gapi + +namespace detail +{ + template<> struct CompileArgTag + { + static const char* tag() { return "gapi.wip.ov.benchmark_mode"; } + }; +} + } // namespace cv #endif // OPENCV_GAPI_INFER_OV_HPP diff --git a/modules/gapi/include/opencv2/gapi/ot.hpp b/modules/gapi/include/opencv2/gapi/ot.hpp new file mode 100644 index 0000000000..b73d7e6ee0 --- /dev/null +++ b/modules/gapi/include/opencv2/gapi/ot.hpp @@ -0,0 +1,194 @@ +// This file is part of OpenCV project. +// It is subject to the license terms in the LICENSE file found in the top-level directory +// of this distribution and at http://opencv.org/license.html. +// +// Copyright (C) 2023 Intel Corporation + +#ifndef OPENCV_GAPI_OT_HPP +#define OPENCV_GAPI_OT_HPP + +#include +#include +#include + +namespace cv { +namespace gapi { +/** + * @brief This namespace contains G-API Operation Types for + * VAS Object Tracking module functionality. + */ +namespace ot { + +/** + * @enum TrackingStatus + * + * Tracking status twin for vas::ot::TrackingStatus + */ +enum TrackingStatus +{ + NEW = 0, /**< The object is newly added. */ + TRACKED, /**< The object is being tracked. */ + LOST /**< The object gets lost now. The object can be tracked again + by specifying detected object manually. */ +}; + +struct GAPI_EXPORTS_W_SIMPLE ObjectTrackerParams +{ + /** + * Maximum number of trackable objects in a frame. + * Valid range: 1 <= max_num_objects. Or it can be -1 if there is no limitation + * of maximum number in X86. KMB/TBH has limitation up to 1024. + * Default value is -1 which means there is no limitation in X86. KMB/TBH is -1 means 200. + */ + GAPI_PROP_RW int32_t max_num_objects = -1; + + /** + * Input color format. Supports 0(BGR), 1(NV12), 2(BGRX) and 4(I420) + */ + GAPI_PROP_RW int32_t input_image_format = 0; + + /** + * Specifies whether tracker to use detection class for keeping id of an object. + * If it is true, new detection will be associated from previous tracking only when + * those two have same class. + * class id of an object is fixed across video frames. + * If it is false, new detection can be associated across different-class objects. + * In this case, the class id of an object may change across video frames depending on the tracker input. + * It is recommended to turn this option off when it is likely that detector confuses the class of object. + * For example, when detector confuses bicycle and motorbike. Turning this option off will increase + * the tracking reliability as tracker will ignore the class label of detector. + * @n + * Default value is true. + */ + GAPI_PROP_RW bool tracking_per_class = true; + + bool operator==(const ObjectTrackerParams& other) const + { + return max_num_objects == other.max_num_objects + && input_image_format == other.input_image_format + && tracking_per_class == other.tracking_per_class; + } +}; + +using GTrackedInfo = std::tuple, cv::GArray, cv::GArray, cv::GArray>; + +G_API_OP(GTrackFromMat, , cv::GArray, float)>, "com.intel.track_from_mat") +{ + static std::tuple outMeta(cv::GMatDesc, cv::GArrayDesc, cv::GArrayDesc, float) + { + return std::make_tuple(cv::empty_array_desc(), cv::empty_array_desc(), + cv::empty_array_desc(), cv::empty_array_desc()); + } +}; + +G_API_OP(GTrackFromFrame, , cv::GArray, float)>, "com.intel.track_from_frame") +{ + static std::tuple outMeta(cv::GFrameDesc, cv::GArrayDesc, cv::GArrayDesc, float) + { + return std::make_tuple(cv::empty_array_desc(), cv::empty_array_desc(), + cv::empty_array_desc(), cv::empty_array_desc()); + } +}; + +/** + * @brief Tracks objects with video frames. + * If a detected object is overlapped enough with one of tracked object, the tracked object's + * informationis updated with the input detected object. + * On the other hand, if a detected object is overlapped with none of tracked objects, + * the detected object is newly added and ObjectTracker starts to track the object. + * In zero term tracking type, ObjectTracker clears tracked objects in case that empty + * list of detected objects is passed in. + * + * @param mat Input frame. + * @param detected_rects Detected objects rectangles in the input frame. + * @param detected_class_labels Detected objects class labels in the input frame. + * @param delta Frame_delta_t Delta time between two consecutive tracking in seconds. + * The valid range is [0.005 ~ 0.5]. + * @return Tracking results of target objects. + * cv::GArray Array of rectangles for tracked objects. + * cv::GArray Array of detected objects labels. + * cv::GArray Array of tracking IDs for objects. + * Numbering sequence starts from 1. + * The value 0 means the tracking ID of this object has + * not been assigned. + * cv::GArray Array of tracking statuses for objects. + */ +GAPI_EXPORTS_W std::tuple, + cv::GArray, + cv::GArray, + cv::GArray> + track(const cv::GMat& mat, + const cv::GArray& detected_rects, + const cv::GArray& detected_class_labels, + float delta); + + +/** + @overload + * @brief Tracks objects with video frames. Overload of track(...) for frame as GFrame. + * + * @param frame Input frame. + * @param detected_rects Detected objects rectangles in the input frame. + * @param detected_class_labels Detected objects class labels in the input frame. + * @param delta Frame_delta_t Delta time between two consecutive tracking in seconds. + * The valid range is [0.005 ~ 0.5]. + * @return Tracking results of target objects. + * @return Tracking results of target objects. + * cv::GArray Array of rectangles for tracked objects. + * cv::GArray Array of detected objects labels. + * cv::GArray Array of tracking IDs for objects. + * Numbering sequence starts from 1. + * The value 0 means the tracking ID of this object has + * not been assigned. + * cv::GArray Array of tracking statuses for objects. + */ +GAPI_EXPORTS_W std::tuple, + cv::GArray, + cv::GArray, + cv::GArray> + track(const cv::GFrame& frame, + const cv::GArray& detected_rects, + const cv::GArray& detected_class_labels, + float delta); +} // namespace ot +} // namespace gapi +} // namespace cv + +// FIXME: move to a separate file? +namespace cv +{ +namespace detail +{ +template<> struct CompileArgTag +{ + static const char* tag() + { + return "cv.gapi.ot.object_tracker_params"; + } +}; +} // namespace detail + +namespace gapi +{ +namespace s11n +{ +namespace detail +{ +template<> struct S11N { + static void serialize(IOStream &os, const cv::gapi::ot::ObjectTrackerParams &p) { + os << p. max_num_objects << p.input_image_format << p.tracking_per_class; + } + static cv::gapi::ot::ObjectTrackerParams deserialize(IIStream &is) { + cv::gapi::ot::ObjectTrackerParams p; + is >> p. max_num_objects >> p.input_image_format >> p.tracking_per_class; + return p; + } +}; +} // namespace detail +} // namespace s11n +} // namespace gapi +} // namespace cv + +#endif // OPENCV_GAPI_OT_HPP diff --git a/modules/gapi/misc/python/package/gapi/__init__.py b/modules/gapi/misc/python/package/gapi/__init__.py index 992fa2aee2..2b21e54e41 100644 --- a/modules/gapi/misc/python/package/gapi/__init__.py +++ b/modules/gapi/misc/python/package/gapi/__init__.py @@ -56,6 +56,14 @@ class GOpaque(): def __new__(self): return cv.GOpaqueT(cv.gapi.CV_INT) + class Int64(): + def __new__(self): + return cv.GOpaqueT(cv.gapi.CV_INT64) + + class UInt64(): + def __new__(self): + return cv.GOpaqueT(cv.gapi.CV_UINT64) + class Double(): def __new__(self): return cv.GOpaqueT(cv.gapi.CV_DOUBLE) @@ -111,6 +119,14 @@ class GArray(): def __new__(self): return cv.GArrayT(cv.gapi.CV_INT) + class Int64(): + def __new__(self): + return cv.GArrayT(cv.gapi.CV_INT64) + + class UInt64(): + def __new__(self): + return cv.GArrayT(cv.gapi.CV_UINT64) + class Double(): def __new__(self): return cv.GArrayT(cv.gapi.CV_DOUBLE) @@ -170,6 +186,8 @@ def op(op_id, in_types, out_types): garray_types= { cv.GArray.Bool: cv.gapi.CV_BOOL, cv.GArray.Int: cv.gapi.CV_INT, + cv.GArray.Int64: cv.gapi.CV_INT64, + cv.GArray.UInt64: cv.gapi.CV_UINT64, cv.GArray.Double: cv.gapi.CV_DOUBLE, cv.GArray.Float: cv.gapi.CV_FLOAT, cv.GArray.String: cv.gapi.CV_STRING, @@ -190,6 +208,8 @@ def op(op_id, in_types, out_types): cv.GOpaque.Rect: cv.gapi.CV_RECT, cv.GOpaque.Bool: cv.gapi.CV_BOOL, cv.GOpaque.Int: cv.gapi.CV_INT, + cv.GOpaque.Int64: cv.gapi.CV_INT64, + cv.GOpaque.UInt64: cv.gapi.CV_UINT64, cv.GOpaque.Double: cv.gapi.CV_DOUBLE, cv.GOpaque.Float: cv.gapi.CV_FLOAT, cv.GOpaque.String: cv.gapi.CV_STRING, @@ -205,6 +225,8 @@ def op(op_id, in_types, out_types): type2str = { cv.gapi.CV_BOOL: 'cv.gapi.CV_BOOL' , cv.gapi.CV_INT: 'cv.gapi.CV_INT' , + cv.gapi.CV_INT64: 'cv.gapi.CV_INT64' , + cv.gapi.CV_UINT64: 'cv.gapi.CV_UINT64' , cv.gapi.CV_DOUBLE: 'cv.gapi.CV_DOUBLE' , cv.gapi.CV_FLOAT: 'cv.gapi.CV_FLOAT' , cv.gapi.CV_STRING: 'cv.gapi.CV_STRING' , diff --git a/modules/gapi/misc/python/pyopencv_gapi.hpp b/modules/gapi/misc/python/pyopencv_gapi.hpp index a13b8e545d..66c3910756 100644 --- a/modules/gapi/misc/python/pyopencv_gapi.hpp +++ b/modules/gapi/misc/python/pyopencv_gapi.hpp @@ -31,6 +31,7 @@ using map_string_and_vector_float = std::map>; using map_int_and_double = std::map; using ep_OpenVINO = cv::gapi::onnx::ep::OpenVINO; using ep_DirectML = cv::gapi::onnx::ep::DirectML; +using ep_CoreML = cv::gapi::onnx::ep::CoreML; using ep_CUDA = cv::gapi::onnx::ep::CUDA; using ep_TensorRT = cv::gapi::onnx::ep::TensorRT; @@ -246,6 +247,7 @@ PyObject* pyopencv_from(const cv::GArg& value) HANDLE_CASE(BOOL, bool); HANDLE_CASE(INT, int); HANDLE_CASE(INT64, int64_t); + HANDLE_CASE(UINT64, uint64_t); HANDLE_CASE(DOUBLE, double); HANDLE_CASE(FLOAT, float); HANDLE_CASE(STRING, std::string); @@ -258,7 +260,6 @@ PyObject* pyopencv_from(const cv::GArg& value) HANDLE_CASE(MAT, cv::Mat); HANDLE_CASE(UNKNOWN, cv::detail::PyObjectHolder); HANDLE_CASE(DRAW_PRIM, cv::gapi::wip::draw::Prim); - UNSUPPORTED(UINT64); #undef HANDLE_CASE #undef UNSUPPORTED } @@ -304,6 +305,7 @@ PyObject* pyopencv_from(const cv::detail::OpaqueRef& o) case cv::detail::OpaqueKind::CV_BOOL : return pyopencv_from(o.rref()); case cv::detail::OpaqueKind::CV_INT : return pyopencv_from(o.rref()); case cv::detail::OpaqueKind::CV_INT64 : return pyopencv_from(o.rref()); + case cv::detail::OpaqueKind::CV_UINT64 : return pyopencv_from(o.rref()); case cv::detail::OpaqueKind::CV_DOUBLE : return pyopencv_from(o.rref()); case cv::detail::OpaqueKind::CV_FLOAT : return pyopencv_from(o.rref()); case cv::detail::OpaqueKind::CV_STRING : return pyopencv_from(o.rref()); @@ -314,7 +316,6 @@ PyObject* pyopencv_from(const cv::detail::OpaqueRef& o) case cv::detail::OpaqueKind::CV_RECT : return pyopencv_from(o.rref()); case cv::detail::OpaqueKind::CV_UNKNOWN : return pyopencv_from(o.rref()); case cv::detail::OpaqueKind::CV_DRAW_PRIM : return pyopencv_from(o.rref()); - case cv::detail::OpaqueKind::CV_UINT64 : break; case cv::detail::OpaqueKind::CV_SCALAR : break; case cv::detail::OpaqueKind::CV_MAT : break; } @@ -331,6 +332,7 @@ PyObject* pyopencv_from(const cv::detail::VectorRef& v) case cv::detail::OpaqueKind::CV_BOOL : return pyopencv_from_generic_vec(v.rref()); case cv::detail::OpaqueKind::CV_INT : return pyopencv_from_generic_vec(v.rref()); case cv::detail::OpaqueKind::CV_INT64 : return pyopencv_from_generic_vec(v.rref()); + case cv::detail::OpaqueKind::CV_UINT64 : return pyopencv_from_generic_vec(v.rref()); case cv::detail::OpaqueKind::CV_DOUBLE : return pyopencv_from_generic_vec(v.rref()); case cv::detail::OpaqueKind::CV_FLOAT : return pyopencv_from_generic_vec(v.rref()); case cv::detail::OpaqueKind::CV_STRING : return pyopencv_from_generic_vec(v.rref()); @@ -343,7 +345,6 @@ PyObject* pyopencv_from(const cv::detail::VectorRef& v) case cv::detail::OpaqueKind::CV_MAT : return pyopencv_from_generic_vec(v.rref()); case cv::detail::OpaqueKind::CV_UNKNOWN : return pyopencv_from_generic_vec(v.rref()); case cv::detail::OpaqueKind::CV_DRAW_PRIM : return pyopencv_from_generic_vec(v.rref()); - case cv::detail::OpaqueKind::CV_UINT64 : break; } PyErr_SetString(PyExc_TypeError, "Unsupported GArray type"); @@ -502,6 +503,8 @@ static cv::detail::OpaqueRef extract_opaque_ref(PyObject* from, cv::detail::Opaq { HANDLE_CASE(BOOL, bool); HANDLE_CASE(INT, int); + HANDLE_CASE(INT64, int64_t); + HANDLE_CASE(UINT64, uint64_t); HANDLE_CASE(DOUBLE, double); HANDLE_CASE(FLOAT, float); HANDLE_CASE(STRING, std::string); @@ -511,8 +514,6 @@ static cv::detail::OpaqueRef extract_opaque_ref(PyObject* from, cv::detail::Opaq HANDLE_CASE(SIZE, cv::Size); HANDLE_CASE(RECT, cv::Rect); HANDLE_CASE(UNKNOWN, cv::GArg); - UNSUPPORTED(UINT64); - UNSUPPORTED(INT64); UNSUPPORTED(SCALAR); UNSUPPORTED(MAT); UNSUPPORTED(DRAW_PRIM); @@ -535,6 +536,8 @@ static cv::detail::VectorRef extract_vector_ref(PyObject* from, cv::detail::Opaq { HANDLE_CASE(BOOL, bool); HANDLE_CASE(INT, int); + HANDLE_CASE(INT64, int64_t); + HANDLE_CASE(UINT64, uint64_t); HANDLE_CASE(DOUBLE, double); HANDLE_CASE(FLOAT, float); HANDLE_CASE(STRING, std::string); @@ -547,8 +550,6 @@ static cv::detail::VectorRef extract_vector_ref(PyObject* from, cv::detail::Opaq HANDLE_CASE(MAT, cv::Mat); HANDLE_CASE(UNKNOWN, cv::GArg); HANDLE_CASE(DRAW_PRIM, cv::gapi::wip::draw::Prim); - UNSUPPORTED(UINT64); - UNSUPPORTED(INT64); #undef HANDLE_CASE #undef UNSUPPORTED } diff --git a/modules/gapi/misc/python/python_bridge.hpp b/modules/gapi/misc/python/python_bridge.hpp index 8926b21a79..f384b6907b 100644 --- a/modules/gapi/misc/python/python_bridge.hpp +++ b/modules/gapi/misc/python/python_bridge.hpp @@ -31,6 +31,7 @@ using cv::gapi::wip::draw::Prim; WRAP_ARGS(bool , cv::gapi::ArgType::CV_BOOL, G) \ WRAP_ARGS(int , cv::gapi::ArgType::CV_INT, G) \ WRAP_ARGS(int64_t , cv::gapi::ArgType::CV_INT64, G) \ +WRAP_ARGS(uint64_t , cv::gapi::ArgType::CV_UINT64, G) \ WRAP_ARGS(double , cv::gapi::ArgType::CV_DOUBLE, G) \ WRAP_ARGS(float , cv::gapi::ArgType::CV_FLOAT, G) \ WRAP_ARGS(std::string , cv::gapi::ArgType::CV_STRING, G) \ @@ -49,6 +50,7 @@ WRAP_ARGS(cv::GMat , cv::gapi::ArgType::CV_GMAT, G2) \ WRAP_ARGS(bool , cv::gapi::ArgType::CV_BOOL, G) \ WRAP_ARGS(int , cv::gapi::ArgType::CV_INT, G) \ WRAP_ARGS(int64_t , cv::gapi::ArgType::CV_INT64, G) \ +WRAP_ARGS(uint64_t , cv::gapi::ArgType::CV_UINT64, G) \ WRAP_ARGS(double , cv::gapi::ArgType::CV_DOUBLE, G) \ WRAP_ARGS(float , cv::gapi::ArgType::CV_FLOAT, G) \ WRAP_ARGS(std::string , cv::gapi::ArgType::CV_STRING, G) \ @@ -67,6 +69,7 @@ enum ArgType { CV_BOOL, CV_INT, CV_INT64, + CV_UINT64, CV_DOUBLE, CV_FLOAT, CV_STRING, diff --git a/modules/gapi/misc/python/shadow_gapi.hpp b/modules/gapi/misc/python/shadow_gapi.hpp index c0c1e38136..a87115753a 100644 --- a/modules/gapi/misc/python/shadow_gapi.hpp +++ b/modules/gapi/misc/python/shadow_gapi.hpp @@ -8,6 +8,7 @@ struct GAPI_EXPORTS_W_SIMPLE GCompileArg GAPI_WRAP GCompileArg(GKernelPackage arg); GAPI_WRAP GCompileArg(gapi::GNetPackage arg); GAPI_WRAP GCompileArg(gapi::streaming::queue_capacity arg); + GAPI_WRAP GCompileArg(gapi::ot::ObjectTrackerParams arg); }; class GAPI_EXPORTS_W_SIMPLE GInferInputs diff --git a/modules/gapi/misc/python/test/test_gapi_ot.py b/modules/gapi/misc/python/test/test_gapi_ot.py new file mode 100644 index 0000000000..794ed018e3 --- /dev/null +++ b/modules/gapi/misc/python/test/test_gapi_ot.py @@ -0,0 +1,58 @@ +#!/usr/bin/env python + +import numpy as np +import cv2 as cv +import os +import sys +import unittest + +from tests_common import NewOpenCVTests + + +try: + + if sys.version_info[:2] < (3, 0): + raise unittest.SkipTest('Python 2.x is not supported') + + class gapi_ot_test(NewOpenCVTests): + + def test_ot_smoke(self): + # Input + img_path = self.find_file('cv/face/david2.jpg', [os.environ.get('OPENCV_TEST_DATA_PATH')]) + in_image = cv.cvtColor(cv.imread(img_path), cv.COLOR_RGB2BGR) + in_rects = [ (138, 89, 71, 64) ] + in_rects_cls = [ 0 ] + + # G-API + g_in = cv.GMat() + g_in_rects = cv.GArray.Rect() + g_in_rects_cls = cv.GArray.Int() + delta = 0.5 + + g_out_rects, g_out_rects_cls, g_track_ids, g_track_sts = \ + cv.gapi.ot.track(g_in, g_in_rects, g_in_rects_cls, delta) + + + comp = cv.GComputation(cv.GIn(g_in, g_in_rects, g_in_rects_cls), + cv.GOut(g_out_rects, g_out_rects_cls, + g_track_ids, g_track_sts)) + + __, __, __, sts = comp.apply(cv.gin(in_image, in_rects, in_rects_cls), + args=cv.gapi.compile_args(cv.gapi.ot.cpu.kernels())) + + self.assertEqual(cv.gapi.ot.NEW, sts[0]) + +except unittest.SkipTest as e: + + message = str(e) + + class TestSkip(unittest.TestCase): + def setUp(self): + self.skipTest('Skip tests: ' + message) + + def test_skip(): + pass + + +if __name__ == '__main__': + NewOpenCVTests.bootstrap() diff --git a/modules/gapi/misc/python/test/test_gapi_types.py b/modules/gapi/misc/python/test/test_gapi_types.py index 41bfbabd61..cdf8a089fd 100644 --- a/modules/gapi/misc/python/test/test_gapi_types.py +++ b/modules/gapi/misc/python/test/test_gapi_types.py @@ -17,10 +17,10 @@ try: class gapi_types_test(NewOpenCVTests): def test_garray_type(self): - types = [cv.gapi.CV_BOOL , cv.gapi.CV_INT , cv.gapi.CV_DOUBLE , cv.gapi.CV_FLOAT, - cv.gapi.CV_STRING, cv.gapi.CV_POINT , cv.gapi.CV_POINT2F, cv.gapi.CV_POINT3F , - cv.gapi.CV_SIZE , cv.gapi.CV_RECT , cv.gapi.CV_SCALAR , cv.gapi.CV_MAT , - cv.gapi.CV_GMAT] + types = [cv.gapi.CV_BOOL , cv.gapi.CV_INT , cv.gapi.CV_INT64 , cv.gapi.CV_UINT64, + cv.gapi.CV_DOUBLE , cv.gapi.CV_FLOAT , cv.gapi.CV_STRING, cv.gapi.CV_POINT , + cv.gapi.CV_POINT2F, cv.gapi.CV_POINT3F, cv.gapi.CV_SIZE , cv.gapi.CV_RECT , + cv.gapi.CV_SCALAR , cv.gapi.CV_MAT , cv.gapi.CV_GMAT] for t in types: g_array = cv.GArrayT(t) @@ -28,9 +28,9 @@ try: def test_gopaque_type(self): - types = [cv.gapi.CV_BOOL , cv.gapi.CV_INT , cv.gapi.CV_DOUBLE , cv.gapi.CV_FLOAT , - cv.gapi.CV_STRING, cv.gapi.CV_POINT, cv.gapi.CV_POINT2F, cv.gapi.CV_POINT3F, - cv.gapi.CV_SIZE , cv.gapi.CV_RECT] + types = [cv.gapi.CV_BOOL , cv.gapi.CV_INT , cv.gapi.CV_INT64 , cv.gapi.CV_UINT64, + cv.gapi.CV_DOUBLE , cv.gapi.CV_FLOAT , cv.gapi.CV_STRING, cv.gapi.CV_POINT , + cv.gapi.CV_POINT2F, cv.gapi.CV_POINT3F, cv.gapi.CV_SIZE , cv.gapi.CV_RECT] for t in types: g_opaque = cv.GOpaqueT(t) diff --git a/modules/gapi/src/3rdparty/vasot/LICENSE.txt b/modules/gapi/src/3rdparty/vasot/LICENSE.txt new file mode 100644 index 0000000000..8a60c284c6 --- /dev/null +++ b/modules/gapi/src/3rdparty/vasot/LICENSE.txt @@ -0,0 +1,21 @@ +The MIT License + +Copyright (c) 2018-2019 Intel Corporation + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +THE SOFTWARE. diff --git a/modules/gapi/src/3rdparty/vasot/include/vas/common.hpp b/modules/gapi/src/3rdparty/vasot/include/vas/common.hpp new file mode 100644 index 0000000000..ae6303dbb1 --- /dev/null +++ b/modules/gapi/src/3rdparty/vasot/include/vas/common.hpp @@ -0,0 +1,83 @@ +/******************************************************************************* + * Copyright (C) 2023 Intel Corporation + * + * SPDX-License-Identifier: MIT + ******************************************************************************/ + +#ifndef VAS_COMMON_HPP +#define VAS_COMMON_HPP + +#include + +#define OT_VERSION_MAJOR 1 +#define OT_VERSION_MINOR 0 +#define OT_VERSION_PATCH 0 + +#define VAS_EXPORT //__attribute__((visibility("default"))) + +namespace vas { + +/** + * @class Version + * + * Contains version information. + */ +class Version { + public: + /** + * Constructor. + * + * @param[in] major Major version. + * @param[in] minor Minor version. + * @param[in] patch Patch version. + */ + explicit Version(uint32_t major, uint32_t minor, uint32_t patch) : major_(major), minor_(minor), patch_(patch) { + } + + /** + * Returns major version. + */ + uint32_t GetMajor() const noexcept { + return major_; + } + + /** + * Returns minor version. + */ + uint32_t GetMinor() const noexcept { + return minor_; + } + + /** + * Returns patch version. + */ + uint32_t GetPatch() const noexcept { + return patch_; + } + + private: + uint32_t major_; + uint32_t minor_; + uint32_t patch_; +}; + +/** + * @enum BackendType + * + * Represents HW backend types. + */ +enum class BackendType { + CPU, /**< CPU */ + GPU /**< GPU */ +}; + +/** + * @enum ColorFormat + * + * Represents Color formats. + */ +enum class ColorFormat { BGR, NV12, BGRX, GRAY, I420 }; + +}; // namespace vas + +#endif // VAS_COMMON_HPP diff --git a/modules/gapi/src/3rdparty/vasot/include/vas/ot.hpp b/modules/gapi/src/3rdparty/vasot/include/vas/ot.hpp new file mode 100644 index 0000000000..ef5e1f4b80 --- /dev/null +++ b/modules/gapi/src/3rdparty/vasot/include/vas/ot.hpp @@ -0,0 +1,440 @@ +/******************************************************************************* + * Copyright (C) 2023 Intel Corporation + * + * SPDX-License-Identifier: MIT + ******************************************************************************/ + +#ifndef VAS_OT_HPP +#define VAS_OT_HPP + +#include + +#include + +#include +#include +#include +#include + +namespace vas { + +/** + * @namespace vas::ot + * @brief %vas::ot namespace. + * + * The ot namespace has classes, functions, and definitions for object tracker. + * It is a general tracker, and an object is represented as a rectangular box. + * Thus, you can use any kind of detector if it generates a rectangular box as output. + * Once an object is added to object tracker, the object is started to be tracked. + */ +namespace ot { + +/** + * Returns current version. + */ +VAS_EXPORT vas::Version GetVersion() noexcept; + +/** + * @enum TrackingType + * + * Tracking type. + */ +enum class TrackingType { + LONG_TERM, + SHORT_TERM, + ZERO_TERM, + SHORT_TERM_KCFVAR, + SHORT_TERM_IMAGELESS, + ZERO_TERM_IMAGELESS, + ZERO_TERM_COLOR_HISTOGRAM +}; + +/** + * @enum TrackingStatus + * + * Tracking status. + */ +enum class TrackingStatus { + NEW = 0, /**< The object is newly added. */ + TRACKED, /**< The object is being tracked. */ + LOST /**< The object gets lost now. The object can be tracked again automatically(long term tracking) or by + specifying detected object manually(short term and zero term tracking). */ +}; + +/** + * @class DetectedObject + * @brief Represents an input object. + * + * In order to track an object, detected object should be added one or more times to ObjectTracker. + * When an object is required to be added to ObjectTracker, you can create an instance of this class and fill its + * values. + */ +class DetectedObject { + public: + /** + * Default constructor. + */ + DetectedObject() : rect(), class_label() { + } + + /** + * Constructor with specific values. + * + * @param[in] input_rect Rectangle of input object. + * @param[in] input_class_label Class label of input object. + */ + DetectedObject(const cv::Rect &input_rect, int32_t input_class_label) + : rect(input_rect), class_label(input_class_label) { + } + + public: + /** + * Object rectangle. + */ + cv::Rect rect; + + /** + * Input class label. + * It is an arbitrary value that is specified by user. + * You can utilize this value to categorize input objects. + * Same value will be assigned to the class_label in Object class. + */ + int32_t class_label; +}; + +/** + * @class Object + * @brief Represents tracking result of a target object. + * + * It contains tracking information of a target object. + * ObjectTracker generates an instance of this class per tracked object when Track method is called. + */ +class Object { + public: + /** + * Object rectangle. + */ + cv::Rect rect; + + /** + * Tracking ID. + * Numbering sequence starts from 1. + * The value 0 means the tracking ID of this object has not been assigned. + */ + uint64_t tracking_id; + + /** + * Class label. + * This is specified by DetectedObject. + */ + int32_t class_label; + + /** + * Tracking status. + */ + TrackingStatus status; + + /** + * Index in the DetectedObject vector. + * If the Object was not in detection input at this frame, then it will be -1. + */ + int32_t association_idx; +}; + +VAS_EXPORT std::ostream &operator<<(std::ostream &os, TrackingStatus ts); +VAS_EXPORT std::ostream &operator<<(std::ostream &os, const Object &object); + +/** + * @class ObjectTracker + * @brief Tracks objects from video frames. + * + * This class tracks objects from the input frames. + * In order to create an instance of this class, you need to use ObjectTracker::Builder class. + * @n + * ObjectTracker can run in three different ways as TrackingType defines. + * @n + * In short term tracking, an object is added at the beginning, and the object is tracked with consecutive input frames. + * It is recommended to update the tracked object's information for every 10-20 frames. + * @n + * Zero term tracking can be thought as association between a detected object and tracked object. + * Detected objects should always be added when Track method is invoked. + * For each frame, detected objects are mapped to tracked objects with this tracking type, which enables ID tracking of + detected objects. + * @n + * Long term tracking is deprecated. + * In long term tracking, an object is added at the beginning, and the object is tracked with consecutive input frames. + * User doesn't need to update manually the object's information. + * Long term tracking takes relatively long time to track objects. + * @n + * You can specify tracking type by setting attributes of Builder class when you create instances of this class. + * It is not possible to run ObjectTracker with two or more different tracking types in one instance. + * You can also limit the number of tracked objects by setting attributes of Builder class. + * @n + * Currently, ObjectTracker does not support HW offloading. + * It is possible to run ObjectTracker only on CPU. + * @n@n + * Following sample code shows how to use short term tracking type. + * Objects are added to ObjectTracker at the beginnning of tracking and in the middle of tracking periodically as well. + * @code + cv::VideoCapture video("/path/to/video/source"); + cv::Mat frame; + cv::Mat first_frame; + video >> first_frame; + + vas::ot::ObjectTracker::Builder ot_builder; + auto ot = ot_builder.Build(vas::ot::TrackingType::SHORT_TERM); + + vas::pvd::PersonVehicleDetector::Builder pvd_builder; + auto pvd = pvd_builder.Build("/path/to/directory/of/fd/model/files"); + + std::vector person_vehicles; + std::vector detected_objects; + + // Assume that there're objects in the first frame + person_vehicles = pvd->Detect(first_frame); + for (const auto& pv : person_vehicles) + detected_objects.emplace_back(pv.rect, static_cast(pv.type)); + + ot->Track(first_frame, detected_objects); + + // Assume that now pvd is running in another thread + StartThread(pvd); + + while (video.read(frame)) + { + detected_objects.clear(); + + // Assume that frames are forwarded to the thread on which pvd is running + EnqueueFrame(frame); + + // Assumes that pvd is adding its result into a queue in another thread. + // Assumes also that latency from the last pvd frame to current frame is ignorable. + person_vehicles = DequeuePersonVehicles(); + if (!person_vehicles.empty()) + { + detected_objects.clear(); + for (const auto& pv : person_vehicles) + detected_objects.emplace_back(pv.rect, static_cast(pv.type)); + } + + auto objects = ot->Track(frame, detected_objects); + for (const auto& object : objects) + { + // Handle tracked object + } + } + * @endcode + * @n + * Following sample code shows how to use zero term tracking type. + * In this sample, pvd runs for each input frame. + * After pvd generates results, ot runs with the results and object IDs are preserved. + * @code + cv::VideoCapture video("/path/to/video/source"); + cv::Mat frame; + + vas::ot::ObjectTracker::Builder ot_builder; + auto ot = ot_builder.Build(vas::ot::TrackingType::ZERO_TERM); + + vas::pvd::PersonVehicleDetector::Builder pvd_builder; + auto pvd = pvd_builder.Build("/path/to/directory/of/fd/model/files"); + + std::vector detected_objects; + + ot->SetFrameDeltaTime(0.033f); + while (video.read(frame)) + { + detected_objects.clear(); + + auto person_vehicles = pvd->Detect(first_frame); + for (const auto& pv : person_vehicles) + detected_objects.emplace_back(pv.rect, static_cast(pv.type)); + + auto objects = ot->Track(frame, detected_objects); + for (const auto& object : objects) + { + // Handle tracked object + } + } + * @endcode + */ +class ObjectTracker { + public: + class Builder; + + public: + ObjectTracker() = delete; + ObjectTracker(const ObjectTracker &) = delete; + ObjectTracker(ObjectTracker &&) = delete; + + /** + * Destructor. + */ + VAS_EXPORT ~ObjectTracker(); + + public: + ObjectTracker &operator=(const ObjectTracker &) = delete; + ObjectTracker &operator=(ObjectTracker &&) = delete; + + public: + /** + * Tracks objects with video frames. + * Also, this method is used to add detected objects. + * If a detected object is overlapped enough with one of tracked object, the tracked object's information is updated + * with the input detected object. On the other hand, if a detected object is overlapped with none of tracked + * objects, the detected object is newly added and ObjectTracker starts to track the object. In long term and short + * term tracking type, ObjectTracker continues to track objects in case that empty list of detected objects is + * passed in. In zero term tracking type, however, ObjectTracker clears tracked objects in case that empty list of + * detected objects is passed in. + * @n + * The supported color formats are BGR, NV12, BGRx and I420. + * + * @param[in] frame Input frame. + * @param[in] detected_objects Detected objects in the input frame. Default value is an empty vector. + * @return Information of tracked objects. + * @exception std::invalid_argument Input frame is invalid. + */ + VAS_EXPORT std::vector + Track(const cv::Mat &frame, const std::vector &detected_objects = std::vector()); + + /** + * This function is to set a parameter indicating 'delta time' between now and last call to Track() in seconds. + * The default value of the delta time is 0.033f which is tuned for 30 fps video frame rate. + * It is to achieve improved tracking quality for other frame rates or inconstant frame rate by frame drops. + * If input frames come from a video stream of constant frame rate, then a user needs to set this value as 1.0/fps + * just after video open. For example, 60 fps video stream should set 0.0167f. If input frames have inconstant frame + * rate, then a user needs to call this function before the Track() function. + * + * @param[in] frame_delta_t Delta time between two consecutive tracking in seconds. The valid range is [0.005 ~ + * 0.5]. + */ + VAS_EXPORT void SetFrameDeltaTime(float frame_delta_t); + + /** + * Returns the tracking type of current instance. + */ + VAS_EXPORT TrackingType GetTrackingType() const noexcept; + + /** + * Returns the currently set maximum number of trackable objects. + */ + VAS_EXPORT int32_t GetMaxNumObjects() const noexcept; + + /** + * Returns the currently set frame delta time. + */ + VAS_EXPORT float GetFrameDeltaTime() const noexcept; + + /** + * Returns the currently set color format. + */ + VAS_EXPORT vas::ColorFormat GetInputColorFormat() const noexcept; + + /** + * Returns the backend type of current instance. + */ + VAS_EXPORT vas::BackendType GetBackendType() const noexcept; + + /** + * Returns the current set tracking per class. + */ + VAS_EXPORT bool GetTrackingPerClass() const noexcept; + + private: + class Impl; + + private: + explicit ObjectTracker(Impl *impl); + + private: + std::unique_ptr impl_; + friend class Builder; +}; + +/** + * @class ObjectTracker::Builder + * @brief Creates ObjectTracker instances. + * + * This class is used to build ObjectTracker instances. + * All the attributes of this class affects how ObjectTracker is initialized. + */ +class ObjectTracker::Builder { + public: + /** + * Default constructor. + */ + VAS_EXPORT Builder(); + + /** + * Destructor. + */ + VAS_EXPORT ~Builder(); + + public: + /** + * Creates an instance of ObjectTracker based on tracking type and attributes you set. + * In case that you set valid values for all attributes, an instance of ObjectTracker is created successfully. + * + * @param[in] tracking_type Tracking type for newly created ObjectTracker instance. + * @exception std::invalid_argument One or more attributes you set are invalid. + * @return ObjectTracker instance. + */ + VAS_EXPORT std::unique_ptr Build(TrackingType tracking_type) const; + + public: + /** + * Specifies HW backend on which object tracker runs. + * @n + * Default value is vas::BackendType::CPU. + */ + vas::BackendType backend_type; + + /** + * Maximum number of trackable objects in a frame. + * @n + * Valid range: 1 <= max_num_objects. Or it can be -1 if there is no limitation of maximum number in X86. + * @n + * Default value is -1 which means there is no limitation in X86. + */ + int32_t max_num_objects; + + /** + * Input color format vas::ColorFormat. Supports BGR, BGRX, NV12 and I420 + * @n + * Default value is BGR. + */ + vas::ColorFormat input_image_format; + + /** + * Specifies whether tracker to use detection class for keeping id of an object. + * If it is true, new detection will be associated from previous tracking only when those two have same class. + * class id of an object is fixed across video frames. + * If it is false, new detection can be associated across different-class objects. + * In this case, the class id of an object may change across video frames depending on the tracker input. + * It is recommended to turn this option off when it is likely that detector confuses the class of object. + * For example, when detector confuses bicycle and motorbike. Turning this option off will increase the tracking + * reliability as tracker will ignore the class label of detector. + * @n + * Default value is true. + */ + bool tracking_per_class; + + /** + * Platform configuration + * You can set various configuraions for each platform using predefined configurations + * @n + * For Parallelization in KCFVAR mode, use key "max_num_threads" to set the maximum number of threads. Consult the + * following format + * @code platform_config["max_num_threads"] = "2"; // set maximum number of threads(concurrency level) to 2 @endcode + * @n + * Default value is 1 + * if value >=1, set value as the number of threads to process OT in parallel mode + * if value >= Number of available cores OR value is -1, limit concurrency level to maximum available logical cores + * otherwise: @exception Invalid input + */ + std::map platform_config; +}; + +}; // namespace ot +}; // namespace vas + +#endif // VAS_OT_HPP diff --git a/modules/gapi/src/3rdparty/vasot/src/common/exception.hpp b/modules/gapi/src/3rdparty/vasot/src/common/exception.hpp new file mode 100644 index 0000000000..72c8e510a3 --- /dev/null +++ b/modules/gapi/src/3rdparty/vasot/src/common/exception.hpp @@ -0,0 +1,24 @@ +/******************************************************************************* + * Copyright (C) 2023 Intel Corporation + * + * SPDX-License-Identifier: MIT + ******************************************************************************/ + +#ifndef VAS_COMMON_EXCEPTION_HPP +#define VAS_COMMON_EXCEPTION_HPP + +#include + +#include +#include + +#define ETHROW(condition, exception_class, message, ...) \ + { \ + if (!(condition)) { \ + throw std::exception_class(message); \ + } \ + } + +#define TRACE(fmt, ...) + +#endif // VAS_COMMON_EXCEPTION_HPP diff --git a/modules/gapi/src/3rdparty/vasot/src/common/prof.hpp b/modules/gapi/src/3rdparty/vasot/src/common/prof.hpp new file mode 100644 index 0000000000..7584841584 --- /dev/null +++ b/modules/gapi/src/3rdparty/vasot/src/common/prof.hpp @@ -0,0 +1,144 @@ +/******************************************************************************* + * Copyright (C) 2023 Intel Corporation + * + * SPDX-License-Identifier: MIT + ******************************************************************************/ + +#ifndef VAS_COMMON_PROF_HPP +#define VAS_COMMON_PROF_HPP + +#include +#include +#include +#include +#include +#include +#include +#include + +#define PROF_COMP_NAME(comp) vas::Prof::Component::comp + +#ifdef BUILD_OPTION_PROFILING +#define PROF_INIT(component) vas::Prof::Init(PROF_COMP_NAME(component)) +#define PROF_START(tag) vas::Prof::Start(tag, __FUNCTION__, __LINE__) +#define PROF_END(tag) vas::Prof::End(tag) +#define PROF_EXTRA(tag, value) vas::Prof::SetExtra(tag, value) +#define PROF_FLUSH(component) vas::Prof::GetInstance(PROF_COMP_NAME(component)).Flush() +#else +#define PROF_INIT(tag) +#define PROF_START(tag) +#define PROF_END(tag) +#define PROF_EXTRA(tag, value) +#define PROF_FLUSH(component) +#endif + +#define PROF_TAG_GENERATE(component, group_id, description) \ + { PROF_COMP_NAME(component), group_id, description } + +namespace vas { + +/** + * @class Prof + * + * Global Prof instance accumulates all ProfData in a Tree structure. + * Parallel codes within sigle vas component (ex. STKCF TBB) creates wrong profile result. + */ +class Prof { + public: + enum class Component : int32_t { FD, FR, PVD, CD, FAC, OT, PAC, HD, REID, BASE, KN, kCount }; + + typedef uint64_t GroupId; + typedef uint64_t UniqueId; + + /** + * @class Prof::ProfData + * + * Data Node withtin Prof class + * Accumulates elapsed times between PROF_START / PROF_END + */ + class ProfData { + public: + ProfData(UniqueId id, GroupId group_id, size_t depth, const char *function_name, const int64_t line, + const char *description); + ProfData(const ProfData &other); + ~ProfData() = default; + ProfData &operator=(const ProfData &) = delete; + bool operator==(const ProfData &) const; + ProfData *clone(); + + std::vector accum_time; + std::list children; + + const UniqueId id; + const GroupId group_id; + const size_t depth; + + const char *function_name; + const int64_t line; + const char *description; + int64_t start_time; + }; + + typedef struct _ProfTag { + vas::Prof::Component component; + vas::Prof::GroupId group_id; + const char *description; + } ProfTag; + + public: + Prof(); + ~Prof() = default; + + static void Init(Component comp); + static void Start(const ProfTag &tag, const char *function_name, int64_t line); + static void End(const ProfTag &tag); + + static Prof &GetInstance(Component comp); + + static void SetExtra(const ProfTag &tag, int32_t value); + + void StartProfile(GroupId group_id, const char *function_name, int64_t line, const char *description); + void EndProfile(); + void SetExtraData(const std::string &key, int32_t value); + void Flush(); + + void MergeToMainInstance(Prof *in); + + private: + const char *GetComponentName(Component comp); + void Clear(); + + // Print detailed prof data. + void PrintSummary1(std::ostream *out); + + // Print prof data merged in same stack. + void PrintSummary2(std::ostream *out); + + // Print prof data merged with the same group-id. + void PrintSummary3(std::ostream *out); + + void PrintSummary1ToCSV(std::ostream *out); + + void PrintExtra(std::ostream *out); + + void PrintAllData(std::ostream *out); + + void Traverse(const ProfData *root, const std::list &data_list, + void (*print_function)(const ProfData *, const ProfData &, std::ostream *), std::ostream *out); + void TraverseMergeSameStackGroups(const std::list &in_data_list, std::list *out_data_list); + void TraverseMergeAllGroups(const std::list &in_data_list, std::list *out_data_list); + + void MergeProfDataList(std::list *mergeList, const std::list &addList); + + private: + std::string outdir_; + std::string out_prof_file_; + Component component_; + std::list root_data_list_; + std::stack current_data_; + std::map> extra_data_; +}; + +} // namespace vas + +#endif // VAS_COMMON_PROF_HPP diff --git a/modules/gapi/src/3rdparty/vasot/src/components/ot/kalman_filter/kalman_filter_no_opencv.cpp b/modules/gapi/src/3rdparty/vasot/src/components/ot/kalman_filter/kalman_filter_no_opencv.cpp new file mode 100644 index 0000000000..31aa8cb17e --- /dev/null +++ b/modules/gapi/src/3rdparty/vasot/src/components/ot/kalman_filter/kalman_filter_no_opencv.cpp @@ -0,0 +1,292 @@ +/******************************************************************************* + * Copyright (C) 2023 Intel Corporation + * + * SPDX-License-Identifier: MIT + ******************************************************************************/ + +#include "kalman_filter_no_opencv.hpp" +#include "../../../common/exception.hpp" + +#include + +#define KALMAN_FILTER_NSHIFT 4 +#define KF_USE_PARTIAL_64F + +namespace vas { + +// R +const int32_t kNoiseCovarFactor = 8; +const float kDefaultDeltaT = 0.033f; +const int32_t kDefaultErrCovFactor = 1; + +// lower noise = slowly changed +KalmanFilterNoOpencv::KalmanFilterNoOpencv(const cv::Rect2f &initial_rect) : delta_t_(kDefaultDeltaT) { + int32_t left = static_cast(initial_rect.x); + int32_t right = static_cast(initial_rect.x + initial_rect.width); + int32_t top = static_cast(initial_rect.y); + int32_t bottom = static_cast(initial_rect.y + initial_rect.height); + int32_t cX = (left + right) << (KALMAN_FILTER_NSHIFT - 1); + int32_t cY = (top + bottom) << (KALMAN_FILTER_NSHIFT - 1); + int32_t cRX = (right - left) << (KALMAN_FILTER_NSHIFT - 1); + int32_t cRY = (bottom - top) << (KALMAN_FILTER_NSHIFT - 1); + kalmanfilter1d32i_init(&kfX, &cX, 0); + kalmanfilter1d32i_init(&kfY, &cY, 0); + kalmanfilter1d32i_init(&kfRX, &cRX, 0); + kalmanfilter1d32i_init(&kfRY, &cRY, 0); + + int32_t object_size = std::max(64, (cRX * cRY)); + noise_ratio_coordinates_ = kMeasurementNoiseCoordinate; + noise_ratio_rect_size_ = kMeasurementNoiseRectSize; + + // Set default Q + int32_t cood_cov = static_cast(object_size * noise_ratio_coordinates_); + int32_t size_cov = static_cast(object_size * noise_ratio_rect_size_); + kfX.Q[0][0] = cood_cov; + kfX.Q[1][1] = cood_cov; + kfY.Q[0][0] = cood_cov; + kfY.Q[1][1] = cood_cov; + kfRX.Q[0][0] = size_cov; + kfRY.Q[0][0] = size_cov; +} + +cv::Rect2f KalmanFilterNoOpencv::Predict(float delta_tf) { + delta_t_ = delta_tf; + + kalmanfilter1d32i_predict_phase(&kfX, delta_tf); + kalmanfilter1d32i_predict_phase(&kfY, delta_tf); + kalmanfilter1d32i_predict_phase(&kfRX, 0); + kalmanfilter1d32i_predict_phase(&kfRY, 0); + + int32_t cp_x = kfX.Xk[0] >> KALMAN_FILTER_NSHIFT; + int32_t cp_y = kfY.Xk[0] >> KALMAN_FILTER_NSHIFT; + int32_t rx = kfRX.Xk[0] >> KALMAN_FILTER_NSHIFT; + int32_t ry = kfRY.Xk[0] >> KALMAN_FILTER_NSHIFT; + + int32_t pre_x = cp_x - rx; + int32_t pre_y = cp_y - ry; + auto width = 2 * rx; + auto height = 2 * ry; + + // printf(" - In Predict: result (%d, %d %dx%d)\n", pre_x, pre_y, width, height); + return cv::Rect2f(float(pre_x), float(pre_y), + float(width), float(height)); +} + +cv::Rect2f KalmanFilterNoOpencv::Correct(const cv::Rect2f &measured_region) { + int32_t pX = static_cast(measured_region.x + (measured_region.x + measured_region.width)) + << (KALMAN_FILTER_NSHIFT - 1); + int32_t pY = static_cast(measured_region.y + (measured_region.y + measured_region.height)) + << (KALMAN_FILTER_NSHIFT - 1); + int32_t pRX = static_cast(measured_region.width) << (KALMAN_FILTER_NSHIFT - 1); + int32_t pRY = static_cast(measured_region.height) << (KALMAN_FILTER_NSHIFT - 1); + int32_t cX = 0; + int32_t cY = 0; + int32_t cRX = 0; + int32_t cRY = 0; + + int32_t delta_t = static_cast(delta_t_ * 31.3f); + if (delta_t < kDefaultErrCovFactor) + delta_t = kDefaultErrCovFactor; + + // Set rect-size-adaptive process/observation noise covariance + int32_t object_size = std::max(64, (pRX * pRY)); + // Q + int32_t cood_cov = static_cast(object_size * noise_ratio_coordinates_ * delta_t); + int32_t size_cov = static_cast(object_size * noise_ratio_rect_size_ * delta_t); + + kfX.Q[0][0] = cood_cov; + kfX.Q[1][1] = cood_cov; + kfY.Q[0][0] = cood_cov; + kfY.Q[1][1] = cood_cov; + kfRX.Q[0][0] = size_cov; + kfRY.Q[0][0] = size_cov; + + if (kfX.Xk[0] == 0 && kfY.Xk[0] == 0) { + kalmanfilter1d32i_predict_phase(&kfX, delta_t_); + kalmanfilter1d32i_predict_phase(&kfY, delta_t_); + kalmanfilter1d32i_predict_phase(&kfRX, 0); + kalmanfilter1d32i_predict_phase(&kfRY, 0); + } + + // R + int32_t noise_covariance = object_size >> (kNoiseCovarFactor + delta_t); + kfX.R = noise_covariance; + kfY.R = noise_covariance; + kfRX.R = noise_covariance; + kfRY.R = noise_covariance; + + kalmanfilter1d32i_update_phase(&kfX, pX, &cX); + kalmanfilter1d32i_update_phase(&kfY, pY, &cY); + kalmanfilter1d32i_update_phase(&kfRX, pRX, &cRX); + kalmanfilter1d32i_update_phase(&kfRY, pRY, &cRY); + + auto x = (cX - cRX) >> KALMAN_FILTER_NSHIFT; + auto y = (cY - cRY) >> KALMAN_FILTER_NSHIFT; + auto width = (cRX >> (KALMAN_FILTER_NSHIFT - 1)); + auto height = (cRY >> (KALMAN_FILTER_NSHIFT - 1)); + + // printf(" - In Correct: result (%d, %d %dx%d)\n", x, y, width, height); + return cv::Rect2f(float(x), float(y), + float(width), float(height)); +} + +void KalmanFilterNoOpencv::kalmanfilter1d32i_init(kalmanfilter1d32i *kf, int32_t *z, int32_t var) { + std::memset(kf, 0, sizeof(kalmanfilter1d32i)); + if (z) { + kf->X[0] = *z; + } + + kf->P[0][0] = var; + kf->P[1][1] = 0; +} + +static void mul_matvec_32f(int32_t Ab[2], float A[2][2], int32_t b[2]) { + Ab[0] = static_cast(A[0][0] * b[0] + A[0][1] * b[1]); // b[0] + dt * b[1] + Ab[1] = static_cast(A[1][0] * b[0] + A[1][1] * b[1]); // b[1] ( A[1][0] == 0) +} + +static void mul_matmat_32f(int32_t AB[2][2], float trans_mat[2][2], int32_t B[2][2]) { + AB[0][0] = + static_cast(trans_mat[0][0] * B[0][0] + trans_mat[0][1] * B[1][0]); // kf->P[0][0] + dt * kf->P[1][0] + AB[0][1] = + static_cast(trans_mat[0][0] * B[0][1] + trans_mat[0][1] * B[1][1]); // kf->P[0][1] + dt * kf->P[1][1] + AB[1][0] = static_cast(trans_mat[1][1] * B[1][0]); + AB[1][1] = static_cast(trans_mat[1][1] * B[1][1]); +} + +#ifndef KF_USE_PARTIAL_64F +static void mul_matmat_32i(int32_t AB[2][2], int32_t A[2][2], int32_t B[2][2]) { + AB[0][0] = A[0][0] * B[0][0] + A[0][1] * B[1][0]; + AB[0][1] = A[0][0] * B[0][1] + A[0][1] * B[1][1]; + AB[1][0] = A[1][0] * B[0][0] + A[1][1] * B[1][0]; + AB[1][1] = A[1][0] * B[0][1] + A[1][1] * B[1][1]; +} + +static void mul_matmatT_32i(int32_t ABt[2][2], int32_t A[2][2], int32_t B[2][2]) { + ABt[0][0] = A[0][0] * B[0][0] + A[0][1] * B[0][1]; + ABt[0][1] = A[0][0] * B[1][0] + A[0][1] * B[1][1]; + ABt[1][0] = A[1][0] * B[0][0] + A[1][1] * B[0][1]; + ABt[1][1] = A[1][0] * B[1][0] + A[1][1] * B[1][1]; +} +#endif + +static void mul_matmatT_32f(int32_t ABt[2][2], int32_t A[2][2], float B[2][2]) { + ABt[0][0] = static_cast(A[0][0] * B[0][0] + A[0][1] * B[0][1]); + ABt[0][1] = static_cast(A[0][0] * B[1][0] + A[0][1] * B[1][1]); + ABt[1][0] = static_cast(A[1][0] * B[0][0] + A[1][1] * B[0][1]); + ABt[1][1] = static_cast(A[1][0] * B[1][0] + A[1][1] * B[1][1]); +} + +static void add_matmat_32i(int32_t A_B[2][2], int32_t A[2][2], int32_t B[2][2]) { + A_B[0][0] = A[0][0] + B[0][0]; + A_B[0][1] = A[0][1] + B[0][1]; + A_B[1][0] = A[1][0] + B[1][0]; + A_B[1][1] = A[1][1] + B[1][1]; +} + +void KalmanFilterNoOpencv::kalmanfilter1d32i_predict_phase(kalmanfilter1d32i *kf, float dt) { + float F[2][2] = {{1.f, 1.f}, {0.f, 1.f}}; + float A[2][2] = {{1.f, 1.f}, {0.f, 1.f}}; + int32_t AP[2][2]; + int32_t APAt[2][2]; + + float weight = 8.f; // 2^(KALMAN_FILTER_NSHIFT - 1) + float delta_t = dt * weight; + + F[0][1] = delta_t; + + // Predict state + // - [x(k) = F x(k-1)] + mul_matvec_32f(kf->Xk, F, kf->X); + + // Predict error estimate covariance matrix (Predicted estimate covariance) : P(k) + // - [P(k) = F P(k-1) Ft + Q] + mul_matmat_32f(AP, A, kf->P); + mul_matmatT_32f(APAt, AP, A); + add_matmat_32i(kf->Pk, APAt, kf->Q); + + // Update kf->x from x(k-1) to x(k) + kf->X[0] = kf->Xk[0]; + kf->X[1] = kf->Xk[1]; + + // Update kf->P from P(k-1) to P(k) + kf->P[0][0] = kf->Pk[0][0]; + kf->P[0][1] = kf->Pk[0][1]; + kf->P[1][0] = kf->Pk[1][0]; + kf->P[1][1] = kf->Pk[1][1]; +} + +void KalmanFilterNoOpencv::kalmanfilter1d32i_update_phase(kalmanfilter1d32i *kf, int32_t z, int32_t *x) { + int32_t y; + int32_t S; + int32_t K[2]; + int32_t I_KH[2][2]; + + if (kf->Xk[0] == 0 && kf->Pk[0][0] == 0) { + (*x) = z; + return; + } + + // Compute measurement pre-fit residual : Y + // H : measurement matrix + // z(k) : actual reading(observed) result of k + // - [ Y(k) = z(k) - H * X(k) ] + y = z - kf->Xk[0]; + + // Compute residual covariance : S + // - [ S = H*P(k)*Ht + R] + S = kf->Pk[0][0] + kf->R; + + if (S == 0) { + (*x) = z; + return; + } + + // Compute optimal kalman gain : K(k) + // - [ K(k) = P(k)*Ht*inv(S)] + // K[0] = kf->P[0][0]/S; + // K[1] = kf->P[1][0]/S; + K[0] = kf->Pk[0][0]; + K[1] = kf->Pk[1][0]; + + // Get updated state + // - [ x'(k) = x(k) + K'*Y )] + kf->X[0] = kf->Xk[0] + K[0] * y / S; + kf->X[1] = kf->Xk[1] + K[1] * y / S; + + // 7. Get updated estimate covariance : P'(k) + // - [ P'(k) = (I - K(k) * H) * P(k)] + I_KH[0][0] = S - K[0]; + I_KH[0][1] = 0; + I_KH[1][0] = -K[1]; + I_KH[1][1] = S; + + // modified by chan - 20110329 - start + // Here, INTEGER is 32bit. + // To avoid overflow in the below matrix multiplecation, this code is modified. +#ifdef KF_USE_PARTIAL_64F + { + kf->P[0][0] = static_cast( + (I_KH[0][0] * static_cast(kf->Pk[0][0]) + I_KH[0][1] * static_cast(kf->Pk[1][0])) / S); + kf->P[0][1] = static_cast( + (I_KH[0][0] * static_cast(kf->Pk[0][1]) + I_KH[0][1] * static_cast(kf->Pk[1][1])) / S); + kf->P[1][0] = static_cast( + (I_KH[1][0] * static_cast(kf->Pk[0][0]) + I_KH[1][1] * static_cast(kf->Pk[1][0])) / S); + kf->P[1][1] = static_cast( + (I_KH[1][0] * static_cast(kf->Pk[0][1]) + I_KH[1][1] * static_cast(kf->Pk[1][1])) / S); + } +#else // KF_USE_PARTIAL_64F + { + mul_matmat_32i(kf->P, I_KH, kf->Pk); + kf->P[0][0] /= S; + kf->P[0][1] /= S; + kf->P[1][0] /= S; + kf->P[1][1] /= S; + } +#endif // KF_USE_PARTIAL_64F + + // 9. return result + (*x) = kf->X[0]; +} + +}; // namespace vas diff --git a/modules/gapi/src/3rdparty/vasot/src/components/ot/kalman_filter/kalman_filter_no_opencv.hpp b/modules/gapi/src/3rdparty/vasot/src/components/ot/kalman_filter/kalman_filter_no_opencv.hpp new file mode 100644 index 0000000000..1f8cd4cf35 --- /dev/null +++ b/modules/gapi/src/3rdparty/vasot/src/components/ot/kalman_filter/kalman_filter_no_opencv.hpp @@ -0,0 +1,91 @@ +/******************************************************************************* + * Copyright (C) 2023 Intel Corporation + * + * SPDX-License-Identifier: MIT + ******************************************************************************/ + +#ifndef VAS_OT_KALMAN_FILTER_NO_OPENCV_HPP +#define VAS_OT_KALMAN_FILTER_NO_OPENCV_HPP + +#include + +#include + +const float kMeasurementNoiseCoordinate = 0.001f; + +const float kMeasurementNoiseRectSize = 0.002f; + +namespace vas { + +/* + * This class implements a kernel of a standard kalman filter without using of OpenCV. + * It supplies simple and common APIs to be use by all components. + * + */ +class KalmanFilterNoOpencv { + public: + /** @brief Create & initialize KalmanFilterNoOpencv + * This function initializes Kalman filter with a spectific value of the ratio for measurement noise covariance + * matrix. If you consider the detection method is enough reliable, it is recommended to use lower ratio value than + * the default value. + * @code + * cv::Rect2f input_rect(50.f, 50.f, 100.f, 100.f); + * cv::Rect2f predicted, corrected; + * vas::KalmanFilter kalman_filter = new vas::KalmanFilter(input_rect); + * predicted = kalman_filter->Predict(); + * corrected = kalman_filter->Correct(cv::Rect(52, 52, 105, 105)); + * delete kalman_filter; + * @endcode + * @param + * initial_rect Initial rectangular coordinates + */ + explicit KalmanFilterNoOpencv(const cv::Rect2f &initial_rect); + KalmanFilterNoOpencv() = delete; + + KalmanFilterNoOpencv(const KalmanFilterNoOpencv &) = delete; + KalmanFilterNoOpencv &operator=(const KalmanFilterNoOpencv &) = delete; + + /* @brief Destroy Kalman filter kernel + */ + ~KalmanFilterNoOpencv() = default; + + /* + * This function computes a predicted state. + * input 'delta_t' is not used. + */ + cv::Rect2f Predict(float delta_t = 0.033f); + + /* + * This function updates the predicted state from the measurement. + */ + cv::Rect2f Correct(const cv::Rect2f &detect_rect); + + private: + struct kalmanfilter1d32i { + int32_t X[2]; + int32_t P[2][2]; + int32_t Q[2][2]; + int32_t R; + + int32_t Pk[2][2]; // buffer to copy from Pk-1 to Pk + int32_t Xk[2]; // buffer to copy form Xk-1 to Xk + }; + + void kalmanfilter1d32i_init(kalmanfilter1d32i *kf, int32_t *z, int32_t var); + void kalmanfilter1d32i_predict_phase(kalmanfilter1d32i *kf, float dt); + void kalmanfilter1d32i_update_phase(kalmanfilter1d32i *kf, int32_t z, int32_t *x); + void kalmanfilter1d32i_filter(kalmanfilter1d32i *kf, int32_t *z, int32_t dt, int32_t *x); + + kalmanfilter1d32i kfX; + kalmanfilter1d32i kfY; + kalmanfilter1d32i kfRX; + kalmanfilter1d32i kfRY; + + float noise_ratio_coordinates_; + float noise_ratio_rect_size_; + float delta_t_; +}; + +}; // namespace vas + +#endif // VAS_OT_KALMAN_FILTER_NO_OPENCV_HPP diff --git a/modules/gapi/src/3rdparty/vasot/src/components/ot/mtt/hungarian_wrap.cpp b/modules/gapi/src/3rdparty/vasot/src/components/ot/mtt/hungarian_wrap.cpp new file mode 100644 index 0000000000..ee6273e4f9 --- /dev/null +++ b/modules/gapi/src/3rdparty/vasot/src/components/ot/mtt/hungarian_wrap.cpp @@ -0,0 +1,325 @@ +/******************************************************************************* + * Copyright (C) 2023 Intel Corporation + * + * SPDX-License-Identifier: MIT + ******************************************************************************/ + +#include "hungarian_wrap.hpp" +#include "../../../common/exception.hpp" + +#include + +#include +#include + +const float kHungarianValueScale = 1024.0f; +namespace vas { +namespace ot { + +HungarianAlgo::HungarianAlgo(const cv::Mat_ &cost_map) + : size_width_(cost_map.cols), size_height_(cost_map.rows), int_cost_map_rows_(), int_cost_map_(), problem_() { + // Convert float 2D cost matrix into int32_t** 2D array with scaling + int_cost_map_rows_.resize(size_height_, nullptr); + int_cost_map_.create(size_height_, size_width_); + for (int32_t r = 0; r < size_height_; ++r) { + int_cost_map_rows_[r] = reinterpret_cast(int_cost_map_.ptr(r)); + + for (int32_t c = 0; c < size_width_; ++c) + int_cost_map_(r, c) = static_cast(cost_map(r, c) * kHungarianValueScale); + } +} + +HungarianAlgo::~HungarianAlgo() { + FreeHungarian(); +} + +cv::Mat_ HungarianAlgo::Solve() { + ETHROW(size_height_ > 0 && size_width_ > 0, invalid_argument, "Initialized with invalid cost_map size in Solve"); + + // Initialize the gungarian_problem using the cost matrix + cv::Mat_ assignment_map; + int32_t matrix_size = InitHungarian(kHungarianModeMinimizeCost); + + // Solve the assignement problem + SolveHungarian(); + + // Copy assignment map + assignment_map.create(matrix_size, matrix_size); + for (int32_t r = 0; r < matrix_size; ++r) { + for (int32_t c = 0; c < matrix_size; ++c) { + (assignment_map)(r, c) = static_cast(problem_.assignment[r][c]); + } + } + + // Free used memory + FreeHungarian(); + + return assignment_map; +} + +// Returns the row size of the assignment matrix +int32_t HungarianAlgo::InitHungarian(int32_t mode) { + int32_t max_cost = 0; + int32_t **cost_matrix = &int_cost_map_rows_[0]; + + // Is the number of cols not equal to number of size_height_ : if yes, expand with 0-size_width_ / 0-size_width_ + ETHROW(size_height_ > 0 && size_width_ > 0, invalid_argument, + "Initialized with invalid cost_map size in InitHungarian"); + problem_.num_rows = (size_width_ < size_height_) ? size_height_ : size_width_; + problem_.num_cols = problem_.num_rows; + + problem_.cost.resize(problem_.num_rows); + problem_.assignment.resize(problem_.num_rows); + + for (int32_t i = 0; i < problem_.num_rows; ++i) { + problem_.cost[i].resize(problem_.num_cols, 0); + problem_.assignment[i].resize(problem_.num_cols, 0); + } + + for (int32_t i = 0; i < problem_.num_rows; ++i) { + for (int32_t j = 0; j < problem_.num_cols; ++j) { + problem_.cost[i][j] = (i < size_height_ && j < size_width_) ? cost_matrix[i][j] : 0; + problem_.assignment[i][j] = 0; + + if (max_cost < problem_.cost[i][j]) + max_cost = problem_.cost[i][j]; + } + } + + if (mode == kHungarianModeMaximizeUtil) { + for (int32_t i = 0; i < problem_.num_rows; ++i) { + for (int32_t j = 0; j < problem_.num_cols; ++j) { + problem_.cost[i][j] = max_cost - problem_.cost[i][j]; + } + } + } else if (mode == kHungarianModeMinimizeCost) { + // Nothing to do + } else { + TRACE(" Unknown mode. Mode was set to HUNGARIAN_MODE_MINIMIZE_COST"); + } + + return problem_.num_rows; +} + +// +// +void HungarianAlgo::FreeHungarian() { +} + +void HungarianAlgo::SolveHungarian() { + int32_t k = 0; + int32_t l = 0; + int32_t unmatched = 0; + + ETHROW(problem_.cost.size() != 0 && problem_.assignment.size() != 0, logic_error, "Unexpected solve"); + + std::unique_ptr vert_col(new int32_t[problem_.num_rows]); + std::unique_ptr row_unselected(new int32_t[problem_.num_rows]); + std::unique_ptr row_dec(new int32_t[problem_.num_rows]); + std::unique_ptr row_slack(new int32_t[problem_.num_rows]); + + std::unique_ptr vert_row(new int32_t[problem_.num_cols]); + std::unique_ptr parent_row(new int32_t[problem_.num_cols]); + std::unique_ptr col_inc(new int32_t[problem_.num_cols]); + std::unique_ptr slack(new int32_t[problem_.num_cols]); + + for (int32_t i = 0; i < problem_.num_rows; ++i) { + vert_col[i] = 0; + row_unselected[i] = 0; + row_dec[i] = 0; + row_slack[i] = 0; + } + + for (int32_t i = 0; i < problem_.num_cols; ++i) { + vert_row[i] = 0; + parent_row[i] = 0; + col_inc[i] = 0; + slack[i] = 0; + } + + for (int32_t i = 0; i < problem_.num_rows; ++i) + for (int32_t j = 0; j < problem_.num_cols; ++j) + problem_.assignment[i][j] = kHungarianNotAssigned; + + // Begin subtract column minima in order to start with lots of zeroes 12 + TRACE(" Using heuristic"); + + for (int32_t i = 0; i < problem_.num_cols; ++i) { + int32_t s = problem_.cost[0][i]; + for (int32_t j = 1; j < problem_.num_rows; ++j) { + if (problem_.cost[j][i] < s) + s = problem_.cost[j][i]; + } + + if (s != 0) { + for (int32_t j = 0; j < problem_.num_rows; ++j) + problem_.cost[j][i] -= s; + } + } + // End subtract column minima in order to start with lots of zeroes 12 + + // Begin initial state 16 + int32_t t = 0; + for (int32_t i = 0; i < problem_.num_cols; ++i) { + vert_row[i] = -1; + parent_row[i] = -1; + col_inc[i] = 0; + slack[i] = kIntMax; + } + + for (k = 0; k < problem_.num_rows; ++k) { + bool is_row_done = false; + int32_t s = problem_.cost[k][0]; + for (l = 1; l < problem_.num_cols; ++l) { + if (problem_.cost[k][l] < s) + s = problem_.cost[k][l]; + } + row_dec[k] = s; + + for (l = 0; l < problem_.num_cols; ++l) { + if (s == problem_.cost[k][l] && vert_row[l] < 0) { + vert_col[k] = l; + vert_row[l] = k; + TRACE(" Matching col (%d)==row (%d)", l, k); + + is_row_done = true; + break; + } + } + + if (is_row_done == true) { + continue; + } else { + vert_col[k] = -1; + TRACE(" Node %d: unmatched row %d", t, k); + row_unselected[t++] = k; + } + } + // End initial state 16 + + // Begin Hungarian algorithm 18 + if (t == 0) + goto done; + + unmatched = t; + while (1) { + TRACE("Matched %d rows.", problem_.num_rows - t); + int32_t q = 0; + while (1) { + while (q < t) { + // Begin explore node q of the forest 19 + k = row_unselected[q]; + int32_t s = row_dec[k]; + for (l = 0; l < problem_.num_cols; ++l) { + if (slack[l] == 0) + continue; + + int32_t del = problem_.cost[k][l] - s + col_inc[l]; + if (del >= slack[l]) + continue; + + if (del == 0) { + if (vert_row[l] < 0) + goto leave_break_thru; + slack[l] = 0; + parent_row[l] = k; + TRACE("node %d: row %d==col %d--row %d", t, vert_row[l], l, k); + + row_unselected[t++] = vert_row[l]; + } else { + slack[l] = del; + row_slack[l] = k; + } + } + // End explore node q of the forest 19 + + q++; + } + + // Begin introduce a new zero into the matrix 21 + int32_t s = kIntMax; + for (int32_t i = 0; i < problem_.num_cols; ++i) { + if (slack[i] && slack[i] < s) + s = slack[i]; + } + + for (q = 0; q < t; ++q) { + row_dec[row_unselected[q]] += s; + } + + for (l = 0; l < problem_.num_cols; ++l) { + if (slack[l]) { + slack[l] -= s; + if (slack[l] == 0) { + // Begin look at a new zero 22 + k = row_slack[l]; + TRACE("Decreasing uncovered elements by %d produces zero at [%d,%d]", s, k, l); + if (vert_row[l] < 0) { + for (int32_t j = l + 1; j < problem_.num_cols; ++j) { + if (slack[j] == 0) + col_inc[j] += s; + } + + goto leave_break_thru; + } else { + parent_row[l] = k; + TRACE("node %d: row %d==col %d--row %d", t, vert_row[l], l, k); + row_unselected[t++] = vert_row[l]; + } + // End look at a new zero 22 + } + } else { + col_inc[l] += s; + } + } + // End introduce a new zero into the matrix 21 + } // while (1) + + leave_break_thru: + TRACE("Breakthrough at node %d of %d!", q, t); + while (1) { + int32_t j = vert_col[k]; + vert_col[k] = l; + vert_row[l] = k; + TRACE("rematching col %d==row %d", l, k); + if (j < 0) + break; + + k = parent_row[j]; + l = j; + } + + // End update the matching 20 + if (--unmatched == 0) + goto done; + + // Begin get ready for another stage 17 + t = 0; + for (int32_t i = 0; i < problem_.num_cols; ++i) { + parent_row[i] = -1; + slack[i] = kIntMax; + } + + for (int32_t i = 0; i < problem_.num_rows; ++i) { + if (vert_col[i] < 0) { + TRACE(" Node %d: unmatched row %d", t, i); + row_unselected[t++] = i; + } + } + // End get ready for another stage 17 + } + +done: + for (int32_t i = 0; i < problem_.num_rows; ++i) { + problem_.assignment[i][vert_col[i]] = kHungarianAssigned; + } + + for (int32_t i = 0; i < problem_.num_rows; ++i) { + for (int32_t j = 0; j < problem_.num_cols; ++j) { + problem_.cost[i][j] = problem_.cost[i][j] - row_dec[i] + col_inc[j]; + } + } +} + +}; // namespace ot +}; // namespace vas diff --git a/modules/gapi/src/3rdparty/vasot/src/components/ot/mtt/hungarian_wrap.hpp b/modules/gapi/src/3rdparty/vasot/src/components/ot/mtt/hungarian_wrap.hpp new file mode 100644 index 0000000000..80a9dddaff --- /dev/null +++ b/modules/gapi/src/3rdparty/vasot/src/components/ot/mtt/hungarian_wrap.hpp @@ -0,0 +1,71 @@ +/******************************************************************************* + * Copyright (C) 2023 Intel Corporation + * + * SPDX-License-Identifier: MIT + ******************************************************************************/ + +#ifndef VAS_OT_HUNGARIAN_WRAP_HPP +#define VAS_OT_HUNGARIAN_WRAP_HPP + +#include + +#include +#include + +namespace vas { +namespace ot { + +const int32_t kHungarianModeMinimizeCost = 0; +const int32_t kHungarianModeMaximizeUtil = 1; + +typedef struct { + int32_t num_rows; + int32_t num_cols; + + std::vector> cost; + std::vector> assignment; +} hungarian_problem_t; + +class HungarianAlgo { + public: + explicit HungarianAlgo(const cv::Mat_ &cost_map); + ~HungarianAlgo(); + + cv::Mat_ Solve(); + + HungarianAlgo() = delete; + HungarianAlgo(const HungarianAlgo &) = delete; + HungarianAlgo(HungarianAlgo &&) = delete; + HungarianAlgo &operator=(const HungarianAlgo &) = delete; + HungarianAlgo &operator=(HungarianAlgo &&) = delete; + + protected: + /* This method initializes the hungarian_problem structure and the cost matrices (missing lines or columns are + *filled with 0). It returns the size of the quadratic(!) assignment matrix. + **/ + int32_t InitHungarian(int32_t mode); + + // Computes the optimal assignment + void SolveHungarian(); + + // Free the memory allocated by Init + void FreeHungarian(); + + int32_t size_width_; + int32_t size_height_; + + private: + const int32_t kHungarianNotAssigned = 0; + const int32_t kHungarianAssigned = 1; + const int32_t kIntMax = INT_MAX; + + std::vector int_cost_map_rows_; + cv::Mat_ int_cost_map_; + + hungarian_problem_t problem_; +}; + +}; // namespace ot +}; // namespace vas + +#endif // VAS_OT_HUNGARIAN_WRAP_HPP diff --git a/modules/gapi/src/3rdparty/vasot/src/components/ot/mtt/objects_associator.cpp b/modules/gapi/src/3rdparty/vasot/src/components/ot/mtt/objects_associator.cpp new file mode 100644 index 0000000000..f2ad032dcb --- /dev/null +++ b/modules/gapi/src/3rdparty/vasot/src/components/ot/mtt/objects_associator.cpp @@ -0,0 +1,183 @@ +/******************************************************************************* + * Copyright (C) 2023 Intel Corporation + * + * SPDX-License-Identifier: MIT + ******************************************************************************/ + +#include "objects_associator.hpp" +#include "hungarian_wrap.hpp" +#include "rgb_histogram.hpp" +#include "../prof_def.hpp" +#include "../../../common/exception.hpp" + +namespace vas { +namespace ot { + +const float kAssociationCostThreshold = 1.0f; +const float kRgbHistDistScale = 0.25f; +const float kNormCenterDistScale = 0.5f; +const float kNormShapeDistScale = 0.75f; + +ObjectsAssociator::ObjectsAssociator(bool tracking_per_class) : tracking_per_class_(tracking_per_class) { +} + +ObjectsAssociator::~ObjectsAssociator() { +} + +std::pair, std::vector> +ObjectsAssociator::Associate(const std::vector &detections, + const std::vector> &tracklets, + const std::vector *detection_rgb_features) { + PROF_START(PROF_COMPONENTS_OT_ASSOCIATE_COMPUTE_DIST_TABLE); + std::vector> d2t_rgb_dist_table; + + if (detection_rgb_features != nullptr) { + d2t_rgb_dist_table = ComputeRgbDistance(detections, tracklets, detection_rgb_features); + } + + auto n_detections = detections.size(); + auto n_tracklets = tracklets.size(); + + std::vector d_is_associated(n_detections, false); + std::vector t_associated_d_index(n_tracklets, -1); + + // Compute detection-tracklet normalized position distance table + std::vector> d2t_pos_dist_table(n_detections, std::vector(n_tracklets, 1000.0f)); + for (std::size_t d = 0; d < n_detections; ++d) { + TRACE("input detect(%.0f,%.0f %.0fx%.0f)", detections[d].rect.x, detections[d].rect.y, detections[d].rect.width, + detections[d].rect.height); + for (std::size_t t = 0; t < n_tracklets; ++t) { + if (tracking_per_class_ && (detections[d].class_label != tracklets[t]->label)) + continue; + + d2t_pos_dist_table[d][t] = NormalizedCenterDistance(detections[d].rect, tracklets[t]->trajectory.back()); + } + } + + // Compute detection-tracklet normalized shape distance table + std::vector> d2t_shape_dist_table(n_detections, std::vector(n_tracklets, 1000.0f)); + for (std::size_t d = 0; d < n_detections; ++d) { + for (std::size_t t = 0; t < n_tracklets; ++t) { + if (tracking_per_class_ && (detections[d].class_label != tracklets[t]->label)) + continue; + + d2t_shape_dist_table[d][t] = NormalizedShapeDistance(detections[d].rect, tracklets[t]->trajectory.back()); + } + } + PROF_END(PROF_COMPONENTS_OT_ASSOCIATE_COMPUTE_DIST_TABLE); + + PROF_START(PROF_COMPONENTS_OT_ASSOCIATE_COMPUTE_COST_TABLE); + // Compute detection-tracklet association cost table + cv::Mat_ d2t_cost_table; + d2t_cost_table.create(static_cast(detections.size()), + static_cast(tracklets.size() + detections.size())); + d2t_cost_table = kAssociationCostThreshold + 1.0f; + + for (std::size_t t = 0; t < n_tracklets; ++t) { + const auto &tracklet = tracklets[t]; + float rgb_hist_dist_scale = kRgbHistDistScale; + + float const_ratio = 0.95f; + float norm_center_dist_scale = + (1.0f - const_ratio) * kNormCenterDistScale * tracklet->association_delta_t / 0.033f + + const_ratio * kNormCenterDistScale; // adaptive to delta_t + float norm_shape_dist_scale = + (1.0f - const_ratio) * kNormShapeDistScale * tracklet->association_delta_t / 0.033f + + const_ratio * kNormShapeDistScale; // adaptive to delta_t + float log_term = logf(rgb_hist_dist_scale * norm_center_dist_scale * norm_shape_dist_scale); + + for (std::size_t d = 0; d < n_detections; ++d) { + if (tracking_per_class_ && (detections[d].class_label != tracklets[t]->label)) + continue; + + d2t_cost_table(static_cast(d), static_cast(t)) = + log_term + d2t_pos_dist_table[d][t] / norm_center_dist_scale + + d2t_shape_dist_table[d][t] / norm_shape_dist_scale; + + if (d2t_rgb_dist_table.empty() == false) { + d2t_cost_table(static_cast(d), static_cast(t)) += + d2t_rgb_dist_table[d][t] / kRgbHistDistScale; + } + } + } + + for (std::size_t d = 0; d < n_detections; ++d) { + d2t_cost_table(static_cast(d), static_cast(d + n_tracklets)) = + kAssociationCostThreshold; + } + PROF_END(PROF_COMPONENTS_OT_ASSOCIATE_COMPUTE_COST_TABLE); + + // Solve detection-tracking association using Hungarian algorithm + PROF_START(PROF_COMPONENTS_OT_ASSOCIATE_WITH_HUNGARIAN); + HungarianAlgo hungarian(d2t_cost_table); + cv::Mat_ d2t_assign_table = hungarian.Solve(); + PROF_END(PROF_COMPONENTS_OT_ASSOCIATE_WITH_HUNGARIAN); + + for (std::size_t d = 0; d < n_detections; ++d) { + for (std::size_t t = 0; t < n_tracklets; ++t) { + if (d2t_assign_table(static_cast(d), static_cast(t))) { + d_is_associated[d] = true; + t_associated_d_index[t] = static_cast(d); + break; + } + } + } + + return std::make_pair(d_is_associated, t_associated_d_index); +} + +std::vector> +ObjectsAssociator::ComputeRgbDistance(const std::vector &detections, + const std::vector> &tracklets, + const std::vector *detection_rgb_features) { + auto n_detections = detections.size(); + auto n_tracklets = tracklets.size(); + + // Compute detection-tracklet RGB feature distance table + std::vector> d2t_rgb_dist_table(n_detections, std::vector(n_tracklets, 1000.0f)); + for (std::size_t d = 0; d < n_detections; ++d) { + const auto &d_rgb_feature = (*detection_rgb_features)[d]; + for (std::size_t t = 0; t < n_tracklets; ++t) { + if (tracking_per_class_ && (detections[d].class_label != tracklets[t]->label)) + continue; + + // Find best match in rgb feature history + float min_dist = 1000.0f; + for (const auto &t_rgb_feature : *(tracklets[t]->GetRgbFeatures())) { + min_dist = std::min(min_dist, 1.0f - RgbHistogram::ComputeSimilarity(d_rgb_feature, t_rgb_feature)); + } + d2t_rgb_dist_table[d][t] = min_dist; + } + } + + return d2t_rgb_dist_table; +} + +float ObjectsAssociator::NormalizedCenterDistance(const cv::Rect2f &r1, const cv::Rect2f &r2) { + float normalizer = std::min(0.5f * (r1.width + r1.height), 0.5f * (r2.width + r2.height)); + + float r1x = r1.x + 0.5f * r1.width; + float r1y = r1.y + 0.5f * r1.height; + float r2x = r2.x + 0.5f * r2.width; + float r2y = r2.y + 0.5f * r2.height; + float dx = (r2x - r1x) / normalizer; + float dy = (r2y - r1y) / normalizer; + return std::sqrt(dx * dx + dy * dy); +} + +float ObjectsAssociator::NormalizedShapeDistance(const cv::Rect2f &r1, const cv::Rect2f &r2) { + int32_t normalize_w = int32_t(r1.width); + int32_t normalize_h = int32_t(r1.height); + + if (r2.width + r2.height < r1.width + r1.height) { + normalize_w = int32_t(r2.width); + normalize_h = int32_t(r2.height); + } + + float dw = (r2.width - r1.width) / normalize_w; + float dh = (r2.height - r1.height) / normalize_h; + return std::sqrt(dw * dw + dh * dh); +} + +}; // namespace ot +}; // namespace vas diff --git a/modules/gapi/src/3rdparty/vasot/src/components/ot/mtt/objects_associator.hpp b/modules/gapi/src/3rdparty/vasot/src/components/ot/mtt/objects_associator.hpp new file mode 100644 index 0000000000..ebdb573ffb --- /dev/null +++ b/modules/gapi/src/3rdparty/vasot/src/components/ot/mtt/objects_associator.hpp @@ -0,0 +1,41 @@ +/******************************************************************************* + * Copyright (C) 2023 Intel Corporation + * + * SPDX-License-Identifier: MIT + ******************************************************************************/ + +#ifndef VAS_OT_OBJECTS_ASSOCIATOR_HPP +#define VAS_OT_OBJECTS_ASSOCIATOR_HPP + +#include "../tracklet.hpp" + +namespace vas { +namespace ot { + +class ObjectsAssociator { + public: + explicit ObjectsAssociator(bool tracking_per_class); + virtual ~ObjectsAssociator(); + ObjectsAssociator() = delete; + + public: + std::pair, std::vector> + Associate(const std::vector &detections, const std::vector> &tracklets, + const std::vector *detection_rgb_features = nullptr); + + private: + std::vector> ComputeRgbDistance(const std::vector &detections, + const std::vector> &tracklets, + const std::vector *detection_rgb_features); + + static float NormalizedCenterDistance(const cv::Rect2f &r1, const cv::Rect2f &r2); + static float NormalizedShapeDistance(const cv::Rect2f &r1, const cv::Rect2f &r2); + + private: + bool tracking_per_class_; +}; + +}; // namespace ot +}; // namespace vas + +#endif // VAS_OT_OBJECTS_ASSOCIATOR_HPP diff --git a/modules/gapi/src/3rdparty/vasot/src/components/ot/mtt/rgb_histogram.cpp b/modules/gapi/src/3rdparty/vasot/src/components/ot/mtt/rgb_histogram.cpp new file mode 100644 index 0000000000..bd121fbf49 --- /dev/null +++ b/modules/gapi/src/3rdparty/vasot/src/components/ot/mtt/rgb_histogram.cpp @@ -0,0 +1,126 @@ +/******************************************************************************* + * Copyright (C) 2023 Intel Corporation + * + * SPDX-License-Identifier: MIT + ******************************************************************************/ + +#include "rgb_histogram.hpp" + +namespace vas { +namespace ot { + +RgbHistogram::RgbHistogram(int32_t rgb_bin_size) + : rgb_bin_size_(rgb_bin_size), rgb_num_bins_(256 / rgb_bin_size), + rgb_hist_size_(static_cast(pow(rgb_num_bins_, 3))) { +} + +RgbHistogram::~RgbHistogram(void) { +} + +void RgbHistogram::Compute(const cv::Mat &image, cv::Mat *hist) { + // Init output buffer + hist->create(1, rgb_hist_size_, CV_32FC1); + (*hist) = cv::Scalar(0); + float *hist_data = hist->ptr(); + + // Compute quantized RGB histogram + AccumulateRgbHistogram(image, hist_data); +} + +void RgbHistogram::ComputeFromBgra32(const cv::Mat &image, cv::Mat *hist) { + // Init output buffer + hist->create(1, rgb_hist_size_, CV_32FC1); + (*hist) = cv::Scalar(0); + float *hist_data = hist->ptr(); + + // Compute quantized RGB histogram + AccumulateRgbHistogramFromBgra32(image, hist_data); +} + +int32_t RgbHistogram::FeatureSize(void) const { + return rgb_hist_size_; +} + +float RgbHistogram::ComputeSimilarity(const cv::Mat &hist1, const cv::Mat &hist2) { + // PROF_START(PROF_COMPONENTS_OT_SHORTTERM_HIST_SIMILARITY); + // Bhattacharyya coeff (w/o weights) + const float eps = 0.0001f; + const int32_t hist_size = hist1.cols; + const float *hist_data1 = hist1.ptr(); + const float *hist_data2 = hist2.ptr(); + float corr = 0.0f; + float sum1 = 0.0f; + float sum2 = 0.0f; + for (int32_t i = 0; i < hist_size; ++i) { + float v1 = hist_data1[i]; + float v2 = hist_data2[i]; + corr += sqrtf(v1 * v2); + sum1 += v1; + sum2 += v2; + } + + // PROF_END(PROF_COMPONENTS_OT_SHORTTERM_HIST_SIMILARITY); + if (sum1 > eps && sum2 > eps) { + return corr / sqrtf(sum1 * sum2); + } else { + return 0.0f; + } +} + +void RgbHistogram::AccumulateRgbHistogram(const cv::Mat &patch, float *rgb_hist) const { + for (int32_t y = 0; y < patch.rows; ++y) { + const cv::Vec3b *patch_ptr = patch.ptr(y); + for (int32_t x = 0; x < patch.cols; ++x) { + int32_t index0 = patch_ptr[x][0] / rgb_bin_size_; + int32_t index1 = patch_ptr[x][1] / rgb_bin_size_; + int32_t index2 = patch_ptr[x][2] / rgb_bin_size_; + int32_t hist_index = rgb_num_bins_ * (rgb_num_bins_ * index0 + index1) + index2; + rgb_hist[hist_index] += 1.0f; + } + } +} + +void RgbHistogram::AccumulateRgbHistogram(const cv::Mat &patch, const cv::Mat &weight, float *rgb_hist) const { + for (int32_t y = 0; y < patch.rows; ++y) { + const cv::Vec3b *patch_ptr = patch.ptr(y); + const float *weight_ptr = weight.ptr(y); + for (int32_t x = 0; x < patch.cols; ++x) { + int32_t index0 = patch_ptr[x][0] / rgb_bin_size_; + int32_t index1 = patch_ptr[x][1] / rgb_bin_size_; + int32_t index2 = patch_ptr[x][2] / rgb_bin_size_; + int32_t hist_index = rgb_num_bins_ * (rgb_num_bins_ * index0 + index1) + index2; + rgb_hist[hist_index] += weight_ptr[x]; + } + } +} + +void RgbHistogram::AccumulateRgbHistogramFromBgra32(const cv::Mat &patch, float *rgb_hist) const { + for (int32_t y = 0; y < patch.rows; ++y) { + const cv::Vec4b *patch_ptr = patch.ptr(y); + for (int32_t x = 0; x < patch.cols; ++x) { + int32_t index0 = patch_ptr[x][0] / rgb_bin_size_; + int32_t index1 = patch_ptr[x][1] / rgb_bin_size_; + int32_t index2 = patch_ptr[x][2] / rgb_bin_size_; + int32_t hist_index = rgb_num_bins_ * (rgb_num_bins_ * index0 + index1) + index2; + rgb_hist[hist_index] += 1.0f; + } + } +} + +void RgbHistogram::AccumulateRgbHistogramFromBgra32(const cv::Mat &patch, const cv::Mat &weight, + float *rgb_hist) const { + for (int32_t y = 0; y < patch.rows; ++y) { + const cv::Vec4b *patch_ptr = patch.ptr(y); + const float *weight_ptr = weight.ptr(y); + for (int32_t x = 0; x < patch.cols; ++x) { + int32_t index0 = patch_ptr[x][0] / rgb_bin_size_; + int32_t index1 = patch_ptr[x][1] / rgb_bin_size_; + int32_t index2 = patch_ptr[x][2] / rgb_bin_size_; + int32_t hist_index = rgb_num_bins_ * (rgb_num_bins_ * index0 + index1) + index2; + rgb_hist[hist_index] += weight_ptr[x]; + } + } +} + +}; // namespace ot +}; // namespace vas diff --git a/modules/gapi/src/3rdparty/vasot/src/components/ot/mtt/rgb_histogram.hpp b/modules/gapi/src/3rdparty/vasot/src/components/ot/mtt/rgb_histogram.hpp new file mode 100644 index 0000000000..9a7847509b --- /dev/null +++ b/modules/gapi/src/3rdparty/vasot/src/components/ot/mtt/rgb_histogram.hpp @@ -0,0 +1,42 @@ +/******************************************************************************* + * Copyright (C) 2023 Intel Corporation + * + * SPDX-License-Identifier: MIT + ******************************************************************************/ + +#ifndef VAS_OT_RGB_HISTOGRAM_HPP +#define VAS_OT_RGB_HISTOGRAM_HPP + +#include +#include + +namespace vas { +namespace ot { + +class RgbHistogram { + public: + explicit RgbHistogram(int32_t rgb_bin_size); + virtual ~RgbHistogram(void); + + virtual void Compute(const cv::Mat &image, cv::Mat *hist); + virtual void ComputeFromBgra32(const cv::Mat &image, cv::Mat *hist); + virtual int32_t FeatureSize(void) const; // currently 512 * float32 + + static float ComputeSimilarity(const cv::Mat &hist1, const cv::Mat &hist2); + + protected: + int32_t rgb_bin_size_; + int32_t rgb_num_bins_; + int32_t rgb_hist_size_; + + void AccumulateRgbHistogram(const cv::Mat &patch, float *rgb_hist) const; + void AccumulateRgbHistogram(const cv::Mat &patch, const cv::Mat &weight, float *rgb_hist) const; + + void AccumulateRgbHistogramFromBgra32(const cv::Mat &patch, float *rgb_hist) const; + void AccumulateRgbHistogramFromBgra32(const cv::Mat &patch, const cv::Mat &weight, float *rgb_hist) const; +}; + +}; // namespace ot +}; // namespace vas + +#endif // VAS_OT_RGB_HISTOGRAM_HPP diff --git a/modules/gapi/src/3rdparty/vasot/src/components/ot/object_tracker.cpp b/modules/gapi/src/3rdparty/vasot/src/components/ot/object_tracker.cpp new file mode 100644 index 0000000000..6e55ce5ddd --- /dev/null +++ b/modules/gapi/src/3rdparty/vasot/src/components/ot/object_tracker.cpp @@ -0,0 +1,363 @@ +/******************************************************************************* + * Copyright (C) 2023 Intel Corporation + * + * SPDX-License-Identifier: MIT + ******************************************************************************/ + +#include "prof_def.hpp" +#include "tracker.hpp" +#include "../../common/exception.hpp" + +#include +#include + +#include + +namespace vas { +namespace ot { +const float kDefaultDeltaTime = 0.033f; +const int kDefaultNumThreads = 1; +const char kNameMaxNumThreads[] = "max_num_threads"; + +vas::Version GetVersion() noexcept { + vas::Version version(OT_VERSION_MAJOR, OT_VERSION_MINOR, OT_VERSION_PATCH); + return version; +} + +std::ostream &operator<<(std::ostream &os, TrackingStatus ts) { + if (ts == TrackingStatus::NEW) + os << "NEW"; + else if (ts == TrackingStatus::TRACKED) + os << "TRACKED"; + // else if (ts == TrackingStatus::LOST) + else + os << "LOST"; + + return os; +} + +std::ostream &operator<<(std::ostream &os, const Object &object) { + os << "Object:" << std::endl; + os << " rect -> " << object.rect << std::endl; + os << " tracking id -> " << object.tracking_id << std::endl; + os << " class label -> " << object.class_label << std::endl; + os << " tracking status -> " << object.status; + + return os; +} + +// Internal implementation: includes OT component +class ObjectTracker::Impl { + public: + class InitParameters : public vas::ot::Tracker::InitParameters { + public: + TrackingType tracking_type; + vas::BackendType backend_type; + }; + + public: + explicit Impl(const InitParameters ¶m); + + Impl() = delete; + ~Impl(); + Impl(const Impl &) = delete; + Impl(Impl &&) = delete; + Impl &operator=(const Impl &) = delete; + Impl &operator=(Impl &&) = delete; + + public: + int32_t GetMaxNumObjects() const noexcept; + TrackingType GetTrackingType() const noexcept; + vas::ColorFormat GetInputColorFormat() const noexcept; + float GetDeltaTime() const noexcept; + vas::BackendType GetBackendType() const noexcept; + bool GetTrackingPerClass() const noexcept; + void SetDeltaTime(float delta_t); + std::vector Track(const cv::Mat &frame, const std::vector &objects); + + private: + std::unique_ptr tracker_; + std::vector> produced_tracklets_; + + int32_t max_num_objects_; + float delta_t_; + TrackingType tracking_type_; + vas::BackendType backend_type_; + vas::ColorFormat input_color_format_; + bool tracking_per_class_; +#ifdef DUMP_OTAV + Otav otav_; +#endif + + friend class ObjectTracker::Builder; +}; + +namespace { +void vas_exit() { +} +} // anonymous namespace + +ObjectTracker::ObjectTracker(ObjectTracker::Impl *impl) : impl_(impl) { + atexit(vas_exit); +} + +ObjectTracker::~ObjectTracker() = default; + +int32_t ObjectTracker::GetMaxNumObjects() const noexcept { + return impl_->GetMaxNumObjects(); +} + +TrackingType ObjectTracker::GetTrackingType() const noexcept { + return impl_->GetTrackingType(); +} + +vas::ColorFormat ObjectTracker::GetInputColorFormat() const noexcept { + return impl_->GetInputColorFormat(); +} + +float ObjectTracker::GetFrameDeltaTime() const noexcept { + return impl_->GetDeltaTime(); +} + +vas::BackendType ObjectTracker::GetBackendType() const noexcept { + return impl_->GetBackendType(); +} + +bool ObjectTracker::GetTrackingPerClass() const noexcept { + return impl_->GetTrackingPerClass(); +} + +void ObjectTracker::SetFrameDeltaTime(float frame_delta_t) { + impl_->SetDeltaTime(frame_delta_t); +} + +std::vector ObjectTracker::Track(const cv::Mat &frame, const std::vector &objects) { + return impl_->Track(frame, objects); +} + +ObjectTracker::Impl::Impl(const InitParameters ¶m) + : max_num_objects_(param.max_num_objects), delta_t_(kDefaultDeltaTime), tracking_type_(param.tracking_type), + backend_type_(param.backend_type), input_color_format_(param.format), + tracking_per_class_(param.tracking_per_class) { + PROF_INIT(OT); + TRACE("BEGIN"); + if ((param.max_num_objects) != -1 && (param.max_num_objects <= 0)) { + std::cout << "Error: Invalid maximum number of objects: " << param.max_num_objects << std::endl; + ETHROW(false, invalid_argument, "Invalid maximum number of objects"); + } + + TRACE("tracking_type: %d, backend_type: %d, color_format: %d, max_num_object: %d, tracking_per_class: %d", + static_cast(tracking_type_), static_cast(backend_type_), + static_cast(input_color_format_), max_num_objects_, tracking_per_class_); + + if (param.backend_type == vas::BackendType::CPU) { + tracker_.reset(vas::ot::Tracker::CreateInstance(param)); + } else { + std::cout << "Error: Unexpected backend type" << std::endl; + ETHROW(false, invalid_argument, "Unexpected backend type"); + } + + produced_tracklets_.clear(); + + TRACE("END"); +} + +ObjectTracker::Impl::~Impl() { + PROF_FLUSH(OT); +} + +void ObjectTracker::Impl::SetDeltaTime(float delta_t) { + if (delta_t < 0.005f || delta_t > 0.5f) { + std::cout << "Error: Invalid argument for SetFrameDeltaTime " << delta_t << std::endl; + ETHROW(false, invalid_argument, "Invalid argument for SetFrameDeltaTime"); + } + + delta_t_ = delta_t; + return; +} + +int32_t ObjectTracker::Impl::GetMaxNumObjects() const noexcept { + return max_num_objects_; +} + +TrackingType ObjectTracker::Impl::GetTrackingType() const noexcept { + return tracking_type_; +} + +vas::ColorFormat ObjectTracker::Impl::GetInputColorFormat() const noexcept { + return input_color_format_; +} + +float ObjectTracker::Impl::GetDeltaTime() const noexcept { + return delta_t_; +} + +vas::BackendType ObjectTracker::Impl::GetBackendType() const noexcept { + return backend_type_; +} + +bool ObjectTracker::Impl::GetTrackingPerClass() const noexcept { + return tracking_per_class_; +} + +std::vector ObjectTracker::Impl::Track(const cv::Mat &frame, + const std::vector &detected_objects) { + if (frame.cols <= 0 || frame.rows <= 0) { + std::cout << "Error: Invalid frame size(" << frame.cols << "x" << frame.rows << ") empty(" + << frame.empty() << ")" << std::endl; + ETHROW(false, invalid_argument, "Invalid frame size(%dx%d) empty(%d)\n", frame.cols, frame.rows, frame.empty()); + } + int32_t frame_w = frame.cols; + int32_t frmae_h = (input_color_format_ == vas::ColorFormat::NV12) ? frame.rows * 2 / 3 : frame.rows; + cv::Rect frame_rect(0, 0, frame_w, frmae_h); + + TRACE("START"); + PROF_START(PROF_COMPONENTS_OT_RUN_TRACK); + std::vector detections; + + TRACE("+ Number: Detected objects (%d)", static_cast(detected_objects.size())); + int32_t index = 0; + for (const auto &object : detected_objects) { + vas::ot::Detection detection; + + detection.class_label = object.class_label; + detection.rect = static_cast(object.rect); + detection.index = index; + + detections.emplace_back(detection); + index++; + } + + std::vector objects; + if (backend_type_ == vas::BackendType::CPU) { + tracker_->TrackObjects(frame, detections, &produced_tracklets_, delta_t_); + TRACE("+ Number: Tracking objects (%d)", static_cast(produced_tracklets_.size())); + + for (const auto &tracklet : produced_tracklets_) // result 'Tracklet' + { + cv::Rect rect = static_cast(tracklet->trajectory_filtered.back()); + if ((rect & frame_rect).area() > 0) { + Object object; + // TRACE(" - ID(%d) Status(%d)", tracklet.id, tracklet.status); + object.rect = static_cast(tracklet->trajectory_filtered.back()); + object.tracking_id = tracklet->id; + object.class_label = tracklet->label; + object.association_idx = tracklet->association_idx; + object.status = vas::ot::TrackingStatus::LOST; + switch (tracklet->status) { + case ST_NEW: + object.status = vas::ot::TrackingStatus::NEW; + break; + case ST_TRACKED: + object.status = vas::ot::TrackingStatus::TRACKED; + break; + case ST_LOST: + default: + object.status = vas::ot::TrackingStatus::LOST; + } + objects.emplace_back(object); + } else { + TRACE("[ %d, %d, %d, %d ] is out of the image bound! -> Filtered out.", rect.x, rect.y, rect.width, + rect.height); + } + } + } else { + ETHROW(false, invalid_argument, "Unexpected input backend type for VAS-OT.") + } + TRACE("+ Number: Result objects (%d)", static_cast(objects.size())); + + PROF_END(PROF_COMPONENTS_OT_RUN_TRACK); + +#ifdef DUMP_OTAV + otav_.Dump(frame, detections, produced_tracklets_, tracker_->GetFrameCount() - 1); +#endif + + TRACE("END"); + return objects; +} + +ObjectTracker::Builder::Builder() + : backend_type(vas::BackendType::CPU), max_num_objects(kDefaultMaxNumObjects), + input_image_format(vas::ColorFormat::BGR), tracking_per_class(true) { +} + +ObjectTracker::Builder::~Builder() { +} + +std::unique_ptr ObjectTracker::Builder::Build(TrackingType tracking_type) const { + TRACE("BEGIN"); + + ObjectTracker::Impl *ot_impl = nullptr; + ObjectTracker::Impl::InitParameters param; + + param.max_num_objects = max_num_objects; + param.format = input_image_format; + param.backend_type = backend_type; + param.tracking_type = tracking_type; + param.tracking_per_class = tracking_per_class; + + if (static_cast(vas::ColorFormat::BGR) > static_cast(input_image_format) || + static_cast(vas::ColorFormat::I420) < static_cast(input_image_format)) { + ETHROW(false, invalid_argument, "Invalid color format(%d)", static_cast(input_image_format)); + } + + switch (tracking_type) { + case vas::ot::TrackingType::LONG_TERM: + param.profile = vas::ot::Tracker::PROFILE_LONG_TERM; + break; + case vas::ot::TrackingType::SHORT_TERM: + param.profile = vas::ot::Tracker::PROFILE_SHORT_TERM; + break; + case vas::ot::TrackingType::SHORT_TERM_KCFVAR: + param.profile = vas::ot::Tracker::PROFILE_SHORT_TERM_KCFVAR; + break; + case vas::ot::TrackingType::SHORT_TERM_IMAGELESS: + param.profile = vas::ot::Tracker::PROFILE_SHORT_TERM_IMAGELESS; + break; + case vas::ot::TrackingType::ZERO_TERM: + param.profile = vas::ot::Tracker::PROFILE_ZERO_TERM; + break; + case vas::ot::TrackingType::ZERO_TERM_COLOR_HISTOGRAM: + param.profile = vas::ot::Tracker::PROFILE_ZERO_TERM_COLOR_HISTOGRAM; + break; + case vas::ot::TrackingType::ZERO_TERM_IMAGELESS: + param.profile = vas::ot::Tracker::PROFILE_ZERO_TERM_IMAGELESS; + break; + default: + std::cout << "Error: Invalid tracker type vas::ot::Tracker" << std::endl; + ETHROW(false, invalid_argument, "Invalid tracker type vas::ot::Tracker"); + return nullptr; + } + + // Not exposed to external parameter + param.min_region_ratio_in_boundary = + kMinRegionRatioInImageBoundary; // Ratio threshold of size: used by zttchist, zttimgless, sttkcfvar, sttimgless + + for (const auto &item : platform_config) { + (void)item; // resolves ununsed warning when LOG_TRACE is OFF + TRACE("platform_config[%s] = %s", item.first.c_str(), item.second.c_str()); + } + + int max_num_threads = kDefaultNumThreads; + auto max_num_threads_iter = platform_config.find(kNameMaxNumThreads); + if (max_num_threads_iter != platform_config.end()) { + try { + max_num_threads = std::stoi(max_num_threads_iter->second); + } catch (const std::exception &) { + ETHROW(false, invalid_argument, "max_num_threads should be integer"); + } + + if (max_num_threads == 0 || max_num_threads < -1) + ETHROW(false, invalid_argument, "max_num_threads cannot be 0 or smaller than -1"); + } + param.max_num_threads = max_num_threads; + + ot_impl = new ObjectTracker::Impl(param); + std::unique_ptr ot(new ObjectTracker(ot_impl)); + + TRACE("END"); + return ot; +} + +}; // namespace ot +}; // namespace vas diff --git a/modules/gapi/src/3rdparty/vasot/src/components/ot/prof_def.hpp b/modules/gapi/src/3rdparty/vasot/src/components/ot/prof_def.hpp new file mode 100644 index 0000000000..e16f901613 --- /dev/null +++ b/modules/gapi/src/3rdparty/vasot/src/components/ot/prof_def.hpp @@ -0,0 +1,30 @@ +/******************************************************************************* + * Copyright (C) 2023 Intel Corporation + * + * SPDX-License-Identifier: MIT + ******************************************************************************/ + +#ifndef VAS_OT_PROF_DEF_HPP +#define VAS_OT_PROF_DEF_HPP + +#include "../../common/prof.hpp" + +// 0 ~ 999 : Reserved group id for UnitTests. +// 1000 ~ : User Defined id range under modules. + +#define PROF_COMPONENTS_OT_RUN_TRACK PROF_TAG_GENERATE(OT, 1000, " ObjectTracker::Track") + +#define PROF_COMPONENTS_OT_ZEROTERM_RUN_TRACKER PROF_TAG_GENERATE(OT, 1400, " ZeroTermTracker::TrackObjects") +#define PROF_COMPONENTS_OT_ZEROTERM_KALMAN_PREDICTION PROF_TAG_GENERATE(OT, 1411, " ZeroTermTracker::KalmanPrediction") +#define PROF_COMPONENTS_OT_ZEROTERM_RUN_ASSOCIATION PROF_TAG_GENERATE(OT, 1421, " ZeroTermTracker::Association") +#define PROF_COMPONENTS_OT_ZEROTERM_UPDATE_STATUS PROF_TAG_GENERATE(OT, 1441, " ZeroTermTracker::UpdateTrackedStatus") +#define PROF_COMPONENTS_OT_ZEROTERM_COMPUTE_OCCLUSION PROF_TAG_GENERATE(OT, 1461, " ZeroTermTracker::ComputeOcclusion") +#define PROF_COMPONENTS_OT_ZEROTERM_UPDATE_MODEL PROF_TAG_GENERATE(OT, 1481, " ZeroTermTracker::UpdateModel") +#define PROF_COMPONENTS_OT_ZEROTERM_REGISTER_OBJECT PROF_TAG_GENERATE(OT, 1491, " ZeroTermTracker::RegisterObject") + +#define PROF_COMPONENTS_OT_ASSOCIATE_COMPUTE_DIST_TABLE \ + PROF_TAG_GENERATE(OT, 1600, " Association::ComputeDistanceTable") +#define PROF_COMPONENTS_OT_ASSOCIATE_COMPUTE_COST_TABLE PROF_TAG_GENERATE(OT, 1610, " Association::ComputeCostTable") +#define PROF_COMPONENTS_OT_ASSOCIATE_WITH_HUNGARIAN PROF_TAG_GENERATE(OT, 1620, " Association::AssociateWithHungarian") + +#endif // __OT_PROF_DEF_H__ diff --git a/modules/gapi/src/3rdparty/vasot/src/components/ot/short_term_imageless_tracker.cpp b/modules/gapi/src/3rdparty/vasot/src/components/ot/short_term_imageless_tracker.cpp new file mode 100644 index 0000000000..be788bba62 --- /dev/null +++ b/modules/gapi/src/3rdparty/vasot/src/components/ot/short_term_imageless_tracker.cpp @@ -0,0 +1,256 @@ +/******************************************************************************* + * Copyright (C) 2023 Intel Corporation + * + * SPDX-License-Identifier: MIT + ******************************************************************************/ + +#include "short_term_imageless_tracker.hpp" +#include "prof_def.hpp" +#include "../../common/exception.hpp" + +#include + +namespace vas { +namespace ot { + +const int32_t kMaxAssociationLostCount = 2; // ST_TRACKED -> ST_LOST +const int32_t kMaxAssociationFailCount = 20; // ST_LOST -> ST_DEAD +const int32_t kMaxOutdatedCountInTracked = 30; // ST_TRACKED -> ST_LOST +const int32_t kMaxOutdatedCountInLost = 20; // ST_LOST -> ST_DEAD +const int32_t kMaxTrajectorySize = 30; + +/** + * + * ShortTermImagelessTracker + * + **/ +ShortTermImagelessTracker::ShortTermImagelessTracker(vas::ot::Tracker::InitParameters init_param) + : Tracker(init_param.max_num_objects, init_param.min_region_ratio_in_boundary, init_param.format, + init_param.tracking_per_class), + image_sz(0, 0) { + TRACE(" - Created tracker = ShortTermImagelessTracker"); +} + +ShortTermImagelessTracker::~ShortTermImagelessTracker() { +} + +int32_t ShortTermImagelessTracker::TrackObjects(const cv::Mat &mat, const std::vector &detections, + std::vector> *tracklets, float delta_t) { + PROF_START(PROF_COMPONENTS_OT_SHORTTERM_RUN_TRACKER); + + int32_t input_img_width = mat.cols; + int32_t input_img_height = mat.rows; + + if (input_image_format_ == vas::ColorFormat::NV12 || input_image_format_ == vas::ColorFormat::I420) { + input_img_height = mat.rows / 3 * 2; + } + + const cv::Rect2f image_boundary(0.0f, 0.0f, static_cast(input_img_width), + static_cast(input_img_height)); + + TRACE("Start TrackObjects frame_count_: %d, detection: %d, tracklet: %d ----------------", frame_count_, + detections.size(), tracklets_.size()); + bool is_dead = false; + if (image_sz.width != input_img_width || image_sz.height != input_img_height) { + if (image_sz.width != 0 || image_sz.height != 0) { + is_dead = true; + } + image_sz.width = input_img_width; + image_sz.height = input_img_height; + } + + PROF_START(PROF_COMPONENTS_OT_SHORTTERM_KALMAN_PREDICTION); + // Predict tracklets state + for (auto &tracklet : tracklets_) { + auto sttimgless_tracklet = std::dynamic_pointer_cast(tracklet); + cv::Rect2f predicted_rect = sttimgless_tracklet->kalman_filter->Predict(delta_t); + sttimgless_tracklet->predicted = predicted_rect; + sttimgless_tracklet->trajectory.push_back(predicted_rect); + sttimgless_tracklet->trajectory_filtered.push_back(predicted_rect); + sttimgless_tracklet->association_delta_t += delta_t; + // Reset association index every frame for new detection input + sttimgless_tracklet->association_idx = kNoMatchDetection; + } + + PROF_END(PROF_COMPONENTS_OT_SHORTTERM_KALMAN_PREDICTION); + + PROF_START(PROF_COMPONENTS_OT_SHORTTERM_UPDATE_STATUS); + // Conduct tracking of SOT for each tracklet + TRACE(" Update status"); + for (auto &tracklet : tracklets_) { + if (is_dead) { + tracklet->status = ST_DEAD; + continue; + } + + // tracklet->association_delta_t = 0.0f; // meaning updated by SOT + } + + if (is_dead) { + RemoveDeadTracklets(); + } + PROF_END(PROF_COMPONENTS_OT_SHORTTERM_UPDATE_STATUS); + + PROF_START(PROF_COMPONENTS_OT_SHORTTERM_RUN_ASSOCIATION); + + // Tracklet-detection association + int32_t n_detections = static_cast(detections.size()); + int32_t n_tracklets = static_cast(tracklets_.size()); + + std::vector d_is_associated(n_detections, false); + std::vector t_associated_d_index(n_tracklets, -1); + + if (n_detections > 0) { + auto result = associator_.Associate(detections, tracklets_); + d_is_associated = result.first; + t_associated_d_index = result.second; + } + + PROF_END(PROF_COMPONENTS_OT_SHORTTERM_RUN_ASSOCIATION); + + PROF_START(PROF_COMPONENTS_OT_SHORTTERM_UPDATE_STATUS); + // Update tracklets' state + if (n_detections > 0) { + for (int32_t t = 0; t < n_tracklets; ++t) { + auto &tracklet = tracklets_[t]; + if (t_associated_d_index[t] >= 0) { + tracklet->association_delta_t = 0.0f; + int32_t associated_d_index = t_associated_d_index[t]; + const cv::Rect2f &d_bounding_box = detections[associated_d_index].rect & image_boundary; + + // Apply associated detection to tracklet + tracklet->association_idx = detections[associated_d_index].index; + tracklet->association_fail_count = 0; + tracklet->age = 0; + tracklet->label = detections[associated_d_index].class_label; + + auto sttimgless_tracklet = std::dynamic_pointer_cast(tracklet); + if (!sttimgless_tracklet) + continue; + + if (sttimgless_tracklet->status == ST_NEW) { + sttimgless_tracklet->trajectory.back() = d_bounding_box; + sttimgless_tracklet->trajectory_filtered.back() = + sttimgless_tracklet->kalman_filter->Correct(sttimgless_tracklet->trajectory.back()); + sttimgless_tracklet->status = ST_TRACKED; + } else if (sttimgless_tracklet->status == ST_TRACKED) { + sttimgless_tracklet->trajectory.back() = d_bounding_box; + sttimgless_tracklet->trajectory_filtered.back() = + sttimgless_tracklet->kalman_filter->Correct(sttimgless_tracklet->trajectory.back()); + } else if (sttimgless_tracklet->status == ST_LOST) { + sttimgless_tracklet->RenewTrajectory(d_bounding_box); + sttimgless_tracklet->status = ST_TRACKED; + } + } else // Association failure + { + tracklet->association_fail_count++; + if (tracklet->status == ST_NEW) { + tracklet->status = ST_DEAD; // regard non-consecutive association as false alarm + } else if (tracklet->status == ST_TRACKED) { + if (tracklet->association_fail_count > kMaxAssociationLostCount) { + // # association fail > threshold while tracking -> MISSING + tracklet->status = ST_LOST; + tracklet->association_fail_count = 0; + tracklet->age = 0; + } + } else if (tracklet->status == ST_LOST) { + if (tracklet->association_fail_count > kMaxAssociationFailCount) { + // # association fail > threshold while missing -> DEAD + tracklet->status = ST_DEAD; + } + } + } + } + } else // detections.size() == 0 + { + for (int32_t t = 0; t < static_cast(tracklets_.size()); ++t) { + auto &tracklet = tracklets_[t]; + // Always change ST_NEW to ST_TRACKED: no feature tracking from previous detection input. + if (tracklet->status == ST_NEW) { + tracklet->status = ST_TRACKED; + } + + auto sttimgless_tracklet = std::dynamic_pointer_cast(tracklet); + if (!sttimgless_tracklet) + continue; + + if (sttimgless_tracklet->status == ST_TRACKED) { + if (sttimgless_tracklet->age > kMaxOutdatedCountInTracked) { + sttimgless_tracklet->status = ST_LOST; + sttimgless_tracklet->association_fail_count = 0; + sttimgless_tracklet->age = 0; + } else { + sttimgless_tracklet->trajectory_filtered.back() = + sttimgless_tracklet->kalman_filter->Correct(sttimgless_tracklet->trajectory.back()); + } + } + + if (sttimgless_tracklet->status == ST_LOST) { + if (sttimgless_tracklet->age >= kMaxOutdatedCountInLost) { + // # association fail > threshold while missing -> DEAD + sttimgless_tracklet->status = ST_DEAD; + } + } + } + } + PROF_END(PROF_COMPONENTS_OT_SHORTTERM_UPDATE_STATUS); + + PROF_START(PROF_COMPONENTS_OT_SHORTTERM_COMPUTE_OCCLUSION); + ComputeOcclusion(); + PROF_END(PROF_COMPONENTS_OT_SHORTTERM_COMPUTE_OCCLUSION); + + PROF_START(PROF_COMPONENTS_OT_SHORTTERM_REGISTER_OBJECT); + // Register remaining detections as new objects + for (int32_t d = 0; d < static_cast(detections.size()); ++d) { + if (d_is_associated[d] == false) { + if (static_cast(tracklets_.size()) >= max_objects_ && max_objects_ != -1) + continue; + + std::unique_ptr tracklet(new ShortTermImagelessTracklet()); + + tracklet->status = ST_NEW; + tracklet->id = GetNextTrackingID(); + tracklet->label = detections[d].class_label; + tracklet->association_idx = detections[d].index; + + const cv::Rect2f &bounding_box = detections[d].rect & image_boundary; + tracklet->InitTrajectory(bounding_box); + tracklet->kalman_filter.reset(new KalmanFilterNoOpencv(bounding_box)); + tracklets_.push_back(std::move(tracklet)); + } + } + PROF_END(PROF_COMPONENTS_OT_SHORTTERM_REGISTER_OBJECT); + + RemoveDeadTracklets(); + RemoveOutOfBoundTracklets(input_img_width, input_img_height); + TrimTrajectories(); + + *tracklets = tracklets_; + + // Increase age + for (auto &tracklet : tracklets_) { + tracklet->age++; + } + + IncreaseFrameCount(); + PROF_END(PROF_COMPONENTS_OT_SHORTTERM_RUN_TRACKER); + return 0; +} + +void ShortTermImagelessTracker::TrimTrajectories() { + for (auto &tracklet : tracklets_) { + auto &trajectory = tracklet->trajectory; + while (trajectory.size() > kMaxTrajectorySize) { + trajectory.pop_front(); + } + + // + auto &trajectory_filtered = tracklet->trajectory_filtered; + while (trajectory_filtered.size() > kMaxTrajectorySize) { + trajectory_filtered.pop_front(); + } + } +} + +}; // namespace ot +}; // namespace vas diff --git a/modules/gapi/src/3rdparty/vasot/src/components/ot/short_term_imageless_tracker.hpp b/modules/gapi/src/3rdparty/vasot/src/components/ot/short_term_imageless_tracker.hpp new file mode 100644 index 0000000000..ac04936deb --- /dev/null +++ b/modules/gapi/src/3rdparty/vasot/src/components/ot/short_term_imageless_tracker.hpp @@ -0,0 +1,39 @@ +/******************************************************************************* + * Copyright (C) 2023 Intel Corporation + * + * SPDX-License-Identifier: MIT + ******************************************************************************/ + +#ifndef VAS_OT_SHORT_TERM_IMAGELESS_TRACKER_HPP +#define VAS_OT_SHORT_TERM_IMAGELESS_TRACKER_HPP + +#include "tracker.hpp" + +#include +#include + +namespace vas { +namespace ot { + +class ShortTermImagelessTracker : public Tracker { + public: + explicit ShortTermImagelessTracker(vas::ot::Tracker::InitParameters init_param); + virtual ~ShortTermImagelessTracker(); + + virtual int32_t TrackObjects(const cv::Mat &mat, const std::vector &detections, + std::vector> *tracklets, float delta_t) override; + + ShortTermImagelessTracker(const ShortTermImagelessTracker &) = delete; + ShortTermImagelessTracker &operator=(const ShortTermImagelessTracker &) = delete; + + private: + void TrimTrajectories(); + + private: + cv::Size image_sz; +}; + +}; // namespace ot +}; // namespace vas + +#endif // VAS_OT_SHORT_TERM_IMAGELESS_TRACKER_HPP diff --git a/modules/gapi/src/3rdparty/vasot/src/components/ot/tracker.cpp b/modules/gapi/src/3rdparty/vasot/src/components/ot/tracker.cpp new file mode 100644 index 0000000000..cba960d595 --- /dev/null +++ b/modules/gapi/src/3rdparty/vasot/src/components/ot/tracker.cpp @@ -0,0 +1,132 @@ +/******************************************************************************* + * Copyright (C) 2023 Intel Corporation + * + * SPDX-License-Identifier: MIT + ******************************************************************************/ + +#include "short_term_imageless_tracker.hpp" +#include "zero_term_imageless_tracker.hpp" + +#include "../../common/exception.hpp" + +namespace vas { +namespace ot { + +Tracker::Tracker(int32_t max_objects, float min_region_ratio_in_boundary, vas::ColorFormat format, bool class_per_class) + : max_objects_(max_objects), next_id_(1), frame_count_(0), + min_region_ratio_in_boundary_(min_region_ratio_in_boundary), input_image_format_(format), + associator_(ObjectsAssociator(class_per_class)) { +} + +Tracker::~Tracker() { +} + +Tracker *Tracker::CreateInstance(InitParameters init_parameters) { + TRACE("START CreateInstance Tracker"); + + Tracker *tracker = nullptr; + if (init_parameters.profile == PROFILE_SHORT_TERM_IMAGELESS) { + tracker = new ShortTermImagelessTracker(init_parameters); + } else if (init_parameters.profile == PROFILE_ZERO_TERM_IMAGELESS) { + tracker = new ZeroTermImagelessTracker(init_parameters); + } else { + throw std::runtime_error("Unsupported tracking type"); + } + + TRACE(" - max_num_objects(%d)", init_parameters.max_num_objects); + + TRACE("END"); + return tracker; +} + +int32_t Tracker::RemoveObject(const int32_t id) { + if (id == 0) + return -1; + + for (auto tracklet = tracklets_.begin(); tracklet != tracklets_.end(); ++tracklet) { + if ((*tracklet)->id == id) { + tracklet = tracklets_.erase(tracklet); + return 0; + } + } + return -1; +} + +void Tracker::Reset(void) { + frame_count_ = 0; + tracklets_.clear(); +} + +int32_t Tracker::GetFrameCount(void) const { + return frame_count_; +} + +int32_t Tracker::GetNextTrackingID() { + return next_id_++; +} + +void Tracker::IncreaseFrameCount() { + frame_count_++; +} + +void Tracker::ComputeOcclusion() { + // Compute occlusion ratio + for (int32_t t0 = 0; t0 < static_cast(tracklets_.size()); ++t0) { + auto &tracklet0 = tracklets_[t0]; + if (tracklet0->status != ST_TRACKED) + continue; + + const cv::Rect2f &r0 = tracklet0->trajectory.back(); + float max_occlusion_ratio = 0.0f; + for (int32_t t1 = 0; t1 < static_cast(tracklets_.size()); ++t1) { + const auto &tracklet1 = tracklets_[t1]; + if (t0 == t1 || tracklet1->status == ST_LOST) + continue; + + const cv::Rect2f &r1 = tracklet1->trajectory.back(); + max_occlusion_ratio = std::max(max_occlusion_ratio, (r0 & r1).area() / r0.area()); // different from IoU + } + tracklets_[t0]->occlusion_ratio = max_occlusion_ratio; + } +} + +void Tracker::RemoveOutOfBoundTracklets(int32_t input_width, int32_t input_height, bool is_filtered) { + const cv::Rect2f image_region(0.0f, 0.0f, static_cast(input_width), static_cast(input_height)); + for (auto tracklet = tracklets_.begin(); tracklet != tracklets_.end();) { + const cv::Rect2f &object_region = + is_filtered ? (*tracklet)->trajectory_filtered.back() : (*tracklet)->trajectory.back(); + if ((image_region & object_region).area() / object_region.area() < + min_region_ratio_in_boundary_) { // only 10% is in image boundary + tracklet = tracklets_.erase(tracklet); + } else { + ++tracklet; + } + } +} + +void Tracker::RemoveDeadTracklets() { + for (auto tracklet = tracklets_.begin(); tracklet != tracklets_.end();) { + if ((*tracklet)->status == ST_DEAD) { + tracklet = tracklets_.erase(tracklet); + } else { + ++tracklet; + } + } +} + +bool Tracker::RemoveOneLostTracklet() { + for (auto tracklet = tracklets_.begin(); tracklet != tracklets_.end();) { + if ((*tracklet)->status == ST_LOST) { + // The first tracklet is the oldest + tracklet = tracklets_.erase(tracklet); + return true; + } else { + ++tracklet; + } + } + + return false; +} + +}; // namespace ot +}; // namespace vas diff --git a/modules/gapi/src/3rdparty/vasot/src/components/ot/tracker.hpp b/modules/gapi/src/3rdparty/vasot/src/components/ot/tracker.hpp new file mode 100644 index 0000000000..424bd1b45d --- /dev/null +++ b/modules/gapi/src/3rdparty/vasot/src/components/ot/tracker.hpp @@ -0,0 +1,123 @@ +/******************************************************************************* + * Copyright (C) 2023 Intel Corporation + * + * SPDX-License-Identifier: MIT + ******************************************************************************/ + +#ifndef VAS_OT_TRACKER_HPP +#define VAS_OT_TRACKER_HPP + +#include "mtt/objects_associator.hpp" +#include "tracklet.hpp" + +#include + +#include +#include + +namespace vas { +namespace ot { + +const int32_t kDefaultMaxNumObjects = -1; +const float kMaxTargetAreaFactor = 0.8f; +const float kMinRegionRatioInImageBoundary = 0.75f; // MIN_REGION_RATIO_IN_IMAGE_BOUNDARY + +class Tracker { + public: + enum Profile { + PROFILE_LONG_TERM = 0, // for long-term tracking usage + PROFILE_SHORT_TERM, // for short-term tracking usage (suitable for using with an object detector) + PROFILE_SHORT_TERM_KCFVAR, // alias of 'PROFILE_SHORT_TERM'. 'PROFILE_SHORT_TERM' will be deprecated + PROFILE_SHORT_TERM_IMAGELESS, // for short-term tracking usage with kalman tracking + PROFILE_ZERO_TERM, // for zero-term tracking usage (only works with object association algorithm, not tracking) + PROFILE_ZERO_TERM_IMAGELESS, // for zero-term tracking usage with kalman tracking + PROFILE_ZERO_TERM_COLOR_HISTOGRAM, // alias of 'PROFILE_ZERO_TERM'. 'PROFILE_ZERO_TERM' will be deprecated + }; + + class InitParameters { + public: + Profile profile; // tracking type + int32_t max_num_objects; + int32_t max_num_threads; // for Parallelization + vas::ColorFormat format; + bool tracking_per_class; + + // Won't be exposed to the external + float min_region_ratio_in_boundary; // For ST, ZT + }; + + public: + virtual ~Tracker(); + + /** + * create new object tracker instance + * @param InitParameters + */ + static Tracker *CreateInstance(InitParameters init_parameters); + + /** + * perform tracking + * + * @param[in] mat Input frame + * @param[in] detection Newly detected object data vector which will be added to the tracker. put zero length vector + * if there is no new object in the frame. + * @param[in] delta_t Time passed after the latest call to TrackObjects() in seconds. Use 1.0/FPS in case of + * constant frame rate + * @param[out] tracklets Tracked object data vector. + * @return 0 for success. negative value for failure + */ + virtual int32_t TrackObjects(const cv::Mat &mat, const std::vector &detections, + std::vector> *tracklets, float delta_t = 0.033f) = 0; + + /** + * remove object + * + * @param[in] id Object id for removing. it should be the 'id' value of the Tracklet + * @return 0 for success. negative value for failure. + */ + int32_t RemoveObject(const int32_t id); + + /** + * reset all internal state to its initial. + * + * @return 0 for success. negative value for failure. + */ + void Reset(void); + + /** + * get cumulated frame number + * + * @return 0 + */ + int32_t GetFrameCount(void) const; + + protected: + explicit Tracker(int32_t max_objects, float min_region_ratio_in_boundary, vas::ColorFormat format, + bool class_per_class = true); + Tracker() = delete; + + int32_t GetNextTrackingID(); + void IncreaseFrameCount(); + + void ComputeOcclusion(); + + void RemoveOutOfBoundTracklets(int32_t input_width, int32_t input_height, bool is_filtered = false); + void RemoveDeadTracklets(); + bool RemoveOneLostTracklet(); + + protected: + int32_t max_objects_; // -1 means no limitation + int32_t next_id_; + int32_t frame_count_; + + float min_region_ratio_in_boundary_; + vas::ColorFormat input_image_format_; + + ObjectsAssociator associator_; + std::vector> tracklets_; +}; + +}; // namespace ot +}; // namespace vas + +#endif // VAS_OT_TRACKER_HPP diff --git a/modules/gapi/src/3rdparty/vasot/src/components/ot/tracklet.cpp b/modules/gapi/src/3rdparty/vasot/src/components/ot/tracklet.cpp new file mode 100644 index 0000000000..62e8d10cf6 --- /dev/null +++ b/modules/gapi/src/3rdparty/vasot/src/components/ot/tracklet.cpp @@ -0,0 +1,147 @@ +/******************************************************************************* + * Copyright (C) 2023 Intel Corporation + * + * SPDX-License-Identifier: MIT + ******************************************************************************/ + +#include "tracklet.hpp" + +#include + +namespace vas { +namespace ot { + +Tracklet::Tracklet() + : id(0), label(-1), association_idx(kNoMatchDetection), status(ST_DEAD), age(0), confidence(0.f), + occlusion_ratio(0.f), association_delta_t(0.f), association_fail_count(0) { +} + +Tracklet::~Tracklet() { +} + +void Tracklet::ClearTrajectory() { + trajectory.clear(); + trajectory_filtered.clear(); +} + +void Tracklet::InitTrajectory(const cv::Rect2f &bounding_box) { + trajectory.push_back(bounding_box); + trajectory_filtered.push_back(bounding_box); +} + +void Tracklet::AddUpdatedTrajectory(const cv::Rect2f &bounding_box, const cv::Rect2f &corrected_box) { + trajectory.push_back(bounding_box); + trajectory_filtered.push_back(corrected_box); +} + +void Tracklet::UpdateLatestTrajectory(const cv::Rect2f &bounding_box, const cv::Rect2f &corrected_box) { + trajectory.back() = bounding_box; + trajectory_filtered.back() = corrected_box; +} + +void Tracklet::RenewTrajectory(const cv::Rect2f &bounding_box) { + ClearTrajectory(); + trajectory.push_back(bounding_box); + trajectory_filtered.push_back(bounding_box); +} + +#define DEFINE_STRING_VAR(var_name, value) \ + std::stringstream __##var_name; \ + __##var_name << value; \ + std::string var_name = __##var_name.str(); + +#define ROUND_F(value, scale) (round((value)*scale) / scale) + +std::string Tracklet::Serialize() const { +#ifdef DUMP_OTAV + DEFINE_STRING_VAR(s_id, id); + DEFINE_STRING_VAR(s_label, label); + DEFINE_STRING_VAR(s_association_idx, association_idx); + DEFINE_STRING_VAR(s_status, static_cast(status)); + DEFINE_STRING_VAR(s_age, age); + DEFINE_STRING_VAR(s_confidence, ROUND_F(confidence, 100.0)); + DEFINE_STRING_VAR(s_occlusion_ratio, ROUND_F(occlusion_ratio, 100.0)); + DEFINE_STRING_VAR(s_association_delta_t, association_delta_t); + DEFINE_STRING_VAR(s_association_fail_count, association_fail_count); + DEFINE_STRING_VAR(t_x, ROUND_F(trajectory.back().x, 10.0)); + DEFINE_STRING_VAR(t_y, ROUND_F(trajectory.back().y, 10.0)); + DEFINE_STRING_VAR(t_w, ROUND_F(trajectory.back().width, 10.0)); + DEFINE_STRING_VAR(t_h, ROUND_F(trajectory.back().height, 10.0)); + DEFINE_STRING_VAR(tf_x, ROUND_F(trajectory_filtered.back().x, 10.0)); + DEFINE_STRING_VAR(tf_y, ROUND_F(trajectory_filtered.back().y, 10.0)); + DEFINE_STRING_VAR(tf_w, ROUND_F(trajectory_filtered.back().width, 10.0)); + DEFINE_STRING_VAR(tf_h, ROUND_F(trajectory_filtered.back().height, 10.0)); + DEFINE_STRING_VAR(p_x, ROUND_F(predicted.x, 10.0)); + DEFINE_STRING_VAR(p_y, ROUND_F(predicted.y, 10.0)); + DEFINE_STRING_VAR(p_w, ROUND_F(predicted.width, 10.0)); + DEFINE_STRING_VAR(p_h, ROUND_F(predicted.height, 10.0)); + std::string formatted_msg = "meta:\"" + s_id + "," + s_label + "," + s_association_idx + "," + s_status + "," + + s_age + "," + s_confidence + "," + s_occlusion_ratio + "," + s_association_delta_t + + "," + s_association_fail_count + "\", roi:\"" + t_x + "," + t_y + "," + t_w + "," + + t_h + "\", roif:\"" + tf_x + "," + tf_y + "," + tf_w + "," + tf_h + "\", roip:\"" + + p_x + "," + p_y + "," + p_w + "," + p_h + "\""; + + std::string free_msg; + if (otav_msg.size() > 0) { + free_msg = ", msg: ["; + for (auto line : otav_msg) { + if (line.size() > 0) + free_msg += "\n\"" + line + "\","; + } + free_msg += "]"; + otav_msg.clear(); + } + return formatted_msg + free_msg; +#else + return ""; +#endif +} + +std::deque *Tracklet::GetRgbFeatures() { + return nullptr; +} + +ZeroTermImagelessTracklet::ZeroTermImagelessTracklet() : Tracklet(), birth_count(1) { +} + +ZeroTermImagelessTracklet::~ZeroTermImagelessTracklet() { +} + +void ZeroTermImagelessTracklet::RenewTrajectory(const cv::Rect2f &bounding_box) { + float velo_x = bounding_box.x - trajectory.back().x; + float velo_y = bounding_box.y - trajectory.back().y; + cv::Rect rect_predict(int(bounding_box.x + velo_x / 3), int(bounding_box.y + velo_y / 3), + int(bounding_box.width), int(bounding_box.height)); + + ClearTrajectory(); + kalman_filter.reset(new KalmanFilterNoOpencv(bounding_box)); + kalman_filter->Predict(); + kalman_filter->Correct(rect_predict); + + trajectory.push_back(bounding_box); + trajectory_filtered.push_back(bounding_box); +} + +ShortTermImagelessTracklet::ShortTermImagelessTracklet() : Tracklet() { +} + +ShortTermImagelessTracklet::~ShortTermImagelessTracklet() { +} + +void ShortTermImagelessTracklet::RenewTrajectory(const cv::Rect2f &bounding_box) { + float velo_x = bounding_box.x - trajectory.back().x; + float velo_y = bounding_box.y - trajectory.back().y; + cv::Rect rect_predict(int(bounding_box.x + velo_x / 3), int(bounding_box.y + velo_y / 3), + int(bounding_box.width), int(bounding_box.height)); + + ClearTrajectory(); + kalman_filter.reset(new KalmanFilterNoOpencv(bounding_box)); + kalman_filter->Predict(); + kalman_filter->Correct(rect_predict); + + trajectory.push_back(bounding_box); + trajectory_filtered.push_back(bounding_box); +} + +}; // namespace ot +}; // namespace vas diff --git a/modules/gapi/src/3rdparty/vasot/src/components/ot/tracklet.hpp b/modules/gapi/src/3rdparty/vasot/src/components/ot/tracklet.hpp new file mode 100644 index 0000000000..762e3f6ea6 --- /dev/null +++ b/modules/gapi/src/3rdparty/vasot/src/components/ot/tracklet.hpp @@ -0,0 +1,94 @@ +/******************************************************************************* + * Copyright (C) 2023 Intel Corporation + * + * SPDX-License-Identifier: MIT + ******************************************************************************/ + +#ifndef VAS_OT_TRACKET_HPP +#define VAS_OT_TRACKET_HPP + +#include "kalman_filter/kalman_filter_no_opencv.hpp" + +#include + +#include +#include + +namespace vas { +namespace ot { + +const int32_t kNoMatchDetection = -1; + +enum Status { + ST_DEAD = -1, // dead + ST_NEW = 0, // new + ST_TRACKED = 1, // tracked + ST_LOST = 2 // lost but still alive (in the detection phase if it configured) +}; + +struct Detection { + cv::Rect2f rect; + int32_t class_label = -1; + int32_t index = -1; +}; + +class Tracklet { + public: + Tracklet(); + virtual ~Tracklet(); + + public: + void ClearTrajectory(); + void InitTrajectory(const cv::Rect2f &bounding_box); + void AddUpdatedTrajectory(const cv::Rect2f &bounding_box, const cv::Rect2f &corrected_box); + void UpdateLatestTrajectory(const cv::Rect2f &bounding_box, const cv::Rect2f &corrected_box); + virtual void RenewTrajectory(const cv::Rect2f &bounding_box); + + virtual std::deque *GetRgbFeatures(); + virtual std::string Serialize() const; // Returns key:value with comma separated format + + public: + int32_t id; // If hasnot been assigned : -1 to 0 + int32_t label; + int32_t association_idx; + Status status; + int32_t age; + float confidence; + + float occlusion_ratio; + float association_delta_t; + int32_t association_fail_count; + + std::deque trajectory; + std::deque trajectory_filtered; + cv::Rect2f predicted; // Result from Kalman prediction. It is for debugging (OTAV) + mutable std::vector otav_msg; // Messages for OTAV +}; + +class ZeroTermImagelessTracklet : public Tracklet { + public: + ZeroTermImagelessTracklet(); + virtual ~ZeroTermImagelessTracklet(); + + void RenewTrajectory(const cv::Rect2f &bounding_box) override; + + public: + int32_t birth_count; + std::unique_ptr kalman_filter; +}; + +class ShortTermImagelessTracklet : public Tracklet { + public: + ShortTermImagelessTracklet(); + virtual ~ShortTermImagelessTracklet(); + + void RenewTrajectory(const cv::Rect2f &bounding_box) override; + + public: + std::unique_ptr kalman_filter; +}; + +}; // namespace ot +}; // namespace vas + +#endif // VAS_OT_TRACKET_HPP diff --git a/modules/gapi/src/3rdparty/vasot/src/components/ot/zero_term_imageless_tracker.cpp b/modules/gapi/src/3rdparty/vasot/src/components/ot/zero_term_imageless_tracker.cpp new file mode 100644 index 0000000000..c56c7813fc --- /dev/null +++ b/modules/gapi/src/3rdparty/vasot/src/components/ot/zero_term_imageless_tracker.cpp @@ -0,0 +1,185 @@ +/******************************************************************************* + * Copyright (C) 2023 Intel Corporation + * + * SPDX-License-Identifier: MIT + ******************************************************************************/ + +#include "zero_term_imageless_tracker.hpp" +#include "prof_def.hpp" +#include "../../common/exception.hpp" + +#include +#include + +namespace vas { +namespace ot { + +// const int32_t kMaxAssociationFailCount = 600; // about 20 seconds +const int32_t kMaxAssociationFailCount = 120; // about 4 seconds +const int32_t kMaxTrajectorySize = 30; + +const int32_t kMinBirthCount = 3; + +/** + * + * ZeroTermImagelessTracker + * + **/ +ZeroTermImagelessTracker::ZeroTermImagelessTracker(vas::ot::Tracker::InitParameters init_param) + : Tracker(init_param.max_num_objects, init_param.min_region_ratio_in_boundary, init_param.format, + init_param.tracking_per_class) { + TRACE(" - Created tracker = ZeroTermImagelessTracker"); +} + +ZeroTermImagelessTracker::~ZeroTermImagelessTracker() { +} + +int32_t ZeroTermImagelessTracker::TrackObjects(const cv::Mat &mat, const std::vector &detections, + std::vector> *tracklets, float delta_t) { + PROF_START(PROF_COMPONENTS_OT_ZEROTERM_RUN_TRACKER); + + int32_t input_img_width = mat.cols; + int32_t input_img_height = mat.rows; + + if (input_image_format_ == vas::ColorFormat::NV12 || input_image_format_ == vas::ColorFormat::I420) { + input_img_height = mat.rows / 3 * 2; + } + const cv::Rect2f image_boundary(0.0f, 0.0f, static_cast(input_img_width), + static_cast(input_img_height)); + + PROF_START(PROF_COMPONENTS_OT_ZEROTERM_KALMAN_PREDICTION); + // Predict tracklets state + for (auto &tracklet : tracklets_) { + auto zttimgless_tracklet = std::dynamic_pointer_cast(tracklet); + cv::Rect2f predicted_rect = zttimgless_tracklet->kalman_filter->Predict(delta_t); + zttimgless_tracklet->predicted = predicted_rect; + zttimgless_tracklet->trajectory.push_back(predicted_rect); + zttimgless_tracklet->trajectory_filtered.push_back(predicted_rect); + zttimgless_tracklet->association_delta_t += delta_t; + // Reset association index every frame for new detection input + zttimgless_tracklet->association_idx = kNoMatchDetection; + } + + PROF_END(PROF_COMPONENTS_OT_ZEROTERM_KALMAN_PREDICTION); + + PROF_START(PROF_COMPONENTS_OT_ZEROTERM_RUN_ASSOCIATION); + + // Tracklet-detection association + int32_t n_detections = static_cast(detections.size()); + int32_t n_tracklets = static_cast(tracklets_.size()); + + std::vector d_is_associated(n_detections, false); + std::vector t_associated_d_index(n_tracklets, -1); + + if (n_detections > 0) { + auto result = associator_.Associate(detections, tracklets_); + d_is_associated = result.first; + t_associated_d_index = result.second; + } + + PROF_END(PROF_COMPONENTS_OT_ZEROTERM_RUN_ASSOCIATION); + + PROF_START(PROF_COMPONENTS_OT_ZEROTERM_UPDATE_STATUS); + // Update tracklets' state + for (int32_t t = 0; t < n_tracklets; ++t) { + auto &tracklet = tracklets_[t]; + if (t_associated_d_index[t] >= 0) { + tracklet->association_delta_t = 0.0f; + int32_t associated_d_index = t_associated_d_index[t]; + const cv::Rect2f &d_bounding_box = detections[associated_d_index].rect & image_boundary; + + // Apply associated detection to tracklet + tracklet->association_idx = detections[associated_d_index].index; + tracklet->association_fail_count = 0; + tracklet->label = detections[associated_d_index].class_label; + + auto zttimgless_tracklet = std::dynamic_pointer_cast(tracklet); + if (!zttimgless_tracklet) + continue; + + if (zttimgless_tracklet->status == ST_NEW) { + zttimgless_tracklet->trajectory.back() = d_bounding_box; + zttimgless_tracklet->trajectory_filtered.back() = + zttimgless_tracklet->kalman_filter->Correct(zttimgless_tracklet->trajectory.back()); + zttimgless_tracklet->birth_count += 1; + if (zttimgless_tracklet->birth_count >= kMinBirthCount) { + zttimgless_tracklet->status = ST_TRACKED; + } + } else if (zttimgless_tracklet->status == ST_TRACKED) { + zttimgless_tracklet->trajectory.back() = d_bounding_box; + zttimgless_tracklet->trajectory_filtered.back() = + zttimgless_tracklet->kalman_filter->Correct(zttimgless_tracklet->trajectory.back()); + } else if (zttimgless_tracklet->status == ST_LOST) { + zttimgless_tracklet->RenewTrajectory(d_bounding_box); + zttimgless_tracklet->status = ST_TRACKED; + } + } else { + if (tracklet->status == ST_NEW) { + tracklet->status = ST_DEAD; // regard non-consecutive association as false alarm + } else if (tracklet->status == ST_TRACKED) { + tracklet->status = ST_LOST; + tracklet->association_fail_count = 0; + } else if (tracklet->status == ST_LOST) { + if (++tracklet->association_fail_count >= kMaxAssociationFailCount) { + // # association fail > threshold while missing -> DEAD + tracklet->status = ST_DEAD; + } + } + } + } + PROF_END(PROF_COMPONENTS_OT_ZEROTERM_UPDATE_STATUS); + + PROF_START(PROF_COMPONENTS_OT_ZEROTERM_COMPUTE_OCCLUSION); + ComputeOcclusion(); + PROF_END(PROF_COMPONENTS_OT_ZEROTERM_COMPUTE_OCCLUSION); + + PROF_START(PROF_COMPONENTS_OT_ZEROTERM_REGISTER_OBJECT); + // Register remaining detections as new objects + for (int32_t d = 0; d < static_cast(detections.size()); ++d) { + if (d_is_associated[d] == false) { + if (static_cast(tracklets_.size()) >= max_objects_ && max_objects_ != -1) + continue; + + std::unique_ptr tracklet(new ZeroTermImagelessTracklet()); + + tracklet->status = ST_NEW; + tracklet->id = GetNextTrackingID(); + tracklet->label = detections[d].class_label; + tracklet->association_idx = detections[d].index; + + const cv::Rect2f &bounding_box = detections[d].rect & image_boundary; + tracklet->InitTrajectory(bounding_box); + tracklet->kalman_filter.reset(new KalmanFilterNoOpencv(bounding_box)); + tracklets_.push_back(std::move(tracklet)); + } + } + PROF_END(PROF_COMPONENTS_OT_ZEROTERM_REGISTER_OBJECT); + + RemoveDeadTracklets(); + RemoveOutOfBoundTracklets(input_img_width, input_img_height); + TrimTrajectories(); + + *tracklets = tracklets_; + + IncreaseFrameCount(); + PROF_END(PROF_COMPONENTS_OT_ZEROTERM_RUN_TRACKER); + return 0; +} + +void ZeroTermImagelessTracker::TrimTrajectories() { + for (auto &tracklet : tracklets_) { + auto &trajectory = tracklet->trajectory; + while (trajectory.size() > kMaxTrajectorySize) { + trajectory.pop_front(); + } + + // + auto &trajectory_filtered = tracklet->trajectory_filtered; + while (trajectory_filtered.size() > kMaxTrajectorySize) { + trajectory_filtered.pop_front(); + } + } +} + +}; // namespace ot +}; // namespace vas diff --git a/modules/gapi/src/3rdparty/vasot/src/components/ot/zero_term_imageless_tracker.hpp b/modules/gapi/src/3rdparty/vasot/src/components/ot/zero_term_imageless_tracker.hpp new file mode 100644 index 0000000000..1f5f760e62 --- /dev/null +++ b/modules/gapi/src/3rdparty/vasot/src/components/ot/zero_term_imageless_tracker.hpp @@ -0,0 +1,37 @@ +/******************************************************************************* + * Copyright (C) 2023 Intel Corporation + * + * SPDX-License-Identifier: MIT + ******************************************************************************/ + +#ifndef VAS_OT_ZERO_TERM_IMAGELESS_TRACKER_HPP +#define VAS_OT_ZERO_TERM_IMAGELESS_TRACKER_HPP + +#include "tracker.hpp" + +#include +#include + +namespace vas { +namespace ot { + +class ZeroTermImagelessTracker : public Tracker { + public: + explicit ZeroTermImagelessTracker(vas::ot::Tracker::InitParameters init_param); + virtual ~ZeroTermImagelessTracker(); + + virtual int32_t TrackObjects(const cv::Mat &mat, const std::vector &detections, + std::vector> *tracklets, float delta_t) override; + + ZeroTermImagelessTracker() = delete; + ZeroTermImagelessTracker(const ZeroTermImagelessTracker &) = delete; + ZeroTermImagelessTracker &operator=(const ZeroTermImagelessTracker &) = delete; + + private: + void TrimTrajectories(); +}; + +}; // namespace ot +}; // namespace vas + +#endif // VAS_OT_ZERO_TERM_IMAGELESS_TRACKER_HPP diff --git a/modules/gapi/src/api/kernels_ot.cpp b/modules/gapi/src/api/kernels_ot.cpp new file mode 100644 index 0000000000..412b07d151 --- /dev/null +++ b/modules/gapi/src/api/kernels_ot.cpp @@ -0,0 +1,43 @@ +// This file is part of OpenCV project. +// It is subject to the license terms in the LICENSE file found in the top-level directory +// of this distribution and at http://opencv.org/license.html. +// +// Copyright (C) 2023 Intel Corporation + +#include +#include + +#include + +namespace cv +{ +namespace gapi +{ +namespace ot +{ +GAPI_EXPORTS_W std::tuple, + cv::GArray, + cv::GArray, + cv::GArray> + track(const cv::GMat& mat, + const cv::GArray& detected_rects, + const cv::GArray& detected_class_labels, + float delta) +{ + return GTrackFromMat::on(mat, detected_rects, detected_class_labels, delta); +} + +GAPI_EXPORTS_W std::tuple, + cv::GArray, + cv::GArray, + cv::GArray> + track(const cv::GFrame& frame, + const cv::GArray& detected_rects, + const cv::GArray& detected_class_labels, + float delta) +{ + return GTrackFromFrame::on(frame, detected_rects, detected_class_labels, delta); +} +} // namespace ot +} // namespace gapi +} // namespace cv diff --git a/modules/gapi/src/backends/cpu/gcpuot.cpp b/modules/gapi/src/backends/cpu/gcpuot.cpp new file mode 100644 index 0000000000..abe7327be8 --- /dev/null +++ b/modules/gapi/src/backends/cpu/gcpuot.cpp @@ -0,0 +1,163 @@ +// This file is part of OpenCV project. +// It is subject to the license terms in the LICENSE file found in the top-level directory +// of this distribution and at http://opencv.org/license.html. +// +// Copyright (C) 2023 Intel Corporation + +#include +#include +#include + +#include + +namespace cv +{ +namespace gapi +{ +namespace ot +{ + +// Helper functions for OT kernels +namespace { +void GTrackImplSetup(cv::GArrayDesc, cv::GArrayDesc, float, + std::shared_ptr& state, + const ObjectTrackerParams& params) { + vas::ot::ObjectTracker::Builder ot_builder; + ot_builder.max_num_objects = params.max_num_objects; + ot_builder.input_image_format = vas::ColorFormat(params.input_image_format); + ot_builder.tracking_per_class = params.tracking_per_class; + + state = ot_builder.Build(vas::ot::TrackingType::ZERO_TERM_IMAGELESS); +} + +void GTrackImplPrepare(const std::vector& in_rects, + const std::vector& in_class_labels, + float delta, + std::vector& detected_objs, + vas::ot::ObjectTracker& state) +{ + if (in_rects.size() != in_class_labels.size()) + { + cv::util::throw_error(std::invalid_argument("Track() implementation run() method: in_rects and in_class_labels " + "sizes are different.")); + } + + detected_objs.reserve(in_rects.size()); + + for (std::size_t i = 0; i < in_rects.size(); ++i) + { + detected_objs.emplace_back(in_rects[i], in_class_labels[i]); + } + + state.SetFrameDeltaTime(delta); +} +} // anonymous namespace + +GAPI_OCV_KERNEL_ST(GTrackFromMatImpl, cv::gapi::ot::GTrackFromMat, vas::ot::ObjectTracker) +{ + static void setup(cv::GMatDesc, cv::GArrayDesc rects_desc, + cv::GArrayDesc labels_desc, float delta, + std::shared_ptr& state, + const cv::GCompileArgs& compile_args) + { + auto params = cv::gapi::getCompileArg(compile_args) + .value_or(ObjectTrackerParams{}); + + GAPI_Assert(params.input_image_format == 0 && "Only BGR input as cv::GMat is supported for now"); + GTrackImplSetup(rects_desc, labels_desc, delta, state, params); + } + + static void run(const cv::Mat& in_mat, const std::vector& in_rects, + const std::vector& in_class_labels, float delta, + std::vector& out_tr_rects, + std::vector& out_rects_classes, + std::vector& out_tr_ids, + std::vector& out_tr_statuses, + vas::ot::ObjectTracker& state) + { + std::vector detected_objs; + GTrackImplPrepare(in_rects, in_class_labels, delta, detected_objs, state); + + GAPI_Assert(in_mat.type() == CV_8UC3 && "Input mat is not in BGR format"); + + auto objects = state.Track(in_mat, detected_objs); + + for (auto&& object : objects) + { + out_tr_rects.push_back(object.rect); + out_rects_classes.push_back(object.class_label); + out_tr_ids.push_back(object.tracking_id); + out_tr_statuses.push_back(static_cast(object.status)); + } + } +}; + +GAPI_OCV_KERNEL_ST(GTrackFromFrameImpl, cv::gapi::ot::GTrackFromFrame, vas::ot::ObjectTracker) +{ + static void setup(cv::GFrameDesc, cv::GArrayDesc rects_desc, + cv::GArrayDesc labels_desc, float delta, + std::shared_ptr& state, + const cv::GCompileArgs& compile_args) + { + auto params = cv::gapi::getCompileArg(compile_args) + .value_or(ObjectTrackerParams{}); + + GAPI_Assert(params.input_image_format == 1 && "Only NV12 input as cv::GFrame is supported for now"); + GTrackImplSetup(rects_desc, labels_desc, delta, state, params); + } + + static void run(const cv::MediaFrame& in_frame, const std::vector& in_rects, + const std::vector& in_class_labels, float delta, + std::vector& out_tr_rects, + std::vector& out_rects_classes, + std::vector& out_tr_ids, + std::vector& out_tr_statuses, + vas::ot::ObjectTracker& state) + { + std::vector detected_objs; + GTrackImplPrepare(in_rects, in_class_labels, delta, detected_objs, state); + + // Extract metadata from MediaFrame and construct cv::Mat atop of it + cv::MediaFrame::View view = in_frame.access(cv::MediaFrame::Access::R); + auto ptrs = view.ptr; + auto strides = view.stride; + auto desc = in_frame.desc(); + + GAPI_Assert((desc.fmt == cv::MediaFormat::NV12 || desc.fmt == cv::MediaFormat::BGR) \ + && "Input frame is not in NV12 or BGR format"); + + cv::Mat in; + if (desc.fmt == cv::MediaFormat::NV12) { + GAPI_Assert(ptrs[0] != nullptr && "Y plane pointer is empty"); + GAPI_Assert(ptrs[1] != nullptr && "UV plane pointer is empty"); + if (strides[0] > 0) { + in = cv::Mat(desc.size, CV_8UC1, ptrs[0], strides[0]); + } else { + in = cv::Mat(desc.size, CV_8UC1, ptrs[0]); + } + } + + auto objects = state.Track(in, detected_objs); + + for (auto&& object : objects) + { + out_tr_rects.push_back(object.rect); + out_rects_classes.push_back(object.class_label); + out_tr_ids.push_back(object.tracking_id); + out_tr_statuses.push_back(static_cast(object.status)); + } + } +}; + +cv::gapi::GKernelPackage cpu::kernels() +{ + return cv::gapi::kernels + < + GTrackFromFrameImpl, + GTrackFromMatImpl + >(); +} + +} // namespace ot +} // namespace gapi +} // namespace cv diff --git a/modules/gapi/src/backends/ie/giebackend.cpp b/modules/gapi/src/backends/ie/giebackend.cpp index 6026f29ae5..935f81d275 100644 --- a/modules/gapi/src/backends/ie/giebackend.cpp +++ b/modules/gapi/src/backends/ie/giebackend.cpp @@ -2082,15 +2082,8 @@ struct InferList2: public cv::detail::KernelTag { ? uu.net.getInputsInfo().at(input_name_0)->getTensorDesc() : uu.this_network.GetInputsInfo().at(input_name_0)->getTensorDesc(); - if (cv::util::holds_alternative(mm_0) || - cv::util::holds_alternative(mm_0)) { - const auto trait = clarifyTrait(mm_0, tensor_desc_0.getDims()); - if (trait != cv::gapi::ie::TraitAs::IMAGE) { - util::throw_error(std::runtime_error( - "IE Backend: Only images is" - " supported as the 0th argument")); - } - } else { + if (!(cv::util::holds_alternative(mm_0) || + cv::util::holds_alternative(mm_0))) { util::throw_error(std::runtime_error( "IE Backend: Unsupported input meta" " for 0th argument in IE backend")); @@ -2107,7 +2100,10 @@ struct InferList2: public cv::detail::KernelTag { && "Non-array inputs are not supported"); if (op.k.inKinds[idx] == cv::detail::OpaqueKind::CV_RECT) { - const auto input_trait = cv::gapi::ie::TraitAs::IMAGE; + const auto input_trait = clarifyTrait(mm_0, tensor_desc_0.getDims()); + GAPI_Assert(input_trait == cv::gapi::ie::TraitAs::IMAGE + && "IE Backend: Only image is supported as the 0th argument for an input array of cv::Rect"); + // NB: Configuring input precision and network reshape must be done // only in the loadNetwork case. if (uu.params.kind == cv::gapi::ie::detail::ParamDesc::Kind::Load) { diff --git a/modules/gapi/src/backends/onnx/bindings_onnx.cpp b/modules/gapi/src/backends/onnx/bindings_onnx.cpp index b41ec7b1b1..0703f1753d 100644 --- a/modules/gapi/src/backends/onnx/bindings_onnx.cpp +++ b/modules/gapi/src/backends/onnx/bindings_onnx.cpp @@ -33,6 +33,12 @@ cv::gapi::onnx::PyParams::cfgAddExecutionProvider(cv::gapi::onnx::ep::DirectML e return *this; } +cv::gapi::onnx::PyParams& +cv::gapi::onnx::PyParams::cfgAddExecutionProvider(cv::gapi::onnx::ep::CoreML ep) { + m_priv->cfgAddExecutionProvider(std::move(ep)); + return *this; +} + cv::gapi::onnx::PyParams& cv::gapi::onnx::PyParams::cfgAddExecutionProvider(cv::gapi::onnx::ep::CUDA ep) { m_priv->cfgAddExecutionProvider(std::move(ep)); diff --git a/modules/gapi/src/backends/onnx/coreml_ep.cpp b/modules/gapi/src/backends/onnx/coreml_ep.cpp new file mode 100644 index 0000000000..3c9507863d --- /dev/null +++ b/modules/gapi/src/backends/onnx/coreml_ep.cpp @@ -0,0 +1,50 @@ +// This file is part of OpenCV project. +// It is subject to the license terms in the LICENSE file found in the top-level directory +// of this distribution and at http://opencv.org/license.html. +// +// Copyright (C) 2023 Intel Corporation + +#include "backends/onnx/coreml_ep.hpp" +#include "logger.hpp" + +#ifdef HAVE_ONNX +#include + +#ifdef HAVE_ONNX_COREML +#include "../providers/coreml/coreml_provider_factory.h" + +void cv::gimpl::onnx::addCoreMLExecutionProvider(Ort::SessionOptions *session_options, + const cv::gapi::onnx::ep::CoreML &coreml_ep) { + uint32_t flags = 0u; + if (coreml_ep.use_cpu_only) { + flags |= COREML_FLAG_USE_CPU_ONLY; + } + + if (coreml_ep.enable_on_subgraph) { + flags |= COREML_FLAG_ENABLE_ON_SUBGRAPH; + } + + if (coreml_ep.enable_only_ane) { + flags |= COREML_FLAG_ONLY_ENABLE_DEVICE_WITH_ANE; + } + + try { + OrtSessionOptionsAppendExecutionProvider_CoreML(*session_options, flags); + } catch (const std::exception &e) { + std::stringstream ss; + ss << "ONNX Backend: Failed to enable CoreML" + << " Execution Provider: " << e.what(); + cv::util::throw_error(std::runtime_error(ss.str())); + } +} + +#else // HAVE_ONNX_COREML + +void cv::gimpl::onnx::addCoreMLExecutionProvider(Ort::SessionOptions*, + const cv::gapi::onnx::ep::CoreML&) { + util::throw_error(std::runtime_error("G-API has been compiled with ONNXRT" + " without CoreML support")); +} + +#endif // HAVE_ONNX_COREML +#endif // HAVE_ONNX diff --git a/modules/gapi/src/backends/onnx/coreml_ep.hpp b/modules/gapi/src/backends/onnx/coreml_ep.hpp new file mode 100644 index 0000000000..ddc2baeae9 --- /dev/null +++ b/modules/gapi/src/backends/onnx/coreml_ep.hpp @@ -0,0 +1,23 @@ +// This file is part of OpenCV project. +// It is subject to the license terms in the LICENSE file found in the top-level directory +// of this distribution and at http://opencv.org/license.html. +// +// Copyright (C) 2023 Intel Corporation + +#ifndef OPENCV_GAPI_COREML_EP_HPP +#define OPENCV_GAPI_COREML_EP_HPP + +#include "opencv2/gapi/infer/onnx.hpp" +#ifdef HAVE_ONNX + +#include + +namespace cv { +namespace gimpl { +namespace onnx { +void addCoreMLExecutionProvider(Ort::SessionOptions *session_options, + const cv::gapi::onnx::ep::CoreML &coreml_ep); +}}} + +#endif // HAVE_ONNX +#endif // OPENCV_GAPI_COREML_EP_HPP diff --git a/modules/gapi/src/backends/onnx/dml_ep.cpp b/modules/gapi/src/backends/onnx/dml_ep.cpp index 7f59e1f3d6..671fa2dbcb 100644 --- a/modules/gapi/src/backends/onnx/dml_ep.cpp +++ b/modules/gapi/src/backends/onnx/dml_ep.cpp @@ -13,13 +13,240 @@ #ifdef HAVE_ONNX_DML #include "../providers/dml/dml_provider_factory.h" +#ifdef HAVE_DIRECTML + +#undef WINVER +#define WINVER 0x0A00 +#undef _WIN32_WINNT +#define _WIN32_WINNT 0x0A00 + +#include + +#include +#include +#include +#include +#include +#include +#include +#include + +#pragma comment (lib, "d3d11.lib") +#pragma comment (lib, "d3d12.lib") +#pragma comment (lib, "dxgi.lib") +#pragma comment (lib, "dxcore.lib") +#pragma comment (lib, "directml.lib") + +#endif // HAVE_DIRECTML + +static void addDMLExecutionProviderWithAdapterName(Ort::SessionOptions *session_options, + const std::string &adapter_name); + void cv::gimpl::onnx::addDMLExecutionProvider(Ort::SessionOptions *session_options, const cv::gapi::onnx::ep::DirectML &dml_ep) { namespace ep = cv::gapi::onnx::ep; - GAPI_Assert(cv::util::holds_alternative(dml_ep.ddesc)); - const int device_id = cv::util::get(dml_ep.ddesc); + switch (dml_ep.ddesc.index()) { + case ep::DirectML::DeviceDesc::index_of(): { + const int device_id = cv::util::get(dml_ep.ddesc); + try { + OrtSessionOptionsAppendExecutionProvider_DML(*session_options, device_id); + } catch (const std::exception &e) { + std::stringstream ss; + ss << "ONNX Backend: Failed to enable DirectML" + << " Execution Provider: " << e.what(); + cv::util::throw_error(std::runtime_error(ss.str())); + } + break; + } + case ep::DirectML::DeviceDesc::index_of(): { + const std::string adapter_name = cv::util::get(dml_ep.ddesc); + addDMLExecutionProviderWithAdapterName(session_options, adapter_name); + break; + } + default: + GAPI_Assert(false && "Invalid DirectML device description"); + } +} + +#ifdef HAVE_DIRECTML + +#define THROW_IF_FAILED(hr, error_msg) \ +{ \ + if ((hr) != S_OK) \ + throw std::runtime_error(error_msg); \ +} + +template +void release(T *ptr) { + if (ptr) { + ptr->Release(); + } +} + +template +using ComPtrGuard = std::unique_ptr)>; + +template +ComPtrGuard make_com_ptr(T *ptr) { + return ComPtrGuard{ptr, &release}; +} + +struct AdapterDesc { + ComPtrGuard ptr; + std::string description; +}; + +static std::vector getAvailableAdapters() { + std::vector all_adapters; + + IDXCoreAdapterFactory* factory_ptr; + GAPI_LOG_DEBUG(nullptr, "Create IDXCoreAdapterFactory"); + THROW_IF_FAILED( + DXCoreCreateAdapterFactory( + __uuidof(IDXCoreAdapterFactory), (void**)&factory_ptr), + "Failed to create IDXCoreAdapterFactory"); + auto factory = make_com_ptr(factory_ptr); + + IDXCoreAdapterList* adapter_list_ptr; + const GUID dxGUIDs[] = { DXCORE_ADAPTER_ATTRIBUTE_D3D12_CORE_COMPUTE }; + GAPI_LOG_DEBUG(nullptr, "CreateAdapterList"); + THROW_IF_FAILED( + factory->CreateAdapterList( + ARRAYSIZE(dxGUIDs), dxGUIDs, __uuidof(IDXCoreAdapterList), (void**)&adapter_list_ptr), + "Failed to create IDXCoreAdapterList"); + auto adapter_list = make_com_ptr(adapter_list_ptr); + + for (UINT i = 0; i < adapter_list->GetAdapterCount(); i++) + { + IDXCoreAdapter* curr_adapter_ptr; + GAPI_LOG_DEBUG(nullptr, "GetAdapter"); + THROW_IF_FAILED( + adapter_list->GetAdapter( + i, __uuidof(IDXCoreAdapter), (void**)&curr_adapter_ptr), + "Failed to obtain IDXCoreAdapter" + ); + auto curr_adapter = make_com_ptr(curr_adapter_ptr); + + bool is_hardware = false; + curr_adapter->GetProperty(DXCoreAdapterProperty::IsHardware, &is_hardware); + // NB: Filter out if not hardware adapter. + if (!is_hardware) { + continue; + } + + size_t desc_size = 0u; + char description[256]; + curr_adapter->GetPropertySize(DXCoreAdapterProperty::DriverDescription, &desc_size); + curr_adapter->GetProperty(DXCoreAdapterProperty::DriverDescription, desc_size, &description); + all_adapters.push_back(AdapterDesc{std::move(curr_adapter), description}); + } + return all_adapters; +}; + +struct DMLDeviceInfo { + ComPtrGuard device; + ComPtrGuard cmd_queue; +}; + +static DMLDeviceInfo createDMLInfo(IDXCoreAdapter* adapter) { + auto pAdapter = make_com_ptr(adapter); + D3D_FEATURE_LEVEL d3dFeatureLevel = D3D_FEATURE_LEVEL_1_0_CORE; + if (adapter->IsAttributeSupported(DXCORE_ADAPTER_ATTRIBUTE_D3D12_GRAPHICS)) + { + GAPI_LOG_INFO(nullptr, "DXCORE_ADAPTER_ATTRIBUTE_D3D12_GRAPHICS is supported"); + d3dFeatureLevel = D3D_FEATURE_LEVEL::D3D_FEATURE_LEVEL_11_0; + + IDXGIFactory4* dxgiFactory4; + GAPI_LOG_DEBUG(nullptr, "CreateDXGIFactory2"); + THROW_IF_FAILED( + CreateDXGIFactory2(0, __uuidof(IDXGIFactory4), (void**)&dxgiFactory4), + "Failed to create IDXGIFactory4" + ); + // If DXGI factory creation was successful then get the IDXGIAdapter from the LUID + // acquired from the selectedAdapter + LUID adapterLuid; + IDXGIAdapter* spDxgiAdapter; + + GAPI_LOG_DEBUG(nullptr, "Get DXCoreAdapterProperty::InstanceLuid property"); + THROW_IF_FAILED( + adapter->GetProperty(DXCoreAdapterProperty::InstanceLuid, &adapterLuid), + "Failed to get DXCoreAdapterProperty::InstanceLuid property"); + + GAPI_LOG_DEBUG(nullptr, "Get IDXGIAdapter by luid"); + THROW_IF_FAILED( + dxgiFactory4->EnumAdapterByLuid( + adapterLuid, __uuidof(IDXGIAdapter), (void**)&spDxgiAdapter), + "Failed to get IDXGIAdapter"); + pAdapter = make_com_ptr(spDxgiAdapter); + } else { + GAPI_LOG_INFO(nullptr, "DXCORE_ADAPTER_ATTRIBUTE_D3D12_GRAPHICS isn't supported"); + } + + ID3D12Device* d3d12_device_ptr; + GAPI_LOG_DEBUG(nullptr, "Create D3D12Device"); + THROW_IF_FAILED( + D3D12CreateDevice( + pAdapter.get(), d3dFeatureLevel, __uuidof(ID3D12Device), (void**)&d3d12_device_ptr), + "Failed to create ID3D12Device"); + auto d3d12_device = make_com_ptr(d3d12_device_ptr); + + D3D12_COMMAND_LIST_TYPE commandQueueType = D3D12_COMMAND_LIST_TYPE_COMPUTE; + ID3D12CommandQueue* cmd_queue_ptr; + D3D12_COMMAND_QUEUE_DESC commandQueueDesc = {}; + commandQueueDesc.Type = commandQueueType; + GAPI_LOG_DEBUG(nullptr, "Create D3D12CommandQueue"); + THROW_IF_FAILED( + d3d12_device->CreateCommandQueue( + &commandQueueDesc, __uuidof(ID3D12CommandQueue), (void**)&cmd_queue_ptr), + "Failed to create D3D12CommandQueue" + ); + GAPI_LOG_DEBUG(nullptr, "Create D3D12CommandQueue - successful"); + auto cmd_queue = make_com_ptr(cmd_queue_ptr); + + IDMLDevice* dml_device_ptr; + GAPI_LOG_DEBUG(nullptr, "Create DirectML device"); + THROW_IF_FAILED( + DMLCreateDevice( + d3d12_device.get(), DML_CREATE_DEVICE_FLAG_NONE, IID_PPV_ARGS(&dml_device_ptr)), + "Failed to create IDMLDevice"); + GAPI_LOG_DEBUG(nullptr, "Create DirectML device - successful"); + auto dml_device = make_com_ptr(dml_device_ptr); + + return {std::move(dml_device), std::move(cmd_queue)}; +}; + +static void addDMLExecutionProviderWithAdapterName(Ort::SessionOptions *session_options, + const std::string &adapter_name) { + auto all_adapters = getAvailableAdapters(); + + std::vector selected_adapters; + std::stringstream log_msg; + for (auto&& adapter : all_adapters) { + log_msg << adapter.description << std::endl; + if (std::strstr(adapter.description.c_str(), adapter_name.c_str())) { + selected_adapters.emplace_back(std::move(adapter)); + } + } + GAPI_LOG_INFO(NULL, "\nAvailable DirectML adapters:\n" << log_msg.str()); + + if (selected_adapters.empty()) { + std::stringstream error_msg; + error_msg << "ONNX Backend: No DirectML adapters found match to \"" << adapter_name << "\""; + cv::util::throw_error(std::runtime_error(error_msg.str())); + } else if (selected_adapters.size() > 1) { + std::stringstream error_msg; + error_msg << "ONNX Backend: More than one adapter matches to \"" << adapter_name << "\":\n"; + for (const auto &selected_adapter : selected_adapters) { + error_msg << selected_adapter.description << "\n"; + } + cv::util::throw_error(std::runtime_error(error_msg.str())); + } + + GAPI_LOG_INFO(NULL, "Selected device: " << selected_adapters.front().description); + auto dml = createDMLInfo(selected_adapters.front().ptr.get()); try { - OrtSessionOptionsAppendExecutionProvider_DML(*session_options, device_id); + OrtSessionOptionsAppendExecutionProviderEx_DML( + *session_options, dml.device.release(), dml.cmd_queue.release()); } catch (const std::exception &e) { std::stringstream ss; ss << "ONNX Backend: Failed to enable DirectML" @@ -28,6 +255,16 @@ void cv::gimpl::onnx::addDMLExecutionProvider(Ort::SessionOptions *session_optio } } +#else // HAVE_DIRECTML + +static void addDMLExecutionProviderWithAdapterName(Ort::SessionOptions*, const std::string&) { + std::stringstream ss; + ss << "ONNX Backend: Failed to add DirectML Execution Provider with adapter name." + << " DirectML support is required."; + cv::util::throw_error(std::runtime_error(ss.str())); +} + +#endif // HAVE_DIRECTML #else // HAVE_ONNX_DML void cv::gimpl::onnx::addDMLExecutionProvider(Ort::SessionOptions*, diff --git a/modules/gapi/src/backends/onnx/gonnxbackend.cpp b/modules/gapi/src/backends/onnx/gonnxbackend.cpp index c552b8b0e6..e2ddd3ea59 100644 --- a/modules/gapi/src/backends/onnx/gonnxbackend.cpp +++ b/modules/gapi/src/backends/onnx/gonnxbackend.cpp @@ -10,6 +10,7 @@ #ifdef HAVE_ONNX #include "backends/onnx/dml_ep.hpp" +#include "backends/onnx/coreml_ep.hpp" #include // any_of #include @@ -211,6 +212,12 @@ static void addExecutionProvider(Ort::SessionOptions *session_options, addDMLExecutionProvider(session_options, dml_ep); break; } + case ep::EP::index_of(): { + GAPI_LOG_INFO(NULL, "CoreML Execution Provider is added."); + const auto &coreml_ep = cv::util::get(execution_provider); + addCoreMLExecutionProvider(session_options, coreml_ep); + break; + } case ep::EP::index_of(): { GAPI_LOG_INFO(NULL, "CUDA Execution Provider is added."); const auto &cuda_ep = cv::util::get(execution_provider); diff --git a/modules/gapi/src/backends/ov/govbackend.cpp b/modules/gapi/src/backends/ov/govbackend.cpp index 8384a9d188..7e99af4f77 100644 --- a/modules/gapi/src/backends/ov/govbackend.cpp +++ b/modules/gapi/src/backends/ov/govbackend.cpp @@ -252,7 +252,8 @@ public: const std::vector & outs, cv::GRunArg::Meta && meta, std::vector && input_objs, - std::vector && output_objs); + std::vector && output_objs, + const cv::gimpl::ov::Options & options); const cv::GArgs& inArgs() const; @@ -281,6 +282,9 @@ public: std::exception_ptr eptr; const cv::GRunArg::Meta& getMeta() { return m_meta; }; + + const cv::gimpl::ov::Options& getOptions() const { return m_options; }; + private: cv::detail::VectorRef& outVecRef(std::size_t idx); @@ -301,6 +305,8 @@ private: // Input parameters passed to an inference operation. cv::GArgs m_args; cv::GShapes m_in_shapes; + + cv::gimpl::ov::Options m_options; }; OVCallContext::OVCallContext(const OVUnit & unit, @@ -309,9 +315,11 @@ OVCallContext::OVCallContext(const OVUnit & const std::vector & outs, cv::GRunArg::Meta && meta, std::vector && input_objs, - std::vector && output_objs) + std::vector && output_objs, + const cv::gimpl::ov::Options & options) : uu(unit), out(output), m_meta(std::move(meta)), - m_input_objs(std::move(input_objs)), m_output_objs(std::move(output_objs)) + m_input_objs(std::move(input_objs)), m_output_objs(std::move(output_objs)), + m_options(options) { for (auto& it : m_input_objs) cv::gimpl::magazine::bindInArg (m_res, it.first, it.second); for (auto& it : m_output_objs) cv::gimpl::magazine::bindOutArg(m_res, it.first, it.second); @@ -577,9 +585,10 @@ static void PostOutputs(::ov::InferRequest &infer_request, ctx->eptr = std::move(eptr); for (auto i : ade::util::iota(ctx->uu.params.num_out)) { - // NB: Copy data back only if execution finished sucessfuly. - // Otherwise just post outputs to keep streaming executor contract. - if (!ctx->eptr) { + // NB: Copy data back only if execution finished sucessfuly + // and inference only mode is disabled. + // Otherwise just post outputs to maintain streaming executor contract. + if (!ctx->eptr && !ctx->getOptions().inference_only) { const auto& out_name = ctx->uu.params.output_names[i]; copyFromOV(infer_request.get_tensor(out_name), ctx->outMatR(i)); @@ -765,14 +774,19 @@ public: if (explicit_in_model_layout) { input_info.model().set_layout(::ov::Layout(*explicit_in_model_layout)); } else if (m_model->input(input_name).get_shape().size() == 4u) { - // NB: Back compatibility with IR's without any layout information. - // Note that default is only applicable for 4D inputs in order to - // support auto resize for image use cases. - GAPI_LOG_WARNING(NULL, "Failed to find layout for input layer \"" - << input_name << "\" - NCHW is set by default"); - const std::string default_layout = "NCHW"; - input_info.model().set_layout(::ov::Layout(default_layout)); - m_input_model_layout.emplace(input_name, default_layout); + const auto& input_layout = ::ov::layout::get_layout(m_model->input(input_name)); + if (!input_layout.empty()) { + GAPI_LOG_INFO(NULL, "Model input layout " << input_name << " found: " << input_layout.to_string() << "."); + } else { + // NB: Back compatibility with IR's without any layout information. + // Note that default is only applicable for 4D inputs in order to + // support auto resize for image use cases. + GAPI_LOG_WARNING(NULL, "Failed to find layout for input layer \"" + << input_name << "\" - NCHW is set by default"); + const std::string default_layout = "NCHW"; + input_info.model().set_layout(::ov::Layout(default_layout)); + m_input_model_layout.emplace(input_name, default_layout); + } } const auto explicit_in_tensor_layout = lookUp(m_input_tensor_layout, input_name); if (explicit_in_tensor_layout) { @@ -990,6 +1004,11 @@ struct Infer: public cv::detail::KernelTag { reqPool.getIdleRequest()->execute( IInferExecutor::Task { [ctx](::ov::InferRequest &infer_request) { + // NB: No need to populate model inputs with data + // if it's inference only mode. + if (ctx->getOptions().inference_only) { + return; + } for (auto i : ade::util::iota(ctx->uu.params.num_in)) { const auto& input_name = ctx->uu.params.input_names[i]; auto input_tensor = infer_request.get_tensor(input_name); @@ -1069,6 +1088,10 @@ struct InferROI: public cv::detail::KernelTag { static void run(std::shared_ptr ctx, cv::gimpl::ov::RequestPool &reqPool) { using namespace std::placeholders; + if (ctx->getOptions().inference_only) { + cv::util::throw_error( + std::logic_error("OV Backend: Inference only mode is not supported for InferROI!")); + } reqPool.getIdleRequest()->execute( IInferExecutor::Task { [ctx](::ov::InferRequest &infer_request) { @@ -1141,6 +1164,10 @@ struct InferList: public cv::detail::KernelTag { static void run(std::shared_ptr ctx, cv::gimpl::ov::RequestPool &reqPool) { + if (ctx->getOptions().inference_only) { + cv::util::throw_error( + std::logic_error("OV Backend: Inference only mode is not supported for InferList!")); + } const auto& in_roi_vec = ctx->inArg(0u).rref(); // NB: In case there is no input data need to post output anyway if (in_roi_vec.empty()) { @@ -1257,6 +1284,10 @@ struct InferList2: public cv::detail::KernelTag { static void run(std::shared_ptr ctx, cv::gimpl::ov::RequestPool &reqPool) { + if (ctx->getOptions().inference_only) { + cv::util::throw_error( + std::logic_error("OV Backend: Inference only mode is not supported for InferList2!")); + } GAPI_Assert(ctx->inArgs().size() > 1u && "This operation must have at least two arguments"); // NB: This blob will be used to make roi from its, so @@ -1348,9 +1379,9 @@ class GOVBackendImpl final: public cv::gapi::GBackend::Priv { } virtual EPtr compile(const ade::Graph &graph, - const cv::GCompileArgs &, + const cv::GCompileArgs &compileArgs, const std::vector &nodes) const override { - return EPtr{new cv::gimpl::ov::GOVExecutable(graph, nodes)}; + return EPtr{new cv::gimpl::ov::GOVExecutable(graph, compileArgs, nodes)}; } virtual cv::GKernelPackage auxiliaryKernels() const override { @@ -1391,9 +1422,12 @@ createInferRequests(::ov::CompiledModel &compiled_model, // GOVExecutable implementation ////////////////////////////////////////////// cv::gimpl::ov::GOVExecutable::GOVExecutable(const ade::Graph &g, + const cv::GCompileArgs &compileArgs, const std::vector &nodes) : m_g(g), m_gm(m_g) { + m_options.inference_only = + cv::gapi::getCompileArg(compileArgs).has_value(); // FIXME: Currently this backend is capable to run a single inference node only. // Need to extend our island fusion with merge/not-to-merge decision making parametrization GConstGOVModel ovm(g); @@ -1471,7 +1505,7 @@ void cv::gimpl::ov::GOVExecutable::run(cv::gimpl::GIslandExecutable::IInput &in const auto &op = m_gm.metadata(this_nh).get(); auto ctx = std::make_shared(uu, out, op.args, op.outs, - std::move(stub_meta), std::move(input_objs), std::move(output_objs)); + std::move(stub_meta), std::move(input_objs), std::move(output_objs), m_options); const auto &kk = giem.metadata(this_nh).get(); diff --git a/modules/gapi/src/backends/ov/govbackend.hpp b/modules/gapi/src/backends/ov/govbackend.hpp index 0ac858dc52..ff9793afad 100644 --- a/modules/gapi/src/backends/ov/govbackend.hpp +++ b/modules/gapi/src/backends/ov/govbackend.hpp @@ -26,6 +26,12 @@ struct OVCompiled { class RequestPool; +struct Options { + // Only performs inference of the model + // without i/o data transfer if enabled. + bool inference_only = false; +}; + class GOVExecutable final: public GIslandExecutable { const ade::Graph &m_g; @@ -42,8 +48,12 @@ class GOVExecutable final: public GIslandExecutable // To manage multiple async requests std::unique_ptr m_reqPool; + // To manage additional execution options + Options m_options; + public: GOVExecutable(const ade::Graph &graph, + const cv::GCompileArgs &compileArgs, const std::vector &nodes); virtual inline bool canReshape() const override { return false; } diff --git a/modules/gapi/test/cpu/gapi_ot_tests_cpu.cpp b/modules/gapi/test/cpu/gapi_ot_tests_cpu.cpp new file mode 100644 index 0000000000..c02185e621 --- /dev/null +++ b/modules/gapi/test/cpu/gapi_ot_tests_cpu.cpp @@ -0,0 +1,287 @@ +// This file is part of OpenCV project. +// It is subject to the license terms in the LICENSE file found in the top-level directory +// of this distribution and at http://opencv.org/license.html. +// +// Copyright (C) 2023 Intel Corporation + + +#include "../test_precomp.hpp" + +#include +#include + +#include "opencv2/gapi/streaming/meta.hpp" +#include "opencv2/gapi/streaming/cap.hpp" + +namespace { +cv::gapi::ot::TrackingStatus from_string(const std::string& status) { + if (status == "NEW") { + return cv::gapi::ot::TrackingStatus::NEW; + } + else if (status == "TRACKED") { + return cv::gapi::ot::TrackingStatus::TRACKED; + } + else if (status == "LOST") { + return cv::gapi::ot::TrackingStatus::LOST; + } + + throw std::runtime_error("String representation for cv::gapi::ot::TrackingStatus: \"" + + status + "\" contains incorrect value!"); +} +} // anonymous namespace + +namespace opencv_test { +struct FrameDetections { + std::size_t frame_no{}; + std::vector> boxes; + std::vector> box_ids; + FrameDetections() {} + FrameDetections(std::size_t in_frame_no, const std::vector>& in_boxes, + const std::vector>& in_box_ids) : + frame_no(in_frame_no), + boxes(in_boxes), + box_ids(in_box_ids) {} +}; + +struct TrackerReference { + std::size_t frame_no{}; + std::vector> tracked_boxes; + std::vector> tracked_box_ids; + std::vector> tracking_ids; + std::vector> tracking_statuses; + TrackerReference() {} + TrackerReference(std::size_t in_frame_no, + const std::vector>& in_tracked_boxes, + const std::vector>& in_tracked_box_ids, + const std::vector>& in_tracking_ids, + const std::vector>& + in_tracking_statuses) : + frame_no(in_frame_no), + tracked_boxes(in_tracked_boxes), + tracked_box_ids(in_tracked_box_ids), + tracking_ids(in_tracking_ids), + tracking_statuses(in_tracking_statuses) {} +}; + +struct FrameDetectionsParams { + FrameDetections value; +}; + +struct TrackerReferenceParams { + TrackerReference value; +}; +} // namespace opencv_test + +namespace cv { + namespace detail { + template<> struct CompileArgTag { + static const char* tag() { + return "org.opencv.test.frame_detections_params"; + } + }; + } // namespace detail + + namespace detail { + template<> struct CompileArgTag { + static const char* tag() { + return "org.opencv.test.tracker_reference_params"; + } + }; + } // namespace detail +} // namespace cv + +namespace opencv_test { +G_API_OP(CvVideo768x576_Detect, , cv::GArray>(cv::GMat)>, + "test.custom.cv_video_768x576_detect") { + static std::tuple outMeta(cv::GMatDesc) { + return std::make_tuple(cv::empty_array_desc(), cv::empty_array_desc()); + } +}; + +GAPI_OCV_KERNEL_ST(OCV_CvVideo768x576_Detect, CvVideo768x576_Detect, FrameDetections) { + static void setup(cv::GMatDesc, + std::shared_ptr &state, + const cv::GCompileArgs &compileArgs) { + auto params = cv::gapi::getCompileArg(compileArgs) + .value_or(opencv_test::FrameDetectionsParams{ }); + state = std::make_shared(params.value); + } + + static void run(const cv::Mat&, + std::vector &out_boxes, + std::vector &out_box_ids, + FrameDetections &state) { + if (state.frame_no < state.boxes.size()) { + out_boxes = state.boxes[state.frame_no]; + out_box_ids = state.box_ids[state.frame_no]; + ++state.frame_no; + } + } +}; + +G_API_OP(CheckTrackerResults, (cv::GArray, cv::GArray, + cv::GArray, cv::GArray)>, + "test.custom.check_tracker_results") { + static cv::GOpaqueDesc outMeta(cv::GArrayDesc, cv::GArrayDesc, cv::GArrayDesc, cv::GArrayDesc) { + return cv::empty_gopaque_desc(); + } +}; + +GAPI_OCV_KERNEL_ST(OCVCheckTrackerResults, CheckTrackerResults, TrackerReference) { + static void setup(cv::GArrayDesc, cv::GArrayDesc, + cv::GArrayDesc, cv::GArrayDesc, + std::shared_ptr &state, + const cv::GCompileArgs &compileArgs) { + auto params = cv::gapi::getCompileArg(compileArgs) + .value_or(opencv_test::TrackerReferenceParams{ }); + state = std::make_shared(params.value); + } + + static void run(const std::vector &in_tr_rcts, + const std::vector &in_det_ids, + const std::vector &in_tr_ids, + const std::vector &in_tr_statuses, + bool& success, + TrackerReference& state) { + + if (state.frame_no < state.tracked_boxes.size()) { + auto reference_boxes = state.tracked_boxes[state.frame_no]; + auto reference_box_ids = state.tracked_box_ids[state.frame_no]; + auto reference_tr_ids = state.tracking_ids[state.frame_no]; + auto reference_tr_statuses = state.tracking_statuses[state.frame_no]; + + success = true; + GAPI_Assert(in_tr_rcts.size() == reference_boxes.size()); + GAPI_Assert(in_det_ids.size() == reference_box_ids.size()); + GAPI_Assert(in_tr_ids.size() == reference_tr_ids.size()); + GAPI_Assert(in_tr_statuses.size() == reference_tr_statuses.size()); + for (uint32_t i = 0; (i < in_tr_rcts.size() && success); ++i) { + const cv::Rect& reference_rc = reference_boxes[i]; + const cv::Rect& in_rc = in_tr_rcts[i]; + + success &= (reference_rc == in_rc); + success &= (reference_box_ids[i] == in_det_ids[i]); + success &= (reference_tr_ids[i] == in_tr_ids[i]); + success &= (reference_tr_statuses[i] == in_tr_statuses[i]); + } + + ++state.frame_no; + } + else { + success = true; + } + } +}; + +TEST(VASObjectTracker, PipelineTest) +{ + constexpr int32_t frames_to_handle = 30; + std::string pathToVideo = opencv_test::findDataFile("cv/video/768x576.avi"); + + std::vector> input_boxes(frames_to_handle); + std::vector> input_det_ids(frames_to_handle); + + std::string path_to_boxes = opencv_test::findDataFile("cv/video/vas_object_tracking/detections_30_frames.yml"); + + cv::FileStorage fs_input_boxes(path_to_boxes, cv::FileStorage::READ); + cv::FileNode fn_input_boxes = fs_input_boxes.root(); + for (auto it = fn_input_boxes.begin(); it != fn_input_boxes.end(); ++it) { + cv::FileNode fn_frame = *it; + std::string frame_name = fn_frame.name(); + int frame_no = std::stoi(frame_name.substr(frame_name.find("_") + 1)); + + for (auto fit = fn_frame.begin(); fit != fn_frame.end(); ++fit) { + cv::FileNode fn_box = *fit; + + cv::Rect box((int)fn_box["x"], (int)fn_box["y"], + (int)fn_box["width"], (int)fn_box["height"]); + input_boxes[frame_no].push_back(box); + input_det_ids[frame_no].push_back(fn_box["id"]); + } + } + + std::vector> reference_trackings(frames_to_handle); + std::vector> reference_trackings_det_ids(frames_to_handle); + std::vector> reference_trackings_tr_ids(frames_to_handle); + std::vector> reference_trackings_tr_statuses(frames_to_handle); + + std::string path_to_trackings = opencv_test::findDataFile("cv/video/vas_object_tracking/trackings_30_frames.yml"); + + cv::FileStorage fs_reference_trackings(path_to_trackings, cv::FileStorage::READ); + cv::FileNode fn_reference_trackings = fs_reference_trackings.root(); + for (auto it = fn_reference_trackings.begin(); it != fn_reference_trackings.end(); ++it) { + cv::FileNode fn_frame = *it; + std::string frame_name = fn_frame.name(); + int frame_no = std::stoi(frame_name.substr(frame_name.find("_") + 1)); + + for (auto fit = fn_frame.begin(); fit != fn_frame.end(); ++fit) { + cv::FileNode fn_tracked_box = *fit; + + cv::Rect tracked_box((int)fn_tracked_box["x"], (int)fn_tracked_box["y"], + (int)fn_tracked_box["width"], (int)fn_tracked_box["height"]); + reference_trackings[frame_no].push_back(tracked_box); + reference_trackings_det_ids[frame_no].push_back(fn_tracked_box["id"]); + reference_trackings_tr_ids[frame_no].push_back(int(fn_tracked_box["tracking_id"])); + reference_trackings_tr_statuses[frame_no].push_back( + from_string(fn_tracked_box["tracking_status"])); + } + } + + cv::GMat in; + + cv::GArray detections; + cv::GArray det_ids; + std::tie(detections, det_ids) = CvVideo768x576_Detect::on(in); + + constexpr float delta_time = 0.055f; + cv::GArray tracking_rects; + cv::GArray tracking_det_ids; + cv::GArray tracking_ids; + cv::GArray tracking_statuses; + std::tie(tracking_rects, tracking_det_ids, tracking_ids, tracking_statuses) = + cv::gapi::ot::track(in, detections, det_ids, delta_time); + + cv::GOpaque check_result = + CheckTrackerResults::on(tracking_rects, tracking_det_ids, tracking_ids, tracking_statuses); + + cv::GComputation ccomp(cv::GIn(in), cv::GOut(check_result)); + + + opencv_test::FrameDetections fds { 0, input_boxes, input_det_ids }; + opencv_test::TrackerReference tr { 0, reference_trackings, + reference_trackings_det_ids, + reference_trackings_tr_ids, + reference_trackings_tr_statuses }; + + // Graph compilation for streaming mode: + auto compiled = + ccomp.compileStreaming(cv::compile_args( + cv::gapi::combine(cv::gapi::kernels(), + cv::gapi::ot::cpu::kernels()), + opencv_test::FrameDetectionsParams{ fds }, + opencv_test::TrackerReferenceParams{ tr })); + + EXPECT_TRUE(compiled); + EXPECT_FALSE(compiled.running()); + + compiled.setSource(pathToVideo); + + // Start of streaming: + compiled.start(); + EXPECT_TRUE(compiled.running()); + + // Streaming: + bool success; + + std::size_t counter { }, limit { 30 }; + while(compiled.pull(cv::gout(success)) && (counter < limit)) { + ++counter; + } + + compiled.stop(); + + EXPECT_TRUE(success); + EXPECT_FALSE(compiled.running()); +} +} // namespace opencv_test diff --git a/modules/highgui/src/window_QT.cpp b/modules/highgui/src/window_QT.cpp index 1f0f432981..142ab00827 100644 --- a/modules/highgui/src/window_QT.cpp +++ b/modules/highgui/src/window_QT.cpp @@ -2481,6 +2481,10 @@ DefaultViewPort::DefaultViewPort(CvWindow* arg, int arg2) : QGraphicsView(arg), setInteractive(false); setMouseTracking(true); //receive mouse event everytime + + // #13657 Tab key disables arrow keys + // #20215 QT backend: cv::waitKey() and cv::waitKeyEx() do not capture arrow keys once you click on the image or press TAB + setFocusPolicy(Qt::NoFocus); } diff --git a/modules/imgcodecs/CMakeLists.txt b/modules/imgcodecs/CMakeLists.txt index 8183837c43..1468d4d73b 100644 --- a/modules/imgcodecs/CMakeLists.txt +++ b/modules/imgcodecs/CMakeLists.txt @@ -137,11 +137,11 @@ if(APPLE OR APPLE_FRAMEWORK) list(APPEND imgcodecs_srcs ${CMAKE_CURRENT_LIST_DIR}/src/apple_conversions.h) list(APPEND imgcodecs_srcs ${CMAKE_CURRENT_LIST_DIR}/src/apple_conversions.mm) endif() -if(IOS) +if(IOS OR XROS) list(APPEND imgcodecs_srcs ${CMAKE_CURRENT_LIST_DIR}/src/ios_conversions.mm) list(APPEND IMGCODECS_LIBRARIES "-framework UIKit") endif() -if(APPLE AND (NOT IOS)) +if(APPLE AND (NOT IOS) AND (NOT XROS)) list(APPEND imgcodecs_srcs ${CMAKE_CURRENT_LIST_DIR}/src/macosx_conversions.mm) list(APPEND IMGCODECS_LIBRARIES "-framework AppKit") endif() diff --git a/modules/imgcodecs/misc/objc/gen_dict.json b/modules/imgcodecs/misc/objc/gen_dict.json new file mode 100644 index 0000000000..cfcd5889e7 --- /dev/null +++ b/modules/imgcodecs/misc/objc/gen_dict.json @@ -0,0 +1,5 @@ +{ + "SourceMap" : { + "visionos" : "ios" + } +} diff --git a/modules/imgcodecs/src/grfmt_png.cpp b/modules/imgcodecs/src/grfmt_png.cpp index 388a3fcaf4..aca73bd000 100644 --- a/modules/imgcodecs/src/grfmt_png.cpp +++ b/modules/imgcodecs/src/grfmt_png.cpp @@ -58,11 +58,7 @@ # define _FILE_OFFSET_BITS 0 #endif -#ifdef HAVE_LIBPNG_PNG_H -#include -#else #include -#endif #include #include "grfmt_png.hpp" diff --git a/modules/imgcodecs/src/grfmt_spng.cpp b/modules/imgcodecs/src/grfmt_spng.cpp index e3c61164e4..fa15bd46c7 100644 --- a/modules/imgcodecs/src/grfmt_spng.cpp +++ b/modules/imgcodecs/src/grfmt_spng.cpp @@ -521,7 +521,6 @@ int SPngEncoder::writeDataToBuf(void *ctx, void *user, void *dst_src, size_t len bool SPngEncoder::write(const Mat &img, const std::vector ¶ms) { - int fmt; spng_ctx *ctx = spng_ctx_new(SPNG_CTX_ENCODER); FILE *volatile f = 0; int width = img.cols, height = img.rows; @@ -558,11 +557,10 @@ bool SPngEncoder::write(const Mat &img, const std::vector ¶ms) isBilevel = params[i + 1] != 0; } } - fmt = channels == 1 ? SPNG_COLOR_TYPE_GRAYSCALE : channels == 3 ? SPNG_COLOR_TYPE_TRUECOLOR - : SPNG_COLOR_TYPE_TRUECOLOR_ALPHA; ihdr.bit_depth = depth == CV_8U ? isBilevel ? 1 : 8 : 16; - ihdr.color_type = fmt; + ihdr.color_type = (uint8_t)(channels == 1 ? SPNG_COLOR_TYPE_GRAYSCALE : channels == 3 ? SPNG_COLOR_TYPE_TRUECOLOR + : SPNG_COLOR_TYPE_TRUECOLOR_ALPHA); ihdr.interlace_method = SPNG_INTERLACE_NONE; ihdr.filter_method = SPNG_FILTER_NONE; ihdr.compression_method = 0; @@ -597,7 +595,7 @@ bool SPngEncoder::write(const Mat &img, const std::vector ¶ms) ret = spng_encode_image(ctx, nullptr, 0, SPNG_FMT_PNG, SPNG_ENCODE_PROGRESSIVE); if (channels > 1) { - int error; + int error = SPNG_OK; if (ret == SPNG_OK) { if (depth == CV_16U) @@ -659,7 +657,7 @@ bool SPngEncoder::write(const Mat &img, const std::vector ¶ms) } else { - int error; + int error = SPNG_OK; for (int y = 0; y < height; y++) { error = spng_encode_row(ctx, img.data + y * img.step, width * channels * (depth == CV_16U ? 2 : 1)); @@ -711,17 +709,17 @@ void spngCvt_BGRA2Gray_8u_C4C1R(const uchar *bgra, int rgba_step, uchar *gray, int gray_step, cv::Size size, int _swap_rb) { - int i; for (; size.height--; gray += gray_step) { double cBGR0 = 0.1140441895; + double cBGR1 = 0.5869750977; double cBGR2 = 0.2989807129; + if (_swap_rb) std::swap(cBGR0, cBGR2); - for (i = 0; i < size.width; i++, bgra += 4) + for (int i = 0; i < size.width; i++, bgra += 4) { - int t = cBGR0 * bgra[0] + 0.5869750977 * bgra[1] + cBGR2 * bgra[2]; - gray[i] = (uchar)t; + gray[i] = cv::saturate_cast(cBGR0 * bgra[0] + cBGR1 * bgra[1] + cBGR2 * bgra[2]); } bgra += rgba_step - size.width * 4; @@ -732,17 +730,17 @@ void spngCvt_BGRA2Gray_16u_CnC1R(const ushort *bgr, int bgr_step, ushort *gray, int gray_step, cv::Size size, int ncn, int _swap_rb) { - int i; for (; size.height--; gray += gray_step) { double cBGR0 = 0.1140441895; + double cBGR1 = 0.5869750977; double cBGR2 = 0.2989807129; + if (_swap_rb) std::swap(cBGR0, cBGR2); - for (i = 0; i < size.width; i++, bgr += ncn) + for (int i = 0; i < size.width; i++, bgr += ncn) { - int t = cBGR0 * bgr[0] + 0.5869750977 * bgr[1] + cBGR2 * bgr[2]; - gray[i] = (ushort)t; + gray[i] = (ushort)(cBGR0 * bgr[0] + cBGR1 * bgr[1] + cBGR2 * bgr[2]); } bgr += bgr_step - size.width * ncn; diff --git a/modules/imgproc/include/opencv2/imgproc.hpp b/modules/imgproc/include/opencv2/imgproc.hpp index d0b63ad370..26796e3c27 100644 --- a/modules/imgproc/include/opencv2/imgproc.hpp +++ b/modules/imgproc/include/opencv2/imgproc.hpp @@ -2281,7 +2281,7 @@ too large, some circles may be missed. @param param1 First method-specific parameter. In case of #HOUGH_GRADIENT and #HOUGH_GRADIENT_ALT, it is the higher threshold of the two passed to the Canny edge detector (the lower one is twice smaller). Note that #HOUGH_GRADIENT_ALT uses #Scharr algorithm to compute image derivatives, so the threshold value -shough normally be higher, such as 300 or normally exposed and contrasty images. +should normally be higher, such as 300 or normally exposed and contrasty images. @param param2 Second method-specific parameter. In case of #HOUGH_GRADIENT, it is the accumulator threshold for the circle centers at the detection stage. The smaller it is, the more false circles may be detected. Circles, corresponding to the larger accumulator values, will be diff --git a/modules/imgproc/src/color.hpp b/modules/imgproc/src/color.hpp index 7751d823b1..6ebca26a2c 100644 --- a/modules/imgproc/src/color.hpp +++ b/modules/imgproc/src/color.hpp @@ -277,7 +277,7 @@ struct OclHelper int pxPerWIy = dev.isIntel() && (dev.type() & ocl::Device::TYPE_GPU) ? 4 : 1; int pxPerWIx = 1; - cv::String baseOptions = format("-D depth=%d -D scn=%d -D PIX_PER_WI_Y=%d ", + cv::String baseOptions = format("-D SRC_DEPTH=%d -D SCN=%d -D PIX_PER_WI_Y=%d ", src.depth(), src.channels(), pxPerWIy); switch (sizePolicy) diff --git a/modules/imgproc/src/color_hsv.dispatch.cpp b/modules/imgproc/src/color_hsv.dispatch.cpp index f1678f5deb..8639784927 100644 --- a/modules/imgproc/src/color_hsv.dispatch.cpp +++ b/modules/imgproc/src/color_hsv.dispatch.cpp @@ -219,7 +219,7 @@ bool oclCvtColorHSV2BGR( InputArray _src, OutputArray _dst, int dcn, int bidx, b int hrange = _src.depth() == CV_32F ? 360 : (!full ? 180 : 255); if(!h.createKernel("HSV2RGB", ocl::imgproc::color_hsv_oclsrc, - format("-D dcn=%d -D bidx=%d -D hrange=%d -D hscale=%ff", dcn, bidx, hrange, 6.f/hrange))) + format("-D DCN=%d -D BIDX=%d -D HRANGE=%d -D HSCALE=%ff", dcn, bidx, hrange, 6.f/hrange))) { return false; } @@ -234,7 +234,7 @@ bool oclCvtColorHLS2BGR( InputArray _src, OutputArray _dst, int dcn, int bidx, b int hrange = _src.depth() == CV_32F ? 360 : (!full ? 180 : 255); if(!h.createKernel("HLS2RGB", ocl::imgproc::color_hsv_oclsrc, - format("-D dcn=%d -D bidx=%d -D hrange=%d -D hscale=%ff", dcn, bidx, hrange, 6.f/hrange))) + format("-D DCN=%d -D BIDX=%d -D HRANGE=%d -D HSCALE=%ff", dcn, bidx, hrange, 6.f/hrange))) { return false; } @@ -249,7 +249,7 @@ bool oclCvtColorBGR2HLS( InputArray _src, OutputArray _dst, int bidx, bool full float hscale = (_src.depth() == CV_32F ? 360.f : (!full ? 180.f : 256.f))/360.f; if(!h.createKernel("RGB2HLS", ocl::imgproc::color_hsv_oclsrc, - format("-D hscale=%ff -D bidx=%d -D dcn=3", hscale, bidx))) + format("-D HSCALE=%ff -D BIDX=%d -D DCN=3", hscale, bidx))) { return false; } @@ -264,8 +264,8 @@ bool oclCvtColorBGR2HSV( InputArray _src, OutputArray _dst, int bidx, bool full int hrange = _src.depth() == CV_32F ? 360 : (!full ? 180 : 256); cv::String options = (_src.depth() == CV_8U ? - format("-D hrange=%d -D bidx=%d -D dcn=3", hrange, bidx) : - format("-D hscale=%ff -D bidx=%d -D dcn=3", hrange*(1.f/360.f), bidx)); + format("-D HRANGE=%d -D BIDX=%d -D DCN=3", hrange, bidx) : + format("-D HSCALE=%ff -D BIDX=%d -D DCN=3", hrange*(1.f/360.f), bidx)); if(!h.createKernel("RGB2HSV", ocl::imgproc::color_hsv_oclsrc, options)) { diff --git a/modules/imgproc/src/color_lab.cpp b/modules/imgproc/src/color_lab.cpp index 6a8a0ea7f9..fdf797808a 100644 --- a/modules/imgproc/src/color_lab.cpp +++ b/modules/imgproc/src/color_lab.cpp @@ -1223,119 +1223,120 @@ static LABLUVLUT_s16_t initLUTforLABLUVs16(const softfloat & un, const softfloat } -static void initLabTabs() +static bool createLabTabs() { - static bool initialized = false; - if(!initialized) + softfloat f[LAB_CBRT_TAB_SIZE+1], g[GAMMA_TAB_SIZE+1], ig[GAMMA_TAB_SIZE+1]; + softfloat scale = softfloat::one()/softfloat(LabCbrtTabScale); + int i; + for(i = 0; i <= LAB_CBRT_TAB_SIZE; i++) { - softfloat f[LAB_CBRT_TAB_SIZE+1], g[GAMMA_TAB_SIZE+1], ig[GAMMA_TAB_SIZE+1]; - softfloat scale = softfloat::one()/softfloat(LabCbrtTabScale); - int i; - for(i = 0; i <= LAB_CBRT_TAB_SIZE; i++) - { - softfloat x = scale*softfloat(i); - f[i] = x < lthresh ? mulAdd(x, lscale, lbias) : cbrt(x); - } - LabCbrtTab = splineBuild(f, LAB_CBRT_TAB_SIZE); + softfloat x = scale*softfloat(i); + f[i] = x < lthresh ? mulAdd(x, lscale, lbias) : cbrt(x); + } + LabCbrtTab = splineBuild(f, LAB_CBRT_TAB_SIZE); - scale = softfloat::one()/softfloat(GammaTabScale); - for(i = 0; i <= GAMMA_TAB_SIZE; i++) + scale = softfloat::one()/softfloat(GammaTabScale); + for(i = 0; i <= GAMMA_TAB_SIZE; i++) + { + softfloat x = scale*softfloat(i); + g[i] = applyGamma(x); + ig[i] = applyInvGamma(x); + } + + sRGBGammaTab = splineBuild(g, GAMMA_TAB_SIZE); + sRGBInvGammaTab = splineBuild(ig, GAMMA_TAB_SIZE); + + static const softfloat intScale(255*(1 << gamma_shift)); + for(i = 0; i < 256; i++) + { + softfloat x = softfloat(i)/f255; + sRGBGammaTab_b[i] = (ushort)(cvRound(intScale*applyGamma(x))); + linearGammaTab_b[i] = (ushort)(i*(1 << gamma_shift)); + } + static const softfloat invScale = softfloat::one()/softfloat((int)INV_GAMMA_TAB_SIZE); + for(i = 0; i < INV_GAMMA_TAB_SIZE; i++) + { + softfloat x = invScale*softfloat(i); + sRGBInvGammaTab_b[i] = (ushort)(cvRound(f255*applyInvGamma(x))); + linearInvGammaTab_b[i] = (ushort)(cvTrunc(f255*x)); + } + + static const softfloat cbTabScale(softfloat::one()/(f255*(1 << gamma_shift))); + static const softfloat lshift2(1 << lab_shift2); + for(i = 0; i < LAB_CBRT_TAB_SIZE_B; i++) + { + softfloat x = cbTabScale*softfloat(i); + LabCbrtTab_b[i] = (ushort)(cvRound(lshift2 * (x < lthresh ? mulAdd(x, lscale, lbias) : cbrt(x)))); + } + + //Lookup table for L to y and ify calculations + for(i = 0; i < 256; i++) + { + int y, ify; + //8 * 255.0 / 100.0 == 20.4 + if( i <= 20) { - softfloat x = scale*softfloat(i); - g[i] = applyGamma(x); - ig[i] = applyInvGamma(x); + //yy = li / 903.3f; + //y = L*100/903.3f; 903.3f = (29/3)^3, 255 = 17*3*5 + y = cvRound(softfloat(i*LUT_BASE*20*9)/softfloat(17*29*29*29)); + //fy = 7.787f * yy + 16.0f / 116.0f; 7.787f = (29/3)^3/(29*4) + ify = cvRound(softfloat((int)LUT_BASE)*(softfloat(16)/softfloat(116) + softfloat(i*5)/softfloat(3*17*29))); + } + else + { + //fy = (li + 16.0f) / 116.0f; + softfloat fy = (softfloat(i*100*LUT_BASE)/softfloat(255*116) + + softfloat(16*LUT_BASE)/softfloat(116)); + ify = cvRound(fy); + //yy = fy * fy * fy; + y = cvRound(fy*fy*fy/softfloat(LUT_BASE*LUT_BASE)); } - sRGBGammaTab = splineBuild(g, GAMMA_TAB_SIZE); - sRGBInvGammaTab = splineBuild(ig, GAMMA_TAB_SIZE); + LabToYF_b[i*2 ] = (ushort)y; // 0 <= y <= BASE + LabToYF_b[i*2+1] = (ushort)ify; // 2260 <= ify <= BASE + } - static const softfloat intScale(255*(1 << gamma_shift)); - for(i = 0; i < 256; i++) - { - softfloat x = softfloat(i)/f255; - sRGBGammaTab_b[i] = (ushort)(cvRound(intScale*applyGamma(x))); - linearGammaTab_b[i] = (ushort)(i*(1 << gamma_shift)); - } - static const softfloat invScale = softfloat::one()/softfloat((int)INV_GAMMA_TAB_SIZE); - for(i = 0; i < INV_GAMMA_TAB_SIZE; i++) - { - softfloat x = invScale*softfloat(i); - sRGBInvGammaTab_b[i] = (ushort)(cvRound(f255*applyInvGamma(x))); - linearInvGammaTab_b[i] = (ushort)(cvTrunc(f255*x)); - } + //Lookup table for a,b to x,z conversion + abToXZ_b = initLUTforABXZ(); - static const softfloat cbTabScale(softfloat::one()/(f255*(1 << gamma_shift))); - static const softfloat lshift2(1 << lab_shift2); - for(i = 0; i < LAB_CBRT_TAB_SIZE_B; i++) - { - softfloat x = cbTabScale*softfloat(i); - LabCbrtTab_b[i] = (ushort)(cvRound(lshift2 * (x < lthresh ? mulAdd(x, lscale, lbias) : cbrt(x)))); - } + softfloat dd = D65[0] + D65[1]*softdouble(15) + D65[2]*softdouble(3); + dd = softfloat::one()/max(dd, softfloat::eps()); + softfloat un = dd*softfloat(13*4)*D65[0]; + softfloat vn = dd*softfloat(13*9)*D65[1]; - //Lookup table for L to y and ify calculations - for(i = 0; i < 256; i++) + //Luv LUT + LUVLUT = initLUTforLUV(un, vn); + + //try to suppress warning + static const bool calcLUT = enableRGB2LabInterpolation || enableRGB2LuvInterpolation; + if(calcLUT) + { + + LABLUVLUTs16 = initLUTforLABLUVs16(un, vn); + + for(int16_t p = 0; p < TRILINEAR_BASE; p++) { - int y, ify; - //8 * 255.0 / 100.0 == 20.4 - if( i <= 20) + int16_t pp = TRILINEAR_BASE - p; + for(int16_t q = 0; q < TRILINEAR_BASE; q++) { - //yy = li / 903.3f; - //y = L*100/903.3f; 903.3f = (29/3)^3, 255 = 17*3*5 - y = cvRound(softfloat(i*LUT_BASE*20*9)/softfloat(17*29*29*29)); - //fy = 7.787f * yy + 16.0f / 116.0f; 7.787f = (29/3)^3/(29*4) - ify = cvRound(softfloat((int)LUT_BASE)*(softfloat(16)/softfloat(116) + softfloat(i*5)/softfloat(3*17*29))); - } - else - { - //fy = (li + 16.0f) / 116.0f; - softfloat fy = (softfloat(i*100*LUT_BASE)/softfloat(255*116) + - softfloat(16*LUT_BASE)/softfloat(116)); - ify = cvRound(fy); - //yy = fy * fy * fy; - y = cvRound(fy*fy*fy/softfloat(LUT_BASE*LUT_BASE)); - } - - LabToYF_b[i*2 ] = (ushort)y; // 0 <= y <= BASE - LabToYF_b[i*2+1] = (ushort)ify; // 2260 <= ify <= BASE - } - - //Lookup table for a,b to x,z conversion - abToXZ_b = initLUTforABXZ(); - - softfloat dd = D65[0] + D65[1]*softdouble(15) + D65[2]*softdouble(3); - dd = softfloat::one()/max(dd, softfloat::eps()); - softfloat un = dd*softfloat(13*4)*D65[0]; - softfloat vn = dd*softfloat(13*9)*D65[1]; - - //Luv LUT - LUVLUT = initLUTforLUV(un, vn); - - //try to suppress warning - static const bool calcLUT = enableRGB2LabInterpolation || enableRGB2LuvInterpolation; - if(calcLUT) - { - - LABLUVLUTs16 = initLUTforLABLUVs16(un, vn); - - for(int16_t p = 0; p < TRILINEAR_BASE; p++) - { - int16_t pp = TRILINEAR_BASE - p; - for(int16_t q = 0; q < TRILINEAR_BASE; q++) + int16_t qq = TRILINEAR_BASE - q; + for(int16_t r = 0; r < TRILINEAR_BASE; r++) { - int16_t qq = TRILINEAR_BASE - q; - for(int16_t r = 0; r < TRILINEAR_BASE; r++) - { - int16_t rr = TRILINEAR_BASE - r; - int16_t* w = &trilinearLUT[8*p + 8*TRILINEAR_BASE*q + 8*TRILINEAR_BASE*TRILINEAR_BASE*r]; - w[0] = pp * qq * rr; w[1] = pp * qq * r ; w[2] = pp * q * rr; w[3] = pp * q * r ; - w[4] = p * qq * rr; w[5] = p * qq * r ; w[6] = p * q * rr; w[7] = p * q * r ; - } + int16_t rr = TRILINEAR_BASE - r; + int16_t* w = &trilinearLUT[8*p + 8*TRILINEAR_BASE*q + 8*TRILINEAR_BASE*TRILINEAR_BASE*r]; + w[0] = pp * qq * rr; w[1] = pp * qq * r ; w[2] = pp * q * rr; w[3] = pp * q * r ; + w[4] = p * qq * rr; w[5] = p * qq * r ; w[6] = p * q * rr; w[7] = p * q * r ; } } } - - initialized = true; } + return true; +} + +static bool initLabTabs() +{ + static bool initialized = createLabTabs(); + return initialized; } @@ -4423,7 +4424,7 @@ bool oclCvtColorBGR2Luv( InputArray _src, OutputArray _dst, int bidx, bool srgb) OclHelper< Set<3, 4>, Set<3>, Set > h(_src, _dst, 3); if(!h.createKernel("BGR2Luv", ocl::imgproc::color_lab_oclsrc, - format("-D dcn=3 -D bidx=%d%s", bidx, srgb ? " -D SRGB" : ""))) + format("-D DCN=3 -D BIDX=%d%s", bidx, srgb ? " -D SRGB" : ""))) { return false; } @@ -4491,7 +4492,7 @@ bool oclCvtColorBGR2Lab( InputArray _src, OutputArray _dst, int bidx, bool srgb OclHelper< Set<3, 4>, Set<3>, Set > h(_src, _dst, 3); if(!h.createKernel("BGR2Lab", ocl::imgproc::color_lab_oclsrc, - format("-D dcn=3 -D bidx=%d%s", bidx, srgb ? " -D SRGB" : ""))) + format("-D DCN=3 -D BIDX=%d%s", bidx, srgb ? " -D SRGB" : ""))) { return false; } @@ -4586,7 +4587,7 @@ bool oclCvtColorLab2BGR(InputArray _src, OutputArray _dst, int dcn, int bidx, bo OclHelper< Set<3>, Set<3, 4>, Set > h(_src, _dst, dcn); if(!h.createKernel("Lab2BGR", ocl::imgproc::color_lab_oclsrc, - format("-D dcn=%d -D bidx=%d%s", dcn, bidx, srgb ? " -D SRGB" : ""))) + format("-D DCN=%d -D BIDX=%d%s", dcn, bidx, srgb ? " -D SRGB" : ""))) { return false; } @@ -4637,7 +4638,7 @@ bool oclCvtColorLuv2BGR(InputArray _src, OutputArray _dst, int dcn, int bidx, bo OclHelper< Set<3>, Set<3, 4>, Set > h(_src, _dst, dcn); if(!h.createKernel("Luv2BGR", ocl::imgproc::color_lab_oclsrc, - format("-D dcn=%d -D bidx=%d%s", dcn, bidx, srgb ? " -D SRGB" : ""))) + format("-D DCN=%d -D BIDX=%d%s", dcn, bidx, srgb ? " -D SRGB" : ""))) { return false; } @@ -4691,7 +4692,7 @@ bool oclCvtColorBGR2XYZ( InputArray _src, OutputArray _dst, int bidx ) OclHelper< Set<3, 4>, Set<3>, Set > h(_src, _dst, 3); if(!h.createKernel("RGB2XYZ", ocl::imgproc::color_lab_oclsrc, - format("-D dcn=3 -D bidx=%d", bidx))) + format("-D DCN=3 -D BIDX=%d", bidx))) { return false; } @@ -4739,7 +4740,7 @@ bool oclCvtColorXYZ2BGR( InputArray _src, OutputArray _dst, int dcn, int bidx ) OclHelper< Set<3>, Set<3, 4>, Set > h(_src, _dst, dcn); if(!h.createKernel("XYZ2RGB", ocl::imgproc::color_lab_oclsrc, - format("-D dcn=%d -D bidx=%d", dcn, bidx))) + format("-D DCN=%d -D BIDX=%d", dcn, bidx))) { return false; } diff --git a/modules/imgproc/src/color_rgb.dispatch.cpp b/modules/imgproc/src/color_rgb.dispatch.cpp index ed2961f0fb..efe6c9d6cb 100644 --- a/modules/imgproc/src/color_rgb.dispatch.cpp +++ b/modules/imgproc/src/color_rgb.dispatch.cpp @@ -428,7 +428,7 @@ bool oclCvtColorBGR2BGR( InputArray _src, OutputArray _dst, int dcn, bool revers OclHelper< Set<3, 4>, Set<3, 4>, Set > h(_src, _dst, dcn); if(!h.createKernel("RGB", ocl::imgproc::color_rgb_oclsrc, - format("-D dcn=%d -D bidx=0 -D %s", dcn, reverse ? "REVERSE" : "ORDER"))) + format("-D DCN=%d -D BIDX=0 -D %s", dcn, reverse ? "REVERSE" : "ORDER"))) { return false; } @@ -441,7 +441,7 @@ bool oclCvtColorBGR25x5( InputArray _src, OutputArray _dst, int bidx, int gbits OclHelper< Set<3, 4>, Set<2>, Set > h(_src, _dst, 2); if(!h.createKernel("RGB2RGB5x5", ocl::imgproc::color_rgb_oclsrc, - format("-D dcn=2 -D bidx=%d -D greenbits=%d", bidx, gbits))) + format("-D DCN=2 -D BIDX=%d -D GREENBITS=%d", bidx, gbits))) { return false; } @@ -454,7 +454,7 @@ bool oclCvtColor5x52BGR( InputArray _src, OutputArray _dst, int dcn, int bidx, i OclHelper< Set<2>, Set<3, 4>, Set > h(_src, _dst, dcn); if(!h.createKernel("RGB5x52RGB", ocl::imgproc::color_rgb_oclsrc, - format("-D dcn=%d -D bidx=%d -D greenbits=%d", dcn, bidx, gbits))) + format("-D DCN=%d -D BIDX=%d -D GREENBITS=%d", dcn, bidx, gbits))) { return false; } @@ -467,7 +467,7 @@ bool oclCvtColor5x52Gray( InputArray _src, OutputArray _dst, int gbits) OclHelper< Set<2>, Set<1>, Set > h(_src, _dst, 1); if(!h.createKernel("BGR5x52Gray", ocl::imgproc::color_rgb_oclsrc, - format("-D dcn=1 -D bidx=0 -D greenbits=%d", gbits))) + format("-D DCN=1 -D BIDX=0 -D GREENBITS=%d", gbits))) { return false; } @@ -480,7 +480,7 @@ bool oclCvtColorGray25x5( InputArray _src, OutputArray _dst, int gbits) OclHelper< Set<1>, Set<2>, Set > h(_src, _dst, 2); if(!h.createKernel("Gray2BGR5x5", ocl::imgproc::color_rgb_oclsrc, - format("-D dcn=2 -D bidx=0 -D greenbits=%d", gbits))) + format("-D DCN=2 -D BIDX=0 -D GREENBITS=%d", gbits))) { return false; } @@ -494,7 +494,7 @@ bool oclCvtColorBGR2Gray( InputArray _src, OutputArray _dst, int bidx) int stripeSize = 1; if(!h.createKernel("RGB2Gray", ocl::imgproc::color_rgb_oclsrc, - format("-D dcn=1 -D bidx=%d -D STRIPE_SIZE=%d", bidx, stripeSize))) + format("-D DCN=1 -D BIDX=%d -D STRIPE_SIZE=%d", bidx, stripeSize))) { return false; } @@ -507,7 +507,7 @@ bool oclCvtColorGray2BGR( InputArray _src, OutputArray _dst, int dcn) { OclHelper< Set<1>, Set<3, 4>, Set > h(_src, _dst, dcn); if(!h.createKernel("Gray2RGB", ocl::imgproc::color_rgb_oclsrc, - format("-D bidx=0 -D dcn=%d", dcn))) + format("-D BIDX=0 -D DCN=%d", dcn))) { return false; } @@ -520,7 +520,7 @@ bool oclCvtColorRGBA2mRGBA( InputArray _src, OutputArray _dst) OclHelper< Set<4>, Set<4>, Set > h(_src, _dst, 4); if(!h.createKernel("RGBA2mRGBA", ocl::imgproc::color_rgb_oclsrc, - "-D dcn=4 -D bidx=3")) + "-D DCN=4 -D BIDX=3")) { return false; } @@ -533,7 +533,7 @@ bool oclCvtColormRGBA2RGBA( InputArray _src, OutputArray _dst) OclHelper< Set<4>, Set<4>, Set > h(_src, _dst, 4); if(!h.createKernel("mRGBA2RGBA", ocl::imgproc::color_rgb_oclsrc, - "-D dcn=4 -D bidx=3")) + "-D DCN=4 -D BIDX=3")) { return false; } diff --git a/modules/imgproc/src/color_yuv.dispatch.cpp b/modules/imgproc/src/color_yuv.dispatch.cpp index 8720908100..73e1aea32d 100644 --- a/modules/imgproc/src/color_yuv.dispatch.cpp +++ b/modules/imgproc/src/color_yuv.dispatch.cpp @@ -232,7 +232,7 @@ bool oclCvtColorYUV2BGR( InputArray _src, OutputArray _dst, int dcn, int bidx ) OclHelper< Set<3>, Set<3, 4>, Set > h(_src, _dst, dcn); if(!h.createKernel("YUV2RGB", ocl::imgproc::color_yuv_oclsrc, - format("-D dcn=%d -D bidx=%d", dcn, bidx))) + format("-D DCN=%d -D BIDX=%d", dcn, bidx))) { return false; } @@ -245,7 +245,7 @@ bool oclCvtColorBGR2YUV( InputArray _src, OutputArray _dst, int bidx ) OclHelper< Set<3, 4>, Set<3>, Set > h(_src, _dst, 3); if(!h.createKernel("RGB2YUV", ocl::imgproc::color_yuv_oclsrc, - format("-D dcn=3 -D bidx=%d", bidx))) + format("-D DCN=3 -D BIDX=%d", bidx))) { return false; } @@ -258,7 +258,7 @@ bool oclCvtcolorYCrCb2BGR( InputArray _src, OutputArray _dst, int dcn, int bidx) OclHelper< Set<3>, Set<3, 4>, Set > h(_src, _dst, dcn); if(!h.createKernel("YCrCb2RGB", ocl::imgproc::color_yuv_oclsrc, - format("-D dcn=%d -D bidx=%d", dcn, bidx))) + format("-D DCN=%d -D BIDX=%d", dcn, bidx))) { return false; } @@ -271,7 +271,7 @@ bool oclCvtColorBGR2YCrCb( InputArray _src, OutputArray _dst, int bidx) OclHelper< Set<3, 4>, Set<3>, Set > h(_src, _dst, 3); if(!h.createKernel("RGB2YCrCb", ocl::imgproc::color_yuv_oclsrc, - format("-D dcn=3 -D bidx=%d", bidx))) + format("-D DCN=3 -D BIDX=%d", bidx))) { return false; } @@ -285,7 +285,7 @@ bool oclCvtColorOnePlaneYUV2BGR( InputArray _src, OutputArray _dst, int dcn, int bool optimized = _src.offset() % 4 == 0 && _src.step() % 4 == 0; if(!h.createKernel("YUV2RGB_422", ocl::imgproc::color_yuv_oclsrc, - format("-D dcn=%d -D bidx=%d -D uidx=%d -D yidx=%d%s", dcn, bidx, uidx, yidx, + format("-D DCN=%d -D BIDX=%d -D UIDX=%d -D YIDX=%d%s", dcn, bidx, uidx, yidx, optimized ? " -D USE_OPTIMIZED_LOAD" : ""))) { return false; @@ -299,7 +299,7 @@ bool oclCvtColorOnePlaneBGR2YUV( InputArray _src, OutputArray _dst, int dcn, int OclHelper< Set<3, 4>, Set<2>, Set > h(_src, _dst, dcn); if(!h.createKernel("RGB2YUV_422", ocl::imgproc::color_yuv_oclsrc, - format("-D dcn=%d -D bidx=%d -D uidx=%d -D yidx=%d", dcn, bidx, uidx, yidx + format("-D DCN=%d -D BIDX=%d -D UIDX=%d -D YIDX=%d", dcn, bidx, uidx, yidx ))) { return false; @@ -321,7 +321,7 @@ bool oclCvtColorTwoPlaneYUV2BGR( InputArray _src, OutputArray _dst, int dcn, int OclHelper< Set<1>, Set<3, 4>, Set, FROM_YUV > h(_src, _dst, dcn); if(!h.createKernel("YUV2RGB_NVx", ocl::imgproc::color_yuv_oclsrc, - format("-D dcn=%d -D bidx=%d -D uidx=%d", dcn, bidx, uidx))) + format("-D DCN=%d -D BIDX=%d -D UIDX=%d", dcn, bidx, uidx))) { return false; } @@ -334,7 +334,7 @@ bool oclCvtColorThreePlaneYUV2BGR( InputArray _src, OutputArray _dst, int dcn, i OclHelper< Set<1>, Set<3, 4>, Set, FROM_YUV > h(_src, _dst, dcn); if(!h.createKernel("YUV2RGB_YV12_IYUV", ocl::imgproc::color_yuv_oclsrc, - format("-D dcn=%d -D bidx=%d -D uidx=%d%s", dcn, bidx, uidx, + format("-D DCN=%d -D BIDX=%d -D UIDX=%d%s", dcn, bidx, uidx, _src.isContinuous() ? " -D SRC_CONT" : ""))) { return false; @@ -348,7 +348,7 @@ bool oclCvtColorBGR2ThreePlaneYUV( InputArray _src, OutputArray _dst, int bidx, OclHelper< Set<3, 4>, Set<1>, Set, TO_YUV > h(_src, _dst, 1); if(!h.createKernel("RGB2YUV_YV12_IYUV", ocl::imgproc::color_yuv_oclsrc, - format("-D dcn=1 -D bidx=%d -D uidx=%d", bidx, uidx))) + format("-D DCN=1 -D BIDX=%d -D UIDX=%d", bidx, uidx))) { return false; } diff --git a/modules/imgproc/src/contours.cpp b/modules/imgproc/src/contours.cpp index cae583f618..421aeffd5c 100644 --- a/modules/imgproc/src/contours.cpp +++ b/modules/imgproc/src/contours.cpp @@ -512,7 +512,7 @@ typedef struct _CvContourScanner* CvContourScanner; /* Initializes scanner structure. - Prepare image for scanning ( clear borders and convert all pixels to 0-1. + Prepare image for scanning ( clear borders and convert all pixels to 0-1 ). */ static CvContourScanner cvStartFindContours_Impl( void* _img, CvMemStorage* storage, @@ -551,7 +551,7 @@ cvStartFindContours_Impl( void* _img, CvMemStorage* storage, scanner->img = (schar *) (img + step); scanner->img_step = step; scanner->img_size.width = size.width - 1; /* exclude rightest column */ - scanner->img_size.height = size.height - 1; /* exclude bottomost row */ + scanner->img_size.height = size.height - 1; /* exclude bottommost row */ scanner->mode = mode; scanner->offset = offset; scanner->pt.x = scanner->pt.y = 1; diff --git a/modules/imgproc/src/cornersubpix.cpp b/modules/imgproc/src/cornersubpix.cpp index 3297f2bbd7..f823fa7999 100644 --- a/modules/imgproc/src/cornersubpix.cpp +++ b/modules/imgproc/src/cornersubpix.cpp @@ -96,6 +96,7 @@ void cv::cornerSubPix( InputArray _image, InputOutputArray _corners, for( int pt_i = 0; pt_i < count; pt_i++ ) { Point2f cT = corners[pt_i], cI = cT; + CV_Assert( Rect(0, 0, src.cols, src.rows).contains(cT) ); int iter = 0; double err = 0; @@ -140,9 +141,10 @@ void cv::cornerSubPix( InputArray _image, InputOutputArray _corners, cI2.x = (float)(cI.x + c*scale*bb1 - b*scale*bb2); cI2.y = (float)(cI.y - b*scale*bb1 + a*scale*bb2); err = (cI2.x - cI.x) * (cI2.x - cI.x) + (cI2.y - cI.y) * (cI2.y - cI.y); - cI = cI2; - if( cI.x < 0 || cI.x >= src.cols || cI.y < 0 || cI.y >= src.rows ) + // if new point is out of image, leave previous point as the result + if( !Rect(0, 0, src.cols, src.rows).contains(cI2) ) break; + cI = cI2; } while( ++iter < max_iters && err > eps ); diff --git a/modules/imgproc/src/deriv.cpp b/modules/imgproc/src/deriv.cpp index 8fec9885b8..5488141c50 100644 --- a/modules/imgproc/src/deriv.cpp +++ b/modules/imgproc/src/deriv.cpp @@ -583,9 +583,9 @@ static bool ocl_Laplacian5(InputArray _src, OutputArray _dst, "BORDER_REFLECT_101" }; String opts = cv::format("-D BLK_X=%d -D BLK_Y=%d -D RADIUS=%d%s%s" - " -D convertToWT=%s -D convertToDT=%s" - " -D %s -D srcT1=%s -D dstT1=%s -D WT1=%s" - " -D srcT=%s -D dstT=%s -D WT=%s" + " -D CONVERT_TO_WT=%s -D CONVERT_TO_DT=%s" + " -D %s -D SRC_T1=%s -D DST_T1=%s -D WT1=%s" + " -D SRC_T=%s -D DST_T=%s -D WT=%s" " -D CN=%d ", (int)lt2[0], (int)lt2[1], kernelX.cols / 2, ocl::kernelToStr(kernelX, wdepth, "KERNEL_MATRIX_X").c_str(), @@ -627,8 +627,8 @@ static bool ocl_Laplacian5(InputArray _src, OutputArray _dst, char cvt[2][50]; ocl::Kernel k("sumConvert", ocl::imgproc::laplacian5_oclsrc, format("-D ONLY_SUM_CONVERT " - "-D srcT=%s -D WT=%s -D dstT=%s -D coeffT=%s -D wdepth=%d " - "-D convertToWT=%s -D convertToDT=%s%s", + "-D SRC_T=%s -D WT=%s -D DST_T=%s -D COEFF_T=%s -D WDEPTH=%d " + "-D CONVERT_TO_WT=%s -D CONVERT_TO_DT=%s%s", ocl::typeToStr(CV_MAKE_TYPE(depth, kercn)), ocl::typeToStr(CV_MAKE_TYPE(wdepth, kercn)), ocl::typeToStr(CV_MAKE_TYPE(ddepth, kercn)), diff --git a/modules/imgproc/src/distransform.cpp b/modules/imgproc/src/distransform.cpp index b11da8ebc2..67935e4e5a 100755 --- a/modules/imgproc/src/distransform.cpp +++ b/modules/imgproc/src/distransform.cpp @@ -817,14 +817,15 @@ void cv::distanceTransform( InputArray _src, OutputArray _dst, OutputArray _labe Size size = src.size(); int border = maskSize == cv::DIST_MASK_3 ? 1 : 2; - Mat temp( size.height + border*2, size.width + border*2, CV_32SC1 ); + Mat temp; if( !need_labels ) { if( maskSize == cv::DIST_MASK_3 ) { -#if defined (HAVE_IPP) && (IPP_VERSION_X100 >= 700) && 0 // disabled: https://github.com/opencv/opencv/issues/15904 - CV_IPP_CHECK() +#if defined (HAVE_IPP) && (IPP_VERSION_X100 >= 700) + bool has_int_overflow = (int64)src.cols * src.rows >= INT_MAX; + if (!has_int_overflow && CV_IPP_CHECK_COND) { IppiSize roi = { src.cols, src.rows }; if (CV_INSTRUMENT_FUN_IPP(ippiDistanceTransform_3x3_8u32f_C1R, src.ptr(), (int)src.step, dst.ptr(), (int)dst.step, roi, _mask) >= 0) @@ -836,12 +837,14 @@ void cv::distanceTransform( InputArray _src, OutputArray _dst, OutputArray _labe } #endif + temp.create(size.height + border*2, size.width + border*2, CV_32SC1); distanceTransform_3x3(src, temp, dst, _mask); } else { #if defined (HAVE_IPP) && (IPP_VERSION_X100 >= 700) - CV_IPP_CHECK() + bool has_int_overflow = (int64)src.cols * src.rows >= INT_MAX; + if (!has_int_overflow && CV_IPP_CHECK_COND) { IppiSize roi = { src.cols, src.rows }; if (CV_INSTRUMENT_FUN_IPP(ippiDistanceTransform_5x5_8u32f_C1R, src.ptr(), (int)src.step, dst.ptr(), (int)dst.step, roi, _mask) >= 0) @@ -853,6 +856,7 @@ void cv::distanceTransform( InputArray _src, OutputArray _dst, OutputArray _labe } #endif + temp.create(size.height + border*2, size.width + border*2, CV_32SC1); distanceTransform_5x5(src, temp, dst, _mask); } } @@ -879,7 +883,8 @@ void cv::distanceTransform( InputArray _src, OutputArray _dst, OutputArray _labe } } - distanceTransformEx_5x5( src, temp, dst, labels, _mask ); + temp.create(size.height + border*2, size.width + border*2, CV_32SC1); + distanceTransformEx_5x5( src, temp, dst, labels, _mask ); } } diff --git a/modules/imgproc/src/imgwarp.cpp b/modules/imgproc/src/imgwarp.cpp index 5c1d2d4dd3..8a9fc596cb 100644 --- a/modules/imgproc/src/imgwarp.cpp +++ b/modules/imgproc/src/imgwarp.cpp @@ -1354,7 +1354,7 @@ static bool ocl_remap(InputArray _src, OutputArray _dst, InputArray _map1, Input static const char * const interMap[] = { "INTER_NEAREST", "INTER_LINEAR", "INTER_CUBIC", "INTER_LINEAR", "INTER_LANCZOS" }; static const char * const borderMap[] = { "BORDER_CONSTANT", "BORDER_REPLICATE", "BORDER_REFLECT", "BORDER_WRAP", "BORDER_REFLECT_101", "BORDER_TRANSPARENT" }; - String buildOptions = format("-D %s -D %s -D T=%s -D rowsPerWI=%d", + String buildOptions = format("-D %s -D %s -D T=%s -D ROWS_PER_WI=%d", interMap[interpolation], borderMap[borderType], ocl::typeToStr(type), rowsPerWI); @@ -1363,8 +1363,8 @@ static bool ocl_remap(InputArray _src, OutputArray _dst, InputArray _map1, Input char cvt[3][50]; int wdepth = std::max(CV_32F, depth); buildOptions = buildOptions - + format(" -D WT=%s -D convertToT=%s -D convertToWT=%s" - " -D convertToWT2=%s -D WT2=%s", + + format(" -D WT=%s -D CONVERT_TO_T=%s -D CONVERT_TO_WT=%s" + " -D CONVERT_TO_WT2=%s -D WT2=%s", ocl::typeToStr(CV_MAKE_TYPE(wdepth, cn)), ocl::convertTypeStr(wdepth, depth, cn, cvt[0], sizeof(cvt[0])), ocl::convertTypeStr(depth, wdepth, cn, cvt[1], sizeof(cvt[1])), @@ -1373,7 +1373,7 @@ static bool ocl_remap(InputArray _src, OutputArray _dst, InputArray _map1, Input } int scalarcn = cn == 3 ? 4 : cn; int sctype = CV_MAKETYPE(depth, scalarcn); - buildOptions += format(" -D T=%s -D T1=%s -D cn=%d -D ST=%s -D depth=%d", + buildOptions += format(" -D T=%s -D T1=%s -D CN=%d -D ST=%s -D SRC_DEPTH=%d", ocl::typeToStr(type), ocl::typeToStr(depth), cn, ocl::typeToStr(sctype), depth); @@ -2494,7 +2494,7 @@ static bool ocl_warpTransform(InputArray _src, OutputArray _dst, InputArray _M0, String opts; if (interpolation == INTER_NEAREST) { - opts = format("-D INTER_NEAREST -D T=%s%s -D CT=%s -D T1=%s -D ST=%s -D cn=%d -D rowsPerWI=%d", + opts = format("-D INTER_NEAREST -D T=%s%s -D CT=%s -D T1=%s -D ST=%s -D CN=%d -D ROWS_PER_WI=%d", ocl::typeToStr(type), doubleSupport ? " -D DOUBLE_SUPPORT" : "", useDouble ? "double" : "float", @@ -2504,8 +2504,8 @@ static bool ocl_warpTransform(InputArray _src, OutputArray _dst, InputArray _M0, else { char cvt[2][50]; - opts = format("-D INTER_%s -D T=%s -D T1=%s -D ST=%s -D WT=%s -D depth=%d" - " -D convertToWT=%s -D convertToT=%s%s -D CT=%s -D cn=%d -D rowsPerWI=%d", + opts = format("-D INTER_%s -D T=%s -D T1=%s -D ST=%s -D WT=%s -D SRC_DEPTH=%d" + " -D CONVERT_TO_WT=%s -D CONVERT_TO_T=%s%s -D CT=%s -D CN=%d -D ROWS_PER_WI=%d", interpolationMap[interpolation], ocl::typeToStr(type), ocl::typeToStr(CV_MAT_DEPTH(type)), ocl::typeToStr(sctype), diff --git a/modules/imgproc/src/opencl/color_hsv.cl b/modules/imgproc/src/opencl/color_hsv.cl index eb883bdc96..8eec8edd1c 100644 --- a/modules/imgproc/src/opencl/color_hsv.cl +++ b/modules/imgproc/src/opencl/color_hsv.cl @@ -46,21 +46,21 @@ /**************************************PUBLICFUNC*************************************/ -#if depth == 0 +#if SRC_DEPTH == 0 #define DATA_TYPE uchar #define MAX_NUM 255 #define HALF_MAX_NUM 128 #define COEFF_TYPE int #define SAT_CAST(num) convert_uchar_sat(num) #define DEPTH_0 -#elif depth == 2 +#elif SRC_DEPTH == 2 #define DATA_TYPE ushort #define MAX_NUM 65535 #define HALF_MAX_NUM 32768 #define COEFF_TYPE int #define SAT_CAST(num) convert_ushort_sat(num) #define DEPTH_2 -#elif depth == 5 +#elif SRC_DEPTH == 5 #define DATA_TYPE float #define MAX_NUM 1.0f #define HALF_MAX_NUM 0.5f @@ -78,18 +78,18 @@ enum hsv_shift = 12 }; -#define scnbytes ((int)sizeof(DATA_TYPE)*scn) -#define dcnbytes ((int)sizeof(DATA_TYPE)*dcn) +#define scnbytes ((int)sizeof(DATA_TYPE)*SCN) +#define dcnbytes ((int)sizeof(DATA_TYPE)*DCN) -#ifndef hscale -#define hscale 0 +#ifndef HSCALE +#define HSCALE 0 #endif -#ifndef hrange -#define hrange 0 +#ifndef HRANGE +#define HRANGE 0 #endif -#if bidx == 0 +#if BIDX == 0 #define R_COMP z #define G_COMP y #define B_COMP x @@ -148,7 +148,7 @@ __kernel void RGB2HSV(__global const uchar* src, int src_step, int src_offset, h = (vr & (g - b)) + (~vr & ((vg & mad24(diff, 2, b - r)) + ((~vg) & mad24(4, diff, r - g)))); h = mad24(h, hdiv_table[diff], (1 << (hsv_shift-1))) >> hsv_shift; - h += h < 0 ? hrange : 0; + h += h < 0 ? HRANGE : 0; dst[dst_index] = convert_uchar_sat_rte(h); dst[dst_index + 1] = (uchar)s; @@ -188,7 +188,7 @@ __kernel void HSV2RGB(__global const uchar* src, int src_step, int src_offset, { float tab[4]; int sector; - h *= hscale; + h *= HSCALE; if( h < 0 ) do h += 6; while( h < 0 ); else if( h >= 6 ) @@ -213,10 +213,10 @@ __kernel void HSV2RGB(__global const uchar* src, int src_step, int src_offset, else b = g = r = v; - dst[dst_index + bidx] = convert_uchar_sat_rte(b*255.f); + dst[dst_index + BIDX] = convert_uchar_sat_rte(b*255.f); dst[dst_index + 1] = convert_uchar_sat_rte(g*255.f); - dst[dst_index + (bidx^2)] = convert_uchar_sat_rte(r*255.f); -#if dcn == 4 + dst[dst_index + (BIDX^2)] = convert_uchar_sat_rte(r*255.f); +#if DCN == 4 dst[dst_index + 3] = MAX_NUM; #endif @@ -275,7 +275,7 @@ __kernel void RGB2HSV(__global const uchar* srcptr, int src_step, int src_offset if( h < 0 ) h += 360.f; - dst[0] = h*hscale; + dst[0] = h*HSCALE; dst[1] = s; dst[2] = v; @@ -316,7 +316,7 @@ __kernel void HSV2RGB(__global const uchar* srcptr, int src_step, int src_offset { float tab[4]; int sector; - h *= hscale; + h *= HSCALE; if(h < 0) do h += 6; while (h < 0); else if (h >= 6) @@ -341,10 +341,10 @@ __kernel void HSV2RGB(__global const uchar* srcptr, int src_step, int src_offset else b = g = r = v; - dst[bidx] = b; + dst[BIDX] = b; dst[1] = g; - dst[bidx^2] = r; -#if dcn == 4 + dst[BIDX^2] = r; +#if DCN == 4 dst[3] = MAX_NUM; #endif @@ -410,7 +410,7 @@ __kernel void RGB2HLS(__global const uchar* src, int src_step, int src_offset, h += 360.f; } - dst[dst_index] = convert_uchar_sat_rte(h*hscale); + dst[dst_index] = convert_uchar_sat_rte(h*HSCALE); dst[dst_index + 1] = convert_uchar_sat_rte(l*255.f); dst[dst_index + 2] = convert_uchar_sat_rte(s*255.f); @@ -451,7 +451,7 @@ __kernel void HLS2RGB(__global const uchar* src, int src_step, int src_offset, float p2 = l <= 0.5f ? l*(1 + s) : l + s - l*s; float p1 = 2*l - p2; - h *= hscale; + h *= HSCALE; if( h < 0 ) do h += 6; while( h < 0 ); else if( h >= 6 ) @@ -472,10 +472,10 @@ __kernel void HLS2RGB(__global const uchar* src, int src_step, int src_offset, else b = g = r = l; - dst[dst_index + bidx] = convert_uchar_sat_rte(b*255.f); + dst[dst_index + BIDX] = convert_uchar_sat_rte(b*255.f); dst[dst_index + 1] = convert_uchar_sat_rte(g*255.f); - dst[dst_index + (bidx^2)] = convert_uchar_sat_rte(r*255.f); -#if dcn == 4 + dst[dst_index + (BIDX^2)] = convert_uchar_sat_rte(r*255.f); +#if DCN == 4 dst[dst_index + 3] = MAX_NUM; #endif @@ -538,7 +538,7 @@ __kernel void RGB2HLS(__global const uchar* srcptr, int src_step, int src_offset if( h < 0.f ) h += 360.f; } - dst[0] = h*hscale; + dst[0] = h*HSCALE; dst[1] = l; dst[2] = s; @@ -582,7 +582,7 @@ __kernel void HLS2RGB(__global const uchar* srcptr, int src_step, int src_offset float p2 = l <= 0.5f ? l*(1 + s) : l + s - l*s; float p1 = 2*l - p2; - h *= hscale; + h *= HSCALE; if( h < 0 ) do h += 6; while( h < 0 ); else if( h >= 6 ) @@ -603,10 +603,10 @@ __kernel void HLS2RGB(__global const uchar* srcptr, int src_step, int src_offset else b = g = r = l; - dst[bidx] = b; + dst[BIDX] = b; dst[1] = g; - dst[bidx^2] = r; -#if dcn == 4 + dst[BIDX^2] = r; +#if DCN == 4 dst[3] = MAX_NUM; #endif diff --git a/modules/imgproc/src/opencl/color_lab.cl b/modules/imgproc/src/opencl/color_lab.cl index 16a96d25e7..1be74a2466 100644 --- a/modules/imgproc/src/opencl/color_lab.cl +++ b/modules/imgproc/src/opencl/color_lab.cl @@ -44,21 +44,21 @@ // //M*/ -#if depth == 0 +#if SRC_DEPTH == 0 #define DATA_TYPE uchar #define MAX_NUM 255 #define HALF_MAX_NUM 128 #define COEFF_TYPE int #define SAT_CAST(num) convert_uchar_sat(num) #define DEPTH_0 -#elif depth == 2 +#elif SRC_DEPTH == 2 #define DATA_TYPE ushort #define MAX_NUM 65535 #define HALF_MAX_NUM 32768 #define COEFF_TYPE int #define SAT_CAST(num) convert_ushort_sat(num) #define DEPTH_2 -#elif depth == 5 +#elif SRC_DEPTH == 5 #define DATA_TYPE float #define MAX_NUM 1.0f #define HALF_MAX_NUM 0.5f @@ -76,8 +76,8 @@ enum xyz_shift = 12, }; -#define scnbytes ((int)sizeof(DATA_TYPE)*scn) -#define dcnbytes ((int)sizeof(DATA_TYPE)*dcn) +#define scnbytes ((int)sizeof(DATA_TYPE)*SCN) +#define dcnbytes ((int)sizeof(DATA_TYPE)*DCN) #define __CAT(x, y) x##y #define CAT(x, y) __CAT(x, y) @@ -167,11 +167,11 @@ __kernel void XYZ2RGB(__global const uchar * srcptr, int src_step, int src_offse DATA_TYPE dst0 = SAT_CAST(b); DATA_TYPE dst1 = SAT_CAST(g); DATA_TYPE dst2 = SAT_CAST(r); -#if dcn == 3 || defined DEPTH_5 +#if DCN == 3 || defined DEPTH_5 dst[0] = dst0; dst[1] = dst1; dst[2] = dst2; -#if dcn == 4 +#if DCN == 4 dst[3] = MAX_NUM; #endif #else @@ -403,7 +403,7 @@ __kernel void Lab2BGR(__global const uchar * src, int src_step, int src_offset, #endif coeffs, lThresh, fThresh); -#if dcn == 3 +#if DCN == 3 dst_ptr[0] = SAT_CAST(dstbuf[0] * 255.0f); dst_ptr[1] = SAT_CAST(dstbuf[1] * 255.0f); dst_ptr[2] = SAT_CAST(dstbuf[2] * 255.0f); @@ -455,7 +455,7 @@ __kernel void Lab2BGR(__global const uchar * srcptr, int src_step, int src_offse coeffs, lThresh, fThresh); dst[0] = dstbuf[0], dst[1] = dstbuf[1], dst[2] = dstbuf[2]; -#if dcn == 4 +#if DCN == 4 dst[3] = MAX_NUM; #endif ++y; @@ -644,7 +644,7 @@ __kernel void Luv2BGR(__global const uchar * srcptr, int src_step, int src_offse dst[0] = R; dst[1] = G; dst[2] = B; -#if dcn == 4 +#if DCN == 4 dst[3] = MAX_NUM; #endif ++y; @@ -717,7 +717,7 @@ __kernel void Luv2BGR(__global const uchar * src, int src_step, int src_offset, uchar dst1 = SAT_CAST(G * 255.0f); uchar dst2 = SAT_CAST(B * 255.0f); -#if dcn == 4 +#if DCN == 4 *(__global uchar4 *)dst = (uchar4)(dst0, dst1, dst2, MAX_NUM); #else dst[0] = dst0; diff --git a/modules/imgproc/src/opencl/color_rgb.cl b/modules/imgproc/src/opencl/color_rgb.cl index dd4563e111..d5cffe6dcd 100644 --- a/modules/imgproc/src/opencl/color_rgb.cl +++ b/modules/imgproc/src/opencl/color_rgb.cl @@ -46,21 +46,21 @@ /**************************************PUBLICFUNC*************************************/ -#if depth == 0 +#if SRC_DEPTH == 0 #define DATA_TYPE uchar #define MAX_NUM 255 #define HALF_MAX_NUM 128 #define COEFF_TYPE int #define SAT_CAST(num) convert_uchar_sat(num) #define DEPTH_0 -#elif depth == 2 +#elif SRC_DEPTH == 2 #define DATA_TYPE ushort #define MAX_NUM 65535 #define HALF_MAX_NUM 32768 #define COEFF_TYPE int #define SAT_CAST(num) convert_ushort_sat(num) #define DEPTH_2 -#elif depth == 5 +#elif SRC_DEPTH == 5 #define DATA_TYPE float #define MAX_NUM 1.0f #define HALF_MAX_NUM 0.5f @@ -86,10 +86,10 @@ enum #define G2YF 0.587f #define R2YF 0.299f -#define scnbytes ((int)sizeof(DATA_TYPE)*scn) -#define dcnbytes ((int)sizeof(DATA_TYPE)*dcn) +#define scnbytes ((int)sizeof(DATA_TYPE)*SCN) +#define dcnbytes ((int)sizeof(DATA_TYPE)*DCN) -#if bidx == 0 +#if BIDX == 0 #define R_COMP z #define G_COMP y #define B_COMP x @@ -160,9 +160,9 @@ __kernel void Gray2RGB(__global const uchar * srcptr, int src_step, int src_offs __global const DATA_TYPE* src = (__global const DATA_TYPE*)(srcptr + src_index); __global DATA_TYPE* dst = (__global DATA_TYPE*)(dstptr + dst_index); DATA_TYPE val = src[0]; -#if dcn == 3 || defined DEPTH_5 +#if DCN == 3 || defined DEPTH_5 dst[0] = dst[1] = dst[2] = val; -#if dcn == 4 +#if DCN == 4 dst[3] = MAX_NUM; #endif #else @@ -197,7 +197,7 @@ __kernel void RGB(__global const uchar* srcptr, int src_step, int src_offset, { __global const DATA_TYPE * src = (__global const DATA_TYPE *)(srcptr + src_index); __global DATA_TYPE * dst = (__global DATA_TYPE *)(dstptr + dst_index); -#if scn == 3 +#if SCN == 3 DATA_TYPE_3 src_pix = vload3(0, src); #else DATA_TYPE_4 src_pix = vload4(0, src); @@ -213,8 +213,8 @@ __kernel void RGB(__global const uchar* srcptr, int src_step, int src_offset, dst[2] = src_pix.z; #endif -#if dcn == 4 -#if scn == 3 +#if DCN == 4 +#if SCN == 3 dst[3] = MAX_NUM; #else dst[3] = src[3]; @@ -250,18 +250,18 @@ __kernel void RGB5x52RGB(__global const uchar* src, int src_step, int src_offset { ushort t = *((__global const ushort*)(src + src_index)); -#if greenbits == 6 - dst[dst_index + bidx] = (uchar)(t << 3); +#if GREENBITS == 6 + dst[dst_index + BIDX] = (uchar)(t << 3); dst[dst_index + 1] = (uchar)((t >> 3) & ~3); - dst[dst_index + (bidx^2)] = (uchar)((t >> 8) & ~7); + dst[dst_index + (BIDX^2)] = (uchar)((t >> 8) & ~7); #else - dst[dst_index + bidx] = (uchar)(t << 3); + dst[dst_index + BIDX] = (uchar)(t << 3); dst[dst_index + 1] = (uchar)((t >> 2) & ~7); - dst[dst_index + (bidx^2)] = (uchar)((t >> 7) & ~7); + dst[dst_index + (BIDX^2)] = (uchar)((t >> 7) & ~7); #endif -#if dcn == 4 -#if greenbits == 6 +#if DCN == 4 +#if GREENBITS == 6 dst[dst_index + 3] = 255; #else dst[dst_index + 3] = t & 0x8000 ? 255 : 0; @@ -295,9 +295,9 @@ __kernel void RGB2RGB5x5(__global const uchar* src, int src_step, int src_offset { uchar4 src_pix = vload4(0, src + src_index); -#if greenbits == 6 +#if GREENBITS == 6 *((__global ushort*)(dst + dst_index)) = (ushort)((src_pix.B_COMP >> 3)|((src_pix.G_COMP&~3) << 3)|((src_pix.R_COMP&~7) << 8)); -#elif scn == 3 +#elif SCN == 3 *((__global ushort*)(dst + dst_index)) = (ushort)((src_pix.B_COMP >> 3)|((src_pix.G_COMP&~7) << 2)|((src_pix.R_COMP&~7) << 7)); #else *((__global ushort*)(dst + dst_index)) = (ushort)((src_pix.B_COMP >> 3)|((src_pix.G_COMP&~7) << 2)| @@ -333,7 +333,7 @@ __kernel void BGR5x52Gray(__global const uchar* src, int src_step, int src_offse { int t = *((__global const ushort*)(src + src_index)); -#if greenbits == 6 +#if GREENBITS == 6 dst[dst_index] = (uchar)CV_DESCALE(mad24((t << 3) & 0xf8, BY15, mad24((t >> 3) & 0xfc, GY15, ((t >> 8) & 0xf8) * RY15)), gray_shift); #else dst[dst_index] = (uchar)CV_DESCALE(mad24((t << 3) & 0xf8, BY15, mad24((t >> 2) & 0xf8, GY15, ((t >> 7) & 0xf8) * RY15)), gray_shift); @@ -365,7 +365,7 @@ __kernel void Gray2BGR5x5(__global const uchar* src, int src_step, int src_offse { int t = src[src_index]; -#if greenbits == 6 +#if GREENBITS == 6 *((__global ushort*)(dst + dst_index)) = (ushort)((t >> 3) | ((t & ~3) << 3) | ((t & ~7) << 8)); #else t >>= 3; diff --git a/modules/imgproc/src/opencl/color_yuv.cl b/modules/imgproc/src/opencl/color_yuv.cl index c536f87a0b..ebef91743f 100644 --- a/modules/imgproc/src/opencl/color_yuv.cl +++ b/modules/imgproc/src/opencl/color_yuv.cl @@ -46,21 +46,21 @@ /**************************************PUBLICFUNC*************************************/ -#if depth == 0 +#if SRC_DEPTH == 0 #define DATA_TYPE uchar #define MAX_NUM 255 #define HALF_MAX_NUM 128 #define COEFF_TYPE int #define SAT_CAST(num) convert_uchar_sat(num) #define DEPTH_0 -#elif depth == 2 +#elif SRC_DEPTH == 2 #define DATA_TYPE ushort #define MAX_NUM 65535 #define HALF_MAX_NUM 32768 #define COEFF_TYPE int #define SAT_CAST(num) convert_ushort_sat(num) #define DEPTH_2 -#elif depth == 5 +#elif SRC_DEPTH == 5 #define DATA_TYPE float #define MAX_NUM 1.0f #define HALF_MAX_NUM 0.5f @@ -114,10 +114,10 @@ enum #define CR2GI -11698 #define CB2BI 29049 -#define scnbytes ((int)sizeof(DATA_TYPE)*scn) -#define dcnbytes ((int)sizeof(DATA_TYPE)*dcn) +#define scnbytes ((int)sizeof(DATA_TYPE)*SCN) +#define dcnbytes ((int)sizeof(DATA_TYPE)*DCN) -#if bidx == 0 +#if BIDX == 0 #define R_COMP z #define G_COMP y #define B_COMP x @@ -127,12 +127,12 @@ enum #define B_COMP z #endif -#ifndef uidx -#define uidx 0 +#ifndef UIDX +#define UIDX 0 #endif -#ifndef yidx -#define yidx 0 +#ifndef YIDX +#define YIDX 0 #endif #ifndef PIX_PER_WI_X @@ -234,10 +234,10 @@ __kernel void YUV2RGB(__global const uchar* srcptr, int src_step, int src_offset const int b = Y + CV_DESCALE(mul24(U - HALF_MAX_NUM, coeffs[0]), yuv_shift); #endif - dst[bidx] = SAT_CAST( b ); + dst[BIDX] = SAT_CAST( b ); dst[1] = SAT_CAST( g ); - dst[bidx^2] = SAT_CAST( r ); -#if dcn == 4 + dst[BIDX^2] = SAT_CAST( r ); +#if DCN == 4 dst[3] = MAX_NUM; #endif ++y; @@ -266,7 +266,7 @@ __kernel void YUV2RGB_NVx(__global const uchar* srcptr, int src_step, int src_of { __global const uchar* ysrc = srcptr + mad24(y << 1, src_step, (x << 1) + src_offset); __global const uchar* usrc = srcptr + mad24(rows + y, src_step, (x << 1) + src_offset); - __global uchar* dst1 = dstptr + mad24(y << 1, dst_step, mad24(x, dcn<<1, dt_offset)); + __global uchar* dst1 = dstptr + mad24(y << 1, dst_step, mad24(x, DCN<<1, dt_offset)); __global uchar* dst2 = dst1 + dst_step; float Y1 = ysrc[0]; @@ -274,8 +274,8 @@ __kernel void YUV2RGB_NVx(__global const uchar* srcptr, int src_step, int src_of float Y3 = ysrc[src_step]; float Y4 = ysrc[src_step + 1]; - float U = ((float)usrc[uidx]) - HALF_MAX_NUM; - float V = ((float)usrc[1-uidx]) - HALF_MAX_NUM; + float U = ((float)usrc[UIDX]) - HALF_MAX_NUM; + float V = ((float)usrc[1-UIDX]) - HALF_MAX_NUM; __constant float* coeffs = c_YUV2RGBCoeffs_420; float ruv = fma(coeffs[4], V, 0.5f); @@ -283,34 +283,34 @@ __kernel void YUV2RGB_NVx(__global const uchar* srcptr, int src_step, int src_of float buv = fma(coeffs[1], U, 0.5f); Y1 = max(0.f, Y1 - 16.f) * coeffs[0]; - dst1[2 - bidx] = convert_uchar_sat(Y1 + ruv); + dst1[2 - BIDX] = convert_uchar_sat(Y1 + ruv); dst1[1] = convert_uchar_sat(Y1 + guv); - dst1[bidx] = convert_uchar_sat(Y1 + buv); -#if dcn == 4 + dst1[BIDX] = convert_uchar_sat(Y1 + buv); +#if DCN == 4 dst1[3] = 255; #endif Y2 = max(0.f, Y2 - 16.f) * coeffs[0]; - dst1[dcn + 2 - bidx] = convert_uchar_sat(Y2 + ruv); - dst1[dcn + 1] = convert_uchar_sat(Y2 + guv); - dst1[dcn + bidx] = convert_uchar_sat(Y2 + buv); -#if dcn == 4 + dst1[DCN + 2 - BIDX] = convert_uchar_sat(Y2 + ruv); + dst1[DCN + 1] = convert_uchar_sat(Y2 + guv); + dst1[DCN + BIDX] = convert_uchar_sat(Y2 + buv); +#if DCN == 4 dst1[7] = 255; #endif Y3 = max(0.f, Y3 - 16.f) * coeffs[0]; - dst2[2 - bidx] = convert_uchar_sat(Y3 + ruv); + dst2[2 - BIDX] = convert_uchar_sat(Y3 + ruv); dst2[1] = convert_uchar_sat(Y3 + guv); - dst2[bidx] = convert_uchar_sat(Y3 + buv); -#if dcn == 4 + dst2[BIDX] = convert_uchar_sat(Y3 + buv); +#if DCN == 4 dst2[3] = 255; #endif Y4 = max(0.f, Y4 - 16.f) * coeffs[0]; - dst2[dcn + 2 - bidx] = convert_uchar_sat(Y4 + ruv); - dst2[dcn + 1] = convert_uchar_sat(Y4 + guv); - dst2[dcn + bidx] = convert_uchar_sat(Y4 + buv); -#if dcn == 4 + dst2[DCN + 2 - BIDX] = convert_uchar_sat(Y4 + ruv); + dst2[DCN + 1] = convert_uchar_sat(Y4 + guv); + dst2[DCN + BIDX] = convert_uchar_sat(Y4 + buv); +#if DCN == 4 dst2[7] = 255; #endif } @@ -319,7 +319,7 @@ __kernel void YUV2RGB_NVx(__global const uchar* srcptr, int src_step, int src_of } } -#if uidx < 2 +#if UIDX < 2 __kernel void YUV2RGB_YV12_IYUV(__global const uchar* srcptr, int src_step, int src_offset, __global uchar* dstptr, int dst_step, int dt_offset, @@ -336,7 +336,7 @@ __kernel void YUV2RGB_YV12_IYUV(__global const uchar* srcptr, int src_step, int if (y < rows / 2 ) { __global const uchar* ysrc = srcptr + mad24(y << 1, src_step, (x << 1) + src_offset); - __global uchar* dst1 = dstptr + mad24(y << 1, dst_step, x * (dcn<<1) + dt_offset); + __global uchar* dst1 = dstptr + mad24(y << 1, dst_step, x * (DCN<<1) + dt_offset); __global uchar* dst2 = dst1 + dst_step; float Y1 = ysrc[0]; @@ -354,8 +354,8 @@ __kernel void YUV2RGB_YV12_IYUV(__global const uchar* srcptr, int src_step, int __global const uchar* vsrc = usrc + mad24(rows >> 2, src_step, rows % 4 ? vsteps[y%2] : 0); float uv[2] = { ((float)usrc[0]) - HALF_MAX_NUM, ((float)vsrc[0]) - HALF_MAX_NUM }; #endif - float U = uv[uidx]; - float V = uv[1-uidx]; + float U = uv[UIDX]; + float V = uv[1-UIDX]; __constant float* coeffs = c_YUV2RGBCoeffs_420; float ruv = fma(coeffs[4], V, 0.5f); @@ -363,34 +363,34 @@ __kernel void YUV2RGB_YV12_IYUV(__global const uchar* srcptr, int src_step, int float buv = fma(coeffs[1], U, 0.5f); Y1 = max(0.f, Y1 - 16.f) * coeffs[0]; - dst1[2 - bidx] = convert_uchar_sat(Y1 + ruv); + dst1[2 - BIDX] = convert_uchar_sat(Y1 + ruv); dst1[1] = convert_uchar_sat(Y1 + guv); - dst1[bidx] = convert_uchar_sat(Y1 + buv); -#if dcn == 4 + dst1[BIDX] = convert_uchar_sat(Y1 + buv); +#if DCN == 4 dst1[3] = 255; #endif Y2 = max(0.f, Y2 - 16.f) * coeffs[0]; - dst1[dcn + 2 - bidx] = convert_uchar_sat(Y2 + ruv); - dst1[dcn + 1] = convert_uchar_sat(Y2 + guv); - dst1[dcn + bidx] = convert_uchar_sat(Y2 + buv); -#if dcn == 4 + dst1[DCN + 2 - BIDX] = convert_uchar_sat(Y2 + ruv); + dst1[DCN + 1] = convert_uchar_sat(Y2 + guv); + dst1[DCN + BIDX] = convert_uchar_sat(Y2 + buv); +#if DCN == 4 dst1[7] = 255; #endif Y3 = max(0.f, Y3 - 16.f) * coeffs[0]; - dst2[2 - bidx] = convert_uchar_sat(Y3 + ruv); + dst2[2 - BIDX] = convert_uchar_sat(Y3 + ruv); dst2[1] = convert_uchar_sat(Y3 + guv); - dst2[bidx] = convert_uchar_sat(Y3 + buv); -#if dcn == 4 + dst2[BIDX] = convert_uchar_sat(Y3 + buv); +#if DCN == 4 dst2[3] = 255; #endif Y4 = max(0.f, Y4 - 16.f) * coeffs[0]; - dst2[dcn + 2 - bidx] = convert_uchar_sat(Y4 + ruv); - dst2[dcn + 1] = convert_uchar_sat(Y4 + guv); - dst2[dcn + bidx] = convert_uchar_sat(Y4 + buv); -#if dcn == 4 + dst2[DCN + 2 - BIDX] = convert_uchar_sat(Y4 + ruv); + dst2[DCN + 1] = convert_uchar_sat(Y4 + guv); + dst2[DCN + BIDX] = convert_uchar_sat(Y4 + buv); +#if DCN == 4 dst2[7] = 255; #endif } @@ -401,7 +401,7 @@ __kernel void YUV2RGB_YV12_IYUV(__global const uchar* srcptr, int src_step, int #endif -#if uidx < 2 +#if UIDX < 2 __constant float c_RGB2YUVCoeffs_420[8] = { 0.256999969f, 0.50399971f, 0.09799957f, -0.1479988098f, -0.2909994125f, 0.438999176f, -0.3679990768f, -0.0709991455f }; @@ -415,7 +415,7 @@ __kernel void RGB2YUV_YV12_IYUV(__global const uchar* srcptr, int src_step, int if (x < cols/2) { - int src_index = mad24(y << 1, src_step, mad24(x << 1, scn, src_offset)); + int src_index = mad24(y << 1, src_step, mad24(x << 1, SCN, src_offset)); int ydst_index = mad24(y << 1, dst_step, (x << 1) + dst_offset); int y_rows = rows / 3 * 2; int vsteps[2] = { cols >> 1, dst_step - (cols >> 1)}; @@ -438,56 +438,56 @@ __kernel void RGB2YUV_YV12_IYUV(__global const uchar* srcptr, int src_step, int int s11 = *((__global const int*) src1); int s12 = *((__global const int*) src1 + 1); int s13 = *((__global const int*) src1 + 2); -#if scn == 4 +#if SCN == 4 int s14 = *((__global const int*) src1 + 3); #endif int s21 = *((__global const int*) src2); int s22 = *((__global const int*) src2 + 1); int s23 = *((__global const int*) src2 + 2); -#if scn == 4 +#if SCN == 4 int s24 = *((__global const int*) src2 + 3); #endif - float src_pix1[scn * 4], src_pix2[scn * 4]; + float src_pix1[SCN * 4], src_pix2[SCN * 4]; *((float4*) src_pix1) = convert_float4(as_uchar4(s11)); *((float4*) src_pix1 + 1) = convert_float4(as_uchar4(s12)); *((float4*) src_pix1 + 2) = convert_float4(as_uchar4(s13)); -#if scn == 4 +#if SCN == 4 *((float4*) src_pix1 + 3) = convert_float4(as_uchar4(s14)); #endif *((float4*) src_pix2) = convert_float4(as_uchar4(s21)); *((float4*) src_pix2 + 1) = convert_float4(as_uchar4(s22)); *((float4*) src_pix2 + 2) = convert_float4(as_uchar4(s23)); -#if scn == 4 +#if SCN == 4 *((float4*) src_pix2 + 3) = convert_float4(as_uchar4(s24)); #endif uchar4 y1, y2; - y1.x = convert_uchar_sat(fma(coeffs[0], src_pix1[ 2-bidx], fma(coeffs[1], src_pix1[ 1], fma(coeffs[2], src_pix1[ bidx], 16.5f)))); - y1.y = convert_uchar_sat(fma(coeffs[0], src_pix1[ scn+2-bidx], fma(coeffs[1], src_pix1[ scn+1], fma(coeffs[2], src_pix1[ scn+bidx], 16.5f)))); - y1.z = convert_uchar_sat(fma(coeffs[0], src_pix1[2*scn+2-bidx], fma(coeffs[1], src_pix1[2*scn+1], fma(coeffs[2], src_pix1[2*scn+bidx], 16.5f)))); - y1.w = convert_uchar_sat(fma(coeffs[0], src_pix1[3*scn+2-bidx], fma(coeffs[1], src_pix1[3*scn+1], fma(coeffs[2], src_pix1[3*scn+bidx], 16.5f)))); - y2.x = convert_uchar_sat(fma(coeffs[0], src_pix2[ 2-bidx], fma(coeffs[1], src_pix2[ 1], fma(coeffs[2], src_pix2[ bidx], 16.5f)))); - y2.y = convert_uchar_sat(fma(coeffs[0], src_pix2[ scn+2-bidx], fma(coeffs[1], src_pix2[ scn+1], fma(coeffs[2], src_pix2[ scn+bidx], 16.5f)))); - y2.z = convert_uchar_sat(fma(coeffs[0], src_pix2[2*scn+2-bidx], fma(coeffs[1], src_pix2[2*scn+1], fma(coeffs[2], src_pix2[2*scn+bidx], 16.5f)))); - y2.w = convert_uchar_sat(fma(coeffs[0], src_pix2[3*scn+2-bidx], fma(coeffs[1], src_pix2[3*scn+1], fma(coeffs[2], src_pix2[3*scn+bidx], 16.5f)))); + y1.x = convert_uchar_sat(fma(coeffs[0], src_pix1[ 2-BIDX], fma(coeffs[1], src_pix1[ 1], fma(coeffs[2], src_pix1[ BIDX], 16.5f)))); + y1.y = convert_uchar_sat(fma(coeffs[0], src_pix1[ SCN+2-BIDX], fma(coeffs[1], src_pix1[ SCN+1], fma(coeffs[2], src_pix1[ SCN+BIDX], 16.5f)))); + y1.z = convert_uchar_sat(fma(coeffs[0], src_pix1[2*SCN+2-BIDX], fma(coeffs[1], src_pix1[2*SCN+1], fma(coeffs[2], src_pix1[2*SCN+BIDX], 16.5f)))); + y1.w = convert_uchar_sat(fma(coeffs[0], src_pix1[3*SCN+2-BIDX], fma(coeffs[1], src_pix1[3*SCN+1], fma(coeffs[2], src_pix1[3*SCN+BIDX], 16.5f)))); + y2.x = convert_uchar_sat(fma(coeffs[0], src_pix2[ 2-BIDX], fma(coeffs[1], src_pix2[ 1], fma(coeffs[2], src_pix2[ BIDX], 16.5f)))); + y2.y = convert_uchar_sat(fma(coeffs[0], src_pix2[ SCN+2-BIDX], fma(coeffs[1], src_pix2[ SCN+1], fma(coeffs[2], src_pix2[ SCN+BIDX], 16.5f)))); + y2.z = convert_uchar_sat(fma(coeffs[0], src_pix2[2*SCN+2-BIDX], fma(coeffs[1], src_pix2[2*SCN+1], fma(coeffs[2], src_pix2[2*SCN+BIDX], 16.5f)))); + y2.w = convert_uchar_sat(fma(coeffs[0], src_pix2[3*SCN+2-BIDX], fma(coeffs[1], src_pix2[3*SCN+1], fma(coeffs[2], src_pix2[3*SCN+BIDX], 16.5f)))); *((__global int*) ydst1) = as_int(y1); *((__global int*) ydst2) = as_int(y2); - float uv[4] = { fma(coeffs[3], src_pix1[ 2-bidx], fma(coeffs[4], src_pix1[ 1], fma(coeffs[5], src_pix1[ bidx], 128.5f))), - fma(coeffs[5], src_pix1[ 2-bidx], fma(coeffs[6], src_pix1[ 1], fma(coeffs[7], src_pix1[ bidx], 128.5f))), - fma(coeffs[3], src_pix1[2*scn+2-bidx], fma(coeffs[4], src_pix1[2*scn+1], fma(coeffs[5], src_pix1[2*scn+bidx], 128.5f))), - fma(coeffs[5], src_pix1[2*scn+2-bidx], fma(coeffs[6], src_pix1[2*scn+1], fma(coeffs[7], src_pix1[2*scn+bidx], 128.5f))) }; + float uv[4] = { fma(coeffs[3], src_pix1[ 2-BIDX], fma(coeffs[4], src_pix1[ 1], fma(coeffs[5], src_pix1[ BIDX], 128.5f))), + fma(coeffs[5], src_pix1[ 2-BIDX], fma(coeffs[6], src_pix1[ 1], fma(coeffs[7], src_pix1[ BIDX], 128.5f))), + fma(coeffs[3], src_pix1[2*SCN+2-BIDX], fma(coeffs[4], src_pix1[2*SCN+1], fma(coeffs[5], src_pix1[2*SCN+BIDX], 128.5f))), + fma(coeffs[5], src_pix1[2*SCN+2-BIDX], fma(coeffs[6], src_pix1[2*SCN+1], fma(coeffs[7], src_pix1[2*SCN+BIDX], 128.5f))) }; - udst[0] = convert_uchar_sat(uv[uidx] ); - vdst[0] = convert_uchar_sat(uv[1 - uidx]); - udst[1] = convert_uchar_sat(uv[2 + uidx]); - vdst[1] = convert_uchar_sat(uv[3 - uidx]); + udst[0] = convert_uchar_sat(uv[UIDX] ); + vdst[0] = convert_uchar_sat(uv[1 - UIDX]); + udst[1] = convert_uchar_sat(uv[2 + UIDX]); + vdst[1] = convert_uchar_sat(uv[3 - UIDX]); #else float4 src_pix1 = convert_float4(vload4(0, src1)); - float4 src_pix2 = convert_float4(vload4(0, src1+scn)); + float4 src_pix2 = convert_float4(vload4(0, src1+SCN)); float4 src_pix3 = convert_float4(vload4(0, src2)); - float4 src_pix4 = convert_float4(vload4(0, src2+scn)); + float4 src_pix4 = convert_float4(vload4(0, src2+SCN)); ydst1[0] = convert_uchar_sat(fma(coeffs[0], src_pix1.R_COMP, fma(coeffs[1], src_pix1.G_COMP, fma(coeffs[2], src_pix1.B_COMP, 16.5f)))); ydst1[1] = convert_uchar_sat(fma(coeffs[0], src_pix2.R_COMP, fma(coeffs[1], src_pix2.G_COMP, fma(coeffs[2], src_pix2.B_COMP, 16.5f)))); @@ -497,8 +497,8 @@ __kernel void RGB2YUV_YV12_IYUV(__global const uchar* srcptr, int src_step, int float uv[2] = { fma(coeffs[3], src_pix1.R_COMP, fma(coeffs[4], src_pix1.G_COMP, fma(coeffs[5], src_pix1.B_COMP, 128.5f))), fma(coeffs[5], src_pix1.R_COMP, fma(coeffs[6], src_pix1.G_COMP, fma(coeffs[7], src_pix1.B_COMP, 128.5f))) }; - udst[0] = convert_uchar_sat(uv[uidx] ); - vdst[0] = convert_uchar_sat(uv[1-uidx]); + udst[0] = convert_uchar_sat(uv[UIDX] ); + vdst[0] = convert_uchar_sat(uv[1-UIDX]); #endif ++y; src_index += 2*src_step; @@ -520,7 +520,7 @@ __kernel void YUV2RGB_422(__global const uchar* srcptr, int src_step, int src_of if (x < cols / 2) { __global const uchar* src = srcptr + mad24(y, src_step, (x << 2) + src_offset); - __global uchar* dst = dstptr + mad24(y, dst_step, mad24(x << 1, dcn, dst_offset)); + __global uchar* dst = dstptr + mad24(y, dst_step, mad24(x << 1, DCN, dst_offset)); #pragma unroll for (int cy = 0; cy < PIX_PER_WI_Y; ++cy) @@ -530,34 +530,34 @@ __kernel void YUV2RGB_422(__global const uchar* srcptr, int src_step, int src_of __constant float* coeffs = c_YUV2RGBCoeffs_420; #ifndef USE_OPTIMIZED_LOAD - float U = ((float) src[uidx]) - HALF_MAX_NUM; - float V = ((float) src[(2 + uidx) % 4]) - HALF_MAX_NUM; - float y00 = max(0.f, ((float) src[yidx]) - 16.f) * coeffs[0]; - float y01 = max(0.f, ((float) src[yidx + 2]) - 16.f) * coeffs[0]; + float U = ((float) src[UIDX]) - HALF_MAX_NUM; + float V = ((float) src[(2 + UIDX) % 4]) - HALF_MAX_NUM; + float y00 = max(0.f, ((float) src[YIDX]) - 16.f) * coeffs[0]; + float y01 = max(0.f, ((float) src[YIDX + 2]) - 16.f) * coeffs[0]; #else int load_src = *((__global int*) src); float vec_src[4] = { load_src & 0xff, (load_src >> 8) & 0xff, (load_src >> 16) & 0xff, (load_src >> 24) & 0xff}; - float U = vec_src[uidx] - HALF_MAX_NUM; - float V = vec_src[(2 + uidx) % 4] - HALF_MAX_NUM; - float y00 = max(0.f, vec_src[yidx] - 16.f) * coeffs[0]; - float y01 = max(0.f, vec_src[yidx + 2] - 16.f) * coeffs[0]; + float U = vec_src[UIDX] - HALF_MAX_NUM; + float V = vec_src[(2 + UIDX) % 4] - HALF_MAX_NUM; + float y00 = max(0.f, vec_src[YIDX] - 16.f) * coeffs[0]; + float y01 = max(0.f, vec_src[YIDX + 2] - 16.f) * coeffs[0]; #endif float ruv = fma(coeffs[4], V, 0.5f); float guv = fma(coeffs[3], V, fma(coeffs[2], U, 0.5f)); float buv = fma(coeffs[1], U, 0.5f); - dst[2 - bidx] = convert_uchar_sat(y00 + ruv); + dst[2 - BIDX] = convert_uchar_sat(y00 + ruv); dst[1] = convert_uchar_sat(y00 + guv); - dst[bidx] = convert_uchar_sat(y00 + buv); -#if dcn == 4 + dst[BIDX] = convert_uchar_sat(y00 + buv); +#if DCN == 4 dst[3] = 255; #endif - dst[dcn + 2 - bidx] = convert_uchar_sat(y01 + ruv); - dst[dcn + 1] = convert_uchar_sat(y01 + guv); - dst[dcn + bidx] = convert_uchar_sat(y01 + buv); -#if dcn == 4 + dst[DCN + 2 - BIDX] = convert_uchar_sat(y01 + ruv); + dst[DCN + 1] = convert_uchar_sat(y01 + guv); + dst[DCN + BIDX] = convert_uchar_sat(y01 + buv); +#if DCN == 4 dst[7] = 255; #endif } @@ -600,7 +600,7 @@ __kernel void RGB2YUV_422(__global const uchar* srcptr, int src_step, int src_of __global DATA_TYPE* dst = (__global DATA_TYPE*)(dstptr + dst_index); DATA_TYPE_3 src_pix1 = vload3(0, src); DATA_TYPE b1 = src_pix1.B_COMP, g1 = src_pix1.G_COMP, r1 = src_pix1.R_COMP; - DATA_TYPE_3 src_pix2 = vload3(0, src+scn); + DATA_TYPE_3 src_pix2 = vload3(0, src+SCN); DATA_TYPE b2 = src_pix2.B_COMP, g2 = src_pix2.G_COMP, r2 = src_pix2.R_COMP; @@ -625,10 +625,10 @@ __kernel void RGB2YUV_422(__global const uchar* srcptr, int src_step, int src_of const res_dtype U = MAC_fn(coeffs[5], sr, coeffs[1] + MAC_fn(coeffs[6], sg, mul_fn(coeffs[7], sb))); const res_dtype V = MAC_fn(coeffs[7], sr, coeffs[1] + MAC_fn(coeffs[8], sg, mul_fn(coeffs[9], sb))); - dst[uidx] = output_scale_fn(U); - dst[(2 + uidx) % 4] = output_scale_fn(V); - dst[yidx] = output_scale_fn(Y1); - dst[yidx+2] = output_scale_fn(Y2); + dst[UIDX] = output_scale_fn(U); + dst[(2 + UIDX) % 4] = output_scale_fn(V); + dst[YIDX] = output_scale_fn(Y1); + dst[YIDX+2] = output_scale_fn(Y2); ++y; dst_index += dst_step; @@ -728,10 +728,10 @@ __kernel void YCrCb2RGB(__global const uchar* src, int src_step, int src_offset, int b = yp + CV_DESCALE(coeff[3] * (cb - HALF_MAX_NUM), yuv_shift); #endif - dstptr[(bidx^2)] = SAT_CAST(r); + dstptr[(BIDX^2)] = SAT_CAST(r); dstptr[1] = SAT_CAST(g); - dstptr[bidx] = SAT_CAST(b); -#if dcn == 4 + dstptr[BIDX] = SAT_CAST(b); +#if DCN == 4 dstptr[3] = MAX_NUM; #endif diff --git a/modules/imgproc/src/opencl/filter2DSmall.cl b/modules/imgproc/src/opencl/filter2DSmall.cl index 564bbcfd73..38f23d2fe2 100644 --- a/modules/imgproc/src/opencl/filter2DSmall.cl +++ b/modules/imgproc/src/opencl/filter2DSmall.cl @@ -256,21 +256,21 @@ inline PX_LOAD_FLOAT_VEC_TYPE readSrcPixelGroup(int2 pos, __global const uchar* } // Macros to ensure unrolled loops -#define LOOP1(VAR, STMT) (STMT); (VAR)++; -#define LOOP2(VAR, STMT) LOOP1(VAR, STMT); (STMT); (VAR)++; -#define LOOP3(VAR, STMT) LOOP2(VAR, STMT); (STMT); (VAR)++; -#define LOOP4(VAR, STMT) LOOP3(VAR, STMT); (STMT); (VAR)++; -#define LOOP5(VAR, STMT) LOOP4(VAR, STMT); (STMT); (VAR)++; -#define LOOP6(VAR, STMT) LOOP5(VAR, STMT); (STMT); (VAR)++; -#define LOOP7(VAR, STMT) LOOP6(VAR, STMT); (STMT); (VAR)++; -#define LOOP8(VAR, STMT) LOOP7(VAR, STMT); (STMT); (VAR)++; -#define LOOP9(VAR, STMT) LOOP8(VAR, STMT); (STMT); (VAR)++; -#define LOOP10(VAR, STMT) LOOP9(VAR, STMT); (STMT); (VAR)++; -#define LOOP11(VAR, STMT) LOOP10(VAR, STMT); (STMT); (VAR)++; -#define LOOP12(VAR, STMT) LOOP11(VAR, STMT); (STMT); (VAR)++; -#define LOOP13(VAR, STMT) LOOP12(VAR, STMT); (STMT); (VAR)++; +#define LOOP1(VAR, STMT) STMT; (VAR)++; +#define LOOP2(VAR, STMT) LOOP1(VAR, STMT); STMT; (VAR)++; +#define LOOP3(VAR, STMT) LOOP2(VAR, STMT); STMT; (VAR)++; +#define LOOP4(VAR, STMT) LOOP3(VAR, STMT); STMT; (VAR)++; +#define LOOP5(VAR, STMT) LOOP4(VAR, STMT); STMT; (VAR)++; +#define LOOP6(VAR, STMT) LOOP5(VAR, STMT); STMT; (VAR)++; +#define LOOP7(VAR, STMT) LOOP6(VAR, STMT); STMT; (VAR)++; +#define LOOP8(VAR, STMT) LOOP7(VAR, STMT); STMT; (VAR)++; +#define LOOP9(VAR, STMT) LOOP8(VAR, STMT); STMT; (VAR)++; +#define LOOP10(VAR, STMT) LOOP9(VAR, STMT); STMT; (VAR)++; +#define LOOP11(VAR, STMT) LOOP10(VAR, STMT); STMT; (VAR)++; +#define LOOP12(VAR, STMT) LOOP11(VAR, STMT); STMT; (VAR)++; +#define LOOP13(VAR, STMT) LOOP12(VAR, STMT); STMT; (VAR)++; -#define LOOP(N, VAR, STMT) CAT(LOOP, N)((VAR), (STMT)) +#define LOOP(N, VAR, STMT) CAT(LOOP, N)(VAR, STMT) #define DIG(a) a, __constant WT1 kernelData[] = { COEFF }; diff --git a/modules/imgproc/src/opencl/filterSmall.cl b/modules/imgproc/src/opencl/filterSmall.cl index 222edc6068..2b5cabd3cb 100644 --- a/modules/imgproc/src/opencl/filterSmall.cl +++ b/modules/imgproc/src/opencl/filterSmall.cl @@ -177,21 +177,21 @@ inline PX_LOAD_FLOAT_VEC_TYPE readSrcPixelGroup(int2 pos, __global const uchar * } // Macros to ensure unrolled loops -#define LOOP1(VAR, STMT) (STMT); (VAR)++; -#define LOOP2(VAR, STMT) LOOP1(VAR, STMT); (STMT); (VAR)++; -#define LOOP3(VAR, STMT) LOOP2(VAR, STMT); (STMT); (VAR)++; -#define LOOP4(VAR, STMT) LOOP3(VAR, STMT); (STMT); (VAR)++; -#define LOOP5(VAR, STMT) LOOP4(VAR, STMT); (STMT); (VAR)++; -#define LOOP6(VAR, STMT) LOOP5(VAR, STMT); (STMT); (VAR)++; -#define LOOP7(VAR, STMT) LOOP6(VAR, STMT); (STMT); (VAR)++; -#define LOOP8(VAR, STMT) LOOP7(VAR, STMT); (STMT); (VAR)++; -#define LOOP9(VAR, STMT) LOOP8(VAR, STMT); (STMT); (VAR)++; -#define LOOP10(VAR, STMT) LOOP9(VAR, STMT); (STMT); (VAR)++; -#define LOOP11(VAR, STMT) LOOP10(VAR, STMT); (STMT); (VAR)++; -#define LOOP12(VAR, STMT) LOOP11(VAR, STMT); (STMT); (VAR)++; -#define LOOP13(VAR, STMT) LOOP12(VAR, STMT); (STMT); (VAR)++; +#define LOOP1(VAR, STMT) STMT; (VAR)++; +#define LOOP2(VAR, STMT) LOOP1(VAR, STMT); STMT; (VAR)++; +#define LOOP3(VAR, STMT) LOOP2(VAR, STMT); STMT; (VAR)++; +#define LOOP4(VAR, STMT) LOOP3(VAR, STMT); STMT; (VAR)++; +#define LOOP5(VAR, STMT) LOOP4(VAR, STMT); STMT; (VAR)++; +#define LOOP6(VAR, STMT) LOOP5(VAR, STMT); STMT; (VAR)++; +#define LOOP7(VAR, STMT) LOOP6(VAR, STMT); STMT; (VAR)++; +#define LOOP8(VAR, STMT) LOOP7(VAR, STMT); STMT; (VAR)++; +#define LOOP9(VAR, STMT) LOOP8(VAR, STMT); STMT; (VAR)++; +#define LOOP10(VAR, STMT) LOOP9(VAR, STMT); STMT; (VAR)++; +#define LOOP11(VAR, STMT) LOOP10(VAR, STMT); STMT; (VAR)++; +#define LOOP12(VAR, STMT) LOOP11(VAR, STMT); STMT; (VAR)++; +#define LOOP13(VAR, STMT) LOOP12(VAR, STMT); STMT; (VAR)++; -#define LOOP(N, VAR, STMT) CAT(LOOP, N)((VAR), (STMT)) +#define LOOP(N, VAR, STMT) CAT(LOOP, N)(VAR, STMT) #ifdef OP_BOX_FILTER #define PROCESS_ELEM \ diff --git a/modules/imgproc/src/opencl/laplacian5.cl b/modules/imgproc/src/opencl/laplacian5.cl index 1404a8c51e..d6f7b9e664 100644 --- a/modules/imgproc/src/opencl/laplacian5.cl +++ b/modules/imgproc/src/opencl/laplacian5.cl @@ -13,25 +13,25 @@ __kernel void sumConvert(__global const uchar * src1ptr, int src1_step, int src1_offset, __global const uchar * src2ptr, int src2_step, int src2_offset, __global uchar * dstptr, int dst_step, int dst_offset, int dst_rows, int dst_cols, - coeffT scale, coeffT delta) + COEFF_T scale, COEFF_T delta) { int x = get_global_id(0); int y = get_global_id(1); if (y < dst_rows && x < dst_cols) { - int src1_index = mad24(y, src1_step, mad24(x, (int)sizeof(srcT), src1_offset)); - int src2_index = mad24(y, src2_step, mad24(x, (int)sizeof(srcT), src2_offset)); - int dst_index = mad24(y, dst_step, mad24(x, (int)sizeof(dstT), dst_offset)); + int src1_index = mad24(y, src1_step, mad24(x, (int)sizeof(SRC_T), src1_offset)); + int src2_index = mad24(y, src2_step, mad24(x, (int)sizeof(SRC_T), src2_offset)); + int dst_index = mad24(y, dst_step, mad24(x, (int)sizeof(DST_T), dst_offset)); - __global const srcT * src1 = (__global const srcT *)(src1ptr + src1_index); - __global const srcT * src2 = (__global const srcT *)(src2ptr + src2_index); - __global dstT * dst = (__global dstT *)(dstptr + dst_index); + __global const SRC_T * src1 = (__global const SRC_T *)(src1ptr + src1_index); + __global const SRC_T * src2 = (__global const SRC_T *)(src2ptr + src2_index); + __global DST_T * dst = (__global DST_T *)(dstptr + dst_index); -#if wdepth <= 4 - dst[0] = convertToDT( mad24((WT)(scale), convertToWT(src1[0]) + convertToWT(src2[0]), (WT)(delta)) ); +#if WDEPTH <= 4 + dst[0] = CONVERT_TO_DT( mad24((WT)(scale), CONVERT_TO_WT(src1[0]) + CONVERT_TO_WT(src2[0]), (WT)(delta)) ); #else - dst[0] = convertToDT( mad((WT)(scale), convertToWT(src1[0]) + convertToWT(src2[0]), (WT)(delta)) ); + dst[0] = CONVERT_TO_DT( mad((WT)(scale), CONVERT_TO_WT(src1[0]) + CONVERT_TO_WT(src2[0]), (WT)(delta)) ); #endif } } @@ -74,18 +74,18 @@ __kernel void sumConvert(__global const uchar * src1ptr, int src1_step, int src1 #endif #if CN != 3 -#define loadpix(addr) *(__global const srcT *)(addr) -#define storepix(val, addr) *(__global dstT *)(addr) = val -#define SRCSIZE (int)sizeof(srcT) -#define DSTSIZE (int)sizeof(dstT) +#define loadpix(addr) *(__global const SRC_T *)(addr) +#define storepix(val, addr) *(__global DST_T *)(addr) = val +#define SRCSIZE (int)sizeof(SRC_T) +#define DSTSIZE (int)sizeof(DST_T) #else -#define loadpix(addr) vload3(0, (__global const srcT1 *)(addr)) -#define storepix(val, addr) vstore3(val, 0, (__global dstT1 *)(addr)) -#define SRCSIZE (int)sizeof(srcT1)*3 -#define DSTSIZE (int)sizeof(dstT1)*3 +#define loadpix(addr) vload3(0, (__global const SRC_T1 *)(addr)) +#define storepix(val, addr) vstore3(val, 0, (__global DST_T1 *)(addr)) +#define SRCSIZE (int)sizeof(SRC_T1)*3 +#define DSTSIZE (int)sizeof(DST_T1)*3 #endif -#define SRC(_x,_y) convertToWT(loadpix(Src + mad24(_y, src_step, SRCSIZE * _x))) +#define SRC(_x,_y) CONVERT_TO_WT(loadpix(Src + mad24(_y, src_step, SRCSIZE * _x))) #ifdef BORDER_CONSTANT // CCCCCC|abcdefgh|CCCCCCC @@ -173,7 +173,7 @@ __kernel void laplacian(__global uchar* Src, int src_step, int srcOffsetX, int s } WT sum = mad(scale_v, (sum1 + sum2), delta_v); - storepix(convertToDT(sum), Dst + mad24(y + liy, dst_step, mad24(x, DSTSIZE, dst_offset))); + storepix(CONVERT_TO_DT(sum), Dst + mad24(y + liy, dst_step, mad24(x, DSTSIZE, dst_offset))); } for (int i = liy * BLK_X + lix; i < (RADIUS*2) * (BLK_X+(RADIUS*2)); i += BLK_X * BLK_Y) @@ -203,4 +203,4 @@ __kernel void laplacian(__global uchar* Src, int src_step, int srcOffsetX, int s } } -#endif \ No newline at end of file +#endif diff --git a/modules/imgproc/src/opencl/pyr_down.cl b/modules/imgproc/src/opencl/pyr_down.cl index 5d2e7156ef..22b1472b3e 100644 --- a/modules/imgproc/src/opencl/pyr_down.cl +++ b/modules/imgproc/src/opencl/pyr_down.cl @@ -67,7 +67,7 @@ #error No extrapolation method #endif -#if cn != 3 +#if CN != 3 #define loadpix(addr) *(__global const T*)(addr) #define storepix(val, addr) *(__global T*)(addr) = (val) #define PIXSIZE ((int)sizeof(T)) @@ -77,9 +77,9 @@ #define PIXSIZE ((int)sizeof(T1)*3) #endif -#define SRC(_x,_y) convertToFT(loadpix(srcData + mad24(_y, src_step, PIXSIZE * _x))) +#define SRC(_x,_y) CONVERT_TO_FT(loadpix(srcData + mad24(_y, src_step, PIXSIZE * _x))) -#if kercn == 4 +#if KERCN == 4 #define SRC4(_x,_y) convert_float4(vload4(0, srcData + mad24(_y, src_step, PIXSIZE * _x))) #endif @@ -107,7 +107,7 @@ smem[1][col_lcl] = sum1; -#if kercn == 4 +#if KERCN == 4 #define LOAD_LOCAL4(col_gl, col_lcl) \ sum40 = co3* SRC4(col_gl, EXTRAPOLATE_(src_y - 2, src_rows)); \ sum40 = MAD(co2, SRC4(col_gl, EXTRAPOLATE_(src_y - 1, src_rows)), sum40); \ @@ -131,7 +131,7 @@ __kernel void pyrDown(__global const uchar * src, int src_step, int src_offset, int src_rows, int src_cols, __global uchar * dst, int dst_step, int dst_offset, int dst_rows, int dst_cols) { - const int x = get_global_id(0)*kercn; + const int x = get_global_id(0)*KERCN; const int y = 2*get_global_id(1); __local FT smem[2][LOCAL_SIZE + 4]; @@ -150,7 +150,7 @@ __kernel void pyrDown(__global const uchar * src, int src_step, int src_offset, { #undef EXTRAPOLATE_ #define EXTRAPOLATE_(val, maxVal) val -#if kercn == 1 +#if KERCN == 1 col = EXTRAPOLATE(x, src_cols); LOAD_LOCAL(col, 2 + get_local_id(0)) #else @@ -183,7 +183,7 @@ __kernel void pyrDown(__global const uchar * src, int src_step, int src_offset, { #undef EXTRAPOLATE_ #define EXTRAPOLATE_(val, maxVal) EXTRAPOLATE(val, maxVal) -#if kercn == 1 +#if KERCN == 1 col = EXTRAPOLATE(x, src_cols); LOAD_LOCAL(col, 2 + get_local_id(0)) #else @@ -215,7 +215,7 @@ __kernel void pyrDown(__global const uchar * src, int src_step, int src_offset, barrier(CLK_LOCAL_MEM_FENCE); -#if kercn == 1 +#if KERCN == 1 if (get_local_id(0) < LOCAL_SIZE / 2) { const int tid2 = get_local_id(0) * 2; @@ -226,8 +226,8 @@ __kernel void pyrDown(__global const uchar * src, int src_step, int src_offset, { for (int yin = y, y1 = min(dst_rows, y + 2); yin < y1; yin++) { -#if cn == 1 -#if fdepth <= 5 +#if CN == 1 +#if FDEPTH <= 5 FT sum = dot(vload4(0, (__local float*) (&smem) + tid2 + (yin - y) * (LOCAL_SIZE + 4)), (float4)(co3, co2, co1, co2)); #else FT sum = dot(vload4(0, (__local double*) (&smem) + tid2 + (yin - y) * (LOCAL_SIZE + 4)), (double4)(co3, co2, co1, co2)); @@ -239,7 +239,7 @@ __kernel void pyrDown(__global const uchar * src, int src_step, int src_offset, sum = MAD(co2, smem[yin - y][2 + tid2 + 1], sum); #endif sum = MAD(co3, smem[yin - y][2 + tid2 + 2], sum); - storepix(convertToT(sum), dstData + yin * dst_step + dst_x * PIXSIZE); + storepix(CONVERT_TO_T(sum), dstData + yin * dst_step + dst_x * PIXSIZE); } } } @@ -256,7 +256,7 @@ __kernel void pyrDown(__global const uchar * src, int src_step, int src_offset, sum = MAD(co2, smem[yin - y][2 + tid4 - 1], sum); sum = MAD(co1, smem[yin - y][2 + tid4 ], sum); sum = MAD(co2, smem[yin - y][2 + tid4 + 1], sum); - storepix(convertToT(sum), dstData + mad24(yin, dst_step, dst_x * PIXSIZE)); + storepix(CONVERT_TO_T(sum), dstData + mad24(yin, dst_step, dst_x * PIXSIZE)); dst_x ++; sum = co3* smem[yin - y][2 + tid4 + 4]; @@ -264,7 +264,7 @@ __kernel void pyrDown(__global const uchar * src, int src_step, int src_offset, sum = MAD(co2, smem[yin - y][2 + tid4 + 1], sum); sum = MAD(co1, smem[yin - y][2 + tid4 + 2], sum); sum = MAD(co2, smem[yin - y][2 + tid4 + 3], sum); - storepix(convertToT(sum), dstData + mad24(yin, dst_step, dst_x * PIXSIZE)); + storepix(CONVERT_TO_T(sum), dstData + mad24(yin, dst_step, dst_x * PIXSIZE)); dst_x --; } @@ -279,7 +279,7 @@ __kernel void pyrDown(__global const uchar * src, int src_step, int src_offset, sum = MAD(co1, smem[yin - y][2 + tid4 ], sum); sum = MAD(co2, smem[yin - y][2 + tid4 + 1], sum); - storepix(convertToT(sum), dstData + mad24(yin, dst_step, dst_x * PIXSIZE)); + storepix(CONVERT_TO_T(sum), dstData + mad24(yin, dst_step, dst_x * PIXSIZE)); } } #endif diff --git a/modules/imgproc/src/opencl/pyr_up.cl b/modules/imgproc/src/opencl/pyr_up.cl index d033d7ee4e..194be1c830 100644 --- a/modules/imgproc/src/opencl/pyr_up.cl +++ b/modules/imgproc/src/opencl/pyr_up.cl @@ -58,7 +58,7 @@ #endif #endif -#if cn != 3 +#if CN != 3 #define loadpix(addr) *(__global const T*)(addr) #define storepix(val, addr) *(__global T*)(addr) = (val) #define PIXSIZE ((int)sizeof(T)) @@ -92,7 +92,7 @@ __kernel void pyrUp(__global const uchar * src, int src_step, int src_offset, in int srcx = EXTRAPOLATE(mad24((int)get_group_id(0), LOCAL_SIZE/2, tidx) - 1, src_cols); int srcy = EXTRAPOLATE(mad24((int)get_group_id(1), LOCAL_SIZE/2, tidy) - 1, src_rows); - s_srcPatch[tidy][tidx] = convertToFT(loadpix(srcData + srcy * src_step + srcx * PIXSIZE)); + s_srcPatch[tidy][tidx] = CONVERT_TO_FT(loadpix(srcData + srcy * src_step + srcx * PIXSIZE)); } barrier(CLK_LOCAL_MEM_FENCE); @@ -124,7 +124,7 @@ __kernel void pyrUp(__global const uchar * src, int src_step, int src_offset, in sum = mad(coefy2, s_dstPatch[1 + ((tidy + 2) >> 1)][tidx], sum); if ((x < dst_cols) && (y < dst_rows)) - storepix(convertToT(sum), dstData + y * dst_step + x * PIXSIZE); + storepix(CONVERT_TO_T(sum), dstData + y * dst_step + x * PIXSIZE); } @@ -149,10 +149,10 @@ __kernel void pyrUp_unrolled(__global const uchar * src, int src_step, int src_o int srcx2 = EXTRAPOLATE(srcx+1, src_cols); int srcy1 = EXTRAPOLATE(srcy, src_rows); int srcy2 = EXTRAPOLATE(srcy+1, src_rows); - s_srcPatch[ly][lx] = convertToFT(loadpix(srcData + srcy1 * src_step + srcx1 * PIXSIZE)); - s_srcPatch[ly+1][lx] = convertToFT(loadpix(srcData + srcy2 * src_step + srcx1 * PIXSIZE)); - s_srcPatch[ly][lx+1] = convertToFT(loadpix(srcData + srcy1 * src_step + srcx2 * PIXSIZE)); - s_srcPatch[ly+1][lx+1] = convertToFT(loadpix(srcData + srcy2 * src_step + srcx2 * PIXSIZE)); + s_srcPatch[ly][lx] = CONVERT_TO_FT(loadpix(srcData + srcy1 * src_step + srcx1 * PIXSIZE)); + s_srcPatch[ly+1][lx] = CONVERT_TO_FT(loadpix(srcData + srcy2 * src_step + srcx1 * PIXSIZE)); + s_srcPatch[ly][lx+1] = CONVERT_TO_FT(loadpix(srcData + srcy1 * src_step + srcx2 * PIXSIZE)); + s_srcPatch[ly+1][lx+1] = CONVERT_TO_FT(loadpix(srcData + srcy2 * src_step + srcx2 * PIXSIZE)); } barrier(CLK_LOCAL_MEM_FENCE); @@ -213,22 +213,22 @@ __kernel void pyrUp_unrolled(__global const uchar * src, int src_step, int src_o sum = co3 * s_dstPatch[1 + get_local_id(1) - 1][lx]; sum = mad(co1, s_dstPatch[1 + get_local_id(1) ][lx], sum); sum = mad(co3, s_dstPatch[1 + get_local_id(1) + 1][lx], sum); - storepix(convertToT(sum), dstData + dst_y * dst_step + dst_x * PIXSIZE); + storepix(CONVERT_TO_T(sum), dstData + dst_y * dst_step + dst_x * PIXSIZE); // (x+1,y) sum = co3 * s_dstPatch[1 + get_local_id(1) - 1][lx+1]; sum = mad(co1, s_dstPatch[1 + get_local_id(1) ][lx+1], sum); sum = mad(co3, s_dstPatch[1 + get_local_id(1) + 1][lx+1], sum); - storepix(convertToT(sum), dstData + dst_y * dst_step + (dst_x+1) * PIXSIZE); + storepix(CONVERT_TO_T(sum), dstData + dst_y * dst_step + (dst_x+1) * PIXSIZE); // (x,y+1) sum = co2 * s_dstPatch[1 + get_local_id(1) ][lx]; sum = mad(co2, s_dstPatch[1 + get_local_id(1) + 1][lx], sum); - storepix(convertToT(sum), dstData + (dst_y+1) * dst_step + dst_x * PIXSIZE); + storepix(CONVERT_TO_T(sum), dstData + (dst_y+1) * dst_step + dst_x * PIXSIZE); // (x+1,y+1) sum = co2 * s_dstPatch[1 + get_local_id(1) ][lx+1]; sum = mad(co2, s_dstPatch[1 + get_local_id(1) + 1][lx+1], sum); - storepix(convertToT(sum), dstData + (dst_y+1) * dst_step + (dst_x+1) * PIXSIZE); + storepix(CONVERT_TO_T(sum), dstData + (dst_y+1) * dst_step + (dst_x+1) * PIXSIZE); } } diff --git a/modules/imgproc/src/opencl/remap.cl b/modules/imgproc/src/opencl/remap.cl index 1a30c326b9..8c8b933ab5 100644 --- a/modules/imgproc/src/opencl/remap.cl +++ b/modules/imgproc/src/opencl/remap.cl @@ -53,7 +53,7 @@ #define noconvert -#if cn != 3 +#if CN != 3 #define loadpix(addr) *(__global const T*)(addr) #define storepix(val, addr) *(__global T*)(addr) = val #define TSIZE ((int)sizeof(T)) @@ -73,7 +73,7 @@ enum }; #ifdef INTER_NEAREST -#define convertToWT +#define CONVERT_TO_WT #endif #ifdef BORDER_CONSTANT @@ -82,7 +82,7 @@ enum #define EXTRAPOLATE(v2, v) \ { \ v2 = max(min(v2, (int2)(src_cols - 1, src_rows - 1)), (int2)(0)); \ - v = convertToWT(loadpix((__global const T*)(srcptr + mad24(v2.y, src_step, v2.x * TSIZE + src_offset)))); \ + v = CONVERT_TO_WT(loadpix((__global const T*)(srcptr + mad24(v2.y, src_step, v2.x * TSIZE + src_offset)))); \ } #elif defined BORDER_WRAP #define EXTRAPOLATE(v2, v) \ @@ -96,7 +96,7 @@ enum v2.y -= ((v2.y - src_rows + 1) / src_rows) * src_rows; \ if( v2.y >= src_rows ) \ v2.y %= src_rows; \ - v = convertToWT(loadpix((__global const T*)(srcptr + mad24(v2.y, src_step, v2.x * TSIZE + src_offset)))); \ + v = CONVERT_TO_WT(loadpix((__global const T*)(srcptr + mad24(v2.y, src_step, v2.x * TSIZE + src_offset)))); \ } #elif defined(BORDER_REFLECT) || defined(BORDER_REFLECT_101) #ifdef BORDER_REFLECT @@ -130,7 +130,7 @@ enum v2.y = src_rows - 1 - (v2.y - src_rows) - delta; \ } \ while (v2.y >= src_rows || v2.y < 0); \ - v = convertToWT(loadpix((__global const T*)(srcptr + mad24(v2.y, src_step, v2.x * TSIZE + src_offset)))); \ + v = CONVERT_TO_WT(loadpix((__global const T*)(srcptr + mad24(v2.y, src_step, v2.x * TSIZE + src_offset)))); \ } #else #error No extrapolation method @@ -147,7 +147,7 @@ __kernel void remap_2_32FC1(__global const uchar * srcptr, int src_step, int src ST nVal) { int x = get_global_id(0); - int y = get_global_id(1) * rowsPerWI; + int y = get_global_id(1) * ROWS_PER_WI; if (x < dst_cols) { @@ -158,7 +158,7 @@ __kernel void remap_2_32FC1(__global const uchar * srcptr, int src_step, int src int dst_index = mad24(y, dst_step, mad24(x, TSIZE, dst_offset)); #pragma unroll - for (int i = 0; i < rowsPerWI; ++i, ++y, + for (int i = 0; i < ROWS_PER_WI; ++i, ++y, map1_index += map1_step, map2_index += map2_step, dst_index += dst_step) if (y < dst_rows) { @@ -193,7 +193,7 @@ __kernel void remap_32FC2(__global const uchar * srcptr, int src_step, int src_o ST nVal) { int x = get_global_id(0); - int y = get_global_id(1) * rowsPerWI; + int y = get_global_id(1) * ROWS_PER_WI; if (x < dst_cols) { @@ -202,7 +202,7 @@ __kernel void remap_32FC2(__global const uchar * srcptr, int src_step, int src_o int map_index = mad24(y, map_step, mad24(x, (int)sizeof(float2), map_offset)); #pragma unroll - for (int i = 0; i < rowsPerWI; ++i, ++y, + for (int i = 0; i < ROWS_PER_WI; ++i, ++y, map_index += map_step, dst_index += dst_step) if (y < dst_rows) { @@ -233,7 +233,7 @@ __kernel void remap_16SC2(__global const uchar * srcptr, int src_step, int src_o ST nVal) { int x = get_global_id(0); - int y = get_global_id(1) * rowsPerWI; + int y = get_global_id(1) * ROWS_PER_WI; if (x < dst_cols) { @@ -242,7 +242,7 @@ __kernel void remap_16SC2(__global const uchar * srcptr, int src_step, int src_o int map_index = mad24(y, map_step, mad24(x, (int)sizeof(short2), map_offset)); #pragma unroll - for (int i = 0; i < rowsPerWI; ++i, ++y, + for (int i = 0; i < ROWS_PER_WI; ++i, ++y, map_index += map_step, dst_index += dst_step) if (y < dst_rows) { @@ -274,7 +274,7 @@ __kernel void remap_16SC2_16UC1(__global const uchar * srcptr, int src_step, int ST nVal) { int x = get_global_id(0); - int y = get_global_id(1) * rowsPerWI; + int y = get_global_id(1) * ROWS_PER_WI; if (x < dst_cols) { @@ -284,7 +284,7 @@ __kernel void remap_16SC2_16UC1(__global const uchar * srcptr, int src_step, int int map2_index = mad24(y, map2_step, mad24(x, (int)sizeof(ushort), map2_offset)); #pragma unroll - for (int i = 0; i < rowsPerWI; ++i, ++y, + for (int i = 0; i < ROWS_PER_WI; ++i, ++y, map1_index += map1_step, map2_index += map2_step, dst_index += dst_step) if (y < dst_rows) { @@ -330,17 +330,17 @@ __kernel void remap_16SC2_16UC1(__global const uchar * srcptr, int src_step, int ST nVal) { int x = get_global_id(0); - int y = get_global_id(1) * rowsPerWI; + int y = get_global_id(1) * ROWS_PER_WI; if (x < dst_cols) { - WT scalar = convertToWT(convertScalar(nVal)); + WT scalar = CONVERT_TO_WT(convertScalar(nVal)); int dst_index = mad24(y, dst_step, mad24(x, TSIZE, dst_offset)); int map1_index = mad24(y, map1_step, mad24(x, (int)sizeof(short2), map1_offset)); int map2_index = mad24(y, map2_step, mad24(x, (int)sizeof(ushort), map2_offset)); #pragma unroll - for (int i = 0; i < rowsPerWI; ++i, ++y, + for (int i = 0; i < ROWS_PER_WI; ++i, ++y, map1_index += map1_step, map2_index += map2_step, dst_index += dst_step) if (y < dst_rows) { @@ -359,22 +359,22 @@ __kernel void remap_16SC2_16UC1(__global const uchar * srcptr, int src_step, int WT a = scalar, b = scalar, c = scalar, d = scalar; if (!NEED_EXTRAPOLATION(map_dataA.x, map_dataA.y)) - a = convertToWT(loadpix((__global const T *)(srcptr + mad24(map_dataA.y, src_step, map_dataA.x * TSIZE + src_offset)))); + a = CONVERT_TO_WT(loadpix((__global const T *)(srcptr + mad24(map_dataA.y, src_step, map_dataA.x * TSIZE + src_offset)))); else EXTRAPOLATE(map_dataA, a); if (!NEED_EXTRAPOLATION(map_dataB.x, map_dataB.y)) - b = convertToWT(loadpix((__global const T *)(srcptr + mad24(map_dataB.y, src_step, map_dataB.x * TSIZE + src_offset)))); + b = CONVERT_TO_WT(loadpix((__global const T *)(srcptr + mad24(map_dataB.y, src_step, map_dataB.x * TSIZE + src_offset)))); else EXTRAPOLATE(map_dataB, b); if (!NEED_EXTRAPOLATION(map_dataC.x, map_dataC.y)) - c = convertToWT(loadpix((__global const T *)(srcptr + mad24(map_dataC.y, src_step, map_dataC.x * TSIZE + src_offset)))); + c = CONVERT_TO_WT(loadpix((__global const T *)(srcptr + mad24(map_dataC.y, src_step, map_dataC.x * TSIZE + src_offset)))); else EXTRAPOLATE(map_dataC, c); if (!NEED_EXTRAPOLATION(map_dataD.x, map_dataD.y)) - d = convertToWT(loadpix((__global const T *)(srcptr + mad24(map_dataD.y, src_step, map_dataD.x * TSIZE + src_offset)))); + d = CONVERT_TO_WT(loadpix((__global const T *)(srcptr + mad24(map_dataD.y, src_step, map_dataD.x * TSIZE + src_offset)))); else EXTRAPOLATE(map_dataD, d); @@ -382,7 +382,7 @@ __kernel void remap_16SC2_16UC1(__global const uchar * srcptr, int src_step, int b * (u.x) * (1 - u.y) + c * (1 - u.x) * (u.y) + d * (u.x) * (u.y); - storepix(convertToT(dst_data), dst); + storepix(CONVERT_TO_T(dst_data), dst); } } } @@ -394,17 +394,17 @@ __kernel void remap_2_32FC1(__global const uchar * srcptr, int src_step, int src ST nVal) { int x = get_global_id(0); - int y = get_global_id(1) * rowsPerWI; + int y = get_global_id(1) * ROWS_PER_WI; if (x < dst_cols) { - WT scalar = convertToWT(convertScalar(nVal)); + WT scalar = CONVERT_TO_WT(convertScalar(nVal)); int dst_index = mad24(y, dst_step, mad24(x, TSIZE, dst_offset)); int map1_index = mad24(y, map1_step, mad24(x, (int)sizeof(float), map1_offset)); int map2_index = mad24(y, map2_step, mad24(x, (int)sizeof(float), map2_offset)); #pragma unroll - for (int i = 0; i < rowsPerWI; ++i, ++y, + for (int i = 0; i < ROWS_PER_WI; ++i, ++y, map1_index += map1_step, map2_index += map2_step, dst_index += dst_step) if (y < dst_rows) { @@ -431,13 +431,13 @@ __kernel void remap_2_32FC1(__global const uchar * srcptr, int src_step, int src xsum = (WT)(0); if (sx >= 0 && sx + 2 < src_cols) { -#if depth == 0 && cn == 1 +#if SRC_DEPTH == 0 && CN == 1 uchar2 value = vload2(0, srcptr + src_index); xsum = dot(convert_float2(value), (float2)(coeffs_x[0], coeffs_x[1])); #else #pragma unroll for (int xp = 0; xp < 2; ++xp) - xsum = fma(convertToWT(loadpix(srcptr + mad24(xp, TSIZE, src_index))), coeffs_x[xp], xsum); + xsum = fma(CONVERT_TO_WT(loadpix(srcptr + mad24(xp, TSIZE, src_index))), coeffs_x[xp], xsum); #endif } else @@ -445,7 +445,7 @@ __kernel void remap_2_32FC1(__global const uchar * srcptr, int src_step, int src #pragma unroll for (int xp = 0; xp < 2; ++xp) xsum = fma(sx + xp >= 0 && sx + xp < src_cols ? - convertToWT(loadpix(srcptr + mad24(xp, TSIZE, src_index))) : scalar, coeffs_x[xp], xsum); + CONVERT_TO_WT(loadpix(srcptr + mad24(xp, TSIZE, src_index))) : scalar, coeffs_x[xp], xsum); } sum = fma(xsum, coeffs_y[yp], sum); } @@ -453,7 +453,7 @@ __kernel void remap_2_32FC1(__global const uchar * srcptr, int src_step, int src sum = fma(scalar, coeffs_y[yp], sum); } - storepix(convertToT(sum), dst); + storepix(CONVERT_TO_T(sum), dst); #else float2 map_data = (float2)(map1[0], map2[0]); @@ -463,27 +463,27 @@ __kernel void remap_2_32FC1(__global const uchar * srcptr, int src_step, int src int2 map_dataD = (int2)(map_dataA.x + 1, map_dataA.y + 1); float2 _u = map_data - convert_float2(map_dataA); - WT2 u = convertToWT2(convert_int2_rte(convertToWT2(_u) * (WT2)INTER_TAB_SIZE)) / (WT2)INTER_TAB_SIZE; - WT scalar = convertToWT(convertScalar(nVal)); + WT2 u = CONVERT_TO_WT2(convert_int2_rte(CONVERT_TO_WT2(_u) * (WT2)INTER_TAB_SIZE)) / (WT2)INTER_TAB_SIZE; + WT scalar = CONVERT_TO_WT(convertScalar(nVal)); WT a = scalar, b = scalar, c = scalar, d = scalar; if (!NEED_EXTRAPOLATION(map_dataA.x, map_dataA.y)) - a = convertToWT(loadpix((__global const T *)(srcptr + mad24(map_dataA.y, src_step, map_dataA.x * TSIZE + src_offset)))); + a = CONVERT_TO_WT(loadpix((__global const T *)(srcptr + mad24(map_dataA.y, src_step, map_dataA.x * TSIZE + src_offset)))); else EXTRAPOLATE(map_dataA, a); if (!NEED_EXTRAPOLATION(map_dataB.x, map_dataB.y)) - b = convertToWT(loadpix((__global const T *)(srcptr + mad24(map_dataB.y, src_step, map_dataB.x * TSIZE + src_offset)))); + b = CONVERT_TO_WT(loadpix((__global const T *)(srcptr + mad24(map_dataB.y, src_step, map_dataB.x * TSIZE + src_offset)))); else EXTRAPOLATE(map_dataB, b); if (!NEED_EXTRAPOLATION(map_dataC.x, map_dataC.y)) - c = convertToWT(loadpix((__global const T *)(srcptr + mad24(map_dataC.y, src_step, map_dataC.x * TSIZE + src_offset)))); + c = CONVERT_TO_WT(loadpix((__global const T *)(srcptr + mad24(map_dataC.y, src_step, map_dataC.x * TSIZE + src_offset)))); else EXTRAPOLATE(map_dataC, c); if (!NEED_EXTRAPOLATION(map_dataD.x, map_dataD.y)) - d = convertToWT(loadpix((__global const T *)(srcptr + mad24(map_dataD.y, src_step, map_dataD.x * TSIZE + src_offset)))); + d = CONVERT_TO_WT(loadpix((__global const T *)(srcptr + mad24(map_dataD.y, src_step, map_dataD.x * TSIZE + src_offset)))); else EXTRAPOLATE(map_dataD, d); @@ -491,7 +491,7 @@ __kernel void remap_2_32FC1(__global const uchar * srcptr, int src_step, int src b * (u.x) * (1 - u.y) + c * (1 - u.x) * (u.y) + d * (u.x) * (u.y); - storepix(convertToT(dst_data), dst); + storepix(CONVERT_TO_T(dst_data), dst); #endif } } @@ -503,16 +503,16 @@ __kernel void remap_32FC2(__global const uchar * srcptr, int src_step, int src_o ST nVal) { int x = get_global_id(0); - int y = get_global_id(1) * rowsPerWI; + int y = get_global_id(1) * ROWS_PER_WI; if (x < dst_cols) { - WT scalar = convertToWT(convertScalar(nVal)); + WT scalar = CONVERT_TO_WT(convertScalar(nVal)); int dst_index = mad24(y, dst_step, mad24(x, TSIZE, dst_offset)); int map_index = mad24(y, map_step, mad24(x, (int)sizeof(float2), map_offset)); #pragma unroll - for (int i = 0; i < rowsPerWI; ++i, ++y, + for (int i = 0; i < ROWS_PER_WI; ++i, ++y, map_index += map_step, dst_index += dst_step) if (y < dst_rows) { @@ -526,26 +526,26 @@ __kernel void remap_32FC2(__global const uchar * srcptr, int src_step, int src_o int2 map_dataD = (int2)(map_dataA.x + 1, map_dataA.y + 1); float2 _u = map_data - convert_float2(map_dataA); - WT2 u = convertToWT2(convert_int2_rte(convertToWT2(_u) * (WT2)INTER_TAB_SIZE)) / (WT2)INTER_TAB_SIZE; + WT2 u = CONVERT_TO_WT2(convert_int2_rte(CONVERT_TO_WT2(_u) * (WT2)INTER_TAB_SIZE)) / (WT2)INTER_TAB_SIZE; WT a = scalar, b = scalar, c = scalar, d = scalar; if (!NEED_EXTRAPOLATION(map_dataA.x, map_dataA.y)) - a = convertToWT(loadpix((__global const T *)(srcptr + mad24(map_dataA.y, src_step, map_dataA.x * TSIZE + src_offset)))); + a = CONVERT_TO_WT(loadpix((__global const T *)(srcptr + mad24(map_dataA.y, src_step, map_dataA.x * TSIZE + src_offset)))); else EXTRAPOLATE(map_dataA, a); if (!NEED_EXTRAPOLATION(map_dataB.x, map_dataB.y)) - b = convertToWT(loadpix((__global const T *)(srcptr + mad24(map_dataB.y, src_step, map_dataB.x * TSIZE + src_offset)))); + b = CONVERT_TO_WT(loadpix((__global const T *)(srcptr + mad24(map_dataB.y, src_step, map_dataB.x * TSIZE + src_offset)))); else EXTRAPOLATE(map_dataB, b); if (!NEED_EXTRAPOLATION(map_dataC.x, map_dataC.y)) - c = convertToWT(loadpix((__global const T *)(srcptr + mad24(map_dataC.y, src_step, map_dataC.x * TSIZE + src_offset)))); + c = CONVERT_TO_WT(loadpix((__global const T *)(srcptr + mad24(map_dataC.y, src_step, map_dataC.x * TSIZE + src_offset)))); else EXTRAPOLATE(map_dataC, c); if (!NEED_EXTRAPOLATION(map_dataD.x, map_dataD.y)) - d = convertToWT(loadpix((__global const T *)(srcptr + mad24(map_dataD.y, src_step, map_dataD.x * TSIZE + src_offset)))); + d = CONVERT_TO_WT(loadpix((__global const T *)(srcptr + mad24(map_dataD.y, src_step, map_dataD.x * TSIZE + src_offset)))); else EXTRAPOLATE(map_dataD, d); @@ -553,7 +553,7 @@ __kernel void remap_32FC2(__global const uchar * srcptr, int src_step, int src_o b * (u.x) * (1 - u.y) + c * (1 - u.x) * (u.y) + d * (u.x) * (u.y); - storepix(convertToT(dst_data), dst); + storepix(CONVERT_TO_T(dst_data), dst); } } } diff --git a/modules/imgproc/src/opencl/resize.cl b/modules/imgproc/src/opencl/resize.cl index a28c59296e..e31c80dc3b 100644 --- a/modules/imgproc/src/opencl/resize.cl +++ b/modules/imgproc/src/opencl/resize.cl @@ -55,44 +55,44 @@ #define noconvert -#if cn != 3 +#if CN != 3 #define loadpix(addr) *(__global const T *)(addr) #define storepix(val, addr) *(__global T *)(addr) = val #define TSIZE (int)sizeof(T) #else #define loadpix(addr) vload3(0, (__global const T1 *)(addr)) #define storepix(val, addr) vstore3(val, 0, (__global T1 *)(addr)) -#define TSIZE (int)sizeof(T1)*cn +#define TSIZE (int)sizeof(T1)*CN #endif #if defined USE_SAMPLER -#if cn == 1 +#if CN == 1 #define READ_IMAGE(X,Y,Z) read_imagef(X,Y,Z).x #define INTERMEDIATE_TYPE float -#elif cn == 2 +#elif CN == 2 #define READ_IMAGE(X,Y,Z) read_imagef(X,Y,Z).xy #define INTERMEDIATE_TYPE float2 -#elif cn == 3 +#elif CN == 3 #define READ_IMAGE(X,Y,Z) read_imagef(X,Y,Z).xyz #define INTERMEDIATE_TYPE float3 -#elif cn == 4 +#elif CN == 4 #define READ_IMAGE(X,Y,Z) read_imagef(X,Y,Z) #define INTERMEDIATE_TYPE float4 #endif #define __CAT(x, y) x##y #define CAT(x, y) __CAT(x, y) -//#define INTERMEDIATE_TYPE CAT(float, cn) +//#define INTERMEDIATE_TYPE CAT(float, CN) #define float1 float -#if depth == 0 +#if SRC_DEPTH == 0 #define RESULT_SCALE 255.0f -#elif depth == 1 +#elif SRC_DEPTH == 1 #define RESULT_SCALE 127.0f -#elif depth == 2 +#elif SRC_DEPTH == 2 #define RESULT_SCALE 65535.0f -#elif depth == 3 +#elif SRC_DEPTH == 3 #define RESULT_SCALE 32767.0f #else #define RESULT_SCALE 1.0f @@ -114,10 +114,10 @@ __kernel void resizeSampler(__read_only image2d_t srcImage, INTERMEDIATE_TYPE intermediate = READ_IMAGE(srcImage, sampler, (float2)(sx, sy)); -#if depth <= 4 - T uval = convertToDT(round(intermediate * RESULT_SCALE)); +#if SRC_DEPTH <= 4 + T uval = CONVERT_TO_DT(round(intermediate * RESULT_SCALE)); #else - T uval = convertToDT(intermediate * RESULT_SCALE); + T uval = CONVERT_TO_DT(intermediate * RESULT_SCALE); #endif if(dx < dstcols && dy < dstrows) @@ -149,15 +149,15 @@ __kernel void resizeLN(__global const uchar * srcptr, int src_step, int src_offs int src_index0 = mad24(sy0, src_step, mad24(sx0, TSIZE, src_offset)), src_index1 = mad24(sy1, src_step, mad24(sx0, TSIZE, src_offset)); - WT data0 = convertToWT(loadpix(srcptr + src_index0)); - WT data1 = convertToWT(loadpix(srcptr + src_index0 + TSIZE)); - WT data2 = convertToWT(loadpix(srcptr + src_index1)); - WT data3 = convertToWT(loadpix(srcptr + src_index1 + TSIZE)); + WT data0 = CONVERT_TO_WT(loadpix(srcptr + src_index0)); + WT data1 = CONVERT_TO_WT(loadpix(srcptr + src_index0 + TSIZE)); + WT data2 = CONVERT_TO_WT(loadpix(srcptr + src_index1)); + WT data3 = CONVERT_TO_WT(loadpix(srcptr + src_index1 + TSIZE)); WT val = ( (((data0 * a0 + data1 * a1) >> 4) * b0) >> 16) + ( (((data2 * a0 + data3 * a1) >> 4) * b1) >> 16); - storepix(convertToDT((val + 2) >> 2), + storepix(CONVERT_TO_DT((val + 2) >> 2), dstptr + mad24(dy, dst_step, mad24(dx, TSIZE, dst_offset))); } } @@ -186,7 +186,7 @@ __kernel void resizeLN(__global const uchar * srcptr, int src_step, int src_offs int y_ = INC(y, src_rows); int x_ = INC(x, src_cols); -#if depth <= 1 // 8U/8S only, 16U+ cause integer overflows +#if SRC_DEPTH <= 1 // 8U/8S only, 16U+ cause integer overflows #define INTER_RESIZE_COEF_SCALE (1 << INTER_RESIZE_COEF_BITS) #define CAST_BITS (INTER_RESIZE_COEF_BITS << 1) u = u * INTER_RESIZE_COEF_SCALE; @@ -197,24 +197,24 @@ __kernel void resizeLN(__global const uchar * srcptr, int src_step, int src_offs int U1 = rint(INTER_RESIZE_COEF_SCALE - u); int V1 = rint(INTER_RESIZE_COEF_SCALE - v); - WT data0 = convertToWT(loadpix(srcptr + mad24(y, src_step, mad24(x, TSIZE, src_offset)))); - WT data1 = convertToWT(loadpix(srcptr + mad24(y, src_step, mad24(x_, TSIZE, src_offset)))); - WT data2 = convertToWT(loadpix(srcptr + mad24(y_, src_step, mad24(x, TSIZE, src_offset)))); - WT data3 = convertToWT(loadpix(srcptr + mad24(y_, src_step, mad24(x_, TSIZE, src_offset)))); + WT data0 = CONVERT_TO_WT(loadpix(srcptr + mad24(y, src_step, mad24(x, TSIZE, src_offset)))); + WT data1 = CONVERT_TO_WT(loadpix(srcptr + mad24(y, src_step, mad24(x_, TSIZE, src_offset)))); + WT data2 = CONVERT_TO_WT(loadpix(srcptr + mad24(y_, src_step, mad24(x, TSIZE, src_offset)))); + WT data3 = CONVERT_TO_WT(loadpix(srcptr + mad24(y_, src_step, mad24(x_, TSIZE, src_offset)))); WT val = mul24((WT)mul24(U1, V1), data0) + mul24((WT)mul24(U, V1), data1) + mul24((WT)mul24(U1, V), data2) + mul24((WT)mul24(U, V), data3); - T uval = convertToDT((val + (1<<(CAST_BITS-1)))>>CAST_BITS); + T uval = CONVERT_TO_DT((val + (1<<(CAST_BITS-1)))>>CAST_BITS); #else float u1 = 1.f - u; float v1 = 1.f - v; - WT data0 = convertToWT(loadpix(srcptr + mad24(y, src_step, mad24(x, TSIZE, src_offset)))); - WT data1 = convertToWT(loadpix(srcptr + mad24(y, src_step, mad24(x_, TSIZE, src_offset)))); - WT data2 = convertToWT(loadpix(srcptr + mad24(y_, src_step, mad24(x, TSIZE, src_offset)))); - WT data3 = convertToWT(loadpix(srcptr + mad24(y_, src_step, mad24(x_, TSIZE, src_offset)))); + WT data0 = CONVERT_TO_WT(loadpix(srcptr + mad24(y, src_step, mad24(x, TSIZE, src_offset)))); + WT data1 = CONVERT_TO_WT(loadpix(srcptr + mad24(y, src_step, mad24(x_, TSIZE, src_offset)))); + WT data2 = CONVERT_TO_WT(loadpix(srcptr + mad24(y_, src_step, mad24(x, TSIZE, src_offset)))); + WT data3 = CONVERT_TO_WT(loadpix(srcptr + mad24(y_, src_step, mad24(x_, TSIZE, src_offset)))); - T uval = convertToDT((u1 * v1) * data0 + (u * v1) * data1 + (u1 * v) * data2 + (u * v) * data3); + T uval = CONVERT_TO_DT((u1 * v1) * data0 + (u * v1) * data1 + (u1 * v) * data2 + (u * v) * data3); #endif storepix(uval, dstptr + mad24(dy, dst_step, mad24(dx, TSIZE, dst_offset))); } @@ -268,11 +268,11 @@ __kernel void resizeAREA_FAST(__global const uchar * src, int src_step, int src_ for (int px = 0; px < XSCALE; ++px) { int x = min(sx + px, src_cols - 1); - sum += convertToWTV(loadpix(src + src_index + x*TSIZE)); + sum += CONVERT_TO_WTV(loadpix(src + src_index + x*TSIZE)); } } - storepix(convertToT(convertToWT2V(sum) * (WT2V)(SCALE)), dst + mad24(dx, TSIZE, dst_index)); + storepix(CONVERT_TO_T(CONVERT_TO_WT2V(sum) * (WT2V)(SCALE)), dst + mad24(dx, TSIZE, dst_index)); } } @@ -314,12 +314,12 @@ __kernel void resizeAREA(__global const uchar * src, int src_step, int src_offse for (int sx = sx0, xk = xk0; sx <= sx1; ++sx, ++xk) { WTV alpha = (WTV)(xalpha_tab[xk]); - buf += convertToWTV(loadpix(src + mad24(sx, TSIZE, src_index))) * alpha; + buf += CONVERT_TO_WTV(loadpix(src + mad24(sx, TSIZE, src_index))) * alpha; } sum += buf * beta; } - storepix(convertToT(sum), dst + mad24(dx, TSIZE, dst_index)); + storepix(CONVERT_TO_T(sum), dst + mad24(dx, TSIZE, dst_index)); } } diff --git a/modules/imgproc/src/opencl/warp_affine.cl b/modules/imgproc/src/opencl/warp_affine.cl index bfbd0a6bfb..d937e237ff 100644 --- a/modules/imgproc/src/opencl/warp_affine.cl +++ b/modules/imgproc/src/opencl/warp_affine.cl @@ -66,7 +66,7 @@ #define ST T #endif -#if cn != 3 +#if CN != 3 #define loadpix(addr) *(__global const T*)(addr) #define storepix(val, addr) *(__global T*)(addr) = val #define scalar scalar_ @@ -89,7 +89,7 @@ __kernel void warpAffine(__global const uchar * srcptr, int src_step, int src_of __constant CT * M, ST scalar_) { int dx = get_global_id(0); - int dy0 = get_global_id(1) * rowsPerWI; + int dy0 = get_global_id(1) * ROWS_PER_WI; if (dx < dst_cols) { @@ -99,7 +99,7 @@ __kernel void warpAffine(__global const uchar * srcptr, int src_step, int src_of int Y0_ = rint(M[3] * dx * AB_SCALE); int dst_index = mad24(dy0, dst_step, mad24(dx, pixsize, dst_offset)); - for (int dy = dy0, dy1 = min(dst_rows, dy0 + rowsPerWI); dy < dy1; ++dy, dst_index += dst_step) + for (int dy = dy0, dy1 = min(dst_rows, dy0 + ROWS_PER_WI); dy < dy1; ++dy, dst_index += dst_step) { int X0 = X0_ + rint(fma(M[1], (CT)dy, M[2]) * AB_SCALE) + round_delta; int Y0 = Y0_ + rint(fma(M[4], (CT)dy, M[5]) * AB_SCALE) + round_delta; @@ -133,7 +133,7 @@ __kernel void warpAffine(__global const uchar * srcptr, int src_step, int src_of __constant CT * M, ST scalar_) { int dx = get_global_id(0); - int dy0 = get_global_id(1) * rowsPerWI; + int dy0 = get_global_id(1) * ROWS_PER_WI; if (dx < dst_cols) { @@ -141,7 +141,7 @@ __kernel void warpAffine(__global const uchar * srcptr, int src_step, int src_of int X0_ = rint(M[0] * tmp); int Y0_ = rint(M[3] * tmp); - for (int dy = dy0, dy1 = min(dst_rows, dy0 + rowsPerWI); dy < dy1; ++dy) + for (int dy = dy0, dy1 = min(dst_rows, dy0 + ROWS_PER_WI); dy < dy1; ++dy) { int X0 = X0_ + rint(fma(M[1], (CT)dy, M[2]) * AB_SCALE) + ROUND_DELTA; int Y0 = Y0_ + rint(fma(M[4], (CT)dy, M[5]) * AB_SCALE) + ROUND_DELTA; @@ -151,21 +151,21 @@ __kernel void warpAffine(__global const uchar * srcptr, int src_step, int src_of short sx = convert_short_sat(X0 >> INTER_BITS), sy = convert_short_sat(Y0 >> INTER_BITS); short ax = convert_short(X0 & (INTER_TAB_SIZE-1)), ay = convert_short(Y0 & (INTER_TAB_SIZE-1)); -#if defined AMD_DEVICE || depth > 4 +#if defined AMD_DEVICE || SRC_DEPTH > 4 WT v0 = scalar, v1 = scalar, v2 = scalar, v3 = scalar; if (sx >= 0 && sx < src_cols) { if (sy >= 0 && sy < src_rows) - v0 = convertToWT(loadpix(srcptr + mad24(sy, src_step, mad24(sx, pixsize, src_offset)))); + v0 = CONVERT_TO_WT(loadpix(srcptr + mad24(sy, src_step, mad24(sx, pixsize, src_offset)))); if (sy+1 >= 0 && sy+1 < src_rows) - v2 = convertToWT(loadpix(srcptr + mad24(sy+1, src_step, mad24(sx, pixsize, src_offset)))); + v2 = CONVERT_TO_WT(loadpix(srcptr + mad24(sy+1, src_step, mad24(sx, pixsize, src_offset)))); } if (sx+1 >= 0 && sx+1 < src_cols) { if (sy >= 0 && sy < src_rows) - v1 = convertToWT(loadpix(srcptr + mad24(sy, src_step, mad24(sx+1, pixsize, src_offset)))); + v1 = CONVERT_TO_WT(loadpix(srcptr + mad24(sy, src_step, mad24(sx+1, pixsize, src_offset)))); if (sy+1 >= 0 && sy+1 < src_rows) - v3 = convertToWT(loadpix(srcptr + mad24(sy+1, src_step, mad24(sx+1, pixsize, src_offset)))); + v3 = CONVERT_TO_WT(loadpix(srcptr + mad24(sy+1, src_step, mad24(sx+1, pixsize, src_offset)))); } float taby = 1.f/INTER_TAB_SIZE*ay; @@ -173,18 +173,18 @@ __kernel void warpAffine(__global const uchar * srcptr, int src_step, int src_of int dst_index = mad24(dy, dst_step, mad24(dx, pixsize, dst_offset)); -#if depth <= 4 +#if SRC_DEPTH <= 4 int itab0 = convert_short_sat_rte( (1.0f-taby)*(1.0f-tabx) * INTER_REMAP_COEF_SCALE ); int itab1 = convert_short_sat_rte( (1.0f-taby)*tabx * INTER_REMAP_COEF_SCALE ); int itab2 = convert_short_sat_rte( taby*(1.0f-tabx) * INTER_REMAP_COEF_SCALE ); int itab3 = convert_short_sat_rte( taby*tabx * INTER_REMAP_COEF_SCALE ); WT val = mad24(v0, itab0, mad24(v1, itab1, mad24(v2, itab2, v3 * itab3))); - storepix(convertToT((val + (1 << (INTER_REMAP_COEF_BITS-1))) >> INTER_REMAP_COEF_BITS), dstptr + dst_index); + storepix(CONVERT_TO_T((val + (1 << (INTER_REMAP_COEF_BITS-1))) >> INTER_REMAP_COEF_BITS), dstptr + dst_index); #else float tabx2 = 1.0f - tabx, taby2 = 1.0f - taby; WT val = fma(tabx2, fma(v0, taby2, v2 * taby), tabx * fma(v1, taby2, v3 * taby)); - storepix(convertToT(val), dstptr + dst_index); + storepix(CONVERT_TO_T(val), dstptr + dst_index); #endif #else // INTEL_DEVICE __constant float * coeffs_y = coeffs + (ay << 1), * coeffs_x = coeffs + (ax << 1); @@ -202,13 +202,13 @@ __kernel void warpAffine(__global const uchar * srcptr, int src_step, int src_of xsum = (WT)(0); if (sx >= 0 && sx + 2 < src_cols) { -#if depth == 0 && cn == 1 +#if SRC_DEPTH == 0 && CN == 1 uchar2 value = vload2(0, srcptr + src_index); xsum = dot(convert_float2(value), (float2)(coeffs_x[0], coeffs_x[1])); #else #pragma unroll for (int x = 0; x < 2; x++) - xsum = fma(convertToWT(loadpix(srcptr + mad24(x, pixsize, src_index))), coeffs_x[x], xsum); + xsum = fma(CONVERT_TO_WT(loadpix(srcptr + mad24(x, pixsize, src_index))), coeffs_x[x], xsum); #endif } else @@ -216,7 +216,7 @@ __kernel void warpAffine(__global const uchar * srcptr, int src_step, int src_of #pragma unroll for (int x = 0; x < 2; x++) xsum = fma(sx + x >= 0 && sx + x < src_cols ? - convertToWT(loadpix(srcptr + mad24(x, pixsize, src_index))) : scalar, coeffs_x[x], xsum); + CONVERT_TO_WT(loadpix(srcptr + mad24(x, pixsize, src_index))) : scalar, coeffs_x[x], xsum); } sum = fma(xsum, coeffs_y[y], sum); } @@ -224,7 +224,7 @@ __kernel void warpAffine(__global const uchar * srcptr, int src_step, int src_of sum = fma(scalar, coeffs_y[y], sum); } - storepix(convertToT(sum), dstptr + dst_index); + storepix(CONVERT_TO_T(sum), dstptr + dst_index); #endif } } @@ -290,7 +290,7 @@ __kernel void warpAffine(__global const uchar * srcptr, int src_step, int src_of #pragma unroll for (int x = 0; x < 4; x++) v[mad24(y, 4, x)] = sx+x >= 0 && sx+x < src_cols ? - convertToWT(loadpix(srcptr + mad24(sy+y, src_step, mad24(sx+x, pixsize, src_offset)))) : scalar; + CONVERT_TO_WT(loadpix(srcptr + mad24(sy+y, src_step, mad24(sx+x, pixsize, src_offset)))) : scalar; } else { @@ -310,7 +310,7 @@ __kernel void warpAffine(__global const uchar * srcptr, int src_step, int src_of int dst_index = mad24(dy, dst_step, mad24(dx, pixsize, dst_offset)); WT sum = (WT)(0); -#if depth <= 4 +#if SRC_DEPTH <= 4 int itab[16]; #pragma unroll @@ -320,12 +320,12 @@ __kernel void warpAffine(__global const uchar * srcptr, int src_step, int src_of #pragma unroll for (int i = 0; i < 16; i++) sum = mad24(v[i], itab[i], sum); - storepix(convertToT( (sum + (1 << (INTER_REMAP_COEF_BITS-1))) >> INTER_REMAP_COEF_BITS ), dstptr + dst_index); + storepix(CONVERT_TO_T( (sum + (1 << (INTER_REMAP_COEF_BITS-1))) >> INTER_REMAP_COEF_BITS ), dstptr + dst_index); #else #pragma unroll for (int i = 0; i < 16; i++) sum = fma(v[i], tab1y[(i>>2)] * tab1x[(i&3)], sum); - storepix(convertToT( sum ), dstptr + dst_index); + storepix(CONVERT_TO_T( sum ), dstptr + dst_index); #endif #else // INTEL_DEVICE __constant float * coeffs_y = coeffs + (ay << 2), * coeffs_x = coeffs + (ax << 2); @@ -343,13 +343,13 @@ __kernel void warpAffine(__global const uchar * srcptr, int src_step, int src_of xsum = (WT)(0); if (sx >= 0 && sx + 4 < src_cols) { -#if depth == 0 && cn == 1 +#if SRC_DEPTH == 0 && CN == 1 uchar4 value = vload4(0, srcptr + src_index); xsum = dot(convert_float4(value), (float4)(coeffs_x[0], coeffs_x[1], coeffs_x[2], coeffs_x[3])); #else #pragma unroll for (int x = 0; x < 4; x++) - xsum = fma(convertToWT(loadpix(srcptr + mad24(x, pixsize, src_index))), coeffs_x[x], xsum); + xsum = fma(CONVERT_TO_WT(loadpix(srcptr + mad24(x, pixsize, src_index))), coeffs_x[x], xsum); #endif } else @@ -357,7 +357,7 @@ __kernel void warpAffine(__global const uchar * srcptr, int src_step, int src_of #pragma unroll for (int x = 0; x < 4; x++) xsum = fma(sx + x >= 0 && sx + x < src_cols ? - convertToWT(loadpix(srcptr + mad24(x, pixsize, src_index))) : scalar, coeffs_x[x], xsum); + CONVERT_TO_WT(loadpix(srcptr + mad24(x, pixsize, src_index))) : scalar, coeffs_x[x], xsum); } sum = fma(xsum, coeffs_y[y], sum); } @@ -365,7 +365,7 @@ __kernel void warpAffine(__global const uchar * srcptr, int src_step, int src_of sum = fma(scalar, coeffs_y[y], sum); } - storepix(convertToT(sum), dstptr + dst_index); + storepix(CONVERT_TO_T(sum), dstptr + dst_index); #endif } } diff --git a/modules/imgproc/src/opencl/warp_perspective.cl b/modules/imgproc/src/opencl/warp_perspective.cl index 20e3a27404..06bc2bd7fa 100644 --- a/modules/imgproc/src/opencl/warp_perspective.cl +++ b/modules/imgproc/src/opencl/warp_perspective.cl @@ -65,7 +65,7 @@ #define ST T #endif -#if cn != 3 +#if CN != 3 #define loadpix(addr) *(__global const T*)(addr) #define storepix(val, addr) *(__global T*)(addr) = val #define scalar scalar_ @@ -134,31 +134,31 @@ __kernel void warpPerspective(__global const uchar * srcptr, int src_step, int s short ax = (short)(X & (INTER_TAB_SIZE - 1)); WT v0 = (sx >= 0 && sx < src_cols && sy >= 0 && sy < src_rows) ? - convertToWT(loadpix(srcptr + mad24(sy, src_step, src_offset + sx * pixsize))) : scalar; + CONVERT_TO_WT(loadpix(srcptr + mad24(sy, src_step, src_offset + sx * pixsize))) : scalar; WT v1 = (sx+1 >= 0 && sx+1 < src_cols && sy >= 0 && sy < src_rows) ? - convertToWT(loadpix(srcptr + mad24(sy, src_step, src_offset + (sx+1) * pixsize))) : scalar; + CONVERT_TO_WT(loadpix(srcptr + mad24(sy, src_step, src_offset + (sx+1) * pixsize))) : scalar; WT v2 = (sx >= 0 && sx < src_cols && sy+1 >= 0 && sy+1 < src_rows) ? - convertToWT(loadpix(srcptr + mad24(sy+1, src_step, src_offset + sx * pixsize))) : scalar; + CONVERT_TO_WT(loadpix(srcptr + mad24(sy+1, src_step, src_offset + sx * pixsize))) : scalar; WT v3 = (sx+1 >= 0 && sx+1 < src_cols && sy+1 >= 0 && sy+1 < src_rows) ? - convertToWT(loadpix(srcptr + mad24(sy+1, src_step, src_offset + (sx+1) * pixsize))) : scalar; + CONVERT_TO_WT(loadpix(srcptr + mad24(sy+1, src_step, src_offset + (sx+1) * pixsize))) : scalar; float taby = 1.f/INTER_TAB_SIZE*ay; float tabx = 1.f/INTER_TAB_SIZE*ax; int dst_index = mad24(dy, dst_step, dst_offset + dx * pixsize); -#if depth <= 4 +#if SRC_DEPTH <= 4 int itab0 = convert_short_sat_rte( (1.0f-taby)*(1.0f-tabx) * INTER_REMAP_COEF_SCALE ); int itab1 = convert_short_sat_rte( (1.0f-taby)*tabx * INTER_REMAP_COEF_SCALE ); int itab2 = convert_short_sat_rte( taby*(1.0f-tabx) * INTER_REMAP_COEF_SCALE ); int itab3 = convert_short_sat_rte( taby*tabx * INTER_REMAP_COEF_SCALE ); WT val = v0 * itab0 + v1 * itab1 + v2 * itab2 + v3 * itab3; - storepix(convertToT((val + (1 << (INTER_REMAP_COEF_BITS-1))) >> INTER_REMAP_COEF_BITS), dstptr + dst_index); + storepix(CONVERT_TO_T((val + (1 << (INTER_REMAP_COEF_BITS-1))) >> INTER_REMAP_COEF_BITS), dstptr + dst_index); #else float tabx2 = 1.0f - tabx, taby2 = 1.0f - taby; WT val = v0 * tabx2 * taby2 + v1 * tabx * taby2 + v2 * tabx2 * taby + v3 * tabx * taby; - storepix(convertToT(val), dstptr + dst_index); + storepix(CONVERT_TO_T(val), dstptr + dst_index); #endif } } @@ -201,7 +201,7 @@ __kernel void warpPerspective(__global const uchar * srcptr, int src_step, int s #pragma unroll for (int x = 0; x < 4; x++) v[mad24(y, 4, x)] = (sx+x >= 0 && sx+x < src_cols && sy+y >= 0 && sy+y < src_rows) ? - convertToWT(loadpix(srcptr + mad24(sy+y, src_step, src_offset + (sx+x) * pixsize))) : scalar; + CONVERT_TO_WT(loadpix(srcptr + mad24(sy+y, src_step, src_offset + (sx+x) * pixsize))) : scalar; float tab1y[4], tab1x[4]; @@ -213,7 +213,7 @@ __kernel void warpPerspective(__global const uchar * srcptr, int src_step, int s int dst_index = mad24(dy, dst_step, dst_offset + dx * pixsize); WT sum = (WT)(0); -#if depth <= 4 +#if SRC_DEPTH <= 4 int itab[16]; #pragma unroll @@ -223,12 +223,12 @@ __kernel void warpPerspective(__global const uchar * srcptr, int src_step, int s #pragma unroll for (int i = 0; i < 16; i++) sum += v[i] * itab[i]; - storepix(convertToT( (sum + (1 << (INTER_REMAP_COEF_BITS-1))) >> INTER_REMAP_COEF_BITS ), dstptr + dst_index); + storepix(CONVERT_TO_T( (sum + (1 << (INTER_REMAP_COEF_BITS-1))) >> INTER_REMAP_COEF_BITS ), dstptr + dst_index); #else #pragma unroll for (int i = 0; i < 16; i++) sum += v[i] * tab1y[(i>>2)] * tab1x[(i&3)]; - storepix(convertToT( sum ), dstptr + dst_index); + storepix(CONVERT_TO_T( sum ), dstptr + dst_index); #endif } } diff --git a/modules/imgproc/src/pyramids.cpp b/modules/imgproc/src/pyramids.cpp index 9d6d76c812..0e65a81278 100644 --- a/modules/imgproc/src/pyramids.cpp +++ b/modules/imgproc/src/pyramids.cpp @@ -1189,8 +1189,8 @@ static bool ocl_pyrDown( InputArray _src, OutputArray _dst, const Size& _dsz, in "BORDER_REFLECT_101" }; char cvt[2][50]; String buildOptions = format( - "-D T=%s -D FT=%s -D convertToT=%s -D convertToFT=%s%s " - "-D T1=%s -D cn=%d -D kercn=%d -D fdepth=%d -D %s -D LOCAL_SIZE=%d", + "-D T=%s -D FT=%s -D CONVERT_TO_T=%s -D CONVERT_TO_FT=%s%s " + "-D T1=%s -D CN=%d -D KERCN=%d -D FDEPTH=%d -D %s -D LOCAL_SIZE=%d", ocl::typeToStr(type), ocl::typeToStr(CV_MAKETYPE(float_depth, cn)), ocl::convertTypeStr(float_depth, depth, cn, cvt[0], sizeof(cvt[0])), ocl::convertTypeStr(depth, float_depth, cn, cvt[1], sizeof(cvt[1])), @@ -1232,8 +1232,8 @@ static bool ocl_pyrUp( InputArray _src, OutputArray _dst, const Size& _dsz, int const int local_size = channels == 1 ? 16 : 8; char cvt[2][50]; String buildOptions = format( - "-D T=%s -D FT=%s -D convertToT=%s -D convertToFT=%s%s " - "-D T1=%s -D cn=%d -D LOCAL_SIZE=%d", + "-D T=%s -D FT=%s -D CONVERT_TO_T=%s -D CONVERT_TO_FT=%s%s " + "-D T1=%s -D CN=%d -D LOCAL_SIZE=%d", ocl::typeToStr(type), ocl::typeToStr(CV_MAKETYPE(float_depth, channels)), ocl::convertTypeStr(float_depth, depth, channels, cvt[0], sizeof(cvt[0])), ocl::convertTypeStr(depth, float_depth, channels, cvt[1], sizeof(cvt[1])), diff --git a/modules/imgproc/src/resize.cpp b/modules/imgproc/src/resize.cpp index a1d4433f68..30fec64d18 100644 --- a/modules/imgproc/src/resize.cpp +++ b/modules/imgproc/src/resize.cpp @@ -3396,8 +3396,8 @@ static bool ocl_resize( InputArray _src, OutputArray _dst, Size dsize, { int wdepth = std::max(depth, CV_32S); char buf[2][50]; - cv::String compileOpts = format("-D USE_SAMPLER -D depth=%d -D T=%s -D T1=%s " - "-D convertToDT=%s -D cn=%d", + cv::String compileOpts = format("-D USE_SAMPLER -D SRC_DEPTH=%d -D T=%s -D T1=%s " + "-D CONVERT_TO_DT=%s -D CN=%d", depth, ocl::typeToStr(type), ocl::typeToStr(depth), ocl::convertTypeStr(wdepth, depth, cn, buf[1], sizeof(buf[1])), cn); @@ -3461,8 +3461,8 @@ static bool ocl_resize( InputArray _src, OutputArray _dst, Size dsize, Mat(1, static_cast(_buffer.size()), CV_8UC1, _buffer.data()).copyTo(coeffs); k.create("resizeLN", ocl::imgproc::resize_oclsrc, - format("-D INTER_LINEAR_INTEGER -D depth=%d -D T=%s -D T1=%s " - "-D WT=%s -D convertToWT=%s -D convertToDT=%s -D cn=%d " + format("-D INTER_LINEAR_INTEGER -D SRC_DEPTH=%d -D T=%s -D T1=%s " + "-D WT=%s -D CONVERT_TO_WT=%s -D CONVERT_TO_DT=%s -D CN=%d " "-D INTER_RESIZE_COEF_BITS=%d", depth, ocl::typeToStr(type), ocl::typeToStr(depth), ocl::typeToStr(wtype), ocl::convertTypeStr(depth, wdepth, cn, buf[0], sizeof(buf[0])), @@ -3479,8 +3479,8 @@ static bool ocl_resize( InputArray _src, OutputArray _dst, Size dsize, int wdepth = depth <= CV_8S ? CV_32S : std::max(depth, CV_32F); int wtype = CV_MAKETYPE(wdepth, cn); k.create("resizeLN", ocl::imgproc::resize_oclsrc, - format("-D INTER_LINEAR -D depth=%d -D T=%s -D T1=%s " - "-D WT=%s -D convertToWT=%s -D convertToDT=%s -D cn=%d " + format("-D INTER_LINEAR -D SRC_DEPTH=%d -D T=%s -D T1=%s " + "-D WT=%s -D CONVERT_TO_WT=%s -D CONVERT_TO_DT=%s -D CN=%d " "-D INTER_RESIZE_COEF_BITS=%d", depth, ocl::typeToStr(type), ocl::typeToStr(depth), ocl::typeToStr(wtype), ocl::convertTypeStr(depth, wdepth, cn, buf[0], sizeof(buf[0])), @@ -3496,7 +3496,7 @@ static bool ocl_resize( InputArray _src, OutputArray _dst, Size dsize, else if (interpolation == INTER_NEAREST) { k.create("resizeNN", ocl::imgproc::resize_oclsrc, - format("-D INTER_NEAREST -D T=%s -D T1=%s -D cn=%d", + format("-D INTER_NEAREST -D T=%s -D T1=%s -D CN=%d", ocl::vecopTypeToStr(type), ocl::vecopTypeToStr(depth), cn)); if (k.empty()) return false; @@ -3510,7 +3510,7 @@ static bool ocl_resize( InputArray _src, OutputArray _dst, Size dsize, int wtype = CV_MAKE_TYPE(wdepth, cn); char cvt[2][50]; - String buildOption = format("-D INTER_AREA -D T=%s -D T1=%s -D WTV=%s -D convertToWTV=%s -D cn=%d", + String buildOption = format("-D INTER_AREA -D T=%s -D T1=%s -D WTV=%s -D CONVERT_TO_WTV=%s -D CN=%d", ocl::typeToStr(type), ocl::typeToStr(depth), ocl::typeToStr(wtype), ocl::convertTypeStr(depth, wdepth, cn, cvt[0], sizeof(cvt[0])), cn); @@ -3520,7 +3520,7 @@ static bool ocl_resize( InputArray _src, OutputArray _dst, Size dsize, if (is_area_fast) { int wdepth2 = std::max(CV_32F, depth), wtype2 = CV_MAKE_TYPE(wdepth2, cn); - buildOption = buildOption + format(" -D convertToT=%s -D WT2V=%s -D convertToWT2V=%s -D INTER_AREA_FAST" + buildOption = buildOption + format(" -D CONVERT_TO_T=%s -D WT2V=%s -D CONVERT_TO_WT2V=%s -D INTER_AREA_FAST" " -D XSCALE=%d -D YSCALE=%d -D SCALE=%ff", ocl::convertTypeStr(wdepth2, depth, cn, cvt[0], sizeof(cvt[0])), ocl::typeToStr(wtype2), ocl::convertTypeStr(wdepth, wdepth2, cn, cvt[1], sizeof(cvt[1])), @@ -3532,7 +3532,7 @@ static bool ocl_resize( InputArray _src, OutputArray _dst, Size dsize, } else { - buildOption = buildOption + format(" -D convertToT=%s", ocl::convertTypeStr(wdepth, depth, cn, cvt[0], sizeof(cvt[0]))); + buildOption = buildOption + format(" -D CONVERT_TO_T=%s", ocl::convertTypeStr(wdepth, depth, cn, cvt[0], sizeof(cvt[0]))); k.create("resizeAREA", ocl::imgproc::resize_oclsrc, buildOption); if (k.empty()) return false; diff --git a/modules/imgproc/test/test_cornersubpix.cpp b/modules/imgproc/test/test_cornersubpix.cpp new file mode 100644 index 0000000000..86484d2482 --- /dev/null +++ b/modules/imgproc/test/test_cornersubpix.cpp @@ -0,0 +1,68 @@ +/*M/////////////////////////////////////////////////////////////////////////////////////// +// +// IMPORTANT: READ BEFORE DOWNLOADING, COPYING, INSTALLING OR USING. +// +// By downloading, copying, installing or using the software you agree to this license. +// If you do not agree to this license, do not download, install, +// copy or use the software. +// +// +// License Agreement +// For Open Source Computer Vision Library +// +// Copyright (C) 2015-2023, OpenCV Foundation, all rights reserved. +// Third party copyrights are property of their respective owners. +// +// Redistribution and use in source and binary forms, with or without modification, +// are permitted provided that the following conditions are met: +// +// * Redistribution's of source code must retain the above copyright notice, +// this list of conditions and the following disclaimer. +// +// * Redistribution's in binary form must reproduce the above copyright notice, +// this list of conditions and the following disclaimer in the documentation +// and/or other materials provided with the distribution. +// +// * The name of the copyright holders may not be used to endorse or promote products +// derived from this software without specific prior written permission. +// +// This software is provided by the copyright holders and contributors "as is" and +// any express or implied warranties, including, but not limited to, the implied +// warranties of merchantability and fitness for a particular purpose are disclaimed. +// In no event shall the Intel Corporation or contributors be liable for any direct, +// indirect, incidental, special, exemplary, or consequential damages +// (including, but not limited to, procurement of substitute goods or services; +// loss of use, data, or profits; or business interruption) however caused +// and on any theory of liability, whether in contract, strict liability, +// or tort (including negligence or otherwise) arising in any way out of +// the use of this software, even if advised of the possibility of such damage. +// +//M*/ + +#include "test_precomp.hpp" + +namespace opencv_test { namespace { + +TEST(Imgproc_CornerSubPix, out_of_image_corners) +{ + const uint8_t image_pixels[] = { + 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 1, + 0, 0, 0, 0, 0, 0, 2, + 0, 0, 0, 0, 0, 0, 3}; + + cv::Mat image(cv::Size(7, 7), CV_8UC1, (void*)image_pixels, cv::Mat::AUTO_STEP); + std::vector corners = {cv::Point2f(5.25, 6.5)}; + cv::Size win(1, 1); + cv::Size zeroZone(-1, -1); + cv::TermCriteria criteria; + cv::cornerSubPix(image, corners, win, zeroZone, criteria); + + ASSERT_EQ(corners.size(), 1u); + ASSERT_TRUE(Rect(0, 0, image.cols, image.rows).contains(corners.front())); +} + +}} // namespace diff --git a/modules/java/android_sdk/CMakeLists.txt b/modules/java/android_sdk/CMakeLists.txt index b3308c03f6..0bcc89ae29 100644 --- a/modules/java/android_sdk/CMakeLists.txt +++ b/modules/java/android_sdk/CMakeLists.txt @@ -27,12 +27,19 @@ if(ANDROID_SDK_COMPATIBLE_TARGET) set(ANDROID_SDK_COMPATIBLE_TARGET "${ANDROID_SDK_COMPATIBLE_TARGET}" CACHE INTERNAL "") endif() string(REGEX REPLACE "android-" "" android_sdk_target_num ${ANDROID_SDK_COMPATIBLE_TARGET}) + if( (ANDROID_SDK_TARGET AND ANDROID_SDK_TARGET LESS 21) OR (android_sdk_target_num LESS 21) ) message(STATUS "[OpenCV for Android SDK]: A new OpenGL Camera Bridge (CameraGLSurfaceView, CameraGLRendererBase, CameraRenderer, Camera2Renderer) is disabled, because ANDROID_SDK_TARGET (${android_sdk_target_num}) < 21") else() ocv_copyfiles_append_dir(JAVA_SRC_COPY "${OPENCV_JAVA_BINDINGS_DIR}/gen/android-21/java" "${java_src_dir}") endif() +if( (ANDROID_SDK_TARGET AND ANDROID_SDK_TARGET LESS 24) OR (android_sdk_target_num LESS 24) ) + message(STATUS "[OpenCV for Android SDK]: An experiemntal Native Camera is disabled, because ANDROID_SDK_TARGET (${android_sdk_target_num}) < 24") +else() + ocv_copyfiles_append_dir(JAVA_SRC_COPY "${OPENCV_JAVA_BINDINGS_DIR}/gen/android-24/java" "${java_src_dir}") +endif() + # copy boilerplate file(GLOB_RECURSE seed_project_files_rel RELATIVE "${CMAKE_CURRENT_SOURCE_DIR}/android_lib/" "${CMAKE_CURRENT_SOURCE_DIR}/android_lib/*") list(REMOVE_ITEM seed_project_files_rel "${ANDROID_MANIFEST_FILE}") @@ -113,6 +120,7 @@ else() # gradle build #TODO: INSTALL ONLY ocv_copyfiles_append_dir(JAVA_SRC_COPY "${OPENCV_JAVA_BINDINGS_DIR}/gen/android/java" "${java_src_dir}") ocv_copyfiles_append_dir(JAVA_SRC_COPY "${OPENCV_JAVA_BINDINGS_DIR}/gen/android-21/java" "${java_src_dir}") +ocv_copyfiles_append_dir(JAVA_SRC_COPY "${OPENCV_JAVA_BINDINGS_DIR}/gen/android-24/java" "${java_src_dir}") # copy boilerplate set(__base_dir "${CMAKE_CURRENT_SOURCE_DIR}/android_gradle_lib/") diff --git a/modules/java/android_sdk/android_gradle_lib/build.gradle b/modules/java/android_sdk/android_gradle_lib/build.gradle index 41cdb9e5e1..b887cdb4b9 100644 --- a/modules/java/android_sdk/android_gradle_lib/build.gradle +++ b/modules/java/android_sdk/android_gradle_lib/build.gradle @@ -5,6 +5,7 @@ def openCVersionName = "@OPENCV_VERSION@" def openCVersionCode = ((@OPENCV_VERSION_MAJOR@ * 100 + @OPENCV_VERSION_MINOR@) * 100 + @OPENCV_VERSION_PATCH@) * 10 + 0 android { + @OPENCV_ANDROID_NAMESPACE_DECLARATION@ compileSdkVersion @ANDROID_COMPILE_SDK_VERSION@ defaultConfig { diff --git a/modules/java/android_sdk/build.gradle.in b/modules/java/android_sdk/build.gradle.in index 6b71d1fe11..ccaa1c1d0c 100644 --- a/modules/java/android_sdk/build.gradle.in +++ b/modules/java/android_sdk/build.gradle.in @@ -49,7 +49,7 @@ // - avoid using of "OpenCVLoader.initAsync()" approach - it is deprecated // It may load library with different version (from OpenCV Android Manager, which is installed separatelly on device) // -// - use "System.loadLibrary("opencv_java4")" or "OpenCVLoader.initDebug()" +// - use "System.loadLibrary("opencv_java5")" or "OpenCVLoader.initDebug()" // TODO: Add accurate API to load OpenCV native library // // @@ -89,6 +89,7 @@ // apply plugin: 'com.android.library' +apply plugin: 'maven-publish' @KOTLIN_PLUGIN_DECLARATION@ def openCVersionName = "@OPENCV_VERSION@" @@ -137,6 +138,17 @@ android { } } + buildFeatures { + aidl true + prefabPublishing true + buildConfig true + } + prefab { + opencv_jni_shared { + headers "native/jni/include" + } + } + sourceSets { main { jniLibs.srcDirs = ['native/libs'] @@ -147,6 +159,13 @@ android { } } + publishing { + singleVariant('release') { + withSourcesJar() + withJavadocJar() + } + } + externalNativeBuild { cmake { path (project.projectDir.toString() + '/libcxx_helper/CMakeLists.txt') @@ -154,5 +173,25 @@ android { } } +publishing { + publications { + release(MavenPublication) { + groupId = 'org.opencv' + artifactId = 'opencv' + version = '@OPENCV_VERSION_PLAIN@' + + afterEvaluate { + from components.release + } + } + } + repositories { + maven { + name = 'myrepo' + url = "${project.buildDir}/repo" + } + } +} + dependencies { } diff --git a/modules/java/android_sdk/libcxx_helper/CMakeLists.txt b/modules/java/android_sdk/libcxx_helper/CMakeLists.txt index bc9146f457..c2dcd6d4d8 100644 --- a/modules/java/android_sdk/libcxx_helper/CMakeLists.txt +++ b/modules/java/android_sdk/libcxx_helper/CMakeLists.txt @@ -1,4 +1,6 @@ cmake_minimum_required(VERSION 3.6) +project(opencv_jni_shared) + # dummy target to bring libc++_shared.so into packages add_library(opencv_jni_shared STATIC dummy.cpp) diff --git a/modules/java/generator/android-21/java/org/opencv/android/JavaCamera2View.java b/modules/java/generator/android-21/java/org/opencv/android/JavaCamera2View.java index 017de7f26a..c899389e25 100644 --- a/modules/java/generator/android-21/java/org/opencv/android/JavaCamera2View.java +++ b/modules/java/generator/android-21/java/org/opencv/android/JavaCamera2View.java @@ -45,6 +45,7 @@ public class JavaCamera2View extends CameraBridgeViewBase { protected ImageReader mImageReader; protected int mPreviewFormat = ImageFormat.YUV_420_888; + protected int mRequestTemplate = CameraDevice.TEMPLATE_PREVIEW; protected CameraDevice mCameraDevice; protected CameraCaptureSession mCaptureSession; @@ -155,6 +156,35 @@ public class JavaCamera2View extends CameraBridgeViewBase { }; + protected CameraCaptureSession.StateCallback allocateSessionStateCallback() { + return new CameraCaptureSession.StateCallback() { + @Override + public void onConfigured(CameraCaptureSession cameraCaptureSession) { + Log.i(LOGTAG, "createCaptureSession::onConfigured"); + if (null == mCameraDevice) { + return; // camera is already closed + } + mCaptureSession = cameraCaptureSession; + try { + mPreviewRequestBuilder.set(CaptureRequest.CONTROL_AF_MODE, + CaptureRequest.CONTROL_AF_MODE_CONTINUOUS_PICTURE); + mPreviewRequestBuilder.set(CaptureRequest.CONTROL_AE_MODE, + CaptureRequest.CONTROL_AE_MODE_ON_AUTO_FLASH); + + mCaptureSession.setRepeatingRequest(mPreviewRequestBuilder.build(), null, mBackgroundHandler); + Log.i(LOGTAG, "CameraPreviewSession has been started"); + } catch (Exception e) { + Log.e(LOGTAG, "createCaptureSession failed", e); + } + } + + @Override + public void onConfigureFailed(CameraCaptureSession cameraCaptureSession) { + Log.e(LOGTAG, "createCameraPreviewSession failed"); + } + }; + } + private void createCameraPreviewSession() { final int w = mPreviewSize.getWidth(), h = mPreviewSize.getHeight(); Log.i(LOGTAG, "createCameraPreviewSession(" + w + "x" + h + ")"); @@ -191,38 +221,11 @@ public class JavaCamera2View extends CameraBridgeViewBase { }, mBackgroundHandler); Surface surface = mImageReader.getSurface(); - mPreviewRequestBuilder = mCameraDevice.createCaptureRequest(CameraDevice.TEMPLATE_PREVIEW); + mPreviewRequestBuilder = mCameraDevice.createCaptureRequest(mRequestTemplate); mPreviewRequestBuilder.addTarget(surface); mCameraDevice.createCaptureSession(Arrays.asList(surface), - new CameraCaptureSession.StateCallback() { - @Override - public void onConfigured(CameraCaptureSession cameraCaptureSession) { - Log.i(LOGTAG, "createCaptureSession::onConfigured"); - if (null == mCameraDevice) { - return; // camera is already closed - } - mCaptureSession = cameraCaptureSession; - try { - mPreviewRequestBuilder.set(CaptureRequest.CONTROL_AF_MODE, - CaptureRequest.CONTROL_AF_MODE_CONTINUOUS_PICTURE); - mPreviewRequestBuilder.set(CaptureRequest.CONTROL_AE_MODE, - CaptureRequest.CONTROL_AE_MODE_ON_AUTO_FLASH); - - mCaptureSession.setRepeatingRequest(mPreviewRequestBuilder.build(), null, mBackgroundHandler); - Log.i(LOGTAG, "CameraPreviewSession has been started"); - } catch (Exception e) { - Log.e(LOGTAG, "createCaptureSession failed", e); - } - } - - @Override - public void onConfigureFailed(CameraCaptureSession cameraCaptureSession) { - Log.e(LOGTAG, "createCameraPreviewSession failed"); - } - }, - null - ); + allocateSessionStateCallback(), null); } catch (CameraAccessException e) { Log.e(LOGTAG, "createCameraPreviewSession", e); } @@ -321,6 +324,10 @@ public class JavaCamera2View extends CameraBridgeViewBase { } createCameraPreviewSession(); } + + if (mFpsMeter != null) { + mFpsMeter.setResolution(mFrameWidth, mFrameHeight); + } } catch (RuntimeException e) { throw new RuntimeException("Interrupted while setCameraPreviewSize.", e); } diff --git a/modules/java/generator/android-24/java/org/opencv/android/NativeCameraView.java b/modules/java/generator/android-24/java/org/opencv/android/NativeCameraView.java new file mode 100644 index 0000000000..44ed8c4114 --- /dev/null +++ b/modules/java/generator/android-24/java/org/opencv/android/NativeCameraView.java @@ -0,0 +1,191 @@ +package org.opencv.android; + +import org.opencv.core.Mat; +import org.opencv.core.Size; + +import org.opencv.imgproc.Imgproc; + +import org.opencv.videoio.Videoio; +import org.opencv.videoio.VideoCapture; +import org.opencv.videoio.VideoWriter; + +import android.content.Context; +import android.util.AttributeSet; +import android.util.Log; +import android.view.ViewGroup.LayoutParams; + +/** + * This class is an implementation of a bridge between SurfaceView and OpenCV VideoCapture. + * The class is experimental implementation and not recoomended for production usage. + */ +public class NativeCameraView extends CameraBridgeViewBase { + + public static final String TAG = "NativeCameraView"; + private boolean mStopThread; + private Thread mThread; + + protected VideoCapture mCamera; + protected NativeCameraFrame mFrame; + + public NativeCameraView(Context context, int cameraId) { + super(context, cameraId); + } + + public NativeCameraView(Context context, AttributeSet attrs) { + super(context, attrs); + } + + @Override + protected boolean connectCamera(int width, int height) { + + /* 1. We need to instantiate camera + * 2. We need to start thread which will be getting frames + */ + /* First step - initialize camera connection */ + if (!initializeCamera(width, height)) + return false; + + /* now we can start update thread */ + mThread = new Thread(new CameraWorker()); + mThread.start(); + + return true; + } + + @Override + protected void disconnectCamera() { + /* 1. We need to stop thread which updating the frames + * 2. Stop camera and release it + */ + if (mThread != null) { + try { + mStopThread = true; + mThread.join(); + } catch (InterruptedException e) { + e.printStackTrace(); + } finally { + mThread = null; + mStopThread = false; + } + } + + /* Now release camera */ + releaseCamera(); + } + + public static class OpenCvSizeAccessor implements ListItemAccessor { + + public int getWidth(Object obj) { + Size size = (Size)obj; + return (int)size.width; + } + + public int getHeight(Object obj) { + Size size = (Size)obj; + return (int)size.height; + } + + } + + private boolean initializeCamera(int width, int height) { + synchronized (this) { + + if (mCameraIndex == -1) { + Log.d(TAG, "Try to open default camera"); + mCamera = new VideoCapture(0, Videoio.CAP_ANDROID); + } else { + Log.d(TAG, "Try to open camera with index " + mCameraIndex); + mCamera = new VideoCapture(mCameraIndex, Videoio.CAP_ANDROID); + } + + if (mCamera == null) + return false; + + if (mCamera.isOpened() == false) + return false; + + mFrame = new NativeCameraFrame(mCamera); + + mCamera.set(Videoio.CAP_PROP_FRAME_WIDTH, width); + mCamera.set(Videoio.CAP_PROP_FRAME_HEIGHT, height); + + mFrameWidth = (int)mCamera.get(Videoio.CAP_PROP_FRAME_WIDTH); + mFrameHeight = (int)mCamera.get(Videoio.CAP_PROP_FRAME_HEIGHT); + + if ((getLayoutParams().width == LayoutParams.MATCH_PARENT) && (getLayoutParams().height == LayoutParams.MATCH_PARENT)) + mScale = Math.min(((float)height)/mFrameHeight, ((float)width)/mFrameWidth); + else + mScale = 0; + + if (mFpsMeter != null) { + mFpsMeter.setResolution(mFrameWidth, mFrameHeight); + } + + AllocateCache(); + } + + Log.i(TAG, "Selected camera frame size = (" + mFrameWidth + ", " + mFrameHeight + ")"); + + return true; + } + + private void releaseCamera() { + synchronized (this) { + if (mFrame != null) mFrame.release(); + if (mCamera != null) mCamera.release(); + } + } + + private static class NativeCameraFrame implements CvCameraViewFrame { + + @Override + public Mat rgba() { + mCapture.set(Videoio.CAP_PROP_FOURCC, VideoWriter.fourcc('R','G','B','3')); + mCapture.retrieve(mBgr); + Log.d(TAG, "Retrived frame with size " + mBgr.cols() + "x" + mBgr.rows() + " and channels: " + mBgr.channels()); + Imgproc.cvtColor(mBgr, mRgba, Imgproc.COLOR_RGB2RGBA); + return mRgba; + } + + @Override + public Mat gray() { + mCapture.set(Videoio.CAP_PROP_FOURCC, VideoWriter.fourcc('G','R','E','Y')); + mCapture.retrieve(mGray); + Log.d(TAG, "Retrived frame with size " + mGray.cols() + "x" + mGray.rows() + " and channels: " + mGray.channels()); + return mGray; + } + + public NativeCameraFrame(VideoCapture capture) { + mCapture = capture; + mGray = new Mat(); + mRgba = new Mat(); + mBgr = new Mat(); + } + + public void release() { + if (mGray != null) mGray.release(); + if (mRgba != null) mRgba.release(); + if (mBgr != null) mBgr.release(); + } + + private VideoCapture mCapture; + private Mat mRgba; + private Mat mGray; + private Mat mBgr; + }; + + private class CameraWorker implements Runnable { + + public void run() { + do { + if (!mCamera.grab()) { + Log.e(TAG, "Camera frame grab failed"); + break; + } + + deliverAndDrawFrame(mFrame); + } while (!mStopThread); + } + } + +} diff --git a/modules/java/generator/android/java/org/opencv/android/AsyncServiceHelper.java b/modules/java/generator/android/java/org/opencv/android/AsyncServiceHelper.java deleted file mode 100644 index cb3c6428d1..0000000000 --- a/modules/java/generator/android/java/org/opencv/android/AsyncServiceHelper.java +++ /dev/null @@ -1,391 +0,0 @@ -package org.opencv.android; - -import java.io.File; -import java.util.StringTokenizer; - -import org.opencv.core.Core; -import org.opencv.engine.OpenCVEngineInterface; - -import android.content.ComponentName; -import android.content.Context; -import android.content.Intent; -import android.content.ServiceConnection; -import android.net.Uri; -import android.os.IBinder; -import android.os.RemoteException; -import android.util.Log; - -class AsyncServiceHelper -{ - public static boolean initOpenCV(String Version, final Context AppContext, - final LoaderCallbackInterface Callback) - { - AsyncServiceHelper helper = new AsyncServiceHelper(Version, AppContext, Callback); - Intent intent = new Intent("org.opencv.engine.BIND"); - intent.setPackage("org.opencv.engine"); - if (AppContext.bindService(intent, helper.mServiceConnection, Context.BIND_AUTO_CREATE)) - { - return true; - } - else - { - AppContext.unbindService(helper.mServiceConnection); - InstallService(AppContext, Callback); - return false; - } - } - - protected AsyncServiceHelper(String Version, Context AppContext, LoaderCallbackInterface Callback) - { - mOpenCVersion = Version; - mUserAppCallback = Callback; - mAppContext = AppContext; - } - - protected static final String TAG = "OpenCVManager/Helper"; - protected static final int MINIMUM_ENGINE_VERSION = 2; - protected OpenCVEngineInterface mEngineService; - protected LoaderCallbackInterface mUserAppCallback; - protected String mOpenCVersion; - protected Context mAppContext; - protected static boolean mServiceInstallationProgress = false; - protected static boolean mLibraryInstallationProgress = false; - - protected static boolean InstallServiceQuiet(Context context) - { - boolean result = true; - try - { - Intent intent = new Intent(Intent.ACTION_VIEW, Uri.parse(OPEN_CV_SERVICE_URL)); - intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK); - context.startActivity(intent); - } - catch(Exception e) - { - result = false; - } - - return result; - } - - protected static void InstallService(final Context AppContext, final LoaderCallbackInterface Callback) - { - if (!mServiceInstallationProgress) - { - Log.d(TAG, "Request new service installation"); - InstallCallbackInterface InstallQuery = new InstallCallbackInterface() { - private LoaderCallbackInterface mUserAppCallback = Callback; - public String getPackageName() - { - return "OpenCV Manager"; - } - public void install() { - Log.d(TAG, "Trying to install OpenCV Manager via Google Play"); - - boolean result = InstallServiceQuiet(AppContext); - if (result) - { - mServiceInstallationProgress = true; - Log.d(TAG, "Package installation started"); - } - else - { - Log.d(TAG, "OpenCV package was not installed!"); - int Status = LoaderCallbackInterface.MARKET_ERROR; - Log.d(TAG, "Init finished with status " + Status); - Log.d(TAG, "Unbind from service"); - Log.d(TAG, "Calling using callback"); - mUserAppCallback.onManagerConnected(Status); - } - } - - public void cancel() - { - Log.d(TAG, "OpenCV library installation was canceled"); - int Status = LoaderCallbackInterface.INSTALL_CANCELED; - Log.d(TAG, "Init finished with status " + Status); - Log.d(TAG, "Calling using callback"); - mUserAppCallback.onManagerConnected(Status); - } - - public void wait_install() - { - Log.e(TAG, "Installation was not started! Nothing to wait!"); - } - }; - - Callback.onPackageInstall(InstallCallbackInterface.NEW_INSTALLATION, InstallQuery); - } - else - { - Log.d(TAG, "Waiting current installation process"); - InstallCallbackInterface WaitQuery = new InstallCallbackInterface() { - private LoaderCallbackInterface mUserAppCallback = Callback; - public String getPackageName() - { - return "OpenCV Manager"; - } - public void install() - { - Log.e(TAG, "Nothing to install we just wait current installation"); - } - public void cancel() - { - Log.d(TAG, "Waiting for OpenCV canceled by user"); - mServiceInstallationProgress = false; - int Status = LoaderCallbackInterface.INSTALL_CANCELED; - Log.d(TAG, "Init finished with status " + Status); - Log.d(TAG, "Calling using callback"); - mUserAppCallback.onManagerConnected(Status); - } - public void wait_install() - { - InstallServiceQuiet(AppContext); - } - }; - - Callback.onPackageInstall(InstallCallbackInterface.INSTALLATION_PROGRESS, WaitQuery); - } - } - - /** - * URL of OpenCV Manager page on Google Play Market. - */ - protected static final String OPEN_CV_SERVICE_URL = "market://details?id=org.opencv.engine"; - - protected ServiceConnection mServiceConnection = new ServiceConnection() - { - public void onServiceConnected(ComponentName className, IBinder service) - { - Log.d(TAG, "Service connection created"); - mEngineService = OpenCVEngineInterface.Stub.asInterface(service); - if (null == mEngineService) - { - Log.d(TAG, "OpenCV Manager Service connection fails. May be service was not installed?"); - InstallService(mAppContext, mUserAppCallback); - } - else - { - mServiceInstallationProgress = false; - try - { - if (mEngineService.getEngineVersion() < MINIMUM_ENGINE_VERSION) - { - Log.d(TAG, "Init finished with status " + LoaderCallbackInterface.INCOMPATIBLE_MANAGER_VERSION); - Log.d(TAG, "Unbind from service"); - mAppContext.unbindService(mServiceConnection); - Log.d(TAG, "Calling using callback"); - mUserAppCallback.onManagerConnected(LoaderCallbackInterface.INCOMPATIBLE_MANAGER_VERSION); - return; - } - - Log.d(TAG, "Trying to get library path"); - String path = mEngineService.getLibPathByVersion(mOpenCVersion); - if ((null == path) || (path.length() == 0)) - { - if (!mLibraryInstallationProgress) - { - InstallCallbackInterface InstallQuery = new InstallCallbackInterface() { - public String getPackageName() - { - return "OpenCV library"; - } - public void install() { - Log.d(TAG, "Trying to install OpenCV lib via Google Play"); - try - { - if (mEngineService.installVersion(mOpenCVersion)) - { - mLibraryInstallationProgress = true; - Log.d(TAG, "Package installation started"); - Log.d(TAG, "Unbind from service"); - mAppContext.unbindService(mServiceConnection); - } - else - { - Log.d(TAG, "OpenCV package was not installed!"); - Log.d(TAG, "Init finished with status " + LoaderCallbackInterface.MARKET_ERROR); - Log.d(TAG, "Unbind from service"); - mAppContext.unbindService(mServiceConnection); - Log.d(TAG, "Calling using callback"); - mUserAppCallback.onManagerConnected(LoaderCallbackInterface.MARKET_ERROR); - } - } catch (RemoteException e) { - e.printStackTrace();; - Log.d(TAG, "Init finished with status " + LoaderCallbackInterface.INIT_FAILED); - Log.d(TAG, "Unbind from service"); - mAppContext.unbindService(mServiceConnection); - Log.d(TAG, "Calling using callback"); - mUserAppCallback.onManagerConnected(LoaderCallbackInterface.INIT_FAILED); - } - } - public void cancel() { - Log.d(TAG, "OpenCV library installation was canceled"); - Log.d(TAG, "Init finished with status " + LoaderCallbackInterface.INSTALL_CANCELED); - Log.d(TAG, "Unbind from service"); - mAppContext.unbindService(mServiceConnection); - Log.d(TAG, "Calling using callback"); - mUserAppCallback.onManagerConnected(LoaderCallbackInterface.INSTALL_CANCELED); - } - public void wait_install() { - Log.e(TAG, "Installation was not started! Nothing to wait!"); - } - }; - - mUserAppCallback.onPackageInstall(InstallCallbackInterface.NEW_INSTALLATION, InstallQuery); - } - else - { - InstallCallbackInterface WaitQuery = new InstallCallbackInterface() { - public String getPackageName() - { - return "OpenCV library"; - } - - public void install() { - Log.e(TAG, "Nothing to install we just wait current installation"); - } - public void cancel() - { - Log.d(TAG, "OpenCV library installation was canceled"); - mLibraryInstallationProgress = false; - Log.d(TAG, "Init finished with status " + LoaderCallbackInterface.INSTALL_CANCELED); - Log.d(TAG, "Unbind from service"); - mAppContext.unbindService(mServiceConnection); - Log.d(TAG, "Calling using callback"); - mUserAppCallback.onManagerConnected(LoaderCallbackInterface.INSTALL_CANCELED); - } - public void wait_install() { - Log.d(TAG, "Waiting for current installation"); - try - { - if (!mEngineService.installVersion(mOpenCVersion)) - { - Log.d(TAG, "OpenCV package was not installed!"); - Log.d(TAG, "Init finished with status " + LoaderCallbackInterface.MARKET_ERROR); - Log.d(TAG, "Calling using callback"); - mUserAppCallback.onManagerConnected(LoaderCallbackInterface.MARKET_ERROR); - } - else - { - Log.d(TAG, "Waiting for package installation"); - } - - Log.d(TAG, "Unbind from service"); - mAppContext.unbindService(mServiceConnection); - - } catch (RemoteException e) { - e.printStackTrace(); - Log.d(TAG, "Init finished with status " + LoaderCallbackInterface.INIT_FAILED); - Log.d(TAG, "Unbind from service"); - mAppContext.unbindService(mServiceConnection); - Log.d(TAG, "Calling using callback"); - mUserAppCallback.onManagerConnected(LoaderCallbackInterface.INIT_FAILED); - } - } - }; - - mUserAppCallback.onPackageInstall(InstallCallbackInterface.INSTALLATION_PROGRESS, WaitQuery); - } - return; - } - else - { - Log.d(TAG, "Trying to get library list"); - mLibraryInstallationProgress = false; - String libs = mEngineService.getLibraryList(mOpenCVersion); - Log.d(TAG, "Library list: \"" + libs + "\""); - Log.d(TAG, "First attempt to load libs"); - int status; - if (initOpenCVLibs(path, libs)) - { - Log.d(TAG, "First attempt to load libs is OK"); - String eol = System.getProperty("line.separator"); - for (String str : Core.getBuildInformation().split(eol)) - Log.i(TAG, str); - - status = LoaderCallbackInterface.SUCCESS; - } - else - { - Log.d(TAG, "First attempt to load libs fails"); - status = LoaderCallbackInterface.INIT_FAILED; - } - - Log.d(TAG, "Init finished with status " + status); - Log.d(TAG, "Unbind from service"); - mAppContext.unbindService(mServiceConnection); - Log.d(TAG, "Calling using callback"); - mUserAppCallback.onManagerConnected(status); - } - } - catch (RemoteException e) - { - e.printStackTrace(); - Log.d(TAG, "Init finished with status " + LoaderCallbackInterface.INIT_FAILED); - Log.d(TAG, "Unbind from service"); - mAppContext.unbindService(mServiceConnection); - Log.d(TAG, "Calling using callback"); - mUserAppCallback.onManagerConnected(LoaderCallbackInterface.INIT_FAILED); - } - } - } - - public void onServiceDisconnected(ComponentName className) - { - mEngineService = null; - } - }; - - private boolean loadLibrary(String AbsPath) - { - boolean result = true; - - Log.d(TAG, "Trying to load library " + AbsPath); - try - { - System.load(AbsPath); - Log.d(TAG, "OpenCV libs init was ok!"); - } - catch(UnsatisfiedLinkError e) - { - Log.d(TAG, "Cannot load library \"" + AbsPath + "\""); - e.printStackTrace(); - result = false; - } - - return result; - } - - private boolean initOpenCVLibs(String Path, String Libs) - { - Log.d(TAG, "Trying to init OpenCV libs"); - if ((null != Path) && (Path.length() != 0)) - { - boolean result = true; - if ((null != Libs) && (Libs.length() != 0)) - { - Log.d(TAG, "Trying to load libs by dependency list"); - StringTokenizer splitter = new StringTokenizer(Libs, ";"); - while(splitter.hasMoreTokens()) - { - String AbsLibraryPath = Path + File.separator + splitter.nextToken(); - result &= loadLibrary(AbsLibraryPath); - } - } - else - { - // If the dependencies list is not defined or empty. - String AbsLibraryPath = Path + File.separator + "libopencv_java4.so"; - result = loadLibrary(AbsLibraryPath); - } - - return result; - } - else - { - Log.d(TAG, "Library path \"" + Path + "\" is empty"); - return false; - } - } -} diff --git a/modules/java/generator/android/java/org/opencv/android/BaseLoaderCallback.java b/modules/java/generator/android/java/org/opencv/android/BaseLoaderCallback.java deleted file mode 100644 index 8ece662514..0000000000 --- a/modules/java/generator/android/java/org/opencv/android/BaseLoaderCallback.java +++ /dev/null @@ -1,141 +0,0 @@ -package org.opencv.android; - -import android.app.Activity; -import android.app.AlertDialog; -import android.content.Context; -import android.content.DialogInterface; -import android.content.DialogInterface.OnClickListener; -import android.util.Log; - -/** - * Basic implementation of LoaderCallbackInterface. - */ -public abstract class BaseLoaderCallback implements LoaderCallbackInterface { - - public BaseLoaderCallback(Context AppContext) { - mAppContext = AppContext; - } - - public void onManagerConnected(int status) - { - switch (status) - { - /** OpenCV initialization was successful. **/ - case LoaderCallbackInterface.SUCCESS: - { - /** Application must override this method to handle successful library initialization. **/ - } break; - /** OpenCV loader can not start Google Play Market. **/ - case LoaderCallbackInterface.MARKET_ERROR: - { - Log.e(TAG, "Package installation failed!"); - AlertDialog MarketErrorMessage = new AlertDialog.Builder(mAppContext).create(); - MarketErrorMessage.setTitle("OpenCV Manager"); - MarketErrorMessage.setMessage("Package installation failed!"); - MarketErrorMessage.setCancelable(false); // This blocks the 'BACK' button - MarketErrorMessage.setButton(AlertDialog.BUTTON_POSITIVE, "OK", new OnClickListener() { - public void onClick(DialogInterface dialog, int which) { - finish(); - } - }); - MarketErrorMessage.show(); - } break; - /** Package installation has been canceled. **/ - case LoaderCallbackInterface.INSTALL_CANCELED: - { - Log.d(TAG, "OpenCV library installation was canceled by user"); - finish(); - } break; - /** Application is incompatible with this version of OpenCV Manager. Possibly, a service update is required. **/ - case LoaderCallbackInterface.INCOMPATIBLE_MANAGER_VERSION: - { - Log.d(TAG, "OpenCV Manager Service is uncompatible with this app!"); - AlertDialog IncomatibilityMessage = new AlertDialog.Builder(mAppContext).create(); - IncomatibilityMessage.setTitle("OpenCV Manager"); - IncomatibilityMessage.setMessage("OpenCV Manager service is incompatible with this app. Try to update it via Google Play."); - IncomatibilityMessage.setCancelable(false); // This blocks the 'BACK' button - IncomatibilityMessage.setButton(AlertDialog.BUTTON_POSITIVE, "OK", new OnClickListener() { - public void onClick(DialogInterface dialog, int which) { - finish(); - } - }); - IncomatibilityMessage.show(); - } break; - /** Other status, i.e. INIT_FAILED. **/ - default: - { - Log.e(TAG, "OpenCV loading failed!"); - AlertDialog InitFailedDialog = new AlertDialog.Builder(mAppContext).create(); - InitFailedDialog.setTitle("OpenCV error"); - InitFailedDialog.setMessage("OpenCV was not initialised correctly. Application will be shut down"); - InitFailedDialog.setCancelable(false); // This blocks the 'BACK' button - InitFailedDialog.setButton(AlertDialog.BUTTON_POSITIVE, "OK", new OnClickListener() { - - public void onClick(DialogInterface dialog, int which) { - finish(); - } - }); - - InitFailedDialog.show(); - } break; - } - } - - public void onPackageInstall(final int operation, final InstallCallbackInterface callback) - { - switch (operation) - { - case InstallCallbackInterface.NEW_INSTALLATION: - { - AlertDialog InstallMessage = new AlertDialog.Builder(mAppContext).create(); - InstallMessage.setTitle("Package not found"); - InstallMessage.setMessage(callback.getPackageName() + " package was not found! Try to install it?"); - InstallMessage.setCancelable(false); // This blocks the 'BACK' button - InstallMessage.setButton(AlertDialog.BUTTON_POSITIVE, "Yes", new OnClickListener() - { - public void onClick(DialogInterface dialog, int which) - { - callback.install(); - } - }); - - InstallMessage.setButton(AlertDialog.BUTTON_NEGATIVE, "No", new OnClickListener() { - - public void onClick(DialogInterface dialog, int which) - { - callback.cancel(); - } - }); - - InstallMessage.show(); - } break; - case InstallCallbackInterface.INSTALLATION_PROGRESS: - { - AlertDialog WaitMessage = new AlertDialog.Builder(mAppContext).create(); - WaitMessage.setTitle("OpenCV is not ready"); - WaitMessage.setMessage("Installation is in progress. Wait or exit?"); - WaitMessage.setCancelable(false); // This blocks the 'BACK' button - WaitMessage.setButton(AlertDialog.BUTTON_POSITIVE, "Wait", new OnClickListener() { - public void onClick(DialogInterface dialog, int which) { - callback.wait_install(); - } - }); - WaitMessage.setButton(AlertDialog.BUTTON_NEGATIVE, "Exit", new OnClickListener() { - public void onClick(DialogInterface dialog, int which) { - callback.cancel(); - } - }); - - WaitMessage.show(); - } break; - } - } - - void finish() - { - ((Activity) mAppContext).finish(); - } - - protected Context mAppContext; - private final static String TAG = "OCV/BaseLoaderCallback"; -} diff --git a/modules/java/generator/android/java/org/opencv/android/FpsMeter.java b/modules/java/generator/android/java/org/opencv/android/FpsMeter.java index d22c68e415..5727ead3f8 100644 --- a/modules/java/generator/android/java/org/opencv/android/FpsMeter.java +++ b/modules/java/generator/android/java/org/opencv/android/FpsMeter.java @@ -54,6 +54,7 @@ public class FpsMeter { } public void setResolution(int width, int height) { + Log.d(TAG, "FpsMeter.setResolution " + Integer.valueOf(mWidth) + "x" + Integer.valueOf(mHeight)); mWidth = width; mHeight = height; } diff --git a/modules/java/generator/android/java/org/opencv/android/InstallCallbackInterface.java b/modules/java/generator/android/java/org/opencv/android/InstallCallbackInterface.java deleted file mode 100644 index f68027a7ba..0000000000 --- a/modules/java/generator/android/java/org/opencv/android/InstallCallbackInterface.java +++ /dev/null @@ -1,34 +0,0 @@ -package org.opencv.android; - -/** - * Installation callback interface. - */ -public interface InstallCallbackInterface -{ - /** - * New package installation is required. - */ - static final int NEW_INSTALLATION = 0; - /** - * Current package installation is in progress. - */ - static final int INSTALLATION_PROGRESS = 1; - - /** - * Target package name. - * @return Return target package name. - */ - public String getPackageName(); - /** - * Installation is approved. - */ - public void install(); - /** - * Installation is canceled. - */ - public void cancel(); - /** - * Wait for package installation. - */ - public void wait_install(); -}; diff --git a/modules/java/generator/android/java/org/opencv/android/LoaderCallbackInterface.java b/modules/java/generator/android/java/org/opencv/android/LoaderCallbackInterface.java deleted file mode 100644 index a941e8377b..0000000000 --- a/modules/java/generator/android/java/org/opencv/android/LoaderCallbackInterface.java +++ /dev/null @@ -1,40 +0,0 @@ -package org.opencv.android; - -/** - * Interface for callback object in case of asynchronous initialization of OpenCV. - */ -public interface LoaderCallbackInterface -{ - /** - * OpenCV initialization finished successfully. - */ - static final int SUCCESS = 0; - /** - * Google Play Market cannot be invoked. - */ - static final int MARKET_ERROR = 2; - /** - * OpenCV library installation has been canceled by the user. - */ - static final int INSTALL_CANCELED = 3; - /** - * This version of OpenCV Manager Service is incompatible with the app. Possibly, a service update is required. - */ - static final int INCOMPATIBLE_MANAGER_VERSION = 4; - /** - * OpenCV library initialization has failed. - */ - static final int INIT_FAILED = 0xff; - - /** - * Callback method, called after OpenCV library initialization. - * @param status status of initialization (see initialization status constants). - */ - public void onManagerConnected(int status); - - /** - * Callback method, called in case the package installation is needed. - * @param callback answer object with approve and cancel methods and the package description. - */ - public void onPackageInstall(final int operation, InstallCallbackInterface callback); -}; diff --git a/modules/java/generator/android/java/org/opencv/android/OpenCVLoader.java.in b/modules/java/generator/android/java/org/opencv/android/OpenCVLoader.java.in index 625c3daf27..91cc534e84 100644 --- a/modules/java/generator/android/java/org/opencv/android/OpenCVLoader.java.in +++ b/modules/java/generator/android/java/org/opencv/android/OpenCVLoader.java.in @@ -7,102 +7,26 @@ import android.content.Context; */ public class OpenCVLoader { - /** - * OpenCV Library version 2.4.2. - */ - public static final String OPENCV_VERSION_2_4_2 = "2.4.2"; - - /** - * OpenCV Library version 2.4.3. - */ - public static final String OPENCV_VERSION_2_4_3 = "2.4.3"; - - /** - * OpenCV Library version 2.4.4. - */ - public static final String OPENCV_VERSION_2_4_4 = "2.4.4"; - - /** - * OpenCV Library version 2.4.5. - */ - public static final String OPENCV_VERSION_2_4_5 = "2.4.5"; - - /** - * OpenCV Library version 2.4.6. - */ - public static final String OPENCV_VERSION_2_4_6 = "2.4.6"; - - /** - * OpenCV Library version 2.4.7. - */ - public static final String OPENCV_VERSION_2_4_7 = "2.4.7"; - - /** - * OpenCV Library version 2.4.8. - */ - public static final String OPENCV_VERSION_2_4_8 = "2.4.8"; - - /** - * OpenCV Library version 2.4.9. - */ - public static final String OPENCV_VERSION_2_4_9 = "2.4.9"; - - /** - * OpenCV Library version 2.4.10. - */ - public static final String OPENCV_VERSION_2_4_10 = "2.4.10"; - - /** - * OpenCV Library version 2.4.11. - */ - public static final String OPENCV_VERSION_2_4_11 = "2.4.11"; - - /** - * OpenCV Library version 2.4.12. - */ - public static final String OPENCV_VERSION_2_4_12 = "2.4.12"; - - /** - * OpenCV Library version 2.4.13. - */ - public static final String OPENCV_VERSION_2_4_13 = "2.4.13"; - - /** - * OpenCV Library version 3.0.0. - */ - public static final String OPENCV_VERSION_3_0_0 = "3.0.0"; - - /** - * OpenCV Library version 3.1.0. - */ - public static final String OPENCV_VERSION_3_1_0 = "3.1.0"; - - /** - * OpenCV Library version 3.2.0. - */ - public static final String OPENCV_VERSION_3_2_0 = "3.2.0"; - - /** - * OpenCV Library version 3.3.0. - */ - public static final String OPENCV_VERSION_3_3_0 = "3.3.0"; - - /** - * OpenCV Library version 3.4.0. - */ - public static final String OPENCV_VERSION_3_4_0 = "3.4.0"; - /** * Current OpenCV Library version */ public static final String OPENCV_VERSION = "@OPENCV_VERSION_MAJOR@.@OPENCV_VERSION_MINOR@.@OPENCV_VERSION_PATCH@"; + /** + * Synonym for initLocal. Deprecated. + */ + @Deprecated + public static boolean initDebug() + { + return StaticHelper.initOpenCV(false); + } + /** * Loads and initializes OpenCV library from current application package. Roughly, it's an analog of system.loadLibrary("opencv_java"). * @return Returns true is initialization of OpenCV was successful. */ - public static boolean initDebug() + public static boolean initLocal() { return StaticHelper.initOpenCV(false); } @@ -112,21 +36,9 @@ public class OpenCVLoader * @param InitCuda load and initialize CUDA runtime libraries. * @return Returns true is initialization of OpenCV was successful. */ + @Deprecated public static boolean initDebug(boolean InitCuda) { return StaticHelper.initOpenCV(InitCuda); } - - /** - * Loads and initializes OpenCV library using OpenCV Engine service. - * @param Version OpenCV library version. - * @param AppContext application context for connecting to the service. - * @param Callback object, that implements LoaderCallbackInterface for handling the connection status. - * @return Returns true if initialization of OpenCV is successful. - */ - public static boolean initAsync(String Version, Context AppContext, - LoaderCallbackInterface Callback) - { - return AsyncServiceHelper.initOpenCV(Version, AppContext, Callback); - } } diff --git a/modules/java/generator/android/java/org/opencv/android/StaticHelper.java b/modules/java/generator/android/java/org/opencv/android/StaticHelper.java index 934dd7570c..7b3b22ae31 100644 --- a/modules/java/generator/android/java/org/opencv/android/StaticHelper.java +++ b/modules/java/generator/android/java/org/opencv/android/StaticHelper.java @@ -13,30 +13,10 @@ class StaticHelper { String libs = ""; if(InitCuda) - { - loadLibrary("cudart"); - loadLibrary("nppc"); - loadLibrary("nppi"); - loadLibrary("npps"); - loadLibrary("cufft"); - loadLibrary("cublas"); - } + Log.w(TAG, "CUDA support was removed!"); - Log.d(TAG, "Trying to get library list"); - - try - { - System.loadLibrary("opencv_info"); - libs = getLibraryList(); - } - catch(UnsatisfiedLinkError e) - { - Log.e(TAG, "OpenCV error: Cannot load info library for OpenCV"); - } - - Log.d(TAG, "Library list: \"" + libs + "\""); Log.d(TAG, "First attempt to load libs"); - if (initOpenCVLibs(libs)) + if (loadLibrary("opencv_java5")) { Log.d(TAG, "First attempt to load libs is OK"); String eol = System.getProperty("line.separator"); @@ -74,30 +54,6 @@ class StaticHelper { return result; } - private static boolean initOpenCVLibs(String Libs) - { - Log.d(TAG, "Trying to init OpenCV libs"); - - boolean result = true; - - if ((null != Libs) && (Libs.length() != 0)) - { - Log.d(TAG, "Trying to load libs by dependency list"); - StringTokenizer splitter = new StringTokenizer(Libs, ";"); - while(splitter.hasMoreTokens()) - { - result &= loadLibrary(splitter.nextToken()); - } - } - else - { - // If dependencies list is not defined or empty. - result = loadLibrary("opencv_java4"); - } - - return result; - } - private static final String TAG = "OpenCV/StaticHelper"; private static native String getLibraryList(); diff --git a/modules/java/generator/android/java/org/opencv/engine/OpenCVEngineInterface.aidl b/modules/java/generator/android/java/org/opencv/engine/OpenCVEngineInterface.aidl deleted file mode 100644 index 21fe5f716b..0000000000 --- a/modules/java/generator/android/java/org/opencv/engine/OpenCVEngineInterface.aidl +++ /dev/null @@ -1,33 +0,0 @@ -package org.opencv.engine; - -/** -* Class provides a Java interface for OpenCV Engine Service. It's synchronous with native OpenCVEngine class. -*/ -interface OpenCVEngineInterface -{ - /** - * @return Returns service version. - */ - int getEngineVersion(); - - /** - * Finds an installed OpenCV library. - * @param OpenCV version. - * @return Returns path to OpenCV native libs or an empty string if OpenCV can not be found. - */ - String getLibPathByVersion(String version); - - /** - * Tries to install defined version of OpenCV from Google Play Market. - * @param OpenCV version. - * @return Returns true if installation was successful or OpenCV package has been already installed. - */ - boolean installVersion(String version); - - /** - * Returns list of libraries in loading order, separated by semicolon. - * @param OpenCV version. - * @return Returns names of OpenCV libraries, separated by semicolon. - */ - String getLibraryList(String version); -} diff --git a/modules/java/generator/gen_java.py b/modules/java/generator/gen_java.py index 6a4ece5603..ba5ed49416 100755 --- a/modules/java/generator/gen_java.py +++ b/modules/java/generator/gen_java.py @@ -1433,7 +1433,8 @@ if __name__ == "__main__": java_base_path = os.path.join(dstdir, 'java'); mkdir_p(java_base_path) java_test_base_path = os.path.join(dstdir, 'test'); mkdir_p(java_test_base_path) - for (subdir, target_subdir) in [('src/java', 'java'), ('android/java', None), ('android-21/java', None)]: + for (subdir, target_subdir) in [('src/java', 'java'), ('android/java', None), + ('android-21/java', None), ('android-24/java', None)]: if target_subdir is None: target_subdir = subdir java_files_dir = os.path.join(SCRIPT_DIR, subdir) diff --git a/modules/js/generator/embindgen.py b/modules/js/generator/embindgen.py index 8a16f92f5e..005ac9f175 100644 --- a/modules/js/generator/embindgen.py +++ b/modules/js/generator/embindgen.py @@ -482,7 +482,7 @@ class JSWrapperGenerator(object): ret_type = type_dict[ptr_type] for key in type_dict: if key in ret_type: - ret_type = re.sub('(^|[^\w])' + key + '($|[^\w])', type_dict[key], 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: @@ -670,7 +670,7 @@ class JSWrapperGenerator(object): # Replace types. Instead of ret_type.replace we use regular # expression to exclude false matches. # See https://github.com/opencv/opencv/issues/15514 - ret_type = re.sub('(^|[^\w])' + key + '($|[^\w])', type_dict[key], ret_type) + ret_type = re.sub(r"\b" + key + r"\b", type_dict[key], ret_type) if variant.constret and ret_type.startswith('const') == False: ret_type = 'const ' + ret_type if variant.refret and ret_type.endswith('&') == False: diff --git a/modules/js/src/core_bindings.cpp b/modules/js/src/core_bindings.cpp index addee2de20..d5bf9b076c 100644 --- a/modules/js/src/core_bindings.cpp +++ b/modules/js/src/core_bindings.cpp @@ -99,6 +99,10 @@ typedef QRCodeDetectorAruco::Params QRCodeDetectorAruco_Params; using namespace cv::dnn; #endif +#ifdef HAVE_OPENCV_FEATURES2D +typedef SimpleBlobDetector::Params SimpleBlobDetector_Params; +#endif + #ifdef HAVE_OPENCV_VIDEO typedef TrackerMIL::Params TrackerMIL_Params; #endif @@ -461,6 +465,7 @@ EMSCRIPTEN_BINDINGS(binding_utils) register_vector("CharVector"); register_vector("FloatVector"); register_vector("DoubleVector"); + register_vector("StringVector"); register_vector("PointVector"); register_vector("MatVector"); register_vector("RectVector"); diff --git a/modules/js/test/test_features2d.js b/modules/js/test/test_features2d.js index 45e3d4d715..c5eb73a123 100644 --- a/modules/js/test/test_features2d.js +++ b/modules/js/test/test_features2d.js @@ -62,6 +62,15 @@ QUnit.test('Detectors', function(assert) { assert.equal(kp.size(), 53, 'AKAZE'); }); +QUnit.test('SimpleBlobDetector', function(assert) { + let image = generateTestFrame(); + + let kp = new cv.KeyPointVector(); + let sbd = new cv.SimpleBlobDetector(); + sbd.detect(image, kp); + assert.equal(kp.size(), 0); +}); + QUnit.test('BFMatcher', function(assert) { // Generate key points. let image = generateTestFrame(); diff --git a/modules/objc/generator/CMakeLists.txt b/modules/objc/generator/CMakeLists.txt index b3cbbd3f5f..bd8f8325b3 100644 --- a/modules/objc/generator/CMakeLists.txt +++ b/modules/objc/generator/CMakeLists.txt @@ -9,6 +9,8 @@ file(REMOVE_RECURSE "${OPENCV_OBJC_BINDINGS_DIR}/osx") file(REMOVE "${OPENCV_DEPHELPER}/gen_opencv_objc_source_osx") # force re-run after CMake file(REMOVE_RECURSE "${OPENCV_OBJC_BINDINGS_DIR}/ios") file(REMOVE "${OPENCV_DEPHELPER}/gen_opencv_objc_source_ios") # force re-run after CMake +file(REMOVE_RECURSE "${OPENCV_OBJC_BINDINGS_DIR}/visionos") +file(REMOVE "${OPENCV_DEPHELPER}/gen_opencv_objc_source_visionos") # force re-run after CMake # This file is included from a subdirectory set(OBJC_SOURCE_DIR "${CMAKE_CURRENT_SOURCE_DIR}/..") @@ -67,6 +69,8 @@ string(REPLACE "opencv_" "" MODULES "${OPENCV_OBJC_MODULES}") if(NOT DEFINED OPENCV_OBJC_TARGET AND APPLE_FRAMEWORK) if(IOS) set(OPENCV_OBJC_TARGET "ios") + elseif(XROS) + set(OPENCV_OBJC_TARGET "visionos") else() set(OPENCV_OBJC_TARGET "osx") endif() @@ -117,6 +121,7 @@ if(OPENCV_OBJC_TARGET) else() ocv_add_objc_generated_target(osx) ocv_add_objc_generated_target(ios) + ocv_add_objc_generated_target(visionos) endif() add_custom_target(gen_opencv_objc_source diff --git a/modules/objc/generator/gen_objc.py b/modules/objc/generator/gen_objc.py index b140070d95..6c3b309564 100755 --- a/modules/objc/generator/gen_objc.py +++ b/modules/objc/generator/gen_objc.py @@ -1601,7 +1601,7 @@ if __name__ == "__main__": arg_parser = argparse.ArgumentParser(description='OpenCV Objective-C Wrapper Generator') arg_parser.add_argument('-p', '--parser', required=True, help='OpenCV header parser') arg_parser.add_argument('-c', '--config', required=True, help='OpenCV modules config') - arg_parser.add_argument('-t', '--target', required=True, help='Target (either ios or osx)') + arg_parser.add_argument('-t', '--target', required=True, help='Target (either ios or osx or visionos)') arg_parser.add_argument('-f', '--framework', required=True, help='Framework name') args=arg_parser.parse_args() @@ -1672,6 +1672,7 @@ if __name__ == "__main__": logging.info("\nCommon headers (%d):\n%s", len(common_headers), pformat(common_headers)) gendict_fname = os.path.join(misc_location, 'gen_dict.json') + module_source_map = {} if os.path.exists(gendict_fname): with open(gendict_fname) as f: gen_type_dict = json.load(f) @@ -1688,6 +1689,7 @@ if __name__ == "__main__": header_fix.update(gen_type_dict.get("header_fix", {})) enum_fix.update(gen_type_dict.get("enum_fix", {})) const_fix.update(gen_type_dict.get("const_fix", {})) + module_source_map = gen_type_dict.get("SourceMap", {}) namespaces_dict.update(gen_type_dict.get("namespaces_dict", {})) module_imports += gen_type_dict.get("module_imports", []) @@ -1696,15 +1698,10 @@ if __name__ == "__main__": if os.path.exists(objc_files_dir): copied_files += copy_objc_files(objc_files_dir, objc_base_path, module, True) - if args.target == 'ios': - ios_files_dir = os.path.join(misc_location, 'ios') - if os.path.exists(ios_files_dir): - copied_files += copy_objc_files(ios_files_dir, objc_base_path, module, True) - - if args.target == 'osx': - osx_files_dir = os.path.join(misc_location, 'macosx') - if os.path.exists(osx_files_dir): - copied_files += copy_objc_files(osx_files_dir, objc_base_path, module, True) + target_path = 'macosx' if args.target == 'osx' else module_source_map.get(args.target, args.target) + target_files_dir = os.path.join(misc_location, target_path) + if os.path.exists(target_files_dir): + copied_files += copy_objc_files(target_files_dir, objc_base_path, module, True) objc_test_files_dir = os.path.join(misc_location, 'test') if os.path.exists(objc_test_files_dir): diff --git a/modules/objdetect/include/opencv2/objdetect/aruco_detector.hpp b/modules/objdetect/include/opencv2/objdetect/aruco_detector.hpp index c51abb0c93..d537e43dc5 100644 --- a/modules/objdetect/include/opencv2/objdetect/aruco_detector.hpp +++ b/modules/objdetect/include/opencv2/objdetect/aruco_detector.hpp @@ -33,7 +33,7 @@ struct CV_EXPORTS_W_SIMPLE DetectorParameters { polygonalApproxAccuracyRate = 0.03; minCornerDistanceRate = 0.05; minDistanceToBorder = 3; - minMarkerDistanceRate = 0.05; + minMarkerDistanceRate = 0.125; cornerRefinementMethod = (int)CORNER_REFINE_NONE; cornerRefinementWinSize = 5; relativeCornerRefinmentWinSize = 0.3f; @@ -100,12 +100,26 @@ struct CV_EXPORTS_W_SIMPLE DetectorParameters { /// minimum distance of any corner to the image border for detected markers (in pixels) (default 3) CV_PROP_RW int minDistanceToBorder; - /** @brief minimum mean distance beetween two marker corners to be considered imilar, so that the smaller one is removed. + /** @brief minimum average distance between the corners of the two markers to be grouped (default 0.125). * - * The rate is relative to the smaller perimeter of the two markers (default 0.05). + * The rate is relative to the smaller perimeter of the two markers. + * Two markers are grouped if average distance between the corners of the two markers is less than + * min(MarkerPerimeter1, MarkerPerimeter2)*minMarkerDistanceRate. + * + * default value is 0.125 because 0.125*MarkerPerimeter = (MarkerPerimeter / 4) * 0.5 = half the side of the marker. + * + * @note default value was changed from 0.05 after 4.8.1 release, because the filtering algorithm has been changed. + * Now a few candidates from the same group can be added to the list of candidates if they are far from each other. + * @sa minGroupDistance. */ CV_PROP_RW double minMarkerDistanceRate; + /** @brief minimum average distance between the corners of the two markers in group to add them to the list of candidates + * + * The average distance between the corners of the two markers is calculated relative to its module size (default 0.21). + */ + CV_PROP_RW float minGroupDistance = 0.21f; + /** @brief default value CORNER_REFINE_NONE */ CV_PROP_RW int cornerRefinementMethod; diff --git a/modules/objdetect/include/opencv2/objdetect/face.hpp b/modules/objdetect/include/opencv2/objdetect/face.hpp index d8e96b5dfd..9b53f83128 100644 --- a/modules/objdetect/include/opencv2/objdetect/face.hpp +++ b/modules/objdetect/include/opencv2/objdetect/face.hpp @@ -71,7 +71,7 @@ public: */ CV_WRAP virtual int detect(InputArray image, OutputArray faces) = 0; - /** @brief Creates an instance of this class with given parameters + /** @brief Creates an instance of face detector class with given parameters * * @param model the path to the requested model * @param config the path to the config file for compability, which is not requested for ONNX models @@ -90,6 +90,29 @@ public: int top_k = 5000, int backend_id = 0, int target_id = 0); + + /** @overload + * + * @param framework Name of origin framework + * @param bufferModel A buffer with a content of binary file with weights + * @param bufferConfig A buffer with a content of text file contains network configuration + * @param input_size the size of the input image + * @param score_threshold the threshold to filter out bounding boxes of score smaller than the given value + * @param nms_threshold the threshold to suppress bounding boxes of IoU bigger than the given value + * @param top_k keep top K bboxes before NMS + * @param backend_id the id of backend + * @param target_id the id of target device + */ + CV_WRAP static Ptr create(const String& framework, + const std::vector& bufferModel, + const std::vector& bufferConfig, + const Size& input_size, + float score_threshold = 0.9f, + float nms_threshold = 0.3f, + int top_k = 5000, + int backend_id = 0, + int target_id = 0); + }; /** @brief DNN-based face recognizer diff --git a/modules/objdetect/perf/perf_qrcode_pipeline.cpp b/modules/objdetect/perf/perf_qrcode_pipeline.cpp index 150ed8cbbe..3d6360ca48 100644 --- a/modules/objdetect/perf/perf_qrcode_pipeline.cpp +++ b/modules/objdetect/perf/perf_qrcode_pipeline.cpp @@ -29,7 +29,6 @@ PERF_TEST_P_(Perf_Objdetect_QRCode, detect) SANITY_CHECK_NOTHING(); } -#ifdef HAVE_QUIRC PERF_TEST_P_(Perf_Objdetect_QRCode, decode) { const std::string name_current_image = GetParam(); @@ -52,7 +51,6 @@ PERF_TEST_P_(Perf_Objdetect_QRCode, decode) check_qr(root, name_current_image, "test_images", corners, {decoded_info}, pixels_error); SANITY_CHECK_NOTHING(); } -#endif typedef ::perf::TestBaseWithParam> Perf_Objdetect_QRCode_Multi; @@ -78,7 +76,6 @@ PERF_TEST_P_(Perf_Objdetect_QRCode_Multi, detectMulti) SANITY_CHECK_NOTHING(); } -#ifdef HAVE_QUIRC PERF_TEST_P_(Perf_Objdetect_QRCode_Multi, decodeMulti) { const std::string name_current_image = get<0>(GetParam()); @@ -116,7 +113,6 @@ PERF_TEST_P_(Perf_Objdetect_QRCode_Multi, decodeMulti) check_qr(root, name_current_image, "multiple_images", corners_result, decoded_info, pixels_error, true); SANITY_CHECK_NOTHING(); } -#endif INSTANTIATE_TEST_CASE_P(/*nothing*/, Perf_Objdetect_QRCode, ::testing::Values( @@ -163,7 +159,6 @@ PERF_TEST_P_(Perf_Objdetect_Not_QRCode, detect) SANITY_CHECK_NOTHING(); } -#ifdef HAVE_QUIRC PERF_TEST_P_(Perf_Objdetect_Not_QRCode, decode) { Mat straight_barcode; @@ -195,7 +190,6 @@ PERF_TEST_P_(Perf_Objdetect_Not_QRCode, decode) TEST_CYCLE() ASSERT_TRUE(qrcode.decode(not_qr_code, corners, straight_barcode).empty()); SANITY_CHECK_NOTHING(); } -#endif INSTANTIATE_TEST_CASE_P(/*nothing*/, Perf_Objdetect_Not_QRCode, ::testing::Combine( diff --git a/modules/objdetect/src/aruco/aruco_detector.cpp b/modules/objdetect/src/aruco/aruco_detector.cpp index 8faa92a3da..71fdc17182 100644 --- a/modules/objdetect/src/aruco/aruco_detector.cpp +++ b/modules/objdetect/src/aruco/aruco_detector.cpp @@ -50,6 +50,7 @@ static inline bool readWrite(DetectorParameters ¶ms, const FileNode* readNod readNode, writeStorage); check |= readWriteParameter("minOtsuStdDev", params.minOtsuStdDev, readNode, writeStorage); check |= readWriteParameter("errorCorrectionRate", params.errorCorrectionRate, readNode, writeStorage); + check |= readWriteParameter("minGroupDistance", params.minGroupDistance, readNode, writeStorage); // new aruco 3 functionality check |= readWriteParameter("useAruco3Detection", params.useAruco3Detection, readNode, writeStorage); check |= readWriteParameter("minSideLengthCanonicalImg", params.minSideLengthCanonicalImg, readNode, writeStorage); @@ -212,149 +213,65 @@ static void _reorderCandidatesCorners(vector > &candidates) { } } -/** - * @brief to make sure that the corner's order of both candidates (default/white) is the same - */ -static vector alignContourOrder(Point2f corner, vector candidate) { - uint8_t r=0; - double min = norm( Vec2f( corner - candidate[0] ), NORM_L2SQR); - for(uint8_t pos=1; pos < 4; pos++) { - double nDiff = norm( Vec2f( corner - candidate[pos] ), NORM_L2SQR); - if(nDiff < min){ - r = pos; - min =nDiff; - } +static float getAverageModuleSize(const vector& markerCorners, int markerSize, int markerBorderBits) { + float averageArucoModuleSize = 0.f; + for (size_t i = 0ull; i < 4ull; i++) { + averageArucoModuleSize += sqrt(normL2Sqr(Point2f(markerCorners[i] - markerCorners[(i+1ull) % 4ull]))); } - std::rotate(candidate.begin(), candidate.begin() + r, candidate.end()); - return candidate; + int numModules = markerSize + markerBorderBits * 2; + averageArucoModuleSize /= ((float)markerCorners.size()*numModules); + return averageArucoModuleSize; } -/** - * @brief Check candidates that are too close to each other, save the potential candidates - * (i.e. biggest/smallest contour) and remove the rest - */ -static void _filterTooCloseCandidates(const vector > &candidatesIn, - vector > > &candidatesSetOut, - const vector > &contoursIn, - vector > > &contoursSetOut, - double minMarkerDistanceRate, bool detectInvertedMarker) { +static bool checkMarker1InMarker2(const vector& marker1, const vector& marker2) { + return pointPolygonTest(marker2, marker1[0], false) >= 0 && pointPolygonTest(marker2, marker1[1], false) >= 0 && + pointPolygonTest(marker2, marker1[2], false) >= 0 && pointPolygonTest(marker2, marker1[3], false) >= 0; +} - CV_Assert(minMarkerDistanceRate >= 0); - vector candGroup; - candGroup.resize(candidatesIn.size(), -1); - vector > groupedCandidates; - for(unsigned int i = 0; i < candidatesIn.size(); i++) { - bool isSingleContour = true; - for(unsigned int j = i + 1; j < candidatesIn.size(); j++) { +struct MarkerCandidate { + vector corners; + vector contour; + float perimeter = 0.f; +}; - int minimumPerimeter = min((int)contoursIn[i].size(), (int)contoursIn[j].size() ); +struct MarkerCandidateTree : MarkerCandidate{ + int parent = -1; + int depth = 0; + vector closeContours; - // fc is the first corner considered on one of the markers, 4 combinations are possible - for(int fc = 0; fc < 4; fc++) { - double distSq = 0; - for(int c = 0; c < 4; c++) { - // modC is the corner considering first corner is fc - int modC = (c + fc) % 4; - distSq += (candidatesIn[i][modC].x - candidatesIn[j][c].x) * - (candidatesIn[i][modC].x - candidatesIn[j][c].x) + - (candidatesIn[i][modC].y - candidatesIn[j][c].y) * - (candidatesIn[i][modC].y - candidatesIn[j][c].y); - } - distSq /= 4.; + MarkerCandidateTree() {} - // if mean square distance is too low, remove the smaller one of the two markers - double minMarkerDistancePixels = double(minimumPerimeter) * minMarkerDistanceRate; - if(distSq < minMarkerDistancePixels * minMarkerDistancePixels) { - isSingleContour = false; - // i and j are not related to a group - if(candGroup[i]<0 && candGroup[j]<0){ - // mark candidates with their corresponding group number - candGroup[i] = candGroup[j] = (int)groupedCandidates.size(); - - // create group - vector grouped; - grouped.push_back(i); - grouped.push_back(j); - groupedCandidates.push_back( grouped ); - } - // i is related to a group - else if(candGroup[i] > -1 && candGroup[j] == -1){ - int group = candGroup[i]; - candGroup[j] = group; - - // add to group - groupedCandidates[group].push_back( j ); - } - // j is related to a group - else if(candGroup[j] > -1 && candGroup[i] == -1){ - int group = candGroup[j]; - candGroup[i] = group; - - // add to group - groupedCandidates[group].push_back( i ); - } - } - } - } - if (isSingleContour && candGroup[i] < 0) - { - candGroup[i] = (int)groupedCandidates.size(); - vector grouped; - grouped.push_back(i); - grouped.push_back(i); // step "save possible candidates" require minimum 2 elements - groupedCandidates.push_back(grouped); + MarkerCandidateTree(vector&& corners_, vector&& contour_) { + corners = std::move(corners_); + contour = std::move(contour_); + perimeter = 0.f; + for (size_t i = 0ull; i < 4ull; i++) { + perimeter += sqrt(normL2Sqr(Point2f(corners[i] - corners[(i+1ull) % 4ull]))); } } - // save possible candidates - candidatesSetOut.clear(); - contoursSetOut.clear(); - - vector > biggerCandidates; - vector > biggerContours; - vector > smallerCandidates; - vector > smallerContours; - - // save possible candidates - for(unsigned int i = 0; i < groupedCandidates.size(); i++) { - unsigned int smallerIdx = groupedCandidates[i][0]; - unsigned int biggerIdx = smallerIdx; - double smallerArea = contourArea(candidatesIn[smallerIdx]); - double biggerArea = smallerArea; - - // evaluate group elements - for(unsigned int j = 1; j < groupedCandidates[i].size(); j++) { - unsigned int currIdx = groupedCandidates[i][j]; - double currArea = contourArea(candidatesIn[currIdx]); - - // check if current contour is bigger - if(currArea >= biggerArea) { - biggerIdx = currIdx; - biggerArea = currArea; - } - - // check if current contour is smaller - if(currArea < smallerArea && detectInvertedMarker) { - smallerIdx = currIdx; - smallerArea = currArea; - } - } - - // add contours and candidates - biggerCandidates.push_back(candidatesIn[biggerIdx]); - biggerContours.push_back(contoursIn[biggerIdx]); - if(detectInvertedMarker) { - smallerCandidates.push_back(alignContourOrder(candidatesIn[biggerIdx][0], candidatesIn[smallerIdx])); - smallerContours.push_back(contoursIn[smallerIdx]); - } + bool operator<(const MarkerCandidateTree& m) const { + // sorting the contors in descending order + return perimeter > m.perimeter; } - // to preserve the structure :: candidateSet< defaultCandidates, whiteCandidates > - // default candidates - candidatesSetOut.push_back(biggerCandidates); - contoursSetOut.push_back(biggerContours); - // white candidates - candidatesSetOut.push_back(smallerCandidates); - contoursSetOut.push_back(smallerContours); +}; + + +// returns the average distance between the marker points +float static inline getAverageDistance(const std::vector& marker1, const std::vector& marker2) { + float minDistSq = std::numeric_limits::max(); + // fc is the first corner considered on one of the markers, 4 combinations are possible + for(int fc = 0; fc < 4; fc++) { + float distSq = 0; + for(int c = 0; c < 4; c++) { + // modC is the corner considering first corner is fc + int modC = (c + fc) % 4; + distSq += normL2Sqr(marker1[modC] - marker2[c]); + } + distSq /= 4.f; + minDistSq = min(minDistSq, distSq); + } + return sqrt(minDistSq); } /** @@ -403,29 +320,6 @@ static void _detectInitialCandidates(const Mat &grey, vector > & } -/** - * @brief Detect square candidates in the input image - */ -static void _detectCandidates(InputArray _grayImage, vector > >& candidatesSetOut, - vector > >& contoursSetOut, const DetectorParameters &_params) { - Mat grey = _grayImage.getMat(); - CV_DbgAssert(grey.total() != 0); - CV_DbgAssert(grey.type() == CV_8UC1); - - /// 1. DETECT FIRST SET OF CANDIDATES - vector > candidates; - vector > contours; - _detectInitialCandidates(grey, candidates, contours, _params); - /// 2. SORT CORNERS - _reorderCandidatesCorners(candidates); - - /// 3. FILTER OUT NEAR CANDIDATE PAIRS - // save the outter/inner border (i.e. potential candidates) - _filterTooCloseCandidates(candidates, candidatesSetOut, contours, contoursSetOut, - _params.minMarkerDistanceRate, _params.detectInvertedMarker); -} - - /** * @brief Given an input image and candidate corners, extract the bits of the candidate, including * the border bits @@ -527,12 +421,10 @@ static int _getBorderErrors(const Mat &bits, int markerSize, int borderSize) { * 1 if the candidate is a black candidate (default candidate) * 2 if the candidate is a white candidate */ -static uint8_t _identifyOneCandidate(const Dictionary& dictionary, InputArray _image, +static uint8_t _identifyOneCandidate(const Dictionary& dictionary, const Mat& _image, const vector& _corners, int& idx, const DetectorParameters& params, int& rotation, const float scale = 1.f) { - CV_DbgAssert(_corners.size() == 4); - CV_DbgAssert(_image.getMat().total() != 0); CV_DbgAssert(params.markerBorderBits > 0); uint8_t typ=1; // get bits @@ -610,87 +502,6 @@ static size_t _findOptPyrImageForCanonicalImg( return optLevel; } -/** - * @brief Identify square candidates according to a marker dictionary - */ - -static void _identifyCandidates(InputArray grey, - const vector& image_pyr, - vector > >& _candidatesSet, - vector > >& _contoursSet, const Dictionary &_dictionary, - vector >& _accepted, vector >& _contours, vector& ids, - const DetectorParameters ¶ms, - OutputArrayOfArrays _rejected = noArray()) { - CV_DbgAssert(grey.getMat().total() != 0); - CV_DbgAssert(grey.getMat().type() == CV_8UC1); - int ncandidates = (int)_candidatesSet[0].size(); - vector > accepted; - vector > rejected; - vector > contours; - - vector idsTmp(ncandidates, -1); - vector rotated(ncandidates, 0); - vector validCandidates(ncandidates, 0); - - //// Analyze each of the candidates - parallel_for_(Range(0, ncandidates), [&](const Range &range) { - const int begin = range.start; - const int end = range.end; - - vector >& candidates = params.detectInvertedMarker ? _candidatesSet[1] : _candidatesSet[0]; - vector >& contourS = params.detectInvertedMarker ? _contoursSet[1] : _contoursSet[0]; - - for(int i = begin; i < end; i++) { - int currId = -1; - // implements equation (4) - if (params.useAruco3Detection) { - const int perimeterOfContour = static_cast(contourS[i].size()); - const int min_perimeter = params.minSideLengthCanonicalImg * 4; - const size_t nearestImgId = _findOptPyrImageForCanonicalImg(image_pyr, grey.cols(), perimeterOfContour, min_perimeter); - const float scale = image_pyr[nearestImgId].cols / static_cast(grey.cols()); - - validCandidates[i] = _identifyOneCandidate(_dictionary, image_pyr[nearestImgId], candidates[i], currId, params, rotated[i], scale); - } - else { - validCandidates[i] = _identifyOneCandidate(_dictionary, grey, candidates[i], currId, params, rotated[i]); - } - - if(validCandidates[i] > 0) - idsTmp[i] = currId; - } - }); - - for(int i = 0; i < ncandidates; i++) { - if(validCandidates[i] > 0) { - // to choose the right set of candidates :: 0 for default, 1 for white markers - uint8_t set = validCandidates[i]-1; - - // shift corner positions to the correct rotation - correctCornerPosition(_candidatesSet[set][i], rotated[i]); - - if( !params.detectInvertedMarker && validCandidates[i] == 2 ) - continue; - - // add valid candidate - accepted.push_back(_candidatesSet[set][i]); - ids.push_back(idsTmp[i]); - - contours.push_back(_contoursSet[set][i]); - - } else { - rejected.push_back(_candidatesSet[0][i]); - } - } - - // parse output - _accepted = accepted; - - _contours= contours; - - if(_rejected.needed()) { - _copyVector2Output(rejected, _rejected); - } -} /** * Line fitting A * B = C :: Called from function refineCandidateLines @@ -846,16 +657,210 @@ struct ArucoDetector::ArucoDetectorImpl { ArucoDetectorImpl(const Dictionary &_dictionary, const DetectorParameters &_detectorParams, const RefineParameters& _refineParams): dictionary(_dictionary), detectorParams(_detectorParams), refineParams(_refineParams) {} + /** + * @brief Detect square candidates in the input image + */ + void detectCandidates(const Mat& grey, vector >& candidates, vector >& contours) { + /// 1. DETECT FIRST SET OF CANDIDATES + _detectInitialCandidates(grey, candidates, contours, detectorParams); + /// 2. SORT CORNERS + _reorderCandidatesCorners(candidates); + } - float getAverageArucoPinSize(vector markerCorners) { - float averageArucoModuleSize = 0.f; - int numPins = dictionary.markerSize + detectorParams.markerBorderBits * 2; - for (size_t i = 0ull; i < markerCorners.size(); i++) { - averageArucoModuleSize += sqrt(normL2Sqr(Point2f(markerCorners[i] - markerCorners[(i+1ull)%markerCorners.size()]))); + /** + * @brief FILTER OUT NEAR CANDIDATE PAIRS + * + * save the outter/inner border (i.e. potential candidates) to vector, + * clear candidates and contours + */ + vector + filterTooCloseCandidates(vector > &candidates, vector > &contours) { + CV_Assert(detectorParams.minMarkerDistanceRate >= 0.); + vector candidateTree(candidates.size()); + for(size_t i = 0ull; i < candidates.size(); i++) { + candidateTree[i] = MarkerCandidateTree(std::move(candidates[i]), std::move(contours[i])); } - averageArucoModuleSize /= ((float)markerCorners.size()*numPins); - return averageArucoModuleSize; -} + candidates.clear(); + contours.clear(); + + // sort candidates from big to small + std::sort(candidateTree.begin(), candidateTree.end()); + // group index for each candidate + vector groupId(candidateTree.size(), -1); + vector > groupedCandidates; + vector isSelectedContours(candidateTree.size(), true); + + size_t countSelectedContours = 0ull; + for (size_t i = 0ull; i < candidateTree.size(); i++) { + for (size_t j = i + 1ull; j < candidateTree.size(); j++) { + float minDist = getAverageDistance(candidateTree[i].corners, candidateTree[j].corners); + // if mean distance is too low, group markers + // the distance between the points of two independent markers should be more than half the side of the marker + // half the side of the marker = (perimeter / 4) * 0.5 = perimeter * 0.125 + if(minDist < candidateTree[j].perimeter*(float)detectorParams.minMarkerDistanceRate) { + isSelectedContours[i] = false; + isSelectedContours[j] = false; + // i and j are not related to a group + if(groupId[i] < 0 && groupId[j] < 0){ + // mark candidates with their corresponding group number + groupId[i] = groupId[j] = (int)groupedCandidates.size(); + // create group + groupedCandidates.push_back({i, j}); + } + // i is related to a group + else if(groupId[i] > -1 && groupId[j] == -1) { + int group = groupId[i]; + groupId[j] = group; + // add to group + groupedCandidates[group].push_back(j); + } + // j is related to a group + else if(groupId[j] > -1 && groupId[i] == -1) { + int group = groupId[j]; + groupId[i] = group; + // add to group + groupedCandidates[group].push_back(i); + } + } + } + countSelectedContours += isSelectedContours[i]; + } + + for (vector& grouped : groupedCandidates) { + if (detectorParams.detectInvertedMarker) // if detectInvertedMarker choose smallest contours + std::sort(grouped.begin(), grouped.end(), [](const size_t &a, const size_t &b) { + return a > b; + }); + else // if detectInvertedMarker==false choose largest contours + std::sort(grouped.begin(), grouped.end()); + size_t currId = grouped[0]; + isSelectedContours[currId] = true; + for (size_t i = 1ull; i < grouped.size(); i++) { + size_t id = grouped[i]; + float dist = getAverageDistance(candidateTree[id].corners, candidateTree[currId].corners); + float moduleSize = getAverageModuleSize(candidateTree[id].corners, dictionary.markerSize, detectorParams.markerBorderBits); + if (dist > detectorParams.minGroupDistance*moduleSize) { + currId = id; + candidateTree[grouped[0]].closeContours.push_back(candidateTree[id]); + } + } + } + + vector selectedCandidates(countSelectedContours + groupedCandidates.size()); + countSelectedContours = 0ull; + for (size_t i = 0ull; i < candidateTree.size(); i++) { + if (isSelectedContours[i]) { + selectedCandidates[countSelectedContours] = std::move(candidateTree[i]); + countSelectedContours++; + } + } + + // find hierarchy in the candidate tree + for (int i = (int)selectedCandidates.size()-1; i >= 0; i--) { + for (int j = i - 1; j >= 0; j--) { + if (checkMarker1InMarker2(selectedCandidates[i].corners, selectedCandidates[j].corners)) { + selectedCandidates[i].parent = j; + selectedCandidates[j].depth = max(selectedCandidates[j].depth, selectedCandidates[i].depth + 1); + break; + } + } + } + return selectedCandidates; + } + + /** + * @brief Identify square candidates according to a marker dictionary + */ + void identifyCandidates(const Mat& grey, const vector& image_pyr, vector& selectedContours, + vector >& accepted, vector >& contours, + vector& ids, OutputArrayOfArrays _rejected = noArray()) { + size_t ncandidates = selectedContours.size(); + vector > rejected; + + vector idsTmp(ncandidates, -1); + vector rotated(ncandidates, 0); + vector validCandidates(ncandidates, 0); + vector was(ncandidates, false); + bool checkCloseContours = true; + + int maxDepth = 0; + for (size_t i = 0ull; i < selectedContours.size(); i++) + maxDepth = max(selectedContours[i].depth, maxDepth); + vector> depths(maxDepth+1); + for (size_t i = 0ull; i < selectedContours.size(); i++) { + depths[selectedContours[i].depth].push_back(i); + } + + //// Analyze each of the candidates + int depth = 0; + size_t counter = 0; + while (counter < ncandidates) { + parallel_for_(Range(0, (int)depths[depth].size()), [&](const Range& range) { + const int begin = range.start; + const int end = range.end; + for (int i = begin; i < end; i++) { + size_t v = depths[depth][i]; + was[v] = true; + Mat img = grey; + // implements equation (4) + if (detectorParams.useAruco3Detection) { + const int minPerimeter = detectorParams.minSideLengthCanonicalImg * 4; + const size_t nearestImgId = _findOptPyrImageForCanonicalImg(image_pyr, grey.cols, static_cast(selectedContours[v].contour.size()), minPerimeter); + img = image_pyr[nearestImgId]; + } + const float scale = detectorParams.useAruco3Detection ? img.cols / static_cast(grey.cols) : 1.f; + + validCandidates[v] = _identifyOneCandidate(dictionary, img, selectedContours[v].corners, idsTmp[v], detectorParams, rotated[v], scale); + + if (validCandidates[v] == 0 && checkCloseContours) { + for (const MarkerCandidate& closeMarkerCandidate: selectedContours[v].closeContours) { + validCandidates[v] = _identifyOneCandidate(dictionary, img, closeMarkerCandidate.corners, idsTmp[v], detectorParams, rotated[v], scale); + if (validCandidates[v] > 0) { + selectedContours[v].corners = closeMarkerCandidate.corners; + selectedContours[v].contour = closeMarkerCandidate.contour; + break; + } + } + } + } + }); + + // visit the parent vertices of the detected markers to skip identify parent contours + for(size_t v : depths[depth]) { + if(validCandidates[v] > 0) { + int parent = selectedContours[v].parent; + while (parent != -1) { + if (!was[parent]) { + was[parent] = true; + counter++; + } + parent = selectedContours[parent].parent; + } + } + counter++; + } + depth++; + } + + 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]); + + accepted.push_back(selectedContours[i].corners); + contours.push_back(selectedContours[i].contour); + ids.push_back(idsTmp[i]); + } + else { + rejected.push_back(selectedContours[i].corners); + } + } + + // parse output + if(_rejected.needed()) { + _copyVector2Output(rejected, _rejected); + } + } }; @@ -929,23 +934,21 @@ void ArucoDetector::detectMarkers(InputArray _image, OutputArrayOfArrays _corner vector > contours; vector ids; - vector > > candidatesSet; - vector > > contoursSet; - /// STEP 2.a Detect marker candidates :: using AprilTag if(detectorParams.cornerRefinementMethod == (int)CORNER_REFINE_APRILTAG){ _apriltag(grey, detectorParams, candidates, contours); - - candidatesSet.push_back(candidates); - contoursSet.push_back(contours); } /// STEP 2.b Detect marker candidates :: traditional way - else - _detectCandidates(grey, candidatesSet, contoursSet, detectorParams); + else { + arucoDetectorImpl->detectCandidates(grey, candidates, contours); + } + + /// STEP 2.c FILTER OUT NEAR CANDIDATE PAIRS + auto selectedCandidates = arucoDetectorImpl->filterTooCloseCandidates(candidates, contours); /// STEP 2: Check candidate codification (identify markers) - _identifyCandidates(grey, grey_pyramid, candidatesSet, contoursSet, dictionary, - candidates, contours, ids, detectorParams, _rejectedImgPoints); + arucoDetectorImpl->identifyCandidates(grey, grey_pyramid, selectedCandidates, candidates, contours, + ids, _rejectedImgPoints); /// STEP 3: Corner refinement :: use corner subpix if (detectorParams.cornerRefinementMethod == (int)CORNER_REFINE_SUBPIX) { @@ -963,7 +966,7 @@ void ArucoDetector::detectMarkers(InputArray _image, OutputArrayOfArrays _corner } else { int cornerRefinementWinSize = std::max(1, cvRound(detectorParams.relativeCornerRefinmentWinSize* - arucoDetectorImpl->getAverageArucoPinSize(candidates[i]))); + getAverageModuleSize(candidates[i], dictionary.markerSize, detectorParams.markerBorderBits))); cornerRefinementWinSize = min(cornerRefinementWinSize, detectorParams.cornerRefinementWinSize); cornerSubPix(grey, Mat(candidates[i]), Size(cornerRefinementWinSize, cornerRefinementWinSize), Size(-1, -1), TermCriteria(TermCriteria::MAX_ITER | TermCriteria::EPS, @@ -1238,7 +1241,7 @@ void ArucoDetector::refineDetectedMarkers(InputArray _image, const Board& _board std::vector marker(closestRotatedMarker.begin(), closestRotatedMarker.end()); int cornerRefinementWinSize = std::max(1, cvRound(detectorParams.relativeCornerRefinmentWinSize* - arucoDetectorImpl->getAverageArucoPinSize(marker))); + getAverageModuleSize(marker, dictionary.markerSize, detectorParams.markerBorderBits))); cornerRefinementWinSize = min(cornerRefinementWinSize, detectorParams.cornerRefinementWinSize); cornerSubPix(grey, closestRotatedMarker, Size(cornerRefinementWinSize, cornerRefinementWinSize), diff --git a/modules/objdetect/src/aruco/charuco_detector.cpp b/modules/objdetect/src/aruco/charuco_detector.cpp index d5b4087a2c..041cf3b8f8 100644 --- a/modules/objdetect/src/aruco/charuco_detector.cpp +++ b/modules/objdetect/src/aruco/charuco_detector.cpp @@ -314,7 +314,9 @@ struct CharucoDetector::CharucoDetectorImpl { vector > rejectedMarkers; arucoDetector.detectMarkers(image, _markerCorners, _markerIds, rejectedMarkers); if (charucoParameters.tryRefineMarkers) - arucoDetector.refineDetectedMarkers(image, board, _markerCorners, _markerIds, rejectedMarkers); + arucoDetector.refineDetectedMarkers(image, board, _markerCorners, _markerIds, rejectedMarkers); + if (_markerCorners.empty() && _markerIds.empty()) + return; } // if camera parameters are avaible, use approximated calibration if(!charucoParameters.cameraMatrix.empty()) diff --git a/modules/objdetect/src/face_detect.cpp b/modules/objdetect/src/face_detect.cpp index 8da13eda45..bff7e0b13e 100644 --- a/modules/objdetect/src/face_detect.cpp +++ b/modules/objdetect/src/face_detect.cpp @@ -48,6 +48,35 @@ public: topK = top_k; } + FaceDetectorYNImpl(const String& framework, + const std::vector& bufferModel, + const std::vector& bufferConfig, + const Size& input_size, + float score_threshold, + float nms_threshold, + int top_k, + int backend_id, + int target_id) + :divisor(32), + strides({8, 16, 32}) + { + net = dnn::readNet(framework, bufferModel, bufferConfig); + CV_Assert(!net.empty()); + + net.setPreferableBackend(backend_id); + net.setPreferableTarget(target_id); + + inputW = input_size.width; + inputH = input_size.height; + + padW = (int((inputW - 1) / divisor) + 1) * divisor; + padH = (int((inputH - 1) / divisor) + 1) * divisor; + + scoreThreshold = score_threshold; + nmsThreshold = nms_threshold; + topK = top_k; + } + void setInputSize(const Size& input_size) override { inputW = input_size.width; @@ -264,4 +293,22 @@ Ptr FaceDetectorYN::create(const String& model, #endif } +Ptr FaceDetectorYN::create(const String& framework, + const std::vector& bufferModel, + const std::vector& bufferConfig, + const Size& input_size, + const float score_threshold, + const float nms_threshold, + const int top_k, + const int backend_id, + const int target_id) +{ +#ifdef HAVE_OPENCV_DNN + return makePtr(framework, bufferModel, bufferConfig, input_size, score_threshold, nms_threshold, top_k, backend_id, target_id); +#else + CV_UNUSED(bufferModel); CV_UNUSED(bufferConfig); CV_UNUSED(input_size); CV_UNUSED(score_threshold); CV_UNUSED(nms_threshold); CV_UNUSED(top_k); CV_UNUSED(backend_id); CV_UNUSED(target_id); + CV_Error(cv::Error::StsNotImplemented, "cv::FaceDetectorYN requires enabled 'dnn' module."); +#endif +} + } // namespace cv diff --git a/modules/objdetect/src/graphical_code_detector_impl.hpp b/modules/objdetect/src/graphical_code_detector_impl.hpp index 76429222ff..bb7ff5c177 100644 --- a/modules/objdetect/src/graphical_code_detector_impl.hpp +++ b/modules/objdetect/src/graphical_code_detector_impl.hpp @@ -20,6 +20,18 @@ struct GraphicalCodeDetector::Impl { OutputArray points, OutputArrayOfArrays straight_code) const = 0; }; +class QRCodeDecoder { +public: + virtual ~QRCodeDecoder(); + + static Ptr create(); + + virtual bool decode(const Mat& straight, String& decoded_info) = 0; + + QRCodeEncoder::EncodeMode mode; + QRCodeEncoder::ECIEncodings eci; +}; + } -#endif \ No newline at end of file +#endif diff --git a/modules/objdetect/src/qrcode.cpp b/modules/objdetect/src/qrcode.cpp index 17e97f7cac..37f276085f 100644 --- a/modules/objdetect/src/qrcode.cpp +++ b/modules/objdetect/src/qrcode.cpp @@ -2728,7 +2728,6 @@ bool QRDecode::samplingForVersion() return true; } - static bool checkASCIIcompatible(const uint8_t* str, const size_t size) { for (size_t i = 0; i < size; ++i) { uint8_t byte = str[i]; @@ -2782,6 +2781,10 @@ static std::string encodeUTF8_bytesarray(const uint8_t* str, const size_t size) bool QRDecode::decodingProcess() { + QRCodeEncoder::EncodeMode mode; + QRCodeEncoder::ECIEncodings eci; + const uint8_t* payload; + size_t payload_len; #ifdef HAVE_QUIRC if (straight.empty()) { return false; } @@ -2811,65 +2814,79 @@ bool QRDecode::decodingProcess() CV_LOG_INFO(NULL, "QR: decoded with .version=" << qr_code_data.version << " .data_type=" << qr_code_data.data_type << " .eci=" << qr_code_data.eci << " .payload_len=" << qr_code_data.payload_len) - switch (qr_code_data.data_type) + mode = static_cast(qr_code_data.data_type); + eci = static_cast(qr_code_data.eci); + payload = qr_code_data.payload; + payload_len = qr_code_data.payload_len; +#else + auto decoder = QRCodeDecoder::create(); + if (!decoder->decode(straight, result_info)) + return false; + mode = decoder->mode; + eci = decoder->eci; + payload = reinterpret_cast(result_info.c_str()); + payload_len = result_info.size(); +#endif + + // Check output string format + switch (mode) { - case QUIRC_DATA_TYPE_NUMERIC: - if (!checkASCIIcompatible(qr_code_data.payload, qr_code_data.payload_len)) { + case QRCodeEncoder::EncodeMode::MODE_NUMERIC: + if (!checkASCIIcompatible(payload, payload_len)) { CV_LOG_INFO(NULL, "QR: DATA_TYPE_NUMERIC payload must be ACSII compatible string"); return false; } - result_info.assign((const char*)qr_code_data.payload, qr_code_data.payload_len); + result_info.assign((const char*)payload, payload_len); return true; - case QUIRC_DATA_TYPE_ALPHA: - if (!checkASCIIcompatible(qr_code_data.payload, qr_code_data.payload_len)) { + case QRCodeEncoder::EncodeMode::MODE_ALPHANUMERIC: + if (!checkASCIIcompatible(payload, payload_len)) { CV_LOG_INFO(NULL, "QR: DATA_TYPE_ALPHA payload must be ASCII compatible string"); return false; } - result_info.assign((const char*)qr_code_data.payload, qr_code_data.payload_len); + result_info.assign((const char*)payload, payload_len); return true; - case QUIRC_DATA_TYPE_BYTE: + case QRCodeEncoder::EncodeMode::MODE_BYTE: // https://en.wikipedia.org/wiki/Extended_Channel_Interpretation - if (qr_code_data.eci == QUIRC_ECI_UTF_8) { + if (eci == QRCodeEncoder::ECIEncodings::ECI_UTF8) { CV_LOG_INFO(NULL, "QR: payload ECI is UTF-8"); - if (!checkUTF8(qr_code_data.payload, qr_code_data.payload_len)) { + if (!checkUTF8(payload, payload_len)) { CV_LOG_INFO(NULL, "QUIRC_DATA_TYPE_BYTE with UTF-8 ECI must be UTF-8 compatible string"); return false; } - result_info.assign((const char*)qr_code_data.payload, qr_code_data.payload_len); - } else if (qr_code_data.eci == 25/*ECI_UTF_16BE*/) { + result_info.assign((const char*)payload, payload_len); + } else if (eci == 25/*ECI_UTF_16BE*/) { CV_LOG_INFO(NULL, "QR: UTF-16BE ECI is not supported"); return false; - } else if (checkASCIIcompatible(qr_code_data.payload, qr_code_data.payload_len)) { + } else if (checkASCIIcompatible(payload, payload_len)) { CV_LOG_INFO(NULL, "QR: payload is ASCII compatible (special handling for symbols encoding is not needed)"); - result_info.assign((const char*)qr_code_data.payload, qr_code_data.payload_len); + result_info.assign((const char*)payload, payload_len); } else { - if (checkUTF8(qr_code_data.payload, qr_code_data.payload_len)) { + if (checkUTF8(payload, payload_len)) { CV_LOG_INFO(NULL, "QR: payload QUIRC_DATA_TYPE_BYTE is UTF-8 compatible, return as-is"); - result_info.assign((const char*)qr_code_data.payload, qr_code_data.payload_len); + result_info.assign((const char*)payload, payload_len); } else { CV_LOG_INFO(NULL, "QR: assume 1-byte per symbol encoding"); - result_info = encodeUTF8_bytesarray(qr_code_data.payload, qr_code_data.payload_len); + result_info = encodeUTF8_bytesarray(payload, payload_len); } } return true; - case QUIRC_DATA_TYPE_KANJI: + case QRCodeEncoder::EncodeMode::MODE_KANJI: // FIXIT BUG: we must return UTF-8 compatible string CV_LOG_WARNING(NULL, "QR: Kanji is not supported properly"); - result_info.assign((const char*)qr_code_data.payload, qr_code_data.payload_len); + result_info.assign((const char*)payload, payload_len); return true; + case QRCodeEncoder::EncodeMode::MODE_ECI: + CV_LOG_WARNING(NULL, "QR: ECI is not supported properly"); + result_info.assign((const char*)payload, payload_len); + return true; + default: + CV_LOG_WARNING(NULL, "QR: unsupported QR data type"); + return false; } - - CV_LOG_WARNING(NULL, "QR: unsupported QR data type"); - return false; -#else - return false; -#endif - } bool QRDecode::straightDecodingProcess() { -#ifdef HAVE_QUIRC if (!updatePerspective(getHomography())) { return false; } if (!versionDefinition()) { return false; } if (useAlignmentMarkers) @@ -2877,24 +2894,15 @@ bool QRDecode::straightDecodingProcess() if (!samplingForVersion()) { return false; } if (!decodingProcess()) { return false; } return true; -#else - std::cout << "Library QUIRC is not linked. No decoding is performed. Take it to the OpenCV repository." << std::endl; - return false; -#endif } bool QRDecode::curvedDecodingProcess() { -#ifdef HAVE_QUIRC if (!preparingCurvedQRCodes()) { return false; } if (!versionDefinition()) { return false; } if (!samplingForVersion()) { return false; } if (!decodingProcess()) { return false; } return true; -#else - std::cout << "Library QUIRC is not linked. No decoding is performed. Take it to the OpenCV repository." << std::endl; - return false; -#endif } QRDecode::QRDecode(bool _useAlignmentMarkers): @@ -4477,25 +4485,14 @@ static vector analyzeFinderPatterns(const vector > &corners, const Mat& img, const QRCodeDetectorAruco::Params& qrDetectorParameters) { vector qrCodes; - vector patterns; + vector patterns(corners.size()); if (img.empty()) return qrCodes; float maxModuleSize = 0.f; for (size_t i = 0ull; i < corners.size(); i++) { FinderPatternInfo pattern = FinderPatternInfo(corners[i]); - // TODO: improve thinning Aruco markers - bool isUniq = true; - for (const FinderPatternInfo& tmp : patterns) { - Point2f dist = pattern.center - tmp.center; - if (max(abs(dist.x), abs(dist.y)) < 3.f * tmp.moduleSize) { - isUniq = false; - break; - } - } - if (isUniq) { - patterns.push_back(pattern); - maxModuleSize = max(maxModuleSize, patterns.back().moduleSize); - } + patterns[i] = pattern; + maxModuleSize = max(maxModuleSize, pattern.moduleSize); } const int threshold = cvRound(qrDetectorParameters.minModuleSizeInPyramid * 12.5f) + (cvRound(qrDetectorParameters.minModuleSizeInPyramid * 12.5f) % 2 ? 0 : 1); diff --git a/modules/objdetect/src/qrcode_encoder.cpp b/modules/objdetect/src/qrcode_encoder.cpp index 4ab1e1ac40..fca74421a0 100644 --- a/modules/objdetect/src/qrcode_encoder.cpp +++ b/modules/objdetect/src/qrcode_encoder.cpp @@ -6,6 +6,8 @@ #include "precomp.hpp" #include "qrcode_encoder_table.inl.hpp" +#include "graphical_code_detector_impl.hpp" + namespace cv { using std::vector; @@ -19,6 +21,7 @@ const uint8_t INVALID_REGION_VALUE = 110; static void decToBin(const int dec_number, const int total_bits, std::vector &bin_number); static uint8_t gfPow(uint8_t x, int power); static uint8_t gfMul(const uint8_t x, const uint8_t y); +static uint8_t gfDiv(const uint8_t x, const uint8_t y); static void gfPolyMul(const vector &p, const vector &q, vector &product); static void gfPolyDiv(const vector ÷nd, const vector &divisor, const int ecc_num, vector "ient); static void polyGenerator(const int n, vector &result); @@ -51,6 +54,13 @@ static uint8_t gfMul(const uint8_t x, const uint8_t y) return gf_exp[(gf_log[x] + gf_log[y]) % 255]; } +static uint8_t gfDiv(const uint8_t x, const uint8_t y) +{ + if (x == 0 || y == 0) + return 0; + return gf_exp[(gf_log[x] + 255 - gf_log[y]) % 255]; +} + static void gfPolyMul(const vector &p, const vector &q, vector &product) { int len_p = (int)p.size(); @@ -141,6 +151,8 @@ static int mapSymbol(char c) return -1; } +static void maskData(const Mat& original, const int mask_type_num, Mat &masked); + QRCodeEncoder::QRCodeEncoder() { // nothing @@ -196,17 +208,18 @@ protected: uint8_t total_num; vector final_qrcodes; - Ptr version_info; - Ptr cur_ecc_params; + const VersionInfo* version_info; + const BlockParams* cur_ecc_params; - bool isNumeric(const std::string& input); - bool isAlphaNumeric(const std::string& input); + bool isNumeric(const std::string& input) const; + bool isAlphaNumeric(const std::string& input) const; + EncodeMode autoEncodeMode(const std::string &input) const ; bool encodeByte(const std::string& input, vector &output); bool encodeAlpha(const std::string& input, vector &output); bool encodeNumeric(const std::string& input, vector &output); bool encodeECI(const std::string& input, vector &output); bool encodeKanji(const std::string& input, vector &output); - bool encodeAuto(const std::string& input, vector &output); + bool encodeAuto(const std::string& input, vector &output, EncodeMode *mode = nullptr); bool encodeStructure(const std::string& input, vector &output); int eccLevelToCode(CorrectionLevel level); void padBitStream(); @@ -220,11 +233,10 @@ protected: void formatGenerate(const int mask_type_num, vector &format_array); void versionInfoGenerate(const int version_level_num, vector &version_array); void fillReserved(const vector &format_array, Mat &masked); - void maskData(const int mask_type_num, Mat &masked); void findAutoMaskType(); - bool estimateVersion(const int input_length, vector &possible_version); + bool estimateVersion(const int input_length, EncodeMode mode, vector &possible_version); int versionAuto(const std::string &input_str); - int findVersionCapacity(const int input_length, const int ecc, const int version_begin, const int version_end); + int findVersionCapacity(const int input_length, const int ecc, const std::vector& possible_versions); void generatingProcess(const std::string& input, Mat &qrcode); void generateQR(const std::string& input); }; @@ -247,17 +259,17 @@ int QRCodeEncoderImpl::eccLevelToCode(CorrectionLevel level) "CORRECT_LEVEL_L, CORRECT_LEVEL_M, CORRECT_LEVEL_Q, CORRECT_LEVEL_H." ); } -int QRCodeEncoderImpl::findVersionCapacity(const int input_length, const int ecc, const int version_begin, const int version_end) +int QRCodeEncoderImpl::findVersionCapacity(const int input_length, const int ecc, const std::vector& possible_versions) { int data_codewords, version_index = -1; const int byte_len = 8; version_index = -1; - for (int i = version_begin; i < version_end; i++) + for (int i : possible_versions) { - Ptr tmp_ecc_params = makePtr(version_info_database[i].ecc[ecc]); - data_codewords = tmp_ecc_params->data_codewords_in_G1 * tmp_ecc_params->num_blocks_in_G1 + - tmp_ecc_params->data_codewords_in_G2 * tmp_ecc_params->num_blocks_in_G2; + auto& tmp_ecc_params = version_info_database[i].ecc[ecc]; + data_codewords = tmp_ecc_params.data_codewords_in_G1 * tmp_ecc_params.num_blocks_in_G1 + + tmp_ecc_params.data_codewords_in_G2 * tmp_ecc_params.num_blocks_in_G2; if (data_codewords * byte_len >= input_length) { @@ -268,53 +280,70 @@ int QRCodeEncoderImpl::findVersionCapacity(const int input_length, const int ecc return version_index; } -bool QRCodeEncoderImpl::estimateVersion(const int input_length, vector& possible_version) +static inline int getCapacity(int version, QRCodeEncoder::CorrectionLevel ecc_level, QRCodeEncoder::EncodeMode mode) { + const int* capacity = version_capacity_database[version].ec_level[ecc_level].encoding_modes; + switch (mode) { + case QRCodeEncoder::EncodeMode::MODE_NUMERIC: + return capacity[0]; + case QRCodeEncoder::EncodeMode::MODE_ALPHANUMERIC: + return capacity[1]; + case QRCodeEncoder::EncodeMode::MODE_BYTE: + return capacity[2]; + case QRCodeEncoder::EncodeMode::MODE_KANJI: + return capacity[3]; + default: + CV_Error(Error::StsNotImplemented, format("Unexpected mode %d", mode)); + } +} + +bool QRCodeEncoderImpl::estimateVersion(const int input_length, EncodeMode mode, vector& possible_version) { possible_version.clear(); - if (input_length > version_capacity_database[40].ec_level[ecc_level].encoding_modes[1]) + + CV_Assert(mode != EncodeMode::MODE_AUTO); + + if (input_length > getCapacity(MAX_VERSION, ecc_level, mode)) + { return false; - if (input_length <= version_capacity_database[9].ec_level[ecc_level].encoding_modes[3]) - { - possible_version.push_back(1); } - else if (input_length <= version_capacity_database[9].ec_level[ecc_level].encoding_modes[1]) + + int version = MAX_VERSION; + + for (; version > 0; --version) { - possible_version.push_back(1); - possible_version.push_back(2); + if (input_length > getCapacity(version, ecc_level, mode)) { + break; + } } - else if (input_length <= version_capacity_database[26].ec_level[ecc_level].encoding_modes[3]) + + if (version < MAX_VERSION) { - possible_version.push_back(2); + version += 1; } - else if (input_length <= version_capacity_database[26].ec_level[ecc_level].encoding_modes[1]) + + possible_version.push_back(version); + + if (version < MAX_VERSION) { - possible_version.push_back(2); - possible_version.push_back(3); - } - else - { - possible_version.push_back(3); + possible_version.push_back(version + 1); } + return true; } int QRCodeEncoderImpl::versionAuto(const std::string& input_str) { - vector possible_version; - estimateVersion((int)input_str.length(), possible_version); - int tmp_version = 0; vector payload_tmp; - int version_range[5] = {0, 1, 10, 27, 41}; - for(size_t i = 0; i < possible_version.size(); i++) - { - int version_range_index = possible_version[i]; + EncodeMode mode; + encodeAuto(input_str, payload_tmp, &mode); - encodeAuto(input_str, payload_tmp); - tmp_version = findVersionCapacity((int)payload_tmp.size(), ecc_level, - version_range[version_range_index], version_range[version_range_index + 1]); - if(tmp_version != -1) - break; + vector possible_version; + if (!estimateVersion((int)input_str.length(), mode, possible_version)) { + return -1; } + + const auto tmp_version = findVersionCapacity((int)payload_tmp.size(), ecc_level, possible_version); + return tmp_version; } @@ -342,20 +371,23 @@ void QRCodeEncoderImpl::generateQR(const std::string &input) std::string input_info = input.substr(segment_begin, segment_end); string_itr += segment_end; + int detected_version = versionAuto(input_info); - CV_Assert(detected_version != -1); - if (version_level == 0) - version_level = detected_version; - else if (version_level < detected_version) + int tmp_version_level = version_level; + if (detected_version == -1) + CV_Error(Error::StsBadArg, "The given input exceeds the maximum capacity of a QR code with the selected encoding mode and error correction level " ); + else if (tmp_version_level == 0) + tmp_version_level = detected_version; + else if (tmp_version_level < detected_version) CV_Error(Error::StsBadArg, "The given version is not suitable for the given input string length "); payload.clear(); payload.reserve(MAX_PAYLOAD_LEN); format = vector (15, 255); version_reserved = vector (18, 255); - version_size = (21 + (version_level - 1) * 4); - version_info = makePtr(version_info_database[version_level]); - cur_ecc_params = makePtr(version_info->ecc[ecc_level]); + version_size = (21 + (tmp_version_level - 1) * 4); + version_info = &version_info_database[tmp_version_level]; + cur_ecc_params = &version_info->ecc[ecc_level]; original = Mat(Size(version_size, version_size), CV_8UC1, Scalar(255)); masked_data = original.clone(); Mat qrcode = masked_data.clone(); @@ -366,36 +398,10 @@ void QRCodeEncoderImpl::generateQR(const std::string &input) void QRCodeEncoderImpl::formatGenerate(const int mask_type_num, vector &format_array) { - const int mask_bits_num = 3; - const int level_bits_num = 2; - - std::vector mask_type_bin(mask_bits_num); - std::vector ec_level_bin(level_bits_num); - decToBin(mask_type_num, mask_bits_num, mask_type_bin); - decToBin(eccLevelToCode(ecc_level), level_bits_num, ec_level_bin); - - std::vector format_bits; - hconcat(ec_level_bin, mask_type_bin, format_bits); - std::reverse(format_bits.begin(), format_bits.end()); - - const int ecc_info_bits = 10; - - std::vector shift(ecc_info_bits, 0); - std::vector polynomial; - hconcat(shift, format_bits, polynomial); - - const int generator_len = 11; - const uint8_t generator_arr[generator_len] = {1, 1, 1, 0, 1, 1, 0, 0, 1, 0, 1}; - std::vector format_generator (generator_arr, generator_arr + sizeof(generator_arr) / sizeof(generator_arr[0])); - vector ecc_code; - gfPolyDiv(polynomial, format_generator, ecc_info_bits, ecc_code); - hconcat(ecc_code, format_bits, format_array); - - const uint8_t mask_arr[MAX_FORMAT_LENGTH] = {0, 1, 0, 0, 1, 0, 0, 0, 0, 0, 1, 0, 1, 0, 1}; - std::vector system_mask (mask_arr, mask_arr + sizeof(mask_arr) / sizeof(mask_arr[0])); - for(int i = 0; i < MAX_FORMAT_LENGTH; i++) - { - format_array[i] ^= system_mask[i]; + int idx = (eccLevelToCode(ecc_level) << 3) | mask_type_num; + format_array.resize(MAX_FORMAT_LENGTH); + for (int i = 0; i < MAX_FORMAT_LENGTH; ++i) { + format_array[i] = (formatInfoLUT[idx] >> i) & 1; } } @@ -613,7 +619,7 @@ bool QRCodeEncoderImpl::encodeStructure(const std::string& input, vector& output) +QRCodeEncoder::EncodeMode QRCodeEncoderImpl::autoEncodeMode(const std::string &input) const { if (isNumeric(input)) - encodeNumeric(input, output); - else if (isAlphaNumeric(input)) - encodeAlpha(input, output); - else - encodeByte(input, output); + { + return EncodeMode::MODE_NUMERIC; + } + + if (isAlphaNumeric(input)) + { + return EncodeMode::MODE_ALPHANUMERIC; + } + + return EncodeMode::MODE_BYTE; +} + +bool QRCodeEncoderImpl::encodeAuto(const std::string& input, vector& output, EncodeMode *mode) +{ + const auto selected_mode = autoEncodeMode(input); + + CV_Assert(selected_mode != EncodeMode::MODE_AUTO); + + switch (selected_mode) + { + case EncodeMode::MODE_NUMERIC: + encodeNumeric(input, output); + break; + case EncodeMode::MODE_ALPHANUMERIC: + encodeAlpha(input, output); + break; + case EncodeMode::MODE_STRUCTURED_APPEND: + encodeByte(input, output); + break; + case EncodeMode::MODE_BYTE: + encodeByte(input, output); + break; + case EncodeMode::MODE_KANJI: + encodeKanji(input, output); + break; + case EncodeMode::MODE_ECI: + encodeECI(input, output); + break; + default: + break; + } + + if (mode != nullptr) + { + *mode = selected_mode; + } + return true; } @@ -787,7 +835,7 @@ void QRCodeEncoderImpl::findAutoMaskType() { Mat test_result = masked_data.clone(); vector test_format = format; - maskData(cur_type, test_result); + maskData(original, cur_type, test_result); formatGenerate(cur_type, test_format); fillReserved(test_format, test_result); int continued_num = 0; @@ -899,8 +947,9 @@ void QRCodeEncoderImpl::findAutoMaskType() mask_type = best_index; } -void QRCodeEncoderImpl::maskData(const int mask_type_num, Mat& masked) +void maskData(const Mat& original, const int mask_type_num, Mat& masked) { + int version_size = original.rows; for (int i = 0; i < version_size; i++) { for (int j = 0; j < version_size; j++) @@ -1204,7 +1253,7 @@ void QRCodeEncoderImpl::structureFinalMessage() writeReservedArea(); writeData(); findAutoMaskType(); - maskData(mask_type, masked_data); + maskData(original, mask_type, masked_data); formatGenerate(mask_type, format); versionInfoGenerate(version_level, version_reserved); fillReserved(format, masked_data); @@ -1260,4 +1309,521 @@ Ptr QRCodeEncoder::create(const QRCodeEncoder::Params& parameters return makePtr(parameters); } +class QRCodeDecoderImpl : public QRCodeDecoder { +public: + bool decode(const Mat& straight, String& decoded_info) CV_OVERRIDE; + +private: + QRCodeEncoder::CorrectionLevel level; + int version; + + struct Bitstream { + int next(int bits) { + CV_Assert(idx < data.size()); + + int val = 0; + while (bits >= actualBits) { + val |= data[idx++] << (bits - actualBits); + bits -= actualBits; + actualBits = 8; + } + if (bits) { + val |= data[idx] >> (actualBits - bits); + actualBits -= bits; + data[idx] &= 255 >> (8 - actualBits); + } + return val; + } + + bool empty() { + return idx >= data.size(); + } + + std::vector data; + int actualBits = 8; + size_t idx = 0; + } bitstream; + + bool run(const Mat& straight, String& decoded_info); + bool decodeFormatInfo(const Mat& straight, int& mask); + bool correctFormatInfo(uint16_t& format_info); + void extractCodewords(Mat& source, std::vector& codewords); + bool errorCorrection(std::vector& codewords); + bool errorCorrectionBlock(std::vector& codewords); + void decodeSymbols(String& result); + void decodeNumeric(String& result); + void decodeAlpha(String& result); + void decodeByte(String& result); + void decodeECI(String& result); + void decodeKanji(String& result); +}; + +QRCodeDecoder::~QRCodeDecoder() +{ + // nothing +} + +Ptr QRCodeDecoder::create() { + return makePtr(); +} + +bool QRCodeDecoderImpl::decode(const Mat& _straight, String& decoded_info) { + Mat straight = ~_straight; // Invert modules + bool decoded = run(straight, decoded_info); + if (!decoded) { + cv::transpose(straight, straight); + decoded = run(straight, decoded_info); + } + return decoded; +} + +// Unmask format info bits and apply error correction +bool QRCodeDecoderImpl::correctFormatInfo(uint16_t& format_info) { + static const uint16_t mask_pattern = 0b101010000010010; + + cv::Hamming hd; + for (int i = 0; i < 32; ++i) { + // Compute Hamming distance + int distance = hd(reinterpret_cast(&formatInfoLUT[i]), + reinterpret_cast(&format_info), 2); + // Up to 3 bit errors might be corrected. + // So if distance is less or equal than 3 - we found a correct format info. + if (distance <= 3) { + format_info = formatInfoLUT[i] ^ mask_pattern; + return true; + } + } + return false; +} + +bool QRCodeDecoderImpl::decodeFormatInfo(const Mat& straight, int& mask) { + // Read left-top format info + uint16_t format_info = 0; + for (int i = 0; i < 6; ++i) + format_info |= (straight.at(i, 8) & 1) << i; + + format_info |= (straight.at(7, 8) & 1) << 6; + format_info |= (straight.at(8, 8) & 1) << 7; + format_info |= (straight.at(8, 7) & 1) << 8; + + for (int i = 9; i < 15; ++i) + format_info |= (straight.at(8, 14 - i) & 1) << i; + + bool correct = correctFormatInfo(format_info); + + // Format information 15bit sequence appears twice. + // Try extract format info from different position. + uint16_t format_info_dup = 0; + for (int i = 0; i < 8; ++i) + format_info_dup |= (straight.at(8, straight.cols - 1 - i) & 1) << i; + for (int i = 0; i < 7; ++i) + format_info_dup |= (straight.at(straight.rows - 7 + i, 8) & 1) << (i + 8); + + if (correctFormatInfo(format_info_dup)) { + // Both strings must be the same + if (correct && format_info != format_info_dup) + return false; + format_info = format_info_dup; + } else { + if (!correct) + return false; + } + + switch((format_info >> 13) & 0b11) { + case 0: level = QRCodeEncoder::CorrectionLevel::CORRECT_LEVEL_M; break; + case 1: level = QRCodeEncoder::CorrectionLevel::CORRECT_LEVEL_L; break; + case 2: level = QRCodeEncoder::CorrectionLevel::CORRECT_LEVEL_H; break; + case 3: level = QRCodeEncoder::CorrectionLevel::CORRECT_LEVEL_Q; break; + }; + mask = (format_info >> 10) & 0b111; + return true; +} + +bool QRCodeDecoderImpl::run(const Mat& straight, String& decoded_info) { + CV_Assert(straight.rows == straight.cols); + version = (straight.rows - 21) / 4 + 1; + + decoded_info = ""; + mode = static_cast(0); + eci = static_cast(0); + + // Decode format info + int maskPattern; + bool decoded = decodeFormatInfo(straight, maskPattern); + if (!decoded) { + return false; + } + + // Generate data mask + Mat masked = straight.clone(); + maskData(straight, maskPattern, masked); + + extractCodewords(masked, bitstream.data); + if (!errorCorrection(bitstream.data)) { + return false; + } + decodeSymbols(decoded_info); + return true; +} + +bool QRCodeDecoderImpl::errorCorrection(std::vector& codewords) { + CV_CheckEQ((int)codewords.size(), version_info_database[version].total_codewords, + "Number of codewords"); + + int numBlocks = version_info_database[version].ecc[level].num_blocks_in_G1 + + version_info_database[version].ecc[level].num_blocks_in_G2; + if (numBlocks == 1) { + return errorCorrectionBlock(codewords); + } + + size_t numData = 0; + std::vector blockSizes; + blockSizes.reserve(numBlocks); + for (int i = 0; i < version_info_database[version].ecc[level].num_blocks_in_G1; ++i) { + blockSizes.push_back(version_info_database[version].ecc[level].data_codewords_in_G1); + numData += blockSizes.back(); + } + for (int i = 0; i < version_info_database[version].ecc[level].num_blocks_in_G2; ++i) { + blockSizes.push_back(version_info_database[version].ecc[level].data_codewords_in_G2); + numData += blockSizes.back(); + } + + // TODO: parallel_for + std::vector> blocks(numBlocks); + int minBlockSize = *std::min_element(blockSizes.begin(), blockSizes.end()); + size_t offset = 0; + for (int i = 0; i < minBlockSize; ++i) { + for (int j = 0; j < numBlocks; ++j) { + blocks[j].push_back(codewords[offset++]); + } + } + // Put remaining data codewords + for (int j = 0; j < numBlocks; ++j) { + CV_Assert(blockSizes[j] == minBlockSize || blockSizes[j] == minBlockSize + 1); + if (blockSizes[j] > minBlockSize) + blocks[j].push_back(codewords[offset++]); + } + // Copy error correction codewords + int numEcc = version_info_database[version].ecc[level].ecc_codewords; + for (int i = 0; i < numEcc; ++i) { + for (int j = 0; j < numBlocks; ++j) { + blocks[j].push_back(codewords[offset++]); + } + } + + parallel_for_(Range(0, numBlocks), [&](const Range& r) { + for (int i = r.start; i < r.end; ++i) { + if (!errorCorrectionBlock(blocks[i])) { + blocks[i].clear(); + return; + } + } + }); + + // Collect blocks back after error correction. Trim error correction codewords. + codewords.resize(numData); + offset = 0; + for (size_t i = 0; i < blocks.size(); ++i) { + if (blocks[i].empty()) + return false; + std::copy(blocks[i].begin(), blocks[i].end(), codewords.begin() + offset); + offset += blocks[i].size(); + } + + return true; +} + +bool QRCodeDecoderImpl::errorCorrectionBlock(std::vector& codewords) { + size_t numEcc = version_info_database[version].ecc[level].ecc_codewords; + size_t numSyndromes = numEcc; + + // According to the ISO there is a formula for a number of the syndromes. + // However several tests don't pass the error correction step because of less number of syndromes: + // 1M: qrcodes/detection/lots/image001.jpg from BoofCV (8 syndromes by formula, 10 needed) + // 1L: Objdetect_QRCode_Multi.regression/13 (4 syndromes by formula, 6 needed) + // 2L: qrcodes/detection/brightness/image011.jpg from BoofCV (8 syndromes by formula, 10 needed) + if (numSyndromes % 2 == 1) + numSyndromes -= 1; + + // Compute syndromes + bool hasError = false; + std::vector syndromes(numSyndromes, codewords[0]); + for (size_t i = 0; i < syndromes.size(); ++i) { + for (size_t j = 1; j < codewords.size(); ++j) { + syndromes[i] = gfMul(syndromes[i], gfPow(2, static_cast(i))) ^ codewords[j]; + } + hasError |= syndromes[i] != 0; + } + if (!hasError) { + // Trim error correction codewords + codewords.resize(codewords.size() - numEcc); + return true; + } + + // Run Berlekamp–Massey algorithm to find error positions (coefficients of locator poly) + size_t L = 0; // number of assumed errors + size_t m = 1; // shift value (between C and B) + uint8_t b = 1; // discrepancy from last L update + + std::vector C(numSyndromes, 0); // Error locator polynomial + std::vector B(numSyndromes, 0); // A copy of error locator from previos L update + C[0] = B[0] = 1; + for (size_t i = 0; i < numSyndromes; ++i) { + CV_Assert(m + L - 1 < C.size()); // m >= 1 on any iteration + uint8_t discrepancy = syndromes[i]; + for (size_t j = 1; j <= L; ++j) { + discrepancy ^= gfMul(C[j], syndromes[i - j]); + } + + if (discrepancy == 0) { + m += 1; + } else { + std::vector C_copy = C; + uint8_t inv_b = gfDiv(1, b); + uint8_t tmp = gfMul(discrepancy, inv_b); + + for (size_t j = 0; j < L; ++j) { + C[m + j] ^= gfMul(tmp, B[j]); + } + + if (2 * L <= i) { + L = i + 1 - L; + B = C_copy; + b = discrepancy; + m = 1; + } else { + m += 1; + } + } + } + + // There is an error at i-th position if i is a root of locator poly + std::vector errLocs; + errLocs.reserve(L); + for (size_t i = 0; i < codewords.size(); ++i) { + uint8_t val = 1; + uint8_t pos = gfPow(2, static_cast(i)); + for (size_t j = 1; j <= L; ++j) { + val = gfMul(val, pos) ^ C[j]; + } + if (val == 0) { + errLocs.push_back(static_cast(codewords.size() - 1 - i)); + } + } + + // Number of assumed errors does not match number of error locations + if (errLocs.size() != L) + return false; + + // Forney algorithm for error correction using syndromes and known error locations + std::vector errEval; + gfPolyMul(C, syndromes, errEval); + + for (size_t i = 0; i < errLocs.size(); ++i) { + uint8_t numenator = 0, denominator = 0; + uint8_t X = gfPow(2, static_cast(codewords.size() - 1 - errLocs[i])); + uint8_t inv_X = gfDiv(1, X); + + for (size_t j = 0; j < L; ++j) { + numenator = gfMul(numenator, inv_X) ^ errEval[L - 1 - j]; + } + + // Compute demoninator as a product of (1-X_i * X_k) for i != k + // TODO: optimize, there is a dubplicated compute + denominator = 1; + for (size_t j = 0; j < errLocs.size(); ++j) { + if (i == j) + continue; + uint8_t Xj = gfPow(2, static_cast(codewords.size() - 1 - errLocs[j])); + denominator = gfMul(denominator, 1 ^ gfMul(inv_X, Xj)); + } + + uint8_t errValue = gfDiv(numenator, denominator); + codewords[errLocs[i]] ^= errValue; + } + + // Trim error correction codewords + codewords.resize(codewords.size() - numEcc); + return true; +} + +void QRCodeDecoderImpl::extractCodewords(Mat& source, std::vector& codewords) { + const VersionInfo& version_info = version_info_database[version]; + + // Mask alignment markers + std::vector alignCenters; + alignCenters.reserve(MAX_ALIGNMENT); + for (int i = 0; i < MAX_ALIGNMENT && version_info.alignment_pattern[i]; i++) + alignCenters.push_back(version_info.alignment_pattern[i]); + + for (size_t i = 0; i < alignCenters.size(); i++) + { + for (size_t j = 0; j < alignCenters.size(); j++) + { + if ((i == alignCenters.size() - 1 && j == 0) || (i == 0 && j == 0) || + (j == alignCenters.size() - 1 && i == 0)) + continue; + int x = alignCenters[i]; + int y = alignCenters[j]; + Mat area = source({x - 2, x + 3}, {y - 2, y + 3}); + area.setTo(INVALID_REGION_VALUE); + } + } + + // Mask detection markers + source.rowRange(0, 9).colRange(source.cols - 8, source.cols).setTo(INVALID_REGION_VALUE); + source.rowRange(0, 9).colRange(0, 9).setTo(INVALID_REGION_VALUE); + source.colRange(0, 9).rowRange(source.rows - 8, source.rows).setTo(INVALID_REGION_VALUE); + + // Mask Version Information blocks + if (version >= 7) { + source.rowRange(0, 6).colRange(source.cols - 12, source.cols - 9).setTo(INVALID_REGION_VALUE); + source.colRange(0, 6).rowRange(source.rows - 12, source.rows - 9).setTo(INVALID_REGION_VALUE); + } + + // Mask timing pattern + source.row(6) = INVALID_REGION_VALUE; + + std::vector bits; + bits.reserve(source.total() - source.cols); + bool moveUpwards = true; + for (auto& data : {source.colRange(7, source.cols), source.colRange(0, 6)}) { + for (int i = data.cols / 2 - 1; i >= 0; --i) { + Mat col0 = data.col(i * 2); + Mat col1 = data.col(i * 2 + 1); + for (int j = 0; j < data.rows; ++j) { + if (moveUpwards) { + bits.push_back(col1.at(data.rows - 1 - j)); + bits.push_back(col0.at(data.rows - 1 - j)); + } else { + bits.push_back(col1.at(j)); + bits.push_back(col0.at(j)); + } + } + moveUpwards = !moveUpwards; + } + } + + // Combine bits to codewords + size_t numCodewords = version_info.total_codewords; + codewords.resize(numCodewords); + + size_t offset = 0; + for (size_t i = 0; i < numCodewords; ++i) { + codewords[i] = 0; + for (size_t j = 0; j < 8; ++j) { + while (bits[offset] == INVALID_REGION_VALUE) { + offset += 1; + CV_Assert(offset < bits.size()); + } + codewords[i] |= (bits[offset] & 1) << (7 - j); + offset += 1; + } + } +} + +void QRCodeDecoderImpl::decodeSymbols(String& result) { + CV_Assert(!bitstream.empty()); + + // Decode depends on the mode + result = ""; + while (!bitstream.empty()) { + // Determine mode + auto currMode = static_cast(bitstream.next(4)); + if (this->mode == 0) { + mode = currMode; + } + + if (currMode == 0 || bitstream.empty()) + return; + if (currMode == QRCodeEncoder::EncodeMode::MODE_NUMERIC) + decodeNumeric(result); + else if (currMode == QRCodeEncoder::EncodeMode::MODE_ALPHANUMERIC) + decodeAlpha(result); + else if (currMode == QRCodeEncoder::EncodeMode::MODE_BYTE) + decodeByte(result); + else if (currMode == QRCodeEncoder::EncodeMode::MODE_ECI) + decodeECI(result); + else if (currMode == QRCodeEncoder::EncodeMode::MODE_KANJI) + decodeKanji(result); + else + CV_Error(Error::StsNotImplemented, format("mode %d", currMode)); + } +} + +void QRCodeDecoderImpl::decodeNumeric(String& result) { + int numDigits = bitstream.next(version <= 9 ? 10 : (version <= 26 ? 12 : 14)); + for (int i = 0; i < numDigits / 3; ++i) { + int triple = bitstream.next(10); + result += static_cast('0' + triple / 100); + result += static_cast('0' + (triple / 10) % 10); + result += static_cast('0' + triple % 10); + } + int remainingDigits = numDigits % 3; + if (remainingDigits) { + int triple = bitstream.next(remainingDigits == 1 ? 4 : 7); + if (remainingDigits == 2) + result += '0' + (triple / 10) % 10; + result += '0' + triple % 10; + } +} + +void QRCodeDecoderImpl::decodeAlpha(String& result) { + static const char map[] = {'0', '1', '2', '3', '4', '5', '6', '7', '8', '9', + 'A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', 'I', 'J', + 'K', 'L', 'M', 'N', 'O', 'P', 'Q', 'R', 'S', 'T', + 'U', 'V', 'W', 'X', 'Y', 'Z', ' ', '$', '%', '*', + '+', '-', '.', '/', ':'}; + + int num = bitstream.next(version <= 9 ? 9 : (version <= 26 ? 11 : 13)); + for (int i = 0; i < num / 2; ++i) { + int tuple = bitstream.next(11); + result += map[tuple / 45]; + result += map[tuple % 45]; + } + if (num % 2) { + int value = bitstream.next(6); + result += map[value]; + } +} + +void QRCodeDecoderImpl::decodeByte(String& result) { + int num = bitstream.next(version <= 9 ? 8 : 16); + for (int i = 0; i < num; ++i) { + result += static_cast(bitstream.next(8)); + } +} + +void QRCodeDecoderImpl::decodeECI(String& result) { + int eciAssignValue = bitstream.next(8); + for (int i = 0; i < 8; ++i) { + if (eciAssignValue & 1 << (7 - i)) + eciAssignValue |= bitstream.next(8) << (i + 1) * 8; + else + break; + } + if (this->eci == 0) { + this->eci = static_cast(eciAssignValue); + } + decodeSymbols(result); + +} + +void QRCodeDecoderImpl::decodeKanji(String& result) { + int num = bitstream.next(version <= 9 ? 8 : (version <= 26 ? 10 : 12)); + for (int i = 0; i < num; ++i) { + int data = bitstream.next(13); + int high_byte = data / 0xC0; + int low_byte = data - high_byte * 0xC0; + int symbol = (high_byte << 8) + low_byte; + if (0 <= symbol && symbol <= 0x9FFC - 0x8140) { + symbol += 0x8140; + } else if (0xE040 - 0xC140 <= symbol && symbol <= 0xEBBF - 0xC140) { + symbol += 0xC140; + } + result += (symbol >> 8) & 0xff; + result += symbol & 0xff; + } +} + } diff --git a/modules/objdetect/src/qrcode_encoder_table.inl.hpp b/modules/objdetect/src/qrcode_encoder_table.inl.hpp index fc2ec37038..5a9c071aff 100644 --- a/modules/objdetect/src/qrcode_encoder_table.inl.hpp +++ b/modules/objdetect/src/qrcode_encoder_table.inl.hpp @@ -857,4 +857,13 @@ static const uint8_t gf_log[256] = { 0x4f, 0xae, 0xd5, 0xe9, 0xe6, 0xe7, 0xad, 0xe8, 0x74, 0xd6, 0xf4, 0xea, 0xa8, 0x50, 0x58, 0xaf }; + +// There are only 32 combinations of format info sequences. +static const uint16_t formatInfoLUT[32] = { + 0x5412, 0x5125, 0x5e7c, 0x5b4b, 0x45f9, 0x40ce, 0x4f97, 0x4aa0, + 0x77c4, 0x72f3, 0x7daa, 0x789d, 0x662f, 0x6318, 0x6c41, 0x6976, + 0x1689, 0x13be, 0x1ce7, 0x19d0, 0x0762, 0x0255, 0x0d0c, 0x083b, + 0x355f, 0x3068, 0x3f31, 0x3a06, 0x24b4, 0x2183, 0x2eda, 0x2bed +}; + } diff --git a/modules/objdetect/test/test_arucodetection.cpp b/modules/objdetect/test/test_arucodetection.cpp index 7369f80647..8e4f416c02 100644 --- a/modules/objdetect/test/test_arucodetection.cpp +++ b/modules/objdetect/test/test_arucodetection.cpp @@ -613,6 +613,32 @@ TEST(CV_ArucoDetectMarkers, regression_2492) } } + +TEST(CV_ArucoDetectMarkers, regression_contour_24220) +{ + aruco::ArucoDetector detector; + vector markerIds; + vector > markerCorners; + string imgPath = cvtest::findDataFile("aruco/failmask9.png"); + Mat image = imread(imgPath); + + const size_t N = 1ull; + const int goldCorners[8] = {392,175, 99,257, 117,109, 365,44}; + const int goldCornersId = 0; + + detector.detectMarkers(image, markerCorners, markerIds); + + ASSERT_EQ(N, markerIds.size()); + ASSERT_EQ(4ull, markerCorners[0].size()); + ASSERT_EQ(goldCornersId, markerIds[0]); + for (int j = 0; j < 4; j++) + { + EXPECT_NEAR(static_cast(goldCorners[j * 2]), markerCorners[0][j].x, 1.f); + EXPECT_NEAR(static_cast(goldCorners[j * 2 + 1]), markerCorners[0][j].y, 1.f); + } +} + + struct ArucoThreading: public testing::TestWithParam { struct NumThreadsSetter { diff --git a/modules/objdetect/test/test_charucodetection.cpp b/modules/objdetect/test/test_charucodetection.cpp index ff6f4fbe5f..89608213be 100644 --- a/modules/objdetect/test/test_charucodetection.cpp +++ b/modules/objdetect/test/test_charucodetection.cpp @@ -652,7 +652,7 @@ TEST(Charuco, issue_14014) EXPECT_EQ(Size(4, 1), corners[0].size()); // check dimension of detected corners size_t numRejPoints = rejectedPoints.size(); - ASSERT_EQ(rejectedPoints.size(), 26ull); // optional check to track regressions + ASSERT_EQ(rejectedPoints.size(), 24ull); // optional check to track regressions EXPECT_EQ(Size(4, 1), rejectedPoints[0].size()); // check dimension of detected corners detector.refineDetectedMarkers(img, board, corners, ids, rejectedPoints); diff --git a/modules/objdetect/test/test_qr_utils.hpp b/modules/objdetect/test/test_qr_utils.hpp index 115c767f71..5186acc7ad 100644 --- a/modules/objdetect/test/test_qr_utils.hpp +++ b/modules/objdetect/test/test_qr_utils.hpp @@ -10,9 +10,6 @@ void check_qr(const string& root, const string& name_current_image, const string const std::vector& corners, const std::vector& decoded_info, const int max_pixel_error, bool isMulti = false) { -#ifndef HAVE_QUIRC - CV_UNUSED(decoded_info); -#endif const std::string dataset_config = findDataFile(root + "dataset_config.json"); FileStorage file_config(dataset_config, FileStorage::READ); ASSERT_TRUE(file_config.isOpened()) << "Can't read validation data: " << dataset_config; @@ -50,7 +47,7 @@ void check_qr(const string& root, const string& name_current_image, const string EXPECT_NEAR(y, corners[i].y, max_pixel_error); } } -#ifdef HAVE_QUIRC + if (decoded_info.size() == 0ull) return; if (isMulti) { @@ -70,7 +67,7 @@ void check_qr(const string& root, const string& name_current_image, const string std::string original_info = config["info"]; EXPECT_EQ(decoded_info[0], original_info); } -#endif + return; // done } } diff --git a/modules/objdetect/test/test_qrcode.cpp b/modules/objdetect/test/test_qrcode.cpp index 89954c993a..63b50a2a59 100644 --- a/modules/objdetect/test/test_qrcode.cpp +++ b/modules/objdetect/test/test_qrcode.cpp @@ -56,9 +56,8 @@ TEST(Objdetect_QRCode, generate_test_data) std::string decoded_info; ASSERT_FALSE(src.empty()) << "Can't read image: " << image_path; EXPECT_TRUE(detectQRCode(src, corners)); -#ifdef HAVE_QUIRC EXPECT_TRUE(decodeQRCode(src, corners, decoded_info, straight_barcode)); -#endif + file_config << "x" << "[:"; for (size_t j = 0; j < corners.size(); j++) { file_config << corners[j].x; } file_config << "]"; @@ -95,9 +94,8 @@ TEST(Objdetect_QRCode_Close, generate_test_data) Size new_size(width, height); resize(src, barcode, new_size, 0, 0, INTER_LINEAR_EXACT); EXPECT_TRUE(detectQRCode(barcode, corners)); -#ifdef HAVE_QUIRC EXPECT_TRUE(decodeQRCode(barcode, corners, decoded_info, straight_barcode)); -#endif + file_config << "x" << "[:"; for (size_t j = 0; j < corners.size(); j++) { file_config << corners[j].x; } file_config << "]"; @@ -133,9 +131,8 @@ TEST(Objdetect_QRCode_Monitor, generate_test_data) Size new_size(width, height); resize(src, barcode, new_size, 0, 0, INTER_LINEAR_EXACT); EXPECT_TRUE(detectQRCode(barcode, corners)); -#ifdef HAVE_QUIRC EXPECT_TRUE(decodeQRCode(barcode, corners, decoded_info, straight_barcode)); -#endif + file_config << "x" << "[:"; for (size_t j = 0; j < corners.size(); j++) { file_config << corners[j].x; } file_config << "]"; @@ -165,9 +162,8 @@ TEST(Objdetect_QRCode_Curved, generate_test_data) std::string decoded_info; ASSERT_FALSE(src.empty()) << "Can't read image: " << image_path; EXPECT_TRUE(detectQRCode(src, corners)); -#ifdef HAVE_QUIRC EXPECT_TRUE(decodeCurvedQRCode(src, corners, decoded_info, straight_barcode)); -#endif + file_config << "x" << "[:"; for (size_t j = 0; j < corners.size(); j++) { file_config << corners[j].x; } file_config << "]"; @@ -198,11 +194,10 @@ TEST(Objdetect_QRCode_Multi, generate_test_data) std::vector corners; QRCodeDetector qrcode; EXPECT_TRUE(qrcode.detectMulti(src, corners)); -#ifdef HAVE_QUIRC std::vector decoded_info; std::vector straight_barcode; EXPECT_TRUE(qrcode.decodeMulti(src, corners, decoded_info, straight_barcode)); -#endif + file_config << "x" << "[:"; for(size_t j = 0; j < corners.size(); j += 4) { @@ -256,15 +251,11 @@ TEST_P(Objdetect_QRCode, regression) std::vector corners; std::string decoded_info; QRCodeDetector qrcode; -#ifdef HAVE_QUIRC decoded_info = qrcode.detectAndDecode(src, corners, straight_barcode); ASSERT_FALSE(corners.empty()); ASSERT_FALSE(decoded_info.empty()); int expected_barcode_type = CV_8UC1; EXPECT_EQ(expected_barcode_type, straight_barcode.type()); -#else - ASSERT_TRUE(qrcode.detect(src, corners)); -#endif check_qr(root, name_current_image, "test_images", corners, {decoded_info}, pixels_error); } @@ -287,15 +278,11 @@ TEST_P(Objdetect_QRCode_Close, regression) std::vector corners; std::string decoded_info; QRCodeDetector qrcode; -#ifdef HAVE_QUIRC decoded_info = qrcode.detectAndDecode(barcode, corners, straight_barcode); ASSERT_FALSE(corners.empty()); ASSERT_FALSE(decoded_info.empty()); int expected_barcode_type = CV_8UC1; EXPECT_EQ(expected_barcode_type, straight_barcode.type()); -#else - ASSERT_TRUE(qrcode.detect(barcode, corners)); -#endif check_qr(root, name_current_image, "close_images", corners, {decoded_info}, pixels_error); } @@ -318,15 +305,11 @@ TEST_P(Objdetect_QRCode_Monitor, regression) std::vector corners; std::string decoded_info; QRCodeDetector qrcode; -#ifdef HAVE_QUIRC decoded_info = qrcode.detectAndDecode(barcode, corners, straight_barcode); ASSERT_FALSE(corners.empty()); ASSERT_FALSE(decoded_info.empty()); int expected_barcode_type = CV_8UC1; EXPECT_EQ(expected_barcode_type, straight_barcode.type()); -#else - ASSERT_TRUE(qrcode.detect(barcode, corners)); -#endif check_qr(root, name_current_image, "monitor_images", corners, {decoded_info}, pixels_error); } @@ -344,15 +327,11 @@ TEST_P(Objdetect_QRCode_Curved, regression) std::vector corners; std::string decoded_info; QRCodeDetector qrcode; -#ifdef HAVE_QUIRC decoded_info = qrcode.detectAndDecodeCurved(src, corners, straight_barcode); ASSERT_FALSE(corners.empty()); ASSERT_FALSE(decoded_info.empty()); int expected_barcode_type = CV_8UC1; EXPECT_EQ(expected_barcode_type, straight_barcode.type()); -#else - ASSERT_TRUE(qrcode.detect(src, corners)); -#endif check_qr(root, name_current_image, "test_images", corners, {decoded_info}, pixels_error); } @@ -375,7 +354,6 @@ TEST_P(Objdetect_QRCode_Multi, regression) } std::vector corners; std::vector decoded_info; -#ifdef HAVE_QUIRC std::vector straight_barcode; EXPECT_TRUE(qrcode.detectAndDecodeMulti(src, decoded_info, corners, straight_barcode)); ASSERT_FALSE(corners.empty()); @@ -383,9 +361,6 @@ TEST_P(Objdetect_QRCode_Multi, regression) int expected_barcode_type = CV_8UC1; for(size_t i = 0; i < straight_barcode.size(); i++) EXPECT_EQ(expected_barcode_type, straight_barcode[i].type()); -#else - ASSERT_TRUE(qrcode.detectMulti(src, corners)); -#endif check_qr(root, name_current_image, "multiple_images", corners, decoded_info, pixels_error, true); } @@ -398,7 +373,6 @@ INSTANTIATE_TEST_CASE_P(/**/, Objdetect_QRCode_Multi, testing::Combine(testing:: TEST(Objdetect_QRCode_decodeMulti, decode_regression_16491) { -#ifdef HAVE_QUIRC Mat zero_image = Mat::zeros(256, 256, CV_8UC1); Point corners_[] = {Point(16, 16), Point(128, 16), Point(128, 128), Point(16, 128), Point(16, 16), Point(128, 16), Point(128, 128), Point(16, 128)}; @@ -413,7 +387,6 @@ TEST(Objdetect_QRCode_decodeMulti, decode_regression_16491) Mat mat_corners(2, 4, CV_32SC2, (void*)&vec_corners[0]); QRCodeDetector mat_qrcode; EXPECT_NO_THROW(mat_qrcode.decodeMulti(zero_image, mat_corners, decoded_info, straight_barcode)); -#endif } typedef testing::TestWithParam Objdetect_QRCode_detectMulti; @@ -449,7 +422,6 @@ TEST_P(Objdetect_QRCode_detectAndDecodeMulti, check_output_parameters_type_19363 std::string image_path = findDataFile(root + name_current_image); Mat src = imread(image_path); ASSERT_FALSE(src.empty()) << "Can't read image: " << image_path; -#ifdef HAVE_QUIRC GraphicalCodeDetector qrcode = QRCodeDetector(); if (method == "aruco_based") { qrcode = QRCodeDetectorAruco(); @@ -467,7 +439,6 @@ TEST_P(Objdetect_QRCode_detectAndDecodeMulti, check_output_parameters_type_19363 ASSERT_FALSE(corners.empty()); for(size_t i = 0; i < straight_barcode.size(); i++) EXPECT_EQ(expected_barcode_type, straight_barcode[i].type()); -#endif } INSTANTIATE_TEST_CASE_P(/**/, Objdetect_QRCode_detectAndDecodeMulti, testing::Values("contours_based", "aruco_based")); @@ -487,9 +458,7 @@ TEST(Objdetect_QRCode_detect, detect_regression_20882) cv::String decoded_info; EXPECT_TRUE(qrcode.detect(src, corners)); EXPECT_TRUE(!corners.empty()); -#ifdef HAVE_QUIRC EXPECT_NO_THROW(qrcode.decode(src, corners, straight_barcode)); -#endif } TEST(Objdetect_QRCode_basic, not_found_qrcode) @@ -500,10 +469,8 @@ TEST(Objdetect_QRCode_basic, not_found_qrcode) Mat zero_image = Mat::zeros(256, 256, CV_8UC1); QRCodeDetector qrcode; EXPECT_FALSE(qrcode.detect(zero_image, corners)); -#ifdef HAVE_QUIRC corners = std::vector(4); EXPECT_ANY_THROW(qrcode.decode(zero_image, corners, straight_barcode)); -#endif } TEST(Objdetect_QRCode_detect, detect_regression_21287) @@ -521,9 +488,7 @@ TEST(Objdetect_QRCode_detect, detect_regression_21287) cv::String decoded_info; EXPECT_TRUE(qrcode.detect(src, corners)); EXPECT_TRUE(!corners.empty()); -#ifdef HAVE_QUIRC EXPECT_NO_THROW(qrcode.decode(src, corners, straight_barcode)); -#endif } TEST(Objdetect_QRCode_detect_flipped, regression_23249) @@ -549,12 +514,10 @@ TEST(Objdetect_QRCode_detect_flipped, regression_23249) EXPECT_TRUE(qrcode.detect(src, corners)); EXPECT_TRUE(!corners.empty()); std::string decoded_msg; - #ifdef HAVE_QUIRC - const std::string &expect_msg = flipped_image.second; - EXPECT_NO_THROW(decoded_msg = qrcode.decode(src, corners, straight_barcode)); - ASSERT_FALSE(straight_barcode.empty()) << "Can't decode qrimage."; - EXPECT_EQ(expect_msg, decoded_msg); - #endif + const std::string &expect_msg = flipped_image.second; + EXPECT_NO_THROW(decoded_msg = qrcode.decode(src, corners, straight_barcode)); + ASSERT_FALSE(straight_barcode.empty()) << "Can't decode qrimage."; + EXPECT_EQ(expect_msg, decoded_msg); } } @@ -577,12 +540,10 @@ TEST(Objdetect_QRCode_decode, decode_regression_21929) EXPECT_TRUE(qrcode.detect(src, corners)); EXPECT_TRUE(!corners.empty()); -#ifdef HAVE_QUIRC cv::String decoded_msg; EXPECT_NO_THROW(decoded_msg = qrcode.decode(src, corners, straight_barcode)); ASSERT_FALSE(straight_barcode.empty()) << "Can't decode qrimage."; EXPECT_EQ(expect_msg, decoded_msg); -#endif } TEST(Objdetect_QRCode_decode, decode_regression_version_25) @@ -603,12 +564,11 @@ TEST(Objdetect_QRCode_decode, decode_regression_version_25) EXPECT_TRUE(qrcode.detect(src, corners)); EXPECT_TRUE(!corners.empty()); -#ifdef HAVE_QUIRC + cv::String decoded_msg; EXPECT_NO_THROW(decoded_msg = qrcode.decode(src, corners, straight_barcode)); ASSERT_FALSE(straight_barcode.empty()) << "Can't decode qrimage."; EXPECT_EQ(expect_msg, decoded_msg); -#endif } TEST_P(Objdetect_QRCode_detectAndDecodeMulti, decode_9_qrcodes_version7) @@ -639,9 +599,6 @@ TEST_P(Objdetect_QRCode_detectAndDecodeMulti, decode_9_qrcodes_version7) TEST(Objdetect_QRCode_detectAndDecode, utf8_output) { -#ifndef HAVE_QUIRC - throw SkipTestException("Quirc is required for decoding"); -#else const std::string name_current_image = "umlaut.png"; const std::string root = "qrcode/"; @@ -655,7 +612,6 @@ TEST(Objdetect_QRCode_detectAndDecode, utf8_output) std::string decoded_info = qrcode.detectAndDecode(src, corners, straight); EXPECT_FALSE(decoded_info.empty()); EXPECT_NE(decoded_info.find("M\xc3\xbcllheimstrasse"), std::string::npos); -#endif // HAVE_QUIRC } }} // namespace diff --git a/modules/objdetect/test/test_qrcode_encode.cpp b/modules/objdetect/test/test_qrcode_encode.cpp index 1005793269..7f5eb37f09 100644 --- a/modules/objdetect/test/test_qrcode_encode.cpp +++ b/modules/objdetect/test/test_qrcode_encode.cpp @@ -118,9 +118,7 @@ TEST(Objdetect_QRCode_Encode, generate_test_data) } std::string decoded_info = ""; -#ifdef HAVE_QUIRC EXPECT_TRUE(decodeQRCode(resized_src, corners, decoded_info, straight_barcode)) << "The QR code cannot be decoded: " << image_path; -#endif file_config << "info" << decoded_info; file_config << "}"; } @@ -306,7 +304,6 @@ TEST(Objdetect_QRCode_Encode_Decode, regression) corners[k].y = corners[k].y * height_ratio; } -#ifdef HAVE_QUIRC Mat straight_barcode; std::string output_info = QRCodeDetector().decode(resized_src, corners, straight_barcode); EXPECT_FALSE(output_info.empty()) @@ -314,7 +311,6 @@ TEST(Objdetect_QRCode_Encode_Decode, regression) << " version: " << version << " error correction level: " << (int)level; EXPECT_EQ(input_info, output_info) << "The generated QRcode is not same as test data." << " Mode: " << (int)mode << " version: " << version << " error correction level: " << (int)level; -#endif } } } @@ -356,12 +352,10 @@ TEST(Objdetect_QRCode_Encode_Kanji, regression) corners[j].y = corners[j].y * height_ratio; } -#ifdef HAVE_QUIRC Mat straight_barcode; std::string decoded_info = QRCodeDetector().decode(resized_src, corners, straight_barcode); EXPECT_FALSE(decoded_info.empty()) << "The generated QRcode cannot be decoded."; EXPECT_EQ(input_info, decoded_info); -#endif } } @@ -423,21 +417,15 @@ TEST(Objdetect_QRCode_Encode_Decode_Structured_Append, DISABLED_regression) corners[m].y = corners[m].y * height_ratio; } -#ifdef HAVE_QUIRC Mat straight_barcode; std::string decoded_info = QRCodeDetector().decode(resized_src, corners, straight_barcode); EXPECT_FALSE(decoded_info.empty()) << "The generated QRcode cannot be decoded." << " Mode: " << modes[i] << " structures number: " << k << "/" << j; output_info += decoded_info; -#endif } -#ifdef HAVE_QUIRC EXPECT_EQ(input_info, output_info) << "The generated QRcode is not same as test data." << " Mode: " << mode << " structures number: " << j; -#else - std::cout << "Mode=" << mode << ": Unable to verify generated QR codes - QUIRC is disabled" << std::endl; -#endif } } } @@ -548,4 +536,60 @@ TEST(Objdetect_QRCode_Encode_Decode, regression_issue22029) } } +// This test reproduces issue https://github.com/opencv/opencv/issues/24366 only in a loop +TEST(Objdetect_QRCode_Encode_Decode, auto_version_pick) +{ + cv::QRCodeEncoder::Params params; + params.correction_level = cv::QRCodeEncoder::CORRECT_LEVEL_L; + params.mode = cv::QRCodeEncoder::EncodeMode::MODE_AUTO; + + cv::Ptr encoder = cv::QRCodeEncoder::create(params); + + for (int len = 1; len < 19; len++) { + std::string input; + input.resize(len); + cv::randu(Mat(1, len, CV_8U, &input[0]), 'a', 'z' + 1); + cv::Mat qrcode; + encoder->encode(input, qrcode); + } +} + +// Test two QR codes which error correction procedure requires more number of +// syndroms that described in the ISO/IEC 18004 +typedef testing::TestWithParam> Objdetect_QRCode_decoding; +TEST_P(Objdetect_QRCode_decoding, error_correction) +{ + const std::string filename = get<0>(GetParam()); + const std::string expected = get<1>(GetParam()); + + QRCodeDetector qrcode; + cv::String decoded_msg; + Mat src = cv::imread(findDataFile("qrcode/" + filename), IMREAD_GRAYSCALE); + + std::vector corners(4); + corners[0] = Point2f(0, 0); + corners[1] = Point2f(src.cols * 1.0f, 0); + corners[2] = Point2f(src.cols * 1.0f, src.rows * 1.0f); + corners[3] = Point2f(0, src.rows * 1.0f); + + Mat resized_src; + resize(src, resized_src, fixed_size, 0, 0, INTER_AREA); + float width_ratio = resized_src.cols * 1.0f / src.cols; + float height_ratio = resized_src.rows * 1.0f / src.rows; + for(size_t m = 0; m < corners.size(); m++) + { + corners[m].x = corners[m].x * width_ratio; + corners[m].y = corners[m].y * height_ratio; + } + + Mat straight_barcode; + EXPECT_NO_THROW(decoded_msg = qrcode.decode(resized_src, corners, straight_barcode)); + ASSERT_FALSE(straight_barcode.empty()) << "Can't decode qrimage " << filename; + EXPECT_EQ(expected, decoded_msg); +} +INSTANTIATE_TEST_CASE_P(/**/, Objdetect_QRCode_decoding, testing::ValuesIn(std::vector>{ + {"err_correct_1M.png", "New"}, + {"err_correct_2L.png", "Version 2 QR Code Test Image"}, +})); + }} // namespace diff --git a/modules/python/src2/cv2_convert.hpp b/modules/python/src2/cv2_convert.hpp index 96a30e521f..8d2c876ada 100644 --- a/modules/python/src2/cv2_convert.hpp +++ b/modules/python/src2/cv2_convert.hpp @@ -156,6 +156,33 @@ struct PyOpenCV_Converter } }; +// There is conflict between "uint64_t" and "size_t". +// They are the same type on some 32-bit platforms. +template +struct PyOpenCV_Converter + < T, typename std::enable_if< std::is_same::value && !std::is_same::value >::type > +{ + static inline PyObject* from(const uint64_t& value) + { + return PyLong_FromUnsignedLongLong(value); + } + + static inline bool to(PyObject* obj, uint64_t& value, const ArgInfo& info) + { + CV_UNUSED(info); + if(!obj || obj == Py_None) + return true; + if(PyInt_Check(obj)) + value = (uint64_t)PyInt_AsUnsignedLongLongMask(obj); + else if(PyLong_Check(obj)) + value = (uint64_t)PyLong_AsUnsignedLongLong(obj); + else + return false; + return value != (uint64_t)-1 || !PyErr_Occurred(); + } +}; + + // --- uchar template<> bool pyopencv_to(PyObject* obj, uchar& value, const ArgInfo& info); template<> PyObject* pyopencv_from(const uchar& value); diff --git a/modules/python/src2/pycompat.hpp b/modules/python/src2/pycompat.hpp index c8806dc812..05a3909562 100644 --- a/modules/python/src2/pycompat.hpp +++ b/modules/python/src2/pycompat.hpp @@ -53,6 +53,7 @@ #define PyInt_CheckExact PyLong_CheckExact #define PyInt_AsLong PyLong_AsLong #define PyInt_AS_LONG PyLong_AS_LONG +#define PyInt_AsUnsignedLongLongMask PyLong_AsUnsignedLongLongMask #define PyInt_FromLong PyLong_FromLong #define PyNumber_Int PyNumber_Long diff --git a/modules/stereo/src/stereo_geom.cpp b/modules/stereo/src/stereo_geom.cpp index cd698f6637..31cb19dc0e 100644 --- a/modules/stereo/src/stereo_geom.cpp +++ b/modules/stereo/src/stereo_geom.cpp @@ -263,11 +263,11 @@ void stereoRectify( InputArray _cameraMatrix1, InputArray _distCoeffs1, s0); double s1 = std::min(std::min(std::min((double)cx1/(cx1_0 - outer1.x), (double)cy1/(cy1_0 - outer1.y)), - (double)(newImgSize.width - cx1)/(outer1.x + outer1.width - cx1_0)), - (double)(newImgSize.height - cy1)/(outer1.y + outer1.height - cy1_0)); + (double)(newImgSize.width - 1 - cx1)/(outer1.x + outer1.width - cx1_0)), + (double)(newImgSize.height - 1 - cy1)/(outer1.y + outer1.height - cy1_0)); s1 = std::min(std::min(std::min(std::min((double)cx2/(cx2_0 - outer2.x), (double)cy2/(cy2_0 - outer2.y)), - (double)(newImgSize.width - cx2)/(outer2.x + outer2.width - cx2_0)), - (double)(newImgSize.height - cy2)/(outer2.y + outer2.height - cy2_0)), + (double)(newImgSize.width - 1 - cx2)/(outer2.x + outer2.width - cx2_0)), + (double)(newImgSize.height - 1 - cy2)/(outer2.y + outer2.height - cy2_0)), s1); s = s0*(1 - alpha) + s1*alpha; diff --git a/modules/videoio/CMakeLists.txt b/modules/videoio/CMakeLists.txt index eee706d306..f800babdcd 100644 --- a/modules/videoio/CMakeLists.txt +++ b/modules/videoio/CMakeLists.txt @@ -1,5 +1,5 @@ set(VIDEOIO_ENABLE_PLUGINS_DEFAULT ON) -if(EMSCRIPTEN OR IOS OR WINRT) +if(EMSCRIPTEN OR IOS OR XROS OR WINRT) set(VIDEOIO_ENABLE_PLUGINS_DEFAULT OFF) endif() @@ -197,7 +197,7 @@ if(TARGET ocv.3rdparty.aravis) endif() if(TARGET ocv.3rdparty.avfoundation) - if(IOS) + if(IOS OR XROS) list(APPEND videoio_srcs ${CMAKE_CURRENT_LIST_DIR}/src/cap_avfoundation.mm) else() list(APPEND videoio_srcs ${CMAKE_CURRENT_LIST_DIR}/src/cap_avfoundation_mac.mm) diff --git a/modules/videoio/include/opencv2/videoio.hpp b/modules/videoio/include/opencv2/videoio.hpp index 0e69c27731..18eedff60e 100644 --- a/modules/videoio/include/opencv2/videoio.hpp +++ b/modules/videoio/include/opencv2/videoio.hpp @@ -103,7 +103,7 @@ enum VideoCaptureAPIs { CAP_CMU1394 = CAP_FIREWIRE, //!< Same value as CAP_FIREWIRE CAP_DSHOW = 700, //!< DirectShow (via videoInput) CAP_PVAPI = 800, //!< PvAPI, Prosilica GigE SDK - CAP_ANDROID = 1000, //!< Android - not used + CAP_ANDROID = 1000, //!< MediaNDK (API Level 21+) and NDK Camera (API level 24+) for Android CAP_XIAPI = 1100, //!< XIMEA Camera API CAP_AVFOUNDATION = 1200, //!< AVFoundation framework for iOS (OS X Lion will have the same API) CAP_MSMF = 1400, //!< Microsoft Media Foundation (via videoInput). See platform specific notes above. @@ -122,7 +122,7 @@ enum VideoCaptureAPIs { CAP_INTEL_MFX = 2300, //!< Intel MediaSDK CAP_XINE = 2400, //!< XINE engine (Linux) CAP_UEYE = 2500, //!< uEye Camera API - CAP_OBSENSOR = 2600, //!< For Orbbec 3D-Sensor device/module (Astra+, Femto) + CAP_OBSENSOR = 2600, //!< For Orbbec 3D-Sensor device/module (Astra+, Femto, Astra2, Gemini2, Gemini2L, Gemini2XL, Femto Mega) attention: Astra2, Gemini2, and Gemini2L cameras currently only support Windows and Linux kernel versions no higher than 4.15, and higher versions of Linux kernel may have exceptions. }; @@ -1017,7 +1017,7 @@ public: VideoWriter::fourcc('P','I','M','1') is a MPEG-1 codec, VideoWriter::fourcc('M','J','P','G') is a motion-jpeg codec etc. List of codes can be obtained at [MSDN](https://docs.microsoft.com/en-us/windows/win32/medfound/video-fourccs) page - or with this [archived page](https://web.archive.org/web/20220316062600/http://www.fourcc.org/codecs.php) + or with this [page](https://fourcc.org/codecs.php) of the fourcc site for a more complete list). FFMPEG backend with MP4 container natively uses other values as fourcc code: see [ObjectType](http://mp4ra.org/#/codecs), so you may receive a warning message from OpenCV about fourcc code conversion. diff --git a/modules/videoio/src/cap_android_camera.cpp b/modules/videoio/src/cap_android_camera.cpp index 84034e6208..b74810368d 100644 --- a/modules/videoio/src/cap_android_camera.cpp +++ b/modules/videoio/src/cap_android_camera.cpp @@ -533,6 +533,7 @@ public: cachedIndex = index; cameraManager = std::shared_ptr(ACameraManager_create(), deleter_ACameraManager); if (!cameraManager) { + LOGE("Cannot create camera manager!"); return false; } ACameraIdList* cameraIds = nullptr; @@ -591,6 +592,7 @@ public: } } } + LOGI("Best resolution match: %dx%d", bestMatchWidth, bestMatchHeight); ACameraMetadata_const_entry val = { 0, }; camera_status_t status = ACameraMetadata_getConstEntry(cameraMetadata.get(), ACAMERA_SENSOR_INFO_EXPOSURE_TIME_RANGE, &val); @@ -654,7 +656,11 @@ public: return false; } sessionOutput = std::shared_ptr(output, deleter_ACaptureSessionOutput); - ACaptureSessionOutputContainer_add(outputContainer.get(), sessionOutput.get()); + cStatus = ACaptureSessionOutputContainer_add(outputContainer.get(), sessionOutput.get()); + if (cStatus != ACAMERA_OK) { + LOGE("CaptureSessionOutput Container add failed with error code: %d", cStatus); + return false; + } sessionOutputAdded = true; ACameraOutputTarget* target; diff --git a/modules/videoio/src/cap_avfoundation.mm b/modules/videoio/src/cap_avfoundation.mm index 12fa42f5b5..d0f2b41c37 100644 --- a/modules/videoio/src/cap_avfoundation.mm +++ b/modules/videoio/src/cap_avfoundation.mm @@ -61,6 +61,8 @@ #define DISABLE_AUTO_RESTART 999 +#if !TARGET_OS_VISION + @interface CaptureDelegate : NSObject { int newFrame; @@ -125,6 +127,7 @@ class CvCaptureCAM : public CvCapture { int disableAutoRestart; }; +#endif /***************************************************************************** * @@ -160,6 +163,7 @@ private: uint32_t mMode; int mFormat; + void handleTracks(NSArray* tracks, const char* filename); bool setupReadingAt(CMTime position); IplImage* retrieveFramePixelBuffer(); int getPreferredOrientationDegrees() const; @@ -217,6 +221,8 @@ cv::Ptr cv::create_AVFoundation_capture_file(const std::strin } +#if !TARGET_OS_VISION + cv::Ptr cv::create_AVFoundation_capture_cam(int index) { CvCaptureCAM* retval = new CvCaptureCAM(index); @@ -226,6 +232,8 @@ cv::Ptr cv::create_AVFoundation_capture_cam(int index) return 0; } +#endif + cv::Ptr cv::create_AVFoundation_writer(const std::string& filename, int fourcc, double fps, const cv::Size &frameSize, const cv::VideoWriterParameters& params) @@ -245,6 +253,8 @@ cv::Ptr cv::create_AVFoundation_writer(const std::string& file * *****************************************************************************/ +#if !TARGET_OS_VISION + CvCaptureCAM::CvCaptureCAM(int cameraNum) { mCaptureSession = nil; mCaptureDeviceInput = nil; @@ -773,6 +783,7 @@ fromConnection:(AVCaptureConnection *)connection{ @end +#endif /***************************************************************************** * @@ -811,24 +822,26 @@ CvCaptureFile::CvCaptureFile(const char* filename) { return; } +// Available since iOS 15 +#if TARGET_OS_VISION || (defined(__IPHONE_OS_VERSION_MIN_REQUIRED) && __IPHONE_OS_VERSION_MIN_REQUIRED >= 150000) + if (@available(iOS 15, visionOS 1, *)) { + [mAsset loadTracksWithMediaType:AVMediaTypeVideo completionHandler:^(NSArray* tracks, NSError* err) { + if (err != nil) { + handleTracks(tracks, filename); + } + [localpool drain]; + }]; + return; + } else { +#if !TARGET_OS_VISION + NSArray *tracks = [mAsset tracksWithMediaType:AVMediaTypeVideo]; + handleTracks(tracks, filename); +#endif + } +#else NSArray *tracks = [mAsset tracksWithMediaType:AVMediaTypeVideo]; - if ([tracks count] == 0) { - fprintf(stderr, "OpenCV: Couldn't read video stream from file \"%s\"\n", filename); - [localpool drain]; - started = 0; - return; - } - - mAssetTrack = [tracks[0] retain]; - - if ( ! setupReadingAt(kCMTimeZero) ) { - fprintf(stderr, "OpenCV: Couldn't read movie file \"%s\"\n", filename); - [localpool drain]; - started = 0; - return; - } - - started = 1; + handleTracks(tracks, filename); +#endif [localpool drain]; } @@ -850,6 +863,24 @@ CvCaptureFile::~CvCaptureFile() { [localpool drain]; } +void CvCaptureFile::handleTracks(NSArray* tracks, const char* filename) { + if ([tracks count] == 0) { + fprintf(stderr, "OpenCV: Couldn't read video stream from file \"%s\"\n", filename); + started = 0; + return; + } + + mAssetTrack = [tracks[0] retain]; + + if ( ! setupReadingAt(kCMTimeZero) ) { + fprintf(stderr, "OpenCV: Couldn't read movie file \"%s\"\n", filename); + started = 0; + return; + } + + started = 1; +} + bool CvCaptureFile::setupReadingAt(CMTime position) { if (mAssetReader) { if (mAssetReader.status == AVAssetReaderStatusReading) { @@ -1269,25 +1300,25 @@ CvVideoWriter_AVFoundation::CvVideoWriter_AVFoundation(const char* filename, int //exception; } - // Three codec supported AVVideoCodecH264 AVVideoCodecJPEG AVVideoCodecTypeHEVC + // Three codec supported AVVideoCodecTypeH264 AVVideoCodecTypeJPEG AVVideoCodecTypeHEVC // On iPhone 3G H264 is not supported. if (fourcc == CV_FOURCC('J','P','E','G') || fourcc == CV_FOURCC('j','p','e','g') || fourcc == CV_FOURCC('M','J','P','G') || fourcc == CV_FOURCC('m','j','p','g')){ - codec = [AVVideoCodecJPEG copy]; // Use JPEG codec if specified, otherwise H264 + codec = [AVVideoCodecTypeJPEG copy]; // Use JPEG codec if specified, otherwise H264 }else if(fourcc == CV_FOURCC('H','2','6','4') || fourcc == CV_FOURCC('a','v','c','1')){ - codec = [AVVideoCodecH264 copy]; + codec = [AVVideoCodecTypeH264 copy]; // Available since iOS 11 -#if defined(__IPHONE_OS_VERSION_MIN_REQUIRED) && __IPHONE_OS_VERSION_MIN_REQUIRED >= 110000 +#if TARGET_OS_VISION || (defined(__IPHONE_OS_VERSION_MIN_REQUIRED) && __IPHONE_OS_VERSION_MIN_REQUIRED >= 110000) }else if(fourcc == CV_FOURCC('H','2','6','5') || fourcc == CV_FOURCC('h','v','c','1') || fourcc == CV_FOURCC('H','E','V','C') || fourcc == CV_FOURCC('h','e','v','c')){ - if (@available(iOS 11, *)) { + if (@available(iOS 11, visionOS 1, *)) { codec = [AVVideoCodecTypeHEVC copy]; } else { - codec = [AVVideoCodecH264 copy]; + codec = [AVVideoCodecTypeH264 copy]; } #endif }else{ - codec = [AVVideoCodecH264 copy]; // default canonical H264. + codec = [AVVideoCodecTypeH264 copy]; // default canonical H264. } //NSLog(@"Path: %@", path); @@ -1349,17 +1380,17 @@ CvVideoWriter_AVFoundation::~CvVideoWriter_AVFoundation() { NSAutoreleasePool* localpool = [[NSAutoreleasePool alloc] init]; [mMovieWriterInput markAsFinished]; - [mMovieWriter finishWriting]; - [mMovieWriter release]; - [mMovieWriterInput release]; - [mMovieWriterAdaptor release]; - [path release]; - [codec release]; - [fileType release]; - cvReleaseImage(&argbimage); - - [localpool drain]; + [mMovieWriter finishWritingWithCompletionHandler:^() { + [mMovieWriter release]; + [mMovieWriterInput release]; + [mMovieWriterAdaptor release]; + [path release]; + [codec release]; + [fileType release]; + cvReleaseImage(&argbimage); + [localpool drain]; + }]; } bool CvVideoWriter_AVFoundation::writeFrame(const IplImage* iplimage) { diff --git a/modules/videoio/src/cap_ffmpeg_impl.hpp b/modules/videoio/src/cap_ffmpeg_impl.hpp index bf259b4daf..3dcd4e81d5 100644 --- a/modules/videoio/src/cap_ffmpeg_impl.hpp +++ b/modules/videoio/src/cap_ffmpeg_impl.hpp @@ -428,11 +428,15 @@ inline const char* _opencv_avcodec_get_name(CV_CODEC_ID id) } -static -inline int _opencv_ffmpeg_interrupt_callback(void *ptr) +static int _opencv_ffmpeg_interrupt_callback(void *ptr) { AVInterruptCallbackMetadata* metadata = (AVInterruptCallbackMetadata*)ptr; - CV_Assert(metadata); + + if(!metadata) + { + CV_LOG_WARNING(NULL, "Stream timeout without metadata passed"); + return 0; + } if (metadata->timeout_after_ms == 0) { @@ -442,9 +446,15 @@ inline int _opencv_ffmpeg_interrupt_callback(void *ptr) timespec now; get_monotonic_time(&now); - metadata->timeout = get_monotonic_time_diff_ms(metadata->value, now) > metadata->timeout_after_ms; + double timeout = get_monotonic_time_diff_ms(metadata->value, now); + metadata->timeout = timeout > metadata->timeout_after_ms; + if (metadata->timeout) + { + CV_LOG_WARNING(NULL, cv::format("Stream timeout triggered after %lf ms", timeout)); + return -1; + } - return metadata->timeout ? -1 : 0; + return 0; } #endif @@ -1877,15 +1887,16 @@ int64_t CvCapture_FFMPEG::get_bitrate() const double CvCapture_FFMPEG::get_fps() const { -#if LIBAVFORMAT_BUILD >= CALC_FFMPEG_VERSION(55, 1, 100) && LIBAVFORMAT_VERSION_MICRO >= 100 - double fps = r2d(av_guess_frame_rate(ic, ic->streams[video_stream], NULL)); -#else +#if LIBAVCODEC_BUILD >= CALC_FFMPEG_VERSION(54, 1, 0) || LIBAVFORMAT_BUILD >= CALC_FFMPEG_VERSION(52, 111, 0) double fps = r2d(ic->streams[video_stream]->avg_frame_rate); +#else + double fps = r2d(ic->streams[video_stream]->r_frame_rate); +#endif -#if LIBAVFORMAT_BUILD >= CALC_FFMPEG_VERSION(52, 111, 0) +#if LIBAVFORMAT_BUILD >= CALC_FFMPEG_VERSION(55, 1, 100) && LIBAVFORMAT_VERSION_MICRO >= 100 if (fps < eps_zero) { - fps = r2d(ic->streams[video_stream]->avg_frame_rate); + fps = r2d(av_guess_frame_rate(ic, ic->streams[video_stream], NULL)); } #endif @@ -1893,7 +1904,7 @@ double CvCapture_FFMPEG::get_fps() const { fps = 1.0 / r2d(ic->streams[video_stream]->time_base); } -#endif + return fps; } diff --git a/modules/videoio/src/cap_gstreamer.cpp b/modules/videoio/src/cap_gstreamer.cpp index 305d527ce9..cdaccabe45 100644 --- a/modules/videoio/src/cap_gstreamer.cpp +++ b/modules/videoio/src/cap_gstreamer.cpp @@ -114,6 +114,7 @@ template<> inline void GSafePtr_release(GstBuffer** pPtr) { if (pPtr) template<> inline void GSafePtr_release(GstSample** pPtr) { if (pPtr) { gst_sample_unref(*pPtr); *pPtr = NULL; } } template<> inline void GSafePtr_release(GstBus** pPtr) { if (pPtr) { gst_object_unref(G_OBJECT(*pPtr)); *pPtr = NULL; } } template<> inline void GSafePtr_release(GstMessage** pPtr) { if (pPtr) { gst_message_unref(*pPtr); *pPtr = NULL; } } +template<> inline void GSafePtr_release(GstQuery** pPtr) { if (pPtr) { gst_query_unref(*pPtr); *pPtr = NULL; } } template<> inline void GSafePtr_release(GMainLoop** pPtr) { if (pPtr) { g_main_loop_unref(*pPtr); *pPtr = NULL; } } template<> inline void GSafePtr_release(GstEncodingVideoProfile** pPtr) { if (pPtr) { gst_encoding_profile_unref(*pPtr); *pPtr = NULL; } } @@ -367,6 +368,7 @@ private: gint audioBitPerFrame; gint audioSampleSize; std::string audioFormat; + guint64 timestamp; Mat audioFrame; std::deque bufferAudioData; @@ -433,7 +435,8 @@ GStreamerCapture::GStreamerCapture() : audioSamplesPerSecond(44100), audioBitPerFrame(0), audioSampleSize(0), - audioFormat("S16LE") + audioFormat("S16LE"), + timestamp(0) , va_type(VIDEO_ACCELERATION_NONE) , hw_device(-1) {} @@ -680,6 +683,11 @@ bool GStreamerCapture::grabVideoFrame() stopFlag = true; emulatedFrameNumber++; } + if (usedVideoSample) + { + auto *buffer = gst_sample_get_buffer((GstSample*)usedVideoSample); + timestamp = GST_BUFFER_PTS(buffer); + } returnFlag = true; } } @@ -792,6 +800,7 @@ bool GStreamerCapture::grabAudioFrame() CV_LOG_ERROR(NULL, "GStreamer: Failed. Buffer is empty"); return false; } + timestamp = GST_BUFFER_PTS(buf); if (!gst_buffer_map(buf, &map_info, GST_MAP_READ)) { CV_LOG_ERROR(NULL, "GStreamer: Failed to map GStreamer buffer to system memory"); @@ -1389,6 +1398,7 @@ bool GStreamerCapture::open(const String &filename_, const cv::VideoCaptureParam GSafePtr uri; GSafePtr bus; + GSafePtr queue; GSafePtr uridecodebin; GSafePtr color; GSafePtr convert; @@ -1493,6 +1503,7 @@ bool GStreamerCapture::open(const String &filename_, const cv::VideoCaptureParam if (strstr(name, "opencvsink") != NULL || strstr(name, "appsink") != NULL) { sink.attach(GST_ELEMENT(gst_object_ref(element))); + audiosink.attach(GST_ELEMENT(gst_object_ref(element))); } else if (strstr(name, COLOR_ELEM_NAME) != NULL) { @@ -1534,6 +1545,8 @@ bool GStreamerCapture::open(const String &filename_, const cv::VideoCaptureParam if (videoStream >= 0) { + queue.reset(gst_element_factory_make("queue", NULL)); + CV_Assert(queue); sink.reset(gst_element_factory_make("appsink", NULL)); CV_Assert(sink); // videoconvert (in 0.10: ffmpegcolorspace, in 1.x autovideoconvert) @@ -1541,7 +1554,7 @@ bool GStreamerCapture::open(const String &filename_, const cv::VideoCaptureParam color.reset(gst_element_factory_make(COLOR_ELEM, NULL)); CV_Assert(color); - gst_bin_add_many(GST_BIN(pipeline.get()), uridecodebin.get(), color.get(), sink.get(), NULL); + gst_bin_add_many(GST_BIN(pipeline.get()), queue.get(), uridecodebin.get(), color.get(), sink.get(), NULL); if (element_from_uri) { @@ -1566,6 +1579,8 @@ bool GStreamerCapture::open(const String &filename_, const cv::VideoCaptureParam } if (audioStream >= 0) { + queue.reset(gst_element_factory_make("queue", NULL)); + CV_Assert(queue); convert.reset(gst_element_factory_make("audioconvert", NULL)); resample.reset(gst_element_factory_make("audioresample", NULL)); audiosink.reset(gst_element_factory_make("appsink", NULL)); @@ -1573,7 +1588,7 @@ bool GStreamerCapture::open(const String &filename_, const cv::VideoCaptureParam CV_Assert(resample); CV_Assert(audiosink); - gst_bin_add_many (GST_BIN (pipeline.get()), uridecodebin.get(), convert.get(), resample.get(), audiosink.get(), NULL); + gst_bin_add_many (GST_BIN (pipeline.get()), queue.get(), uridecodebin.get(), convert.get(), resample.get(), audiosink.get(), NULL); if (!gst_element_link_many (convert.get(), resample.get(), audiosink.get(), NULL)) { CV_WARN("GStreamer(audio): cannot link convert -> resample -> sink"); @@ -1646,14 +1661,17 @@ bool GStreamerCapture::open(const String &filename_, const cv::VideoCaptureParam } if (manualpipeline) { - GSafePtr peer_caps; - GSafePtr sink_pad; - sink_pad.attach(gst_element_get_static_pad(sink, "sink")); - peer_caps.attach(gst_pad_peer_query_caps(sink_pad, NULL)); - if (!gst_caps_can_intersect(caps, peer_caps)) + if (videoStream >= 0) { - caps.attach(gst_caps_from_string("video/x-raw, format=(string){UYVY,YUY2,YVYU,NV12,NV21,YV12,I420,BGRA,RGBA,BGRx,RGBx,GRAY16_LE,GRAY16_BE}")); - CV_Assert(caps); + GSafePtr peer_caps; + GSafePtr sink_pad; + sink_pad.attach(gst_element_get_static_pad(sink, "sink")); + peer_caps.attach(gst_pad_peer_query_caps(sink_pad, NULL)); + if (!gst_caps_can_intersect(caps, peer_caps)) + { + caps.attach(gst_caps_from_string("video/x-raw, format=(string){UYVY,YUY2,YVYU,NV12,NV21,YV12,I420,BGRA,RGBA,BGRx,RGBx,GRAY16_LE,GRAY16_BE}")); + CV_Assert(caps); + } } } if (videoStream >= 0) @@ -1661,6 +1679,7 @@ bool GStreamerCapture::open(const String &filename_, const cv::VideoCaptureParam gst_app_sink_set_caps(GST_APP_SINK(sink.get()), caps); caps.release(); } + { GST_DEBUG_BIN_TO_DOT_FILE(GST_BIN(pipeline.get()), GST_DEBUG_GRAPH_SHOW_ALL, "pipeline-init"); @@ -1688,18 +1707,6 @@ bool GStreamerCapture::open(const String &filename_, const cv::VideoCaptureParam GSafePtr buffer_caps; buffer_caps.attach(gst_pad_get_current_caps(pad)); - GstFormat format; - - format = GST_FORMAT_DEFAULT; - if(!gst_element_query_duration(sink, format, &duration)) - { - handleMessage(pipeline); - CV_WARN("unable to query duration of stream"); - duration = -1; - } - - handleMessage(pipeline); - const GstStructure *structure = gst_caps_get_structure(buffer_caps, 0); // no lifetime transfer if (!gst_structure_get_int (structure, "width", &width) || !gst_structure_get_int (structure, "height", &height)) @@ -1708,13 +1715,55 @@ bool GStreamerCapture::open(const String &filename_, const cv::VideoCaptureParam } gint num = 0, denom=1; + bool fps_query_success = true; + if (!gst_structure_get_fraction(structure, "framerate", &num, &denom)) { CV_WARN("cannot query video fps"); + fps_query_success = false; } fps = (double)num/(double)denom; + // If num == 0 and denom == 1 -> variable frame rate video. + if (fps_query_success && !(num == 0 && denom == 1)) + { + GSafePtr query; + query.attach(gst_query_new_duration(GST_FORMAT_DEFAULT)); + + gboolean res = gst_element_query(pipeline.get(), query); + + if (res) + { + gst_query_parse_duration(query, NULL, &duration); + } + else if (fps != 0) + { + GSafePtr query2; + query2.attach(gst_query_new_duration(GST_FORMAT_TIME)); + gboolean res2 = gst_element_query(pipeline.get(), query2); + + if (res2) + { + gst_query_parse_duration(query2, NULL, &duration); + duration = static_cast((float)duration / GST_SECOND * fps); + CV_WARN("frame count is estimated by duration and fps"); + } + else + { + CV_WARN("unable to query duration of stream"); + duration = -1; + } + } + else + { + CV_WARN("unable to query frame count of stream and fps are not available to estimate it"); + duration = -1; + } + } + + handleMessage(pipeline); + { GstFormat format_; gint64 value_ = -1; @@ -1814,20 +1863,7 @@ double GStreamerCapture::getProperty(int propId) const switch(propId) { case CV_CAP_PROP_POS_MSEC: - CV_LOG_ONCE_WARNING(NULL, "OpenCV | GStreamer: CAP_PROP_POS_MSEC property result may be unrealiable: " - "https://github.com/opencv/opencv/issues/19025"); - if (audioStream != -1) - { - return usedVideoSampleTimeNS * 1e-6; - } - format = GST_FORMAT_TIME; - status = gst_element_query_position(sink.get(), CV_GST_FORMAT(format), &value); - if(!status) { - handleMessage(pipeline); - CV_WARN("GStreamer: unable to query position of stream"); - return 0; - } - return value * 1e-6; // nano seconds to milli seconds + return double(timestamp) / GST_MSECOND; case CV_CAP_PROP_POS_FRAMES: if (!isPosFramesSupported) { @@ -1859,7 +1895,7 @@ double GStreamerCapture::getProperty(int propId) const case CV_CAP_PROP_FPS: return fps; case CV_CAP_PROP_FRAME_COUNT: - return duration; + return (double)duration; case CV_CAP_PROP_BRIGHTNESS: case CV_CAP_PROP_CONTRAST: case CV_CAP_PROP_SATURATION: @@ -1936,13 +1972,15 @@ bool GStreamerCapture::setProperty(int propId, double value) return false; } - bool wasPlaying = this->isPipelinePlaying(); - if (wasPlaying) + bool needRestart = this->isPipelinePlaying() && (propId == CV_CAP_PROP_FRAME_WIDTH || propId == CV_CAP_PROP_FRAME_HEIGHT || propId == CV_CAP_PROP_FPS); + if (needRestart) { this->stopPipeline(); + } switch(propId) { case CV_CAP_PROP_POS_MSEC: + { if(!gst_element_seek_simple(GST_ELEMENT(pipeline.get()), GST_FORMAT_TIME, flags, (gint64) (value * GST_MSECOND))) { handleMessage(pipeline); @@ -1950,6 +1988,9 @@ bool GStreamerCapture::setProperty(int propId, double value) } else { + // Optimistically caching the target timestamp before reading the first frame from the new position since + // the timestamp in GStreamer can be reliable extracted from the read frames. + timestamp = (gint64)value; if (isPosFramesEmulated) { if (value == 0) @@ -1963,7 +2004,8 @@ bool GStreamerCapture::setProperty(int propId, double value) } } } - break; + return true; + } case CV_CAP_PROP_POS_FRAMES: { if (!isPosFramesSupported) @@ -1977,24 +2019,34 @@ bool GStreamerCapture::setProperty(int propId, double value) return true; } } - return false; CV_WARN("unable to seek"); + return false; } + // Certain mov and mp4 files seek incorrectly if the pipeline is not stopped before. + if (this->isPipelinePlaying()) { + this->stopPipeline(); + } + if(!gst_element_seek_simple(GST_ELEMENT(pipeline.get()), GST_FORMAT_DEFAULT, flags, (gint64) value)) { handleMessage(pipeline); CV_WARN("GStreamer: unable to seek"); - break; + return false; } // wait for status update gst_element_get_state(pipeline, NULL, NULL, GST_CLOCK_TIME_NONE); return true; } case CV_CAP_PROP_POS_AVI_RATIO: + { + // https://stackoverflow.com/questions/31290315 + // GStreamer docs: GST_FORMAT_PERCENT (5) – percentage of stream (few, if any, elements implement this as of May 2009) + CV_WARN("GStreamer: seeking by file percent are not supported by most GStreamer elements"); if(!gst_element_seek_simple(GST_ELEMENT(pipeline.get()), GST_FORMAT_PERCENT, flags, (gint64) (value * GST_FORMAT_PERCENT_MAX))) { handleMessage(pipeline); CV_WARN("GStreamer: unable to seek"); + return false; } else { @@ -2011,7 +2063,8 @@ bool GStreamerCapture::setProperty(int propId, double value) } } } - break; + return true; + } case CV_CAP_PROP_FRAME_WIDTH: if(value > 0) setFilter("width", G_TYPE_INT, (int) value, 0); @@ -2099,8 +2152,9 @@ bool GStreamerCapture::setProperty(int propId, double value) CV_WARN("GStreamer: unhandled property"); } - if (wasPlaying) + if (needRestart) { this->startPipeline(); + } return false; } @@ -2572,7 +2626,7 @@ bool CvVideoWriter_GStreamer::open( const std::string &filename, int fourcc, if (stateret == GST_STATE_CHANGE_FAILURE) { handleMessage(pipeline); - CV_WARN("GStreamer: cannot put pipeline to play\n"); + CV_WARN("GStreamer: cannot put pipeline to play"); pipeline.release(); return false; } diff --git a/modules/videoio/src/cap_ios_photo_camera.mm b/modules/videoio/src/cap_ios_photo_camera.mm index 9b44156efc..c210f17f06 100644 --- a/modules/videoio/src/cap_ios_photo_camera.mm +++ b/modules/videoio/src/cap_ios_photo_camera.mm @@ -139,7 +139,7 @@ { // setup still image output with jpeg codec self.stillImageOutput = [[AVCaptureStillImageOutput alloc] init]; - NSDictionary *outputSettings = [NSDictionary dictionaryWithObjectsAndKeys:AVVideoCodecJPEG, AVVideoCodecKey, nil]; + NSDictionary *outputSettings = [NSDictionary dictionaryWithObjectsAndKeys:AVVideoCodecTypeJPEG, AVVideoCodecKey, nil]; [self.stillImageOutput setOutputSettings:outputSettings]; [self.captureSession addOutput:self.stillImageOutput]; diff --git a/modules/videoio/src/cap_ios_video_camera.mm b/modules/videoio/src/cap_ios_video_camera.mm index c1282ecbd8..82e81806a0 100644 --- a/modules/videoio/src/cap_ios_video_camera.mm +++ b/modules/videoio/src/cap_ios_video_camera.mm @@ -376,7 +376,7 @@ static CGFloat DegreesToRadians(CGFloat degrees) {return degrees * M_PI / 180;} NSDictionary *outputSettings = [NSDictionary dictionaryWithObjectsAndKeys:[NSNumber numberWithInt:self.imageWidth], AVVideoWidthKey, [NSNumber numberWithInt:self.imageHeight], AVVideoHeightKey, - AVVideoCodecH264, AVVideoCodecKey, + AVVideoCodecTypeH264, AVVideoCodecKey, nil ]; diff --git a/modules/videoio/src/cap_msmf.cpp b/modules/videoio/src/cap_msmf.cpp index 4b234b8cae..6fbcd2aa02 100644 --- a/modules/videoio/src/cap_msmf.cpp +++ b/modules/videoio/src/cap_msmf.cpp @@ -18,6 +18,7 @@ #include #include +#include #include #include #include @@ -159,6 +160,11 @@ private: template inline T absDiff(T a, T b) { return a >= b ? a - b : b - a; } +// synonym for system MFVideoFormat_D16. D3DFMT_D16 = 80 +// added to fix builds with old MSVS and platform SDK +// see https://learn.microsoft.com/en-us/windows/win32/medfound/video-subtype-guids#luminance-and-depth-formats +DEFINE_MEDIATYPE_GUID( OCV_MFVideoFormat_D16, 80 ); + //================================================================================================== // Structure for collecting info about types of video which are supported by current video device @@ -350,9 +356,7 @@ struct MediaType } bool VideoIsAvailable() const { - return ((subType == MFVideoFormat_RGB32) || - (subType == MFVideoFormat_RGB24) || - (subType == MFVideoFormat_YUY2)); + return (subType != OCV_MFVideoFormat_D16); } }; diff --git a/modules/videoio/src/cap_obsensor/obsensor_stream_channel_interface.hpp b/modules/videoio/src/cap_obsensor/obsensor_stream_channel_interface.hpp index ff78c5a696..7337452359 100644 --- a/modules/videoio/src/cap_obsensor/obsensor_stream_channel_interface.hpp +++ b/modules/videoio/src/cap_obsensor/obsensor_stream_channel_interface.hpp @@ -37,6 +37,8 @@ namespace obsensor { #define OBSENSOR_ASTRA2_PID 0x0660 // pid of Orbbec Astra 2 Camera #define OBSENSOR_GEMINI2_PID 0x0670 // pid of Orbbec Gemini 2 Camera #define OBSENSOR_FEMTO_MEGA_PID 0x0669 // pid of Orbbec Femto Mega Camera +#define OBSENSOR_GEMINI2L_PID 0x0673 // pid of Orbbec Gemini 2 L Camera +#define OBSENSOR_GEMINI2XL_PID 0x0671 // pid of Orbbec Gemini 2 XL Camera enum StreamType { diff --git a/modules/videoio/src/cap_obsensor/obsensor_uvc_stream_channel.cpp b/modules/videoio/src/cap_obsensor/obsensor_uvc_stream_channel.cpp index 6e8a4f653c..9145a010d2 100644 --- a/modules/videoio/src/cap_obsensor/obsensor_uvc_stream_channel.cpp +++ b/modules/videoio/src/cap_obsensor/obsensor_uvc_stream_channel.cpp @@ -46,6 +46,9 @@ const uint8_t OB_EXT_CMD7[16] = { 0x47, 0x4d, 0x04, 0x00, 0x02, 0x00, 0xfe, 0x12 const uint8_t OB_EXT_CMD8[16] = { 0x47, 0x4d, 0x04, 0x00, 0x02, 0x00, 0xfe, 0x13, 0x3f, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00 }; const uint8_t OB_EXT_CMD9[16] = { 0x47, 0x4d, 0x04, 0x00, 0x02, 0x00, 0xfa, 0x13, 0x4b, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00 }; const uint8_t OB_EXT_CMD10[16] = { 0x47, 0x4d, 0x04, 0x00, 0x02, 0x00, 0xfa, 0x13, 0x3f, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00 }; +const uint8_t OB_EXT_CMD11[16] = { 0x47, 0x4d, 0x04, 0x00, 0x02, 0x00, 0xfe, 0x13, 0x3f, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 }; +const uint8_t OB_EXT_CMD12[16] = { 0x47, 0x4d, 0x04, 0x00, 0x02, 0x00, 0xfe, 0x13, 0x3f, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00 }; +const uint8_t OB_EXT_CMD13[16] = { 0x47, 0x4d, 0x04, 0x00, 0x02, 0x00, 0xfa, 0x13, 0x4b, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 }; #if defined(HAVE_OBSENSOR_V4L2) #define fourCc2Int(a, b, c, d) \ @@ -269,14 +272,22 @@ bool IUvcStreamChannel::setProperty(int propId, const uint8_t* /*data*/, uint32_ rst &= getXu(2, &rcvData, &rcvLen); rst &= setXu(2, OB_EXT_CMD6, sizeof(OB_EXT_CMD6)); rst &= getXu(2, &rcvData, &rcvLen); - } - else if(OBSENSOR_ASTRA2_PID == devInfo_.pid ){ - rst &= setXu(2, OB_EXT_CMD10, sizeof(OB_EXT_CMD8)); + }else if(OBSENSOR_ASTRA2_PID == devInfo_.pid ){ + rst &= setXu(2, OB_EXT_CMD12, sizeof(OB_EXT_CMD12)); rst &= getXu(2, &rcvData, &rcvLen); rst &= setXu(2, OB_EXT_CMD6, sizeof(OB_EXT_CMD6)); rst &= getXu(2, &rcvData, &rcvLen); - } - else{ + }else if(OBSENSOR_GEMINI2L_PID == devInfo_.pid){ + rst &= setXu(2, OB_EXT_CMD11, sizeof(OB_EXT_CMD11)); + rst &= getXu(2, &rcvData, &rcvLen); + rst &= setXu(2, OB_EXT_CMD6, sizeof(OB_EXT_CMD6)); + rst &= getXu(2, &rcvData, &rcvLen); + }else if(OBSENSOR_GEMINI2XL_PID == devInfo_.pid){ + rst &= setXu(2, OB_EXT_CMD11, sizeof(OB_EXT_CMD11)); + rst &= getXu(2, &rcvData, &rcvLen); + rst &= setXu(2, OB_EXT_CMD6, sizeof(OB_EXT_CMD6)); + rst &= getXu(2, &rcvData, &rcvLen); + }else{ rst &= setXu(2, OB_EXT_CMD0, sizeof(OB_EXT_CMD0)); rst &= getXu(2, &rcvData, &rcvLen); rst &= setXu(2, OB_EXT_CMD1, sizeof(OB_EXT_CMD1)); @@ -319,6 +330,40 @@ bool IUvcStreamChannel::getProperty(int propId, uint8_t* recvData, uint32_t* rec param.p7[1] = 480; *recvDataSize = sizeof(CameraParam); memcpy(recvData, ¶m, *recvDataSize); + }else if(OBSENSOR_GEMINI2L_PID == devInfo_.pid){ + // return default param + CameraParam param; + param.p0[0] = 688.87f; + param.p0[1] = 688.922f; + param.p0[2] = 644.317f; + param.p0[3] = 354.382f; + param.p1[0] = 688.87f; + param.p1[1] = 688.922f; + param.p1[2] = 644.317f; + param.p1[3] = 354.382f; + param.p6[0] = 1280; + param.p6[1] = 720; + param.p7[0] = 1280; + param.p7[1] = 720; + *recvDataSize = sizeof(CameraParam); + memcpy(recvData, ¶m, *recvDataSize); + }else if(OBSENSOR_GEMINI2XL_PID == devInfo_.pid){ + // return default param + CameraParam param; + param.p0[0] = 610.847f; + param.p0[1] = 610.829f; + param.p0[2] = 640.647f; + param.p0[3] = 401.817f; + param.p1[0] = 610.847f; + param.p1[1] = 610.829f; + param.p1[2] = 640.647f; + param.p1[3] = 401.817f; + param.p6[0] = 640; + param.p6[1] = 480; + param.p7[0] = 640; + param.p7[1] = 480; + *recvDataSize = sizeof(CameraParam); + memcpy(recvData, ¶m, *recvDataSize); } else if(OBSENSOR_ASTRA2_PID == devInfo_.pid){ // return default param @@ -376,7 +421,7 @@ bool IUvcStreamChannel::getProperty(int propId, uint8_t* recvData, uint32_t* rec bool IUvcStreamChannel::initDepthFrameProcessor() { - if(OBSENSOR_GEMINI2_PID == devInfo_.pid || OBSENSOR_ASTRA2_PID == devInfo_.pid){ + if(OBSENSOR_GEMINI2_PID == devInfo_.pid || OBSENSOR_ASTRA2_PID == devInfo_.pid || OBSENSOR_GEMINI2L_PID == devInfo_.pid){ uint8_t* rcvData; uint32_t rcvLen; @@ -387,6 +432,17 @@ bool IUvcStreamChannel::initDepthFrameProcessor() getXu(2, &rcvData, &rcvLen); depthFrameProcessor_ = makePtr(); + return true; + }else if(OBSENSOR_GEMINI2XL_PID == devInfo_.pid){ + uint8_t* rcvData; + uint32_t rcvLen; + + setXu(2, OB_EXT_CMD7, sizeof(OB_EXT_CMD7)); + getXu(2, &rcvData, &rcvLen); + + setXu(2, OB_EXT_CMD13, sizeof(OB_EXT_CMD13)); + getXu(2, &rcvData, &rcvLen); + return true; } else if (streamType_ == OBSENSOR_STREAM_DEPTH && setXu(2, OB_EXT_CMD4, sizeof(OB_EXT_CMD4))) diff --git a/modules/videoio/src/cap_obsensor_capture.cpp b/modules/videoio/src/cap_obsensor_capture.cpp index 8138f09333..21db152fc3 100644 --- a/modules/videoio/src/cap_obsensor_capture.cpp +++ b/modules/videoio/src/cap_obsensor_capture.cpp @@ -35,9 +35,14 @@ VideoCapture_obsensor::VideoCapture_obsensor(int index) : isOpened_(false) static const obsensor::StreamProfile colorProfile = { 640, 480, 30, obsensor::FRAME_FORMAT_MJPG }; static const obsensor::StreamProfile depthProfile = {640, 480, 30, obsensor::FRAME_FORMAT_Y16}; static const obsensor::StreamProfile gemini2DepthProfile = {1280, 800, 30, obsensor::FRAME_FORMAT_Y14}; - static const obsensor::StreamProfile astra2DepthProfile = {640, 480, 30, obsensor::FRAME_FORMAT_Y14}; + static const obsensor::StreamProfile astra2ColorProfile = {800, 600, 30, obsensor::FRAME_FORMAT_MJPG}; + static const obsensor::StreamProfile astra2DepthProfile = {800, 600, 30, obsensor::FRAME_FORMAT_Y14}; static const obsensor::StreamProfile megaColorProfile = {1280, 720, 30, obsensor::FRAME_FORMAT_MJPG}; static const obsensor::StreamProfile megaDepthProfile = {640, 576, 30, obsensor::FRAME_FORMAT_Y16}; + static const obsensor::StreamProfile gemini2lColorProfile = { 1280, 720, 30, obsensor::FRAME_FORMAT_MJPG}; + static const obsensor::StreamProfile gemini2lDepthProfile = {1280, 800, 30, obsensor::FRAME_FORMAT_Y14}; + static const obsensor::StreamProfile gemini2XlColorProfile = { 1280, 800, 10, obsensor::FRAME_FORMAT_MJPG}; + static const obsensor::StreamProfile gemini2XlDepthProfile = {1280, 800, 10, obsensor::FRAME_FORMAT_Y16}; streamChannelGroup_ = obsensor::getStreamChannelGroup(index); if (!streamChannelGroup_.empty()) @@ -52,6 +57,12 @@ VideoCapture_obsensor::VideoCapture_obsensor(int index) : isOpened_(false) auto profile = colorProfile; if(OBSENSOR_FEMTO_MEGA_PID == channel->getPid()){ profile = megaColorProfile; + }else if(OBSENSOR_GEMINI2L_PID == channel->getPid()){ + profile = gemini2lColorProfile; + }else if(OBSENSOR_ASTRA2_PID == channel->getPid()){ + profile = astra2ColorProfile; + }else if(OBSENSOR_GEMINI2XL_PID == channel->getPid()){ + profile = gemini2XlColorProfile; } channel->start(profile, [&](obsensor::Frame* frame) { std::unique_lock lk(frameMutex_); @@ -74,8 +85,11 @@ VideoCapture_obsensor::VideoCapture_obsensor(int index) : isOpened_(false) } else if(OBSENSOR_FEMTO_MEGA_PID == channel->getPid()){ profile = megaDepthProfile; + }else if(OBSENSOR_GEMINI2L_PID == channel->getPid()){ + profile = gemini2lDepthProfile; + }else if(OBSENSOR_GEMINI2XL_PID == channel->getPid()){ + profile = gemini2XlDepthProfile; } - channel->start(profile, [&](obsensor::Frame* frame) { std::unique_lock lk(frameMutex_); depthFrame_ = Mat(frame->height, frame->width, CV_16UC1, frame->data, frame->width * 2).clone(); @@ -140,8 +154,13 @@ bool VideoCapture_obsensor::retrieveFrame(int outputType, OutputArray frame) else if(OBSENSOR_FEMTO_MEGA_PID == streamChannelGroup_.front()->getPid()){ Rect rect(0, 0, 640, 360); grabbedDepthFrame_(rect).copyTo(frame); - } - else{ + }else if(OBSENSOR_GEMINI2L_PID == streamChannelGroup_.front()->getPid()){ + grabbedDepthFrame_ = grabbedDepthFrame_*0.8; + Rect rect(0, 40, 1280, 720); + grabbedDepthFrame_(rect).copyTo(frame); + }else if(OBSENSOR_GEMINI2XL_PID == streamChannelGroup_.front()->getPid()){ + grabbedDepthFrame_.copyTo(frame); + }else{ grabbedDepthFrame_.copyTo(frame); } grabbedDepthFrame_.release(); diff --git a/modules/videoio/src/cap_openni2.cpp b/modules/videoio/src/cap_openni2.cpp index d4b3ae4153..034509cdf7 100644 --- a/modules/videoio/src/cap_openni2.cpp +++ b/modules/videoio/src/cap_openni2.cpp @@ -150,7 +150,7 @@ protected: IplImage* retrieveIrImage(); void toggleStream(int stream, bool toggle); - void readCamerasParams(); + bool readCamerasParams(); double getDepthGeneratorProperty(int propIdx) const; bool setDepthGeneratorProperty(int propIdx, double propVal); @@ -396,13 +396,14 @@ void CvCapture_OpenNI2::toggleStream(int stream, bool toggle) } -void CvCapture_OpenNI2::readCamerasParams() +bool CvCapture_OpenNI2::readCamerasParams() { double pixelSize = 0; if (streams[CV_DEPTH_STREAM].getProperty(XN_STREAM_PROPERTY_ZERO_PLANE_PIXEL_SIZE, &pixelSize) != openni::STATUS_OK) { - CV_Error(CV_StsError, "CvCapture_OpenNI2::readCamerasParams : Could not read pixel size!" + - std::string(openni::OpenNI::getExtendedError())); + CV_LOG_ERROR(NULL, "CvCapture_OpenNI2::readCamerasParams : Could not read pixel size!" + + std::string(openni::OpenNI::getExtendedError())); + return false; } // pixel size @ VGA = pixel size @ SXGA x 2 @@ -412,14 +413,16 @@ void CvCapture_OpenNI2::readCamerasParams() unsigned long long zeroPlaneDistance; // in mm if (streams[CV_DEPTH_STREAM].getProperty(XN_STREAM_PROPERTY_ZERO_PLANE_DISTANCE, &zeroPlaneDistance) != openni::STATUS_OK) { - CV_Error(CV_StsError, "CvCapture_OpenNI2::readCamerasParams : Could not read virtual plane distance!" + - std::string(openni::OpenNI::getExtendedError())); + CV_LOG_ERROR(NULL, "CvCapture_OpenNI2::readCamerasParams : Could not read virtual plane distance!" + + std::string(openni::OpenNI::getExtendedError())); + return false; } if (streams[CV_DEPTH_STREAM].getProperty(XN_STREAM_PROPERTY_EMITTER_DCMOS_DISTANCE, &baseline) != openni::STATUS_OK) { - CV_Error(CV_StsError, "CvCapture_OpenNI2::readCamerasParams : Could not read base line!" + - std::string(openni::OpenNI::getExtendedError())); + CV_LOG_ERROR(NULL, "CvCapture_OpenNI2::readCamerasParams : Could not read base line!" + + std::string(openni::OpenNI::getExtendedError())); + return false; } // baseline from cm -> mm @@ -427,6 +430,8 @@ void CvCapture_OpenNI2::readCamerasParams() // focal length from mm -> pixels (valid for 640x480) depthFocalLength_VGA = (int)((double)zeroPlaneDistance / (double)pixelSize); + + return true; } double CvCapture_OpenNI2::getProperty( int propIdx ) const @@ -513,7 +518,7 @@ double CvCapture_OpenNI2::getCommonProperty( int propIdx ) const break; } default : - CV_Error( CV_StsBadArg, cv::format("Such parameter (propIdx=%d) isn't supported for getting.", propIdx) ); + CV_LOG_WARNING( NULL, cv::format("Such parameter (propIdx=%d) isn't supported for getting.", propIdx) ); } return propValue; @@ -551,7 +556,7 @@ bool CvCapture_OpenNI2::setCommonProperty( int propIdx, double propValue ) break; default: - CV_Error(CV_StsBadArg, cv::format("Such parameter (propIdx=%d) isn't supported for setting.", propIdx)); + CV_LOG_WARNING(NULL, cv::format("Such parameter (propIdx=%d) isn't supported for setting.", propIdx)); } return isSet; @@ -585,12 +590,14 @@ double CvCapture_OpenNI2::getDepthGeneratorProperty( int propIdx ) const break; case CV_CAP_PROP_OPENNI_BASELINE : if(baseline <= 0) - const_cast(this)->readCamerasParams(); + if (!const_cast(this)->readCamerasParams()) + return 0; propValue = baseline; break; case CV_CAP_PROP_OPENNI_FOCAL_LENGTH : if(depthFocalLength_VGA <= 0) - const_cast(this)->readCamerasParams(); + if (!const_cast(this)->readCamerasParams()) + return 0; propValue = (double)depthFocalLength_VGA; break; case CV_CAP_PROP_OPENNI_REGISTRATION : @@ -603,7 +610,7 @@ double CvCapture_OpenNI2::getDepthGeneratorProperty( int propIdx ) const propValue = streamFrames[CV_DEPTH_STREAM].getFrameIndex(); break; default : - CV_Error( CV_StsBadArg, cv::format("Depth generator does not support such parameter (propIdx=%d) for getting.", propIdx) ); + CV_LOG_WARNING( NULL, cv::format("Depth generator does not support such parameter (propIdx=%d) for getting.", propIdx) ); } return propValue; @@ -638,13 +645,17 @@ bool CvCapture_OpenNI2::setDepthGeneratorProperty( int propIdx, double propValue { openni::Status status = device.setImageRegistrationMode(mode); if( status != openni::STATUS_OK ) - CV_Error(CV_StsError, std::string("CvCapture_OpenNI2::setDepthGeneratorProperty: ") + - std::string(openni::OpenNI::getExtendedError())); + { + CV_LOG_ERROR(NULL, std::string("CvCapture_OpenNI2::setDepthGeneratorProperty: ") + + std::string(openni::OpenNI::getExtendedError())); + } else isSet = true; } else - CV_Error(CV_StsError, "CvCapture_OpenNI2::setDepthGeneratorProperty: Unsupported viewpoint."); + { + CV_LOG_ERROR(NULL, "CvCapture_OpenNI2::setDepthGeneratorProperty: Unsupported viewpoint."); + } } else isSet = true; @@ -654,15 +665,17 @@ bool CvCapture_OpenNI2::setDepthGeneratorProperty( int propIdx, double propValue { openni::Status status = device.setImageRegistrationMode(openni::IMAGE_REGISTRATION_OFF); if( status != openni::STATUS_OK ) - CV_Error(CV_StsError, std::string("CvCapture_OpenNI2::setDepthGeneratorProperty: ") + - std::string(openni::OpenNI::getExtendedError())); + { + CV_LOG_ERROR(NULL, std::string("CvCapture_OpenNI2::setDepthGeneratorProperty: ") + + std::string(openni::OpenNI::getExtendedError())); + } else isSet = true; } } break; default: - CV_Error( CV_StsBadArg, cv::format("Depth generator does not support such parameter (propIdx=%d) for setting.", propIdx) ); + CV_LOG_WARNING( NULL, cv::format("OpenNI2: Depth generator does not support such parameter (propIdx=%d) for setting.", propIdx) ); } return isSet; @@ -696,7 +709,7 @@ double CvCapture_OpenNI2::getImageGeneratorProperty( int propIdx ) const propValue = (double)streamFrames[CV_COLOR_STREAM].getFrameIndex(); break; default : - CV_Error( CV_StsBadArg, cv::format("Image generator does not support such parameter (propIdx=%d) for getting.", propIdx) ); + CV_LOG_WARNING( NULL, cv::format("OpenNI2: Image generator does not support such parameter (propIdx=%d) for getting.", propIdx) ); } return propValue; @@ -744,19 +757,22 @@ bool CvCapture_OpenNI2::setImageGeneratorProperty(int propIdx, double propValue) mode.setFps(60); break; default : - CV_Error( CV_StsBadArg, "Unsupported image generator output mode."); + CV_LOG_WARNING( NULL, "Unsupported image generator output mode."); + return false; } openni::Status status = streams[CV_COLOR_STREAM].setVideoMode( mode ); if( status != openni::STATUS_OK ) - CV_Error(CV_StsError, std::string("CvCapture_OpenNI2::setImageGeneratorProperty: ") + - std::string(openni::OpenNI::getExtendedError())); + { + CV_LOG_ERROR(NULL, std::string("CvCapture_OpenNI2::setImageGeneratorProperty: ") + + std::string(openni::OpenNI::getExtendedError())); + } else isSet = true; break; } default: - CV_Error( CV_StsBadArg, cv::format("Image generator does not support such parameter (propIdx=%d) for setting.", propIdx) ); + CV_LOG_WARNING( NULL, cv::format("Image generator does not support such parameter (propIdx=%d) for setting.", propIdx) ); } return isSet; @@ -790,7 +806,7 @@ double CvCapture_OpenNI2::getIrGeneratorProperty(int propIdx) const propValue = (double)streamFrames[CV_IR_STREAM].getFrameIndex(); break; default: - CV_Error(CV_StsBadArg, cv::format("Image generator does not support such parameter (propIdx=%d) for getting.", propIdx)); + CV_LOG_WARNING(NULL, cv::format("Image generator does not support such parameter (propIdx=%d) for getting.", propIdx)); } return propValue; @@ -838,19 +854,21 @@ bool CvCapture_OpenNI2::setIrGeneratorProperty(int propIdx, double propValue) mode.setFps(60); break; default: - CV_Error(CV_StsBadArg, "Unsupported image generator output mode."); + CV_LOG_WARNING(NULL, "Unsupported image generator output mode."); } openni::Status status = streams[CV_IR_STREAM].setVideoMode(mode); if (status != openni::STATUS_OK) - CV_Error(CV_StsError, std::string("CvCapture_OpenNI2::setImageGeneratorProperty: ") + - std::string(openni::OpenNI::getExtendedError())); + { + CV_LOG_ERROR(NULL, std::string("CvCapture_OpenNI2::setImageGeneratorProperty: ") + + std::string(openni::OpenNI::getExtendedError())); + } else isSet = true; break; } default: - CV_Error(CV_StsBadArg, cv::format("Image generator does not support such parameter (propIdx=%d) for setting.", propIdx)); + CV_LOG_WARNING(NULL, cv::format("Image generator does not support such parameter (propIdx=%d) for setting.", propIdx)); } return isSet; @@ -965,9 +983,10 @@ static void computeDisparity_32F( const openni::VideoFrameRef& depthMetaData, cv IplImage* CvCapture_OpenNI2::retrieveDisparityMap() { if (!streamFrames[CV_DEPTH_STREAM].isValid()) - return 0; + return nullptr; - readCamerasParams(); + if (!readCamerasParams()) + return nullptr; cv::Mat disp32; computeDisparity_32F(streamFrames[CV_DEPTH_STREAM], disp32, baseline, depthFocalLength_VGA, noSampleValue, shadowValue); @@ -980,9 +999,10 @@ IplImage* CvCapture_OpenNI2::retrieveDisparityMap() IplImage* CvCapture_OpenNI2::retrieveDisparityMap_32F() { if (!streamFrames[CV_DEPTH_STREAM].isValid()) - return 0; + return nullptr; - readCamerasParams(); + if (!readCamerasParams()) + return nullptr; computeDisparity_32F(streamFrames[CV_DEPTH_STREAM], outputMaps[CV_CAP_OPENNI_DISPARITY_MAP_32F].mat, baseline, depthFocalLength_VGA, noSampleValue, shadowValue); @@ -992,7 +1012,7 @@ IplImage* CvCapture_OpenNI2::retrieveDisparityMap_32F() IplImage* CvCapture_OpenNI2::retrieveValidDepthMask() { if (!streamFrames[CV_DEPTH_STREAM].isValid()) - return 0; + return nullptr; cv::Mat d; getDepthMapFromMetaData(streamFrames[CV_DEPTH_STREAM], d, noSampleValue, shadowValue); diff --git a/modules/videoio/src/container_avi.cpp b/modules/videoio/src/container_avi.cpp index 3223e77090..4aed7c888c 100644 --- a/modules/videoio/src/container_avi.cpp +++ b/modules/videoio/src/container_avi.cpp @@ -3,6 +3,7 @@ // of this distribution and at http://opencv.org/license.html. #include "opencv2/videoio/container_avi.private.hpp" +#include #include #include #include @@ -645,6 +646,11 @@ bool BitStream::open(const String& filename) { close(); output.open(filename.c_str(), std::ios_base::binary); + if (!output.is_open()) + { + CV_LOG_DEBUG(NULL, cv::format("Failed to open stream for writing to \"%s\"", filename.c_str())); + return false; + } m_current = m_start; m_pos = 0; return true; diff --git a/modules/videoio/test/test_audio.cpp b/modules/videoio/test/test_audio.cpp index 0a1abd43ad..bbe1d526bc 100644 --- a/modules/videoio/test/test_audio.cpp +++ b/modules/videoio/test/test_audio.cpp @@ -186,6 +186,7 @@ public: double audio_shift = cap.get(CAP_PROP_AUDIO_SHIFT_NSEC); double video0_timestamp = cap.get(CAP_PROP_POS_MSEC) * 1e-3; audio0_timestamp = video0_timestamp + audio_shift * 1e-9; + std::cout << "video0 timestamp: " << video0_timestamp << " audio0 timestamp: " << audio0_timestamp << " (audio shift nanoseconds: " << audio_shift << " , seconds: " << audio_shift * 1e-9 << ")" << std::endl; } ASSERT_TRUE(cap.retrieve(videoFrame)); @@ -230,7 +231,7 @@ public: EXPECT_NEAR( cap.get(CAP_PROP_AUDIO_POS) / samplePerSecond + audio0_timestamp, cap.get(CAP_PROP_POS_MSEC) * 1e-3, - (1.0 / fps) * 0.3) + (1.0 / fps) * 0.6) << "CAP_PROP_AUDIO_POS=" << cap.get(CAP_PROP_AUDIO_POS) << " CAP_PROP_POS_MSEC=" << cap.get(CAP_PROP_POS_MSEC); } if (frame != 0 && frame != numberOfFrames-1 && audioData[0].size() != (size_t)numberOfSamples) diff --git a/modules/videoio/test/test_ffmpeg.cpp b/modules/videoio/test/test_ffmpeg.cpp index 9f59480c9c..7e09d61729 100644 --- a/modules/videoio/test/test_ffmpeg.cpp +++ b/modules/videoio/test/test_ffmpeg.cpp @@ -240,9 +240,6 @@ typedef testing::TestWithParam< videoio_encapsulate_params_t > videoio_encapsula TEST_P(videoio_encapsulate, write) { -#ifdef _WIN32 - throw SkipTestException("Test disabled until PR for raw video encapsulation is merged and windows dll is updated"); -#else const VideoCaptureAPIs api = CAP_FFMPEG; if (!videoio_registry::hasBackend(api)) throw SkipTestException("FFmpeg backend was not found"); @@ -296,7 +293,7 @@ TEST_P(videoio_encapsulate, write) ASSERT_TRUE(capActualRaw.isOpened()); const double fpsReference = capReference.get(CAP_PROP_FPS); const double fpsActual = capActual.get(CAP_PROP_FPS); - ASSERT_EQ(fpsReference, fpsActual); + ASSERT_NEAR(fpsReference, fpsActual, 1e-2); const int nFramesActual = static_cast(capActual.get(CAP_PROP_FRAME_COUNT)); ASSERT_EQ(nFrames, nFramesActual); @@ -316,7 +313,6 @@ TEST_P(videoio_encapsulate, write) } ASSERT_EQ(0, remove(fileNameOut.c_str())); -#endif } const videoio_encapsulate_params_t videoio_encapsulate_params[] = @@ -351,9 +347,6 @@ INSTANTIATE_TEST_CASE_P(/**/, videoio_encapsulate, testing::ValuesIn(videoio_enc TEST(videoio_encapsulate_set_idr, write) { -#ifdef _WIN32 - throw SkipTestException("Test disabled until PR for raw video encapsulation is merged and windows dll is updated"); -#else const VideoCaptureAPIs api = CAP_FFMPEG; if (!videoio_registry::hasBackend(api)) throw SkipTestException("FFmpeg backend was not found"); @@ -390,7 +383,7 @@ TEST(videoio_encapsulate_set_idr, write) memcpy(rawFrame.data, extraData.data, extraData.total()); memcpy(rawFrame.data + extraData.total(), tmp.data, tmp.total()); } - if (static_cast(capRaw.get(CAP_PROP_LRF_HAS_KEY_FRAME))) + if (capRaw.get(CAP_PROP_LRF_HAS_KEY_FRAME) != 0) container.set(VideoWriterProperties::VIDEOWRITER_PROP_KEY_FLAG, 1); else container.set(VideoWriterProperties::VIDEOWRITER_PROP_KEY_FLAG, 0); @@ -432,7 +425,6 @@ TEST(videoio_encapsulate_set_idr, write) } ASSERT_EQ(0, remove(fileNameOut.c_str())); -#endif } typedef tuple videoio_skip_params_t; @@ -677,7 +669,6 @@ static void ffmpeg_check_read_raw(VideoCapture& cap) EXPECT_TRUE(data.rows == 1 || data.cols == 1) << data.size; EXPECT_EQ((size_t)37118, data.total()); -#ifndef WIN32 // 12 is the nearset key frame to frame 18 EXPECT_TRUE(cap.set(CAP_PROP_POS_FRAMES, 18.)); EXPECT_EQ(cap.get(CAP_PROP_POS_FRAMES), 12.); @@ -685,7 +676,6 @@ static void ffmpeg_check_read_raw(VideoCapture& cap) EXPECT_EQ(CV_8UC1, data.type()) << "CV_8UC1 != " << typeToString(data.type()); EXPECT_TRUE(data.rows == 1 || data.cols == 1) << data.size; EXPECT_EQ((size_t)8726, data.total()); -#endif } TEST(videoio_ffmpeg, ffmpeg_check_extra_data) @@ -719,9 +709,7 @@ TEST(videoio_ffmpeg, open_with_property) // confirm properties are returned without initializing AVCodecContext EXPECT_EQ(cap.get(CAP_PROP_FORMAT), -1); EXPECT_EQ(static_cast(cap.get(CAP_PROP_FOURCC)), fourccFromString("FMP4")); -#ifndef WIN32 EXPECT_EQ(cap.get(CAP_PROP_N_THREADS), 0.0); -#endif EXPECT_EQ(cap.get(CAP_PROP_FRAME_HEIGHT), 384.0); EXPECT_EQ(cap.get(CAP_PROP_FRAME_WIDTH), 672.0); EXPECT_EQ(cap.get(CAP_PROP_FRAME_COUNT), 125); @@ -742,9 +730,7 @@ TEST(videoio_ffmpeg, create_with_property) // confirm properties are returned without initializing AVCodecContext EXPECT_TRUE(cap.get(CAP_PROP_FORMAT) == -1); EXPECT_EQ(static_cast(cap.get(CAP_PROP_FOURCC)), fourccFromString("FMP4")); -#ifndef WIN32 EXPECT_EQ(cap.get(CAP_PROP_N_THREADS), 0.0); -#endif EXPECT_EQ(cap.get(CAP_PROP_FRAME_HEIGHT), 384.0); EXPECT_EQ(cap.get(CAP_PROP_FRAME_WIDTH), 672.0); EXPECT_EQ(cap.get(CAP_PROP_FRAME_COUNT), 125); diff --git a/modules/videoio/test/test_gstreamer.cpp b/modules/videoio/test/test_gstreamer.cpp index a8c24be438..ef9a6765a8 100644 --- a/modules/videoio/test/test_gstreamer.cpp +++ b/modules/videoio/test/test_gstreamer.cpp @@ -178,4 +178,47 @@ TEST(videoio_gstreamer, timeout_property) } } +//============================================================================== +// Seeking test with manual GStreamer pipeline +typedef testing::TestWithParam gstreamer_bunny; + +TEST_P(gstreamer_bunny, manual_seek) +{ + if (!videoio_registry::hasBackend(CAP_GSTREAMER)) + throw SkipTestException("GStreamer backend was not found"); + + const string video_file = BunnyParameters::getFilename("." + GetParam()); + const string pipeline = "filesrc location=" + video_file + " ! decodebin ! videoconvert ! video/x-raw, format=BGR ! appsink drop=1"; + const double target_pos = 3000.0; + const double ms_per_frame = 1000.0 / BunnyParameters::getFps(); + VideoCapture cap; + cap.open(pipeline, CAP_GSTREAMER); + ASSERT_TRUE(cap.isOpened()); + Mat img; + for (int i = 0; i < 10; i++) + { + cap >> img; + } + EXPECT_FALSE(img.empty()); + cap.set(CAP_PROP_POS_MSEC, target_pos); + cap >> img; + EXPECT_FALSE(img.empty()); + double actual_pos = cap.get(CAP_PROP_POS_MSEC); + EXPECT_NEAR(actual_pos, target_pos, ms_per_frame); +} + +static const string bunny_params[] = { + // string("wmv"), + string("mov"), + string("mp4"), + // string("mpg"), + string("avi"), + // string("h264"), + // string("h265"), + string("mjpg.avi") +}; + +INSTANTIATE_TEST_CASE_P(videoio, gstreamer_bunny, testing::ValuesIn(bunny_params)); + + }} // namespace diff --git a/modules/videoio/test/test_video_io.cpp b/modules/videoio/test/test_video_io.cpp index 765efb0812..370531770f 100644 --- a/modules/videoio/test/test_video_io.cpp +++ b/modules/videoio/test/test_video_io.cpp @@ -67,6 +67,11 @@ public: std::cout << "CAP_PROP_FRAME_COUNT is not supported by backend. Assume 50 frames." << std::endl; n_frames = 50; } + // GStreamer can't read frame count of big_buck_bunny.wmv + if (apiPref == CAP_GSTREAMER && ext == "wmv") + { + n_frames = 125; + } { SCOPED_TRACE("consecutive read"); @@ -166,7 +171,7 @@ public: EXPECT_NO_THROW(count_prop = (int)cap.get(CAP_PROP_FRAME_COUNT)); // mpg file reports 5.08 sec * 24 fps => property returns 122 frames // but actual number of frames returned is 125 - if (ext != "mpg") + if (ext != "mpg" && !(apiPref == CAP_GSTREAMER && ext == "wmv")) { if (count_prop > 0) { @@ -200,12 +205,11 @@ public: if (!isBackendAvailable(apiPref, cv::videoio_registry::getStreamBackends())) throw SkipTestException(cv::String("Backend is not available/disabled: ") + cv::videoio_registry::getBackendName(apiPref)); - // GStreamer: https://github.com/opencv/opencv/issues/19025 - if (apiPref == CAP_GSTREAMER) + if (((apiPref == CAP_GSTREAMER) && (ext == "avi"))) throw SkipTestException(cv::String("Backend ") + cv::videoio_registry::getBackendName(apiPref) + - cv::String(" does not return reliable values for CAP_PROP_POS_MSEC property")); + cv::String(" does not support CAP_PROP_POS_MSEC option")); - if (((apiPref == CAP_FFMPEG) && ((ext == "h264") || (ext == "h265")))) + if (((apiPref == CAP_FFMPEG || apiPref == CAP_GSTREAMER) && ((ext == "h264") || (ext == "h265")))) throw SkipTestException(cv::String("Backend ") + cv::videoio_registry::getBackendName(apiPref) + cv::String(" does not support CAP_PROP_POS_MSEC option")); @@ -221,8 +225,8 @@ public: // mpg file reports 5.08 sec * 24 fps => property returns 122 frames,but actual number of frames returned is 125 // HACK: CAP_PROP_FRAME_COUNT is not supported for vmw + MSMF. Just force check for all 125 frames if (ext == "mpg") - EXPECT_GT(frame_count, 121); - else if ((ext == "wmv") && (apiPref == CAP_MSMF)) + EXPECT_GE(frame_count, 114); + else if ((ext == "wmv") && (apiPref == CAP_MSMF || apiPref == CAP_GSTREAMER)) frame_count = 125; else EXPECT_EQ(frame_count, 125); @@ -240,6 +244,9 @@ public: if (cvtest::debugLevel > 0) std::cout << "i = " << i << ": timestamp = " << timestamp << std::endl; const double frame_period = 1000.f/bunny_param.getFps(); + // big_buck_bunny.mpg starts at 0.500 msec + if ((ext == "mpg") && (apiPref == CAP_GSTREAMER)) + timestamp -= 500.0; // NOTE: eps == frame_period, because videoCapture returns frame beginning timestamp or frame end // timestamp depending on codec and back-end. So the first frame has timestamp 0 or frame_period. EXPECT_NEAR(timestamp, i*frame_period, frame_period) << "i=" << i; diff --git a/modules/world/CMakeLists.txt b/modules/world/CMakeLists.txt index b14378599e..b6a0fedde1 100644 --- a/modules/world/CMakeLists.txt +++ b/modules/world/CMakeLists.txt @@ -22,6 +22,24 @@ if(NOT OPENCV_INITIAL_PASS) set(ENABLE_PRECOMPILED_HEADERS OFF CACHE INTERNAL "" FORCE) project(opencv_world) + # MSVS 2014 (vc14): LINK : fatal error LNK1210: exceeded internal ILK size limit; link with /INCREMENTAL:NO + if(MSVC AND MSVC_VERSION EQUAL 1900) + foreach(flag_var + CMAKE_EXE_LINKER_FLAGS_DEBUG + CMAKE_EXE_LINKER_FLAGS_RELWITHDEBINFO + CMAKE_MODULE_LINKER_FLAGS_DEBUG + CMAKE_MODULE_LINKER_FLAGS_RELWITHDEBINFO + CMAKE_SHARED_LINKER_FLAGS_DEBUG + CMAKE_SHARED_LINKER_FLAGS_RELWITHDEBINFO + ) + if(${flag_var} MATCHES "/INCREMENTAL") + string(REGEX REPLACE "/INCREMENTAL[^ ]*" "/INCREMENTAL:NO" ${flag_var} "${${flag_var}}") + else() + set(${flag_var} "${${flag_var}} /INCREMENTAL:NO*") + endif() + endforeach(flag_var) + endif() + message(STATUS "Processing WORLD modules...") foreach(m ${OPENCV_MODULES_BUILD}) set(the_module ${m}) diff --git a/platforms/android/aar-template/OpenCV/build.gradle.template b/platforms/android/aar-template/OpenCV/build.gradle.template new file mode 100644 index 0000000000..4f3a3846ec --- /dev/null +++ b/platforms/android/aar-template/OpenCV/build.gradle.template @@ -0,0 +1,88 @@ +plugins { + id 'com.android.library' + id 'maven-publish' +} + +android { + namespace 'org.opencv' + compileSdk ${COMPILE_SDK} + + defaultConfig { + minSdk ${MIN_SDK} + targetSdk ${TARGET_SDK} + + testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner" + externalNativeBuild { + cmake { + cppFlags "" + arguments "-DANDROID_STL=${LIB_TYPE}" + } + } + ndk { + abiFilters ${ABI_FILTERS} + } + } + + buildTypes { + release { + minifyEnabled false + proguardFiles getDefaultProguardFile('proguard-android-optimize.txt'), 'proguard-rules.pro' + } + } + compileOptions { + sourceCompatibility JavaVersion.VERSION_${JAVA_VERSION} + targetCompatibility JavaVersion.VERSION_${JAVA_VERSION} + } + externalNativeBuild { + cmake { + path file('src/main/cpp/CMakeLists.txt') + } + } + buildFeatures { + aidl true + prefabPublishing true + buildConfig true + } + prefab { + ${LIB_NAME} { + headers "src/main/cpp/include" + } + } + sourceSets { + main { + java.srcDirs = ['src/main/java'] + //jniLibs.srcDirs = ['libs'] + aidl.srcDirs = ['src/main/java'] + } + } + + publishing { + singleVariant('release') { + withSourcesJar() + } + } +} + +publishing { + publications { + release(MavenPublication) { + groupId = 'org.opencv' + artifactId = '${PACKAGE_NAME}' + version = '${OPENCV_VERSION}' + artifact("opencv-release.aar") + +// afterEvaluate { +// from components.release +// } + } + } + repositories { + maven { + name = 'myrepo' + url = "${project.buildDir}/repo" + } + } +} + +dependencies { +} \ No newline at end of file diff --git a/platforms/android/aar-template/OpenCV/proguard-rules.pro b/platforms/android/aar-template/OpenCV/proguard-rules.pro new file mode 100644 index 0000000000..481bb43481 --- /dev/null +++ b/platforms/android/aar-template/OpenCV/proguard-rules.pro @@ -0,0 +1,21 @@ +# Add project specific ProGuard rules here. +# You can control the set of applied configuration files using the +# proguardFiles setting in build.gradle. +# +# For more details, see +# http://developer.android.com/guide/developing/tools/proguard.html + +# If your project uses WebView with JS, uncomment the following +# and specify the fully qualified class name to the JavaScript interface +# class: +#-keepclassmembers class fqcn.of.javascript.interface.for.webview { +# public *; +#} + +# Uncomment this to preserve the line number information for +# debugging stack traces. +#-keepattributes SourceFile,LineNumberTable + +# If you keep the line number information, uncomment this to +# hide the original source file name. +#-renamesourcefileattribute SourceFile \ No newline at end of file diff --git a/platforms/android/aar-template/OpenCV/src/main/AndroidManifest.xml b/platforms/android/aar-template/OpenCV/src/main/AndroidManifest.xml new file mode 100644 index 0000000000..cce937ee78 --- /dev/null +++ b/platforms/android/aar-template/OpenCV/src/main/AndroidManifest.xml @@ -0,0 +1,4 @@ + + + + \ No newline at end of file diff --git a/platforms/android/aar-template/OpenCV/src/main/cpp/CMakeLists.txt.template b/platforms/android/aar-template/OpenCV/src/main/cpp/CMakeLists.txt.template new file mode 100644 index 0000000000..02ef035e0a --- /dev/null +++ b/platforms/android/aar-template/OpenCV/src/main/cpp/CMakeLists.txt.template @@ -0,0 +1,5 @@ +cmake_minimum_required(VERSION 3.6) + +project("opencv") + +add_library(${LIB_NAME} ${LIB_TYPE} native-lib.cpp) diff --git a/platforms/android/aar-template/OpenCV/src/main/cpp/include/temp.h b/platforms/android/aar-template/OpenCV/src/main/cpp/include/temp.h new file mode 100644 index 0000000000..4974d8ca96 --- /dev/null +++ b/platforms/android/aar-template/OpenCV/src/main/cpp/include/temp.h @@ -0,0 +1 @@ +// This empty .h file is used for creating an AAR with empty C++ lib that will be replaced with OpenCV C++ lib \ No newline at end of file diff --git a/platforms/android/aar-template/OpenCV/src/main/cpp/native-lib.cpp b/platforms/android/aar-template/OpenCV/src/main/cpp/native-lib.cpp new file mode 100644 index 0000000000..73ac04eb7a --- /dev/null +++ b/platforms/android/aar-template/OpenCV/src/main/cpp/native-lib.cpp @@ -0,0 +1 @@ +// This empty .cpp file is used for creating an AAR with empty C++ lib that will be replaced with OpenCV C++ lib \ No newline at end of file diff --git a/platforms/android/aar-template/README.md b/platforms/android/aar-template/README.md new file mode 100644 index 0000000000..a851364dd4 --- /dev/null +++ b/platforms/android/aar-template/README.md @@ -0,0 +1,29 @@ +## Scripts for creating an AAR package and a local Maven repository with OpenCV libraries for Android + +### How to run the scripts +1. Set JAVA_HOME and ANDROID_HOME enviroment variables. For example: +``` +export JAVA_HOME=~/Android Studio/jbr +export ANDROID_HOME=~/Android/SDK +``` +2. Download OpenCV SDK for Android +3. Run build script for version with Java and a shared C++ library: +``` +python build_java_shared_aar.py "~/opencv-4.7.0-android-sdk/OpenCV-android-sdk" +``` +4. Run build script for version with static C++ libraries: +``` +python build_static_aar.py "~/opencv-4.7.0-android-sdk/OpenCV-android-sdk" +``` +The AAR libraries and the local Maven repository will be created in the **outputs** directory +### Technical details +The scripts consist of 5 steps: +1. Preparing Android AAR library project template +2. Adding Java code to the project. Adding C++ public headers for shared version to the project. +3. Compiling the project to build an AAR package +4. Adding C++ binary libraries to the AAR package. Adding C++ public headers for static version to the AAR package. +5. Creating Maven repository with the AAR package + +There are a few minor limitations: +1. Due to the AAR design the Java + shared C++ AAR package contains duplicates of C++ binary libraries, but the final user's Android application contains only one library instance. +2. The compile definitions from cmake configs are skipped, but it shouldn't affect the library because the script uses precompiled C++ binaries from SDK. diff --git a/platforms/android/aar-template/build.gradle b/platforms/android/aar-template/build.gradle new file mode 100644 index 0000000000..decab5bc55 --- /dev/null +++ b/platforms/android/aar-template/build.gradle @@ -0,0 +1,4 @@ +// Top-level build file where you can add configuration options common to all sub-projects/modules. +plugins { + id 'com.android.library' version '7.3.0' apply false +} diff --git a/platforms/android/aar-template/gradle.properties b/platforms/android/aar-template/gradle.properties new file mode 100644 index 0000000000..3e927b11ef --- /dev/null +++ b/platforms/android/aar-template/gradle.properties @@ -0,0 +1,21 @@ +# Project-wide Gradle settings. +# IDE (e.g. Android Studio) users: +# Gradle settings configured through the IDE *will override* +# any settings specified in this file. +# For more details on how to configure your build environment visit +# http://www.gradle.org/docs/current/userguide/build_environment.html +# Specifies the JVM arguments used for the daemon process. +# The setting is particularly useful for tweaking memory settings. +org.gradle.jvmargs=-Xmx2048m -Dfile.encoding=UTF-8 +# When configured, Gradle will run in incubating parallel mode. +# This option should only be used with decoupled projects. More details, visit +# http://www.gradle.org/docs/current/userguide/multi_project_builds.html#sec:decoupled_projects +# org.gradle.parallel=true +# AndroidX package structure to make it clearer which packages are bundled with the +# Android operating system, and which are packaged with your app's APK +# https://developer.android.com/topic/libraries/support-library/androidx-rn +android.useAndroidX=true +# Enables namespacing of each library's R class so that its R class includes only the +# resources declared in the library itself and none from the library's dependencies, +# thereby reducing the size of the R class for that library +android.nonTransitiveRClass=true \ No newline at end of file diff --git a/platforms/android/aar-template/gradle/wrapper/gradle-wrapper.jar b/platforms/android/aar-template/gradle/wrapper/gradle-wrapper.jar new file mode 100644 index 0000000000..e708b1c023 Binary files /dev/null and b/platforms/android/aar-template/gradle/wrapper/gradle-wrapper.jar differ diff --git a/platforms/android/aar-template/gradle/wrapper/gradle-wrapper.properties b/platforms/android/aar-template/gradle/wrapper/gradle-wrapper.properties new file mode 100644 index 0000000000..c4f47b9a2e --- /dev/null +++ b/platforms/android/aar-template/gradle/wrapper/gradle-wrapper.properties @@ -0,0 +1,6 @@ +#Mon Jul 10 11:57:38 SGT 2023 +distributionBase=GRADLE_USER_HOME +distributionPath=wrapper/dists +distributionUrl=https\://services.gradle.org/distributions/gradle-7.6.3-bin.zip +zipStoreBase=GRADLE_USER_HOME +zipStorePath=wrapper/dists diff --git a/platforms/android/aar-template/gradlew b/platforms/android/aar-template/gradlew new file mode 100755 index 0000000000..4f906e0c81 --- /dev/null +++ b/platforms/android/aar-template/gradlew @@ -0,0 +1,185 @@ +#!/usr/bin/env sh + +# +# Copyright 2015 the original author or authors. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# https://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# + +############################################################################## +## +## Gradle start up script for UN*X +## +############################################################################## + +# Attempt to set APP_HOME +# Resolve links: $0 may be a link +PRG="$0" +# Need this for relative symlinks. +while [ -h "$PRG" ] ; do + ls=`ls -ld "$PRG"` + link=`expr "$ls" : '.*-> \(.*\)$'` + if expr "$link" : '/.*' > /dev/null; then + PRG="$link" + else + PRG=`dirname "$PRG"`"/$link" + fi +done +SAVED="`pwd`" +cd "`dirname \"$PRG\"`/" >/dev/null +APP_HOME="`pwd -P`" +cd "$SAVED" >/dev/null + +APP_NAME="Gradle" +APP_BASE_NAME=`basename "$0"` + +# Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. +DEFAULT_JVM_OPTS='"-Xmx64m" "-Xms64m"' + +# Use the maximum available, or set MAX_FD != -1 to use that value. +MAX_FD="maximum" + +warn () { + echo "$*" +} + +die () { + echo + echo "$*" + echo + exit 1 +} + +# OS specific support (must be 'true' or 'false'). +cygwin=false +msys=false +darwin=false +nonstop=false +case "`uname`" in + CYGWIN* ) + cygwin=true + ;; + Darwin* ) + darwin=true + ;; + MINGW* ) + msys=true + ;; + NONSTOP* ) + nonstop=true + ;; +esac + +CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar + + +# Determine the Java command to use to start the JVM. +if [ -n "$JAVA_HOME" ] ; then + if [ -x "$JAVA_HOME/jre/sh/java" ] ; then + # IBM's JDK on AIX uses strange locations for the executables + JAVACMD="$JAVA_HOME/jre/sh/java" + else + JAVACMD="$JAVA_HOME/bin/java" + fi + if [ ! -x "$JAVACMD" ] ; then + die "ERROR: JAVA_HOME is set to an invalid directory: $JAVA_HOME + +Please set the JAVA_HOME variable in your environment to match the +location of your Java installation." + fi +else + JAVACMD="java" + which java >/dev/null 2>&1 || die "ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. + +Please set the JAVA_HOME variable in your environment to match the +location of your Java installation." +fi + +# Increase the maximum file descriptors if we can. +if [ "$cygwin" = "false" -a "$darwin" = "false" -a "$nonstop" = "false" ] ; then + MAX_FD_LIMIT=`ulimit -H -n` + if [ $? -eq 0 ] ; then + if [ "$MAX_FD" = "maximum" -o "$MAX_FD" = "max" ] ; then + MAX_FD="$MAX_FD_LIMIT" + fi + ulimit -n $MAX_FD + if [ $? -ne 0 ] ; then + warn "Could not set maximum file descriptor limit: $MAX_FD" + fi + else + warn "Could not query maximum file descriptor limit: $MAX_FD_LIMIT" + fi +fi + +# For Darwin, add options to specify how the application appears in the dock +if $darwin; then + GRADLE_OPTS="$GRADLE_OPTS \"-Xdock:name=$APP_NAME\" \"-Xdock:icon=$APP_HOME/media/gradle.icns\"" +fi + +# For Cygwin or MSYS, switch paths to Windows format before running java +if [ "$cygwin" = "true" -o "$msys" = "true" ] ; then + APP_HOME=`cygpath --path --mixed "$APP_HOME"` + CLASSPATH=`cygpath --path --mixed "$CLASSPATH"` + + JAVACMD=`cygpath --unix "$JAVACMD"` + + # We build the pattern for arguments to be converted via cygpath + ROOTDIRSRAW=`find -L / -maxdepth 1 -mindepth 1 -type d 2>/dev/null` + SEP="" + for dir in $ROOTDIRSRAW ; do + ROOTDIRS="$ROOTDIRS$SEP$dir" + SEP="|" + done + OURCYGPATTERN="(^($ROOTDIRS))" + # Add a user-defined pattern to the cygpath arguments + if [ "$GRADLE_CYGPATTERN" != "" ] ; then + OURCYGPATTERN="$OURCYGPATTERN|($GRADLE_CYGPATTERN)" + fi + # Now convert the arguments - kludge to limit ourselves to /bin/sh + i=0 + for arg in "$@" ; do + CHECK=`echo "$arg"|egrep -c "$OURCYGPATTERN" -` + CHECK2=`echo "$arg"|egrep -c "^-"` ### Determine if an option + + if [ $CHECK -ne 0 ] && [ $CHECK2 -eq 0 ] ; then ### Added a condition + eval `echo args$i`=`cygpath --path --ignore --mixed "$arg"` + else + eval `echo args$i`="\"$arg\"" + fi + i=`expr $i + 1` + done + case $i in + 0) set -- ;; + 1) set -- "$args0" ;; + 2) set -- "$args0" "$args1" ;; + 3) set -- "$args0" "$args1" "$args2" ;; + 4) set -- "$args0" "$args1" "$args2" "$args3" ;; + 5) set -- "$args0" "$args1" "$args2" "$args3" "$args4" ;; + 6) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" ;; + 7) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" ;; + 8) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" ;; + 9) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" "$args8" ;; + esac +fi + +# Escape application args +save () { + for i do printf %s\\n "$i" | sed "s/'/'\\\\''/g;1s/^/'/;\$s/\$/' \\\\/" ; done + echo " " +} +APP_ARGS=`save "$@"` + +# Collect all arguments for the java command, following the shell quoting and substitution rules +eval set -- $DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS "\"-Dorg.gradle.appname=$APP_BASE_NAME\"" -classpath "\"$CLASSPATH\"" org.gradle.wrapper.GradleWrapperMain "$APP_ARGS" + +exec "$JAVACMD" "$@" diff --git a/platforms/android/aar-template/gradlew.bat b/platforms/android/aar-template/gradlew.bat new file mode 100644 index 0000000000..107acd32c4 --- /dev/null +++ b/platforms/android/aar-template/gradlew.bat @@ -0,0 +1,89 @@ +@rem +@rem Copyright 2015 the original author or authors. +@rem +@rem Licensed under the Apache License, Version 2.0 (the "License"); +@rem you may not use this file except in compliance with the License. +@rem You may obtain a copy of the License at +@rem +@rem https://www.apache.org/licenses/LICENSE-2.0 +@rem +@rem Unless required by applicable law or agreed to in writing, software +@rem distributed under the License is distributed on an "AS IS" BASIS, +@rem WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +@rem See the License for the specific language governing permissions and +@rem limitations under the License. +@rem + +@if "%DEBUG%" == "" @echo off +@rem ########################################################################## +@rem +@rem Gradle startup script for Windows +@rem +@rem ########################################################################## + +@rem Set local scope for the variables with windows NT shell +if "%OS%"=="Windows_NT" setlocal + +set DIRNAME=%~dp0 +if "%DIRNAME%" == "" set DIRNAME=. +set APP_BASE_NAME=%~n0 +set APP_HOME=%DIRNAME% + +@rem Resolve any "." and ".." in APP_HOME to make it shorter. +for %%i in ("%APP_HOME%") do set APP_HOME=%%~fi + +@rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. +set DEFAULT_JVM_OPTS="-Xmx64m" "-Xms64m" + +@rem Find java.exe +if defined JAVA_HOME goto findJavaFromJavaHome + +set JAVA_EXE=java.exe +%JAVA_EXE% -version >NUL 2>&1 +if "%ERRORLEVEL%" == "0" goto execute + +echo. +echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. +echo. +echo Please set the JAVA_HOME variable in your environment to match the +echo location of your Java installation. + +goto fail + +:findJavaFromJavaHome +set JAVA_HOME=%JAVA_HOME:"=% +set JAVA_EXE=%JAVA_HOME%/bin/java.exe + +if exist "%JAVA_EXE%" goto execute + +echo. +echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME% +echo. +echo Please set the JAVA_HOME variable in your environment to match the +echo location of your Java installation. + +goto fail + +:execute +@rem Setup the command line + +set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar + + +@rem Execute Gradle +"%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %* + +:end +@rem End local scope for the variables with windows NT shell +if "%ERRORLEVEL%"=="0" goto mainEnd + +:fail +rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of +rem the _cmd.exe /c_ return code! +if not "" == "%GRADLE_EXIT_CONSOLE%" exit 1 +exit /b 1 + +:mainEnd +if "%OS%"=="Windows_NT" endlocal + +:omega diff --git a/platforms/android/aar-template/settings.gradle b/platforms/android/aar-template/settings.gradle new file mode 100644 index 0000000000..fb1a50602c --- /dev/null +++ b/platforms/android/aar-template/settings.gradle @@ -0,0 +1,16 @@ +pluginManagement { + repositories { + google() + mavenCentral() + gradlePluginPortal() + } +} +dependencyResolutionManagement { + repositoriesMode.set(RepositoriesMode.FAIL_ON_PROJECT_REPOS) + repositories { + google() + mavenCentral() + } +} +rootProject.name = "OpenCV" +include ':OpenCV' diff --git a/platforms/android/android.toolchain.cmake b/platforms/android/android.toolchain.cmake deleted file mode 100644 index 69c5598a97..0000000000 --- a/platforms/android/android.toolchain.cmake +++ /dev/null @@ -1,1795 +0,0 @@ -# Copyright (c) 2010-2011, Ethan Rublee -# Copyright (c) 2011-2014, Andrey Kamaev -# All rights reserved. -# -# Redistribution and use in source and binary forms, with or without -# modification, are permitted provided that the following conditions are met: -# -# 1. Redistributions of source code must retain the above copyright notice, -# this list of conditions and the following disclaimer. -# -# 2. Redistributions in binary form must reproduce the above copyright notice, -# this list of conditions and the following disclaimer in the documentation -# and/or other materials provided with the distribution. -# -# 3. Neither the name of the copyright holder nor the names of its -# contributors may be used to endorse or promote products derived from this -# software without specific prior written permission. -# -# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" -# AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE -# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE -# ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE -# LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR -# CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF -# SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS -# INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN -# CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) -# ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE -# POSSIBILITY OF SUCH DAMAGE. - -# ------------------------------------------------------------------------------ -# Android CMake toolchain file, for use with the Android NDK r5-r10d -# Requires cmake 2.6.3 or newer (2.8.9 or newer is recommended). -# See home page: https://github.com/taka-no-me/android-cmake -# -# Usage Linux: -# $ export ANDROID_NDK=/absolute/path/to/the/android-ndk -# $ mkdir build && cd build -# $ cmake -DCMAKE_TOOLCHAIN_FILE=path/to/the/android.toolchain.cmake .. -# $ make -j8 -# -# Usage Windows: -# You need native port of make to build your project. -# Android NDK r7 (and newer) already has make.exe on board. -# For older NDK you have to install it separately. -# For example, this one: http://gnuwin32.sourceforge.net/packages/make.htm -# -# $ SET ANDROID_NDK=C:\absolute\path\to\the\android-ndk -# $ mkdir build && cd build -# $ cmake.exe -G"MinGW Makefiles" -# -DCMAKE_TOOLCHAIN_FILE=path\to\the\android.toolchain.cmake -# -DCMAKE_MAKE_PROGRAM="%ANDROID_NDK%\prebuilt\windows\bin\make.exe" .. -# $ cmake.exe --build . -# -# -# Options (can be set as cmake parameters: -D=): -# ANDROID_NDK=/opt/android-ndk - path to the NDK root. -# Can be set as environment variable. Can be set only at first cmake run. -# -# ANDROID_ABI=armeabi-v7a - specifies the target Application Binary -# Interface (ABI). This option nearly matches to the APP_ABI variable -# used by ndk-build tool from Android NDK. -# -# Possible targets are: -# "armeabi" - ARMv5TE based CPU with software floating point operations -# "armeabi-v7a" - ARMv7 based devices with hardware FPU instructions -# this ABI target is used by default -# "armeabi-v7a-hard with NEON" - ARMv7 based devices with hardware FPU instructions and hardfp -# "armeabi-v7a with NEON" - same as armeabi-v7a, but -# sets NEON as floating-point unit -# "armeabi-v7a with VFPV3" - same as armeabi-v7a, but -# sets VFPV3 as floating-point unit (has 32 registers instead of 16) -# "armeabi-v6 with VFP" - tuned for ARMv6 processors having VFP -# "x86" - IA-32 instruction set -# "mips" - MIPS32 instruction set -# -# 64-bit ABIs for NDK r10 and newer: -# "arm64-v8a" - ARMv8 AArch64 instruction set -# "x86_64" - Intel64 instruction set (r1) -# "mips64" - MIPS64 instruction set (r6) -# -# ANDROID_NATIVE_API_LEVEL=android-8 - level of Android API compile for. -# Option is read-only when standalone toolchain is used. -# Note: building for "android-L" requires explicit configuration. -# -# ANDROID_TOOLCHAIN_NAME=arm-linux-androideabi-4.9 - the name of compiler -# toolchain to be used. The list of possible values depends on the NDK -# version. For NDK r10c the possible values are: -# -# * aarch64-linux-android-4.9 -# * aarch64-linux-android-clang3.4 -# * aarch64-linux-android-clang3.5 -# * arm-linux-androideabi-4.6 -# * arm-linux-androideabi-4.8 -# * arm-linux-androideabi-4.9 (default) -# * arm-linux-androideabi-clang3.4 -# * arm-linux-androideabi-clang3.5 -# * mips64el-linux-android-4.9 -# * mips64el-linux-android-clang3.4 -# * mips64el-linux-android-clang3.5 -# * mipsel-linux-android-4.6 -# * mipsel-linux-android-4.8 -# * mipsel-linux-android-4.9 -# * mipsel-linux-android-clang3.4 -# * mipsel-linux-android-clang3.5 -# * x86-4.6 -# * x86-4.8 -# * x86-4.9 -# * x86-clang3.4 -# * x86-clang3.5 -# * x86_64-4.9 -# * x86_64-clang3.4 -# * x86_64-clang3.5 -# -# ANDROID_FORCE_ARM_BUILD=OFF - set ON to generate 32-bit ARM instructions -# instead of Thumb. Is not available for "armeabi-v6 with VFP" -# (is forced to be ON) ABI. -# -# ANDROID_NO_UNDEFINED=ON - set ON to show all undefined symbols as linker -# errors even if they are not used. -# -# ANDROID_SO_UNDEFINED=OFF - set ON to allow undefined symbols in shared -# libraries. Automatically turned for NDK r5x and r6x due to GLESv2 -# problems. -# -# ANDROID_STL=gnustl_static - specify the runtime to use. -# -# Possible values are: -# none -> Do not configure the runtime. -# system -> Use the default minimal system C++ runtime library. -# Implies -fno-rtti -fno-exceptions. -# Is not available for standalone toolchain. -# system_re -> Use the default minimal system C++ runtime library. -# Implies -frtti -fexceptions. -# Is not available for standalone toolchain. -# gabi++_static -> Use the GAbi++ runtime as a static library. -# Implies -frtti -fno-exceptions. -# Available for NDK r7 and newer. -# Is not available for standalone toolchain. -# gabi++_shared -> Use the GAbi++ runtime as a shared library. -# Implies -frtti -fno-exceptions. -# Available for NDK r7 and newer. -# Is not available for standalone toolchain. -# stlport_static -> Use the STLport runtime as a static library. -# Implies -fno-rtti -fno-exceptions for NDK before r7. -# Implies -frtti -fno-exceptions for NDK r7 and newer. -# Is not available for standalone toolchain. -# stlport_shared -> Use the STLport runtime as a shared library. -# Implies -fno-rtti -fno-exceptions for NDK before r7. -# Implies -frtti -fno-exceptions for NDK r7 and newer. -# Is not available for standalone toolchain. -# gnustl_static -> Use the GNU STL as a static library. -# Implies -frtti -fexceptions. -# gnustl_shared -> Use the GNU STL as a shared library. -# Implies -frtti -fno-exceptions. -# Available for NDK r7b and newer. -# Silently degrades to gnustl_static if not available. -# c++_static -> Use the LLVM libc++ runtime as a static library. -# Implies -frtti -fexceptions. -# c++_shared -> Use the LLVM libc++ runtime as a shared library. -# Implies -frtti -fno-exceptions. -# -# ANDROID_STL_FORCE_FEATURES=ON - turn rtti and exceptions support based on -# chosen runtime. If disabled, then the user is responsible for settings -# these options. -# -# What?: -# android-cmake toolchain searches for NDK/toolchain in the following order: -# ANDROID_NDK - cmake parameter -# ANDROID_NDK - environment variable -# ANDROID_STANDALONE_TOOLCHAIN - cmake parameter -# ANDROID_STANDALONE_TOOLCHAIN - environment variable -# ANDROID_NDK - default locations -# ANDROID_STANDALONE_TOOLCHAIN - default locations -# -# Make sure to do the following in your scripts: -# SET( CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} ${my_cxx_flags}" ) -# SET( CMAKE_C_FLAGS "${CMAKE_C_FLAGS} ${my_cxx_flags}" ) -# The flags will be prepopulated with critical flags, so don't loose them. -# Also be aware that toolchain also sets configuration-specific compiler -# flags and linker flags. -# -# ANDROID and BUILD_ANDROID will be set to true, you may test any of these -# variables to make necessary Android-specific configuration changes. -# -# Also ARMEABI or ARMEABI_V7A or ARMEABI_V7A_HARD or X86 or MIPS or ARM64_V8A or X86_64 or MIPS64 -# will be set true, mutually exclusive. NEON option will be set true -# if VFP is set to NEON. -# -# ------------------------------------------------------------------------------ - -cmake_minimum_required( VERSION 2.8.12.2 ) - -if( DEFINED CMAKE_CROSSCOMPILING ) - # subsequent toolchain loading is not really needed - return() -endif() - -if( CMAKE_TOOLCHAIN_FILE ) - # touch toolchain variable to suppress "unused variable" warning -endif() - -# inherit settings in recursive loads -get_property( _CMAKE_IN_TRY_COMPILE GLOBAL PROPERTY IN_TRY_COMPILE ) -if( _CMAKE_IN_TRY_COMPILE ) - include( "${CMAKE_CURRENT_SOURCE_DIR}/../android.toolchain.config.cmake" OPTIONAL ) -endif() - -# this one is important -if( CMAKE_VERSION VERSION_GREATER "3.0.99" ) - set( CMAKE_SYSTEM_NAME Android ) -else() - set( CMAKE_SYSTEM_NAME Linux ) -endif() - -# this one not so much -set( CMAKE_SYSTEM_VERSION 1 ) - -# rpath makes low sense for Android -set( CMAKE_SHARED_LIBRARY_RUNTIME_C_FLAG "" ) -set( CMAKE_SKIP_RPATH TRUE CACHE BOOL "If set, runtime paths are not added when using shared libraries." ) - -# NDK search paths -set( ANDROID_SUPPORTED_NDK_VERSIONS ${ANDROID_EXTRA_NDK_VERSIONS} -r10d -r10c -r10b -r10 -r9d -r9c -r9b -r9 -r8e -r8d -r8c -r8b -r8 -r7c -r7b -r7 -r6b -r6 -r5c -r5b -r5 "" ) -if( NOT DEFINED ANDROID_NDK_SEARCH_PATHS ) - if( CMAKE_HOST_WIN32 ) - file( TO_CMAKE_PATH "$ENV{PROGRAMFILES}" ANDROID_NDK_SEARCH_PATHS ) - set( ANDROID_NDK_SEARCH_PATHS "${ANDROID_NDK_SEARCH_PATHS}" "$ENV{SystemDrive}/NVPACK" ) - else() - file( TO_CMAKE_PATH "$ENV{HOME}" ANDROID_NDK_SEARCH_PATHS ) - set( ANDROID_NDK_SEARCH_PATHS /opt "${ANDROID_NDK_SEARCH_PATHS}/NVPACK" ) - endif() -endif() -if( NOT DEFINED ANDROID_STANDALONE_TOOLCHAIN_SEARCH_PATH ) - set( ANDROID_STANDALONE_TOOLCHAIN_SEARCH_PATH /opt/android-toolchain ) -endif() - -# known ABIs -set( ANDROID_SUPPORTED_ABIS_arm "armeabi-v7a;armeabi;armeabi-v7a with NEON;armeabi-v7a-hard with NEON;armeabi-v7a with VFPV3;armeabi-v6 with VFP" ) -set( ANDROID_SUPPORTED_ABIS_arm64 "arm64-v8a" ) -set( ANDROID_SUPPORTED_ABIS_x86 "x86" ) -set( ANDROID_SUPPORTED_ABIS_x86_64 "x86_64" ) -set( ANDROID_SUPPORTED_ABIS_mips "mips" ) -set( ANDROID_SUPPORTED_ABIS_mips64 "mips64" ) - -# API level defaults -set( ANDROID_DEFAULT_NDK_API_LEVEL 9 ) -set( ANDROID_DEFAULT_NDK_API_LEVEL_arm64 21 ) -set( ANDROID_DEFAULT_NDK_API_LEVEL_x86 9 ) -set( ANDROID_DEFAULT_NDK_API_LEVEL_x86_64 21 ) -set( ANDROID_DEFAULT_NDK_API_LEVEL_mips 9 ) -set( ANDROID_DEFAULT_NDK_API_LEVEL_mips64 21 ) - - -macro( __LIST_FILTER listvar regex ) - if( ${listvar} ) - foreach( __val ${${listvar}} ) - if( __val MATCHES "${regex}" ) - list( REMOVE_ITEM ${listvar} "${__val}" ) - endif() - endforeach() - endif() -endmacro() - -macro( __INIT_VARIABLE var_name ) - set( __test_path 0 ) - foreach( __var ${ARGN} ) - if( __var STREQUAL "PATH" ) - set( __test_path 1 ) - break() - endif() - endforeach() - - if( __test_path AND NOT EXISTS "${${var_name}}" ) - unset( ${var_name} CACHE ) - endif() - - if( " ${${var_name}}" STREQUAL " " ) - set( __values 0 ) - foreach( __var ${ARGN} ) - if( __var STREQUAL "VALUES" ) - set( __values 1 ) - elseif( NOT __var STREQUAL "PATH" ) - if( __var MATCHES "^ENV_.*$" ) - string( REPLACE "ENV_" "" __var "${__var}" ) - set( __value "$ENV{${__var}}" ) - elseif( DEFINED ${__var} ) - set( __value "${${__var}}" ) - elseif( __values ) - set( __value "${__var}" ) - else() - set( __value "" ) - endif() - - if( NOT " ${__value}" STREQUAL " " AND (NOT __test_path OR EXISTS "${__value}") ) - set( ${var_name} "${__value}" ) - break() - endif() - endif() - endforeach() - unset( __value ) - unset( __values ) - endif() - - if( __test_path ) - file( TO_CMAKE_PATH "${${var_name}}" ${var_name} ) - endif() - unset( __test_path ) -endmacro() - -macro( __DETECT_NATIVE_API_LEVEL _var _path ) - set( __ndkApiLevelRegex "^[\t ]*#define[\t ]+__ANDROID_API__[\t ]+([0-9]+)[\t ]*.*$" ) - file( STRINGS ${_path} __apiFileContent REGEX "${__ndkApiLevelRegex}" ) - if( NOT __apiFileContent ) - set( __ndkApiLevelRegex "^[\t ]*#define[\t ]+__ANDROID_API__[\t ]+__ANDROID_API_FUTURE__[\t ]*$" ) - file( STRINGS ${_path} __apiFileContent REGEX "${__ndkApiLevelRegex}" ) - if( __apiFileContent ) - set(${_var} 10000) - else() - message( SEND_ERROR "Could not get Android native API level. Probably you have specified invalid level value, or your copy of NDK/toolchain is broken." ) - endif() - else() - string( REGEX REPLACE "${__ndkApiLevelRegex}" "\\1" ${_var} "${__apiFileContent}" ) - endif() - unset( __apiFileContent ) - unset( __ndkApiLevelRegex ) -endmacro() - -macro( __DETECT_TOOLCHAIN_MACHINE_NAME _var _root ) - if( EXISTS "${_root}" ) - file( GLOB __gccExePath RELATIVE "${_root}/bin/" "${_root}/bin/*-gcc${TOOL_OS_SUFFIX}" ) - __LIST_FILTER( __gccExePath "^[.].*" ) - list( LENGTH __gccExePath __gccExePathsCount ) - if( NOT __gccExePathsCount EQUAL 1 AND NOT _CMAKE_IN_TRY_COMPILE ) - message( WARNING "Could not determine machine name for compiler from ${_root}" ) - set( ${_var} "" ) - else() - get_filename_component( __gccExeName "${__gccExePath}" NAME_WE ) - string( REPLACE "-gcc" "" ${_var} "${__gccExeName}" ) - endif() - unset( __gccExePath ) - unset( __gccExePathsCount ) - unset( __gccExeName ) - else() - set( ${_var} "" ) - endif() -endmacro() - - -# fight against cygwin -set( ANDROID_FORBID_SYGWIN TRUE CACHE BOOL "Prevent cmake from working under cygwin and using cygwin tools") -mark_as_advanced( ANDROID_FORBID_SYGWIN ) -if( ANDROID_FORBID_SYGWIN ) - if( CYGWIN ) - message( FATAL_ERROR "Android NDK and android-cmake toolchain are not welcome Cygwin. It is unlikely that this cmake toolchain will work under cygwin. But if you want to try then you can set cmake variable ANDROID_FORBID_SYGWIN to FALSE and rerun cmake." ) - endif() - - if( CMAKE_HOST_WIN32 ) - # remove cygwin from PATH - set( __new_path "$ENV{PATH}") - __LIST_FILTER( __new_path "cygwin" ) - set(ENV{PATH} "${__new_path}") - unset(__new_path) - endif() -endif() - - -# detect current host platform -if( NOT DEFINED ANDROID_NDK_HOST_X64 AND (CMAKE_HOST_SYSTEM_PROCESSOR MATCHES "amd64|x86_64|AMD64" OR CMAKE_HOST_APPLE) ) - set( ANDROID_NDK_HOST_X64 1 CACHE BOOL "Try to use 64-bit compiler toolchain" ) - mark_as_advanced( ANDROID_NDK_HOST_X64 ) -endif() - -set( TOOL_OS_SUFFIX "" ) -if( CMAKE_HOST_APPLE ) - set( ANDROID_NDK_HOST_SYSTEM_NAME "darwin-x86_64" ) - set( ANDROID_NDK_HOST_SYSTEM_NAME2 "darwin-x86" ) -elseif( CMAKE_HOST_WIN32 ) - set( ANDROID_NDK_HOST_SYSTEM_NAME "windows-x86_64" ) - set( ANDROID_NDK_HOST_SYSTEM_NAME2 "windows" ) - set( TOOL_OS_SUFFIX ".exe" ) -elseif( CMAKE_HOST_UNIX ) - set( ANDROID_NDK_HOST_SYSTEM_NAME "linux-x86_64" ) - set( ANDROID_NDK_HOST_SYSTEM_NAME2 "linux-x86" ) -else() - message( FATAL_ERROR "Cross-compilation on your platform is not supported by this cmake toolchain" ) -endif() - -if( NOT ANDROID_NDK_HOST_X64 ) - set( ANDROID_NDK_HOST_SYSTEM_NAME ${ANDROID_NDK_HOST_SYSTEM_NAME2} ) -endif() - -# see if we have path to Android NDK -if( NOT ANDROID_NDK AND NOT ANDROID_STANDALONE_TOOLCHAIN ) - __INIT_VARIABLE( ANDROID_NDK PATH ENV_ANDROID_NDK ) -endif() -if( NOT ANDROID_NDK ) - # see if we have path to Android standalone toolchain - __INIT_VARIABLE( ANDROID_STANDALONE_TOOLCHAIN PATH ENV_ANDROID_STANDALONE_TOOLCHAIN ) - - if( NOT ANDROID_STANDALONE_TOOLCHAIN ) - #try to find Android NDK in one of the default locations - set( __ndkSearchPaths ) - foreach( __ndkSearchPath ${ANDROID_NDK_SEARCH_PATHS} ) - foreach( suffix ${ANDROID_SUPPORTED_NDK_VERSIONS} ) - list( APPEND __ndkSearchPaths "${__ndkSearchPath}/android-ndk${suffix}" ) - endforeach() - endforeach() - __INIT_VARIABLE( ANDROID_NDK PATH VALUES ${__ndkSearchPaths} ) - unset( __ndkSearchPaths ) - - if( ANDROID_NDK ) - message( STATUS "Using default path for Android NDK: ${ANDROID_NDK}" ) - message( STATUS " If you prefer to use a different location, please define a cmake or environment variable: ANDROID_NDK" ) - else() - #try to find Android standalone toolchain in one of the default locations - __INIT_VARIABLE( ANDROID_STANDALONE_TOOLCHAIN PATH ANDROID_STANDALONE_TOOLCHAIN_SEARCH_PATH ) - - if( ANDROID_STANDALONE_TOOLCHAIN ) - message( STATUS "Using default path for standalone toolchain ${ANDROID_STANDALONE_TOOLCHAIN}" ) - message( STATUS " If you prefer to use a different location, please define the variable: ANDROID_STANDALONE_TOOLCHAIN" ) - endif( ANDROID_STANDALONE_TOOLCHAIN ) - endif( ANDROID_NDK ) - endif( NOT ANDROID_STANDALONE_TOOLCHAIN ) -endif( NOT ANDROID_NDK ) - -# remember found paths -if( ANDROID_NDK ) - get_filename_component( ANDROID_NDK "${ANDROID_NDK}" ABSOLUTE ) - set( ANDROID_NDK "${ANDROID_NDK}" CACHE INTERNAL "Path of the Android NDK" FORCE ) - set( BUILD_WITH_ANDROID_NDK True ) - if( EXISTS "${ANDROID_NDK}/RELEASE.TXT" ) - file( STRINGS "${ANDROID_NDK}/RELEASE.TXT" ANDROID_NDK_RELEASE_FULL LIMIT_COUNT 1 REGEX "r[0-9]+[a-z]?" ) - string( REGEX MATCH "r([0-9]+)([a-z]?)" ANDROID_NDK_RELEASE "${ANDROID_NDK_RELEASE_FULL}" ) - else() - set( ANDROID_NDK_RELEASE "r1x" ) - set( ANDROID_NDK_RELEASE_FULL "unreleased" ) - endif() - string( REGEX REPLACE "r([0-9]+)([a-z]?)" "\\1*1000" ANDROID_NDK_RELEASE_NUM "${ANDROID_NDK_RELEASE}" ) - string( FIND " abcdefghijklmnopqastuvwxyz" "${CMAKE_MATCH_2}" __ndkReleaseLetterNum ) - math( EXPR ANDROID_NDK_RELEASE_NUM "${ANDROID_NDK_RELEASE_NUM}+${__ndkReleaseLetterNum}" ) -elseif( ANDROID_STANDALONE_TOOLCHAIN ) - get_filename_component( ANDROID_STANDALONE_TOOLCHAIN "${ANDROID_STANDALONE_TOOLCHAIN}" ABSOLUTE ) - # try to detect change - if( CMAKE_AR ) - string( LENGTH "${ANDROID_STANDALONE_TOOLCHAIN}" __length ) - string( SUBSTRING "${CMAKE_AR}" 0 ${__length} __androidStandaloneToolchainPreviousPath ) - if( NOT __androidStandaloneToolchainPreviousPath STREQUAL ANDROID_STANDALONE_TOOLCHAIN ) - message( FATAL_ERROR "It is not possible to change path to the Android standalone toolchain on subsequent run." ) - endif() - unset( __androidStandaloneToolchainPreviousPath ) - unset( __length ) - endif() - set( ANDROID_STANDALONE_TOOLCHAIN "${ANDROID_STANDALONE_TOOLCHAIN}" CACHE INTERNAL "Path of the Android standalone toolchain" FORCE ) - set( BUILD_WITH_STANDALONE_TOOLCHAIN True ) -else() - list(GET ANDROID_NDK_SEARCH_PATHS 0 ANDROID_NDK_SEARCH_PATH) - message( FATAL_ERROR "Could not find neither Android NDK nor Android standalone toolchain. - You should either set an environment variable: - export ANDROID_NDK=~/my-android-ndk - or - export ANDROID_STANDALONE_TOOLCHAIN=~/my-android-toolchain - or put the toolchain or NDK in the default path: - sudo ln -s ~/my-android-ndk ${ANDROID_NDK_SEARCH_PATH}/android-ndk - sudo ln -s ~/my-android-toolchain ${ANDROID_STANDALONE_TOOLCHAIN_SEARCH_PATH}" ) -endif() - -# android NDK layout -if( BUILD_WITH_ANDROID_NDK ) - if( NOT DEFINED ANDROID_NDK_LAYOUT ) - # try to automatically detect the layout - if( EXISTS "${ANDROID_NDK}/RELEASE.TXT") - set( ANDROID_NDK_LAYOUT "RELEASE" ) - elseif( EXISTS "${ANDROID_NDK}/../../linux-x86/toolchain/" ) - set( ANDROID_NDK_LAYOUT "LINARO" ) - elseif( EXISTS "${ANDROID_NDK}/../../gcc/" ) - set( ANDROID_NDK_LAYOUT "ANDROID" ) - endif() - endif() - set( ANDROID_NDK_LAYOUT "${ANDROID_NDK_LAYOUT}" CACHE STRING "The inner layout of NDK" ) - mark_as_advanced( ANDROID_NDK_LAYOUT ) - if( ANDROID_NDK_LAYOUT STREQUAL "LINARO" ) - set( ANDROID_NDK_HOST_SYSTEM_NAME ${ANDROID_NDK_HOST_SYSTEM_NAME2} ) # only 32-bit at the moment - set( ANDROID_NDK_TOOLCHAINS_PATH "${ANDROID_NDK}/../../${ANDROID_NDK_HOST_SYSTEM_NAME}/toolchain" ) - set( ANDROID_NDK_TOOLCHAINS_SUBPATH "" ) - set( ANDROID_NDK_TOOLCHAINS_SUBPATH2 "" ) - elseif( ANDROID_NDK_LAYOUT STREQUAL "ANDROID" ) - set( ANDROID_NDK_HOST_SYSTEM_NAME ${ANDROID_NDK_HOST_SYSTEM_NAME2} ) # only 32-bit at the moment - set( ANDROID_NDK_TOOLCHAINS_PATH "${ANDROID_NDK}/../../gcc/${ANDROID_NDK_HOST_SYSTEM_NAME}/arm" ) - set( ANDROID_NDK_TOOLCHAINS_SUBPATH "" ) - set( ANDROID_NDK_TOOLCHAINS_SUBPATH2 "" ) - else() # ANDROID_NDK_LAYOUT STREQUAL "RELEASE" - set( ANDROID_NDK_TOOLCHAINS_PATH "${ANDROID_NDK}/toolchains" ) - set( ANDROID_NDK_TOOLCHAINS_SUBPATH "/prebuilt/${ANDROID_NDK_HOST_SYSTEM_NAME}" ) - set( ANDROID_NDK_TOOLCHAINS_SUBPATH2 "/prebuilt/${ANDROID_NDK_HOST_SYSTEM_NAME2}" ) - endif() - get_filename_component( ANDROID_NDK_TOOLCHAINS_PATH "${ANDROID_NDK_TOOLCHAINS_PATH}" ABSOLUTE ) - - # try to detect change of NDK - if( CMAKE_AR ) - string( LENGTH "${ANDROID_NDK_TOOLCHAINS_PATH}" __length ) - string( SUBSTRING "${CMAKE_AR}" 0 ${__length} __androidNdkPreviousPath ) - if( NOT __androidNdkPreviousPath STREQUAL ANDROID_NDK_TOOLCHAINS_PATH ) - message( FATAL_ERROR "It is not possible to change the path to the NDK on subsequent CMake run. You must remove all generated files from your build folder first. - " ) - endif() - unset( __androidNdkPreviousPath ) - unset( __length ) - endif() -endif() - - -# get all the details about standalone toolchain -if( BUILD_WITH_STANDALONE_TOOLCHAIN ) - __DETECT_NATIVE_API_LEVEL( ANDROID_SUPPORTED_NATIVE_API_LEVELS "${ANDROID_STANDALONE_TOOLCHAIN}/sysroot/usr/include/android/api-level.h" ) - set( ANDROID_STANDALONE_TOOLCHAIN_API_LEVEL ${ANDROID_SUPPORTED_NATIVE_API_LEVELS} ) - set( __availableToolchains "standalone" ) - __DETECT_TOOLCHAIN_MACHINE_NAME( __availableToolchainMachines "${ANDROID_STANDALONE_TOOLCHAIN}" ) - if( NOT __availableToolchainMachines ) - message( FATAL_ERROR "Could not determine machine name of your toolchain. Probably your Android standalone toolchain is broken." ) - endif() - if( __availableToolchainMachines MATCHES x86_64 ) - set( __availableToolchainArchs "x86_64" ) - elseif( __availableToolchainMachines MATCHES i686 ) - set( __availableToolchainArchs "x86" ) - elseif( __availableToolchainMachines MATCHES aarch64 ) - set( __availableToolchainArchs "arm64" ) - elseif( __availableToolchainMachines MATCHES arm ) - set( __availableToolchainArchs "arm" ) - elseif( __availableToolchainMachines MATCHES mips64el ) - set( __availableToolchainArchs "mips64" ) - elseif( __availableToolchainMachines MATCHES mipsel ) - set( __availableToolchainArchs "mips" ) - endif() - execute_process( COMMAND "${ANDROID_STANDALONE_TOOLCHAIN}/bin/${__availableToolchainMachines}-gcc${TOOL_OS_SUFFIX}" -dumpversion - OUTPUT_VARIABLE __availableToolchainCompilerVersions OUTPUT_STRIP_TRAILING_WHITESPACE ) - string( REGEX MATCH "[0-9]+[.][0-9]+([.][0-9]+)?" __availableToolchainCompilerVersions "${__availableToolchainCompilerVersions}" ) - if( EXISTS "${ANDROID_STANDALONE_TOOLCHAIN}/bin/clang${TOOL_OS_SUFFIX}" ) - list( APPEND __availableToolchains "standalone-clang" ) - list( APPEND __availableToolchainMachines ${__availableToolchainMachines} ) - list( APPEND __availableToolchainArchs ${__availableToolchainArchs} ) - list( APPEND __availableToolchainCompilerVersions ${__availableToolchainCompilerVersions} ) - endif() -endif() - -macro( __GLOB_NDK_TOOLCHAINS __availableToolchainsVar __availableToolchainsLst __toolchain_subpath ) - foreach( __toolchain ${${__availableToolchainsLst}} ) - if( "${__toolchain}" MATCHES "-clang3[.][0-9]$" AND NOT EXISTS "${ANDROID_NDK_TOOLCHAINS_PATH}/${__toolchain}${__toolchain_subpath}" ) - SET( __toolchainVersionRegex "^TOOLCHAIN_VERSION[\t ]+:=[\t ]+(.*)$" ) - FILE( STRINGS "${ANDROID_NDK_TOOLCHAINS_PATH}/${__toolchain}/setup.mk" __toolchainVersionStr REGEX "${__toolchainVersionRegex}" ) - if( __toolchainVersionStr ) - string( REGEX REPLACE "${__toolchainVersionRegex}" "\\1" __toolchainVersionStr "${__toolchainVersionStr}" ) - string( REGEX REPLACE "-clang3[.][0-9]$" "-${__toolchainVersionStr}" __gcc_toolchain "${__toolchain}" ) - else() - string( REGEX REPLACE "-clang3[.][0-9]$" "-4.6" __gcc_toolchain "${__toolchain}" ) - endif() - unset( __toolchainVersionStr ) - unset( __toolchainVersionRegex ) - else() - set( __gcc_toolchain "${__toolchain}" ) - endif() - __DETECT_TOOLCHAIN_MACHINE_NAME( __machine "${ANDROID_NDK_TOOLCHAINS_PATH}/${__gcc_toolchain}${__toolchain_subpath}" ) - if( __machine ) - string( REGEX MATCH "[0-9]+[.][0-9]+([.][0-9x]+)?$" __version "${__gcc_toolchain}" ) - if( __machine MATCHES x86_64 ) - set( __arch "x86_64" ) - elseif( __machine MATCHES i686 ) - set( __arch "x86" ) - elseif( __machine MATCHES aarch64 ) - set( __arch "arm64" ) - elseif( __machine MATCHES arm ) - set( __arch "arm" ) - elseif( __machine MATCHES mips64el ) - set( __arch "mips64" ) - elseif( __machine MATCHES mipsel ) - set( __arch "mips" ) - else() - set( __arch "" ) - endif() - #message("machine: !${__machine}!\narch: !${__arch}!\nversion: !${__version}!\ntoolchain: !${__toolchain}!\n") - if (__arch) - list( APPEND __availableToolchainMachines "${__machine}" ) - list( APPEND __availableToolchainArchs "${__arch}" ) - list( APPEND __availableToolchainCompilerVersions "${__version}" ) - list( APPEND ${__availableToolchainsVar} "${__toolchain}" ) - endif() - endif() - unset( __gcc_toolchain ) - endforeach() -endmacro() - -# get all the details about NDK -if( BUILD_WITH_ANDROID_NDK ) - file( GLOB ANDROID_SUPPORTED_NATIVE_API_LEVELS RELATIVE "${ANDROID_NDK}/platforms" "${ANDROID_NDK}/platforms/android-*" ) - string( REPLACE "android-" "" ANDROID_SUPPORTED_NATIVE_API_LEVELS "${ANDROID_SUPPORTED_NATIVE_API_LEVELS}" ) - set( __availableToolchains "" ) - set( __availableToolchainMachines "" ) - set( __availableToolchainArchs "" ) - set( __availableToolchainCompilerVersions "" ) - if( ANDROID_TOOLCHAIN_NAME AND EXISTS "${ANDROID_NDK_TOOLCHAINS_PATH}/${ANDROID_TOOLCHAIN_NAME}/" ) - # do not go through all toolchains if we know the name - set( __availableToolchainsLst "${ANDROID_TOOLCHAIN_NAME}" ) - __GLOB_NDK_TOOLCHAINS( __availableToolchains __availableToolchainsLst "${ANDROID_NDK_TOOLCHAINS_SUBPATH}" ) - if( NOT __availableToolchains AND NOT ANDROID_NDK_TOOLCHAINS_SUBPATH STREQUAL ANDROID_NDK_TOOLCHAINS_SUBPATH2 ) - __GLOB_NDK_TOOLCHAINS( __availableToolchains __availableToolchainsLst "${ANDROID_NDK_TOOLCHAINS_SUBPATH2}" ) - if( __availableToolchains ) - set( ANDROID_NDK_TOOLCHAINS_SUBPATH ${ANDROID_NDK_TOOLCHAINS_SUBPATH2} ) - endif() - endif() - endif() - if( NOT __availableToolchains ) - file( GLOB __availableToolchainsLst RELATIVE "${ANDROID_NDK_TOOLCHAINS_PATH}" "${ANDROID_NDK_TOOLCHAINS_PATH}/*" ) - if( __availableToolchainsLst ) - list(SORT __availableToolchainsLst) # we need clang to go after gcc - endif() - __LIST_FILTER( __availableToolchainsLst "^[.]" ) - __LIST_FILTER( __availableToolchainsLst "llvm" ) - __LIST_FILTER( __availableToolchainsLst "renderscript" ) - __GLOB_NDK_TOOLCHAINS( __availableToolchains __availableToolchainsLst "${ANDROID_NDK_TOOLCHAINS_SUBPATH}" ) - if( NOT __availableToolchains AND NOT ANDROID_NDK_TOOLCHAINS_SUBPATH STREQUAL ANDROID_NDK_TOOLCHAINS_SUBPATH2 ) - __GLOB_NDK_TOOLCHAINS( __availableToolchains __availableToolchainsLst "${ANDROID_NDK_TOOLCHAINS_SUBPATH2}" ) - if( __availableToolchains ) - set( ANDROID_NDK_TOOLCHAINS_SUBPATH ${ANDROID_NDK_TOOLCHAINS_SUBPATH2} ) - endif() - endif() - endif() - if( NOT __availableToolchains ) - message( FATAL_ERROR "Could not find any working toolchain in the NDK. Probably your Android NDK is broken." ) - endif() -endif() - -# build list of available ABIs -set( ANDROID_SUPPORTED_ABIS "" ) -set( __uniqToolchainArchNames ${__availableToolchainArchs} ) -list( REMOVE_DUPLICATES __uniqToolchainArchNames ) -list( SORT __uniqToolchainArchNames ) -foreach( __arch ${__uniqToolchainArchNames} ) - list( APPEND ANDROID_SUPPORTED_ABIS ${ANDROID_SUPPORTED_ABIS_${__arch}} ) -endforeach() -unset( __uniqToolchainArchNames ) -if( NOT ANDROID_SUPPORTED_ABIS ) - message( FATAL_ERROR "No one of known Android ABIs is supported by this cmake toolchain." ) -endif() - -# choose target ABI -__INIT_VARIABLE( ANDROID_ABI VALUES ${ANDROID_SUPPORTED_ABIS} ) -# verify that target ABI is supported -list( FIND ANDROID_SUPPORTED_ABIS "${ANDROID_ABI}" __androidAbiIdx ) -if( __androidAbiIdx EQUAL -1 ) - string( REPLACE ";" "\", \"" PRINTABLE_ANDROID_SUPPORTED_ABIS "${ANDROID_SUPPORTED_ABIS}" ) - message( FATAL_ERROR "Specified ANDROID_ABI = \"${ANDROID_ABI}\" is not supported by this cmake toolchain or your NDK/toolchain. - Supported values are: \"${PRINTABLE_ANDROID_SUPPORTED_ABIS}\" - " ) -endif() -unset( __androidAbiIdx ) - -# set target ABI options -if( ANDROID_ABI STREQUAL "x86" ) - set( X86 true ) - set( ANDROID_NDK_ABI_NAME "x86" ) - set( ANDROID_ARCH_NAME "x86" ) - set( ANDROID_LLVM_TRIPLE "i686-none-linux-android" ) - set( CMAKE_SYSTEM_PROCESSOR "i686" ) -elseif( ANDROID_ABI STREQUAL "x86_64" ) - set( X86 true ) - set( X86_64 true ) - set( ANDROID_NDK_ABI_NAME "x86_64" ) - set( ANDROID_ARCH_NAME "x86_64" ) - set( CMAKE_SYSTEM_PROCESSOR "x86_64" ) - set( ANDROID_LLVM_TRIPLE "x86_64-none-linux-android" ) -elseif( ANDROID_ABI STREQUAL "mips64" ) - set( MIPS64 true ) - set( ANDROID_NDK_ABI_NAME "mips64" ) - set( ANDROID_ARCH_NAME "mips64" ) - set( ANDROID_LLVM_TRIPLE "mips64el-none-linux-android" ) - set( CMAKE_SYSTEM_PROCESSOR "mips64" ) -elseif( ANDROID_ABI STREQUAL "mips" ) - set( MIPS true ) - set( ANDROID_NDK_ABI_NAME "mips" ) - set( ANDROID_ARCH_NAME "mips" ) - set( ANDROID_LLVM_TRIPLE "mipsel-none-linux-android" ) - set( CMAKE_SYSTEM_PROCESSOR "mips" ) -elseif( ANDROID_ABI STREQUAL "arm64-v8a" ) - set( ARM64_V8A true ) - set( ANDROID_NDK_ABI_NAME "arm64-v8a" ) - set( ANDROID_ARCH_NAME "arm64" ) - set( ANDROID_LLVM_TRIPLE "aarch64-none-linux-android" ) - set( CMAKE_SYSTEM_PROCESSOR "aarch64" ) - set( VFPV3 true ) - set( NEON true ) -elseif( ANDROID_ABI STREQUAL "armeabi" ) - set( ARMEABI true ) - set( ANDROID_NDK_ABI_NAME "armeabi" ) - set( ANDROID_ARCH_NAME "arm" ) - set( ANDROID_LLVM_TRIPLE "armv5te-none-linux-androideabi" ) - set( CMAKE_SYSTEM_PROCESSOR "armv5te" ) -elseif( ANDROID_ABI STREQUAL "armeabi-v6 with VFP" ) - set( ARMEABI_V6 true ) - set( ANDROID_NDK_ABI_NAME "armeabi" ) - set( ANDROID_ARCH_NAME "arm" ) - set( ANDROID_LLVM_TRIPLE "armv5te-none-linux-androideabi" ) - set( CMAKE_SYSTEM_PROCESSOR "armv6" ) - # need always fallback to older platform - set( ARMEABI true ) -elseif( ANDROID_ABI STREQUAL "armeabi-v7a") - set( ARMEABI_V7A true ) - set( ANDROID_NDK_ABI_NAME "armeabi-v7a" ) - set( ANDROID_ARCH_NAME "arm" ) - set( ANDROID_LLVM_TRIPLE "armv7-none-linux-androideabi" ) - set( CMAKE_SYSTEM_PROCESSOR "armv7-a" ) -elseif( ANDROID_ABI STREQUAL "armeabi-v7a with VFPV3" ) - set( ARMEABI_V7A true ) - set( ANDROID_NDK_ABI_NAME "armeabi-v7a" ) - set( ANDROID_ARCH_NAME "arm" ) - set( ANDROID_LLVM_TRIPLE "armv7-none-linux-androideabi" ) - set( CMAKE_SYSTEM_PROCESSOR "armv7-a" ) - set( VFPV3 true ) -elseif( ANDROID_ABI STREQUAL "armeabi-v7a with NEON" ) - set( ARMEABI_V7A true ) - set( ANDROID_NDK_ABI_NAME "armeabi-v7a" ) - set( ANDROID_ARCH_NAME "arm" ) - set( ANDROID_LLVM_TRIPLE "armv7-none-linux-androideabi" ) - set( CMAKE_SYSTEM_PROCESSOR "armv7-a" ) - set( VFPV3 true ) - set( NEON true ) -elseif( ANDROID_ABI STREQUAL "armeabi-v7a-hard with NEON" ) - set( ARMEABI_V7A_HARD true ) - set( ANDROID_NDK_ABI_NAME "armeabi-v7a-hard" ) - set( ANDROID_ARCH_NAME "arm" ) - set( ANDROID_LLVM_TRIPLE "armv7-none-linux-androideabi" ) - set( CMAKE_SYSTEM_PROCESSOR "armv7-a" ) - set( VFPV3 true ) - set( NEON true ) -else() - message( SEND_ERROR "Unknown ANDROID_ABI=\"${ANDROID_ABI}\" is specified." ) -endif() - -if( CMAKE_BINARY_DIR AND EXISTS "${CMAKE_BINARY_DIR}${CMAKE_FILES_DIRECTORY}/CMakeSystem.cmake" ) - # really dirty hack - # it is not possible to change CMAKE_SYSTEM_PROCESSOR after the first run... - file( APPEND "${CMAKE_BINARY_DIR}${CMAKE_FILES_DIRECTORY}/CMakeSystem.cmake" "SET(CMAKE_SYSTEM_PROCESSOR \"${CMAKE_SYSTEM_PROCESSOR}\")\n" ) -endif() - -if( ANDROID_ARCH_NAME STREQUAL "arm" AND NOT ARMEABI_V6 ) - __INIT_VARIABLE( ANDROID_FORCE_ARM_BUILD VALUES OFF ) - set( ANDROID_FORCE_ARM_BUILD ${ANDROID_FORCE_ARM_BUILD} CACHE BOOL "Use 32-bit ARM instructions instead of Thumb-1" FORCE ) - mark_as_advanced( ANDROID_FORCE_ARM_BUILD ) -else() - unset( ANDROID_FORCE_ARM_BUILD CACHE ) -endif() - -# choose toolchain -if( ANDROID_TOOLCHAIN_NAME ) - list( FIND __availableToolchains "${ANDROID_TOOLCHAIN_NAME}" __toolchainIdx ) - if( __toolchainIdx EQUAL -1 ) - list( SORT __availableToolchains ) - string( REPLACE ";" "\n * " toolchains_list "${__availableToolchains}" ) - set( toolchains_list " * ${toolchains_list}") - message( FATAL_ERROR "Specified toolchain \"${ANDROID_TOOLCHAIN_NAME}\" is missing in your NDK or broken. Please verify that your NDK is working or select another compiler toolchain. -To configure the toolchain set CMake variable ANDROID_TOOLCHAIN_NAME to one of the following values:\n${toolchains_list}\n" ) - endif() - list( GET __availableToolchainArchs ${__toolchainIdx} __toolchainArch ) - if( NOT __toolchainArch STREQUAL ANDROID_ARCH_NAME ) - message( SEND_ERROR "Selected toolchain \"${ANDROID_TOOLCHAIN_NAME}\" is not able to compile binaries for the \"${ANDROID_ARCH_NAME}\" platform." ) - endif() -else() - set( __toolchainIdx -1 ) - set( __applicableToolchains "" ) - set( __toolchainMaxVersion "0.0.0" ) - list( LENGTH __availableToolchains __availableToolchainsCount ) - math( EXPR __availableToolchainsCount "${__availableToolchainsCount}-1" ) - foreach( __idx RANGE ${__availableToolchainsCount} ) - list( GET __availableToolchainArchs ${__idx} __toolchainArch ) - if( __toolchainArch STREQUAL ANDROID_ARCH_NAME ) - list( GET __availableToolchainCompilerVersions ${__idx} __toolchainVersion ) - string( REPLACE "x" "99" __toolchainVersion "${__toolchainVersion}") - if( __toolchainVersion VERSION_GREATER __toolchainMaxVersion ) - set( __toolchainMaxVersion "${__toolchainVersion}" ) - set( __toolchainIdx ${__idx} ) - endif() - endif() - endforeach() - unset( __availableToolchainsCount ) - unset( __toolchainMaxVersion ) - unset( __toolchainVersion ) -endif() -unset( __toolchainArch ) -if( __toolchainIdx EQUAL -1 ) - message( FATAL_ERROR "No one of available compiler toolchains is able to compile for ${ANDROID_ARCH_NAME} platform." ) -endif() -list( GET __availableToolchains ${__toolchainIdx} ANDROID_TOOLCHAIN_NAME ) -list( GET __availableToolchainMachines ${__toolchainIdx} ANDROID_TOOLCHAIN_MACHINE_NAME ) -list( GET __availableToolchainCompilerVersions ${__toolchainIdx} ANDROID_COMPILER_VERSION ) - -unset( __toolchainIdx ) -unset( __availableToolchains ) -unset( __availableToolchainMachines ) -unset( __availableToolchainArchs ) -unset( __availableToolchainCompilerVersions ) - -# choose native API level -__INIT_VARIABLE( ANDROID_NATIVE_API_LEVEL ENV_ANDROID_NATIVE_API_LEVEL ANDROID_API_LEVEL ENV_ANDROID_API_LEVEL ANDROID_STANDALONE_TOOLCHAIN_API_LEVEL ANDROID_DEFAULT_NDK_API_LEVEL_${ANDROID_ARCH_NAME} ANDROID_DEFAULT_NDK_API_LEVEL ) -string( REPLACE "android-" "" ANDROID_NATIVE_API_LEVEL "${ANDROID_NATIVE_API_LEVEL}" ) -string( STRIP "${ANDROID_NATIVE_API_LEVEL}" ANDROID_NATIVE_API_LEVEL ) -# adjust API level -set( __real_api_level ${ANDROID_DEFAULT_NDK_API_LEVEL_${ANDROID_ARCH_NAME}} ) -foreach( __level ${ANDROID_SUPPORTED_NATIVE_API_LEVELS} ) - if( (__level LESS ANDROID_NATIVE_API_LEVEL OR __level STREQUAL ANDROID_NATIVE_API_LEVEL) AND NOT __level LESS __real_api_level ) - set( __real_api_level ${__level} ) - endif() -endforeach() -if( __real_api_level AND NOT ANDROID_NATIVE_API_LEVEL STREQUAL __real_api_level ) - message( STATUS "Adjusting Android API level 'android-${ANDROID_NATIVE_API_LEVEL}' to 'android-${__real_api_level}'") - set( ANDROID_NATIVE_API_LEVEL ${__real_api_level} ) -endif() -unset(__real_api_level) -# validate -list( FIND ANDROID_SUPPORTED_NATIVE_API_LEVELS "${ANDROID_NATIVE_API_LEVEL}" __levelIdx ) -if( __levelIdx EQUAL -1 ) - message( SEND_ERROR "Specified Android native API level 'android-${ANDROID_NATIVE_API_LEVEL}' is not supported by your NDK/toolchain.\nSupported values of ANDROID_NATIVE_API_LEVEL: ${ANDROID_SUPPORTED_NATIVE_API_LEVELS}" ) -else() - if( BUILD_WITH_ANDROID_NDK ) - if(EXISTS "${ANDROID_NDK}/platforms/android-${ANDROID_NATIVE_API_LEVEL}/arch-${ANDROID_ARCH_NAME}/usr/include/android/api-level.h") - __DETECT_NATIVE_API_LEVEL( __realApiLevel "${ANDROID_NDK}/platforms/android-${ANDROID_NATIVE_API_LEVEL}/arch-${ANDROID_ARCH_NAME}/usr/include/android/api-level.h" ) - else() - __DETECT_NATIVE_API_LEVEL( __realApiLevel "${ANDROID_NDK}/sysroot/usr/include/android/api-level.h") - endif() - - if( NOT __realApiLevel EQUAL ANDROID_NATIVE_API_LEVEL AND NOT __realApiLevel GREATER 9000 ) - message( SEND_ERROR "Specified Android API level (${ANDROID_NATIVE_API_LEVEL}) does not match to the level found (${__realApiLevel}). Probably your copy of NDK is broken." ) - endif() - unset( __realApiLevel ) - endif() - set( ANDROID_NATIVE_API_LEVEL "${ANDROID_NATIVE_API_LEVEL}" CACHE STRING "Android API level for native code" FORCE ) - set( CMAKE_ANDROID_API ${ANDROID_NATIVE_API_LEVEL} ) - if( CMAKE_VERSION VERSION_GREATER "2.8" ) - list( SORT ANDROID_SUPPORTED_NATIVE_API_LEVELS ) - set_property( CACHE ANDROID_NATIVE_API_LEVEL PROPERTY STRINGS ${ANDROID_SUPPORTED_NATIVE_API_LEVELS} ) - endif() -endif() -unset( __levelIdx ) - - -# remember target ABI -set( ANDROID_ABI "${ANDROID_ABI}" CACHE STRING "The target ABI for Android. If arm, then armeabi-v7a is recommended for hardware floating point." FORCE ) -if( CMAKE_VERSION VERSION_GREATER "2.8" ) - list( SORT ANDROID_SUPPORTED_ABIS_${ANDROID_ARCH_NAME} ) - set_property( CACHE ANDROID_ABI PROPERTY STRINGS ${ANDROID_SUPPORTED_ABIS_${ANDROID_ARCH_NAME}} ) -endif() - - -# runtime choice (STL, rtti, exceptions) -if( NOT ANDROID_STL ) - set( ANDROID_STL gnustl_static ) -endif() -set( ANDROID_STL "${ANDROID_STL}" CACHE STRING "C++ runtime" ) -set( ANDROID_STL_FORCE_FEATURES ON CACHE BOOL "automatically configure rtti and exceptions support based on C++ runtime" ) -mark_as_advanced( ANDROID_STL ANDROID_STL_FORCE_FEATURES ) - -if( BUILD_WITH_ANDROID_NDK ) - if( NOT "${ANDROID_STL}" MATCHES "^(none|system|system_re|gabi\\+\\+_static|gabi\\+\\+_shared|stlport_static|stlport_shared|gnustl_static|gnustl_shared|c\\+\\+_static|c\\+\\+_shared)$") - message( FATAL_ERROR "ANDROID_STL is set to invalid value \"${ANDROID_STL}\". -The possible values are: - none -> Do not configure the runtime. - system -> Use the default minimal system C++ runtime library. - system_re -> Same as system but with rtti and exceptions. - gabi++_static -> Use the GAbi++ runtime as a static library. - gabi++_shared -> Use the GAbi++ runtime as a shared library. - stlport_static -> Use the STLport runtime as a static library. - stlport_shared -> Use the STLport runtime as a shared library. - gnustl_static -> (default) Use the GNU STL as a static library. - gnustl_shared -> Use the GNU STL as a shared library. - c++_shared -> Use the LLVM libc++ runtime as a shared library. - c++_static -> Use the LLVM libc++ runtime as a static library. -" ) - endif() -elseif( BUILD_WITH_STANDALONE_TOOLCHAIN ) - if( NOT "${ANDROID_STL}" MATCHES "^(none|gnustl_static|gnustl_shared|c\\+\\+_static|c\\+\\+_shared)$") - message( FATAL_ERROR "ANDROID_STL is set to invalid value \"${ANDROID_STL}\". -The possible values are: - none -> Do not configure the runtime. - gnustl_static -> (default) Use the GNU STL as a static library. - gnustl_shared -> Use the GNU STL as a shared library. - c++_shared -> Use the LLVM libc++ runtime as a shared library. - c++_static -> Use the LLVM libc++ runtime as a static library. -" ) - endif() -endif() - -unset( ANDROID_RTTI ) -unset( ANDROID_EXCEPTIONS ) -unset( ANDROID_STL_INCLUDE_DIRS ) -unset( __libstl ) -unset( __libsupcxx ) - -if( NOT _CMAKE_IN_TRY_COMPILE AND ANDROID_NDK_RELEASE STREQUAL "r7b" AND ARMEABI_V7A AND NOT VFPV3 AND ANDROID_STL MATCHES "gnustl" ) - message( WARNING "The GNU STL armeabi-v7a binaries from NDK r7b can crash non-NEON devices. The files provided with NDK r7b were not configured properly, resulting in crashes on Tegra2-based devices and others when trying to use certain floating-point functions (e.g., cosf, sinf, expf). -You are strongly recommended to switch to another NDK release. -" ) -endif() - -if( NOT _CMAKE_IN_TRY_COMPILE AND X86 AND ANDROID_STL MATCHES "gnustl" AND ANDROID_NDK_RELEASE STREQUAL "r6" ) - message( WARNING "The x86 system header file from NDK r6 has incorrect definition for ptrdiff_t. You are recommended to upgrade to a newer NDK release or manually patch the header: -See https://android.googlesource.com/platform/development.git f907f4f9d4e56ccc8093df6fee54454b8bcab6c2 - diff --git a/ndk/platforms/android-9/arch-x86/include/machine/_types.h b/ndk/platforms/android-9/arch-x86/include/machine/_types.h - index 5e28c64..65892a1 100644 - --- a/ndk/platforms/android-9/arch-x86/include/machine/_types.h - +++ b/ndk/platforms/android-9/arch-x86/include/machine/_types.h - @@ -51,7 +51,11 @@ typedef long int ssize_t; - #endif - #ifndef _PTRDIFF_T - #define _PTRDIFF_T - -typedef long ptrdiff_t; - +# ifdef __ANDROID__ - + typedef int ptrdiff_t; - +# else - + typedef long ptrdiff_t; - +# endif - #endif -" ) -endif() - - -# setup paths and STL for standalone toolchain -if( BUILD_WITH_STANDALONE_TOOLCHAIN ) - set( ANDROID_TOOLCHAIN_ROOT "${ANDROID_STANDALONE_TOOLCHAIN}" ) - set( ANDROID_CLANG_TOOLCHAIN_ROOT "${ANDROID_STANDALONE_TOOLCHAIN}" ) - set( ANDROID_SYSROOT "${ANDROID_STANDALONE_TOOLCHAIN}/sysroot" ) - set( ANDROID_SYSROOT_INCLUDE "${ANDROID_STANDALONE_TOOLCHAIN}/sysroot/usr/include" ) - - if( NOT ANDROID_STL STREQUAL "none" ) - set( ANDROID_STL_INCLUDE_DIRS "${ANDROID_STANDALONE_TOOLCHAIN}/include/c++/${ANDROID_COMPILER_VERSION}" ) - if( NOT EXISTS "${ANDROID_STL_INCLUDE_DIRS}" ) - # old location ( pre r8c ) - set( ANDROID_STL_INCLUDE_DIRS "${ANDROID_STANDALONE_TOOLCHAIN}/${ANDROID_TOOLCHAIN_MACHINE_NAME}/include/c++/${ANDROID_COMPILER_VERSION}" ) - endif() - if( (ARMEABI_V7A OR ARMEABI_V7A_HARD) AND EXISTS "${ANDROID_STL_INCLUDE_DIRS}/${ANDROID_TOOLCHAIN_MACHINE_NAME}/${CMAKE_SYSTEM_PROCESSOR}/bits" ) - list( APPEND ANDROID_STL_INCLUDE_DIRS "${ANDROID_STL_INCLUDE_DIRS}/${ANDROID_TOOLCHAIN_MACHINE_NAME}/${CMAKE_SYSTEM_PROCESSOR}" ) - elseif( ARMEABI AND NOT ANDROID_FORCE_ARM_BUILD AND EXISTS "${ANDROID_STL_INCLUDE_DIRS}/${ANDROID_TOOLCHAIN_MACHINE_NAME}/thumb/bits" ) - list( APPEND ANDROID_STL_INCLUDE_DIRS "${ANDROID_STL_INCLUDE_DIRS}/${ANDROID_TOOLCHAIN_MACHINE_NAME}/thumb" ) - else() - list( APPEND ANDROID_STL_INCLUDE_DIRS "${ANDROID_STL_INCLUDE_DIRS}/${ANDROID_TOOLCHAIN_MACHINE_NAME}" ) - endif() - # always search static GNU STL to get the location of libsupc++.a - if( (ARMEABI_V7A OR ARMEABI_V7A_HARD) AND NOT ANDROID_FORCE_ARM_BUILD AND EXISTS "${ANDROID_STANDALONE_TOOLCHAIN}/${ANDROID_TOOLCHAIN_MACHINE_NAME}/lib/${CMAKE_SYSTEM_PROCESSOR}/thumb/libstdc++.a" ) - set( __libstl "${ANDROID_STANDALONE_TOOLCHAIN}/${ANDROID_TOOLCHAIN_MACHINE_NAME}/lib/${CMAKE_SYSTEM_PROCESSOR}/thumb" ) - elseif( (ARMEABI_V7A OR ARMEABI_V7A_HARD) AND EXISTS "${ANDROID_STANDALONE_TOOLCHAIN}/${ANDROID_TOOLCHAIN_MACHINE_NAME}/lib/${CMAKE_SYSTEM_PROCESSOR}/libstdc++.a" ) - set( __libstl "${ANDROID_STANDALONE_TOOLCHAIN}/${ANDROID_TOOLCHAIN_MACHINE_NAME}/lib/${CMAKE_SYSTEM_PROCESSOR}" ) - elseif( ARMEABI AND NOT ANDROID_FORCE_ARM_BUILD AND EXISTS "${ANDROID_STANDALONE_TOOLCHAIN}/${ANDROID_TOOLCHAIN_MACHINE_NAME}/lib/thumb/libstdc++.a" ) - set( __libstl "${ANDROID_STANDALONE_TOOLCHAIN}/${ANDROID_TOOLCHAIN_MACHINE_NAME}/lib/thumb" ) - elseif( EXISTS "${ANDROID_STANDALONE_TOOLCHAIN}/${ANDROID_TOOLCHAIN_MACHINE_NAME}/lib/libstdc++.a" ) - set( __libstl "${ANDROID_STANDALONE_TOOLCHAIN}/${ANDROID_TOOLCHAIN_MACHINE_NAME}/lib" ) - endif() - if( __libstl ) - set( __libsupcxx "${__libstl}/libsupc++.a" ) - set( __libstl "${__libstl}/libstdc++.a" ) - endif() - if( NOT EXISTS "${__libsupcxx}" ) - message( FATAL_ERROR "The required libstdsupc++.a is missing in your standalone toolchain. - Usually it happens because of bug in make-standalone-toolchain.sh script from NDK r7, r7b and r7c. - You need to either upgrade to newer NDK or manually copy - $ANDROID_NDK/sources/cxx-stl/gnu-libstdc++/libs/${ANDROID_NDK_ABI_NAME}/libsupc++.a - to - ${__libsupcxx} - " ) - endif() - if( ANDROID_STL STREQUAL "gnustl_shared" ) - if( (ARMEABI_V7A OR ARMEABI_V7A_HARD) AND EXISTS "${ANDROID_STANDALONE_TOOLCHAIN}/${ANDROID_TOOLCHAIN_MACHINE_NAME}/lib/${CMAKE_SYSTEM_PROCESSOR}/libgnustl_shared.so" ) - set( __libstl "${ANDROID_STANDALONE_TOOLCHAIN}/${ANDROID_TOOLCHAIN_MACHINE_NAME}/lib/${CMAKE_SYSTEM_PROCESSOR}/libgnustl_shared.so" ) - elseif( ARMEABI AND NOT ANDROID_FORCE_ARM_BUILD AND EXISTS "${ANDROID_STANDALONE_TOOLCHAIN}/${ANDROID_TOOLCHAIN_MACHINE_NAME}/lib/thumb/libgnustl_shared.so" ) - set( __libstl "${ANDROID_STANDALONE_TOOLCHAIN}/${ANDROID_TOOLCHAIN_MACHINE_NAME}/lib/thumb/libgnustl_shared.so" ) - elseif( EXISTS "${ANDROID_STANDALONE_TOOLCHAIN}/${ANDROID_TOOLCHAIN_MACHINE_NAME}/lib/libgnustl_shared.so" ) - set( __libstl "${ANDROID_STANDALONE_TOOLCHAIN}/${ANDROID_TOOLCHAIN_MACHINE_NAME}/lib/libgnustl_shared.so" ) - endif() - endif() - endif() -endif() - -# clang -if( "${ANDROID_TOOLCHAIN_NAME}" STREQUAL "standalone-clang" ) - set( ANDROID_COMPILER_IS_CLANG 1 ) - execute_process( COMMAND "${ANDROID_CLANG_TOOLCHAIN_ROOT}/bin/clang${TOOL_OS_SUFFIX}" --version OUTPUT_VARIABLE ANDROID_CLANG_VERSION OUTPUT_STRIP_TRAILING_WHITESPACE ) - string( REGEX MATCH "[0-9]+[.][0-9]+" ANDROID_CLANG_VERSION "${ANDROID_CLANG_VERSION}") -elseif( "${ANDROID_TOOLCHAIN_NAME}" MATCHES "-clang3[.][0-9]?$" ) - string( REGEX MATCH "3[.][0-9]$" ANDROID_CLANG_VERSION "${ANDROID_TOOLCHAIN_NAME}") - string( REGEX REPLACE "-clang${ANDROID_CLANG_VERSION}$" "-${ANDROID_COMPILER_VERSION}" ANDROID_GCC_TOOLCHAIN_NAME "${ANDROID_TOOLCHAIN_NAME}" ) - if( NOT EXISTS "${ANDROID_NDK_TOOLCHAINS_PATH}/llvm-${ANDROID_CLANG_VERSION}${ANDROID_NDK_TOOLCHAINS_SUBPATH}/bin/clang${TOOL_OS_SUFFIX}" ) - message( FATAL_ERROR "Could not find the Clang compiler driver" ) - endif() - set( ANDROID_COMPILER_IS_CLANG 1 ) - set( ANDROID_CLANG_TOOLCHAIN_ROOT "${ANDROID_NDK_TOOLCHAINS_PATH}/llvm-${ANDROID_CLANG_VERSION}${ANDROID_NDK_TOOLCHAINS_SUBPATH}" ) -else() - set( ANDROID_GCC_TOOLCHAIN_NAME "${ANDROID_TOOLCHAIN_NAME}" ) - unset( ANDROID_COMPILER_IS_CLANG CACHE ) -endif() - -string( REPLACE "." "" _clang_name "clang${ANDROID_CLANG_VERSION}" ) -if( NOT EXISTS "${ANDROID_CLANG_TOOLCHAIN_ROOT}/bin/${_clang_name}${TOOL_OS_SUFFIX}" ) - set( _clang_name "clang" ) -endif() - - -# setup paths and STL for NDK -if( BUILD_WITH_ANDROID_NDK ) - set( ANDROID_TOOLCHAIN_ROOT "${ANDROID_NDK_TOOLCHAINS_PATH}/${ANDROID_GCC_TOOLCHAIN_NAME}${ANDROID_NDK_TOOLCHAINS_SUBPATH}" ) - set( ANDROID_SYSROOT "${ANDROID_NDK}/platforms/android-${ANDROID_NATIVE_API_LEVEL}/arch-${ANDROID_ARCH_NAME}" ) - if( EXISTS "${ANDROID_SYSROOT}/usr/include" ) - set( ANDROID_SYSROOT_INCLUDE "${ANDROID_SYSROOT}/usr/include" ) - else() - set( ANDROID_SYSROOT_INCLUDE "${ANDROID_NDK}/sysroot/usr/include" "${ANDROID_NDK}/sysroot/usr/include/${ANDROID_TOOLCHAIN_MACHINE_NAME}" ) - endif() - - if( ANDROID_STL STREQUAL "none" ) - # do nothing - elseif( ANDROID_STL STREQUAL "system" ) - set( ANDROID_RTTI OFF ) - set( ANDROID_EXCEPTIONS OFF ) - set( ANDROID_STL_INCLUDE_DIRS "${ANDROID_NDK}/sources/cxx-stl/system/include" ) - elseif( ANDROID_STL STREQUAL "system_re" ) - set( ANDROID_RTTI ON ) - set( ANDROID_EXCEPTIONS ON ) - set( ANDROID_STL_INCLUDE_DIRS "${ANDROID_NDK}/sources/cxx-stl/system/include" ) - elseif( ANDROID_STL MATCHES "gabi" ) - if( ANDROID_NDK_RELEASE_NUM LESS 7000 ) # before r7 - message( FATAL_ERROR "gabi++ is not available in your NDK. You have to upgrade to NDK r7 or newer to use gabi++.") - endif() - set( ANDROID_RTTI ON ) - set( ANDROID_EXCEPTIONS OFF ) - set( ANDROID_STL_INCLUDE_DIRS "${ANDROID_NDK}/sources/cxx-stl/gabi++/include" ) - set( __libstl "${ANDROID_NDK}/sources/cxx-stl/gabi++/libs/${ANDROID_NDK_ABI_NAME}/libgabi++_static.a" ) - elseif( ANDROID_STL MATCHES "stlport" ) - if( NOT ANDROID_NDK_RELEASE_NUM LESS 8004 ) # before r8d - set( ANDROID_EXCEPTIONS ON ) - else() - set( ANDROID_EXCEPTIONS OFF ) - endif() - if( ANDROID_NDK_RELEASE_NUM LESS 7000 ) # before r7 - set( ANDROID_RTTI OFF ) - else() - set( ANDROID_RTTI ON ) - endif() - set( ANDROID_STL_INCLUDE_DIRS "${ANDROID_NDK}/sources/cxx-stl/stlport/stlport" ) - set( __libstl "${ANDROID_NDK}/sources/cxx-stl/stlport/libs/${ANDROID_NDK_ABI_NAME}/libstlport_static.a" ) - elseif( ANDROID_STL MATCHES "gnustl" ) - set( ANDROID_EXCEPTIONS ON ) - set( ANDROID_RTTI ON ) - if( EXISTS "${ANDROID_NDK}/sources/cxx-stl/gnu-libstdc++/${ANDROID_COMPILER_VERSION}" ) - if( ARMEABI_V7A AND ANDROID_COMPILER_VERSION VERSION_EQUAL "4.7" AND ANDROID_NDK_RELEASE STREQUAL "r8d" ) - # gnustl binary for 4.7 compiler is buggy :( - # TODO: look for right fix - set( __libstl "${ANDROID_NDK}/sources/cxx-stl/gnu-libstdc++/4.6" ) - else() - set( __libstl "${ANDROID_NDK}/sources/cxx-stl/gnu-libstdc++/${ANDROID_COMPILER_VERSION}" ) - endif() - else() - set( __libstl "${ANDROID_NDK}/sources/cxx-stl/gnu-libstdc++" ) - endif() - set( ANDROID_STL_INCLUDE_DIRS "${__libstl}/include" "${__libstl}/libs/${ANDROID_NDK_ABI_NAME}/include" "${__libstl}/include/backward" ) - if( EXISTS "${__libstl}/libs/${ANDROID_NDK_ABI_NAME}/libgnustl_static.a" ) - set( __libstl "${__libstl}/libs/${ANDROID_NDK_ABI_NAME}/libgnustl_static.a" ) - else() - set( __libstl "${__libstl}/libs/${ANDROID_NDK_ABI_NAME}/libstdc++.a" ) - endif() - elseif( ANDROID_STL MATCHES "c\\+\\+" ) - set( ANDROID_EXCEPTIONS ON ) - set( ANDROID_RTTI ON ) - set( __libstl "${ANDROID_NDK}/sources/cxx-stl/llvm-libc++" ) - set( __libstl "${__libstl}/libs/${ANDROID_NDK_ABI_NAME}/libc++_static.a" ) - set( ANDROID_STL_INCLUDE_DIRS "${ANDROID_NDK}/sources/android/support/include" "${ANDROID_NDK}/sources/cxx-stl/llvm-libc++/libcxx/include" "${ANDROID_NDK}/sources/cxx-stl/llvm-libc++abi/libcxxabi/include" ) - else() - message( FATAL_ERROR "Unknown runtime: ${ANDROID_STL}" ) - endif() - - # find libsupc++.a - rtti & exceptions - if( ANDROID_STL STREQUAL "system_re" OR ANDROID_STL MATCHES "gnustl" ) - set( __libsupcxx "${ANDROID_NDK}/sources/cxx-stl/gnu-libstdc++/${ANDROID_COMPILER_VERSION}/libs/${ANDROID_NDK_ABI_NAME}/libsupc++.a" ) # r8b or newer - if( NOT EXISTS "${__libsupcxx}" ) - set( __libsupcxx "${ANDROID_NDK}/sources/cxx-stl/gnu-libstdc++/libs/${ANDROID_NDK_ABI_NAME}/libsupc++.a" ) # r7-r8 - endif() - if( NOT EXISTS "${__libsupcxx}" ) # before r7 - if( ARMEABI_V7A ) - if( ANDROID_FORCE_ARM_BUILD ) - set( __libsupcxx "${ANDROID_TOOLCHAIN_ROOT}/${ANDROID_TOOLCHAIN_MACHINE_NAME}/lib/${CMAKE_SYSTEM_PROCESSOR}/libsupc++.a" ) - else() - set( __libsupcxx "${ANDROID_TOOLCHAIN_ROOT}/${ANDROID_TOOLCHAIN_MACHINE_NAME}/lib/${CMAKE_SYSTEM_PROCESSOR}/thumb/libsupc++.a" ) - endif() - elseif( ARMEABI AND NOT ANDROID_FORCE_ARM_BUILD ) - set( __libsupcxx "${ANDROID_TOOLCHAIN_ROOT}/${ANDROID_TOOLCHAIN_MACHINE_NAME}/lib/thumb/libsupc++.a" ) - else() - set( __libsupcxx "${ANDROID_TOOLCHAIN_ROOT}/${ANDROID_TOOLCHAIN_MACHINE_NAME}/lib/libsupc++.a" ) - endif() - endif() - if( NOT EXISTS "${__libsupcxx}") - message( ERROR "Could not find libsupc++.a for a chosen platform. Either your NDK is not supported or is broken.") - endif() - endif() -endif() - - -# case of shared STL linkage -if( ANDROID_STL MATCHES "shared" AND DEFINED __libstl ) - string( REPLACE "_static.a" "_shared.so" __libstl "${__libstl}" ) - if( NOT EXISTS "${__libstl}" ) - message( FATAL_ERROR "Unable to find shared library ${__libstl}" ) - endif() -endif() - - -# ccache support -__INIT_VARIABLE( _ndk_ccache NDK_CCACHE ENV_NDK_CCACHE ) -if( _ndk_ccache ) - if( DEFINED NDK_CCACHE AND NOT EXISTS NDK_CCACHE ) - unset( NDK_CCACHE CACHE ) - endif() - find_program( NDK_CCACHE "${_ndk_ccache}" DOC "The path to ccache binary") -else() - unset( NDK_CCACHE CACHE ) -endif() -unset( _ndk_ccache ) - - -# setup the cross-compiler -if( NOT CMAKE_C_COMPILER ) - if( NDK_CCACHE AND NOT ANDROID_SYSROOT MATCHES "[ ;\"]" ) - set( CMAKE_C_COMPILER "${NDK_CCACHE}" CACHE PATH "ccache as C compiler" ) - set( CMAKE_CXX_COMPILER "${NDK_CCACHE}" CACHE PATH "ccache as C++ compiler" ) - if( ANDROID_COMPILER_IS_CLANG ) - set( CMAKE_C_COMPILER_ARG1 "${ANDROID_CLANG_TOOLCHAIN_ROOT}/bin/${_clang_name}${TOOL_OS_SUFFIX}" CACHE PATH "C compiler") - set( CMAKE_CXX_COMPILER_ARG1 "${ANDROID_CLANG_TOOLCHAIN_ROOT}/bin/${_clang_name}++${TOOL_OS_SUFFIX}" CACHE PATH "C++ compiler") - else() - set( CMAKE_C_COMPILER_ARG1 "${ANDROID_TOOLCHAIN_ROOT}/bin/${ANDROID_TOOLCHAIN_MACHINE_NAME}-gcc${TOOL_OS_SUFFIX}" CACHE PATH "C compiler") - set( CMAKE_CXX_COMPILER_ARG1 "${ANDROID_TOOLCHAIN_ROOT}/bin/${ANDROID_TOOLCHAIN_MACHINE_NAME}-g++${TOOL_OS_SUFFIX}" CACHE PATH "C++ compiler") - endif() - else() - if( ANDROID_COMPILER_IS_CLANG ) - set( CMAKE_C_COMPILER "${ANDROID_CLANG_TOOLCHAIN_ROOT}/bin/${_clang_name}${TOOL_OS_SUFFIX}" CACHE PATH "C compiler") - set( CMAKE_CXX_COMPILER "${ANDROID_CLANG_TOOLCHAIN_ROOT}/bin/${_clang_name}++${TOOL_OS_SUFFIX}" CACHE PATH "C++ compiler") - else() - set( CMAKE_C_COMPILER "${ANDROID_TOOLCHAIN_ROOT}/bin/${ANDROID_TOOLCHAIN_MACHINE_NAME}-gcc${TOOL_OS_SUFFIX}" CACHE PATH "C compiler" ) - set( CMAKE_CXX_COMPILER "${ANDROID_TOOLCHAIN_ROOT}/bin/${ANDROID_TOOLCHAIN_MACHINE_NAME}-g++${TOOL_OS_SUFFIX}" CACHE PATH "C++ compiler" ) - endif() - endif() - set( CMAKE_ASM_COMPILER "${ANDROID_TOOLCHAIN_ROOT}/bin/${ANDROID_TOOLCHAIN_MACHINE_NAME}-gcc${TOOL_OS_SUFFIX}" CACHE PATH "assembler" ) - set( CMAKE_STRIP "${ANDROID_TOOLCHAIN_ROOT}/bin/${ANDROID_TOOLCHAIN_MACHINE_NAME}-strip${TOOL_OS_SUFFIX}" CACHE PATH "strip" ) - if( EXISTS "${ANDROID_TOOLCHAIN_ROOT}/bin/${ANDROID_TOOLCHAIN_MACHINE_NAME}-gcc-ar${TOOL_OS_SUFFIX}" ) - # Use gcc-ar if we have it for better LTO support. - set( CMAKE_AR "${ANDROID_TOOLCHAIN_ROOT}/bin/${ANDROID_TOOLCHAIN_MACHINE_NAME}-gcc-ar${TOOL_OS_SUFFIX}" CACHE PATH "archive" ) - else() - set( CMAKE_AR "${ANDROID_TOOLCHAIN_ROOT}/bin/${ANDROID_TOOLCHAIN_MACHINE_NAME}-ar${TOOL_OS_SUFFIX}" CACHE PATH "archive" ) - endif() - set( CMAKE_LINKER "${ANDROID_TOOLCHAIN_ROOT}/bin/${ANDROID_TOOLCHAIN_MACHINE_NAME}-ld${TOOL_OS_SUFFIX}" CACHE PATH "linker" ) - set( CMAKE_NM "${ANDROID_TOOLCHAIN_ROOT}/bin/${ANDROID_TOOLCHAIN_MACHINE_NAME}-nm${TOOL_OS_SUFFIX}" CACHE PATH "nm" ) - set( CMAKE_OBJCOPY "${ANDROID_TOOLCHAIN_ROOT}/bin/${ANDROID_TOOLCHAIN_MACHINE_NAME}-objcopy${TOOL_OS_SUFFIX}" CACHE PATH "objcopy" ) - set( CMAKE_OBJDUMP "${ANDROID_TOOLCHAIN_ROOT}/bin/${ANDROID_TOOLCHAIN_MACHINE_NAME}-objdump${TOOL_OS_SUFFIX}" CACHE PATH "objdump" ) - set( CMAKE_RANLIB "${ANDROID_TOOLCHAIN_ROOT}/bin/${ANDROID_TOOLCHAIN_MACHINE_NAME}-ranlib${TOOL_OS_SUFFIX}" CACHE PATH "ranlib" ) -endif() - -set( _CMAKE_TOOLCHAIN_PREFIX "${ANDROID_TOOLCHAIN_MACHINE_NAME}-" ) -if( CMAKE_VERSION VERSION_LESS 2.8.5 ) - set( CMAKE_ASM_COMPILER_ARG1 "-c" ) -endif() -if( APPLE ) - find_program( CMAKE_INSTALL_NAME_TOOL NAMES install_name_tool ) - if( NOT CMAKE_INSTALL_NAME_TOOL ) - message( FATAL_ERROR "Could not find install_name_tool, please check your installation." ) - endif() - mark_as_advanced( CMAKE_INSTALL_NAME_TOOL ) -endif() - -# Force set compilers because standard identification works badly for us -if( CMAKE_VERSION VERSION_LESS 3.5.0 ) - include( CMakeForceCompiler ) - CMAKE_FORCE_C_COMPILER( "${CMAKE_C_COMPILER}" GNU ) -endif() -if( ANDROID_COMPILER_IS_CLANG ) - set( CMAKE_C_COMPILER_ID Clang ) -endif() -set( CMAKE_C_PLATFORM_ID Linux ) -if( X86_64 OR MIPS64 OR ARM64_V8A ) - set( CMAKE_C_SIZEOF_DATA_PTR 8 ) -else() - set( CMAKE_C_SIZEOF_DATA_PTR 4 ) -endif() -set( CMAKE_C_HAS_ISYSROOT 1 ) -set( CMAKE_C_COMPILER_ABI ELF ) -if( CMAKE_VERSION VERSION_LESS 3.5.0 ) - CMAKE_FORCE_CXX_COMPILER( "${CMAKE_CXX_COMPILER}" GNU ) -endif() -if( ANDROID_COMPILER_IS_CLANG ) - set( CMAKE_CXX_COMPILER_ID Clang) -endif() -set( CMAKE_CXX_PLATFORM_ID Linux ) -set( CMAKE_CXX_SIZEOF_DATA_PTR ${CMAKE_C_SIZEOF_DATA_PTR} ) -set( CMAKE_CXX_HAS_ISYSROOT 1 ) -set( CMAKE_CXX_COMPILER_ABI ELF ) -set( CMAKE_CXX_SOURCE_FILE_EXTENSIONS cc cp cxx cpp CPP c++ C ) -# force ASM compiler (required for CMake < 2.8.5) -set( CMAKE_ASM_COMPILER_ID_RUN TRUE ) -set( CMAKE_ASM_COMPILER_ID GNU ) -set( CMAKE_ASM_COMPILER_WORKS TRUE ) -set( CMAKE_ASM_COMPILER_FORCED TRUE ) -set( CMAKE_COMPILER_IS_GNUASM 1) -set( CMAKE_ASM_SOURCE_FILE_EXTENSIONS s S asm ) - -foreach( lang C CXX ASM ) - if( ANDROID_COMPILER_IS_CLANG ) - set( CMAKE_${lang}_COMPILER_VERSION ${ANDROID_CLANG_VERSION} ) - else() - set( CMAKE_${lang}_COMPILER_VERSION ${ANDROID_COMPILER_VERSION} ) - endif() -endforeach() - -# flags and definitions -remove_definitions( -DANDROID ) -add_definitions( -DANDROID ) - -if( ANDROID_SYSROOT MATCHES "[ ;\"]" ) - if( CMAKE_HOST_WIN32 ) - # try to convert path to 8.3 form - file( WRITE "${CMAKE_BINARY_DIR}${CMAKE_FILES_DIRECTORY}/cvt83.cmd" "@echo %~s1" ) - execute_process( COMMAND "$ENV{ComSpec}" /c "${CMAKE_BINARY_DIR}${CMAKE_FILES_DIRECTORY}/cvt83.cmd" "${ANDROID_SYSROOT}" - OUTPUT_VARIABLE __path OUTPUT_STRIP_TRAILING_WHITESPACE - RESULT_VARIABLE __result ERROR_QUIET ) - if( __result EQUAL 0 ) - file( TO_CMAKE_PATH "${__path}" ANDROID_SYSROOT ) - set( ANDROID_CXX_FLAGS "--sysroot=${ANDROID_SYSROOT}" ) - else() - set( ANDROID_CXX_FLAGS "--sysroot=\"${ANDROID_SYSROOT}\"" ) - endif() - else() - set( ANDROID_CXX_FLAGS "'--sysroot=${ANDROID_SYSROOT}'" ) - endif() - if( NOT _CMAKE_IN_TRY_COMPILE ) - # quotes can break try_compile and compiler identification - message(WARNING "Path to your Android NDK (or toolchain) has non-alphanumeric symbols.\nThe build might be broken.\n") - endif() -else() - set( ANDROID_CXX_FLAGS "--sysroot=${ANDROID_SYSROOT}" ) -endif() - -# NDK flags -if (ARM64_V8A ) - set( ANDROID_CXX_FLAGS "${ANDROID_CXX_FLAGS} -funwind-tables" ) - set( ANDROID_CXX_FLAGS_RELEASE "-fomit-frame-pointer -fstrict-aliasing" ) - set( ANDROID_CXX_FLAGS_DEBUG "-fno-omit-frame-pointer -fno-strict-aliasing" ) - if( NOT ANDROID_COMPILER_IS_CLANG ) - set( ANDROID_CXX_FLAGS_RELEASE "${ANDROID_CXX_FLAGS_RELEASE} -funswitch-loops -finline-limit=300" ) - endif() -elseif( ARMEABI OR ARMEABI_V7A OR ARMEABI_V7A_HARD) - set( ANDROID_CXX_FLAGS "${ANDROID_CXX_FLAGS} -funwind-tables" ) - if( NOT ANDROID_FORCE_ARM_BUILD AND NOT ARMEABI_V6 ) - set( ANDROID_CXX_FLAGS_RELEASE "-mthumb -fomit-frame-pointer -fno-strict-aliasing" ) - set( ANDROID_CXX_FLAGS_DEBUG "-marm -fno-omit-frame-pointer -fno-strict-aliasing" ) - if( NOT ANDROID_COMPILER_IS_CLANG ) - set( ANDROID_CXX_FLAGS "${ANDROID_CXX_FLAGS} -finline-limit=64" ) - endif() - else() - # always compile ARMEABI_V6 in arm mode; otherwise there is no difference from ARMEABI - set( ANDROID_CXX_FLAGS_RELEASE "-marm -fomit-frame-pointer -fstrict-aliasing" ) - set( ANDROID_CXX_FLAGS_DEBUG "-marm -fno-omit-frame-pointer -fno-strict-aliasing" ) - if( NOT ANDROID_COMPILER_IS_CLANG ) - set( ANDROID_CXX_FLAGS "${ANDROID_CXX_FLAGS} -funswitch-loops -finline-limit=300" ) - endif() - endif() -elseif( X86 OR X86_64 ) - set( ANDROID_CXX_FLAGS "${ANDROID_CXX_FLAGS} -funwind-tables" ) - if( NOT ANDROID_COMPILER_IS_CLANG ) - set( ANDROID_CXX_FLAGS "${ANDROID_CXX_FLAGS} -funswitch-loops -finline-limit=300" ) - endif() - set( ANDROID_CXX_FLAGS_RELEASE "-fomit-frame-pointer -fstrict-aliasing" ) - set( ANDROID_CXX_FLAGS_DEBUG "-fno-omit-frame-pointer -fno-strict-aliasing" ) -elseif( MIPS OR MIPS64 ) - set( ANDROID_CXX_FLAGS "${ANDROID_CXX_FLAGS} -fno-strict-aliasing -finline-functions -funwind-tables -fmessage-length=0" ) - set( ANDROID_CXX_FLAGS_RELEASE "-fomit-frame-pointer" ) - set( ANDROID_CXX_FLAGS_DEBUG "-fno-omit-frame-pointer" ) - if( NOT ANDROID_COMPILER_IS_CLANG ) - set( ANDROID_CXX_FLAGS "${ANDROID_CXX_FLAGS} -fno-inline-functions-called-once -fgcse-after-reload -frerun-cse-after-loop -frename-registers" ) - set( ANDROID_CXX_FLAGS_RELEASE "${ANDROID_CXX_FLAGS_RELEASE} -funswitch-loops -finline-limit=300" ) - endif() -elseif() - set( ANDROID_CXX_FLAGS_RELEASE "" ) - set( ANDROID_CXX_FLAGS_DEBUG "" ) -endif() - -set( ANDROID_CXX_FLAGS "${ANDROID_CXX_FLAGS} -fsigned-char" ) # good/necessary when porting desktop libraries - -if( NOT X86 AND NOT ANDROID_COMPILER_IS_CLANG ) - set( ANDROID_CXX_FLAGS "-Wno-psabi ${ANDROID_CXX_FLAGS}" ) -endif() - -if( NOT ANDROID_COMPILER_VERSION VERSION_LESS "4.6" ) - set( ANDROID_CXX_FLAGS "${ANDROID_CXX_FLAGS} -no-canonical-prefixes" ) # see https://android-review.googlesource.com/#/c/47564/ -endif() - -# ABI-specific flags -if( ARMEABI_V7A_HARD ) - set( ANDROID_CXX_FLAGS "${ANDROID_CXX_FLAGS} -march=armv7-a -mfloat-abi=hard -mhard-float -D_NDK_MATH_NO_SOFTFP=1" ) - if( NEON ) - set( ANDROID_CXX_FLAGS "${ANDROID_CXX_FLAGS} -mfpu=neon" ) - elseif( VFPV3 ) - set( ANDROID_CXX_FLAGS "${ANDROID_CXX_FLAGS} -mfpu=vfpv3" ) - else() - set( ANDROID_CXX_FLAGS "${ANDROID_CXX_FLAGS} -mfpu=vfpv3-d16" ) - endif() -elseif( ARMEABI_V7A ) - set( ANDROID_CXX_FLAGS "${ANDROID_CXX_FLAGS} -march=armv7-a -mfloat-abi=softfp" ) - if( NEON ) - set( ANDROID_CXX_FLAGS "${ANDROID_CXX_FLAGS} -mfpu=neon" ) - elseif( VFPV3 ) - set( ANDROID_CXX_FLAGS "${ANDROID_CXX_FLAGS} -mfpu=vfpv3" ) - else() - set( ANDROID_CXX_FLAGS "${ANDROID_CXX_FLAGS} -mfpu=vfpv3-d16" ) - endif() - -elseif( ARMEABI_V6 ) - set( ANDROID_CXX_FLAGS "${ANDROID_CXX_FLAGS} -march=armv6 -mfloat-abi=softfp -mfpu=vfp" ) # vfp == vfpv2 -elseif( ARMEABI ) - set( ANDROID_CXX_FLAGS "${ANDROID_CXX_FLAGS} -march=armv5te -mtune=xscale -msoft-float" ) -endif() - -if( ANDROID_STL MATCHES "gnustl" AND (EXISTS "${__libstl}" OR EXISTS "${__libsupcxx}") ) - set( CMAKE_CXX_CREATE_SHARED_LIBRARY " -o " ) - set( CMAKE_CXX_CREATE_SHARED_MODULE " -o " ) - set( CMAKE_CXX_LINK_EXECUTABLE " -o " ) -else() - set( CMAKE_CXX_CREATE_SHARED_LIBRARY " -o " ) - set( CMAKE_CXX_CREATE_SHARED_MODULE " -o " ) - set( CMAKE_CXX_LINK_EXECUTABLE " -o " ) -endif() - -# STL -if( EXISTS "${__libstl}" OR EXISTS "${__libsupcxx}" ) - if( EXISTS "${__libstl}" ) - set( CMAKE_CXX_CREATE_SHARED_LIBRARY "${CMAKE_CXX_CREATE_SHARED_LIBRARY} \"${__libstl}\"" ) - set( CMAKE_CXX_CREATE_SHARED_MODULE "${CMAKE_CXX_CREATE_SHARED_MODULE} \"${__libstl}\"" ) - set( CMAKE_CXX_LINK_EXECUTABLE "${CMAKE_CXX_LINK_EXECUTABLE} \"${__libstl}\"" ) - endif() - if( EXISTS "${__libsupcxx}" ) - set( CMAKE_CXX_CREATE_SHARED_LIBRARY "${CMAKE_CXX_CREATE_SHARED_LIBRARY} \"${__libsupcxx}\"" ) - set( CMAKE_CXX_CREATE_SHARED_MODULE "${CMAKE_CXX_CREATE_SHARED_MODULE} \"${__libsupcxx}\"" ) - set( CMAKE_CXX_LINK_EXECUTABLE "${CMAKE_CXX_LINK_EXECUTABLE} \"${__libsupcxx}\"" ) - # C objects: - set( CMAKE_C_CREATE_SHARED_LIBRARY " -o " ) - set( CMAKE_C_CREATE_SHARED_MODULE " -o " ) - set( CMAKE_C_LINK_EXECUTABLE " -o " ) - set( CMAKE_C_CREATE_SHARED_LIBRARY "${CMAKE_C_CREATE_SHARED_LIBRARY} \"${__libsupcxx}\"" ) - set( CMAKE_C_CREATE_SHARED_MODULE "${CMAKE_C_CREATE_SHARED_MODULE} \"${__libsupcxx}\"" ) - set( CMAKE_C_LINK_EXECUTABLE "${CMAKE_C_LINK_EXECUTABLE} \"${__libsupcxx}\"" ) - endif() - if( ANDROID_STL MATCHES "gnustl" ) - if( NOT EXISTS "${ANDROID_LIBM_PATH}" ) - set( ANDROID_LIBM_PATH -lm ) - endif() - set( CMAKE_CXX_CREATE_SHARED_LIBRARY "${CMAKE_CXX_CREATE_SHARED_LIBRARY} ${ANDROID_LIBM_PATH}" ) - set( CMAKE_CXX_CREATE_SHARED_MODULE "${CMAKE_CXX_CREATE_SHARED_MODULE} ${ANDROID_LIBM_PATH}" ) - set( CMAKE_CXX_LINK_EXECUTABLE "${CMAKE_CXX_LINK_EXECUTABLE} ${ANDROID_LIBM_PATH}" ) - endif() -endif() - -# variables controlling optional build flags -if( ANDROID_NDK_RELEASE_NUM LESS 7000 ) # before r7 - # libGLESv2.so in NDK's prior to r7 refers to missing external symbols. - # So this flag option is required for all projects using OpenGL from native. - __INIT_VARIABLE( ANDROID_SO_UNDEFINED VALUES ON ) -else() - __INIT_VARIABLE( ANDROID_SO_UNDEFINED VALUES OFF ) -endif() -__INIT_VARIABLE( ANDROID_NO_UNDEFINED VALUES ON ) -__INIT_VARIABLE( ANDROID_FUNCTION_LEVEL_LINKING VALUES ON ) -__INIT_VARIABLE( ANDROID_GOLD_LINKER VALUES ON ) -__INIT_VARIABLE( ANDROID_NOEXECSTACK VALUES ON ) -__INIT_VARIABLE( ANDROID_RELRO VALUES ON ) - -set( ANDROID_NO_UNDEFINED ${ANDROID_NO_UNDEFINED} CACHE BOOL "Show all undefined symbols as linker errors" ) -set( ANDROID_SO_UNDEFINED ${ANDROID_SO_UNDEFINED} CACHE BOOL "Allows or disallows undefined symbols in shared libraries" ) -set( ANDROID_FUNCTION_LEVEL_LINKING ${ANDROID_FUNCTION_LEVEL_LINKING} CACHE BOOL "Put each function in separate section and enable garbage collection of unused input sections at link time" ) -set( ANDROID_GOLD_LINKER ${ANDROID_GOLD_LINKER} CACHE BOOL "Enables gold linker" ) -set( ANDROID_NOEXECSTACK ${ANDROID_NOEXECSTACK} CACHE BOOL "Allows or disallows undefined symbols in shared libraries" ) -set( ANDROID_RELRO ${ANDROID_RELRO} CACHE BOOL "Enables RELRO - a memory corruption mitigation technique" ) -mark_as_advanced( ANDROID_NO_UNDEFINED ANDROID_SO_UNDEFINED ANDROID_FUNCTION_LEVEL_LINKING ANDROID_GOLD_LINKER ANDROID_NOEXECSTACK ANDROID_RELRO ) - -# linker flags -set( ANDROID_LINKER_FLAGS "" ) - -if( ARMEABI_V7A ) - # this is *required* to use the following linker flags that routes around - # a CPU bug in some Cortex-A8 implementations: - set( ANDROID_LINKER_FLAGS "${ANDROID_LINKER_FLAGS} -Wl,--fix-cortex-a8" ) -endif() - -if( ARMEABI_V7A_HARD ) - set( ANDROID_LINKER_FLAGS "${ANDROID_LINKER_FLAGS} -Wl,--no-warn-mismatch -lm_hard" ) -endif() - -if( ANDROID_NO_UNDEFINED ) - if( MIPS ) - # there is some sysroot-related problem in mips linker... - if( NOT ANDROID_SYSROOT MATCHES "[ ;\"]" ) - set( ANDROID_LINKER_FLAGS "${ANDROID_LINKER_FLAGS} -Wl,--no-undefined -Wl,-rpath-link,${ANDROID_SYSROOT}/usr/lib" ) - endif() - else() - set( ANDROID_LINKER_FLAGS "${ANDROID_LINKER_FLAGS} -Wl,--no-undefined" ) - endif() -endif() - -if( ANDROID_SO_UNDEFINED ) - set( ANDROID_LINKER_FLAGS "${ANDROID_LINKER_FLAGS} -Wl,-allow-shlib-undefined" ) -endif() - -if( ANDROID_FUNCTION_LEVEL_LINKING ) - set( ANDROID_CXX_FLAGS "${ANDROID_CXX_FLAGS} -fdata-sections -ffunction-sections" ) - set( ANDROID_LINKER_FLAGS "${ANDROID_LINKER_FLAGS} -Wl,--gc-sections" ) -endif() - -if( ANDROID_COMPILER_VERSION VERSION_EQUAL "4.6" ) - if( ANDROID_GOLD_LINKER AND (CMAKE_HOST_UNIX OR ANDROID_NDK_RELEASE_NUM GREATER 8002) AND (ARMEABI OR ARMEABI_V7A OR ARMEABI_V7A_HARD OR X86) ) - set( ANDROID_LINKER_FLAGS "${ANDROID_LINKER_FLAGS} -fuse-ld=gold" ) - elseif( ANDROID_NDK_RELEASE_NUM GREATER 8002 ) # after r8b - set( ANDROID_LINKER_FLAGS "${ANDROID_LINKER_FLAGS} -fuse-ld=bfd" ) - elseif( ANDROID_NDK_RELEASE STREQUAL "r8b" AND ARMEABI AND NOT _CMAKE_IN_TRY_COMPILE ) - message( WARNING "The default bfd linker from arm GCC 4.6 toolchain can fail with 'unresolvable R_ARM_THM_CALL relocation' error message. See https://code.google.com/p/android/issues/detail?id=35342 - On Linux and OS X host platform you can workaround this problem using gold linker (default). - Rerun cmake with -DANDROID_GOLD_LINKER=ON option in case of problems. -" ) - endif() -endif() # version 4.6 - -if( ANDROID_NOEXECSTACK ) - if( ANDROID_COMPILER_IS_CLANG ) - set( ANDROID_CXX_FLAGS "${ANDROID_CXX_FLAGS} -Xclang -mnoexecstack" ) - else() - set( ANDROID_CXX_FLAGS "${ANDROID_CXX_FLAGS} -Wa,--noexecstack" ) - endif() - set( ANDROID_LINKER_FLAGS "${ANDROID_LINKER_FLAGS} -Wl,-z,noexecstack" ) -endif() - -if( ANDROID_RELRO ) - set( ANDROID_LINKER_FLAGS "${ANDROID_LINKER_FLAGS} -Wl,-z,relro -Wl,-z,now" ) -endif() - -if( ANDROID_COMPILER_IS_CLANG ) - set( ANDROID_CXX_FLAGS "-target ${ANDROID_LLVM_TRIPLE} -Qunused-arguments ${ANDROID_CXX_FLAGS}" ) - if( BUILD_WITH_ANDROID_NDK ) - set( ANDROID_CXX_FLAGS "-gcc-toolchain ${ANDROID_TOOLCHAIN_ROOT} ${ANDROID_CXX_FLAGS}" ) - endif() -endif() - -# cache flags -set( CMAKE_CXX_FLAGS "" CACHE STRING "c++ flags" ) -set( CMAKE_C_FLAGS "" CACHE STRING "c flags" ) -set( CMAKE_CXX_FLAGS_RELEASE "-O3 -DNDEBUG" CACHE STRING "c++ Release flags" ) -set( CMAKE_C_FLAGS_RELEASE "-O3 -DNDEBUG" CACHE STRING "c Release flags" ) -set( CMAKE_CXX_FLAGS_DEBUG "-O0 -g -DDEBUG -D_DEBUG" CACHE STRING "c++ Debug flags" ) -set( CMAKE_C_FLAGS_DEBUG "-O0 -g -DDEBUG -D_DEBUG" CACHE STRING "c Debug flags" ) -set( CMAKE_SHARED_LINKER_FLAGS "" CACHE STRING "shared linker flags" ) -set( CMAKE_MODULE_LINKER_FLAGS "" CACHE STRING "module linker flags" ) -set( CMAKE_EXE_LINKER_FLAGS "-Wl,-z,nocopyreloc" CACHE STRING "executable linker flags" ) - -# put flags to cache (for debug purpose only) -set( ANDROID_CXX_FLAGS "${ANDROID_CXX_FLAGS}" CACHE INTERNAL "Android specific c/c++ flags" ) -set( ANDROID_CXX_FLAGS_RELEASE "${ANDROID_CXX_FLAGS_RELEASE}" CACHE INTERNAL "Android specific c/c++ Release flags" ) -set( ANDROID_CXX_FLAGS_DEBUG "${ANDROID_CXX_FLAGS_DEBUG}" CACHE INTERNAL "Android specific c/c++ Debug flags" ) -set( ANDROID_LINKER_FLAGS "${ANDROID_LINKER_FLAGS}" CACHE INTERNAL "Android specific c/c++ linker flags" ) - -# finish flags -set( CMAKE_CXX_FLAGS "${ANDROID_CXX_FLAGS} ${CMAKE_CXX_FLAGS}" ) -set( CMAKE_C_FLAGS "${ANDROID_CXX_FLAGS} ${CMAKE_C_FLAGS}" ) -set( CMAKE_CXX_FLAGS_RELEASE "${ANDROID_CXX_FLAGS_RELEASE} ${CMAKE_CXX_FLAGS_RELEASE}" ) -set( CMAKE_C_FLAGS_RELEASE "${ANDROID_CXX_FLAGS_RELEASE} ${CMAKE_C_FLAGS_RELEASE}" ) -set( CMAKE_CXX_FLAGS_DEBUG "${ANDROID_CXX_FLAGS_DEBUG} ${CMAKE_CXX_FLAGS_DEBUG}" ) -set( CMAKE_C_FLAGS_DEBUG "${ANDROID_CXX_FLAGS_DEBUG} ${CMAKE_C_FLAGS_DEBUG}" ) -set( CMAKE_SHARED_LINKER_FLAGS "${ANDROID_LINKER_FLAGS} ${CMAKE_SHARED_LINKER_FLAGS}" ) -set( CMAKE_MODULE_LINKER_FLAGS "${ANDROID_LINKER_FLAGS} ${CMAKE_MODULE_LINKER_FLAGS}" ) -set( CMAKE_EXE_LINKER_FLAGS "${ANDROID_LINKER_FLAGS} ${CMAKE_EXE_LINKER_FLAGS}" ) - -if( MIPS AND BUILD_WITH_ANDROID_NDK AND ANDROID_NDK_RELEASE STREQUAL "r8" ) - set( CMAKE_SHARED_LINKER_FLAGS "-Wl,-T,${ANDROID_NDK_TOOLCHAINS_PATH}/${ANDROID_GCC_TOOLCHAIN_NAME}/mipself.xsc ${CMAKE_SHARED_LINKER_FLAGS}" ) - set( CMAKE_MODULE_LINKER_FLAGS "-Wl,-T,${ANDROID_NDK_TOOLCHAINS_PATH}/${ANDROID_GCC_TOOLCHAIN_NAME}/mipself.xsc ${CMAKE_MODULE_LINKER_FLAGS}" ) - set( CMAKE_EXE_LINKER_FLAGS "-Wl,-T,${ANDROID_NDK_TOOLCHAINS_PATH}/${ANDROID_GCC_TOOLCHAIN_NAME}/mipself.x ${CMAKE_EXE_LINKER_FLAGS}" ) -endif() - -# pie/pic -if( NOT (ANDROID_NATIVE_API_LEVEL LESS 16) AND (NOT DEFINED ANDROID_APP_PIE OR ANDROID_APP_PIE) AND (CMAKE_VERSION VERSION_GREATER 2.8.8) ) - set( CMAKE_POSITION_INDEPENDENT_CODE TRUE ) - set( CMAKE_EXE_LINKER_FLAGS "${CMAKE_EXE_LINKER_FLAGS} -fPIE -pie") -else() - set( CMAKE_POSITION_INDEPENDENT_CODE FALSE ) - set( CMAKE_CXX_FLAGS "-fpic ${CMAKE_CXX_FLAGS}" ) - set( CMAKE_C_FLAGS "-fpic ${CMAKE_C_FLAGS}" ) -endif() - -# configure rtti -if( DEFINED ANDROID_RTTI AND ANDROID_STL_FORCE_FEATURES ) - if( ANDROID_RTTI ) - set( CMAKE_CXX_FLAGS "-frtti ${CMAKE_CXX_FLAGS}" ) - else() - set( CMAKE_CXX_FLAGS "-fno-rtti ${CMAKE_CXX_FLAGS}" ) - endif() -endif() - -# configure exceptions -if( DEFINED ANDROID_EXCEPTIONS AND ANDROID_STL_FORCE_FEATURES ) - if( ANDROID_EXCEPTIONS ) - set( CMAKE_CXX_FLAGS "-fexceptions ${CMAKE_CXX_FLAGS}" ) - set( CMAKE_C_FLAGS "-fexceptions ${CMAKE_C_FLAGS}" ) - else() - set( CMAKE_CXX_FLAGS "-fno-exceptions ${CMAKE_CXX_FLAGS}" ) - set( CMAKE_C_FLAGS "-fno-exceptions ${CMAKE_C_FLAGS}" ) - endif() -endif() - -# global includes and link directories -include_directories( SYSTEM "${ANDROID_SYSROOT_INCLUDE}" ${ANDROID_STL_INCLUDE_DIRS} ) -get_filename_component(__android_install_path "${CMAKE_INSTALL_PREFIX}/libs/${ANDROID_NDK_ABI_NAME}" ABSOLUTE) # avoid CMP0015 policy warning -link_directories( "${__android_install_path}" ) -set( CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -DANDROID -D__ANDROID_API__=${ANDROID_NATIVE_API_LEVEL}" ) -set( CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -DANDROID -D__ANDROID_API__=${ANDROID_NATIVE_API_LEVEL}" ) - -# detect if need link crtbegin_so.o explicitly -if( NOT DEFINED ANDROID_EXPLICIT_CRT_LINK ) - set( __cmd "${CMAKE_CXX_CREATE_SHARED_LIBRARY}" ) - string( REPLACE "" "${CMAKE_CXX_COMPILER} ${CMAKE_CXX_COMPILER_ARG1}" __cmd "${__cmd}" ) - string( REPLACE "" "${CMAKE_C_COMPILER} ${CMAKE_C_COMPILER_ARG1}" __cmd "${__cmd}" ) - string( REPLACE "" "${CMAKE_CXX_FLAGS}" __cmd "${__cmd}" ) - string( REPLACE "" "" __cmd "${__cmd}" ) - string( REPLACE "" "${CMAKE_SHARED_LINKER_FLAGS}" __cmd "${__cmd}" ) - string( REPLACE "" "-shared" __cmd "${__cmd}" ) - string( REPLACE "" "" __cmd "${__cmd}" ) - string( REPLACE "" "" __cmd "${__cmd}" ) - string( REPLACE "" "${CMAKE_BINARY_DIR}${CMAKE_FILES_DIRECTORY}/toolchain_crtlink_test.so" __cmd "${__cmd}" ) - string( REPLACE "" "\"${ANDROID_SYSROOT}/usr/lib/crtbegin_so.o\"" __cmd "${__cmd}" ) - string( REPLACE "" "" __cmd "${__cmd}" ) - separate_arguments( __cmd ) - foreach( __var ANDROID_NDK ANDROID_NDK_TOOLCHAINS_PATH ANDROID_STANDALONE_TOOLCHAIN ) - if( ${__var} ) - set( __tmp "${${__var}}" ) - separate_arguments( __tmp ) - string( REPLACE "${__tmp}" "${${__var}}" __cmd "${__cmd}") - endif() - endforeach() - string( REPLACE "'" "" __cmd "${__cmd}" ) - string( REPLACE "\"" "" __cmd "${__cmd}" ) - execute_process( COMMAND ${__cmd} RESULT_VARIABLE __cmd_result OUTPUT_QUIET ERROR_QUIET ) - if( __cmd_result EQUAL 0 ) - set( ANDROID_EXPLICIT_CRT_LINK ON ) - else() - set( ANDROID_EXPLICIT_CRT_LINK OFF ) - endif() -endif() - -if( ANDROID_EXPLICIT_CRT_LINK ) - set( CMAKE_CXX_CREATE_SHARED_LIBRARY "${CMAKE_CXX_CREATE_SHARED_LIBRARY} \"${ANDROID_SYSROOT}/usr/lib/crtbegin_so.o\"" ) - set( CMAKE_CXX_CREATE_SHARED_MODULE "${CMAKE_CXX_CREATE_SHARED_MODULE} \"${ANDROID_SYSROOT}/usr/lib/crtbegin_so.o\"" ) -endif() - -# setup output directories -if(NOT DEFINED CMAKE_INSTALL_PREFIX) - set(CMAKE_INSTALL_PREFIX_INITIALIZED_TO_DEFAULT 1) -endif() -set( CMAKE_INSTALL_PREFIX "${ANDROID_TOOLCHAIN_ROOT}/user" CACHE STRING "path for installing" ) - -if( DEFINED LIBRARY_OUTPUT_PATH_ROOT - OR EXISTS "${CMAKE_SOURCE_DIR}/AndroidManifest.xml" - OR (EXISTS "${CMAKE_SOURCE_DIR}/../AndroidManifest.xml" AND EXISTS "${CMAKE_SOURCE_DIR}/../jni/") ) - set( LIBRARY_OUTPUT_PATH_ROOT ${CMAKE_SOURCE_DIR} CACHE PATH "Root for binaries output, set this to change where Android libs are installed to" ) - if( NOT _CMAKE_IN_TRY_COMPILE ) - if( EXISTS "${CMAKE_SOURCE_DIR}/jni/CMakeLists.txt" ) - set( EXECUTABLE_OUTPUT_PATH "${LIBRARY_OUTPUT_PATH_ROOT}/bin/${ANDROID_NDK_ABI_NAME}" CACHE PATH "Output directory for applications" ) - else() - set( EXECUTABLE_OUTPUT_PATH "${LIBRARY_OUTPUT_PATH_ROOT}/bin" CACHE PATH "Output directory for applications" ) - endif() - set( LIBRARY_OUTPUT_PATH "${LIBRARY_OUTPUT_PATH_ROOT}/libs/${ANDROID_NDK_ABI_NAME}" CACHE PATH "Output directory for Android libs" ) - endif() -endif() - -# copy shaed stl library to build directory -if( NOT _CMAKE_IN_TRY_COMPILE AND __libstl MATCHES "[.]so$" AND DEFINED LIBRARY_OUTPUT_PATH ) - get_filename_component( __libstlname "${__libstl}" NAME ) - execute_process( COMMAND "${CMAKE_COMMAND}" -E copy_if_different "${__libstl}" "${LIBRARY_OUTPUT_PATH}/${__libstlname}" RESULT_VARIABLE __fileCopyProcess ) - if( NOT __fileCopyProcess EQUAL 0 OR NOT EXISTS "${LIBRARY_OUTPUT_PATH}/${__libstlname}") - message( SEND_ERROR "Failed copying of ${__libstl} to the ${LIBRARY_OUTPUT_PATH}/${__libstlname}" ) - endif() - unset( __fileCopyProcess ) - unset( __libstlname ) -endif() - - -# set these global flags for cmake client scripts to change behavior -set( ANDROID True ) -set( BUILD_ANDROID True ) - -# where is the target environment -set( CMAKE_FIND_ROOT_PATH - "${ANDROID_TOOLCHAIN_ROOT}/bin" - "${ANDROID_TOOLCHAIN_ROOT}/${ANDROID_TOOLCHAIN_MACHINE_NAME}" - "${ANDROID_SYSROOT}" - "${ANDROID_NDK}/sysroot" # NDK16+ - "${CMAKE_INSTALL_PREFIX}" - "${CMAKE_INSTALL_PREFIX}/share" ) - -# only search for libraries and includes in the ndk toolchain -if(NOT CMAKE_FIND_ROOT_PATH_MODE_LIBRARY) - set( CMAKE_FIND_ROOT_PATH_MODE_LIBRARY ONLY ) -endif() - -if(NOT CMAKE_FIND_ROOT_PATH_MODE_INCLUDE) - set( CMAKE_FIND_ROOT_PATH_MODE_INCLUDE ONLY ) -endif() - -if(NOT CMAKE_FIND_ROOT_PATH_MODE_PACKAGE) - set( CMAKE_FIND_ROOT_PATH_MODE_PACKAGE ONLY ) -endif() - -if(NOT CMAKE_FIND_ROOT_PATH_MODE_PROGRAM) - set( CMAKE_FIND_ROOT_PATH_MODE_PROGRAM NEVER ) -endif() - -macro(__cmake_find_root_save_and_reset) - foreach(v - CMAKE_FIND_ROOT_PATH_MODE_LIBRARY - CMAKE_FIND_ROOT_PATH_MODE_INCLUDE - CMAKE_FIND_ROOT_PATH_MODE_PACKAGE - CMAKE_FIND_ROOT_PATH_MODE_PROGRAM - ) - set(__save_${v} ${${v}}) - set(${v} NEVER) - endforeach() -endmacro() - -macro(__cmake_find_root_restore) - foreach(v - CMAKE_FIND_ROOT_PATH_MODE_LIBRARY - CMAKE_FIND_ROOT_PATH_MODE_INCLUDE - CMAKE_FIND_ROOT_PATH_MODE_PACKAGE - CMAKE_FIND_ROOT_PATH_MODE_PROGRAM - ) - set(${v} ${__save_${v}}) - unset(__save_${v}) - endforeach() -endmacro() - -# macro to find packages on the host OS -macro( find_host_package ) - __cmake_find_root_save_and_reset() - if( CMAKE_HOST_WIN32 ) - SET( WIN32 1 ) - SET( UNIX ) - elseif( CMAKE_HOST_APPLE ) - SET( APPLE 1 ) - SET( UNIX ) - endif() - find_package( ${ARGN} ) - SET( WIN32 ) - SET( APPLE ) - SET( UNIX 1 ) - __cmake_find_root_restore() -endmacro() - - -# macro to find programs on the host OS -macro( find_host_program ) - __cmake_find_root_save_and_reset() - if( CMAKE_HOST_WIN32 ) - SET( WIN32 1 ) - SET( UNIX ) - elseif( CMAKE_HOST_APPLE ) - SET( APPLE 1 ) - SET( UNIX ) - endif() - find_program( ${ARGN} ) - SET( WIN32 ) - SET( APPLE ) - SET( UNIX 1 ) - __cmake_find_root_restore() -endmacro() - - -# export toolchain settings for the try_compile() command -if( NOT _CMAKE_IN_TRY_COMPILE ) - set( __toolchain_config "") - foreach( __var NDK_CCACHE LIBRARY_OUTPUT_PATH_ROOT ANDROID_FORBID_SYGWIN - ANDROID_NDK_HOST_X64 - ANDROID_NDK - ANDROID_NDK_LAYOUT - ANDROID_STANDALONE_TOOLCHAIN - ANDROID_TOOLCHAIN_NAME - ANDROID_ABI - ANDROID_NATIVE_API_LEVEL - ANDROID_STL - ANDROID_STL_FORCE_FEATURES - ANDROID_FORCE_ARM_BUILD - ANDROID_NO_UNDEFINED - ANDROID_SO_UNDEFINED - ANDROID_FUNCTION_LEVEL_LINKING - ANDROID_GOLD_LINKER - ANDROID_NOEXECSTACK - ANDROID_RELRO - ANDROID_LIBM_PATH - ANDROID_EXPLICIT_CRT_LINK - ANDROID_APP_PIE - ) - if( DEFINED ${__var} ) - if( ${__var} MATCHES " ") - set( __toolchain_config "${__toolchain_config}set( ${__var} \"${${__var}}\" CACHE INTERNAL \"\" )\n" ) - else() - set( __toolchain_config "${__toolchain_config}set( ${__var} ${${__var}} CACHE INTERNAL \"\" )\n" ) - endif() - endif() - endforeach() - file( WRITE "${CMAKE_BINARY_DIR}${CMAKE_FILES_DIRECTORY}/android.toolchain.config.cmake" "${__toolchain_config}" ) - unset( __toolchain_config ) -endif() - - -# force cmake to produce / instead of \ in build commands for Ninja generator -if( CMAKE_GENERATOR MATCHES "Ninja" AND CMAKE_HOST_WIN32 ) - # it is a bad hack after all - # CMake generates Ninja makefiles with UNIX paths only if it thinks that we are going to build with MinGW - set( CMAKE_COMPILER_IS_MINGW TRUE ) # tell CMake that we are MinGW - set( CMAKE_CROSSCOMPILING TRUE ) # stop recursion - enable_language( C ) - enable_language( CXX ) - # unset( CMAKE_COMPILER_IS_MINGW ) # can't unset because CMake does not convert back-slashes in response files without it - unset( MINGW ) -endif() - - -# Variables controlling behavior or set by cmake toolchain: -# ANDROID_ABI : "armeabi-v7a" (default), "armeabi", "armeabi-v7a with NEON", "armeabi-v7a-hard with NEON", "armeabi-v7a with VFPV3", "armeabi-v6 with VFP", "x86", "mips", "arm64-v8a", "x86_64", "mips64" -# ANDROID_NATIVE_API_LEVEL : 3,4,5,8,9,14,15,16,17,18,19,21 (depends on NDK version) -# ANDROID_STL : gnustl_static/gnustl_shared/stlport_static/stlport_shared/gabi++_static/gabi++_shared/system_re/system/none -# ANDROID_FORBID_SYGWIN : ON/OFF -# ANDROID_NO_UNDEFINED : ON/OFF -# ANDROID_SO_UNDEFINED : OFF/ON (default depends on NDK version) -# ANDROID_FUNCTION_LEVEL_LINKING : ON/OFF -# ANDROID_GOLD_LINKER : ON/OFF -# ANDROID_NOEXECSTACK : ON/OFF -# ANDROID_RELRO : ON/OFF -# ANDROID_FORCE_ARM_BUILD : ON/OFF -# ANDROID_STL_FORCE_FEATURES : ON/OFF -# ANDROID_LIBM_PATH : path to libm.so (set to something like $(TOP)/out/target/product//obj/lib/libm.so) to workaround unresolved `sincos` -# Can be set only at the first run: -# ANDROID_NDK : path to your NDK install -# NDK_CCACHE : path to your ccache executable -# ANDROID_TOOLCHAIN_NAME : the NDK name of compiler toolchain -# ANDROID_NDK_HOST_X64 : try to use x86_64 toolchain (default for x64 host systems) -# ANDROID_NDK_LAYOUT : the inner NDK structure (RELEASE, LINARO, ANDROID) -# LIBRARY_OUTPUT_PATH_ROOT : -# ANDROID_STANDALONE_TOOLCHAIN -# -# Primary read-only variables: -# ANDROID : always TRUE -# ARMEABI : TRUE for arm v6 and older devices -# ARMEABI_V6 : TRUE for arm v6 -# ARMEABI_V7A : TRUE for arm v7a -# ARMEABI_V7A_HARD : TRUE for arm v7a with hardfp -# ARM64_V8A : TRUE for arm64-v8a -# NEON : TRUE if NEON unit is enabled -# VFPV3 : TRUE if VFP version 3 is enabled -# X86 : TRUE if configured for x86 -# X86_64 : TRUE if configured for x86_64 -# MIPS : TRUE if configured for mips -# MIPS64 : TRUE if configured for mips64 -# BUILD_WITH_ANDROID_NDK : TRUE if NDK is used -# BUILD_WITH_STANDALONE_TOOLCHAIN : TRUE if standalone toolchain is used -# ANDROID_NDK_HOST_SYSTEM_NAME : "windows", "linux-x86" or "darwin-x86" depending on host platform -# ANDROID_NDK_ABI_NAME : "armeabi", "armeabi-v7a", "armeabi-v7a-hard", "x86", "mips", "arm64-v8a", "x86_64", "mips64" depending on ANDROID_ABI -# ANDROID_NDK_RELEASE : from r5 to r10d; set only for NDK -# ANDROID_NDK_RELEASE_NUM : numeric ANDROID_NDK_RELEASE version (1000*major+minor) -# ANDROID_ARCH_NAME : "arm", "x86", "mips", "arm64", "x86_64", "mips64" depending on ANDROID_ABI -# ANDROID_SYSROOT : path to the compiler sysroot -# ANDROID_SYSROOT_INCLUDE : paths to system include paths -# TOOL_OS_SUFFIX : "" or ".exe" depending on host platform -# ANDROID_COMPILER_IS_CLANG : TRUE if clang compiler is used -# -# Secondary (less stable) read-only variables: -# ANDROID_COMPILER_VERSION : GCC version used (not Clang version) -# ANDROID_CLANG_VERSION : version of clang compiler if clang is used -# ANDROID_CXX_FLAGS : C/C++ compiler flags required by Android platform -# ANDROID_SUPPORTED_ABIS : list of currently allowed values for ANDROID_ABI -# ANDROID_TOOLCHAIN_MACHINE_NAME : "arm-linux-androideabi", "arm-eabi" or "i686-android-linux" -# ANDROID_TOOLCHAIN_ROOT : path to the top level of toolchain (standalone or placed inside NDK) -# ANDROID_CLANG_TOOLCHAIN_ROOT : path to clang tools -# ANDROID_SUPPORTED_NATIVE_API_LEVELS : list of native API levels found inside NDK -# ANDROID_STL_INCLUDE_DIRS : stl include paths -# ANDROID_RTTI : if rtti is enabled by the runtime -# ANDROID_EXCEPTIONS : if exceptions are enabled by the runtime -# ANDROID_GCC_TOOLCHAIN_NAME : read-only, differs from ANDROID_TOOLCHAIN_NAME only if clang is used -# -# Defaults: -# ANDROID_DEFAULT_NDK_API_LEVEL -# ANDROID_DEFAULT_NDK_API_LEVEL_${ARCH} -# ANDROID_NDK_SEARCH_PATHS -# ANDROID_SUPPORTED_ABIS_${ARCH} -# ANDROID_SUPPORTED_NDK_VERSIONS diff --git a/platforms/android/build-tests/test_gradle.sh b/platforms/android/build-tests/test_gradle.sh index 9f1b233ff2..129d1480bc 100755 --- a/platforms/android/build-tests/test_gradle.sh +++ b/platforms/android/build-tests/test_gradle.sh @@ -1,5 +1,6 @@ #!/bin/bash -e SDK_DIR=$1 + echo "OpenCV Android SDK path: ${SDK_DIR}" ANDROID_HOME=${ANDROID_HOME:-${ANDROID_SDK_ROOT:-${ANDROID_SDK?Required ANDROID_HOME/ANDROID_SDK/ANDROID_SDK_ROOT}}} @@ -29,6 +30,10 @@ rm -rf "test-gradle" cp -rp "${SDK_DIR}" "test-gradle" echo "Cloning OpenCV Android SDK ... Done!" +# drop cmake bin name and "bin" folder from path +echo "ndk.dir=${ANDROID_NDK}" > "test-gradle/samples/local.properties" +echo "cmake.dir=$(dirname $(dirname $(which cmake)))" >> "test-gradle/samples/local.properties" + echo "Run gradle ..." (cd "test-gradle/samples"; ./gradlew -i assemble) diff --git a/platforms/android/build-tests/test_gradle_aar.sh b/platforms/android/build-tests/test_gradle_aar.sh new file mode 100755 index 0000000000..63f34514b1 --- /dev/null +++ b/platforms/android/build-tests/test_gradle_aar.sh @@ -0,0 +1,47 @@ +#!/bin/bash -e +SDK_DIR=$1 +LOCAL_MAVEN_REPO=$2 +echo "OpenCV Android SDK path: ${SDK_DIR}" +echo "Use local maven repo from $LOCAL_MAVEN_REPO" + +ANDROID_HOME=${ANDROID_HOME:-${ANDROID_SDK_ROOT:-${ANDROID_SDK?Required ANDROID_HOME/ANDROID_SDK/ANDROID_SDK_ROOT}}} +ANDROID_NDK=${ANDROID_NDK_HOME-${ANDROID_NDK:-${NDKROOT?Required ANDROID_NDK_HOME/ANDROID_NDK/NDKROOT}}} + +echo "Android SDK: ${ANDROID_HOME}" +echo "Android NDK: ${ANDROID_NDK}" + +if [ ! -d "${ANDROID_HOME}" ]; then + echo "FATAL: Missing Android SDK directory" + exit 1 +fi +if [ ! -d "${ANDROID_NDK}" ]; then + echo "FATAL: Missing Android NDK directory" + exit 1 +fi + +export ANDROID_HOME=${ANDROID_HOME} +export ANDROID_SDK=${ANDROID_HOME} +export ANDROID_SDK_ROOT=${ANDROID_HOME} + +export ANDROID_NDK=${ANDROID_NDK} +export ANDROID_NDK_HOME=${ANDROID_NDK} + +echo "Cloning OpenCV Android SDK ..." +rm -rf "test-gradle-aar" +mkdir test-gradle-aar +cp -rp ${SDK_DIR}/samples/* test-gradle-aar/ +echo "Cloning OpenCV Android SDK ... Done!" + +# drop cmake bin name and "bin" folder from path +echo "ndk.dir=${ANDROID_NDK}" > "test-gradle-aar/local.properties" +echo "cmake.dir=$(dirname $(dirname $(which cmake)))" >> "test-gradle-aar/local.properties" + +sed -i "s/opencv_source = 'sdk_path'/opencv_source = 'maven_local'/g" test-gradle-aar/settings.gradle +sed -i "s+opencv_maven_path = ''+opencv_maven_path = 'file\\://$LOCAL_MAVEN_REPO'+g" test-gradle-aar/settings.gradle + +echo "Run gradle ..." +(cd "test-gradle-aar"; ./gradlew -i assemble) + +echo "#" +echo "# Done!" +echo "#" diff --git a/platforms/android/build_aar.sh b/platforms/android/build_aar.sh new file mode 100755 index 0000000000..9af3cd0941 --- /dev/null +++ b/platforms/android/build_aar.sh @@ -0,0 +1,45 @@ +#!/bin/bash -e +SDK_DIR=$1 + +echo "OpenCV Android SDK path: ${SDK_DIR}" + +ANDROID_HOME=${ANDROID_HOME:-${ANDROID_SDK_ROOT:-${ANDROID_SDK?Required ANDROID_HOME/ANDROID_SDK/ANDROID_SDK_ROOT}}} +ANDROID_NDK=${ANDROID_NDK_HOME-${ANDROID_NDK:-${NDKROOT?Required ANDROID_NDK_HOME/ANDROID_NDK/NDKROOT}}} + +echo "Android SDK: ${ANDROID_HOME}" +echo "Android NDK: ${ANDROID_NDK}" + +if [ ! -d "${ANDROID_HOME}" ]; then + echo "FATAL: Missing Android SDK directory" + exit 1 +fi +if [ ! -d "${ANDROID_NDK}" ]; then + echo "FATAL: Missing Android NDK directory" + exit 1 +fi + +export ANDROID_HOME=${ANDROID_HOME} +export ANDROID_SDK=${ANDROID_HOME} +export ANDROID_SDK_ROOT=${ANDROID_HOME} + +export ANDROID_NDK=${ANDROID_NDK} +export ANDROID_NDK_HOME=${ANDROID_NDK} + +echo "Cloning OpenCV Android SDK ..." +rm -rf "aar-build" +cp -rp "${SDK_DIR}" "aar-build" +echo "Cloning OpenCV Android SDK ... Done!" + +# drop cmake bin name and "bin" folder from path +echo "ndk.dir=${ANDROID_NDK}" > "aar-build/samples/local.properties" +echo "cmake.dir=$(dirname $(dirname $(which cmake)))" >> "aar-build/samples/local.properties" + +echo "Run gradle ..." +(cd "aar-build/samples"; ./gradlew ${OPENCV_GRADLE_VERBOSE_OPTIONS:--i} opencv:publishReleasePublicationToMyrepoRepository) + +mkdir "maven_repo" +cp -r aar-build/sdk/build/repo/* ./maven_repo/ + +echo "#" +echo "# Done!" +echo "#" diff --git a/platforms/android/build_java_shared_aar.py b/platforms/android/build_java_shared_aar.py new file mode 100755 index 0000000000..e99c78ec28 --- /dev/null +++ b/platforms/android/build_java_shared_aar.py @@ -0,0 +1,168 @@ +#!/usr/bin/env python + +import argparse +from os import path +import os +import re +import shutil +import string +import subprocess + + +COPY_FROM_SDK_TO_ANDROID_PROJECT = [ + ["sdk/native/jni/include", "OpenCV/src/main/cpp/include"], + ["sdk/java/src/org", "OpenCV/src/main/java/org"], + ["sdk/java/res", "OpenCV/src/main/res"] +] + +COPY_FROM_SDK_TO_APK = [ + ["sdk/native/libs//lib.so", "jni//lib.so"], + ["sdk/native/libs//lib.so", "prefab/modules//libs/android./lib.so"], +] + +ANDROID_PROJECT_TEMPLATE_DIR = path.join(path.dirname(__file__), "aar-template") +TEMP_DIR = "build_java_shared" +ANDROID_PROJECT_DIR = path.join(TEMP_DIR, "AndroidProject") +COMPILED_AAR_PATH_1 = path.join(ANDROID_PROJECT_DIR, "OpenCV/build/outputs/aar/OpenCV-release.aar") # original package name +COMPILED_AAR_PATH_2 = path.join(ANDROID_PROJECT_DIR, "OpenCV/build/outputs/aar/opencv-release.aar") # lower case package name +AAR_UNZIPPED_DIR = path.join(TEMP_DIR, "aar_unzipped") +FINAL_AAR_PATH_TEMPLATE = "outputs/opencv_java_shared_.aar" +FINAL_REPO_PATH = "outputs/maven_repo" +MAVEN_PACKAGE_NAME = "opencv" + +def fill_template(src_path, dst_path, args_dict): + with open(src_path, "r") as f: + template_text = f.read() + template = string.Template(template_text) + text = template.safe_substitute(args_dict) + with open(dst_path, "w") as f: + f.write(text) + +def get_opencv_version(opencv_sdk_path): + version_hpp_path = path.join(opencv_sdk_path, "sdk/native/jni/include/opencv2/core/version.hpp") + with open(version_hpp_path, "rt") as f: + data = f.read() + major = re.search(r'^#define\W+CV_VERSION_MAJOR\W+(\d+)$', data, re.MULTILINE).group(1) + minor = re.search(r'^#define\W+CV_VERSION_MINOR\W+(\d+)$', data, re.MULTILINE).group(1) + revision = re.search(r'^#define\W+CV_VERSION_REVISION\W+(\d+)$', data, re.MULTILINE).group(1) + return "%(major)s.%(minor)s.%(revision)s" % locals() + +def get_compiled_aar_path(path1, path2): + if path.exists(path1): + return path1 + elif path.exists(path2): + return path2 + else: + raise Exception("Can't find compiled AAR path in [" + path1 + ", " + path2 + "]") + +def cleanup(paths_to_remove): + exists = False + for p in paths_to_remove: + if path.exists(p): + exists = True + if path.isdir(p): + shutil.rmtree(p) + else: + os.remove(p) + print("Removed", p) + if not exists: + print("Nothing to remove") + +def main(args): + opencv_version = get_opencv_version(args.opencv_sdk_path) + abis = os.listdir(path.join(args.opencv_sdk_path, "sdk/native/libs")) + lib_name = "opencv_java" + opencv_version.split(".")[0] + final_aar_path = FINAL_AAR_PATH_TEMPLATE.replace("", opencv_version) + + print("Removing data from previous runs...") + cleanup([TEMP_DIR, final_aar_path, path.join(FINAL_REPO_PATH, "org/opencv", MAVEN_PACKAGE_NAME)]) + + print("Preparing Android project...") + # ANDROID_PROJECT_TEMPLATE_DIR contains an Android project template that creates AAR + shutil.copytree(ANDROID_PROJECT_TEMPLATE_DIR, ANDROID_PROJECT_DIR) + + # Configuring the Android project to Java + shared C++ lib version + shutil.rmtree(path.join(ANDROID_PROJECT_DIR, "OpenCV/src/main/cpp/include")) + + fill_template(path.join(ANDROID_PROJECT_DIR, "OpenCV/build.gradle.template"), + path.join(ANDROID_PROJECT_DIR, "OpenCV/build.gradle"), + {"LIB_NAME": lib_name, + "LIB_TYPE": "c++_shared", + "PACKAGE_NAME": MAVEN_PACKAGE_NAME, + "OPENCV_VERSION": opencv_version, + "COMPILE_SDK": args.android_compile_sdk, + "MIN_SDK": args.android_min_sdk, + "TARGET_SDK": args.android_target_sdk, + "ABI_FILTERS": ", ".join(['"' + x + '"' for x in abis]), + "JAVA_VERSION": args.java_version, + }) + fill_template(path.join(ANDROID_PROJECT_DIR, "OpenCV/src/main/cpp/CMakeLists.txt.template"), + path.join(ANDROID_PROJECT_DIR, "OpenCV/src/main/cpp/CMakeLists.txt"), + {"LIB_NAME": lib_name, "LIB_TYPE": "SHARED"}) + + local_props = "" + if args.ndk_location: + local_props += "ndk.dir=" + args.ndk_location + "\n" + if args.cmake_location: + local_props += "cmake.dir=" + args.cmake_location + "\n" + + if local_props: + with open(path.join(ANDROID_PROJECT_DIR, "local.properties"), "wt") as f: + f.write(local_props) + + # Copying Java code and C++ public headers from SDK to the Android project + for src, dst in COPY_FROM_SDK_TO_ANDROID_PROJECT: + shutil.copytree(path.join(args.opencv_sdk_path, src), + path.join(ANDROID_PROJECT_DIR, dst)) + + print("Running gradle assembleRelease...") + # Running gradle to build the Android project + subprocess.run(["./gradlew", "assembleRelease"], + shell=False, + cwd=ANDROID_PROJECT_DIR, + check=True) + + print("Adding libs to AAR...") + # The created AAR package doesn't contain C++ shared libs. + # We need to add them manually. + # AAR package is just a zip archive. + complied_aar_path = get_compiled_aar_path(COMPILED_AAR_PATH_1, COMPILED_AAR_PATH_2) # two possible paths + shutil.unpack_archive(complied_aar_path, AAR_UNZIPPED_DIR, "zip") + + for abi in abis: + for src, dst in COPY_FROM_SDK_TO_APK: + src = src.replace("", abi).replace("", lib_name) + dst = dst.replace("", abi).replace("", lib_name) + shutil.copy(path.join(args.opencv_sdk_path, src), + path.join(AAR_UNZIPPED_DIR, dst)) + + # Creating final AAR zip archive + os.makedirs("outputs", exist_ok=True) + shutil.make_archive(final_aar_path, "zip", AAR_UNZIPPED_DIR, ".") + os.rename(final_aar_path + ".zip", final_aar_path) + + print("Creating local maven repo...") + + shutil.copy(final_aar_path, path.join(ANDROID_PROJECT_DIR, "OpenCV/opencv-release.aar")) + subprocess.run(["./gradlew", "publishReleasePublicationToMyrepoRepository"], + shell=False, + cwd=ANDROID_PROJECT_DIR, + check=True) + + os.makedirs(path.join(FINAL_REPO_PATH, "org/opencv"), exist_ok=True) + shutil.move(path.join(ANDROID_PROJECT_DIR, "OpenCV/build/repo/org/opencv", MAVEN_PACKAGE_NAME), + path.join(FINAL_REPO_PATH, "org/opencv", MAVEN_PACKAGE_NAME)) + + +if __name__ == "__main__": + parser = argparse.ArgumentParser(description="Builds AAR with Java and shared C++ libs from OpenCV SDK") + parser.add_argument('opencv_sdk_path') + parser.add_argument('--android_compile_sdk', default="31") + parser.add_argument('--android_min_sdk', default="21") + parser.add_argument('--android_target_sdk', default="31") + parser.add_argument('--java_version', default="1_8") + parser.add_argument('--ndk_location', default="") + parser.add_argument('--cmake_location', default="") + args = parser.parse_args() + + main(args) diff --git a/platforms/android/build_sdk.py b/platforms/android/build_sdk.py index f445be10f6..f27a392f7e 100755 --- a/platforms/android/build_sdk.py +++ b/platforms/android/build_sdk.py @@ -215,7 +215,7 @@ class Builder: for d in ["CMakeCache.txt", "CMakeFiles/", "bin/", "libs/", "lib/", "package/", "install/samples/"]: rm_one(d) - def build_library(self, abi, do_install): + def build_library(self, abi, do_install, no_media_ndk): cmd = [self.cmake_path, "-GNinja"] cmake_vars = dict( CMAKE_TOOLCHAIN_FILE=self.get_toolchain_file(), @@ -260,6 +260,9 @@ class Builder: if do_install: cmd.extend(["-DBUILD_TESTS=ON", "-DINSTALL_TESTS=ON"]) + if no_media_ndk: + cmake_vars['WITH_ANDROID_MEDIANDK'] = "OFF" + cmake_vars.update(abi.cmake_vars) cmd += [ "-D%s='%s'" % (k, v) for (k, v) in cmake_vars.items() if v is not None] cmd.append(self.opencvdir) @@ -270,7 +273,7 @@ class Builder: if self.no_samples_build: execute([self.ninja_path, "install" if (self.debug_info or self.debug) else "install/strip"]) else: - execute([self.ninja_path, "-j1" if (self.debug_info or self.debug) else "-j3", "install" if (self.debug_info or self.debug) else "install/strip"]) + execute([self.ninja_path, "-j1", "install" if (self.debug_info or self.debug) else "install/strip"]) def build_javadoc(self): classpaths = [] @@ -370,6 +373,7 @@ if __name__ == "__main__": parser.add_argument('--opencl', action="store_true", help="Enable OpenCL support") parser.add_argument('--no_kotlin', action="store_true", help="Disable Kotlin extensions") parser.add_argument('--shared', action="store_true", help="Build shared libraries") + parser.add_argument('--no_media_ndk', action="store_true", help="Do not link Media NDK (required for video I/O support)") args = parser.parse_args() log.basicConfig(format='%(message)s', level=log.DEBUG) @@ -447,7 +451,7 @@ if __name__ == "__main__": os.chdir(builder.libdest) builder.clean_library_build_dir() - builder.build_library(abi, do_install) + builder.build_library(abi, do_install, args.no_media_ndk) builder.gather_results() diff --git a/platforms/android/build_static_aar.py b/platforms/android/build_static_aar.py new file mode 100755 index 0000000000..c1ab4046f4 --- /dev/null +++ b/platforms/android/build_static_aar.py @@ -0,0 +1,241 @@ +#!/usr/bin/env python + +import argparse +import json +from os import path +import os +import shutil +import subprocess + +from build_java_shared_aar import cleanup, fill_template, get_compiled_aar_path, get_opencv_version + + +ANDROID_PROJECT_TEMPLATE_DIR = path.join(path.dirname(__file__), "aar-template") +TEMP_DIR = "build_static" +ANDROID_PROJECT_DIR = path.join(TEMP_DIR, "AndroidProject") +COMPILED_AAR_PATH_1 = path.join(ANDROID_PROJECT_DIR, "OpenCV/build/outputs/aar/OpenCV-release.aar") # original package name +COMPILED_AAR_PATH_2 = path.join(ANDROID_PROJECT_DIR, "OpenCV/build/outputs/aar/opencv-release.aar") # lower case package name +AAR_UNZIPPED_DIR = path.join(TEMP_DIR, "aar_unzipped") +FINAL_AAR_PATH_TEMPLATE = "outputs/opencv_static_.aar" +FINAL_REPO_PATH = "outputs/maven_repo" +MAVEN_PACKAGE_NAME = "opencv-static" + + +def get_list_of_opencv_libs(sdk_dir): + files = os.listdir(path.join(sdk_dir, "sdk/native/staticlibs/arm64-v8a")) + libs = [f[3:-2] for f in files if f[:3] == "lib" and f[-2:] == ".a"] + return libs + +def get_list_of_3rdparty_libs(sdk_dir, abis): + libs = [] + for abi in abis: + files = os.listdir(path.join(sdk_dir, "sdk/native/3rdparty/libs/" + abi)) + cur_libs = [f[3:-2] for f in files if f[:3] == "lib" and f[-2:] == ".a"] + for lib in cur_libs: + if lib not in libs: + libs.append(lib) + return libs + +def add_printing_linked_libs(sdk_dir, opencv_libs): + """ + Modifies CMakeLists.txt file in Android project, so it prints linked libraries for each OpenCV library" + """ + sdk_jni_dir = sdk_dir + "/sdk/native/jni" + with open(path.join(ANDROID_PROJECT_DIR, "OpenCV/src/main/cpp/CMakeLists.txt"), "a") as f: + f.write('\nset(OpenCV_DIR "' + sdk_jni_dir + '")\n') + f.write('find_package(OpenCV REQUIRED)\n') + for lib_name in opencv_libs: + output_filename_prefix = "linkedlibs." + lib_name + "." + f.write('get_target_property(OUT "' + lib_name + '" INTERFACE_LINK_LIBRARIES)\n') + f.write('file(WRITE "' + output_filename_prefix + '${ANDROID_ABI}.txt" "${OUT}")\n') + +def read_linked_libs(lib_name, abis): + """ + Reads linked libs for each OpenCV library from files, that was generated by gradle. See add_printing_linked_libs() + """ + deps_lists = [] + for abi in abis: + with open(path.join(ANDROID_PROJECT_DIR, "OpenCV/src/main/cpp", f"linkedlibs.{lib_name}.{abi}.txt")) as f: + text = f.read() + linked_libs = text.split(";") + linked_libs = [x.replace("$", "") for x in linked_libs] + deps_lists.append(linked_libs) + + return merge_dependencies_lists(deps_lists) + +def merge_dependencies_lists(deps_lists): + """ + One library may have different dependencies for different ABIS. + We need to merge them into one list with all the dependencies preserving the order. + """ + result = [] + for d_list in deps_lists: + for i in range(len(d_list)): + if d_list[i] not in result: + if i == 0: + result.append(d_list[i]) + else: + index = result.index(d_list[i-1]) + result = result[:index + 1] + [d_list[i]] + result[index + 1:] + + return result + +def convert_deps_list_to_prefab(linked_libs, opencv_libs, external_libs): + """ + Converting list of dependencies into prefab format. + """ + prefab_linked_libs = [] + for lib in linked_libs: + if (lib in opencv_libs) or (lib in external_libs): + prefab_linked_libs.append(":" + lib) + elif (lib[:3] == "lib" and lib[3:] in external_libs): + prefab_linked_libs.append(":" + lib[3:]) + elif lib == "ocv.3rdparty.android_mediandk": + prefab_linked_libs += ["-landroid", "-llog", "-lmediandk"] + print("Warning: manualy handled ocv.3rdparty.android_mediandk dependency") + elif lib == "ocv.3rdparty.flatbuffers": + print("Warning: manualy handled ocv.3rdparty.flatbuffers dependency") + elif lib.startswith("ocv.3rdparty"): + raise Exception("Unknown lib " + lib) + else: + prefab_linked_libs.append("-l" + lib) + return prefab_linked_libs + +def main(args): + opencv_version = get_opencv_version(args.opencv_sdk_path) + abis = os.listdir(path.join(args.opencv_sdk_path, "sdk/native/libs")) + final_aar_path = FINAL_AAR_PATH_TEMPLATE.replace("", opencv_version) + sdk_dir = args.opencv_sdk_path + + print("Removing data from previous runs...") + cleanup([TEMP_DIR, final_aar_path, path.join(FINAL_REPO_PATH, "org/opencv", MAVEN_PACKAGE_NAME)]) + + print("Preparing Android project...") + # ANDROID_PROJECT_TEMPLATE_DIR contains an Android project template that creates AAR + shutil.copytree(ANDROID_PROJECT_TEMPLATE_DIR, ANDROID_PROJECT_DIR) + + # Configuring the Android project to static C++ libs version + fill_template(path.join(ANDROID_PROJECT_DIR, "OpenCV/build.gradle.template"), + path.join(ANDROID_PROJECT_DIR, "OpenCV/build.gradle"), + {"LIB_NAME": "templib", + "LIB_TYPE": "c++_static", + "PACKAGE_NAME": MAVEN_PACKAGE_NAME, + "OPENCV_VERSION": opencv_version, + "COMPILE_SDK": args.android_compile_sdk, + "MIN_SDK": args.android_min_sdk, + "TARGET_SDK": args.android_target_sdk, + "ABI_FILTERS": ", ".join(['"' + x + '"' for x in abis]), + "JAVA_VERSION": args.java_version, + }) + fill_template(path.join(ANDROID_PROJECT_DIR, "OpenCV/src/main/cpp/CMakeLists.txt.template"), + path.join(ANDROID_PROJECT_DIR, "OpenCV/src/main/cpp/CMakeLists.txt"), + {"LIB_NAME": "templib", "LIB_TYPE": "STATIC"}) + + local_props = "" + if args.ndk_location: + local_props += "ndk.dir=" + args.ndk_location + "\n" + if args.cmake_location: + local_props += "cmake.dir=" + args.cmake_location + "\n" + + if local_props: + with open(path.join(ANDROID_PROJECT_DIR, "local.properties"), "wt") as f: + f.write(local_props) + + opencv_libs = get_list_of_opencv_libs(sdk_dir) + external_libs = get_list_of_3rdparty_libs(sdk_dir, abis) + + add_printing_linked_libs(sdk_dir, opencv_libs) + + print("Running gradle assembleRelease...") + # Running gradle to build the Android project + subprocess.run(["./gradlew", "assembleRelease"], + shell=False, + cwd=ANDROID_PROJECT_DIR, + check=True) + + # The created AAR package contains only one empty libtemplib.a library. + # We need to add OpenCV libraries manually. + # AAR package is just a zip archive + complied_aar_path = get_compiled_aar_path(COMPILED_AAR_PATH_1, COMPILED_AAR_PATH_2) # two possible paths + shutil.unpack_archive(complied_aar_path, AAR_UNZIPPED_DIR, "zip") + + print("Adding libs to AAR...") + + # Copying 3rdparty libs from SDK into the AAR + for lib in external_libs: + for abi in abis: + os.makedirs(path.join(AAR_UNZIPPED_DIR, "prefab/modules/" + lib + "/libs/android." + abi)) + if path.exists(path.join(sdk_dir, "sdk/native/3rdparty/libs/" + abi, "lib" + lib + ".a")): + shutil.copy(path.join(sdk_dir, "sdk/native/3rdparty/libs/" + abi, "lib" + lib + ".a"), + path.join(AAR_UNZIPPED_DIR, "prefab/modules/" + lib + "/libs/android." + abi, "lib" + lib + ".a")) + else: + # One OpenCV library may have different dependency lists for different ABIs, but we can write only one + # full dependency list for all ABIs. So we just add empty .a library if this ABI doesn't have this dependency. + shutil.copy(path.join(AAR_UNZIPPED_DIR, "prefab/modules/templib/libs/android." + abi, "libtemplib.a"), + path.join(AAR_UNZIPPED_DIR, "prefab/modules/" + lib + "/libs/android." + abi, "lib" + lib + ".a")) + shutil.copy(path.join(AAR_UNZIPPED_DIR, "prefab/modules/templib/libs/android." + abi + "/abi.json"), + path.join(AAR_UNZIPPED_DIR, "prefab/modules/" + lib + "/libs/android." + abi + "/abi.json")) + shutil.copy(path.join(AAR_UNZIPPED_DIR, "prefab/modules/templib/module.json"), + path.join(AAR_UNZIPPED_DIR, "prefab/modules/" + lib + "/module.json")) + + # Copying OpenV libs from SDK into the AAR + for lib in opencv_libs: + for abi in abis: + os.makedirs(path.join(AAR_UNZIPPED_DIR, "prefab/modules/" + lib + "/libs/android." + abi)) + shutil.copy(path.join(sdk_dir, "sdk/native/staticlibs/" + abi, "lib" + lib + ".a"), + path.join(AAR_UNZIPPED_DIR, "prefab/modules/" + lib + "/libs/android." + abi, "lib" + lib + ".a")) + shutil.copy(path.join(AAR_UNZIPPED_DIR, "prefab/modules/templib/libs/android." + abi + "/abi.json"), + path.join(AAR_UNZIPPED_DIR, "prefab/modules/" + lib + "/libs/android." + abi + "/abi.json")) + os.makedirs(path.join(AAR_UNZIPPED_DIR, "prefab/modules/" + lib + "/include/opencv2")) + shutil.copy(path.join(sdk_dir, "sdk/native/jni/include/opencv2/" + lib.replace("opencv_", "") + ".hpp"), + path.join(AAR_UNZIPPED_DIR, "prefab/modules/" + lib + "/include/opencv2/" + lib.replace("opencv_", "") + ".hpp")) + shutil.copytree(path.join(sdk_dir, "sdk/native/jni/include/opencv2/" + lib.replace("opencv_", "")), + path.join(AAR_UNZIPPED_DIR, "prefab/modules/" + lib + "/include/opencv2/" + lib.replace("opencv_", ""))) + + # Adding dependencies list + module_json_text = { + "export_libraries": convert_deps_list_to_prefab(read_linked_libs(lib, abis), opencv_libs, external_libs), + "android": {}, + } + with open(path.join(AAR_UNZIPPED_DIR, "prefab/modules/" + lib + "/module.json"), "w") as f: + json.dump(module_json_text, f) + + for h_file in ("cvconfig.h", "opencv.hpp", "opencv_modules.hpp"): + shutil.copy(path.join(sdk_dir, "sdk/native/jni/include/opencv2/" + h_file), + path.join(AAR_UNZIPPED_DIR, "prefab/modules/opencv_core/include/opencv2/" + h_file)) + + + shutil.rmtree(path.join(AAR_UNZIPPED_DIR, "prefab/modules/templib")) + + # Creating final AAR zip archive + os.makedirs("outputs", exist_ok=True) + shutil.make_archive(final_aar_path, "zip", AAR_UNZIPPED_DIR, ".") + os.rename(final_aar_path + ".zip", final_aar_path) + + print("Creating local maven repo...") + + shutil.copy(final_aar_path, path.join(ANDROID_PROJECT_DIR, "OpenCV/opencv-release.aar")) + + subprocess.run(["./gradlew", "publishReleasePublicationToMyrepoRepository"], + shell=False, + cwd=ANDROID_PROJECT_DIR, + check=True) + + os.makedirs(path.join(FINAL_REPO_PATH, "org/opencv"), exist_ok=True) + shutil.move(path.join(ANDROID_PROJECT_DIR, "OpenCV/build/repo/org/opencv", MAVEN_PACKAGE_NAME), + path.join(FINAL_REPO_PATH, "org/opencv", MAVEN_PACKAGE_NAME)) + print("Done") + + +if __name__ == "__main__": + parser = argparse.ArgumentParser(description="Builds AAR with static C++ libs from OpenCV SDK") + parser.add_argument('opencv_sdk_path') + parser.add_argument('--android_compile_sdk', default="31") + parser.add_argument('--android_min_sdk', default="21") + parser.add_argument('--android_target_sdk', default="31") + parser.add_argument('--java_version', default="1_8") + parser.add_argument('--ndk_location', default="") + parser.add_argument('--cmake_location', default="") + args = parser.parse_args() + + main(args) diff --git a/platforms/android/service/CMakeLists.txt b/platforms/android/service/CMakeLists.txt deleted file mode 100644 index 66e0c468a9..0000000000 --- a/platforms/android/service/CMakeLists.txt +++ /dev/null @@ -1,10 +0,0 @@ -if(NOT ANDROID_PROJECTS_BUILD_TYPE STREQUAL "ANT") - message(STATUS "Android OpenCV Manager is ignored") - return() -endif() - -if(BUILD_ANDROID_SERVICE) - add_subdirectory(engine) -endif() - -install(FILES "readme.txt" DESTINATION "apk/" COMPONENT libs) diff --git a/platforms/android/service/engine/AndroidManifest.xml b/platforms/android/service/engine/AndroidManifest.xml deleted file mode 100644 index 660152ed29..0000000000 --- a/platforms/android/service/engine/AndroidManifest.xml +++ /dev/null @@ -1,30 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - diff --git a/platforms/android/service/engine/CMakeLists.txt b/platforms/android/service/engine/CMakeLists.txt deleted file mode 100644 index a31790a903..0000000000 --- a/platforms/android/service/engine/CMakeLists.txt +++ /dev/null @@ -1,3 +0,0 @@ -configure_file("${CMAKE_CURRENT_SOURCE_DIR}/${ANDROID_MANIFEST_FILE}" "${OpenCV_BINARY_DIR}/platforms/android/service/engine/.build/${ANDROID_MANIFEST_FILE}" @ONLY) -unset(__android_project_chain CACHE) -add_android_project(opencv_engine "${CMAKE_CURRENT_SOURCE_DIR}" SDK_TARGET 9 ${ANDROID_SDK_TARGET} IGNORE_JAVA ON IGNORE_MANIFEST ON COPY_LIBS ON) diff --git a/platforms/android/service/engine/res/layout/main.xml b/platforms/android/service/engine/res/layout/main.xml deleted file mode 100644 index a4717c8bf1..0000000000 --- a/platforms/android/service/engine/res/layout/main.xml +++ /dev/null @@ -1,49 +0,0 @@ - - - - - - - - - - - - - -