File size: 12,011 Bytes
fea6c63
dbd36d0
fea6c63
dbd36d0
 
929719e
fea6c63
 
dbd36d0
 
 
 
 
70351dc
 
dabc6db
 
70351dc
 
fea6c63
 
 
 
 
70351dc
4da5f7b
 
 
fea6c63
70351dc
 
 
 
 
6a26852
70351dc
 
 
fea6c63
dbd36d0
 
 
fea6c63
dbd36d0
70351dc
 
 
 
dbd36d0
 
70351dc
 
 
dbd36d0
70351dc
dbd36d0
70351dc
 
 
 
dbd36d0
 
70351dc
 
dbd36d0
 
 
70351dc
 
dbd36d0
70351dc
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
dbd36d0
70351dc
dbd36d0
70351dc
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
dbd36d0
6a26852
dbd36d0
 
70351dc
 
fea6c63
 
2940ad4
 
 
 
 
 
6a26852
2940ad4
 
937f233
 
2940ad4
 
 
 
937f233
2940ad4
dd3858d
70351dc
dd3858d
 
70351dc
 
558835b
 
 
 
 
 
 
 
 
dd3858d
70351dc
4e8ae89
 
 
 
fea6c63
558835b
 
70351dc
558835b
 
70351dc
33a4bc0
dd3858d
 
 
 
9d3e3dd
 
 
937f233
 
558835b
9d3e3dd
dd3858d
 
558835b
dd3858d
 
2940ad4
 
937f233
 
558835b
2940ad4
fea6c63
4e8ae89
70351dc
dd3858d
7fb1eb0
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
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
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
import datetime
from pathlib import Path
import random
import gradio as gr
import os
import json
import firebase_admin
from firebase_admin import db, credentials

##################################################################
# Constants
##################################################################

# Constants for video display
VIDEO_FOLDERS = {
    "ours": "./userstudy_200_ours/",
    "omni": "./userstudy_200_omni/"
}
NUMBER_OF_EXPERIMENTS = 200  # Assuming sample0.mp4 to sample199.mp4

#################################################################################################################################################
# Authentication
#################################################################################################################################################

# Read secret API key and other configurations from environment variables
FIREBASE_API_KEY = os.environ.get('FirebaseSecret')
FIREBASE_URL = os.environ.get('FirebaseURL')
DATASET = os.environ.get('Dataset')

# Initialize Firebase service
try:
    firebase_creds = credentials.Certificate(json.loads(FIREBASE_API_KEY))
    firebase_app = firebase_admin.initialize_app(firebase_creds, {'databaseURL': FIREBASE_URL})
    firebase_data_ref = db.reference("data")
    print("Firebase initialized successfully.")
except Exception as e:
    print(f"Failed to initialize Firebase: {e}")
    raise e

##################################################################
# Data Layer
##################################################################

class Experiment(dict):
    """
    Represents an experiment consisting of two videos and the user's selection.
    """
    def __init__(self, dataset, video_left, video_right, selected_video=None):
        super().__init__(
            dataset=dataset,
            video_left=video_left,
            video_right=video_right,
            selected_video=selected_video,
        )

def experiment_to_dict(experiment, skip=False):
    """
    Converts the Experiment object to a dictionary for Firebase storage.
    Includes whether the selected video is from 'omni' or 'ours' folder.
    """
    info = {
        "dataset": experiment["dataset"],
        "video_left": experiment["video_left"],
        "video_right": experiment["video_right"],
    }

    if skip:
        info["selected_video"] = "None"
        info["Control"] = "None"
    else:
        info["selected_video"] = experiment["selected_video"]

        # Determine "Control" based on which video was selected
        if experiment["selected_video"] == "left":
            selected_video_path = Path(experiment["video_left"])
        elif experiment["selected_video"] == "right":
            selected_video_path = Path(experiment["video_right"])
        else:
            selected_video_path = None

        if selected_video_path:
            # Check the parent folder name to determine the source
            if "userstudy_200_omni" in selected_video_path.parts:
                info["Control"] = "OmniControl"
            elif "userstudy_200_ours" in selected_video_path.parts:
                info["Control"] = "Ours"
            else:
                info["Control"] = "Unknown"
        else:
            info["Control"] = "Unknown"

    info["timestamp"] = datetime.datetime.now().strftime("%Y-%m-%d %H:%M:%S")

    return info

def generate_new_experiment() -> Experiment:
    """
    Generates a new experiment by randomly selecting a sample number and fetching corresponding videos from both folders.
    The positions of the videos (left/right) are randomized.
    """
    # Randomly select an integer between 0 and 199 inclusive
    random_int = random.randint(0, NUMBER_OF_EXPERIMENTS - 1)
    video_name = f"sample{random_int}.mp4"

    # Paths to the two videos
    video_ours_path = Path(VIDEO_FOLDERS["ours"]) / video_name
    video_omni_path = Path(VIDEO_FOLDERS["omni"]) / video_name

    # Ensure that both videos exist
    if not video_ours_path.exists() or not video_omni_path.exists():
        raise FileNotFoundError(f"One or both video files not found: {video_ours_path}, {video_omni_path}")

    # Randomly decide the order (left/right)
    if random.choice([True, False]):
        video_left, video_right = video_ours_path, video_omni_path
    else:
        video_left, video_right = video_omni_path, video_ours_path

    print(f"Generated new experiment with left video: {video_left}, right video: {video_right}")
    return Experiment(
        DATASET,
        str(video_left),
        str(video_right),
    )

def load_initial():
    """
    Initializes the first experiment on app load.
    """
    try:
        new_experiment = generate_new_experiment()
        print("Initial experiment loaded.")
        return [
            new_experiment,
            gr.update(value=new_experiment["video_left"]),
            gr.update(value=new_experiment["video_right"]),
            ""  # Empty message
        ]
    except Exception as e:
        print(f"Error loading initial experiment: {e}")
        return [None, gr.update(value=""), gr.update(value=""), "❌ Failed to load initial videos ❌"]

def select_and_load(selected_label, experiment, message_component):
    """
    Handles the selection of a video by the user.
    Saves the selection and loads the next experiment.
    """
    try:
        # Update the experiment's selected video
        if selected_label.lower() == "left":
            experiment["selected_video"] = "left"
        elif selected_label.lower() == "right":
            experiment["selected_video"] = "right"
        else:
            experiment["selected_video"] = None  # For safety

        # Save the current selection to Firebase
        dict_to_save = experiment_to_dict(experiment, skip=False)
        firebase_data_ref.push(dict_to_save)

        print("=====================")
        print(dict_to_save)
        print("=====================")

        # Set success message
        new_message = "✅ Your choice has been saved to Firebase ✅"
    except Exception as e:
        # Set error message
        new_message = f"❌ Failed to save your choice: {e} ❌"
        print(f"Error saving to Firebase: {e}")

    # Generate the next experiment
    try:
        new_experiment = generate_new_experiment()

        # Update the video components with new video paths
        return [
            new_experiment,
            gr.update(value=new_experiment["video_left"]),
            gr.update(value=new_experiment["video_right"]),
            new_message
        ]
    except Exception as e:
        # If generating a new experiment fails, notify the user
        new_message = f"❌ Failed to load new videos: {e} ❌"
        print(f"Error generating new experiment: {e}")
        # Keep the current experiment and videos
        return [
            experiment,
            gr.update(value=experiment["video_left"]),
            gr.update(value=experiment["video_right"]),
            new_message
        ]

def skip_experiment(experiment, message_component):
    """
    Handles the skipping of an experiment.
    Saves the skip and loads the next experiment.
    """
    try:
        # Set selected_video to "None" to indicate skip
        experiment["selected_video"] = "None"

        # Save the skip to Firebase
        dict_to_save = experiment_to_dict(experiment, skip=True)
        firebase_data_ref.push(dict_to_save)

        print("=====================")
        print(dict_to_save)
        print("=====================")

        # Set success message
        new_message = "✅ Your skip has been recorded ✅"
    except Exception as e:
        # Set error message
        new_message = f"❌ Failed to record your skip: {e} ❌"
        print(f"Error saving skip to Firebase: {e}")

    # Generate the next experiment
    try:
        new_experiment = generate_new_experiment()

        # Update the video components with new video paths
        return [
            new_experiment,
            gr.update(value=new_experiment["video_left"]),
            gr.update(value=new_experiment["video_right"]),
            new_message
        ]
    except Exception as e:
        # If generating a new experiment fails, notify the user
        new_message = f"❌ Failed to load new videos: {e} ❌"
        print(f"Error generating new experiment: {e}")
        # Keep the current experiment and videos
        return [
            experiment,
            gr.update(value=experiment["video_left"]),
            gr.update(value=experiment["video_right"]),
            new_message
        ]

##################################################################
# UI Layer
##################################################################

css = """
#padded {
    padding-left: 2%;
    padding-right: 2%;
}
.select-button {
    margin-top: 10px;
    width: 100%;
}
.select-button:hover {
    background-color: #00c0ff;
    color: white;
}
.video-container {
    display: flex;
    justify-content: center;
    align-items: center;
}
video {
    width: 100%;
    height: auto;
}
"""

with gr.Blocks(title="Unsupervised Video Editing", css=css) as demo:
    # Initialize the state
    experiment = gr.State()

    with gr.Row(elem_id="padded"):
        with gr.Column(scale=3, elem_id="padded"):
            gr.Markdown("<div style='width: 100%'><h1 style='text-align: center;'>Choose Your Preferred Video</h1></div>")
            gr.Markdown("<div style='width: 100%'><h3 style='text-align: center;'>Select the video you prefer.<br/>⚠️Consider fidelity and quality⚠️</h3></div>")
            btn_skip = gr.Button("I have no preference")
            message_component = gr.Markdown("")  # For displaying messages

    with gr.Row():
        # Define both video components first
        with gr.Column(scale=1):
            video_left_component = gr.Video(
                label="left",
                elem_id="video-left",
                show_label=False,
                show_download_button=False,
                show_share_button=False,
                interactive=True
            )
        with gr.Column(scale=1):
            video_right_component = gr.Video(
                label="right",
                elem_id="video-right",
                show_label=False,
                show_download_button=False,
                show_share_button=False,
                interactive=True
            )

    with gr.Row():
        # Add Select buttons below each video
        with gr.Column(scale=1):
            select_left = gr.Button("Select Left Video", elem_id="select-button-left", variant="primary")
        with gr.Column(scale=1):
            select_right = gr.Button("Select Right Video", elem_id="select-button-right", variant="primary")

    with gr.Row():
        # Configure the skip button
        btn_skip.click(
            fn=skip_experiment,
            inputs=[experiment, message_component],
            outputs=[experiment, video_left_component, video_right_component, message_component],
            queue=True,
            show_progress=True
        )

    # Configure the select buttons to handle selection and load next experiment
    select_left.click(
        fn=lambda exp, msg: select_and_load("left", exp, msg),
        inputs=[experiment, message_component],
        outputs=[experiment, video_left_component, video_right_component, message_component],
        queue=True,
        show_progress=True
    )
    select_right.click(
        fn=lambda exp, msg: select_and_load("right", exp, msg),
        inputs=[experiment, message_component],
        outputs=[experiment, video_left_component, video_right_component, message_component],
        queue=True,
        show_progress=True
    )

    # Load the first experiment on app startup
    demo.load(
        fn=load_initial,
        inputs=None,
        outputs=[experiment, video_left_component, video_right_component, message_component]
    )

# Launch the app with share=True to create a public link
demo.launch(share=True)