1 Mat对象与IplImage对象
- Mat对象OpenCV2.0之后引进的图像数据结构,能够自动分配内存,不存在内存泄漏的问题,是面向对象的数据结构。分了两个部分,头部与数据部分
- IplImage是从2001年OpenCV发布之后就一直存在,是C语言风格的数据结构,需要开发者自己分配与管理内存,对大的程序使用它容易导致内存泄漏问题
2 Mat组成部分
- Mat有两个必不可少的组成部分:一个头部和一个数据块
- 头部:包含了矩阵的所有相关信息(大小、通道数量、数据类型等)
- 数据块:包含了图像中所有像素的值
- 头部有一个指向数据块的指针,即data属性
- cv::Mat有一个很重要的属性,即只有在明确要求时,内存块才会被复制。实际上,大多数操作仅仅复制了cv::Mat的头部,因此多个对象会指向同一个数据块
3 Mat对象构造函数与常用方法
3.1 对象构造函数
Mat()
:无参构造方法Mat(int rows, int cols, int type)
:创建行数为rows,列为col,类型为type的图像(图像元素类型,如CV_8UC3等)Mat(Size size, int type)
:创建大小为size,类型为type的图像Mat(int rows, int cols, int type, const Scalar &s)
:创建行数为 rows,列数为 col,类型为 type 的图像,并将所有元素初始化为值 sMat(Size size, int type, const Scalar &s)
:创建大小为 size,类型为 type 的图像,并将所有元素初始化为值 sMat(int ndims, const int *sizes, int type)
:构造n维矩阵Mat(int ndims, const int *size, int type, const Scalar &s)
:构造n维矩阵并初始化为值s
3.2 常用方法
void copyTo(Mat mat)
void convertTo(Mat dst, int type)
Mat clone()
int channels()
int depth()
bool empty()
uchar* ptr(i = 0)
4 Mat对象使用
4.1 复制
- 部分复制:一般情况下只会复制Mat对象的头和指针部分,不会复制数据部分
Mat A = imread(imgFilePath);
Mat B(A); //初始化对象,只复制头部和指针部分
- 完全复制:如果想把Mat对象的头部和数据部分一起复制,可以通过如下两个API实现
//方法1
Mat F = A.clone();
//方法2
Mat G;
A.copyTo(G);
4.2 Mat对象使用-四个要点
- 输出图像的内存是自动分配的
- 使用OpenCV的C++接口,不需要考虑内存分配问题
- 赋值操作和拷贝构造函数只会复制头部分
- 使用clone和copyTo两个函数实现数据完全复制
4.3 Mat对象创建
cv::Mat::Mat
构造函数
Mat M(2, 2, CV_8UC3, Scalar(0, 0, 255))
- 前两个参数分别表示行(row)跟列(column)
- 第三个参数CV_8UC3中的8表示每个通道占8位、U表示无符号、C表示Char类型、3表示通道数目是3(RGB图像)
- 第四个参数是向量表示初始化每个像素值是多少,向量长度对应通道数目一致
- 创建多维数组
cv::Mat::create
int sz[3] = {2, 2, 2};
Mat L(3, sz, CV_8UC1, Scalar::all(0));
cv::Mat::create
实现
Mat M;
M.create(4, 3, CV_8UC2);
M = Scalar(127, 127);
cout << "M = " << endl << M << endl;
uchar* firstRow = M.ptr<uchar>(0);
printf("%d", *firstRow);
- 定义小数组
Mat C = (Mat_<double>(3,3) << 0,-1,0,-1,5,-1,0,-1,0);
cout << "C = " << endl << C << endl;
5 代码
- 头文件
#include<opencv2/opencv.hpp>
#include<iostream>
using namespace std;
using namespace cv;
- 读取图像,显示图像(原图)
Mat src; //定义一个Mat对象
src = imread("D:/C++project/OpenCVProject/test.jpg");
if (src.empty()) {
cout << "could not load image..." << endl;
return -1;
}
namedWindow("My image", WINDOW_AUTOSIZE);
imshow("my image", src);
- Scalar使用
Mat dst;
dst = Mat(src.size(), src.type()); //使用Mat的构造函数
dst = Scalar(127, 0, 255);
namedWindow("output", WINDOW_AUTOSIZE);
imshow("output", dst);
- 复制
//方法1
Mat dst = src.clone(); //完全拷贝
namedWindow("output", WINDOW_AUTOSIZE);
imshow("output", dst);
//方法2
Mat dst;
src.copyTo(dst); //复制
namedWindow("output", WINDOW_AUTOSIZE);
imshow("output", dst);
- 通道数、行数、列数
cvtColor(src, dst, COLOR_BGR2GRAY); //转换色彩空间,从BGR转换到灰度
printf("input image channels : %d\n", src.channels()); //转换前的通道数,即RGB的通道数
printf("output image channels : %d\n", dst.channels()); //转换后的通道数,即灰度图像的通道数
int cols = dst.cols; //cols为列数(columns)
int rows = dst.rows; //rows为行数(rows)
printf("rows : %d cols : %d\n", rows, cols);
const uchar* firstRow = dst.ptr<uchar>(0); //指向第一个像素的指针,获取第0个的像素点灰度值
printf("first pixel value : %d", *firstRow); //firstRow是一个指针
- 构造函数
//可以先用小的图像来验证算法的正确性,然后再应用到大的图像中
Mat M(3, 3, CV_8UC3, Scalar(0,0,255)); //定义一个3*3的三通道矩阵
cout << "M = " << endl << M << endl;
Mat M1(100, 100, CV_8UC1, Scalar(127)); //创建一个一通道的灰度图像
namedWindow("output", WINDOW_AUTOSIZE);
imshow("output", M1);
- create函数
Mat m1;
m1.create(src.size(), src.type()); //创造一个与src相同大小图像
m1 = Scalar(0, 0, 255); //表示红色
namedWindow("output", WINDOW_AUTOSIZE);
imshow("output", m1);
- zeros函数和eye函数
//元素点像素值为全0,表示纯黑色的图片,与输入图像大小一致
Mat m2 = Mat::zeros(src.size(), src.type());
namedWindow("output", WINDOW_AUTOSIZE);
imshow("output", m2);
Mat m3 = Mat::zeros(2, 2, CV_8UC1);
cout << "m3 = " << endl << m3 << endl;
Mat m4 = Mat::eye(2, 2, CV_8UC1);
cout << "m4 = " << endl << m4 << endl;
【注】
- 在RGB系统中,图像是这样存储的:(注意是BGR的形式)