From d47c112434064c90816af2cf993e1ede3b303a7a Mon Sep 17 00:00:00 2001 From: Vladislav Vinogradov Date: Mon, 12 Nov 2012 12:18:31 +0400 Subject: [PATCH 01/23] fix abs_func and minimum/maximum functors --- .../include/opencv2/gpu/device/functional.hpp | 89 +++++++++++++++++-- .../include/opencv2/gpu/device/vec_math.hpp | 4 +- modules/gpu/src/cuda/pyrlk.cu | 4 +- modules/gpu/src/cuda/surf.cu | 4 +- 4 files changed, 86 insertions(+), 15 deletions(-) diff --git a/modules/gpu/include/opencv2/gpu/device/functional.hpp b/modules/gpu/include/opencv2/gpu/device/functional.hpp index c601cf5273..6e0471e9ac 100644 --- a/modules/gpu/include/opencv2/gpu/device/functional.hpp +++ b/modules/gpu/include/opencv2/gpu/device/functional.hpp @@ -302,18 +302,18 @@ namespace cv { namespace gpu { namespace device template <> struct name : binary_function \ { \ __device__ __forceinline__ type operator()(type lhs, type rhs) const {return op(lhs, rhs);} \ - __device__ __forceinline__ name(const name& other):binary_function(){}\ - __device__ __forceinline__ name():binary_function(){}\ + __device__ __forceinline__ name() {}\ + __device__ __forceinline__ name(const name&) {}\ }; template struct maximum : binary_function { __device__ __forceinline__ T operator()(typename TypeTraits::ParameterType lhs, typename TypeTraits::ParameterType rhs) const { - return lhs < rhs ? rhs : lhs; + return max(lhs, rhs); } - __device__ __forceinline__ maximum(const maximum& other):binary_function(){} - __device__ __forceinline__ maximum():binary_function(){} + __device__ __forceinline__ maximum() {} + __device__ __forceinline__ maximum(const maximum&) {} }; OPENCV_GPU_IMPLEMENT_MINMAX(maximum, uchar, ::max) @@ -330,10 +330,10 @@ namespace cv { namespace gpu { namespace device { __device__ __forceinline__ T operator()(typename TypeTraits::ParameterType lhs, typename TypeTraits::ParameterType rhs) const { - return lhs < rhs ? lhs : rhs; + return min(lhs, rhs); } - __device__ __forceinline__ minimum(const minimum& other):binary_function(){} - __device__ __forceinline__ minimum():binary_function(){} + __device__ __forceinline__ minimum() {} + __device__ __forceinline__ minimum(const minimum&) {} }; OPENCV_GPU_IMPLEMENT_MINMAX(minimum, uchar, ::min) @@ -350,6 +350,78 @@ namespace cv { namespace gpu { namespace device // Math functions ///bound========================================= + + template struct abs_func : unary_function + { + __device__ __forceinline__ T operator ()(typename TypeTraits::ParameterType x) const + { + return abs(x); + } + }; + template <> struct abs_func : unary_function + { + __device__ __forceinline__ unsigned char operator ()(unsigned char x) const + { + return x; + } + }; + template <> struct abs_func : unary_function + { + __device__ __forceinline__ signed char operator ()(signed char x) const + { + return ::abs(x); + } + }; + template <> struct abs_func : unary_function + { + __device__ __forceinline__ char operator ()(char x) const + { + return ::abs(x); + } + }; + template <> struct abs_func : unary_function + { + __device__ __forceinline__ unsigned short operator ()(unsigned short x) const + { + return x; + } + }; + template <> struct abs_func : unary_function + { + __device__ __forceinline__ short operator ()(short x) const + { + return ::abs(x); + } + }; + template <> struct abs_func : unary_function + { + __device__ __forceinline__ unsigned int operator ()(unsigned int x) const + { + return x; + } + }; + template <> struct abs_func : unary_function + { + __device__ __forceinline__ int operator ()(int x) const + { + return ::abs(x); + } + }; + template <> struct abs_func : unary_function + { + __device__ __forceinline__ float operator ()(float x) const + { + return ::fabsf(x); + } + }; + template <> struct abs_func : unary_function + { + __device__ __forceinline__ double operator ()(double x) const + { + return ::fabs(x); + } + }; + #define OPENCV_GPU_IMPLEMENT_UN_FUNCTOR(name, func) \ template struct name ## _func : unary_function \ { \ @@ -382,7 +454,6 @@ namespace cv { namespace gpu { namespace device } \ }; - OPENCV_GPU_IMPLEMENT_UN_FUNCTOR(fabs, ::fabs) OPENCV_GPU_IMPLEMENT_UN_FUNCTOR(sqrt, ::sqrt) OPENCV_GPU_IMPLEMENT_UN_FUNCTOR(exp, ::exp) OPENCV_GPU_IMPLEMENT_UN_FUNCTOR(exp2, ::exp2) diff --git a/modules/gpu/include/opencv2/gpu/device/vec_math.hpp b/modules/gpu/include/opencv2/gpu/device/vec_math.hpp index 0ec790c0b7..1c46dc0c33 100644 --- a/modules/gpu/include/opencv2/gpu/device/vec_math.hpp +++ b/modules/gpu/include/opencv2/gpu/device/vec_math.hpp @@ -280,7 +280,7 @@ namespace cv { namespace gpu { namespace device OPENCV_GPU_IMPLEMENT_VEC_UNOP (type, operator ! , logical_not) \ OPENCV_GPU_IMPLEMENT_VEC_BINOP(type, max, maximum) \ OPENCV_GPU_IMPLEMENT_VEC_BINOP(type, min, minimum) \ - OPENCV_GPU_IMPLEMENT_VEC_UNOP(type, fabs, fabs_func) \ + OPENCV_GPU_IMPLEMENT_VEC_UNOP(type, abs, abs_func) \ OPENCV_GPU_IMPLEMENT_VEC_UNOP(type, sqrt, sqrt_func) \ OPENCV_GPU_IMPLEMENT_VEC_UNOP(type, exp, exp_func) \ OPENCV_GPU_IMPLEMENT_VEC_UNOP(type, exp2, exp2_func) \ @@ -327,4 +327,4 @@ namespace cv { namespace gpu { namespace device #undef OPENCV_GPU_IMPLEMENT_VEC_INT_OP }}} // namespace cv { namespace gpu { namespace device -#endif // __OPENCV_GPU_VECMATH_HPP__ \ No newline at end of file +#endif // __OPENCV_GPU_VECMATH_HPP__ diff --git a/modules/gpu/src/cuda/pyrlk.cu b/modules/gpu/src/cuda/pyrlk.cu index d1a65c210f..811c3b90b5 100644 --- a/modules/gpu/src/cuda/pyrlk.cu +++ b/modules/gpu/src/cuda/pyrlk.cu @@ -267,7 +267,7 @@ namespace cv { namespace gpu { namespace device } __device__ __forceinline__ float4 abs_(const float4& a) { - return fabs(a); + return abs(a); } template @@ -681,4 +681,4 @@ namespace cv { namespace gpu { namespace device } }}} -#endif /* CUDA_DISABLER */ \ No newline at end of file +#endif /* CUDA_DISABLER */ diff --git a/modules/gpu/src/cuda/surf.cu b/modules/gpu/src/cuda/surf.cu index 8c80559c5b..aebda0ea9e 100644 --- a/modules/gpu/src/cuda/surf.cu +++ b/modules/gpu/src/cuda/surf.cu @@ -638,7 +638,7 @@ namespace cv { namespace gpu { namespace device kp_dir *= 180.0f / CV_PI_F; kp_dir = 360.0f - kp_dir; - if (abs(kp_dir - 360.f) < FLT_EPSILON) + if (::fabsf(kp_dir - 360.f) < FLT_EPSILON) kp_dir = 0.f; featureDir[blockIdx.x] = kp_dir; @@ -1003,4 +1003,4 @@ namespace cv { namespace gpu { namespace device }}} // namespace cv { namespace gpu { namespace device -#endif /* CUDA_DISABLER */ \ No newline at end of file +#endif /* CUDA_DISABLER */ From 7a1874b2ccf9bafd12a343baa2f133194439f659 Mon Sep 17 00:00:00 2001 From: Vladislav Vinogradov Date: Mon, 12 Nov 2012 10:55:32 +0400 Subject: [PATCH 02/23] new reduce and reduceKeyVal implementation --- .../opencv2/gpu/device/detail/reduce.hpp | 352 +++++++++++++ .../gpu/device/detail/reduce_key_val.hpp | 489 ++++++++++++++++++ .../gpu/include/opencv2/gpu/device/reduce.hpp | 197 +++++++ .../include/opencv2/gpu/device/utility.hpp | 2 +- .../opencv2/gpu/device/vec_distance.hpp | 8 +- .../opencv2/gpu/device/warp_shuffle.hpp | 97 ++++ modules/gpu/src/cuda/orb.cu | 14 +- modules/gpu/src/cuda/surf.cu | 4 +- 8 files changed, 1149 insertions(+), 14 deletions(-) create mode 100644 modules/gpu/include/opencv2/gpu/device/detail/reduce.hpp create mode 100644 modules/gpu/include/opencv2/gpu/device/detail/reduce_key_val.hpp create mode 100644 modules/gpu/include/opencv2/gpu/device/reduce.hpp create mode 100644 modules/gpu/include/opencv2/gpu/device/warp_shuffle.hpp diff --git a/modules/gpu/include/opencv2/gpu/device/detail/reduce.hpp b/modules/gpu/include/opencv2/gpu/device/detail/reduce.hpp new file mode 100644 index 0000000000..628129ea33 --- /dev/null +++ b/modules/gpu/include/opencv2/gpu/device/detail/reduce.hpp @@ -0,0 +1,352 @@ +/*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) 2000-2008, Intel Corporation, all rights reserved. +// Copyright (C) 2009, Willow Garage Inc., 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*/ + +#ifndef __OPENCV_GPU_REDUCE_DETAIL_HPP__ +#define __OPENCV_GPU_REDUCE_DETAIL_HPP__ + +#include +#include "../warp.hpp" +#include "../warp_shuffle.hpp" + +namespace cv { namespace gpu { namespace device +{ + namespace reduce_detail + { + template struct GetType; + template struct GetType + { + typedef T type; + }; + template struct GetType + { + typedef T type; + }; + template struct GetType + { + typedef T type; + }; + + template + struct For + { + template + static __device__ void loadToSmem(const PointerTuple& smem, const ValTuple& val, unsigned int tid) + { + thrust::get(smem)[tid] = thrust::get(val); + + For::loadToSmem(smem, val, tid); + } + template + static __device__ void loadFromSmem(const PointerTuple& smem, const ValTuple& val, unsigned int tid) + { + thrust::get(val) = thrust::get(smem)[tid]; + + For::loadFromSmem(smem, val, tid); + } + + template + static __device__ void merge(const PointerTuple& smem, const ValTuple& val, unsigned int tid, unsigned int delta, const OpTuple& op) + { + typename GetType::type>::type reg = thrust::get(smem)[tid + delta]; + thrust::get(smem)[tid] = thrust::get(val) = thrust::get(op)(thrust::get(val), reg); + + For::merge(smem, val, tid, delta, op); + } + template + static __device__ void mergeShfl(const ValTuple& val, unsigned int delta, unsigned int width, const OpTuple& op) + { + typename GetType::type>::type reg = shfl_down(thrust::get(val), delta, width); + thrust::get(val) = thrust::get(op)(thrust::get(val), reg); + + For::mergeShfl(val, delta, width, op); + } + }; + template + struct For + { + template + static __device__ void loadToSmem(const PointerTuple&, const ValTuple&, unsigned int) + { + } + template + static __device__ void loadFromSmem(const PointerTuple&, const ValTuple&, unsigned int) + { + } + + template + static __device__ void merge(const PointerTuple&, const ValTuple&, unsigned int, unsigned int, const OpTuple&) + { + } + template + static __device__ void mergeShfl(const ValTuple&, unsigned int, unsigned int, const OpTuple&) + { + } + }; + + template + __device__ __forceinline__ void loadToSmem(volatile T* smem, T& val, unsigned int tid) + { + smem[tid] = val; + } + template + __device__ __forceinline__ void loadFromSmem(volatile T* smem, T& val, unsigned int tid) + { + val = smem[tid]; + } + template + __device__ __forceinline__ void loadToSmem(const thrust::tuple& smem, + const thrust::tuple& val, + unsigned int tid) + { + For<0, thrust::tuple_size >::value>::loadToSmem(smem, val, tid); + } + template + __device__ __forceinline__ void loadFromSmem(const thrust::tuple& smem, + const thrust::tuple& val, + unsigned int tid) + { + For<0, thrust::tuple_size >::value>::loadFromSmem(smem, val, tid); + } + + template + __device__ __forceinline__ void merge(volatile T* smem, T& val, unsigned int tid, unsigned int delta, const Op& op) + { + T reg = smem[tid + delta]; + smem[tid] = val = op(val, reg); + } + template + __device__ __forceinline__ void mergeShfl(T& val, unsigned int delta, unsigned int width, const Op& op) + { + T reg = shfl_down(val, delta, width); + val = op(val, reg); + } + template + __device__ __forceinline__ void merge(const thrust::tuple& smem, + const thrust::tuple& val, + unsigned int tid, + unsigned int delta, + const thrust::tuple& op) + { + For<0, thrust::tuple_size >::value>::merge(smem, val, tid, delta, op); + } + template + __device__ __forceinline__ void mergeShfl(const thrust::tuple& val, + unsigned int delta, + unsigned int width, + const thrust::tuple& op) + { + For<0, thrust::tuple_size >::value>::mergeShfl(val, delta, width, op); + } + + template struct Generic + { + template + static __device__ void reduce(Pointer smem, Reference val, unsigned int tid, Op op) + { + loadToSmem(smem, val, tid); + if (N >= 32) + __syncthreads(); + + if (N >= 2048) + { + if (tid < 1024) + merge(smem, val, tid, 1024, op); + + __syncthreads(); + } + if (N >= 1024) + { + if (tid < 512) + merge(smem, val, tid, 512, op); + + __syncthreads(); + } + if (N >= 512) + { + if (tid < 256) + merge(smem, val, tid, 256, op); + + __syncthreads(); + } + if (N >= 256) + { + if (tid < 128) + merge(smem, val, tid, 128, op); + + __syncthreads(); + } + if (N >= 128) + { + if (tid < 64) + merge(smem, val, tid, 64, op); + + __syncthreads(); + } + if (N >= 64) + { + if (tid < 32) + merge(smem, val, tid, 32, op); + } + + if (tid < 16) + { + merge(smem, val, tid, 16, op); + merge(smem, val, tid, 8, op); + merge(smem, val, tid, 4, op); + merge(smem, val, tid, 2, op); + merge(smem, val, tid, 1, op); + } + } + }; + + template struct WarpOptimized + { + template + static __device__ void reduce(Pointer smem, Reference val, unsigned int tid, Op op) + { + #if __CUDA_ARCH >= 300 + (void) smem; + (void) tid; + + #pragma unroll + for (unsigned int i = N / 2; i >= 1; i /= 2) + mergeShfl(val, i, N, op); + #else + loadToSmem(smem, val, tid); + + if (tid < N / 2) + { + #pragma unroll + for (unsigned int i = N / 2; i >= 1; i /= 2) + merge(smem, val, tid, i, op); + } + #endif + } + }; + + template struct GenericOptimized32 + { + enum { M = N / 32 }; + + template + static __device__ void reduce(Pointer smem, Reference val, unsigned int tid, Op op) + { + const unsigned int laneId = Warp::laneId(); + + #if __CUDA_ARCH >= 300 + #pragma unroll + for (int i = 16; i >= 1; i /= 2) + mergeShfl(val, i, warpSize, op); + + if (laneId == 0) + loadToSmem(smem, val, tid / 32); + #else + loadToSmem(smem, val, tid); + + if (laneId < 16) + { + #pragma unroll + for (int i = 16; i >= 1; i /= 2) + merge(smem, val, tid, i, op); + } + + __syncthreads(); + + if (laneId == 0) + loadToSmem(smem, val, tid / 32); + #endif + + __syncthreads(); + + loadFromSmem(smem, val, tid); + + if (tid < 32) + { + #if __CUDA_ARCH >= 300 + #pragma unroll + for (int i = M / 2; i >= 1; i /= 2) + mergeShfl(val, i, M, op); + #else + #pragma unroll + for (int i = M / 2; i >= 1; i /= 2) + merge(smem, val, tid, i, op); + #endif + } + } + }; + + template struct StaticIf; + template struct StaticIf + { + typedef T1 type; + }; + template struct StaticIf + { + typedef T2 type; + }; + + template struct IsPowerOf2 + { + enum { value = ((N != 0) && !(N & (N - 1))) }; + }; + + template struct Dispatcher + { + typedef typename StaticIf< + (N <= 32) && IsPowerOf2::value, + WarpOptimized, + typename StaticIf< + (N <= 1024) && IsPowerOf2::value, + GenericOptimized32, + Generic + >::type + >::type reductor; + }; + } +}}} + +#endif // __OPENCV_GPU_REDUCE_DETAIL_HPP__ diff --git a/modules/gpu/include/opencv2/gpu/device/detail/reduce_key_val.hpp b/modules/gpu/include/opencv2/gpu/device/detail/reduce_key_val.hpp new file mode 100644 index 0000000000..f7531da246 --- /dev/null +++ b/modules/gpu/include/opencv2/gpu/device/detail/reduce_key_val.hpp @@ -0,0 +1,489 @@ +/*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) 2000-2008, Intel Corporation, all rights reserved. +// Copyright (C) 2009, Willow Garage Inc., 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*/ + +#ifndef __OPENCV_GPU_PRED_VAL_REDUCE_DETAIL_HPP__ +#define __OPENCV_GPU_PRED_VAL_REDUCE_DETAIL_HPP__ + +#include +#include "../warp.hpp" +#include "../warp_shuffle.hpp" + +namespace cv { namespace gpu { namespace device +{ + namespace reduce_key_val_detail + { + template struct GetType; + template struct GetType + { + typedef T type; + }; + template struct GetType + { + typedef T type; + }; + template struct GetType + { + typedef T type; + }; + + template + struct For + { + template + static __device__ void loadToSmem(const PointerTuple& smem, const ReferenceTuple& data, unsigned int tid) + { + thrust::get(smem)[tid] = thrust::get(data); + + For::loadToSmem(smem, data, tid); + } + template + static __device__ void loadFromSmem(const PointerTuple& smem, const ReferenceTuple& data, unsigned int tid) + { + thrust::get(data) = thrust::get(smem)[tid]; + + For::loadFromSmem(smem, data, tid); + } + + template + static __device__ void copyShfl(const ReferenceTuple& val, unsigned int delta, int width) + { + thrust::get(val) = shfl_down(thrust::get(val), delta, width); + + For::copyShfl(val, delta, width); + } + template + static __device__ void copy(const PointerTuple& svals, const ReferenceTuple& val, unsigned int tid, unsigned int delta) + { + thrust::get(svals)[tid] = thrust::get(val) = thrust::get(svals)[tid + delta]; + + For::copy(svals, val, tid, delta); + } + + template + static __device__ void mergeShfl(const KeyReferenceTuple& key, const ValReferenceTuple& val, const CmpTuple& cmp, unsigned int delta, int width) + { + typename GetType::type>::type reg = shfl_down(thrust::get(key), delta, width); + + if (thrust::get(cmp)(reg, thrust::get(key))) + { + thrust::get(key) = reg; + thrust::get(val) = shfl_down(thrust::get(val), delta, width); + } + + For::mergeShfl(key, val, cmp, delta, width); + } + template + static __device__ void merge(const KeyPointerTuple& skeys, const KeyReferenceTuple& key, + const ValPointerTuple& svals, const ValReferenceTuple& val, + const CmpTuple& cmp, + unsigned int tid, unsigned int delta) + { + typename GetType::type>::type reg = thrust::get(skeys)[tid + delta]; + + if (thrust::get(cmp)(reg, thrust::get(key))) + { + thrust::get(skeys)[tid] = thrust::get(key) = reg; + thrust::get(svals)[tid] = thrust::get(val) = thrust::get(svals)[tid + delta]; + } + + For::merge(skeys, key, svals, val, cmp, tid, delta); + } + }; + template + struct For + { + template + static __device__ void loadToSmem(const PointerTuple&, const ReferenceTuple&, unsigned int) + { + } + template + static __device__ void loadFromSmem(const PointerTuple&, const ReferenceTuple&, unsigned int) + { + } + + template + static __device__ void copyShfl(const ReferenceTuple&, unsigned int, int) + { + } + template + static __device__ void copy(const PointerTuple&, const ReferenceTuple&, unsigned int, unsigned int) + { + } + + template + static __device__ void mergeShfl(const KeyReferenceTuple&, const ValReferenceTuple&, const CmpTuple&, unsigned int, int) + { + } + template + static __device__ void merge(const KeyPointerTuple&, const KeyReferenceTuple&, + const ValPointerTuple&, const ValReferenceTuple&, + const CmpTuple&, + unsigned int, unsigned int) + { + } + }; + + ////////////////////////////////////////////////////// + // loadToSmem + + template + __device__ __forceinline__ void loadToSmem(volatile T* smem, T& data, unsigned int tid) + { + smem[tid] = data; + } + template + __device__ __forceinline__ void loadFromSmem(volatile T* smem, T& data, unsigned int tid) + { + data = smem[tid]; + } + template + __device__ __forceinline__ void loadToSmem(const thrust::tuple& smem, + const thrust::tuple& data, + unsigned int tid) + { + For<0, thrust::tuple_size >::value>::loadToSmem(smem, data, tid); + } + template + __device__ __forceinline__ void loadFromSmem(const thrust::tuple& smem, + const thrust::tuple& data, + unsigned int tid) + { + For<0, thrust::tuple_size >::value>::loadFromSmem(smem, data, tid); + } + + ////////////////////////////////////////////////////// + // copyVals + + template + __device__ __forceinline__ void copyValsShfl(V& val, unsigned int delta, int width) + { + val = shfl_down(val, delta, width); + } + template + __device__ __forceinline__ void copyVals(volatile V* svals, V& val, unsigned int tid, unsigned int delta) + { + svals[tid] = val = svals[tid + delta]; + } + template + __device__ __forceinline__ void copyValsShfl(const thrust::tuple& val, + unsigned int delta, + int width) + { + For<0, thrust::tuple_size >::value>::copyShfl(val, delta, width); + } + template + __device__ __forceinline__ void copyVals(const thrust::tuple& svals, + const thrust::tuple& val, + unsigned int tid, unsigned int delta) + { + For<0, thrust::tuple_size >::value>::copy(svals, val, tid, delta); + } + + ////////////////////////////////////////////////////// + // merge + + template + __device__ __forceinline__ void mergeShfl(K& key, V& val, const Cmp& cmp, unsigned int delta, int width) + { + K reg = shfl_down(key, delta, width); + + if (cmp(reg, key)) + { + key = reg; + copyValsShfl(val, delta, width); + } + } + template + __device__ __forceinline__ void merge(volatile K* skeys, K& key, volatile V* svals, V& val, const Cmp& cmp, unsigned int tid, unsigned int delta) + { + K reg = skeys[tid + delta]; + + if (cmp(reg, key)) + { + skeys[tid] = key = reg; + copyVals(svals, val, tid, delta); + } + } + template + __device__ __forceinline__ void mergeShfl(K& key, + const thrust::tuple& val, + const Cmp& cmp, + unsigned int delta, int width) + { + K reg = shfl_down(key, delta, width); + + if (cmp(reg, key)) + { + key = reg; + copyValsShfl(val, delta, width); + } + } + template + __device__ __forceinline__ void merge(volatile K* skeys, K& key, + const thrust::tuple& svals, + const thrust::tuple& val, + const Cmp& cmp, unsigned int tid, unsigned int delta) + { + K reg = skeys[tid + delta]; + + if (cmp(reg, key)) + { + skeys[tid] = key = reg; + copyVals(svals, val, tid, delta); + } + } + template + __device__ __forceinline__ void mergeShfl(const thrust::tuple& key, + const thrust::tuple& val, + const thrust::tuple& cmp, + unsigned int delta, int width) + { + For<0, thrust::tuple_size >::value>::mergeShfl(key, val, cmp, delta, width); + } + template + __device__ __forceinline__ void merge(const thrust::tuple& skeys, + const thrust::tuple& key, + const thrust::tuple& svals, + const thrust::tuple& val, + const thrust::tuple& cmp, + unsigned int tid, unsigned int delta) + { + For<0, thrust::tuple_size >::value>::merge(skeys, key, svals, val, cmp, tid, delta); + } + + ////////////////////////////////////////////////////// + // Generic + + template struct Generic + { + template + static __device__ void reduce(KP skeys, KR key, VP svals, VR val, unsigned int tid, Cmp cmp) + { + loadToSmem(skeys, key, tid); + loadValsToSmem(svals, val, tid); + if (N >= 32) + __syncthreads(); + + if (N >= 2048) + { + if (tid < 1024) + merge(skeys, key, svals, val, cmp, tid, 1024); + + __syncthreads(); + } + if (N >= 1024) + { + if (tid < 512) + merge(skeys, key, svals, val, cmp, tid, 512); + + __syncthreads(); + } + if (N >= 512) + { + if (tid < 256) + merge(skeys, key, svals, val, cmp, tid, 256); + + __syncthreads(); + } + if (N >= 256) + { + if (tid < 128) + merge(skeys, key, svals, val, cmp, tid, 128); + + __syncthreads(); + } + if (N >= 128) + { + if (tid < 64) + merge(skeys, key, svals, val, cmp, tid, 64); + + __syncthreads(); + } + if (N >= 64) + { + if (tid < 32) + merge(skeys, key, svals, val, cmp, tid, 32); + } + + if (tid < 16) + { + merge(skeys, key, svals, val, cmp, tid, 16); + merge(skeys, key, svals, val, cmp, tid, 8); + merge(skeys, key, svals, val, cmp, tid, 4); + merge(skeys, key, svals, val, cmp, tid, 2); + merge(skeys, key, svals, val, cmp, tid, 1); + } + } + }; + + template struct WarpOptimized + { + template + static __device__ void reduce(KP skeys, KR key, VP svals, VR val, unsigned int tid, Cmp cmp) + { + #if __CUDA_ARCH >= 300 + (void) skeys; + (void) svals; + (void) tid; + + #pragma unroll + for (unsigned int i = N / 2; i >= 1; i /= 2) + mergeShfl(key, val, cml, i, N); + #else + loadToSmem(skeys, key, tid); + loadToSmem(svals, val, tid); + + if (tid < N / 2) + { + #pragma unroll + for (unsigned int i = N / 2; i >= 1; i /= 2) + merge(skeys, key, svals, val, cmp, tid, i); + } + #endif + } + }; + + template struct GenericOptimized32 + { + enum { M = N / 32 }; + + template + static __device__ void reduce(KP skeys, KR key, VP svals, VR val, unsigned int tid, Cmp cmp) + { + const unsigned int laneId = Warp::laneId(); + + #if __CUDA_ARCH >= 300 + #pragma unroll + for (unsigned int i = 16; i >= 1; i /= 2) + mergeShfl(key, val, cml, i, warpSize); + + if (laneId == 0) + { + loadToSmem(skeys, key, tid / 32); + loadToSmem(svals, val, tid / 32); + } + #else + loadToSmem(skeys, key, tid); + loadToSmem(svals, val, tid); + + if (laneId < 16) + { + #pragma unroll + for (int i = 16; i >= 1; i /= 2) + merge(skeys, key, svals, val, cmp, tid, i); + } + + __syncthreads(); + + if (laneId == 0) + { + loadToSmem(skeys, key, tid / 32); + loadToSmem(svals, val, tid / 32); + } + #endif + + __syncthreads(); + + loadFromSmem(skeys, key, tid); + + if (tid < 32) + { + #if __CUDA_ARCH >= 300 + loadFromSmem(svals, val, tid); + + #pragma unroll + for (unsigned int i = M / 2; i >= 1; i /= 2) + mergeShfl(key, val, cml, i, M); + #else + #pragma unroll + for (unsigned int i = M / 2; i >= 1; i /= 2) + merge(skeys, key, svals, val, cmp, tid, i); + #endif + } + } + }; + + template struct StaticIf; + template struct StaticIf + { + typedef T1 type; + }; + template struct StaticIf + { + typedef T2 type; + }; + + template struct IsPowerOf2 + { + enum { value = ((N != 0) && !(N & (N - 1))) }; + }; + + template struct Dispatcher + { + typedef typename StaticIf< + (N <= 32) && IsPowerOf2::value, + WarpOptimized, + typename StaticIf< + (N <= 1024) && IsPowerOf2::value, + GenericOptimized32, + Generic + >::type + >::type reductor; + }; + } +}}} + +#endif // __OPENCV_GPU_PRED_VAL_REDUCE_DETAIL_HPP__ diff --git a/modules/gpu/include/opencv2/gpu/device/reduce.hpp b/modules/gpu/include/opencv2/gpu/device/reduce.hpp new file mode 100644 index 0000000000..2161b06495 --- /dev/null +++ b/modules/gpu/include/opencv2/gpu/device/reduce.hpp @@ -0,0 +1,197 @@ +/*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) 2000-2008, Intel Corporation, all rights reserved. +// Copyright (C) 2009, Willow Garage Inc., 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*/ + +#ifndef __OPENCV_GPU_REDUCE_HPP__ +#define __OPENCV_GPU_REDUCE_HPP__ + +#include +#include "detail/reduce.hpp" +#include "detail/reduce_key_val.hpp" + +namespace cv { namespace gpu { namespace device +{ + template + __device__ __forceinline__ void reduce(volatile T* smem, T& val, unsigned int tid, const Op& op) + { + reduce_detail::Dispatcher::reductor::template reduce(smem, val, tid, op); + } + template + __device__ __forceinline__ void reduce(const thrust::tuple& smem, + const thrust::tuple& val, + unsigned int tid, + const thrust::tuple& op) + { + reduce_detail::Dispatcher::reductor::template reduce< + const thrust::tuple&, + const thrust::tuple&, + const thrust::tuple&>(smem, val, tid, op); + } + + template + __device__ __forceinline__ void reduceKeyVal(volatile K* skeys, K& key, volatile V* svals, V& val, unsigned int tid, const Cmp& cmp) + { + reduce_key_val_detail::Dispatcher::reductor::template reduce(skeys, key, svals, val, tid, cmp); + } + template + __device__ __forceinline__ void reduceKeyVal(volatile K* skeys, K& key, + const thrust::tuple& svals, + const thrust::tuple& val, + unsigned int tid, const Cmp& cmp) + { + reduce_key_val_detail::Dispatcher::reductor::template reduce&, + const thrust::tuple&, + const Cmp&>(skeys, key, svals, val, tid, cmp); + } + template + __device__ __forceinline__ void reduceKeyVal(const thrust::tuple& skeys, + const thrust::tuple& key, + const thrust::tuple& svals, + const thrust::tuple& val, + unsigned int tid, + const thrust::tuple& cmp) + { + reduce_key_val_detail::Dispatcher::reductor::template reduce< + const thrust::tuple&, + const thrust::tuple&, + const thrust::tuple&, + const thrust::tuple&, + const thrust::tuple& + >(skeys, key, svals, val, tid, cmp); + } + + // smem_tuple + + template + __device__ __forceinline__ + thrust::tuple + smem_tuple(T0* t0) + { + return thrust::make_tuple((volatile T0*) t0); + } + + template + __device__ __forceinline__ + thrust::tuple + smem_tuple(T0* t0, T1* t1) + { + return thrust::make_tuple((volatile T0*) t0, (volatile T1*) t1); + } + + template + __device__ __forceinline__ + thrust::tuple + smem_tuple(T0* t0, T1* t1, T2* t2) + { + return thrust::make_tuple((volatile T0*) t0, (volatile T1*) t1, (volatile T2*) t2); + } + + template + __device__ __forceinline__ + thrust::tuple + smem_tuple(T0* t0, T1* t1, T2* t2, T3* t3) + { + return thrust::make_tuple((volatile T0*) t0, (volatile T1*) t1, (volatile T2*) t2, (volatile T3*) t3); + } + + template + __device__ __forceinline__ + thrust::tuple + smem_tuple(T0* t0, T1* t1, T2* t2, T3* t3, T4* t4) + { + return thrust::make_tuple((volatile T0*) t0, (volatile T1*) t1, (volatile T2*) t2, (volatile T3*) t3, (volatile T4*) t4); + } + + template + __device__ __forceinline__ + thrust::tuple + smem_tuple(T0* t0, T1* t1, T2* t2, T3* t3, T4* t4, T5* t5) + { + return thrust::make_tuple((volatile T0*) t0, (volatile T1*) t1, (volatile T2*) t2, (volatile T3*) t3, (volatile T4*) t4, (volatile T5*) t5); + } + + template + __device__ __forceinline__ + thrust::tuple + smem_tuple(T0* t0, T1* t1, T2* t2, T3* t3, T4* t4, T5* t5, T6* t6) + { + return thrust::make_tuple((volatile T0*) t0, (volatile T1*) t1, (volatile T2*) t2, (volatile T3*) t3, (volatile T4*) t4, (volatile T5*) t5, (volatile T6*) t6); + } + + template + __device__ __forceinline__ + thrust::tuple + smem_tuple(T0* t0, T1* t1, T2* t2, T3* t3, T4* t4, T5* t5, T6* t6, T7* t7) + { + return thrust::make_tuple((volatile T0*) t0, (volatile T1*) t1, (volatile T2*) t2, (volatile T3*) t3, (volatile T4*) t4, (volatile T5*) t5, (volatile T6*) t6, (volatile T7*) t7); + } + + template + __device__ __forceinline__ + thrust::tuple + smem_tuple(T0* t0, T1* t1, T2* t2, T3* t3, T4* t4, T5* t5, T6* t6, T7* t7, T8* t8) + { + return thrust::make_tuple((volatile T0*) t0, (volatile T1*) t1, (volatile T2*) t2, (volatile T3*) t3, (volatile T4*) t4, (volatile T5*) t5, (volatile T6*) t6, (volatile T7*) t7, (volatile T8*) t8); + } + + template + __device__ __forceinline__ + thrust::tuple + smem_tuple(T0* t0, T1* t1, T2* t2, T3* t3, T4* t4, T5* t5, T6* t6, T7* t7, T8* t8, T9* t9) + { + return thrust::make_tuple((volatile T0*) t0, (volatile T1*) t1, (volatile T2*) t2, (volatile T3*) t3, (volatile T4*) t4, (volatile T5*) t5, (volatile T6*) t6, (volatile T7*) t7, (volatile T8*) t8, (volatile T9*) t9); + } +}}} + +#endif // __OPENCV_GPU_UTILITY_HPP__ diff --git a/modules/gpu/include/opencv2/gpu/device/utility.hpp b/modules/gpu/include/opencv2/gpu/device/utility.hpp index 4489a20b15..e44d51a6ab 100644 --- a/modules/gpu/include/opencv2/gpu/device/utility.hpp +++ b/modules/gpu/include/opencv2/gpu/device/utility.hpp @@ -159,7 +159,7 @@ namespace cv { namespace gpu { namespace device /////////////////////////////////////////////////////////////////////////////// // Reduction - template __device__ __forceinline__ void reduce(volatile T* data, T& partial_reduction, int tid, const Op& op) + template __device__ __forceinline__ void reduce_old(volatile T* data, T& partial_reduction, int tid, const Op& op) { StaticAssert= 8 && n <= 512>::check(); utility_detail::ReductionDispatcher::reduce(data, partial_reduction, tid, op); diff --git a/modules/gpu/include/opencv2/gpu/device/vec_distance.hpp b/modules/gpu/include/opencv2/gpu/device/vec_distance.hpp index b7861bca75..f65af3aa56 100644 --- a/modules/gpu/include/opencv2/gpu/device/vec_distance.hpp +++ b/modules/gpu/include/opencv2/gpu/device/vec_distance.hpp @@ -63,7 +63,7 @@ namespace cv { namespace gpu { namespace device template __device__ __forceinline__ void reduceAll(int* smem, int tid) { - reduce(smem, mySum, tid, plus()); + reduce_old(smem, mySum, tid, plus()); } __device__ __forceinline__ operator int() const @@ -87,7 +87,7 @@ namespace cv { namespace gpu { namespace device template __device__ __forceinline__ void reduceAll(float* smem, int tid) { - reduce(smem, mySum, tid, plus()); + reduce_old(smem, mySum, tid, plus()); } __device__ __forceinline__ operator float() const @@ -113,7 +113,7 @@ namespace cv { namespace gpu { namespace device template __device__ __forceinline__ void reduceAll(float* smem, int tid) { - reduce(smem, mySum, tid, plus()); + reduce_old(smem, mySum, tid, plus()); } __device__ __forceinline__ operator float() const @@ -138,7 +138,7 @@ namespace cv { namespace gpu { namespace device template __device__ __forceinline__ void reduceAll(int* smem, int tid) { - reduce(smem, mySum, tid, plus()); + reduce_old(smem, mySum, tid, plus()); } __device__ __forceinline__ operator int() const diff --git a/modules/gpu/include/opencv2/gpu/device/warp_shuffle.hpp b/modules/gpu/include/opencv2/gpu/device/warp_shuffle.hpp new file mode 100644 index 0000000000..39b7e852ab --- /dev/null +++ b/modules/gpu/include/opencv2/gpu/device/warp_shuffle.hpp @@ -0,0 +1,97 @@ +/*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) 2000-2008, Intel Corporation, all rights reserved. +// Copyright (C) 2009, Willow Garage Inc., 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*/ + +#ifndef __OPENCV_GPU_WARP_SHUFFLE_HPP__ +#define __OPENCV_GPU_WARP_SHUFFLE_HPP__ + +namespace cv { namespace gpu { namespace device +{ + template + __device__ __forceinline__ T shfl(T val, int srcLane, int width = warpSize) + { + #if __CUDA_ARCH__ >= 300 + return __shfl(val, srcLane, width); + #else + return T(); + #endif + } + __device__ __forceinline__ double shfl(double val, int srcLane, int width = warpSize) + { + #if __CUDA_ARCH__ >= 300 + int lo = __double2loint(val); + int hi = __double2hiint(val); + + lo = __shfl(lo, srcLane, width); + hi = __shfl(hi, srcLane, width); + + return __hiloint2double(hi, lo); + #else + return 0.0; + #endif + } + + template + __device__ __forceinline__ T shfl_down(T val, unsigned int delta, int width = warpSize) + { + #if __CUDA_ARCH__ >= 300 + return __shfl_down(val, delta, width); + #else + return T(); + #endif + } + __device__ __forceinline__ double shfl_down(double val, unsigned int delta, int width = warpSize) + { + #if __CUDA_ARCH__ >= 300 + int lo = __double2loint(val); + int hi = __double2hiint(val); + + lo = __shfl_down(lo, delta, width); + hi = __shfl_down(hi, delta, width); + + return __hiloint2double(hi, lo); + #else + return 0.0; + #endif + } +}}} + +#endif // __OPENCV_GPU_WARP_SHUFFLE_HPP__ diff --git a/modules/gpu/src/cuda/orb.cu b/modules/gpu/src/cuda/orb.cu index 2d441a472a..91c5709574 100644 --- a/modules/gpu/src/cuda/orb.cu +++ b/modules/gpu/src/cuda/orb.cu @@ -109,9 +109,9 @@ namespace cv { namespace gpu { namespace device c += Ix * Iy; } - reduce<32>(srow, a, threadIdx.x, plus()); - reduce<32>(srow, b, threadIdx.x, plus()); - reduce<32>(srow, c, threadIdx.x, plus()); + reduce_old<32>(srow, a, threadIdx.x, plus()); + reduce_old<32>(srow, b, threadIdx.x, plus()); + reduce_old<32>(srow, c, threadIdx.x, plus()); if (threadIdx.x == 0) { @@ -167,7 +167,7 @@ namespace cv { namespace gpu { namespace device for (int u = threadIdx.x - half_k; u <= half_k; u += blockDim.x) m_10 += u * image(loc.y, loc.x + u); - reduce<32>(srow, m_10, threadIdx.x, plus()); + reduce_old<32>(srow, m_10, threadIdx.x, plus()); for (int v = 1; v <= half_k; ++v) { @@ -185,8 +185,8 @@ namespace cv { namespace gpu { namespace device m_sum += u * (val_plus + val_minus); } - reduce<32>(srow, v_sum, threadIdx.x, plus()); - reduce<32>(srow, m_sum, threadIdx.x, plus()); + reduce_old<32>(srow, v_sum, threadIdx.x, plus()); + reduce_old<32>(srow, m_sum, threadIdx.x, plus()); m_10 += m_sum; m_01 += v * v_sum; @@ -419,4 +419,4 @@ namespace cv { namespace gpu { namespace device } }}} -#endif /* CUDA_DISABLER */ \ No newline at end of file +#endif /* CUDA_DISABLER */ diff --git a/modules/gpu/src/cuda/surf.cu b/modules/gpu/src/cuda/surf.cu index aebda0ea9e..451fb425d3 100644 --- a/modules/gpu/src/cuda/surf.cu +++ b/modules/gpu/src/cuda/surf.cu @@ -599,8 +599,8 @@ namespace cv { namespace gpu { namespace device sumy += s_Y[threadIdx.x + 96]; } - device::reduce<32>(s_sumx + threadIdx.y * 32, sumx, threadIdx.x, plus()); - device::reduce<32>(s_sumy + threadIdx.y * 32, sumy, threadIdx.x, plus()); + device::reduce_old<32>(s_sumx + threadIdx.y * 32, sumx, threadIdx.x, plus()); + device::reduce_old<32>(s_sumy + threadIdx.y * 32, sumy, threadIdx.x, plus()); const float temp_mod = sumx * sumx + sumy * sumy; if (temp_mod > best_mod) From 05db02fbc8af694c89c4740ecb067ce71eefd999 Mon Sep 17 00:00:00 2001 From: Vladislav Vinogradov Date: Mon, 12 Nov 2012 12:46:49 +0400 Subject: [PATCH 03/23] BruteForceMatcher --- .../opencv2/gpu/device/vec_distance.hpp | 10 +- modules/gpu/src/cuda/bf_knnmatch.cu | 97 ++++++++++++++++++- modules/gpu/src/cuda/bf_match.cu | 21 ++-- modules/gpu/src/cuda/bf_radius_match.cu | 13 +-- 4 files changed, 108 insertions(+), 33 deletions(-) diff --git a/modules/gpu/include/opencv2/gpu/device/vec_distance.hpp b/modules/gpu/include/opencv2/gpu/device/vec_distance.hpp index f65af3aa56..d5b4bb202c 100644 --- a/modules/gpu/include/opencv2/gpu/device/vec_distance.hpp +++ b/modules/gpu/include/opencv2/gpu/device/vec_distance.hpp @@ -43,7 +43,7 @@ #ifndef __OPENCV_GPU_VEC_DISTANCE_HPP__ #define __OPENCV_GPU_VEC_DISTANCE_HPP__ -#include "utility.hpp" +#include "reduce.hpp" #include "functional.hpp" #include "detail/vec_distance_detail.hpp" @@ -63,7 +63,7 @@ namespace cv { namespace gpu { namespace device template __device__ __forceinline__ void reduceAll(int* smem, int tid) { - reduce_old(smem, mySum, tid, plus()); + reduce(smem, mySum, tid, plus()); } __device__ __forceinline__ operator int() const @@ -87,7 +87,7 @@ namespace cv { namespace gpu { namespace device template __device__ __forceinline__ void reduceAll(float* smem, int tid) { - reduce_old(smem, mySum, tid, plus()); + reduce(smem, mySum, tid, plus()); } __device__ __forceinline__ operator float() const @@ -113,7 +113,7 @@ namespace cv { namespace gpu { namespace device template __device__ __forceinline__ void reduceAll(float* smem, int tid) { - reduce_old(smem, mySum, tid, plus()); + reduce(smem, mySum, tid, plus()); } __device__ __forceinline__ operator float() const @@ -138,7 +138,7 @@ namespace cv { namespace gpu { namespace device template __device__ __forceinline__ void reduceAll(int* smem, int tid) { - reduce_old(smem, mySum, tid, plus()); + reduce(smem, mySum, tid, plus()); } __device__ __forceinline__ operator int() const diff --git a/modules/gpu/src/cuda/bf_knnmatch.cu b/modules/gpu/src/cuda/bf_knnmatch.cu index 6a778735b8..44e567b75a 100644 --- a/modules/gpu/src/cuda/bf_knnmatch.cu +++ b/modules/gpu/src/cuda/bf_knnmatch.cu @@ -42,10 +42,13 @@ #if !defined CUDA_DISABLER -#include "internal_shared.hpp" +#include "opencv2/gpu/device/common.hpp" +#include "opencv2/gpu/device/utility.hpp" +#include "opencv2/gpu/device/reduce.hpp" #include "opencv2/gpu/device/limits.hpp" #include "opencv2/gpu/device/vec_distance.hpp" #include "opencv2/gpu/device/datamov_utils.hpp" +#include "opencv2/gpu/device/warp_shuffle.hpp" namespace cv { namespace gpu { namespace device { @@ -59,6 +62,45 @@ namespace cv { namespace gpu { namespace device int& bestTrainIdx1, int& bestTrainIdx2, float* s_distance, int* s_trainIdx) { + #if __CUDA_ARCH__ >= 300 + (void) s_distance; + (void) s_trainIdx; + + float d1, d2; + int i1, i2; + + #pragma unroll + for (int i = BLOCK_SIZE / 2; i >= 1; i /= 2) + { + d1 = shfl_down(bestDistance1, i, BLOCK_SIZE); + d2 = shfl_down(bestDistance2, i, BLOCK_SIZE); + i1 = shfl_down(bestTrainIdx1, i, BLOCK_SIZE); + i2 = shfl_down(bestTrainIdx2, i, BLOCK_SIZE); + + if (bestDistance1 < d1) + { + if (d1 < bestDistance2) + { + bestDistance2 = d1; + bestTrainIdx2 = i1; + } + } + else + { + bestDistance2 = bestDistance1; + bestTrainIdx2 = bestTrainIdx1; + + bestDistance1 = d1; + bestTrainIdx1 = i1; + + if (d2 < bestDistance2) + { + bestDistance2 = d2; + bestTrainIdx2 = i2; + } + } + } + #else float myBestDistance1 = numeric_limits::max(); float myBestDistance2 = numeric_limits::max(); int myBestTrainIdx1 = -1; @@ -122,6 +164,7 @@ namespace cv { namespace gpu { namespace device bestTrainIdx1 = myBestTrainIdx1; bestTrainIdx2 = myBestTrainIdx2; + #endif } template @@ -130,6 +173,53 @@ namespace cv { namespace gpu { namespace device int& bestImgIdx1, int& bestImgIdx2, float* s_distance, int* s_trainIdx, int* s_imgIdx) { + #if __CUDA_ARCH__ >= 300 + (void) s_distance; + (void) s_trainIdx; + (void) s_imgIdx; + + float d1, d2; + int i1, i2; + int j1, j2; + + #pragma unroll + for (int i = BLOCK_SIZE / 2; i >= 1; i /= 2) + { + d1 = shfl_down(bestDistance1, i, BLOCK_SIZE); + d2 = shfl_down(bestDistance2, i, BLOCK_SIZE); + i1 = shfl_down(bestTrainIdx1, i, BLOCK_SIZE); + i2 = shfl_down(bestTrainIdx2, i, BLOCK_SIZE); + j1 = shfl_down(bestImgIdx1, i, BLOCK_SIZE); + j2 = shfl_down(bestImgIdx2, i, BLOCK_SIZE); + + if (bestDistance1 < d1) + { + if (d1 < bestDistance2) + { + bestDistance2 = d1; + bestTrainIdx2 = i1; + bestImgIdx2 = j1; + } + } + else + { + bestDistance2 = bestDistance1; + bestTrainIdx2 = bestTrainIdx1; + bestImgIdx2 = bestImgIdx1; + + bestDistance1 = d1; + bestTrainIdx1 = i1; + bestImgIdx1 = j1; + + if (d2 < bestDistance2) + { + bestDistance2 = d2; + bestTrainIdx2 = i2; + bestImgIdx2 = j2; + } + } + } + #else float myBestDistance1 = numeric_limits::max(); float myBestDistance2 = numeric_limits::max(); int myBestTrainIdx1 = -1; @@ -205,6 +295,7 @@ namespace cv { namespace gpu { namespace device bestImgIdx1 = myBestImgIdx1; bestImgIdx2 = myBestImgIdx2; + #endif } /////////////////////////////////////////////////////////////////////////////// @@ -1005,7 +1096,7 @@ namespace cv { namespace gpu { namespace device s_trainIdx[threadIdx.x] = bestIdx; __syncthreads(); - reducePredVal(s_dist, dist, s_trainIdx, bestIdx, threadIdx.x, less()); + reduceKeyVal(s_dist, dist, s_trainIdx, bestIdx, threadIdx.x, less()); if (threadIdx.x == 0) { @@ -1164,4 +1255,4 @@ namespace cv { namespace gpu { namespace device }}} // namespace cv { namespace gpu { namespace device { -#endif /* CUDA_DISABLER */ \ No newline at end of file +#endif /* CUDA_DISABLER */ diff --git a/modules/gpu/src/cuda/bf_match.cu b/modules/gpu/src/cuda/bf_match.cu index f50089ed94..9745dee82f 100644 --- a/modules/gpu/src/cuda/bf_match.cu +++ b/modules/gpu/src/cuda/bf_match.cu @@ -42,7 +42,9 @@ #if !defined CUDA_DISABLER -#include "internal_shared.hpp" +#include "opencv2/gpu/device/common.hpp" +#include "opencv2/gpu/device/utility.hpp" +#include "opencv2/gpu/device/reduce.hpp" #include "opencv2/gpu/device/limits.hpp" #include "opencv2/gpu/device/vec_distance.hpp" #include "opencv2/gpu/device/datamov_utils.hpp" @@ -60,12 +62,7 @@ namespace cv { namespace gpu { namespace device s_distance += threadIdx.y * BLOCK_SIZE; s_trainIdx += threadIdx.y * BLOCK_SIZE; - s_distance[threadIdx.x] = bestDistance; - s_trainIdx[threadIdx.x] = bestTrainIdx; - - __syncthreads(); - - reducePredVal(s_distance, bestDistance, s_trainIdx, bestTrainIdx, threadIdx.x, less()); + reduceKeyVal(s_distance, bestDistance, s_trainIdx, bestTrainIdx, threadIdx.x, less()); } template @@ -75,13 +72,7 @@ namespace cv { namespace gpu { namespace device s_trainIdx += threadIdx.y * BLOCK_SIZE; s_imgIdx += threadIdx.y * BLOCK_SIZE; - s_distance[threadIdx.x] = bestDistance; - s_trainIdx[threadIdx.x] = bestTrainIdx; - s_imgIdx [threadIdx.x] = bestImgIdx; - - __syncthreads(); - - reducePredVal2(s_distance, bestDistance, s_trainIdx, bestTrainIdx, s_imgIdx, bestImgIdx, threadIdx.x, less()); + reduceKeyVal(s_distance, bestDistance, smem_tuple(s_trainIdx, s_imgIdx), thrust::tie(bestTrainIdx, bestImgIdx), threadIdx.x, less()); } /////////////////////////////////////////////////////////////////////////////// @@ -782,4 +773,4 @@ namespace cv { namespace gpu { namespace device }}} // namespace cv { namespace gpu { namespace device { -#endif /* CUDA_DISABLER */ \ No newline at end of file +#endif /* CUDA_DISABLER */ diff --git a/modules/gpu/src/cuda/bf_radius_match.cu b/modules/gpu/src/cuda/bf_radius_match.cu index 934b8fe84c..bb828829f0 100644 --- a/modules/gpu/src/cuda/bf_radius_match.cu +++ b/modules/gpu/src/cuda/bf_radius_match.cu @@ -42,7 +42,8 @@ #if !defined CUDA_DISABLER -#include "internal_shared.hpp" +#include "opencv2/gpu/device/common.hpp" +#include "opencv2/gpu/device/utility.hpp" #include "opencv2/gpu/device/limits.hpp" #include "opencv2/gpu/device/vec_distance.hpp" #include "opencv2/gpu/device/datamov_utils.hpp" @@ -58,8 +59,6 @@ namespace cv { namespace gpu { namespace device __global__ void matchUnrolled(const PtrStepSz query, int imgIdx, const PtrStepSz train, float maxDistance, const Mask mask, PtrStepi bestTrainIdx, PtrStepi bestImgIdx, PtrStepf bestDistance, unsigned int* nMatches, int maxCount) { - #if defined(__CUDA_ARCH__) && (__CUDA_ARCH__ >= 110) - extern __shared__ int smem[]; const int queryIdx = blockIdx.y * BLOCK_SIZE + threadIdx.y; @@ -110,8 +109,6 @@ namespace cv { namespace gpu { namespace device bestDistance.ptr(queryIdx)[ind] = distVal; } } - - #endif } template @@ -170,8 +167,6 @@ namespace cv { namespace gpu { namespace device __global__ void match(const PtrStepSz query, int imgIdx, const PtrStepSz train, float maxDistance, const Mask mask, PtrStepi bestTrainIdx, PtrStepi bestImgIdx, PtrStepf bestDistance, unsigned int* nMatches, int maxCount) { - #if defined(__CUDA_ARCH__) && (__CUDA_ARCH__ >= 110) - extern __shared__ int smem[]; const int queryIdx = blockIdx.y * BLOCK_SIZE + threadIdx.y; @@ -221,8 +216,6 @@ namespace cv { namespace gpu { namespace device bestDistance.ptr(queryIdx)[ind] = distVal; } } - - #endif } template @@ -469,4 +462,4 @@ namespace cv { namespace gpu { namespace device }}} // namespace cv { namespace gpu { namespace device -#endif /* CUDA_DISABLER */ \ No newline at end of file +#endif /* CUDA_DISABLER */ From e2995956670488282b4743bc2841279f6481a2ee Mon Sep 17 00:00:00 2001 From: Vladislav Vinogradov Date: Mon, 12 Nov 2012 12:50:00 +0400 Subject: [PATCH 04/23] computeHypothesisScoresKernel --- modules/gpu/src/cuda/calib3d.cu | 27 ++++++++++++--------------- 1 file changed, 12 insertions(+), 15 deletions(-) diff --git a/modules/gpu/src/cuda/calib3d.cu b/modules/gpu/src/cuda/calib3d.cu index 40c847547e..0fd482c41a 100644 --- a/modules/gpu/src/cuda/calib3d.cu +++ b/modules/gpu/src/cuda/calib3d.cu @@ -42,9 +42,10 @@ #if !defined CUDA_DISABLER -#include "internal_shared.hpp" +#include "opencv2/gpu/device/common.hpp" #include "opencv2/gpu/device/transform.hpp" #include "opencv2/gpu/device/functional.hpp" +#include "opencv2/gpu/device/reduce.hpp" namespace cv { namespace gpu { namespace device { @@ -66,6 +67,8 @@ namespace cv { namespace gpu { namespace device crot1.x * p.x + crot1.y * p.y + crot1.z * p.z + ctransl.y, crot2.x * p.x + crot2.y * p.y + crot2.z * p.z + ctransl.z); } + __device__ __forceinline__ TransformOp() {} + __device__ __forceinline__ TransformOp(const TransformOp&) {} }; void call(const PtrStepSz src, const float* rot, @@ -103,6 +106,8 @@ namespace cv { namespace gpu { namespace device (cproj0.x * t.x + cproj0.y * t.y) / t.z + cproj0.z, (cproj1.x * t.x + cproj1.y * t.y) / t.z + cproj1.z); } + __device__ __forceinline__ ProjectOp() {} + __device__ __forceinline__ ProjectOp(const ProjectOp&) {} }; void call(const PtrStepSz src, const float* rot, @@ -134,6 +139,7 @@ namespace cv { namespace gpu { namespace device return x * x; } + template __global__ void computeHypothesisScoresKernel( const int num_points, const float3* object, const float2* image, const float dist_threshold, int* g_num_inliers) @@ -156,19 +162,11 @@ namespace cv { namespace gpu { namespace device ++num_inliers; } - extern __shared__ float s_num_inliers[]; - s_num_inliers[threadIdx.x] = num_inliers; - __syncthreads(); - - for (int step = blockDim.x / 2; step > 0; step >>= 1) - { - if (threadIdx.x < step) - s_num_inliers[threadIdx.x] += s_num_inliers[threadIdx.x + step]; - __syncthreads(); - } + __shared__ int s_num_inliers[BLOCK_SIZE]; + reduce(s_num_inliers, num_inliers, threadIdx.x, plus()); if (threadIdx.x == 0) - g_num_inliers[blockIdx.x] = s_num_inliers[0]; + g_num_inliers[blockIdx.x] = num_inliers; } void computeHypothesisScores( @@ -181,9 +179,8 @@ namespace cv { namespace gpu { namespace device dim3 threads(256); dim3 grid(num_hypotheses); - int smem_size = threads.x * sizeof(float); - computeHypothesisScoresKernel<<>>( + computeHypothesisScoresKernel<256><<>>( num_points, object, image, dist_threshold, hypothesis_scores); cudaSafeCall( cudaGetLastError() ); @@ -193,4 +190,4 @@ namespace cv { namespace gpu { namespace device }}} // namespace cv { namespace gpu { namespace device -#endif /* CUDA_DISABLER */ \ No newline at end of file +#endif /* CUDA_DISABLER */ From 28716d7f306cfc89d7e5507259e98a05bd9b7b8b Mon Sep 17 00:00:00 2001 From: Vladislav Vinogradov Date: Mon, 12 Nov 2012 13:02:17 +0400 Subject: [PATCH 05/23] Canny --- modules/gpu/include/opencv2/gpu/gpu.hpp | 24 +- modules/gpu/src/cuda/canny.cu | 694 ++++++++++++------------ modules/gpu/src/imgproc.cpp | 107 ++-- modules/gpu/test/test_imgproc.cpp | 2 +- 4 files changed, 400 insertions(+), 427 deletions(-) diff --git a/modules/gpu/include/opencv2/gpu/gpu.hpp b/modules/gpu/include/opencv2/gpu/gpu.hpp index 2cbd450852..4396a0a102 100644 --- a/modules/gpu/include/opencv2/gpu/gpu.hpp +++ b/modules/gpu/include/opencv2/gpu/gpu.hpp @@ -792,31 +792,23 @@ private: GpuMat lab, l, ab; }; - -struct CV_EXPORTS CannyBuf; - -CV_EXPORTS void Canny(const GpuMat& image, GpuMat& edges, double low_thresh, double high_thresh, int apperture_size = 3, bool L2gradient = false); -CV_EXPORTS void Canny(const GpuMat& image, CannyBuf& buf, GpuMat& edges, double low_thresh, double high_thresh, int apperture_size = 3, bool L2gradient = false); -CV_EXPORTS void Canny(const GpuMat& dx, const GpuMat& dy, GpuMat& edges, double low_thresh, double high_thresh, bool L2gradient = false); -CV_EXPORTS void Canny(const GpuMat& dx, const GpuMat& dy, CannyBuf& buf, GpuMat& edges, double low_thresh, double high_thresh, bool L2gradient = false); - struct CV_EXPORTS CannyBuf { - CannyBuf() {} - explicit CannyBuf(const Size& image_size, int apperture_size = 3) {create(image_size, apperture_size);} - CannyBuf(const GpuMat& dx_, const GpuMat& dy_); - void create(const Size& image_size, int apperture_size = 3); - void release(); GpuMat dx, dy; - GpuMat dx_buf, dy_buf; - GpuMat edgeBuf; - GpuMat trackBuf1, trackBuf2; + GpuMat mag; + GpuMat map; + GpuMat st1, st2; Ptr filterDX, filterDY; }; +CV_EXPORTS void Canny(const GpuMat& image, GpuMat& edges, double low_thresh, double high_thresh, int apperture_size = 3, bool L2gradient = false); +CV_EXPORTS void Canny(const GpuMat& image, CannyBuf& buf, GpuMat& edges, double low_thresh, double high_thresh, int apperture_size = 3, bool L2gradient = false); +CV_EXPORTS void Canny(const GpuMat& dx, const GpuMat& dy, GpuMat& edges, double low_thresh, double high_thresh, bool L2gradient = false); +CV_EXPORTS void Canny(const GpuMat& dx, const GpuMat& dy, CannyBuf& buf, GpuMat& edges, double low_thresh, double high_thresh, bool L2gradient = false); + class CV_EXPORTS ImagePyramid { public: diff --git a/modules/gpu/src/cuda/canny.cu b/modules/gpu/src/cuda/canny.cu index 3dc0486783..b08a61c83e 100644 --- a/modules/gpu/src/cuda/canny.cu +++ b/modules/gpu/src/cuda/canny.cu @@ -43,459 +43,463 @@ #if !defined CUDA_DISABLER #include -#include -#include "internal_shared.hpp" +#include "opencv2/gpu/device/common.hpp" +#include "opencv2/gpu/device/emulation.hpp" +#include "opencv2/gpu/device/transform.hpp" +#include "opencv2/gpu/device/functional.hpp" +#include "opencv2/gpu/device/utility.hpp" -namespace cv { namespace gpu { namespace device +using namespace cv::gpu; +using namespace cv::gpu::device; + +namespace { - namespace canny + struct L1 : binary_function { - __global__ void calcSobelRowPass(const PtrStepb src, PtrStepi dx_buf, PtrStepi dy_buf, int rows, int cols) + __device__ __forceinline__ float operator ()(int x, int y) const { - __shared__ int smem[16][18]; - - const int j = blockIdx.x * blockDim.x + threadIdx.x; - const int i = blockIdx.y * blockDim.y + threadIdx.y; - - if (i < rows) - { - smem[threadIdx.y][threadIdx.x + 1] = src.ptr(i)[j]; - if (threadIdx.x == 0) - { - smem[threadIdx.y][0] = src.ptr(i)[::max(j - 1, 0)]; - smem[threadIdx.y][17] = src.ptr(i)[::min(j + 16, cols - 1)]; - } - __syncthreads(); - - if (j < cols) - { - dx_buf.ptr(i)[j] = -smem[threadIdx.y][threadIdx.x] + smem[threadIdx.y][threadIdx.x + 2]; - dy_buf.ptr(i)[j] = smem[threadIdx.y][threadIdx.x] + 2 * smem[threadIdx.y][threadIdx.x + 1] + smem[threadIdx.y][threadIdx.x + 2]; - } - } + return ::abs(x) + ::abs(y); } - void calcSobelRowPass_gpu(PtrStepb src, PtrStepi dx_buf, PtrStepi dy_buf, int rows, int cols) + __device__ __forceinline__ L1() {} + __device__ __forceinline__ L1(const L1&) {} + }; + struct L2 : binary_function + { + __device__ __forceinline__ float operator ()(int x, int y) const { - dim3 block(16, 16, 1); - dim3 grid(divUp(cols, block.x), divUp(rows, block.y), 1); + return ::sqrtf(x * x + y * y); + } - calcSobelRowPass<<>>(src, dx_buf, dy_buf, rows, cols); - cudaSafeCall( cudaGetLastError() ); + __device__ __forceinline__ L2() {} + __device__ __forceinline__ L2(const L2&) {} + }; +} - cudaSafeCall( cudaDeviceSynchronize() ); - } +namespace cv { namespace gpu { namespace device +{ + template <> struct TransformFunctorTraits : DefaultTransformFunctorTraits + { + enum { smart_shift = 4 }; + }; + template <> struct TransformFunctorTraits : DefaultTransformFunctorTraits + { + enum { smart_shift = 4 }; + }; +}}} - struct L1 - { - static __device__ __forceinline__ float calc(int x, int y) - { - return ::abs(x) + ::abs(y); - } - }; - struct L2 - { - static __device__ __forceinline__ float calc(int x, int y) - { - return ::sqrtf(x * x + y * y); - } - }; +namespace +{ + texture tex_src(false, cudaFilterModePoint, cudaAddressModeClamp); + struct SrcTex + { + const int xoff; + const int yoff; + __host__ SrcTex(int _xoff, int _yoff) : xoff(_xoff), yoff(_yoff) {} - template __global__ void calcMagnitude(const PtrStepi dx_buf, const PtrStepi dy_buf, - PtrStepi dx, PtrStepi dy, PtrStepf mag, int rows, int cols) + __device__ __forceinline__ int operator ()(int y, int x) const { - __shared__ int sdx[18][16]; - __shared__ int sdy[18][16]; + return tex2D(tex_src, x + xoff, y + yoff); + } + }; - const int j = blockIdx.x * blockDim.x + threadIdx.x; - const int i = blockIdx.y * blockDim.y + threadIdx.y; + template __global__ + void calcMagnitude(const SrcTex src, PtrStepi dx, PtrStepi dy, PtrStepSzf mag, const Norm norm) + { + const int x = blockIdx.x * blockDim.x + threadIdx.x; + const int y = blockIdx.y * blockDim.y + threadIdx.y; - if (j < cols) - { - sdx[threadIdx.y + 1][threadIdx.x] = dx_buf.ptr(i)[j]; - sdy[threadIdx.y + 1][threadIdx.x] = dy_buf.ptr(i)[j]; - if (threadIdx.y == 0) - { - sdx[0][threadIdx.x] = dx_buf.ptr(::max(i - 1, 0))[j]; - sdx[17][threadIdx.x] = dx_buf.ptr(::min(i + 16, rows - 1))[j]; + if (y >= mag.rows || x >= mag.cols) + return; - sdy[0][threadIdx.x] = dy_buf.ptr(::max(i - 1, 0))[j]; - sdy[17][threadIdx.x] = dy_buf.ptr(::min(i + 16, rows - 1))[j]; - } - __syncthreads(); + int dxVal = (src(y - 1, x + 1) + 2 * src(y, x + 1) + src(y + 1, x + 1)) - (src(y - 1, x - 1) + 2 * src(y, x - 1) + src(y + 1, x - 1)); + int dyVal = (src(y + 1, x - 1) + 2 * src(y + 1, x) + src(y + 1, x + 1)) - (src(y - 1, x - 1) + 2 * src(y - 1, x) + src(y - 1, x + 1)); - if (i < rows) - { - int x = sdx[threadIdx.y][threadIdx.x] + 2 * sdx[threadIdx.y + 1][threadIdx.x] + sdx[threadIdx.y + 2][threadIdx.x]; - int y = -sdy[threadIdx.y][threadIdx.x] + sdy[threadIdx.y + 2][threadIdx.x]; + dx(y, x) = dxVal; + dy(y, x) = dyVal; - dx.ptr(i)[j] = x; - dy.ptr(i)[j] = y; + mag(y, x) = norm(dxVal, dyVal); + } +} - mag.ptr(i + 1)[j + 1] = Norm::calc(x, y); - } - } - } +namespace canny +{ + void calcMagnitude(PtrStepSzb srcWhole, int xoff, int yoff, PtrStepSzi dx, PtrStepSzi dy, PtrStepSzf mag, bool L2Grad) + { + const dim3 block(16, 16); + const dim3 grid(divUp(mag.cols, block.x), divUp(mag.rows, block.y)); - void calcMagnitude_gpu(PtrStepi dx_buf, PtrStepi dy_buf, PtrStepi dx, PtrStepi dy, PtrStepf mag, int rows, int cols, bool L2Grad) - { - dim3 block(16, 16, 1); - dim3 grid(divUp(cols, block.x), divUp(rows, block.y), 1); + bindTexture(&tex_src, srcWhole); + SrcTex src(xoff, yoff); - if (L2Grad) - calcMagnitude<<>>(dx_buf, dy_buf, dx, dy, mag, rows, cols); - else - calcMagnitude<<>>(dx_buf, dy_buf, dx, dy, mag, rows, cols); + if (L2Grad) + { + L2 norm; + ::calcMagnitude<<>>(src, dx, dy, mag, norm); + } + else + { + L1 norm; + ::calcMagnitude<<>>(src, dx, dy, mag, norm); + } - cudaSafeCall( cudaGetLastError() ); + cudaSafeCall( cudaGetLastError() ); - cudaSafeCall(cudaThreadSynchronize()); - } + cudaSafeCall(cudaThreadSynchronize()); + } - template __global__ void calcMagnitude(PtrStepi dx, PtrStepi dy, PtrStepf mag, int rows, int cols) + void calcMagnitude(PtrStepSzi dx, PtrStepSzi dy, PtrStepSzf mag, bool L2Grad) + { + if (L2Grad) { - const int j = blockIdx.x * blockDim.x + threadIdx.x; - const int i = blockIdx.y * blockDim.y + threadIdx.y; - - if (i < rows && j < cols) - mag.ptr(i + 1)[j + 1] = Norm::calc(dx.ptr(i)[j], dy.ptr(i)[j]); + L2 norm; + transform(dx, dy, mag, norm, WithOutMask(), 0); } - - void calcMagnitude_gpu(PtrStepi dx, PtrStepi dy, PtrStepf mag, int rows, int cols, bool L2Grad) + else { - dim3 block(16, 16, 1); - dim3 grid(divUp(cols, block.x), divUp(rows, block.y), 1); + L1 norm; + transform(dx, dy, mag, norm, WithOutMask(), 0); + } + } +} - if (L2Grad) - calcMagnitude<<>>(dx, dy, mag, rows, cols); - else - calcMagnitude<<>>(dx, dy, mag, rows, cols); +////////////////////////////////////////////////////////////////////////////////////////// - cudaSafeCall( cudaGetLastError() ); +namespace +{ + texture tex_mag(false, cudaFilterModePoint, cudaAddressModeClamp); - cudaSafeCall( cudaDeviceSynchronize() ); - } + __global__ void calcMap(const PtrStepSzi dx, const PtrStepi dy, PtrStepi map, const float low_thresh, const float high_thresh) + { + const int CANNY_SHIFT = 15; + const int TG22 = (int)(0.4142135623730950488016887242097*(1<= dx.cols || y >= dx.rows) + return; - __global__ void calcMap(const PtrStepi dx, const PtrStepi dy, const PtrStepf mag, PtrStepi map, int rows, int cols, float low_thresh, float high_thresh) - { - __shared__ float smem[18][18]; + int dxVal = dx(y, x); + int dyVal = dy(y, x); - const int j = blockIdx.x * 16 + threadIdx.x; - const int i = blockIdx.y * 16 + threadIdx.y; + const int s = (dxVal ^ dyVal) < 0 ? -1 : 1; + const float m = tex2D(tex_mag, x, y); - const int tid = threadIdx.y * 16 + threadIdx.x; - const int lx = tid % 18; - const int ly = tid / 18; + dxVal = ::abs(dxVal); + dyVal = ::abs(dyVal); - if (ly < 14) - smem[ly][lx] = mag.ptr(blockIdx.y * 16 + ly)[blockIdx.x * 16 + lx]; + // 0 - the pixel can not belong to an edge + // 1 - the pixel might belong to an edge + // 2 - the pixel does belong to an edge + int edge_type = 0; - if (ly < 4 && blockIdx.y * 16 + ly + 14 <= rows && blockIdx.x * 16 + lx <= cols) - smem[ly + 14][lx] = mag.ptr(blockIdx.y * 16 + ly + 14)[blockIdx.x * 16 + lx]; + if (m > low_thresh) + { + const int tg22x = dxVal * TG22; + const int tg67x = tg22x + ((dxVal + dxVal) << CANNY_SHIFT); - __syncthreads(); + dyVal <<= CANNY_SHIFT; - if (i < rows && j < cols) + if (dyVal < tg22x) + { + if (m > tex2D(tex_mag, x - 1, y) && m >= tex2D(tex_mag, x + 1, y)) + edge_type = 1 + (int)(m > high_thresh); + } + else if(dyVal > tg67x) { - int x = dx.ptr(i)[j]; - int y = dy.ptr(i)[j]; - const int s = (x ^ y) < 0 ? -1 : 1; - const float m = smem[threadIdx.y + 1][threadIdx.x + 1]; + if (m > tex2D(tex_mag, x, y - 1) && m >= tex2D(tex_mag, x, y + 1)) + edge_type = 1 + (int)(m > high_thresh); + } + else + { + if (m > tex2D(tex_mag, x - s, y - 1) && m >= tex2D(tex_mag, x + s, y + 1)) + edge_type = 1 + (int)(m > high_thresh); + } + } - x = ::abs(x); - y = ::abs(y); + map(y, x) = edge_type; + } +} - // 0 - the pixel can not belong to an edge - // 1 - the pixel might belong to an edge - // 2 - the pixel does belong to an edge - int edge_type = 0; +namespace canny +{ + void calcMap(PtrStepSzi dx, PtrStepSzi dy, PtrStepSzf mag, PtrStepSzi map, float low_thresh, float high_thresh) + { + const dim3 block(16, 16); + const dim3 grid(divUp(dx.cols, block.x), divUp(dx.rows, block.y)); - if (m > low_thresh) - { - const int tg22x = x * TG22; - const int tg67x = tg22x + ((x + x) << CANNY_SHIFT); - - y <<= CANNY_SHIFT; - - if (y < tg22x) - { - if (m > smem[threadIdx.y + 1][threadIdx.x] && m >= smem[threadIdx.y + 1][threadIdx.x + 2]) - edge_type = 1 + (int)(m > high_thresh); - } - else if( y > tg67x ) - { - if (m > smem[threadIdx.y][threadIdx.x + 1] && m >= smem[threadIdx.y + 2][threadIdx.x + 1]) - edge_type = 1 + (int)(m > high_thresh); - } - else - { - if (m > smem[threadIdx.y][threadIdx.x + 1 - s] && m > smem[threadIdx.y + 2][threadIdx.x + 1 + s]) - edge_type = 1 + (int)(m > high_thresh); - } - } + bindTexture(&tex_mag, mag); - map.ptr(i + 1)[j + 1] = edge_type; - } - } + ::calcMap<<>>(dx, dy, map, low_thresh, high_thresh); + cudaSafeCall( cudaGetLastError() ); - #undef CANNY_SHIFT - #undef TG22 + cudaSafeCall( cudaDeviceSynchronize() ); + } +} - void calcMap_gpu(PtrStepi dx, PtrStepi dy, PtrStepf mag, PtrStepi map, int rows, int cols, float low_thresh, float high_thresh) +////////////////////////////////////////////////////////////////////////////////////////// + +namespace +{ + __device__ int counter = 0; + + __global__ void edgesHysteresisLocal(PtrStepSzi map, ushort2* st) + { + __shared__ volatile int smem[18][18]; + + const int x = blockIdx.x * blockDim.x + threadIdx.x; + const int y = blockIdx.y * blockDim.y + threadIdx.y; + + smem[threadIdx.y + 1][threadIdx.x + 1] = x < map.cols && y < map.rows ? map(y, x) : 0; + if (threadIdx.y == 0) + smem[0][threadIdx.x + 1] = y > 0 ? map(y - 1, x) : 0; + if (threadIdx.y == blockDim.y - 1) + smem[blockDim.y + 1][threadIdx.x + 1] = y + 1 < map.rows ? map(y + 1, x) : 0; + if (threadIdx.x == 0) + smem[threadIdx.y + 1][0] = x > 0 ? map(y, x - 1) : 0; + if (threadIdx.x == blockDim.x - 1) + smem[threadIdx.y + 1][blockDim.x + 1] = x + 1 < map.cols ? map(y, x + 1) : 0; + if (threadIdx.x == 0 && threadIdx.y == 0) + smem[0][0] = y > 0 && x > 0 ? map(y - 1, x - 1) : 0; + if (threadIdx.x == blockDim.x - 1 && threadIdx.y == 0) + smem[0][blockDim.x + 1] = y > 0 && x + 1 < map.cols ? map(y - 1, x + 1) : 0; + if (threadIdx.x == 0 && threadIdx.y == blockDim.y - 1) + smem[blockDim.y + 1][0] = y + 1 < map.rows && x > 0 ? map(y + 1, x - 1) : 0; + if (threadIdx.x == blockDim.x - 1 && threadIdx.y == blockDim.y - 1) + smem[blockDim.y + 1][blockDim.x + 1] = y + 1 < map.rows && x + 1 < map.cols ? map(y + 1, x + 1) : 0; + + __syncthreads(); + + if (x >= map.cols || y >= map.rows) + return; + + int n; + + #pragma unroll + for (int k = 0; k < 16; ++k) { - dim3 block(16, 16, 1); - dim3 grid(divUp(cols, block.x), divUp(rows, block.y), 1); + n = 0; - calcMap<<>>(dx, dy, mag, map, rows, cols, low_thresh, high_thresh); - cudaSafeCall( cudaGetLastError() ); + if (smem[threadIdx.y + 1][threadIdx.x + 1] == 1) + { + n += smem[threadIdx.y ][threadIdx.x ] == 2; + n += smem[threadIdx.y ][threadIdx.x + 1] == 2; + n += smem[threadIdx.y ][threadIdx.x + 2] == 2; - cudaSafeCall( cudaDeviceSynchronize() ); - } + n += smem[threadIdx.y + 1][threadIdx.x ] == 2; + n += smem[threadIdx.y + 1][threadIdx.x + 2] == 2; - ////////////////////////////////////////////////////////////////////////////////////////// + n += smem[threadIdx.y + 2][threadIdx.x ] == 2; + n += smem[threadIdx.y + 2][threadIdx.x + 1] == 2; + n += smem[threadIdx.y + 2][threadIdx.x + 2] == 2; + } - __device__ unsigned int counter = 0; + if (n > 0) + smem[threadIdx.y + 1][threadIdx.x + 1] = 2; + } - __global__ void edgesHysteresisLocal(PtrStepi map, ushort2* st, int rows, int cols) - { - #if defined (__CUDA_ARCH__) && (__CUDA_ARCH__ >= 120) + const int e = smem[threadIdx.y + 1][threadIdx.x + 1]; - __shared__ int smem[18][18]; + map(y, x) = e; - const int j = blockIdx.x * 16 + threadIdx.x; - const int i = blockIdx.y * 16 + threadIdx.y; + n = 0; - const int tid = threadIdx.y * 16 + threadIdx.x; - const int lx = tid % 18; - const int ly = tid / 18; + if (e == 2) + { + n += smem[threadIdx.y ][threadIdx.x ] == 1; + n += smem[threadIdx.y ][threadIdx.x + 1] == 1; + n += smem[threadIdx.y ][threadIdx.x + 2] == 1; - if (ly < 14) - smem[ly][lx] = map.ptr(blockIdx.y * 16 + ly)[blockIdx.x * 16 + lx]; + n += smem[threadIdx.y + 1][threadIdx.x ] == 1; + n += smem[threadIdx.y + 1][threadIdx.x + 2] == 1; - if (ly < 4 && blockIdx.y * 16 + ly + 14 <= rows && blockIdx.x * 16 + lx <= cols) - smem[ly + 14][lx] = map.ptr(blockIdx.y * 16 + ly + 14)[blockIdx.x * 16 + lx]; + n += smem[threadIdx.y + 2][threadIdx.x ] == 1; + n += smem[threadIdx.y + 2][threadIdx.x + 1] == 1; + n += smem[threadIdx.y + 2][threadIdx.x + 2] == 1; + } - __syncthreads(); + if (n > 0) + { + const int ind = ::atomicAdd(&counter, 1); + st[ind] = make_ushort2(x, y); + } + } +} - if (i < rows && j < cols) - { - int n; +namespace canny +{ + void edgesHysteresisLocal(PtrStepSzi map, ushort2* st1) + { + void* counter_ptr; + cudaSafeCall( cudaGetSymbolAddress(&counter_ptr, counter) ); - #pragma unroll - for (int k = 0; k < 16; ++k) - { - n = 0; + cudaSafeCall( cudaMemset(counter_ptr, 0, sizeof(int)) ); - if (smem[threadIdx.y + 1][threadIdx.x + 1] == 1) - { - n += smem[threadIdx.y ][threadIdx.x ] == 2; - n += smem[threadIdx.y ][threadIdx.x + 1] == 2; - n += smem[threadIdx.y ][threadIdx.x + 2] == 2; + const dim3 block(16, 16); + const dim3 grid(divUp(map.cols, block.x), divUp(map.rows, block.y)); - n += smem[threadIdx.y + 1][threadIdx.x ] == 2; - n += smem[threadIdx.y + 1][threadIdx.x + 2] == 2; + ::edgesHysteresisLocal<<>>(map, st1); + cudaSafeCall( cudaGetLastError() ); - n += smem[threadIdx.y + 2][threadIdx.x ] == 2; - n += smem[threadIdx.y + 2][threadIdx.x + 1] == 2; - n += smem[threadIdx.y + 2][threadIdx.x + 2] == 2; - } + cudaSafeCall( cudaDeviceSynchronize() ); + } +} - if (n > 0) - smem[threadIdx.y + 1][threadIdx.x + 1] = 2; - } +////////////////////////////////////////////////////////////////////////////////////////// - const int e = smem[threadIdx.y + 1][threadIdx.x + 1]; +namespace +{ + __constant__ int c_dx[8] = {-1, 0, 1, -1, 1, -1, 0, 1}; + __constant__ int c_dy[8] = {-1, -1, -1, 0, 0, 1, 1, 1}; - map.ptr(i + 1)[j + 1] = e; + __global__ void edgesHysteresisGlobal(PtrStepSzi map, ushort2* st1, ushort2* st2, const int count) + { + const int stack_size = 512; - n = 0; + __shared__ int s_counter; + __shared__ int s_ind; + __shared__ ushort2 s_st[stack_size]; - if (e == 2) - { - n += smem[threadIdx.y ][threadIdx.x ] == 1; - n += smem[threadIdx.y ][threadIdx.x + 1] == 1; - n += smem[threadIdx.y ][threadIdx.x + 2] == 1; + if (threadIdx.x == 0) + s_counter = 0; - n += smem[threadIdx.y + 1][threadIdx.x ] == 1; - n += smem[threadIdx.y + 1][threadIdx.x + 2] == 1; + __syncthreads(); - n += smem[threadIdx.y + 2][threadIdx.x ] == 1; - n += smem[threadIdx.y + 2][threadIdx.x + 1] == 1; - n += smem[threadIdx.y + 2][threadIdx.x + 2] == 1; - } + int ind = blockIdx.y * gridDim.x + blockIdx.x; - if (n > 0) - { - const unsigned int ind = atomicInc(&counter, (unsigned int)(-1)); - st[ind] = make_ushort2(j + 1, i + 1); - } - } + if (ind >= count) + return; - #endif - } + ushort2 pos = st1[ind]; - void edgesHysteresisLocal_gpu(PtrStepi map, ushort2* st1, int rows, int cols) + if (threadIdx.x < 8) { - void* counter_ptr; - cudaSafeCall( cudaGetSymbolAddress(&counter_ptr, counter) ); - - cudaSafeCall( cudaMemset(counter_ptr, 0, sizeof(unsigned int)) ); + pos.x += c_dx[threadIdx.x]; + pos.y += c_dy[threadIdx.x]; - dim3 block(16, 16, 1); - dim3 grid(divUp(cols, block.x), divUp(rows, block.y), 1); + if (pos.x > 0 && pos.x <= map.cols && pos.y > 0 && pos.y <= map.rows && map(pos.y, pos.x) == 1) + { + map(pos.y, pos.x) = 2; - edgesHysteresisLocal<<>>(map, st1, rows, cols); - cudaSafeCall( cudaGetLastError() ); + ind = Emulation::smem::atomicAdd(&s_counter, 1); - cudaSafeCall( cudaDeviceSynchronize() ); + s_st[ind] = pos; + } } - __constant__ int c_dx[8] = {-1, 0, 1, -1, 1, -1, 0, 1}; - __constant__ int c_dy[8] = {-1, -1, -1, 0, 0, 1, 1, 1}; + __syncthreads(); - __global__ void edgesHysteresisGlobal(PtrStepi map, ushort2* st1, ushort2* st2, int rows, int cols, int count) + while (s_counter > 0 && s_counter <= stack_size - blockDim.x) { - #if defined (__CUDA_ARCH__) && __CUDA_ARCH__ >= 120 + const int subTaskIdx = threadIdx.x >> 3; + const int portion = ::min(s_counter, blockDim.x >> 3); - const int stack_size = 512; + if (subTaskIdx < portion) + pos = s_st[s_counter - 1 - subTaskIdx]; - __shared__ unsigned int s_counter; - __shared__ unsigned int s_ind; - __shared__ ushort2 s_st[stack_size]; + __syncthreads(); if (threadIdx.x == 0) - s_counter = 0; - __syncthreads(); + s_counter -= portion; - int ind = blockIdx.y * gridDim.x + blockIdx.x; + __syncthreads(); - if (ind < count) + if (subTaskIdx < portion) { - ushort2 pos = st1[ind]; + pos.x += c_dx[threadIdx.x & 7]; + pos.y += c_dy[threadIdx.x & 7]; - if (pos.x > 0 && pos.x <= cols && pos.y > 0 && pos.y <= rows) + if (pos.x > 0 && pos.x <= map.cols && pos.y > 0 && pos.y <= map.rows && map(pos.y, pos.x) == 1) { - if (threadIdx.x < 8) - { - pos.x += c_dx[threadIdx.x]; - pos.y += c_dy[threadIdx.x]; - - if (map.ptr(pos.y)[pos.x] == 1) - { - map.ptr(pos.y)[pos.x] = 2; - - ind = atomicInc(&s_counter, (unsigned int)(-1)); - - s_st[ind] = pos; - } - } - __syncthreads(); - - while (s_counter > 0 && s_counter <= stack_size - blockDim.x) - { - const int subTaskIdx = threadIdx.x >> 3; - const int portion = ::min(s_counter, blockDim.x >> 3); - - pos.x = pos.y = 0; - - if (subTaskIdx < portion) - pos = s_st[s_counter - 1 - subTaskIdx]; - __syncthreads(); - - if (threadIdx.x == 0) - s_counter -= portion; - __syncthreads(); - - if (pos.x > 0 && pos.x <= cols && pos.y > 0 && pos.y <= rows) - { - pos.x += c_dx[threadIdx.x & 7]; - pos.y += c_dy[threadIdx.x & 7]; - - if (map.ptr(pos.y)[pos.x] == 1) - { - map.ptr(pos.y)[pos.x] = 2; - - ind = atomicInc(&s_counter, (unsigned int)(-1)); - - s_st[ind] = pos; - } - } - __syncthreads(); - } - - if (s_counter > 0) - { - if (threadIdx.x == 0) - { - ind = atomicAdd(&counter, s_counter); - s_ind = ind - s_counter; - } - __syncthreads(); - - ind = s_ind; - - for (int i = threadIdx.x; i < s_counter; i += blockDim.x) - { - st2[ind + i] = s_st[i]; - } - } + map(pos.y, pos.x) = 2; + + ind = Emulation::smem::atomicAdd(&s_counter, 1); + + s_st[ind] = pos; } } - #endif + __syncthreads(); } - void edgesHysteresisGlobal_gpu(PtrStepi map, ushort2* st1, ushort2* st2, int rows, int cols) + if (s_counter > 0) { - void* counter_ptr; - cudaSafeCall( cudaGetSymbolAddress(&counter_ptr, counter) ); - - unsigned int count; - cudaSafeCall( cudaMemcpy(&count, counter_ptr, sizeof(unsigned int), cudaMemcpyDeviceToHost) ); - - while (count > 0) + if (threadIdx.x == 0) { - cudaSafeCall( cudaMemset(counter_ptr, 0, sizeof(unsigned int)) ); - - dim3 block(128, 1, 1); - dim3 grid(std::min(count, 65535u), divUp(count, 65535), 1); - edgesHysteresisGlobal<<>>(map, st1, st2, rows, cols, count); - cudaSafeCall( cudaGetLastError() ); + ind = ::atomicAdd(&counter, s_counter); + s_ind = ind - s_counter; + } - cudaSafeCall( cudaDeviceSynchronize() ); + __syncthreads(); - cudaSafeCall( cudaMemcpy(&count, counter_ptr, sizeof(unsigned int), cudaMemcpyDeviceToHost) ); + ind = s_ind; - std::swap(st1, st2); - } + for (int i = threadIdx.x; i < s_counter; i += blockDim.x) + st2[ind + i] = s_st[i]; } + } +} - __global__ void getEdges(PtrStepi map, PtrStepb dst, int rows, int cols) - { - const int j = blockIdx.x * 16 + threadIdx.x; - const int i = blockIdx.y * 16 + threadIdx.y; +namespace canny +{ + void edgesHysteresisGlobal(PtrStepSzi map, ushort2* st1, ushort2* st2) + { + void* counter_ptr; + cudaSafeCall( cudaGetSymbolAddress(&counter_ptr, ::counter) ); - if (i < rows && j < cols) - dst.ptr(i)[j] = (uchar)(-(map.ptr(i + 1)[j + 1] >> 1)); - } + int count; + cudaSafeCall( cudaMemcpy(&count, counter_ptr, sizeof(int), cudaMemcpyDeviceToHost) ); - void getEdges_gpu(PtrStepi map, PtrStepb dst, int rows, int cols) + while (count > 0) { - dim3 block(16, 16, 1); - dim3 grid(divUp(cols, block.x), divUp(rows, block.y), 1); + cudaSafeCall( cudaMemset(counter_ptr, 0, sizeof(int)) ); + + const dim3 block(128); + const dim3 grid(::min(count, 65535u), divUp(count, 65535), 1); - getEdges<<>>(map, dst, rows, cols); + ::edgesHysteresisGlobal<<>>(map, st1, st2, count); cudaSafeCall( cudaGetLastError() ); cudaSafeCall( cudaDeviceSynchronize() ); + + cudaSafeCall( cudaMemcpy(&count, counter_ptr, sizeof(int), cudaMemcpyDeviceToHost) ); + + std::swap(st1, st2); } - } // namespace canny -}}} // namespace cv { namespace gpu { namespace device + } +} + +////////////////////////////////////////////////////////////////////////////////////////// +namespace +{ + struct GetEdges : unary_function + { + __device__ __forceinline__ uchar operator ()(int e) const + { + return (uchar)(-(e >> 1)); + } + + __device__ __forceinline__ GetEdges() {} + __device__ __forceinline__ GetEdges(const GetEdges&) {} + }; +} + +namespace cv { namespace gpu { namespace device +{ + template <> struct TransformFunctorTraits : DefaultTransformFunctorTraits + { + enum { smart_shift = 4 }; + }; +}}} + +namespace canny +{ + void getEdges(PtrStepSzi map, PtrStepSzb dst) + { + transform(map, dst, GetEdges(), WithOutMask(), 0); + } +} -#endif /* CUDA_DISABLER */ \ No newline at end of file +#endif /* CUDA_DISABLER */ diff --git a/modules/gpu/src/imgproc.cpp b/modules/gpu/src/imgproc.cpp index 0bf9c81c2e..b733faf5d0 100644 --- a/modules/gpu/src/imgproc.cpp +++ b/modules/gpu/src/imgproc.cpp @@ -91,7 +91,6 @@ void cv::gpu::Canny(const GpuMat&, GpuMat&, double, double, int, bool) { throw_n void cv::gpu::Canny(const GpuMat&, CannyBuf&, GpuMat&, double, double, int, bool) { throw_nogpu(); } void cv::gpu::Canny(const GpuMat&, const GpuMat&, GpuMat&, double, double, bool) { throw_nogpu(); } void cv::gpu::Canny(const GpuMat&, const GpuMat&, CannyBuf&, GpuMat&, double, double, bool) { throw_nogpu(); } -cv::gpu::CannyBuf::CannyBuf(const GpuMat&, const GpuMat&) { throw_nogpu(); } void cv::gpu::CannyBuf::create(const Size&, int) { throw_nogpu(); } void cv::gpu::CannyBuf::release() { throw_nogpu(); } @@ -1466,92 +1465,76 @@ void cv::gpu::convolve(const GpuMat& image, const GpuMat& templ, GpuMat& result, ////////////////////////////////////////////////////////////////////////////// // Canny -cv::gpu::CannyBuf::CannyBuf(const GpuMat& dx_, const GpuMat& dy_) : dx(dx_), dy(dy_) -{ - CV_Assert(dx_.type() == CV_32SC1 && dy_.type() == CV_32SC1 && dx_.size() == dy_.size()); - - create(dx_.size(), -1); -} - void cv::gpu::CannyBuf::create(const Size& image_size, int apperture_size) { - ensureSizeIsEnough(image_size, CV_32SC1, dx); - ensureSizeIsEnough(image_size, CV_32SC1, dy); - - if (apperture_size == 3) + if (apperture_size > 0) { - ensureSizeIsEnough(image_size, CV_32SC1, dx_buf); - ensureSizeIsEnough(image_size, CV_32SC1, dy_buf); - } - else if(apperture_size > 0) - { - if (!filterDX) + ensureSizeIsEnough(image_size, CV_32SC1, dx); + ensureSizeIsEnough(image_size, CV_32SC1, dy); + + if (apperture_size != 3) + { filterDX = createDerivFilter_GPU(CV_8UC1, CV_32S, 1, 0, apperture_size, BORDER_REPLICATE); - if (!filterDY) filterDY = createDerivFilter_GPU(CV_8UC1, CV_32S, 0, 1, apperture_size, BORDER_REPLICATE); + } } - ensureSizeIsEnough(image_size.height + 2, image_size.width + 2, CV_32FC1, edgeBuf); + ensureSizeIsEnough(image_size, CV_32FC1, mag); + ensureSizeIsEnough(image_size, CV_32SC1, map); - ensureSizeIsEnough(1, image_size.width * image_size.height, CV_16UC2, trackBuf1); - ensureSizeIsEnough(1, image_size.width * image_size.height, CV_16UC2, trackBuf2); + ensureSizeIsEnough(1, image_size.area(), CV_16UC2, st1); + ensureSizeIsEnough(1, image_size.area(), CV_16UC2, st2); } void cv::gpu::CannyBuf::release() { dx.release(); dy.release(); - dx_buf.release(); - dy_buf.release(); - edgeBuf.release(); - trackBuf1.release(); - trackBuf2.release(); + mag.release(); + map.release(); + st1.release(); + st2.release(); } -namespace cv { namespace gpu { namespace device +namespace canny { - namespace canny - { - void calcSobelRowPass_gpu(PtrStepb src, PtrStepi dx_buf, PtrStepi dy_buf, int rows, int cols); - - void calcMagnitude_gpu(PtrStepi dx_buf, PtrStepi dy_buf, PtrStepi dx, PtrStepi dy, PtrStepf mag, int rows, int cols, bool L2Grad); - void calcMagnitude_gpu(PtrStepi dx, PtrStepi dy, PtrStepf mag, int rows, int cols, bool L2Grad); + void calcMagnitude(PtrStepSzb srcWhole, int xoff, int yoff, PtrStepSzi dx, PtrStepSzi dy, PtrStepSzf mag, bool L2Grad); + void calcMagnitude(PtrStepSzi dx, PtrStepSzi dy, PtrStepSzf mag, bool L2Grad); - void calcMap_gpu(PtrStepi dx, PtrStepi dy, PtrStepf mag, PtrStepi map, int rows, int cols, float low_thresh, float high_thresh); + void calcMap(PtrStepSzi dx, PtrStepSzi dy, PtrStepSzf mag, PtrStepSzi map, float low_thresh, float high_thresh); - void edgesHysteresisLocal_gpu(PtrStepi map, ushort2* st1, int rows, int cols); + void edgesHysteresisLocal(PtrStepSzi map, ushort2* st1); - void edgesHysteresisGlobal_gpu(PtrStepi map, ushort2* st1, ushort2* st2, int rows, int cols); + void edgesHysteresisGlobal(PtrStepSzi map, ushort2* st1, ushort2* st2); - void getEdges_gpu(PtrStepi map, PtrStepb dst, int rows, int cols); - } -}}} + void getEdges(PtrStepSzi map, PtrStepSzb dst); +} namespace { - void CannyCaller(CannyBuf& buf, GpuMat& dst, float low_thresh, float high_thresh) + void CannyCaller(const GpuMat& dx, const GpuMat& dy, CannyBuf& buf, GpuMat& dst, float low_thresh, float high_thresh) { - using namespace ::cv::gpu::device::canny; + using namespace canny; - calcMap_gpu(buf.dx, buf.dy, buf.edgeBuf, buf.edgeBuf, dst.rows, dst.cols, low_thresh, high_thresh); + calcMap(dx, dy, buf.mag, buf.map, low_thresh, high_thresh); - edgesHysteresisLocal_gpu(buf.edgeBuf, buf.trackBuf1.ptr(), dst.rows, dst.cols); + edgesHysteresisLocal(buf.map, buf.st1.ptr()); - edgesHysteresisGlobal_gpu(buf.edgeBuf, buf.trackBuf1.ptr(), buf.trackBuf2.ptr(), dst.rows, dst.cols); + edgesHysteresisGlobal(buf.map, buf.st1.ptr(), buf.st2.ptr()); - getEdges_gpu(buf.edgeBuf, dst, dst.rows, dst.cols); + getEdges(buf.map, dst); } } void cv::gpu::Canny(const GpuMat& src, GpuMat& dst, double low_thresh, double high_thresh, int apperture_size, bool L2gradient) { - CannyBuf buf(src.size(), apperture_size); + CannyBuf buf; Canny(src, buf, dst, low_thresh, high_thresh, apperture_size, L2gradient); } void cv::gpu::Canny(const GpuMat& src, CannyBuf& buf, GpuMat& dst, double low_thresh, double high_thresh, int apperture_size, bool L2gradient) { - using namespace ::cv::gpu::device::canny; + using namespace canny; CV_Assert(src.type() == CV_8UC1); @@ -1562,37 +1545,37 @@ void cv::gpu::Canny(const GpuMat& src, CannyBuf& buf, GpuMat& dst, double low_th std::swap( low_thresh, high_thresh); dst.create(src.size(), CV_8U); - dst.setTo(Scalar::all(0)); - buf.create(src.size(), apperture_size); - buf.edgeBuf.setTo(Scalar::all(0)); if (apperture_size == 3) { - calcSobelRowPass_gpu(src, buf.dx_buf, buf.dy_buf, src.rows, src.cols); + Size wholeSize; + Point ofs; + src.locateROI(wholeSize, ofs); + GpuMat srcWhole(wholeSize, src.type(), src.datastart, src.step); - calcMagnitude_gpu(buf.dx_buf, buf.dy_buf, buf.dx, buf.dy, buf.edgeBuf, src.rows, src.cols, L2gradient); + calcMagnitude(srcWhole, ofs.x, ofs.y, buf.dx, buf.dy, buf.mag, L2gradient); } else { buf.filterDX->apply(src, buf.dx, Rect(0, 0, src.cols, src.rows)); buf.filterDY->apply(src, buf.dy, Rect(0, 0, src.cols, src.rows)); - calcMagnitude_gpu(buf.dx, buf.dy, buf.edgeBuf, src.rows, src.cols, L2gradient); + calcMagnitude(buf.dx, buf.dy, buf.mag, L2gradient); } - CannyCaller(buf, dst, static_cast(low_thresh), static_cast(high_thresh)); + CannyCaller(buf.dx, buf.dy, buf, dst, static_cast(low_thresh), static_cast(high_thresh)); } void cv::gpu::Canny(const GpuMat& dx, const GpuMat& dy, GpuMat& dst, double low_thresh, double high_thresh, bool L2gradient) { - CannyBuf buf(dx, dy); + CannyBuf buf; Canny(dx, dy, buf, dst, low_thresh, high_thresh, L2gradient); } void cv::gpu::Canny(const GpuMat& dx, const GpuMat& dy, CannyBuf& buf, GpuMat& dst, double low_thresh, double high_thresh, bool L2gradient) { - using namespace ::cv::gpu::device::canny; + using namespace canny; CV_Assert(TargetArchs::builtWith(SHARED_ATOMICS) && DeviceInfo().supports(SHARED_ATOMICS)); CV_Assert(dx.type() == CV_32SC1 && dy.type() == CV_32SC1 && dx.size() == dy.size()); @@ -1601,17 +1584,11 @@ void cv::gpu::Canny(const GpuMat& dx, const GpuMat& dy, CannyBuf& buf, GpuMat& d std::swap( low_thresh, high_thresh); dst.create(dx.size(), CV_8U); - dst.setTo(Scalar::all(0)); - - buf.dx = dx; buf.dy = dy; buf.create(dx.size(), -1); - buf.edgeBuf.setTo(Scalar::all(0)); - calcMagnitude_gpu(dx, dy, buf.edgeBuf, dx.rows, dx.cols, L2gradient); + calcMagnitude(dx, dy, buf.mag, L2gradient); - CannyCaller(buf, dst, static_cast(low_thresh), static_cast(high_thresh)); + CannyCaller(dx, dy, buf, dst, static_cast(low_thresh), static_cast(high_thresh)); } #endif /* !defined (HAVE_CUDA) */ - - diff --git a/modules/gpu/test/test_imgproc.cpp b/modules/gpu/test/test_imgproc.cpp index e77cad69af..71d4a8e654 100644 --- a/modules/gpu/test/test_imgproc.cpp +++ b/modules/gpu/test/test_imgproc.cpp @@ -313,7 +313,7 @@ TEST_P(Canny, Accuracy) cv::Mat edges_gold; cv::Canny(img, edges_gold, low_thresh, high_thresh, apperture_size, useL2gradient); - EXPECT_MAT_SIMILAR(edges_gold, edges, 1e-2); + EXPECT_MAT_SIMILAR(edges_gold, edges, 2e-2); } } From 7e57648ea22761f9e5cce2d5bba5b7b2ff71b331 Mon Sep 17 00:00:00 2001 From: Vladislav Vinogradov Date: Mon, 12 Nov 2012 13:09:39 +0400 Subject: [PATCH 06/23] FGDStatModel --- modules/gpu/src/cuda/fgd_bgfg.cu | 57 +++----------------------------- 1 file changed, 5 insertions(+), 52 deletions(-) diff --git a/modules/gpu/src/cuda/fgd_bgfg.cu b/modules/gpu/src/cuda/fgd_bgfg.cu index 6040d021b8..6361e1811a 100644 --- a/modules/gpu/src/cuda/fgd_bgfg.cu +++ b/modules/gpu/src/cuda/fgd_bgfg.cu @@ -46,6 +46,8 @@ #include "opencv2/gpu/device/vec_math.hpp" #include "opencv2/gpu/device/limits.hpp" #include "opencv2/gpu/device/utility.hpp" +#include "opencv2/gpu/device/reduce.hpp" +#include "opencv2/gpu/device/functional.hpp" #include "fgd_bgfg_common.hpp" using namespace cv::gpu; @@ -181,57 +183,8 @@ namespace bgfg __shared__ unsigned int data1[MERGE_THREADBLOCK_SIZE]; __shared__ unsigned int data2[MERGE_THREADBLOCK_SIZE]; - data0[threadIdx.x] = sum0; - data1[threadIdx.x] = sum1; - data2[threadIdx.x] = sum2; - __syncthreads(); - - if (threadIdx.x < 128) - { - data0[threadIdx.x] = sum0 += data0[threadIdx.x + 128]; - data1[threadIdx.x] = sum1 += data1[threadIdx.x + 128]; - data2[threadIdx.x] = sum2 += data2[threadIdx.x + 128]; - } - __syncthreads(); - - if (threadIdx.x < 64) - { - data0[threadIdx.x] = sum0 += data0[threadIdx.x + 64]; - data1[threadIdx.x] = sum1 += data1[threadIdx.x + 64]; - data2[threadIdx.x] = sum2 += data2[threadIdx.x + 64]; - } - __syncthreads(); - - if (threadIdx.x < 32) - { - volatile unsigned int* vdata0 = data0; - volatile unsigned int* vdata1 = data1; - volatile unsigned int* vdata2 = data2; - - vdata0[threadIdx.x] = sum0 += vdata0[threadIdx.x + 32]; - vdata1[threadIdx.x] = sum1 += vdata1[threadIdx.x + 32]; - vdata2[threadIdx.x] = sum2 += vdata2[threadIdx.x + 32]; - - vdata0[threadIdx.x] = sum0 += vdata0[threadIdx.x + 16]; - vdata1[threadIdx.x] = sum1 += vdata1[threadIdx.x + 16]; - vdata2[threadIdx.x] = sum2 += vdata2[threadIdx.x + 16]; - - vdata0[threadIdx.x] = sum0 += vdata0[threadIdx.x + 8]; - vdata1[threadIdx.x] = sum1 += vdata1[threadIdx.x + 8]; - vdata2[threadIdx.x] = sum2 += vdata2[threadIdx.x + 8]; - - vdata0[threadIdx.x] = sum0 += vdata0[threadIdx.x + 4]; - vdata1[threadIdx.x] = sum1 += vdata1[threadIdx.x + 4]; - vdata2[threadIdx.x] = sum2 += vdata2[threadIdx.x + 4]; - - vdata0[threadIdx.x] = sum0 += vdata0[threadIdx.x + 2]; - vdata1[threadIdx.x] = sum1 += vdata1[threadIdx.x + 2]; - vdata2[threadIdx.x] = sum2 += vdata2[threadIdx.x + 2]; - - vdata0[threadIdx.x] = sum0 += vdata0[threadIdx.x + 1]; - vdata1[threadIdx.x] = sum1 += vdata1[threadIdx.x + 1]; - vdata2[threadIdx.x] = sum2 += vdata2[threadIdx.x + 1]; - } + plus op; + reduce(smem_tuple(data0, data1, data2), thrust::tie(sum0, sum1, sum2), threadIdx.x, thrust::make_tuple(op, op, op)); if(threadIdx.x == 0) { @@ -845,4 +798,4 @@ namespace bgfg template void updateBackgroundModel_gpu(PtrStepSzb prevFrame, PtrStepSzb curFrame, PtrStepSzb Ftd, PtrStepSzb Fbd, PtrStepSzb foreground, PtrStepSzb background, int deltaC, int deltaCC, float alpha1, float alpha2, float alpha3, int N1c, int N1cc, int N2c, int N2cc, float T, cudaStream_t stream); } -#endif /* CUDA_DISABLER */ \ No newline at end of file +#endif /* CUDA_DISABLER */ From 0ddd16cf78229ceaaeaa5fb5a226a877cfc8feb8 Mon Sep 17 00:00:00 2001 From: Vladislav Vinogradov Date: Mon, 12 Nov 2012 13:16:47 +0400 Subject: [PATCH 07/23] calcHist & equalizeHist --- modules/gpu/include/opencv2/gpu/gpu.hpp | 2 - modules/gpu/perf/perf_imgproc.cpp | 5 +- modules/gpu/src/cuda/hist.cu | 227 +++++++++--------------- modules/gpu/src/imgproc.cpp | 50 +----- 4 files changed, 91 insertions(+), 193 deletions(-) diff --git a/modules/gpu/include/opencv2/gpu/gpu.hpp b/modules/gpu/include/opencv2/gpu/gpu.hpp index 4396a0a102..4aed05caee 100644 --- a/modules/gpu/include/opencv2/gpu/gpu.hpp +++ b/modules/gpu/include/opencv2/gpu/gpu.hpp @@ -1028,11 +1028,9 @@ CV_EXPORTS void histRange(const GpuMat& src, GpuMat hist[4], const GpuMat levels //! Calculates histogram for 8u one channel image //! Output hist will have one row, 256 cols and CV32SC1 type. CV_EXPORTS void calcHist(const GpuMat& src, GpuMat& hist, Stream& stream = Stream::Null()); -CV_EXPORTS void calcHist(const GpuMat& src, GpuMat& hist, GpuMat& buf, Stream& stream = Stream::Null()); //! normalizes the grayscale image brightness and contrast by normalizing its histogram CV_EXPORTS void equalizeHist(const GpuMat& src, GpuMat& dst, Stream& stream = Stream::Null()); -CV_EXPORTS void equalizeHist(const GpuMat& src, GpuMat& dst, GpuMat& hist, Stream& stream = Stream::Null()); CV_EXPORTS void equalizeHist(const GpuMat& src, GpuMat& dst, GpuMat& hist, GpuMat& buf, Stream& stream = Stream::Null()); //////////////////////////////// StereoBM_GPU //////////////////////////////// diff --git a/modules/gpu/perf/perf_imgproc.cpp b/modules/gpu/perf/perf_imgproc.cpp index 30377e148f..a4e199bc72 100644 --- a/modules/gpu/perf/perf_imgproc.cpp +++ b/modules/gpu/perf/perf_imgproc.cpp @@ -581,13 +581,12 @@ PERF_TEST_P(Sz, ImgProc_CalcHist, GPU_TYPICAL_MAT_SIZES) { cv::gpu::GpuMat d_src(src); cv::gpu::GpuMat d_hist; - cv::gpu::GpuMat d_buf; - cv::gpu::calcHist(d_src, d_hist, d_buf); + cv::gpu::calcHist(d_src, d_hist); TEST_CYCLE() { - cv::gpu::calcHist(d_src, d_hist, d_buf); + cv::gpu::calcHist(d_src, d_hist); } GPU_SANITY_CHECK(d_hist); diff --git a/modules/gpu/src/cuda/hist.cu b/modules/gpu/src/cuda/hist.cu index 3a2b59b857..2adc5d5b4d 100644 --- a/modules/gpu/src/cuda/hist.cu +++ b/modules/gpu/src/cuda/hist.cu @@ -43,182 +43,115 @@ #if !defined CUDA_DISABLER -#include "internal_shared.hpp" -#include "opencv2/gpu/device/utility.hpp" -#include "opencv2/gpu/device/saturate_cast.hpp" +#include "opencv2/gpu/device/common.hpp" +#include "opencv2/gpu/device/functional.hpp" +#include "opencv2/gpu/device/emulation.hpp" +#include "opencv2/gpu/device/transform.hpp" -namespace cv { namespace gpu { namespace device -{ - #define UINT_BITS 32U - - //Warps == subhistograms per threadblock - #define WARP_COUNT 6 - - //Threadblock size - #define HISTOGRAM256_THREADBLOCK_SIZE (WARP_COUNT * OPENCV_GPU_WARP_SIZE) - #define HISTOGRAM256_BIN_COUNT 256 - - //Shared memory per threadblock - #define HISTOGRAM256_THREADBLOCK_MEMORY (WARP_COUNT * HISTOGRAM256_BIN_COUNT) - - #define PARTIAL_HISTOGRAM256_COUNT 240 - - #define MERGE_THREADBLOCK_SIZE 256 +using namespace cv::gpu; +using namespace cv::gpu::device; - #define USE_SMEM_ATOMICS (defined (__CUDA_ARCH__) && (__CUDA_ARCH__ >= 120)) - - namespace hist +namespace +{ + __global__ void histogram256(const uchar* src, int cols, int rows, size_t step, int* hist) { - #if (!USE_SMEM_ATOMICS) - - #define TAG_MASK ( (1U << (UINT_BITS - OPENCV_GPU_LOG_WARP_SIZE)) - 1U ) - - __forceinline__ __device__ void addByte(volatile uint* s_WarpHist, uint data, uint threadTag) - { - uint count; - do - { - count = s_WarpHist[data] & TAG_MASK; - count = threadTag | (count + 1); - s_WarpHist[data] = count; - } while (s_WarpHist[data] != count); - } - - #else + __shared__ int shist[256]; - #define TAG_MASK 0xFFFFFFFFU + const int y = blockIdx.x * blockDim.y + threadIdx.y; + const int tid = threadIdx.y * blockDim.x + threadIdx.x; - __forceinline__ __device__ void addByte(uint* s_WarpHist, uint data, uint threadTag) - { - atomicAdd(s_WarpHist + data, 1); - } + shist[tid] = 0; + __syncthreads(); - #endif - - __forceinline__ __device__ void addWord(uint* s_WarpHist, uint data, uint tag, uint pos_x, uint cols) + if (y < rows) { - uint x = pos_x << 2; - - if (x + 0 < cols) addByte(s_WarpHist, (data >> 0) & 0xFFU, tag); - if (x + 1 < cols) addByte(s_WarpHist, (data >> 8) & 0xFFU, tag); - if (x + 2 < cols) addByte(s_WarpHist, (data >> 16) & 0xFFU, tag); - if (x + 3 < cols) addByte(s_WarpHist, (data >> 24) & 0xFFU, tag); - } + const unsigned int* rowPtr = (const unsigned int*) (src + y * step); - __global__ void histogram256(const PtrStep d_Data, uint* d_PartialHistograms, uint dataCount, uint cols) - { - //Per-warp subhistogram storage - __shared__ uint s_Hist[HISTOGRAM256_THREADBLOCK_MEMORY]; - uint* s_WarpHist= s_Hist + (threadIdx.x >> OPENCV_GPU_LOG_WARP_SIZE) * HISTOGRAM256_BIN_COUNT; - - //Clear shared memory storage for current threadblock before processing - #pragma unroll - for (uint i = 0; i < (HISTOGRAM256_THREADBLOCK_MEMORY / HISTOGRAM256_THREADBLOCK_SIZE); i++) - s_Hist[threadIdx.x + i * HISTOGRAM256_THREADBLOCK_SIZE] = 0; - - //Cycle through the entire data set, update subhistograms for each warp - const uint tag = threadIdx.x << (UINT_BITS - OPENCV_GPU_LOG_WARP_SIZE); - - __syncthreads(); - const uint colsui = d_Data.step / sizeof(uint); - for(uint pos = blockIdx.x * blockDim.x + threadIdx.x; pos < dataCount; pos += blockDim.x * gridDim.x) + const int cols_4 = cols / 4; + for (int x = threadIdx.x; x < cols_4; x += blockDim.x) { - uint pos_y = pos / colsui; - uint pos_x = pos % colsui; - uint data = d_Data.ptr(pos_y)[pos_x]; - addWord(s_WarpHist, data, tag, pos_x, cols); + unsigned int data = rowPtr[x]; + + Emulation::smem::atomicAdd(&shist[(data >> 0) & 0xFFU], 1); + Emulation::smem::atomicAdd(&shist[(data >> 8) & 0xFFU], 1); + Emulation::smem::atomicAdd(&shist[(data >> 16) & 0xFFU], 1); + Emulation::smem::atomicAdd(&shist[(data >> 24) & 0xFFU], 1); } - //Merge per-warp histograms into per-block and write to global memory - __syncthreads(); - for(uint bin = threadIdx.x; bin < HISTOGRAM256_BIN_COUNT; bin += HISTOGRAM256_THREADBLOCK_SIZE) + if (cols % 4 != 0 && threadIdx.x == 0) { - uint sum = 0; - - for (uint i = 0; i < WARP_COUNT; i++) - sum += s_Hist[bin + i * HISTOGRAM256_BIN_COUNT] & TAG_MASK; - - d_PartialHistograms[blockIdx.x * HISTOGRAM256_BIN_COUNT + bin] = sum; + for (int x = cols_4 * 4; x < cols; ++x) + { + unsigned int data = ((const uchar*)rowPtr)[x]; + Emulation::smem::atomicAdd(&shist[data], 1); + } } } - //////////////////////////////////////////////////////////////////////////////// - // Merge histogram256() output - // Run one threadblock per bin; each threadblock adds up the same bin counter - // from every partial histogram. Reads are uncoalesced, but mergeHistogram256 - // takes only a fraction of total processing time - //////////////////////////////////////////////////////////////////////////////// - - __global__ void mergeHistogram256(const uint* d_PartialHistograms, int* d_Histogram) - { - uint sum = 0; + __syncthreads(); - #pragma unroll - for (uint i = threadIdx.x; i < PARTIAL_HISTOGRAM256_COUNT; i += MERGE_THREADBLOCK_SIZE) - sum += d_PartialHistograms[blockIdx.x + i * HISTOGRAM256_BIN_COUNT]; + const int histVal = shist[tid]; + if (histVal > 0) + ::atomicAdd(hist + tid, histVal); + } +} - __shared__ uint data[MERGE_THREADBLOCK_SIZE]; - data[threadIdx.x] = sum; - - for (uint stride = MERGE_THREADBLOCK_SIZE / 2; stride > 0; stride >>= 1) - { - __syncthreads(); - if(threadIdx.x < stride) - data[threadIdx.x] += data[threadIdx.x + stride]; - } - - if(threadIdx.x == 0) - d_Histogram[blockIdx.x] = saturate_cast(data[0]); - } +namespace hist +{ + void histogram256(PtrStepSzb src, int* hist, cudaStream_t stream) + { + const dim3 block(32, 8); + const dim3 grid(divUp(src.rows, block.y)); - void histogram256_gpu(PtrStepSzb src, int* hist, uint* buf, cudaStream_t stream) - { - histogram256<<>>( - PtrStepSz(src), - buf, - static_cast(src.rows * src.step / sizeof(uint)), - src.cols); + ::histogram256<<>>(src.data, src.cols, src.rows, src.step, hist); + cudaSafeCall( cudaGetLastError() ); - cudaSafeCall( cudaGetLastError() ); + if (stream == 0) + cudaSafeCall( cudaDeviceSynchronize() ); + } +} - mergeHistogram256<<>>(buf, hist); +///////////////////////////////////////////////////////////////////////// - cudaSafeCall( cudaGetLastError() ); +namespace +{ + __constant__ int c_lut[256]; - if (stream == 0) - cudaSafeCall( cudaDeviceSynchronize() ); - } + struct EqualizeHist : unary_function + { + float scale; - __constant__ int c_lut[256]; + __host__ EqualizeHist(float _scale) : scale(_scale) {} - __global__ void equalizeHist(const PtrStepSzb src, PtrStepb dst) + __device__ __forceinline__ uchar operator ()(uchar val) const { - const int x = blockIdx.x * blockDim.x + threadIdx.x; - const int y = blockIdx.y * blockDim.y + threadIdx.y; - - if (x < src.cols && y < src.rows) - { - const uchar val = src.ptr(y)[x]; - const int lut = c_lut[val]; - dst.ptr(y)[x] = __float2int_rn(255.0f / (src.cols * src.rows) * lut); - } + const int lut = c_lut[val]; + return __float2int_rn(scale * lut); } + }; +} - void equalizeHist_gpu(PtrStepSzb src, PtrStepSzb dst, const int* lut, cudaStream_t stream) - { - dim3 block(16, 16); - dim3 grid(divUp(src.cols, block.x), divUp(src.rows, block.y)); +namespace cv { namespace gpu { namespace device +{ + template <> struct TransformFunctorTraits : DefaultTransformFunctorTraits + { + enum { smart_shift = 4 }; + }; +}}} +namespace hist +{ + void equalizeHist(PtrStepSzb src, PtrStepSzb dst, const int* lut, cudaStream_t stream) + { + if (stream == 0) cudaSafeCall( cudaMemcpyToSymbol(c_lut, lut, 256 * sizeof(int), 0, cudaMemcpyDeviceToDevice) ); + else + cudaSafeCall( cudaMemcpyToSymbolAsync(c_lut, lut, 256 * sizeof(int), 0, cudaMemcpyDeviceToDevice, stream) ); - equalizeHist<<>>(src, dst); - cudaSafeCall( cudaGetLastError() ); - - if (stream == 0) - cudaSafeCall( cudaDeviceSynchronize() ); - } - } // namespace hist -}}} // namespace cv { namespace gpu { namespace device + const float scale = 255.0f / (src.cols * src.rows); + transform(src, dst, EqualizeHist(scale), WithOutMask(), stream); + } +} -#endif /* CUDA_DISABLER */ \ No newline at end of file +#endif /* CUDA_DISABLER */ diff --git a/modules/gpu/src/imgproc.cpp b/modules/gpu/src/imgproc.cpp index b733faf5d0..b216de8741 100644 --- a/modules/gpu/src/imgproc.cpp +++ b/modules/gpu/src/imgproc.cpp @@ -71,9 +71,7 @@ void cv::gpu::histRange(const GpuMat&, GpuMat&, const GpuMat&, GpuMat&, Stream&) void cv::gpu::histRange(const GpuMat&, GpuMat*, const GpuMat*, Stream&) { throw_nogpu(); } void cv::gpu::histRange(const GpuMat&, GpuMat*, const GpuMat*, GpuMat&, Stream&) { throw_nogpu(); } void cv::gpu::calcHist(const GpuMat&, GpuMat&, Stream&) { throw_nogpu(); } -void cv::gpu::calcHist(const GpuMat&, GpuMat&, GpuMat&, Stream&) { throw_nogpu(); } void cv::gpu::equalizeHist(const GpuMat&, GpuMat&, Stream&) { throw_nogpu(); } -void cv::gpu::equalizeHist(const GpuMat&, GpuMat&, GpuMat&, Stream&) { throw_nogpu(); } void cv::gpu::equalizeHist(const GpuMat&, GpuMat&, GpuMat&, GpuMat&, Stream&) { throw_nogpu(); } void cv::gpu::cornerHarris(const GpuMat&, GpuMat&, int, int, double, int) { throw_nogpu(); } void cv::gpu::cornerHarris(const GpuMat&, GpuMat&, GpuMat&, GpuMat&, int, int, double, int) { throw_nogpu(); } @@ -989,36 +987,20 @@ void cv::gpu::histRange(const GpuMat& src, GpuMat hist[4], const GpuMat levels[4 hist_callers[src.depth()](src, hist, levels, buf, StreamAccessor::getStream(stream)); } -namespace cv { namespace gpu { namespace device -{ - namespace hist - { - void histogram256_gpu(PtrStepSzb src, int* hist, unsigned int* buf, cudaStream_t stream); - - const int PARTIAL_HISTOGRAM256_COUNT = 240; - const int HISTOGRAM256_BIN_COUNT = 256; - - void equalizeHist_gpu(PtrStepSzb src, PtrStepSzb dst, const int* lut, cudaStream_t stream); - } -}}} - -void cv::gpu::calcHist(const GpuMat& src, GpuMat& hist, Stream& stream) +namespace hist { - GpuMat buf; - calcHist(src, hist, buf, stream); + void histogram256(PtrStepSzb src, int* hist, cudaStream_t stream); + void equalizeHist(PtrStepSzb src, PtrStepSzb dst, const int* lut, cudaStream_t stream); } -void cv::gpu::calcHist(const GpuMat& src, GpuMat& hist, GpuMat& buf, Stream& stream) +void cv::gpu::calcHist(const GpuMat& src, GpuMat& hist, Stream& stream) { - using namespace ::cv::gpu::device::hist; - CV_Assert(src.type() == CV_8UC1); hist.create(1, 256, CV_32SC1); + hist.setTo(Scalar::all(0)); - ensureSizeIsEnough(1, PARTIAL_HISTOGRAM256_COUNT * HISTOGRAM256_BIN_COUNT, CV_32SC1, buf); - - histogram256_gpu(src, hist.ptr(), buf.ptr(), StreamAccessor::getStream(stream)); + hist::histogram256(src, hist.ptr(), StreamAccessor::getStream(stream)); } void cv::gpu::equalizeHist(const GpuMat& src, GpuMat& dst, Stream& stream) @@ -1028,16 +1010,8 @@ void cv::gpu::equalizeHist(const GpuMat& src, GpuMat& dst, Stream& stream) equalizeHist(src, dst, hist, buf, stream); } -void cv::gpu::equalizeHist(const GpuMat& src, GpuMat& dst, GpuMat& hist, Stream& stream) -{ - GpuMat buf; - equalizeHist(src, dst, hist, buf, stream); -} - void cv::gpu::equalizeHist(const GpuMat& src, GpuMat& dst, GpuMat& hist, GpuMat& buf, Stream& s) { - using namespace ::cv::gpu::device::hist; - CV_Assert(src.type() == CV_8UC1); dst.create(src.size(), src.type()); @@ -1045,15 +1019,12 @@ void cv::gpu::equalizeHist(const GpuMat& src, GpuMat& dst, GpuMat& hist, GpuMat& int intBufSize; nppSafeCall( nppsIntegralGetBufferSize_32s(256, &intBufSize) ); - int bufSize = static_cast(std::max(256 * 240 * sizeof(int), intBufSize + 256 * sizeof(int))); - - ensureSizeIsEnough(1, bufSize, CV_8UC1, buf); + ensureSizeIsEnough(1, intBufSize + 256 * sizeof(int), CV_8UC1, buf); - GpuMat histBuf(1, 256 * 240, CV_32SC1, buf.ptr()); GpuMat intBuf(1, intBufSize, CV_8UC1, buf.ptr()); GpuMat lut(1, 256, CV_32S, buf.ptr() + intBufSize); - calcHist(src, hist, histBuf, s); + calcHist(src, hist, s); cudaStream_t stream = StreamAccessor::getStream(s); @@ -1061,10 +1032,7 @@ void cv::gpu::equalizeHist(const GpuMat& src, GpuMat& dst, GpuMat& hist, GpuMat& nppSafeCall( nppsIntegral_32s(hist.ptr(), lut.ptr(), 256, intBuf.ptr()) ); - if (stream == 0) - cudaSafeCall( cudaDeviceSynchronize() ); - - equalizeHist_gpu(src, dst, lut.ptr(), stream); + hist::equalizeHist(src, dst, lut.ptr(), stream); } //////////////////////////////////////////////////////////////////////// From 0e339dd13741a52e29cf65ee4e155e114218af4b Mon Sep 17 00:00:00 2001 From: Vladislav Vinogradov Date: Mon, 12 Nov 2012 13:19:48 +0400 Subject: [PATCH 08/23] hog --- modules/gpu/src/cuda/hog.cu | 197 +++++++++++++----------------------- 1 file changed, 69 insertions(+), 128 deletions(-) diff --git a/modules/gpu/src/cuda/hog.cu b/modules/gpu/src/cuda/hog.cu index 953fdec1dc..6a7e927d18 100644 --- a/modules/gpu/src/cuda/hog.cu +++ b/modules/gpu/src/cuda/hog.cu @@ -42,7 +42,10 @@ #if !defined CUDA_DISABLER -#include "internal_shared.hpp" +#include "opencv2/gpu/device/common.hpp" +#include "opencv2/gpu/device/reduce.hpp" +#include "opencv2/gpu/device/functional.hpp" +#include "opencv2/gpu/device/warp_shuffle.hpp" namespace cv { namespace gpu { namespace device { @@ -226,29 +229,30 @@ namespace cv { namespace gpu { namespace device template - __device__ float reduce_smem(volatile float* smem) + __device__ float reduce_smem(float* smem, float val) { unsigned int tid = threadIdx.x; - float sum = smem[tid]; + float sum = val; - if (size >= 512) { if (tid < 256) smem[tid] = sum = sum + smem[tid + 256]; __syncthreads(); } - if (size >= 256) { if (tid < 128) smem[tid] = sum = sum + smem[tid + 128]; __syncthreads(); } - if (size >= 128) { if (tid < 64) smem[tid] = sum = sum + smem[tid + 64]; __syncthreads(); } + reduce(smem, sum, tid, plus()); - if (tid < 32) + if (size == 32) { - if (size >= 64) smem[tid] = sum = sum + smem[tid + 32]; - if (size >= 32) smem[tid] = sum = sum + smem[tid + 16]; - if (size >= 16) smem[tid] = sum = sum + smem[tid + 8]; - if (size >= 8) smem[tid] = sum = sum + smem[tid + 4]; - if (size >= 4) smem[tid] = sum = sum + smem[tid + 2]; - if (size >= 2) smem[tid] = sum = sum + smem[tid + 1]; + #if __CUDA_ARCH__ >= 300 + return shfl(sum, 0); + #else + return smem[0]; + #endif } + #if __CUDA_ARCH__ >= 300 + if (threadIdx.x == 0) + smem[0] = sum; + #endif + __syncthreads(); - sum = smem[0]; - return sum; + return smem[0]; } @@ -272,19 +276,13 @@ namespace cv { namespace gpu { namespace device if (threadIdx.x < block_hist_size) elem = hist[0]; - squares[threadIdx.x] = elem * elem; - - __syncthreads(); - float sum = reduce_smem(squares); + float sum = reduce_smem(squares, elem * elem); float scale = 1.0f / (::sqrtf(sum) + 0.1f * block_hist_size); elem = ::min(elem * scale, threshold); - __syncthreads(); - squares[threadIdx.x] = elem * elem; + sum = reduce_smem(squares, elem * elem); - __syncthreads(); - sum = reduce_smem(squares); scale = 1.0f / (::sqrtf(sum) + 1e-3f); if (threadIdx.x < block_hist_size) @@ -330,65 +328,36 @@ namespace cv { namespace gpu { namespace device // return confidence values not just positive location template // Number of histogram block processed by single GPU thread block + int nblocks> // Number of histogram block processed by single GPU thread block __global__ void compute_confidence_hists_kernel_many_blocks(const int img_win_width, const int img_block_width, const int win_block_stride_x, const int win_block_stride_y, const float* block_hists, const float* coefs, float free_coef, float threshold, float* confidences) { - const int win_x = threadIdx.z; - if (blockIdx.x * blockDim.z + win_x >= img_win_width) - return; - - const float* hist = block_hists + (blockIdx.y * win_block_stride_y * img_block_width + - blockIdx.x * win_block_stride_x * blockDim.z + win_x) * - cblock_hist_size; - - float product = 0.f; - for (int i = threadIdx.x; i < cdescr_size; i += nthreads) - { - int offset_y = i / cdescr_width; - int offset_x = i - offset_y * cdescr_width; - product += coefs[i] * hist[offset_y * img_block_width * cblock_hist_size + offset_x]; - } - - __shared__ float products[nthreads * nblocks]; - - const int tid = threadIdx.z * nthreads + threadIdx.x; - products[tid] = product; - - __syncthreads(); - - if (nthreads >= 512) - { - if (threadIdx.x < 256) products[tid] = product = product + products[tid + 256]; - __syncthreads(); - } - if (nthreads >= 256) - { - if (threadIdx.x < 128) products[tid] = product = product + products[tid + 128]; - __syncthreads(); - } - if (nthreads >= 128) - { - if (threadIdx.x < 64) products[tid] = product = product + products[tid + 64]; - __syncthreads(); - } - - if (threadIdx.x < 32) - { - volatile float* smem = products; - if (nthreads >= 64) smem[tid] = product = product + smem[tid + 32]; - if (nthreads >= 32) smem[tid] = product = product + smem[tid + 16]; - if (nthreads >= 16) smem[tid] = product = product + smem[tid + 8]; - if (nthreads >= 8) smem[tid] = product = product + smem[tid + 4]; - if (nthreads >= 4) smem[tid] = product = product + smem[tid + 2]; - if (nthreads >= 2) smem[tid] = product = product + smem[tid + 1]; - } - - if (threadIdx.x == 0) - confidences[blockIdx.y * img_win_width + blockIdx.x * blockDim.z + win_x] - = (float)(product + free_coef); + const int win_x = threadIdx.z; + if (blockIdx.x * blockDim.z + win_x >= img_win_width) + return; + + const float* hist = block_hists + (blockIdx.y * win_block_stride_y * img_block_width + + blockIdx.x * win_block_stride_x * blockDim.z + win_x) * + cblock_hist_size; + + float product = 0.f; + for (int i = threadIdx.x; i < cdescr_size; i += nthreads) + { + int offset_y = i / cdescr_width; + int offset_x = i - offset_y * cdescr_width; + product += coefs[i] * hist[offset_y * img_block_width * cblock_hist_size + offset_x]; + } + + __shared__ float products[nthreads * nblocks]; + + const int tid = threadIdx.z * nthreads + threadIdx.x; + + reduce(products, product, tid, plus()); + + if (threadIdx.x == 0) + confidences[blockIdx.y * img_win_width + blockIdx.x * blockDim.z + win_x] = product + free_coef; } @@ -396,32 +365,32 @@ namespace cv { namespace gpu { namespace device int win_stride_y, int win_stride_x, int height, int width, float* block_hists, float* coefs, float free_coef, float threshold, float *confidences) { - const int nthreads = 256; - const int nblocks = 1; - - int win_block_stride_x = win_stride_x / block_stride_x; - int win_block_stride_y = win_stride_y / block_stride_y; - int img_win_width = (width - win_width + win_stride_x) / win_stride_x; - int img_win_height = (height - win_height + win_stride_y) / win_stride_y; - - dim3 threads(nthreads, 1, nblocks); - dim3 grid(divUp(img_win_width, nblocks), img_win_height); - - cudaSafeCall(cudaFuncSetCacheConfig(compute_confidence_hists_kernel_many_blocks, - cudaFuncCachePreferL1)); - - int img_block_width = (width - CELLS_PER_BLOCK_X * CELL_WIDTH + block_stride_x) / - block_stride_x; - compute_confidence_hists_kernel_many_blocks<<>>( - img_win_width, img_block_width, win_block_stride_x, win_block_stride_y, - block_hists, coefs, free_coef, threshold, confidences); - cudaSafeCall(cudaThreadSynchronize()); + const int nthreads = 256; + const int nblocks = 1; + + int win_block_stride_x = win_stride_x / block_stride_x; + int win_block_stride_y = win_stride_y / block_stride_y; + int img_win_width = (width - win_width + win_stride_x) / win_stride_x; + int img_win_height = (height - win_height + win_stride_y) / win_stride_y; + + dim3 threads(nthreads, 1, nblocks); + dim3 grid(divUp(img_win_width, nblocks), img_win_height); + + cudaSafeCall(cudaFuncSetCacheConfig(compute_confidence_hists_kernel_many_blocks, + cudaFuncCachePreferL1)); + + int img_block_width = (width - CELLS_PER_BLOCK_X * CELL_WIDTH + block_stride_x) / + block_stride_x; + compute_confidence_hists_kernel_many_blocks<<>>( + img_win_width, img_block_width, win_block_stride_x, win_block_stride_y, + block_hists, coefs, free_coef, threshold, confidences); + cudaSafeCall(cudaThreadSynchronize()); } template // Number of histogram block processed by single GPU thread block + int nblocks> // Number of histogram block processed by single GPU thread block __global__ void classify_hists_kernel_many_blocks(const int img_win_width, const int img_block_width, const int win_block_stride_x, const int win_block_stride_y, const float* block_hists, const float* coefs, @@ -446,36 +415,8 @@ namespace cv { namespace gpu { namespace device __shared__ float products[nthreads * nblocks]; const int tid = threadIdx.z * nthreads + threadIdx.x; - products[tid] = product; - __syncthreads(); - - if (nthreads >= 512) - { - if (threadIdx.x < 256) products[tid] = product = product + products[tid + 256]; - __syncthreads(); - } - if (nthreads >= 256) - { - if (threadIdx.x < 128) products[tid] = product = product + products[tid + 128]; - __syncthreads(); - } - if (nthreads >= 128) - { - if (threadIdx.x < 64) products[tid] = product = product + products[tid + 64]; - __syncthreads(); - } - - if (threadIdx.x < 32) - { - volatile float* smem = products; - if (nthreads >= 64) smem[tid] = product = product + smem[tid + 32]; - if (nthreads >= 32) smem[tid] = product = product + smem[tid + 16]; - if (nthreads >= 16) smem[tid] = product = product + smem[tid + 8]; - if (nthreads >= 8) smem[tid] = product = product + smem[tid + 4]; - if (nthreads >= 4) smem[tid] = product = product + smem[tid + 2]; - if (nthreads >= 2) smem[tid] = product = product + smem[tid + 1]; - } + reduce(products, product, tid, plus()); if (threadIdx.x == 0) labels[blockIdx.y * img_win_width + blockIdx.x * blockDim.z + win_x] = (product + free_coef >= threshold); @@ -868,4 +809,4 @@ namespace cv { namespace gpu { namespace device }}} // namespace cv { namespace gpu { namespace device -#endif /* CUDA_DISABLER */ \ No newline at end of file +#endif /* CUDA_DISABLER */ From 1b571bde10183b8270e8b46ab0d296ae55a6d39e Mon Sep 17 00:00:00 2001 From: Vladislav Vinogradov Date: Mon, 12 Nov 2012 14:08:46 +0400 Subject: [PATCH 09/23] StereoConstantSpaceBP --- modules/gpu/src/cuda/stereocsbp.cu | 44 ++++++------------------------ 1 file changed, 8 insertions(+), 36 deletions(-) diff --git a/modules/gpu/src/cuda/stereocsbp.cu b/modules/gpu/src/cuda/stereocsbp.cu index 1c95ed9e10..7b76f478b4 100644 --- a/modules/gpu/src/cuda/stereocsbp.cu +++ b/modules/gpu/src/cuda/stereocsbp.cu @@ -42,9 +42,11 @@ #if !defined CUDA_DISABLER -#include "internal_shared.hpp" +#include "opencv2/gpu/device/common.hpp" #include "opencv2/gpu/device/saturate_cast.hpp" #include "opencv2/gpu/device/limits.hpp" +#include "opencv2/gpu/device/reduce.hpp" +#include "opencv2/gpu/device/functional.hpp" namespace cv { namespace gpu { namespace device { @@ -297,28 +299,13 @@ namespace cv { namespace gpu { namespace device } extern __shared__ float smem[]; - float* dline = smem + winsz * threadIdx.z; - dline[tid] = val; - - __syncthreads(); - - if (winsz >= 256) { if (tid < 128) { dline[tid] += dline[tid + 128]; } __syncthreads(); } - if (winsz >= 128) { if (tid < 64) { dline[tid] += dline[tid + 64]; } __syncthreads(); } - - volatile float* vdline = smem + winsz * threadIdx.z; - - if (winsz >= 64) if (tid < 32) vdline[tid] += vdline[tid + 32]; - if (winsz >= 32) if (tid < 16) vdline[tid] += vdline[tid + 16]; - if (winsz >= 16) if (tid < 8) vdline[tid] += vdline[tid + 8]; - if (winsz >= 8) if (tid < 4) vdline[tid] += vdline[tid + 4]; - if (winsz >= 4) if (tid < 2) vdline[tid] += vdline[tid + 2]; - if (winsz >= 2) if (tid < 1) vdline[tid] += vdline[tid + 1]; + reduce(smem + winsz * threadIdx.z, val, tid, plus()); T* data_cost = (T*)ctemp + y_out * cmsg_step + x_out; if (tid == 0) - data_cost[cdisp_step1 * d] = saturate_cast(dline[0]); + data_cost[cdisp_step1 * d] = saturate_cast(val); } } @@ -496,26 +483,11 @@ namespace cv { namespace gpu { namespace device } extern __shared__ float smem[]; - float* dline = smem + winsz * threadIdx.z; - dline[tid] = val; - - __syncthreads(); - - if (winsz >= 256) { if (tid < 128) { dline[tid] += dline[tid + 128]; } __syncthreads(); } - if (winsz >= 128) { if (tid < 64) { dline[tid] += dline[tid + 64]; } __syncthreads(); } - - volatile float* vdline = smem + winsz * threadIdx.z; - - if (winsz >= 64) if (tid < 32) vdline[tid] += vdline[tid + 32]; - if (winsz >= 32) if (tid < 16) vdline[tid] += vdline[tid + 16]; - if (winsz >= 16) if (tid < 8) vdline[tid] += vdline[tid + 8]; - if (winsz >= 8) if (tid < 4) vdline[tid] += vdline[tid + 4]; - if (winsz >= 4) if (tid < 2) vdline[tid] += vdline[tid + 2]; - if (winsz >= 2) if (tid < 1) vdline[tid] += vdline[tid + 1]; + reduce(smem + winsz * threadIdx.z, val, tid, plus()); if (tid == 0) - data_cost[cdisp_step1 * d] = saturate_cast(dline[0]); + data_cost[cdisp_step1 * d] = saturate_cast(val); } } @@ -889,4 +861,4 @@ namespace cv { namespace gpu { namespace device } // namespace stereocsbp }}} // namespace cv { namespace gpu { namespace device { -#endif /* CUDA_DISABLER */ \ No newline at end of file +#endif /* CUDA_DISABLER */ From 1f1e24be3c475054f168bf2f776bd597e79d4ca4 Mon Sep 17 00:00:00 2001 From: Vladislav Vinogradov Date: Mon, 12 Nov 2012 14:12:27 +0400 Subject: [PATCH 10/23] PyrLKOpticalFlow --- modules/gpu/src/cuda/pyrlk.cu | 846 +++++++++++++++------------------- modules/gpu/src/pyrlk.cpp | 35 +- 2 files changed, 379 insertions(+), 502 deletions(-) diff --git a/modules/gpu/src/cuda/pyrlk.cu b/modules/gpu/src/cuda/pyrlk.cu index 811c3b90b5..c0f54bd33c 100644 --- a/modules/gpu/src/cuda/pyrlk.cu +++ b/modules/gpu/src/cuda/pyrlk.cu @@ -52,244 +52,187 @@ #include "opencv2/gpu/device/functional.hpp" #include "opencv2/gpu/device/limits.hpp" #include "opencv2/gpu/device/vec_math.hpp" +#include "opencv2/gpu/device/reduce.hpp" -namespace cv { namespace gpu { namespace device +using namespace cv::gpu; +using namespace cv::gpu::device; + +namespace { - namespace pyrlk - { - __constant__ int c_winSize_x; - __constant__ int c_winSize_y; + __constant__ int c_winSize_x; + __constant__ int c_winSize_y; + __constant__ int c_halfWin_x; + __constant__ int c_halfWin_y; + __constant__ int c_iters; - __constant__ int c_halfWin_x; - __constant__ int c_halfWin_y; + texture tex_If(false, cudaFilterModeLinear, cudaAddressModeClamp); + texture tex_If4(false, cudaFilterModeLinear, cudaAddressModeClamp); + texture tex_Ib(false, cudaFilterModePoint, cudaAddressModeClamp); - __constant__ int c_iters; + texture tex_Jf(false, cudaFilterModeLinear, cudaAddressModeClamp); + texture tex_Jf4(false, cudaFilterModeLinear, cudaAddressModeClamp); - void loadConstants(int2 winSize, int iters) + template struct Tex_I; + template <> struct Tex_I<1> + { + static __device__ __forceinline__ float read(float x, float y) { - cudaSafeCall( cudaMemcpyToSymbol(c_winSize_x, &winSize.x, sizeof(int)) ); - cudaSafeCall( cudaMemcpyToSymbol(c_winSize_y, &winSize.y, sizeof(int)) ); - - int2 halfWin = make_int2((winSize.x - 1) / 2, (winSize.y - 1) / 2); - cudaSafeCall( cudaMemcpyToSymbol(c_halfWin_x, &halfWin.x, sizeof(int)) ); - cudaSafeCall( cudaMemcpyToSymbol(c_halfWin_y, &halfWin.y, sizeof(int)) ); - - cudaSafeCall( cudaMemcpyToSymbol(c_iters, &iters, sizeof(int)) ); + return tex2D(tex_If, x, y); } - - __device__ void reduce(float& val1, float& val2, float& val3, float* smem1, float* smem2, float* smem3, int tid) + }; + template <> struct Tex_I<4> + { + static __device__ __forceinline__ float4 read(float x, float y) { - smem1[tid] = val1; - smem2[tid] = val2; - smem3[tid] = val3; - __syncthreads(); + return tex2D(tex_If4, x, y); + } + }; -#if defined(__CUDA_ARCH__) && (__CUDA_ARCH__ > 110) - if (tid < 128) - { - smem1[tid] = val1 += smem1[tid + 128]; - smem2[tid] = val2 += smem2[tid + 128]; - smem3[tid] = val3 += smem3[tid + 128]; - } - __syncthreads(); -#endif + template struct Tex_J; + template <> struct Tex_J<1> + { + static __device__ __forceinline__ float read(float x, float y) + { + return tex2D(tex_Jf, x, y); + } + }; + template <> struct Tex_J<4> + { + static __device__ __forceinline__ float4 read(float x, float y) + { + return tex2D(tex_Jf4, x, y); + } + }; - if (tid < 64) - { - smem1[tid] = val1 += smem1[tid + 64]; - smem2[tid] = val2 += smem2[tid + 64]; - smem3[tid] = val3 += smem3[tid + 64]; - } - __syncthreads(); + __device__ __forceinline__ void accum(float& dst, float val) + { + dst += val; + } + __device__ __forceinline__ void accum(float& dst, const float4& val) + { + dst += val.x + val.y + val.z; + } - if (tid < 32) - { - volatile float* vmem1 = smem1; - volatile float* vmem2 = smem2; - volatile float* vmem3 = smem3; + __device__ __forceinline__ float abs_(float a) + { + return ::fabsf(a); + } + __device__ __forceinline__ float4 abs_(const float4& a) + { + return abs(a); + } - vmem1[tid] = val1 += vmem1[tid + 32]; - vmem2[tid] = val2 += vmem2[tid + 32]; - vmem3[tid] = val3 += vmem3[tid + 32]; + template + __global__ void sparse(const float2* prevPts, float2* nextPts, uchar* status, float* err, const int level, const int rows, const int cols) + { + #if __CUDA_ARCH__ <= 110 + const int BLOCK_SIZE = 128; + #else + const int BLOCK_SIZE = 256; + #endif - vmem1[tid] = val1 += vmem1[tid + 16]; - vmem2[tid] = val2 += vmem2[tid + 16]; - vmem3[tid] = val3 += vmem3[tid + 16]; + __shared__ float smem1[BLOCK_SIZE]; + __shared__ float smem2[BLOCK_SIZE]; + __shared__ float smem3[BLOCK_SIZE]; - vmem1[tid] = val1 += vmem1[tid + 8]; - vmem2[tid] = val2 += vmem2[tid + 8]; - vmem3[tid] = val3 += vmem3[tid + 8]; + const unsigned int tid = threadIdx.y * blockDim.x + threadIdx.x; - vmem1[tid] = val1 += vmem1[tid + 4]; - vmem2[tid] = val2 += vmem2[tid + 4]; - vmem3[tid] = val3 += vmem3[tid + 4]; + float2 prevPt = prevPts[blockIdx.x]; + prevPt.x *= (1.0f / (1 << level)); + prevPt.y *= (1.0f / (1 << level)); - vmem1[tid] = val1 += vmem1[tid + 2]; - vmem2[tid] = val2 += vmem2[tid + 2]; - vmem3[tid] = val3 += vmem3[tid + 2]; + if (prevPt.x < 0 || prevPt.x >= cols || prevPt.y < 0 || prevPt.y >= rows) + { + if (tid == 0 && level == 0) + status[blockIdx.x] = 0; - vmem1[tid] = val1 += vmem1[tid + 1]; - vmem2[tid] = val2 += vmem2[tid + 1]; - vmem3[tid] = val3 += vmem3[tid + 1]; - } + return; } - __device__ void reduce(float& val1, float& val2, float* smem1, float* smem2, int tid) - { - smem1[tid] = val1; - smem2[tid] = val2; - __syncthreads(); + prevPt.x -= c_halfWin_x; + prevPt.y -= c_halfWin_y; -#if defined(__CUDA_ARCH__) && (__CUDA_ARCH__ > 110) - if (tid < 128) - { - smem1[tid] = val1 += smem1[tid + 128]; - smem2[tid] = val2 += smem2[tid + 128]; - } - __syncthreads(); -#endif + // extract the patch from the first image, compute covariation matrix of derivatives - if (tid < 64) - { - smem1[tid] = val1 += smem1[tid + 64]; - smem2[tid] = val2 += smem2[tid + 64]; - } - __syncthreads(); + float A11 = 0; + float A12 = 0; + float A22 = 0; + + typedef typename TypeVec::vec_type work_type; - if (tid < 32) + work_type I_patch [PATCH_Y][PATCH_X]; + work_type dIdx_patch[PATCH_Y][PATCH_X]; + work_type dIdy_patch[PATCH_Y][PATCH_X]; + + for (int yBase = threadIdx.y, i = 0; yBase < c_winSize_y; yBase += blockDim.y, ++i) + { + for (int xBase = threadIdx.x, j = 0; xBase < c_winSize_x; xBase += blockDim.x, ++j) { - volatile float* vmem1 = smem1; - volatile float* vmem2 = smem2; + float x = prevPt.x + xBase + 0.5f; + float y = prevPt.y + yBase + 0.5f; - vmem1[tid] = val1 += vmem1[tid + 32]; - vmem2[tid] = val2 += vmem2[tid + 32]; + I_patch[i][j] = Tex_I::read(x, y); - vmem1[tid] = val1 += vmem1[tid + 16]; - vmem2[tid] = val2 += vmem2[tid + 16]; + // Sharr Deriv - vmem1[tid] = val1 += vmem1[tid + 8]; - vmem2[tid] = val2 += vmem2[tid + 8]; + work_type dIdx = 3.0f * Tex_I::read(x+1, y-1) + 10.0f * Tex_I::read(x+1, y) + 3.0f * Tex_I::read(x+1, y+1) - + (3.0f * Tex_I::read(x-1, y-1) + 10.0f * Tex_I::read(x-1, y) + 3.0f * Tex_I::read(x-1, y+1)); - vmem1[tid] = val1 += vmem1[tid + 4]; - vmem2[tid] = val2 += vmem2[tid + 4]; + work_type dIdy = 3.0f * Tex_I::read(x-1, y+1) + 10.0f * Tex_I::read(x, y+1) + 3.0f * Tex_I::read(x+1, y+1) - + (3.0f * Tex_I::read(x-1, y-1) + 10.0f * Tex_I::read(x, y-1) + 3.0f * Tex_I::read(x+1, y-1)); - vmem1[tid] = val1 += vmem1[tid + 2]; - vmem2[tid] = val2 += vmem2[tid + 2]; + dIdx_patch[i][j] = dIdx; + dIdy_patch[i][j] = dIdy; - vmem1[tid] = val1 += vmem1[tid + 1]; - vmem2[tid] = val2 += vmem2[tid + 1]; + accum(A11, dIdx * dIdx); + accum(A12, dIdx * dIdy); + accum(A22, dIdy * dIdy); } } - __device__ void reduce(float& val1, float* smem1, int tid) - { - smem1[tid] = val1; - __syncthreads(); - -#if defined(__CUDA_ARCH__) && (__CUDA_ARCH__ > 110) - if (tid < 128) - { - smem1[tid] = val1 += smem1[tid + 128]; - } - __syncthreads(); -#endif + reduce(smem_tuple(smem1, smem2, smem3), thrust::tie(A11, A12, A22), tid, thrust::make_tuple(plus(), plus(), plus())); - if (tid < 64) - { - smem1[tid] = val1 += smem1[tid + 64]; - } - __syncthreads(); - - if (tid < 32) - { - volatile float* vmem1 = smem1; - - vmem1[tid] = val1 += vmem1[tid + 32]; - vmem1[tid] = val1 += vmem1[tid + 16]; - vmem1[tid] = val1 += vmem1[tid + 8]; - vmem1[tid] = val1 += vmem1[tid + 4]; - vmem1[tid] = val1 += vmem1[tid + 2]; - vmem1[tid] = val1 += vmem1[tid + 1]; - } + #if __CUDA_ARCH__ >= 300 + if (tid == 0) + { + smem1[0] = A11; + smem2[0] = A12; + smem3[0] = A22; } + #endif - texture tex_If(false, cudaFilterModeLinear, cudaAddressModeClamp); - texture tex_If4(false, cudaFilterModeLinear, cudaAddressModeClamp); - texture tex_Ib(false, cudaFilterModePoint, cudaAddressModeClamp); + __syncthreads(); - texture tex_Jf(false, cudaFilterModeLinear, cudaAddressModeClamp); - texture tex_Jf4(false, cudaFilterModeLinear, cudaAddressModeClamp); + A11 = smem1[0]; + A12 = smem2[0]; + A22 = smem3[0]; - template struct Tex_I; - template <> struct Tex_I<1> - { - static __device__ __forceinline__ float read(float x, float y) - { - return tex2D(tex_If, x, y); - } - }; - template <> struct Tex_I<4> - { - static __device__ __forceinline__ float4 read(float x, float y) - { - return tex2D(tex_If4, x, y); - } - }; + float D = A11 * A22 - A12 * A12; - template struct Tex_J; - template <> struct Tex_J<1> - { - static __device__ __forceinline__ float read(float x, float y) - { - return tex2D(tex_Jf, x, y); - } - }; - template <> struct Tex_J<4> + if (D < numeric_limits::epsilon()) { - static __device__ __forceinline__ float4 read(float x, float y) - { - return tex2D(tex_Jf4, x, y); - } - }; + if (tid == 0 && level == 0) + status[blockIdx.x] = 0; - __device__ __forceinline__ void accum(float& dst, float val) - { - dst += val; - } - __device__ __forceinline__ void accum(float& dst, const float4& val) - { - dst += val.x + val.y + val.z; + return; } - __device__ __forceinline__ float abs_(float a) - { - return ::fabs(a); - } - __device__ __forceinline__ float4 abs_(const float4& a) - { - return abs(a); - } + D = 1.f / D; + + A11 *= D; + A12 *= D; + A22 *= D; - template - __global__ void lkSparse(const float2* prevPts, float2* nextPts, uchar* status, float* err, const int level, const int rows, const int cols) + float2 nextPt = nextPts[blockIdx.x]; + nextPt.x *= 2.f; + nextPt.y *= 2.f; + + nextPt.x -= c_halfWin_x; + nextPt.y -= c_halfWin_y; + + for (int k = 0; k < c_iters; ++k) { -#if defined(__CUDA_ARCH__) && (__CUDA_ARCH__ <= 110) - __shared__ float smem1[128]; - __shared__ float smem2[128]; - __shared__ float smem3[128]; -#else - __shared__ float smem1[256]; - __shared__ float smem2[256]; - __shared__ float smem3[256]; -#endif - - const int tid = threadIdx.y * blockDim.x + threadIdx.x; - - float2 prevPt = prevPts[blockIdx.x]; - prevPt.x *= (1.0f / (1 << level)); - prevPt.y *= (1.0f / (1 << level)); - - if (prevPt.x < 0 || prevPt.x >= cols || prevPt.y < 0 || prevPt.y >= rows) + if (nextPt.x < -c_halfWin_x || nextPt.x >= cols || nextPt.y < -c_halfWin_y || nextPt.y >= rows) { if (tid == 0 && level == 0) status[blockIdx.x] = 0; @@ -297,388 +240,329 @@ namespace cv { namespace gpu { namespace device return; } - prevPt.x -= c_halfWin_x; - prevPt.y -= c_halfWin_y; - - // extract the patch from the first image, compute covariation matrix of derivatives + float b1 = 0; + float b2 = 0; - float A11 = 0; - float A12 = 0; - float A22 = 0; - - typedef typename TypeVec::vec_type work_type; - - work_type I_patch [PATCH_Y][PATCH_X]; - work_type dIdx_patch[PATCH_Y][PATCH_X]; - work_type dIdy_patch[PATCH_Y][PATCH_X]; - - for (int yBase = threadIdx.y, i = 0; yBase < c_winSize_y; yBase += blockDim.y, ++i) + for (int y = threadIdx.y, i = 0; y < c_winSize_y; y += blockDim.y, ++i) { - for (int xBase = threadIdx.x, j = 0; xBase < c_winSize_x; xBase += blockDim.x, ++j) + for (int x = threadIdx.x, j = 0; x < c_winSize_x; x += blockDim.x, ++j) { - float x = prevPt.x + xBase + 0.5f; - float y = prevPt.y + yBase + 0.5f; - - I_patch[i][j] = Tex_I::read(x, y); - - // Sharr Deriv - - work_type dIdx = 3.0f * Tex_I::read(x+1, y-1) + 10.0f * Tex_I::read(x+1, y) + 3.0f * Tex_I::read(x+1, y+1) - - (3.0f * Tex_I::read(x-1, y-1) + 10.0f * Tex_I::read(x-1, y) + 3.0f * Tex_I::read(x-1, y+1)); + work_type I_val = I_patch[i][j]; + work_type J_val = Tex_J::read(nextPt.x + x + 0.5f, nextPt.y + y + 0.5f); - work_type dIdy = 3.0f * Tex_I::read(x-1, y+1) + 10.0f * Tex_I::read(x, y+1) + 3.0f * Tex_I::read(x+1, y+1) - - (3.0f * Tex_I::read(x-1, y-1) + 10.0f * Tex_I::read(x, y-1) + 3.0f * Tex_I::read(x+1, y-1)); + work_type diff = (J_val - I_val) * 32.0f; - dIdx_patch[i][j] = dIdx; - dIdy_patch[i][j] = dIdy; - - accum(A11, dIdx * dIdx); - accum(A12, dIdx * dIdy); - accum(A22, dIdy * dIdy); + accum(b1, diff * dIdx_patch[i][j]); + accum(b2, diff * dIdy_patch[i][j]); } } - reduce(A11, A12, A22, smem1, smem2, smem3, tid); - __syncthreads(); - - A11 = smem1[0]; - A12 = smem2[0]; - A22 = smem3[0]; + reduce(smem_tuple(smem1, smem2), thrust::tie(b1, b2), tid, thrust::make_tuple(plus(), plus())); - float D = A11 * A22 - A12 * A12; - - if (D < numeric_limits::epsilon()) + #if __CUDA_ARCH__ >= 300 + if (tid == 0) { - if (tid == 0 && level == 0) - status[blockIdx.x] = 0; - - return; + smem1[0] = b1; + smem2[0] = b2; } + #endif - D = 1.f / D; + __syncthreads(); + + b1 = smem1[0]; + b2 = smem2[0]; - A11 *= D; - A12 *= D; - A22 *= D; + float2 delta; + delta.x = A12 * b2 - A22 * b1; + delta.y = A12 * b1 - A11 * b2; - float2 nextPt = nextPts[blockIdx.x]; - nextPt.x *= 2.f; - nextPt.y *= 2.f; + nextPt.x += delta.x; + nextPt.y += delta.y; - nextPt.x -= c_halfWin_x; - nextPt.y -= c_halfWin_y; + if (::fabs(delta.x) < 0.01f && ::fabs(delta.y) < 0.01f) + break; + } - for (int k = 0; k < c_iters; ++k) + float errval = 0; + if (calcErr) + { + for (int y = threadIdx.y, i = 0; y < c_winSize_y; y += blockDim.y, ++i) { - if (nextPt.x < -c_halfWin_x || nextPt.x >= cols || nextPt.y < -c_halfWin_y || nextPt.y >= rows) + for (int x = threadIdx.x, j = 0; x < c_winSize_x; x += blockDim.x, ++j) { - if (tid == 0 && level == 0) - status[blockIdx.x] = 0; + work_type I_val = I_patch[i][j]; + work_type J_val = Tex_J::read(nextPt.x + x + 0.5f, nextPt.y + y + 0.5f); + + work_type diff = J_val - I_val; - return; + accum(errval, abs_(diff)); } + } - float b1 = 0; - float b2 = 0; + reduce(smem1, errval, tid, plus()); + } - for (int y = threadIdx.y, i = 0; y < c_winSize_y; y += blockDim.y, ++i) - { - for (int x = threadIdx.x, j = 0; x < c_winSize_x; x += blockDim.x, ++j) - { - work_type I_val = I_patch[i][j]; - work_type J_val = Tex_J::read(nextPt.x + x + 0.5f, nextPt.y + y + 0.5f); + if (tid == 0) + { + nextPt.x += c_halfWin_x; + nextPt.y += c_halfWin_y; - work_type diff = (J_val - I_val) * 32.0f; + nextPts[blockIdx.x] = nextPt; - accum(b1, diff * dIdx_patch[i][j]); - accum(b2, diff * dIdy_patch[i][j]); - } - } - - reduce(b1, b2, smem1, smem2, tid); - __syncthreads(); + if (calcErr) + err[blockIdx.x] = static_cast(errval) / (cn * c_winSize_x * c_winSize_y); + } + } - b1 = smem1[0]; - b2 = smem2[0]; + template + void sparse_caller(int rows, int cols, const float2* prevPts, float2* nextPts, uchar* status, float* err, int ptcount, + int level, dim3 block, cudaStream_t stream) + { + dim3 grid(ptcount); - float2 delta; - delta.x = A12 * b2 - A22 * b1; - delta.y = A12 * b1 - A11 * b2; + if (level == 0 && err) + sparse<<>>(prevPts, nextPts, status, err, level, rows, cols); + else + sparse<<>>(prevPts, nextPts, status, err, level, rows, cols); - nextPt.x += delta.x; - nextPt.y += delta.y; + cudaSafeCall( cudaGetLastError() ); - if (::fabs(delta.x) < 0.01f && ::fabs(delta.y) < 0.01f) - break; - } + if (stream == 0) + cudaSafeCall( cudaDeviceSynchronize() ); + } - float errval = 0; - if (calcErr) - { - for (int y = threadIdx.y, i = 0; y < c_winSize_y; y += blockDim.y, ++i) - { - for (int x = threadIdx.x, j = 0; x < c_winSize_x; x += blockDim.x, ++j) - { - work_type I_val = I_patch[i][j]; - work_type J_val = Tex_J::read(nextPt.x + x + 0.5f, nextPt.y + y + 0.5f); + template + __global__ void dense(PtrStepf u, PtrStepf v, const PtrStepf prevU, const PtrStepf prevV, PtrStepf err, const int rows, const int cols) + { + extern __shared__ int smem[]; - work_type diff = J_val - I_val; + const int patchWidth = blockDim.x + 2 * c_halfWin_x; + const int patchHeight = blockDim.y + 2 * c_halfWin_y; - accum(errval, abs_(diff)); - } - } + int* I_patch = smem; + int* dIdx_patch = I_patch + patchWidth * patchHeight; + int* dIdy_patch = dIdx_patch + patchWidth * patchHeight; - reduce(errval, smem1, tid); - } + const int xBase = blockIdx.x * blockDim.x; + const int yBase = blockIdx.y * blockDim.y; - if (tid == 0) + for (int i = threadIdx.y; i < patchHeight; i += blockDim.y) + { + for (int j = threadIdx.x; j < patchWidth; j += blockDim.x) { - nextPt.x += c_halfWin_x; - nextPt.y += c_halfWin_y; + float x = xBase - c_halfWin_x + j + 0.5f; + float y = yBase - c_halfWin_y + i + 0.5f; - nextPts[blockIdx.x] = nextPt; + I_patch[i * patchWidth + j] = tex2D(tex_Ib, x, y); - if (calcErr) - err[blockIdx.x] = static_cast(errval) / (cn * c_winSize_x * c_winSize_y); + // Sharr Deriv + + dIdx_patch[i * patchWidth + j] = 3 * tex2D(tex_Ib, x+1, y-1) + 10 * tex2D(tex_Ib, x+1, y) + 3 * tex2D(tex_Ib, x+1, y+1) - + (3 * tex2D(tex_Ib, x-1, y-1) + 10 * tex2D(tex_Ib, x-1, y) + 3 * tex2D(tex_Ib, x-1, y+1)); + + dIdy_patch[i * patchWidth + j] = 3 * tex2D(tex_Ib, x-1, y+1) + 10 * tex2D(tex_Ib, x, y+1) + 3 * tex2D(tex_Ib, x+1, y+1) - + (3 * tex2D(tex_Ib, x-1, y-1) + 10 * tex2D(tex_Ib, x, y-1) + 3 * tex2D(tex_Ib, x+1, y-1)); } } - template - void lkSparse_caller(int rows, int cols, const float2* prevPts, float2* nextPts, uchar* status, float* err, int ptcount, - int level, dim3 block, cudaStream_t stream) - { - dim3 grid(ptcount); + __syncthreads(); - if (level == 0 && err) - lkSparse<<>>(prevPts, nextPts, status, err, level, rows, cols); - else - lkSparse<<>>(prevPts, nextPts, status, err, level, rows, cols); + const int x = xBase + threadIdx.x; + const int y = yBase + threadIdx.y; - cudaSafeCall( cudaGetLastError() ); + if (x >= cols || y >= rows) + return; - if (stream == 0) - cudaSafeCall( cudaDeviceSynchronize() ); - } + int A11i = 0; + int A12i = 0; + int A22i = 0; - void lkSparse1_gpu(PtrStepSzf I, PtrStepSzf J, const float2* prevPts, float2* nextPts, uchar* status, float* err, int ptcount, - int level, dim3 block, dim3 patch, cudaStream_t stream) + for (int i = 0; i < c_winSize_y; ++i) { - typedef void (*func_t)(int rows, int cols, const float2* prevPts, float2* nextPts, uchar* status, float* err, int ptcount, - int level, dim3 block, cudaStream_t stream); - - static const func_t funcs[5][5] = + for (int j = 0; j < c_winSize_x; ++j) { - {lkSparse_caller<1, 1, 1>, lkSparse_caller<1, 2, 1>, lkSparse_caller<1, 3, 1>, lkSparse_caller<1, 4, 1>, lkSparse_caller<1, 5, 1>}, - {lkSparse_caller<1, 1, 2>, lkSparse_caller<1, 2, 2>, lkSparse_caller<1, 3, 2>, lkSparse_caller<1, 4, 2>, lkSparse_caller<1, 5, 2>}, - {lkSparse_caller<1, 1, 3>, lkSparse_caller<1, 2, 3>, lkSparse_caller<1, 3, 3>, lkSparse_caller<1, 4, 3>, lkSparse_caller<1, 5, 3>}, - {lkSparse_caller<1, 1, 4>, lkSparse_caller<1, 2, 4>, lkSparse_caller<1, 3, 4>, lkSparse_caller<1, 4, 4>, lkSparse_caller<1, 5, 4>}, - {lkSparse_caller<1, 1, 5>, lkSparse_caller<1, 2, 5>, lkSparse_caller<1, 3, 5>, lkSparse_caller<1, 4, 5>, lkSparse_caller<1, 5, 5>} - }; - - bindTexture(&tex_If, I); - bindTexture(&tex_Jf, J); - - funcs[patch.y - 1][patch.x - 1](I.rows, I.cols, prevPts, nextPts, status, err, ptcount, - level, block, stream); - } + int dIdx = dIdx_patch[(threadIdx.y + i) * patchWidth + (threadIdx.x + j)]; + int dIdy = dIdy_patch[(threadIdx.y + i) * patchWidth + (threadIdx.x + j)]; - void lkSparse4_gpu(PtrStepSz I, PtrStepSz J, const float2* prevPts, float2* nextPts, uchar* status, float* err, int ptcount, - int level, dim3 block, dim3 patch, cudaStream_t stream) - { - typedef void (*func_t)(int rows, int cols, const float2* prevPts, float2* nextPts, uchar* status, float* err, int ptcount, - int level, dim3 block, cudaStream_t stream); - - static const func_t funcs[5][5] = - { - {lkSparse_caller<4, 1, 1>, lkSparse_caller<4, 2, 1>, lkSparse_caller<4, 3, 1>, lkSparse_caller<4, 4, 1>, lkSparse_caller<4, 5, 1>}, - {lkSparse_caller<4, 1, 2>, lkSparse_caller<4, 2, 2>, lkSparse_caller<4, 3, 2>, lkSparse_caller<4, 4, 2>, lkSparse_caller<4, 5, 2>}, - {lkSparse_caller<4, 1, 3>, lkSparse_caller<4, 2, 3>, lkSparse_caller<4, 3, 3>, lkSparse_caller<4, 4, 3>, lkSparse_caller<4, 5, 3>}, - {lkSparse_caller<4, 1, 4>, lkSparse_caller<4, 2, 4>, lkSparse_caller<4, 3, 4>, lkSparse_caller<4, 4, 4>, lkSparse_caller<4, 5, 4>}, - {lkSparse_caller<4, 1, 5>, lkSparse_caller<4, 2, 5>, lkSparse_caller<4, 3, 5>, lkSparse_caller<4, 4, 5>, lkSparse_caller<4, 5, 5>} - }; - - bindTexture(&tex_If4, I); - bindTexture(&tex_Jf4, J); - - funcs[patch.y - 1][patch.x - 1](I.rows, I.cols, prevPts, nextPts, status, err, ptcount, - level, block, stream); + A11i += dIdx * dIdx; + A12i += dIdx * dIdy; + A22i += dIdy * dIdy; + } } - template - __global__ void lkDense(PtrStepf u, PtrStepf v, const PtrStepf prevU, const PtrStepf prevV, PtrStepf err, const int rows, const int cols) - { - extern __shared__ int smem[]; - - const int patchWidth = blockDim.x + 2 * c_halfWin_x; - const int patchHeight = blockDim.y + 2 * c_halfWin_y; + float A11 = A11i; + float A12 = A12i; + float A22 = A22i; - int* I_patch = smem; - int* dIdx_patch = I_patch + patchWidth * patchHeight; - int* dIdy_patch = dIdx_patch + patchWidth * patchHeight; + float D = A11 * A22 - A12 * A12; - const int xBase = blockIdx.x * blockDim.x; - const int yBase = blockIdx.y * blockDim.y; - - for (int i = threadIdx.y; i < patchHeight; i += blockDim.y) - { - for (int j = threadIdx.x; j < patchWidth; j += blockDim.x) - { - float x = xBase - c_halfWin_x + j + 0.5f; - float y = yBase - c_halfWin_y + i + 0.5f; - - I_patch[i * patchWidth + j] = tex2D(tex_Ib, x, y); + if (D < numeric_limits::epsilon()) + { + if (calcErr) + err(y, x) = numeric_limits::max(); - // Sharr Deriv + return; + } - dIdx_patch[i * patchWidth + j] = 3 * tex2D(tex_Ib, x+1, y-1) + 10 * tex2D(tex_Ib, x+1, y) + 3 * tex2D(tex_Ib, x+1, y+1) - - (3 * tex2D(tex_Ib, x-1, y-1) + 10 * tex2D(tex_Ib, x-1, y) + 3 * tex2D(tex_Ib, x-1, y+1)); + D = 1.f / D; - dIdy_patch[i * patchWidth + j] = 3 * tex2D(tex_Ib, x-1, y+1) + 10 * tex2D(tex_Ib, x, y+1) + 3 * tex2D(tex_Ib, x+1, y+1) - - (3 * tex2D(tex_Ib, x-1, y-1) + 10 * tex2D(tex_Ib, x, y-1) + 3 * tex2D(tex_Ib, x+1, y-1)); - } - } + A11 *= D; + A12 *= D; + A22 *= D; - __syncthreads(); + float2 nextPt; + nextPt.x = x + prevU(y/2, x/2) * 2.0f; + nextPt.y = y + prevV(y/2, x/2) * 2.0f; - const int x = xBase + threadIdx.x; - const int y = yBase + threadIdx.y; + for (int k = 0; k < c_iters; ++k) + { + if (nextPt.x < 0 || nextPt.x >= cols || nextPt.y < 0 || nextPt.y >= rows) + { + if (calcErr) + err(y, x) = numeric_limits::max(); - if (x >= cols || y >= rows) return; + } - int A11i = 0; - int A12i = 0; - int A22i = 0; + int b1 = 0; + int b2 = 0; for (int i = 0; i < c_winSize_y; ++i) { for (int j = 0; j < c_winSize_x; ++j) { + int I = I_patch[(threadIdx.y + i) * patchWidth + threadIdx.x + j]; + int J = tex2D(tex_Jf, nextPt.x - c_halfWin_x + j + 0.5f, nextPt.y - c_halfWin_y + i + 0.5f); + + int diff = (J - I) * 32; + int dIdx = dIdx_patch[(threadIdx.y + i) * patchWidth + (threadIdx.x + j)]; int dIdy = dIdy_patch[(threadIdx.y + i) * patchWidth + (threadIdx.x + j)]; - A11i += dIdx * dIdx; - A12i += dIdx * dIdy; - A22i += dIdy * dIdy; + b1 += diff * dIdx; + b2 += diff * dIdy; } } - float A11 = A11i; - float A12 = A12i; - float A22 = A22i; + float2 delta; + delta.x = A12 * b2 - A22 * b1; + delta.y = A12 * b1 - A11 * b2; - float D = A11 * A22 - A12 * A12; + nextPt.x += delta.x; + nextPt.y += delta.y; - if (D < numeric_limits::epsilon()) - { - if (calcErr) - err(y, x) = numeric_limits::max(); - - return; - } - - D = 1.f / D; + if (::fabs(delta.x) < 0.01f && ::fabs(delta.y) < 0.01f) + break; + } - A11 *= D; - A12 *= D; - A22 *= D; + u(y, x) = nextPt.x - x; + v(y, x) = nextPt.y - y; - float2 nextPt; - nextPt.x = x + prevU(y/2, x/2) * 2.0f; - nextPt.y = y + prevV(y/2, x/2) * 2.0f; + if (calcErr) + { + int errval = 0; - for (int k = 0; k < c_iters; ++k) + for (int i = 0; i < c_winSize_y; ++i) { - if (nextPt.x < 0 || nextPt.x >= cols || nextPt.y < 0 || nextPt.y >= rows) + for (int j = 0; j < c_winSize_x; ++j) { - if (calcErr) - err(y, x) = numeric_limits::max(); + int I = I_patch[(threadIdx.y + i) * patchWidth + threadIdx.x + j]; + int J = tex2D(tex_Jf, nextPt.x - c_halfWin_x + j + 0.5f, nextPt.y - c_halfWin_y + i + 0.5f); - return; + errval += ::abs(J - I); } + } - int b1 = 0; - int b2 = 0; - - for (int i = 0; i < c_winSize_y; ++i) - { - for (int j = 0; j < c_winSize_x; ++j) - { - int I = I_patch[(threadIdx.y + i) * patchWidth + threadIdx.x + j]; - int J = tex2D(tex_Jf, nextPt.x - c_halfWin_x + j + 0.5f, nextPt.y - c_halfWin_y + i + 0.5f); - - int diff = (J - I) * 32; + err(y, x) = static_cast(errval) / (c_winSize_x * c_winSize_y); + } + } +} - int dIdx = dIdx_patch[(threadIdx.y + i) * patchWidth + (threadIdx.x + j)]; - int dIdy = dIdy_patch[(threadIdx.y + i) * patchWidth + (threadIdx.x + j)]; +namespace pyrlk +{ + void loadConstants(int2 winSize, int iters) + { + cudaSafeCall( cudaMemcpyToSymbol(c_winSize_x, &winSize.x, sizeof(int)) ); + cudaSafeCall( cudaMemcpyToSymbol(c_winSize_y, &winSize.y, sizeof(int)) ); - b1 += diff * dIdx; - b2 += diff * dIdy; - } - } + int2 halfWin = make_int2((winSize.x - 1) / 2, (winSize.y - 1) / 2); + cudaSafeCall( cudaMemcpyToSymbol(c_halfWin_x, &halfWin.x, sizeof(int)) ); + cudaSafeCall( cudaMemcpyToSymbol(c_halfWin_y, &halfWin.y, sizeof(int)) ); - float2 delta; - delta.x = A12 * b2 - A22 * b1; - delta.y = A12 * b1 - A11 * b2; + cudaSafeCall( cudaMemcpyToSymbol(c_iters, &iters, sizeof(int)) ); + } - nextPt.x += delta.x; - nextPt.y += delta.y; + void sparse1(PtrStepSzf I, PtrStepSzf J, const float2* prevPts, float2* nextPts, uchar* status, float* err, int ptcount, + int level, dim3 block, dim3 patch, cudaStream_t stream) + { + typedef void (*func_t)(int rows, int cols, const float2* prevPts, float2* nextPts, uchar* status, float* err, int ptcount, + int level, dim3 block, cudaStream_t stream); - if (::fabs(delta.x) < 0.01f && ::fabs(delta.y) < 0.01f) - break; - } + static const func_t funcs[5][5] = + { + {::sparse_caller<1, 1, 1>, ::sparse_caller<1, 2, 1>, ::sparse_caller<1, 3, 1>, ::sparse_caller<1, 4, 1>, ::sparse_caller<1, 5, 1>}, + {::sparse_caller<1, 1, 2>, ::sparse_caller<1, 2, 2>, ::sparse_caller<1, 3, 2>, ::sparse_caller<1, 4, 2>, ::sparse_caller<1, 5, 2>}, + {::sparse_caller<1, 1, 3>, ::sparse_caller<1, 2, 3>, ::sparse_caller<1, 3, 3>, ::sparse_caller<1, 4, 3>, ::sparse_caller<1, 5, 3>}, + {::sparse_caller<1, 1, 4>, ::sparse_caller<1, 2, 4>, ::sparse_caller<1, 3, 4>, ::sparse_caller<1, 4, 4>, ::sparse_caller<1, 5, 4>}, + {::sparse_caller<1, 1, 5>, ::sparse_caller<1, 2, 5>, ::sparse_caller<1, 3, 5>, ::sparse_caller<1, 4, 5>, ::sparse_caller<1, 5, 5>} + }; - u(y, x) = nextPt.x - x; - v(y, x) = nextPt.y - y; + bindTexture(&tex_If, I); + bindTexture(&tex_Jf, J); - if (calcErr) - { - int errval = 0; + funcs[patch.y - 1][patch.x - 1](I.rows, I.cols, prevPts, nextPts, status, err, ptcount, + level, block, stream); + } - for (int i = 0; i < c_winSize_y; ++i) - { - for (int j = 0; j < c_winSize_x; ++j) - { - int I = I_patch[(threadIdx.y + i) * patchWidth + threadIdx.x + j]; - int J = tex2D(tex_Jf, nextPt.x - c_halfWin_x + j + 0.5f, nextPt.y - c_halfWin_y + i + 0.5f); + void sparse4(PtrStepSz I, PtrStepSz J, const float2* prevPts, float2* nextPts, uchar* status, float* err, int ptcount, + int level, dim3 block, dim3 patch, cudaStream_t stream) + { + typedef void (*func_t)(int rows, int cols, const float2* prevPts, float2* nextPts, uchar* status, float* err, int ptcount, + int level, dim3 block, cudaStream_t stream); - errval += ::abs(J - I); - } - } + static const func_t funcs[5][5] = + { + {::sparse_caller<4, 1, 1>, ::sparse_caller<4, 2, 1>, ::sparse_caller<4, 3, 1>, ::sparse_caller<4, 4, 1>, ::sparse_caller<4, 5, 1>}, + {::sparse_caller<4, 1, 2>, ::sparse_caller<4, 2, 2>, ::sparse_caller<4, 3, 2>, ::sparse_caller<4, 4, 2>, ::sparse_caller<4, 5, 2>}, + {::sparse_caller<4, 1, 3>, ::sparse_caller<4, 2, 3>, ::sparse_caller<4, 3, 3>, ::sparse_caller<4, 4, 3>, ::sparse_caller<4, 5, 3>}, + {::sparse_caller<4, 1, 4>, ::sparse_caller<4, 2, 4>, ::sparse_caller<4, 3, 4>, ::sparse_caller<4, 4, 4>, ::sparse_caller<4, 5, 4>}, + {::sparse_caller<4, 1, 5>, ::sparse_caller<4, 2, 5>, ::sparse_caller<4, 3, 5>, ::sparse_caller<4, 4, 5>, ::sparse_caller<4, 5, 5>} + }; - err(y, x) = static_cast(errval) / (c_winSize_x * c_winSize_y); - } - } + bindTexture(&tex_If4, I); + bindTexture(&tex_Jf4, J); - void lkDense_gpu(PtrStepSzb I, PtrStepSzf J, PtrStepSzf u, PtrStepSzf v, PtrStepSzf prevU, PtrStepSzf prevV, - PtrStepSzf err, int2 winSize, cudaStream_t stream) - { - dim3 block(16, 16); - dim3 grid(divUp(I.cols, block.x), divUp(I.rows, block.y)); + funcs[patch.y - 1][patch.x - 1](I.rows, I.cols, prevPts, nextPts, status, err, ptcount, + level, block, stream); + } - bindTexture(&tex_Ib, I); - bindTexture(&tex_Jf, J); + void dense(PtrStepSzb I, PtrStepSzf J, PtrStepSzf u, PtrStepSzf v, PtrStepSzf prevU, PtrStepSzf prevV, PtrStepSzf err, int2 winSize, cudaStream_t stream) + { + dim3 block(16, 16); + dim3 grid(divUp(I.cols, block.x), divUp(I.rows, block.y)); - int2 halfWin = make_int2((winSize.x - 1) / 2, (winSize.y - 1) / 2); - const int patchWidth = block.x + 2 * halfWin.x; - const int patchHeight = block.y + 2 * halfWin.y; - size_t smem_size = 3 * patchWidth * patchHeight * sizeof(int); + bindTexture(&tex_Ib, I); + bindTexture(&tex_Jf, J); - if (err.data) - { - lkDense<<>>(u, v, prevU, prevV, err, I.rows, I.cols); - cudaSafeCall( cudaGetLastError() ); - } - else - { - lkDense<<>>(u, v, prevU, prevV, PtrStepf(), I.rows, I.cols); - cudaSafeCall( cudaGetLastError() ); - } + int2 halfWin = make_int2((winSize.x - 1) / 2, (winSize.y - 1) / 2); + const int patchWidth = block.x + 2 * halfWin.x; + const int patchHeight = block.y + 2 * halfWin.y; + size_t smem_size = 3 * patchWidth * patchHeight * sizeof(int); - if (stream == 0) - cudaSafeCall( cudaDeviceSynchronize() ); + if (err.data) + { + ::dense<<>>(u, v, prevU, prevV, err, I.rows, I.cols); + cudaSafeCall( cudaGetLastError() ); + } + else + { + ::dense<<>>(u, v, prevU, prevV, PtrStepf(), I.rows, I.cols); + cudaSafeCall( cudaGetLastError() ); } + + if (stream == 0) + cudaSafeCall( cudaDeviceSynchronize() ); } -}}} +} #endif /* CUDA_DISABLER */ diff --git a/modules/gpu/src/pyrlk.cpp b/modules/gpu/src/pyrlk.cpp index 47ab90415f..593e37cc66 100644 --- a/modules/gpu/src/pyrlk.cpp +++ b/modules/gpu/src/pyrlk.cpp @@ -55,21 +55,18 @@ void cv::gpu::PyrLKOpticalFlow::releaseMemory() {} #else /* !defined (HAVE_CUDA) */ -namespace cv { namespace gpu { namespace device +namespace pyrlk { - namespace pyrlk - { - void loadConstants(int2 winSize, int iters); + void loadConstants(int2 winSize, int iters); - void lkSparse1_gpu(PtrStepSzf I, PtrStepSzf J, const float2* prevPts, float2* nextPts, uchar* status, float* err, int ptcount, - int level, dim3 block, dim3 patch, cudaStream_t stream = 0); - void lkSparse4_gpu(PtrStepSz I, PtrStepSz J, const float2* prevPts, float2* nextPts, uchar* status, float* err, int ptcount, - int level, dim3 block, dim3 patch, cudaStream_t stream = 0); + void sparse1(PtrStepSzf I, PtrStepSzf J, const float2* prevPts, float2* nextPts, uchar* status, float* err, int ptcount, + int level, dim3 block, dim3 patch, cudaStream_t stream = 0); + void sparse4(PtrStepSz I, PtrStepSz J, const float2* prevPts, float2* nextPts, uchar* status, float* err, int ptcount, + int level, dim3 block, dim3 patch, cudaStream_t stream = 0); - void lkDense_gpu(PtrStepSzb I, PtrStepSzf J, PtrStepSzf u, PtrStepSzf v, PtrStepSzf prevU, PtrStepSzf prevV, - PtrStepSzf err, int2 winSize, cudaStream_t stream = 0); - } -}}} + void dense(PtrStepSzb I, PtrStepSzf J, PtrStepSzf u, PtrStepSzf v, PtrStepSzf prevU, PtrStepSzf prevV, + PtrStepSzf err, int2 winSize, cudaStream_t stream = 0); +} cv::gpu::PyrLKOpticalFlow::PyrLKOpticalFlow() { @@ -104,8 +101,6 @@ namespace void cv::gpu::PyrLKOpticalFlow::sparse(const GpuMat& prevImg, const GpuMat& nextImg, const GpuMat& prevPts, GpuMat& nextPts, GpuMat& status, GpuMat* err) { - using namespace cv::gpu::device::pyrlk; - if (prevPts.empty()) { nextPts.release(); @@ -166,19 +161,19 @@ void cv::gpu::PyrLKOpticalFlow::sparse(const GpuMat& prevImg, const GpuMat& next pyrDown(nextPyr_[level - 1], nextPyr_[level]); } - loadConstants(make_int2(winSize.width, winSize.height), iters); + pyrlk::loadConstants(make_int2(winSize.width, winSize.height), iters); for (int level = maxLevel; level >= 0; level--) { if (cn == 1) { - lkSparse1_gpu(prevPyr_[level], nextPyr_[level], + pyrlk::sparse1(prevPyr_[level], nextPyr_[level], prevPts.ptr(), nextPts.ptr(), status.ptr(), level == 0 && err ? err->ptr() : 0, prevPts.cols, level, block, patch); } else { - lkSparse4_gpu(prevPyr_[level], nextPyr_[level], + pyrlk::sparse4(prevPyr_[level], nextPyr_[level], prevPts.ptr(), nextPts.ptr(), status.ptr(), level == 0 && err ? err->ptr() : 0, prevPts.cols, level, block, patch); } @@ -187,8 +182,6 @@ void cv::gpu::PyrLKOpticalFlow::sparse(const GpuMat& prevImg, const GpuMat& next void cv::gpu::PyrLKOpticalFlow::dense(const GpuMat& prevImg, const GpuMat& nextImg, GpuMat& u, GpuMat& v, GpuMat* err) { - using namespace cv::gpu::device::pyrlk; - CV_Assert(prevImg.type() == CV_8UC1); CV_Assert(prevImg.size() == nextImg.size() && prevImg.type() == nextImg.type()); CV_Assert(maxLevel >= 0); @@ -219,7 +212,7 @@ void cv::gpu::PyrLKOpticalFlow::dense(const GpuMat& prevImg, const GpuMat& nextI vPyr_[1].setTo(Scalar::all(0)); int2 winSize2i = make_int2(winSize.width, winSize.height); - loadConstants(winSize2i, iters); + pyrlk::loadConstants(winSize2i, iters); PtrStepSzf derr = err ? *err : PtrStepSzf(); @@ -229,7 +222,7 @@ void cv::gpu::PyrLKOpticalFlow::dense(const GpuMat& prevImg, const GpuMat& nextI { int idx2 = (idx + 1) & 1; - lkDense_gpu(prevPyr_[level], nextPyr_[level], uPyr_[idx], vPyr_[idx], uPyr_[idx2], vPyr_[idx2], + pyrlk::dense(prevPyr_[level], nextPyr_[level], uPyr_[idx], vPyr_[idx], uPyr_[idx2], vPyr_[idx2], level == 0 ? derr : PtrStepSzf(), winSize2i); if (level > 0) From 7f97fb481cbdd3b2a432332afecb6ae7ca421d8a Mon Sep 17 00:00:00 2001 From: Vladislav Vinogradov Date: Mon, 12 Nov 2012 14:14:48 +0400 Subject: [PATCH 11/23] FastNonLocalMeans --- modules/gpu/src/cuda/nlm.cu | 116 ++++++++++++++++++++++++++++-------- 1 file changed, 90 insertions(+), 26 deletions(-) diff --git a/modules/gpu/src/cuda/nlm.cu b/modules/gpu/src/cuda/nlm.cu index e267c733e0..cd3f0b5c3a 100644 --- a/modules/gpu/src/cuda/nlm.cu +++ b/modules/gpu/src/cuda/nlm.cu @@ -43,11 +43,11 @@ #if !defined CUDA_DISABLER -#include "internal_shared.hpp" - +#include "opencv2/gpu/device/common.hpp" #include "opencv2/gpu/device/vec_traits.hpp" #include "opencv2/gpu/device/vec_math.hpp" -#include "opencv2/gpu/device/block.hpp" +#include "opencv2/gpu/device/functional.hpp" +#include "opencv2/gpu/device/reduce.hpp" #include "opencv2/gpu/device/border_interpolate.hpp" using namespace cv::gpu; @@ -184,6 +184,85 @@ namespace cv { namespace gpu { namespace device { namespace imgproc { + + template struct Unroll; + template <> struct Unroll<1> + { + template + static __device__ __forceinline__ thrust::tuple smem_tuple(float* smem) + { + return cv::gpu::device::smem_tuple(smem, smem + BLOCK_SIZE); + } + + static __device__ __forceinline__ thrust::tuple tie(float& val1, float& val2) + { + return thrust::tie(val1, val2); + } + + static __device__ __forceinline__ const thrust::tuple, plus > op() + { + plus op; + return thrust::make_tuple(op, op); + } + }; + template <> struct Unroll<2> + { + template + static __device__ __forceinline__ thrust::tuple smem_tuple(float* smem) + { + return cv::gpu::device::smem_tuple(smem, smem + BLOCK_SIZE, smem + 2 * BLOCK_SIZE); + } + + static __device__ __forceinline__ thrust::tuple tie(float& val1, float2& val2) + { + return thrust::tie(val1, val2.x, val2.y); + } + + static __device__ __forceinline__ const thrust::tuple, plus, plus > op() + { + plus op; + return thrust::make_tuple(op, op, op); + } + }; + template <> struct Unroll<3> + { + template + static __device__ __forceinline__ thrust::tuple smem_tuple(float* smem) + { + return cv::gpu::device::smem_tuple(smem, smem + BLOCK_SIZE, smem + 2 * BLOCK_SIZE, smem + 3 * BLOCK_SIZE); + } + + static __device__ __forceinline__ thrust::tuple tie(float& val1, float3& val2) + { + return thrust::tie(val1, val2.x, val2.y, val2.z); + } + + static __device__ __forceinline__ const thrust::tuple, plus, plus, plus > op() + { + plus op; + return thrust::make_tuple(op, op, op, op); + } + }; + template <> struct Unroll<4> + { + template + static __device__ __forceinline__ thrust::tuple smem_tuple(float* smem) + { + return cv::gpu::device::smem_tuple(smem, smem + BLOCK_SIZE, smem + 2 * BLOCK_SIZE, smem + 3 * BLOCK_SIZE, smem + 4 * BLOCK_SIZE); + } + + static __device__ __forceinline__ thrust::tuple tie(float& val1, float4& val2) + { + return thrust::tie(val1, val2.x, val2.y, val2.z, val2.w); + } + + static __device__ __forceinline__ const thrust::tuple, plus, plus, plus, plus > op() + { + plus op; + return thrust::make_tuple(op, op, op, op, op); + } + }; + __device__ __forceinline__ int calcDist(const uchar& a, const uchar& b) { return (a-b)*(a-b); } __device__ __forceinline__ int calcDist(const uchar2& a, const uchar2& b) { return (a.x-b.x)*(a.x-b.x) + (a.y-b.y)*(a.y-b.y); } __device__ __forceinline__ int calcDist(const uchar3& a, const uchar3& b) { return (a.x-b.x)*(a.x-b.x) + (a.y-b.y)*(a.y-b.y) + (a.z-b.z)*(a.z-b.z); } @@ -340,30 +419,15 @@ namespace cv { namespace gpu { namespace device sum = sum + weight * saturate_cast(src(sy + y, sx + x)); } - volatile __shared__ float cta_buffer[CTA_SIZE]; - - int tid = threadIdx.x; + __shared__ float cta_buffer[CTA_SIZE * (VecTraits::cn + 1)]; - cta_buffer[tid] = weights_sum; - __syncthreads(); - Block::reduce(cta_buffer, plus()); - weights_sum = cta_buffer[0]; - - __syncthreads(); - - - for(int n = 0; n < VecTraits::cn; ++n) - { - cta_buffer[tid] = reinterpret_cast(&sum)[n]; - __syncthreads(); - Block::reduce(cta_buffer, plus()); - reinterpret_cast(&sum)[n] = cta_buffer[0]; - - __syncthreads(); - } + reduce(Unroll::cn>::template smem_tuple(cta_buffer), + Unroll::cn>::tie(weights_sum, sum), + threadIdx.x, + Unroll::cn>::op()); - if (tid == 0) - dst = saturate_cast(sum/weights_sum); + if (threadIdx.x == 0) + dst = saturate_cast(sum / weights_sum); } __device__ __forceinline__ void operator()(PtrStepSz& dst) const @@ -503,4 +567,4 @@ namespace cv { namespace gpu { namespace device }}} -#endif /* CUDA_DISABLER */ \ No newline at end of file +#endif /* CUDA_DISABLER */ From 19c87d1c9d75b348bb1027afe6fc29cf7457a3c0 Mon Sep 17 00:00:00 2001 From: Vladislav Vinogradov Date: Mon, 12 Nov 2012 14:17:48 +0400 Subject: [PATCH 12/23] ORB --- modules/gpu/src/cuda/orb.cu | 30 ++++++++++++++++++------------ 1 file changed, 18 insertions(+), 12 deletions(-) diff --git a/modules/gpu/src/cuda/orb.cu b/modules/gpu/src/cuda/orb.cu index 91c5709574..d66b3e9ece 100644 --- a/modules/gpu/src/cuda/orb.cu +++ b/modules/gpu/src/cuda/orb.cu @@ -50,7 +50,7 @@ #include #include "opencv2/gpu/device/common.hpp" -#include "opencv2/gpu/device/utility.hpp" +#include "opencv2/gpu/device/reduce.hpp" #include "opencv2/gpu/device/functional.hpp" namespace cv { namespace gpu { namespace device @@ -75,9 +75,9 @@ namespace cv { namespace gpu { namespace device __global__ void HarrisResponses(const PtrStepb img, const short2* loc_, float* response, const int npoints, const int blockSize, const float harris_k) { - __shared__ int smem[8 * 32]; - - volatile int* srow = smem + threadIdx.y * blockDim.x; + __shared__ int smem0[8 * 32]; + __shared__ int smem1[8 * 32]; + __shared__ int smem2[8 * 32]; const int ptidx = blockIdx.x * blockDim.y + threadIdx.y; @@ -109,9 +109,12 @@ namespace cv { namespace gpu { namespace device c += Ix * Iy; } - reduce_old<32>(srow, a, threadIdx.x, plus()); - reduce_old<32>(srow, b, threadIdx.x, plus()); - reduce_old<32>(srow, c, threadIdx.x, plus()); + int* srow0 = smem0 + threadIdx.y * blockDim.x; + int* srow1 = smem1 + threadIdx.y * blockDim.x; + int* srow2 = smem2 + threadIdx.y * blockDim.x; + + plus op; + reduce<32>(smem_tuple(srow0, srow1, srow2), thrust::tie(a, b, c), threadIdx.x, thrust::make_tuple(op, op, op)); if (threadIdx.x == 0) { @@ -151,9 +154,13 @@ namespace cv { namespace gpu { namespace device __global__ void IC_Angle(const PtrStepb image, const short2* loc_, float* angle, const int npoints, const int half_k) { - __shared__ int smem[8 * 32]; + __shared__ int smem0[8 * 32]; + __shared__ int smem1[8 * 32]; + + int* srow0 = smem0 + threadIdx.y * blockDim.x; + int* srow1 = smem1 + threadIdx.y * blockDim.x; - volatile int* srow = smem + threadIdx.y * blockDim.x; + plus op; const int ptidx = blockIdx.x * blockDim.y + threadIdx.y; @@ -167,7 +174,7 @@ namespace cv { namespace gpu { namespace device for (int u = threadIdx.x - half_k; u <= half_k; u += blockDim.x) m_10 += u * image(loc.y, loc.x + u); - reduce_old<32>(srow, m_10, threadIdx.x, plus()); + reduce<32>(srow0, m_10, threadIdx.x, op); for (int v = 1; v <= half_k; ++v) { @@ -185,8 +192,7 @@ namespace cv { namespace gpu { namespace device m_sum += u * (val_plus + val_minus); } - reduce_old<32>(srow, v_sum, threadIdx.x, plus()); - reduce_old<32>(srow, m_sum, threadIdx.x, plus()); + reduce<32>(smem_tuple(srow0, srow1), thrust::tie(v_sum, m_sum), threadIdx.x, thrust::make_tuple(op, op)); m_10 += m_sum; m_01 += v * v_sum; From fbf3de43a27259959f5a6096fcc7f3bec34a5915 Mon Sep 17 00:00:00 2001 From: Vladislav Vinogradov Date: Mon, 12 Nov 2012 14:20:43 +0400 Subject: [PATCH 13/23] SURF --- modules/gpu/src/cuda/surf.cu | 346 +++++++++++---------------- modules/gpu/src/surf.cpp | 3 +- modules/gpu/test/test_features2d.cpp | 2 +- 3 files changed, 138 insertions(+), 213 deletions(-) diff --git a/modules/gpu/src/cuda/surf.cu b/modules/gpu/src/cuda/surf.cu index 451fb425d3..c121925551 100644 --- a/modules/gpu/src/cuda/surf.cu +++ b/modules/gpu/src/cuda/surf.cu @@ -47,13 +47,13 @@ #if !defined CUDA_DISABLER -#include "internal_shared.hpp" +#include "opencv2/gpu/device/common.hpp" #include "opencv2/gpu/device/limits.hpp" #include "opencv2/gpu/device/saturate_cast.hpp" +#include "opencv2/gpu/device/reduce.hpp" #include "opencv2/gpu/device/utility.hpp" #include "opencv2/gpu/device/functional.hpp" #include "opencv2/gpu/device/filters.hpp" -#include namespace cv { namespace gpu { namespace device { @@ -599,8 +599,9 @@ namespace cv { namespace gpu { namespace device sumy += s_Y[threadIdx.x + 96]; } - device::reduce_old<32>(s_sumx + threadIdx.y * 32, sumx, threadIdx.x, plus()); - device::reduce_old<32>(s_sumy + threadIdx.y * 32, sumy, threadIdx.x, plus()); + plus op; + device::reduce<32>(smem_tuple(s_sumx + threadIdx.y * 32, s_sumy + threadIdx.y * 32), + thrust::tie(sumx, sumy), threadIdx.x, thrust::make_tuple(op, op)); const float temp_mod = sumx * sumx + sumy * sumy; if (temp_mod > best_mod) @@ -638,7 +639,7 @@ namespace cv { namespace gpu { namespace device kp_dir *= 180.0f / CV_PI_F; kp_dir = 360.0f - kp_dir; - if (::fabsf(kp_dir - 360.f) < FLT_EPSILON) + if (::fabsf(kp_dir - 360.f) < numeric_limits::epsilon()) kp_dir = 0.f; featureDir[blockIdx.x] = kp_dir; @@ -697,11 +698,6 @@ namespace cv { namespace gpu { namespace device { typedef uchar elem_type; - __device__ __forceinline__ WinReader(float centerX_, float centerY_, float win_offset_, float cos_dir_, float sin_dir_) : - centerX(centerX_), centerY(centerY_), win_offset(win_offset_), cos_dir(cos_dir_), sin_dir(sin_dir_) - { - } - __device__ __forceinline__ uchar operator ()(int i, int j) const { float pixel_x = centerX + (win_offset + j) * cos_dir + (win_offset + i) * sin_dir; @@ -715,285 +711,215 @@ namespace cv { namespace gpu { namespace device float win_offset; float cos_dir; float sin_dir; + int width; + int height; }; - __device__ void calc_dx_dy(float s_dx_bin[25], float s_dy_bin[25], - const float* featureX, const float* featureY, const float* featureSize, const float* featureDir) + __device__ void calc_dx_dy(const float* featureX, const float* featureY, const float* featureSize, const float* featureDir, + float& dx, float& dy) { - __shared__ float s_PATCH[6][6]; + __shared__ float s_PATCH[PATCH_SZ + 1][PATCH_SZ + 1]; - const float centerX = featureX[blockIdx.x]; - const float centerY = featureY[blockIdx.x]; - const float size = featureSize[blockIdx.x]; - float descriptor_dir = 360.0f - featureDir[blockIdx.x]; - if (std::abs(descriptor_dir - 360.f) < FLT_EPSILON) - descriptor_dir = 0.f; - descriptor_dir *= (float)(CV_PI_F / 180.0f); + dx = dy = 0.0f; - /* The sampling intervals and wavelet sized for selecting an orientation - and building the keypoint descriptor are defined relative to 's' */ - const float s = size * 1.2f / 9.0f; + WinReader win; - /* Extract a window of pixels around the keypoint of size 20s */ - const int win_size = (int)((PATCH_SZ + 1) * s); + win.centerX = featureX[blockIdx.x]; + win.centerY = featureY[blockIdx.x]; - float sin_dir; - float cos_dir; - sincosf(descriptor_dir, &sin_dir, &cos_dir); + // The sampling intervals and wavelet sized for selecting an orientation + // and building the keypoint descriptor are defined relative to 's' + const float s = featureSize[blockIdx.x] * 1.2f / 9.0f; - /* Nearest neighbour version (faster) */ - const float win_offset = -(float)(win_size - 1) / 2; + // Extract a window of pixels around the keypoint of size 20s + const int win_size = (int)((PATCH_SZ + 1) * s); - // Compute sampling points - // since grids are 2D, need to compute xBlock and yBlock indices - const int xBlock = (blockIdx.y & 3); // blockIdx.y % 4 - const int yBlock = (blockIdx.y >> 2); // floor(blockIdx.y/4) - const int xIndex = xBlock * 5 + threadIdx.x; - const int yIndex = yBlock * 5 + threadIdx.y; + win.width = win.height = win_size; - const float icoo = ((float)yIndex / (PATCH_SZ + 1)) * win_size; - const float jcoo = ((float)xIndex / (PATCH_SZ + 1)) * win_size; + // Nearest neighbour version (faster) + win.win_offset = -(win_size - 1.0f) / 2.0f; - LinearFilter filter(WinReader(centerX, centerY, win_offset, cos_dir, sin_dir)); + float descriptor_dir = 360.0f - featureDir[blockIdx.x]; + if (::fabsf(descriptor_dir - 360.f) < numeric_limits::epsilon()) + descriptor_dir = 0.f; + descriptor_dir *= CV_PI_F / 180.0f; + sincosf(descriptor_dir, &win.sin_dir, &win.cos_dir); - s_PATCH[threadIdx.y][threadIdx.x] = filter(icoo, jcoo); + const int tid = threadIdx.y * blockDim.x + threadIdx.x; - __syncthreads(); + const int xLoadInd = tid % (PATCH_SZ + 1); + const int yLoadInd = tid / (PATCH_SZ + 1); - if (threadIdx.x < 5 && threadIdx.y < 5) + if (yLoadInd < (PATCH_SZ + 1)) { - const int tid = threadIdx.y * 5 + threadIdx.x; - - const float dw = c_DW[yIndex * PATCH_SZ + xIndex]; + if (s > 1) + { + AreaFilter filter(win, s, s); + s_PATCH[yLoadInd][xLoadInd] = filter(yLoadInd, xLoadInd); + } + else + { + LinearFilter filter(win); + s_PATCH[yLoadInd][xLoadInd] = filter(yLoadInd * s, xLoadInd * s); + } + } - const float vx = (s_PATCH[threadIdx.y ][threadIdx.x + 1] - s_PATCH[threadIdx.y][threadIdx.x] + s_PATCH[threadIdx.y + 1][threadIdx.x + 1] - s_PATCH[threadIdx.y + 1][threadIdx.x ]) * dw; - const float vy = (s_PATCH[threadIdx.y + 1][threadIdx.x ] - s_PATCH[threadIdx.y][threadIdx.x] + s_PATCH[threadIdx.y + 1][threadIdx.x + 1] - s_PATCH[threadIdx.y ][threadIdx.x + 1]) * dw; + __syncthreads(); - s_dx_bin[tid] = vx; - s_dy_bin[tid] = vy; - } - } + const int xPatchInd = threadIdx.x % 5; + const int yPatchInd = threadIdx.x / 5; - __device__ void reduce_sum25(volatile float* sdata1, volatile float* sdata2, volatile float* sdata3, volatile float* sdata4, int tid) - { - // first step is to reduce from 25 to 16 - if (tid < 9) // use 9 threads + if (yPatchInd < 5) { - sdata1[tid] += sdata1[tid + 16]; - sdata2[tid] += sdata2[tid + 16]; - sdata3[tid] += sdata3[tid + 16]; - sdata4[tid] += sdata4[tid + 16]; - } + const int xBlockInd = threadIdx.y % 4; + const int yBlockInd = threadIdx.y / 4; - // sum (reduce) from 16 to 1 (unrolled - aligned to a half-warp) - if (tid < 8) - { - sdata1[tid] += sdata1[tid + 8]; - sdata1[tid] += sdata1[tid + 4]; - sdata1[tid] += sdata1[tid + 2]; - sdata1[tid] += sdata1[tid + 1]; - - sdata2[tid] += sdata2[tid + 8]; - sdata2[tid] += sdata2[tid + 4]; - sdata2[tid] += sdata2[tid + 2]; - sdata2[tid] += sdata2[tid + 1]; - - sdata3[tid] += sdata3[tid + 8]; - sdata3[tid] += sdata3[tid + 4]; - sdata3[tid] += sdata3[tid + 2]; - sdata3[tid] += sdata3[tid + 1]; - - sdata4[tid] += sdata4[tid + 8]; - sdata4[tid] += sdata4[tid + 4]; - sdata4[tid] += sdata4[tid + 2]; - sdata4[tid] += sdata4[tid + 1]; + const int xInd = xBlockInd * 5 + xPatchInd; + const int yInd = yBlockInd * 5 + yPatchInd; + + const float dw = c_DW[yInd * PATCH_SZ + xInd]; + + dx = (s_PATCH[yInd ][xInd + 1] - s_PATCH[yInd][xInd] + s_PATCH[yInd + 1][xInd + 1] - s_PATCH[yInd + 1][xInd ]) * dw; + dy = (s_PATCH[yInd + 1][xInd ] - s_PATCH[yInd][xInd] + s_PATCH[yInd + 1][xInd + 1] - s_PATCH[yInd ][xInd + 1]) * dw; } } - __global__ void compute_descriptors64(PtrStepf descriptors, const float* featureX, const float* featureY, const float* featureSize, const float* featureDir) + __global__ void compute_descriptors_64(PtrStep descriptors, const float* featureX, const float* featureY, const float* featureSize, const float* featureDir) { - // 2 floats (dx,dy) for each thread (5x5 sample points in each sub-region) - __shared__ float sdx[25]; - __shared__ float sdy[25]; - __shared__ float sdxabs[25]; - __shared__ float sdyabs[25]; + __shared__ float smem[32 * 16]; - calc_dx_dy(sdx, sdy, featureX, featureY, featureSize, featureDir); - __syncthreads(); + float* sRow = smem + threadIdx.y * 32; + float dx, dy; + calc_dx_dy(featureX, featureY, featureSize, featureDir, dx, dy); - const int tid = threadIdx.y * blockDim.x + threadIdx.x; + float dxabs = ::fabsf(dx); + float dyabs = ::fabsf(dy); - if (tid < 25) - { - sdxabs[tid] = ::fabs(sdx[tid]); // |dx| array - sdyabs[tid] = ::fabs(sdy[tid]); // |dy| array - __syncthreads(); + plus op; - reduce_sum25(sdx, sdy, sdxabs, sdyabs, tid); - __syncthreads(); + reduce<32>(sRow, dx, threadIdx.x, op); + reduce<32>(sRow, dy, threadIdx.x, op); + reduce<32>(sRow, dxabs, threadIdx.x, op); + reduce<32>(sRow, dyabs, threadIdx.x, op); - float* descriptors_block = descriptors.ptr(blockIdx.x) + (blockIdx.y << 2); + float4* descriptors_block = descriptors.ptr(blockIdx.x) + threadIdx.y; - // write dx, dy, |dx|, |dy| - if (tid == 0) - { - descriptors_block[0] = sdx[0]; - descriptors_block[1] = sdy[0]; - descriptors_block[2] = sdxabs[0]; - descriptors_block[3] = sdyabs[0]; - } - } + // write dx, dy, |dx|, |dy| + if (threadIdx.x == 0) + *descriptors_block = make_float4(dx, dy, dxabs, dyabs); } - __global__ void compute_descriptors128(PtrStepf descriptors, const float* featureX, const float* featureY, const float* featureSize, const float* featureDir) + __global__ void compute_descriptors_128(PtrStep descriptors, const float* featureX, const float* featureY, const float* featureSize, const float* featureDir) { - // 2 floats (dx,dy) for each thread (5x5 sample points in each sub-region) - __shared__ float sdx[25]; - __shared__ float sdy[25]; + __shared__ float smem[32 * 16]; - // sum (reduce) 5x5 area response - __shared__ float sd1[25]; - __shared__ float sd2[25]; - __shared__ float sdabs1[25]; - __shared__ float sdabs2[25]; + float* sRow = smem + threadIdx.y * 32; - calc_dx_dy(sdx, sdy, featureX, featureY, featureSize, featureDir); - __syncthreads(); + float dx, dy; + calc_dx_dy(featureX, featureY, featureSize, featureDir, dx, dy); - const int tid = threadIdx.y * blockDim.x + threadIdx.x; + float4* descriptors_block = descriptors.ptr(blockIdx.x) + threadIdx.y * 2; - if (tid < 25) - { - if (sdy[tid] >= 0) - { - sd1[tid] = sdx[tid]; - sdabs1[tid] = ::fabs(sdx[tid]); - sd2[tid] = 0; - sdabs2[tid] = 0; - } - else - { - sd1[tid] = 0; - sdabs1[tid] = 0; - sd2[tid] = sdx[tid]; - sdabs2[tid] = ::fabs(sdx[tid]); - } - __syncthreads(); + plus op; - reduce_sum25(sd1, sd2, sdabs1, sdabs2, tid); - __syncthreads(); + float d1 = 0.0f; + float d2 = 0.0f; + float abs1 = 0.0f; + float abs2 = 0.0f; - float* descriptors_block = descriptors.ptr(blockIdx.x) + (blockIdx.y << 3); - - // write dx (dy >= 0), |dx| (dy >= 0), dx (dy < 0), |dx| (dy < 0) - if (tid == 0) - { - descriptors_block[0] = sd1[0]; - descriptors_block[1] = sdabs1[0]; - descriptors_block[2] = sd2[0]; - descriptors_block[3] = sdabs2[0]; - } - __syncthreads(); + if (dy >= 0) + { + d1 = dx; + abs1 = ::fabsf(dx); + } + else + { + d2 = dx; + abs2 = ::fabsf(dx); + } - if (sdx[tid] >= 0) - { - sd1[tid] = sdy[tid]; - sdabs1[tid] = ::fabs(sdy[tid]); - sd2[tid] = 0; - sdabs2[tid] = 0; - } - else - { - sd1[tid] = 0; - sdabs1[tid] = 0; - sd2[tid] = sdy[tid]; - sdabs2[tid] = ::fabs(sdy[tid]); - } - __syncthreads(); + reduce<32>(sRow, d1, threadIdx.x, op); + reduce<32>(sRow, d2, threadIdx.x, op); + reduce<32>(sRow, abs1, threadIdx.x, op); + reduce<32>(sRow, abs2, threadIdx.x, op); - reduce_sum25(sd1, sd2, sdabs1, sdabs2, tid); - __syncthreads(); + // write dx (dy >= 0), |dx| (dy >= 0), dx (dy < 0), |dx| (dy < 0) + if (threadIdx.x == 0) + descriptors_block[0] = make_float4(d1, abs1, d2, abs2); - // write dy (dx >= 0), |dy| (dx >= 0), dy (dx < 0), |dy| (dx < 0) - if (tid == 0) - { - descriptors_block[4] = sd1[0]; - descriptors_block[5] = sdabs1[0]; - descriptors_block[6] = sd2[0]; - descriptors_block[7] = sdabs2[0]; - } + if (dx >= 0) + { + d1 = dy; + abs1 = ::fabsf(dy); + d2 = 0.0f; + abs2 = 0.0f; } + else + { + d1 = 0.0f; + abs1 = 0.0f; + d2 = dy; + abs2 = ::fabsf(dy); + } + + reduce<32>(sRow, d1, threadIdx.x, op); + reduce<32>(sRow, d2, threadIdx.x, op); + reduce<32>(sRow, abs1, threadIdx.x, op); + reduce<32>(sRow, abs2, threadIdx.x, op); + + // write dy (dx >= 0), |dy| (dx >= 0), dy (dx < 0), |dy| (dx < 0) + if (threadIdx.x == 0) + descriptors_block[1] = make_float4(d1, abs1, d2, abs2); } template __global__ void normalize_descriptors(PtrStepf descriptors) { + __shared__ float smem[BLOCK_DIM_X]; + __shared__ float s_len; + // no need for thread ID float* descriptor_base = descriptors.ptr(blockIdx.x); // read in the unnormalized descriptor values (squared) - __shared__ float sqDesc[BLOCK_DIM_X]; - const float lookup = descriptor_base[threadIdx.x]; - sqDesc[threadIdx.x] = lookup * lookup; - __syncthreads(); - - if (BLOCK_DIM_X >= 128) - { - if (threadIdx.x < 64) - sqDesc[threadIdx.x] += sqDesc[threadIdx.x + 64]; - __syncthreads(); - } + const float val = descriptor_base[threadIdx.x]; - // reduction to get total - if (threadIdx.x < 32) - { - volatile float* smem = sqDesc; - - smem[threadIdx.x] += smem[threadIdx.x + 32]; - smem[threadIdx.x] += smem[threadIdx.x + 16]; - smem[threadIdx.x] += smem[threadIdx.x + 8]; - smem[threadIdx.x] += smem[threadIdx.x + 4]; - smem[threadIdx.x] += smem[threadIdx.x + 2]; - smem[threadIdx.x] += smem[threadIdx.x + 1]; - } + float len = val * val; + reduce(smem, len, threadIdx.x, plus()); - // compute length (square root) - __shared__ float len; if (threadIdx.x == 0) - { - len = sqrtf(sqDesc[0]); - } + s_len = ::sqrtf(len); + __syncthreads(); // normalize and store in output - descriptor_base[threadIdx.x] = lookup / len; + descriptor_base[threadIdx.x] = val / s_len; } - void compute_descriptors_gpu(const PtrStepSzf& descriptors, - const float* featureX, const float* featureY, const float* featureSize, const float* featureDir, int nFeatures) + void compute_descriptors_gpu(PtrStepSz descriptors, const float* featureX, const float* featureY, const float* featureSize, const float* featureDir, int nFeatures) { // compute unnormalized descriptors, then normalize them - odd indexing since grid must be 2D if (descriptors.cols == 64) { - compute_descriptors64<<>>(descriptors, featureX, featureY, featureSize, featureDir); + compute_descriptors_64<<>>(descriptors, featureX, featureY, featureSize, featureDir); cudaSafeCall( cudaGetLastError() ); cudaSafeCall( cudaDeviceSynchronize() ); - normalize_descriptors<64><<>>(descriptors); + normalize_descriptors<64><<>>((PtrStepSzf) descriptors); cudaSafeCall( cudaGetLastError() ); cudaSafeCall( cudaDeviceSynchronize() ); } else { - compute_descriptors128<<>>(descriptors, featureX, featureY, featureSize, featureDir); + compute_descriptors_128<<>>(descriptors, featureX, featureY, featureSize, featureDir); cudaSafeCall( cudaGetLastError() ); cudaSafeCall( cudaDeviceSynchronize() ); - normalize_descriptors<128><<>>(descriptors); + normalize_descriptors<128><<>>((PtrStepSzf) descriptors); cudaSafeCall( cudaGetLastError() ); cudaSafeCall( cudaDeviceSynchronize() ); diff --git a/modules/gpu/src/surf.cpp b/modules/gpu/src/surf.cpp index 72bb9c15e0..4d1e74d9a6 100644 --- a/modules/gpu/src/surf.cpp +++ b/modules/gpu/src/surf.cpp @@ -86,8 +86,7 @@ namespace cv { namespace gpu { namespace device void icvCalcOrientation_gpu(const float* featureX, const float* featureY, const float* featureSize, float* featureDir, int nFeatures); - void compute_descriptors_gpu(const PtrStepSzf& descriptors, - const float* featureX, const float* featureY, const float* featureSize, const float* featureDir, int nFeatures); + void compute_descriptors_gpu(PtrStepSz descriptors, const float* featureX, const float* featureY, const float* featureSize, const float* featureDir, int nFeatures); } }}} diff --git a/modules/gpu/test/test_features2d.cpp b/modules/gpu/test/test_features2d.cpp index 4fff37f1a4..c76fe91663 100644 --- a/modules/gpu/test/test_features2d.cpp +++ b/modules/gpu/test/test_features2d.cpp @@ -328,7 +328,7 @@ TEST_P(SURF, Descriptor) int matchedCount = getMatchedPointsCount(keypoints, keypoints, matches); double matchedRatio = static_cast(matchedCount) / keypoints.size(); - EXPECT_GT(matchedRatio, 0.35); + EXPECT_GT(matchedRatio, 0.6); } } From e8f9762ef373be508692d2d692d5c5a97ad533f9 Mon Sep 17 00:00:00 2001 From: Vladislav Vinogradov Date: Mon, 12 Nov 2012 13:34:25 +0400 Subject: [PATCH 14/23] matrix reduction --- modules/gpu/src/cuda/matrix_reductions.cu | 2738 ++++++++------------- modules/gpu/src/matrix_reductions.cpp | 638 ++--- modules/gpu/test/test_core.cpp | 14 +- 3 files changed, 1221 insertions(+), 2169 deletions(-) diff --git a/modules/gpu/src/cuda/matrix_reductions.cu b/modules/gpu/src/cuda/matrix_reductions.cu index 5cda3dab39..7a0e8d2fed 100644 --- a/modules/gpu/src/cuda/matrix_reductions.cu +++ b/modules/gpu/src/cuda/matrix_reductions.cu @@ -42,2062 +42,1268 @@ #if !defined CUDA_DISABLER -#include "internal_shared.hpp" +#include "opencv2/gpu/device/common.hpp" #include "opencv2/gpu/device/limits.hpp" #include "opencv2/gpu/device/saturate_cast.hpp" +#include "opencv2/gpu/device/vec_traits.hpp" #include "opencv2/gpu/device/vec_math.hpp" +#include "opencv2/gpu/device/reduce.hpp" +#include "opencv2/gpu/device/functional.hpp" +#include "opencv2/gpu/device/utility.hpp" +#include "opencv2/gpu/device/type_traits.hpp" -namespace cv { namespace gpu { namespace device +using namespace cv::gpu; +using namespace cv::gpu::device; + +namespace { - namespace matrix_reductions + template struct Unroll; + template <> struct Unroll<1> { - // Performs reduction in shared memory - template - __device__ void sumInSmem(volatile T* data, const uint tid) + template + static __device__ __forceinline__ volatile R* smem_tuple(R* smem) { - T sum = data[tid]; - - if (size >= 512) { if (tid < 256) { data[tid] = sum = sum + data[tid + 256]; } __syncthreads(); } - if (size >= 256) { if (tid < 128) { data[tid] = sum = sum + data[tid + 128]; } __syncthreads(); } - if (size >= 128) { if (tid < 64) { data[tid] = sum = sum + data[tid + 64]; } __syncthreads(); } + return smem; + } - if (tid < 32) - { - if (size >= 64) data[tid] = sum = sum + data[tid + 32]; - if (size >= 32) data[tid] = sum = sum + data[tid + 16]; - if (size >= 16) data[tid] = sum = sum + data[tid + 8]; - if (size >= 8) data[tid] = sum = sum + data[tid + 4]; - if (size >= 4) data[tid] = sum = sum + data[tid + 2]; - if (size >= 2) data[tid] = sum = sum + data[tid + 1]; - } + template + static __device__ __forceinline__ R& tie(R& val) + { + return val; } - struct Mask8U + template + static __device__ __forceinline__ const Op& op(const Op& op) + { + return op; + } + }; + template <> struct Unroll<2> + { + template + static __device__ __forceinline__ thrust::tuple smem_tuple(R* smem) { - explicit Mask8U(PtrStepb mask_): mask(mask_) {} + return cv::gpu::device::smem_tuple(smem, smem + BLOCK_SIZE); + } - __device__ __forceinline__ bool operator()(int y, int x) const - { - return mask.ptr(y)[x]; - } + template + static __device__ __forceinline__ thrust::tuple::elem_type&, typename VecTraits::elem_type&> tie(R& val) + { + return thrust::tie(val.x, val.y); + } - PtrStepb mask; - }; + template + static __device__ __forceinline__ const thrust::tuple op(const Op& op) + { + return thrust::make_tuple(op, op); + } + }; + template <> struct Unroll<3> + { + template + static __device__ __forceinline__ thrust::tuple smem_tuple(R* smem) + { + return cv::gpu::device::smem_tuple(smem, smem + BLOCK_SIZE, smem + 2 * BLOCK_SIZE); + } - struct MaskTrue + template + static __device__ __forceinline__ thrust::tuple::elem_type&, typename VecTraits::elem_type&, typename VecTraits::elem_type&> tie(R& val) { - __device__ __forceinline__ bool operator()(int y, int x) const - { - return true; - } - __device__ __forceinline__ MaskTrue(){} - __device__ __forceinline__ MaskTrue(const MaskTrue& mask_){} - }; + return thrust::tie(val.x, val.y, val.z); + } - ////////////////////////////////////////////////////////////////////////////// - // Min max - - // To avoid shared bank conflicts we convert each value into value of - // appropriate type (32 bits minimum) - template struct MinMaxTypeTraits {}; - template <> struct MinMaxTypeTraits { typedef int best_type; }; - template <> struct MinMaxTypeTraits { typedef int best_type; }; - template <> struct MinMaxTypeTraits { typedef int best_type; }; - template <> struct MinMaxTypeTraits { typedef int best_type; }; - template <> struct MinMaxTypeTraits { typedef int best_type; }; - template <> struct MinMaxTypeTraits { typedef float best_type; }; - template <> struct MinMaxTypeTraits { typedef double best_type; }; - - namespace minmax + template + static __device__ __forceinline__ const thrust::tuple op(const Op& op) { - __constant__ int ctwidth; - __constant__ int ctheight; + return thrust::make_tuple(op, op, op); + } + }; + template <> struct Unroll<4> + { + template + static __device__ __forceinline__ thrust::tuple smem_tuple(R* smem) + { + return cv::gpu::device::smem_tuple(smem, smem + BLOCK_SIZE, smem + 2 * BLOCK_SIZE, smem + 3 * BLOCK_SIZE); + } - // Global counter of blocks finished its work - __device__ uint blocks_finished = 0; + template + static __device__ __forceinline__ thrust::tuple::elem_type&, typename VecTraits::elem_type&, typename VecTraits::elem_type&, typename VecTraits::elem_type&> tie(R& val) + { + return thrust::tie(val.x, val.y, val.z, val.w); + } + template + static __device__ __forceinline__ const thrust::tuple op(const Op& op) + { + return thrust::make_tuple(op, op, op, op); + } + }; +} - // Estimates good thread configuration - // - threads variable satisfies to threads.x * threads.y == 256 - void estimateThreadCfg(int cols, int rows, dim3& threads, dim3& grid) - { - threads = dim3(32, 8); - grid = dim3(divUp(cols, threads.x * 8), divUp(rows, threads.y * 32)); - grid.x = std::min(grid.x, threads.x); - grid.y = std::min(grid.y, threads.y); - } +///////////////////////////////////////////////////////////// +// sum +namespace sum +{ + __device__ unsigned int blocks_finished = 0; - // Returns required buffer sizes - void getBufSizeRequired(int cols, int rows, int elem_size, int& bufcols, int& bufrows) - { - dim3 threads, grid; - estimateThreadCfg(cols, rows, threads, grid); - bufcols = grid.x * grid.y * elem_size; - bufrows = 2; - } + template struct AtomicAdd; + template struct AtomicAdd + { + static __device__ void run(R* ptr, R val) + { + ::atomicAdd(ptr, val); + } + }; + template struct AtomicAdd + { + typedef typename TypeVec::vec_type val_type; + static __device__ void run(R* ptr, val_type val) + { + ::atomicAdd(ptr, val.x); + ::atomicAdd(ptr + 1, val.y); + } + }; + template struct AtomicAdd + { + typedef typename TypeVec::vec_type val_type; - // Estimates device constants which are used in the kernels using specified thread configuration - void setKernelConsts(int cols, int rows, const dim3& threads, const dim3& grid) - { - int twidth = divUp(divUp(cols, grid.x), threads.x); - int theight = divUp(divUp(rows, grid.y), threads.y); - cudaSafeCall(cudaMemcpyToSymbol(ctwidth, &twidth, sizeof(ctwidth))); - cudaSafeCall(cudaMemcpyToSymbol(ctheight, &theight, sizeof(ctheight))); - } + static __device__ void run(R* ptr, val_type val) + { + ::atomicAdd(ptr, val.x); + ::atomicAdd(ptr + 1, val.y); + ::atomicAdd(ptr + 2, val.z); + } + }; + template struct AtomicAdd + { + typedef typename TypeVec::vec_type val_type; + static __device__ void run(R* ptr, val_type val) + { + ::atomicAdd(ptr, val.x); + ::atomicAdd(ptr + 1, val.y); + ::atomicAdd(ptr + 2, val.z); + ::atomicAdd(ptr + 3, val.w); + } + }; - // Does min and max in shared memory - template - __device__ __forceinline__ void merge(uint tid, uint offset, volatile T* minval, volatile T* maxval) - { - minval[tid] = ::min(minval[tid], minval[tid + offset]); - maxval[tid] = ::max(maxval[tid], maxval[tid + offset]); - } + template + struct GlobalReduce + { + typedef typename TypeVec::vec_type result_type; + static __device__ void run(result_type& sum, result_type* result, int tid, int bid, R* smem) + { + #if __CUDA_ARCH__ >= 200 + if (tid == 0) + AtomicAdd::run((R*) result, sum); + #else + __shared__ bool is_last; - template - __device__ void findMinMaxInSmem(volatile T* minval, volatile T* maxval, const uint tid) + if (tid == 0) { - if (size >= 512) { if (tid < 256) { merge(tid, 256, minval, maxval); } __syncthreads(); } - if (size >= 256) { if (tid < 128) { merge(tid, 128, minval, maxval); } __syncthreads(); } - if (size >= 128) { if (tid < 64) { merge(tid, 64, minval, maxval); } __syncthreads(); } + result[bid] = sum; - if (tid < 32) - { - if (size >= 64) merge(tid, 32, minval, maxval); - if (size >= 32) merge(tid, 16, minval, maxval); - if (size >= 16) merge(tid, 8, minval, maxval); - if (size >= 8) merge(tid, 4, minval, maxval); - if (size >= 4) merge(tid, 2, minval, maxval); - if (size >= 2) merge(tid, 1, minval, maxval); - } + __threadfence(); + + unsigned int ticket = ::atomicAdd(&blocks_finished, 1); + is_last = (ticket == gridDim.x * gridDim.y - 1); } + __syncthreads(); - template - __global__ void minMaxKernel(const PtrStepSzb src, Mask mask, T* minval, T* maxval) + if (is_last) { - typedef typename MinMaxTypeTraits::best_type best_type; - __shared__ best_type sminval[nthreads]; - __shared__ best_type smaxval[nthreads]; - - uint x0 = blockIdx.x * blockDim.x * ctwidth + threadIdx.x; - uint y0 = blockIdx.y * blockDim.y * ctheight + threadIdx.y; - uint tid = threadIdx.y * blockDim.x + threadIdx.x; - - T mymin = numeric_limits::max(); - T mymax = numeric_limits::is_signed ? -numeric_limits::max() : numeric_limits::min(); - uint y_end = ::min(y0 + (ctheight - 1) * blockDim.y + 1, src.rows); - uint x_end = ::min(x0 + (ctwidth - 1) * blockDim.x + 1, src.cols); - for (uint y = y0; y < y_end; y += blockDim.y) - { - const T* src_row = (const T*)src.ptr(y); - for (uint x = x0; x < x_end; x += blockDim.x) - { - T val = src_row[x]; - if (mask(y, x)) - { - mymin = ::min(mymin, val); - mymax = ::max(mymax, val); - } - } - } - - sminval[tid] = mymin; - smaxval[tid] = mymax; - __syncthreads(); + sum = tid < gridDim.x * gridDim.y ? result[tid] : VecTraits::all(0); - findMinMaxInSmem(sminval, smaxval, tid); + device::reduce(Unroll::template smem_tuple(smem), Unroll::tie(sum), tid, Unroll::op(plus())); if (tid == 0) { - minval[blockIdx.y * gridDim.x + blockIdx.x] = (T)sminval[0]; - maxval[blockIdx.y * gridDim.x + blockIdx.x] = (T)smaxval[0]; + result[0] = sum; + blocks_finished = 0; } - - #if defined (__CUDA_ARCH__) && (__CUDA_ARCH__ >= 110) - __shared__ bool is_last; - - if (tid == 0) - { - minval[blockIdx.y * gridDim.x + blockIdx.x] = (T)sminval[0]; - maxval[blockIdx.y * gridDim.x + blockIdx.x] = (T)smaxval[0]; - __threadfence(); - - uint ticket = atomicInc(&blocks_finished, gridDim.x * gridDim.y); - is_last = ticket == gridDim.x * gridDim.y - 1; - } - - __syncthreads(); - - if (is_last) - { - uint idx = ::min(tid, gridDim.x * gridDim.y - 1); - - sminval[tid] = minval[idx]; - smaxval[tid] = maxval[idx]; - __syncthreads(); - - findMinMaxInSmem(sminval, smaxval, tid); - - if (tid == 0) - { - minval[0] = (T)sminval[0]; - maxval[0] = (T)smaxval[0]; - blocks_finished = 0; - } - } - #else - if (tid == 0) - { - minval[blockIdx.y * gridDim.x + blockIdx.x] = (T)sminval[0]; - maxval[blockIdx.y * gridDim.x + blockIdx.x] = (T)smaxval[0]; - } - #endif - } - - - template - void minMaxMaskCaller(const PtrStepSzb src, const PtrStepb mask, double* minval, double* maxval, PtrStepb buf) - { - dim3 threads, grid; - estimateThreadCfg(src.cols, src.rows, threads, grid); - setKernelConsts(src.cols, src.rows, threads, grid); - - T* minval_buf = (T*)buf.ptr(0); - T* maxval_buf = (T*)buf.ptr(1); - - minMaxKernel<256, T, Mask8U><<>>(src, Mask8U(mask), minval_buf, maxval_buf); - cudaSafeCall( cudaGetLastError() ); - - cudaSafeCall( cudaDeviceSynchronize() ); - - T minval_, maxval_; - cudaSafeCall( cudaMemcpy(&minval_, minval_buf, sizeof(T), cudaMemcpyDeviceToHost) ); - cudaSafeCall( cudaMemcpy(&maxval_, maxval_buf, sizeof(T), cudaMemcpyDeviceToHost) ); - *minval = minval_; - *maxval = maxval_; } + #endif + } + }; + template + struct GlobalReduce + { + typedef typename TypeVec::vec_type result_type; - template void minMaxMaskCaller(const PtrStepSzb, const PtrStepb, double*, double*, PtrStepb); - template void minMaxMaskCaller(const PtrStepSzb, const PtrStepb, double*, double*, PtrStepb); - template void minMaxMaskCaller(const PtrStepSzb, const PtrStepb, double*, double*, PtrStepb); - template void minMaxMaskCaller(const PtrStepSzb, const PtrStepb, double*, double*, PtrStepb); - template void minMaxMaskCaller(const PtrStepSzb, const PtrStepb, double*, double*, PtrStepb); - template void minMaxMaskCaller(const PtrStepSzb, const PtrStepb, double*, double*, PtrStepb); - template void minMaxMaskCaller(const PtrStepSzb, const PtrStepb, double*, double*, PtrStepb); - + static __device__ void run(result_type& sum, result_type* result, int tid, int bid, double* smem) + { + __shared__ bool is_last; - template - void minMaxCaller(const PtrStepSzb src, double* minval, double* maxval, PtrStepb buf) + if (tid == 0) { - dim3 threads, grid; - estimateThreadCfg(src.cols, src.rows, threads, grid); - setKernelConsts(src.cols, src.rows, threads, grid); - - T* minval_buf = (T*)buf.ptr(0); - T* maxval_buf = (T*)buf.ptr(1); - - minMaxKernel<256, T, MaskTrue><<>>(src, MaskTrue(), minval_buf, maxval_buf); - cudaSafeCall( cudaGetLastError() ); + result[bid] = sum; - cudaSafeCall( cudaDeviceSynchronize() ); + __threadfence(); - T minval_, maxval_; - cudaSafeCall( cudaMemcpy(&minval_, minval_buf, sizeof(T), cudaMemcpyDeviceToHost) ); - cudaSafeCall( cudaMemcpy(&maxval_, maxval_buf, sizeof(T), cudaMemcpyDeviceToHost) ); - *minval = minval_; - *maxval = maxval_; + unsigned int ticket = ::atomicAdd(&blocks_finished, 1); + is_last = (ticket == gridDim.x * gridDim.y - 1); } - template void minMaxCaller(const PtrStepSzb, double*, double*, PtrStepb); - template void minMaxCaller(const PtrStepSzb, double*, double*, PtrStepb); - template void minMaxCaller(const PtrStepSzb, double*, double*, PtrStepb); - template void minMaxCaller(const PtrStepSzb, double*, double*, PtrStepb); - template void minMaxCaller(const PtrStepSzb, double*, double*, PtrStepb); - template void minMaxCaller(const PtrStepSzb, double*,double*, PtrStepb); - template void minMaxCaller(const PtrStepSzb, double*, double*, PtrStepb); - + __syncthreads(); - template - __global__ void minMaxPass2Kernel(T* minval, T* maxval, int size) + if (is_last) { - typedef typename MinMaxTypeTraits::best_type best_type; - __shared__ best_type sminval[nthreads]; - __shared__ best_type smaxval[nthreads]; - - uint tid = threadIdx.y * blockDim.x + threadIdx.x; - uint idx = ::min(tid, size - 1); - - sminval[tid] = minval[idx]; - smaxval[tid] = maxval[idx]; - __syncthreads(); + sum = tid < gridDim.x * gridDim.y ? result[tid] : VecTraits::all(0); - findMinMaxInSmem(sminval, smaxval, tid); + device::reduce(Unroll::template smem_tuple(smem), Unroll::tie(sum), tid, Unroll::op(plus())); if (tid == 0) { - minval[0] = (T)sminval[0]; - maxval[0] = (T)smaxval[0]; + result[0] = sum; + blocks_finished = 0; } } + } + }; + template + __global__ void kernel(const PtrStepSz src, result_type* result, const Op op, const int twidth, const int theight) + { + typedef typename VecTraits::elem_type T; + typedef typename VecTraits::elem_type R; + const int cn = VecTraits::cn; - template - void minMaxMaskMultipassCaller(const PtrStepSzb src, const PtrStepb mask, double* minval, double* maxval, PtrStepb buf) - { - dim3 threads, grid; - estimateThreadCfg(src.cols, src.rows, threads, grid); - setKernelConsts(src.cols, src.rows, threads, grid); - - T* minval_buf = (T*)buf.ptr(0); - T* maxval_buf = (T*)buf.ptr(1); - - minMaxKernel<256, T, Mask8U><<>>(src, Mask8U(mask), minval_buf, maxval_buf); - cudaSafeCall( cudaGetLastError() ); - minMaxPass2Kernel<256, T><<<1, 256>>>(minval_buf, maxval_buf, grid.x * grid.y); - cudaSafeCall( cudaGetLastError() ); - - cudaSafeCall(cudaDeviceSynchronize()); - - T minval_, maxval_; - cudaSafeCall( cudaMemcpy(&minval_, minval_buf, sizeof(T), cudaMemcpyDeviceToHost) ); - cudaSafeCall( cudaMemcpy(&maxval_, maxval_buf, sizeof(T), cudaMemcpyDeviceToHost) ); - *minval = minval_; - *maxval = maxval_; - } - - template void minMaxMaskMultipassCaller(const PtrStepSzb, const PtrStepb, double*, double*, PtrStepb); - template void minMaxMaskMultipassCaller(const PtrStepSzb, const PtrStepb, double*, double*, PtrStepb); - template void minMaxMaskMultipassCaller(const PtrStepSzb, const PtrStepb, double*, double*, PtrStepb); - template void minMaxMaskMultipassCaller(const PtrStepSzb, const PtrStepb, double*, double*, PtrStepb); - template void minMaxMaskMultipassCaller(const PtrStepSzb, const PtrStepb, double*, double*, PtrStepb); - template void minMaxMaskMultipassCaller(const PtrStepSzb, const PtrStepb, double*, double*, PtrStepb); - - - template - void minMaxMultipassCaller(const PtrStepSzb src, double* minval, double* maxval, PtrStepb buf) - { - dim3 threads, grid; - estimateThreadCfg(src.cols, src.rows, threads, grid); - setKernelConsts(src.cols, src.rows, threads, grid); - - T* minval_buf = (T*)buf.ptr(0); - T* maxval_buf = (T*)buf.ptr(1); - - minMaxKernel<256, T, MaskTrue><<>>(src, MaskTrue(), minval_buf, maxval_buf); - cudaSafeCall( cudaGetLastError() ); - minMaxPass2Kernel<256, T><<<1, 256>>>(minval_buf, maxval_buf, grid.x * grid.y); - cudaSafeCall( cudaGetLastError() ); - - cudaSafeCall( cudaDeviceSynchronize() ); + __shared__ R smem[BLOCK_SIZE * cn]; - T minval_, maxval_; - cudaSafeCall( cudaMemcpy(&minval_, minval_buf, sizeof(T), cudaMemcpyDeviceToHost) ); - cudaSafeCall( cudaMemcpy(&maxval_, maxval_buf, sizeof(T), cudaMemcpyDeviceToHost) ); - *minval = minval_; - *maxval = maxval_; - } + const int x0 = blockIdx.x * blockDim.x * twidth + threadIdx.x; + const int y0 = blockIdx.y * blockDim.y * theight + threadIdx.y; - template void minMaxMultipassCaller(const PtrStepSzb, double*, double*, PtrStepb); - template void minMaxMultipassCaller(const PtrStepSzb, double*, double*, PtrStepb); - template void minMaxMultipassCaller(const PtrStepSzb, double*, double*, PtrStepb); - template void minMaxMultipassCaller(const PtrStepSzb, double*, double*, PtrStepb); - template void minMaxMultipassCaller(const PtrStepSzb, double*, double*, PtrStepb); - template void minMaxMultipassCaller(const PtrStepSzb, double*, double*, PtrStepb); - } // namespace minmax + const int tid = threadIdx.y * blockDim.x + threadIdx.x; + const int bid = blockIdx.y * gridDim.x + blockIdx.x; - /////////////////////////////////////////////////////////////////////////////// - // minMaxLoc + result_type sum = VecTraits::all(0); - namespace minmaxloc + for (int i = 0, y = y0; i < theight && y < src.rows; ++i, y += blockDim.y) { - __constant__ int ctwidth; - __constant__ int ctheight; + const src_type* ptr = src.ptr(y); - // Global counter of blocks finished its work - __device__ uint blocks_finished = 0; - - - // Estimates good thread configuration - // - threads variable satisfies to threads.x * threads.y == 256 - void estimateThreadCfg(int cols, int rows, dim3& threads, dim3& grid) + for (int j = 0, x = x0; j < twidth && x < src.cols; ++j, x += blockDim.x) { - threads = dim3(32, 8); - grid = dim3(divUp(cols, threads.x * 8), divUp(rows, threads.y * 32)); - grid.x = std::min(grid.x, threads.x); - grid.y = std::min(grid.y, threads.y); - } - + const src_type srcVal = ptr[x]; - // Returns required buffer sizes - void getBufSizeRequired(int cols, int rows, int elem_size, int& b1cols, - int& b1rows, int& b2cols, int& b2rows) - { - dim3 threads, grid; - estimateThreadCfg(cols, rows, threads, grid); - b1cols = grid.x * grid.y * elem_size; // For values - b1rows = 2; - b2cols = grid.x * grid.y * sizeof(int); // For locations - b2rows = 2; + sum = sum + op(saturate_cast(srcVal)); } + } + device::reduce(Unroll::template smem_tuple(smem), Unroll::tie(sum), tid, Unroll::op(plus())); - // Estimates device constants which are used in the kernels using specified thread configuration - void setKernelConsts(int cols, int rows, const dim3& threads, const dim3& grid) - { - int twidth = divUp(divUp(cols, grid.x), threads.x); - int theight = divUp(divUp(rows, grid.y), threads.y); - cudaSafeCall(cudaMemcpyToSymbol(ctwidth, &twidth, sizeof(ctwidth))); - cudaSafeCall(cudaMemcpyToSymbol(ctheight, &theight, sizeof(ctheight))); - } + GlobalReduce::run(sum, result, tid, bid, smem); + } + const int threads_x = 32; + const int threads_y = 8; - template - __device__ void merge(uint tid, uint offset, volatile T* minval, volatile T* maxval, - volatile uint* minloc, volatile uint* maxloc) - { - T val = minval[tid + offset]; - if (val < minval[tid]) - { - minval[tid] = val; - minloc[tid] = minloc[tid + offset]; - } - val = maxval[tid + offset]; - if (val > maxval[tid]) - { - maxval[tid] = val; - maxloc[tid] = maxloc[tid + offset]; - } - } + void getLaunchCfg(int cols, int rows, dim3& block, dim3& grid) + { + block = dim3(threads_x, threads_y); + grid = dim3(divUp(cols, block.x * block.y), + divUp(rows, block.y * block.x)); - template - __device__ void findMinMaxLocInSmem(volatile T* minval, volatile T* maxval, volatile uint* minloc, - volatile uint* maxloc, const uint tid) - { - if (size >= 512) { if (tid < 256) { merge(tid, 256, minval, maxval, minloc, maxloc); } __syncthreads(); } - if (size >= 256) { if (tid < 128) { merge(tid, 128, minval, maxval, minloc, maxloc); } __syncthreads(); } - if (size >= 128) { if (tid < 64) { merge(tid, 64, minval, maxval, minloc, maxloc); } __syncthreads(); } + grid.x = ::min(grid.x, block.x); + grid.y = ::min(grid.y, block.y); + } - if (tid < 32) - { - if (size >= 64) merge(tid, 32, minval, maxval, minloc, maxloc); - if (size >= 32) merge(tid, 16, minval, maxval, minloc, maxloc); - if (size >= 16) merge(tid, 8, minval, maxval, minloc, maxloc); - if (size >= 8) merge(tid, 4, minval, maxval, minloc, maxloc); - if (size >= 4) merge(tid, 2, minval, maxval, minloc, maxloc); - if (size >= 2) merge(tid, 1, minval, maxval, minloc, maxloc); - } - } + void getBufSize(int cols, int rows, int cn, int& bufcols, int& bufrows) + { + dim3 block, grid; + getLaunchCfg(cols, rows, block, grid); + bufcols = grid.x * grid.y * sizeof(double) * cn; + bufrows = 1; + } - template - __global__ void minMaxLocKernel(const PtrStepSzb src, Mask mask, T* minval, T* maxval, - uint* minloc, uint* maxloc) - { - typedef typename MinMaxTypeTraits::best_type best_type; - __shared__ best_type sminval[nthreads]; - __shared__ best_type smaxval[nthreads]; - __shared__ uint sminloc[nthreads]; - __shared__ uint smaxloc[nthreads]; - - uint x0 = blockIdx.x * blockDim.x * ctwidth + threadIdx.x; - uint y0 = blockIdx.y * blockDim.y * ctheight + threadIdx.y; - uint tid = threadIdx.y * blockDim.x + threadIdx.x; - - T mymin = numeric_limits::max(); - T mymax = numeric_limits::is_signed ? -numeric_limits::max() : numeric_limits::min(); - uint myminloc = 0; - uint mymaxloc = 0; - uint y_end = ::min(y0 + (ctheight - 1) * blockDim.y + 1, src.rows); - uint x_end = ::min(x0 + (ctwidth - 1) * blockDim.x + 1, src.cols); - - for (uint y = y0; y < y_end; y += blockDim.y) - { - const T* ptr = (const T*)src.ptr(y); - for (uint x = x0; x < x_end; x += blockDim.x) - { - if (mask(y, x)) - { - T val = ptr[x]; - if (val <= mymin) { mymin = val; myminloc = y * src.cols + x; } - if (val >= mymax) { mymax = val; mymaxloc = y * src.cols + x; } - } - } - } + template class Op> + void caller(PtrStepSzb src_, void* buf_, double* out) + { + typedef typename TypeVec::vec_type src_type; + typedef typename TypeVec::vec_type result_type; - sminval[tid] = mymin; - smaxval[tid] = mymax; - sminloc[tid] = myminloc; - smaxloc[tid] = mymaxloc; - __syncthreads(); + PtrStepSz src(src_); + result_type* buf = (result_type*) buf_; - findMinMaxLocInSmem(sminval, smaxval, sminloc, smaxloc, tid); + dim3 block, grid; + getLaunchCfg(src.cols, src.rows, block, grid); - #if defined (__CUDA_ARCH__) && (__CUDA_ARCH__ >= 110) - __shared__ bool is_last; + const int twidth = divUp(divUp(src.cols, grid.x), block.x); + const int theight = divUp(divUp(src.rows, grid.y), block.y); - if (tid == 0) - { - minval[blockIdx.y * gridDim.x + blockIdx.x] = (T)sminval[0]; - maxval[blockIdx.y * gridDim.x + blockIdx.x] = (T)smaxval[0]; - minloc[blockIdx.y * gridDim.x + blockIdx.x] = sminloc[0]; - maxloc[blockIdx.y * gridDim.x + blockIdx.x] = smaxloc[0]; - __threadfence(); - - uint ticket = atomicInc(&blocks_finished, gridDim.x * gridDim.y); - is_last = ticket == gridDim.x * gridDim.y - 1; - } + Op op; - __syncthreads(); + kernel<<>>(src, buf, op, twidth, theight); + cudaSafeCall( cudaGetLastError() ); - if (is_last) - { - uint idx = ::min(tid, gridDim.x * gridDim.y - 1); + cudaSafeCall( cudaDeviceSynchronize() ); - sminval[tid] = minval[idx]; - smaxval[tid] = maxval[idx]; - sminloc[tid] = minloc[idx]; - smaxloc[tid] = maxloc[idx]; - __syncthreads(); + R result[4] = {0, 0, 0, 0}; + cudaSafeCall( cudaMemcpy(&result, buf, sizeof(result_type), cudaMemcpyDeviceToHost) ); - findMinMaxLocInSmem(sminval, smaxval, sminloc, smaxloc, tid); + out[0] = result[0]; + out[1] = result[1]; + out[2] = result[2]; + out[3] = result[3]; + } - if (tid == 0) - { - minval[0] = (T)sminval[0]; - maxval[0] = (T)smaxval[0]; - minloc[0] = sminloc[0]; - maxloc[0] = smaxloc[0]; - blocks_finished = 0; - } - } - #else - if (tid == 0) - { - minval[blockIdx.y * gridDim.x + blockIdx.x] = (T)sminval[0]; - maxval[blockIdx.y * gridDim.x + blockIdx.x] = (T)smaxval[0]; - minloc[blockIdx.y * gridDim.x + blockIdx.x] = sminloc[0]; - maxloc[blockIdx.y * gridDim.x + blockIdx.x] = smaxloc[0]; - } - #endif - } + template struct SumType; + template <> struct SumType { typedef unsigned int R; }; + template <> struct SumType { typedef int R; }; + template <> struct SumType { typedef unsigned int R; }; + template <> struct SumType { typedef int R; }; + template <> struct SumType { typedef int R; }; + template <> struct SumType { typedef float R; }; + template <> struct SumType { typedef double R; }; + template + void run(PtrStepSzb src, void* buf, double* out) + { + typedef typename SumType::R R; + caller(src, buf, out); + } + + template void run(PtrStepSzb src, void* buf, double* out); + template void run(PtrStepSzb src, void* buf, double* out); + template void run(PtrStepSzb src, void* buf, double* out); + template void run(PtrStepSzb src, void* buf, double* out); + + template void run(PtrStepSzb src, void* buf, double* out); + template void run(PtrStepSzb src, void* buf, double* out); + template void run(PtrStepSzb src, void* buf, double* out); + template void run(PtrStepSzb src, void* buf, double* out); + + template void run(PtrStepSzb src, void* buf, double* out); + template void run(PtrStepSzb src, void* buf, double* out); + template void run(PtrStepSzb src, void* buf, double* out); + template void run(PtrStepSzb src, void* buf, double* out); + + template void run(PtrStepSzb src, void* buf, double* out); + template void run(PtrStepSzb src, void* buf, double* out); + template void run(PtrStepSzb src, void* buf, double* out); + template void run(PtrStepSzb src, void* buf, double* out); + + template void run(PtrStepSzb src, void* buf, double* out); + template void run(PtrStepSzb src, void* buf, double* out); + template void run(PtrStepSzb src, void* buf, double* out); + template void run(PtrStepSzb src, void* buf, double* out); + + template void run(PtrStepSzb src, void* buf, double* out); + template void run(PtrStepSzb src, void* buf, double* out); + template void run(PtrStepSzb src, void* buf, double* out); + template void run(PtrStepSzb src, void* buf, double* out); + + template void run(PtrStepSzb src, void* buf, double* out); + template void run(PtrStepSzb src, void* buf, double* out); + template void run(PtrStepSzb src, void* buf, double* out); + template void run(PtrStepSzb src, void* buf, double* out); + + template + void runAbs(PtrStepSzb src, void* buf, double* out) + { + typedef typename SumType::R R; + caller(src, buf, out); + } + + template void runAbs(PtrStepSzb src, void* buf, double* out); + template void runAbs(PtrStepSzb src, void* buf, double* out); + template void runAbs(PtrStepSzb src, void* buf, double* out); + template void runAbs(PtrStepSzb src, void* buf, double* out); + + template void runAbs(PtrStepSzb src, void* buf, double* out); + template void runAbs(PtrStepSzb src, void* buf, double* out); + template void runAbs(PtrStepSzb src, void* buf, double* out); + template void runAbs(PtrStepSzb src, void* buf, double* out); + + template void runAbs(PtrStepSzb src, void* buf, double* out); + template void runAbs(PtrStepSzb src, void* buf, double* out); + template void runAbs(PtrStepSzb src, void* buf, double* out); + template void runAbs(PtrStepSzb src, void* buf, double* out); + + template void runAbs(PtrStepSzb src, void* buf, double* out); + template void runAbs(PtrStepSzb src, void* buf, double* out); + template void runAbs(PtrStepSzb src, void* buf, double* out); + template void runAbs(PtrStepSzb src, void* buf, double* out); + + template void runAbs(PtrStepSzb src, void* buf, double* out); + template void runAbs(PtrStepSzb src, void* buf, double* out); + template void runAbs(PtrStepSzb src, void* buf, double* out); + template void runAbs(PtrStepSzb src, void* buf, double* out); + + template void runAbs(PtrStepSzb src, void* buf, double* out); + template void runAbs(PtrStepSzb src, void* buf, double* out); + template void runAbs(PtrStepSzb src, void* buf, double* out); + template void runAbs(PtrStepSzb src, void* buf, double* out); + + template void runAbs(PtrStepSzb src, void* buf, double* out); + template void runAbs(PtrStepSzb src, void* buf, double* out); + template void runAbs(PtrStepSzb src, void* buf, double* out); + template void runAbs(PtrStepSzb src, void* buf, double* out); + + template struct Sqr : unary_function + { + __device__ __forceinline__ T operator ()(T x) const + { + return x * x; + } + }; - template - void minMaxLocMaskCaller(const PtrStepSzb src, const PtrStepb mask, double* minval, double* maxval, - int minloc[2], int maxloc[2], PtrStepb valbuf, PtrStepb locbuf) - { - dim3 threads, grid; - estimateThreadCfg(src.cols, src.rows, threads, grid); - setKernelConsts(src.cols, src.rows, threads, grid); - - T* minval_buf = (T*)valbuf.ptr(0); - T* maxval_buf = (T*)valbuf.ptr(1); - uint* minloc_buf = (uint*)locbuf.ptr(0); - uint* maxloc_buf = (uint*)locbuf.ptr(1); - - minMaxLocKernel<256, T, Mask8U><<>>(src, Mask8U(mask), minval_buf, maxval_buf, - minloc_buf, maxloc_buf); - cudaSafeCall( cudaGetLastError() ); - - cudaSafeCall( cudaDeviceSynchronize() ); - - T minval_, maxval_; - cudaSafeCall( cudaMemcpy(&minval_, minval_buf, sizeof(T), cudaMemcpyDeviceToHost) ); - cudaSafeCall( cudaMemcpy(&maxval_, maxval_buf, sizeof(T), cudaMemcpyDeviceToHost) ); - *minval = minval_; - *maxval = maxval_; - - uint minloc_, maxloc_; - cudaSafeCall( cudaMemcpy(&minloc_, minloc_buf, sizeof(int), cudaMemcpyDeviceToHost) ); - cudaSafeCall( cudaMemcpy(&maxloc_, maxloc_buf, sizeof(int), cudaMemcpyDeviceToHost) ); - minloc[1] = minloc_ / src.cols; minloc[0] = minloc_ - minloc[1] * src.cols; - maxloc[1] = maxloc_ / src.cols; maxloc[0] = maxloc_ - maxloc[1] * src.cols; - } + template + void runSqr(PtrStepSzb src, void* buf, double* out) + { + caller(src, buf, out); + } + + template void runSqr(PtrStepSzb src, void* buf, double* out); + template void runSqr(PtrStepSzb src, void* buf, double* out); + template void runSqr(PtrStepSzb src, void* buf, double* out); + template void runSqr(PtrStepSzb src, void* buf, double* out); + + template void runSqr(PtrStepSzb src, void* buf, double* out); + template void runSqr(PtrStepSzb src, void* buf, double* out); + template void runSqr(PtrStepSzb src, void* buf, double* out); + template void runSqr(PtrStepSzb src, void* buf, double* out); + + template void runSqr(PtrStepSzb src, void* buf, double* out); + template void runSqr(PtrStepSzb src, void* buf, double* out); + template void runSqr(PtrStepSzb src, void* buf, double* out); + template void runSqr(PtrStepSzb src, void* buf, double* out); + + template void runSqr(PtrStepSzb src, void* buf, double* out); + template void runSqr(PtrStepSzb src, void* buf, double* out); + template void runSqr(PtrStepSzb src, void* buf, double* out); + template void runSqr(PtrStepSzb src, void* buf, double* out); + + template void runSqr(PtrStepSzb src, void* buf, double* out); + template void runSqr(PtrStepSzb src, void* buf, double* out); + template void runSqr(PtrStepSzb src, void* buf, double* out); + template void runSqr(PtrStepSzb src, void* buf, double* out); + + template void runSqr(PtrStepSzb src, void* buf, double* out); + template void runSqr(PtrStepSzb src, void* buf, double* out); + template void runSqr(PtrStepSzb src, void* buf, double* out); + template void runSqr(PtrStepSzb src, void* buf, double* out); + + template void runSqr(PtrStepSzb src, void* buf, double* out); + template void runSqr(PtrStepSzb src, void* buf, double* out); + template void runSqr(PtrStepSzb src, void* buf, double* out); + template void runSqr(PtrStepSzb src, void* buf, double* out); +} + +///////////////////////////////////////////////////////////// +// minMax + +namespace minMax +{ + __device__ unsigned int blocks_finished = 0; + + // To avoid shared bank conflicts we convert each value into value of + // appropriate type (32 bits minimum) + template struct MinMaxTypeTraits; + template <> struct MinMaxTypeTraits { typedef int best_type; }; + template <> struct MinMaxTypeTraits { typedef int best_type; }; + template <> struct MinMaxTypeTraits { typedef int best_type; }; + template <> struct MinMaxTypeTraits { typedef int best_type; }; + template <> struct MinMaxTypeTraits { typedef int best_type; }; + template <> struct MinMaxTypeTraits { typedef float best_type; }; + template <> struct MinMaxTypeTraits { typedef double best_type; }; + + template + struct GlobalReduce + { + static __device__ void run(R& mymin, R& mymax, R* minval, R* maxval, int tid, int bid, R* sminval, R* smaxval) + { + __shared__ bool is_last; - template void minMaxLocMaskCaller(const PtrStepSzb, const PtrStepb, double*, double*, int[2], int[2], PtrStepb, PtrStepb); - template void minMaxLocMaskCaller(const PtrStepSzb, const PtrStepb, double*, double*, int[2], int[2], PtrStepb, PtrStepb); - template void minMaxLocMaskCaller(const PtrStepSzb, const PtrStepb, double*, double*, int[2], int[2], PtrStepb, PtrStepb); - template void minMaxLocMaskCaller(const PtrStepSzb, const PtrStepb, double*, double*, int[2], int[2], PtrStepb, PtrStepb); - template void minMaxLocMaskCaller(const PtrStepSzb, const PtrStepb, double*, double*, int[2], int[2], PtrStepb, PtrStepb); - template void minMaxLocMaskCaller(const PtrStepSzb, const PtrStepb, double*, double*, int[2], int[2], PtrStepb, PtrStepb); - template void minMaxLocMaskCaller(const PtrStepSzb, const PtrStepb, double*, double*, int[2], int[2], PtrStepb, PtrStepb); + if (tid == 0) + { + minval[bid] = mymin; + maxval[bid] = mymax; + __threadfence(); - template - void minMaxLocCaller(const PtrStepSzb src, double* minval, double* maxval, - int minloc[2], int maxloc[2], PtrStepb valbuf, PtrStepb locbuf) - { - dim3 threads, grid; - estimateThreadCfg(src.cols, src.rows, threads, grid); - setKernelConsts(src.cols, src.rows, threads, grid); - - T* minval_buf = (T*)valbuf.ptr(0); - T* maxval_buf = (T*)valbuf.ptr(1); - uint* minloc_buf = (uint*)locbuf.ptr(0); - uint* maxloc_buf = (uint*)locbuf.ptr(1); - - minMaxLocKernel<256, T, MaskTrue><<>>(src, MaskTrue(), minval_buf, maxval_buf, - minloc_buf, maxloc_buf); - cudaSafeCall( cudaGetLastError() ); - - cudaSafeCall( cudaDeviceSynchronize() ); - - T minval_, maxval_; - cudaSafeCall(cudaMemcpy(&minval_, minval_buf, sizeof(T), cudaMemcpyDeviceToHost)); - cudaSafeCall(cudaMemcpy(&maxval_, maxval_buf, sizeof(T), cudaMemcpyDeviceToHost)); - *minval = minval_; - *maxval = maxval_; - - uint minloc_, maxloc_; - cudaSafeCall(cudaMemcpy(&minloc_, minloc_buf, sizeof(int), cudaMemcpyDeviceToHost)); - cudaSafeCall(cudaMemcpy(&maxloc_, maxloc_buf, sizeof(int), cudaMemcpyDeviceToHost)); - minloc[1] = minloc_ / src.cols; minloc[0] = minloc_ - minloc[1] * src.cols; - maxloc[1] = maxloc_ / src.cols; maxloc[0] = maxloc_ - maxloc[1] * src.cols; + unsigned int ticket = ::atomicAdd(&blocks_finished, 1); + is_last = (ticket == gridDim.x * gridDim.y - 1); } - template void minMaxLocCaller(const PtrStepSzb, double*, double*, int[2], int[2], PtrStepb, PtrStepb); - template void minMaxLocCaller(const PtrStepSzb, double*, double*, int[2], int[2], PtrStepb, PtrStepb); - template void minMaxLocCaller(const PtrStepSzb, double*, double*, int[2], int[2], PtrStepb, PtrStepb); - template void minMaxLocCaller(const PtrStepSzb, double*, double*, int[2], int[2], PtrStepb, PtrStepb); - template void minMaxLocCaller(const PtrStepSzb, double*, double*, int[2], int[2], PtrStepb, PtrStepb); - template void minMaxLocCaller(const PtrStepSzb, double*, double*, int[2], int[2], PtrStepb, PtrStepb); - template void minMaxLocCaller(const PtrStepSzb, double*, double*, int[2], int[2], PtrStepb, PtrStepb); - + __syncthreads(); - // This kernel will be used only when compute capability is 1.0 - template - __global__ void minMaxLocPass2Kernel(T* minval, T* maxval, uint* minloc, uint* maxloc, int size) + if (is_last) { - typedef typename MinMaxTypeTraits::best_type best_type; - __shared__ best_type sminval[nthreads]; - __shared__ best_type smaxval[nthreads]; - __shared__ uint sminloc[nthreads]; - __shared__ uint smaxloc[nthreads]; + int idx = ::min(tid, gridDim.x * gridDim.y - 1); - uint tid = threadIdx.y * blockDim.x + threadIdx.x; - uint idx = ::min(tid, size - 1); + mymin = minval[idx]; + mymax = maxval[idx]; - sminval[tid] = minval[idx]; - smaxval[tid] = maxval[idx]; - sminloc[tid] = minloc[idx]; - smaxloc[tid] = maxloc[idx]; - __syncthreads(); - - findMinMaxLocInSmem(sminval, smaxval, sminloc, smaxloc, tid); + const minimum minOp; + const maximum maxOp; + device::reduce(smem_tuple(sminval, smaxval), thrust::tie(mymin, mymax), tid, thrust::make_tuple(minOp, maxOp)); if (tid == 0) { - minval[0] = (T)sminval[0]; - maxval[0] = (T)smaxval[0]; - minloc[0] = sminloc[0]; - maxloc[0] = smaxloc[0]; - } - } - - - template - void minMaxLocMaskMultipassCaller(const PtrStepSzb src, const PtrStepb mask, double* minval, double* maxval, - int minloc[2], int maxloc[2], PtrStepb valbuf, PtrStepb locbuf) - { - dim3 threads, grid; - estimateThreadCfg(src.cols, src.rows, threads, grid); - setKernelConsts(src.cols, src.rows, threads, grid); - - T* minval_buf = (T*)valbuf.ptr(0); - T* maxval_buf = (T*)valbuf.ptr(1); - uint* minloc_buf = (uint*)locbuf.ptr(0); - uint* maxloc_buf = (uint*)locbuf.ptr(1); - - minMaxLocKernel<256, T, Mask8U><<>>(src, Mask8U(mask), minval_buf, maxval_buf, - minloc_buf, maxloc_buf); - cudaSafeCall( cudaGetLastError() ); - minMaxLocPass2Kernel<256, T><<<1, 256>>>(minval_buf, maxval_buf, minloc_buf, maxloc_buf, grid.x * grid.y); - cudaSafeCall( cudaGetLastError() ); - - cudaSafeCall( cudaDeviceSynchronize() ); - - T minval_, maxval_; - cudaSafeCall(cudaMemcpy(&minval_, minval_buf, sizeof(T), cudaMemcpyDeviceToHost)); - cudaSafeCall(cudaMemcpy(&maxval_, maxval_buf, sizeof(T), cudaMemcpyDeviceToHost)); - *minval = minval_; - *maxval = maxval_; - - uint minloc_, maxloc_; - cudaSafeCall(cudaMemcpy(&minloc_, minloc_buf, sizeof(int), cudaMemcpyDeviceToHost)); - cudaSafeCall(cudaMemcpy(&maxloc_, maxloc_buf, sizeof(int), cudaMemcpyDeviceToHost)); - minloc[1] = minloc_ / src.cols; minloc[0] = minloc_ - minloc[1] * src.cols; - maxloc[1] = maxloc_ / src.cols; maxloc[0] = maxloc_ - maxloc[1] * src.cols; - } - - template void minMaxLocMaskMultipassCaller(const PtrStepSzb, const PtrStepb, double*, double*, int[2], int[2], PtrStepb, PtrStepb); - template void minMaxLocMaskMultipassCaller(const PtrStepSzb, const PtrStepb, double*, double*, int[2], int[2], PtrStepb, PtrStepb); - template void minMaxLocMaskMultipassCaller(const PtrStepSzb, const PtrStepb, double*, double*, int[2], int[2], PtrStepb, PtrStepb); - template void minMaxLocMaskMultipassCaller(const PtrStepSzb, const PtrStepb, double*, double*, int[2], int[2], PtrStepb, PtrStepb); - template void minMaxLocMaskMultipassCaller(const PtrStepSzb, const PtrStepb, double*, double*, int[2], int[2], PtrStepb, PtrStepb); - template void minMaxLocMaskMultipassCaller(const PtrStepSzb, const PtrStepb, double*, double*, int[2], int[2], PtrStepb, PtrStepb); + minval[0] = mymin; + maxval[0] = mymax; - - template - void minMaxLocMultipassCaller(const PtrStepSzb src, double* minval, double* maxval, - int minloc[2], int maxloc[2], PtrStepb valbuf, PtrStepb locbuf) - { - dim3 threads, grid; - estimateThreadCfg(src.cols, src.rows, threads, grid); - setKernelConsts(src.cols, src.rows, threads, grid); - - T* minval_buf = (T*)valbuf.ptr(0); - T* maxval_buf = (T*)valbuf.ptr(1); - uint* minloc_buf = (uint*)locbuf.ptr(0); - uint* maxloc_buf = (uint*)locbuf.ptr(1); - - minMaxLocKernel<256, T, MaskTrue><<>>(src, MaskTrue(), minval_buf, maxval_buf, - minloc_buf, maxloc_buf); - cudaSafeCall( cudaGetLastError() ); - minMaxLocPass2Kernel<256, T><<<1, 256>>>(minval_buf, maxval_buf, minloc_buf, maxloc_buf, grid.x * grid.y); - cudaSafeCall( cudaGetLastError() ); - - cudaSafeCall( cudaDeviceSynchronize() ); - - T minval_, maxval_; - cudaSafeCall(cudaMemcpy(&minval_, minval_buf, sizeof(T), cudaMemcpyDeviceToHost)); - cudaSafeCall(cudaMemcpy(&maxval_, maxval_buf, sizeof(T), cudaMemcpyDeviceToHost)); - *minval = minval_; - *maxval = maxval_; - - uint minloc_, maxloc_; - cudaSafeCall(cudaMemcpy(&minloc_, minloc_buf, sizeof(int), cudaMemcpyDeviceToHost)); - cudaSafeCall(cudaMemcpy(&maxloc_, maxloc_buf, sizeof(int), cudaMemcpyDeviceToHost)); - minloc[1] = minloc_ / src.cols; minloc[0] = minloc_ - minloc[1] * src.cols; - maxloc[1] = maxloc_ / src.cols; maxloc[0] = maxloc_ - maxloc[1] * src.cols; + blocks_finished = 0; + } } - - template void minMaxLocMultipassCaller(const PtrStepSzb, double*, double*, int[2], int[2], PtrStepb, PtrStepb); - template void minMaxLocMultipassCaller(const PtrStepSzb, double*, double*, int[2], int[2], PtrStepb, PtrStepb); - template void minMaxLocMultipassCaller(const PtrStepSzb, double*, double*, int[2], int[2], PtrStepb, PtrStepb); - template void minMaxLocMultipassCaller(const PtrStepSzb, double*, double*, int[2], int[2], PtrStepb, PtrStepb); - template void minMaxLocMultipassCaller(const PtrStepSzb, double*, double*, int[2], int[2], PtrStepb, PtrStepb); - template void minMaxLocMultipassCaller(const PtrStepSzb, double*, double*, int[2], int[2], PtrStepb, PtrStepb); - } // namespace minmaxloc - - ////////////////////////////////////////////////////////////////////////////////////////////////////////// - // countNonZero - - namespace countnonzero + } + }; + template + struct GlobalReduce + { + static __device__ void run(int& mymin, int& mymax, int* minval, int* maxval, int tid, int bid, int* sminval, int* smaxval) { - __constant__ int ctwidth; - __constant__ int ctheight; - - __device__ uint blocks_finished = 0; - - void estimateThreadCfg(int cols, int rows, dim3& threads, dim3& grid) + #if __CUDA_ARCH__ >= 200 + if (tid == 0) { - threads = dim3(32, 8); - grid = dim3(divUp(cols, threads.x * 8), divUp(rows, threads.y * 32)); - grid.x = std::min(grid.x, threads.x); - grid.y = std::min(grid.y, threads.y); + ::atomicMin(minval, mymin); + ::atomicMax(maxval, mymax); } + #else + __shared__ bool is_last; - - void getBufSizeRequired(int cols, int rows, int& bufcols, int& bufrows) + if (tid == 0) { - dim3 threads, grid; - estimateThreadCfg(cols, rows, threads, grid); - bufcols = grid.x * grid.y * sizeof(int); - bufrows = 1; - } + minval[bid] = mymin; + maxval[bid] = mymax; + __threadfence(); - void setKernelConsts(int cols, int rows, const dim3& threads, const dim3& grid) - { - int twidth = divUp(divUp(cols, grid.x), threads.x); - int theight = divUp(divUp(rows, grid.y), threads.y); - cudaSafeCall(cudaMemcpyToSymbol(ctwidth, &twidth, sizeof(twidth))); - cudaSafeCall(cudaMemcpyToSymbol(ctheight, &theight, sizeof(theight))); + unsigned int ticket = ::atomicAdd(&blocks_finished, 1); + is_last = (ticket == gridDim.x * gridDim.y - 1); } + __syncthreads(); - template - __global__ void countNonZeroKernel(const PtrStepSzb src, volatile uint* count) + if (is_last) { - __shared__ uint scount[nthreads]; + int idx = ::min(tid, gridDim.x * gridDim.y - 1); - uint x0 = blockIdx.x * blockDim.x * ctwidth + threadIdx.x; - uint y0 = blockIdx.y * blockDim.y * ctheight + threadIdx.y; - uint tid = threadIdx.y * blockDim.x + threadIdx.x; + mymin = minval[idx]; + mymax = maxval[idx]; - uint cnt = 0; - for (uint y = 0; y < ctheight && y0 + y * blockDim.y < src.rows; ++y) - { - const T* ptr = (const T*)src.ptr(y0 + y * blockDim.y); - for (uint x = 0; x < ctwidth && x0 + x * blockDim.x < src.cols; ++x) - cnt += ptr[x0 + x * blockDim.x] != 0; - } - - scount[tid] = cnt; - __syncthreads(); - - sumInSmem(scount, tid); - - #if defined(__CUDA_ARCH__) && (__CUDA_ARCH__ >= 110) - __shared__ bool is_last; + const minimum minOp; + const maximum maxOp; + device::reduce(smem_tuple(sminval, smaxval), thrust::tie(mymin, mymax), tid, thrust::make_tuple(minOp, maxOp)); if (tid == 0) { - count[blockIdx.y * gridDim.x + blockIdx.x] = scount[0]; - __threadfence(); - - uint ticket = atomicInc(&blocks_finished, gridDim.x * gridDim.y); - is_last = ticket == gridDim.x * gridDim.y - 1; - } - - __syncthreads(); - - if (is_last) - { - scount[tid] = tid < gridDim.x * gridDim.y ? count[tid] : 0; - __syncthreads(); + minval[0] = mymin; + maxval[0] = mymax; - sumInSmem(scount, tid); - - if (tid == 0) - { - count[0] = scount[0]; - blocks_finished = 0; - } + blocks_finished = 0; } - #else - if (tid == 0) count[blockIdx.y * gridDim.x + blockIdx.x] = scount[0]; - #endif - } - - - template - int countNonZeroCaller(const PtrStepSzb src, PtrStepb buf) - { - dim3 threads, grid; - estimateThreadCfg(src.cols, src.rows, threads, grid); - setKernelConsts(src.cols, src.rows, threads, grid); - - uint* count_buf = (uint*)buf.ptr(0); - - countNonZeroKernel<256, T><<>>(src, count_buf); - cudaSafeCall( cudaGetLastError() ); - - cudaSafeCall( cudaDeviceSynchronize() ); - - uint count; - cudaSafeCall(cudaMemcpy(&count, count_buf, sizeof(int), cudaMemcpyDeviceToHost)); - - return count; - } - - template int countNonZeroCaller(const PtrStepSzb, PtrStepb); - template int countNonZeroCaller(const PtrStepSzb, PtrStepb); - template int countNonZeroCaller(const PtrStepSzb, PtrStepb); - template int countNonZeroCaller(const PtrStepSzb, PtrStepb); - template int countNonZeroCaller(const PtrStepSzb, PtrStepb); - template int countNonZeroCaller(const PtrStepSzb, PtrStepb); - template int countNonZeroCaller(const PtrStepSzb, PtrStepb); - - - template - __global__ void countNonZeroPass2Kernel(uint* count, int size) - { - __shared__ uint scount[nthreads]; - uint tid = threadIdx.y * blockDim.x + threadIdx.x; - - scount[tid] = tid < size ? count[tid] : 0; - __syncthreads(); - - sumInSmem(scount, tid); - - if (tid == 0) - count[0] = scount[0]; } + #endif + } + }; + template + __global__ void kernel(const PtrStepSz src, const Mask mask, R* minval, R* maxval, const int twidth, const int theight) + { + __shared__ R sminval[BLOCK_SIZE]; + __shared__ R smaxval[BLOCK_SIZE]; - template - int countNonZeroMultipassCaller(const PtrStepSzb src, PtrStepb buf) - { - dim3 threads, grid; - estimateThreadCfg(src.cols, src.rows, threads, grid); - setKernelConsts(src.cols, src.rows, threads, grid); - - uint* count_buf = (uint*)buf.ptr(0); - - countNonZeroKernel<256, T><<>>(src, count_buf); - cudaSafeCall( cudaGetLastError() ); - countNonZeroPass2Kernel<256, T><<<1, 256>>>(count_buf, grid.x * grid.y); - cudaSafeCall( cudaGetLastError() ); - - cudaSafeCall( cudaDeviceSynchronize() ); - - uint count; - cudaSafeCall(cudaMemcpy(&count, count_buf, sizeof(int), cudaMemcpyDeviceToHost)); - - return count; - } - - template int countNonZeroMultipassCaller(const PtrStepSzb, PtrStepb); - template int countNonZeroMultipassCaller(const PtrStepSzb, PtrStepb); - template int countNonZeroMultipassCaller(const PtrStepSzb, PtrStepb); - template int countNonZeroMultipassCaller(const PtrStepSzb, PtrStepb); - template int countNonZeroMultipassCaller(const PtrStepSzb, PtrStepb); - template int countNonZeroMultipassCaller(const PtrStepSzb, PtrStepb); + const int x0 = blockIdx.x * blockDim.x * twidth + threadIdx.x; + const int y0 = blockIdx.y * blockDim.y * theight + threadIdx.y; - } // namespace countnonzero + const int tid = threadIdx.y * blockDim.x + threadIdx.x; + const int bid = blockIdx.y * gridDim.x + blockIdx.x; + R mymin = numeric_limits::max(); + R mymax = -numeric_limits::max(); - ////////////////////////////////////////////////////////////////////////// - // Sum + const minimum minOp; + const maximum maxOp; - namespace sum + for (int i = 0, y = y0; i < theight && y < src.rows; ++i, y += blockDim.y) { - template struct SumType {}; - template <> struct SumType { typedef uint R; }; - template <> struct SumType { typedef int R; }; - template <> struct SumType { typedef uint R; }; - template <> struct SumType { typedef int R; }; - template <> struct SumType { typedef int R; }; - template <> struct SumType { typedef float R; }; - template <> struct SumType { typedef double R; }; - - template - struct IdentityOp { static __device__ __forceinline__ R call(R x) { return x; } }; + const T* ptr = src.ptr(y); - template - struct AbsOp { static __device__ __forceinline__ R call(R x) { return ::abs(x); } }; - - template <> - struct AbsOp { static __device__ __forceinline__ uint call(uint x) { return x; } }; - - template - struct SqrOp { static __device__ __forceinline__ R call(R x) { return x * x; } }; - - __constant__ int ctwidth; - __constant__ int ctheight; - __device__ uint blocks_finished = 0; - - const int threads_x = 32; - const int threads_y = 8; - - void estimateThreadCfg(int cols, int rows, dim3& threads, dim3& grid) + for (int j = 0, x = x0; j < twidth && x < src.cols; ++j, x += blockDim.x) { - threads = dim3(threads_x, threads_y); - grid = dim3(divUp(cols, threads.x * threads.y), - divUp(rows, threads.y * threads.x)); - grid.x = std::min(grid.x, threads.x); - grid.y = std::min(grid.y, threads.y); - } - - - void getBufSizeRequired(int cols, int rows, int cn, int& bufcols, int& bufrows) - { - dim3 threads, grid; - estimateThreadCfg(cols, rows, threads, grid); - bufcols = grid.x * grid.y * sizeof(double) * cn; - bufrows = 1; - } - - - void setKernelConsts(int cols, int rows, const dim3& threads, const dim3& grid) - { - int twidth = divUp(divUp(cols, grid.x), threads.x); - int theight = divUp(divUp(rows, grid.y), threads.y); - cudaSafeCall(cudaMemcpyToSymbol(ctwidth, &twidth, sizeof(twidth))); - cudaSafeCall(cudaMemcpyToSymbol(ctheight, &theight, sizeof(theight))); - } - - template - __global__ void sumKernel(const PtrStepSzb src, R* result) - { - __shared__ R smem[nthreads]; - - const int x0 = blockIdx.x * blockDim.x * ctwidth + threadIdx.x; - const int y0 = blockIdx.y * blockDim.y * ctheight + threadIdx.y; - const int tid = threadIdx.y * blockDim.x + threadIdx.x; - const int bid = blockIdx.y * gridDim.x + blockIdx.x; - - R sum = 0; - for (int y = 0; y < ctheight && y0 + y * blockDim.y < src.rows; ++y) + if (mask(y, x)) { - const T* ptr = (const T*)src.ptr(y0 + y * blockDim.y); - for (int x = 0; x < ctwidth && x0 + x * blockDim.x < src.cols; ++x) - sum += Op::call(ptr[x0 + x * blockDim.x]); - } - - smem[tid] = sum; - __syncthreads(); + const R srcVal = ptr[x]; - sumInSmem(smem, tid); - - #if defined(__CUDA_ARCH__) && (__CUDA_ARCH__ >= 110) - __shared__ bool is_last; - - if (tid == 0) - { - result[bid] = smem[0]; - __threadfence(); - - uint ticket = atomicInc(&blocks_finished, gridDim.x * gridDim.y); - is_last = (ticket == gridDim.x * gridDim.y - 1); - } - - __syncthreads(); - - if (is_last) - { - smem[tid] = tid < gridDim.x * gridDim.y ? result[tid] : 0; - __syncthreads(); - - sumInSmem(smem, tid); - - if (tid == 0) - { - result[0] = smem[0]; - blocks_finished = 0; - } + mymin = minOp(mymin, srcVal); + mymax = maxOp(mymax, srcVal); } - #else - if (tid == 0) result[bid] = smem[0]; - #endif } + } + device::reduce(smem_tuple(sminval, smaxval), thrust::tie(mymin, mymax), tid, thrust::make_tuple(minOp, maxOp)); - template - __global__ void sumPass2Kernel(R* result, int size) - { - __shared__ R smem[nthreads]; - int tid = threadIdx.y * blockDim.x + threadIdx.x; - - smem[tid] = tid < size ? result[tid] : 0; - __syncthreads(); - - sumInSmem(smem, tid); + GlobalReduce::run(mymin, mymax, minval, maxval, tid, bid, sminval, smaxval); + } - if (tid == 0) - result[0] = smem[0]; - } + const int threads_x = 32; + const int threads_y = 8; + void getLaunchCfg(int cols, int rows, dim3& block, dim3& grid) + { + block = dim3(threads_x, threads_y); - template - __global__ void sumKernel_C2(const PtrStepSzb src, typename TypeVec::vec_type* result) - { - typedef typename TypeVec::vec_type SrcType; - typedef typename TypeVec::vec_type DstType; + grid = dim3(divUp(cols, block.x * block.y), + divUp(rows, block.y * block.x)); - __shared__ R smem[nthreads * 2]; + grid.x = ::min(grid.x, block.x); + grid.y = ::min(grid.y, block.y); + } - const int x0 = blockIdx.x * blockDim.x * ctwidth + threadIdx.x; - const int y0 = blockIdx.y * blockDim.y * ctheight + threadIdx.y; - const int tid = threadIdx.y * blockDim.x + threadIdx.x; - const int bid = blockIdx.y * gridDim.x + blockIdx.x; + void getBufSize(int cols, int rows, int& bufcols, int& bufrows) + { + dim3 block, grid; + getLaunchCfg(cols, rows, block, grid); - SrcType val; - DstType sum = VecTraits::all(0); - for (int y = 0; y < ctheight && y0 + y * blockDim.y < src.rows; ++y) - { - const SrcType* ptr = (const SrcType*)src.ptr(y0 + y * blockDim.y); - for (int x = 0; x < ctwidth && x0 + x * blockDim.x < src.cols; ++x) - { - val = ptr[x0 + x * blockDim.x]; - sum = sum + VecTraits::make(Op::call(val.x), Op::call(val.y)); - } - } + bufcols = grid.x * grid.y * sizeof(double); + bufrows = 2; + } - smem[tid] = sum.x; - smem[tid + nthreads] = sum.y; - __syncthreads(); + __global__ void setDefaultKernel(int* minval_buf, int* maxval_buf) + { + *minval_buf = numeric_limits::max(); + *maxval_buf = numeric_limits::min(); + } - sumInSmem(smem, tid); - sumInSmem(smem + nthreads, tid); + template + void setDefault(R*, R*) + { + } + void setDefault(int* minval_buf, int* maxval_buf) + { + setDefaultKernel<<<1, 1>>>(minval_buf, maxval_buf); + } - #if defined(__CUDA_ARCH__) && (__CUDA_ARCH__ >= 110) - __shared__ bool is_last; + template + void run(const PtrStepSzb src, const PtrStepb mask, double* minval, double* maxval, PtrStepb buf) + { + typedef typename MinMaxTypeTraits::best_type R; - if (tid == 0) - { - DstType res; - res.x = smem[0]; - res.y = smem[nthreads]; - result[bid] = res; - __threadfence(); - - uint ticket = atomicInc(&blocks_finished, gridDim.x * gridDim.y); - is_last = (ticket == gridDim.x * gridDim.y - 1); - } + dim3 block, grid; + getLaunchCfg(src.cols, src.rows, block, grid); - __syncthreads(); + const int twidth = divUp(divUp(src.cols, grid.x), block.x); + const int theight = divUp(divUp(src.rows, grid.y), block.y); - if (is_last) - { - DstType res = tid < gridDim.x * gridDim.y ? result[tid] : VecTraits::all(0); - smem[tid] = res.x; - smem[tid + nthreads] = res.y; - __syncthreads(); + R* minval_buf = (R*) buf.ptr(0); + R* maxval_buf = (R*) buf.ptr(1); - sumInSmem(smem, tid); - sumInSmem(smem + nthreads, tid); + setDefault(minval_buf, maxval_buf); - if (tid == 0) - { - res.x = smem[0]; - res.y = smem[nthreads]; - result[0] = res; - blocks_finished = 0; - } - } - #else - if (tid == 0) - { - DstType res; - res.x = smem[0]; - res.y = smem[nthreads]; - result[bid] = res; - } - #endif - } + if (mask.data) + kernel<<>>((PtrStepSz) src, SingleMask(mask), minval_buf, maxval_buf, twidth, theight); + else + kernel<<>>((PtrStepSz) src, WithOutMask(), minval_buf, maxval_buf, twidth, theight); + cudaSafeCall( cudaGetLastError() ); - template - __global__ void sumPass2Kernel_C2(typename TypeVec::vec_type* result, int size) - { - typedef typename TypeVec::vec_type DstType; + cudaSafeCall( cudaDeviceSynchronize() ); - __shared__ R smem[nthreads * 2]; + R minval_, maxval_; + cudaSafeCall( cudaMemcpy(&minval_, minval_buf, sizeof(R), cudaMemcpyDeviceToHost) ); + cudaSafeCall( cudaMemcpy(&maxval_, maxval_buf, sizeof(R), cudaMemcpyDeviceToHost) ); + *minval = minval_; + *maxval = maxval_; + } - const int tid = threadIdx.y * blockDim.x + threadIdx.x; + template void run(const PtrStepSzb src, const PtrStepb mask, double* minval, double* maxval, PtrStepb buf); + template void run(const PtrStepSzb src, const PtrStepb mask, double* minval, double* maxval, PtrStepb buf); + template void run(const PtrStepSzb src, const PtrStepb mask, double* minval, double* maxval, PtrStepb buf); + template void run(const PtrStepSzb src, const PtrStepb mask, double* minval, double* maxval, PtrStepb buf); + template void run(const PtrStepSzb src, const PtrStepb mask, double* minval, double* maxval, PtrStepb buf); + template void run(const PtrStepSzb src, const PtrStepb mask, double* minval, double* maxval, PtrStepb buf); + template void run(const PtrStepSzb src, const PtrStepb mask, double* minval, double* maxval, PtrStepb buf); +} - DstType res = tid < size ? result[tid] : VecTraits::all(0); - smem[tid] = res.x; - smem[tid + nthreads] = res.y; - __syncthreads(); +///////////////////////////////////////////////////////////// +// minMaxLoc - sumInSmem(smem, tid); - sumInSmem(smem + nthreads, tid); +namespace minMaxLoc +{ + __device__ unsigned int blocks_finished = 0; + + // To avoid shared bank conflicts we convert each value into value of + // appropriate type (32 bits minimum) + template struct MinMaxTypeTraits; + template <> struct MinMaxTypeTraits { typedef int best_type; }; + template <> struct MinMaxTypeTraits { typedef int best_type; }; + template <> struct MinMaxTypeTraits { typedef int best_type; }; + template <> struct MinMaxTypeTraits { typedef int best_type; }; + template <> struct MinMaxTypeTraits { typedef int best_type; }; + template <> struct MinMaxTypeTraits { typedef float best_type; }; + template <> struct MinMaxTypeTraits { typedef double best_type; }; + + template + __global__ void kernel(const PtrStepSz src, const Mask mask, T* minval, T* maxval, unsigned int* minloc, unsigned int* maxloc, const int twidth, const int theight) + { + typedef typename MinMaxTypeTraits::best_type work_type; - if (tid == 0) - { - res.x = smem[0]; - res.y = smem[nthreads]; - result[0] = res; - } - } + __shared__ work_type sminval[BLOCK_SIZE]; + __shared__ work_type smaxval[BLOCK_SIZE]; + __shared__ unsigned int sminloc[BLOCK_SIZE]; + __shared__ unsigned int smaxloc[BLOCK_SIZE]; + __shared__ bool is_last; + const int x0 = blockIdx.x * blockDim.x * twidth + threadIdx.x; + const int y0 = blockIdx.y * blockDim.y * theight + threadIdx.y; - template - __global__ void sumKernel_C3(const PtrStepSzb src, typename TypeVec::vec_type* result) - { - typedef typename TypeVec::vec_type SrcType; - typedef typename TypeVec::vec_type DstType; + const int tid = threadIdx.y * blockDim.x + threadIdx.x; + const int bid = blockIdx.y * gridDim.x + blockIdx.x; - __shared__ R smem[nthreads * 3]; + work_type mymin = numeric_limits::max(); + work_type mymax = -numeric_limits::max(); + unsigned int myminloc = 0; + unsigned int mymaxloc = 0; - const int x0 = blockIdx.x * blockDim.x * ctwidth + threadIdx.x; - const int y0 = blockIdx.y * blockDim.y * ctheight + threadIdx.y; - const int tid = threadIdx.y * blockDim.x + threadIdx.x; - const int bid = blockIdx.y * gridDim.x + blockIdx.x; + for (int i = 0, y = y0; i < theight && y < src.rows; ++i, y += blockDim.y) + { + const T* ptr = src.ptr(y); - SrcType val; - DstType sum = VecTraits::all(0); - for (int y = 0; y < ctheight && y0 + y * blockDim.y < src.rows; ++y) + for (int j = 0, x = x0; j < twidth && x < src.cols; ++j, x += blockDim.x) + { + if (mask(y, x)) { - const SrcType* ptr = (const SrcType*)src.ptr(y0 + y * blockDim.y); - for (int x = 0; x < ctwidth && x0 + x * blockDim.x < src.cols; ++x) + const work_type srcVal = ptr[x]; + + if (srcVal < mymin) { - val = ptr[x0 + x * blockDim.x]; - sum = sum + VecTraits::make(Op::call(val.x), Op::call(val.y), Op::call(val.z)); + mymin = srcVal; + myminloc = y * src.cols + x; } - } - - smem[tid] = sum.x; - smem[tid + nthreads] = sum.y; - smem[tid + 2 * nthreads] = sum.z; - __syncthreads(); - - sumInSmem(smem, tid); - sumInSmem(smem + nthreads, tid); - sumInSmem(smem + 2 * nthreads, tid); - - #if defined(__CUDA_ARCH__) && __CUDA_ARCH__ >= 110 - __shared__ bool is_last; - - if (tid == 0) - { - DstType res; - res.x = smem[0]; - res.y = smem[nthreads]; - res.z = smem[2 * nthreads]; - result[bid] = res; - __threadfence(); - - uint ticket = atomicInc(&blocks_finished, gridDim.x * gridDim.y); - is_last = (ticket == gridDim.x * gridDim.y - 1); - } - - __syncthreads(); - - if (is_last) - { - DstType res = tid < gridDim.x * gridDim.y ? result[tid] : VecTraits::all(0); - smem[tid] = res.x; - smem[tid + nthreads] = res.y; - smem[tid + 2 * nthreads] = res.z; - __syncthreads(); - sumInSmem(smem, tid); - sumInSmem(smem + nthreads, tid); - sumInSmem(smem + 2 * nthreads, tid); - - if (tid == 0) + if (srcVal > mymax) { - res.x = smem[0]; - res.y = smem[nthreads]; - res.z = smem[2 * nthreads]; - result[0] = res; - blocks_finished = 0; + mymax = srcVal; + mymaxloc = y * src.cols + x; } } - #else - if (tid == 0) - { - DstType res; - res.x = smem[0]; - res.y = smem[nthreads]; - res.z = smem[2 * nthreads]; - result[bid] = res; - } - #endif - } - - - template - __global__ void sumPass2Kernel_C3(typename TypeVec::vec_type* result, int size) - { - typedef typename TypeVec::vec_type DstType; - - __shared__ R smem[nthreads * 3]; - - const int tid = threadIdx.y * blockDim.x + threadIdx.x; - - DstType res = tid < size ? result[tid] : VecTraits::all(0); - smem[tid] = res.x; - smem[tid + nthreads] = res.y; - smem[tid + 2 * nthreads] = res.z; - __syncthreads(); - - sumInSmem(smem, tid); - sumInSmem(smem + nthreads, tid); - sumInSmem(smem + 2 * nthreads, tid); - - if (tid == 0) - { - res.x = smem[0]; - res.y = smem[nthreads]; - res.z = smem[2 * nthreads]; - result[0] = res; - } } + } - template - __global__ void sumKernel_C4(const PtrStepSzb src, typename TypeVec::vec_type* result) - { - typedef typename TypeVec::vec_type SrcType; - typedef typename TypeVec::vec_type DstType; + reduceKeyVal(smem_tuple(sminval, smaxval), thrust::tie(mymin, mymax), + smem_tuple(sminloc, smaxloc), thrust::tie(myminloc, mymaxloc), + tid, + thrust::make_tuple(less(), greater())); - __shared__ R smem[nthreads * 4]; + if (tid == 0) + { + minval[bid] = (T) mymin; + maxval[bid] = (T) mymax; + minloc[bid] = myminloc; + maxloc[bid] = mymaxloc; - const int x0 = blockIdx.x * blockDim.x * ctwidth + threadIdx.x; - const int y0 = blockIdx.y * blockDim.y * ctheight + threadIdx.y; - const int tid = threadIdx.y * blockDim.x + threadIdx.x; - const int bid = blockIdx.y * gridDim.x + blockIdx.x; + __threadfence(); - SrcType val; - DstType sum = VecTraits::all(0); - for (int y = 0; y < ctheight && y0 + y * blockDim.y < src.rows; ++y) - { - const SrcType* ptr = (const SrcType*)src.ptr(y0 + y * blockDim.y); - for (int x = 0; x < ctwidth && x0 + x * blockDim.x < src.cols; ++x) - { - val = ptr[x0 + x * blockDim.x]; - sum = sum + VecTraits::make(Op::call(val.x), Op::call(val.y), - Op::call(val.z), Op::call(val.w)); - } - } + unsigned int ticket = ::atomicInc(&blocks_finished, gridDim.x * gridDim.y); + is_last = (ticket == gridDim.x * gridDim.y - 1); + } - smem[tid] = sum.x; - smem[tid + nthreads] = sum.y; - smem[tid + 2 * nthreads] = sum.z; - smem[tid + 3 * nthreads] = sum.w; - __syncthreads(); + __syncthreads(); - sumInSmem(smem, tid); - sumInSmem(smem + nthreads, tid); - sumInSmem(smem + 2 * nthreads, tid); - sumInSmem(smem + 3 * nthreads, tid); + if (is_last) + { + unsigned int idx = ::min(tid, gridDim.x * gridDim.y - 1); - #if defined(__CUDA_ARCH__) && (__CUDA_ARCH__ >= 110) - __shared__ bool is_last; + mymin = minval[idx]; + mymax = maxval[idx]; + myminloc = minloc[idx]; + mymaxloc = maxloc[idx]; - if (tid == 0) - { - DstType res; - res.x = smem[0]; - res.y = smem[nthreads]; - res.z = smem[2 * nthreads]; - res.w = smem[3 * nthreads]; - result[bid] = res; - __threadfence(); - - uint ticket = atomicInc(&blocks_finished, gridDim.x * gridDim.y); - is_last = (ticket == gridDim.x * gridDim.y - 1); - } + reduceKeyVal(smem_tuple(sminval, smaxval), thrust::tie(mymin, mymax), + smem_tuple(sminloc, smaxloc), thrust::tie(myminloc, mymaxloc), + tid, + thrust::make_tuple(less(), greater())); - __syncthreads(); + if (tid == 0) + { + minval[0] = (T) mymin; + maxval[0] = (T) mymax; + minloc[0] = myminloc; + maxloc[0] = mymaxloc; - if (is_last) - { - DstType res = tid < gridDim.x * gridDim.y ? result[tid] : VecTraits::all(0); - smem[tid] = res.x; - smem[tid + nthreads] = res.y; - smem[tid + 2 * nthreads] = res.z; - smem[tid + 3 * nthreads] = res.w; - __syncthreads(); - - sumInSmem(smem, tid); - sumInSmem(smem + nthreads, tid); - sumInSmem(smem + 2 * nthreads, tid); - sumInSmem(smem + 3 * nthreads, tid); - - if (tid == 0) - { - res.x = smem[0]; - res.y = smem[nthreads]; - res.z = smem[2 * nthreads]; - res.w = smem[3 * nthreads]; - result[0] = res; - blocks_finished = 0; - } - } - #else - if (tid == 0) - { - DstType res; - res.x = smem[0]; - res.y = smem[nthreads]; - res.z = smem[2 * nthreads]; - res.w = smem[3 * nthreads]; - result[bid] = res; - } - #endif + blocks_finished = 0; } + } + } + const int threads_x = 32; + const int threads_y = 8; - template - __global__ void sumPass2Kernel_C4(typename TypeVec::vec_type* result, int size) - { - typedef typename TypeVec::vec_type DstType; - - __shared__ R smem[nthreads * 4]; + void getLaunchCfg(int cols, int rows, dim3& block, dim3& grid) + { + block = dim3(threads_x, threads_y); - const int tid = threadIdx.y * blockDim.x + threadIdx.x; + grid = dim3(divUp(cols, block.x * block.y), + divUp(rows, block.y * block.x)); - DstType res = tid < size ? result[tid] : VecTraits::all(0); - smem[tid] = res.x; - smem[tid + nthreads] = res.y; - smem[tid + 2 * nthreads] = res.z; - smem[tid + 3 * nthreads] = res.w; - __syncthreads(); + grid.x = ::min(grid.x, block.x); + grid.y = ::min(grid.y, block.y); + } - sumInSmem(smem, tid); - sumInSmem(smem + nthreads, tid); - sumInSmem(smem + 2 * nthreads, tid); - sumInSmem(smem + 3 * nthreads, tid); + void getBufSize(int cols, int rows, size_t elem_size, int& b1cols, int& b1rows, int& b2cols, int& b2rows) + { + dim3 block, grid; + getLaunchCfg(cols, rows, block, grid); - if (tid == 0) - { - res.x = smem[0]; - res.y = smem[nthreads]; - res.z = smem[2 * nthreads]; - res.w = smem[3 * nthreads]; - result[0] = res; - } - } + // For values + b1cols = grid.x * grid.y * elem_size; + b1rows = 2; - template - void sumMultipassCaller(const PtrStepSzb src, PtrStepb buf, double* sum, int cn) - { - typedef typename SumType::R R; + // For locations + b2cols = grid.x * grid.y * sizeof(int); + b2rows = 2; + } - dim3 threads, grid; - estimateThreadCfg(src.cols, src.rows, threads, grid); - setKernelConsts(src.cols, src.rows, threads, grid); + template + void run(const PtrStepSzb src, const PtrStepb mask, double* minval, double* maxval, int* minloc, int* maxloc, PtrStepb valbuf, PtrStep locbuf) + { + dim3 block, grid; + getLaunchCfg(src.cols, src.rows, block, grid); + + const int twidth = divUp(divUp(src.cols, grid.x), block.x); + const int theight = divUp(divUp(src.rows, grid.y), block.y); + + T* minval_buf = (T*) valbuf.ptr(0); + T* maxval_buf = (T*) valbuf.ptr(1); + unsigned int* minloc_buf = locbuf.ptr(0); + unsigned int* maxloc_buf = locbuf.ptr(1); + + if (mask.data) + kernel<<>>((PtrStepSz) src, SingleMask(mask), minval_buf, maxval_buf, minloc_buf, maxloc_buf, twidth, theight); + else + kernel<<>>((PtrStepSz) src, WithOutMask(), minval_buf, maxval_buf, minloc_buf, maxloc_buf, twidth, theight); + + cudaSafeCall( cudaGetLastError() ); + + cudaSafeCall( cudaDeviceSynchronize() ); + + T minval_, maxval_; + cudaSafeCall( cudaMemcpy(&minval_, minval_buf, sizeof(T), cudaMemcpyDeviceToHost) ); + cudaSafeCall( cudaMemcpy(&maxval_, maxval_buf, sizeof(T), cudaMemcpyDeviceToHost) ); + *minval = minval_; + *maxval = maxval_; + + unsigned int minloc_, maxloc_; + cudaSafeCall( cudaMemcpy(&minloc_, minloc_buf, sizeof(unsigned int), cudaMemcpyDeviceToHost) ); + cudaSafeCall( cudaMemcpy(&maxloc_, maxloc_buf, sizeof(unsigned int), cudaMemcpyDeviceToHost) ); + minloc[1] = minloc_ / src.cols; minloc[0] = minloc_ - minloc[1] * src.cols; + maxloc[1] = maxloc_ / src.cols; maxloc[0] = maxloc_ - maxloc[1] * src.cols; + } + + template void run(const PtrStepSzb src, const PtrStepb mask, double* minval, double* maxval, int* minloc, int* maxloc, PtrStepb valbuf, PtrStep locbuf); + template void run(const PtrStepSzb src, const PtrStepb mask, double* minval, double* maxval, int* minloc, int* maxloc, PtrStepb valbuf, PtrStep locbuf); + template void run(const PtrStepSzb src, const PtrStepb mask, double* minval, double* maxval, int* minloc, int* maxloc, PtrStepb valbuf, PtrStep locbuf); + template void run(const PtrStepSzb src, const PtrStepb mask, double* minval, double* maxval, int* minloc, int* maxloc, PtrStepb valbuf, PtrStep locbuf); + template void run(const PtrStepSzb src, const PtrStepb mask, double* minval, double* maxval, int* minloc, int* maxloc, PtrStepb valbuf, PtrStep locbuf); + template void run(const PtrStepSzb src, const PtrStepb mask, double* minval, double* maxval, int* minloc, int* maxloc, PtrStepb valbuf, PtrStep locbuf); + template void run(const PtrStepSzb src, const PtrStepb mask, double* minval, double* maxval, int* minloc, int* maxloc, PtrStepb valbuf, PtrStep locbuf); +} + +///////////////////////////////////////////////////////////// +// countNonZero + +namespace countNonZero +{ + __device__ unsigned int blocks_finished = 0; - switch (cn) - { - case 1: - sumKernel, threads_x * threads_y><<>>( - src, (typename TypeVec::vec_type*)buf.ptr(0)); - cudaSafeCall( cudaGetLastError() ); - - sumPass2Kernel<<<1, threads_x * threads_y>>>( - (typename TypeVec::vec_type*)buf.ptr(0), grid.x * grid.y); - cudaSafeCall( cudaGetLastError() ); - - break; - case 2: - sumKernel_C2, threads_x * threads_y><<>>( - src, (typename TypeVec::vec_type*)buf.ptr(0)); - cudaSafeCall( cudaGetLastError() ); - - sumPass2Kernel_C2<<<1, threads_x * threads_y>>>( - (typename TypeVec::vec_type*)buf.ptr(0), grid.x * grid.y); - cudaSafeCall( cudaGetLastError() ); - - break; - case 3: - sumKernel_C3, threads_x * threads_y><<>>( - src, (typename TypeVec::vec_type*)buf.ptr(0)); - cudaSafeCall( cudaGetLastError() ); - - sumPass2Kernel_C3<<<1, threads_x * threads_y>>>( - (typename TypeVec::vec_type*)buf.ptr(0), grid.x * grid.y); - cudaSafeCall( cudaGetLastError() ); - - break; - case 4: - sumKernel_C4, threads_x * threads_y><<>>( - src, (typename TypeVec::vec_type*)buf.ptr(0)); - cudaSafeCall( cudaGetLastError() ); - - sumPass2Kernel_C4<<<1, threads_x * threads_y>>>( - (typename TypeVec::vec_type*)buf.ptr(0), grid.x * grid.y); - cudaSafeCall( cudaGetLastError() ); - - break; - } - cudaSafeCall( cudaDeviceSynchronize() ); + template + __global__ void kernel(const PtrStepSz src, unsigned int* count, const int twidth, const int theight) + { + __shared__ unsigned int scount[BLOCK_SIZE]; + __shared__ bool is_last; - R result[4] = {0, 0, 0, 0}; - cudaSafeCall(cudaMemcpy(&result, buf.ptr(0), sizeof(R) * cn, cudaMemcpyDeviceToHost)); + const int x0 = blockIdx.x * blockDim.x * twidth + threadIdx.x; + const int y0 = blockIdx.y * blockDim.y * theight + threadIdx.y; - sum[0] = result[0]; - sum[1] = result[1]; - sum[2] = result[2]; - sum[3] = result[3]; - } + const int tid = threadIdx.y * blockDim.x + threadIdx.x; + const int bid = blockIdx.y * gridDim.x + blockIdx.x; - template void sumMultipassCaller(const PtrStepSzb, PtrStepb, double*, int); - template void sumMultipassCaller(const PtrStepSzb, PtrStepb, double*, int); - template void sumMultipassCaller(const PtrStepSzb, PtrStepb, double*, int); - template void sumMultipassCaller(const PtrStepSzb, PtrStepb, double*, int); - template void sumMultipassCaller(const PtrStepSzb, PtrStepb, double*, int); - template void sumMultipassCaller(const PtrStepSzb, PtrStepb, double*, int); + unsigned int mycount = 0; + for (int i = 0, y = y0; i < theight && y < src.rows; ++i, y += blockDim.y) + { + const T* ptr = src.ptr(y); - template - void sumCaller(const PtrStepSzb src, PtrStepb buf, double* sum, int cn) + for (int j = 0, x = x0; j < twidth && x < src.cols; ++j, x += blockDim.x) { - typedef typename SumType::R R; - - dim3 threads, grid; - estimateThreadCfg(src.cols, src.rows, threads, grid); - setKernelConsts(src.cols, src.rows, threads, grid); - - switch (cn) - { - case 1: - sumKernel, threads_x * threads_y><<>>( - src, (typename TypeVec::vec_type*)buf.ptr(0)); - break; - case 2: - sumKernel_C2, threads_x * threads_y><<>>( - src, (typename TypeVec::vec_type*)buf.ptr(0)); - break; - case 3: - sumKernel_C3, threads_x * threads_y><<>>( - src, (typename TypeVec::vec_type*)buf.ptr(0)); - break; - case 4: - sumKernel_C4, threads_x * threads_y><<>>( - src, (typename TypeVec::vec_type*)buf.ptr(0)); - break; - } - cudaSafeCall( cudaGetLastError() ); - - cudaSafeCall( cudaDeviceSynchronize() ); + const T srcVal = ptr[x]; - R result[4] = {0, 0, 0, 0}; - cudaSafeCall(cudaMemcpy(&result, buf.ptr(0), sizeof(R) * cn, cudaMemcpyDeviceToHost)); - - sum[0] = result[0]; - sum[1] = result[1]; - sum[2] = result[2]; - sum[3] = result[3]; + mycount += (srcVal != 0); } + } - template void sumCaller(const PtrStepSzb, PtrStepb, double*, int); - template void sumCaller(const PtrStepSzb, PtrStepb, double*, int); - template void sumCaller(const PtrStepSzb, PtrStepb, double*, int); - template void sumCaller(const PtrStepSzb, PtrStepb, double*, int); - template void sumCaller(const PtrStepSzb, PtrStepb, double*, int); - template void sumCaller(const PtrStepSzb, PtrStepb, double*, int); - + device::reduce(scount, mycount, tid, plus()); - template - void absSumMultipassCaller(const PtrStepSzb src, PtrStepb buf, double* sum, int cn) - { - typedef typename SumType::R R; + #if __CUDA_ARCH__ >= 200 + if (tid == 0) + ::atomicAdd(count, mycount); + #else + if (tid == 0) + { + count[bid] = mycount; - dim3 threads, grid; - estimateThreadCfg(src.cols, src.rows, threads, grid); - setKernelConsts(src.cols, src.rows, threads, grid); + __threadfence(); - switch (cn) - { - case 1: - sumKernel, threads_x * threads_y><<>>( - src, (typename TypeVec::vec_type*)buf.ptr(0)); - cudaSafeCall( cudaGetLastError() ); - - sumPass2Kernel<<<1, threads_x * threads_y>>>( - (typename TypeVec::vec_type*)buf.ptr(0), grid.x * grid.y); - cudaSafeCall( cudaGetLastError() ); - - break; - case 2: - sumKernel_C2, threads_x * threads_y><<>>( - src, (typename TypeVec::vec_type*)buf.ptr(0)); - cudaSafeCall( cudaGetLastError() ); - - sumPass2Kernel_C2<<<1, threads_x * threads_y>>>( - (typename TypeVec::vec_type*)buf.ptr(0), grid.x * grid.y); - cudaSafeCall( cudaGetLastError() ); - - break; - case 3: - sumKernel_C3, threads_x * threads_y><<>>( - src, (typename TypeVec::vec_type*)buf.ptr(0)); - cudaSafeCall( cudaGetLastError() ); - - sumPass2Kernel_C3<<<1, threads_x * threads_y>>>( - (typename TypeVec::vec_type*)buf.ptr(0), grid.x * grid.y); - cudaSafeCall( cudaGetLastError() ); - - break; - case 4: - sumKernel_C4, threads_x * threads_y><<>>( - src, (typename TypeVec::vec_type*)buf.ptr(0)); - cudaSafeCall( cudaGetLastError() ); - - sumPass2Kernel_C4<<<1, threads_x * threads_y>>>( - (typename TypeVec::vec_type*)buf.ptr(0), grid.x * grid.y); - cudaSafeCall( cudaGetLastError() ); - - break; - } - cudaSafeCall( cudaDeviceSynchronize() ); - - R result[4] = {0, 0, 0, 0}; - cudaSafeCall(cudaMemcpy(result, buf.ptr(0), sizeof(R) * cn, cudaMemcpyDeviceToHost)); + unsigned int ticket = ::atomicInc(&blocks_finished, gridDim.x * gridDim.y); + is_last = (ticket == gridDim.x * gridDim.y - 1); + } - sum[0] = result[0]; - sum[1] = result[1]; - sum[2] = result[2]; - sum[3] = result[3]; - } + __syncthreads(); - template void absSumMultipassCaller(const PtrStepSzb, PtrStepb, double*, int); - template void absSumMultipassCaller(const PtrStepSzb, PtrStepb, double*, int); - template void absSumMultipassCaller(const PtrStepSzb, PtrStepb, double*, int); - template void absSumMultipassCaller(const PtrStepSzb, PtrStepb, double*, int); - template void absSumMultipassCaller(const PtrStepSzb, PtrStepb, double*, int); - template void absSumMultipassCaller(const PtrStepSzb, PtrStepb, double*, int); + if (is_last) + { + mycount = tid < gridDim.x * gridDim.y ? count[tid] : 0; + device::reduce(scount, mycount, tid, plus()); - template - void absSumCaller(const PtrStepSzb src, PtrStepb buf, double* sum, int cn) + if (tid == 0) { - typedef typename SumType::R R; - - dim3 threads, grid; - estimateThreadCfg(src.cols, src.rows, threads, grid); - setKernelConsts(src.cols, src.rows, threads, grid); - - switch (cn) - { - case 1: - sumKernel, threads_x * threads_y><<>>( - src, (typename TypeVec::vec_type*)buf.ptr(0)); - break; - case 2: - sumKernel_C2, threads_x * threads_y><<>>( - src, (typename TypeVec::vec_type*)buf.ptr(0)); - break; - case 3: - sumKernel_C3, threads_x * threads_y><<>>( - src, (typename TypeVec::vec_type*)buf.ptr(0)); - break; - case 4: - sumKernel_C4, threads_x * threads_y><<>>( - src, (typename TypeVec::vec_type*)buf.ptr(0)); - break; - } - cudaSafeCall( cudaGetLastError() ); - - cudaSafeCall( cudaDeviceSynchronize() ); - - R result[4] = {0, 0, 0, 0}; - cudaSafeCall(cudaMemcpy(result, buf.ptr(0), sizeof(R) * cn, cudaMemcpyDeviceToHost)); + count[0] = mycount; - sum[0] = result[0]; - sum[1] = result[1]; - sum[2] = result[2]; - sum[3] = result[3]; + blocks_finished = 0; } + } + #endif + } - template void absSumCaller(const PtrStepSzb, PtrStepb, double*, int); - template void absSumCaller(const PtrStepSzb, PtrStepb, double*, int); - template void absSumCaller(const PtrStepSzb, PtrStepb, double*, int); - template void absSumCaller(const PtrStepSzb, PtrStepb, double*, int); - template void absSumCaller(const PtrStepSzb, PtrStepb, double*, int); - template void absSumCaller(const PtrStepSzb, PtrStepb, double*, int); - + const int threads_x = 32; + const int threads_y = 8; - template - void sqrSumMultipassCaller(const PtrStepSzb src, PtrStepb buf, double* sum, int cn) - { - typedef typename SumType::R R; + void getLaunchCfg(int cols, int rows, dim3& block, dim3& grid) + { + block = dim3(threads_x, threads_y); - dim3 threads, grid; - estimateThreadCfg(src.cols, src.rows, threads, grid); - setKernelConsts(src.cols, src.rows, threads, grid); + grid = dim3(divUp(cols, block.x * block.y), + divUp(rows, block.y * block.x)); - switch (cn) - { - case 1: - sumKernel, threads_x * threads_y><<>>( - src, (typename TypeVec::vec_type*)buf.ptr(0)); - cudaSafeCall( cudaGetLastError() ); - - sumPass2Kernel<<<1, threads_x * threads_y>>>( - (typename TypeVec::vec_type*)buf.ptr(0), grid.x * grid.y); - cudaSafeCall( cudaGetLastError() ); - - break; - case 2: - sumKernel_C2, threads_x * threads_y><<>>( - src, (typename TypeVec::vec_type*)buf.ptr(0)); - cudaSafeCall( cudaGetLastError() ); - - sumPass2Kernel_C2<<<1, threads_x * threads_y>>>( - (typename TypeVec::vec_type*)buf.ptr(0), grid.x * grid.y); - cudaSafeCall( cudaGetLastError() ); - - break; - case 3: - sumKernel_C3, threads_x * threads_y><<>>( - src, (typename TypeVec::vec_type*)buf.ptr(0)); - cudaSafeCall( cudaGetLastError() ); - - sumPass2Kernel_C3<<<1, threads_x * threads_y>>>( - (typename TypeVec::vec_type*)buf.ptr(0), grid.x * grid.y); - cudaSafeCall( cudaGetLastError() ); - - break; - case 4: - sumKernel_C4, threads_x * threads_y><<>>( - src, (typename TypeVec::vec_type*)buf.ptr(0)); - cudaSafeCall( cudaGetLastError() ); - - sumPass2Kernel_C4<<<1, threads_x * threads_y>>>( - (typename TypeVec::vec_type*)buf.ptr(0), grid.x * grid.y); - cudaSafeCall( cudaGetLastError() ); - - break; - } - cudaSafeCall( cudaDeviceSynchronize() ); + grid.x = ::min(grid.x, block.x); + grid.y = ::min(grid.y, block.y); + } - R result[4] = {0, 0, 0, 0}; - cudaSafeCall(cudaMemcpy(result, buf.ptr(0), sizeof(R) * cn, cudaMemcpyDeviceToHost)); + void getBufSize(int cols, int rows, int& bufcols, int& bufrows) + { + dim3 block, grid; + getLaunchCfg(cols, rows, block, grid); - sum[0] = result[0]; - sum[1] = result[1]; - sum[2] = result[2]; - sum[3] = result[3]; - } + bufcols = grid.x * grid.y * sizeof(int); + bufrows = 1; + } - template void sqrSumMultipassCaller(const PtrStepSzb, PtrStepb, double*, int); - template void sqrSumMultipassCaller(const PtrStepSzb, PtrStepb, double*, int); - template void sqrSumMultipassCaller(const PtrStepSzb, PtrStepb, double*, int); - template void sqrSumMultipassCaller(const PtrStepSzb, PtrStepb, double*, int); - template void sqrSumMultipassCaller(const PtrStepSzb, PtrStepb, double*, int); - template void sqrSumMultipassCaller(const PtrStepSzb, PtrStepb, double*, int); + template + int run(const PtrStepSzb src, PtrStep buf) + { + dim3 block, grid; + getLaunchCfg(src.cols, src.rows, block, grid); + const int twidth = divUp(divUp(src.cols, grid.x), block.x); + const int theight = divUp(divUp(src.rows, grid.y), block.y); - template - void sqrSumCaller(const PtrStepSzb src, PtrStepb buf, double* sum, int cn) - { - typedef double R; + unsigned int* count_buf = buf.ptr(0); - dim3 threads, grid; - estimateThreadCfg(src.cols, src.rows, threads, grid); - setKernelConsts(src.cols, src.rows, threads, grid); + cudaSafeCall( cudaMemset(count_buf, 0, sizeof(unsigned int)) ); - switch (cn) - { - case 1: - sumKernel, threads_x * threads_y><<>>( - src, (typename TypeVec::vec_type*)buf.ptr(0)); - break; - case 2: - sumKernel_C2, threads_x * threads_y><<>>( - src, (typename TypeVec::vec_type*)buf.ptr(0)); - break; - case 3: - sumKernel_C3, threads_x * threads_y><<>>( - src, (typename TypeVec::vec_type*)buf.ptr(0)); - break; - case 4: - sumKernel_C4, threads_x * threads_y><<>>( - src, (typename TypeVec::vec_type*)buf.ptr(0)); - break; - } - cudaSafeCall( cudaGetLastError() ); + kernel<<>>((PtrStepSz) src, count_buf, twidth, theight); + cudaSafeCall( cudaGetLastError() ); - cudaSafeCall( cudaDeviceSynchronize() ); + cudaSafeCall( cudaDeviceSynchronize() ); - R result[4] = {0, 0, 0, 0}; - cudaSafeCall(cudaMemcpy(result, buf.ptr(0), sizeof(R) * cn, cudaMemcpyDeviceToHost)); + unsigned int count; + cudaSafeCall(cudaMemcpy(&count, count_buf, sizeof(unsigned int), cudaMemcpyDeviceToHost)); - sum[0] = result[0]; - sum[1] = result[1]; - sum[2] = result[2]; - sum[3] = result[3]; - } + return count; + } - template void sqrSumCaller(const PtrStepSzb, PtrStepb, double*, int); - template void sqrSumCaller(const PtrStepSzb, PtrStepb, double*, int); - template void sqrSumCaller(const PtrStepSzb, PtrStepb, double*, int); - template void sqrSumCaller(const PtrStepSzb, PtrStepb, double*, int); - template void sqrSumCaller(const PtrStepSzb, PtrStepb, double*, int); - template void sqrSumCaller(const PtrStepSzb, PtrStepb, double*, int); - } // namespace sum + template int run(const PtrStepSzb src, PtrStep buf); + template int run(const PtrStepSzb src, PtrStep buf); + template int run(const PtrStepSzb src, PtrStep buf); + template int run(const PtrStepSzb src, PtrStep buf); + template int run(const PtrStepSzb src, PtrStep buf); + template int run(const PtrStepSzb src, PtrStep buf); + template int run(const PtrStepSzb src, PtrStep buf); +} - ////////////////////////////////////////////////////////////////////////////// - // reduce +////////////////////////////////////////////////////////////////////////////// +// reduce - template struct SumReductor +namespace reduce +{ + struct Sum + { + template + __device__ __forceinline__ T startValue() const { - __device__ __forceinline__ S startValue() const - { - return 0; - } - - __device__ __forceinline__ SumReductor(const SumReductor& other){} - __device__ __forceinline__ SumReductor(){} - - __device__ __forceinline__ S operator ()(volatile S a, volatile S b) const - { - return a + b; - } - - __device__ __forceinline__ S result(S r, double) const - { - return r; - } - }; + return VecTraits::all(0); + } - template struct AvgReductor + template + __device__ __forceinline__ T operator ()(T a, T b) const { - __device__ __forceinline__ S startValue() const - { - return 0; - } + return a + b; + } - __device__ __forceinline__ AvgReductor(const AvgReductor& other){} - __device__ __forceinline__ AvgReductor(){} + template + __device__ __forceinline__ T result(T r, double) const + { + return r; + } - __device__ __forceinline__ S operator ()(volatile S a, volatile S b) const - { - return a + b; - } + __device__ __forceinline__ Sum() {} + __device__ __forceinline__ Sum(const Sum&) {} + }; - __device__ __forceinline__ double result(S r, double sz) const - { - return r / sz; - } - }; + struct Avg + { + template + __device__ __forceinline__ T startValue() const + { + return VecTraits::all(0); + } - template struct MinReductor + template + __device__ __forceinline__ T operator ()(T a, T b) const { - __device__ __forceinline__ S startValue() const - { - return numeric_limits::max(); - } + return a + b; + } - __device__ __forceinline__ MinReductor(const MinReductor& other){} - __device__ __forceinline__ MinReductor(){} + template + __device__ __forceinline__ typename TypeVec::cn>::vec_type result(T r, double sz) const + { + return r / sz; + } - template __device__ __forceinline__ T operator ()(volatile T a, volatile T b) const - { - return saturate_cast(::min(a, b)); - } - __device__ __forceinline__ float operator ()(volatile float a, volatile float b) const - { - return ::fmin(a, b); - } + __device__ __forceinline__ Avg() {} + __device__ __forceinline__ Avg(const Avg&) {} + }; - __device__ __forceinline__ S result(S r, double) const - { - return r; - } - }; + struct Min + { + template + __device__ __forceinline__ T startValue() const + { + return VecTraits::all(numeric_limits::elem_type>::max()); + } - template struct MaxReductor + template + __device__ __forceinline__ T operator ()(T a, T b) const { - __device__ __forceinline__ S startValue() const - { - return numeric_limits::min(); - } + minimum minOp; + return minOp(a, b); + } - __device__ __forceinline__ MaxReductor(const MaxReductor& other){} - __device__ __forceinline__ MaxReductor(){} + template + __device__ __forceinline__ T result(T r, double) const + { + return r; + } - template __device__ __forceinline__ int operator ()(volatile T a, volatile T b) const - { - return ::max(a, b); - } - __device__ __forceinline__ float operator ()(volatile float a, volatile float b) const - { - return ::fmax(a, b); - } + __device__ __forceinline__ Min() {} + __device__ __forceinline__ Min(const Min&) {} + }; - __device__ __forceinline__ S result(S r, double) const - { - return r; - } - }; + struct Max + { + template + __device__ __forceinline__ T startValue() const + { + return VecTraits::all(-numeric_limits::elem_type>::max()); + } - template __global__ void reduceRows(const PtrStepSz src, D* dst, const Op op) + template + __device__ __forceinline__ T operator ()(T a, T b) const { - __shared__ S smem[16 * 16]; + maximum maxOp; + return maxOp(a, b); + } - const int x = blockIdx.x * 16 + threadIdx.x; + template + __device__ __forceinline__ T result(T r, double) const + { + return r; + } - S myVal = op.startValue(); + __device__ __forceinline__ Max() {} + __device__ __forceinline__ Max(const Max&) {} + }; - if (x < src.cols) - { - for (int y = threadIdx.y; y < src.rows; y += 16) - myVal = op(myVal, src.ptr(y)[x]); - } + /////////////////////////////////////////////////////////// - smem[threadIdx.x * 16 + threadIdx.y] = myVal; - __syncthreads(); + template + __global__ void rowsKernel(const PtrStepSz src, D* dst, const Op op) + { + __shared__ S smem[16 * 16]; - if (threadIdx.x < 8) - { - volatile S* srow = smem + threadIdx.y * 16; - srow[threadIdx.x] = op(srow[threadIdx.x], srow[threadIdx.x + 8]); - srow[threadIdx.x] = op(srow[threadIdx.x], srow[threadIdx.x + 4]); - srow[threadIdx.x] = op(srow[threadIdx.x], srow[threadIdx.x + 2]); - srow[threadIdx.x] = op(srow[threadIdx.x], srow[threadIdx.x + 1]); - } - __syncthreads(); + const int x = blockIdx.x * 16 + threadIdx.x; - if (threadIdx.y == 0 && x < src.cols) - dst[x] = saturate_cast(op.result(smem[threadIdx.x * 16], src.rows)); - } + S myVal = op.template startValue(); - template