`## 实现自动生成卡点视频
通过librosa模块识别音频卡点,和音频长度,opencv进行视频合成,ffmpeg进行视频音频拼接
import cv2
import librosa
import numpy as np
import os
from PIL import Image
# 最后影片的分辨率片,根据视频来设置,默认是1920*1080
img_size = (int(1280), int(720))
# 图片处理
def resize_image(target_image_path, target_size):
"""
调整图片大小,缺失的部分用黑色填充
:param target_image_path: 图片路径
:param target_size: 分辨率大小
:return:
"""
image = Image.open(target_image_path)
iw, ih = image.size # 原始图像的尺寸
w, h = target_size # 目标图像的尺寸
scale = min(w / iw, h / ih) # 转换的最小比例
# 保证长或宽,至少一个符合目标图像的尺寸
nw = int(iw * scale)
nh = int(ih * scale)
image = image.resize((nw, nh), Image.BICUBIC) # 缩小图像
# image.show()
new_image = Image.new('RGB', target_size, (0, 0, 0, 0)) # 生成黑色图像
# // 为整数除法,计算图像的位置
new_image.paste(image, ((w - nw) // 2, (h - nh) // 2)) # 将图像填充为中间图像,两侧为灰色的样式
# new_image.show()
# 覆盖原图片
new_image.save(target_image_path)
# 临时文件处理
def get_temp_path(file_path, temp_name):
"""
获取临时文件的完整路径
"""
filepath, filename_with_extension, filename_without_extension, extension = get_filePath_fileName_all(file_path)
return filepath + "/" + temp_name + extension
# 文件处理
def get_filePath_fileName_all(filename):
"""
获取文件的路径、文件名【带后缀】、文件名【不带后缀】、后缀名
:param filename:
:return:
"""
(filepath, filename_with_extension) = os.path.split(filename)
(filename_without_extension, extension) = os.path.splitext(filename_with_extension)
return filepath, filename_with_extension, filename_without_extension, extension
# 获取节拍点
def getBeats(bgm_path):
y, sr = librosa.load(bgm_path, sr=None)
onset_env = librosa.onset.onset_strength(y, sr=sr, aggregate=np.median)
tempo, beats = librosa.beat.beat_track(onset_envelope=onset_env, sr=sr)
beats = np.array(librosa.frames_to_time(beats[:-1], sr=sr)) # 转为列表
# time = librosa.get_duration(filename=bgm_path)
return beats
def add_water_mask(video_path, mask_word):
"""
给视频增加水印
:param video_part3: 视频源
:param mask_word: 水印文字
:return:
"""
# 获取视频帧数
cap = cv2.VideoCapture(video_path)
fps = cap.get(cv2.CAP_PROP_FPS)
# 保证帧率不变
fourcc = cv2.VideoWriter_fourcc('m', 'p', '4', 'v')
video_temp_path = get_temp_path(video_path, 'temp')
video_writer = cv2.VideoWriter(video_temp_path, fourcc, fps, img_size)
ret, frame = cap.read()
while ret:
# 文字在图中的坐标(注意:这里的坐标原点是图片左上角)
x, y = img_size[0] - 200, img_size[1] - 50
cv2.putText(img=frame, text=mask_word,
org=(x, y), fontFace=cv2.FONT_HERSHEY_COMPLEX_SMALL,
fontScale=1, color=(255, 255, 255))
video_writer.write(frame)
ret, frame = cap.read()
# 删除源文件,并重命名临时文件
os.remove(video_path)
os.rename(video_temp_path, video_path)
print('水印添加完成~')
video_writer.release()
cap.release()
def compound_pic_special(images_path, output_video_path, bgm_path, fps=30):
"""
图片合成视频【卡点视频】
:param images_path: 图片文件路径
:param output_video_path:合成视频的路径
:return:
"""
res = getBeats(bgm_path)
beat_list = []
for i in range(len(res) - 1):
beat = res[i + 1] - res[i]
beat_list.append(beat)
print(beat_list)
# 获取该目录下的所有文件名
filelist = os.listdir(images_path)
print('一共有%d张图片' % len(filelist))
fourcc = cv2.VideoWriter_fourcc('m', 'p', '4', 'v')
# 生成一个视频对象
video = cv2.VideoWriter(output_video_path, fourcc, fps, img_size)
i = 0
time_count = 0
while True:
if filelist[i].endswith('.jpg'): # 判断图片后缀是否是.jpg
image_path = images_path + '/' + filelist[i]
# 缩放图片到合适的分辨率,并覆盖源文件
resize_image(image_path, img_size)
frame = cv2.imread(image_path)
# 直接缩放到指定大小
frame_suitable = cv2.resize(frame, (img_size[0], img_size[1]), interpolation=cv2.INTER_CUBIC)
# 把图片写进视频
# 重复写入多少次
count = 0
total_count = round(30 / beat_list[time_count])
print(total_count)
while count < total_count:
video.write(frame_suitable)
count += 1
time_count += 1
if time_count == len(beat_list) - 1:
break
else:
print('名称为:%s,文件格式不对,过滤掉~' % filelist[i])
if i + 1 == len(filelist):
i = 0
else:
i += 1
print('i', i)
# 释放资源
video.release()
def compound_bgm(images_path, video_path, bgm_path, fps=30):
"""
通过视频、BGM 合成一段视频
:param video_path: 视频路径
:param bgm_path: BGM路径
:return:
"""
compound_pic_special(images_path, video_path, bgm_path, fps)
# 视频、音频合二为一
video_temp_path = get_temp_path(video_path, 'temp')
os.system('ffmpeg -i %s -i %s -c:v copy -c:a aac -strict -2 %s -y -loglevel quiet' % (
video_path, bgm_path, video_temp_path))
os.remove(video_path)
os.rename(video_temp_path, video_path)
print('音视频合成完成~')
if __name__ == '__main__':
# 制作卡点视频
images_path = 'images'
output_video_path = 'video/res.mp4'
bgm_path = 'bgm/cn.mp3'
compound_bgm(images_path, output_video_path, bgm_path)
`