Opencv中文网

遍历3通道BGR图

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日

copyright @重庆教主 WPF中文网 联系站长:(QQ)23611316 (微信)movieclip (QQ群).NET小白课堂:864486030 | 本文由WPF中文网原创发布,谢绝转载 渝ICP备2023009518号-1