Spaces:
Running
Running
<html lang="en"> | |
<head> | |
<meta charset="UTF-8"> | |
<meta name="viewport" content="width=device-width, initial-scale=1.0"> | |
<title>Professional Image Editor</title> | |
<style> | |
* { | |
margin: 0; | |
padding: 0; | |
box-sizing: border-box; | |
font-family: 'Segoe UI', Tahoma, Geneva, Verdana, sans-serif; | |
} | |
body { | |
background: linear-gradient(135deg, #1a1a2e, #16213e); | |
color: #fff; | |
min-height: 100vh; | |
padding: 20px; | |
} | |
.container { | |
max-width: 1200px; | |
margin: 0 auto; | |
padding: 20px; | |
} | |
header { | |
text-align: center; | |
margin-bottom: 30px; | |
padding: 20px; | |
background: rgba(255, 255, 255, 0.05); | |
border-radius: 15px; | |
box-shadow: 0 8px 32px rgba(0, 0, 0, 0.2); | |
backdrop-filter: blur(4px); | |
border: 1px solid rgba(255, 255, 255, 0.1); | |
} | |
h1 { | |
font-size: 2.5rem; | |
margin-bottom: 10px; | |
background: linear-gradient(45deg, #ff7e5f, #feb47b); | |
-webkit-background-clip: text; | |
background-clip: text; | |
color: transparent; | |
} | |
.subtitle { | |
font-size: 1.2rem; | |
color: #a0a0c0; | |
max-width: 700px; | |
margin: 0 auto; | |
line-height: 1.6; | |
} | |
.app-container { | |
display: grid; | |
grid-template-columns: 1fr 1fr; | |
gap: 30px; | |
margin-bottom: 40px; | |
} | |
@media (max-width: 768px) { | |
.app-container { | |
grid-template-columns: 1fr; | |
} | |
} | |
.panel { | |
background: rgba(30, 30, 50, 0.7); | |
border-radius: 15px; | |
padding: 25px; | |
box-shadow: 0 8px 32px rgba(0, 0, 0, 0.2); | |
backdrop-filter: blur(10px); | |
border: 1px solid rgba(255, 255, 255, 0.1); | |
} | |
.panel-title { | |
font-size: 1.5rem; | |
margin-bottom: 20px; | |
display: flex; | |
align-items: center; | |
gap: 10px; | |
color: #feb47b; | |
} | |
.panel-title i { | |
font-size: 1.8rem; | |
} | |
.form-group { | |
margin-bottom: 20px; | |
} | |
label { | |
display: block; | |
margin-bottom: 8px; | |
font-weight: 500; | |
color: #d0d0e0; | |
} | |
input, textarea { | |
width: 100%; | |
padding: 12px 15px; | |
border-radius: 8px; | |
border: 1px solid rgba(255, 255, 255, 0.2); | |
background: rgba(20, 20, 40, 0.8); | |
color: #fff; | |
font-size: 1rem; | |
} | |
textarea { | |
min-height: 100px; | |
resize: vertical; | |
} | |
input::placeholder, textarea::placeholder { | |
color: #8888a8; | |
} | |
.upload-area { | |
border: 2px dashed rgba(255, 255, 255, 0.2); | |
border-radius: 10px; | |
padding: 30px; | |
text-align: center; | |
cursor: pointer; | |
transition: all 0.3s ease; | |
background: rgba(20, 20, 40, 0.5); | |
} | |
.upload-area:hover { | |
border-color: #ff7e5f; | |
background: rgba(20, 20, 40, 0.7); | |
} | |
.upload-area i { | |
font-size: 3rem; | |
color: #ff7e5f; | |
margin-bottom: 15px; | |
} | |
.upload-text { | |
color: #a0a0c0; | |
margin-bottom: 15px; | |
} | |
.file-input { | |
display: none; | |
} | |
.btn { | |
background: linear-gradient(45deg, #ff7e5f, #feb47b); | |
color: white; | |
border: none; | |
padding: 14px 25px; | |
font-size: 1.1rem; | |
font-weight: 600; | |
border-radius: 8px; | |
cursor: pointer; | |
transition: all 0.3s ease; | |
width: 100%; | |
display: flex; | |
justify-content: center; | |
align-items: center; | |
gap: 10px; | |
} | |
.btn:hover { | |
transform: translateY(-3px); | |
box-shadow: 0 5px 15px rgba(255, 126, 95, 0.4); | |
} | |
.btn:disabled { | |
background: #555575; | |
cursor: not-allowed; | |
transform: none; | |
box-shadow: none; | |
} | |
.image-container { | |
position: relative; | |
overflow: hidden; | |
border-radius: 10px; | |
height: 400px; | |
display: flex; | |
align-items: center; | |
justify-content: center; | |
background: rgba(20, 20, 40, 0.5); | |
border: 1px solid rgba(255, 255, 255, 0.1); | |
} | |
.image-placeholder { | |
color: #8888a8; | |
text-align: center; | |
padding: 20px; | |
} | |
.image-placeholder i { | |
font-size: 4rem; | |
margin-bottom: 20px; | |
color: #555575; | |
} | |
img { | |
max-width: 100%; | |
max-height: 100%; | |
object-fit: contain; | |
display: none; | |
} | |
.loading { | |
display: none; | |
text-align: center; | |
padding: 20px; | |
} | |
.spinner { | |
border: 4px solid rgba(255, 255, 255, 0.3); | |
border-radius: 50%; | |
border-top: 4px solid #ff7e5f; | |
width: 40px; | |
height: 40px; | |
animation: spin 1s linear infinite; | |
margin: 0 auto 20px; | |
} | |
@keyframes spin { | |
0% { transform: rotate(0deg); } | |
100% { transform: rotate(360deg); } | |
} | |
.tips { | |
background: rgba(30, 30, 50, 0.7); | |
border-radius: 15px; | |
padding: 25px; | |
margin-top: 30px; | |
box-shadow: 0 8px 32px rgba(0, 0, 0, 0.2); | |
backdrop-filter: blur(10px); | |
border: 1px solid rgba(255, 255, 255, 0.1); | |
} | |
.tips h2 { | |
margin-bottom: 20px; | |
color: #feb47b; | |
} | |
.tip { | |
display: flex; | |
gap: 15px; | |
margin-bottom: 15px; | |
align-items: flex-start; | |
} | |
.tip i { | |
color: #ff7e5f; | |
font-size: 1.2rem; | |
margin-top: 3px; | |
} | |
.footer { | |
text-align: center; | |
margin-top: 40px; | |
padding: 20px; | |
color: #8888a8; | |
font-size: 0.9rem; | |
} | |
.api-info { | |
display: flex; | |
gap: 10px; | |
align-items: center; | |
justify-content: center; | |
margin-top: 10px; | |
} | |
.api-badge { | |
background: rgba(255, 126, 95, 0.2); | |
padding: 5px 10px; | |
border-radius: 20px; | |
font-size: 0.8rem; | |
} | |
</style> | |
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/6.4.0/css/all.min.css"> | |
</head> | |
<body> | |
<div class="container"> | |
<header> | |
<h1>Professional Image Editor</h1> | |
<p class="subtitle">Transform your images with AI-powered editing. Powered by Hugging Face's multimodalart/cosxl model running on CPU.</p> | |
</header> | |
<div class="app-container"> | |
<div class="panel"> | |
<h2 class="panel-title"><i class="fas fa-edit"></i> Edit Parameters</h2> | |
<div class="form-group"> | |
<label for="imageUpload">Upload Image</label> | |
<div class="upload-area" id="dropArea"> | |
<i class="fas fa-cloud-upload-alt"></i> | |
<p class="upload-text">Drag & drop your image here or click to browse</p> | |
<button class="btn" id="browseBtn"><i class="fas fa-folder-open"></i> Select Image</button> | |
<input type="file" id="imageUpload" accept="image/*" class="file-input"> | |
</div> | |
</div> | |
<div class="form-group"> | |
<label for="prompt">Edit Prompt</label> | |
<textarea id="prompt" placeholder="Describe what to edit (e.g. 'Change the dress color to blue', 'Add a hat', 'Make background blurry')"></textarea> | |
</div> | |
<div class="form-group"> | |
<label for="negativePrompt">Negative Prompt</label> | |
<input type="text" id="negativePrompt" value="blurry, distorted" placeholder="Things to avoid in the result"> | |
</div> | |
<div class="form-group"> | |
<label for="guidanceScale">Guidance Scale: <span id="scaleValue">7</span></label> | |
<input type="range" id="guidanceScale" min="1" max="15" step="0.5" value="7"> | |
</div> | |
<div class="form-group"> | |
<label for="steps">Processing Steps: <span id="stepsValue">20</span></label> | |
<input type="range" id="steps" min="5" max="50" value="20"> | |
</div> | |
<button class="btn" id="runEdit"> | |
<i class="fas fa-magic"></i> Apply Edits | |
</button> | |
</div> | |
<div class="panel"> | |
<h2 class="panel-title"><i class="fas fa-image"></i> Results</h2> | |
<div class="image-container" id="resultContainer"> | |
<div class="image-placeholder" id="originalPlaceholder"> | |
<i class="fas fa-image"></i> | |
<p>Original image will appear here</p> | |
</div> | |
<img id="originalImage"> | |
</div> | |
<div class="loading" id="loadingIndicator"> | |
<div class="spinner"></div> | |
<p>Processing your image... This may take 15-30 seconds</p> | |
<p><small>Using CPU-based model - slower than GPU but more accessible</small></p> | |
</div> | |
<div class="image-container" id="resultContainer"> | |
<div class="image-placeholder" id="resultPlaceholder"> | |
<i class="fas fa-star"></i> | |
<p>Edited result will appear here</p> | |
</div> | |
<img id="resultImage"> | |
</div> | |
</div> | |
</div> | |
<div class="tips"> | |
<h2><i class="fas fa-lightbulb"></i> Tips for Best Results</h2> | |
<div class="tip"> | |
<i class="fas fa-check-circle"></i> | |
<p>Be specific in your prompts: "Change the dress color to navy blue" works better than "Change color"</p> | |
</div> | |
<div class="tip"> | |
<i class="fas fa-check-circle"></i> | |
<p>Use negative prompts to avoid unwanted artifacts: "blurry, distorted, low quality"</p> | |
</div> | |
<div class="tip"> | |
<i class="fas fa-check-circle"></i> | |
<p>For clothing edits, mention the garment: "Recolor the dress to emerald green"</p> | |
</div> | |
<div class="tip"> | |
<i class="fas fa-check-circle"></i> | |
<p>Higher guidance scale (7-10) gives better adherence to prompts</p> | |
</div> | |
<div class="tip"> | |
<i class="fas fa-check-circle"></i> | |
<p>More steps (20-30) generally improve quality but take longer to process</p> | |
</div> | |
</div> | |
<div class="footer"> | |
<p>Professional Image Editor | Powered by Hugging Face's multimodalart/cosxl model</p> | |
<div class="api-info"> | |
<span class="api-badge">API: multimodalart/cosxl</span> | |
<span class="api-badge">Mode: CPU</span> | |
<span class="api-badge">Image Editing</span> | |
</div> | |
<p>Note: Processing may take 15-30 seconds as it uses CPU-based models</p> | |
</div> | |
</div> | |
<script> | |
document.addEventListener('DOMContentLoaded', function() { | |
// DOM elements | |
const imageUpload = document.getElementById('imageUpload'); | |
const dropArea = document.getElementById('dropArea'); | |
const browseBtn = document.getElementById('browseBtn'); | |
const originalImage = document.getElementById('originalImage'); | |
const originalPlaceholder = document.getElementById('originalPlaceholder'); | |
const resultImage = document.getElementById('resultImage'); | |
const resultPlaceholder = document.getElementById('resultPlaceholder'); | |
const runEditBtn = document.getElementById('runEdit'); | |
const loadingIndicator = document.getElementById('loadingIndicator'); | |
const promptInput = document.getElementById('prompt'); | |
const negativePromptInput = document.getElementById('negativePrompt'); | |
const guidanceScaleInput = document.getElementById('guidanceScale'); | |
const scaleValue = document.getElementById('scaleValue'); | |
const stepsInput = document.getElementById('steps'); | |
const stepsValue = document.getElementById('stepsValue'); | |
// Set initial slider values | |
scaleValue.textContent = guidanceScaleInput.value; | |
stepsValue.textContent = stepsInput.value; | |
// Event listeners for sliders | |
guidanceScaleInput.addEventListener('input', () => { | |
scaleValue.textContent = guidanceScaleInput.value; | |
}); | |
stepsInput.addEventListener('input', () => { | |
stepsValue.textContent = stepsInput.value; | |
}); | |
// File selection | |
browseBtn.addEventListener('click', () => { | |
imageUpload.click(); | |
}); | |
imageUpload.addEventListener('change', function(e) { | |
if (this.files && this.files[0]) { | |
handleImageUpload(this.files[0]); | |
} | |
}); | |
// Drag and drop | |
dropArea.addEventListener('dragover', (e) => { | |
e.preventDefault(); | |
dropArea.style.borderColor = '#ff7e5f'; | |
dropArea.style.backgroundColor = 'rgba(20, 20, 40, 0.9)'; | |
}); | |
dropArea.addEventListener('dragleave', () => { | |
dropArea.style.borderColor = 'rgba(255, 255, 255, 0.2)'; | |
dropArea.style.backgroundColor = 'rgba(20, 20, 40, 0.5)'; | |
}); | |
dropArea.addEventListener('drop', (e) => { | |
e.preventDefault(); | |
dropArea.style.borderColor = 'rgba(255, 255, 255, 0.2)'; | |
dropArea.style.backgroundColor = 'rgba(20, 20, 40, 0.5)'; | |
if (e.dataTransfer.files && e.dataTransfer.files[0]) { | |
handleImageUpload(e.dataTransfer.files[0]); | |
} | |
}); | |
// Handle image upload and preview | |
function handleImageUpload(file) { | |
if (!file.type.match('image.*')) { | |
alert('Please select an image file'); | |
return; | |
} | |
const reader = new FileReader(); | |
reader.onload = function(e) { | |
originalImage.src = e.target.result; | |
originalImage.style.display = 'block'; | |
originalPlaceholder.style.display = 'none'; | |
// Clear previous result | |
resultImage.style.display = 'none'; | |
resultPlaceholder.style.display = 'block'; | |
}; | |
reader.readAsDataURL(file); | |
} | |
// Run the edit process | |
runEditBtn.addEventListener('click', async () => { | |
// Validate inputs | |
if (!originalImage.src || originalImage.src === window.location.href) { | |
alert('Please upload an image first'); | |
return; | |
} | |
if (!promptInput.value.trim()) { | |
alert('Please enter an edit prompt'); | |
return; | |
} | |
// Show loading indicator | |
runEditBtn.disabled = true; | |
loadingIndicator.style.display = 'block'; | |
try { | |
// Create FormData | |
const formData = new FormData(); | |
// Convert data URL to blob | |
const blob = await fetch(originalImage.src).then(r => r.blob()); | |
formData.append('image', blob, 'image.png'); | |
// Add other parameters | |
formData.append('prompt', promptInput.value); | |
formData.append('negative_prompt', negativePromptInput.value); | |
formData.append('guidance_scale', guidanceScaleInput.value); | |
formData.append('steps', stepsInput.value); | |
// Make request to Hugging Face API | |
const response = await fetch('https://multimodalart-cosxl.hf.space/run/run_edit', { | |
method: 'POST', | |
body: formData | |
}); | |
if (!response.ok) { | |
throw new Error(`API request failed with status ${response.status}`); | |
} | |
// Get the result image | |
const resultBlob = await response.blob(); | |
const resultUrl = URL.createObjectURL(resultBlob); | |
// Display result | |
resultImage.src = resultUrl; | |
resultImage.style.display = 'block'; | |
resultPlaceholder.style.display = 'none'; | |
} catch (error) { | |
console.error('Error processing image:', error); | |
alert('Error processing image. Please try again.'); | |
} finally { | |
// Hide loading indicator | |
runEditBtn.disabled = false; | |
loadingIndicator.style.display = 'none'; | |
} | |
}); | |
// Example prompts | |
const examplePrompts = [ | |
"Change the dress color to royal blue", | |
"Make the background blurry with bokeh effect", | |
"Recolor the jacket to bright red", | |
"Add a sun hat to the person", | |
"Change the hair color to purple", | |
"Make the dress look like silk material" | |
]; | |
// Set a random example prompt | |
promptInput.placeholder = examplePrompts[Math.floor(Math.random() * examplePrompts.length)]; | |
}); | |
</script> | |
</body> | |
</html> |