前言
这几年一直在it行业里摸爬滚打,一路走来,不少总结了一些python行业里的高频面试,看到大部分初入行的新鲜血液,还在为各样的面试题答案或收录有各种困难问题
图像转字符
- 构建基本方法,将像素对应灰度值转化为可以代表的字符
- 图像内容解析,转化为字符内容
- 字符内容转化为像素,写入图像
- gif解析为单独的每一帧图片经过以上步骤处理
- 将处理完成的单独图像组合成一个gif
必备概念
- 灰度值:黑白图像中点的颜色深度,范围一般从
0~255
,白色为255
,黑色为0
,所以黑白图片也被成为灰度图像 - alpha通道值:一般用作不透明度参数。如果一个像素的alpha通道数值为0%,那它就是完全透明的(也就是看不见的),而数值为100%则意味着一个完全不透明的像素(传统的数字图像)
灰度值字符映射
每一个灰度值,我们都会有对应的ascii字符与之对应,通过字符所占空间大小,来确定其亮度,越靠前的,那么灰度值越低,也就越暗
灰度值计算公式:0.299 * R
+ 0.587 * G
+ 0.144 * B
def gray2char(r, g, b, alpha=256):
'''
根据RGBA值进行灰度值计算,并返回对应亮度的字符
'''
_ = list("$@B%8&WM#*oahkbdpqwmZO0QLCJUYXzcvunxrjft/\|()1{}[]?-_+~<>i!lI;:,\"^`'. ")
char_length = len(_) # 字符序列长度
proportion = 255 / char_length # 总灰度值对应列表索引范围
gray = 0.2126 * r + 0.7152 * g + 0.0722 * b # 灰度值与rgb的计算公式
return _[int(gray / proportion) - 1] # 返回当前灰度值所对应字符
图像解析为字符
通过解析图像,我们可以得处图像每处像素点的rgb颜色值,并且通过这色值对应转化出灰度值,利用灰度值字符函数,将这个像素对应的字符拿到
注意由于字符和像素的宽高是有区别的,一个字符要比一个字符大的多,所以一张图像在处理过程中,宽高首先要进行比例划算,一般一个字符的宽高对于一个像素来说是6:1、11:1
def image2text(path):
'''
对图像进行解析
@path: 图像路径
'''
img = Image.open(path).convert('RGB') # 打开图像
pic_width, pic_height = img.width, img.height # 图像原始宽高
width, height = int(pic_width / 6), int(pic_height / 11) # 字符像素宽高转化
img = img.resize((width, height), Image.NEAREST) # 原始图像进行缩略,适合字符处理
# 像素遍历,进行灰度值字符转化
content = '' # 存储转化结果字符
colors = [] # 对应坐标原始颜色,为了之后给字符上色使用
for h in range(height): # 从高开始
for w in range(width): # 遍历每一行像素
px = img.getpixel((w, h)) # 获取某一点的像素值
char = gray2char(px[0], px[1], px[2], px[3] if len(px) > 3 else 256)
colors.append((px[0], px[1], px[2]))
content += char
content += '\n' # 每一行像素换行追加\n
colors.append((255, 255, 255)) # 给换行未来的颜色就是白色
return content, colors, pic_width, pic_height
灰度字符转图片
通过上一步方法,得到的content返回值,正式接下来需要存储如图片的字符内容,这里需要开启一个新的图像对象
结合对应颜色,将字符写入对应的像素点上,最后存储为图片,格式可以是jpg,这样比较小一些,png质量高,结果会大
def text2image(content, colors, pic_width, pic_height, path):
'''
字符存储为图像
@path: 存储路径
'''
image = Image.new("RGB", (pic_width, pic_height), (255, 255, 255)) # 创建存储图像对象
canvas = ImageDraw.Draw(image) # 创建一个支持绘制的画布
font = ImageFont.load_default().font # 直接使用默认字体对象
x = 0
y = 0
font_w, font_h = font.getsize(content[1]) # 字体的宽高
for i in range(len(content)): # 遍历字符内容对象
if content[i] == '\n': # 遍历到\n那就是下一行的元素了
x = -font_w # 每次初始化横纵坐标
y += font_h
continue
canvas.text((x, y), content[i], colors[i]) # 写入字符,带上颜色
x += font_w # 偏移一个字体的像素
image.save(path)
基本测试
对一张基本图像进行字符转化,并将结果存储为char.jpg
from PIL import Image, ImageFont, ImageDraw
def main():
content, colors, pic_width, pic_height = image2text(path="test.jpg")
text2image(content, colors, pic_width, pic_height, path="char.jpg")
GIF迭代
如果需要处理的是一个gif图像对象,那么首先将gif图像对象中的每一帧图片单独保存下来,接着图像解析为字符
接着将灰度字符转图片,最后再将字符图像组合为一个gif即可
def gif2image(path):
'''
gif图像拆分,并将拆分结果存储当前工作目录下的temp目录中
@path: gif图像位置
'''
img = Image.open(path)
work_path = os.getcwd() # 当前工作路径
cache_dir = os.path.join(work_path, 'gifTemp')
if not os.path.exists(cache_dir): # 如果不存在保存单独每一帧图片的目录,则创建该目录
os.mkdir(cache_dir)
while True:
try:
current = img.tell() # 获取当前帧位置
file_name = os.path.join(cache_dir, str(current)+'.png')
img.save(file_name)
img.seek(current+1) # 向下一帧读取
except EOFError: # GIF读取完毕
break
return current
之后即可通过for循环使用上面的图像处理的两个方法对其进行处理,处理完成的图像可以保存至content目录下
通过遍历content目录下的处理好的字符图片,对其进行gif拼接
import imageio
def image2gif(_id, dir_name='content', duration=15 / 130):
'''
将之前处理好的字符png图片组合成GIF图像
通过imageio模块处理合并
'''
path = os.path.join(os.getcwd(), dir_name)
images = []
for pic_id in range(_id):
# 遍历取出每一张处理后的字符图片id值
images.append(imageio.imread(os.path.join(path, '%d.png' % pic_id)))
# 从文件中读入数据
imageio.mimsave(os.path.join(os.getcwd(), 'fin.gif'),
images, duration=duration)