Opencv中文网

遍历单通道灰度图

关于遍历Mat图像的每个像素,最常用的是遍历单通道灰度图和3通道彩色图。首先是单通道灰度图的像素遍历。

单通道灰度图遍历方法

单通道灰度图是一个最简单的二维矩阵,可以通过两层for循环和at<类型>(y,x)进行遍历,还可以使用指针进行遍历,或是用迭代器进行遍历。下面将分别演示这几种情况。

1.for循环加at方法遍历

#include<opencv2/opencv.hpp>
#include <iostream>
using namespace std;
using namespace cv;

int main()
{    
    Mat girl = imread("images/girl.jpg", IMREAD_GRAYSCALE);//单通道灰度图(CV_8UC1)
    if (girl.empty()) {
        return 0;
    }
    imshow("window1", girl);//显示图像
    imwrite("photo1.jpg", girl);//保存图像
    for (int i = 0; i < girl.rows; i++)
    {
        for (int j = 0; j < girl.cols; j++)
        {
            uchar pixel = girl.at<uchar>(i, j);//读取像素点值
            girl.at<uchar>(i, j) = 255 - pixel;//反色后写入像素值
        }
    }
    imshow("window2", girl);//显示图像
    imwrite("photo2.jpg", girl);//保存图像
    waitKey(0);//等待按键  
    return 0;
}

在本例中,通过imread将原本是3通道的彩色图以单通道灰度图进行加载,随后显示该灰度图。

灰度图

灰度图的每个像素点的数据类型为uchar,于是采用两层for循环嵌套,配合at<uchar>(i, j) 遍历每个像素点,计算其反色值后,再写回到每个像素点。最后效果如下图所示。

反色图

at<类型>(y, x)的语法简单,直接通过坐标访问,每次调用会检查边界。特别是在Debug模式下有性能开效,如果需要高性能的连续内存遍历,推荐使用下面的指针遍历。

2.指针遍历

使用指针遍历图像是最快的方式,对于灰度图,通常按如下所示代码进行操作。

//指针遍历
for (int i = 0; i < girl.rows; i++)
{
    uchar* row = girl.ptr<uchar>(i);//获取第i行指针
    for (int  j = 0; j < girl.cols; j++)
    {
        uchar pixel = row[j];//访问像素 
        row[j] = 255 - pixel;//修改像素(反色)
    }
}

Mat类提供了ptr()函数,专用于获取某行的指针。如果想直接获取整个数据矩阵的指针,可直接访问Mat类的data。如果数据内存又是连续的,那么还有更高效的单循环遍历方式。如下面的代码所示。

//单循环指针遍历
if (girl.isContinuous()) {
    uchar* data = girl.data;//获取整个图像的指针
    int pixels = girl.rows * girl.cols;
    for (int  i = 0; i < pixels; i++)
    {
        data[i] = 255 - data[i];//反色
    }
}

3.使用迭代器遍历

遍历访问Mat对象最安全的方式是使用迭代器,完全不需要担心越界的问题。迭代器的访问速度虽然比不上指针,但是比Mat.at<类型>()要快。

//迭代器遍历
MatIterator_<uchar> it = girl.begin<uchar>();
MatIterator_<uchar> end = girl.end<uchar>();
while (it != end) {
    *it = 255 - *it;//反色
    it++;
}

其中,MatIterator_是一个模板类,用于以迭代器(Iterator)的方式安全高效地遍历Mat矩阵中的像素,它类似于C++ STL(标准模板库)中的迭代器,提供了面向对象的像素访问方式。它的效率也是at<>快,但比指针慢。

4.forEach()函数遍历

这是Mat类提供的一个遍历矩阵元素的函数,结合了C++的Lambda表达式和并行化优化潜力,代码简洁,性能高效。特别适合对每个像素独立操作的场景,如阈值或颜色转换。

函数原型

mat.forEach<PixelType>([](PixelType &pixel, const int* position) {
    // pixel: 当前像素的引用(可直接修改)
    // position: 可选参数,像素位置 [y, x]
    //PixelType:与 Mat 数据类型匹配(如 uchar、float、cv::Vec3b 等)。
});

使用示例

//foreach遍历
girl.forEach<uchar>([](uchar& pixel, const int* pos) {
    pixel = 255 - pixel;
    });

通过网盘分享的文件:001-遍历单通道灰度图.zip
链接: https://pan.baidu.com/s/1jviZHwpySzlNMvXo0tF8Sg 提取码: 8m96

——重庆教主 2025年5月8日

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