AnonymousECCV15285's picture
Upload 30 files
e266831 verified
#!/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()