1. 存取像素值


在opencv中能够直接对cv::Mat类型的图像调用at函数读取或赋值某个像素,我们用个简单的案例来说明:
//在一张图像上增加椒盐噪声,image为输入图像。n为噪点个数
void salt(Mat &image, int n)
{
for(int k = 0;k < n;k++)
{
//随机产生白色噪点
int i = qrand()%image.cols;
int j = qrand()%image.rows;
//假设是灰度图每一个像素的存取类型为uchar,即8bit整型数
if(image.channels() == 1){
image.at<uchar>(j,i) = 255;
}
//彩色图像有三个通道,像素存取类型为cv::Vec3b,即由三个uchar组成的向量。这里用下标[i]訪问每一个通道
else{
image.at<Vec3b>(j,i)[0] = 255;
image.at<Vec3b>(j,i)[1] = 255;
image.at<Vec3b>(j,i)[2] = 255;
}
}
}

效果例如以下:




能够看到有非常多白色的噪声点,像雪花一样^_^



2. 指针遍历图像

我们用颜色缩减函数来说明用行首地址的方式遍历整个图像的像素。颜色缩减就是将每一个通道的颜色数减少,假设每一个通道的强度都是有unsigned char表示。那么就有256*256*256个颜色数目,假设将每一个通道的颜色数减少为原先的1/8,那个总颜色数就是32*32*32。大概就是这个意思。
//颜色缩减函数,image为输入图像。div为缩减的倍数
void colorReduce(Mat&image, int div = 64)
{
int nl = image.rows; //图像的行数
//图像每行的像素数
int nc = image.cols * image.channels();
for(int j =0;j<nl;j++)
{
//得到第j行的首地址
uchar* data = image.ptr<uchar>(j);
//遍历每行的像素
for(int i =0;i<nc;i++)
{
data[i] = data[i]/div*div; //将每一个像素值都变为div的倍数,即将颜色数缩减了div倍
}
}
}

效果例如以下:





当输入图像为连续的时候,即没有对图像行尾填补元素,这时就能够将整个图像看成是一个长为W*H的一维数组。即用第一行的行首指针就能遍历到整个图像的像素:
//颜色缩减函数。image为输入图像,div为缩减的倍数
void colorReduce(Mat&image, int div = 64)
{
int nl = image.rows; //图像的行数
//图像每行的像素数
int nc = image.cols * image.channels(); //假设图像连续
if(image.isContinuous())
{
//reshape函数用于改变矩阵维度
//图像行数为1,列数为原先的行数乘上列数
image.reshape(1,image.cols*image.rows);
} for(int j =0;j<nl;j++)
{
//得到第j行的首地址
uchar* data = image.ptr<uchar>(j);
//遍历每行的像素
for(int i =0;i<nc;i++)
{
data[i] = data[i]/div*div; //将每一个像素值都变为div的倍数,即将颜色数缩减了div倍
}
}
}

当然opencv还有更为底层的指针。在cv::Mat中。图像数据以unsigned char形式保存在一块内存中。

这块内存的首地址能够通过data成员变量得到。data是一个unsigned char型的指针,所以循环能够用下面方式:

    //获得图像指针
uchar *data = image.data;
//获得第j行,第i列个像素值,step代表图像的行宽(包含填补像素)
data = image.data + j*image.step + i*image.elemSize();

3. 迭代器遍历图像

事实上说到遍历。非常多人都会想到用迭代器来实现,迭代器是一种特殊的类。专门用来遍历集合中的各个元素,openCV相同为cv::Mat提供了与STL迭代器兼容的迭代器,以下我们还是用颜色缩减为例说明迭代器的使用:
//颜色缩减函数。image为输入图像。div为缩减的倍数
void colorReduce(Mat&image, int div = 64)
{
//得到初始位置的迭代器
Mat_<Vec3b>::iterator it = image.begin<Vec3b>();
//得到终止位置的迭代器
Mat_<Vec3b>::iterator itend = image.end<Vec3b>();
//遍历全部像素
for(; it != itend; ++it){
(*it)[0] = (*it)[0]/div*div;
(*it)[1] = (*it)[1]/div*div;
(*it)[2] = (*it)[2]/div*div;
}
}

效果与用指针遍历的一样。



4. 以上四种存取像素方式效率对照


opencv中能够用getTickCount()来測量一段代码的执行时间,此函数返回从上次开机算起的时钟周期数,getTickFrequency()能够得到每秒内的时钟周期数,有这两个函数就能得到随意一段代码的执行时间了。
我们还是以颜色衰减函数为例,分别用以上四种方法遍历实现。看看执行时间有何不同:
//at方法
void colorReduce1(Mat&image, int div = 64)
{
int nl = image.rows; //图像的行数
//图像每行的像素数
int nc = image.cols * image.channels();
for(int j =0;j<nl-2;j++)
{
for(int i =0;i<nc-2;i++)
{
image.at<Vec3b>(j,i)[0] = image.at<Vec3b>(j,i)[0]/div*div;
image.at<Vec3b>(j,i)[1] = image.at<Vec3b>(j,i)[1]/div*div;
image.at<Vec3b>(j,i)[2] = image.at<Vec3b>(j,i)[2]/div*div;
}
}
} //行首指针方法
void colorReduce2(Mat&image, int div = 64)
{
int nl = image.rows; //图像的行数
//图像每行的像素数
int nc = image.cols * image.channels();
for(int j =0;j<nl;j++)
{
//得到第j行的首地址
uchar* data = image.ptr<uchar>(j);
//遍历每行的像素
for(int i =0;i<nc;i++)
{
data[i] = data[i]/div*div; //将每一个像素值都变为div的倍数,即将颜色数缩减了div倍
}
}
} //一维数组
void colorReduce3(Mat&image, int div = 64)
{
int nl = image.rows; //图像的行数
//图像每行的像素数
int nc = image.cols * image.channels(); //假设图像连续
if(image.isContinuous())
{
//reshape函数用于改变矩阵维度
//图像行数为1,列数为原先的行数乘上列数
image.reshape(1,image.cols*image.rows);
} for(int j =0;j<nl;j++)
{
//得到第j行的首地址
uchar* data = image.ptr<uchar>(j);
//遍历每行的像素
for(int i =0;i<nc;i++)
{
data[i] = data[i]/div*div; //将每一个像素值都变为div的倍数,即将颜色数缩减了div倍
}
}
} //迭代器方法
void colorReduce4(Mat&image, int div = 64)
{
//得到初始位置的迭代器
Mat_<Vec3b>::iterator it = image.begin<Vec3b>();
//得到终止位置的迭代器
Mat_<Vec3b>::iterator itend = image.end<Vec3b>();
//遍历全部像素
for(; it != itend; ++it){
(*it)[0] = (*it)[0]/div*div;
(*it)[1] = (*it)[1]/div*div;
(*it)[2] = (*it)[2]/div*div;
}
} //測试4种像素遍历方式执行时间
void calrunTime(int v,Mat&image)
{
double duration;
duration = static_cast<double>(getTickCount());
for(int i = 0;i<10;i++) //执行十次取平均值
{
switch(v)
{
case 1:
colorReduce1(image);
break;
case 2:
colorReduce2(image);
break;
case 3:
colorReduce3(image);
break;
case 4:
colorReduce4(image);
break;
default:
break;
}
}
duration = static_cast<double>(getTickCount()) - duration;
duration /= getTickFrequency()/100; //执行时间,以ms为单位
qDebug()<<"duration"<<v<<":"<<duration<<"ms";
}



可见用at的方式读取像素效率最低,用迭代器速度也比較慢。效率最高的方式还是使用指针读取。
好了,本篇就到此结束吧。过两天继续更^_^


參考书籍

《openCV2计算机视觉编程手冊》


(转载请注明作者和出处:Shawn-HT  http://blog.csdn.net/shawn_ht 未经同意请勿用于商业用途)






【从零学习openCV】opecv操作像素的更多相关文章

  1. 【从零学习openCV】IOS7下的人脸检測

    前言: 人脸检測与识别一直是计算机视觉领域一大热门研究方向,并且也从安全监控等工业级的应用扩展到了手机移动端的app,总之随着人脸识别技术获得突破,其应用前景和市场价值都是不可估量的,眼下在学习ope ...

  2. 【从零学习openCV】IOS7根据人脸检测

    前言: 人脸检測与识别一直是计算机视觉领域一大热门研究方向,并且也从安全监控等工业级的应用扩展到了手机移动端的app.总之随着人脸识别技术获得突破,其应用前景和市场价值都是不可估量的,眼下在学习ope ...

  3. 【从零学习openCV】IOS7人脸识别实战

    前言 接着上篇<IOS7下的人脸检測>,我们顺藤摸瓜的学习怎样在IOS7下用openCV的进行人脸识别,实际上非常easy,因为人脸检測部分已经完毕,剩下的无非调用openCV的方法对採集 ...

  4. 【从零学习openCV】IOS7下的openCV开发起步(Xcode5.1.1&amp;openCV2.49)

    前言: 开发IOS7已经有一月的时间了.近期在准备推研的事,有点想往CV方向发展.于是開始自学openCV. 关注CSDN已经非常久了.也从非常多博主那学到了非常多知识,于是我也从这周开启自己的blo ...

  5. 图像混合学习。运用加权函数,学习opencv基础操作

               {          cout<<     }           {          cout<<     }       ,,logoImage.c ...

  6. 学习Opencv 2.4.9(二) ---操作像素

    作者:咕唧咕唧liukun321 来自:http://blog.csdn.net/liukun321 本质上说一张图像就是由数值组成的矩阵.Opencv 2.x由 cv::Mat 这个数据结构来表示一 ...

  7. OpenCV学习C++接口 Mat像素遍历详解

    OpenCV学习C++接口 Mat像素遍历详解

  8. OpenCV坐标系与操作像素的四种方法

    像素是图像的基本组成单位,熟悉了如何操作像素,就能更好的理解对图像的各种处理变换的实现方式了. 1.at方法 第一种操作像素的方法是使用"at",如一幅3通道的彩色图像image的 ...

  9. 【学习opencv第六篇】图像的反转操作

    考试终于完了,现在终于有时间可以继续学习这个了.写这篇博客主要是因为以前一直搞不清楚图像数据到底是怎么存储的,以及这个step到底是什么,后来查了一下才知道原来step就是数据行的长度.. #incl ...

随机推荐

  1. js中with、this的用法

    with 语句 为一个或一组语句指定默认对象. 用法:with (<对象>) <语句>; with 语句通常用来缩短特定情形下必须写的代码量.在下面的例子中,请注意 Math ...

  2. Python 绝对简明手册

    Python 绝对简明手册 help(函数名)来获取相关信息 另外,自带的文档和google也是不可少的 2. 基本语法2.1. if / elif / else x=int(raw_input(&q ...

  3. IntelliJ IDEA导出Java 可执行Jar包

    原文:IntelliJ IDEA导出Java 可执行Jar包 保证自己的Java代码是没有问题的,在IDEA里面是可以正常运行的,然后,按下面步骤: 打开File -> Project Stru ...

  4. Android Mediaplayer 调用release()的时候ANR

    先stop,然后再release,最后吧media置为null

  5. WCF技术剖析之二十三:服务实例(Service Instance)生命周期如何控制[下篇]

    原文:WCF技术剖析之二十三:服务实例(Service Instance)生命周期如何控制[下篇] 在[第2篇]中,我们深入剖析了单调(PerCall)模式下WCF对服务实例生命周期的控制,现在我们来 ...

  6. win7系统远程连接其它计算机,并且向远程机传输文件

    首先,打开开始菜单,在程序自带的 “附件“ 中找到 "远程桌面连接"并打开,出现远程桌面对话框: 其次,在对话框左下角点击“选项”,选择“本地资源对话框”,在本地设备和资源下点击“ ...

  7. oralce 简单错误汇集。。。。。

    1.ora-12560 TNS:协议适配器错误 实例名被错误修改或者oracle 服务没有正常启动.

  8. TCP连接状态具体解释

    tcp状态: LISTEN:侦听来自远方的TCPport的连接请求 SYN-SENT:再发送连接请求后等待匹配的连接请求 SYN-RECEIVED:再收到和发送一个连接请求后等待对方对连接请求的确认 ...

  9. MSSQL - 创建新用户

    1.首先使用Windows身份验证登陆. 2.然后一次打开:安全性--->登录名.右键登录名,点击新建登录名. 3.常规选项卡下:填写登录名.选择SQL Server身份验证,填写登录名密码.取 ...

  10. VC 无标题栏对话框移动

    操作系统:Windows 7软件环境:Visual C++ 2008 SP1本次目的:实现无框移动 所谓的无标题栏对话框,是基于对话框的工程,对话框属性Border设置为None,对话框如下所示: 为 ...