天天看点

opencv学习-----像素遍历

  1. OpenCV是一个图像处理库,囊括了大量的图像处理函数,为了解决问题通常要使用库中的多个函数,因此在函数中传递图像是家常便饭。同时不要忘了我们正在讨论的是计算量很大的图像处理算法,因此,除非万不得已,我们不应该拷贝大 的图像,因为这会降低程序速度。
  2. OpenCV函数中输出图像的内存分配是自动完成的(如果不特别指定的话)。
  3. 使用OpenCV的C++接口时不需要考虑内存释放问题。
  4. 赋值运算符和拷贝构造函数( ctor )只拷贝信息头。
  5. 使用函数 clone() 或者 copyTo() 来拷贝一副图像的矩阵。
  • 遍历图像的三种方法:
  1. 效率最高,查找表法
    Mat& ScanImageAndReduceC(Mat& I, const uchar* const table)
    {
        // accept only char type matrices
        CV_Assert(I.depth() != sizeof(uchar));     
    
        int channels = I.channels();
    
        int nRows = I.rows * channels; 
        int nCols = I.cols;
    
        if (I.isContinuous())
        {
            nCols *= nRows;
            nRows = 1;         
        }
    
        int i,j;
        uchar* p; 
        for( i = 0; i < nRows; ++i)
        {
            p = I.ptr<uchar>(i);
            for ( j = 0; j < nCols; ++j)
            {
                p[j] = table[p[j]];             
            }
        }
        return I; 
    }
               
    这里,我们获取了每一行开始处的指针,然后遍历至该行末尾。如果矩阵是以连续方式存储的,我们只需请求一次指针、然后一路遍历下去就行。彩色图像的情况有必要加以注意:因为三个通道的原因,我们需要遍历的元素数目也是3倍。
  2. 迭代法:在高性能法(the efficient way)中,我们可以通过遍历正确的 uchar 域并跳过行与行之间可能的空缺-你必须自己来确认是否有空缺,来实现图像扫描,迭代法则被认为是一种以更安全的方式来实现这一功能。在迭代法中,你所需要做的仅仅是获得图像矩阵的begin和end,然后增加迭代直至从begin到end。将*操作符添加在迭代指针前,即可访问当前指向的内容。
    Mat& ScanImageAndReduceIterator(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: 
            {
                MatIterator_<uchar> it, end; 
                for( it = I.begin<uchar>(), end = I.end<uchar>(); it != end; ++it)
                    *it = table[*it];
                break;
            }
        case 3: 
            {
                MatIterator_<Vec3b> it, end; 
                for( it = I.begin<Vec3b>(), end = I.end<Vec3b>(); it != end; ++it)
                {
                    (*it)[0] = table[(*it)[0]];
                    (*it)[1] = table[(*it)[1]];
                    (*it)[2] = table[(*it)[2]];
                }
            }
        }
        
        return I; 
    }
               
    对于彩色图像中的一行,每列中有3个uchar元素,这可以被认为是一个小的包含uchar元素的vector,在OpenCV中用 Vec3b 来命名。如果要访问第n个子列,我们只需要简单的利用[]来操作就可以。需要指出的是,OpenCV的迭代在扫描过一行中所有列后会自动跳至下一行,所以说如果在彩色图像中如果只使用一个简单的 uchar 而不是 Vec3b 迭代的话就只能获得蓝色通道(B)里的值。
  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<uchar>(i,j) = table[I.at<uchar>(i,j)];
                break;
            }
        case 3: 
            {
             Mat_<Vec3b> _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;
    }