Java实现证件照质量检测
以下是一个使用Java实现的证件照质量检测程序,主要使用OpenCV库进行图像处理和分析:
- 项目依赖
首先需要在pom.xml中添加OpenCV依赖:
<dependencies>
<dependency>
<groupId>org.openpnp</groupId>
<artifactId>opencv</artifactId>
<version>4.5.1-2</version>
</dependency>
</dependencies>
2.证件照质量检测主类
import org.apache.poi.xssf.usermodel.XSSFWorkbook;
import org.opencv.core.*;
import org.opencv.imgcodecs.Imgcodecs;
import org.opencv.imgproc.Imgproc;
import org.opencv.objdetect.CascadeClassifier;
import org.springframework.boot.system.ApplicationHome;
import org.springframework.util.ResourceUtils;
import java.io.File;
import java.net.URLDecoder;
import java.util.*;
import java.util.List;
import org.apache.poi.ss.usermodel.*;
import java.io.FileOutputStream;
import java.io.IOException;
import java.util.ArrayList;
import static com.dx.utils.DateUtils.getDate;
public class IDphotoUtils {
String rootPath = "";
{
try {
String osName = System.getProperties().getProperty("os.name");
System.out.println(osName);
if(osName.equals("Linux"))
{
ApplicationHome home = new ApplicationHome(getClass());
File jarFile = home.getSource();
rootPath = jarFile.getParentFile().toString();
}
else
{
rootPath = ResourceUtils.getURL("classpath:").getPath() + "static";
rootPath = URLDecoder.decode(rootPath,"utf-8");
}
} catch (Exception e) {
e.printStackTrace();
}
}
/**
* 静态常量:证件照标准尺寸映射(key为尺寸类型,value为宽×高,单位:毫米)
* 包含1寸、2寸、护照、签证四种常见尺寸
*/
private static final Map<String, Size> STANDARD_SIZES = new HashMap<>();
static {
STANDARD_SIZES.put("1inch", new Size(25, 35)); // 1寸:25mm × 35mm
STANDARD_SIZES.put("2inch", new Size(35, 49)); // 2寸:35mm × 49mm
STANDARD_SIZES.put("passport", new Size(33, 48)); // 护照:33mm × 48mm
STANDARD_SIZES.put("visa", new Size(35, 45)); // 签证:35mm × 45mm
}
/**
* 静态常量:质量标准阈值(用于判断各维度是否合格)
* 包含亮度、对比度、清晰度、背景噪声、人脸占比的上下限
*/
private static final int MIN_BRIGHTNESS = 100; // 最低亮度阈值
private static final int MAX_BRIGHTNESS = 200; // 最高亮度阈值
private static final int MIN_CONTRAST = 40; // 最低对比度阈值
private static final int MIN_SHARPNESS = 50; // 最低清晰度阈值
private static final int MAX_NOISE = 40; // 最高背景噪声阈值
private static final double FACE_RATIO_MIN = 0.10;// 人脸占图像最小比例(15%)
private static final double FACE_RATIO_MAX = 0.45;// 人脸占图像最大比例(25%)
// 人脸检测器(基于OpenCV的Haar级联分类器)
private CascadeClassifier faceDetector;
/**
* 构造方法:初始化OpenCV本地库与人脸检测器
* 注意:当前人脸检测器未实际加载(load方法被注释),需在实际使用时打开
*/
public IDphotoUtils() {
// 加载OpenCV本地库(nu.pattern.OpenCV为第三方封装,简化库加载流程)
nu.pattern.OpenCV.loadLocally();
// 初始化人脸检测器:拼接haar分类器文件路径(根路径+分类器文件名)
// String haarPath = "path/to/haarcascade_frontalface_default.xml"; // 示例路径
String haarPath = "C:\\Users\\Administrator\\Desktop\\update\\haarcascade_frontalface_default.xml" ;// 实际路径(Windows分隔符)
// 在实际使用中,需要提供正确的haar分类器路径(确保文件存在)
// System.err.println(haarPath);
// this.faceDetector = new CascadeClassifier(haarPath); // 实例化人脸检测器
// this.faceDetector.load(haarPath); // 注释:未加载分类器文件,当前人脸检测为模拟逻辑
this.faceDetector = new CascadeClassifier();
// 加载模型文件
boolean isLoaded = this.faceDetector.load(haarPath);
if (!isLoaded) {
throw new RuntimeException("人脸模型加载失败,请检查路径:" + haarPath);
}
}
/**
* 加载图像文件
* @param imagePath 图像文件路径(绝对路径)
* @return 加载后的OpenCV图像对象(Mat)
* @throws IllegalArgumentException 若文件不存在或无法读取,抛出参数异常
*/
public Mat loadImage(String imagePath) {
File file = new File(imagePath);
// 校验文件是否存在
if (!file.exists()) {
throw new IllegalArgumentException("图片文件不存在: " + imagePath);
}
// 用OpenCV的Imgcodecs读取图像(支持常见格式如JPG/PNG)
Mat image = Imgcodecs.imread(imagePath);
// 校验图像是否读取成功(empty()返回true表示读取失败)
System.err.println("--------------"+image.empty());
if (image.empty()) {
throw new IllegalArgumentException("无法读取图片文件: " + imagePath);
}
return image;
}
/**
* 检查图像分辨率是否符合证件照标准
* @param image 待检查的图像对象(Mat)
* @param dpi 图像的DPI(每英寸像素数,默认300)
* @return 分辨率检查结果(包含像素尺寸、实际英寸尺寸、是否达标)
*/
public ResolutionResult checkResolution(Mat image, int dpi) {
Size size = image.size(); // 获取图像像素尺寸(宽×高)
double widthPixels = size.width; // 像素宽度
double heightPixels = size.height; // 像素高度
// 计算实际物理尺寸(英寸):像素数 / DPI
double widthInch = widthPixels / dpi;
double heightInch = heightPixels / dpi;
// 判断是否符合标准:宽≥1英寸且高≥1.4英寸(对应常见证件照最小尺寸)
boolean meetsStandard = widthInch >= 1 && heightInch >= 1.4;
// 返回封装后的检查结果
return new ResolutionResult(
(int) widthPixels, (int) heightPixels,
widthInch, heightInch, dpi, meetsStandard
);
}
/**
* 检查图像的亮度与对比度
* @param image 待检查的图像对象(Mat,BGR格式)
* @return 亮度对比度检查结果(包含具体数值与是否合格)
*/
public BrightnessContrastResult checkBrightnessContrast(Mat image) {
// 1. 将BGR图像转为灰度图像(亮度与对比度分析基于灰度图)
Mat gray = new Mat();
Imgproc.cvtColor(image, gray, Imgproc.COLOR_BGR2GRAY);
// 2. 计算亮度:灰度图像的平均像素值(均值越大,亮度越高)
Scalar mean = Core.mean(gray);
double brightness = mean.val[0]; // Scalar.val[0]为灰度图的均值
// 3. 计算对比度:灰度图像的标准差(标准差越大,对比度越高)
MatOfDouble meanMat = new MatOfDouble(); // 存储均值(备用)
MatOfDouble stdMat = new MatOfDouble(); // 存储标准差
Core.meanStdDev(gray, meanMat, stdMat); // 计算均值与标准差
double contrast = stdMat.get(0, 0)[0]; // 获取标准差数值
// 4. 判断是否合格:亮度在[100,200]之间,对比度≥40
boolean brightnessOk = brightness >= MIN_BRIGHTNESS && brightness <= MAX_BRIGHTNESS;
boolean contrastOk = contrast >= MIN_CONTRAST;
// 返回封装后的检查结果
return new BrightnessContrastResult(brightness, contrast, brightnessOk, contrastOk);
}
/**
* 检查图像清晰度(模糊程度)
* 原理:拉普拉斯算子检测边缘,方差越大,边缘越清晰(图像越清晰)
* @param image 待检查的图像对象(Mat,BGR格式)
* @return 清晰度检查结果(包含清晰度分数与是否合格)
*/
public SharpnessResult checkSharpness(Mat image) {
// 1. 转为灰度图像(清晰度分析基于灰度图)
Mat gray = new Mat();
Imgproc.cvtColor(image, gray, Imgproc.COLOR_BGR2GRAY);
// 2. 应用拉普拉斯算子(64位浮点型,保留边缘细节)
Mat laplacian = new Mat();
Imgproc.Laplacian(gray, laplacian, CvType.CV_64F);
// 3. 计算拉普拉斯结果的均值与标准差
MatOfDouble mean = new MatOfDouble();
MatOfDouble std = new MatOfDouble();
Core.meanStdDev(laplacian, mean, std);
// 4. 计算清晰度分数:标准差的平方(放大差异,便于判断)
double sharpness = Math.pow(std.get(0, 0)[0], 2);
// 判断是否合格:清晰度分数≥50
boolean sharpnessOk = sharpness >= MIN_SHARPNESS;
// 返回封装后的检查结果
return new SharpnessResult(sharpness, sharpnessOk);
}
/**
* 检查背景均匀性(是否有杂色/噪声)
* 原理:取图像四个边缘区域,计算像素值标准差,标准差越小,背景越均匀
* @param image 待检查的图像对象(Mat,BGR格式)
* @return 背景检查结果(包含均匀性分数与是否合格)
*/
public BackgroundResult checkBackground(Mat image) {
// 获取图像的高度(rows)与宽度(cols)
int h = image.rows();
int w = image.cols();
// 定义四个边缘区域(各占图像的1/10大小)
List<Mat> borderRegions = new ArrayList<>();
borderRegions.add(image.submat(0, h/10, 0, w)); // 上边:顶部1/10高度
borderRegions.add(image.submat(h - h/10, h, 0, w)); // 下边:底部1/10高度
borderRegions.add(image.submat(0, h, 0, w/10)); // 左边:左侧1/10宽度
borderRegions.add(image.submat(0, h, w - w/10, w)); // 右边:右侧1/10宽度
// 计算所有边缘区域的标准差平均值
double totalStd = 0;
int validRegions = 0; // 有效区域计数(避免空区域影响结果)
for (Mat region : borderRegions) {
if (!region.empty()) { // 跳过空区域
MatOfDouble mean = new MatOfDouble();
MatOfDouble std = new MatOfDouble();
Core.meanStdDev(region, mean, std); // 计算区域的均值与标准差
totalStd += std.get(0, 0)[0]; // 累加标准差
validRegions++;
}
}
// 计算平均背景标准差(均匀性分数:分数越低,背景越均匀)
double avgBackgroundStd = validRegions > 0 ? totalStd / validRegions : 0;
// 判断是否合格:平均标准差≤30(噪声不超过阈值)
boolean backgroundOk = avgBackgroundStd <= MAX_NOISE;
// 返回封装后的检查结果
return new BackgroundResult(avgBackgroundStd, backgroundOk);
}
/**
* 释放 OpenCV Mat 资源
*/
/**
* 释放 OpenCV Mat 资源(兼容所有版本)
*/
private void releaseResources(Mat... mats) {
for (Mat mat : mats) {
// 检查 mat 不为 null,且未释放(用 empty() 替代 isReleased())
if (mat != null && !mat.empty()) {
mat.release(); // 释放资源
}
}
}
/**
* 检测图像中的人脸(位置、数量、尺寸占比)
* 注意:当前为模拟逻辑(分类器未加载),实际使用需打开faceDetector.load()
* @param image 待检查的图像对象(Mat,BGR格式)
* @return 人脸检测结果(包含是否检测到人脸、位置、尺寸是否合格)
*/
public FaceDetectionResult detectFace(Mat image) {
// 1. 转为灰度图像(人脸检测基于灰度图,减少计算量)
Mat gray = new Mat();
Imgproc.cvtColor(image, gray, Imgproc.COLOR_BGR2GRAY);
// 2. 图像预处理:转为灰度图(减少计算量,符合模型要求)
// 3. (可选)直方图均衡化:增强对比度,提升检测效果
// Imgproc.equalizeHist(grayImage, grayImage);
// 4. 检测人脸:结果存入 MatOfRect
MatOfRect faces = new MatOfRect();
faceDetector.detectMultiScale(
gray, // 输入灰度图
faces // 输出:人脸矩形集合
// 1.1, // 缩放因子(每次缩小 10%)
// 5, // 最小邻域数(值越大,检测越严格)
// 0, // 旧版本参数,设为 0 即可
// new Size(50, 50),// 最小人脸尺寸(过滤过小区域)
// new Size() // 最大人脸尺寸(无限制)
);
// 5. 解析检测结果
Rect[] faceArray = faces.toArray();
boolean faceDetected = faceArray.length > 0;
int faceCount = faceArray.length;
// 若未检测到人脸,直接返回结果
if (!faceDetected) {
releaseResources(image, gray, faces);
return new FaceDetectionResult(false, 0, 0, false, false, null);
}
// 6. 分析第一张人脸(默认取最大/最可能的人脸,证件照通常只有一张)
Rect mainFace = faceArray[0]; // 取第一个检测到的人脸
int imgWidth = image.cols(); // 图像宽度
int imgHeight = image.rows(); // 图像高度
// 计算人脸占比(面积比)
double faceArea = mainFace.width * mainFace.height;
double imgArea = imgWidth * imgHeight;
double faceRatio = faceArea / imgArea;
// 检查人脸位置是否居中(计算人脸中心与图像中心的偏移)
Point imgCenter = new Point(imgWidth / 2, imgHeight / 2); // 图像中心
Point faceCenter = new Point(
mainFace.x + mainFace.width / 2,
mainFace.y + mainFace.height / 2
); // 人脸中心
double offset = Math.sqrt(
Math.pow(faceCenter.x - imgCenter.x, 2) +
Math.pow(faceCenter.y - imgCenter.y, 2)
); // 偏移距离(欧几里得距离)
boolean positionOk = offset < Math.min(imgWidth, imgHeight) * 0.1; // 偏移 < 10% 最小边长
// 检查人脸大小是否在合格范围
boolean sizeOk = faceRatio >= FACE_RATIO_MIN && faceRatio <= FACE_RATIO_MAX;
// 7. 释放资源(避免内存泄漏)、
releaseResources(image, gray, faces);
// 8. 返回封装结果
return new FaceDetectionResult(
true, faceCount, faceRatio, positionOk, sizeOk, mainFace
);
// // 2. 实际人脸检测逻辑(当前注释,需加载分类器后启用)
// MatOfRect faces = new MatOfRect(); // 存储检测到的人脸矩形
//
// faceDetector.detectMultiScale(gray, faces); // 多尺度检测人脸
//
// // 3. 模拟人脸检测结果(替代实际检测,便于测试)
//// boolean faceDetected = true; // 模拟:检测到人脸(实际应为faces.toArray().length > 0)
// boolean faceDetected = faces.toArray().length > 0 ;
//// int faceCount = 1; // 模拟:检测到1张人脸(实际应为faces.toArray().length)
// int faceCount = faces.toArray().length;
//
// // 若未检测到人脸,直接返回失败结果
// if (!faceDetected) {
// return new FaceDetectionResult(false, 0, 0, false, false, null);
// }
//
// // 4. 模拟人脸区域(实际应从faces中获取Rect对象)
// int imgHeight = image.rows(); // 图像高度
// int imgWidth = image.cols(); // 图像宽度
//// int faceWidth = (int) (imgWidth * 0.2); // 人脸宽度:图像宽度的20%
//// int faceHeight = (int) (imgHeight * 0.3); // 人脸高度:图像高度的30%
// int faceWidth = faces.width();
// int faceHeight= faces.height();
// System.err.println("人脸宽度*高度: "+faceWidth +" * " + faceHeight);
// int faceX = (imgWidth - faceWidth) / 2; // 人脸X坐标:水平居中
// int faceY = (imgHeight - faceHeight) / 3; // 人脸Y坐标:垂直偏上(1/3高度处)
// Rect faceRect = new Rect(faceX, faceY, faceWidth, faceHeight); // 人脸矩形
//
// // 5. 计算人脸占比:人脸面积 / 图像面积
// double faceRatio = (faceWidth * faceHeight) / (double) (imgWidth * imgHeight);
//
// // 6. 检查人脸位置是否居中
// int centerX = imgWidth / 2; // 图像中心点X坐标
// int centerY = imgHeight / 2; // 图像中心点Y坐标
// int faceCenterX = faceX + faceWidth / 2; // 人脸中心点X坐标
// int faceCenterY = faceY + faceHeight / 2; // 人脸中心点Y坐标
// // 计算人脸中心与图像中心的距离(欧几里得距离)
// double positionOffset = Math.sqrt(
// Math.pow(faceCenterX - centerX, 2) + Math.pow(faceCenterY - centerY, 2)
// );
// // 位置合格条件:偏移距离 < 图像最小边长的10%
// boolean positionOk = positionOffset < Math.min(imgWidth, imgHeight) * 0.1;
// // 尺寸合格条件:人脸占比在[15%,25%]之间
// boolean sizeOk = faceRatio >= FACE_RATIO_MIN && faceRatio <= FACE_RATIO_MAX;
//
// // 返回封装后的人脸检测结果
// return new FaceDetectionResult(
// true, faceCount, faceRatio, positionOk, sizeOk, faceRect
// );
}
/**
* 检查图像的色彩平衡(判断是否存在偏色,如偏红、偏蓝等)
* 原理:通过分析RGB三通道的平均亮度差异,差异越小说明色彩越平衡
* @param image 待检查的图像(OpenCV的Mat对象,BGR格式,注意OpenCV默认通道顺序为BGR而非RGB)
* @return 色彩平衡检查结果(包含平衡分数和是否合格的判断)
*/
public ColorBalanceResult checkColorBalance(Mat image) {
// 1. 分离图像的BGR三通道(OpenCV中图像默认存储为BGR格式,而非RGB)
if (image.empty()) {
System.err.println("错误:图像读取失败,路径可能错误或文件损坏。");
}
List<Mat> channels = new ArrayList<>();
Core.split(image, channels); // 将图像拆分为单个通道,存储到List中
// 2. 分别获取B、G、R通道(注意索引对应顺序:0=Blue,1=Green,2=Red)
Mat b = channels.get(0); // 蓝色通道
Mat g = channels.get(1); // 绿色通道
Mat r = channels.get(2); // 红色通道
// 3. 计算每个通道的平均像素值(反映该颜色通道的整体亮度)
Scalar meanB = Core.mean(b); // 蓝色通道的均值(Scalar是OpenCV中存储多元素值的结构)
Scalar meanG = Core.mean(g); // 绿色通道的均值
Scalar meanR = Core.mean(r); // 红色通道的均值
// 4. 提取均值的数值(Scalar.val[0]表示该通道均值的具体数值)
double avgB = meanB.val[0]; // 蓝色通道平均亮度
double avgG = meanG.val[0]; // 绿色通道平均亮度
double avgR = meanR.val[0]; // 红色通道平均亮度
// 5. 计算三通道中最亮(maxAvg)和最暗(minAvg)的平均亮度
double maxAvg = Math.max(avgB, Math.max(avgG, avgR)); // 取三通道最大值
double minAvg = Math.min(avgB, Math.min(avgG, avgR)); // 取三通道最小值
// 6. 计算色彩平衡分数:(最亮 - 最暗) / 最亮
// 含义:差异占最亮通道的比例,值越小说明三通道亮度越接近,色彩越平衡
// 特殊处理:若最亮通道为0(全黑图像),则平衡分数为0
double colorBalance = maxAvg > 0 ? (maxAvg - minAvg) / maxAvg : 0;
// 7. 判断色彩是否合格:允许最大30%的差异(即平衡分数<0.3)
boolean colorOk = colorBalance < 0.3;
// 返回封装后的色彩平衡检查结果
return new ColorBalanceResult(colorBalance, colorOk);
}
/**
* 完整的证件照质量校验方法
* 功能:整合所有单项检查(分辨率、亮度、人脸等),生成综合校验结果
* @param imagePath 待校验的证件照文件路径
* @param dpi 图像的DPI(每英寸像素数),可为null(默认300)
* @return 包含所有检查结果和总体评分的ValidationResult对象
*/
public ValidationResult validatePhoto(String imagePath, Integer dpi) {
try {
// 处理DPI参数:若为null则使用默认值300(证件照常见DPI标准)
if (dpi == null) {
dpi = 300; // 默认DPI
}
// 1. 加载图像文件(内部会校验文件存在性和可读性)
Mat image = loadImage(imagePath);
if (image.empty()){
System.err.println("错误:图像读取失败,路径可能错误或文件损坏。");
} else {
System.err.println("图像读取成功~~~~");
}
// 2. 初始化校验结果对象,用于存储各项检查结果
ValidationResult result = new ValidationResult();
// 3. 执行各项专项检查,并将结果存入result对象
result.setResolution(checkResolution(image, dpi)); // 分辨率检查
result.setBrightnessContrast(checkBrightnessContrast(image)); // 亮度对比度检查
result.setSharpness(checkSharpness(image)); // 清晰度检查
result.setBackground(checkBackground(image)); // 背景均匀性检查
result.setColorBalance(checkColorBalance(image)); // 色彩平衡检查
result.setFaceDetection(detectFace(image)); // 人脸检测与位置检查
// 4. 根据各项检查结果计算总体评分(合格项占比)
result.calculateOverallScore();
// 返回完整的校验结果
return result;
} catch (Exception e) {
// 若校验过程中发生异常(如文件不存在、图像损坏等)
// 生成错误结果对象,存储错误信息并返回
ValidationResult errorResult = new ValidationResult();
errorResult.setError(e.getMessage());
return errorResult;
}
}
/**
* 生成证件照质量校验的文字报告
* 功能:将ValidationResult中的各项检查结果格式化为人易读的文本报告
* @param result 完整的校验结果对象(包含各项检查数据和总体评分)
* @return 格式化的校验报告字符串
*/
public String generateReport(ValidationResult result) {
// 1. 处理异常情况:若校验过程有错误,直接返回错误信息
if (result.getError() != null) {
return "校验过程中出现错误: " + result.getError();
}
// 2. 初始化字符串构建器,用于拼接报告内容
StringBuilder report = new StringBuilder();
// 3. 报告头部:标题、总体评分、是否合格
report.append("=== 证件照质量校验报告 ===\n");
// 总体评分保留1位小数(如85.0/100)
report.append(String.format("总体评分: %.1f/100\n", result.getOverallScore()));
// 合格状态根据总体评分判断(默认≥70分为合格)
report.append(String.format("是否合格: %s\n", result.isAcceptable() ? "是" : "否"));
report.append("\n"); // 空行分隔头部与详情
// 4. 分辨率检查结果
ResolutionResult res = result.getResolution();
report.append("1. 分辨率检查:\n");
report.append(String.format(" 尺寸: %dx%d 像素\n", res.getWidthPixels(), res.getHeightPixels())); // 像素尺寸
report.append(String.format(" DPI: %d\n", res.getDpi())); // 图像DPI值
report.append(String.format(" 是否符合标准: %s\n", res.isMeetsStandard() ? "是" : "否")); // 达标状态
// 5. 亮度与对比度检查结果
BrightnessContrastResult bc = result.getBrightnessContrast();
report.append("2. 亮度对比度检查:\n");
// 亮度值保留1位小数,同时显示合格状态
report.append(String.format(" 亮度: %.1f (%s)\n", bc.getBrightness(), bc.isBrightnessOk() ? "合格" : "不合格"));
// 对比度值保留1位小数,同时显示合格状态
report.append(String.format(" 对比度: %.1f (%s)\n", bc.getContrast(), bc.isContrastOk() ? "合格" : "不合格"));
// 6. 清晰度检查结果
SharpnessResult sharp = result.getSharpness();
report.append("3. 清晰度检查:\n");
// 清晰度分数保留1位小数,显示合格状态
report.append(String.format(" 清晰度分数: %.1f (%s)\n", sharp.getSharpness(), sharp.isSharpnessOk() ? "合格" : "不合格"));
// 7. 背景均匀性检查结果
BackgroundResult bg = result.getBackground();
report.append("4. 背景检查:\n");
// 背景均匀性分数保留1位小数,显示合格状态(分数越低越均匀)
report.append(String.format(" 背景均匀性: %.1f (%s)\n", bg.getBackgroundUniformity(), bg.isBackgroundOk() ? "合格" : "不合格"));
// 8. 人脸检测结果(分情况处理:检测到人脸/未检测到人脸)
FaceDetectionResult face = result.getFaceDetection();
report.append("5. 人脸检测:\n");
if (face.isFaceDetected()) {
// 检测到人脸:显示数量、占比、位置是否合格
report.append(String.format(" 检测到人脸数量: %d\n", face.getFaceCount()));
report.append(String.format(" 人脸比例: %.3f (%s)\n", face.getFaceRatio(), face.isFaceSizeOk() ? "合格" : "不合格"));
report.append(String.format(" 人脸位置: %s\n", face.isFacePositionOk() ? "合格" : "不合格"));
} else {
// 未检测到人脸:直接提示
report.append(" 未检测到人脸!\n");
}
// 9. 色彩平衡检查结果
ColorBalanceResult color = result.getColorBalance();
report.append("6. 色彩平衡检查:\n");
// 色彩平衡分数保留3位小数,显示合格状态(分数越低越平衡)
report.append(String.format(" 色彩平衡分数: %.3f (%s)\n", color.getColorBalance(), color.isColorOk() ? "合格" : "不合格"));
// 返回完整的报告字符串
return report.toString();
}
/**
* 生成证件照质量校验报告
* @param result
* @return
*/
public Map<String,Object> generateReportMap(ValidationResult result) {
Map<String,Object> map = new HashMap<>();
if (result.getError() != null) {
map.put("errmsg",result.getError());
} else {
map.put("errmsg","");
}
// 2. 初始化字符串构建器,用于拼接报告内容
// StringBuilder report = new StringBuilder();
// 3. 报告头部:标题、总体评分、是否合格
// report.append("=== 证件照质量校验报告 ===\n");
// 总体评分保留1位小数(如85.0/100)
map.put("allscore",String.format("%.1f/100\n", result.getOverallScore()));
// 合格状态根据总体评分判断(默认≥70分为合格)
map.put("isQualified",String.format("%s\n", result.isAcceptable() ? "合格" : "不合格"));
// 4. 分辨率检查结果
ResolutionResult res = result.getResolution();
map.put("pixels",String.format("%dx%d\n", res.getWidthPixels(), res.getHeightPixels())); // 像素尺寸
map.put("dpi",String.format("%d\n", res.getDpi())); // 图像DPI值
map.put("meetsStandard",String.format("%s\n", res.isMeetsStandard() ? "是" : "否")); // 达标状态
// 5. 亮度与对比度检查结果
BrightnessContrastResult bc = result.getBrightnessContrast();
// 亮度值保留1位小数,同时显示合格状态
map.put("brightness",String.format("%.1f (%s)\n", bc.getBrightness(), bc.isBrightnessOk() ? "合格" : "不合格"));
// 对比度值保留1位小数,同时显示合格状态
map.put("contrast",String.format("%.1f (%s)\n", bc.getContrast(), bc.isContrastOk() ? "合格" : "不合格"));
// 6. 清晰度检查结果
SharpnessResult sharp = result.getSharpness();
// 清晰度分数保留1位小数,显示合格状态
map.put("sharpness",String.format("%.1f (%s)\n", sharp.getSharpness(), sharp.isSharpnessOk() ? "合格" : "不合格"));
// 7. 背景均匀性检查结果
BackgroundResult bg = result.getBackground();
// 背景均匀性分数保留1位小数,显示合格状态(分数越低越均匀)
map.put("backgroundUniformity",String.format("%.1f (%s)\n", bg.getBackgroundUniformity(), bg.isBackgroundOk() ? "合格" : "不合格"));
// 8. 人脸检测结果(分情况处理:检测到人脸/未检测到人脸)
FaceDetectionResult face = result.getFaceDetection();
if (face.isFaceDetected()) {
// 检测到人脸:显示数量、占比、位置是否合格
map.put("faceCount",String.format("%d", face.getFaceCount()));
map.put("faceRatio",String.format("%.3f (%s)", face.getFaceRatio(), face.isFaceSizeOk() ? "合格" : "不合格"));
map.put("isFacePositionOk",String.format("%s", face.isFacePositionOk() ? "合格" : "不合格"));
} else {
// 未检测到人脸:直接提示
map.put("errmsg","未检测到人脸!");
map.put("faceCount","");
map.put("faceRatio","不合格");
map.put("isFacePositionOk","不合格");
}
// 9. 色彩平衡检查结果
ColorBalanceResult color = result.getColorBalance();
// 色彩平衡分数保留3位小数,显示合格状态(分数越低越平衡)
map.put("colorBalance",String.format("%.3f (%s)", color.getColorBalance(), color.isColorOk() ? "合格" : "不合格"));
return map;
}
// 图像预处理方法
// 功能:对输入图像进行去噪和对比度增强,优化图像质量以提升后续分析(如人脸检测)的准确性
public Mat preprocessImage(Mat image) {
// 克隆原始图像,避免直接修改输入图像(保护原始数据)
Mat processed = image.clone();
// 1. 高斯模糊去噪
// 原理:通过高斯核平滑图像,减少高频噪声(如斑点、颗粒)
// 参数说明:
// - processed:输入输出图像(此处输入输出为同一对象,原地处理)
// - new Size(3, 3):高斯核大小(3x3,较小的核可保留更多细节)
// - 0:高斯函数在X方向的标准差(设为0时由核大小自动计算)
Imgproc.GaussianBlur(processed, processed, new Size(3, 3), 0);
// 2. 直方图均衡化增强对比度(基于YCrCb色彩空间,避免色彩失真)
// 步骤1:将BGR图像转换为YCrCb色彩空间
// YCrCb中Y通道表示亮度,Cr和Cb表示色度,仅对Y通道处理可避免色彩失真
Mat ycrcb = new Mat();
Imgproc.cvtColor(processed, ycrcb, Imgproc.COLOR_BGR2YCrCb);
// 步骤2:分离YCrCb的三个通道
List<Mat> channels = new ArrayList<>();
Core.split(ycrcb, channels); // channels[0]=Y(亮度), channels[1]=Cr, channels[2]=Cb
// 步骤3:对Y通道(亮度通道)进行直方图均衡化
// 直方图均衡化通过拉伸像素值分布范围,增强图像明暗对比
Imgproc.equalizeHist(channels.get(0), channels.get(0));
// 步骤4:合并处理后的通道,还原为YCrCb图像
Mat merged = new Mat();
Core.merge(channels, ycrcb);
// 步骤5:将YCrCb图像转换回BGR格式(OpenCV默认处理格式)
Imgproc.cvtColor(ycrcb, processed, Imgproc.COLOR_YCrCb2BGR);
// 返回预处理后的图像
return processed;
}
/**
* 保存带标记的结果图像
* 功能:在原始图像上标注检测到的人脸区域及相关信息,并保存为新图像
* 便于直观查看人脸检测结果(位置、占比等)
* @param image 原始图像(Mat对象,BGR格式)
* @param faceResult 人脸检测结果(包含是否检测到人脸、人脸矩形区域等信息)
* @param outputPath 标注后图像的保存路径(含文件名及格式,如"result.jpg")
*/
public void saveAnnotatedImage(Mat image, FaceDetectionResult faceResult, String outputPath) {
// 克隆原始图像,避免直接修改输入图像(保护原始数据)
Mat annotated = image.clone();
// 仅当检测到人脸且人脸区域有效时,进行标注
if (faceResult.isFaceDetected() && faceResult.getFaceRect() != null) {
// 1. 绘制人脸框:用绿色矩形框标记人脸位置
// 参数说明:
// - annotated:目标图像(在该图像上绘制)
// - faceResult.getFaceRect():人脸区域的矩形坐标(x,y,宽,高)
// - new Scalar(0, 255, 0):矩形颜色(OpenCV中为BGR格式,此处0,255,0表示绿色)
// - 2:矩形边框厚度(2像素)
Imgproc.rectangle(annotated, faceResult.getFaceRect(),
new Scalar(0, 255, 0), 2);
// 2. 添加文本标注:显示人脸占比信息
// 格式化文本内容:人脸占比(百分比,保留1位小数)
String label = String.format("Face: %.1f%%", faceResult.getFaceRatio() * 100);
// 文本位置:位于人脸框上方10像素处(与框左侧对齐)
Point textPos = new Point(faceResult.getFaceRect().x,
faceResult.getFaceRect().y - 10);
// 绘制文本
// 参数说明:
// - annotated:目标图像
// - label:文本内容
// - textPos:文本起始坐标
// - Imgproc.FONT_HERSHEY_SIMPLEX:字体样式(简洁无衬线字体)
// - 0.5:字体大小(缩放比例)
// - new Scalar(0, 255, 0):文本颜色(绿色,与边框保持一致)
// - 1:文本线条厚度(1像素)
Imgproc.putText(annotated, label, textPos,
Imgproc.FONT_HERSHEY_SIMPLEX, 0.5, new Scalar(0, 255, 0), 1);
}
// 将标注后的图像保存到指定路径(支持常见格式如JPG、PNG等)
Imgcodecs.imwrite(outputPath, annotated);
}
// Java 8 兼容:用 Arrays.asList 替代 List.of
private static final List<String> PHOTO_EXTENSIONS = Arrays.asList(
"jpg", "jpeg", "png", "gif", "bmp", "tiff", "webp","jfif"
);
// 读取文件夹内所有照片文件
public static List<String> readAllPhotos(String folderPath) {
List<String> photoPaths = new ArrayList<>();
File folder = new File(folderPath);
if (!folder.exists() || !folder.isDirectory()) {
System.out.println("文件夹不存在或路径错误:" + folderPath);
return photoPaths;
}
traverseFolder(folder, photoPaths);
return photoPaths;
}
private static void traverseFolder(File currentFile, List<String> photoPaths) {
if (currentFile.isFile()) {
String fileExtension = getFileExtension(currentFile.getName()).toLowerCase();
if (PHOTO_EXTENSIONS.contains(fileExtension)) {
photoPaths.add(currentFile.getAbsolutePath());
}
return;
}
File[] subFiles = currentFile.listFiles();
if (subFiles == null) {
System.out.println("无法访问文件夹:" + currentFile.getAbsolutePath());
return;
}
for (File subFile : subFiles) {
traverseFolder(subFile, photoPaths);
}
}
private static String getFileExtension(String fileName) {
int lastDotIndex = fileName.lastIndexOf(".");
return lastDotIndex > 0 && lastDotIndex < fileName.length() - 1
? fileName.substring(lastDotIndex + 1)
: "";
}
public static void main(String[] args) {
IDphotoUtils validator = new IDphotoUtils();
String folderPath = "D:\\2025xyrsphoto\\backup\\candidate_photo\\2025\\";
String folderPath1 = "D:\\2025xyrsphoto\\minface"; // 检测有问题的图片文件夹
List<String> allPhotos = readAllPhotos(folderPath);
System.out.println("找到 " + allPhotos.size() + " 张照片:");
// 1. 创建工作簿(XSSFWorkbook 对应 .xlsx,HSSFWorkbook 对应 .xls)
Workbook workbook = new XSSFWorkbook();
// 2. 创建工作表(指定工作表名称)
Sheet sheet = workbook.createSheet("证件照质量检测数据");
// 3. 创建单元格样式(表头:加粗、居中;数据行:居中)
// 表头样式
CellStyle headerStyle = workbook.createCellStyle();
Font headerFont = workbook.createFont();
headerFont.setBold(true); // 字体加粗
headerStyle.setFont(headerFont);
headerStyle.setAlignment(HorizontalAlignment.CENTER); // 水平居中
headerStyle.setVerticalAlignment(VerticalAlignment.CENTER); // 垂直居中
// 数据行样式
CellStyle dataStyle = workbook.createCellStyle();
dataStyle.setAlignment(HorizontalAlignment.CENTER);
dataStyle.setVerticalAlignment(VerticalAlignment.CENTER);
// 4. 创建表头行(第 0 行,Excel 行号从 0 开始)
Row headerRow = sheet.createRow(0);
String[] headers = {"照片路径", "总体评分", "最终评判", "尺寸(像素)","DPI","尺寸是否符合","亮度",
"对比度","清晰度分数","背景均匀性","检测到人脸数量","人脸比例","人脸位置","色彩平衡分数",
"证件照质量","对比过程中的错误信息"}; // 表头内容
for (int i = 0; i < headers.length; i++) {
Cell cell = headerRow.createCell(i); // 创建单元格
cell.setCellValue(headers[i]); // 设置单元格内容
cell.setCellStyle(headerStyle); // 应用表头样式
sheet.autoSizeColumn(i); // 自动调整列宽(适配内容)
}
// 5. 填充数据行(从第 1 行开始,对应表头下的第一行数据)
for (int i = 0; i < allPhotos.size(); i++) {
System.out.println(allPhotos.get(i));
String path = allPhotos.get(i);
// String imagePath = "C:\\Users\\Administrator\\Pictures\\61272319870318004X.jpg";
// 执行校验
ValidationResult result = validator.validatePhoto(path, 300);
// 生成报告
Map<String, Object> map = validator.generateReportMap(result);
if (map.get("errormsg") != null){
throw new RuntimeException("出现错误");
}
// String report = validator.generateReport(result);
// System.out.println(report);
//
// // 根据结果判断是否合格
// if (result.isAcceptable()) {
// System.out.println("✓ 证件照质量合格");
// } else {
// System.out.println("✗ 证件照质量不合格,请重新拍摄");
// }
Row dataRow = sheet.createRow(i + 1); // 数据行号 = 索引 + 1(跳过表头)
// 填充每列数据,并应用样式
Cell cell0 = dataRow.createCell(0);
cell0.setCellValue(path);// 照片路径
cell0.setCellStyle(dataStyle);
Cell cell1 = dataRow.createCell(1);
cell1.setCellValue(map.get("allscore").toString()); // 总体评分
cell1.setCellStyle(dataStyle);
Cell cell2 = dataRow.createCell(2);
cell2.setCellValue(map.get("isQualified").toString()); // 最终评判
cell2.setCellStyle(dataStyle);
Cell cell3 = dataRow.createCell(3);
cell3.setCellValue(map.get("pixels").toString()); // 尺寸(像素)
cell3.setCellStyle(dataStyle);
Cell cell4 = dataRow.createCell(4);
cell4.setCellValue(map.get("dpi").toString()); // DPI
cell4.setCellStyle(dataStyle);
Cell cell5 = dataRow.createCell(5);
cell5.setCellValue(map.get("meetsStandard").toString()); // 尺寸是否符合标准
cell5.setCellStyle(dataStyle);
Cell cell6 = dataRow.createCell(6);
cell6.setCellValue(map.get("brightness").toString()); // 亮度
cell6.setCellStyle(dataStyle);
Cell cell7 = dataRow.createCell(7);
cell7.setCellValue(map.get("contrast").toString()); // 对比度
cell7.setCellStyle(dataStyle);
Cell cell8 = dataRow.createCell(8);
cell8.setCellValue(map.get("sharpness").toString()); // 清晰度分数
cell8.setCellStyle(dataStyle);
Cell cell9 = dataRow.createCell(9);
cell9.setCellValue(map.get("backgroundUniformity").toString()); // 背景均匀性
cell9.setCellStyle(dataStyle);
Cell cell10 = dataRow.createCell(10);
cell10.setCellValue(map.get("faceCount").toString()); // 检测到人脸数量
cell10.setCellStyle(dataStyle);
Cell cell11 = dataRow.createCell(11);
cell11.setCellValue(map.get("faceRatio").toString()); // 人脸比例
cell11.setCellStyle(dataStyle);
Cell cell12 = dataRow.createCell(12);
cell12.setCellValue(map.get("isFacePositionOk").toString()); // 人脸位置
cell12.setCellStyle(dataStyle);
Cell cell13 = dataRow.createCell(13);
cell13.setCellValue(map.get("colorBalance").toString()); // 色彩平衡分数
cell13.setCellStyle(dataStyle);
if (result.isAcceptable()
// && !(map.get("faceRatio").toString().contains("不合格")) // 不满足人脸比例要求的
) {
System.out.println("✓ 证件照质量合格");
map.put("quality","证件照质量合格");
} else {
// System.out.println("✗ 证件照质量不合格,请重新拍摄");
map.put("quality","证件照质量不合格");
}
Cell cell14 = dataRow.createCell(14);
cell14.setCellValue(map.get("quality").toString()); // 证件照质量
cell14.setCellStyle(dataStyle);
Cell cell15 = dataRow.createCell(15);
cell15.setCellValue(map.get("errmsg").toString()); // 对比过程中的错误信息
cell15.setCellStyle(dataStyle);
}
// 6. 写入文件(通过 FileOutputStream 输出到指定路径)
String dateform = getDate();
String outputPath = "C:\\Users\\Administrator\\Desktop\\update\\EasyExcel"+dateform+".xlsx";
try (FileOutputStream fos = new FileOutputStream(outputPath)) {
workbook.write(fos);
System.out.println("Excel 导出成功!保存路径:" + outputPath);
} catch (IOException e) {
System.out.println("Excel 导出失败:" + e.getMessage());
e.printStackTrace();
} finally {
// 7. 关闭工作簿,释放资源
try {
workbook.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
/**
* 分辨率检查结果类
* 功能:封装证件照分辨率检查的各项数据,包括像素尺寸、物理尺寸、DPI及是否符合标准
*/
class ResolutionResult {
// 图像宽度(像素数)
private int widthPixels;
// 图像高度(像素数)
private int heightPixels;
// 图像实际宽度(英寸,根据像素数和DPI计算)
private double widthInch;
// 图像实际高度(英寸,根据像素数和DPI计算)
private double heightInch;
// 图像的DPI(每英寸像素数,影响实际打印尺寸)
private int dpi;
// 分辨率是否符合证件照标准(true=符合,false=不符合)
private boolean meetsStandard;
/**
* 构造方法:初始化分辨率检查结果的所有属性
* @param widthPixels 图像宽度(像素)
* @param heightPixels 图像高度(像素)
* @param widthInch 实际宽度(英寸)
* @param heightInch 实际高度(英寸)
* @param dpi 图像的DPI值
* @param meetsStandard 是否符合标准
*/
public ResolutionResult(int widthPixels, int heightPixels, double widthInch,
double heightInch, int dpi, boolean meetsStandard) {
this.widthPixels = widthPixels;
this.heightPixels = heightPixels;
this.widthInch = widthInch;
this.heightInch = heightInch;
this.dpi = dpi;
this.meetsStandard = meetsStandard;
}
// getter和setter方法:用于访问和修改私有属性
/**
* 获取图像宽度(像素)
* @return 宽度像素数
*/
public int getWidthPixels() {
return widthPixels;
}
/**
* 设置图像宽度(像素)
* @param widthPixels 宽度像素数
*/
public void setWidthPixels(int widthPixels) {
this.widthPixels = widthPixels;
}
/**
* 获取图像高度(像素)
* @return 高度像素数
*/
public int getHeightPixels() {
return heightPixels;
}
/**
* 设置图像高度(像素)
* @param heightPixels 高度像素数
*/
public void setHeightPixels(int heightPixels) {
this.heightPixels = heightPixels;
}
/**
* 获取实际宽度(英寸)
* @return 宽度(英寸)
*/
public double getWidthInch() {
return widthInch;
}
/**
* 设置实际宽度(英寸)
* @param widthInch 宽度(英寸)
*/
public void setWidthInch(double widthInch) {
this.widthInch = widthInch;
}
/**
* 获取实际高度(英寸)
* @return 高度(英寸)
*/
public double getHeightInch() {
return heightInch;
}
/**
* 设置实际高度(英寸)
* @param heightInch 高度(英寸)
*/
public void setHeightInch(double heightInch) {
this.heightInch = heightInch;
}
/**
* 获取图像DPI值
* @return DPI(每英寸像素数)
*/
public int getDpi() {
return dpi;
}
/**
* 设置图像DPI值
* @param dpi DPI(每英寸像素数)
*/
public void setDpi(int dpi) {
this.dpi = dpi;
}
/**
* 判断分辨率是否符合标准
* @return true=符合标准,false=不符合
*/
public boolean isMeetsStandard() {
return meetsStandard;
}
/**
* 设置分辨率是否符合标准
* @param meetsStandard true=符合标准,false=不符合
*/
public void setMeetsStandard(boolean meetsStandard) {
this.meetsStandard = meetsStandard;
}
}
/**
* 亮度对比度检查结果类
* 功能:封装证件照亮度和对比度的检测数据,包括具体数值与是否合格的判断
*/
class BrightnessContrastResult {
// 亮度值:基于图像灰度通道的平均像素值(范围通常0-255)
private double brightness;
// 对比度值:基于图像灰度通道的像素标准差(值越大,对比度越高)
private double contrast;
// 亮度是否合格(true=符合阈值范围,false=不符合)
private boolean brightnessOk;
// 对比度是否合格(true=高于最小阈值,false=低于最小阈值)
private boolean contrastOk;
/**
* 构造方法:初始化亮度和对比度的检测结果
* @param brightness 亮度具体数值
* @param contrast 对比度具体数值
* @param brightnessOk 亮度是否合格
* @param contrastOk 对比度是否合格
*/
public BrightnessContrastResult(double brightness, double contrast,
boolean brightnessOk, boolean contrastOk) {
this.brightness = brightness;
this.contrast = contrast;
this.brightnessOk = brightnessOk;
this.contrastOk = contrastOk;
}
// getter和setter方法:用于安全访问和修改私有属性
/**
* 获取对比度数值
* @return 对比度(标准差)
*/
public double getContrast() {
return contrast;
}
/**
* 设置对比度数值
* @param contrast 对比度(标准差)
*/
public void setContrast(double contrast) {
this.contrast = contrast;
}
/**
* 判断亮度是否合格
* @return true=合格,false=不合格
*/
public boolean isBrightnessOk() {
return brightnessOk;
}
/**
* 设置亮度是否合格的状态
* @param brightnessOk true=合格,false=不合格
*/
public void setBrightnessOk(boolean brightnessOk) {
this.brightnessOk = brightnessOk;
}
/**
* 判断对比度是否合格
* @return true=合格,false=不合格
*/
public boolean isContrastOk() {
return contrastOk;
}
/**
* 设置对比度是否合格的状态
* @param contrastOk true=合格,false=不合格
*/
public void setContrastOk(boolean contrastOk) {
this.contrastOk = contrastOk;
}
/**
* 获取亮度数值
* @return 亮度(灰度通道平均值)
*/
public double getBrightness() {
return brightness;
}
/**
* 设置亮度数值
* @param brightness 亮度(灰度通道平均值)
*/
public void setBrightness(double brightness) {
this.brightness = brightness;
}
}
/**
* 清晰度检查结果类
* 功能:封装证件照清晰度的检测数据,包括清晰度分数与是否合格的判断
*/
class SharpnessResult {
// 清晰度分数:基于拉普拉斯算子结果的方差(值越大,图像越清晰)
private double sharpness;
// 清晰度是否合格(true=高于最小阈值,false=低于最小阈值)
private boolean sharpnessOk;
/**
* 构造方法:初始化清晰度的检测结果
* @param sharpness 清晰度分数
* @param sharpnessOk 清晰度是否合格
*/
public SharpnessResult(double sharpness, boolean sharpnessOk) {
this.sharpness = sharpness;
this.sharpnessOk = sharpnessOk;
}
// getter和setter方法:用于安全访问和修改私有属性
/**
* 获取清晰度分数
* @return 清晰度(拉普拉斯方差)
*/
public double getSharpness() {
return sharpness;
}
/**
* 设置清晰度分数
* @param sharpness 清晰度(拉普拉斯方差)
*/
public void setSharpness(double sharpness) {
this.sharpness = sharpness;
}
/**
* 判断清晰度是否合格
* @return true=合格,false=不合格
*/
public boolean isSharpnessOk() {
return sharpnessOk;
}
/**
* 设置清晰度是否合格的状态
* @param sharpnessOk true=合格,false=不合格
*/
public void setSharpnessOk(boolean sharpnessOk) {
this.sharpnessOk = sharpnessOk;
}
}
/**
* 背景检查结果类
* 功能:封装证件照背景均匀性的检测数据,包括均匀性分数与是否合格的判断
*/
class BackgroundResult {
// 背景均匀性分数:基于图像边缘区域的像素标准差(值越小,背景越均匀)
private double backgroundUniformity;
// 背景是否合格(true=均匀性达标,false=存在明显杂色/噪声)
private boolean backgroundOk;
/**
* 构造方法:初始化背景均匀性的检测结果
* @param backgroundUniformity 背景均匀性分数
* @param backgroundOk 背景是否合格
*/
public BackgroundResult(double backgroundUniformity, boolean backgroundOk) {
this.backgroundUniformity = backgroundUniformity;
this.backgroundOk = backgroundOk;
}
// getter和setter方法:用于安全访问和修改私有属性
/**
* 获取背景均匀性分数
* @return 均匀性(边缘区域标准差)
*/
public double getBackgroundUniformity() {
return backgroundUniformity;
}
/**
* 设置背景均匀性分数
* @param backgroundUniformity 均匀性(边缘区域标准差)
*/
public void setBackgroundUniformity(double backgroundUniformity) {
this.backgroundUniformity = backgroundUniformity;
}
/**
* 判断背景是否合格
* @return true=合格,false=不合格
*/
public boolean isBackgroundOk() {
return backgroundOk;
}
/**
* 设置背景是否合格的状态
* @param backgroundOk true=合格,false=不合格
*/
public void setBackgroundOk(boolean backgroundOk) {
this.backgroundOk = backgroundOk;
}
}
/**
* 人脸检测结果类
* 功能:封装证件照人脸检测的全量数据,包括检测状态、人脸参数与位置信息
*/
class FaceDetectionResult {
// 是否检测到人脸(true=检测到,false=未检测到)
private boolean faceDetected;
// 检测到的人脸数量(证件照通常需且仅需1张人脸)
private int faceCount;
// 人脸占比:人脸面积 / 图像总面积(反映人脸大小是否合适)
private double faceRatio;
// 人脸位置是否合格(true=居中,false=偏移过多)
private boolean facePositionOk;
// 人脸大小是否合格(true=占比在阈值范围内,false=过大/过小)
private boolean faceSizeOk;
// 人脸矩形区域:存储人脸在图像中的坐标(x,y,宽度,高度),用于后续标注
private Rect faceRect;
/**
* 构造方法:初始化人脸检测的全量结果
* @param faceDetected 是否检测到人脸
* @param faceCount 人脸数量
* @param faceRatio 人脸占比
* @param facePositionOk 位置是否合格
* @param faceSizeOk 大小是否合格
* @param faceRect 人脸矩形区域
*/
public FaceDetectionResult(boolean faceDetected, int faceCount, double faceRatio,
boolean facePositionOk, boolean faceSizeOk, Rect faceRect) {
this.faceDetected = faceDetected;
this.faceCount = faceCount;
this.faceRatio = faceRatio;
this.facePositionOk = facePositionOk;
this.faceSizeOk = faceSizeOk;
this.faceRect = faceRect;
}
// getter和setter方法:用于安全访问和修改私有属性
/**
* 判断是否检测到人脸
* @return true=检测到,false=未检测到
*/
public boolean isFaceDetected() {
return faceDetected;
}
/**
* 设置是否检测到人脸的状态
* @param faceDetected true=检测到,false=未检测到
*/
public void setFaceDetected(boolean faceDetected) {
this.faceDetected = faceDetected;
}
/**
* 获取检测到的人脸数量
* @return 人脸数量
*/
public int getFaceCount() {
return faceCount;
}
/**
* 设置检测到的人脸数量
* @param faceCount 人脸数量
*/
public void setFaceCount(int faceCount) {
this.faceCount = faceCount;
}
/**
* 获取人脸占比
* @return 人脸占比(0-1之间的小数)
*/
public double getFaceRatio() {
return faceRatio;
}
/**
* 设置人脸占比
* @param faceRatio 人脸占比(0-1之间的小数)
*/
public void setFaceRatio(double faceRatio) {
this.faceRatio = faceRatio;
}
/**
* 判断人脸位置是否合格
* @return true=合格,false=不合格
*/
public boolean isFacePositionOk() {
return facePositionOk;
}
/**
* 设置人脸位置是否合格的状态
* @param facePositionOk true=合格,false=不合格
*/
public void setFacePositionOk(boolean facePositionOk) {
this.facePositionOk = facePositionOk;
}
/**
* 判断人脸大小是否合格
* @return true=合格,false=不合格
*/
public boolean isFaceSizeOk() {
return faceSizeOk;
}
/**
* 设置人脸大小是否合格的状态
* @param faceSizeOk true=合格,false=不合格
*/
public void setFaceSizeOk(boolean faceSizeOk) {
this.faceSizeOk = faceSizeOk;
}
/**
* 获取人脸矩形区域
* @return 人脸坐标与尺寸(Rect对象)
*/
public Rect getFaceRect() {
return faceRect;
}
/**
* 设置人脸矩形区域
* @param faceRect 人脸坐标与尺寸(Rect对象)
*/
public void setFaceRect(Rect faceRect) {
this.faceRect = faceRect;
}
}
/**
* 色彩平衡检查结果类
* 功能:封装证件照色彩平衡的检测数据,包含量化的平衡分数与是否合格的判断结果
* 用于直观呈现图像是否存在偏色(如偏红、偏蓝、偏黄),确保色彩符合证件照标准
*/
class ColorBalanceResult {
// 色彩平衡分数:量化色彩偏移程度的核心指标
// 计算逻辑:(RGB三通道中最亮均值 - 最暗均值) / 最亮均值
// 数值范围:0~1,值越接近0表示三通道亮度越均衡(色彩越平衡),值越大表示偏色越严重
private double colorBalance;
// 色彩是否合格:基于平衡分数的最终判断结果
// 合格标准:一般设定为平衡分数<0.3(允许≤30%的色彩差异,避免明显偏色)
private boolean colorOk;
/**
* 构造方法:初始化色彩平衡检查的核心数据
* @param colorBalance 色彩平衡分数(0~1,值越小越平衡)
* @param colorOk 色彩是否合格(true=合格,false=不合格)
*/
public ColorBalanceResult(double colorBalance, boolean colorOk) {
this.colorBalance = colorBalance;
this.colorOk = colorOk;
}
// getter和setter方法:实现私有属性的安全访问与修改,符合面向对象封装原则
/**
* 获取色彩平衡分数
* @return 平衡分数(0~1)
*/
public double getColorBalance() {
return colorBalance;
}
/**
* 设置色彩平衡分数
* @param colorBalance 平衡分数(0~1)
*/
public void setColorBalance(double colorBalance) {
this.colorBalance = colorBalance;
}
/**
* 判断色彩是否合格
* @return true=色彩平衡达标(无明显偏色),false=色彩失衡(偏色严重)
*/
public boolean isColorOk() {
return colorOk;
}
/**
* 设置色彩是否合格的状态
* @param colorOk true=合格,false=不合格
*/
public void setColorOk(boolean colorOk) {
this.colorOk = colorOk;
}
}
/**
* 完整的验证结果类
* 功能:聚合所有专项检查结果(分辨率、亮度、人脸等),计算总体评分并判断是否合格
* 是证件照质量校验的最终结果载体,包含全量校验数据与结论
*/
class ValidationResult {
// 分辨率检查结果(关联ResolutionResult类)
private ResolutionResult resolution;
// 亮度对比度检查结果(关联BrightnessContrastResult类)
private BrightnessContrastResult brightnessContrast;
// 清晰度检查结果(关联SharpnessResult类)
private SharpnessResult sharpness;
// 背景均匀性检查结果(关联BackgroundResult类)
private BackgroundResult background;
// 人脸检测结果(关联FaceDetectionResult类)
private FaceDetectionResult faceDetection;
// 色彩平衡检查结果(关联ColorBalanceResult类)
private ColorBalanceResult colorBalance;
// 总体评分(所有合格项占比,满分100分)
private double overallScore;
// 是否合格(总体评分≥70分为合格)
private boolean acceptable;
// 错误信息(校验过程中若发生异常,存储异常描述)
private String error;
/**
* 计算总体评分与合格状态
* 逻辑:统计所有专项检查的合格项数量,按占比计算评分,70分以上判定为合格
*/
public void calculateOverallScore() {
// 1. 初始化列表,存储所有专项检查的合格状态(true=合格,false=不合格)
List<Boolean> allChecks = new ArrayList<>();
// 2. 添加固定专项检查的合格状态(共6项)
allChecks.add(resolution.isMeetsStandard()); // 分辨率是否合格
allChecks.add(brightnessContrast.isBrightnessOk()); // 亮度是否合格
allChecks.add(brightnessContrast.isContrastOk()); // 对比度是否合格
allChecks.add(sharpness.isSharpnessOk()); // 清晰度是否合格
allChecks.add(background.isBackgroundOk()); // 背景是否合格
allChecks.add(colorBalance.isColorOk()); // 色彩平衡是否合格
// 3. 条件添加人脸相关检查(仅当检测到人脸时,才统计人脸大小和位置的合格状态)
if (faceDetection.isFaceDetected()) {
allChecks.add(faceDetection.isFaceSizeOk()); // 人脸大小是否合格
allChecks.add(faceDetection.isFacePositionOk()); // 人脸位置是否合格
}
// 4. 统计合格项数量
int passed = 0;
for (Boolean check : allChecks) {
if (check) passed++; // 若某项合格,合格计数器+1
}
// 5. 计算总体评分:(合格项数 / 总检查项数) * 100(保留小数精度)
this.overallScore = (double) passed / allChecks.size() * 100;
// 6. 判断是否合格:总体评分≥70分为合格
this.acceptable = this.overallScore >= 65;
}
// getter和setter方法:用于安全访问和修改所有私有属性,实现数据封装
/**
* 获取分辨率检查结果
* @return 分辨率结果对象(ResolutionResult)
*/
public ResolutionResult getResolution() {
return resolution;
}
/**
* 设置分辨率检查结果
* @param resolution 分辨率结果对象(ResolutionResult)
*/
public void setResolution(ResolutionResult resolution) {
this.resolution = resolution;
}
/**
* 获取亮度对比度检查结果
* @return 亮度对比度结果对象(BrightnessContrastResult)
*/
public BrightnessContrastResult getBrightnessContrast() {
return brightnessContrast;
}
/**
* 设置亮度对比度检查结果
* @param brightnessContrast 亮度对比度结果对象(BrightnessContrastResult)
*/
public void setBrightnessContrast(BrightnessContrastResult brightnessContrast) {
this.brightnessContrast = brightnessContrast;
}
/**
* 获取清晰度检查结果
* @return 清晰度结果对象(SharpnessResult)
*/
public SharpnessResult getSharpness() {
return sharpness;
}
/**
* 设置清晰度检查结果
* @param sharpness 清晰度结果对象(SharpnessResult)
*/
public void setSharpness(SharpnessResult sharpness) {
this.sharpness = sharpness;
}
/**
* 获取背景检查结果
* @return 背景结果对象(BackgroundResult)
*/
public BackgroundResult getBackground() {
return background;
}
/**
* 设置背景检查结果
* @param background 背景结果对象(BackgroundResult)
*/
public void setBackground(BackgroundResult background) {
this.background = background;
}
/**
* 获取人脸检测结果
* @return 人脸检测结果对象(FaceDetectionResult)
*/
public FaceDetectionResult getFaceDetection() {
return faceDetection;
}
/**
* 设置人脸检测结果
* @param faceDetection 人脸检测结果对象(FaceDetectionResult)
*/
public void setFaceDetection(FaceDetectionResult faceDetection) {
this.faceDetection = faceDetection;
}
/**
* 获取色彩平衡检查结果
* @return 色彩平衡结果对象(ColorBalanceResult)
*/
public ColorBalanceResult getColorBalance() {
return colorBalance;
}
/**
* 设置色彩平衡检查结果
* @param colorBalance 色彩平衡结果对象(ColorBalanceResult)
*/
public void setColorBalance(ColorBalanceResult colorBalance) {
this.colorBalance = colorBalance;
}
/**
* 获取总体评分
* @return 总体评分(0-100分)
*/
public double getOverallScore() {
return overallScore;
}
/**
* 设置总体评分(一般通过calculateOverallScore()自动计算,非手动设置)
* @param overallScore 总体评分(0-100分)
*/
public void setOverallScore(double overallScore) {
this.overallScore = overallScore;
}
/**
* 判断证件照是否合格
* @return true=合格,false=不合格
*/
public boolean isAcceptable() {
return acceptable;
}
/**
* 设置是否合格(一般通过calculateOverallScore()自动判断,非手动设置)
* @param acceptable true=合格,false=不合格
*/
public void setAcceptable(boolean acceptable) {
this.acceptable = acceptable;
}
/**
* 获取校验过程中的错误信息
* @return 错误描述(无错误时为null)
*/
public String getError() {
return error;
}
/**
* 设置校验过程中的错误信息(发生异常时调用)
* @param error 错误描述
*/
public void setError(String error) {
this.error = error;
}
}
注意事项
- OpenCV安装:确保正确安装OpenCV并配置Java绑定
- 人脸检测器:需要提供正确的Haar级联分类器文件路径
- 性能优化:对于大量图片处理,可以考虑使用线程池
- 内存管理:及时释放Mat对象避免内存泄漏
- 异常处理:增强错误处理和日志记录
这个Java实现的证件照质量检测工具提供了全面的质量检查功能,可以根据实际需求进行调整和扩展
785

被折叠的 条评论
为什么被折叠?



