FaceID / scripts /ingest_lfw.py
Ypeng12's picture
Fix libgl1 package name for Debian Trixie/Bookworm in Dockerfile
69def8e
Raw
History Blame Contribute Delete
4.2 kB
import argparse
import os
import sys
import yaml
import numpy as np
# Python 3.12 removed distutils, but tensorflow still requires it.
try:
import distutils
except ImportError:
import setuptools
sys.modules['distutils'] = setuptools._distutils
import tensorflow_datasets as tfds
from PIL import Image
def load_config(config_path):
with open(config_path, 'r') as f:
return yaml.safe_load(f)
def set_seed(seed):
np.random.seed(seed)
def main():
parser = argparse.ArgumentParser(description="Ingest LFW dataset deterministically.")
parser.add_argument("--config", type=str, required=True, help="Path to the config file.")
args = parser.parse_args()
config = load_config(args.config)
seed = config.get("seed", 42)
set_seed(seed)
print(f"Loading {config['data_source']}...")
# Load dataset. LFW provides 'train' split default usually, but tfds lfw might not have pre-defined splits other than 'train'
ds, ds_info = tfds.load("lfw", split="train", with_info=True, as_supervised=False)
# We will build a dictionary of identity -> list of (image_array, original_filename)
# LFW in TFDS has 'label' which is integer, and 'image' which is array.
# We need the actual string labels to sort them properly if we want identity names,
# or we can just use the integer label if we sort deterministically.
# LFW feature 'label' contains the text identity
dataset_dict = {}
print("Iterating over dataset to group by identity...")
for i, example in enumerate(tfds.as_numpy(ds)):
identity = example['label'].decode('utf-8')
image = example['image']
if identity not in dataset_dict:
dataset_dict[identity] = []
dataset_dict[identity].append(image)
identities = sorted(list(dataset_dict.keys()))
print(f"Found {len(identities)} identities and {sum(len(v) for v in dataset_dict.values())} total images.")
# Shuffle identities deterministically
np.random.shuffle(identities)
# Simple split of identities
n_total = len(identities)
n_train = int(n_total * config["train_ratio"])
n_val = int(n_total * config["val_ratio"])
train_ids = identities[:n_train]
val_ids = identities[n_train:n_train + n_val]
test_ids = identities[n_train + n_val:]
splits = {
"train": train_ids,
"val": val_ids,
"test": test_ids
}
output_dir = config["output_dir"]
os.makedirs(output_dir, exist_ok=True)
counts = {}
print("Saving images to disk...")
for split_name, ids in splits.items():
split_dir = os.path.join(output_dir, split_name)
os.makedirs(split_dir, exist_ok=True)
num_images = 0
for identity in ids:
identity_dir = os.path.join(split_dir, identity)
os.makedirs(identity_dir, exist_ok=True)
images = dataset_dict[identity]
# Since tfds doesn't guarantee order inside identity, we ensure deterministic order by... wait, tfds arrays are indistinguishable.
# We'll just save them in the order they appeared, which is deterministic given TFDS yields deterministically.
for i, img_arr in enumerate(images):
img = Image.fromarray(img_arr)
img_path = os.path.join(identity_dir, f"{identity}_{i:04d}.jpg")
img.save(img_path)
num_images += 1
counts[split_name] = {
"identities": len(ids),
"images": num_images
}
print(f"{split_name}: {len(ids)} identities, {num_images} images.")
manifest = {
"seed": seed,
"split_policy": config["split_policy"],
"counts": counts,
"data_source": config["data_source"],
"cache_location": output_dir
}
manifest_path = config["manifest_path"]
os.makedirs(os.path.dirname(manifest_path), exist_ok=True)
with open(manifest_path, 'w') as f:
yaml.dump(manifest, f, sort_keys=False)
print(f"Manifest written to {manifest_path}")
if __name__ == "__main__":
main()