png图片自动转ttf字体(使用python实现)

2023-05-16

这里写目录标题

  • 一、任务概述
  • 二、实现
    • 2.1 ocr识别
      • 2.1.1 安装环境
      • 2.1.2 实现脚本
    • 2.2 图形文字精确提取
    • 2.3 png转svg
    • 2.4 svg转ttf

一、任务概述

在这里插入图片描述
任务要求:需要将上述生僻字png图片批量自动转成ttf字体文件,该字体对应的unicode码为图中下半部分对应的16进制值。

整个任务分成几个部分实现:

  • OCR识别:识别出图片下半部分对应的16进制数值;
  • 图形文字精确提取:提取出图片上半部分精确的文字图形区域;
  • png图片转svg: 通过图像处理算法提取上半部分图片的字形轮廓,将其转成svg文件;
  • svg转ttf:通过FontForge提供的Python工具实现批量转换;

二、实现

2.1 ocr识别

综合考虑识别精度和CPU推理速度要求,本文使用PaddleOCR实现。

2.1.1 安装环境

python -m pip install paddlepaddle==2.3.0 -i https://mirror.baidu.com/pypi/simple
pip install paddlehub -i https://pypi.tuna.tsinghua.edu.cn/simple 
pip install shapely -i https://pypi.tuna.tsinghua.edu.cn/simple

2.1.2 实现脚本

# 导入系统库
import cv2
import os

# 导入字体识别库
import paddlehub as hub

# 导入自定义识别库
from tools import sim_code 

def main(): 
    '''
    主函数
    '''
    # 定义参数
    img_list = list()
    img_dir = './screenshot/25A2C_2625F'  
    target_width, target_height = 363, 269

    # 获取文件列表
    for file in os.listdir(img_dir):
            if os.path.splitext(file)[1].lower() in '.png|.jpg':
                img_list.append(file)
    print('当前总图片数量: %d' % len(img_list))
    
    # 创建识别器
    ocr = hub.Module(name="chinese_ocr_db_crnn_server")
    
    # 循环处理图片
    index = 0
    error_index = 0
    for img_path in img_list:
        img = cv2.imread(os.path.join(img_dir,img_path),cv2.IMREAD_COLOR)
        # 图像标准化
        h, w, _=img.shape
        if h != target_height or w != target_width:
            img = cv2.resize(img, dsize=(target_width, target_height))
            
        # 提取ocr区域
        ocrimg = img[170:259,40:310,:]    
        h,w,_= ocrimg.shape
        ocrimg = cv2.copyMakeBorder(ocrimg,2*h,2*h,2*w,2*w, cv2.BORDER_CONSTANT,value=[255,255,255])    
        result = ocr.recognize_text([ocrimg])
        
        code = result[0]['data']
        if len(code)==0:
            error_index+=1
            cv2.imwrite('error/%d.png' % error_index, img)
            continue
        
        code = code[0]["text"].strip()
        code = sim_code(code)
        if len(code)!=5:
            error_index+=1
            cv2.imwrite('error/%d.png' % error_index, img)
            continue
        try:
            a = int(code,16)
        except Exception as e:
            error_index+=1
            cv2.imwrite('error/%d.png' % error_index, img)
            continue
        
        index += 1 
        print(img_path+'   识读结果:'+code)
        
        # 检查是否有同名文件
        save_path = 'ocr/%s.png' % code
        if os.path.exists(save_path):
            error_index+=1
            cv2.imwrite('error/%d_repeat.png' % error_index, img)
            continue
            
        textimg = img   
        cv2.imwrite(save_path, textimg)


if __name__ == "__main__":
    '''
    程序入口
    '''
    main()

其中sim_code函数定义如下:

def sim_code(code):
    code = code.strip()
    code = code.upper()
    # 剔除常见错误项
    code = code.replace("G", "")
    code = code.replace("H", "")
    code = code.replace("I", "1")
    code = code.replace("J", "")
    code = code.replace("K", "")
    code = code.replace("L", "")
    code = code.replace("M", "")
    code = code.replace("N", "")
    code = code.replace("O", "0")
    code = code.replace("P", "")
    code = code.replace("Q", "")
    code = code.replace("R", "")
    code = code.replace("S", "")
    code = code.replace("T", "")
    code = code.replace("U", "")
    code = code.replace("V", "")
    code = code.replace("W", "")
    code = code.replace("X", "")
    code = code.replace("Y", "")
    code = code.replace("Z", "")
    code = code.replace("0", "0")   
    return code

识读结果如下图所示:
在这里插入图片描述

2.2 图形文字精确提取

完整代码如下:

# 导入系统库
import cv2
import os
import numpy as np


def main(): 
    '''
    主函数
    '''
    # 定义参数
    img_list = list()
    img_dir = './ocr' 
    target_width, target_height = 363, 269

    # 获取文件列表
    for file in os.listdir(img_dir):
            if os.path.splitext(file)[1].lower() in '.png|.jpg':
                img_list.append(file)
    print('当前总图片数量: %d' % len(img_list))
    
    # 循环处理图片
    index = 0
    error_index = 0
    for img_path in img_list:
        img = cv2.imread(os.path.join(img_dir,img_path),cv2.IMREAD_COLOR)
        # 图像标准化
        h, w, _=img.shape
        if h != target_height or w != target_width:
            img = cv2.resize(img, dsize=(target_width, target_height))
        
        # 去掉上下左右边界线
        image_gray = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY)
        edges = cv2.Canny(image_gray, 170, 220, apertureSize=3)
        lines = cv2.HoughLines(edges, 1, np.pi / 180, 250)
        for line in lines:
            # 获取rho和theta
            rho, theta = line[0]
            a = np.cos(theta)
            b = np.sin(theta)
            x0 = a * rho
            y0 = b * rho
            x1 = int(x0 + 1000 * (-b))
            y1 = int(y0 + 1000 * (a))
            x2 = int(x0 - 1000 * (-b))
            y2 = int(y0 - 1000 * (a))
            cv2.line(img, (x1, y1), (x2, y2), (255, 255, 255), thickness=20)
        
        # 提取图形区域
        img = img[5:155,100:270,:]
        gray = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY)
        
        # 精确裁剪
        ret, thresh = cv2.threshold(gray,127,255,cv2.THRESH_BINARY_INV)
        coords = np.column_stack(np.where(thresh > 0))
        coords = np.array(coords, dtype=np.float32)
        #min_rect = cv2.minAreaRect(coords)
        
        rect = cv2.boundingRect(coords)
        [y, x, h, w] = rect
        img = img[y:y+h,x:x+w,:]
        
        # 调整为正方形
        h,w,_ = img.shape
        if h>w:
            pad = int((h-w)/2.0)
            img = cv2.copyMakeBorder(img,0,0,pad,pad, cv2.BORDER_CONSTANT,value=[255,255,255])
        elif w>h:
            pad = int((w-h)/2.0)
            img = cv2.copyMakeBorder(img,pad,pad,0,0, cv2.BORDER_CONSTANT,value=[255,255,255])
                    
        # 统一缩放
        img = cv2.resize(img, dsize=(128, 128))

        # 边缘补白
        img = cv2.copyMakeBorder(img,10,10,10,10, cv2.BORDER_CONSTANT,value=[255,255,255])   
        
        # 保存
        code = os.path.splitext(img_path)[0]
        save_path = 'crop/%s.png' % code  
        cv2.imwrite(save_path, img)
        index += 1
        print(img_path)   
        


if __name__ == "__main__":
    '''
    程序入口
    '''
    main()

效果如下图所示:
在这里插入图片描述

2.3 png转svg

这里主要通过opencv的形态学操作提取图像轮廓实现转换。

# 导入系统库
import cv2
import os

def main():
    '''主函数'''
    # 定义参数
    img_list = list()
    img_dir = './crop' 

    # 获取文件列表
    for file in os.listdir(img_dir):
            if os.path.splitext(file)[1].lower() in '.png|.jpg':
                img_list.append(file)
    print('当前总图片数量: %d' % len(img_list))
    
    # 循环处理图片
    index = 0
    for img_path in img_list:
        textimg = cv2.imread(os.path.join(img_dir,img_path),cv2.IMREAD_COLOR)
        
        # 提取图形区域
        textimg = cv2.resize(textimg, dsize=(640, 640))
        blur = cv2.GaussianBlur(textimg, (3, 3), 0)
        gray = cv2.cvtColor(blur, cv2.COLOR_BGR2GRAY)
        ret, thresh = cv2.threshold(gray, 10, 255, cv2.THRESH_BINARY_INV + cv2.THRESH_OTSU)               
        contours,hierarchy = cv2.findContours(thresh, mode=cv2.RETR_TREE, method=cv2.CHAIN_APPROX_SIMPLE)
        
        epsilon = 10
        h, w, _ = textimg.shape
        code = os.path.splitext(img_path)[0]
        svg_path = 'svg/'+code+'.svg'
        with open(svg_path, "w+") as f:
            f.write(f'<svg version="1.0" xmlns="http://www.w3.org/2000/svg" width="{w}.000000pt" height="{h}.000000pt" viewBox="0 0 680.000000 680.000000" preserveAspectRatio="xMidYMid meet">')      
            f.write(f'<g transform="scale(1.00000,1.00000)">')
            for c in contours:
                f.write('<path d="M')
                approx = cv2.approxPolyDP(c,epsilon,False)
                for i in range(len(approx)):
                    x, y = approx[i][0]
                    if i == len(approx)-1:
                        f.write(f"{x} {y}")
                    else:
                        f.write(f"{x} {y} ")
                f.write('"/>')                
            f.write(f'</g>')
            f.write("</svg>")
        index +=1
        print('当前处理完 %d 张图片' % index)
    print('全部处理结束')

if __name__ == "__main__":
    '''程序入口'''
    main()

部分样例转换结果如下图所示:
在这里插入图片描述

2.4 svg转ttf

ttf是专门的字体库文件,目前能够支持ttf编辑的软件并不多。这里推荐使用FontForge,该软件提供了python处理接口,可以使用python脚本批量转换svg到ttf。

首先从官网下载windows版的FontForge并安装,本文将其安装到D盘的toolplace的文件夹中。要使用FontForge提供的python接口,我们必须要使用FontForge的python执行器,这个执行器位于FontForge安装目录的bin文件夹中,完整路径如下图所示:

D:\toolplace\FontForgeBuilds\bin

在这个目录下面有个名为ffpython.exe的可执行文件,这个就是FontForge提供的python执行器。为了能够正常使用这个执行器,我们需要将我们的可执行目录切换到bin文件夹下面,然后创建一个转换脚本main_ttf.py,内容如下:

# 导入系统库
from time import sleep
import fontforge, os,psMat


def main():
    '''
    主函数
    '''
    # 定义参数
    img_list = list()
    img_dir = './svg' 

    # 获取文件列表
    for file in os.listdir(img_dir):
            if os.path.splitext(file)[1].lower() in '.svg':
                img_list.append(file)
    print('当前总图片数量: %d' % len(img_list))
    
    # 循环处理图片
    index = 0
    
    for img_path in img_list:  
        print('当前处理 '+img_path)
        
        # 获取unicode
        codestr = os.path.splitext(img_path)[0]
        code = int(codestr,16)
        
         # 创建字体
        font = fontforge.font()
        font.encoding = 'UnicodeFull'
        font.version = '1.0'
        font.weight = 'Regular'
        font.fontname = 'uni'+codestr
        font.familyname = 'uni'+codestr
        font.fullname = 'uni'+codestr       
        
        # 创建字符
        glyph = font.createChar(code, "uni"+codestr)
        glyph.importOutlines(os.path.join(img_dir,img_path))
        
        # 位移调整
        base_matrix = psMat.translate(0,0)
        glyph.transform(base_matrix)
        
        # 写入ttf
        font.generate('./ttf/'+codestr+'.ttf')      
        index +=1
        # if index>1:
        #     break
        print('当前处理完 %d 张图片' % index)
        
        # 删除文件
        os.remove(os.path.join(img_dir,img_path))
        
    print('全部处理结束')


if __name__ == "__main__":
    '''
    程序入口
    '''
    main()

然后使用下面的命令执行该脚本:

./ffpython.exe main_ttf.py

最后在当前目录下会生成一个个的ttf文件。

我们可以使用FontForge的客户端查看我们这个生成的字体文件,打开后在菜单栏上选择Encoding-Compact,如下图所示:
在这里插入图片描述
可以看到我们已经成功的将png图片批量转换成了ttf文件。

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

png图片自动转ttf字体(使用python实现) 的相关文章

  • 什么是栈,栈存储结构详情

    什么是栈 xff0c 栈存储结构详情 同顺序表和链表一样 xff0c 栈也是用来存储逻辑关系为一对一数据的线性存储结构 xff0c 如图所示 从图1我们看到 xff0c 栈存储结构与之前学的线性存储有所差异 xff0c 这源于栈对数存和取的
  • C++cmath数学常用库中的代码介绍

    今天小编来分享一波C 43 43 cmath数学常用库中的常用代码 首先 xff0c 这些所有的代码都来自cmath库 xff0c 所以必须先引用cmath头文件 xff0c 即 xff1a include lt cmath gt 1 绝对
  • vue-cli-service Axios 持续返还401

    后端 xff1a 在登录超时或没有登录的情况 xff0c 所有请求都会拒绝并且返还HTTP状态码401 前端 xff1a 前端将认证的Token放到loadStorage中 xff0c 在认证失效时候清空 在接到HTTP状态码为401时 x
  • 载波相位测量

    1 简述GPS载波相位测量的基本原理 载波相位测量的观测量是GPS接收机所接收的卫星载波信号与接收机本振参考信号的相位差 利用接收机测定载波相位观测值 xff0c 经基线向量解算以获得两个同步观测站之间的基线向量坐标差 2 在高精度GPS测
  • PJLib开发杂谈

    去年公司搞了一个智慧乡村的项目 xff0c 其中涉及到视频监控的问题 电信提供的解决方案 xff0c 我们的平台只需要跟电信对接HLS流就可以了 这就有一个问题 xff0c 原来公司安装的监控无法接入到自己的平台 xff0c 好尴尬啊 于是
  • 开发GB28181监控平台前期准备总结

    首先得准备PJLIB的编译 xff0c 这个搜一下 xff0c 下载以后它是有VS的工程文件的 xff0c 所以编译很方便 得到这个库以后 xff0c 就可以编写SIP服务程序了 xff0c 服务程序可以验证GB28181的相关通讯流程 x
  • esp8266 丢失固件 丢失程序问题

    1 首先esp8266 丢失固件 丢失程序问题已经解决 2 解决方法 我们制作了一个固件保护主板 xff0c 提供2种供电接口 xff0c 支持5v稳压 串口电平保护 xff0c 固件保护 xff0c 反电动势保护 xff0c 支持复位按键
  • printf重定向

    1 printf与fputc 对于 printf 函数相信大家都不陌生 xff0c 第一个C语言程序就是使用 printf 函数在屏幕上的控制台打印出Hello World xff0c 之后使用 printf 函数输出各种类型的数据 xff
  • ESP32_BLUFI代码移植过程遇到的问题

    1 先是运行esp32官方给的例程 xff0c 出现了错误报错如下 xff1a esp image Image length 1053648 doesn t fit in partition length 1048576 boot Fact
  • Java 中的 Iterator 迭代器详解

    x1f366 Iterator 接口 在程序开发中 xff0c 经常需要遍历集合中的所有元素 针对这种需求 xff0c JDK 专门提供了一个接口 java util Iterator Iterator 接口也是 Java 集合中的一员 x
  • 三.【NodeJs入门学习】POST接口

    上一节我们学习了get接口 xff0c 这一节我们自己来写一下post接口 1 复习一下 先复习一下上一节中get请求的步骤 上图是在入口app js中处理get请求 xff0c 先拿到请求的url xff0c 然后设置了一个函数handl
  • 多进程和多线程比较

    原文 xff1a http blog csdn net lishenglong666 article details 8557215 很详细 对比维度 多进程 多线程 总结 数据共享 同步 数据共享复杂 xff0c 需要用IPC xff1b
  • C++ 之头文件声明定义

    最近在学习 c 43 43 在编译与链接过程中遇到了一些定义与声明的问题 经过多处查阅资料 基本解惑 现记录与此 希望让后面人少走些弯路 C 43 43 的头文件应该用什么扩展名 目前业界的常用格式如下 implementation fil
  • arduino修改串口缓冲区大小的三种办法

    由于SoftwareSerial h默认只接收64字节串行缓冲区 xff0c Arduino会将之后接收到的数据丢弃 xff0c 不满足业务需求 以下三种方法是笔者参考网上各种资料总结出来 xff0c 对于WEMOS D1 R2 xff0c
  • C语言调用libcurl的一个简单例子

    首先我们创建一个php页面 xff1a lt meta http equiv 61 span class hljs string 34 Content Type 34 span content 61 span class hljs stri
  • 【C++】类构造函数、析构函数的调用顺序「完整版」

    一 全局变量 静态变量和局部变量 全局变量在程序开始时调用构造函数 在程序结束时调用析构函数 静态变量在所在函数第一次被调用时调用构造函数 在程序结束时调用析构函数 xff0c 只调用一次 局部变量在所在的代码段被执行时调用构造函数 xff
  • linux下使用shell发送http请求

    本文主要介绍如何在linux下使用shell发送http请求 一 curl 1 get请求 curl命令默认下就是使用get方式发送http请求 curl www span class hljs preprocessor baidu spa
  • 【STL真好用】1057 Stack C++(30)

    1057 Stack 30 分 Stack is one of the most fundamental data structures which is based on the principle of Last In First Ou
  • C++学习之头文件引用

    目录结构如下 test h的定义如下 xff1a ifndef TEST H define TEST H include lt vector gt include lt string gt using namespace std class
  • checksum 算法

    说明 checksum xff1a 总和检验码 xff0c 校验和 xff0c 可以理解为check xff08 校验 xff09 xff0c sum xff08 和 xff09 在数据处理和通信领域 xff0c 通过一定算法对传输的数据进

随机推荐

  • 解决cannot open shared object file: No such file or directory

    一 linux下调用动态库 so文件时提示 xff1a cannot open shared object file No such file or directory 解决办法 xff1a 1 此时ldd xxx查看依赖缺少哪些库 lib
  • cmake 使用(六)

    本文是 cmake 使用的第六篇 主要介绍如何设置编译器优化标志 上一篇的链接为 xff1a https blog csdn net QCZL CC article details 119825737 xff0c 主要介绍如何将自己的软件安
  • 8086寄存器介绍

    8086 有14个16位寄存器 xff0c 这14个寄存器按其用途可分为 1 通用寄存器 2 指令指针 3 标志寄存器和 4 段寄存器等4类 1 通用寄存器有8个 又可以分成2组 一组是数据寄存器 4个 另一组是指针寄存器及变址寄存器 4个
  • C++常用操作符:: -> . (例子详解)

    C 43 43 提供了三种访问类或者类对象的操作符 xff0c 他们是 双冒号 点 箭头 gt 这三种操作符有着各自的使用场景和定义 双冒号 A B 表示作用域运算符 A一定是一个类的名称或命名空间的名称 仅仅用于当B是A类 A命名空间的一
  • STM32中断优先级的分配以及中断原则

    STM32d的中断优先级由NVIC IPRx寄存器来配置 xff0c IPR的宽度为8bit所以原则上每个中断可配置的优先级为0 255 xff0c 数值越小优先级越高 xff0c 但对于大部分的 Cortex M3芯片都会精简设计 xff
  • 晶体管的结构、类型和三种组态

    晶体管有两大类型 双极型晶体管 BJT 和场效应管 FET 双极型晶体管又称为半导体三极管 晶体三极管 xff0c 简称晶体管 它由两个PN结组合而成 xff0c 有两种载流子参与导电是一种电流控制电流源器件 场效应管仅有一种载流子参与导电
  • STM32单片机基础09——重定向printf函数到串口输出的多种方法

    本文详细的介绍了如何重定向printf输出到串口输出的多种方法 xff0c 包括调用MDK微库 xff08 MicroLib xff09 的方法 xff0c 调用标准库的方法 xff0c 以及适用于 GNUC 系列编译器的方法 1 prin
  • STM32直流减速电机控制篇(一)PWM调速

    直流电机原理 下面是分析直流电机的物理模型图 其中 xff0c 固定部分有磁铁 xff0c 这里称作主磁极 xff1b 固定部分还有电刷 转动部分有环形铁芯和绕在环形铁芯上的绕组 直流电机的转动原理我就不再赘述 xff0c 比较简单易懂 直
  • STM32直流减速电机控制篇(二)编码器测速原理

    编码器 编码器是一种将角位移或者角速度转换成一连串电数字脉冲的旋转式传感器 xff0c 我们可以通过编码器测量到底位移或者速度信息 编码器从输出数据类型上分可以分为增量式编码器和绝对式编码器 从编码器检测原理上来分 xff0c 还可以分为光
  • STM32直流减速电机控制篇(三)编码器测速程序编写

    编程思路 任何一个程序的编写我们都应该先理清楚编程思路 xff0c 通过上一篇讲解的编码器测速原理我们应该知道要想通过编码器得知电机转速我们第一步就应该是捕获A相和B相输出的脉冲 因为电机速度的定义是单位时间内的转数 xff0c 所以第二步
  • GPIO模式

    开漏输出 只能输出低电平 xff0c 不能输出高电
  • 单片机485通信

    1 RS485简介 485 xff08 一般称作 RS485 EIA 485 xff09 是隶属于 OSI 模型物理层的电气特性规定为 2 线 xff0c 半双工 xff0c 多点信的标准 它的电气特性和 RS 232 大不一样 用缆线两端
  • Jetson Xavier NX 镜像制作、烧录及克隆

    以下所有方法仅适用于Jetson Xavier Nx 16G emmc版本 其他版本仅供参考 官方文档下载链接为https developer nvidia com embedded downloads search 61 Develope
  • Postman下载,安装,注册及登录教程

    目录 一 Postman简介 二 Postman的注册 1 首先下载Postman xff0c 进入官网 xff1a Download Postman Get Started for Free 2 安装Postman 3 找到所下载的app
  • 一文掌握fastapi微服务开发

    目录 一 概述 1 1 微服务 1 1 1 微服务的优势 1 1 2 微服务的缺点 1 2 为何使用Python开发微服务 1 3 FastAPI概述 二 开发 2 1 安装FastAPI 2 1 1 安装虚拟环境 2 1 2 创建虚拟环境
  • Windows通过SSH连接虚拟机中的ubuntu系统

    zz windows通过ssh连接虚拟机中的ubuntu步骤 音量 博客园
  • PaddleServing图像语义分割部署实践

    目录 一 任务概述 二 官方示例部署 2 1 安装PaddleServing 2 2 导出静态图模型 2 3 转换为serving模型 2 4 启动服务 2 5 客户端请求 三 基于PipeLine的抠图功能部署 3 1 基于深度学习的抠图
  • C/C++资源大全(各种库、框架等)

    转载 https www cplusplus me 2182 html C 43 43 资源大全 各种库 框架等 目录 隐藏 1 标准库2 框架3 人工智能4 异步事件循环5 音频6 生态学7 压缩8 并发性9 容器10 密码学11 数据库
  • 一文掌握面向Windows平台的深度学习工控程序开发(使用Paddle Inference部署MFC、C#程序,内含完整代码链接)

    目录 一 概述1 1 智能制造和飞桨1 2 Paddle Inference工业级应用部署工具 二 算法训练和导出2 1 任务概述和实现原理2 2 训练和静态模型导出 三 部署环境准备四 Windows下C 43 43 工程编译和运行4 1
  • png图片自动转ttf字体(使用python实现)

    这里写目录标题 一 任务概述二 实现2 1 ocr识别2 1 1 安装环境2 1 2 实现脚本 2 2 图形文字精确提取2 3 png转svg2 4 svg转ttf 一 任务概述 任务要求 xff1a 需要将上述生僻字png图片批量自动转成