libfacedetection

libfacedetection是深圳大学于仕途琪老师开发的一个用于人脸检测的算法,并且开源到了GitHub上面,它是基于CNN模型的开源库,可以在Linux、ARM、Windows等任何平台下使用C++编译器编译源代码。

获取libfacedetection

可以到GitHub直接搜索libfacedetection,选择于仕琪教授的。

人脸识别java开发源码 人脸识别java开源框架_目标检测


有的小伙伴可能无法访问到GitHub,我这里也将于仕琪教授开源到GitHub上的源码转移到了gitee上,需要的小伙伴可以私聊我获取源码。

编译libfacedetection

COMPILE.md文件中有介绍如何在各个平台编译libfacedetection。

人脸识别java开发源码 人脸识别java开源框架_java_02

使用Java封装libfacedetection

1、加载动态链接库

将编译好的.dll文件或者.so文件放在/resource/lib目录下;
导入JNA依赖(接触过JNI的人应该知道,它允许Java代码和其他语言(尤其C/C++)写的代码进行交互,只要遵守调用约定即可,不过使用JNI进行交叉编译步骤非常繁多,所以sun公司则推出了一种可以简化调用本地方法过程的Java开源框架-JNA,使用JNA之后我们只需要写Java代码,而不用写JNI或本地代码,大大降低了开发难度);

<dependency>
            <groupId>net.java.dev.jna</groupId>
            <artifactId>jna</artifactId>
            <version>5.9.0</version>
</dependency>

2、创建接口继承Library,加载动态链接库,声明动态链接库中的 facedetect_cnn() 方法;

public interface CLibrary extends Library {
    // 通过反射创建CLibrary实例
    CLibrary faceDetect = Native.load("libFaceDetection", CLibrary.class);

    // 声明源码中的facedetect_cnn方法
    Pointer facedetect_cnn(Pointer result_buffer, Pointer rgb_image_data, int width, int height, int step);

}

3、创建实体类用来保存结果集;

public class FacePoint {
    Integer X;
    Integer Y;

    public Integer getX() {
        return X;
    }

    public void setX(Integer x) {
        X = x;
    }

    public Integer getY() {
        return Y;
    }

    public void setY(Integer y) {
        Y = y;
    }

}
public class FaceInfo {
    Integer Confidence;
    Integer X;
    Integer Y;
    Integer Width;
    Integer Height;
    ArrayList<FacePoint> points;

    public Integer getConfidence() {
        return Confidence;
    }

    public void setConfidence(Integer confidence) {
        Confidence = confidence;
    }

    public Integer getX() {
        return X;
    }

    public void setX(Integer x) {
        X = x;
    }

    public Integer getY() {
        return Y;
    }

    public void setY(Integer y) {
        Y = y;
    }

    public Integer getWidth() {
        return Width;
    }

    public void setWidth(Integer width) {
        Width = width;
    }

    public Integer getHeight() {
        return Height;
    }

    public void setHeight(Integer height) {
        Height = height;
    }

    public ArrayList<FacePoint> getPoints() {
        return points;
    }

    public void setPoints(ArrayList<FacePoint> points) {
        this.points = points;
    }

}
public class FaceResult {
    Integer FaceCount;
    ArrayList<FaceInfo> faceInfos;

    public Integer getFaceCount() {
        return FaceCount;
    }

    public void setFaceCount(Integer faceCount) {
        FaceCount = faceCount;
    }

    public ArrayList<FaceInfo> getFaceInfos() {
        return faceInfos;
    }

    public void setFaceInfos(ArrayList<FaceInfo> faceInfos) {
        this.faceInfos = faceInfos;
    }

}

4、使用JNA调用 facedetect_cnn() 方法获取结果集;

public class FaceDetectUtil {

    //定义缓冲区大小:0X20000字节
    public static final Long DETECT_BUFFER_SIZE = 0x20000L;

    /**
     * @param imgPath  服务器中文件位置
     * @param fileName 文件名
     * @return 结果集
     */
    public static FaceResult faceDetect(String imgPath, String fileName) {

        //读取图片资源
        Mat mat = opencv_imgcodecs.imread(imgPath);
        if (mat.cols() > 600) {
            float temp = (float) mat.rows() / mat.cols();
            //重设图片大小,(输入图像,输出图像,新的大小)
            opencv_imgproc.resize(mat, mat, new Size(600, Math.round(600 * temp)));
        }
        //获取mat对象的地址
        Pointer bgrData = new Pointer(mat.data().address());
        //分配缓冲区内存
        Pointer resultBuffer = new Memory(DETECT_BUFFER_SIZE);
        //调用人脸识别方法
        Pointer pResults = CLibrary.faceDetect.facedetect_cnn(resultBuffer, bgrData, mat.cols(), mat.rows(), (int) mat.step1());

        //opencv处理图片,获取结果集
        FaceResult faceResult = new FaceResult();
        faceResult.setFaceCount(pResults.getInt(0L));
        System.err.println("FaceDetectUtil.faceDetect:faceCount:" + faceResult.getFaceCount());
        ArrayList<FaceInfo> faceInfos = new ArrayList<>();
        for (int i = 0; i < faceResult.getFaceCount(); i++) {
            Pointer p = pResults.share(4L).share(142L * i * 2L);

            FaceInfo faceInfo = new FaceInfo();
            faceInfo.setConfidence((int) p.getShort(0L));
            faceInfo.setX((int) p.getShort(2L));
            faceInfo.setY((int) p.getShort(4L));
            faceInfo.setWidth((int) p.getShort(6L));
            faceInfo.setHeight((int) p.getShort(8L));

            ArrayList<FacePoint> points = new ArrayList<>();
            for (int j = 0; j < 5; j++) {

                FacePoint facePoint = new FacePoint();
                facePoint.setX((int) p.getShort(10L + j * 4L));
                facePoint.setY((int) p.getShort(12L + j * 4L));

                points.add(j, facePoint);
            }
            faceInfo.setPoints(points);
            faceInfos.add(i, faceInfo);
        }
        faceResult.setFaceInfos(faceInfos);
        return faceResult;
    }

}

5、导入OpenCV依赖,使用结果集中的数据对图片进行处理;

<dependency>
            <groupId>org.bytedeco</groupId>
            <artifactId>javacpp</artifactId>
            <version>1.5.8</version>
            <classifier>${opencv.platform}</classifier>
        </dependency>
        <dependency>
            <groupId>org.bytedeco</groupId>
            <artifactId>openblas</artifactId>
            <version>0.3.21-1.5.8</version>
            <classifier>${opencv.platform}</classifier>
        </dependency>
        <dependency>
            <groupId>org.bytedeco</groupId>
            <artifactId>opencv</artifactId>
            <version>4.6.0-1.5.8</version>
            <classifier>${opencv.platform}</classifier>
        </dependency>
        <dependency>
            <groupId>org.bytedeco</groupId>
            <artifactId>opencv</artifactId>
            <version>4.6.0-1.5.8</version>
        </dependency>
public File testDetect(String imgPath, String fileName) {
        //读取图片资源
        Mat mat = opencv_imgcodecs.imread(imgPath);
        if (mat.cols() > 600) {
            float temp = (float) mat.rows() / mat.cols();
            //重设图片大小,(输入图像,输出图像,新的大小)
            opencv_imgproc.resize(mat, mat, new Size(600, Math.round(600 * temp)));
        }

        //opencv处理图片,标记人脸以及人脸特征点
        Mat resultImage = mat.clone();
        FaceResult faceResult = FaceDetectUtil.faceDetect(imgPath, fileName);

        for (int i = 0; i < faceResult.getFaceCount(); i++) {
            //矩形框标记人脸
            opencv_imgproc.rectangle(resultImage,
                    new Rect(faceResult.getFaceInfos().get(i).getX(),
                            faceResult.getFaceInfos().get(i).getY(),
                            faceResult.getFaceInfos().get(i).getWidth(),
                            faceResult.getFaceInfos().get(i).getHeight()),
                    new Scalar(0d, 255d, 0d, 0));

            Scalar[] scalars = new Scalar[5];
            scalars[0] = new Scalar(255, 0, 0, 0);
            scalars[1] = new Scalar(0, 0, 255, 0);
            scalars[2] = new Scalar(0, 255, 0, 0);
            scalars[3] = new Scalar(255, 0, 255, 0);
            scalars[4] = new Scalar(0, 255, 255, 0);

            //标记人脸特征点
            for (int j = 0; j < 5; j++) {
                try (Scalar scalar = scalars[j]) {
                    opencv_imgproc.circle(resultImage, new Point(faceResult.getFaceInfos().get(i).getPoints().get(j).getX(),
                            faceResult.getFaceInfos().get(i).getPoints().get(j).getY()), 1, scalar);
                }
            }
        }

        //返回处理后的临时文件
        String hzName = fileName.substring(fileName.lastIndexOf("."));
        fileName = UUID.randomUUID() + hzName;
        String tmpPath = System.getProperty("java.io.tmpdir");
        String finalPath = tmpPath + fileName;
        opencv_imgcodecs.imwrite(finalPath, resultImage);
        return new File(finalPath);
    }

处理后的结果

人脸识别java开发源码 人脸识别java开源框架_scala_03


使用libfacedetection库进行人脸检测时相应速度平均只有200毫秒,相较于使用JNA自带的人脸检测,执行效率有很明显的提升。