3通道BGR图的遍历和单通道灰度图类似,不同在于每个像素点实际上有3个byte。其中每个像素由Blue、Green、Red三个通道的值组成。下面是几种遍历方法。
1.for循环加at方法遍历
#include<opencv2/opencv.hpp>
#include <iostream>
using namespace std;
using namespace cv;
int main()
{
Mat girl = imread("images/girl.jpg", IMREAD_COLOR);//3通道(CV_8UC3)
if (girl.empty()) {
return 0;
}
imshow("window1", girl);//显示图像
imwrite("photo1.jpg", girl);//保存图像
//使用at<>方法
for (int i = 0; i < girl.rows; i++)
{
for (int j = 0; j < girl.cols; j++)
{
// 获取引用可以直接修改
Vec3b& pixel = girl.at<Vec3b>(i, j);
//读取像素点值
uchar blue = pixel[0];
uchar green = pixel[1];
uchar red = pixel[2];
//反色后写入像素值
pixel[0] = 255 - blue;
pixel[1] = 255 - green;
pixel[2] = 255 - red;
}
}
imshow("window2", girl);//显示图像
imwrite("photo2.jpg", girl);//保存图像
waitKey(0);//等待按键
return 0;
}
在上面的示例中,Vec3b
是OpenCV中用于表示3通道8位无符号整型像素数据的模板类。Vec:Vector的缩写,表示向量/数组。3:表示有3个元素。b:表示元素类型是uchar(unsigned char,即8位无符号整数,0-255)。OpenCV中类似的向量类型还有:
Vec2b
:2个uchar元素的向量Vec4b
:4个uchar元素的向量(可用于BGRA图像)Vec3s
:3个short元素的向量Vec3i
:3个int元素的向量Vec3f
:3个float元素的向量Vec3d
:3个double元素的向量
本例中,Vec3b& pixel = girl.at<Vec3b>(i, j);表示获取当前行当前列的像素数据的地址引用。然后就可以像数组操作一样,读取该像素点的值。
整个代码的执行流程是,首先通过imread()函数以BGR三通道彩色图的模式加载一张图并显示。

然后通过两层for循环,结合at<Vec3b>()函数,遍历每个像素点,并对其进行反色操作。

2.指针遍历
使用指针遍历图像,永远是最高效的,下面是指针访问3通道图的代码示例。
//指针遍历
for (int i = 0; i < girl.rows; i++)
{
Vec3b* row = girl.ptr<Vec3b>(i);//获取第i行指针
for (int j = 0; j < girl.cols; j++)
{
//读取像素点值
uchar blue = row[j][0];
uchar green = row[j][1];
uchar red = row[j][2];
//反色后写入像素值
row[j][0] = 255 - blue;
row[j][1] = 255 - green;
row[j][2] = 255 - red;
}
}
上面的代码使用两层for循环语句,girl.ptr<Vec3b>(i)表示获取图像某行的指针。也可以只使用一层for循环的指针遍历。
//单循环指针遍历
if (girl.isContinuous()) {
// 获取指向图像数据的指针
uchar* data = girl.data;
// 计算总元素数(宽×高×通道数)
int pixels = girl.rows * girl.cols * girl.channels();
for (int i = 0; i < pixels; i+=3)
{
uchar blue = data[i]; // B通道
uchar green = data[i + 1]; // G通道
uchar red = data[i + 2]; // R通道
// 修改像素值(例如反转颜色)
data[i] = 255 - blue; // 修改B通道
data[i + 1] = 255 - green; // 修改G通道
data[i + 2] = 255 - red; // 修改R通道
}
}
在单循环指针遍历中,data表示整张图的指针,channels()表示获取图像的通道数,rows表示图像高,cols表示图像宽,pixels表示图像所有像素总和。
在for语句中,迭代变量i的步长为3,因为是3通道,即一个像素点有3个像素值。同时,变量i将做为访问每个像素的指针下标。
3.使用迭代器遍历
//迭代器遍历
Mat_<Vec3b> img = girl; // 转换为Mat_类型方便使用迭代器
for (auto it = img.begin(); it != img.end(); ++it) {
// 通过迭代器直接修改
(*it)[0] = 255 - (*it)[0]; // 蓝色通道
(*it)[1] = 255 - (*it)[1]; // 绿色通道
(*it)[2] = 255 - (*it)[2]; // 红色通道
// 或者整体赋值
// *it = cv::Vec3b(0, 255, 0); // 设置为绿色
}
Mat_是Mat的模板子类,它是对Mat类的封装,提供了更安全和更方便的类型安全访问方式。Mat_<Vec3b> img = girl;表示从girl图像创建一个Mat_对象。然后使用Mat_类的begin()函数以获取一个迭代器,end()函数表示图像结尾。
如果不采用Mat_,直接使用Mat的begin()和end()也是可以的。
//迭代器遍历
MatIterator_<Vec3b> it = girl.begin<Vec3b>();
MatIterator_<Vec3b> end = girl.end<Vec3b>();
while (it != end) {
// 通过迭代器直接修改
(*it)[0] = 255 - (*it)[0]; // 蓝色通道
(*it)[1] = 255 - (*it)[1]; // 绿色通道
(*it)[2] = 255 - (*it)[2]; // 红色通道
it++;
}
4.forEach()函数遍历
forEach是OpenCV提供的一种高效遍历方法,特别适合多通道图像处理。它利用了C++11的lambda表达式,并且内部可能使用并行化处理来提高性能。
//forEach遍历
girl.forEach<Vec3b>([](Vec3b& pixel, const int* pos) {
// 处理每个像素
// pixel[0]: 蓝色通道(B)
// pixel[1]: 绿色通道(G)
// pixel[2]: 红色通道(R)
// position[0]: 行号(y)
// position[1]: 列号(x)
pixel[0] = 255 - pixel[0];//反色
pixel[1] = 255 - pixel[1];//反色
pixel[2] = 255 - pixel[2];//反色
});
我们还可以利用forEach将3通道彩色图转为3通道的灰度图。
//转为灰度图
girl.forEach<Vec3b>([](Vec3b& pixel, const int* /*position*/) {
uchar gray = 0.299 * pixel[2] + 0.587 * pixel[1] + 0.114 * pixel[0];
pixel[0] = pixel[1] = pixel[2] = gray;
});

通过网盘分享的文件:002-遍历3通道图.zip
链接: https://pan.baidu.com/s/1O7iJ7N-666Re8PFRERRT3Q 提取码: 1xww
——重庆教主 2025年5月9日
若文章对您有帮助,可以激励一下我哦,祝您平安幸福!
微信 | 支付宝 |
---|---|
![]() |
![]() |