时间为友,记录点滴。

不知道你有没有跟我一样,看到一些关键的类或结构体总想看看他的结构是什么?因为我觉得单单知其然是不够的,知其所以然才能走得更远。

虽然我们只写了两个例子,但是很明显,这里面的核心内容是Mat类。

The class Mat represents an n-dimensional dense numerical single-channel or multi-channel array. It can be used to store real or complex-valued vectors and matrices, grayscale or color images, voxel volumes, vector fields, point clouds, tensors, histograms (though, very high-dimensional histograms may be better stored in a SparseMat ).
上面的一段话引用自官方的文档,Mat类用于表示一个多维的单通道或者多通道的稠密数组。能够用来保存实数或复数的向量、矩阵,灰度或彩色图像,立体元素,点云,张量以及直方图(高维的直方图使用SparseMat保存比较好)。简而言之,Mat就是用来保存多维的矩阵的。

如果你在mat.hpp中点开Mat的类定义,估计要被它一长长串的构造函数和重载看崩溃,我们直接略过令人崩溃的地方(在实际使用中,常用的定义一个Mat类型的对象的方法就那几个),看到下面的成员变量:

  • data:

uchar型的指针。Mat类分为了两个部分:矩阵头和指向矩阵数据部分的指针,data就是指向矩阵数据的指针。

  • dims:

矩阵的维度,例如5*6矩阵是二维矩阵,则dims=2,三维矩阵dims=3.

  • rows:

矩阵的行数

  • cols:

矩阵的列数

  • size:

矩阵的大小,size(cols,rows),如果矩阵的维数大于2,则是size(-1,-1)

  • channels:

矩阵元素拥有的通道数,例如常见的彩色图像,每一个像素由BGR三部分组成,则channels = 3。

  • type:

表示了矩阵中元素的类型以及矩阵的通道个数,它是一系列的预定义的常量,其命名规则为CV_(位数)+(数据类型)+(通道数)。这里U(unsigned integer)表示的是无符号整数,S(signed integer)是有符号整数,F(float)是浮点数。

例如:CV_16UC2,表示的是元素类型是一个16位的无符号整数,通道为2. C1,C2,C3,C4则表示通道是1,2,3,4。type一般是在创建Mat对象时设定,如果要取得Mat的元素类型,则无需使用type,使用下面的depth

输出的数值,对照表
  • depth

矩阵中元素的一个通道的数据类型,这个值和type是相关的。例如 type为 CV_16SC2,一个2通道的16位的有符号整数。那么,depth则是CV_16S。depth也是一系列的预定义值,
将type的预定义值去掉通道信息就是depth值:
CV_8U CV_8S CV_16U CV_16S CV_32S CV_32F CV_64F

  • elemSize

矩阵一个元素占用的字节数,例如:type是CV_16SC3,那么elemSize = 3 * 16 / 8 = 6 bytes

  • elemSize1

矩阵元素一个通道占用的字节数,例如:type是CV_16CS3,那么elemSize1 = 16 / 8 = 2 bytes = elemSize / channels

如何创建一个Mat也有好多中方法(我最常用的就是下面这三种):

  1. Mat M(2, 2, CV_8UC3, Scalar(0, 0, 255));
  2. Mat Z = Mat::zeros(3, 3, CV_64F);Mat F = Mat::ones(2, 2, CV_64F);Mat F = Mat::ones(2, 2, CV_64F);
  3. Mat M = (Mat_<double>(3, 3) << 0, 1, 2, 1, 2, 3, 2, 3, 4) ;

多说无益,我们用代码来验证,可以写一个函数来把img的各个参数打印出来:

现有图片测试

static void showImgPara(Mat &img)
{cout << "sizeof(img) is: " << sizeof(img) << ", img size is: " << img.size << endl;cout << "rows x cols: (" << img.rows << " x " << img.cols << ")" << endl;cout << "dims: " << img.dims << endl;cout << "channels: " << img.channels() << endl;cout << "type: " << img.type() << endl;cout << "depth:" << img.depth() << endl;cout << "elemSize:" << img.elemSize() << " (Bytes per element)" << endl;cout << "elemSize1:" << img.elemSize1() << "(Bytes per channel)" << endl;cout << "step[0]: " << img.step[0] << " (Bytes per cows only when 2 dims)" << endl;cout <<   "step[1]: " << img.step[1] << " (Bytes per element only when 2 dims)" << endl;cout << "step1(0): " << img.step1(0) << ", step1(1): " << img.step1(1) << " (step / elemSize1)" << endl;
}

static bool pictureTest(Mat &img, string name)
{if (img.empty()){cout << "Load picture fail!" << endl;return false;}cout << endl << "/******************pictureTest******************/" << endl;showImgPara(img);namedWindow(name);imshow(name, img);return true;
}

int main()
{Mat pic = imread("girl.jpg");pictureTest(pic, "girl");waitKey(0);return 0;
}

运行结果

自建Mat类型:

除了看自己创建Mat对象的一些属性,主要考量把自建对象的数据dump出来,可以看到使用了step。

static bool matTest(Mat &img)
{uchar* pTemp = NULL;cout << endl << "/******************matTest******************/" << endl;cout << img << endl;showImgPara(img);for (int i = 0; i < img.rows; i++){for(int j = 0; j < img.cols; j++){cout << "[";for (int k = 0; k < img.step[1]; k++){pTemp = img.data + img.step[0] * i + img.step[1] * j + k;cout << (int)*pTemp << " ";}cout << "] ";}cout << endl;}return true;
}

创建一个3行4列, 每个元素是8bit(unsigned char),包含两个通道,并且初始化为[1, 2]

int main()
{Mat m2(3, 4, CV_8UC2, Scalar_<uchar>(1, 2));matTest(m2);waitKey(0);return 0;
}

matTest

随意创建一张涂色图片

我们除了提前给data赋值,也可以通过data指针修改图像中的任意像素。比如下图可以修改创建图片的中间方块为任意颜色(本例子中全部涂黑),并且通过imshow直观的体现出来。

static bool picCreat(Mat &img, string name)
{for (int i = 0; i < img.rows; i++){uchar *p = img.ptr<uchar>(i);for (int j = 0; j < img.cols * img.channels(); j += img.channels()){if (i > img.rows/4 && i < img.rows*3/4 && j > img.cols*img.channels()/4 && j < img.cols*img.channels()*3/4){p[j] = 0;p[j + 1] = 0;p[j + 2] = 0;}else{p[j] = 255;p[j + 1] = 255;p[j + 2] = 255;}}}cout << endl << "/******************picCreat******************/" << endl;showImgPara(img);namedWindow(name);imshow(name, img);return true;
}

创建一个200x300大小的图片。

int main()
{Mat m1 = Mat::zeros(200, 400, CV_8UC3);picCreat(m1, "White");waitKey(0);return 0;
}

通过填充数据的方法生成一张中间是黑色的图片
picCreate

图片导成文件

为了肉眼观测方便,实现了一个多通道转单通道的函数。(暴力得是把第一个通道的数据取出来)

static bool tran2singleChannel(Mat &src, Mat &tar)
{vector<Mat> channels;split(src, channels);tar = channels.at(0);return true;
}

把一个任意图片,转换成单通道,把data dump出来,存在文件中。

static bool pic2digit(Mat &img, string picName, string fileName)
{uchar*     pTemp = NULL;Mat           signalChannel;ofstream  output;if (img.empty()){cout << "Load picture fail!" << endl;return false;}cout << endl << "/******************pic2digit******************/" << endl;//showImgPara(img);//namedWindow(picName);//imshow(picName, img);tran2singleChannel(img, signalChannel);//namedWindow(picName + "_signalChannel");//imshow(picName + "_signalChannel", signalChannel);showImgPara(signalChannel);output.open(fileName);for (int i = 0; i < signalChannel.rows; i++){for (int j = 0; j < signalChannel.cols; j++){for (int k = 0; k < signalChannel.step[1]; k++){pTemp = signalChannel.data + signalChannel.step[0] * i + signalChannel.step[1] * j + k;output << setw(2) << setfill('0') << (int)*pTemp << " ";}}output << endl;}output.close();imwrite(picName + "_signalChannel.jpg", signalChannel);return true;
}

照一张简单的对勾图片

int main()
{Mat pic1 = imread("check-circle.png");pic2digit(pic1, "check", "check-circle.txt");waitKey(0);return 0;
}

pic2digit
生成的文件,跟原图比轮廓一致

全部代码:

#include <opencv2/opencv.hpp>
#include <iostream>
#include <string>
#include <fstream>using namespace std;
using namespace cv;static void showImgPara(Mat &img);
static bool pictureTest(Mat &img, string name);
static bool matTest(Mat &img);
static bool picCreat(Mat &img, string name);
static bool pic2digit(Mat &img, string picName, string fileName);
static bool tran2singleChannel(Mat &src, Mat &tar);int main()
{Mat pic = imread("girl.jpg");pictureTest(pic, "girl");Mat m1 = Mat::zeros(200, 400, CV_8UC3);picCreat(m1, "White");Mat m2(3, 4, CV_8UC2, Scalar_<uchar>(1, 2));matTest(m2);Mat pic1 = imread("check-circle.png");pic2digit(pic1, "check", "check-circle.txt");waitKey(0);return 0;
}static void showImgPara(Mat &img)
{cout << "sizeof(img) is: " << sizeof(img) << ", img size is: " << img.size << endl;cout << "rows x cols: (" << img.rows << " x " << img.cols << ")" << endl;cout << "dims: " << img.dims << endl;cout << "channels: " << img.channels() << endl;cout << "type: " << img.type() << endl;cout << "depth:" << img.depth() << endl;cout << "elemSize:" << img.elemSize() << " (Bytes per element)" << endl;cout << "elemSize1:" << img.elemSize1() << "(Bytes per channel)" << endl;cout << "step[0]: " << img.step[0] << " (Bytes per cows only when 2 dims)" << endl;cout <<   "step[1]: " << img.step[1] << " (Bytes per element only when 2 dims)" << endl;cout << "step1(0): " << img.step1(0) << ", step1(1): " << img.step1(1) << " (step / elemSize1)" << endl;
}static bool pictureTest(Mat &img, string name)
{if (img.empty()){cout << "Load picture fail!" << endl;return false;}cout << endl << "/******************pictureTest******************/" << endl;showImgPara(img);namedWindow(name);imshow(name, img);return true;
}static bool matTest(Mat &img)
{uchar* pTemp = NULL;cout << endl << "/******************matTest******************/" << endl;cout << img << endl;showImgPara(img);for (int i = 0; i < img.rows; i++){for(int j = 0; j < img.cols; j++){cout << "[";for (int k = 0; k < img.step[1]; k++){pTemp = img.data + img.step[0] * i + img.step[1] * j + k;cout << (int)*pTemp << " ";}cout << "] ";}cout << endl;}return true;
}static bool picCreat(Mat &img, string name)
{for (int i = 0; i < img.rows; i++){uchar *p = img.ptr<uchar>(i);for (int j = 0; j < img.cols * img.channels(); j += img.channels()){if (i > img.rows/4 && i < img.rows*3/4 && j > img.cols*img.channels()/4 && j < img.cols*img.channels()*3/4){p[j] = 0;p[j + 1] = 0;p[j + 2] = 0;}else{p[j] = 255;p[j + 1] = 255;p[j + 2] = 255;}}}cout << endl << "/******************picCreat******************/" << endl;showImgPara(img);namedWindow(name);imshow(name, img);return true;
}static bool pic2digit(Mat &img, string picName, string fileName)
{uchar*     pTemp = NULL;Mat           signalChannel;ofstream  output;if (img.empty()){cout << "Load picture fail!" << endl;return false;}cout << endl << "/******************pic2digit******************/" << endl;//showImgPara(img);//namedWindow(picName);//imshow(picName, img);tran2singleChannel(img, signalChannel);//namedWindow(picName + "_signalChannel");//imshow(picName + "_signalChannel", signalChannel);showImgPara(signalChannel);output.open(fileName);for (int i = 0; i < signalChannel.rows; i++){for (int j = 0; j < signalChannel.cols; j++){for (int k = 0; k < signalChannel.step[1]; k++){pTemp = signalChannel.data + signalChannel.step[0] * i + signalChannel.step[1] * j + k;output << setw(2) << setfill('0') << (int)*pTemp << " ";}}output << endl;}output.close();imwrite(picName + "_signalChannel.jpg", signalChannel);return true;
}static bool tran2singleChannel(Mat &src, Mat &tar)
{vector<Mat> channels;split(src, channels);tar = channels.at(0);return true;
}

check-circle.png
girl.jpg

写在后面:

Mat类中有两个数据部分组成:

  • 矩阵头:包括矩阵尺寸、存储方法、存储地址等。
  • 指针:该指针指向存储所有像素值的矩阵。

因为图片的数据量比较大,所以OpenCV中的Mat定义都是只申请了矩阵头和指针,尽量避免对指针指向的内容进行拷贝操作,所以如下操作都是共享的数据:

        Mat A, C; // Identify header onlycout << "sizeof(A): " << sizeof(A) << "; sizeof(C): " << sizeof(C) << endl;cout << "A:n" << A << endl;A = Mat::ones(Size(3, 3), CV_8UC1);C = A;cout << "sizeof(A): " << sizeof(A) << "; sizeof(C): " << sizeof(C) << endl;cout << "A:n" << A << endl;Mat B(A);cout << "B:n" << B << endl;B.at<uchar>(1, 1) = 255;cout << "C:n" << C << endl;Mat D = A.clone();cout << "D:n" << D << endl;D.at<uchar>(0, 0) = 123;cout << "A:n" << A << endl;cout << "D:n" << D << endl;Mat E;A.copyTo(E);cout << "E:n" << E << endl;E.at<uchar>(2, 2) = 100;cout << "A:n" << A << endl;cout << "E:n" << E << endl;

所以,关于Mat类型的变量定义以及赋值分为:

公用一个数据块:

  • =
  • 构造函数

不公用一个数据块:

  • clone
  • copyto

cv mat保存图片_(七)神秘的Mat相关推荐

  1. opencv mat赋值_【3】OpenCV图像处理模块(18)重映射

    本例中使用OpenCV的remap()函数实现简单的图像重映射(remapping). 什么是重映射? 将一幅图像中的像素,改变位置得到一副新的图像,如图像缩放.翻转等. 因为源图像和新图像之间不一定 ...

  2. 图像指针与矩阵格式转换——Mat转uchar*及uchar*转Mat代码实现

    本篇文章实现RGB3通道图像Mat转uchar及uchar转Mat,编程环境:vs2013,opencv2.4.13 ,由于OpenCV读入和显示都是BGR类型,本文显示图像也用的BGR格式,若需换成 ...

  3. mat 释放_c++ - OpenCV无法释放Mat *内存 - 堆栈内存溢出

    绝对可以使用cv :: Mat指针,但是(与所有指针一样)您必须格外小心地删除创建的对象,以再次释放分配的内存. 在您的示例代码中,尚不清楚是否将在任何地方删除其指针在函数中返回的对象. 如果不是这种 ...

  4. OpenCV中图像Mat存储格式和MATLAB中图像Mat存储格式的区别

    首先,看一下图像中的宽高与笛卡尔坐标系之间的关系如下图所示,即x与width(cols)对应,y与height(rows)对应,x是按列来进行变化,y按行变化. OpenCV读入图像以Mat形式存储时 ...

  5. OpenCV图像处理使用笔记(三)——单通道的Mat对象强转为三通道的Mat对象

    前言 在机器学习和深度学习的时候,对图像进行处理时,会遇到明明是给的三通道的图像,但因为是黑白的,程序判断为单通道的图像,这样让人很无语,还有有时候有些库必须要传入三通道的RGB图像,但为了提升处理的 ...

  6. C/C++学习之路_七: 内存管理

    C/C++学习之路_七: 内存管理 目录 作用域 内存布局 内存分区代码分析 1. 作用域 C语言变量的作用域分为: 代码块作用域(代码块是{}之间的一段代码) 函数作用域 文件作用域 1. 局部变量 ...

  7. python导入mat文件_python读取并写入mat文件的方法

    先给大家介绍下python读取并写入mat文件的方法 用matlab生成一个示例mat文件: clear;clc matrix1 = magic(5); matrix2 = magic(6); sav ...

  8. 无水印保存图片_图片怎么去水印

    无水印保存图片https://www.shuiyinyun.com/无水印保存图片_图片怎么去水印:

  9. python mat文件_python变量保存为.mat文件

    本文主要介绍python中的变量(list, dict, ndarray 类型)怎么保存为.mat文件( matlab 使用) 方法:使用scipy库中的scipy.io.savemat(savePa ...

最新文章

  1. ​别再用方括号在Python中获取字典的值,试试这个方法
  2. Back-propagation, an introduction
  3. MyBatisPlus插件扩展_PerformanceInterceptor性能分析插件的使用
  4. hadoop hive 2.1.1 将Hive启动为服务
  5. suse linux如何重置密码忘记,SUSE Linux忘记root密码的处理办法
  6. Leaflet文档阅读笔记-Zoom levels笔记
  7. 3dcaptcha php,php实现的Captcha验证码类实例
  8. jqgrid 批量启动所有行为可编辑状态
  9. 老白聊数据-关于销售预测的那些事
  10. 招聘 | 语言资源高精尖创新中心研发人员招聘启事
  11. C#将窗口最小化到系统托盘,并显示图标和快捷菜单
  12. python xlrd模块 xlwd模块 python操作Excel python读取Excel
  13. TCP编程-端口扫描器
  14. datagrid的deleteRow使用
  15. 百度百科爬虫爬人物信息
  16. react项目中引入的组件在src外从而报错
  17. 免费SSL证书和付费SSL证书的区别在哪儿?
  18. 微信公众号配置token
  19. 男人应该做的50件事(17-33)
  20. 梯度下降的一阶泰勒公式展开证明

热门文章

  1. 七年程序员生涯,我学到最重要的 6 个教训,别再中招!
  2. 为什么大学普遍都教C/C++、Java,而不教Golang、Python、Rust?
  3. 字节跳动28岁程序员退休:我只想做一个自由的“废物”
  4. nodemanager不能正常关闭_家居燃气安全,燃气阀门的开与关可不能不当回事
  5. left join 索引失效无条件_从零开始学数据分析-mysql索引优化方案
  6. OpenCV中的模板匹配
  7. 明天 | 2021单细胞组学国际研讨会(线上)【附直播链接】
  8. 精选| 2021年2月R新包推荐(第51期)
  9. 明明不胖,为什么你一直都喊着要减肥?
  10. python实现单例模式方法_Python实现单例模式的5种方式