Spaces:
Running
Running
changed to ai-generated score
Browse files- app/static/index.html +49 -48
app/static/index.html
CHANGED
|
@@ -113,7 +113,7 @@
|
|
| 113 |
</div>
|
| 114 |
|
| 115 |
<div>
|
| 116 |
-
<div id="verdict-text" class="text-3xl sm:text-4xl font-extrabold leading-tight
|
| 117 |
<div id="advice-text" class="mt-3 text-lg sm:text-xl font-semibold text-gray-900"></div>
|
| 118 |
<div id="frames-info" class="mt-4 text-sm text-gray-500"></div>
|
| 119 |
</div>
|
|
@@ -159,25 +159,25 @@
|
|
| 159 |
clear: "Clear",
|
| 160 |
analyzing: "Analyzing...",
|
| 161 |
analyzing_video: "Sampling frames and running the model...",
|
| 162 |
-
|
| 163 |
how_calculated: "How is this calculated?",
|
| 164 |
-
|
| 165 |
-
|
| 166 |
-
|
| 167 |
-
|
| 168 |
-
|
| 169 |
-
|
| 170 |
-
|
| 171 |
-
|
| 172 |
-
|
| 173 |
-
|
| 174 |
analyze_another: "Analyze another file",
|
| 175 |
error_generic: "Something went wrong. Please try again.",
|
| 176 |
error_size: "File is too large.",
|
| 177 |
error_type: "Unsupported file type.",
|
| 178 |
frames_info: "Averaged over {n} frames.",
|
| 179 |
how_calculated_title: "How the score is computed",
|
| 180 |
-
how_calculated_body: "We use a Swin Transformer V2 model fine-tuned to distinguish real photographs from AI-generated images. For videos, we sample 5 frames evenly across the duration and average the model's confidence. The
|
| 181 |
close: "Close",
|
| 182 |
privacy_note: "Files are processed in memory and not stored.",
|
| 183 |
},
|
|
@@ -190,25 +190,25 @@
|
|
| 190 |
clear: "Effacer",
|
| 191 |
analyzing: "Analyse en cours...",
|
| 192 |
analyzing_video: "Échantillonnage des images et exécution du modèle...",
|
| 193 |
-
|
| 194 |
how_calculated: "Comment est-ce calculé ?",
|
| 195 |
-
|
| 196 |
-
|
| 197 |
-
|
| 198 |
-
|
| 199 |
-
|
| 200 |
-
|
| 201 |
-
|
| 202 |
-
|
| 203 |
-
|
| 204 |
-
|
| 205 |
analyze_another: "Analyser un autre fichier",
|
| 206 |
error_generic: "Une erreur est survenue. Veuillez réessayer.",
|
| 207 |
error_size: "Le fichier est trop volumineux.",
|
| 208 |
error_type: "Type de fichier non pris en charge.",
|
| 209 |
frames_info: "Moyenne sur {n} images.",
|
| 210 |
how_calculated_title: "Comment le score est calculé",
|
| 211 |
-
how_calculated_body: "Nous utilisons un modèle Swin Transformer V2 entraîné pour distinguer les vraies photographies des images générées par IA. Pour les vidéos, nous échantillonnons 5 images réparties uniformément sur la durée et faisons la moyenne de la confiance du modèle. Le score
|
| 212 |
close: "Fermer",
|
| 213 |
privacy_note: "Les fichiers sont traités en mémoire et ne sont pas conservés.",
|
| 214 |
},
|
|
@@ -249,26 +249,26 @@
|
|
| 249 |
applyI18n();
|
| 250 |
}
|
| 251 |
|
| 252 |
-
function getVerdict(
|
| 253 |
const T = t();
|
| 254 |
-
if (
|
| 255 |
return {
|
| 256 |
-
verdict: mediaType === "video" ? T.
|
| 257 |
-
advice: T.
|
| 258 |
-
tone: "
|
| 259 |
};
|
| 260 |
}
|
| 261 |
-
if (
|
| 262 |
return {
|
| 263 |
-
verdict: mediaType === "video" ? T.
|
| 264 |
-
advice: T.
|
| 265 |
-
tone: "
|
| 266 |
};
|
| 267 |
}
|
| 268 |
return {
|
| 269 |
-
verdict: mediaType === "video" ? T.
|
| 270 |
-
advice: T.
|
| 271 |
-
tone: "
|
| 272 |
};
|
| 273 |
}
|
| 274 |
|
|
@@ -335,18 +335,19 @@
|
|
| 335 |
|
| 336 |
function renderResultText() {
|
| 337 |
if (!state.result) return;
|
| 338 |
-
const
|
| 339 |
-
const v = getVerdict(
|
| 340 |
$("verdict-text").textContent = v.verdict;
|
| 341 |
$("advice-text").textContent = v.advice;
|
| 342 |
-
$("
|
| 343 |
-
const arc = $("arc-fg");
|
| 344 |
const tones = {
|
| 345 |
-
|
| 346 |
-
|
| 347 |
-
|
| 348 |
};
|
| 349 |
-
|
|
|
|
|
|
|
| 350 |
if (state.result.media_type === "video") {
|
| 351 |
$("frames-info").textContent = t().frames_info.replace("{n}", state.result.n_frames);
|
| 352 |
} else {
|
|
@@ -354,14 +355,14 @@
|
|
| 354 |
}
|
| 355 |
}
|
| 356 |
|
| 357 |
-
function animateArc(
|
| 358 |
const arc = $("arc-fg");
|
| 359 |
arc.style.transition = "none";
|
| 360 |
arc.setAttribute("stroke-dashoffset", CIRCUMFERENCE);
|
| 361 |
requestAnimationFrame(() => {
|
| 362 |
requestAnimationFrame(() => {
|
| 363 |
arc.style.transition = "";
|
| 364 |
-
const target = CIRCUMFERENCE * (1 - Math.max(0, Math.min(1,
|
| 365 |
arc.setAttribute("stroke-dashoffset", target);
|
| 366 |
});
|
| 367 |
});
|
|
@@ -399,7 +400,7 @@
|
|
| 399 |
state.result = await res.json();
|
| 400 |
renderResultText();
|
| 401 |
showCard("result-card");
|
| 402 |
-
animateArc(state.result.
|
| 403 |
} catch (e) {
|
| 404 |
showError(e.message);
|
| 405 |
showCard("upload-card");
|
|
|
|
| 113 |
</div>
|
| 114 |
|
| 115 |
<div>
|
| 116 |
+
<div id="verdict-text" class="text-3xl sm:text-4xl font-extrabold leading-tight"></div>
|
| 117 |
<div id="advice-text" class="mt-3 text-lg sm:text-xl font-semibold text-gray-900"></div>
|
| 118 |
<div id="frames-info" class="mt-4 text-sm text-gray-500"></div>
|
| 119 |
</div>
|
|
|
|
| 159 |
clear: "Clear",
|
| 160 |
analyzing: "Analyzing...",
|
| 161 |
analyzing_video: "Sampling frames and running the model...",
|
| 162 |
+
ai_score: "AI-generated score",
|
| 163 |
how_calculated: "How is this calculated?",
|
| 164 |
+
ai_label: "AI-generated",
|
| 165 |
+
verdict_ai_image: "This image is likely AI-generated,",
|
| 166 |
+
verdict_ai_video: "This video is likely AI-generated,",
|
| 167 |
+
verdict_uncertain_image: "We're uncertain about this image,",
|
| 168 |
+
verdict_uncertain_video: "We're uncertain about this video,",
|
| 169 |
+
verdict_real_image: "This image is likely authentic,",
|
| 170 |
+
verdict_real_video: "This video is likely authentic,",
|
| 171 |
+
advice_ai: "you should not share it with your network.",
|
| 172 |
+
advice_uncertain: "verify it from trusted sources before sharing.",
|
| 173 |
+
advice_real: "but always cross-check important content.",
|
| 174 |
analyze_another: "Analyze another file",
|
| 175 |
error_generic: "Something went wrong. Please try again.",
|
| 176 |
error_size: "File is too large.",
|
| 177 |
error_type: "Unsupported file type.",
|
| 178 |
frames_info: "Averaged over {n} frames.",
|
| 179 |
how_calculated_title: "How the score is computed",
|
| 180 |
+
how_calculated_body: "We use a Swin Transformer V2 model fine-tuned to distinguish real photographs from AI-generated images. For videos, we sample 5 frames evenly across the duration and average the model's confidence. The score shown is the model's estimated probability that the content was generated by AI.",
|
| 181 |
close: "Close",
|
| 182 |
privacy_note: "Files are processed in memory and not stored.",
|
| 183 |
},
|
|
|
|
| 190 |
clear: "Effacer",
|
| 191 |
analyzing: "Analyse en cours...",
|
| 192 |
analyzing_video: "Échantillonnage des images et exécution du modèle...",
|
| 193 |
+
ai_score: "Score d'IA générée",
|
| 194 |
how_calculated: "Comment est-ce calculé ?",
|
| 195 |
+
ai_label: "IA générée",
|
| 196 |
+
verdict_ai_image: "Cette image est probablement générée par IA,",
|
| 197 |
+
verdict_ai_video: "Cette vidéo est probablement générée par IA,",
|
| 198 |
+
verdict_uncertain_image: "Nous ne sommes pas certains pour cette image,",
|
| 199 |
+
verdict_uncertain_video: "Nous ne sommes pas certains pour cette vidéo,",
|
| 200 |
+
verdict_real_image: "Cette image est probablement authentique,",
|
| 201 |
+
verdict_real_video: "Cette vidéo est probablement authentique,",
|
| 202 |
+
advice_ai: "vous ne devriez pas la partager avec votre réseau.",
|
| 203 |
+
advice_uncertain: "vérifiez auprès de sources fiables avant de partager.",
|
| 204 |
+
advice_real: "vérifiez tout de même les contenus importants.",
|
| 205 |
analyze_another: "Analyser un autre fichier",
|
| 206 |
error_generic: "Une erreur est survenue. Veuillez réessayer.",
|
| 207 |
error_size: "Le fichier est trop volumineux.",
|
| 208 |
error_type: "Type de fichier non pris en charge.",
|
| 209 |
frames_info: "Moyenne sur {n} images.",
|
| 210 |
how_calculated_title: "Comment le score est calculé",
|
| 211 |
+
how_calculated_body: "Nous utilisons un modèle Swin Transformer V2 entraîné pour distinguer les vraies photographies des images générées par IA. Pour les vidéos, nous échantillonnons 5 images réparties uniformément sur la durée et faisons la moyenne de la confiance du modèle. Le score affiché correspond à la probabilité estimée que le contenu ait été généré par IA.",
|
| 212 |
close: "Fermer",
|
| 213 |
privacy_note: "Les fichiers sont traités en mémoire et ne sont pas conservés.",
|
| 214 |
},
|
|
|
|
| 249 |
applyI18n();
|
| 250 |
}
|
| 251 |
|
| 252 |
+
function getVerdict(aiScore, mediaType) {
|
| 253 |
const T = t();
|
| 254 |
+
if (aiScore >= 0.60) {
|
| 255 |
return {
|
| 256 |
+
verdict: mediaType === "video" ? T.verdict_ai_video : T.verdict_ai_image,
|
| 257 |
+
advice: T.advice_ai,
|
| 258 |
+
tone: "ai",
|
| 259 |
};
|
| 260 |
}
|
| 261 |
+
if (aiScore >= 0.30) {
|
| 262 |
return {
|
| 263 |
+
verdict: mediaType === "video" ? T.verdict_uncertain_video : T.verdict_uncertain_image,
|
| 264 |
+
advice: T.advice_uncertain,
|
| 265 |
+
tone: "uncertain",
|
| 266 |
};
|
| 267 |
}
|
| 268 |
return {
|
| 269 |
+
verdict: mediaType === "video" ? T.verdict_real_video : T.verdict_real_image,
|
| 270 |
+
advice: T.advice_real,
|
| 271 |
+
tone: "real",
|
| 272 |
};
|
| 273 |
}
|
| 274 |
|
|
|
|
| 335 |
|
| 336 |
function renderResultText() {
|
| 337 |
if (!state.result) return;
|
| 338 |
+
const score = state.result.p_fake;
|
| 339 |
+
const v = getVerdict(score, state.result.media_type);
|
| 340 |
$("verdict-text").textContent = v.verdict;
|
| 341 |
$("advice-text").textContent = v.advice;
|
| 342 |
+
$("score-pct").textContent = `${Math.round(score * 100)}%`;
|
|
|
|
| 343 |
const tones = {
|
| 344 |
+
ai: "#dc2626",
|
| 345 |
+
uncertain: "#d97706",
|
| 346 |
+
real: "#16a34a",
|
| 347 |
};
|
| 348 |
+
const color = tones[v.tone];
|
| 349 |
+
$("arc-fg").setAttribute("stroke", color);
|
| 350 |
+
$("verdict-text").style.color = color;
|
| 351 |
if (state.result.media_type === "video") {
|
| 352 |
$("frames-info").textContent = t().frames_info.replace("{n}", state.result.n_frames);
|
| 353 |
} else {
|
|
|
|
| 355 |
}
|
| 356 |
}
|
| 357 |
|
| 358 |
+
function animateArc(fraction) {
|
| 359 |
const arc = $("arc-fg");
|
| 360 |
arc.style.transition = "none";
|
| 361 |
arc.setAttribute("stroke-dashoffset", CIRCUMFERENCE);
|
| 362 |
requestAnimationFrame(() => {
|
| 363 |
requestAnimationFrame(() => {
|
| 364 |
arc.style.transition = "";
|
| 365 |
+
const target = CIRCUMFERENCE * (1 - Math.max(0, Math.min(1, fraction)));
|
| 366 |
arc.setAttribute("stroke-dashoffset", target);
|
| 367 |
});
|
| 368 |
});
|
|
|
|
| 400 |
state.result = await res.json();
|
| 401 |
renderResultText();
|
| 402 |
showCard("result-card");
|
| 403 |
+
animateArc(state.result.p_fake);
|
| 404 |
} catch (e) {
|
| 405 |
showError(e.message);
|
| 406 |
showCard("upload-card");
|