ZQCNN亦是一款开源人脸检测、特征点定位的优秀代码库,其基于MTCNN算法构建。
cpu 25ms,侧脸,低头都能检测到,精度不是很高,人脸框不是特别稳。
以下内容主要来自:
https://github.com/zuoqing1988/ZQCNN-MTCNN-vs-libfacedetection
ZQCNN-MTCNN-vs-libfacedetection
依赖库:
windows:mkl, opencv3.4.2
arm-linux: openblas, opencv3.4.2
libfacedetection:下载时间2019-03-25 09:00
该库无任何依赖。点赞~
mtcnn测试代码:
MKL 拷贝dll即可运行。
200多ms,人脸框不稳定,关键点也不是特别准。
#include "ZQ_CNN_Net.h"
#include "ZQ_CNN_MTCNN_old.h"
#include "ZQ_CNN_MTCNN.h"
#include <vector>
#include <iostream>
#include "opencv2/opencv.hpp"
#include "ZQ_CNN_CompileConfig.h"
#include <opencv2\imgproc\types_c.h>
#if ZQ_CNN_USE_BLAS_GEMM
#if __ARM_NEON
#include <openblas/cblas.h>
#else
#include <openblas/cblas.h>
#pragma comment(lib,"libopenblas.lib")
#endif
#elif ZQ_CNN_USE_MKL_GEMM
#include "mkl/mkl.h"
#pragma comment(lib,"mklml.lib")
#else
#pragma comment(lib,"ZQ_GEMM.lib")
#endif
using namespace ZQ;
using namespace std;
using namespace cv;
static void Draw(cv::Mat &image, const std::vector<ZQ_CNN_BBox>& thirdBbox)
{
std::vector<ZQ_CNN_BBox>::const_iterator it = thirdBbox.begin();
for (; it != thirdBbox.end(); it++)
{
if ((*it).exist)
{
if (it->score > 0.7)
{
cv::rectangle(image, cv::Point((*it).col1, (*it).row1), cv::Point((*it).col2, (*it).row2), cv::Scalar(0, 0, 255), 2, 8, 0);
}
else
{
cv::rectangle(image, cv::Point((*it).col1, (*it).row1), cv::Point((*it).col2, (*it).row2), cv::Scalar(0, 255, 0), 2, 8, 0);
}
for (int num = 0; num < 5; num++)
circle(image, cv::Point(*(it->ppoint + num) + 0.5f, *(it->ppoint + num + 5) + 0.5f), 1, cv::Scalar(0, 255, 255), -1);
}
else
{
printf("not exist!\n");
}
}
}
static void Draw(cv::Mat &image, const std::vector<ZQ_CNN_BBox106>& thirdBbox)
{
std::vector<ZQ_CNN_BBox106>::const_iterator it = thirdBbox.begin();
for (; it != thirdBbox.end(); it++)
{
if ((*it).exist)
{
if (it->score > 0.7)
{
cv::rectangle(image, cv::Point((*it).col1, (*it).row1), cv::Point((*it).col2, (*it).row2), cv::Scalar(0, 0, 255), 2, 8, 0);
}
else
{
cv::rectangle(image, cv::Point((*it).col1, (*it).row1), cv::Point((*it).col2, (*it).row2), cv::Scalar(0, 255, 0), 2, 8, 0);
}
for (int num = 0; num < 106; num++)
circle(image, cv::Point(*(it->ppoint + num * 2) + 0.5f, *(it->ppoint + num * 2 + 1) + 0.5f), 1, cv::Scalar(0, 255, 255), -1);
}
else
{
printf("not exist!\n");
}
}
}
int main()
{
int num_threads = 1;
#if ZQ_CNN_USE_BLAS_GEMM
printf("set openblas thread_num = %d\n",num_threads);
openblas_set_num_threads(num_threads);
#elif ZQ_CNN_USE_MKL_GEMM
mkl_set_num_threads(num_threads);
#endif
VideoCapture cap = VideoCapture(0);
if (!cap.isOpened())//如果视频不能正常打开则返回
return 1;
Mat image0;
while (1)
{
cap >> image0;//等价于cap.read(frame);
if (image0.empty())//如果某帧为空则退出循环
break;
//cv::resize(image0, image0, cv::Size(), 2, 2);
if (image0.channels() == 1)
cv::cvtColor(image0, image0, CV_GRAY2BGR);
//cv::convertScaleAbs(image0, image0, 2.0);
/* TIPS: when finding tiny faces for very big image, gaussian blur is very useful for Pnet*/
bool run_blur = true;
int kernel_size = 3, sigma = 2;
if (image0.cols * image0.rows >= 2500 * 1600)
{
run_blur = false;
kernel_size = 5;
sigma = 3;
}
else if (image0.cols * image0.rows >= 1920 * 1080)
{
run_blur = false;
kernel_size = 3;
sigma = 2;
}
else
{
run_blur = false;
}
if (run_blur)
{
cv::Mat blur_image0;
int nBlurIters = 1000;
double t00 = omp_get_wtime();
for (int i = 0; i < nBlurIters; i++)
cv::GaussianBlur(image0, blur_image0, cv::Size(kernel_size, kernel_size), sigma, sigma);
double t01 = omp_get_wtime();
printf("[%d] blur cost %.3f secs, 1 blur costs %.3f ms\n", nBlurIters, t01 - t00, 1000 * (t01 - t00) / nBlurIters);
cv::GaussianBlur(image0, image0, cv::Size(kernel_size, kernel_size), sigma, sigma);
}
std::vector<ZQ_CNN_BBox> thirdBbox;
std::vector<ZQ_CNN_BBox106> thirdBbox106;
ZQ_CNN_MTCNN mtcnn;
std::string result_name;
mtcnn.TurnOnShowDebugInfo();
//mtcnn.SetLimit(300, 50, 20);
const int use_pnet20 = true;
bool landmark106 = true;
int thread_num = 0;
bool special_handle_very_big_face = false;
result_name = "resultdet.jpg";
if (use_pnet20)
{
if (landmark106)
{
#if defined(_WIN32)
if (!mtcnn.Init("model/det1-dw20-fast.zqparams", "model/det1-dw20-fast.nchwbin",
"model/det2-dw24-fast.zqparams", "model/det2-dw24-fast.nchwbin",
//"model/det2.zqparams", "model/det2_bgr.nchwbin",
"model/det3-dw48-fast.zqparams", "model/det3-dw48-fast.nchwbin",
thread_num, true,
"model/det5-dw64-v3s.zqparams", "model/det5-dw64-v3s.nchwbin"
//"model/det3.zqparams", "model/det3_bgr.nchwbin"
#else
if (!mtcnn.Init("../../model/det1-dw20-fast.zqparams", "../../model/det1-dw20-fast.nchwbin",
"../../model/det2-dw24-fast.zqparams", "../../model/det2-dw24-fast.nchwbin",
//"../../model/det2.zqparams", "../../model/det2_bgr.nchwbin",
"../../model/det3-dw48-fast.zqparams", "../../model/det3-dw48-fast.nchwbin",
thread_num, true,
"../../model/det5-dw64-v3s.zqparams", "../../model/det5-dw64-v3s.nchwbin"
//"../../model/det3.zqparams", "../../model/det3_bgr.nchwbin"
#endif
))
{
cout << "failed to init!\n";
return EXIT_FAILURE;
}
}
else
{
#if defined(_WIN32)
if (!mtcnn.Init("model/det1-dw20-fast.zqparams", "model/det1-dw20-fast.nchwbin",
"model/det2-dw24-fast.zqparams", "model/det2-dw24-fast.nchwbin",
//"model\\det2.zqparams", "model\\det2_bgr.nchwbin",
"model/det3-dw48-fast.zqparams", "model/det3-dw48-fast.nchwbin",
//"model/det3.zqparams", "model/det3_bgr.nchwbin",
thread_num, false,
"model/det4-dw48-v2n.zqparams", "model/det4-dw48-v2n.nchwbin"
//"model/det3.zqparams", "model/det3_bgr.nchwbin"
#else
if (!mtcnn.Init("../../model/det1-dw20-fast.zqparams", "../../model/det1-dw20-fast.nchwbin",
"../../model/det2-dw24-fast.zqparams", "../../model/det2-dw24-fast.nchwbin",
//"model/det2.zqparams", "model/det2_bgr.nchwbin",
"../../model/det3-dw48-fast.zqparams", "../../model/det3-dw48-fast.nchwbin",
thread_num, false,
"model/det4-dw48-v2s.zqparams", "model/det4-dw48-v2s.nchwbin"
//"../../model/det3.zqparams", "../../model/det3_bgr.nchwbin"
#endif
))
{
cout << "failed to init!\n";
return EXIT_FAILURE;
}
}
mtcnn.SetPara(image0.cols, image0.rows, 80, 0.5, 0.6, 0.8, 0.4, 0.5, 0.5, 0.709, 3, 20, 4, special_handle_very_big_face);
}
else
{
#if defined(_WIN32)
if (!mtcnn.Init("model/det1.zqparams", "model/det1_bgr.nchwbin",
"model/det2.zqparams", "model/det2_bgr.nchwbin",
"model/det3.zqparams", "model/det3_bgr.nchwbin", thread_num))
#else
if (!mtcnn.Init("../../model/det1.zqparams", "../../model/det1_bgr.nchwbin",
"../../model/det2.zqparams", "../../model/det2_bgr.nchwbin",
"../../model/det3.zqparams", "../../model/det3_bgr.nchwbin", thread_num))
#endif
{
cout << "failed to init!\n";
return EXIT_FAILURE;
}
mtcnn.SetPara(image0.cols, image0.rows, 20, 0.6, 0.7, 0.7, 0.4, 0.5, 0.5, 0.709, 4, 12, 2, special_handle_very_big_face);
}
mtcnn.TurnOffShowDebugInfo();
//mtcnn.TurnOnShowDebugInfo();
int iters = 100;
double t1 = omp_get_wtime();
for (int i = 0; i < iters; i++)
{
if (i == iters / 2)
mtcnn.TurnOnShowDebugInfo();
else
mtcnn.TurnOffShowDebugInfo();
if (landmark106 && use_pnet20)
{
if (!mtcnn.Find106(image0.data, image0.cols, image0.rows, image0.step[0], thirdBbox106))
{
cout << "failed to find face!\n";
//return EXIT_FAILURE;
continue;
}
}
else
{
if (!mtcnn.Find(image0.data, image0.cols, image0.rows, image0.step[0], thirdBbox))
{
cout << "failed to find face!\n";
//return EXIT_FAILURE;
continue;
}
}
}
double t2 = omp_get_wtime();
printf("total %.3f s / %d = %.3f ms\n", t2 - t1, iters, 1000 * (t2 - t1) / iters);
namedWindow("result");
if (landmark106 && use_pnet20)
Draw(image0, thirdBbox106);
else
Draw(image0, thirdBbox);
imwrite(result_name, image0);
imshow("result", image0);
waitKey(1);
}
return EXIT_SUCCESS;
}
(一) WINDOWS下对比
(测试机器为E5-1650V4 3.6GHz)
编译方式:
ZQCNN用.sln打开
libfacedetection用cmake-gui配置,勾选avx2和DEMO,不勾选int8和neon,配置之后用vs打开,注意在facedetection、facedetection_shared、fdt_demo三个项目的项目属性->C++->代码生成里开启AVX2和快速浮点。
(1)对比keliamoniz1.jpg
结果比较:
(2)对比4.jpg
结果比较:
(3)对比其他图片
结果比较:
(二)ARM-LINUX下对比
(测试机器为firefly rk3399)
编译方式:
ZQCNN: 先编译OpenBLAS, OpenCV3.4.2, 然后编译ZQCNN, 使用命令cmake .. -DSIMD_ARCH_TYPE=arm64 -DBLAS_TYPE=openblas_zq_gemm 具体参见ZQCNN项目的README
libfacedetection: 命令cmake .. -DENABLE_NEON=ON -DCMAKE_BUILD_TYPE=RELEASE
(1)对比keliamoniz1.jpg
结果比较:
(2)对比4.jpg
结果比较:
(3)对比其他图片
结果比较:
总结
根据以上比较结果可知,按照上述编译方式,在上述图像集中,ZQCNN-MTCNN在速度和精度上都优于libfacedetection。
不过要声明一点,该比较中使用的图像数量过少,感兴趣的读者可自行下载编译测试。
ZQCNN使用了mkl、openblas,这些基础加速库可能是其速度占优的原因。
libfacedetection追求无依赖库,在实际项目中,应用可以更广泛。
代码数据下载
作者已经把上述代码及测试图像开源了,在OpenCV中文网公众号对话界面回复“facepk”,即可收到下载地址。