mlbench123's picture
Upload 9 files
492772b verified
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Background Removal - AI Segmentation</title>
<style>
* {
margin: 0;
padding: 0;
box-sizing: border-box;
}
body {
font-family: 'Segoe UI', Tahoma, Geneva, Verdana, sans-serif;
background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
min-height: 100vh;
padding: 20px;
}
.container {
max-width: 1200px;
margin: 0 auto;
background: white;
border-radius: 20px;
box-shadow: 0 20px 60px rgba(0,0,0,0.3);
overflow: hidden;
}
header {
background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
color: white;
padding: 30px;
text-align: center;
}
header h1 {
font-size: 2.5em;
margin-bottom: 10px;
}
header p {
font-size: 1.1em;
opacity: 0.9;
}
.content {
padding: 40px;
}
.upload-section {
text-align: center;
margin-bottom: 40px;
}
.upload-zone {
border: 3px dashed #667eea;
border-radius: 15px;
padding: 60px 40px;
background: #f8f9ff;
cursor: pointer;
transition: all 0.3s;
position: relative;
}
.upload-zone:hover {
border-color: #764ba2;
background: #f0f2ff;
}
.upload-zone.dragover {
border-color: #764ba2;
background: #e8ebff;
transform: scale(1.02);
}
.upload-icon {
font-size: 4em;
color: #667eea;
margin-bottom: 20px;
}
.upload-text {
font-size: 1.2em;
color: #333;
margin-bottom: 10px;
}
.upload-hint {
color: #666;
font-size: 0.9em;
}
input[type="file"] {
display: none;
}
.controls {
display: grid;
grid-template-columns: repeat(auto-fit, minmax(200px, 1fr));
gap: 20px;
margin-bottom: 30px;
}
.control-group {
display: flex;
flex-direction: column;
}
.control-group label {
font-weight: 600;
margin-bottom: 8px;
color: #333;
}
select, input[type="range"] {
padding: 10px;
border: 2px solid #ddd;
border-radius: 8px;
font-size: 1em;
transition: border-color 0.3s;
}
select:focus, input[type="range"]:focus {
outline: none;
border-color: #667eea;
}
.threshold-value {
display: inline-block;
background: #667eea;
color: white;
padding: 4px 12px;
border-radius: 20px;
font-size: 0.9em;
margin-left: 10px;
}
.btn {
background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
color: white;
border: none;
padding: 15px 40px;
font-size: 1.1em;
font-weight: 600;
border-radius: 10px;
cursor: pointer;
transition: all 0.3s;
box-shadow: 0 4px 15px rgba(102, 126, 234, 0.4);
}
.btn:hover {
transform: translateY(-2px);
box-shadow: 0 6px 20px rgba(102, 126, 234, 0.6);
}
.btn:active {
transform: translateY(0);
}
.btn:disabled {
opacity: 0.5;
cursor: not-allowed;
}
.results {
display: grid;
grid-template-columns: repeat(auto-fit, minmax(300px, 1fr));
gap: 30px;
margin-top: 40px;
}
.result-card {
background: #f8f9ff;
border-radius: 15px;
padding: 20px;
box-shadow: 0 4px 10px rgba(0,0,0,0.1);
}
.result-card h3 {
color: #333;
margin-bottom: 15px;
font-size: 1.2em;
}
.result-card img {
width: 100%;
border-radius: 10px;
box-shadow: 0 2px 8px rgba(0,0,0,0.1);
}
.download-btn {
display: block;
width: 100%;
margin-top: 15px;
background: #10b981;
color: white;
padding: 10px;
text-align: center;
border-radius: 8px;
text-decoration: none;
font-weight: 600;
transition: background 0.3s;
}
.download-btn:hover {
background: #059669;
}
.loading {
text-align: center;
padding: 40px;
display: none;
}
.loading.active {
display: block;
}
.spinner {
border: 4px solid #f3f4f6;
border-top: 4px solid #667eea;
border-radius: 50%;
width: 50px;
height: 50px;
animation: spin 1s linear infinite;
margin: 0 auto 20px;
}
@keyframes spin {
0% { transform: rotate(0deg); }
100% { transform: rotate(360deg); }
}
.error {
background: #fee;
color: #c33;
padding: 15px;
border-radius: 8px;
margin-top: 20px;
display: none;
}
.error.active {
display: block;
}
.model-info {
background: #e8f4f8;
padding: 15px;
border-radius: 8px;
margin-top: 10px;
font-size: 0.9em;
color: #555;
}
</style>
</head>
<body>
<div class="container">
<header>
<h1>🎨 AI Background Removal</h1>
<p>Remove backgrounds from images using advanced AI models</p>
</header>
<div class="content">
<div class="upload-section">
<div class="upload-zone" id="uploadZone">
<div class="upload-icon">📁</div>
<div class="upload-text">Click to upload or drag & drop</div>
<div class="upload-hint">Supports: JPG, PNG, WEBP (Max 10MB)</div>
<input type="file" id="fileInput" accept="image/*">
</div>
</div>
<div class="controls">
<div class="control-group">
<label for="modelSelect">AI Model</label>
<select id="modelSelect">
<option value="u2netp" selected>U2NETP (Fast & Lightweight)</option>
<option value="birefnet">BiRefNet (Best Quality)</option>
<option value="rmbg">RMBG (Balanced)</option>
</select>
<div class="model-info" id="modelInfo">
⚡⚡⚡ Speed | ⭐⭐ Quality | 4.7 MB
</div>
</div>
<div class="control-group">
<label for="thresholdRange">
Threshold <span class="threshold-value" id="thresholdValue">0.5</span>
</label>
<input type="range" id="thresholdRange" min="0" max="1" step="0.1" value="0.5">
</div>
<div class="control-group">
<label for="outputType">Output Type</label>
<select id="outputType">
<option value="rgba" selected>Transparent PNG</option>
<option value="mask">Binary Mask</option>
<option value="both">Both</option>
</select>
</div>
</div>
<button class="btn" id="processBtn" disabled>Process Image</button>
<div class="loading" id="loading">
<div class="spinner"></div>
<p>Processing your image...</p>
</div>
<div class="error" id="error"></div>
<div class="results" id="results"></div>
</div>
</div>
<script>
const uploadZone = document.getElementById('uploadZone');
const fileInput = document.getElementById('fileInput');
const processBtn = document.getElementById('processBtn');
const loading = document.getElementById('loading');
const error = document.getElementById('error');
const results = document.getElementById('results');
const modelSelect = document.getElementById('modelSelect');
const modelInfo = document.getElementById('modelInfo');
const thresholdRange = document.getElementById('thresholdRange');
const thresholdValue = document.getElementById('thresholdValue');
const outputType = document.getElementById('outputType');
let selectedFile = null;
// Model information
const modelData = {
u2netp: { speed: '⚡⚡⚡', quality: '⭐⭐', size: '4.7 MB' },
birefnet: { speed: '⚡', quality: '⭐⭐⭐', size: '~400 MB' },
rmbg: { speed: '⚡⚡', quality: '⭐⭐⭐', size: '~200 MB' }
};
// Update model info
modelSelect.addEventListener('change', () => {
const model = modelData[modelSelect.value];
modelInfo.textContent = `${model.speed} Speed | ${model.quality} Quality | ${model.size}`;
});
// Update threshold value
thresholdRange.addEventListener('input', () => {
thresholdValue.textContent = thresholdRange.value;
});
// Upload zone click
uploadZone.addEventListener('click', () => {
fileInput.click();
});
// Drag and drop
uploadZone.addEventListener('dragover', (e) => {
e.preventDefault();
uploadZone.classList.add('dragover');
});
uploadZone.addEventListener('dragleave', () => {
uploadZone.classList.remove('dragover');
});
uploadZone.addEventListener('drop', (e) => {
e.preventDefault();
uploadZone.classList.remove('dragover');
if (e.dataTransfer.files.length > 0) {
handleFile(e.dataTransfer.files[0]);
}
});
// File input change
fileInput.addEventListener('change', (e) => {
if (e.target.files.length > 0) {
handleFile(e.target.files[0]);
}
});
function handleFile(file) {
if (!file.type.startsWith('image/')) {
showError('Please select an image file');
return;
}
if (file.size > 10 * 1024 * 1024) {
showError('File size must be less than 10MB');
return;
}
selectedFile = file;
processBtn.disabled = false;
uploadZone.querySelector('.upload-text').textContent = `Selected: ${file.name}`;
uploadZone.querySelector('.upload-icon').textContent = '✅';
hideError();
}
// Process button
processBtn.addEventListener('click', async () => {
if (!selectedFile) return;
const formData = new FormData();
formData.append('file', selectedFile);
formData.append('model', modelSelect.value);
formData.append('threshold', thresholdRange.value);
processBtn.disabled = true;
loading.classList.add('active');
results.innerHTML = '';
hideError();
try {
let response;
if (outputType.value === 'both') {
// Use base64 endpoint for both outputs
response = await fetch('/segment/base64', {
method: 'POST',
body: formData
});
const data = await response.json();
if (!response.ok) {
throw new Error(data.detail || 'Processing failed');
}
// Display results
results.innerHTML = '';
if (data.rgba) {
results.innerHTML += `
<div class="result-card">
<h3>Transparent PNG</h3>
<img src="${data.rgba}" alt="Transparent result">
<a href="${data.rgba}" download="transparent.png" class="download-btn">
Download PNG
</a>
</div>
`;
}
if (data.mask) {
results.innerHTML += `
<div class="result-card">
<h3>Binary Mask</h3>
<img src="${data.mask}" alt="Mask result">
<a href="${data.mask}" download="mask.png" class="download-btn">
Download Mask
</a>
</div>
`;
}
} else {
// Use appropriate endpoint
const endpoint = outputType.value === 'mask' ? '/segment/mask' : '/segment';
response = await fetch(endpoint, {
method: 'POST',
body: formData
});
if (!response.ok) {
const errorData = await response.json();
throw new Error(errorData.detail || 'Processing failed');
}
// Get blob
const blob = await response.blob();
const url = URL.createObjectURL(blob);
// Display result
const title = outputType.value === 'mask' ? 'Binary Mask' : 'Transparent PNG';
results.innerHTML = `
<div class="result-card">
<h3>${title}</h3>
<img src="${url}" alt="Result">
<a href="${url}" download="result.png" class="download-btn">
Download Image
</a>
</div>
`;
}
} catch (err) {
showError(err.message);
} finally {
loading.classList.remove('active');
processBtn.disabled = false;
}
});
function showError(message) {
error.textContent = message;
error.classList.add('active');
}
function hideError() {
error.classList.remove('active');
}
</script>
</body>
</html>