一、功能展示
识别一种图上的所有人的脸,并且标出人脸的位置,画出 人眼以及嘴的位置,展示效果如下:

二、实现思路
- 图片转换为灰色(降低为一维的灰度,减低计算强度)
- 图片上画矩阵
- 使用训练分类器查找人脸
三、实现步骤
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文件
五、报错解决


