1. 模板函数
基本写法
template<typename(或class) T>
T fuc(T x, T y)
{
T x;
//……
}
typename或class没有区别,这里建议使用typename,一是意思明确,就是类型名;二是class在C++中已经是一个定义类的关键字,不宜再使用它。
OpenCV中的实例:
template<typename T> static void
inRange_(const T* src1, size_t step1, const T* src2, size_t step2,
const T* src3, size_t step3, uchar* dst, size_t step,
Size size)
{
step1 /= sizeof(src1[0]);
step2 /= sizeof(src2[0]);
step3 /= sizeof(src3[0]);
for( ; size.height--; src1 += step1, src2 += step2, src3 += step3, dst += step )
{
int x = 0;
#if CV_ENABLE_UNROLLED
for( ; x <= size.width - 4; x += 4 )
{
int t0, t1;
t0 = src2[x] <= src1[x] && src1[x] <= src3[x];
t1 = src2[x+1] <= src1[x+1] && src1[x+1] <= src3[x+1];
dst[x] = (uchar)-t0; dst[x+1] = (uchar)-t1;
t0 = src2[x+2] <= src1[x+2] && src1[x+2] <= src3[x+2];
t1 = src2[x+3] <= src1[x+3] && src1[x+3] <= src3[x+3];
dst[x+2] = (uchar)-t0; dst[x+3] = (uchar)-t1;
}
#endif
for( ; x < size.width; x++ )
dst[x] = (uchar)-(src2[x] <= src1[x] && src1[x] <= src3[x]);
}
}
static void inRange8u(const uchar* src1, size_t step1, const uchar* src2, size_t step2,
const uchar* src3, size_t step3, uchar* dst, size_t step, Size size)
{
inRange_(src1, step1, src2, step2, src3, step3, dst, step, size);
}
static void inRange8s(const schar* src1, size_t step1, const schar* src2, size_t step2,
const schar* src3, size_t step3, uchar* dst, size_t step, Size size)
{
inRange_(src1, step1, src2, step2, src3, step3, dst, step, size);
}
static void inRange16u(const ushort* src1, size_t step1, const ushort* src2, size_t step2,
const ushort* src3, size_t step3, uchar* dst, size_t step, Size size)
{
inRange_(src1, step1, src2, step2, src3, step3, dst, step, size);
}
static void inRange16s(const short* src1, size_t step1, const short* src2, size_t step2,
const short* src3, size_t step3, uchar* dst, size_t step, Size size)
{
inRange_(src1, step1, src2, step2, src3, step3, dst, step, size);
}
static void inRange32s(const int* src1, size_t step1, const int* src2, size_t step2,
const int* src3, size_t step3, uchar* dst, size_t step, Size size)
{
inRange_(src1, step1, src2, step2, src3, step3, dst, step, size);
}
static void inRange32f(const float* src1, size_t step1, const float* src2, size_t step2,
const float* src3, size_t step3, uchar* dst, size_t step, Size size)
{
inRange_(src1, step1, src2, step2, src3, step3, dst, step, size);
}
static void inRange64f(const double* src1, size_t step1, const double* src2, size_t step2,
const double* src3, size_t step3, uchar* dst, size_t step, Size size)
{
inRange_(src1, step1, src2, step2, src3, step3, dst, step, size);
}
static void inRangeReduce(const uchar* src, uchar* dst, size_t len, int cn)
{
int k = cn % 4 ? cn % 4 : 4;
size_t i, j;
if( k == 1 )
for( i = j = 0; i < len; i++, j += cn )
dst[i] = src[j];
else if( k == 2 )
for( i = j = 0; i < len; i++, j += cn )
dst[i] = src[j] & src[j+1];
else if( k == 3 )
for( i = j = 0; i < len; i++, j += cn )
dst[i] = src[j] & src[j+1] & src[j+2];
else
for( i = j = 0; i < len; i++, j += cn )
dst[i] = src[j] & src[j+1] & src[j+2] & src[j+3];
for( ; k < cn; k += 4 )
{
for( i = 0, j = k; i < len; i++, j += cn )
dst[i] &= src[j] & src[j+1] & src[j+2] & src[j+3];
}
}
typedef void (*InRangeFunc)( const uchar* src1, size_t step1, const uchar* src2, size_t step2,
const uchar* src3, size_t step3, uchar* dst, size_t step, Size sz );
static InRangeFunc getInRangeFunc(int depth)
{
static InRangeFunc inRangeTab[] =
{
(InRangeFunc)GET_OPTIMIZED(inRange8u), (InRangeFunc)GET_OPTIMIZED(inRange8s), (InRangeFunc)GET_OPTIMIZED(inRange16u),
(InRangeFunc)GET_OPTIMIZED(inRange16s), (InRangeFunc)GET_OPTIMIZED(inRange32s), (InRangeFunc)GET_OPTIMIZED(inRange32f),
(InRangeFunc)inRange64f, 0
};
return inRangeTab[depth];
}
}
void cv::inRange(InputArray _src, InputArray _lowerb,
InputArray _upperb, OutputArray _dst)
{
int skind = _src.kind(), lkind = _lowerb.kind(), ukind = _upperb.kind();
Mat src = _src.getMat(), lb = _lowerb.getMat(), ub = _upperb.getMat();
bool lbScalar = false, ubScalar = false;
if( (lkind == _InputArray::MATX && skind != _InputArray::MATX) ||
src.size != lb.size || src.type() != lb.type() )
{
if( !checkScalar(lb, src.type(), lkind, skind) )
CV_Error( CV_StsUnmatchedSizes,
"The lower bounary is neither an array of the same size and same type as src, nor a scalar");
lbScalar = true;
}
if( (ukind == _InputArray::MATX && skind != _InputArray::MATX) ||
src.size != ub.size || src.type() != ub.type() )
{
if( !checkScalar(ub, src.type(), ukind, skind) )
CV_Error( CV_StsUnmatchedSizes,
"The upper bounary is neither an array of the same size and same type as src, nor a scalar");
ubScalar = true;
}
CV_Assert( ((int)lbScalar ^ (int)ubScalar) == 0 );
int cn = src.channels(), depth = src.depth();
size_t esz = src.elemSize();
size_t blocksize0 = (size_t)(BLOCK_SIZE + esz-1)/esz;
_dst.create(src.dims, src.size, CV_8U);
Mat dst = _dst.getMat();
InRangeFunc func = getInRangeFunc(depth);
const Mat* arrays_sc[] = { &src, &dst, 0 };
const Mat* arrays_nosc[] = { &src, &dst, &lb, &ub, 0 };
uchar* ptrs[4];
NAryMatIterator it(lbScalar && ubScalar ? arrays_sc : arrays_nosc, ptrs);
size_t total = it.size, blocksize = std::min(total, blocksize0);
AutoBuffer<uchar> _buf(blocksize*(((int)lbScalar + (int)ubScalar)*esz + cn) + 2*cn*sizeof(int) + 128);
uchar *buf = _buf, *mbuf = buf, *lbuf = 0, *ubuf = 0;
buf = alignPtr(buf + blocksize*cn, 16);
if( lbScalar && ubScalar )
{
lbuf = buf;
ubuf = buf = alignPtr(buf + blocksize*esz, 16);
CV_Assert( lb.type() == ub.type() );
int scdepth = lb.depth();
if( scdepth != depth && depth < CV_32S )
{
int* ilbuf = (int*)alignPtr(buf + blocksize*esz, 16);
int* iubuf = ilbuf + cn;
BinaryFunc sccvtfunc = getConvertFunc(scdepth, CV_32S);
sccvtfunc(lb.data, 0, 0, 0, (uchar*)ilbuf, 0, Size(cn, 1), 0);
sccvtfunc(ub.data, 0, 0, 0, (uchar*)iubuf, 0, Size(cn, 1), 0);
int minval = cvRound(getMinVal(depth)), maxval = cvRound(getMaxVal(depth));
for( int k = 0; k < cn; k++ )
{
if( ilbuf[k] > iubuf[k] || ilbuf[k] > maxval || iubuf[k] < minval )
ilbuf[k] = minval+1, iubuf[k] = minval;
}
lb = Mat(cn, 1, CV_32S, ilbuf);
ub = Mat(cn, 1, CV_32S, iubuf);
}
convertAndUnrollScalar( lb, src.type(), lbuf, blocksize );
convertAndUnrollScalar( ub, src.type(), ubuf, blocksize );
}
for( size_t i = 0; i < it.nplanes; i++, ++it )
{
for( size_t j = 0; j < total; j += blocksize )
{
int bsz = (int)MIN(total - j, blocksize);
size_t delta = bsz*esz;
uchar *lptr = lbuf, *uptr = ubuf;
if( !lbScalar )
{
lptr = ptrs[2];
ptrs[2] += delta;
}
if( !ubScalar )
{
int idx = !lbScalar ? 3 : 2;
uptr = ptrs[idx];
ptrs[idx] += delta;
}
func( ptrs[0], 0, lptr, 0, uptr, 0, cn == 1 ? ptrs[1] : mbuf, 0, Size(bsz*cn, 1));
if( cn > 1 )
inRangeReduce(mbuf, ptrs[1], bsz, cn);
ptrs[0] += delta;
ptrs[1] += bsz;
}
}
}
从这段代码可以看出,OpenCV中先定义一个模板函数,然后再定义一组出入参和返回值一样的函数,将这些函数放入一个函数指针数组,根据不同的数据类型,取不同的函数调用,最后在接口cv::inRange中直接使用函数指针。这样的写法在OpenCV中十分普遍,特别是在有原来的C代码的地方。这样的用法在cv::add
2. 模板类
写法
template <typename T>
class Foo
{
……
}
OpenCV实例:
template<typename _Tp> class Point_
{
public:
typedef _Tp value_type;
// various constructors
Point_();
Point_(_Tp _x, _Tp _y);
Point_(const Point_& pt);
Point_(const CvPoint& pt);
Point_(const CvPoint2D32f& pt);
Point_(const Size_<_Tp>& sz);
Point_(const Vec<_Tp, 2>& v);
Point_& operator = (const Point_& pt);
//! conversion to another data type
template<typename _Tp2> operator Point_<_Tp2>() const;
//! conversion to the old-style C structures
operator CvPoint() const;
operator CvPoint2D32f() const;
operator Vec<_Tp, 2>() const;
//! dot product
_Tp dot(const Point_& pt) const;
//! dot product computed in double-precision arithmetics
double ddot(const Point_& pt) const;
//! cross-product
double cross(const Point_& pt) const;
//! checks whether the point is inside the specified rectangle
bool inside(const Rect_<_Tp>& r) const;
_Tp x, y; //< the point coordinates
};
typedef Point_<int> Point2i;
typedef Point2i Point;
3. 转载一段关于仿函数的内容
仿函数
仿函数这个词经常会出现在模板库里(比如 STL),那么什么是仿函数呢?
顾名思义:仿函数就是能像函数一样工作的东西,请原谅我用东西这样一个代词,下面我会慢慢解释。
void dosome( int i )
这个 dosome 是一个函数,我们可以这样来使用它: dosome(5);
那么,有什么东西可以像这样工作么?
答案1:重载了 () 操作符的对象,因此,这里需要明确两点:
1 仿函数不是函数,它是个类;
2 仿函数重载了()运算符,使得它的对你可以像函数那样子调用
代码的形式好像是在调用函数,比如:
struct DoSome
{
void operator()( int i );
}
DoSome dosome;
这里类(对 C++ 来说,struct 和类是相同的) 重载了()操作符,因此它的实例 dosome 可以这样用 dosome(5);
和上面的函数调用一模一样,不是么?所以 dosome 就是一个仿函数了。
实际上还有答案2:
函数指针指向的对象。
typedef void( *DoSomePtr )( int );
typedef void( DoSome )( int );
DoSomePtr *ptr=&func;
DoSome& dosome=*ptr;
dosome(5); // 这里又和函数调用一模一样了。
当然,答案3 成员函数指针指向的成员函数就是意料之中的答案了。
仿函数的用处
不管是对象还是函数指针等等,它们都是可以被作为参数传递,或者被作为变量保存的。因此我们就可以把一个仿函数传递给一个函数,
由这个函数根据需要来调用这个仿函数(有点类似回调)。
STL 模板库中,大量使用了这种技巧,来实现库的“灵活”。
比如:
for_each, 它的源代码大致如下:
template< typename Iterator, typename Functor >
void for_each( Iterator begin, Iterator end, Fucntor func )
{
for( ; begin!=end; begin++ )
func( *begin );
}
这个 for 循环遍历了容器中的每一个元素,对每个元素调用了仿函数 func,这样就实现了 对“每个元素做同样的事”这样一种编程的思想。
特别的,如果仿函数是一个对象,这个对象是可以有成员变量的,这就让 仿函数有了“状态”,从而实现了更高的灵活性。
OpenCV中也是使用了很多仿函数的形式。
为便于说明,代码有删减
template <typename Cvt>
class CvtColorLoop_Invoker : public ParallelLoopBody
{
typedef typename Cvt::channel_type _Tp;
public:
CvtColorLoop_Invoker(const Mat& _src, Mat& _dst, const Cvt& _cvt) :
ParallelLoopBody(), src(_src), dst(_dst), cvt(_cvt)
{
}
virtual void operator()(const Range& range) const
{
const uchar* yS = src.ptr<uchar>(range.start);
uchar* yD = dst.ptr<uchar>(range.start);
for( int i = range.start; i < range.end; ++i, yS += src.step, yD += dst.step )
cvt((const _Tp*)yS, (_Tp*)yD, src.cols);
}
private:
const Mat& src;
Mat& dst;
const Cvt& cvt;
const CvtColorLoop_Invoker& operator= (const CvtColorLoop_Invoker&);
};
template <typename Cvt>
void CvtColorLoop(const Mat& src, Mat& dst, const Cvt& cvt)
{
parallel_for_(Range(0, src.rows), CvtColorLoop_Invoker<Cvt>(src, dst, cvt), src.total()/(double)(1<<16) );
}
template<typename _Tp>
struct Gray2RGB
{
typedef _Tp channel_type;
Gray2RGB(int _dstcn) : dstcn(_dstcn) {}
void operator()(const _Tp* src, _Tp* dst, int n) const
{
if( dstcn == 3 )
for( int i = 0; i < n; i++, dst += 3 )
{
dst[0] = dst[1] = dst[2] = src[i];
}
else
{
_Tp alpha = ColorChannel<_Tp>::max();
for( int i = 0; i < n; i++, dst += 4 )
{
dst[0] = dst[1] = dst[2] = src[i];
dst[3] = alpha;
}
}
}
int dstcn;
};
template<typename _Tp> struct RGB2Gray
{
typedef _Tp channel_type;
RGB2Gray(int _srccn, int blueIdx, const float* _coeffs) : srccn(_srccn)
{
static const float coeffs0[] = { 0.299f, 0.587f, 0.114f };
memcpy( coeffs, _coeffs ? _coeffs : coeffs0, 3*sizeof(coeffs[0]) );
if(blueIdx == 0)
std::swap(coeffs[0], coeffs[2]);
}
void operator()(const _Tp* src, _Tp* dst, int n) const
{
int scn = srccn;
float cb = coeffs[0], cg = coeffs[1], cr = coeffs[2];
for(int i = 0; i < n; i++, src += scn)
dst[i] = saturate_cast<_Tp>(src[0]*cb + src[1]*cg + src[2]*cr);
}
int srccn;
float coeffs[3];
};
//为便于说明,代码有删减
void cv::cvtColor( InputArray _src, OutputArray _dst, int code, int dcn )
{
Mat src = _src.getMat(), dst;
Size sz = src.size();
int scn = src.channels(), depth = src.depth(), bidx;
CV_Assert( depth == CV_8U || depth == CV_16U || depth == CV_32F );
switch( code )
{
//为便于说明,代码有删减
//...............................................
case CV_BGR2GRAY: case CV_BGRA2GRAY: case CV_RGB2GRAY: case CV_RGBA2GRAY:
CV_Assert( scn == 3 || scn == 4 );
_dst.create(sz, CV_MAKETYPE(depth, 1));
dst = _dst.getMat();
bidx = code == CV_BGR2GRAY || code == CV_BGRA2GRAY ? 0 : 2;
if( depth == CV_8U )
{
CvtColorLoop(src, dst, RGB2Gray<uchar>(scn, bidx, 0));
}
else if( depth == CV_16U )
CvtColorLoop(src, dst, RGB2Gray<ushort>(scn, bidx, 0));
else
CvtColorLoop(src, dst, RGB2Gray<float>(scn, bidx, 0));
break;
case CV_BGR5652GRAY: case CV_BGR5552GRAY:
CV_Assert( scn == 2 && depth == CV_8U );
_dst.create(sz, CV_8UC1);
dst = _dst.getMat();
CvtColorLoop(src, dst, RGB5x52Gray(code == CV_BGR5652GRAY ? 6 : 5));
break;
case CV_GRAY2BGR: case CV_GRAY2BGRA:
if( dcn <= 0 ) dcn = (code==CV_GRAY2BGRA) ? 4 : 3;
CV_Assert( scn == 1 && (dcn == 3 || dcn == 4));
_dst.create(sz, CV_MAKETYPE(depth, dcn));
dst = _dst.getMat();
if( depth == CV_8U )
{
CvtColorLoop(src, dst, Gray2RGB<uchar>(dcn));
}
else if( depth == CV_16U )
CvtColorLoop(src, dst, Gray2RGB<ushort>(dcn));
else
CvtColorLoop(src, dst, Gray2RGB<float>(dcn));
break;
//................................................
default:
CV_Error( CV_StsBadFlag, "Unknown/unsupported color conversion code" );
}
}
这是cvtColor的基本实现方式,
类CvtColorLoop_Invoker实现了virtual void operator()(const Range& range) const
这是一个仿函数形式,并且它将在CvtColorLoop函数的parallel_for_中调用,在template <typename Cvt>中的Cvt代表一个实现了仿函数形式的struct,并在cvt((const _Tp*)yS, (_Tp*)yD, src.cols)调用了,而template<typename _Tp>struct Gray2RGB和template<typename _Tp> struct RGB2Gray就是对应于Cvt结构的两个struct,这两个struct都实现了operator()。
在这种编程的方式中,比如cvtColor这个颜色空间转换函数,而这种转换有很多种类型,比如RGB2GRAY, RGB2HSV,RGB2HLS等等,但是对像素的操作方式确是类似的,所以这里将每一种转换都独立出来,写在一个struct中,并且都实现了operator()(这个operator()有同样的参数),然后将这些不同的转换放在一个统一的对像素的操作流程中,这里是CvtColorLoop函数。