一、实现思路
- 打开本地摄像头
- 遍历每一个图像帧检查出人脸
- 对人脸进行灰度化
- 戴白色面具的人,大多数时候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);
}
}