基于ncnn框架搭建MTCNN人脸检测工程

1、下载git

2、在桌面新建文件夹test,进入该文件夹,鼠标右击选择Git Bash Here

3、在弹出的命令行窗口中输入以下指令,将该仓从github上下载下来

git clone https://github.com/moli232777144/mtcnn_ncnn.git

4、继续输入以下指令更新子模块

git submodule update --init

5、双击tools文件夹下的protobuf.bat文件进行编译。protobuf.bat文件内容如下图

在vs2015中打开./3rdparty/src/protobuf/cmake/build下的protobuf.sln工程,编译Debug及Release版本(即重新生成解决方案);

再调用tools下的copyProtobuf.bat文件,生成protobuf的依赖库到第三方公共文件夹3rdparty下。

6、修改tools下的ncnn.bat工具,将DProtobuf几个参数(下图红色部分)替换为自己对应的目录

 运行ncnn.bat文件后,在3rdparty/src/ncnn/build会生成一个ncnn.sln工程,用vs2015打开编译Realease版本;编译后,build目录下生成的src中包含ncnn.lib库,tools里有caffe以及mxnet的转换工具; 为了以后方便使用,运行下tools里的copyNcnn.bat文件,统一放到公共目录3rdparty目录下,主要移动了caffe2ncnn的exe文件,ncnn的lib库及.h头文件;

7、将caffe模型转为ncnn模型

调用格式:caffe2ncnn.exe xx.prototxt xx.caffemodel xx.param xx.bin

或者直接启动tools下的mtcnn2ncnn.bat脚本,即可将mtcnn目录下的model文件都转化为ncnn模型存储方式(如果项目存在旧版prototxt需使用caffe项目中的upgrade_net_proto_text与upgrade_net_proto_binary进行新版转换)。

本仓里面的caffe模型是旧版的,下载https://github.com/ElegantGod/ncnn该仓,将该仓里面ncnn/mtcnn里面三个网络的caffemodel和prototxt文件(新版的)覆盖本仓model下相对应的文件。另外,mtcnn模型是caffe+matlab训练的,生成的是col-major模型,与ncnn模型默认的row-major不匹配,须将该仓tools下的caffe2ncnn.cpp覆盖本仓3rdparty\src\ncnn\tools\caffe下的同名文件。重新执行上面模型转换指令,即直接运行tools下的mtcnn2ncnn.bat脚本。

8、建立VS工程

生成正确的ncnn模型后,主要就是建立vs工程进行调试,可以通过vs新建工程,添加包含目录导入3rdparty的opencv及ncnn头文件目录,接着在链接器里添加两者的lib库引用;

当然为了更好地学习脚本,个人仍采用cmake的构建方式,执行tools下的ncnnBuild.bat脚本创建vs2015工程。打开vs2015目录下的mtcnn_ncnn.sln,编译Rlease版本;将Release下生成的可执行文件拷贝到当前工程目录下,缺少的dll文件可在3rdpatry的bin目录找到也拷贝到里面。双击可执行文件即可执行。

扩展

如果想将facenet也接入mtcnn_ncnn框架,实现人脸检测识别一体化。下载该仓https://github.com/tongxiaobin/ncnn-mtcnn-facenet,其MacOS文件夹下的models里面有facenet预训练好的模型二进制参数文件.bin和配置文件.parm。其MacOS文件夹下的src里有相关人脸识别源文件,facenet的主要识别函数为main.cpp里的test_facenet函数。在上面mtcnn_ncnn框架中加入该函数进行相应修改即可。

以下是我对应修改的:

void test_facenet() {
	//加载facenet
	const char *model_path = "../models";
	Recognize recognize(model_path);

	//计算人脸库特征
	ifstream file("path.txt");
	vector<string>name;
	vector<vector<float>>feature;
	char separator = ';';
	string line, path, classname;
	while (getline(file, line))
	{
		stringstream lines(line);
		getline(lines, path, separator);
		getline(lines, classname);
		name.push_back(classname);

		cv::Mat sample_img = cv::imread(path, CV_LOAD_IMAGE_COLOR);
		std::vector<float> samplefea;
		recognize.start(sample_img, samplefea);
		feature.push_back(samplefea);		
	}


	//加载mtcnn
	MTCNN mtcnn(model_path);
	mtcnn.SetMinFace(40);

	//open camera
	cv::VideoCapture mVideoCapture(0);
	if (!mVideoCapture.isOpened()) {
		return;
	}
	cv::Mat frame;
	mVideoCapture >> frame;
	while (!frame.empty()) {
		mVideoCapture >> frame;
		if (frame.empty()) {
			break;
		}

		//计时开始
		clock_t start_time = clock();
		ncnn::Mat ncnn_img = ncnn::Mat::from_pixels(frame.data, ncnn::Mat::PIXEL_BGR2RGB, frame.cols, frame.rows);
		std::vector<Bbox> finalBbox;
		
#if(MAXFACEOPEN==1)
		mtcnn.detectMaxFace(ncnn_img, finalBbox);
#else
		mtcnn.detect(ncnn_img, finalBbox);
#endif
		const int num_box = finalBbox.size();
		std::vector<cv::Rect> bbox;
		bbox.resize(num_box);


		//画出人脸框及其五个关键点
		for(int i = 0; i < num_box; i++){
		bbox[i] = cv::Rect(finalBbox[i].x1, finalBbox[i].y1, finalBbox[i].x2 - finalBbox[i].x1 + 1, finalBbox[i].y2 - finalBbox[i].y1 + 1);
		rectangle(frame, bbox[i], Scalar(0, 0, 255), 2, 8, 0);
		for (int j = 0; j<5; j = j + 1)
		{
		cv::circle(frame, cvPoint(finalBbox[i].ppoint[j], finalBbox[i].ppoint[j + 5]), 2, CV_RGB(0, 255, 0), CV_FILLED);
		}
		}


		//计算相似度
		if (num_box <= 0)continue;
		for (int i = 0; i < num_box; i++) {
			cv::Rect r = Rect(finalBbox[i].x1, finalBbox[i].y1, finalBbox[i].x2 - finalBbox[i].x1 + 1, finalBbox[i].y2 - finalBbox[i].y1 + 1);
			cv::Mat ROI(frame, r);

			cv::Mat croppedImage;
			std::vector<float> croppedfea;

			// Copy the data into new matrix
			ROI.copyTo(croppedImage);
			recognize.start(croppedImage, croppedfea);

			//计算最佳匹配
			double max = 0.0;
			int k = 0;
			for (int i = 0; i < feature.size(); i++)
			{
				double similar = calculSimilar(feature[i], croppedfea);
				
				if (similar > max)
				{
					max = similar;
					k = i;
				}				
			}		

		
			//显示结果
			if (max > 0.65) {
				cout << "similarity:" << max << endl;
				//rectangle(frame, r, Scalar(0, 0, 255), 2, 8, 0);
				putTextZH(frame, name[k].c_str(), Point(r.x+r.width/2-30, r.y), Scalar(0, 255, 0),18);
				//putText(frame, name[k], Point(r.x, r.y), FONT_HERSHEY_SIMPLEX, 1, Scalar(0, 255, 0), 1, 8);
			}
			else {
				for (int j = 0; j<5; j = j + 1)
				{
					//cv::circle(frame, cvPoint(finalBbox[i].ppoint[j], finalBbox[i].ppoint[j + 5]), 2, CV_RGB(0, 255, 0), CV_FILLED);//显示五官
					rectangle(frame, r, Scalar(0, 0, 255), 2, 8, 0);
					putTextZH(frame, "未注册", Point(r.x+r.width/2-30, r.y), Scalar(0, 255, 0), 18);
					//putText(frame, "error", Point(r.x, r.y), FONT_HERSHEY_SIMPLEX, 1, Scalar(0, 255, 0), 1, 8);
				}
			}
		}

		clock_t finish_time = clock();
		double total_time = (double)(finish_time - start_time) / CLOCKS_PER_SEC;
		//std::cout << "time" << total_time * 1000 << "ms" << std::endl;

		imshow("face_detection", frame);
		if (cv::waitKey(10) == 27) {
			break;
		}
	}
	return;
}

猜你喜欢

转载自blog.csdn.net/qq_24946843/article/details/90905009