Initialize this repository
This commit is contained in:
commit
c1c50bbcbe
7
.gitignore
vendored
Normal file
7
.gitignore
vendored
Normal file
@ -0,0 +1,7 @@
|
||||
pic/*
|
||||
sucai/*
|
||||
archived/pic/*
|
||||
archived/sucai/*
|
||||
*.jpg
|
||||
*.png
|
||||
*.ttf
|
||||
76
MatrixGenerator/__init__.py
Normal file
76
MatrixGenerator/__init__.py
Normal 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
69
README.md
Normal 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)
|
||||
```
|
||||
BIN
archived/__pycache__/op.cpython-39.pyc
Normal file
BIN
archived/__pycache__/op.cpython-39.pyc
Normal file
Binary file not shown.
92
archived/final.py
Normal file
92
archived/final.py
Normal 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
100
archived/op.py
Normal 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
70
archived/test.py
Normal 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
19
main.py
Normal 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
1
requriement.txt
Normal file
@ -0,0 +1 @@
|
||||
pillow==11.3.0
|
||||
Loading…
x
Reference in New Issue
Block a user