Spaces:
Runtime error
Runtime error
<html lang="en"> | |
<head> | |
<meta charset="UTF-8"> | |
<title>Fraud Score</title> | |
<meta name="viewport" content="width=device-width, initial-scale=1"> | |
<link href="https://cdn.jsdelivr.net/npm/bootstrap@5.1.3/dist/css/bootstrap.min.css" rel="stylesheet"> | |
<link href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/6.4.0/css/all.min.css" rel="stylesheet"> | |
<style> | |
:root{ | |
--apple-blue:#007aff; | |
--apple-gray:#f2f2f7; | |
--apple-border:#d1d1d6; | |
--apple-shadow:0 4px 12px rgba(0,0,0,.08); | |
} | |
body{font-family:-apple-system,BlinkMacSystemFont,"Segoe UI",Roboto,Helvetica,Arial,sans-serif;background:var(--apple-gray);color:#1c1c1e;} | |
h1,h2,h5{font-weight:600;} | |
.preset-image-container{overflow:hidden;margin-bottom:12px} | |
.preset-image{width:100%;border-radius:12px;border:2px solid transparent;box-shadow:var(--apple-shadow);transition:transform .3s ease,border-color .3s ease;cursor:pointer} | |
.preset-image:hover{transform:scale(1.05)} | |
.preset-image.selected{border-color:var(--apple-blue)} | |
.dropzone{border:2px dashed var(--apple-border);border-radius:16px;background:#fff;padding:20px;text-align:center;box-shadow:var(--apple-shadow)} | |
.preview-image{max-width:220px;border-radius:16px;box-shadow:var(--apple-shadow);opacity:0;transition:opacity .4s ease} | |
.preview-image.visible{opacity:1} | |
.btn-primary{background:var(--apple-blue);border:none;font-weight:500} | |
.btn-primary:hover{background:#005ecb} | |
.jauge-container{display:none;align-items:center;gap:24px;margin-top:24px} | |
.jauge{width:24px;height:160px;border-radius:12px;background:#e5e5ea;position:relative;overflow:hidden;border:1px solid var(--apple-border)} | |
.jauge-level{position:absolute;bottom:0;width:100%;border-radius:12px;background:var(--apple-blue);transition:height .6s ease,background .3s ease} | |
.jauge-labels{font-size:.7rem;color:#636366;display:flex;flex-direction:column;justify-content:space-between;height:160px} | |
.icon-buttons { display: flex; flex-direction: column; gap: 16px; justify-content: center; align-items: center; margin-left: 12px; } | |
.icon-buttons button { background: none; border: none; color: var(--apple-blue); font-size: 24px; cursor: pointer; } | |
#compareSection{margin-top:40px;text-align:center;display:none} | |
</style> | |
</head> | |
<body> | |
<div class="container py-5"> | |
<h1 class="text-center mb-1">Fraud Score</h1> | |
<p class="text-center text-muted mb-5 fs-5">Is my work used by generative AI ?</p> | |
<div class="row g-5"> | |
<div class="col-md-6"> | |
<h5 class="text-center mb-3">Select Original Image</h5> | |
<div class="row"> | |
<div class="col-4"><div class="preset-image-container"><img id="preset-1-1" src="/static/marilyn_1.jpg" class="preset-image" onclick="selectPreset(1,1)" alt="Original 1"></div></div> | |
<div class="col-4"><div class="preset-image-container"><img id="preset-2-1" src="/static/star wars_1b.jpg" class="preset-image" onclick="selectPreset(2,1)" alt="Original 2"></div></div> | |
<div class="col-4"><div class="preset-image-container"><img id="preset-3-1" src="/static/muhammad ali_1.jpg" class="preset-image" onclick="selectPreset(3,1)" alt="Original 3"></div></div> | |
</div> | |
<div class="dropzone mt-3" id="dropzone1"> | |
<p class="mb-2">Drag & Drop or Upload (.jpg/.png/.webp)</p> | |
<input type="file" id="fileInput1" accept="image/*" hidden> | |
<button class="btn btn-outline-secondary" onclick="document.getElementById('fileInput1').click()">Upload</button> | |
</div> | |
</div> | |
<div class="col-md-6"> | |
<h5 class="text-center mb-3">Select AI Generated Image</h5> | |
<div class="row"> | |
<div class="col-4"><div class="preset-image-container"><img id="preset-1-2" src="/static/marilyn_2.png" class="preset-image" onclick="selectPreset(1,2)" alt="AI 1"></div></div> | |
<div class="col-4"><div class="preset-image-container"><img id="preset-2-2" src="/static/star wars_2b.png" class="preset-image" onclick="selectPreset(2,2)" alt="AI 2"></div></div> | |
<div class="col-4"><div class="preset-image-container"><img id="preset-3-2" src="/static/muhammad ali_2.png" class="preset-image" onclick="selectPreset(3,2)" alt="AI 3"></div></div> | |
</div> | |
<div class="dropzone mt-3" id="dropzone2"> | |
<p class="mb-2">Drag & Drop or Upload (.jpg/.png/.webp)</p> | |
<input type="file" id="fileInput2" accept="image/*" hidden> | |
<button class="btn btn-outline-secondary" onclick="document.getElementById('fileInput2').click()">Upload</button> | |
</div> | |
</div> | |
</div> | |
<div id="compareSection"> | |
<div class="d-flex justify-content-center align-items-center gap-5 mt-5"> | |
<img id="previewImage1" class="preview-image" alt="Original preview"> | |
<div class="d-flex flex-column align-items-center"> | |
<div id="compareButtonContainer" class="mb-3"></div> | |
<div id="scoreBlock" style="display:none;"> | |
<h2 class="fw-semibold mb-3">Fraud Score <span id="euclidVal" class="text-muted"></span>%</h2> | |
<div class="d-flex align-items-center gap-4"> | |
<div class="jauge-container mx-auto" id="gaugeWrapper"> | |
<div class="jauge"><div class="jauge-level" id="jaugeLevel"></div></div> | |
<div class="jauge-labels text-start"> | |
<span>most certainly</span><span>very probably</span><span>probably</span><span>possibly</span><span>probably not</span><span>definitely not</span> | |
</div> | |
</div> | |
<div class="icon-buttons"> | |
<button title="Print"><i class="fas fa-print"></i></button> | |
<button title="Certificate"><i class="fas fa-certificate"></i></button> | |
</div> | |
</div> | |
</div> | |
</div> | |
<img id="previewImage2" class="preview-image" alt="AI preview"> | |
</div> | |
<div id="results" class="mt-4"></div> | |
</div> | |
</div> | |
<script> | |
let file1=null, file2=null; | |
function resetSelection(target){ | |
for(let i=1;i<=3;i++){ | |
const thumb=document.getElementById(`preset-${i}-${target}`); | |
if(thumb)thumb.classList.remove('selected'); | |
} | |
} | |
function selectPreset(n,target){ | |
const imgEl=document.getElementById(`preset-${n}-${target}`); | |
const src=imgEl.src; | |
fetch(src).then(r=>r.blob()).then(blob=>{ | |
const file=new File([blob],src.split('/').pop(),{type:blob.type}); | |
if(target===1){file1=file;} else {file2=file;} | |
updatePreview(blob,target); | |
resetSelection(target); | |
imgEl.classList.add('selected'); | |
checkReady(); | |
}); | |
} | |
function updatePreview(fileOrBlob,target){ | |
const reader=new FileReader(); | |
reader.onload=e=>{ | |
const img=document.getElementById(`previewImage${target}`); | |
img.src=e.target.result; | |
img.classList.add('visible'); | |
document.getElementById('compareSection').style.display='block'; | |
}; | |
reader.readAsDataURL(fileOrBlob); | |
} | |
document.getElementById('fileInput1').addEventListener('change',e=>{ | |
if(e.target.files.length){file1=e.target.files[0];updatePreview(file1,1);resetSelection(1);checkReady();} | |
}); | |
document.getElementById('fileInput2').addEventListener('change',e=>{ | |
if(e.target.files.length){file2=e.target.files[0];updatePreview(file2,2);resetSelection(2);checkReady();} | |
}); | |
function checkReady(){ | |
const container=document.getElementById('compareButtonContainer'); | |
if(file1&&file2){ | |
container.innerHTML='<button class="btn btn-primary" onclick="processImages()">Calculate Fraud Score</button>'; | |
}else{ | |
container.innerHTML=''; | |
} | |
} | |
async function processImages(){ | |
const fd=new FormData(); | |
fd.append('image1',file1); | |
fd.append('image2',file2); | |
const resDiv=document.getElementById('results'); | |
resDiv.innerHTML='<div class="spinner-border text-primary"></div>'; | |
try{ | |
const res=await fetch('/process',{method:'POST',body:fd}); | |
const data=await res.json(); | |
const euclid=parseFloat(data.distance); | |
const fraud=Math.min(100,Math.max(2,185-2.73*euclid)); | |
document.getElementById('euclidVal').textContent=fraud.toFixed(1); | |
document.getElementById('scoreBlock').style.display='block'; | |
let color='#198754'; | |
if(fraud>90)color='#dc3545'; | |
else if(fraud>75)color='#fd7e14'; | |
else if(fraud>60)color='#ffc107'; | |
else if(fraud>45)color='#0dcaf0'; | |
else if(fraud>30)color='#0d6efd'; | |
const gaugeLevel=document.getElementById('jaugeLevel'); | |
gaugeLevel.style.height=`${Math.min(100,fraud)}%`; | |
gaugeLevel.style.backgroundColor=color; | |
document.getElementById('gaugeWrapper').style.display='flex'; | |
resDiv.innerHTML=''; | |
}catch(err){ | |
resDiv.innerHTML='<div class="text-danger">Error processing images.</div>'; | |
} | |
} | |
</script> | |
<script src="https://cdn.jsdelivr.net/npm/bootstrap@5.1.3/dist/js/bootstrap.bundle.min.js"></script> | |
</body> | |
</html> | |