File size: 10,668 Bytes
a12c07f | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 | # TODO: Implement Chamfer for 3D tasks
# Input modules
import os
import sys
import subprocess
import argparse
import time
import json
from PIL import Image
from utils import photometric_loss, img2img_clip_similarity, blender_step, clip_similarity
from tqdm import tqdm
task_instance_count_dict = {
'geometry': 45,
'material': 40,
'blendshape': 75,
'placement': 40,
'lighting': 40
}
if __name__=='__main__':
parser = argparse.ArgumentParser(description='Image-based program edits')
parser.add_argument('--inference_metadata_saved_path',
type=str,
help="Path to the inference metadata in json format (paths of proposal edit scripts, winner information, etc.)"
)
parser.add_argument('--eval_render_save_dir',
type=str, default=None,
help="The directory that all evaluation renders will be saved to.."
)
parser.add_argument('--infinigen_installation_path',
type=str, default=f"{os.path.abspath('infinigen/blender/blender')}",
help="The installation path of blender executable file. It's `infinigen/blender/blender` by default."
)
# parse, save, and validate the args
args = parser.parse_args()
inference_metadata_saved_path = args.inference_metadata_saved_path
eval_render_save_dir = args.eval_render_save_dir
infinigen_installation_path = args.infinigen_installation_path
blender_render_script_path = "bench_data/all_render_script.py"
if not os.path.isfile(inference_metadata_saved_path):
raise ValueError(f'Invalid input for --inference_metadata_saved_path: {inference_metadata_saved_path}.')
# Load in the data from pipeline inference
with open(inference_metadata_saved_path, 'r') as file:
inference_metadata = json.load(file)
# Derive name for eval_render_save_dir
if not eval_render_save_dir:
eval_render_save_dir = f"eval_renders/{inference_metadata['output_dir_name']}"
os.makedirs(eval_render_save_dir, exist_ok=True)
tasks = inference_metadata.keys()
# Create eval renders for all instances
scores_across_tasks = {}
intermediates = {}
for task in tasks:
if task not in task_instance_count_dict.keys():
continue
# Iterate through each instance
scores_across_instances = {'best_n_clip':[], 'selected_n_clip':[], 'best_pl':[], 'selected_pl':[]}
for task_instance, instance_info in inference_metadata[task].items():
task_instance_dir = os.path.join(eval_render_save_dir, task_instance)
os.makedirs(task_instance_dir, exist_ok=True)
# Store local scores: score for each executable render
task_instance_scores = {}
try:
# Iterate through each proposal_renders_path
blender_file_path = instance_info['blender_file_path']
start_file_path = instance_info['start_script_path']
goal_file_path = instance_info['goal_script_path']
except:
continue
executable_proposal_names = []
for proposal_path in (instance_info['proposal_edits_paths'] + [start_file_path, goal_file_path]):
# Render the images for that proposal_renders_path
proposal_name = os.path.basename(proposal_path).split('.')[0] # Extract the name of py file, without suffix
proposal_renders_dir = os.path.join(task_instance_dir, proposal_name)
# Render images. "executable" checks whether the proposal is executable in Blender-Python API.
if not os.path.exists(proposal_renders_dir) or not os.listdir(proposal_renders_dir):
try:
executable = blender_step(infinigen_installation_path, blender_file_path, blender_render_script_path, proposal_path, proposal_renders_dir, merge_all_renders=False, replace_if_overlap=True)
except:
continue
if executable:
executable_proposal_names.append((proposal_renders_dir,proposal_name))
else:
executable_proposal_names.append((proposal_renders_dir,proposal_name))
# Loop through each executable proposal to compute their scores
for proposal_renders_dir, proposal_name in tqdm(executable_proposal_names):
if proposal_name == 'goal':
continue
task_instance_scores[proposal_name] = {}
n_clip_views = []
pl_views = []
for render_name in os.listdir(proposal_renders_dir):
task_instance_scores[proposal_name][render_name] = {}
# Get path for render
try:
proposal_render = Image.open(os.path.join(proposal_renders_dir, render_name))
gt_render = Image.open(os.path.join(task_instance_dir, 'goal', render_name))
except:
continue
# Compute n_clip and pl
n_clip = float(1 - clip_similarity(proposal_render, gt_render))
pl = float(photometric_loss(proposal_render, gt_render))
# Aggregate scores across all views for a proposal edit to compute average
n_clip_views.append(n_clip)
pl_views.append(pl)
# Record scores for this render
task_instance_scores[proposal_name][render_name]['n_clip'] = n_clip
task_instance_scores[proposal_name][render_name]['pl'] = pl
# Compute average n_clip for this proposal
if n_clip_views:
average_n_clip_views = sum(n_clip_views) / len(n_clip_views)
# Compute average pl for this task instance
if pl_views:
average_pl_views = sum(pl_views) / len(pl_views)
# Record average scores for a proposal
task_instance_scores[proposal_name]['avg_n_clip'] = average_n_clip_views
task_instance_scores[proposal_name]['avg_pl'] = average_pl_views
# Save the local scores to the task_instance dir
task_instance_scores_path = os.path.join(task_instance_dir, 'scores.json')
with open(task_instance_scores_path, 'w') as file:
json.dump(task_instance_scores, file, indent=4)
# Extract best scores and record them
best_n_clip_proposal_name = min(task_instance_scores, key=lambda proposal_name: task_instance_scores[proposal_name]['avg_n_clip'])
best_pl_proposal_name = min(task_instance_scores, key=lambda proposal_name: task_instance_scores[proposal_name]['avg_pl'])
best_n_clip = task_instance_scores[best_n_clip_proposal_name]['avg_n_clip']
best_pl = task_instance_scores[best_pl_proposal_name]['avg_pl']
task_instance_scores['best_n_clip'] = (best_n_clip_proposal_name, best_n_clip)
task_instance_scores['best_pl'] = (best_pl_proposal_name, best_pl)
# Register this instance to the scores across this task
scores_across_instances['best_n_clip'].append(best_n_clip)
scores_across_instances['best_pl'].append(best_pl)
# Handle selected edit if applicable
selected_proposal_path = instance_info['selected_edit_path']
if selected_proposal_path:
selected_proposal_name = os.path.basename(selected_proposal_path).split('.')[0]
try:
selectd_n_clip = task_instance_scores[selected_proposal_name]['avg_n_clip']
selected_pl = task_instance_scores[selected_proposal_name]['avg_pl']
task_instance_scores['selected_scores'] = (selected_proposal_name, {'avg_n_clip':selectd_n_clip, 'avg_pl':selected_pl})
except:
continue
# Register this instance to the scores across this task
scores_across_instances["selected_n_clip"].append(selectd_n_clip)
scores_across_instances["selected_pl"].append(selected_pl)
# Save the local scores to the task_instance dir
task_instance_scores_path = os.path.join(task_instance_dir, 'scores.json')
with open(task_instance_scores_path, 'w') as file:
json.dump(task_instance_scores, file, indent=4)
scores_across_instances_path = os.path.join(eval_render_save_dir, f'{task}_scores.json',)
with open(scores_across_instances_path, 'w') as file:
json.dump(scores_across_instances, file, indent=4)
# If the model cannot provide any edit for more than 75%
if len(scores_across_instances['best_n_clip']) < (len(inference_metadata[task]) * 0.25) :
scores_across_tasks[task] = {}
# If VLM system doesn't support selection
elif not scores_across_instances["selected_n_clip"]:
scores_across_tasks[task] = {
'best_n_clip': sum(scores_across_instances['best_n_clip']) / len(scores_across_instances['best_n_clip']),
'best_pl': sum(scores_across_instances['best_pl']) / len(scores_across_instances['best_pl']),
}
else:
scores_across_tasks[task] = {
'best_n_clip': sum(scores_across_instances['best_n_clip']) / len(scores_across_instances['best_n_clip']),
'best_pl': sum(scores_across_instances['best_pl']) / len(scores_across_instances['best_pl']),
'selected_n_clip': sum(scores_across_instances['selected_n_clip']) / len(scores_across_instances['selected_n_clip']),
'selected_pl': sum(scores_across_instances['selected_pl']) / len(scores_across_instances['selected_pl']),
}
intermediates[task] = scores_across_instances
scores_across_tasks_path = os.path.join(eval_render_save_dir, 'overall_scores.json',)
with open(scores_across_tasks_path, 'w') as file:
json.dump(scores_across_tasks, file, indent=4)
scores_across_instances_path = os.path.join(eval_render_save_dir, 'intermediate_scores.json',)
with open(scores_across_instances_path, 'w') as file:
json.dump(intermediates, file, indent=4)
# Compute Chamfer Distance for 3D-related tasks
|