《OpenCV3编程入门》——5.1.5 访问图像中像素的三类方法(指针访问、迭代器iterator、动态地址计算配合at元素)

目录

1、指针访问像素

2、迭代器操作像素

3、动态地址计算


OpenCV中,有三种方式访问图像像素: 

  1. 指针访问:C操作符[];
  2. 迭代器iterator
  3. 动态地址计算

上述方法在访问速度上略有差异。debug模式下,差异非常明显,在release模式下,差异就不太明显。

下边通过一组例子来说明这三种方法的使用,程序的作用是减少颜色的数量,比如原来的图像是256种颜色,我们希望将它变成64中颜色,只需要将原来的颜色除以4(整除)以后再乘以4即可。

主程序代码如下:

//---------------------------------【头文件、命名空间包含部分】--------------------------
//		描述:包含程序所使用的头文件和命名空间
//---------------------------------------------------------------------------------------
#include <opencv2/opencv.hpp>   
#include <iostream>  

using namespace std;
using namespace cv;

//-----------------------------------【全局函数声明部分】-----------------------------------
//          描述:全局函数声明
//------------------------------------------------------------------------------------------
//减少颜色
void colorReduce(Mat& inputImage, Mat& outputImage, int div);


//--------------------------------------【main( )函数】---------------------------------------
//          描述:控制台应用程序的入口函数,我们的程序从这里开始执行
//-----------------------------------------------------------------------------------------------
int main()
{
    //【1】创建原始图并显示
    Mat srcImage = imread("1.jpg");
    imshow("原始图像", srcImage);

    //【2】按原始图的参数规格来创建创建效果图
    Mat dstImage;
    dstImage.create(srcImage.rows, srcImage.cols, srcImage.type());//效果图的大小、类型与原图片相同 

    //【3】记录起始时间
    double time0 = static_cast<double>(getTickCount());

    //【4】调用颜色空间缩减函数
    colorReduce(srcImage, dstImage, 32);

    //【5】计算运行时间并输出
    time0 = ((double)getTickCount() - time0) / getTickFrequency();
    cout << "\t此方法运行时间为: " << time0 << "秒" << endl;  //输出运行时间

    //【6】显示效果图
    imshow("效果图", dstImage);
    waitKey(0);
}

1、指针访问像素

指针访问像素利用的是C语言中的操作符[]。这种方法最快,但是略有点抽象。实验条件下单词运行时间为0.00370487秒,代码如下:

//---------------------------------【colorReduce( )函数】---------------------------------
//          描述:使用【指针访问:C操作符[ ]】方法版的颜色空间缩减函数
//----------------------------------------------------------------------------------------------
void colorReduce(Mat& inputImage, Mat& outputImage, int div)
{
    //参数准备
    outputImage = inputImage.clone();  //拷贝实参到临时变量
    int rowNumber = outputImage.rows;  //行数
    int colNumber = outputImage.cols*outputImage.channels();  //列数 x 通道数=每一行元素的个数

    //双重循环,遍历所有的像素值
    for (int i = 0; i < rowNumber; i++)  //行循环
    {
        uchar* data = outputImage.ptr<uchar>(i);  //获取第i行的首地址
        for (int j = 0; j < colNumber; j++)   //列循环
        {
            // ---------【开始处理每个像素】-------------     
            data[j] = data[j] / div*div;
            // ----------【处理结束】---------------------
        }  //行处理结束
    }
}

分析讲解上述代码:

Mat类属性:

  • 公有变量cols和rows给出图像的宽和高
  • channels():返回图像的通道数。灰度图通道数为1,彩色通道数为3
  • ptr()函数:得到任意行的首地址。ptr是一个模板函数,它返回第i行的首地址:uchar* data = outputImage.ptr<uchar>(i);  //获取第i行的首地址

每行的像素值个数以下语句得到:

int colNumber = outputImage.cols*outputImage.channels();  //列数 x 通道数=每一行元素的个数

运行结果:

                                            原始图

                                           效果图

2、迭代器操作像素

迭代器需要做的仅仅是获得图像矩阵的begin和end,然后增加迭代直至从begin到end。将 * 操作符添加在迭代指针前,即可访问当前指向的内容。

相比使用指针可能越界问题,迭代器绝对是非常安全的方法。

//-------------------------------------【colorReduce( )函数】-----------------------------
//		描述:使用【迭代器】方法版的颜色空间缩减函数
//----------------------------------------------------------------------------------------------
void colorReduce(Mat& inputImage, Mat& outputImage, int div)
{
    //参数准备
    outputImage = inputImage.clone();  //拷贝实参到临时变量
    //获取迭代器
    Mat_<Vec3b>::iterator it = outputImage.begin<Vec3b>();  //初始位置的迭代器
    Mat_<Vec3b>::iterator itend = outputImage.end<Vec3b>();  //终止位置的迭代器

    //存取彩色图像像素
    for (; it != itend; ++it)
    {
        // ------------------------【开始处理每个像素】--------------------
        (*it)[0] = (*it)[0] / div*div;  //蓝色通道
        (*it)[1] = (*it)[1] / div*div;  //绿色通道
        (*it)[2] = (*it)[2] / div*div;  //红色通道
        // ------------------------【处理结束】----------------------------
    }
}

运行结果:  

                                            原始图

                                           效果图

3、动态地址计算

动态地址运算配合at方法。这种方法简单明了,符合我们对像素的认识。

//----------------------------------【colorReduce( )函数】-------------------------------
//          描述:使用【动态地址运算配合at】方法版本的颜色空间缩减函数
//----------------------------------------------------------------------------------------------
void colorReduce(Mat& inputImage, Mat& outputImage, int div)
{
    //参数准备
    outputImage = inputImage.clone();  //拷贝实参到临时变量
    int rowNumber = outputImage.rows;  //行数
    int colNumber = outputImage.cols;  //列数

    //存取彩色图像像素
    for (int i = 0; i < rowNumber; i++)
    {
        for (int j = 0; j < colNumber; j++)
        {
            // ------------------------【开始处理每个像素】--------------------
            outputImage.at<Vec3b>(i, j)[0] = outputImage.at<Vec3b>(i, j)[0] / div*div;  //蓝色通道
            outputImage.at<Vec3b>(i, j)[1] = outputImage.at<Vec3b>(i, j)[1] / div*div;  //绿色通道
            outputImage.at<Vec3b>(i, j)[2] = outputImage.at<Vec3b>(i, j)[2] / div*div;  //红是通道
            // -------------------------【处理结束】----------------------------
        }  // 行处理结束     
    }
}

分析讲解代码:

Mat:

  • 成员函数at(int y, int x):用来存储图像元素,但是必须在编译期知道图像的数据类型。需要注意的是,一定要确保指定的数据类型和矩阵中的数据类型符合,因为at方法本身不会对任何数据类型进行转换。

Vec3b:

  • 一个图像的Mat会返回一个由三个8位数组成的向量,,将其定义为Vec3b,即由三个unsigned char组成的向量,所以存取彩色图像像素的代码可以写出如下形式:
image.at<Vec3b>(i, j)[chanel] = value;  //索引值channel标明了颜色通道号

运行结果:

                                            原始图

                                           效果图

猜你喜欢

转载自blog.csdn.net/Sophia_11/article/details/84593343