内容:
1.什么是Mat
2.Mat的属性与操作
3.创建Mat
1.什么是Mat
- 图像文件的内存数据对象
对于人类来说,看到一个图像时,脑子里会想到这个图像内容,比如一张帅哥的照片,在我们看来就是帅哥,对于计算机来说,它只能识别0和1,不管是一张什么图像,在它眼中也只是由一些特定数字组成的数据,所以对它来说,一张图像就是一个二维矩阵。而Mat就是存储这个数据的对象,也就是存储图像文件的内存数据对象,而这个对象最主要的就是一些矩阵。
或者说是将imread()读取到的图像信息存储起来的一个数据结构,就是Mat。
Mat对象可分成两个部分,一个是包含图像长宽、色彩等信息的头部,存储源数据; 一个是含有像素信息的数据部分。想改变图像信息时,就要改变像素信息。
当读了一个图像信息之后,把它赋给另一个Mat对象时,这两个Mat对象都指向同一个Data Block,数据部分并没有改变,只进行了一个地址的指向。当使用克隆或者拷贝的API时,会创建一个新的Mat对象,跟原来的Mat对象是两个类层的Mat对象。
输出Mat对象所包含的一些信息,比如宽、高、通道数目、通道深度和字节类型:
#include <opencv2/opencv.hpp>
#include <iostream>
using namespace cv;
using namespace std;
int main(int argc,char** argv)
{
// 读入图像
Mat src = imread("../test.jpg",IMREAD_COLOR);
// 读入灰度图像
//Mat src = imread("../test.jpg",IMREAD_GRAYSCALE);
if(src.empty())
{
printf("不能找到文件。\n");
return -1;
}
namedWindow("input",WINDOW_AUTOSIZE);
imshow("input", src);
int width = src.cols;
int height = src.rows;
int dim = src.channels();
// 通道深度
int d = src.depth();
// 字节类型
int t = src.type();
// 打印宽、高、通道数
// 8位+无符号Char+3个通道
if(t == CV_8UC3)
// 枚举类型是16
printf("type:%d\n",t);
// 只有当深度类型变为浮点数时,深度值才会改变
printf("d:%d\n",d);
printf("width:%d height:%d dim:%d\n",width,height,dim);
waitKey(0);
destroyAllWindows();
return 0;
}
输出结果:
type:16
d:0
width:474 height:237 dim:3
关于通道深度和字节类型可参考下面图片中的内容:
2.Mat对象创建与使用
- 创建空白Mat对象
1.第一种方法Mat( , , )
#include <opencv2/opencv.hpp>
#include <iostream>
using namespace cv;
using namespace std;
int main(int argc,char** argv)
{
// 创建了一个256×256大小的灰度(单通道)图像
Mat t1 = Mat(256,256,CV_8UC1);
// 0代表黑色
t1 = Scalar(0);
imshow("t1", t1);
// 创建了一个256×256大小的三通道图像
Mat t2 = Mat(256,256,CV_8SC3);
// 创建了红色图像
t2 = Scalar(0,0,255);
imshow("t2", t2);
waitKey(0);
destroyAllWindows();
return 0;
}
运行结果:
2.第二种方法Mat(Size(), )
#include <opencv2/opencv.hpp>
#include <iostream>
using namespace cv;
using namespace std;
int main(int argc,char** argv)
{
// 创建了一个512×512大小的三通道图像
Mat t1 = Mat(Size(512, 512), CV_8UC3);
t1 = Scalar(255, 255, 0);
imshow("t1", t1);
}
输出结果:
3.第三种方法Mat::zeros(,):
#include <opencv2/opencv.hpp>
#include <iostream>
using namespace cv;
using namespace std;
int main(int argc,char** argv)
{
// 创建了一个初始值全为0的256×256大小的三通道图像
Mat t2 = Mat::zeros(Size(256,256),CV_8SC3);
imshow("t2", t2);
waitKey(0);
destroyAllWindows();
return 0;
}
输出结果:
- 从现有图像创建
1.赋值和克隆
从现有图像创建Mat对象,然后顺便验证一下最开始说的赋值和克隆的区别。
将t1的值赋值给t3后不改变t3的值,t4是t1克隆的值:
#include <opencv2/opencv.hpp>
#include <iostream>
using namespace cv;
using namespace std;
int main(int argc,char** argv)
{
// 创建了一个256×256大小的三通道图像
Mat t1 = Mat(Size(256, 256), CV_8UC3);
t1 = Scalar(255, 255, 0);
imshow("t1", t1);
// 赋值
Mat t3 = t1;
imshow("t3", t3);
// 改变t3的值
//t3 = Scalar(0,255,0);
// 克隆
Mat t4 = t1.clone();
imshow("t4", t4);
waitKey(0);
destroyAllWindows();
return 0;
}
输出结果:
修改t3的值:
#include <opencv2/opencv.hpp>
#include <iostream>
using namespace cv;
using namespace std;
int main(int argc,char** argv)
{
// 创建了一个256×256大小的三通道图像
Mat t1 = Mat(Size(256, 256), CV_8UC3);
t1 = Scalar(255, 255, 0);
imshow("t1", t1);
// 赋值
Mat t3 = t1;
imshow("t3", t3);
// 改变t3的值
t3 = Scalar(0,255,0);
// 克隆
Mat t4 = t1.clone();
imshow("t4", t4);
waitKey(0);
destroyAllWindows();
return 0;
}
输出改变t3前的t1、t3值,以及改变t3后t4的值:
可以看到由于t3的改变,t1的值也随之变化,t4的值也随t1的值发生改变。这说明t1和被t1赋值后的t3是指向同一个数据的。修改其一,数据就会发生改变。
若只改变克隆t1的t4的值,输出改变后的t1、t3、t4的值:
#include <opencv2/opencv.hpp>
#include <iostream>
using namespace cv;
using namespace std;
int main(int argc,char** argv)
{
// 创建了一个256×256大小的三通道图像
Mat t1 = Mat(Size(256, 256), CV_8UC3);
t1 = Scalar(255, 255, 0);
// 赋值
Mat t3 = t1;
// 克隆
Mat t4 = t1.clone();
// 改变t4的值
t4 = Scalar(0,255,0);
imshow("t1", t1);
imshow("t3", t3);
imshow("t4", t4);
waitKey(0);
destroyAllWindows();
return 0;
}
输出结果:
很明显可以看出克隆t1的t4的值的改变是影响不了t1、t3的值的。
2.拷贝
还有一种就是拷贝:
Mat t5;
t1.copyTo(t5);
意为将t1的值拷贝给t5,t5和t4的性质是相同的,在这就不进行验证了,有兴趣可以自己尝试一下。
3.Mat::zeros(,)
还可以创建和原始图像大小、类型相同,但内容可能不同的图像:
#include <opencv2/opencv.hpp>
#include <iostream>
using namespace cv;
using namespace std;
int main(int argc,char** argv)
{
// 创建了一个256×256大小的三通道图像
Mat t1 = Mat(Size(256, 256), CV_8UC3);
t1 = Scalar(255, 255, 0);
// 创建了一个与t1大小、类型相同的黑色图像;
Mat t6 = Mat::zeros(t1.size(),t1.type());
imshow("t1", t1);
imshow("t6", t6);
waitKey(0);
destroyAllWindows();
return 0;
}
输出结果:
- 遍历与访问像素值
1.基于数组方式遍历
为了更容易看出遍历过一边图像的所有像素值,我会在遍历过程中改变一下遍历过的像素值,更容易看出遍历操作。
#include <opencv2/opencv.hpp>
#include <iostream>
using namespace cv;
using namespace std;
int main(int argc,char** argv)
{
Mat src = imread("../test.jpg");
// 遍历每个像素值
int height = src.rows;
int width = src.cols;
int ch = src.channels();
for(int row =0;row < height; row++)
{
for(int col = 0;col < width; col++)
{
if(ch == 3)
{
Vec3b pixel = src.at<Vec3b>(row,col);
int blue = pixel[0];
int green = pixel[1];
int red = pixel[2];
// 字节类型+3通道+bit 通过Vec3b获得
src.at<Vec3b>(row,col)[0] = 255 - blue;
src.at<Vec3b>(row,col)[1] = 255 - green;
src.at<Vec3b>(row,col)[2] = 255 - red;
}
if(ch == 1)
{
// 单通道类型
int pv = src.at<uchar>(row,col);
src.at<uchar>(row,col) = (255 - pv);
}
}
}
imshow("pixel-demo",src);
waitKey(0);
destroyAllWindows();
return 0;
}
输出结果:
可以看出来,与原图明显有差别,每个像素的值都改变过了。
2.用指针的方式遍历
把每个用指针遍历过的像素的值拷贝给另一个图像,最后可以输出一个和原图完全相同的图像。
#include <opencv2/opencv.hpp>
#include <iostream>
using namespace cv;
using namespace std;
int main(int argc,char** argv)
{
Mat src = imread("../test.jpg");
imshow("原图",src);
// 遍历每个像素值
int height = src.rows;
int width = src.cols;
int ch = src.channels();
// 记录结果,用于后面把原图像中的遍历过的每个像素值赋值给result;
Mat result = Mat::zeros(src.size(),src.type());
// 所有像素的数据都作为一个个uchar类型的数据块放在内存地址当中,要从指向开头的指针开始遍历数据
for(int row =0;row < height; row++)
{
// 获取当前行的指针
uchar* curr_row = src.ptr<uchar>(row);
uchar* result_row = result.ptr<uchar>(row);
for(int col = 0;col < width; col++)
{
if(ch == 3)
{
int blue = *curr_row++;
int green = *curr_row++;
int red = *curr_row++;
*result_row++ = blue;
*result_row++ = green;
*result_row++ = red;
}
if(ch == 1)
{
int pv = *curr_row++;
src.at<uchar>(row,col) = (255 - pv);
*result_row++ = pv;
}
}
}
imshow("pixel-demo",result);
waitKey(0);
destroyAllWindows();
return 0;
}
运行结果:
使用数组方式时要明确图像像素的具体类型是什么,而对于指针方式来说,使用的都是uchar类型,省去了一些麻烦,两种方式对比来说,后者的性能更加高效一些。
版权声明:本文内容由互联网用户自发贡献,该文观点仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 举报,一经查实,本站将立刻删除。
如需转载请保留出处:https://bianchenghao.cn/bian-cheng-ji-chu/105320.html