Skip to content

Instantly share code, notes, and snippets.

@dslwind
Last active April 9, 2024 09:24
Show Gist options
  • Save dslwind/93bc63546524d81c26ebd846251b00a1 to your computer and use it in GitHub Desktop.
Save dslwind/93bc63546524d81c26ebd846251b00a1 to your computer and use it in GitHub Desktop.
使用OpenCV、Python以及FFmpeg逐帧处理视频
# 邪魔歪道之使用OpenCV精准切割视频
## ffmpeg 切割视频
使用ffmpeg直接切割视频的命令
```
ffmpeg -i test.mp4 -ss 00:00:00 -t 00:00:30 -c:v copy -c:a copy output.mp4
```
或者
```
ffmpeg -i test.mp4 -ss 00:00:00 -to 00:00:30 -c:v copy -c:a copy output.mp4
```
其中
```
-i 指定输入文件
-ss 指定开始时间
-to 指定结束时间
-t 指定需要截取的时长
```
上述命令将从视频开头截取一段30s的视频。但是,如果截取的开始时间和结束时间不是关键帧,那么得到的视频开头画面通常会卡住,而声音是正常的。这时候可以对视频流进行重编码,命令如下:
```
ffmpeg -ss 00:00:00 -to 00:00:30 -i test.mp4 -c:v h264 -c:a copy output.mp4
```
上述方法基本上可以满足大部分人的需求,时间精确到了秒。对于强迫症来说可能还不够准确,需要精确到帧。
## OpenCV精准切割视频
首先,使用ffmpeg粗略的切割视频
```
ffmpeg -ss START -to END -i INPUT -c:v copy -c:a copy cut.mp4
```
这里START建议设置在目标开始时间前几秒,END设置在目标结束时间后几秒,以保证待截取的所有视频帧都包含在输出文件内。
接下来使用Python+OpenCV库实现以帧为单位的精准切割视频。首先将cut.mp4逐帧保存为图片,这里可以使用OpenCV或者ffmpeg生成(在当前目录下新建一个`frames`文件夹,否则无法保存):
```
ffmpeg -i cut.mp4 frames/frames_%05d.jpg
```
或者使用OpenCV
```python2
from __future__ import absolute_import
from __future__ import division
from __future__ import print_function
import cv2
cap = cv2.VideoCapture('cut.mp4')
success, image = cap.read()
count = 0
success = True
while success:
# save frame as JPEG file
cv2.imwrite("frames/frame%d.jpg" % count, image)
success, image = cap.read()
print('Read a new frame: ', count)
count += 1
```
在frames文件夹下,我们可以看到视频帧都转换成了图像文件。接下来,找出需要截取的视频开始帧和结束帧对应的文件,并记下编号(下一步用到)。
```python2
from __future__ import absolute_import
from __future__ import division
from __future__ import print_function
import cv2
cap = cv2.VideoCapture('cut.mp4')
outpath = 'cut_1.mp4'
start_frame = 372 # 开始帧
end_frame = 1230 # 结束帧
# 获取视频分辨率
size = (int(cap.get(cv2.CAP_PROP_FRAME_WIDTH)), int(cap.get(cv2.CAP_PROP_FRAME_HEIGHT)))
# 输出文件编码,Linux下可选X264
fourcc = cv2.VideoWriter_fourcc(*'MJPG')
# 视频帧率
fps = cap.get(cv2.CAP_PROP_FPS)
success, image = cap.read()
count = 0
success = True
# 从视频开头获取每一帧,直到到达开始帧
while success:
success, image = cap.read()
count += 1
if (count==start_frame):
success = False
# 开始帧的时间(单位ms),相当于ffmpeg的ss参数
ss = int(cap.get(cv2.CAP_PROP_POS_MSEC))
# 输出
out = cv2.VideoWriter(outpath, fourcc, fps, size)
# 读取开始帧到结束帧的每一帧并写入新视频
while (count<end_frame):
success, image = cap.read()
out.write(image)
count+=1
# 结束帧的时间,相对于ffmpeg的to参数
to = int(cap.get(cv2.CAP_PROP_POS_MSEC))
print(ss, to)
cap.release()
out.release()
```
以上程序将生产一个精确到帧的视频文件,但是没有声音,而ffmpeg在分割音频的时候是很精确的。接下来将用ffmpeg来切割音频,并和上述视频合并。
## ffmpeg切割音频并合并视频
```
ffmpeg -i cut_1.mp4 -i cut.mp4 -ss TIME_OFF -to TIME_STOP -c copy output.mkv
```
这里`TIME_OFF`和`TIME_STOP`与前一节的`ss`和`to`相对应,但是ffmpeg里面时间单位是s,因此应先除以`1000`。
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment