在windows和linux下使用cmake、dlib编译人脸识别动态链接库

开发人脸识别应用时,由于前端设备及操作系统种类繁多,如果选择在侵短实现识别功能适配工作较为繁琐,故计划将人脸识别功能在后端实现从而统一为应用提供服务。需要对dlib进行封装以供其他语言调用。

前提:cmake安装及dlib的编译

使用cmake将dlib编译成静态库lib文件,或者动态链接库(windows上为dll,linux上为so)。

开发环境

windows

win11
cmake-3.32.2
Microsoft Visual Studio 2015

linux

redhat7.6
cmake-3.25.2
gcc-9.3.1

CMakeLists.txt 及 c++代码

CMakeLists.txt

CMAKE_MINIMUN_REQUIRED(VERSION 2.8.4)

PROJECT(facerecog)

SET(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -std=c++11 -O2 -DDLIB_JPEG_SUPPORT")

IF(UNIX)
INCLUDE_DIRECTORIES(/root/dlib-19.24)
ELSE()
ADD_DEFINITIONS(-DFRDLL_EXPORTS)
INCLUDE_DIRECTORIES(D:/FaceRecognize/dlib-19.24)
ENDIF()

IF(UNIX)
LINK_DIRECTORIES(/root/dlib-19.24/build/dlib)
ELSE()
LINK_DIRECTORIES(D:/FaceRecognize/dlib-19.24/build/dlib/Release)
ENDIF()

#ADD_EXECUTABLE(facerecog dlib_test.cpp)
ADD_LIBRARY(facerecog SHARED facerecoglib.h facerecoglib.cpp)

IF(UNIX)
#TARGET_LINK_LIBRARIES(facerecog dlib)
TARGET_LINK_LIBRARIES(facerecog libdlib.so)
TARGET_LINK_LIBRARIES(facerecog pthread)
ELSE()
TARGET_LINK_LIBRARIES(facerecog dlib19.24.0_release_64bit_msvc1900.lib)
ENDIF()

facerecoglib.h

#ifndef FACERECOGLIB_H
#define FACERECOGLIB_H

#ifdef _WINDOWS

#ifdef FRDLL_EXPORTS
#define FRDLL_API __declspec(dllexport)
#else
#define FRDLL_API __declspec(dllimport)
#endif
#else
#define FRDLL_API
#endif

extern "C"
{
    // 功能:初始化特征库,读取并保存基准图像文件夹中所有人脸特征值及特征信息(文件
    FRDLL_API int fg_init(const char* featurepicdir,float threshold);

    // 功能:识别路径未picpath的图片,返回对应的人员信息
    FRDLL_API char* fg_recog(const char* picpath);

    // 功能:识别是否匹配特定人脸特征
    FRDLL_API int fg_recogspecial(const char* picpath,const char* featureinfo);

    // 功能:插入新特征值到特征库
    FRDLL_API int fb_insertfeature(const char* picpath);

    // 功能:删除指定的特征值及特征信息
    FRDLL_API int fb_deletefeature(const char* featureinfo);
}

#endif

facerecoglib.cpp

//尽量减少对其他库的依赖,未使用图形及opencv支持
#include <algorithm>
#include <dlib/dnn.h>
#include <dlib/clustering.h>

#include <dlib/image_processing/frontal_face_detector.h>
#include <dlib/image_io.h>
#include <iostream>

#include <vector>
#include <string>
#include <map>

#ifdef _WINDOWS
#include "io.h"
#else
#include <dirent.h>
#endif // _WINDOWS

#include "facerecoglib.h"

using namespace dlib;
using namespace std;

template <template <int, template<typename>class, int, typename> class block, int N, template<typename>class BN, typename SUBNET>
using residual = add_prev1<block<N, BN, 1, tag1<SUBNET>>>;

template <template <int, template<typename>class, int, typename> class block, int N, template<typename>class BN, typename SUBNET>
using residual_down = add_prev2<avg_pool<2, 2, 2, 2, skip1<tag2<block<N, BN, 2, tag1<SUBNET>>>>>>;

template <int N, template <typename> class BN, int stride, typename SUBNET>
using block = BN<con<N, 3, 3, 1, 1, relu<BN<con<N, 3, 3, stride, stride, SUBNET>>>>>;

template <int N, typename SUBNET> using ares = relu<residual<block, N, affine, SUBNET>>;
template <int N, typename SUBNET> using ares_down = relu<residual_down<block, N, affine, SUBNET>>;

template <typename SUBNET> using alevel0 = ares_down<256, SUBNET>;
template <typename SUBNET> using alevel1 = ares<256, ares<256, ares_down<256, SUBNET>>>;
template <typename SUBNET> using alevel2 = ares<128, ares<128, ares_down<128, SUBNET>>>;
template <typename SUBNET> using alevel3 = ares<64, ares<64, ares<64, ares_down<64, SUBNET>>>>;
template <typename SUBNET> using alevel4 = ares<32, ares<32, ares<32, SUBNET>>>;

using anet_type = loss_metric<fc_no_bias<128, avg_pool_everything<
	alevel0<
	alevel1<
	alevel2<
	alevel3<
	alevel4<
	max_pool<3, 3, 2, 2, relu<affine<con<32, 7, 7, 2, 2,
	input_rgb_image_sized<150>
	>>>>>>>>>>>>;

frontal_face_detector detector;
shape_predictor predictor;
anet_type net;
float frthreshold = 0.4;
std::map<std::string,matrix<float, 0, 1>> mapFeature;

static char retstr[255];
//返回图像img中所有人脸特征值
std::vector<matrix<float, 0, 1>> GetFaceFeature(matrix<rgb_pixel> img)
{
	std::vector<matrix<float, 0, 1>> features;
	std::vector<dlib::rectangle> dets = detector(img);  		
	std::vector<matrix<rgb_pixel>> faces;
	for (auto const& det : dets)
	{
		auto shape = predictor(img, det);
		matrix<rgb_pixel> face_chip;
		extract_image_chip(img, get_face_chip_details(shape, 150, 0.25), face_chip);
		faces.push_back(move(face_chip));
	}

	std::vector<matrix<float, 0, 1>> face_descriptors = net(faces);	
	for (auto const& feature : face_descriptors)
	{
		features.push_back(feature);
	}

	return features;
}

#读取strfeaturepicdir路径下所有图片作为识别基准数据
int CreateDataset(std::string strfeaturepicdir)
{
    mapFeature.clear();
    
    std::vector<std::string> arrFileNames; //图片完整路径
    std::vector<std::string> arrLabels;  //图片名称,无扩展名,作为识别输出信息

#ifdef _WINDOWS
	_finddata_t file;
	intptr_t HANDLE;
	std::string strdir = strfeaturepicdir + "\\*.*";
	HANDLE = _findfirst(strdir.c_str(), &file);
	if (HANDLE == -1L)
	{
		return -1;
	}
	do {
		//判断是否有子目录  
		if (file.attrib & _A_SUBDIR == 0)
		{
			std::string fileName = file.name;
			if (fileName.find(".jpg") != string::npos || fileName.find(".jpeg") != string::npos
				|| fileName.find(".bmp") != string::npos || fileName.find(".JPEG") != string::npos
				|| fileName.find(".JPG") != string::npos || fileName.find(".BMP") != string::npos)
			{
				std::string label = fileName.find_last_of('.') == string::npos ? fileName : fileName.substr(0, fileName.find_last_of('.'));
				fileName = strfeaturepicdir + "\\" + fileName;
				arrFileNames.push_back(fileName);
				arrLabels.push_back(label);
			}
		}
	} while (_findnext(HANDLE, &file) == 0);
	_findclose(HANDLE);

#else
    DIR *dir;
    struct dirent *ptr;
    if ((dir = opendir(strfeaturepicdir.c_str())) == NULL) 
    { 
        return -1; 
    }
    while ((ptr = readdir(dir)) != NULL) 
    { 
        if (ptr->d_name[0] == '.') 
            continue; 
        if (ptr->d_type == 8) 
        { 
            std::string fileName = ptr->d_name;

            if (fileName.find(".jpg") != string::npos || fileName.find(".jpeg") != string::npos
            || fileName.find(".bmp") != string::npos || fileName.find(".JPEG") != string::npos
            || fileName.find(".JPG") != string::npos || fileName.find(".BMP") != string::npos) 
            {
				std::string label = fileName.find_last_of('.') == string::npos ? fileName : fileName.substr(0,fileName.find_last_of('.'));
				fileName = strfeaturepicdir + "//" + fileName;
				arrFileNames.push_back(fileName);
				arrLabels.push_back(label);
            } 
        }
    }
    closedir(dir);
    
#endif // _WINDOWS

    for (int k = 0; k < arrFileNames.size(); k++)
    {
		matrix<rgb_pixel> img;
		try
		{
			load_image(img, arrFileNames[k]);
		}
		catch(exception& e)
		{
			cout << "\nexception thrown!" << endl;
			cout << e.what() << endl;
			return -5;
		}

		std::vector<matrix<float, 0, 1>> features = GetFaceFeature(img);
		if (features.size() == 0 || features.size() > 1)
			return -2;
		else
			mapFeature.insert(pair<string,matrix<float, 0, 1>>(arrLabels[k],features[0]));
    }
    return mapFeature.size();
}


std::string GetRecogInfo(matrix<float, 0, 1> predict_value)
{
    std::string strInfo;
    float min_dis = 10000;
    for(std::map<std::string,matrix<float, 0, 1>>::iterator it = mapFeature.begin(); it != mapFeature.end(); it++)
    {
		float dis = (float)length(predict_value - it->second);
		if (min_dis > dis)
		{
     			min_dis = dis;
			strInfo = it->first;
		}
    }

    if (min_dis > frthreshold)
		strInfo = "UNKNOWN";

    return strInfo;
}

int fg_init(const char* picdir,float threshold)
{
    frthreshold = threshold;
    try
    {
        detector = get_frontal_face_detector();
        deserialize("model//shape_predictor_68_face_landmarks.dat") >> predictor;
        deserialize("model//dlib_face_recognition_resnet_model_v1.dat") >> net;
    }
    catch (exception& e)
    {
	cout << "\nexception thrown!" << endl;
	cout << e.what() << endl;
	return -3;
    }
    std::string strpicdir = picdir;
    return CreateDataset(strpicdir);
}

char* fg_recog(const char* picpath)
{
    if(mapFeature.size() == 0)
        return "NOFACEFEATURES";

    matrix<rgb_pixel> img;
    try{
   		load_image(img, picpath);
    }
    catch(exception& e)
    {
        cout << "\nexception thrown!" << endl;
		cout << e.what() << endl;
		return "CANNOTOPENIMG";
    }
    std::vector<matrix<float, 0, 1>> fsrc = GetFaceFeature(img);

    if(fsrc.size() == 0)
        return "NOSOURCEFACE";

    if(fsrc.size() > 1)
        return "MOREFACES";

    std::string strRet = GetRecogInfo(fsrc[0]);

	if (strRet.length() > 254)
		return "";

	strcpy(retstr, strRet.c_str());
    return retstr;
}

int fg_recogspecial(const char* picpath,const char* featureinfo)
{
    string strfeatureinfo = featureinfo;

    if(strfeatureinfo.empty())
        return -1;

    matrix<rgb_pixel> img;
    try
    {
    	load_image(img, picpath);
    }
    catch(exception& e)
    {
	cout << "\nexception thrown!" << endl;
	cout << e.what() << endl;
	return -5;
    }

    std::vector<matrix<float, 0, 1>> fsrc = GetFaceFeature(img);

    if(fsrc.size() > 1 || fsrc.size() == 0)
        return -2;

    if(mapFeature.count(strfeatureinfo) == 0)
        return -3;

    float dis = (float)length(fsrc[0] - mapFeature.at(strfeatureinfo));

    if(dis > frthreshold)
        return 0;
    else
        return 1;

}

int fb_insertfeature(const char* picpath)
{
	std::string strPicPath = picpath;
	if(strPicPath.empty())
		return 0;

	if(mapFeature.count(strPicPath))
		return -1;

	matrix<rgb_pixel> img;
	try
	{
		load_image(img, strPicPath);
	}
	catch(exception& e)
	{
		cout << "\nexception thrown!" << endl;
		cout << e.what() << endl;
		return -5;
	}

	std::vector<matrix<float, 0, 1>> fsrc = GetFaceFeature(img);

	if(fsrc.size() > 1 || fsrc.size() == 0)
    	return -2;
	std::string label = fileName.find_last_of('.') == string::npos ? fileName : fileName.substr(0,fileName.find_last_of('.'));
	mapFeature.insert(pair<string,matrix<float, 0, 1>>(label,fsrc[0]));
	return 1;
}

int fb_deletefeature(const char* featureinfo)
{
	string strInfo = featureinfo;
	if(strInfo.empty())
		return -1;
	if(mapFeature.count(strInfo) == 0)
		return 0;
	mapFeature.erase(strInfo);
	return 1;
}

命令行编译

windows

mkdir build
cd build
cmake -G "Visual Studio 14 2015 Win64" -T host=x64 ..
cmake --build . --config Release

linux

mkdir build
cd build
cmake ..
cmake --build . --config Release

链接库测试

linux下可以使用nm命令查看so文件函数列表;windows下使用vs2015命令行下的dumpbin命令查看dll文件的函数列表。
cd 到动态链接库所在文件夹下,输入命令:

windows

dumpbin /exports facerecog.dll

linux

nm libfacerecog.so | grep fg
nm libfacerecog.so | grep fb

demo

frtest.cpp

#include "facerecoglib.h"
#include <string>
#include <iostream>

int main()
{
	int res = fg_init("data", 0.4);
	if(res > 0)
	{
		std::cout << "print pic path to recog person" << std::endl;
		std::string path;
		while(1)
		{
			
			std::cin >> path;
			std::cout << fg_recog(path.c_str()) << std::endl;
			std::cout << "continue print pic path" << std::endl; 
		}

	}

	std::cout << res << std::endl;
	return 0;
}

编写一个简单的c++demo对动态连接库进行调用测试,demo目录中内容如图
测试demo目录
data中保存基准图片,model保存模型,命令行运行frtest,结果如下
在这里插入图片描述

参考

机器学习库dlib的C++编译和使用(windows和linux)_踏莎行hyx的博客-CSDN博客_dlib c++
linux环境下利用Cmake生成动态链接库并使用实例_搞技术的火星人的博客-CSDN博客_cmake生成动态库 linux
c++人脸识别-vs2017 + dlib19.19_zzubqh103的博客-CSDN博客_dlib人脸识别c++

Logo

瓜分20万奖金 获得内推名额 丰厚实物奖励 易参与易上手

更多推荐