上一篇我们下载安装了opencv,并且运行了第一个示例程序。接下来,我们剖析下该示例程序。
一、 示例代码来源
上一篇指出,opencv下载并解压后,目录下会产生两个目录
其中,”build”目录主要包含了我们程序开发和运行时所需要的头文件(.h)和库文件(.lib/.dll)。
”source”目录下,包含了项目源码、示例源码、编译相关的数据和文档。
其中”sample”目录中,提供了一定的示例的代码。
我们上一篇所运行的程序示例,就是这个”samples”目录下的facedetect.cpp。
该示例程序用于打开默认摄像头,捕捉图像,定位人脸,以及双眼部位。
二、 修改opencv头文件
*
如果想正常运行该程序,还需要做些修改
如上图所示,源文件中,需要包含一大堆的头文件,在新版的opencv中,只需包含”opencv.hpp”就可以了,原因很简单,这个头文件直接或间接包含了上述所使用到的头文件。
事实上,新版的opencv只提供了这一个头文件,所以几乎所有的opencv开发,都只需包含这一个头文件——“<opencv2/opencv.hpp>”。
注意:第三行加入了一句“#define _CRT_SECURE_NO_WARNINGS”,用于消除一些过期函数的错误提示。否则,会因为一些诸如“fopen”等函数报错而无法通过编译。
三、 使用opencv域名
使用域名很简单,就是如果你不想每次调用opencv相关函数时,都要在前面加上”cv::”,那么就把上述域名使用的声明添加上去。
四、 构造主函数,确定该程序运行时所需要的参数
程序看起来很复杂。实际上就是从程序入口“main”函数,传入一些参数,然后程序根据这些参数,确定“人脸模板”和“眼部模板”的位置,用于定位视频图像中的人脸和眼部。
所以,这个main函数,从一开始,就声明,需要传入的参数。
接下来,在主函数之前声明两个全局变量,字符串类型,用来接收和在函数间传递“人脸模板”路径,“眼部模板”路径。
最后,在主程序内部。解析参数,获取两个变量的值
这里用到一个重要的类,命令行解析
类:cv::CommandLineParser(这里”cv::”可省去)
(一)构造该类对象parser:
-
需要传递从main中获取的argv,argc,并待后续从中提取各个“参数—值”的键值对。
-
需要传递参数列表及其默认值。参数列表决定了,我们从argv中需要包含哪些参数信息(比如该程序需要参数“help”,“cascade”,“nested-cascade”,“scale)。
-
另外在argv中解析不到相应参数时(比如用户没有输入’cascade”参数值)时,默认参数值(比如“data/haarcascades/haarcascade_frontalface_alt.xml”)来代替。
(二)利用parser对象解析参数
- 在解析参数之前,我们应首先判断一下用户是否是需要调用帮助文档,如果是,那么并不真正执行程序,而是显示帮助文档内容,因此并不需要再往下解析参数
- parser对象的get函数,传入参数名,返回参数值。
- 继续解析,利用parser的check函数检查有无丢失参数等错误。
五、最后今天就解析这里,在此可以把本篇代码简化为以下
编译后,在命令行中运行该程序,可以检测参数的输入与解析
// face1.cpp : 此文件包含 "main" 函数。程序执行将在此处开始并结束。
//
#define _CRT_SECURE_NO_WARNINGS
#include <iostream>
#include <opencv2/opencv.hpp>
using namespace std;
using namespace cv;
string cascadeName;
string nestedCascadeName;
int main(int argc, const char** argv)
{
string inputName;
bool tryflip;
double scale;
CommandLineParser parser(argc, argv,
"{help h||}"
"{cascade|data/haarcascades/haarcascade_frontalface_alt.xml|}"
"{nested-cascade|data/haarcascades/smile.xml|}"
"{scale|1|}{try-flip||}{@filename||}"
);
if (parser.has("help"))
{
return 0;
}
cascadeName = parser.get<string>("cascade");
nestedCascadeName = parser.get<string>("nested-cascade");
scale = parser.get<double>("scale");
if (scale < 1)
scale = 1;
tryflip = parser.has("try-flip");
inputName = parser.get<string>("@filename");
if (!parser.check())
{
parser.printErrors();
return 0;
}
cout << "该程序接收参数如下:" << endl;
cout << "cascadeName:" << cascadeName << endl;
cout << "nestedCascadeName:" << nestedCascadeName << endl;
cout << "scale:" << scale << endl;
cout << "tryflip:" << tryflip << endl;
cout << "inputName:" << inputName << endl;
return 0;
}
没有输入参数情况下
用户指定inputName情况下
六:练习,大家可以修改此代码,试图在用户输入参数“-h”时显示帮助文档。