通过ChatGPT使用JavaCV

2024-06-24 17:21:14 浏览数 (1)

最近我在自己的服务器里基于Pandora搭建了ChatGPT页面,以后只需要accessToken或者key就可以使用,不再需要再去官网玩了,省下了不少时间。

兴致勃勃的了不起,开启了ChatGPT学习(tiaojiao)之路。

这次打算基于它来进行JavaCV开发,我负责胡思乱想,ChatGPT负责实现。看看它能够给我们带来什么惊喜

JavaCV

JavaCV是对OpenCV的一种接口封装,说白了就是使用Java调用C,以往这方面,我们都是使用C 来做的,但是自从有了JavaCV之后,Java也丰富了关于这方面的玩法。具体对JavaCV感兴趣的,可以自己baidu查查。

废话少说了,直接开始tiaojiao

第一步当然就是引入Maven依赖了,这一步有手就行,不用问了。

一个是JavaCV依赖,一个是lombok依赖,因为打算使用@Slf4j

代码语言:javascript复制
<!-- javacv相关依赖,一个就够了 -->
<dependency>
    <groupId>org.bytedeco</groupId>
    <artifactId>javacv-platform</artifactId>
    <version>1.5.6</version>
</dependency>
 <dependency>
     <groupId>org.projectlombok</groupId>
     <artifactId>lombok</artifactId>
     <version>1.18.12</version>
 </dependency>

ChatGPT回答:

重点分析:

  1. 导入相关库:代码开头使用import语句导入了所需的JavaCV和OpenCV库。
  2. 创建OpenCVFrameGrabber对象:通过创建OpenCVFrameGrabber对象,我们指定要使用的摄像头设备。在示例中,使用的是默认的摄像头设备,表示为0
  3. 启动摄像头:调用grabber.start()方法来启动摄像头,准备开始捕获图像帧。
  4. 捕获和处理图像帧:使用一个无限循环,在每次迭代中调用grabber.grab()方法来捕获一帧图像。返回的帧对象可以被转换为Mat对象,以便进行进一步的图像处理和计算机视觉任务。
  5. 图像处理和计算机视觉任务:在获取到每一帧图像后,你可以在注释部分中对图像进行处理。这是一个重要的部分,你可以在这里应用各种图像处理算法或进行计算机视觉任务,例如人脸检测、目标追踪、图像分割等。你可以使用JavaCV提供的函数和类来执行这些任务。
  6. 显示图像:使用OpenCV的imshow函数显示图像,通过传递窗口名称和Mat对象来显示捕获的图像帧。waitKey函数用于等待键盘输入,其中参数表示等待时间(以毫秒为单位)。这样可以以一定的速率显示连续的图像帧。

从重点分析中我们可以看出,获取摄像头的关键在于OpenCVFrameGrabber对象,本机摄像头一般是0。而展示画面则是使用openCV的imshow方法。

在这里你会发现,ChatGPT虽然只给了调用摄像头的代码,但是关于后续能够进行的操作,它通过注释告诉了我们,可以再哪一步进行处理。

添加分辨率

接下来,我们根据ChatGPT联系上下文的功能,为OpenCVFrameGrabber对象添加分辨率来丰富这项功能。

学过Java的都知道,Java有一部分内容是Swing,接下来我们让它对此功能添加JFrame框,让我们通过JFrame来看到拉去的摄像头。

使用Swing

而JavaCV对此包装了一个CanvasFrame类来展示画面

对此,ChatGPT使用CanvasFrame来创建一个窗口并设置其尺寸。在每次循环中,调用frame.showImage(mat)来显示从摄像头捕获到的图像帧。使用CanvasFrame可以简化图像显示,因为CanvasFrame提供了内置的绘图画布。它还提供了一些附加功能,如调整窗口大小、关闭事件处理等

增加水印

并且,我们还可以通过JavaCV来给视频画面添加一些水印,比如当前时间。

使用opencv_core.Point对象指定水印的位置

由于需要对Frame对象进行操作,所以我们需要使用OpenCVFrameConverter对象,它能帮助我们把Frame对象转成OpenCV的IplImage对象,它是OpenCV中的图像数据结构,包含着图像像素和相关元数据,很多人脸识别的功能都是基于它来实现的。

简单加入水印,最后的结果就是下图的这样。

资源释放

最后,学过IO的都知道,最重要的一步就是资源的释放

我们这次的代码中使用了OpenCVFrameGrabberCanvasFrame对象,最后的肯定就是对它们两个对象进行资源的释放。

代码语言:javascript复制
log.info("输出结束");
if (null!= previewCanvas) {
    previewCanvas.dispose();
}
if (null!=grabber) {
    try {
        grabber.close();
    } catch (Exception exception) {
        log.error("close grabber error", exception);
    }
}

到此,一份拉取摄像头的功能就结束了。

代码分享
代码语言:javascript复制
package com.gateway.link.cv.local;

import lombok.extern.slf4j.Slf4j;
import org.bytedeco.ffmpeg.global.avutil;
import org.bytedeco.javacv.*;
import org.bytedeco.opencv.global.opencv_imgproc;
import org.bytedeco.opencv.opencv_core.IplImage;
import org.bytedeco.opencv.opencv_core.Mat;
import org.bytedeco.opencv.opencv_core.Scalar;

import javax.swing.*;
import java.text.SimpleDateFormat;
import java.util.Date;

import static org.bytedeco.opencv.global.opencv_core.cvFlip;

/**
 * 本地调用笔记本摄像头代码
 */
@Slf4j
public class localVideo {

    /**
     * 输出帧率
     */
    private static final double frameRate = 30;
    /**
     * 摄像头视频的宽
     */
    private static final int cameraImageWidth = 1280;

    /**
     * 摄像头视频的高
     */
    private static final int cameraImageHeight = 720;

    /**
     * 转换器
     */
    private static final OpenCVFrameConverter.ToIplImage openCVConverter = new OpenCVFrameConverter.ToIplImage();



    public static void main(String[] args) throws Exception
    {
        long startTime = System.currentTimeMillis();

        // 设置ffmepg日志级别
        avutil.av_log_set_level(avutil.AV_LOG_INFO);
        FFmpegLogCallback.set();
        //获取本地摄像头,一般是0
        OpenCVFrameGrabber grabber = new OpenCVFrameGrabber(0);
        // 摄像头有可能有多个分辨率,这里指定
        // 可以指定宽高,也可以不指定反而调用grabber.getImageWidth去获取,
        grabber.setImageWidth(1280);
        grabber.setImageHeight(720);

        // 开启抓取器
        grabber.start();

        //创建一个Frame窗口
        CanvasFrame previewCanvas = new CanvasFrame("摄像头预览", CanvasFrame.getDefaultGamma() / grabber.getGamma());
        previewCanvas.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
        previewCanvas.setAlwaysOnTop(true);


        log.info("初始化完成,耗时[{}]毫秒,帧率[{}],图像宽度[{}],图像高度[{}]",
                System.currentTimeMillis()-startTime,
                frameRate,
                cameraImageWidth,
                cameraImageHeight);

        // 添加水印时用到的时间工具
        SimpleDateFormat simpleDateFormat = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");

        //控制视频拉取时间,也可以while(true)永久播放
        long endTime = System.currentTimeMillis()   1000L * 1000;

        // 两帧输出之间的间隔时间,默认是1000除以帧率
        int interVal = getInterval();

        // 水印在图片上的位置
        org.bytedeco.opencv.opencv_core.Point point = new org.bytedeco.opencv.opencv_core.Point(15, 35);

        Frame captureFrame;
        IplImage img;
        Mat mat;

        // 超过指定时间就结束循环
        while (System.currentTimeMillis()<endTime) {
            // 取一帧
            captureFrame = grabber.grab();

            if (null==captureFrame) {
                log.error("帧对象为空");
                break;
            }

            // 将帧对象转为IplImage对象
            img = openCVConverter.convert(captureFrame);

            // 镜像翻转
            cvFlip(img, img, 1);

            // IplImage转mat
            mat = new Mat(img);

            // 在图片上添加水印,水印内容是当前时间,位置是左上角
            opencv_imgproc.putText(mat,
                    simpleDateFormat.format(new Date()),
                    point,
                    opencv_imgproc.CV_FONT_VECTOR0,
                    0.8,
                    new Scalar(0, 200, 255, 0),
                    1,
                    0,
                    false);

            // 输出
            previewCanvas.showImage(openCVConverter.convert(mat));
            // 适当间隔,让肉眼感受不到闪屏即可
            if(interVal>0) {
                Thread.sleep(interVal);
            }
        }

        log.info("输出结束");
        if (null!= previewCanvas) {
            previewCanvas.dispose();
        }
        if (null!=grabber) {
            try {
                grabber.close();
            } catch (Exception exception) {
                log.error("close grabber error", exception);
            }
        }
    }


    /**
     * 两帧之间的间隔时间
     * @return
     */
    protected static int getInterval() {
        // 假设一秒钟30帧,那么两帧间隔就是(1000/30)毫秒
        return (int)(1000/ frameRate );
    }

}

关于代码细节,简单的修改了一下,复制粘贴就可以直接使用。

结束语

这次的学习到此告一段落,下一次,我打算通过JavaCV来做到人脸部位的识别,也是基于ChatGPT来实现。

觉得有用可以关注我

0 人点赞