一、需求分析

随着人脸识别技术的快速发展,各大高校也开始尝试在新生入校时就通过人脸识别相关技术对新生人脸信息进行采集并录入高校人脸信息库中。在今后的学习生活中,已经录入人脸信息的同学可以轻松的通过人脸识别设备进行人份认证。

人脸识别在高校中的使用场景也十分广泛,例如高校宿舍进出管理,实验室进出管理,图书馆进出管理,图书借阅等。笔者所在学校在信息化建设过程中也充分的发挥了人脸识别的作用,在疫情期间结合人脸识别搭建了一套测温系统。

本项目旨在通过javafx以及webcam和ArcFace等技术搭建一个简单的高校新生人脸信息录入与识别系统。

二、关键技术

javafx:JavaFX 是一个开源的下一代客户端应用平台,适用于基于Java构建的桌面、移动端和嵌入式系统。 它是许多个人和公司的共同努力的成果,目的是为开发丰富的客户端应用提供一个现代、高效、功能齐全的工具包。

webcam:java第三方库,可以调用系统摄像头并进行拍照

ArcFace:虹软提供的离线人脸识别库,版本为3.0。

三、实现目标

  • 可以通过摄像头对用户信息(人脸信息、姓名、学号、专业班级)进行录入,数据使用mysql进行存储。
  • 可以通过人脸对用户信息进行识别

四、效果展示

1、人脸信息录入


java多模态下的人脸是识别_虹软

用户人脸已经录入则提示:


java多模态下的人脸是识别_ArcFace_02

当检测不到人脸信息时:


java多模态下的人脸是识别_java多模态下的人脸是识别_03

录入成功:


java多模态下的人脸是识别_ArcFace_04

数据库:

java多模态下的人脸是识别_ArcFace_05

2、人脸信息识别


java多模态下的人脸是识别_ArcFace_06

识别结果:


java多模态下的人脸是识别_ArcFace_07

无人脸信息则没有反应:


java多模态下的人脸是识别_ArcFace_08

五、代码实现及数据库设计

1、数据库设计

java多模态下的人脸是识别_ArcFace_09

2、流程图

人脸信息录入流程图

java多模态下的人脸是识别_java多模态下的人脸是识别_10

人脸信息识别流程图

java多模态下的人脸是识别_java多模态下的人脸是识别_11

3、代码实现

依赖:

<dependencies>
        <!-- https://mvnrepository.com/artifact/com.github.sarxos/webcam-capture -->
        <dependency>
            <groupId>com.github.sarxos</groupId>
            <artifactId>webcam-capture</artifactId>
            <version>0.3.12</version>
        </dependency>
        <!-- https://mvnrepository.com/artifact/junit/junit -->
        <dependency>
            <groupId>junit</groupId>
            <artifactId>junit</artifactId>
            <version>4.13.2</version>
            <scope>test</scope>
        </dependency>
        <!-- https://mvnrepository.com/artifact/cn.hutool/hutool-core -->
        <dependency>
            <groupId>cn.hutool</groupId>
            <artifactId>hutool-all</artifactId>
            <version>5.6.1</version>
        </dependency>
        <!-- https://mvnrepository.com/artifact/org.apache.commons/commons-lang3 -->
        <dependency>
            <groupId>org.apache.commons</groupId>
            <artifactId>commons-lang3</artifactId>
            <version>3.12.0</version>
        </dependency>
        <!-- https://mvnrepository.com/artifact/mysql/mysql-connector-java -->
        <dependency>
            <groupId>mysql</groupId>
            <artifactId>mysql-connector-java</artifactId>
            <version>5.1.6</version>
        </dependency>
        <dependency>
            <groupId>commons-dbutils</groupId>
            <artifactId>commons-dbutils</artifactId>
            <version>1.7</version>
        </dependency>
        <!-- https://mvnrepository.com/artifact/com.mchange/c3p0 -->
        <dependency>
            <groupId>com.mchange</groupId>
            <artifactId>c3p0</artifactId>
            <version>0.9.5.2</version>
        </dependency>
    </dependencies>

工具类:

package zzuli.zw.camera;

import com.arcsoft.face.*;
import com.arcsoft.face.enums.DetectMode;
import com.arcsoft.face.enums.DetectOrient;
import com.arcsoft.face.toolkit.ImageFactory;
import com.arcsoft.face.toolkit.ImageInfo;
import java.io.File;
import java.util.ArrayList;
import java.util.List;

import static com.arcsoft.face.toolkit.ImageFactory.getRGBData;

/**
 * @ClassName FaceUtils
 * @Description TODO
 * @Author wu2wen
 * @Date 2021/7/9 15:00
 * @Version 1.0
 */
public class FaceUtils {
    private  FaceEngine faceEngine;
    private ImageInfo imageInfo;
    private FunctionConfiguration functionConfiguration;
    //根据实际情况进行配置
    private static final String configFile = "C:\\Users\\wu2we\\Desktop\\ArcSoft_ArcFace_Java_Windows_x64_V3.0\\libs\\WIN64";
    public FaceUtils(){
        faceEngine = new FaceEngine(configFile);
        //引擎配置
        EngineConfiguration engineConfiguration = new EngineConfiguration();
        engineConfiguration.setDetectMode(DetectMode.ASF_DETECT_MODE_IMAGE);
        engineConfiguration.setDetectFaceOrientPriority(DetectOrient.ASF_OP_ALL_OUT);
        engineConfiguration.setDetectFaceMaxNum(10);
        engineConfiguration.setDetectFaceScaleVal(16);
        //功能配置
        functionConfiguration = new FunctionConfiguration();
        functionConfiguration.setSupportAge(true);
        functionConfiguration.setSupportFace3dAngle(true);
        functionConfiguration.setSupportFaceDetect(true);
        functionConfiguration.setSupportFaceRecognition(true);
        functionConfiguration.setSupportGender(true);
        functionConfiguration.setSupportLiveness(true);
        functionConfiguration.setSupportIRLiveness(true);
        engineConfiguration.setFunctionConfiguration(functionConfiguration);
        faceEngine.init(engineConfiguration);
    }
    public FaceUtils(String imageUrl){
        imageInfo = getRGBData(new File(imageUrl));
        faceEngine = new FaceEngine(configFile);
        //引擎配置
        EngineConfiguration engineConfiguration = new EngineConfiguration();
        engineConfiguration.setDetectMode(DetectMode.ASF_DETECT_MODE_IMAGE);
        engineConfiguration.setDetectFaceOrientPriority(DetectOrient.ASF_OP_ALL_OUT);
        engineConfiguration.setDetectFaceMaxNum(10);
        engineConfiguration.setDetectFaceScaleVal(16);
        //功能配置
        functionConfiguration = new FunctionConfiguration();
        functionConfiguration.setSupportAge(true);
        functionConfiguration.setSupportFace3dAngle(true);
        functionConfiguration.setSupportFaceDetect(true);
        functionConfiguration.setSupportFaceRecognition(true);
        functionConfiguration.setSupportGender(true);
        functionConfiguration.setSupportLiveness(true);
        engineConfiguration.setFunctionConfiguration(functionConfiguration);
        faceEngine.init(engineConfiguration);
    }

    public FaceEngine getFaceEngine() {
        return faceEngine;
    }

    public void setImageInfo(String imageUrl) {
        this.imageInfo = getRGBData(new File(imageUrl));
    }
    public float compareTo(byte[] byte1,byte[] byte2){
        //特征比对
        FaceFeature targetFaceFeature = new FaceFeature();
        targetFaceFeature.setFeatureData(byte1);
        FaceFeature sourceFaceFeature = new FaceFeature();
        sourceFaceFeature.setFeatureData(byte2);
        FaceSimilar faceSimilar = new FaceSimilar();
        int errorCode = faceEngine.compareFaceFeature(targetFaceFeature, sourceFaceFeature, faceSimilar);
        if (errorCode != ResponseCode.MOK)return -1;
        System.out.println("相似度:" + faceSimilar.getScore());
        return faceSimilar.getScore();
    }
    /**
     * @Date: 2021/7/9 16:43
     * @Author 索半斤
     * @Description: 获取人脸信息
     * @MethodName:
     */
    public  List<FaceInfo> getFaceInfo(){
        if (imageInfo == null)return null;
        List<FaceInfo> faceInfoList = new ArrayList<>();
        int errorCode = faceEngine.detectFaces(imageInfo.getImageData(), imageInfo.getWidth(), imageInfo.getHeight(), imageInfo.getImageFormat(), faceInfoList);
        if (errorCode != ResponseCode.MOK)return null;
        if (faceInfoList.size() == 0)return null;
        return faceInfoList;
    }
    /**
     * @Date: 2021/7/9 16:43
     * @Author 索半斤
     * @Description: 检测是否为活体
     * @MethodName:
     */
    public  boolean isLive(){
        if (imageInfo == null)return false;
        faceEngine.setLivenessParam(0.5f, 0.7f);
        FunctionConfiguration configuration = new FunctionConfiguration();
        configuration.setSupportAge(true);
        configuration.setSupportFace3dAngle(true);
        configuration.setSupportGender(true);
        configuration.setSupportLiveness(true);
        List<FaceInfo> faceInfo = getFaceInfo();
        int errorCode = faceEngine.process(imageInfo.getImageData(), imageInfo.getWidth(), imageInfo.getHeight(), imageInfo.getImageFormat(), faceInfo, configuration);
        if (errorCode != ResponseCode.MOK)return false;
        //活体检测
        List<LivenessInfo> livenessInfoList = new ArrayList<>();
        errorCode = faceEngine.getLiveness(livenessInfoList);
        if (errorCode != ResponseCode.MOK)return false;
        if (livenessInfoList.size() == 0)return false;
        return livenessInfoList.get(0).getLiveness() == 1;
    }
    /**
     * @Date: 2021/7/9 15:46
     * @Author 索半斤
     * @Description: 获取人脸特征值
     * @MethodName:
     */
    public byte[] getFaceFeature(){
        if (imageInfo == null)return null;
        if (!isLive())return null;
        List<FaceInfo> faceInfoList = getFaceInfo();
        if (faceInfoList == null || faceInfoList.size() == 0)return null;
        //特征提取
        FaceFeature faceFeature = new FaceFeature();
        int errorCode = faceEngine.extractFaceFeature(imageInfo.getImageData(), imageInfo.getWidth(), imageInfo.getHeight(), imageInfo.getImageFormat(), faceInfoList.get(0), faceFeature);
        if (errorCode != ResponseCode.MOK)return null;
        return faceFeature.getFeatureData();
    }

    public void unInit(){
        faceEngine.unInit();
    }
}

人脸信息录入:

package zzuli.zw.camera;

import com.github.sarxos.webcam.Webcam;
import com.github.sarxos.webcam.WebcamResolution;
import javafx.application.Application;
import javafx.application.Platform;
import javafx.beans.property.ObjectProperty;
import javafx.beans.property.SimpleObjectProperty;
import javafx.beans.value.ObservableValue;
import javafx.collections.FXCollections;
import javafx.collections.ObservableList;
import javafx.concurrent.Task;
import javafx.embed.swing.SwingFXUtils;
import javafx.geometry.Insets;
import javafx.geometry.Orientation;
import javafx.geometry.Pos;
import javafx.scene.Scene;
import javafx.scene.control.*;
import javafx.scene.image.Image;
import javafx.scene.image.ImageView;
import javafx.scene.layout.BorderPane;
import javafx.scene.layout.FlowPane;
import javafx.scene.layout.HBox;
import javafx.scene.layout.VBox;
import javafx.stage.Stage;
import org.apache.commons.lang3.StringUtils;
import javax.imageio.ImageIO;
import java.awt.image.BufferedImage;
import java.io.File;
import java.io.IOException;
import java.util.List;

public class MyCameraGetImage extends Application {
    /**
     * 拍照存储的文件路径
     */
    String filePath = "C:/image/";

    private static class WebCamInfo {
        private String webCamName;
        private int webCamIndex;

        public String getWebCamName() {
            return webCamName;
        }

        public void setWebCamName(String webCamName) {
            this.webCamName = webCamName;
        }

        public int getWebCamIndex() {
            return webCamIndex;
        }

        public void setWebCamIndex(int webCamIndex) {
            this.webCamIndex = webCamIndex;
        }
    }

    private FlowPane bottomCameraControlPane;
    private FlowPane topPane;
    private ImageView imgWebCamCapturedImage;
    private Webcam webCam = null;
    private boolean stopCamera = false;
    private BufferedImage grabbedImage;
    private ObjectProperty<Image> imageProperty = new SimpleObjectProperty<>();
    private BorderPane webCamPane;

    @Override
    public void start(Stage primaryStage) {
        primaryStage.setTitle("摄像");
        BorderPane root = new BorderPane();
        topPane = new FlowPane();
        topPane.setAlignment(Pos.CENTER);
        topPane.setHgap(20);
        topPane.setOrientation(Orientation.HORIZONTAL);
        topPane.setPrefHeight(40);
        root.setTop(topPane);
        webCamPane = new BorderPane();
        webCamPane.setStyle("-fx-background-color: #ccc;");
        imgWebCamCapturedImage = new ImageView();
        webCamPane.setCenter(imgWebCamCapturedImage);
        root.setCenter(webCamPane);
        createTopPanel();
        bottomCameraControlPane = new FlowPane();
        bottomCameraControlPane.setOrientation(Orientation.HORIZONTAL);
        bottomCameraControlPane.setAlignment(Pos.CENTER);
        bottomCameraControlPane.setHgap(20);
        bottomCameraControlPane.setVgap(10);
        bottomCameraControlPane.setPrefHeight(40);
        bottomCameraControlPane.setDisable(true);
        createCameraControls();
        root.setBottom(bottomCameraControlPane);
        primaryStage.setScene(new Scene(root));
        primaryStage.setHeight(700);
        primaryStage.setWidth(600);
        primaryStage.centerOnScreen();
        primaryStage.show();
        Platform.runLater(this::setImageViewSize);
    }

    protected void setImageViewSize() {
        double height = webCamPane.getHeight();
        double width = webCamPane.getWidth();
        imgWebCamCapturedImage.setFitHeight(height);
        imgWebCamCapturedImage.setFitWidth(width);
        imgWebCamCapturedImage.prefHeight(height);
        imgWebCamCapturedImage.prefWidth(width);
        imgWebCamCapturedImage.setPreserveRatio(true);
    }

    private void createTopPanel() {
        int webCamCounter = 0;
        Label lbInfoLabel = new Label("选择摄像头:");
        ObservableList<WebCamInfo> options = FXCollections.observableArrayList();
        topPane.getChildren().add(lbInfoLabel);
        for (Webcam webcam : Webcam.getWebcams()) {
            WebCamInfo webCamInfo = new WebCamInfo();
            webCamInfo.setWebCamIndex(webCamCounter);
            webCamInfo.setWebCamName(webcam.getName());
            options.add(webCamInfo);
            webCamCounter++;
        }

        ComboBox<WebCamInfo> cameraOptions = new ComboBox<>();
        cameraOptions.setItems(options);
        String cameraListPromptText = "选择摄像头:";
        cameraOptions.setPromptText(cameraListPromptText);
        cameraOptions.getSelectionModel().selectedItemProperty().addListener((ObservableValue<? extends WebCamInfo> arg0, WebCamInfo arg1, WebCamInfo arg2) -> {
            if (arg2 != null) {
                initializeWebCam(arg2.getWebCamIndex());
            }
        });
        topPane.getChildren().add(cameraOptions);
    }

    protected void initializeWebCam(final int webCamIndex) {
        Task<Void> webCamTask = new Task<Void>() {
            @Override
            protected Void call() {
                webCam = Webcam.getWebcams().get(webCamIndex);
                webCam.setViewSize(WebcamResolution.VGA.getSize());
                webCam.open();
                startWebCamStream();
                return null;
            }
        };
        Thread webCamThread = new Thread(webCamTask);
        webCamThread.setDaemon(true);
        webCamThread.start();
        bottomCameraControlPane.setDisable(false);
    }
    protected void startWebCamStream() {
        stopCamera = false;
        Task<Void> task = new Task<Void>() {
            @Override
            protected Void call() {
                while (!stopCamera) {
                    try {
                        if ((grabbedImage = webCam.getImage()) != null) {
                            Platform.runLater(() -> {
                                Image mainImage = SwingFXUtils.toFXImage(grabbedImage, null);
                                imageProperty.set(mainImage);
                            });
                            grabbedImage.flush();
                        }
                    } catch (Exception e) {
                        e.printStackTrace();
                    }
                }
                return null;
            }
        };
        Thread th = new Thread(task);
        th.setDaemon(true);
        th.start();
        imgWebCamCapturedImage.imageProperty().bind(imageProperty);
    }
    private void createCameraControls() {
        Button banCameraGetImage = new Button();
        banCameraGetImage.setOnAction(event -> getImagine());
        banCameraGetImage.setText("拍照采集");
        bottomCameraControlPane.getChildren().add(banCameraGetImage);
    }
    protected void getImagine() {
        Image image = imgWebCamCapturedImage.getImage();
        ImageView imageView = new ImageView(image);
        Label label = new Label("姓      名:");
        TextField textField = new TextField();
        textField.setPromptText("例:张三");
        HBox hBox = new HBox(5);
        hBox.setAlignment(Pos.CENTER);
        hBox.getChildren().addAll(label, textField);
        Label label2 = new Label("专业年级:");
        TextField textField2 = new TextField();
        textField2.setPromptText("例:软件工程18-04");
        HBox hBox2 = new HBox(5);
        hBox2.setAlignment(Pos.CENTER);
        hBox2.getChildren().addAll(label2, textField2);
        Label label3 = new Label("学      号:");
        TextField textField3 = new TextField();
        textField3.setPromptText("例:541813460400");
        HBox hBox3 = new HBox(5);
        hBox3.setAlignment(Pos.CENTER);
        hBox3.getChildren().addAll(label3, textField3);
        Button button = new Button("保存");
        Stage stage = new Stage();
        stage.setTitle("信息录入");
        button.setOnAction(event -> {
            String name = textField.getText();
            String student = textField2.getText();
            String grade = textField3.getText();
            Alert alert = new Alert(Alert.AlertType.WARNING);
            if (StringUtils.isEmpty(name) || StringUtils.isEmpty(student) || StringUtils.isEmpty(grade)){
                alert.setContentText("必选项不能为空!");
                alert.showAndWait();
            }else {
                try {
                    File file = new File(filePath + student + ".png");
                    ImageIO.write(SwingFXUtils.fromFXImage(image, null), "png", file);
                    FaceUtils faceUtils = new FaceUtils();
                    faceUtils.setImageInfo(filePath + student + ".png");
                    byte[] faceFeature = faceUtils.getFaceFeature();
                    if (faceFeature == null){
                        alert.setContentText("人脸信息采集失败,请确定镜头中已包含人脸!");
                        alert.showAndWait();
                    }else {
                        alert.setAlertType(Alert.AlertType.INFORMATION);
                        alert.setHeaderText("正在采集");
                        alert.show();
                        Student studentInfo = new Student();
                        studentInfo.setFeature(faceFeature);
                        studentInfo.setGrade(grade);
                        studentInfo.setName(name);
                        studentInfo.setStudent(student);
                        studentInfo.setImage(filePath + student + ".png");
                        InfoMapper infoMapper = new InfoMapper();
                        List<Student> allInfo = infoMapper.findAllInfo();
                        boolean flag = false;
                        for (Student student1 : allInfo) {
                            byte[] feature = student1.getFeature();
                            float compare = faceUtils.compareTo(faceFeature, feature);
                            if (compare >= 0.99) {
                                flag = true;
                                break;
                            }
                        }
                        if (flag){
                            alert.setHeaderText("人脸已经录入过了!");
                            alert.show();
                        }else {
                            int i = infoMapper.insertInfo(studentInfo);
                            alert.hide();
                            if (i <= 0) {
                                alert.setHeaderText("采集失败");
                                alert.showAndWait();
                            } else {
                                alert.setHeaderText("采集成功");
                                alert.showAndWait();
                                stage.close();
                            }
                        }
                    }
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }
        });
        VBox vBox = new VBox(10);
        vBox.setAlignment(Pos.CENTER);
        vBox.setPadding(new Insets(10,10,10,10));
        vBox.getChildren().addAll(imageView, hBox,hBox2,hBox3,button);
        stage.setScene(new Scene(vBox));
        stage.show();
    }

    public static void main(String[] args) {
        launch(args);
    }
}

人脸信息识别:

package zzuli.zw.camera;

import com.github.sarxos.webcam.Webcam;
import com.github.sarxos.webcam.WebcamEvent;
import com.github.sarxos.webcam.WebcamListener;
import com.github.sarxos.webcam.WebcamResolution;
import javafx.application.Application;
import javafx.application.Platform;
import javafx.beans.property.ObjectProperty;
import javafx.beans.property.SimpleObjectProperty;
import javafx.beans.value.ObservableValue;
import javafx.collections.FXCollections;
import javafx.collections.ObservableList;
import javafx.concurrent.Task;
import javafx.embed.swing.SwingFXUtils;
import javafx.geometry.Insets;
import javafx.geometry.Orientation;
import javafx.geometry.Pos;
import javafx.scene.Scene;
import javafx.scene.control.*;
import javafx.scene.image.Image;
import javafx.scene.image.ImageView;
import javafx.scene.layout.BorderPane;
import javafx.scene.layout.FlowPane;
import javafx.scene.layout.HBox;
import javafx.scene.layout.VBox;
import javafx.stage.Stage;
import org.apache.commons.lang3.StringUtils;

import javax.imageio.ImageIO;
import java.awt.image.BufferedImage;
import java.io.File;
import java.io.FileInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.util.*;

public class MyCameraConfig extends Application {
    /**
     * 拍照存储的文件路径
     */
    String filePath = "C:/image/";

    private static class WebCamInfo {
        private String webCamName;
        private int webCamIndex;

        public String getWebCamName() {
            return webCamName;
        }

        public void setWebCamName(String webCamName) {
            this.webCamName = webCamName;
        }

        public int getWebCamIndex() {
            return webCamIndex;
        }

        public void setWebCamIndex(int webCamIndex) {
            this.webCamIndex = webCamIndex;
        }
    }

    private FlowPane bottomCameraControlPane;
    private FlowPane topPane;
    private ImageView imgWebCamCapturedImage;
    private Webcam webCam = null;
    private boolean stopCamera = false;
    private BufferedImage grabbedImage;
    private ObjectProperty<Image> imageProperty = new SimpleObjectProperty<>();
    private BorderPane webCamPane;

    @Override
    public void start(Stage primaryStage) {
        primaryStage.setTitle("摄像");
        BorderPane root = new BorderPane();
        topPane = new FlowPane();
        topPane.setAlignment(Pos.CENTER);
        topPane.setHgap(20);
        topPane.setOrientation(Orientation.HORIZONTAL);
        topPane.setPrefHeight(40);
        root.setTop(topPane);
        webCamPane = new BorderPane();
        webCamPane.setStyle("-fx-background-color: #ccc;");
        imgWebCamCapturedImage = new ImageView();
        webCamPane.setCenter(imgWebCamCapturedImage);
        root.setCenter(webCamPane);
        createTopPanel();
        bottomCameraControlPane = new FlowPane();
        bottomCameraControlPane.setOrientation(Orientation.HORIZONTAL);
        bottomCameraControlPane.setAlignment(Pos.CENTER);
        bottomCameraControlPane.setHgap(20);
        bottomCameraControlPane.setVgap(10);
        bottomCameraControlPane.setPrefHeight(40);
        bottomCameraControlPane.setDisable(true);
        createCameraControls();
        root.setBottom(bottomCameraControlPane);
        primaryStage.setScene(new Scene(root));
        primaryStage.setHeight(700);
        primaryStage.setWidth(600);
        primaryStage.centerOnScreen();
        primaryStage.show();
        Platform.runLater(this::setImageViewSize);
    }

    protected void setImageViewSize() {
        double height = webCamPane.getHeight();
        double width = webCamPane.getWidth();
        imgWebCamCapturedImage.setFitHeight(height);
        imgWebCamCapturedImage.setFitWidth(width);
        imgWebCamCapturedImage.prefHeight(height);
        imgWebCamCapturedImage.prefWidth(width);
        imgWebCamCapturedImage.setPreserveRatio(true);
    }

    private void createTopPanel() {
        int webCamCounter = 0;
        Label lbInfoLabel = new Label("选择摄像头:");
        ObservableList<WebCamInfo> options = FXCollections.observableArrayList();
        topPane.getChildren().add(lbInfoLabel);
        for (Webcam webcam : Webcam.getWebcams()) {
            WebCamInfo webCamInfo = new WebCamInfo();
            webCamInfo.setWebCamIndex(webCamCounter);
            webCamInfo.setWebCamName(webcam.getName());
            options.add(webCamInfo);
            webCamCounter++;
        }

        ComboBox<WebCamInfo> cameraOptions = new ComboBox<>();
        cameraOptions.setItems(options);
        String cameraListPromptText = "选择摄像头:";
        cameraOptions.setPromptText(cameraListPromptText);
        cameraOptions.getSelectionModel().selectedItemProperty().addListener((ObservableValue<? extends WebCamInfo> arg0, WebCamInfo arg1, WebCamInfo arg2) -> {
            if (arg2 != null) {
                initializeWebCam(arg2.getWebCamIndex());
            }
        });
        topPane.getChildren().add(cameraOptions);
    }

    protected void initializeWebCam(final int webCamIndex) {
        Task<Void> webCamTask = new Task<Void>() {
            @Override
            protected Void call() {
                webCam = Webcam.getWebcams().get(webCamIndex);
                webCam.setViewSize(WebcamResolution.VGA.getSize());
                webCam.open();
                startWebCamStream();
                return null;
            }
        };
        Thread webCamThread = new Thread(webCamTask);
        webCamThread.setDaemon(true);
        webCamThread.start();
        bottomCameraControlPane.setDisable(false);
    }

    protected void startWebCamStream() {
        stopCamera = false;
        Task<Void> task = new Task<Void>() {
            @Override
            protected Void call() {
                while (!stopCamera) {
                    try {
                        if ((grabbedImage = webCam.getImage()) != null) {
                            Platform.runLater(() -> {
                                Image mainImage = SwingFXUtils.toFXImage(grabbedImage, null);

                                imageProperty.set(mainImage);
                            });
                            grabbedImage.flush();
                        }
                    } catch (Exception e) {
                        e.printStackTrace();
                    }
                }
                return null;
            }
        };
        Thread th = new Thread(task);
        th.setDaemon(true);
        th.start();
        imgWebCamCapturedImage.imageProperty().bind(imageProperty);
        Timer timer = new Timer();
        timer.schedule(new TimerTask() {
            @Override
            public void run() {
                System.out.println("hello");
                Platform.runLater(()->{
                    getImagine();
                    timer.cancel();
                });
            }
        }, 2000);

    }

    private void createCameraControls() {
    }

    protected void getImagine() {
        Image image = imgWebCamCapturedImage.getImage();
        ImageView imageView = new ImageView();
        Label label = new Label("姓      名:");
        TextField textField = new TextField();
        HBox hBox = new HBox(5);
        hBox.setAlignment(Pos.CENTER);
        hBox.getChildren().addAll(label, textField);
        Label label2 = new Label("专业年级:");
        TextField textField2 = new TextField();
        HBox hBox2 = new HBox(5);
        hBox2.setAlignment(Pos.CENTER);
        hBox2.getChildren().addAll(label2, textField2);
        Label label3 = new Label("学      号:");
        TextField textField3 = new TextField();
        Label label4 = new Label("相   似  度:");
        TextField textField4 = new TextField();
        HBox hBox3 = new HBox(5);
        hBox3.setAlignment(Pos.CENTER);
        hBox3.getChildren().addAll(label3, textField3);
        HBox hBox4 = new HBox(5);
        hBox4.setAlignment(Pos.CENTER);
        hBox4.getChildren().addAll(label4, textField4);
        Stage stage = new Stage();
        stage.setOnHiding(event -> {
           Timer timer = new Timer();
           timer.schedule(new TimerTask() {
               @Override
               public void run() {
                   System.out.println("hello");
                   Platform.runLater(()->{
                       getImagine();
                       timer.cancel();
                   });
               }
           }, 2000);
        });
        Alert alert = new Alert(Alert.AlertType.WARNING);
        VBox vBox = new VBox(10);
        try {
            File file = new File(filePath + "temp" + ".png");
            ImageIO.write(SwingFXUtils.fromFXImage(image, null), "png", file);
            FaceUtils faceUtils = new FaceUtils();
            faceUtils.setImageInfo(filePath + "temp"+ ".png");
            byte[] faceFeature = faceUtils.getFaceFeature();
            if (faceFeature == null) {
                Timer timer = new Timer();
                timer.schedule(new TimerTask() {
                    @Override
                    public void run() {
                        System.out.println("hello");
                        Platform.runLater(()->{
                            getImagine();
                            timer.cancel();
                        });
                    }
                }, 2000);
            } else {
                Student studentInfo = new Student();
                studentInfo.setFeature(faceFeature);
                InfoMapper infoMapper = new InfoMapper();
                List<Student> allInfo = infoMapper.findAllInfo();
                Map<Float,Student> map = new TreeMap<>((o1, o2) -> (int)(o2-o1));
                for (Student student1 : allInfo) {
                    byte[] feature = student1.getFeature();
                    float compare = faceUtils.compareTo(faceFeature, feature);
                    map.put(compare, student1);
                }
                Set<Float> keySet = map.keySet();
                for (Float aFloat : keySet) {
                    Student student = map.get(aFloat);
                    textField.setText(student.getName());
                    textField2.setText(student.getGrade());
                    textField3.setText(student.getStudent());
                    textField4.setText(aFloat.toString());
                    InputStream inputStream = new FileInputStream(student.getImage());
                    imageView.setImage(new Image(inputStream));
                    inputStream.close();
                    break;
                }
                vBox.setAlignment(Pos.CENTER);
                vBox.setPadding(new Insets(10, 10, 10, 10));
                vBox.getChildren().addAll(imageView, hBox, hBox2, hBox3,hBox4);
                stage.setScene(new Scene(vBox));
                stage.show();
            }
        } catch (IOException e) {
            e.printStackTrace();
        }
    }
    public static void main(String[] args) {
        launch(args);
    }
}

六、完整代码

https://github.com/suobanjin/javacv

七、设计难点

人脸信息自动识别

在第一版设计的时候我采用的是用户被动识别的方式,当用户点击按钮时对当前界面抓拍并进行信息识别。但是考虑到实际的应用场景,我认为不太方便,于是使用定时任务,定时抓取图片进行人脸识别比对。

Timer timer = new Timer();
timer.schedule(new TimerTask() {
    @Override
    public void run() {
        System.out.println("hello");
        Platform.runLater(()->{
            getImagine();
            timer.cancel();
        });
    }
}, 2000);

在摄像头初始化时设置任务,延迟2s后执行。当用户识别成功后会跳转到用户信息界面,这时我们设置窗口关闭事件:

stage.setOnHiding(event -> {
   Timer timer = new Timer();
   timer.schedule(new TimerTask() {
       @Override
       public void run() {
           System.out.println("hello");
           Platform.runLater(()->{
               getImagine();
               timer.cancel();
           });
       }
   }, 2000);
});

同时,当用户识别不成功时:

if (faceFeature == null) {
    Timer timer = new Timer();
    timer.schedule(new TimerTask() {
        @Override
        public void run() {
            System.out.println("hello");
            Platform.runLater(()->{
                getImagine();
                timer.cancel();
            });
        }
    }, 2000);
}

八、项目总结

总体而言,本项目较为简单。有许多需要改进的地方:

  • webcam使用不熟悉,没有过多查看文档,目前在查看文档以便可以实现人脸标记以及避免人脸重复检测
  • 数据存储有何更高效也是需要考虑的问题,可以看到人脸特征值大概在1kb左右,如果人脸信息库信息较多,那么我们如果将数据库中信息全部查出并比对会浪费很多时间,效率较低。目前考虑是否可以存入文件中进行操作,当然这个还需要作比对,看哪个效率更高一点。