探索 JavaCV:开启计算机视觉与多媒体处理新世界

本文涉及的产品
任务调度 XXL-JOB 版免费试用,400 元额度,开发版规格
注册配置 MSE Nacos/ZooKeeper,182元/月
MSE Nacos 企业版免费试用,1600元额度,限量50份
简介: JavaCV 是基于 OpenCV 和 FFmpeg 的 Java 接口库,助力开发者实现视频处理、图像分析等功能。支持多种音视频格式编解码、GPU 加速及跨平台运行,适用于直播录制、摄像头捕获、美颜相机等场景,是多媒体开发的利器。

[toc]

在当今的技术领域,计算机视觉和多媒体处理的应用愈发广泛。从视频监控到直播录制,再到美颜相机等有趣的功能,都离不开强大的处理库。JavaCV 作为基于 OpenCV 和 FFmpeg 的 Java 接口库,为 Java 开发者打开了一扇通往这些功能的大门。接下来,就让我们一起深入了解 JavaCV 的神奇之处吧!

JavaCV 是什么?

JavaCV 就像是一个超级桥梁,它将 OpenCV 和 FFmpeg 这两个在 C++ 世界中叱咤风云的库,引入到了 Java 的环境里。有了 JavaCV,开发者无需再纠结于 C++ 的复杂语法和编译环境,在 Java 里就能轻松实现视频处理、图像分析、特征提取等高端操作。

它的特性超厉害

  • OpenCV 功能封装:JavaCV 把 OpenCV 的 C++ API 进行了精心封装,提供了和原生 API 很相似的 Java 接口。不管是图像处理、特征检测,还是目标跟踪,都能信手拈来。
  • FFmpeg 集成:集成了 FFmpeg 库,意味着 JavaCV 支持多种音视频格式的编解码、转码以及流媒体处理。以后处理音视频,就像吃蛋糕一样简单。
  • GPU 加速支持:在这个追求速度的时代,JavaCV 支持利用 GPU 进行加速计算。对于那些计算密集型任务,速度提升可不是一星半点。
  • 多平台兼容:无论是 Windows、Linux 还是 macOS,JavaCV 都能完美运行,跨平台性一流。

安装指南

如果你使用 Gradle,安装 JavaCV 就像下面这样简单:


dependencies {
   
    implementation 'org.springframework.boot:spring-boot-starter-web'

    // JavaCV 核心依赖
    implementation group: 'org.bytedeco', name: 'javacv-platform', version: '1.5.7'

    // Tesseract JavaCPP Presets
    implementation group: 'org.bytedeco', name: 'tesseract-platform', version: 
'5.0.1-1.5.7'



    compileOnly 'org.projectlombok:lombok'
    annotationProcessor 'org.projectlombok:lombok'
    // Apache HttpClient 依赖
    implementation 'org.apache.httpcomponents:httpclient:4.5.14'
    // fastjson2 核心依赖
    implementation 'com.alibaba.fastjson2:fastjson2:2.0.36'
    implementation 'cn.hutool:hutool-all:5.8.1'
    implementation 'org.springdoc:springdoc-openapi-ui:1.7.0'
}
AI 代码解读

这里要注意啦,本文使用的 SpringBoot 版本为 2.7.6,JDK 版本为 11。而且在 Intellj IDEA 的“外部库”依赖关系中可以发现,javacv - platform 已经自带了很多依赖包,甚至连 ffmpeg 库都包含在内,所以使用时无需单独安装 ffmpeg 库。

自包含ffmpeg.png

有趣的 JavaCV 使用示例

录制 RTMP 直播流

想象一下,你正在观看一场精彩的直播,想要把它录制下来慢慢回味。JavaCV 就能帮你轻松实现这个需求。

核心对象揭秘

  • FFmpegFrameGrabber:它就像一个勤劳的小蜜蜂,从直播源中抓取帧数据。
  • FFmpegFrameRecorder:负责把抓取到的帧数据录制下来,保存成 MP4 文件。

下面是封装在 Spring Boot 中的示例代码:


@Slf4j
@RequiredArgsConstructor
@Component
public class RecordComponent {
   

    /**
     * 录制MP4
     *
     * @param deviceCode 设备编码
     * @param videoUrl  直播url
     * @param dpi 录制的视频质量(720p,1080p,2k)
     * @return
     */
    @Async("customThreadPool")
    public String record(String deviceCode, String videoUrl, DpiEnum dpi) {
   
        log.debug("video_record_param:deviceCode={},videoUrl={},dpi={}", deviceCode, videoUrl, dpi);
        try (FFmpegFrameGrabber grabber = new FFmpegFrameGrabber(videoUrl)) {
   
            if (videoUrl.startsWith("rtmp")) {
   
                //使用 TCP 避免丢包
                grabber.setOption("rtmp_transport", "tcp");
            }
            if (videoUrl.startsWith("rtsp")) {
   
                //使用 TCP 避免丢包
                grabber.setOption("rtsp_transport", "tcp");
            }
            //禁用数据缓冲区,直接从源读取,适用于实时流(如 RTSP/RTMP),减少从源到帧抓取的延迟
            grabber.setOption("fflags", "nobuffer");
            grabber.start();

            String videoSavePath="";
            try (FFmpegFrameRecorder recorder = new FFmpegFrameRecorder(videoSavePath, dpi.getWidth(), dpi.getHeight())) {
   
                // 禁用音频,若源中存在音频通道(即grabber.getAudioChannels()>0),此配置无法生效
                recorder.setAudioChannels(0);
                //设置编码格式:h264
                recorder.setVideoCodec(avcodec.AV_CODEC_ID_H264);
                recorder.setFormat("mp4");
                //fps,帧率表示视频中每秒钟显示的静态画面数量
                recorder.setFrameRate(grabber.getFrameRate());
                //视频色彩相关配置
                recorder.setPixelFormat(avutil.AV_PIX_FMT_YUV420P);
                //降低视频编码延迟,适用于对实时性要求极高的场景
                recorder.setOption("tune", "zerolatency");
                //预设值影响编码速度和压缩效率的平衡
                recorder.setOption("preset", "veryfast");
                recorder.start();

                Frame frame;
                while ((frame = grabber.grab()) != null) {
   
                    recorder.record(frame);
                    log.debug("video_record_{}视频帧录制成功", deviceCode);
                }
            } catch (Exception e) {
   
                log.error("video_record_recorder_error,msg={},deviceCode={}", e.getMessage(), deviceCode);
                log.error("video_record_recorder_error", e);
            }
        } catch (Exception e) {
   
            log.error("video_record_grabber_error,msg={},deviceCode={}", e.getMessage(), deviceCode);
            log.error("video_record_grabber_error", e);
        }
    }
}
AI 代码解读

这里的 @Component 让这个类成为 Spring IOC 组件,方便在其他地方注入使用。@Async 开启了异步线程,因为录制直播可能需要很长时间,不能让请求方一直等待。

原理:通过 FFmpegFrameGrabber 从指定的直播地址抓取帧数据,然后通过 FFmpegFrameRecorder 将帧数据录制为 mp4 文件。这里面会通过调用自包含的ffmpeg库,实现录制功能。

捕获摄像头画面

想不想用 Java 打开笔记本的摄像头,看看自己帅气或美丽的脸庞?下面的代码就能帮你实现:


public class CameraComponent {
   

    /**
     * 打开摄像头并显示视频流,不可以以SpringBoot Web方式调用, 可以使用 main 调用。
     */
    public static void openCamera() {
   
        try (OpenCVFrameGrabber grabber = new OpenCVFrameGrabber(0)) {
   
            grabber.start();

            CanvasFrame frameViewer = new CanvasFrame("摄像头视频");
            frameViewer.setCanvasSize(grabber.getImageWidth(), grabber.getImageHeight());

            Frame frame;
            while ((frame = grabber.grab()) != null && frameViewer.isVisible()) {
   
                frameViewer.showImage(frame);
            }

            frameViewer.dispose();
        } catch (Exception e) {
   
            log.error("打开摄像头失败", e);
        }
    }

    public static void main(String[] args) {
   
        openCamera();
    }
}
AI 代码解读

开启摄像头.png

OpenCVFrameGrabber 负责捕获摄像头的视频流,CanvasFrame 则用来显示视频流。不过要注意哦,这个方法只能在 main 方法里运行,Spring Boot Web 项目启动会报错。

美颜相机

有了摄像头,怎么能少了美颜功能呢?JavaCV 也能实现简单的美颜效果。


public class CameraComponent {
   

    /**
     * 美颜相机
     */
    public static void beautyFace() {
   
        try (OpenCVFrameGrabber grabber = new OpenCVFrameGrabber(0)) {
   
            CanvasFrame frameViewer = new CanvasFrame("美颜相机");
            grabber.start();
            frameViewer.setCanvasSize(grabber.getImageWidth(), grabber.getImageHeight());

            String cascadePath = "D:\\temp\\haarcascade_frontalface_default.xml";
            System.out.println("人脸检测器路径: " + cascadePath);
            CascadeClassifier faceDetector = new CascadeClassifier(cascadePath);

            OpenCVFrameConverter.ToMat converter = new OpenCVFrameConverter.ToMat();

            Frame frame;
            while ((frame = grabber.grab()) != null && frameViewer.isVisible()) {
   
                Mat mat = converter.convert(frame);
                if (mat == null) continue;

                Mat gray = new Mat();
                cvtColor(mat, gray, COLOR_BGR2GRAY);
                equalizeHist(gray, gray);

                RectVector faceDetections = new RectVector();
                faceDetector.detectMultiScale(gray, faceDetections);

                gray.release();

                for (int i = 0; i < faceDetections.size(); i++) {
   
                    Rect rect = faceDetections.get(i);
                    applyBeautyFilter(mat, rect);
                }

                frameViewer.showImage(converter.convert(mat));
            }
        } catch (Exception e) {
   
            e.printStackTrace();
        }
    }

    /**
     * 简化版美颜滤镜实现
     * @param frame
     * @param face
     */
    private static void applyBeautyFilter(Mat frame, Rect face) {
   
        int expand = face.width() / 4;
        Rect expandedFace = new Rect(
                Math.max(0, face.x() - expand),
                Math.max(0, face.y() - expand),
                Math.min(frame.cols() - face.x() - 1, face.width() + 2 * expand),
                Math.min(frame.rows() - face.y() - 1, face.height() + 2 * expand)
        );

        Mat faceROI = new Mat(frame, expandedFace);

        Mat smoothed = new Mat();
        bilateralFilter(faceROI, smoothed, 15, 80, 80);

        smoothed.copyTo(new Mat(frame, expandedFace));

        smoothed.release();
        faceROI.release();
    }
}
AI 代码解读

这个美颜功能主要是磨皮和虚化,让你的皮肤看起来更加光滑。不过它只对正脸有效哦,如果侧着脸或者没有检测到人脸,就不会有效果啦。

大致思路为,先捕捉摄像头的视频流,解析出帧数据,然后将帧数据转成图片给OpenCV库处理,处理完毕就是美颜之后的图片,再渲染到播放窗口上。

bilateralFilter:调用OpenCV的这个方法,实现磨皮效果。具体这个方法的作用可以参考:https://homepages.inf.ed.ac.uk/rbf/CVonline/LOCAL_COPIES/MANDUCHI1/Bilateral_Filtering.html

haarcascade_frontalface_default.xml: 这是 OpenCV 提供的一个预训练模型文件,用于人脸检测。它基于 Haar 级联分类器(Haar Cascade Classifier) 算法,包含了训练好的人脸特征数据,用于识别图像中的正面人脸区域。

faceDetections:这个对象的size 大于0 ,说明检测到了人脸。


JavaCV 的功能远不止这些,它就像一个宝藏库,等待着开发者们去挖掘更多有趣又实用的功能。赶紧动手试试吧!

引用

https://github.com/bytedeco/javacv

https://bytedeco.org/

https://gitee.com/naylor_personal/ramble-spring-boot/tree/master/java-cv

目录
打赏
0
0
0
0
71
分享
相关文章
大模型基本概念介绍
大模型(LLM)是基于深度学习构建的大型人工智能模型,具备强大的语言理解、分析与生成能力。其参数量庞大,模拟人脑神经元,提升推理准确性。训练过程包括预训练、微调、强化学习等阶段,广泛应用于内容创作、机器人、科研等领域。按用途可分为通用与专用模型,按输入输出则有语言、视觉、多模态等类型。提示词工程(Prompt Engineering)则是引导大模型输出的关键技术。
608 0
|
11月前
|
大疆无人机对接
本文介绍了大疆无人机对接第三方云平台的方案,包括设备对接和CloudAPI对接两种方式,重点讨论了CloudAPI对接。CloudAPI对接方案通过DJI Pilot 2或大疆机场将无人机与第三方云平台连接,实现低门槛接入,无需重复开发APP。方案优势在于让开发者更专注于业务开发,而非无人机功能适配。文章详细阐述了对接流程,包括环境准备、申请APPKey、对接流程、直播功能及获取无人机实时数据等内容,并提供了丰富的接口说明和技术支持资源。
5488 5
大疆无人机对接
进阶版|企业级 AI Agent 的构建实践
我们将构建 AI 应用扩展到了运行时和可观测,并尝试将 Agent、LLM、MCP 服务这几者之间如何有机协作尽量清晰化,未来还会扩展到Memory、LiteMQ 等更完整的技术栈,旨在帮助大家厘清完整的企业级 AI 应用构建的最佳实践。
从“手环”到“健康顾问”:可穿戴设备背后的数据魔法
从“手环”到“健康顾问”:可穿戴设备背后的数据魔法
251 10
从“手环”到“健康顾问”:可穿戴设备背后的数据魔法
跨境卖家必看:1688商品列表页面数据接口抓取攻略
1688平台提供商品列表数据接口(1688.item_search),支持通过关键词搜索商品,返回商品ID、标题、价格、销量、图片等信息。参数包括关键词q、页码page、每页数量page_size等。开发者需注意签名机制与调用频率限制,确保稳定获取数据。
PostGIS简介
PostGIS是PostgreSQL的扩展插件,增强其处理地理空间数据的能力,支持空间数据存储、索引、查询及分析等功能。它适用于2D和3D空间数据,提供多种几何类型和栅格数据支持,兼容多种第三方工具。安装需先配置EPEL和PowerTools仓库,然后通过DNF安装PostGIS包,并在目标数据库中启用扩展。PostGIS支持geometry、geography和raster等数据类型,适用于不同场景的空间数据分析。
517 0
“直播”极简教程
本文以一个非常简单的实际例子,搭建一个直播所需要的基础软件支撑平台,浅尝直播业务中核心业务概念及他们的交互流程。 对于一场直播,大致会拥有如下环节: * 主播通过直播设备将画面推送到直播平台 * 平台接收主播推送的画面 * 观众通过平台找到主播的直播画面,具体来说就是要找到主播的房间号 * 观众从平台拉取房间号中的直播画面
440 11
“直播”极简教程
Python爬虫——基于JWT的模拟登录爬取实战
Python爬虫——基于JWT的模拟登录爬取实战
207 1
Python爬虫——基于JWT的模拟登录爬取实战
SpringBoot 如何解决跨域问题?
本文深入探讨了Spring Boot解决跨域问题的方法,包括全局配置CORS、使用@CrossOrigin注解和自定义过滤器,提供了详细的代码示例和分析,帮助开发者有效应对Web开发中的跨域挑战。
690 4
AI助理

你好,我是AI助理

可以解答问题、推荐解决方案等