Download third_party/harmonizer/src/utils/training_utils.py from kimjunhan/vkgs-sim: direct link, hf CLI and curl.
- Browser
- Download file 29.8 kB
-
https://huggingface.co/kimjunhan/vkgs-sim/resolve/main/third_party/harmonizer/src/utils/training_utils.py
- Command line
-
hf download hf://kimjunhan/vkgs-sim/third_party/harmonizer/src/utils/training_utils.py
-
curl -L -o training_utils.py https://huggingface.co/kimjunhan/vkgs-sim/resolve/main/third_party/harmonizer/src/utils/training_utils.py
29.8 kB
| # SPDX-FileCopyrightText: Copyright (c) 2026 NVIDIA CORPORATION & AFFILIATES. All rights reserved. | |
| # SPDX-License-Identifier: Apache-2.0 | |
| # | |
| # Licensed under the Apache License, Version 2.0 (the "License"); | |
| # you may not use this file except in compliance with the License. | |
| # You may obtain a copy of the License at | |
| # | |
| # http://www.apache.org/licenses/LICENSE-2.0 | |
| # | |
| # Unless required by applicable law or agreed to in writing, software | |
| # distributed under the License is distributed on an "AS IS" BASIS, | |
| # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | |
| # See the License for the specific language governing permissions and | |
| # limitations under the License. | |
| import argparse | |
| import json | |
| import os | |
| import torch | |
| import torchvision.transforms.functional as F | |
| from PIL import Image | |
| from torchvision import transforms | |
| from tqdm import tqdm | |
| def parse_args_paired_training(input_args=None): | |
| """ | |
| Parses command-line arguments used for configuring an paired session (pix2pix-Turbo). | |
| This function sets up an argument parser to handle various training options. | |
| Returns: | |
| argparse.Namespace: The parsed command-line arguments. | |
| """ | |
| parser = argparse.ArgumentParser() | |
| # args for the loss function | |
| parser.add_argument("--gan_disc_type", default="vagan_clip") | |
| parser.add_argument("--gan_loss_type", default="multilevel_sigmoid_s") | |
| parser.add_argument("--lambda_gan", default=0.5, type=float) | |
| parser.add_argument("--lambda_lpips", default=5, type=float) | |
| parser.add_argument("--lambda_reg", default=10, type=float) | |
| parser.add_argument("--lambda_l2", default=1.0, type=float) | |
| parser.add_argument("--lambda_clipsim", default=5.0, type=float) | |
| parser.add_argument("--lambda_gram", default=1.0, type=float) | |
| parser.add_argument("--lambda_tv", default=1.0, type=float) | |
| parser.add_argument("--lambda_opticalflow", default=0.0, type=float) | |
| parser.add_argument("--N_resize", default=2.0, type=float) | |
| parser.add_argument("--gram_loss_warmup_steps", default=2000, type=int) | |
| # dataset options | |
| parser.add_argument("--dataset_folder", required=True, type=str) | |
| parser.add_argument("--train_image_prep", default="resized_crop_512", type=str) | |
| parser.add_argument("--test_image_prep", default="resized_crop_512", type=str) | |
| parser.add_argument("--prompt", default=None, type=str) | |
| # validation eval args | |
| parser.add_argument("--eval_freq", default=100, type=int) | |
| parser.add_argument("--track_val_fid", default=False, action="store_true") | |
| parser.add_argument("--num_samples_eval", type=int, default=100, help="Number of samples to use for all evaluation") | |
| parser.add_argument("--viz_freq", type=int, default=100, help="Frequency of visualizing the outputs.") | |
| parser.add_argument( | |
| "--tracker_project_name", | |
| type=str, | |
| default="train_pix2pix_turbo", | |
| help="The name of the wandb project to log to.", | |
| ) | |
| parser.add_argument("--tracker_run_name", type=str, required=True) | |
| # details about the model architecture | |
| parser.add_argument("--pretrained_model_name_or_path") | |
| parser.add_argument( | |
| "--revision", | |
| type=str, | |
| default=None, | |
| ) | |
| parser.add_argument( | |
| "--variant", | |
| type=str, | |
| default=None, | |
| ) | |
| parser.add_argument("--tokenizer_name", type=str, default=None) | |
| parser.add_argument("--lora_rank_unet", default=8, type=int) | |
| parser.add_argument("--lora_rank_vae", default=4, type=int) | |
| parser.add_argument("--freeze_vae_encoder", action="store_true") | |
| parser.add_argument("--freeze_vae", action="store_true") | |
| parser.add_argument("--add_noise", action="store_true") | |
| parser.add_argument( | |
| "--pretrained_path", | |
| type=str, | |
| default=None, | |
| ) | |
| parser.add_argument("--train_full_unet", action="store_true") | |
| parser.add_argument("--unet_in_channels", default=4, type=int) | |
| parser.add_argument("--timestep", default=999, type=int) | |
| # training details | |
| parser.add_argument("--output_dir", required=True) | |
| parser.add_argument( | |
| "--cache_dir", | |
| default=None, | |
| ) | |
| parser.add_argument("--seed", type=int, default=None, help="A seed for reproducible training.") | |
| parser.add_argument( | |
| "--resolution", | |
| type=int, | |
| default=512, | |
| ) | |
| parser.add_argument( | |
| "--train_batch_size", type=int, default=4, help="Batch size (per device) for the training dataloader." | |
| ) | |
| parser.add_argument("--num_training_epochs", type=int, default=10) | |
| parser.add_argument( | |
| "--max_train_steps", | |
| type=int, | |
| default=10_000, | |
| ) | |
| parser.add_argument( | |
| "--checkpointing_steps", | |
| type=int, | |
| default=500, | |
| ) | |
| parser.add_argument( | |
| "--gradient_accumulation_steps", | |
| type=int, | |
| default=1, | |
| help="Number of updates steps to accumulate before performing a backward/update pass.", | |
| ) | |
| parser.add_argument( | |
| "--gradient_checkpointing", | |
| action="store_true", | |
| ) | |
| parser.add_argument("--learning_rate", type=float, default=5e-6) | |
| parser.add_argument( | |
| "--lr_scheduler", | |
| type=str, | |
| default="constant", | |
| help=( | |
| 'The scheduler type to use. Choose between ["linear", "cosine", "cosine_with_restarts", "polynomial",' | |
| ' "constant", "constant_with_warmup"]' | |
| ), | |
| ) | |
| parser.add_argument( | |
| "--lr_warmup_steps", type=int, default=500, help="Number of steps for the warmup in the lr scheduler." | |
| ) | |
| parser.add_argument( | |
| "--lr_num_cycles", | |
| type=int, | |
| default=1, | |
| help="Number of hard resets of the lr in cosine_with_restarts scheduler.", | |
| ) | |
| parser.add_argument("--lr_power", type=float, default=1.0, help="Power factor of the polynomial scheduler.") | |
| parser.add_argument( | |
| "--dataloader_num_workers", | |
| type=int, | |
| default=0, | |
| ) | |
| parser.add_argument("--adam_beta1", type=float, default=0.9, help="The beta1 parameter for the Adam optimizer.") | |
| parser.add_argument("--adam_beta2", type=float, default=0.999, help="The beta2 parameter for the Adam optimizer.") | |
| parser.add_argument("--adam_weight_decay", type=float, default=1e-2, help="Weight decay to use.") | |
| parser.add_argument("--adam_epsilon", type=float, default=1e-08, help="Epsilon value for the Adam optimizer") | |
| parser.add_argument("--max_grad_norm", default=1.0, type=float, help="Max gradient norm.") | |
| parser.add_argument( | |
| "--allow_tf32", | |
| action="store_true", | |
| help=( | |
| "Whether or not to allow TF32 on Ampere GPUs. Can be used to speed up training. For more information, see" | |
| " https://pytorch.org/docs/stable/notes/cuda.html#tensorfloat-32-tf32-on-ampere-devices" | |
| ), | |
| ) | |
| parser.add_argument( | |
| "--report_to", | |
| type=str, | |
| default="wandb", | |
| help=( | |
| 'The integration to report the results and logs to. Supported platforms are `"tensorboard"`' | |
| ' (default), `"wandb"` and `"comet_ml"`. Use `"all"` to report to all integrations.' | |
| ), | |
| ) | |
| parser.add_argument("--hf_path", type=str, default=None) | |
| # cosmos parameters | |
| parser.add_argument( | |
| "--experiment_name", | |
| type=str, | |
| default="official_runs_t2i_fast_205_stage3_0p6b_1024res_synthrealmix_1_1_filtered_with_alpamayo", | |
| ) | |
| parser.add_argument("--s3_checkpoint_dir", type=str, default="None") | |
| parser.add_argument("--av_model_path", type=str, default="") | |
| parser.add_argument("--mixed_precision", type=str, default=None, choices=["no", "fp16", "bf16"]) | |
| parser.add_argument( | |
| "--enable_xformers_memory_efficient_attention", action="store_true", help="Whether or not to use xformers." | |
| ) | |
| parser.add_argument("--set_grads_to_none", action="store_true") | |
| # resume | |
| parser.add_argument("--resume", default=None, type=str) | |
| parser.add_argument("--swinir", default=False, action="store_true") | |
| parser.add_argument("--unetscratch", default=False, action="store_true") | |
| parser.add_argument("--use_sched", action="store_true") | |
| parser.add_argument("--use_large_postnet", action="store_true") | |
| parser.add_argument("--vae_skip_connection", action="store_true") | |
| parser.add_argument("--fix_deconv", action="store_true") | |
| parser.add_argument("--weighted_sampler", action="store_true") | |
| parser.add_argument("--fixing_data_weight", type=float, default=1.0, | |
| help="Multiplier on the weighted-sampler weight for the fixing data source " | |
| "(any --dataset_folder entry containing 'nre_data').") | |
| if input_args is not None: | |
| args = parser.parse_args(input_args) | |
| else: | |
| args = parser.parse_args() | |
| return args | |
| def parse_args_unpaired_training(): | |
| """ | |
| Parses command-line arguments used for configuring an unpaired session (CycleGAN-Turbo). | |
| This function sets up an argument parser to handle various training options. | |
| Returns: | |
| argparse.Namespace: The parsed command-line arguments. | |
| """ | |
| parser = argparse.ArgumentParser(description="Simple example of a ControlNet training script.") | |
| # fixed random seed | |
| parser.add_argument("--seed", type=int, default=42, help="A seed for reproducible training.") | |
| # args for the loss function | |
| parser.add_argument("--gan_disc_type", default="vagan_clip") | |
| parser.add_argument("--gan_loss_type", default="multilevel_sigmoid") | |
| parser.add_argument("--lambda_gan", default=0.5, type=float) | |
| parser.add_argument("--lambda_idt", default=1, type=float) | |
| parser.add_argument("--lambda_cycle", default=1, type=float) | |
| parser.add_argument("--lambda_cycle_lpips", default=10.0, type=float) | |
| parser.add_argument("--lambda_idt_lpips", default=1.0, type=float) | |
| parser.add_argument("--lambda_paired_l2", default=1.0, type=float) | |
| parser.add_argument("--lambda_paired_lpips", default=5.0, type=float) | |
| # args for dataset and dataloader options | |
| parser.add_argument("--dataset_folder", required=True, type=str) | |
| parser.add_argument("--train_img_prep", required=True) | |
| parser.add_argument("--val_img_prep", required=True) | |
| parser.add_argument("--dataloader_num_workers", type=int, default=0) | |
| parser.add_argument( | |
| "--train_batch_size", type=int, default=4, help="Batch size (per device) for the training dataloader." | |
| ) | |
| parser.add_argument("--max_train_epochs", type=int, default=100) | |
| parser.add_argument("--max_train_steps", type=int, default=None) | |
| parser.add_argument("--scene_id", type=str, default=None) | |
| parser.add_argument("--paired_ratio", type=float, default=0.5) | |
| # args for the model | |
| parser.add_argument("--pretrained_model_name_or_path", default="stabilityai/sd-turbo") | |
| parser.add_argument("--revision", default=None, type=str) | |
| parser.add_argument("--variant", default=None, type=str) | |
| parser.add_argument("--lora_rank_unet", default=128, type=int) | |
| parser.add_argument("--lora_rank_vae", default=4, type=int) | |
| # args for validation and logging | |
| parser.add_argument("--viz_freq", type=int, default=20) | |
| parser.add_argument("--output_dir", type=str, required=True) | |
| parser.add_argument("--report_to", type=str, default="wandb") | |
| parser.add_argument("--tracker_project_name", type=str, required=True) | |
| parser.add_argument("--tracker_run_name", type=str, required=True) | |
| parser.add_argument("--tracker_run_id", type=str, default=None) | |
| parser.add_argument("--validation_steps", type=int, default=500) | |
| parser.add_argument( | |
| "--validation_num_images", | |
| type=int, | |
| default=-1, | |
| help="Number of images to use for validation. -1 to use all images.", | |
| ) | |
| parser.add_argument("--checkpointing_steps", type=int, default=500) | |
| # args for the optimization options | |
| parser.add_argument("--learning_rate", type=float, default=5e-6) | |
| parser.add_argument("--adam_beta1", type=float, default=0.9, help="The beta1 parameter for the Adam optimizer.") | |
| parser.add_argument("--adam_beta2", type=float, default=0.999, help="The beta2 parameter for the Adam optimizer.") | |
| parser.add_argument("--adam_weight_decay", type=float, default=1e-2, help="Weight decay to use.") | |
| parser.add_argument("--adam_epsilon", type=float, default=1e-08, help="Epsilon value for the Adam optimizer") | |
| parser.add_argument("--max_grad_norm", default=10.0, type=float, help="Max gradient norm.") | |
| parser.add_argument( | |
| "--lr_scheduler", | |
| type=str, | |
| default="constant", | |
| help=( | |
| 'The scheduler type to use. Choose between ["linear", "cosine", "cosine_with_restarts", "polynomial",' | |
| ' "constant", "constant_with_warmup"]' | |
| ), | |
| ) | |
| parser.add_argument( | |
| "--lr_warmup_steps", type=int, default=500, help="Number of steps for the warmup in the lr scheduler." | |
| ) | |
| parser.add_argument( | |
| "--lr_num_cycles", | |
| type=int, | |
| default=1, | |
| help="Number of hard resets of the lr in cosine_with_restarts scheduler.", | |
| ) | |
| parser.add_argument("--lr_power", type=float, default=1.0, help="Power factor of the polynomial scheduler.") | |
| parser.add_argument("--gradient_accumulation_steps", type=int, default=1) | |
| # memory saving options | |
| parser.add_argument( | |
| "--allow_tf32", | |
| action="store_true", | |
| help=( | |
| "Whether or not to allow TF32 on Ampere GPUs. Can be used to speed up training. For more information, see" | |
| " https://pytorch.org/docs/stable/notes/cuda.html#tensorfloat-32-tf32-on-ampere-devices" | |
| ), | |
| ) | |
| parser.add_argument( | |
| "--gradient_checkpointing", | |
| action="store_true", | |
| help="Whether or not to use gradient checkpointing to save memory at the expense of slower backward pass.", | |
| ) | |
| parser.add_argument( | |
| "--mixed_precision", | |
| type=str, | |
| default=None, | |
| choices=["no", "fp16", "bf16"], | |
| ) | |
| parser.add_argument( | |
| "--enable_xformers_memory_efficient_attention", action="store_true", help="Whether or not to use xformers." | |
| ) | |
| # resume | |
| parser.add_argument("--resume", default=None, type=str) | |
| args = parser.parse_args() | |
| return args | |
| def build_transform(image_prep, interpolation=Image.LANCZOS): | |
| """ | |
| Constructs a transformation pipeline based on the specified image preparation method. | |
| Parameters: | |
| - image_prep (str): A string describing the desired image preparation | |
| Returns: | |
| - torchvision.transforms.Compose: A composable sequence of transformations to be applied to images. | |
| """ | |
| if image_prep == "resized_crop_512": | |
| T = transforms.Compose( | |
| [ | |
| transforms.Resize(512, interpolation=transforms.InterpolationMode.LANCZOS), | |
| transforms.CenterCrop(512), | |
| ] | |
| ) | |
| elif image_prep == "resized_random_crop_512": | |
| T = transforms.Compose( | |
| [ | |
| transforms.Resize(512, interpolation=transforms.InterpolationMode.LANCZOS), | |
| transforms.RandomCrop((512, 512)), | |
| ] | |
| ) | |
| elif image_prep == "resize_286_randomcrop_256x256_hflip": | |
| T = transforms.Compose( | |
| [ | |
| transforms.Resize((286, 286), interpolation=interpolation), | |
| transforms.RandomCrop((256, 256)), | |
| transforms.RandomHorizontalFlip(), | |
| ] | |
| ) | |
| elif image_prep in ["resize_256", "resize_256x256"]: | |
| T = transforms.Compose([transforms.Resize((256, 256), interpolation=interpolation)]) | |
| elif image_prep in ["resize_512", "resize_512x512"]: | |
| T = transforms.Compose([transforms.Resize((512, 512), interpolation=interpolation)]) | |
| elif image_prep == "resize_576x1024": | |
| T = transforms.Compose( | |
| [ | |
| transforms.Resize((576, 1024), interpolation=interpolation), | |
| ] | |
| ) | |
| elif image_prep == "resize_288x512": | |
| T = transforms.Compose( | |
| [ | |
| transforms.Resize((288, 512), interpolation=interpolation), | |
| ] | |
| ) | |
| elif image_prep == "resize_384x704": | |
| T = transforms.Compose( | |
| [ | |
| transforms.Resize((384, 704), interpolation=interpolation), | |
| ] | |
| ) | |
| elif image_prep == "resize_448x832": | |
| T = transforms.Compose( | |
| [ | |
| transforms.Resize((448, 832), interpolation=interpolation), | |
| ] | |
| ) | |
| elif image_prep == "resize_200x360": | |
| T = transforms.Compose( | |
| [ | |
| transforms.Resize((200, 360), interpolation=interpolation), | |
| ] | |
| ) | |
| elif image_prep == "resize_544x960": | |
| T = transforms.Compose( | |
| [ | |
| transforms.Resize((544, 960), interpolation=interpolation), | |
| ] | |
| ) | |
| elif image_prep == "resize_384x672": | |
| T = transforms.Compose( | |
| [ | |
| transforms.Resize((384, 672), interpolation=interpolation), | |
| ] | |
| ) | |
| elif image_prep == "resize_768x1360": | |
| T = transforms.Compose( | |
| [ | |
| transforms.Resize((768, 1360), interpolation=interpolation), | |
| ] | |
| ) | |
| elif image_prep == "resize_416x736": | |
| T = transforms.Compose( | |
| [ | |
| transforms.Resize((416, 736), interpolation=interpolation), | |
| ] | |
| ) | |
| elif image_prep == "resize_1088x1920": | |
| T = transforms.Compose( | |
| [ | |
| transforms.Resize((1088, 1920), interpolation=interpolation), | |
| ] | |
| ) | |
| elif image_prep == "resize_2176x3840": | |
| T = transforms.Compose( | |
| [ | |
| transforms.Resize((2176, 3840), interpolation=interpolation), | |
| ] | |
| ) | |
| elif image_prep == "resize_576x1024_cropcar": | |
| T = transforms.Compose( | |
| [ | |
| transforms.Resize((576, 1024), interpolation=interpolation), | |
| transforms.Lambda(lambda img: F.crop(img, 0, 0, 416, 1024)), # crop out the car | |
| ] | |
| ) | |
| elif image_prep == "resize_576x1024_cropcar_randomcrop_400x400": | |
| T = transforms.Compose( | |
| [ | |
| transforms.Resize((576, 1024), interpolation=interpolation), | |
| transforms.Lambda(lambda img: F.crop(img, 0, 0, 416, 1024)), # crop out the car | |
| transforms.RandomCrop((400, 400)), | |
| ] | |
| ) | |
| elif image_prep == "resize_1024x576": | |
| T = transforms.Compose( | |
| [ | |
| transforms.Resize((1024, 576), interpolation=interpolation), | |
| ] | |
| ) | |
| elif image_prep == "no_resize": | |
| T = transforms.Lambda(lambda x: x) | |
| return T | |
| # --------------------------------------------------------------------------- | |
| # Multi-view reference helpers ported from the legacy diffusion-fixer | |
| # (cosmos_fixer/.../my_utils/training_utils.py). These produce a list of N | |
| # reference image paths from a single ``train_A`` / ``test_A`` input image so | |
| # the dataloader can stack them along the view axis. | |
| # --------------------------------------------------------------------------- | |
| import random | |
| from pathlib import Path | |
| PUREWHITE_PATH = "/lustre/fs12/portfolios/nvr/users/alezhang/purewhite.png" | |
| def _get_prior_gt_frames_kx(input_path, offset=-1): | |
| assert "_A/" in input_path | |
| file_name = input_path.split("/")[-1].split(".")[0] | |
| frame_index = int(file_name.split("frame_")[-1]) | |
| condition_index = frame_index + offset | |
| if condition_index < 1: | |
| return PUREWHITE_PATH | |
| output_path = input_path.replace(f"frame_{frame_index:04d}.png", f"frame_{condition_index:04d}.png") | |
| assert input_path != output_path | |
| output_path = output_path.replace("test_A/", "test_B/").replace("train_A/", "train_B/") | |
| assert "_B/" in output_path | |
| return output_path | |
| def _get_prior_gt_frames_ds(input_path, offset=-1): | |
| assert "_A/" in input_path | |
| file_path = Path(input_path) | |
| file_name = file_path.stem | |
| file_suffix = file_path.suffix | |
| frame_index = int(file_name.split("frame_")[-1]) | |
| condition_index = frame_index + offset | |
| if condition_index < 1: | |
| return PUREWHITE_PATH | |
| output_path = input_path.replace( | |
| f"frame_{frame_index:04d}{file_suffix}", f"frame_{condition_index:04d}{file_suffix}" | |
| ) | |
| assert input_path != output_path | |
| output_path = output_path.replace("test_A/", "test_B/").replace("train_A/", "train_B/") | |
| assert "_B/" in output_path | |
| return output_path | |
| def _get_prior_gt_frames_kx_ispdata(input_path, offset=-1): | |
| assert "_A/" in input_path | |
| file_name = input_path.split("/")[-1].split(".")[0] | |
| frame_index = int(file_name.split("frame_")[-1].split("_sim")[0]) | |
| condition_index = frame_index + offset | |
| if condition_index < 1: | |
| return PUREWHITE_PATH | |
| output_path = input_path.replace(f"frame_{frame_index:04d}", f"frame_{condition_index:04d}") | |
| assert input_path != output_path | |
| output_path = output_path.replace("test_A/", "test_B/").replace("train_A/", "train_B/") | |
| assert "_B/" in output_path | |
| return output_path | |
| def get_gt_multiview_randomNone_noteven2_kx_ispdata_gap1(input_img): | |
| assert "_A" in input_img | |
| if random.random() <= 0.5: | |
| ref = [_get_prior_gt_frames_kx_ispdata(input_img, off) for off in [-1, -2, -3, -4]] | |
| if any(PUREWHITE_PATH in p for p in ref): | |
| return None | |
| return ref | |
| return None | |
| def get_gt_multiview_randomNone_noteven2_ds_gap1(input_img): | |
| assert "_A" in input_img | |
| if random.random() <= 0.5: | |
| ref = [_get_prior_gt_frames_ds(input_img, off) for off in [-1, -2, -3, -4]] | |
| if any(PUREWHITE_PATH in p for p in ref): | |
| return None | |
| return ref | |
| return None | |
| def get_gt_multiview_randomwhite_noteven2_kx_gap1(input_img): | |
| assert "_A" in input_img | |
| if random.random() <= 0.5: | |
| return [_get_prior_gt_frames_kx(input_img, off) for off in [-1, -2, -3, -4]] | |
| return [PUREWHITE_PATH] * 4 | |
| def _get_prior_gt_frames_relight(input_path, offset=-1): | |
| # Filenames in the relighting dataset are | |
| # <clipID_cameraID>_seg<S:04d>_env<E:04d>_frame_<rawIdx:04d>.jpg | |
| # The seg/env tag is retained on prior frames so train_B hardlinks (created | |
| # by 0517_relighting_data.py) resolve. Existence is checked here because | |
| # segment-boundary priors may not have been pre-populated for every (seg, env). | |
| assert "_A/" in input_path | |
| file_path = Path(input_path) | |
| file_name = file_path.stem | |
| file_suffix = file_path.suffix | |
| frame_index = int(file_name.split("frame_")[-1]) | |
| condition_index = frame_index + offset | |
| if condition_index < 1: | |
| return PUREWHITE_PATH | |
| output_path = input_path.replace( | |
| f"frame_{frame_index:04d}{file_suffix}", f"frame_{condition_index:04d}{file_suffix}" | |
| ) | |
| assert input_path != output_path | |
| output_path = output_path.replace("test_A/", "test_B/").replace("train_A/", "train_B/") | |
| assert "_B/" in output_path | |
| if not os.path.exists(output_path): | |
| return PUREWHITE_PATH | |
| return output_path | |
| def get_gt_multiview_randomNone_noteven2_relight_gap1(input_img): | |
| assert "_A" in input_img | |
| if random.random() <= 0.5: | |
| ref = [_get_prior_gt_frames_relight(input_img, off) for off in [-1, -2, -3, -4]] | |
| if any(PUREWHITE_PATH in p for p in ref): | |
| return None | |
| return ref | |
| return None | |
| REF_METHOD_DICT = { | |
| "get_gt_multiview_randomNone_noteven2_kx_ispdata_gap1": get_gt_multiview_randomNone_noteven2_kx_ispdata_gap1, | |
| "get_gt_multiview_randomNone_noteven2_ds_gap1": get_gt_multiview_randomNone_noteven2_ds_gap1, | |
| "get_gt_multiview_randomwhite_noteven2_kx_gap1": get_gt_multiview_randomwhite_noteven2_kx_gap1, | |
| "get_gt_multiview_randomNone_noteven2_relight_gap1": get_gt_multiview_randomNone_noteven2_relight_gap1, | |
| } | |
| class PairedDataset(torch.utils.data.Dataset): | |
| """Directory-style paired dataset (``train_A`` / ``train_B`` / ``train_prompts.json``) | |
| with optional ``+ref_method`` suffix that appends N reference frames so | |
| ``__getitem__`` returns a (V, C, H, W) stacked tensor.""" | |
| def __init__(self, dataset_folder, split, image_prep, tokenizer=None): | |
| super().__init__() | |
| if "+" in dataset_folder: | |
| dataset_folder, ref_method = dataset_folder.split("+", 1) | |
| self.ref_func = REF_METHOD_DICT[ref_method] | |
| else: | |
| self.ref_func = None | |
| if split == "train": | |
| self.input_folder = os.path.join(dataset_folder, "train_A") | |
| self.output_folder = os.path.join(dataset_folder, "train_B") | |
| captions_path = os.path.join(dataset_folder, "train_prompts.json") | |
| else: | |
| self.input_folder = os.path.join(dataset_folder, "test_A") | |
| self.output_folder = os.path.join(dataset_folder, "test_B") | |
| captions_path = os.path.join(dataset_folder, "test_prompts.json") | |
| with open(captions_path) as f: | |
| self.captions = json.load(f) | |
| self.img_names = list(self.captions.keys()) | |
| self.T = build_transform(image_prep) | |
| self.tokenizer = tokenizer | |
| def __len__(self): | |
| return len(self.img_names) | |
| def preprocess_image(self, input_img): | |
| img_t = self.T(input_img) | |
| img_t = F.to_tensor(img_t) | |
| img_t = F.normalize(img_t, mean=[0.5], std=[0.5]) | |
| return img_t | |
| def __getitem__(self, idx): | |
| img_name = self.img_names[idx] | |
| input_path = os.path.join(self.input_folder, img_name) | |
| output_path = os.path.join(self.output_folder, img_name) | |
| ref_paths = self.ref_func(input_path) if self.ref_func is not None else None | |
| input_img = Image.open(input_path) | |
| output_img = Image.open(output_path) | |
| img_t = self.preprocess_image(input_img) | |
| output_t = self.preprocess_image(output_img) | |
| if ref_paths is not None: | |
| ref_paths = [ref_paths] if not isinstance(ref_paths, list) else ref_paths | |
| ref_ts = [self.preprocess_image(Image.open(p)) for p in ref_paths] | |
| img_t = torch.stack([img_t] + ref_ts, dim=0) | |
| output_t = torch.stack([output_t] + ref_ts, dim=0) | |
| out = { | |
| "output_pixel_values": output_t, | |
| "conditioning_pixel_values": img_t, | |
| "caption": self.captions[img_name], | |
| } | |
| return out | |
| class PairedDatasetV2(torch.utils.data.Dataset): | |
| def _load_from_json(json_path, split): | |
| """Load data from JSON file""" | |
| with open(json_path) as f: | |
| json_data = json.load(f)[split] | |
| return json_data, list(json_data.keys()) | |
| def _load_from_directory(dir_path, split): | |
| """Load data from directory structure""" | |
| input_dir = os.path.join(dir_path, f"{split}_A") | |
| output_dir = os.path.join(dir_path, f"{split}_B") | |
| caption_file = os.path.join(dir_path, f"{split}_prompts.json") | |
| with open(caption_file) as f: | |
| captions = json.load(f) | |
| input_files = list(captions.keys()) | |
| new_data = {} | |
| for img_file in tqdm(input_files, desc=f"Loading {split} from {os.path.basename(dir_path)}"): | |
| new_data[img_file] = { | |
| "image": os.path.join(input_dir, img_file), | |
| "target_image": os.path.join(output_dir, img_file), | |
| "prompt": captions[img_file], # Empty string if no caption exists | |
| } | |
| return new_data, input_files | |
| def __init__(self, dataset_folder, split, image_prep, tokenizer=None): | |
| super().__init__() | |
| self.data = {} | |
| self.img_names = [] | |
| # Split the dataset_folder string into individual paths | |
| data_sources = [source.strip() for source in dataset_folder.split(",")] | |
| for source in data_sources: | |
| if source.endswith(".json"): | |
| data, file_names = self._load_from_json(source, split) | |
| else: | |
| data, file_names = self._load_from_directory(source, split) | |
| self.data.update(data) | |
| self.img_names.extend(file_names) | |
| self.T = build_transform(image_prep) | |
| self.tokenizer = tokenizer | |
| def __len__(self): | |
| """ | |
| Returns: | |
| int: The total number of items in the dataset. | |
| """ | |
| return len(self.img_names) | |
| def preprocess_image(self, input_img): | |
| img_t = self.T(input_img) | |
| img_t = F.to_tensor(img_t) | |
| img_t = F.normalize(img_t, mean=[0.5], std=[0.5]) | |
| return img_t | |
| def __getitem__(self, idx): | |
| img_name = self.img_names[idx] | |
| input_img = self.data[img_name]["image"] | |
| output_img = self.data[img_name]["target_image"] | |
| ref_img = None | |
| caption = self.data[img_name]["prompt"] | |
| input_img = Image.open(input_img) | |
| output_img = Image.open(output_img) | |
| if ref_img is not None: | |
| ref_img = [ref_img] if type(ref_img) != list else ref_img | |
| ref_img = [Image.open(path) for path in ref_img] | |
| # input images scaled to -1,1 | |
| img_t = self.preprocess_image(input_img) | |
| # output images scaled to -1,1 | |
| output_t = self.preprocess_image(output_img) | |
| ref_t = None | |
| if ref_img is not None: | |
| ref_t_all = [] | |
| for tmp_img in ref_img: | |
| ref_t = self.preprocess_image(tmp_img) | |
| ref_t_all.append(ref_t) | |
| img_t = torch.stack([img_t] + ref_t_all, dim=0) | |
| output_t = torch.stack([output_t] + ref_t_all, dim=0) | |
| out = {"output_pixel_values": output_t, "conditioning_pixel_values": img_t, "caption": caption} | |
| return out | |