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));
}
// ----------------------------------------------------------------------------------------