优化图片生成速度

This commit is contained in:
CMHopeSunshine 2022-06-01 12:59:03 +08:00
parent d92913772c
commit cc92c655a7
5 changed files with 95 additions and 80 deletions

View File

@ -2,7 +2,7 @@ import datetime
import os import os
from PIL import Image, ImageDraw, ImageFont from PIL import Image, ImageDraw, ImageFont
from pathlib import Path
from utils import aiorequests from utils import aiorequests
from utils.message_util import MessageBuild from utils.message_util import MessageBuild
from utils.file_handler import load_image from utils.file_handler import load_image
@ -111,27 +111,32 @@ async def draw_abyss_card(data, uid, floor_num):
top_img.alpha_composite(role_img, (width, 165)) top_img.alpha_composite(role_img, (width, 165))
width += 150 width += 150
defeat_rank = data['defeat_rank'][0] defeat_rank = data['defeat_rank'][0]
defeat_rank_img = await aiorequests.get_img(url=defeat_rank['avatar_icon'], size=(60, 60), mode='RGBA') avatar_img = Path() / 'data' / 'LittlePaimon' / 'res' / 'avatar_side' / defeat_rank['avatar_icon'].split('/')[-1]
defeat_rank_img = await aiorequests.get_img(url=defeat_rank['avatar_icon'], size=(60, 60), mode='RGBA', save_path=avatar_img)
top_draw.text((160, 343), str(defeat_rank['value']), font=get_font(21), fill='white') top_draw.text((160, 343), str(defeat_rank['value']), font=get_font(21), fill='white')
top_img.alpha_composite(defeat_rank_img, (280, 320)) top_img.alpha_composite(defeat_rank_img, (280, 320))
damage_rank = data['damage_rank'][0] damage_rank = data['damage_rank'][0]
damage_rank_img = await aiorequests.get_img(url=damage_rank['avatar_icon'], size=(60, 60), mode='RGBA') avatar_img = Path() / 'data' / 'LittlePaimon' / 'res' / 'avatar_side' / damage_rank['avatar_icon'].split('/')[-1]
damage_rank_img = await aiorequests.get_img(url=damage_rank['avatar_icon'], size=(60, 60), mode='RGBA', save_path=avatar_img)
top_draw.text((495, 343), str(damage_rank['value']), font=get_font(21), fill='white') top_draw.text((495, 343), str(damage_rank['value']), font=get_font(21), fill='white')
top_img.alpha_composite(damage_rank_img, (590, 320)) top_img.alpha_composite(damage_rank_img, (590, 320))
take_damage_rank = data['take_damage_rank'][0] take_damage_rank = data['take_damage_rank'][0]
take_damage_rank_img = await aiorequests.get_img(url=take_damage_rank['avatar_icon'], size=(60, 60), mode='RGBA') avatar_img = Path() / 'data' / 'LittlePaimon' / 'res' / 'avatar_side' / take_damage_rank['avatar_icon'].split('/')[-1]
take_damage_rank_img = await aiorequests.get_img(url=take_damage_rank['avatar_icon'], size=(60, 60), mode='RGBA', save_path=avatar_img)
top_draw.text((180, 389), str(take_damage_rank['value']), font=get_font(21), fill='white') top_draw.text((180, 389), str(take_damage_rank['value']), font=get_font(21), fill='white')
top_img.alpha_composite(take_damage_rank_img, (280, 365)) top_img.alpha_composite(take_damage_rank_img, (280, 365))
energy_skill_rank = data['energy_skill_rank'][0] energy_skill_rank = data['energy_skill_rank'][0]
energy_skill_rank_img = await aiorequests.get_img(url=energy_skill_rank['avatar_icon'], size=(60, 60), mode='RGBA') avatar_img = Path() / 'data' / 'LittlePaimon' / 'res' / 'avatar_side' / energy_skill_rank['avatar_icon'].split('/')[-1]
energy_skill_rank_img = await aiorequests.get_img(url=energy_skill_rank['avatar_icon'], size=(60, 60), mode='RGBA', save_path=avatar_img)
top_draw.text((530, 389), str(energy_skill_rank['value']), font=get_font(21), fill='white') top_draw.text((530, 389), str(energy_skill_rank['value']), font=get_font(21), fill='white')
top_img.alpha_composite(energy_skill_rank_img, (590, 365)) top_img.alpha_composite(energy_skill_rank_img, (590, 365))
normal_skill_rank = data['normal_skill_rank'][0] normal_skill_rank = data['normal_skill_rank'][0]
normal_skill_rank_img = await aiorequests.get_img(url=normal_skill_rank['avatar_icon'], size=(60, 60), mode='RGBA') avatar_img = Path() / 'data' / 'LittlePaimon' / 'res' / 'avatar_side' / normal_skill_rank['avatar_icon'].split('/')[-1]
normal_skill_rank_img = await aiorequests.get_img(url=normal_skill_rank['avatar_icon'], size=(60, 60), mode='RGBA', save_path=avatar_img)
top_draw.text((195, 435), str(normal_skill_rank['value']), font=get_font(21), fill='white') top_draw.text((195, 435), str(normal_skill_rank['value']), font=get_font(21), fill='white')
top_img.alpha_composite(normal_skill_rank_img, (280, 410)) top_img.alpha_composite(normal_skill_rank_img, (280, 410))

View File

@ -5,6 +5,7 @@ import random
import matplotlib.pyplot as plt import matplotlib.pyplot as plt
from PIL import Image, ImageDraw, ImageFont from PIL import Image, ImageDraw, ImageFont
from pathlib import Path
from utils import aiorequests from utils import aiorequests
from utils.message_util import MessageBuild from utils.message_util import MessageBuild
from utils.file_handler import load_image from utils.file_handler import load_image
@ -121,7 +122,8 @@ async def draw_daily_note_card(data, uid):
exp = data['expeditions'] exp = data['expeditions']
i = 0 i = 0
for role in exp: for role in exp:
role_avatar = await aiorequests.get_img(url=role['avatar_side_icon'], size=(135, 135), mode='RGBA') role_avatar = Path() / 'data' / 'LittlePaimon' / 'res' / 'avatar_side' / role['avatar_side_icon'].split('/')[-1]
role_avatar = await aiorequests.get_img(url=role['avatar_side_icon'], size=(135, 135), mode='RGBA', save_path=role_avatar)
bg_img.alpha_composite(role_avatar, (i * 200 + 168, 1537)) bg_img.alpha_composite(role_avatar, (i * 200 + 168, 1537))
bg_img.alpha_composite(await draw_ring(1 - int(role['remained_time']) / 72000), (i * 201 + 101, 1490)) bg_img.alpha_composite(await draw_ring(1 - int(role['remained_time']) / 72000), (i * 201 + 101, 1490))
if role['status'] == 'Ongoing': if role['status'] == 'Ongoing':
@ -147,7 +149,6 @@ async def draw_daily_note_card(data, uid):
bg_draw.text((1408, 1588), last_finish_str, fill="#5680d2", bg_draw.text((1408, 1588), last_finish_str, fill="#5680d2",
font=get_font(60, '优设标题黑.ttf')) font=get_font(60, '优设标题黑.ttf'))
role_img = load_image(os.path.join(res_path, 'emoticons', random.choice(os.listdir(os.path.join(res_path, 'emoticons')))), size=3.5, mode='RGBA') role_img = load_image(os.path.join(res_path, 'emoticons', random.choice(os.listdir(os.path.join(res_path, 'emoticons')))), size=3.5, mode='RGBA')
bg_img.alpha_composite(role_img, (1220, 200)) bg_img.alpha_composite(role_img, (1220, 200))
now = datetime.datetime.now().strftime('%m月%d%H:%M') now = datetime.datetime.now().strftime('%m月%d%H:%M')

View File

@ -3,6 +3,7 @@ import os
import random import random
import re import re
from pathlib import Path
from PIL import Image, ImageDraw, ImageFont from PIL import Image, ImageDraw, ImageFont
from utils import aiorequests from utils import aiorequests
@ -11,6 +12,18 @@ from utils.file_handler import load_image
res_path = os.path.join(os.path.dirname(os.path.dirname(__file__)), 'res') res_path = os.path.join(os.path.dirname(os.path.dirname(__file__)), 'res')
b = load_image(os.path.join(res_path, 'player_card', '角色卡底部.png'))
b_c = load_image(os.path.join(res_path, 'player_card', 'chara_botton.png'))
fetter = []
for i in range(0, 11):
fetter.append(load_image(os.path.join(res_path, 'player_card', f'好感度{i}.png')))
weapon_bg = []
for i in range(1, 6):
weapon_bg.append(load_image(os.path.join(res_path, 'player_card', f'{i}星武器.png')))
constellation = []
for i in range(0, 7):
constellation.append(load_image(os.path.join(res_path, 'player_card', f'命之座{i}.png')))
def get_font(size): def get_font(size):
return ImageFont.truetype(os.path.join(res_path, 'msyh.ttc'), size) return ImageFont.truetype(os.path.join(res_path, 'msyh.ttc'), size)
@ -31,22 +44,18 @@ async def get_chara_card(data):
chara_card = Image.new("RGBA", (226, 313), (255, 255, 255, 255)) chara_card = Image.new("RGBA", (226, 313), (255, 255, 255, 255))
chara_img = load_image(os.path.join(res_path, 'role_card', f'{data["id"]}.png')) chara_img = load_image(os.path.join(res_path, 'role_card', f'{data["id"]}.png'))
chara_card.alpha_composite(chara_img, (0, 0)) chara_card.alpha_composite(chara_img, (0, 0))
b = load_image(os.path.join(res_path, 'player_card', 'chara_botton.png')) chara_card.alpha_composite(b_c, (0, 236))
chara_card.alpha_composite(b, (0, 236))
# 命座 # 命座
if data['name'] != '埃洛伊': if data['name'] != '埃洛伊':
actived_constellation_num = load_image( chara_card.alpha_composite(constellation[data["actived_constellation_num"]], (155, 0))
os.path.join(res_path, 'player_card', f'命之座{data["actived_constellation_num"]}.png'))
chara_card.alpha_composite(actived_constellation_num, (155, 0))
# 好感度 # 好感度
if data['name'] != '旅行者': if data['name'] != '旅行者':
fetter = load_image(os.path.join(res_path, 'player_card', f'好感度{data["fetter"]}.png')) chara_card.alpha_composite(fetter[data["fetter"]], (155, 166))
chara_card.alpha_composite(fetter, (155, 166))
# 武器背景 # 武器背景
weapon_bg = load_image(os.path.join(res_path, 'player_card', f'{data["weapon"]["rarity"]}星武器.png')) chara_card.alpha_composite(weapon_bg[data["weapon"]["rarity"] - 1], (0, 227))
chara_card.alpha_composite(weapon_bg, (0, 227))
# 武器图标 # 武器图标
weapon_icon = await aiorequests.get_img(url=data['weapon']['icon'], size=(63, 63), mode='RGBA') weapon_icon = Path() / 'data' / 'LittlePaimon' / 'res' / 'weapon' / data['weapon']['icon'].split('/')[-1]
weapon_icon = await aiorequests.get_img(url=data['weapon']['icon'], size=(63, 63), mode='RGBA', save_path=weapon_icon)
chara_card.alpha_composite(weapon_icon, (0, 230)) chara_card.alpha_composite(weapon_icon, (0, 230))
# 等级信息 # 等级信息
chara_draw = ImageDraw.Draw(chara_card) chara_draw = ImageDraw.Draw(chara_card)
@ -252,7 +261,6 @@ async def get_chara_card_long(data):
chara_card = Image.new("RGBA", (226, 382), (255, 255, 255, 255)) chara_card = Image.new("RGBA", (226, 382), (255, 255, 255, 255))
chara_img = load_image(os.path.join(res_path, 'role_card', f'{data["id"]}.png')) chara_img = load_image(os.path.join(res_path, 'role_card', f'{data["id"]}.png'))
chara_card.alpha_composite(chara_img, (0, 0)) chara_card.alpha_composite(chara_img, (0, 0))
b = load_image(os.path.join(res_path, 'player_card', '角色卡底部.png'))
chara_card.alpha_composite(b, (0, 282)) chara_card.alpha_composite(b, (0, 282))
# 命座 # 命座
if data['name'] != '埃洛伊': if data['name'] != '埃洛伊':
@ -261,12 +269,11 @@ async def get_chara_card_long(data):
chara_card.alpha_composite(actived_constellation_num, (155, 0)) chara_card.alpha_composite(actived_constellation_num, (155, 0))
# 好感度 # 好感度
if data['name'] != '旅行者': if data['name'] != '旅行者':
fetter = load_image(os.path.join(res_path, 'player_card', f'好感度{data["fetter"]}.png')) chara_card.alpha_composite(fetter[data['fetter']], (155, 166))
chara_card.alpha_composite(fetter, (155, 166))
# 武器背景 # 武器背景
weapon_bg = load_image(os.path.join(res_path, 'player_card', f'{data["weapon"]["rarity"]}星武器.png')) chara_card.alpha_composite(weapon_bg[data["weapon"]["rarity"] - 1], (3, 288))
chara_card.alpha_composite(weapon_bg, (3, 288)) weapon_icon = Path() / 'data' / 'LittlePaimon' / 'res' / 'weapon' / data['weapon']['icon'].split('/')[-1]
weapon_icon = await aiorequests.get_img(url=data['weapon']['icon'], size=(62, 62), mode='RGBA') weapon_icon = await aiorequests.get_img(url=data['weapon']['icon'], size=(62, 62), mode='RGBA', save_path=weapon_icon)
chara_card.alpha_composite(weapon_icon, (3, 291)) chara_card.alpha_composite(weapon_icon, (3, 291))
# 等级信息 # 等级信息
chara_draw = ImageDraw.Draw(chara_card) chara_draw = ImageDraw.Draw(chara_card)
@ -330,7 +337,8 @@ shadow = load_image(os.path.join(res_path, 'other', 'shadow.png'))
async def draw_reli_icon(data): async def draw_reli_icon(data):
base_icon = load_image(os.path.join(res_path, 'other', f'star{data["rarity"]}.png'), size=(80, 80)) base_icon = load_image(os.path.join(res_path, 'other', f'star{data["rarity"]}.png'), size=(80, 80))
icon = await aiorequests.get_img(url=data["icon"], size=(80, 80), mode='RGBA') icon = Path() / 'data' / 'LittlePaimon' / 'res' / 'reli' / data['icon'].split('/')[-1]
icon = await aiorequests.get_img(url=data["icon"], size=(80, 80), mode='RGBA', save_path=icon)
base_icon.alpha_composite(icon, (0, 0)) base_icon.alpha_composite(icon, (0, 0))
base_icon.alpha_composite(shadow, (40, 60)) base_icon.alpha_composite(shadow, (40, 60))
base_icon_draw = ImageDraw.Draw(base_icon) base_icon_draw = ImageDraw.Draw(base_icon)
@ -340,7 +348,8 @@ async def draw_reli_icon(data):
async def draw_const_skill_icon(data, name): async def draw_const_skill_icon(data, name):
base_icon = load_image(os.path.join(res_path, 'other', '命座.png'), size=(65, 65)) base_icon = load_image(os.path.join(res_path, 'other', '命座.png'), size=(65, 65))
icon = await aiorequests.get_img(url=data["icon"], size=(65, 65), mode='RGBA') icon = Path() / 'data' / 'LittlePaimon' / 'res' / 'skill' / data['icon'].split('/')[-1]
icon = await aiorequests.get_img(url=data["icon"], size=(65, 65), mode='RGBA', save_path=icon)
base_icon.alpha_composite(icon, (0, 0)) base_icon.alpha_composite(icon, (0, 0))
if 'is_actived' in data and not data['is_actived']: if 'is_actived' in data and not data['is_actived']:
unlock_icon = load_image(os.path.join(res_path, 'other', '命座未解锁.png'), size=(65, 65)) unlock_icon = load_image(os.path.join(res_path, 'other', '命座未解锁.png'), size=(65, 65))
@ -412,7 +421,8 @@ async def draw_chara_card(data, skill_data, chara_name, uid):
# 武器 # 武器
weapon_bg = load_image(os.path.join(res_path, 'other', f'star{character["weapon"]["rarity"]}.png'), size=(100, 100)) weapon_bg = load_image(os.path.join(res_path, 'other', f'star{character["weapon"]["rarity"]}.png'), size=(100, 100))
weapon_icon = await aiorequests.get_img(url=character['weapon']['icon'], size=(100, 100), mode='RGBA') weapon_icon = Path() / 'data' / 'LittlePaimon' / 'res' / 'weapon' / character['weapon']['icon'].split('/')[-1]
weapon_icon = await aiorequests.get_img(url=character['weapon']['icon'], size=(100, 100), mode='RGBA', save_path=weapon_icon)
bg_img.alpha_composite(weapon_bg, (293, 175)) bg_img.alpha_composite(weapon_bg, (293, 175))
bg_img.alpha_composite(weapon_icon, (293, 175)) bg_img.alpha_composite(weapon_icon, (293, 175))
bg_img.alpha_composite(shadow.resize((50, 25)), (344, 250)) bg_img.alpha_composite(shadow.resize((50, 25)), (344, 250))

View File

@ -66,7 +66,7 @@ async def get_img(url: str,
params: Optional[Dict[str, Any]] = None, params: Optional[Dict[str, Any]] = None,
timeout: Optional[int] = 20, timeout: Optional[int] = 20,
save_path: Optional[Union[str, Path]] = None, save_path: Optional[Union[str, Path]] = None,
size: Optional[Tuple[int, int]] = None, size: Optional[Union[Tuple[int, int], float]] = None,
mode: Optional[str] = None, mode: Optional[str] = None,
crop: Optional[Tuple[int, int, int, int]] = None, crop: Optional[Tuple[int, int, int, int]] = None,
**kwargs) -> Union[str, Image.Image]: **kwargs) -> Union[str, Image.Image]:
@ -83,51 +83,42 @@ async def get_img(url: str,
:param mode: 图片模式为空则不做修改 :param mode: 图片模式为空则不做修改
:param crop: 图片裁剪为空则不做修改 :param crop: 图片裁剪为空则不做修改
""" """
try: if save_path and Path(save_path).exists():
async with httpx.AsyncClient() as client: img = Image.open(save_path)
resp = await client.get(url, else:
headers=headers, try:
params=params, async with httpx.AsyncClient() as client:
timeout=timeout, resp = await client.get(url,
**kwargs) headers=headers,
resp = resp.read() params=params,
if b'error' in resp: timeout=timeout,
return 'No Such File' **kwargs)
img = Image.open(BytesIO(resp)) resp = resp.read()
if size: if b'error' in resp:
img = img.resize(size, Image.ANTIALIAS) return 'No Such File'
if mode: img = Image.open(BytesIO(resp))
img = img.convert(mode) except SSLCertVerificationError:
if crop: async with httpx.AsyncClient() as client:
img = img.crop(crop) resp = await client.get(url.replace('https', 'http'),
if save_path: headers=headers,
if isinstance(save_path, str): params=params,
save_path = Path(save_path) timeout=timeout,
save_path.parent.mkdir(parents=True, exist_ok=True) **kwargs)
img.save(save_path) resp = resp.read()
return img if b'error' in resp:
except SSLCertVerificationError: return 'No Such File'
async with httpx.AsyncClient() as client: img = Image.open(BytesIO(resp))
resp = await client.get(url.replace('https', 'http'), if size:
headers=headers, if isinstance(size, float):
params=params, img = img.resize((int(img.size[0] * size), int(img.size[1] * size)), Image.ANTIALIAS)
timeout=timeout, elif isinstance(size, tuple):
**kwargs) img = img.resize(size, Image.ANTIALIAS)
resp = resp.read() if mode:
if b'error' in resp: img = img.convert(mode)
return 'No Such File' if crop:
img = Image.open(BytesIO(resp)) img = img.crop(crop)
if size: if save_path and not Path(save_path).exists():
img = img.resize(size, Image.ANTIALIAS) save_path = Path(save_path)
if mode: save_path.parent.mkdir(parents=True, exist_ok=True)
img = img.convert(mode) img.save(save_path)
if crop: return img
img = img.crop(crop)
if save_path:
if isinstance(save_path, str):
save_path = Path(save_path)
save_path.parent.mkdir(parents=True, exist_ok=True)
img.save(save_path)
return img
except Exception:
traceback.print_exc()

View File

@ -28,11 +28,20 @@ class MessageBuild:
) -> MessageSegment: ) -> MessageSegment:
if isinstance(img, str) or isinstance(img, Path): if isinstance(img, str) or isinstance(img, Path):
img = load_image(path=img, size=size, mode=mode, crop=crop) img = load_image(path=img, size=size, mode=mode, crop=crop)
else:
if size:
if isinstance(size, float):
img = img.resize((int(img.size[0] * size), int(img.size[1] * size)), Image.ANTIALIAS)
elif isinstance(size, tuple):
img = img.resize(size, Image.ANTIALIAS)
if crop:
img = img.crop(crop)
if mode:
img = img.convert(mode)
bio = BytesIO() bio = BytesIO()
img = img.convert(mode) img = img.convert(mode)
img.save(bio, format='JPEG' if mode == 'RGB' else 'PNG', quality=quality) img.save(bio, format='JPEG' if mode == 'RGB' else 'PNG', quality=quality)
img_b64 = 'base64://' + base64.b64encode(bio.getvalue()).decode() return MessageSegment.image(bio)
return MessageSegment.image(img_b64)
@classmethod @classmethod
async def StaticImage(cls, async def StaticImage(cls,
@ -58,8 +67,7 @@ class MessageBuild:
bio = BytesIO() bio = BytesIO()
img = img.convert(mode) img = img.convert(mode)
img.save(bio, format='JPEG' if mode == 'RGB' else 'PNG', quality=quality) img.save(bio, format='JPEG' if mode == 'RGB' else 'PNG', quality=quality)
img_b64 = 'base64://' + base64.b64encode(bio.getvalue()).decode() return MessageSegment.image(bio)
return MessageSegment.image(img_b64)
@classmethod @classmethod
def Text(cls, text: str) -> MessageSegment: def Text(cls, text: str) -> MessageSegment: