Java版人脸检测详解下篇:编码

<?xml version="1.0" encoding="UTF-8"?>

<project xmlns=“http://maven.apache.org/POM/4.0.0”

xmlns:xsi=“http://www.w3.org/2001/XMLSchema-instance”

xsi:schemaLocation=“http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd”>

javacv-tutorials

com.bolingcavalry

1.0-SNAPSHOT

4.0.0

face-detect-demo

jar

org.springframework.boot

spring-boot-dependencies

${springboot.version}

pom

import

org.springframework.boot

spring-boot-starter-freemarker

org.springframework.boot

spring-boot-starter-web

org.projectlombok

lombok

org.springframework.boot

spring-boot-starter-test

test

org.bytedeco

javacv-platform

org.bytedeco

javacv

org.bytedeco

javacpp

org.bytedeco.javacpp-presets

ffmpeg-platform

org.bytedeco.javacpp-presets

ffmpeg

org.springframework.boot

spring-boot-maven-plugin

com.bolingcavalry.facedetect.FaceDetectApplication

repackage

  • 配置文件如下,要重点关注前段模板、文件上传大小、模型文件目录等配置:

FreeMarker 配置

spring.freemarker.allow-request-override=false

#Enable template caching.启用模板缓存。

spring.freemarker.cache=false

spring.freemarker.check-template-location=true

spring.freemarker.charset=UTF-8

spring.freemarker.content-type=text/html

spring.freemarker.expose-request-attributes=false

spring.freemarker.expose-session-attributes=false

spring.freemarker.expose-spring-macro-helpers=false

#设置面板后缀

spring.freemarker.suffix=.ftl

设置单个文件最大内存

spring.servlet.multipart.max-file-size=100MB

设置所有文件最大内存

spring.servlet.multipart.max-request-size=1000MB

自定义文件上传路径

web.upload-path=/app/images

模型路径

opencv.model-path=/app/model/haarcascade_frontalface_default.xml

  • 前端页面文件只有一个index.ftl,请原谅欣宸不入流的前端水平,前端只有一个页面,可以提交页面,同时也是展示处理结果的页面:
图片上传Demo

图片上传Demo

选择检测文件:

周围检测数量:

<#–判断是否上传文件–>

<#if msg??>

${msg}

<#else >

${msg!(“文件未上传”)}

</#if>

<#–显示图片,一定要在img中的src发请求给controller,否则直接跳转是乱码–>

<#if fileName??>

<#––>

<#else>

<#––>

</#if>

  • 再来看后台代码,先是最常见的应用启动类:

package com.bolingcavalry.facedetect;

import org.springframework.boot.SpringApplication;

import org.springframework.boot.autoconfigure.SpringBootApplication;

@SpringBootApplication

public class FaceDetectApplication {

public static void main(String[] args) {

SpringApplication.run(FaceDetectApplication.class, args);

}

}

  • 前端上传图片后,后端要做哪些处理呢?先不贴代码,咱们把后端要做的事情捋一遍,如下图:

在这里插入图片描述

  • 接下来是最核心的业务类UploadController.java,web接口和业务逻辑处理都在这里面,是按照上图的流程顺序执行的,有几处要注意的地方稍后会提到:

package com.bolingcavalry.facedetect.controller;

import lombok.extern.slf4j.Slf4j;

import org.springframework.beans.factory.annotation.Autowired;

import org.springframework.beans.factory.annotation.Value;

import org.springframework.core.io.ResourceLoader;

import org.springframework.http.ResponseEntity;

import org.springframework.stereotype.Controller;

import org.springframework.web.bind.annotation.RequestMapping;

import org.springframework.web.bind.annotation.RequestParam;

import org.springframework.web.multipart.MultipartFile;

import java.io.File;

import java.io.IOException;

import java.util.Map;

import org.opencv.core.*;

import org.opencv.imgcodecs.Imgcodecs;

import org.opencv.imgproc.Imgproc;

import org.opencv.objdetect.CascadeClassifier;

import java.util.UUID;

import static org.bytedeco.javacpp.opencv_objdetect.CV_HAAR_DO_CANNY_PRUNING;

@Controller

@Slf4j

public class UploadController {

static {

// 加载 动态链接库

System.loadLibrary(Core.NATIVE_LIBRARY_NAME);

}

private final ResourceLoader resourceLoader;

@Autowired

public UploadController(ResourceLoader resourceLoader) {

this.resourceLoader = resourceLoader;

}

@Value("${web.upload-path}")

private String uploadPath;

@Value("${opencv.model-path}")

private String modelPath;

/**

  • 跳转到文件上传页面

  • @return

*/

@RequestMapping(“index”)

public String toUpload(){

return “index”;

}

/**

  • 上次文件到指定目录

  • @param file 文件

  • @param path 文件存放路径

  • @param fileName 源文件名

  • @return

*/

private static boolean upload(MultipartFile file, String path, String fileName){

//使用原文件名

String realPath = path + “/” + fileName;

File dest = new File(realPath);

//判断文件父目录是否存在

if(!dest.getParentFile().exists()){

dest.getParentFile().mkdir();

}

try {

//保存文件

file.transferTo(dest);

return true;

} catch (IllegalStateException e) {

// TODO Auto-generated catch block

e.printStackTrace();

return false;

} catch (IOException e) {

// TODO Auto-generated catch block

e.printStackTrace();

return false;

}

}

/**

  • @param file 要上传的文件

  • @return

*/

@RequestMapping(“fileUpload”)

public String upload(@RequestParam(“fileName”) MultipartFile file, @RequestParam(“minneighbors”) int minneighbors, Map<String, Object> map){

log.info(“file [{}], size [{}], minneighbors [{}]”, file.getOriginalFilename(), file.getSize(), minneighbors);

String originalFileName = file.getOriginalFilename();

if (!upload(file, uploadPath, originalFileName)){

map.put(“msg”, “上传失败!”);

return “forward:/index”;

}

String realPath = uploadPath + “/” + originalFileName;

Mat srcImg = Imgcodecs.imread(realPath);

// 目标灰色图像

Mat dstGrayImg = new Mat();

// 转换灰色

Imgproc.c

【一线大厂Java面试题解析+后端开发学习笔记+最新架构讲解视频+实战项目源码讲义】

浏览器打开:qq.cn.hn/FTf 开源分享

vtColor(srcImg, dstGrayImg, Imgproc.COLOR_BGR2GRAY);

// OpenCv人脸识别分类器

CascadeClassifier classifier = new CascadeClassifier(modelPath);

// 用来存放人脸矩形

MatOfRect faceRect = new MatOfRect();

// 特征检测点的最小尺寸

Size minSize = new Size(32, 32);

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

double scaleFactor = 1.2;

// 执行人脸检测

classifier.detectMultiScale(dstGrayImg, faceRect, scaleFactor, minneighbors, CV_HAAR_DO_CANNY_PRUNING, minSize);

//遍历矩形,画到原图上面

// 定义绘制颜色

Scalar color = new Scalar(0, 0, 255);

Rect[] rects = faceRect.toArray();

// 没检测到

if (null==rects || rects.length<1) {

// 显示图片

map.put(“msg”, “未检测到人脸”);

// 文件名

map.put(“fileName”, originalFileName);

return “forward:/index”;

}

// 逐个处理

for(Rect rect: rects) {

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);

}

// 添加人脸框之后的图片的名字

String newFileName = UUID.randomUUID().toString() + “.png”;

// 保存

Imgcodecs.imwrite(uploadPath + “/” + newFileName, srcImg);

// 显示图片

map.put(“msg”, “一共检测到” + rects.length + “个人脸”);

// 文件名

map.put(“fileName”, newFileName);

return “forward:/index”;

}

/**

  • 显示单张图片

  • @return

*/

@RequestMapping(“show”)

public ResponseEntity showPhotos(String fileName){

if (null==fileName) {

return ResponseEntity.notFound().build();

}

try {

// 由于是读取本机的文件,file是一定要加上的, path是在application配置文件中的路径

return ResponseEntity.ok(resourceLoader.getResource(“file:” + uploadPath + “/” + fileName));

} catch (Exception e) {

return ResponseEntity.notFound().build();

}

}

}

  • UploadController.java的代码,有以下几处要关注:
  1. 在静态方法中通过System.loadLibrary加载本地库函,实际开发过程中,这里是最容易报错的地方,一定要确保-Djava.library.path参数配置的路径中的本地库是正常可用的,前文制作的基础镜像中已经准比好了这些本地库,因此只要确保-Djava.library.path参数配置正确即可,这个配置在稍后的Dockerfile中会提到

  2. public String upload方法是处理人脸检测的代码入口,内部按照前面分析的流程顺序执行

  3. new CascadeClassifier(modelPath)是根据指定的模型来实例化分类器,模型文件是从GitHub下载的,opencv官方提前训练好的模型,地址是:https://github.com/opencv/opencv/tree/master/data/haarcascades

  4. 看似神奇的人脸检测功能,实际上只需一行代码classifier.detectMultiScale,就能得到每个人脸在原图中的矩形位置,接下来,咱们只要按照位置在原图上添加矩形框即可

  • 现在代码已经写完了,接下来将其做成docker镜像

docker镜像制作

  • 首先是编写Dockerfile:

基础镜像集成了openjdk8和opencv3.4.3

FROM bolingcavalry/opencv3.4.3:0.0.3

创建目录


版权声明:本文为m0_63174618原创文章,遵循CC 4.0 BY-SA版权协议,转载请附上原文出处链接和本声明。