OpenCV学习笔记(三) 访问像素

转自:OpenCV如何扫描图像、利用查找表和计时

测试代码:opencv\samples\cpp\tutorial_code\core\how_to_scan_images

测试函数耗时

cv::getTickCount()    the number of clock cycle

cv::getTickFrequency()  the number of cycles per seconds

double t = (double)getTickCount();
// 做点什么 ...
t = ((double)getTickCount() - t)/getTickFrequency();
cout << "Times passed in seconds: " << t << endl;

访问像素点

以颜色缩减为例,可通过查找表替换颜色从而缩减存储,查找表构建方法:

uchar table[256]; 
for (int i = 0; i < 256; ++i)
   table[i] = divideWith* (i/divideWith);

有以下几种访问像素方法:

1. []运算符 加 查找表

int nRows = I.rows * I.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]];             
    }
}

我们获取了每一行开始处的指针,然后遍历至该行末尾。如果矩阵是以连续方式存储的,我们只需请求一次指针、然后一路遍历下去就行。彩色图像的情况有必要加以注意:因为三个通道的原因,我们需要遍历的元素数目也是3倍。

2. 迭代器

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]];
        }
    }
}

在cv::Mat_的子类中,下划线表示这是一个模板类。OpenCV将Mat设计为一个容器,可以这样声明迭代器:

cv::MatIterator_<cv::Vec3b> it;
cv::Mat_<cv::Vec3b>::iterator it;
// 之后就可以使用begin()和end()等方法了
cv::Mat_<cv::Vec3b>::iterator it= image.begin<cv::Vec3b>();

Mat_类型在使用begin和end方法时可以不加类型:

cv::Mat_<cv::Vec3b> cimage= image;
cv::Mat_<cv::Vec3b>::iterator it= cimage.begin();
cv::Mat_<cv::Vec3b>::iterator itend= cimage.end();

3. On-the-fly 动态计算地址:at()函数

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;
    }
}

注意:当且仅当在 debug 模式下 它会检查你的输入坐标是否有效或者超出范围。

cv::Mat_类型可以直接用operator()访问像素,省略.at(),少写两个字。

cv::Mat_<uchar> im2= image; // im2 refers to image
im2(50,100)= 0; // access to row 50 and column 100

4. 核心函数LUT(The core function)

Mat lookUpTable(1, 256, CV_8U);
uchar* p = lookUpTable.data; 
for( int i = 0; i < 256; ++i)
    p[i] = table[i];
LUT(I, lookUpTable, J);

对于一个给定的值,将其替换成其他的值是一个很常见的操作,OpenCV 提供里一个函数直接实现该操作,并不需要你自己扫描图像,就是:operationsOnArrays:LUT() <lut> ,一个包含于core module的函数。

性能表现

4. LUT > 1. Efficient way > 2. Iterator > 3. On-The-Fly random access

输入:600*450

输出:

Time of reducing with the C operator [] (averaged for 100 runs): 4.05264 milliseconds.
Time of reducing with the iterator (averaged for 100 runs): 137.583 milliseconds.
Time of reducing with the on-the-fly address generation - at function (averaged for 100 runs): 255.371 milliseconds.
Time of reducing with the LUT function (averaged for 100 runs): 3.51129 milliseconds.

结论: 尽量使用 OpenCV 内置函数. 调用LUT 函数可以获得最快的速度. 这是因为OpenCV库可以通过英特尔线程架构启用多线程. 

posted on 2014-03-02 14:13  eric.xing  阅读(2047)  评论(0编辑  收藏  举报

导航