Java实现人脸检测

简介: Java实现人脸检测

一、前言


之前上传了一个资源,就是Java实现人脸检测,发现很多人都不会用,就是这个https://download.csdn.net/download/b379685397/10023135。各种乱七八糟评论都有,那就如大家所愿,我发个教程吧。


当前很多博客实现人脸识别的大部分都是调用云厂家的接口,如百度,阿里云。以及我们乐橙开放平台也支持人脸识别等人工智能服务。这些都比较简单,会接开放平台,走接口请求基本上都掌握了。缺点就是有限制,收费。


那么我就在想,能不能不依赖第三方,自己实现人脸检测呢。搜索了相关开源软件,发现有几款比较合适,如openCV,SeetaFaceEngine,openface等。经测试,SeetaFaceEngine准确率最高,因为模型经过大量训练。但存在内存泄漏问题。应该是底层库没有处理好(猜测,没有深究)。openCV对JAVA支持性最好,但准确率差点。需要自己调参不断尝试。


接下来,开始使用Java+openCV实现人脸识别和人眼识别等功能.


其实功能比较简单,主要就是使用了openCV,默认训练好的分类器。接下来进入正片。


二、openCV安装


1、opencv下载


打开opencv官网https://opencv.org/releases/。下载opencv。我使用的是3.43版本。本地为windows、所以选择windows的3.43版本。按照自己的需求进行下载。


20191002213604158.png


2、安装openCV


安装比较简单,一直下一步即可。但记得修改安装路径。


3、openCV重要目录


安装好opencv之后,有build以及source目录。

build目录如下。有对应支持的语言的dll库和引用包。


20191002214024531.png


sources\data目录下,存放着为opencv实现的各种分类器。我们需要使用的人脸和人眼检测的分类器都在里面。如果想详细了解的话可以在opencv的官网里进行查看。


2019100221414882.png


三、工程搭建


人脸识别项目已经上传github,大家可以进行下载导入,下载地址为https://github.com/379685397/FaceDetect。可以的话,帮忙加个星啊亲~。哈哈


1、工程目录


20191002214610604.png


config目录存放的为opencv的分类器。此处使用了正面人脸以及人眼的分类器。


func为实现人脸相关接口


image存放的为测试用图片


tmp为测试使用输出图片。


lib里包含opencv的使用jar包和本地dll库。


工程导入完成之后,需要配置对应的jar包以及修改JDK。


IDEA的话通过file->Project Structure进行设置。选中加号,选择外部jar包引用。选择工程里lib目录下的openCV343.jar。


2019100221501990.png


之后重新编译。看是否报错。没有报错的话项目导入成功


2、代码实现


1、DetectFace

package com.facedetect.func;
import org.opencv.core.*;
import org.opencv.imgcodecs.Imgcodecs;
import org.opencv.imgproc.Imgproc;
import org.opencv.objdetect.CascadeClassifier;
/**
 * @Auther: DarkKing
 * @Date: 2019/10/2 11:06
 * @Description:
 */
public class DetectFace {
    //定义程序的基础路径
    private String basePath =System.getProperty("user.dir");
    //人眼识别分类器路径
    private   String eyeConfigPath=basePath+"\\src\\com\\facedetect\\config\\haarcascade_eye_tree_eyeglasses.xml";
    //人脸识别分类器路径
    private   String faceConfigPath=basePath+"\\src\\com\\facedetect\\config\\haarcascade_frontalface_alt2.xml";
    static{
        // 载入opencv的库
        String opencvpath = System.getProperty("user.dir") + "\\libs\\x64\\";
        String opencvDllName = opencvpath + Core.NATIVE_LIBRARY_NAME + ".dll";
        System.load(opencvDllName);
    }
    /**
     * opencv实现人脸识别
     * @param imagePath
     * @param outFile
     * @throws Exception
     */
    public  void detectFace(String imagePath,  String outFile) throws Exception
    {
        System.out.println("Running DetectFace ...,config path is  "+faceConfigPath);
        String basePath =System.getProperty("user.dir");
        String path= basePath+ "\\src\\com\\facedetect\\tmp\\";
        // 从配置文件lbpcascade_frontalface.xml中创建一个人脸识别器,该文件位于opencv安装目录中,为了方便从安装方便放到了程序路径里
        CascadeClassifier faceDetector = new CascadeClassifier(faceConfigPath);
        //创建图片处理对象
        Mat image = Imgcodecs.imread(imagePath);
        // 在图片中检测人脸
        MatOfRect faceDetections = new MatOfRect();
        //多条件结果检测
        faceDetector.detectMultiScale(image, faceDetections);
        System.out.println(String.format("Detected %s faces", faceDetections.toArray().length));
        //检测结果集
        Rect[] rects = faceDetections.toArray();
        // 在每一个识别出来的人脸周围画出一个方框
        for (int i = 0; i < rects.length; i++) {
            Rect rect = rects[i];
            Imgproc.rectangle(image, new Point(rect.x-2, rect.y-2),
                    new Point(rect.x + rect.width, rect.y + rect.height),
                    new Scalar(0, 255, 0));
            Mat copy = new Mat(image,rect);
            Mat temp  = new Mat();
            copy.copyTo(temp);
            //输出图片
            Imgcodecs.imwrite(path+i+".png", temp);
        }
        Imgcodecs.imwrite(outFile, image);
        System.out.println(String.format("人脸识别成功,人脸图片文件为: %s", outFile));
    }
    /**
     * opencv实现人眼识别
     * @param imagePath
     * @param outFile
     * @throws Exception
     */
    public  void detectEye(String imagePath,  String outFile) throws Exception {
        System.out.println("Running DetectFace ...,config path is  "+eyeConfigPath);
        CascadeClassifier eyeDetector = new CascadeClassifier(
                eyeConfigPath);
        Mat image = Imgcodecs.imread(imagePath);  //读取图片
        // 在图片中检测人脸
        MatOfRect faceDetections = new MatOfRect();
        eyeDetector.detectMultiScale(image, faceDetections, 2.0,1,1,new Size(20,20),new Size(20,20));
        System.out.println(String.format("Detected %s eyes", faceDetections.toArray().length));
        Rect[] rects = faceDetections.toArray();
        if(rects != null && rects.length <2){
            throw new RuntimeException("不是一双眼睛");
        }
        Rect eyea = rects[0];
        Rect eyeb = rects[1];
        System.out.println("a-中心坐标 " + eyea.x + " and " + eyea.y);
        System.out.println("b-中心坐标 " + eyeb.x + " and " + eyeb.y);
        //获取两个人眼的角度
        double dy=(eyeb.y-eyea.y);
        double dx=(eyeb.x-eyea.x);
        double len=Math.sqrt(dx*dx+dy*dy);
        System.out.println("dx is "+dx);
        System.out.println("dy is "+dy);
        System.out.println("len is "+len);
        double angle=Math.atan2(Math.abs(dy),Math.abs(dx))*180.0/Math.PI;
        System.out.println("angle is "+angle);
        for(Rect rect:faceDetections.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));
        }
        Imgcodecs.imwrite(outFile, image);
        System.out.println(String.format("人眼识别成功,人眼图片文件为: %s", outFile));
    }
}


该函数主要实现了两个方法,一个是人脸检测,一个是人眼检测。方法都差不多。主要是加载的分类器不同。以及结果集的过滤。其中重要的一个方法时是MatOfRect的detectMultiScale方法。该方法共有7个参数。含义如下。


  • 参数1:image--待检测图片,一般为灰度图像加快检测速度;
  • 参数2:objects--被检测物体的矩形框向量组;
  • 参数3:scaleFactor--表示在前后两次相继的扫描中,搜索窗口的比例系数。默认为1.1即每次搜索窗口依次扩大10%;
  • 参数4:minNeighbors--表示构成检测目标的相邻矩形的最小个数(默认为3个)。        如果组成检测目标的小矩形的个数和小于 min_neighbors - 1 都会被排除。       如果min_neighbors 为 0, 则函数不做任何操作就返回所有的被检候选矩形框,        这种设定值一般用在用户自定义对检测结果的组合程序上
  • 参数5:flags--要么使用默认值,要么使用CV_HAAR_DO_CANNY_PRUNING,如果设置为          CV_HAAR_DO_CANNY_PRUNING,那么函数将会使用Canny边缘检测来排除边缘过多或过少的区域,       因此这些区    域通常不会是人脸所在区域;
  • 参数6、7:minSize和maxSize用来限制得到的目标区域的范围。


2、ImageUtils

package com.facedetect.func;
import org.opencv.core.Mat;
import org.opencv.core.Rect;
import org.opencv.core.Size;
import org.opencv.imgcodecs.Imgcodecs;
import org.opencv.imgproc.Imgproc;
import javax.imageio.ImageIO;
import javax.swing.*;
import java.awt.*;
import java.awt.image.BufferedImage;
import java.io.File;
import java.io.IOException;
/**
 * @Auther: DarkKing
 * @Date: 2019/10/2 11:12
 * @Description:
 */
public class ImageUtils {
    /**
     * 裁剪图片并重新装换大小
     * @param imagePath
     * @param posX
     * @param posY
     * @param width
     * @param height
     * @param outFile
     */
    public static void imageCut(String imagePath,String outFile, int posX,int posY,int width,int height ){
        //原始图像
        Mat image = Imgcodecs.imread(imagePath);
        //截取的区域:参数,坐标X,坐标Y,截图宽度,截图长度
        Rect rect = new Rect(posX,posY,width,height);
        //两句效果一样
        Mat sub = image.submat(rect);   //Mat sub = new Mat(image,rect);
        Mat mat = new Mat();
        Size size = new Size(300, 300);
        Imgproc.resize(sub, mat, size);//将人脸进行截图并保存
        Imgcodecs.imwrite(outFile, mat);
        System.out.println(String.format("图片裁切成功,裁切后图片文件为: %s", outFile));
    }
    /**
     *
     * @param imagePath
     * @param outFile
     */
    public static void setAlpha(String imagePath,  String outFile) {
        /**
         * 增加测试项
         * 读取图片,绘制成半透明
         */
        try {
            ImageIcon imageIcon = new ImageIcon(imagePath);
            BufferedImage bufferedImage = new BufferedImage(imageIcon.getIconWidth(),
                    imageIcon.getIconHeight(), BufferedImage.TYPE_4BYTE_ABGR);
            Graphics2D g2D = (Graphics2D) bufferedImage.getGraphics();
            g2D.drawImage(imageIcon.getImage(), 0, 0, imageIcon.getImageObserver());
            //循环每一个像素点,改变像素点的Alpha值
            int alpha = 100;
            for (int j1 = bufferedImage.getMinY(); j1 < bufferedImage.getHeight(); j1++) {
                for (int j2 = bufferedImage.getMinX(); j2 < bufferedImage.getWidth(); j2++) {
                    int rgb = bufferedImage.getRGB(j2, j1);
                    rgb = ( (alpha + 1) << 24) | (rgb & 0x00ffffff);
                    bufferedImage.setRGB(j2, j1, rgb);
                }
            }
            g2D.drawImage(bufferedImage, 0, 0, imageIcon.getImageObserver());
            //生成图片为PNG
            ImageIO.write(bufferedImage, "png",  new File(outFile));
            System.out.println(String.format("绘制图片半透明成功,图片文件为: %s", outFile));
        }
        catch (Exception e) {
            e.printStackTrace();
        }
    }
    /**
     * 为图像添加水印
     * @param buffImgFile 底图
     * @param waterImgFile 水印
     * @param outFile 输出图片
     * @param alpha   透明度
     * @throws IOException
     */
    private static void watermark(String buffImgFile,String waterImgFile,String outFile, float alpha) throws IOException {
        // 获取底图
        BufferedImage buffImg = ImageIO.read(new File(buffImgFile));
        // 获取层图
        BufferedImage waterImg = ImageIO.read(new File(waterImgFile));
        // 创建Graphics2D对象,用在底图对象上绘图
        Graphics2D g2d = buffImg.createGraphics();
        int waterImgWidth = waterImg.getWidth();// 获取水印层图的宽度
        int waterImgHeight = waterImg.getHeight();// 获取水印层图的高度
        // 在图形和图像中实现混合和透明效果
        g2d.setComposite(AlphaComposite.getInstance(AlphaComposite.SRC_ATOP, alpha));
        // 绘制
        g2d.drawImage(waterImg, 0, 0, waterImgWidth, waterImgHeight, null);
        g2d.dispose();// 释放图形上下文使用的系统资源
        //生成图片为PNG
        ImageIO.write(buffImg, "png",  new File(outFile));
        System.out.println(String.format("图片添加水印成功,图片文件为: %s", outFile));
    }
    /**
     * 图片合成
     * @param image1
     * @param image2
     * @param posw
     * @param posh
     * @param outFile
     * @return
     */
    public static void simpleMerge(String image1, String image2, int posw, int posh, String outFile) throws IOException{
        // 获取底图
        BufferedImage buffImg1 = ImageIO.read(new File(image1));
        // 获取层图
        BufferedImage buffImg2 = ImageIO.read(new File(image2));
        //合并两个图像
        int w1 = buffImg1.getWidth();
        int h1 = buffImg1.getHeight();
        int w2 = buffImg2.getWidth();
        int h2 = buffImg2.getHeight();
        BufferedImage imageSaved = new BufferedImage(w1, h1, BufferedImage.TYPE_INT_ARGB); //创建一个新的内存图像
        Graphics2D g2d = imageSaved.createGraphics();
        g2d.drawImage(buffImg1, null, 0, 0);  //绘制背景图像
        for (int i = 0; i < w2; i++) {
            for (int j = 0; j < h2; j++) {
                int rgb1 = buffImg1.getRGB(i + posw, j + posh);
                int rgb2 = buffImg2.getRGB(i, j);
                /*if (rgb1 != rgb2) {
                    rgb2 = rgb1 & rgb2;
                }*/
                imageSaved.setRGB(i + posw, j + posh, rgb2); //修改像素值
            }
        }
        ImageIO.write(imageSaved, "png", new File(outFile));
        System.out.println(String.format("图片合成成功,合成图片文件为: %s", outFile));
    }
}


改类为文件处理类。

3、DetectFaceTest 测试类

package com.facedetect;
import java.awt.AlphaComposite;
import java.awt.Graphics2D;
import java.awt.image.BufferedImage;
import java.io.File;
import java.io.IOException;
import javax.imageio.ImageIO;
import javax.swing.ImageIcon;
import com.facedetect.func.DetectFace;
import com.facedetect.func.ImageUtils;
import com.sun.imageio.plugins.common.ImageUtil;
import org.opencv.core.Core;
import org.opencv.core.Mat;
import org.opencv.core.MatOfRect;
import org.opencv.core.Point;
import org.opencv.core.Rect;
import org.opencv.core.Scalar;
import org.opencv.core.Size;
import org.opencv.imgcodecs.Imgcodecs;
import org.opencv.imgproc.Imgproc;
import org.opencv.objdetect.CascadeClassifier;
/**
 * DrakKing
 */
public class DetectFaceTest {
    public static void main(String[] args) throws Exception {
        DetectFace df = new DetectFace();
         String basePath =System.getProperty("user.dir");
        String s1= basePath+ "\\src\\com\\facedetect\\image\\test1.jpg";
        String s2= basePath+ "\\src\\com\\facedetect\\image\\test2.jpg";
        String s3= basePath+ "\\src\\com\\facedetect\\image\\test3.jpg";
        String e1= basePath+ "\\src\\com\\facedetect\\image\\1.png";
        String faceTemp = basePath+"\\src\\com\\facedetect\\tmp\\faceTemp.png";
        String eyeTemp = basePath+"\\src\\com\\facedetect\\tmp\\eyeTemp.png";
        //人脸识别
        df.detectFace(s1, faceTemp);
        //人眼识别
      df.detectEye(e1,  eyeTemp);
        //图片裁切
       // ImageUtils.imageCut(s3,temp, 50, 50,100,100);
        //设置图片为半透明
//        ImageUtils.setAlpha(s, temp);
        //为图片添加水印
//        ImageUtils.watermark(s,"E:\\ling.jpg",temp, 0.2f);
        //图片合成
//        ImageUtils.simpleMerge(s, "E:\\ling.jpg", 45, 50, temp);
    }
}


4、测试结果


测试拿test1图片做人脸测试,1.png做人眼测试。查看输出结果

20191002220330769.png


20191002220457713.png

执行DetectFaceTest。查看结果。人脸检测到3张。人眼检测到一张。


2019100222044033.png


图片输出


20191002220531244.png


20191002220621112.png


20191002220713533.png


2019100222060056.png




到这教程就结束了,在实际使用过程中,我发现人眼识别准确率很低。可能是我的参数有问题。大家可以多进行测试一下。

目录
相关文章
|
6天前
|
缓存 算法 Java
本文聚焦于Java内存管理与调优,介绍Java内存模型、内存泄漏检测与预防、高效字符串拼接、数据结构优化及垃圾回收机制
在现代软件开发中,性能优化至关重要。本文聚焦于Java内存管理与调优,介绍Java内存模型、内存泄漏检测与预防、高效字符串拼接、数据结构优化及垃圾回收机制。通过调整垃圾回收器参数、优化堆大小与布局、使用对象池和缓存技术,开发者可显著提升应用性能和稳定性。
21 6
|
4月前
|
存储 算法 Java
Java面试题:深入探究Java内存模型与垃圾回收机制,解释JVM中堆内存和栈内存的主要区别,谈谈对Java垃圾回收机制的理解,Java中的内存泄漏及其产生原因,如何检测和解决内存泄漏问题
Java面试题:深入探究Java内存模型与垃圾回收机制,解释JVM中堆内存和栈内存的主要区别,谈谈对Java垃圾回收机制的理解,Java中的内存泄漏及其产生原因,如何检测和解决内存泄漏问题
65 0
|
5月前
|
机器学习/深度学习 Java 开发工具
【移花接木】OpenCV4.8 For Java 深度学习 实时人脸检测
【移花接木】OpenCV4.8 For Java 深度学习 实时人脸检测
101 0
|
1月前
|
安全 Java Python
基于python-django的Java网站全站漏洞检测系统
基于python-django的Java网站全站漏洞检测系统
32 0
|
3月前
|
监控 算法 安全
Java并发编程案例分析:死锁的检测与解决
Java并发编程案例分析:死锁的检测与解决
37 2
|
3月前
|
网络协议 Java
JAVA实现心跳检测【长连接】
这篇文章介绍了Java中实现心跳检测机制的方法,包括心跳机制的简介、实现方式、客户端和服务端的代码实现,以及具体的测试结果。文中详细阐述了如何通过自定义心跳包和超时检测来维持长连接,并提供了完整的客户端和服务端示例代码。
JAVA实现心跳检测【长连接】
|
4月前
|
SQL 安全 JavaScript
Java中的代码审计与漏洞检测
Java中的代码审计与漏洞检测
|
5月前
|
Java API Apache
探讨Java中检测字符串是否包含数字和字母的技术
探讨Java中检测字符串是否包含数字和字母的技术
77 2
|
4月前
|
SQL 安全 Java
Java中的代码审计与漏洞检测实践指南
Java中的代码审计与漏洞检测实践指南
|
5月前
|
监控 Java API
使用Java检测当前CPU负载状态的技术博客
使用Java检测当前CPU负载状态的技术博客
86 0