Spaces:
Paused
Paused
| """ControlNet dataset: paired training images and conditioning (control) images. | |
| Extracted from ``library.train_util`` as part of the dataset-split refactor; | |
| imports the abstract :class:`~library.dataset.BaseDataset` and its | |
| :class:`~library.subset.ControlNetSubset` configuration class. | |
| """ | |
| import logging | |
| import os | |
| from typing import Any, List, Optional, Sequence, Tuple | |
| import torch | |
| from accelerate import Accelerator | |
| from library.dataset import ( | |
| IMAGE_TRANSFORMS, | |
| BaseDataset, | |
| glob_images, | |
| load_image, | |
| ) | |
| from library.dreambooth_dataset import DreamBoothDataset | |
| from library.subset import ControlNetSubset, DreamBoothSubset | |
| from library.utils import resize_image, setup_logging, trim_and_resize_if_required | |
| setup_logging() | |
| logger = logging.getLogger(__name__) | |
| class ControlNetDataset(BaseDataset): | |
| def __init__( | |
| self, | |
| subsets: Sequence[ControlNetSubset], | |
| batch_size: int, | |
| resolution, | |
| network_multiplier: float, | |
| enable_bucket: bool, | |
| min_bucket_reso: int, | |
| max_bucket_reso: int, | |
| bucket_reso_steps: int, | |
| bucket_no_upscale: bool, | |
| train_inpainting: bool, | |
| debug_dataset: bool, | |
| validation_split: float, | |
| validation_seed: Optional[int], | |
| resize_interpolation: Optional[str] = None, | |
| skip_image_resolution: Optional[Tuple[int, int]] = None, | |
| ) -> None: | |
| super().__init__( | |
| resolution, | |
| network_multiplier, | |
| train_inpainting, | |
| debug_dataset, | |
| resize_interpolation, | |
| skip_image_resolution, | |
| ) | |
| db_subsets = [] | |
| for subset in subsets: | |
| assert ( | |
| not subset.random_crop | |
| ), "random_crop is not supported in ControlNetDataset / random_cropはControlNetDatasetではサポートされていません" | |
| db_subset = DreamBoothSubset( | |
| subset.image_dir, | |
| False, | |
| None, | |
| subset.caption_extension, | |
| subset.cache_info, | |
| False, | |
| subset.num_repeats, | |
| subset.shuffle_caption, | |
| subset.caption_separator, | |
| subset.keep_tokens, | |
| subset.keep_tokens_separator, | |
| subset.secondary_separator, | |
| subset.enable_wildcard, | |
| subset.color_aug, | |
| subset.flip_aug, | |
| subset.face_crop_aug_range, | |
| subset.random_crop, | |
| subset.caption_dropout_rate, | |
| subset.caption_dropout_every_n_epochs, | |
| subset.caption_tag_dropout_rate, | |
| subset.caption_prefix, | |
| subset.caption_suffix, | |
| subset.token_warmup_min, | |
| subset.token_warmup_step, | |
| resize_interpolation=subset.resize_interpolation, | |
| ) | |
| db_subsets.append(db_subset) | |
| self.dreambooth_dataset_delegate = DreamBoothDataset( | |
| db_subsets, | |
| True, | |
| batch_size, | |
| resolution, | |
| network_multiplier, | |
| enable_bucket, | |
| min_bucket_reso, | |
| max_bucket_reso, | |
| bucket_reso_steps, | |
| bucket_no_upscale, | |
| 1.0, | |
| train_inpainting, | |
| debug_dataset, | |
| validation_split, | |
| validation_seed, | |
| resize_interpolation, | |
| skip_image_resolution, | |
| ) | |
| # config_util等から参照される値をいれておく(若干微妙なのでなんとかしたい) | |
| self.image_data = self.dreambooth_dataset_delegate.image_data | |
| self.batch_size = batch_size | |
| self.num_train_images = self.dreambooth_dataset_delegate.num_train_images | |
| self.num_reg_images = self.dreambooth_dataset_delegate.num_reg_images | |
| self.validation_split = validation_split | |
| self.validation_seed = validation_seed | |
| self.resize_interpolation = resize_interpolation | |
| # assert all conditioning data exists | |
| missing_imgs = [] | |
| cond_imgs_with_pair = set() | |
| for image_key, info in self.dreambooth_dataset_delegate.image_data.items(): | |
| db_subset = self.dreambooth_dataset_delegate.image_to_subset[image_key] | |
| subset = None | |
| for s in subsets: | |
| if s.image_dir == db_subset.image_dir: | |
| subset = s | |
| break | |
| assert subset is not None, "internal error: subset not found" | |
| if not os.path.isdir(subset.conditioning_data_dir): | |
| logger.warning(f"not directory: {subset.conditioning_data_dir}") | |
| continue | |
| img_basename = os.path.splitext(os.path.basename(info.absolute_path))[0] | |
| ctrl_img_path = glob_images(subset.conditioning_data_dir, img_basename) | |
| if len(ctrl_img_path) < 1: | |
| missing_imgs.append(img_basename) | |
| continue | |
| ctrl_img_path = ctrl_img_path[0] | |
| ctrl_img_path = os.path.abspath(ctrl_img_path) # normalize path | |
| info.cond_img_path = ctrl_img_path | |
| cond_imgs_with_pair.add(os.path.splitext(ctrl_img_path)[0]) # remove extension because Windows is case insensitive | |
| extra_imgs = [] | |
| for subset in subsets: | |
| conditioning_img_paths = glob_images(subset.conditioning_data_dir, "*") | |
| conditioning_img_paths = [os.path.abspath(p) for p in conditioning_img_paths] # normalize path | |
| extra_imgs.extend([p for p in conditioning_img_paths if os.path.splitext(p)[0] not in cond_imgs_with_pair]) | |
| assert ( | |
| len(missing_imgs) == 0 | |
| ), f"missing conditioning data for {len(missing_imgs)} images / 制御用画像が見つかりませんでした: {missing_imgs}" | |
| if len(extra_imgs) > 0: | |
| logger.warning(f"extra conditioning data for {len(extra_imgs)} images / 余分な制御用画像があります: {extra_imgs}") | |
| self.conditioning_image_transforms = IMAGE_TRANSFORMS | |
| def set_current_strategies(self): | |
| return self.dreambooth_dataset_delegate.set_current_strategies() | |
| def make_buckets(self): | |
| self.dreambooth_dataset_delegate.make_buckets() | |
| self.bucket_manager = self.dreambooth_dataset_delegate.bucket_manager | |
| self.buckets_indices = self.dreambooth_dataset_delegate.buckets_indices | |
| def new_cache_latents(self, model: Any, accelerator: Accelerator): | |
| return self.dreambooth_dataset_delegate.new_cache_latents(model, accelerator) | |
| def new_cache_text_encoder_outputs(self, models: List[Any], is_main_process: bool): | |
| return self.dreambooth_dataset_delegate.new_cache_text_encoder_outputs(models, is_main_process) | |
| def __len__(self): | |
| return self.dreambooth_dataset_delegate.__len__() | |
| def __getitem__(self, index): | |
| example = self.dreambooth_dataset_delegate[index] | |
| bucket = self.dreambooth_dataset_delegate.bucket_manager.buckets[ | |
| self.dreambooth_dataset_delegate.buckets_indices[index].bucket_index | |
| ] | |
| bucket_batch_size = self.dreambooth_dataset_delegate.buckets_indices[index].bucket_batch_size | |
| image_index = self.dreambooth_dataset_delegate.buckets_indices[index].batch_index * bucket_batch_size | |
| conditioning_images = [] | |
| for i, image_key in enumerate(bucket[image_index : image_index + bucket_batch_size]): | |
| image_info = self.dreambooth_dataset_delegate.image_data[image_key] | |
| target_size_hw = example["target_sizes_hw"][i] | |
| original_size_hw = example["original_sizes_hw"][i] | |
| crop_top_left = example["crop_top_lefts"][i] | |
| flipped = example["flippeds"][i] | |
| cond_img = load_image(image_info.cond_img_path) | |
| if self.dreambooth_dataset_delegate.enable_bucket: | |
| assert ( | |
| cond_img.shape[0] == original_size_hw[0] and cond_img.shape[1] == original_size_hw[1] | |
| ), f"size of conditioning image is not match / 画像サイズが合いません: {image_info.absolute_path}" | |
| cond_img, _, _ = trim_and_resize_if_required( | |
| False, # TODO support random crop | |
| cond_img, | |
| image_info.bucket_reso, | |
| image_info.resized_size, | |
| resize_interpolation=image_info.resize_interpolation, | |
| ) | |
| else: | |
| # assert ( | |
| # cond_img.shape[0] == self.height and cond_img.shape[1] == self.width | |
| # ), f"image size is small / 画像サイズが小さいようです: {image_info.absolute_path}" | |
| # resize to target | |
| if cond_img.shape[0] != target_size_hw[0] or cond_img.shape[1] != target_size_hw[1]: | |
| cond_img = resize_image( | |
| cond_img, | |
| cond_img.shape[0], | |
| cond_img.shape[1], | |
| target_size_hw[1], | |
| target_size_hw[0], | |
| self.resize_interpolation, | |
| ) | |
| if flipped: | |
| cond_img = cond_img[:, ::-1, :].copy() # copy to avoid negative stride | |
| cond_img = self.conditioning_image_transforms(cond_img) | |
| conditioning_images.append(cond_img) | |
| example["conditioning_images"] = torch.stack(conditioning_images).to(memory_format=torch.contiguous_format).float() | |
| return example | |