抖音上看到那种把舞蹈变成字符型的小视频,如下图那种。就一直想弄一个。参考了网上写的好多代码,自己整理了一个。
大概思路:
- 先将原视频分割为一张一张的图片
- 再将这些图片转换为字符型的
- 又将这些字符型的图片串成视频即可
废话不多说,完整代码如下,有注释说明,只需更改一下视频路径及其存储路径即可。
# -*- coding: utf-8 -*-
"""
Created on Sun Aug 11 20:55:29 2019
@author: Administrator
"""
import cv2
import os
from PIL import Image, ImageDraw, ImageFont
WIDTH = 80 #定义输出画面的宽度
HEIGHT = 45 #定义
ascii_char = list("$@B%8&WM#*oahkbdpqwmZO0QLCJUYXzcvunxrjft/\|()1{}[]?-_+~<>i!lI;:,\"^`'. ") #所用字符列表
# 将256灰度映射到70个字符上
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(sourcePath,storagePath):
print("开始将图片转为字符型:")
# 循环最多读取1100张图片,自己可更改
for icount in range(1, 1100):
IMG = sourcePath+str(icount)+'.jpg' #文件路径
if os.path.exists(IMG):
im = Image.open(IMG)
#视频分割后图片的长与宽,与合成视频时要相统一,保存下来,合成字符视频时用到
sourceImage=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(storagePath + str(icount) + '.jpg')
return sourceImage
def charToVideo(storagePath,sourceImage):
# 设置视频编码器,这里使用使用MJPG编码器
#fourcc = cv2.VideoWriter_fourcc(*'MJPG')
#这里是mp4格式
fourcc = cv2.VideoWriter_fourcc('D', 'I', 'V', 'X')#不同视频编码对应不同视频格式(例:'I','4','2','0' 对应avi格式)
print("开始将字符型图片变为视频:")
#输出视频参数设置,包含视频文件名、编码器、帧率、视频宽高(此处参数需和字符图片大小一致)
videoWriter = cv2.VideoWriter(storagePath+'test.mp4', fourcc, 30.0, (sourceImage.width,sourceImage.height))
#循环最多读取1100张图片,自己可更改
for i in range(1, 1100):
filename = storagePath +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("字符视频已成功生成!!!")
def VideoToPicture(path):
# 在当前目录下新建文件夹
folder_path = "imgbear/"
os.makedirs(folder_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 folder_path
if __name__=='__main__':
#视频所在路径
path='C://Users//Administrator//Desktop//aa.mp4'
#存储路径,路径上的文件夹必须已经存在
storagePath='C://Users//Administrator//Desktop//1//'
sourcePath = VideoToPicture(path)
sourceImage = PictureToChar(sourcePath,storagePath)
charToVideo(storagePath,sourceImage)
最后结果如下: