Python下字符画(ascii art)生成
之前在b站上看到有人用C写了个脚本把妹抖龙op转换成字符画的形式输出了,感觉比较好玩在下就用python也写了一遍(主要是因为python比较简单好用)。这里就这里就不介绍字符画了,因为能搜到这个的肯定知道自己在干什么 = =。
首先梳理转换思路:转换图片也就是转换视频,因为视频就是有连续的图片组成的。而为了简单起见,我们这里先把彩色图片先转换成黑白图片(将RGB三通道转换成一个),然后读取图片的每个像素点,根据像素点的灰度(grayscale)选择相应的字符(比如@#对应高灰度点,而.,对应低灰度点),将所有当前图片或帧的像素转换成字符后输出就是我们看到的字符画了。
这里我们可以看到这其实是一个很简单的事,最重要无非就是要根据灰度选择字符。这一点已经有好事者统计每个字符所占的像素帮大家做了(参考)。
完整版(灰度从高到低排列,注意最后的空白字符):$@B%8&WM#*oahkbdpqwmZO0QLCJUYXzcvunxrjft/\\|()1{}[]?-_+~<>i!lI;:,"^\
'. 而在实际应用中,发现**精简版**的打印效果更好(其实可以自己随便挑一些):
@%#*+=-:. `
代码实现:
为了简单起见,这里只讲如何将视屏直接打印到到终端(输出为黑白,不保留颜色),至于输出为其他文件形式具体可以参考这里
1.读取图片或视频
这里我们用Pillow或者opencv包进行图片读取和处理,后者可以用于视频操作,但是由于opencv读取视频的格式只支持avi,所以进行视频处理的时候我们需要另一个包:scikit-video;而如果是转换图片,那么写出可以用matplotlib包。
import skvideo.io
import cv2
def read_image(image_path, method="opencv"):
if self.method == "pillow":
img = PI.open(image_path)
grey_image = img.convert("L")
elif self.method == "opencv":
img = cv2.imread(image_path)
grey_image = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY)
return grey_image
def read_video(video_path):
videogen = skvideo.io.vreader(video_path)
for frame in videogen:
grey_frame = cv2.cvtColor(frame, cv2.COLOR_BGR2GRAY)
yield grey_frame # 由于视频包含的图片很多,为了防止一次处理的内存过大,这里用generator逐帧返回图片
- 将读取的图片各像素点转换成相应的字符
STANDARD_CHAR_LIST = list("$@B%8&WM#*oahkbdpqwmZO0QLCJUYXzcvunxrjft/\\|()1{}[]?-_+~<>i!lI;:,\"^'\`. ")
ABBREVIATED_CHAR_LIST = list("@%#*+=-:. ")
def img2ascii(grey_image, char_list=ABBREVIATED_CHAR_LIST, scale=1.0, method='pillow'):
if method == 'pillow':
scaled_img_width = int(grey_image.size[0] * scale) # 按比例放大或缩小图片
scaled_img_height = int(grey_image.size[1] * scale)
scaled_grey_img = grey_image.resize((scaled_img_width, scaled_img_height))
elif method == 'opencv':
scaled_grey_img = cv2.resize(grey_image, None, fx=scale, fy=scale)
scaled_img_width = len(scaled_grey_img[0])
scaled_img_height = len(scaled_grey_img)
char_list_length = len(char_list)
ascii_img = [[None for i in range(scaled_img_width)] for j in range(scaled_img_height)]
ascii_color = [x[:] for x in ascii_img]
for i in range(scaled_img_height):
for j in range(scaled_img_width):
if self.method == "pillow":
# brightness: the larger, the brighter, and later position in given char list
brightness = scaled_grey_img.getpixel((j, i))
elif self.method == "opencv":
brightness = scaled_grey_img[i, j]
ascii_img[i][j] = char_list[brightness * char_list_length / 255]
return ascii_img
3.打印图片/视频
打印图片和视频稍微有些不同,因为打印视频是希望能与原视频播放同步,因此实时读取转换打印的时间是无法在0.0278s(默认24帧/1s)内完成的,需要先将转换结果保存到数组然后再依次打印。
a)图片
output_str = ""
for line in ascii_img:
output_str += "".join(line) + "\n"
os.system(self.clear_console)
sys.stdout.write(output_str)
b)视频
total_video = []
video_temp_file = video_path + ".temp"
if not os.path.exists(video_temp_file):
with open(video_temp_file, "wb") as of:
first_frame = True
for grey_frame in read_video(video_path):
ascii_img = self.img2ascii(grey_image=grey_frame, char_list=char_list, scale=scale)
output_str = ""
for line in ascii_img:
output_str += "".join(line) + "\n"
total_video.append(output_str)
if first_frame:
of.write("{}\n".format(len(ascii_img))) # 再文件开头保存打印每一帧所需的行数
first_frame = False
of.write(output_str)
of.close()
else:
row = 1
frame_str = ""
with open(video_temp_file, "rb") as fi:
frame_row = int(fi.readline().strip())
for line in fi:
frame_str += line
if row % frame_row == 0:
total_video.append(frame_str)
frame_str = ""
row += 1
fi.close()
for frame in total_video:
sys.stdout.write(frame)
sys.stdout.flush() # 清空屏幕
sys.stdout.write("\x1b[0;0H") # 重新定位打印光标于最开始的为止(0,0)
time.sleep(0.015) # 设置与下一帧的打印时间间隔
注意:这里要选择将中间字符保存到文件,然后再读取打印,这是因为在打印的过程中读取和打印都需要时间,所以如果设置sleep时间为0.0278,那么将会出现播放时间延迟不同步,因此需要调整sleep的时间。为了避免每次调试重新转化需要大量时间,这里就进行了保存。
总结:转换图片到字符画并不是什么难的事情,不过在下发现转换以后的字符画跟原图相比就跟打了码一样,那么不免想到不妨用DL技术训练一个模型用于还原万恶的马赛克,岂不美哉2333
参考
http://paulbourke.net/dataformats/asciiart/
http://pillow.readthedocs.io/en/3.0.x/handbook/tutorial.html
opencv官方文档(随便找找,这边忘了链接就不给了)
Python下字符画(ascii art)生成的更多相关文章
- python中字符与ascii码转换
ASCII码转字符用chr()函数: 字符转ASCII码用ord()函数:
- 图片处理拓展篇 : 图片转字符画(ascii)
首先要明确思路, 图片是由像素组成的, 不同的像素有不同的颜色(rgb), 那么既然我们要转化为字符画, 最直接的办法就是利用字符串来替代像素, 也就是用不同的字符串来代表不同的像素. 另外图片一般来 ...
- python学习 —— 字符画
代码: import os from PIL import Image WIDTH = int(250) HEIGHT = int(250/2) ascii_char = list('toahkbdp ...
- AsciiMorph - 新奇的 ASCII 字符画生成工具&插件
AsciiMorph 是一个新奇的 ASCII 字符画生成工具和开源插件.字符画(ASCII Art)的历史可以追溯到几十年前,起初是用在图形显示功能受限的设备上,用ASCII字符集里的可打印字符来拼 ...
- 使用Python生成ASCII字符画
使用Python生成ASCII字符画 在很多的网站主页中或者程序的注释中会有一些好看的字符注释画.显得很牛逼的样子 例如: 知乎 _____ _____ _____ _____ /\ \ /\ \ / ...
- python生成字符画
python生成字符画 这个idea来自于实验楼,非常适合练习PIL的像素处理,更重要的是非常有意思. 环境配置 依赖的第三方库就是PIL(Python Image Library),可以直接使用pi ...
- 将图片转为ASCII字符画
原文:将图片转为ASCII字符画 Copyright 2012 Conmajia 源代码下载:点击这里 什么是字符画?就是用ASCII字符来近似组成图像,就像这样: ╭╮ ╭╮ ││ ││ ╭┴┴—— ...
- Python 图片转字符画
Python 图片转字符画 一.课程介绍 1. 课程来源 原创 2. 内容简介 本课程讲述怎样使用 Python 将图片转为字符画 3. 前置课程 Python编程语言 Linux 基础入门(新版) ...
- python图片转字符画(转)
先上代码: from PIL import Image import argparse #命令行输入参数处理 parser = argparse.ArgumentParser() parser.add ...
随机推荐
- Maven Tomcat:run 使用tomcat7
<build> <finalName>service</finalName> <resources> <resource> <dire ...
- andorid——自定义seekbar(转)
xml 新建=>xml=>values <?xml version="1.0" encoding="utf-8"?> <resou ...
- drupal pathauto的配置
- oozie 客户端常用命令
1.提交作业,作业进入PREP状态 oozie job -oozie http://localhost:11000/oozie -config job.properties -submit job: ...
- UK 更新惊魂记
本文前提是.由于更easy安装各种webserver.数据库,redis缓存.mq等软件,笔者使用Ubuntu Kylin作为开发系统已经好长时间了. 而今天(2015-07-23)下午2时许,系统提 ...
- Ubuntu16.04怎么将桌面左侧的启动器移动到屏幕底部
与其他 Linux 发行版不同,Ubuntu 多年来一直使用 Unity 做桌面环境,该环境的最突出特点就是桌面左侧有一个启动器栏(Launcher).从 16.04 版本开始,Ubuntu 提供了一 ...
- 【Django】 积累
■ 数据库的长连接 众所周知,数据库的长连接可以在一定程度上提高整个应用的读写效率,节省创建和销毁数据库连接的成本.Django在1.6版本之后就已经支持了长连接的设置,是在settings中的DAT ...
- R语言 使用命令行参数运行R程序
args_test.R 代码如下: Args <- commandArgs()cat("Args[1]=",Args[1],"\n")cat(" ...
- matlab中log函数与rssi转距离
我们通常所说的log是指以10为底的对数,而MATLAB中的log却不是这样.Matlab中的log函数在默认情况下是以e为底,即loge,如果需要计算以10为底的对数,那么需要用log10()函数. ...
- [技术选型] dubbo
分布式服务架构 - 阿里开源项目 简介 Dubbo架构设计详解 Dubbo与Zookeeper.SpringMVC整合和使用(负载均衡.容错)