杰瑞科技汇

OpenCV Java摄像头如何开启?

  1. 环境准备:安装 JDK、Maven 和 OpenCV Java 库。
  2. 创建 Maven 项目:在 IDE(如 IntelliJ IDEA 或 Eclipse)中创建一个标准的 Maven 项目。
  3. 编写代码:编写 Java 代码来启动摄像头、捕获视频流并显示在窗口中。
  4. 运行和测试:运行程序并查看效果。
  5. 进阶功能:介绍如何拍照、保存图片以及释放资源。

第 1 步:环境准备

在开始之前,请确保你已经安装了以下软件:

OpenCV Java摄像头如何开启?-图1
(图片来源网络,侵删)
  1. JDK (Java Development Kit):Java 开发工具包,建议使用 JDK 8 或更高版本。
  2. Maven:项目管理和构建自动化工具,用于管理 OpenCV 的依赖。
  3. IDE (集成开发环境):推荐使用 IntelliJ IDEA 或 Eclipse。
  4. OpenCV:我们需要 OpenCV 的 Java 绑定库。

最关键的一步:配置 OpenCV Java 库

OpenCV 官方提供了预编译的 Java 库,你可以从其官网下载。

  1. 下载 OpenCV:访问 OpenCV 官网 Releases 页面,下载最新的稳定版本(opencv-4.x.x)。
  2. 解压:下载后解压到一个你熟悉的目录,D:\dev\opencv
  3. 找到 opencv-java-xxx.jar:在解压目录的 opencv\build\java 文件夹下,你会找到一个名为 opencv-490.jar(版本号可能不同)的文件,这就是我们 Java 项目需要引入的库文件。
  4. 加载 OpenCV 的本地库(.dll.so:OpenCV 的 Java 库本身是依赖底层 C++ 实现的动态链接库(Windows上是 .dll,Linux上是 .so),你必须告诉 JVM 在哪里找到这个库。
    • opencv\build\java\x64 目录下,你会找到一个名为 opencv_java490.dll 的文件(同样,版本号可能不同)。
    • 你需要确保你的 Java 程序在运行时能加载到这个 .dll 文件,最简单的方法是在代码中手动指定其路径。

第 2 步:创建 Maven 项目

这里以 IntelliJ IDEA 为例:

  1. 打开 IntelliJ IDEA,选择 File -> New -> Project
  2. 选择 Maven,不要勾选 Create from archetype,直接点击 Next
  3. 填写 GroupId (com.example) 和 ArtifactId (opencv-camera-demo),点击 Next
  4. 选择你的 Maven 设置和 JDK,点击 Finish

项目创建后,你可以在 pom.xml 文件中手动添加 OpenCV 的依赖,但更推荐的方式是在代码中直接加载 .jar.dll 文件,这样可以避免 Maven 仓库中版本不匹配的问题。

OpenCV Java摄像头如何开启?-图2
(图片来源网络,侵删)

第 3 步:编写核心代码

src/main/java 目录下,创建一个 Java 类,CameraDemo.java

下面是完整的代码,并附有详细的注释。

import org.opencv.core.Core;
import org.opencv.core.Mat;
import org.opencv.videoio.VideoCapture;
import org.opencv.videoio.Videoio;
import javax.swing.*;
import java.awt.*;
import java.awt.image.BufferedImage;
import java.awt.image.DataBufferByte;
public class CameraDemo {
    // OpenCV 的 native 库路径
    // !!! 请务必修改成你自己的 OpenCV 解压路径 !!!
    private static final String OPENCV_DLL_PATH = "D:\\dev\\opencv\\build\\java\\x64\\opencv_java490.dll";
    public static void main(String[] args) {
        // 1. 加载 OpenCV native 库
        // 这是至关重要的一步,如果找不到 .dll 文件,程序会抛出 UnsatisfiedLinkError
        System.load(OPENCV_DLL_PATH);
        System.out.println("OpenCV version: " + Core.VERSION);
        // 2. 创建 VideoCapture 对象
        // 0 表示默认摄像头
        VideoCapture camera = new VideoCapture(0);
        // 检查摄像头是否成功打开
        if (!camera.isOpened()) {
            System.err.println("Error: Could not open camera.");
            return;
        }
        // 3. 设置摄像头分辨率 (可选)
        camera.set(Videoio.CAP_PROP_FRAME_WIDTH, 640);
        camera.set(Videoio.CAP_PROP_FRAME_HEIGHT, 480);
        // 4. 创建一个 Swing 窗口来显示视频
        JFrame frame = new JFrame("OpenCV Camera");
        frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
        frame.setSize(640, 480);
        // JLabel 用于显示图像
        JLabel label = new JLabel();
        frame.add(label);
        frame.setVisible(true);
        // 5. 循环读取摄像头帧
        Mat frameMat = new Mat();
        try {
            while (true) {
                // 从摄像头读取一帧
                if (camera.read(frameMat)) {
                    // 将 OpenCV 的 Mat 对象转换为 Java 的 BufferedImage
                    BufferedImage image = matToBufferedImage(frameMat);
                    // 在 Swing 的 Event Dispatch Thread 中更新 UI
                    SwingUtilities.invokeLater(() -> {
                        label.setIcon(new ImageIcon(image));
                    });
                }
                // 添加一个小的延迟,避免 CPU 占用过高
                Thread.sleep(30); // 约 33 FPS
            }
        } catch (Exception e) {
            e.printStackTrace();
        } finally {
            // 6. 释放资源
            System.out.println("Releasing resources...");
            camera.release();
            frame.dispose();
        }
    }
    /**
     * 将 OpenCV 的 Mat 对象转换为 Java 的 BufferedImage
     * @param mat OpenCV Mat 对象
     * @return BufferedImage 对象
     */
    private static BufferedImage matToBufferedImage(Mat mat) {
        int width = mat.cols();
        int height = mat.rows();
        int channels = mat.channels();
        BufferedImage image = null;
        byte[] data = new byte[width * height * channels];
        mat.get(0, 0, data); // 将 Mat 中的像素数据复制到 byte 数组
        // 根据通道数创建不同类型的 BufferedImage
        if (channels == 1) { // 灰度图
            image = new BufferedImage(width, height, BufferedImage.TYPE_BYTE_GRAY);
        } else if (channels == 3) { // BGR 彩色图
            image = new BufferedImage(width, height, BufferedImage.TYPE_3BYTE_BGR);
            // OpenCV 使用 BGR 格式,而 Java 使用 RGB,所以需要转换
            // byteBufferByte 会自动处理,但需要注意颜色通道顺序
        }
        // 将 byte 数组的数据写入 BufferedImage
        byte[] targetPixels = ((DataBufferByte) image.getRaster().getDataBuffer()).getData();
        System.arraycopy(data, 0, targetPixels, 0, data.length);
        return image;
    }
}

第 4 步:运行和测试

  1. 修改路径:最重要的一步!将代码中的 OPENCV_DLL_PATH 修改为你自己电脑上 opencv_javaXXX.dll 文件的绝对路径。
  2. 运行:在 IntelliJ IDEA 中右键点击 CameraDemo.java,选择 Run 'CameraDemo.main()'

如果一切顺利,你应该会弹出一个标题为 "OpenCV Camera" 的窗口,其中实时显示你摄像头的画面,在控制台,你还会看到打印出的 OpenCV 版本信息。

当你关闭窗口时,程序会自动进入 finally 块,释放摄像头资源并退出。

OpenCV Java摄像头如何开启?-图3
(图片来源网络,侵删)

第 5 步:进阶功能示例

拍照功能

我们可以在窗口上添加一个按钮,点击时捕获当前帧并保存为图片。

修改 CameraDemo.java

// ... 在 main 方法中,创建 frame 之后添加 ...
JButton captureButton = new JButton("拍照");
frame.add(captureButton, BorderLayout.SOUTH); // 将按钮添加到窗口底部
// ... 在 while 循环之前,定义一个变量来存储当前帧 ...
Mat lastFrame = null;
// ... 修改 while 循环内部 ...
if (camera.read(frameMat)) {
    lastFrame = frameMat.clone(); // 保存当前帧的副本
    BufferedImage image = matToBufferedImage(frameMat);
    // ... 更新 UI ...
}
// ... 在 frame.setVisible(true) 之后添加按钮事件监听 ...
captureButton.addActionListener(e -> {
    if (lastFrame != null && !lastFrame.empty()) {
        // 保存图片
        String fileName = "capture_" + System.currentTimeMillis() + ".png";
        System.out.println("Saving image to: " + fileName);
        Imgcodecs.imwrite(fileName, lastFrame);
    } else {
        System.out.println("No frame to capture.");
    }
});

注意:你需要在文件顶部添加 import org.opencv.imgcodecs.Imgcodecs;

正确的资源释放(try-with-resources

上面的 finally 块是手动释放资源,Java 7+ 提供了 try-with-resources 语法,可以更优雅地管理实现了 AutoCloseable 接口的资源(如 VideoCapture)。

// 替换掉原来的 try-catch-finally 结构
try (VideoCapture camera = new VideoCapture(0)) {
    if (!camera.isOpened()) {
        System.err.println("Error: Could not open camera.");
        return;
    }
    camera.set(Videoio.CAP_PROP_FRAME_WIDTH, 640);
    camera.set(Videoio.CAP_PROP_FRAME_HEIGHT, 480);
    JFrame frame = new JFrame("OpenCV Camera");
    // ... 设置 frame ...
    JLabel label = new JLabel();
    frame.add(label);
    frame.setVisible(true);
    Mat frameMat = new Mat();
    while (true) {
        if (camera.read(frameMat)) {
            BufferedImage image = matToBufferedImage(frameMat);
            SwingUtilities.invokeLater(() -> label.setIcon(new ImageIcon(image)));
        }
        Thread.sleep(30);
    }
} catch (Exception e) {
    e.printStackTrace();
} // 当 try 块执行完毕后,camera 会自动调用 release()

这种方式更简洁,且能确保资源在异常发生时也能被正确释放。

常见问题与解决

  • java.lang.UnsatisfiedLinkError: no opencv_javaXXX in java.library.path

    • 原因:JVM 找不到 OpenCV 的 native 库(.dll.so 文件)。
    • 解决:检查 System.load() 中的路径是否正确,确保路径指向 opencv_javaXXX.dll 文件本身,而不是它所在的文件夹。
  • Exception in thread "AWT-EventQueue-0" java.lang.NullPointerException

    • 原因:可能是摄像头没有正确打开,导致 camera.read() 返回 false,但代码继续尝试处理一个空的 Mat 对象。
    • 解决:在 camera.read(frameMat) 之后增加判断 if (!frameMat.empty())
  • 画面卡顿或延迟严重

    • 原因Thread.sleep() 的时间太短,或者电脑性能不足。
    • 解决:适当增加 sleep 的时间,50ms (20 FPS),或者在 SwingUtilities.invokeLater() 中使用 repaint() 而不是不断更换 Icon

希望这份详细的指南能帮助你成功地在 Java 中使用 OpenCV 调用摄像头!

分享:
扫描分享到社交APP
上一篇
下一篇