Initialize this repository

This commit is contained in:
Starlight-0208 2025-07-21 18:47:59 +08:00
commit c1c50bbcbe
9 changed files with 434 additions and 0 deletions

7
.gitignore vendored Normal file
View File

@ -0,0 +1,7 @@
pic/*
sucai/*
archived/pic/*
archived/sucai/*
*.jpg
*.png
*.ttf

View File

@ -0,0 +1,76 @@
from PIL import Image, ImageDraw, ImageFile, ImageFont
from typing import Union, Type
class PicItem:
def __init__(self, pic, label: str):
self.pic = Image.open(pic) if isinstance(pic, str) else 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)

69
README.md Normal file
View File

@ -0,0 +1,69 @@
## 项目简介
我也不知道这个东西是什么,乱搞的。
## 使用方法
### 安装依赖
首先安装一个图像处理包
```sh
pip install pillow==11.3.0
```
或者直接使用`requirement.txt`
```sh
pip install -r requirement.txt
```
### 项目使用
`MatrixGenerator`就是模块本身。
使用的时候直接导入`PictureMatrixGenerater``PicItem`
```python
from MatrixGenerator import PictureMatrixGenerater, PicItem
```
前者是生成器,后者是图片项。
图片项包含两个属性:
- `pic`:图片路径或者图片对象
- `label`:图片标签
图片支持两种类型:
- 图片路径:直接传入图片路径
- 图片对象:直接传入图片对象(`PIL.Image`
图片对象导入方式大概是这样的:
```python
from PIL import Image
Image.open("/path/to/open.jpg")
```
使用`PictureMatrixGenerater`生成图片矩阵,首先需要传入背景图片路径、图片矩阵大小、图片项大小、图片项间距、图片项数量、字体间距、字体路径、字体大小。
- `background`:背景图片路径(必须是路径,不支持图片对象)
- `picSize`:图片矩阵大小
- `itemSize`:图片项大小
- `margin`:图片项间距
- `itemNum`:图片项数量
- `fontMargin`:字体间距
- `font`:字体路径
- `fontSize`:字体大小
例如:
```python
generator = PictureMatrixGenerater(
background="./pic/background.jpg",
picSize=(1920, 1080),
itemSize=(200, 200),
margin=20,
itemNum=4,
fontMargin=20,
font="./font/STKAITI.TTF",
fontSize=30
)
```
生成图片矩阵之前,需要一个图片项列表:
```python
picList = [
PicItem("./pic/1.jpg", "1"),
PicItem("./pic/2.jpg", "2"),
PicItem("./pic/3.jpg", "3"),
PicItem("./pic/4.jpg", "4"),
]
```
这里提供一个小技巧:
```python
picList = [PicItem(pic, str(i)) for i, pic in enumerate(picList)]
```
之后调用`generate`方法生成图片矩阵:
```python
generator.generate(picList)
```

Binary file not shown.

92
archived/final.py Normal file
View File

@ -0,0 +1,92 @@
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)

100
archived/op.py Normal file
View File

@ -0,0 +1,100 @@
import os
from PIL import Image, ImageDraw, ImageFile, ImageFont
picSize = (1920, 1080)
itemSize = (320, 320)
margin = 50
itemNum = 10
fontSize = 26
fontMargin = 20
actualSize = (itemSize[0], itemSize[1] + fontMargin + int(fontSize * 1.5))
font = ImageFont.truetype("./font/HarmonyOS_Sans_SC_Medium.ttf", fontSize)
num = len(os.listdir('./sucai'))
# num = 10
# 计算某行的实际宽度
vSize = num * (itemSize[0] + num - 1) if num <= 5 else 5 * (itemSize[0] + num - 1)
# 计算左右边距以确保当前行居中展示
mSize = (picSize[0] - vSize) / 2 - margin * 2
# 如果只有一行则计算居中如果两行则设置为200
marginTop = int(picSize[1] / 2) - int(itemSize[1] / 2) if num <= 5 else 200
nextYStart = marginTop + actualSize[1] + 50 if num > 5 else -1
nextViewSize = (num % 5) * (itemSize[0] + num - 1) if num < 10 else 5 * (itemSize[0] + num - 1)
nextMarginSize = (picSize[0] - nextViewSize) / 2 - margin * 2
print(nextMarginSize)
# colNum = int(num / 5) + 1
# # print(colNum)
def cutPic(img: ImageFile) -> Image:
# 等比放小50%
img = img.resize((int(img.width * 0.5), int(img.height * 0.5)))
left = (img.width - itemSize[0])/2
top = (img.height - itemSize[1])/2
right = (img.width + itemSize[0])/2
bottom = (img.height + itemSize[1])/2
return img.crop((left, top, right, bottom))
def drawComponent(pic: ImageFile, text: str, startX: int = 0, startY: int = 0) -> Image:
components = Image.new('RGBA', actualSize, color=(0, 0, 0, 0))
components.paste(cutPic(pic), (0, 0))
draw = ImageDraw.Draw(components)
text_bbox = draw.textbbox((0, 0), text, font=font)
# 计算文本宽度(文本框实际宽度)
text_width = text_bbox[2] - text_bbox[0]
# 计算文本居中
item_center_x = (startX + (itemSize[0] / 2))
text_y = startY + itemSize[1] + fontMargin
draw.text((item_center_x - (text_width / 2), text_y), text, fill=(255, 255, 255), font=font)
return components
backImage = Image.new('RGB', picSize, color=(255, 255, 255))
backImage.paste(Image.open('./pic/luoxiaohei.jpg'), (0, 0))
# backImage.paste(drawComponent(Image.open('./sucai/1.jpg'), "我真的很长很长很长的文本", 0, 0), (200, 200))
for idx, img in enumerate(os.listdir('./sucai')):
calc_X = int((itemSize[0] + margin) * idx + mSize) if idx < 5 else int((itemSize[0] + margin) * (idx % 5) + nextMarginSize)
calc_Y = marginTop if idx < 5 else nextYStart
print((calc_X, calc_Y))
component = drawComponent(Image.open('./sucai/' + img), img, 0, 0)
backImage.paste(component, (calc_X, calc_Y), mask=component)
# 写入结果
with open('./final.png', 'wb') as f:
backImage.save(f)
exit(0)
for idx, file in enumerate(os.listdir('./sucai')):
# 将图片修剪为指定大小
img = Image.open('./sucai/' + file)
# img = img.resize(itemSize)
# img.thumbnail(itemSize)
# 再居中裁剪
img = cutPic(img)
# 粘贴到背景图
backImage.paste(img, (int((itemSize[0] + margin) * idx + mSize) , marginTop))
# 在图片下面嵌字
draw = ImageDraw.Draw(backImage)
font = ImageFont.load_default()
# 使用24px大小的字体
# 绘制文本
# 计算文本宽度
text = "我真的很长很长很长的文本"
text_bbox = draw.textbbox((0, 0), text, font=font)
text_width = text_bbox[2] - text_bbox[0]
# 计算图片项的中心位置
item_center_x = ((itemSize[0] + margin) * idx + mSize) + (itemSize[0] / 2)
# 计算文本的起始x坐标使其居中
text_x = item_center_x - (text_width / 2)
text_y = marginTop + itemSize[1] + fontMargin
draw.text((text_x, text_y), text, fill=(255, 255, 255), font=font)
# 写入结果
with open('./luoxiaohei.jpg', 'wb') as f:
backImage.save(f)

70
archived/test.py Normal file
View File

@ -0,0 +1,70 @@
from PIL import Image, ImageDraw, ImageFont
import math
def generate_image_grid(images, titles, output_path, small_width=200, small_height=200, caption_height=30, max_per_row=5):
num_images = len(images)
# 参数检查
if num_images > 10:
raise ValueError("最多十张图片")
if num_images != len(titles):
raise ValueError("图片和标题数量必须一致")
# 计算行数
rows = math.ceil(num_images / max_per_row)
# 创建大图
total_width = max_per_row * small_width
total_height = rows * (small_height + caption_height)
grid_image = Image.new('RGB', (total_width, total_height), color=(255, 255, 255))
draw = ImageDraw.Draw(grid_image)
# 加载字体(尝试使用系统字体,否则使用默认字体)
try:
font = ImageFont.truetype("arial.ttf", 16)
except:
font = ImageFont.load_default()
# 拼接图片并添加标题
for i in range(num_images):
row = i // max_per_row
col = i % max_per_row
x = col * small_width
y = row * (small_height + caption_height)
# 调整图片大小并粘贴
img = images[i].resize((small_width, small_height))
grid_image.paste(img, (x, y))
# 绘制标题
caption = titles[i]
text_width, text_height = font.getsize(caption)
text_x = x + (small_width - text_width) // 2
text_y = y + small_height + (caption_height - text_height) // 2
draw.text((text_x, text_y), caption, fill=(0, 0, 0), font=font)
# 保存结果
grid_image.save(output_path)
def create_test_images(n, size=(200, 200)):
colors = ['red', 'green', 'blue', 'yellow', 'cyan',
'magenta', 'black', 'white', 'orange', 'purple']
images = []
for i in range(n):
img = Image.new('RGB', size, color=colors[i % len(colors)])
draw = ImageDraw.Draw(img)
draw.text((10, 10), f"Image {i+1}", fill=(255, 255, 255), font=ImageFont.load_default())
images.append(img)
return images
# 示例调用
if __name__ == "__main__":
# 生成 7 张测试图片
test_images = create_test_images(7)
# 设置标题
titles = [f"Title {i+1}" for i in range(7)]
# 拼接图片并保存
generate_image_grid(test_images, titles, 'grid_output.png')

19
main.py Normal file
View File

@ -0,0 +1,19 @@
import os
from PIL import Image
from MatrixGenerator import PicItem, PictureMatrixGenerater
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/")]
Sol = generator.generate(picList)
# 写入结果
with open('./finalc.png', 'wb') as f:
Sol.save(f)

1
requriement.txt Normal file
View File

@ -0,0 +1 @@
pillow==11.3.0