最近我在自己的服务器里基于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回答:
重点分析:
- 导入相关库:代码开头使用
import
语句导入了所需的JavaCV和OpenCV库。 - 创建
OpenCVFrameGrabber
对象:通过创建OpenCVFrameGrabber
对象,我们指定要使用的摄像头设备。在示例中,使用的是默认的摄像头设备,表示为0
。 - 启动摄像头:调用
grabber.start()
方法来启动摄像头,准备开始捕获图像帧。 - 捕获和处理图像帧:使用一个无限循环,在每次迭代中调用
grabber.grab()
方法来捕获一帧图像。返回的帧对象可以被转换为Mat
对象,以便进行进一步的图像处理和计算机视觉任务。 - 图像处理和计算机视觉任务:在获取到每一帧图像后,你可以在注释部分中对图像进行处理。这是一个重要的部分,你可以在这里应用各种图像处理算法或进行计算机视觉任务,例如人脸检测、目标追踪、图像分割等。你可以使用JavaCV提供的函数和类来执行这些任务。
- 显示图像:使用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的都知道,最重要的一步就是资源的释放
我们这次的代码中使用了OpenCVFrameGrabber
和CanvasFrame
对象,最后的肯定就是对它们两个对象进行资源的释放。
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来实现。
觉得有用可以关注我