JavaCV人脸训练和人脸识别代码实现
Posted 洛阳泰山
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了JavaCV人脸训练和人脸识别代码实现相关的知识,希望对你有一定的参考价值。
一、首先找两个人物的头像照片,分别用文件区分放置
二,然后用java代码训练生成模型文件
首先maven中引入javacv依赖jar包
<!-- https://mvnrepository.com/artifact/org.bytedeco/javacv-platform -->
<dependency>
<groupId>org.bytedeco</groupId>
<artifactId>javacv-platform</artifactId>
<version>1.5.5</version>
</dependency>
java代码如下:
import org.bytedeco.opencv.global.opencv_imgcodecs;
import org.bytedeco.opencv.opencv_core.Mat;
import org.bytedeco.opencv.opencv_core.MatVector;
import org.bytedeco.opencv.opencv_core.Size;
import org.bytedeco.opencv.opencv_face.FaceRecognizer;
import org.bytedeco.opencv.opencv_face.FisherFaceRecognizer;
import java.io.File;
import java.io.IOException;
import java.nio.IntBuffer;
import java.util.LinkedList;
import java.util.List;
import static org.bytedeco.opencv.global.opencv_core.CV_32SC1;
import static org.bytedeco.opencv.global.opencv_imgcodecs.IMREAD_GRAYSCALE;
import static org.bytedeco.opencv.global.opencv_imgproc.resize;
/**
* @author willzhao
* @version 1.0
* @description 训练
* @date 2021/12/12 18:26
*/
public class FaceTrainDemo
/**
* 从指定目录下
* @param dirs
* @param outputPath
*/
private void train(String[] dirs, String outputPath)
int totalImageNums = 0;
// 统计每个路径下的照片数,加在一起就是照片总数
for(String dir : dirs)
List<String> files = getAllFilePath(dir);
totalImageNums += files.size();
//todo截取人脸
System.out.println("total : " + totalImageNums);
// 这里用来保存每一张照片的序号,和照片的Mat对象
MatVector imageIndexMatMap = new MatVector(totalImageNums);
Mat lables = new Mat(totalImageNums, 1, CV_32SC1);
// 这里用来保存每一张照片的序号,和照片的类别
IntBuffer lablesBuf = lables.createBuffer();
// 类别序号,从1开始,dirs中的每个目录就是一个类别
int kindIndex = 1;
// 照片序号,从0开始
int imageIndex = 0;
// 每个目录下的照片都遍历
for(String dir : dirs)
// 得到当前目录下所有照片的绝对路径
List<String> files = getAllFilePath(dir);
// 处理一个目录下的每张照片,它们的序号不同,类别相同
for(String file : files)
// imageIndexMatMap放的是照片的序号和Mat对象
imageIndexMatMap.put(imageIndex, read(file));
// bablesBuf放的是照片序号和类别
lablesBuf.put(imageIndex, kindIndex);
// 照片序号加一
imageIndex++;
// 每当遍历完一个目录,才会将类别加一
kindIndex++;
// 实例化人脸识别类
FaceRecognizer faceRecognizer = FisherFaceRecognizer.create();
// 训练,入参就是图片集合和分类集合
faceRecognizer.train(imageIndexMatMap, lables);
// 训练完成后,模型保存在指定位置
faceRecognizer.save(outputPath);
//释放资源
faceRecognizer.close();
/**
* 读取指定图片的灰度图,调整为指定大小
* @param path
* @return
*/
private static Mat read(String path)
Mat faceMat = opencv_imgcodecs.imread(path,IMREAD_GRAYSCALE);
resize(faceMat, faceMat, new Size(164, 164));
return faceMat;
/**
* 把指定路径下所有文件的绝对路径放入list集合中返回
* @param path
* @return
*/
public static List<String> getAllFilePath(String path)
List<String> paths = new LinkedList<>();
File file = new File(path);
if (file.exists())
// 列出该目录下的所有文件
File[] files = file.listFiles();
for (File f : files)
if (!f.isDirectory())
// 把每个文件的绝对路径都放在list中
paths.add(f.getAbsolutePath());
return paths;
public static void main(String[] args) throws IOException
String base = "D:\\\\face_img\\\\";
// 存储图片的两个目录
// man目录下保存了群众演员A的所有人脸照片,
// woman目录下保存了群众演员B的所有人脸照片
String[] dirs = base + "msk", base + "tlp";
// 开始训练,并指定模型输出位置
new FaceTrainDemo().train(dirs, base + "faceRecognizer.xml");
注意 dirs数组至少是两个。
idea编辑器右键run运行即可,会生成一个faceRecognizer.xml的模型文件。
java识别代码如下:
kindNameMap的key是训练代码中dirs数组顺序,第一个是msk,第二个是tlp,以此类推。
import org.bytedeco.javacv.Frame;
import org.bytedeco.javacv.Java2DFrameConverter;
import org.bytedeco.javacv.OpenCVFrameConverter;
import org.bytedeco.opencv.opencv_core.*;
import org.bytedeco.opencv.opencv_face.FaceRecognizer;
import org.bytedeco.opencv.opencv_face.FisherFaceRecognizer;
import org.bytedeco.opencv.opencv_objdetect.CascadeClassifier;
import javax.imageio.ImageIO;
import java.awt.image.BufferedImage;
import java.io.File;
import java.io.IOException;
import java.util.HashMap;
import java.util.Map;
import static org.bytedeco.opencv.global.opencv_imgproc.*;
/**
* @author tarzan
*/
public class FaceDemo
public static void main(String[] args) throws IOException
Map<Integer, String> kindNameMap=new HashMap<>(2);
kindNameMap.put(1,"msk");
kindNameMap.put(2,"tlp");
faceRecognize("D:\\\\face_img\\\\test\\\\msk.jpg",kindNameMap);
/**
* 调整后的文件宽度
*/
public final static int RESIZE_WIDTH = 164;
/**
* 调整后的文件高度
*/
public final static int RESIZE_HEIGHT = 164;
/**
* 超过这个置信度就明显有问题了
*/
public final static double MAX_CONFIDENCE = 50d;
public final static String frontalFaceModelPath="D:\\\\face_img\\\\lbpcascade_frontalface.xml";
public final static String faceRecognizerPath="D:\\\\face_img\\\\faceRecognizer.xml";
/**
* 人脸检测
*
* @param filePath 图片路径
*/
public static RectVector faceDetect(Mat grayImg) throws IOException
// 读取opencv人脸检测器
CascadeClassifier cascade = new CascadeClassifier(frontalFaceModelPath);
// 检测到的人脸
RectVector faces = new RectVector();
//多人脸检测
cascade.detectMultiScale(grayImg, faces);
return faces;
/**
* 人脸识别
*
* @param kindNameMap 人物名称集合
*/
public static void faceRecognize(String filePath,Map<Integer, String> kindNameMap) throws IOException
File file=new File(filePath);
BufferedImage image = ImageIO.read(file);
Java2DFrameConverter imageConverter = new Java2DFrameConverter();
Frame frame = imageConverter.convert(image);
//类型转换
OpenCVFrameConverter.ToMat converter = new OpenCVFrameConverter.ToMat();
Mat original = converter.convertToMat(frame);
//存放灰度图
Mat grayImg = new Mat();
//模式设置成ImageMode.Gray下不需要再做灰度 摄像头获取的是彩色图像,所以先灰度化下
cvtColor(original, grayImg, COLOR_BGRA2GRAY);
// 均衡化直方图
equalizeHist(grayImg, grayImg);
RectVector faces=faceDetect(grayImg);
// 遍历人脸
for (int i = 0; i < faces.size(); i++)
Rect face_i = faces.get(i);
//绘制人脸矩形区域,scalar色彩顺序:BGR(蓝绿红)
rectangle(original, face_i, new Scalar(0, 255, 0, 1));
int pos_x = Math.max(face_i.tl().x() - 10, 0);
int pos_y = Math.max(face_i.tl().y() - 10, 0);
oneFaceRecognize(grayImg,face_i,kindNameMap);
// 在人脸矩形上方绘制提示文字(中文会乱码)
putText(original, "people face", new Point(pos_x, pos_y), FONT_HERSHEY_COMPLEX, 1.0, new Scalar(0, 0, 255, 2.0));
frame = converter.convert(original);
image = imageConverter.convert(frame);
String fileName=file.getName();
String extension=fileName.substring(fileName.lastIndexOf(".")+1);
String newFileName=fileName.substring(0,fileName.lastIndexOf("."))+"_result."+extension;
ImageIO.write(image, extension, new File(file.getParent()+File.separator+newFileName));
/**
* 人脸识别
*
* @param kindNameMap 人物名称集合
*/
public static String oneFaceRecognize(Mat grayImg,Rect face,Map<Integer, String> kindNameMap)
String kindName=null;
Mat mat=new Mat(grayImg, face);
Size size= new Size(RESIZE_WIDTH, RESIZE_HEIGHT);
// 核心代码,把检测到的人脸拿去识别
// 调整到和训练一致的尺寸
resize(mat, mat, size);
// 推理结果的标签
int[] labels = new int[1];
// 推理结果的置信度
double[] confidences = new double[1];
try
// 推理(这一行可能抛出RuntimeException异常,因此要补货,否则会导致程序退出)
FaceRecognizer faceRecognizer= FisherFaceRecognizer.create();
// 加载的是训练时生成的模型
faceRecognizer.read(faceRecognizerPath);
// 设置门限,这个可以根据您自身的情况不断调整
faceRecognizer.setThreshold(MAX_CONFIDENCE);
//人脸检测
faceRecognizer.predict(mat, labels, confidences);
catch (RuntimeException runtimeException)
runtimeException.printStackTrace();
// 得到分类编号后,从map中取得名字,用来显示
if (kindNameMap.containsKey(labels[0]))
kindName = String.format("%s, confidence : %.4f", kindNameMap.get(labels[0]), confidences[0]);
else
// 取不到名字的时候,就显示unknown
kindName = "unknown(" + labels[0] + ")";
System.out.println(kindName);
return kindName;
右键运行,即可。
注意事项:
1.文件路径最好不要用中文。
2.图片格式不要自己随便改后缀,否则有可能读取不到图片。
相关文章推荐
JavaCV 实现图片中人脸检测完整代码教程_洛阳泰山的博客-CSDN博客_javacv人脸匹配
以上是关于JavaCV人脸训练和人脸识别代码实现的主要内容,如果未能解决你的问题,请参考以下文章