Opencv文件操作与数据存储
- 格式:doc
- 大小:69.00 KB
- 文档页数:4
C++的OpenCV使用方法总结在计算机视觉和图像处理领域,OpenCV是一个非常强大的开源库,它提供了丰富的功能和工具,用于处理图像和视频。
作为C++程序员,了解并熟练使用OpenCV库是非常重要的。
本文将对C++中使用OpenCV的方法进行总结,并探讨一些常见的应用和技巧。
一、安装和配置OpenCV在开始使用OpenCV之前,首先需要安装和配置这个库。
在Windows评台上,可以通过下载预编译的二进制文件进行安装;在Linux评台上,可以通过包管理器进行安装。
安装完毕后,还需进行一些环境配置,确保编译器能够正确信息OpenCV库文件。
二、基本图像处理1. 读取和显示图像在C++中使用OpenCV读取和显示图像非常简单,只需几行代码即可完成。
首先需要使用imread函数读取图像文件,然后使用imshow 函数显示图像。
在进行图像显示后,需要使用waitKey函数等待用户按下某个键,以便关闭显示窗口。
2. 图像的基本操作OpenCV提供了丰富的图像处理函数,包括图像缩放、旋转、平移、通道拆分与合并等。
这些函数可以帮助我们对图像进行各种基本操作,从而满足不同的需求。
三、特征提取与描述1. Harris角点检测Harris角点检测是一种经典的特征点检测方法,它可以用来识别图像中的角点。
在OpenCV中,我们可以使用cornerHarris函数来实现Harris角点检测,然后对检测结果进行筛选和标记。
2. SIFT特征提取SIFT是一种广泛应用的特征提取算法,它具有旋转不变性和尺度不变性。
在OpenCV中,我们可以使用SIFT算法来提取图像的关键点和特征描述子,从而实现图像匹配和目标识别等功能。
四、图像分类与识别1. 使用支持向量机(SVM)进行图像分类OpenCV提供了对机器学习算法的支持,包括SVM分类器。
我们可以使用SVM对图像进行分类,从而实现图像识别和目标检测等功能。
2. 使用深度学习模型进行图像识别近年来,深度学习在图像识别领域取得了显著的成就。
OpenCV FileStorage类读写XML/YML文件在OpenCV程序中,需要保存中间结果的时候常常会使用.xml / .yml文件,opencv2.0之前都是使用C风格的代码,当时读写XML文件分别使用函数cvLoad()和cvSave()。
在2.0以后的OpenCV转为支持C++,这一举措大大减少了代码量以及编程时需要考虑的细节。
新版本的OpenCV的C++接口中,imwrite()和imread()只能保存整数数据,且需要以图像格式。
当需要保存浮点数据或者XML/YML文件时,之前的C语言接口cvSave()函数已经在C++接口中被删除,代替它的是FileStorage类。
这个类非常的方便,封装了很多数据结构的细节,编程的时候可以根据统一的接口对数据结构进行保存。
1. FileStorage类写XML/YML文件•新建一个FileStorage对象,以FileStorage::WRITE的方式打开一个文件。
•使用<< 操作对该文件进行操作。
•释放该对象,对文件进行关闭。
例子如下:FileStorage fs("test.yml", FileStorage::WRITE);fs << "frameCount" << 5;time_t rawtime; time(&rawtime);fs << "calibrationDate" << asctime(localtime(&rawtime));Mat cameraMatrix = (Mat_<double>(3,3) << 1000, 0, 320, 0, 1000, 240, 0, 0, 1); //又一种Mat初始化方式Mat distCoeffs = (Mat_<double>(5,1) << 0.1, 0.01, -0.001, 0, 0);fs << "cameraMatrix" << cameraMatrix << "distCoeffs" << distCoeffs;//features为一个大小为3的向量,其中每个元素由随机数x,y和大小为8的uchar数组组成fs << "features" << "[";for( int i = 0; i < 3; i++ ){int x = rand() % 640;int y = rand() % 480;uchar lbp = rand() % 256;fs << "{:" << "x" << x << "y" << y << "lbp" << "[:";for( int j = 0; j < 8; j++ )fs << ((lbp >> j) & 1);fs << "]" << "}";}fs << "]";fs.release();2. FileStorage类读XML/YML文件FileStorage对存储内容在内存中是以层次的节点组成的,每个节点类型为FileNode,FileNode可以使单个的数值、数组或者一系列FileNode的集合。
imread 读图namedWindow 产生一个被命名的窗口imshow 显示图像using namespace 全局的命名空间,可以避免导致全局命名冲突问题。
cv表示opencv的命名空间std是标准输入输出的命名空间waitKey 程序运行等待时间ms为单位#include<opencv2/highgui/highgui.hpp> 包含输入输出操作#include<iostream> VC的标准输入输出cout << 输出cin>> 输入均以endl结尾#include<string> 字符串库cvtColor 彩色图像变灰度图像imwrite 写出图像F9设置断点F5调试运行后点击变量中的+显示数据结构argv[1]的定位方式为:在工程属性中,命令行参数添加路径,例如:#include<opencv2/core/core.hpp> 定义的基本构建块库Mat 基本图像容器,是一个矩阵类。
用法:Mat A; //定义矩阵Mat B(A);//复制A矩阵数据给BMat D(A,Rect(10, 10, 100, 100)); //将A矩阵的一个区域给DMat F = A.clone();//复制A矩阵数据给FMat M(2,2,CV_8UC3,Scalar(0,0,255));//创建一个矩阵,2×2大小,数据结构为CV_[每个数据占用BIT][Signed or Unsigned][Type Prefix]C[取前几个数或书写几遍] Scalar数据向量。
Signed有符号:8位数据范围为:-128~127Unsigned无符号:8位数据范围为:0~255Mat::eye(4, 4,CV_64F) //单位矩阵Mat::ones(2, 2,CV_32F) //全1矩阵Mat::zeros(3,3,CV_8UC1) //全0矩阵Mat C = (Mat_<double>(3,3) << 0, -1, 0, -1, 5, -1, 0, -1, 0);//常规矩阵定义randu(R,Scalar::all(0),Scalar::all(255));//任意矩阵的生成Point2f P(5, 1);//定义二维点Point3f P3f(2, 6, 7); //定义三维点vector<float> v;//定义浮点数向量vector<Point2f> vPoints(20);//定义点坐标(二维数据)向量uchar就是uint8加注const 不能改变参数值;sizeof 读取大小I.channels(); //图像维数I.rows; //图像的行数I.cols;//图像的列数I.depth();//图像的深度对于图像的点式处理:uchar* p;//定义一个uint8图像for(i = 0;i < nRows; ++i){p = I.ptr<uchar>(i); //将每一行的向量赋给pfor (j = 0;j < nCols; ++j){p[j] = table[p[j]];//对每一行的向量,每一列的元素进行赋值,table是变换好的数组}}或者:uchar* p = I.data;//将图像的数据空间给pfor( unsigned int i =0;i < ncol*nrows; ++i)*p++ = table[*p];//*p中存储了图像的数据,一个一个赋值或者:LUT(I,lookUpTable,J); //效率最高的读图方式Mat lookUpTable(1, 256,CV_8U);uchar * p2 = lookUpTable.data;for( int i = 0;i < 256; ++i)p2[i] = table[i];图像的RGB分离:const int channels = I.channels();switch(channels){case 1:{MatIterator_<uchar> it,end;//灰度图像的变换for(it = I.begin<uchar>(),end = I.end<uchar>();it != end; ++it)*it = table[*it];break;}case 3:{MatIterator_<Vec3b> it,end;//RGB图像的变换for(it = I.begin<Vec3b>(),end = I.end<Vec3b>();it != end; ++it)//<Vec3b>指三列向量,即三个通道分量{(*it)[0] = table[(*it)[0]];//B分量(*it)[1] = table[(*it)[1]]; //G分量(*it)[2] = table[(*it)[2]]; //R分量}}}或者:case 3:{Mat_<Vec3b> _I = I;for( int i = 0;i < I.rows; ++i)for( int j = 0;j < I.cols; ++j){_I(i,j)[0] = table[_I(i,j)[0]];_I(i,j)[1] = table[_I(i,j)[1]];_I(i,j)[2] = table[_I(i,j)[2]];}I = _I;break;}Mat& ScanImageAndReduceC(Mat& I, const uchar* const table)//看图函数Mat& ScanImageAndReduceIterator(Mat& I, const uchar* const table) //看图函数Mat& ScanImageAndReduceRandomAccess(Mat& I, const uchar* const table) //看图函数模板运算:void Sharpen(const Mat& myImage, Mat& Result) //图像锐化函数CV_Assert(myImage.depth() == CV_8U); //判决图像是否是整型的数据Result.create(myImage.size(),myImage.type());//创建MAT矩阵类型执行算法:for(int j = 1 ;j < myImage.rows-1; ++j){const uchar* previous = myImage.ptr<uchar>(j - 1);const uchar* current = myIma ge.ptr<uchar>(j);const uchar* next = myImage.ptr<uchar>(j + 1);uchar* output = Result.ptr<uchar>(j);for(int i= nChannels;i < nChannels*(myImage.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));//将0行的像素值设为0或者:Mat kern = (Mat_<char>(3,3) << 0, -1, 0,-1, 5, -1,0, -1, 0);filter2D(I,K,I.depth(),kern);//使用模板滤波锐化图像,该函数所在库为<opencv2/imgproc/imgproc.hpp>图像融合算法:addWeighted(src1,alpha,src2,beta, 0.0,dst);//融合函数,需两幅图像一样大图像的亮度调整算法:相应的代码for( int y = 0;y < image.rows;y++ ){ for( int x = 0;x < image.cols;x++ ){ for( int c = 0;c < 3;c++ ) //三个通道分别运算{new_image.at<Vec3b>(y,x)[c] =saturate_cast<uchar>(alpha*(image.at<Vec3b>(y,x)[c]) + beta);}}}Mat::zeros(image.size(),image.type());//创建0阵image.convertTo(new_image, -1,alpha,beta);//线性图像变换图元:Point pt = Point(10, 8);//点的坐标Scalar(B,G,R) ;//给出像素的颜色值ellipse( img,Point(x,y),Size(x,y), angle,0, 360,Scalar( 255, 0, 0 ),thickness, lineType );//画椭圆,img图像名,point中心点,size大小,angle角度,0,起始点角度,360终点角度,scalar色彩,thickness线宽(-1为全填充),lineType 线型;调用时凡是给定的参数不管,只需给出其他参数,例如Ellipse( atom_image, 90 );给出了图像名和角度,其他量为已知。
读入图像﹑显示图像和保存图像是图像处理过程中最基本的,也是必不可少的操作.配置好OpenCV以后,包含以下两个头文件:#include "cv.h"#include "highgui.h"IplImage* image=cvLoadImage("D:\\123.jpg",-1);//函数cvLoadImage()的第1个参数是图像文件的路径.//第2个参数是读取图像的方式:-1表示按照图像本身的类型来读取,1表示强制彩色化,0表示//强制灰值化.if(image==NULL){MessageBox("无法读取图像数据!", "提示",MB_OK);//在MFC工程中这样用//若在win32控制台程序中,用printf("无法读取图像数据!\n");return;//不作任何操作,就不会执行后面的程序了}cvNamedWindow("图像显示",CV_WINDOW_AUTOSIZE);//该函数的功能是按照指定方式创建一个窗口,用于显示图像.//第1个参数是窗口的名称,自己可以任意设置//第2个参数表示窗口的大小会自动根据图像尺寸而变化cvShowImage("图像显示",image);//该函数的功能是在指定的窗口上显示图像.//第1个参数是显示图像窗口的名称//第2个参数是要显示的图像cvSaveImage("D:\\saveImage.jpg",image);//该函数的功能是将图像另存为//第1个参数是保存的路径,自己可以设置其它路径//第2个参数是要保存的图像cvWaitKey(0);//一直等待按键没有这句的话图像不能正常显示cvReleaseImage(&image);//释放图像内存cvDestroyWindow("图像显示");//销毁窗口资源//读取和显示完图像之后,要及时释放所占的内存资源.运行示例:。
CVMat是OpenCV中用于表示和操作图像的数据类型之一。
在图像处理和计算机视觉应用中,经常需要对图像进行像素级的操作,而读取像素值是其中一个基本操作。
掌握CVMat高效读取像素值的方法对于图像处理的效率至关重要。
1. CVMat简介CVMat是OpenCV中用于存储图像数据的多维数组类型。
它允许用户访问和操作图像的像素值,并提供了丰富的函数和方法用于图像处理、分析和计算。
CVMat类型的对象可以直接从文件中读取图像数据,也可以由用户创建,并通过矩阵操作进行图像处理。
2. 像素值的表示在CVMat中,图像的像素值通常以矩阵的形式存储。
对于灰度图像,每个像素值由一个标量表示;对于彩色图像,每个像素值通常由三个标量表示,分别代表红、绿、蓝三个通道的数值。
读取像素值的基本操作就是获取对应位置的标量或标量组。
3. 读取灰度图像的像素值在CVMat中,可以使用"at<>"方法来获取特定位置的像素值。
对于灰度图像mat,可以使用mat.at<uchar>(i, j)来获取第i行第j列位置的像素值,其中uchar表示像素值的数据类型。
这种方法直接通过索引来访问像素值,效率较高。
4. 读取彩色图像的像素值对于彩色图像,由于每个像素点有三个通道的像素值,因此需要使用Vec3b类型来表示。
对于彩色图像mat,可以使用mat.at<Vec3b>(i, j)来获取第i行第j列位置的像素值,返回一个包含三个标量的Vec3b对象。
同样地,这种方法也是直接通过索引来访问像素值,效率也较高。
5. 使用指针访问像素值除了直接使用at<>方法来访问像素值外,还可以使用指针来加速像素值的读取操作。
通过将CVMat对象的数据指针进行类型转换,可以直接通过指针来访问像素值,从而避免了at<>方法中的索引操作,提高了读取像素值的效率。
6. 读取像素值的性能比较针对不同的图像处理应用场景,可以根据实际需求选择不同的方法来读取像素值。
目录1引言 (1)2 OpenCV的结构 (1)3 VC 6下的安装与配置 (2)3.1安装OpenCV(略) (2)3.2 配置Windows环境变量 (2)4 VC++的环境设置 (4)5如何创建一个项目来开始OpenCV 编程 (5)6如何读入和显示图像 (7)7如何访问图像像素 (10)8如何访问矩阵元素 (11)9如何在OpenCV 中处理我自己的数据 (12)10. 例程 (13)10.1 Kalman滤波进行旋转点的跟踪 (13)10.2 背景建模 (16)10.3 视频I/O (21)10.4 矩阵操作 (23)10.5 轮廓检测 (27)1引言OpenCV(Intel® Open Source Computer Vision Library) 是Intel 公司面向应用程序开发者开发的计算机视觉库,其中包含大量的函数用来处理计算机视觉领域中常见的问题,例如运动分析和跟踪、人脸识别、3D 重建和目标识别等。
目前该函数库的最新版本是OpenCV 4.0,可以通过访问/projects/opencvlibrary免费获得OpenCV 库以及相关的资料。
另外,还可以通过访问/group/OpenCV,对于OpenCV使用中的一些问题与经验进行讨论。
相对于其它图像函数库,OpenCV是一种源码开放式的函数库,开发者可以自由地调用函数库中的相关处理函数。
OpenCV中包含500多个处理函数,具备强大的图像和矩阵运算能力,可以大大减少开发者的编程工作量,有效提高开发效率和程序运行的可靠性。
另外,由于OpenCV具有很好的移植性,开发者可以根据需要在MS-Windows和Linux两种平台进行开发,速度快,使用方便。
2 OpenCV的结构目前OpenCV包含如下几个部分:Cxcore: 一些基本函数(各种数据类型的基本运算等)Cv: 图像处理和计算机视觉功能(图像处理,结构分析,运动分析,物体跟踪,模式识别,摄像定标)Highgui: 用户交互部分(GUI, 图像视频I/O, 系统调用函数)Cvaux: 一些实验性的函数(ViewMorphing, 三维跟踪,PCA,HMM)另外还有cvcam, 不过linux版本中已经抛弃。
数据存储OpenCV提供了一种机制来序列化(serialize)和去序列化(de-serialize)其各种数据类型,可以从磁盘中按YAML或XML格式读/写。
在第4章中,我们将专门介绍存储和调用常见的对象IplImages的函数(cvSaveImage()和cvLoadImage())。
此外,第4章将讨论读/写视频的特有函数:可以从文件或者摄影机中读取数据的函数cvGrabFrame()以及写操作函数cvCreateVideoWriter()和cvWriteFrame()。
本小节将侧重于一般对象的永久存储:读/写矩阵、OpenCV结构、配置与日志文件。
首先,我们从有效且简便的OpenCV矩阵的保存和读取功能函数开始。
函数是cvSave()和cvLoad()。
例3-15展示了如何保存和读取一个5×5的单位矩阵(对角线上是1,其余地方都是0)。
例3-15:存储和读取CvMat1.CvMat A= cvMat( 5, 5, CV_32F, the_matrix_data );2.3.cvSave( "my_matrix.xml", &A );4.. . .5.// to load it then in some other program use …6.CvMat* A1= (CvMat*) cvLoad( "my_matrix.xml" );CxCore参考手册中有整节内容都在讨论数据存储。
首先要知道,在OpenCV中,一般的数据存储要先创建一个CvFileStorage结构(如例3-16)所示,该结构将内存对象存储在一个树形结构中。
然后通过使用CV_STORAGE_READ参数的cvOpenFileStorage()从磁盘读取数据,创建填充该结构,也可以通过使用CV_STORAGE_WRITE的cvOpenFileStorage()创建并打开CvFileStorage写数据,而后使用适当的数据存储函数来填充它。
在磁盘上,数据的存储格式为XML或者YAML。
例3-16:CvFileStorage结构,数据通过CxCore数据存储函数访问1.typedef struct CvFileStorage2.{3.... // hidden fields4.} CvFileStorage;CvFileStorage树内部的数据是一个层次化的数据集合,包括标量、CxCore对象(矩阵、序列和图)以及用户定义的对象。
假如有一个配置文件或日志文件。
配置文件告诉我们视频有多少帧(10),画面大小(320×240)并且将应用一个3×3的色彩转换矩阵。
例3-17展示了如何从磁盘中调出cfg.xml文件。
例3-17:往磁盘上写一个配置文件cfg.xml1.CvFileStorage* fs= cvOpenFileStorage(2."cfg.xml",3.0,4.CV_STORAGE_WRITE5.);6.cvWriteInt( fs, "frame_count", 10 );7.cvStartWriteStruct( fs, "frame_size", CV_NODE_SEQ );8.cvWriteInt( fs, 0, 320 );9.cvWriteInt( fs, 0, 200 );10.cvEndWriteStruct(fs);11.cvWrite( fs, "color_cvt_matrix", cmatrix );12.cvReleaseFileStorage( &fs );请留意这个例子中的一些关键函数。
我们可以定义一个整型变量通过cvWritelnt()向结构中写数据。
我们也可以使用cvStartWriteStruct()来创建任意一个可以任选一个名称(如果无名称请输入0或NULL)的结构。
这个结构有两个未命名的整型变量,使用cvEndWriteStruct()结束编写结构。
如果有更多的结构体,我们用相似的方法来解决;这种结构可以进行任意深度的嵌套。
最后,我们使用cvWrite()编写处色彩转换矩阵。
将这个相对复杂的矩阵程序与例3-15中简单的cvSave()程序进行对比。
便会发现cvSave()是cvWrite()在只保存一个矩阵时的快捷方式。
当写完数据后,使用cvReleaseFileStorage()释放CvFileStorage句柄。
例3-18显示了XML格式的输出内容。
例3-18:磁盘中的cfg.xml文件1.<?xml version="1.0"?>2.<opencv_storage>3.<frame_count>10</frame_count>4.<frame_size>320 200</frame_size>5.<color_cvt_matrix type_id="opencv-matrix">6.<rows>3</rows><cols>3</cols>7.<dt>f</dt>8.<data>...</data></color_cvt_matrix>9.</opencv_storage>我们将会在例3-19中将这个配置文件读入。
例3-19:磁盘中的cfg.xml文件1.CvFileStorage* fs= cvOpenFileStorage(2."cfg.xml",3.0,4.CV_STORAGE_READ5.);6.7.int frame_count= cvReadIntByName(8.fs,9.0,10."frame_count",11. 5 /* default value */12.);13.14.CvSeq* s= cvGetFileNodeByName(fs,0,"frame_size")->data.seq;15.16.int frame_width= cvReadInt(17.(CvFileNode*)cvGetSeqElem(s,0)18.);19.20.int frame_height= cvReadInt(21.(CvFileNode*)cvGetSeqElem(s,1)22.);23.24.CvMat* color_cvt_matrix= (CvMat*) cvReadByName(25.fs,26.0,27."color_cvt_matrix"28.);29.30.cvReleaseFileStorage( &fs );在阅读时,我们像例3-19中那样用cvOpenFileStorage()打开XML配置文件。
然后用cvReadlntByName()来读取frame_count,如果有没有读到的数,则赋一个默认值。
在这个例子中默认的值是5。
然后使用cvGetFileNodeByName()得到结构体frame_size。
在这里我们用cvReadlnt()读两个无名称的整型数据。
随后使用cvReadByName()读出我们已经定义的色彩转换矩阵。
将本例与例3-15中的cvLoad()进行对比。
如果我们只有一个矩阵要读取,那么可以使用cvLoad(),但是如果矩阵是内嵌在一个较大的结构中,必须使用cvRead()。
最后,释放CvFileStorage结构。
数据函数存储与CvFileStorage结构相关的表单列在表3-16中。
想了解更多细节,请查看CxCore手册。
表3-16:数据存储函数续表This line causes the error:> cvWrite(myFileStorage," X,Y,Reflectance", point3DSequence);It is too advanced syntax to be understood by OpenCV writing functions, namely, the tag name could not contain spaces or comma's.If you want to store each point as a structure,you may try the following:cvStartWriteStruct( myFileStorage, "point3DSequence", CV_NODE_SEQ ); for( i = 0; i < point3DSequence->total; i++ ){CvPoint3D32f* pt=(CvPoint3D32f*)cvGetSeqElem(point3DSequence,i); cvStartWriteStruct(myFileStorage, 0, CV_NODE_MAP+CV_NODE_FLOW); cvWriteReal( myFileStorage, "x", pt->x );cvWriteReal( myFileStorage, "y", pt->y );cvWriteReal( myFileStorage, "reflectance", pt->z ); cvEndWriteStruct( myFileStorage );}cvEndWriteStruct( myFileStorage );that will look nice, but a little bit bulky and then you willhave to read the elements manually from the filestorage.Or, if the all the data could be just written as a plain array:x0 y0 refl0 x1 y1 refl1 ...then just use:cvWrite( myFileStorage, "point3DSequence", point3DSequence );。