准备工具
通义千问 通义tongyi.ai_你的全能AI助手
通义千问是一个语言大模型,使用简单,提问的字数上限较高
使用通义来分析baseline的代码
step1 代码准备
!pip install simple-aesthetics-predictor
!pip install -v -e data-juicer
!pip uninstall pytorch-lightning -y
!pip install peft lightning pandas torchvision
!pip install -e DiffSynth-Studio
from modelscope.msdatasets import MsDataset
ds = MsDataset.load(
'AI-ModelScope/lowres_anime',
subset_name='default',
split='train',
cache_dir="/mnt/workspace/kolors/data"
)
import json, os
from data_juicer.utils.mm_utils import SpecialTokens
from tqdm import tqdm
os.makedirs("./data/lora_dataset/train", exist_ok=True)
os.makedirs("./data/data-juicer/input", exist_ok=True)
with open("./data/data-juicer/input/metadata.jsonl", "w") as f:
for data_id, data in enumerate(tqdm(ds)):
image = data["image"].convert("RGB")
image.save(f"/mnt/workspace/kolors/data/lora_dataset/train/{data_id}.jpg")
metadata = {"text": "二次元", "image": [f"/mnt/workspace/kolors/data/lora_dataset/train/{data_id}.jpg"]}
f.write(json.dumps(metadata))
f.write("\n")
data_juicer_config = """
# global parameters
project_name: 'data-process'
dataset_path: './data/data-juicer/input/metadata.jsonl' # path to your dataset directory or file
np: 4 # number of subprocess to process your dataset
text_keys: 'text'
image_key: 'image'
image_special_token: '<__dj__image>'
export_path: './data/data-juicer/output/result.jsonl'
# process schedule
# a list of several process operators with their arguments
process:
- image_shape_filter:
min_width: 1024
min_height: 1024
any_or_all: any
- image_aspect_ratio_filter:
min_ratio: 0.5
max_ratio: 2.0
any_or_all: any
"""
with open("data/data-juicer/data_juicer_config.yaml", "w") as file:
file.write(data_juicer_config.strip())
!dj-process --config data/data-juicer/data_juicer_config.yaml
import pandas as pd
import os, json
from PIL import Image
from tqdm import tqdm
texts, file_names = [], []
os.makedirs("./data/data-juicer/output/images", exist_ok=True)
with open("./data/data-juicer/output/result.jsonl", "r") as f:
for line in tqdm(f):
metadata = json.loads(line)
texts.append(metadata["text"])
file_names.append(metadata["image"][0])
df = pd.DataFrame({"text": texts, "file_name": file_names})
df.to_csv("./data/data-juicer/output/result.csv", index=False)
df
from transformers import CLIPProcessor, CLIPModel
import torch
model = CLIPModel.from_pretrained("openai/clip-vit-base-patch32")
processor = CLIPProcessor.from_pretrained("openai/clip-vit-base-patch32")
images = [Image.open(img_path) for img_path in df["file_name"]]
inputs = processor(text=df["text"].tolist(), images=images, return_tensors="pt", padding=True)
outputs = model(**inputs)
logits_per_image = outputs.logits_per_image # this is the image-text similarity score
probs = logits_per_image.softmax(dim=1) # we can take the softmax to get the probabilities
probs
from torch.utils.data import Dataset, DataLoader
class CustomDataset(Dataset):
def __init__(self, df, processor):
self.texts = df["text"].tolist()
self.images = [Image.open(img_path) for img_path in df["file_name"]]
self.processor = processor
def __len__(self):
return len(self.texts)
def __getitem__(self, idx):
inputs = self.processor(text=self.texts[idx], images=self.images[idx], return_tensors="pt", padding=True)
return inputs
dataset = CustomDataset(df, processor)
dataloader = DataLoader(dataset, batch_size=8)
for batch in dataloader:
outputs = model(**batch)
logits_per_image = outputs.logits_per_image
probs = logits_per_image.softmax(dim=1)
print(probs)
import torch
from diffusers import StableDiffusionPipeline
torch.manual_seed(1)
pipe = StableDiffusionPipeline.from_pretrained("CompVis/stable-diffusion-v-1-4", torch_dtype=torch.float16)
pipe = pipe.to("cuda")
prompt = "二次元,一个紫色长发小女孩穿着粉色吊带漏肩连衣裙,在练习室练习唱歌,手持话筒"
negative_prompt = "丑陋、变形、嘈杂、模糊、低对比度"
guidance_scale = 4
num_inference_steps = 50
image = pipe(
prompt=prompt,
negative_prompt=negative_prompt,
guidance_scale=guidance_scale,
num_inference_steps=num_inference_steps,
height=1024,
width=1024,
).images[0]
image.save("example_image.png")
image
from PIL import Image
torch.manual_seed(1)
image = pipe(
prompt="二次元,日系动漫,演唱会的观众席,人山人海,一个紫色短发小女孩穿着粉色吊带漏肩连衣裙坐在演唱会的观众席,舞台上衣着华丽的歌星们在唱歌",
negative_prompt="丑陋、变形、嘈杂、模糊、低对比度",
cfg_scale=4,
num_inference_steps=50, height=1024, width=1024,
)
image.save("1.jpg")
torch.manual_seed(1)
image = pipe(
prompt="二次元,一个紫色短发小女孩穿着粉色吊带漏肩连衣裙坐在演唱会的观众席,露出憧憬的神情",
negative_prompt="丑陋、变形、嘈杂、模糊、低对比度,色情擦边",
cfg_scale=4,
num_inference_steps=50, height=1024, width=1024,
)
image.save("2.jpg")
torch.manual_seed(2)
image = pipe(
prompt="二次元,一个紫色短发小女孩穿着粉色吊带漏肩连衣裙坐在演唱会的观众席,露出憧憬的神情",
negative_prompt="丑陋、变形、嘈杂、模糊、低对比度,色情擦边",
cfg_scale=4,
num_inference_steps=50, height=1024, width=1024,
)
image.save("3.jpg")
torch.manual_seed(5)
image = pipe(
prompt="二次元,一个紫色短发小女孩穿着粉色吊带漏肩连衣裙,对着流星许愿,闭着眼睛,十指交叉,侧面",
negative_prompt="丑陋、变形、嘈杂、模糊、低对比度,扭曲的手指,多余的手指",
cfg_scale=4,
num_inference_steps=50, height=1024, width=1024,
)
image.save("4.jpg")
torch.manual_seed(0)
image = pipe(
prompt="二次元,一个紫色中等长度头发小女孩穿着粉色吊带漏肩连衣裙,在练习室练习唱歌",
negative_prompt="丑陋、变形、嘈杂、模糊、低对比度",
cfg_scale=4,
num_inference_steps=50, height=1024, width=1024,
)
image.save("5.jpg")
torch.manual_seed(1)
image = pipe(
prompt="二次元,一个紫色长发小女孩穿着粉色吊带漏肩连衣裙,在练习室练习唱歌,手持话筒",
negative_prompt="丑陋、变形、嘈杂、模糊、低对比度",
cfg_scale=4,
num_inference_steps=50, height=1024, width=1024,
)
image.save("6.jpg")
torch.manual_seed(7)
image = pipe(
prompt="二次元,紫色长发少女,穿着黑色连衣裙,试衣间,心情忐忑",
negative_prompt="丑陋、变形、嘈杂、模糊、低对比度",
cfg_scale=4,
num_inference_steps=50, height=1024, width=1024,
)
image.save("7.jpg")
torch.manual_seed(0)
image = pipe(
prompt="二次元,紫色长发少女,穿着黑色礼服,连衣裙,在台上唱歌",
negative_prompt="丑陋、变形、嘈杂、模糊、低对比度",
cfg_scale=4,
num_inference_steps=50, height=1024, width=1024,
)
image.save("8.jpg")
import numpy as np
from PIL import Image
images = [np.array(Image.open(f"{i}.jpg")) for i in range(1, 9)]
image = np.concatenate([
np.concatenate(images[0:2], axis=1),
np.concatenate(images[2:4], axis=1),
np.concatenate(images[4:6], axis=1),
np.concatenate(images[6:8], axis=1),
], axis=0)
image = Image.fromarray(image).resize((1024, 2048))
image
step2 对整段代码进行提问
以下是返回的结果:
这段代码主要完成了以下几个任务:
-
安装和卸载依赖库
- 安装
simple-aesthetics-predictor
,data-juicer
,peft
,lightning
,pandas
,torchvision
和DiffSynth-Studio
库。 - 卸载
pytorch-lightning
并重新安装lightning
。
- 安装
-
加载数据集
- 使用 ModelScope 加载名为
AI-ModelScope/lowres_anime
的数据集,并将数据集保存到指定的缓存目录/mnt/workspace/kolors/data
。
- 使用 ModelScope 加载名为
-
数据预处理
- 将数据集中的图像转换为 RGB 格式并保存到文件系统。
- 创建 JSONL 文件来记录每张图片对应的元数据(包括文本描述和图片路径)。
-
使用 Data-Juicer 进行数据过滤
- 配置 Data-Juicer 的 YAML 文件,定义了数据集的处理流程,包括过滤掉不符合尺寸要求或长宽比异常的图片。
- 调用 Data-Juicer 对数据进行处理,输出处理后的结果到 JSONL 文件。
-
整理处理后的数据
- 读取 Data-Juicer 输出的 JSONL 文件,并创建 CSV 文件,其中包含文本描述和图片路径。
- 使用 CLIP 模型计算文本和图片之间的相似度。
-
创建 PyTorch 数据集和数据加载器
- 基于 DataFrame 创建了一个自定义的数据集类
CustomDataset
。 - 使用 DataLoader 从自定义的数据集中加载批次数据。
- 基于 DataFrame 创建了一个自定义的数据集类
-
生成图片
- 使用
StableDiffusionPipeline
生成基于不同提示(prompt)的二次元风格图片。 - 通过不同的种子(seed)和提示语句生成多张图片,并保存这些图片。
- 使用
-
合并图片
- 最后,将生成的多个图片合并成一张大图,并调整大小。
总结起来,整个代码可以分为以下几个部分:
- 环境准备 - 安装必要的库和配置环境。
- 数据加载与处理 - 加载原始数据集,并对其进行预处理和过滤。
- 特征提取与相似度计算 - 利用 CLIP 模型提取特征并计算文本与图片之间的相似度。
- 数据集构建 - 构建 PyTorch 数据集以供后续模型训练使用。
- 图像生成 - 使用 Stable Diffusion 模型根据给定的提示生成新的图像。
- 图像整合 - 合并生成的多张图像为一张大图像。
以上的回答较为笼统,所以需要更加精细的提问
step3 按照在terminal里的分段给的代码分段进行提问
!pip install simple-aesthetics-predictor
!pip install -v -e data-juicer
!pip uninstall pytorch-lightning -y
!pip install peft lightning pandas torchvision
!pip install -e DiffSynth-Studio
这段代码是使用python自带的包管理工具pip安装simple-aesthetics-predictor
, data-juicer
, peft
, lightning
, pandas
, torchvision
和 DiffSynth-Studio
库,并卸载 pytorch-lightning
并重新安装 lightning
Simple-Aesthetics-Predictor是一个用于预测图像美学质量的模型。它可以评估一张图片的艺术美感程度,并给出相应的评分。这通常基于深度学习技术实现,可以用于自动化图片筛选或评价任务。
Data-Juicer是一个高质量文本数据清洗工具,用于处理和清洗大规模文本数据集,以提高数据的质量和一致性。它可以帮助去除重复项、校正拼写错误等,是进行自然语言处理(NLP)任务前的重要预处理步骤。
PEFT (Prompt-Enhanced Fine-Tuning)是一种用于微调大型语言模型的技术,它通过添加额外的参数层来适应下游任务,而不需要修改模型本身的权重。这种方法可以在保持原有模型大小的同时有效地适应新的任务。
Lightning (PyTorch Lightning)是一个用于简化 PyTorch 模型训练和研究过程的库。它提供了一种更简洁的方式来进行深度学习实验,使得用户能够专注于模型的设计和调整,而不是复杂的训练循环和设置。
Pandas是一个强大的数据分析库,提供了高性能、易用的数据结构和数据分析工具。它非常适合处理表格数据,并且广泛应用于数据清洗、转换、分析等领域。
TorchVision是 PyTorch 的一个子项目,专注于计算机视觉任务。它包含了许多常用的预训练模型、数据集以及图像变换工具,可以大大简化计算机视觉应用的开发过程。
from modelscope.msdatasets import MsDataset
ds = MsDataset.load(
'AI-ModelScope/lowres_anime',
subset_name='default',
split='train',
cache_dir="/mnt/workspace/kolors/data"
)
第一行代码是从 ModelScope 库的 msdatasets
模块中导入了 MsDataset
类。ModelScope 是阿里云推出的一个模型开放平台,提供了大量的预训练模型和数据集。
接下来的代码
'AI-ModelScope/lowres_anime'
: 这里指定了要加载的数据集名称。在 ModelScope 中,数据集通常按照 组织名/数据集名
的格式命名。
subset_name='default'
: 指定了数据集的子集名称。这里使用 'default'
表示加载默认子集,如果数据集有多个子集,则可以通过这个参数选择不同的子集。
split='train'
: 指定了要加载的数据集分割部分。通常数据集会被分为训练集 ('train'
)、验证集 ('validation'
) 和测试集 ('test'
) 等。这里加载的是训练集部分。
cache_dir="/mnt/workspace/kolors/data"
: 指定了数据集缓存的位置。加载的数据集会缓存到这个目录下,以便后续快速访问。这里的数据集将会被缓存到 /mnt/workspace/kolors/data
目录中。
ds
是一个 MsDataset
实例,它包含了加载的数据集。你可以使用这个实例来访问数据集中的数据。
import json, os
from data_juicer.utils.mm_utils import SpecialTokens
from tqdm import tqdm
os.makedirs("./data/lora_dataset/train", exist_ok=True)
os.makedirs("./data/data-juicer/input", exist_ok=True)
with open("./data/data-juicer/input/metadata.jsonl", "w") as f:
for data_id, data in enumerate(tqdm(ds)):
image = data["image"].convert("RGB")
image.save(f"/mnt/workspace/kolors/data/lora_dataset/train/{data_id}.jpg")
metadata = {"text": "二次元", "image": [f"/mnt/workspace/kolors/data/lora_dataset/train/{data_id}.jpg"]}
f.write(json.dumps(metadata))
f.write("\n")
前三行导入需要的模块
json
: 用于处理 JSON 格式的文件。os
: 提供了一组用于与操作系统交互的功能。SpecialTokens
: 这个类可能来自data-juicer
库,但在这个上下文中似乎没有直接使用。tqdm
: 一个进度条库,用于显示循环进度。
接下来的两行创建两个目录:./data/lora_dataset/train
和 ./data/data-juicer/input
。exist_ok=True
表示如果目录已经存在,则不会引发异常。
剩下的代码
打开一个名为 metadata.jsonl
的文件,用于写入元数据信息。
使用 tqdm
包装 ds
数据集,显示一个进度条,跟踪数据处理进度。
对于 ds
中的每一个数据点 data
:
- 从数据点中获取图像,并将其转换为 RGB 格式。
- 将图像保存到
/mnt/workspace/kolors/data/lora_dataset/train/
目录下,文件名为{data_id}.jpg
。 - 创建一个字典
metadata
,其中包含一个键"text"
,其值为字符串"二次元"
,以及一个键"image"
,其值是一个列表,列表中包含保存的图像路径。 - 将
metadata
字典序列化为 JSON 格式,并写入到文件中。 - 每个元数据条目后面添加换行符
\n
,以便每个条目都在新的一行开始。
data_juicer_config = """
# global parameters
project_name: 'data-process'
dataset_path: './data/data-juicer/input/metadata.jsonl' # path to your dataset directory or file
np: 4 # number of subprocess to process your dataset
text_keys: 'text'
image_key: 'image'
image_special_token: '<__dj__image>'
export_path: './data/data-juicer/output/result.jsonl'
# process schedule
# a list of several process operators with their arguments
process:
- image_shape_filter:
min_width: 1024
min_height: 1024
any_or_all: any
- image_aspect_ratio_filter:
min_ratio: 0.5
max_ratio: 2.0
any_or_all: any
"""
with open("data/data-juicer/data_juicer_config.yaml", "w") as file:
file.write(data_juicer_config.strip())
!dj-process --config data/data-juicer/data_juicer_config.yaml
""""""中的内容是定义配置内容
project_name
: 定义项目名称。dataset_path
: 指定要处理的数据集文件的路径。np
: 指定用于数据处理的子进程数。text_keys
: 指定元数据中包含文本信息的键。image_key
: 指定元数据中包含图像路径的键。image_special_token
: 如果数据集中包含图像,可以用这个特殊标记替换图像路径。export_path
: 指定处理后的数据输出文件的路径。process
: 定义了一系列的数据处理操作。例如,这里定义了过滤图像尺寸和长宽比的操作。
打开文件并将配置内容写入到 data/data-juicer/data_juicer_config.yaml
文件中。
使用 dj-process
命令行工具运行数据处理,这里通过 !
前缀在 Jupyter Notebook 或类似的环境中运行系统命令。
--config
: 指定要使用的配置文件路径。
import pandas as pd
import os, json
from PIL import Image
from tqdm import tqdm
texts, file_names = [], []
os.makedirs("./data/lora_dataset_processed/train", exist_ok=True)
with open("./data/data-juicer/output/result.jsonl", "r") as file:
for data_id, data in enumerate(tqdm(file.readlines())):
data = json.loads(data)
text = data["text"]
texts.append(text)
image = Image.open(data["image"][0])
image_path = f"./data/lora_dataset_processed/train/{data_id}.jpg"
image.save(image_path)
file_names.append(f"{data_id}.jpg")
data_frame = pd.DataFrame()
data_frame["file_name"] = file_names
data_frame["text"] = texts
data_frame.to_csv("./data/lora_dataset_processed/train/metadata.csv", index=False, encoding="utf-8-sig")
data_frame
截止到os.makedirs,在前面的代码块已经有了注释,这里不再赘述
剩余代码
打开处理后的数据集文件 ./data/data-juicer/output/result.jsonl
。
使用 tqdm
包装 file.readlines()
来显示一个进度条,跟踪数据处理进度。
对于文件中的每一行数据:
- 将每一行数据解析为 JSON 对象。
- 从 JSON 对象中提取文本描述,并将其添加到
texts
列表中。 - 从 JSON 对象中提取图像路径,使用
PIL.Image
打开图像。 - 将图像保存到
./data/lora_dataset_processed/train/
目录下,文件名为{data_id}.jpg
。 - 将图像文件名添加到
file_names
列表中。 - 创建一个空的
pandas
DataFrame。
将 file_names
和 texts
列表作为列添加到 DataFrame 中。
将 DataFrame 保存到 CSV 文件 ./data/lora_dataset_processed/train/metadata.csv
中。index=False
表示不保存索引列,encoding="utf-8-sig"
设置编码格式。
最后一行 data_frame
将 DataFrame 输出到控制台,用于检查 DataFrame 的内容。
from diffsynth import download_models
download_models(["Kolors", "SDXL-vae-fp16-fix"])
下载 diffsynth
库中 Kolors
和 SDXL-vae-fp16-fix
模型。
!python DiffSynth-Studio/examples/train/kolors/train_kolors_lora.py -h
运行 train_kolors_lora.py
脚本,并通过 -h
参数请求帮助信息
import os
cmd = """
python DiffSynth-Studio/examples/train/kolors/train_kolors_lora.py \
--pretrained_unet_path models/kolors/Kolors/unet/diffusion_pytorch_model.safetensors \
--pretrained_text_encoder_path models/kolors/Kolors/text_encoder \
--pretrained_fp16_vae_path models/sdxl-vae-fp16-fix/diffusion_pytorch_model.safetensors \
--lora_rank 16 \
--lora_alpha 4.0 \
--dataset_path data/lora_dataset_processed \
--output_path ./models \
--max_epochs 1 \
--center_crop \
--use_gradient_checkpointing \
--precision "16-mixed"
""".strip()
os.system(cmd)
定义一个多行字符串 cmd
,它包含了要执行的shell命令。由于字符串跨越多行,所以在每行结尾使用了 \
符号来进行转义,表示下一行是当前行的延续。最后 .strip()
方法用于移除字符串两端的空白字符。
python DiffSynth-Studio/examples/train/kolors/train_kolors_lora.py
: 运行名为train_kolors_lora.py
的Python脚本。--pretrained_unet_path models/kolors/Kolors/unet/diffusion_pytorch_model.safetensors
: 指定预训练的 U-Net 模型路径。--pretrained_text_encoder_path models/kolors/Kolors/text_encoder
: 指定预训练的文本编码器路径。--pretrained_fp16_vae_path models/sdxl-vae-fp16-fix/diffusion_pytorch_model.safetensors
: 指定预训练的 VAE (变分自动编码器) 路径。--lora_rank 16
: 设置 LoRA 的秩(rank)为 16。--lora_alpha 4.0
: 设置 LoRA 的 alpha 参数为 4.0。--dataset_path data/lora_dataset_processed
: 指定数据集的路径。--output_path ./models
: 指定输出模型的保存路径。--max_epochs 1
: 设置最大训练周期数为 1。--center_crop
: 启用中心裁剪。--use_gradient_checkpointing
: 启用梯度检查点(gradient checkpointing),以节省内存。--precision "16-mixed"
: 设置混合精度训练为 FP16。
使用 os.system()
函数执行 cmd
字符串中的命令。这会启动一个新的子进程来执行命令,并等待命令完成。
from diffsynth import ModelManager, SDXLImagePipeline
from peft import LoraConfig, inject_adapter_in_model
import torch
"""
定义一个load_lora函数,该函数接收一个模型 model,LoRA 的秩 lora_rank,LoRA 的 alpha 参数 lora_alpha,以及 LoRA 模型的路径 lora_path。
创建一个 LoraConfig 实例,配置 LoRA 的参数。
使用 inject_adapter_in_model 函数将 LoRA 适配器注入到模型中。
加载 LoRA 模型的状态字典,并将其加载到模型中。
返回注入了 LoRA 适配器的模型。
"""
def load_lora(model, lora_rank, lora_alpha, lora_path):
lora_config = LoraConfig(
r=lora_rank,
lora_alpha=lora_alpha,
init_lora_weights="gaussian",
target_modules=["to_q", "to_k", "to_v", "to_out"],
)
model = inject_adapter_in_model(lora_config, model)
state_dict = torch.load(lora_path, map_location="cpu")
model.load_state_dict(state_dict, strict=False)
return model
# Load models
"""
使用 ModelManager 加载模型,指定使用半精度浮点数 torch.float16,设备为 GPU (cuda)。
指定要加载的模型文件路径列表,包括文本编码器、U-Net 和 VAE 模型。
从 model_manager 创建一个 SDXLImagePipeline 实例。
"""
model_manager = ModelManager(torch_dtype=torch.float16, device="cuda",
file_path_list=[
"models/kolors/Kolors/text_encoder",
"models/kolors/Kolors/unet/diffusion_pytorch_model.safetensors",
"models/kolors/Kolors/vae/diffusion_pytorch_model.safetensors"
])
pipe = SDXLImagePipeline.from_model_manager(model_manager)
# Load LoRA
"""
调用 load_lora 函数,将 LoRA 适配器注入到 pipe.unet 中。
指定 LoRA 的秩为 16,alpha 参数为 2.0,以及 LoRA 模型的路径。
"""
pipe.unet = load_lora(
pipe.unet,
lora_rank=16, # This parameter should be consistent with that in your training script.
lora_alpha=2.0, # lora_alpha can control the weight of LoRA.
lora_path="models/lightning_logs/version_0/checkpoints/epoch=0-step=500.ckpt"
)
torch.manual_seed(0)
image = pipe(
prompt="二次元,紫色长发少女,穿着黑色礼服,连衣裙,在台上唱歌",
negative_prompt="丑陋、变形、嘈杂、模糊、低对比度",
cfg_scale=4,
num_inference_steps=50, height=1024, width=1024,
)
image.save("8.jpg")
第一行代码设置 PyTorch 的随机种子为 0
,确保每次运行代码时都能得到相同的随机结果。这对于实验的可重复性非常重要。
pipe
是一个预先配置好的图像生成管道,它通常基于深度学习模型如扩散模型(diffusion models)。prompt
参数指定了生成图像的描述:“二次元,紫色长发少女,穿着黑色礼服,连衣裙,在台上唱歌”。这个描述会引导模型生成相应的图像。negative_prompt
参数指定了不希望出现在图像中的元素,例如“丑陋、变形、嘈杂、模糊、低对比度”,这有助于避免生成这些特征。cfg_scale
参数控制了条件生成(conditional generation)的强度,值越大,生成的图像越接近prompt
描述的内容。num_inference_steps
设置了生成过程中迭代步骤的数量。更多的步骤通常意味着更精细的结果,但也需要更多的时间。height
和width
参数指定了输出图像的尺寸,这里为 1024x1024 像素。
最后一行代码将生成的图像保存为名为 "8.jpg" 的 JPEG 文件。
import numpy as np
from PIL import Image
images = [np.array(Image.open(f"{i}.jpg")) for i in range(1, 9)]
image = np.concatenate([
np.concatenate(images[0:2], axis=1),
np.concatenate(images[2:4], axis=1),
np.concatenate(images[4:6], axis=1),
np.concatenate(images[6:8], axis=1),
], axis=0)
image = Image.fromarray(image).resize((1024, 2048))
image
- 使用列表推导式 (
list comprehension
) 来循环读取从1.jpg
到8.jpg
的图像文件。 - 对于每一个图像文件,使用
Image.open()
函数打开图像。 - 使用
np.array()
将每个图像转换为 NumPy 数组,以便于后续处理。 - 使用
np.concatenate()
函数按顺序合并图像。 - 首先,将每两个图像水平拼接在一起 (
axis=1
),形成四个条带。 - 然后,将这四个条带垂直拼接在一起 (
axis=0
),形成最终的大图像。 - 使用
Image.fromarray()
将 NumPy 数组转换回 Pillow 图像对象。 - 使用
resize()
方法调整图像的尺寸到 1024x2048 像素。