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日
若文章对您有帮助,可以激励一下我哦,祝您平安幸福!
| 微信 | 支付宝 | 
|---|---|
|  |  |