397

rk3588平台人脸识别sdk封装(.so动态链接库):Golang调用 笔记

乐果   发表于   2024 年 01 月 12 日 标签:cc++golangcgoai

上周的预研中,测试了离线 sdk 中官方的 demo 代码编译、运行测试,均已达到预期测试效果。

但官方 demo 源码只是简单样例,实际业务应用时需要对它做更多逻辑操作,并且业务逻辑层很可能不是 CC++ 语言,例如我一般使用 golang 语言作为业务层的开发语言。那么,基于这个现实场景,需要对原生sdk的源码采用 C 语言进行二次暴露封装,然后将它编译成动态链接库(例如 .so ),使其他编程语言能方便调用。

二次暴露封装

采用 C 语言对 官方 sdkC++ 源码进行二次封装暴露,为啥要做这一步?

  • 首先 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调用 这样从零到一的构思 完美成功!

乐果   发表于   2024 年 01 月 12 日 标签:cc++golangcgoai

0

文章评论