一、功能展示
识别一种图上的所有人的脸,并且标出人脸的位置,画出 人眼以及嘴的位置,展示效果如下:
二、实现思路
- 图片转换为灰色(降低为一维的灰度,减低计算强度)
- 图片上画矩阵
- 使用训练分类器查找人脸
三、实现步骤
1、代码pom引入jar
<properties>
<java.version>1.8</java.version>
<!-- javacpp当前版本 -->
<javacpp.version>1.4.3</javacpp.version>
<!-- opencv版本 -->
<opencv.version>3.4.3</opencv.version>
<!-- ffmpeg版本 -->
<ffmpeg.version>4.0.2</ffmpeg.version>
</properties>
<!-- javacv -->
<dependency>
<groupId>org.bytedeco</groupId>
<artifactId>javacv-platform</artifactId>
<version>${javacpp.version}</version>
</dependency>
<dependency>
<groupId>org.bytedeco</groupId>
<artifactId>javacv</artifactId>
<version>${javacpp.version}</version>
</dependency>
<!-- javacpp -->
<dependency>
<groupId>org.bytedeco</groupId>
<artifactId>javacpp</artifactId>
<version>${javacpp.version}</version>
</dependency>
<!-- ffmpeg -->
<dependency>
<groupId>org.bytedeco.javacpp-presets</groupId>
<artifactId>ffmpeg-platform</artifactId>
<version>${ffmpeg.version}-${javacpp.version}</version>
</dependency>
<dependency>
<groupId>org.bytedeco.javacpp-presets</groupId>
<artifactId>ffmpeg</artifactId>
<version>${ffmpeg.version}-${javacpp.version}</version>
</dependency>
2、图片转换为灰色
使用OpenCV的cvtColor()转换图片颜色,代码如下:
/**
* 图片转换成灰色(降低为一维的灰度,减低计算强度)
* @param path
* @return
*/
private Mat transferToGray(String path) {
// 读取图片
Mat srcImg = Imgcodecs.imread(path);
// 目标灰色图像
Mat dstGrayImg = new Mat();
// 转换灰色
Imgproc.cvtColor(srcImg, dstGrayImg, Imgproc.COLOR_BGR2GRAY);
return dstGrayImg;
}
3、图片上画矩阵
使用OpenCV的rectangle()绘制矩阵,代码如下:
/**
* 在图片上画矩形
* @param path
*/
private void drawRect(String path) {
// 读取图片
Mat srcImg = Imgcodecs.imread(path);
// 目标灰色图像
Mat dstGrayImg = new Mat();
// 转换灰色
Imgproc.cvtColor(srcImg, dstGrayImg, Imgproc.COLOR_BGR2GRAY);
// 坐标
double x = 10, y = 10;
// 矩形大小(宽、高)
double w = 100;
// 定义绘制颜色
Scalar color = new Scalar(0, 0, 255);
Imgproc.rectangle(srcImg, new Point(x, y), new Point(x + w, y + w), color, 1);
HighGui.imshow("预览", srcImg);
// 显示图像
HighGui.waitKey(0);
// 释放所有的窗体资源
HighGui.destroyAllWindows();
}
4、使用训练分类器查找人脸
在使用OpenCV的人脸检测之前,需要一个人脸训练模型,格式是xml的,我们这里使用OpenCV提供好的人脸分类模型xml。
四、完成实现代码
package com.opencv.api;
import org.opencv.core.*;
import org.opencv.highgui.HighGui;
import org.opencv.imgcodecs.Imgcodecs;
import org.opencv.imgproc.Imgproc;
import org.opencv.objdetect.CascadeClassifier;
import java.math.BigDecimal;
import static org.bytedeco.javacpp.opencv_objdetect.CV_HAAR_DO_CANNY_PRUNING;
/**
* 人脸 检测 眉毛 嘴巴
* @Author xxk
* @Date 2021/11/22 17:09
* @Param
* @return
*/
public class ImageFace {
static {
// 加载 动态链接库
// System.loadLibrary(Core.NATIVE_LIBRARY_NAME);
System.load("E:\\OpenCv\\opencv\\build\\java\\x64\\opencv_java453.dll");
}
public static void main(String[] args) {
// String filepath = "/home/yinyue/opencv/test.JPG";
String filePath = "E:\\OpenCv\\img\\l.jpg";
Mat srcImg = Imgcodecs.imread(filePath);
// 目标灰色图像
Mat dstGrayImg = new Mat();
// 转换灰色
Imgproc.cvtColor(srcImg, dstGrayImg, Imgproc.COLOR_BGR2GRAY);
// OpenCv人脸识别分类器
CascadeClassifier classifier = new CascadeClassifier("E:\\OpenCv\\opencv\\sources\\data\\haarcascades\\haarcascade_frontalface_default.xml");
// 用来存放人脸矩形
MatOfRect faceRect = new MatOfRect();
// 特征检测点的最小尺寸
Size minSize = new Size(32, 32);
// 图像缩放比例,可以理解为相机的X倍镜
double scaleFactor = 1.2;
// 对特征检测点周边多少有效检测点同时检测,这样可以避免选取的特征检测点大小而导致遗漏
int minNeighbors = 3;
// 执行人脸检测
classifier.detectMultiScale(dstGrayImg, faceRect, scaleFactor, minNeighbors, CV_HAAR_DO_CANNY_PRUNING, minSize);
//遍历矩形,画到原图上面
// 定义绘制颜色
Scalar color = new Scalar(0, 0, 255);
for(Rect rect: faceRect.toArray()) {
int x = rect.x;
int y = rect.y;
int w = rect.width;
int h = rect.height;
// 单独框出每一张人脸
Imgproc.rectangle(srcImg, new Point(x, y), new Point(x + w, y + w), color, 2);
// 左眼
Imgproc.circle(srcImg, new Point(x + Math.floor(getDivideDouble(w, 4)), y + Math.floor(getDivideDouble(h, 4)) + 15), Math.min(getDivideInt(h, 8), getDivideInt(w, 8)), color);
// 右眼
Imgproc.circle(srcImg, new Point(x + 3 * Math.floor(getDivideDouble(w, 4)), y + Math.floor(getDivideDouble(h, 4)) + 15), Math.min(getDivideInt(h, 8), getDivideInt(w, 8)), color);
// 嘴巴
Imgproc.rectangle(srcImg, new Point(x + 3 * Math.floor(getDivideDouble(w, 8)), y + 3 * Math.floor(getDivideDouble(h, 4)) - 5), new Point(x + 5 * Math.floor(getDivideDouble(w, 8)) + 10, y + 7 * Math.floor(getDivideDouble(h, 8))), color, 2);
}
HighGui.imshow("预览", srcImg);
// 显示图像
HighGui.waitKey(0) ;
// 释放所有的窗体资源
HighGui.destroyAllWindows();
}
/**
* 图片转换成灰色(降低为一维的灰度,减低计算强度)
* @param path
* @return
*/
private static Mat transferToGray(String path) {
// 读取图片
Mat srcImg = Imgcodecs.imread(path);
// 目标灰色图像
Mat dstGrayImg = new Mat();
// 转换灰色
Imgproc.cvtColor(srcImg, dstGrayImg, Imgproc.COLOR_BGR2GRAY);
return dstGrayImg;
}
/**
* 在图片上画矩形
* @param path
*/
private static void drawRect(String path) {
// 读取图片
Mat srcImg = Imgcodecs.imread(path);
// 目标灰色图像
Mat dstGrayImg = new Mat();
// 转换灰色
Imgproc.cvtColor(srcImg, dstGrayImg, Imgproc.COLOR_BGR2GRAY);
// 坐标
double x = 10, y = 10;
// 矩形大小(宽、高)
double w = 100;
// 定义绘制颜色
Scalar color = new Scalar(0, 0, 255);
Imgproc.rectangle(srcImg, new Point(x, y), new Point(x + w, y + w), color, 1);
HighGui.imshow("预览", srcImg);
// 显示图像
HighGui.waitKey(0);
// 释放所有的窗体资源
HighGui.destroyAllWindows();
}
/**
* 计算除法
* @param a
* @param b
* @return
*/
private static double getDivideDouble(int a, int b) {
return new BigDecimal(a).divide(new BigDecimal(b), 2, BigDecimal.ROUND_HALF_UP).doubleValue();
}
/**
* 计算除法
* @param a
* @param b
* @return
*/
private static int getDivideInt(int a, int b) {
return new BigDecimal(a).divide(new BigDecimal(b), 2, BigDecimal.ROUND_HALF_UP).intValue();
}
}
如果想检测身体其他部位,则选择其他的xml文件