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");
vectorname;
vector>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 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 finalBbox;
#if(MAXFACEOPEN==1)
mtcnn.detectMaxFace(ncnn_img, finalBbox);
#else
mtcnn.detect(ncnn_img, finalBbox);
#endif
const int num_box = finalBbox.size();
std::vector 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 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;
}