Java版本的OpenCV提供了一个videoio包,以及一个特定的VideoCapture对象,它提供了多种方法来直接从连接的视频设备中读取矩阵对象。首先,你会看到如何从视频设备中获取一个特定大小的矩阵对象,然后将矩阵存入文件中。通过使用帧(frame),你将看到如何将之前学习到的预处理代码应用在实时获取到的图像中。
首先介绍do_still_captures函数。它的输入参数是一组需要抓取的帧、每帧间隔的时间以及从哪个camera_id读入图像。camera_id是连接到你机器的捕获设备索引。通常你会使用0,但是如果你还有其他外接设备的话,就要选择对应的camera_id。首先创建一个 VideoCapture对象,camera_id作为参数。然后创建一个空的矩阵对象,把它传入camera.read()函数来读取数据。这里的矩阵对象是你熟悉的标准OpenCV矩阵Mat,于是你也可以应用那些之前学过的变换。到目前为止,我们先把每一帧存储好,用时间戳作为文件名。完成后,你可以通过VideoCapture对象中的release函数来把相机设置回待机模式。
看看以下代码实现如下:
static void do_still_captures(int frames, int lapse, int camera_id) {
VideoCapture capture = new VideoCapture(camera_id);
capture.set(Videoio.CAP_PROP_FRAME_WIDTH, 320);
capture.set(Videoio.CAP_PROP_FRAME_HEIGHT, 240);
Mat mat = new Mat();
for (int i = 0; i < frames; i++) {
if (capture.read(mat)) {
String fileName = "D:\\ProgramFiles\\personDocument\\picture\\" + System.currentTimeMillis() + ".jpg";
Imgcodecs.imwrite(fileName, mat);
try {
Thread.sleep(lapse * 1000);
} catch (Exception e) {
e.printStackTrace();
}
}
}
capture.release();
}
写个测试方法,调用新建的函数只需填入所需参数,接下来从ID为0的设备中读取10张图片,每间隔1秒拍摄1次。代码实现:
@Test
public void test21() throws Exception {
URL url = ClassLoader.getSystemResource("lib/opencv_java440.dll");
System.load(url.getPath());
do_still_captures(10, 1, 0);
}
当执行上面代码时,会调用笔记本摄像头进行拍照。效果如下:
OpenCV的Java封装不包含将矩阵转为BufferedImage的明确方法,BufferedImage是Java的graphic包中处理图像的对象。假设你需要一个MatToBufferedImage函数来实时处理Java帧,通过把矩阵对象转换为BufferedImage,即可将它渲染为标准的Java GUI对象。让我们快速地写一个函数,将矩阵转换为标准的Java BufferedImage。代码实现:
static BufferedImage MatToBufferedImage(Mat frame) {
int type = 0;
if (frame == null) return null;
if (frame.channels() == 1)
type = BufferedImage.TYPE_BYTE_GRAY;
else if (frame.channels() == 3)
type = BufferedImage.TYPE_3BYTE_BGR;
BufferedImage image = new BufferedImage(frame.width(), frame.height(), type);
WritableRaster raster = image.getRaster();
DataBufferByte dataBufferByte = (DataBufferByte) raster.getDataBuffer();
byte[] data = dataBufferByte.getData();
frame.get(0, 0, data);
return image;
}
当你有了这段代码之后,事情就变得简单了起来。但你仍然需要另外一段代码:一个自定义的panel,它继承了Java的Panel类JPanel。这个自定义的panel,称为MatPanel,包含一个需要绘制的矩阵对象。MatPanel继承Java的JPanel类的方法是,在paint() 函数中直接调用你刚刚见过的函数:MatToBufferedImage。代码实现:
class MatPanel extends JPanel {
public Mat mat;
public void paint(Graphics g) {
g.drawImage(MatToBufferedImage(mat), 0, 0, this);
}
}
标准OpenCV包中缺少的代码已经被实现了,你可以直接创建JFrame来接收矩阵对象。最后一步是使用一段与do_still_captures函数类似的代码,但并不在几帧之后停下,你将会写一个无限循环来处理视频流。代码实现:
@Test
public void test22() throws Exception {
URL url = ClassLoader.getSystemResource("lib/opencv_java440.dll");
System.load(url.getPath());
// do_still_captures(10, 1, 0);
MatPanel matPanel = new MatPanel();
JFrame jFrame = new JFrame();
jFrame.getContentPane().add(matPanel);
jFrame.setDefaultCloseOperation(JFrame.DISPOSE_ON_CLOSE);
jFrame.setSize(320, 240);
jFrame.setVisible(true);
jFrame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
VideoCapture capture = new VideoCapture(0);
capture.set(Videoio.CAP_PROP_FRAME_WIDTH, 320);
capture.set(Videoio.CAP_PROP_FRAME_HEIGHT, 240);
Mat mat = new Mat();
while (true) {
if (capture.read(mat)) {
matPanel.mat = mat;
matPanel.repaint();
}
}
}
通过调用设备摄像头进行视频流的实时输出,效果图如下:
接下来我们通过 Canny 实时进行图像处理,。将Canny函数应用在视频读取的矩阵对象中。代码实现:
@Test
public void test23() throws Exception {
URL url = ClassLoader.getSystemResource("lib/opencv_java440.dll");
System.load(url.getPath());
// do_still_captures(10, 1, 0);
MatPanel matPanel = new MatPanel();
JFrame jFrame = new JFrame();
jFrame.getContentPane().add(matPanel);
jFrame.setDefaultCloseOperation(JFrame.DISPOSE_ON_CLOSE);
jFrame.setSize(320, 240);
jFrame.setVisible(true);
jFrame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
VideoCapture capture = new VideoCapture(0);
capture.set(Videoio.CAP_PROP_FRAME_WIDTH, 320);
capture.set(Videoio.CAP_PROP_FRAME_HEIGHT, 240);
Mat mat = new Mat();
while (true) {
if (capture.read(mat)) {
Imgproc.cvtColor(mat,mat,Imgproc.COLOR_RGB2GRAY);
Mat targer = new Mat();
Imgproc.Canny(mat,targer,100,150,3,true);
matPanel.mat = targer;
matPanel.repaint();
}
}
}
实现效果:
如有不当之处请多多指教,如对你有所帮助,请评论或点赞予以支持,谢谢!