zoukankan      html  css  js  c++  java
  • OpenCV 笔记 (C++模板template)

    在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 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));
                forint 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);
            forint 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 )
                forint i = 0; i < n; i++, dst += 3 )
                {
                    dst[0] = dst[1] = dst[2] = src[i];
                }
            else
            {
                _Tp alpha = ColorChannel<_Tp>::max();
                forint 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函数。





  • 相关阅读:
    Prism 4 文档 ---第6章 高级MVVM场景
    Prism 4 文档 ---第5章 实现MVVM模式
    Prism 4 文档 ---第4章 模块化应用程序开发
    Prism 4 文档 ---第3章 管理组件间的依赖关系
    Prism 4 文档 ---第2章:初始化Prism应用程序
    阿里人都在使用的在线诊断工具—Arthas
    开发人员如何规范你的Git commit?
    重磅:Java 16 正式发布了!
    MySQL 要分表分库怎么进行数据切分?
    Kafka 中所谓的 ‘零拷贝’ 技术到底是什么?
  • 原文地址:https://www.cnblogs.com/fireae/p/3688221.html
Copyright © 2011-2022 走看看