【C++的OpenCV】第十四课-OpenCV基础强化(二):访问单通道Mat中的值之at()、ptr()、iscontinuous()

🎉🎉🎉 欢 迎 各 位 来 到 小 白 p i a o 的 学 习 空 间 ! \color{red}{欢迎各位来到小白piao的学习空间!} 欢迎各位来到小白piao的学习空间!🎉🎉🎉

💖💖💖 持 续 更 新 , 期 待 关 注 ! \color{blue}{持续更新,期待关注!} 持续更新,期待关注!💖💖💖


我 的 主 页 : \color{purple}{我的主页:} 我的主页:我的主页

  1. Python基础、中级、高级;
  2. C++数据结构和算法;
  3. Python数据结构和算法;
  4. OpenCV相关内容等重点内容

我 的 资 源 : \color{purple}{我的资源:} 我的资源:我的资源

  1. IT技术各档次模板
  2. 各类项目(企业、毕设)
  3. 数据库安装包(Mysql8.0)
  4. 技能资料(电子书、软考等)

目录

  • [前 言 \color{purple}{前言} 前言](#前 言 \color{purple}{前言} 前言)
  • [一 、 访 问 的 方 法 \color{blue}{一、访问的方法} 一、访问的方法](#一 、 访 问 的 方 法 \color{blue}{一、访问的方法} 一、访问的方法)
    • [1.1 利 用 成 员 函 数 a t ( ) \color{green}{1.1 利用成员函数at()} 1.1利用成员函数at()](#1.1 利 用 成 员 函 数 a t ( ) \color{green}{1.1 利用成员函数at()} 1.1利用成员函数at())
    • [1.2 利 用 成 员 函 数 p t r ( ) \color{green}{1.2利用成员函数ptr()} 1.2利用成员函数ptr()](#1.2 利 用 成 员 函 数 p t r ( ) \color{green}{1.2利用成员函数ptr()} 1.2利用成员函数ptr())
    • [1.3 利 用 成 员 函 数 i s C o n t i n u o u s 和 p t r \color{green}{1.3利用成员函数isContinuous和ptr} 1.3利用成员函数isContinuous和ptr](#1.3 利 用 成 员 函 数 i s C o n t i n u o u s 和 p t r \color{green}{1.3利用成员函数isContinuous和ptr} 1.3利用成员函数isContinuous和ptr)
      • [1.3.1 isContinuous()介绍](#1.3.1 isContinuous()介绍)
      • [1.3.2 我们来看看其和ptr()组合如何完成实际需求(访问Mat中的值)](#1.3.2 我们来看看其和ptr()组合如何完成实际需求(访问Mat中的值))
  • [四 、 总 结 \color{red}{四、总结} 四、总结](#四 、 总 结 \color{red}{四、总结} 四、总结)

----------------------------------以下为正式内容----------------------------------------

前 言 \color{purple}{前言} 前言

书接上回,我们继续来讨论有关Mat的基本知识,这次我们继续深化了解Mat对象,以便后期更好的进行开发工作。这一章节主要是一些使用层面的知识,相比上章节,我相信读者更愿意看本章内容,因为可以快速得到自己想要的结果,但是还是那句话,最好掌握原理之后,再去使用更为科学


前文链接:【C++的OpenCV】第十三课-OpenCV基础强化(一):绝对有用!Mat相关的一系列知识(基础->进阶)


一 、 访 问 的 方 法 \color{blue}{一、访问的方法} 一、访问的方法

1.1 利 用 成 员 函 数 a t ( ) \color{green}{1.1 利用成员函数at()} 1.1利用成员函数at()

先来通过帮助文档了解一下at函数的基本信息:点击这里了解详情,总共有12种形式,这里为大家略微截图,大家可以自行阅读,我摘抄其中重点,其余感兴趣的可以做进一步了解:

1.1.1 at函数的功能:

这么多英文懵了吧?懵了就对了,下边我给大家讲几句!!!😃

大概意思就是说,这个方法会返回一个你所指定的数组元素的引用 ,而关于下标索引范围的检查动作仅仅在测试模式下生效(生产模式下不生效的目的是为了提高程序的执行效率)。注意:这里得到是某个元素的引用哦 !!!

再直白一点,就是说它可以得到矩阵的某行某列的元素。

1.1.2 多种at()函数原型的介绍及案例

a) 类型一:_Tp& cv::Mat::at ( int i = 0)
cpp 复制代码
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<float>(k+4) and B.at<int>(2i+1) instead of A.at<float>(0,k+4) and B.at<int>(2 i+1,0), respectively.

大概意思:这个参数i,可以指代任意一个单行或者单列的二维矩阵,例如:一个1行N列的浮点矩阵A,和一个M行1列的整型矩阵B,分别找到其中某个元素的方法就可以简化为:A.at<float>(k+4) 和 B.at<int>(2*i+1)。

注意:这里函数原型中的返回值部分是_TP&哦,大家知道这是什么意思吧?(&是引用)

这里来给大家一个使用上的要求:

c++ 复制代码
    If matrix is of type CV_8U then use Mat.at<uchar>(y,x).
    如果矩阵是CV_8U的类型,那么使用模版时类型也要是对应的数据类型uchar,即:Mat.at<uchar>(y,x)
    这里和大家解释一个细节:8U的意思就是8bit的无符号整数。
    If matrix is of type CV_8S then use Mat.at<schar>(y,x).
    同上的翻译,一个意思,即类型是CV_8S,则使用类型为schar,即:Mat.at<schar>(y,x),下边不再赘述。
    If matrix is of type CV_16U then use Mat.at<ushort>(y,x).
    If matrix is of type CV_16S then use Mat.at<short>(y,x).
    If matrix is of type CV_32S then use Mat.at<int>(y,x).
    If matrix is of type CV_32F then use Mat.at<float>(y,x).
    If matrix is of type CV_64F then use Mat.at<double>(y,x).
b) 类型二:_Tp& cv::Mat::at( int row,int col )
cpp 复制代码
template<typename _Tp >
_Tp& cv::Mat::at( int row,int col ) 		

那这种就是最常见的那种咯,输入行列找到对应的值咯。给大家个例子:

cpp 复制代码
#include <iostream>
#include <opencv2/core.hpp>
#include <opencv2/highgui.hpp>

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<int>(1,1)中的两个1,其实就得知道at的参数含义了,
	//注意,at()方法中的row和col都是从0开始的,0即代表第一行或者第一列,明白了吧?
	//注意:at()中是先行后列!
    return 0;
}
c)类型三 :_Tp& cv::Mat::at ( Point pt)
cpp 复制代码
template<typename _Tp >
_Tp& cv::Mat::at ( Point  	pt) 	

其实这种也很常见以及其返回const类型的方法都是一个原理,这里需要传入一个点对象作为参数,说白了,就是按照点的位置在对应矩阵这种查找对应的元素即可了。举个例:

cpp 复制代码
#include<iostream>
#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的基础教程,大家都可以持续关注起来哦!

1.2 利 用 成 员 函 数 p t r ( ) \color{green}{1.2利用成员函数ptr()} 1.2利用成员函数ptr()

先来通过帮助文档了解一下at函数的基本信息:点击这里了解详情,这个方法更多,有20种,是不是看文档很累?看我的吧,帮你总结好了学习的方法,轻松上手,熟练掌握!

1.2.1 ptr()函数的功能

首先呢,它会返回一个指向矩阵中某元素(或者行)的指针 !注意哦,返回的是指针!并且哦,这个指针是uchar类型的哦!

1.2.2 多种ptr()函数原型介绍及案例

a) 类型一:uchar* cv::Mat::ptr ( int i0 = 0 )
cpp 复制代码
 // 这个是模版方法:
 template<typename _Tp >
_Tp* cv::Mat::ptr 	( 	int  	i0 = 0	) 	// i0 :基于0的行索引,说白了,从零开始!

//个人而言,更乐意直接使用模版方法,因为它可以返回模版类型的指针(行或者元素),而不是单纯的uchar*!!


//以下这个是基础款:
uchar* cv::Mat::ptr (int  i0 = 0) 	

注意,这个方法!返回的就是某一行的行指针!

举个浅显易懂的例子吧:

cpp 复制代码
#include<iostream>
#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;
}

结果如下:

b) 类型二:uchar* cv::Mat::ptr ( int row,int col )
cpp 复制代码
// 这个是模板:
 template<typename _Tp >
_Tp* cv::Mat::ptr 	( 	int  	row,
		int  	col 
	) 		

//以下这个是基础款:
uchar* cv::Mat::ptr ( int  row,int  col ) 		

这种方式不就更直接了吗?直接通过行列访问到对应元素上(注意此时这个返回值就是直接返回元素的指针了!),我们试试看:

cpp 复制代码
#include<iostream>
#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<int>(1,1)得到的是索引为(1,1)的元素的指针!
		//而在其之前加上*号就是获取该指针地址对应的元素(这个过程通常被称为"解引用"操作)
	return 0;
}

看着方法众多,很多方法几乎用不到,但是得有这个功能,新手先掌握这些,剩下的内容等用到了再查就行!掌握了规律,剩下的无师自通了哈!

1.3 利 用 成 员 函 数 i s C o n t i n u o u s 和 p t r \color{green}{1.3利用成员函数isContinuous和ptr} 1.3利用成员函数isContinuous和ptr

1.3.1 isContinuous()介绍

原文链接得有:点击这里了解详情,毕竟是教大家学东西,同时培养自学能力,这么良心的作者已经很少了!

函数功能:

**如果矩阵元素连续存储,且每行末尾没有间隙,则该方法返回true。否则,它将返回false。**显然,1x1或1xN矩阵总是连续的。使用Mat::create创建的矩阵总是连续的 。但是,如果 使用Mat::col、Mat::diag等提取矩阵的一部分,或者为外部分配的数据构造矩阵头,则此类矩阵可能不再具有此属性。 想想看,图像边界连续性检测不就是这个原理吗?

💖这里,为了给大家更好的理解,我们加一节小灶:Mat对象至少一行中的所有元素在内存汇总的排布是连续的!而行与行之间不一定连续(除非你用create创建Mat),如果不连续,其行与行的间隔也是一样的!!

函数的原型:

cpp 复制代码
bool cv::Mat::isContinuous ( ) const

// 返回值是一个布尔值,如果是连续的就是true,否则就是false呗!

1.3.2 我们来看看其和ptr()组合如何完成实际需求(访问Mat中的值)

这里就不废话了,都是讲过的内容,直接上代码:

cpp 复制代码
#include<iostream>
#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结构去打印一下"不连续"!证明,这样子创建的矩阵,每行之间是有固定间隙的哦(仔细看下下边的内容)!

那我们来换一种方式:

cpp 复制代码
#include<iostream>
#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;
}

这下结果明朗了,顺道学习了新技能!

四 、 总 结 \color{red}{四、总结} 四、总结

内容虽然不多,分成两章更新,下章还在持续更新!但都是精华如果你觉得内容难懂,不妨从最基础的内容学起来,请看我的首页哦!我的主页

顺道呢,发现一个不错的资源:点我进入

其中包含了opencv入门的保姆级教程(环境的安装--->实际的项目【人脸识别&教程】,以及配套了一本中文版OpenCV书籍)


💖💖💖 持 续 更 新 , 期 待 关 注 ! \color{blue}{持续更新,期待关注!} 持续更新,期待关注!💖💖💖

相关推荐
MavenTalk3 分钟前
Move开发语言在区块链的开发与应用
开发语言·python·rust·区块链·solidity·move
XiaoLeisj35 分钟前
【JavaEE初阶 — 多线程】生产消费模型 & 阻塞队列
java·开发语言·java-ee
2401_8401922738 分钟前
python基础大杂烩
linux·开发语言·python
@东辰43 分钟前
【golang-技巧】- 定时任务 - cron
开发语言·golang·cron
机器人天才一号1 小时前
C#从入门到放弃
开发语言·c#
Mr_Xuhhh2 小时前
递归搜索与回溯算法
c语言·开发语言·c++·算法·github
文军的烹饪实验室2 小时前
ValueError: Circular reference detected
开发语言·前端·javascript
无敌岩雀2 小时前
C++设计模式行为模式———命令模式
c++·设计模式·命令模式
B20080116刘实2 小时前
CTF攻防世界小白刷题自学笔记13
开发语言·笔记·web安全·网络安全·php
Qter_Sean3 小时前
自己动手写Qt Creator插件
开发语言·qt