Last active
August 16, 2024 12:19
-
-
Save dslwind/93bc63546524d81c26ebd846251b00a1 to your computer and use it in GitHub Desktop.
使用OpenCV、Python以及FFmpeg逐帧处理视频
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
# 邪魔歪道之使用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
确实,但是n年前我也不知道咋想的,要搞的这么花里胡哨,算是折腾之后简单记录一下,实际上也不实用