opencv遍历像素点最快的方法,14种选1

 

对于uint8类型3通道图像,不论是BGR还是HSV,这种图像在内存的排序方式就是(BGR为蓝绿红):

BGRBGRBGR.....
BGRBGRBGR.....
BGRBGRBGR.....
...............................

对于uint8类型的单通道灰度图,排序方式就是最简单的二维数组(Y代表灰度值):
YYYYYY.............
YYYYYY.............
YYYYYY.............
............................

 

做C语言的朋友们肯定知道,访问最快的方法莫过于指针直接访问,知道了内存布局,就可以很容易的实现指针访问(读和写)像素值了。

同时把一副彩图和灰度图处理为黑色,最快的操作方式如下,

Mat bgrImg = imread("xxx.jpg");//载入彩图
    Mat grayImg;
    cvtColor(bgrImg, grayImg, CV_BGR2GRAY);
    
    int rowCnt = bgrImg.rows;
    int colCnt = bgrImg.cols;
    
    for (int row=0; row < rowCnt; row++)//使用指针遍历
    {
        uchar* bgrData= bgrImg.ptr<uchar>(row);//指向行数组,格式为BGRBGRBGR....
        uchar* grayData= grayImg.ptr<uchar>(row);
        for (int col=0;col < colCnt;col++)
        {
            //-------------开始处理每个像素-------------------
            bgrData[0] = bgrData[1] = bgrData[2] = 0;//分别修改BGR通道
            grayData[0] = 0;
            //处理完成,移动指针
            bgrData += 3;//3通道数据指针每次+3           
            grayData++;//灰度图指针每次+1
        }
    }

上述代码的原理就是直接访问二维数组,我们还知道,二维数组在内存里的布局,其实还是一位数组,所以最极限的方式就是按照一维数组访问图像,不过我测试发现,这种方法仅仅比上面的二维数组方式,稍微快1%,提升不太明显,不过对于实时计算,提升1%也不错了:

int pixCnt = srcImg.rows * srcImg.cols;//行数x列数,得出总像素数
    uchar* bgrData = srcImg.data; 
    uchar* grayData= grayImg.data;
    for (int pix=0; pix < pixCnt; pix++)//使用指针遍历
    {
       bgrData[0] = bgrData[1] = bgrData[2] = 0;//分别修改BGR通道
       grayData[0] = 0;
      //处理完成,移动指针
       bgrData += 3;//3通道数据指针每次+3           
       grayData++;//灰度图指针每次+1

    }

 

如果想随机访问像素点,可以这么写:

Point pnt(35,64);//访问第64行第35列个像素
grayImg.ptr<uchar>(pnt.y)[pnt.x] = 255;
colorImg..ptr<vec3b>(64)[35] = color(255,0,255);
vec3b实际上是vec<uchar>

特别注意:第一个坐标元素是y,因为y代表了行号,x代表列号。
 

 

 

下面示例程序来自于浅墨大神著作的示例程序,书中记录了14种访问像素值的方法,里面赋值像素值用的方法不同,因此不太科学。为了保证测试的公平性,我把像素赋值操作都改成了0,朋友们可以自己测试:

我实测,确实是指针操作是最快的。

 

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



//---------------------------------【宏定义部分】---------------------------------------------
//		描述:包含程序所使用宏定义
//-------------------------------------------------------------------------------------------------
#define NTESTS 14
#define NITERATIONS 20



//----------------------------------------- 【方法一】-------------------------------------------
//		说明:利用.ptr 和 []
//-------------------------------------------------------------------------------------------------
void colorReduce0(Mat &image, int div=64) {

    int nl= image.rows; //行数
    int nc= image.cols * image.channels(); //每行元素的总元素数量

    for (int j=0; j<nl; j++)
    {

        uchar* data= image.ptr<uchar>(j);

        for (int i=0; i<nc; i++)
        {

            //-------------开始处理每个像素-------------------

            data[i]= 0;

            //-------------结束像素处理------------------------

        } //单行处理结束
    }
}

//-----------------------------------【方法二】-------------------------------------------------
//		说明:利用 .ptr 和 * ++
//-------------------------------------------------------------------------------------------------
void colorReduce1(Mat &image, int div=64) {

    int nl= image.rows; //行数
    int nc= image.cols * image.channels(); //每行元素的总元素数量

    for (int j=0; j<nl; j++)
    {

        uchar* data= image.ptr<uchar>(j);

        for (int i=0; i<nc; i++)
        {

            //-------------开始处理每个像素-------------------

            *data++= 0;

            //-------------结束像素处理------------------------

        } //单行处理结束
    }
}

//-----------------------------------------【方法三】-------------------------------------------
//		说明:利用.ptr 和 * ++ 以及模操作
//-------------------------------------------------------------------------------------------------
void colorReduce2(Mat &image, int div=64) {

    int nl= image.rows; //行数
    int nc= image.cols * image.channels(); //每行元素的总元素数量

    for (int j=0; j<nl; j++)
    {

        uchar* data= image.ptr<uchar>(j);

        for (int i=0; i<nc; i++)
        {

            //-------------开始处理每个像素-------------------


            *data++= 0;

            //-------------结束像素处理------------------------

        } //单行处理结束
    }
}

//----------------------------------------【方法四】---------------------------------------------
//		说明:利用.ptr 和 * ++ 以及位操作
//----------------------------------------------------------------------------------------------------
void colorReduce3(Mat &image, int div=64) {

    int nl= image.rows; //行数
    int nc= image.cols * image.channels(); //每行元素的总元素数量
    int n= static_cast<int>(log(static_cast<double>(div))/log(2.0));
    //掩码值
    uchar mask= 0xFF<<n; // e.g. 对于 div=16, mask= 0xF0

    for (int j=0; j<nl; j++) {

        uchar* data= image.ptr<uchar>(j);

        for (int i=0; i<nc; i++) {

            //------------开始处理每个像素-------------------

            *data++= 0;

            //-------------结束像素处理------------------------
        }  //单行处理结束
    }
}


//----------------------------------------【方法五】----------------------------------------------
//		说明:利用指针算术运算
//---------------------------------------------------------------------------------------------------
void colorReduce4(Mat &image, int div=64) {

    int nl= image.rows; //行数
    int nc= image.cols * image.channels(); //每行元素的总元素数量
    int n= static_cast<int>(log(static_cast<double>(div))/log(2.0));
    int step= image.step; //有效宽度
    //掩码值
    uchar mask= 0xFF<<n; // e.g. 对于 div=16, mask= 0xF0

    //获取指向图像缓冲区的指针
    uchar *data= image.data;

    for (int j=0; j<nl; j++)
    {

        for (int i=0; i<nc; i++)
        {

            //-------------开始处理每个像素-------------------

            *(data+i)= 0;

            //-------------结束像素处理------------------------

        } //单行处理结束

        data+= step;  // next line
    }
}

//---------------------------------------【方法六】----------------------------------------------
//		说明:利用 .ptr 和 * ++以及位运算、image.cols * image.channels()
//-------------------------------------------------------------------------------------------------
void colorReduce5(Mat &image, int div=64) {

    int nl= image.rows; //行数
    int n= static_cast<int>(log(static_cast<double>(div))/log(2.0));
    //掩码值
    uchar mask= 0xFF<<n; // e.g. 例如div=16, mask= 0xF0

    for (int j=0; j<nl; j++)
    {

        uchar* data= image.ptr<uchar>(j);

        for (int i=0; i<image.cols * image.channels(); i++)
        {

            //-------------开始处理每个像素-------------------

            *data++= 0;

            //-------------结束像素处理------------------------

        } //单行处理结束
    }
}

// -------------------------------------【方法七】----------------------------------------------
//		说明:利用.ptr 和 * ++ 以及位运算(continuous)
//-------------------------------------------------------------------------------------------------
void colorReduce6(Mat &image, int div=64) {

    int nl= image.rows; //行数
    int nc= image.cols * image.channels(); //每行元素的总元素数量

    if (image.isContinuous())
    {
        //无填充像素
        nc= nc*nl;
        nl= 1;  // 为一维数列
    }

    int n= static_cast<int>(log(static_cast<double>(div))/log(2.0));
    //掩码值
    uchar mask= 0xFF<<n; // e.g. 比如div=16, mask= 0xF0

    for (int j=0; j<nl; j++) {

        uchar* data= image.ptr<uchar>(j);

        for (int i=0; i<nc; i++) {

            //-------------开始处理每个像素-------------------

            *data++= 0;

            //-------------结束像素处理------------------------

        } //单行处理结束
    }
}

//------------------------------------【方法八】------------------------------------------------
//		说明:利用 .ptr 和 * ++ 以及位运算 (continuous+channels)
//-------------------------------------------------------------------------------------------------
void colorReduce7(Mat &image, int div=64) {

    int nl= image.rows; //行数
    int nc= image.cols ; //列数

    if (image.isContinuous())
    {
        //无填充像素
        nc= nc*nl;
        nl= 1;  // 为一维数组
    }

    int n= static_cast<int>(log(static_cast<double>(div))/log(2.0));
    //掩码值
    uchar mask= 0xFF<<n; // e.g. 比如div=16, mask= 0xF0

    for (int j=0; j<nl; j++) {

        uchar* data= image.ptr<uchar>(j);

        for (int i=0; i<nc; i++) {

            //-------------开始处理每个像素-------------------

            *data++= 0;
            *data++= 0;
            *data++= 0;

            //-------------结束像素处理------------------------

        } //单行处理结束
    }
}


// -----------------------------------【方法九】 ------------------------------------------------
//		说明:利用Mat_ iterator
//-------------------------------------------------------------------------------------------------
void colorReduce8(Mat &image, int div=64) {

    //获取迭代器
    Mat_<Vec3b>::iterator it= image.begin<Vec3b>();
    Mat_<Vec3b>::iterator itend= image.end<Vec3b>();

    for ( ; it!= itend; ++it) {

        //-------------开始处理每个像素-------------------

        (*it)[0]= 0;
        (*it)[1]= 0;
        (*it)[2]= 0;

        //-------------结束像素处理------------------------
    }//单行处理结束
}

//-------------------------------------【方法十】-----------------------------------------------
//		说明:利用Mat_ iterator以及位运算
//-------------------------------------------------------------------------------------------------
void colorReduce9(Mat &image, int div=64) {

    // div必须是2的幂
    int n= static_cast<int>(log(static_cast<double>(div))/log(2.0));
    //掩码值
    uchar mask= 0xFF<<n; // e.g. 比如 div=16, mask= 0xF0

    // 获取迭代器
    Mat_<Vec3b>::iterator it= image.begin<Vec3b>();
    Mat_<Vec3b>::iterator itend= image.end<Vec3b>();

    //扫描所有元素
    for ( ; it!= itend; ++it)
    {

        //-------------开始处理每个像素-------------------

        (*it)[0]= 0;
        (*it)[1]= 0;
        (*it)[2]= 0;

        //-------------结束像素处理------------------------
    }//单行处理结束
}

//------------------------------------【方法十一】---------------------------------------------
//		说明:利用Mat Iterator_
//-------------------------------------------------------------------------------------------------
void colorReduce10(Mat &image, int div=64) {

    //获取迭代器
    Mat_<Vec3b> cimage= image;
    Mat_<Vec3b>::iterator it=cimage.begin();
    Mat_<Vec3b>::iterator itend=cimage.end();

    for ( ; it!= itend; it++) {

        //-------------开始处理每个像素-------------------

        (*it)[0]= 0;
        (*it)[1]= 0;
        (*it)[2]= 0;

        //-------------结束像素处理------------------------
    }
}

//--------------------------------------【方法十二】--------------------------------------------
//		说明:利用动态地址计算配合at
//-------------------------------------------------------------------------------------------------
void colorReduce11(Mat &image, int div=64) {

    int nl= image.rows; //行数
    int nc= image.cols; //列数

    for (int j=0; j<nl; j++)
    {
        for (int i=0; i<nc; i++)
        {

            //-------------开始处理每个像素-------------------

            image.at<Vec3b>(j,i)[0]=	0;
            image.at<Vec3b>(j,i)[1]=	 0;
            image.at<Vec3b>(j,i)[2]=	 0;

            //-------------结束像素处理------------------------

        } //单行处理结束
    }
}

//----------------------------------【方法十三】-----------------------------------------------
//		说明:利用图像的输入与输出
//-------------------------------------------------------------------------------------------------
void colorReduce12(const Mat &image, //输入图像
    Mat &result,      // 输出图像
    int div=64) {

        int nl= image.rows; //行数
        int nc= image.cols ; //列数

        //准备好初始化后的Mat给输出图像
        result.create(image.rows,image.cols,image.type());

        //创建无像素填充的图像
        nc= nc*nl;
        nl= 1;  //单维数组

        int n= static_cast<int>(log(static_cast<double>(div))/log(2.0));
        //掩码值
        uchar mask= 0xFF<<n; // e.g.比如div=16, mask= 0xF0

        for (int j=0; j<nl; j++) {

            uchar* data= result.ptr<uchar>(j);
            const uchar* idata= image.ptr<uchar>(j);

            for (int i=0; i<nc; i++) {

                //-------------开始处理每个像素-------------------

                *data++= 0;
                *data++= 0;
                *data++= 0;

                //-------------结束像素处理------------------------

            } //单行处理结束
        }
}

//--------------------------------------【方法十四】-------------------------------------------
//		说明:利用操作符重载
//-------------------------------------------------------------------------------------------------
void colorReduce13(Mat &image, int div=64) {

    int n= static_cast<int>(log(static_cast<double>(div))/log(2.0));
    //掩码值
    uchar mask= 0xFF<<n; // e.g. 比如div=16, mask= 0xF0

    //进行色彩还原
    image=(image&Scalar(mask,mask,mask))+Scalar(0,0,0);
}




//-----------------------------------【ShowHelpText( )函数】-----------------------------
//		描述:输出一些帮助信息
//----------------------------------------------------------------------------------------------
void ShowHelpText()
{
    //输出欢迎信息和OpenCV版本
    printf("\n\n\t\t\t非常感谢购买《OpenCV3编程入门》一书!\n");
    printf("\n\n\t\t\t此为本书OpenCV2版的第24个配套示例程序\n");
    printf("\n\n\t\t\t   当前使用的OpenCV版本为:" CV_VERSION );
    printf("\n\n  ----------------------------------------------------------------------------\n");

    printf("\n\n正在进行存取操作,请稍等……\n\n");
}



#define IMG_PATH "E:/CHENGXU/vision/res/QR_Test4.jpg"
//-----------------------------------【main( )函数】--------------------------------------------
//		描述:控制台应用程序的入口函数,我们的程序从这里开始
//-------------------------------------------------------------------------------------------------
int main( )
{
    int64 t[NTESTS],tinit;
    Mat image0;
    Mat image1;
    Mat image2;

    system("color 4F");

    ShowHelpText();

    image0= imread(IMG_PATH);
    if (!image0.data)
    {
        cout << "读取图像失败,请输入正确的图片地址";
        return 0;
    }


    //时间值设为0
    for (int i=0; i<NTESTS; i++)
        t[i]= 0;


    // 多次重复测试
    int n=NITERATIONS;
    for (int k=0; k<n; k++)
    {
        cout << k << " of " << n << endl;

        image1= imread(IMG_PATH);
        //【方法一】利用.ptr 和 []
        tinit= getTickCount();
        colorReduce0(image1);
        t[0]+= getTickCount()-tinit;

        //【方法二】利用 .ptr 和 * ++
        image1= imread(IMG_PATH);
        tinit= getTickCount();
        colorReduce1(image1);
        t[1]+= getTickCount()-tinit;

        //【方法三】利用.ptr 和 * ++ 以及模操作
        image1= imread(IMG_PATH);
        tinit= getTickCount();
        colorReduce2(image1);
        t[2]+= getTickCount()-tinit;

        //【方法四】 利用.ptr 和 * ++ 以及位操作
        image1= imread(IMG_PATH);
        tinit= getTickCount();
        colorReduce3(image1);
        t[3]+= getTickCount()-tinit;

        //【方法五】 利用指针的算术运算
        image1= imread(IMG_PATH);
        tinit= getTickCount();
        colorReduce4(image1);
        t[4]+= getTickCount()-tinit;

        //【方法六】利用 .ptr 和 * ++以及位运算、image.cols * image.channels()
        image1= imread(IMG_PATH);
        tinit= getTickCount();
        colorReduce5(image1);
        t[5]+= getTickCount()-tinit;

        //【方法七】利用.ptr 和 * ++ 以及位运算(continuous)
        image1= imread(IMG_PATH);
        tinit= getTickCount();
        colorReduce6(image1);
        t[6]+= getTickCount()-tinit;

        //【方法八】利用 .ptr 和 * ++ 以及位运算 (continuous+channels)
        image1= imread(IMG_PATH);
        tinit= getTickCount();
        colorReduce7(image1);
        t[7]+= getTickCount()-tinit;

        //【方法九】 利用Mat_ iterator
        image1= imread(IMG_PATH);
        tinit= getTickCount();
        colorReduce8(image1);
        t[8]+= getTickCount()-tinit;

        //【方法十】 利用Mat_ iterator以及位运算
        image1= imread(IMG_PATH);
        tinit= getTickCount();
        colorReduce9(image1);
        t[9]+= getTickCount()-tinit;

        //【方法十一】利用Mat Iterator_
        image1= imread(IMG_PATH);
        tinit= getTickCount();
        colorReduce10(image1);
        t[10]+= getTickCount()-tinit;

        //【方法十二】 利用动态地址计算配合at
        image1= imread(IMG_PATH);
        tinit= getTickCount();
        colorReduce11(image1);
        t[11]+= getTickCount()-tinit;

        //【方法十三】 利用图像的输入与输出
        image1= imread(IMG_PATH);
        tinit= getTickCount();
        Mat result;
        colorReduce12(image1, result);
        t[12]+= getTickCount()-tinit;
        image2= result;

        //【方法十四】 利用操作符重载
        image1= imread(IMG_PATH);
        tinit= getTickCount();
        colorReduce13(image1);
        t[13]+= getTickCount()-tinit;

        //------------------------------
    }
    //输出图像
    imshow("原始图像",image0);
    imshow("结果",image2);
    imshow("图像结果",image1);

    // 输出平均执行时间
    cout << endl << "-------------------------------------------" << endl << endl;
    cout << "\n【方法一】利用.ptr 和 []的方法所用时间为 " << 1000.*t[0]/getTickFrequency()/n << "ms" << endl;
    cout << "\n【方法二】利用 .ptr 和 * ++ 的方法所用时间为" << 1000.*t[1]/getTickFrequency()/n << "ms" << endl;
    cout << "\n【方法三】利用.ptr 和 * ++ 以及模操作的方法所用时间为" << 1000.*t[2]/getTickFrequency()/n << "ms" << endl;
    cout << "\n【方法四】利用.ptr 和 * ++ 以及位操作的方法所用时间为" << 1000.*t[3]/getTickFrequency()/n << "ms" << endl;
    cout << "\n【方法五】利用指针算术运算的方法所用时间为" << 1000.*t[4]/getTickFrequency()/n << "ms" << endl;
    cout << "\n【方法六】利用 .ptr 和 * ++以及位运算、channels()的方法所用时间为" << 1000.*t[5]/getTickFrequency()/n << "ms" << endl;
    cout << "\n【方法七】利用.ptr 和 * ++ 以及位运算(continuous)的方法所用时间为" << 1000.*t[6]/getTickFrequency()/n << "ms" << endl;
    cout << "\n【方法八】利用 .ptr 和 * ++ 以及位运算 (continuous+channels)的方法所用时间为" << 1000.*t[7]/getTickFrequency()/n << "ms" << endl;
    cout << "\n【方法九】利用Mat_ iterator 的方法所用时间为" << 1000.*t[8]/getTickFrequency()/n << "ms" << endl;
    cout << "\n【方法十】利用Mat_ iterator以及位运算的方法所用时间为" << 1000.*t[9]/getTickFrequency()/n << "ms" << endl;
    cout << "\n【方法十一】利用Mat Iterator_的方法所用时间为" << 1000.*t[10]/getTickFrequency()/n << "ms" << endl;
    cout << "\n【方法十二】利用动态地址计算配合at 的方法所用时间为" << 1000.*t[11]/getTickFrequency()/n << "ms" << endl;
    cout << "\n【方法十三】利用图像的输入与输出的方法所用时间为" << 1000.*t[12]/getTickFrequency()/n << "ms" << endl;
    cout << "\n【方法十四】利用操作符重载的方法所用时间为" << 1000.*t[13]/getTickFrequency()/n << "ms" << endl;

    waitKey();
    return 0;
}

 

  • 3
    点赞
  • 33
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值