摘要:具體差異可以使用源碼測試一下還是有問題優化完圖片生成速度后,發現在長文本狀態下,公號還是會超時報錯。經過檢查發現是圖片上傳到公眾平臺太慢了服務器只有帶寬,沒有辦法。
最近有文字轉圖片的需求,但是不太想下載 APP,就使用 Python Pillow 實現了一個,效果如下:
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()
為什么要計算文字的寬高呢?把文字直接寫到背景圖不可以么?
Pillow PIL.ImageDraw.ImageDraw.text寫文字是按換行符 換行的,如果一句話特別長,文字就會超出背景圖的寬度,所以第一步我們需要先把文本按固定的寬度計算出高度。
像圖上寫的這樣,文字轉圖片分三步:
計算文字寬高
生成響應尺寸背景圖
把文字寫到圖片上
計算文字寬高這里背景圖寬度是固定的,所以文字的寬可以不用計算。PIL.ImageDraw.ImageDraw.text 是通過 來換行的,那我們只需要在文字合適的位置加上 就可以了。
第一個想到的是 textwrap 方法,textwrap 可以實現通過調整換行符的位置來格式化文本。但 textwrap 還有一個問題就是它是根據字符長度來分隔的,但文本中的字符并不是等寬的,通過textwrap格式化后的文字寫到圖片上效果可能是這樣的:
使用這種方式,如果我們要調整字體大小,每一行的長度都還需要再重新調整。
為了保證每一行寬度盡可能的一致,這里使用 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 += " " paragraph += char line_height = max(h, line_height) if not paragraph.endswith(" "): paragraph += " " return paragraph, line_height, line_numbers def split_text(text): # 將文本按規定寬度分組 max_line_height, total_lines = 0, 0 paragraphs = [] for t in text.split(" "): # 先按 把文本分段 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
這是按字符寬度分隔文本寫到圖片的效果:
由于文本長度不固定,生成得到的文本高度也不固定,背景圖我們也需要動態生成根據文本高度生成背景圖
通過圖片我們可以看到,頭部和尾部是固定的,變化的是文字部分,那么背景圖片的高度計算公式為
背景圖片高度=頭部高度+尾部高度+文本高度
實現代碼如下:
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) # 分別為 左上角坐標 0, left # 右下角坐標 self.body_width, right+height backgroud.paste(image, (0, left, body_width, right+height)) left += height # 從上往下拼接,左上角的縱坐標遞增 right += height # 左下角的縱坐標也遞增 backgroud.save(background_img, quality=85) return background_img將文字寫到圖片
現在我們得到了背景圖以及分隔好的文本,就可以直接將文本寫到圖片上了
def draw_text(paragraphs, height): background_img = make_backgroud() note_img = Image.open(background_img).convert("RGBA") draw = ImageDraw.Draw(note_img) # 文字開始位置坐標,需要根據背景圖的大小做調整 x, y = 80, 100 for paragraph, line_numbers in paragraphs: for line in paragraph.split(" ")[:-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/mom...
執行后效果如圖:
遇到的問題為了能方便使用,我把這個做成了公號的一個功能,然后遇到了一個嚴重問題,太慢了!
使用 line_profiler 分析可以發現,大部分時間都消耗在了圖片保存這一步,
note_img.save(filename, "png", quality=1, optimize=True)
性能分析工具也會占用時間,測試完成后需要關閉分析
解決這個問題可能的方法:
減小背景圖片大小
減小字體大小
通過測試,發現把背景圖寬度從990減到660,字體大小從40px 調整到24px,生成的圖片大小體積縮小了接近1倍,生成速度也比原來快了2/5。
還是有問題相同代碼,相同文本,使用 python3 只用了2.3s,而 Python2 用時卻是5.3 s,還從來沒在其它功能上遇到過 Python2 和 Python3 有這么大的差別。
具體差異可以使用源碼測試一下
優化完圖片生成速度后,發現在長文本狀態下,公號還是會超時報錯。經過檢查發現是圖片上傳到公眾平臺太慢了(服務器只有1M 帶寬,沒有辦法.)。
解決方法,把圖片上傳到騰訊云(文件上傳使用的是內網帶寬,不受限制),返回圖片 url。
文章版權歸作者所有,未經允許請勿轉載,若此文章存在違規行為,您可以聯系管理員刪除。
轉載請注明本文地址:http://specialneedsforspecialkids.com/yun/41944.html
摘要:使用圖靈機器人訓練之前在對比聊天機器人實現方案的時候,我試用過圖靈機器人,他們號稱中文語境下智能渡最高的機器人大腦。 上一篇 一小時搭建微信聊天機器人 介紹了如何搭建一個可用的聊天機器人,但是和機器人聊完你會發現,聊天機器人實在是太傻了,來回就那么幾句。這是因為我們給聊天機器人的數據太少,他只能在我們給的訓練集中找它認為最合適的。那么,如何導入更多的訓練數據呢?我能想到最簡單的方法是找...
摘要:然后又介紹了基于的公號賬本應用的數據庫設計。歡迎關注公號四月試用。 前兩篇 微信公號DIY 系列: 微信公號DIY:一小時搭建微信聊天機器人 微信公號DIY:訓練聊天機器人&公號變身圖片上傳工具 介紹了如何使用搭建&訓練聊天機器人以及讓公號支持圖片上傳到七牛,把公號變成一個七牛圖片上傳客戶端。這一篇將繼續開發公號,讓公號變成一個更加實用的工具賬本(理財從記賬開始)。 代碼: 項目代...
摘要:然后又介紹了基于的公號賬本應用的數據庫設計。歡迎關注公號四月試用。 前兩篇 微信公號DIY 系列: 微信公號DIY:一小時搭建微信聊天機器人 微信公號DIY:訓練聊天機器人&公號變身圖片上傳工具 介紹了如何使用搭建&訓練聊天機器人以及讓公號支持圖片上傳到七牛,把公號變成一個七牛圖片上傳客戶端。這一篇將繼續開發公號,讓公號變成一個更加實用的工具賬本(理財從記賬開始)。 代碼: 項目代...
摘要:最近借用了女朋友的公號,感覺如果只是用來發文章,太浪費微信給提供的這些功能了。想了想,先從最簡單的開始,做一個聊天機器人吧。是一款接口的,基于一系列規則和機器學習算法完成的聊天機器人。 最近借用了女朋友的公號,感覺如果只是用來發文章,太浪費微信給提供的這些功能了。想了想,先從最簡單的開始,做一個聊天機器人吧。 使用Python實現聊天機器人的方案有多種:AIML、chatterBot以...
閱讀 3456·2021-09-08 10:46
閱讀 1186·2019-08-30 13:17
閱讀 2362·2019-08-30 13:05
閱讀 1207·2019-08-29 15:29
閱讀 2887·2019-08-29 11:31
閱讀 539·2019-08-26 12:13
閱讀 1535·2019-08-26 11:42
閱讀 1828·2019-08-23 18:37