• Springboot实现人脸识别与WebSocket长连接的实现


    0.什么是WebSocket,由于普通的请求是间断式发送的,如果要同一时间发生大量的请求,必然导致响应速度慢(因为根据tcp协议要经过三层握手,如果不持续发送,就会导致n多次握手,关闭连接,打开连接)

    1.业务需求: 由于我需要使用java来处理视频的问题,视频其实就是图片,相当于每张图片就是帧,不停发送帧去实现人脸失败,然后返回处理结果,(支付宝刷脸支付也是同样的道理)

    2.前端建立WebSocket()对象,onMessage函数监听返回的结果

    DOCTYPE html>
    <html>
    <head>
        <title>视频帧捕获title>
    head>
    <body>
        <video id="videoElement" autoplay>video>
        <canvas id="canvasElement" style="display: none;">canvas>
    
        <script>
         //如果是https协议的话,就需要改为 wss
               var socket = new WebSocket("ws://localhost:8080/facedetect");
           const video = document.getElementById('videoElement');
            const canvas = document.getElementById('canvasElement');
            const context = canvas.getContext('2d');
        socket.onopen = function() {
              console.log("xxxx");
           // 每1秒发送一次视频帧数据,必须要在这里写定时器,因为打开连接后才能发送请求,不然每次都会报Websocket close的错误
              setInterval(captureFrame,10000)
           
    
        };
          socket.onmessage = function(event) {
        	var result = event.data;
      	  // 处理服务器返回的结果
       	 console.log(result);//打印出结果
          };
    
        socket.onclose = function(event) {
            console.log("WebSocket已关闭");
            
        };
        socket.onerror = function(event) {
      console.error('WebSocket错误:', event);
     };
    
    
            navigator.mediaDevices.getUserMedia({ video: true })
                .then(stream => {
                    video.srcObject = stream;
                })
                .catch(error => {
                    console.error('无法访问摄像头:', error);
                });
    
            function captureFrame() {
                context.drawImage(video, 0, 0, canvas.width, canvas.height);
                
                 const imageDataUrl = canvas.toDataURL('image/jpeg', 0.5);
                 console.log(imageDataUrl) 
                socket.send(imageDataUrl);
              // 将数据URL发送到WebSocket服务器
    
            }
    
            // 每隔一段时间捕获一帧并发送到Servlet
        
        script>
    body>
    html>
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50
    • 51
    • 52
    • 53
    • 54
    • 55
    • 56
    • 57
    • 58
    • 59
    • 60

    3.后端写配置类,配置websocket的路径

    @Configuration
    @EnableWebSocket
    public class WebSocketConfig implements WebSocketConfigurer {
    
        @Override
        public void registerWebSocketHandlers(WebSocketHandlerRegistry registry) {
               //录入人脸数据页面
            registry.addHandler(myHandler(),  "/face").setAllowedOrigins("*");
             //人脸识别页面
            registry.addHandler(myHandler1(), "/facedetect").setAllowedOrigins("*");
        }
    
        @Bean
        public WebSocketHandler myHandler() {
            return new FaceController();
        }
        @Bean
        public WebSocketHandler myHandler1() {
            return new FaceController1();
        }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21

    4.写controller

    //人脸录入的controller
    @Controller
    @RequestMapping("/face")
    @CrossOrigin
    public class FaceController extends TextWebSocketHandler {
    
    private WebSocketSession session;
    
    
        // 处理WebSocket连接请求
        @Override
        public void afterConnectionEstablished(WebSocketSession session) throws Exception {
            System.out.println("WebSocket连接已建立");
    
            // 保存WebSocket会话
            this.session = session;
        }
    
        // 处理WebSocket文本消息
        @Override
        protected void handleTextMessage(WebSocketSession session, TextMessage message) throws Exception {
            String text = message.getPayload();
            System.out.println(text);
            text = text.replaceFirst("^data:image/[^;]+;base64,?\\s*", "");
            text = text.replaceAll("[^A-Za-z0-9+/=]", "");
            System.out.println(text);
            byte[] imageBytes = Base64.getDecoder().decode(text);
            if (imageBytes != null) {
                try {
                    // 读取字节数组并返回BufferedImage对象
                    ByteArrayInputStream bis = new ByteArrayInputStream(imageBytes);
                    BufferedImage bufferedImage = ImageIO.read(bis);
    
                    if (bufferedImage != null) {
                        // 示例:显示图像宽度和高度
                        int width = bufferedImage.getWidth();
                        int height = bufferedImage.getHeight();
                        System.out.println("图像宽度:" + width);
                        System.out.println("图像高度:" + height);
                   //录入人脸
                        Employee e1 = HRService.addEmp(UUID.randomUUID().toString().substring(0,10), bufferedImage);
                        ImageService.saveFaceImage(bufferedImage, e1.getCode());// 保存员工照片文件
                        System.out.println(e1.getCode());
    
    
                        // 在这里可以对BufferedImage对象进行其他操作
                    } else {
                        System.out.println("无法读取图像");
                    }
    
                } catch (Exception e) {
                    e.printStackTrace();
                }
            } else {
                System.out.println("无效的base64数据");
            }
        }
            // 根据接收到的文本消息进行相应的处理
    
    
    
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50
    • 51
    • 52
    • 53
    • 54
    • 55
    • 56
    • 57
    • 58
    • 59
    • 60
    • 61
    • 62
    //人脸检测的控制器
    @Controller
    @RequestMapping("/facedetect")
    @CrossOrigin
    public class FaceController1 extends TextWebSocketHandler {
    private WebSocketSession session;
    
    
        // 处理WebSocket连接请求
        @Override
        public void afterConnectionEstablished(WebSocketSession session) throws Exception {
            System.out.println("WebSocket连接已建立");
    
            // 保存WebSocket会话
            this.session = session;
        }
    
        // 处理WebSocket文本消息
        @Override
        protected void handleTextMessage(WebSocketSession session, TextMessage message) throws Exception {
            System.out.println("detect");
            String text = message.getPayload();
            System.out.println(text);
            text = text.replaceFirst("^data:image/[^;]+;base64,?\\s*", "");
            text = text.replaceAll("[^A-Za-z0-9+/=]", "");
            System.out.println(text);
            byte[] imageBytes = Base64.getDecoder().decode(text);
            if (imageBytes != null) {
                try {
                    // 读取字节数组并返回BufferedImage对象
                    ByteArrayInputStream bis = new ByteArrayInputStream(imageBytes);
                    BufferedImage bufferedImage = ImageIO.read(bis);
    
                    if (bufferedImage != null) {
                        FaceEngineService.loadAllFaceFeature();
                        FaceFeature faceFeature = FaceEngineService.getFaceFeature(bufferedImage);
                        // 获取当前帧中出现的人脸对应的特征码
                      String code = FaceEngineService.detectFace(faceFeature);
                        System.out.println(code);
                        if (code != null) {// 如果特征码不为null,表明画面中存在某员工的人脸
                            Employee e = HRService.getEmp(code);// 根据特征码获取员工对象
                            HRService.addClockInRecord(e);// 为此员工添加打卡记录
                            // 文本域添加提示信息
                            session.sendMessage(new TextMessage("打卡成功"));
    
                        }
    
                        // 在这里可以对BufferedImage对象进行其他操作
                    } else {
                        session.sendMessage(new TextMessage("打卡成功"));
                    }
    
                } catch (Exception e) {
                    e.printStackTrace();
                }
            } else {
                System.out.println("无效的base64数据");
            }
        }
            // 根据接收到的文本消息进行相应的处理
    
    
    
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50
    • 51
    • 52
    • 53
    • 54
    • 55
    • 56
    • 57
    • 58
    • 59
    • 60
    • 61
    • 62
    • 63
    • 64
  • 相关阅读:
    react仿照antd progress实现可自定义颜色的直角矩形进度条
    python:flatten()参数详解
    设计模式入门笔记
    一种使用wireshark快速分析抓包文件amr音频流的思路方法
    从GDPR和个保法看,为什么要做数据合规?
    NURBS曲线-节点插入(原理+代码)
    5.1获取物理内存容量
    【图形学】29 更复杂的光照
    “Python+”集成技术高光谱遥感数据处理与机器学习教程
    wsl下jdk+wsl调试环境
  • 原文地址:https://blog.csdn.net/weixin_47039303/article/details/134232172