如何挽救鉴黄师的职业生涯 - Python绘制像素图

2,351 阅读6分钟

欢迎访问集智主站:集智,通向智能时代的引擎

注意!

本文所有代码均可在集智原贴中运行调试,有需要的同学可以点击这里前往原贴



01. ASCII艺术

ASCII的全称是American Standard Code for Information Interchange,即美国信息交换标准码。是由军用电报编码发展而来,并成为最通用的现代计算机编码系统。

在显卡还不能摧毁航母战斗群的年代,计算机还主要用来计算导弹弹道和卫星轨道,其图形处理能力是非常弱的,甚至还不如今天高级一点的示波器。但这并不能阻止人类对美的追求,正如四万年前的莽荒也没有耽误拉斯科洞窟壁画的诞生。

拉斯科洞窟壁画

在我刚上网那阵,有个流传很广的帖子,是教你观看命令行里的《星球大战》,而这部星战正是由ASCII编码中的字符构成的,这被称作ASCII art

ASCII星球大战

在斗图代替打字、点播变成直播甚至VR/AR的今天,图形的处理已经不再是瓶颈,反而成为了新的增长点。道高一尺魔高一丈,技术的进步也带来了有害信息,比如广大家长朋友们特别关注的色情信息。刚开始色情的鉴定是由人工完成的,李迪同志就是在《暴走大事件》中扮演鉴黄师唐马儒而一炮走红。

但是,基于人工智能的图像识别也在飞速进步,自动鉴黄已经投入实用,鉴黄师的职业前景就面临着严重的威胁!

02. 字符画

把一张照片转换为字符画,大致需要三步:

  1. 将图片尺寸压缩到字符画所能接受的量级;
  2. 彩色图转换为灰度图,灰度是一个0-255的数值;
  3. 建立灰度值与字符集之间的映射关系。

早年间以上步骤还需要专门做一个小软件来完成,而现在只需要简单的代码就可以直接在浏览器中实现。

典型的字符画,看窗口边框大概就知道年代

因为字符画的本质是“字符”,可以用文本编辑器打开,对于计算机来说,他们和其他的字符没有任何区别,“画”的性质只是由人类的想象力后天赋予的。所以,如果你用字符画的形式传播春宫图,还是需要唐马儒。

03. Python扩展库

针对字符画生成的基础功能,Python已经内置了很多优秀的扩展库,可以在此基础上直接调用,而无需重复制造轮子。

  • 图库PIL(Python Imaging Library)

    基本的图像处理功能。

  • 网库urllib

    获取网络资源,如下载网上的图片。

from PIL import Image # 图像处理模块
from urllib import request #网络请求模块

没有必要把0-255的灰度值一一对应为不同的字符,一般十几个也就足够了。这里做如下定义:

ASCII_CHARS = [' ', '#', '?', '%', '.', '+', '.', '*', ':', ',', '@']

这里将最低的灰度段映射为' '(空格),也就是原图中空白或接近空白的部分,在字符画中也会会变成空白;而原图的黑点则变成@

读者朋友可以在后文的开放代码环境中想要定义自己的字符,并可以传入任意网络图片做实验。

04. 图片预处理

4K屏已经逐渐普及,现在差不多的电脑显示器也能支持2K分辨率,如果把每个像素点都变成一个字符,那出来的图片实在是太大了。

所以首先要对源图片进行压缩,再转换为灰度模式,即抛出色彩信息。

# 压缩图片
def scale_image(image, new_width=60):
    (original_width, original_height) = image.size # 获取原图尺寸
    aspect_ratio = original_height/float(original_width) * 0.5 # 计算高宽比,因为输出文本有2倍行距,所以乘0.5维持高宽比
    new_height = int(aspect_ratio * new_width)
    new_image = image.resize((new_width, new_height))
    return new_image

# 灰度模式
def convert_to_grayscale(image):
    return image.convert('L') # 调用image对象自己的.convert()方法

05. 图片到字符

然后是建立图片(压缩后)像素点到字符集的映射关系。

def map_pixels_to_ascii_chars(image, range_width=25):

    # 将每个像素根据其灰度值映射为一个字符,每个字符对应25个灰度值
    pixels_in_image = list(image.getdata()) # 获取原图灰度值列表   
    pixels_to_chars = [ASCII_CHARS[int(pixel_value/range_width)] for pixel_value in pixels_in_image]
    # 对于每个像素点,将其灰度值转换为列表ASCII_CHARS的索引
    return "".join(pixels_to_chars)[/amalthea_sample_code]

最后综合前面几个函数,以文本形式输出字符画。

def convert_image_to_ascii(image, new_width=60):
    image = scale_image(image, new_width) # 调用scale_image()函数,压缩图片
    image = convert_to_grayscale(image) # convert_to_grayscale()函数,转换为灰度图

    pixels_to_chars = map_pixels_to_ascii_chars(image) # 映射至字符集
    len_pixels_to_chars = len(pixels_to_chars) # 获取字符集长度

    image_ascii = [pixels_to_chars[index: index + new_width] for index in range(0, len_pixels_to_chars, new_width)]

    return "\n".join(image_ascii)


def handle_image_conversion(image_filepath, new_width=60):
    image = Image.open(image_filepath) # Image.open()打开源图片
    image_ascii = convert_image_to_ascii(image, new_width) # 调用上面的convert_image_to_ascii()函数
    print(image_ascii) # 输出字符画

06. 图源采集

接下来我们可以将任意图片转换为字符画看看效果,为了充分发扬互联网精神,目前仅支持具有网络地址的图片(其实是图片上传系统还没做好)。

下面来看一个实例,将一张QQ企鹅的图标转换为字符画,点击运行即可得到字符画。

from PIL import Image

ASCII_CHARS = [' ', '#', '?', '%', '.', '+', '.', '*', ':', ',', '@']


def scale_image(image, new_width=60):
    """Resizes an image preserving the aspect ratio.
    """
    (original_width, original_height) = image.size
    aspect_ratio = original_height/float(original_width) * 0.5
    new_height = int(aspect_ratio * new_width)

    new_image = image.resize((new_width, new_height))
    return new_image


def convert_to_grayscale(image):
    return image.convert('L')


def map_pixels_to_ascii_chars(image, range_width=25):

    pixels_in_image = list(image.getdata())    
    pixels_to_chars = [ASCII_CHARS[int(pixel_value/range_width)] for pixel_value in
            pixels_in_image]

    return "".join(pixels_to_chars)


def convert_image_to_ascii(image, new_width=60):
    image = scale_image(image, new_width)
    image = convert_to_grayscale(image)

    pixels_to_chars = map_pixels_to_ascii_chars(image)
    len_pixels_to_chars = len(pixels_to_chars)

    image_ascii = [pixels_to_chars[index: index + new_width] for index in
            range(0, len_pixels_to_chars, new_width)]

    return "\n".join(image_ascii)

def handle_image_conversion(image_filepath, new_width=60):
    image = Image.open(image_filepath)
    image_ascii = convert_image_to_ascii(image, new_width)
    print(image_ascii)

from urllib import request
    
image_file_path = 'image2ascii.jpg' # 图片的本地名称
image_url = "http://upload.wikimedia.org/wikipedia/en/thumb/9/9c/Tencent_QQ.png/64px-Tencent_QQ.png" # 图片的网络地址
request.urlretrieve(image_url, image_file_path) # 将网络图片下载到本地,并重命名
handle_image_conversion(image_file_path) # 启动handle_image_conversion()这个总函数

07. 开放空间

上节的例子仍然保留了默认的字符集和默认图像宽度60,接下来的部分留给读者自由发挥,可以通过修改如下参数获得自己的字符画:

  • new_width:字符画的尺寸(宽的字符数)
  • ASCII_CHARS:字符集
  • image_url:网络图片地址,就是你想要转换的图片

操作示例:

当你在网上看到一张图片,右键-复制图片地址。

http://weibo.com/kaiser0730

将图片地址赋值予变量image_url,点击运行即可。

替换相应变量

from PIL import Image
from urllib import request

def scale_image(image, new_width=60):
    (original_width, original_height) = image.size
    aspect_ratio = original_height/float(original_width) * 0.5
    new_height = int(aspect_ratio * new_width)

    new_image = image.resize((new_width, new_height))
    return new_image

def convert_to_grayscale(image):
    return image.convert('L')

def map_pixels_to_ascii_chars(image, range_width=25):
    pixels_in_image = list(image.getdata())    
    pixels_to_chars = [ASCII_CHARS[int(pixel_value/range_width)] for pixel_value in
            pixels_in_image]

    return "".join(pixels_to_chars)

def convert_image_to_ascii(image, new_width=60):
    image = scale_image(image, new_width)
    image = convert_to_grayscale(image)
    pixels_to_chars = map_pixels_to_ascii_chars(image)
    len_pixels_to_chars = len(pixels_to_chars)
    image_ascii = [pixels_to_chars[index: index + new_width] for index in
            range(0, len_pixels_to_chars, new_width)]
    return "\n".join(image_ascii)

def handle_image_conversion(image_filepath, new_width=60):
    image = Image.open(image_filepath)
    image_ascii = convert_image_to_ascii(image, new_width)
    print(image_ascii)

image_file_path = 'image2ascii.jpg' # 图片的本地名称
new_width = 60 # 字符画宽几个字符    

ASCII_CHARS = [' ', '#', '?', '%', '.', '+', '.', '*', ':', ',', '@'] # 字符集

image_url =  "http://upload.wikimedia.org/wikipedia/en/thumb/9/9c/Tencent_QQ.png/64px-Tencent_QQ.png" # 图片的网络地址
request.urlretrieve(image_url, image_file_path) 

handle_image_conversion(image_file_path, new_width)

摘掉眼镜看效果更佳。

Sublime编辑器里的成像效果


推荐阅读

“女生科技体验节” TensorFlow Workshop

这评论有毒!——文本分类的一般套路

我做了一个叫“瑟曦”的机器人,可是她动不动就想让格雷果爵士弄死我。