您的位置:首页 > 编程语言 > C语言/C++

OpenCV 笔记 (C++模板template)

2014-04-25 10:52 483 查看
在OpenCV中大量使用了模板编程,因为图像的数据类型有8字节,16字节,32字节,float,double等多种数据类型,如果对每一种数据类型都写一遍函数,这是很糟糕的,像Point_,Rect_等等很多的基础类都是用模板写出来的,现在补一下C++模板在OpenCV中的使用方式。
1. 模板函数基本写法template<typename(或class) T>T fuc(T x, T y){ T x; //……}typename或class没有区别,这里建议使用typename,一是意思明确,就是类型名;二是class在C++中已经是一个定义类的关键字,不宜再使用它。OpenCV中的实例:template<typename T> static voidinRange_(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函数。

来自为知笔记(Wiz)
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: