| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
|
|
| import importlib |
| import inspect |
| import os |
| import tempfile |
| from dataclasses import dataclass |
| from typing import Any, Dict, List, Optional, Union |
|
|
| import numpy as np |
| import paddle |
| import paddle.nn as nn |
| import PIL |
| from huggingface_hub import ( |
| create_repo, |
| get_hf_file_metadata, |
| hf_hub_url, |
| repo_type_and_id_from_hf_id, |
| upload_folder, |
| ) |
| from huggingface_hub.utils import EntryNotFoundError |
| from packaging import version |
| from PIL import Image |
| from tqdm.auto import tqdm |
|
|
| from . import FastDeployRuntimeModel |
| from .configuration_utils import ConfigMixin |
| from .utils import PPDIFFUSERS_CACHE, BaseOutput, deprecate, logging |
|
|
| INDEX_FILE = "model_state.pdparams" |
| CUSTOM_PIPELINE_FILE_NAME = "pipeline.py" |
| DUMMY_MODULES_FOLDER = "ppdiffusers.utils" |
| PADDLENLP_DUMMY_MODULES_FOLDER = "paddlenlp.transformers.utils" |
|
|
| logger = logging.get_logger(__name__) |
|
|
| LOADABLE_CLASSES = { |
| "ppdiffusers": { |
| "ModelMixin": ["save_pretrained", "from_pretrained"], |
| "SchedulerMixin": ["save_pretrained", "from_pretrained"], |
| "DiffusionPipeline": ["save_pretrained", "from_pretrained"], |
| "FastDeployRuntimeModel": ["save_pretrained", "from_pretrained"], |
| }, |
| "paddlenlp.transformers": { |
| "PretrainedTokenizer": ["save_pretrained", "from_pretrained"], |
| "PretrainedModel": ["save_pretrained", "from_pretrained"], |
| "FeatureExtractionMixin": ["save_pretrained", "from_pretrained"], |
| "ProcessorMixin": ["save_pretrained", "from_pretrained"], |
| "ImageProcessingMixin": ["save_pretrained", "from_pretrained"], |
| }, |
| } |
|
|
| ALL_IMPORTABLE_CLASSES = {} |
| for library in LOADABLE_CLASSES: |
| ALL_IMPORTABLE_CLASSES.update(LOADABLE_CLASSES[library]) |
|
|
|
|
| @dataclass |
| class ImagePipelineOutput(BaseOutput): |
| """ |
| Output class for image pipelines. |
| |
| Args: |
| images (`List[PIL.Image.Image]` or `np.ndarray`) |
| List of denoised PIL images of length `batch_size` or numpy array of shape `(batch_size, height, width, |
| num_channels)`. PIL images or numpy array present the denoised images of the diffusion pipeline. |
| """ |
|
|
| images: Union[List[PIL.Image.Image], np.ndarray] |
|
|
|
|
| @dataclass |
| class AudioPipelineOutput(BaseOutput): |
| """ |
| Output class for audio pipelines. |
| |
| Args: |
| audios (`np.ndarray`) |
| List of denoised samples of shape `(batch_size, num_channels, sample_rate)`. Numpy array present the |
| denoised audio samples of the diffusion pipeline. |
| """ |
|
|
| audios: np.ndarray |
|
|
|
|
| class DiffusionPipeline(ConfigMixin): |
| r""" |
| Base class for all models. |
| |
| [`DiffusionPipeline`] takes care of storing all components (models, schedulers, processors) for diffusion pipelines |
| and handles methods for loading, downloading and saving models as well as a few methods common to all pipelines to: |
| |
| - move all Paddle modules to the device of your choice |
| - enabling/disabling the progress bar for the denoising iteration |
| |
| Class attributes: |
| |
| - **config_name** (`str`) -- name of the config file that will store the class and module names of all |
| - **_optional_components** (List[`str`]) -- list of all components that are optional so they don't have to be |
| passed for the pipeline to function (should be overridden by subclasses). |
| """ |
| config_name = "model_index.json" |
| _optional_components = [] |
|
|
| def register_modules(self, **kwargs): |
| |
| from . import pipelines |
|
|
| for name, module in kwargs.items(): |
| |
| if module is None: |
| register_dict = {name: (None, None)} |
| else: |
| |
| if "paddlenlp" in module.__module__.split(".") or "ppnlp_patch_utils" in module.__module__.split("."): |
| library = "paddlenlp.transformers" |
| else: |
| library = module.__module__.split(".")[0] |
|
|
| |
| pipeline_dir = module.__module__.split(".")[-2] if len(module.__module__.split(".")) > 2 else None |
| path = module.__module__.split(".") |
| is_pipeline_module = pipeline_dir in path and hasattr(pipelines, pipeline_dir) |
|
|
| |
| |
| |
| if library not in LOADABLE_CLASSES or is_pipeline_module: |
| library = pipeline_dir |
|
|
| |
| class_name = module.__class__.__name__ |
|
|
| register_dict = {name: (library, class_name)} |
|
|
| |
| self.register_to_config(**register_dict) |
|
|
| |
| setattr(self, name, module) |
|
|
| def save_pretrained(self, save_directory: Union[str, os.PathLike]): |
| """ |
| Save all variables of the pipeline that can be saved and loaded as well as the pipelines configuration file to |
| a directory. A pipeline variable can be saved and loaded if its class implements both a save and loading |
| method. The pipeline can easily be re-loaded using the `[`~DiffusionPipeline.from_pretrained`]` class method. |
| |
| Arguments: |
| save_directory (`str` or `os.PathLike`): |
| Directory to which to save. Will be created if it doesn't exist. |
| """ |
| self.save_config(save_directory) |
|
|
| model_index_dict = dict(self.config) |
| model_index_dict.pop("_class_name") |
| |
| model_index_dict.pop("_diffusers_paddle_version", None) |
| model_index_dict.pop("_diffusers_version", None) |
| model_index_dict.pop("_ppdiffusers_version", None) |
| model_index_dict.pop("_module", None) |
|
|
| expected_modules, optional_kwargs = self._get_signature_keys(self) |
|
|
| def is_saveable_module(name, value): |
| if name not in expected_modules: |
| return False |
| if name in self._optional_components and value[0] is None: |
| return False |
| return True |
|
|
| model_index_dict = {k: v for k, v in model_index_dict.items() if is_saveable_module(k, v)} |
|
|
| for pipeline_component_name in model_index_dict.keys(): |
| sub_model = getattr(self, pipeline_component_name) |
|
|
| model_cls = sub_model.__class__ |
|
|
| save_method_name = None |
| |
| for library_name, library_classes in LOADABLE_CLASSES.items(): |
| library = importlib.import_module(library_name) |
| for base_class, save_load_methods in library_classes.items(): |
| class_candidate = getattr(library, base_class, None) |
| if class_candidate is not None and issubclass(model_cls, class_candidate): |
| |
| save_method_name = save_load_methods[0] |
| break |
| if save_method_name is not None: |
| break |
|
|
| save_method = getattr(sub_model, save_method_name) |
| save_method(os.path.join(save_directory, pipeline_component_name)) |
|
|
| def save_to_hf_hub( |
| self, |
| repo_id: str, |
| private: Optional[bool] = None, |
| commit_message: Optional[str] = None, |
| revision: Optional[str] = None, |
| create_pr: bool = False, |
| ): |
| """ |
| Uploads all elements of this pipeline to a new HuggingFace Hub repository. |
| Args: |
| repo_id (str): Repository name for your model/tokenizer in the Hub. |
| private (bool, optional): Whether the model/tokenizer is set to private |
| commit_message (str, optional) — The summary / title / first line of the generated commit. Defaults to: f"Upload {path_in_repo} with huggingface_hub" |
| revision (str, optional) — The git revision to commit from. Defaults to the head of the "main" branch. |
| create_pr (boolean, optional) — Whether or not to create a Pull Request with that commit. Defaults to False. |
| If revision is not set, PR is opened against the "main" branch. If revision is set and is a branch, PR is opened against this branch. |
| If revision is set and is not a branch name (example: a commit oid), an RevisionNotFoundError is returned by the server. |
| |
| Returns: The url of the commit of your model in the given repository. |
| """ |
| repo_url = create_repo(repo_id, private=private, exist_ok=True) |
|
|
| |
| |
| _, repo_owner, repo_name = repo_type_and_id_from_hf_id(repo_url) |
|
|
| repo_id = f"{repo_owner}/{repo_name}" |
|
|
| |
| try: |
| get_hf_file_metadata(hf_hub_url(repo_id=repo_id, filename="README.md", revision=revision)) |
| has_readme = True |
| except EntryNotFoundError: |
| has_readme = False |
|
|
| with tempfile.TemporaryDirectory() as tmp_dir: |
| |
| self.save_pretrained(tmp_dir) |
| |
| logger.info("README.md not found, adding the default README.md") |
| if not has_readme: |
| with open(os.path.join(tmp_dir, "README.md"), "w") as f: |
| f.write(f"---\nlibrary_name: ppdiffusers\n---\n# {repo_id}") |
|
|
| |
| logger.info(f"Pushing to the {repo_id}. This might take a while") |
| return upload_folder( |
| repo_id=repo_id, |
| repo_type="model", |
| folder_path=tmp_dir, |
| commit_message=commit_message, |
| revision=revision, |
| create_pr=create_pr, |
| ) |
|
|
| def to(self, paddle_device: Optional[str] = None): |
| if paddle_device is None: |
| return self |
|
|
| module_names, _, _ = self.extract_init_dict(dict(self.config)) |
| for name in module_names.keys(): |
| module = getattr(self, name) |
| if isinstance(module, nn.Layer): |
| if module.dtype == paddle.float16 and str(paddle_device) in ["cpu"]: |
| logger.warning( |
| "Pipelines loaded with `paddle_dtype=paddle.float16` cannot run with `cpu` device. It" |
| " is not recommended to move them to `cpu` as running them will fail. Please make" |
| " sure to use an accelerator to run the pipeline in inference, due to the lack of" |
| " support for`float16` operations on this device in Paddle. Please, remove the" |
| " `paddle_dtype=paddle.float16` argument, or use another device for inference." |
| ) |
| module.to(paddle_device) |
| return self |
|
|
| @property |
| def device(self): |
| r""" |
| Returns: |
| `paddle.device`: The paddle device on which the pipeline is located. |
| """ |
| module_names, _, _ = self.extract_init_dict(dict(self.config)) |
| for name in module_names.keys(): |
| module = getattr(self, name) |
| if isinstance(module, nn.Layer): |
| return module.place |
| return "cpu" |
|
|
| @classmethod |
| def from_pretrained(cls, pretrained_model_name_or_path: Optional[Union[str, os.PathLike]], **kwargs): |
| r""" |
| Instantiate a Paddle diffusion pipeline from pre-trained pipeline weights. |
| |
| The pipeline is set in evaluation mode by default using `model.eval()` (Dropout modules are deactivated). |
| |
| The warning *Weights from XXX not initialized from pretrained model* means that the weights of XXX do not come |
| pretrained with the rest of the model. It is up to you to train those weights with a downstream fine-tuning |
| task. |
| |
| The warning *Weights from XXX not used in YYY* means that the layer XXX is not used by YYY, therefore those |
| weights are discarded. |
| |
| Parameters: |
| pretrained_model_name_or_path (`str` or `os.PathLike`, *optional*): |
| Can be either: |
| |
| - A string, the *model id* of a pretrained pipeline hosted inside in `https://bj.bcebos.com/paddlenlp/models/community`. |
| like `CompVis/stable-diffusion-v1-4`, `CompVis/ldm-text2im-large-256`. |
| - A path to a *directory* containing pipeline weights saved using |
| [`~DiffusionPipeline.save_pretrained`], e.g., `./my_pipeline_directory/`. |
| paddle_dtype (`str` or `paddle.dtype`, *optional*): |
| Override the default `paddle.dtype` and load the model under this dtype. If `"auto"` is passed the dtype |
| will be automatically derived from the model's weights. |
| output_loading_info(`bool`, *optional*, defaults to `False`): |
| Whether or not to also return a dictionary containing missing keys, unexpected keys and error messages. |
| from_hf_hub (bool, *optional*): |
| Whether to load from Hugging Face Hub. Defaults to False |
| kwargs (remaining dictionary of keyword arguments, *optional*): |
| Can be used to overwrite load - and saveable variables - *i.e.* the pipeline components - of the |
| specific pipeline class. The overwritten components are then directly passed to the pipelines |
| `__init__` method. See example below for more information. |
| |
| Examples: |
| |
| ```py |
| >>> from ppdiffusers import DiffusionPipeline |
| |
| >>> # Download pipeline from bos and cache. |
| >>> pipeline = DiffusionPipeline.from_pretrained("CompVis/ldm-text2im-large-256") |
| |
| >>> # Download pipeline that requires an authorization token |
| >>> # For more information on access tokens, please refer to this section |
| >>> # of the documentation](https://huggingface.co/docs/hub/security-tokens) |
| >>> pipeline = DiffusionPipeline.from_pretrained("runwayml/stable-diffusion-v1-5") |
| |
| >>> # Use a different scheduler |
| >>> from ppdiffusers import LMSDiscreteScheduler |
| |
| >>> scheduler = LMSDiscreteScheduler.from_config(pipeline.scheduler.config) |
| >>> pipeline.scheduler = scheduler |
| ``` |
| """ |
| cache_dir = kwargs.pop("cache_dir", PPDIFFUSERS_CACHE) |
| paddle_dtype = kwargs.pop("paddle_dtype", None) |
| |
| |
| |
| runtime_options = kwargs.pop("runtime_options", None) |
| from_hf_hub = kwargs.pop("from_hf_hub", False) |
|
|
| |
| if not os.path.isdir(pretrained_model_name_or_path): |
| config_dict = cls.load_config( |
| pretrained_model_name_or_path, |
| cache_dir=cache_dir, |
| from_hf_hub=from_hf_hub, |
| ) |
| else: |
| config_dict = cls.load_config(pretrained_model_name_or_path) |
|
|
| |
| if cls != DiffusionPipeline: |
| pipeline_class = cls |
| else: |
| diffusers_module = importlib.import_module(cls.__module__.split(".")[0]) |
| pipeline_class = getattr(diffusers_module, config_dict["_class_name"]) |
|
|
| |
| |
| _ppdiffusers_version = ( |
| config_dict["_diffusers_paddle_version"] |
| if "_diffusers_paddle_version" in config_dict |
| else config_dict["_ppdiffusers_version"] |
| ) |
| if pipeline_class.__name__ == "StableDiffusionInpaintPipeline" and version.parse( |
| version.parse(_ppdiffusers_version).base_version |
| ) <= version.parse("0.5.1"): |
| from . import ( |
| StableDiffusionInpaintPipeline, |
| StableDiffusionInpaintPipelineLegacy, |
| ) |
|
|
| pipeline_class = StableDiffusionInpaintPipelineLegacy |
|
|
| deprecation_message = ( |
| "You are using a legacy checkpoint for inpainting with Stable Diffusion, therefore we are loading the" |
| f" {StableDiffusionInpaintPipelineLegacy} class instead of {StableDiffusionInpaintPipeline}. For" |
| " better inpainting results, we strongly suggest using Stable Diffusion's official inpainting" |
| " checkpoint: https://huggingface.co/runwayml/stable-diffusion-inpainting instead or adapting your" |
| f" checkpoint {pretrained_model_name_or_path} to the format of" |
| " https://huggingface.co/runwayml/stable-diffusion-inpainting. Note that we do not actively maintain" |
| " the {StableDiffusionInpaintPipelineLegacy} class and will likely remove it in version 1.0.0." |
| ) |
| deprecate("StableDiffusionInpaintPipelineLegacy", "1.0.0", deprecation_message, standard_warn=False) |
|
|
| |
| |
| |
| expected_modules, optional_kwargs = cls._get_signature_keys(pipeline_class) |
|
|
| passed_class_obj = {k: kwargs.pop(k) for k in expected_modules if k in kwargs} |
| passed_pipe_kwargs = {k: kwargs.pop(k) for k in optional_kwargs if k in kwargs} |
|
|
| init_dict, unused_kwargs, _ = pipeline_class.extract_init_dict(config_dict, **kwargs) |
|
|
| |
| init_kwargs = {k: init_dict.pop(k) for k in optional_kwargs if k in init_dict} |
| init_kwargs = {**init_kwargs, **passed_pipe_kwargs} |
|
|
| |
| def load_module(name, value): |
| if value[0] is None: |
| return False |
| if name in passed_class_obj and passed_class_obj[name] is None: |
| return False |
| return True |
|
|
| init_dict = {k: v for k, v in init_dict.items() if load_module(k, v)} |
|
|
| if len(unused_kwargs) > 0: |
| logger.warning( |
| f"Keyword arguments {unused_kwargs} are not expected by {pipeline_class.__name__} and will be ignored." |
| ) |
| |
| from . import pipelines |
|
|
| |
| for name, (library_name, class_name) in init_dict.items(): |
| |
| if library_name == "diffusers_paddle": |
| library_name = "ppdiffusers" |
|
|
| is_pipeline_module = hasattr(pipelines, library_name) |
| loaded_sub_model = None |
|
|
| |
| if name in passed_class_obj: |
| |
| if not is_pipeline_module: |
| library = importlib.import_module(library_name) |
| class_obj = getattr(library, class_name) |
| importable_classes = LOADABLE_CLASSES[library_name] |
| class_candidates = {c: getattr(library, c, None) for c in importable_classes.keys()} |
|
|
| expected_class_obj = None |
| for class_name, class_candidate in class_candidates.items(): |
| if class_candidate is not None and issubclass(class_obj, class_candidate): |
| expected_class_obj = class_candidate |
|
|
| if not issubclass(passed_class_obj[name].__class__, expected_class_obj): |
| raise ValueError( |
| f"{passed_class_obj[name]} is of type: {type(passed_class_obj[name])}, but should be" |
| f" {expected_class_obj}" |
| ) |
| else: |
| logger.warning( |
| f"You have passed a non-standard module {passed_class_obj[name]}. We cannot verify whether it" |
| " has the correct type" |
| ) |
|
|
| |
| loaded_sub_model = passed_class_obj[name] |
| elif is_pipeline_module: |
| pipeline_module = getattr(pipelines, library_name) |
| class_obj = getattr(pipeline_module, class_name) |
| importable_classes = ALL_IMPORTABLE_CLASSES |
| class_candidates = {c: class_obj for c in importable_classes.keys()} |
| else: |
| |
| library = importlib.import_module(library_name) |
|
|
| class_obj = getattr(library, class_name) |
| importable_classes = LOADABLE_CLASSES[library_name] |
| class_candidates = {c: getattr(library, c, None) for c in importable_classes.keys()} |
|
|
| if loaded_sub_model is None: |
| load_method_name = None |
| for class_name, class_candidate in class_candidates.items(): |
| if class_candidate is not None and issubclass(class_obj, class_candidate): |
| load_method_name = importable_classes[class_name][1] |
|
|
| if load_method_name is None: |
| none_module = class_obj.__module__ |
| is_dummy_path = none_module.startswith(DUMMY_MODULES_FOLDER) or none_module.startswith( |
| PADDLENLP_DUMMY_MODULES_FOLDER |
| ) |
| if is_dummy_path and "dummy" in none_module: |
| |
| class_obj() |
|
|
| raise ValueError( |
| f"The component {class_obj} of {pipeline_class} cannot be loaded as it does not seem to have" |
| f" any of the loading methods defined in {ALL_IMPORTABLE_CLASSES}." |
| ) |
|
|
| load_method = getattr(class_obj, load_method_name) |
| loading_kwargs = { |
| "from_hf_hub": from_hf_hub, |
| "cache_dir": cache_dir, |
| } |
|
|
| if issubclass(class_obj, FastDeployRuntimeModel): |
| if isinstance(runtime_options, dict): |
| options = runtime_options.get(name, None) |
| else: |
| options = runtime_options |
| loading_kwargs["runtime_options"] = options |
|
|
| if os.path.isdir(pretrained_model_name_or_path): |
| model_path_dir = os.path.join(pretrained_model_name_or_path, name) |
| elif from_hf_hub: |
| model_path_dir = pretrained_model_name_or_path |
| loading_kwargs["subfolder"] = name |
| else: |
| |
| model_path_dir = pretrained_model_name_or_path + "/" + name |
|
|
| loaded_sub_model = load_method(model_path_dir, **loading_kwargs) |
|
|
| |
| if isinstance(loaded_sub_model, nn.Layer): |
| if paddle_dtype is not None and next(loaded_sub_model.named_parameters())[1].dtype != paddle_dtype: |
| loaded_sub_model = loaded_sub_model.to(dtype=paddle_dtype) |
| |
| loaded_sub_model.eval() |
|
|
| init_kwargs[name] = loaded_sub_model |
|
|
| |
| missing_modules = set(expected_modules) - set(init_kwargs.keys()) |
| passed_modules = list(passed_class_obj.keys()) |
| optional_modules = pipeline_class._optional_components |
| if len(missing_modules) > 0 and missing_modules <= set(passed_modules + optional_modules): |
| for module in missing_modules: |
| init_kwargs[module] = passed_class_obj.get(module, None) |
| elif len(missing_modules) > 0: |
| passed_modules = set(list(init_kwargs.keys()) + list(passed_class_obj.keys())) - optional_kwargs |
| raise ValueError( |
| f"Pipeline {pipeline_class} expected {expected_modules}, but only {passed_modules} were passed." |
| ) |
|
|
| |
| model = pipeline_class(**init_kwargs) |
| return model |
|
|
| def enable_attention_slicing(self, slice_size: Optional[Union[str, int]] = "auto"): |
| r""" |
| Enable sliced attention computation. |
| When this option is enabled, the attention module will split the input tensor in slices, to compute attention |
| in several steps. This is useful to save some memory in exchange for a small speed decrease. |
| Args: |
| slice_size (`str` or `int`, *optional*, defaults to `"auto"`): |
| When `"auto"`, halves the input to the attention heads, so attention will be computed in two steps. If |
| `"max"`, maxium amount of memory will be saved by running only one slice at a time. If a number is |
| provided, uses as many slices as `attention_head_dim // slice_size`. In this case, `attention_head_dim` |
| must be a multiple of `slice_size`. |
| """ |
| self.set_attention_slice(slice_size) |
|
|
| def disable_attention_slicing(self): |
| r""" |
| Disable sliced attention computation. If `enable_attention_slicing` was previously invoked, this method will go |
| back to computing attention in one step. |
| """ |
| |
| self.enable_attention_slicing(None) |
|
|
| def set_attention_slice(self, slice_size: Optional[int]): |
| module_names, _, _ = self.extract_init_dict(dict(self.config)) |
| for module_name in module_names: |
| module = getattr(self, module_name) |
| if isinstance(module, nn.Layer) and hasattr(module, "set_attention_slice"): |
| module.set_attention_slice(slice_size) |
|
|
| @staticmethod |
| def _get_signature_keys(obj): |
| parameters = inspect.signature(obj.__init__).parameters |
| required_parameters = {k: v for k, v in parameters.items() if v.default == inspect._empty} |
| optional_parameters = set({k for k, v in parameters.items() if v.default != inspect._empty}) |
| expected_modules = set(required_parameters.keys()) - set(["self"]) |
| return expected_modules, optional_parameters |
|
|
| @property |
| def components(self) -> Dict[str, Any]: |
| r""" |
| |
| The `self.components` property can be useful to run different pipelines with the same weights and |
| configurations to not have to re-allocate memory. |
| |
| Examples: |
| |
| ```py |
| >>> from ppdiffusers import ( |
| ... StableDiffusionPipeline, |
| ... StableDiffusionImg2ImgPipeline, |
| ... StableDiffusionInpaintPipeline, |
| ... ) |
| |
| >>> text2img = StableDiffusionPipeline.from_pretrained("runwayml/stable-diffusion-v1-5") |
| >>> img2img = StableDiffusionImg2ImgPipeline(**text2img.components) |
| >>> inpaint = StableDiffusionInpaintPipeline(**text2img.components) |
| ``` |
| |
| Returns: |
| A dictionaly containing all the modules needed to initialize the pipeline. |
| """ |
| expected_modules, optional_parameters = self._get_signature_keys(self) |
| components = { |
| k: getattr(self, k) for k in self.config.keys() if not k.startswith("_") and k not in optional_parameters |
| } |
|
|
| if set(components.keys()) != expected_modules: |
| raise ValueError( |
| f"{self} has been incorrectly initialized or {self.__class__} is incorrectly implemented. Expected" |
| f" {expected_modules} to be defined, but {components} are defined." |
| ) |
|
|
| return components |
|
|
| @staticmethod |
| def numpy_to_pil(images): |
| """ |
| Convert a numpy image or a batch of images to a PIL image. |
| """ |
| if images.ndim == 3: |
| images = images[None, ...] |
| images = (images * 255).round().astype("uint8") |
| if images.shape[-1] == 1: |
| |
| pil_images = [Image.fromarray(image.squeeze(), mode="L") for image in images] |
| else: |
| pil_images = [Image.fromarray(image) for image in images] |
|
|
| return pil_images |
|
|
| def progress_bar(self, iterable=None, total=None): |
| if not hasattr(self, "_progress_bar_config"): |
| self._progress_bar_config = {} |
| elif not isinstance(self._progress_bar_config, dict): |
| raise ValueError( |
| f"`self._progress_bar_config` should be of type `dict`, but is {type(self._progress_bar_config)}." |
| ) |
|
|
| if iterable is not None: |
| return tqdm(iterable, **self._progress_bar_config) |
| elif total is not None: |
| return tqdm(total=total, **self._progress_bar_config) |
| else: |
| raise ValueError("Either `total` or `iterable` has to be defined.") |
|
|
| def set_progress_bar_config(self, **kwargs): |
| self._progress_bar_config = kwargs |
|
|