ICode9

精准搜索请尝试: 精确搜索
首页 > 其他分享> 文章详细

【OpenCV】访问Mat图像中每个像素的值

2021-07-29 21:32:51  阅读:243  来源: 互联网

标签:uchar Mat int ++ 像素 OpenCV table


转载自:https://blog.csdn.net/warrenwg/article/details/48056363  侵删

今天百度搜资料还搜到了自己的。。。《访问图像中每个像素的值》,这是之前写的了,用的也是2.0的风格IplImage*格式,不太适用后来Mat的格式,特此重写一篇。

以下例子源自《The OpenCV Tutorials --Release 2.4.2》2.2 How to scan images, lookup tables and time measurement with OpenCV


图像容器Mat
还是先看Mat的存储形式。Mat和Matlab里的数组格式有点像,但一般是二维向量,如果是灰度图,一般存放 <uchar>类型;如果是RGB彩色图,存放 <Vec3b>类型。
单通道灰度图数据存放格式:
 
多通道的图像中,每列并列存放通道数量的子列,如RGB三通道彩色图:
 
注意通道的顺序反转了:BGR。通常情况内存足够大的话图像的每一行是连续存放的,也就是在内存上图像的所有数据存放成一行,这中情况在访问时可以提供很大方便。可以用  isContinuous()函数来判断图像数组是否为连续的。

访问图像中的像素

高效的方法:C操作符[ ]
最快的是直接用C风格的内存访问操作符[]来访问:

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 ;
    int nCols = I.cols* channels;
    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;
}


注意: 书中这段代码是有问题的,前面写成了  
 

int nRows = I.rows * channels;
int nCols = I.cols;


一般情况 isContinous为true,运行不会出错,但你可以注释掉那个if,会有访问越界的问题。
这种访问形式就是在每行定义一个指针,然后在内存上直接连续访问。如果整个数组在内存上都是连续存放的,那么只需要定义一个指针就可以访问所有的数据!如单通道的灰度图访问方式如下:
uchar* p = I.data;
for( unsigned int i =0; i < ncol*nrows; ++i)
    *p++ = table[*p];

安全的方法:迭代器iterator
相比用指针直接访问可能出现越界问题,迭代器绝对是非常安全的方法: 

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


这里我们只定义了一个迭代器,用了一个for循环,这是因为在OpenCV里迭代器会访问每一列然后自动跳到下一行,不用管在内存上是否isContinous。另外要注意的是在三通道图像中我们定义的是 <Vec3b>格式的迭代器,如果定义成uchar,则只能访问到B即蓝色通道的值。 
这种方式虽然安全,但是挺慢的,一会儿就知道了。

更慢的方法:动态地址计算
这种方法在需要连续扫描所有点的应用时并不推荐,因为它更实用与随机访问。这种方法最基本的用途是访问任意的某一行某一列: 

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


因为这种方法是为随机访问设计的,所以真的是奇慢无比。。。 

减小颜色空间 color space reduction
现在来介绍下上述函数对每个元素的操作,也就是用table更改像素值。这里其实是做了个减小颜色空间的操作,这在一些识别之类的应用中会大大降低运算复杂度。类如uchar类型的三通道图像,每个通道取值可以是0~255,于是就有 256*256个不同的值。我们可以通过定义: 
0~9 范围的像素值为 0 
10~19 范围的像素值 为 10 
20~29 范围的像素值为 20 
。。。。。。 
着这样的操作将颜色取值降低为 26*26*26 种情况。这个操作可以用一个简单的公式: 
 
来实现,因为C++中int类型除法操作会自动截余。 类如 Iold=14; Inew=(Iold/10)*10=(14/10)*10=1*10=10; 
在处理图像像素时,每个像素需要进行一遍上述计算也需要一定的时间花销。但我们注意到其实只有 0~255 种像素,即只有256种情况。进一步可以把256种计算好的结果提前存在表中 table 中,这样每种情况不需计算直接从 table 中取结果即可。 


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


于是table[i]存放的是值为i的像素减小颜色空间的结果,这样也就可以理解上述方法中的操作: 

p[j] = table[p[j]];

LUT : Look up table
OpenCV 很聪明的有个 LUT 函数就是针对这种 Look up talbe 的操作:

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

算法计时
为了验证几种方法的效率,可以用一个简单的计时和输出: 

double t;
t = (double)getTickCount();
t = 1000*((double)getTickCount() - t)/getTickFrequency();
t /= times;

实验结果

原图: 

降低颜色空间结果: 

算法时间: 

更清楚的时间对比表: 

转载请注明出处:http://blog.csdn.net/xiaowei_cqu/article/details/7771760

实验代码下载:http://download.csdn.net/detail/xiaowei_cqu/4443761

标签:uchar,Mat,int,++,像素,OpenCV,table
来源: https://blog.csdn.net/weixin_39450742/article/details/119220960

本站声明: 1. iCode9 技术分享网(下文简称本站)提供的所有内容,仅供技术学习、探讨和分享;
2. 关于本站的所有留言、评论、转载及引用,纯属内容发起人的个人观点,与本站观点和立场无关;
3. 关于本站的所有言论和文字,纯属内容发起人的个人观点,与本站观点和立场无关;
4. 本站文章均是网友提供,不完全保证技术分享内容的完整性、准确性、时效性、风险性和版权归属;如您发现该文章侵犯了您的权益,可联系我们第一时间进行删除;
5. 本站为非盈利性的个人网站,所有内容不会用来进行牟利,也不会利用任何形式的广告来间接获益,纯粹是为了广大技术爱好者提供技术内容和技术思想的分享性交流网站。

专注分享技术,共同学习,共同进步。侵权联系[81616952@qq.com]

Copyright (C)ICode9.com, All Rights Reserved.

ICode9版权所有