前言

这几年一直在it行业里摸爬滚打,一路走来,不少总结了一些python行业里的高频面试,看到大部分初入行的新鲜血液,还在为各样的面试题答案或收录有各种困难问题



图像转字符

  1. 构建基本方法,将像素对应灰度值转化为可以代表的字符
  2. 图像内容解析,转化为字符内容
  3. 字符内容转化为像素,写入图像
  4. gif解析为单独的每一帧图片经过以上步骤处理
  5. 将处理完成的单独图像组合成一个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)