92 lines
4.1 KiB
Python
92 lines
4.1 KiB
Python
import os
|
||
from PIL import Image, ImageDraw, ImageFile, ImageFont
|
||
|
||
class PicItem:
|
||
def __init__(self, pic: ImageFile, label: str):
|
||
self.pic = pic
|
||
self.label = label
|
||
|
||
class PictureMatrixGenerater():
|
||
def __init__(self, background: str, picSize: tuple, itemSize: tuple, margin: int, itemNum: int, fontMargin: int, font: str, fontSize: int):
|
||
self.backImage = Image.open(background)
|
||
self.picSize = picSize
|
||
self.itemSize = itemSize
|
||
self.margin = margin
|
||
self.itemNum = itemNum
|
||
self.fontSize = fontSize
|
||
self.fontMargin = fontMargin
|
||
self.actualSize = (itemSize[0], itemSize[1] + fontMargin + int(fontSize * 1.5))
|
||
self.font = ImageFont.truetype(font, fontSize)
|
||
|
||
def cutPic(self, img: ImageFile) -> Image:
|
||
# 等比放小50%
|
||
img = img.resize((int(img.width * 0.5), int(img.height * 0.5)))
|
||
left = (img.width - self.itemSize[0])/2
|
||
top = (img.height - self.itemSize[1])/2
|
||
right = (img.width + self.itemSize[0])/2
|
||
bottom = (img.height + self.itemSize[1])/2
|
||
return img.crop((left, top, right, bottom))
|
||
|
||
|
||
def drawComponent(self, pic: ImageFile, text: str, startX: int = 0, startY: int = 0) -> Image:
|
||
components = Image.new('RGBA', self.actualSize, color=(0, 0, 0, 0))
|
||
components.paste(self.cutPic(pic), (0, 0))
|
||
draw = ImageDraw.Draw(components)
|
||
text_bbox = draw.textbbox((0, 0), text, font=self.font)
|
||
# 计算文本宽度(文本框实际宽度)
|
||
text_width = text_bbox[2] - text_bbox[0]
|
||
# 计算文本居中
|
||
item_center_x = (startX + (self.itemSize[0] / 2))
|
||
text_y = startY + self.itemSize[1] + self.fontMargin
|
||
draw.text((item_center_x - (text_width / 2), text_y), text, fill=(255, 255, 255), font=self.font)
|
||
return components
|
||
|
||
def generate(self, picList: list[PicItem]) -> Image:
|
||
backImage = Image.new('RGB', self.picSize, color=(255, 255, 255))
|
||
backImage.paste(self.backImage, (0, 0))
|
||
|
||
num: int = len(picList)
|
||
# 计算某行的实际宽度
|
||
vSize = num * (self.actualSize[0] + num - 1) if num <= 5 else 5 * (self.actualSize[0] + num - 1)
|
||
# 计算左右边距以确保当前行居中展示
|
||
mSize = (self.picSize[0] - vSize) / 2 - self.margin * 2
|
||
# 如果只有一行,则计算居中,如果两行,则设置为200
|
||
marginTop = int(self.picSize[1] / 2) - int(self.itemSize[1] / 2) if num <= 5 else 200
|
||
nextYStart = marginTop + self.actualSize[1] + 50 if num > 5 else -1
|
||
nextViewSize = (num % 5) * (self.itemSize[0] + num - 1) if num < 10 else 5 * (self.itemSize[0] + num - 1)
|
||
nextMarginSize = (self.picSize[0] - nextViewSize) / 2 - self.margin * 2
|
||
|
||
for idx, img in enumerate(picList):
|
||
calc_X = int((self.itemSize[0] + self.margin) * idx + mSize) if idx < 5 else int((self.itemSize[0] + self.margin) * (idx % 5) + nextMarginSize)
|
||
calc_Y = marginTop if idx < 5 else nextYStart
|
||
# print((calc_X, calc_Y))
|
||
component = self.drawComponent(img.pic, img.label, 0, 0)
|
||
backImage.paste(component, (calc_X, calc_Y), mask=component)
|
||
return backImage
|
||
|
||
def config(self, picSize: tuple, itemSize: tuple, margin: int, itemNum: int, fontMargin: int, font: str, fontSize: int):
|
||
self.picSize = picSize
|
||
self.itemSize = itemSize
|
||
self.margin = margin
|
||
self.itemNum = itemNum
|
||
self.fontSize = fontSize
|
||
self.fontMargin = fontMargin
|
||
self.actualSize = (itemSize[0], itemSize[1] + fontMargin + int(fontSize * 1.5))
|
||
self.font = ImageFont.truetype(font, fontSize)
|
||
|
||
generator = PictureMatrixGenerater(
|
||
background="./pic/background.jpg",
|
||
picSize=(1920, 1080),
|
||
itemSize=(320, 320),
|
||
margin=50,
|
||
itemNum=10,
|
||
fontMargin=20,
|
||
font="./font/HarmonyOS_Sans_SC_Medium.ttf",
|
||
fontSize=26
|
||
)
|
||
picList = [PicItem(Image.open('./sucai/' + i), i) for i in os.listdir("./sucai/")]
|
||
print(picList)
|
||
Sol = generator.generate(picList)
|
||
# 写入结果
|
||
with open('./finalc.png', 'wb') as f:
|
||
Sol.save(f) |