Opencv中图像的遍历与像素操做

      Opencv中图像的遍历与像素操做

    OpenCV中表示图像的数据结构是cv::Mat,Mat对象本质上是一个由数值组成的矩阵。矩阵的每个元素表明一个像素,对于灰度图像,像素是由8位无符号数来表示(0表明黑,255表明白);对于彩色图像,每一个像素是一个三元向量,即由三个8位无符号数来表示三个颜色通道(Opencv中顺次为蓝、绿、红)。 
咱们先来介绍下cv::Mat类的获取像素的成员函数at(),其函数原型以下:
ios

template<typename _Tp> _Tp& at(int i0, int i1); //因为Mat能够存听任意数据类型的元素,因此该函数是用模板函数来实现的 //它自己不会进行任何数据类型转换,在调用的过程当中须要指明像素的数据类型 //即要与矩阵中的数据类型相匹配。如: img.at<uchar>(i,j)=255 img.at<cv::Vec3b>(i,j)[0]=255

在OpenCV中通常有四种图像遍历的方式,第一种天然是最平凡的数组遍历啦。为方便起见,如下全部的示例都是In-place变换操做。c++

一、数组遍历

在这里咱们经过操做像素的办法来实现图像的镜像变换,即实现flip(img,img,1)的功能。代码以下:数组

#include<iostream> #include<opencv2/core/core.hpp> #include<opencv2/imgproc/imgproc.hpp> #include<opencv2/highgui/highgui.hpp> using namespace std; using namespace cv; void Flip(Mat &img) { int rows=img.rows; int cols=img.cols; for(int i=0; i<rows; i++) { for(int j=0; j<cols/2; j++) { uchar t; if(img.channels()==1) { t=img.at<uchar>(i,j); img.at<uchar>(i,j)=img.at<uchar>(i,cols-1-i); img.at<uchar>(i,cols-1-i)=t; } else if(img.channels()==3) { for(int k=0; k<3; k++) { t=img.at<Vec3b>(i,j)[k]; img.at<Vec3b>(i,j)[k]=img.at<Vec3b>(i,cols-1-j)[k]; img.at<Vec3b>(i,cols-1-j)[k]=t; } } } } } int main() { Mat img1=imread("test.jpg"); //将任意一张名为test.jpg的图片放置于工程文件夹test中 imshow("First",img1); if(!img1.data) { cout<<"error! The image is not built!"<<endl; return -1; } Flip(img1); imshow("Second",img1); waitKey(); return 0; }

效果以下: 
这里写图片描述
ruby

二、指针遍历

OpenCV中cv::Mat类提供了成员函数ptr获得图像任意行的首地址。ptr函数是一个模板函数,其原型为:数据结构

template<typename _Tp> _Tp* Mat::ptr(int i=0)

在这里咱们经过操做像素的办法来实现图像的水平反转,即实现flip(img,img,0)的功能。代码以下:函数

#include<iostream> #include<opencv2/core/core.hpp> #include<opencv2/imgproc/imgproc.hpp> #include<opencv2/highgui/highgui.hpp> using namespace std; using namespace cv; void Flip(Mat &img) { int rows=img.rows; int cols=img.cols*img.channels(); for(int i=0; i<rows/2; i++) { uchar *p=img.ptr<uchar>(i); uchar *q=img.ptr<uchar>(rows-1-i); uchar t; for(int j=0; j<cols;j++) { t=*p; *p++=*q; *q++=t; } } } int main() { Mat img1=imread("test.jpg"); //将任意一张名为test.jpg的图片放置于工程文件夹test中 imshow("First",img1); if(!img1.data) { cout<<"error! The image is not built!"<<endl; return -1; } Flip(img1); imshow("Second",img1); waitKey(); return 0; }

效果以下: 
这里写图片描述
ui

指针遍历图像的过程当中,咱们可能会受以往遍历矩阵的影响,获得图像首行地址后,想直接经过一个循环去遍历rows*cols*img.channels()的内存,可是考虑到一些多媒体处理芯片在行的长度为是4或8的倍数时,对图像的处理会更加高效,因此OpenCV中对图像的每行会填补一些额外像素(不显示、不保存),将填补后的行的长度称为关键字,成员变量step表明以字节为单位的图像的有效宽度。所以,咱们只有在图像的有效宽度等于图像的真实宽度,即没有填补时,进行一重循环遍历。咱们能够经过cv::Mat的成员函数isContinuous来判断图像是否对行进行了填充,返回值为真,表示没有对行进行填充,反之填充。此外,咱们能够经过cv::Mat的成员变量data获得图像的首地址,等效于上面程序中的一种写法以下:spa

uchar *p=img.data;    //首行首地址 *p += img.step; //次行首地址 ……

三、迭代器遍历

只要对对C++稍有了解,就知道迭代器是专门用于遍历数据集合的一种很是重要的特殊的类,用其遍历隐藏了在给定集合上元素迭代的具体实现方式。C++的STL为每一个容器类型都提供了迭代器,OpenCV一样为cv::Mat提供了与STL迭代器兼容的迭代器。指针

cv::Mat实例的迭代器能够经过建立一个cv::MatIterator_的实例来获得,因为这是一个模板类,因此在声明时需指定图像像素的数据类型。code

cv::MatIterator_<cv::Ver3b> it;

另外可以使用定义在Mat_内部的迭代器类型

cv::Mat_<cv::Verc3b>::iterator it;

在这里咱们经过操做像素的办法来实现图像中心对称反转。代码以下:

#include<iostream> #include<opencv2/core/core.hpp> #include<opencv2/imgproc/imgproc.hpp> #include<opencv2/highgui/highgui.hpp> using namespace std; using namespace cv; void Flip(Mat &img) { uchar t; if(img.channels()==1) { Mat_<uchar>::iterator it=img.begin<uchar>(); Mat_<uchar>::iterator itend=img.end<uchar>(); itend--; //经过end成员函数获得的迭代器已超出集合,因此在这里自减 for(;it<itend;it++,itend--) { t=*it;*it=*itend;*itend=t; } } else if(img.channels()==3) { Mat_<Vec3b>::iterator it=img.begin<Vec3b>(); Mat_<Vec3b>::iterator itend=img.end<Vec3b>(); itend--; for(;it<itend;it++,itend--) for(int k=0; k<3; k++) { t=(*it)[k];(*it)[k]=(*itend)[k];(*itend)[k]=t; } } } int main() { Mat img1=imread("test.jpg"); //将任意一张名为test.jpg的图片放置于工程文件夹test中 imshow("First",img1); if(!img1.data) { cout<<"error! The image is not built!"<<endl; return -1; } Flip(img1); imshow("Second",img1); waitKey(); return 0; }

效果以下: 
这里写图片描述

相关文章
相关标签/搜索