ZQCNN快速人脸检测

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”,即可收到下载地址。

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

AI算法网奇

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值