From 3f8afcac7ef87807a60e3545fcdc46d4c75bf8e1 Mon Sep 17 00:00:00 2001 From: Jaret Burkett Date: Wed, 29 Jul 2026 17:50:45 -0600 Subject: [PATCH] Allow dataloader to encode first frame with the text embeddings if the model needs it. --- toolkit/data_loader.py | 1 + toolkit/data_transfer_object/data_loader.py | 3 +++ toolkit/dataloader_mixins.py | 28 +++++++++++++++++++++ 3 files changed, 32 insertions(+) diff --git a/toolkit/data_loader.py b/toolkit/data_loader.py index ee90e26..884a6bd 100644 --- a/toolkit/data_loader.py +++ b/toolkit/data_loader.py @@ -533,6 +533,7 @@ class AiToolkitDataset(LatentCachingMixin, ControlCachingMixin, CLIPCachingMixin size_database=self.size_database, dataset_root=dataset_folder, encode_control_in_text_embeddings=self.sd.encode_control_in_text_embeddings if self.sd else False, + encode_first_frame_in_text_embeddings=getattr(self.sd, 'encode_first_frame_in_text_embeddings', False) if self.sd else False, text_embedding_space_version=self.sd.text_embedding_space_version if self.sd else "sd1", te_padding_side=self.sd.te_padding_side if self.sd else "right", latent_space_version=latent_space_version, diff --git a/toolkit/data_transfer_object/data_loader.py b/toolkit/data_transfer_object/data_loader.py index d07a6d6..103b173 100644 --- a/toolkit/data_transfer_object/data_loader.py +++ b/toolkit/data_transfer_object/data_loader.py @@ -65,6 +65,9 @@ class FileItemDTO( self.encode_control_in_text_embeddings = kwargs.get( "encode_control_in_text_embeddings", False ) + self.encode_first_frame_in_text_embeddings = kwargs.get( + "encode_first_frame_in_text_embeddings", False + ) self.te_padding_side = kwargs.get("te_padding_side", "right") self.latent_space_version = kwargs.get("latent_space_version", "sd1") self.text_embedding_space_version = kwargs.get("text_embedding_space_version", "sd1") diff --git a/toolkit/dataloader_mixins.py b/toolkit/dataloader_mixins.py index 0c5ac31..afd9bc2 100644 --- a/toolkit/dataloader_mixins.py +++ b/toolkit/dataloader_mixins.py @@ -1899,6 +1899,13 @@ class TextEmbeddingFileItemDTOMixin: # if we have a control image, cache the path if self.encode_control_in_text_embeddings and self.control_path is not None: item["control_path"] = self.control_path + # first-frame vision conditioning changes the embedding content -> new cache key + elif ( + getattr(self, "encode_first_frame_in_text_embeddings", False) + and self.dataset_config.do_i2v + and (self.dataset_config.auto_frame_count or self.dataset_config.num_frames > 1) + ): + item["first_frame_in_te"] = True return item def get_text_embedding_path(self: 'FileItemDTO', recalculate=False): @@ -1984,6 +1991,27 @@ class TextEmbeddingCachingMixin: else: ctrl_img = ctrl_img_list prompt_embeds: PromptEmbeds = self.sd.encode_prompt(file_item.caption, control_images=ctrl_img) + elif ( + getattr(self.sd, 'encode_first_frame_in_text_embeddings', False) + and self.dataset_config.do_i2v + and (self.dataset_config.auto_frame_count or self.dataset_config.num_frames > 1) + ): + # video item: encode the clip's FIRST FRAME into the text embeddings + # as a vision reference, matching sampling (where the ctrl image goes + # into the embeds and is held as the clean first frames) + file_item.load_and_process_image(self.transform, only_load_latents=True) + frames = file_item.tensor # (T, C, H, W) or (C, H, W), in [-1, 1] + first = frames[0] if frames.dim() == 4 else frames + ctrl_img = ( + ((first + 1.0) / 2.0) + .clamp(0, 1) + .unsqueeze(0) + .to(self.sd.device_torch, dtype=self.sd.torch_dtype) + ) + if self.sd.has_multiple_control_images: + ctrl_img = [ctrl_img] + prompt_embeds: PromptEmbeds = self.sd.encode_prompt(file_item.caption, control_images=ctrl_img) + file_item.tensor = None else: prompt_embeds: PromptEmbeds = self.sd.encode_prompt(file_item.caption) # save it