• 设为首页
  • 点击收藏
  • 手机版
    手机扫一扫访问
    迪恩网络手机版
  • 关注官方公众号
    微信扫一扫关注
    迪恩网络公众号

ArcSoft3.0NodeJs 版本实现: 采用ffi调用虹软3.0免费版本动态库,node 调用 C++ 实例 ...

原作者: [db:作者] 来自: 网络 收藏 邀请

开源软件名称:

ArcSoft3.0NodeJs 版本实现

开源软件地址:

https://gitee.com/BlacksMoon/arc_soft_node_js_3.0

开源软件介绍:

ArcSoft3.0_Nodejs

一、项目说明

虹软官网传送门 ,有不同平台和不同版本的sdk,有需要的根据业务下载。此次项目支持windows和linux系统,mac 由于 禁用electron打包的应用。

  1. 项目由electron-vue脚手架创建。
  2. 在主线程中实现与虹软SDK交互,如果还需要和render层交互,还需使用app.on来接收render层传过来的业务。
  3. vue打包时,需要在package.json中配置包含路径。
  4. 采用ffi库调用C++库,ref资料1ref资料2ref资料3其它的请自行百度,此处就不细细阐述了。
  5. 图片处理有两种,一种是opencv4nodejs(node版本大于10.x,我用的是12的最后一版本,最新版本的用的python3.3,编译问题太多),一种是jimp,都有方法实现,我推荐opencv,处理速度快,但是配置环境略复杂。资料看官方的npm即可,其余资料参考python调用opencv的方法类似。
  6. 此次主要针对window版本的dll,linux调用方法一样,将路径换位自己的路径即可,文件名为.so

二、ffi安装及环境安装

1、安装 Visual Studio 15 生成工具 2017 利用 微软自带的安装exe  【vs_BuildTools.exe】目录:C:\Users\Administrator\.windows-build-tools,脚本安装时,会因为安装包过大和网络问题,导致安装失败。2、指定编译MSBuild.exe位置,因为2019和上述装的2017都有这个exe,最好使用2017的,2019我安装失败了。npm config set msbuild_path "C:\Program Files (x86)\Microsoft Visual Studio\2017\BuildTools\MSBuild\15.0\Bin\MSBuild.exe"3、指定python版本,还是建议2.7,其他高版本问题太多,我的3.8.5(不推荐)npm config set python C:/Python27npm config set python C:/python3.8.53、安装gypnpm install node-gyp -g或者npm install --global node-gyp@latest4、安装ffi refnpm install ffi -gnpm install ref -g【20201208重要说明】node.js调用C++动态库依赖node-ffi库,因node-ffi支持的node版本版本过低,在electron高版本中无法使用;有一位国外作者提供了 node-ffi-napi 的库来支持高版本的node.js,推荐大家使用。即在package.json中替换为:{    "ffi-napi": "^3.1.0",    "ref-array-napi": "^1.2.1",    "ref-napi": "^3.0.1",    "ref-struct-napi": "^1.1.1",}成功编译

三、opencv4nodejs的安装

手动安装opencv的环境,设置环境变量OPENCV_BIN_DIR=E:\commonsoft\opencv\build\x64\vc14\binOPENCV_INCLUDE_DIR=E:\commonsoft\opencv\build\includeOPENCV_LIB_DIR=E:\commonsoft\opencv\build\x64\vc14\lib【20201208】在package.json中添加``` bash"opencv4nodejs": {    "disableAutoBuild": 1,    "opencvIncludeDir": "E:\\commonsoft\\opencv\\build\\include",    "opencvLibDir": "E:\\commonsoft\\opencv\\build\\x64\\vc14\\lib",    "opencvBinDir": "E:\\commonsoft\\opencv\\build\\x64\\vc14\\bin"  }

此处这么安装,成功率90%

set OPENCV4NODEJS_DISABLE_AUTOBUILD=1npm i -g opencv4nodejs

【20201208重要说明】此处很重要,不然 很多报错,比如 无法解析外部参数ffi-napi 等其它包不要rebuildelectron-rebuild编译opencv4nodejs

【20201208重要说明】如果要单独用node xxx.js执行,则npm i 的版本的就可以,否则报node version against。只能求求其一

四、代码结构

├─.electron-vue├─.idea├─build│  └─icons├─dist│  ├─electron│  └─web├─images├─src│  ├─main│  │  ├─inc│  │  ├─lib│  │  │  ├─X64│  │  │  └─X86│  │  └─modual│  │      ├─img│  │      │  ├─faces│  │      │  └─test│  │      └─log│  └─renderer│      ├─assets│      ├─components│      │  └─LandingPage│      ├─router│      └─store│          └─modules├─static└─test    ├─e2e    │  └─specs    └─unitmodual文件夹为人脸识别工作目录lib文件夹为放置dll文件路径的地方,x86 x64 为对应的版本img 图片路径(faces 为 drawFace 的操作路径 test 是 cvImages 的操作路径)

五、代码调用实例及说明

const arc_face = require('./face_engine');const path = require('path');const config = require('./config');const m = require('./logger');const client = require('./clients');const imageHelper = require('./face_cv_image');const fsUtil = require('./fileUtils');// 设置dll路径let dllPath = "";if (process.env.NODE_ENV !== 'development') {    dllPath = require('path').join(__dirname, '../../main/lib/X64').replace(/\\/g, '\\\\')}else{    dllPath = require('path').join(__dirname, '../lib/X64').replace(/\\/g, '\\\\')}// 添加DLL所在目录到环境变量process.env['PATH'] = `${process.env.PATH};${dllPath}`;// 初始化接口const arc = new arc_face(config.lib_win64);Object.prototype.toString = function(){    return JSON.stringify(this);};Date.prototype.toLocaleString = function() {    return this.getFullYear() + "-" + (this.getMonth() + 1) + "-" + this.getDate() + " " + this.getHours() + ":" + this.getMinutes() + ":" + this.getSeconds();};this.test_arc = async () => {    try{        let dataInfo = arc.getActiveFileInfo();        if (null != dataInfo){            let start = new Date(parseInt(dataInfo.startTime) * 1000);            let end = new Date(parseInt(dataInfo.endTime) * 1000);            m.logger.info("expire date is from %s to %s, please check the date for expired.thank you.",                start.toLocaleString(),                end.toLocaleString()            );            if (new Date().getTime() > end){                m.logger.warn('your arc soft is expired,please change the file to continue use it,thank you.');                return;            }        }        client.get('active', (err, res) => {            // 激活            if (!res || parseInt(res) !== 1){                let ib = arc.activeFaceEngine(config.appId,config.appKey);                client.set('active',ib?1:0);            }else{                m.logger.info("engine has already initialed, do not repeat active");            }        });        let version = arc.getVersion();        m.logger.info("current verson is %s",version.Version);        // 初始化引擎        arc.initialFaceEngine(16,50);        // 设置可信度        arc.setLiveParam(0.5,0.7);        let img_path1 = path.join(__dirname, './img/1.jpg');        let img_path2 = path.join(__dirname, './img/2.jpg');        let img_path3 = path.join(__dirname, './img/3.jpg');        // jimp 处理图片        // let asvl1 = await imageHelper.parseImage(img_path1,false);        // let asvl2 = await imageHelper.parseImage(img_path2,false);        // let asvl3 = await imageHelper.parseImage(img_path3,false);        // opencv 处理图片        let asvl1 = await imageHelper.cvImages(img_path1,false,path.join(__dirname, './img/test'));        let asvl2 = await imageHelper.cvImages(img_path2,false,path.join(__dirname, './img/test'));        let asvl3 = await imageHelper.cvImages(img_path3,false,path.join(__dirname, './img/test'));        // 检测人脸        let data1 = arc.detectFacesEx(asvl1.imageData);        let data2 = arc.detectFacesEx(asvl2.imageData);        // 提取特征值        let feature1 = arc.extractFeature(asvl1.imageData, data1.multi.faceRect[0], data1.multi.faceOrient[0]);        let feature2 = arc.extractFeature(asvl2.imageData, data2.multi.faceRect[0], data2.multi.faceOrient[0]);        if (!feature1 || !feature2){            m.logger.error("feature1 or feature2 is null. please check.");            return;        }        // console.log("feature1",feature1)        // console.log("feature2",feature2)        // 比较        let sim = arc.compareFeature(feature1.nav,feature2.nav);        if (sim < 0){            m.logger.info("similar is %d,is not the same person.",sim);            return;        }        if (sim > 0.8){            m.logger.info("similar is %d,is the same person.",sim);        }else{            m.logger.info("similar is %d,is not the same person.",sim);        }        // base64 转 feature 测试 ,实际效果会差0.02        let f1 = arc.base64ToFeature(feature1.nab.featureBuffer);        let f2 = arc.base64ToFeature(feature2.nab.featureBuffer);        let ff = arc.compareFeature(f1,f2);        if (ff > 0.8){            m.logger.info("similar is %d,is the same person.",ff);        }else{            m.logger.info("similar is %d,is not the same person.",ff);        }        // 释放人脸指针        arc.release(f1.feature);        arc.release(f2.feature);        // 单体检测        let ib = arc.processEx(asvl3.imageData,false);        if (!ib){            m.logger.error("single process checked failed.");            return;        }        arc.getSexInfo();        arc.getAgeInfo();        arc.getAngleInfo();        arc.getLivenessScore(false);        // 测试画框        let isIR = false;        let asvl5 = await imageHelper.cvImages(img_path1,isIR);        let infos = arc.detectFacesEx(asvl5.imageData);        let filePath = path.join(__dirname, './img/faces');        await fsUtil.dirExists(filePath);        imageHelper.drawFace(img_path1,infos.multi,true,filePath);                // detect 和 process 系列        let fileCv = path.join(__dirname, './img/test');        await fsUtil.dirExists(fileCv);        let asvl6 = await imageHelper.cvImages(img_path1,isIR,fileCv);        let faces = arc.detectFaces(asvl6,false);        let info = arc.processNone(asvl6,false);        arc.getLivenessScore(false);        // console.log('info',info);        // 释放 函数中已经释放调用的指针        arc.unInitialEngine();        client.quit();    }catch (e) {        m.logger.info("some error happened.%s",e.toString());        arc.unInitialEngine();        client.quit()    }};this.test_arc();

六、安装及使用

# install dependenciesnpm install# serve with hot reload at localhost:9080npm run dev# build electron application for productionnpm run build# run unit & end-to-end testsnpm test# 测试人脸部分node face_test.js

七、常见问题

1、Dynamic Linking Error: Win32 error 126这个错误有三种原因(1)通常是传入的 DLL 路径错误,找不到 Dll 文件,推荐使用绝对路径。(2)如果是在 x64 的node/electron下引用 32 位的 DLL,也会报这个错,反之亦然。要确保 DLL 要求的 CPU 架构和你的运行环境相同。DLL 还有引用其他 DLL 文件,但是找不到引用的 DLL 文件,可能是 VC 依赖库或者多个 DLL 之间存在依赖关系。设置dll的工作的环境变量process.env['PATH'] = `${process.env.PATH};${'E:/arface_node/arface_nodejs/src/main/lib/X64'}` //添加DLL所在目录到环境变量Dynamic Linking Error: Win32 error 127:DLL (3)没有找到对应名称的函数,需要检查头文件定义的函数名是否与 DLL 调用时写的函数名是否相同。2、electron-vue 运行问题,如果提示app of undefined 类似的问题,在创建窗体时加入如下模块enableRemoteModule:true, // 加入此句就可以获取app模块,就不会报错了3、在node里面,指针 -> 缓存(buffer), let a = new (typedef.MInt32.size) 表示 a*,a.deref() 可以取出指针中的值,普通变量 b, 那 b.ref() 表示 b*.size 相当于C++ 里面的 size_t4、调用C++ 第一步就是对应 数据类型 // ref提供了C++的基本数据类型和定义,nodejs参数类型和C语言参数类型转换 const ref = require('ref');typedef.MLong = ref.types.long;typedef.UMLong = ref.types.uint32;typedef.MFloat = ref.types.float;// 提供 C++ 结构体定义方法 const StructType = require('ref-struct');typedef.__tag_rect = StructType({  left: typedef.MInt32,  top: typedef.MInt32,  right: typedef.MInt32,  bottom: typedef.MInt32});// 提供 C++ 数组定义方法const ArrayType = require('ref-array');ppu8Plane: ArrayType(ref.refType(typedef.MUInt8), 4),ref.refType(XXX)表示指针5、C++接口映射// 一种是不带回调函数的,如下:ffi.Library(libFile, {    // 映射的接口名称 C++接口,对应的数据类型    ASFGetActiveFileInfo: [TypeDef.MRESULT,        [            TypeDef.LPASF_ActiveFileInfo // [out] 激活文件信息        ]    ]}// 一种是带回调函数的调用let libname = ffi.Library('./libname', {  'setCallback': ['void', ['pointer']]});let callback = ffi.Callback('void', ['int', 'string'],  function(id, name) {    console.log("id: ", id);    console.log("name: ", name);});libname.setCallback(callback);// 退出时引用回调指针以避免GC(垃圾回收)process.on('exit', function() {  callback});6、指针拷贝操作,用到 linux 中的 libc 操作分配指定大小的空间指针: libc.malloc(size);初始化指针:libc.memset(*p,offset,size);指针拷贝:libc.memcpy(*to, *from ,size);7、结合node中的buffer,将指针拷贝至bufferconst arr = new Buffer(TypeDef.MByte.size);for (let i = 0; i < _feature.featureSize; i++) {    libc.memcpy(arr.address(), feature.feature.address() + i * TypeDef.MByte.size, TypeDef.MByte.size);    this.pointers.push(feature.feature);    _normal.feature.push(ref.get(arr, 0, TypeDef.MByte));}arr.deref()即取出指针中的值8、分配的指针空间一定要释放libc.free(*p)

八、开源说明

  • 开源不易,请使用代码时,注明引用,谢谢~
  • 如果你觉得我的代码帮到了你,请捐赠以表示支持,谢谢~输入图片说明

鲜花

握手

雷人

路过

鸡蛋
该文章已有0人参与评论

请发表评论

全部评论

专题导读
热门推荐
热门话题
阅读排行榜

扫描微信二维码

查看手机版网站

随时了解更新最新资讯

139-2527-9053

在线客服(服务时间 9:00~18:00)

在线QQ客服
地址:深圳市南山区西丽大学城创智工业园
电邮:jeky_zhao#qq.com
移动电话:139-2527-9053

Powered by 互联科技 X3.4© 2001-2213 极客世界.|Sitemap