本文介绍windows系统下springboot+opencv进行对图片中的人脸进行提取,适合入门学习
参考:springboot,opencv

一、配置项目

1. 创建Springboot应用

2. 下载opencv开发资源包

下载windows下opencv开发包4.5.1点击下载,下载完成后打开exe提取资源到本地

3. 拷贝jar包,修改pom配置

  • 在springboot项目resources目录下新建lib包,将上一步提取资源目录下的\build\java\opencv-451.jar复制到lib包下

  • 修改pom文件,将该jar包添加到项目依赖中

<dependency> 
    <groupId>org.opencv</groupId> 
    <artifactId>opencv</artifactId> 
    <version>4.5.1</version> 
    <scope>system</scope> 
    <systemPath>${project.basedir}/src/main/resources/lib/opencv-451.jar</systemPath> 
</dependency>
  • 添加动态库
    在项目根目录新建文件夹dll,将opencv资源目录下的\build\java x64,x86复制到dll下

  • 添加opencv分类器
    在项目根目录新建文件夹haarcascades,将opencv资源目录下\sources\data\haarcascades中的xml文件复制到该目录
    注: 也可以使用其他分类器,如haarcascades_cuda、lbpcascades下的,效果对比参考此处

  • 最终的项目结构如下:

  • 二、代码编写

1. 人脸处理工具类FaceUtils.java源码

java

package com.demo.face;

import org.opencv.core.*;
import org.opencv.imgproc.Imgproc;
import org.opencv.objdetect.CascadeClassifier;

import java.io.File;
import java.util.Comparator;


public class FaceUtils {
    static {
        String dir = System.getProperty("user.dir").replace("/", "\\");
        String filePath = "\\dll\\" + (System.getProperty("java.vm.name").contains("64") ? "x64" : "x86") + "\\opencv_java451.dll";
        String path = dir.concat(filePath);
        File file = new File(path);
        if (!file.exists()) {
            throw new RuntimeException("找不到动态库".concat(path));
        }
        System.load(path);
        System.out.println(path.concat("  load success"));
    }

    /**
     * 检测人脸数量
     *
     * @param base64
     * @return
     */
    public static int checkFaceNo(String base64) {
        String path = System.getProperty("user.dir").concat("/haarcascades/haarcascade_frontalface_alt.xml");
        CascadeClassifier faceDetector = new CascadeClassifier(path);
        MatOfRect faceDetections = new MatOfRect();
        faceDetector.detectMultiScale(StreamUtils.base642Mat(base64), faceDetections);
        return faceDetections.toArray().length;
    }

    public static String extractMaxFace(String base64) {
        String path = System.getProperty("user.dir").concat("/haarcascades/haarcascade_frontalface_alt.xml");
        CascadeClassifier faceDetector = new CascadeClassifier(path);
        MatOfRect faceDetections = new MatOfRect();
        Mat mat = StreamUtils.base642Mat(base64);
        faceDetector.detectMultiScale(mat, faceDetections);
        if (faceDetections.toArray().length > 0) {
            Rect rect = faceDetections.toList().parallelStream().max(Comparator.comparingInt(o -> o.height * o.width)).get();
            Mat face = imageCut(mat, rect.x, rect.y, rect.width, rect.height);

            return StreamUtils.catToBase64(face);

        }


        return null;
    }

    /**
     * @param image  原始图像
     * @param posX   x坐标
     * @param posY   y坐标
     * @param width  宽度
     * @param height 高度
     * @return
     */
    private static Mat imageCut(Mat image, int posX, int posY, int width, int height) {

        // 截取的区域:参数,坐标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(width, height);
        Imgproc.resize(sub, mat, size);// 将人脸进行截图并保存
        return mat;

    }


    public static String markFace(String base64Images) {
        String path = System.getProperty("user.dir").concat("/haarcascades/haarcascade_frontalface_alt.xml");
        CascadeClassifier faceDetector = new CascadeClassifier(path);
        MatOfRect faceDetections = new MatOfRect();
        Mat mat = StreamUtils.base642Mat(base64Images);
        faceDetector.detectMultiScale(mat, faceDetections);
        if (faceDetections.toArray().length > 0) {
            for (Rect rect : faceDetections.toList()) {
                Imgproc.rectangle(mat, new Point(rect.x, rect.y), new Point(rect.x + rect.width, rect.y + rect.height), new Scalar(0, 255, 0), 3);
            }
        }
        return StreamUtils.catToBase64(mat);
    }
}

用到的StreamUtils.java源码

java

package com.demo.face;

import org.opencv.core.CvType;
import org.opencv.core.Mat;
import org.opencv.core.MatOfByte;
import org.opencv.imgcodecs.Imgcodecs;
import org.springframework.util.Base64Utils;
import sun.misc.BASE64Decoder;

import javax.imageio.ImageIO;
import java.awt.*;
import java.awt.image.BufferedImage;
import java.awt.image.DataBufferByte;
import java.io.ByteArrayInputStream;
import java.io.IOException;


public class StreamUtils {
    /**
     * 装换回编码
     *
     * @param correctMat
     * @return
     */
    public static String catToBase64(Mat correctMat) {
        return bufferToBase64(toByteArray(correctMat));
    }

    /**
     * 转换成base64编码
     *
     * @param buffer
     * @return
     */
    public static String bufferToBase64(byte[] buffer) {
        return Base64Utils.encodeToString(buffer);
    }

    /**
     * base64编码转换成字节数组
     *
     * @param base64Str
     * @return
     */
    public static byte[] base64ToByteArray(String base64Str) {
        return Base64Utils.decodeFromString(base64Str);
    }

    /**
     * base64 编码转换为 BufferedImage
     *
     * @param base64
     * @return
     */
    public static BufferedImage base64ToBufferedImage(String base64) {
        BASE64Decoder Base64 = new BASE64Decoder();
        try {
            byte[] bytes1 = Base64.decodeBuffer(base64);
            ByteArrayInputStream bais = new ByteArrayInputStream(bytes1);
            return ImageIO.read(bais);
        } catch (IOException e) {
            e.printStackTrace();
        }
        return null;
    }

    /**
     * mat转换成bufferedImage
     *
     * @param matrix
     * @return
     */
    public static byte[] toByteArray(Mat matrix) {
        MatOfByte mob = new MatOfByte();
        Imgcodecs.imencode(".jpg", matrix, mob);
        return mob.toArray();
    }

    /**
     * mat转换成bufferedImage
     *
     * @param matrix
     * @return
     */
    public static BufferedImage toBufferedImage(Mat matrix) throws IOException {
        byte[] buffer = toByteArray(matrix);
        ByteArrayInputStream bais = new ByteArrayInputStream(buffer);
        return ImageIO.read(bais);
    }

    /**
     * base64转Mat
     *
     * @param base64
     * @return
     * @throws IOException
     */
    public static Mat base642Mat(String base64) {
        return bufImg2Mat(base64ToBufferedImage(base64), BufferedImage.TYPE_3BYTE_BGR, CvType.CV_8UC3);
    }

    /**
     * BufferedImage转换成Mat
     *
     * @param original 要转换的BufferedImage
     * @param imgType  bufferedImage的类型 如 BufferedImage.TYPE_3BYTE_BGR
     * @param matType  转换成mat的type 如 CvType.CV_8UC3
     */
    public static Mat bufImg2Mat(BufferedImage original, int imgType, int matType) {
        if (original == null) {
            throw new IllegalArgumentException("original == null");
        }
        // Don't convert if it already has correct type
        if (original.getType() != imgType) {
            // Create a buffered image
            BufferedImage image = new BufferedImage(original.getWidth(), original.getHeight(), imgType);
            // Draw the image onto the new buffer
            Graphics2D g = image.createGraphics();
            try {
                g.setComposite(AlphaComposite.Src);
                g.drawImage(original, 0, 0, null);
                original = image;
            } catch (Exception e) {
                e.printStackTrace();
            } finally {
                g.dispose();
            }
        }
        byte[] pixels = ((DataBufferByte) original.getRaster().getDataBuffer()).getData();
        Mat mat = Mat.eye(original.getHeight(), original.getWidth(), matType);
        mat.put(0, 0, pixels);
        return mat;
    }
}

2. 测试controller源码

java

package com.demo.face;

import org.springframework.util.StringUtils;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;

import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.io.PrintWriter;


@RestController
@RequestMapping
public class FaceController {

    /**
     * 获取图片中人脸个数
     *
     * @param base64Images base64图片,不包含逗号之前的部分
     * @return
     */
    @PostMapping("checkFaceNumber")
    public int checkFaceNumber(String base64Images) {
        return FaceUtils.checkFaceNo(base64Images);
    }

    /**
     * 提取最大脸
     *
     * @param response
     * @param base64Images
     * @throws IOException
     */
    @PostMapping("extractMaxFace")
    public void extractMaxFace(HttpServletResponse response, String base64Images) throws IOException {
        int indexOf = base64Images.indexOf(",");
        if (indexOf > 0) {
            base64Images = base64Images.substring(indexOf + 1);
        }
        response.setContentType("application/octet-stream");
        PrintWriter writer = response.getWriter();
        base64Images = FaceUtils.extractMaxFace(base64Images);
        if (!StringUtils.hasLength(base64Images)) {
            throw new RuntimeException("没有人脸");
        }
        writer.write("data:image/png;base64,".concat(base64Images));
        writer.flush();

    }

    /**
     * 标记脸
     *
     * @param response
     * @param base64Images
     * @throws IOException
     */
    @PostMapping("markFace")
    public void markFace(HttpServletResponse response, String base64Images) throws IOException {
        int indexOf = base64Images.indexOf(",");
        if (indexOf > 0) {
            base64Images = base64Images.substring(indexOf + 1);
        }
        response.setContentType("application/octet-stream");
        PrintWriter writer = response.getWriter();
        base64Images = FaceUtils.markFace(base64Images);
        writer.write("data:image/png;base64,".concat(base64Images));
        writer.flush();

    }
}

三、最终效果

标记脸的效果如下:有框的是识别后的

                       

找了一段视频 

rtsp://wowzaec2demo.streamlock.net/vod/mp4:BigBuckBunny_115k.mp4

居然也能识别,好神奇

                  

 

 

四、其他

绘制文字,绘制线

java

public static void main(String[] args) throws IOException {

        Mat mat = Imgcodecs.imread("d:\\face\\1.jpg");

        String path = System.getProperty("user.dir").concat("/haarcascades/haarcascade_frontalface_alt.xml");
        CascadeClassifier faceDetector = new CascadeClassifier(path);
        MatOfRect faceDetections = new MatOfRect();
        faceDetector.detectMultiScale(mat, faceDetections);
        if (faceDetections.toArray().length > 0) {
            for (Rect rect : faceDetections.toList()) {
                //绘制长方形
//                Imgproc.rectangle(mat, new Point(rect.x, rect.y), new Point(rect.x + rect.width, rect.y + rect.height), new Scalar(0, 255, 0), 3);
                //绘制文字
//                Imgproc.putText(mat,"Face", new Point(rect.x, rect.y), 0,0.5,new Scalar(0, 255, 0),3);
                //绘制线
                Imgproc.line(mat, new Point(rect.x, rect.y), new Point(rect.x, rect.y + 20), new Scalar(0, 255, 0), 3);
                Imgproc.line(mat, new Point(rect.x, rect.y), new Point(rect.x + 20, rect.y), new Scalar(0, 255, 0), 3);

                Imgproc.line(mat, new Point(rect.x + rect.width, rect.y), new Point(rect.x + rect.width - 20, rect.y), new Scalar(0, 255, 0), 3);
                Imgproc.line(mat, new Point(rect.x + rect.width, rect.y), new Point(rect.x + rect.width, rect.y + 20), new Scalar(0, 255, 0), 3);

                Imgproc.line(mat, new Point(rect.x, rect.y + rect.height), new Point(rect.x, rect.y + rect.height - 20), new Scalar(0, 255, 0), 3);
                Imgproc.line(mat, new Point(rect.x, rect.y + rect.height), new Point(rect.x + 20, rect.y + rect.height), new Scalar(0, 255, 0), 3);

                Imgproc.line(mat, new Point(rect.x + rect.width, rect.y + rect.height), new Point(rect.x + rect.width, rect.y + rect.height - 20), new Scalar(0, 255, 0), 3);
                Imgproc.line(mat, new Point(rect.x + rect.width, rect.y + rect.height), new Point(rect.x + rect.width - 20, rect.y + rect.height), new Scalar(0, 255, 0), 3);

            }
        }

        Imgcodecs.imwrite("D:\\face\\1-new.jpg", mat);


    }
Logo

为开发者提供学习成长、分享交流、生态实践、资源工具等服务,帮助开发者快速成长。

更多推荐