• Python制作字符图片


    背景

    字符图片,即纯使用字符构造出一幅图片。关于这个,网上的教程和程序已经非常多了,都是使用不同复杂程度的字符模拟图片的灰度(比如字符'@'就比字符','复杂,但是我要做的是像这样的:(原图是星之卡比)

                                           ------------------
                                     ---(() ----------------/((/---
                                  -()/---                      ---(()--
                              --(//-                                --/--
                           -/()--                                      -)//--
                          /(/(-           --/)--         -//            )/
                        /()-             )(  \        /)- -((             -/
                       / /               (|   ) |      |(|   |)               ) 
                     -/ (                | --- |      | --/-||               \-
                 --/(|)(                 |      |      |      ||                -(|()--
               //-( -(                 |(|--- |      |((---||                  )//-/)
             /()(-  --       ---- -----  -/( //)        )- /-(  ----------       -   --(/
            / /     ||       -       - |  )())/-        -()/  |         ||      ||     )/
           / (      |       ----------        -(-  --(-        ----------       )|      )(
          |(|       |/(                         ---// --                         )|        |
          |((-       |)(                                                        /(|       -) |
           /\------ -/                                                      -(-/-------) /
            ()---   (/(                                                   /()|  --/-(//
              ---/////  -                                                // ) /)/(  ---
                       ()- -                                           -/--// /
                      ---/------                                    -/-- -/  --
                    /-)/- -/   -----                            ------ -/ /----/--
                  /(/-      --()--   -------                 ------   --/--     -/-
                -/ /           --(/--     ------------------    --/-(---         -/
               )(/ --             ---//)---                -----)/--             --) 
               |) - /                  --- /()()/------(  (  ----                |( \)(
               || --                           //----/  --                      --| |
               /(                        ---(/--      --(/-- ---                    ///|
                ()---              --- ()/--             --- ()/---           ---- //
                  ----/((////////(/ ----                      ---- (/(/( )/()() ---
    

    即使用斜杠、减号等符号围出图案的边缘构造图片,和Linux下的figlet、toilet等程序效果类似,但是这两个程序只能构造艺术字,这个程序能够处理任意图片,因此在此分享一下。

    思路

    首先,要用符号围出图案的边缘,需要能够知道图案的边缘在哪。这个可以用OpenCV库里的Canny边缘检测函数检测出来。之后就是研究如何用字符围出来,我采用了像素比较的方法,将图片分成一定数量相同大小的子图片,对于每个子图片,我将其和用到的所有字符的图片计算一个“欧几里得距离”(即所有像素差值的平方和),选取一个差值最小的字符作为这个像素对应的字符。

    程序中还有两点细节,一是我做边缘检测前后各做了一次放缩,第一次是为了方便边缘检测,第二次是适应不同的字符拟合需求,有的时候需要尽量少的字符,则需要在拟合前把图片缩小,有时希望拟合的效果更好,这时需要在拟合前把图片放大;二是为了防止子图片中边缘的像素点偏离图片中心,导致拟合出现错误,所以我在比较前把字符图片和边缘像素点都对齐到图片中心。

    代码

    import cv2
    import numpy as np
    from matplotlib import pyplot
    from PIL import Image, ImageDraw, ImageFont
    
    first_scale = 1 # 第一次放缩倍数
    detection_strength = 80 # 边缘检测强度
    second_scale = 1 # 第二次放缩倍数
    block_width = 10 # 子图片宽
    block_height = 25 # 子图片长,可以通过调整这两个参数适应不同显示环境字符的高矮胖瘦
    
    img = cv2.imread('Kirby.jpg')
    img = cv2.resize(img, (0, 0), fx=first_scale, fy=first_scale) # 第一次放缩
    img = cv2.Canny(img, detection_strength, detection_strength * 3) # 边缘检测
    kernel = cv2.getStructuringElement(cv2.MORPH_RECT,(3, 3))
    img = cv2.dilate(img, kernel) # 膨胀边缘检测后的图片,让边缘清晰完整
    img = cv2.resize(img, (0, 0), fx=second_scale, fy=second_scale) # 第二次放缩
    chars = ['/', '\', '(', ')', '|', '-', ' '] # 用到的字符
    chart = {}
    font = ImageFont.truetype('C:\Windows\Fonts\simhei.ttf', 20) # 这里指定字符图片的字体为黑体,可换成别的
    for i in chars:
        if i != ' ':
            chart[i] = np.zeros((20, 10), np.uint8)
            t = Image.fromarray(chart[i])
            draw = ImageDraw.Draw(t)
            draw.text((0, 0), i, font=font, fill=255) # 这里用PIL制作字符图片,其绘制字符的函数比OpenCV好控制
            chart[i] = np.array(t)
            chart[i] = cv2.resize(chart[i], (block_width, block_height)) # 把字符图片缩放为子图片的大小
            t = np.where(chart[i] > 0)
            y1, y2 = np.min(t[0]), np.max(t[0])
            x1, x2 = np.min(t[1]), np.max(t[1])
            w = x2 - x1 + 1
            ws = (block_width - w) // 2
            h = y2 - y1 + 1
            hs = (block_height - h) // 2
            t = np.zeros((block_height, block_width), np.int32)
            t[hs:hs + h, ws:ws + w] = chart[i][y1:y2 + 1, x1:x2 + 1] # 把字符实际像素对齐到图片中心
            chart[i] = t
        else:
            chart[i] = np.zeros((block_height, block_width), np.int32)
    s = ''
    for i in range(0, img.shape[0] - block_height, block_height):
        # 如果显示图片的地方默认有行距,比如单倍行距,则可以把上面这句话改成for i in range(0, img.shape[0] - block_height, block_height * 2):
        for j in range(0, img.shape[1] - block_width, block_width): # 枚举子图片
            mn = 0x3fffffff
            mx = ' '
            block = img[i:i + block_height, j:j + block_width]
            t = np.where(block > 0)
            if t[0].shape[0] != 0 or t[1].shape[0] != 0:
                y1, y2 = np.min(t[0]), np.max(t[0])
                x1, x2 = np.min(t[1]), np.max(t[1])
                w = x2 - x1 + 1
                ws = (block_width - w) // 2
                h = y2 - y1 + 1
                hs = (block_height - h) // 2
                t = np.zeros((block_height, block_width), np.int32)
                t[hs:hs + h, ws:ws + w] = block[y1:y2 + 1, x1:x2 + 1] # 将子图片边缘像素对齐到图片中心
                for k in chars:
                    a = np.sum((t - chart[k]) ** 2) # 计算欧几里得距离(省略开方)
                    if a < mn:
                        mn = a
                        mx = k
            s += mx
        s += '
    '
    print(s)
    pyplot.imshow(img, cmap='gray')
    pyplot.show()
    
  • 相关阅读:
    数据仓库--事实表
    Oracle SQL函数pivot、unpivot转置函数实现行转列、列转行
    在Oracle中使用rank()over()排名的问题
    python 有四个数字:1、2、3、4,能组成多少个互不相同且无重复数字的三位数?各是多少? 程序分析:可填在百位、十位、个位的数字都是1、2、3、4。组成所有的排列后再去 掉不满足条件的排列。(用列表推导式)
    pyhton 打印菱形
    三元运算
    Python代码书写规范
    DDT驱动
    使用 JsonPath 完成接口自动化测试中参数关联和数据验证(Python语言)
    数据类型(字典)
  • 原文地址:https://www.cnblogs.com/YuanZiming/p/13518376.html
Copyright © 2020-2023  润新知