Download scripts/make_pairs.py from Ypeng12/FaceID: direct link, hf CLI and curl.
- Browser
- Download file 4.57 kB
-
https://huggingface.co/spaces/Ypeng12/FaceID/resolve/main/scripts/make_pairs.py
- Command line
-
hf download hf://spaces/Ypeng12/FaceID/scripts/make_pairs.py
-
curl -L -o make_pairs.py https://huggingface.co/spaces/Ypeng12/FaceID/resolve/main/scripts/make_pairs.py
4.57 kB
| import argparse | |
| import os | |
| import yaml | |
| import numpy as np | |
| import csv | |
| 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 get_images_per_identity(split_dir): | |
| """Returns a dict {identity: [list of relative image paths]}""" | |
| images_dict = {} | |
| if not os.path.exists(split_dir): | |
| return images_dict | |
| identities = sorted(os.listdir(split_dir)) | |
| for identity in identities: | |
| identity_dir = os.path.join(split_dir, identity) | |
| if os.path.isdir(identity_dir): | |
| images = sorted(os.listdir(identity_dir)) | |
| # Store paths relative to project root for portability | |
| images_paths = [os.path.relpath(os.path.join(identity_dir, img)) for img in images if img.endswith('.jpg')] | |
| if images_paths: | |
| images_dict[identity] = images_paths | |
| return images_dict | |
| def generate_pairs_for_split(images_dict, num_pairs, split_name, enforce_unique=False): | |
| """Generate num_pairs/2 positive and num_pairs/2 negative pairs.""" | |
| identities = sorted(list(images_dict.keys())) | |
| # Positive pairs: need identities with at least 2 images | |
| pos_identities = [id for id in identities if len(images_dict[id]) >= 2] | |
| pairs = [] | |
| used_pos = set() | |
| used_neg = set() | |
| # Generate positive pairs | |
| n_pos = num_pairs // 2 | |
| attempts = 0 | |
| while len(pairs) < n_pos and attempts < n_pos * 10: | |
| attempts += 1 | |
| if not pos_identities: | |
| break | |
| identity = np.random.choice(pos_identities) | |
| imgs = images_dict[identity] | |
| idx1, idx2 = np.random.choice(len(imgs), 2, replace=False) | |
| idx1_norm, idx2_norm = min(idx1, idx2), max(idx1, idx2) | |
| pair_key = (identity, idx1_norm, idx2_norm) | |
| if enforce_unique and pair_key in used_pos: | |
| continue | |
| used_pos.add(pair_key) | |
| pairs.append({ | |
| 'left_path': imgs[idx1], | |
| 'right_path': imgs[idx2], | |
| 'label': 1, | |
| 'split': split_name | |
| }) | |
| # Generate negative pairs | |
| n_neg = num_pairs - len(pairs) | |
| attempts = 0 | |
| while len(pairs) < num_pairs and attempts < n_neg * 10: | |
| attempts += 1 | |
| if len(identities) < 2: | |
| break | |
| id1, id2 = np.random.choice(identities, 2, replace=False) | |
| img1 = np.random.choice(images_dict[id1]) | |
| img2 = np.random.choice(images_dict[id2]) | |
| id1_norm, id2_norm, i1, i2 = (id1, id2, img1, img2) if id1 < id2 else (id2, id1, img2, img1) | |
| pair_key = (id1_norm, i1, id2_norm, i2) | |
| if enforce_unique and pair_key in used_neg: | |
| continue | |
| used_neg.add(pair_key) | |
| pairs.append({ | |
| 'left_path': img1, | |
| 'right_path': img2, | |
| 'label': 0, | |
| 'split': split_name | |
| }) | |
| return pairs | |
| def main(): | |
| parser = argparse.ArgumentParser(description="Deterministic pair generation.") | |
| parser.add_argument("--config", type=str, required=True, help="Path to pairs config.") | |
| args = parser.parse_args() | |
| config = load_config(args.config) | |
| seed = config.get("seed", 42) | |
| set_seed(seed) | |
| data_dir = config["data_dir"] | |
| output_dir = config["output_dir"] | |
| os.makedirs(output_dir, exist_ok=True) | |
| split_configs = { | |
| 'train': config.get('train_pairs', 2000), | |
| 'val': config.get('val_pairs', 500), | |
| 'test': config.get('test_pairs', 500) | |
| } | |
| for split_name, num_pairs in split_configs.items(): | |
| split_dir = os.path.join(data_dir, split_name) | |
| images_dict = get_images_per_identity(split_dir) | |
| if not images_dict: | |
| print(f"Warning: No images found for split {split_name} in {split_dir}") | |
| continue | |
| enforce_unique = config.get('enforce_unique', False) | |
| pairs = generate_pairs_for_split(images_dict, num_pairs, split_name, enforce_unique=enforce_unique) | |
| output_file = os.path.join(output_dir, f"{split_name}.csv") | |
| with open(output_file, 'w', newline='') as f: | |
| writer = csv.DictWriter(f, fieldnames=['left_path', 'right_path', 'label', 'split']) | |
| writer.writeheader() | |
| writer.writerows(pairs) | |
| print(f"[{split_name}] Generated {len(pairs)} pairs -> {output_file}") | |
| print("Pair generation complete.") | |
| if __name__ == "__main__": | |
| main() | |