opencv3访问图像像素的五种方法

                                  opencv3访问图像像素的五种方法

在做图像处理中,很多时候并不是针对整个图像进行操作,往往需要遍历图像的所有的点或者部分的点。比如一张1920X1080的图像,它的像素很多,所以在遍历图像像素的算法就需要考虑操作方式的优劣性。所以总结一下,常用的方法。

会有以下五种方式。下面的例子以对图像进行反色操作为例,分析五种方法的时间复杂度。

一:下标M.at<float>(i,j)

Mat inverseColor1(Mat srcImg)

{

    Mat temp = srcImg.clone();

    int row = temp.rows;

    int col = temp.cols;

    for (int i = 0; i < row; i++)

    {

        for (int j = 0; j < col;j++)

        {

            temp.at<cv::Vec3b>(i, j)[0] = 255 - temp.at<cv::Vec3b>(i, j)[0];

            temp.at<cv::Vec3b>(i, j)[1] = 255 - temp.at<cv::Vec3b>(i, j)[1];

            temp.at<cv::Vec3b>(i, j)[2] = 255 - temp.at<cv::Vec3b>(i, j)[2];

        }

 

    }

 

    return temp;

}

 

temp.at<cv::Vec3b>(i, j)[n]表示彩色3通道图像中i行j列第k个通道的颜色像素值,其中<cv::Vec3b>是opencv里面的像素值类型。其函数模板为typedef Vec<uchar,3>Vec3b,表示3通道uchar。

at速度是五种方式里面较慢的,也是最简单的使用方式。

 

 

 

二:指针遍历Mat::ptr<type>

Mat inverseColor2(Mat srcImg)

{

    Mat temp = srcImg.clone();

    int row = temp.rows;

    int col = temp.cols;

    int nStep = temp.cols * temp.channels();

    for (int i = 0; i < row; i++)

    {

        uchar *pSrcData = srcImg.ptr<uchar>(i);

        uchar *pResuiltData = temp.ptr<uchar>(i);

        for (int j = 0; j < nStep;j++)

        {

            pResuiltData[j] = saturate_cast<uchar>(255 - pSrcData[j]);

           

        }

 

    }

 

    return temp;

}

 

Opencv提供了ptr的方法,用于表示遍历图像的每一个字节,默认返回值为uchar* 或者const uchar* 。来自模板temp<typename _Tp>_Tp*Mat::ptr(int i0=0),i0代表是以零行为基准的索引,函数返回指向特定矩阵的uchar* 或指针。

ptr速度优于at和MatIterator_,是最常用的一种方式。相比第一种而言,操作方式复杂,运用到指针操作。相比第三种,操作简单。

 

 

 

 

三:迭代器MatIterator_

Mat inverseColor3(Mat srcImg)

{

    Mat temp = srcImg.clone();

 

    MatConstIterator_<Vec3b> srcIterStart = srcImg.begin<Vec3b>();

    MatConstIterator_<Vec3b> srcIterEnd = srcImg.end<Vec3b>();

 

    MatIterator_<Vec3b> resIterStart = temp.begin<Vec3b>();

    MatIterator_<Vec3b> resIterEnd = temp.end<Vec3b>();

 

    while (srcIterStart != srcIterEnd)

    {

        (*resIterStart)[0] = 255 - (*srcIterStart)[0];

        (*resIterStart)[1] = 255 - (*srcIterStart)[1];

        (*resIterStart)[2] = 255 - (*srcIterStart)[2];

        srcIterStart++;

        resIterStart++;

    }

   

    return temp;

}

 

MatConstIterator_和MatIterator_是两个迭代器,MatConstIterator_<Vec3b> srcIterStart = srcImg.begin<Vec3b>();表示指向迭代器的起始位置。MatConstIterator_<Vec3b> srcIterEnd = srcImg.end<Vec3b>();表示指向迭代器的终止位置,通过起始起始和终止位置之间,移动指针完成对图像的像素遍历。

MatConstIterator_和MatIterator_  

两者关系如下图:存在继承关系。

MatConstiterator                   将迭代器设置为构造函数。

MatConstIterator_和MatIterator_   两者是将迭代器设置为矩阵指定元素的构造函数。

迭代器MatIterator_速度是五种方式里面的最慢的,操作方式比较复杂。不推荐使用。

 

 

 

四:isContinouous

Mat inverseColor4(Mat src)

{

    int row = src.rows;

    int col = src.cols;

    cv::Mat temp = src.clone();

    // 判断是否是连续图像,即是否有像素填充

    if (src.isContinuous() && temp.isContinuous())

    {

        row = 1;

        // 按照行展开

        col = col * src.rows * src.channels();

    }

    // 遍历图像的每个像素

    for (int i = 0; i < row; i++)

    {

        // 设定图像数据源指针及输出图像数据指针

        const uchar* pSrcData = src.ptr<uchar>(i);

        uchar* pResultData = temp.ptr<uchar>(i);

        for (int j = 0; j < col; j++)

        {

            *pResultData++ = 255 - *pSrcData++;

        }

    }

    return temp;

}

图像行与行之间的存储可能是不连续的,上述三种方法,进行像素值遍历,很大程度上造成数据指针移动的浪费。Mat提供了一个检测是否连续的函数isContinuous(),1xN的图像矩阵是连续的。当图像元素连续时,可以看成一行,按行展开,利用指针来获取起始行的位置,进行遍历,节省了寻址的时间。

例:一张M x N的图像按行展开后,成为了1 x( N x M) 列的连续像素点。

isContinuous()的方法优于前面三种方法,操作难度居中,推荐使用(备注:图像必须是连续的才能使用)。

图像不是连续的,可看下面这篇博客,改成连续的图像。

 

参考博客:https://blog.csdn.net/guyuealian/article/details/78614662

 

 

 

五:LTU查表法

Mat inverseColor5(Mat src)

{

    int row = src.rows;

    int col = src.cols;

    Mat temp = src.clone();

    // 建立LUT 反色table

    uchar LutTable[256 * 3];

    for (int i = 0; i < 256; ++i)

    {

        LutTable[i * 3] = 255 - i;

        LutTable[i * 3+1] = 255 - i;

        LutTable[i * 3+2] = 255 - i;

    }

    Mat lookUpTable(1, 256, CV_8UC3, LutTable);

    // 应用索引表进行查找

    LUT(src, lookUpTable, temp);

    return temp;

}

 

/*LTU查表反色处理(单通道)*/

Mat inverseColor5_1(Mat src)

{

    int row = src.rows;

    int col = src.cols;

    Mat temp = src.clone();

    // 建立LUT 反色table

    uchar LutTable[256];

    for (int i = 0; i < 256; ++i)

    {

        LutTable[i] = 255 - i;

    }

    Mat lookUpTable(1, 256, CV_8U);

    uchar* pData = lookUpTable.data;

    // 建立映射表

    for (int i = 0; i < 256; ++i)

    {

        pData[i] = LutTable[i];

    }

    // 应用索引表进行查找

    LUT(src, lookUpTable, temp);

    return temp;

}

LUT函数介绍(来自opencv官网文档):数组的查找表转换。

函数LUT以来自查找表的值填充输出数组。每个像素的索引是从输入数组中获取的。也就是说,这个函数处理src的每个元素如下:

dst(I)←lut(src(I) + d)

参数:

src输入阵列的8位元素。256个元素的lut查找表;

在多通道输入阵列的情况下,该表应该有一个单独的通道(在本例中为所有通道使用相同的表)或与输入阵列相同的通道数量。与src相同大小和通道数量的dst输出阵列,和lut的深度相同。

 

总之,单通道使用单个映射表;多通道输入时,可以多个通道使用同一个映射表,也可以使用多个映射表(看个人需求),上例三通道图像使用同一个表,输出图像的通道和映射表的深度有关。

LUT方法通过映射关系表大大减少相关操作的时间复杂度,是这五种方法中最快的处理方式,常用于(替换、反转、赋值、阈值、二值化、灰度变换等)图像操作。

 

 

参考博客:https://blog.csdn.net/y363703390/article/details/79431450

 

总结:

本人选取一张1920x1080测试时间结果如下(取10次的平均值):

一:0.965547秒 、

二:0.11931秒 、

三:1.04746秒 、

四:0.0185972秒、

五: 0.0176833秒、

 

很明显后两种速度优于前三种,对要求速度快的朋友,推荐使用后两种。本人曾尝试选取过2560x1600的图片测试,LTU比isContinuous()的方法还是快了许多。

代码文件地址:https://download.csdn.net/download/stack_moon/10549645

 

运行环境为WINDOWS10 VS2015,opencv3.3.0。

如有错误欢迎指出,本人邮箱:972839018@qq.com

 

  • 20
    点赞
  • 94
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值