Python: 生成带用户昵称的头像

需求

新建用户后,根据用户输入的昵称生成图片。(例:注册"钉钉"用户后,头像根据输入的名字生成)

开发环境

  • Windows 10
  • Python 3.8
  • Pillow 8.1.2

实现

蛇皮皮蛋:Python创建文字图片(居中)/多图片合并(PIL),参考这篇文章,实现了新建一个图片,并把文字渲染到图片上。代码如下:

import cv2
import numpy as np
from PIL import ImageFont, ImageDraw, Imagedef new_image(size, color, name):img = Image.new('RGB', size, color)  # 生成图片img.show()  # 展示图片img.save(name)  # 保存图片def create_font_img(value, file_name, path):img = cv2.imread(path)  # 打开底片font_path = "simsun/simsun.ttc"  # 设置需要显示的字体font = ImageFont.truetype(font_path, size=100)  # 字体大小为 100img_pil = Image.fromarray(img)draw = ImageDraw.Draw(img_pil)text_width, text_height = draw.textsize(value, font)  # 获取字体宽高# 绘制文字信息,img.shape 求图片的宽和高,字体绘制的开始位置(x, y),(255,255,255) 为白色字体draw.text(((img.shape[1] - text_width) / 2, (img.shape[0] - text_height) / 2), value, fill=(255, 255, 255), font=font)bk_img = np.array(img_pil)# cv2.imshow("add_text", bk_img)# cv2.waitKey()cv2.imwrite(file_name + "_font.png", bk_img)new_image((128, 128), (192, 202, 208), "new.jpg")
create_font_img("张", "name", "new.jpg")

代码执行后得到两张图片,如下:

底图

成果图1

根据上文提到的文章,简化后得到了以上代码,第一个函数 new_image() 新建一个图片,没有异议。

第二个函数 create_font_img() 将文字渲染到图片上。首先是 cv2.imread() 读取图片,之后设置文字的字体与大小,draw.text() 将文字画到图片上。注意到此函数的最后三行代码,cv2.imshow() 的意思应该非常明显,即直接显示图片,cv2.imwrite() 保存图片,那么 cv2.waitKey() 是什么意思呢?

这不是此文章重点,可以看看下面这篇文章:山上有强强:cv2.waitKey的入门级理解

注意 cv2 需要安装 opencv-python 使用,命令是 pip install opencv-python

事情到这里并没有结束,我对以上代码并不满意。主要问题如下:

  • 需要新建一张图片为底片,再读取此图片,渲染文字。如果能不保存,直接使用此图片就更好了。
  • 需要读字体文件,一个字体文件非常大,至少此处用到的 simsun.ttc 字体文件就有 10 MB 之大。如果能从系统读取,或者不使用字体文件会更好。
  • 所用第三方库太多,达到三个。三个库看起来不多,但是如非必要勿增实体(包括上面的字体文件),特别是部分库版本不一,用在项目中,越少越好,以免出现什么问题。

基于这三个问题,必需简化代码。

进阶

经过搜索,发现了下面这篇文章:amigo1226:自动生成带昵称的头像(仿照钉钉头像)。此文章,实现了不用底片直接生成图片。不过这篇文章是用 Java 写的,为了验证是否成功,我特意用 IDEA 运行了代码,确实可以成功,得到的图与上文的成果图1一样。那么,Python 理论上也是可以的。

仔细阅读此文章中的代码,发现一句代码:BufferedImage bi = new BufferedImage(width, height, BufferedImage.TYPE_INT_RGB);, BufferedImage 这意思似乎是说图片存在缓存中?那么就看看 PIL 库有没有类似的方法把。果然,Image 类中还真有一个方法,是 frombuffer(),不过在多次尝试后,此方法似乎并不行。仔细思考我们的代码,第一个函数中用了 img.save() 来保存图片。

那么我们不能立即保存图片,其次 create_font_img() 中使用了 cv2.imread() 读取图片,既然不用读取图片,这句代码也就可以删除了。此时 cv2 的用法只剩 cv2.imwrite() 保存图片了,既然打算不使用那么多库,PIL 也是有保存方法的(img.save()),如此一来便可以将 cv2 库的用法从代码中删除。

def create_font_img(value, file_name, path):# img = cv2.imread(path)  # 打开底片img = Image.new('RGB', (128, 128), (192, 202, 208))  # 生成图片font_path = "simsun/simsun.ttc"  # 设置需要显示的字体font = ImageFont.truetype(font_path, size=100)  # 字体大小为 100img_pil = Image.fromarray(img)draw = ImageDraw.Draw(img_pil)text_width, text_height = draw.textsize(value, font)  # 获取字体宽高# 绘制文字信息,img.shape 求图片的宽和高,字体绘制的开始位置(x, y),(255,255,255) 为白色字体draw.text(((img.shape[1] - text_width) / 2, (img.shape[0] - text_height) / 2), value, fill=(255, 255, 255), font=font)bk_img = np.array(img_pil)# cv2.imwrite(file_name + "_font.png", bk_img)img.save(file_name + "_font.png")  # 保存图片

初步简化代码如上,这样的代码并不能正确执行,问题很多,首先是,cv2.imread() 读取图片文件得到的是 <class 'numpy.ndarray'> 类型。这个类型下面 Image.fromarray() 创建图像,和 img.shape 要读取图片像素大小。而 Image.new() 创建的图片并不是此种类型,而是 <class 'PIL.Image.Image'>。本来打算看看是否可以将 <class 'PIL.Image.Image'> 转为 <class 'numpy.ndarray'>(其实就是 np.array() 这个方法),但转念一想,不打算用 numpy 库,也就没有转类型的必要,直接看能否只使用 PIL 库作图。

整理一下,那么整个作图流程如下:新建一张图,求出图的像素大小(其实大小在新建图时已给出),设置需要画上去的文字的字体与大小,求出文字的像素大小(设置字体时已给出),将文字画在图片上;保存图片。

那么由此得出的代码如下:

def create_font_img(name):img = Image.new('RGB', (128, 128), (192, 202, 208))  # 生成一张像素 128*128 图片font_path = "simsun/simsun.ttc"  # 设置需要显示的字体font = ImageFont.truetype(font_path, size=100)  # 字体大小为 100draw = ImageDraw.Draw(img)  # 绘图# 绘制文字信息,字体绘制的开始位置(x, y),(255,255,255) 为白色字体draw.text(((128 - 100) / 2, (128 - 100) / 2), name, fill=(255, 255, 255), font=font)# img.show()  # 展示图片img.save("name.png")  # 保存图片

执行以上的代码,得到的图与成果图1一样。

那么图片的像素大小与文字的像素大小是否有相应的方法获取呢?既然 cv2.imread()img.shape()) 和 ImageDraw.Draw()draw.textsize())可以读取图片和文字像素大小,理论上 Image.new()ImageFont.truetype() 应该有对应的方法。通过查找该对象所拥有的函数,发现了两个方法 size 和 getsize,分别使用 img.sizefont.getsize() 可以得到图片像素和文字像素。

修改后的代码如下:

def create_font_img(name):img = Image.new('RGB', (128, 128), (192, 202, 208))  # 生成图片img_width, img_height  = img.sizefont_path = "simsun/simsun.ttc"  # 设置需要显示的字体font = ImageFont.truetype(font_path, size=100)  # 字体大小为 100text_width, text_height = font.getsize(name)  # 获取字体宽高draw = ImageDraw.Draw(img)# 绘制文字信息,img.shape 求图片的宽和高,字体绘制的开始位置(x, y),(255,255,255) 为白色字体draw.text(((img_width - text_width) / 2, (img_height - text_height) / 2), name, fill=(255, 255, 255), font=font)# img.show()img.save("name.png")  # 保存图片

用户昵称可能是中文也可能是英文,这样字体的大小为一个不变的值不太好,同时将需要更改的数值提取出来,优化代码后如下:

from PIL import Image, ImageFont, ImageDrawdef create_font_img(username, img_size, img_bg_color, file_name):img = Image.new('RGB', img_size, img_bg_color)  # 生成图片img_width, img_height  = img.sizefont_path = "simsun/simsun.ttc"  # 设置需要显示的字体if '\u4e00' <= username <= '\u9fff':font = ImageFont.truetype(font_path, size=100)  # 中文字符字体大小为 100elif 'a' <= username <= 'z' or 'A' <= username <= 'Z':username = username.upper()  # 将英文字母转为大写font = ImageFont.truetype(font_path, size=60)  # 英文字符字体大小为 60else:font = ImageFont.truetype(font_path, size=32)  # 其他字符字体大小为 32text_width, text_height = font.getsize(username)  # 获取字体宽高draw = ImageDraw.Draw(img)# 绘制文字信息,字体绘制的开始位置(x, y),(255,255,255) 为白色字体draw.text(((img_width - text_width) / 2, (img_height - text_height) / 2), username, fill=(255, 255, 255), font=font)img.save(file_name + ".png")  # 保存图片create_font_img('张', (128, 128), (192, 202, 208), 'zhang')

到此,解决了需要中间图片和第三方库多的问题,只剩下一个字体文件的问题?能否不使用字体文件呢?或者使用系统中的文件呢?

经过尝试,如果使用默认的字体文件,无法获取字体的像素大小,因为设置默认字体的方法是 ImageFont.load_default().font,并没有指定像素大小,将 draw.text() 中的 char_width_height 设置定值,但是 draw.text() 中的 font 出现了中文字符的编解码问题,英文倒是可以正确执行。不过没有设置字体大小,导致一张 128*128 像素的图片中,字符的大小非常小,几乎不可见,如图:

成果图2

经过搜索,在这个问答中 如何使用Python ImageDraw库更改字体大小 发现了一句话,如下:

每个PIL’s docs,ImageDraw的默认字体是位图字体,因此无法缩放。要进行缩放,需要选择真正的字体类型。我希望不难找到一个不错的truetype字体“看起来有点像”默认字体在您想要的字体大小!

意思应该非常明显了,如果想要缩放字体的大小,那么就必需指定字体文件了。在搜索中了解到,也可以从系统中,加载字体文件,windows 系统中有许多字体文件,目录为 C:\Windows\Fonts,可以从这里获取字体文件。在项目中没有 simsun.ttc 时,需要先下载一个,或者先使用 Fonts 下的文件,Fonts 中的文件如下图:

Fonts 中的字体文件

将上文代码中 font_path = "simsun/simsun.ttc" 修改为 font_path = "C:\\Windows\\Fonts\\STXINGKA.TTF",字体为"华文行楷 常规",得到的图片如下:

成果图3

总结

本文介绍了如何生成一张图片,并加以文字(用户昵称),探索了一些 PIL 的用法。

生成的图片可能并不完美,比如色彩的搭配,图片的圆角设置,以及文字字符过多如何割舍等问题,留待读者自行探索。

Python: 生成带用户昵称的头像相关推荐

  1. Python生成带自定义信息和头像图片的二维码

    直接上代码,有关二维码各种版本尺寸大小与信息容量以及容错相关的内容请自行搜索查阅. from os import listdir from os.path import splitext import ...

  2. Python生成带圆角图片的二维码

    示例代码1 #!/usr/bin/python # -*- coding: UTF-8 -*- # author: Carl time:2020/5/15import qrcode from PIL ...

  3. 微信小程序——最新获取用户昵称和头像的方法总结

    前段时间微信小程序对获取用户昵称和头像方法进行了更新,网上很多的文章都已经不适用了,这里简单总结一下 首先,传统接口wx.getUserInfo的效果会弹出一个给用户的弹窗,需要用户授权,经过测试传统 ...

  4. Java获取游戏头像_小程序最新获取用户昵称和头像的方法总结

    前段时间微信小程序对获取用户昵称和头像方法进行了更新,网上很多的文章都已经不适用了,这里简单总结一下 首先,传统接口wx.getUserInfo的效果会弹出一个给用户的弹窗,需要用户授权,经过测试传统 ...

  5. 融云--如何显示用户昵称和头像的

    融云认为,每一个设计良好且功能健全的 App 都应该能够在本地获取.缓存并更新用户信息.所以,融云不维护用户基本信息(用户 Id.昵称.头像).此外,App 提供用户信息也避免了由于缓存导致的用户信息 ...

  6. 随机生成游戏用户昵称(nodejs版本)(含机器人头像,金币等)

    1 前言 有时需要生成随机的用户(或机器人)昵称,头像,金币等,但又不想太生硬,可以现在网上爬一些常见昵称到文本中,然后读取出来,随机使用即可. 2 代码 var nickNameArr = [];f ...

  7. python—生成带logo的二维码(零基础向)

    在python 中可以用qrcode 库将文本.图片.视频链接等生成二维码,并用图片处理库PIL 中的Image 方法添加自定义图片,制作带有logo的二维码.本文以Windows 系统为例进行演示. ...

  8. 【快速文档】open-data标签,在小程序中无需用户授权也可以使用用户昵称和头像

    介绍 按照现在的规定,在小程序中使用任何有关于用户的个人信息,都是需要授权的.但是,如果是通过open-data来使用用户的信息的话,则无需用户授权. 为什么open-data不需要授权呢,因为ope ...

  9. 【uniapp,样式,登录】【微信小程序】获取用户昵称和头像 uni.getUserProfile 废弃 后 新规则 写法

    uni.getUserProfile 已废弃 //授权获取用户信息 废弃getUserProfile(e) {console.log('授权登录')uni.showLoading({title: '加 ...

最新文章

  1. Cross-Validation交叉验证是什么?详解及实施
  2. Mysql aborted_client_MySQL之aborted connections和aborted clients
  3. 神经网络中的「注意力」是什么?怎么用?
  4. 常见字符串拼接性能比较
  5. Kubernetes StatefulSet源码分析
  6. conda安装tensorflow-gpu简洁版_笔记本的垃圾显卡也能装Tensorflow GPU版,简明教程
  7. Oracle10g备份集压缩新特性(Backupset Compression)
  8. 自动控制matlab实验,自动控制matlab实验.doc
  9. 启动代码格式:nginx安装目录地址 -c nginx配置文件地址
  10. 早秋精品电商男装页面\海报设计PSD模板
  11. java swt designerpdf_eclipse学习笔记!(4) ----- SWT Designer 下 SWT常用组件
  12. 2020统计局的行政划分表_天津市第七次全国人口普查区域划分与地图绘制试点工作在西青区开展...
  13. sql 安装程序文件_【病毒文件分析】MedusaLocker勒索病毒,小心全网被加密
  14. 2021下半年软考网络工程师上午真题(二)
  15. Docker容器 Cgroup资源分配(CPU和内存资源分配)
  16. matlab正弦波用示波器测失真,请教下,自己做个正弦波信号发生器,如何测试波形的失真大小?...
  17. C#微信、支付宝扫码支付源码
  18. <tx:annoation-driven/>, web.xml的Spring配置文件位置,Spring父子容器, Spring部分源代码分析,<mvc:annotation-driven/>
  19. 宝付国际一文读懂:跨境电商的外汇风险敞口(四)
  20. 短信通知接口json报文开发设计总结

热门文章

  1. 在线涂改图片 php,php网站怎么修改图片
  2. ​​你会项目管理吗?
  3. 串口RS232/RS485/RS422的DB9引脚定义
  4. STM32的BOOT的作用和设置
  5. MTK camera驱动浅析(2)
  6. Oracle数据库的安装以及访问
  7. 2019高教社杯全国大学生数学建模竞赛题目 D题 空气质量数据的校准
  8. 谷歌地图下载_拔剑-浆糊的传说_新浪博客
  9. Lumion 和 Vray这2款渲染器哪个更强?
  10. 透明背景转换时变为黑色解决方法