Dlib源码解析之一 matrix和array2d和image_view

image_view解析

之前使用dlib中load_image方法读取图像的时候,发现参数用matrix和array2d都是可以的。

使用matrix来表示图像:

    matrix<rgb_pixel> img;
    load_image(img, argv[1]);

使用array2d来表示图像:

    array2d<unsigned char> img;
    load_image(img, argv[i]);

查看load_image源码,发现最终到达下面一个方法:

    template <
        typename image_type 
        >
    void load_bmp (
        image_type& image_,
        std::istream& in_
    )
意思 matrix和array2d都是可以作为一个image_type来用,为什么?

查到源码image_processing中generic_image.h中,有注释摘要如下:

        In dlib, an "image" is any object that implements the generic image interface.  In
        particular, this simply means that an image type (let's refer to it as image_type
        from here on) has the following seven global functions defined for it:
            - long        num_rows      (const image_type& img)
            - long        num_columns   (const image_type& img)
            - void        set_image_size(      image_type& img, long rows, long cols)
            - void*       image_data    (      image_type& img)
            - const void* image_data    (const image_type& img)
            - long        width_step    (const image_type& img)
            - void        swap          (      image_type& a, image_type& b)

即一个对象要成为一个image_type,必须要定义上面这些方法,在源码总查看array2d文件夹,确实发现存在这些定义:


Image_view的定义声明如下:

    template <
        typename image_type
        >
    class image_view
它的构造函数:

   image_view(
            image_type& img
        ) : 
            _data((char*)image_data(img)), 
            _width_step(width_step(img)),
            _nr(num_rows(img)),
            _nc(num_columns(img)),
            _img(&img) 
        {}
可以看成image_view就是image_type的一个代理。image_view是一个模板类,成员函数都是直接调用全局重载函数(不同的模板类当做参数),这些全局重载函数往往都是放在模板类定义的头文件中。这样的好处就是使用一个image_view就可以将具体的数据底层表示和上层使用隔离开来。如果使用常规的方法,应该是将image_view定义一个接口,matrix和array2d都来进行实现。但如果我们的matrix又要能够支持序列化等等,那么就要实现相应的接口,这样类岂不是很庞大?

解决方法就是使用dlib里面这种写法:

1 首先是定义基础类array2d

2 如果要支持图像接口,就新增一个文件定义以array2d为参数的全局函数,如果想要支持序列化,就新增一个文件定义以array2d为参数的全局函数;

3 定义上层模板类,就像上面的image_view模板类,在里面调用以模板类为参数的全局函数;或者定义上层模板方法,像序列化serial方法,在serial里面在调用以模板为参数的全局方法。

有点就是,如果我只想使用基本的array2d,我就包含这个array2d的头文件就可以了,如果我想使用序列化我就再包含序列化相关的头文件就可以了。用起来是不是很方便?同时源码也可以保持很简洁。

matrix和array2d解析

matrix的定义头如下:

    template <
        typename T,
        long num_rows,
        long num_cols,
        typename mem_manager,
        typename layout
        >
    class matrix : public matrix_exp<matrix<T,num_rows,num_cols, mem_manager,layout> > 
matrix_exp类的定义如下:

  template <
        typename EXP
        >
    class matrix_exp 
    {
        /*!
            REQUIREMENTS ON EXP
                EXP should be something convertible to a matrix_exp.  That is,
                it should inherit from matrix_exp
        !*/

    public:
        typedef typename matrix_traits<EXP>::type type;
        typedef type value_type;
        typedef typename matrix_traits<EXP>::const_ret_type const_ret_type;
        typedef typename matrix_traits<EXP>::mem_manager_type mem_manager_type;
        typedef typename matrix_traits<EXP>::layout_type layout_type;
        const static long NR = matrix_traits<EXP>::NR;
        const static long NC = matrix_traits<EXP>::NC;
        const static long cost = matrix_traits<EXP>::cost;

        typedef matrix<type,NR,NC,mem_manager_type,layout_type> matrix_type;
        typedef EXP exp_type;
        typedef matrix_exp_iterator<EXP> iterator;
        typedef matrix_exp_iterator<EXP> const_iterator;

        inline const_ret_type operator() (
            long r,
            long c
        ) const 
        { 
            DLIB_ASSERT(r < nr() && c < nc() && r >= 0 && c >= 0, 
                "\tconst type matrix_exp::operator(r,c)"
                << "\n\tYou must give a valid row and column"
                << "\n\tr:    " << r 
                << "\n\tc:    " << c
                << "\n\tnr(): " << nr()
                << "\n\tnc(): " << nc() 
                << "\n\tthis: " << this
                );
            return ref()(r,c); 
        }

        const_ret_type operator() (
            long i
        ) const 
        {
            COMPILE_TIME_ASSERT(NC == 1 || NC == 0 || NR == 1 || NR == 0);
            DLIB_ASSERT(nc() == 1 || nr() == 1, 
                "\tconst type matrix_exp::operator(i)"
                << "\n\tYou can only use this operator on column or row vectors"
                << "\n\ti:    " << i
                << "\n\tnr(): " << nr()
                << "\n\tnc(): " << nc()
                << "\n\tthis: " << this
                );
            DLIB_ASSERT( ((nc() == 1 && i < nr()) || (nr() == 1 && i < nc())) && i >= 0, 
                "\tconst type matrix_exp::operator(i)"
                << "\n\tYou must give a valid row/column number"
                << "\n\ti:    " << i
                << "\n\tnr(): " << nr()
                << "\n\tnc(): " << nc()
                << "\n\tthis: " << this
                );
            if (nc() == 1)
                return ref()(i,0);
            else
                return ref()(0,i);
        }

        long size (
        ) const { return nr()*nc(); }

        long nr (
        ) const { return get_nr_helper<exp_type,NR>::get(ref()); }

        long nc (
        ) const { return get_nc_helper<exp_type,NC>::get(ref()); }

        template <typename U>
        bool aliases (
            const matrix_exp<U>& item
        ) const { return ref().aliases(item); }

        template <typename U>
        bool destructively_aliases (
            const matrix_exp<U>& item
        ) const { return ref().destructively_aliases(item); }

        inline const exp_type& ref (
        ) const { return *static_cast<const exp_type*>(this); }

        inline operator const type (
        ) const 
        {
            COMPILE_TIME_ASSERT(NC == 1 || NC == 0);
            COMPILE_TIME_ASSERT(NR == 1 || NR == 0);
            DLIB_ASSERT(nr() == 1 && nc() == 1, 
                "\tmatrix_exp::operator const type() const"
                << "\n\tYou can only use this operator on a 1x1 matrix"
                << "\n\tnr(): " << nr()
                << "\n\tnc(): " << nc()
                << "\n\tthis: " << this
                );

            // Put the expression contained in this matrix_exp into
            // a temporary 1x1 matrix so that the expression will encounter
            // all the overloads of matrix_assign() and have the chance to
            // go through any applicable optimizations.
            matrix<type,1,1,mem_manager_type,layout_type> temp(ref());
            return temp(0);
        }

        const_iterator begin() const { return matrix_exp_iterator<EXP>(ref(),0,0); }
        const_iterator end()   const { return matrix_exp_iterator<EXP>(ref(),nr(),0); }

    protected:
        matrix_exp() {}
        matrix_exp(const matrix_exp& ) {}

    private:

        matrix_exp& operator= (const matrix_exp&);
    };

可以很清楚的看到,matrix_exp里面的成员函数都是调用ref()的方法,而ref()返回的就是模板类的引用,即matrix。为什么要这样多此一举?

我们看matrix_mat.h头文件matrix_op类:

    template <
        typename OP
        >
    class matrix_op : public matrix_exp<matrix_op<OP> >
    {
        /*!
            WHAT THIS OBJECT REPRESENTS
                The matrix_op is simply a tool for reducing the amount of boilerplate
                you need to write when creating matrix expressions.  
        !*/

    public:
        typedef typename matrix_traits<matrix_op>::type type;
        typedef typename matrix_traits<matrix_op>::const_ret_type const_ret_type;
        typedef typename matrix_traits<matrix_op>::mem_manager_type mem_manager_type;
        typedef typename matrix_traits<matrix_op>::layout_type layout_type;
        const static long NR = matrix_traits<matrix_op>::NR;
        const static long NC = matrix_traits<matrix_op>::NC;
        const static long cost = matrix_traits<matrix_op>::cost;

    private:
        // This constructor exists simply for the purpose of causing a compile time error if
        // someone tries to create an instance of this object with the wrong kind of object.
        template <typename T1>
        matrix_op (T1); 
    public:

        matrix_op (
            const OP& op_
        ) :
            op(op_)
        {}

        const_ret_type operator() (
            long r, 
            long c
        ) const { return op.apply(r,c); }

        const_ret_type operator() ( long i ) const 
        { return matrix_exp<matrix_op>::operator()(i); }

        template <typename U>
        bool aliases (
            const matrix_exp<U>& item
        ) const { return op.aliases(item); }

        template <typename U>
        bool destructively_aliases (
            const matrix_exp<U>& item
        ) const { return op.destructively_aliases(item); }

        long nr (
        ) const { return op.nr(); }

        long nc (
        ) const { return op.nc(); }


        const OP op;
    };

这个类和matrix地位是一样的,但里面的成员函数都是间接调用了模板类op的方法,模板类op其实也是一个包装,可以包装std::vector,dlib::array2d->op_array2d_to_mat,甚至Eigen库的matrix->op_eigen_Matrix_to_mat,这样vector,array2d都可以像matrix一样兼容了,他们都继承自matrix_exp。dlib中提供的mean,sum等函数都是接受matrix_exp为参数的函数,如

    template <
        typename EXP
        >
    const typename lazy_enable_if<is_matrix<typename EXP::type>, EXP>::type sum (
        const matrix_exp<EXP>& m
    )

dlib中提供了mat全局函数,将std::vector,array2d转换成为matrix_op对象,这样就可以在上面的函数上使用了。

mat函数源码摘要:

// ----------------------------------------------------------------------------------------

    template <
        typename value_type,
        typename alloc
        >
    const matrix_op<op_std_vect_to_mat<std::vector<value_type,alloc> > > mat (
        const std::vector<value_type,alloc>& vector
    )
    {
        typedef op_std_vect_to_mat<std::vector<value_type,alloc> > op;
        return matrix_op<op>(op(vector));
    }

// ----------------------------------------------------------------------------------------

    template <
        typename value_type,
        typename alloc
        >
    const matrix_op<op_std_vect_to_mat<std_vector_c<value_type,alloc> > > mat (
        const std_vector_c<value_type,alloc>& vector
    )
    {
        typedef op_std_vect_to_mat<std_vector_c<value_type,alloc> > op;
        return matrix_op<op>(op(vector));
    }

// ----------------------------------------------------------------------------------------


  • 2
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值