關於虹軟
虹軟是計算機視覺行業領先的算法服務提供商及解決方案供應商,服務於世界各地的客戶,將領先的計算機視覺技術商業化應用在智能手機、智能汽車、智能家居、智能零售、互聯網視頻等領域,並且仍在不斷探索新的領域與方向
簡單的說它是為各種各樣的應用程序提供視覺算法服務的第三方廠商,是一個在計算機視覺技術上優秀的‘輪子’,除此之外他的SDK簡單易用,對開發者十分友好
官網地址
集成的前提
javaSE 8+
spring boot 項目
虹軟的SDK
你只需下載人臉識別的sdk即可,該sdk已經包括如 人臉識別,人證核驗,活體檢測等功能
以下是從虹軟下載來的jar包目錄結構
ArcSoft_ArcFace_Java_Windows_x64_V3.0 (1)
+ doc <--- 這里存放的是 ArcFace SDK API 的介紹建議你仔細看一下
+ libs <--- 這里存放的是jar包
+ WIN64 <--- 這里存放的是 dll或者so 文件,這些c++文件主要作為 FaceEngine 的驅動
+ arcsoft-sdk-face-x.x.x.jar
+ samplecode <--- 這里是ArcFace SDK API 的調用案例
1
2
3
4
5
6
集成准備
jar包的引入
虹軟並沒有為spring boot 提供 maven 的引入方式,所以你需要手動將他的jar包集成到本地,不建議你將jar包上傳到私服,因為他的jar包使用是有時效的
首先在你spring boot 項目中,src 同級目錄下創建libs 文件夾,將虹軟的jar包放到這個文件中
其次在 pom.xml 文件中將這個jar包引入到項目中
<dependency>
<groupId>com.arcsoft.face</groupId>
<artifactId>arcsoft-sdk-face</artifactId>
<version>3.0.0.0</version>
<scope>system</scope>
<systemPath>${basedir}/lib/arcsoft-sdk-face-win-3.0.0.0.jar</systemPath>
</dependency>
注意這里你的jar包版本號可能和我不一樣,但是其他的格式必須一致
做完以上的步驟之后還不夠,你還需要在如下插件中做如下配置,這個configuration是允許你的項目在打包發布后仍然可以調用本地路徑下的jar包,如果你沒有設配置,你的項目在打包后將無法正常運行
<build>
<plugins>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
<configuration>
<includeSystemScope>true</includeSystemScope>
</configuration>
</plugin>
</plugins>
</build>
Arcface SDK 主要的對象介紹
com.arcsoft.face.FaceEngine
這個對象是 arcface 的核心,之后的不管是人臉檢測,認證核驗,還是活體檢測都需要依靠這個對象
因此官方要求你在使用的時候必須對這個對象進行很多的配置
com.arcsoft.face.toolkit.ImageInfo
這個對象是用來包裝你的圖片流對象,比如需要檢測人臉的照片,或者證件照
com.arcsoft.face.toolkit.ImageInfoX
這個對象也是用來包裝你的圖片流對象
com.arcsoft.face.FaceInfo
這個對象是用來存放人臉信息的,總之一張臉對應一個FaceInfo
com.arcsoft.face.FaceFeature
這個對象是用來存放FaceInfo中的人臉信息的特征值,之后通過特征值的比較來進行人證核驗
集成思路
想要集成第三方服務,最好的方式使用spring boot 的 configuration 機制,工廠模式創建對象,並交由spring 容器管理,
由於 FaceEngine 需要大量的配置,我們可以將配置分類后寫在application.yaml中
不管是人臉檢測,還是人證核驗都需要通過調用FaceEngine來進行,所以我們還需要對這些操作進行封裝,我建議不要都封裝成utils的靜態工廠方法,可以使用 component 機制將這些操作的封裝對象也交由spring boot 容器來進行管理。
使用utils的靜態工廠方法來處理圖片流
封裝
對FaceEngine的封裝
application.yaml 中設置引擎的配置
arcface: <----- 這個節點的配置對應 ArcFaceConfig
appId: xxxxxxxxxxxxx
sdkKey: xxxxxxxxxxxx
dllPath: C:\arcface\dll <-------------------- 這是你存放dll(win)或so(Linus)的位置
function-configuration: <--------- 這個節點的配置對應 FunConfigurationProperty
supportAge: true
supportFace3dAngle: true
supportFaceDetect: true
supportFaceRecognition: true
supportGender: true
supportLiveness: true
supportIRLiveness: true
engine-configuration: <--------- 這個節點的配置對應 EngineConfigurationProperty
detectMode: IMAGE
detectFaceOrientPriority: ASF_OP_0_ONLY
detectFaceScale: 32
detectFaceMaxNum: 8
FunConfigurationProperty
@Data
@ConfigurationProperties(prefix = "arcface.function-configuration")
public class FunConfigurationProperty {
private boolean supportFace3dAngle = false;
private boolean supportFaceDetect = true;
private boolean supportFaceRecognition = true;
private boolean supportGender = false;
private boolean supportAge = false;
private boolean supportLiveness = true;
private boolean supportIRLiveness = true;
}
EngineConfigurationProperty
@Data
@ConfigurationProperties(prefix = "arcface.engine-configuration")
public class EngineConfigurationProperty {
private String detectMode;
private String detectFaceOrientPriority;
private Integer detectFaceScale;
private Integer detectFaceMaxNum;
}
ArcFaceConfig
@Setter
@Configuration
@ConfigurationProperties(prefix = "arcface")
@EnableConfigurationProperties({
FunConfigurationProperty.class,
EngineConfigurationProperty.class})
public class ArcfaceConfig {
private String appId;
private String sdkKey;
private String dllPath;
@Bean
public FaceEngine faceEngine(){
FaceEngine faceEngine = new FaceEngine(dllPath);
int errorCode = faceEngine.activeOnline(appId, sdkKey);
if (errorCode != ErrorInfo.MOK.getValue() &&
errorCode != ErrorInfo.MERR_ASF_ALREADY_ACTIVATED.getValue())
throw new RuntimeException("引擎注冊失敗");
EngineConfiguration engineConfiguration = getFaceEngineConfiguration();
//初始化引擎
errorCode = faceEngine.init(engineConfiguration);
if (errorCode != ErrorInfo.MOK.getValue())
throw new RuntimeException("初始化引擎失敗");
return faceEngine;
}
/**
* 初始化引擎配置
* @return
*/
private EngineConfiguration getFaceEngineConfiguration() {
EngineConfiguration engineConfiguration = new EngineConfiguration();
//配置引擎模式
if ("VVIDEO".equals(engineConfigurationProperty.getDetectMode()))
engineConfiguration.setDetectMode(DetectMode.ASF_DETECT_MODE_VIDEO);
else
engineConfiguration.setDetectMode(DetectMode.ASF_DETECT_MODE_IMAGE);
//配置人臉角度 全角度 ASF_OP_ALL_OUT 不夠准確且檢測速度慢
switch (engineConfigurationProperty.getDetectFaceOrientPriority()){
case "ASF_OP_0_ONLY":
engineConfiguration
.setDetectFaceOrientPriority(DetectOrient.ASF_OP_0_ONLY);
break;
case "ASF_OP_90_ONLY":
engineConfiguration
.setDetectFaceOrientPriority(DetectOrient.ASF_OP_90_ONLY);
break;
case "ASF_OP_270_ONLY":
engineConfiguration
.setDetectFaceOrientPriority(DetectOrient.ASF_OP_270_ONLY);
break;
case "ASF_OP_180_ONLY":
engineConfiguration
.setDetectFaceOrientPriority(DetectOrient.ASF_OP_180_ONLY);
break;
case "ASF_OP_ALL_OUT":
engineConfiguration
.setDetectFaceOrientPriority(DetectOrient.ASF_OP_ALL_OUT);
break;
default:
engineConfiguration
.setDetectFaceOrientPriority(DetectOrient.ASF_OP_ALL_OUT);
}
//設置識別的最小人臉比 n
engineConfiguration
.setDetectFaceScaleVal(engineConfigurationProperty.getDetectFaceScale());
engineConfiguration
.setDetectFaceMaxNum(engineConfigurationProperty.getDetectFaceMaxNum());
//功能配置
initFuncConfiguration(engineConfiguration);
return engineConfiguration;
}
/**
* 功能配置
* @param engineConfiguration
*/
private void initFuncConfiguration(EngineConfiguration engineConfiguration){
FunctionConfiguration functionConfiguration = new FunctionConfiguration();
//是否支持年齡檢測
functionConfiguration.setSupportAge(funConfigurationProperty.isSupportAge());
//是否支持3d 檢測
functionConfiguration
.setSupportFace3dAngle(funConfigurationProperty.isSupportFace3dAngle());
//是否支持人臉檢測
functionConfiguration
.setSupportFaceDetect(funConfigurationProperty.isSupportFaceDetect());
//是否支持人臉識別
functionConfiguration
.setSupportFaceRecognition(funConfigurationProperty.isSupportFaceRecognition());
//是否支持性別檢測
functionConfiguration
.setSupportGender(funConfigurationProperty.isSupportGender());
//是否支持活體檢測
functionConfiguration
.setSupportLiveness(funConfigurationProperty.isSupportLiveness());
//是否至此IR活體檢測
functionConfiguration
.setSupportIRLiveness(funConfigurationProperty.isSupportIRLiveness());
engineConfiguration.setFunctionConfiguration(functionConfiguration);
}
}
對圖片流處理的靜態工廠方法的封裝
ArcFaceUtils
//這個靜態的jar包需要你手動引入
import static com.arcsoft.face.toolkit.ImageFactory.getRGBData;
public class ArcfaceUtils {
/**
* 處理 File 的圖片流
* @param img
* @return
*/
public static ImageInfoMeta packImageInfoEx(File img){
ImageInfo imageInfo = getRGBData(img);
return packImageInfoMeta(imageInfo);
}
/**
* 處理 byte[] 的圖片流
* @param img
* @return
*/
public static ImageInfoMeta packImageInfoMeta(byte[] img){
ImageInfo imageInfo = getRGBData(img);
return packImageInfoMeta(imageInfo);
}
/**
* 處理 InpuStream 的圖片流
* @param img
* @return
*/
public static ImageInfoMeta packImageInfoMeta(InputStream img){
ImageInfo imageInfo = getRGBData(img);
return packImageInfoMeta(imageInfo);
}
/**
* 打包生成 ImageInfoMeta
* @param imageInfo
* @return
*/
private static ImageInfoMeta packImageInfoMeta(ImageInfo imageInfo){
ImageInfoMeta imageInfoMeta = new ImageInfoMeta(imageInfo);
return imageInfoMeta;
}
/**
* 對imageInfo 和 imageInfoEx 的打包對象
* @param img
* @return
*/
@Data
public static class ImageInfoMeta{
private ImageInfo imageInfo;
private ImageInfoEx imageInfoEx;
public ImageInfoMeta(ImageInfo imageInfo) {
this.imageInfo = imageInfo;
imageInfoEx = new ImageInfoEx();
imageInfoEx.setHeight(imageInfo.getHeight());
imageInfoEx.setWidth(imageInfo.getWidth());
imageInfoEx.setImageFormat(imageInfo.getImageFormat());
imageInfoEx.setImageDataPlanes(new byte[][]{imageInfo.getImageData()});
imageInfoEx.setImageStrides(new int[]{imageInfo.getWidth() * 3});
}
}
}
對人證核驗,人臉檢測等方法的封裝
ArcFaceWorker
@Component
public class ArcfaceWorker {
@Autowired
private FaceEngine faceEngine;
/**
* 人臉檢測
* @return
*/
public List<FaceInfo> detectFace(ImageInfoEx imageInfoEx) {
if (imageInfoEx == null)
return null;
List<FaceInfo> faceInfoList = new ArrayList<FaceInfo>();
int i = faceEngine
.detectFaces(imageInfoEx, DetectModel.ASF_DETECT_MODEL_RGB, faceInfoList);
checkEngineResult(i, ErrorInfo.MOK.getValue(), "人臉檢測失敗");
return faceInfoList;
}
/**
* 特征提取
* @param faceInfoList
* @param imageInfoEx
* @return
*/
public FaceFeature extractFaceFeature(
List<FaceInfo> faceInfoList, ImageInfoEx imageInfoEx) {
if (faceInfoList == null || imageInfoEx == null)
return null;
FaceFeature faceFeature = new FaceFeature();
int i = faceEngine
.extractFaceFeature(imageInfoEx, faceInfoList.get(0), faceFeature);
checkEngineResult(i, ErrorInfo.MOK.getValue(), "人臉特征提取失敗");
return faceFeature;
}
public FaceSimilar compareFaceFeature(
FaceFeature target, FaceFeature source, CompareModel compareModel) {
FaceSimilar faceSimilar = new FaceSimilar();
int i = faceEngine
.compareFaceFeature(target, source, compareModel, faceSimilar);
checkEngineResult(i, ErrorInfo.MOK.getValue(), "人臉特征對比失敗");
return faceSimilar;
}
/**
* 錯誤檢測
* @param errorCode
* @param sourceCode
* @param errMsg
*/
private void checkEngineResult(int errorCode, int sourceCode, String errMsg) {
if (errorCode != sourceCode)
throw new RuntimeException(errMsg);
}
}
測試案例
@RunWith(SpringRunner.class)
@SpringBootTest(classes = DemoApplication.class)
public class ArcfaceConfigTest {
@Autowired
private ArcfaceWorker arcfaceWorker;
@Test
public void testDetectFacesDemo01() {
FaceInfo faceInfoA = getFaceInfo(new File("C:\\Pictures\\CameraRoll\\Me3.jpg"));
int A = faceInfoA.getOrient();
FaceInfo faceInfoB = getFaceInfo(new File("C:\\Pictures\\CameraRoll\\Me4.jpg"));
int B = faceInfoB.getOrient();
FaceInfo faceInfoC = getFaceInfo(new File("C:\\Pictures\\CameraRoll\\Me5.jpg"));
int C = faceInfoC.getOrient();
System.out.println("正臉:"+ A);
System.out.println("左臉:"+ B);
System.out.println("右臉:"+ C);
}
public FaceInfo getFaceInfo(File file){
ArcfaceUtils.ImageInfoMeta imageInfoMeta = ArcfaceUtils.packImageInfoEx(file);
List<FaceInfo> faceInfos = arcfaceWorker
.detectFace(imageInfoMeta.getImageInfoEx());
return faceInfos.get(0);
}
@Test
public void testextractFaceFeature() {
File file = new File("C:\\Users\\14530\\Pictures\\Camera Roll\\idCard02.jpg");
ArcfaceUtils.ImageInfoMeta imageInfoMeta = ArcfaceUtils.packImageInfoEx(file);
List<FaceInfo> faceInfos = arcfaceWorker
.detectFace(imageInfoMeta.getImageInfoEx());
FaceFeature faceFeature = arcfaceWorker
.extractFaceFeature(faceInfos, imageInfoMeta.getImageInfoEx());
System.out.println(faceFeature);
}
@Test
public void testCompareFaceFeature () {
//提取身份證的人臉特征
File fileCard = new File("C:\\Pictures\\Camera Roll\\idCard03.jpg");
ArcfaceUtils.ImageInfoMeta imageInfoMetaCard = ArcfaceUtils
.packImageInfoEx(fileCard);
List<FaceInfo> faceInfosCard = arcfaceWorker
.detectFace(imageInfoMetaCard.getImageInfoEx());
FaceFeature faceFeatureCard = arcfaceWorker
.extractFaceFeature(faceInfosCard, imageInfoMetaCard.getImageInfoEx());
//提取目標的人臉特征
File fileMe = new File("C:\\Camera Roll\\me1.jpg");
ArcfaceUtils.ImageInfoMeta imageInfoMetaMe = ArcfaceUtils
.packImageInfoEx(fileMe);
List<FaceInfo> faceInfosMe = arcfaceWorker
.detectFace(imageInfoMetaMe.getImageInfoEx());
FaceFeature faceFeatureMe = arcfaceWorker
.extractFaceFeature(faceInfosMe, imageInfoMetaMe.getImageInfoEx());
FaceSimilar faceSimilar = arcfaceWorker
.compareFaceFeature(faceFeatureCard, faceFeatureMe, CompareModel.ID_PHOTO);
System.out.println(faceSimilar.getScore());
}
}
結:本次的spring boot 集成虹軟人證檢測的demo , 是我們為睦寓租房實名認證功能提供后台的人證自動核驗時的經驗總結,這里感謝虹軟免費提供的技術支持