音视频处理工具
Project description
bm-video-tools
针对图片、视频、音频常见操作的简易封装
快速开始
1.依赖
- ffmpeg 4.4+
- python >= 3.8
2.安装
pip install bm-video-tools
3.基础使用
(1) 操作类
该类是对ffmpeg
指令相关选项的封装
from bm_video_tools import Operate
op = Operate()
裁剪
方法:cut(width, height, x, y)
参数:
- width: 裁剪宽度
- height: 裁剪高度
- x: 裁剪的坐标点(默认左上角)
- y: 裁剪的坐标点(默认左上角)
返回:Self
示例:
op = Operate().cut(300, 300)
如果不指定高度但指定坐标点,则裁剪坐标点到图像右下角的区域
op = Operate().cut(x=100, y=100)
缩放
方法:scale(width, height)
参数:
- width: 缩放的宽度
- height: 缩放的高度
返回:Self
示例:
op = Operate().scale(200, 100)
如果指定宽度和高度,则按照指定的值进行缩放
如果指定其中一个值,另一个为-1
,则按照指定值等比例缩放
op = Operate().scale(200, -1) # 缩放宽度为200,高度等比例缩放
旋转
方法:rotate(unit)
参数:
- unit: 旋转角度
返回:Self
示例:
op = Operate().rotate(1) # 旋转90度
op = Operate().rotate(2) # 旋转180度
op = Operate().rotate(3) # 旋转270度
op = Operate().rotate(-1) # 旋转270度(逆时针旋转90度)
翻转
方法:turn(h, v)
参数:
- h: 水平翻转
- v: 垂直翻转
返回:Self
示例:
op = Operate().turn(v=True)
拆分
方法:split(start, end, second)
参数:
- start: 开始时间
- end: 结束时间
- second: 截取时长(秒)
返回:Self
示例:
op = Operate().split(5, second=5)
剔除
方法:eliminate(a, v)
参数:
- a: 剔除音频流
- v: 剔除视频流
返回:Self
示例:
op = Operate().eliminate(a=True)
倒放
方法:reverse()
参数:无
返回:Self
示例:
op = Operate().reverse()
倍速
方法:speed(rate)
参数:
- rate: 速率(范围0.5~2.0)
返回:Self
示例:
op = Operate().speed(0.5)
修改参数
方法:params(...)
参数:
- pix_fmt: 像素格式
- codec_v: 视频编码器
- fps: 视频帧速率
- frames_v: 视频总帧数
- codec_a: 音频编码器
- sample: 音频采样率
- frames_a: 音频总帧数
返回:Self
示例:
op = Operate().params(fps=30)
理论上,音视频流相关格式参数都可修改,当前只列举了一些常见的修改参数,该方法会根据后续实际业务不断更新
注意:
Operate
的成员方法返回的都是实例对象自身,这意味着这些方法可以链式调用:
op = Operate().rotate(2).turn(h=True).rotate(1).reverse()
op = Operate().split(5, 15).cut(200, 200)
但是,这些方法其实并没有实质性的作用,需与下面几个类共同使用
(2) 图片
from bm_video_tools import Image
img = Image(input_path)
参数:
- input_path:文件输入路径
执行基础操作
方法:run(op, output_path)
参数:
- op: Operation实例对象
- output_path:输出路径
返回:Image
示例:
img = Image('xxx')
op = Operate().cut(300, 300, 0, 0)
await img.run(op, output_path='xxx')
查看图片信息
方法:get_info()
参数:无
返回:dict
示例:
info = await img.get_info()
{
'streams': [ // 媒体流
{
'index': 0, // 媒体流索引
'codec_name': 'mjpeg', // 编码器名
'codec_long_name': 'Motion JPEG', // 编码器全名
'profile': 'Baseline', // 编码器配置文件
'codec_type': 'video', // 流的类型
'codec_tag_string': '[0][0][0][0]', // 流使用的编码器的标识符字符串
'codec_tag': '0x0000', // 流使用的编码器的标识符整数值
'width': 1024, // 流的宽度
'height': 782, // 流的高度
'coded_width': 1024, // 流编码时的宽度
'coded_height': 782, // 流编码时的高度
'closed_captions': 0, // 流是否包含了有关视频注释和字幕的信息(这个参数只对视频流有效)
'film_grain': 0, // 流是否添加了电影颗粒噪点
'has_b_frames': 0, // 流是否存在B帧
'sample_aspect_ratio': '1:1', // 采样宽高比
'display_aspect_ratio': '512:391', // 显示宽高比
'pix_fmt': 'yuvj420p', // 像素格式
'level': -99, // 流的级别
'color_range': 'pc', // 颜色范围(tv: 表示颜色采用了 16-235 范围,pc: 表示颜色采用了 0-255 范围)
'color_space': 'bt470bg', // 颜色空间
'chroma_location': 'center', // 色度坐标的放置位置
'refs': 1, // 编码器中参考图像的数量
'r_frame_rate': '25/1', // 码率的分子和分母
'avg_frame_rate': '25/1', // 平均帧率
'time_base': '1/25', // 时间戳的基本时间单位
'start_pts': 0, // 首个PTS
'start_time': '0.000000', // 流的开始时间
'duration_ts': 1, // 流总时长的时间戳
'duration': '0.040000', // 流总时长的秒数
'bits_per_raw_sample': '8', // 原始采样数据每个样本所占用的位数(音频通常为16、24,视频通常为8、10、12)
'disposition': { // 流状态信息
'default': 0,
'dub': 0,
'original': 0,
'comment': 0,
'lyrics': 0,
'karaoke': 0,
'forced': 0,
'hearing_impaired': 0,
'visual_impaired': 0,
'clean_effects': 0,
'attached_pic': 0,
'timed_thumbnails': 0,
'captions': 0,
'descriptions': 0,
'metadata': 0,
'dependent': 0,
'still_image': 0
}
}
],
'format': { // 容器格式信息
'filename': './asset/image1.jpg', // 输入文件的路径或网络URL地址
'nb_streams': 1, // 媒体文件中流的数量
'nb_programs': 0, // 媒体文件中程序的数量
'format_name': 'image2', // 媒体封装格式的名称
'format_long_name': 'image2 sequence', // 媒体封装格式的全名
'start_time': '0.000000', // 从媒体中哪个时间点开始进行分析
'duration': '0.040000', // 媒体的总时长
'size': '217901', // 媒体文件的大小
'bit_rate': '43580200', // 媒体文件中平均每秒使用的总比特率
'probe_score': 50 // 判定该媒体文件是正确格式的可能性得分(分值范围为 0~100)
}
}
人物抠图
方法:remove(output_path, model_name[, op])
参数:
- output_path: 输出路径
- (可选)model_name: 模型名称(默认u2net)
- (可选)op: Operation实例对象
返回:Image
示例:
img = Image('xxx')
await img.remove('xxx')
人物抠图功能是基于backgroundremover项目实现的
可选模型:u2netp、u2net、u2net_human_seg
请从项目中下载模型至操作系统当前用户的
.bm-video-tools
文件夹,否则无法使用抠图功能
有时候,我们需要对媒体资源进行一些前置操作,然后再执行相应方法,例如:
img = Image('xxx')
op = Operate().cut(200, 200)
# 使用临时文件执行操作,对操作后的临时文件进行抠图
temp_file = tempfile.NamedTemporaryFile(delete=False, suffix='.png')
temp_file_path = temp_file.name.replace("\\", "/")
temp_file.close()
try:
img_res = await img.run(op, output_path=temp_file_path)
await img_res.remove('xxx')
del img_res
finally:
if os.path.exists(temp_file_path):
os.remove(temp_file_path)
但对于使用者来说,并不需要关注这些中间状态以及临时文件,上述代码等同于:
img = Image('xxx')
op = Operate().cut(200, 200)
await video.remove("xxx", op=op) # 注意: 该Operate实例对象是对输入文件进行处理,而非输出文件
人脸检测
方法:face_detection(model_name[, op])
参数:
- (可选)model_name: 模型名称(默认haarcascade_frontalface_alt2)
- (可选)op: op: Operation实例对象
返回:bool
示例:
img = Image(image1_path)
await img.face_detection()
人脸检测使用的是OpenCV的检测方法
可选模型:haarcascade_frontalface_alt2、haarcascade_fullbody等
和人物抠图一样,需将xml文件保存至操作系统当前用户的
.bm-video-tools
文件夹,否则无法正常使用
(3) 视频
from bm_video_tools import Video
video = Video(input_path)
参数:
- input_path:文件输入路径
执行基础操作
方法:run(op, output_path)
参数:
- op: Operation实例对象
- output_path:输出路径
返回:Video
示例:
video = Video("xxx")
op = Operate().turn(v=True)
await video.run(op, output_path="xxx")
查看视频信息
方法:get_info()
参数:无
返回:dict
示例:
info = await video.get_info()
{
'streams': [
{
'index': 0,
'codec_name': 'h264',
'codec_long_name': 'H.264 / AVC / MPEG-4 AVC / MPEG-4 part 10',
'profile': 'Constrained Baseline',
'codec_type': 'video',
'codec_tag_string': 'avc1',
'codec_tag': '0x31637661',
'width': 320,
'height': 240,
'coded_width': 320,
'coded_height': 240,
'closed_captions': 0,
'film_grain': 0,
'has_b_frames': 0,
'pix_fmt': 'yuv420p',
'level': 13,
'color_range': 'tv',
'color_space': 'smpte170m',
'color_transfer': 'bt709',
'color_primaries': 'smpte170m',
'chroma_location': 'left',
'field_order': 'progressive',
'refs': 1,
'is_avc': 'true',
'nal_length_size': '4',
'id': '0x1',
'r_frame_rate':
'30000/1001',
'avg_frame_rate': '93500/3153',
'time_base': '1/90000',
'start_pts': 0,
'start_time': '0.000000',
'duration_ts': 1135080,
'duration': '12.612000',
'bit_rate': '80637',
'bits_per_raw_sample': '8',
'nb_frames': '374',
'extradata_size': 40,
'disposition': {
'default': 1,
'dub': 0,
'original': 0,
'comment': 0,
'lyrics': 0,
'karaoke': 0,
'forced': 0,
'hearing_impaired': 0,
'visual_impaired': 0,
'clean_effects': 0,
'attached_pic': 0,
'timed_thumbnails': 0,
'captions': 0,
'descriptions': 0,
'metadata': 0,
'dependent': 0,
'still_image': 0
},
'tags': {
'creation_time': '2010-05-11T10:32:06.000000Z',
'language': 'und',
'vendor_id': '[0][0][0][0]',
'encoder': 'JVT/AVC Coding'
}
}, {
'index': 1,
'codec_name': 'aac',
'codec_long_name': 'AAC (Advanced Audio Coding)',
'profile': 'LC',
'codec_type': 'audio',
'codec_tag_string': 'mp4a',
'codec_tag': '0x6134706d',
'sample_fmt': 'fltp',
'sample_rate': '48000',
'channels': 2,
'channel_layout': 'stereo',
'bits_per_sample': 0,
'initial_padding': 0,
'id': '0x2',
'r_frame_rate': '0/0',
'avg_frame_rate': '0/0',
'time_base': '1/48000',
'start_pts': 0,
'start_time': '0.000000',
'duration_ts': 605184,
'duration': '12.608000',
'bit_rate': '115752',
'nb_frames': '591',
'extradata_size': 2,
'disposition': {
'default': 1,
'dub': 0,
'original': 0,
'comment': 0,
'lyrics': 0,
'karaoke': 0,
'forced': 0,
'hearing_impaired': 0,
'visual_impaired': 0,
'clean_effects': 0,
'attached_pic': 0,
'timed_thumbnails': 0,
'captions': 0,
'descriptions': 0,
'metadata': 0,
'dependent': 0,
'still_image': 0
},
'tags': {
'creation_time': '2010-05-11T10:32:06.000000Z',
'language': 'und',
'vendor_id': '[0][0][0][0]'
}
}, {
'index': 2,
'codec_name': 'bin_data',
'codec_long_name': 'binary data',
'codec_type': 'data',
'codec_tag_string': 'text',
'codec_tag': '0x74786574',
'id': '0x3',
'r_frame_rate': '0/0',
'avg_frame_rate': '0/0',
'time_base': '1/90000',
'start_pts': 0,
'start_time': '0.000000',
'duration_ts': 1135080,
'duration': '12.612000',
'bit_rate': '8',
'nb_frames': '1',
'extradata_size': 43,
'disposition': {
'default': 0,
'dub': 0,
'original': 0,
'comment': 0,
'lyrics': 0,
'karaoke': 0,
'forced': 0,
'hearing_impaired': 0,
'visual_impaired': 0,
'clean_effects': 0,
'attached_pic': 0,
'timed_thumbnails': 0,
'captions': 0,
'descriptions': 0,
'metadata': 0,
'dependent': 0,
'still_image': 0
},
'tags': {
'creation_time': '2010-05-11T10:32:06.000000Z',
'language': 'und'
}
}
],
'format': {
'filename': './asset/video1.mp4',
'nb_streams': 3,
'nb_programs': 0,
'format_name': 'mov,mp4,m4a,3gp,3g2,mj2',
'format_long_name': 'QuickTime / MOV',
'start_time': '0.000000',
'duration': '12.612000',
'size': '318465',
'bit_rate': '202007',
'probe_score': 100,
'tags': {
'major_brand': 'mp42',
'minor_version': '0',
'compatible_brands': 'mp42isomavc1',
'creation_time': '2010-05-11T10:32:06.000000Z',
'encoder': 'HandBrake 0.9.4 2009112300'
}
}
}
视频截图
方法:screenshot(output_path[, op])
参数:
- output_path:图片输出路径
- (可选)op: Operation实例对象
返回:Image
示例:
img = await video.screenshot("xxx")
人物抠图
方法:remove(output_path, model_name, worker_nodes, gpu_batch_size, frame_limit, frame_rate[, op])
参数:
- output_path: 输出路径
- (可选)model_name: 模型名称
- (可选)frame_limit: 总帧数
- (可选)frame_rate: 帧率
- (可选)timeout: 超时时间(秒)
- (可选)op: Operation实例对象
返回:Video
示例:
video = Video(video3_path)
await video.remove('xxx.webm')
可选模型:u2netp、u2net、u2net_human_seg
注意:
-
一般情况下,
frame_limit
和frame_rate
无需输入,除非无法正常获取视频总帧数和帧率,可手动指定 -
工具会识别GPU是否可用,若可用,则使用GPU进行算法处理,否则使用CPU
工具默认安装
torch
和torchvision
版本如下:torch~=2.0.1 torchvision~=0.15.2
请根据设备CUDA版本安装对应的依赖版本,否则可能识别不到CUDA
-
当前仅允许输出
mov
和webm
格式文件。若输出webm
格式,请确认是否存在libvpx-vp9
编码器:ffmpeg -codecs | grep vp9
默认编译安装的版本可能缺少该编码器,可尝试如下安装:
# 安装libvpx apt-get install libvpx-dev # 下载ffmpeg安装包 wget https://ffmpeg.org/releases/ffmpeg-4.4.tar.gz # 解压 tar -xzvf ffmpeg-4.4.tar.gz # 进入文件夹 cd ffmpeg-4.4 # 配置编译并安装 ./configure --enable-libvpx && make && make install
-
推荐传入
timeout
以防止意外情况所产生的僵尸进程。该参数仅控制子进程运行的最大时间,并不能准确控制完整流程的超时时间
人脸检测
方法:face_detection(model_name, proportion, worker_nodes[, op])
参数:
- (可选)model_name: 模型名称(默认haarcascade_frontalface_alt2)
- (可选)proportion: 人脸出现占比(0~1,默认0.9)
- (可选)timeout: 超时时间(秒)
- (可选)op: op: Operation实例对象
返回:bool
示例:
video = Video("xxx")
await video.face_detection()
await video.face_detection(worker_nodes=2)
可选模型:haarcascade_frontalface_alt2、haarcascade_fullbody等
视频拼接
方法:concat(output_path, video_list, audio, logger)
参数:
-
output_path: 输出路径
-
video_list: 视频列表
{ "media":Video实例对象, "op":Operate实例对象(可选) }
-
(可选)audio: 音频
如果传递该参数,则使用该音频作为最终合成视频的音频流
-
(可选)logger: 是否打印进度
返回:Video
示例:
# 视频倒放+拼接
video = Video("xxx")
op = Operate().reverse()
await Video.concat('xxx', [{"media": video}, {"media": video, "op": op}])
注意:该方法为静态方法,使用Video.
调用
视频合成
方法:composite(output_path, media_list, audio, canvas, logger)
参数:
-
output_path: 输出路径
-
media_list: 视频/图片列表
{ "media":Video/Image实例对象, "op":Operate实例对象(可选), "start":开始时间(可选), "end":结束时间(可选), "duration":持续时间(可选), "x":在画布中的坐标点(可选), "y":在画布中的坐标点(可选) }
-
(可选)audio: 音频
如果传递该参数,则使用该音频作为最终合成视频的音频流
-
(可选)canvas: 画布大小
在默认情况下,合成的视频和第一个剪辑的尺寸相同,但是如果你想让你的剪辑在更大的合成视频里浮动,可以使用该参数:
composite(..., canvas=(1280,720))
-
(可选)logger: 是否打印进度
返回:Video
示例:
video1 = Video(video1_path)
video2 = Video(video2_path)
image1 = Image(image1_path)
await Video.composite(f'{output_path}/video_composite.mp4', [
{"media": video2},
{"media": video1, "start": 5},
{"media": image1, "start": 3, "x": 50, "y": 50}
])
注意:
- 该方法为静态方法,使用
Video.
调用 - 如果是
Image
实例对象,默认显示10s,若不满足要求,请指定该媒体对象的持续时间
(4) 音频
from bm_video_tools import Audio
audio = Audio(input_path)
参数:
- input_path:文件输入路径
执行基础操作
方法:run(op, output_path)
参数:
- op: Operation实例对象
- output_path:输出路径
返回:Audio
示例:
audio = Audio("xxx")
op = Operate().params(sample='44100')
await audio.run(op, output_path="xxx")
查看音频信息
方法:get_info()
参数:无
返回:dict
示例:
info = await audio.get_info()
音频拼接
方法:concat(output_path, audio_list, logger)
参数:
-
output_path: 输出路径
-
audio_list: 音频列表
{ "media":Audio实例对象, "op":Operate实例对象 }
-
(可选)logger: 是否打印进度
返回:Audio
示例:
audio1 = Audio(audio1_path)
audio2 = Audio(audio2_path)
await Audio.concat(f'{output_path}/audio_concat.mp3', [{"media": audio1}, {"media": audio2}])
注意:该方法为静态方法,使用Audio.
调用
音频合成
方法:composite(output_path, audio_list, logger)
参数:
-
output_path: 输出路径
-
audio_list: 音频列表
{ "media":Audio实例对象, "op":Operate实例对象(可选), "start":音频开始时间(可选), "end":音频结束时间(可选), "duration":持续时间(可选), }
-
(可选)logger: 是否打印进度
返回:Audio
示例:
audio1 = Audio(audio1_path)
audio2 = Audio(audio2_path)
await Audio.composite(f'{output_path}/audio_composite.mp3', [{"media": audio1, "start": 5}, {"media": audio2}])
注意:该方法为静态方法,使用Audio.
调用
4.异常类型
异常类名称 | 说明 |
---|---|
ToolsRuntimeError | 运行异常 |
ToolsTimeoutError | 超时异常 |
ToolsFileError | 文件读取异常 |
ToolsValueError | 值异常 |
引用方法:
from bm_video_tools.error import ToolsRuntimeError, ToolsValueError, ToolsTimeoutError, ToolsFileError
Project details
Download files
Download the file for your platform. If you're not sure which to choose, learn more about installing packages.
Source Distribution
Built Distribution
File details
Details for the file bm-video-tools-0.1.6.tar.gz
.
File metadata
- Download URL: bm-video-tools-0.1.6.tar.gz
- Upload date:
- Size: 36.9 kB
- Tags: Source
- Uploaded using Trusted Publishing? No
- Uploaded via: twine/4.0.2 CPython/3.8.18
File hashes
Algorithm | Hash digest | |
---|---|---|
SHA256 | 19483d92a47554e7a30406b0507e06f68d1ce7beefc097bf4821a418915ba86d |
|
MD5 | e4c7f5096a62d270bbbf4f3319be126a |
|
BLAKE2b-256 | 0286dee1824f73620831626b8f4d823dd1f21c89623a08d274bb4da133d17af4 |
File details
Details for the file bm_video_tools-0.1.6-py3-none-any.whl
.
File metadata
- Download URL: bm_video_tools-0.1.6-py3-none-any.whl
- Upload date:
- Size: 39.4 kB
- Tags: Python 3
- Uploaded using Trusted Publishing? No
- Uploaded via: twine/4.0.2 CPython/3.8.18
File hashes
Algorithm | Hash digest | |
---|---|---|
SHA256 | eb90bc3f11ab373efef64c976f84ba8334e9958c509e74162cd435b21ed05a89 |
|
MD5 | cd6ae8fdc46407f0904c4bc496912b17 |
|
BLAKE2b-256 | 0e9ee298dbda52995ebb3815144cbad0109a21c971802c2145b4e474c79feb25 |