فهرست منبع

对接摄像头

yingp 6 سال پیش
والد
کامیت
95891a80de
12فایلهای تغییر یافته به همراه305 افزوده شده و 58 حذف شده
  1. 0 35
      applications/device/device-client-biometric/src/main/java/com/usoftchina/smartschool/device/client/biometric/control/VideoPanel.java
  2. 113 0
      applications/device/device-client-biometric/src/main/java/com/usoftchina/smartschool/device/client/biometric/controller/CameraController.java
  3. 1 0
      applications/device/device-client-biometric/src/main/java/com/usoftchina/smartschool/device/client/biometric/controller/GateController.java
  4. 1 0
      applications/device/device-client-biometric/src/main/java/com/usoftchina/smartschool/device/client/biometric/controller/PersonController.java
  5. 70 20
      applications/device/device-client-biometric/src/main/java/com/usoftchina/smartschool/device/client/biometric/controller/PersonEditController.java
  6. 56 0
      applications/device/device-client-biometric/src/main/java/com/usoftchina/smartschool/device/client/biometric/controller/ViewController.java
  7. 35 0
      applications/device/device-client-biometric/src/main/java/com/usoftchina/smartschool/device/client/biometric/service/FileService.java
  8. 1 0
      applications/device/device-client-biometric/src/main/java/com/usoftchina/smartschool/device/client/biometric/support/AbstractJavaFxApplicationSupport.java
  9. 3 1
      applications/device/device-client-biometric/src/main/resources/application.yml
  10. 1 1
      applications/device/device-client-biometric/src/main/resources/schema.sql
  11. 23 0
      applications/device/device-client-biometric/src/main/resources/view/camera.fxml
  12. 1 1
      applications/device/device-client-biometric/src/main/resources/view/person_edit.fxml

+ 0 - 35
applications/device/device-client-biometric/src/main/java/com/usoftchina/smartschool/device/client/biometric/control/VideoPanel.java

@@ -1,35 +0,0 @@
-package com.usoftchina.smartschool.device.client.biometric.control;
-
-import com.github.sarxos.webcam.Webcam;
-import com.github.sarxos.webcam.WebcamPanel;
-import com.github.sarxos.webcam.WebcamResolution;
-import javafx.embed.swing.SwingNode;
-
-import javax.swing.*;
-
-/**
- * @author yingp
- * @date 2019/11/15
- */
-public class VideoPanel {
-
-    public SwingNode getVideoPanel() {
-        final SwingNode swingNode = new SwingNode();
-        createSwingContent(swingNode);
-        return swingNode;
-    }
-
-    private void createSwingContent(final SwingNode swingNode) {
-        SwingUtilities.invokeLater(() -> {
-            Webcam webcam = Webcam.getDefault();
-            webcam.setViewSize(WebcamResolution.VGA.getSize());
-            WebcamPanel panel = new WebcamPanel(webcam);
-            panel.setFPSDisplayed(true);
-            panel.setDisplayDebugInfo(true);
-            panel.setImageSizeDisplayed(true);
-            panel.setMirrored(true);
-
-            swingNode.setContent(panel);
-        });
-    }
-}

+ 113 - 0
applications/device/device-client-biometric/src/main/java/com/usoftchina/smartschool/device/client/biometric/controller/CameraController.java

@@ -0,0 +1,113 @@
+package com.usoftchina.smartschool.device.client.biometric.controller;
+
+import com.github.sarxos.webcam.Webcam;
+import com.github.sarxos.webcam.WebcamPanel;
+import com.github.sarxos.webcam.WebcamResolution;
+import com.usoftchina.smartschool.device.client.biometric.control.ParameterizedScene;
+import javafx.application.Platform;
+import javafx.beans.property.ObjectProperty;
+import javafx.embed.swing.SwingFXUtils;
+import javafx.embed.swing.SwingNode;
+import javafx.event.ActionEvent;
+import javafx.fxml.FXML;
+import javafx.scene.Node;
+import javafx.scene.Scene;
+import javafx.scene.image.Image;
+import org.springframework.util.CollectionUtils;
+
+import javax.imageio.ImageIO;
+import javax.swing.*;
+import java.io.File;
+import java.nio.file.Files;
+import java.util.List;
+
+/**
+ * @author yingp
+ * @date 2019-11-18
+ */
+public class CameraController extends ViewController {
+    @FXML
+    public SwingNode optionsNode;
+    @FXML
+    public SwingNode cameraNode;
+
+    private Webcam webcam;
+
+    @Override
+    protected void init() {
+        createSwingContent();
+    }
+
+    @Override
+    protected Node getPrimaryNode() {
+        return cameraNode;
+    }
+
+    @Override
+    public void onWindowClose() {
+        close();
+    }
+
+    private void createSwingContent() {
+        SwingUtilities.invokeLater(() -> {
+            // 这个操作在MacOS下,直接在JavaFX进程下执行会卡住
+            final List<Webcam> webcams = Webcam.getWebcams();
+            if (CollectionUtils.isEmpty(webcams)) {
+                return;
+            }
+            JComboBox<String> camOptions = new JComboBox<>();
+            webcams.forEach(webcam -> {
+                camOptions.addItem(webcam.getName());
+            });
+            optionsNode.setContent(camOptions);
+            camOptions.setSelectedIndex(0);
+            createCamPanel(webcams.get(0));
+            camOptions.addItemListener(e -> {
+                createCamPanel(webcams.get(camOptions.getSelectedIndex()));
+            });
+        });
+    }
+
+    private void createCamPanel(final Webcam webcam) {
+        if (null != webcam && webcam.isOpen()) {
+            webcam.close();
+        }
+        this.webcam = webcam;
+        webcam.setViewSize(WebcamResolution.VGA.getSize());
+        WebcamPanel cam = new WebcamPanel(webcam);
+        cam.setFPSDisplayed(true);
+        cam.setDisplayDebugInfo(true);
+        cam.setImageSizeDisplayed(true);
+        cam.setMirrored(true);
+        cameraNode.setContent(cam);
+    }
+
+    @FXML
+    public void handleSnapshot(ActionEvent actionEvent) {
+        Platform.runLater(() -> {
+            if (null != webcam) {
+                try {
+                    Scene scene = getScene();
+                    if (scene instanceof ParameterizedScene) {
+                        ObjectProperty<File> fileProperty = (ObjectProperty<File>) ((ParameterizedScene) scene).getParams();
+//                        imageObjectProperty.setValue(SwingFXUtils.toFXImage(webcam.getImage(), null));
+                        File tempImageFile = Files.createTempFile("snapshot", "png").toFile();
+                        ImageIO.write(webcam.getImage(), "PNG", tempImageFile);
+                        fileProperty.setValue(tempImageFile);
+                    }
+                    cameraNode.getScene().getWindow().hide();
+                } catch (Exception ex) {
+                    ex.printStackTrace();
+                }
+            }
+        });
+    }
+
+    public void close() {
+        SwingUtilities.invokeLater(() -> {
+            if (null != webcam && webcam.isOpen()) {
+                webcam.close();
+            }
+        });
+    }
+}

+ 1 - 0
applications/device/device-client-biometric/src/main/java/com/usoftchina/smartschool/device/client/biometric/controller/GateController.java

@@ -126,6 +126,7 @@ public class GateController implements Initializable {
             stage.setResizable(false);
             stage.setTitle("人脸闸机设备");
             stage.getIcons().addAll(DeviceClient.defaultIcons);
+            stage.centerOnScreen();
             stage.show();
             stage.showingProperty().addListener(new ChangeListener<Boolean>() {
                 @Override

+ 1 - 0
applications/device/device-client-biometric/src/main/java/com/usoftchina/smartschool/device/client/biometric/controller/PersonController.java

@@ -148,6 +148,7 @@ public class PersonController implements Initializable {
             stage.setResizable(false);
             stage.setTitle("人员信息");
             stage.getIcons().addAll(DeviceClient.defaultIcons);
+            stage.centerOnScreen();
             stage.show();
             stage.showingProperty().addListener(new ChangeListener<Boolean>() {
                 @Override

+ 70 - 20
applications/device/device-client-biometric/src/main/java/com/usoftchina/smartschool/device/client/biometric/controller/PersonEditController.java

@@ -1,14 +1,16 @@
 package com.usoftchina.smartschool.device.client.biometric.controller;
 
-import com.github.sarxos.webcam.Webcam;
 import com.usoftchina.smartschool.device.client.biometric.DeviceClient;
 import com.usoftchina.smartschool.device.client.biometric.control.ParameterizedScene;
-import com.usoftchina.smartschool.device.client.biometric.control.VideoPanel;
 import com.usoftchina.smartschool.device.client.biometric.po.Person;
 import com.usoftchina.smartschool.device.client.biometric.po.SexType;
+import com.usoftchina.smartschool.device.client.biometric.service.FileService;
 import com.usoftchina.smartschool.device.client.biometric.service.PersonService;
 import com.usoftchina.smartschool.device.client.biometric.util.AlertUtils;
+import com.usoftchina.smartschool.device.client.biometric.util.ViewUtils;
 import com.usoftchina.smartschool.device.context.SpringContextHolder;
+import javafx.beans.property.ObjectProperty;
+import javafx.beans.property.SimpleObjectProperty;
 import javafx.beans.value.ChangeListener;
 import javafx.beans.value.ObservableValue;
 import javafx.collections.FXCollections;
@@ -22,15 +24,14 @@ import javafx.scene.control.ComboBox;
 import javafx.scene.control.TextField;
 import javafx.scene.image.Image;
 import javafx.scene.image.ImageView;
-import javafx.scene.layout.StackPane;
 import javafx.stage.FileChooser;
 import javafx.stage.Stage;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
 import org.springframework.util.StringUtils;
 
-import javax.imageio.ImageIO;
 import java.io.File;
+import java.io.IOException;
 import java.net.URL;
 import java.util.ResourceBundle;
 
@@ -55,11 +56,17 @@ public class PersonEditController implements Initializable {
 
     private PersonService personService;
 
+    private FileService fileService;
+
     private Person person;
 
     private FileChooser fileChooser;
 
-    private File imageFile;
+    private File lastSelectedImage;
+
+    private File lastSnapshot;
+
+    private File unsavedImage;
 
     private ObservableList<SexType> sexOptions =
             FXCollections.observableArrayList(
@@ -70,6 +77,7 @@ public class PersonEditController implements Initializable {
     @Override
     public void initialize(URL location, ResourceBundle resources) {
         personService = SpringContextHolder.getContext().getBean(PersonService.class);
+        fileService = SpringContextHolder.getContext().getBean(FileService.class);
 
         nameField.sceneProperty().addListener(new ChangeListener<Scene>() {
             @Override
@@ -104,6 +112,17 @@ public class PersonEditController implements Initializable {
         if (null == person.getSex()) {
             sexBox.getSelectionModel().selectFirst();
         }
+        lastSnapshot = null;
+        lastSelectedImage = null;
+        unsavedImage = null;
+        if (null != person.getFaceImage()) {
+            File imageFile = fileService.getById(person.getFaceImage());
+            if (imageFile.exists()) {
+                imageView.imageProperty().setValue(new Image(imageFile.toURI().toString()));
+            }
+        } else {
+            imageView.imageProperty().setValue(null);
+        }
     }
 
     private void save() {
@@ -115,6 +134,19 @@ public class PersonEditController implements Initializable {
             AlertUtils.warn("请填写卡号").ifPresent(b -> cardNoField.requestFocus());
             return;
         }
+        if (null != unsavedImage) {
+            try {
+                person.setFaceImage(fileService.save(unsavedImage));
+                // 清除临时文件
+                if (null != lastSnapshot) {
+                    lastSnapshot.delete();
+                }
+            } catch (IOException e) {
+                logger.error("Save image error", e);
+            }
+        } else if (null == imageView.getImage()){
+            person.setFaceImage(null);
+        }
         personService.save(person);
     }
 
@@ -141,25 +173,43 @@ public class PersonEditController implements Initializable {
             fileChooser.getExtensionFilters().add(new FileChooser.ExtensionFilter("图片文件", "*.png", "*.jpg", "*.bmp", "*.gif"));
         }
         // 打开上次文件夹
-        if (null != imageFile) {
-            fileChooser.setInitialDirectory(imageFile.getParentFile());
+        if (null != lastSelectedImage) {
+            fileChooser.setInitialDirectory(lastSelectedImage.getParentFile());
         }
-        imageFile = fileChooser.showOpenDialog(nameField.getScene().getWindow());
-        if (null != imageFile) {
-            imageView.setImage(new Image(imageFile.toURI().toString()));
+        lastSelectedImage = fileChooser.showOpenDialog(nameField.getScene().getWindow());
+        if (null != lastSelectedImage) {
+            unsavedImage = lastSelectedImage;
+            imageView.imageProperty().setValue(new Image(unsavedImage.toURI().toString()));
         }
     }
 
     @FXML
-    public void handleOpenVideo(ActionEvent event) {
-        VideoPanel videoPanel = new VideoPanel();
-        StackPane stackPane = new StackPane();
-        stackPane.getChildren().add(videoPanel.getVideoPanel());
-        Stage stage = new Stage();
-        stage.setScene(new Scene(stackPane, 800, 600));
-        stage.setResizable(false);
-        stage.setTitle("人脸采集");
-        stage.getIcons().addAll(DeviceClient.defaultIcons);
-        stage.show();
+    public void handleOpenCamera(ActionEvent event) {
+        try {
+            ObjectProperty<File> fileObjectProperty = new SimpleObjectProperty<>();
+            Scene scene = ViewUtils.load("/view/camera.fxml", fileObjectProperty);
+            Stage stage = new Stage();
+            stage.setScene(scene);
+            stage.setResizable(false);
+            stage.setTitle("人脸采集");
+            stage.getIcons().addAll(DeviceClient.defaultIcons);
+            stage.centerOnScreen();
+            stage.show();
+            fileObjectProperty.addListener(new ChangeListener<File>() {
+                @Override
+                public void changed(ObservableValue<? extends File> observable, File oldValue, File newValue) {
+                    if (null != lastSnapshot) {
+                        lastSnapshot.delete();
+                    }
+                    lastSnapshot = newValue;
+                    if (null != lastSnapshot) {
+                        unsavedImage = lastSnapshot;
+                        imageView.imageProperty().setValue(new Image(unsavedImage.toURI().toString()));
+                    }
+                }
+            });
+        } catch (Exception e) {
+            logger.error("Camera error", e);
+        }
     }
 }

+ 56 - 0
applications/device/device-client-biometric/src/main/java/com/usoftchina/smartschool/device/client/biometric/controller/ViewController.java

@@ -0,0 +1,56 @@
+package com.usoftchina.smartschool.device.client.biometric.controller;
+
+import javafx.beans.value.ChangeListener;
+import javafx.beans.value.ObservableValue;
+import javafx.fxml.Initializable;
+import javafx.scene.Node;
+import javafx.scene.Scene;
+import javafx.stage.Window;
+
+import java.net.URL;
+import java.util.ResourceBundle;
+
+/**
+ * @author yingp
+ * @date 2019-11-18
+ */
+public abstract class ViewController implements Initializable {
+
+    @Override
+    public void initialize(URL location, ResourceBundle resources) {
+        getPrimaryNode().sceneProperty().addListener(new ChangeListener<Scene>() {
+            @Override
+            public void changed(ObservableValue<? extends Scene> observable, Scene oldValue, Scene newValue) {
+                if (null != newValue) {
+                    newValue.windowProperty().addListener(new ChangeListener<Window>() {
+                        @Override
+                        public void changed(ObservableValue<? extends Window> observable, Window oldValue, Window newValue) {
+                            if (null != newValue) {
+                                newValue.showingProperty().addListener(new ChangeListener<Boolean>() {
+                                    @Override
+                                    public void changed(ObservableValue<? extends Boolean> observable, Boolean oldValue, Boolean newValue) {
+                                        if (null == newValue || !newValue.booleanValue()) {
+                                            onWindowClose();
+                                        }
+                                    }
+                                });
+                            }
+                        }
+                    });
+                }
+            }
+        });
+        init();
+    }
+
+    protected abstract void init();
+
+    protected abstract Node getPrimaryNode();
+
+    public void onWindowClose() {
+    }
+
+    public Scene getScene() {
+        return getPrimaryNode().getScene();
+    }
+}

+ 35 - 0
applications/device/device-client-biometric/src/main/java/com/usoftchina/smartschool/device/client/biometric/service/FileService.java

@@ -0,0 +1,35 @@
+package com.usoftchina.smartschool.device.client.biometric.service;
+
+import com.usoftchina.smartschool.device.client.biometric.util.RandomUtils;
+import org.springframework.beans.factory.annotation.Value;
+import org.springframework.stereotype.Service;
+
+import java.io.File;
+import java.io.IOException;
+import java.nio.file.Files;
+import java.nio.file.Paths;
+
+/**
+ * @author yingp
+ * @date 2019-11-18
+ */
+@Service
+public class FileService {
+
+    @Value("${assert.path}")
+    private String assertPath;
+
+    public File getById(String assertId) {
+        return new File(assertPath, assertId);
+    }
+
+    public String save(File file) throws IOException {
+        String assertId = RandomUtils.randomString();
+        File parent = new File(assertPath);
+        if (!parent.exists()) {
+            Files.createDirectory(parent.toPath());
+        }
+        Files.copy(file.toPath(), Paths.get(assertPath, assertId));
+        return assertId;
+    }
+}

+ 1 - 0
applications/device/device-client-biometric/src/main/java/com/usoftchina/smartschool/device/client/biometric/support/AbstractJavaFxApplicationSupport.java

@@ -105,6 +105,7 @@ public abstract class AbstractJavaFxApplicationSupport extends Application {
         try {
             Scene scene = ViewUtils.load(viewPath);
             primaryStage.setScene(scene);
+            primaryStage.centerOnScreen();
             primaryStage.show();
         } catch (Exception e) {
             e.printStackTrace();

+ 3 - 1
applications/device/device-client-biometric/src/main/resources/application.yml

@@ -29,4 +29,6 @@ spring:
 logging:
   path: ${user.home}/.device-client/logs/
   level:
-    com.usoftchina.smartschool: debug
+    com.usoftchina.smartschool: debug
+assert:
+  path: ${user.home}/.device-client/assert/

+ 1 - 1
applications/device/device-client-biometric/src/main/resources/schema.sql

@@ -34,5 +34,5 @@ clazz varchar(100),
 cardNo varchar(20) not null,
 sex int,
 createTime datetime,
-faceImage clob
+faceImage varchar(100)
 );

+ 23 - 0
applications/device/device-client-biometric/src/main/resources/view/camera.fxml

@@ -0,0 +1,23 @@
+<?xml version="1.0" encoding="UTF-8"?>
+
+<?import javafx.scene.control.*?>
+<?import javafx.scene.layout.*?>
+
+<?import javafx.embed.swing.SwingNode?>
+<BorderPane xmlns="http://javafx.com/javafx"
+            xmlns:fx="http://javafx.com/fxml"
+            fx:controller="com.usoftchina.smartschool.device.client.biometric.controller.CameraController"
+            prefHeight="600.0" prefWidth="800.0">
+    <top>
+        <AnchorPane prefHeight="40" prefWidth="800">
+            <children>
+                <Label layoutX="20" layoutY="12" text="选择摄像头"/>
+                <SwingNode fx:id="optionsNode" layoutX="100" layoutY="8"/>
+                <Button layoutX="735" layoutY="7" text="采集" onAction="#handleSnapshot"/>
+            </children>
+        </AnchorPane>
+    </top>
+    <center>
+        <SwingNode fx:id="cameraNode"/>
+    </center>
+</BorderPane>

+ 1 - 1
applications/device/device-client-biometric/src/main/resources/view/person_edit.fxml

@@ -43,7 +43,7 @@
         <ImageView fx:id="imageView" fitHeight="155.0" fitWidth="210.0" layoutX="340.0" layoutY="65.0" pickOnBounds="true" preserveRatio="true" />
         <HBox alignment="CENTER_LEFT" layoutX="340.0" layoutY="30.0" prefHeight="20.0" prefWidth="210.0" spacing="10" style="-fx-background-color: transparent">
             <Button onAction="#handleChoosePicture" prefHeight="20.0" prefWidth="110.0" text="选择本地图片" />
-            <Button onAction="#handleOpenVideo" prefHeight="20.0" prefWidth="100.0" text="打开摄像头" />
+            <Button onAction="#handleOpenCamera" prefHeight="20.0" prefWidth="100.0" text="打开摄像头" />
         </HBox>
     </children>
 </AnchorPane>