OpenCV2中访问Mat元素

本文介绍了在OpenCV2中访问Mat矩阵元素的方法,重点讲解了使用at和ptr函数进行行优先(先Y轴后X轴)的高效访问。

Mat中不管是以at访问还是ptr访问,都是行优先 ,先Y轴后X轴(即先行后列)

1、使用at访问

/*
*OpenCV2中Mat的at操作访问矩阵元素
*
*/

#include <highgui.h>

using namespace std ;
using namespace cv ;

int main()
{
	Mat image = imread("forest.jpg") ;
	imshow("image" , image) ;

	//三通道图像,at(y , x)索引是先行(y轴) , 后列(x轴)
	//第一种方法
	for(int h = 0 ; h < image.rows ; ++ h)
	{
		for(int w = 0 ; w < image.cols / 2 ; ++ w)
		{
			image.at<Vec3b>(h , w)[0] = 255 ;
			image.at<Vec3b>(h , w)[1] = 0 ;
			image.at<Vec3b>(h , w)[2] = 0 ;
		}
	}
	imshow("color1" , image) ;
	//方法二
	for(int h = 0 ; h < image.rows ; ++ h)
	{
		for(int w = 0 ; w < image.cols / 2 ; ++ w)
		{
			Vec3b &bgr = image.at<Vec3b>(h , w) ;
			bgr.val[0] = 0 ;
			bgr.val[1] = 255 ;
			bgr.val[2] = 0 ;
		}
	}
	imshow("color2" , image) ;

	image = imread("forest.jpg" , 0) ;
	//单通道图像,
<think>我们参考了多个关于OpenCV访问Mat元素的引用,总结出以下几种常用方法: 1. 使用`at`方法:通过模板函数`at<类型>(row, col)`访问指定位置的元素,适用于随机访问2. 使用指针`ptr`:通过行指针`ptr<类型>(row)`获取行首指针,然后通过列索引访问,效率较高。 3. 使用迭代器:通过`begin`和`end`迭代器遍历元素,安全且代码简洁。 4. 使用`Mat_`类:将Mat对象转换为`Mat_`对象后,可以使用`()`操作符直接访问,类似于`at`但更简洁。 5. 使用`data`指针:直接通过Mat的`data`成员指针访问,但需要注意数据布局(连续存储时可用一维索引,否则需按行访问)。 下面我们将详细介绍这些方法,并给出代码示例。 ### 1. 使用`at`方法访问 `at`方法是一个模板方法,需要指定元素类型。对于单通道图像,直接使用`at<基本类型>(row, col)`;对于多通道图像,使用`at<Vec类型>(row, col)`,然后通过`[]`访问通道。 示例代码(三通道和单通道): ```cpp #include <opencv2/opencv.hpp> using namespace cv; int main() { // 三通道图像 Mat img_color = imread("image.jpg", IMREAD_COLOR); // 遍历前一半列 for (int r = 0; r < img_color.rows; r++) { for (int c = 0; c < img_color.cols / 2; c++) { // 方法1:直接访问每个通道 img_color.at<Vec3b>(r, c)[0] = 255; // 蓝色通道 img_color.at<Vec3b>(r, c)[1] = 0; // 绿色通道 img_color.at<Vec3b>(r, c)[2] = 0; // 红色通道 } } // 单通道图像 Mat img_gray = imread("image.jpg", IMREAD_GRAYSCALE); for (int r = 0; r < img_gray.rows; r++) { for (int c = 0; c < img_gray.cols / 2; c++) { img_gray.at<uchar>(r, c) = 128; // 设置灰度值 } } return 0; } ``` ### 2. 使用`ptr`指针访问 通过`ptr`获取行指针,然后按列访问。效率高,适合遍历整行。 示例代码: ```cpp Mat img_color = imread("image.jpg", IMREAD_COLOR); for (int r = 0; r < img_color.rows; r++) { // 获取行指针,类型为Vec3b* Vec3b* row_ptr = img_color.ptr<Vec3b>(r); for (int c = 0; c < img_color.cols; c++) { row_ptr[c][0] = 255; // 蓝色通道 row_ptr[c][1] = 0; row_ptr[c][2] = 0; } } Mat img_gray = imread("image.jpg", IMREAD_GRAYSCALE); for (int r = 0; r < img_gray.rows; r++) { uchar* row_ptr = img_gray.ptr<uchar>(r); for (int c = 0; c < img_gray.cols; c++) { row_ptr[c] = 128; } } ``` ### 3. 使用迭代器访问 迭代器提供了类似STL容器的遍历方式,安全且代码简洁。使用`Mat_<T>`的迭代器。 示例代码: ```cpp // 三通道图像 Mat img_color = imread("image.jpg", IMREAD_COLOR); Mat_<Vec3b>::iterator it = img_color.begin<Vec3b>(); Mat_<Vec3b>::iterator itend = img_color.end<Vec3b>(); for (; it != itend; ++it) { (*it)[0] = 255; // 蓝色通道 (*it)[1] = 0; (*it)[2] = 0; } // 单通道图像 Mat img_gray = imread("image.jpg", IMREAD_GRAYSCALE); Mat_<uchar>::iterator it_gray = img_gray.begin<uchar>(); Mat_<uchar>::iterator itend_gray = img_gray.end<uchar>(); for (; it_gray != itend_gray; ++it_gray) { *it_gray = 128; } ``` ### 4. 使用`Mat_`类简化访问 `Mat_`是Mat的模板子类,可以直接使用`()`操作符访问元素,无需指定类型。 示例代码: ```cpp // 将Mat转换为Mat_<T>,注意:不会复制数据 Mat img_color = imread("image.jpg", IMREAD_COLOR); Mat_<Vec3b> img_color_mat = img_color; for (int r = 0; r < img_color_mat.rows; r++) { for (int c = 0; c < img_color_mat.cols; c++) { img_color_mat(r, c)[0] = 255; img_color_mat(r, c)[1] = 0; img_color_mat(r, c)[2] = 0; } } // 单通道类似 Mat img_gray = imread("image.jpg", IMREAD_GRAYSCALE); Mat_<uchar> img_gray_mat = img_gray; for (int r = 0; r < img_gray_mat.rows; r++) { for (int c = 0; c < img_gray_mat.cols; c++) { img_gray_mat(r, c) = 128; } } ``` ### 5. 使用`data`指针直接访问 通过Mat的`data`指针直接访问内存。注意:需要确保内存连续(`isContinuous()`为true),否则需分行访问。 示例代码(假设连续): ```cpp Mat img_gray = imread("image.jpg", IMREAD_GRAYSCALE); if (img_gray.isContinuous()) { uchar* data = img_gray.data; int total = img_gray.rows * img_gray.cols; for (int i = 0; i < total; i++) { data[i] = 128; } } ``` ### 性能比较 1. `ptr`指针和`data`指针(连续内存)效率较高,适合大规模遍历。 2. `at`方法在随机访问时方便,但在循环中效率低于指针。 3. 迭代器安全,但效率略低于指针。 4. `Mat_`类访问与`at`类似,但代码更简洁。 根据需求选择合适的方法。例如,在需要高效遍历整幅图像时,推荐使用`ptr`方法;在需要安全遍历时,使用迭代器;在随机访问个别像素时,使用`at`方法[^1][^2][^3][^4][^5]。 §§相关问题§§ 1. 如何判断OpenCVMat矩阵在内存中是否连续? 2. 使用`at`方法访问多通道图像时,如何确定Vec的类型? 3. OpenCV中如何高效遍历非连续存储的Mat图像? 4. 使用迭代器访问Mat元素时,如何处理多通道和单通道的统一代码? </think>### OpenCV访问Mat元素的常用方法 根据引用资料,OpenCV访问Mat元素主要有以下五种高效方式: #### 1. **指针访问(`ptr`方法)** 最推荐的高效方式,通过行指针遍历: ```cpp // 三通道图像 for(int r=0; r<mat.rows; ++r) { Vec3b* row_ptr = mat.ptr<Vec3b>(r); // 获取行指针 for(int c=0; c<mat.cols; ++c) { row_ptr[c][0] = 255; // 蓝色通道 row_ptr[c][1] = 0; // 绿色通道 row_ptr[c][2] = 0; // 红色通道 } } ``` 优势:避免函数调用开销,循环内直接内存操作[^1]。 #### 2. **`at`方法访问** 直接定位元素,适合随机访问: ```cpp // 单通道图像 mat.at<uchar>(r, c) = 128; // 三通道图像 Vec3b &pixel = mat.at<Vec3b>(r, c); pixel[0] = 0; // B通道 pixel[1] = 255;// G通道 ``` 注意:坐标顺序为`(行,列)`[^4]。 #### 3. **迭代器访问** 安全的遍历方式,自动处理边界: ```cpp // 三通道迭代器 Mat_<Vec3b>::iterator it = mat.begin<Vec3b>(); Mat_<Vec3b>::iterator end = mat.end<Vec3b>(); for(; it!=end; ++it) { (*it)[0] = 255; // 修改蓝色通道 } // 单通道迭代器 Mat_<uchar>::iterator it = mat.begin<uchar>(); ``` 优势:代码简洁,避免越界风险[^2]。 #### 4. **`Mat_`模板类访问** 简化操作的封装类: ```cpp Mat_<Vec3b> mat_ref = mat; // 无数据拷贝 for(int r=0; r<mat_ref.rows; ++r) { for(int c=0; c<mat_ref.cols; ++c) { mat_ref(r,c)[1] = 255; // 直接操作绿色通道 } } ``` 特点:操作符`()`比`at`更简洁,保持高效[^3]。 #### 5. **`data`指针访问(底层)** 直接操作内存(需连续存储): ```cpp if(mat.isContinuous()) { uchar* data = mat.data; for(int i=0; i<mat.total(); ++i) { data[i] = 100; // 单通道直接赋值 } } ``` 注意:需检查`isContinuous()`[^2]。 ### 类型对照表 | 访问方式 | 适用场景 | 效率等级 | |--------------|------------------|---------| | `ptr`指针 | 逐行遍历 | ★★★★★ | | `data`指针 | 连续内存块操作 | ★★★★☆ | | `Mat_`类 | 类型安全操作 | ★★★★☆ | | 迭代器 | 安全遍历 | ★★★☆☆ | | `at`方法 | 随机访问 | ★★☆☆☆ | > **最佳实践**:大数据量遍历首选`ptr`指针,简单操作推荐`Mat_`类[^1][^3][^4]。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值