概念区分:人脸检测 vs. 人脸识别
在学习之前,必须先分清这两个概念:

- 人脸检测:在一张图片或视频帧中,找到人脸的位置,并用方框把它标记出来,它只回答“哪里有人脸?”这个问题。
- 人脸识别:在已经检测到人脸的基础上,判断这张脸是谁,它回答“这是谁?”这个问题。
人脸识别通常包括以下步骤:
- 人脸检测:从图像中裁剪出人脸区域。
- 特征提取:将裁剪出的人脸图像转换成一个独特的数学向量(也称为“嵌入”或 Embedding),这个向量代表了人脸的唯一特征。
- 比对:将新的人脸特征向量与数据库中已知人脸的特征向量进行比较,找出最相似的一个。
第一步:环境搭建
安装 Java
确保你已经安装了 JDK 8 或更高版本,并配置好了 JAVA_HOME 环境变量。
下载 OpenCV
你需要下载 OpenCV 的 Java 包,注意,你需要的是包含 Java 库的版本。
- 访问 OpenCV 官方下载页面:https://opencv.org/releases/
- 下载最新的版本(
opencv-4.x.x)。 - 解压下载的 ZIP 文件,在解压后的目录中,找到
build/java文件夹。opencv-4.x.x/build/java/opencv-490.jar:这是 Java 的 JAR 包。opencv-4.x.x/build/java/x64/opencv_java490.dll(Windows) 或libopencv_java490.so(Linux):这是本地库文件。
配置你的 Java 项目(以 IntelliJ IDEA 为例)
- 创建新项目:创建一个新的 Java Maven 或 Gradle 项目。
- 添加 JAR 包:
- 手动添加:将
opencv-490.jar复制到你的lib目录下,然后在项目设置中将其添加为库。 - Maven 方式(推荐):在你的
pom.xml文件中添加以下依赖,Maven 会自动帮你下载和管理依赖。<dependency> <groupId>org.openpnp</groupId> <artifactId>opencv</artifactId> <version>4.9.0-0</version> <!-- 可以使用最新版本 --> </dependency>
- 手动添加:将
- 加载本地库:这是最关键的一步,Java 代码需要加载 OpenCV 的本地库(
.dll或.so文件)才能工作。- 方法一(推荐):将
opencv_java490.dll(Windows) 或libopencv_java490.so(Linux) 文件复制到你的项目输出目录(通常是target/classes或out/production/你的项目名)。 - 方法二(代码中指定路径):在代码中明确指定库文件的路径。
- 方法一(推荐):将
第二步:人脸检测
人脸识别的第一步是找到人脸,OpenCV 提供了基于 Haar 级联分类器和基于深度学习(如 DNN 模块)的两种方法,我们这里使用更常用和效果更好的 DNN 模块。

下载预训练模型
你需要一个预训练好的模型来检测人脸,OpenCV 官方提供了一个基于 Caffe 的模型:
- 模型文件:
deploy.prototxt - 权重文件:
res10_300x300_ssd_iter_140000.caffemodel
这两个文件可以在 OpenCV 的 GitHub 仓库中找到:https://github.com/opencv/opencv_3rdparty/tree/dnn_samples_face_detector_20250830
下载这两个文件,并放到你的项目资源文件夹(src/main/resources)中。
人脸检测代码示例
import org.opencv.core.*;
import org.opencv.dnn.Dnn;
import org.opencv.imgcodecs.Imgcodecs;
import org.opencv.imgproc.Imgproc;
import java.io.File;
public class FaceDetection {
static {
// 加载 OpenCV 的本地库
// dll/so 文件在项目输出目录,可以直接用 System.loadLibrary
// 否则,需要指定绝对路径
// System.loadLibrary(Core.NATIVE_LIBRARY_NAME);
String opencvPath = "D:/path/to/your/opencv/build/java/x64/"; // 修改为你的 OpenCV 路径
System.load(opencvPath + "opencv_java490.dll");
}
public static void main(String[] args) {
// 1. 加载预训练的 DNN 模型
String modelWeights = "src/main/resources/res10_300x300_ssd_iter_140000.caffemodel";
String modelConfig = "src/main/resources/deploy.prototxt";
Net net = Dnn.readNetFromCaffe(modelConfig, modelWeights);
// 2. 读取图片
String imagePath = "path/to/your/image.jpg"; // 替换为你的图片路径
Mat image = Imgcodecs.imread(imagePath);
if (image.empty()) {
System.out.println("无法加载图片: " + imagePath);
return;
}
// 3. 预处理图片
// DNN 模型通常需要固定大小的输入
Mat blob = Dnn.blobFromImage(image, 1.0, new Size(300, 300), new Scalar(104, 177, 123), false, false);
net.setInput(blob);
// 4. 进行人脸检测
Mat detections = net.forward();
// 5. 解析检测结果并绘制
// detections 矩阵的维度是 [1, N, 7],N 是检测到的对象数量
// 每个对象包含 [id, confidence, left, top, right, bottom, ...]
for (int i = 0; i < detections.rows(); i++) {
Mat row = detections.row(i);
double confidence = row.get(0, 2)[0]; // 置信度
// 设置一个置信度阈值,过滤掉低置信度的检测结果
if (confidence > 0.7) {
int x = (int) (row.get(0, 3)[0] * image.cols());
int y = (int) (row.get(0, 4)[0] * image.rows());
int width = (int) ((row.get(0, 5)[0] - row.get(0, 3)[0]) * image.cols());
int height = (int) ((row.get(0, 6)[0] - row.get(0, 4)[0]) * image.rows());
// 绘制矩形框
Imgproc.rectangle(image, new Point(x, y), new Point(x + width, y + height), new Scalar(0, 255, 0), 2);
// 显示置信度
String label = String.format("Confidence: %.2f", confidence);
Imgproc.putText(image, label, new Point(x, y - 10), Imgproc.FONT_HERSHEY_SIMPLEX, 0.5, new Scalar(0, 255, 0), 1);
}
}
// 6. 保存结果图片
Imgcodecs.imwrite "detected_faces.jpg", image);
System.out.println("人脸检测完成,结果已保存为 detected_faces.jpg");
}
}
运行这段代码后,你会在项目目录下得到一张标记了人脸方框的图片。

第三步:人脸识别
人脸识别的核心是提取特征向量,我们将使用 OpenCV 的 FaceRecognizer API,它支持几种经典的算法(如 Eigenfaces, Fisherfaces, LBPH),这里我们选择 LBPH (Local Binary Patterns Histograms),因为它对光照变化不敏感,且易于使用。
准备数据集
为了进行识别,你需要一个包含多个人脸的数据库,数据集的组织结构如下:
dataset/
├── person1/
│ ├── 1.jpg
│ ├── 2.jpg
│ └── ...
├── person2/
│ ├── 1.jpg
│ ├── 2.jpg
│ └── ...
└── ...
- 每个文件夹代表一个不同的人。
- 文件夹名就是人的标签("person1", "person2")。
- 每个文件夹里存放这个人的多张不同角度、不同光照下的照片。
训练模型并识别
我们将编写一个程序,完成以下任务:
- 从
dataset目录加载所有的人脸图片和对应的标签。 - 使用这些数据训练一个 LBPH 人脸识别器。
- 加载一张待识别的图片。
- 在待识别图片中检测人脸。
- 将检测到的人脸输入到训练好的识别器中,得到识别结果。
import org.opencv.core.*;
import org.opencv.dnn.Dnn;
import org.opencv.face.Face;
import org.opencv.face.FaceRecognizer;
import org.opencv.face LBPHFaceRecognizer;
import org.opencv.imgcodecs.Imgcodecs;
import org.opencv.imgproc.Imgproc;
import org.opencv.objdetect.CascadeClassifier;
import java.io.File;
import java.io.FilenameFilter;
import java.nio.IntBuffer;
import java.util.ArrayList;
import java.util.List;
public class FaceRecognition {
static {
// 加载 OpenCV 的本地库
String opencvPath = "D:/path/to/your/opencv/build/java/x64/"; // 修改为你的 OpenCV 路径
System.load(opencvPath + "opencv_java490.dll");
}
public static void main(String[] args) {
// 1. 准备训练数据
String trainingDir = "path/to/your/dataset"; // 你的数据集路径
File root = new File(trainingDir);
// 用于存储人脸数据和标签
List<Mat> images = new ArrayList<>();
List<Integer> labels = new ArrayList<>();
Integer label = 0;
// Haar 级联分类器用于人脸检测(也可以用上面的 DNN 模型)
String haarCascadePath = "D:/path/to/opencv/data/haarcascades/haarcascade_frontalface_alt.xml"; // OpenCV 安装路径下的分类器
CascadeClassifier faceDetector = new CascadeClassifier(haarCascadePath);
if (faceDetector.empty()) {
System.out.println("无法加载 Haar 级联分类器");
return;
}
// 遍历数据集文件夹
File[] classDirs = root.listFiles(File::isDirectory);
if (classDirs == null) return;
for (File classDir : classDirs) {
File[] imageFiles = classDir.listFiles((dir, name) -> name.endsWith(".jpg") || name.endsWith(".png"));
if (imageFiles == null || imageFiles.length == 0) continue;
for (File imageFile : imageFiles) {
Mat image = Imgcodecs.imread(imageFile.getAbsolutePath(), Imgcodecs.IMREAD_GRAYSCALE); // 转为灰度图
if (image.empty()) continue;
// 检测人脸
MatOfRect faceDetections = new MatOfRect();
faceDetector.detectMultiScale(image, faceDetections);
// 如果检测到人脸,则添加到训练数据中
if (!faceDetections.empty()) {
// 通常取检测到的人脸中最大的一个
Rect largestFace = findLargestFace(faceDetections.toArray());
Mat faceROI = image.submat(largestFace);
images.add(faceROI);
labels.add(label);
}
}
label++; // 每个文件夹处理完后,标签加1
}
if (images.isEmpty()) {
System.out.println("未找到任何可用于训练的人脸图像");
return;
}
// 2. 训练 LBPH 模型
FaceRecognizer recognizer = LBPHFaceRecognizer.create();
Mat labelsMat = new Mat(labels.size(), 1, CvType.CV_32SC1);
for (int i = 0; i < labels.size(); i++) {
labelsMat.put(i, 0, labels.get(i));
}
recognizer.train(new MatList(images), labelsMat);
System.out.println("模型训练完成!");
// 3. 识别新图片中的人脸
String testImagePath = "path/to/your/test_image.jpg"; // 你的测试图片路径
Mat testImage = Imgcodecs.imread(testImagePath);
Mat testImageGray = new Mat();
Imgproc.cvtColor(testImage, testImageGray, Imgproc.COLOR_BGR2GRAY);
MatOfRect testFaceDetections = new MatOfRect();
faceDetector.detectMultiScale(testImageGray, testFaceDetections);
for (Rect rect : testFaceDetections.toArray()) {
Mat testFaceROI = testImageGray.submat(rect);
// 进行预测
IntBuffer labelBuf = IntBuffer.allocate(1);
DoubleBuffer confidenceBuf = DoubleBuffer.allocate(1);
recognizer.predict(testFaceROI, labelBuf, confidenceBuf);
int predictedLabel = labelBuf.get();
double confidence = confidenceBuf.get();
// 设置一个置信度阈值,低于此阈值则认为是未知人脸
if (confidence < 70.0) { // 这个阈值需要根据你的模型和数据集调整
String predictedName = classDirs[predictedLabel].getName();
Imgproc.rectangle(testImage, new Point(rect.x, rect.y), new Point(rect.x + rect.width, rect.y + rect.height), new Scalar(0, 255, 0), 2);
Imgproc.putText(testImage, predictedName, new Point(rect.x, rect.y - 10), Imgproc.FONT_HERSHEY_SIMPLEX, 1, new Scalar(0, 255, 0), 2);
} else {
Imgproc.rectangle(testImage, new Point(rect.x, rect.y), new Point(rect.x + rect.width, rect.y + rect.height), new Scalar(0, 0, 255), 2);
Imgproc.putText(testImage, "Unknown", new Point(rect.x, rect.y - 10), Imgproc.FONT_HERSHEY_SIMPLEX, 1, new Scalar(0, 0, 255), 2);
}
}
// 4. 保存识别结果
Imgcodecs.imwrite("recognized_faces.jpg", testImage);
System.out.println("人脸识别完成,结果已保存为 recognized_faces.jpg");
}
// 辅助函数:找到最大的人脸
private static Rect findLargestFace(Rect[] faces) {
if (faces == null || faces.length == 0) {
return new Rect();
}
Rect largestFace = faces[0];
int maxArea = largestFace.width * largestFace.height;
for (Rect face : faces) {
int area = face.width * face.height;
if (area > maxArea) {
maxArea = area;
largestFace = face;
}
}
return largestFace;
}
}
总结与进阶
-
核心流程回顾:
- 环境配置:下载 OpenCV,配置 JAR 和本地库。
- 人脸检测:使用 DNN 或 Haar 级联分类器定位人脸。
- 数据准备:为识别任务整理好人脸数据集(每人一个文件夹)。
- 模型训练:使用
FaceRecognizer(如 LBPH) 训练一个识别模型。 - 人脸识别:用训练好的模型对检测到的人脸进行预测,得到身份和置信度。
-
进阶方向:
- 更先进的特征提取:LBPH 是传统方法,效果有限,现代人脸识别通常使用深度学习模型提取特征,如 FaceNet、DeepFace 等,这些模型能生成更具区分度的特征向量,识别率更高。
实现方式:可以使用 OpenCV 的 DNN 模块加载这些预训练模型(如 TensorFlow 或 Caffe 格式的模型),然后提取人脸图像的特征向量,再使用余弦相似度或欧氏距离来比对。
- 实时视频流处理:将上述逻辑应用到摄像头视频流中,使用
VideoCapture类捕获每一帧,然后进行检测和识别。 - 人脸数据库:在实际应用中,你可能需要一个数据库(如 MySQL, SQLite)来存储用户信息和他们的人脸特征向量,而不是像示例中那样依赖文件系统。
- 多线程处理:为了提高视频处理的性能,可以将人脸检测和识别放在不同的线程中。
- 更先进的特征提取:LBPH 是传统方法,效果有限,现代人脸识别通常使用深度学习模型提取特征,如 FaceNet、DeepFace 等,这些模型能生成更具区分度的特征向量,识别率更高。
希望这份详细的指南能帮助你成功地在 Java 中使用 OpenCV 进行人脸识别!
