Python每日一練(7)-圖片轉字符畫

1. 效果演示

字符畫是一系列字符組合成的文本,看起來就像一幅畫一樣,如圖1所示。如果我們要手寫一個字符畫,首先要有紮實的美術基礎,其次還要花費大量的時間和精力。對於零基礎小白而言,困難可想而知。
在這裏插入圖片描述
但是不要發愁,我們可以使用Python,只需要幾行代碼,就能夠將一張圖片輕而易舉地轉化爲一個字符畫。這張圖片可以是黑白圖片,轉化效果如圖2所示。也可以彩色圖片,轉化效果如圖3所示。
在這裏插入圖片描述

2. 顏色處理

在完成這個案例之前,我們需要一些前置的知識。讀者這裏使用PS隨意打開一張本地的圖片,然後放大,效果如圖所示。
在這裏插入圖片描述
由圖中我們可以看出一張圖片實質是由一個個的小格子組成的,這個小格子就是一個像素,把這些小格子描上點實際上就構成了我們的一幅畫。那這個案例的本質就是用字符去將一個個的小格子給替換掉,相同像素就使用同一個字符替換,這樣最後就形成我們的字符畫了。在瞭解案例的原理之後,爲了方便去處理像素格,我們還需要了解一下其他的幾個概念。

灰度值 :指黑白圖像中點的顏色深度,範圍一般從0到255, 白色爲255,黑色爲0,故黑白圖片也稱灰度圖像。
在這裏插入圖片描述
RGB色彩:RGB色彩模式是工業界的一種顏色標準。通過對紅(R)綠(G)藍(B) 三個顏色通道的變化以及它們相互之間的疊加來得到各式各樣的顏色的。
在這裏插入圖片描述
RGB 即是代表紅、綠、藍三個通道的顏色,這個標準幾乎包括了人類視力所能感知的所有顏色,是目前運用最廣的顏色系統之一。
在這裏插入圖片描述
灰度值和RGB色彩間產生映射:
在這裏插入圖片描述
答案:使用灰度值公式,將像素的RGB值映射到灰度值

# r代表紅色 g代表綠色 b代表的藍色
# gray: 代表的是灰度值
gray = 0.2126 * r + 0.7152 * g + 0.0722 * b

注意這個公式並不是一個真實的算法,而是簡化的公式,真實的公式更復雜一些,不過在我們的這個應用場景下並沒有必要使用真實的公式。

灰度值和字符間的映射:
在這裏插入圖片描述(1) 首先我們需要有個字符串

# 字符串左邊密集,會映射到黑色,黑色的灰度值爲0
# 字符串右邊稀疏,會映射到白色,白色的灰度值爲255
color_str = "wmzвгдеёжзийклмнопрсoahkbdpqjftZO0QLCJUYX/\|()1{}[]?-_+~<>i!lI;:,\"^`'. "

(2) 然後我們將字符串轉換成列表

# 通過list()函數進行轉換
ascii_char = list(color_str)

在這裏插入圖片描述
這裏因爲數軸長度的原因,沒有將所有的字符都列舉出來,實際是有70個字符去等分256個灰度值的。
(3) 最後通過公式來,將灰度值映射到字符

# 灰度值範圍爲0-255,而字符集只有70
# 需要進行如下處理才能將灰度值映射到指定的字符上
# length代表的是字符的長度,即列表ascii_char的長度
unit = (256.0 + 0) / length  # 得到的結果表示一個字符代表了灰度值中的3個值

# 返回灰度值對應的字符
arg_str = ascii_char[int(gray / unit)]  # ascii_char[index]
# 可以使用整除
# arg_str = ascii_char[gray // unit]

3. 圖像處理

剛纔在上一節,我們已經學習瞭如何將彩色值處理成黑白色值。那麼,我們的彩色值如何來獲取呢?

3.1 使用PIL(pillow)處理圖像

(1) 首先介紹PIL的使用

PIL是一個Python圖像處理庫,是本案例使用的重要工具,使用下面的命令來安裝pillow(PIL)庫:一定要聯網安裝

pip install -i http://pypi.douban.com/simple --trusted-host pypi.douban.com pillow

(2) 獲取圖片對象

from PIL import Image
# 使用open()來獲取圖片
img = Image.open(path)  # img圖片對象
# path: 圖片地址,字符串類型

(3) 獲取圖片大小

# size是屬性,沒有括號
WIDTH, HEIGHT = img.size

(4) 重設圖片大小

# 使用resize()重新設置圖片大小
new_img = img.resize((width, height),Image.ANTIALIAS)
# width: 圖片寬度,數字類型
# height: 圖片高度,數字類型
# Image.ANTIALIAS: 高質量
# new_img: 新圖片

(5) 獲取某個位置的色彩值

rgb_s = img.getpixel((,))
# (列, 行): 座標(列, 行)位置
# rgb_s: RGB像素值(有的時候會包含alpha值),返回的結果是一個元組,例如 (1,2,3)或者(1,2,3,0)。

(6) 修改圖片名稱和類型

# 使用save()函數進行圖片的保存
new_img.save(fileout, type)
# fileout: 圖片名稱,字符串類型,如: hello.png
# type: 圖片類型,字符串類型,如: png/gif/jpg

3.2 完整代碼

from PIL import Image  # 導入


def resize_image(pic_in, pic_out, width, height, pic_type):
    img = Image.open(pic_in)
    new_img = img.resize((width, height), Image.ANTIALIAS)
    new_img.save(pic_out, pic_type)


if __name__ == '__main__':
    pic_in = r"timg.jpeg"  # 輸入圖片
    pic_out = r"out.png"  # 輸出圖片
    width = 600  # 輸出圖片寬度
    height = 600  # 輸出圖片高度
    pic_type = "png"  # 輸出圖片類型
    resize_image(pic_in, pic_out, width, height, pic_type)

4. 案例代碼

# -*- coding: utf-8 -*-
# @Time    : 2020/4/6 20:19
# @Author  : 我就是任性-Amo
# @FileName: test.py
# @Software: PyCharm
# @Blog    :https://blog.csdn.net/xw1680

from PIL import Image  # 導入


def get_char(r, g, b, alpha=256):
    if alpha == 0:
        return " "
    length = len(ascii_char)  # 獲取字符集的長度
    gray = 0.2126 * r + 0.7152 * g + 0.0722 * b  # 將RGB值轉爲灰度值
    unit = 256 / length  # 灰度值有256個 而字符集只有70個
    return ascii_char[int(gray / unit)]


def get_new_img(img_name=""):
    im = Image.open(img_name)  # 打開並調整圖片的寬和高
    WIDTH, HEIGHT = im.size  # 獲取圖片的寬和高
    print(f"圖片原始的寬和高爲:{WIDTH},{HEIGHT}")
    if not WIDTH or not HEIGHT:
        pass  # 防止獲取到的圖片是損壞的圖片,因此進行判斷
        # 如果是損壞的,寬或者高就沒有數據
    # 如果沒有損壞,寬或高就有數據
    else:
        if 299 >= WIDTH or 299 >= HEIGHT:
            WIDTH = int(WIDTH / 1.4)
            HEIGHT = int(HEIGHT / 1.4)
        if 500 >= WIDTH >= 300 or 500 >= HEIGHT >= 300:
            WIDTH = int(WIDTH / 2)
            HEIGHT = int(HEIGHT / 2)
        if 1000 > WIDTH > 500 or 1000 > HEIGHT > 500:
            WIDTH = int(WIDTH / 3.8)
            HEIGHT = int(HEIGHT / 3.8)
        if WIDTH >= 1000 or HEIGHT >= 1000:
            WIDTH = int(WIDTH / 10)
            HEIGHT = int(HEIGHT / 10)
    print(f"修改後圖片的寬和高爲:{WIDTH},{HEIGHT}")
    """
    Image.ANTIALIAS: 高質量
    Image.NEAREST: 低質量
    Image.BILINEAR: 雙線性
    Image.BICUBIC: 三次樣條插值
    """
    im = im.resize((WIDTH, HEIGHT), Image.ANTIALIAS)
    return im


def get_str_img(im, output=None):
    WIDTH, HEIGHT = im.size  # 獲取圖片的寬和高
    txt = ""  # 初始化輸出的字符串
    # 遍歷圖片的中的每一行
    for i in range(HEIGHT):
        # 遍歷圖片中的每一列
        for j in range(WIDTH):
            txt += get_char(*im.getpixel((j, i)))
        # 遍歷完一行後需要增加換行符
        txt += "\n"
    # 字幅畫輸出到文件
    if output:
        with open(output, "w") as file:
            file.write(txt)
    else:
        with open("output.txt", "w") as file:
            file.write(txt)


if __name__ == '__main__':
    # 定義字符
    ascii_char = list("$@B%8&WM#*oahkbdpqwmZO0QLCJUYXzcvunxrjft/\|()1{}[]?-_+~<>i!lI;:,\"^`'. ")
    img = get_new_img("timg.jpeg")
    get_str_img(img)

運行效果如圖所示:
在這裏插入圖片描述

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章