质量声明:原创文章,内容质量问题请评论吐槽。如对您产生干扰,可私信删除。
主要参考:<学习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 ] [0,255] [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 ] [0.0,1.0] [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();