欢迎各位来到小白 p i a o 的学习空间! \color{red}{欢迎各位来到小白piao的学习空间!} 欢迎各位来到小白piao的学习空间!
持续更新,期待关注! \color{blue}{持续更新,期待关注!} 持续更新,期待关注!
我的主页: \color{purple}{我的主页:} 我的主页:我的主页
我的资源: \color{purple}{我的资源:} 我的资源:我的资源
书接上回,我们继续来讨论有关Mat的基本知识,这次我们继续深化了解Mat对象,以便后期更好的进行开发工作。这一章节主要是一些使用层面的知识,相比上章节,我相信读者更愿意看本章内容,因为可以快速得到自己想要的结果,但是还是那句话,最好掌握原理之后,再去使用更为科学。
前文链接:【C++的OpenCV】第十三课-OpenCV基础强化(一):绝对有用!Mat相关的一系列知识(基础->进阶)
先来通过帮助文档了解一下at函数的基本信息:点击这里了解详情,总共有12种形式,这里为大家略微截图,大家可以自行阅读,我摘抄其中重点,其余感兴趣的可以做进一步了解:
这么多英文懵了吧?懵了就对了,下边我给大家讲几句!!!
大概意思就是说,这个方法会返回一个你所指定的数组元素的引用,而关于下标索引范围的检查动作仅仅在测试模式下生效(生产模式下不生效的目的是为了提高程序的执行效率)。注意:这里得到是某个元素的引用哦!!!
再直白一点,就是说它可以得到矩阵的某行某列的元素。
template<typename _Tp >
_Tp& cv::Mat::at ( int i = 0)
原文:That is, if, for example, A is a 1 x N floating-point matrix and B is an M x 1 integer matrix, you can simply write A.at
大概意思:这个参数i,可以指代任意一个单行或者单列的二维矩阵,例如:一个1行N列的浮点矩阵A,和一个M行1列的整型矩阵B,分别找到其中某个元素的方法就可以简化为:A.at
注意:这里函数原型中的返回值部分是_TP&哦,大家知道这是什么意思吧?(&是引用)
这里来给大家一个使用上的要求:
If matrix is of type CV_8U then use Mat.at(y,x).
如果矩阵是CV_8U的类型,那么使用模版时类型也要是对应的数据类型uchar,即:Mat.at(y,x)
这里和大家解释一个细节:8U的意思就是8bit的无符号整数。
If matrix is of type CV_8S then use Mat.at(y,x).
同上的翻译,一个意思,即类型是CV_8S,则使用类型为schar,即:Mat.at(y,x),下边不再赘述。
If matrix is of type CV_16U then use Mat.at(y,x).
If matrix is of type CV_16S then use Mat.at(y,x).
If matrix is of type CV_32S then use Mat.at(y,x).
If matrix is of type CV_32F then use Mat.at(y,x).
If matrix is of type CV_64F then use Mat.at(y,x).
template<typename _Tp >
_Tp& cv::Mat::at( int row,int col )
那这种就是最常见的那种咯,输入行列找到对应的值咯。给大家个例子:
#include
#include
#include
using namespace cv;
int main()
{
Mat m = (Mat_<int>(3,2) << 1,2,3,4,5,6)
cout << m.at<int>(1,1)<< endl; // 这里的结果是:4 ,为什么知道吗?
cout << m <<endl; // 注意:显示的是3行2列
//首先,这里创建的是一个3行2列的矩阵,这个不难懂,4是出现在第二行第二列的元素:
/*
[1,2
3,4
5,6]
*/
// 那这个m.at(1,1)中的两个1,其实就得知道at的参数含义了,
//注意,at()方法中的row和col都是从0开始的,0即代表第一行或者第一列,明白了吧?
//注意:at()中是先行后列!
return 0;
}
template<typename _Tp >
_Tp& cv::Mat::at ( Point pt)
其实这种也很常见以及其返回const类型的方法都是一个原理,这里需要传入一个点对象作为参数,说白了,就是按照点的位置在对应矩阵这种查找对应的元素即可了。举个例:
#include
#include"opencv2/core/utility.hpp"
using namespace std;
using namespace cv;
int main()
{
Mat m = (Mat_<int>(3,2) << 1,2,3,4,5,6);
cout<<m<<endl;
/*
[1, 2;
3, 4;
5, 6]
*/
cout << m.at<int>(Point(1,0)) << endl; // 结果是2哦,看看上一节内容就知道,Point(c,r),是先列后行,且这两个值都是列和行的索引(基于0)
return 0;
}
虽然方法很多,其他的方法都可以触类旁通,只要学好C++的基础语法,这都不是难事哦,后期我也将会为大家写写C++和Python的基础教程,大家都可以持续关注起来哦!
先来通过帮助文档了解一下at函数的基本信息:点击这里了解详情,这个方法更多,有20种,是不是看文档很累?看我的吧,帮你总结好了学习的方法,轻松上手,熟练掌握!
首先呢,它会返回一个指向矩阵中某元素(或者行)的指针!注意哦,返回的是指针!并且哦,这个指针是uchar类型的哦!
// 这个是模版方法:
template<typename _Tp >
_Tp* cv::Mat::ptr ( int i0 = 0 ) // i0 :基于0的行索引,说白了,从零开始!
//个人而言,更乐意直接使用模版方法,因为它可以返回模版类型的指针(行或者元素),而不是单纯的uchar*!!
//以下这个是基础款:
uchar* cv::Mat::ptr (int i0 = 0)
注意,这个方法!返回的就是某一行的行指针!
举个浅显易懂的例子吧:
#include
#include"opencv2/core/utility.hpp"
using namespace std;
using namespace cv;
int main()
{
Mat m = (Mat_<int>(3,2) << 1,2,3,4,5,6);
cout << m <<endl; // 注意:显示的是3行2列
/*
[1, 2;
3, 4;
5, 6]
*/
cout << m.size() << endl; // [2 x 3] 注意:size()的表示方式是2列3行!
for(int r=0; r < m.rows; r++){
//得到每行的行首指针,r就是当前行的索引
const int* rptr = m.ptr<int>(r);
//打印第r行的元素的所有值:
for(int c=0; c<m.cols; c++){ //c是列的索引,也是基于0的索引,几乎所有数字型索引都是从0开始!
cout<<rptr[c]<<",";
}
cout<<endl; //换行
}
return 0;
}
// 这个是模板:
template<typename _Tp >
_Tp* cv::Mat::ptr ( int row,
int col
)
//以下这个是基础款:
uchar* cv::Mat::ptr ( int row,int col )
这种方式不就更直接了吗?直接通过行列访问到对应元素上(注意此时这个返回值就是直接返回元素的指针了!),我们试试看:
#include
#include"opencv2/core/utility.hpp"
using namespace std;
using namespace cv;
int main()
{
Mat m = (Mat_<int>(3,2) << 1,2,3,4,5,6);
cout << m <<endl; // 注意:显示的是3行2列
/*
[1, 2;
3, 4;
5, 6]
*/
cout << m.size() << endl; // [2 x 3] 注意:size()的表示方式是2列3行!
cout << *m.ptr<int>(1,1) << endl; //注意这里一个细节哦,仔细看哦,结果是4
//这里m.ptr(1,1)得到的是索引为(1,1)的元素的指针!
//而在其之前加上*号就是获取该指针地址对应的元素(这个过程通常被称为“解引用”操作)
return 0;
}
看着方法众多,很多方法几乎用不到,但是得有这个功能,新手先掌握这些,剩下的内容等用到了再查就行!掌握了规律,剩下的无师自通了哈!
原文链接得有:点击这里了解详情,毕竟是教大家学东西,同时培养自学能力,这么良心的作者已经很少了!
函数功能:
**如果矩阵元素连续存储,且每行末尾没有间隙,则该方法返回true。否则,它将返回false。**显然,1x1或1xN矩阵总是连续的。使用Mat::create创建的矩阵总是连续的。但是,如果 使用Mat::col、Mat::diag等提取矩阵的一部分,或者为外部分配的数据构造矩阵头,则此类矩阵可能不再具有此属性。 想想看,图像边界连续性检测不就是这个原理吗?
这里,为了给大家更好的理解,我们加一节小灶:Mat对象至少一行中的所有元素在内存汇总的排布是连续的!而行与行之间不一定连续(除非你用create创建Mat),如果不连续,其行与行的间隔也是一样的!!
函数的原型:
bool cv::Mat::isContinuous ( ) const
// 返回值是一个布尔值,如果是连续的就是true,否则就是false呗!
这里就不废话了,都是讲过的内容,直接上代码:
#include
#include"opencv2/core/utility.hpp"
using namespace std;
using namespace cv;
int main()
{
Mat m = (Mat_<int>(3,2) << 1,2,3,4,5,6);
cout << m <<endl; // 注意:显示的是3行2列
/*
[1, 2;
3, 4;
5, 6]
*/
if(m.isContinuous()){
int* ptr = m.ptr<int>(0); // 得到第一行行首指针
for(int n = 0; n < m.rows*m.cols; n++){
//解释一下:n < m.rows*m.cols这个边界条件是一种经验!
//意思:m.rows*m.cols即行数*列数
//其次,在内存层面上,如果矩阵连续(m.isContinuous()的结果为true),
//就是说内存上这些值都是挨着存储的,所以索引也是连续的
//故这是一个以为数组,则最大索引就是m.rows*m.cols-1
//怎么样?有意思吧?认真学习起来。
cout<<ptr[n]<<",";
}
}
return 0;
}
注意仔细看,上述代码如下的结果 \color{red}{注意仔细看,上述代码如下的结果} 注意仔细看,上述代码如下的结果:
说明上述代码不连续哦,当然你也可以尝试一下加一个else结构去打印一下“不连续”!证明,这样子创建的矩阵,每行之间是有固定间隙的哦(仔细看下下边的内容)!
#include
#include"opencv2/core/utility.hpp"
using namespace std;
using namespace cv;
int main()
{
Mat m = (Mat_<int>(3,2) << 1,2,3,4,5,6);
Mat m_c;
m_c.create(m.size(),m.type());//注意上文提到过:create创建的是连续存储的矩阵
cout << m_c <<endl; // 注意:显示的是3行2列
/*
[1, 2;
3, 4;
5, 6]
*/
if(m.isContinuous()){
int* ptr = m_c.ptr<int>(0); // 得到第一行行首指针
for(int n = 0; n < m_c.rows*m_c.cols; n++){
cout<<ptr[n]<<",";
}
cout<<endl;
}
return 0;
}
内容虽然不多,分成两章更新,下章还在持续更新!但都是精华如果你觉得内容难懂,不妨从最基础的内容学起来,请看我的首页哦!我的主页
顺道呢,发现一个不错的资源:点我进入
其中包含了opencv入门的保姆级教程(环境的安装—>实际的项目【人脸识别&教程】,以及配套了一本中文版OpenCV书籍)
持续更新,期待关注! \color{blue}{持续更新,期待关注!} 持续更新,期待关注!