Spaces:
Sleeping
Sleeping
Newbyl
commited on
Commit
·
50ba3db
1
Parent(s):
7b27a72
modif
Browse files
app.py
ADDED
|
@@ -0,0 +1,275 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
import os
|
| 2 |
+
import gradio as gr
|
| 3 |
+
import random
|
| 4 |
+
import json
|
| 5 |
+
import itertools
|
| 6 |
+
from pathlib import Path
|
| 7 |
+
|
| 8 |
+
# List of methods that already have results
|
| 9 |
+
EXISTING_METHODS = ["Liveportrait", "ControlTalk", "Lia", "Hallo2", "Echomimic", "Dimitra", "Sadtalker", "Wav2Lip"]
|
| 10 |
+
|
| 11 |
+
# List of new methods to compare
|
| 12 |
+
NEW_METHODS = ["First-Order-Motion", "X-Portrait", "MCNET", "EMOPortrait", "DaGAN", "LIA-X", "OmniAvatar", "Real3DPortrait"]
|
| 13 |
+
|
| 14 |
+
# Number of videos per new method to compare
|
| 15 |
+
VIDEOS_PER_NEW_METHOD = 23
|
| 16 |
+
|
| 17 |
+
# File to save results
|
| 18 |
+
RESULTS_FILE = "comparison_results.json"
|
| 19 |
+
|
| 20 |
+
def find_videos_by_method(base_dir):
|
| 21 |
+
"""Find all videos in the method folders."""
|
| 22 |
+
videos_by_method = {}
|
| 23 |
+
|
| 24 |
+
# Check each method directory
|
| 25 |
+
for method in EXISTING_METHODS + NEW_METHODS:
|
| 26 |
+
method_dir = os.path.join(base_dir, method)
|
| 27 |
+
if not os.path.exists(method_dir):
|
| 28 |
+
continue
|
| 29 |
+
|
| 30 |
+
videos = []
|
| 31 |
+
# Walk through the directory structure to find all video files
|
| 32 |
+
for root, _, files in os.walk(method_dir):
|
| 33 |
+
for file in files:
|
| 34 |
+
if file.lower().endswith(('.mp4', '.avi', '.mov', '.mkv')):
|
| 35 |
+
videos.append(os.path.join(root, file))
|
| 36 |
+
|
| 37 |
+
if videos:
|
| 38 |
+
videos_by_method[method] = videos
|
| 39 |
+
|
| 40 |
+
return videos_by_method
|
| 41 |
+
|
| 42 |
+
def generate_comparison_pairs(videos_by_method):
|
| 43 |
+
"""Generate pairs of videos between new methods and existing methods."""
|
| 44 |
+
pairs = []
|
| 45 |
+
|
| 46 |
+
for new_method in NEW_METHODS:
|
| 47 |
+
if new_method not in videos_by_method:
|
| 48 |
+
continue
|
| 49 |
+
|
| 50 |
+
for existing_method in EXISTING_METHODS:
|
| 51 |
+
if existing_method not in videos_by_method:
|
| 52 |
+
continue
|
| 53 |
+
|
| 54 |
+
# Get video basenames to match videos across methods
|
| 55 |
+
new_method_videos = {os.path.basename(v): v for v in videos_by_method[new_method]}
|
| 56 |
+
existing_method_videos = {os.path.basename(v): v for v in videos_by_method[existing_method]}
|
| 57 |
+
|
| 58 |
+
# Find common videos between the two methods
|
| 59 |
+
common_videos = set(new_method_videos.keys()) & set(existing_method_videos.keys())
|
| 60 |
+
|
| 61 |
+
for video_name in common_videos:
|
| 62 |
+
new_video = new_method_videos[video_name]
|
| 63 |
+
existing_video = existing_method_videos[video_name]
|
| 64 |
+
|
| 65 |
+
# Randomly order the videos (left/right)
|
| 66 |
+
if random.choice([True, False]):
|
| 67 |
+
pairs.append({
|
| 68 |
+
'left_video': new_video,
|
| 69 |
+
'right_video': existing_video,
|
| 70 |
+
'left_method': new_method,
|
| 71 |
+
'right_method': existing_method,
|
| 72 |
+
'video_name': video_name
|
| 73 |
+
})
|
| 74 |
+
else:
|
| 75 |
+
pairs.append({
|
| 76 |
+
'left_video': existing_video,
|
| 77 |
+
'right_video': new_video,
|
| 78 |
+
'left_method': existing_method,
|
| 79 |
+
'right_method': new_method,
|
| 80 |
+
'video_name': video_name
|
| 81 |
+
})
|
| 82 |
+
|
| 83 |
+
# Shuffle pairs for unbiased comparison
|
| 84 |
+
random.shuffle(pairs)
|
| 85 |
+
return pairs
|
| 86 |
+
|
| 87 |
+
def load_results():
|
| 88 |
+
"""Load existing comparison results."""
|
| 89 |
+
if os.path.exists(RESULTS_FILE):
|
| 90 |
+
try:
|
| 91 |
+
with open(RESULTS_FILE, 'r') as f:
|
| 92 |
+
return json.load(f)
|
| 93 |
+
except json.JSONDecodeError:
|
| 94 |
+
pass
|
| 95 |
+
return {"comparisons": [], "count": 0}
|
| 96 |
+
|
| 97 |
+
def save_result(results, comparison_data):
|
| 98 |
+
"""Save the comparison result."""
|
| 99 |
+
results["comparisons"].append(comparison_data)
|
| 100 |
+
results["count"] += 1
|
| 101 |
+
|
| 102 |
+
with open(RESULTS_FILE, 'w') as f:
|
| 103 |
+
json.dump(results, f, indent=4)
|
| 104 |
+
|
| 105 |
+
return results
|
| 106 |
+
|
| 107 |
+
def get_total_expected_comparisons():
|
| 108 |
+
"""Calculate the total number of comparisons needed."""
|
| 109 |
+
return len(NEW_METHODS) * VIDEOS_PER_NEW_METHOD
|
| 110 |
+
|
| 111 |
+
def create_app(videos_dir):
|
| 112 |
+
"""Create the Gradio app for video comparison."""
|
| 113 |
+
# Load existing results if any
|
| 114 |
+
results = load_results()
|
| 115 |
+
|
| 116 |
+
# Find videos in the directory structure
|
| 117 |
+
videos_by_method = find_videos_by_method(videos_dir)
|
| 118 |
+
|
| 119 |
+
# Generate pairs for comparison
|
| 120 |
+
all_pairs = generate_comparison_pairs(videos_by_method)
|
| 121 |
+
|
| 122 |
+
# Calculate the total expected comparisons
|
| 123 |
+
total_expected = get_total_expected_comparisons()
|
| 124 |
+
|
| 125 |
+
# Track the current state
|
| 126 |
+
state = {
|
| 127 |
+
"current_pair_index": 0,
|
| 128 |
+
"results": results,
|
| 129 |
+
"all_pairs": all_pairs,
|
| 130 |
+
"total_expected": total_expected
|
| 131 |
+
}
|
| 132 |
+
|
| 133 |
+
def update_ui_state(state):
|
| 134 |
+
"""Update the UI based on current state."""
|
| 135 |
+
if state["results"]["count"] >= state["total_expected"]:
|
| 136 |
+
return {
|
| 137 |
+
"left_video": None,
|
| 138 |
+
"right_video": None,
|
| 139 |
+
"status": f"Completed all {state['total_expected']} comparisons!",
|
| 140 |
+
"left_button_visible": False,
|
| 141 |
+
"right_button_visible": False,
|
| 142 |
+
"done_visible": True,
|
| 143 |
+
"progress": 100
|
| 144 |
+
}
|
| 145 |
+
|
| 146 |
+
if state["current_pair_index"] >= len(state["all_pairs"]):
|
| 147 |
+
return {
|
| 148 |
+
"left_video": None,
|
| 149 |
+
"right_video": None,
|
| 150 |
+
"status": "No more pairs to compare, but target count not reached.",
|
| 151 |
+
"left_button_visible": False,
|
| 152 |
+
"right_button_visible": False,
|
| 153 |
+
"done_visible": True,
|
| 154 |
+
"progress": (state["results"]["count"] / state["total_expected"]) * 100
|
| 155 |
+
}
|
| 156 |
+
|
| 157 |
+
current_pair = state["all_pairs"][state["current_pair_index"]]
|
| 158 |
+
progress_percent = (state["results"]["count"] / state["total_expected"]) * 100
|
| 159 |
+
|
| 160 |
+
return {
|
| 161 |
+
"left_video": current_pair["left_video"],
|
| 162 |
+
"right_video": current_pair["right_video"],
|
| 163 |
+
"status": f"Compare videos: {state['results']['count']}/{state['total_expected']} completed ({progress_percent:.1f}%)",
|
| 164 |
+
"left_button_visible": True,
|
| 165 |
+
"right_button_visible": True,
|
| 166 |
+
"done_visible": False,
|
| 167 |
+
"progress": progress_percent
|
| 168 |
+
}
|
| 169 |
+
|
| 170 |
+
def handle_choice(choice, state):
|
| 171 |
+
"""Handle user's choice between videos."""
|
| 172 |
+
if state["results"]["count"] >= state["total_expected"]:
|
| 173 |
+
return state, update_ui_state(state)
|
| 174 |
+
|
| 175 |
+
if state["current_pair_index"] < len(state["all_pairs"]):
|
| 176 |
+
current_pair = state["all_pairs"][state["current_pair_index"]]
|
| 177 |
+
|
| 178 |
+
# Record the comparison result
|
| 179 |
+
comparison_data = {
|
| 180 |
+
"video_name": current_pair["video_name"],
|
| 181 |
+
"choice": choice,
|
| 182 |
+
"left_method": current_pair["left_method"],
|
| 183 |
+
"right_method": current_pair["right_method"],
|
| 184 |
+
"timestamp": str(import_datetime().now())
|
| 185 |
+
}
|
| 186 |
+
|
| 187 |
+
# Update results
|
| 188 |
+
state["results"] = save_result(state["results"], comparison_data)
|
| 189 |
+
state["current_pair_index"] += 1
|
| 190 |
+
|
| 191 |
+
return state, update_ui_state(state)
|
| 192 |
+
|
| 193 |
+
def import_datetime():
|
| 194 |
+
"""Import datetime module on demand."""
|
| 195 |
+
import datetime
|
| 196 |
+
return datetime
|
| 197 |
+
|
| 198 |
+
with gr.Blocks() as demo:
|
| 199 |
+
gr.Markdown("# Video Comparison App")
|
| 200 |
+
gr.Markdown("Compare the quality of videos generated by different methods")
|
| 201 |
+
|
| 202 |
+
# State components
|
| 203 |
+
state_data = gr.State(state)
|
| 204 |
+
|
| 205 |
+
# Display components
|
| 206 |
+
with gr.Row():
|
| 207 |
+
with gr.Column():
|
| 208 |
+
left_video = gr.Video(label="Video A")
|
| 209 |
+
left_button = gr.Button("Choose Video A", variant="primary")
|
| 210 |
+
with gr.Column():
|
| 211 |
+
right_video = gr.Video(label="Video B")
|
| 212 |
+
right_button = gr.Button("Choose Video B", variant="primary")
|
| 213 |
+
|
| 214 |
+
progress_bar = gr.Progress()
|
| 215 |
+
status_text = gr.Markdown("")
|
| 216 |
+
done_text = gr.Markdown("All comparisons completed! Thank you for your participation.", visible=False)
|
| 217 |
+
|
| 218 |
+
# Initialize UI with first pair
|
| 219 |
+
ui_state = update_ui_state(state)
|
| 220 |
+
left_video.value = ui_state["left_video"]
|
| 221 |
+
right_video.value = ui_state["right_video"]
|
| 222 |
+
status_text.value = ui_state["status"]
|
| 223 |
+
left_button.visible = ui_state["left_button_visible"]
|
| 224 |
+
right_button.visible = ui_state["right_button_visible"]
|
| 225 |
+
done_text.visible = ui_state["done_visible"]
|
| 226 |
+
|
| 227 |
+
# Button click handlers
|
| 228 |
+
def handle_left_click(state_data):
|
| 229 |
+
new_state, ui_state = handle_choice("left", state_data)
|
| 230 |
+
return [
|
| 231 |
+
new_state,
|
| 232 |
+
ui_state["left_video"],
|
| 233 |
+
ui_state["right_video"],
|
| 234 |
+
ui_state["status"],
|
| 235 |
+
ui_state["left_button_visible"],
|
| 236 |
+
ui_state["right_button_visible"],
|
| 237 |
+
ui_state["done_visible"],
|
| 238 |
+
ui_state["progress"]
|
| 239 |
+
]
|
| 240 |
+
|
| 241 |
+
def handle_right_click(state_data):
|
| 242 |
+
new_state, ui_state = handle_choice("right", state_data)
|
| 243 |
+
return [
|
| 244 |
+
new_state,
|
| 245 |
+
ui_state["left_video"],
|
| 246 |
+
ui_state["right_video"],
|
| 247 |
+
ui_state["status"],
|
| 248 |
+
ui_state["left_button_visible"],
|
| 249 |
+
ui_state["right_button_visible"],
|
| 250 |
+
ui_state["done_visible"],
|
| 251 |
+
ui_state["progress"]
|
| 252 |
+
]
|
| 253 |
+
|
| 254 |
+
left_button.click(
|
| 255 |
+
handle_left_click,
|
| 256 |
+
inputs=[state_data],
|
| 257 |
+
outputs=[state_data, left_video, right_video, status_text, left_button, right_button, done_text, progress_bar]
|
| 258 |
+
)
|
| 259 |
+
|
| 260 |
+
right_button.click(
|
| 261 |
+
handle_right_click,
|
| 262 |
+
inputs=[state_data],
|
| 263 |
+
outputs=[state_data, left_video, right_video, status_text, left_button, right_button, done_text, progress_bar]
|
| 264 |
+
)
|
| 265 |
+
|
| 266 |
+
return demo
|
| 267 |
+
|
| 268 |
+
if __name__ == "__main__":
|
| 269 |
+
# Set the directory containing the method folders
|
| 270 |
+
videos_directory = input("Enter the directory containing the method folders: ").strip()
|
| 271 |
+
if not videos_directory:
|
| 272 |
+
videos_directory = "." # Default to current directory
|
| 273 |
+
|
| 274 |
+
app = create_app(videos_directory)
|
| 275 |
+
app.launch()
|