虹软SDK在nodejs中的集成
==虹软官网地址==
http://www.arcsoft.com.cn
在官网注册账号,并且申请人脸识别激活码, 选择SDK版本和运行系统(windows/linux/android/ios) ,我们选择windows做测试,申请类型选择1:N ,功能模块包括人脸检测、人脸跟踪、人脸识别。申请之后会获取APP_ID 和SDK_Key,在代码中会用到。
==虹软SDK人脸检测目的==
主要是与face++人脸检测做对比,看能否在face++人脸检测之前选择虹软事先检测一下。
==c++部分功能实现==
选择 Qtcreator 4.2.1 ,新建c++ 库。
设置Qt .pro文件
#不加载Qt库 QT -= core gui #生成库名字 TARGET = detect_lib #定义生成lib TEMPLATE = lib DEFINES += DETECT_LIB_LIBRARY SOURCES += detect_lib.cpp #加载虹软sdk头文件 HEADERS += detect_lib.h \ inc/amcomdef.h \ inc/ammem.h \ inc/arcsoft_fsdk_face_detection.h \ inc/asvloffscreen.h \ inc/merror.h unix { target.path = /usr/lib INSTALLS += target } unix|win32: LIBS += -L$$PWD/lib/ -llibarcsoft_fsdk_face_detection INCLUDEPATH += $$PWD/. DEPENDPATH += $$PWD/.
上面是.pro文件,主要是一些配置信息,如生成库名字 加载虹软SDK 和头文件...
下面是detect_lib.h文件 主要供nodejs调用的接口文件。
#ifndef DETECT_LIB_H #define DETECT_LIB_H # ifdef __cplusplus # define EXTERN_NAME extern "C" # else # define EXTERN_NAME extern # endif #if defined(WIN32) # define Q_DECL_EXPORT __declspec(dllexport) # define Q_DECL_IMPORT __declspec(dllexport) #if defined(DETECT_LIB_LIBRARY) # define DETECT_LIBSHARED_EXPORT EXTERN_NAME Q_DECL_EXPORT # else # define DETECT_LIBSHARED_EXPORT EXTERN_NAME Q_DECL_IMPORT #endif #else # define DETECT_LIBSHARED_EXPORT EXTERN_NAME #endif DETECT_LIBSHARED_EXPORT int add(int a,int b); DETECT_LIBSHARED_EXPORT int detect(unsigned char * data,int width,int height); #endif // DETECT_LIB_H
接口add 函数 主要做测试用
int detect(unsigned char * data,int width,int height);
检测人脸函数, data:rgb像素值,width:图片宽度,height:图片高度
detect_lib.cpp
#include "detect_lib.h" #include <stdio.h> #include <stdlib.h> #include <stdint.h> #include <Windows.h> #include "inc/arcsoft_fsdk_face_detection.h" #include "inc/merror.h" //申请引擎使用的空间 #define WORKBUF_SIZE (40*1024*1024) char* APPID = const_cast<char*>("4KjUsRayGjxVv2UrajQBcgB2RKB3JFxGu5HZCrv7T6no"); char* SDKKey = const_cast<char*>("BdFg8U29aDvUKdScmJcCZpep2xqYmrx4NouJ7iGm5BuX"); void changeFormat(unsigned char * data,unsigned char ** destData,int width,int height) { *destData = new unsigned char[width * height * 3]; int num = 0; for(int i = 0 ;i < width * height ;i ++) { int index = 4 * i; (*destData) [num ++] = data[index +2]; (*destData) [num ++] = data[index +1]; (*destData) [num ++] = data[index +0]; } } void charToUint (char* data,uint8_t ** imageData,int height,int width){ int lineByte = (width * 24 / 8 + 3) / 4 * 4; *imageData = (uint8_t *)malloc(lineByte * (height)); char * tImageData = (char *)malloc(lineByte * (height)); int num = 0; for(int i = 0 ;i < width * height*4 ;i++){ if((i+1) % 4 != 0){ tImageData[num] = data[i]; num ++; } } for (int i = 0; i < height; i++) { for (int j = 0; j < width; j++) { memcpy((*imageData) + i * width * 3 + j * 3, (uint8_t *)tImageData + ((height - 1) - i) * lineByte + j * 3, 3); } } free(tImageData); } int add(int a,int b){ return a + b; } int detect(unsigned char * data,int width,int height) { /* 初始化引擎和变量 */ MRESULT nRet = MERR_UNKNOWN; MHandle hEngine = nullptr; MInt32 nScale = 32; MInt32 nMaxFace = 10; MByte *pWorkMem = (MByte *)malloc(WORKBUF_SIZE); if (pWorkMem == nullptr) { return -1; } nRet = AFD_FSDK_InitialFaceEngine(APPID, SDKKey, pWorkMem, WORKBUF_SIZE, &hEngine, AFD_FSDK_OPF_0_HIGHER_EXT, nScale, nMaxFace); if (nRet != MOK) { return -1; } /* 打印版本信息 */ const AFD_FSDK_Version * pVersionInfo = nullptr; pVersionInfo = AFD_FSDK_GetVersion(hEngine); fprintf(stdout, "%d %d %d %d\n", pVersionInfo->lCodebase, pVersionInfo->lMajor, pVersionInfo->lMinor, pVersionInfo->lBuild); fprintf(stdout, "%s\n", pVersionInfo->Version); fprintf(stdout, "%s\n", pVersionInfo->BuildDate); fprintf(stdout, "%s\n", pVersionInfo->CopyRight); /* 读取静态图片信息,并保存到ASVLOFFSCREEN结构体 (以ASVL_PAF_RGB24_B8G8R8格式为例) */ ASVLOFFSCREEN offInput = { 0 }; //offInput.u32PixelArrayFormat = ASVL_PAF_RGB32_R8G8B8; offInput.u32PixelArrayFormat = ASVL_PAF_RGB24_B8G8R8; // offInput.ppu8Plane[0] = nullptr; offInput.i32Width = width; offInput.i32Height = height; // charToUint(data,(uint8_t**)&offInput.ppu8Plane[0],height,width); //readBmp24(INPUT_IMAGE_PATH, (uint8_t**)&offInput.ppu8Plane[0], &offInput.i32Width, &offInput.i32Height); unsigned char *dstdata = nullptr; changeFormat(data,&dstdata,width,height); offInput.ppu8Plane[0] = (MUInt8*) dstdata; if (!offInput.ppu8Plane[0]) { //fprintf(stderr, "Fail to ReadBmp(%s)\n", INPUT_IMAGE_PATH); AFD_FSDK_UninitialFaceEngine(hEngine); free(pWorkMem); return -1; } else { fprintf(stdout, "Picture width : %d , height : %d \n", offInput.i32Width, offInput.i32Height); } offInput.pi32Pitch[0] = offInput.i32Width * 3; /* 人脸检测 */ LPAFD_FSDK_FACERES FaceRes = nullptr; nRet = AFD_FSDK_StillImageFaceDetection(hEngine, &offInput, &FaceRes); int nface = 0; if (nRet != MOK) { fprintf(stderr, "Face Detection failed, error code: %d\n", nRet); } else { nface = FaceRes->nFace; fprintf(stdout, "The number of face: %d\n", FaceRes->nFace); for (int i = 0; i < FaceRes->nFace; ++i) { fprintf(stdout, "Face[%d]: rect[%d,%d,%d,%d], Face orient: %d\n", i, FaceRes->rcFace[i].left, FaceRes->rcFace[i].top, FaceRes->rcFace[i].right, FaceRes->rcFace[i].bottom, FaceRes->lfaceOrient[i]); } } /* 释放引擎和内存 */ nRet = AFD_FSDK_UninitialFaceEngine(hEngine); if (nRet != MOK) { fprintf(stderr, "UninitialFaceEngine failed , errorcode is %d \n", nRet); } //free(offInput.ppu8Plane[0]); delete offInput.ppu8Plane[0]; free(pWorkMem); return nface; }
2.nodejs集成
nodejs 使用node-gyp模块集成c++模块
1.新建binding.gyp文件
{
"targets": [
{
"target_name": "detect",
"sources": [ "detect_lib.h","detect.cpp" ],
'library_dirs': ['./'],
"include_dirs" : ["<!(node -e \"require('nan')\")","./"],
'libraries': ['-ldetect_lib',]
}
]
}
这是gyp配置文件,
target_name表示生成.node文件的名字,
source是在家c++的文件(.h,.cpp,.c)
library_dirs:库目录设置
include_dirs:头文件目录,除了头文件放置目录, 还包括会使用到nan模块
libraries:库名字
新建ArcDetect.cpp node的v8引擎接口文件
#include <nan.h> #include "detect_lib.h" using namespace Nan ; using namespace v8; class DetectWorker : public AsyncWorker { public: DetectWorker(Callback *callback, unsigned char* buffer,int width,int height) : AsyncWorker(callback), p_buffer(buffer), m_width(width),m_height(height) {m_num = 0;} ~DetectWorker() {} //这个函数运行在工作线程,而不是v8线程,所以不能访问v8的数据 void Execute () { //m_num = add(12,3); m_num = detect(p_buffer,m_width,m_height); // m_num = 5; } //这个是libuv的回调函数,在这里可以使用v8的数据 void HandleOKCallback () { Local<Object> bmpData = NewBuffer(m_num).ToLocalChecked(); Local<Value> argv[] = { Nan::Null() ,Uint32::New(v8::Isolate::GetCurrent(),m_num) }; callback->Call(2, argv); }; private: unsigned char * p_buffer; int m_width; int m_height; int m_num; }; NAN_METHOD(detect){ unsigned char * buffer = (unsigned char*) node::Buffer::Data(info[0]->ToObject()); int width = info[1]->Uint32Value(); int height = info[2]->Uint32Value(); Callback *callback = new Callback(info[3].As<Function>()); AsyncQueueWorker(new DetectWorker(callback, buffer,width ,height)); } NAN_MODULE_INIT(Init) { Nan::Set(target,New<String>("detect").ToLocalChecked(), GetFunction(New<FunctionTemplate>(detect)).ToLocalChecked()); } NODE_MODULE(detect, Init)
NAN_METHOD(detect) 表示定义接口detect ,js可以直接调用,
这里主要是node中的buffer直接以字节的方式传递给c++。也是nodejs与c++交互的重要方式。
将编译好的dll 和虹软sdk dll 和detect_lib.h拷贝到当前目录,然后通过node-gyp configure 和node-gyp build 生成.node
至此.node库编译完成,可以使用require直接饮用该.node 如:var detect = require('./build/Release/detect.node');