diffusers-源码解析-三十二-

diffusers 源码解析(三十二)

.\diffusers\pipelines\kandinsky\pipeline_kandinsky_inpaint.py

# Copyright 2024 The HuggingFace Team. All rights reserved.
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
#     http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.

from copy import deepcopy  # 从 copy 模块导入 deepcopy 函数,用于深拷贝对象
from typing import Callable, List, Optional, Union  # 导入类型提示功能,用于函数签名

import numpy as np  # 导入 numpy 库,通常用于数组和矩阵操作
import PIL.Image  # 导入 PIL.Image 模块,用于图像处理
import torch  # 导入 PyTorch 库,深度学习框架
import torch.nn.functional as F  # 导入 PyTorch 的功能性神经网络模块
from packaging import version  # 导入 version 模块,用于版本管理
from PIL import Image  # 导入 PIL.Image 模块,用于图像操作
from transformers import (  # 从 transformers 库导入以下组件
    XLMRobertaTokenizer,  # 导入 XLM-RoBERTa 的分词器
)

from ... import __version__  # 导入当前模块的版本信息
from ...models import UNet2DConditionModel, VQModel  # 导入模型类
from ...schedulers import DDIMScheduler  # 导入 DDIM 调度器
from ...utils import (  # 从 utils 模块导入以下工具
    logging,  # 导入日志模块
    replace_example_docstring,  # 导入用于替换示例文档字符串的函数
)
from ...utils.torch_utils import randn_tensor  # 从 torch_utils 导入 randn_tensor 函数
from ..pipeline_utils import DiffusionPipeline, ImagePipelineOutput  # 导入扩散管道和图像管道输出类
from .text_encoder import MultilingualCLIP  # 导入多语言 CLIP 文本编码器

logger = logging.get_logger(__name__)  # 创建日志记录器,用于记录模块日志,名称为当前模块名
# pylint: disable=invalid-name  # 禁用 pylint 对无效名称的警告

EXAMPLE_DOC_STRING = """  # 定义示例文档字符串,提供代码示例
    Examples:
        ```py
        >>> from diffusers import KandinskyInpaintPipeline, KandinskyPriorPipeline  # 从 diffusers 导入管道类
        >>> from diffusers.utils import load_image  # 从 utils 导入图像加载函数
        >>> import torch  # 导入 PyTorch
        >>> import numpy as np  # 导入 numpy

        >>> pipe_prior = KandinskyPriorPipeline.from_pretrained(  # 从预训练模型创建管道
        ...     "kandinsky-community/kandinsky-2-1-prior", torch_dtype=torch.float16  # 指定模型和数据类型
        ... )
        >>> pipe_prior.to("cuda")  # 将管道移动到 CUDA 设备

        >>> prompt = "a hat"  # 定义生成图像的提示词
        >>> image_emb, zero_image_emb = pipe_prior(prompt, return_dict=False)  # 获取图像嵌入和零图像嵌入

        >>> pipe = KandinskyInpaintPipeline.from_pretrained(  # 从预训练模型创建图像修复管道
        ...     "kandinsky-community/kandinsky-2-1-inpaint", torch_dtype=torch.float16  # 指定模型和数据类型
        ... )
        >>> pipe.to("cuda")  # 将管道移动到 CUDA 设备

        >>> init_image = load_image(  # 加载初始图像
        ...     "https://huggingface.co/datasets/hf-internal-testing/diffusers-images/resolve/main"  # 图像的 URL
        ...     "/kandinsky/cat.png"  # 图像文件名
        ... )

        >>> mask = np.zeros((768, 768), dtype=np.float32)  # 创建一个全零的掩膜
        >>> mask[:250, 250:-250] = 1  # 在掩膜中设置特定区域为 1

        >>> out = pipe(  # 调用管道进行图像生成
        ...     prompt,  # 提示词
        ...     image=init_image,  # 初始图像
        ...     mask_image=mask,  # 掩膜图像
        ...     image_embeds=image_emb,  # 图像嵌入
        ...     negative_image_embeds=zero_image_emb,  # 负图像嵌入
        ...     height=768,  # 输出图像的高度
        ...     width=768,  # 输出图像的宽度
        ...     num_inference_steps=50,  # 推理步骤数
        ... )

        >>> image = out.images[0]  # 获取输出的第一张图像
        >>> image.save("cat_with_hat.png")  # 保存生成的图像
        ```py
"""


def get_new_h_w(h, w, scale_factor=8):  # 定义函数,计算新的高和宽
    new_h = h // scale_factor**2  # 计算新的高度,使用整除
    if h % scale_factor**2 != 0:  # 如果原高度不能被 scale_factor^2 整除
        new_h += 1  # 高度加一
    new_w = w // scale_factor**2  # 计算新的宽度,使用整除
    if w % scale_factor**2 != 0:  # 如果原宽度不能被 scale_factor^2 整除
        new_w += 1  # 宽度加一
    return new_h * scale_factor, new_w * scale_factor  # 返回新的高和宽,均乘以 scale_factor
# 准备掩码
def prepare_mask(masks):
    # 初始化一个空列表以存储准备好的掩码
    prepared_masks = []
    # 遍历所有输入掩码
    for mask in masks:
        # 深拷贝当前掩码以避免修改原始数据
        old_mask = deepcopy(mask)
        # 遍历掩码的每一行
        for i in range(mask.shape[1]):
            #
    # 检查 image 是否为 torch.Tensor 类型
        if isinstance(image, torch.Tensor):
            # 如果 mask 不是 torch.Tensor,则抛出类型错误
            if not isinstance(mask, torch.Tensor):
                raise TypeError(f"`image` is a torch.Tensor but `mask` (type: {type(mask)} is not")
    
            # 如果 image 是单张图像(3 个维度)
            if image.ndim == 3:
                # 断言图像的通道数为 3,确保形状符合要求
                assert image.shape[0] == 3, "Image outside a batch should be of shape (3, H, W)"
                # 添加一个维度以表示批量
                image = image.unsqueeze(0)
    
            # 如果 mask 是单张图像(2 个维度),则添加通道维度
            if mask.ndim == 2:
                mask = mask.unsqueeze(0).unsqueeze(0)
    
            # 检查 mask 的维度
            if mask.ndim == 3:
                # 如果 mask 是单个批量图像,且没有通道维度
                if mask.shape[0] == 1:
                    mask = mask.unsqueeze(0)
    
                # 如果 mask 是批量图像且没有通道维度
                else:
                    mask = mask.unsqueeze(1)
    
            # 断言 image 和 mask 都必须有 4 个维度
            assert image.ndim == 4 and mask.ndim == 4, "Image and Mask must have 4 dimensions"
            # 断言 image 和 mask 的空间维度相同
            assert image.shape[-2:] == mask.shape[-2:], "Image and Mask must have the same spatial dimensions"
            # 断言 image 和 mask 的批量大小相同
            assert image.shape[0] == mask.shape[0], "Image and Mask must have the same batch size"
    
            # 检查 image 的值是否在 [-1, 1] 范围内
            if image.min() < -1 or image.max() > 1:
                raise ValueError("Image should be in [-1, 1] range")
    
            # 检查 mask 的值是否在 [0, 1] 范围内
            if mask.min() < 0 or mask.max() > 1:
                raise ValueError("Mask should be in [0, 1] range")
    
            # 将 mask 二值化
            mask[mask < 0.5] = 0
            mask[mask >= 0.5] = 1
    
            # 将 image 转换为 float32 类型
            image = image.to(dtype=torch.float32)
        # 如果 mask 是 torch.Tensor,但 image 不是,则抛出类型错误
        elif isinstance(mask, torch.Tensor):
            raise TypeError(f"`mask` is a torch.Tensor but `image` (type: {type(image)} is not")
    else:
        # 处理图像
        if isinstance(image, (PIL.Image.Image, np.ndarray)):
            # 将单个图像转换为列表形式
            image = [image]

        if isinstance(image, list) and isinstance(image[0], PIL.Image.Image):
            # 根据传入的高度和宽度调整所有图像的大小
            image = [i.resize((width, height), resample=Image.BICUBIC, reducing_gap=1) for i in image]
            # 将每个图像转换为 RGB 数组,并添加一个维度
            image = [np.array(i.convert("RGB"))[None, :] for i in image]
            # 将多个图像数组在第一个维度上连接起来
            image = np.concatenate(image, axis=0)
        elif isinstance(image, list) and isinstance(image[0], np.ndarray):
            # 将多个 ndarray 在第一个维度上连接起来
            image = np.concatenate([i[None, :] for i in image], axis=0)

        # 调整图像维度顺序,从 (N, H, W, C) 到 (N, C, H, W)
        image = image.transpose(0, 3, 1, 2)
        # 将 numpy 数组转换为 PyTorch 张量,并进行归一化处理
        image = torch.from_numpy(image).to(dtype=torch.float32) / 127.5 - 1.0

        # 处理掩码
        if isinstance(mask, (PIL.Image.Image, np.ndarray)):
            # 将单个掩码转换为列表形式
            mask = [mask]

        if isinstance(mask, list) and isinstance(mask[0], PIL.Image.Image):
            # 根据传入的高度和宽度调整所有掩码的大小
            mask = [i.resize((width, height), resample=PIL.Image.LANCZOS) for i in mask]
            # 将每个掩码转换为灰度数组,并添加两个维度
            mask = np.concatenate([np.array(m.convert("L"))[None, None, :] for m in mask], axis=0)
            # 将掩码数组的值归一化到 [0, 1]
            mask = mask.astype(np.float32) / 255.0
        elif isinstance(mask, list) and isinstance(mask[0], np.ndarray):
            # 将多个 ndarray 在第一个维度上连接起来
            mask = np.concatenate([m[None, None, :] for m in mask], axis=0)

        # 将掩码进行二值化处理,低于 0.5 设为 0,高于等于 0.5 设为 1
        mask[mask < 0.5] = 0
        mask[mask >= 0.5] = 1
        # 将 numpy 数组转换为 PyTorch 张量
        mask = torch.from_numpy(mask)

    # 将掩码取反
    mask = 1 - mask

    # 返回掩码和图像
    return mask, image
# 定义Kandinsky图像修复管道,继承自DiffusionPipeline
class KandinskyInpaintPipeline(DiffusionPipeline):
    """
    使用Kandinsky2.1进行文本引导的图像修复的管道

    该模型继承自[`DiffusionPipeline`]。请查看超类文档以获取库为所有管道实现的通用方法
    (例如下载或保存,运行在特定设备上等)。

    参数:
        text_encoder ([`MultilingualCLIP`]):
            冻结的文本编码器。
        tokenizer ([`XLMRobertaTokenizer`]):
            类的分词器
        scheduler ([`DDIMScheduler`]):
            用于与`unet`结合生成图像潜在表示的调度器。
        unet ([`UNet2DConditionModel`]):
            用于去噪图像嵌入的条件U-Net架构。
        movq ([`VQModel`]):
            MoVQ图像编码器和解码器
    """

    # 定义模型CPU卸载顺序
    model_cpu_offload_seq = "text_encoder->unet->movq"

    def __init__(
        self,
        text_encoder: MultilingualCLIP,
        movq: VQModel,
        tokenizer: XLMRobertaTokenizer,
        unet: UNet2DConditionModel,
        scheduler: DDIMScheduler,
    ):
        # 初始化父类
        super().__init__()

        # 注册模型组件
        self.register_modules(
            text_encoder=text_encoder,
            movq=movq,
            tokenizer=tokenizer,
            unet=unet,
            scheduler=scheduler,
        )
        # 计算MoVQ的缩放因子
        self.movq_scale_factor = 2 ** (len(self.movq.config.block_out_channels) - 1)
        # 初始化警告标志
        self._warn_has_been_called = False

    # 从diffusers.pipelines.unclip.pipeline_unclip.UnCLIPPipeline复制的方法,用于准备潜在表示
    def prepare_latents(self, shape, dtype, device, generator, latents, scheduler):
        # 如果没有给定潜在表示,则随机生成
        if latents is None:
            latents = randn_tensor(shape, generator=generator, device=device, dtype=dtype)
        else:
            # 检查潜在表示的形状是否符合预期
            if latents.shape != shape:
                raise ValueError(f"Unexpected latents shape, got {latents.shape}, expected {shape}")
            # 将潜在表示转移到指定设备
            latents = latents.to(device)

        # 使用调度器的初始噪声标准差调整潜在表示
        latents = latents * scheduler.init_noise_sigma
        # 返回处理后的潜在表示
        return latents

    def _encode_prompt(
        self,
        prompt,
        device,
        num_images_per_prompt,
        do_classifier_free_guidance,
        negative_prompt=None,
    ):
    # 定义一个可调用的类方法
        def __call__(
            self,
            # 接受一个字符串或字符串列表作为提示
            prompt: Union[str, List[str]],
            # 接受一个图像张量或 PIL 图像作为输入图像
            image: Union[torch.Tensor, PIL.Image.Image],
            # 接受一个掩膜图像,可以是张量、PIL 图像或 NumPy 数组
            mask_image: Union[torch.Tensor, PIL.Image.Image, np.ndarray],
            # 接受一个图像嵌入的张量
            image_embeds: torch.Tensor,
            # 接受一个负图像嵌入的张量
            negative_image_embeds: torch.Tensor,
            # 可选参数,接受一个字符串或字符串列表作为负提示
            negative_prompt: Optional[Union[str, List[str]]] = None,
            # 设置输出图像的高度,默认为 512
            height: int = 512,
            # 设置输出图像的宽度,默认为 512
            width: int = 512,
            # 设置推理步骤的数量,默认为 100
            num_inference_steps: int = 100,
            # 设置引导比例,默认为 4.0
            guidance_scale: float = 4.0,
            # 设置每个提示生成的图像数量,默认为 1
            num_images_per_prompt: int = 1,
            # 可选参数,接受一个随机数生成器或生成器列表
            generator: Optional[Union[torch.Generator, List[torch.Generator]]] = None,
            # 可选参数,接受潜在空间的张量
            latents: Optional[torch.Tensor] = None,
            # 可选参数,设置输出类型,默认为 "pil"
            output_type: Optional[str] = "pil",
            # 可选参数,接受一个回调函数,用于处理图像生成的步骤
            callback: Optional[Callable[[int, int, torch.Tensor], None]] = None,
            # 设置回调函数调用的步数,默认为 1
            callback_steps: int = 1,
            # 设置返回字典的布尔值,默认为 True
            return_dict: bool = True,

.\diffusers\pipelines\kandinsky\pipeline_kandinsky_prior.py

# 版权信息,表明该代码的版权归 HuggingFace 团队所有
# 
# 根据 Apache 许可证,版本 2.0(“许可证”),
# 除非符合许可证,否则您不得使用此文件。
# 您可以在以下网址获得许可证的副本:
# 
#     http://www.apache.org/licenses/LICENSE-2.0
# 
# 除非适用法律要求或书面同意,软件按“现状”分发,
# 不提供任何形式的担保或条件,无论是明示或暗示。
# 查看许可证以了解特定语言适用的权限和
# 限制。

# 从 dataclasses 模块导入 dataclass 装饰器
from dataclasses import dataclass
# 从 typing 模块导入 List, Optional 和 Union 类型
from typing import List, Optional, Union

# 导入 numpy 库,并简化为 np
import numpy as np
# 导入 PIL 库中的 Image 模块
import PIL.Image
# 导入 PyTorch 库
import torch
# 从 transformers 库中导入 CLIP 相关的处理器和模型
from transformers import CLIPImageProcessor, CLIPTextModelWithProjection, CLIPTokenizer, CLIPVisionModelWithProjection

# 从父级模块导入 PriorTransformer 类
from ...models import PriorTransformer
# 从父级模块导入 UnCLIPScheduler 类
from ...schedulers import UnCLIPScheduler
# 从父级模块导入多个工具函数
from ...utils import (
    BaseOutput,  # 基本输出类
    logging,     # 日志模块
    replace_example_docstring,  # 替换示例文档字符串的函数
)
# 从 torch_utils 模块导入随机张量生成函数
from ...utils.torch_utils import randn_tensor
# 从 pipeline_utils 模块导入 DiffusionPipeline 类
from ..pipeline_utils import DiffusionPipeline

# 获取当前模块的日志记录器
logger = logging.get_logger(__name__)  # pylint: disable=invalid-name

# 示例文档字符串,包含用法示例
EXAMPLE_DOC_STRING = """
    Examples:
        ```py
        >>> from diffusers import KandinskyPipeline, KandinskyPriorPipeline
        >>> import torch

        >>> pipe_prior = KandinskyPriorPipeline.from_pretrained("kandinsky-community/kandinsky-2-1-prior")
        >>> pipe_prior.to("cuda")

        >>> prompt = "red cat, 4k photo"
        >>> out = pipe_prior(prompt)
        >>> image_emb = out.image_embeds
        >>> negative_image_emb = out.negative_image_embeds

        >>> pipe = KandinskyPipeline.from_pretrained("kandinsky-community/kandinsky-2-1")
        >>> pipe.to("cuda")

        >>> image = pipe(
        ...     prompt,
        ...     image_embeds=image_emb,
        ...     negative_image_embeds=negative_image_emb,
        ...     height=768,
        ...     width=768,
        ...     num_inference_steps=100,
        ... ).images

        >>> image[0].save("cat.png")
        ```py
"""

# 另一个示例文档字符串,未填充内容
EXAMPLE_INTERPOLATE_DOC_STRING = """


# 示例用法
Examples:
    ```py
    # 从 diffusers 库中导入 KandinskyPriorPipeline 和 KandinskyPipeline
    >>> from diffusers import KandinskyPriorPipeline, KandinskyPipeline
    # 从 diffusers.utils 导入 load_image 函数
    >>> from diffusers.utils import load_image
    # 导入 PIL 库
    >>> import PIL

    # 导入 torch 库
    >>> import torch
    # 从 torchvision 导入 transforms 模块
    >>> from torchvision import transforms

    # 加载预训练的 KandinskyPriorPipeline,设置数据类型为 float16
    >>> pipe_prior = KandinskyPriorPipeline.from_pretrained(
    ...     "kandinsky-community/kandinsky-2-1-prior", torch_dtype=torch.float16
    ... )
    # 将管道移动到 GPU 设备
    >>> pipe_prior.to("cuda")

    # 从指定 URL 加载第一张图像
    >>> img1 = load_image(
    ...     "https://huggingface.co/datasets/hf-internal-testing/diffusers-images/resolve/main"
    ...     "/kandinsky/cat.png"
    ... )

    # 从指定 URL 加载第二张图像
    >>> img2 = load_image(
    ...     "https://huggingface.co/datasets/hf-internal-testing/diffusers-images/resolve/main"
    ...     "/kandinsky/starry_night.jpeg"
    ... )

    # 定义图像和描述文本的列表
    >>> images_texts = ["a cat", img1, img2]
    # 定义每个图像的权重
    >>> weights = [0.3, 0.3, 0.4]
    # 使用管道进行插值,返回图像嵌入和零图像嵌入
    >>> image_emb, zero_image_emb = pipe_prior.interpolate(images_texts, weights)

    # 加载预训练的 KandinskyPipeline,设置数据类型为 float16
    >>> pipe = KandinskyPipeline.from_pretrained("kandinsky-community/kandinsky-2-1", torch_dtype=torch.float16)
    # 将管道移动到 GPU 设备
    >>> pipe.to("cuda")

    # 使用管道生成图像,设置相关参数
    >>> image = pipe(
    ...     "",
    ...     image_embeds=image_emb,
    ...     negative_image_embeds=zero_image_emb,
    ...     height=768,
    ...     width=768,
    ...     num_inference_steps=150,
    ... ).images[0]

    # 将生成的图像保存为文件 "starry_cat.png"
    >>> image.save("starry_cat.png")

"""

文档字符串,描述该模块的用途

@dataclass

装饰器,定义数据类以便更方便地管理属性

class KandinskyPriorPipelineOutput(BaseOutput):
"""
Output class for KandinskyPriorPipeline.
# 文档字符串,描述 KandinskyPriorPipeline 的输出类

Args:
    image_embeds (`torch.Tensor`)
        clip image embeddings for text prompt
    # 描述图像嵌入的参数,供文本提示使用
    negative_image_embeds (`List[PIL.Image.Image]` or `np.ndarray`)
        clip image embeddings for unconditional tokens
    # 描述用于无条件标记的图像嵌入参数
"""

# 定义输出类的属性,包括图像嵌入和负图像嵌入
image_embeds: Union[torch.Tensor, np.ndarray]
# 图像嵌入属性,可以是 PyTorch 张量或 NumPy 数组
negative_image_embeds: Union[torch.Tensor, np.ndarray]
# 负图像嵌入属性,也可以是 PyTorch 张量或 NumPy 数组

class KandinskyPriorPipeline(DiffusionPipeline):
"""
Pipeline for generating image prior for Kandinsky
# 文档字符串,描述生成 Kandinsky 图像先验的管道

This model inherits from [`DiffusionPipeline`]. Check the superclass documentation for the generic methods the
# 说明该模型继承自 DiffusionPipeline,参考超类文档以了解通用方法
library implements for all the pipelines (such as downloading or saving, running on a particular device, etc.)

Args:
    prior ([`PriorTransformer`]):
        The canonical unCLIP prior to approximate the image embedding from the text embedding.
    # 先验变换器,用于从文本嵌入近似图像嵌入
    image_encoder ([`CLIPVisionModelWithProjection`]):
        Frozen image-encoder.
    # 冻结的图像编码器
    text_encoder ([`CLIPTextModelWithProjection`]):
        Frozen text-encoder.
    # 冻结的文本编码器
    tokenizer (`CLIPTokenizer`):
        Tokenizer of class
        [CLIPTokenizer](https://huggingface.co/docs/transformers/v4.21.0/en/model_doc/clip#transformers.CLIPTokenizer).
    # CLIP 词元化器
    scheduler ([`UnCLIPScheduler`]):
        A scheduler to be used in combination with `prior` to generate image embedding.
    # 调度器,用于结合先验生成图像嵌入
"""

# 定义不进行 CPU 卸载的模块
_exclude_from_cpu_offload = ["prior"]
# 定义模型 CPU 卸载序列
model_cpu_offload_seq = "text_encoder->prior"

def __init__(
    self,
    prior: PriorTransformer,
    image_encoder: CLIPVisionModelWithProjection,
    text_encoder: CLIPTextModelWithProjection,
    tokenizer: CLIPTokenizer,
    scheduler: UnCLIPScheduler,
    image_processor: CLIPImageProcessor,
):
    # 初始化方法,接受多个参数用于构建管道
    super().__init__()
    # 调用超类的初始化方法

    # 注册模块,方便后续管理
    self.register_modules(
        prior=prior,
        text_encoder=text_encoder,
        tokenizer=tokenizer,
        scheduler=scheduler,
        image_encoder=image_encoder,
        image_processor=image_processor,
    )

@torch.no_grad()
# 修饰函数,表示在此函数中不需要计算梯度
@replace_example_docstring(EXAMPLE_INTERPOLATE_DOC_STRING)
# 替换示例文档字符串,提供相关的例子
def interpolate(
    self,
    images_and_prompts: List[Union[str, PIL.Image.Image, torch.Tensor]],
    # 输入图像和提示的列表,可以是字符串、PIL 图像或 PyTorch 张量
    weights: List[float],
    # 对应每个提示的权重列表
    num_images_per_prompt: int = 1,
    # 每个提示生成的图像数量,默认为 1
    num_inference_steps: int = 25,
    # 推理步骤的数量,默认为 25
    generator: Optional[Union[torch.Generator, List[torch.Generator]]] = None,
    # 随机数生成器,用于控制生成的随机性
    latents: Optional[torch.Tensor] = None,
    # 潜在变量,可选,默认为 None
    negative_prior_prompt: Optional[str] = None,
    # 负先验提示,默认为 None
    negative_prompt: str = "",
    # 负提示,默认为空字符串
    guidance_scale: float = 4.0,
    # 引导缩放因子,影响生成图像的风格
    device=None,
    # 指定设备,默认为 None
# Copied from diffusers.pipelines.unclip.pipeline_unclip.UnCLIPPipeline.prepare_latents
# 准备潜在变量,依据输入形状、数据类型、设备等参数
def prepare_latents(self, shape, dtype, device, generator, latents, scheduler):
    # 如果潜在变量为空,则生成随机张量
    if latents is None:
        latents = randn_tensor(shape, generator=generator, device=device, dtype=dtype)
    else:
        # 如果潜在变量的形状不匹配,则引发错误
        if latents.shape != shape:
            raise ValueError(f"Unexpected latents shape, got {latents.shape}, expected {shape}")
        # 将潜在变量转移到指定设备
        latents = latents.to(device)

    # 将潜在变量乘以调度器的初始噪声标准差
    latents = latents * scheduler.init_noise_sigma
    # 返回调整后的潜在变量
    return latents

# 获取零嵌入,用于生成图像的初始状态
def get_zero_embed(self, batch_size=1, device=None):
    # 如果未指定设备,则使用默认设备
    device = device or self.device
    # 创建一个零张量,形状为图像大小
    zero_img = torch.zeros(1, 3, self.image_encoder.config.image_size, self.image_encoder.config.image_size).to(
        device=device, dtype=self.image_encoder.dtype
    )
    # 通过图像编码器获取零图像的嵌入表示
    zero_image_emb = self.image_encoder(zero_img)["image_embeds"]
    # 将零图像嵌入重复指定的批大小
    zero_image_emb = zero_image_emb.repeat(batch_size, 1)
    # 返回零图像嵌入
    return zero_image_emb

# 编码提示的私有方法,处理输入提示和设备
def _encode_prompt(
    self,
    prompt,
    device,
    num_images_per_prompt,
    do_classifier_free_guidance,
    negative_prompt=None,
):
    # 在后续逻辑中会使用此方法进行提示编码
    @torch.no_grad()
    @replace_example_docstring(EXAMPLE_DOC_STRING)
    # 定义调用方法,处理输入提示
    def __call__(
        self,
        prompt: Union[str, List[str]],
        negative_prompt: Optional[Union[str, List[str]]] = None,
        num_images_per_prompt: int = 1,
        num_inference_steps: int = 25,
        generator: Optional[Union[torch.Generator, List[torch.Generator]]] = None,
        latents: Optional[torch.Tensor] = None,
        guidance_scale: float = 4.0,
        output_type: Optional[str] = "pt",
        return_dict: bool = True,

# `.\diffusers\pipelines\kandinsky\text_encoder.py`

```py
# 导入 PyTorch 库
import torch
# 从 transformers 库导入预训练模型类和配置类
from transformers import PreTrainedModel, XLMRobertaConfig, XLMRobertaModel


# 定义 MCLIPConfig 类,继承自 XLMRobertaConfig
class MCLIPConfig(XLMRobertaConfig):
    # 设置模型类型为 "M-CLIP"
    model_type = "M-CLIP"

    # 初始化方法,接受变换器和图像维度大小的参数
    def __init__(self, transformerDimSize=1024, imageDimSize=768, **kwargs):
        # 将变换器维度大小赋值给实例变量
        self.transformerDimensions = transformerDimSize
        # 将图像维度大小赋值给实例变量
        self.numDims = imageDimSize
        # 调用父类的初始化方法
        super().__init__(**kwargs)


# 定义 MultilingualCLIP 类,继承自 PreTrainedModel
class MultilingualCLIP(PreTrainedModel):
    # 指定配置类为 MCLIPConfig
    config_class = MCLIPConfig

    # 初始化方法,接受配置和其他参数
    def __init__(self, config, *args, **kwargs):
        # 调用父类的初始化方法
        super().__init__(config, *args, **kwargs)
        # 创建 XLMRobertaModel 实例,使用传入的配置
        self.transformer = XLMRobertaModel(config)
        # 创建线性变换层,输入特征为变换器维度,输出特征为图像维度
        self.LinearTransformation = torch.nn.Linear(
            in_features=config.transformerDimensions, out_features=config.numDims
        )

    # 定义前向传播方法,接受输入 ID 和注意力掩码
    def forward(self, input_ids, attention_mask):
        # 获取变换器的嵌入表示,使用输入 ID 和注意力掩码
        embs = self.transformer(input_ids=input_ids, attention_mask=attention_mask)[0]
        # 根据注意力掩码计算加权嵌入表示
        embs2 = (embs * attention_mask.unsqueeze(2)).sum(dim=1) / attention_mask.sum(dim=1)[:, None]
        # 返回线性变换后的嵌入表示和原始嵌入表示
        return self.LinearTransformation(embs2), embs

.\diffusers\pipelines\kandinsky\__init__.py

# 从 typing 模块导入 TYPE_CHECKING,用于类型检查
from typing import TYPE_CHECKING

# 从 utils 模块导入相关的工具和常量
from ...utils import (
    DIFFUSERS_SLOW_IMPORT,  # 导入用于标识慢速导入的常量
    OptionalDependencyNotAvailable,  # 导入表示可选依赖不可用的异常
    _LazyModule,  # 导入懒加载模块的类
    get_objects_from_module,  # 导入从模块获取对象的函数
    is_torch_available,  # 导入检查 Torch 库可用性的函数
    is_transformers_available,  # 导入检查 Transformers 库可用性的函数
)

# 用于存储虚拟对象的字典
_dummy_objects = {}
# 用于存储模块导入结构的字典
_import_structure = {}

try:
    # 检查 Transformers 和 Torch 是否都可用
    if not (is_transformers_available() and is_torch_available()):
        # 如果不可用,则抛出可选依赖不可用异常
        raise OptionalDependencyNotAvailable()
# 捕获可选依赖不可用异常
except OptionalDependencyNotAvailable:
    # 从 utils 模块导入虚拟的 Torch 和 Transformers 对象,忽略 F403 警告
    from ...utils import dummy_torch_and_transformers_objects  # noqa F403

    # 更新 _dummy_objects 字典,获取虚拟对象
    _dummy_objects.update(get_objects_from_module(dummy_torch_and_transformers_objects))
else:
    # 如果可用,则更新导入结构字典,添加各种管道的导入
    _import_structure["pipeline_kandinsky"] = ["KandinskyPipeline"]
    _import_structure["pipeline_kandinsky_combined"] = [
        "KandinskyCombinedPipeline",
        "KandinskyImg2ImgCombinedPipeline",
        "KandinskyInpaintCombinedPipeline",
    ]
    _import_structure["pipeline_kandinsky_img2img"] = ["KandinskyImg2ImgPipeline"]
    _import_structure["pipeline_kandinsky_inpaint"] = ["KandinskyInpaintPipeline"]
    _import_structure["pipeline_kandinsky_prior"] = ["KandinskyPriorPipeline", "KandinskyPriorPipelineOutput"]
    _import_structure["text_encoder"] = ["MultilingualCLIP"]

# 如果在类型检查模式或慢速导入模式下执行
if TYPE_CHECKING or DIFFUSERS_SLOW_IMPORT:
    try:
        # 再次检查 Transformers 和 Torch 是否都可用
        if not (is_transformers_available() and is_torch_available()):
            # 如果不可用,则抛出可选依赖不可用异常
            raise OptionalDependencyNotAvailable()
    # 捕获可选依赖不可用异常
    except OptionalDependencyNotAvailable:
        # 从虚拟对象模块导入所有内容
        from ...utils.dummy_torch_and_transformers_objects import *

    else:
        # 从各个管道模块导入实际的类
        from .pipeline_kandinsky import KandinskyPipeline
        from .pipeline_kandinsky_combined import (
            KandinskyCombinedPipeline,
            KandinskyImg2ImgCombinedPipeline,
            KandinskyInpaintCombinedPipeline,
        )
        from .pipeline_kandinsky_img2img import KandinskyImg2ImgPipeline
        from .pipeline_kandinsky_inpaint import KandinskyInpaintPipeline
        from .pipeline_kandinsky_prior import KandinskyPriorPipeline, KandinskyPriorPipelineOutput
        from .text_encoder import MultilingualCLIP

else:
    # 如果不是在类型检查模式或慢速导入模式,则使用懒加载模块
    import sys

    # 将当前模块的引用替换为懒加载模块
    sys.modules[__name__] = _LazyModule(
        __name__,
        globals()["__file__"],
        _import_structure,  # 传入导入结构
        module_spec=__spec__,  # 传入模块规范
    )

    # 将虚拟对象添加到当前模块
    for name, value in _dummy_objects.items():
        setattr(sys.modules[__name__], name, value)

.\diffusers\pipelines\kandinsky2_2\pipeline_kandinsky2_2.py

# 版权声明,2024年由 HuggingFace 团队保留所有权利
# 
# 根据 Apache 许可证 2.0 版("许可证")进行许可;
# 除非遵守许可证,否则不得使用此文件。
# 可在以下地址获得许可证副本:
# 
#     http://www.apache.org/licenses/LICENSE-2.0
# 
# 除非适用法律或书面协议另有规定,软件
# 以“按原样”方式分发,不附带任何形式的保证或条件,
# 明示或暗示。
# 请参见许可证以获取与权限和
# 限制相关的具体信息。

from typing import Callable, Dict, List, Optional, Union  # 从 typing 模块导入类型提示相关类

import torch  # 导入 PyTorch 库

# 从模型模块导入 UNet2DConditionModel 和 VQModel
from ...models import UNet2DConditionModel, VQModel  
# 从调度器模块导入 DDPMScheduler
from ...schedulers import DDPMScheduler  
# 从工具模块导入实用功能
from ...utils import deprecate, logging, replace_example_docstring  
# 从工具中的 torch_utils 模块导入 randn_tensor
from ...utils.torch_utils import randn_tensor  
# 从管道工具导入 DiffusionPipeline 和 ImagePipelineOutput
from ..pipeline_utils import DiffusionPipeline, ImagePipelineOutput  

# 创建日志记录器,用于记录信息和错误
logger = logging.get_logger(__name__)  # pylint: disable=invalid-name

# 示例文档字符串,包含使用该管道的示例代码
EXAMPLE_DOC_STRING = """
    Examples:
        ```py
        >>> from diffusers import KandinskyV22Pipeline, KandinskyV22PriorPipeline
        >>> import torch

        >>> pipe_prior = KandinskyV22PriorPipeline.from_pretrained("kandinsky-community/kandinsky-2-2-prior")  # 从预训练模型创建管道
        >>> pipe_prior.to("cuda")  # 将管道转移到 CUDA 设备
        >>> prompt = "red cat, 4k photo"  # 设置生成图像的提示文本
        >>> out = pipe_prior(prompt)  # 使用提示生成图像嵌入
        >>> image_emb = out.image_embeds  # 提取生成的图像嵌入
        >>> zero_image_emb = out.negative_image_embeds  # 提取负图像嵌入
        >>> pipe = KandinskyV22Pipeline.from_pretrained("kandinsky-community/kandinsky-2-2-decoder")  # 创建解码器管道
        >>> pipe.to("cuda")  # 将解码器管道转移到 CUDA 设备
        >>> image = pipe(  # 使用图像嵌入生成图像
        ...     image_embeds=image_emb,
        ...     negative_image_embeds=zero_image_emb,
        ...     height=768,  # 设置生成图像的高度
        ...     width=768,  # 设置生成图像的宽度
        ...     num_inference_steps=50,  # 设置推理步骤数量
        ... ).images  # 提取生成的图像
        >>> image[0].save("cat.png")  # 保存生成的图像为 PNG 文件
        ```py
"""

# 定义一个函数,用于根据给定的高度和宽度进行缩放
def downscale_height_and_width(height, width, scale_factor=8):  # 接受高度、宽度和缩放因子
    new_height = height // scale_factor**2  # 计算新的高度
    if height % scale_factor**2 != 0:  # 检查高度是否不能被缩放因子整除
        new_height += 1  # 如果不能整除,则增加高度
    new_width = width // scale_factor**2  # 计算新的宽度
    if width % scale_factor**2 != 0:  # 检查宽度是否不能被缩放因子整除
        new_width += 1  # 如果不能整除,则增加宽度
    return new_height * scale_factor, new_width * scale_factor  # 返回缩放后的高度和宽度

# 定义 KandinskyV22Pipeline 类,继承自 DiffusionPipeline
class KandinskyV22Pipeline(DiffusionPipeline):
    """
    用于使用 Kandinsky 进行文本到图像生成的管道

    该模型继承自 [`DiffusionPipeline`]。有关库为所有管道实现的通用方法的文档,请参见父类文档(例如下载或保存,运行在特定设备等)。

    Args:
        scheduler (Union[`DDIMScheduler`,`DDPMScheduler`]):
            与 `unet` 结合使用以生成图像潜在特征的调度器。
        unet ([`UNet2DConditionModel`]):
            条件 U-Net 结构,用于去噪图像嵌入。
        movq ([`VQModel`]):
            MoVQ 解码器,用于从潜在特征生成图像。
    """
    # 定义模型的 CPU 卸载顺序,这里是将 unet 的输出移动到 q 形式
    model_cpu_offload_seq = "unet->movq"
    # 定义输入张量的名称列表
    _callback_tensor_inputs = ["latents", "image_embeds", "negative_image_embeds"]

    # 初始化方法,用于创建类的实例
    def __init__(
        # UNet2DConditionModel 实例,用于生成模型
        unet: UNet2DConditionModel,
        # DDPMScheduler 实例,用于调度过程
        scheduler: DDPMScheduler,
        # VQModel 实例,用于处理量化
        movq: VQModel,
    ):
        # 调用父类的初始化方法
        super().__init__()

        # 注册模型组件
        self.register_modules(
            unet=unet,
            scheduler=scheduler,
            movq=movq,
        )
        # 计算 movq 的缩放因子,基于块输出通道数
        self.movq_scale_factor = 2 ** (len(self.movq.config.block_out_channels) - 1)

    # 从 diffusers.pipelines.unclip.pipeline_unclip.UnCLIPPipeline 复制的准备潜在变量的方法
    def prepare_latents(self, shape, dtype, device, generator, latents, scheduler):
        # 如果没有提供潜在变量,则随机生成一个
        if latents is None:
            latents = randn_tensor(shape, generator=generator, device=device, dtype=dtype)
        else:
            # 如果提供的潜在变量形状不符合预期,则抛出错误
            if latents.shape != shape:
                raise ValueError(f"Unexpected latents shape, got {latents.shape}, expected {shape}")
            # 将潜在变量转移到指定设备
            latents = latents.to(device)

        # 将潜在变量乘以调度器的初始噪声标准差
        latents = latents * scheduler.init_noise_sigma
        # 返回处理后的潜在变量
        return latents

    # 获取指导缩放因子的属性
    @property
    def guidance_scale(self):
        return self._guidance_scale

    # 获取是否进行无分类器指导的属性,基于指导缩放因子
    @property
    def do_classifier_free_guidance(self):
        return self._guidance_scale > 1

    # 获取时间步数的属性
    @property
    def num_timesteps(self):
        return self._num_timesteps

    # 禁用梯度计算,防止在推理过程中计算梯度
    @torch.no_grad()
    # 替换示例文档字符串
    @replace_example_docstring(EXAMPLE_DOC_STRING)
    def __call__(
        # 图像嵌入,可以是张量或张量列表
        image_embeds: Union[torch.Tensor, List[torch.Tensor]],
        # 负图像嵌入,可以是张量或张量列表
        negative_image_embeds: Union[torch.Tensor, List[torch.Tensor]],
        # 输出图像的高度,默认 512
        height: int = 512,
        # 输出图像的宽度,默认 512
        width: int = 512,
        # 推理步骤数,默认 100
        num_inference_steps: int = 100,
        # 指导缩放因子,默认 4.0
        guidance_scale: float = 4.0,
        # 每个提示生成的图像数量,默认 1
        num_images_per_prompt: int = 1,
        # 随机数生成器,可以是单个生成器或生成器列表
        generator: Optional[Union[torch.Generator, List[torch.Generator]]] = None,
        # 可选的潜在变量张量
        latents: Optional[torch.Tensor] = None,
        # 输出类型,默认是 "pil"
        output_type: Optional[str] = "pil",
        # 是否返回字典,默认是 True
        return_dict: bool = True,
        # 结束时的回调函数,可选
        callback_on_step_end: Optional[Callable[[int, int, Dict], None]] = None,
        # 结束时张量输入的回调名称列表,默认是 ["latents"]
        callback_on_step_end_tensor_inputs: List[str] = ["latents"],
        # 其他可选关键字参数
        **kwargs,

.\diffusers\pipelines\kandinsky2_2\pipeline_kandinsky2_2_combined.py

# 版权信息,注明该代码的所有权归 HuggingFace 团队所有
# Copyright 2024 The HuggingFace Team. All rights reserved.
#
# 根据 Apache 2.0 许可协议进行许可
# Licensed under the Apache License, Version 2.0 (the "License");
# 除非遵守该许可,否则不得使用此文件
# you may not use this file except in compliance with the License.
# 你可以在以下网址获取许可的副本
# You may obtain a copy of the License at
#
#     http://www.apache.org/licenses/LICENSE-2.0
#
# 除非适用法律要求或书面协议,否则软件以“原样”方式分发
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# 不提供任何形式的保证或条件,明示或暗示
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# 请参见许可证以了解特定语言的权限和限制
# See the License for the specific language governing permissions and
# limitations under the License.

# 导入类型相关的类,以支持类型注解
from typing import Callable, Dict, List, Optional, Union

# 导入 PIL 库中的 Image 模块,用于图像处理
import PIL.Image
# 导入 PyTorch 库
import torch
# 从 transformers 库中导入 CLIP 相关模型和处理器
from transformers import CLIPImageProcessor, CLIPTextModelWithProjection, CLIPTokenizer, CLIPVisionModelWithProjection

# 从本地模块导入各种模型和调度器
from ...models import PriorTransformer, UNet2DConditionModel, VQModel
from ...schedulers import DDPMScheduler, UnCLIPScheduler
from ...utils import deprecate, logging, replace_example_docstring
from ..pipeline_utils import DiffusionPipeline
from .pipeline_kandinsky2_2 import KandinskyV22Pipeline
from .pipeline_kandinsky2_2_img2img import KandinskyV22Img2ImgPipeline
from .pipeline_kandinsky2_2_inpainting import KandinskyV22InpaintPipeline
from .pipeline_kandinsky2_2_prior import KandinskyV22PriorPipeline

# 创建日志记录器,用于记录模块中的日志信息
logger = logging.get_logger(__name__)  # pylint: disable=invalid-name

# 定义文本到图像转换的示例文档字符串
TEXT2IMAGE_EXAMPLE_DOC_STRING = """
    Examples:
        ```py
        # 从 diffusers 库中导入自动文本到图像管道
        from diffusers import AutoPipelineForText2Image
        # 导入 PyTorch 库
        import torch

        # 加载预训练的文本到图像管道,使用半精度浮点数
        pipe = AutoPipelineForText2Image.from_pretrained(
            "kandinsky-community/kandinsky-2-2-decoder", torch_dtype=torch.float16
        )
        # 启用模型的 CPU 卸载,以减少内存占用
        pipe.enable_model_cpu_offload()

        # 定义要生成图像的提示语
        prompt = "A lion in galaxies, spirals, nebulae, stars, smoke, iridescent, intricate detail, octane render, 8k"

        # 生成图像,指定推理步骤数量,并获取生成的第一张图像
        image = pipe(prompt=prompt, num_inference_steps=25).images[0]
        ```py
"""

# 定义图像到图像转换的示例文档字符串
IMAGE2IMAGE_EXAMPLE_DOC_STRING = """
    Examples:
        ```py
        # 从 diffusers 库中导入自动图像到图像管道
        from diffusers import AutoPipelineForImage2Image
        # 导入 PyTorch 库
        import torch
        # 导入请求库用于下载图像
        import requests
        # 从 io 库中导入 BytesIO,用于处理字节流
        from io import BytesIO
        # 导入 PIL 库中的 Image 模块
        from PIL import Image
        # 导入 os 库
        import os

        # 加载预训练的图像到图像管道,使用半精度浮点数
        pipe = AutoPipelineForImage2Image.from_pretrained(
            "kandinsky-community/kandinsky-2-2-decoder", torch_dtype=torch.float16
        )
        # 启用模型的 CPU 卸载,以减少内存占用
        pipe.enable_model_cpu_offload()

        # 定义要生成图像的提示语
        prompt = "A fantasy landscape, Cinematic lighting"
        # 定义负面提示语,用于控制生成的图像质量
        negative_prompt = "low quality, bad quality"

        # 定义要下载的图像 URL
        url = "https://raw.githubusercontent.com/CompVis/stable-diffusion/main/assets/stable-samples/img2img/sketch-mountains-input.jpg"

        # 发起 GET 请求以获取图像
        response = requests.get(url)
        # 打开获取的图像内容,并将其转换为 RGB 模式
        image = Image.open(BytesIO(response.content)).convert("RGB")
        # 调整图像大小,以适应后续处理
        image.thumbnail((768, 768))

        # 使用管道生成新图像,传入原始图像和提示语,获取生成的第一张图像
        image = pipe(prompt=prompt, image=original_image, num_inference_steps=25).images[0]
        ```py
"""

# 定义图像修复的示例文档字符串
INPAINT_EXAMPLE_DOC_STRING = """

    # 示例代码展示如何使用 Diffusers 库进行图像修复
        Examples:
            ```py
            # 从 diffusers 库导入 AutoPipelineForInpainting 类
            from diffusers import AutoPipelineForInpainting
            # 从 diffusers.utils 导入 load_image 函数
            from diffusers.utils import load_image
            # 导入 PyTorch 库
            import torch
            # 导入 NumPy 库
            import numpy as np
    
            # 从预训练模型加载图像修复管道,指定数据类型为 float16
            pipe = AutoPipelineForInpainting.from_pretrained(
                "kandinsky-community/kandinsky-2-2-decoder-inpaint", torch_dtype=torch.float16
            )
            # 启用模型的 CPU 内存卸载功能,以节省显存
            pipe.enable_model_cpu_offload()
    
            # 定义图像修复的提示语
            prompt = "A fantasy landscape, Cinematic lighting"
            # 定义负面提示,以避免生成低质量图像
            negative_prompt = "low quality, bad quality"
    
            # 加载原始图像,使用给定的 URL
            original_image = load_image(
                "https://huggingface.co/datasets/hf-internal-testing/diffusers-images/resolve/main" "/kandinsky/cat.png"
            )
    
            # 创建一个全零的掩码,大小为 (768, 768),数据类型为 float32
            mask = np.zeros((768, 768), dtype=np.float32)
            # 在猫的头部上方的区域设置掩码为 1,以进行遮挡
            mask[:250, 250:-250] = 1
    
            # 使用管道进行图像修复,传入提示、原始图像和掩码,指定推理步骤数为 25,获取生成的图像
            image = pipe(prompt=prompt, image=original_image, mask_image=mask, num_inference_steps=25).images[0]
            ```py 
# 定义一个结合管道类,用于基于 Kandinsky 的文本到图像生成
class KandinskyV22CombinedPipeline(DiffusionPipeline):
    # 文档字符串,描述该类的功能和参数
    """
    Combined Pipeline for text-to-image generation using Kandinsky

    This model inherits from [`DiffusionPipeline`]. Check the superclass documentation for the generic methods the
    library implements for all the pipelines (such as downloading or saving, running on a particular device, etc.)

    Args:
        scheduler (Union[`DDIMScheduler`,`DDPMScheduler`]):
            A scheduler to be used in combination with `unet` to generate image latents.
        unet ([`UNet2DConditionModel`]):
            Conditional U-Net architecture to denoise the image embedding.
        movq ([`VQModel`]):
            MoVQ Decoder to generate the image from the latents.
        prior_prior ([`PriorTransformer`]):
            The canonical unCLIP prior to approximate the image embedding from the text embedding.
        prior_image_encoder ([`CLIPVisionModelWithProjection`]):
            Frozen image-encoder.
        prior_text_encoder ([`CLIPTextModelWithProjection`]):
            Frozen text-encoder.
        prior_tokenizer (`CLIPTokenizer`):
            Tokenizer of class
            [CLIPTokenizer](https://huggingface.co/docs/transformers/v4.21.0/en/model_doc/clip#transformers.CLIPTokenizer).
        prior_scheduler ([`UnCLIPScheduler`]):
            A scheduler to be used in combination with `prior` to generate image embedding.
        prior_image_processor ([`CLIPImageProcessor`]):
            A image_processor to be used to preprocess image from clip.
    """
    
    # 定义模型的 CPU 卸载顺序,指定各部分的执行顺序
    model_cpu_offload_seq = "prior_text_encoder->prior_image_encoder->unet->movq"
    # 表示需要加载连接的管道
    _load_connected_pipes = True
    # 指定从 CPU 卸载时要排除的组件
    _exclude_from_cpu_offload = ["prior_prior"]

    # 初始化方法,接受多个模型作为参数
    def __init__(
        self,
        unet: UNet2DConditionModel,  # 条件 U-Net 模型,用于图像去噪
        scheduler: DDPMScheduler,  # 调度器,用于生成图像潜在特征
        movq: VQModel,  # MoVQ 解码器,从潜在特征生成图像
        prior_prior: PriorTransformer,  # 用于近似图像嵌入的先验转换器
        prior_image_encoder: CLIPVisionModelWithProjection,  # 冻结的图像编码器
        prior_text_encoder: CLIPTextModelWithProjection,  # 冻结的文本编码器
        prior_tokenizer: CLIPTokenizer,  # CLIP 的分词器
        prior_scheduler: UnCLIPScheduler,  # 生成图像嵌入的调度器
        prior_image_processor: CLIPImageProcessor,  # 用于预处理图像的图像处理器
    # 初始化父类
        ):
            super().__init__()
    
            # 注册各个模块,以便于后续使用
            self.register_modules(
                unet=unet,
                scheduler=scheduler,
                movq=movq,
                prior_prior=prior_prior,
                prior_image_encoder=prior_image_encoder,
                prior_text_encoder=prior_text_encoder,
                prior_tokenizer=prior_tokenizer,
                prior_scheduler=prior_scheduler,
                prior_image_processor=prior_image_processor,
            )
            # 创建先前处理管道,用于图像和文本处理
            self.prior_pipe = KandinskyV22PriorPipeline(
                prior=prior_prior,
                image_encoder=prior_image_encoder,
                text_encoder=prior_text_encoder,
                tokenizer=prior_tokenizer,
                scheduler=prior_scheduler,
                image_processor=prior_image_processor,
            )
            # 创建解码管道,负责生成最终输出
            self.decoder_pipe = KandinskyV22Pipeline(
                unet=unet,
                scheduler=scheduler,
                movq=movq,
            )
    
        # 启用高效内存注意力机制
        def enable_xformers_memory_efficient_attention(self, attention_op: Optional[Callable] = None):
            # 调用解码管道的方法,启用内存高效注意力
            self.decoder_pipe.enable_xformers_memory_efficient_attention(attention_op)
    
        # 启用顺序 CPU 卸载
        def enable_sequential_cpu_offload(self, gpu_id: Optional[int] = None, device: Union[torch.device, str] = "cuda"):
            r"""
            将所有模型卸载到 CPU,从而显著减少内存使用。当调用时,unet、text_encoder、vae 和安全检查器的状态字典保存到 CPU,然后移至
            `torch.device('meta'),并在特定子模块调用其 `forward` 方法时加载到 GPU。
            注意,卸载是针对子模块进行的。内存节省比 `enable_model_cpu_offload` 高,但性能较低。
            """
            # 启用先前处理管道的顺序 CPU 卸载
            self.prior_pipe.enable_sequential_cpu_offload(gpu_id=gpu_id, device=device)
            # 启用解码管道的顺序 CPU 卸载
            self.decoder_pipe.enable_sequential_cpu_offload(gpu_id=gpu_id, device=device)
    
        # 进度条功能
        def progress_bar(self, iterable=None, total=None):
            # 调用先前处理管道的进度条功能
            self.prior_pipe.progress_bar(iterable=iterable, total=total)
            # 调用解码管道的进度条功能
            self.decoder_pipe.progress_bar(iterable=iterable, total=total)
            # 启用解码管道的模型 CPU 卸载
            self.decoder_pipe.enable_model_cpu_offload()
    
        # 设置进度条的配置
        def set_progress_bar_config(self, **kwargs):
            # 为先前处理管道设置进度条配置
            self.prior_pipe.set_progress_bar_config(**kwargs)
            # 为解码管道设置进度条配置
            self.decoder_pipe.set_progress_bar_config(**kwargs)
    
        # 无梯度计算装饰器,避免计算梯度
        @torch.no_grad()
        # 替换示例文档字符串
        @replace_example_docstring(TEXT2IMAGE_EXAMPLE_DOC_STRING)
    # 定义一个可调用的类方法,接受多个参数进行处理
        def __call__(
            self,
            # 输入的提示,可以是字符串或字符串列表
            prompt: Union[str, List[str]],
            # 可选的负面提示,也可以是字符串或字符串列表
            negative_prompt: Optional[Union[str, List[str]]] = None,
            # 推理步骤的数量,默认为100
            num_inference_steps: int = 100,
            # 指导比例,默认为4.0
            guidance_scale: float = 4.0,
            # 每个提示生成的图像数量,默认为1
            num_images_per_prompt: int = 1,
            # 输出图像的高度,默认为512
            height: int = 512,
            # 输出图像的宽度,默认为512
            width: int = 512,
            # 先前指导比例,默认为4.0
            prior_guidance_scale: float = 4.0,
            # 先前推理步骤的数量,默认为25
            prior_num_inference_steps: int = 25,
            # 随机数生成器,可选,支持单个或多个生成器
            generator: Optional[Union[torch.Generator, List[torch.Generator]]] = None,
            # 可选的潜变量,默认为None
            latents: Optional[torch.Tensor] = None,
            # 输出类型,默认为"pil"
            output_type: Optional[str] = "pil",
            # 可选的回调函数,用于处理推理过程中的步骤
            callback: Optional[Callable[[int, int, torch.Tensor], None]] = None,
            # 回调函数触发的步骤间隔,默认为1
            callback_steps: int = 1,
            # 返回结果的字典形式,默认为True
            return_dict: bool = True,
            # 可选的先前回调函数,在步骤结束时调用
            prior_callback_on_step_end: Optional[Callable[[int, int, Dict], None]] = None,
            # 用于先前回调的张量输入,默认为["latents"]
            prior_callback_on_step_end_tensor_inputs: List[str] = ["latents"],
            # 可选的回调函数,在步骤结束时调用
            callback_on_step_end: Optional[Callable[[int, int, Dict], None]] = None,
            # 用于回调的张量输入,默认为["latents"]
            callback_on_step_end_tensor_inputs: List[str] = ["latents"],
# 定义一个结合图像到图像生成的管道类 KandinskyV22Img2ImgCombinedPipeline,继承自 DiffusionPipeline
class KandinskyV22Img2ImgCombinedPipeline(DiffusionPipeline):
    """
    Combined Pipeline for image-to-image generation using Kandinsky

    This model inherits from [`DiffusionPipeline`]. Check the superclass documentation for the generic methods the
    library implements for all the pipelines (such as downloading or saving, running on a particular device, etc.)

    Args:
        scheduler (Union[`DDIMScheduler`,`DDPMScheduler`]):
            A scheduler to be used in combination with `unet` to generate image latents.
        unet ([`UNet2DConditionModel`]):
            Conditional U-Net architecture to denoise the image embedding.
        movq ([`VQModel`]):
            MoVQ Decoder to generate the image from the latents.
        prior_prior ([`PriorTransformer`]):
            The canonical unCLIP prior to approximate the image embedding from the text embedding.
        prior_image_encoder ([`CLIPVisionModelWithProjection`]):
            Frozen image-encoder.
        prior_text_encoder ([`CLIPTextModelWithProjection`]):
            Frozen text-encoder.
        prior_tokenizer (`CLIPTokenizer`):
            Tokenizer of class
            [CLIPTokenizer](https://huggingface.co/docs/transformers/v4.21.0/en/model_doc/clip#transformers.CLIPTokenizer).
        prior_scheduler ([`UnCLIPScheduler`]):
            A scheduler to be used in combination with `prior` to generate image embedding.
        prior_image_processor ([`CLIPImageProcessor`]):
            A image_processor to be used to preprocess image from clip.
    """

    # 定义 CPU 卸载顺序的模型组件
    model_cpu_offload_seq = "prior_text_encoder->prior_image_encoder->unet->movq"
    # 指定是否加载连接的管道
    _load_connected_pipes = True
    # 指定从 CPU 卸载中排除的组件
    _exclude_from_cpu_offload = ["prior_prior"]

    # 初始化方法,接受多个模型组件作为参数
    def __init__(
        # 条件 U-Net 模型,用于图像嵌入去噪
        self,
        unet: UNet2DConditionModel,
        # DDPMScheduler 调度器
        scheduler: DDPMScheduler,
        # MoVQ 解码器
        movq: VQModel,
        # 用于图像嵌入的 unCLIP 先验
        prior_prior: PriorTransformer,
        # 冻结的图像编码器
        prior_image_encoder: CLIPVisionModelWithProjection,
        # 冻结的文本编码器
        prior_text_encoder: CLIPTextModelWithProjection,
        # CLIP 令牌化器
        prior_tokenizer: CLIPTokenizer,
        # 先验的调度器
        prior_scheduler: UnCLIPScheduler,
        # 图像预处理器
        prior_image_processor: CLIPImageProcessor,
    # 初始化父类
        ):
            super().__init__()
    
            # 注册多个模块以供后续使用
            self.register_modules(
                unet=unet,  # UNet模型
                scheduler=scheduler,  # 调度器
                movq=movq,  # MovQ模型
                prior_prior=prior_prior,  # 先验模型
                prior_image_encoder=prior_image_encoder,  # 图像编码器
                prior_text_encoder=prior_text_encoder,  # 文本编码器
                prior_tokenizer=prior_tokenizer,  # 标记器
                prior_scheduler=prior_scheduler,  # 先验调度器
                prior_image_processor=prior_image_processor,  # 图像处理器
            )
            # 创建先验管道实例
            self.prior_pipe = KandinskyV22PriorPipeline(
                prior=prior_prior,  # 先验模型
                image_encoder=prior_image_encoder,  # 图像编码器
                text_encoder=prior_text_encoder,  # 文本编码器
                tokenizer=prior_tokenizer,  # 标记器
                scheduler=prior_scheduler,  # 先验调度器
                image_processor=prior_image_processor,  # 图像处理器
            )
            # 创建图像到图像的转换管道实例
            self.decoder_pipe = KandinskyV22Img2ImgPipeline(
                unet=unet,  # UNet模型
                scheduler=scheduler,  # 调度器
                movq=movq,  # MovQ模型
            )
    
        # 启用高效的 xformers 内存注意力机制
        def enable_xformers_memory_efficient_attention(self, attention_op: Optional[Callable] = None):
            self.decoder_pipe.enable_xformers_memory_efficient_attention(attention_op)
    
        # 启用模型 CPU 卸载
        def enable_model_cpu_offload(self, gpu_id: Optional[int] = None, device: Union[torch.device, str] = "cuda"):
            r"""
            使用 accelerate 将所有模型卸载到 CPU,减少内存使用,性能影响低。与 `enable_sequential_cpu_offload` 相比,
            此方法在调用 `forward` 方法时将整个模型一次性移动到 GPU,并在下一个模型运行前保持在 GPU。
            内存节省较少,但性能因 UNet 的迭代执行而更好。
            """
            # 启用先验管道的 CPU 卸载
            self.prior_pipe.enable_model_cpu_offload(gpu_id=gpu_id, device=device)
            # 启用解码管道的 CPU 卸载
            self.decoder_pipe.enable_model_cpu_offload(gpu_id=gpu_id, device=device)
    
        # 启用顺序 CPU 卸载
        def enable_sequential_cpu_offload(self, gpu_id: Optional[int] = None, device: Union[torch.device, str] = "cuda"):
            r"""
            使用 accelerate 将所有模型卸载到 CPU,显著减少内存使用。调用时,unet、text_encoder、vae 和安全检查器
            的状态字典保存到 CPU,然后移动到 `torch.device('meta')`,仅在其特定子模块调用 `forward` 方法时加载到 GPU。
            注意卸载是按子模块进行的。内存节省高于 `enable_model_cpu_offload`,但性能较低。
            """
            # 启用先验管道的顺序 CPU 卸载
            self.prior_pipe.enable_sequential_cpu_offload(gpu_id=gpu_id, device=device)
            # 启用解码管道的顺序 CPU 卸载
            self.decoder_pipe.enable_sequential_cpu_offload(gpu_id=gpu_id, device=device)
    
        # 显示进度条
        def progress_bar(self, iterable=None, total=None):
            # 在先验管道中显示进度条
            self.prior_pipe.progress_bar(iterable=iterable, total=total)
            # 在解码管道中显示进度条
            self.decoder_pipe.progress_bar(iterable=iterable, total=total)
            # 启用解码管道的模型 CPU 卸载
            self.decoder_pipe.enable_model_cpu_offload()
    # 设置进度条的配置,允许通过关键字参数自定义
    def set_progress_bar_config(self, **kwargs):
        # 在优先处理管道中设置进度条配置,传入关键字参数
        self.prior_pipe.set_progress_bar_config(**kwargs)
        # 在解码器管道中设置进度条配置,传入关键字参数
        self.decoder_pipe.set_progress_bar_config(**kwargs)

    # 在不计算梯度的情况下进行操作,节省内存
    @torch.no_grad()
    # 用于替换示例文档字符串的装饰器
    @replace_example_docstring(IMAGE2IMAGE_EXAMPLE_DOC_STRING)
    # 定义调用方法,支持多种输入类型和参数配置
    def __call__(
        # 提示文本,可以是单个字符串或字符串列表
        prompt: Union[str, List[str]],
        # 输入图像,可以是张量或 PIL 图像,也支持列表
        image: Union[torch.Tensor, PIL.Image.Image, List[torch.Tensor], List[PIL.Image.Image]],
        # 可选的负提示文本,用于限制生成
        negative_prompt: Optional[Union[str, List[str]]] = None,
        # 推理步骤数量,默认为 100
        num_inference_steps: int = 100,
        # 指导比例,控制生成的多样性,默认为 4.0
        guidance_scale: float = 4.0,
        # 强度参数,控制图像变换的强烈程度,默认为 0.3
        strength: float = 0.3,
        # 每个提示生成的图像数量,默认为 1
        num_images_per_prompt: int = 1,
        # 生成图像的高度,默认为 512
        height: int = 512,
        # 生成图像的宽度,默认为 512
        width: int = 512,
        # 优先指导比例,默认为 4.0
        prior_guidance_scale: float = 4.0,
        # 优先推理步骤数量,默认为 25
        prior_num_inference_steps: int = 25,
        # 随机数生成器,支持单个或多个生成器
        generator: Optional[Union[torch.Generator, List[torch.Generator]]] = None,
        # 预设的潜在变量,默认为 None
        latents: Optional[torch.Tensor] = None,
        # 输出类型,默认为 "pil"(PIL 格式)
        output_type: Optional[str] = "pil",
        # 回调函数,用于处理生成过程中的步骤
        callback: Optional[Callable[[int, int, torch.Tensor], None]] = None,
        # 回调执行的步骤频率,默认为 1
        callback_steps: int = 1,
        # 是否返回字典格式的结果,默认为 True
        return_dict: bool = True,
        # 优先回调函数在步骤结束时执行
        prior_callback_on_step_end: Optional[Callable[[int, int, Dict], None]] = None,
        # 在步骤结束时传入的优先回调的张量输入,默认为 ["latents"]
        prior_callback_on_step_end_tensor_inputs: List[str] = ["latents"],
        # 回调函数在步骤结束时执行
        callback_on_step_end: Optional[Callable[[int, int, Dict], None]] = None,
        # 在步骤结束时传入的回调的张量输入,默认为 ["latents"]
        callback_on_step_end_tensor_inputs: List[str] = ["latents"],
# 定义一个名为 KandinskyV22InpaintCombinedPipeline 的类,继承自 DiffusionPipeline
class KandinskyV22InpaintCombinedPipeline(DiffusionPipeline):
    """
    使用 Kandinsky 进行图像修复生成的组合管道

    该模型继承自 [`DiffusionPipeline`]。有关库为所有管道实现的通用方法(例如下载或保存、在特定设备上运行等),请查看父类文档。

    Args:
        scheduler (Union[`DDIMScheduler`,`DDPMScheduler`]):
            要与 `unet` 结合使用的调度器,用于生成图像潜在表示。
        unet ([`UNet2DConditionModel`]):
            条件 U-Net 结构,用于去噪图像嵌入。
        movq ([`VQModel`]):
            MoVQ 解码器,从潜在表示生成图像。
        prior_prior ([`PriorTransformer`]):
            经典的 unCLIP 先验,用于近似文本嵌入的图像嵌入。
        prior_image_encoder ([`CLIPVisionModelWithProjection`]):
            冻结的图像编码器。
        prior_text_encoder ([`CLIPTextModelWithProjection`]):
            冻结的文本编码器。
        prior_tokenizer (`CLIPTokenizer`):
            该类的分词器
            [CLIPTokenizer](https://huggingface.co/docs/transformers/v4.21.0/en/model_doc/clip#transformers.CLIPTokenizer)。
        prior_scheduler ([`UnCLIPScheduler`]):
            要与 `prior` 结合使用的调度器,用于生成图像嵌入。
        prior_image_processor ([`CLIPImageProcessor`]):
            用于预处理图像的图像处理器。
    """

    # 定义模型的 CPU 卸载顺序
    model_cpu_offload_seq = "prior_text_encoder->prior_image_encoder->unet->movq"
    # 指定是否加载连接的管道
    _load_connected_pipes = True
    # 指定要排除在 CPU 卸载之外的组件
    _exclude_from_cpu_offload = ["prior_prior"]

    # 初始化方法,定义管道的参数
    def __init__(
        # 条件 U-Net 模型
        self,
        unet: UNet2DConditionModel,
        # 扩散调度器
        scheduler: DDPMScheduler,
        # MoVQ 解码器
        movq: VQModel,
        # 先验变换器
        prior_prior: PriorTransformer,
        # 图像编码器
        prior_image_encoder: CLIPVisionModelWithProjection,
        # 文本编码器
        prior_text_encoder: CLIPTextModelWithProjection,
        # 分词器
        prior_tokenizer: CLIPTokenizer,
        # 先验调度器
        prior_scheduler: UnCLIPScheduler,
        # 图像处理器
        prior_image_processor: CLIPImageProcessor,
    ):
        # 调用父类的初始化方法
        super().__init__()

        # 注册各个模块,包括 UNet、调度器等
        self.register_modules(
            unet=unet,
            scheduler=scheduler,
            movq=movq,
            prior_prior=prior_prior,
            prior_image_encoder=prior_image_encoder,
            prior_text_encoder=prior_text_encoder,
            prior_tokenizer=prior_tokenizer,
            prior_scheduler=prior_scheduler,
            prior_image_processor=prior_image_processor,
        )
        # 创建 KandinskyV22PriorPipeline 实例,传入必要的组件
        self.prior_pipe = KandinskyV22PriorPipeline(
            prior=prior_prior,
            image_encoder=prior_image_encoder,
            text_encoder=prior_text_encoder,
            tokenizer=prior_tokenizer,
            scheduler=prior_scheduler,
            image_processor=prior_image_processor,
        )
        # 创建 KandinskyV22InpaintPipeline 实例,传入 UNet 和调度器
        self.decoder_pipe = KandinskyV22InpaintPipeline(
            unet=unet,
            scheduler=scheduler,
            movq=movq,
        )

    # 启用 xformers 的内存高效注意力机制
    def enable_xformers_memory_efficient_attention(self, attention_op: Optional[Callable] = None):
        # 调用解码管道的方法启用内存高效注意力
        self.decoder_pipe.enable_xformers_memory_efficient_attention(attention_op)

    # 启用顺序 CPU 卸载,减少内存使用
    def enable_sequential_cpu_offload(self, gpu_id: Optional[int] = None, device: Union[torch.device, str] = "cuda"):
        r"""
        使用 accelerate 将所有模型卸载到 CPU,显著减少内存使用。调用时,unet、
        text_encoder、vae 和安全检查器的状态字典被保存到 CPU,然后移动到
        `torch.device('meta'),仅在特定子模块调用其 `forward` 方法时加载到 GPU。
        注意,卸载是基于子模块的。内存节省大于 `enable_model_cpu_offload`,但性能较低。
        """
        # 在优先管道上启用顺序 CPU 卸载
        self.prior_pipe.enable_sequential_cpu_offload(gpu_id=gpu_id, device=device)
        # 在解码管道上启用顺序 CPU 卸载
        self.decoder_pipe.enable_sequential_cpu_offload(gpu_id=gpu_id, device=device)

    # 显示进度条
    def progress_bar(self, iterable=None, total=None):
        # 在优先管道上显示进度条
        self.prior_pipe.progress_bar(iterable=iterable, total=total)
        # 在解码管道上显示进度条
        self.decoder_pipe.progress_bar(iterable=iterable, total=total)
        # 启用解码管道的模型 CPU 卸载
        self.decoder_pipe.enable_model_cpu_offload()

    # 设置进度条配置
    def set_progress_bar_config(self, **kwargs):
        # 在优先管道上设置进度条配置
        self.prior_pipe.set_progress_bar_config(**kwargs)
        # 在解码管道上设置进度条配置
        self.decoder_pipe.set_progress_bar_config(**kwargs)

    # 使用装饰器以禁用梯度计算
    @torch.no_grad()
    # 替换示例文档字符串
    @replace_example_docstring(INPAINT_EXAMPLE_DOC_STRING)
    # 定义可调用对象的 __call__ 方法,允许实例像函数一样被调用
        def __call__(
            # 输入提示,可以是字符串或字符串列表
            self,
            prompt: Union[str, List[str]],
            # 输入图像,可以是张量、PIL 图像或它们的列表
            image: Union[torch.Tensor, PIL.Image.Image, List[torch.Tensor], List[PIL.Image.Image]],
            # 掩膜图像,可以是张量、PIL 图像或它们的列表
            mask_image: Union[torch.Tensor, PIL.Image.Image, List[torch.Tensor], List[PIL.Image.Image]],
            # 可选的负提示,可以是字符串或字符串列表
            negative_prompt: Optional[Union[str, List[str]]] = None,
            # 推理步骤的数量,默认为 100
            num_inference_steps: int = 100,
            # 指导比例,默认为 4.0
            guidance_scale: float = 4.0,
            # 每个提示生成的图像数量,默认为 1
            num_images_per_prompt: int = 1,
            # 输出图像的高度,默认为 512
            height: int = 512,
            # 输出图像的宽度,默认为 512
            width: int = 512,
            # 先验指导比例,默认为 4.0
            prior_guidance_scale: float = 4.0,
            # 先验推理步骤的数量,默认为 25
            prior_num_inference_steps: int = 25,
            # 随机数生成器,可选,可以是张量或生成器的列表
            generator: Optional[Union[torch.Generator, List[torch.Generator]]] = None,
            # 可选的潜在变量,默认为 None
            latents: Optional[torch.Tensor] = None,
            # 输出类型,默认为 "pil"
            output_type: Optional[str] = "pil",
            # 是否返回字典格式的结果,默认为 True
            return_dict: bool = True,
            # 先验回调函数,可选,接收步骤和状态的回调
            prior_callback_on_step_end: Optional[Callable[[int, int, Dict], None]] = None,
            # 先验回调函数的输入张量名称列表,默认为 ["latents"]
            prior_callback_on_step_end_tensor_inputs: List[str] = ["latents"],
            # 回调函数,可选,接收步骤和状态的回调
            callback_on_step_end: Optional[Callable[[int, int, Dict], None]] = None,
            # 回调函数的输入张量名称列表,默认为 ["latents"]
            callback_on_step_end_tensor_inputs: List[str] = ["latents"],
            # 其他可选参数
            **kwargs,
posted @ 2024-10-22 12:33  绝不原创的飞龙  阅读(10)  评论(0编辑  收藏  举报