ICode9

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

OpenCV官方教程学习----Core moudle----图片访问

2021-09-23 11:01:55  阅读:191  来源: 互联网

标签:Core Mat 访问 int moudle ---- ++ table channels


Core moudle(基础模块)

1.How to scan images, lookup tables and time measurement with OpenCV

1.1 测试用实例描述

OpenCV官方文档中此部分的应用背景:一种简答的色彩还原方法

问题描述:由于元素在矩阵中的存储最少也是256个不同值,如果直接采用一对一的映射将十分的需要资源,为了解决这个问题,首先对图像进行一个简单的色彩空间缩减算法,原理很简单,通过C++中的取商后乘十来实现,具体公式如下;
I n e w = ( I o l d 10 ) ∗ 10 I_{new}=(\frac{I_{old}}{10})*10 Inew​=(10Iold​​)∗10
这样以个256的像素值就会被压缩很多,如此一来就基本上解决了数据过大的问题,然而,就算使用这个公式进行计算,由于图像像素多,对于每个图像都应用这个公式进行操作也十分消耗时间,为此使用查找表的方式,先计算斌存好映射关系和结果,到时候直接根据像素值进行查找就可以了,省去实际计算时间。

首先是计算查找表

int divideWith = 0; //划分的宽度,由输入而来
stringstream s;
s << argv[2];
s >> divideWith;		//将输入的字符转换成int型
if (!s || !divideWith)
{
    cout << "Invalid number entered for dividing. " << endl;
    return -1;
}
uchar table[256];		//查找表计算,256个像素值对应26个结果,多对一的映射
for (int i = 0; i < 256; ++i)
    table[i] = (uchar)(divideWith * (i/divideWith));

如何衡量时间

  1. cv::getTickCount(),返回来自某个事件的系统 CPU 的滴答数,一直在变化。
  2. cv::getTickFrequency(),返回您的 CPU 在一秒内发出滴答声的次数。
double t = (double) getTickCount ();//起始时间
// 做点什么 ...
t = ((double) getTickCount () - t)/ getTickFrequency ();	//时间差除以CPU每秒滴答数=运行时间s
cout << "时间以秒为单位:" << t << endl;

1.2 图像矩阵在内存中如何存储。

一个图像矩阵的存储方式取决于:颜色系统(通道数),分辨位数。

对于灰度图像只有一个通道,以及像素值。
在这里插入图片描述

对于多通道图像,每一列中包含多个子列

在这里插入图片描述

注意:通道的存储方式是BGR不是RGB

在内存足够的情况下,每一个元素的存储时连续的,从而创建一个超级长的一行数据,可以通过cv::Mat::isContinuous()来查看是否图像存储是否连续。

1.3 有效的访问方式

在性能方面,OpenCV自己表示C风格的指针访问或者[]访问效率高,所以依然要使用C中访问数组元素的方式。

于是就有一个问题,我们要怎么样来对如此庞大的数据进行重新组合,或者有效访问呢

Mat& ScanImageAndReduceC(Mat& I, const uchar* const table)
{
    // accept only char type matrices
    CV_Assert(I.depth() == CV_8U);	//这是一个异常判断的函数,无符号八位存储方式很重要
    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);		//p指向每一行的首地址
        for ( j = 0; j < nCols; ++j)
        {
            p[j] = table[p[j]];		//根据每行元素的值进行查找
        }
    }
    return I;		//经过处理后,矩阵的数据完全变化了,而其他信息没有变化
}

另一种方法是使用Mat对象的data数据成员生成指针,因为data数据成员的指针指向第一行第一列的元素的地址。

uchar* p = I.data;
for( unsigned int i = 0; i < ncol*nrows; ++i)
    *p++ = table[*p];

这种方式访问是十分不直观的,无法知道是操作的是哪一行那一列数据。

1.4 迭代器方法----安全

迭代器访问不需要我们自己去计算从哪访问,要访问多少个,迭代器的方式更加模块化。

Mat& ScanImageAndReduceIterator(Mat& I, const uchar* const table)
{
    // accept only char type matrices
    CV_Assert(I.depth() == CV_8U);
    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;
}

如果用<char>型的迭代器去访问三个通道的图像,这样只能访问蓝色通道所有数据。可以看出,迭代器得到的类似于“每个像素的首地址”,并且迭代器访问完一行行后自动换行

1.5 On-the-fly address calculation with reference returning(这种方法不建议用)

Mat& ScanImageAndReduceRandomAccess(Mat& I, const uchar* const table)
{
    // accept only char type matrices
    CV_Assert(I.depth() == CV_8U);
    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;
}

这种方式访问数据,每次都要输入行和列以及数据的解释形式,容易出错且效率低。为了方便操作,OpenCV中提供了一种数据类型,cv::Mat_这种数据类型需要在定义的时候额外添加数据类型Mat_<double> M(20,20),如此一来以后访问就不用指定访问的数据类型了,可以直接使用()cv::Mat::at.

1.6 使用核心函数

在Core Function中提供了一种直接根据查找表操作进行的函数cv::LUT().

//创建查找表
Mat lookUpTable(1, 256, CV_8U);
uchar* p = lookUpTable.ptr();
for( int i = 0; i < 256; ++i)
    p[i] = table[i];
//使用函数直接完成查找,I根据lookUpTable进行查找并得到J,J是输出
LUT(I, lookUpTable, J);

1.7几种方法的速度

在这里插入图片描述

咳咳,事实证明,能用函数就不要自己写,费力不讨好。。。解决开始提出的问题的源码地址

标签:Core,Mat,访问,int,moudle,----,++,table,channels
来源: https://blog.csdn.net/mapleleaf98/article/details/120430782

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

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

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

ICode9版权所有