Opencv_04 图像的数据类型Mat详解

文章目录

*

+ 一. Mat数据类型介绍
+ 二. Mat的常用操作
+
* ① 创建Mat对象,常用的Mat构造函数
* ② Mat的行与列相关的操作
* ③ 拷贝和转换
* ④ Mat类常用的成员属性
* ⑤ 图像的基本信息
* ⑥ 按照类型生成图像矩阵

一. Mat数据类型介绍

  1. 首先 Mat数据你不需要手动管理它的内存,如果你传递了一个已经存在的 Mat对象,它已经为矩阵分配了所需的内存空间,它将被重用.

  2. Mat包含两个数据部分的类:矩阵头(包含诸如矩阵大小,用于存储的方法,存储的矩阵地址等信息)和指向包含像素值矩阵(根据选择的存储方法而有不同的维度)的指针.矩阵头大小是个常量,不同大小的图像的矩阵大小各不相同,通常矩阵大小要比图像大小大几个数量级.

  3. opencv是一个图像处理库,其中包含大量图像处理函数.为了解决计算难题,多数情况下选用库中的多个函数来实现计算功能,常见的做法是将图像传递给函数.而图像处理算法的计算量往往非常大,所以要通过避免不必要的图像复制来进一步提升程序的运行速度.为了解决上述问题,Opencv采用了一种引用计数系统.

具体做法是,每个Mat对象有其各自的头,两个Mat对象可以通过将矩阵指针指向同一块地址来共享一个矩阵,复制操作只复制Mat头和指向矩阵的指针,而不是复制数据本身


#include "MyOpencv.h"
string imagePath = IMAGE_PATH + "\\lena.jpg";
int main()
{
    Mat A, C;
    cout << "A.size = " << sizeof(A) << " C.size = " << sizeof(C) << endl;
    A = imread(imagePath);
    cout << "A.size = " << sizeof(A) << " A.data = " <<
     A.cols * A.rows * A.channels() * A.elemSize() << endl;
    Mat B(A);
    C = A;
    return 0;
}

&#x89E3;&#x6790;:

上述所有的对象均指向同一个数据矩阵,对矩阵的任何变动均会影响所有的对象.在实际的示例中,不同的对象只是对同一数据的不同方式的访问,尽管如此,不同 MAT对象的头各不相同.问题来了,如果像素矩阵可以属于多个 MAT对象,那么当它不需要再次被使用时,由谁来负责清空?答案是:通过引用计数机制来实现,由最后一个使用它的对象来清空.每次拷贝MAT对象头时,计数器便会加1;当对MAT对象头进行清空时,此计数会减一.当计数器值为零的时候,矩阵会被释放.当需要对矩阵本身进行复制的时候,Opencv提供了如下两个方法

Mat F = A.clone();
Mat G;
A.copyTo(G);

修改F或者G不会影响A所指向的矩阵,需要记住以下几点:

  1. Opencv函数,输出图像分配时是自动的(除非另行规定)
  2. 无需考虑OPencv中的C++的接口的内存管理
  3. 赋值操作符和拷贝构造函数仅仅复制MAT对象头和那个data指针
  4. 图像的基本矩阵可以利用cv::Mat::clone() 和cv::Mat::copyTo()两个函数进行复制

二. Mat的常用操作

① 创建Mat对象,常用的Mat构造函数

&#x57FA;&#x672C;: &#x5C3A;&#x5BF8;+&#x7C7B;&#x578B;


Mat(int rows,int cols,int type);

Mat(Size size,int type);

Mat(int ndims,const int * sizes,int type);

Mat(cosnt std::vector<int>& sizes,int type);

&#x9996;&#x5148;&#x89E3;&#x91CA;&#x4E0B;type&#x53D8;&#x91CF;&#x7684;&#x542B;&#x4E49;

在构造哈数中,通常可以看到需要输入一个整型的变量type,这个参数通过宏定义在相关头文件中.用来指明Mat对象的存储的数据类型(主要是data部分)

CV_[&#x4F4D;&#x6570;][&#x5E26;&#x7B26;&#x53F7;&#x4E0E;&#x5426;][&#x7C7B;&#x578B;&#x524D;&#x7F00;]C[&#x901A;&#x9053;&#x6570;] CV_8UC1 -> 表示8位单通道无符号char类型数组,
CV_32FC2表示一个2通道的32位浮点型数组.

depth:&#x6DF1;&#x5EA6;

  • CV_8U: bool或者uchar
  • CV_8S: schar或者char
  • CV_16U:ushort
  • CV_16S:short
  • CV_32S:int 或者unsigned int
  • CV_32F: float
  • CV_64F:double

Mat&#x6709;&#x4E00;&#x4E2A;type()&#x51FD;&#x6570;&#x53EF;&#x4EE5;&#x8FD4;&#x56DE;&#x8BE5;Mat&#x7684;&#x7C7B;&#x578B;.&#x7C7B;&#x578B;&#x8868;&#x793A;&#x4E86;&#x77E9;&#x9635;&#x4E2D;&#x5143;&#x7D20;&#x7684;&#x7C7B;&#x578B;&#x4EE5;&#x53CA;&#x77E9;&#x9635;&#x901A;&#x9053;&#x4E2A;&#x6570;,&#x5B83;&#x662F;&#x4E00;&#x7CFB;&#x5217;&#x7684;&#x9884;&#x5B9A;&#x4E49;&#x7684;&#x5E38;&#x91CF;,&#x5176;&#x547D;&#x540D;&#x89C4;&#x5219;&#x4E3A;CV_(&#x4F4D;&#x6570;) + (&#x6570;&#x636E;&#x7C7B;&#x578B;) + (&#x901A;&#x9053;&#x6570;)

Opencv_04 图像的数据类型Mat详解

&#x5B9E;&#x4F8B;:

#include"MyOpencv.h"
#include
int main()
{

    Mat m1 = Mat(3, 3, CV_8UC1);
    Size size(3, 4);

    Mat m2 = Mat(size, CV_8UC1);

    int sizes[] = { 2,3 };
    Mat m3 = Mat(2, sizes, CV_8UC1);

    vector<int> v;
    v.push_back(2);
    v.push_back(3);
    v.push_back(3);
    Mat m4 = Mat(v, CV_8UC3);
    return 0;
}

&#x5C3A;&#x5BF8; + &#x989C;&#x8272;

Mat(int rows,int cols,int type,const Scalar& s);

Mat(Size size,int type,const Scalar& s);

Mat(int ndims,const int* sizes,int type,const Scalar& s);

Mat(const std::vector<int>& sizes,int type,cosnt Scalar& s);

&#x6D4B;&#x8BD5;&#x4EE3;&#x7801;:

#include "MyOpencv.h"
#include

int main()
{

    Mat m1 = Mat(3, 3, CV_8UC1, cv::Scalar(0));

    Mat m2 = Mat(4, 4, CV_8UC3, cv::Scalar(0,0, 255));

    Mat m3 = Mat(cv::Size(3, 4), CV_8UC3, cv::Scalar(1, 2, 3));

    int sizes[] = { 2,3 };
    Mat m4 = Mat(2, sizes, CV_8UC1, cv::Scalar(1));

    vector<int> v;
    v.push_back(4);
    v.push_back(3);
    Mat m5 = Mat(v, CV_8UC1, cv::Scalar(8));
    cout << "m1 = \n" << m1 << endl;
    cout << "m2 = \n" << m2 << endl;
    cout << "m3 = \n" << m3 << endl;
    cout << "m4 = \n" << m4 << endl;
    cout << "m5 = \n" << m5 << endl;

    system("pause");
    return 0;
}

结果:

Opencv_04 图像的数据类型Mat详解

&#x57FA;&#x672C;+&#x6570;&#x636E;

第三种类型就是基本类型中添加数据 data,也就是尺寸+类型+数据,其中表示数据的是: void* data

Mat(int rows,int cols,int type,void *data,size_t step=AUTO_SETP);

Mat(Size size,int type,void * data,size_t step=AUTO_STEP);

Mat(int ndims,const int * sizes,int type,void * data,const size_t* steps = 0);

Mat(cosnt std::vector<int>& sizes,int type,void* data,const size_t* steps = 0);

&#x53C2;&#x6570;&#x8BF4;&#x660E;:

  • void* data: 指向实现准备好的用户的数据的指针.矩阵构造器将会取数据和步长参数,而不会分配矩阵数据.取而代之的是,仅仅初始化矩阵的头去指向具体的数据,这意味着数据不会被拷贝.这个操作将会非常的高效,用来处理外部的数据,值得注意的是,这些外部的数据不会被自动释放
  • step: 矩阵每行占据的字节数.这个值必须包含每行补齐的数据.如果不提供这个参数,就假设没有补齐,实际的步长就等于cols*elemSize();

&#x8FD9;&#x79CD;&#x5E26;void*data&#x7684;&#x51FD;&#x6570;&#x5728;&#x4F7F;&#x7528;&#x65F6;&#x8981;&#x6CE8;&#x610F;:

  1. 这个函数在执行的时候,只是初始化了mat的头,不拷贝数据,也就是使得mat->data指向了这个外部数据的data
  2. 外部数据data需要另外释放,opencv的内存管理不会对这一部分内存进行释放
  3. 如果传入的data是一个局部对象的data,后期在传出时必须要进行深拷贝可以传出正确的数据
  4. 如果传入的数据是用户data,需要自己手动释放掉,如果是opencv的Mat的data会进行自动内存管理.

&#x4E3E;&#x4F8B;:

#include "MyOpencv.h"
#include
int main()
{
    Mat m1 = Mat(3, 4, CV_8UC1, cv::Scalar(2));

    Mat m2 = Mat(4, 3, CV_8UC1, (void *)m1.data);

    Mat m3 = Mat(cv::Size(2, 3), CV_8UC2, (void *)m1.data);

    int sizes[] = { 2,2 };
    Mat m4 = Mat(2, sizes, CV_8UC3, (void *)m1.data);

    vector<int> v;
    v.push_back(4);
    v.push_back(1);
    Mat m5 = Mat(v, CV_8UC3, (void *)m1.data);

    cout << "m1 = \n" << endl;
    cout << m1 << endl;
    cout << "m2 = \n" << endl;
    cout << m2 << endl;
    cout << "m3 = \n" << endl;
    cout << m3 << endl;
    cout << "m4 = \n" << endl;
    cout << m4 << endl;
    cout << "m5 = \n" << endl;
    cout << m5 << endl;
    return 0;
}

结果:

Opencv_04 图像的数据类型Mat详解

Mat + &#x5176;&#x4ED6;

Mat(const Mat& m);

Mat(const Mat& m,const Range& rowRange,const Range& colRange=Range::all());

Mat(const Mat& m,const Rect& rio);

Mat(const Mat& m,const Range& ranges);

Mat(const Mat& m,const std::vector<Range>& ranges);

&#x53C2;&#x6570;&#x89E3;&#x91CA;:
Range:

用来指明一个序列的连续的子序列.拥有两个公共成员: startend.可以使用这两个成员来表示子序列的范围,左开右闭.可以使用Range.all()表示所有.

Rect&#x7C7B;&#x578B;:

创建一个矩形区域,可以用来提取感兴趣区域.前两位为一个坐标,后两位表示偏移量.

&#x6CE8;&#x610F;:

这些构造函数,没有复制数据,构造指向m数据,如果有引用计数器,则递增.当你修改矩阵的首,通过这样的构造函数,对应的m的数据也会被修改,如果你想要不修改,请使用Mat::clone()出一个副本.


#include "MyOpencv.h"

int main()
{
    Mat m1 = Mat(10, 10, CV_8UC1,cv::Scalar(6));

    Mat m2 = Mat(m1);

    Mat m3 = Mat(m1, cv::Range(0, 3), cv::Range::all());

    Mat m4 = Mat(m1, cv::Range::all(), cv::Range(0, 3));

    Mat m5 = Mat(m1, cv::Rect(4, 4, 2, 2));

    cout << "m1 = m2 = \n" << endl;
    cout << m2 << endl;
    cout << "m3 = \n" << endl;
    cout << m1 << endl;
    cout << "m4 = \n" << endl;
    cout << m4 << endl;
    cout << "m5 = \n" << endl;
    cout << m5 << endl;

    return 0;
}

结果:

Opencv_04 图像的数据类型Mat详解

② Mat的行与列相关的操作


Mat row(int y) const;

Mat col(int x) const;

Mat rowRange(int startrow,int endrow) const;

Mat rowRange(const Range& r) const;

Mat colRange(int startcol,int endcol) const;

Mat colRange(const Range& r) const;

&#x793A;&#x4F8B;:

#include "MyOpencv.h"

int main()
{
    Mat m1 = Mat(5, 5, CV_8UC1, cv::Scalar(1));

    Mat m2 = m1.row(0);

    Mat m3 = m1.col(1);

    Mat m4 = m1.rowRange(0, 2);
    Mat m5 = m1.rowRange(cv::Range(0, 2));

    Mat m6 = m1.colRange(1, 3);
    Mat m7 = m1.colRange(cv::Range(1, 3));
    cout << "m1 = \n" << endl;
    cout << m1 << endl;
    cout << "m2 = \n" << endl;
    cout << m2 << endl;
    cout << "m3 = \n" << endl;
    cout << m3 << endl;
    cout << "m4 = \n" << endl;
    cout << m4 << endl;
    cout << "m5 = \n" << endl;
    cout << m5 << endl;
    cout << "m6 = \n" << endl;
    cout << m6 << endl;
    cout << "m7 = \n" << endl;
    cout << m7 << endl;

    system("pause");
    return 0;
}

结果:

Opencv_04 图像的数据类型Mat详解

③ 拷贝和转换


Mat clone() const;

void copyTo(OutputArray m) const;

void copyTo(OutputArray m,inputArray mask) const;

void convertTo(OutputArray m,int rtype,double alpha=1,double beta=0)const;

void assignTo(Mat& m,int type=-1) const;

Mat& setTo(inputArray value,InputArray mask=noArray());

#include "MyOpencv.h"

int main()
{
    Mat m1 = Mat::ones(1, 4, CV_32F);
    Mat m2 = m1;
    Mat m3 = Mat::zeros(1, 4, CV_32F);
    m3.copyTo(m1);

    cout << m1 << endl;
    cout << m2 << endl;

    Mat m4 = Mat::ones(1, 5, CV_32F);
    m4.copyTo(m1);
    cout << m1 << endl;
    cout << m2 << endl;

    return 0;
}

结果:

Opencv_04 图像的数据类型Mat详解

#include "MyOpencv.h"

int main()
{
    Mat m1 = Mat(3, 3, CV_8UC1, cv::Scalar(2));
    m1.at<uchar>(0, 0) = 0;
    m1.at<uchar>(2, 2) = 0;
    Mat m2 = Mat(3, 3, CV_8UC1, cv::Scalar(1));

    m1.copyTo(m2, m1);
    cout << "m2 = " << endl;
    cout << m2 << endl;
    return 0;
}

结果:

Opencv_04 图像的数据类型Mat详解
  • m: 目标矩阵.如果m在运算前没有合适的尺寸或者类型,将被重新分配
  • rtype: 目标矩阵的类型.因为目标矩阵的通道数与源矩阵一样,所以 rtype也可以看做是目标矩阵的位深度.如果 rtype为负值(一般为-1),目标矩阵(输出矩阵)将使用和源矩阵(输入矩阵)相同的类型.

  • alpha: 尺度变换因子(缩放)

  • beta: 附加到尺度变换后的值上的偏移量(可选),即将输入数组元素按比例缩放后添加的值 dst(i) = src(i) * scale + (beta,beta,...);如果没有规定,则默认是0.

&#x8BF4;&#x660E;:

  1. 如果scale = 1,beta = 0,则不进行比例缩放,也即目标矩阵和源矩阵没有区别
  2. 如果输入数组与输出数组的类型相同,则函数可以被用于缩放和平移操作
  3. 如果输入数组和输出数组的类型不同,则用于做类型上的转换
  4. converTo()在进行转换的时候,输出的通道数与输入的通道数相同,即使你填入的转换类型通道数不同,输出的通道数还是与输入的通道数相同
  5. converTo()支持就地(in-place)操作

#include "MyOpencv.h"
string imagePath = IMAGE_PATH + "\\lena.jpg";
int main()
{
    Mat imgColor = imread(imagePath, IMREAD_COLOR);
    Mat imgGray = imread(imagePath, IMREAD_GRAYSCALE);
    cout << "Original: " << endl;
    cout << "ImageColor.type() = " << imgColor.type() << endl;
    cout << "ImageGray.type() = " << imgGray.type() << endl;
    cout << "--------------------------------------------------" << endl;

    Mat imgColorC3;
    Mat imgGrayC3;
    imgColor.convertTo(imgColorC3, CV_16UC3);
    imgGray.convertTo(imgGrayC3, CV_16UC3);
    cout << "convertTo CV_16UC3: " << endl;
    cout << "ImageColorC3.type() = " << imgColorC3.type() << endl;
    cout << "ImageGrayC3.type() = " << imgGrayC3.type() << endl;

    cout << "--------------------------------------------------" << endl;

    Mat imgColorC1;
    Mat imgGrayC1;
    imgColor.convertTo(imgColorC1, CV_16UC1);
    imgGray.convertTo(imgGrayC1, CV_16UC1);

    cout << "convertTo CV_16UC1:" << endl;
    cout << "ImageColorC1.type() = " << imgColorC1.type() << endl;
    cout << "ImageColorC2.type() = " << imgGrayC1.type() << endl;

    cout << "--------------------------------------------------" << endl;

    imgColor.convertTo(imgColor, CV_16UC3);
    imgGray.convertTo(imgGray, CV_16UC3);
    cout << "In place: " << endl;
    cout << "imageColor.type() = " << imgColor.type() << endl;
    cout << "imageGray.type() = " << imgGray.type() << endl;

    return 0;
}

④ Mat类常用的成员属性


int flags;

int dims;

int rows,cols;

uchar* data;

flags&#x8BE6;&#x89E3;

Opencv_04 图像的数据类型Mat详解
  • 0-2位代表depth即数据类型(如CV_8U),Opencv的数据类型一共有7种,所以3位即可表示.

  • 3-11位代表通道数channels,因为Opencv的最大通道数为512,只需要9位即可表示全部

  • 0-11位共同代表type即通道数和数据类型(如 CV_8UC3)
  • 12-13位未使用
  • 14位代表Mat的内存是否连续,一般由creat创建的mat均是连续的,如果是连续的,将加快数据的访问
  • 15位代表改Mat是否为某一个Mat的submatrix,一般通过ROI以及row(),col(),rowRange,colRange()得到的mat均为submatrix.

  • 16-31代表magic signature,暂时理解为用来区分Mat的类型,如Mat和SparseMat


#include "MyOpencv.h"

int main()
{
    Mat m = Mat(3, 4, CV_8UC3,cv::Scalar(1,2,3));
    cout << "m.flags = " << m.flags << endl;
    cout << "m.dims = " << m.dims << endl;
    cout << "m.rows = " << m.rows << " m.cols = " << m.cols << endl;
    cout << "m = " << endl;
    cout << m << endl;

    m.rows = 2;
    m.cols = 2;
    cout << "2行2列: " << endl;
    cout << m << endl;

    m.flags = 0;
    cout << "flags = 0之后: m = " << endl;
    cout << m << endl;

    return 0;
}

结果:

Opencv_04 图像的数据类型Mat详解

⑤ 图像的基本信息


int type() const;

int depth() const;

int channels() const;

bool empty() const

&#x89E3;&#x6790;:
类型返回的是一个int类型,定位为宏数据,其对应的值表示的意思如下:

Opencv_04 图像的数据类型Mat详解
比如0 -> CV_8UC1, 1-> CV_8S_C1,依次类推

⑥ 按照类型生成图像矩阵

这些函数可以生成全是1,全是0,对角矩阵或者按照类型生成矩阵,主要有:


static MatExpr zeros(int rows,int cols,int type);
Mat A;
A = Mat::zeros(3,3,CV_32F);

static MatExpr zeros(Size size,int type);

static MatExpr zeros(int ndims,const int* sz,int type);

static MatExpr ones(int rows,int cols,int type);

static MatExpr ones(Size size,int type);

static MatExpr ones(int ndims,const int * sz,int type);

static MatExpr eye(int rows,int cols,int type);

static MatExpr eye(Size size,int type);

void create(int rows,int cols,int type);

void create(Size size,int type);

void create(int ndims,const int* sizes,int type);

void create(const std::vector<int>& sizes,int type);

&#x5B9E;&#x4F8B;&#x89E3;&#x6790;:

#include"MyOpencv.h"

int main()
{

    Mat m1 = Mat::zeros(2, 2, CV_8UC1);

    Mat m2 = Mat::zeros(cv::Size(3, 2), CV_8UC3);

    int sizes[] = { 3,4 };
    Mat m3 = Mat::zeros(2, sizes, CV_8UC1);

    Mat m4 = Mat::ones(2, 3, CV_8UC1);

    Mat m5 = Mat::eye(3, 3, CV_8UC1);

    cout << "m1 = " << endl;
    cout << m1 << endl;
    cout << "m2 = " << endl;
    cout << m2 << endl;
    cout << "m3 = " << endl;
    cout << m3 << endl;
    cout << "m4 = " << endl;
    cout << m4 << endl;
    cout << "m5 = " << endl;
    cout << m5 << endl;

    return 0;
}

结果:

Opencv_04 图像的数据类型Mat详解

&#x5173;&#x4E8E;create&#x51FD;&#x6570;&#x7684;&#x8BF4;&#x660E;

1)这个是Mat的关键方法之一,大多数新型的Opencv函数和方法对每个输出数组的创建都会调动此方法.

2)这个方法采用的算法如下:

  • &#x5982;&#x679C;&#x5F53;&#x524D;&#x7684;&#x6570;&#x7EC4;&#x548C;&#x5F62;&#x72B6;&#x5339;&#x914D;&#x65B0;&#x7684;,&#x7ACB;&#x5373;&#x8FD4;&#x56DE;.&#x5426;&#x5219;&#x91CA;&#x653E;&#x4E4B;&#x524D;&#x7684;&#x6570;&#x636E;&#x7684;&#x5F15;&#x7528;,&#x521D;&#x59CB;&#x5316;&#x65B0;&#x7684;&#x5934;,&#x4E3A;&#x6570;&#x636E;&#x5206;&#x914D;&#x65B0;&#x7684;&#x6570;&#x636E;,&#x5E76;&#x4E14;&#x5F15;&#x7528;&#x8BA1;&#x6570;&#x5668;&#x8BBE;&#x7F6E;&#x4E3A;1.&#x8FD9;&#x79CD;&#x8BBE;&#x8BA1;,&#x4F7F;&#x5F97;&#x7528;&#x6237;&#x4E0D;&#x7528;&#x663E;&#x793A;&#x7684;&#x6307;&#x5B9A;&#x8F93;&#x51FA;&#x6570;&#x7EC4;&#x7684;&#x5927;&#x5C0F;&#x548C;&#x7C7B;&#x578B;,&#x4F7F;&#x5F97;&#x53D8;&#x6210;&#x5F88;&#x65B9;&#x4FBF;

Original: https://blog.csdn.net/Fioman_GYM/article/details/124421118
Author: Fioman_Hammer
Title: Opencv_04 图像的数据类型Mat详解

原创文章受到原创版权保护。转载请注明出处:https://www.johngo689.com/701548/

转载文章受原作者版权保护。转载请注明原作者出处!

(0)

大家都在看

亲爱的 Coder【最近整理,可免费获取】👉 最新必读书单  | 👏 面试题下载  | 🌎 免费的AI知识星球