质量声明:原创文章,内容质量问题请评论吐槽。如对您产生干扰,可私信删除。
主要参考:<学习Opencv 3 中文版>, Mat数据类型总结, 像素值读取at()函数
文章目录
摘要: 随时更新完善opencv中Mat相关的定义,类型,访问和应用.
Mat矩阵的定义
opencv中的cv::Mat类用于定义任意维度的稠密数组,所谓稠密,是指该数组任一位置的元素都有确定值,即使是0.
独立定义
-
定义变量的同时进行初始化:
cv::Mat img(640, 480, CV_8UC1, cv::Scalar(100)); // 定义单通道灰度图, 640x480, 100
cv::Mat src(800, 640, CV_8UC3, cv::Scalar(70, 160, 70)); // 定义三通道彩色图,数据类型为8位无符号整型
cv::Mat dst(960, 540, CV_32FC3, cv::Scalar(1.0f, 0.0f, 0.0f)); // 定义三通道彩色图,数据类型为32位浮点型
-
先定义,再初始化:
cv::Mat src; src.create(800, 640, CV_8UC1); img.setTo(cv::Scalar(0));
cv::Mat src(800, 640, CV_8UC3); src.setTo(cv::Scalar(255, 0, 0));
引用定义(内存相同)
cv::Mat dst(src);
cv::Mat dst = src;
cv::Mat& dst = src;
cv::Mat roi(src, cv::Rect(0, 0, 200, 100));
复制定义(内存独立)
-
clone与copyTo:
cv::Mat dst = src.clone();
cv::Mat dst; src.copyTo(dst);
-
构造同型矩阵:
cv::Mat dst(src.size(), CV_8UC1, cv::Scalar(0));
-
独立提取ROI:
cv::Mat roi = src(cv::Rect(0, 0, 200, 100)).clone();
cv::Rect roi_rect(0, 0, 200, 100); cv::Mat roi = src(roi_rect).clone();
cv::Rect roi_rect(0, 0, 200, 100); cv::Mat roi; src(roi_rect).copyTo(roi);
特殊定义
-
零矩阵:
cv::Mat zeros = cv::Mat::zeros(100, 100, CV_8UC1);
-
幺矩阵:
cv::Mat ones = cv::Mat::ones(100, 100, CV_8UC1);
-
单位阵:
cv::Mat eye = cv::Mat::eye(100, 100, CV_8UC1);
-
特别注意: 如果创建多通道数组, 则只有第一通道被赋值, 其余通道为全0. 以下示例创建的是个蓝色图像, 而不是纯白图像:
cv::Mat blue= cv::Mat::ones(100, 100, CV_8UC3) * 255;
模板定义
(待完善)
cv::Mat m(const cv::Vec<T, n>& vec, bool = copyData = true);
cv::Mat(const cv::Matx<T, m, n>& vec, bool copyData = true);
cv::Mat(const std::vector<T>& vec, bool copyData = true);
Mat矩阵的数据类型
基本类型
- 明确矩阵元素的数据类型,对于访问数组元素和元素运算至关重要,选错数据类型会产生错误结果,误导后续处理.
- 一个有效的数据类型需要同时指明数据的类型和通道数,格式为
CV_{8U,8S,16U,16S,32S,32F,64F }C{1,2,3}
数值 | 位数bits | 具体类型 | 取值范围 | 等同C++变量 |
---|---|---|---|---|
CV_8U | 8 位 | 无符号整数 | (0……255) | uchar , unsigned char |
CV_8S | 8 位 | 带符号整数 | (-128……127) | char |
CV_16U | 16 位 | 无符号整数 | (0……65535) | ushort , unsigned short , unsigned short int |
CV_16S | 16 位 | 带符号整数 | (-32768……32767) | short , short int |
CV_32S | 32 位 | 带符号整数 | (-2147483648……2147483647 ) | int , long |
CV_32F | 32 位 | 单精度浮点数 | ±(1.18e-38……3.40e38) | float |
CV_64F | 64 位 | 双精度浮点数 | ±(2.23e-308……1.79e308) | double |
- 注:CV_USRTYPE1 support has been dropped in OpenCV 4.0.
- 如何获取未知矩阵的数据类型, 最直接的方法是查阅接口文档, 熟记常用变量的数据类型
- 也可以通过成员函数type()获得Mat矩阵元素的数据类型,不建议使用depth()获取通道中的元素类型. 但type()函数返回的是int型数值,需进一步查表, 才能得到对应的数据类型.
类型 | C1 | C2 | C3 | C4 |
---|---|---|---|---|
CV_8U | 0 | 8 | 16 | 24 |
CV_8S | 1 | 9 | 17 | 25 |
CV_16U | 2 | 10 | 18 | 26 |
CV_16S | 3 | 11 | 19 | 27 |
CV_32S | 4 | 12 | 20 | 28 |
CV_32F | 5 | 13 | 21 | 29 |
CV_64F | 6 | 14 | 22 | 30 |
提示: PNG 格式的彩色图像除了 BGR 3个通道外,还有一个透明度通道,所以存在 C4.
示例:
cv::Mat src = cv::Mat::ones(100, 100, CV_8SC3);
cout << "type = " << src.type() << endl;
cout << "depth = " << src.depth() << endl;
输出:
type = 17 查表知, 数据类型为 CV_8SC3
depth = 1 查表知, 通道中的数据类型为 CV_8S, 但未指明通道数. 因此, 不建议使用depth()获取通道中的元素类型
数据类型转换 convertTo
- 转浮点数
img.convertTo(img, CV_32F);
- 转整型
img.convertTo(img, CV_8U);
- 转为可显示图像Mat
// 必须是[0,1]的浮点数, 缩放
img.convertTo(img, CV_32F, 1./255);
// 或者是[0,255]的整数, 不缩放
img.convertTo(img, CV_8U);
归一化 cv::normalize
- 区间化为 [0,255] 整数
img.convertTo(img, CV_8U);
cv::normalize(img, img, 0, 255, cv::NORM_MINMAX);
cv::imshow("range.jpg", img);
- 归一化为 [0.0,1.0] 的浮点数
img.convertTo(img, CV_32F);
cv::normalize(img, img, 1, 0, cv::NORM_MINMAX);
cv::imshow("normalization.jpg", img);
Mat数组的访问
独立访问数组元素
- 模板函数
at<>()
可以实现对数组元素进行独立访问, 但对不同数据类型的数组有不同的参数要求. - 以单通道为例,at 方法接受的数据类型是 uchar ,而非 CV_8U, 即
img.at<uchar>(2,3)
数据类型/at参数 | C1 | C2 | C3 | C4 | C6 |
---|---|---|---|---|---|
CV_8U | uchar | cv::Vec2b | cv::Vec3b | cv::Vec4b | |
CV_8S | / | / | / | / | |
CV_16U | ushort | cv::Vec2w | cv::Vec3w | cv::Vec4w | |
CV_16S | short | cv::Vec2s | cv::Vec3s | cv::Vec4s | |
CV_32S | int | cv::Vec2i | cv::Vec3i | cv::Vec4i | |
CV_32F | float | cv::Vec2f | cv::Vec3f | cv::Vec4f | cv::Vec6f |
CV_64F | double | cv::Vec2d | cv::Vec3d | cv::Vec4d | cv::Vec6d |
- 常见的对应关系
CV_8U | CV_32S | CV_32F | CV_64F |
---|---|---|---|
uchar | int | float | double |
示例:
cv::Mat img = cv::Mat::ones(20, 4, CV_64FC3) * CV_PI;
auto vec3d = img.at<cv::Vec3d>(2, 1);
auto vec3d0 = img.at<cv::Vec3d>(2, 1)[0];
auto vec3d1 = img.at<cv::Vec3d>(2, 1)[1];
auto vec3d2 = img.at<cv::Vec3d>(2, 1)[2];
std::cout << "vec3d = " << vec3d << std::endl;
std::cout << "vec3d0 = " << vec3d0 << std::endl;
std::cout << "vec3d1 = " << vec3d1 << std::endl;
std::cout << "vec3d2 = " << vec3d2 << std::endl;
输出:
vec3d = [3.14159, 0, 0]
vec3d0 = 3.14159
vec3d1 = 0
vec3d2 = 0
提示: 错误的数据类型将输出错误的取值. 如cv::Vec3i
的取值是全零.
区块访问与赋值
-
单行/单列
src.row(30) = cv::Scalar(255, 255, 255); // 将图像第30行设为白色 src.col(30) = cv::Scalar(255, 255, 255); // 将图像第30列设为白色
-
多行/多列
src.rowRange(30, 50) = cv::Scalar(255, 255, 255); // 将图像第30~50行设为白色 src.colRange(30, 50) = cv::Scalar(255, 255, 255); // 将图像第30~50列设为白色
-
矩形区域
(x1, y1, w, h)
src(cv::Rect(0, 0, 200, 100)) = cv::Scalar(200, 0, 150);
cv::Rect roi_rect(0, 0, 200, 100); src(roi_rect).setTo(cv::Scalar(200, 0, 150));
src(cv::Range(0, 100), cv::Range(0, 200)) = cv::Scalar(200, 0, 150);
区域替换
cv::Mat src(800, 640, CV_8UC3, cv::Scalar(70, 160, 70));
cv::Mat dst(800, 640, CV_8UC3, cv::Scalar(10, 10, 200));
cv::Rect roi_rect(0, 0, 200, 100);
src(roi_rect).copyTo(dst(roi_rect));
// 无效替换 dst(roi_rect) = src(roi_rect);
// 无效替换 dst(roi_rect) = src(roi_rect).clone();