mirror of
https://github.com/storytold/FineTrainers-Conditioning.git
synced 2026-10-18 00:33:52 +00:00
add VideoDatasetWithResizeAndRectangleCrop dataset resize crop (#13)
* fix issue 12: train dataset _preprocess_data crop video * add_argument video_reshape_mode input videos are reshaped to this mode * arg enable_model_cpu_offloading * Add imageio-ffmpeg and imageio to requirements.txt * apply changes to i2v lora script * make style --------- Co-authored-by: --unset <--unset> Co-authored-by: Aryan <aryan@huggingface.co>
This commit is contained in:
@@ -11,3 +11,7 @@ pandas
|
|||||||
torch
|
torch
|
||||||
torchvision
|
torchvision
|
||||||
torchao
|
torchao
|
||||||
|
sentencepiece
|
||||||
|
imageio-ffmpeg
|
||||||
|
imageio
|
||||||
|
numpy==2.1.1
|
||||||
@@ -192,6 +192,12 @@ def _get_training_args(parser: argparse.ArgumentParser) -> None:
|
|||||||
default=720,
|
default=720,
|
||||||
help="All input videos are resized to this width.",
|
help="All input videos are resized to this width.",
|
||||||
)
|
)
|
||||||
|
parser.add_argument(
|
||||||
|
"--video_reshape_mode",
|
||||||
|
type=str,
|
||||||
|
default=None,
|
||||||
|
help="All input videos are reshaped to this mode. Choose between ['center', 'random', 'none']",
|
||||||
|
)
|
||||||
parser.add_argument("--fps", type=int, default=8, help="All input videos will be used at this FPS.")
|
parser.add_argument("--fps", type=int, default=8, help="All input videos will be used at this FPS.")
|
||||||
parser.add_argument(
|
parser.add_argument(
|
||||||
"--max_num_frames",
|
"--max_num_frames",
|
||||||
|
|||||||
@@ -55,7 +55,7 @@ from transformers import AutoTokenizer, T5EncoderModel
|
|||||||
|
|
||||||
|
|
||||||
from args import get_args # isort:skip
|
from args import get_args # isort:skip
|
||||||
from dataset import BucketSampler, VideoDatasetWithResizing # isort:skip
|
from dataset import BucketSampler, VideoDatasetWithResizing, VideoDatasetWithResizeAndRectangleCrop # isort:skip
|
||||||
from text_encoder import compute_prompt_embeddings # isort:skip
|
from text_encoder import compute_prompt_embeddings # isort:skip
|
||||||
from utils import get_gradient_norm, get_optimizer, prepare_rotary_positional_embeddings, print_memory, reset_memory # isort:skip
|
from utils import get_gradient_norm, get_optimizer, prepare_rotary_positional_embeddings, print_memory, reset_memory # isort:skip
|
||||||
|
|
||||||
@@ -465,20 +465,37 @@ def main(args):
|
|||||||
)
|
)
|
||||||
|
|
||||||
# Dataset and DataLoader
|
# Dataset and DataLoader
|
||||||
train_dataset = VideoDatasetWithResizing(
|
if not args.video_reshape_mode:
|
||||||
data_root=args.data_root,
|
train_dataset = VideoDatasetWithResizing(
|
||||||
dataset_file=args.dataset_file,
|
data_root=args.data_root,
|
||||||
caption_column=args.caption_column,
|
dataset_file=args.dataset_file,
|
||||||
video_column=args.video_column,
|
caption_column=args.caption_column,
|
||||||
max_num_frames=args.max_num_frames,
|
video_column=args.video_column,
|
||||||
id_token=args.id_token,
|
max_num_frames=args.max_num_frames,
|
||||||
height_buckets=args.height_buckets,
|
id_token=args.id_token,
|
||||||
width_buckets=args.width_buckets,
|
height_buckets=args.height_buckets,
|
||||||
frame_buckets=args.frame_buckets,
|
width_buckets=args.width_buckets,
|
||||||
load_tensors=args.load_tensors,
|
frame_buckets=args.frame_buckets,
|
||||||
random_flip=args.random_flip,
|
load_tensors=args.load_tensors,
|
||||||
image_to_video=True,
|
random_flip=args.random_flip,
|
||||||
)
|
image_to_video=True,
|
||||||
|
)
|
||||||
|
else:
|
||||||
|
train_dataset = VideoDatasetWithResizeAndRectangleCrop(
|
||||||
|
video_reshape_mode=args.video_reshape_mode,
|
||||||
|
data_root=args.data_root,
|
||||||
|
dataset_file=args.dataset_file,
|
||||||
|
caption_column=args.caption_column,
|
||||||
|
video_column=args.video_column,
|
||||||
|
max_num_frames=args.max_num_frames,
|
||||||
|
id_token=args.id_token,
|
||||||
|
height_buckets=args.height_buckets,
|
||||||
|
width_buckets=args.width_buckets,
|
||||||
|
frame_buckets=args.frame_buckets,
|
||||||
|
load_tensors=args.load_tensors,
|
||||||
|
random_flip=args.random_flip,
|
||||||
|
image_to_video=True,
|
||||||
|
)
|
||||||
|
|
||||||
def collate_fn(data):
|
def collate_fn(data):
|
||||||
prompts = [x["prompt"] for x in data[0]]
|
prompts = [x["prompt"] for x in data[0]]
|
||||||
|
|||||||
@@ -54,7 +54,7 @@ from transformers import AutoTokenizer, T5EncoderModel
|
|||||||
|
|
||||||
|
|
||||||
from args import get_args # isort:skip
|
from args import get_args # isort:skip
|
||||||
from dataset import BucketSampler, VideoDatasetWithResizing # isort:skip
|
from dataset import BucketSampler, VideoDatasetWithResizing, VideoDatasetWithResizeAndRectangleCrop # isort:skip
|
||||||
from text_encoder import compute_prompt_embeddings # isort:skip
|
from text_encoder import compute_prompt_embeddings # isort:skip
|
||||||
from utils import get_gradient_norm, get_optimizer, prepare_rotary_positional_embeddings, print_memory, reset_memory # isort:skip
|
from utils import get_gradient_norm, get_optimizer, prepare_rotary_positional_embeddings, print_memory, reset_memory # isort:skip
|
||||||
|
|
||||||
@@ -462,19 +462,35 @@ def main(args):
|
|||||||
)
|
)
|
||||||
|
|
||||||
# Dataset and DataLoader
|
# Dataset and DataLoader
|
||||||
train_dataset = VideoDatasetWithResizing(
|
if not args.video_reshape_mode:
|
||||||
data_root=args.data_root,
|
train_dataset = VideoDatasetWithResizing(
|
||||||
dataset_file=args.dataset_file,
|
data_root=args.data_root,
|
||||||
caption_column=args.caption_column,
|
dataset_file=args.dataset_file,
|
||||||
video_column=args.video_column,
|
caption_column=args.caption_column,
|
||||||
max_num_frames=args.max_num_frames,
|
video_column=args.video_column,
|
||||||
id_token=args.id_token,
|
max_num_frames=args.max_num_frames,
|
||||||
height_buckets=args.height_buckets,
|
id_token=args.id_token,
|
||||||
width_buckets=args.width_buckets,
|
height_buckets=args.height_buckets,
|
||||||
frame_buckets=args.frame_buckets,
|
width_buckets=args.width_buckets,
|
||||||
load_tensors=args.load_tensors,
|
frame_buckets=args.frame_buckets,
|
||||||
random_flip=args.random_flip,
|
load_tensors=args.load_tensors,
|
||||||
)
|
random_flip=args.random_flip,
|
||||||
|
)
|
||||||
|
else:
|
||||||
|
train_dataset = VideoDatasetWithResizeAndRectangleCrop(
|
||||||
|
video_reshape_mode=args.video_reshape_mode,
|
||||||
|
data_root=args.data_root,
|
||||||
|
dataset_file=args.dataset_file,
|
||||||
|
caption_column=args.caption_column,
|
||||||
|
video_column=args.video_column,
|
||||||
|
max_num_frames=args.max_num_frames,
|
||||||
|
id_token=args.id_token,
|
||||||
|
height_buckets=args.height_buckets,
|
||||||
|
width_buckets=args.width_buckets,
|
||||||
|
frame_buckets=args.frame_buckets,
|
||||||
|
load_tensors=args.load_tensors,
|
||||||
|
random_flip=args.random_flip,
|
||||||
|
)
|
||||||
|
|
||||||
def collate_fn(data):
|
def collate_fn(data):
|
||||||
prompts = [x["prompt"] for x in data[0]]
|
prompts = [x["prompt"] for x in data[0]]
|
||||||
|
|||||||
@@ -53,7 +53,7 @@ from transformers import AutoTokenizer, T5EncoderModel
|
|||||||
|
|
||||||
|
|
||||||
from args import get_args # isort:skip
|
from args import get_args # isort:skip
|
||||||
from dataset import BucketSampler, VideoDatasetWithResizing # isort:skip
|
from dataset import BucketSampler, VideoDatasetWithResizing, VideoDatasetWithResizeAndRectangleCrop # isort:skip
|
||||||
from text_encoder import compute_prompt_embeddings # isort:skip
|
from text_encoder import compute_prompt_embeddings # isort:skip
|
||||||
from utils import get_gradient_norm, get_optimizer, prepare_rotary_positional_embeddings, print_memory, reset_memory # isort:skip
|
from utils import get_gradient_norm, get_optimizer, prepare_rotary_positional_embeddings, print_memory, reset_memory # isort:skip
|
||||||
|
|
||||||
@@ -426,19 +426,35 @@ def main(args):
|
|||||||
)
|
)
|
||||||
|
|
||||||
# Dataset and DataLoader
|
# Dataset and DataLoader
|
||||||
train_dataset = VideoDatasetWithResizing(
|
if not args.video_reshape_mode:
|
||||||
data_root=args.data_root,
|
train_dataset = VideoDatasetWithResizing(
|
||||||
dataset_file=args.dataset_file,
|
data_root=args.data_root,
|
||||||
caption_column=args.caption_column,
|
dataset_file=args.dataset_file,
|
||||||
video_column=args.video_column,
|
caption_column=args.caption_column,
|
||||||
max_num_frames=args.max_num_frames,
|
video_column=args.video_column,
|
||||||
id_token=args.id_token,
|
max_num_frames=args.max_num_frames,
|
||||||
height_buckets=args.height_buckets,
|
id_token=args.id_token,
|
||||||
width_buckets=args.width_buckets,
|
height_buckets=args.height_buckets,
|
||||||
frame_buckets=args.frame_buckets,
|
width_buckets=args.width_buckets,
|
||||||
load_tensors=args.load_tensors,
|
frame_buckets=args.frame_buckets,
|
||||||
random_flip=args.random_flip,
|
load_tensors=args.load_tensors,
|
||||||
)
|
random_flip=args.random_flip,
|
||||||
|
)
|
||||||
|
else:
|
||||||
|
train_dataset = VideoDatasetWithResizeAndRectangleCrop(
|
||||||
|
video_reshape_mode=args.video_reshape_mode,
|
||||||
|
data_root=args.data_root,
|
||||||
|
dataset_file=args.dataset_file,
|
||||||
|
caption_column=args.caption_column,
|
||||||
|
video_column=args.video_column,
|
||||||
|
max_num_frames=args.max_num_frames,
|
||||||
|
id_token=args.id_token,
|
||||||
|
height_buckets=args.height_buckets,
|
||||||
|
width_buckets=args.width_buckets,
|
||||||
|
frame_buckets=args.frame_buckets,
|
||||||
|
load_tensors=args.load_tensors,
|
||||||
|
random_flip=args.random_flip,
|
||||||
|
)
|
||||||
|
|
||||||
def collate_fn(data):
|
def collate_fn(data):
|
||||||
prompts = [x["prompt"] for x in data[0]]
|
prompts = [x["prompt"] for x in data[0]]
|
||||||
|
|||||||
@@ -2,11 +2,14 @@ import random
|
|||||||
from pathlib import Path
|
from pathlib import Path
|
||||||
from typing import Any, Dict, List, Optional, Tuple
|
from typing import Any, Dict, List, Optional, Tuple
|
||||||
|
|
||||||
|
import numpy as np
|
||||||
import pandas as pd
|
import pandas as pd
|
||||||
import torch
|
import torch
|
||||||
|
import torchvision.transforms as TT
|
||||||
from accelerate.logging import get_logger
|
from accelerate.logging import get_logger
|
||||||
from torch.utils.data import Dataset, Sampler
|
from torch.utils.data import Dataset, Sampler
|
||||||
from torchvision import transforms
|
from torchvision import transforms
|
||||||
|
from torchvision.transforms import InterpolationMode
|
||||||
from torchvision.transforms.functional import resize
|
from torchvision.transforms.functional import resize
|
||||||
|
|
||||||
|
|
||||||
@@ -281,6 +284,71 @@ class VideoDatasetWithResizing(VideoDataset):
|
|||||||
return nearest_res[1], nearest_res[2]
|
return nearest_res[1], nearest_res[2]
|
||||||
|
|
||||||
|
|
||||||
|
class VideoDatasetWithResizeAndRectangleCrop(VideoDataset):
|
||||||
|
def __init__(self, video_reshape_mode: str = "center", *args, **kwargs) -> None:
|
||||||
|
super().__init__(*args, **kwargs)
|
||||||
|
self.video_reshape_mode = video_reshape_mode
|
||||||
|
|
||||||
|
def _resize_for_rectangle_crop(self, arr, image_size):
|
||||||
|
reshape_mode = self.video_reshape_mode
|
||||||
|
if arr.shape[3] / arr.shape[2] > image_size[1] / image_size[0]:
|
||||||
|
arr = resize(
|
||||||
|
arr,
|
||||||
|
size=[image_size[0], int(arr.shape[3] * image_size[0] / arr.shape[2])],
|
||||||
|
interpolation=InterpolationMode.BICUBIC,
|
||||||
|
)
|
||||||
|
else:
|
||||||
|
arr = resize(
|
||||||
|
arr,
|
||||||
|
size=[int(arr.shape[2] * image_size[1] / arr.shape[3]), image_size[1]],
|
||||||
|
interpolation=InterpolationMode.BICUBIC,
|
||||||
|
)
|
||||||
|
|
||||||
|
h, w = arr.shape[2], arr.shape[3]
|
||||||
|
arr = arr.squeeze(0)
|
||||||
|
|
||||||
|
delta_h = h - image_size[0]
|
||||||
|
delta_w = w - image_size[1]
|
||||||
|
|
||||||
|
if reshape_mode == "random" or reshape_mode == "none":
|
||||||
|
top = np.random.randint(0, delta_h + 1)
|
||||||
|
left = np.random.randint(0, delta_w + 1)
|
||||||
|
elif reshape_mode == "center":
|
||||||
|
top, left = delta_h // 2, delta_w // 2
|
||||||
|
else:
|
||||||
|
raise NotImplementedError
|
||||||
|
arr = TT.functional.crop(arr, top=top, left=left, height=image_size[0], width=image_size[1])
|
||||||
|
return arr
|
||||||
|
|
||||||
|
def _preprocess_video(self, path: Path) -> torch.Tensor:
|
||||||
|
if self.load_tensors:
|
||||||
|
return self._load_preprocessed_latents_and_embeds(path)
|
||||||
|
else:
|
||||||
|
video_reader = decord.VideoReader(uri=path.as_posix())
|
||||||
|
video_num_frames = len(video_reader)
|
||||||
|
nearest_frame_bucket = min(
|
||||||
|
self.frame_buckets, key=lambda x: abs(x - min(video_num_frames, self.max_num_frames))
|
||||||
|
)
|
||||||
|
|
||||||
|
frame_indices = list(range(0, video_num_frames, video_num_frames // nearest_frame_bucket))
|
||||||
|
|
||||||
|
frames = video_reader.get_batch(frame_indices)
|
||||||
|
frames = frames[:nearest_frame_bucket].float()
|
||||||
|
frames = frames.permute(0, 3, 1, 2).contiguous()
|
||||||
|
|
||||||
|
nearest_res = self._find_nearest_resolution(frames.shape[2], frames.shape[3])
|
||||||
|
frames_resized = self._resize_for_rectangle_crop(frames, nearest_res)
|
||||||
|
frames = torch.stack([self.video_transforms(frame) for frame in frames_resized], dim=0)
|
||||||
|
|
||||||
|
image = frames[:1].clone() if self.image_to_video else None
|
||||||
|
|
||||||
|
return image, frames, None
|
||||||
|
|
||||||
|
def _find_nearest_resolution(self, height, width):
|
||||||
|
nearest_res = min(self.resolutions, key=lambda x: abs(x[1] - height) + abs(x[2] - width))
|
||||||
|
return nearest_res[1], nearest_res[2]
|
||||||
|
|
||||||
|
|
||||||
class BucketSampler(Sampler):
|
class BucketSampler(Sampler):
|
||||||
def __init__(self, data_source: VideoDataset, batch_size: int = 8, shuffle: bool = True) -> None:
|
def __init__(self, data_source: VideoDataset, batch_size: int = 8, shuffle: bool = True) -> None:
|
||||||
self.data_source = data_source
|
self.data_source = data_source
|
||||||
|
|||||||
@@ -7,12 +7,16 @@ import pathlib
|
|||||||
import traceback
|
import traceback
|
||||||
from typing import Any, Dict, List, Optional, Tuple, Union
|
from typing import Any, Dict, List, Optional, Tuple, Union
|
||||||
|
|
||||||
|
import numpy as np
|
||||||
import pandas as pd
|
import pandas as pd
|
||||||
import torch
|
import torch
|
||||||
import torch.distributed as dist
|
import torch.distributed as dist
|
||||||
|
import torchvision.transforms as TT
|
||||||
from diffusers import AutoencoderKLCogVideoX
|
from diffusers import AutoencoderKLCogVideoX
|
||||||
from diffusers.utils import export_to_video, get_logger
|
from diffusers.utils import export_to_video, get_logger
|
||||||
from torchvision import transforms
|
from torchvision import transforms
|
||||||
|
from torchvision.transforms import InterpolationMode
|
||||||
|
from torchvision.transforms.functional import resize
|
||||||
from tqdm import tqdm
|
from tqdm import tqdm
|
||||||
from transformers import T5EncoderModel, T5Tokenizer
|
from transformers import T5EncoderModel, T5Tokenizer
|
||||||
|
|
||||||
@@ -153,13 +157,51 @@ def load_dataset_from_csv(
|
|||||||
return prompts, video_paths
|
return prompts, video_paths
|
||||||
|
|
||||||
|
|
||||||
|
def resize_for_rectangle_crop(arr, height, width, reshape_mode):
|
||||||
|
image_size = height, width
|
||||||
|
if arr.shape[3] / arr.shape[2] > image_size[1] / image_size[0]:
|
||||||
|
arr = resize(
|
||||||
|
arr,
|
||||||
|
size=[image_size[0], int(arr.shape[3] * image_size[0] / arr.shape[2])],
|
||||||
|
interpolation=InterpolationMode.BICUBIC,
|
||||||
|
)
|
||||||
|
else:
|
||||||
|
arr = resize(
|
||||||
|
arr,
|
||||||
|
size=[int(arr.shape[2] * image_size[1] / arr.shape[3]), image_size[1]],
|
||||||
|
interpolation=InterpolationMode.BICUBIC,
|
||||||
|
)
|
||||||
|
|
||||||
|
h, w = arr.shape[2], arr.shape[3]
|
||||||
|
arr = arr.squeeze(0)
|
||||||
|
|
||||||
|
delta_h = h - image_size[0]
|
||||||
|
delta_w = w - image_size[1]
|
||||||
|
|
||||||
|
if reshape_mode == "random" or reshape_mode == "none":
|
||||||
|
top = np.random.randint(0, delta_h + 1)
|
||||||
|
left = np.random.randint(0, delta_w + 1)
|
||||||
|
elif reshape_mode == "center":
|
||||||
|
top, left = delta_h // 2, delta_w // 2
|
||||||
|
else:
|
||||||
|
raise NotImplementedError
|
||||||
|
arr = TT.functional.crop(arr, top=top, left=left, height=image_size[0], width=image_size[1])
|
||||||
|
return arr
|
||||||
|
|
||||||
|
|
||||||
def load_and_preprocess_video(
|
def load_and_preprocess_video(
|
||||||
path: pathlib.Path, height: int, width: int, max_num_frames: int, video_transforms, num_threads: int = 0
|
path: pathlib.Path,
|
||||||
|
height: int,
|
||||||
|
width: int,
|
||||||
|
max_num_frames: int,
|
||||||
|
video_transforms,
|
||||||
|
num_threads: int = 0,
|
||||||
|
video_reshape_mode: str = "center",
|
||||||
) -> Optional[torch.Tensor]:
|
) -> Optional[torch.Tensor]:
|
||||||
frames = None
|
frames = None
|
||||||
|
|
||||||
try:
|
try:
|
||||||
video_reader = decord.VideoReader(uri=path.as_posix(), height=height, width=width, num_threads=num_threads)
|
video_reader = decord.VideoReader(uri=path.as_posix(), num_threads=num_threads)
|
||||||
video_num_frames = len(video_reader)
|
video_num_frames = len(video_reader)
|
||||||
|
|
||||||
if video_num_frames < max_num_frames:
|
if video_num_frames < max_num_frames:
|
||||||
@@ -172,6 +214,7 @@ def load_and_preprocess_video(
|
|||||||
frames: torch.Tensor = video_reader.get_batch(indices)
|
frames: torch.Tensor = video_reader.get_batch(indices)
|
||||||
frames = frames[:max_num_frames].float()
|
frames = frames[:max_num_frames].float()
|
||||||
frames = frames.permute(0, 3, 1, 2).contiguous()
|
frames = frames.permute(0, 3, 1, 2).contiguous()
|
||||||
|
frames = resize_for_rectangle_crop(frames, height, width, video_reshape_mode)
|
||||||
frames = torch.stack([video_transforms(frame) for frame in frames], dim=0)
|
frames = torch.stack([video_transforms(frame) for frame in frames], dim=0)
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
logger.error(f"Error: {e}. Skipping video located at `{path.as_posix()}`")
|
logger.error(f"Error: {e}. Skipping video located at `{path.as_posix()}`")
|
||||||
|
|||||||
Reference in New Issue
Block a user