zoukankan      html  css  js  c++  java
  • 虹软人脸识别ArcSoft3.0NodeJs 版本实现

    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资料1
      ref资料2
      ref资料3
      其它的请自行百度,此处就不细细阐述了。
    5. 图片处理有两种,一种是opencv4nodejs(node版本大于10.x,我用的是10的最后一版本,最新版本的用的python3.3,编译问题太多),一种是jimp,都有方法实现,我推荐opencv,处理速度快,但是配置环境略复杂。
      资料看官方的npm即可,其余资料参考python调用opencv的方法类似。
    6. 此次主要针对window版本的dll,linux调用方法一样,将路径换位自己的路径即可,文件名为.so

    二、ffi安装及环境安装

    1、安装 Visual Studio 15 生成工具 2017 
    利用 微软自带的安装exe  【vs_BuildTools.exe】
    目录:C:UsersAdministrator.windows-build-tools,脚本安装时,会因为安装包过大和网络问题,导致安装失败。
    
    2、指定编译MSBuild.exe位置,因为2019和上述装的2017都有这个exe,最好使用2017的,2019我安装失败了。
    npm config set msbuild_path "C:Program Files (x86)Microsoft Visual Studio2017BuildToolsMSBuild15.0BinMSBuild.exe"
    
    3、指定python版本,还是建议2.7,其他高版本问题太多,我的3.8.5(不推荐)
    npm config set python C:/Python27
    npm config set python C:/python3.8.5
    
    3、安装gyp
    npm install node-gyp -g
    或者
    npm install --global node-gyp@latest
    
    4、安装ffi ref
    npm install ffi -g
    npm install ref -g
    成功编译
    

    三、opencv4nodejs的安装

    手动安装opencv的环境,设置环境变量
    OPENCV_BIN_DIR=E:commonsoftopencvuildx64vc14in
    OPENCV_INCLUDE_DIR=E:commonsoftopencvuildinclude
    OPENCV_LIB_DIR=E:commonsoftopencvuildx64vc14lib
    
    此处这么安装,成功率90%
    set OPENCV4NODEJS_DISABLE_AUTOBUILD=1
    npm i -g opencv4nodejs
    
    

    四、代码结构

    ├─.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
        └─unit
    
    modual文件夹为人脸识别工作目录
    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 dependencies
    npm install
    
    # serve with hot reload at localhost:9080
    npm run dev
    
    # build electron application for production
    npm run build
    
    # run unit & end-to-end tests
    npm 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_t
    
    4、调用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,将指针拷贝至buffer
    const 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)
    

    八、开源说明
    开源不易,请使用代码时,注明引用,谢谢~
    如果你觉得我的代码帮到了你,请捐赠以表示支持,谢谢~
    在这里插入图片描述

    了解更多人脸识别产品相关内容请到虹软视觉开放平台

  • 相关阅读:
    codebak
    (转)关于APACHE和php
    c#pda(wince)摄像头拍照程序
    C#语言实现WINCE全屏幕显示
    (转).NET反编译工具Reflector及插件
    (转)解决WebService第一次访问速度慢的问题
    (转)C#.net winform skin 皮肤大全
    python模块整理19pyMongo
    python模块整理26数据持久性pickle、shelve、SQLite
    python模块整理21multiprocessing
  • 原文地址:https://www.cnblogs.com/ccLqqy/p/14084571.html
Copyright © 2011-2022 走看看