Spaces:
Running
Running
Commit
·
d87f99a
1
Parent(s):
5b8460a
Improved UI
Browse files
app.py
CHANGED
|
@@ -38,55 +38,55 @@ def click_timer(is_checked, state):
|
|
| 38 |
state["timer_start"] = datetime.now().isoformat()
|
| 39 |
return state
|
| 40 |
|
| 41 |
-
def record_run(name, session_start,
|
| 42 |
-
"""
|
|
|
|
|
|
|
|
|
|
| 43 |
ws.append_row([
|
| 44 |
name,
|
| 45 |
-
session_start, # when
|
| 46 |
-
|
| 47 |
-
|
| 48 |
familiarity, # slider value
|
| 49 |
-
json.dumps(
|
| 50 |
-
json.dumps(
|
| 51 |
])
|
| 52 |
|
| 53 |
def get_new_grid(state=None):
|
| 54 |
-
|
| 55 |
-
|
| 56 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 57 |
# attempts = []
|
| 58 |
# wrongs = []
|
|
|
|
| 59 |
# else:
|
| 60 |
-
#
|
| 61 |
-
#
|
| 62 |
-
#
|
| 63 |
-
|
| 64 |
-
|
| 65 |
-
# 1)
|
| 66 |
-
|
| 67 |
-
|
| 68 |
-
|
| 69 |
-
|
| 70 |
-
|
| 71 |
-
|
| 72 |
-
|
| 73 |
-
|
| 74 |
-
|
| 75 |
-
|
| 76 |
-
|
| 77 |
-
|
| 78 |
-
|
| 79 |
-
|
| 80 |
-
real_files = [os.path.join(folder_real, f) for f in os.listdir(folder_real)
|
| 81 |
-
if f.lower().endswith(".png")]
|
| 82 |
-
t = random.randint(4, 12)
|
| 83 |
-
sel_ai = random.sample(ai_files, t)
|
| 84 |
-
sel_real = random.sample(real_files, 16 - t)
|
| 85 |
-
combined = [(p, False) for p in sel_ai] + [(p, True) for p in sel_real]
|
| 86 |
-
random.shuffle(combined)
|
| 87 |
-
|
| 88 |
-
paths = [p for p, _ in combined]
|
| 89 |
-
truth = [lbl for _, lbl in combined]
|
| 90 |
thumbs = [np.array(Image.open(p).convert("RGB")) for p in paths]
|
| 91 |
resets = [False] * 16
|
| 92 |
|
|
@@ -100,87 +100,67 @@ def get_new_grid(state=None):
|
|
| 100 |
# }
|
| 101 |
|
| 102 |
new_state = {
|
| 103 |
-
"attempts": attempts,
|
| 104 |
-
"wrongs": wrongs,
|
| 105 |
"paths": paths,
|
| 106 |
-
"truth": truth
|
|
|
|
| 107 |
}
|
| 108 |
-
#
|
| 109 |
-
if
|
| 110 |
-
new_state["timer_start"] =
|
| 111 |
|
| 112 |
-
# 4) Return images, resets, and the full state
|
| 113 |
return (*thumbs, *resets, new_state)
|
|
|
|
| 114 |
def evaluate(
|
| 115 |
sel0, sel1, sel2, sel3,
|
| 116 |
sel4, sel5, sel6, sel7,
|
| 117 |
sel8, sel9, sel10, sel11,
|
| 118 |
sel12, sel13, sel14, sel15,
|
| 119 |
-
state
|
|
|
|
|
|
|
| 120 |
):
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 121 |
sels = [sel0, sel1, sel2, sel3, sel4, sel5, sel6, sel7,
|
| 122 |
sel8, sel9, sel10, sel11, sel12, sel13, sel14, sel15]
|
| 123 |
-
paths = state["paths"]
|
| 124 |
-
truth = state["truth"]
|
| 125 |
bordered, wrong_list = [], []
|
| 126 |
correct_count = 0
|
| 127 |
|
| 128 |
for i, chosen in enumerate(sels):
|
| 129 |
-
img = Image.open(paths[i]).convert("RGB")
|
| 130 |
-
is_real = truth[i]
|
| 131 |
-
|
| 132 |
-
|
|
|
|
| 133 |
correct_count += 1
|
| 134 |
else:
|
| 135 |
-
wrong_list.append(os.path.basename(paths[i]))
|
| 136 |
-
color = "red"
|
| 137 |
bordered.append(np.array(ImageOps.expand(img, border=5, fill=color)))
|
| 138 |
|
| 139 |
-
#
|
| 140 |
-
|
| 141 |
-
state["attempts"].append(correct_count)
|
| 142 |
-
state["wrongs"].append(wrong_list)
|
| 143 |
-
|
| 144 |
-
return (*bordered, state)
|
| 145 |
-
|
| 146 |
-
def done_now(name, state, familiarity):
|
| 147 |
-
# only use the timer_start from the box click; if missing, elapsed = 0
|
| 148 |
-
timer_iso = state.get("timer_start")
|
| 149 |
-
if timer_iso is None:
|
| 150 |
-
elapsed = 0.0
|
| 151 |
-
timer_iso = datetime.now().isoformat()
|
| 152 |
-
else:
|
| 153 |
-
start = datetime.fromisoformat(timer_iso)
|
| 154 |
-
elapsed = (datetime.now() - start).total_seconds()
|
| 155 |
-
|
| 156 |
-
# if they never clicked Start or Submit, elapsed = 0; else from first interaction
|
| 157 |
timer_iso = state.get("timer_start")
|
| 158 |
-
if timer_iso
|
| 159 |
-
elapsed = (datetime.now() - datetime.fromisoformat(timer_iso)).total_seconds()
|
| 160 |
-
else:
|
| 161 |
-
elapsed = 0.0
|
| 162 |
-
timer_iso = datetime.now().isoformat()
|
| 163 |
|
| 164 |
-
#
|
| 165 |
-
|
| 166 |
-
name,
|
| 167 |
-
state.get("timer_start", datetime.now().isoformat()),
|
| 168 |
-
state["attempts"],
|
| 169 |
-
state["wrongs"],
|
| 170 |
-
elapsed,
|
| 171 |
-
familiarity
|
| 172 |
-
)
|
| 173 |
-
|
| 174 |
-
minutes = int(elapsed // 60)
|
| 175 |
-
seconds = int(elapsed % 60)
|
| 176 |
|
| 177 |
-
|
| 178 |
-
|
| 179 |
-
|
| 180 |
-
|
| 181 |
-
|
|
|
|
|
|
|
|
|
|
| 182 |
)
|
|
|
|
|
|
|
|
|
|
| 183 |
|
|
|
|
| 184 |
|
| 185 |
|
| 186 |
with gr.Blocks() as demo:
|
|
@@ -196,7 +176,7 @@ with gr.Blocks() as demo:
|
|
| 196 |
|
| 197 |
user_name = gr.Textbox(label="Your name", placeholder="e.g. Can be anonymous ex: 'Trisolaris123'")
|
| 198 |
|
| 199 |
-
gr.Markdown("# Instructions: Click the images you think are AI-generated. \
|
| 200 |
# Familiarity slider (1=first time, 10=expert)
|
| 201 |
familiarity = gr.Slider(
|
| 202 |
minimum=1, maximum=10, step=1, value=1,
|
|
@@ -227,30 +207,25 @@ with gr.Blocks() as demo:
|
|
| 227 |
cb.change(fn=click_timer, inputs=[cb, state], outputs=[state])
|
| 228 |
|
| 229 |
submit_btn = gr.Button("Submit")
|
| 230 |
-
|
| 231 |
-
|
| 232 |
-
done_out = gr.Textbox(interactive=False)
|
| 233 |
|
| 234 |
demo.load(
|
| 235 |
fn=get_new_grid,
|
| 236 |
inputs=[state],
|
| 237 |
outputs=[*image_components, *checkbox_components, state]
|
| 238 |
)
|
| 239 |
-
|
| 240 |
fn=get_new_grid,
|
| 241 |
inputs=[state],
|
| 242 |
outputs=[*image_components, *checkbox_components, state]
|
| 243 |
)
|
| 244 |
submit_btn.click(
|
| 245 |
fn=evaluate,
|
| 246 |
-
inputs=[*checkbox_components, state],
|
| 247 |
-
outputs=[*image_components, state]
|
| 248 |
-
)
|
| 249 |
-
done_btn.click(
|
| 250 |
-
fn=done_now,
|
| 251 |
-
inputs=[user_name, state, familiarity],
|
| 252 |
-
outputs=[done_out]
|
| 253 |
)
|
|
|
|
| 254 |
|
| 255 |
# Reveal main UI only after agreeing to terms
|
| 256 |
terms_cb.change(
|
|
|
|
| 38 |
state["timer_start"] = datetime.now().isoformat()
|
| 39 |
return state
|
| 40 |
|
| 41 |
+
def record_run(name, session_start, attempt_num, elapsed_sec, familiarity, correct_count, wrong_list):
|
| 42 |
+
"""
|
| 43 |
+
Append one row with exactly:
|
| 44 |
+
Name | StartTime | #Attempt | ElapsedSec | Familiarity | AttemptsList | WrongsList
|
| 45 |
+
"""
|
| 46 |
ws.append_row([
|
| 47 |
name,
|
| 48 |
+
session_start, # when that grid/timer started
|
| 49 |
+
attempt_num, # 1, 2, 3, …
|
| 50 |
+
elapsed_sec, # seconds for this attempt
|
| 51 |
familiarity, # slider value
|
| 52 |
+
json.dumps([correct_count]), # e.g. [9]
|
| 53 |
+
json.dumps(wrong_list) # e.g. ["217_1.png","243_2.png"]
|
| 54 |
])
|
| 55 |
|
| 56 |
def get_new_grid(state=None):
|
| 57 |
+
"""
|
| 58 |
+
Generate a fresh 4×4 grid:
|
| 59 |
+
- Preserve attempt_count
|
| 60 |
+
- **Reset** timer_start to "now" whenever the user hits Retry (i.e. state is not None)
|
| 61 |
+
- Leave timer_start unset on initial load (so they must click Start or first Fake?)
|
| 62 |
+
"""
|
| 63 |
+
|
| 64 |
+
# # 1) Initialize or preserve only attempts/wrongs and any existing timer
|
| 65 |
+
# if state is None or "attempts" not in state:
|
| 66 |
# attempts = []
|
| 67 |
# wrongs = []
|
| 68 |
+
# # do NOT set timer_start here—that only comes from the checkbox
|
| 69 |
# else:
|
| 70 |
+
# attempts = state["attempts"]
|
| 71 |
+
# wrongs = state["wrongs"]
|
| 72 |
+
# # preserve timer_start if previously set
|
| 73 |
+
# timer_start = state.get("timer_start") if state else None
|
| 74 |
+
|
| 75 |
+
# 1) Preserve attempt_count or start at 0
|
| 76 |
+
attempt_count = (state or {}).get("attempt_count", 0)
|
| 77 |
+
|
| 78 |
+
|
| 79 |
+
# 2) Sample & shuffle images as before
|
| 80 |
+
ai_files = [os.path.join(folder_ai, f) for f in os.listdir(folder_ai) if f.endswith(".png")]
|
| 81 |
+
real_files = [os.path.join(folder_real, f) for f in os.listdir(folder_real) if f.endswith(".png")]
|
| 82 |
+
t = random.randint(4, 12)
|
| 83 |
+
sel_ai = random.sample(ai_files, t)
|
| 84 |
+
sel_real = random.sample(real_files, 16 - t)
|
| 85 |
+
combo = [(p, False) for p in sel_ai] + [(p, True) for p in sel_real]
|
| 86 |
+
random.shuffle(combo)
|
| 87 |
+
paths, truth = zip(*combo)
|
| 88 |
+
|
| 89 |
+
# 3) Build thumbnails + reset flags
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 90 |
thumbs = [np.array(Image.open(p).convert("RGB")) for p in paths]
|
| 91 |
resets = [False] * 16
|
| 92 |
|
|
|
|
| 100 |
# }
|
| 101 |
|
| 102 |
new_state = {
|
|
|
|
|
|
|
| 103 |
"paths": paths,
|
| 104 |
+
"truth": truth,
|
| 105 |
+
"attempt_count": attempt_count , # increment attempt count
|
| 106 |
}
|
| 107 |
+
# Reset timer on Retry (state exists); leave unset on first load (state is None)
|
| 108 |
+
if state is not None:
|
| 109 |
+
new_state["timer_start"] = datetime.now().isoformat()
|
| 110 |
|
|
|
|
| 111 |
return (*thumbs, *resets, new_state)
|
| 112 |
+
|
| 113 |
def evaluate(
|
| 114 |
sel0, sel1, sel2, sel3,
|
| 115 |
sel4, sel5, sel6, sel7,
|
| 116 |
sel8, sel9, sel10, sel11,
|
| 117 |
sel12, sel13, sel14, sel15,
|
| 118 |
+
state,
|
| 119 |
+
user_name,
|
| 120 |
+
familiarity
|
| 121 |
):
|
| 122 |
+
# 1) Increment the simple count
|
| 123 |
+
attempt_count = state.get("attempt_count", 0) + 1
|
| 124 |
+
state["attempt_count"] = attempt_count
|
| 125 |
+
|
| 126 |
+
# 2) Border logic as before...
|
| 127 |
sels = [sel0, sel1, sel2, sel3, sel4, sel5, sel6, sel7,
|
| 128 |
sel8, sel9, sel10, sel11, sel12, sel13, sel14, sel15]
|
|
|
|
|
|
|
| 129 |
bordered, wrong_list = [], []
|
| 130 |
correct_count = 0
|
| 131 |
|
| 132 |
for i, chosen in enumerate(sels):
|
| 133 |
+
img = Image.open(state["paths"][i]).convert("RGB")
|
| 134 |
+
is_real = state["truth"][i]
|
| 135 |
+
correct = chosen == (not is_real)
|
| 136 |
+
color = "green" if correct else "red"
|
| 137 |
+
if correct:
|
| 138 |
correct_count += 1
|
| 139 |
else:
|
| 140 |
+
wrong_list.append(os.path.basename(state["paths"][i]))
|
|
|
|
| 141 |
bordered.append(np.array(ImageOps.expand(img, border=5, fill=color)))
|
| 142 |
|
| 143 |
+
# 3) Timer logic
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 144 |
timer_iso = state.get("timer_start")
|
| 145 |
+
elapsed = (datetime.now() - datetime.fromisoformat(timer_iso)).total_seconds() if timer_iso else 0.0
|
|
|
|
|
|
|
|
|
|
|
|
|
| 146 |
|
| 147 |
+
# incremented earlier in evaluate:
|
| 148 |
+
attempt_num = state["attempt_count"]
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 149 |
|
| 150 |
+
record_run(
|
| 151 |
+
user_name, # your textbox input
|
| 152 |
+
state["timer_start"], # ISO start time for this grid
|
| 153 |
+
attempt_num, # 1, 2, 3, ...
|
| 154 |
+
elapsed, # per‐attempt elapsed
|
| 155 |
+
familiarity, # slider input
|
| 156 |
+
correct_count, # how many correct this try
|
| 157 |
+
wrong_list # which files were wrong
|
| 158 |
)
|
| 159 |
+
# 5) Summary text
|
| 160 |
+
pct = correct_count / 16 * 100
|
| 161 |
+
summary = f"Attempt {attempt_count}: {correct_count}/16 correct ({pct:.1f}%), time {elapsed:.1f}s. \n Retry to improve your score! (The grid of images will refresh )"
|
| 162 |
|
| 163 |
+
return (*bordered, state, summary)
|
| 164 |
|
| 165 |
|
| 166 |
with gr.Blocks() as demo:
|
|
|
|
| 176 |
|
| 177 |
user_name = gr.Textbox(label="Your name", placeholder="e.g. Can be anonymous ex: 'Trisolaris123'")
|
| 178 |
|
| 179 |
+
gr.Markdown("# Instructions: Click the images you think are AI-generated. \nWhen you are done, click 'Submit' to record your results.\nClick 'Retry' to get a new set of images and improve your score (infinite retries). ")
|
| 180 |
# Familiarity slider (1=first time, 10=expert)
|
| 181 |
familiarity = gr.Slider(
|
| 182 |
minimum=1, maximum=10, step=1, value=1,
|
|
|
|
| 207 |
cb.change(fn=click_timer, inputs=[cb, state], outputs=[state])
|
| 208 |
|
| 209 |
submit_btn = gr.Button("Submit")
|
| 210 |
+
retry_btn = gr.Button("Retry")
|
| 211 |
+
score_out = gr.Textbox(label="Score & Time", interactive=False)
|
|
|
|
| 212 |
|
| 213 |
demo.load(
|
| 214 |
fn=get_new_grid,
|
| 215 |
inputs=[state],
|
| 216 |
outputs=[*image_components, *checkbox_components, state]
|
| 217 |
)
|
| 218 |
+
retry_btn.click(
|
| 219 |
fn=get_new_grid,
|
| 220 |
inputs=[state],
|
| 221 |
outputs=[*image_components, *checkbox_components, state]
|
| 222 |
)
|
| 223 |
submit_btn.click(
|
| 224 |
fn=evaluate,
|
| 225 |
+
inputs=[*checkbox_components, state,user_name, familiarity],
|
| 226 |
+
outputs=[*image_components, state,score_out]
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 227 |
)
|
| 228 |
+
|
| 229 |
|
| 230 |
# Reveal main UI only after agreeing to terms
|
| 231 |
terms_cb.change(
|