搜索

查看: 3105|回复: 11

[Python] python HZK16字库使用详解

[复制链接]
发表于 2023-5-4 17:23:13 | 显示全部楼层 |阅读模式
Editor 2023-5-4 17:23:13 3105 11 看全部
目录
  • 简介
  • 实现的原理:
  • HZK16简介
  • 案例:环境: windows7, python2.7

    简介
    偶然在网上看到热心网友使用python讲微信头像进行了组字,感觉很有意思,就做下研究。
    感谢,原文参考自: Python玩微信头像组字
    需求的相关工具:
    python第三方库image
    pip install image      # 图像处理
    HZK16字库的下载
    自行百度下载吧,由于网盘链接失效暂不给大家分享了。
    本应该要安装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~0xFEFE,A1-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文件后,放置到指定的目录中,其目录结构为:

    2023022209432616.png

    2023022209432616.png


    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
                        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 != '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'生成成功!!!')
    执行结果:

    2023022209432617.png

    2023022209432617.png


    2023022209432718.png

    2023022209432718.png


    参考资料:
    HZK16的原理
    python实现点阵字体读取与转换
    到此这篇关于python HZK16字库使用的文章就介绍到这了,更多相关python HZK16字库内容请搜索知鸟论坛以前的文章或继续浏览下面的相关文章希望大家以后多多支持知鸟论坛
  • 回复

    使用道具 举报

    发表于 2023-6-28 20:44:48 | 显示全部楼层
    老橡树1 2023-6-28 20:44:48 看全部
    论坛不能没有像楼主这样的人才啊!我会一直支持知鸟论坛
    回复

    使用道具 举报

    发表于 2023-6-28 23:09:07 | 显示全部楼层
    123456868 2023-6-28 23:09:07 看全部
    我看不错噢 谢谢楼主!知鸟论坛越来越好!
    回复

    使用道具 举报

    发表于 2023-6-29 10:51:43 | 显示全部楼层
    井底燕雀傥 2023-6-29 10:51:43 看全部
    这个帖子不回对不起自己!我想我是一天也不能离开知鸟论坛
    回复

    使用道具 举报

    发表于 2023-6-29 12:25:29 | 显示全部楼层
    xinting_6ym 2023-6-29 12:25:29 看全部
    楼主,我太崇拜你了!我想我是一天也不能离开知鸟论坛
    回复

    使用道具 举报

    发表于 2023-6-29 14:46:08 | 显示全部楼层
    123456848 2023-6-29 14:46:08 看全部
    楼主,大恩不言谢了!知鸟论坛是最棒的!
    回复

    使用道具 举报

    发表于 2023-6-29 18:05:02 | 显示全部楼层
    尘埃416 2023-6-29 18:05:02 看全部
    感谢楼主的无私分享!要想知鸟论坛好 就靠你我他
    回复

    使用道具 举报

    发表于 2023-6-29 23:24:47 | 显示全部楼层
    Gordon520 2023-6-29 23:24:47 看全部
    我看不错噢 谢谢楼主!知鸟论坛越来越好!
    回复

    使用道具 举报

    发表于 2023-6-30 00:33:07 | 显示全部楼层
    素色流年783 2023-6-30 00:33:07 看全部
    论坛不能没有像楼主这样的人才啊!我会一直支持知鸟论坛
    回复

    使用道具 举报

    发表于 2023-6-30 08:30:28 | 显示全部楼层
    术数古籍专卖疤 2023-6-30 08:30:28 看全部
    这东西我收了!谢谢楼主!知鸟论坛真好!
    回复

    使用道具 举报

    • 您可能感兴趣
    点击右侧快捷回复 【请勿灌水】
    您需要登录后才可以回帖 登录 | 立即注册

    本版积分规则 返回列表

    RSS订阅| SiteMap| 小黑屋| 知鸟论坛
    联系邮箱E-mail:zniao@foxmail.com
    快速回复 返回顶部 返回列表