Datawhale X 魔搭 AI夏令营 第四期-AIGC方向Task2笔记

baseline的流程图

Baseline代码

!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

主体框架工作流

  1. 环境准备和依赖安装
    • 使用pip命令安装所需的Python库,包括simple-aesthetics-predictordata-juicerpytorch-lightningpeftlightningpandastorchvisionDiffSynth-Studio
  2. 数据集加载
    • modelscope.msdatasets加载名为AI-ModelScope/lowres_anime的数据集,并指定子集和分割类型。
  3. 数据预处理
    • 创建保存图片和元数据的目录。
    • 遍历数据集,将图片保存为JPEG格式,并生成包含文本和图片路径的元数据文件。
  4. 配置数据处理
    • 定义data_juicer的配置文件,包括项目名称、数据集路径、进程数、文本和图片键、特殊标记以及导出路径。
    • 写入配置文件并使用dj-process命令行工具处理数据。
  5. 数据处理结果分析
    • 读取处理后的数据,提取文本和文件名,并将它们保存到CSV文件中。
  6. 模型加载和推理
    • 加载CLIP模型和处理器,对图片和文本进行编码,获取图像-文本相似度得分。
  7. 自定义数据集和数据加载器
    • 定义自定义数据集类CustomDataset,用于封装处理后的图片和文本数据。
    • 创建数据加载器DataLoader,用于批量加载数据。
  8. 模型训练/推理循环
    • 遍历数据加载器中的批次,进行模型推理,获取每个批次的图像-文本相似度得分。
  9. 生成图像
    • 使用Stable Diffusion模型根据给定的提示生成图像,并保存到文件。
  10. 图像拼接
  • 将生成的多张图像拼接成一张长图,并调整大小。

逐行代码解释

  1. 安装所需的Python包
!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
  • simple-aesthetics-predictor:安装一个简单的美学预测器。
  • data-juicer:安装数据处理工具包,使用可编辑模式安装(-e)。
  • 卸载已有的pytorch-lightning版本。
  • 安装peft(参数高效微调)、lightning(新的PyTorch Lightning库)、pandas(数据处理)和torchvision(计算机视觉工具包)。
  • 安装DiffSynth-Studio,DiffSynth-Studio是一个开源的扩散引擎,专为实现图片和视频的风格转换而设计。
  1. 加载数据集
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类。
  • 使用MsDataset.load方法加载名为'AI-ModelScope/lowres_anime'的数据集,指定使用默认子集('default'),数据划分为训练集('train'),并将数据缓存到指定目录。
  1. 准备数据处理
import json, os
from data_juicer.utils.mm_utils import SpecialTokens
from tqdm import tqdm
  • 导入必要的库:json(处理JSON数据)、os(文件系统操作)、tqdm(进度条显示)。
  • data_juicer的工具模块中导入SpecialTokens(用于处理特殊标记)。
  1. 创建必要的目录
os.makedirs("./data/lora_dataset/train", exist_ok=True)
os.makedirs("./data/data-juicer/input", exist_ok=True)
  • 创建用于存储训练图像和输入元数据的目录,如果目录已存在则不报错。
  1. 处理并保存图像和元数据
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")
  • 打开一个名为metadata.jsonl的文件,用于写入每个图像的元数据。
  • 遍历数据集中的每一条数据:
    • 将图像转换为RGB格式。
    • 将图像保存到指定目录,文件名为数据的索引。
    • 创建元数据字典,包含文本描述"二次元"和图像路径。
    • 将元数据以JSON格式写入文件,每条记录占一行。
  1. 配置data_juicer
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())
  • 定义data_juicer的配置,内容包括:
    • 全局参数,如项目名称、数据集路径、并行处理的子进程数量等。
    • 指定文本和图像的键,以及特殊的图像标记。
    • 输出路径。
    • 处理流程,包括图像尺寸过滤(最小宽度和高度为1024)和图像长宽比过滤(比例在0.5到2.0之间)。
  • 将配置写入data_juicer_config.yaml文件。
  1. 运行data_juicer处理数据
!dj-process --config data/data-juicer/data_juicer_config.yaml
  • 使用data_juicer的命令行工具,根据配置文件处理数据集。
  1. 读取处理后的数据并创建DataFrame
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
  • 导入必要的库:pandas(数据处理)、osjsonPIL.Image(图像处理)和tqdm
  • 创建用于存储图像的输出目录。
  • 打开处理后的结果文件result.jsonl,逐行读取:
    • 解析每行的JSON数据,提取文本和图像路径,分别存储到列表中。
  • 使用pandas创建一个包含文本和图像文件名的DataFrame。
  • 将DataFrame保存为CSV文件。
  • 显示DataFrame的内容。
  1. 使用CLIP模型计算图像和文本的相似度
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
  • transformers库中导入CLIPProcessorCLIPModel,以及torch
  • 加载预训练的CLIP模型和处理器。
  • 打开DataFrame中所有的图像文件。
  • 使用处理器将文本和图像进行预处理,返回PyTorch张量,并进行适当的填充。
  • 将预处理后的数据输入模型,获得输出。
  • 提取每个图像与文本的相似度分数(logits_per_image)。
  • 对相似度分数应用softmax,得到概率分布。
  • 显示计算得到的概率。
  1. 定义自定义数据集和数据加载器
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)
  • torch.utils.data中导入DatasetDataLoader
  • 定义CustomDataset类:
    • 在初始化时,存储文本列表、打开的图像列表和处理器。
    • 实现__len__方法,返回数据集的大小。
    • 实现__getitem__方法,对指定索引的数据进行处理,并返回处理后的输入。
  • 创建数据集和数据加载器,设置批次大小为8。
  • 遍历数据加载器中的每个批次:
    • 将批次数据输入模型,计算相似度分数和概率,并打印结果。
  1. 使用Stable Diffusion生成图像
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
  • 导入torchStableDiffusionPipeline
  • 设置随机种子为1,确保生成的图像可重复。
  • 加载预训练的Stable Diffusion模型,并将其移动到GPU上,设置为半精度浮点数以节省显存。
  • 定义正向和负向提示词,以及指导尺度和推理步数。
  • 使用提示词生成图像,指定图像的高度和宽度为1024像素。
  • 保存生成的图像为example_image.png,并显示图像。
  1. 生成更多图像并保存
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")
  • 使用不同的随机种子和提示词,生成多张图像,并分别保存为1.jpg8.jpg
  1. 拼接生成的图像
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
  • 导入numpyPIL.Image
  • 打开之前生成的8张图像,将其转换为numpy数组。
  • 将图像按照行和列拼接,形成一个大的图像网格:
    • 每行拼接两张图像,共四行。
  • 将拼接后的图像调整大小为1024x2048像素。
  • 显示最终的拼接图像。

利用AI生成剧本(以kimi为例)

提示词1:你是一个古风漫画家,我们现在要做一个古风仙侠漫,现在要写一个故事简介
提示词2:根据故事简介,写一个故事大纲
提示词3:展开第四章:修仙大会,并把漫画具体的场景描绘出来
提示词4:

你是一个文生图专家,我们现在要做一个实战项目,就是要根据刚才展开的修仙大会内容编排一个文生图,
由8张场景图片生成,你需要输出每张图片的生图提示词
生图提示词要求
1、风格为古风
2、根据场景确定是使用全身还是上半身
3、人物描述
4、场景描述
5、做啥事情

例子:
古风,水墨画,一个黑色长发少女,坐在教室里,盯着黑板,深思,上半身,红色长裙
修仙大会的提示词(AI生成)

AI生成的提示词微调后整理如下:

编号场景正向提示词负向提示词结果
1大会前夕古风,水墨画,市集繁华,一个笑容甜美的少女(笑笑),背影转头,穿梭在人群中,好奇地打量着各种摊位,全身,身着简单朴素的布衣。丑陋,变形,嘈杂,模糊,低对比度image.png
2报名处的混乱古风,水墨画,报名处人潮汹涌,一个表情轻松的少女(笑笑)站在人群中,用幽默化解紧张气氛,上半身,身着带有些许修仙特色的服饰。丑陋,变形,嘈杂,模糊,低对比度image.png
3大会开幕式古风,水墨画,宏伟的主会场,一个少女(笑笑)和其他修仙者一起,仰头观看开幕式,全身,身着节日盛装。丑陋,变形,嘈杂,模糊,低对比度image.png
4比武大会古风,水墨画,比武台上,一个少女(笑笑)正在进行灵力控制比赛,全身,身着便于活动的修仙服饰,脸上带着自信的微笑。丑陋,变形,嘈杂,模糊,低对比度image.png
5夜晚的庆祝古风,水墨画,夜市灯火通明,一个少女(笑笑)和她的队友们围坐一桌,品尝美食,享受烟火表演,上半身,身着轻便的夜行衣。丑陋,变形,嘈杂,模糊,低对比度image.png
6意外的挑战古风,水墨画,夜市角落,一个少女(笑笑)面对神秘人物,准备接受挑战,全身,身着带有一些神秘符号的服饰,表情认真。丑陋,变形,嘈杂,模糊,低对比度image.png
7智慧的较量古风,水墨画,僻静角落,一个少女(笑笑)和神秘人物进行灵力与智慧的较量,上半身,身着代表智慧的服饰,手中拿着棋子或类似道具。丑陋,变形,嘈杂,模糊,低对比度image.png
8大会的落幕古风,水墨画,主会场,一个少女(笑笑)和她的队友们站在领奖台上,接受“最具潜力新人奖”,全身,身着带有荣誉象征的服饰,脸上洋溢着喜悦和自豪。丑陋,变形,嘈杂,模糊,低对比度image.png
  • 10
    点赞
  • 6
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值