在Opencv1代的时候,是使用lplImage 和 CvMat 数据结构来表示图像的,他们都是C语言的结构,申请的内存需要自己手动管理,特别是采用 lplImage 会直接暴露内存,如果忘记释放内存,就会造成内存泄漏。为此,OpenCV在2.0版本中引入了一个新的C++接口,利用自动内存管理给出了解决问题的新方法。使用这个方法,你不需要纠结在管理内存上,而且你的代码会变得简洁。Mat的优点如下:
- 不需要手动申请一块内存;
- 在不需要时不用再手动
释放内存
;- 赋值运算符和拷贝构造函数
只复制信息头
;但可以通过函数clone()或者copyTo()来复制一幅图像的矩阵。- 可以通过类的
封装
,方便的获取到数据的相关信息。
Mat 是一个类,由两个数据部分组成:矩阵头
(包含矩阵尺寸,存储方法,存储地址等信息)和一个指向存储所有像素值的矩阵
(根据所选存储方法的不同矩阵可以是不同的维数)的指针。
class CV_EXPORTS Mat
{
public:
/*..很多方法..*/
/*............*/
int flags;/*flags指定图像的颜色空间
flags > 0 3通道的彩色图像
flags = 0 灰度图像
flags < 0 不作改变
*/
int dims; /*矩阵的维数*/
int rows,cols; /*行和列的数量;矩阵的维数超过2维时为(-1,-1)*/
uchar *data; /*指向数据*/
int * refcount; /*指针的引用计数器; 阵列指向用户分配的数据时,指针为 NULL
/* 其他成员 */
...
};
Mat的常用成员变量列表如下:
变量 | 解释说明 |
---|---|
rows | 行 |
cols | 列 |
dims | 矩阵的维度,二维矩阵dims=2,三维矩阵dims=3 |
data | 指是指向矩阵数据的uchar类指针,用*解引用后再强转为int可以读到第一个像素数据。 |
说明:
Mat常用成员函数列表如下:
成员函数 | 解释说明 |
---|---|
channels() | 矩阵元素 拥有的通道数,例如常见的彩色图像,每一个像素由RGB三部分组成,则image.channels() = 3 。 |
size() | 该方法返回一个矩阵大小:size(cols, rows) 。矩阵超过 2 维时返回大小为(-1,-1)。 |
type() | 矩阵中元素的类型以及矩阵的通道个数,它是一系列的预定义的常量,其命名规则为CV_(位数)+(数据类型)+(通道数)。 |
depth() | 矩阵中元素的一个通道的数据类型,这个值和type是相关的。例如 type为 CV_16SC2 ,一个2通道的16位的有符号整数。那么,depth则是CV_16S 。 |
empty() | 如果数组有没有 elemens,则返回 true。 |
isContinuous() | 判断矩阵是否是连续存储的 |
elemSize() | 矩阵一个元素占用的字节数 ,例如:type是CV_16SC3,那么elemSize = 3 * 16 / 8 = 6 bytes,即每个元素占6个字节。 |
elemSize1() | 单个元素在1个通道中的大小。elemSize1() = sizeof(数据类型) = elemSize() / channels()。 |
step[i] | step是一个数组,定义了矩阵的布局,以二维矩阵为例,step[0]表示第一维度的单位长度占用字节数(即每行),step[1]表示第二维度的单位长度占用字节数(即每个元素) |
step1(i) | step1也是一个数组,但是是使用()访问的,这个数组在step[i]的基础上/elemSize1() |
说明:
去掉通道信息
就是depth值 cv::Mat M1(3, 3, CV_8UC4, cv::Scalar(0, 0, 0, 255));
std::cout << "M1 = " << std::endl << M1 << std::endl;
这里使用了构造函数对Mat进行定义,分别指定行数、列数、4通道的矩阵,每个点的颜色为(0, 0, 0, 255)。该例的输出结果为:
M1 =
[ 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255;
0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255;
0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255]
int sz[2] = { 3, 3 };//矩阵维数3*3
cv::Mat M2(2, sz, CV_8UC1, cv::Scalar::all(0));
std::cout << "M2 = " << std::endl << M2 << std::endl;
M2的分别指定了2维、sz指定了3行3列、单通道8位无符号和全部元素都为0,输出结果如下:
M2 =
[ 0, 0, 0;
0, 0, 0;
0, 0, 0]
cv::Mat M3;
M3.create(4, 4, CV_8UC(1));
std::cout << "M3 = " << std::endl << M3 << std::endl;
create构造了4*4的单通道8位无符号,并且矩阵中的数据位随机值。如下:
[205, 205, 205, 205;
205, 205, 205, 205;
205, 205, 205, 205;
205, 205, 205, 205]
cv::Mat mat_eye= cv::Mat::eye(4, 4, CV_64F);//对角
std::cout << "mat_eye= " << std::endl << mat_eye<< std::endl;
cv::Mat mat_ones= cv::Mat::ones(4, 4, CV_64F);//全1
std::cout << "mat_ones= " << std::endl << mat_ones<< std::endl;
cv::Mat mat_zeros= cv::Mat::zeros(4, 4, CV_64F);//全0
std::cout << "mat_zeros= " << std::endl << mat_zeros<< std::endl;
eye函数表示的是单位矩阵,ones为全1的矩阵,zeros表示全是0的矩阵。输出如下:
mat_eye=
[1, 0, 0, 0;
0, 1, 0, 0;
0, 0, 1, 0;
0, 0, 0, 1]
mat_ones=
[1, 1, 1, 1;
1, 1, 1, 1;
1, 1, 1, 1;
1, 1, 1, 1]
mat_zeros=
[0, 0, 0, 0;
0, 0, 0, 0;
0, 0, 0, 0;
0, 0, 0, 0]
cv::Mat M4 = (cv::Mat_<double>(3, 3) << 0, -1, 0, -1, 0, 0, 0, 0, 1);
std::cout << "M4 = " << std::endl << M4 << std::endl;
创建M4的矩阵如下所示:
M4 =
[0, -1, 0;
-1, 0, 0;
0, 0, 1]
cv::Mat M5 = M4.row(1).clone();//拷贝第一行
std::cout << "M5 = " << std::endl << M5 << std::endl;
Mat F = A.clone();//拷贝A矩阵
Mat G;
A.copyTo(G);//拷贝A矩阵
clone为深拷贝,输出结果如下:
M5 =
[-1, 0, 0]
除了clone和copyTo函数,类似于拷贝构造函数、赋值运算符的运算等都是通过采用引用的方式,即多个Mat对象共享同一个矩阵数据,这里使用的原理类似c++11中的共享指针,也就是说
它们共享矩阵数据,而不共享矩阵头。
创建感兴趣区域也是共享矩阵。
Mat D(A, Rect(10, 10, 100, 100));//使用矩阵界定
Mat E(Range:all(), Range(1, 3));//使用行和列来界定
R
IplImage* iplImg = cvLoadImage("greatwave.jpg", 1);
Mat mtx(iplImg);
存储像素值需要指定颜色空间和数据类型,最简单的颜色空间为灰度级空间,只处理黑色和白色,对它们进行组合便可以产生不同的灰色。
RGB颜色空间是最常用的颜色空间,这归功于它也是人眼内部构成颜色的方式。有时为了表示透明颜色也会加入第4个元素alpha(A)。
颜色系统有很多,具体如下:
- RGB是最常见的,这是因为人眼采用相似的工作机制,它也被显示设备所采用的。
- HSV和HLS把颜色分解成色调、饱和度和亮度/明度。这是描述颜色更自然的方式,比如可以通过抛弃最后一个元素,使算法对输入图像的光照条件不敏感。
- YCrCb在JPEG图像格式中被广泛使用。
- CIE Lab*是一种在感知上均匀的颜色空间,它适合用来度量两个颜色之间的距离。
每个组成元素都有其定义域,定义域取决于其数据类型。如最小的数据类型是char,占一个字节,可以是有符号类型(0到255之间)或无符号类型(-127到+127之间)。尽管使用三个char可以表示1600万种颜色,但是使用float或者double则能给出更加精细的颜色分辨能力。
Point类数据结构表示了二维坐标系下的点。
Point point;
point.x = 10;
point.y = 8;
或者
Point point = Point(10, 8);
关于Point有如下定义:
typedef Point_<int> Point2i;
typedef Point2i Point;//Point类型
typedef Point_<float> Point2f;//float类型,同理还有Point3f
Scalar()表示具有4个元素的数组,在OpenCV中被大量用于传递像素值,如RGB颜色值。
Scalar(a, b, c);//红色:c 绿色:b 蓝色:a
Scalar类的源头为Scalar_类,而Scalar_类是Vec4x的一个变种,我们常用的Scalar其实就是Scalar_
Vec是Matx的一个派生类,Matx是一个轻量级的Mat,Matx必须在使用前规定好大小。
Size的定义如下:
typedef Size_<int> Size2i;
typedef Size2i Size;
Size_是一个模板类,使用频率最高的是下面的这个构造函数:
Size_ (_Tp _width, _Tp -height);
下面给出示例:
Size(5, 5);//宽度和高度都是5
Rect的成员变量有x,y,width,height,分别为左上角点的坐标和矩阵的宽和高。常用的成员函数有:
- Size():返回值为Size;
- area();返回矩阵的面积;
- contains(Point):判断点是否在矩形内;
- inside(Rect):判断矩形是否在该矩形内;
- tl():返回左上角点坐标;
- br():返回右下角坐标;
求矩阵的常用操作:
Rect rect = rect1 & rect2;//交集
Rect rect = rect1 | rect2;//并集
Rect rectShift = rect + point;//矩阵平移
Rect rectScale = rect + size;//矩阵缩放
关于cvtColor可以参考这篇文章。