OpenCV和关于VS平台的一些编程总结

0代表黑,256代表白,size又为Size(cols, rows)而rows,即图像的高度;Mat大小先是高度然后是宽度,而size大小显示宽度然后是高度

Opencv配置完全版:
http://blog.csdn.net/poem_qianmo/article/details/19809337重要而全面
http://wenku.baidu.com/link?url=6tvApl_rT8KG-xx3HOnLQFoI0EDyXlireHjPGb5lqSq4Tn2WHCr1HPzCM1vDJor9QWTaIAzwIegetLrJxdfszHv15O8wsBEjqCKeQsYQxkS
注:
对于32位系统,就添加:
”;…… opencv\build\x86\vc10\bin”(和之前的就有有的环境变量用英文的分号“;”进行分隔)

而对于64位系统,可以两个都添加上:
”;…… opencv\build\x86\vc10\bin”
和”…… opencv\build\x64\vc10\bin”,
这样,到时候才可以在编译器Win32和X64中来回切换都吃得开,游刃有余~
PS:有童鞋亲测说64位系统也只需添加”…… opencv\build\x86\vc10\bin”即可。
本电脑利用了在Microsoft.Cpp.Win32.user属性表下只配置了debug属性表,若再加入release属性表会有冲突。
另外在D:\workplace\VisualC++\opencv-shuxingbiao下有属性表可用,但是当项目opencv-shuxingbiao名变了,就打不开使用该属性表的其他项目了

之所以把D:\opencv\build\x86\vc12\bin加入到系统变量path中,是为了程序能自动利用bin中的dll文件。


D:\opencv\build\x86\vc12中含有动态链接要用的库lib(只包含包含dll的引用地址)和静态链接要用的库staticlib,两者都包含.lib文件,但是前者生成的链接程序要用到D:\opencv\build\x86\vc12中bin文件夹dll文件,而后者生成的连接程序不需要,因为后者的lib文件中既包含声明地址又包含函数的实现。动态库编译完之后是要一个exe 和这个dll同时在才能运行。好处?dll可以动态加载,也可以被多个程序调用,lib库存在版本必须一致的问题,例如2008的代码连接的时候 必须要连接2008编译出来的lib库,而dll 或许不需要这么强制对应。你试下在release应该没问题,其实就是库的配置问题,我们一般加的是不带_d的库release下的库是不加_d的所以可以。在debug下的库要加_d,在debug配置中将库换了就好了。
Debug属性表要使链接器正确链接到D:\opencv\build\x86\vc12\  *d.lib
Release属性表要使链接器正确链接到D:\opencv\build\x86\vc12\   .lib
Debug版:将所有opencv_*d.dll文件放在exe相同目录下,使程序可在其他电脑移植运行。
Release版:将所有opencv_*(且最后一个字母不是d).dll文件放在exe相同目录下,使程序可在其他电脑移植运行。
DEBUG版本包含调试信息,编译器生成的代码方便调试
RELEASE版本算是发布版本,RELEASE下不能调试,生成的文件更小,编译器生成的程序速度更快

Microsoft.Cpp.Win32.user属性表
通过它我们可以配置自己的属性文件.在文章最后有更具体的说明.在IDE中,打开View->Other Windows->Property Manager。展开树形后,你会发现一个名为“Microsoft.Cpp.Win32.user”的项目,右击并点击“Properties”后,你会看到一个和VC Project properties类似的属性设置框。详细介绍:
http://blog.csdn.net/zeusuperman1/article/details/9794111
环境变量
%PATH% 代表的是可执行文件的搜索路径,默认为 Windows 目录(C:\windows)和系统目录(C:\windows\system32),在此两个目录中的文件或文件夹不需要输入完整路径即可通过运行打开。如,在运行中输入 system32:打开 C:\windows\system32 文件夹,输入 notepad:打开 C:\windows\notepad.exe (就是记事本),输入 dllcache:打开 C:\windows\system32\dllcache 文件夹,等等。只要是这两个文件夹内的文件或文件夹,直接输入名称即可打开。你还可以把其他路径加入到 %Path% 变量,这样你就可以通过运行开输入你要打开的程序。
path(环境变量)是dos以前的内部命令,windows继续沿用至今。用作运行某个命令的时候,本地查找不到某个命令或文件,会到这个声明的目录中去查找。
一般设定java的时候为了在任何目录下都可以运行bin文件夹下的命令。就将java的bin目录声明到path中。在dos下运行path命令就可以设定或查看。在windows中要在系统设置中设定。
环境变量分为系统环境变量和用户环境变量。环境变量说白了就是指定一个软件的路径,比如说配置TomcatJdk等软件时就必须设置环境变量。例如Windows和DOS操作系统中的path环境变量,当要求系统运行一个程序而没有告诉它程序所在的完整路径时,系统除了在当前目录下面寻找此程序外,还应到path中指定的路径去找。path就是放一些程序的路径,放入path中的程序可以在 开始-运行-cmd-直接输入程序名称 enter 就可以运行。用户通过设置环境变量,来更好的运行进程。方法如下: 点击我的电脑然后按右键——属性——高级——环境变量——新建——名称/路径。所说的环境变量是指系统环境变量,对所有用户起作用,而用户环境变量只对当前用户起作用。例如要用java,那么把java的bin目录加入到path变量下面,那么它就是系统环境变量,所用用户登陆,在命令行输入java都会有java的帮助信息出来。而如果在某个用户的变量下面新建一个变量,那么它就只对这个用户有用,当以其他用户登陆时这个变量就和不存在一样。环境变量是包含诸如驱动器、路径或文件名之类的字符串。环境变量控制着多种程序的行为。例如,TEMP 环境变量指定程序放置临时文件的位置。任何用户都可以添加、修改或删除用户的环境变量。但是,只有管理员才能添加、修改或删除系统环境变量。一个“HOME=/home/ACCP286”的环境变量指明你在这个电脑上的个人主目录是“/home/ACCP286”,你每次要回到个人主目录时,不需要输入“cd /home/ACCP286”,只需要“cd $HOME”或者在文件管理器的地址栏输“$HOME”即可;你写一个脚本程序,用到你的个人主目录,也只需要写“$HOME”而不需要写“/home/ACCP286”。
使用“控制面板”中的“系统”可以自定义下列变量:
用于 logged_on_user_name 的用户环境变量 对于特定计算机的每个用户来说,用户环境变量是不同的。变量包括由用户设置的任何内容,以及由应用程序定义的所有变量,例如应用程序文件的路径。

VS2013中各种类型文件的作用:
    一个VS2013的工程中,所有的参数都是保存在.sln/.vcxproj/.vcxproj.user 三个文件中的,所有debug的设定,都是被保存在vcxproj.user中的。sln是解决方案的配置,主要是管理这个方案里的多个vcxproj。vcxproj是工程的配置文件,管理工程中细节比如包含的文件,写有项目的路径, 改变路径,引用库等。一般没有sln,也可以直接打开vcxproj,也可以重新生成sln。sln里有多个工程,当你移除某个工程时sln会有变化,sln并不是太重要
    .sln 相当于VC6中 .dsw
    .vcxproj 相当于VC6中 .dsp
    .suo 相当于VC6中 .ncb
    .vcxproj.filters 用于项目下文件的虚拟目录
    .vcxproj.user 是用户的一些相关配置,如True,允许多个用户使用自己喜好的方式配置这个项目(例如打开项目时候窗体位置等与项目内容无关的配置
其它文件和文件夹(res除外)一般都是中间物,在保存工程时删除不会有大问题。删除一些不重要的文件可以大大减少整个工程文件大小,节省磁盘空间。
项目是在开发过程中为了便于管理而运用的代码管理方式,程序员在开发软件时可以根据各个类的不同功能而将其归类到不同的项目中。而一个或多个项目可以组成一个解决方案。 说白了,项目就是为代码管理提供方便。 而代码的运行并不需要.csproj、.csproj.user之类的文件,所以在开发完成之后生成的软件或网站中,不会有以上之类的文件。 因此: 在软件开发中需要项目进行代码管理, 而在软件使用中则不需要项目。

.filters
vcxproj.filters后缀的文件是工程中的文件过滤器配置文件。VS通过这个文件中的配置,可以在解决方案中按照设置好的过滤方案,进行分类。这个就像资源视图中一样,可以分门别类的将这些资源归类,方便查找。vcxproj.filters所要实现的功能,就是在解决方案管理器中,实现文件的分类显示。

.sln和.suo
Visual Studio.NET采用两种文件类型(.sln和.suo)来存储特定于解决方案的设置,它们总称为解决方案文件。为解决方案资源管理器提供显示管理文件的图形接口所需的信息从而在每次继续开发任务时,不会因开发环境而分散精力;
*.sln:(Visual Studio.Solution) 通过为环境提供对项目、项目项和解决方案项在磁盘上位置的引用,可将它们组织到解决方案中。比如是生成Debug模式,还是Release模式,是通用CPU还是专用的等
*.suo: (solution user opertion) 解决方案用户选项记录所有将与解决方案建立关联的选项,
以便在每次打开时,它都包含您所做的自定义设置。.suo是solution user option的缩写,它是很重要的文件,它储存了用户界面的自定义配置,包括布局、断点和项目最后编译的而又没有关掉的文件(下次打开时用)等,以便于下一次你打开Visual Studio可以恢复这些设置,因此不要随便删除也无法删除,况且它们就是隐藏文件,所以不要管它们。 其实上面英文中解释已经很清楚了,要申明的是PDB中不带断点信息

.hpp和.h区别
hpp,其实质就是将.cpp的实现代码混入.h头文件当中,定义与实现都包含在同一文件,则该类的调用者只需要include该hpp文件即可,无需再将cpp加入到project中进行编译。而实现代码将直接编译到调用者的obj文件中,不再生成单独的obj,采用hpp将大幅度减少调用 project中的cpp文件数与编译次数,也不用再发布烦人的lib与dll,因此非常适合用来编写公用的开源库。
    hpp的优点不少,但是编写中有以下几点要注意:
1、是Header   Plus   Plus 的简写。
2、与*.h类似,hpp是C++程序头文件 。
3、是VCL专用的头文件,已预编译。
4、是一般模板类的头文件。
5、一般来说,*.h里面只有声明,没有实现,而*.hpp里声明实现都有,后者可以减少.cpp的数量。
6、*.h里面可以有using   namespace   std,而*.hpp里则无。

PDB文件
在生成项目调试程序时会同时生成pdb文件,它是一个程序数据库文件,保存着调试和项目状态信息,使用这些信息可以对程序的调试配置进行增量链接,pdb文件包含了编译后程序指向源代码的位置信息,用于调试的时候定位到源代码,主要是用来方便调试的。在程序发布为release模式时,建议将 pdb文件删除, 同时,对外发布的时候,也把 pdb删除,有利于保护程序。 
程序数据库 (PDB) 文件保存着调试和项目状态信息,使用这些信息可以对程序的调试配置进行增量链接。当用 /ZI 或 /Zi 编译 C/C++ 程序时或用 /debug 编译 Visual Basic/C#/JScript .NET 程序时将创建 PDB 文件。 Visual Studio 调试器使用由链接器直接创建的 project.PDB 文件并将此 PDB 的绝对路径嵌入到 EXE 或 DLL 文件中。project.PDB 该文件存储 .exe 文件的所有调试信息。对于本机代码,它驻留在 \debug 子目录中。

.sdf文件
SQL Server Compact Edition Database File (.sdf)文件,是工程的信息保存成了数据库文件,如果你没有参加大型的团队项目,不涉及到高深的调试过程,这个文件对于你来说没什么用了,可以放心的删除,如果你后来又需要这个文件了,简单,打开工程里的.sln文件重新编译链接就ok了。如果完全不需要,有讨厌这个文件太大,那么可以:在Visual Studio里进入如下设置:进入“Tools > Options”,选择“Text Editor > C/C++ > Advanced”,然后找到“Fallback Location”。然后把“Always use Fallback Location”和“Do Not Warn if Fallback Location”设置成“True”

BIN文件夹
主要放执行文件、debug、pdb(含调试信息)、dll(一种动态链接可执行文件)以及执行文件所需的相关数据包、动态库。

.hpp文件
是C++程序头文件。HPP,计算机术语,用C/C++语言编写的头文件,通常用来定义数据类型,声明变量、函数、结构和类。hpp,其实质就是将.cpp的实现代码混入.h头文件当中,定义与实现都包含在同一文件,则该类的调用者只需要include该hpp文件即可,无需再 将cpp加入到project中进行编译。而实现代码将直接编译到调用者的obj文件中,不再生成单独的obj,采用hpp将大幅度减少调用 project中的cpp文件数与编译次数,也不用再发布烦人的lib与dll,因此非常适合用来编写公用的开源库。

DLL文件
DLL文件即动态链接库文件,是一种可执行文件,它允许程序共享执行特殊任务所必需的代码和其他资源。Windows提供的DLL文件中包含了允许基于Windows的程序在Windows环境下操作的许多函数和资源。DLL多数情况下是带有DLL扩展名的文件,但也可能是EXE或其他扩展名。它们向运行于Windows操作系统下的程序提供代码、数据或函数。程序可根据DLL文件中的指令打开、启用、查询、禁用和关闭驱动程序。
DLL可在“C:Windows”目录“C:Windows/System”目录和程序的安装目录中找到。如果启动程序,但一个或多个DLL文件丢失或毁坏,则会收到出错消息,如“找不到xyz.dll”。如果启动的程序带有一个过期的DLL文件或不匹配的DLL文件,则会出现“未定义的动态链接调用”消息。这时,你可在其他电脑上找到正确的DLL文件并将它拷贝到适当的目录下,程序就能正确运行。

lib文件夹
.lib是一种文件名后缀,该文件为Windows操作系统中的库文件,相当于Linux中的·a或·o、.so文件。lib文件通过编译才可以使用,编译分静态与动态之分。
静态编译:静态lib将导出声明和实现都放在lib中。编译后所有代码都嵌入到宿主程序。
动态编译:动态lib相当于一个h文件,是对实现部分(.dll文件)的导出部分的声明。编译后只是将导出声明部分编译到宿主程序中,运行时候需要相应的dll文件支持。
.lib文件是程序的二进制库文件,编译器连接程序是一般会用到.LIB是C生成的目标文件,如果你要调用别人编译好的函数,就要用到LIB文件比如#include ,编译器就要去找stdio.lib而.h是引子,.lib才是主角。

.dll .h .lib等文件的作用与区别
.h用于编译阶段的审核,如在math.h中有函数声明:int abs(int);
但是在使用中写为#include ...abs(3,5);编译器阶段就会检测出错误。
.dll用于运行阶段,如调用SetWindowText()函数等,需要在user32.dll中找到该函数。DLL可以简单认为是一种包含供别人调用的函数和资源的可执行文件。
.lib用于链接阶段,在链接各部分目标文件(通常为.obj)到可执行文件(通常为.exe)过程中,需要在.lib文件中查找动态调用函数(一般为DLL中的函数)的地址信息,此时需要在lib文件中查找,如查找SetWindowText()函数的地址偏移就需要查找user32.lib文件。(.lib也可用于静态链接的内嵌代码)

包含目录include和库目录区别
(1)lib是编译时需要的,dll是运行时需要的。如果要完成源代码的编译,有lib就够了。如果也使动态连接的程序运行起来,有dll就够了。开发和调试阶段,当然最好都有。
(2)一般的动态库程序有lib文件和dll文件。lib文件是必须在编译期就连接到应用程序中的,而dll文件是运行期才会被调用的。如果有dll文件,那么对应的lib文件一般是一些索引信息,具体的实现在dll文件中。如果只有lib文件,那么这个lib文件是静态编译出来的,索引和实现都在其中。静态编译的lib文件有好处:给用户安装时就不需要再挂动态库了。但也有缺点,就是导致应用程序比较大,而且失去了动态库的灵活性,在版本升级时,同时要发布新的应用程序才行。
(3)在动态库的情况下,有两个文件,一个是引入库(.LIB)文件,一个是DLL文件,引入库文件包含被DLL导出的函数的名称和位置,DLL包含实际的函数和数据,应用程序使用LIB文件链接到所需要使用的DLL文件,库中的函数和数据并不复制到可执行文件中,因此在应用程序的可执行文件中,存放的不是被调用的函数代码,而是DLL中所要调用的函数的内存地址,这样当一个或多个应用程序运行是再把程序代码和被调用的函数代码链接起来,从而节省了内存资源。从上面的说明可以看出,DLL和.LIB文件必须随应用程序一起发行,否则应用程序将会产生错误。lib一般放库文件,也就是后缀.lib的,include 一般包含.h头文件目录bin表示binary目录,一般都是dll,exe等

各目录解释:
可执行文件目录:搜索可执行文件时使用的路径。与环境变量PATH相对应。
包含目录            :搜索包含文件时使用的路径。与INCLUDE相对应;
引用目录            :搜索通过#Using引入的文件时使用的路径。与环境变量LIBPATH相对应;
库目录                :搜素库文件时使用的路径。与环境变量LIB相对应。

Opencv引用图片路径:
在opencv中文件的路径显示不同与我们以往的
例如在 C:\Users\Yeahping YE\gg.jpg
在使用的时候应该多加一个斜杠\
src=cvLoadImage("C:\\Users\\Yeahping YE\\gg.jpg",1);
这个是在字符串的显示当中因为\是转义字符,所以为了显示\.我们需要\\ 这个在显示文件路径的时候我们比较容易忘记

SDK
1.微软每推出一个重要的windows版本,一般都会同时推出一个SDK(Software Development Kit)。SDK包含了开发该windows版本所需的windows函数和常数定义、API函数说明文档、相关工具和示例。SDK一般使用C语言,但不包括编译器。高版本VC++包括了SDK所有的头文件、帮助、示例和工具,不需要再安装SDK,低版本如VC++5.0则需要安装SDK
2.在目录C:\Program Files (x86)\Microsoft SDKs\Windows中可以看到你所安装过的所有SDK版本.当你进入这些目录的时候你会发现每个都不相同,所以在VS中也有对应每个SDK版本的名称,如:$(WindowsSdkDir_71A)lib,可以看到对应的版本不言而喻.
3.有时候我们为了我们的程序支持低版本操作系统,可以在General Configuration(图VS2012)中的Platfrom Toolset 如下设置:这时候会发现VS属性中Include Directories 和 Library Directories会自动回溯到以前的SDKS 路径,就是为了支持旧操作系统.

下载安装cmake生成不同的未编译解决方案或项目
可用于对源码选择不同的的编译器生成不同的解决方案文件,如使用vc12编译器生成后缀为sln的vs2013版解决方案,方案可以包含很多的项目,可以各自编译生成可执行文件如dll或者exe文件,具体操作可访问:
http://blog.csdn.net/poem_qianmo/article/details/21974023

OpenCV2:特征匹配及其优化
DescriptorMatcher是匹配特征向量的抽象类,在OpenCV2中的特征匹配方法都继承自该类(例如:BFmatcher,FlannBasedMatcher)。该类主要包含了两组匹配方法:图像对之间的匹配以及图像和一个图像集之间的匹配。
匹配过程中很可能发生错误的匹配,错误的匹配主要有两种:匹配的特征点事错误的,图像上的特征点无法匹配。常用的删除错误的匹配有
交叉过滤
如果第一幅图像的一个特征点和第二幅图像的一个特征点相匹配,则进行一个相反的检查,即将第二幅图像上的特征点与第一幅图像上相应特征点进行匹配,如果匹配成功,则认为这对匹配是正确的。

OpenCV中的BFMatcher已经包含了这种过滤   BFMatcher matcher(NORM_L2,true),在构造BFMatcher是将第二个参数设置为true。
比率测试 
KNNMatch,可设置K = 2 ,即对每个匹配返回两个最近邻描述符,仅当第一个匹配与第二个匹配之间的距离足够小时,才认为这是一个匹配。
在抽象基类DescriptorMatcher中封装了knnMatch方法
RANSIC方法计算--基础矩阵,并细化匹配结果
如果已经知道了两视图(图像)间的多个点的匹配,就可以进行基础矩阵F的计算了。OpenCV2中可以使用findFundamentalMat方法,其声明如下:
CV_EXPORTS_W Mat findFundamentalMat( InputArray points1, InputArray points2,
                                     int method=FM_RANSAC,
                                     double param1=3., double param2=0.99,
                                     OutputArray mask=noArray());

参数说明:points1,points2 两幅图像间相匹配的点,点的坐标要是浮点数(float或者double)第三个参数method是用来计算基础矩阵的具体方法,是一个枚举值。
param1,param2保持默认值即可。主要来说下mask参数,有N个匹配点用来计算基础矩阵,则该值有N个元素,每个元素的值为0或者1.值为0时,代表该匹配点事错误的匹配(离群值),只在使用RANSAC和LMeds方法时该值有效,可以使用该值来删除错误的匹配。http://www.cnblogs.com/wangguchangqing/p/4333873.html
同基础矩阵类似,得到匹配的特征点后也可以计算单应矩阵。
//! computes the best-fit perspective transformation mapping srcPoints to dstPoints.
CV_EXPORTS_W Mat findHomography( InputArray srcPoints, InputArray dstPoints,
                                 int method=0, double ransacReprojThreshold=3,
                                 OutputArray mask=noArray());
rcPoints,dstPoints是两视图中匹配的点;method 是计算单应矩阵所使用的方法,是一个枚举值。ransacReprojThreshold 是允许的最大反投影错误,只在使用RANSAC方法时有效。mask 同findFundamentalMat 类似,指出匹配的点是不是离群值,用来优化匹配结果。

OpenCV求两幅图像的单应性矩阵即透视变换矩阵
平面射影变换是关于其次3维矢量的一种线性变换,可以使用一个非奇异的3×3矩阵H表示,X′=HX,射影变换也叫做单应(Homography)。计算出两幅图像之间的单应矩阵H,那么应用这个关系可以将一个视图中的所有点变换到另一个视图中。
使用OpenCV可以调用库函数findHomography计算两幅图像的单应矩阵,其声明如下
Mat findHomography(InputArray srcPoints, InputArray dstPoints, int method=0, double ransacReprojThreshold=3, OutputArray mask=noArray() )
单应矩阵的计算需要两幅图像中相对应的点,srcPoints,dstPoints是两幅图像中相对应的点,可以是Vector或者是CV_32FC2类型的矩阵,Method是计算单应矩阵时所使用的方法。得到了图像的单应矩阵H就可以使用函数warpPerspective将图像变换到另一个视图中
void warpPerspective(InputArray src, OutputArray dst, InputArray M, Size dsize, int flags=INTER_LINEAR, int borderMode=BORDER_CONSTANT, const Scalar& borderValue=Scalar())
就使用上面提到的两个函数,计算出两幅图像之间的单应矩阵H,并且将两幅图像合并为一副图像。

opencv中的各种“矩阵”
在老版本的 opencv 中,矩阵主要用 c 的结构体实现,主要的几个结构体为:
cvArr (通用数组)、
cvMat (多通道二维矩阵)、
cvMatND (多通道多维稠密矩阵)、
cvSparseMat (多通道多维稀疏矩阵)、
IplImage (图片,二维矩阵,数据只能是1\2\3\4通道)、
CvSeq (序列)
CvSet (集合,派生于序列CvSet)
很多函数都是以cvArr*作为参数,这时可以将上面提到的各种类型的指针作为实参传递给函数。这里有两个问题需要搞清楚:
利用这三个构造函数可以分别将 cvMat、cvMatND、IplImage 类型的矩阵转化为 Mat 类型:
1./! converts old-style CvMat to the new matrix; the data is not copied by default  
2.Mat(const CvMat* m, bool copyData=false);  
3.//! converts old-style CvMatND to the new matrix; the data is not copied by default  
4.Mat(const CvMatND* m, bool copyData=false);  
5.//! converts old-style IplImage to the new matrix; the data is not copied by default  
6.Mat(const IplImage* img, bool copyData=false);  


新版本的opencv中,直接使用C++来实现数据结构和算法。opencv中定义了一个cv命名空间,在该命名空间中又定义了一个 Mat 类,这个类可以取代旧的结构体 cvMat 和 cvMatND。在新版本的opencv中,虽然保留了传统的 C 风格的接口,但内部实现已都变为C++ (这段代码来自Arithm.cpp,是个C++文件)。为了能在 C 代码中调用这些 CPP 文件中定义的函数,需要将CPP文件中定义的 C 风格的接口函数都用CV_IMPL声明,CV_IMPL其实就是 extern "C"。传统的C接口函数大都是以小写的"cv"开头。

特征检测器 FeatureDetector
OpenCV提供FeatureDetector实现特征检测及匹配,FeatureDetetor是虚类,通过定义FeatureDetector的对象可以使用多种特征检测方法。通过create()函数调用:
OpenCV 2.4.3提供了10种特征检测方法:
"FAST" – FastFeatureDetector   "STAR" – StarFeatureDetector
"SIFT" – SIFT (nonfree module)   "SURF" – SURF (nonfree module)
"ORB" – ORB  "MSER" – MSER  "GFTT" – GoodFeaturesToTrackDetector
"HARRIS" – GoodFeaturesToTrackDetector with Harris detector enabled
"Dense" – DenseFeatureDetector   "SimpleBlob" – SimpleBlobDetector
最常用的当属SIFT,尺度不变特征匹配算法(参考这里);以及后来发展起来的SURF,都可以看做较为复杂的块特征。这两个算法在OpenCV nonfree的模块里面,需要在附件引用项中添加opencv_nonfree243.lib,同时在代码中加入:initModule_nonfree(); 

初识仿射变换
仿射变换(Affine Transformation或 Affine Map),又称仿射映射,是指在几何中,一个向量空间进行一次线性变换并接上一个平移,变换为另一个向量空间的过程。它保持了二维图形的“平直性”(即:直线经过变换之后依然是直线)和“平行性”(即:二维图形之间的相对位置关系保持不变,平行线依然是平行线,且直线上点的位置顺序不变)。一个任意的仿射变换都能表示为乘以一个矩阵(线性变换)接着再加上一个向量(平移)的形式。那么, 我们能够用仿射变换来表示如下三种常见的变换形式:旋转-rotation (线性变换)、平移-translation(向量加)、缩放-scale(线性变换)
如果进行更深层次的理解,仿射变换代表的是两幅图之间的一种映射关系。
而我们通常使用2 x 3的矩阵来表示仿射变换。OpenCV仿射变换相关的函数一般涉及到warpAffine和getRotationMatrix2D这两个:
使用OpenCV函数warpAffine 来实现一些简单的重映射.
使用OpenCV函数getRotationMatrix2D 来获得旋转矩阵。
void warpAffine(InputArray src,OutputArray dst, InputArray M, Size dsize, int flags=INTER_LINEAR, intborderMode=BORDER_CONSTANT, const Scalar& borderValue=Scalar())
第一个参数,InputArray类型的src,输入图像,即源图像,填Mat类的对象即可。
第二个参数,OutputArray类型的dst,函数调用后的运算结果存在这里,需和源图片有一样的尺寸和类型。第三个参数,InputArray类型的M,2×3的变换矩阵。第四个参数,Size类型的dsize,表示输出图像的尺寸。第五个参数,int类型的flags,插值方法的标识符。此参数有默认值INTER_LINEAR(线性插值),可选的插值方式如下:
INTER_NEAREST - 最近邻插值
INTER_LINEAR - 线性插值(默认值)
INTER_AREA - 区域插值
INTER_CUBIC –三次样条插值
INTER_LANCZOS4 -Lanczos插值
CV_WARP_FILL_OUTLIERS - 填充所有输出图像的象素。如果部分象素落在输入图像的边界外,那么它们的值设定为 fillval.
CV_WARP_INVERSE_MAP –表示M为输出图像到输入图像的反变换,即 。因此可以直接用来做象素插值。否则, warpAffine函数从M矩阵得到反变换。
第六个参数,int类型的borderMode,边界像素模式,默认值为BORDER_CONSTANT。
第七个参数,const Scalar&类型的borderValue,在恒定的边界模式取的值,默认值为Scalar(),即0。

getRotationMatrix2D
Mat getRotationMatrix2D(Point2f center, double angle, double scale)  
第一个参数,Point2f类型的center,表示源图像的旋转中心。
第二个参数,double类型的angle,旋转角度。角度为正值表示向逆时针旋转(坐标原点是左上角)。
第三个参数,double类型的scale,缩放系数。

求得仿射变换对应的矩阵参数  
warpMat = getAffineTransform( srcTriangle, dstTriangle ); 两个参数是像素坐标数组返回变换矩阵参数解。下一步应用该参数求目标图像 
1.对源图像应用刚刚求得的仿射变换  
2.    warpAffine( srcImage, dstImage_warp, warpMat, dstImage_warp.size() ); 

 对图像进行缩放后再旋转  
    // 计算绕图像中点顺时针旋转50度缩放因子为0.6的旋转矩阵  
    Point center = Point( dstImage_warp.cols/2, dstImage_warp.rows/2 );  //图像中心坐标
    double angle = -50.0;  //角度为正值表示向逆时针旋转(坐标原点是左上角)
    double scale = 0.6;  //缩放系数
    // 通过上面的旋转细节信息求得旋转矩阵  
    rotMat = getRotationMatrix2D( center, angle, scale );  
    // 旋转已缩放后的图像  
    warpAffine( dstImage_warp, dstImage_warp_rotate, rotMat, dstImage_warp.size() );  

drawMatches函数详解
void drawMatches(const Mat& img1, constvector& keypoints1, const Mat& img2, constvector& keypoints2, constvector& matches1to2, Mat& outImg, const Scalar&matchColor=Scalar::all(-1),const Scalar&singlePointColor=Scalar::all(-1),  const vector<char>&matchesMask=vector<char>(),  intflags=DrawMatchesFlags::DEFAULT )  
第一个参数,const Mat&类型的img1,第一幅源图像。
第二个参数,const vector&类型的keypoints1,根据第一幅源图像得到的特征点,它是一个输出参数。
第三个参数,const Mat&类型的img2,第二幅源图像。
第四个参数,const vector&类型的keypoints2,根据第二幅源图像得到的特征点,它是一个输出参数。
第五个参数,matches1to2,第一幅图像到第二幅图像的匹配点,即表示每一个图1中的特征点都在图2中有一一对应的点、
第六个参数,Mat&类型的outImg,输出图像,其内容取决于第五个参数标识符falgs。
第七个参数,const Scalar&类型的matchColor,匹配的输出颜色,即线和关键点的颜色。它有默认值Scalar::all(-1),表示颜色是随机生成的。
第八个参数,const Scalar&类型的singlePointColor,单一特征点的颜色,它也有表示随机生成颜色的默认值Scalar::all(-1)。
第九个参数,matchesMask,确定哪些匹配是会绘制出来的掩膜,如果掩膜为空,表示所有匹配都进行绘制。
第十个参数,int类型的flags,特征绘制的标识符,有默认值DrawMatchesFlags::DEFAULT。可以在如下这个DrawMatchesFlags枚举结构体中选取值

BruteForceMatcher类继承于BFMatcher源码分析

D:\ProgramFiles(x86)\opencv\sources\modules\legacy\include\opencv2\legacy\legacy.hpp路径下,可以找到BruteForceMatcher类的定义,而BFMatcher类位于D:\Program Files(x86)\opencv\sources\modules\features2d\include\opencv2\features2d\features2d.hpp,发现BFMatcher公共其继承自DescriptorMatcher类,再次进行溯源,在D:\Program Files(x86)\opencv\sources\modules\features2d\include\opencv2\features2d\features2d.hpp路径下找到DescriptorMatcher类的定义。而DescriptorMatcher类继承于虚基类Algorithm。
可以发现,DescriptorMatcher类和之前我们讲到FeatureDetector 类和DescriptorExtractor类一样,都是继承自他们“德高望重的祖先”Algorithm基类的。BruteForceMatcher 定义为:
template  
class CV_EXPORTS BruteForceMatcher : publicBFMatcher  
{  
public:  
   BruteForceMatcher( Distance d = Distance() ) : BFMatcher(Distance::normType,false) {(void)d;}  
   virtual ~BruteForceMatcher() {}  
};  我们用BruteForceMatcher类时用到最多的match方法,是它从DescriptorMatcher类那里的“拿来主义”。定义如下:
//为各种描述符找到一个最佳的匹配(若掩膜为空)  
CV_WRAP void match( const Mat& queryDescriptors, const Mat&trainDescriptors,  
             CV_OUTvector& matches, const Mat& mask=Mat() ) const;  

SURF算法涉及的类介绍
OpenCV中关于SURF算法的部分,常常涉及到的是SURF、SurfFeatureDetector、SurfDescriptorExtractor这三个类,这一小节我们就来对他们进行人肉,挖挖其背景,看看他们究竟是什么来头。
在D:\Program Files (x86)\opencv\sources\modules\nonfree\include\opencv2\nonfree下的features2d.hpp头文件中,我们可以发现这样两句定义:typedef SURF SurfFeatureDetector;  
typedef SURF SurfDescriptorExtractor; typedef声明是为现有类型创建一个新的名字,类型别名。这就表示,SURF类忽然同时有了两个新名字SurfFeatureDetector以及SurfDescriptorExtractor。
也就是说,我们平常使用的SurfFeatureDetector类和SurfDescriptorExtractor类,其实就是SURF类,他们三者等价。SURF类公共继承自Feature2D类,我们再次进行转到,可以在路径d:\Program Files(x86)\opencv\build\include\opencv2\features2d\features2d.hpp看到Feature2D类的声明:
Feature2D类又是公共继承自FeatureDetector以及 DescriptorExtractor类,经常会用到的detect( )方法重载的两个原型,原来是SURF类经过两层的继承,从FeatureDetector类继承而来,同样,看看SURF类的另一个“爷爷”DescriptorExtractor类的声明。FeatureDetector 类和DescriptorExtractor类都虚继承自Algorithm基类。

 SURF特征匹配示例程序

程序利用了SURF特征的特征描述办法,其操作封装在类SurfFeatureDetector中,利用类内的detect函数可以检测出SURF特征的关键点,保存在vector容器中。第二步利用SurfDescriptorExtractor类进行特征向量的相关计算。将之前的vector变量变成向量矩阵形式保存在Mat中。最后强行匹配两幅图像的特征向量,利用了类BruteForceMatcher中的函数match
使用 DescriptorExtractor 接口来寻找关键点对应的特征向量。
使用 SurfDescriptorExtractor 以及它的函数 compute 来完成特定的计算。
使用 BruteForceMatcher 来匹配特征向量。
使用函数 drawMatches 来绘制检测到的匹配点。
关键点讲解:OpenCV2引入了一个通用类,用于提取不同的特征点描述子,计算如下:
//【4】计算描述子(特征向量)  
       SurfDescriptorExtractor extractor;  
       Mat descriptors1, descriptors2;  
       extractor.compute(srcImage1, keyPoint1, descriptors1 );  
       extractor.compute(srcImage2, keyPoints2, descriptors2 );  
里的结果为一个Mat矩阵,它的行数与特征点向量中元素个数是一致的。每行都是一个N维描述子的向量,比如SURF算法默认的描述子维度为64,该向量描绘了特征点周围的强度样式。两个特征点越相似,他们的特征向量也越靠近。这些描述子在图像匹配中尤其有用,如我们想匹配同一个场景中的两幅图像。首先,我们检测每幅图像中的特征,然后提取他们的描述子。第一幅图像中的每一个特征描述子向量都会与第二幅图中的描述子进行比较,得分最高的一对描述子,也就是两个向量的距离最近)将被视为那个特征的最佳匹配。该过程对于第一幅图像中的所有特征进行重复,这便是BruteForceMatcher中实行的最基本的策略。相关代码如下:
1.//【5】使用BruteForce进行匹配  
2.//实例化一个匹配器  
3.BruteForceMatcherfloat> > matcher;  
4.std::vector matches;  
5.//匹配两幅图中的描述子(descriptors)  
6.matcher.match(descriptors1, descriptors2, matches );  

BruteForceMatcher是由DescriptorMatcher派生出来的一个类,而DescriptorMatcher定义了不同的匹配策略的共同接口。调用match方法后,在其第三个参数输出一个cv::DMatch向量。于是我们定义一个std::vector类型的matches。
调用match方法之后,我们便可以使用drawMatches方法对匹配到的点进行绘制,并最终显示出来。相关代码如下:
1.//【6】绘制从两个图像中匹配出的关键点  
2.MatimgMatches;  
3.drawMatches(srcImage1, keyPoint1, srcImage2, keyPoints2, matches, imgMatches );//进行绘制  
4.//【7】显示效果图  
5.imshow("匹配图", imgMatches );  

矩阵数据mat中的私有size
//矩阵数据mat中的私有size返回值第一个代表矩阵横坐标width即x方向,第二个代表矩阵高height即y方向

drawKeypoints函数详解
使用函数 drawKeypoints 绘制检测到的关键点。
 void drawKeypoints(const Mat&image, const vector& keypoints, Mat& outImage, constScalar& color=Scalar::all(-1), int flags=DrawMatchesFlags::DEFAULT )  
第一个参数,const Mat&类型的src,输入图像。
第二个参数,const vector&类型的keypoints,根据源图像得到的特征点,它是一个输出参数。
第三个参数,Mat&类型的outImage,输出图像,其内容取决于第五个参数标识符falgs。
第四个参数,const Scalar&类型的color,关键点的颜色,有默认值Scalar::all(-1)。
第五个参数,int类型的flags,绘制关键点的特征标识符,有默认值DrawMatchesFlags::DEFAULT。可以在如下这个结构体中选取值。

stdafx.h
我们一般用TC或vc编译C程序的时候都要首先包含这个stdio.h头文件,这个头文件里面包含了scanf和printf函数的定义,如果我们不在程序开头include这个文件,那么你调用上面这两个函数就不会成功,它其实和c++中的iostream(iostream:这个就是1998年标准化以后的标准头文件;iostream.h: 这个就是标准化以前的头文件)文件的作用差不多的,它们一般都已经在stdafx.h文件中被包含。

定义一个burte force matcher对象
BruteForceMatcherfloat>>matcher;//定义一个burte force matcher对象
    vectormatches;
matcher.match(descriptors_1,descriptors_2,matches);

opencv读取图片并显示出来:
#include "stdafx.h"
#include 

using namespace cv;

int main(int argc,unsigned char* argv[])
{
    Mat img_src;
    for (;;)
    {
        img_src=imread("lena.jpg");
        imshow("lena_show",img_src);
        waitKey(30);
    }
    return 0;
}

opencv读取avi文件并显示出来:
注意有些avi格式的视频是读不出来的
#include "stdafx.h"
#include 

using namespace cv;

int main(int argc,unsigned char* argv[])
{
    Mat img_src;
    VideoCapture vido_file("tree.avi");
    for (;;)
    {
        vido_file >>img_src;
        imshow("video_src",img_src);//可以事先不用新建一个窗口
        char c=(char)waitKey(47);
        if (c==27)
        {
            break;    
        }
    }
    return 0;
}
opencv驱动摄像头并显示出来:
#include "stdafx.h"
#include 

using namespace cv;

int main(int argc,unsigned char* argv[])
{
    Mat img_src;
    VideoCapture cam(0);
    for (;;)
    {
        cam >>img_src;
        imshow("camera",img_src);//可以事先不用新建一个窗口
        char c=(char)waitKey(30);
        if (c==27)
        {
            break;    
        }
    }
    return 0;
}

opencv打开摄像头并对摄像头内视频进行canny边缘检测。
// cam_test.cpp : 定义控制台应用程序的入口点。
//

#include "stdafx.h"
#include 
#include 
#include 
#include 

#pragma comment( lib, "opencv_core242.lib" )
#pragma comment( lib, "opencv_highgui242.lib" )
#pragma comment( lib, "opencv_imgproc242.lib" )

using namespace cv;
using namespace std;

int main( int argc, const char **argv )
{

    VideoCapture cap(0); // open the default camera
    if(!cap.isOpened()) // check if we succeeded
        return -1;
    Mat edges;
    namedWindow("edges",1);
    for(;;)
    {
        Mat frame;
        cap >> frame; // get a new frame from camera
        cvtColor(frame, edges, CV_BGR2GRAY);
        GaussianBlur(edges, edges, Size(7,7), 1.5, 1.5);
        Canny(edges, edges, 0, 30, 3);
        imshow("edges", edges);
        if(waitKey(30) >= 0) break;
    }
    // the camera will be deinitialized automatically in VideoCapture destructor
    return 0;
}

实现鼠标选定矩形框和怎样读取一帧图像
功能:打开摄像头,捕捉视频图像,用鼠标选定视频区域(支持各种选择习惯)。
#include "stdafx.h"
#include "opencv2/imgproc/imgproc.hpp"
#include 
#include 
#include 
#include 

using namespace cv;
using namespace std;
Rect select;
bool select_flag=false;
Point origin;
Mat frame;

/************************************************************************************************************************/
/****                    如果采用这个onMouse()函数的话,则只能画出从左上到右下,或者从右下到左上的矩形框                    ****/
/************************************************************************************************************************/
//void onMouse(int event,int x,int y,int,void*)
//{
//    if(event==CV_EVENT_LBUTTONDOWN)
//    {
//        select.x=x;
//        select.y=y;
//        tracking=false;
//    }
//    else if(event==CV_EVENT_LBUTTONUP)
//    {
//        select.width=x-select.x;//以下2行计算出来的值要么都大于0,要么都小于0
//        select.height=y-select.y;
//        tracking=true;//左键完后,开始跟踪
//    }
//}

/************************************************************************************************************************/
/****                            如果采用这个onMouse()函数的话,则可以画出鼠标拖动矩形框的4种情形                        ****/
/************************************************************************************************************************/
void onMouse(int event,int x,int y,int,void*)
{
    //Point origin;//不能在这个地方进行定义,因为这是基于消息响应的函数,执行完后origin就释放了,所以达不到效果。
    if(select_flag)
    {
        select.x=MIN(origin.x,x);//不一定要等鼠标弹起才计算矩形框,而应该在鼠标按下开始到弹起这段时间实时计算所选矩形框
        select.y=MIN(origin.y,y);
        select.width=abs(x-origin.x);//算矩形宽度和高度
        select.height=abs(y-origin.y);
        select&=Rect(0,0,frame.cols,frame.rows);//保证所选矩形框在视频显示区域之内
    }
    if(event==CV_EVENT_LBUTTONDOWN)
    {
        select_flag=true;//鼠标按下的标志赋真值
        origin=Point(x,y);//保存下来单击是捕捉到的点
        select=Rect(x,y,0,0);//这里一定要初始化,宽和高为(0,0)是因为在opencv中Rect矩形框类内的点是包含左上角那个点的,但是不含右下角那个点
    }
    else if(event==CV_EVENT_LBUTTONUP)
    {
        select_flag=false;
    }
}

int main(int argc, unsigned char* argv[])
{
    char c;

    //打开摄像头
    VideoCapture cam(0);
    if (!cam.isOpened())
        return -1;

    //建立窗口
    namedWindow("camera",1);//显示视频原图像的窗口

    //捕捉鼠标
    setMouseCallback("camera",onMouse,0);

    while(1)
    {
        //读取一帧图像
        cam>>frame;
        if(frame.empty())
            return -1;

        //画出矩形框
        rectangle(frame,select,Scalar(255,0,0),3,8,0);//能够实时显示在画矩形窗口时的痕迹

        //显示视频图片到窗口
        imshow("camera",frame);

    //    select.zeros();
        //键盘响应
        c=(char)waitKey(20);
        if(27==c)//ESC键
            return -1;
    }

    return 0;
}

imwrite()函数的具体用法。
  bool imwrite(const string& filename, InputArray img, const vector<int>& params=vector<int>() )
  该函数是把程序中的Mat类型的矩阵保存为图像到指定位置。参数filename为所需保存图像的文件目录和文件名。这里的文件名需要带有图像格式后缀的,目前OpenCV该函数只支持JPEG,PNG,PPM,PGM,PBM,TIFF等。并不是所有Mat类型都支持。img参数为图像数据来源,其类型为Mat。注意也不是所有格式的Mat型数据都能被使用保存为图片,目前OpenCV主要只支持单通道和3通道的图像,并且此时要求其深度为8bit和16bit无符号(即CV_16U)。所以其他一些数据类型是不支持的,比如说float型等。如果Mat类型数据的深度和通道数不满足上面的要求,则需要使用convertTo()函数和cvtColor()函数来进行转换。convertTo()函数负责转换数据类型不同的Mat,即可以将类似float型的Mat转换到imwrite()函数能够接受的类型。而cvtColor()函数是负责转换不同通道的Mat,因为该函数的第4个参数就可以设置目的Mat数据的通道数(只是我们一般没有用到它,一般情况下这个函数是用来进行色彩空间转换的)。另外也可以不用imwrite()函数来存图片数据,可以直接用通用的XML IO接口函数将数据存在XML或者YXML中。
  参数params是用来设置对应图片格式的参数的,因为一般情况下这些图片格式都是经过了压缩的,这里就是设置这些压缩参数来控制图片的质量。该参数是一个vector<int>类型,里面分别存入paramId_1, paramValue_1, paramId_2, paramValue_2, ... 也就是说存入一对属性值。如果不设置该参数的话,则程序会自动根据所保存的图像格式采用一个默认的参数。
  本实验中还用到了下面这个函数。
  template<...> _Tp saturate_cast(_Tp2 v)
  将参数v转换成模板中的类型,比如说:
  uchar a = saturate_cast(-100); 
//c版本中的保存图片为cvSaveImage()函数,c++版本中直接与matlab的相似,imwrite()函数。

坐标系、Mat::at(x,y)和Mat::at(Point(x, y))的区别
本次实验通过一个简短的例子,主要来说明下面4个问题:
  1. 坐标体系中的零点坐标为图片的左上角,X轴为图像矩形的上面那条水平线;Y轴为图像矩形左边的那条垂直线。该坐标体系在诸如结构体Mat,Rect,Point中都是适用的。(虽然网上有学着说OpenCV中有些数据结构的坐标原点是在图片的左下角,但是我暂时还没碰到过)。
  2. 在使用image.at(x1, x2)来访问图像中点的值的时候,x1并不是图片中对应点的x轴坐标,而是图片中对应点的y坐标。因此其访问的结果其实是访问image图像中的Point(x2, x1)点,即与image.at(Point(x2, x1))效果相同。
  3. 如果所画图像是多通道的,比如说image图像的通道数时n,则使用Mat::at(x, y)时,其x的范围依旧是0到image的height,而y的取值范围则是0到image的width乘以n,因为这个时候是有n个通道,所以每个像素需要占有n列。但是如果在同样的情况下,使用Mat::at(point)来访问的话,则这时候可以不用考虑通道的个数,因为你要赋值给获取Mat::at(point)的值时,都不是一个数字,而是一个对应的n维向量。
4. 多通道图像在使用minMaxLoc()函数是不能给出其最大最小值坐标的,因为每个像素点其实有多个坐标,所以是不会给出的。因此在编程时,这2个位置应该给NULL。

opencv中c版本和c++版本区别

1.  显示窗口大小的改变方法不同。
在c版本中,定义一个窗口时用cvNamedWindow.比如说cvNamedWindow(“src”,0);后面的参数为0表示窗口大小可以手动改变,否则窗口的大小是自适应图片大小的。而在c++版本中定义一个窗口用namedWindow. 比如说namedWindow(“src”,1);不管后面第二个参数是多少,都不能手动更改窗口的大小,因为它的尺寸是根据图片大小自动生成的。并且要看到手动调整窗口大小的效果,还需要配合cvShowImage(“src”,img);也就是说如果后面显示用c++版本的imshow(“src”,img);也是看不到手动调整图片大小的效果的。
2.  显示图片的函数不同。
在opencv的c版本中,显示图片用cvShowImage;比如说,cvShowImage(“src”,img);并且这里的img是IplImage*类型,所以如果你定义的img是Mat类型的话就用不了,因为程序不能自动将Mat类型转换成IplImage*类型。在opencv的c++版本中,显示图片用的是imshow;
比如说,imshow(“src”,img);当然这里的img就是Mat类型了。也就是说如果这里的img用IplImage*就不行了,程序不能自动将IplImage*转换成Mat类型。通过上面2点应该注意到,如果要手动改变图片的尺寸,就必须cvNamedWidow(“src”,0);cvShowImage(“src”,img)一起用。其中img是IplImage*类型。 当然cvNamedWindow(“src”,0)也可以imshow(“src”,img)一起用,其中img是Mat类型,但是这样达不到自动改变窗口大小的目的。因为一般情况下,namedWindow(“src”,1)和imshow(“src”,img)一起用的。而Mat结构体包含一个Mat头部(头部中记录的是矩阵的大小,存储方式等等)和一个指向矩阵的指针。所以2者还是有很大区别的,所以说程序不能自动将他们转换是有原因的,因为Mat结构更加复杂。
3. 读取图片的方式不同。
在c版本中读取图片用的是cvLoadImage;比如说cvLoadImage(“lena.jpg”);在c++版本中,读取图片用的是imread;比如说imread(“lena.jpg”);当然这2种的lena图片都是放入工程目录下的。但是这里有一点非常不同的是,cvLoadImage()中的参数为const char*类型,而imread()中的参数是const &string型,这两种是完全不同的,在opencv中也不能自动转换好他们,混合使用c和c++函数一起编写opencv代码时要小心。
4. 路径表示方式不同。
在c版本中路径之间用2个双右斜线,即”//”。用1个斜线”/”会报错。在c++版本中路径之间用1个或者2个甚至更多个斜线都是可以的。

Opencv编程注意事项:
1.  得到Mat类型img的size,可以使用函数img.size(),注意这里有括号。但是在需得到img的行和列时,不需要使用括号,即使用img.rows和img.cols. 
2. 已经定义好了img为Mat型,但是没有初始化,在后面程序的任何一个位置可以使用下面的代码初始化img,比如img=Mat(***1,***2),其中***1是矩形区域的大小,***2是数组内数据类型。 
3. 在opencv中像素点的数据类型能找到对应类似charintfloat,double的,比如说是分别为CV_8UC1,CV_16UC1,CV_32FC1, CV_64FC1,一定要注意是没有这一说法的CV_16FC1。
4. 在对opencv工程进行debug时,如果想查看Mat型内部的数据,直接在变量监视器里面看是不行的,且观察时其内部还有可能报错。最好是将Mat类型数据cout到屏幕终端观察。
5. 将一个vector类型的变量转换成一个Mat型变量,可以采用下面的方法: Vector& c=contours(0);Mat cnew;Cnew=Mat(c);    
6.Scalar与CvScalar类似,都是标量,用于存储像素值的,有4个通道,每个通道可存double型,其内部定义为:
  typedef  struct CvScalar
   {   
      double val[4];
  }CvScalar; 
7.当图像像素值为hsv空间时,hsv中3个分量的取值范围为[0 180],[0 255],[0,255].
8.opencv中的NAN表示是非数字,即Not A Number,INF表示无穷大,比如+INF:正无穷,-INF:负无穷。 
9.RGB空间是类似人眼工作机制,被各种显示设备采用。HSV,HLS是描述颜色更自然的方法,去掉最后一个元素可以使算法对光照不敏感。YCrCb在jpeg中广泛使用。Lab是在感知上比较均匀的颜色空间,适合度量2个颜色之间的距离。
10.在使用zeros函数时,如果是用这个zeros(int rows, int cols, int type),注意第一个参数为rows,即图像的高度。如果是用zeros(Size size, int type)而size又为Size(cols, rows),其第一个参数为cols,即图像的宽度。即Mat大小先是高度然后是宽度,而size大小显示宽度然后是高度。
11.  Size wholeSize;Point roiofs;img.locateROI(wholeSize, roiofs); 
locateROI在此处是如果img图像是从其它父图像中某一部分得来的,那么其父图像221 的大小尺寸就为whleSize了,img图像左上角那个点相对于父图像的左上角位置就为点roiofs了。
12. 在opencv学习中,经常会读一些基于1.0版本的opencv代码,如果我们需要在2.0以后版本中使用已有的代码,遇到的问题最多的就是Mat和IplImage*之间的转换,现在举个例子来简单说明一下这2者之间的转换。
    假设已经定义: Mat src;
            IplImage* img;
       并且也假设src或者img中已经有了数据。则如果需要将src转换到img,应该使用下面的语句:
    img = &src.operator IplImage();
    如果是将img转换到src,则直接使用下面的语句:
    src = Mat(img);
    这是opencv基于c版本和c++版本混合编程的处理方法
13. 在opencv的core.hpp里面有AutoBuffer<>()函数,该函数为自动分配一段指定大小的内存,并且可以指定内存中数据的类型。
14.调用Mat::copyTo()函数时,如果需要有mask操作,则不管源图像是多少通道的,其mask矩阵都要定义为单通道,另外可以对一个mask矩阵画一个填充的矩形来达到使mask矩阵中对应ROI的位置的值为设定值,这样就不需要去一一扫描赋值了。
15. 在使用OpenCV的Mat矩阵且需要对该矩阵进行扫描时,一定要注意其取值顺序,比如说列和行的顺序,如果弄反了,则经常会报内存错误。
16. 在使用OpenCV内部的判断条件时应该使用CV_Assert()函数,而不是CV_ASSERT()。
17. 通过实验测试发现,虽然经过calcHist()函数计算过后的直方图保存在hist中,这里hist是一个Mat类型,并且如果计算的是一维的直方图的话,则hist是一个列向量。
18. 当Mat中数据的类型为CV_16UC1的时候,这里的16U并不是指unsigned int,而是指的是unsigned short int,因为在OpenCV框架中,int不是16位的,而是32位的。没想到我使用OpenCV一年了,今天才弄清楚这个。
19. 坐标体系中的零点坐标为图片的左上角,X轴为图像矩形的上面那条水平线;Y轴为图像矩形左边的那条垂直线。该坐标体系在诸如结构体Mat,Rect,Point中都是适用的。(虽然网上有学着说OpenCV中有些数据结构的坐标原点是在图片的左下角,但是我暂时还没碰到过)。
20. 在使用image.at(x1, x2)来访问图像中点的值的时候,x1并不是图片中对应点的x轴坐标,而是图片中对应点的y坐标。因此其访问的结果其实是访问image图像中的Point(x2, x1)点,即与image.at(Point(x2, x1))效果相同。
21. 如果所画图像是多通道的,比如说image图像的通道数时n,则使用Mat::at(x, y)时,其x的范围依旧是0到image的height,而y的取值范围则是0到image的width乘以n,因为这个时候是有n个通道,所以每个像素需要占有n列。但是如果在同样的情况下,使用Mat::at(point)来访问的话,则这时候可以不用考虑通道的个数,因为你要赋值给获取Mat::at(point)的值时,都不是一个数字,而是一个对应的n维向量。
22. 多通道图像在使用minMaxLoc()函数是不能给出其最大最小值坐标的,因为每个像素点其实有多个坐标,所以是不会给出的。因此在编程时,这2个位置应该给NULL。

你可能感兴趣的:(OpenCV,opencv学习系列,编程,opencv,visual,studio,学习总结)