OpenCV2学习笔记.doc
- 配套讲稿:
如PPT文件的首页显示word图标,表示该PPT已包含配套word讲稿。双击word图标可打开word文档。
- 特殊限制:
部分文档作品中含有的国旗、国徽等图片,仅作为作品整体效果示例展示,禁止商用。设计者仅对作品中独创性部分享有著作权。
- 关 键 词:
- OpenCV2 学习 笔记
- 资源描述:
-
一:Opencv2与opencv1的区别: Opencv1.0版本于2006年面世,主要基于C语言。2009年发布opencv2,主要基于C++。此时opencv库被划分成多个模块,这些模块被编译成库文件后,位于lib文件夹中。主要有以下模块(版本1的结构见我的这篇blog: Opencv_core模块:包含核心功能,尤其是底层数据结构和算法函数。 Opencv_improc模块:包含图像处理函数。 Opencv_highgui模块:包含读写图像及视频的函数,以及操作图形用户界面函数。 Opencv_features2d模块:包含兴趣点检测子,描述子以及兴趣点匹配框架。 Opencv_calib3d模块:包含相机标定,双目几何估计以及立体视觉函数。 Opencv_video模块:包含运动估算,特征跟踪以及前景提取函数与类。 Opencv_objdetect模块:包括物体检测函数,如脸部和行人检测。 库中还包含其它的工具模块,如机器学习(opencv_ml),计算几何(opencv_flann),第三方代码(opencv_contrib)等。这些模块都对有一个单独的头文件(位于include文件夹)。推荐的声明方式如下: #include<opencv2\core\core.hpp> #include<opencv2\highgui\highgui.hpp> #include<opencv2\imgproc\imgproc.hpp> 而#include "cv.h"这是旧的代码方式,那是库还没有被划分为模块。 二:读取、显示和保存图片 (1)代码: [cpp] view plaincopyprint? #include <opencv2\core\core.hpp> #include <opencv2\highgui\highgui.hpp> #include <opencv2\imgproc\imgproc.hpp> #include <iostream> using namespace std; using namespace cv; int main() { Mat image = imread("F:\\tongtong.jpg", 1); //读取图片 if(!image.data) // data指向已分配内存块的指针 { cout << "fail to load image" << endl; } cout << "image size: " << image.size().height << "," << image.size().width << endl; //size()返回的是一个结构体 namedWindow("show"); imshow("show", image); // 显示图片 imwrite("F:\\tongtong2.jpg", image); <span style="white-space:pre"> </span>Mat result; flip(image,result, 0); namedWindow("result",0); imshow("result", result); waitKey(0); return 0; } (2)Explaination: <1>opencv2中用于存储图像数据为Mat类型,而在opencv1中用IplImage(详细见我的这篇blog: <2>image.data是指向已分配的内存块的指针,当图片没有加载进来,则为NULL。 <3>image.size()返回的是一个结构体,实际上包括width和height这两个成员变量。 <4>flip(image,result,0); //其中正数表示水平反转,0表示垂直反转,负数表示既有水平又有垂直反转。 3)结果: 三:深浅拷贝 (1) 浅拷贝: Mat B; B = image ; // 第一种方式 Mat C(image); // 第二种方式 这两种方式称为浅copy,是由于它们有不同的矩阵头,但是它们共享内存空间,即指向一个矩阵。当图像矩阵发生变化时,两者相关联,都会变化。 (2) 深拷贝: Mat B,C; B = image.clone(); // 第一种方式 image.copyTo(C); // 第二种方式 深拷贝是真正的copy了一个新的图像矩阵,此时image,B,C三者相互没有影响。 四:IplImage装换为Mat IplImage*iplImage = cvLoadImage("F:\\11.jpg",1); Mat image2(iplImage,false); // 其中false为浅拷贝,而true为深拷贝,默认为false。此时注意需要释放iplImage 当然opencv提供了另外一种指针类,无需手动释放,但现在已经不用了,可以使用它来封装IplImage指针: Ptr<IplImage> iplImage = cvLoadImage("F:\\11.jpg",1); 图像遍历主要有三种方法,本节主要介绍和比较这三种方法。 一:简单存取像素值 首先介绍一个名词—椒盐噪点:它是一种特殊的噪点,它随机的将图像中的部分像素设置为白色或者黑色。 Code: [cpp] view plaincopyprint? #include <opencv2\highgui\highgui.hpp> #include <opencv2\imgproc\imgproc.hpp> #include <opencv2\core\core.hpp> #include <iostream> using namespace std; using namespace cv; void salt(Mat &image, int n) { for(int k = 0; k < n; k++) { rand(); int i = rand() % image.cols; int j = rand() % image.rows; if(image.channels() == 1) { image.at<uchar>(j,i) = 255; } else if(image.channels() == 3) { image.at<Vec3b>(j,i)[0] = 255; image.at<Vec3b>(j,i)[1] = 255; image.at<Vec3b>(j,i)[2] = 255; } } } void salt2(Mat_<Vec3b> image, int n) // 这个只针对彩色三通道的图片 { for(int k = 0; k < n; k++) { rand(); int i = rand() % image.cols; int j = rand() % image.rows; if(image.channels() == 3) { image(j,i)[0] = 255; image(j,i)[1] = 255; image(j,i)[2] = 255; } } } int main() { Mat image = imread("F:\\huangrong.jpg", 1); salt(image, 3000); namedWindow("image"); imshow("image", image); waitKey(0); return 0; } Explaination: (1) rand()是随机数生成函数。 (2)image.at<Vec3b>(j,i)[0] = 255; 成员函数at(inty, int x)可以用来存取图像元素,但必须在编译时期知道它的数据类型,这样的函数被称为模板函数,即需要指定操作成员的类型at<uchar>(y,x) Opencv 中的类型Vec3b是由三个uchar组成的向量,可以通过操作符[]来获取。 (3)Mat_<Vec3b> image; image(j,i)[0] =255; Mat_是Mat的一个模板子类。在事先知道矩阵类型的情况下,使用Mat_可以带来一些便利。这个类定义了一些额外的成员方法,但没有定义成员变量。它重载了操作符(),允许我们可以通过它直接存取矩阵元素。 Result: 二:通过指针遍历图像 需要说明的是:一个宽为W,高为H的三通道图像需要填补一个大小由W*H*3个uchar构成的内存块。但是,出于效率的考虑,图像会在每行行尾填补一些额外的像素。这是因为,如果行的长度是4或8的倍数,一些多媒体处理芯片可以更高效的处理图像。 Mat中的成员函数isContinuous()可以判断这幅图像是否对行进行了填补。如果为真,则没有填补。 Code: [cpp] view plaincopyprint? /*通过指针遍历图像*/ #include <opencv2\highgui\highgui.hpp> #include <opencv2\imgproc\imgproc.hpp> #include <opencv2\core\core.hpp> #include <iostream> using namespace std; using namespace cv; void colorReduce(Mat &image, int div = 64) // 颜色缩减函数 { /*if(image.isContinuous()) { image.reshape(3, image.cols *image.rows); cout << image.rows << endl; cout << image.cols << endl; }*/ int n1 = image.rows; int nc = image.cols * image.channels(); if(image.isContinuous()) { nc = n1 * nc; n1 = 1; } for(int j = 0; j < n1; j++) { uchar *data = image.ptr(j); //uchar *data = image.data + j * image.step; // 得到指向第i行的地址有两种方法 ptr是成员函数,而data是成员变量,不推荐使用data,易出错 for(int i = 0; i < nc; i++) { data[i] = data[i]/div *div + div/2; } } cout << "一行含有的字节数:" << image.step << endl; cout << "一个像素所含有的字节:" << image.elemSize() << endl; cout << "通道数:" << image.channels()<< endl; cout << "总像素个数:" << image.total() << endl; cout << "width:" << image.size().width << endl; cout << "width:" << image.cols << endl; cout << "height:" << image.rows << endl; } void colorReduce2(const Mat &image, Mat &result, int div = 64) { int n1 = image.rows; //int nc = image.cols * image.channels(); int nc = image.cols ; for(int j = 0; j < n1; j++) { //uchar *data = image.ptr(j); //uchar *data_in = image.data + j * image.step; // 得到指向第i行的地址有两种方法 ptr是成员函数,而data是成员变量 //uchar *data_out = result.data + j * result.step; for(int i = 0; i < nc; i++) { uchar *data = image.data + j * image.step + i * image.elemSize(); // 这种方式不推荐使用,一方面容易出错,还不适用于带有"感兴趣区域" //data_out[i] = data_in[i]/div *div + div/2; data[0] = 0; data[1] = 0; data[2] = 0; } } } int main() { Mat image = imread("F:\\huangrong.jpg", 1); Mat result; result.create(Size(image.cols, image.rows),image.type()); // create函数为result创建一个与输入图像的尺寸和类型完全相同的矩阵 //colorReduce2(image, result); colorReduce(image); namedWindow("image"); imshow("image", image); //namedWindow("result"); //imshow("result", result); waitKey(0); return 0; } Explaination: (1) 得到指向第j行的地址有两种方法: image.ptr(j)是成员函数,而data是成员变量image.data+ j * image.step;但后面一种方法复杂点,推荐使用前者。 (2)成员变量step,rows,cols,成员函数elemSize,channels,total,size,create等解释见代码注释。 Result: 三:通过迭代器遍历图像 C++中迭代器不熟的可以看看我转载的一篇blog: Code: [cpp] view plaincopyprint? #include <opencv2\core\core.hpp> #include <opencv2\imgproc\imgproc.hpp> #include <opencv2\highgui\highgui.hpp> #include <iostream> using namespace std; using namespace cv; void colorReduce(Mat &image, int div = 64) { //Mat_<Vec3b>::iterator it = image.begin<Vec3b>(); //Mat_<Vec3b>::iterator itend = image.end<Vec3b>(); MatIterator_<Vec3b> it = image.begin<Vec3b>(); // 第二种迭代器方式 MatIterator_<Vec3b> itend = image.end<Vec3b>(); for(; it != itend; ++it) { (*it)[0] = (*it)[0]/div * div + div/2; (*it)[1] = (*it)[1]/div * div + div/2; (*it)[2] = (*it)[2]/div * div + div/2; } } void colorReduce2(Mat_<Vec3b> image, int div = 64) { //Mat_<Vec3b>::iterator it = image.begin(); // const_iterator //Mat_<Vec3b>::iterator itend = image.end(); MatIterator_<Vec3b> it = image.begin(); // 第二种迭代器方式 MatIterator_<Vec3b> itend = image.end(); // 常量迭代器 MatConstIterator_ Mat 实例不能修改 for(; it != itend; ++it) { (*it)[0] = (*it)[0]/div * div + div/2; (*it)[1] = (*it)[1]/div * div + div/2; (*it)[2] = (*it)[2]/div * div + div/2; } } int main() { Mat image = imread("F:\\huangrong.jpg", 1); if(!image.data){ cout << "fail to load image" << endl; return 0; } colorReduce(image); namedWindow("image"); imshow("image", image); waitKey(0); return 0; } Explaination: (1)定义迭代器有两种方法: MatIterator_<Vec3b>it ; //它是一个Mat实例的迭代器,是一个模板类 Mat_<Vec3b>::iterator it; // 它是定义在Mat_内部的迭代器 <Vec3b>表明是迭代器的类型,各个颜色分量可以通过操作符[]得到。 (2)获得迭代器开始和结束位置也有两种方法: image.begin<Vec3b>(); //image 是Mat类型,Mat的方法 image.begin(); // image是Mat_类型,通过Mat_实例得到的 (3)指向的mat是const类型所对应的迭代器,此时Mat对象不能修改。 MatConstIterator_<Vec3b>it ; //它是一个Mat实例的迭代器,是一个模板类 Mat_<Vec3b>::const_iteratorit; // 它是定义在Mat_内部的迭代器 Result: 四:三种方法的时间效率比较: Opencv中提供了两个非常实用的函数getTickCount()和getTickFrequency()可以非常方便的计算一段代码运行的时间; 代码: [cpp] view plaincopyprint? void timeCalc(Mat &image){ double duration; duration = static_cast<double>(getTickCount()); // CPU 滴答的次数 colorReduce(image); duration = static_cast<double>(getTickCount()) - duration; duration /= getTickFrequency(); // 运行时间,以ms为单位 //CPU 每ms滴答的次数 } 首先讲下: static_case<type_id>(expression) 该运算符把expression转换为type-id类型,但没有运行时类型检查来保证转换的安全性。 通过以上计算代码运行时间的方法我们可以得出上面三种遍历方法的运行时间:其中指针遍历时间最快,而迭代遍历最慢。可以说迭代器遍历代码简单,是一种最安全的遍历方法,出错率低。而简单的存取方法一般用来对随机位置的像素进行读写,并不适合用来遍历图像。遍历一般用指针。最后需要说明的是一次性处理一个像素的三个通道比循环处理每个通道所花费的时间要少,效率要高。 一:邻域操作 以下例子主要对图像进行锐化。基于拉普拉斯算子<后面讨论>。这幅图像的边缘部分将得到放大,细节部分将更加的锐利。计算方式为: sharpened_pixel = 5*current – left – right –up – down. Code: [cpp] view plaincopyprint? #include <opencv2\highgui\highgui.hpp> #include <opencv2\imgproc\imgproc.hpp> #include <opencv2\core\core.hpp> #include <iostream> using namespace std; using namespace cv; void sharpen(const Mat &image, Mat &result) { const int nChannels = image.channels(); for(int j = 1; j < image.rows - 1; j++) { const uchar *previous = image.ptr<const uchar>(j-1); // 上一行 const uchar *current = image.ptr<const uchar>(j); // 此行 const uchar *next = image.ptr<const uchar>(j+1); //下一行 uchar *output = result.ptr<uchar>(j); for(int i = 1; i < image.cols - 1; i++) { if(image.channels() == 1) // 黑白图像 { output[i] = saturate_cast<uchar>(5*current[i] - current[i-1] - current[i+1] - previous[i] - next[i]); } else if(image.channels() == 3) // 彩色图像 { *(output + i*image.elemSize()) =saturate_cast<uchar>(5 * *(current + i*image.elemSize()) - *(current + (i-1)*image.elemSize())- *(current + (i+1)*image.elemSize()) - *(previous + i*image.elemSize())- *(next + i*image.elemSize())); *(output + i*image.elemSize() + 1) = saturate_cast<uchar>(5 * *(current + i*image.elemSize() + 1) - *(current + (i-1)*image.elemSize() + 1)- *(current + (i+1)*image.elemSize() + 1) - *(previous + i*image.elemSize() + 1)- *(next + i*image.elemSize() + 1)); *(output + i*image.elemSize() + 2) = saturate_cast<uchar>(5 * *(current + i*image.elemSize() + 2) - *(current + (i-1)*image.elemSize() + 2)- *(current + (i+1)*image.elemSize() + 2) - *(previous + i*image.elemSize() + 2)- *(next + i*image.elemSize() + 2)); } } /*for(int i = nChannels; i < nChannels * (image.cols - 1); ++i) { *output ++ = saturate_cast<uchar>(5*current[i] - current[i-nChannels] - current[i+nChannels] - previous[i] - next[i]); }*/ } result.row(0).setTo(Scalar(0)); result.row(result.rows - 1).setTo(Scalar(0)); result.col(0).setTo(Scalar(0,0,0)); result.col(result.cols - 1).setTo(Scalar(0)); return; } void sharpen2D(const Mat &image, Mat &result) { Mat kernel(3, 3, CV_32FC1, Scalar(0)); kernel.at<float>(1,1) = 5.0; kernel.at<float>(0, 1) = -1.0; kernel.at<float>(1, 0) = -1.0; kernel.at<float>(1, 2) = -1.0; kernel.at<float>(2,1) = -1.0; /*也可以这样定义,这是opencv中简单初始化只有几个像素的mat类型*/ //Mat kernel = (Mat_<float>(3,3) << 0, -1.0, 0.0, -1.0, 5.0, -1.0, 0, -1.0, 0); filter2D(image, result,image.depth(), kernel); return; } int main() { Mat image = imread("F:\\huangrong.jpg", 0); Mat result; cout << image.depth() << endl; result.create(Size(image.cols, image.rows), image.type()); sharpen(image, result); //sharpen2D(image, result); namedWindow("image"); imshow("image", image); namedWindow("result"); imshow("result", result); waitKey(0); return 0; } Explaination: (1) saturate_cast<uchar>主要是类型检查来保证转换的安全性。如对于计算得到的结果不在0~255范围内,则进行截断。通常做法是将负值截断为0,将大于255的截断为255. (2) Matkernel = (Mat_<float>(3,3) << 0, -1.0, 0.0, -1.0, 5.0, -1.0, 0,-1.0, 0); //opencv中简单初始化只有几个像素的mat类型 (3) result.row(0).setTo(Scalar(0)); //可以将矩阵的第0行所有像素全部设置为0 (4)opencv中自带了filter2D这个函数可以进行领域操作,其结果是一样的,在某些情况下回更高效点。 Result: 二:算术操作 Code: [cpp] view plaincopyprint? #include <opencv2\core\core.hpp> #include <opencv2\highgui\highgui.hpp> #include <opencv2\imgproc\imgproc.hpp> #include <iostream> using namespace std; using namespace cv; Mat addWeight(Mat &image, Mat &image2) { Mat result; //addWeighted(image, 0.5, image2, 0.5, 0.0, result); result = image*0.5 + image2*0.5 + 0.3; // 大多数算术函数在opencv2中都有对应的重载操作符 return result; } Mat addWeight2(Mat &image, Mat &image3) { Mat result; vector<Mat> planes; split(image, planes); /// 将一个彩色三通道图像分解为三个单通道图像 planes[0] += image3; merge(planes, result); /// 将三个单通道图像合并为一个彩色三通道图像 return result; } int main() { Mat image = imread("F:\\huangrong.jpg", 1); if(!image.data){ cout << "fail to load image" << endl; return 0; } Mat image2 =Mat::zeros(image.rows, image.cols, image.type()); //Mat image2(image.rows, image.cols, image.ty展开阅读全文
咨信网温馨提示:1、咨信平台为文档C2C交易模式,即用户上传的文档直接被用户下载,收益归上传人(含作者)所有;本站仅是提供信息存储空间和展示预览,仅对用户上传内容的表现方式做保护处理,对上载内容不做任何修改或编辑。所展示的作品文档包括内容和图片全部来源于网络用户和作者上传投稿,我们不确定上传用户享有完全著作权,根据《信息网络传播权保护条例》,如果侵犯了您的版权、权益或隐私,请联系我们,核实后会尽快下架及时删除,并可随时和客服了解处理情况,尊重保护知识产权我们共同努力。
2、文档的总页数、文档格式和文档大小以系统显示为准(内容中显示的页数不一定正确),网站客服只以系统显示的页数、文件格式、文档大小作为仲裁依据,个别因单元格分列造成显示页码不一将协商解决,平台无法对文档的真实性、完整性、权威性、准确性、专业性及其观点立场做任何保证或承诺,下载前须认真查看,确认无误后再购买,务必慎重购买;若有违法违纪将进行移交司法处理,若涉侵权平台将进行基本处罚并下架。
3、本站所有内容均由用户上传,付费前请自行鉴别,如您付费,意味着您已接受本站规则且自行承担风险,本站不进行额外附加服务,虚拟产品一经售出概不退款(未进行购买下载可退充值款),文档一经付费(服务费)、不意味着购买了该文档的版权,仅供个人/单位学习、研究之用,不得用于商业用途,未经授权,严禁复制、发行、汇编、翻译或者网络传播等,侵权必究。
4、如你看到网页展示的文档有www.zixin.com.cn水印,是因预览和防盗链等技术需要对页面进行转换压缩成图而已,我们并不对上传的文档进行任何编辑或修改,文档下载后都不会有水印标识(原文档上传前个别存留的除外),下载后原文更清晰;试题试卷类文档,如果标题没有明确说明有答案则都视为没有答案,请知晓;PPT和DOC文档可被视为“模板”,允许上传人保留章节、目录结构的情况下删减部份的内容;PDF文档不管是原文档转换或图片扫描而得,本站不作要求视为允许,下载前可先查看【教您几个在下载文档中可以更好的避免被坑】。
5、本文档所展示的图片、画像、字体、音乐的版权可能需版权方额外授权,请谨慎使用;网站提供的党政主题相关内容(国旗、国徽、党徽--等)目的在于配合国家政策宣传,仅限个人学习分享使用,禁止用于任何广告和商用目的。
6、文档遇到问题,请及时联系平台进行协调解决,联系【微信客服】、【QQ客服】,若有其他问题请点击或扫码反馈【服务填表】;文档侵犯商业秘密、侵犯著作权、侵犯人身权等,请点击“【版权申诉】”,意见反馈和侵权处理邮箱:1219186828@qq.com;也可以拔打客服电话:0574-28810668;投诉电话:18658249818。




OpenCV2学习笔记.doc



实名认证













自信AI助手
















微信客服
客服QQ
发送邮件
意见反馈



链接地址:https://www.zixin.com.cn/doc/4868943.html