Java利用opencv实现人脸识别

opencv下载地址:https://opencv.org/releases.html

下载后安装,找到安装目录下:/opencv/build/java/,这个目录下面存放的就是Java的jar和所需要的dll。

然后还需要探测器:/opencv/build/etc/,这个目录下面的xml配置文件。

下面是代码:

package com.acgist.face;

import java.awt.image.BufferedImage;
import java.awt.image.DataBufferByte;
import java.io.IOException;
import java.io.InputStream;
import java.net.URISyntaxException;

import javax.imageio.ImageIO;

import org.opencv.core.CvType;
import org.opencv.core.Mat;
import org.opencv.core.MatOfRect;
import org.opencv.core.Point;
import org.opencv.core.Rect;
import org.opencv.core.Scalar;
import org.opencv.imgcodecs.Imgcodecs;
import org.opencv.imgproc.Imgproc;
import org.opencv.objdetect.CascadeClassifier;

/**
 * 人脸探测
 */
public class Face {

	private static final String DDL_PATH = "/opencv/opencv_java341.dll";
	private static final String XML_PATH = "/opencv/haarcascade_frontalface_alt.xml";	
	
	public static void main(String[] args) throws URISyntaxException, IOException {
//		System.loadLibrary(Core.NATIVE_LIBRARY_NAME);
		System.load(localPath(DDL_PATH));
		Mat image = openImage("/face/csol01.jpg");
//		Mat image = openImage("/face/崔智云.jpg");
		CascadeClassifier faceDetector = new CascadeClassifier(localPath(XML_PATH));
		MatOfRect faceDetections = new MatOfRect();
		faceDetector.detectMultiScale(image, faceDetections);
		for (Rect rect : faceDetections.toArray()) {
			Imgproc.rectangle(image, new Point(rect.x, rect.y), new Point(rect.x + rect.width, rect.y + rect.height), new Scalar(0, 255, 0));
		}
		String filename = "ouput.png";
		Imgcodecs.imwrite(filename, image);
		System.out.println("over");
	}
	
	public static final Mat openImage(String name) throws IOException {
//		不能读取含有中文的文件
//		String path = Face.class.getResource(name).getPath().substring(1);
//		Mat image = Imgcodecs.imread(path);
//		解决中文问题
		InputStream input = Face.class.getResourceAsStream(name);
		BufferedImage src = ImageIO.read(input);
		Mat image = new Mat(src.getHeight(), src.getWidth(), CvType.CV_8UC3);
		image.put(0, 0, ((DataBufferByte) src.getRaster().getDataBuffer()).getData());
		return image;
	}
	
	public static final String localPath(String name) {
		return Face.class.getResource(name).getPath().substring(1);
	}

}

这里需要注意Imgcodecs.imread这个方法不支持中文,所以这里使用BufferedImage转化为Mat

人脸识别需要使用:EigenFaceRecognizerFisherFaceRecognizer或者LBPHFaceRecognizer
但是opencv里面的代码测试时提示一下内容:

Exception in thread "main" java.lang.UnsatisfiedLinkError: org.opencv.face.EigenFaceRecognizer.create_1()J
	at org.opencv.face.EigenFaceRecognizer.create_1(Native Method)
	at org.opencv.face.EigenFaceRecognizer.create(EigenFaceRecognizer.java:36)
	at com.acgist.face.FaceJava.main(FaceJava.java:46)

所以后来使用了JavaCV这个工具,代码如下:

package com.acgist.face;

import java.io.File;
import java.io.IOException;
import java.util.ArrayList;
import java.util.List;

import org.bytedeco.javacpp.opencv_core.Mat;
import org.bytedeco.javacpp.opencv_core.MatVector;
import org.bytedeco.javacpp.opencv_face.EigenFaceRecognizer;
import org.bytedeco.javacpp.opencv_face.LBPHFaceRecognizer;
import org.bytedeco.javacpp.opencv_imgcodecs;

import com.acgist.utils.LocalPath;

/**
 * 人脸识别
 */
public class FaceID {

	private static final String FACE_PATH = CreateFace.FACE_PATH;
	private static final List<String> IMAGES = new ArrayList<>(); // 训练数据
	private static final List<String> TEST_IMAGES = new ArrayList<>(); // 测试数据-训练时排除这些数据
	
	static {
		TEST_IMAGES.add(LocalPath.localPath("/image/meimei/face/face-0009.jpg"));
		TEST_IMAGES.add(LocalPath.localPath("/image/meimei/face/face-0010.jpg"));
		TEST_IMAGES.add(LocalPath.localPath("/image/meimei/face/face-0011.jpg"));
		File floder = new File(FACE_PATH);
		String path = null;
		for (File image : floder.listFiles()) {
			path = image.getAbsolutePath();
			if(!TEST_IMAGES.contains(path)) {
				IMAGES.add(path);
			}
		}
	}
	
	public static void main(String[] args) throws IOException {
//		FaceID.train();
		FaceID.label();
	}
	
	/**
	 * 人脸置信度
	 */
	public static final void train() throws IOException {
		EigenFaceRecognizer eigenFaceRecognizer = EigenFaceRecognizer.create();
//		FisherFaceRecognizer fisherFaceRecognizer = FisherFaceRecognizer.create(); // 训练时必须大于等于两个label
		LBPHFaceRecognizer lbphFaceRecognizer = LBPHFaceRecognizer.create();
		int index = 0;
		int[] labes = new int[IMAGES.size()];
		MatVector src = new MatVector();
		for (String image : IMAGES) {
			System.out.println("开始训练:" + image);
//			src.push_back(opencv_imgcodecs.imread(image, opencv_imgcodecs.CV_LOAD_IMAGE_GRAYSCALE));
			src.push_back(opencv_imgcodecs.imread(image, opencv_imgcodecs.CV_LOAD_IMAGE_GRAYSCALE));
			labes[index++] = 1;
		}
		eigenFaceRecognizer.train(src, new Mat(labes));
//		fisherFaceRecognizer.train(src, new Mat(labes));
		lbphFaceRecognizer.train(src, new Mat(labes));
		
		// 保存训练模型
		eigenFaceRecognizer.save("eigenFace.xml");
		lbphFaceRecognizer.save("lbphFace.xml");
		
		// 加载训练模型
//		eigenFaceRecognizer = EigenFaceRecognizer.create();
//		eigenFaceRecognizer.read("eigenFace.xml");
//		lbphFaceRecognizer = LBPHFaceRecognizer.create();
//		lbphFaceRecognizer.read("lbphFace.xml");
		
		// 图片识别
//		IntBuffer label = IntBuffer.allocate(1);
//		DoubleBuffer confidence = DoubleBuffer.allocate(1);
		int [] label = new int[1];
		double [] confidence = new double[1];
		for (String path : TEST_IMAGES) {
			eigenFaceRecognizer.predict(opencv_imgcodecs.imread(path, opencv_imgcodecs.CV_LOAD_IMAGE_GRAYSCALE), label, confidence);
			System.out.print("测试图片:" + path);
			System.out.print(",标签(label):" + label[0]);
			System.out.println(",置信度(confidence):" + confidence[0]);
		}
		for (String path : TEST_IMAGES) {
			lbphFaceRecognizer.predict(opencv_imgcodecs.imread(path, opencv_imgcodecs.CV_LOAD_IMAGE_GRAYSCALE), label, confidence);
			System.out.print("测试图片:" + path);
			System.out.print(",标签(label):" + label[0]);
			System.out.println(",置信度(confidence):" + confidence[0]);
		}
//		System.out.println("label:" + eigenFaceRecognizer.predict_label(opencv_imgcodecs.imread("E://face85.jpg", opencv_imgcodecs.CV_LOAD_IMAGE_GRAYSCALE)));
	}
	
	/**
	 * 打标签
	 */
	public static final void label() {
		EigenFaceRecognizer eigenFaceRecognizer = EigenFaceRecognizer.create();
//		FisherFaceRecognizer fisherFaceRecognizer = FisherFaceRecognizer.create(); // 训练时必须大于等于两个label
		LBPHFaceRecognizer lbphFaceRecognizer = LBPHFaceRecognizer.create();
		int index = 0;
		IMAGES.addAll(TEST_IMAGES); // 添加测试数据
		int[] labes = new int[IMAGES.size()];
		MatVector src = new MatVector();
		for (String image : IMAGES) {
			System.out.println("开始训练:" + image);
//			src.push_back(opencv_imgcodecs.imread(image, opencv_imgcodecs.CV_LOAD_IMAGE_GRAYSCALE));
			src.push_back(opencv_imgcodecs.imread(image, opencv_imgcodecs.CV_LOAD_IMAGE_GRAYSCALE));
			if(image.contains("face-0010")) {
				labes[index++] = 2;
			} else if(image.contains("face-0011")) {
				labes[index++] = 3;
			} else {
				labes[index++] = 1;
			}
		}
		eigenFaceRecognizer.train(src, new Mat(labes));
//		fisherFaceRecognizer.train(src, new Mat(labes));
		lbphFaceRecognizer.train(src, new Mat(labes));
		
		// 保存训练模型
		eigenFaceRecognizer.save("eigenFace.xml");
		lbphFaceRecognizer.save("lbphFace.xml");
		
		// 加载训练模型
//		eigenFaceRecognizer = EigenFaceRecognizer.create();
//		eigenFaceRecognizer.read("eigenFace.xml");
//		lbphFaceRecognizer = LBPHFaceRecognizer.create();
//		lbphFaceRecognizer.read("lbphFace.xml");
		
		// 图片识别
		for (String path : TEST_IMAGES) {
			System.out.println("label:" + eigenFaceRecognizer.predict_label(opencv_imgcodecs.imread(path, opencv_imgcodecs.CV_LOAD_IMAGE_GRAYSCALE)));
		}
	}

}

不过测试的时候发现置信度并不是0-1.0之间,而是一个非常夸张的数字。😤

完整代码:https://github.com/acgist/aimldl/tree/master/opencv-face