html 视频转字符视频,python 将视频转化为字符画之badapple

看了用PYTHON制作字符动画演示科技bilibili哔哩哔哩弹幕视频网后觉得挺好玩,复现了一下,整体思路很简单,将视频分解为图片,然后将图片逐一转换为字符画,然后利用浏览器进行逐帧播放。

500dfe5b57e7?tdsourcetag=s_pctim_aiomsg

浏览器播放效果

首先安装FFmpeg,一款开源的视频软件,有丰富的视频处理功能。如何在Windows上安装FFmpeg程序。

然后使用window下的批处理batch对视频抓帧,在工作目录下新建run.bat

mkdir images

set /p input="input file:"

set /p rate="set frame rate(Hz value, fraction or abbreviation):"

set /p output="output file:"

ffmpeg -i %input% -r %rate% %output%

然后双击该bat运行,在第一句后输入被转化视频名称,在第二句后指定抓帧频率,为33.333(与后文代码中的播放间隔相对应),在第三句后输入images/%d.bmp,将所有转化图片放于工作目录下的images文件夹中。

500dfe5b57e7?tdsourcetag=s_pctim_aiomsg

抓帧

测试单张转换。在这里,仅仅先将图片转换为灰阶,然后对每个像素点做判断,根据黑白分别转化为'@'或' '(空格),最后输出文本到html中。

import os

os.chdir(r'F:\badapple!!\images') # 转移到工作目录

from PIL import Image, ImageFont, ImageDraw

originalImage = Image.open('6382.bmp')# 图片尺寸为(480, 360)

grayImage = originalImage.resize((120, int(90/2))).convert('L')

# 将图片尺寸缩小,即减少像素点,并转换为灰阶

# int(90/2) 因为字符的高约是宽的两倍,由于之后是一个像素点替换为一个字符,所以提前将高缩小一倍

resulttext = ''

for row in range(grayImage.size[1]): # 先行后列

for col in range(grayImage.size[0]):

pixel = grayImage.getpixel((col, row))

char = '@' if pixel < 127 else ' ' # 像素点值为0是黑色

resulttext += char

resulttext += '\n'

#print(resulttext)

head = '''

pre {font-size:14px; line-height:14px}

 
 

'''

foot = '''

'''

with open('1.html','w') as f:

f.write(head)

f.write(resulttext)

f.write(foot)

500dfe5b57e7?tdsourcetag=s_pctim_aiomsg

单张转换效果

由于上述对图像的转化只有黑白两个层次,太过简单,表现力不够丰富,所以我们将一系列字符按一定规则排序,然后匹配到相应的灰度上。我们先从网上下载一份simsun的字体文件来提供画图中的字体,然后利用PIL中的画图功能,先创建一个最大字符尺寸的矩形白板,然后在上面画上字符,然后计算它的平均像素(每个像素点*该点像素值/总像素点数),根据平均像素来排序。然后再做下单张测试。此外,由于文本输出到html文件,所以需要html.escape()函数进行转义。

补充:chr函数将数字转换为ACII码对应的字符

500dfe5b57e7?tdsourcetag=s_pctim_aiomsg

chr

### 然后对像素对字符的转换方式进行改进

font= ImageFont.truetype('../simsun.ttf', 14)

chars = list(chr(i) for i in range(32, 126))

sizeList = list(font.getsize(char) for char in chars)

import functools

maxSize = functools.reduce(lambda x,y:(max(x[0],y[0]), max(x[1],y[1])), sizeList)

#(8, 15)

tempCharImage = Image.new('L', maxSize, 'white')

tempCharDraw = ImageDraw.Draw(tempCharImage)

charDegreeDict = {}

for char in chars:

tempCharDraw.rectangle([(0,0), maxSize], fill='white')#在(0,0)位置处以白色填充一个canvasSize的矩形。

tempCharDraw.text((0,0), char, font=font)

pixelColor = tempCharImage.getcolors()#返回当前图片上的所有色彩及其像素点数的列表,[(个数,色彩),(个数,色彩),...]

grayDegree = sum(pixelnum*color for pixelnum,color in pixelColor)/(maxSize[0]*maxSize[1])

charDegreeDict[char] = grayDegree

sortedCharDegreeList = sorted(charDegreeDict.items(), key=lambda d:d[1])

sortedCharDegreeList = list(i[0] for i in sortedCharDegreeList)

charsIndexMax = len(sortedCharDegreeList) -1

# ['M', 'W', '@', 'N', 'H', '%', 'X', '$', 'B', 'K', 'R', '#', 'E', 'U', 'Q', 'D', '&', 'F', 'w', '8', 'k', '9', '6', 'h', 'm', 'A', 'P', 'p', 'd', '*', 'V', 'g', 'Y', '0', '5', 'b', 'q', 'G', 'O', 'n', 'x', 'f', 'S', 'J', 'T', 'Z', '3', 'y', 'u', 'I', 'C', 'L', '2', 'a', ']', '[', '1', '?', 'l', 's', 'e', 'r', '|', '}', '{', 't', 'z', 'o', 'v', '4', '+', '/', 'i', 'j', '=', 'c', '7', '(', ')', '!', '\\', '_', '>', '

### 按灰度替换字符重新测试

import os

os.chdir(r'F:\badapple!!\images')

from PIL import Image, ImageFont, ImageDraw

originalImage = Image.open('6382.bmp')# 图片尺寸为(480, 360)

grayImage = originalImage.resize((120, int(90/2))).convert('L')

# 将图片尺寸缩小,即减少像素点,并转换为灰阶

# int(90/2) 因为字符的高是长的两倍,由于之后是一个像素点替换为一个字符,所以提前将高缩小一倍

resulttext = ''

for row in range(grayImage.size[1]):

for col in range(grayImage.size[0]):

pixel = grayImage.getpixel((col, row))

char = sortedCharDegreeList[int(pixel/255*charsIndexMax)] # 像素点值为0是黑色,255为白色

resulttext += char

resulttext += '\n'

#print(resulttext)

head = '''

pre {font-family:simsun;font-size:14px; line-height:14px}

 
 

'''

foot = '''

'''

with open('1.html','w') as f:

f.write(head)

import html

f.write(html.escape(resulttext))

f.write(foot)

500dfe5b57e7?tdsourcetag=s_pctim_aiomsg

多层次字符替换

然后就是对所有图片进行转换了,利用glob找出工作目录images文件夹下的所有bmp图片,依次处理。所有字符图片存于html文件中的

标签中,一个标签对应一个图,然后在js代码中每隔30秒进行下一张图的显示和前一张的隐藏,这样就实现了播放。

补充:glob的用法

import glob

#获取指定目录下的所有图片

print glob.glob(r"E:\Picture\*\*.jpg")

#获取上级目录的所有.py文件

print glob.glob(r'../*.py') #相对路径

### ffmpeg -i "Touhou - Bad Apple!! PV.webm" -f mp3 -vn apple.mp3 可以输出音频,暂时没有用到

workdir = r'F:\badapple!!\images'

### 按灰度替换的字符列表

sortedCharDegreeList = ['M', 'W', '@', 'N', 'H', '%', 'X', '$', 'B', 'K', 'R', '#', 'E', 'U', 'Q', 'D', '&', 'F', 'w', '8', 'k', '9', '6', 'h', 'm', 'A', 'P', 'p', 'd', '*', 'V', 'g', 'Y', '0', '5', 'b', 'q', 'G', 'O', 'n', 'x', 'f', 'S', 'J', 'T', 'Z', '3', 'y', 'u', 'I', 'C', 'L', '2', 'a', ']', '[', '1', '?', 'l', 's', 'e', 'r', '|', '}', '{', 't', 'z', 'o', 'v', '4', '+', '/', 'i', 'j', '=', 'c', '7', '(', ')', '!', '\\', '_', '>', '

charsIndexMax = len(sortedCharDegreeList) -1

import os, glob, html

os.chdir(workdir)

from PIL import Image, ImageFont, ImageDraw

result = []

imgs = glob.glob('*.bmp')

imgs = sorted(imgs, key=lambda x: int(x.split('.')[0])) # 这里对图片路径进行了处理,取后缀前的数字值进行排序

#

for img in imgs:

originalImage = Image.open(img)# 图片尺寸为(480, 360)

grayImage = originalImage.resize((120, int(90/2))).convert('L')

# 将图片尺寸缩小,即减少像素点,并转换为灰阶

# int(90/2) 因为字符的高是长的两倍,由于之后是一个像素点替换为一个字符,所以提前将高缩小一倍

resulttext = ''

for row in range(grayImage.size[1]):

for col in range(grayImage.size[0]):

pixel = grayImage.getpixel((col, row))

char = sortedCharDegreeList[int(pixel/255*charsIndexMax)] # 像素点值为0是黑色,255为白色

resulttext += char

resulttext += '\n'

result.append(resulttext)

print(img,'is done!')

#

head = '''

pre {display:none;font-family:simsun;font-size:14px; line-height:14px}

window.onload = function(){

var pres = document.getElementsByTagName('pre');

var i = 0;

var play = function(){

if(i > 0){

pres[i-1].style.display = 'none';

}

pres[i].style.display = 'inline-block';

i++;

if(i == pres.length){

clearInterval(run)

}

}

run = setInterval(play, 30)

}

'''

foot = '''

'''

with open('2.html','w') as f:

f.write(head)

for resulttext in result:

f.write("

")

f.write(html.escape(resulttext))

f.write("

")

f.write(foot)

500dfe5b57e7?tdsourcetag=s_pctim_aiomsg

最终效果

最后你可以优化一下字符的替换方式,去掉某些显示效果不好的字符。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值