之前在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逐帧返回图片
  1. 将读取的图片各像素点转换成相应的字符
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)生成的更多相关文章

  1. python中字符与ascii码转换

    ASCII码转字符用chr()函数:  字符转ASCII码用ord()函数:  

  2. 图片处理拓展篇 : 图片转字符画(ascii)

    首先要明确思路, 图片是由像素组成的, 不同的像素有不同的颜色(rgb), 那么既然我们要转化为字符画, 最直接的办法就是利用字符串来替代像素, 也就是用不同的字符串来代表不同的像素. 另外图片一般来 ...

  3. python学习 —— 字符画

    代码: import os from PIL import Image WIDTH = int(250) HEIGHT = int(250/2) ascii_char = list('toahkbdp ...

  4. AsciiMorph - 新奇的 ASCII 字符画生成工具&插件

    AsciiMorph 是一个新奇的 ASCII 字符画生成工具和开源插件.字符画(ASCII Art)的历史可以追溯到几十年前,起初是用在图形显示功能受限的设备上,用ASCII字符集里的可打印字符来拼 ...

  5. 使用Python生成ASCII字符画

    使用Python生成ASCII字符画 在很多的网站主页中或者程序的注释中会有一些好看的字符注释画.显得很牛逼的样子 例如: 知乎 _____ _____ _____ _____ /\ \ /\ \ / ...

  6. python生成字符画

    python生成字符画 这个idea来自于实验楼,非常适合练习PIL的像素处理,更重要的是非常有意思. 环境配置 依赖的第三方库就是PIL(Python Image Library),可以直接使用pi ...

  7. 将图片转为ASCII字符画

    原文:将图片转为ASCII字符画 Copyright 2012 Conmajia 源代码下载:点击这里 什么是字符画?就是用ASCII字符来近似组成图像,就像这样: ╭╮ ╭╮ ││ ││ ╭┴┴—— ...

  8. Python 图片转字符画

    Python 图片转字符画 一.课程介绍 1. 课程来源 原创 2. 内容简介 本课程讲述怎样使用 Python 将图片转为字符画 3. 前置课程 Python编程语言 Linux 基础入门(新版) ...

  9. python图片转字符画(转)

    先上代码: from PIL import Image import argparse #命令行输入参数处理 parser = argparse.ArgumentParser() parser.add ...

随机推荐

  1. Linxu 学习记录

    1.配置Java人环境变量,设置后需要使用命令: source /etc/profile 让配置立即生效. # jdk7 settings JAVA_HOME=/usr/jdk1..0_79 JRE_ ...

  2. background-size 导致的背景不居中问题

    1. 理想中的效果是这样的   2. 然后想改一下圆圈的大小   3. 容器大小为偶数,图片大小为奇数,就会使图片并不真正居中,如果仔细量一下,会发现图片上面比下面多1px! 貌似只有 backgro ...

  3. Altium Designer 规则设置

    设计规则设置 Designer Rules Check(DRC) Electrical 电气规则.安全间距,线网连接等 Routing 布线,线宽.过孔形状尺寸.布线拓扑.布线层.封装出线等 SMT ...

  4. nano 命令 linux

    用途说明 nano是一个字符终端的文本编辑器,有点像DOS下的editor程序.它比vi/vim要简单得多,比较适合Linux初学者使用.某些Linux发行版的默认编辑器就是nano.(nano - ...

  5. 关于VS2008和VS2013中字体的选择

    我这学期上ASP.NET的课,用C#语言,配合VS2008.自己课余在研究手机游戏的开发,用的是C++语言,配合VS2013.这两种开发环境我自己试过好多字体,后来感觉适合我自己的应该是以下这两种: ...

  6. Java相对路径/绝对路径总结

    Version:0.9 StartHTML:-1 EndHTML:-1 StartFragment:00000099 EndFragment:00019826 Java相对路径/绝对路径总结(2) 修 ...

  7. numpy reshape resize用法

    https://docs.scipy.org/doc/numpy/reference/generated/numpy.resize.html a = np.zeros((100,28*28)) pri ...

  8. c 预处理的宏定义

    概念 以“#”号开头的都是预处理命令 例如 #include <stdio.h>宏定义 宏定义无参数的宏名后不带参数# 表示这是一条预处理命令, define 为宏定义命令.“标识符”为所 ...

  9. SAP Actual Costing with Material Ledger 激活实际成本后台配置

    Actual Costing with Material Ledger 1      Purpose This configuration guide provides the information ...

  10. 多线程系列三:Lock和Condition

    有了synchronized为什么还要Lock? 因为Lock和synchronized比较有如下优点 1. 尝试非阻塞地获取锁 2. 获取锁的过程可以被中断 3. 超时获取锁 Lock的标准用法 p ...