Spaces:
Sleeping
Sleeping
| import os, random, json | |
| from datetime import datetime | |
| from PIL import Image, ImageOps | |
| import numpy as np | |
| import gradio as gr | |
| from gspread import service_account_from_dict | |
| from huggingface_hub import HfApi, hf_hub_download | |
| # -------- Configuration -------- | |
| HF_TOKEN = os.environ["HF_TOKEN"] | |
| api = HfApi(token=HF_TOKEN) | |
| REPO_ID = "HotshotGoku/Images_Test_AI_or_Real" | |
| # List files in the dataset | |
| all_files = api.list_repo_files( | |
| repo_id=REPO_ID, | |
| repo_type="dataset" | |
| ) | |
| # Get the AI vs Real folders | |
| ai_remote = [f for f in all_files if f.startswith("AI/")] | |
| real_remote = [f for f in all_files if f.startswith("Real/")] | |
| ai_files = [hf_hub_download(repo_id=REPO_ID, repo_type="dataset",filename=path, token=HF_TOKEN) for path in ai_remote] | |
| real_files = [hf_hub_download(repo_id=REPO_ID, repo_type="dataset",filename=path, token=HF_TOKEN) for path in real_remote] | |
| # Load a sample real image for the demo | |
| real_demo_path= hf_hub_download(repo_id=REPO_ID, repo_type="dataset",filename="Experiment_grid_3x3.png", token=HF_TOKEN) | |
| real_demo = Image.open(real_demo_path).convert("RGB") | |
| real_demo.thumbnail((500, 500), resample=Image.LANCZOS) # now ≤500×500 | |
| # 2) Turn it into a numpy array | |
| real_demo_small = np.array(real_demo) | |
| history_file = "history_state.json" | |
| # -------------------------------- | |
| # ——— Google Sheets setup ——— | |
| # load service‐account JSON from HF secret | |
| info = json.loads(os.environ["JSON_ACCESS"]) | |
| # info = json.loads('service_account.json') # for local testing only | |
| gc = service_account_from_dict(info) | |
| # open by key for stability | |
| ws = gc.open_by_key("1lfhomrFnhmRIxQvdJZr-7gASA3sONBT_mblHNreMKvc").sheet1 | |
| def record_run(name, session_start, attempt_num, elapsed_sec, familiarity, correct_count, wrong_list): | |
| """ | |
| Append one row with exactly: | |
| Name | StartTime | #Attempt | ElapsedSec | Familiarity | AttemptsList | WrongsList | |
| """ | |
| ws.append_row([ | |
| name, | |
| session_start, # when that grid/timer started | |
| attempt_num, # 1, 2, 3, … | |
| elapsed_sec, # seconds for this attempt | |
| familiarity, # slider value | |
| json.dumps([correct_count]), # e.g. [9] | |
| json.dumps(wrong_list) # e.g. ["217_1.png","243_2.png"] | |
| ]) | |
| def get_new_grid(state=None): | |
| """ | |
| Generate a fresh 4×4 grid: | |
| - Preserve attempt_count | |
| - **Reset** timer_start to "now" whenever the user hits Retry (i.e. state is not None) | |
| - Leave timer_start unset on initial load (so they must click Start or first Fake?) | |
| """ | |
| attempt_count = (state or {}).get("attempt_count", 0) | |
| t = random.randint(4, 12) | |
| sel_ai = random.sample(ai_files, t) | |
| sel_real = random.sample(real_files, 16 - t) | |
| combo = [(p, False) for p in sel_ai] + [(p, True) for p in sel_real] | |
| random.shuffle(combo) | |
| paths, truth = zip(*combo) | |
| # 3) Build thumbnails + reset flags | |
| thumbs = [np.array(Image.open(p).convert("RGB")) for p in paths] | |
| resets = [False] * 16 | |
| new_state = { | |
| "paths": paths, | |
| "truth": truth, | |
| "attempt_count": attempt_count , # increment attempt count, | |
| "timer_start": datetime.now().isoformat() | |
| } | |
| return (*thumbs, *resets, new_state) | |
| def evaluate( | |
| sel0, sel1, sel2, sel3, | |
| sel4, sel5, sel6, sel7, | |
| sel8, sel9, sel10, sel11, | |
| sel12, sel13, sel14, sel15, | |
| state, | |
| user_name, | |
| familiarity | |
| ): | |
| # 1) Increment the simple count | |
| attempt_count = state.get("attempt_count", 0) + 1 | |
| state["attempt_count"] = attempt_count | |
| # 2) Border logic as before... | |
| sels = [sel0, sel1, sel2, sel3, sel4, sel5, sel6, sel7, | |
| sel8, sel9, sel10, sel11, sel12, sel13, sel14, sel15] | |
| bordered, wrong_list = [], [] | |
| correct_count = 0 | |
| for i, chosen in enumerate(sels): | |
| img = Image.open(state["paths"][i]).convert("RGB") | |
| is_real = state["truth"][i] | |
| correct = chosen == (not is_real) | |
| color = "green" if correct else "red" | |
| if correct: | |
| correct_count += 1 | |
| else: | |
| wrong_list.append(os.path.basename(state["paths"][i])) | |
| bordered.append(np.array(ImageOps.expand(img, border=5, fill=color))) | |
| # 3) Timer logic | |
| timer_iso = state.get("timer_start") | |
| elapsed = (datetime.now() - datetime.fromisoformat(timer_iso)).total_seconds() if timer_iso else 0.0 | |
| # incremented earlier in evaluate: | |
| attempt_num = state["attempt_count"] | |
| record_run( | |
| user_name, # your textbox input | |
| state["timer_start"], # ISO start time for this grid | |
| attempt_num, # 1, 2, 3, ... | |
| elapsed, # per‐attempt elapsed | |
| familiarity, # slider input | |
| correct_count, # how many correct this try | |
| wrong_list # which files were wrong | |
| ) | |
| # 5) Summary text | |
| pct = correct_count / 16 * 100 | |
| 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 )" | |
| return (*bordered, state, summary) | |
| with gr.Blocks(fill_width=True) as demo: | |
| # Terms & Conditions gate | |
| terms_cb = gr.Checkbox( | |
| label="I agree that my game data can be used for downstream analysis purposes.", | |
| value=False | |
| ) | |
| # Main UI hidden until terms are accepted | |
| with gr.Column(visible=False) as main_ui: | |
| gr.Markdown("# Identify the Fake \nEnter your name, then play:") | |
| user_name = gr.Textbox(label="Your name", placeholder="e.g. Can be anonymous ex: 'Trisolaris123'") | |
| 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). ") | |
| # Familiarity slider (1=first time, 10=expert) | |
| familiarity = gr.Slider( | |
| minimum=1, maximum=10, step=1, value=1, | |
| label="How familiar are you with P. aeruginosa patterns? 1: First time seeing these 10: Have worked with these patterns before", | |
| interactive=True | |
| ) | |
| state = gr.State(None) | |
| example_ui = gr.Column(visible=True) | |
| with example_ui: | |
| gr.Markdown("### Before you begin: This are what *Real* Images look like. Click start to begin the game. ") | |
| gr.Image(value=real_demo_small, show_label=False) #.style( | |
| # width=300, # px | |
| # height=300 # px | |
| # ) # set desired display height) | |
| gr.Markdown("---") | |
| # Add a Start button that WILL load images and start the timer | |
| start_btn = gr.Button("Start") | |
| # Now wrap the grid + controls in a hidden Column | |
| with gr.Column(visible=False) as grid_ui: | |
| image_components = [] | |
| checkbox_components = [] | |
| for row in range(4): | |
| with gr.Row(): | |
| for col in range(4): | |
| idx = row * 4 + col | |
| with gr.Column(min_width=200, scale=1): | |
| img = gr.Image(type="numpy", interactive=False, show_label=False) | |
| cb = gr.Checkbox(label="Fake?", elem_id=f"cb_{idx}") | |
| image_components.append(img) | |
| checkbox_components.append(cb) | |
| submit_btn = gr.Button("Submit") | |
| retry_btn = gr.Button("Retry") | |
| score_out = gr.Textbox(label="Score & Time", interactive=False) | |
| retry_btn.click( | |
| fn=get_new_grid, | |
| inputs=[state], | |
| outputs=[*image_components, *checkbox_components, state] | |
| ) | |
| submit_btn.click( | |
| fn=evaluate, | |
| inputs=[*checkbox_components, state,user_name, familiarity], | |
| outputs=[*image_components, state,score_out] | |
| ) | |
| # Wire start_btn to both load the grid _and_ reveal it | |
| start_btn.click( | |
| fn=get_new_grid, | |
| inputs=[state], | |
| outputs=[*image_components, *checkbox_components, state] | |
| ) | |
| start_btn.click( | |
| fn=lambda _: gr.update(visible=True), | |
| inputs=[], | |
| outputs=[grid_ui] | |
| ) | |
| start_btn.click( | |
| fn=lambda _: gr.update(visible=False), | |
| inputs=[], | |
| outputs=[example_ui] | |
| ) | |
| # Reveal main UI only after agreeing to terms | |
| terms_cb.change( | |
| fn=lambda agreed: gr.update(visible=agreed), | |
| inputs=[terms_cb], | |
| outputs=[main_ui] | |
| ) | |
| demo.launch() | |