惯性聚合 高效追踪和阅读你感兴趣的博客、新闻、科技资讯
阅读原文 在惯性聚合中打开

推荐订阅源

让小产品的独立变现更简单 - ezindie.com
让小产品的独立变现更简单 - ezindie.com
人人都是产品经理
人人都是产品经理
Cisco Talos Blog
Cisco Talos Blog
钛媒体:引领未来商业与生活新知
钛媒体:引领未来商业与生活新知
V
V2EX
博客园 - 三生石上(FineUI控件)
Martin Fowler
Martin Fowler
WordPress大学
WordPress大学
D
Docker
S
SegmentFault 最新的问题
博客园 - 聂微东
美团技术团队
Apple Machine Learning Research
Apple Machine Learning Research
月光博客
月光博客
奇客Solidot–传递最新科技情报
奇客Solidot–传递最新科技情报
Last Week in AI
Last Week in AI
M
MIT News - Artificial intelligence
F
Fortinet All Blogs
freeCodeCamp Programming Tutorials: Python, JavaScript, Git & More
The GitHub Blog
The GitHub Blog
GbyAI
GbyAI
L
LangChain Blog
Vercel News
Vercel News
博客园 - 叶小钗
MongoDB | Blog
MongoDB | Blog
Stack Overflow Blog
Stack Overflow Blog
H
Help Net Security
OSCHINA 社区最新新闻
OSCHINA 社区最新新闻
The Cloudflare Blog
Engineering at Meta
Engineering at Meta
T
Threat Research - Cisco Blogs
T
Threatpost
Scott Helme
Scott Helme
T
Tailwind CSS Blog
Latest news
Latest news
Stack Overflow Blog
Stack Overflow Blog
Blog — PlanetScale
Blog — PlanetScale
The Register - Security
The Register - Security
罗磊的独立博客
P
Proofpoint News Feed
腾讯CDC
S
Schneier on Security
雷峰网
雷峰网
A
About on SuperTechFans
T
Tenable Blog
F
Full Disclosure
Cyberwarzone
Cyberwarzone
博客园_首页
有赞技术团队
有赞技术团队
K
Kaspersky official blog

文章列表

游戏玩后感:ReLief:献给亲爱的你 我的周边(谷子)分享 游戏玩后感:Kanon 简谱:致真实的你 《Rust中常见的有关生命周期的误解》学习笔记 简谱:StarMap 简谱:かく咲きたらばいと恋ひめやも 简谱:东风 简谱:无法诉说的思念 简谱:Girlish 游戏玩后感:时钟机关的Layline 简谱:风之琶音 简谱:星空的记忆 简谱:因为遇见了你 简谱:月童 番茄简谱脚本转调器 游戏玩后感:青空下的约定:Refine 游戏玩后感:在这苍穹展翅 书籍读后感:控制论与科学方法论 游戏玩后感:恋爱表达式 游戏玩后感:樱之诗 MLIR-tutorial学习笔记 游戏玩后感:潜伏之赤途 游戏玩后感:纯爱咖啡厅:帕露菲重制版 游戏玩后感:智以泪聚 游戏玩后感:初雪樱 游戏玩后感:告别回忆:从今以后 游戏玩后感:梦灯花 游戏玩后感:金辉恋曲四重奏 游戏玩后感:五彩斑斓的世界 昇腾310P使用记录 游戏玩后感:AIR 游戏玩后感:弹丸论破 游戏玩后感:流景之海的艾佩莉亚 Xilinx_HLS上板过程记录 游戏玩后感:告别回忆2 游戏玩后感:恋爱绮谭 Faiss和Rapidsai_Raft使用记录 游戏玩后感:近月少女的礼仪 游戏玩后感:樱色之云,绯色之恋 游戏玩后感:幸运草的约定 游戏玩后感:星之梦、候鸟和丸子与银河龙 游戏玩后感:白色相簿2 Windows上使用VTune分析PyTorchExtension调用的Cpp程序 SpinalHDL上板过程记录 游戏玩后感:仰望夜空的星辰 最简单的算卦方法之一:梅花易数法 游戏玩后感:苍之彼方的四重奏 krkr引擎解包工具介绍 自定义CUDA实现PyTorch算子的四种简单方法 游戏玩后感:星空的记忆 游戏玩后感:9nine 游戏玩后感:AtriMyDearMoments 游戏玩后感:极限脱出 游戏玩后感:魔女的夜宴 SSH实现多跳代理 动漫观后感:向山进发 flv重封装H264、AAC流 动漫观后感:夏日重现 CSP模板 游戏玩后感:海沙风云 动漫观后感:灵能百分百 游戏玩后感:交响乐之雨 游戏玩后感:爱上火车LastRun 游戏玩后感:LittleBustersEX 游戏玩后感:SummerPockets 游戏玩后感:逆转裁判 Ultra96V2开发板简单使用 SpinalWorkshop实验笔记(三) SpinalWorkshop实验笔记(二) SpinalWorkshop实验笔记(一) PYNQ开发板上使用USB声卡+OSS兼容层播放音频 TestOS移植K210开发板 rCore-Tutorial-Book-v3学习笔记(七) 动漫观后感:凉宫春日的忧郁 rCore-Tutorial-Book-v3学习笔记(♭七) rCore-Tutorial-Book-v3学习笔记(六) rCore-Tutorial-Book-v3学习笔记(五) rCore-Tutorial-Book-v3学习笔记(四) rCore-Tutorial-Book-v3学习笔记(三) rCore-Tutorial-Book-v3学习笔记(二) rCore-Tutorial-Book-v3学习笔记(一) 游戏玩后感:RewritePlus MIT-6.S081-2020实验(xv6-riscv64)十一:net MIT-6.S081-2020实验(xv6-riscv64)十:mmap MIT-6.S081-2020实验(xv6-riscv64)九:fs MIT-6.S081-2020实验(xv6-riscv64)八:lock MIT-6.S081-2020实验(xv6-riscv64)七:thread MIT-6.S081-2020实验(xv6-riscv64)六:cow MIT-6.S081-2020实验(xv6-riscv64)五:lazy MIT-6.S081-2020实验(xv6-riscv64)四:traps MIT-6.S081-2020实验(xv6-riscv64)三:pgtbl MIT-6.S081-2020实验(xv6-riscv64)二:syscall 动漫观后感:吹响吧上低音号 MIT-6.S081-2020实验(xv6-riscv64)一:util 快速生成网络mp4视频缩略图技术 用plantuml画图示例 QQ缩略图和大图不同实现 动漫观后感:命运石之门 Unity3D+Post Processing Stack V2自定义后处理效果研究
Python制作字符图片
VnYzm · 2020-08-17 · via

背景

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

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

即使用斜杠、减号等符号围出图案的边缘构造图片,和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 += '\n'
print(s)
pyplot.imshow(img, cmap='gray')
pyplot.show()