溫馨提示×

溫馨提示×

您好,登錄后才能下訂單哦!

密碼登錄×
登錄注冊×
其他方式登錄
點(diǎn)擊 登錄注冊 即表示同意《億速云用戶服務(wù)條款》

怎么利用Python生成便簽圖片

發(fā)布時(shí)間:2021-02-07 14:58:14 來源:億速云 閱讀:197 作者:小新 欄目:開發(fā)技術(shù)

這篇文章給大家分享的是有關(guān)怎么利用Python生成便簽圖片的內(nèi)容。小編覺得挺實(shí)用的,因此分享給大家做個(gè)參考,一起跟隨小編過來看看吧。

使用 Python Pillow生成便簽圖片,效果如下:

怎么利用Python生成便簽圖片

PIL 提供了 PIL.ImageDraw.ImageDraw.text 方法,可以方便的把文字寫到圖片上,簡單示例如下:

from PIL import Image, ImageDraw, ImageFont
# get an image
base = Image.open('Pillow/Tests/images/hopper.png').convert('RGBA')

# make a blank image for the text, initialized to transparent text color
txt = Image.new('RGBA', base.size, (255,255,255,0))

# get a font
fnt = ImageFont.truetype('Pillow/Tests/fonts/FreeMono.ttf', 40)
# get a drawing context
d = ImageDraw.Draw(txt)

# draw text, half opacity
d.text((10,10), "Hello", font=fnt, fill=(255,255,255,128))
# draw text, full opacity
d.text((10,60), "World", font=fnt, fill=(255,255,255,255))

out = Image.alpha_composite(base, txt)

out.show()

為什么要計(jì)算文字的寬高呢?把文字直接寫到背景圖不可以么?

Pillow PIL.ImageDraw.ImageDraw.text 寫文字是按換行符 \n 換行的,如果個(gè)字符串特別長,文字部分就會(huì)超出背景圖的寬度,所以第一步我們需要先把文本按固定的寬度計(jì)算出高度。

像圖上寫的這樣,文字轉(zhuǎn)圖片分三步:

  • 計(jì)算文字寬高

  • 生成響應(yīng)尺寸背景圖

  • 把文字寫到圖片上

計(jì)算文字寬高

這里背景圖寬度是固定的,所以文字的寬可以不用計(jì)算。 PIL.ImageDraw.ImageDraw.text 是通過 \n 來換行的,那我們只需要在文字合適的位置加上 \n 就可以了。

第一個(gè)想到的是 textwrap 方法,textwrap 可以實(shí)現(xiàn)通過調(diào)整換行符的位置來格式化文本。但 textwrap 還有一個(gè)問題就是它是根據(jù)字符長度來分隔的,但文本中的字符并不是等寬的,通過 textwrap 格式化后的文字寫到圖片上效果可能是這樣的:

怎么利用Python生成便簽圖片

使用這種方式,如果我們要調(diào)整字體大小,每一行的長度都還需要再重新調(diào)整。

為了保證每一行寬度盡可能的一致,這里使用 PIL.ImageDraw.ImageDraw.textsize 獲取字符寬高,然后按約定寬度把長文本分隔成文本列表,然后把列表每行文字寫到圖片上。

def get_paragraph(text, note_width):
 # 把每段文字按約定寬度分隔成幾行
 txt = Image.new('RGBA', (100, 100), (255, 255, 255, 0))
 # get a drawing context
 draw = ImageDraw.Draw(txt)
 paragraph, sum_width = '', 0
 line_numbers, line_height = 1, 0
 for char in text:
 w, h = draw.textsize(char, font)
 sum_width += w
 if sum_width > note_width:
  line_numbers += 1
  sum_width = 0
  paragraph += '\n'
 paragraph += char
 line_height = max(h, line_height)
 if not paragraph.endswith('\n'):
 paragraph += '\n'
 return paragraph, line_height, line_numbers


def split_text(text):
 # 將文本按規(guī)定寬度分組
 max_line_height, total_lines = 0, 0
 paragraphs = []
 for t in text.split('\n'):
 # 先按 \n 把文本分段
 paragraph, line_height, line_numbers = get_paragraph(t)
 max_line_height = max(line_height, max_line_height)
 total_lines += line_numbers
 paragraphs.append((paragraph, line_numbers))
 line_height = max_line_height
 total_height = total_lines * line_height
 # 這里返回分好的段,文本總高度以及行高
 return paragraphs, total_height, line_height

這是按字符寬度分隔文本寫到圖片的效果:


怎么利用Python生成便簽圖片

由于文本長度不固定,生成得到的文本高度也不固定,背景圖我們也需要?jiǎng)討B(tài)生成

根據(jù)文本高度生成背景圖

怎么利用Python生成便簽圖片

通過圖片我們可以看到,頭部和尾部是固定的,變化的是文字部分,那么背景圖片的高度計(jì)算公式為

背景圖片高度=頭部高度+尾部高度+文本高度

實(shí)現(xiàn)代碼如下:

NOTE_HEADER_IMG = path.normpath(path.join(
 path.dirname(__file__), 'note_header_660.png'))
NOTE_BODY_IMG = path.normpath(path.join(
 path.dirname(__file__), 'note_body_660.png'))
NOTE_FOOTER_IMG = path.normpath(path.join(
 path.dirname(__file__), 'note_footer_660.png'))
NOTE_WIDTH = 660
NOTE_TEXT_WIDTH = 460
body_height = NOTE_BODY_HEIGHT = 206
header_height = NOTE_HEADER_HEIGHT = 89
footer_height = NOTE_FOOTER_HEIGHT = 145
font = ImageFont.truetype(NOTE_OTF, 24)


def get_images(note_height):
 numbers = note_height // body_height + 1
 images = [(NOTE_HEADER_IMG, header_height)]
 images.extend([(NOTE_BODY_IMG, body_height)] * numbers)
 images.append((NOTE_FOOTER_IMG, footer_height))
 return images


def make_backgroud():
 # 將圖片拼接到一起
 images = get_images()
 total_height = sum([height for _, height in images])
 # 最終拼接完成后的圖片
 backgroud = Image.new('RGB', (body_width, total_height))
 left, right = 0, 0
 background_img = '/tmp/%s_backgroud.png' % total_height
 # 判斷背景圖是否存在
 if path.exists(background_img):
 return background_img
 for image_file, height in images:
 image = Image.open(image_file)
 # (0, left, self.body_width, right+height)
 # 分別為 左上角坐標(biāo) 0, left
 # 右下角坐標(biāo) self.body_width, right+height
 backgroud.paste(image, (0, left, body_width, right+height))
 left += height # 從上往下拼接,左上角的縱坐標(biāo)遞增
 right += height # 左下角的縱坐標(biāo)也遞增
 backgroud.save(background_img, quality=85)
 return background_img

將文字寫到圖片

現(xiàn)在我們得到了背景圖以及分隔好的文本,就可以直接將文本寫到圖片上了

def draw_text(paragraphs, height):
 background_img = make_backgroud()
 note_img = Image.open(background_img).convert("RGBA")
 draw = ImageDraw.Draw(note_img)
 # 文字開始位置坐標(biāo),需要根據(jù)背景圖的大小做調(diào)整
 x, y = 80, 100
 for paragraph, line_numbers in paragraphs:
 for line in paragraph.split('\n')[:-1]:
  draw.text((x, y), line, fill=(110, 99, 87), font=font)
  y += line_height
 # draw.text((x, y), paragraph, fill=(110, 99, 87), font=font)
 # y += self.line_height * line_numbers
 note_img.save(filename, "png", quality=1, optimize=True)
 return filename

完整版代碼請查看 [ https://github.com/gusibi/momo/blob/master/momo/note.py ]

執(zhí)行后效果如圖:

怎么利用Python生成便簽圖片

遇到的問題

為了能方便使用,我把這個(gè)做成了公號(hào)的一個(gè)功能,然后遇到了一個(gè)嚴(yán)重問題, 太慢了!

使用 line_profiler 分析可以發(fā)現(xiàn),大部分時(shí)間都消耗在了圖片保存這一步,

note_img.save(filename, "png", quality=1, optimize=True)

性能分析工具也會(huì)占用時(shí)間,測試完成后需要關(guān)閉分析

解決這個(gè)問題可能的方法:

  • 減小背景圖片大小

  • 減小字體大小

通過測試,發(fā)現(xiàn)把背景圖寬度從990減到660,字體大小從40px 調(diào)整到24px,生成的圖片大小體積縮小了接近1倍,生成速度也比原來快了2/5。

相同代碼,相同文本,使用 python3 只用了2.3s,而 Python2 用時(shí)卻是5.3 s,還從來沒在其它功能上遇到過 Python2 和 Python3 有這么大的差別。

具體差異可以使用源碼測試一下

還是有問題

優(yōu)化完圖片生成速度后,發(fā)現(xiàn)在長文本狀態(tài)下,公號(hào)還是會(huì)超時(shí)報(bào)錯(cuò)。經(jīng)過檢查發(fā)現(xiàn)是圖片上傳到公眾平臺(tái)太慢了(服務(wù)器只有1M 帶寬,沒有辦法.)。

解決方法,把圖片上傳到騰訊云(文件上傳使用的是內(nèi)網(wǎng)帶寬,不受限制),返回圖片 url。

怎么利用Python生成便簽圖片

感謝各位的閱讀!關(guān)于“怎么利用Python生成便簽圖片”這篇文章就分享到這里了,希望以上內(nèi)容可以對(duì)大家有一定的幫助,讓大家可以學(xué)到更多知識(shí),如果覺得文章不錯(cuò),可以把它分享出去讓更多的人看到吧!

向AI問一下細(xì)節(jié)

免責(zé)聲明:本站發(fā)布的內(nèi)容(圖片、視頻和文字)以原創(chuàng)、轉(zhuǎn)載和分享為主,文章觀點(diǎn)不代表本網(wǎng)站立場,如果涉及侵權(quán)請聯(lián)系站長郵箱:is@yisu.com進(jìn)行舉報(bào),并提供相關(guān)證據(jù),一經(jīng)查實(shí),將立刻刪除涉嫌侵權(quán)內(nèi)容。

AI