- 人脸检测:在图片或视频中找到人脸的位置(用矩形框标出),这是识别的基础。
- 人脸识别:在检测到的人脸基础上,判断这个人是谁。
我们将从最简单的人脸检测开始,然后逐步深入到人脸识别。

准备工作:环境配置
在开始编码之前,你需要准备好 OpenCV Java 的开发环境。
下载 OpenCV
访问 OpenCV 官网下载页面:https://opencv.org/releases/
下载适合你操作系统的版本(opencv-4.x.x-windows)。
配置 IDE (以 IntelliJ IDEA 为例)
这是最关键的一步,让你的 Java 项目能够找到并使用 OpenCV 的库文件(.dll 或 .so)。
- 解压 OpenCV:将下载的
.zip文件解压到一个固定路径,D:\dev\opencv。 - 创建新项目:在 IntelliJ IDEA 中创建一个新的 Java 项目。
- 添加 OpenCV 库:
- 打开
File->Project Structure...。 - 选择
Libraries-> 点击 号 -> 选择Java。 - 浏览并选择你解压的 OpenCV 目录下的
build/java/opencv-4xx.jar文件(xx是版本号)。 - 非常重要:在接下来的窗口中,确保勾选了 "Extract to the project"(或者 "Copy to the project"),这会将 JAR 文件复制到你的项目中。
- 点击
OK。
- 打开
- 配置 Native Library Location:
- 再次打开
Project Structure...->Libraries。 - 找到你刚刚添加的
opencv-4xx.jar库,选中它。 - 在右侧的
Native library locations中,点击文件夹图标,然后选择 OpenCV 解压目录下的build/java/x64(如果你是 64 位系统) 或build/java/x86(32 位系统)。 - 点击
OK。
- 再次打开
现在你的项目已经可以正确使用 OpenCV 了。

第一部分:人脸检测
人脸检测的原理是使用一个预先训练好的“级联分类器”(Haar Cascade 或 LBP)模型,它在图像中滑动窗口,判断每个窗口是否包含人脸。
获取分类器文件
你需要一个 .xml 格式的分类器文件,OpenCV 安装包中已经包含了。
- Haar Cascade 人脸检测器:效果较好,但对光照敏感,路径:
<opencv_path>/data/haarcascades/haarcascade_frontalface_alt.xml - LBP (Local Binary Patterns) 人脸检测器:速度更快,对光照不敏感,但精度略低,路径:
<opencv_path>/data/lbpcascades/lbpcascade_frontalface.xml
我们将使用 Haar Cascade 作为示例。
代码实现:从图片中检测人脸
import org.opencv.core.*;
import org.opencv.imgcodecs.Imgcodecs;
import org.opencv.imgproc.Imgproc;
import org.opencv.objdetect.CascadeClassifier;
import javax.swing.*;
import java.awt.*;
import java.awt.image.BufferedImage;
public class FaceDetectionImage {
static {
// 加载 OpenCV 的 native 库
// 这行代码会去你在 Project Structure 中配置的 Native Library Location 路径下查找 .dll 文件
System.loadLibrary(Core.NATIVE_LIBRARY_NAME);
}
public static void main(String[] args) {
// 1. 加载分类器
// 请确保这里的路径是正确的,指向你本地的 haarcascade_frontalface_alt.xml 文件
String classifierPath = "D:/dev/opencv/data/haarcascades/haarcascade_frontalface_alt.xml";
CascadeClassifier faceDetector = new CascadeClassifier();
if (!faceDetector.load(classifierPath)) {
System.err.println("Error loading classifier file");
return;
}
// 2. 读取图片
String imagePath = "path/to/your/image.jpg"; // 替换成你的图片路径
Mat image = Imgcodecs.imread(imagePath);
if (image.empty()) {
System.err.println("Error loading image");
return;
}
// 3. 检测人脸
// MatOfRect 用于存储检测到的人脸的矩形区域
MatOfRect faceDetections = new MatOfRect();
// detectMultiScale 方法执行检测
// 参数:图像, 结果, 缩放因子, 邻近数, 标志, 最小尺寸
faceDetector.detectMultiScale(image, faceDetections);
System.out.println(String.format("Detected %s faces", faceDetections.toArray().length));
// 4. 在检测到的人脸周围画矩形
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), // 颜色 (BGR格式, 绿色)
3 // 线条粗细
);
}
// 5. 显示结果
// OpenCV 的 Mat 不能直接显示,需要转换为 Java 的 BufferedImage
HighGui.imshow("Face Detection", matToBufferedImage(image));
HighGui.waitKey(0); // 等待按键
HighGui.destroyAllWindows();
}
// Mat 转 BufferedImage 的辅助方法
public static BufferedImage matToBufferedImage(Mat mat) {
int type = BufferedImage.TYPE_BYTE_GRAY;
if (mat.channels() == 3) {
type = BufferedImage.TYPE_3BYTE_BGR;
}
int bufferSize = mat.channels() * mat.cols() * mat.rows();
byte[] b = new byte[bufferSize];
mat.get(0, 0, b); // get all the pixels
BufferedImage image = new BufferedImage(mat.cols(), mat.rows(), type);
final byte[] targetPixels = ((DataBufferByte) image.getRaster().getDataBuffer()).getData();
System.arraycopy(b, 0, targetPixels, 0, b.length);
return image;
}
}
代码实现:从摄像头实时检测人脸
import org.opencv.core.*;
import org.opencv.videoio.VideoCapture;
import org.opencv.objdetect.CascadeClassifier;
import javax.swing.*;
import java.awt.*;
import java.awt.image.BufferedImage;
public class FaceDetectionWebcam {
static {
System.loadLibrary(Core.NATIVE_LIBRARY_NAME);
}
public static void main(String[] args) {
// 1. 加载分类器
String classifierPath = "D:/dev/opencv/data/haarcascades/haarcascade_frontalface_alt.xml";
CascadeClassifier faceDetector = new CascadeClassifier(classifierPath);
// 2. 打开摄像头
VideoCapture camera = new VideoCapture(0); // 0 表示默认摄像头
if (!camera.isOpened()) {
System.err.println("Cannot open camera");
return;
}
// 3. 创建显示窗口
JFrame frame = new JFrame("Camera Face Detection");
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
JLabel label = new JLabel();
frame.getContentPane().add(label);
frame.pack();
frame.setVisible(true);
Mat frameMat = new Mat();
while (true) {
// 4. 读取摄像头一帧
if (camera.read(frameMat)) {
// 5. 检测人脸
MatOfRect faceDetections = new MatOfRect();
faceDetector.detectMultiScale(frameMat, faceDetections);
// 6. 画框
for (Rect rect : faceDetections.toArray()) {
Imgproc.rectangle(frameMat, new Point(rect.x, rect.y), new Point(rect.x + rect.width, rect.y + rect.height), new Scalar(0, 255, 0), 3);
}
// 7. 显示
Icon icon = new ImageIcon(matToBufferedImage(frameMat));
label.setIcon(icon);
frame.pack();
}
// 按 ESC 键退出
if (HighGui.waitKey(10) == 27) {
break;
}
}
// 8. 释放资源
camera.release();
frame.dispose();
}
// Mat 转 BufferedImage 的辅助方法 (同上)
public static BufferedImage matToBufferedImage(Mat mat) {
// ... (代码同上)
}
}
第二部分:人脸识别
人脸识别的目标是识别出“这是谁”,这需要一个训练过程,将人脸数据转换成特征向量,然后通过比较特征向量来判断身份。

我们将使用 OpenCV 自带的 LBPH (Local Binary Patterns Histograms) 人脸识别器,它相对简单且效果好。
流程:
- 收集数据:为每个要识别的人准备多张不同角度、表情的照片。
- 人脸检测与数据准备:从每张照片中检测人脸,并将其裁剪、缩放到统一大小(100x100)。
- 训练识别器:将所有人的脸数据喂给 LBPH 识别器进行训练。
- 识别:用训练好的识别器来识别新的人脸。
创建训练数据
你需要一个数据集,一个简单的目录结构如下:
dataset/
├── person_01/
│ ├── 1.jpg
│ ├── 2.jpg
│ └── ...
├── person_02/
│ ├── 1.jpg
│ ├── 2.jpg
│ └── ...
└── ...
person_01 是你要识别的人 A,person_02 是人 B,以此类推,每个文件夹里的图片都应包含该人的清晰正面人脸。
代码实现:训练识别器
这个脚本会读取 dataset 目录,为每个人创建一个标签,然后训练识别器,并将训练好的模型保存为 lbph_face_classifier.yml。
import org.opencv.core.*;
import org.opencv.imgcodecs.Imgcodecs;
import org.opencv.imgproc.Imgproc;
import org.opencv.objdetect.CascadeClassifier;
import org.opencv.face.*;
import java.io.File;
import java.io.FilenameFilter;
import java.nio.IntBuffer;
public face.TrainRecognizer {
static {
System.loadLibrary(Core.NATIVE_LIBRARY_NAME);
}
public static void main(String[] args) {
// 1. 加载人脸检测器
CascadeClassifier faceDetector = new CascadeClassifier("D:/dev/opencv/data/haarcascades/haarcascade_frontalface_alt.xml");
// 2. 准备训练数据
File root = new File("dataset");
File[] personDirs = root.listFiles(File::isDirectory);
if (personDirs == null || personDirs.length == 0) {
System.err.println("No person directories found in 'dataset'");
return;
}
// 存储所有人脸图像和对应的标签ID
MatOfInt labels = new MatOfInt();
MatOfFloat images = new MatOfFloat(); // 注意:LBPH可以直接使用Mat,这里为了清晰使用MatOfFloat
// 更正:LBPH应该使用Mat来存储图像数据
Mat trainingImages = new Mat();
Mat labelsMat = new Mat();
int labelId = 0;
for (File personDir : personDirs) {
File[] imageFiles = personDir.listFiles((dir, name) -> name.endsWith(".jpg") || name.endsWith(".png"));
if (imageFiles == null) continue;
System.out.println("Processing person: " + personDir.getName() + " with ID: " + labelId);
for (File imageFile : imageFiles) {
// 读取图片
Mat image = Imgcodecs.imread(imageFile.getAbsolutePath());
if (image.empty()) continue;
// 转换为灰度图
Mat grayImage = new Mat();
Imgproc.cvtColor(image, grayImage, Imgproc.COLOR_BGR2GRAY);
// 检测人脸
MatOfRect faceDetections = new MatOfRect();
faceDetector.detectMultiScale(grayImage, faceDetections);
if (faceDetections.toArray().length > 0) {
// 假设每张图只有一张人脸,取第一个
Rect face = faceDetections.toArray()[0];
Mat faceROI = new Mat(grayImage, face);
// 调整大小到统一尺寸,100x100
Mat resizedFace = new Mat();
Size size = new Size(100, 100);
Imgproc.resize(faceROI, resizedFace, size);
// 将人脸图像添加到训练数据集中
trainingImages.push_back(resizedFace.reshape(1, 1).t());
labelsMat.put(labelId, 0, labelId);
}
}
labelId++;
}
// 转换为正确的数据类型
trainingImages.convertTo(trainingImages, CvType.CV_32F);
labelsMat.convertTo(labelsMat, CvType.CV_32S);
// 3. 创建并训练 LBPH 识别器
FaceRecognizer lbphRecognizer = FaceRecognizer.createLBPHFaceRecognizer();
// 注意:新版本的OpenCV中,FaceRecognizer的API有变化,使用createLBPHFaceRecognizer()
// 并且训练方法也改了
lbphRecognizer.train(trainingImages, labelsMat);
// 4. 保存训练好的模型
lbphRecognizer.save("lbph_face_classifier.yml");
System.out.println("Training complete and model saved as lbph_face_classifier.yml");
}
}
注意:OpenCV Java 的 API 在不同版本间有差异,上面的代码使用了较新的 API (FaceRecognizer.createLBPHFaceRecognizer() 和 train(Mat, Mat)),如果你的版本较旧,可能需要使用 LBPHFaceRecognizer.create() 和 train(MatOfInt, Mat)。
代码实现:使用训练好的模型进行识别
现在我们创建一个新程序,加载 lbph_face_classifier.yml 模型,然后对摄像头捕获到的人脸进行识别。
import org.opencv.core.*;
import org.opencv.videoio.VideoCapture;
import org.opencv.imgcodecs.Imgcodecs;
import org.opencv.imgproc.Imgproc;
import org.opencv.objdetect.CascadeClassifier;
import org.opencv.face.*;
import javax.swing.*;
import java.awt.*;
import java.io.File;
public class FaceRecognizerApp {
static {
System.loadLibrary(Core.NATIVE_LIBRARY_NAME);
}
public static void main(String[] args) {
// 1. 加载人脸检测器和识别器模型
CascadeClassifier faceDetector = new CascadeClassifier("D:/dev/opencv/data/haarcascades/haarcascade_frontalface_alt.xml");
FaceRecognizer recognizer = FaceRecognizer.createLBPHFaceRecognizer();
recognizer.read("lbph_face_classifier.yml");
// 2. 创建标签到姓名的映射
// 你需要根据你训练时的情况手动创建这个映射
// 假设训练时 person_01 的 label 是 0, person_02 的是 1
String[] names = {"Person A", "Person B", "Unknown"}; // Unknown 用于未识别出的人
// 3. 打开摄像头
VideoCapture camera = new VideoCapture(0);
if (!camera.isOpened()) {
System.err.println("Cannot open camera");
return;
}
// 4. 创建显示窗口
JFrame frame = new JFrame("Face Recognition");
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
JLabel label = new JLabel();
frame.getContentPane().add(label);
frame.pack();
frame.setVisible(true);
Mat frameMat = new Mat();
while (true) {
if (camera.read(frameMat)) {
Mat grayFrame = new Mat();
Imgproc.cvtColor(frameMat, grayFrame, Imgproc.COLOR_BGR2GRAY);
MatOfRect faceDetections = new MatOfRect();
faceDetector.detectMultiScale(grayFrame, faceDetections);
for (Rect rect : faceDetections.toArray()) {
// 裁剪人脸
Mat faceROI = new Mat(grayFrame, rect);
Mat resizedFace = new Mat();
Imgproc.resize(faceROI, resizedFace, new Size(100, 100));
// 进行预测
int[] label = new int[1];
double[] confidence = new double[1];
recognizer.predict(resizedFace, label, confidence);
// 获取预测结果
String predictedName = names[label[0]];
System.out.println("Predicted: " + predictedName + ", Confidence: " + confidence[0]);
// 在人脸旁边显示姓名和置信度
Imgproc.putText(
frameMat,
predictedName + " (" + String.format("%.2f", confidence[0]) + ")",
new Point(rect.x, rect.y - 10),
Imgproc.FONT_HERSHEY_SIMPLEX,
0.8,
new Scalar(0, 255, 0),
2
);
Imgproc.rectangle(frameMat, new Point(rect.x, rect.y), new Point(rect.x + rect.width, rect.y + rect.height), new Scalar(0, 255, 0), 3);
}
// 显示结果
Icon icon = new ImageIcon(matToBufferedImage(frameMat));
label.setIcon(icon);
frame.pack();
}
if (HighGui.waitKey(10) == 27) { // ESC to exit
break;
}
}
camera.release();
frame.dispose();
}
// Mat 转 BufferedImage 的辅助方法 (同上)
public static BufferedImage matToBufferedImage(Mat mat) {
// ... (代码同上)
}
}
总结与进阶
- 人脸检测是基础:没有准确的人脸检测,后续的识别无从谈起。
- 数据质量至关重要:人脸识别的准确率高度依赖于训练数据的质量(数量、多样性、清晰度)。
- LBPH 是一个不错的起点:LBPH 对光照变化不敏感,训练速度快,是入门级人脸识别的绝佳选择。
- 进阶模型:LBPH 的准确率不能满足你,可以尝试更先进的模型,如 Eigenfaces 和 Fisherfaces,它们需要更多的训练数据,但效果通常更好,在 OpenCV 中,它们的使用方式与 LBPH 类似,只需在创建识别器时换掉即可(
FaceRecognizer.createEigenFaceRecognizer())。 - 深度学习:对于更高精度的需求,应该使用基于深度学习的模型,如 OpenCV 的 DNN 模块配合
Caffe或TensorFlow训练好的模型(如Facenet),这会更复杂,但效果是目前最好的。
希望这个详细的教程能帮助你开始 OpenCV Java 人脸识别的旅程!
