图像的方向
一般教科书上默认图像左上角为(0,0)原点,沿原点向右为x正方向,向下为y正方向。这与在小孔成像模型中,我们面朝小孔看到的图像坐标一致。
即使存在Mat类中,左上方的第一个像素点也是mat[0][0],即行列首个下标均为0
Mat定义
是一个类,由两个数据部分构成,矩阵头(包含矩阵尺寸,存储方法,存储地址等等)和一个指向存储所有像素值的矩阵的指针
成员变量
int cv::Mat::cols; //返回矩阵的列数(宽度)
int cv::Mat::rows // 返回矩阵行数(高度)
cv::Mat::total; //返回图像的像素数
uchar* cv::Mat::data // 指向矩阵的数据单元的指针
int cv::Mat::dims // 返回矩阵维度,该维度≥2
MatSize cv::Mat::size // 返回矩阵大小
//不常用
cv::Mat::step //step代表以字节为单位的图像的有效宽度
cv::Mat::elemSize //elemSize返回像素的大小=颜色大小(字节)*通道数
uchar* cv::Mat::data //存储图像内容的首地址指针,定义为uchar*类型
如果要获取数据,参考
Mat depth;
float* dat=(float*)depth.data
//或者
float* dat=reinterpret_cast<float*>(depth.data);
成员函数
- 获取图像位深度,(即矩阵元素的存储方式,存储每个像素所用的位数):
mat_name.depth()
- 获取矩阵通道的数目:
mat_name.channnels()
- 获取存储的矩阵元素的数据类型(包括位深度,通道数,数据类型):
mat_name.type()
- 矩阵转置
mat_name.t()
- 矩阵提取
Mat Rcw=Tcw.rowRange(0,3).colRange(0,3); //提取变换矩阵Tcw4*4的前三行和前三列组成旋转矩阵
Mat tcw=Tcw.rowRange(0,3).col(3); //提取变换矩阵Tcw的平移向量部分
- 矩阵拷贝
彻底拷贝矩阵头和矩阵内容(深拷贝),而不仅仅是使用智能指针引用。注意采用Mat的拷贝构造函数时浅拷贝
Mat Mat::clone() //拷贝矩阵到新的矩阵中
void Mat::copyTo(Mat ) //拷贝到参数所代表的矩阵中去
创建二维Mat对象
参考浅谈Opencv Mat类(常用构造函数和成员函数整理)
常见的初始化的示例:
Mat Matrix_name(行数,列数,存储元素的数据类型,每个矩阵点的通道数)
Mat m(640,480,CV_8UC1,255); //创建一个mat,每个元素都是255白、
Mat m(640,480,CV_8UC3,cv::Scalar(255,255,255));
Mat m(640,480,CV_8UC1); //创建一个mat
Mat m(cv::Size(640,480),CV_8UC1);
Mat examples=(Mat_<float>(3,3)<<1,0,0,0,1,0,0,0,1); //先行后列输入
存储元素的数据类型
CV_[位数][带符号与否][类型前缀]C[通道数]
带符号与否:S为符号整型,U为无符号整型,F为浮点型
例如CV_8UC3
通道数赋值cv::Scalar()
Scalar是一个short型的向量,能够使用指定的定制化值来初始化矩阵,还可以用来表示颜色。cv::Scalar(0)代表每个像素值只有1个通道,值为0,cv::Scalar(0,0,0,0)代表每个像素值四个通道都初始化为0。
cv::Mat mat;
mat.setTo(cv::Scalar(0)); //将mat的所有像素的所有通道都设为0
cv::Scalar::all(0) //所有通道都设为0
矩阵尺寸cv::Size()
cv::Size(cols,rows); //注意先列后行,先宽后高,与Mat构造函数不同
警告,cv::Mat的size是先列,后行,千万别搞反了。
矩阵运算
矩阵点乘:
cv::Mat C=A*B
注意
参与点乘的两个Mat矩阵的数据类型(type)只能是 CV_32F、 CV_64FC1、 CV_32FC2、 CV_64FC2 这4种类 型中的一种。若选用其他类型,比如CV_8UC1,编译器会报错“OpenCV Error:Assertion failed”
向量点乘
提取子矩阵
参考OpenCV中的提取子矩阵的函数
例如:
Mat exam=(Mat_<double>(4,4)<<
1,2,3,4,
5,6,7,8
9,10,11,12,
13,14,15,16);
cout<<exam.rowRange(0,3).colRange(0,3)<<endl; //提取前三行前三列组成3*3矩阵
矩阵拼接
访问与修改Mat的单个像素元素
1.使用Mat的成员函数ptr<>()
推荐方法,cv::Mat中提供ptr函数访问任意一行像素的首地址,ptr方式访问效率高,自带越界判定。
示例:
int nl = image.rows; //获得图像的行数nl
int nc = image.cols * image.channels();//获得图像每一列的总像素数(包括多通道)
for (int j = 0; j<nl; j++)//对每一行像素遍历
{
uchar* elem_ptr = image.ptr<uchar>(j); //取图像第j行首列元素指针为data
for (int i = 0; i<nc; i++){ //对同行的各个通道的像素元素逐个遍历
elem_ptr[i] = elem_ptr[i] / div*div + div / 2; //修改单个元素的值
}
}
2.使用迭代器遍历图像
安全,效率低,Iterator有两种调用方式:
cv::MatIterator_<cv::Vec3b> it; //方式1
cv::Mat_<cv::Vec3b>::iterator it; //方式2
其示例:
cv::Mat_<cv::Vec3b>::iterator it = image.begin<cv::Vec3b>(); //迭代器开始
cv::Mat_<cv::Vec3b>::iterator itend = image.end<cv::Vec3b>(); //迭代器结束
for (; it != itend; ++it) //迭代循环
{
(*it)[0] = (*it)[0] / div*div + div / 2; //对每个元素进行操作
(*it)[1] = (*it)[1] / div*div + div / 2;
(*it)[2] = (*it)[2] / div*div + div / 2;
}
3.使用Mat的成员函数at<>()
访问速度最慢,
for (int j = 0; j< image.rows; j++){//这里先按行遍历
for (int i = 0; i< image.cols; i++){//再按列遍历
image.at<cv::Vec3b>(j, i)[0] = image.at<cv::Vec3b>(j,i)[0] / div*div + div/2;
image.at<cv::Vec3b>(j, i)[1] = image.at<cv::Vec3b>(j,i)[1] / div*div + div / 2;
image.at<cv::Vec3b>(j, i)[2] = image.at<cv::Vec3b>(j, i)[2] / div*div + div / 2;
}
}
注意at方式访问像素是(y,x)方式,即先写行数,后写列数,跟cv::keypoint刚好相反
为何0代表黑色,255代表白色
因为灰度是由传感器接收到的光强决定的,光强越强,亮度越高,所以传感器由光强转换的电压也更高,所以数值就越大。
Opencv中显示任意像素像素值
- 通过photoshop的吸管工具和信息面板
- 通过下面函数,运行后界面上方会出现一些缩放按钮,然后某个像素放大到极致就会出现像素值
Mat src=imread("0.png",-1);
namedWindow("nihao",CV_WINDOW_AUTOSIZE);
imshow("nihao",src)
很奇怪,方法2在我电脑上没有用,不会显示那些缩放按钮。怪事