opencv学习笔记
标签(空格分隔): 学习笔记
(1)操作像素
1.1对像素值的索引
1.1.1一维矩阵
if(iamge.channels()==1){image.at<uchar>(i,j);}初始化矩阵的某一行
result.row(0).setTo(cv::Scalar(0));//单独设置矩阵中某一行的像素值 result.row(result.rows - 1).setTo(cv::Scalar(0)); result.col(0).setTo(cv::Scalar(0)); result.col(result.cols - 1).setTo(cv::Scalar(0));1.1.2多维矩阵(以RGB图像为例)
if(image.channels()==3){image.at<cv::Vec3b>(i,j)[0]=255;image.at<cv::Vec3b>(i,j)[1]=255;image.at<cv::Vec3b>(i,j)[2]=255;//对三维矩阵的元素进行索引}注:opencv还有二元素向量和四元素向量类型(cv::Vec2b和cv::Vec4b).同样的,也有其他数据类型(如s代表short, i代表int,f代表float,d代表double.)所有的这些类型都是使用模板类cv::Vect
1.1.3扩展
使用CV::Mat的成员函数的返回值类型必须通过在调用时通过模板参数指定。因此,opencv提供了类cv::Mat_,此类的指针或者引用可以直接进项相互类型转换。该类重载了()操作符。例:
cv::Mat_<uchar> impointer=image;//impointer指针指向image;impointer(50,100)=0;//直接索引到图像矩阵的第50行,100列;1.1.4 程序运用:对RGB图像加入椒盐噪声
void Salt(cv::Mat &image, int n){ for (int k = 0; k < n; ++k) { //rand()随机数生成 int i = rand() % image.rows; int j = rand() % image.cols; if (image.channels() == 1)//彩色图 { image.at<uchar>(i, j) = 255; } if (image.channels() == 3)//灰度图 { image.at<cv::Vec3b>(i, j)[0] = 255; image.at<cv::Vec3b>(i, j)[1] = 255; image.at<cv::Vec3b>(i, j)[2] = 255; } }}1.2使用指针遍历图像
1.2.1 RGB图片在opencv的存储方式
图像缓冲区的前面三个字节对应图像左上角像素的三个通道值,接下来的三个字节对应第一行的第二个像素,以此类推。
1.2.2使用输入输出参数
在处理图像时,如果不想图片被改变,可以创建图像的“深拷贝”。
iamge=cv::imread("baboon.png");//克隆图像cv::Mat imageClone=image.clone();或者使用create函数创建一个与输入图像的尺寸和类型相同的矩阵:
image.create(image.rows,image.cols,image.type());注:create函数创建的图像的内存都是连续的,create函数不会对图像进行填补。要遍历图像需要使用两个指针完成:
for(int i=0;i<image.rows;++i){ //得到图像的第i行的首地址 const uchar*data_in=image.ptr<uchar>(i); uchar* data_out=result.ptr<uchar>(i); for(int j=0;j<image.cols*image.channels();++j) { //进行对每一个像素的处理 }}1.2.3底层指针运算
cv::Mat形式的矩阵在内存中的存储首地址可以通过data成员变量得到,且data是一个unsigned cahr型的指针:
uchar* data =image.data;从当前行到下一行可以通过对指针加上行宽得到:
data += iamge.step;可以通过下一行代码调用i行j列像素地址
data = image.data+i*image.step+j*image.elemSize();//相当于常用的矩阵索引(i*cols+j),先定义到位置:i*image.step,再加上下一个元素所占的内存空间+j*image.elemSize()1.3 使用迭代器遍历图像
一个图像的迭代器可以这样声明:
cv::MatIterator_<cv::Vec3b>it;或者
cv::Mat_<cv::Vec3b>::iterator it;1.3.1使用迭代器遍历图像所有像素
//得到初始位置的迭代器cv::Mat_<cv::Vec3b>::iteraator it = image.begin<cv::Vec3b>();//得到终止位置的迭代器cv::Mat_<cv::Vec3b>::iterator itend = image.end<cv::Vec3b>();//遍历所有像素for(;it!=itend;++it){(*it)[0]=...;(*it)[1]=...;(*it)[2]=...;}1.3.2高效的图像遍历循环
void colorReduce(cv::Mat &image, int div = 64){ int nl = image.rows; int nc = image.cols; //判断图像是否连续存储 if (image.isContinuous()) { nc = nc*nl;//把图像拉成一维数组 nl = 1; } int n = static_cast<int>(log(static_cast<double>(div)) / log(2.0)); uchar mask = 0xFF << n; for (int i = 0; i < nl; i++) { ucahr* data = image.ptr<uchar>(j); for (int i = 0; i < nc; i++) { //一次处理处理三个通道的一个像素 *data++ = *data&mask + dic / 2; *data++ = *data&mask + div / 2; *data++ = *data&mask + div / 2; } }}1.3.3遍历图像和邻域操作
通过获取相邻像素的指针对图像进行锐化处理void sharpen(const cv::Mat &image, cv::Mat &result){ result.create(image.size(), image.type()); for (int i = 1; i < image.rows - 1; i++)//除了第一行和最后一行的所有行 { const uchar* PRevious = image.ptr<const uchar>(i - 1);//获取图像上一行的指针 const uchar* current = image.ptr<const uchar>(i);//获取当前行的指针 const uchar* next = image.ptr<const uchar>(i + 1);//获取下一行的指针 uchar *output = result.ptr<uchar>(i);//输出行 for (int j = 1; j < image.cols - 1; j++) { *output = cv::saturate_cast<uchar>(5*current[j]-current[j-1]-current[j+1]-previous[j]-next[j]); output++; } } //将未处理的像素全部设置为0 result.row(0).setTo(cv::Scalar(0)); //单独设置矩阵中某一行的像素值 result.row(result.rows - 1).setTo(cv::Scalar(0)); result.col(0).setTo(cv::Scalar(0)); result.col(result.cols - 1).setTo(cv::Scalar(0)); }1.3.4把多维矩阵分层拉出
int main(){ cv::Mat image; image = cv::imread("baboon.png"); std::vector<cv::Mat>planes; cv::split(image, planes); cv::namedWindow("channel1"); cv::imshow("channel1", planes[0]); cv::namedWindow("channel2"); cv::imshow("channel2", planes[1]); cv::namedWindow("channel3"); cv::imshow("channel3", planes[2]); cv::waitKey(0);}1.3.5 定义感兴趣区域
方法一:使用cv::Rect cv::Rect须指定矩形左上角的坐标(构造函数的前两个参数)和矩形的长宽(构造函数的后两个参数)。 方法二:使用cv::Range cv::Range
//270,385是矩形左上角的坐标,logo.rows和logo.cols是矩形的长宽cv::Mat imageROI = image(cv::Range(270,270+logo.rows),cv::Range(385,385+logo.cols))方法三:直接在原图上定义
start,end为起始坐标cv::Mat imageROI = image.rowRange(start,end);cv::Mat imageROI = image.colRange(start,end);(2)基于类的图像处理
2.1面向对象的编程思想
2.1.1 示例程序:鉴定图像中含有给定颜色的所有像素,并返回一个二值图像,相同像素赋值255,其他像素赋值0;
//类定义class ColorDetector{private: int minDist; cv::Vec3b target; cv::Mat result;public://类方法申明 ColorDetector() :minDist(100){ //初始化默认参数 target[0] = target[1] = target[2] = 0; } cv::Mat process(const cv::Mat &image); int getDistance(const cv::Vec3b & color)const; void setColorDistanceThreshold(int distance); int setColorDistanceThreshold()const; void setTargetColor(unsigned char red, unsigned char green, unsigned char blue); cv::Vec3b getTargetColor()const; ~ColorDetector(){};};cv::Vec3b ColorDetector::getTargetColor()const{ return target;}void ColorDetector::setTargetColor(unsigned char red, unsigned char green, unsigned char blue){ //BGR顺序 target[2] = red; target[1] = green; target[0] = blue;}void ColorDetector::setColorDistanceThreshold(int distance){ if (distance < 0) { distance = 0; } minDist = distance;}int ColorDetector::setColorDistanceThreshold()const{ return minDist;}int ColorDetector::getDistance(const cv::Vec3b & color)const{ return abs(color[0] - target[0]) + abs(color[1] - target[1]) + abs(color[2] - target[2]);} cv::Mat ColorDetector::process(const cv::Mat &image){ //按需重新分配二值图像 //与输入图像的尺寸相同,但是只有一个通道 result.create(image.rows, image.cols, CV_8U); //得到迭代器 cv::Mat_<cv::Vec3b>::const_iterator it = image.begin<cv::Vec3b>(); cv::Mat_<cv::Vec3b>::const_iterator itend = image.end<cv::Vec3b>(); cv::Mat_<uchar>::iterator itout = result.begin<uchar>(); //处理每个像素 for (; it != itend; it++, itout++) { //计算每个像素和目标颜色的距离 if (getDistance(*it) < minDist) { *itout = 255; } else { *itout = 0; } } return result;}测试程序
int main() { //创建图像处理的对象 ColorDetector cdetect; //读取输入图像 cv::Mat image = cv::imread("boldt.jpg"); if (!image.data) return 0; //设置输入参数 cdetect.setTargetColor(130,190,230); cv::namedWindow("result"); //处理并显示结果 cv::imshow("result", cdetect.process(image)); cv::waitKey(); return 0; }处理结果 
![输出效果图][2]
2.2%20图像空间的转换
//RGB图像空间转换至LAB颜色空间cv::cvtColor(image,converted,CV_BGR2Lab);//RGB图像空间转换至YCbCr空间cv::cvtColor(image,converted,CV_BGR2YCrCb);//RGB图像空间转换至HSV图形空间cv::cvtColor(image,converted,%20CV_BGR2HSV);//RGB图像转换为灰度图cv::cvtColor(color,gray,CV_BGR2Gray);(3)使用直方图统计像素
3.1统计直方图并显示
3.1.1直方图统计类
class%20Histgram1D{private:%20%20%20%20//项的数量、范围、通道数%20%20%20%20int%20hist_size[1];%20%20%20%20float%20hist_range[2];%20%20%20%20const%20float*%20ranges[1];%20%20%20%20int%20channels[1];public:%20%20%20%20//准备1D直方图的参数,创建构造函数%20%20%20%20Histgram1D()%20%20%20%20{%20%20%20%20%20%20%20%20hist_size[0]%20=%20256;%20%20%20%20%20%20%20%20hist_range[0]%20=%200.0;%20%20%20%20%20%20%20%20hist_range[1]%20=%20255.0;%20%20%20%20%20%20%20%20ranges[0]%20=%20hist_range;%20%20%20%20%20%20%20%20channels[0]%20=%200;%20%20%20%20}%20%20%20%20//定义获取直方图的函数%20%20%20%20cv::MatND%20getHistgram(const%20cv::Mat%20&%20image)%20%20%20%20{%20%20%20%20%20%20%20%20cv::MatND%20hist;%20%20%20%20%20%20%20%20//调用calcHist函数统计图像的直方图%20%20%20%20%20%20%20%20cv::calcHist(&image,%20%20%20%20//输入图像%20%20%20%20%20%20%20%20%20%20%20%201,%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20//输入图像的个数%20%20%20%20%20%20%20%20%20%20%20%20channels,%20%20%20%20%20%20%20%20%20%20%20//通道数%20%20%20%20%20%20%20%20%20%20%20%20cv::Mat(),%20%20%20%20%20%20%20%20%20%20//不使用图像作为掩码%20%20%20%20%20%20%20%20%20%20%20%20hist,%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20//返回的直方图%20%20%20%20%20%20%20%20%20%20%20%201,%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20//1D直方图%20%20%20%20%20%20%20%20%20%20%20%20hist_size,%20%20%20%20%20%20%20%20%20%20//项的数量%20%20%20%20%20%20%20%20%20%20%20%20ranges%20%20%20%20%20%20%20%20%20%20%20%20%20%20//像素值的范围%20%20%20%20%20%20%20%20%20%20%20%20);%20%20%20%20%20%20%20%20%20%20%20%20return%20hist;%20%20%20%20}%20%20%20%20//定义绘制直方图的函数%20%20%20%20cv::Mat%20getHistgramImage(const%20cv::Mat%20&%20image)%20%20%20%20{%20%20%20%20%20%20%20%20//首先获得统计好的直方图数据%20%20%20%20%20%20%20%20cv::MatND%20hist%20=%20getHistgram(image);%20%20%20%20%20%20%20%20//获取直方图边界%20%20%20%20%20%20%20%20double%20max_value%20=%200;%20%20%20%20%20%20%20%20double%20min_value%20=%200;%20%20%20%20%20%20%20%20cv::minMaxLoc(hist,%20&min_value,%20&max_value,%200,%200);%20%20%20%20%20%20%20%20//显示直方图的图像%20%20%20%20%20%20%20%20//创建一个大小为256*256,的矩阵,矩阵中的数值类型uchar,初始化为255%20%20%20%20%20%20%20%20cv::Mat%20histImg(hist_size[0],%20hist_size[0],%20CV_8U,%20cv::Scalar(255));%20%20%20%20%20%20%20%20//设置最高点为nbins的90%%20%20%20%20%20%20%20%20int%20high_pointer%20=%20static_cast<int>(0.9*hist_size[0]);%20%20%20%20%20%20%20%20//每个像素值都绘制一条垂直线%20%20%20%20%20%20%20%20for%20(int%20i%20=%200;%20i%20<%20hist_size[0];%20i++)%20%20%20%20%20%20%20%20{%20%20%20%20%20%20%20%20%20%20%20%20float%20bin_value%20=%20hist.at<float>(i);//获取统计好的直方图数据%20%20%20%20%20%20%20%20%20%20%20%20//定义垂直线%20%20%20%20%20%20%20%20%20%20%20%20int%20intensity%20=%20static_cast<int>(bin_value*high_pointer%20/%20max_value);%20%20%20%20%20%20%20%20%20%20%20%20//绘制垂直线%20%20%20%20%20%20%20%20%20%20%20%20cv::line(histImg,%20cv::Point(i,%20hist_size[0]),%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20cv::Point(i,%20hist_size[0]%20-%20intensity),%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20cv::Scalar::all(0));%20%20%20%20%20%20%20%20}%20%20%20%20%20%20%20%20return%20histImg;%20%20%20%20}};3.1.2测试函数
int%20main(){%20%20%20%20cv::Mat%20image%20=%20cv::imread("lena.bmp", 0); if (!image.data) { //图像未被成功打开 std::cout << "can not open this image!" << std::endl; exit(0); } //创建Histgram1D 类对象 Histgram1D CalcHist; //计算直方图 cv::Mat histo = CalcHist.getHistgramImage(image); //遍历每个条目 /*for (int i = 0; i < 256; i++) { std::cout << "Count[" << i << "]=" << histo.at<float>(i) << std::endl; }*/ //显示直方图 cv::namedWindow("Histgram"); cv::imshow("Histgram", histo); cv::waitKey(0); }显示结果如下图:

####3.1.3生成二值图像的阈值函数 cv::Mat thresholded; cv::threshold(image,thresholded,60,255,cv::THRESH_BINARY); //(原图,输出图,图像阈值,像素灰度最大值,二值图像格式)