【从零学习openCV】opecv操作像素
来源:互联网 发布:十月革命100周年 知乎 编辑:程序博客网 时间:2024/06/05 19:06
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倍 } }}
//获得图像指针 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 未经允许请勿用于商业用途)
0 0
- 【从零学习openCV】opecv操作像素
- 【从零学习openCV】使用直方图统计像素
- opencv学习(4)像素操作
- 【OpenCV学习笔记】一.操作像素
- OpenCV 学习(像素操作 2)
- Opencv学习笔记之二:操作像素
- 【OpenCV学习笔记 005】 操作像素
- 【OpenCV学习笔记】三、操作像素
- OpenCV学习笔记二:操作像素
- opencv学习笔记 二 操作像素
- 【从零学习openCV】ubuntu+openCV+qt安装配置
- opencv 操作像素
- opencv Mat 像素操作
- OpenCV操作像素
- opencv mat像素操作
- opencv操作每个像素
- openCV 操作像素矩阵
- Opencv用迭代器操作像素
- js中的继承
- Schema的快速入门
- 十一周四个数的最大公约数
- iPhone 6 为何坚持1GB内存?
- static关键字
- 【从零学习openCV】opecv操作像素
- 基于VC++6.0的DLL开发(转)
- Java编程思想第四版*第七章*个人练习
- Java陷阱之assert关键字
- Android之SQLite数据库的使用
- 杨辉三角
- 虚函数实现原理(转)
- OC---分数的加、减、乘、除、比较大小
- 3Foundation Kit-1 字符串