Spaces:
Build error
Build error
Upload app.py
Browse files
app.py
CHANGED
|
@@ -8,52 +8,46 @@ import streamlit as st
|
|
| 8 |
import os
|
| 9 |
import random
|
| 10 |
import pandas as pd
|
| 11 |
-
import base64
|
| 12 |
-
from io import BytesIO
|
| 13 |
from PIL import Image
|
| 14 |
|
| 15 |
-
#
|
| 16 |
-
|
| 17 |
-
#
|
| 18 |
-
|
| 19 |
-
|
| 20 |
-
|
| 21 |
-
# --- Fonctions utilitaires ---
|
| 22 |
|
|
|
|
|
|
|
|
|
|
| 23 |
def load_image_pair(index):
|
| 24 |
-
"""
|
| 25 |
-
|
|
|
|
| 26 |
"""
|
| 27 |
idx_str = str(index).zfill(5)
|
| 28 |
gt_path = os.path.join(IMAGE_DIR, f"{idx_str}.png")
|
| 29 |
pred_path = os.path.join(IMAGE_DIR, f"{idx_str}_gen0.png")
|
| 30 |
return gt_path, pred_path
|
| 31 |
|
| 32 |
-
def
|
| 33 |
-
"""
|
| 34 |
return Image.open(path)
|
| 35 |
|
| 36 |
def get_shuffled_pair(index):
|
| 37 |
-
"""Charge la paire d'images
|
| 38 |
-
|
| 39 |
"""
|
| 40 |
gt_path, pred_path = load_image_pair(index)
|
| 41 |
-
img_gt =
|
| 42 |
-
img_pred =
|
| 43 |
pair = [("GT", img_gt), ("Pred", img_pred)]
|
| 44 |
random.shuffle(pair)
|
| 45 |
return pair
|
| 46 |
|
| 47 |
-
|
| 48 |
-
|
| 49 |
-
|
| 50 |
-
image.save(buffered, format="PNG")
|
| 51 |
-
img_str = base64.b64encode(buffered.getvalue()).decode()
|
| 52 |
-
html_code = f'<a href="?selected={{}}"><img src="data:image/png;base64,{img_str}" width="{width}"></a>'
|
| 53 |
-
return html_code
|
| 54 |
-
|
| 55 |
-
# --- Gestion de la navigation entre pages via st.session_state ---
|
| 56 |
-
|
| 57 |
if "page" not in st.session_state:
|
| 58 |
st.session_state.page = "intro"
|
| 59 |
if "user_name" not in st.session_state:
|
|
@@ -62,21 +56,23 @@ if "current_index" not in st.session_state:
|
|
| 62 |
st.session_state.current_index = 1
|
| 63 |
if "results" not in st.session_state:
|
| 64 |
st.session_state.results = []
|
| 65 |
-
if "pair_order" not in st.session_state:
|
| 66 |
-
st.session_state.pair_order = {}
|
| 67 |
|
| 68 |
-
#
|
|
|
|
|
|
|
| 69 |
if st.session_state.page == "intro":
|
| 70 |
st.title("Wood Surface Evaluation Study")
|
| 71 |
-
st.markdown(
|
| 72 |
-
|
|
|
|
| 73 |
|
| 74 |
-
|
| 75 |
-
|
| 76 |
-
|
| 77 |
-
|
| 78 |
-
|
| 79 |
-
|
|
|
|
| 80 |
name = st.text_input("Enter your name:")
|
| 81 |
if st.button("Start Evaluation") and name:
|
| 82 |
st.session_state.user_name = name
|
|
@@ -84,57 +80,60 @@ if st.session_state.page == "intro":
|
|
| 84 |
st.rerun()
|
| 85 |
st.stop()
|
| 86 |
|
| 87 |
-
#
|
|
|
|
|
|
|
| 88 |
if st.session_state.page == "evaluation":
|
| 89 |
st.title("Wood Surface Evaluation")
|
| 90 |
st.write(f"User: **{st.session_state.user_name}**")
|
| 91 |
|
|
|
|
| 92 |
if st.session_state.current_index > NUM_PAIRS:
|
| 93 |
st.success("Thank you for completing the evaluation!")
|
| 94 |
-
# Exporter les résultats sous forme de CSV
|
| 95 |
results_df = pd.DataFrame(st.session_state.results)
|
| 96 |
results_df.to_csv(RESULTS_FILE, index=False)
|
| 97 |
-
st.write("Results saved in:
|
| 98 |
st.stop()
|
| 99 |
|
| 100 |
st.write(f"Image Pair {st.session_state.current_index} of {NUM_PAIRS}")
|
| 101 |
|
| 102 |
# Charger et mélanger la paire pour l'index courant
|
| 103 |
pair = get_shuffled_pair(st.session_state.current_index)
|
| 104 |
-
|
| 105 |
-
|
| 106 |
-
# Afficher les deux images dans deux colonnes, en les rendant cliquables via HTML
|
| 107 |
col1, col2 = st.columns(2)
|
|
|
|
| 108 |
with col1:
|
| 109 |
-
|
| 110 |
-
|
| 111 |
-
# On injecte le code HTML en remplaçant {} par "1"
|
| 112 |
-
st.markdown(img_html.format("1"), unsafe_allow_html=True)
|
| 113 |
-
st.caption("Image 1")
|
| 114 |
with col2:
|
| 115 |
-
|
| 116 |
-
st.
|
| 117 |
-
st.caption("Image 2")
|
| 118 |
|
| 119 |
-
st.
|
| 120 |
-
|
| 121 |
-
|
| 122 |
-
|
| 123 |
-
|
| 124 |
-
|
| 125 |
-
|
| 126 |
-
|
| 127 |
-
|
| 128 |
-
|
| 129 |
-
|
| 130 |
-
|
| 131 |
-
|
| 132 |
-
|
| 133 |
-
|
| 134 |
-
|
| 135 |
-
|
| 136 |
-
|
| 137 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 138 |
|
| 139 |
# import streamlit as st
|
| 140 |
# import os
|
|
|
|
| 8 |
import os
|
| 9 |
import random
|
| 10 |
import pandas as pd
|
|
|
|
|
|
|
| 11 |
from PIL import Image
|
| 12 |
|
| 13 |
+
# -------------------------
|
| 14 |
+
# Global parameters
|
| 15 |
+
# -------------------------
|
| 16 |
+
IMAGE_DIR = "images" # Dossier contenant les images
|
| 17 |
+
NUM_PAIRS = 25 # Nombre total de paires à évaluer
|
| 18 |
+
RESULTS_FILE = "results.csv" # Fichier CSV de sauvegarde des réponses
|
|
|
|
| 19 |
|
| 20 |
+
# -------------------------
|
| 21 |
+
# Helper functions
|
| 22 |
+
# -------------------------
|
| 23 |
def load_image_pair(index):
|
| 24 |
+
"""
|
| 25 |
+
Pour un index donné (entier), retourne le chemin du ground truth et celui de la génération.
|
| 26 |
+
Les fichiers sont nommés avec un index à 5 chiffres.
|
| 27 |
"""
|
| 28 |
idx_str = str(index).zfill(5)
|
| 29 |
gt_path = os.path.join(IMAGE_DIR, f"{idx_str}.png")
|
| 30 |
pred_path = os.path.join(IMAGE_DIR, f"{idx_str}_gen0.png")
|
| 31 |
return gt_path, pred_path
|
| 32 |
|
| 33 |
+
def open_image(path):
|
| 34 |
+
"""Ouvre l'image avec PIL."""
|
| 35 |
return Image.open(path)
|
| 36 |
|
| 37 |
def get_shuffled_pair(index):
|
| 38 |
+
"""Charge la paire d'images et retourne une liste de tuples (label, image) dans un ordre aléatoire.
|
| 39 |
+
Le label est 'GT' pour ground truth et 'Pred' pour generated.
|
| 40 |
"""
|
| 41 |
gt_path, pred_path = load_image_pair(index)
|
| 42 |
+
img_gt = open_image(gt_path)
|
| 43 |
+
img_pred = open_image(pred_path)
|
| 44 |
pair = [("GT", img_gt), ("Pred", img_pred)]
|
| 45 |
random.shuffle(pair)
|
| 46 |
return pair
|
| 47 |
|
| 48 |
+
# -------------------------
|
| 49 |
+
# Navigation via st.session_state
|
| 50 |
+
# -------------------------
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 51 |
if "page" not in st.session_state:
|
| 52 |
st.session_state.page = "intro"
|
| 53 |
if "user_name" not in st.session_state:
|
|
|
|
| 56 |
st.session_state.current_index = 1
|
| 57 |
if "results" not in st.session_state:
|
| 58 |
st.session_state.results = []
|
|
|
|
|
|
|
| 59 |
|
| 60 |
+
# -------------------------
|
| 61 |
+
# Intro page
|
| 62 |
+
# -------------------------
|
| 63 |
if st.session_state.page == "intro":
|
| 64 |
st.title("Wood Surface Evaluation Study")
|
| 65 |
+
st.markdown(
|
| 66 |
+
"""
|
| 67 |
+
**Welcome!**
|
| 68 |
|
| 69 |
+
In this study, you will be shown pairs of wood surface images.
|
| 70 |
+
One image is a real photograph (Ground Truth) and the other is generated by AI.
|
| 71 |
+
Your task is to select, by answering below each image, which one you believe is **real**.
|
| 72 |
+
|
| 73 |
+
Please enter your name below and click **Start Evaluation** to begin.
|
| 74 |
+
"""
|
| 75 |
+
)
|
| 76 |
name = st.text_input("Enter your name:")
|
| 77 |
if st.button("Start Evaluation") and name:
|
| 78 |
st.session_state.user_name = name
|
|
|
|
| 80 |
st.rerun()
|
| 81 |
st.stop()
|
| 82 |
|
| 83 |
+
# -------------------------
|
| 84 |
+
# Evaluation page
|
| 85 |
+
# -------------------------
|
| 86 |
if st.session_state.page == "evaluation":
|
| 87 |
st.title("Wood Surface Evaluation")
|
| 88 |
st.write(f"User: **{st.session_state.user_name}**")
|
| 89 |
|
| 90 |
+
# Si toutes les paires ont été évaluées, afficher un message et sauvegarder les résultats
|
| 91 |
if st.session_state.current_index > NUM_PAIRS:
|
| 92 |
st.success("Thank you for completing the evaluation!")
|
|
|
|
| 93 |
results_df = pd.DataFrame(st.session_state.results)
|
| 94 |
results_df.to_csv(RESULTS_FILE, index=False)
|
| 95 |
+
st.write(f"Results saved in: **{RESULTS_FILE}**")
|
| 96 |
st.stop()
|
| 97 |
|
| 98 |
st.write(f"Image Pair {st.session_state.current_index} of {NUM_PAIRS}")
|
| 99 |
|
| 100 |
# Charger et mélanger la paire pour l'index courant
|
| 101 |
pair = get_shuffled_pair(st.session_state.current_index)
|
| 102 |
+
# Affichage dans deux colonnes avec radio buttons sous chacune
|
|
|
|
|
|
|
| 103 |
col1, col2 = st.columns(2)
|
| 104 |
+
|
| 105 |
with col1:
|
| 106 |
+
st.image(pair[0][1], caption="Image 1", use_column_width=True)
|
| 107 |
+
choice1 = st.radio("Is this image real?", options=["No", "Yes"], index=0, key=f"choice_{st.session_state.current_index}_1")
|
|
|
|
|
|
|
|
|
|
| 108 |
with col2:
|
| 109 |
+
st.image(pair[1][1], caption="Image 2", use_column_width=True)
|
| 110 |
+
choice2 = st.radio("Is this image real?", options=["No", "Yes"], index=0, key=f"choice_{st.session_state.current_index}_2")
|
|
|
|
| 111 |
|
| 112 |
+
if st.button("Submit Evaluation"):
|
| 113 |
+
selections = [choice1, choice2]
|
| 114 |
+
yes_count = selections.count("Yes")
|
| 115 |
+
if yes_count != 1:
|
| 116 |
+
st.error("Please select exactly one image as real (one Yes and one No).")
|
| 117 |
+
else:
|
| 118 |
+
if choice1 == "Yes":
|
| 119 |
+
selected_label = pair[0][0] # "GT" ou "Pred"
|
| 120 |
+
chosen_image = "Image 1"
|
| 121 |
+
else:
|
| 122 |
+
selected_label = pair[1][0]
|
| 123 |
+
chosen_image = "Image 2"
|
| 124 |
+
|
| 125 |
+
st.success(f"You selected **{chosen_image}** as the real image.")
|
| 126 |
+
|
| 127 |
+
# Enregistrer le résultat
|
| 128 |
+
st.session_state.results.append({
|
| 129 |
+
"pair_index": st.session_state.current_index,
|
| 130 |
+
"pair_order": [pair[0][0], pair[1][0]], # ordre des labels affichés
|
| 131 |
+
"selected": selected_label,
|
| 132 |
+
})
|
| 133 |
+
|
| 134 |
+
# Passer à la paire suivante
|
| 135 |
+
st.session_state.current_index += 1
|
| 136 |
+
st.rerun()
|
| 137 |
|
| 138 |
# import streamlit as st
|
| 139 |
# import os
|