写点什么

Java + opencv 实现图片人脸检测

用户头像
张音乐
关注
发布于: 2021 年 03 月 29 日
Java + opencv 实现图片人脸检测

一、功能展示

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


二、技术实现思路

图片转换成灰色(降低为一维的灰度,减低计算强度)


图片上画矩形


使用训练分类器查找人脸


三、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>
 
复制代码


四、具体实现代码

(1)、图片转换成灰色

使用 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;
}



复制代码


(2)、图片上画矩形

使用 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();

}
复制代码


(3)、使用训练分类器查找人脸

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


下载地址:https://github.com/opencv/opencv/tree/master/data/haarcascades 可全部下载到本地,


本人存放的路径是:D:\workspace\opencv\data\haarcascades\


五、完整实现代码:

package com.biubiu.example;  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; /** * 人脸检测 */public class FaceDetect {     static {        // 加载 动态链接库        System.loadLibrary(Core.NATIVE_LIBRARY_NAME);    }     public static void main(String[] args) {         String filepath = "/home/yinyue/opencv/test.JPG";        Mat srcImg = Imgcodecs.imread(filepath);        // 目标灰色图像        Mat dstGrayImg = new Mat();        // 转换灰色        Imgproc.cvtColor(srcImg, dstGrayImg, Imgproc.COLOR_BGR2GRAY);        // OpenCv人脸识别分类器        CascadeClassifier classifier = new CascadeClassifier("D:\workspace\opencv\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 文件


OpenCv 人脸识别分类器 classifier.detectMultiScale(dstGrayImg, faceRect, scaleFactor, minNeighbors, CVHAARDO_CANNY_PRUNING, minSize); 参数说明:

gray:转换的灰图

scaleFactor:图像缩放比例,可理解为相机的 X 倍镜

minNeighbors:对特征检测点周边多少有效点同时检测,这样可避免因选取的特征检测点太小而导致遗漏

minSize:特征检测点的最小尺寸

flags:操作方式。


分为 :

CVHAARDO_CANNY_PRUNING(CANNY 边缘检测)

CVHAARSCALE_IMAGE(缩放图像检测)

CVHAARFIND_BIGGEST_OBJECT(寻找最大的目标)CVHAARDO_ROUGH_SEARCH(做粗略搜索)

如果 CVHAARDO_CANNY_PRUNING 被设定,函数利用 Canny 边缘检测器来排除一些边缘很少或者很多的图像区域,因为这样的区域一般不含被检目标。人脸检测中通过设定阈值使用了这种方法,并因此提高了检测速度。当然该标记是在没有定义 CVHAARSCALE_IMAGE 下使用的,也就是说使用缩放检测窗口的形式定义的


如果 CVHAARSCALE_IMAGE 被设定则在每一个 scale 上缩放图像检测,如果没有定义则缩放检测窗口进行检测,当缩放检测窗口检测的时候是不能返回 rejectLevels 和 levelWeights 的。


如果 CVHAARFIND_BIGGEST_OBJECT 被设定,如果没有设定 CVHAARSCALEIMAGE,保存当前检测窗口中面积最大的矩形,不管设定没有设定 CVHAAR_SCALE_IMAGE,最后都输出一个面积最大的矩形(如果检测结果不为空的话),详细分析可以参考 cvHaarDetectObjectsForROC


如果 CVHAARDO_ROUGH_SEARCH 设定了,则最小的缩放比例为 0.6,否则为 0.4,仅在缩放检测窗口中有效


六、windows 下报错问题解决

(1) 、UnsatisfiedLinkError 异常

在程序运行前 加载 动态链接库


(2) 、no opencv_java343 in java.library.path


解决办法

如果你的版本和我的不一致,可去下载你自己对应的版本.

windows 下载 opencv-3.4.3-vc14_vc15.exe 并安装,下载地址:  

https://nchc.dl.sourceforge.net/project/opencvlibrary/opencv-win/3.4.3/opencv-3.4.3-vc14_vc15.exe

我的安装地址是 D:\workstation\opencv\opencv 

在 idea 中配置 -Djava.library.path=D:\workstation\opencv\opencv\build\java\x64

Linux 下载 opencv-3.4.3.zip 下载地址:  https://udomain.dl.sourceforge.net/project/opencvlibrary/opencv-unix/3.4.3/opencv-3.4.3.zip

同样配置,或追加运行参数

在 linux 环境下的安装可参考

重要的事情说三遍,注意版本号,注意版本号,注意版本号


发布于: 2021 年 03 月 29 日阅读数: 14
用户头像

张音乐

关注

求你关注我,别不识抬举.别逼我跪下来求你. 2021.03.28 加入

还未添加个人简介

评论

发布
暂无评论
Java + opencv 实现图片人脸检测