CoolFace
Datasetpublic

diffusers/community-pipelines-mirror

Community Pipeline Examples For more information about community pipelines, please have a look at this issue. Community pipeline examples consist pipelines that have been added by the community. Please have a look at the following tables to get an overview of all community examples. Click on the Code Example to get a copy-and-paste ready code example that you can try out. If a community pipeline doesn't work as expected, please open an issue and ping the author on it. Please… See the full description on the dataset page: https://huggingface.co/datasets/diffusers/community-pipelines-mirror.

sourceHugging Faceupdated 28d agoView on Hugging Face
9likes22kdownloads
mixture_tiling.py406 linesDownload Raw Back to root
1import inspect2from copy import deepcopy3from enum import Enum4from typing import List, Optional, Tuple, Union5 6import torch7from tqdm.auto import tqdm8 9from diffusers.models import AutoencoderKL, UNet2DConditionModel10from diffusers.pipelines.pipeline_utils import DiffusionPipeline11from diffusers.pipelines.stable_diffusion import StableDiffusionSafetyChecker12from diffusers.schedulers import DDIMScheduler, LMSDiscreteScheduler, PNDMScheduler13from diffusers.utils import logging14 15 16try:17    from ligo.segments import segment18    from transformers import CLIPFeatureExtractor, CLIPTextModel, CLIPTokenizer19except ImportError:20    raise ImportError("Please install transformers and ligo-segments to use the mixture pipeline")21 22logger = logging.get_logger(__name__)  # pylint: disable=invalid-name23 24EXAMPLE_DOC_STRING = """25    Examples:26        ```py27        >>> from diffusers import LMSDiscreteScheduler, DiffusionPipeline28 29        >>> scheduler = LMSDiscreteScheduler(beta_start=0.00085, beta_end=0.012, beta_schedule="scaled_linear", num_train_timesteps=1000)30        >>> pipeline = DiffusionPipeline.from_pretrained("CompVis/stable-diffusion-v1-4", scheduler=scheduler, custom_pipeline="mixture_tiling")31        >>> pipeline.to("cuda")32 33        >>> image = pipeline(34        >>>     prompt=[[35        >>>         "A charming house in the countryside, by jakub rozalski, sunset lighting, elegant, highly detailed, smooth, sharp focus, artstation, stunning masterpiece",36        >>>         "A dirt road in the countryside crossing pastures, by jakub rozalski, sunset lighting, elegant, highly detailed, smooth, sharp focus, artstation, stunning masterpiece",37        >>>         "An old and rusty giant robot lying on a dirt road, by jakub rozalski, dark sunset lighting, elegant, highly detailed, smooth, sharp focus, artstation, stunning masterpiece"38        >>>     ]],39        >>>     tile_height=640,40        >>>     tile_width=640,41        >>>     tile_row_overlap=0,42        >>>     tile_col_overlap=256,43        >>>     guidance_scale=8,44        >>>     seed=7178915308,45        >>>     num_inference_steps=50,46    >>> )["images"][0]47        ```48"""49 50 51def _tile2pixel_indices(tile_row, tile_col, tile_width, tile_height, tile_row_overlap, tile_col_overlap):52    """Given a tile row and column numbers returns the range of pixels affected by that tiles in the overall image53 54    Returns a tuple with:55        - Starting coordinates of rows in pixel space56        - Ending coordinates of rows in pixel space57        - Starting coordinates of columns in pixel space58        - Ending coordinates of columns in pixel space59    """60    px_row_init = 0 if tile_row == 0 else tile_row * (tile_height - tile_row_overlap)61    px_row_end = px_row_init + tile_height62    px_col_init = 0 if tile_col == 0 else tile_col * (tile_width - tile_col_overlap)63    px_col_end = px_col_init + tile_width64    return px_row_init, px_row_end, px_col_init, px_col_end65 66 67def _pixel2latent_indices(px_row_init, px_row_end, px_col_init, px_col_end):68    """Translates coordinates in pixel space to coordinates in latent space"""69    return px_row_init // 8, px_row_end // 8, px_col_init // 8, px_col_end // 870 71 72def _tile2latent_indices(tile_row, tile_col, tile_width, tile_height, tile_row_overlap, tile_col_overlap):73    """Given a tile row and column numbers returns the range of latents affected by that tiles in the overall image74 75    Returns a tuple with:76        - Starting coordinates of rows in latent space77        - Ending coordinates of rows in latent space78        - Starting coordinates of columns in latent space79        - Ending coordinates of columns in latent space80    """81    px_row_init, px_row_end, px_col_init, px_col_end = _tile2pixel_indices(82        tile_row, tile_col, tile_width, tile_height, tile_row_overlap, tile_col_overlap83    )84    return _pixel2latent_indices(px_row_init, px_row_end, px_col_init, px_col_end)85 86 87def _tile2latent_exclusive_indices(88    tile_row, tile_col, tile_width, tile_height, tile_row_overlap, tile_col_overlap, rows, columns89):90    """Given a tile row and column numbers returns the range of latents affected only by that tile in the overall image91 92    Returns a tuple with:93        - Starting coordinates of rows in latent space94        - Ending coordinates of rows in latent space95        - Starting coordinates of columns in latent space96        - Ending coordinates of columns in latent space97    """98    row_init, row_end, col_init, col_end = _tile2latent_indices(99        tile_row, tile_col, tile_width, tile_height, tile_row_overlap, tile_col_overlap100    )101    row_segment = segment(row_init, row_end)102    col_segment = segment(col_init, col_end)103    # Iterate over the rest of tiles, clipping the region for the current tile104    for row in range(rows):105        for column in range(columns):106            if row != tile_row and column != tile_col:107                clip_row_init, clip_row_end, clip_col_init, clip_col_end = _tile2latent_indices(108                    row, column, tile_width, tile_height, tile_row_overlap, tile_col_overlap109                )110                row_segment = row_segment - segment(clip_row_init, clip_row_end)111                col_segment = col_segment - segment(clip_col_init, clip_col_end)112    # return row_init, row_end, col_init, col_end113    return row_segment[0], row_segment[1], col_segment[0], col_segment[1]114 115 116class StableDiffusionExtrasMixin:117    """Mixin providing additional convenience method to Stable Diffusion pipelines"""118 119    def decode_latents(self, latents, cpu_vae=False):120        """Decodes a given array of latents into pixel space"""121        # scale and decode the image latents with vae122        if cpu_vae:123            lat = deepcopy(latents).cpu()124            vae = deepcopy(self.vae).cpu()125        else:126            lat = latents127            vae = self.vae128 129        lat = 1 / 0.18215 * lat130        image = vae.decode(lat).sample131 132        image = (image / 2 + 0.5).clamp(0, 1)133        image = image.cpu().permute(0, 2, 3, 1).numpy()134 135        return self.numpy_to_pil(image)136 137 138class StableDiffusionTilingPipeline(DiffusionPipeline, StableDiffusionExtrasMixin):139    def __init__(140        self,141        vae: AutoencoderKL,142        text_encoder: CLIPTextModel,143        tokenizer: CLIPTokenizer,144        unet: UNet2DConditionModel,145        scheduler: Union[DDIMScheduler, PNDMScheduler],146        safety_checker: StableDiffusionSafetyChecker,147        feature_extractor: CLIPFeatureExtractor,148    ):149        super().__init__()150        self.register_modules(151            vae=vae,152            text_encoder=text_encoder,153            tokenizer=tokenizer,154            unet=unet,155            scheduler=scheduler,156            safety_checker=safety_checker,157            feature_extractor=feature_extractor,158        )159 160    class SeedTilesMode(Enum):161        """Modes in which the latents of a particular tile can be re-seeded"""162 163        FULL = "full"164        EXCLUSIVE = "exclusive"165 166    @torch.no_grad()167    def __call__(168        self,169        prompt: Union[str, List[List[str]]],170        num_inference_steps: Optional[int] = 50,171        guidance_scale: Optional[float] = 7.5,172        eta: Optional[float] = 0.0,173        seed: Optional[int] = None,174        tile_height: Optional[int] = 512,175        tile_width: Optional[int] = 512,176        tile_row_overlap: Optional[int] = 256,177        tile_col_overlap: Optional[int] = 256,178        guidance_scale_tiles: Optional[List[List[float]]] = None,179        seed_tiles: Optional[List[List[int]]] = None,180        seed_tiles_mode: Optional[Union[str, List[List[str]]]] = "full",181        seed_reroll_regions: Optional[List[Tuple[int, int, int, int, int]]] = None,182        cpu_vae: Optional[bool] = False,183    ):184        r"""185        Function to run the diffusion pipeline with tiling support.186 187        Args:188            prompt: either a single string (no tiling) or a list of lists with all the prompts to use (one list for each row of tiles). This will also define the tiling structure.189            num_inference_steps: number of diffusions steps.190            guidance_scale: classifier-free guidance.191            seed: general random seed to initialize latents.192            tile_height: height in pixels of each grid tile.193            tile_width: width in pixels of each grid tile.194            tile_row_overlap: number of overlap pixels between tiles in consecutive rows.195            tile_col_overlap: number of overlap pixels between tiles in consecutive columns.196            guidance_scale_tiles: specific weights for classifier-free guidance in each tile.197            guidance_scale_tiles: specific weights for classifier-free guidance in each tile. If None, the value provided in guidance_scale will be used.198            seed_tiles: specific seeds for the initialization latents in each tile. These will override the latents generated for the whole canvas using the standard seed parameter.199            seed_tiles_mode: either "full" "exclusive". If "full", all the latents affected by the tile be overriden. If "exclusive", only the latents that are affected exclusively by this tile (and no other tiles) will be overriden.200            seed_reroll_regions: a list of tuples in the form (start row, end row, start column, end column, seed) defining regions in pixel space for which the latents will be overriden using the given seed. Takes priority over seed_tiles.201            cpu_vae: the decoder from latent space to pixel space can require too mucho GPU RAM for large images. If you find out of memory errors at the end of the generation process, try setting this parameter to True to run the decoder in CPU. Slower, but should run without memory issues.202 203        Examples:204 205        Returns:206            A PIL image with the generated image.207 208        """209        if not isinstance(prompt, list) or not all(isinstance(row, list) for row in prompt):210            raise ValueError(f"`prompt` has to be a list of lists but is {type(prompt)}")211        grid_rows = len(prompt)212        grid_cols = len(prompt[0])213        if not all(len(row) == grid_cols for row in prompt):214            raise ValueError("All prompt rows must have the same number of prompt columns")215        if not isinstance(seed_tiles_mode, str) and (216            not isinstance(seed_tiles_mode, list) or not all(isinstance(row, list) for row in seed_tiles_mode)217        ):218            raise ValueError(f"`seed_tiles_mode` has to be a string or list of lists but is {type(prompt)}")219        if isinstance(seed_tiles_mode, str):220            seed_tiles_mode = [[seed_tiles_mode for _ in range(len(row))] for row in prompt]221 222        modes = [mode.value for mode in self.SeedTilesMode]223        if any(mode not in modes for row in seed_tiles_mode for mode in row):224            raise ValueError(f"Seed tiles mode must be one of {modes}")225        if seed_reroll_regions is None:226            seed_reroll_regions = []227        batch_size = 1228 229        # create original noisy latents using the timesteps230        height = tile_height + (grid_rows - 1) * (tile_height - tile_row_overlap)231        width = tile_width + (grid_cols - 1) * (tile_width - tile_col_overlap)232        latents_shape = (batch_size, self.unet.config.in_channels, height // 8, width // 8)233        generator = torch.Generator("cuda").manual_seed(seed)234        latents = torch.randn(latents_shape, generator=generator, device=self.device)235 236        # overwrite latents for specific tiles if provided237        if seed_tiles is not None:238            for row in range(grid_rows):239                for col in range(grid_cols):240                    if (seed_tile := seed_tiles[row][col]) is not None:241                        mode = seed_tiles_mode[row][col]242                        if mode == self.SeedTilesMode.FULL.value:243                            row_init, row_end, col_init, col_end = _tile2latent_indices(244                                row, col, tile_width, tile_height, tile_row_overlap, tile_col_overlap245                            )246                        else:247                            row_init, row_end, col_init, col_end = _tile2latent_exclusive_indices(248                                row,249                                col,250                                tile_width,251                                tile_height,252                                tile_row_overlap,253                                tile_col_overlap,254                                grid_rows,255                                grid_cols,256                            )257                        tile_generator = torch.Generator("cuda").manual_seed(seed_tile)258                        tile_shape = (latents_shape[0], latents_shape[1], row_end - row_init, col_end - col_init)259                        latents[:, :, row_init:row_end, col_init:col_end] = torch.randn(260                            tile_shape, generator=tile_generator, device=self.device261                        )262 263        # overwrite again for seed reroll regions264        for row_init, row_end, col_init, col_end, seed_reroll in seed_reroll_regions:265            row_init, row_end, col_init, col_end = _pixel2latent_indices(266                row_init, row_end, col_init, col_end267            )  # to latent space coordinates268            reroll_generator = torch.Generator("cuda").manual_seed(seed_reroll)269            region_shape = (latents_shape[0], latents_shape[1], row_end - row_init, col_end - col_init)270            latents[:, :, row_init:row_end, col_init:col_end] = torch.randn(271                region_shape, generator=reroll_generator, device=self.device272            )273 274        # Prepare scheduler275        accepts_offset = "offset" in set(inspect.signature(self.scheduler.set_timesteps).parameters.keys())276        extra_set_kwargs = {}277        if accepts_offset:278            extra_set_kwargs["offset"] = 1279        self.scheduler.set_timesteps(num_inference_steps, **extra_set_kwargs)280        # if we use LMSDiscreteScheduler, let's make sure latents are multiplied by sigmas281        if isinstance(self.scheduler, LMSDiscreteScheduler):282            latents = latents * self.scheduler.sigmas[0]283 284        # get prompts text embeddings285        text_input = [286            [287                self.tokenizer(288                    col,289                    padding="max_length",290                    max_length=self.tokenizer.model_max_length,291                    truncation=True,292                    return_tensors="pt",293                )294                for col in row295            ]296            for row in prompt297        ]298        text_embeddings = [[self.text_encoder(col.input_ids.to(self.device))[0] for col in row] for row in text_input]299 300        # here `guidance_scale` is defined analog to the guidance weight `w` of equation (2)301        # of the Imagen paper: https://arxiv.org/pdf/2205.11487.pdf . `guidance_scale = 1`302        # corresponds to doing no classifier free guidance.303        do_classifier_free_guidance = guidance_scale > 1.0  # TODO: also active if any tile has guidance scale304        # get unconditional embeddings for classifier free guidance305        if do_classifier_free_guidance:306            for i in range(grid_rows):307                for j in range(grid_cols):308                    max_length = text_input[i][j].input_ids.shape[-1]309                    uncond_input = self.tokenizer(310                        [""] * batch_size, padding="max_length", max_length=max_length, return_tensors="pt"311                    )312                    uncond_embeddings = self.text_encoder(uncond_input.input_ids.to(self.device))[0]313 314                    # For classifier free guidance, we need to do two forward passes.315                    # Here we concatenate the unconditional and text embeddings into a single batch316                    # to avoid doing two forward passes317                    text_embeddings[i][j] = torch.cat([uncond_embeddings, text_embeddings[i][j]])318 319        # prepare extra kwargs for the scheduler step, since not all schedulers have the same signature320        # eta (η) is only used with the DDIMScheduler, it will be ignored for other schedulers.321        # eta corresponds to η in DDIM paper: https://arxiv.org/abs/2010.02502322        # and should be between [0, 1]323        accepts_eta = "eta" in set(inspect.signature(self.scheduler.step).parameters.keys())324        extra_step_kwargs = {}325        if accepts_eta:326            extra_step_kwargs["eta"] = eta327 328        # Mask for tile weights strength329        tile_weights = self._gaussian_weights(tile_width, tile_height, batch_size)330 331        # Diffusion timesteps332        for i, t in tqdm(enumerate(self.scheduler.timesteps)):333            # Diffuse each tile334            noise_preds = []335            for row in range(grid_rows):336                noise_preds_row = []337                for col in range(grid_cols):338                    px_row_init, px_row_end, px_col_init, px_col_end = _tile2latent_indices(339                        row, col, tile_width, tile_height, tile_row_overlap, tile_col_overlap340                    )341                    tile_latents = latents[:, :, px_row_init:px_row_end, px_col_init:px_col_end]342                    # expand the latents if we are doing classifier free guidance343                    latent_model_input = torch.cat([tile_latents] * 2) if do_classifier_free_guidance else tile_latents344                    latent_model_input = self.scheduler.scale_model_input(latent_model_input, t)345                    # predict the noise residual346                    noise_pred = self.unet(latent_model_input, t, encoder_hidden_states=text_embeddings[row][col])[347                        "sample"348                    ]349                    # perform guidance350                    if do_classifier_free_guidance:351                        noise_pred_uncond, noise_pred_text = noise_pred.chunk(2)352                        guidance = (353                            guidance_scale354                            if guidance_scale_tiles is None or guidance_scale_tiles[row][col] is None355                            else guidance_scale_tiles[row][col]356                        )357                        noise_pred_tile = noise_pred_uncond + guidance * (noise_pred_text - noise_pred_uncond)358                        noise_preds_row.append(noise_pred_tile)359                noise_preds.append(noise_preds_row)360            # Stitch noise predictions for all tiles361            noise_pred = torch.zeros(latents.shape, device=self.device)362            contributors = torch.zeros(latents.shape, device=self.device)363            # Add each tile contribution to overall latents364            for row in range(grid_rows):365                for col in range(grid_cols):366                    px_row_init, px_row_end, px_col_init, px_col_end = _tile2latent_indices(367                        row, col, tile_width, tile_height, tile_row_overlap, tile_col_overlap368                    )369                    noise_pred[:, :, px_row_init:px_row_end, px_col_init:px_col_end] += (370                        noise_preds[row][col] * tile_weights371                    )372                    contributors[:, :, px_row_init:px_row_end, px_col_init:px_col_end] += tile_weights373            # Average overlapping areas with more than 1 contributor374            noise_pred /= contributors375 376            # compute the previous noisy sample x_t -> x_t-1377            latents = self.scheduler.step(noise_pred, t, latents).prev_sample378 379        # scale and decode the image latents with vae380        image = self.decode_latents(latents, cpu_vae)381 382        return {"images": image}383 384    def _gaussian_weights(self, tile_width, tile_height, nbatches):385        """Generates a gaussian mask of weights for tile contributions"""386        import numpy as np387        from numpy import exp, pi, sqrt388 389        latent_width = tile_width // 8390        latent_height = tile_height // 8391 392        var = 0.01393        midpoint = (latent_width - 1) / 2  # -1 because index goes from 0 to latent_width - 1394        x_probs = [395            exp(-(x - midpoint) * (x - midpoint) / (latent_width * latent_width) / (2 * var)) / sqrt(2 * pi * var)396            for x in range(latent_width)397        ]398        midpoint = latent_height / 2399        y_probs = [400            exp(-(y - midpoint) * (y - midpoint) / (latent_height * latent_height) / (2 * var)) / sqrt(2 * pi * var)401            for y in range(latent_height)402        ]403 404        weights = np.outer(y_probs, x_probs)405        return torch.tile(torch.tensor(weights, device=self.device), (nbatches, self.unet.config.in_channels, 1, 1))406