参考:
https://ffmpeg.org/ffmpeg.html
https://aenes.com/post/ffmpeg.html
https://www.jianshu.com/p/91727ab25227
https://liuyujie714.com/33.html
FFmpeg是一个开源免费跨平台的视频和音频流方案,属于自由软件。 别看这东西只有几十Mb,但却是个能格式转换、剪辑、播放几乎无所不能的命令行软件。 就如格式工厂,其核心也是FFmpeg。 在专业领域常被部署在服务端,用以做云端视频相关服务。 如七牛云存储就是利用FFmpeg来完成各种格式转换的。 其官方网址为:FFmpeg.org。 在那里可以下载到各种主流电脑平台的FFmpeg程序。
FFmpeg主要包含四个程序:
ffmpeg 主要用于对媒体文件的内容进行操作,如格式转换等,是最主要的部件
ffplay 简易播放器,虽然没有什么UI,但是能播放各种格式的视频
ffprobe 用于探查媒体文件的属性,如meta标签等,可以选择输出JSON或XML格式
ffserver 流媒体服务器,不可多得的免费流媒体服务器软件,可用于架设视频直播
FFmpeg除了提供可运行程序,还提供一套libav多媒体处理C库,可集成到别的软件当中提供多媒体文件解码、编码等功能。
对于一些比较专业的命令,本文也不会过多叙述,因为那需要更多的多媒体文件基础知识才能理解。 另外注意,这里讲的是正统的FFmpeg,而不是Debian搞出来的分支LibAV,里面那个ffmpeg(Ubuntu内置的)。
以下命令主要针对视频文件操作
ffmpeg [global_options] {[input_file_options] -i input_url} ... {[output_file_options] output_url} ...
ffmpeg -i [输入文件名] [参数选项] -f [格式] [输出文件]
参数选项:
(1) -an: 去掉音频
(2) -vn: 去掉视频
(3) -acodec: 设定音频的编码器,未设定时则使用与输入流相同的编解码器。音频解复用在一般后面加copy表示拷贝
(4) -vcodec: 设定视频的编码器,未设定时则使用与输入流相同的编解码器,视频解复用一般后面加copy表示拷贝
(5) –f: 输出格式(视频转码)
(6) -bf: B帧数目控制
(7) -g: 关键帧间隔控制(视频跳转需要关键帧)
(8) -s: 设定画面的宽和高,分辨率控制(352*278)
(9) -i: 设定输入流
(10) -ss: 指定开始时间(0:0:05)
(11) -t: 指定持续时间(0:05)
(12) -b: 设定视频流量,默认是200Kbit/s
(13) -aspect: 设定画面的比例
(14) -ar: 设定音频采样率
(15) -ac: 设定声音的Channel数
(16) -r: 提取图像频率(用于视频截图)
(17) -c:v: 输出视频格式
(18) -c:a: 输出音频格式
(18) -y: 输出时覆盖输出目录已存在的同名文件
-vcoder 设定视频的编码器,未设定时则使用与输入流相同的编解码器
例如增加水印、增加gif、音频延迟等。一般是通过滤镜功能实现。
滤镜参考资料:
http://ffmpeg.org/ffmpeg-filters.html
http://blog.chinaunix.net/uid-26000296-id-4282537.html
查看视频参数信息
ffmpeg -i xxx.mp4
格式转换
ffmpeg最常用功能就是格式转换,在这里要特别提的是,音、视频文件格式有两个容器格式(如mov、flv)与编码格式(如H.264)。 很多人知道前者,却不知道后者,二者的关系与异同可在别处查到,不在此赘述。 简单的格式转换如下:
ffmpeg -i input.flv output.mp4
上面的命令就把一个flv文件转换成了一个mp4文件,其中-i xxx.xxx指定的输入文件,单独写的文件名指定输出文件路径。
一般FFmpeg会根据文件格式选择最合适的容器格式与编码格式,也可以手动指定。 常见的用例是需要一个保留alpha通道的视频,通常会使用mov容器格式,png编码格式,但是FFmpeg会默认使用H.264编码格式(不支持透明)。 如此命令如下:
ffmpeg -i input.flv -c:v png output.mov
想要知道自己的FFmpeg都支持哪些容器格式,使用命令ffmpeg -formats。 看都支持哪些编码格式,使用命令ffmpeg -codecs。
特别强调,如果处理文件不是图片,不要让输入与输出文件相同
ffmpeg -i input.mp4 -s 640x360 output.mp4
ffmpeg -i input.MP4 -vf scale=1920:1080 -c:v libx264 -c:a copy -crf 20 -preset slow output.mp4
Note:
Try adding -c:v libx264 -crf 20 -preset slow to the command.
-c:v libx264 tells it to use the libx264 encoder,
crf 20 uses the Constant Rate Factor quantiser (which paradoxially means variable bit rate, but constant quality) with a value of 20 (pretty good quality; lower is better quality / larger files, higher is crappier / smaller),
the slow preset is a shortcut for a bunch of encoder settings that means it puts a bit more effort into it than the default (medium).
You can tweak these settings, see the h.264 encoding guide for instructions on what knobs to twiddle.
And if you're using the audio as-is, add -c:a copy. That will do a straight copy of the audio stream without re-encoding.
上面的命令由-s 640x360定义了输出视频的画面尺寸会是640x360。
ffmpeg -i input.mp4 -ss 5 -t 10 output.mp4
上面的命令-ss 5指定从输入视频第5秒开始截取,-t 10指明最多截取10秒。 但是上面的命令可能会比较慢,更好的命令如下:
ffmpeg -ss 5 -i input.mp4 -t 10 -c:v copy -c:a copy output.mp4
上面的命令把-ss 5放到-i前面,与原来的区别是,这样会先跳转到第5秒在开始解码输入视频,而原来的会从开始解码,只是丢弃掉前5秒的结果。 而-c:v copy -c:a copy
标示视频与音频的编码不发生改变,而是直接复制,这样会大大提升速度,因为这样就不需要完全解码视频(视频剪切也不需要完全解码)。
ffmpeg -i test.avi -r 1 -f image2 image.jpeg //视频截图
ffmpeg -i input.avi -ss 0:1:30 -t 0:0:20 -vcoder copy -acoder copy output.avi //剪切视频 -r 提取图像频率, -ss 开始时间, -t 持续时间
内容反转(reverse)
// For video only
ffmpeg -i input-file.mp4 -vf reverse output.mp4
// For audio and video:
ffmpeg -i input-file.mp4 -vf reverse -af areverse output.mp4
ffmpeg -i %04d.jpg output.mp4
ffmpeg -i input.mp4 %04d.jpg
图片转换为视频
ffmpeg -f image2 -i out%4d.png -r 25 video.mp4
分离视频音频流
ffmpeg -i input_file -vcodec copy -an output_file_video //分离视频流
ffmpeg -i input_file -acodec copy -vn output_file_audio //分离音频流
第一行命令是把0001.jpg、0002.jpg、0003.jpg等编码成output.mp4,第二行则是相反把input.mp4变成0001.jpg……。 %04d.jpg表示从1开始用0补全的4位整数为文件名的jpg文件序列。 如果想要序列文件名为hello_00001.png等等的话,就是hello_%05d.png
ffmpeg -i input.mp3 -i %04d.jpg output.mp4
改变视频FPS
FFmpeg可以用于降低或提高视频的帧率,因为信息丢失不可逆法则,提高帧率只会简单地让某些帧的画面多重复一次或多次,所以提高帧率不会提高画质。
ffmpeg -i input.mp4 -r 30 output.mp4
上面的命令,不论原始视频帧率是多少,输出视频都会是30帧每秒。这种情况之下视频的时间轴不会变化,不会有慢动作或快动作的效果。
ffmpeg -r 30 -i input.mp4 output.mp4
上面这种条换顺序之后的写法比较有意思,-r 30放在输入文件之前表示影响的时输入文件,而非输出文件。 这样的命令表达的是,把输入文件当做30帧每秒,而忽略它的原始帧率。这样如果原来的视频FPS是25,被视作30之后,输出的视频会有快进的效果。 这个命令没有指定输出视频的FPS,默认会与输入文件保持一样,可以与本节第一个命令和在一起,写两个-r参数,第一个指定输入FPS,第二个指定 输出FPS即可既控制播放速度,又控制输出帧率。
把视频的前30帧转换成一个Animated Gif
ffmpeg -i input_file -vframes 30 -y -f gif output.gif
从视频中生成GIF图片
ffmpeg -i out.mp4 -t 10 -pix_fmt rgb24 out.gif
从视频截选指定长度的内容生成GIF图片
ffmpeg -ss 3 -t 5 -i input.mp4 -s 480*270 -f gif out.gif
H264视频首尾拼接
如果确定输入文件都是H264编码,且尺寸、帧率等都相同,先把源视频转换成用于直播的ts格式。 然后直接对多个ts文件进行文件级的拼接,然后在转换回到目标格式。这个过程中,不会发生格式转换,所以非常迅速。
ffmpeg -i q.mp4 -c copy -bsf h264_mp4toannexb q.ts
ffmpeg -i r.mp4 -c copy -bsf h264_mp4toannexb r.ts
ffmpeg -i "concat:q.ts|r.ts" -c copy -bsf aac_adtstoasc qr.mp4
制作gif动图
ffmpeg -i inmpt.mp4 output.gif
整个视频作为动图,此时得到的gif文件特别大。
ffmpeg -i input.mp4 -s 200*300 -r 15 output.gif
改变输出gif的辨率为200*300,设置帧数为15。
ffmpeg -ss 00:00:05 -t 30 -i input.mp4 -vf scale=100:50 -r 15 output.gif
截取输入视频从第5 s开始,直到30 s后的片段,指定100:50的宽和高。
自动字幕选择
ffmpeg -i C.mkv out1.mkv -c:s dvdsuub -an out2.mkv
输出out1.mkv文件接收字幕流,但是仅仅选择视频和音频流。输入文件C.mkv的字幕流是基于图片的,而ffmpeg默认选择的是基于文本的,因此字幕选择会失败。然而out2.mkv指定了字幕编码器(-c:s dvdsuub-DVD字幕解析),因此会被选中。-an选项会关闭out2.mkv的音频输出。
以下命令主要用于音频操作。有许多上面已经给出的视频操作,比如格式转换,剪切等也可适用于音频。大部分视频也都包含音频,所以下面的命令 往往可以与视频命令混合适用。
ffmpeg -i out.mp4 -vn -acodec copy out.aac
-i : 指定视频文件
-vn : 去除视频
-acodec copy : 音频编码处理方式
ffmpeg -i apple.mp4 -f mp3 -vn apple.mp3
参数解释:
-i 表示input,即输入文件
-f 表示format,即输出格式
-vn表示vedio not,即输出不包含视频
ffmpeg -i 3.mp4 -f wav -ar 16000 2-20.wav
参数说明
-i 3.mp4 // 输入的文件路径
-f wav ///输出wav格式的文件
-ar 16000 //采样率为16K
2-20.wav // 输出的文件名
抽取音频命令
ffmpeg -i 3.mp4 -vn -y -acodec copy 3.aac
ffmpeg -i 3.mp4 -vn -y -acodec copy 3.m4a
提取视频 (Extract Video)
ffmpeg -i Life.of.Pi.has.subtitles.mkv -vcodec copy –an videoNoAudioSubtitle.mp4
音视频合成命令
ffmpeg -i video.mp4 -i audio.wav -c:v copy -c:a aac -strict experimental output.mp4
# 如果视频中已经包含了音频,这个时候还可以替换视频中的音频,使用下面命令行
ffmpeg -i bns_blink.mp4 -i Avatar_Hezheng_demo1215_V2_denoise.wav -c:v copy -c:a aac -strict experimental -map 0:v:0 -map 1:a:0 bnsblink_matting.mp4
查看音视频文件信息命令
ffmpeg -i 3.mp4
ffmpeg -i 3.aac
ffmpeg -i 3.m4a
aac和ac3是音频编码格式。
aac全名是Advanced Audio Coding,后缀名一般为m4a、aac、mp4、mkv等,其中mp4、mkv为视频格式,采样率一般为44.1khz,码率一般是64kbps到192kbps,声道一般为双声道立体声。
ac3全称是(Dolby)Audio Codec 3,一般出现在DVD视频格式中,后缀名为vob,采样率一般为48khz,码率一般是192kbps到384kbps,声道为双声道或6声道。
ffmpeg -i out.mp4 -vcodec copy -acodec copy out.flv
-i: 输入文件
-vcodec copy : 视频的编解码处理方式
-acodec copy : 音频编码的处理方式
ffmpeg -i out.mp4 -an -vcodec copy out.h264
-i : 指定视频文件
-an : 去除音频
-vcodec copy : 视频编码处理方式
提取音乐中的封面图片
有些音乐文件包含专辑封面图片在里面,可以用如下命令简单取出。命令从字面意义来讲,就是把MP3格式转换成JPEG格式了。
ffmpeg -i input.mp3 cover.jpg
音乐有损压缩
在某些场合下,比如在给网站做背景音乐,或音乐网站提供预览版音乐时,会选择以牺牲音频质量为代价降低文件大小,让网络播放更顺畅。 一个典型的压缩命令如下:
ffmpeg -i input.mp3 -ac 1 -ar 32k -bit_rate:a 128k output.mp3
上面命令做了三件事情: - -ac 1 指定只保留一个声道,所有声道都融合成一个(这里有个FFmpeg的bug,输出音量会变小)。 - -ar 32k 表示采样率改为32000,通常的高保真音频都是48K左右,这个数值变小,会裁剪掉高音部分,32K会裁掉不少高音,不过普通人如果没个对比,听不出什么问题。 如果音频文件不是音乐,而是人声内容(比如广播),则可以打手一挥设置成22k或16k(电话是16k) - -bit_rate:a 128k设置的时音频比特率,如果是-bit_rate:v就成了视频比特率,128K表示,输出文件大概每秒钟的内容会有16KB左右的文件大小,需要至少128kbps的网络才能流畅播放 128K算是比较理想的比特率,文件小,音频质量损失又不是特别明显(对于普通人)
苹果系统的问题
经过FFmpeg处理的音频文件,在苹果的系统(包括OSX、iOS)以及苹果的播放器(iTunes、QuickTime)上往往会显示错误的长度时间。 这是个FFmpeg潜在的Bug,不过可以通过添加参数规避:
ffmpeg -i input.mp3 -write_xing 0 .... output.mp3
这样输出的文件在苹果产品上就会表现正常。原先不正常的文件,亦可以使用这个参数再经过一次ffmpeg处理来解决长度问题。
ffplay
ffplay是FFmpeg家族中的一个媒体文件播放器,可以播放许多种格式,不过与其说它是个完整的播放器,不如说这是个DEMO程序, 用来演示如何使用FFmpeg提供的解码接口来做播放器。它的命令很简单,通常播放一个正常的媒体文件只需要如下:
ffplay target.mp4
ffplay可以接受参数,相当于ffmpeg程序里-i之前的参数,用来改变输入文件的输入方式,比如通过改变FPS造成快进或慢镜头的效果。
ffprobe
ffprobe可以非常方便地用于检测媒体文件的一些隐藏信息,通常只有更深层次的专业人员才会用到。一个典型的命令如下:
ffprobe target.mp4 -show_format -show_streams -print_format json -loglevel fatal
可以用-threads n 来实施多线程的运算,充分利用多核cpu,例子如下:
ffmpeg -threads 2 -crf 20 -y -i ML-02.avi -strict experimental ML-02.mp4
sox官网
install
sudo apt-get install sox
查看音频信息
soxi sa1.wav
Input File : 'sa1.wav'
Channels : 1
Sample Rate : 16000
Precision : 16-bit
Duration : 00:00:06.46 = 103424 samples ~ 484.8 CDDA sectors
File Size : 207k
Bit Rate : 256k
Sample Encoding: 16-bit Signed Integer PCM
ls wav_folder | xargs -P 20 -i sox wav_folder/{} -c 1 -r 16000 wav_out/{}
sox是把wav的音量统一:
ls input_folder | xargs -P 20 -i sox --norm=-3 input_folder/{} output/{}
16000HZ 单声道:
ls wav_folder | xargs -P 20 -i sox wav_folder/{} -c 1 -r 16000 wav_out/{}
sox -V *.wav -n
sox sa1.wav -n stat
Samples read: 103424
Length (seconds): 6.464000
Scaled by: 2147483647.0
Maximum amplitude: 0.054565
Minimum amplitude: -0.050568
Midline amplitude: 0.001999
Mean norm: 0.003897
Mean amplitude: 0.000174
RMS amplitude: 0.005530
Maximum delta: 0.054108
Minimum delta: 0.000000
Mean delta: 0.000981
RMS delta: 0.002194
Rough frequency: 1010
Volume adjustment: 18.327
截取3s音频
截取时间开始点为 1 s处, 截取长度 为 3s
sox sa1.wav 3seconds.wav trim 1 3
连接两个wav
将sa1.wav , sa2.wav按照次序连接在一起,输出文件是connection.wav
sox sa1.wav sa2.wav connection.wav
混合重叠两个wav
sa1.wav 和 sa2.wav 被混合重叠在一起,音轨数目不需要一样,输出文件可能减少音轨,输出文件是不可逆的。
sox -m sa1.wav sa2.wav merge.wav
混合重叠两个采样率一样的wav
采用sequence或merge来联和文件时,输入文件的样本速率必须一样,否则联合不起来。例如,采用merge联合两个文件:实际上,-M参数主要用来将几个声道混合成一个联合声道,例如将两个单声道混合成立体声道
sox -M sa1.wav sa2.wav merge.wav
#.h264格式 视频抽帧
os.system('ffmpeg -i {} -q:v 2 -f image2 {}/%06d.jpg'.format(video_path, prefixname))
def yuv2jpeg(source_path):
'''
source_path
'''
old_shuffix = source_path.split('.')[-1]
out_path = source_path.replace(old_shuffix, 'jpeg')
os.system('ffmpeg -y -s 1920x1080 -i {} {}'.format(source_path, out_path))
def jpg2yuv(source_path):
'''
source_path
'''
old_shuffix = source_path.split('.')[-1]
out_path = source_path.replace(old_shuffix, 'yuv')
os.system('ffmpeg -i {} -s 1024x680 -pix_fmt yuvj420p {}'.format(source_path, out_path))
def jpg2nv12(source_path):
'''
source_path
'''
old_shuffix = source_path.split('.')[-1]
out_path = source_path.replace(old_shuffix, 'yuv')
os.system('ffmpeg -i {} -pix_fmt nv12 {}'.format(source_path, out_path))
ffmpeg -i test.mp4 -y -f mjpeg -ss 3 -t 1 test1.jpg
-f fmt 强迫采用格式fmt
-I filename 输入文件
-y 覆盖输出文件
-t duration 设置纪录时间 hh:mm:ss[.xxx]格式的记录时间也支持
-ss position 搜索到指定的时间 [-]hh:mm:ss[.xxx]的格式也支持
#视频抽帧
def VideoFraming(video_path, shuffix):
dir_path = '.'.join(video_path.split('.')[:-1])
# dir_path = video_path.split('.')[0] + '.' + video_path.split('.')[1]
if not os.path.exists(dir_path):
os.makedirs(dir_path)
files = os.listdir(dir_path)
prefixname = dir_path.split('/')[-1]
a, b, c = os.popen3('ffmpeg -i {}'.format(video_path))
out = c.read()
dp = out.index('Duration: ')
duration = out[dp+10:dp+out[dp:].index(',')]
hh, mm, ss = map(float, duration.split(":"))
#total time ss
total = (hh*60 + mm)*60 + ss
for i in range(9):
t = int((i + 1) * total / 10)
# ffmpeg -i test.mp4 -y -f mjpeg -ss 3 -t 1 test1.jpg
os.system('ffmpeg -i {} -y -f mjpeg -ss {} -t 1 {}/img_{}{}'.format(video_path, t, prefixname, i, shuffix))
import os
import sys
if __name__ == '__main__':
in_dir = sys.argv[1]
out_dir = sys.argv[2]
img_type = sys.argv[3] # yuv444, nv12, nv21, gray
img_name_list = os.listdir(in_dir)
for img_name in img_name_list:
img_path = os.path.join(in_dir, img_name)
img_name = img_path.split('/')[-1]
old_shuffix = img_name.split('.')[-1]
out_path = os.path.join(out_dir, img_name).replace(old_shuffix, 'yuv')
if img_type == 'yuv444':
os.system('ffmpeg -i {} -pix_fmt yuv444p {}'.format(img_path, out_path))
elif img_type == 'nv12':
os.system('ffmpeg -i {} -pix_fmt nv12 {}'.format(img_path, out_path))
elif img_type == 'nv21':
os.system('ffmpeg -i {} -pix_fmt nv21 {}'.format(img_path, out_path))
elif img_type == 'gray':
os.system('ffmpeg -i {} -pix_fmt gray {}'.format(img_path, out_path))
else:
assert False, 'not support img type: {}'.format(img_type)
#!/usr/bin/env python3
# -*- coding: utf-8 -*-
import cv2
import numpy as np
def yuv2bgr(filename, height, width, startfrm):
"""
:param filename: 待处理 YUV 视频的名字
:param height: YUV 视频中图像的高
:param width: YUV 视频中图像的宽
:param startfrm: 起始帧
:return: None
"""
fp = open(filename, 'rb')
framesize = height * width * 3 // 2 # 一帧图像所含的像素个数
h_h = height // 2
h_w = width // 2
fp.seek(0, 2) # 设置文件指针到文件流的尾部
ps = fp.tell() # 当前文件指针位置
numfrm = ps // framesize # 计算输出帧数
fp.seek(framesize * startfrm, 0)
for i in range(numfrm - startfrm):
Yt = np.zeros(shape=(height, width), dtype='uint8', order='C')
Ut = np.zeros(shape=(h_h, h_w), dtype='uint8', order='C')
Vt = np.zeros(shape=(h_h, h_w), dtype='uint8', order='C')
for m in range(height):
for n in range(width):
Yt[m, n] = ord(fp.read(1))
for m in range(h_h):
for n in range(h_w):
Ut[m, n] = ord(fp.read(1))
for m in range(h_h):
for n in range(h_w):
Vt[m, n] = ord(fp.read(1))
img = np.concatenate((Yt.reshape(-1), Ut.reshape(-1), Vt.reshape(-1)))
img = img.reshape((height * 3 // 2, width)).astype('uint8') # YUV 的存储格式为:NV12(YYYY UV)
# 由于 opencv 不能直接读取 YUV 格式的文件, 所以要转换一下格式
bgr_img = cv2.cvtColor(img, cv2.COLOR_YUV2BGR_NV12) # 注意 YUV 的存储格式
cv2.imwrite('yuv2bgr/%d.jpg' % (i + 1), bgr_img)
print("Extract frame %d " % (i + 1))
fp.close()
print("job done!")
return None
if __name__ == '__main__':
_ = yuv2bgr(filename='xxx.yuv', height=1080, width=1920, startfrm=0)
//剪切视频
ffmpeg -ss 0:1:30 -t 0:0:20 -i input.mp4 -vcodec copy -acodec copy output.mp4
// -ss 开始时间; -t 持续时间
# 获取视频时长信息
from moviepy.editor import VideoFileClip
clip = VideoFileClip("my_video.mp4")
print( clip.duration ) # seconds
from pydub import AudioSegment
filePath = '.../'
# 操作函数
def get_wav_make(dataDir):
sound= AudioSegment.from_wav(dataDir)
duration = sound.duration_seconds * 1000 # 音频时长(ms)
begin = 0
end = int(duration/2)
cut_wav = sound[begin:end] #以毫秒为单位截取[begin, end]区间的音频
cut_wav.export(filePath+ 'test.wav', format='wav') #存储新的wav文件
# or
import wave
f = wave.open(data_dir+ pdwav.iloc[i]['SID']+'.wav')
rate = f.getframerate()
frames = f.getnframes()
duration = frames/float(rate) #单位为s
一、YUV 简介
YUV:是一种颜色编码方法,常使用在各个视频处理组件中
Y’UV, YCbCr, YPbPr等专有名词都可以称为 YUV,彼此有重叠
Y表示明亮度(单取此通道即可得灰度图),U和V则是色度、浓度
主流的采样方式有三种,YUV4:4:4,YUV4:2:2,YUV4:2:0
可以根据其采样格式来从码流中还原每个像素点的 YUV 值,进而通过 YUV 与 RGB 的转换公式提取出每个像素点的 RGB 值,然后显示出来
YUV4:2:0 数据在内存中的长度是 3 / 2 * heigth * width,是 RGB24(heigth * width * 3) 格式视频数据内存的一半
二、YUV420(NV12、NV21、I420、YV12)
NV12、NV21 的存储格式为 Y 平面,UV 打包,即:Y 信息存储在一个数组中,UV 信息存储在一个矩阵中。
不同点在于 UV 的排列顺序
NV12: YYYYYYYY UVUV => YUV420SP
NV21: YYYYYYYY VUVU => YUV420SP
I420、YV12 三个分量均为平面格式,即:分别存放在三个 Byte 型数组中
I420: YYYYYYYY UU VV => YUV420P
YV12: YYYYYYYY VV UU => YUV420P
假设一个分辨率为8X4的 YUV 图像,它们的格式如下图:
三、读取 YUV(NV12) 视频文件并保存
#!/usr/bin/env python3
# -*- coding: utf-8 -*-
import cv2
import numpy as np
def yuv2bgr(filename, height, width, startfrm):
"""
:param filename: 待处理 YUV 视频的名字
:param height: YUV 视频中图像的高
:param width: YUV 视频中图像的宽
:param startfrm: 起始帧
:return: None
"""
fp = open(filename, 'rb')
framesize = height * width * 3 // 2 # 一帧图像所含的像素个数
h_h = height // 2
h_w = width // 2
fp.seek(0, 2) # 设置文件指针到文件流的尾部
ps = fp.tell() # 当前文件指针位置
numfrm = ps // framesize # 计算输出帧数
fp.seek(framesize * startfrm, 0)
for i in range(numfrm - startfrm):
Yt = np.zeros(shape=(height, width), dtype='uint8', order='C')
Ut = np.zeros(shape=(h_h, h_w), dtype='uint8', order='C')
Vt = np.zeros(shape=(h_h, h_w), dtype='uint8', order='C')
for m in range(height):
for n in range(width):
Yt[m, n] = ord(fp.read(1))
for m in range(h_h):
for n in range(h_w):
Ut[m, n] = ord(fp.read(1))
for m in range(h_h):
for n in range(h_w):
Vt[m, n] = ord(fp.read(1))
img = np.concatenate((Yt.reshape(-1), Ut.reshape(-1), Vt.reshape(-1)))
img = img.reshape((height * 3 // 2, width)).astype('uint8') # YUV 的存储格式为:NV12(YYYY UV)
# 由于 opencv 不能直接读取 YUV 格式的文件, 所以要转换一下格式
bgr_img = cv2.cvtColor(img, cv2.COLOR_YUV2BGR_NV12) # 注意 YUV 的存储格式
cv2.imwrite('yuv2bgr/%d.jpg' % (i + 1), bgr_img)
print("Extract frame %d " % (i + 1))
fp.close()
print("job done!")
return None
if __name__ == '__main__':
_ = yuv2bgr(filename='xxx.yuv', height=1080, width=1920, startfrm=0)
ord() 函数是 chr() 函数(对于8位的ASCII字符串)或 unichr() 函数(对于Unicode对象)的配对函数,它以一个字符(长度为1的字符串)作为参数,返回对应的 ASCII 数值,或者 Unicode 数值,如果所给的 Unicode 字符超出了你的 Python 定义范围,则会引发一个 TypeError 的异常。
ch() , unichr() , ord()
①chr()函数用一个范围在range(256)内的整数作参数,返回一个对应的字符。
>>>chr(65)
'A'
②unichr()跟chr()一样,只不过返回的是Unicode字符。
>>>unichr(12345)
u'u3039
③超出范围的报ValueErrro异常
④ord()函数是chr()或unichr()的配对函数,以字符作为参数,返回ASCII数值,或者Unicode数值。
>>>ord('a')
97
如果YUV文件需要转换为PIL的image格式,那么只需在main函数中,进行稍微的处理.
YY=data[0][0]
im=Image.frombytes('L',(720,1280),YY.tostring())
另外,如果PIL的image想转换为opencv的mat格式,只需将PIL转换为矩阵的形式.
im_array = np.array(im)
# 也可以用 np.asarray(im) 区别是 np.array() 是深拷贝,np.asarray() 是浅拷贝
yuv格式图片
https://blog.csdn.net/wenmingzheng/article/details/88373192