一、实现思路
- 打开本地摄像头
 - 遍历每一个图像帧检查出人脸
 - 对人脸进行灰度化
 - 戴白色面具的人,大多数时候Opencv 无法正确识别人脸,为克服这个困难,使用“阈值”函数转换黑白图像
 - 检测出每一个人脸上是否有嘴,或者鼻子
 - 如果检测出嘴,则没有带口罩,如果没有检测出嘴,则带了口罩或者面具,面纱遮挡等
 
二、完整代码
package com.biubiu.example;import org.opencv.core.Point;import org.opencv.core.*;import org.opencv.highgui.HighGui;import org.opencv.imgcodecs.Imgcodecs;import org.opencv.imgproc.Imgproc;import org.opencv.objdetect.CascadeClassifier;import org.opencv.videoio.VideoCapture;import javax.imageio.ImageIO;import java.awt.*;import java.awt.image.BufferedImage;import java.awt.image.DataBufferByte;import java.io.ByteArrayInputStream;import java.io.InputStream;import static org.opencv.imgproc.Imgproc.threshold;/** * @author :张音乐 * @date :Created in 2021/10/23 下午2:41 * @description:口罩检测 * @email: zhangyule1993@sina.com * @version: */public class MasksDetect {    static {        // 加载 动态链接库        System.loadLibrary(Core.NATIVE_LIBRARY_NAME);    }    public static void main(String[] args) {        VideoCapture camera = new VideoCapture();        // 参数0表示,获取第一个摄像头。        camera.open(0);        // 图像帧        Mat frame = new Mat();        for(;;) {            camera.read(frame);            draw(frame);            // 等待用户按esc停止检测            if(HighGui.waitKey(100) == 100) {                break;            }        }        // 释放摄像头        camera.release();        // 释放窗口资源        HighGui.destroyAllWindows();    }    /**     * 逐帧处理     * @param frame     */    private static void draw(Mat frame) {        Mat grayFrame = new Mat();        Imgproc.cvtColor(frame, grayFrame, Imgproc.COLOR_BGR2GRAY);        Mat thresh = new Mat();        // 戴白色面具的人,大多数时候 OpenCV 无法正确识别人脸。为了克服这个困难,使用“阈值”函数转换黑白图像        // 根据相机和周围光线在80 到 105 “阈值”范围内调整阈值 (bw_threshold) 值很重要。        threshold(grayFrame, thresh, 80, 255,  Imgproc.THRESH_BINARY | Imgproc.THRESH_OTSU);        // OpenCv人脸识别分类器        CascadeClassifier faceClassifier = new CascadeClassifier("/usr/local/share/OpenCV/haarcascades/haarcascade_frontalface_default.xml");        // 图像缩放比例,可以理解为相机的X倍镜        double scaleFactor = 1.1;        // 对特征检测点周边多少有效检测点同时检测,这样可以避免选取的特征检测点大小而导致遗漏        int minNeighbors = 4;        // 用来存放人脸矩形        MatOfRect faceRect = new MatOfRect();        // 执行人脸检测 “灰色”图像        faceClassifier.detectMultiScale(grayFrame, faceRect, scaleFactor, minNeighbors);        // 用来存放人脸矩形        MatOfRect faceRect1 = new MatOfRect();        // 执行人脸检测 “黑白”图像        faceClassifier.detectMultiScale(thresh, faceRect1, scaleFactor, minNeighbors);        // OpenCv人脸识别分类器        CascadeClassifier mouthClassifier = new CascadeClassifier("/usr/local/share/OpenCV/haarcascades/haarcascade_mcs_mouth.xml");        Scalar color = new Scalar(0, 0, 255);        // 遍历每一个图像帧上的每一张人脸        for(Rect rect: faceRect.toArray()) {            int x = rect.x;            int y = rect.y;            int w = rect.width;            int h = rect.height;            // 框出人脸            Imgproc.rectangle(frame, new Point(x, y), new Point(x + h, y + w), color, 2);            // 用来存放嘴矩形            MatOfRect mouseRects = new MatOfRect();            mouthClassifier.detectMultiScale(grayFrame, mouseRects, 1.5, 5);            if(mouseRects.toArray().length == 0) {                frame = putChineseTxt(frame, "有口罩/遮挡面具", x + w / 2 - 5, y - 10);            }        }        HighGui.imshow("预览", frame);    }    /**     * Mat二维矩阵转Image     * @param matrix     * @param fileExtension     * @return     */    public static BufferedImage matToImg(Mat matrix, String fileExtension) {        // convert the matrix into a matrix of bytes appropriate for        // this file extension        MatOfByte mob = new MatOfByte();        Imgcodecs.imencode(fileExtension, matrix, mob);        // convert the "matrix of bytes" into a byte array        byte[] byteArray = mob.toArray();        BufferedImage bufImage = null;        try {            InputStream in = new ByteArrayInputStream(byteArray);            bufImage = ImageIO.read(in);        } catch (Exception e) {            e.printStackTrace();        }        return bufImage;    }    /**     * BufferedImage转换成 Mat     * @param original     * @param imgType     * @param matType     * @return     */    public static Mat imgToMat(BufferedImage original, int imgType, int matType) {        if (original == null) {            throw new IllegalArgumentException("original == null");        }        if (original.getType() != imgType){            // Create a buffered image            BufferedImage image = new BufferedImage(original.getWidth(), original.getHeight(), imgType);            // Draw the image onto the new buffer            Graphics2D g = image.createGraphics();            try {                g.setComposite(AlphaComposite.Src);                g.drawImage(original, 0, 0, null);            } finally {                g.dispose();            }        }        byte[] pixels = ((DataBufferByte) original.getRaster().getDataBuffer()).getData();        Mat mat = Mat.eye(original.getHeight(), original.getWidth(), matType);        mat.put(0, 0, pixels);        return mat;    }    /**     * 在图片上显示中文     * @param img     * @param text     * @param x     * @param y     * @return     */    private static Mat putChineseTxt(Mat img, String text, int x, int y) {        Font font = new Font("微软雅黑", Font.PLAIN, 20);        BufferedImage bufImg = matToImg(img,".png");        Graphics2D g = bufImg.createGraphics();        g.drawImage(bufImg, 0, 0, bufImg.getWidth(), bufImg.getHeight(), null);        // 设置字体        g.setColor(new Color(255, 10, 52));        g.setFont(font);        // 设置水印的坐标        g.drawString(text, x, y);        g.dispose();        // 加完水印再转换回来        return imgToMat(bufImg, BufferedImage.TYPE_3BYTE_BGR, CvType.CV_8UC3);    }}