吾爱破解 - 52pojie.cn

 找回密码
 注册[Register]

QQ登录

只需一步,快速开始

查看: 2260|回复: 5
收起左侧

[Python 原创] 【python】图片转字符画 cv2+pygame实现

[复制链接]
jjjzw 发表于 2021-7-5 15:37
本帖最后由 jjjzw 于 2021-7-5 16:05 编辑

网上看到一些字符画,非常羡慕,想要用python写一个类似的东西,突然想到字符画不就是把图片分割为像素块再进行替换嘛
恰好之前稍稍入门了python的opencv库,可以对图片进行处理。【传送门:opencv处理bad Apple视频】

处理图片的思想为:对一个区域的像素进行参考值计算,用具有相似参考值的字符进行替代,因此除了图片处理过程,还需要自定义字符取模系统。
这样就把一个完整的流程画出来了:
  • 流程
    • 字符取模
    • 切割图片
    • 替换字符
    • 储存和显示

字符取模


既然是图像处理,完全可以用pygame的界面和截屏功能来创造每一个字符的模块,这里设置区块大小为block=15(可以更改)
将字符显示在窗口大小同样为block的窗口上:

字符"0"

字符"0"

字符“0”的block
得到一个边长block的矩阵。接下来是自定义参考值,考虑到对于一个矩阵,人眼观测到边缘的聚集程度比中间的聚集程度低,而最中间因为像素数量过少,聚集程度也不高,因此设计一种从四周到中间的权重值:
对于block=15,有:
weight = [0.001, 0.002, 0.003, 0.004, 0.005, 0.006, 0.005, 0.004]

矩阵权重

矩阵权重

block矩阵权重图
[Python] 纯文本查看 复制代码
pic = cv2.imread(content, 0)
    for i in range(0, len(weight)):
        sum_ = 0
        for j in range(0, block - 2 * i):
            sum_ = pic[i, j] + sum_
            sum_ = pic[block - i - 1, j] + sum_
            sum_ = pic[j, i] + sum_
            sum_ = pic[j, block - i - 1] + sum_
        sum_ = sum_ - pic[i, i] - pic[i, block - i - 1] - pic[block - i - 1, i] - pic[block - i - 1, block - i - 1]
        sum_ *= weight[i]
        sum_all.append(sum_)
    weight_all = sum(sum_all)
    sum_all.clear()
    weight_list.append(weight_all)

将灰度值与权重相乘并累加,得到一个参考值,但这个参考值并不能用来参考,因为图片分割出来的像素块的参考值与字符的值差距太大,起不到参考作用,因此需要进行归一化,将所有参考值归一化到0-100%内。
[Python] 纯文本查看 复制代码
def gui_yi_hua():
    try:
        os.remove("character.txt")
    except IOError:
        pass
    min_ = min(weight_list)
    for num in weight_list:
        num -= min_
        weight_list_changed.append(num)
    max_ = max(weight_list_changed)
    for weight_ in weight_list_changed:
        with open("character.txt", "a+") as f1:
            f1.write(str(weight_/max_) + "\n")
            sum_all.clear()
            f1.close()

这样全部的取模部分就做好了!
整理一下:
[Python] 纯文本查看 复制代码
import pygame
import cv2
import os

global removed

block = 15

character_list = []
weight = [0.001, 0.002, 0.003, 0.004, 0.005, 0.006, 0.005, 0.004]
sum_all = []
weight_list = []
weight_list_changed = []
pygame.init()
pygame.font.init()
font = pygame.font.Font("Keyboard.ttf", block)
screen = pygame.display.set_mode((block, block))
pygame.display.set_caption("字符取模")
back_color = (255, 255, 255)


def draw(path, x, y):
    screen.blit(path, (x, y))
    pygame.display.update()


def make_list():
    with open("list.txt", "r") as f:
        character = f.read()
        f.close()
    for i in range(0, len(character)):
        character_list.append(character[i])


def write(content):
    text = font.render(content, True, (0, 0, 0), (255, 255, 255))
    draw(text, 0, -1)


def screenshot():
    rect = pygame.Rect(0, 0, block, block)
    shot = screen.subsurface(rect)
    pygame.image.save(shot, "screenshot.jpg")
    count("screenshot.jpg")


def count(content):
    pic = cv2.imread(content, 0)
    for i in range(0, len(weight)):
        sum_ = 0
        for j in range(0, block - 2 * i):
            sum_ = pic[i, j] + sum_
            sum_ = pic[block - i - 1, j] + sum_
            sum_ = pic[j, i] + sum_
            sum_ = pic[j, block - i - 1] + sum_
        sum_ = sum_ - pic[i, i] - pic[i, block - i - 1] - pic[block - i - 1, i] - pic[block - i - 1, block - i - 1]
        sum_ *= weight[i]
        sum_all.append(sum_)
    weight_all = sum(sum_all)
    sum_all.clear()
    weight_list.append(weight_all)


def gui_yi_hua():
    try:
        os.remove("character.txt")
    except IOError:
        pass
    min_ = min(weight_list)
    for num in weight_list:
        num -= min_
        weight_list_changed.append(num)
    max_ = max(weight_list_changed)
    for weight_ in weight_list_changed:
        with open("character.txt", "a+") as f1:
            f1.write(str(weight_/max_) + "\n")
            sum_all.clear()
            f1.close()


while True:
    make_list()
    for h in range(0, len(character_list)):
        screen.fill(back_color)
        write(character_list[h])
        screenshot()
    gui_yi_hua()
    os.remove("screenshot.jpg")
    exit()
    for event in pygame.event.get():
        if event.type == pygame.QUIT:
            exit()


图片处理主程序


首先,因为要划分像素块的问题,图片必须能完全划分,因此需要对宽高进行裁剪,使用cv2模块:
[Python] 纯文本查看 复制代码
def pic2thr(path_):
    global crop, size
    # 图片灰度化,并进行裁剪
    img = cv2.imread(path_, 0)
    size = img.shape
    if (size[1] % block) != 0:
        zuo = int(size[1] % block / 2)
        you = int(size[1] % block / 2) + size[1] % block % 2
    else:
        zuo = 0
        you = 0
    if (size[0] % block) != 0:
        shang = int(size[0] % block / 2)
        xia = int(size[0] % block / 2) + size[0] % block % 2
    else:
        shang = 0
        xia = 0
    crop = img[0 + shang:size[0] - xia, 0 + zuo:size[1] - you]
    cv2.imwrite("Cut.jpg", crop)
    crop = cv2.transpose(crop, crop)
    size = (size[1] - zuo - you, size[0] - shang - xia)
    return crop, size

由于cv2的坐标系和pygame的坐标系是转置关系,因此做了一些调整
接下来是替换部分,由于同样要进行归一化操作,要对图片进行两次处理,第一次得到参考值的列表并进行归一化,第二次再进行比较和替换。
其思路与字符取模部分相似:
[Python] 纯文本查看 复制代码
for ii in range(0, int(size[0] / block)):
            for jj in range(0, int(size[1] / block)):
                for i in range(0, len(weight)):
                    sum_ = 0
                    for j in range(0, block-2*i):
                        sum_ = crop[ii*block+i, jj*block+j] + sum_
                        sum_ = crop[(ii+1)*block-i-1, jj*block+j] + sum_
                        sum_ = crop[ii*block+j, jj*block+i] + sum_
                        sum_ = crop[ii*block+j, (jj+1)*block-i-1] + sum_
                    sum_ = sum_-crop[ii*block+i, jj*block+i]
                    sum_ = sum_-crop[ii*block+i, (jj+1)*block-i-1]
                    sum_ = sum_-crop[(ii+1)*block-i-1, jj*block+i]
                    sum_ = sum_-crop[(ii+1)*block-i-1, (jj+1)*block-i-1]
                    sum_ *= weight[i]
                    sum_all.append(sum_)
                weight_all = sum(sum_all)
                test.append(weight_all)
                sum_all.clear()

最后进行归一化值计算和替换、显示和储存,整理后:
[Python] 纯文本查看 复制代码
import cv2
import os
import pygame

global crop, size
path = "234.jpeg"
block = 10
character_list = []
character_list_weight = []
test = []


def pic2thr(path_):
    global crop, size
    # 图片灰度化,并进行裁剪
    img = cv2.imread(path_, 0)
    size = img.shape
    if (size[1] % block) != 0:
        zuo = int(size[1] % block / 2)
        you = int(size[1] % block / 2) + size[1] % block % 2
    else:
        zuo = 0
        you = 0
    if (size[0] % block) != 0:
        shang = int(size[0] % block / 2)
        xia = int(size[0] % block / 2) + size[0] % block % 2
    else:
        shang = 0
        xia = 0
    crop = img[0 + shang:size[0] - xia, 0 + zuo:size[1] - you]
    cv2.imwrite("Cut.jpg", crop)
    crop = cv2.transpose(crop, crop)
    size = (size[1] - zuo - you, size[0] - shang - xia)
    return crop, size


def make_list():
    with open("list.txt", "r") as f:
        characters = f.read()
        f.close()
    for k in range(0, len(characters)):
        character_list.append(characters[k])
    with open("character.txt", "r") as f:
        characters = f.readlines()
        f.close()
    for k in range(0, len(characters)):
        character_list_weight.append(float(characters[k].strip()))


make_list()
pic2thr(path)
weight = [0.001, 0.002, 0.003, 0.004, 0.005, 0.006, 0.005, 0.004]
charge = 1
change = 0
min_ = max_ = 0
sum_all = []
real = []
pygame.init()
pygame.font.init()
font = pygame.font.Font("Keyboard.ttf", block)
screen = pygame.display.set_mode(size)
pygame.display.set_caption("图片转字符")
back_color = (255, 255, 255)
raw = pygame.image.load("Cut.jpg")


def screenshot():
    rect = pygame.Rect(0, 0, size[0], size[1])
    shot = screen.subsurface(rect)
    pygame.image.save(shot, "trans.jpg")


def write(content, x, y):
    position = (x, y, block, block)
    text = font.render(content, True, (0, 0, 0), (255, 255, 255))
    pygame.draw.rect(screen, (255, 255, 255), position, 0)
    draw(text, x, y)


def draw(_path, x, y):
    screen.blit(_path, (x, y))
    pygame.display.update()


while True:
    if change != 2:
        draw(raw, 0, 0)
        if change == 1:
            os.remove("Cut.jpg")
        for ii in range(0, int(size[0] / block)):
            for jj in range(0, int(size[1] / block)):
                for i in range(0, len(weight)):
                    sum_ = 0
                    for j in range(0, block-2*i):
                        sum_ = crop[ii*block+i, jj*block+j] + sum_
                        sum_ = crop[(ii+1)*block-i-1, jj*block+j] + sum_
                        sum_ = crop[ii*block+j, jj*block+i] + sum_
                        sum_ = crop[ii*block+j, (jj+1)*block-i-1] + sum_
                    sum_ = sum_-crop[ii*block+i, jj*block+i]
                    sum_ = sum_-crop[ii*block+i, (jj+1)*block-i-1]
                    sum_ = sum_-crop[(ii+1)*block-i-1, jj*block+i]
                    sum_ = sum_-crop[(ii+1)*block-i-1, (jj+1)*block-i-1]
                    sum_ *= weight[i]
                    sum_all.append(sum_)
                weight_all = sum(sum_all)
                test.append(weight_all)
                sum_all.clear()
                if change == 1:
                    weight_all -= min_
                    weight_all /= max_
                    character_list_backup = []
                    for num in character_list_weight:
                        num -= weight_all
                        character_list_backup.append(num)
                    for cha in character_list_backup:
                        if cha <= 0:
                            cha *= -1
                            real.append(cha)
                        else:
                            real.append(cha)
                    min_num = min(real)
                    index_ = real.index(min_num)
                    character = character_list[index_]
                    write(character, ii*block, jj*block)
                    real.clear()
        change += 1
        min_ = min(test)
        max_ = max(test)
        screenshot()
    for event in pygame.event.get():
        if event.type == pygame.QUIT:
            exit()


效果图



raw

raw

translated

translated

example1

raw1

raw1


translated1

translated1

example2

可以看到图片中的阴影高光均能很好地区分,但由于字符太过杂乱,不能很好地显示轮廓,设置新的权重或减少字符集也许能解决这个问题


总结


临时起意做这个小玩具,做参考值计算的部分实在是看得头痛,忙了一整天做了个有一点点效果的demo,还算满意:lol ,虽然跟大佬做的和好看的网图比起来差距有点大,但还是有可以改进的地方的!

项目地址:https://github.com/Icingworld/Pic2Character

代码附上,有写得不好的地方希望大佬们指点、斧正!

免费评分

参与人数 2威望 +1 吾爱币 +20 热心值 +2 收起 理由
Lwy666666 + 1 用心讨论,共获提升!
苏紫方璇 + 1 + 20 + 1 感谢发布原创作品,吾爱破解论坛因你更精彩!

查看全部评分

发帖前要善用论坛搜索功能,那里可能会有你要找的答案或者已经有人发布过相同内容了,请勿重复发帖。

 楼主| jjjzw 发表于 2021-7-7 13:28
无敌小车 发表于 2021-7-7 11:54
如果只用点来画应该怎么弄呢?就像这样
&#10495;&#10495;&#10495;&#10495;&#10495;&#10271;&#10251;&#1024 ...

如果是用这些符号来画:
处理方法一是设计一套新的参考值计算方案,因为这些符号可以说是有方向的(四个角落),所以灰度值的权重也不能对称,改动最小的方法是将权重weight设置为block*block的矩阵,计算参考值时直接遍历。
处理方法二是添加一个方向参数a = [1,2,3,4]对应4个角落的符号,通过对符号进行计算、分类减少错误率。

如果只是用 · 来画:
参考 https://www.52pojie.cn/thread-1420181-1-1.html

免费评分

参与人数 1吾爱币 +2 热心值 +1 收起 理由
无敌小车 + 2 + 1 热心回复!

查看全部评分

lihu5841314 发表于 2021-7-5 15:43
lhl0235 发表于 2021-7-5 16:30
无敌小车 发表于 2021-7-7 11:54
如果只用点来画应该怎么弄呢?就像这样
&#10495;&#10495;&#10495;&#10495;&#10495;&#10271;&#10251;&#10244;&#10244;&#10244;&#10244;&#10244;&#10244;&#10244;&#10369;&#10248;&#10427;&#10431;&#10495;&#10495;&#10495;&#10495;&#10495;&#10495;&#10495;
&#10495;&#10495;&#10495;&#10495;&#10495;&#10243;&#10244;&#10244;&#10244;&#10244;&#10244;&#10244;&#10244;&#10244;&#10244;&#10244;&#10244;&#10248;&#10304;&#10285;&#10431;&#10495;&#10495;&#10495;&#10495;
&#10495;&#10495;&#10495;&#10495;&#10335;&#10244;&#10368;&#10494;&#10495;&#10495;&#10495;&#10487;&#10486;&#10495;&#10487;&#10486;&#10486;&#10310;&#10244;&#10244;&#10244;&#10495;&#10495;&#10495;&#10495;
&#10495;&#10495;&#10495;&#10495;&#10311;&#10368;&#10492;&#10495;&#10495;&#10495;&#10495;&#10495;&#10495;&#10495;&#10495;&#10495;&#10495;&#10471;&#10244;&#10244;&#10424;&#10495;&#10495;&#10495;&#10495;
&#10495;&#10495;&#10495;&#10495;&#10439;&#10492;&#10495;&#10495;&#10303;&#10294;&#10265;&#10495;&#10335;&#10273;&#10484;&#10495;&#10493;&#10495;&#10471;&#10244;&#10424;&#10495;&#10495;&#10495;&#10495;
&#10495;&#10495;&#10495;&#10495;&#10495;&#10494;&#10495;&#10495;&#10463;&#10477;&#10494;&#10495;&#10487;&#10486;&#10486;&#10484;&#10486;&#10495;&#10495;&#10372;&#10495;&#10495;&#10495;&#10495;&#10495;
&#10495;&#10495;&#10495;&#10495;&#10495;&#10495;&#10495;&#10495;&#10335;&#10473;&#10495;&#10495;&#10495;&#10319;&#10427;&#10495;&#10495;&#10495;&#10495;&#10495;&#10495;&#10495;&#10495;&#10495;&#10495;
&#10495;&#10495;&#10495;&#10495;&#10495;&#10495;&#10489;&#10315;&#10264;&#10295;&#10470;&#10432;&#10464;&#10358;&#10241;&#10248;&#10241;&#10244;&#10495;&#10495;&#10495;&#10495;&#10495;&#10495;&#10495;
&#10495;&#10495;&#10495;&#10495;&#10495;&#10495;&#10445;&#10243;&#10484;&#10486;&#10324;&#10258;&#10244;&#10464;&#10368;&#10244;&#10244;&#10244;&#10344;&#10495;&#10495;&#10495;&#10495;&#10495;&#10495;
&#10495;&#10495;&#10495;&#10495;&#10495;&#10495;&#10495;&#10470;&#10328;&#10303;&#10487;&#10495;&#10303;&#10271;&#10243;&#10244;&#10244;&#10464;&#10311;&#10248;&#10299;&#10495;&#10495;&#10495;&#10495;
&#10495;&#10495;&#10495;&#10495;&#10367;&#10271;&#10251;&#10369;&#10487;&#10464;&#10244;&#10244;&#10244;&#10244;&#10432;&#10464;&#10494;&#10335;&#10244;&#10244;&#10244;&#10244;&#10249;&#10265;&#10299;
&#10367;&#10271;&#10251;&#10241;&#10244;&#10244;&#10244;&#10424;&#10495;&#10495;&#10351;&#10387;&#10484;&#10494;&#10495;&#10495;&#10335;&#10244;&#10244;&#10244;&#10244;&#10244;&#10244;&#10244;&#10244;
&#10244;&#10244;&#10244;&#10244;&#10244;&#10244;&#10244;&#10495;&#10335;&#10487;&#10244;&#10297;&#10495;&#10495;&#10495;&#10367;&#10241;&#10244;&#10244;&#10244;&#10244;&#10244;&#10244;&#10244;&#10244;
虚情假意 发表于 2021-12-19 10:20
学习一下~
您需要登录后才可以回帖 登录 | 注册[Register]

本版积分规则

返回列表

RSS订阅|小黑屋|处罚记录|联系我们|吾爱破解 - LCG - LSG ( 京ICP备16042023号 | 京公网安备 11010502030087号 )

GMT+8, 2024-11-25 09:42

Powered by Discuz!

Copyright © 2001-2020, Tencent Cloud.

快速回复 返回顶部 返回列表