Java借助OpenCV实现人脸识别登录完整示例
- OpenCV
-
- 效果预览
- 概述
- 下载与安装
- 目录说明
- OpenCV的基本使用
-
- 项目集成
- 图片人脸检测
- 人脸对比相似度
- 识别视频中的人脸
- 摄像头识别人脸
- 自定义窗口
- 摄像头拍摄视频写入本地
- Spring Boot集成OpenCV
-
- 添加依赖
- 项目集成OpenCV
- 请求接口
- 配置application.yml
- OpenCvUtil
- 自定义窗口
- 创建页面
- 启动类配置
- 常见异常记录
-
- 异常1
- 异常2
- 异常3
- 异常4
- 异常5
- 异常6
OpenCV
效果预览
概述
OpenCV(开源计算机视觉库)是在BSD(开源协议)许可下发布的。它是一个高度优化的库,专注于实时应用程序。它具有C ++,Python和Java接口,支持Windows,Linux,Mac OS,iOS和Android。
下载与安装
下载地址:https://opencv.org/releases/
下载到本地后,双击进行安装即可
目录说明
安装目录如下
build :基于window构建
sources:开源,提供源码
build目录说明
这里是Java开发关注java目录即可
x64与x86代表给不同的系统使用
opencv-460.jar给java操作openvc的程序包
由于是64位系统,所以关注x64目录
DLL(Dynamic Link Library)文件为动态链接库文件,又称“应用程序拓展”,是软件文件类型。DLL文件,放置于系统中。当执行某一个程序时,相应的DLL文件就会被调用
OpenCV的基本使用
官网文档地址:https://docs.opencv.org/4.6.0/df/d65/tutorial_table_of_content_introduction.html
中文文档:http://wiki.opencv.org.cn/index.php
教程参考:https://www.w3cschool.cn/opencv/
教程参考:https://www.yiibai.com/opencv/opencv_adding_text.html
项目集成
这里使用IDEA进行开发,导入opencv-460.jar库
使用快捷键 Ctrl+Shift+Alt+S打开
选择库项,导入Java库。
除了上述方式,还可以将opencv-460.jar
安装到本地仓库或私有仓库,然后在pom.xml中引入依赖。
图片人脸检测
public static void main(String[] args) {
imageFaceDetection();
}
/**
* 图片人脸检测
*/
public static void imageFaceDetection() {
// 加载OpenCV本地库
System.loadLibrary(Core.NATIVE_LIBRARY_NAME);
// 从配置文件lbpcascade_frontalface.xml中创建一个人脸识别器,文件位于opencv安装目录中
CascadeClassifier faceDetector = new CascadeClassifier("D:\\Development\\opencv\\sources\\data\\haarcascades\\haarcascade_frontalface_alt.xml");
// 读取测试图片
String imgPath = "D:\\user\\test.png";
Mat image = Imgcodecs.imread(imgPath);
if (image.empty()) {
throw new RuntimeException("图片内存为空");
}
// 检测脸部
MatOfRect face = new MatOfRect();
// 检测图像中的人脸
faceDetector.detectMultiScale(image, face);
// 匹配Rect矩阵
Rect[] rects = face.toArray();
System.out.println("识别人脸个数: " + rects.length);
// 识别图片中的所以人脸并分别保存
int i = 1;
for (Rect rect : face.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), 3);
// 进行图片裁剪
imageCut(imgPath, "D:\\user\\" + i + ".jpg", rect.x, rect.y, rect.width, rect.height);
i++;
}
// 图片中人脸画框保存到本地
Imgcodecs.imwrite("D:\\user\\test1.png", image);
// 展示图片
HighGui.imshow("人脸识别", image);
HighGui.waitKey(0);
}
/**
* 裁剪人脸
*
* @param readPath 读取文件路径
* @param outPath 写出文件路径
* @param x 坐标X
* @param y 坐标Y
* @param width 截图宽度
* @param height 截图长度
*/
public static void imageCut(String readPath, String outPath, int x, int y, int width, int height) {
// 原始图像
Mat image = Imgcodecs.imread(readPath);
// 截取的区域
Rect rect = new Rect(x, y, width, height);
// Mat sub = new Mat(image,rect);
Mat sub = image.submat(rect);
Mat mat = new Mat();
Size size = new Size(width, height);
// 人脸进行截图并保存
Imgproc.resize(sub, mat, size);
Imgcodecs.imwrite(outPath, mat);
}
人脸对比相似度
对比1.jpg与1-1.jpg
对比1.jpg与3.jpg
// 初始化人脸探测器
static CascadeClassifier faceDetector;
static {
// 加载OpenCV本地库
System.loadLibrary(Core.NATIVE_LIBRARY_NAME);
// 从配置文件lbpcascade_frontalface.xml中创建一个人脸识别器,文件位于opencv安装目录中
faceDetector = new CascadeClassifier("D:\\Development\\opencv\\sources\\data\\haarcascades\\haarcascade_frontalface_alt.xml");
}
public static void main(String[] args) {
double comparison = faceRecognitionComparison("D:\\user\\1.jpg", "D:\\user\\1-1.jpg");
System.out.println("对比结果:" + comparison);
if (comparison > 0.85) {
System.out.println("人脸匹配成功");
} else {
System.out.println("人脸不匹配识别");
}
double comparison2 = faceRecognitionComparison("D:\\user\\1.jpg", "D:\\user\\3.jpg");
System.out.println("对比结果:" + comparison2);
if (comparison2 > 0.85) {
System.out.println("人脸匹配成功");
} else {
System.out.println("人脸不匹配识别");
}
// 终止当前运行的 Java 虚拟机。
System.exit(0);
}
/**
* 人脸识别比对
*/
public static double faceRecognitionComparison(String image1, String image2) {
Mat mat1 = conv_Mat(image1);
Mat mat2 = conv_Mat(image2);
Mat mat3 = new Mat();
Mat mat4 = new Mat();
// 颜色范围
MatOfFloat ranges = new MatOfFloat(0f, 256f);
// 直方图大小, 越大匹配越精确 (越慢)
MatOfInt histSize = new MatOfInt(1000);
Imgproc.calcHist(Arrays.asList(mat1), new MatOfInt(0), new Mat(), mat3, histSize, ranges);
Imgproc.calcHist(Arrays.asList(mat2), new MatOfInt(0), new Mat(), mat4, histSize, ranges);
// 比较两个密集或两个稀疏直方图
return Imgproc.compareHist(mat3, mat4, Imgproc.CV_COMP_CORREL);
}
/**
* 灰度化人脸
*/
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;
}
return null;
}
对比结果如下
对比结果:1.0
人脸匹配成功
对比结果:0.2501351968792374
人脸不匹配识别
识别视频中的人脸
// 初始化人脸探测器
static CascadeClassifier faceDetector;
static {
// 加载OpenCV本地库
System.loadLibrary(Core.NATIVE_LIBRARY_NAME);
// 从配置文件lbpcascade_frontalface.xml中创建一个人脸识别器,文件位于opencv安装目录中
faceDetector = new CascadeClassifier("D:\\Development\\opencv\\sources\\data\\haarcascades\\haarcascade_frontalface_alt.xml");
}
public static void main(String[] args) {
videoFaceRecognition();
// 终止当前运行的 Java 虚拟机。
System.exit(0);
}
/**
* 从视频中识别人脸
*/
public static void videoFaceRecognition() {
// 读取视频文件
VideoCapture capture = new VideoCapture();
capture.open("D:\\user\\test.mp4");
if (!capture.isOpened()) {
throw new RuntimeException("读取视频文件失败");
}
Mat video = new Mat();
int index = 0;
while (capture.isOpened()) {
// 抓取、解码并返回下一个视频帧写入Mat对象中
capture.read(video);
// 显示从视频中识别的人脸图像
HighGui.imshow("视频识别人脸", getFace(video));
// 获取键盘输入
index = HighGui.waitKey(100);
// 如果是 Esc 则退出
if (index == 27) {
capture.release();
return;
}
}
}
/**
* 从视频帧中识别人脸
*
* @param image 待处理Mat图片,即视频中的某一帧
* @return 处理后的图片
*/
public static Mat getFace(Mat image) {
MatOfRect face = new MatOfRect();
// 检测输入图像中不同大小的对象。检测到的对象作为矩形列表返回。
faceDetector.detectMultiScale(image, face);
Rect[] rects = face.toArray();
System.out.println("识别人脸个数: " + rects.length);
if (rects.length > 0 && Math.random() * 10 > 8) {
Imgcodecs.imwrite("D:\\user\\" + UUID.randomUUID() + ".png", image);
}
if (rects != null && rects.length >= 1) {
// 为每张识别到的人脸画一个圈
for (int i = 0; i < rects.length; i++) {
/**
* 绘制一个简单的、粗的或填充的直角矩形
*
* img 图像
* pt1 - 矩形的顶点
* pt2 - 与 pt1 相对的矩形的顶点
* color – 矩形颜色或亮度(灰度图像)意味着该函数必须绘制一个填充的矩形。
*/
Imgproc.rectangle(image, new Point(rects[i].x, rects[i].y), new Point(rects[i].x + rects[i].width, rects[i].y + rects[i].height), new Scalar(0, 255, 0));
/**
* 绘制一个文本字符串,放在识别人脸框上
*
* img -- 图像
* text -- 要绘制的文本字符串
* org – 图像中文本字符串的左下角
* fontFace – 字体类型,请参阅#HersheyFonts
* fontScale – 字体比例因子乘以特定字体的基本大小
* color - 文本颜色
* thickness ——用于绘制文本的线条粗细
* lineType – 线型
* bottomLeftOrigin – 当为 true 时,图像数据原点位于左下角。否则,它位于左上角
*/
Imgproc.putText(image, "test", new Point(rects[i].x, rects[i].y), Imgproc.FONT_HERSHEY_SCRIPT_SIMPLEX, 1.0, new Scalar(0, 255, 0), 1, Imgproc.LINE_AA, false);
}
}
return image;
}
摄像头识别人脸
// 初始化人脸探测器
static CascadeClassifier faceDetector;
static {
// 加载OpenCV本地库
System.loadLibrary(Core.NATIVE_LIBRARY_NAME);
// 从配置文件lbpcascade_frontalface.xml中创建一个人脸识别器,文件位于opencv安装目录中
faceDetector = new CascadeClassifier("D:\\Development\\opencv\\sources\\data\\haarcascades\\haarcascade_frontalface_alt.xml");
}
public static void main(String[] args) throws Exception {
cameraFaceRecognition();
// 终止当前运行的 Java 虚拟机。
System.exit(0);
}
/**
* 摄像头实时人脸识别
*
* @throws Exception
*/
public static void cameraFaceRecognition() throws Exception {
// 打开摄像头获取视频流,0 打开默认摄像头
VideoCapture videoCapture = new VideoCapture(0);
// 检查是否支持摄像头 true:代表摄像头可以打开 false:不可以打开
System.out.println(videoCapture.isOpened());
// 获取摄像头高度
int height = (int) videoCapture.get(Videoio.CAP_PROP_FRAME_HEIGHT);
// 获取摄像头宽度
int width = (int) videoCapture.get(Videoio.CAP_PROP_FRAME_WIDTH);
if (height == 0 || width == 0) {
throw new Exception("摄像头不存在");
}
Mat video = new Mat();
int index = 0;
if (videoCapture.isOpened()) {
while (true) {
videoCapture.read(video);
HighGui.imshow("实时人脸识别", getFace(video));
// 键盘输入
index = HighGui.waitKey(50);
// 是Esc则退出,比强制退出好
if (index == 27) {
// 写入人脸
Imgcodecs.imwrite("D:\\user\\" + "face.png", video);
videoCapture.release();
return;
}
}
}
}
/**
* 从视频帧中识别人脸
*
* @param image 待处理Mat图片,即视频中的某一帧
* @return 处理后的图片
*/
public static Mat getFace(Mat image) {
MatOfRect face = new MatOfRect();
// 检测输入图像中不同大小的对象。检测到的对象作为矩形列表返回。
faceDetector.detectMultiScale(image, face);
Rect[] rects = face.toArray();
System.out.println("识别人脸个数: " + rects.length);
if (rects != null && rects.length >= 1) {
// 为每张识别到的人脸画一个圈
for (int i = 0; i < rects.length; i++) {
// 绘制一个简单的、粗的或填充的直角矩形
Imgproc.rectangle(image, new Point(rects[i].x, rects[i].y), new Point(rects[i].x + rects[i].width, rects[i].y + rects[i].height), new Scalar(0, 255, 0));
// 绘制一个文本字符串,放在识别人脸框上
Imgproc.putText(image, "test", new Point(rects[i].x, rects[i].y), Imgproc.FONT_HERSHEY_SCRIPT_SIMPLEX, 1.0, new Scalar(0, 255, 0), 1, Imgproc.LINE_AA, false);
}
}
return image;
}
自定义窗口
OpenCV带的HighGUI图形用户界面感觉可配置参数太少,因此可自定义窗口用于代替。
import org.opencv.core.Point;
import org.opencv.core.*;
import org.opencv.imgcodecs.Imgcodecs;
import org.opencv.imgproc.Imgproc;
import org.opencv.objdetect.CascadeClassifier;
import org.opencv.videoio.VideoCapture;
import org.opencv.videoio.Videoio;
import javax.swing.*;
import java.awt.*;
import java.awt.image.BufferedImage;
public class MyJPanel extends JPanel {
private BufferedImage mImg;
// 初始化人脸探测器
static CascadeClassifier faceDetector;
static VideoCapture videoCapture;
static JFrame frame;
static {
// 加载OpenCV本地库
System.loadLibrary(Core.NATIVE_LIBRARY_NAME);
// 从配置文件lbpcascade_frontalface.xml中创建一个人脸识别器,文件位于opencv安装目录中
faceDetector = new CascadeClassifier("D:\\Development\\opencv\\sources\\data\\haarcascades\\haarcascade_frontalface_alt.xml");
}
public void paintComponent(Graphics g) {
if (mImg != null) {
g.drawImage(mImg, 0, 0, mImg.getWidth(), mImg.getHeight(), this);
}
}
/**
* 摄像头识别人脸
*/
public static void cameraFaceRecognition() throws Exception {
try {
// 打开摄像头获取视频流,0 打开默认摄像头
videoCapture = new VideoCapture(0);
// 检查是否支持摄像头 true:代表摄像头可以打开 false:不可以打开
System.out.println(videoCapture.isOpened());
// 获取摄像头高度
int height = (int) videoCapture.get(Videoio.CAP_PROP_FRAME_HEIGHT);
// 获取摄像头宽度
int width = (int) videoCapture.get(Videoio.CAP_PROP_FRAME_WIDTH);
if (height == 0 || width == 0) {
throw new Exception("摄像头不存在");
}
//使用Swing生成GUI
frame = new JFrame("人脸识别");
frame.setDefaultCloseOperation(WindowConstants.DISPOSE_ON_CLOSE);
MyJPanel panel = new MyJPanel();
//设置中心显示
frame.setContentPane(panel);
frame.setVisible(true);
frame.setSize(width + frame.getInsets().left + frame.getInsets().right, height + frame.getInsets().top + frame.getInsets().bottom);
frame.setLocationRelativeTo(null);
// 创建矩阵
Mat capImg = new Mat();
// 创建一个临时矩阵
Mat temp = new Mat();
while (frame.isShowing()) {
//从摄像头读取一帧数据,保存到capImg矩阵中。
videoCapture.read(capImg);
//转换为彩色图
Imgproc.cvtColor(capImg, temp, Imgproc.COLOR_RGBA2BGRA);
// 人脸识别
capImg = getFace(capImg);
// 本地图片保存
Imgcodecs.imwrite("D:\\user\\1.jpg", capImg);
//转为图像显示
panel.mImg = panel.matToImage(capImg);
// 重绘此组件
panel.repaint();
}
} finally {
// 关闭摄像头
videoCapture.release();
frame.dispose();
}
}
/**
* 从视频帧中识别人脸
*
* @param image 待处理Mat图片,即视频中的某一帧
* @return 处理后的图片
*/
public static Mat getFace(Mat image) {
MatOfRect face = new MatOfRect();
// 检测输入图像中不同大小的对象。检测到的对象作为矩形列表返回。
faceDetector.detectMultiScale(image, face);
Rect[] rects = face.toArray();
System.out.println("识别人脸个数: " + rects.length);
if (rects != null && rects.length >= 1) {
// 为每张识别到的人脸画一个圈
for (int i = 0; i < rects.length; i++) {
// 绘制一个简单的、粗的或填充的直角矩形
Imgproc.rectangle(image, new org.opencv.core.Point(rects[i].x, rects[i].y), new org.opencv.core.Point(rects[i].x + rects[i].width, rects[i].y + rects[i].height), new Scalar(0, 255, 0));
// 绘制一个文本字符串,放在识别人脸框上
Imgproc.putText(image, "test", new Point(rects[i].x, rects[i].y), Imgproc.FONT_HERSHEY_SCRIPT_SIMPLEX, 1.0, new Scalar(0, 255, 0), 1, Imgproc.LINE_AA, false);
}
}
return image;
}
/**
* 转换图像
*/
private BufferedImage matToImage(Mat mat) {
int dataSize = mat.cols() * mat.rows() * (int) mat.elemSize();
byte[] data = new byte[dataSize];
mat.get(0, 0, data);
int type = mat.channels() == 1 ? BufferedImage.TYPE_BYTE_GRAY : BufferedImage.TYPE_3BYTE_BGR;
if (type == BufferedImage.TYPE_3BYTE_BGR) {
for (int i = 0; i < dataSize; i += 3) {
byte blue = data[i + 0];
data[i + 0] = data[i + 2];
data[i + 2] = blue;
}
}
BufferedImage image = new BufferedImage(mat.cols(), mat.rows(), type);
image.getRaster().setDataElements(0, 0, mat.cols(), mat.rows(), data);
return image;
}
}
摄像头拍摄视频写入本地
public static void main(String[] args) throws Exception {
MyJPanel.cameraFaceRecognition();
// 终止当前运行的 Java 虚拟机。
System.exit(0);
}
// 初始化人脸探测器
static CascadeClassifier faceDetector;
static BufferedImage mImg;
static {
// 加载OpenCV本地库
System.loadLibrary(Core.NATIVE_LIBRARY_NAME);
// 从配置文件lbpcascade_frontalface.xml中创建一个人脸识别器,文件位于opencv安装目录中
faceDetector = new CascadeClassifier("D:\\Development\\opencv\\sources\\data\\haarcascades\\haarcascade_frontalface_alt.xml");
}
public static void main(String[] args) throws Exception {
writeVideo();
// 终止当前运行的 Java 虚拟机。
System.exit(0);
}
/**
* 摄像头拍摄视频写入本地
*/
public static void writeVideo() throws Exception {
// 打开摄像头获取视频流,0 打开默认摄像头
VideoCapture videoCapture = new VideoCapture(0);
// 检查是否支持摄像头 true:代表摄像头可以打开 false:不可以打开
System.out.println(videoCapture.isOpened());
// 获取摄像头高度
int height = (int) videoCapture.get(Videoio.CAP_PROP_FRAME_HEIGHT);
// 获取摄像头宽度
int width = (int) videoCapture.get(Videoio.CAP_PROP_FRAME_WIDTH);
if (height == 0 || width == 0) {
throw new Exception("摄像头不存在");
}
Mat video = new Mat();
int index = 0;
Size size = new Size(videoCapture.get(Videoio.CAP_PROP_FRAME_WIDTH), videoCapture.get(Videoio.CAP_PROP_FRAME_HEIGHT));
VideoWriter writer = new VideoWriter("D:\\user\\1.mp4", VideoWriter.fourcc('D', 'I', 'V', 'X'), 30.0, size, true);
while (videoCapture.isOpened()) {
//从摄像头读取一帧数据,保存到capImg矩阵中。
videoCapture.read(video);
writer.write(video);
HighGui.imshow("视频人脸识别", video);
// 获取键盘输入
index = HighGui.waitKey(100);
// 是Esc则退出,若强制退出将导致录制视频无法播放
if (index == 27) {
videoCapture.release();
writer.release();
return;
}
}
}
Spring Boot集成OpenCV
添加依赖
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-devtools</artifactId>
<scope>runtime</scope>
<optional>true</optional>
</dependency>
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<optional>true</optional>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
</dependency>
<dependency>
<groupId>com.alibaba</groupId>
<artifactId>fastjson</artifactId>
<version>1.2.76</version>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-thymeleaf</artifactId>
</dependency>
</dependencies>
项目集成OpenCV
项目集成OpenCV参考上述OpenCV的基本使用
中的项目集成
请求接口
@Controller
@RequestMapping("/user")
public class UserFaceLogin {
@Autowired
private MyJPanel myJPanel;
@RequestMapping("/login")
public String login() throws Exception {
// 调用摄像头显示
boolean result = myJPanel.cameraFaceRecognition();
if (result) {
return "/success.html";
} else {
return "/error.html";
}
}
}
配置application.yml
开发环境与生产环境需区分
opencv:
lib:
linuxxmlpath: /usr/local//opencv/haarcascades/haarcascade_frontalface_alt.xml
windowxmlpath: D:\Development\opencv\sources\data\haarcascades\haarcascade_frontalface_alt.xml
指定虚拟机参数
-Djava.library.path=D:\Development\opencv\build\java\x64
或
-Djava.library.path=D:\Development\opencv\build\java\x64;D:\Development\opencv\build\x64\vc15\bin
OpenCvUtil
完成初始化工作以及添加人脸匹配功能,更多功能扩展此工具类即可。
@Component
public class OpenCvUtil implements CommandLineRunner {
// 初始化人脸探测器
static CascadeClassifier faceDetector;
@Value("${opencv.lib.linuxxmlpath}")
private String linuxXmlPath;
@Value("${opencv.lib.windowxmlpath}")
private String windowXmlPath;
/**
* 判断是否是Windows系统
*/
private static final boolean IS_WINDOWS = System.getProperty("os.name").toLowerCase().contains("win");
@Override
public void run(String... args) {
System.loadLibrary(Core.NATIVE_LIBRARY_NAME);
String path = "";
if (IS_WINDOWS) {
path = windowXmlPath;
} else {
path = linuxXmlPath;
}
/**
* 初始化人脸探测器
*/
faceDetector = new CascadeClassifier(path);
}
public static int match(String loginImagePath, String comparedImagePath) {
Mat mat1 = conv_Mat(loginImagePath);
if (mat1 == null) {
return 0;
}
Mat mat2 = conv_Mat(comparedImagePath);
Mat mat3 = new Mat();
Mat mat4 = new Mat();
// 颜色范围
MatOfFloat ranges = new MatOfFloat(0f, 256f);
// 直方图大小, 越大匹配越精确 (越慢)
MatOfInt histSize = new MatOfInt(1000);
Imgproc.calcHist(Arrays.asList(mat1), new MatOfInt(0), new Mat(), mat3, histSize, ranges);
Imgproc.calcHist(Arrays.asList(mat2), new MatOfInt(0), new Mat(), mat4, histSize, ranges);
// 比较两个密集或两个稀疏直方图
Double score = Imgproc.compareHist(mat3, mat4, Imgproc.CV_COMP_CORREL);
System.out.println("score " + score);
if (score >= 0.8) {
return 1;
}
return 0;
}
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;
}
return null;
}
}
自定义窗口
自定义窗口用于实时获取摄像头拍摄画面
@Component
public class MyJPanel extends JPanel {
@Autowired
private OpenCvUtil openCvUtil;
private BufferedImage mImg;
private VideoCapture videoCapture;
private JFrame frame;
public void paintComponent(Graphics g) {
if (mImg != null) {
g.drawImage(mImg, 0, 0, mImg.getWidth(), mImg.getHeight(), this);
}
}
/**
* 摄像头识别人脸
*/
public Boolean cameraFaceRecognition() throws Exception {
try {
// 打开摄像头获取视频流,0 打开默认摄像头
videoCapture = new VideoCapture(0);
// 检查是否支持摄像头 true:代表摄像头可以打开 false:不可以打开
System.out.println(videoCapture.isOpened());
// 获取摄像头高度
int height = (int) videoCapture.get(Videoio.CAP_PROP_FRAME_HEIGHT);
// 获取摄像头宽度
int width = (int) videoCapture.get(Videoio.CAP_PROP_FRAME_WIDTH);
if (height == 0 || width == 0) {
throw new Exception("摄像头不存在");
}
// 使用Swing生成GUI
frame = new JFrame("人脸识别");
frame.setDefaultCloseOperation(WindowConstants.DISPOSE_ON_CLOSE);
MyJPanel panel = new MyJPanel();
//设置中心显示
frame.setContentPane(panel);
frame.setVisible(true);
frame.setSize(width + frame.getInsets().left + frame.getInsets().right, height + frame.getInsets().top + frame.getInsets().bottom);
frame.setLocationRelativeTo(null);
// 创建矩阵
Mat capImg = new Mat();
// 创建一个临时矩阵
Mat temp = new Mat();
// 对比图片
String comparedImagePath = "D:\\user\\" + "compared.jpg";
// 摄像头拍摄图片
String loginImagePath = "D:\\user\\" + "login.jpg";
int tag = 0;
while (frame.isShowing() && tag < 5) {
tag++;
//从摄像头读取一帧数据,保存到capImg矩阵中。
videoCapture.read(capImg);
//转换为彩色图
Imgproc.cvtColor(capImg, temp, Imgproc.COLOR_RGBA2BGRA);
// 人脸识别
capImg = this.getFace(capImg);
// 本地图片保存
Imgcodecs.imwrite(loginImagePath, capImg);
//转为图像显示
panel.mImg = panel.matToImage(capImg);
// 重绘组件
panel.repaint();
int result = OpenCvUtil.match(loginImagePath, comparedImagePath);
if (result == 1) {
return true;
}
}
} catch (Exception e) {
e.printStackTrace();
} finally {
// 关闭窗口
if (frame != null) {
frame.dispose();
}
// 关闭摄像头
if (videoCapture != null) {
videoCapture.release();
}
}
return false;
}
/**
* 从视频帧中识别人脸
*
* @param image 待处理Mat图片,即视频中的某一帧
* @return 处理后的图片
*/
public Mat getFace(Mat image) {
MatOfRect face = new MatOfRect();
// 检测输入图像中不同大小的对象。检测到的对象作为矩形列表返回。
openCvUtil.faceDetector.detectMultiScale(image, face);
Rect[] rects = face.toArray();
System.out.println("识别人脸个数: " + rects.length);
if (rects != null && rects.length >= 1) {
// 为每张识别到的人脸画一个圈
for (int i = 0; i < rects.length; i++) {
// 绘制一个简单的、粗的或填充的直角矩形
Imgproc.rectangle(image, new org.opencv.core.Point(rects[i].x, rects[i].y), new org.opencv.core.Point(rects[i].x + rects[i].width, rects[i].y + rects[i].height), new Scalar(0, 255, 0));
// 绘制一个文本字符串,放在识别人脸框上
Imgproc.putText(image, "test", new Point(rects[i].x, rects[i].y), Imgproc.FONT_HERSHEY_SCRIPT_SIMPLEX, 1.0, new Scalar(0, 255, 0), 1, Imgproc.LINE_AA, false);
}
}
return image;
}
/**
* 转换图像
*/
private BufferedImage matToImage(Mat mat) {
int dataSize = mat.cols() * mat.rows() * (int) mat.elemSize();
byte[] data = new byte[dataSize];
mat.get(0, 0, data);
int type = mat.channels() == 1 ? BufferedImage.TYPE_BYTE_GRAY : BufferedImage.TYPE_3BYTE_BGR;
if (type == BufferedImage.TYPE_3BYTE_BGR) {
for (int i = 0; i < dataSize; i += 3) {
byte blue = data[i + 0];
data[i + 0] = data[i + 2];
data[i + 2] = blue;
}
}
BufferedImage image = new BufferedImage(mat.cols(), mat.rows(), type);
image.getRaster().setDataElements(0, 0, mat.cols(), mat.rows(), data);
return image;
}
}
创建页面
创建模拟人脸登录的页面Index.html以及人脸登录成功跳转页面success.html和人脸登录失败跳转页面error.html
index.html
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Title</title>
</head>
<body>
<div id="index" class="tab-pane">
<a href="/user/login">人脸登录</a>
</div>
</body>
</html>
success.html
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Title</title>
</head>
<body>
<div>
<h3>人脸识别登录成功</h3>
</div>
</body>
</html>
error.html
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Title</title>
</head>
<body>
<div>
<h3>人脸识别登录失败</h3>
</div>
</body>
</html>
启动类配置
在开发过程中遇到一个异常,即使用自定义窗口时,需要修改启动类,设置
.setHeadless(false)
,或添加JVM参数-Djava.awt.headless=false
来解决。
@SpringBootApplication
public class FaceOpenCvApplication {
public static void main(String[] args) {
SpringApplicationBuilder builder = new SpringApplicationBuilder(FaceOpenCvApplication.class);
builder.headless(false).run(args);
}
}
常见异常记录
异常1
Exception in thread "main" java.lang.UnsatisfiedLinkError: no opencv_java460 in java.library.path
at java.lang.ClassLoader.loadLibrary(ClassLoader.java:1860)
at java.lang.Runtime.loadLibrary0(Runtime.java:871)
at java.lang.System.loadLibrary(System.java:1122)
将D:\Development\opencv\build\java\x64\opencv_java460.dll
文件拷贝至下面2个目录,任选其一即可。
异常2
java.lang.Exception: unknown exception
org.opencv.videoio.VideoCapture.VideoCapture_3(Native Method)
org.opencv.videoio.VideoCapture.<init>(VideoCapture.java:62)
com.boxuegu.servlet.UserFaceLogin.doGet(UserFaceLogin.java:25)
javax.servlet.http.HttpServlet.service(HttpServlet.java:635)
javax.servlet.http.HttpServlet.service(HttpServlet.java:742)
org.apache.tomcat.websocket.server.WsFilter.doFilter(WsFilter.java:52)
配置类库路径
进入D:\Development\opencv\build\x64\vc15\bin
,获取该路径
添加JVM运行参数配置
-Djava.library.path=D:\Development\opencv\build\java\x64
或者
-Djava.library.path=D:\Development\opencv\build\java\x64;D:\Development\opencv\build\x64\vc15\bin
异常3
没重启Tomcat,而是让Tomcat自动重启war包导致
java.lang.UnsatisfiedLinkError: Native Library D:\Development\opencv\build\java\x64\opencv_java460.dll already loaded in another classloader
java.lang.ClassLoader.loadLibrary0(ClassLoader.java:1900)
java.lang.ClassLoader.loadLibrary(ClassLoader.java:1850)
java.lang.Runtime.loadLibrary0(Runtime.java:871)
java.lang.System.loadLibrary(System.java:1122)
com.boxuegu.servlet.UserFaceLogin.doGet(UserFaceLogin.java:24)
javax.servlet.http.HttpServlet.service(HttpServlet.java:635)
javax.servlet.http.HttpServlet.service(HttpServlet.java:742)
org.apache.tomcat.websocket.server.WsFilter.doFilter(WsFilter.java:52)
异常4
Exception in thread "main" java.lang.UnsatisfiedLinkError: org.opencv.videoio.VideoCapture.VideoCapture_5(I)J
at org.opencv.videoio.VideoCapture.VideoCapture_5(Native Method)
at org.opencv.videoio.VideoCapture.<init>(VideoCapture.java:181)
别忘了加载OpenCV本地库
static {
// 加载OpenCV本地库
System.loadLibrary(Core.NATIVE_LIBRARY_NAME);
}
异常5
java.lang.UnsatisfiedLinkError: org.opencv.objdetect.CascadeClassifier.CascadeClassifier_1(Ljava/lang/String;)J
at org.opencv.objdetect.CascadeClassifier.CascadeClassifier_1(Native Method) ~[opencv-460.jar:4.6.0]
at org.opencv.objdetect.CascadeClassifier.<init>(CascadeClassifier.java:48) ~[opencv-460.jar:4.6.0]
受spring-boot-devtools
依赖影响,最初排除此依赖,clean项目后正常。后来又加上此依赖,结果又不影响,注意当修改配置后没反应等异常情况还是多clean项目。
异常6
java.awt.HeadlessException
at java.awt.GraphicsEnvironment.checkHeadless(GraphicsEnvironment.java:204)
at java.awt.Window.<init>(Window.java:536)
at java.awt.Frame.<init>(Frame.java:420)
at javax.swing.JFrame.<init>(JFrame.java:233)
修改启动类,设置.setHeadless(false);
@SpringBootApplication
public class FaceOpenCvApplication {
public static void main(String[] args) {
SpringApplicationBuilder builder = new SpringApplicationBuilder(FaceOpenCvApplication.class);
builder.headless(false).run(args);
}
}
或者设置JVM虚拟机参数
-Djava.awt.headless=false
近期评论