Spaces:
Running
Running
| // Theme toggle functionality | |
| document.addEventListener('DOMContentLoaded', () => { | |
| const themeToggle = document.getElementById('themeToggle'); | |
| const prefersDarkScheme = window.matchMedia('(prefers-color-scheme: dark)'); | |
| // Check for saved theme preference or use system preference | |
| const currentTheme = localStorage.getItem('theme') || | |
| (prefersDarkScheme.matches ? 'dark' : 'light'); | |
| // Apply the current theme | |
| if (currentTheme === 'dark') { | |
| document.documentElement.classList.add('dark'); | |
| themeToggle.innerHTML = '<i data-feather="sun" class="mr-2"></i> Light Mode'; | |
| } else { | |
| document.documentElement.classList.remove('dark'); | |
| themeToggle.innerHTML = '<i data-feather="moon" class="mr-2"></i> Dark Mode'; | |
| } | |
| // Set up the toggle button | |
| themeToggle.addEventListener('click', () => { | |
| const isDark = document.documentElement.classList.contains('dark'); | |
| if (isDark) { | |
| document.documentElement.classList.remove('dark'); | |
| localStorage.setItem('theme', 'light'); | |
| themeToggle.innerHTML = '<i data-feather="moon" class="mr-2"></i> Dark Mode'; | |
| } else { | |
| document.documentElement.classList.add('dark'); | |
| localStorage.setItem('theme', 'dark'); | |
| themeToggle.innerHTML = '<i data-feather="sun" class="mr-2"></i> Light Mode'; | |
| } | |
| feather.replace(); | |
| }); | |
| // Watch for system theme changes | |
| prefersDarkScheme.addEventListener('change', e => { | |
| if (localStorage.getItem('theme') === null) { | |
| if (e.matches) { | |
| document.documentElement.classList.add('dark'); | |
| } else { | |
| document.documentElement.classList.remove('dark'); | |
| } | |
| } | |
| }); | |
| // MediaPipe Selfie Segmentation setup | |
| let selfieSegmentation; | |
| let segmentationActive = false; | |
| async function setupSegmentation() { | |
| selfieSegmentation = new SelfieSegmentation({ | |
| locateFile: (file) => { | |
| return `https://cdn.jsdelivr.net/npm/@mediapipe/selfie_segmentation/${file}`; | |
| } | |
| }); | |
| selfieSegmentation.setOptions({ | |
| modelSelection: 1, // 0 for general segmentation, 1 for landscape (better for full body) | |
| selfieMode: false | |
| }); | |
| await selfieSegmentation.initialize(); | |
| } | |
| // Image upload and clothing selection functionality | |
| const imageUpload = document.getElementById('imageUpload'); | |
| const imagePreview = document.getElementById('imagePreview'); | |
| const previewImage = document.getElementById('previewImage'); | |
| const toolsPanel = document.getElementById('toolsPanel'); | |
| const selectClothesBtn = document.getElementById('selectClothes'); | |
| const clearSelectionBtn = document.getElementById('clearSelection'); | |
| const downloadSelectionBtn = document.getElementById('downloadSelection'); | |
| const selectionCanvas = document.getElementById('selectionCanvas'); | |
| const ctx = selectionCanvas.getContext('2d'); | |
| let imageLoaded = false; | |
| let selectionMode = false; | |
| let selectionPoints = []; | |
| imageUpload.addEventListener('change', function(e) { | |
| const file = e.target.files[0]; | |
| if (file) { | |
| const reader = new FileReader(); | |
| reader.onload = function(event) { | |
| previewImage.onload = function() { | |
| // Set canvas dimensions to match image | |
| selectionCanvas.width = previewImage.width; | |
| selectionCanvas.height = previewImage.height; | |
| imageLoaded = true; | |
| toolsPanel.classList.remove('hidden'); | |
| }; | |
| previewImage.src = event.target.result; | |
| imagePreview.classList.remove('hidden'); | |
| selectionPoints = []; | |
| ctx.clearRect(0, 0, selectionCanvas.width, selectionCanvas.height); | |
| } | |
| reader.readAsDataURL(file); | |
| } | |
| }); | |
| // Initialize clothing selection | |
| selectClothesBtn.addEventListener('click', async function() { | |
| if (!selfieSegmentation) { | |
| await setupSegmentation(); | |
| } | |
| selectionMode = true; | |
| segmentationActive = true; | |
| selectionPoints = []; | |
| ctx.clearRect(0, 0, selectionCanvas.width, selectionCanvas.height); | |
| previewImage.style.cursor = 'crosshair'; | |
| // Process image with segmentation model | |
| if (imageLoaded) { | |
| await processSegmentation(); | |
| } | |
| }); | |
| // Clear selection | |
| clearSelectionBtn.addEventListener('click', function() { | |
| selectionPoints = []; | |
| ctx.clearRect(0, 0, selectionCanvas.width, selectionCanvas.height); | |
| previewImage.style.cursor = 'default'; | |
| selectionMode = false; | |
| segmentationActive = false; | |
| }); | |
| // Download selection | |
| downloadSelectionBtn.addEventListener('click', function() { | |
| if (selectionPoints.length > 0) { | |
| const tempCanvas = document.createElement('canvas'); | |
| const tempCtx = tempCanvas.getContext('2d'); | |
| tempCanvas.width = previewImage.width; | |
| tempCanvas.height = previewImage.height; | |
| // Draw the selected area | |
| tempCtx.drawImage(previewImage, 0, 0); | |
| // Create mask for selection | |
| tempCtx.globalCompositeOperation = 'destination-in'; | |
| tempCtx.fillStyle = 'black'; | |
| tempCtx.beginPath(); | |
| tempCtx.moveTo(selectionPoints[0].x, selectionPoints[0].y); | |
| for (let i = 1; i < selectionPoints.length; i++) { | |
| tempCtx.lineTo(selectionPoints[i].x, selectionPoints[i].y); | |
| } | |
| tempCtx.closePath(); | |
| tempCtx.fill(); | |
| // Create download link | |
| const link = document.createElement('a'); | |
| link.download = 'selected-clothing.png'; | |
| link.href = tempCanvas.toDataURL('image/png'); | |
| link.click(); | |
| } | |
| }); | |
| // Process image with segmentation model | |
| async function processSegmentation() { | |
| if (!selfieSegmentation || !imageLoaded) return; | |
| try { | |
| const result = await selfieSegmentation.send({image: previewImage}); | |
| const mask = result.segmentationMask; | |
| // Draw the segmentation mask | |
| ctx.clearRect(0, 0, selectionCanvas.width, selectionCanvas.height); | |
| ctx.drawImage(mask, 0, 0, selectionCanvas.width, selectionCanvas.height); | |
| // Apply the mask to highlight clothing | |
| ctx.globalCompositeOperation = 'source-in'; | |
| ctx.fillStyle = 'rgba(59, 130, 246, 0.5)'; | |
| ctx.fillRect(0, 0, selectionCanvas.width, selectionCanvas.height); | |
| ctx.globalCompositeOperation = 'source-over'; | |
| } catch (error) { | |
| console.error('Segmentation error:', error); | |
| } | |
| } | |
| // Handle selection drawing | |
| imagePreview.addEventListener('click', function(e) { | |
| if (!selectionMode || !imageLoaded) return; | |
| const rect = previewImage.getBoundingClientRect(); | |
| const x = e.clientX - rect.left; | |
| const y = e.clientY - rect.top; | |
| selectionPoints.push({x, y}); | |
| // Draw selection | |
| ctx.clearRect(0, 0, selectionCanvas.width, selectionCanvas.height); | |
| ctx.strokeStyle = '#3B82F6'; | |
| ctx.lineWidth = 2; | |
| ctx.fillStyle = 'rgba(59, 130, 246, 0.2)'; | |
| if (selectionPoints.length > 0) { | |
| ctx.beginPath(); | |
| ctx.moveTo(selectionPoints[0].x, selectionPoints[0].y); | |
| for (let i = 1; i < selectionPoints.length; i++) { | |
| ctx.lineTo(selectionPoints[i].x, selectionPoints[i].y); | |
| } | |
| if (selectionPoints.length > 2) { | |
| ctx.closePath(); | |
| ctx.fill(); | |
| } | |
| ctx.stroke(); | |
| } | |
| }); | |
| }); |