Python语言系统学习19:作品——基于PIL模块的图片转换字符画

原文链接:https://blog.csdn.net/weixin_45595560/article/details/103651606

本项目程序基于python3环境编写。

字符画是一系列字符的组合,每个字符由于构成的不同,可以视为颜色深浅不一的像素点,聚合在一起构成的图像。根据成像效应,大脑会将视野中相似的像素色块合并,得到一个更加易于理解的图案,这也是人眼分辨字符画内容的理论依据。

基本原理

灰度值是一个像素的固有属性,灰度值决定了该像素在去色条件下的黑白值,也决定该像素在非去色条件下的饱和度。本程序建立字符画的基础在于:构建一个字符与灰度值的映射关系,再由程序将图像中的像素点代替为不同的字符,完成字符画的构建。

灰度值的计算公式为:

gray = 0.2126 * r + 0.7152 * g +
0.0722 * b

通过PIL的函数计算出每个像素点的灰度值,再argparse建立一个灰度值与字符的灰度关系映射。通过映射关系将像素点转化为字符存于数组里。

设计思想

程序设计时需要考虑以下几个要点:

  • argparse输出字符画的尺寸
  • 构建字符灰度字典
  • PIL对于像素灰度值的确定与灰度关系映射
  • getpixel函数得到像素点数据后的传递与处理
  • 双循环结构确立一个数组矩阵

所以在程序中,我将代码分为了三部分。

一、关于argparse


#命令行输入参数处理

parser = argparse.ArgumentParser()

 

parser.add_argument('file')     #输入文件

parser.add_argument('-o', '--output')   #输出文件

parser.add_argument('--width', type = int,
default = 80) #输出字符画宽

parser.add_argument('--height', type = int,
default = 80) #输出字符画高

 

#获取参数

args = parser.parse_args()

 

IMG = args.file

WIDTH = args.width

HEIGHT = args.height

OUTPUT = args.output

argparse模块使用时要先创立ArgumentParser对象,它包含了将命令行参数解析为python语句的全部参数。

需要先给ArgumentParser添加参数,在对其进行解析

通过Add_argument函数给ArgumentParser添加参数,再通过parse_args进行数据解析。

关于argparse.ArgumentParser():
 


class argparse.ArgumentParser(prog=None,
usage=None, description=None, epilog=None, parents=[],
formatter_class=argparse.HelpFormatter, prefix_chars='-',
fromfile_prefix_chars=None, argument_default=None, conflict_handler='error',
add_help=True, allow_abbrev=True)

prog - 程序的名称(默认:sys.argv[0])

usage - 描述程序用途的字符串(默认值:从添加到解析器的参数生成)

description - 在参数帮助文档之前显示的文本(默认值:无)

epilog - 在参数帮助文档之后显示的文本(默认值:无)

parents - 一个ArgumentParser 对象的列表,它们的参数也应包含在内

formatter_class - 用于自定义帮助文档输出格式的类

prefix_chars - 可选参数的前缀字符集合(默认值:’-’)

fromfile_prefix_chars - 当需要从文件中读取其他参数时,用于标识文件名的前缀字符集合(默认值:None)

argument_default - 参数的全局默认值(默认值: None)

conflict_handler - 解决冲突选项的策略(通常是不必要的)

add_help - 为解析器添加一个-h/–help 选项(默认值: True)

allow_abbrev - 如果缩写是无歧义的,则允许缩写长选项 (默认值:True)

对于ArgumentParser,我们仅需要控制字符画的长和宽,以及get文件以及output,所以我们仅需要添加所需要的参数进入ArgumentParser并对其进行解析。

灰度字典、灰度值的计算与映射关系


ascii_char =
list("$@B%8&WM#*oahkbdpqwmZO0QLCJUYXzcvunxrjft/\|()1{}[]?-_+~<>i!lI;:,\"^`'.
")

ascii_char是我们自定义的灰度字典。而list中,数据从左到右读出,所以我们仅需要在构建字典时,确保字典由左到右是灰度值从大到小即可。在灰度值解析完成后,会通过这个字典构建灰度映射关系。


def get_char(r,g,b,alpha = 256):

   
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)]

get_char会对得到的像素进行数据分析,解析出RGB三色值及像素Alpha值,将三色值带入灰度值计算公式中,得到该像素点的灰度值。unit函数则计算了每个字符可以代表多大范围的灰度值,籍此构建灰度映射关系。最后借助灰度字典,确定此像素的gray应该用字典中第几个字符代替。

判断alpha值,若为零直接输出空格,提高运行效率。
 

Main函数


if __name__ == '__main__':

 

   
im = Image.open(IMG)

   
im = im.resize((WIDTH,HEIGHT), Image.NEAREST)

 

   
txt = ""

 

   
for i in range(HEIGHT):

       
for j in range(WIDTH):

           
txt += get_char(*im.getpixel((j,i)))

       
txt += '\n'

 

   
print(txt)

Image.open负责IMG方式打开文件,IMG在前面已经定义过,是借助argparse中的打开方式。resize重置了图片的尺寸,根据WIDTH与HEIGHT来重新构建图片,此二变量均为在第一部分通过argparse定义的长和宽,NEAREST将图片质量设为最低,合并相似的像素点。txt变量负责储存文本。

双循环负责构建一个字符矩阵,i变量负责控制行数,j变量负责控制列数。

假设,我们仅让双循环负责print一个Ascii字符〇,Height设为10,Weight设为10,则会print出如下图像:

〇〇〇〇〇〇〇〇〇〇

〇〇〇〇〇〇〇〇〇〇

〇〇〇〇〇〇〇〇〇〇

〇〇〇〇〇〇〇〇〇〇

〇〇〇〇〇〇〇〇〇〇

〇〇〇〇〇〇〇〇〇〇

〇〇〇〇〇〇〇〇〇〇

〇〇〇〇〇〇〇〇〇〇

〇〇〇〇〇〇〇〇〇〇

〇〇〇〇〇〇〇〇〇〇

鉴于CSDN上字符间距与行宽不等,且不为等宽字体,所以上方举例尽可能采用等宽字体显示。

这段代码中,我们可以理解为,双循环已经构建完成了图片像素矩阵,getpexil仅需要直接从数组中调用像素数据进行分析。

j 变量循环,每一次都会调用get_char函数进行像素分析,并将结构输出到txt中,循环结束时进行换行,进行下一个 i 变量循环。

最后将txt print到终端状态栏,并且新建一个txt文档储存转换完成的 txt 变量。

附上源码


# -*- coding=utf-8 -*-

 

from PIL import Image

import argparse

 

#命令行输入参数处理

parser = argparse.ArgumentParser()

 

parser.add_argument('file')     #输入文件

parser.add_argument('-o', '--output')   #输出文件

parser.add_argument('--width', type = int,
default = 80) #输出字符画宽

parser.add_argument('--height', type = int,
default = 80) #输出字符画高

 

#获取参数

args = parser.parse_args()

 

IMG = args.file

WIDTH = args.width

HEIGHT = args.height

OUTPUT = args.output

 

ascii_char =
list("$@B%8&WM#*oahkbdpqwmZO0QLCJUYXzcvunxrjft/\|()1{}[]?-_+~<>i!lI;:,\"^`'.
")

 

# 将256灰度映射到70个字符上

def get_char(r,g,b,alpha = 256):

   
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)]

 

if __name__ == '__main__':

 

   
im = Image.open(IMG)

   
im = im.resize((WIDTH,HEIGHT), Image.NEAREST)

 

   
txt = ""

 

   
for i in range(HEIGHT):

       
for j in range(WIDTH):

           
txt += get_char(*im.getpixel((j,i)))

       
txt += '\n'

 

   
print(txt)

 

    #字符画输出到文件

   
if OUTPUT:

       
with open(OUTPUT,'w') as f:

           
f.write(txt)

   
else:

       
with open("output.txt",'w') as f:

           
f.write(txt)

 

发布了413 篇原创文章 · 获赞 1104 · 访问量 81万+

猜你喜欢

转载自blog.csdn.net/qingwufeiyang12346/article/details/103778429