672
rk3588平台人脸识别sdk封装(.so动态链接库):Golang调用 笔记
乐果 发表于 2024 年 01 月 12 日 标签:cc++golangcgoai
上周的预研中,测试了离线 sdk
中官方的 demo
代码编译、运行测试,均已达到预期测试效果。
但官方 demo
源码只是简单样例,实际业务应用时需要对它做更多逻辑操作,并且业务逻辑层很可能不是 C
或 C++
语言,例如我一般使用 golang
语言作为业务层的开发语言。那么,基于这个现实场景,需要对原生sdk的源码采用 C
语言进行二次暴露封装,然后将它编译成动态链接库(例如 .so
),使其他编程语言能方便调用。
二次暴露封装
采用 C
语言对 官方 sdk
的 C++
源码进行二次封装暴露,为啥要做这一步?
- 首先
C
语言作为更通用的系统语言,它封装的接口在与其他高级语言嵌入时更具兼容性; - 其次
C++
内置的类概念,在Go
依赖的cgo
方式调用动态链接库方式中 并不支持。 因此,为方便Go
语言集成,必须对它用C
语言作二次封装暴露。
二次封装例子:
新建 C
源码文件 face_api.cpp
:
#include "baidu_face_api.h"
#include "face_api.h"
#include <string>
#include <iostream>
#include <stdio.h>
#include <regex>
#include <sstream>
#include <fstream>
#include <iterator>
#include <thread>
#include "./util/image_base64.h"
#include "./util/image_buf.h"
#include "json/json.h"
using namespace std;
#ifdef __cplusplus
extern "C"
{
#endif
// 初始化sdk api 实例
void *call_BaiDuApi_Create()
{
return new BaiduFaceApi();
}
// 销毁api实例
void call_BaiDuApi_Destroy(void *p)
{
delete static_cast<BaiduFaceApi *>(p);
}
// 实例算法授权初始化
void call_BaiDuApi_Init(void *p,char *path,char *result)
{
BaiduFaceApi *_api = static_cast<BaiduFaceApi *>(p);
int res = _api->sdk_init(path);
std::cout << "after sdk_init" << std::endl;
if (res != 0)
{
strncpy(result, "0", strlen(result));
return ;
}
strncpy(result, "1", strlen(result));
}
// 获取设备指纹
void call_BaiDuApi_GetDeviceID(void *p, char *result)
{
BaiduFaceApi *_api = static_cast<BaiduFaceApi *>(p);
std::string dev_id;
_api->get_device_id(dev_id);
const char *id = dev_id.c_str();
int i = strlen(result);
strncpy(result, id, i);
printf("length: %d, src id: %s, result id: %s, result length: %d \n", i, id, result, strlen(result));
}
// 用户注册
void call_BaiDuApi_UserAdd(void *p, char *group_id, char *user_id, char *images_file, char *result)
{
printf("group id: %s, user_id: %s, image file: %s \n", group_id, user_id, images_file);
BaiduFaceApi *_api = static_cast<BaiduFaceApi *>(p);
cv::Mat mat = cv::imread(images_file);
if (mat.empty())
{
// 图片有问题,检查路径是否正确
printf("cv::imread return empty \n");
return;
}
std::string user_info = "user_info";
// 提取人脸特征值数组(多人会提取多个人的特征值)
std::vector<Feature> fea_list;
std::vector<FaceBox> box_list;
// type 0: 表示rgb 人脸检测 1:表示nir人脸检测
int type = 0;
int face_num = _api->face_feature(fea_list, box_list, &mat, type);
if (fea_list.size() <= 0)
{
// 未获取到人脸特征值,请检查图片是否包含人脸
printf("can not get face feature, please check image \n");
return;
}
// 提取到人脸特征值
Feature f1 = fea_list.at(0);
//人脸注册返回json
std::string res;
_api->user_add(res, &f1, user_id, group_id, user_info.c_str());
int i = strlen(result);
strncpy(result, res.c_str(), i);
}
// 人脸识别 1:N 比对
void call_BaiDuApi_FaceIdentify(void *p,char *group_id,char *images_file,char *result)
{
printf("step: %d, group id: %s, image file: %s \n", 0, group_id, images_file);
BaiduFaceApi *_api = static_cast<BaiduFaceApi *>(p);
//
if (!_api->is_auth()) {
// 算法是否授权
printf("sdk not auth \n");
return;
}
// type 0: 表示rgb 人脸检测 1:表示nir人脸检测
printf("step: %d, group id: %s, image file: %s \n", 1, group_id, images_file);
int type = 0;
cv::Mat mat = cv::imread(images_file);
//
if (mat.empty())
{
// 图片有问题,检查路径是否正确
printf("cv::imread return empty \n");
return;
}
std::vector<Feature> fea_list;
std::vector<FaceBox> box_list;
// 提取第一个人的特征值
// 特征值的type:传 0 可见光生活照特征值,1、可见光证件照特征值 2、表示近红外特征值
int face_num1 = _api->face_feature(fea_list, box_list, &mat, type);
printf("step: %d, group id: %s, image file: %s \n", 3, group_id,images_file);
if (fea_list.size() <= 0)
{
// 未获取到人脸特征值,请检查图片是否包含人脸
printf("can not get face feature, please check image \n");
return;
}
// 和人脸库里面的特征值进行比较,人脸库可参考face_manager.cpp
Feature f1 = fea_list.at(0);
// 传入组名,按组比较,user_id可不传
// identify的type:传 0 可见光生活照特征值,1、可见光证件照特征值 2、表示近红外特征值
std::string res;
_api->identify(res, &f1, group_id, "", type);
//printf("identify result is %s \n", res);
int i = strlen(result);
strncpy(result, res.c_str(), i);
}
#ifdef __cplusplus
}
#endif
新建头文件 face_api.h
:
#ifndef WRAPPER_H_
#define WRAPPER_H_
#ifdef __cplusplus
extern "C"
{
#endif
//初始化sdk api 实例
void *call_BaiDuApi_Create();
// 销毁api实例
void call_BaiDuApi_Destroy(void *p);
// 实例算法授权初始化
void call_BaiDuApi_Init(void *p,char *path,char *result);
// 获取设备指纹
void call_BaiDuApi_GetDeviceID(void *p, char *result);
// 用户注册
void call_BaiDuApi_UserAdd(void *p, char *group_id, char *user_id, char *images_file, char *result);
// 人脸识别 1:N 比对
void call_BaiDuApi_FaceIdentify(void *p, char *group_id, char *images_file, char *result);
#ifdef __cplusplus
}
#endif
#endif // WRAPPER_H_
Cmake编译文件 CMakeLists.txt
:
cmake_minimum_required(VERSION 3.5.1)
set(CMAKE_CXX_STANDARD 11)
project(face_api)
file(GLOB DIR_SRCS face_api.cpp face_draw/*.cpp face_detect/*.cpp
face_track/*.cpp face_attr/*.cpp face_blur/*.cpp face_compare/*.cpp face_action_live/*.cpp
sdk_info/*.cpp face_crop/*.cpp face_best/*.cpp dark_enhance/*.cpp driver_monitor/*.cpp safety_belt/*.cpp
face_eyeclose/*.cpp face_feature/*.cpp face_gaze/*.cpp
face_headpose/*.cpp face_illumination/*.cpp face_landmark/*.cpp face_liveness/*.cpp
face_manager/*.cpp face_mouth_close/*.cpp face_mouth_mask/*.cpp face_emotion/*.cpp
face_occlusion/*.cpp util/*.cpp multi_thread/*.cpp)
include_directories(
${CMAKE_SOURCE_DIR}
${CMAKE_SOURCE_DIR}/../include
${CMAKE_SOURCE_DIR}/../third_party
)
link_directories(
${CMAKE_SOURCE_DIR}/../lib/${ARCH_ABI}
)
set(FACE_TEST "face_api")
add_library(${FACE_TEST} SHARED ${DIR_SRCS})
target_link_libraries(${FACE_TEST} face_sdk crypto ssl curl paddle_light_api_shared opencv_world baidu_face_api pthread)
golang调用so链接库
直接贴案例代码:
package main
/*
#cgo CFLAGS: -I ../include -I ../share
#cgo LDFLAGS: -L../share -lface_api
#include <stdlib.h>
#include <stdio.h>
#include "../share/face_api.h"
*/
import "C"
import (
"fmt"
"reflect"
"strings"
"unsafe"
)
//
func main(){
//创建实例
_faceApi := C.call_BaiDuApi_Create()
//实例销毁
defer C.call_BaiDuApi_Destroy(_faceApi)
//算法初始化:检测算法是否授权
if FaceInit(_faceApi) {
//注册人脸底照
FaceAdd(_faceApi)
//1:N 搜索
FaceSearch(_faceApi)
//获取设备指纹
GetDeviceId(_faceApi)
}
}
//FaceInit 初始化:算法授权
func FaceInit(faceApi unsafe.Pointer) bool {
_result := C.CString(strings.Repeat(" ", 50))
defer C.free(unsafe.Pointer(_result))
_home := C.CString("/home/xiao/work/face_offline_sdk")
defer C.free(unsafe.Pointer(_home))
C.call_BaiDuApi_Init(faceApi,_home,_result)
if C.GoString(_result) == "1" {
return true
}
return false
}
//GetDeviceId 获取设备号
func GetDeviceId(faceApi unsafe.Pointer){
_result := C.CString(strings.Repeat(" ", 50))
C.call_BaiDuApi_GetDeviceID(faceApi,_result)
fmt.Println(reflect.TypeOf(_result).String(),"======= Device id ========", C.GoString(_result))
C.free(unsafe.Pointer(_result))
}
//FaceAdd 注册人脸底照
func FaceAdd(faceApi unsafe.Pointer){
//
_result := C.CString(strings.Repeat(" ", 200))
defer C.free(unsafe.Pointer(_result))
//
_groupID := C.CString("test_group")
defer C.free(unsafe.Pointer(_groupID))
//
_userID := C.CString("123456")
defer C.free(unsafe.Pointer(_userID))
//_faceFile := C.CString("/home/xiao/work/face_offline_sdk/test/2b3045f5a89bc077208556db66250f38.jpg")
_faceFile := C.CString("/home/xiao/work/face_offline_sdk/images/2.jpg")
defer C.free(unsafe.Pointer(_faceFile))
C.call_BaiDuApi_UserAdd(faceApi, _groupID, _userID, _faceFile, _result)
fmt.Println(reflect.TypeOf(_result).String(),"======= face add ========", C.GoString(_result))
}
//FaceSearch 1:N 人脸识别搜索
func FaceSearch(faceApi unsafe.Pointer){
_result := C.CString(strings.Repeat(" ", 1000))
defer C.free(unsafe.Pointer(_result))
_groupID := C.CString("test_group")
defer C.free(unsafe.Pointer(_groupID))
//_faceFile := C.CString("/home/xiao/work/face_offline_sdk/test/2b3045f5a89bc077208556db66250f38.jpg")
_faceFile := C.CString("/home/xiao/work/face_offline_sdk/images/2.jpg")
defer C.free(unsafe.Pointer(_faceFile))
C.call_BaiDuApi_FaceIdentify(faceApi,_groupID,_faceFile,_result)
fmt.Println(reflect.TypeOf(_result).String(),"======= face search ========", C.GoString(_result))
}
编译上述 go
源码,并运行测试:
go build ./ && ./test
运行结果如下截图:
至此,算法SDK(C++)
> C暴露封装(.so动态链接库)
> golang调用
这样从零到一的构思 完美成功!