Java Spring Boot与React结合实现OpenCV图片处理

释放双眼,带上耳机,听听看~!
本文介绍了使用Java Spring Boot和React结合实现OpenCV图片处理的方法,包括配置OpenCV库、导入相应xml处理文件和Jar包以及处理图片的具体步骤。适合对Java和React开发、图像处理感兴趣的开发者阅读。

技术栈

前端: React+face-api

后端: Java+Spring Boot+OpenCV

前言

  • 本文是调研阶段代码未经处理,但是结果是成功的,只不过使用时按照项目逻辑更改代码就好了
  • OpenCV的Jar包在Maven库中没有,需要自行去官网下载
  • react使用webpeak构建工具,如果使用Vite导入model文件容易出现问题

效果图

Java Spring Boot与React结合实现OpenCV图片处理

后端逻辑

1、配置OpenCV库

1:新建一个project项目,在resources包下创建lib(OpenCV存放位置)和static(xml处理文件存放位置)文件夹,

OpenCV下载位置———-opencv.org/releases/

XML处理文件下载位置
github.com/opencv/open…

Java Spring Boot与React结合实现OpenCV图片处理

2:创建启动类和接口文件

Java Spring Boot与React结合实现OpenCV图片处理

3:配置yml文件,解除上传文件限制

Java Spring Boot与React结合实现OpenCV图片处理

2、导入相应xml处理文件和Jar包

pom文件导入

<dependency>
    <groupId>org</groupId>
    <artifactId>opencv</artifactId>
    <version>4.8.1</version>
    <scope>system</scope>
    <systemPath>${project.basedir}srcmainresourceslibopencv-481.jar</systemPath>
</dependency>

MyImage文件初始化以及引入

static CascadeClassifier faceDetector;
static {
    // 加载OpenCV本地库

    System.setProperty("java.awt.headless", "false");
    URL url = ClassLoader.getSystemResource("lib/opencv_java481.dll");
    System.load(url.getPath());
    String classifierPath = "C:Usersliu35Desktophaarcascade_frontalface_alt.xml";
    faceDetector = new CascadeClassifier(classifierPath);
}

3、处理图片

(1)灰度化图片

public static Mat conv_Mat(String img) {
    // 读取图像
    Mat mat1 = Imgcodecs.imread(img);
    if (mat1.empty()) {
        System.err.println("Image not loaded properly.");
        return null; // 或者抛出异常
    }
    Mat mat2 = new Mat();
    // 灰度化:将图像从一种颜色空间转换为另一种颜色空间
    Imgproc.cvtColor(mat1, mat2, Imgproc.COLOR_BGR2GRAY);
    // 探测人脸:检测到的对象作为矩形列表返回
    MatOfRect faceDetections = new MatOfRect();
    List<Rect> faces = faceDetections.toList();
    for (Rect face : faces) {
        System.out.println(face.x+"----"+face.y+"----"+face.width+"----"+face.height);
    }
    if (faces.isEmpty()) {
        System.out.println("No face detected.");
        // 可以考虑返回原图、空白图或null,并在后续使用时妥善处理
        // 这里以返回原图为例
        System.out.println(mat2);
        return mat2;
    }
    Rect rect = faces.get(0); // 假设只取第一个人脸
    Mat face = new Mat(mat1, rect);
    return face;
}

(2)人脸识别比对

public static double faceRecognitionComparison(String image1, String image2) {
    Mat mat1 = conv_Mat(image1);
    Mat mat2 = conv_Mat(image2);
    Mat mat3 = new Mat();
    Mat mat4 = new Mat();
    // 颜色范围
    MatOfFloat ranges = new MatOfFloat(0f, 256f);
    // 直方图大小, 越大匹配越精确 (越慢)
    MatOfInt histSize = new MatOfInt(1000);

    Imgproc.calcHist(Arrays.asList(mat1), new MatOfInt(0), new Mat(), mat3, histSize, ranges);
    Imgproc.calcHist(Arrays.asList(mat2), new MatOfInt(0), new Mat(), mat4, histSize, ranges);

    // 比较两个密集或两个稀疏直方图
    return Imgproc.compareHist(mat3, mat4, Imgproc.CV_COMP_CORREL);
}

(3)主执行函数

public static void main(String[] args) {
     // 比对图片的绝对地址
    String path="C:Usersliu35Desktopsucai3.jpg";
    String path1="C:Usersliu35Desktopsucai2.jpg";

    double comparison = faceRecognitionComparison(path, path1);
    System.out.println("对比结果:" + comparison);
    if (comparison > 0.80) {
        System.out.println("人脸匹配成功");
    } else {
        System.out.println("人脸不匹配识别");
    }
}

至此就可以执行main函数,就可以得到人脸比对结果。

4、处理视频

(1)导入文件

static CascadeClassifier faceDetector;
static {
    // 加载OpenCV本地库
    System.setProperty("java.awt.headless", "false");
    URL url = ClassLoader.getSystemResource("lib/opencv_java481.dll");
    System.load(url.getPath());
    String classifierPath = "C:Usersliu35Desktophaarcascade_frontalface_alt.xml";
    faceDetector = new CascadeClassifier(classifierPath);
}

(3)从视频帧中识别人脸

 public static Mat getFace(Mat image) {
        MatOfRect face = new MatOfRect();
        // 检测输入图像中不同大小的对象。检测到的对象作为矩形列表返回。
        faceDetector.detectMultiScale(image, face);
        Rect[] rects = face.toArray();
        if (rects.length > 0 && Math.random() * 10 > 8) {
            System.out.println("识别人脸个数: " + rects.length);
//            Imgcodecs.imwrite("C:Usersliu35Desktopsucai" + UUID.randomUUID() + ".png", image);
        }

        if (rects != null && rects.length >= 1) {
            // 为每张识别到的人脸画一个圈
            for (int i = 0; i < rects.length; i++) {
                /**
                 * 绘制一个简单的、粗的或填充的直角矩形
                 *
                 * img 图像
                 * pt1 - 矩形的顶点
                 * pt2 - 与 pt1 相对的矩形的顶点
                 * color – 矩形颜色或亮度(灰度图像)意味着该函数必须绘制一个填充的矩形。
                 */
                Imgproc.rectangle(image, new Point(rects[i].x, rects[i].y), new Point(rects[i].x + rects[i].width, rects[i].y + rects[i].height), new Scalar(0, 255, 0));
                /**
                 * 绘制一个文本字符串,放在识别人脸框上
                 *
                 * img -- 图像
                 * text -- 要绘制的文本字符串
                 * org – 图像中文本字符串的左下角
                 * fontFace – 字体类型,请参阅#HersheyFonts
                 * fontScale – 字体比例因子乘以特定字体的基本大小
                 * color - 文本颜色
                 * thickness ——用于绘制文本的线条粗细
                 * lineType – 线型
                 * bottomLeftOrigin – 当为 true 时,图像数据原点位于左下角。否则,它位于左上角
                 */
                Imgproc.putText(image, "test", new Point(rects[i].x, rects[i].y), Imgproc.FONT_HERSHEY_SCRIPT_SIMPLEX, 1.0, new Scalar(0, 255, 0), 1, Imgproc.LINE_AA, false);
            }
        }
        return image;
    }

(4) 灰度化图片

public static Mat conv_Mat(String img) {
    // 读取图像
    Mat mat1 = Imgcodecs.imread(img);
    if (mat1.empty()) {
        System.err.println("Image not loaded properly.");
        return null; // 或者抛出异常
    }
    Mat mat2 = new Mat();
    // 灰度化:将图像从一种颜色空间转换为另一种颜色空间
    Imgproc.cvtColor(mat1, mat2, Imgproc.COLOR_BGR2GRAY);
    // 探测人脸:检测到的对象作为矩形列表返回
    MatOfRect faceDetections = new MatOfRect();
    List<Rect> faces = faceDetections.toList();
    for (Rect face : faces) {
        System.out.println(face.x+"----"+face.y+"----"+face.width+"----"+face.height);
    }
    if (faces.isEmpty()) {
        System.out.println("No face detected.");
        // 可以考虑返回原图、空白图或null,并在后续使用时妥善处理
        // 这里以返回原图为例
        System.out.println(mat2);
        return mat2;
    }
    Rect rect = faces.get(0); // 假设只取第一个人脸
    Mat face = new Mat(mat1, rect);
    return face;
}

(5)比较直方图

public static double faceRecognitionComparison(Mat mat1, String image2) {
    Mat mat2 = conv_Mat(image2);
    Mat mat3 = new Mat();
    Mat mat4 = new Mat();
    // 颜色范围
    MatOfFloat ranges = new MatOfFloat(0f, 256f);
    // 直方图大小, 越大匹配越精确 (越慢)
    MatOfInt histSize = new MatOfInt(100000);
    Imgproc.calcHist(Arrays.asList(mat1), new MatOfInt(0), new Mat(), mat3, histSize, ranges);
    Imgproc.calcHist(Arrays.asList(mat2), new MatOfInt(0), new Mat(), mat4, histSize, ranges);
    // 比较两个密集或两个稀疏直方图
    return Imgproc.compareHist(mat3, mat4, Imgproc.CV_COMP_CORREL);
}

(6) 识别人脸

public static void videoFaceRecognition() {
    // 读取视频文件
    VideoCapture capture = new VideoCapture();
    String path="C:Usersliu35Desktopsucaivideo.webm";
    capture.open(path);
    if (!capture.isOpened()) {
        throw new RuntimeException("读取视频文件失败");
    }
    Mat video = new Mat();
    int index = 0;
    while (capture.isOpened()) {
        // 抓取、解码并返回下一个视频帧写入Mat对象中
        capture.read(video);
        // 显示从视频中识别的人脸图像

        Mat face = getFace(video);
        //比对的图片地址
        String path1="C:Usersliu35Desktopsucai2.jpg";
        double comparison = faceRecognitionComparison(face, path1);
        System.out.println("对比结果:" + comparison);
        if (comparison > 0.60) {
            System.out.println("人脸匹配成功");
        } else {
            System.out.println("人脸不匹配识别");
        }
        return;
    }
}

执行main

public static void main(String[] args) {
    videoFaceRecognition();
}

5、逻辑书写

艺术已成,接下来搭建接口用于前后端操作
代码和上文基本一致,换一种执行方式罢了,所以我就直接展示全部代码

@CrossOrigin(origins = "*", maxAge = 3600)
@RestController
public class face {
    static CascadeClassifier faceDetector;
    static {
        // 加载OpenCV本地库

        System.setProperty("java.awt.headless", "false");
        URL url = ClassLoader.getSystemResource("lib/opencv_java481.dll");
        System.load(url.getPath());
//        String classifierPath = "C:Usersliu35Desktophaarcascade_frontalface_alt.xml";
//        faceDetector = new CascadeClassifier(classifierPath);
        // 加载XML分类器文件
        InputStream is = face.class.getClassLoader().getResourceAsStream("static/haarcascade_frontalface_alt.xml");
        File tempFile = new File(System.getProperty("java.io.tmpdir"), "haarcascade_frontalface_alt.xml");
        try (FileOutputStream fos = new FileOutputStream(tempFile)) {
            byte[] buffer = new byte[1024];
            int bytesRead;
            while ((bytesRead = is.read(buffer)) != -1) {
                fos.write(buffer, 0, bytesRead);
            }
        } catch (IOException e) {
            e.printStackTrace();
            // 处理异常
        }
        String classifierPath = tempFile.getAbsolutePath();
        faceDetector = new CascadeClassifier(classifierPath);
    }
    @PostMapping("/login")
    public ResponseEntity<?> faceLogin(@RequestBody MultipartFile image )throws IOException {
        if (!image.isEmpty() ) {
            // 将MultipartFile转换为临时文件
            File tempFile = convertToFile(image);
            try {
                // 使用临时文件的路径进行处理
                double comparison = faceRecognitionComparison(tempFile.getAbsolutePath(),
                        "C:Usersliu35Desktopsucai3.jpg");
                System.out.println("对比结果:" + comparison);
                if (comparison > 0.70) {
                    System.out.println("人脸匹配成功");
                    HashMap<String, Object> obj = new HashMap<>();
                    obj.put("token","eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJyb2xlIjoidXNlciIsIm5hbWUiOiJiaWFvIiwiZXhwIjoxNzE1MzMxOTg5fQ.aS9y7bxC7-7xroLscn9xa--OcmlbQAitobx2kwC3C8c");
                    obj.put("name","Liu Biao");
                    obj.put("id","1");
                    return ResponseEntity.ok(obj);

                } else {
                    System.out.println("人脸不匹配识别");
                    return new ResponseEntity<>(HttpStatus.UNAUTHORIZED);

                }
            } finally {
                // 清理:删除临时文件(根据需要,也可以选择保留)
                Files.deleteIfExists(tempFile.toPath());
            }
        } else {
            return ResponseEntity.badRequest().body("No image file provided.");
        }
    }
    private File convertToFile(MultipartFile file) throws IOException {
        File tempFile = Files.createTempFile("temp", file.getOriginalFilename()).toFile();
        file.transferTo(tempFile);
        return tempFile;
    }
    /**
     * 人脸识别比对
     */
    public static double faceRecognitionComparison(String image1, String image2) {
        Mat mat1 = conv_Mat(image1);
        Mat mat2 = conv_Mat(image2);
        Mat mat3 = new Mat();
        Mat mat4 = new Mat();
        // 颜色范围
        MatOfFloat ranges = new MatOfFloat(0f, 256f);
        // 直方图大小, 越大匹配越精确 (越慢)
        MatOfInt histSize = new MatOfInt(100000);
        Imgproc.calcHist(Arrays.asList(mat1), new MatOfInt(0), new Mat(), mat3, histSize, ranges);
        Imgproc.calcHist(Arrays.asList(mat2), new MatOfInt(0), new Mat(), mat4, histSize, ranges);
        // 比较两个密集或两个稀疏直方图
        return Imgproc.compareHist(mat3, mat4, Imgproc.CV_COMP_CORREL);
    }
    /**
     * 灰度化人脸
     */
    public static Mat conv_Mat(String img) {
        // 读取图像
        Mat mat1 = Imgcodecs.imread(img);
        if (mat1.empty()) {
            System.err.println("Image not loaded properly.");
            return null; // 或者抛出异常
        }
        Mat mat2 = new Mat();
        // 灰度化:将图像从一种颜色空间转换为另一种颜色空间
        Imgproc.cvtColor(mat1, mat2, Imgproc.COLOR_BGR2GRAY);
        // 探测人脸:检测到的对象作为矩形列表返回
        MatOfRect faceDetections = new MatOfRect();
        List<Rect> faces = faceDetections.toList();
        for (Rect face : faces) {
            System.out.println(face.x+"----"+face.y+"----"+face.width+"----"+face.height);
        }
        if (faces.isEmpty()) {
            System.out.println("No face detected.");
            // 可以考虑返回原图、空白图或null,并在后续使用时妥善处理
            // 这里以返回原图为例
            System.out.println(mat2);
            return mat2;
        }
        Rect rect = faces.get(0); // 假设只取第一个人脸
        Mat face = new Mat(mat1, rect);
        return face;
    }
}

前端逻辑

1、搭建框架和静态文件

记得安装face-api

    "face-api.js": "^0.22.2",

Java Spring Boot与React结合实现OpenCV图片处理

我这边使用的creat-react-app命令搭建的,然后把下载的face-api的处理文件放在public/models中

下载地址 github.com/justadudewh…

引入处理文件和定义状态

  const navigate = useNavigate();
  const videoRef = useRef(null);
  const canvasRef = useRef(null);
  const [isStreaming, setIsStreaming] = useState(false);
  const [isModelLoaded, setIsModelLoaded] = useState(false);
  const [faceResult, setFaceResult] = useState(false);
  // 添加 timeoutId 状态
  const [timeoutId, setTimeoutId] = useState(null);
  useEffect(() => {
    try {
      Promise.all([
        faceapi.nets.tinyFaceDetector.loadFromUri("/models"),
        faceapi.nets.faceLandmark68Net.loadFromUri("/models"),
        faceapi.nets.faceRecognitionNet.loadFromUri("/models"),
      ])
        .then(() => {
          setIsModelLoaded(true);
        })
        .catch((err) => {
          console.error(err);
        });
    } catch {}
  }, []);

2、书写静态页面

外页面自定义样式就行,我这里直接展示摄像头处理页面的代码

<div>
  <div>
    <video autoPlay muted ref={videoRef} />
    <canvas className="canvas" ref={canvasRef} />
  </div>
  {!isStreaming && !isModelLoaded && <p>Loading models...</p>}
</div>

3、调用摄像头并截取画面图像

// 访问摄像头
  useEffect(() => {
    if (!isModelLoaded) return;

    navigator.mediaDevices
      .getUserMedia({ video: true })
      .then((stream) => {
        if (videoRef.current) {
          // 添加这个检查
          videoRef.current.srcObject = stream;
          setIsStreaming(true);
        } else {
          console.warn("videoRef is not ready yet");
        }
      })
      .catch((err) => {
        console.error("Something went wrong!");
      });

    return () => {
      if (videoRef.current) {
        videoRef.current.srcObject.getTracks().forEach((track) => track.stop());
      }
    };
  }, [isModelLoaded]);

4、后端验证并保存token

// 捕获图片并发送
  const captureAndSendImage = async () => {
    if (!isStreaming) return;

    const canvas = canvasRef.current;
    const context = canvas.getContext("2d");

    // 设置canvas大小与视频相同
    canvas.width = videoRef.current.videoWidth;
    canvas.height = videoRef.current.videoHeight;

    // 绘制视频帧到canvas
    context.drawImage(videoRef.current, 0, 0, canvas.width, canvas.height);

    // 转换canvas为图片数据
    canvas.toBlob(async (blob) => {
      // 在这里添加人脸检测逻辑(如果需要)

      // 假设我们有一个sendImageToBackend函数来发送图片到后端
      // sendImageToBackend(blob);

      // 示例:使用fetch API发送图片(作为FormData)
      const formData = new FormData();
      formData.append("image", blob);

      fetch("http://localhost:8080/login", {
        method: "POST",
        body: formData,
      })
        .then((response) => response.json())
        .then((data) => {
          console.log("Response from server:", data);
          setFaceResult(true);

          // 停止视频流
          if (videoRef.current && videoRef.current.srcObject) {
            videoRef.current.srcObject
              .getTracks()
              .forEach((track) => track.stop());
          }

          // 清理定时器
          if (timeoutId) {
            clearTimeout(timeoutId);
            setTimeoutId(null); // 清理后重置状态
          }
          // setIsStreaming
          navigate("/charging");
          return data;
        })
        .catch((error) => {
          // 停止视频流
          if (videoRef.current && videoRef.current.srcObject) {
            videoRef.current.srcObject
              .getTracks()
              .forEach((track) => track.stop());
          }

          console.error("Error:", error);
          navigate("/login");
        });
    });
  };

5、写一个定时器方便看效果

  useEffect(() => {
    let id;
    if (isStreaming) {
      id = setTimeout(() => {
        captureAndSendImage();
      }, 1000);
    }
    setTimeoutId(id); // 存储定时器ID到状态中

    return () => {
      if (id) {
        clearTimeout(id);
      }
    };
  }, [isStreaming]);

结语

本文是对比图像相似图,也可以实现对象人像面部特征哦!总之调研还算顺利,可以正常使用,只不过逻辑要运用到项目中是不行的,可以自行处理相应的项目逻辑实现同样效果。

本网站的内容主要来自互联网上的各种资源,仅供参考和信息分享之用,不代表本网站拥有相关版权或知识产权。如您认为内容侵犯您的权益,请联系我们,我们将尽快采取行动,包括删除或更正。
AI教程

JavaScript模块化数据分析应用及OpenAI API实战项目

2024-6-27 20:21:00

AI教程

ComfyUI工作流转漂亮网页应用教程

2024-6-28 13:20:00

个人中心
购物车
优惠劵
今日签到
有新私信 私信列表
搜索