jayhsu0627 commited on
Commit
99c1172
Β·
1 Parent(s): 4dcf98e
app.py ADDED
@@ -0,0 +1,115 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import gradio as gr
2
+ import torch
3
+ from diffusers import StableVideoDiffusionPipeline
4
+ from utils.unet_spatio_temporal_condition import UNetSpatioTemporalConditionModel
5
+ from utils.pipeline_stable_video_diffusion import StableVideoDiffusionPipeline
6
+
7
+ from transformers import CLIPVisionModelWithProjection
8
+ from diffusers import AutoencoderKLTemporalDecoder
9
+
10
+ # 1. Load once at startup
11
+ unet = UNetSpatioTemporalConditionModel.from_pretrained("models/", subfolder="unet", low_cpu_mem_usage=True).to("cuda")
12
+ image_encoder = CLIPVisionModelWithProjection.from_pretrained("stabilityai/stable-video-diffusion-img2vid", subfolder="image_encoder", revision=None)
13
+ vae = AutoencoderKLTemporalDecoder.from_pretrained("stabilityai/stable-video-diffusion-img2vid", subfolder="vae", revision=None, variant="fp16").to("cuda")
14
+
15
+ pipeline = StableVideoDiffusionPipeline.from_pretrained(
16
+ "stabilityai/stable-video-diffusion-img2vid",
17
+ unet=unet,
18
+ image_encoder=image_encoder,
19
+ vae=vae,
20
+ revision=None,
21
+ torch_dtype=torch.float16,
22
+ )
23
+
24
+
25
+ def load_images_from_folder(folder, mask_folder, is_condition=False):
26
+ images = []
27
+ valid_extensions = {".jpg", ".jpeg", ".png", ".bmp", ".gif", ".tiff"} # Add or remove extensions as needed
28
+
29
+ # Function to extract frame number from the filename
30
+ def frame_number(filename):
31
+ parts = filename.split('_')
32
+ if len(parts) > 1 and parts[0] == 'frame':
33
+ try:
34
+ return int(parts[1].split('.')[0]) # Extracting the number part
35
+ except ValueError:
36
+ return float('inf') # In case of non-integer part, place this file at the end
37
+ return float('inf') # Non-frame files are placed at the end
38
+
39
+ # Sorting files based on frame number
40
+ sorted_files = sorted(os.listdir(folder))
41
+
42
+ # Load images in sorted order
43
+ for i,filename in enumerate(sorted_files):
44
+
45
+ img = Image.open(os.path.join(folder, filename))
46
+
47
+ # Check if the directory exists
48
+ if os.path.isdir(mask_folder):
49
+ mask = combine_masks(mask_folder)[i]
50
+ # Expand mask to 3D to match the shape of image_array (1080, 1920, 3)
51
+ mask_3d = np.expand_dims(mask, axis=-1).repeat(3, axis=-1)
52
+ # Convert image to a NumPy array
53
+ image_array = np.array(img)
54
+ multiplied_image_array = (image_array * mask_3d).astype(np.uint8)
55
+
56
+ multiplied_image_array = multiplied_image_array + ((1-mask_3d) * 255).astype(np.uint8)
57
+
58
+ img = Image.fromarray(multiplied_image_array)
59
+
60
+ if is_condition:
61
+ img = convert_colors(img)
62
+ w, h = img.size # PIL uses (width, height) order
63
+ img = resize_and_pad_image(img)
64
+
65
+ images.append(img)
66
+
67
+ return images
68
+
69
+ def export_to_gif(frames, output_gif_path, fps):
70
+ """
71
+ Export a list of frames to a GIF.
72
+
73
+ Args:
74
+ - frames (list): List of frames (as numpy arrays or PIL Image objects).
75
+ - output_gif_path (str): Path to save the output GIF.
76
+ - duration_ms (int): Duration of each frame in milliseconds.
77
+
78
+ """
79
+ # Convert numpy arrays to PIL Images if needed
80
+ pil_frames = [Image.fromarray(frame) if isinstance(
81
+ frame, np.ndarray) else frame for frame in frames]
82
+
83
+ pil_frames[0].save(output_gif_path.replace('.mp4', '.gif'),
84
+ format='GIF',
85
+ append_images=pil_frames[1:],
86
+ save_all=True,
87
+ duration=500,
88
+ loop=0)
89
+
90
+ def generate(video_folder: str, num_frames: int = 4, height: int = 320, width: int = 512):
91
+ """
92
+ video_folder: path to a folder of image frames (frame_0000.png, …)
93
+ """
94
+ frames = load_images_from_folder(video_folder, mask_folder=None, is_condition=False)
95
+ # run the pipeline
96
+ output = pipeline(frames, num_frames=num_frames, height=height, width=width).frames[0]
97
+ # convert back to a GIF or video bytes
98
+ return export_frames_to_gif(output, fps=7)
99
+
100
+ # 2. Build the Gradio interface
101
+ iface = gr.Interface(
102
+ fn=generate,
103
+ inputs=[
104
+ gr.Textbox(label="Video-frame folder path"),
105
+ gr.Slider(1, 16, value=4, step=1, label="Number of output frames"),
106
+ gr.Slider(128, 1024, value=320, step=32, label="Height"),
107
+ gr.Slider(128, 1024, value=512, step=32, label="Width"),
108
+ ],
109
+ outputs=gr.Video(label="Relit Video"),
110
+ title="Stable Video Diffusion Demo",
111
+ description="Upload a folder of frames and get back your relit video."
112
+ )
113
+
114
+ if __name__ == "__main__":
115
+ iface.launch()
requirements.txt ADDED
@@ -0,0 +1,11 @@
 
 
 
 
 
 
 
 
 
 
 
 
1
+ gradio
2
+ torch
3
+ diffusers>=0.24.0.dev0
4
+ transformers
5
+ accelerate
6
+ einops
7
+ kornia
8
+ Pillow
9
+ opencv-python
10
+ huggingface-hub
11
+ tqdm
utils/pipeline_stable_video_diffusion.py ADDED
@@ -0,0 +1,968 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ # Copyright 2024 The HuggingFace Team. All rights reserved.
2
+ #
3
+ # Licensed under the Apache License, Version 2.0 (the "License");
4
+ # you may not use this file except in compliance with the License.
5
+ # You may obtain a copy of the License at
6
+ #
7
+ # http://www.apache.org/licenses/LICENSE-2.0
8
+ #
9
+ # Unless required by applicable law or agreed to in writing, software
10
+ # distributed under the License is distributed on an "AS IS" BASIS,
11
+ # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12
+ # See the License for the specific language governing permissions and
13
+ # limitations under the License.
14
+
15
+ import inspect
16
+ from dataclasses import dataclass
17
+ from typing import Callable, Dict, List, Optional, Union
18
+
19
+ import numpy as np
20
+ import PIL.Image
21
+ import torch
22
+ from transformers import CLIPImageProcessor, CLIPVisionModelWithProjection
23
+
24
+ from diffusers.image_processor import PipelineImageInput, VaeImageProcessor
25
+ from diffusers.models import AutoencoderKLTemporalDecoder
26
+ from diffusers.schedulers import EulerDiscreteScheduler
27
+ from diffusers.utils import BaseOutput, logging, replace_example_docstring
28
+ from diffusers.utils.torch_utils import is_compiled_module, randn_tensor
29
+ from diffusers.pipelines.pipeline_utils import DiffusionPipeline
30
+
31
+ # Load the added input_ch unet
32
+ from models.unet_spatio_temporal_condition import UNetSpatioTemporalConditionModel
33
+ from einops import rearrange
34
+
35
+
36
+ logger = logging.get_logger(__name__) # pylint: disable=invalid-name
37
+
38
+ EXAMPLE_DOC_STRING = """
39
+ Examples:
40
+ ```py
41
+ >>> from diffusers import StableVideoDiffusionPipeline
42
+ >>> from diffusers.utils import load_image, export_to_video
43
+
44
+ >>> pipe = StableVideoDiffusionPipeline.from_pretrained("stabilityai/stable-video-diffusion-img2vid-xt", torch_dtype=torch.float16, variant="fp16")
45
+ >>> pipe.to("cuda")
46
+
47
+ >>> image = load_image("https://huggingface.co/datasets/huggingface/documentation-images/resolve/main/diffusers/svd-docstring-example.jpeg")
48
+ >>> image = image.resize((1024, 576))
49
+
50
+ >>> frames = pipe(image, num_frames=25, decode_chunk_size=8).frames[0]
51
+ >>> export_to_video(frames, "generated.mp4", fps=7)
52
+ ```
53
+ """
54
+
55
+ # copy from https://github.com/crowsonkb/k-diffusion.git
56
+ def rand_log_normal(shape, loc=0., scale=1., device='cpu', dtype=torch.float32):
57
+ """Draws samples from an lognormal distribution."""
58
+ u = torch.rand(shape, dtype=dtype, device=device) * (1 - 2e-7) + 1e-7
59
+ return torch.distributions.Normal(loc, scale).icdf(u).exp()
60
+
61
+ def _append_dims(x, target_dims):
62
+ """Appends dimensions to the end of a tensor until it has target_dims dimensions."""
63
+ dims_to_append = target_dims - x.ndim
64
+ if dims_to_append < 0:
65
+ raise ValueError(f"input has {x.ndim} dims but target_dims is {target_dims}, which is less")
66
+ return x[(...,) + (None,) * dims_to_append]
67
+
68
+
69
+ # Copied from diffusers.pipelines.animatediff.pipeline_animatediff.tensor2vid
70
+ def tensor2vid(video: torch.Tensor, processor: VaeImageProcessor, output_type: str = "np"):
71
+ batch_size, channels, num_frames, height, width = video.shape
72
+ outputs = []
73
+ for batch_idx in range(batch_size):
74
+ batch_vid = video[batch_idx].permute(1, 0, 2, 3)
75
+ batch_output = processor.postprocess(batch_vid, output_type)
76
+
77
+ outputs.append(batch_output)
78
+
79
+ if output_type == "np":
80
+ outputs = np.stack(outputs)
81
+
82
+ elif output_type == "pt":
83
+ outputs = torch.stack(outputs)
84
+
85
+ elif not output_type == "pil":
86
+ raise ValueError(f"{output_type} does not exist. Please choose one of ['np', 'pt', 'pil']")
87
+
88
+ return outputs
89
+
90
+
91
+ @dataclass
92
+ class StableVideoDiffusionPipelineOutput(BaseOutput):
93
+ r"""
94
+ Output class for Stable Video Diffusion pipeline.
95
+
96
+ Args:
97
+ frames (`[List[List[PIL.Image.Image]]`, `np.ndarray`, `torch.FloatTensor`]):
98
+ List of denoised PIL images of length `batch_size` or numpy array or torch tensor
99
+ of shape `(batch_size, num_frames, height, width, num_channels)`.
100
+ """
101
+
102
+ frames: Union[List[List[PIL.Image.Image]], np.ndarray, torch.FloatTensor]
103
+
104
+
105
+ class StableVideoDiffusionPipeline(DiffusionPipeline):
106
+ r"""
107
+ Pipeline to generate video from an input image using Stable Video Diffusion.
108
+
109
+ This model inherits from [`DiffusionPipeline`]. Check the superclass documentation for the generic methods
110
+ implemented for all pipelines (downloading, saving, running on a particular device, etc.).
111
+
112
+ Args:
113
+ vae ([`AutoencoderKLTemporalDecoder`]):
114
+ Variational Auto-Encoder (VAE) model to encode and decode images to and from latent representations.
115
+ image_encoder ([`~transformers.CLIPVisionModelWithProjection`]):
116
+ Frozen CLIP image-encoder ([laion/CLIP-ViT-H-14-laion2B-s32B-b79K](https://huggingface.co/laion/CLIP-ViT-H-14-laion2B-s32B-b79K)).
117
+ unet ([`UNetSpatioTemporalConditionModel`]):
118
+ A `UNetSpatioTemporalConditionModel` to denoise the encoded image latents.
119
+ scheduler ([`EulerDiscreteScheduler`]):
120
+ A scheduler to be used in combination with `unet` to denoise the encoded image latents.
121
+ feature_extractor ([`~transformers.CLIPImageProcessor`]):
122
+ A `CLIPImageProcessor` to extract features from generated images.
123
+ """
124
+
125
+ model_cpu_offload_seq = "image_encoder->unet->vae"
126
+ _callback_tensor_inputs = ["latents"]
127
+
128
+ def __init__(
129
+ self,
130
+ vae: AutoencoderKLTemporalDecoder,
131
+ image_encoder: CLIPVisionModelWithProjection,
132
+ unet: UNetSpatioTemporalConditionModel,
133
+ scheduler: EulerDiscreteScheduler,
134
+ feature_extractor: CLIPImageProcessor,
135
+ ):
136
+ super().__init__()
137
+
138
+ self.register_modules(
139
+ vae=vae,
140
+ image_encoder=image_encoder,
141
+ unet=unet,
142
+ scheduler=scheduler,
143
+ feature_extractor=feature_extractor,
144
+ )
145
+ self.vae_scale_factor = 2 ** (len(self.vae.config.block_out_channels) - 1)
146
+ self.image_processor = VaeImageProcessor(vae_scale_factor=self.vae_scale_factor)
147
+
148
+
149
+ def _encode_image(
150
+ self,
151
+ image: PipelineImageInput,
152
+ device: Union[str, torch.device],
153
+ num_videos_per_prompt: int,
154
+ do_classifier_free_guidance: bool,
155
+ ) -> torch.FloatTensor:
156
+ dtype = next(self.image_encoder.parameters()).dtype
157
+
158
+ if not isinstance(image, torch.Tensor):
159
+ image = self.image_processor.pil_to_numpy(image)
160
+ image = self.image_processor.numpy_to_pt(image)
161
+
162
+ # We normalize the image before resizing to match with the original implementation.
163
+ # Then we unnormalize it after resizing.
164
+ image = image * 2.0 - 1.0
165
+ image = _resize_with_antialiasing(image, (224, 224))
166
+ image = (image + 1.0) / 2.0
167
+
168
+ # Normalize the image with for CLIP input
169
+ image = self.feature_extractor(
170
+ images=image,
171
+ do_normalize=True,
172
+ do_center_crop=False,
173
+ do_resize=True,
174
+ do_rescale=False,
175
+ return_tensors="pt",
176
+ ).pixel_values
177
+
178
+ image = image.to(device=device, dtype=dtype)
179
+ image_embeddings = self.image_encoder(image).image_embeds
180
+ image_embeddings = image_embeddings.unsqueeze(1)
181
+
182
+ # duplicate image embeddings for each generation per prompt, using mps friendly method
183
+ bs_embed, seq_len, _ = image_embeddings.shape
184
+ image_embeddings = image_embeddings.repeat(1, num_videos_per_prompt, 1)
185
+ image_embeddings = image_embeddings.view(bs_embed * num_videos_per_prompt, seq_len, -1)
186
+
187
+ if do_classifier_free_guidance:
188
+ negative_image_embeddings = torch.zeros_like(image_embeddings)
189
+
190
+ # For classifier free guidance, we need to do two forward passes.
191
+ # Here we concatenate the unconditional and text embeddings into a single batch
192
+ # to avoid doing two forward passes
193
+ image_embeddings = torch.cat([negative_image_embeddings, image_embeddings])
194
+
195
+ return image_embeddings
196
+
197
+ def _encode_vae_image(
198
+ self,
199
+ image: torch.Tensor,
200
+ device: Union[str, torch.device],
201
+ num_videos_per_prompt: int,
202
+ do_classifier_free_guidance: bool,
203
+ ):
204
+ image = image.to(device=device)
205
+ image_latents = self.vae.encode(image).latent_dist.mode()
206
+
207
+ if do_classifier_free_guidance:
208
+ negative_image_latents = torch.zeros_like(image_latents)
209
+
210
+ # For classifier free guidance, we need to do two forward passes.
211
+ # Here we concatenate the unconditional and text embeddings into a single batch
212
+ # to avoid doing two forward passes
213
+ image_latents = torch.cat([negative_image_latents, image_latents])
214
+
215
+ # duplicate image_latents for each generation per prompt, using mps friendly method
216
+ image_latents = image_latents.repeat(num_videos_per_prompt, 1, 1, 1)
217
+
218
+ return image_latents
219
+
220
+ # ===== added part =====
221
+ def _encode_vae_image_mod(
222
+ self,
223
+ image: torch.Tensor,
224
+ device: Union[str, torch.device],
225
+ num_videos_per_prompt: int,
226
+ do_classifier_free_guidance: bool,
227
+ ):
228
+ image = image.to(device=device)
229
+ image_latents = self.vae.encode(image).latent_dist.mode()
230
+
231
+ # to make image_latents (batch * frame, ch, w, h) -> (batch, frame, ch, w, h)
232
+ # image_latents = image_latents.unsqueeze(0)
233
+
234
+ print("debug", image_latents.shape)
235
+ if do_classifier_free_guidance:
236
+ negative_image_latents = torch.zeros_like(image_latents)
237
+
238
+ # For classifier free guidance, we need to do two forward passes.
239
+ # Here we concatenate the unconditional and text embeddings into a single batch
240
+ # to avoid doing two forward passes
241
+ image_latents = torch.cat([negative_image_latents, image_latents])
242
+
243
+ # duplicate image_latents for each generation per prompt, using mps friendly method
244
+ image_latents = image_latents.repeat(num_videos_per_prompt, 1, 1, 1)
245
+
246
+ return image_latents
247
+ # ===== added part =====
248
+
249
+ def _get_add_time_ids(
250
+ self,
251
+ fps: int,
252
+ motion_bucket_id: int,
253
+ noise_aug_strength: float,
254
+ dtype: torch.dtype,
255
+ batch_size: int,
256
+ num_videos_per_prompt: int,
257
+ do_classifier_free_guidance: bool,
258
+ ):
259
+ add_time_ids = [fps, motion_bucket_id, noise_aug_strength]
260
+
261
+ passed_add_embed_dim = self.unet.config.addition_time_embed_dim * len(add_time_ids)
262
+ expected_add_embed_dim = self.unet.add_embedding.linear_1.in_features
263
+
264
+ if expected_add_embed_dim != passed_add_embed_dim:
265
+ raise ValueError(
266
+ f"Model expects an added time embedding vector of length {expected_add_embed_dim}, but a vector of {passed_add_embed_dim} was created. The model has an incorrect config. Please check `unet.config.time_embedding_type` and `text_encoder_2.config.projection_dim`."
267
+ )
268
+
269
+ add_time_ids = torch.tensor([add_time_ids], dtype=dtype)
270
+ add_time_ids = add_time_ids.repeat(batch_size * num_videos_per_prompt, 1)
271
+
272
+ if do_classifier_free_guidance:
273
+ add_time_ids = torch.cat([add_time_ids, add_time_ids])
274
+
275
+ return add_time_ids
276
+
277
+ def decode_latents(self, latents: torch.FloatTensor, num_frames: int, decode_chunk_size: int = 14):
278
+ # [batch, frames, channels, height, width] -> [batch*frames, channels, height, width]
279
+ latents = latents.flatten(0, 1)
280
+
281
+ latents = 1 / self.vae.config.scaling_factor * latents
282
+
283
+ forward_vae_fn = self.vae._orig_mod.forward if is_compiled_module(self.vae) else self.vae.forward
284
+ accepts_num_frames = "num_frames" in set(inspect.signature(forward_vae_fn).parameters.keys())
285
+
286
+ # decode decode_chunk_size frames at a time to avoid OOM
287
+ frames = []
288
+ for i in range(0, latents.shape[0], decode_chunk_size):
289
+ num_frames_in = latents[i : i + decode_chunk_size].shape[0]
290
+ decode_kwargs = {}
291
+ if accepts_num_frames:
292
+ # we only pass num_frames_in if it's expected
293
+ decode_kwargs["num_frames"] = num_frames_in
294
+
295
+ frame = self.vae.decode(latents[i : i + decode_chunk_size], **decode_kwargs).sample
296
+ frames.append(frame)
297
+ frames = torch.cat(frames, dim=0)
298
+
299
+ # [batch*frames, channels, height, width] -> [batch, channels, frames, height, width]
300
+ frames = frames.reshape(-1, num_frames, *frames.shape[1:]).permute(0, 2, 1, 3, 4)
301
+
302
+ # we always cast to float32 as this does not cause significant overhead and is compatible with bfloat16
303
+ frames = frames.float()
304
+ return frames
305
+
306
+ def check_inputs(self, image, height, width):
307
+ if (
308
+ not isinstance(image, torch.Tensor)
309
+ and not isinstance(image, PIL.Image.Image)
310
+ and not isinstance(image, list)
311
+ ):
312
+ raise ValueError(
313
+ "`image` has to be of type `torch.FloatTensor` or `PIL.Image.Image` or `List[PIL.Image.Image]` but is"
314
+ f" {type(image)}"
315
+ )
316
+
317
+ if height % 8 != 0 or width % 8 != 0:
318
+ raise ValueError(f"`height` and `width` have to be divisible by 8 but are {height} and {width}.")
319
+
320
+ def prepare_latents(
321
+ self,
322
+ batch_size: int,
323
+ num_frames: int,
324
+ num_channels_latents: int,
325
+ height: int,
326
+ width: int,
327
+ dtype: torch.dtype,
328
+ device: Union[str, torch.device],
329
+ generator: torch.Generator,
330
+ latents: Optional[torch.FloatTensor] = None,
331
+ ):
332
+ shape = (
333
+ batch_size,
334
+ num_frames,
335
+ num_channels_latents // 2,
336
+ height // self.vae_scale_factor,
337
+ width // self.vae_scale_factor,
338
+ )
339
+ if isinstance(generator, list) and len(generator) != batch_size:
340
+ raise ValueError(
341
+ f"You have passed a list of generators of length {len(generator)}, but requested an effective batch"
342
+ f" size of {batch_size}. Make sure the batch size matches the length of the generators."
343
+ )
344
+
345
+ if latents is None:
346
+ latents = randn_tensor(shape, generator=generator, device=device, dtype=dtype)
347
+ else:
348
+ latents = latents.to(device)
349
+
350
+ # scale the initial noise by the standard deviation required by the scheduler
351
+ latents = latents * self.scheduler.init_noise_sigma
352
+ return latents
353
+
354
+ @property
355
+ def guidance_scale(self):
356
+ return self._guidance_scale
357
+
358
+ # here `guidance_scale` is defined analog to the guidance weight `w` of equation (2)
359
+ # of the Imagen paper: https://arxiv.org/pdf/2205.11487.pdf . `guidance_scale = 1`
360
+ # corresponds to doing no classifier free guidance.
361
+ @property
362
+ def do_classifier_free_guidance(self):
363
+ if isinstance(self.guidance_scale, (int, float)):
364
+ return self.guidance_scale > 1
365
+ return self.guidance_scale.max() > 1
366
+
367
+ @property
368
+ def num_timesteps(self):
369
+ return self._num_timesteps
370
+
371
+ def tensor_to_vae_latent(self, t, vae):
372
+ video_length = t.shape[1]
373
+
374
+ t = rearrange(t, "b f c h w -> (b f) c h w")
375
+ # latents = vae.encode(t).latent_dist.sample()
376
+ latents = vae.encode(t).latent_dist.mode()
377
+ latents = rearrange(latents, "(b f) c h w -> b f c h w", f=video_length)
378
+ latents = latents * vae.config.scaling_factor
379
+
380
+ return latents
381
+
382
+ @torch.inference_mode()
383
+ def encode_video(
384
+ self,
385
+ video: torch.Tensor,
386
+ chunk_size: int = 14,
387
+ ) -> torch.Tensor:
388
+ """
389
+ :param video: [b, c, h, w] in range [0, 1], the b may contain multiple videos or frames
390
+ :param chunk_size: the chunk size to encode video
391
+ :return: image_embeddings in shape of [b, 1024]
392
+ """
393
+
394
+ video_224 = _resize_with_antialiasing(video.float(), (224, 224))
395
+ # video_224 = (video_224 + 1.0) / 2.0 # [-1, 1] -> [0, 1]
396
+
397
+ embeddings = []
398
+ for i in range(0, video_224.shape[0], chunk_size):
399
+ tmp = self.feature_extractor(
400
+ images=video_224[i : i + chunk_size],
401
+ do_normalize=True,
402
+ do_center_crop=False,
403
+ do_resize=False,
404
+ do_rescale=False,
405
+ return_tensors="pt",
406
+ ).pixel_values.to(video.device, dtype=video.dtype)
407
+ embeddings.append(self.image_encoder(tmp).image_embeds) # [b, 1024]
408
+
409
+ embeddings = torch.cat(embeddings, dim=0) # [t, 1024]
410
+ return embeddings
411
+
412
+ @torch.inference_mode()
413
+ def encode_video_batch(
414
+ self,
415
+ videos: torch.Tensor, # [B, F, C, H, W], RGB in [0, 1]
416
+ feature_extractor, # your CLIPImageProcessor
417
+ image_encoder, # your CLIPVisionModelWithProjection
418
+ size: tuple[int, int] = (224, 224), # target CLIP input size
419
+ ):
420
+ """
421
+ Returns:
422
+ torch.Tensor of shape [B, F, D] where D = image_encoder.projection_dim
423
+ """
424
+ B, F, C, H, W = videos.shape
425
+
426
+ # 1) collapse B & F into a single β€œimage batch” of shape [B*F, C, H, W]
427
+ frames = videos.view(B * F, C, H, W)
428
+
429
+ # 2) resize + un-normalize β†’ [0,1]
430
+ frames = _resize_with_antialiasing(frames, size) # reuse your existing resize helper
431
+ # frames = (frames + 1.0) / 2.0 # clip expects [0–1]
432
+
433
+ # 3) run through CLIP preprocessor & encoder
434
+ # feature_extractor can accept a tensor of shape [batch, C, H, W]
435
+ encoding = feature_extractor(
436
+ images=frames,
437
+ do_resize=False, # we already resized
438
+ do_center_crop=False,
439
+ do_normalize=True,
440
+ return_tensors="pt",
441
+ )
442
+ pixel_values = encoding.pixel_values.to(frames.device) # [B*F, 3, 224, 224]
443
+
444
+ embeds = image_encoder(pixel_values).image_embeds # [B*F, D]
445
+
446
+ # 4) restore [B, F, D]
447
+ return embeds.view(B, F, -1)
448
+
449
+ @torch.inference_mode()
450
+ def encode_vae_video(
451
+ self,
452
+ video: torch.Tensor,
453
+ chunk_size: int = 14,
454
+ ):
455
+ """
456
+ :param video: [b, c, h, w] in range [-1, 1], the b may contain multiple videos or frames
457
+ :param chunk_size: the chunk size to encode video
458
+ :return: vae latents in shape of [b, c, h, w]
459
+ """
460
+ video_latents = []
461
+ for i in range(0, video.shape[0], chunk_size):
462
+ video_latents.append(
463
+ self.vae.encode(video[i : i + chunk_size]).latent_dist.mode()
464
+ )
465
+ video_latents = torch.cat(video_latents, dim=0)
466
+ return video_latents
467
+
468
+ @torch.no_grad()
469
+ @replace_example_docstring(EXAMPLE_DOC_STRING)
470
+ def __call__(
471
+ self,
472
+ # image: Union[PIL.Image.Image, List[PIL.Image.Image], torch.FloatTensor], # We highly recommend only use the torch.FloatTensor
473
+ video: Union[np.ndarray, torch.Tensor],
474
+ g_buffer: Union[PIL.Image.Image, List[PIL.Image.Image], torch.FloatTensor, List[torch.FloatTensor]],
475
+ height: int = 576,
476
+ width: int = 1024,
477
+ num_frames: Optional[int] = None,
478
+ num_inference_steps: int = 25,
479
+ min_guidance_scale: float = 1.0,
480
+ max_guidance_scale: float = 3.0,
481
+ fps: int = 7,
482
+ motion_bucket_id: int = 127,
483
+ noise_aug_strength: float = 0.02,
484
+ decode_chunk_size: Optional[int] = None,
485
+ num_videos_per_prompt: Optional[int] = 1,
486
+ generator: Optional[Union[torch.Generator, List[torch.Generator]]] = None,
487
+ latents: Optional[torch.FloatTensor] = None,
488
+ output_type: Optional[str] = "pil",
489
+ callback_on_step_end: Optional[Callable[[int, int, Dict], None]] = None,
490
+ callback_on_step_end_tensor_inputs: List[str] = ["latents"],
491
+ return_dict: bool = True,
492
+ ):
493
+ r"""
494
+ The call function to the pipeline for generation.
495
+
496
+ Args:
497
+ image (`PIL.Image.Image` or `List[PIL.Image.Image]` or `torch.FloatTensor`):
498
+ Image(s) to guide image generation. If you provide a tensor, the expected value range is between `[0, 1]`.
499
+ height (`int`, *optional*, defaults to `self.unet.config.sample_size * self.vae_scale_factor`):
500
+ The height in pixels of the generated image.
501
+ width (`int`, *optional*, defaults to `self.unet.config.sample_size * self.vae_scale_factor`):
502
+ The width in pixels of the generated image.
503
+ num_frames (`int`, *optional*):
504
+ The number of video frames to generate. Defaults to `self.unet.config.num_frames`
505
+ (14 for `stable-video-diffusion-img2vid` and to 25 for `stable-video-diffusion-img2vid-xt`).
506
+ num_inference_steps (`int`, *optional*, defaults to 25):
507
+ The number of denoising steps. More denoising steps usually lead to a higher quality video at the
508
+ expense of slower inference. This parameter is modulated by `strength`.
509
+ min_guidance_scale (`float`, *optional*, defaults to 1.0):
510
+ The minimum guidance scale. Used for the classifier free guidance with first frame.
511
+ max_guidance_scale (`float`, *optional*, defaults to 3.0):
512
+ The maximum guidance scale. Used for the classifier free guidance with last frame.
513
+ fps (`int`, *optional*, defaults to 7):
514
+ Frames per second. The rate at which the generated images shall be exported to a video after generation.
515
+ Note that Stable Diffusion Video's UNet was micro-conditioned on fps-1 during training.
516
+ motion_bucket_id (`int`, *optional*, defaults to 127):
517
+ Used for conditioning the amount of motion for the generation. The higher the number the more motion
518
+ will be in the video.
519
+ noise_aug_strength (`float`, *optional*, defaults to 0.02):
520
+ The amount of noise added to the init image, the higher it is the less the video will look like the init image. Increase it for more motion.
521
+ decode_chunk_size (`int`, *optional*):
522
+ The number of frames to decode at a time. Higher chunk size leads to better temporal consistency at the expense of more memory usage. By default, the decoder decodes all frames at once for maximal
523
+ quality. For lower memory usage, reduce `decode_chunk_size`.
524
+ num_videos_per_prompt (`int`, *optional*, defaults to 1):
525
+ The number of videos to generate per prompt.
526
+ generator (`torch.Generator` or `List[torch.Generator]`, *optional*):
527
+ A [`torch.Generator`](https://pytorch.org/docs/stable/generated/torch.Generator.html) to make
528
+ generation deterministic.
529
+ latents (`torch.FloatTensor`, *optional*):
530
+ Pre-generated noisy latents sampled from a Gaussian distribution, to be used as inputs for video
531
+ generation. Can be used to tweak the same generation with different prompts. If not provided, a latents
532
+ tensor is generated by sampling using the supplied random `generator`.
533
+ output_type (`str`, *optional*, defaults to `"pil"`):
534
+ The output format of the generated image. Choose between `pil`, `np` or `pt`.
535
+ callback_on_step_end (`Callable`, *optional*):
536
+ A function that is called at the end of each denoising step during inference. The function is called
537
+ with the following arguments:
538
+ `callback_on_step_end(self: DiffusionPipeline, step: int, timestep: int, callback_kwargs: Dict)`.
539
+ `callback_kwargs` will include a list of all tensors as specified by `callback_on_step_end_tensor_inputs`.
540
+ callback_on_step_end_tensor_inputs (`List`, *optional*):
541
+ The list of tensor inputs for the `callback_on_step_end` function. The tensors specified in the list
542
+ will be passed as `callback_kwargs` argument. You will only be able to include variables listed in the
543
+ `._callback_tensor_inputs` attribute of your pipeline class.
544
+ return_dict (`bool`, *optional*, defaults to `True`):
545
+ Whether or not to return a [`~pipelines.stable_diffusion.StableDiffusionPipelineOutput`] instead of a
546
+ plain tuple.
547
+
548
+ Examples:
549
+
550
+ Returns:
551
+ [`~pipelines.stable_diffusion.StableVideoDiffusionPipelineOutput`] or `tuple`:
552
+ If `return_dict` is `True`, [`~pipelines.stable_diffusion.StableVideoDiffusionPipelineOutput`] is returned,
553
+ otherwise a `tuple` of (`List[List[PIL.Image.Image]]` or `np.ndarray` or `torch.FloatTensor`) is returned.
554
+ """
555
+ # 0. Default height and width to unet
556
+ height = height or self.unet.config.sample_size * self.vae_scale_factor
557
+ width = width or self.unet.config.sample_size * self.vae_scale_factor
558
+
559
+ num_frames = video.shape[1]
560
+ decode_chunk_size = decode_chunk_size if decode_chunk_size is not None else num_frames
561
+
562
+ # 1. Check inputs. Raise error if not correct
563
+ self.check_inputs(video, height, width)
564
+
565
+ # # 2. Define call parameters
566
+ # if isinstance(image, PIL.Image.Image):
567
+ # batch_size = 1
568
+ # elif isinstance(image, list):
569
+ # batch_size = len(image)
570
+ # else:
571
+ batch_size = video.shape[0]
572
+
573
+ # ===== added part =====
574
+ print("batch_size", batch_size)
575
+ # batch_size = 1
576
+
577
+ # ===== added part =====
578
+
579
+ device = self._execution_device
580
+ # here `guidance_scale` is defined analog to the guidance weight `w` of equation (2)
581
+ # of the Imagen paper: https://arxiv.org/pdf/2205.11487.pdf . `guidance_scale = 1`
582
+ # corresponds to doing no classifier free guidance.
583
+ self._guidance_scale = max_guidance_scale
584
+
585
+ # 3. Encode input video
586
+ if isinstance(video, np.ndarray):
587
+ video = torch.from_numpy(video.transpose(0, 3, 1, 2))
588
+ else:
589
+ assert isinstance(video, torch.Tensor)
590
+ video = video.to(device=device, dtype=self.dtype)
591
+ video = (video + 1) / 2.0 # [-1, 1] -> [0, 1], in [t, c, h, w]
592
+
593
+ # image_embeddings = self._encode_image(image, device, num_videos_per_prompt, False) # self.do_classifier_free_guidance
594
+ # video_embeddings = self.encode_video_batch(video, chunk_size=decode_chunk_size).unsqueeze(0) # [1, t, 1024]
595
+ video_embeddings = self.encode_video_batch(video, self.feature_extractor,self.image_encoder) # β†’ [B, F, D]
596
+
597
+ # print(video_embeddings.shape)
598
+
599
+ video_embeddings = video_embeddings.view(batch_size * num_frames, 1, 1024)
600
+ torch.cuda.empty_cache()
601
+
602
+ # temp_emb = self._encode_image(image[0], device, num_videos_per_prompt, self.do_classifier_free_guidance)
603
+ # print("should be ", image_embeddings.shape, "to", temp_emb.shape)
604
+
605
+ # NOTE: Stable Video Diffusion was conditioned on fps - 1, which is why it is reduced here.
606
+ # See: https://github.com/Stability-AI/generative-models/blob/ed0997173f98eaf8f4edf7ba5fe8f15c6b877fd3/scripts/sampling/simple_video_sample.py#L188
607
+ fps = fps - 1
608
+
609
+ # 4. Encode input image using VAE
610
+ # image = self.image_processor.preprocess(image, height=height, width=width).to(device)
611
+ noise = randn_tensor(video.shape, generator=generator, device=device, dtype=video.dtype)
612
+ # image = image + noise_aug_strength * noise
613
+ video = video + noise_aug_strength * noise # in [t, c, h, w]
614
+
615
+ # ===== added part =====
616
+ # image was -1 to 1
617
+ # depths, normals, albedos, scribbles = g_buffer
618
+ # first, load images from Relight images, depth, normal, albedo, mask, original images
619
+
620
+ rgbs, depths, normals, albedos, scribbles, pixel_values = g_buffer
621
+ # enc_depth, enc_nrm, enc_alb, enc_scb = g_buffer
622
+
623
+ # 2nd, repeat 1-ch to 3-ch for depth and scribbles
624
+ depths_exp = depths.repeat(1, 1, 3, 1, 1) # Expand along dim=2 to 3 channels
625
+ scribbles_exp = scribbles.repeat(1, 1, 3, 1, 1) # Expand along dim=2 to 3 channels
626
+
627
+ # ===== added part =====
628
+
629
+ needs_upcasting = self.vae.dtype == torch.float16 and self.vae.config.force_upcast
630
+ if needs_upcasting:
631
+ self.vae.to(dtype=torch.float32)
632
+
633
+ # ===== added part =====
634
+ # image_latents = self._encode_vae_image(
635
+ # image,
636
+ # device=device,
637
+ # num_videos_per_prompt=num_videos_per_prompt,
638
+ # do_classifier_free_guidance=False,
639
+ # ) # self.do_classifier_free_guidance
640
+
641
+ # video_latents = self.encode_vae_video(
642
+ # video.to(self.vae.dtype),
643
+ # chunk_size=decode_chunk_size,
644
+ # ).unsqueeze(0) # [b, t, c, h, w]
645
+ # torch.cuda.empty_cache()
646
+
647
+ video_latents = self.tensor_to_vae_latent(video, self.vae)
648
+ torch.cuda.empty_cache()
649
+
650
+ # ===== added part =====
651
+
652
+ # image_latents = image_latents.to(image_embeddings.dtype)
653
+
654
+ # cast back to fp16 if needed
655
+ if needs_upcasting:
656
+ self.vae.to(dtype=torch.float16)
657
+
658
+ # Repeat the image latents for each frame so we can concatenate them with the noise
659
+ # image_latents [batch, channels, height, width] ->[batch, num_frames, channels, height, width]
660
+ # image_latents = image_latents.unsqueeze(1).repeat(1, num_frames, 1, 1, 1)
661
+
662
+ # print(' before', video_latents.shape)
663
+
664
+ # # image_latents [batch*num_frames, channels, height, width] ->[batch, num_frames, channels, height, width]
665
+ # video_length = image.shape[0]
666
+ # image_latents = rearrange(image_latents, "(b f) c h w -> b f c h w", f=video_length)
667
+
668
+ print(' after', video_latents.shape, "(b f) c h w -> b f c h w")
669
+
670
+ # ===== added part =====
671
+ # 3rd, convert images to latent space then concatenate
672
+ with torch.no_grad():
673
+ enc_rgb = self.tensor_to_vae_latent(rgbs, self.vae)
674
+ enc_depth = self.tensor_to_vae_latent(depths_exp, self.vae)
675
+ enc_nrm = self.tensor_to_vae_latent(normals, self.vae)
676
+ enc_alb = self.tensor_to_vae_latent(albedos, self.vae)
677
+ enc_scb = self.tensor_to_vae_latent(scribbles_exp, self.vae)
678
+
679
+ # enc_relight = self.tensor_to_vae_latent(pixel_values, self.vae)
680
+
681
+ if False: # self.do_classifier_free_guidance
682
+ negative_image_embeddings = torch.zeros_like(enc_depth)
683
+ enc_rgb = torch.cat([negative_image_embeddings, enc_rgb])
684
+
685
+ enc_depth = torch.cat([negative_image_embeddings, enc_depth])
686
+ enc_nrm = torch.cat([negative_image_embeddings, enc_nrm])
687
+ enc_alb = torch.cat([negative_image_embeddings, enc_alb])
688
+ enc_scb = torch.cat([negative_image_embeddings, enc_scb])
689
+
690
+ add_latents = torch.cat([enc_rgb, enc_depth, enc_nrm, enc_alb, enc_scb], dim=2)
691
+ # add_latents = torch.cat([enc_depth, enc_nrm, enc_alb, enc_scb], dim=2)
692
+
693
+ # πŸš€ Free memory after use
694
+ del enc_rgb, enc_depth, enc_nrm, enc_alb, enc_scb
695
+
696
+ # print(video_latents.shape)
697
+ # print(add_latents.shape)
698
+ # ===== added part =====
699
+
700
+ # 5. Get Added Time IDs
701
+ added_time_ids = self._get_add_time_ids(
702
+ fps,
703
+ motion_bucket_id,
704
+ noise_aug_strength,
705
+ video_embeddings.dtype,
706
+ batch_size,
707
+ num_videos_per_prompt,
708
+ False,
709
+ ) # self.do_classifier_free_guidance
710
+ added_time_ids = added_time_ids.to(device)
711
+
712
+ # 6. Prepare timesteps
713
+ self.scheduler.set_timesteps(num_inference_steps, device=device)
714
+ timesteps = self.scheduler.timesteps
715
+ # print("time steps:", self.scheduler.timesteps)
716
+
717
+ # remapped_timesteps = (timesteps - timesteps.min()) / (timesteps.max() - timesteps.min()) * (1.47 - (-0.8)) + (-0.8)
718
+ # timesteps = remapped_timesteps.cpu()
719
+ # self.scheduler.config.use_karras_sigmas = False
720
+ # self.scheduler.config.prediction_type = None
721
+ # self.scheduler.set_timesteps(timesteps=timesteps)
722
+
723
+ # print("time steps:", timesteps)
724
+
725
+ # print("scheduler steps:", self.scheduler.timesteps)
726
+ # self.scheduler.config.prediction_type = "v_prediction"
727
+
728
+ # 7. Prepare latent variables
729
+ # num_channels_latents = self.unet.config.in_channels
730
+ num_channels_latents = 8
731
+
732
+ # print('check pipeline')
733
+ # print(batch_size * num_videos_per_prompt)
734
+ # print(height, width)
735
+ # print(num_frames)
736
+
737
+ latents = self.prepare_latents(
738
+ batch_size * num_videos_per_prompt,
739
+ num_frames,
740
+ num_channels_latents,
741
+ height,
742
+ width,
743
+ video_embeddings.dtype,
744
+ device,
745
+ generator,
746
+ latents,
747
+ )
748
+
749
+ # 8. Prepare guidance scale
750
+ guidance_scale = torch.linspace(min_guidance_scale, max_guidance_scale, num_frames).unsqueeze(0)
751
+ guidance_scale = guidance_scale.to(device, latents.dtype)
752
+ guidance_scale = guidance_scale.repeat(batch_size * num_videos_per_prompt, 1)
753
+ guidance_scale = _append_dims(guidance_scale, latents.ndim)
754
+
755
+ self._guidance_scale = guidance_scale
756
+
757
+ # 9. Denoising loop
758
+ num_warmup_steps = len(timesteps) - num_inference_steps * self.scheduler.order
759
+ self._num_timesteps = len(timesteps)
760
+ with self.progress_bar(total=num_inference_steps) as progress_bar:
761
+ for i, t in enumerate(timesteps):
762
+ # expand the latents if we are doing classifier free guidance
763
+ latent_model_input = torch.cat([latents] * 2) if False else latents # self.do_classifier_free_guidance
764
+ # print(latents.shape)
765
+ # print(latent_model_input.shape)
766
+ # ===== added part =====
767
+ latent_model_input = self.scheduler.scale_model_input(latent_model_input, t)
768
+ # ===== added part =====
769
+
770
+ # print(latent_model_input.shape)
771
+ # print(image_latents.shape)
772
+
773
+ # Concatenate image_latents over channels dimension
774
+ # latent_model_input = torch.cat([latent_model_input, image_latents], dim=2)
775
+
776
+ # ===== added part =====
777
+ # print(latent_model_input.shape, video_latents.shape, add_latents.shape)
778
+
779
+ latent_model_input = torch.cat([latent_model_input, video_latents, add_latents], dim=2)
780
+
781
+ # print(latent_model_input.shape)
782
+ # print(t.shape)
783
+ # print(video_embeddings.shape)
784
+
785
+ # ===== added part =====
786
+
787
+ # predict the noise residual
788
+ with torch.no_grad():
789
+ noise_pred = self.unet(
790
+ latent_model_input,
791
+ t,
792
+ encoder_hidden_states=video_embeddings,
793
+ added_time_ids=added_time_ids,
794
+ return_dict=False,
795
+ )[0]
796
+
797
+ # perform guidance
798
+ if False: # self.do_classifier_free_guidance
799
+ noise_pred_uncond, noise_pred_cond = noise_pred.chunk(2)
800
+ noise_pred = noise_pred_uncond + self.guidance_scale * (noise_pred_cond - noise_pred_uncond)
801
+
802
+ # compute the previous noisy sample x_t -> x_t-1
803
+ latents = self.scheduler.step(noise_pred, t, latents).prev_sample
804
+
805
+ if callback_on_step_end is not None:
806
+ callback_kwargs = {}
807
+ for k in callback_on_step_end_tensor_inputs:
808
+ callback_kwargs[k] = locals()[k]
809
+ callback_outputs = callback_on_step_end(self, i, t, callback_kwargs)
810
+
811
+ latents = callback_outputs.pop("latents", latents)
812
+
813
+ if i == len(timesteps) - 1 or ((i + 1) > num_warmup_steps and (i + 1) % self.scheduler.order == 0):
814
+ progress_bar.update()
815
+
816
+ if not output_type == "latent":
817
+ # cast back to fp16 if needed
818
+ if needs_upcasting:
819
+ self.vae.to(dtype=torch.float16)
820
+ frames = self.decode_latents(latents, num_frames, decode_chunk_size)
821
+ frames = tensor2vid(frames, self.image_processor, output_type=output_type)
822
+ else:
823
+ frames = latents
824
+
825
+ # # Define the MSE loss function
826
+ # criterion = torch.nn.MSELoss()
827
+ # loss = criterion(enc_relight, latents)
828
+
829
+ # print("latents loss:", loss)
830
+
831
+
832
+
833
+ # # P_mean=0.7 P_std=1.6
834
+ # sigmas = rand_log_normal(shape=[1,], loc=0.7, scale=1.6).to(latents.device)
835
+ # # Add noise to the latents according to the noise magnitude at each timestep
836
+ # # (this is the forward diffusion process)
837
+ # sigmas = sigmas[:, None, None, None, None]
838
+ # weighing = (1 + sigmas ** 2) * (sigmas**-2.0)
839
+
840
+ # cond_sigmas = rand_log_normal(shape=[1,], loc=-3.0, scale=0.5).to(latents.device)
841
+ # noise_aug_strength = cond_sigmas[0] # TODO: support batch > 1
842
+ # # print("noise_aug_strength", noise_aug_strength)
843
+
844
+ # # MSE loss
845
+ # loss = torch.mean(
846
+ # (weighing.float() * (enc_relight.float() -
847
+ # latents.float()) ** 2).reshape(latents.shape[0], -1),
848
+ # dim=1,
849
+ # )
850
+ # loss = loss.mean()
851
+ # print("train loss:", loss)
852
+ # # print("train weighing:", weighing)
853
+
854
+ self.maybe_free_model_hooks()
855
+
856
+ if not return_dict:
857
+ return frames
858
+
859
+ return StableVideoDiffusionPipelineOutput(frames=frames)
860
+
861
+
862
+ # resizing utils
863
+ # TODO: clean up later
864
+ def _resize_with_antialiasing(input, size, interpolation="bicubic", align_corners=True):
865
+ h, w = input.shape[-2:]
866
+ factors = (h / size[0], w / size[1])
867
+
868
+ # First, we have to determine sigma
869
+ # Taken from skimage: https://github.com/scikit-image/scikit-image/blob/v0.19.2/skimage/transform/_warps.py#L171
870
+ sigmas = (
871
+ max((factors[0] - 1.0) / 2.0, 0.001),
872
+ max((factors[1] - 1.0) / 2.0, 0.001),
873
+ )
874
+
875
+ # Now kernel size. Good results are for 3 sigma, but that is kind of slow. Pillow uses 1 sigma
876
+ # https://github.com/python-pillow/Pillow/blob/master/src/libImaging/Resample.c#L206
877
+ # But they do it in the 2 passes, which gives better results. Let's try 2 sigmas for now
878
+ ks = int(max(2.0 * 2 * sigmas[0], 3)), int(max(2.0 * 2 * sigmas[1], 3))
879
+
880
+ # Make sure it is odd
881
+ if (ks[0] % 2) == 0:
882
+ ks = ks[0] + 1, ks[1]
883
+
884
+ if (ks[1] % 2) == 0:
885
+ ks = ks[0], ks[1] + 1
886
+
887
+ input = _gaussian_blur2d(input, ks, sigmas)
888
+
889
+ output = torch.nn.functional.interpolate(input, size=size, mode=interpolation, align_corners=align_corners)
890
+ return output
891
+
892
+
893
+ def _compute_padding(kernel_size):
894
+ """Compute padding tuple."""
895
+ # 4 or 6 ints: (padding_left, padding_right,padding_top,padding_bottom)
896
+ # https://pytorch.org/docs/stable/nn.html#torch.nn.functional.pad
897
+ if len(kernel_size) < 2:
898
+ raise AssertionError(kernel_size)
899
+ computed = [k - 1 for k in kernel_size]
900
+
901
+ # for even kernels we need to do asymmetric padding :(
902
+ out_padding = 2 * len(kernel_size) * [0]
903
+
904
+ for i in range(len(kernel_size)):
905
+ computed_tmp = computed[-(i + 1)]
906
+
907
+ pad_front = computed_tmp // 2
908
+ pad_rear = computed_tmp - pad_front
909
+
910
+ out_padding[2 * i + 0] = pad_front
911
+ out_padding[2 * i + 1] = pad_rear
912
+
913
+ return out_padding
914
+
915
+
916
+ def _filter2d(input, kernel):
917
+ # prepare kernel
918
+ b, c, h, w = input.shape
919
+ tmp_kernel = kernel[:, None, ...].to(device=input.device, dtype=input.dtype)
920
+
921
+ tmp_kernel = tmp_kernel.expand(-1, c, -1, -1)
922
+
923
+ height, width = tmp_kernel.shape[-2:]
924
+
925
+ padding_shape: list[int] = _compute_padding([height, width])
926
+ input = torch.nn.functional.pad(input, padding_shape, mode="reflect")
927
+
928
+ # kernel and input tensor reshape to align element-wise or batch-wise params
929
+ tmp_kernel = tmp_kernel.reshape(-1, 1, height, width)
930
+ input = input.view(-1, tmp_kernel.size(0), input.size(-2), input.size(-1))
931
+
932
+ # convolve the tensor with the kernel.
933
+ output = torch.nn.functional.conv2d(input, tmp_kernel, groups=tmp_kernel.size(0), padding=0, stride=1)
934
+
935
+ out = output.view(b, c, h, w)
936
+ return out
937
+
938
+
939
+ def _gaussian(window_size: int, sigma):
940
+ if isinstance(sigma, float):
941
+ sigma = torch.tensor([[sigma]])
942
+
943
+ batch_size = sigma.shape[0]
944
+
945
+ x = (torch.arange(window_size, device=sigma.device, dtype=sigma.dtype) - window_size // 2).expand(batch_size, -1)
946
+
947
+ if window_size % 2 == 0:
948
+ x = x + 0.5
949
+
950
+ gauss = torch.exp(-x.pow(2.0) / (2 * sigma.pow(2.0)))
951
+
952
+ return gauss / gauss.sum(-1, keepdim=True)
953
+
954
+
955
+ def _gaussian_blur2d(input, kernel_size, sigma):
956
+ if isinstance(sigma, tuple):
957
+ sigma = torch.tensor([sigma], dtype=input.dtype)
958
+ else:
959
+ sigma = sigma.to(dtype=input.dtype)
960
+
961
+ ky, kx = int(kernel_size[0]), int(kernel_size[1])
962
+ bs = sigma.shape[0]
963
+ kernel_x = _gaussian(kx, sigma[:, 1].view(bs, 1))
964
+ kernel_y = _gaussian(ky, sigma[:, 0].view(bs, 1))
965
+ out_x = _filter2d(input, kernel_x[..., None, :])
966
+ out = _filter2d(out_x, kernel_y[..., None])
967
+
968
+ return out
utils/unet_spatio_temporal_condition.py ADDED
@@ -0,0 +1,623 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ from dataclasses import dataclass
2
+ from typing import Dict, Optional, Tuple, Union
3
+
4
+ import torch
5
+ import torch.nn as nn
6
+
7
+ from diffusers.configuration_utils import ConfigMixin, register_to_config
8
+ from diffusers.loaders import UNet2DConditionLoadersMixin
9
+ from diffusers.utils import BaseOutput, logging
10
+ from diffusers.models.attention_processor import CROSS_ATTENTION_PROCESSORS, AttentionProcessor, AttnProcessor
11
+ from diffusers.models.embeddings import TimestepEmbedding, Timesteps
12
+ from diffusers.models.modeling_utils import ModelMixin
13
+ from diffusers.models.unets.unet_3d_blocks import UNetMidBlockSpatioTemporal, get_down_block, get_up_block
14
+
15
+ logger = logging.get_logger(__name__) # pylint: disable=invalid-name
16
+
17
+ # import os
18
+ # import numpy as np
19
+ # import matplotlib.pyplot as plt
20
+ # import seaborn as sns
21
+ # import os
22
+
23
+ # def format_time_step(t):
24
+ # """
25
+ # Format time step as a 5-digit string from a float value.
26
+
27
+ # Examples:
28
+ # 0.1234 -> '01234'
29
+ # 0.1534 -> '01534'
30
+ # 0.0001 -> '00001'
31
+ # 0.9999 -> '99999'
32
+
33
+ # Args:
34
+ # - t (float): Time step value between 0 and 1
35
+
36
+ # Returns:
37
+ # - str: 5-digit representation of the time step
38
+ # """
39
+ # if t is None:
40
+ # return ""
41
+
42
+ # # Ensure t is within [0, 1]
43
+ # t = max(0, min(1, t))
44
+
45
+ # # Convert to 5 digits, removing the leading "0."
46
+ # time_step_str = f"{t:.4f}"[2:7]
47
+
48
+ # return f"t_{time_step_str}_"
49
+
50
+ # def visualize_tensors(tensor_emb, encoder_hidden_states, t=None, output_dir='latent_visualization'):
51
+ # """
52
+ # Visualize tensor embeddings and hidden states as heatmaps with time step tracking.
53
+
54
+ # Args:
55
+ # - tensor_emb (torch.Tensor): Embedding tensor with shape [32, 1280]
56
+ # - encoder_hidden_states (torch.Tensor): Hidden states with shape [32, 1, 1024]
57
+ # - t (int, optional): Time step for filename labeling
58
+ # - output_dir (str): Directory to save visualization images
59
+ # """
60
+ # os.makedirs(output_dir, exist_ok=True)
61
+
62
+ # # Prepare time step string for filenames
63
+ # time_step_str = format_time_step(t)
64
+
65
+ # # Convert tensors to numpy for visualization
66
+ # emb_numpy = tensor_emb.detach().cpu().numpy()
67
+ # hidden_numpy = encoder_hidden_states.squeeze().detach().cpu().numpy()
68
+
69
+ # # Visualization 1: Embedding Tensor Heatmap
70
+ # plt.figure(figsize=(15, 10))
71
+ # sns.heatmap(emb_numpy, cmap='viridis', center=0)
72
+ # plt.title(f'Embedding Tensor Visualization (Time Step {t})' if t is not None else 'Embedding Tensor Visualization')
73
+ # plt.xlabel('Embedding Dimensions')
74
+ # plt.ylabel('Batch Samples')
75
+ # plt.tight_layout()
76
+ # plt.savefig(os.path.join(output_dir, f'{time_step_str}embedding_heatmap.png'))
77
+ # plt.close()
78
+
79
+ # # Visualization 2: Hidden States Heatmap
80
+ # plt.figure(figsize=(15, 10))
81
+ # sns.heatmap(hidden_numpy, cmap='coolwarm', center=0)
82
+ # plt.title(f'Encoder Hidden States Visualization (Time Step {t})' if t is not None else 'Encoder Hidden States Visualization')
83
+ # plt.xlabel('Hidden State Dimensions')
84
+ # plt.ylabel('Batch Samples')
85
+ # plt.tight_layout()
86
+ # plt.savefig(os.path.join(output_dir, f'{time_step_str}hidden_states_heatmap.png'))
87
+ # plt.close()
88
+
89
+ # # Visualization 3: PCA Reduction for Higher Dimensional Insight
90
+ # from sklearn.decomposition import PCA
91
+
92
+ # def plot_pca(data, title, filename):
93
+ # pca = PCA(n_components=2)
94
+ # pca_result = pca.fit_transform(data)
95
+
96
+ # plt.figure(figsize=(10, 8))
97
+ # plt.scatter(pca_result[:, 0], pca_result[:, 1], c=np.arange(len(pca_result)), cmap='viridis')
98
+ # plt.colorbar(label='Sample Index')
99
+ # plt.title(title)
100
+ # plt.xlabel('First Principal Component')
101
+ # plt.ylabel('Second Principal Component')
102
+ # plt.tight_layout()
103
+ # plt.savefig(os.path.join(output_dir, filename))
104
+ # plt.close()
105
+
106
+ # # PCA Visualizations with time step in filename
107
+ # plot_pca(emb_numpy,
108
+ # f'PCA of Embedding Tensor (Time Step {t})' if t is not None else 'PCA of Embedding Tensor',
109
+ # f'{time_step_str}embedding_pca.png')
110
+ # plot_pca(hidden_numpy,
111
+ # f'PCA of Hidden States (Time Step {t})' if t is not None else 'PCA of Hidden States',
112
+ # f'{time_step_str}hidden_states_pca.png')
113
+
114
+ # print(f"Visualizations saved in {output_dir}")
115
+
116
+
117
+ @dataclass
118
+ class UNetSpatioTemporalConditionOutput(BaseOutput):
119
+ """
120
+ The output of [`UNetSpatioTemporalConditionModel`].
121
+
122
+ Args:
123
+ sample (`torch.Tensor` of shape `(batch_size, num_frames, num_channels, height, width)`):
124
+ The hidden states output conditioned on `encoder_hidden_states` input. Output of last layer of model.
125
+ """
126
+
127
+ sample: torch.Tensor = None
128
+
129
+
130
+ class UNetSpatioTemporalConditionModel(ModelMixin, ConfigMixin, UNet2DConditionLoadersMixin):
131
+ r"""
132
+ A conditional Spatio-Temporal UNet model that takes a noisy video frames, conditional state, and a timestep and
133
+ returns a sample shaped output.
134
+
135
+ This model inherits from [`ModelMixin`]. Check the superclass documentation for it's generic methods implemented
136
+ for all models (such as downloading or saving).
137
+
138
+ Parameters:
139
+ sample_size (`int` or `Tuple[int, int]`, *optional*, defaults to `None`):
140
+ Height and width of input/output sample.
141
+ in_channels (`int`, *optional*, defaults to 8): Number of channels in the input sample.
142
+ out_channels (`int`, *optional*, defaults to 4): Number of channels in the output.
143
+ down_block_types (`Tuple[str]`, *optional*, defaults to `("CrossAttnDownBlockSpatioTemporal", "CrossAttnDownBlockSpatioTemporal", "CrossAttnDownBlockSpatioTemporal", "DownBlockSpatioTemporal")`):
144
+ The tuple of downsample blocks to use.
145
+ up_block_types (`Tuple[str]`, *optional*, defaults to `("UpBlockSpatioTemporal", "CrossAttnUpBlockSpatioTemporal", "CrossAttnUpBlockSpatioTemporal", "CrossAttnUpBlockSpatioTemporal")`):
146
+ The tuple of upsample blocks to use.
147
+ block_out_channels (`Tuple[int]`, *optional*, defaults to `(320, 640, 1280, 1280)`):
148
+ The tuple of output channels for each block.
149
+ addition_time_embed_dim: (`int`, defaults to 256):
150
+ Dimension to to encode the additional time ids.
151
+ projection_class_embeddings_input_dim (`int`, defaults to 768):
152
+ The dimension of the projection of encoded `added_time_ids`.
153
+ layers_per_block (`int`, *optional*, defaults to 2): The number of layers per block.
154
+ cross_attention_dim (`int` or `Tuple[int]`, *optional*, defaults to 1280):
155
+ The dimension of the cross attention features.
156
+ transformer_layers_per_block (`int`, `Tuple[int]`, or `Tuple[Tuple]` , *optional*, defaults to 1):
157
+ The number of transformer blocks of type [`~models.attention.BasicTransformerBlock`]. Only relevant for
158
+ [`~models.unets.unet_3d_blocks.CrossAttnDownBlockSpatioTemporal`],
159
+ [`~models.unets.unet_3d_blocks.CrossAttnUpBlockSpatioTemporal`],
160
+ [`~models.unets.unet_3d_blocks.UNetMidBlockSpatioTemporal`].
161
+ num_attention_heads (`int`, `Tuple[int]`, defaults to `(5, 10, 10, 20)`):
162
+ The number of attention heads.
163
+ dropout (`float`, *optional*, defaults to 0.0): The dropout probability to use.
164
+ """
165
+
166
+ _supports_gradient_checkpointing = True
167
+
168
+ @register_to_config
169
+ def __init__(
170
+ self,
171
+ sample_size: Optional[int] = None,
172
+ # in_channels: int = 24, # 8 + 16 additional (latents+conditional) + (depths, normals, albedos, scribbles)
173
+ in_channels: int = 28, # 8 + 20 additional (latents+conditional) + (rgbs, depths, normals, albedos, scribbles)
174
+ out_channels: int = 4,
175
+ down_block_types: Tuple[str] = (
176
+ "CrossAttnDownBlockSpatioTemporal",
177
+ "CrossAttnDownBlockSpatioTemporal",
178
+ "CrossAttnDownBlockSpatioTemporal",
179
+ "DownBlockSpatioTemporal",
180
+ ),
181
+ up_block_types: Tuple[str] = (
182
+ "UpBlockSpatioTemporal",
183
+ "CrossAttnUpBlockSpatioTemporal",
184
+ "CrossAttnUpBlockSpatioTemporal",
185
+ "CrossAttnUpBlockSpatioTemporal",
186
+ ),
187
+ block_out_channels: Tuple[int] = (320, 640, 1280, 1280),
188
+ addition_time_embed_dim: int = 256,
189
+ projection_class_embeddings_input_dim: int = 768,
190
+ layers_per_block: Union[int, Tuple[int]] = 2,
191
+ cross_attention_dim: Union[int, Tuple[int]] = 1024,
192
+ transformer_layers_per_block: Union[int, Tuple[int], Tuple[Tuple]] = 1,
193
+ num_attention_heads: Union[int, Tuple[int]] = (5, 10, 20, 20),
194
+ num_frames: int = 25,
195
+ ):
196
+ super().__init__()
197
+
198
+ self.sample_size = sample_size
199
+
200
+ # Check inputs
201
+ if len(down_block_types) != len(up_block_types):
202
+ raise ValueError(
203
+ f"Must provide the same number of `down_block_types` as `up_block_types`. `down_block_types`: {down_block_types}. `up_block_types`: {up_block_types}."
204
+ )
205
+
206
+ if len(block_out_channels) != len(down_block_types):
207
+ raise ValueError(
208
+ f"Must provide the same number of `block_out_channels` as `down_block_types`. `block_out_channels`: {block_out_channels}. `down_block_types`: {down_block_types}."
209
+ )
210
+
211
+ if not isinstance(num_attention_heads, int) and len(num_attention_heads) != len(down_block_types):
212
+ raise ValueError(
213
+ f"Must provide the same number of `num_attention_heads` as `down_block_types`. `num_attention_heads`: {num_attention_heads}. `down_block_types`: {down_block_types}."
214
+ )
215
+
216
+ if isinstance(cross_attention_dim, list) and len(cross_attention_dim) != len(down_block_types):
217
+ raise ValueError(
218
+ f"Must provide the same number of `cross_attention_dim` as `down_block_types`. `cross_attention_dim`: {cross_attention_dim}. `down_block_types`: {down_block_types}."
219
+ )
220
+
221
+ if not isinstance(layers_per_block, int) and len(layers_per_block) != len(down_block_types):
222
+ raise ValueError(
223
+ f"Must provide the same number of `layers_per_block` as `down_block_types`. `layers_per_block`: {layers_per_block}. `down_block_types`: {down_block_types}."
224
+ )
225
+
226
+ # input
227
+ self.conv_in = nn.Conv2d(
228
+ in_channels,
229
+ block_out_channels[0],
230
+ kernel_size=3,
231
+ padding=1,
232
+ )
233
+
234
+ # time
235
+ time_embed_dim = block_out_channels[0] * 4
236
+
237
+ self.time_proj = Timesteps(block_out_channels[0], True, downscale_freq_shift=0)
238
+ timestep_input_dim = block_out_channels[0]
239
+
240
+ self.time_embedding = TimestepEmbedding(timestep_input_dim, time_embed_dim)
241
+
242
+ self.add_time_proj = Timesteps(addition_time_embed_dim, True, downscale_freq_shift=0)
243
+ self.add_embedding = TimestepEmbedding(projection_class_embeddings_input_dim, time_embed_dim)
244
+
245
+ self.down_blocks = nn.ModuleList([])
246
+ self.up_blocks = nn.ModuleList([])
247
+
248
+ if isinstance(num_attention_heads, int):
249
+ num_attention_heads = (num_attention_heads,) * len(down_block_types)
250
+
251
+ if isinstance(cross_attention_dim, int):
252
+ cross_attention_dim = (cross_attention_dim,) * len(down_block_types)
253
+
254
+ if isinstance(layers_per_block, int):
255
+ layers_per_block = [layers_per_block] * len(down_block_types)
256
+
257
+ if isinstance(transformer_layers_per_block, int):
258
+ transformer_layers_per_block = [transformer_layers_per_block] * len(down_block_types)
259
+
260
+ blocks_time_embed_dim = time_embed_dim
261
+
262
+ # down
263
+ output_channel = block_out_channels[0]
264
+ for i, down_block_type in enumerate(down_block_types):
265
+ input_channel = output_channel
266
+ output_channel = block_out_channels[i]
267
+ is_final_block = i == len(block_out_channels) - 1
268
+
269
+ down_block = get_down_block(
270
+ down_block_type,
271
+ num_layers=layers_per_block[i],
272
+ transformer_layers_per_block=transformer_layers_per_block[i],
273
+ in_channels=input_channel,
274
+ out_channels=output_channel,
275
+ temb_channels=blocks_time_embed_dim,
276
+ add_downsample=not is_final_block,
277
+ resnet_eps=1e-5,
278
+ cross_attention_dim=cross_attention_dim[i],
279
+ num_attention_heads=num_attention_heads[i],
280
+ resnet_act_fn="silu",
281
+ )
282
+ self.down_blocks.append(down_block)
283
+
284
+ # mid
285
+ self.mid_block = UNetMidBlockSpatioTemporal(
286
+ block_out_channels[-1],
287
+ temb_channels=blocks_time_embed_dim,
288
+ transformer_layers_per_block=transformer_layers_per_block[-1],
289
+ cross_attention_dim=cross_attention_dim[-1],
290
+ num_attention_heads=num_attention_heads[-1],
291
+ )
292
+
293
+ # count how many layers upsample the images
294
+ self.num_upsamplers = 0
295
+
296
+ # up
297
+ reversed_block_out_channels = list(reversed(block_out_channels))
298
+ reversed_num_attention_heads = list(reversed(num_attention_heads))
299
+ reversed_layers_per_block = list(reversed(layers_per_block))
300
+ reversed_cross_attention_dim = list(reversed(cross_attention_dim))
301
+ reversed_transformer_layers_per_block = list(reversed(transformer_layers_per_block))
302
+
303
+ output_channel = reversed_block_out_channels[0]
304
+ for i, up_block_type in enumerate(up_block_types):
305
+ is_final_block = i == len(block_out_channels) - 1
306
+
307
+ prev_output_channel = output_channel
308
+ output_channel = reversed_block_out_channels[i]
309
+ input_channel = reversed_block_out_channels[min(i + 1, len(block_out_channels) - 1)]
310
+
311
+ # add upsample block for all BUT final layer
312
+ if not is_final_block:
313
+ add_upsample = True
314
+ self.num_upsamplers += 1
315
+ else:
316
+ add_upsample = False
317
+
318
+ up_block = get_up_block(
319
+ up_block_type,
320
+ num_layers=reversed_layers_per_block[i] + 1,
321
+ transformer_layers_per_block=reversed_transformer_layers_per_block[i],
322
+ in_channels=input_channel,
323
+ out_channels=output_channel,
324
+ prev_output_channel=prev_output_channel,
325
+ temb_channels=blocks_time_embed_dim,
326
+ add_upsample=add_upsample,
327
+ resnet_eps=1e-5,
328
+ resolution_idx=i,
329
+ cross_attention_dim=reversed_cross_attention_dim[i],
330
+ num_attention_heads=reversed_num_attention_heads[i],
331
+ resnet_act_fn="silu",
332
+ )
333
+ self.up_blocks.append(up_block)
334
+ prev_output_channel = output_channel
335
+
336
+ # out
337
+ self.conv_norm_out = nn.GroupNorm(num_channels=block_out_channels[0], num_groups=32, eps=1e-5)
338
+ self.conv_act = nn.SiLU()
339
+
340
+ self.conv_out = nn.Conv2d(
341
+ block_out_channels[0],
342
+ out_channels,
343
+ kernel_size=3,
344
+ padding=1,
345
+ )
346
+
347
+ @property
348
+ def attn_processors(self) -> Dict[str, AttentionProcessor]:
349
+ r"""
350
+ Returns:
351
+ `dict` of attention processors: A dictionary containing all attention processors used in the model with
352
+ indexed by its weight name.
353
+ """
354
+ # set recursively
355
+ processors = {}
356
+
357
+ def fn_recursive_add_processors(
358
+ name: str,
359
+ module: torch.nn.Module,
360
+ processors: Dict[str, AttentionProcessor],
361
+ ):
362
+ if hasattr(module, "get_processor"):
363
+ processors[f"{name}.processor"] = module.get_processor()
364
+
365
+ for sub_name, child in module.named_children():
366
+ fn_recursive_add_processors(f"{name}.{sub_name}", child, processors)
367
+
368
+ return processors
369
+
370
+ for name, module in self.named_children():
371
+ fn_recursive_add_processors(name, module, processors)
372
+
373
+ return processors
374
+
375
+ def set_attn_processor(self, processor: Union[AttentionProcessor, Dict[str, AttentionProcessor]]):
376
+ r"""
377
+ Sets the attention processor to use to compute attention.
378
+
379
+ Parameters:
380
+ processor (`dict` of `AttentionProcessor` or only `AttentionProcessor`):
381
+ The instantiated processor class or a dictionary of processor classes that will be set as the processor
382
+ for **all** `Attention` layers.
383
+
384
+ If `processor` is a dict, the key needs to define the path to the corresponding cross attention
385
+ processor. This is strongly recommended when setting trainable attention processors.
386
+
387
+ """
388
+ count = len(self.attn_processors.keys())
389
+
390
+ if isinstance(processor, dict) and len(processor) != count:
391
+ raise ValueError(
392
+ f"A dict of processors was passed, but the number of processors {len(processor)} does not match the"
393
+ f" number of attention layers: {count}. Please make sure to pass {count} processor classes."
394
+ )
395
+
396
+ def fn_recursive_attn_processor(name: str, module: torch.nn.Module, processor):
397
+ if hasattr(module, "set_processor"):
398
+ if not isinstance(processor, dict):
399
+ module.set_processor(processor)
400
+ else:
401
+ module.set_processor(processor.pop(f"{name}.processor"))
402
+
403
+ for sub_name, child in module.named_children():
404
+ fn_recursive_attn_processor(f"{name}.{sub_name}", child, processor)
405
+
406
+ for name, module in self.named_children():
407
+ fn_recursive_attn_processor(name, module, processor)
408
+
409
+ def set_default_attn_processor(self):
410
+ """
411
+ Disables custom attention processors and sets the default attention implementation.
412
+ """
413
+ if all(proc.__class__ in CROSS_ATTENTION_PROCESSORS for proc in self.attn_processors.values()):
414
+ processor = AttnProcessor()
415
+ else:
416
+ raise ValueError(
417
+ f"Cannot call `set_default_attn_processor` when attention processors are of type {next(iter(self.attn_processors.values()))}"
418
+ )
419
+
420
+ self.set_attn_processor(processor)
421
+
422
+ # Copied from diffusers.models.unets.unet_3d_condition.UNet3DConditionModel.enable_forward_chunking
423
+ def enable_forward_chunking(self, chunk_size: Optional[int] = None, dim: int = 0) -> None:
424
+ """
425
+ Sets the attention processor to use [feed forward
426
+ chunking](https://huggingface.co/blog/reformer#2-chunked-feed-forward-layers).
427
+
428
+ Parameters:
429
+ chunk_size (`int`, *optional*):
430
+ The chunk size of the feed-forward layers. If not specified, will run feed-forward layer individually
431
+ over each tensor of dim=`dim`.
432
+ dim (`int`, *optional*, defaults to `0`):
433
+ The dimension over which the feed-forward computation should be chunked. Choose between dim=0 (batch)
434
+ or dim=1 (sequence length).
435
+ """
436
+ if dim not in [0, 1]:
437
+ raise ValueError(f"Make sure to set `dim` to either 0 or 1, not {dim}")
438
+
439
+ # By default chunk size is 1
440
+ chunk_size = chunk_size or 1
441
+
442
+ def fn_recursive_feed_forward(module: torch.nn.Module, chunk_size: int, dim: int):
443
+ if hasattr(module, "set_chunk_feed_forward"):
444
+ module.set_chunk_feed_forward(chunk_size=chunk_size, dim=dim)
445
+
446
+ for child in module.children():
447
+ fn_recursive_feed_forward(child, chunk_size, dim)
448
+
449
+ for module in self.children():
450
+ fn_recursive_feed_forward(module, chunk_size, dim)
451
+
452
+ def forward(
453
+ self,
454
+ sample: torch.Tensor,
455
+ timestep: Union[torch.Tensor, float, int],
456
+ encoder_hidden_states: torch.Tensor,
457
+ added_time_ids: torch.Tensor,
458
+ return_dict: bool = True,
459
+ ) -> Union[UNetSpatioTemporalConditionOutput, Tuple]:
460
+ r"""
461
+ The [`UNetSpatioTemporalConditionModel`] forward method.
462
+
463
+ Args:
464
+ sample (`torch.Tensor`):
465
+ The noisy input tensor with the following shape `(batch, num_frames, channel, height, width)`.
466
+ timestep (`torch.Tensor` or `float` or `int`): The number of timesteps to denoise an input.
467
+ encoder_hidden_states (`torch.Tensor`):
468
+ The encoder hidden states with shape `(batch, sequence_length, cross_attention_dim)`.
469
+ added_time_ids: (`torch.Tensor`):
470
+ The additional time ids with shape `(batch, num_additional_ids)`. These are encoded with sinusoidal
471
+ embeddings and added to the time embeddings.
472
+ return_dict (`bool`, *optional*, defaults to `True`):
473
+ Whether or not to return a [`~models.unet_slatio_temporal.UNetSpatioTemporalConditionOutput`] instead
474
+ of a plain tuple.
475
+ Returns:
476
+ [`~models.unet_slatio_temporal.UNetSpatioTemporalConditionOutput`] or `tuple`:
477
+ If `return_dict` is True, an [`~models.unet_slatio_temporal.UNetSpatioTemporalConditionOutput`] is
478
+ returned, otherwise a `tuple` is returned where the first element is the sample tensor.
479
+ """
480
+ # By default samples have to be AT least a multiple of the overall upsampling factor.
481
+ # The overall upsampling factor is equal to 2 ** (# num of upsampling layears).
482
+ # However, the upsampling interpolation output size can be forced to fit any upsampling size
483
+ # on the fly if necessary.
484
+ default_overall_up_factor = 2**self.num_upsamplers
485
+
486
+ # upsample size should be forwarded when sample is not a multiple of `default_overall_up_factor`
487
+ forward_upsample_size = False
488
+ upsample_size = None
489
+
490
+ if any(s % default_overall_up_factor != 0 for s in sample.shape[-2:]):
491
+ logger.info("Forward upsample size to force interpolation output size.")
492
+ forward_upsample_size = True
493
+ # print('sample shape:', sample.shape)
494
+ # print('encoder_hidden_states shape:', encoder_hidden_states.shape)
495
+
496
+ # 1. time
497
+ timesteps = timestep
498
+ if not torch.is_tensor(timesteps):
499
+ # TODO: this requires sync between CPU and GPU. So try to pass timesteps as tensors if you can
500
+ # This would be a good case for the `match` statement (Python 3.10+)
501
+ is_mps = sample.device.type == "mps"
502
+ is_npu = sample.device.type == "npu"
503
+ if isinstance(timestep, float):
504
+ dtype = torch.float32 if (is_mps or is_npu) else torch.float64
505
+ else:
506
+ dtype = torch.int32 if (is_mps or is_npu) else torch.int64
507
+ timesteps = torch.tensor([timesteps], dtype=dtype, device=sample.device)
508
+ elif len(timesteps.shape) == 0:
509
+ timesteps = timesteps[None].to(sample.device)
510
+
511
+ # broadcast to batch dimension in a way that's compatible with ONNX/Core ML
512
+ batch_size, num_frames = sample.shape[:2]
513
+ timesteps = timesteps.expand(batch_size)
514
+
515
+ t_emb = self.time_proj(timesteps)
516
+
517
+ # `Timesteps` does not contain any weights and will always return f32 tensors
518
+ # but time_embedding might actually be running in fp16. so we need to cast here.
519
+ # there might be better ways to encapsulate this.
520
+ t_emb = t_emb.to(dtype=sample.dtype)
521
+
522
+ emb = self.time_embedding(t_emb)
523
+
524
+ time_embeds = self.add_time_proj(added_time_ids.flatten())
525
+ time_embeds = time_embeds.reshape((batch_size, -1))
526
+ time_embeds = time_embeds.to(emb.dtype)
527
+ aug_emb = self.add_embedding(time_embeds)
528
+ emb = emb + aug_emb
529
+
530
+ # Flatten the batch and frames dimensions
531
+ # sample: [batch, frames, channels, height, width] -> [batch * frames, channels, height, width]
532
+ sample = sample.flatten(0, 1)
533
+ # Repeat the embeddings num_video_frames times
534
+ # emb: [batch, channels] -> [batch * frames, channels]
535
+ emb = emb.repeat_interleave(num_frames, dim=0)
536
+ # encoder_hidden_states: [batch, 1, channels] -> [batch * frames, 1, channels]
537
+
538
+ # ===== added part =====
539
+ # Comment out for multi-frames input
540
+ # encoder_hidden_states = encoder_hidden_states.repeat_interleave(num_frames, dim=0)
541
+
542
+ # print(emb.shape)
543
+ # print(encoder_hidden_states.shape)
544
+ # visualize_tensors(emb, encoder_hidden_states, t=timestep, output_dir='/fs/nexus-scratch/sjxu/DiffusionMaskRelight/latents')
545
+
546
+ # ===== added part =====
547
+
548
+ # 2. pre-process
549
+ sample = self.conv_in(sample)
550
+
551
+ # ===== added part =====
552
+ image_only_indicator = torch.zeros(batch_size, num_frames, dtype=sample.dtype, device=sample.device)
553
+ # image_only_indicator = torch.zeros(batch_size * num_frames, 1, dtype=sample.dtype, device=sample.device)
554
+ # ===== added part =====
555
+
556
+ down_block_res_samples = (sample,)
557
+ for downsample_block in self.down_blocks:
558
+ if hasattr(downsample_block, "has_cross_attention") and downsample_block.has_cross_attention:
559
+ sample, res_samples = downsample_block(
560
+ hidden_states=sample,
561
+ temb=emb,
562
+ encoder_hidden_states=encoder_hidden_states,
563
+ image_only_indicator=image_only_indicator,
564
+ )
565
+ else:
566
+ sample, res_samples = downsample_block(
567
+ hidden_states=sample,
568
+ temb=emb,
569
+ image_only_indicator=image_only_indicator,
570
+ )
571
+
572
+ down_block_res_samples += res_samples
573
+
574
+ # 4. mid
575
+ sample = self.mid_block(
576
+ hidden_states=sample,
577
+ temb=emb,
578
+ encoder_hidden_states=encoder_hidden_states,
579
+ image_only_indicator=image_only_indicator,
580
+ )
581
+
582
+ # 5. up
583
+ for i, upsample_block in enumerate(self.up_blocks):
584
+ is_final_block = i == len(self.up_blocks) - 1
585
+
586
+ res_samples = down_block_res_samples[-len(upsample_block.resnets) :]
587
+ down_block_res_samples = down_block_res_samples[: -len(upsample_block.resnets)]
588
+
589
+ # if we have not reached the final block and need to forward the
590
+ # upsample size, we do it here
591
+ if not is_final_block and forward_upsample_size:
592
+ upsample_size = down_block_res_samples[-1].shape[2:]
593
+
594
+ if hasattr(upsample_block, "has_cross_attention") and upsample_block.has_cross_attention:
595
+ sample = upsample_block(
596
+ hidden_states=sample,
597
+ temb=emb,
598
+ res_hidden_states_tuple=res_samples,
599
+ encoder_hidden_states=encoder_hidden_states,
600
+ upsample_size=upsample_size,
601
+ image_only_indicator=image_only_indicator,
602
+ )
603
+ else:
604
+ sample = upsample_block(
605
+ hidden_states=sample,
606
+ temb=emb,
607
+ res_hidden_states_tuple=res_samples,
608
+ upsample_size=upsample_size,
609
+ image_only_indicator=image_only_indicator,
610
+ )
611
+
612
+ # 6. post-process
613
+ sample = self.conv_norm_out(sample)
614
+ sample = self.conv_act(sample)
615
+ sample = self.conv_out(sample)
616
+
617
+ # 7. Reshape back to original shape
618
+ sample = sample.reshape(batch_size, num_frames, *sample.shape[1:])
619
+
620
+ if not return_dict:
621
+ return (sample,)
622
+
623
+ return UNetSpatioTemporalConditionOutput(sample=sample)