spring boot 集成 人脸识别 arcsoft

关于虹软

  • 虹软是计算机视觉行业领先的算法服务提供商及解决方案供应商,服务于世界各地的客户,将领先的计算机视觉技术商业化应用在智能手机、智能汽车、智能家居、智能零售、互联网视频等领域,并且仍在不断探索新的领域与方向
  • 简单的说它是为各种各样的应用程序提供视觉算法服务的第三方厂商,是一个在计算机视觉技术上优秀的‘轮子’,除此之外他的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. 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>
    
  1. 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 , 是我们为睦寓租房实名认证功能提供后台的人证自动核验时的经验总结,这里感谢虹软免费提供的技术支持


版权声明:本文为shiverLHCC原创文章,遵循CC 4.0 BY-SA版权协议,转载请附上原文出处链接和本声明。