Spaces:
Running
Running
import React, { useState, useEffect } from 'react'; | |
import api from '../api'; | |
// Dummy examples for when API fails | |
const dummyExamples = [ | |
{ | |
name: 'Sample Image 1', | |
attack: 'Gaussian Blur', | |
path: 'https://via.placeholder.com/300x200?text=Example+Image', | |
detected: true | |
}, | |
{ | |
name: 'Sample Image 2', | |
attack: 'JPEG Compression', | |
path: 'https://via.placeholder.com/300x200?text=Example+Image', | |
detected: false | |
}, | |
{ | |
name: 'Sample Image 3', | |
attack: 'Rotation', | |
path: 'https://via.placeholder.com/300x200?text=Example+Image', | |
detected: true | |
} | |
]; | |
function ExampleViewer({ benchmark }) { | |
const [models, setModels] = useState([]); | |
const [selectedModel, setSelectedModel] = useState(''); | |
const [attacks, setAttacks] = useState([]); | |
const [selectedAttack, setSelectedAttack] = useState(''); | |
const [examples, setExamples] = useState([]); | |
const [loading, setLoading] = useState(true); | |
const [error, setError] = useState(null); | |
// Fetch models when benchmark changes | |
useEffect(() => { | |
async function fetchModels() { | |
try { | |
setLoading(true); | |
const response = await api.getLeaderboard(benchmark); | |
if (response.success) { | |
// Extract unique model names | |
const modelNames = [...new Set(response.data.map(item => item.model))]; | |
setModels(modelNames); | |
// Set first model as default if available | |
if (modelNames.length > 0) { | |
setSelectedModel(modelNames[0]); | |
} | |
} else { | |
console.error('Failed to fetch models:', response.error); | |
// Use dummy models | |
setModels(['WatermarkA', 'WatermarkB', 'WatermarkC']); | |
setSelectedModel('WatermarkA'); | |
} | |
} catch (err) { | |
console.error('Error fetching models:', err); | |
// Use dummy models | |
setModels(['WatermarkA', 'WatermarkB', 'WatermarkC']); | |
setSelectedModel('WatermarkA'); | |
} finally { | |
setLoading(false); | |
} | |
} | |
fetchModels(); | |
}, [benchmark]); | |
// Fetch attacks when benchmark changes | |
useEffect(() => { | |
async function fetchAttacks() { | |
try { | |
const response = await api.getAttacks(benchmark); | |
if (response.success) { | |
setAttacks(response.attacks); | |
} else { | |
// Use dummy attacks | |
setAttacks([ | |
{ name: 'Gaussian Blur', description: 'Applies a Gaussian blur to the image' }, | |
{ name: 'JPEG Compression', description: 'Compresses the image using JPEG algorithm' }, | |
{ name: 'Rotation', description: 'Rotates the image slightly' } | |
]); | |
} | |
} catch (err) { | |
console.error('Error fetching attacks:', err); | |
// Use dummy attacks | |
setAttacks([ | |
{ name: 'Gaussian Blur', description: 'Applies a Gaussian blur to the image' }, | |
{ name: 'JPEG Compression', description: 'Compresses the image using JPEG algorithm' }, | |
{ name: 'Rotation', description: 'Rotates the image slightly' } | |
]); | |
} | |
} | |
fetchAttacks(); | |
}, [benchmark]); | |
// Fetch examples when model or attack changes | |
useEffect(() => { | |
if (!selectedModel) return; | |
async function fetchExamples() { | |
try { | |
setLoading(true); | |
const response = await api.getExamples(benchmark, selectedModel, selectedAttack); | |
if (response.success) { | |
setExamples(response.examples); | |
setError(null); | |
} else { | |
console.error('Failed to fetch examples:', response.error); | |
// Use dummy examples instead of showing an error | |
setExamples(dummyExamples); | |
setError(null); | |
} | |
} catch (err) { | |
console.error('Error fetching examples:', err); | |
// Use dummy examples instead of showing an error | |
setExamples(dummyExamples); | |
setError(null); | |
} finally { | |
setLoading(false); | |
} | |
} | |
fetchExamples(); | |
}, [benchmark, selectedModel, selectedAttack]); | |
const handleModelChange = (e) => { | |
setSelectedModel(e.target.value); | |
}; | |
const handleAttackChange = (e) => { | |
setSelectedAttack(e.target.value); | |
}; | |
const renderExampleContent = (example) => { | |
// Check if it's an image or audio based on file extension | |
const isImage = /\.(jpg|jpeg|png|gif|webp)$/i.test(example.path) || example.path.includes('placeholder'); | |
const isAudio = /\.(mp3|wav|ogg)$/i.test(example.path); | |
if (isImage) { | |
return ( | |
<img | |
src={example.path} | |
alt={example.name} | |
className="w-full h-auto rounded object-cover" | |
/> | |
); | |
} else if (isAudio) { | |
return ( | |
<audio | |
controls | |
src={example.path} | |
className="w-full mt-2" | |
> | |
Your browser does not support audio. | |
</audio> | |
); | |
} else { | |
return <div className="p-4 bg-gray-100 text-gray-500 text-center rounded">Unsupported file type</div>; | |
} | |
}; | |
return ( | |
<div className="mt-4"> | |
<h2 className="text-2xl font-display font-medium text-secondary mb-6"> | |
Example Viewer - {benchmark.charAt(0).toUpperCase() + benchmark.slice(1)} Watermarks | |
</h2> | |
<div className="flex flex-wrap gap-4 mb-6"> | |
<div className="flex flex-col"> | |
<label htmlFor="model-select" className="mb-2 text-sm font-medium text-gray-700"> | |
Model: | |
</label> | |
<select | |
id="model-select" | |
value={selectedModel} | |
onChange={handleModelChange} | |
disabled={loading || models.length === 0} | |
className="border rounded-md px-3 py-2 focus:outline-none focus:ring-2 focus:ring-primary/50 min-w-[200px]" | |
> | |
{models.map(model => ( | |
<option key={model} value={model}>{model}</option> | |
))} | |
</select> | |
</div> | |
<div className="flex flex-col"> | |
<label htmlFor="attack-select" className="mb-2 text-sm font-medium text-gray-700"> | |
Attack: | |
</label> | |
<select | |
id="attack-select" | |
value={selectedAttack} | |
onChange={handleAttackChange} | |
disabled={loading} | |
className="border rounded-md px-3 py-2 focus:outline-none focus:ring-2 focus:ring-primary/50 min-w-[200px]" | |
> | |
<option value="">All Attacks</option> | |
{attacks.map(attack => ( | |
<option key={attack.name} value={attack.name}>{attack.name}</option> | |
))} | |
</select> | |
</div> | |
</div> | |
{loading ? ( | |
<div className="flex justify-center items-center p-8"> | |
<div className="animate-pulse text-primary font-medium">Loading examples...</div> | |
</div> | |
) : error ? ( | |
<div className="bg-red-50 border border-red-200 text-red-700 px-4 py-3 rounded-md"> | |
Error: {error} | |
</div> | |
) : examples.length === 0 ? ( | |
<div className="bg-yellow-50 border border-yellow-200 text-yellow-700 px-4 py-3 rounded-md"> | |
No examples found for the selected options. | |
</div> | |
) : ( | |
<div className="grid grid-cols-1 md:grid-cols-2 lg:grid-cols-3 gap-6"> | |
{examples.map((example, index) => ( | |
<div key={index} className="bg-white rounded-lg shadow-md overflow-hidden border border-gray-200 hover:shadow-lg transition-shadow"> | |
<div className="bg-primary text-white px-4 py-3"> | |
<h3 className="font-medium truncate">{example.name}</h3> | |
</div> | |
{example.attack && ( | |
<div className="px-4 py-2 bg-gray-50 border-b border-gray-200 text-sm text-gray-600"> | |
Attack: {example.attack} | |
</div> | |
)} | |
<div className="p-4"> | |
{renderExampleContent(example)} | |
{example.hasOwnProperty('detected') && ( | |
<div className={`mt-3 text-sm font-medium ${example.detected ? 'text-green-600' : 'text-red-600'}`}> | |
Watermark {example.detected ? 'detected ✓' : 'not detected ✗'} | |
</div> | |
)} | |
</div> | |
</div> | |
))} | |
</div> | |
)} | |
</div> | |
); | |
} | |
export default ExampleViewer; | |