项目背景
这个项目的启发,当然是来自我们无敌的磊哥
看到磊哥用剪辑软件做出来的MV,我想着,我们可以用python来实现一款相对自动化的全流程MV生成工具。
在这里项目,我们只需要放入歌曲and歌曲的lrc文件以及一张首页图即可得到最终的MV
话不多说,肝着!
导入一些必备的包
# 用来剪切音频和转换mp3为wav !pip install pydub !pip install moviepy !pip install auditok
文本处理
我们要拿到音频的时间点,以及文本,才能去进行一个文生图
我本来写了一个自动智能化根据静默时间切分音频的代码,结果,因为歌曲有背景曲效果很差,那就算了吧
有兴趣尝试自动切分音频并提取文本的可以参考我之前的项目:
这里直接使用Lrc文件看效果,后续找到解决方案再去实现这个坑
目标:
- 时间节点
- 时间节点对应的文本
时间节点、歌词 获取
# 暂时没有找到比较合适的文本提取并且找到对应时间戳的工具 # 已有的 import auditok auditok.split这样的效果都不好 # 我暂且我找的歌曲的Lrc歌词文件 def readLrc(path = '水调歌头.lrc'): lines = [] texts = [] f = open(path,'r') while True: line = f.readline() #包括换行符 line = line[:-1] #去掉换行符 if line: if ('.' in line): timeMin = line.strip().split(']')[0][1:3] timeSec = line.strip().split(']')[0][4:6] text = line.strip().split(']')[1] lines.append([timeMin,timeSec]) texts.append(text) else: break f.close() return lines,texts info,text = readLrc('水调歌头.lrc') print(info[0])
['00', '01']
text
['水调歌头', '作詞∶苏轼', '作曲∶梁弘志', '演唱∶王菲', '明月几时有 把酒问青天', '不知天上宫阙 今夕是何年', '我欲乘风归去 唯恐琼楼玉宇', '高处不胜寒 起舞弄清影 何似在人间', '转朱阁 低绮户 照无眠', '不应有恨 何事长向别时圆', '人有悲欢离合 月有阴晴圆缺', '此事古难全 但愿人长久 千里共婵娟', '^_^ ~~~MUSIC~~~ ^_^', '我欲乘风归去 唯恐琼楼玉宇', '高处不胜寒 起舞弄清影 何似在人间', '转朱阁 低绮户 照无眠', '不应有恨 何事长向别时圆(别时圆)', '人有悲欢离合 月有阴晴圆缺', '此事古难全 但愿人长久 千里共婵娟', '~END']
文心大模型
AK和SK的获取:
https://wenxin.baidu.com/moduleApi/key
当然因为这里要生成很多的图片,要等待很久!
生成图片
!pip install wenxin-api
from tqdm import tqdm import wenxin_api from wenxin_api.tasks.text_to_image import TextToImage # 下面我们安装并导入ernie_vilg模块 # 因为需要发送请求,并等待数据返回,所以,会很慢,这取决于歌词条目的数量 pro= [] # 记录单片段歌词 def get_img(texts): # 你的prompt 我这里发现用逗号连接词语效果更佳 # text_prompt = "程序员,写代码" imgs = [] img_num = [] # 统计这一行歌词生成了几张图片的列表 for i in tqdm(range(len(texts))): t = texts[i].replace(' ',',').split(',') img_num.append(len(t)) for j in t: pro.append(j) text_prompt = j + ',圆月亮,夜晚,古风' wenxin_api.ak = "RgCyG5U1o9CsRr2T9exS4F47TZCgswnV" wenxin_api.sk = "PobN54TGDMwuFY0iens5bqsQWQxq8F44" input_dict = { "text": text_prompt, "style": "油画" } rst = TextToImage.create(**input_dict) imgs.append(rst['imgUrls'][0]) return imgs,img_num img_list,img_num = get_img(text)
下载图片
import requests import os from PIL import Image import matplotlib.pyplot as plt %matplotlib inline # 生成存图的目录 def init_mkdir(data_path): if not os.path.exists(data_path): # 判断文件夹是否存在 os.makedirs(data_path) # 不存在则新建文件夹 init_mkdir('output-img') # 展示生成的图片并保存 for i in range(len(img_list)): #使用requests直接get 下载图片使用 r = requests.get(img_list[i]) ii = '%02d'%i +'.png' out_ii = 'output-img/' + ii with open(out_ii, 'wb') as f: f.write(r.content) f.close() img_ = Image.open(out_ii) plt.show() plt.imshow(img_)
import os import cv2 file_dir = 'output-img' # 返回path下所有文件构成的一个list列表 filelist = os.listdir(file_dir) # 保证读取按照文件的顺序 filelist.sort() if ('.ipynb_checkpoints' in filelist): del filelist[0] print(filelist)
['00.png', '01.png', '02.png', '03.png', '04.png', '05.png', '06.png', '07.png', '08.png', '09.png', '10.png', '11.png', '12.png', '13.png', '14.png', '15.png', '16.png', '17.png', '18.png', '19.png', '20.png', '21.png', '22.png', '23.png', '24.png', '25.png', '26.png', '27.png', '28.png', '29.png', '30.png', '31.png', '32.png', '33.png', '34.png', '35.png', '36.png', '37.png', '38.png', '39.png', '40.png', '41.png', '42.png', '43.png', '44.png']
图片加歌词
给图片加歌词
每一张图片的歌词如下:
前面以及存储过了
在 pro
变量中
from PIL import ImageFont from PIL import Image from PIL import ImageDraw font = ImageFont.truetype("simhei.ttf",58)#设置字体和字体大小 font1 = ImageFont.truetype("STHUPO.TTF",36)#设置字体和字体大小 font2 = ImageFont.truetype("STXINGKA.TTF",55)#设置字体和字体大小 changed = 'output-text-img' init_mkdir(changed) def addTexttoImg(): for i in range(len(pro)): imageFile = file_dir + '/' + filelist[i] tp=Image.open(imageFile) # 在图片上添加文字 draw = ImageDraw.Draw(tp) draw.text((50, 50),'本歌曲由文心大模型生成图片',(255, 153, 0),font=font1) #分别设置文字的xy坐标,文字内容,文字颜色,字体 draw.text((100, 850),pro[i],(0, 153, 255),font=font2) #分别设置文字的xy坐标,文字内容,文字颜色,字体 draw.text((102, 852),pro[i],(255,255,255),font=font2) #分别设置文字的xy坐标,文字内容,文字颜色,字体 draw = ImageDraw.Draw(tp) # 保存 tp.save(changed + '/' + filelist[i]) #图片保存路径 addTexttoImg()
视频生成
这里是真的花了我好多的时间去设计如何让时间节点把所有的歌词对应的比较完美一些
处理时间节点
这个时间决定了我们需要插入多少张图片
PS 先知: 这里我们可以设置视频的帧数为5,后续视频生成也会用的到
因为我们图片不多
其次,要计算好时间需要多少张图片
如下:
# 总共262秒左右 4*60+22
262
# 我们最终需要1310张的图片才行 5*262
1310
生成策略
合成视频
我这里采取的策略是一秒的五帧都是相同的图片
然后我们上面有提取的时间节点,卡点加入后面的图片
# 时间序列 info
[['00', '01'], ['00', '04'], ['00', '06'], ['00', '08'], ['00', '11'], ['00', '22'], ['00', '32'], ['00', '44'], ['01', '03'], ['01', '14'], ['01', '27'], ['01', '38'], ['01', '56'], ['02', '27'], ['02', '38'], ['02', '57'], ['03', '08'], ['03', '30'], ['03', '41'], ['04', '15']]
# 求出每个时间段需要多少张图片 time_img = [] start = 0 for i in info: # 时间差 end = int(i[0])*60 +int(i[1]) time_img.append((end-start)*5) start = end time_img.append(7*5) # 最后一段不要忘记了 sum(time_img)
1310
# 第一段时间留给首页 一秒 删除第一段,我们自行加首页,我已经放在了左侧的目录中 del time_img[0]
综上
我们的处理逻辑如下:
此外:我采取时间段内的图片均分填充
视频所需要的图片列表
import os import cv2 #造新的图片列表 1310 + 5(首页面) 张的 new_list = [] for i in range(5): new_list.append("home.png") num = 0 # 0-20 for j in range(len(time_img)): # 总共有多少个时间段 len(time_img) = 21 同时,前面还记录了每个时间段有多少张图片,就是 img_num # 0-5 5 # 0-15 15 for n in range(time_img[j]): # 每个时间段需要多少张图片 if (img_num[j] == 1): new_list.append(changed + '/' + filelist[sum(img_num[:j])]) if (img_num[j] == 2): if (n < (time_img[j]//2)): new_list.append(changed + '/' + filelist[sum(img_num[:j])]) else: new_list.append(changed + '/' + filelist[sum(img_num[:j])+1]) if (img_num[j] == 3): if (n < (time_img[j]//3)): new_list.append(changed + '/' + filelist[sum(img_num[:j])]) elif (n < (time_img[j]//3)*2): new_list.append(changed + '/' + filelist[sum(img_num[:j])+1]) else: new_list.append(changed + '/' + filelist[sum(img_num[:j])+2]) if (img_num[j] == 5): if (n < (time_img[j]//5)): new_list.append(changed + '/' + filelist[sum(img_num[:j])]) elif (n < (time_img[j]//5)*2): new_list.append(changed + '/' + filelist[sum(img_num[:j])+1]) elif (n < (time_img[j]//5)*3): new_list.append(changed + '/' + filelist[sum(img_num[:j])+2]) elif (n < (time_img[j]//5)*4): new_list.append(changed + '/' + filelist[sum(img_num[:j])+3]) else: new_list.append(changed + '/' + filelist[sum(img_num[:j])+4]) new_list
len(new_list)
1310
图片列表转视频
import cv2 # VideoWriter是cv2库提供的视频保存方法,将合成的视频保存到该路径中 # 'MJPG'意思是支持jpg格式图片 # fps = 5代表视频的帧频为5,如果图片不多,帧频最好设置的小一点 # (1280,720)是生成的视频像素1280*720,一般要与所使用的图片像素大小一致,否则生成的视频无法播放 # 定义保存视频目录名称和压缩格式,像素为1280*720 video = cv2.VideoWriter('test.mp4',cv2.VideoWriter_fourcc(*'mp4v'),5,(1080,980)) for i in range(len(new_list)): #读取图片 img = cv2.imread(new_list[i]) # resize方法是cv2库提供的更改像素大小的方法 # 将图片转换为1024*720像素大小 img = cv2.resize(img,(1080,980)) # 写入视频 video.write(img) # 释放资源 video.release()
音视频合并
from moviepy import * from moviepy.editor import * # pip install moviepy def merge(): video_path = 'test.mp4' audio_path = 'sdgt_wf.mp3' # 提取音轨 audio = AudioFileClip(audio_path) # 读入视频 video = VideoFileClip(video_path) # 将音轨合并到视频中 video = video.set_audio(audio) # 输出 video.write_videofile(f"output.mp4") if __name__ == '__main__': deo = video.set_audio(audio) # 输出 video.write_videofile(f"output.mp4") if __name__ == '__main__': merge()
项目总结
整个 项目的流程完成还是非常的费事
- 文本的提取
- 图片的生成
- 文本合并
- 图片列表生成
- 视频合成
- 音频合成
目前的视频生成还没有做到全自动,并且细节是不够完善的