python HZK16字库使用

news2024/12/23 18:53:35

注: 从个人博客园移植而来

环境: windows7, python2.7


简介

偶然在网上看到热心网友使用python讲微信头像进行了组字,感觉很有意思,就做下研究。

感谢,原文参考: Python玩微信头像组字


需求的相关工具:

  • python第三方库image
pip install image      # 图像处理
  • HZK16字库的下载
百度网盘: https://pan.baidu.com/s/1XTBXSeKY3jAH1N7KxAYhRg 
提取码:wjt0
  • 本应该要安装python的第三方库itchat
pip install itchat     # 开源的微信个人接口

# 腾讯在2019年7月份关闭了网页版登录接口
# itchat的相关接口已经不能够正常使用了,原有使用的主要接口有:

# 自动登录微信网页,会生成一个二维码图片,手机扫码即可登入
itchat.auto_login()
# 获取微信好友信息列表,从而获取头像信息
friendList = itchat.get_friends(update=True)

实现的原理:

通过itchat获取微信好友头像图片,将设定的文字按照HZK16字库转换为矩阵信息,然后在每个矩阵点上放置2X2张图片,最后通过Image生成出来。

虽然itchat不可使用了,但是我们可以使用本地的图片进行模拟效果.


HZK16简介

它是符合GB2312国家标准的16×16点阵字库,每个汉字需要**256(16×16)**个点组成。

其GB2312-80支持的汉字有6763个,符号682个;其中一级汉字有 3755个,按声序排列,二级汉字有3008个,按偏旁部首排列。

通常情况下中文汉字,在UTF-8格式下占用字节为2个;在GBK,GB2312格式下占用字节3个。因此GB2312的HZK16下的中文汉字占用2个字节。其编码范围:0xA1A1~0xFEFEA1-A9为符号区,B0-F7为汉字区。

前面说到GB2312格式下汉字占2个字节,前一个字节为该汉字的区号,每个区中记录94个汉字;后一个字节为该字的位号。用于记录汉字在该区中的位置。

因此要找到一个汉字在HZK16字库中的位置就必须得到它的区码和位码。

  • 区码:汉字的第一个字节 - 0xA0,因为汉字编码是从0xA0区开始的,所以文件最前面就是从0xA0区开始,要算出相对区码
  • 位码:汉字的第二个字节 - 0xA0

通过区码和位码我们就可以得到汉字在HZK16中的绝对偏移位置:

'''
* 区码或者位码减1,是由于数组从0开始,而区号位号是以1开始
* (94*(区号-1)+位号-1)是一个汉字字模占用的字节数
* 乘以32是因为一个汉字由32个字节存储(16*16/8)
'''
offset = (94*(区码-1)+(位码-1))*32

案例:

下载HZK16文件后,放置到指定的目录中,其目录结构为:

img

res中放置着一张75X75的png图片,可放置多个。代码为:

# -*- coding:UTF-8 -*-
#!/usr/bin/env python

import os
import math
import binascii
from PIL import Image

# 用于解决错误:UnicodeEncodeError: 'ascii' codec encode characters in position...
# 原因在于调用ascii编码处理字符流时,若字符流不属于ascii范围内就会报错
import sys
reload(sys)
sys.setdefaultencoding('utf-8')

# 每张头像裁剪后尺寸,建议图片不要太大,最好宽高一致
HEAD_CLIPSIZE = 75

# 每行列头像数目,即每点:2*2,可修改为3,即3*3
HEAD_NUM = 2
RECT_WIDTH = 16                                         # 矩阵点宽度 16             
RECT_HEIGHT = 16                                        # 矩阵点高度 16
BYTE_COUNT_PER_FONT = 2*RECT_HEIGHT                     # 占用字节 32

# 将文字转换为点阵
def char2bit(textStr):
    KEYS = [0x80, 0x40, 0x20, 0x10, 0x08, 0x04, 0x02, 0x01]
    target = []
    global count
    count = 0
    # 遍历文字
    for x in range(len(textStr)):
        text = textStr[x]
        # 初始化16*16点阵位置
        rect_list = [] * RECT_WIDTH
        for i in range(RECT_HEIGHT):
            rect_list.append([] * RECT_WIDTH)

        # 获取GB2312编码字符
        gb2312 = text.encode('gb2312')
        hex_str = binascii.b2a_hex(gb2312)
        result = str(hex_str)
        # 获取汉字第一个字节,区码
        area = eval('0x' + result[:2]) - 0xA0       
        # 获取汉字第二个字节,位码    
        index = eval('0x' + result[2:]) - 0xA0
        # 获取汉字在字库中的绝对偏移值
        offset = (94 * (area-1) + (index-1)) * BYTE_COUNT_PER_FONT

        font_rect = None
        # 读取HZK16字库文件
        with open("HZK16", "rb") as f:
            # 获取目标汉字偏移位置
            f.seek(offset)
            # 从数据中读取32字节数据
            font_rect = f.read(BYTE_COUNT_PER_FONT)

        for k in range(len(font_rect)/2):
            row_list = rect_list[k]
            for j in range(2):
                for i in range(8):
                    asc = binascii.b2a_hex(font_rect[k * 2 + j])
                    asc = asc = eval('0x' + asc)
                    flag = asc & KEYS[i]
                    row_list.append(flag)

        output = []
        _str = ''
        for row in rect_list:
            for i in row:
                if i:
                    output.append('1')
                    _str += '0'
                    count+=1
                else:
                    output.append('0')
                    _str += '.'
            print(_str)
            _str = ''

        target.append(''.join(output))
    return target

def head2char(index, outlist):
    # 获取资源列表
    imgList = []
    workspace = os.getcwd()
    respath = os.path.join(workspace, 'res')
    for root, dirs, files in os.walk(respath):
        for filename in files:
            imgList.append(os.path.join(root, filename))

    # 图片数目
    imgCount = len(imgList)

    #变量n用于循环遍历头像图片,即当所需图片大于头像总数时,循环使用头像图片
    n = 0

    for item in outlist:
        # 创建新图片
        canvasWidth = RECT_WIDTH * HEAD_NUM * HEAD_CLIPSIZE
        canvasHeight = RECT_HEIGHT * HEAD_NUM * HEAD_CLIPSIZE
        canvas = Image.new('RGB', (canvasWidth, canvasHeight), '#E0EEE0')
        # 遍历 RECT_WIDTH * RECT_HEIGHT 矩阵
        for i in range(RECT_WIDTH * RECT_HEIGHT):
            #点阵信息为1,即代表此处要显示头像来组字
            if item[i] != '1':
                continue 

            # 每个点使用放置几个矩阵,比如2*2,3*3
            for count in range(pow(HEAD_NUM, 2)):
                # 获取图片索引
                imgIndex = (n + count) % imgCount
                # 读取图片          
                headImg = Image.open(imgList[imgIndex])  
                # 重置图片大小       
                headImg = headImg.resize((HEAD_CLIPSIZE, HEAD_CLIPSIZE), Image.ANTIALIAS)
                # 拼接图片
                posx = ((i % RECT_WIDTH) * HEAD_NUM + (count%HEAD_NUM)) * HEAD_CLIPSIZE
                posy = ((i // RECT_HEIGHT) * HEAD_NUM + (count//HEAD_NUM)) * HEAD_CLIPSIZE
                canvas.paste(headImg, (posx, posy))

            #调整n以读取后续图片
            n = (n+4) % imgCount

        # 保存图片 quality代表图片质量,1-100
        canvas.save('result_{0}.jpg'.format(index), quality=100)

# 将gbk转换为unicode格式
def transGbk2Unicode(str_v):
    str_s = str_v.replace(r'%', r'\x')
    res = eval(repr(str_s).replace('\\\\', '\\'))
    return res.decode('gb2312')
        
if __name__=="__main__":
    inputStr = u'请输入您想要生成的文字(ENTER结束):'
    # 输入内容,将中文从unicode转换为gbk,防止乱码
    content = raw_input(inputStr.encode('gbk'))
    # 将gbk转换为unicode,以方便遍历时能够遍历每个文字或字母
    content = transGbk2Unicode(content)

    print(u'注意:指定文字每个仅能生成一个')
    # 循环遍历
    index = 0
    for _str in content:
        print(u'生成汉字:' + _str)

        #将字转化为汉字库的点阵数据
        outlist = char2bit(_str)

        #将头像图片按点阵拼接成单字图片                           
        head2char(index, outlist)
        index += 1

    print(u'生成成功!!!')

执行结果:

imgimg

参考:

HZK16的原理

python实现点阵字体读取与转换

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/359020.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

spring cloud gateway 实现redis动态路由及自动项目路由上报

前言 spring cloud gateway默认为内存存储策略,通过配置文件加载的方式生成路由定义信息 可以看到,RouteDefinitionRepository继承了两个父接口,分别为RouteDefinitionLocator和RouteDefinitionWriter,RouteDefinitionLocator定…

Java并发知识点

文章目录1. start()和run()方法的区别?2. volatile关键字的作用?使用volatile能够保证:防止指令重排3. sleep方法和wait方法有什么区别?sleep()方法4. 如何停止一个正在运行的线程?方法一:方法二&#xff1…

MindFusion Diagramming for Java, 最新版 Crack

Diagramming for Java, V4.6.1 A unique Java Swing library for any type of flowchart.您需要的每一个图表功能 图表、方案、图形、网络、算法、树、图表 - 所有这些都是使用 MindFusion Diagramming for Java 工具快速轻松地构建的。结果令人着迷。 Java Dagram 库&#xff…

论文阅读 | Real-Time Intermediate Flow Estimation for Video Frame Interpolation

前言:ECCV2022 快速插帧方法 Real-Time Intermediate Flow Estimation for Video Frame Interpolation 引言 进行视频插帧目前比较常见的方法是基于光流法,分为两个步骤:1.通过光流对齐输入帧,融合对齐的帧 光流并不能直接同于…

CS224W课程学习笔记(三):DeepWalk算法原理与说明

引言 什么是图嵌入? 图嵌入(Graph Embedding,也叫Network Embedding) 是一种将图数据(通常为高维稠密的矩阵)映射为低微稠密向量的过程,能够很好地解决图数据难以高效输入机器学习算法的问题。…

arxiv2017 | 用于分子神经网络建模的数据增强 SMILES Enumeration

论文标题:SMILES Enumeration as Data Augmentation for Neural Network Modeling of Molecules论文地址:https://arxiv.org/abs/1703.07076代码地址:https://github.com/Ebjerrum/SMILES-enumeration一、摘要摘要中明显提出:先指…

TCP/IP网络编程——多播与广播

完整版文章请参考: TCP/IP网络编程完整版文章 文章目录第 14 章 多播与广播14.1 多播14.1.1 多播的数据传输方式以及流量方面的优点14.1.2 路由(Routing)和 TTL(Time to Live,生存时间),以及加入组的办法14…

STM32开发(11)----CubeMX配置独立看门狗(IWDG)

CubeMX配置独立看门狗(IWDG)前言一、独立看门狗的介绍二、实验过程1.STM32CubeMX配置独立看门狗2.代码实现3.硬件连接4.实验结果总结前言 本章介绍使用STM32CubeMX对独立看门狗定时器进行配置的方法。门狗本质上是一个定时器,提供了更高的安…

华为云计算之容灾技术

容灾是物理上的容错技术,不是逻辑上的容错同步远程复制:主备距离≤200km,只有在主备设备上都写成功,才会告诉主机写成功,不会丢失数据异步远程复制:主备距离>200km,只要主设备上写成…

掌握MySQL分库分表(二)Mysql数据库垂直分库分表、水平分库分表

文章目录垂直分表拆分方法举例垂直分库水平分表水平分库小结垂直角度(表结构不一样)水平角度(表结构一样)垂直分表 需求:商品表字段太多,每个字段访问频次不⼀样,浪费了IO资源,需要…

标题标题标题

图床(Typora uPic/PicGo 七牛云) 图床(Typora uPic/PicGo 七牛云) 笔者平时使用 Typora 编写 markdown 文档,文档中常常会放置图片,如果文档不需要分享的话,其实讲图片存放在本地就可以了…

SpringCloud alibaba-Sentinel服务降级策略

文章目录RT:异常比例:异常数:RT: 平均响应时间 (DEGRADE_GRADE_RT):当 1s 内持续进入 N 个请求,对应时刻的平均响应时间(秒级)均超过阈值(count,以 ms 为单位…

一文吃透 Spring 中的IOC和DI(二)

✅作者简介:2022年博客新星 第八。热爱国学的Java后端开发者,修心和技术同步精进。 🍎个人主页:Java Fans的博客 🍊个人信条:不迁怒,不贰过。小知识,大智慧。 💞当前专栏…

【前端八股文】浏览器系列:单进程与多进程、浏览器进程、异步、事件循环、同源策略、输入URL回车后、TCP三次挥手四次握手

文章目录概述单进程VS多进程浏览器进程主要分为异步场景事件循环同源策略与跨域问题输入URL回车后过程URL几大部分TCP连接与释放TCP三次握手TCP四次挥手参考本系列目录:【前端八股文】目录总结 是以《代码随想录》八股文为主的笔记。详情参考在文末。 代码随想录的博…

AI又进化了,突破性革命来了

大家好,我是 Jack。 2023 年,AI 真的杀疯了。短短不到一年的时间,当我们还在感慨 AI 一键生成的二次元画作精美万分的时候,它已经进化到了写实美照也能手到擒来的地步。 更多的效果,可以看刚刚发布的视频,…

Java爬虫系列 - 爬虫补充内容+ElasticSearch展示数据

一,定时任务Cron表达式Component public class TaskTest {Scheduled(cron "0/5 * * * * *") // 从0秒开始,每个五秒 执行一次 { 秒 分 时 天 月 周 }public void test(){System.out.println("定时任务执行了");} }二,网…

第一章 初识 Spring Security

第一章 初识 Spring Security 1、权限管理 权限管理 基本上涉及到用户参与的系统都要进行权限管理,权限管理属于系统安全的范畴,权限管理实现了对用户访问系统的控制,按照安全规则或者安全策略控制用户可以访问而且只能访问自己被授权的资…

【白话科普】聊聊网络架构变革的关键——SDN

最近二狗子在网上冲浪的时候,不小心将 CDN 搜索成了 SDN,结果跳出来了一大堆相关的知识点。 好学的二狗子当然不会随随便便糊弄过去,于是认认真真学习了好久,终于了解了 SDN 是什么。 原来,SDN 的全称是 Software De…

第十一届“泰迪杯”数据挖掘挑战赛携“十万”大奖火热来袭

第十一届“泰迪杯”数据挖掘挑战赛 竞赛组织 主办单位: 泰迪杯数据挖掘挑战赛组织委员会 承办单位: 广东泰迪智能科技股份有限公司 人民邮电出版社 协办单位: 重庆市工业与应用数学学会、广东省工业与应用数学学会、广西数学学会、河北省工业…

心跳机制Redis

 进入命令传播阶段候,master与slave间需要进行信息交换,使用心跳机制进行维护,实现双方连接保持在线 master心跳: 指令:PING 周期:由repl-ping-slave-period决定,默认10秒 作用&#…