虹軟人臉識別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:\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:/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:\commonsoft\opencv\build\x64\vc14\bin
OPENCV_INCLUDE_DIR=E:\commonsoft\opencv\build\include
OPENCV_LIB_DIR=E:\commonsoft\opencv\build\x64\vc14\lib

此處這么安裝,成功率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)

八、開源說明
開源不易,請使用代碼時,注明引用,謝謝~
如果你覺得我的代碼幫到了你,請捐贈以表示支持,謝謝~
在這里插入圖片描述

了解更多人臉識別產品相關內容請到虹軟視覺開放平台


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM