diffusers-源码解析-二十七-

diffusers 源码解析(二十七)

.\diffusers\pipelines\deepfloyd_if\__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,  # 导入函数,检查 PyTorch 是否可用
    is_transformers_available,  # 导入函数,检查 Transformers 是否可用
)

# 初始化一个空字典,用于存储虚拟对象
_dummy_objects = {}
# 定义一个导入结构字典,列出不同模块的导入项
_import_structure = {
    "timesteps": [  # timesteps 模块的导入项列表
        "fast27_timesteps",
        "smart100_timesteps",
        "smart185_timesteps",
        "smart27_timesteps",
        "smart50_timesteps",
        "super100_timesteps",
        "super27_timesteps",
        "super40_timesteps",
    ]
}

# 尝试块,用于检查所需库的可用性
try:
    # 检查 Transformers 和 PyTorch 是否都可用,如果不可用则抛出异常
    if not (is_transformers_available() and is_torch_available()):
        raise OptionalDependencyNotAvailable()  # 抛出可选依赖不可用的异常
# 捕获可选依赖不可用的异常
except OptionalDependencyNotAvailable:
    # 从 utils 模块导入虚拟对象以处理依赖不可用的情况
    from ...utils import dummy_torch_and_transformers_objects  # noqa F403

    # 更新虚拟对象字典,填充从 dummy 对象模块获取的对象
    _dummy_objects.update(get_objects_from_module(dummy_torch_and_transformers_objects))
# 如果没有异常,继续执行以下代码
else:
    # 向导入结构字典添加与管道相关的项
    _import_structure["pipeline_if"] = ["IFPipeline"]
    _import_structure["pipeline_if_img2img"] = ["IFImg2ImgPipeline"]
    _import_structure["pipeline_if_img2img_superresolution"] = ["IFImg2ImgSuperResolutionPipeline"]
    _import_structure["pipeline_if_inpainting"] = ["IFInpaintingPipeline"]
    _import_structure["pipeline_if_inpainting_superresolution"] = ["IFInpaintingSuperResolutionPipeline"]
    _import_structure["pipeline_if_superresolution"] = ["IFSuperResolutionPipeline"]
    _import_structure["pipeline_output"] = ["IFPipelineOutput"]
    _import_structure["safety_checker"] = ["IFSafetyChecker"]
    _import_structure["watermark"] = ["IFWatermarker"]

# 检查类型检查或慢速导入标志
if TYPE_CHECKING or DIFFUSERS_SLOW_IMPORT:
    # 尝试块,检查所需库的可用性
    try:
        # 检查 Transformers 和 PyTorch 是否都可用
        if not (is_transformers_available() and is_torch_available()):
            raise OptionalDependencyNotAvailable()  # 抛出可选依赖不可用的异常

    # 捕获可选依赖不可用的异常
    except OptionalDependencyNotAvailable:
        # 从虚拟对象模块导入所有内容
        from ...utils.dummy_torch_and_transformers_objects import *
    # 如果没有异常,继续执行以下代码
    else:
        # 从各个管道模块导入所需的类
        from .pipeline_if import IFPipeline
        from .pipeline_if_img2img import IFImg2ImgPipeline
        from .pipeline_if_img2img_superresolution import IFImg2ImgSuperResolutionPipeline
        from .pipeline_if_inpainting import IFInpaintingPipeline
        from .pipeline_if_inpainting_superresolution import IFInpaintingSuperResolutionPipeline
        from .pipeline_if_superresolution import IFSuperResolutionPipeline
        from .pipeline_output import IFPipelineOutput
        from .safety_checker import IFSafetyChecker
        # 从 timesteps 模块导入所需的时间步长对象
        from .timesteps import (
            fast27_timesteps,
            smart27_timesteps,
            smart50_timesteps,
            smart100_timesteps,
            smart185_timesteps,
            super27_timesteps,
            super40_timesteps,
            super100_timesteps,
        )
        # 从 watermark 模块导入水印对象
        from .watermark import IFWatermarker

# 如果不进行类型检查且没有慢速导入标志
else:
    # 导入 sys 模块
    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\deprecated\alt_diffusion\modeling_roberta_series.py

# 从 dataclasses 模块导入 dataclass 装饰器
from dataclasses import dataclass
# 从 typing 模块导入可选类型和元组类型
from typing import Optional, Tuple

# 导入 PyTorch 库
import torch
# 从 torch 模块导入神经网络相关的功能
from torch import nn
# 从 transformers 模块导入 Roberta 预训练模型、配置和模型类
from transformers import RobertaPreTrainedModel, XLMRobertaConfig, XLMRobertaModel
# 从 transformers.utils 导入模型输出基类
from transformers.utils import ModelOutput

# 定义一个数据类,用于存储转换模型的输出
@dataclass
class TransformationModelOutput(ModelOutput):
    """
    文本模型输出的基类,包含最后隐藏状态的池化。

    参数:
        text_embeds (`torch.Tensor`,形状为 `(batch_size, output_dim)` *可选*,当模型以 `with_projection=True` 初始化时返回):
            通过对 pooler_output 应用投影层获得的文本嵌入。
        last_hidden_state (`torch.Tensor`,形状为 `(batch_size, sequence_length, hidden_size)`):
            模型最后一层输出的隐藏状态序列。
        hidden_states (`tuple(torch.Tensor)`,*可选*,当 `output_hidden_states=True` 被传递或当 `config.output_hidden_states=True` 时返回):
            隐藏状态的元组(每层输出一个,若模型有嵌入层则还包括嵌入输出),形状为 `(batch_size, sequence_length, hidden_size)`。

            模型在每层输出的隐藏状态,加上可选的初始嵌入输出。
        attentions (`tuple(torch.Tensor)`,*可选*,当 `output_attentions=True` 被传递或当 `config.output_attentions=True` 时返回):
            每层的注意力权重元组,形状为 `(batch_size, num_heads, sequence_length, sequence_length)`。

            在自注意力头中计算加权平均的注意力权重。
    """

    # 投影状态,默认为 None,类型为可选的 torch.Tensor
    projection_state: Optional[torch.Tensor] = None
    # 最后隐藏状态,类型为 torch.Tensor,默认为 None
    last_hidden_state: torch.Tensor = None
    # 隐藏状态,类型为可选的元组,包含多个 torch.Tensor
    hidden_states: Optional[Tuple[torch.Tensor]] = None
    # 注意力权重,类型为可选的元组,包含多个 torch.Tensor
    attentions: Optional[Tuple[torch.Tensor]] = None

# 定义一个配置类,继承自 XLMRobertaConfig
class RobertaSeriesConfig(XLMRobertaConfig):
    # 初始化方法,设置不同的配置参数
    def __init__(
        self,
        pad_token_id=1,  # 填充 token 的 ID,默认为 1
        bos_token_id=0,  # 句子开头 token 的 ID,默认为 0
        eos_token_id=2,  # 句子结尾 token 的 ID,默认为 2
        project_dim=512,  # 投影维度,默认为 512
        pooler_fn="cls",  # 池化函数,默认为 "cls"
        learn_encoder=False,  # 是否学习编码器,默认为 False
        use_attention_mask=True,  # 是否使用注意力掩码,默认为 True
        **kwargs,  # 其他关键字参数
    ):
        # 调用父类的初始化方法,设置填充、开头和结尾 token 的 ID
        super().__init__(pad_token_id=pad_token_id, bos_token_id=bos_token_id, eos_token_id=eos_token_id, **kwargs)
        # 设置投影维度
        self.project_dim = project_dim
        # 设置池化函数
        self.pooler_fn = pooler_fn
        # 设置是否学习编码器
        self.learn_encoder = learn_encoder
        # 设置是否使用注意力掩码
        self.use_attention_mask = use_attention_mask

# 定义一个模型类,继承自 RobertaPreTrainedModel
class RobertaSeriesModelWithTransformation(RobertaPreTrainedModel):
    # 加载时忽略的意外键
    _keys_to_ignore_on_load_unexpected = [r"pooler", r"logit_scale"]
    # 加载时忽略的缺失键
    _keys_to_ignore_on_load_missing = [r"position_ids", r"predictions.decoder.bias"]
    # 基础模型前缀
    base_model_prefix = "roberta"
    # 配置类
    config_class = RobertaSeriesConfig
    # 初始化方法,接收配置参数
    def __init__(self, config):
        # 调用父类的初始化方法,传入配置参数
        super().__init__(config)
        # 初始化 XLM-RoBERTa 模型,使用配置参数
        self.roberta = XLMRobertaModel(config)
        # 定义线性变换层,输入维度为隐藏层大小,输出维度为项目维度
        self.transformation = nn.Linear(config.hidden_size, config.project_dim)
        # 获取配置中的 has_pre_transformation 属性,默认值为 False
        self.has_pre_transformation = getattr(config, "has_pre_transformation", False)
        # 如果启用预变换层
        if self.has_pre_transformation:
            # 定义另一个线性变换层用于预变换
            self.transformation_pre = nn.Linear(config.hidden_size, config.project_dim)
            # 定义层归一化,应用于隐藏层输出
            self.pre_LN = nn.LayerNorm(config.hidden_size, eps=config.layer_norm_eps)
        # 调用后续初始化方法
        self.post_init()

    # 前向传播方法,接收多种输入参数
    def forward(
        self,
        input_ids: Optional[torch.Tensor] = None,  # 输入 ID
        attention_mask: Optional[torch.Tensor] = None,  # 注意力掩码
        token_type_ids: Optional[torch.Tensor] = None,  # 令牌类型 ID
        position_ids: Optional[torch.Tensor] = None,  # 位置 ID
        head_mask: Optional[torch.Tensor] = None,  # 头掩码
        inputs_embeds: Optional[torch.Tensor] = None,  # 输入嵌入
        encoder_hidden_states: Optional[torch.Tensor] = None,  # 编码器的隐藏状态
        encoder_attention_mask: Optional[torch.Tensor] = None,  # 编码器的注意力掩码
        output_attentions: Optional[bool] = None,  # 是否输出注意力
        return_dict: Optional[bool] = None,  # 是否返回字典格式
        output_hidden_states: Optional[bool] = None,  # 是否输出隐藏状态
    ):
        r""" """

        # 如果 return_dict 为 None,则使用配置中的默认值
        return_dict = return_dict if return_dict is not None else self.config.use_return_dict

        # 调用基础模型进行前向传播,传入各种输入参数
        outputs = self.base_model(
            input_ids=input_ids,
            attention_mask=attention_mask,
            token_type_ids=token_type_ids,
            position_ids=position_ids,
            head_mask=head_mask,
            inputs_embeds=inputs_embeds,
            encoder_hidden_states=encoder_hidden_states,
            encoder_attention_mask=encoder_attention_mask,
            output_attentions=output_attentions,
            # 根据是否有预变换层决定是否输出隐藏状态
            output_hidden_states=True if self.has_pre_transformation else output_hidden_states,
            return_dict=return_dict,
        )

        # 如果启用了预变换层
        if self.has_pre_transformation:
            # 获取倒数第二个隐藏状态作为序列输出
            sequence_output2 = outputs["hidden_states"][-2]
            # 对序列输出进行层归一化
            sequence_output2 = self.pre_LN(sequence_output2)
            # 应用预变换层得到投影状态
            projection_state2 = self.transformation_pre(sequence_output2)

            # 返回变换模型输出,包含投影状态和其他输出
            return TransformationModelOutput(
                projection_state=projection_state2,
                last_hidden_state=outputs.last_hidden_state,
                hidden_states=outputs.hidden_states,
                attentions=outputs.attentions,
            )
        else:
            # 如果没有预变换层,直接对最后的隐藏状态应用变换层
            projection_state = self.transformation(outputs.last_hidden_state)
            # 返回变换模型输出,包含投影状态和其他输出
            return TransformationModelOutput(
                projection_state=projection_state,
                last_hidden_state=outputs.last_hidden_state,
                hidden_states=outputs.hidden_states,
                attentions=outputs.attentions,
            )

.\diffusers\pipelines\deprecated\alt_diffusion\pipeline_alt_diffusion.py

# 版权声明,表示该文件的所有权归 HuggingFace 团队所有
# 该文件在 Apache 许可证 2.0 下授权使用
# 许可证的详细信息可以在以下链接获取
# http://www.apache.org/licenses/LICENSE-2.0
# 根据许可证,软件按“原样”提供,不提供任何形式的保证
# 详细的许可证条款可以在下面查看
import inspect  # 导入 inspect 模块,用于检查对象的类型和属性
from typing import Any, Callable, Dict, List, Optional, Union  # 导入类型注解以增强可读性和静态检查

import torch  # 导入 PyTorch 库,进行深度学习相关操作
from packaging import version  # 导入 version 模块,处理版本信息
from transformers import CLIPImageProcessor, CLIPVisionModelWithProjection, XLMRobertaTokenizer  # 导入变换器模型和处理器

# 从当前模块的父级导入配置和处理器工具
from ....configuration_utils import FrozenDict  
from ....image_processor import PipelineImageInput, VaeImageProcessor  

# 从加载器模块导入多种混合类
from ....loaders import (
    FromSingleFileMixin,  # 单文件加载混合
    IPAdapterMixin,  # IP 适配器混合
    StableDiffusionLoraLoaderMixin,  # 稳定扩散 Lora 加载混合
    TextualInversionLoaderMixin,  # 文本反转加载混合
)

# 从模型模块导入不同的模型类
from ....models import AutoencoderKL, ImageProjection, UNet2DConditionModel  
from ....models.lora import adjust_lora_scale_text_encoder  # 导入调整 Lora 比例的函数
from ....schedulers import KarrasDiffusionSchedulers  # 导入 Karras 扩散调度器
from ....utils import (
    USE_PEFT_BACKEND,  # 指示是否使用 PEFT 后端
    deprecate,  # 导入废弃功能的装饰器
    logging,  # 导入日志工具
    replace_example_docstring,  # 替换示例文档字符串的工具
    scale_lora_layers,  # 调整 Lora 层的比例
    unscale_lora_layers,  # 恢复 Lora 层的比例
)
from ....utils.torch_utils import randn_tensor  # 导入生成随机张量的工具
from ...pipeline_utils import DiffusionPipeline, StableDiffusionMixin  # 导入扩散管道和混合类
from ...stable_diffusion.safety_checker import StableDiffusionSafetyChecker  # 导入安全检查器
from .modeling_roberta_series import RobertaSeriesModelWithTransformation  # 导入 Roberta 系列模型
from .pipeline_output import AltDiffusionPipelineOutput  # 导入备用扩散管道输出

logger = logging.get_logger(__name__)  # 初始化日志记录器,用于记录模块相关信息

EXAMPLE_DOC_STRING = """  # 定义示例文档字符串,用于展示如何使用管道
    Examples:  # 示例标题
        ```py  # 示例代码开始
        >>> import torch  # 导入 PyTorch
        >>> from diffusers import AltDiffusionPipeline  # 导入备用扩散管道

        >>> pipe = AltDiffusionPipeline.from_pretrained("BAAI/AltDiffusion-m9", torch_dtype=torch.float16)  # 从预训练模型加载管道
        >>> pipe = pipe.to("cuda")  # 将管道移至 CUDA 设备

        >>> # "dark elf princess, highly detailed, d & d, fantasy, highly detailed, digital painting, trending on artstation, concept art, sharp focus, illustration, art by artgerm and greg rutkowski and fuji choko and viktoria gavrilenko and hoang lap"
        >>> prompt = "黑暗精灵公主,非常详细,幻想,非常详细,数字绘画,概念艺术,敏锐的焦点,插图"  # 定义提示内容
        >>> image = pipe(prompt).images[0]  # 使用提示生成图像并获取第一张
        ```py  # 示例代码结束
"""

# 从扩散管道中复制的函数,调整噪声配置
def rescale_noise_cfg(noise_cfg, noise_pred_text, guidance_rescale=0.0):  # 定义重标定噪声配置的函数
    """
    根据指导重标定噪声配置。基于研究[Common Diffusion Noise Schedules and
    Sample Steps are Flawed](https://arxiv.org/pdf/2305.08891.pdf)。见第 3.4 节
    """
    std_text = noise_pred_text.std(dim=list(range(1, noise_pred_text.ndim)), keepdim=True)  # 计算噪声预测文本的标准差
    # 计算噪声配置的标准差,沿指定维度进行
        std_cfg = noise_cfg.std(dim=list(range(1, noise_cfg.ndim)), keepdim=True)
        # 对指导结果进行重新缩放,以修正过度曝光的问题
        noise_pred_rescaled = noise_cfg * (std_text / std_cfg)
        # 将重新缩放的噪声与原始指导结果混合,通过引导缩放因子避免“平淡”图像
        noise_cfg = guidance_rescale * noise_pred_rescaled + (1 - guidance_rescale) * noise_cfg
        # 返回调整后的噪声配置
        return noise_cfg
# 从 diffusers.pipelines.stable_diffusion.pipeline_stable_diffusion 复制的代码
def retrieve_timesteps(
    # 调度器对象,用于获取时间步
    scheduler,
    # 生成样本时使用的扩散步骤数量,可选参数
    num_inference_steps: Optional[int] = None,
    # 时间步应移动到的设备,字符串或 torch.device 类型
    device: Optional[Union[str, torch.device]] = None,
    # 自定义时间步的列表,可选参数
    timesteps: Optional[List[int]] = None,
    # 自定义 sigma 的列表,可选参数
    sigmas: Optional[List[float]] = None,
    # 其他关键字参数
    **kwargs,
):
    """
    调用调度器的 `set_timesteps` 方法,并在调用后从调度器中获取时间步。处理自定义时间步。
    任何 kwargs 将传递给 `scheduler.set_timesteps`。

    参数:
        scheduler (`SchedulerMixin`):
            用于获取时间步的调度器。
        num_inference_steps (`int`):
            生成样本时使用的扩散步骤数量。如果使用,则 `timesteps` 必须为 `None`。
        device (`str` 或 `torch.device`, *可选*):
            时间步应移动到的设备。如果为 `None`,则时间步不被移动。
        timesteps (`List[int]`, *可选*):
            自定义时间步,用于覆盖调度器的时间步间距策略。如果传入 `timesteps`,则 `num_inference_steps` 和 `sigmas` 必须为 `None`。
        sigmas (`List[float]`, *可选*):
            自定义 sigma,用于覆盖调度器的时间步间距策略。如果传入 `sigmas`,则 `num_inference_steps` 和 `timesteps` 必须为 `None`。

    返回:
        `Tuple[torch.Tensor, int]`: 一个元组,其中第一个元素是调度器的时间步调度,第二个元素是推理步骤的数量。
    """
    # 检查是否同时传入了时间步和 sigma
    if timesteps is not None and sigmas is not None:
        # 抛出错误,因为不能同时使用这两个参数
        raise ValueError("Only one of `timesteps` or `sigmas` can be passed. Please choose one to set custom values")
    # 检查是否传入了自定义时间步
    if timesteps is not None:
        # 检查调度器的 set_timesteps 方法是否接受时间步参数
        accepts_timesteps = "timesteps" in set(inspect.signature(scheduler.set_timesteps).parameters.keys())
        # 如果不支持自定义时间步,抛出错误
        if not accepts_timesteps:
            raise ValueError(
                f"The current scheduler class {scheduler.__class__}'s `set_timesteps` does not support custom"
                f" timestep schedules. Please check whether you are using the correct scheduler."
            )
        # 调用调度器的 set_timesteps 方法,传入自定义时间步和设备
        scheduler.set_timesteps(timesteps=timesteps, device=device, **kwargs)
        # 从调度器获取设置后的时间步
        timesteps = scheduler.timesteps
        # 计算时间步的数量
        num_inference_steps = len(timesteps)
    # 检查是否传入了自定义 sigma
    elif sigmas is not None:
        # 检查调度器的 set_timesteps 方法是否接受 sigma 参数
        accept_sigmas = "sigmas" in set(inspect.signature(scheduler.set_timesteps).parameters.keys())
        # 如果不支持自定义 sigma,抛出错误
        if not accept_sigmas:
            raise ValueError(
                f"The current scheduler class {scheduler.__class__}'s `set_timesteps` does not support custom"
                f" sigmas schedules. Please check whether you are using the correct scheduler."
            )
        # 调用调度器的 set_timesteps 方法,传入自定义 sigma 和设备
        scheduler.set_timesteps(sigmas=sigmas, device=device, **kwargs)
        # 从调度器获取设置后的时间步
        timesteps = scheduler.timesteps
        # 计算时间步的数量
        num_inference_steps = len(timesteps)
    # 否则,设置推理步骤和设备,传入额外的参数
        else:
            scheduler.set_timesteps(num_inference_steps, device=device, **kwargs)
            # 获取调度器的时间步列表
            timesteps = scheduler.timesteps
        # 返回时间步和推理步骤的数量
        return timesteps, num_inference_steps
# 定义一个名为 AltDiffusionPipeline 的类,继承多个父类以实现文本到图像生成的功能
class AltDiffusionPipeline(
    # 继承自 DiffusionPipeline,提供基础的扩散管道功能
    DiffusionPipeline,
    # 继承自 StableDiffusionMixin,提供稳定扩散相关的功能
    StableDiffusionMixin,
    # 继承自 TextualInversionLoaderMixin,提供文本反转加载功能
    TextualInversionLoaderMixin,
    # 继承自 StableDiffusionLoraLoaderMixin,提供 LoRA 权重的加载和保存功能
    StableDiffusionLoraLoaderMixin,
    # 继承自 IPAdapterMixin,提供 IP 适配器的加载功能
    IPAdapterMixin,
    # 继承自 FromSingleFileMixin,提供从单个文件加载的功能
    FromSingleFileMixin,
):
    # 文档字符串,描述该类的用途和功能
    r"""
    Pipeline for text-to-image generation using Alt Diffusion.

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

    The pipeline also inherits the following loading methods:
        - [`~loaders.TextualInversionLoaderMixin.load_textual_inversion`] for loading textual inversion embeddings
        - [`~loaders.StableDiffusionLoraLoaderMixin.load_lora_weights`] for loading LoRA weights
        - [`~loaders.StableDiffusionLoraLoaderMixin.save_lora_weights`] for saving LoRA weights
        - [`~loaders.FromSingleFileMixin.from_single_file`] for loading `.ckpt` files
        - [`~loaders.IPAdapterMixin.load_ip_adapter`] for loading IP Adapters

    Args:
        vae ([`AutoencoderKL`]):
            Variational Auto-Encoder (VAE) model to encode and decode images to and from latent representations.
        text_encoder ([`~transformers.RobertaSeriesModelWithTransformation`]):
            Frozen text-encoder ([clip-vit-large-patch14](https://huggingface.co/openai/clip-vit-large-patch14)).
        tokenizer ([`~transformers.XLMRobertaTokenizer`]):
            A `XLMRobertaTokenizer` to tokenize text.
        unet ([`UNet2DConditionModel`]):
            A `UNet2DConditionModel` to denoise the encoded image latents.
        scheduler ([`SchedulerMixin`]):
            A scheduler to be used in combination with `unet` to denoise the encoded image latents. Can be one of
            [`DDIMScheduler`], [`LMSDiscreteScheduler`], or [`PNDMScheduler`].
        safety_checker ([`StableDiffusionSafetyChecker`]):
            Classification module that estimates whether generated images could be considered offensive or harmful.
            Please refer to the [model card](https://huggingface.co/runwayml/stable-diffusion-v1-5) for more details
            about a model's potential harms.
        feature_extractor ([`~transformers.CLIPImageProcessor`]):
            A `CLIPImageProcessor` to extract features from generated images; used as inputs to the `safety_checker`.
    """
    # 定义模型在 CPU 上卸载的顺序,确保特定组件在内存中的顺序
    model_cpu_offload_seq = "text_encoder->image_encoder->unet->vae"
    # 定义可选组件的列表,这些组件可能在使用时被添加
    _optional_components = ["safety_checker", "feature_extractor", "image_encoder"]
    # 定义在 CPU 卸载时排除的组件,这些组件不会被卸载
    _exclude_from_cpu_offload = ["safety_checker"]
    # 定义需要回调的张量输入列表,这些输入将在特定操作中被处理
    _callback_tensor_inputs = ["latents", "prompt_embeds", "negative_prompt_embeds"]
    # 初始化类的构造函数,接收多个参数以配置模型组件
        def __init__(
            self,
            vae: AutoencoderKL,  # 变分自编码器实例
            text_encoder: RobertaSeriesModelWithTransformation,  # 文本编码器实例
            tokenizer: XLMRobertaTokenizer,  # 令牌化工具
            unet: UNet2DConditionModel,  # UNet模型实例
            scheduler: KarrasDiffusionSchedulers,  # 调度器实例
            safety_checker: StableDiffusionSafetyChecker,  # 安全检查器实例
            feature_extractor: CLIPImageProcessor,  # 特征提取器实例
            image_encoder: CLIPVisionModelWithProjection = None,  # 可选的图像编码器实例
            requires_safety_checker: bool = True,  # 是否需要安全检查器的布尔值
        def _encode_prompt(
            self,
            prompt,  # 输入的提示文本
            device,  # 设备类型(如CPU或GPU)
            num_images_per_prompt,  # 每个提示生成的图像数量
            do_classifier_free_guidance,  # 是否执行无分类器引导的布尔值
            negative_prompt=None,  # 可选的负面提示文本
            prompt_embeds: Optional[torch.Tensor] = None,  # 可选的提示嵌入张量
            negative_prompt_embeds: Optional[torch.Tensor] = None,  # 可选的负面提示嵌入张量
            lora_scale: Optional[float] = None,  # 可选的LoRA缩放因子
            **kwargs,  # 额外的关键字参数
        ):
            # 生成弃用警告信息,提示用户使用新方法
            deprecation_message = "`_encode_prompt()` is deprecated and it will be removed in a future version. Use `encode_prompt()` instead. Also, be aware that the output format changed from a concatenated tensor to a tuple."
            # 调用弃用处理函数,记录弃用警告
            deprecate("_encode_prompt()", "1.0.0", deprecation_message, standard_warn=False)
    
            # 调用encode_prompt方法处理提示文本,并返回嵌入的元组
            prompt_embeds_tuple = self.encode_prompt(
                prompt=prompt,  # 输入的提示文本
                device=device,  # 设备类型
                num_images_per_prompt=num_images_per_prompt,  # 图像数量
                do_classifier_free_guidance=do_classifier_free_guidance,  # 引导选项
                negative_prompt=negative_prompt,  # 负面提示文本
                prompt_embeds=prompt_embeds,  # 提示嵌入
                negative_prompt_embeds=negative_prompt_embeds,  # 负面嵌入
                lora_scale=lora_scale,  # LoRA缩放
                **kwargs,  # 额外参数
            )
    
            # 将返回的嵌入元组拼接为一个张量,兼容旧版本
            prompt_embeds = torch.cat([prompt_embeds_tuple[1], prompt_embeds_tuple[0]])
    
            # 返回最终的提示嵌入张量
            return prompt_embeds
    
        # 定义新的提示编码方法,接收多个参数以处理输入提示
        def encode_prompt(
            self,
            prompt,  # 输入的提示文本
            device,  # 设备类型(如CPU或GPU)
            num_images_per_prompt,  # 每个提示生成的图像数量
            do_classifier_free_guidance,  # 是否执行无分类器引导的布尔值
            negative_prompt=None,  # 可选的负面提示文本
            prompt_embeds: Optional[torch.Tensor] = None,  # 可选的提示嵌入张量
            negative_prompt_embeds: Optional[torch.Tensor] = None,  # 可选的负面提示嵌入张量
            lora_scale: Optional[float] = None,  # 可选的LoRA缩放因子
            clip_skip: Optional[int] = None,  # 可选的剪裁跳过参数
    # 定义一个方法,用于编码图像,参数包括图像、设备、每个提示的图像数量和可选的隐藏状态输出
    def encode_image(self, image, device, num_images_per_prompt, output_hidden_states=None):
        # 获取图像编码器参数的 dtype
        dtype = next(self.image_encoder.parameters()).dtype

        # 检查传入的图像是否为张量类型,如果不是,则使用特征提取器处理图像
        if not isinstance(image, torch.Tensor):
            # 将图像转换为 PyTorch 张量,并提取像素值
            image = self.feature_extractor(image, return_tensors="pt").pixel_values

        # 将图像移动到指定的设备,并转换为指定的数据类型
        image = image.to(device=device, dtype=dtype)
        
        # 如果请求输出隐藏状态
        if output_hidden_states:
            # 获取图像编码器的隐藏状态
            image_enc_hidden_states = self.image_encoder(image, output_hidden_states=True).hidden_states[-2]
            # 重复编码的隐藏状态,以适应每个提示的图像数量
            image_enc_hidden_states = image_enc_hidden_states.repeat_interleave(num_images_per_prompt, dim=0)
            # 对无条件图像进行编码,生成其隐藏状态
            uncond_image_enc_hidden_states = self.image_encoder(
                torch.zeros_like(image), output_hidden_states=True
            ).hidden_states[-2]
            # 重复无条件图像编码的隐藏状态
            uncond_image_enc_hidden_states = uncond_image_enc_hidden_states.repeat_interleave(
                num_images_per_prompt, dim=0
            )
            # 返回图像和无条件图像的编码隐藏状态
            return image_enc_hidden_states, uncond_image_enc_hidden_states
        else:
            # 如果不需要隐藏状态,则直接获取图像嵌入
            image_embeds = self.image_encoder(image).image_embeds
            # 重复图像嵌入,以适应每个提示的图像数量
            image_embeds = image_embeds.repeat_interleave(num_images_per_prompt, dim=0)
            # 创建与图像嵌入形状相同的零张量作为无条件图像嵌入
            uncond_image_embeds = torch.zeros_like(image_embeds)

            # 返回图像嵌入和无条件图像嵌入
            return image_embeds, uncond_image_embeds

    # 定义一个方法,用于运行安全检查器,参数包括图像、设备和数据类型
    def run_safety_checker(self, image, device, dtype):
        # 检查安全检查器是否为 None
        if self.safety_checker is None:
            # 如果是,则无不当内容概念标志为 None
            has_nsfw_concept = None
        else:
            # 检查传入的图像是否为张量类型
            if torch.is_tensor(image):
                # 如果是张量,则将其后处理为 PIL 图像格式
                feature_extractor_input = self.image_processor.postprocess(image, output_type="pil")
            else:
                # 如果不是张量,则将其转换为 PIL 图像
                feature_extractor_input = self.image_processor.numpy_to_pil(image)
            # 使用特征提取器处理后处理后的图像,并将其移动到指定的设备
            safety_checker_input = self.feature_extractor(feature_extractor_input, return_tensors="pt").to(device)
            # 运行安全检查器,检查图像和处理后的输入
            image, has_nsfw_concept = self.safety_checker(
                images=image, clip_input=safety_checker_input.pixel_values.to(dtype)
            )
        # 返回检查后的图像和不当内容概念标志
        return image, has_nsfw_concept

    # 定义一个方法,用于解码潜在变量
    def decode_latents(self, latents):
        # 定义弃用警告消息
        deprecation_message = "The decode_latents method is deprecated and will be removed in 1.0.0. Please use VaeImageProcessor.postprocess(...) instead"
        # 发出弃用警告
        deprecate("decode_latents", "1.0.0", deprecation_message, standard_warn=False)

        # 对潜在变量进行缩放
        latents = 1 / self.vae.config.scaling_factor * latents
        # 使用 VAE 解码潜在变量,返回解码后的图像
        image = self.vae.decode(latents, return_dict=False)[0]
        # 对图像进行归一化和裁剪,以确保值在 [0, 1] 范围内
        image = (image / 2 + 0.5).clamp(0, 1)
        # 将图像移动到 CPU,并调整维度顺序,转换为 numpy 数组,保持为 float32 类型
        image = image.cpu().permute(0, 2, 3, 1).float().numpy()
        # 返回解码后的图像
        return image
    # 定义一个方法,用于准备额外的调度器步骤参数
    def prepare_extra_step_kwargs(self, generator, eta):
        # 为调度器步骤准备额外的参数,因为不同调度器的参数签名可能不同
        # eta (η) 仅在 DDIMScheduler 中使用,其他调度器将忽略它
        # eta 对应于 DDIM 论文中的 η: https://arxiv.org/abs/2010.02502
        # 应在 [0, 1] 之间

        # 检查调度器的 step 方法是否接受 eta 参数
        accepts_eta = "eta" in set(inspect.signature(self.scheduler.step).parameters.keys())
        # 初始化一个空字典,用于存储额外的步骤参数
        extra_step_kwargs = {}
        # 如果接受 eta 参数,将其添加到额外步骤参数字典中
        if accepts_eta:
            extra_step_kwargs["eta"] = eta

        # 检查调度器的 step 方法是否接受 generator 参数
        accepts_generator = "generator" in set(inspect.signature(self.scheduler.step).parameters.keys())
        # 如果接受 generator 参数,将其添加到额外步骤参数字典中
        if accepts_generator:
            extra_step_kwargs["generator"] = generator
        # 返回准备好的额外步骤参数字典
        return extra_step_kwargs

    # 定义一个方法,用于检查输入参数的有效性
    def check_inputs(
        self,
        prompt,  # 用户输入的提示文本
        height,  # 输出图像的高度
        width,   # 输出图像的宽度
        callback_steps,  # 用于回调的步骤数量
        negative_prompt=None,  # 可选的负面提示文本
        prompt_embeds=None,  # 可选的提示嵌入向量
        negative_prompt_embeds=None,  # 可选的负面提示嵌入向量
        callback_on_step_end_tensor_inputs=None,  # 可选的回调输入张量
    ):
        # 检查高度和宽度是否能被8整除
        if height % 8 != 0 or width % 8 != 0:
            # 如果不能整除,抛出值错误
            raise ValueError(f"`height` and `width` have to be divisible by 8 but are {height} and {width}.")

        # 检查回调步骤是否为正整数
        if callback_steps is not None and (not isinstance(callback_steps, int) or callback_steps <= 0):
            # 如果不是,抛出值错误
            raise ValueError(
                f"`callback_steps` has to be a positive integer but is {callback_steps} of type"
                f" {type(callback_steps)}."
            )
        # 检查回调结束张量输入是否在允许的张量输入中
        if callback_on_step_end_tensor_inputs is not None and not all(
            k in self._callback_tensor_inputs for k in callback_on_step_end_tensor_inputs
        ):
            # 如果不在,抛出值错误
            raise ValueError(
                f"`callback_on_step_end_tensor_inputs` has to be in {self._callback_tensor_inputs}, but found {[k for k in callback_on_step_end_tensor_inputs if k not in self._callback_tensor_inputs]}"
            )

        # 检查提示和提示嵌入是否同时提供
        if prompt is not None and prompt_embeds is not None:
            # 如果同时提供,抛出值错误
            raise ValueError(
                f"Cannot forward both `prompt`: {prompt} and `prompt_embeds`: {prompt_embeds}. Please make sure to"
                " only forward one of the two."
            )
        # 检查是否同时未提供提示和提示嵌入
        elif prompt is None and prompt_embeds is None:
            # 如果未提供,抛出值错误
            raise ValueError(
                "Provide either `prompt` or `prompt_embeds`. Cannot leave both `prompt` and `prompt_embeds` undefined."
            )
        # 检查提示类型是否为字符串或列表
        elif prompt is not None and (not isinstance(prompt, str) and not isinstance(prompt, list)):
            # 如果不是,抛出值错误
            raise ValueError(f"`prompt` has to be of type `str` or `list` but is {type(prompt)}")

        # 检查负提示和负提示嵌入是否同时提供
        if negative_prompt is not None and negative_prompt_embeds is not None:
            # 如果同时提供,抛出值错误
            raise ValueError(
                f"Cannot forward both `negative_prompt`: {negative_prompt} and `negative_prompt_embeds`:"
                f" {negative_prompt_embeds}. Please make sure to only forward one of the two."
            )

        # 检查提示嵌入和负提示嵌入形状是否匹配
        if prompt_embeds is not None and negative_prompt_embeds is not None:
            if prompt_embeds.shape != negative_prompt_embeds.shape:
                # 如果形状不匹配,抛出值错误
                raise ValueError(
                    "`prompt_embeds` and `negative_prompt_embeds` must have the same shape when passed directly, but"
                    f" got: `prompt_embeds` {prompt_embeds.shape} != `negative_prompt_embeds`"
                    f" {negative_prompt_embeds.shape}."
                )
    # 准备潜在变量,根据输入参数设置形状
    def prepare_latents(self, batch_size, num_channels_latents, height, width, dtype, device, generator, latents=None):
        # 定义潜在变量的形状,包括批量大小、通道数和缩放后的高度与宽度
        shape = (
            batch_size,
            num_channels_latents,
            int(height) // self.vae_scale_factor,
            int(width) // self.vae_scale_factor,
        )
        # 检查生成器是否为列表且其长度是否与批量大小匹配
        if isinstance(generator, list) and len(generator) != batch_size:
            # 如果不匹配,抛出值错误并提示
            raise ValueError(
                f"You have passed a list of generators of length {len(generator)}, but requested an effective batch"
                f" size of {batch_size}. Make sure the batch size matches the length of the generators."
            )

        # 如果潜在变量未提供,生成随机潜在变量
        if latents is None:
            latents = randn_tensor(shape, generator=generator, device=device, dtype=dtype)
        else:
            # 如果提供了潜在变量,将其转移到指定设备
            latents = latents.to(device)

        # 将初始噪声按调度器要求的标准差进行缩放
        latents = latents * self.scheduler.init_noise_sigma
        # 返回处理后的潜在变量
        return latents

    # 获取引导比例嵌入的函数
    def get_guidance_scale_embedding(self, w, embedding_dim=512, dtype=torch.float32):
        """
        查看 https://github.com/google-research/vdm/blob/dc27b98a554f65cdc654b800da5aa1846545d41b/model_vdm.py#L298

        Args:
            timesteps (`torch.Tensor`):
                在这些时间步生成嵌入向量
            embedding_dim (`int`, *optional*, defaults to 512):
                生成嵌入的维度
            dtype:
                生成嵌入的数据类型

        Returns:
            `torch.Tensor`: 形状为 `(len(timesteps), embedding_dim)` 的嵌入向量
        """
        # 确保输入的 w 是一维的
        assert len(w.shape) == 1
        # 将 w 的值扩大 1000 倍
        w = w * 1000.0

        # 计算半维度
        half_dim = embedding_dim // 2
        # 计算嵌入的基本值
        emb = torch.log(torch.tensor(10000.0)) / (half_dim - 1)
        # 生成指数衰减的嵌入
        emb = torch.exp(torch.arange(half_dim, dtype=dtype) * -emb)
        # 将 w 转换为目标数据类型并进行广播
        emb = w.to(dtype)[:, None] * emb[None, :]
        # 将正弦和余弦值连接起来形成嵌入
        emb = torch.cat([torch.sin(emb), torch.cos(emb)], dim=1)
        # 如果嵌入维度为奇数,则在末尾进行零填充
        if embedding_dim % 2 == 1:  # zero pad
            emb = torch.nn.functional.pad(emb, (0, 1))
        # 确保嵌入的形状符合预期
        assert emb.shape == (w.shape[0], embedding_dim)
        # 返回最终的嵌入
        return emb

    # 引导比例的属性,返回存储的引导比例值
    @property
    def guidance_scale(self):
        return self._guidance_scale

    # 引导重缩放的属性,返回存储的重缩放值
    @property
    def guidance_rescale(self):
        return self._guidance_rescale

    # clip_skip 属性,返回存储的跳过值
    @property
    def clip_skip(self):
        return self._clip_skip

    # 判断是否执行无分类器引导的属性,基于引导比例和条件配置
    @property
    def do_classifier_free_guidance(self):
        return self._guidance_scale > 1 and self.unet.config.time_cond_proj_dim is None

    # 跨注意力参数的属性,返回存储的参数
    @property
    def cross_attention_kwargs(self):
        return self._cross_attention_kwargs

    # 时间步数的属性,返回存储的时间步数
    @property
    def num_timesteps(self):
        return self._num_timesteps

    # 使用装饰器标记后续代码不计算梯度
    @torch.no_grad()
    # 用于替换示例文档字符串的装饰器
        @replace_example_docstring(EXAMPLE_DOC_STRING)
        # 定义可调用方法,接收多个参数
        def __call__(
            # 用户提供的文本提示,可以是字符串或字符串列表
            self,
            prompt: Union[str, List[str]] = None,
            # 生成图像的高度
            height: Optional[int] = None,
            # 生成图像的宽度
            width: Optional[int] = None,
            # 推理步骤的数量,默认为50
            num_inference_steps: int = 50,
            # 可选的时间步列表
            timesteps: List[int] = None,
            # 可选的标准差列表
            sigmas: List[float] = None,
            # 指导缩放系数,默认为7.5
            guidance_scale: float = 7.5,
            # 可选的负面提示,可以是字符串或字符串列表
            negative_prompt: Optional[Union[str, List[str]]] = None,
            # 每个提示生成的图像数量,默认为1
            num_images_per_prompt: Optional[int] = 1,
            # 额外的超参数,默认为0.0
            eta: float = 0.0,
            # 可选的随机数生成器或生成器列表
            generator: Optional[Union[torch.Generator, List[torch.Generator]]] = None,
            # 可选的潜在变量张量
            latents: Optional[torch.Tensor] = None,
            # 可选的提示嵌入张量
            prompt_embeds: Optional[torch.Tensor] = None,
            # 可选的负面提示嵌入张量
            negative_prompt_embeds: Optional[torch.Tensor] = None,
            # 可选的适配器输入图像
            ip_adapter_image: Optional[PipelineImageInput] = None,
            # 输出类型,默认为“pil”格式
            output_type: Optional[str] = "pil",
            # 是否返回字典格式的结果,默认为True
            return_dict: bool = True,
            # 可选的跨注意力参数字典
            cross_attention_kwargs: Optional[Dict[str, Any]] = None,
            # 指导再缩放的系数,默认为0.0
            guidance_rescale: float = 0.0,
            # 可选的跳过的剪辑步数
            clip_skip: Optional[int] = None,
            # 可选的回调函数,在步骤结束时执行
            callback_on_step_end: Optional[Callable[[int, int, Dict], None]] = None,
            # 在步骤结束时回调的张量输入列表,默认为“latents”
            callback_on_step_end_tensor_inputs: List[str] = ["latents"],
            # 其他可变参数
            **kwargs,

.\diffusers\pipelines\deprecated\alt_diffusion\pipeline_alt_diffusion_img2img.py

# 版权所有 2024 HuggingFace 团队。保留所有权利。
#
# 根据 Apache 许可证第 2.0 版(“许可证”)进行授权;
# 除非遵守许可证,否则您不得使用此文件。
# 您可以在以下网址获得许可证的副本:
#
#     http://www.apache.org/licenses/LICENSE-2.0
#
# 除非适用法律要求或书面协议另有约定,依据许可证分发的软件
# 在“按原样”基础上提供,没有任何形式的保证或条件,
# 无论是明示或暗示的。有关许可证的特定权限和
# 限制,请参见许可证。
import inspect  # 导入inspect模块,用于获取对象的实时信息
from typing import Any, Callable, Dict, List, Optional, Union  # 导入类型注解,用于类型提示

import numpy as np  # 导入numpy库,通常用于数值计算
import PIL.Image  # 导入PIL库的Image模块,用于图像处理
import torch  # 导入PyTorch库,用于深度学习
from packaging import version  # 导入version模块,用于处理版本字符串
from transformers import CLIPImageProcessor, CLIPVisionModelWithProjection, XLMRobertaTokenizer  # 导入transformers库中的图像处理和模型类

from ....configuration_utils import FrozenDict  # 从配置工具导入FrozenDict,用于不可变字典
from ....image_processor import PipelineImageInput, VaeImageProcessor  # 从图像处理模块导入相关类
from ....loaders import (  # 从加载器模块导入多个混合类
    FromSingleFileMixin,
    IPAdapterMixin,
    StableDiffusionLoraLoaderMixin,
    TextualInversionLoaderMixin,
)
from ....models import AutoencoderKL, ImageProjection, UNet2DConditionModel  # 从模型模块导入多个模型类
from ....models.lora import adjust_lora_scale_text_encoder  # 从Lora模块导入调整Lora规模的函数
from ....schedulers import KarrasDiffusionSchedulers  # 从调度器模块导入Karras扩散调度器
from ....utils import (  # 从工具模块导入多个工具函数和常量
    PIL_INTERPOLATION,
    USE_PEFT_BACKEND,
    deprecate,
    logging,
    replace_example_docstring,
    scale_lora_layers,
    unscale_lora_layers,
)
from ....utils.torch_utils import randn_tensor  # 从torch工具模块导入生成随机张量的函数
from ...pipeline_utils import DiffusionPipeline, StableDiffusionMixin  # 从管道工具模块导入扩散管道和稳定扩散混合类
from ...stable_diffusion.safety_checker import StableDiffusionSafetyChecker  # 从稳定扩散模块导入安全检查器
from .modeling_roberta_series import RobertaSeriesModelWithTransformation  # 从Roberta系列建模模块导入模型
from .pipeline_output import AltDiffusionPipelineOutput  # 从管道输出模块导入输出类


logger = logging.get_logger(__name__)  # 创建当前模块的日志记录器,禁用pylint对名称的警告

EXAMPLE_DOC_STRING = """  # 定义一个示例文档字符串,展示用法示例
    Examples:
        ```py
        >>> import requests  # 导入requests库,用于发送HTTP请求
        >>> import torch  # 导入PyTorch库,用于深度学习
        >>> from PIL import Image  # 从PIL库导入Image模块,用于图像处理
        >>> from io import BytesIO  # 从io模块导入BytesIO,用于字节流操作

        >>> from diffusers import AltDiffusionImg2ImgPipeline  # 从diffusers库导入图像到图像的扩散管道类

        >>> device = "cuda"  # 设置设备为CUDA(GPU)
        >>> model_id_or_path = "BAAI/AltDiffusion-m9"  # 指定模型的ID或路径
        >>> pipe = AltDiffusionImg2ImgPipeline.from_pretrained(model_id_or_path, torch_dtype=torch.float16)  # 从预训练模型加载管道,并设置数据类型为float16
        >>> pipe = pipe.to(device)  # 将管道移动到指定设备

        >>> url = "https://raw.githubusercontent.com/CompVis/stable-diffusion/main/assets/stable-samples/img2img/sketch-mountains-input.jpg"  # 指定输入图像的URL

        >>> response = requests.get(url)  # 发送HTTP GET请求以获取输入图像
        >>> init_image = Image.open(BytesIO(response.content)).convert("RGB")  # 将响应内容转为图像并转换为RGB模式
        >>> init_image = init_image.resize((768, 512))  # 调整图像大小为768x512

        >>> # "A fantasy landscape, trending on artstation"  # 提示字符串,用于生成图像
        >>> prompt = "幻想风景, artstation"  # 设置生成图像的描述性提示

        >>> images = pipe(prompt=prompt, image=init_image, strength=0.75, guidance_scale=7.5).images  # 调用管道生成图像
        >>> images[0].save("幻想风景.png")  # 保存生成的图像
        ```py  # 结束示例文档字符串
"""
# 从 diffusers.pipelines.stable_diffusion.pipeline_stable_diffusion_img2img 复制
def retrieve_latents(
    # 定义函数接收编码器输出(张量),可选的生成器,和采样模式字符串
    encoder_output: torch.Tensor, generator: Optional[torch.Generator] = None, sample_mode: str = "sample"
):
    # 检查编码器输出是否具有 'latent_dist' 属性,并且采样模式为 'sample'
    if hasattr(encoder_output, "latent_dist") and sample_mode == "sample":
        # 从 'latent_dist' 中进行采样并返回结果
        return encoder_output.latent_dist.sample(generator)
    # 检查编码器输出是否具有 'latent_dist' 属性,并且采样模式为 'argmax'
    elif hasattr(encoder_output, "latent_dist") and sample_mode == "argmax":
        # 返回 'latent_dist' 的众数
        return encoder_output.latent_dist.mode()
    # 检查编码器输出是否具有 'latents' 属性
    elif hasattr(encoder_output, "latents"):
        # 直接返回 'latents'
        return encoder_output.latents
    else:
        # 如果没有有效的属性,抛出属性错误
        raise AttributeError("Could not access latents of provided encoder_output")


# 从 diffusers.pipelines.stable_diffusion.pipeline_stable_diffusion_img2img 复制
def preprocess(image):
    # 定义弃用消息,指明该方法将在 diffusers 1.0.0 中被移除
    deprecation_message = "The preprocess method is deprecated and will be removed in diffusers 1.0.0. Please use VaeImageProcessor.preprocess(...) instead"
    # 发出弃用警告
    deprecate("preprocess", "1.0.0", deprecation_message, standard_warn=False)
    # 检查图像是否为张量类型
    if isinstance(image, torch.Tensor):
        # 如果是张量,直接返回
        return image
    # 检查图像是否为 PIL 图像
    elif isinstance(image, PIL.Image.Image):
        # 如果是单个 PIL 图像,转为列表
        image = [image]

    # 检查列表中的第一个元素是否为 PIL 图像
    if isinstance(image[0], PIL.Image.Image):
        # 获取图像的宽和高
        w, h = image[0].size
        # 将宽和高调整为8的整数倍
        w, h = (x - x % 8 for x in (w, h))  # resize to integer multiple of 8

        # 调整每个图像的大小并转换为 NumPy 数组
        image = [np.array(i.resize((w, h), resample=PIL_INTERPOLATION["lanczos"]))[None, :] for i in image]
        # 将所有图像沿第0维连接
        image = np.concatenate(image, axis=0)
        # 归一化图像数组
        image = np.array(image).astype(np.float32) / 255.0
        # 调整数组维度顺序
        image = image.transpose(0, 3, 1, 2)
        # 将像素值缩放到 [-1, 1]
        image = 2.0 * image - 1.0
        # 将 NumPy 数组转换为张量
        image = torch.from_numpy(image)
    # 检查列表中的第一个元素是否为张量
    elif isinstance(image[0], torch.Tensor):
        # 沿着第0维连接所有张量
        image = torch.cat(image, dim=0)
    # 返回处理后的图像
    return image


# 从 diffusers.pipelines.stable_diffusion.pipeline_stable_diffusion 复制
def retrieve_timesteps(
    # 定义函数接收调度器、可选的推理步骤数量、设备、时间步和标准差列表以及额外参数
    scheduler,
    num_inference_steps: Optional[int] = None,
    device: Optional[Union[str, torch.device]] = None,
    timesteps: Optional[List[int]] = None,
    sigmas: Optional[List[float]] = None,
    **kwargs,
):
    """
    调用调度器的 `set_timesteps` 方法,并在调用后从调度器检索时间步。处理自定义时间步。任何 kwargs 将被传递给 `scheduler.set_timesteps`。
    # 参数说明部分
    Args:
        scheduler (`SchedulerMixin`):  # 接受一个调度器混合类实例,用于获取时间步
            The scheduler to get timesteps from.
        num_inference_steps (`int`):  # 用于生成样本的扩散步数
            The number of diffusion steps used when generating samples with a pre-trained model. If used, `timesteps`
            must be `None`.
        device (`str` or `torch.device`, *optional*):  # 指定时间步所移动到的设备,若为 None,则不移动
            The device to which the timesteps should be moved to. If `None`, the timesteps are not moved.
        timesteps (`List[int]`, *optional*):  # 自定义时间步,覆盖调度器的时间步间隔策略
            Custom timesteps used to override the timestep spacing strategy of the scheduler. If `timesteps` is passed,
            `num_inference_steps` and `sigmas` must be `None`.
        sigmas (`List[float]`, *optional*):  # 自定义 sigma,覆盖调度器的时间步间隔策略
            Custom sigmas used to override the timestep spacing strategy of the scheduler. If `sigmas` is passed,
            `num_inference_steps` and `timesteps` must be `None`.

    # 返回值说明
    Returns:
        `Tuple[torch.Tensor, int]`: A tuple where the first element is the timestep schedule from the scheduler and the
        second element is the number of inference steps.
    """
    # 检查是否同时传入时间步和 sigma,抛出异常
    if timesteps is not None and sigmas is not None:
        raise ValueError("Only one of `timesteps` or `sigmas` can be passed. Please choose one to set custom values")
    # 如果传入时间步
    if timesteps is not None:
        # 检查调度器是否支持自定义时间步
        accepts_timesteps = "timesteps" in set(inspect.signature(scheduler.set_timesteps).parameters.keys())
        if not accepts_timesteps:
            raise ValueError(  # 抛出错误提示当前调度器不支持自定义时间步
                f"The current scheduler class {scheduler.__class__}'s `set_timesteps` does not support custom"
                f" timestep schedules. Please check whether you are using the correct scheduler."
            )
        # 设置调度器的时间步
        scheduler.set_timesteps(timesteps=timesteps, device=device, **kwargs)
        # 获取设置后的时间步
        timesteps = scheduler.timesteps
        # 计算推断步骤数量
        num_inference_steps = len(timesteps)
    # 如果传入 sigma
    elif sigmas is not None:
        # 检查调度器是否支持自定义 sigma
        accept_sigmas = "sigmas" in set(inspect.signature(scheduler.set_timesteps).parameters.keys())
        if not accept_sigmas:
            raise ValueError(  # 抛出错误提示当前调度器不支持自定义 sigma
                f"The current scheduler class {scheduler.__class__}'s `set_timesteps` does not support custom"
                f" sigmas schedules. Please check whether you are using the correct scheduler."
            )
        # 设置调度器的 sigma
        scheduler.set_timesteps(sigmas=sigmas, device=device, **kwargs)
        # 获取设置后的时间步
        timesteps = scheduler.timesteps
        # 计算推断步骤数量
        num_inference_steps = len(timesteps)
    # 如果没有传入时间步和 sigma
    else:
        # 根据推断步骤数量设置调度器的时间步
        scheduler.set_timesteps(num_inference_steps, device=device, **kwargs)
        # 获取设置后的时间步
        timesteps = scheduler.timesteps
    # 返回时间步和推断步骤数量
    return timesteps, num_inference_steps
# 定义一个名为 AltDiffusionImg2ImgPipeline 的类,继承自多个父类
class AltDiffusionImg2ImgPipeline(
    # 继承自 DiffusionPipeline
    DiffusionPipeline,
    # 继承自 StableDiffusionMixin
    StableDiffusionMixin,
    # 继承自 TextualInversionLoaderMixin
    TextualInversionLoaderMixin,
    # 继承自 IPAdapterMixin
    IPAdapterMixin,
    # 继承自 StableDiffusionLoraLoaderMixin
    StableDiffusionLoraLoaderMixin,
    # 继承自 FromSingleFileMixin
    FromSingleFileMixin,
):
    # 文档字符串,描述该类的作用和参数
    r"""
    Pipeline for text-guided image-to-image generation using Alt Diffusion.

    # 描述该模型的功能:基于文本指导的图像到图像生成
    This model inherits from [`DiffusionPipeline`]. Check the superclass documentation for the generic methods
    implemented for all pipelines (downloading, saving, running on a particular device, etc.).

    # 说明该管道继承了一些加载方法
    The pipeline also inherits the following loading methods:
        - [`~loaders.TextualInversionLoaderMixin.load_textual_inversion`] for loading textual inversion embeddings
        - [`~loaders.StableDiffusionLoraLoaderMixin.load_lora_weights`] for loading LoRA weights
        - [`~loaders.StableDiffusionLoraLoaderMixin.save_lora_weights`] for saving LoRA weights
        - [`~loaders.FromSingleFileMixin.from_single_file`] for loading `.ckpt` files
        - [`~loaders.IPAdapterMixin.load_ip_adapter`] for loading IP Adapters

    # 定义构造函数的参数
    Args:
        vae ([`AutoencoderKL`]):
            # Variational Auto-Encoder (VAE) 模型,用于图像的编码和解码
            Variational Auto-Encoder (VAE) model to encode and decode images to and from latent representations.
        text_encoder ([`~transformers.RobertaSeriesModelWithTransformation`]):
            # 冻结的文本编码器,用于处理输入文本
            Frozen text-encoder ([clip-vit-large-patch14](https://huggingface.co/openai/clip-vit-large-patch14)).
        tokenizer ([`~transformers.XLMRobertaTokenizer`]):
            # 用于对文本进行分词的工具
            A `XLMRobertaTokenizer` to tokenize text.
        unet ([`UNet2DConditionModel`]):
            # 用于去噪图像潜在表示的 UNet 模型
            A `UNet2DConditionModel` to denoise the encoded image latents.
        scheduler ([`SchedulerMixin`]):
            # 与 UNet 结合使用的调度器,用于去噪
            A scheduler to be used in combination with `unet` to denoise the encoded image latents. Can be one of
            [`DDIMScheduler`], [`LMSDiscreteScheduler`], or [`PNDMScheduler`].
        safety_checker ([`StableDiffusionSafetyChecker`]):
            # 评估生成图像是否可能被视为冒犯或有害的分类模块
            Classification module that estimates whether generated images could be considered offensive or harmful.
            Please refer to the [model card](https://huggingface.co/runwayml/stable-diffusion-v1-5) for more details
            about a model's potential harms.
        feature_extractor ([`~transformers.CLIPImageProcessor`]):
            # 用于从生成图像中提取特征的处理器
            A `CLIPImageProcessor` to extract features from generated images; used as inputs to the `safety_checker`.
    """

    # 定义模型在 CPU 上的卸载顺序
    model_cpu_offload_seq = "text_encoder->image_encoder->unet->vae"
    # 可选组件列表
    _optional_components = ["safety_checker", "feature_extractor", "image_encoder"]
    # 从 CPU 卸载中排除的组件
    _exclude_from_cpu_offload = ["safety_checker"]
    # 用于回调的张量输入列表
    _callback_tensor_inputs = ["latents", "prompt_embeds", "negative_prompt_embeds"]
    # 初始化方法,用于创建类的实例
        def __init__(
            # VAE(变分自编码器)实例,用于数据编码
            self,
            vae: AutoencoderKL,
            # 文本编码器,用于处理文本输入
            text_encoder: RobertaSeriesModelWithTransformation,
            # 分词器,用于将文本转换为标记
            tokenizer: XLMRobertaTokenizer,
            # UNet 模型,用于图像生成
            unet: UNet2DConditionModel,
            # 调度器,用于控制生成过程中的时间步
            scheduler: KarrasDiffusionSchedulers,
            # 安全检查器,用于过滤不安全的内容
            safety_checker: StableDiffusionSafetyChecker,
            # 特征提取器,用于图像处理
            feature_extractor: CLIPImageProcessor,
            # 可选的图像编码器,用于处理图像输入
            image_encoder: CLIPVisionModelWithProjection = None,
            # 指示是否需要安全检查器的布尔值,默认为 True
            requires_safety_checker: bool = True,
        # 编码提示的方法
        def _encode_prompt(
            self,
            # 提示文本,输入的描述信息
            prompt,
            # 设备类型,指定计算设备(CPU或GPU)
            device,
            # 每个提示生成的图像数量
            num_images_per_prompt,
            # 是否执行分类器自由引导的布尔值
            do_classifier_free_guidance,
            # 可选的负面提示文本
            negative_prompt=None,
            # 可选的提示嵌入,提前计算的提示表示
            prompt_embeds: Optional[torch.Tensor] = None,
            # 可选的负面提示嵌入,提前计算的负面提示表示
            negative_prompt_embeds: Optional[torch.Tensor] = None,
            # 可选的 LoRA 比例,用于模型调优
            lora_scale: Optional[float] = None,
            # 其他可选参数
            **kwargs,
        ):
            # 警告信息,说明该方法已弃用并将在未来版本中删除,建议使用新的方法
            deprecation_message = "`_encode_prompt()` is deprecated and it will be removed in a future version. Use `encode_prompt()` instead. Also, be aware that the output format changed from a concatenated tensor to a tuple."
            # 调用弃用函数的警告方法,记录弃用信息
            deprecate("_encode_prompt()", "1.0.0", deprecation_message, standard_warn=False)
    
            # 调用新的编码方法,获取提示嵌入元组
            prompt_embeds_tuple = self.encode_prompt(
                # 提示文本
                prompt=prompt,
                # 计算设备
                device=device,
                # 每个提示生成的图像数量
                num_images_per_prompt=num_images_per_prompt,
                # 是否执行分类器自由引导
                do_classifier_free_guidance=do_classifier_free_guidance,
                # 可选的负面提示文本
                negative_prompt=negative_prompt,
                # 可选的提示嵌入
                prompt_embeds=prompt_embeds,
                # 可选的负面提示嵌入
                negative_prompt_embeds=negative_prompt_embeds,
                # 可选的 LoRA 比例
                lora_scale=lora_scale,
                # 传递其他参数
                **kwargs,
            )
    
            # 将嵌入元组的两个部分连接在一起,便于后续处理
            prompt_embeds = torch.cat([prompt_embeds_tuple[1], prompt_embeds_tuple[0]])
    
            # 返回连接后的提示嵌入
            return prompt_embeds
    
        # 编码提示的方法,用于处理输入的提示文本
        def encode_prompt(
            self,
            # 提示文本
            prompt,
            # 计算设备
            device,
            # 每个提示生成的图像数量
            num_images_per_prompt,
            # 是否执行分类器自由引导
            do_classifier_free_guidance,
            # 可选的负面提示文本
            negative_prompt=None,
            # 可选的提示嵌入
            prompt_embeds: Optional[torch.Tensor] = None,
            # 可选的负面提示嵌入
            negative_prompt_embeds: Optional[torch.Tensor] = None,
            # 可选的 LoRA 比例
            lora_scale: Optional[float] = None,
            # 可选的跳过剪辑层的参数
            clip_skip: Optional[int] = None,
    # 定义编码图像的方法,接受图像、设备、每个提示的图像数量和隐藏状态输出参数
        def encode_image(self, image, device, num_images_per_prompt, output_hidden_states=None):
            # 获取图像编码器参数的数据类型
            dtype = next(self.image_encoder.parameters()).dtype
    
            # 检查输入是否为张量,若不是则进行特征提取并转换为张量
            if not isinstance(image, torch.Tensor):
                image = self.feature_extractor(image, return_tensors="pt").pixel_values
    
            # 将图像移动到指定设备并转换为相应数据类型
            image = image.to(device=device, dtype=dtype)
            # 如果需要输出隐藏状态
            if output_hidden_states:
                # 通过图像编码器编码图像,并获取倒数第二个隐藏状态
                image_enc_hidden_states = self.image_encoder(image, output_hidden_states=True).hidden_states[-2]
                # 根据每个提示的图像数量重复隐藏状态
                image_enc_hidden_states = image_enc_hidden_states.repeat_interleave(num_images_per_prompt, dim=0)
                # 对于无条件图像,创建零张量并编码以获取隐藏状态
                uncond_image_enc_hidden_states = self.image_encoder(
                    torch.zeros_like(image), output_hidden_states=True
                ).hidden_states[-2]
                # 同样根据每个提示的图像数量重复无条件隐藏状态
                uncond_image_enc_hidden_states = uncond_image_enc_hidden_states.repeat_interleave(
                    num_images_per_prompt, dim=0
                )
                # 返回编码后的图像隐藏状态和无条件隐藏状态
                return image_enc_hidden_states, uncond_image_enc_hidden_states
            else:
                # 编码图像并获取图像嵌入
                image_embeds = self.image_encoder(image).image_embeds
                # 根据每个提示的图像数量重复图像嵌入
                image_embeds = image_embeds.repeat_interleave(num_images_per_prompt, dim=0)
                # 创建与图像嵌入相同形状的零张量作为无条件图像嵌入
                uncond_image_embeds = torch.zeros_like(image_embeds)
    
                # 返回图像嵌入和无条件图像嵌入
                return image_embeds, uncond_image_embeds
    
        # 定义运行安全检查器的方法,接受图像、设备和数据类型
        def run_safety_checker(self, image, device, dtype):
            # 如果安全检查器不存在,设置无NSFW概念标志为None
            if self.safety_checker is None:
                has_nsfw_concept = None
            else:
                # 检查图像是否为张量,若是则进行后处理
                if torch.is_tensor(image):
                    feature_extractor_input = self.image_processor.postprocess(image, output_type="pil")
                else:
                    # 将NumPy数组转换为PIL图像
                    feature_extractor_input = self.image_processor.numpy_to_pil(image)
                # 提取特征并移动到设备
                safety_checker_input = self.feature_extractor(feature_extractor_input, return_tensors="pt").to(device)
                # 运行安全检查器并返回处理后的图像和NSFW概念标志
                image, has_nsfw_concept = self.safety_checker(
                    images=image, clip_input=safety_checker_input.pixel_values.to(dtype)
                )
            # 返回处理后的图像和NSFW概念标志
            return image, has_nsfw_concept
    
        # 定义解码潜在变量的方法
        def decode_latents(self, latents):
            # 定义弃用提示信息
            deprecation_message = "The decode_latents method is deprecated and will be removed in 1.0.0. Please use VaeImageProcessor.postprocess(...) instead"
            # 发出弃用警告
            deprecate("decode_latents", "1.0.0", deprecation_message, standard_warn=False)
    
            # 对潜在变量进行缩放
            latents = 1 / self.vae.config.scaling_factor * latents
            # 解码潜在变量以生成图像
            image = self.vae.decode(latents, return_dict=False)[0]
            # 将图像进行归一化处理并限制范围
            image = (image / 2 + 0.5).clamp(0, 1)
            # 将图像转换为float32格式,方便与bfloat16兼容且不造成显著开销
            image = image.cpu().permute(0, 2, 3, 1).float().numpy()
            # 返回处理后的图像
            return image
    # 准备调度器步骤所需的额外参数,因不同调度器的参数签名可能不同
    def prepare_extra_step_kwargs(self, generator, eta):
        # eta (η) 仅用于 DDIMScheduler,其他调度器将忽略它
        # eta 对应于 DDIM 论文中的 η: https://arxiv.org/abs/2010.02502
        # eta 的值应在 [0, 1] 之间
    
        # 检查调度器的步骤方法是否接受 eta 参数
        accepts_eta = "eta" in set(inspect.signature(self.scheduler.step).parameters.keys())
        # 初始化额外参数字典
        extra_step_kwargs = {}
        # 如果接受 eta,则将其添加到额外参数字典中
        if accepts_eta:
            extra_step_kwargs["eta"] = eta
    
        # 检查调度器的步骤方法是否接受 generator 参数
        accepts_generator = "generator" in set(inspect.signature(self.scheduler.step).parameters.keys())
        # 如果接受 generator,则将其添加到额外参数字典中
        if accepts_generator:
            extra_step_kwargs["generator"] = generator
        # 返回包含额外参数的字典
        return extra_step_kwargs
    
    # 检查输入参数的有效性
    def check_inputs(
        self,
        prompt,
        strength,
        callback_steps,
        negative_prompt=None,
        prompt_embeds=None,
        negative_prompt_embeds=None,
        callback_on_step_end_tensor_inputs=None,
    ):
        # 检查 strength 参数是否在有效范围内 (0 到 1)
        if strength < 0 or strength > 1:
            # 如果不在范围内,抛出值错误
            raise ValueError(f"The value of strength should in [0.0, 1.0] but is {strength}")

        # 检查 callback_steps 是否为正整数
        if callback_steps is not None and (not isinstance(callback_steps, int) or callback_steps <= 0):
            # 如果不是正整数,抛出值错误
            raise ValueError(
                f"`callback_steps` has to be a positive integer but is {callback_steps} of type"
                f" {type(callback_steps)}."
            )

        # 检查 callback_on_step_end_tensor_inputs 中的每个键是否在 self._callback_tensor_inputs 中
        if callback_on_step_end_tensor_inputs is not None and not all(
            k in self._callback_tensor_inputs for k in callback_on_step_end_tensor_inputs
        ):
            # 如果有键不在列表中,抛出值错误
            raise ValueError(
                f"`callback_on_step_end_tensor_inputs` has to be in {self._callback_tensor_inputs}, but found {[k for k in callback_on_step_end_tensor_inputs if k not in self._callback_tensor_inputs]}"
            )
        # 检查是否同时提供了 prompt 和 prompt_embeds
        if prompt is not None and prompt_embeds is not None:
            # 如果同时提供,抛出值错误
            raise ValueError(
                f"Cannot forward both `prompt`: {prompt} and `prompt_embeds`: {prompt_embeds}. Please make sure to"
                " only forward one of the two."
            )
        # 检查 prompt 和 prompt_embeds 是否都为 None
        elif prompt is None and prompt_embeds is None:
            # 如果都为 None,抛出值错误
            raise ValueError(
                "Provide either `prompt` or `prompt_embeds`. Cannot leave both `prompt` and `prompt_embeds` undefined."
            )
        # 检查 prompt 的类型是否为 str 或 list
        elif prompt is not None and (not isinstance(prompt, str) and not isinstance(prompt, list)):
            # 如果类型不正确,抛出值错误
            raise ValueError(f"`prompt` has to be of type `str` or `list` but is {type(prompt)}")

        # 检查是否同时提供了 negative_prompt 和 negative_prompt_embeds
        if negative_prompt is not None and negative_prompt_embeds is not None:
            # 如果同时提供,抛出值错误
            raise ValueError(
                f"Cannot forward both `negative_prompt`: {negative_prompt} and `negative_prompt_embeds`:"
                f" {negative_prompt_embeds}. Please make sure to only forward one of the two."
            )

        # 检查 prompt_embeds 和 negative_prompt_embeds 是否都不为 None
        if prompt_embeds is not None and negative_prompt_embeds is not None:
            # 检查它们的形状是否一致
            if prompt_embeds.shape != negative_prompt_embeds.shape:
                # 如果形状不一致,抛出值错误
                raise ValueError(
                    "`prompt_embeds` and `negative_prompt_embeds` must have the same shape when passed directly, but"
                    f" got: `prompt_embeds` {prompt_embeds.shape} != `negative_prompt_embeds`"
                    f" {negative_prompt_embeds.shape}."
                )

    def get_timesteps(self, num_inference_steps, strength, device):
        # 根据初始化时间步计算原始时间步
        init_timestep = min(int(num_inference_steps * strength), num_inference_steps)

        # 计算时间步的起始位置
        t_start = max(num_inference_steps - init_timestep, 0)
        # 从调度器中获取相应的时间步
        timesteps = self.scheduler.timesteps[t_start * self.scheduler.order :]

        # 返回计算的时间步和剩余推理步骤
        return timesteps, num_inference_steps - t_start
    # 准备潜在向量(latents)以便于后续处理
    def prepare_latents(self, image, timestep, batch_size, num_images_per_prompt, dtype, device, generator=None):
        # 检查输入的 image 是否为指定类型之一:torch.Tensor、PIL.Image.Image 或列表
        if not isinstance(image, (torch.Tensor, PIL.Image.Image, list)):
            raise ValueError(
                # 抛出异常,告知 image 的类型不符合要求
                f"`image` has to be of type `torch.Tensor`, `PIL.Image.Image` or list but is {type(image)}"
            )

        # 将 image 转移到指定的设备并转换为指定的数据类型
        image = image.to(device=device, dtype=dtype)

        # 计算有效的 batch_size,即每个提示的图像数量
        batch_size = batch_size * num_images_per_prompt

        # 如果 image 的通道数为 4,直接将其赋值为初始化潜在向量
        if image.shape[1] == 4:
            init_latents = image

        else:
            # 如果 generator 是列表且其长度与 batch_size 不匹配,抛出异常
            if isinstance(generator, list) and len(generator) != batch_size:
                raise ValueError(
                    # 抛出异常,告知 generator 的长度与请求的 batch_size 不匹配
                    f"You have passed a list of generators of length {len(generator)}, but requested an effective batch"
                    f" size of {batch_size}. Make sure the batch size matches the length of the generators."
                )

            # 如果 generator 是列表,遍历每个生成器以获取潜在向量
            elif isinstance(generator, list):
                init_latents = [
                    # 对每个图像进行编码并检索对应的潜在向量
                    retrieve_latents(self.vae.encode(image[i : i + 1]), generator=generator[i])
                    for i in range(batch_size)
                ]
                # 将所有的潜在向量合并成一个张量
                init_latents = torch.cat(init_latents, dim=0)
            else:
                # 如果 generator 不是列表,直接对整个图像进行编码并检索潜在向量
                init_latents = retrieve_latents(self.vae.encode(image), generator=generator)

            # 将潜在向量乘以配置中的缩放因子
            init_latents = self.vae.config.scaling_factor * init_latents

        # 检查请求的 batch_size 是否大于初始化潜在向量的数量并且能够整除
        if batch_size > init_latents.shape[0] and batch_size % init_latents.shape[0] == 0:
            # 扩展 init_latents 以满足 batch_size
            deprecation_message = (
                # 构造弃用警告消息
                f"You have passed {batch_size} text prompts (`prompt`), but only {init_latents.shape[0]} initial"
                " images (`image`). Initial images are now duplicating to match the number of text prompts. Note"
                " that this behavior is deprecated and will be removed in a version 1.0.0. Please make sure to update"
                " your script to pass as many initial images as text prompts to suppress this warning."
            )
            # 记录弃用警告
            deprecate("len(prompt) != len(image)", "1.0.0", deprecation_message, standard_warn=False)
            # 计算每个提示需要的额外图像数量
            additional_image_per_prompt = batch_size // init_latents.shape[0]
            # 复制初始化潜在向量以满足 batch_size
            init_latents = torch.cat([init_latents] * additional_image_per_prompt, dim=0)
        # 如果 batch_size 大于初始化潜在向量的数量但不能整除,抛出异常
        elif batch_size > init_latents.shape[0] and batch_size % init_latents.shape[0] != 0:
            raise ValueError(
                # 抛出异常,告知不能将潜在向量重复以满足 batch_size
                f"Cannot duplicate `image` of batch size {init_latents.shape[0]} to {batch_size} text prompts."
            )
        else:
            # 如果 batch_size 合法,则将 init_latents 扩展为单一张量
            init_latents = torch.cat([init_latents], dim=0)

        # 获取初始化潜在向量的形状
        shape = init_latents.shape
        # 生成与潜在向量形状相同的随机噪声
        noise = randn_tensor(shape, generator=generator, device=device, dtype=dtype)

        # 将噪声添加到潜在向量中
        init_latents = self.scheduler.add_noise(init_latents, noise, timestep)
        # 将处理后的潜在向量赋值给 latents
        latents = init_latents

        # 返回最终的潜在向量
        return latents
    # 定义获取指导尺度嵌入的函数,接收输入参数 w、嵌入维度和数据类型
    def get_guidance_scale_embedding(self, w, embedding_dim=512, dtype=torch.float32):
        """
        查看关于嵌入生成的更多信息,链接到相关 GitHub 页面

        参数:
            timesteps (`torch.Tensor`):
                在这些时间步生成嵌入向量
            embedding_dim (`int`, *可选*, 默认值为 512):
                生成的嵌入的维度
            dtype:
                生成嵌入的数据类型

        返回:
            `torch.Tensor`: 形状为 `(len(timesteps), embedding_dim)` 的嵌入向量
        """
        # 确保输入的 w 是一维的
        assert len(w.shape) == 1
        # 将 w 放大 1000 倍
        w = w * 1000.0

        # 计算嵌入维度的一半
        half_dim = embedding_dim // 2
        # 计算 log(10000) 除以 (half_dim - 1) 的值,用于后续计算
        emb = torch.log(torch.tensor(10000.0)) / (half_dim - 1)
        # 生成从 0 到 half_dim 的指数衰减的嵌入
        emb = torch.exp(torch.arange(half_dim, dtype=dtype) * -emb)
        # 将 w 转换为指定数据类型,并生成与 emb 组合的嵌入
        emb = w.to(dtype)[:, None] * emb[None, :]
        # 将正弦和余弦嵌入连接在一起
        emb = torch.cat([torch.sin(emb), torch.cos(emb)], dim=1)
        # 如果嵌入维度是奇数,则在最后填充一个零
        if embedding_dim % 2 == 1:  # zero pad
            emb = torch.nn.functional.pad(emb, (0, 1))
        # 确保最终嵌入的形状与预期一致
        assert emb.shape == (w.shape[0], embedding_dim)
        # 返回生成的嵌入
        return emb

    # 定义属性,返回内部的指导尺度
    @property
    def guidance_scale(self):
        return self._guidance_scale

    # 定义属性,返回内部的剪辑跳过值
    @property
    def clip_skip(self):
        return self._clip_skip

    # 这里 `guidance_scale` 类似于公式 (2) 中的指导权重 `w`
    # 在 Imagen 论文中: https://arxiv.org/pdf/2205.11487.pdf . `guidance_scale = 1`
    # 对应于不进行分类器自由指导。
    @property
    def do_classifier_free_guidance(self):
        # 检查指导尺度是否大于 1 且时间条件投影维度是否为空
        return self._guidance_scale > 1 and self.unet.config.time_cond_proj_dim is None

    # 定义属性,返回交叉注意力的关键字参数
    @property
    def cross_attention_kwargs(self):
        return self._cross_attention_kwargs

    # 定义属性,返回时间步的数量
    @property
    def num_timesteps(self):
        return self._num_timesteps

    # 在不计算梯度的情况下,装饰后续函数
    @torch.no_grad()
    # 替换示例文档字符串
    @replace_example_docstring(EXAMPLE_DOC_STRING)
    # 定义一个可调用的方法,允许通过不同参数进行调用
        def __call__(
            # 输入的提示文本,可以是字符串或字符串列表
            self,
            prompt: Union[str, List[str]] = None,
            # 输入的图像,类型为 PipelineImageInput
            image: PipelineImageInput = None,
            # 控制生成强度的参数,默认值为 0.8
            strength: float = 0.8,
            # 推理步骤的数量,默认为 50
            num_inference_steps: Optional[int] = 50,
            # 可选的时间步列表
            timesteps: List[int] = None,
            # 可选的 sigma 值列表
            sigmas: List[float] = None,
            # 引导缩放因子,默认为 7.5
            guidance_scale: Optional[float] = 7.5,
            # 可选的负提示文本,可以是字符串或字符串列表
            negative_prompt: Optional[Union[str, List[str]]] = None,
            # 每个提示生成的图像数量,默认为 1
            num_images_per_prompt: Optional[int] = 1,
            # 可选的 eta 值,默认为 0.0
            eta: Optional[float] = 0.0,
            # 随机数生成器,可以是单个或列表形式
            generator: Optional[Union[torch.Generator, List[torch.Generator]]] = None,
            # 可选的提示嵌入,类型为 torch.Tensor
            prompt_embeds: Optional[torch.Tensor] = None,
            # 可选的负提示嵌入,类型为 torch.Tensor
            negative_prompt_embeds: Optional[torch.Tensor] = None,
            # 可选的图像输入,用于 IP 适配器
            ip_adapter_image: Optional[PipelineImageInput] = None,
            # 输出类型,默认为 "pil"
            output_type: Optional[str] = "pil",
            # 是否返回字典,默认为 True
            return_dict: bool = True,
            # 可选的交叉注意力参数字典
            cross_attention_kwargs: Optional[Dict[str, Any]] = None,
            # 可选的跳过剪辑参数
            clip_skip: int = None,
            # 在步骤结束时的可选回调函数
            callback_on_step_end: Optional[Callable[[int, int, Dict], None]] = None,
            # 在步骤结束时的张量输入列表,默认为 ["latents"]
            callback_on_step_end_tensor_inputs: List[str] = ["latents"],
            # 其他可变参数
            **kwargs,

.\diffusers\pipelines\deprecated\alt_diffusion\pipeline_output.py

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

# 导入 numpy 库并简写为 np
import numpy as np
# 导入 PIL.Image 模块
import PIL.Image

# 从上级模块导入 BaseOutput 类
from ....utils import (
    BaseOutput,
)


@dataclass
# 注释说明:从 diffusers.pipelines.stable_diffusion.pipeline_output 导入的类,Stable 替换为 Alt
class AltDiffusionPipelineOutput(BaseOutput):
    """
    Alt Diffusion 管道的输出类。

    参数:
        images (`List[PIL.Image.Image]` 或 `np.ndarray`)
            包含去噪的 PIL 图像的列表,长度为 `batch_size` 或形状为 `(batch_size, height, width,
            num_channels)` 的 NumPy 数组。
        nsfw_content_detected (`List[bool]`)
            列表,指示对应生成的图像是否包含 "不安全内容" (nsfw),
            如果无法进行安全检查,则为 `None`。
    """

    # 定义 images 属性,类型为列表或 NumPy 数组
    images: Union[List[PIL.Image.Image], np.ndarray]
    # 定义 nsfw_content_detected 属性,类型为可选列表
    nsfw_content_detected: Optional[List[bool]]

.\diffusers\pipelines\deprecated\alt_diffusion\__init__.py

# 导入类型检查相关的常量
from typing import TYPE_CHECKING

# 从上级模块导入工具函数和常量
from ....utils import (
    DIFFUSERS_SLOW_IMPORT,  # 慢导入的标志
    OptionalDependencyNotAvailable,  # 可选依赖不可用的异常
    _LazyModule,  # 懒加载模块的类
    get_objects_from_module,  # 从模块获取对象的函数
    is_torch_available,  # 检查 PyTorch 是否可用的函数
    is_transformers_available,  # 检查 Transformers 是否可用的函数
)

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

try:
    # 检查是否可用 Transformers 和 PyTorch
    if not (is_transformers_available() and is_torch_available()):
        raise OptionalDependencyNotAvailable()  # 抛出异常
except OptionalDependencyNotAvailable:
    # 导入虚拟对象的模块
    from ....utils import dummy_torch_and_transformers_objects

    # 更新虚拟对象字典
    _dummy_objects.update(get_objects_from_module(dummy_torch_and_transformers_objects))
else:
    # 添加可用的模型导入结构
    _import_structure["modeling_roberta_series"] = ["RobertaSeriesModelWithTransformation"]
    _import_structure["pipeline_alt_diffusion"] = ["AltDiffusionPipeline"]
    _import_structure["pipeline_alt_diffusion_img2img"] = ["AltDiffusionImg2ImgPipeline"]

    _import_structure["pipeline_output"] = ["AltDiffusionPipelineOutput"]

# 检查类型或慢导入标志
if TYPE_CHECKING or DIFFUSERS_SLOW_IMPORT:
    try:
        # 检查可用性
        if not (is_transformers_available() and is_torch_available()):
            raise OptionalDependencyNotAvailable()  # 抛出异常
    except OptionalDependencyNotAvailable:
        # 从虚拟对象模块导入所有内容
        from ....utils.dummy_torch_and_transformers_objects import *

    else:
        # 从相关模块导入具体类
        from .modeling_roberta_series import RobertaSeriesModelWithTransformation
        from .pipeline_alt_diffusion import AltDiffusionPipeline
        from .pipeline_alt_diffusion_img2img import AltDiffusionImg2ImgPipeline
        from .pipeline_output import AltDiffusionPipelineOutput

else:
    # 导入 sys 模块
    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\deprecated\audio_diffusion\mel.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.

# 导入 NumPy 库,供后续使用
import numpy as np  # noqa: E402

# 从配置工具导入混合类和注册装饰器
from ....configuration_utils import ConfigMixin, register_to_config
# 从调度工具导入调度混合类
from ....schedulers.scheduling_utils import SchedulerMixin

# 尝试导入 librosa 库
try:
    import librosa  # noqa: E402

    # 如果导入成功,设置标志为真
    _librosa_can_be_imported = True
    # 初始化导入错误信息为空
    _import_error = ""
# 捕获导入错误并设置相关标志和错误信息
except Exception as e:
    _librosa_can_be_imported = False
    # 设置错误信息,指示如何解决导入问题
    _import_error = (
        f"Cannot import librosa because {e}. Make sure to correctly install librosa to be able to install it."
    )

# 导入 PIL 库中的 Image 模块
from PIL import Image  # noqa: E402

# 定义 Mel 类,继承配置和调度混合类
class Mel(ConfigMixin, SchedulerMixin):
    """
    参数说明:
        x_res (`int`):
            频谱图的 x 方向分辨率(时间)。
        y_res (`int`):
            频谱图的 y 方向分辨率(频率区间)。
        sample_rate (`int`):
            音频的采样率。
        n_fft (`int`):
            快速傅里叶变换的数量。
        hop_length (`int`):
            每次移动的长度(当 `y_res` < 256 时,推荐更大的值)。
        top_db (`int`):
            最大分贝值。
        n_iter (`int`):
            Griffin-Lim Mel 反转的迭代次数。
    """

    # 指定配置文件名
    config_name = "mel_config.json"

    # 初始化方法,注册到配置中
    @register_to_config
    def __init__(
        self,
        x_res: int = 256,  # x 方向分辨率,默认值为 256
        y_res: int = 256,  # y 方向分辨率,默认值为 256
        sample_rate: int = 22050,  # 默认采样率为 22050
        n_fft: int = 2048,  # 默认 FFT 数量为 2048
        hop_length: int = 512,  # 默认移动长度为 512
        top_db: int = 80,  # 默认最大分贝值为 80
        n_iter: int = 32,  # 默认迭代次数为 32
    ):
        # 设置 hop_length 属性
        self.hop_length = hop_length
        # 设置采样率属性
        self.sr = sample_rate
        # 设置 FFT 数量属性
        self.n_fft = n_fft
        # 设置最大分贝值属性
        self.top_db = top_db
        # 设置迭代次数属性
        self.n_iter = n_iter
        # 调用方法设置频谱图分辨率
        self.set_resolution(x_res, y_res)
        # 初始化音频属性为 None
        self.audio = None

        # 检查 librosa 是否成功导入,若未导入则引发错误
        if not _librosa_can_be_imported:
            raise ValueError(_import_error)

    # 设置频谱图分辨率的方法
    def set_resolution(self, x_res: int, y_res: int):
        """设置分辨率。

        参数:
            x_res (`int`):
                频谱图的 x 方向分辨率(时间)。
            y_res (`int`):
                频谱图的 y 方向分辨率(频率区间)。
        """
        # 设置 x 方向分辨率
        self.x_res = x_res
        # 设置 y 方向分辨率
        self.y_res = y_res
        # 设置梅尔频率数量
        self.n_mels = self.y_res
        # 计算切片大小
        self.slice_size = self.x_res * self.hop_length - 1
    # 加载音频文件或原始音频数据
        def load_audio(self, audio_file: str = None, raw_audio: np.ndarray = None):
            """Load audio.
    
            Args:
                audio_file (`str`):
                    An audio file that must be on disk due to [Librosa](https://librosa.org/) limitation.
                raw_audio (`np.ndarray`):
                    The raw audio file as a NumPy array.
            """
            # 如果提供了音频文件名,则使用 Librosa 加载音频
            if audio_file is not None:
                self.audio, _ = librosa.load(audio_file, mono=True, sr=self.sr)
            # 否则,使用提供的原始音频数据
            else:
                self.audio = raw_audio
    
            # 如果音频长度不足,使用静音进行填充
            if len(self.audio) < self.x_res * self.hop_length:
                self.audio = np.concatenate([self.audio, np.zeros((self.x_res * self.hop_length - len(self.audio),))])
    
        # 获取音频切片的数量
        def get_number_of_slices(self) -> int:
            """Get number of slices in audio.
    
            Returns:
                `int`:
                    Number of spectograms audio can be sliced into.
            """
            # 返回音频长度除以每个切片的大小,得到切片数量
            return len(self.audio) // self.slice_size
    
        # 获取指定音频切片
        def get_audio_slice(self, slice: int = 0) -> np.ndarray:
            """Get slice of audio.
    
            Args:
                slice (`int`):
                    Slice number of audio (out of `get_number_of_slices()`).
    
            Returns:
                `np.ndarray`:
                    The audio slice as a NumPy array.
            """
            # 返回指定切片的音频数据
            return self.audio[self.slice_size * slice : self.slice_size * (slice + 1)]
    
        # 获取音频的采样率
        def get_sample_rate(self) -> int:
            """Get sample rate.
    
            Returns:
                `int`:
                    Sample rate of audio.
            """
            # 返回音频的采样率
            return self.sr
    
        # 将音频切片转换为声谱图
        def audio_slice_to_image(self, slice: int) -> Image.Image:
            """Convert slice of audio to spectrogram.
    
            Args:
                slice (`int`):
                    Slice number of audio to convert (out of `get_number_of_slices()`).
    
            Returns:
                `PIL Image`:
                    A grayscale image of `x_res x y_res`.
            """
            # 计算音频切片的梅尔声谱图
            S = librosa.feature.melspectrogram(
                y=self.get_audio_slice(slice), sr=self.sr, n_fft=self.n_fft, hop_length=self.hop_length, n_mels=self.n_mels
            )
            # 将声谱图转换为对数刻度
            log_S = librosa.power_to_db(S, ref=np.max, top_db=self.top_db)
            # 将对数声谱图归一化并转换为8位无符号整数
            bytedata = (((log_S + self.top_db) * 255 / self.top_db).clip(0, 255) + 0.5).astype(np.uint8)
            # 从数组创建图像
            image = Image.fromarray(bytedata)
            # 返回生成的图像
            return image
    # 定义一个将光谱图转换为音频的函数
    def image_to_audio(self, image: Image.Image) -> np.ndarray:
        """将光谱图转换为音频。
    
        参数:
            image (`PIL Image`):
                一个灰度图像,尺寸为 `x_res x y_res`。
    
        返回:
            audio (`np.ndarray`):
                以 NumPy 数组形式返回的音频。
        """
        # 将图像数据转换为字节并从字节缓冲区创建一个 NumPy 数组,数据类型为无符号 8 位整型
        bytedata = np.frombuffer(image.tobytes(), dtype="uint8").reshape((image.height, image.width))
        # 将字节数据转换为浮点数,并进行归一化处理,计算对数幅度谱
        log_S = bytedata.astype("float") * self.top_db / 255 - self.top_db
        # 将对数幅度谱转换为功率谱
        S = librosa.db_to_power(log_S)
        # 使用逆梅尔频谱将功率谱转换为音频信号
        audio = librosa.feature.inverse.mel_to_audio(
            S, sr=self.sr, n_fft=self.n_fft, hop_length=self.hop_length, n_iter=self.n_iter
        )
        # 返回生成的音频数组
        return audio

.\diffusers\pipelines\deprecated\audio_diffusion\pipeline_audio_diffusion.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 math import acos, sin
# 导入类型提示相关的类
from typing import List, Tuple, Union

# 导入 NumPy 库
import numpy as np
# 导入 PyTorch 库
import torch
# 导入图像处理库
from PIL import Image

# 从模型模块导入自编码器和 UNet 模型
from ....models import AutoencoderKL, UNet2DConditionModel
# 从调度器模块导入调度器类
from ....schedulers import DDIMScheduler, DDPMScheduler
# 从工具模块导入随机张量生成函数
from ....utils.torch_utils import randn_tensor
# 从管道工具模块导入音频输出和基本输出类
from ...pipeline_utils import AudioPipelineOutput, BaseOutput, DiffusionPipeline, ImagePipelineOutput
# 从音频处理模块导入 Mel 类
from .mel import Mel

# 定义音频扩散管道类,继承自扩散管道基类
class AudioDiffusionPipeline(DiffusionPipeline):
    """
    音频扩散管道。

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

    参数:
        vqae ([`AutoencoderKL`]):
            用于编码和解码图像到潜在表示的变分自编码器(VAE)模型。
        unet ([`UNet2DConditionModel`]):
            用于对编码图像潜在值去噪的 `UNet2DConditionModel`。
        mel ([`Mel`]):
            将音频转换为声谱图。
        scheduler ([`DDIMScheduler`] 或 [`DDPMScheduler`]):
            与 `unet` 一起使用的调度器,用于对编码图像潜在值去噪。可以是 [`DDIMScheduler`] 或 [`DDPMScheduler`] 中的任意一种。
    """

    # 定义可选组件的名称列表
    _optional_components = ["vqvae"]

    # 初始化方法,定义所需的参数
    def __init__(
        self,
        vqvae: AutoencoderKL,  # 变分自编码器模型
        unet: UNet2DConditionModel,  # UNet 模型
        mel: Mel,  # Mel 转换器
        scheduler: Union[DDIMScheduler, DDPMScheduler],  # 调度器
    ):
        super().__init__()  # 调用父类的初始化方法
        # 注册模型和组件
        self.register_modules(unet=unet, scheduler=scheduler, mel=mel, vqvae=vqvae)

    # 获取默认步骤数的方法
    def get_default_steps(self) -> int:
        """返回推荐的推理默认步骤数。

        返回:
            `int`:
                步骤数。
        """
        # 根据调度器类型返回对应的步骤数
        return 50 if isinstance(self.scheduler, DDIMScheduler) else 1000

    # 装饰器,指示以下方法不需要计算梯度
    @torch.no_grad()
    # 定义可调用对象,支持多种参数配置
        def __call__(
            self,  # 该方法可以被直接调用
            batch_size: int = 1,  # 批次大小,默认为 1
            audio_file: str = None,  # 音频文件的路径,默认为 None
            raw_audio: np.ndarray = None,  # 原始音频数据,默认为 None
            slice: int = 0,  # 切片起始位置,默认为 0
            start_step: int = 0,  # 起始步数,默认为 0
            steps: int = None,  # 总步数,默认为 None
            generator: torch.Generator = None,  # 随机数生成器,默认为 None
            mask_start_secs: float = 0,  # 掩码开始时间(秒),默认为 0
            mask_end_secs: float = 0,  # 掩码结束时间(秒),默认为 0
            step_generator: torch.Generator = None,  # 步数生成器,默认为 None
            eta: float = 0,  # 噪声控制参数,默认为 0
            noise: torch.Tensor = None,  # 噪声张量,默认为 None
            encoding: torch.Tensor = None,  # 编码张量,默认为 None
            return_dict=True,  # 是否返回字典格式的结果,默认为 True
        ) -> Union[  # 返回值的类型说明
            Union[AudioPipelineOutput, ImagePipelineOutput],  # 可返回音频或图像管道输出
            Tuple[List[Image.Image], Tuple[int, List[np.ndarray]]],  # 也可返回图像列表和元组
        @torch.no_grad()  # 关闭梯度计算以节省内存
        def encode(self, images: List[Image.Image], steps: int = 50) -> np.ndarray:  # 编码方法
            """
            反向去噪过程以恢复生成图像的噪声图像。
    
            参数:
                images (`List[PIL Image]`):  # 输入图像列表
                    要编码的图像列表。
                steps (`int`):  # 编码步数
                    执行的编码步数(默认为 `50`)。
    
            返回:
                `np.ndarray`:  # 返回的噪声张量
                    形状为 `(batch_size, 1, height, width)` 的噪声张量。
            """
    
            # 仅适用于 DDIM,因为此方法是确定性的
            assert isinstance(self.scheduler, DDIMScheduler)  # 确保调度器是 DDIM 类型
            self.scheduler.set_timesteps(steps)  # 设置调度器的时间步
            sample = np.array(  # 将输入图像转换为 NumPy 数组
                [np.frombuffer(image.tobytes(), dtype="uint8").reshape((1, image.height, image.width)) for image in images]  # 读取图像数据并重塑为适当形状
            )
            sample = (sample / 255) * 2 - 1  # 归一化样本到 [-1, 1] 范围
            sample = torch.Tensor(sample).to(self.device)  # 转换为 PyTorch 张量并移动到设备上
    
            for t in self.progress_bar(torch.flip(self.scheduler.timesteps, (0,))):  # 迭代时间步的反向顺序
                prev_timestep = t - self.scheduler.config.num_train_timesteps // self.scheduler.num_inference_steps  # 计算前一个时间步
                alpha_prod_t = self.scheduler.alphas_cumprod[t]  # 获取当前时间步的累积 alpha 值
                alpha_prod_t_prev = (  # 获取前一个时间步的累积 alpha 值
                    self.scheduler.alphas_cumprod[prev_timestep]  # 如果存在,则使用前一个时间步
                    if prev_timestep >= 0
                    else self.scheduler.final_alpha_cumprod  # 否则使用最终的累积 alpha 值
                )
                beta_prod_t = 1 - alpha_prod_t  # 计算 beta 值
                model_output = self.unet(sample, t)["sample"]  # 通过 UNet 模型获取输出样本
                pred_sample_direction = (1 - alpha_prod_t_prev) ** (0.5) * model_output  # 预测样本方向
                sample = (sample - pred_sample_direction) * alpha_prod_t_prev ** (-0.5)  # 更新样本
                sample = sample * alpha_prod_t ** (0.5) + beta_prod_t ** (0.5) * model_output  # 应用当前时间步的更新
    
            return sample  # 返回处理后的样本
    
        @staticmethod  # 标记为静态方法
    # 定义球面线性插值函数,接受两个张量和一个插值因子
    def slerp(x0: torch.Tensor, x1: torch.Tensor, alpha: float) -> torch.Tensor:
        """Spherical Linear intERPolation.
    
        Args:
            x0 (`torch.Tensor`):
                第一个用于插值的张量。
            x1 (`torch.Tensor`):
                第二个用于插值的张量。
            alpha (`float`):
                插值因子,范围在 0 到 1 之间
    
        Returns:
            `torch.Tensor`:
                插值后的张量。
        """
    
        # 计算两个张量之间的夹角 theta,使用余弦定理
        theta = acos(torch.dot(torch.flatten(x0), torch.flatten(x1)) / torch.norm(x0) / torch.norm(x1))
        # 根据球面线性插值公式计算并返回插值结果
        return sin((1 - alpha) * theta) * x0 / sin(theta) + sin(alpha * theta) * x1 / sin(theta)

.\diffusers\pipelines\deprecated\audio_diffusion\__init__.py

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

# 从上层模块导入 DIFFUSERS_SLOW_IMPORT 和 _LazyModule
from ....utils import DIFFUSERS_SLOW_IMPORT, _LazyModule

# 定义模块的导入结构,指定可导入的子模块及其内容
_import_structure = {
    "mel": ["Mel"],
    "pipeline_audio_diffusion": ["AudioDiffusionPipeline"],
}

# 根据条件选择性导入模块
if TYPE_CHECKING or DIFFUSERS_SLOW_IMPORT:
    # 从当前包中导入 Mel 类
    from .mel import Mel
    # 从当前包中导入 AudioDiffusionPipeline 类
    from .pipeline_audio_diffusion import AudioDiffusionPipeline

else:
    # 导入 sys 模块
    import sys

    # 将当前模块替换为 _LazyModule 的实例,实现延迟加载
    sys.modules[__name__] = _LazyModule(
        __name__,
        globals()["__file__"],
        _import_structure,
        module_spec=__spec__,
    )

.\diffusers\pipelines\deprecated\latent_diffusion_uncond\pipeline_latent_diffusion_uncond.py

# 版权所有 2024 HuggingFace 团队。保留所有权利。
#
# 根据 Apache 许可证第 2.0 版(“许可证”)授权;
# 除非符合许可证,否则您不得使用此文件。
# 您可以在以下网址获得许可证副本:
#
#     http://www.apache.org/licenses/LICENSE-2.0
#
# 除非适用法律要求或书面同意,软件在“按原样”基础上分发,
# 不提供任何形式的担保或条件,明示或暗示。
# 请参阅许可证以了解有关权限和
# 限制的具体信息。

# 导入检查模块,用于获取对象的源代码或签名信息
import inspect
# 从 typing 模块导入类型注解
from typing import List, Optional, Tuple, Union

# 导入 PyTorch 库
import torch

# 从相对路径导入 VQModel 和 UNet2DModel 模型
from ....models import UNet2DModel, VQModel
# 从相对路径导入 DDIMScheduler 调度器
from ....schedulers import DDIMScheduler
# 从工具模块导入随机张量生成函数
from ....utils.torch_utils import randn_tensor
# 从管道工具模块导入扩散管道和图像输出类
from ...pipeline_utils import DiffusionPipeline, ImagePipelineOutput


# 定义一个用于无条件图像生成的潜在扩散管道类
class LDMPipeline(DiffusionPipeline):
    r"""
    使用潜在扩散进行无条件图像生成的管道。

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

    参数:
        vqvae ([`VQModel`]):
            用于编码和解码图像到潜在表示的向量量化(VQ)模型。
        unet ([`UNet2DModel`]):
            用于对编码的图像潜在数据进行去噪的 `UNet2DModel`。
        scheduler ([`SchedulerMixin`]):
            [`DDIMScheduler`] 与 `unet` 一起使用以去噪编码的图像潜在数据。
    """

    # 初始化方法,用于创建 LDMPipeline 实例
    def __init__(self, vqvae: VQModel, unet: UNet2DModel, scheduler: DDIMScheduler):
        # 调用父类的初始化方法
        super().__init__()
        # 注册模型模块
        self.register_modules(vqvae=vqvae, unet=unet, scheduler=scheduler)

    # 禁用梯度计算,优化内存使用
    @torch.no_grad()
    def __call__(
        # 默认批次大小为 1
        self,
        batch_size: int = 1,
        # 可选的生成器,支持单个或多个生成器
        generator: Optional[Union[torch.Generator, List[torch.Generator]]] = None,
        # 默认的 eta 参数设置为 0.0
        eta: float = 0.0,
        # 默认推理步骤数量为 50
        num_inference_steps: int = 50,
        # 输出类型默认为 "pil"
        output_type: Optional[str] = "pil",
        # 返回字典标志,默认为 True
        return_dict: bool = True,
        # 接受额外参数
        **kwargs,

.\diffusers\pipelines\deprecated\latent_diffusion_uncond\__init__.py

# 导入类型检查的相关功能
from typing import TYPE_CHECKING

# 从相对路径导入工具模块中的常量
from ....utils import DIFFUSERS_SLOW_IMPORT, _LazyModule

# 定义模块导入结构,指定包含的子模块
_import_structure = {"pipeline_latent_diffusion_uncond": ["LDMPipeline"]}

# 如果处于类型检查阶段或慢导入标志为真,则导入指定的子模块
if TYPE_CHECKING or DIFFUSERS_SLOW_IMPORT:
    from .pipeline_latent_diffusion_uncond import LDMPipeline
else:
    # 否则,导入 sys 模块
    import sys

    # 使用懒加载模块将当前模块替换为 _LazyModule 实例
    sys.modules[__name__] = _LazyModule(
        __name__,
        globals()["__file__"],
        _import_structure,
        module_spec=__spec__,
    )

.\diffusers\pipelines\deprecated\pndm\pipeline_pndm.py

# 版权声明,声明此代码的版权所有者及使用许可信息
# 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 List, Optional, Tuple, Union

# 导入 PyTorch 库
import torch

# 从模型模块导入 UNet2DModel 类
from ....models import UNet2DModel
# 从调度器模块导入 PNDMScheduler 类
from ....schedulers import PNDMScheduler
# 从工具模块导入随机张量生成函数
from ....utils.torch_utils import randn_tensor
# 从管道工具模块导入 DiffusionPipeline 和 ImagePipelineOutput
from ...pipeline_utils import DiffusionPipeline, ImagePipelineOutput


# 定义无条件图像生成的管道类
class PNDMPipeline(DiffusionPipeline):
    r"""
    无条件图像生成的管道。

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

    参数:
        unet ([`UNet2DModel`]):
            用于去噪编码图像潜在值的 `UNet2DModel`。
        scheduler ([`PNDMScheduler`]):
            用于与 `unet` 结合使用以去噪编码图像的 `PNDMScheduler`。
    """

    # 定义 UNet2DModel 实例变量
    unet: UNet2DModel
    # 定义 PNDMScheduler 实例变量
    scheduler: PNDMScheduler

    # 初始化方法,接收 UNet2DModel 和 PNDMScheduler 实例
    def __init__(self, unet: UNet2DModel, scheduler: PNDMScheduler):
        # 调用父类的初始化方法
        super().__init__()

        # 从配置中创建 PNDMScheduler 实例
        scheduler = PNDMScheduler.from_config(scheduler.config)

        # 注册 unet 和 scheduler 模块
        self.register_modules(unet=unet, scheduler=scheduler)

    # 定义无梯度上下文中的可调用方法
    @torch.no_grad()
    def __call__(
        # 批处理大小,默认为 1
        self,
        batch_size: int = 1,
        # 推理步骤数,默认为 50
        num_inference_steps: int = 50,
        # 可选的随机数生成器,支持单个或多个生成器
        generator: Optional[Union[torch.Generator, List[torch.Generator]]] = None,
        # 输出类型,默认为 "pil"
        output_type: Optional[str] = "pil",
        # 是否返回字典形式的结果,默认为 True
        return_dict: bool = True,
        # 其他可选参数
        **kwargs,
    # 定义生成管道调用的函数,返回生成结果
    ) -> Union[ImagePipelineOutput, Tuple]:
        # 文档字符串,描述生成过程的函数调用及参数说明
        r"""
        生成管道的调用函数。
    
        参数:
            batch_size (`int`, `optional`, defaults to 1):
                生成图像的数量。
            num_inference_steps (`int`, `optional`, defaults to 50):
                去噪步骤的数量。更多的去噪步骤通常会导致更高质量的图像,但推理速度较慢。
            generator (`torch.Generator`, `optional`):
                用于生成确定性结果的 [`torch.Generator`](https://pytorch.org/docs/stable/generated/torch.Generator.html)。
            output_type (`str`, `optional`, defaults to `"pil"`):
                生成图像的输出格式。可以选择 `PIL.Image` 或 `np.array`。
            return_dict (`bool`, *optional*, defaults to `True`):
                是否返回 [`ImagePipelineOutput`] 而不是普通元组。
    
        示例:
    
        ```py
        >>> from diffusers import PNDMPipeline
    
        >>> # 加载模型和调度器
        >>> pndm = PNDMPipeline.from_pretrained("google/ddpm-cifar10-32")
    
        >>> # 在推理中运行管道(采样随机噪声并去噪)
        >>> image = pndm().images[0]
    
        >>> # 保存图像
        >>> image.save("pndm_generated_image.png")
        ```py
    
        返回:
            [`~pipelines.ImagePipelineOutput`] 或 `tuple`:
                如果 `return_dict` 为 `True`,则返回 [`~pipelines.ImagePipelineOutput`],否则返回一个元组,
                其中第一个元素是生成的图像列表。
        """
        # 采样方法的更多信息可以参考官方论文的算法2: https://arxiv.org/pdf/2202.09778.pdf
    
        # 生成高斯噪声以开始循环
        image = randn_tensor(
            # 创建形状为 (batch_size, 通道数, 样本大小, 样本大小) 的随机张量
            (batch_size, self.unet.config.in_channels, self.unet.config.sample_size, self.unet.config.sample_size),
            generator=generator,  # 使用指定的生成器
            device=self.device,  # 设置设备为指定的设备
        )
    
        # 设置调度器的时间步长
        self.scheduler.set_timesteps(num_inference_steps)
        # 遍历进度条中的每个时间步
        for t in self.progress_bar(self.scheduler.timesteps):
            # 通过 UNet 模型进行推理,获取模型输出
            model_output = self.unet(image, t).sample
    
            # 根据调度器步骤更新图像,获取前一帧样本
            image = self.scheduler.step(model_output, t, image).prev_sample
    
        # 将图像缩放到 [0, 1] 的范围
        image = (image / 2 + 0.5).clamp(0, 1)
        # 将图像从张量转为 numpy 数组,并调整维度顺序
        image = image.cpu().permute(0, 2, 3, 1).numpy()
        # 如果输出类型为 "pil",则将 numpy 数组转换为 PIL 图像
        if output_type == "pil":
            image = self.numpy_to_pil(image)
    
        # 如果不返回字典,返回元组格式
        if not return_dict:
            return (image,)
    
        # 返回包含生成图像的 ImagePipelineOutput 对象
        return ImagePipelineOutput(images=image)

.\diffusers\pipelines\deprecated\pndm\__init__.py

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

# 从上级目录的 utils 模块导入 DIFFUSERS_SLOW_IMPORT 和 _LazyModule
from ....utils import DIFFUSERS_SLOW_IMPORT, _LazyModule

# 定义模块导入结构,指定需要导入的子模块和类
_import_structure = {"pipeline_pndm": ["PNDMPipeline"]}

# 检查是否处于类型检查阶段或需要慢速导入
if TYPE_CHECKING or DIFFUSERS_SLOW_IMPORT:
    # 从子模块 pipeline_pndm 导入 PNDMPipeline 类
    from .pipeline_pndm import PNDMPipeline
else:
    # 如果不是类型检查阶段,则导入 sys 模块
    import sys

    # 将当前模块替换为 _LazyModule 对象,以实现懒加载功能
    sys.modules[__name__] = _LazyModule(
        __name__,  # 当前模块名称
        globals()["__file__"],  # 当前模块文件路径
        _import_structure,  # 导入结构
        module_spec=__spec__,  # 模块的规格
    )

.\diffusers\pipelines\deprecated\repaint\pipeline_repaint.py

# 版权声明,表明版权所有者和使用许可证
# Copyright 2024 ETH Zurich Computer Vision Lab and The HuggingFace Team. All rights reserved.
#
# 根据 Apache 许可证第 2.0 版("许可证")进行许可;
# 除非遵守许可证,否则您不得使用此文件。
# 您可以在以下网址获得许可证的副本:
#
#     http://www.apache.org/licenses/LICENSE-2.0
#
# 除非适用的法律要求或书面协议另有约定,软件按“原样”分发,
# 不提供任何明示或暗示的担保或条件。
# 请参见许可证以获取有关权限和限制的具体信息。

# 导入所需的类型
from typing import List, Optional, Tuple, Union

# 导入 numpy 库用于数组操作
import numpy as np
# 导入 PIL 库用于图像处理
import PIL.Image
# 导入 PyTorch 库用于深度学习模型
import torch

# 从本地模块导入 UNet2DModel 类
from ....models import UNet2DModel
# 从本地模块导入 RePaintScheduler 类
from ....schedulers import RePaintScheduler
# 从本地模块导入工具函数
from ....utils import PIL_INTERPOLATION, deprecate, logging
# 从本地模块导入随机张量生成函数
from ....utils.torch_utils import randn_tensor
# 从本地模块导入 DiffusionPipeline 和 ImagePipelineOutput 类
from ...pipeline_utils import DiffusionPipeline, ImagePipelineOutput

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

# 预处理图像的函数,支持多种输入格式
# Copied from diffusers.pipelines.stable_diffusion.pipeline_stable_diffusion_img2img.preprocess
def _preprocess_image(image: Union[List, PIL.Image.Image, torch.Tensor]):
    # 定义弃用信息,提示用户将来版本会删除此方法
    deprecation_message = "The preprocess method is deprecated and will be removed in diffusers 1.0.0. Please use VaeImageProcessor.preprocess(...) instead"
    # 调用弃用函数,发出警告
    deprecate("preprocess", "1.0.0", deprecation_message, standard_warn=False)
    # 如果输入是 PyTorch 张量,直接返回
    if isinstance(image, torch.Tensor):
        return image
    # 如果输入是 PIL 图像,转换为单元素列表
    elif isinstance(image, PIL.Image.Image):
        image = [image]

    # 如果列表中的第一个元素是 PIL 图像
    if isinstance(image[0], PIL.Image.Image):
        # 获取图像的宽度和高度
        w, h = image[0].size
        # 调整宽度和高度,使其为 8 的整数倍
        w, h = (x - x % 8 for x in (w, h))  # resize to integer multiple of 8

        # 调整图像大小并转换为 NumPy 数组,添加新维度
        image = [np.array(i.resize((w, h), resample=PIL_INTERPOLATION["lanczos"]))[None, :] for i in image]
        # 将所有图像沿第 0 维连接成一个数组
        image = np.concatenate(image, axis=0)
        # 转换为浮点型数组并归一化到 [0, 1]
        image = np.array(image).astype(np.float32) / 255.0
        # 调整数组维度顺序为 (批次, 通道, 高度, 宽度)
        image = image.transpose(0, 3, 1, 2)
        # 将像素值从 [0, 1] 映射到 [-1, 1]
        image = 2.0 * image - 1.0
        # 将 NumPy 数组转换为 PyTorch 张量
        image = torch.from_numpy(image)
    # 如果列表中的第一个元素是 PyTorch 张量
    elif isinstance(image[0], torch.Tensor):
        # 将张量沿第 0 维连接
        image = torch.cat(image, dim=0)
    # 返回处理后的图像
    return image

# 预处理掩码的函数,支持多种输入格式
def _preprocess_mask(mask: Union[List, PIL.Image.Image, torch.Tensor]):
    # 如果输入是 PyTorch 张量,直接返回
    if isinstance(mask, torch.Tensor):
        return mask
    # 如果输入是 PIL 图像,转换为单元素列表
    elif isinstance(mask, PIL.Image.Image):
        mask = [mask]

    # 如果列表中的第一个元素是 PIL 图像
    if isinstance(mask[0], PIL.Image.Image):
        # 获取掩码的宽度和高度
        w, h = mask[0].size
        # 调整宽度和高度,使其为 32 的整数倍
        w, h = (x - x % 32 for x in (w, h))  # resize to integer multiple of 32
        # 将掩码调整大小并转换为灰度图像的 NumPy 数组,添加新维度
        mask = [np.array(m.convert("L").resize((w, h), resample=PIL_INTERPOLATION["nearest"]))[None, :] for m in mask]
        # 将所有掩码沿第 0 维连接成一个数组
        mask = np.concatenate(mask, axis=0)
        # 转换为浮点型数组并归一化到 [0, 1]
        mask = mask.astype(np.float32) / 255.0
        # 将值小于 0.5 的部分置为 0
        mask[mask < 0.5] = 0
        # 将值大于等于 0.5 的部分置为 1
        mask[mask >= 0.5] = 1
        # 将 NumPy 数组转换为 PyTorch 张量
        mask = torch.from_numpy(mask)
    # 如果列表中的第一个元素是 PyTorch 张量
    elif isinstance(mask[0], torch.Tensor):
        # 将张量沿第 0 维连接
        mask = torch.cat(mask, dim=0)
    # 返回处理后的掩码
    return mask

# 定义 RePaintPipeline 类,继承自 DiffusionPipeline
class RePaintPipeline(DiffusionPipeline):
    # 文档字符串,描述图像修复的管道使用 RePaint 模型
        r"""
        Pipeline for image inpainting using RePaint.
    
        This model inherits from [`DiffusionPipeline`]. Check the superclass documentation for the generic methods
        implemented for all pipelines (downloading, saving, running on a particular device, etc.).
    
        Parameters:
            unet ([`UNet2DModel`]):
                A `UNet2DModel` to denoise the encoded image latents.
            scheduler ([`RePaintScheduler`]):
                A `RePaintScheduler` to be used in combination with `unet` to denoise the encoded image.
        """
    
        # 定义 UNet2DModel 类型的属性,用于图像去噪
        unet: UNet2DModel
        # 定义 RePaintScheduler 类型的属性,用于图像去噪的调度
        scheduler: RePaintScheduler
        # 设置模型 CPU 卸载序列为 "unet"
        model_cpu_offload_seq = "unet"
    
        # 初始化方法,接收 unet 和 scheduler 参数
        def __init__(self, unet, scheduler):
            # 调用父类的初始化方法
            super().__init__()
            # 注册 unet 和 scheduler 模块
            self.register_modules(unet=unet, scheduler=scheduler)
    
        # 禁用梯度计算的上下文装饰器
        @torch.no_grad()
        def __call__(
            # 接收图像和掩码图像,类型可以是 Torch 张量或 PIL 图像
            image: Union[torch.Tensor, PIL.Image.Image],
            mask_image: Union[torch.Tensor, PIL.Image.Image],
            # 设置推理步骤的数量,默认为 250
            num_inference_steps: int = 250,
            # 设置 eta 参数,默认为 0.0
            eta: float = 0.0,
            # 设置跳跃长度,默认为 10
            jump_length: int = 10,
            # 设置跳跃样本数量,默认为 10
            jump_n_sample: int = 10,
            # 生成器参数,可以是一个生成器或生成器列表,默认为 None
            generator: Optional[Union[torch.Generator, List[torch.Generator]]] = None,
            # 输出类型参数,默认为 "pil"
            output_type: Optional[str] = "pil",
            # 是否返回字典形式的结果,默认为 True
            return_dict: bool = True,
posted @ 2024-10-22 12:35  绝不原创的飞龙  阅读(35)  评论(0编辑  收藏  举报