一、功能展示

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

image.png

二、实现思路

  • 图片转换为灰色(降低为一维的灰度,减低计算强度)
  • 图片上画矩阵
  • 使用训练分类器查找人脸

三、实现步骤

1、代码pom引入jar

  1. <properties>
  2. <java.version>1.8</java.version>
  3. <!-- javacpp当前版本 -->
  4. <javacpp.version>1.4.3</javacpp.version>
  5. <!-- opencv版本 -->
  6. <opencv.version>3.4.3</opencv.version>
  7. <!-- ffmpeg版本 -->
  8. <ffmpeg.version>4.0.2</ffmpeg.version>
  9. </properties>
  10. <!-- javacv -->
  11. <dependency>
  12. <groupId>org.bytedeco</groupId>
  13. <artifactId>javacv-platform</artifactId>
  14. <version>${javacpp.version}</version>
  15. </dependency>
  16. <dependency>
  17. <groupId>org.bytedeco</groupId>
  18. <artifactId>javacv</artifactId>
  19. <version>${javacpp.version}</version>
  20. </dependency>
  21. <!-- javacpp -->
  22. <dependency>
  23. <groupId>org.bytedeco</groupId>
  24. <artifactId>javacpp</artifactId>
  25. <version>${javacpp.version}</version>
  26. </dependency>
  27. <!-- ffmpeg -->
  28. <dependency>
  29. <groupId>org.bytedeco.javacpp-presets</groupId>
  30. <artifactId>ffmpeg-platform</artifactId>
  31. <version>${ffmpeg.version}-${javacpp.version}</version>
  32. </dependency>
  33. <dependency>
  34. <groupId>org.bytedeco.javacpp-presets</groupId>
  35. <artifactId>ffmpeg</artifactId>
  36. <version>${ffmpeg.version}-${javacpp.version}</version>
  37. </dependency>

2、图片转换为灰色

使用OpenCV的cvtColor()转换图片颜色,代码如下:

  1. /**
  2. * 图片转换成灰色(降低为一维的灰度,减低计算强度)
  3. * @param path
  4. * @return
  5. */
  6. private Mat transferToGray(String path) {
  7. // 读取图片
  8. Mat srcImg = Imgcodecs.imread(path);
  9. // 目标灰色图像
  10. Mat dstGrayImg = new Mat();
  11. // 转换灰色
  12. Imgproc.cvtColor(srcImg, dstGrayImg, Imgproc.COLOR_BGR2GRAY);
  13. return dstGrayImg;
  14. }

3、图片上画矩阵

使用OpenCV的rectangle()绘制矩阵,代码如下:

  1. /**
  2. * 在图片上画矩形
  3. * @param path
  4. */
  5. private void drawRect(String path) {
  6. // 读取图片
  7. Mat srcImg = Imgcodecs.imread(path);
  8. // 目标灰色图像
  9. Mat dstGrayImg = new Mat();
  10. // 转换灰色
  11. Imgproc.cvtColor(srcImg, dstGrayImg, Imgproc.COLOR_BGR2GRAY);
  12. // 坐标
  13. double x = 10, y = 10;
  14. // 矩形大小(宽、高)
  15. double w = 100;
  16. // 定义绘制颜色
  17. Scalar color = new Scalar(0, 0, 255);
  18. Imgproc.rectangle(srcImg, new Point(x, y), new Point(x + w, y + w), color, 1);
  19. HighGui.imshow("预览", srcImg);
  20. // 显示图像
  21. HighGui.waitKey(0);
  22. // 释放所有的窗体资源
  23. HighGui.destroyAllWindows();
  24. }

4、使用训练分类器查找人脸

在使用OpenCV的人脸检测之前,需要一个人脸训练模型,格式是xml的,我们这里使用OpenCV提供好的人脸分类模型xml。

四、完成实现代码

  1. package com.opencv.api;
  2. import org.opencv.core.*;
  3. import org.opencv.highgui.HighGui;
  4. import org.opencv.imgcodecs.Imgcodecs;
  5. import org.opencv.imgproc.Imgproc;
  6. import org.opencv.objdetect.CascadeClassifier;
  7. import java.math.BigDecimal;
  8. import static org.bytedeco.javacpp.opencv_objdetect.CV_HAAR_DO_CANNY_PRUNING;
  9. /**
  10. * 人脸 检测 眉毛 嘴巴
  11. * @Author xxk
  12. * @Date 2021/11/22 17:09
  13. * @Param
  14. * @return
  15. */
  16. public class ImageFace {
  17. static {
  18. // 加载 动态链接库
  19. // System.loadLibrary(Core.NATIVE_LIBRARY_NAME);
  20. System.load("E:\\OpenCv\\opencv\\build\\java\\x64\\opencv_java453.dll");
  21. }
  22. public static void main(String[] args) {
  23. // String filepath = "/home/yinyue/opencv/test.JPG";
  24. String filePath = "E:\\OpenCv\\img\\l.jpg";
  25. Mat srcImg = Imgcodecs.imread(filePath);
  26. // 目标灰色图像
  27. Mat dstGrayImg = new Mat();
  28. // 转换灰色
  29. Imgproc.cvtColor(srcImg, dstGrayImg, Imgproc.COLOR_BGR2GRAY);
  30. // OpenCv人脸识别分类器
  31. CascadeClassifier classifier = new CascadeClassifier("E:\\OpenCv\\opencv\\sources\\data\\haarcascades\\haarcascade_frontalface_default.xml");
  32. // 用来存放人脸矩形
  33. MatOfRect faceRect = new MatOfRect();
  34. // 特征检测点的最小尺寸
  35. Size minSize = new Size(32, 32);
  36. // 图像缩放比例,可以理解为相机的X倍镜
  37. double scaleFactor = 1.2;
  38. // 对特征检测点周边多少有效检测点同时检测,这样可以避免选取的特征检测点大小而导致遗漏
  39. int minNeighbors = 3;
  40. // 执行人脸检测
  41. classifier.detectMultiScale(dstGrayImg, faceRect, scaleFactor, minNeighbors, CV_HAAR_DO_CANNY_PRUNING, minSize);
  42. //遍历矩形,画到原图上面
  43. // 定义绘制颜色
  44. Scalar color = new Scalar(0, 0, 255);
  45. for(Rect rect: faceRect.toArray()) {
  46. int x = rect.x;
  47. int y = rect.y;
  48. int w = rect.width;
  49. int h = rect.height;
  50. // 单独框出每一张人脸
  51. Imgproc.rectangle(srcImg, new Point(x, y), new Point(x + w, y + w), color, 2);
  52. // 左眼
  53. 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);
  54. // 右眼
  55. 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);
  56. // 嘴巴
  57. 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);
  58. }
  59. HighGui.imshow("预览", srcImg);
  60. // 显示图像
  61. HighGui.waitKey(0) ;
  62. // 释放所有的窗体资源
  63. HighGui.destroyAllWindows();
  64. }
  65. /**
  66. * 图片转换成灰色(降低为一维的灰度,减低计算强度)
  67. * @param path
  68. * @return
  69. */
  70. private static Mat transferToGray(String path) {
  71. // 读取图片
  72. Mat srcImg = Imgcodecs.imread(path);
  73. // 目标灰色图像
  74. Mat dstGrayImg = new Mat();
  75. // 转换灰色
  76. Imgproc.cvtColor(srcImg, dstGrayImg, Imgproc.COLOR_BGR2GRAY);
  77. return dstGrayImg;
  78. }
  79. /**
  80. * 在图片上画矩形
  81. * @param path
  82. */
  83. private static void drawRect(String path) {
  84. // 读取图片
  85. Mat srcImg = Imgcodecs.imread(path);
  86. // 目标灰色图像
  87. Mat dstGrayImg = new Mat();
  88. // 转换灰色
  89. Imgproc.cvtColor(srcImg, dstGrayImg, Imgproc.COLOR_BGR2GRAY);
  90. // 坐标
  91. double x = 10, y = 10;
  92. // 矩形大小(宽、高)
  93. double w = 100;
  94. // 定义绘制颜色
  95. Scalar color = new Scalar(0, 0, 255);
  96. Imgproc.rectangle(srcImg, new Point(x, y), new Point(x + w, y + w), color, 1);
  97. HighGui.imshow("预览", srcImg);
  98. // 显示图像
  99. HighGui.waitKey(0);
  100. // 释放所有的窗体资源
  101. HighGui.destroyAllWindows();
  102. }
  103. /**
  104. * 计算除法
  105. * @param a
  106. * @param b
  107. * @return
  108. */
  109. private static double getDivideDouble(int a, int b) {
  110. return new BigDecimal(a).divide(new BigDecimal(b), 2, BigDecimal.ROUND_HALF_UP).doubleValue();
  111. }
  112. /**
  113. * 计算除法
  114. * @param a
  115. * @param b
  116. * @return
  117. */
  118. private static int getDivideInt(int a, int b) {
  119. return new BigDecimal(a).divide(new BigDecimal(b), 2, BigDecimal.ROUND_HALF_UP).intValue();
  120. }
  121. }

如果想检测身体其他部位,则选择其他的xml文件
image.png

五、报错解决

image.png
image.png