资讯专栏INFORMATION COLUMN

【正经的AI on Python入门系列】1.1斗图工具的优化——实现文本居中(还混进了一些语法基础

Luosunce / 2965人阅读

摘要:大家还记得上一篇文章来学点吧从一个斗图小工具开始中最后提到的几个问题么,我们这次就来解决一下其中难度最大的一个文本居中看,我把代码优化了上次之后,我偷偷把代码优化了,现在的方法长这样创建表情图调试用生成表情包对的,我把那些老长老长

大家还记得上一篇文章0.来学点Python吧!从一个斗图小工具开始中最后提到的几个问题么,我们这次就来解决一下其中难度最大的一个:文本居中!

看,我把代码优化了!

上次之后,我偷偷把代码优化了,现在的main方法长这样:

# -*- coding:utf-8 -*-
#__author__ = "akers"
from io import BytesIO
from PIL import Image, ImageDraw, ImageFont
import sys,platform,operators.image,operators.clipboard


def main(argv):
    # 创建表情图
    image = operators.image.draw_emo("./resources/background/pander/default.png", "./resources/face/jgz/laugth.png", argv[1])
    output = BytesIO()
    image.save(output, format="BMP")
    operators.clipboard.add_bmp(output)
    image.save("output/facing.png")

    # 调试用
    # plt.figure("生成表情包")
    # plt.imshow(target)
    # plt.show()

if __name__ == "__main__":
    main(sys.argv)

对的,我把那些老长老长的代码藏到别的包里了,下面是一点题外话,顺便介绍一下Python的包与模块

Python的包与模块

以我们重构后的项目为例,一个项目的结构大致如下

python中的包就是一个包含了__init__.py文件的文件夹,包下可有子包

模块

python的模块就是一个py文件,其中可包括类、函数、变量等等内容

导入包

python中使用import关键字引入模块,import的使用分以下几种

import package[.sub_package].model

导入指定的模块,调用时需使用模块全名,例如

import operators.image

operators.image.draw_emo
from package[.sub_package] import model

导入指定模块,调用时直接使用模块名

from operators import image

image.draw_emo()
import package[.sub_package].model import function

导入模块中的函数或成员、类、变量等

from operators.image import draw_emo

draw_emo()

Python的包跟模块的内容还有很多,更详细的就先不赘述了,然后让我们继续主题,咳咳

表情包文本居中

首先,我们来实现文本的居中,我们先来回顾一下我们是如何插入文本的:

#底图上的10,200位置写入文字
draw.text((10, 200), argv[1],fill="black", font=font)

text函数的第一个参数需要个元组,而元组中的0、1两个元素分别代表的x和y的坐标(以像素为单位),那实现居中就很简单了,我们只需要根据插入文本的具体宽度计算出一个合适的x坐标就可以了。
于是请看大屏幕,啊呸,请看下图,


从图中我们可以看到,文本的插入坐标可以用这样的公式进行计算:(底图宽-文本宽) / 2,那问题又来了,文本宽我们怎么得到呢?
在PIL中,我们可以用draw.textsize获取文本所占的大小,返回是个元组:(宽,高),像这样:

txtSize = draw.textsize(text, imageFont)

所以我们修改一下draw_text方法:对x坐标进行计算

def draw_text_v1(text, image, off_set=200):
    """强化版绘制文字v1,让文字在x轴上居中
    Args:
        text: 显示在图片上的文本
        image: 当前正使用的Image
        off_set: 纵向偏移量

    Returns:
        Image

    Raises:

    """
    # 加入文字
    # ImageDraw为PIL的绘图模块
    _DEFAULT_FONT_SIZE = 30
    draw = ImageDraw.Draw(image)
    imageFont = ImageFont.truetype("./resources/msyh.ttc", _DEFAULT_FONT_SIZE)
    _MAX_TXT_HEIGH = 32
    txtSize = draw.textsize(text, imageFont)
    pos_x = (CONST_IMG_WIDTH - txtSize[0]) / 2 if CONST_IMG_WIDTH > txtSize[0] else 0
    print("当前X坐标", pos_x)
    # 默认显示位置
    pos = (pos_x, off_set)
    draw.text(pos, text, fill="black", font=imageFont)
    del draw
    return image

相信眼尖的强迫症患者可能会发现,这里面混着一个很奇怪的写法:

(CONST_IMG_WIDTH - txtSize[0]) / 2 if CONST_IMG_WIDTH > txtSize[0] else 0

这个if跟else是不是有点调皮,其实这个在python 里类似于c语系里的 ? :三目运算符:如果if后的条件成立,则取if前的表达式进行返回,否则取else后的表达式,上面的代码实际等同于:

CONST_IMG_WIDTH > txtSize[0] ? (CONST_IMG_WIDTH - txtSize[0]) / 2 : 0

这个细节又暴露了Python的哲学:我已经有了if else了,还要? :干嘛,关键字的数量也要最简;而个语法应该也是从C语系转过来的小伙伴觉得最别扭的语法之一了吧....

好废话不多说,来让我们看看效果,很中对不对:

正所谓要精益求精,既然都做了居中,索性把对齐做个参数好了,跟居中一样的原理,我把左对齐跟右对齐也做了,只需要对draw_text_v1做一点点润色:

def draw_text_v2(text, image, off_set=(0, 200), allign="center"):
    """强化版绘制文字v2,左中右,想放哪里放哪里
    Args:
        text: 显示在图片上的文本
        image: 当前正使用的Image
        off_set: 偏移量,用于保留最小编剧,(x, y)以像素未单位
        allign: 排版,left左对齐,center居中,right右对齐

    Returns:
        Image

    Raises:

    """
    # 加入文字
    # ImageDraw为PIL的绘图模块
    _DEFAULT_FONT_SIZE = 30
    draw = ImageDraw.Draw(image)
    imageFont = ImageFont.truetype("./resources/msyh.ttc", _DEFAULT_FONT_SIZE)
    _MAX_TXT_HEIGH = 32
    txtSize = draw.textsize(text, imageFont)
    imageFont = ImageFont.truetype("./resources/msyh.ttc", 30)

    # 计算x坐标
    pos_x = {
        # 居中对齐
        "center": lambda max_width, txt_len, off: (max_width / 2 - txt_len / 2 if max_width > txt_len else 0) + off,
        # 左对齐
        "left": lambda max_width, txt_len, off: (off if max_width > txt_len else 0),
        # 右对齐
        "right": lambda max_width, txt_len, off: (max_width - txt_len if max_width > txt_len else 0)
    }[allign if allign in ("center", "left", "right") else "center"](CONST_IMG_WIDTH - 2*off_set[0], txtSize[0], off_set[0])

    # 默认显示位置
    pos = (pos_x, off_set[1])

    # 底图上的10,200位置写入文字
    draw.text(pos, text, fill="black", font=imageFont)
    del draw
    return image

效果好像还不错:

估计又有眼尖的小伙伴发现有个代码不对了,怎么pos_x的计算实现是如此的奇葩,我都看不懂了!

    pos_x = {
        # 居中对齐
        "center": lambda max_width, txt_len, off: (max_width / 2 - txt_len / 2 if max_width > txt_len else 0) + off,
        # 左对齐
        "left": lambda max_width, txt_len, off: (off if max_width > txt_len else 0),
        # 右对齐
        "right": lambda max_width, txt_len, off: (max_width - txt_len if max_width > txt_len else 0)
    }[allign if allign in ("center", "left", "right") else "center"](CONST_IMG_WIDTH - 2*off_set[0], txtSize[0], off_set[0])

其实这里嘛,是Python实现switch功能的一个奇技淫巧...对的,你猜的没错,Python是没有switch结构的,因为:“我们已经有了完美的if...else...要switch干嘛”,所以如果按照官方建议,这段代码应该是这样的:

    max_width = CONST_IMG_WIDTH - 2*off_set[0]
    txt_len = txtSize[0]
    if allign == "center":
        pos_x = (max_width / 2 - txt_len / 2 if max_width > txt_len else 0) + off_set[0]
    else if allign == "left":
        pos_x = off_set[0] if CONST_IMG_WIDTH - 2*off_set[0] > txt_len else 0
    else if allign == "right":
        pos_x = max_width - txt_len if max_width > txt_len else 0
    else:
        pos_x = ((max_width / 2 - txt_len / 2 if max_width > txt_len else 0) + off_set[0]

而我在实现上是采用了字典结构加上lamada表达式进行实现的,这里再多嘴一句,python的lamada表达式只支持单行只支持单行!因为官方觉得如果一个lamada表达式需要多行才能解决问题,那你还是去定义个函数吧!
啊?你说上面的代码还是没搞懂?嗯...好吧,那我把代码好好讲讲。
首先,代码是使用了一些简写的,我们把它拆出来:

    def center(max_width, txt_len, off):
        return (max_width / 2 - txt_len / 2 if max_width > txt_len else 0) + off

    def left(max_width, txt_len, off):
        return off if max_width > txt_len else 0

    def right(max_width, txt_len, off):
        return max_width - txt_len if max_width > txt_len else 0
    
    dict_temp = {
        # 居中对齐
        "center": center,
        # 左对齐
        "left": left,
        # 右对齐
        "right": right
    }

    pos_x_func = dict_temp[allign if allign in ("center", "left", "right") else "center"]
    pos_x = pos_x_func(CONST_IMG_WIDTH - 2*off_set[0], txtSize[0], off_set[0])

得益于python动态语言的特性,我们可以把所有的中间变量都去掉,就形成了样例中的那种代码结构了,这个属于加大阅读难度的(但代码看上去简洁了啊!)大家搞不懂的也无所谓,我就主要简单介绍一下lamada表达式了。
从上面分解的代码可以看出,lamada实际上也是函数,有入参有返回值,但就是没了个名字,所以他实际上是一个匿名函数,其构成是这样的:

lamada 参数1 [, 参数n] : 表达式

记住了,Python不支持多行lamada!

感觉这次文章基础语法扯的有点多了,之后的文章对基础语法的介绍会进一步减少的!
本次的代码样例同样在我的GitHub上可以找到

好的,那么接下来

额...好吧,下次我们就来解决上面这个问题!

文章版权归作者所有,未经允许请勿转载,若此文章存在违规行为,您可以联系管理员删除。

转载请注明本文地址:https://www.ucloud.cn/yun/42126.html

相关文章

  • 正经AI on Python入门系列】1.2 斗图工具优化——文本宽度自适应(来做点小数据分析

    摘要:在上一篇文章图工具的优化实现文本居中中,我们已经实现了对插入字体的左中右对齐显示,那因为上期文章混进去了不少语法讲解,所以后面的内容就顺延到这啦,哈哈哈。 showImg(https://segmentfault.com/img/bVbeIu4?w=250&h=250); 在上一篇文章【图工具的优化——实现文本居中】中,我们已经实现了对插入字体的左中右对齐显示,那因为上期文章混进去了不...

    fireflow 评论0 收藏0
  • 正经AI on Python入门系列】0.来学点Python吧!从一个斗图工具开始

    摘要:因此,本文将会以一些正经的严谨的有深度的大概吧的课题,慢慢的接触人工智能的相关知识。 Before The Beginning     近年,技术圈炒的最火的两个话(ba)题(gua)不外乎就是人工智障智能以及炒币区块链了,这个系列文章我主要以一个小菜鸟的角度一步一步的对人工智能的相关知识做一点了解,也算是一个颤颤巍巍追着AI浪潮公交车的社会主义五好青年,咳咳,扯远了...其实对于人工...

    赵连江 评论0 收藏0
  • [原创]nim与rust特点比较

    摘要:与的特点比较这两个目前都是小众语言做了些时间的研究写了点东西有了点心得相似点有卫生宏区别与的不卫生宏在类或定义体之外定义函数代码没有分成头与实现体例如的头与实现的与定义的接口定义与实现定义是分开的而与是不分开的运用函数式编程高阶函数目前是新 nim与rust的特点比较 这两个目前都是小众语言,做了些时间的研究,写了点东西有了点心得 相似点: 有卫生宏.区别与C++的(不卫生)宏 在类...

    DevTalking 评论0 收藏0
  • 重磅 | 完备 AI 学习路线,最详细资源整理!

    摘要:是你学习从入门到专家必备的学习路线和优质学习资源。的数学基础最主要是高等数学线性代数概率论与数理统计三门课程,这三门课程是本科必修的。其作为机器学习的入门和进阶资料非常适合。书籍介绍深度学习通常又被称为花书,深度学习领域最经典的畅销书。 showImg(https://segmentfault.com/img/remote/1460000019011569); 【导读】本文由知名开源平...

    荆兆峰 评论0 收藏0

发表评论

0条评论

最新活动
阅读需要支付1元查看
<