• C++ opencv图像存储和MAT容器



    1.图像在内存之中的存储方式:


    图像矩阵的大小取决于所用的颜色模型,确切说,取决于所用通道数。如果是灰度图像,矩阵就会如图5.1所示。
     

     

    对于多通道图像来说,矩阵中的列会包含多个子列,其子列个数与通道数相同,如图5.2所示RGB颜色模型的矩阵。

    可以看到,OpenCV中子列的通道顺序是反过来的——BGR而不是RGB。 有时候,由于内存足够大,可实现连续存储,图像中的各行是一行一行连接起来的,形成一个长行。可以使用isContinuous()来判断矩阵是否是连续存储的。

    2.mat矩阵   (图像存储类型)

    1.概念:

    Mat是一个类,由两部分组成:

    什么是Mat呢,Mat其实就是matrix(矩阵)的缩写
    我们看到的图像,就是以数字矩阵的形式存储在计算机中,在opencv中,我们用Mat类的对象存储图像。

    在opencv中,Mat类分为两个部分

    • 矩阵头
    • 矩阵数据

    矩阵头

    图像有很多属性。如:大小,宽和高,数据类型,通道数。这些数据存储在矩阵头

    矩阵数据

    图像也有很多的数据,图像的数据部分是所有像素的值的一个集合,存储在矩阵数据

    在这里插入图片描述

    如下边代码:

    1. Mat A, C;  // 仅创建信息头部分
    2. A = imread("1.jpg", CV_LOAD_IMAGE_COLOR)  // 这里为矩阵开辟内存
    3. Mat B(A)  //使用拷贝构造函数
    4. C=A;     //赋值运算符
    5. 上述代码中的所有Mat对象最终都指向同一个也是唯一一数据矩阵。虽然它们的信息头不同,但通过任何一个对象所做的改变也会影响其他对象。
    6. 创建部分信息数据的信息头:只需要创建包含边界信息的信息头
    7. Mat D(A, Rect(10, 10, 100, 100));  //使用矩形界定
    8. Mat E = A(Range:all(), Range(1, 3)
    9. 复制矩阵本身(不只是复制信息头和矩阵指针):使用函数clone()、copyTo()
    10. Mat F = A.clone();
    11. Mat G;
    12. A.copyTo(G)
    13. 上述代码,改变F和G就不会影响Mat信息头所指向的矩阵。

    2.Mat对象复制


    看上图,我们发现Mat对象复制是有三种方法的

    克隆
    拷贝
    赋值


    这里说一下它们的区别

    赋值:相当于浅复制,只复制了矩阵头,指向的是同一个数据块。类似浅拷贝,指针直接指向

    克隆/拷贝:相当于深复制,还会复制相应的数据块

    1. //1.赋值——浅复制
    2. Mat src = imread("……");
    3. Mat m3 = src;
    4. //2.克隆——深复制
    5. Mat src = imread("……");
    6. Mat m1 = src.clone();
    7. //3.拷贝——深复制
    8. Mat src = imread("……");
    9. Mat m2;
    10. src.copyTo(m2);

    3.Mat对象属性


    Mat对象存储了一些属性,如:列数,行数,通道数(维度),位深度,图像类型

    Mat image= imread("……");

    获取方式:

    1. image.cols;列
    2. image.rows;行
    3. image.channels();通道
    4. image.depth();深度
    5. image.type();类型
    6. image.size() 长x高

    数据类型和通道数
    图像的数据类型type由两部分组成

    std::cout << m << std::endl;//为了说明以上语句的不同,我们把矩阵进行打印

    说明:

    • 通道数

    在opencv中type是枚举类型的数值
    如CV_8UC3:表示 8位无符号整数(字节类型)三通道。枚举数值16
    在这里插入图片描述
    忽略掉前面的字符,我们只关注Cx,可以很快的发现,Cx即表示通道数channels。
    如C1——单通道,C2——双通道……

    注意:

    当为单通道时,C1可以省略,直接为CV_8U
    单通道为灰度图像,三通道为彩色图像

    • 深度

    图像深度depth和数据类型关联密切,其在opencv中也为枚举数值。

    在这里插入图片描述
    图像深度有真实值和枚举值之分。

    • 枚举值

    图像depth的枚举值跟通道数无关,相同类型下如CV_8UC1和CV_8UC3,图像深度的枚举值是一样的

    • 真实值

    图像depth的真实值还要考虑通道数,如CV_8UC3 的通道数为 8*3=24位

    3.Mat对象的创建 与初始化

     
    1.使用无参数构造函数,创建Mat对象。

    1. Mat image = Mat();
    2. image.create(4, 4, CV_8UC3);//创建一个4x4大小的像素块,每个像素都是三通道每个通道的位数都是8位
    3. 1
    4. 2
    5. 上述CV_8UC3中的8表示8位,UC表示uchar类型,3表示三个通道。


    2.使用带行、列、类型这个三个参数的构造函数创建Mat对象

    1. Mat m = Mat(4, 4, CV_8UC3); //创建一个4x4大小的像素块,每个像素都是三通道每个通道的位数都是8位
    2. 与方法二创建的像素块一样。


    3.使用行、列、类型、Scalar向量四个参数的构造函数创建Mat对象。

    1. Mat m = Mat(4, 4, CV_8UC3, Scalar(0, 255, 255)); 
    2. //创建一个4x4大小的像素块,每个像素都是三通道每个通道的位数都是8位,指定三通道颜色值向量Scalar(0, 255, 255)

    同样表示创建一个4x4的像素块,唯一的区别是颜色不是默认值,而是我们指定的三通道颜色值向量Scalar(0, 255, 255)。其中Scalar向量数目永远是等于通道数目。其他赋值情况也一样


    4.使用大小、类型两个参数的构造函数创建Mat对象。

    Mat m = Mat(Size(4, 4), CV_8UC3); //创建一个4x4大小的像素块,每个像素都是三通道每个通道的位数都是8位


    5.使用大小、类型、Scalar向量三个参数的构造函数创建Mat对象。

    Mat m = Mat(Size(4, 4), CV_8UC3, Scalar(255, 0, 0)); //创建一个4x4大小的像素块,每个像素都是三通道每个通道的位数都是8位

    Mat对象创建,常用的是创建空白图像。如下演示了三种

    Mat src = imread("……");


    Mat m4 = Mat::zeros(src.size(),src.type())
    /*
    矩阵填充0
        行列为src行列
            数据类型为src的数据类型
    */

    Mat m5 = Mat::zeros(Size(512,512),CV_8UC3);
    /*
    矩阵填充0
        行列为512*512
            数据类型为CV_8UC3(8UC指8位无符号字符,3指3个通道)            
    */

    Mat m6 = Mat::ones(Size(512,512),CV_8UC3);
    /*
    矩阵填充1
        其余与上相同
    */


    Mat kernel = (Mat_(3,3)<<0,-1,0,-1,5,-1,0,-1,0); 位数,只有灰度
    /*
    矩阵自由填充 类型
        
    */

    4.Mat对象的赋值

    最常用的有4种方法

    • Mat::zeros()
    • Mat::ones();
    • =
    • Scalar

    现在开始分别说明

    • Mat::zeros

    创建空矩阵

    Mat m = Mat::zeros(Size(8, 8), CV_8UC1);
    Mat m = Mat::zeros(Size(8, 8), CV_8UC3);

    矩阵宽度 = 图像的列 * 通道数

    当为CV_8UC1时,单通道,矩阵宽度8
    当为CV_8UC3时,三通道,矩阵宽度24


    在这里插入图片描述
    在这里插入图片描述

    • Mat::ones

    创建1矩阵

    1. Mat m = Mat::ones(Size(8, 8), CV_8UC1);
    2. Mat m = Mat::ones(Size(8, 8), CV_8UC3);

    注:

    当填充为1的时候,要特别小心

    单通道没问题,但是三通道这种多通道,只在第一个通道为1
    在这里插入图片描述

    • 重载的 =

    直接赋值

    m=127;
    

    和Mat::ones相同,单通道没问题,多通道只赋值第一个
    在这里插入图片描述

    • Scalar

    前面的三种方式,只用Mat::zeros实现了多通道的赋值,但是只能赋值为0,非常局限。

    下面介绍一个非常常用的赋值方法,比如我们赋值三通道,全变127

    不能直接加,会有问题,加法减法见后面

    m=Scalar(127,127,127);
    

    在这里插入图片描述

    5.Mat对象的显示


    在opencv中Mat矩阵就是图像,那我们来显示一下全127对应的图像

    1. void QuickDemo::mat_creation_demo(Mat& image) {
    2.     //创建空白图像
    3.     Mat m3 = Mat::ones(Size(400, 400), CV_8UC3);
    4.     m3 = Scalar(127, 127, 127);
    5.     imshow("创建图像", m3);
    6. }

    6.总结:

    OpenCV函数中输出图像的内存分配是自动完成的(如果不特别指定的话)

    使用OpenCV的C++接口时不需要考虑内存释放问题

    赋值运算符和拷贝构造函数(构造函数)只复制信息头和矩阵指针

    使用clone()函数或copyTo()来复制一幅图像的矩阵

    像素值的存储方法:


    RGB:最常见

    HSV、HSL:将颜色分解成色调、饱和度、亮度/明度

    YCrCb:JPEG图像格式广泛使用

    CIE L*a*b*:在感知上均匀的颜色空间,适合度量两个颜色之间的距离
     

  • 相关阅读:
    如何批量跟踪京东物流信息
    自动化测试的类型​以及自动化测试的几个误区!
    终于有阿里p8进行了大汇总(Redis+JVM+MySQL+Spring)还有面试题解全在这里了!
    PaddleNLP通用信息抽取模型:UIE【信息抽取{实体关系抽取、中文分词、精准实体标。情感分析等}、文本纠错、问答系统、闲聊机器人、定制训练】
    轻松整理电脑文件:按大小归类保存,高效管理你的数据
    码农死磕这份Java高级开发文档,成功‘挤‘进一线大厂,这也太强了吧
    QCustomPlot单多坐标系显示
    QLineEdit 使用QValidator 限制各种输入
    并发编程八 Collections之Map&List&Set
    FreeRTOS 任务调度和任务的状态
  • 原文地址:https://blog.csdn.net/weixin_46098612/article/details/127680790