学习目的:
1.了解如何操作Mat
2.如何遍历Mat对象中包含的每一个元素
3.如何创建一个空图或者Mat
创建Mat对象的方法
1.clone()函数调用
src=img.clone();
2.直接赋值法
src=ming;
3.拷贝API调用
img.copyTo(dst);
创建空白图像的方法
Mat CreateEmptyLikeSrc(Mat src)
{
Mat m1 = Mat::zeros(src.size(), src.type());
Mat m2 = Mat::zeros(Size(512,512),CV_8UC3);
Mat m3 = Mat::ones(Size(512, 512), CV_8UC3);
Mat Kernel = (Mat_<char>(3, 3) << 0, -1, 0, -1, 1, 5, -1, 0, -1, 0);
}
本方法的Demo封装笔记
Mat QuickDemo::create_demo(Mat& img)
{
Mat dst1, dst2;
//img.clone()方法,返回一个mat对象,需要使用一个mat对象来接受数据
dst1 = img.clone();
//copyto方法,传入一个mat对象,直接对mat对象进行指针操作,直接改变结构
img.copyTo(dst2);
imshow("dst1", dst1);
imshow("dst2", dst2);
//创建一个新的画布,Mat::zeros(size,type)方法,
//传入Mat数据对象的尺寸和Mat数据对象的类型,同时设置每一个像素点的灰度值都为0
//这里值得一提的是如果改成ones的单通道的画布的话,
//那么就会产生一个灰度值均为1的画布,但加入是三通道的ones画布的话,
//此时会默认将3通道中的第一个通道设置为1,其他通道保持为0
Mat dst3 = Mat::zeros(img.size(), img.type());//拿到该图像的尺寸和该图像的CV_XXX类型
cout << dst3.cols<<" " << dst3.rows << endl;//拿到该图像的宽度(cols)和该图像的高度
cout << dst3.channels();
//接下来学习Scalar的用法,Scalar返回一个Mat数据元素对象,需要用一个Mat对象来接受数据
//用法是根据通道数来对画布内的每一个像素点进行赋值比如对一个三通道的图像附上(127,127,127)的值
//前提是该画布的大小和数据类型都已经知道
dst3 = Scalar(127,127,127);
return dst3;
//dst12任意返回其一即可
//补充个小知识点,以上只有clone方法是深拷贝方法
}
在这里解释说明以下各参数,首先size这个是固定的,相信都明白,但是后面的type参数需要着重注意一下,如果处理不好这个,会导致各种溢出问题!!!
CV_8UC3:
CV_:前缀
8:8位的(2的8次方)
UC:unsigned char无符号的字符类型
3:代表通道数,生成的图片的类型有关,也就是一个像素点的通道数,由几个通道上的属性类决定该像素点的情况。
那么这个通道数指的是什么呢?
需要明确的是,每个像素点是怎么表示出来的?通道数为3,那么代表着该像素点由3个数据组成,在控制台中打印就会出现一个像素点对应3个数据的情况,也就是说,假如我们往API里面传一些不符合的东西进去,就会导致读取越界,出现个各种各样的错误,这一点我们需要特别留意。
遍历图像
Opencv内的遍历图像的方式分为直接数组遍历和指针遍历方式,写法上都是嵌套循环。
二维数组法
void QuickDemo::pixel_visit_demo_array(Mat& img)
{
// 首先学这个之前先把row和col搞清楚
//cols (行,相当于二维数组的n)
//rows (列,相当于二维数组的m)
int n = img.rows;
int m = img.cols;
int dims = img.channels();//通道数决定了我们的遍历方法,如果不搞这个后面遍历就会错误
Mat dst = img.clone();
//二维数组遍历法,性能分析:较慢 没有理解难度
for (int i = 0; i < n; i++)
{
for (int j = 0; j < m; j++)
{
if (dims == 1)//当通道数为1
{
int pv = dst.at<uchar>(i, j);
//本处语法:首先是调用Mat类型的at方法然后用<>指定二维数组的单像素点的数据类型
//再用()来确定来定位哪一个像素点,完成之后就会得到这个点的灰度值
//判断后直接转成赋值符号左边的数据类型
}
if (dims == 3)//当通道数为3,使用这样的遍历方法
{
Vec3b bgr = dst.at<Vec3b>(i, j);
//本处语法:基本上也是和上面的一样,只不过是类型不同而已,这里是获取到的数据
//转化为Vec3b的类型,直接就包含了三个通道的元素,这样就实现了遍历的目的
//取到数据之后操作方法如下:
dst.at<Vec3b>(i, j)[0] = 255 - bgr[0];
dst.at<Vec3b>(i, j)[1] = 255 - bgr[1];
dst.at<Vec3b>(i, j)[2] = 255 - bgr[2];
}
}
}
//test
imshow("dst", dst);
}
基于指针的方法
void QuickDemo::pixel_visit_demo_pointer(Mat &img)
{
//定义常用参量
int n = img.rows;
int m = img.cols;
int dims = img.channels();
Mat dst = img.clone();//深拷贝,不要影响原图像
//减少判断次数
if (dims == 1)
{
for (int i = 0; i < n; i++)
{
uchar* current_row = dst.ptr<uchar>(i);
//获取到当前行指针,然后直接指针取值
for (int j = 0; j < m; j++)
{
int pv = *current_row;
*current_row++ = 255 - pv;
}
}
}
else if (dims == 3)
{
for (int i = 0; i < n; i++)
{
uchar* current_row = dst.ptr<uchar>(i);
for (int j = 0; j < m; j++)
{
*current_row++ = 255 - *current_row;
*current_row++ = 255 - *current_row;
*current_row++ = 255 - *current_row;
}
}
}
//test-api
imshow("dst_pointer", dst);
}