|
@@ -8,9 +8,14 @@ import org.opencv.imgcodecs.Imgcodecs;
|
|
|
import org.opencv.imgproc.Imgproc;
|
|
import org.opencv.imgproc.Imgproc;
|
|
|
import org.opencv.objdetect.CascadeClassifier;
|
|
import org.opencv.objdetect.CascadeClassifier;
|
|
|
import org.springframework.beans.factory.annotation.Value;
|
|
import org.springframework.beans.factory.annotation.Value;
|
|
|
|
|
+import org.springframework.web.multipart.MultipartFile;
|
|
|
|
|
|
|
|
-import java.util.Arrays;
|
|
|
|
|
-import java.util.Set;
|
|
|
|
|
|
|
+import java.io.IOException;
|
|
|
|
|
+import java.util.*;
|
|
|
|
|
+import java.util.concurrent.ExecutionException;
|
|
|
|
|
+import java.util.concurrent.ExecutorService;
|
|
|
|
|
+import java.util.concurrent.Executors;
|
|
|
|
|
+import java.util.concurrent.Future;
|
|
|
|
|
|
|
|
/**
|
|
/**
|
|
|
* 面部对比工具
|
|
* 面部对比工具
|
|
@@ -19,21 +24,23 @@ import java.util.Set;
|
|
|
*/
|
|
*/
|
|
|
public class FaceMatchUtil
|
|
public class FaceMatchUtil
|
|
|
{
|
|
{
|
|
|
- // 初始化人脸探测器
|
|
|
|
|
- static CascadeClassifier faceDetector;
|
|
|
|
|
|
|
|
|
|
@Value("${ktg-mes.prod}")
|
|
@Value("${ktg-mes.prod}")
|
|
|
private static String prodApi;
|
|
private static String prodApi;
|
|
|
|
|
|
|
|
|
|
+ // 加载OpenCV本地库
|
|
|
static {
|
|
static {
|
|
|
- // 加载OpenCV本地库
|
|
|
|
|
- // System.loadLibrary(Core.NATIVE_LIBRARY_NAME);
|
|
|
|
|
String loadPath = "/guoruan/app/opencv/build/java/x64/opencv_java470.dll";
|
|
String loadPath = "/guoruan/app/opencv/build/java/x64/opencv_java470.dll";
|
|
|
if (StringUtils.isBlank(prodApi)) {
|
|
if (StringUtils.isBlank(prodApi)) {
|
|
|
loadPath = "C:/work/app/install/opencv/build/java/x64/opencv_java470.dll";
|
|
loadPath = "C:/work/app/install/opencv/build/java/x64/opencv_java470.dll";
|
|
|
}
|
|
}
|
|
|
System.load(loadPath);
|
|
System.load(loadPath);
|
|
|
- // 从配置文件lbpcascade_frontalface.xml中创建一个人脸识别器,文件位于opencv安装目录中
|
|
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ // 初始化人脸探测器
|
|
|
|
|
+ private static CascadeClassifier faceDetector;
|
|
|
|
|
+
|
|
|
|
|
+ static {
|
|
|
String faceDetectorPath = "/guoruan/app/opencv/sources/data/haarcascades/haarcascade_frontalface_alt.xml";
|
|
String faceDetectorPath = "/guoruan/app/opencv/sources/data/haarcascades/haarcascade_frontalface_alt.xml";
|
|
|
if (StringUtils.isBlank(prodApi)) {
|
|
if (StringUtils.isBlank(prodApi)) {
|
|
|
faceDetectorPath = "C:/work/app/install/opencv/sources/data/haarcascades/haarcascade_frontalface_alt.xml";
|
|
faceDetectorPath = "C:/work/app/install/opencv/sources/data/haarcascades/haarcascade_frontalface_alt.xml";
|
|
@@ -41,73 +48,88 @@ public class FaceMatchUtil
|
|
|
faceDetector = new CascadeClassifier(faceDetectorPath);
|
|
faceDetector = new CascadeClassifier(faceDetectorPath);
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
- /* public static void main(String[] args) {
|
|
|
|
|
- double comparison = faceRecognitionComparison("C:/work/file/11.jpg", "C:/work/file/13.jpg");
|
|
|
|
|
- System.out.println("对比结果:" + comparison);
|
|
|
|
|
- if (comparison > 0.85) {
|
|
|
|
|
- System.out.println("人脸匹配成功1");
|
|
|
|
|
- } else {
|
|
|
|
|
- System.out.println("人脸不匹配识别1");
|
|
|
|
|
- }
|
|
|
|
|
-
|
|
|
|
|
- double comparison2 = faceRecognitionComparison("C:/work/file/12.jpg", "C:/work/file/13.jpg");
|
|
|
|
|
- System.out.println("对比结果:" + comparison2);
|
|
|
|
|
- if (comparison2 > 0.85) {
|
|
|
|
|
- System.out.println("人脸匹配成功2");
|
|
|
|
|
- } else {
|
|
|
|
|
- System.out.println("人脸不匹配识别2");
|
|
|
|
|
- }
|
|
|
|
|
- // 终止当前运行的 Java 虚拟机。
|
|
|
|
|
- System.exit(0);
|
|
|
|
|
- }*/
|
|
|
|
|
-
|
|
|
|
|
-
|
|
|
|
|
/**
|
|
/**
|
|
|
- * 人脸识别比对
|
|
|
|
|
|
|
+ * 人脸识别对比
|
|
|
*/
|
|
*/
|
|
|
- public static FaceMatchVO faceRecognitionComparison(String image1, Set<String> image2) {
|
|
|
|
|
- Mat mat1 = conv_Mat(image1);
|
|
|
|
|
|
|
+ public static FaceMatchVO faceRecognitionComparison(MultipartFile mfile, Set<String> image2) throws IOException {
|
|
|
|
|
+ Mat mat1 = conv_mat(mfile); // 转换为灰度图像的Mat对象
|
|
|
|
|
+ Mat hist1 = computeHistogram(mat1); // 计算第一个图像的直方图
|
|
|
|
|
|
|
|
- Mat mat3 = new Mat();
|
|
|
|
|
- Mat mat4 = new Mat();
|
|
|
|
|
- // 颜色范围
|
|
|
|
|
- MatOfFloat ranges = new MatOfFloat(0f, 256f);
|
|
|
|
|
- // 直方图大小, 越大匹配越精确 (越慢)
|
|
|
|
|
- MatOfInt histSize = new MatOfInt(1000);
|
|
|
|
|
|
|
+ ExecutorService executorService = Executors.newFixedThreadPool(Runtime.getRuntime().availableProcessors()); // 创建线程池
|
|
|
|
|
+ List<Future<FaceMatchVO>> futures = new ArrayList<>(); // 存储Future对象的列表
|
|
|
|
|
|
|
|
- Imgproc.calcHist(Arrays.asList(mat1), new MatOfInt(0), new Mat(), mat3, histSize, ranges);
|
|
|
|
|
|
|
+ for (String imgPath : image2) { // 遍历第二个图像集合
|
|
|
|
|
+ futures.add(executorService.submit(() -> {
|
|
|
|
|
+ Mat mat2 = conv_Mat(imgPath); // 将路径对应的图像转换为灰度图像的Mat对象
|
|
|
|
|
+ if (mat2 == null) {
|
|
|
|
|
+ return null; // 如果转换失败,返回null
|
|
|
|
|
+ }
|
|
|
|
|
+ Mat hist2 = computeHistogram(mat2); // 计算第二个图像的直方图
|
|
|
|
|
+ double v = Imgproc.compareHist(hist1, hist2, Imgproc.CV_COMP_CORREL); // 比较两个直方图的相似度
|
|
|
|
|
+ if (v > 0.80) { // 如果相似度大于0.80,认为匹配成功
|
|
|
|
|
+ return new FaceMatchVO().setContent(imgPath).setScore(v); // 返回匹配结果
|
|
|
|
|
+ } else {
|
|
|
|
|
+ return null; // 否则返回null
|
|
|
|
|
+ }
|
|
|
|
|
+ }));
|
|
|
|
|
+ }
|
|
|
|
|
|
|
|
- // 比较两个密集或两个稀疏直方图
|
|
|
|
|
- for (String s : image2) {
|
|
|
|
|
- Mat mat2 = conv_Mat(s);
|
|
|
|
|
- Imgproc.calcHist(Arrays.asList(mat2), new MatOfInt(0), new Mat(), mat4, histSize, ranges);
|
|
|
|
|
- double v = Imgproc.compareHist(mat3, mat4, Imgproc.CV_COMP_CORREL);
|
|
|
|
|
- System.out.println("匹配人脸:" + s + ",分数:" + v);
|
|
|
|
|
- if (v > 0.80) {
|
|
|
|
|
- System.out.println("匹配到了合适的人脸信息,人脸照片" + s + ",人脸分数" + v);
|
|
|
|
|
- return new FaceMatchVO().setContent(s).setScore(v);
|
|
|
|
|
|
|
+ try {
|
|
|
|
|
+ for (Future<FaceMatchVO> future : futures) { // 遍历所有Future对象
|
|
|
|
|
+ FaceMatchVO result = future.get(); // 获取执行结果
|
|
|
|
|
+ if (result != null) { // 如果结果不为空,说明找到了匹配的图像
|
|
|
|
|
+ executorService.shutdownNow(); // 立即关闭线程池
|
|
|
|
|
+ return result; // 返回匹配结果
|
|
|
|
|
+ }
|
|
|
}
|
|
}
|
|
|
|
|
+ } catch (InterruptedException | ExecutionException e) { // 捕获异常
|
|
|
|
|
+ e.printStackTrace(); // 打印异常信息
|
|
|
|
|
+ } finally {
|
|
|
|
|
+ executorService.shutdown(); // 确保线程池最终被关闭
|
|
|
|
|
+ }
|
|
|
|
|
+ return null; // 如果没有找到匹配的图像,返回null
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ /**
|
|
|
|
|
+ * 灰度化人脸并返回Mat对象
|
|
|
|
|
+ */
|
|
|
|
|
+ public static Mat conv_mat(MultipartFile mfile) throws IOException {
|
|
|
|
|
+ byte[] bytes = mfile.getBytes(); // 获取文件字节数组
|
|
|
|
|
+ Mat mat1 = Imgcodecs.imdecode(new MatOfByte(bytes), Imgcodecs.IMREAD_COLOR); // 解码为彩色图像的Mat对象
|
|
|
|
|
+ Mat mat2 = new Mat(); // 创建一个新的Mat对象用于存储灰度图像
|
|
|
|
|
+ Imgproc.cvtColor(mat1, mat2, Imgcodecs.IMREAD_COLOR); // 将彩色图像转换为灰度图像
|
|
|
|
|
+ MatOfRect faceDetections = new MatOfRect(); // 创建用于存储检测到的人脸区域的MatOfRect对象
|
|
|
|
|
+ faceDetector.detectMultiScale(mat1, faceDetections); // 检测人脸区域
|
|
|
|
|
+ for (Rect rect : faceDetections.toArray()) { // 遍历检测到的人脸区域
|
|
|
|
|
+ return new Mat(mat1, rect); // 返回包含人脸区域的子Mat对象
|
|
|
}
|
|
}
|
|
|
- return null;
|
|
|
|
|
|
|
+ return null; // 如果没有检测到人脸,返回null
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
/**
|
|
|
- * 灰度化人脸
|
|
|
|
|
|
|
+ * 灰度化人脸并返回Mat对象
|
|
|
*/
|
|
*/
|
|
|
- public static Mat conv_Mat(String img) {
|
|
|
|
|
- // 读取图像
|
|
|
|
|
- Mat mat1 = Imgcodecs.imread(img);
|
|
|
|
|
- Mat mat2 = new Mat();
|
|
|
|
|
- // 灰度化:将图像从一种颜色空间转换为另一种颜色空间
|
|
|
|
|
- Imgproc.cvtColor(mat1, mat2, Imgproc.COLOR_BGR2GRAY);
|
|
|
|
|
- // 探测人脸:检测到的对象作为矩形列表返回
|
|
|
|
|
- MatOfRect faceDetections = new MatOfRect();
|
|
|
|
|
- faceDetector.detectMultiScale(mat1, faceDetections);
|
|
|
|
|
- // rect中人脸图片的范围
|
|
|
|
|
- for (Rect rect : faceDetections.toArray()) {
|
|
|
|
|
- Mat face = new Mat(mat1, rect);
|
|
|
|
|
- return face;
|
|
|
|
|
|
|
+ public static Mat conv_Mat(String imgPath) {
|
|
|
|
|
+ Mat mat1 = Imgcodecs.imread(imgPath); // 读取图像文件并转换为Mat对象
|
|
|
|
|
+ Mat mat2 = new Mat(); // 创建一个新的Mat对象用于存储灰度图像
|
|
|
|
|
+ Imgproc.cvtColor(mat1, mat2, Imgcodecs.IMREAD_COLOR); // 将彩色图像转换为灰度图像
|
|
|
|
|
+ MatOfRect faceDetections = new MatOfRect(); // 创建用于存储检测到的人脸区域的MatOfRect对象
|
|
|
|
|
+ faceDetector.detectMultiScale(mat1, faceDetections); // 检测人脸区域
|
|
|
|
|
+ for (Rect rect : faceDetections.toArray()) { // 遍历检测到的人脸区域
|
|
|
|
|
+ return new Mat(mat1, rect); // 返回包含人脸区域的子Mat对象
|
|
|
}
|
|
}
|
|
|
- return null;
|
|
|
|
|
|
|
+ return null; // 如果没有检测到人脸,返回null
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ /**
|
|
|
|
|
+ * 计算直方图
|
|
|
|
|
+ */
|
|
|
|
|
+ private static Mat computeHistogram(Mat mat) {
|
|
|
|
|
+ Mat hist = new Mat(); // 创建用于存储直方图的Mat对象
|
|
|
|
|
+ MatOfFloat ranges = new MatOfFloat(0f, 256f); // 定义直方图的范围
|
|
|
|
|
+ MatOfInt histSize = new MatOfInt(1000); // 定义直方图的大小(越大越精确,但计算速度越慢)
|
|
|
|
|
+ Imgproc.calcHist(Collections.singletonList(mat), new MatOfInt(0), new Mat(), hist, histSize, ranges); // 计算直方图
|
|
|
|
|
+ Core.normalize(hist, hist, 0, 1, Core.NORM_MINMAX); // 归一化直方图,使其值在0到1之间
|
|
|
|
|
+ return hist; // 返回计算好的直方图
|
|
|
}
|
|
}
|
|
|
}
|
|
}
|