openCV学习笔记(四):图像遍历和像素操作

上一篇介绍了Mat的数据结构和存储方式,这里跟随官网学习Mat的遍历操作,因为openCV即是处理像素,所以遍历方法是我们必须要掌握的,这部分内容官网介绍的很详细也易懂,建议大家还是读透这些基础并上手测试加深理解,提前告诉大家,后面的内容比较晦涩,官网的内容很精简不易理解,所以请打好基础。

上篇介绍了Mat数据结构和存储方式:http://blog.csdn.net/jbl20078/article/details/78842477

本章内容官网地址:https://docs.opencv.org/master/db/da5/tutorial_how_to_scan_images.html

openCV如何扫描图像(遍历),利用查找表修改像素

先解释下什么是查找表:table
就是一个uchar类型的数组,用来保存不超多256个值,用来定义每个像素点或者像素中某个通道的值
比如:table[255] = 0  代表RGBA为255的值改为0,一个像素点(255,0,0) 就变成了(0,0,0)从红色变为黑色!
查找表有什么用?
查找表通过给Mat重新赋值,让它变为我们想要的样子,比如我想把一个图片深色部分变为255,浅色部分改为0
那我定义一个table如下:
uchar table[256];
    for(int i = 0; i < sizeof(table)/sizeof(table[0]); i++){
        if(i <= 100) {
            table[i] = 0;
        }
      
        if(i > 100){
            table[i] = 255;
        }
    }
我将这个table应用到图片中,图片就会有很强的对比(看下面对比图,上面的是应用的table)
openCV学习笔记(四):图像遍历和像素操作_第1张图片
认识了查找表,我们看怎么遍历Mat,并且去用查找表修改每一个Mat数据。

官网提供了四种遍历图像数据(Mat)的方法
1、通过指针直接遍历(最快的方式)
Mat& traverseMethod1(Mat& mat,const uchar* const table){
    //直接通过指针访问速度是最快的
    //只能处理char 类型的matrices
    CV_Assert(mat.depth() != sizeof(uchar));
    //获取它有多少个通道
    int channels = mat.channels();
    //有多少行 多少列
    int rows = mat.rows*channels;
    int cols = mat.cols;
    //是否连续存储的,如果连续存储直接一个for循环就是了
    if(mat.isContinuous()){
        cols *= rows;
        rows = 1;
    }
    
    int i,j;
    uchar* p;
    for(i = 0; i < rows; ++i){
        p = mat.ptr(i);
        for(j = 0; j < cols; j++){
            p[j] = table[p[j]];
        }
    }
    return mat;
}


2、通过迭代器遍历(最安全的方式,速度适中,不需要我们知道Mat尺寸,所以安全)

Mat& traverseMethod2(Mat& mat,const uchar* const table){
    //安全的遍历方法,通过迭代器去遍历
    CV_Assert(mat.depth() != sizeof(uchar));
    
    const int channels = mat.channels();
    switch(channels){
        case 1:{
            //单通道
            MatIterator_ it = mat.begin();
            for(;it != mat.end(); ++it){
                *it = table[*it];
            }
            break;
        }
            
        case 4:{
            //因为我这个图片是4通道的
            MatIterator_ it1 = mat.begin();
            for(;it1 != mat.end(); ++it1){
                (*it1)[0] = table[(*it1)[0]];
                (*it1)[1] = table[(*it1)[1]];
                (*it1)[2] = table[(*it1)[2]];
                (*it1)[3] = table[(*it1)[3]];
            }
             break;
        }
    }
    return mat;
}

3、通过at()函数访问地址(这个明显应用于修改特定位置的元素,用它遍历肯定慢)
代码照抄官网,我没自己写
Mat& ScanImageAndReduceRandomAccess(Mat& I, const uchar* const table)
{
    // accept only char type matrices
    CV_Assert(I.depth() != sizeof(uchar));     

    const int channels = I.channels();
    switch(channels)
    {
    case 1: 
        {
            for( int i = 0; i < I.rows; ++i)
                for( int j = 0; j < I.cols; ++j )
                    I.at(i,j) = table[I.at(i,j)];
            break;
        }
    case 3: 
        {
         Mat_ _I = I;
            
         for( int i = 0; i < I.rows; ++i)
            for( int j = 0; j < I.cols; ++j )
               {
                   _I(i,j)[0] = table[_I(i,j)[0]];
                   _I(i,j)[1] = table[_I(i,j)[1]];
                   _I(i,j)[2] = table[_I(i,j)[2]];
            }
         I = _I;
         break;
        }
    }
    
    return I;
}

4、核心函数LUT
这个是openCV提供的批量处理图像的方法,速度最快,也是我们以后最常用的
Mat& traverseMethod3(Mat& mat,const uchar* const table,Mat& outputMat){
    //其实还有一个效率更低的方法 这边不介绍了 官方不推荐使用  是通过mat.at的方法 当确定行数和列数的时候再用吧
    //最快的方法是openCV提供的 LUT函数  第一个参数接受一个mat输入  最后一个参数接受一个输出  中间一个参数是table 类型是mat
    //我们自定义一个table
    Mat lookUpTable(1,256,CV_8U);
    uchar* p = lookUpTable.ptr();
    for(int i =0 ; i < 256; ++i){
        p[i] = table[i];
    }
//    Mat outputMat;
    LUT(mat, lookUpTable, outputMat);
    return outputMat;
}


官网也写了一些计算代码执行时间的方法,这里不介绍了,不是重点,我们知道应该用哪些接口最高效就可以了,上面代码执行的效果如上面效果图所示,大家也可以定义自己的查找表修改一些图试一试,后面我们继续学习滤波图像方面的内容。

下一篇:学习openCV滤波功能
http://blog.csdn.net/jbl20078/article/details/78852194


你可能感兴趣的:(openCV,openCV遍历图像,openCV,lut用法)