基于深度神经网络模型的人脸图片质量检测(附源码)
JavaCV是基于OpenCV、FFmpeg等开源库的Java跨平台接口,提供高效的计算机视觉与多媒体处理能力。它封装了底层C/C++库的复杂操作,支持图像处理(如人脸识别、目标检测)、音视频编解码、流媒体传输等功能,适用于实时视频分析、AI模型集成及多媒体应用开发。通过简化JNI调用,JavaCV让开发者能直接在Java/Kotlin中调用高性能原生算法,广泛应用于安防监控、直播推流、医学影像分
一、为什么需要人脸图片质量检测?
"人脸图片质量检测"功能基于深度神经网络模型训练文件,可智能评估图像中人脸的可识别性,为生物识别、证件核验等场景提供质量管控。通过分析模糊度、光照均匀性、面部遮挡、姿态角度及图像分辨率等核心指标,系统自动生成质量评分并标识具体缺陷。该功能有效解决因低质量图像导致的识别失败问题,可集成于身份认证系统、智能安防平台或影像采集设备,通过前置质量筛查显著提升人脸识别准确率,同时为用户提供实时的拍摄优化建议,降低重复采集成本。
二、如何实现人脸图片质量检测(需求分析)?
2.1 人脸图片质量检测需求分析
人脸图片质量检测本质是对采集过来的图片进行校验,包括:
- 图片尺寸大小
- 图片清晰度
- 图片亮度
- 是否包含人脸
- 人脸是否被遮挡
- 人脸尺寸大小
2.2 检测处理流程
三、关键技术-javacv
3.1 javacv概述
JavaCV是基于OpenCV、FFmpeg等开源库的Java跨平台接口,提供高效的计算机视觉与多媒体处理能力。它封装了底层C/C++库的复杂操作,支持图像处理(如人脸识别、目标检测)、音视频编解码、流媒体传输等功能,适用于实时视频分析、AI模型集成及多媒体应用开发。通过简化JNI调用,JavaCV让开发者能直接在Java/Kotlin中调用高性能原生算法,广泛应用于安防监控、直播推流、医学影像分析等领域,是Java生态中处理音视频与计算机视觉任务的核心工具之一。
其中javacv提供DNN模块,可以加载深度学习预训练好的模型文件。通过加载预训练的人脸检测模型,很好的实现了人脸检测功能,准确率能到99%以上。
四、实现逻辑
4.1 图片基本信息校验
public FaceCheckVO checkFacePic(InputStream imageInputStream) throws IOException {
Mat gayMat = null;
InputStream standardPic = null;
try {
standardPic = convertStandardPic(imageInputStream);
Mat image = convertMat(ImageIO.read(standardPic));
if (image == null){
return new FaceCheckVO(false,"获取图片失败,请重试!");
}
// 基本校验
if(image.width() < minFacePicSize || image.height() < minFacePicSize){
return new FaceCheckVO(false,"图片高、宽不能小于300");
}
//gayMat = cvtColor(picMat);
FaceCheckVO faceCheckVo = new FaceCheckVO();
// 人脸检测
List<Mat> facePicList;
try {
facePicList = doCheckFacePic(image);
}catch (ValidateException e){
return faceCheckVo.toResult(false,e.getMessage());
}
if (facePicList.size() == 0){
return faceCheckVo.toResult(false,"未检测到人脸");
}
if (facePicList.size() > 1){
return faceCheckVo.toResult(false,"检测到多张人脸,请重新拍摄");
}
Mat faceMat = facePicList.get(0);
// 图片灰度化处理
gayMat = cvtColor(faceMat);
//校验亮度
BrightnessBO brightnessBo = brightnessCheck(gayMat);
faceCheckVo.setBrightness(brightnessBo.getCast());
//检测清晰度
Double definition = DefinitionCheck(gayMat);
faceCheckVo.setDefinition(definition);
if (brightnessBo.getResultCode() == 1){
return faceCheckVo.toResult(false,"人脸区域过暗");
}
if (faceCheckVo.getDefinition().doubleValue() < minDefinition){
return faceCheckVo.toResult(false,"人脸区域清晰度过低");
}
return faceCheckVo.toResult(true,"检测通过");
}finally {
if (gayMat != null){
gayMat.release();
}
if (standardPic != null){
standardPic.close();
}
}
}
4.2 加载深度神经网络预训练文件
public void init() throws IOException {
// opencv本地库加载
OpencvJavaLocalLoadUtil.loadOpenCvNative();
// 文件在项目resources包下
MatOfByte deployProtoMat = getMatOfByteFromLocalFile("D:\\code source\\rider\\face-check\\src\\main\\resources\\dnn\\deploy.prototxt");
// 文件在项目resources包下
InputStream ins = new FileInputStream("D:\\code source\\rider\\face-check\\src\\main\\resources\\dnn\\res10_300x300_ssd_iter_140000.caffemodel");
ByteArrayOutputStream ous = new ByteArrayOutputStream();
FileUtil.inputStreamToOutputStream(ins,ous);
MatOfByte caffeModelMat = new MatOfByte(ous.toByteArray());
ins.close();
net = Dnn.readNetFromCaffe(deployProtoMat, caffeModelMat);
}
加载opencv:
/**
* @Description:
* @Create: Date:2025年02月28日
*/
public class OpencvJavaLocalLoadUtil {
private static final Logger log = LoggerFactory.getLogger(OpencvJavaLocalLoadUtil.class);
private static boolean isLoad = false;
public static void loadOpenCvNative(){
if (isLoad){
return;
}
isLoad = true;
try {
System.setProperty("org.bytedeco.javacpp.maxphysicalbytes", "0");
System.setProperty("org.bytedeco.javacpp.maxbytes", "0");
Loader.load(opencv_java.class);
}catch (Exception e){
log.warn("加载opencn_java异常:",e);
}
}
}
4.3 人脸图片检测并裁剪(用于下一步分析)
public List<Mat> faceCheck(Mat image) throws IOException {
List<Mat> faceMatList = new ArrayList<>();
//为了获得最佳精度,必须分别对蓝色、绿色和红色通道执行 `(104, 177, 123)` 通道均值减法
Mat inputBlob = Dnn.blobFromImage(image, 1.0f,
new Size(200, 200),
new Scalar(104, 117, 123), false, false);
net.setInput(inputBlob);
Mat res = net.forward();
Mat faces = res.reshape(1, res.size(2));
for (int i=0; i<faces.rows(); i++) {
float [] data = new float[7];
faces.get(i, 0, data);
float confidence = data[2];
if (confidence > minConfidence) {
int left = (int)(data[3] * image.cols());
int top = (int)(data[4] * image.rows());
int right = (int)(data[5] * image.cols());
int bottom = (int)(data[6] * image.rows());
if (left < 0 || top < 0){
throw new ValidateException("人脸区域部分被遮挡");
}
//Imgproc.rectangle(image, new Point(left,top), new Point(right,bottom), new Scalar(0,200,0), 3);
//截取的区域:参数,坐标X,坐标Y,截图宽度,截图长度,bottom-top
log.info("image width:{},image height:{}",image.width(),image.height());
log.info("left:{},top:{},right:{},bottom:{}",left,top,right,bottom);
log.info("face width:{},face height:{}",right-left,bottom-top);
int faceWidth = right-left;
int faceHeight = bottom-top;
if (faceWidth > image.width() || faceHeight > image.height()){
throw new ValidateException("人脸区域部分被遮挡");
}
if ((top+faceHeight) > image.height()){
throw new ValidateException("人脸区域部分被遮挡");
}
if ((left+faceWidth) > image.width()){
throw new ValidateException("人脸区域部分被遮挡");
}
Rect rect = new Rect (left,top,faceWidth, faceHeight);
Mat sub = image.submat(rect);
MatOfByte mob=new MatOfByte();
Imgcodecs.imencode(".jpg", sub, mob);
ByteArrayInputStream in = new ByteArrayInputStream(mob.toArray());
Mat faceMat = convertMat(ImageIO.read(in));
faceMatList.add(faceMat);
}
}
return faceMatList;
}
/**
* bufferedImage convert mat
* @param im
* @return
*/
private static Mat convertMat(BufferedImage im) {
// Convert INT to BYTE
//im = toBufferedImageOfType(im, BufferedImage.TYPE_3BYTE_BGR);
byte[] pixels = ((DataBufferByte) im.getRaster().getDataBuffer())
.getData();
// Create a Matrix the same size of image
Mat image = new Mat(im.getHeight(), im.getWidth(), 16);
// Fill Matrix with image values
image.put(0, 0, pixels);
return image;
}
4.4 人脸图片区域校验
Mat faceMat = facePicList.get(0);
// 图片灰度化处理
gayMat = cvtColor(faceMat);
//校验亮度
BrightnessBO brightnessBo = brightnessCheck(gayMat);
faceCheckVo.setBrightness(brightnessBo.getCast());
//检测清晰度
Double definition = DefinitionCheck(gayMat);
faceCheckVo.setDefinition(definition);
if (brightnessBo.getResultCode() == 1){
return faceCheckVo.toResult(false,"人脸区域过暗");
}
if (faceCheckVo.getDefinition().doubleValue() < minDefinition){
return faceCheckVo.toResult(false,"人脸区域清晰度过低");
}
return faceCheckVo.toResult(true,"检测通过");
4.5 运行效果
检测样例(网图)
检测结果:
五、结语
以上实现了人脸图片质量检测的从分析到技术实现完整流程,并且在生产环境得到了很好的效果,在人脸图片采集的质量上得到很大的提升。有兴趣学习或刚好有这类需求的同学,点赞、关注私信我发源码项目,相互学习。
更多推荐
所有评论(0)