因为不想像传统的课设作业一样做一个很多人做过的小游戏,管理程序等,所以花了很长时间在选择一个有趣的程序方向,突然想起来以前看到的字符跳舞视频,突然引起了我的兴趣。分析字符视频的创建流程,想到了利用OpenCV将视频分解成一帧一帧的,然后再对每个图片进行处理。
在处理每帧图片时,可以通过灰度来区分色块,计算每个图片各色块的灰度,将256灰度映射到70个字符上,从而形成字符图片,再利用OpenCV将逐帧字符图片合成成字符视频。这样合成的视频是没有声音的,再利用ffmpeg工具将原视频中的音频分离出来,再与字符视频合成。
本以为大功告成了,突然想到找测试视频时候的窘况,各大网站都不支持直接下载视频。决定再加一个输入视频真实网站直接下载视频的功能,方便视频转换成字符视频。利用youtube-dl模块的功能,完成了输入视频真实网址,再选择视频和音频组合下载不同质量的视频。经过测试,可以下载Bilibili,Youtube等视频网站的视频。
设计这种程序先大致想一下流程,如果不是要交报告想来我也是不会画流程图的,呜呜呜。随手一画,别太较真。
1、通过灰度来区分色块,计算每个图片各色块的灰度,将256灰度映射到70个字符,将逐帧图片转换成字符图片(处理逐帧图片核心代码)这个其实是copy了网上其他大佬处理字符图片的办法,嘿嘿嘿
def get_char(r, g, b, alpha=256): # alpha透明度
if alpha == 0:
return ' '
length = len(ascii_char)
gray = int(0.2126 * r + 0.7152 * g + 0.0722 * b) # 计算灰度
unit = (256.0 + 1) / length
return ascii_char[int(gray / unit)] # 不同的灰度对应着不同的字符
# 通过灰度来区分色块
# 该部分以下和灰度值字符画区别所在
def PictureToChar(folder_path, ascii_path, c):
print("开始将图片转为字符型:")
# 循环读取逐帧图片
for icount in range(1, c):
IMG = folder_path + str(icount) + '.jpg' # 文件路径
if os.path.exists(IMG):
im = Image.open(IMG)
# 视频分割后图片的长与宽,与合成视频时要相统一,保存下来,合成字符视频时用到
asciiImage = im
WIDTH = int(im.width / 6) # 高度比例为原图的1/6较好,由于字体宽度
HEIGHT = int(im.height / 15) # 高度比例为原图的1/15较好,由于字体高度
im_txt = Image.new("RGB", (im.width, im.height), (255, 255, 255))
im = im.resize((WIDTH, HEIGHT), Image.NEAREST)
txt = ""
colors = []
for i in range(HEIGHT):
for j in range(WIDTH):
pixel = im.getpixel((j, i))
colors.append((pixel[0], pixel[1], pixel[2])) # 记录像素颜色信息
if (len(pixel) == 4):
txt += get_char(pixel[0], pixel[1], pixel[2], pixel[3])
else:
txt += get_char(pixel[0], pixel[1], pixel[2])
txt += '\n'
colors.append((255, 255, 255))
dr = ImageDraw.Draw(im_txt)
font = ImageFont.load_default().font # 获取字体
x = y = 0
# 获取字体的宽高
font_w, font_h = font.getsize(txt[1])
font_h *= 1.37 # 调整后更佳
# ImageDraw为每个ascii码进行上色
for i in range(len(txt)):
if (txt[i] == '\n'):
x += font_h
y = -font_w
dr.text([y, x], txt[i], colors[i])
y += font_w
# 输出
name = str(icount) + '.jpg'
print(name)
im_txt.save(ascii_path + str(icount) + '.jpg')
return asciiImage
2、原视频分割为逐帧图片
def VideoToPicture(path):
# 进行视频的载入
vc = cv2.VideoCapture(path)
print("开始将原视频分割为图片:")
c = 0
# 判断载入的视频是否可以打开
ret = vc.isOpened()
# 循环读取视频帧
while ret:
c = c + 1
# 进行单张图片的读取,ret的值为True或者Flase,frame表示读入的图片
ret, frame = vc.read()
if ret:
# 存储为图像
cv2.imwrite(folder_path + str(c) + '.jpg', frame)
# 输出图像名称
print(folder_path + str(c) + '.jpg')
# 在一个给定的时间内(单位ms)等待用户按键触发,1ms
cv2.waitKey(1)
else:
break
# 视频释放
vc.release()
return c
3、将字符图片合成字符视频,当然,这个时候是没声音的,还需要后续合成。
def charToVideo(ascii_path, asciiImage, path, c, finalVideo_path):
# 不同视频编码对应不同视频格式
if path.endswith(".mp4"):
fourcc = cv2.VideoWriter_fourcc('D', 'I', 'V', 'X') # 这里是mp4格式,文件名后缀为.mp4
elif path.endswith(".avi"):
fourcc = cv2.VideoWriter_fourcc('I', '4', '2', '0') # (例:'I','4','2','0' 对应avi格式)
elif path.endswith(".flv"):
fourcc = cv2.VideoWriter_fourcc('F', 'L', 'V', '1') # 该参数是Flash视频,文件名后缀为.flv
print("开始将字符型图片变为视频:")
# 输出视频参数设置,包含视频文件名、编码器、帧率、视频宽高(此处参数需和字符图片大小一致)
video_file = finalVideo_path + 'out' + '_' + 'ascci' + '_' + path
videoWriter = cv2.VideoWriter(video_file, fourcc, 30.0,
(asciiImage.width, asciiImage.height))
# 循环读取图片
for i in range(1, c):
filename = ascii_path + str(i) + '.jpg'
# 判断图片是否存在
if os.path.exists(filename):
img = cv2.imread(filename=filename)
# 在一个给定的时间内(单位ms)等待用户按键触发,100ms
cv2.waitKey(100)
# 将图片写入视频中
videoWriter.write(img)
print(str(i) + '.jpg' + ' done!')
# 视频释放
videoWriter.release()
print("字符视频已成功生成!!!")
return video_file
4、分离原视频中的音乐和字符视频合并,形成有声音的字符视频
def VideoToMp3(path, finalVideo_path): # 分离原视频中的音乐
outMusic_name = finalVideo_path + path.split('.')[0] + '.mp3' # 将原视频文件后缀名去掉加上.mp3
os.system(f"ffmpeg -i {path} -vn {outMusic_name} ")
return outMusic_name
def VideoAddMp3(video_file, outMusic_name): # 将分离出来的原视频中的音乐和字符视频合并
video_final = finalVideo_path + 'Final' + '_' + '_' + path
os.system(f"ffmpeg -i {outMusic_name} -i {video_file} {video_final}")
5、视频下载功能(利用youtube-dl下载视频,如果下载有问题可能是重命名的问题,去掉让他直接用解析的网站的名字命名就好了)
def linkToVideo(link_url):
#将链接中的视频下载
os.system(f"youtube-dl -F {link_url}")
print("====请按’视频序号+音频序号‘顺序选择输入视频和音频组合,如果只有一个就只输入一个数字===== ")
num = input()
VideoDownload_name = input("请给下载视频重命名: ")
os.system(f"youtube-dl -f {num} {link_url} -o {VideoDownload_name}")
以上是部分重要函数部分代码,整个项目已上传Github仓库:Github仓库https://github.com/Dddwaiting/Python