cv mat保存图片_(七)神秘的Mat
时间为友,记录点滴。
不知道你有没有跟我一样,看到一些关键的类或结构体总想看看他的结构是什么?因为我觉得单单知其然是不够的,知其所以然才能走得更远。
虽然我们只写了两个例子,但是很明显,这里面的核心内容是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也有好多中方法(我最常用的就是下面这三种):
- Mat M(2, 2, CV_8UC3, Scalar(0, 0, 255));
- Mat Z = Mat::zeros(3, 3, CV_64F);Mat F = Mat::ones(2, 2, CV_64F);Mat F = Mat::ones(2, 2, CV_64F);
- 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;
}
随意创建一张涂色图片
我们除了提前给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;
}
图片导成文件
为了肉眼观测方便,实现了一个多通道转单通道的函数。(暴力得是把第一个通道的数据取出来)
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;
}
全部代码:
#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;
}
写在后面:
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相关推荐
- opencv mat赋值_【3】OpenCV图像处理模块(18)重映射
本例中使用OpenCV的remap()函数实现简单的图像重映射(remapping). 什么是重映射? 将一幅图像中的像素,改变位置得到一副新的图像,如图像缩放.翻转等. 因为源图像和新图像之间不一定 ...
- 图像指针与矩阵格式转换——Mat转uchar*及uchar*转Mat代码实现
本篇文章实现RGB3通道图像Mat转uchar及uchar转Mat,编程环境:vs2013,opencv2.4.13 ,由于OpenCV读入和显示都是BGR类型,本文显示图像也用的BGR格式,若需换成 ...
- mat 释放_c++ - OpenCV无法释放Mat *内存 - 堆栈内存溢出
绝对可以使用cv :: Mat指针,但是(与所有指针一样)您必须格外小心地删除创建的对象,以再次释放分配的内存. 在您的示例代码中,尚不清楚是否将在任何地方删除其指针在函数中返回的对象. 如果不是这种 ...
- OpenCV中图像Mat存储格式和MATLAB中图像Mat存储格式的区别
首先,看一下图像中的宽高与笛卡尔坐标系之间的关系如下图所示,即x与width(cols)对应,y与height(rows)对应,x是按列来进行变化,y按行变化. OpenCV读入图像以Mat形式存储时 ...
- OpenCV图像处理使用笔记(三)——单通道的Mat对象强转为三通道的Mat对象
前言 在机器学习和深度学习的时候,对图像进行处理时,会遇到明明是给的三通道的图像,但因为是黑白的,程序判断为单通道的图像,这样让人很无语,还有有时候有些库必须要传入三通道的RGB图像,但为了提升处理的 ...
- C/C++学习之路_七: 内存管理
C/C++学习之路_七: 内存管理 目录 作用域 内存布局 内存分区代码分析 1. 作用域 C语言变量的作用域分为: 代码块作用域(代码块是{}之间的一段代码) 函数作用域 文件作用域 1. 局部变量 ...
- python导入mat文件_python读取并写入mat文件的方法
先给大家介绍下python读取并写入mat文件的方法 用matlab生成一个示例mat文件: clear;clc matrix1 = magic(5); matrix2 = magic(6); sav ...
- 无水印保存图片_图片怎么去水印
无水印保存图片https://www.shuiyinyun.com/无水印保存图片_图片怎么去水印:
- python mat文件_python变量保存为.mat文件
本文主要介绍python中的变量(list, dict, ndarray 类型)怎么保存为.mat文件( matlab 使用) 方法:使用scipy库中的scipy.io.savemat(savePa ...
最新文章
- ​别再用方括号在Python中获取字典的值,试试这个方法
- Back-propagation, an introduction
- MyBatisPlus插件扩展_PerformanceInterceptor性能分析插件的使用
- hadoop hive 2.1.1 将Hive启动为服务
- suse linux如何重置密码忘记,SUSE Linux忘记root密码的处理办法
- Leaflet文档阅读笔记-Zoom levels笔记
- 3dcaptcha php,php实现的Captcha验证码类实例
- jqgrid 批量启动所有行为可编辑状态
- 老白聊数据-关于销售预测的那些事
- 招聘 | 语言资源高精尖创新中心研发人员招聘启事
- C#将窗口最小化到系统托盘,并显示图标和快捷菜单
- python xlrd模块 xlwd模块 python操作Excel python读取Excel
- TCP编程-端口扫描器
- datagrid的deleteRow使用
- 百度百科爬虫爬人物信息
- react项目中引入的组件在src外从而报错
- 免费SSL证书和付费SSL证书的区别在哪儿?
- 微信公众号配置token
- 男人应该做的50件事(17-33)
- 梯度下降的一阶泰勒公式展开证明
热门文章
- 七年程序员生涯,我学到最重要的 6 个教训,别再中招!
- 为什么大学普遍都教C/C++、Java,而不教Golang、Python、Rust?
- 字节跳动28岁程序员退休:我只想做一个自由的“废物”
- nodemanager不能正常关闭_家居燃气安全,燃气阀门的开与关可不能不当回事
- left join 索引失效无条件_从零开始学数据分析-mysql索引优化方案
- OpenCV中的模板匹配
- 明天 | 2021单细胞组学国际研讨会(线上)【附直播链接】
- 精选| 2021年2月R新包推荐(第51期)
- 明明不胖,为什么你一直都喊着要减肥?
- python实现单例模式方法_Python实现单例模式的5种方式