JavaCV的摄像头实战之十四:口罩检测

打印 上一主题 下一主题

主题 907|帖子 907|积分 2721

欢迎访问我的GitHub

这里分类和汇总了欣宸的全部原创(含配套源码):https://github.com/zq2599/blog_demos
本篇概览


  • 本文是《JavaCV的摄像头实战》系列的第十四篇,如标题所说,今天的功能是检测摄像头内的人是否带了口罩,把检测结果实时标注在预览窗口,如下图所示:

  • 整个处理流程如下,实现口罩检测的关键是将图片提交到百度AI开放平台,然后根据平台返回的结果在本地预览窗口标识出人脸位置,以及此人是否带了口罩:

问题提前告知


  • 依赖云平台处理业务的一个典型问题,就是处理速度受限
  • 首先,如果您在百度AI开放平台注册的账号是个人类型,那么免费的接口调用会被限制到一秒钟两次,如果是企业类型账号,该限制是十次
  • 其次,经过实测,一次人脸检测接口耗时300ms以上
  • 最终,实际上一秒钟只能处理两帧,这样的效果在预览窗口展现出来,就只能是幻灯片效果了(低于每秒十五帧就能感受到明显的卡顿)
  • 因此,本文只适合基本功能展示,无法作为实际场景的解决方案
关于百度AI开放平台

编码:添加依赖库

  1. <dependency>
  2.         <groupId>com.squareup.okhttp3</groupId>
  3.         <artifactId>okhttp</artifactId>
  4.     <version>3.10.0</version>
  5. </dependency>
  6. <dependency>
  7.         <groupId>com.fasterxml.jackson.core</groupId>
  8.     <artifactId>jackson-databind</artifactId>
  9.     <version>2.11.0</version>
  10. </dependency>
复制代码
编码:封装请求和响应百度AI开放平台的代码


  • 接下来要开发一个服务类,这个服务类封装了所有和百度AI开放平台相关的代码
  • 首先,定义web请求的request对象FaceDetectRequest.java:
  1. package com.bolingcavalry.grabpush.bean.request;
  2. import com.fasterxml.jackson.annotation.JsonProperty;
  3. import lombok.Data;
  4. /**
  5. * @author willzhao
  6. * @version 1.0
  7. * @description 请求对象
  8. * @date 2022/1/1 16:21
  9. */
  10. @Data
  11. public class FaceDetectRequest {
  12.     // 图片信息(总数据大小应小于10M),图片上传方式根据image_type来判断
  13.     String image;
  14.     // 图片类型
  15.     // BASE64:图片的base64值,base64编码后的图片数据,编码后的图片大小不超过2M;
  16.     // URL:图片的 URL地址( 可能由于网络等原因导致下载图片时间过长);
  17.     // FACE_TOKEN: 人脸图片的唯一标识,调用人脸检测接口时,会为每个人脸图片赋予一个唯一的FACE_TOKEN,同一张图片多次检测得到的FACE_TOKEN是同一个。
  18.     @JsonProperty("image_type")
  19.     String imageType;
  20.     // 包括age,expression,face_shape,gender,glasses,landmark,landmark150,quality,eye_status,emotion,face_type,mask,spoofing信息
  21.     //逗号分隔. 默认只返回face_token、人脸框、概率和旋转角度
  22.     @JsonProperty("face_field")
  23.     String faceField;
  24.     // 最多处理人脸的数目,默认值为1,根据人脸检测排序类型检测图片中排序第一的人脸(默认为人脸面积最大的人脸),最大值120
  25.     @JsonProperty("max_face_num")
  26.     int maxFaceNum;
  27.     // 人脸的类型
  28.     // LIVE表示生活照:通常为手机、相机拍摄的人像图片、或从网络获取的人像图片等
  29.     // IDCARD表示身份证芯片照:二代身份证内置芯片中的人像照片
  30.     // WATERMARK表示带水印证件照:一般为带水印的小图,如公安网小图
  31.     // CERT表示证件照片:如拍摄的身份证、工卡、护照、学生证等证件图片
  32.     // 默认LIVE
  33.     @JsonProperty("face_type")
  34.     String faceType;
  35.     // 活体控制 检测结果中不符合要求的人脸会被过滤
  36.     // NONE: 不进行控制
  37.     // LOW:较低的活体要求(高通过率 低攻击拒绝率)
  38.     // NORMAL: 一般的活体要求(平衡的攻击拒绝率, 通过率)
  39.     // HIGH: 较高的活体要求(高攻击拒绝率 低通过率)
  40.     // 默认NONE
  41.     @JsonProperty("liveness_control")
  42.     String livenessControl;
  43.     // 人脸检测排序类型
  44.     // 0:代表检测出的人脸按照人脸面积从大到小排列
  45.     // 1:代表检测出的人脸按照距离图片中心从近到远排列
  46.     // 默认为0
  47.     @JsonProperty("face_sort_type")
  48.     int faceSortType;
  49. }
复制代码

  • 其次,定义web响应对象FaceDetectResponse.java:
  1. package com.bolingcavalry.grabpush.bean.response;
  2. import com.fasterxml.jackson.annotation.JsonProperty;
  3. import lombok.Data;
  4. import lombok.ToString;
  5. import java.io.Serializable;
  6. import java.util.List;
  7. @Data
  8. @ToString
  9. public class FaceDetectResponse implements Serializable {
  10.     // 返回码
  11.     @JsonProperty("error_code")
  12.     String errorCode;
  13.     // 描述信息
  14.     @JsonProperty("error_msg")
  15.     String errorMsg;
  16.     // 返回的具体内容
  17.     Result result;
  18.     @Data
  19.     public static class Result {
  20.         // 人脸数量
  21.         @JsonProperty("face_num")
  22.         private int faceNum;
  23.         // 每个人脸的信息
  24.         @JsonProperty("face_list")
  25.         List<Face> faceList;
  26.         /**
  27.          * @author willzhao
  28.          * @version 1.0
  29.          * @description 检测出来的人脸对象
  30.          * @date 2022/1/1 16:03
  31.          */
  32.         @Data
  33.         public static class Face {
  34.             // 位置
  35.             Location location;
  36.             // 是人脸的置信度
  37.             @JsonProperty("face_probability")
  38.             double face_probability;
  39.             // 口罩
  40.             Mask mask;
  41.             /**
  42.              * @author willzhao
  43.              * @version 1.0
  44.              * @description 人脸在图片中的位置
  45.              * @date 2022/1/1 16:04
  46.              */
  47.             @Data
  48.             public static class Location {
  49.                 double left;
  50.                 double top;
  51.                 double width;
  52.                 double height;
  53.                 double rotation;
  54.             }
  55.             /**
  56.              * @author willzhao
  57.              * @version 1.0
  58.              * @description 口罩对象
  59.              * @date 2022/1/1 16:11
  60.              */
  61.             @Data
  62.             public static class Mask {
  63.                 int type;
  64.                 double probability;
  65.             }
  66.         }
  67.     }
  68. }
复制代码

  • 然后是服务类BaiduCloudService.java,把请求和响应百度AI开放平台的逻辑全部集中在这里,可见其实很简单:根据图片的base64字符串构造请求对象、发POST请求(path是人脸检测服务)、收到响应后用Jackson反序列化成FaceDetectResponse对象:
  1. package com.bolingcavalry.grabpush.extend;
  2. import com.bolingcavalry.grabpush.bean.request.FaceDetectRequest;
  3. import com.bolingcavalry.grabpush.bean.response.FaceDetectResponse;
  4. import com.fasterxml.jackson.databind.DeserializationFeature;
  5. import com.fasterxml.jackson.databind.ObjectMapper;
  6. import okhttp3.*;
  7. import java.io.IOException;
  8. /**
  9. * @author willzhao
  10. * @version 1.0
  11. * @description 百度云服务的调用
  12. * @date 2022/1/1 11:06
  13. */
  14. public class BaiduCloudService {
  15.     OkHttpClient client = new OkHttpClient();
  16.     static final MediaType JSON = MediaType.parse("application/json; charset=utf-8");
  17.     static final String URL_TEMPLATE = "https://aip.baidubce.com/rest/2.0/face/v3/detect?access_token=%s";
  18.     String token;
  19.     ObjectMapper mapper = new ObjectMapper();
  20.     public BaiduCloudService(String token) {
  21.         this.token = token;
  22.         // 重要:反序列化的时候,字符的字段如果比类的字段多,下面这个设置可以确保反序列化成功
  23.         mapper.disable(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES);
  24.     }
  25.    
  26.     /**
  27.      * 检测指定的图片
  28.      * @param imageBase64
  29.      * @return
  30.      */
  31.     public FaceDetectResponse detect(String imageBase64) {
  32.         // 请求对象
  33.         FaceDetectRequest faceDetectRequest = new FaceDetectRequest();
  34.         faceDetectRequest.setImageType("BASE64");
  35.         faceDetectRequest.setFaceField("mask");
  36.         faceDetectRequest.setMaxFaceNum(6);
  37.         faceDetectRequest.setFaceType("LIVE");
  38.         faceDetectRequest.setLivenessControl("NONE");
  39.         faceDetectRequest.setFaceSortType(0);
  40.         faceDetectRequest.setImage(imageBase64);
  41.         FaceDetectResponse faceDetectResponse = null;
  42.         try {
  43.             // 用Jackson将请求对象序列化成字符串
  44.             String jsonContent = mapper.writeValueAsString(faceDetectRequest);
  45.             //
  46.             RequestBody requestBody = RequestBody.create(JSON, jsonContent);
  47.             Request request = new Request
  48.                     .Builder()
  49.                     .url(String.format(URL_TEMPLATE, token))
  50.                     .post(requestBody)
  51.                     .build();
  52.             Response response = client.newCall(request).execute();
  53.             String rawRlt = response.body().string();
  54.             faceDetectResponse = mapper.readValue(rawRlt, FaceDetectResponse.class);
  55.         } catch (IOException ioException) {
  56.             ioException.printStackTrace();
  57.         }
  58.         return faceDetectResponse;
  59.     }
  60. }
复制代码

  • 服务类写完了,接下来是主程序把整个逻辑串起来
DetectService接口的实现


  • 熟悉《JavaCV的摄像头实战》系列的读者应该对DetectService接口不陌生了,为了在整个系列的诸多实战中以统一的风格实现抓取帧-->处理帧-->输出处理结果这样的流程,咱们定义了一个DetectService接口,每种不同帧处理业务按照自己的特点来实现此接口即可(例如人脸检测、年龄检测、性别检测等)
  • 先来回顾DetectService接口:
[code]package com.bolingcavalry.grabpush.extend;import org.bytedeco.javacv.Frame;import org.bytedeco.javacv.OpenCVFrameConverter;import org.bytedeco.opencv.opencv_core.*;import org.bytedeco.opencv.opencv_objdetect.CascadeClassifier;import static org.bytedeco.opencv.global.opencv_core.CV_8UC1;import static org.bytedeco.opencv.global.opencv_imgproc.*;/** * @author willzhao * @version 1.0 * @description 检测工具的通用接口 * @date 2021/12/5 10:57 */public interface DetectService {    /**     * 根据传入的MAT构造相同尺寸的MAT,存放灰度图片用于以后的检测     * @param src 原始图片的MAT对象     * @return 相同尺寸的灰度图片的MAT对象     */    static Mat buildGrayImage(Mat src) {        return new Mat(src.rows(), src.cols(), CV_8UC1);    }    /**     * 检测图片,将检测结果用矩形标注在原始图片上     * @param classifier 分类器     * @param converter Frame和mat的转换器     * @param rawFrame 原始视频帧     * @param grabbedImage 原始视频帧对应的mat     * @param grayImage 存放灰度图片的mat     * @return 标注了识别结果的视频帧     */    static Frame detect(CascadeClassifier classifier,                        OpenCVFrameConverter.ToMat converter,                        Frame rawFrame,                        Mat grabbedImage,                        Mat grayImage) {        // 当前图片转为灰度图片        cvtColor(grabbedImage, grayImage, CV_BGR2GRAY);        // 存放检测结果的容器        RectVector objects = new RectVector();        // 开始检测        classifier.detectMultiScale(grayImage, objects);        // 检测结果总数        long total = objects.size();        // 如果没有检测到结果,就用原始帧返回        if (total

本帖子中包含更多资源

您需要 登录 才可以下载或查看,没有账号?立即注册

x
回复

使用道具 举报

0 个回复

倒序浏览

快速回复

您需要登录后才可以回帖 登录 or 立即注册

本版积分规则

羊蹓狼

金牌会员
这个人很懒什么都没写!

标签云

快速回复 返回顶部 返回列表