【计算机视觉】使用ffmpeg抽帧和压缩图片

目录

1、首先conda安装ffmpeg

2、使用ffmpeg从视频中截取图像帧

ffmpeg每隔5秒切分视频为图片

使用ffmpeg提取视频中的图像(根据帧间隔、时间间隔)

使用ffmpeg从视频中截取图像帧(最简单实用的视频抽帧,一句命令)

3、ffmpeg图片压缩

3.1、使用ffmpeg进行webp图片压缩,ffmpeg的帮助信息查看方法

4、补充

4.1、使用 PyAV 保存关键帧

4.2、python实现视频关键帧提取(基于帧间差分)

ffmpeg源码,社区:https://ffmpeg.org/about.html


1、首先conda安装ffmpeg

conda install -c conda-forge ffmpeg

2、使用ffmpeg从视频中截取图像帧

为了对视频做一些处理,需要提取视频中的图像,根据不同需求有时候可能需要按一定时间间隔帧间隔来提取视频中的帧,目的是为了处理提高效率和减少内存的占用,使用OpenCV也可以完成这一功能,不过相对于FFmpeg来说,处理视频的效率要慢不少,对于数量比较大的视频处理建议使用FFmpeg来进行。

如果想要隐藏ffmepg运行过程的命令,只需要在调用ffmpeg.exe时,参数后面添加 -loglevel quiet 即可。

  • ffmpeg每隔5秒切分视频为图片

ffmpeg -i cv.mp4 -vf fps=0.2 F:\ffmpegTest\img\out%d.png

fps为1时按一秒一帧切分,按5秒一帧来切,1/5 = 0.2即可 

 建议用这个?

ffmpeg -i /data/video_1.mp4 -f image2 -vf fps=fps=1/60 -qscale:v 2 /data/mp4-%05d.jpeg

使用ffmpeg提取视频中的图像(根据帧间隔、时间间隔)

  • 按间隔(每隔几帧)帧来提取视频中的帧

ffmpeg -i 10021502.mp4 -vf "select=between(n\,84\,208)*not(mod(n\,4))" -vsync 0 ./pkl/image_%05d.jpg

主体是一个select 的过滤语句:

其中:between(n,*) 是指 从第几帧到第几帧之间进行提取...

   not(mode(n\, K))是指每隔几帧输出一帧。

  • 使用between可以指定需要提取开始帧和结束帧的位置,如果想要对视频中所有的帧进行间隔采样,命令如下

command_extract = "select=(gte(n\,%d))*not(mod(n\,%d))"%(60,60)
com_str = 'ffmpeg -i ' + video_path + ' -vf "%s" -vsync 0 '%command_extract + save_jpg_tmp_path + '/%06d.jpg'

command_extract中的60指的是间隔的帧数video_path指的是视频的路径save_jpg_tmp_path指的是导出视频中的图片存放的路径。

我的代码

command_extract = "select=(gte(n\,{}))*not(mod(n\,{}))".format(frame_interval, frame_interval) 
# 假如 frame_interval是10,
# 意思是:从第10帧开始(第10帧保留),每隔10帧抽取一帧。则实现了按帧间隔抽帧的功能。
# 具体 select 的含义看第5部分,gte是大于的意思~
frame_mode = "ffmpeg -i {} -vf \"{}\" -vsync 0 {}-%05d.jpg".format(inpath, command_extract, outpath)
  • 按时间间隔取帧

ffmpeg -i -filter:v "select=(gte(t\,120))*(isnan(prev_selected_t)+gte(t-prev_selected_t\,120))" -frames:v 1 -y tile.png

上面命令中的120的单位是s,指每120s提取视频中的一帧图像。

以下的ffprobe还没有搞懂怎么用。

  • 通过FFmpeg获取视频的总帧数

ffprobe -select_streams v -show_entries stream=nb_frames -of default=nk=1:nw=1 -v quiet

  • 通过FFmpeg获取视频的总时长

ffprobe -select_streams v -show_entries stream=duration -of default=nk=1:nw=1 -v quiet

使用ffmpeg从视频中截取图像帧(最简单实用的视频抽帧,一句命令)

  • 最佳方式,第二种方法抽帧图片画质很差,这里提供更好的命令,提高分辨率:

ffmpeg -i /data/video_1.mp4 -f image2  -vf fps=fps=1/60 -qscale:v 2 /data/mp4-%05d.jpeg

使用ffmpeg从视频中截取图像帧

  • 普通的命令:(画质差)

ffmpeg -i inputfile.avi -r 1 -f image2 image-%05d.jpeg

-r 指定抽取的帧率,即从视频中每秒钟抽取图片的数量。1代表每秒抽取一帧,5就表示一秒抽5张。

-f 指定保存图片使用的格式,可忽略。

image2: 图像解析模式

image-%05d.jpeg,指定文件的输出名字,可以加个路径。

ffmpeg -i inputfile.avi -r 1 -s 4cif -f image2 image-%05d.jpeg

4cif 代表帧的尺寸为705x576。

ffmpeg -i inputfile.avi -r 1 -t 4 -f image2 image-%05d.jpeg

-t 代表持续时间,单位为秒。

ffmpeg -i inputfile.avi -r 1 -ss 01:30:14 -f image2 image-%05d.jpeg

-ss 指定起始时间。

ffmpeg -i inputfile.avi -r 1 -ss 01:30:14 -vframes 120 4cif -f image2 image-%05d.jpeg

-vframes 指定抽取的帧数

  • 视频旋转

ffmpeg -i input.mp4 -c copy -metadata:s:v:0 rotate=90 output.mp4

  • 批量视频抽帧
#!/usr/bin/env bash
videos_root=/data/videos
save_root=/data/video_imgs/Image_fps50/
for video in $videos_root/*;
do
echo $video
save_dir=$save_root$(basename $video .avi)
if [ ! -d $save_dir ];then
mkdir $save_dir
fi
ffmpeg -i $video -f image2  -vf fps=fps=50 -qscale:v 2 $save_dir/mp4-%05d.jpeg
 
done

3、ffmpeg图片压缩

3.1、使用ffmpeg进行webp图片压缩,ffmpeg的帮助信息查看方法

一些参数:

-lossless 是设置无损压缩

-preset 是几个预设的参数

-quality 这个就是主要的参数了,控制压缩率

调整图片大小用-vf scale=iw/3:ih/3,可以指定和原来的比例,也可以指定明确的像素值比如1280:720。

e.g. 单纯调整图片大小,无损压缩把图片宽高缩小3倍iw/3:ih/3。

ffmpeg -i input.png  -vf scale=iw:ih -codec libwebp -lossless 0 -quality 75 out.webp

  • 选择无损压缩时,“-lossless -q 100” 是最佳方案,注意:cwebp 仅仅对png格式的图片使用无损压缩时,会有较为高效的压缩率和图片质量
  • 选择有损压缩时,“-q 75”是最佳方案(图片质量与体积大小达到均衡)建议其他格式图片使用有损压缩
  • 无论何种压缩参数,加上“-m 6”都能使得输出的 WebP 图片进一步减少体积,量级是1%~2%,但是会增加耗时
  • 非png格式的图片选择无损压缩时,“-lossless -q 100” 时编码时间长,图片质量反而极高可能变大,编码时间时间很长,cpu使用率飙升跑满,不建议使用
  • 建议选择有损压缩时,“-q 75”是最佳方案(图片质量与体积大小达到均衡)建议使用

4、补充

4.1、使用 PyAV 保存关键帧

保存关键帧

对于一个视频序列来说并不是所有帧都一样,因为视频编码在进行帧间预测时会出现相互参考的情况,如果一帧的参考帧丢失或损坏了那么这一帧就无法正确解码,所以对于那些用于被参考的帧就相对更重要了。

在av.video.frame.VideoFrame类中有一个属性key_frame用以表示该帧是否是关键帧。

import av
import av.datasets
​
container = av.open(path_to_video)
# Signal that we only want to look at keyframes.
stream = container.streams.video[0]
stream.codec_context.skip_frame = 'NONKEY'
​
for frame in container.decode(stream):
    # We use `frame.pts` as `frame.index` won't make must sense with the `skip_frame`.
    frame.to_image().save(
        'night-sky.{:04d}.jpg'.format(frame.pts),
        quality=80,
    )

在以上代码中跳过了非关键帧,将所有关键帧保存下来。

4.2、python实现视频关键帧提取(基于帧间差分)

今天我实现了一种比较通用的关键帧提取算法,它基于帧间差分。

算法的原理很简单:我们知道,将两帧图像进行差分,得到图像的平均像素强度可以用来衡量两帧图像的变化大小。因此,基于帧间差分的平均强度,每当视频中的某一帧与前一帧画面内容产生了大的变化,我们便认为它是关键帧,并将其提取出来。

算法的流程简述如下:

首先,我们读取视频,并依次计算每两帧之间的帧间差分,进而得到平均帧间差分强度。

然后,我们可以选择如下的三种方法的一种来提取关键帧,它们都是基于帧间差分的:

1、使用差分强度的顺序

我们对所有帧按照平均帧间差分强度进行排序,选择平均帧间差分强度最高的若干张图片作为视频的关键帧。

2、使用差分强度阈值

我们选择平均帧间差分强度高于预设阈值的帧作为视频的关键帧。

3、使用局部最大值

我们选择具有平均帧间差分强度局部最大值的帧作为视频的关键帧。

这种方法的提取结果在丰富度上表现更好一些,提取结果均匀分散在视频中。

需要注意的是,使用这种方法时,对平均帧间差分强度时间序列进行平滑是很有效的技巧。它可以有效的移除噪声来避免将相似场景下的若干帧均同时提取为关键帧。

这里比较推荐使用第三种方法来提取视频的关键帧,代码~

【计算机视觉】使用ffmpeg抽帧和压缩图片_第1张图片

5、 ffmpeg参数解析

http://ffmpeg.org/pipermail/ffmpeg-devel/2012-December/135576.html

-# select all frames in input
-select
-
-# the above is the same as:
-select=1
-
-# skip all frames:
-select=0
-
-# select only I-frames
-select='eq(pict_type\,I)'
-
-# select one frame every 100 ,每100帧抽一帧
-select='not(mod(n\,100))'
-
-# select only frames contained in the 10-20 time interval
-select='gte(t\,10)*lte(t\,20)'
-
-# select only I frames contained in the 10-20 time interval
-select='gte(t\,10)*lte(t\,20)*eq(pict_type\,I)'
-
-# select frames with a minimum distance of 10 seconds
-select='isnan(prev_selected_t)+gte(t-prev_selected_t\,10)'
-
-# use aselect to select only audio frames with samples number > 100
-aselect='gt(samples_n, 100)'

https://video.stackexchange.com/questions/25540/export-an-image-frame-with-ffmpeg

-vf select=gte(n\,360)

-vf 声明视频过滤器链,即要应用于视频流的一个或多个过滤器的序列。

select过滤器允许用户保留或丢弃各个帧,基于提供给它的表达的结果。对于每一帧,都会对表达式求值。如果结果为零,则丢弃该帧,否则保留该帧。Here, the expression is gte(n\,360) which checks if the index i.e. presentation serial number, of the current frame is equal to or greater than 360. So, this expr will result in all frames with index 0 to 359 to be discarded, and all frames with a higher index to be selected.在这里,表达式gte(n\,360)将检查当前帧的索引(即表示序列号)是否等于或大于360。因此,此expr将导致所有索引为0到359的帧被丢弃,而所有带有a的帧将被丢弃。选择较高的索引。)

有关可用功能列表,请参见https://ffmpeg.org/ffmpeg-utils.html#Expression-Evaluation。

请参阅文档以了解选择过滤器,以查看可以测试的变量列表。

由于-vframes 1存在ffmpeg,因此从选择过滤器收到一帧后,ffmpeg将停止。

select的使用方法

https://astraywu.github.io/2019/04/27/ffmpeg/ffmpeg%E5%B8%B8%E7%94%A8%E5%91%BD%E4%BB%A4/

# 跳过前100帧
ffmpeg -s 1920x1080 -pix_fmt yuv420 -i input.yuv -vf "select=gte(n\,100)" output.yuv
ffmpeg -i input_file -vf scale="gte(n\,5)" output_file

# 截取[200, 300]帧
ffmpeg -s 1920x1080 -pix_fmt yuv420 -i input.yuv -vf "select=between(n\,200\,300)" output.yuv

# 间隔几帧截取,转成图片
ffmpeg -i example.mov -f image2 -vsync vfr -vf "select='not(mod(n,17)')" -frames:v 20 preview-%d.jpeg

6、抽取视频关键帧(IPB帧)

FFmpeg视频抽帧那些事 - 阿水的文章 - 知乎

 

一般情况下关键帧`I帧`是信息最多的帧,也是用途最多的帧。在视频检索和视频分类任务中一般都借助`I帧`来完成,在一个时长60s的视频中,可以抽取得到16个I帧、84个P帧和184个B,I帧数量少包含的信息却是最多的。

  • 视频关键帧(Video Keyframes)是用于视频压缩和视频编解码的帧,视频关键帧是包含了完整信息的帧,其他的非关键帧将会使用与关键帧的差值进行压缩。视频帧具体可以分为IPB帧三种:

  • I帧表示关键帧,是最完整的帧画面,一般视频封面都选择I帧;
  • P帧单预测帧,利用之前的I帧或P帧,采用运动预测的方式进行帧间预测编码;
  • B帧双向预测帧,利用双向帧进行预测编码;
  • 使用ffprobe提取出IPB帧的时间:
ffprobe -i 666051400.mp4 -v quiet -select_streams v -show_entries frame=pkt_pts_time,pict_type
  • 抽取IPB帧到jpg图片:
# 抽取I帧
ffmpeg -i 666051400.mp4 -vf "select=eq(pict_type\,I)"  -vsync vfr -qscale:v 2 -f image2 ./%08d.jpg

# 抽取P帧
ffmpeg -i 666051400.mp4 -vf "select=eq(pict_type\,P)"  -vsync vfr -qscale:v 2 -f image2 ./%08d.jpg

# 抽取B帧
ffmpeg -i 666051400.mp4 -vf "select=eq(pict_type\,B)"  -vsync vfr -qscale:v 2 -f image2 ./%08d.jpg

由于ffmpeg抽取帧并无法按照时间戳来命名,需要手动将ffprobe提取出来的帧时间与抽取帧的图片进行对应重命名。关键帧具体的定义和用途可以参考:https://en.wikipedia.org/wiki/K


参考:内容来源都是每一个标题的链接(*^▽^*)

视频压缩--快速压缩方法

 

你可能感兴趣的:(计算机视觉)