| <!DOCTYPE html> |
| <html lang="en"> |
| <head> |
| <meta charset="UTF-8"> |
| <meta name="viewport" content="width=device-width, initial-scale=1.0"> |
| <title>Visionary Collage Creator</title> |
| <link rel="icon" type="image/x-icon" href="/static/favicon.ico"> |
| <script src="https://cdn.tailwindcss.com"></script> |
| <script src="https://cdn.jsdelivr.net/npm/feather-icons/dist/feather.min.js"></script> |
| <script src="https://unpkg.com/feather-icons"></script> |
| <script src="https://cdn.jsdelivr.net/npm/vanta@latest/dist/vanta.globe.min.js"></script> |
| <script src="https://cdn.jsdelivr.net/npm/@tensorflow/tfjs@3.11.0/dist/tf.min.js"></script> |
| <script src="https://cdn.jsdelivr.net/npm/@tensorflow-models/coco-ssd@2.1.0/dist/coco-ssd.min.js"></script> |
| <style> |
| .collage-item { |
| transition: all 0.3s ease; |
| position: relative; |
| } |
| .collage-item:hover { |
| transform: scale(1.05); |
| z-index: 10; |
| } |
| .camera-container { |
| border-radius: 12px; |
| overflow: hidden; |
| box-shadow: 0 25px 50px -12px rgba(0, 0, 0, 0.25); |
| } |
| .collage-container { |
| min-height: 400px; |
| background: rgba(255, 255, 255, 0.1); |
| backdrop-filter: blur(10px); |
| border-radius: 16px; |
| box-shadow: 0 4px 30px rgba(0, 0, 0, 0.1); |
| border: 1px solid rgba(255, 255, 255, 0.3); |
| overflow: hidden; |
| } |
| .pulse { |
| animation: pulse 2s infinite; |
| } |
| @keyframes pulse { |
| 0% { box-shadow: 0 0 0 0 rgba(59, 130, 246, 0.4); } |
| 70% { box-shadow: 0 0 0 10px rgba(59, 130, 246, 0); } |
| 100% { box-shadow: 0 0 0 0 rgba(59, 130, 246, 0); } |
| } |
| .floating { |
| animation: floating 3s ease-in-out infinite; |
| } |
| @keyframes floating { |
| 0% { transform: translateY(0px); } |
| 50% { transform: translateY(-10px); } |
| 100% { transform: translateY(0px); } |
| } |
| .swaying { |
| animation: swaying 4s ease-in-out infinite; |
| } |
| @keyframes swaying { |
| 0% { transform: rotate(0deg); } |
| 50% { transform: rotate(3deg); } |
| 100% { transform: rotate(0deg); } |
| } |
| .rotating { |
| animation: rotating 8s linear infinite; |
| } |
| @keyframes rotating { |
| 0% { transform: rotate(0deg); } |
| 100% { transform: rotate(360deg); } |
| } |
| .bounce { |
| animation: bounce 2s infinite; |
| } |
| @keyframes bounce { |
| 0%, 100% { transform: translateY(0); } |
| 50% { transform: translateY(-15px); } |
| } |
| </style> |
| </head> |
| <body class="bg-gradient-to-br from-gray-900 to-blue-900 min-h-screen text-white"> |
| |
| <div id="vanta-bg" class="fixed top-0 left-0 w-full h-full -z-10"></div> |
|
|
| |
| <header class="container mx-auto px-4 py-8"> |
| <div class="flex flex-col md:flex-row justify-between items-center"> |
| <div class="flex items-center mb-6 md:mb-0"> |
| <i data-feather="camera" class="mr-3 text-blue-400" size="32"></i> |
| <h1 class="text-3xl font-bold bg-clip-text text-transparent bg-gradient-to-r from-blue-400 to-purple-500"> |
| Visionary Collage Creator |
| </h1> |
| </div> |
| <div class="flex space-x-4"> |
| <button id="start-camera" class="bg-blue-600 hover:bg-blue-700 text-white px-6 py-2 rounded-full flex items-center transition-all"> |
| <i data-feather="video" class="mr-2"></i> Start Camera |
| </button> |
| <button id="capture-btn" class="bg-purple-600 hover:bg-purple-700 text-white px-6 py-2 rounded-full flex items-center transition-all pulse"> |
| <i data-feather="camera" class="mr-2"></i> Capture |
| </button> |
| </div> |
| </div> |
| </header> |
| <main class="container mx-auto px-4 py-8"> |
| |
| <div class="flex justify-center mb-8"> |
| <button id="camera-toggle" class="camera-container bg-black/30 p-6 rounded-2xl flex flex-col items-center cursor-pointer transition-all hover:bg-black/50"> |
| <i data-feather="camera" class="mb-2 text-blue-400" size="32"></i> |
| <span class="text-lg font-semibold">Open Camera</span> |
| </button> |
| </div> |
|
|
| |
| <div id="camera-view" class="hidden flex justify-center mb-8"> |
| <div class="camera-container relative"> |
| <video id="video" autoplay playsinline class="w-full max-w-2xl rounded-lg"></video> |
| <div id="loading" class="absolute inset-0 bg-black/70 flex items-center justify-center hidden"> |
| <div class="animate-spin rounded-full h-12 w-12 border-t-2 border-b-2 border-blue-500"></div> |
| </div> |
| </div> |
| </div> |
|
|
| |
| <div class="text-center mb-8"> |
| <p id="prediction-text" class="text-lg">Point your camera at an object and capture to begin</p> |
| </div> |
|
|
| |
| <div class="collage-container p-6 rounded-2xl"> |
| <div class="flex justify-between items-center mb-4"> |
| <h2 class="text-xl font-semibold flex items-center"> |
| <i data-feather="grid" class="mr-2 text-purple-400"></i> Dynamic Collage |
| </h2> |
| <button id="clear-collage" class="text-sm bg-red-600 hover:bg-red-700 px-3 py-1 rounded-full flex items-center"> |
| <i data-feather="trash-2" class="mr-1" size="16"></i> Clear |
| </button> |
| </div> |
| <div id="collage" class="grid grid-cols-2 sm:grid-cols-3 md:grid-cols-4 lg:grid-cols-6 gap-4"> |
| <div class="col-span-full text-center py-12 text-gray-400"> |
| <i data-feather="image" class="mb-2" size="48"></i> |
| <p>Capture an image to start building your collage</p> |
| </div> |
| </div> |
| </div> |
| |
| <section class="mt-16 bg-black/20 backdrop-blur-sm rounded-2xl p-8"> |
| <h2 class="text-2xl font-bold text-center mb-8">How It Works</h2> |
| <div class="grid grid-cols-1 md:grid-cols-3 gap-8"> |
| <div class="text-center p-6 bg-white/5 rounded-xl"> |
| <div class="w-16 h-16 bg-blue-500 rounded-full flex items-center justify-center mx-auto mb-4"> |
| <i data-feather="camera" size="24"></i> |
| </div> |
| <h3 class="text-xl font-semibold mb-2">Capture Image</h3> |
| <p class="text-gray-300">Point your camera at any object and capture an image with one click</p> |
| </div> |
| <div class="text-center p-6 bg-white/5 rounded-xl"> |
| <div class="w-16 h-16 bg-purple-500 rounded-full flex items-center justify-center mx-auto mb-4"> |
| <i data-feather="cpu" size="24"></i> |
| </div> |
| <h3 class="text-xl font-semibold mb-2">AI Detection</h3> |
| <p class="text-gray-300">Our ML model identifies the object and finds related images</p> |
| </div> |
| <div class="text-center p-6 bg-white/5 rounded-xl"> |
| <div class="w-16 h-16 bg-green-500 rounded-full flex items-center justify-center mx-auto mb-4"> |
| <i data-feather="layout" size="24"></i> |
| </div> |
| <h3 class="text-xl font-semibold mb-2">Dynamic Collage</h3> |
| <p class="text-gray-300">Related images are automatically added to your personalized collage</p> |
| </div> |
| </div> |
| </section> |
| </main> |
|
|
| <footer class="container mx-auto px-4 py-8 mt-12 text-center text-gray-400"> |
| <p>Visionary Collage Creator © 2023 | Powered by Machine Learning</p> |
| </footer> |
|
|
| <script> |
| |
| feather.replace(); |
| |
| |
| VANTA.GLOBE({ |
| el: "#vanta-bg", |
| mouseControls: true, |
| touchControls: true, |
| gyroControls: false, |
| minHeight: 200.00, |
| minWidth: 200.00, |
| scale: 1.00, |
| scaleMobile: 1.00, |
| color: 0x3b82f6, |
| color2: 0x8b5cf6, |
| backgroundColor: 0x0f172a |
| }); |
| |
| |
| const video = document.getElementById('video'); |
| const startCameraBtn = document.getElementById('start-camera'); |
| const captureBtn = document.getElementById('capture-btn'); |
| const predictionText = document.getElementById('prediction-text'); |
| const collage = document.getElementById('collage'); |
| const loading = document.getElementById('loading'); |
| const clearCollageBtn = document.getElementById('clear-collage'); |
| const cameraToggle = document.getElementById('camera-toggle'); |
| |
| |
| let stream = null; |
| |
| |
| cameraToggle.addEventListener('click', async () => { |
| const cameraView = document.getElementById('camera-view'); |
| if (cameraView.classList.contains('hidden')) { |
| |
| try { |
| stream = await navigator.mediaDevices.getUserMedia({ video: true }); |
| video.srcObject = stream; |
| cameraView.classList.remove('hidden'); |
| cameraToggle.innerHTML = '<i data-feather="x" class="mb-2 text-blue-400" size="32"></i><span class="text-lg font-semibold">Close Camera</span>'; |
| feather.replace(); |
| } catch (err) { |
| console.error("Error accessing camera:", err); |
| predictionText.textContent = "Error accessing camera. Please check permissions."; |
| predictionText.classList.add('text-red-400'); |
| } |
| } else { |
| |
| if (stream) { |
| stream.getTracks().forEach(track => track.stop()); |
| stream = null; |
| } |
| video.srcObject = null; |
| cameraView.classList.add('hidden'); |
| cameraToggle.innerHTML = '<i data-feather="camera" class="mb-2 text-blue-400" size="32"></i><span class="text-lg font-semibold">Open Camera</span>'; |
| feather.replace(); |
| } |
| }); |
| |
| |
| let model; |
| cocoSsd.load().then(loadedModel => { |
| model = loadedModel; |
| console.log("COCO-SSD model loaded"); |
| }).catch(err => { |
| console.error("Error loading model:", err); |
| predictionText.textContent = "Failed to load detection model"; |
| predictionText.classList.add('text-red-400'); |
| }); |
| |
| |
| captureBtn.addEventListener('click', async () => { |
| if (!stream) { |
| predictionText.textContent = "Please open the camera first"; |
| predictionText.classList.add('text-yellow-400'); |
| return; |
| } |
| |
| if (!model) { |
| predictionText.textContent = "Model is still loading..."; |
| predictionText.classList.add('text-yellow-400'); |
| return; |
| } |
| |
| loading.classList.remove('hidden'); |
| captureBtn.classList.add('opacity-50', 'cursor-not-allowed'); |
| |
| try { |
| |
| const canvas = document.createElement('canvas'); |
| canvas.width = video.videoWidth; |
| canvas.height = video.videoHeight; |
| const ctx = canvas.getContext('2d'); |
| ctx.drawImage(video, 0, 0, canvas.width, canvas.height); |
| |
| |
| const predictions = await model.detect(canvas); |
| |
| if (predictions.length > 0) { |
| |
| const topPrediction = predictions[0]; |
| const detectedObject = topPrediction.class; |
| |
| predictionText.textContent = `Detected: ${detectedObject} (${Math.round(topPrediction.score * 100)}% confidence)`; |
| predictionText.classList.remove('text-yellow-400', 'text-red-400'); |
| predictionText.classList.add('text-green-400'); |
| |
| |
| addToCollage(detectedObject); |
| } else { |
| predictionText.textContent = "No objects detected. Try another image."; |
| predictionText.classList.remove('text-green-400', 'text-yellow-400'); |
| predictionText.classList.add('text-red-400'); |
| } |
| } catch (error) { |
| console.error("Detection error:", error); |
| predictionText.textContent = "Detection failed. Please try again."; |
| predictionText.classList.add('text-red-400'); |
| } finally { |
| loading.classList.add('hidden'); |
| captureBtn.classList.remove('opacity-50', 'cursor-not-allowed'); |
| } |
| }); |
| |
| |
| function addToCollage(object) { |
| |
| if (collage.children.length === 1 && collage.children[0].classList.contains('col-span-full')) { |
| collage.innerHTML = ''; |
| } |
| |
| |
| for (let i = 0; i < 12; i++) { |
| |
| const collageItem = document.createElement('div'); |
| collageItem.className = 'collage-item rounded-lg overflow-hidden shadow-lg transform transition-transform hover:scale-105'; |
| |
| |
| const animations = ['floating', 'swaying', 'rotating', 'bounce']; |
| const randomAnimation = animations[Math.floor(Math.random() * animations.length)]; |
| collageItem.classList.add(randomAnimation); |
| |
| |
| const seed = Math.floor(Math.random() * 1000); |
| const imageUrl = `http://static.photos/${object}/320x240/${seed}`; |
| |
| collageItem.innerHTML = ` |
| <img src="${imageUrl}" alt="${object}" class="w-full h-32 object-cover"> |
| <div class="p-2 bg-black/50 text-sm truncate">${object}</div> |
| `; |
| |
| collage.appendChild(collageItem); |
| } |
| } |
| |
| |
| clearCollageBtn.addEventListener('click', () => { |
| collage.innerHTML = ` |
| <div class="col-span-full text-center py-12 text-gray-400"> |
| <i data-feather="image" class="mb-2" size="48"></i> |
| <p>Capture an image to start building your collage</p> |
| </div> |
| `; |
| feather.replace(); |
| }); |
| </script> |
| </body> |
| </html> |
|
|