一、Mat
我们有多种方式从现实世界中获取数字图像:数码相机,扫描仪,计算机断层扫描和磁共振成像等等。在任何情况下,我们(人类)看到的都是图像。然而,当将其转换为数字设备时,我们记录的是图像中每个点的数值。
在上述图像中,您可以看到汽车的镜像只不过是一个包含像素点所有强度值的矩阵。我们如何获取和存储像素值可能会根据我们的需要而有所不同,但最终,计算机世界内的所有图像可能会被减少到描述矩阵本身的数字矩阵和其他信息。OpenCV是一个计算机视觉库,其主要重点是处理和操纵这些信息。因此,您需要熟悉的第一件事是OpenCV如何存储和处理图像。1
1.Mat类说明
类Mat表示一个n维密集的数值单通道或多通道阵列。它可以用来存储实值的或复值(real or complex-valued)的向量(vectors)和矩阵(matrices)、灰度或彩色图像(grayscale or color images)、体素卷(voxel volumes)、向量场(vector fields)、点云(point clouds)、张量(tensors)、直方图(histograms )。数组 M
的数据布局由数组 M.step[]
定义,因此元素的地址
(
i
0
,
.
.
.
,
i
M
.
d
i
m
s
−
1
)
(i_0,...,i_{M.dims−1})
(i0,...,iM.dims−1)可以通过以下计算:
a
d
d
r
(
M
i
0
,
.
.
.
,
i
M
.
d
i
m
s
−
1
)
=
M
.
d
a
t
a
+
M
.
s
t
e
p
[
0
]
∗
i
0
+
M
.
s
t
e
p
[
1
]
∗
i
1
+
.
.
.
+
M
.
s
t
e
p
[
M
.
d
i
m
s
−
1
]
∗
i
M
.
d
i
m
s
−
1
addr(M_{i_0,...,i_{M.dims-1}}) = M.data + M.step[0]*i_0 + M.step[1]*i_1 + ... + M.step[M.dims-1]*i_{M.dims-1}
addr(Mi0,...,iM.dims−1)=M.data+M.step[0]∗i0+M.step[1]∗i1+...+M.step[M.dims−1]∗iM.dims−1
对于二维数组,可将上述公式简化为:
a
d
d
r
(
M
i
,
j
)
=
M
.
d
a
t
a
+
M
.
s
t
e
p
[
0
]
∗
i
+
M
.
s
t
e
p
[
1
]
∗
j
addr(M_{i,j}) = M.data + M.step[0]*i + M.step[1]*j
addr(Mi,j)=M.data+M.step[0]∗i+M.step[1]∗j
注意到M.step[i] >= M.step[i+1]
(事实上M.step[i] >= M.step[i+1]*M.size[i+1]
),这意味着二维矩阵是逐行存储的,三维矩阵是逐平面存储的,依此类推。 M.step[M.dims-1]
是最小的,并且总是等于元素大小M.elemSize()
。
因此,Mat中的数据布局与标准工具包和SDK中的大多数密集数组类型兼容,比如Numpy (ndarray)、Win32(独立设备位图)和其他类型,也就是说,与任何使用步幅计算像素位置的数组兼容。由于这种兼容性,可以为用户分配的数据制作一个Mat头文件,并使用OpenCV函数对其进行就地处理。2
2.Mat类声明
opencv-4.1.1\modules\core\include\opencv2\core\mat.hpp
class CV_EXPORTS Mat
{
public:
// ------常用构造函数------ //
Mat();
Mat(int rows, int cols, int type);
Mat(Size size, int type);
Mat(int rows, int cols, int type, const Scalar &s);
Mat(Size size, int type, const Scalar &s);
Mat(int ndims, const int *sizes, int type);
Mat(const std::vector< int > &sizes, int type);
Mat(int ndims, const int *sizes, int type, const Scalar &s);
Mat(const std::vector< int > &sizes, int type, const Scalar &s);
Mat(const Mat &m);
Mat(int rows, int cols, int type, void* data, size_t step=AUTO_STEP);
// ------部分方法-------- //
void create(int rows, int cols, int type);
void create(Size size, int type);
void create(int ndims, const int *sizes, int type);
void create(const std::vector< int > &sizes, int type);
// --------属性-------- //
int flags;
//! 矩阵的维数, >= 2
int dims;
//! 当矩阵的维数大于2时,行数和列数或(- 1,1)
int rows, cols;
//! 指向数据的指针
uchar* data;
//! 在locateROI和adjustROI中使用的助手字段
const uchar* datastart;
const uchar* dataend;
const uchar* datalimit;
//! 定制的分配器
MatAllocator* allocator;
//! 与UMat互动
UMatData* u;
MatSize size;
MatStep step;
// ------静态函数------- //
// 创建一个对角矩阵
static Mat diag(const Mat& d);
// 返回指定大小和类型的单位矩阵
static MatExpr eye(int rows, int cols, int type);
static MatExpr eye(Size size, int type);
// 返回指定大小和类型的所有值为1的数组
static MatExpr ones(int rows, int cols, int type);
static MatExpr ones(Size size, int type);
static MatExpr ones(int ndims, const int* sz, int type);
// 返回指定大小和类型的零数组
static MatExpr zeros(int rows, int cols, int type);
static MatExpr zeros(Size size, int type);
static MatExpr zeros(int ndims, const int* sz, int type);
// ......
// 省略部分代码
// ......
}
3.构造函数实现
opencv-4.1.1\modules\core\include\opencv2\core\mat.inl.hpp
-
Mat()
inline Mat::Mat() : flags(MAGIC_VAL), dims(0), rows(0), cols(0), data(0), datastart(0), dataend(0), datalimit(0), allocator(0), u(0), size(&rows), step(0) {}
-
Mat(Size size, int type)
Size size – 二维数组大小。Size(cols, rows),在Size()构造函数中,行数和
列数的顺序相反。查看 Size 类型。
int type – 数组类型。使用CV_8UC1,…, CV_64FC4创建1-4通道矩阵,或CV_8UC (n),……, CV_64FC(n)创建多通道(最多CV_CN_MAX通道)矩阵。inline Mat::Mat(Size _sz, int _type) : flags(MAGIC_VAL), dims(0), rows(0), cols(0), data(0), datastart(0), dataend(0), datalimit(0), allocator(0), u(0), size(&rows), step(0) { create( _sz.height, _sz.width, _type ); }
-
Mat(int rows, int cols, int type)
int rows – 二维数组中的行数
int cols – 二维数组中的列数
查看 create 函数。inline Mat::Mat(int _rows, int _cols, int _type) : flags(MAGIC_VAL), dims(0), rows(0), cols(0), data(0), datastart(0), dataend(0), datalimit(0), allocator(0), u(0), size(&rows), step(0) { create(_rows, _cols, _type); }
-
Mat(int rows, int cols, int type, const Scalar& s);
const Scalar& s – 参数s是一个可选值,用于初始化每个矩阵元素。查看 Scalar 类型。inline Mat::Mat(int _rows, int _cols, int _type, const Scalar& _s) : flags(MAGIC_VAL), dims(0), rows(0), cols(0), data(0), datastart(0), dataend(0), datalimit(0), allocator(0), u(0), size(&rows), step(0) { create(_rows, _cols, _type); *this = _s; }
-
Mat(Size size, int type, const Scalar &s)
inline Mat::Mat(Size _sz, int _type, const Scalar& _s) : flags(MAGIC_VAL), dims(0), rows(0), cols(0), data(0), datastart(0), dataend(0), datalimit(0), allocator(0), u(0), size(&rows), step(0) { create(_sz.height, _sz.width, _type); *this = _s; }
-
Mat(int ndims, const int* sizes, int type)
inline Mat::Mat(int _dims, const int* _sz, int _type) : flags(MAGIC_VAL), dims(0), rows(0), cols(0), data(0), datastart(0), dataend(0), datalimit(0), allocator(0), u(0), size(&rows), step(0) { create(_dims, _sz, _type); }
-
Mat(const std::vector< int > &sizes, int type)
const std::vector< int > &sizes – 指定n维数组形状的整数数组的大小。inline Mat::Mat(const std::vector<int>& _sz, int _type) : flags(MAGIC_VAL), dims(0), rows(0), cols(0), data(0), datastart(0), dataend(0), datalimit(0), allocator(0), u(0), size(&rows), step(0) { create(_sz, _type); }
-
Mat(int rows, int cols, int type, void* data, size_t step=AUTO_STEP)
size_t step – 每个矩阵行占用的字节数。如果有填充字节,则该值应该包括每一行末尾的填充字节。如果缺少参数(设置为AUTO_STEP),则假定没有填充,实际的步骤计算为cols*elemSize()。typedef unsigned int size_t;
inline Mat::Mat(int _rows, int _cols, int _type, void* _data, size_t _step) : flags(MAGIC_VAL + (_type & TYPE_MASK)), dims(2), rows(_rows), cols(_cols), data((uchar*)_data), datastart((uchar*)_data), dataend(0), datalimit(0), allocator(0), u(0), size(&rows) { CV_Assert(total() == 0 || data != NULL); size_t esz = CV_ELEM_SIZE(_type), esz1 = CV_ELEM_SIZE1(_type); size_t minstep = cols * esz; if( _step == AUTO_STEP ) { _step = minstep; } else { CV_DbgAssert( _step >= minstep ); if (_step % esz1 != 0) { CV_Error(Error::BadStep, "Step must be a multiple of esz1"); } } step[0] = _step; step[1] = esz; datalimit = datastart + _step * rows; dataend = datalimit - _step + minstep; updateContinuityFlag(); }
4.成员函数实现
create
分配新的数组数据。
-
void create(int rows, int cols, int type)
opencv-4.1.1\modules\core\include\opencv2\core\mat.inl.hpp
inline void Mat::create(int _rows, int _cols, int _type) { _type &= TYPE_MASK; if( dims <= 2 && rows == _rows && cols == _cols && type() == _type && data ) return; int sz[] = {_rows, _cols}; create(2, sz, _type); }
-
void create (Size size, int type)
opencv-4.1.1\modules\core\include\opencv2\core\mat.inl.hpp
inline void Mat::create(Size _sz, int _type) { create(_sz.height, _sz.width, _type); }
-
void create(int ndims, const int *sizes, int type)
opencv-4.1.1\modules\core\src\matrix.cpp
void Mat::create(int d, const int* _sizes, int _type) { int i; CV_Assert(0 <= d && d <= CV_MAX_DIM && _sizes); _type = CV_MAT_TYPE(_type); if( data && (d == dims || (d == 1 && dims <= 2)) && _type == type() ) { if( d == 2 && rows == _sizes[0] && cols == _sizes[1] ) return; for( i = 0; i < d; i++ ) if( size[i] != _sizes[i] ) break; if( i == d && (d > 1 || size[1] == 1)) return; } int _sizes_backup[CV_MAX_DIM]; // #5991 if (_sizes == (this->size.p)) { for(i = 0; i < d; i++ ) _sizes_backup[i] = _sizes[i]; _sizes = _sizes_backup; } release(); if( d == 0 ) return; flags = (_type & CV_MAT_TYPE_MASK) | MAGIC_VAL; setSize(*this, d, _sizes, 0, true); if( total() > 0 ) { MatAllocator *a = allocator, *a0 = getDefaultAllocator(); #ifdef HAVE_TGPU if( !a || a == tegra::getAllocator() ) a = tegra::getAllocator(d, _sizes, _type); #endif if(!a) a = a0; try { u = a->allocate(dims, size, _type, 0, step.p, ACCESS_RW /* ignored */, USAGE_DEFAULT); CV_Assert(u != 0); } catch (...) { if (a == a0) throw; u = a0->allocate(dims, size, _type, 0, step.p, ACCESS_RW /* ignored */, USAGE_DEFAULT); CV_Assert(u != 0); } CV_Assert( step[dims-1] == (size_t)CV_ELEM_SIZE(flags) ); } addref(); finalizeHdr(*this); }
-
void create(const std::vector< int > &sizes, int type)
opencv-4.1.1\modules\core\src\matrix.cpp
void Mat::create(const std::vector<int>& _sizes, int _type) { create((int)_sizes.size(), _sizes.data(), _type); }
5.静态函数实现
static Mat diag(const Mat& d)
opencv-4.1.1\modules\core\src\matrix.cpp
Mat Mat::diag(const Mat& d)
{
CV_Assert( d.cols == 1 || d.rows == 1 );
int len = d.rows + d.cols - 1;
Mat m(len, len, d.type(), Scalar(0));
Mat md = m.diag();
if( d.cols == 1 )
d.copyTo(md);
else
transpose(d, md);
return m;
}
eye
、ones
、zeros
opencv-4.1.1\modules\core\src\matrix_expressions.cpp
MatExpr Mat::zeros(int rows, int cols, int type)
{
CV_INSTRUMENT_REGION();
MatExpr e;
MatOp_Initializer::makeExpr(e, '0', Size(cols, rows), type);
return e;
}
MatExpr Mat::zeros(Size size, int type)
{
CV_INSTRUMENT_REGION();
MatExpr e;
MatOp_Initializer::makeExpr(e, '0', size, type);
return e;
}
MatExpr Mat::zeros(int ndims, const int* sizes, int type)
{
CV_INSTRUMENT_REGION();
MatExpr e;
MatOp_Initializer::makeExpr(e, '0', ndims, sizes, type);
return e;
}
MatExpr Mat::ones(int rows, int cols, int type)
{
CV_INSTRUMENT_REGION();
MatExpr e;
MatOp_Initializer::makeExpr(e, '1', Size(cols, rows), type);
return e;
}
MatExpr Mat::ones(Size size, int type)
{
CV_INSTRUMENT_REGION();
MatExpr e;
MatOp_Initializer::makeExpr(e, '1', size, type);
return e;
}
MatExpr Mat::ones(int ndims, const int* sizes, int type)
{
CV_INSTRUMENT_REGION();
MatExpr e;
MatOp_Initializer::makeExpr(e, '1', ndims, sizes, type);
return e;
}
MatExpr Mat::eye(int rows, int cols, int type)
{
CV_INSTRUMENT_REGION();
MatExpr e;
MatOp_Initializer::makeExpr(e, 'I', Size(cols, rows), type);
return e;
}
MatExpr Mat::eye(Size size, int type)
{
CV_INSTRUMENT_REGION();
MatExpr e;
MatOp_Initializer::makeExpr(e, 'I', size, type);
return e;
}
相关代码
Size
用于指定图像或矩形大小。
opencv-4.1.1\modules\core\include\opencv2\core\types.hpp
typedef Size2i Size;
typedef Size_<int> Size2i;
template<typename _Tp> class Size_
{
public:
typedef _Tp value_type;
//! default constructor
Size_();
Size_(_Tp _width, _Tp _height);
Size_(const Size_& sz);
Size_(Size_&& sz) CV_NOEXCEPT;
Size_(const Point_<_Tp>& pt);
Size_& operator = (const Size_& sz);
Size_& operator = (Size_&& sz) CV_NOEXCEPT;
//! the area (width*height)
_Tp area() const;
//! aspect ratio (width/height)
double aspectRatio() const;
//! true if empty
bool empty() const;
//! conversion of another data type.
template<typename _Tp2> operator Size_<_Tp2>() const;
_Tp width; //!< the width
_Tp height; //!< the height
};
数组类型
opencv-4.1.1\modules\core\include\opencv2\core\hal\interface.h
CV_8U - 8位无符号整数(0…255)
CV_8S - 8位带符号整数(-128…127)
CV_16U - 16位无符号整数(0…65535)
CV_16S - 16位带符号整数(-32768…32767)
CV_32S - 32位带符号整数(-2147483648…2147483647)
CV_32F - 32位浮点数(- flt_max …FLT_MAX, INF, NAN)
CV_64F - 64位浮点数(- dbl_max …DBL_MAX, INF, NAN)
#define CV_CN_MAX 512
#define CV_CN_SHIFT 3
#define CV_DEPTH_MAX (1 << CV_CN_SHIFT)
#define CV_8U 0
#define CV_8S 1
#define CV_16U 2
#define CV_16S 3
#define CV_32S 4
#define CV_32F 5
#define CV_64F 6
#define CV_16F 7
#define CV_MAT_DEPTH_MASK (CV_DEPTH_MAX - 1)
#define CV_MAT_DEPTH(flags) ((flags) & CV_MAT_DEPTH_MASK)
#define CV_MAKETYPE(depth,cn) (CV_MAT_DEPTH(depth) + (((cn)-1) << CV_CN_SHIFT))
#define CV_MAKE_TYPE CV_MAKETYPE
#define CV_8UC1 CV_MAKETYPE(CV_8U,1)
#define CV_8UC2 CV_MAKETYPE(CV_8U,2)
#define CV_8UC3 CV_MAKETYPE(CV_8U,3)
#define CV_8UC4 CV_MAKETYPE(CV_8U,4)
#define CV_8UC(n) CV_MAKETYPE(CV_8U,(n))
#define CV_8SC1 CV_MAKETYPE(CV_8S,1)
#define CV_8SC2 CV_MAKETYPE(CV_8S,2)
#define CV_8SC3 CV_MAKETYPE(CV_8S,3)
#define CV_8SC4 CV_MAKETYPE(CV_8S,4)
#define CV_8SC(n) CV_MAKETYPE(CV_8S,(n))
#define CV_16UC1 CV_MAKETYPE(CV_16U,1)
#define CV_16UC2 CV_MAKETYPE(CV_16U,2)
#define CV_16UC3 CV_MAKETYPE(CV_16U,3)
#define CV_16UC4 CV_MAKETYPE(CV_16U,4)
#define CV_16UC(n) CV_MAKETYPE(CV_16U,(n))
#define CV_16SC1 CV_MAKETYPE(CV_16S,1)
#define CV_16SC2 CV_MAKETYPE(CV_16S,2)
#define CV_16SC3 CV_MAKETYPE(CV_16S,3)
#define CV_16SC4 CV_MAKETYPE(CV_16S,4)
#define CV_16SC(n) CV_MAKETYPE(CV_16S,(n))
#define CV_32SC1 CV_MAKETYPE(CV_32S,1)
#define CV_32SC2 CV_MAKETYPE(CV_32S,2)
#define CV_32SC3 CV_MAKETYPE(CV_32S,3)
#define CV_32SC4 CV_MAKETYPE(CV_32S,4)
#define CV_32SC(n) CV_MAKETYPE(CV_32S,(n))
#define CV_32FC1 CV_MAKETYPE(CV_32F,1)
#define CV_32FC2 CV_MAKETYPE(CV_32F,2)
#define CV_32FC3 CV_MAKETYPE(CV_32F,3)
#define CV_32FC4 CV_MAKETYPE(CV_32F,4)
#define CV_32FC(n) CV_MAKETYPE(CV_32F,(n))
#define CV_64FC1 CV_MAKETYPE(CV_64F,1)
#define CV_64FC2 CV_MAKETYPE(CV_64F,2)
#define CV_64FC3 CV_MAKETYPE(CV_64F,3)
#define CV_64FC4 CV_MAKETYPE(CV_64F,4)
#define CV_64FC(n) CV_MAKETYPE(CV_64F,(n))
#define CV_16FC1 CV_MAKETYPE(CV_16F,1)
#define CV_16FC2 CV_MAKETYPE(CV_16F,2)
#define CV_16FC3 CV_MAKETYPE(CV_16F,3)
#define CV_16FC4 CV_MAKETYPE(CV_16F,4)
#define CV_16FC(n) CV_MAKETYPE(CV_16F,(n))
Scalar
包含四个double型值元素的数值向量
opencv-4.1.1\modules\core\include\opencv2\core\types.hpp
typedef Scalar_<double> Scalar;
template<typename _Tp> class Scalar_ : public Vec<_Tp, 4>
{
public:
//! default constructor
Scalar_();
Scalar_(_Tp v0, _Tp v1, _Tp v2=0, _Tp v3=0);
Scalar_(_Tp v0);
Scalar_(const Scalar_& s);
Scalar_(Scalar_&& s) CV_NOEXCEPT;
Scalar_& operator=(const Scalar_& s);
Scalar_& operator=(Scalar_&& s) CV_NOEXCEPT;
template<typename _Tp2, int cn>
Scalar_(const Vec<_Tp2, cn>& v);
//! returns a scalar with all elements set to v0
static Scalar_<_Tp> all(_Tp v0);
//! conversion to another data type
template<typename T2> operator Scalar_<T2>() const;
//! per-element product
Scalar_<_Tp> mul(const Scalar_<_Tp>& a, double scale=1 ) const;
//! returns (v0, -v1, -v2, -v3)
Scalar_<_Tp> conj() const;
//! returns true iff v1 == v2 == v3 == 0
bool isReal() const;
};