DragStream / utils /dataset.py
bowmanchow's picture
add code
0328207
Raw
History Blame Contribute Delete
7.39 kB
from utils.lmdb import get_array_shape_from_lmdb, retrieve_row_from_lmdb
from torch.utils.data import Dataset
import numpy as np
import torch
import lmdb
import json
from pathlib import Path
from PIL import Image
import os
class TextDataset(Dataset):
def __init__(
self,
prompt_path,
extended_prompt_path=None,
):
with open(prompt_path, encoding="utf-8") as f:
self.prompt_list = [line.rstrip() for line in f]
if extended_prompt_path is not None:
with open(extended_prompt_path, encoding="utf-8") as f:
self.extended_prompt_list = [line.rstrip() for line in f]
assert len(self.extended_prompt_list) == len(self.prompt_list)
else:
self.extended_prompt_list = None
def __len__(
self,
):
return len(self.prompt_list)
def __getitem__(
self,
idx,
):
batch = {
"prompts": self.prompt_list[idx],
"idx": idx,
}
if self.extended_prompt_list is not None:
batch["extended_prompts"] = self.extended_prompt_list[idx]
return batch
class ODERegressionLMDBDataset(Dataset):
def __init__(
self,
data_path: str,
max_pair: int = int(1e8),
):
self.env = lmdb.open(data_path, readonly=True, lock=False, readahead=False, meminit=False)
self.latents_shape = get_array_shape_from_lmdb(self.env, "latents")
self.max_pair = max_pair
def __len__(
self,
):
return min(self.latents_shape[0], self.max_pair)
def __getitem__(
self,
idx,
):
"""
Outputs:
- prompts: List of Strings
- latents: Tensor of shape (num_denoising_steps, num_frames, num_channels, height, width). It is ordered from pure noise to clean image.
"""
latents = retrieve_row_from_lmdb(
self.env, "latents", np.float16, idx, shape=self.latents_shape[1:]
)
if len(latents.shape) == 4:
latents = latents[None, ...]
prompts = retrieve_row_from_lmdb(self.env, "prompts", str, idx)
return {
"prompts": prompts,
"ode_latent": torch.tensor(latents, dtype=torch.float32),
}
class ShardingLMDBDataset(Dataset):
def __init__(
self,
data_path: str,
max_pair: int = int(1e8),
):
self.envs = []
self.index = []
for fname in sorted(os.listdir(data_path)):
path = os.path.join(data_path, fname)
env = lmdb.open(path, readonly=True, lock=False, readahead=False, meminit=False)
self.envs.append(env)
self.latents_shape = [None] * len(self.envs)
for shard_id, env in enumerate(self.envs):
self.latents_shape[shard_id] = get_array_shape_from_lmdb(env, "latents")
for local_i in range(self.latents_shape[shard_id][0]):
self.index.append((shard_id, local_i))
# print("shard_id ", shard_id, " local_i ", local_i)
self.max_pair = max_pair
def __len__(
self,
):
return len(self.index)
def __getitem__(
self,
idx,
):
"""
Outputs:
- prompts: List of Strings
- latents: Tensor of shape (num_denoising_steps, num_frames, num_channels, height, width). It is ordered from pure noise to clean image.
"""
shard_id, local_idx = self.index[idx]
latents = retrieve_row_from_lmdb(
self.envs[shard_id],
"latents",
np.float16,
local_idx,
shape=self.latents_shape[shard_id][1:],
)
if len(latents.shape) == 4:
latents = latents[None, ...]
prompts = retrieve_row_from_lmdb(self.envs[shard_id], "prompts", str, local_idx)
return {
"prompts": prompts,
"ode_latent": torch.tensor(latents, dtype=torch.float32),
}
class TextImagePairDataset(Dataset):
def __init__(
self,
data_dir,
transform=None,
eval_first_n=-1,
pad_to_multiple_of=None,
):
"""
Args:
data_dir (str): Path to the directory containing:
- target_crop_info_*.json (metadata file)
- */ (subdirectory containing images with matching aspect ratio)
transform (callable, optional): Optional transform to be applied on the image
"""
self.transform = transform
data_dir = Path(data_dir)
# Find the metadata JSON file
metadata_files = list(data_dir.glob("target_crop_info_*.json"))
if not metadata_files:
raise FileNotFoundError(f"No metadata file found in {data_dir}")
if len(metadata_files) > 1:
raise ValueError(f"Multiple metadata files found in {data_dir}")
metadata_path = metadata_files[0]
# Extract aspect ratio from metadata filename (e.g. target_crop_info_26-15.json -> 26-15)
aspect_ratio = metadata_path.stem.split("_")[-1]
# Use aspect ratio subfolder for images
self.image_dir = data_dir / aspect_ratio
if not self.image_dir.exists():
raise FileNotFoundError(f"Image directory not found: {self.image_dir}")
# Load metadata
with open(metadata_path, "r") as f:
self.metadata = json.load(f)
eval_first_n = eval_first_n if eval_first_n != -1 else len(self.metadata)
self.metadata = self.metadata[:eval_first_n]
# Verify all images exist
for item in self.metadata:
image_path = self.image_dir / item["file_name"]
if not image_path.exists():
raise FileNotFoundError(f"Image not found: {image_path}")
self.dummy_prompt = "DUMMY PROMPT"
self.pre_pad_len = len(self.metadata)
if pad_to_multiple_of is not None and len(self.metadata) % pad_to_multiple_of != 0:
# Duplicate the last entry
self.metadata += [self.metadata[-1]] * (
pad_to_multiple_of - len(self.metadata) % pad_to_multiple_of
)
def __len__(
self,
):
return len(self.metadata)
def __getitem__(
self,
idx,
):
"""
Returns:
dict: A dictionary containing:
- image: PIL Image
- caption: str
- target_bbox: list of int [x1, y1, x2, y2]
- target_ratio: str
- type: str
- origin_size: tuple of int (width, height)
"""
item = self.metadata[idx]
# Load image
image_path = self.image_dir / item["file_name"]
image = Image.open(image_path).convert("RGB")
# Apply transform if specified
if self.transform:
image = self.transform(image)
return {
"image": image,
"prompts": item["caption"],
"target_bbox": item["target_crop"]["target_bbox"],
"target_ratio": item["target_crop"]["target_ratio"],
"type": item["type"],
"origin_size": (item["origin_width"], item["origin_height"]),
"idx": idx,
}
def cycle(
dl,
):
while True:
for data in dl:
yield data