从视频中截取指定帧图片
前言:
我们在很多时候需要对视频文件进行分析,或者对视频产生缩略图。因此视频截取技术必不可少。
从本地文件中读取视频帧
导包
<dependency><groupId>org.jcodec</groupId><artifactId>jcodec</artifactId><version>0.2.5</version></dependency><dependency><groupId>org.jcodec</groupId><artifactId>jcodec-javase</artifactId><version>0.2.5</version></dependency><!-- http://repo1.maven.org/maven2/commons-io/commons-io/2.6/commons-io-2.6.jar --><dependency><groupId>commons-io</groupId><artifactId>commons-io</artifactId><version>2.6</version></dependency>
读取视频帧工具类
package com.wkl.testdemo.vedio;import lombok.extern.slf4j.Slf4j;
import org.apache.commons.io.FileUtils;
import org.jcodec.api.FrameGrab;
import org.jcodec.api.JCodecException;
import org.jcodec.common.model.Picture;
import org.jcodec.scale.AWTUtil;
import org.springframework.web.multipart.MultipartFile;import javax.imageio.ImageIO;
import java.awt.image.BufferedImage;
import java.io.*;
import java.net.URL;
import java.util.UUID;/*** 视频操作工具类*/
@Slf4j
public class VideoUtils {/*** 图片格式*/private static final String FILE_EXT = "jpg";/*** 帧数-第几帧*/private static final int THUMB_FRAME = 500;/*** 获取指定视频的帧并保存为图片至指定目录** @param videoFilePath 源视频文件路径* @param frameFilePath 截取帧的图片存放路径*/public static void fetchFrame(String videoFilePath, String frameFilePath) throws Exception {File videoFile = new File(videoFilePath);File frameFile = new File(frameFilePath);getThumbnail(videoFile, frameFile);}/*** 获取指定视频的帧并保存为图片至指定目录** @param videoFile 源视频文件* @param targetFile 截取帧的图片*/public static void fetchFrame(MultipartFile videoFile, File targetFile) throws Exception {File file = new File(videoFile.getName());FileUtils.copyInputStreamToFile(videoFile.getInputStream(), file);getThumbnail(file, targetFile);}/*** 获取指定视频的帧并保存为图片至指定目录** @param videoFile 源视频文件*/public static File fetchFrame(MultipartFile videoFile) {String originalFilename = videoFile.getOriginalFilename();File file = new File(originalFilename);File targetFile = null;try {FileUtils.copyInputStreamToFile(videoFile.getInputStream(), file);int i = originalFilename.lastIndexOf(".");String imageName;if (i > 0) {imageName = originalFilename.substring(0, i);} else {imageName = UUID.randomUUID().toString().replace("-", "");}imageName = imageName + ".jpg";targetFile = new File(imageName);getThumbnail(file, targetFile);} catch (Exception e) {log.error("获取视频指定帧异常:", e);} finally {if (file.exists()) {file.delete();}}log.debug("视频文件 - 帧截取 - 处理结束");return targetFile;}/*** 获取第一帧缩略图** @param videoFile 视频路径* @param targetFile 缩略图目标路径*/public static void getThumbnail(File videoFile, File targetFile) {try {// 根据扩展名创建一个新文件路径Picture picture = FrameGrab.getFrameFromFile(videoFile, THUMB_FRAME);BufferedImage bufferedImage = AWTUtil.toBufferedImage(picture);ImageIO.write(bufferedImage, FILE_EXT, targetFile);} catch (IOException | JCodecException e) {e.printStackTrace();log.error("获取第一帧缩略图异常:", e);}}public static void main(String[] args) {try {long startTime = System.currentTimeMillis();getThumbnail(new File("D:\\Videos\\2023112911533869304715.mp4"), new File("D:\\Videos\\test1.jpg"));System.out.println("截取图片耗时:" + (System.currentTimeMillis() - startTime));} catch (Exception e) {e.printStackTrace();}}public static void saveImage(String imageUrl, String destinationFile) throws IOException {URL url = new URL(imageUrl);InputStream is = url.openStream();OutputStream os = new FileOutputStream(destinationFile);byte[] b = new byte[2048];int length;while ((length = is.read(b)) != -1) {os.write(b, 0, length);}is.close();os.close();}}
从网络url 读取视频帧-ffmpeg
导包
<!-- 获取视频第一帧依赖 --><dependency><groupId>org.bytedeco</groupId><artifactId>javacpp</artifactId><version>1.4.1</version></dependency><dependency><groupId>org.bytedeco</groupId><artifactId>javacv</artifactId><version>1.4.1</version></dependency><dependency><groupId>org.bytedeco.javacpp-presets</groupId><artifactId>ffmpeg-platform</artifactId><version>3.4.2-1.4.1</version></dependency>
工具类
package com.wkl.testdemo.vedio;import lombok.extern.slf4j.Slf4j;
import org.bytedeco.javacv.FFmpegFrameGrabber;
import org.bytedeco.javacv.Frame;
import org.bytedeco.javacv.FrameGrabber;
import org.bytedeco.javacv.Java2DFrameConverter;import javax.imageio.ImageIO;
import java.awt.image.BufferedImage;
import java.io.*;
import java.util.HashMap;
import java.util.Map;/*** 视频抽帧工具*/
@Slf4j
public class VideoFrame {//传入包含特定时间的Frame ,读取图片public static BufferedImage doExecuteFrame(Frame frame, int index) {if (frame == null || frame.image == null) {return null;}Java2DFrameConverter converter = new Java2DFrameConverter();BufferedImage bi = converter.getBufferedImage(frame);return bi;}/** @description: 读取视频第n秒的图片帧* @author: wangkanglu* @date: 2023/12/8 15:05* @param: [videoUrl, stepSecond]* @return: java.awt.image.BufferedImage**/public static BufferedImage doExecuteFrameByTime(String videoUrl, Integer stepSecond) {FFmpegFrameGrabber ff = new FFmpegFrameGrabber(videoUrl);ff.setOption("timeout", "40000000");long timestamp = stepSecond * 1000000L; //视频是按照微秒计算的,10的6次方分之一秒try {ff.start();ff.setTimestamp(timestamp);Frame frame = ff.grabImage();if (frame == null || frame.image == null) {return null;}Java2DFrameConverter converter = new Java2DFrameConverter();BufferedImage bi = converter.getBufferedImage(frame);ff.stop();return bi;} catch (FrameGrabber.Exception e) {throw new RuntimeException(e);}}/*** 视频文件边下载边抽帧1秒1帧** @param videoUrl 网络视频文件URL* @param stepSecond 每隔几秒取一帧,默认1s* @param count 需要截取的帧个数* @return*/public static Map<Integer, BufferedImage> videoUrlIntercept(String videoUrl, Integer stepSecond, Integer count) {Map<Integer, BufferedImage> files = new HashMap<>();stepSecond = stepSecond == null ? 1 : stepSecond;FFmpegFrameGrabber ff = new FFmpegFrameGrabber(videoUrl);// 设置超时时间为40秒ff.setOption("timeout", "40000000");
// ff.setOption("user_agent", UserAgent.getUserAgent());try {ff.start();long timeLength = ff.getLengthInTime();Frame frame = ff.grabImage();long startTime = frame.timestamp;long timestamp = 0; //视频的当前时长int second = 0; //过了几个时间间隔int picNum = 0;//第n张帧while (timestamp <= timeLength) {log.info("抽取第{}帧,video_url:{}",picNum,videoUrl);timestamp = startTime + second * 1000000L; //视频是按照微秒计算的,10的6次方分之一秒ff.setTimestamp(timestamp);frame = ff.grabImage();if (frame != null) {if (frame.image != null) {BufferedImage bufferedImage = doExecuteFrame(frame, picNum);if (bufferedImage != null) {files.put(picNum, bufferedImage);}picNum++;if (count != null && picNum == count) {break;}}}second += stepSecond;if(picNum > 60) {break;}}ff.stop();} catch (Exception e) {log.error("下载抽帧失败,ipPort:{},videoUrl:{},msg:{}", null, videoUrl, e.getMessage());e.printStackTrace();}return files;}/*** 视频文件指定时间段的帧截取** @param videoUrl 视频文件URL* @param start 视频开始的帧* @param count 需要截取的帧个数* @param isAvgTime 在截帧时 是否均匀分布计算时间* @return*/public static Map<Integer, BufferedImage> videoIntercept(String videoUrl, int start, int count, boolean isAvgTime) {log.info("开始抽取视频帧数,videoUrl:{}",videoUrl);Frame frame = null;//<时间, 图片流>Map<Integer, BufferedImage> files = new HashMap<>();FFmpegFrameGrabber fFmpegFrameGrabber = new FFmpegFrameGrabber(videoUrl);fFmpegFrameGrabber.setOption("timeout", "40000000");try {fFmpegFrameGrabber.start();long frameTime = 1;if (isAvgTime) {frameTime = fFmpegFrameGrabber.getLengthInTime() / count / 1000000L;if (frameTime < 0) {frameTime = 1;}}for (int i = start; i <= count; i++) {fFmpegFrameGrabber.setTimestamp(i * frameTime * 1000 * 1000);frame = fFmpegFrameGrabber.grabImage();BufferedImage bufferedImage = doExecuteFrame(frame, i);if (bufferedImage != null) {files.put(i, bufferedImage);}}fFmpegFrameGrabber.stop();} catch (Exception E) {log.info("下载的视频抽帧失败,msg:" + E.getMessage());E.printStackTrace();}return files;}/** @description: BufferedImage 转 inputStream* @author: wangkanglu* @date: 2023/12/8 14:52* @param: [image]* @return: java.io.InputStream**/public static InputStream bufferedImageToInputStream(BufferedImage image) {ByteArrayOutputStream os = new ByteArrayOutputStream();try {ImageIO.write(image, "jpg", os);InputStream input = new ByteArrayInputStream(os.toByteArray());return input;} catch (IOException e) {}return null;}public static void main(String[] args) throws IOException {String videoUrl = "http://vd2.bdstatic.com/mda-pej1ztfufz8axvtu/360p/h264/1684545921389774683/mda-pej1ztfufz8axvtu.mp4";Map<Integer, BufferedImage> integerInputStreamMap = videoUrlIntercept(videoUrl, 1, 13);System.out.println(integerInputStreamMap.size());for (Integer seconds : integerInputStreamMap.keySet()) {BufferedImage bufferedImage = integerInputStreamMap.get(seconds);String fileName = System.currentTimeMillis()+"抖音测试3" + "_" + seconds + ".jpg";String filePath = "D:\\Videos\\"+fileName;//本地磁盘存储// 本地图片保存地址ImageIO.write(bufferedImage, "png", new File(filePath));System.out.println("seconds: " + seconds + ", uploadURL: " + filePath);}}
}
附赠压缩图片工具类
package com.wkl.testdemo.vedio;import lombok.extern.slf4j.Slf4j;
import org.apache.tomcat.util.codec.binary.Base64;
import org.bytedeco.javacv.FFmpegFrameGrabber;
import org.bytedeco.javacv.Frame;
import org.bytedeco.javacv.FrameGrabber;
import org.bytedeco.javacv.Java2DFrameConverter;
import org.springframework.stereotype.Component;import javax.imageio.ImageIO;
import java.awt.*;
import java.awt.image.BufferedImage;
import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.File;
import java.io.IOException;@Slf4j
@Component
public class VideoFrameGrabber {public static void main(String[] args) throws Exception {// 视频地址String vedioUrl = "http://vd2.bdstatic.com/mda-pej1ztfufz8axvtu/360p/h264/1684545921389774683/mda-pej1ztfufz8axvtu.mp4";BufferedImage image = doExecuteFrameByTime(vedioUrl, 10);double targetSize = 10*1024;while (imageToBytes(image).length > targetSize) {float reduceMultiple = 0.5f;image = resizeImage(image, reduceMultiple);}// 本地图片保存地址ImageIO.write(image, "png", new File("D:\\Videos\\test6.jpg"));}/** @description: 读取视频第n秒的图片帧* @author: wangkanglu* @date: 2023/12/8 15:05* @param: [videoUrl, stepSecond]* @return: java.awt.image.BufferedImage**/public static BufferedImage doExecuteFrameByTime(String videoUrl, Integer stepSecond) {FFmpegFrameGrabber ff = new FFmpegFrameGrabber(videoUrl);ff.setOption("timeout", "40000000");long timestamp = stepSecond * 1000000L; //视频是按照微秒计算的,10的6次方分之一秒try {ff.start();ff.setTimestamp(timestamp);Frame frame = ff.grabImage();if (frame == null || frame.image == null) {return null;}Java2DFrameConverter converter = new Java2DFrameConverter();BufferedImage bi = converter.getBufferedImage(frame);ff.stop();return bi;} catch (FrameGrabber.Exception e) {throw new RuntimeException(e);}}/*** 通过BufferedImage图片流调整图片大小* 指定压缩后长宽*/public static BufferedImage resizeImage(BufferedImage originalImage, int targetWidth, int targetHeight) throws IOException {Image resultingImage = originalImage.getScaledInstance(targetWidth, targetHeight, Image.SCALE_AREA_AVERAGING);BufferedImage outputImage = new BufferedImage(targetWidth, targetHeight, BufferedImage.TYPE_INT_RGB);outputImage.getGraphics().drawImage(resultingImage, 0, 0, null);return outputImage;}/*** 通过BufferedImage图片流调整图片大小* @param originalImage* @param reduceMultiple 缩小倍数* @return* @throws IOException*/public static BufferedImage resizeImage(BufferedImage originalImage, float reduceMultiple) throws IOException {int width = (int) (originalImage.getWidth() * reduceMultiple);int height = (int) (originalImage.getHeight() * reduceMultiple);Image resultingImage = originalImage.getScaledInstance(width, height, Image.SCALE_AREA_AVERAGING);BufferedImage outputImage = new BufferedImage(width, height, BufferedImage.TYPE_INT_RGB);outputImage.getGraphics().drawImage(resultingImage, 0, 0, null);return outputImage;}/*** 压缩图片到指定大小* @param srcImgData* @param reduceMultiple 每次压缩比率* @return* @throws IOException*/public static byte[] resizeImage(byte[] srcImgData, float reduceMultiple) throws IOException {BufferedImage bi = ImageIO.read(new ByteArrayInputStream(srcImgData));int width = (int) (bi.getWidth() * reduceMultiple); // 源图宽度int height = (int) (bi.getHeight() * reduceMultiple); // 源图高度Image image = bi.getScaledInstance(width, height, Image.SCALE_SMOOTH);BufferedImage tag = new BufferedImage(width, height, BufferedImage.TYPE_INT_RGB);Graphics g = tag.getGraphics();g.setColor(Color.RED);g.drawImage(image, 0, 0, null); // 绘制处理后的图g.dispose();ByteArrayOutputStream bOut = new ByteArrayOutputStream();ImageIO.write(tag, "JPEG", bOut);return bOut.toByteArray();}/*** BufferedImage图片流转byte[]数组*/public static byte[] imageToBytes(BufferedImage bImage) {ByteArrayOutputStream out = new ByteArrayOutputStream();try {ImageIO.write(bImage, "jpg", out);} catch (IOException e) {e.printStackTrace();}return out.toByteArray();}/*** byte[]数组转BufferedImage图片流*/private static BufferedImage bytesToBufferedImage(byte[] ImageByte) {ByteArrayInputStream in = new ByteArrayInputStream(ImageByte);BufferedImage image = null;try {image = ImageIO.read(in);} catch (IOException e) {e.printStackTrace();}return image;}}相关文章:
从视频中截取指定帧图片
前言: 我们在很多时候需要对视频文件进行分析,或者对视频产生缩略图。因此视频截取技术必不可少。 从本地文件中读取视频帧 导包 <dependency><groupId>org.jcodec</groupId><artifactId>jcodec</artifactId><versio…...
2023最新vue安装(npm,yarn,国内镜像,vue安装,vue导包)全套教程2023年12月最新
第一步(安装npm) 官网地址:https://nodejs.org/en/download windows安装yarn 详细教程_windows yarn-CSDN博客 第二步(yarn下载) windows 下需要下载msi文件 ,下载地址:https://yarnpkg.com/latest.msi npm install -g…...
性能测试基础
性能测试分类 客户端性能:测试APP自身的性能,例如CPU、内存消耗;web页面元素渲染速度 服务端性能:测试服务端项目程序的支持的并发、处理能力、响应时间等,主要通过接口来做性能测试 性能测试指标 并发 同时向服务…...
算法与数据结构--最短路径Dijkstra算法
题目: 算法与数据结构实验题 10.20 迷路 ★实验任务 学长经常迷路,现在他又遇到问题了,需要求救。 假设他有一张地图,上面有N个点,M条路,他现在在编号为S的地方,想要去编号为E的地方&#x…...
ASP.NET Core 8 在 Windows 上各种部署模型的性能测试
ASP.NET Core 8 在 Windows 上各种部署模型的性能测试 我们知道 Asp.net Core 在 windows 服务器上部署的方案有 4 种之多。这些部署方案对性能的影响一直以来都是靠经验。比如如果是部署在 IIS 下,那么 In Process 会比 Out Process 快;如果是 Self Hos…...
跨框架解决方案-Mitosis【Context】
Context Mitosis的context必须是: 在自己的文件中创建文件名必须以context.lite.ts结尾默认导出必须是一个返回context对象的函数 // simple.context.lite.ts import { createContext } from builder.io/mitosis;export default createContext({foo: bar,get foo…...
有哪些重要的项目是用 Python 开发的?
请访问 https://www.python.org/about/success 查看使用了 Python 的项目列表。 阅览 历次 Python 会议 的日程纪要可以看到许多不同公司和组织所做的贡献。 高水准的 Python 项目包括 Mailman 邮件列表管理器 和 Zope 应用服务器。 多个 Linux 发行版,其中最著名的…...
【计算机网络】应用层电子邮件协议
一、电子邮件系统架构 电子邮件是一个典型的异步通信系统,发送方从UA,也就是邮件客户端,通过应用层SMTP协议,传输层tcp协议,发送给发送方的邮件服务器,比如使用的是163邮箱,163提供的SMTP服务器…...
视频剪辑:视频转码实用技巧,批量将MP4转为MP3音频
随着数字媒体设备的普及,视频和音频文件已成为日常生活中的重要组成部分。有时,可能要将MP4视频文件转换为MP3音频文件,以提取其中的音频内容或者进行其他处理。这是耗费时间的任务,那要如何操作呢?本文详解云炫AI智剪…...
体系化学习运筹学基础算法的实践和总结
文章目录 引言目标设计目标实践文章汇总经验总结一则预告 引言 眨眼间已经12月了,眼看着2023年马上要过完了。 女朋友最近总说,工作以后感觉时间过的好快。事实上,我也是这么认为的。年纪越大,越会担心35岁危机的降临。所以&…...
【Java探索之旅】我与Java的初相识(一):Java的特性与优点及其发展史
🎥 屿小夏 : 个人主页 🔥个人专栏 : Java入门到精通 🌄 莫道桑榆晚,为霞尚满天! 文章目录 一. Java语言概述与优势1.1 Java的概述1.2 Java语言的优势 二. Java领域与发展史2.1 Java的使用领域2.…...
重写 AppiumService 类,添加默认启动参数,并实时显示启动日志
一、前置说明 在Appium的1.6.0版本中引入了AppiumService类,可以很方便的通过该类来管理Appium服务器的启动和停止。经过测试,使用该类的实例执行关闭server时,并没有释放端口号,会导致第二次启动时失败。另外,使用该…...
[方法论]allocation 空间内容分配
区分度 typeanalysisrecognitionconclusion type - 阅读 - 理解- 背诵- 听课 看 听 思考- reproduce/ 默写/ 应用- 背- 想- 写analysis 理解 和 背 是不占用现实空间的,可以在脑内不断消化,可以飞配给没有空间的时间块。 阅读 和 写是占用现实空间的…...
家电制造数字孪生5G智能工厂可视化系统,加速家电制造产业数字化转型
5G数字孪生、三维可视化与工业互联网的融合加速中国新型工业化进程,助推我国从制造大国迈进制造强国。家电行业是中国最具国际竞争力的产业之一,在企业数字化转型中,要求企业从生产设备到数字化系统,一系列的数字化、智能化改革已…...
Flink入门之部署(二)
三种部署模式 standalone集群,会话模式部署:先启动flink集群 web UI提交shell命令提交:bin/flink run -d -m hadoop102:8081 -c com.atguigu.flink.deployment.Flinke1_NordCount./Flink-1.0-SNAPSHOT.jar --hostname hadoop102 --port 8888 …...
SQL命令---修改字段名
介绍 使用sql语句修改字段名。 命令 alter table 表名 change 旧字段名 新字段名 新数据类型;例子 将a表id字段名改为id1 alter table a change id id1 int(12) NOT NULL;...
设计模式篇---代理模式
文章目录 概念结构实例静态代理动态代理 总结 概念 代理模式:给某一个对象提供一个代理或占位符,并由代理对象来控制对原对象的访问。 比如我们想从其他国家买东西,但我们无法直接联系外国的商家,可以找代理商,让他们…...
STM32单片机项目实例:基于TouchGFX的智能手表设计(2)UI交互逻辑的设计
STM32单片机项目实例:基于TouchGFX的智能手表设计(2)UI交互逻辑的设计 目录 一、UI交互逻辑的设计 1.1 硬件平台的资源 1.2 界面切换功能 1.3 表盘界面 1.4 运动界面 1.6 设置界面 1.7 应…...
ES-分析器
分析器 两种常用的英语分析器 1 测试工具 #可以通过这个来测试分析器 实际生产环境中我们肯定是配置在索引中来工作 GET _analyze {"text": "My Moms Son is an excellent teacher","analyzer": "english" }2 实际效果 比如我们有下…...
智能优化算法应用:基于缎蓝园丁鸟算法3D无线传感器网络(WSN)覆盖优化 - 附代码
智能优化算法应用:基于缎蓝园丁鸟算法3D无线传感器网络(WSN)覆盖优化 - 附代码 文章目录 智能优化算法应用:基于缎蓝园丁鸟算法3D无线传感器网络(WSN)覆盖优化 - 附代码1.无线传感网络节点模型2.覆盖数学模型及分析3.缎蓝园丁鸟算法4.实验参数设定5.算法…...
TDengine 快速体验(Docker 镜像方式)
简介 TDengine 可以通过安装包、Docker 镜像 及云服务快速体验 TDengine 的功能,本节首先介绍如何通过 Docker 快速体验 TDengine,然后介绍如何在 Docker 环境下体验 TDengine 的写入和查询功能。如果你不熟悉 Docker,请使用 安装包的方式快…...
Leetcode 3576. Transform Array to All Equal Elements
Leetcode 3576. Transform Array to All Equal Elements 1. 解题思路2. 代码实现 题目链接:3576. Transform Array to All Equal Elements 1. 解题思路 这一题思路上就是分别考察一下是否能将其转化为全1或者全-1数组即可。 至于每一种情况是否可以达到…...
微信小程序 - 手机震动
一、界面 <button type"primary" bindtap"shortVibrate">短震动</button> <button type"primary" bindtap"longVibrate">长震动</button> 二、js逻辑代码 注:文档 https://developers.weixin.qq…...
(二)原型模式
原型的功能是将一个已经存在的对象作为源目标,其余对象都是通过这个源目标创建。发挥复制的作用就是原型模式的核心思想。 一、源型模式的定义 原型模式是指第二次创建对象可以通过复制已经存在的原型对象来实现,忽略对象创建过程中的其它细节。 📌 核心特点: 避免重复初…...
2025盘古石杯决赛【手机取证】
前言 第三届盘古石杯国际电子数据取证大赛决赛 最后一题没有解出来,实在找不到,希望有大佬教一下我。 还有就会议时间,我感觉不是图片时间,因为在电脑看到是其他时间用老会议系统开的会。 手机取证 1、分析鸿蒙手机检材&#x…...
Android15默认授权浮窗权限
我们经常有那种需求,客户需要定制的apk集成在ROM中,并且默认授予其【显示在其他应用的上层】权限,也就是我们常说的浮窗权限,那么我们就可以通过以下方法在wms、ams等系统服务的systemReady()方法中调用即可实现预置应用默认授权浮…...
【笔记】WSL 中 Rust 安装与测试完整记录
#工作记录 WSL 中 Rust 安装与测试完整记录 1. 运行环境 系统:Ubuntu 24.04 LTS (WSL2)架构:x86_64 (GNU/Linux)Rust 版本:rustc 1.87.0 (2025-05-09)Cargo 版本:cargo 1.87.0 (2025-05-06) 2. 安装 Rust 2.1 使用 Rust 官方安…...
Linux nano命令的基本使用
参考资料 GNU nanoを使いこなすnano基础 目录 一. 简介二. 文件打开2.1 普通方式打开文件2.2 只读方式打开文件 三. 文件查看3.1 打开文件时,显示行号3.2 翻页查看 四. 文件编辑4.1 Ctrl K 复制 和 Ctrl U 粘贴4.2 Alt/Esc U 撤回 五. 文件保存与退出5.1 Ctrl …...
9-Oracle 23 ai Vector Search 特性 知识准备
很多小伙伴是不是参加了 免费认证课程(限时至2025/5/15) Oracle AI Vector Search 1Z0-184-25考试,都顺利拿到certified了没。 各行各业的AI 大模型的到来,传统的数据库中的SQL还能不能打,结构化和非结构的话数据如何和…...
【UE5 C++】通过文件对话框获取选择文件的路径
目录 效果 步骤 源码 效果 步骤 1. 在“xxx.Build.cs”中添加需要使用的模块 ,这里主要使用“DesktopPlatform”模块 2. 添加后闭UE编辑器,右键点击 .uproject 文件,选择 "Generate Visual Studio project files",重…...
