File size: 8,777 Bytes
9b6bf86
 
 
 
 
ce66387
5d30462
9b6bf86
 
5d30462
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
f99c6e0
 
1f78597
aa4569e
26884f2
aa4569e
 
 
ce66387
9b6bf86
 
ce66387
 
 
 
 
 
 
9b6bf86
ce66387
 
d87f99a
 
 
 
 
ce66387
 
d87f99a
 
 
ce66387
d87f99a
 
ce66387
9b6bf86
 
d87f99a
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
9b6bf86
ce66387
9b6bf86
 
ce66387
 
d87f99a
e5bb8a3
 
ce66387
e5bb8a3
9b6bf86
ce66387
d87f99a
9b6bf86
 
 
 
 
d87f99a
 
 
9b6bf86
d87f99a
 
 
 
 
ce66387
 
 
 
9b6bf86
 
d87f99a
 
 
 
 
ce66387
9b6bf86
d87f99a
9b6bf86
 
d87f99a
ce66387
d87f99a
ce66387
d87f99a
 
9b6bf86
d87f99a
 
 
 
 
 
 
 
ce66387
d87f99a
 
 
ce66387
d87f99a
9b6bf86
c52f73f
ce66387
 
 
 
9b6bf86
 
ce66387
 
 
 
 
 
d87f99a
ce66387
 
 
e5bb8a3
ce66387
 
 
 
f99c6e0
 
 
aa4569e
 
 
 
f99c6e0
 
e5bb8a3
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
ce66387
 
 
 
e5bb8a3
 
 
 
ce66387
f99c6e0
 
 
 
 
d87f99a
9b6bf86
ce66387
 
 
 
 
9b6bf86
 
 
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
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()