Download scripts/generate_scenes.py from AnonymousECCV15285/MMIB_Counterfactual_image_generation_tool: direct link, hf CLI and curl.
- Browser
- Download file 10.1 kB
-
https://huggingface.co/spaces/AnonymousECCV15285/MMIB_Counterfactual_image_generation_tool/resolve/main/scripts/generate_scenes.py
- Command line
-
hf download hf://spaces/AnonymousECCV15285/MMIB_Counterfactual_image_generation_tool/scripts/generate_scenes.py
-
curl -L -o generate_scenes.py https://huggingface.co/spaces/AnonymousECCV15285/MMIB_Counterfactual_image_generation_tool/resolve/main/scripts/generate_scenes.py
10.1 kB
| #!/usr/bin/env python3 | |
| import sys | |
| import os | |
| script_dir = os.path.dirname(os.path.abspath(__file__)) | |
| project_root = os.path.dirname(script_dir) | |
| sys.path.insert(0, project_root) | |
| from pipeline import ( | |
| find_blender, | |
| create_patched_render_script, | |
| create_run_directory, | |
| generate_base_scene, | |
| generate_counterfactuals, | |
| save_scene, | |
| save_checkpoint, | |
| load_checkpoint, | |
| get_completed_scenes_from_folder, | |
| COUNTERFACTUAL_TYPES, | |
| list_counterfactual_types | |
| ) | |
| import argparse | |
| import random | |
| import json | |
| import shutil | |
| from datetime import datetime | |
| def main(): | |
| parser = argparse.ArgumentParser( | |
| description='Generate scene JSON files with counterfactuals (no rendering)' | |
| ) | |
| parser.add_argument('--num_scenes', type=int, default=5, | |
| help='Number of scene sets to generate') | |
| parser.add_argument('--num_objects', type=int, default=None, | |
| help='Fixed number of objects per scene (overrides min/max)') | |
| parser.add_argument('--min_objects', type=int, default=3, | |
| help='Minimum object count (if num_objects not given)') | |
| parser.add_argument('--max_objects', type=int, default=7, | |
| help='Maximum object count (if num_objects not given)') | |
| parser.add_argument('--num_counterfactuals', type=int, default=2, | |
| help='Number of counterfactual variants per scene') | |
| parser.add_argument('--blender_path', type=str, default=None, | |
| help='Path to Blender executable (auto-detected if not provided)') | |
| parser.add_argument('--output_dir', type=str, default='output', | |
| help='Base directory for all runs') | |
| parser.add_argument('--run_name', type=str, default=None, | |
| help='Optional custom name for this run') | |
| parser.add_argument('--resume', action='store_true', | |
| help='Resume from last checkpoint (requires --run_name)') | |
| parser.add_argument('--cf_types', nargs='+', | |
| choices=[ | |
| 'change_color', 'change_shape', 'change_size', | |
| 'change_material', 'change_position', | |
| 'add_object', 'remove_object', 'swap_attribute', 'occlusion_change', 'relational_flip', | |
| 'replace_object', | |
| 'change_background', | |
| 'change_lighting', 'add_noise', | |
| 'apply_fisheye', 'apply_blur', 'apply_vignette', 'apply_chromatic_aberration' | |
| ], | |
| help='Specific counterfactual types to use') | |
| parser.add_argument('--semantic_only', action='store_true', | |
| help='Generate only Semantic/Image counterfactuals (no Negative CFs)') | |
| parser.add_argument('--negative_only', action='store_true', | |
| help='Generate only Negative counterfactuals (no Semantic CFs)') | |
| parser.add_argument('--same_cf_type', action='store_true', | |
| help='Use the same counterfactual type for all variants') | |
| parser.add_argument('--min_cf_change_score', type=float, default=1.0, | |
| help='Minimum heuristic change score for counterfactuals') | |
| parser.add_argument('--max_cf_attempts', type=int, default=10, | |
| help='Max retries per counterfactual to meet --min_cf_change_score') | |
| parser.add_argument('--min_noise_level', type=str, default='light', | |
| choices=['light', 'medium', 'heavy'], | |
| help='Minimum noise level when using add_noise counterfactual') | |
| parser.add_argument('--list_cf_types', action='store_true', | |
| help='List all available counterfactual types and exit') | |
| args = parser.parse_args() | |
| if args.list_cf_types: | |
| list_counterfactual_types() | |
| return | |
| if args.resume and not args.run_name: | |
| print("ERROR: --run_name is required when using --resume") | |
| return | |
| blender_path = args.blender_path or find_blender() | |
| print(f"Using Blender: {blender_path}") | |
| print("\nPreparing scripts...") | |
| create_patched_render_script() | |
| run_dir = create_run_directory(args.output_dir, args.run_name) | |
| temp_run_id = os.path.basename(run_dir) | |
| print(f"\n{'='*70}") | |
| print(f"RUN DIRECTORY: {run_dir}") | |
| print(f"{'='*70}") | |
| scenes_dir = os.path.join(run_dir, 'scenes') | |
| os.makedirs(scenes_dir, exist_ok=True) | |
| checkpoint_file = os.path.join(run_dir, 'checkpoint.json') | |
| completed_scenes = set() | |
| if args.resume: | |
| completed_scenes = load_checkpoint(checkpoint_file) | |
| existing_scenes = get_completed_scenes_from_folder(scenes_dir) | |
| completed_scenes.update(existing_scenes) | |
| if completed_scenes: | |
| print(f"\n[RESUME] Found {len(completed_scenes)} already completed scenes") | |
| else: | |
| print("\n[WARNING] Resume flag set but no checkpoint found, starting fresh") | |
| print("\n" + "="*70) | |
| print(f"GENERATING {args.num_scenes} SCENE SETS (JSON ONLY)") | |
| print(f"Each with {args.num_counterfactuals} counterfactual variants") | |
| print("="*70) | |
| successful_scenes = 0 | |
| for i in range(args.num_scenes): | |
| if i in completed_scenes: | |
| print(f"\n[SKIP] Skipping scene {i} (already completed)") | |
| successful_scenes += 1 | |
| continue | |
| print(f"\n{'='*70}") | |
| print(f"SCENE SET {i+1}/{args.num_scenes} (Scene #{i})") | |
| print(f"{'='*70}") | |
| if args.num_objects is not None: | |
| num_objects = args.num_objects | |
| else: | |
| num_objects = random.randint(args.min_objects, args.max_objects) | |
| base_scene = None | |
| for retry in range(3): | |
| base_scene = generate_base_scene(num_objects, blender_path, i, temp_run_dir=temp_run_id) | |
| if base_scene and len(base_scene['objects']) > 0: | |
| break | |
| print(f" Retry {retry + 1}/3...") | |
| if not base_scene or len(base_scene['objects']) == 0: | |
| print(f" [FAILED] Failed to generate scene {i+1}") | |
| continue | |
| successful_scenes += 1 | |
| print(f" Creating {args.num_counterfactuals} counterfactuals...") | |
| counterfactuals = generate_counterfactuals( | |
| base_scene, | |
| args.num_counterfactuals, | |
| cf_types=args.cf_types, | |
| same_cf_type=args.same_cf_type, | |
| min_change_score=args.min_cf_change_score, | |
| max_cf_attempts=args.max_cf_attempts, | |
| min_noise_level=args.min_noise_level, | |
| semantic_only=args.semantic_only, | |
| negative_only=args.negative_only | |
| ) | |
| for idx, cf in enumerate(counterfactuals): | |
| cf_cat = cf.get('cf_category', 'unknown') | |
| print(f" CF{idx+1} [{cf_cat}] ({cf['type']}): {cf['description']}") | |
| scene_num = i + 1 | |
| scene_prefix = f"scene_{scene_num:04d}" | |
| scene_paths = {'original': os.path.join(scenes_dir, f"{scene_prefix}_original.json")} | |
| base_scene['cf_metadata'] = { | |
| 'variant': 'original', | |
| 'is_counterfactual': False, | |
| 'cf_index': None, | |
| 'cf_category': 'original', | |
| 'cf_type': None, | |
| 'cf_description': None, | |
| 'source_scene': scene_prefix, | |
| } | |
| save_scene(base_scene, scene_paths['original']) | |
| for idx, cf in enumerate(counterfactuals): | |
| cf_name = f"cf{idx+1}" | |
| scene_paths[cf_name] = os.path.join(scenes_dir, f"{scene_prefix}_{cf_name}.json") | |
| cf_scene = cf['scene'] | |
| cf_scene['cf_metadata'] = { | |
| 'variant': cf_name, | |
| 'is_counterfactual': True, | |
| 'cf_index': idx + 1, | |
| 'cf_category': cf.get('cf_category', 'unknown'), | |
| 'cf_type': cf.get('type', None), | |
| 'cf_description': cf.get('description', None), | |
| 'change_score': cf.get('change_score', None), | |
| 'change_attempts': cf.get('change_attempts', None), | |
| 'source_scene': scene_prefix, | |
| } | |
| save_scene(cf_scene, scene_paths[cf_name]) | |
| print(f" [OK] Saved {len(counterfactuals) + 1} scene files") | |
| completed_scenes.add(i) | |
| save_checkpoint(checkpoint_file, list(completed_scenes)) | |
| metadata = { | |
| 'timestamp': datetime.now().isoformat(), | |
| 'num_scenes': args.num_scenes, | |
| 'num_counterfactuals': args.num_counterfactuals, | |
| 'successful_scenes': successful_scenes, | |
| 'successful_renders': 0, | |
| 'cf_types': args.cf_types if args.cf_types else 'default', | |
| 'semantic_only': args.semantic_only, | |
| 'negative_only': args.negative_only, | |
| } | |
| metadata_path = os.path.join(run_dir, 'run_metadata.json') | |
| with open(metadata_path, 'w') as f: | |
| json.dump(metadata, f, indent=2) | |
| temp_run_path = os.path.join(os.getcwd(), 'temp_output', temp_run_id) | |
| if os.path.exists(temp_run_path): | |
| shutil.rmtree(temp_run_path) | |
| if os.path.exists('render_images_patched.py'): | |
| os.remove('render_images_patched.py') | |
| print("\n" + "="*70) | |
| print("SCENE COMPLETE") | |
| print("="*70) | |
| print(f"Run directory: {run_dir}") | |
| print(f"Successfully generated: {successful_scenes}/{args.num_scenes} scene sets") | |
| print(f"\nOutput:") | |
| print(f" Scene files: {scenes_dir}/") | |
| print(f" Metadata: {metadata_path}") | |
| print(f" Checkpoint: {checkpoint_file}") | |
| print(f"\nNext step: Run 'python pipeline.py --render_only --run_name {args.run_name}' to render these scenes") | |
| print("="*70) | |
| if __name__ == '__main__': | |
| main() | |