Spaces:
Running
Running
| <html lang="tr"> | |
| <head> | |
| <meta charset="UTF-8"> | |
| <meta name="viewport" content="width=device-width, initial-scale=1.0"> | |
| <title>Teachable Machine Görüntü Modeli</title> | |
| <style> | |
| .notification { | |
| position: fixed; | |
| top: 50%; | |
| left: 50%; | |
| transform: translate(-50%, -50%); | |
| background-color: #3498db; | |
| color: white; | |
| padding: 25px 35px; | |
| border-radius: 16px; | |
| box-shadow: 0 15px 30px rgba(0,0,0,0.3); | |
| z-index: 1000; | |
| font-size: 1.5em; | |
| text-align: center; | |
| min-width: 300px; | |
| animation: fadeIn 0.3s; | |
| font-weight: bold; | |
| display: flex; | |
| align-items: center; | |
| justify-content: space-between; | |
| } | |
| .notification-close { | |
| background: none; | |
| border: none; | |
| color: white; | |
| font-size: 1.2em; | |
| cursor: pointer; | |
| margin-left: 20px; | |
| padding: 5px; | |
| } | |
| @keyframes fadeIn { | |
| from { opacity: 0; transform: translate(-50%, -70%); } | |
| to { opacity: 1; transform: translate(-50%, -50%); } | |
| } | |
| @keyframes fadeOut { | |
| from { opacity: 1; } | |
| to { opacity: 0; } | |
| } | |
| body { | |
| font-family: 'Segoe UI', Tahoma, Geneva, Verdana, sans-serif; | |
| display: flex; | |
| flex-direction: column; | |
| align-items: center; | |
| margin: 0; | |
| padding: 20px; | |
| background: linear-gradient(135deg, #f5f7fa 0%, #c3cfe2 100%); | |
| min-height: 100vh; | |
| color: #333; | |
| } | |
| .container { | |
| background-color: white; | |
| border-radius: 16px; | |
| box-shadow: 0 10px 30px rgba(0,0,0,0.1); | |
| padding: 40px; | |
| max-width: 900px; | |
| width: 100%; | |
| margin: 20px auto; | |
| transition: all 0.3s ease; | |
| } | |
| .container:hover { | |
| box-shadow: 0 15px 35px rgba(0,0,0,0.15); | |
| } | |
| h1 { | |
| color: #2c3e50; | |
| margin-bottom: 15px; | |
| font-size: 2.2rem; | |
| text-align: center; | |
| position: relative; | |
| padding-bottom: 15px; | |
| } | |
| h1:after { | |
| content: ''; | |
| position: absolute; | |
| bottom: 0; | |
| left: 50%; | |
| transform: translateX(-50%); | |
| width: 80px; | |
| height: 3px; | |
| background: linear-gradient(to right, #3498db, #9b59b6); | |
| border-radius: 3px; | |
| } | |
| p { | |
| color: #7f8c8d; | |
| margin-bottom: 20px; | |
| } | |
| #webcam-container { | |
| margin: 25px 0; | |
| border: 3px solid #3498db; | |
| border-radius: 12px; | |
| overflow: hidden; | |
| display: flex; | |
| justify-content: center; | |
| align-items: center; | |
| min-height: 300px; | |
| background: linear-gradient(45deg, #f8f9fa, #e9ecef); | |
| box-shadow: 0 8px 20px rgba(0,0,0,0.1); | |
| position: relative; | |
| transition: all 0.3s; | |
| } | |
| #webcam-container:hover { | |
| box-shadow: 0 12px 25px rgba(0,0,0,0.15); | |
| } | |
| #webcam-container:before { | |
| content: "Kamera yükleniyor..."; | |
| position: absolute; | |
| color: #7f8c8d; | |
| font-size: 1em; | |
| font-weight: 500; | |
| } | |
| #webcam-container video, | |
| #webcam-container canvas { | |
| display: block; | |
| max-width: 100%; | |
| max-height: 100%; | |
| object-fit: contain; | |
| } | |
| #label-container { | |
| margin: 25px 0; | |
| font-size: 1.2em; | |
| font-weight: bold; | |
| background: linear-gradient(45deg, #f8f9fa, #e9ecef); | |
| padding: 20px; | |
| border-radius: 12px; | |
| width: 100%; | |
| box-shadow: 0 5px 15px rgba(0,0,0,0.05); | |
| max-height: 300px; | |
| overflow-y: auto; | |
| } | |
| #label-container div { | |
| transition: all 0.3s; | |
| } | |
| #label-container div:hover { | |
| transform: translateX(5px); | |
| background-color: rgba(52, 152, 219, 0.2) ; | |
| } | |
| .button-group { | |
| display: flex; | |
| gap: 10px; | |
| margin: 15px 0; | |
| flex-wrap: wrap; | |
| justify-content: center; | |
| } | |
| button { | |
| padding: 14px 28px; | |
| font-size: 1em; | |
| cursor: pointer; | |
| background: linear-gradient(to right, #3498db, #2980b9); | |
| color: white; | |
| border: none; | |
| border-radius: 50px; | |
| transition: all 0.3s; | |
| font-weight: 600; | |
| box-shadow: 0 4px 6px rgba(0,0,0,0.1); | |
| position: relative; | |
| overflow: hidden; | |
| } | |
| button:hover { | |
| transform: translateY(-3px); | |
| box-shadow: 0 6px 10px rgba(0,0,0,0.15); | |
| } | |
| button:active { | |
| transform: translateY(1px); | |
| } | |
| button:before { | |
| content: ''; | |
| position: absolute; | |
| top: 0; | |
| left: -100%; | |
| width: 100%; | |
| height: 100%; | |
| background: linear-gradient(90deg, transparent, rgba(255,255,255,0.2), transparent); | |
| transition: 0.5s; | |
| } | |
| button:hover:before { | |
| left: 100%; | |
| } | |
| button.secondary { | |
| background: linear-gradient(to right, #95a5a6, #7f8c8d); | |
| } | |
| #imagePreview { | |
| display: flex; | |
| justify-content: center; | |
| align-items: center; | |
| min-height: 200px; | |
| border: 2px dashed #bdc3c7; | |
| border-radius: 12px; | |
| padding: 20px; | |
| margin: 20px 0; | |
| background-color: white; | |
| transition: all 0.3s; | |
| position: relative; | |
| overflow: hidden; | |
| } | |
| #imagePreview:hover { | |
| border-color: #3498db; | |
| box-shadow: 0 5px 15px rgba(0,0,0,0.05); | |
| } | |
| .upload-section { | |
| margin: 30px 0; | |
| padding: 25px; | |
| background: linear-gradient(45deg, #f8f9fa, #e9ecef); | |
| border-radius: 12px; | |
| box-shadow: 0 5px 15px rgba(0,0,0,0.05); | |
| transition: all 0.3s; | |
| } | |
| .upload-section:hover { | |
| box-shadow: 0 8px 20px rgba(0,0,0,0.1); | |
| } | |
| #fileError { | |
| color: #e74c3c; | |
| margin-top: 10px; | |
| font-size: 0.9em; | |
| } | |
| </style> | |
| </head> | |
| <body> | |
| <div class="container"> | |
| <h1>Teachable Machine Görüntü Modeli</h1> | |
| <p>Modeli ve kamerayı başlatmak için butona tıklayın.</p> | |
| <div class="button-group"> | |
| <button type="button" onclick="toggleCamera()" id="cameraBtn">Kamerayı Aç</button> | |
| </div> | |
| <div id="webcam-container"></div> | |
| <div class="upload-section"> | |
| <h2>Veya Resim Yükle</h2> | |
| <div class="button-group"> | |
| <input type="file" id="fileUpload" accept="image/*" style="display: none;"> | |
| <button type="button" onclick="document.getElementById('fileUpload').click()" class="secondary">Resim Seç</button> | |
| </div> | |
| <div id="fileError"></div> | |
| <div id="imagePreview"></div> | |
| </div> | |
| <div class="button-group" style="margin-top: 20px;"> | |
| <button type="button" onclick="predict()" id="predictBtn" disabled>Tahmin Et</button> | |
| </div> | |
| <div id="label-container" style="max-height: 300px; overflow-y: auto; width: 100%; padding: 10px; background: white; border-radius: 12px; box-shadow: 0 5px 15px rgba(0,0,0,0.05);"></div> | |
| </div> | |
| <script src="https://cdn.jsdelivr.net/npm/@tensorflow/tfjs@latest/dist/tf.min.js"></script> | |
| <script src="https://cdn.jsdelivr.net/npm/@teachablemachine/image@latest/dist/teachablemachine-image.min.js"></script> | |
| <script type="text/javascript"> | |
| // Notification function - shows only once on page load | |
| function showNotification(message) { | |
| // Check if notification was already shown | |
| if (sessionStorage.getItem('notificationShown')) return; | |
| const notification = document.createElement('div'); | |
| notification.className = 'notification'; | |
| notification.innerHTML = ` | |
| <span>${message}</span> | |
| <button class="notification-close">×</button> | |
| `; | |
| notification.querySelector('.notification-close').onclick = () => { | |
| notification.remove(); | |
| }; | |
| document.body.appendChild(notification); | |
| sessionStorage.setItem('notificationShown', 'true'); | |
| } | |
| // Show welcome notification on page load | |
| window.onload = function() { | |
| init(); // Original init call | |
| showNotification('Hoş geldiniz!'); | |
| }; | |
| // Daha fazla API fonksiyonu burada bulunabilir: | |
| // https://github.com/googlecreativelab/teachablemachine-community/tree/master/libraries/image | |
| // Teachable Machine dışa aktarma panelinden sağlanan modelinizin bağlantısı | |
| const URL = "https://teachablemachine.withgoogle.com/models/72Gu94mWN1/"; | |
| let model, webcam, labelContainer, maxPredictions; | |
| let cameraActive = false; | |
| // Kamerayı aç/kapat | |
| async function toggleCamera() { | |
| if (!model) { | |
| // Model yüklenmemişse önce yükle | |
| const modelURL = URL + "model.json"; | |
| const metadataURL = URL + "metadata.json"; | |
| model = await tmImage.load(modelURL, metadataURL); | |
| maxPredictions = model.getTotalClasses(); | |
| } | |
| if (!cameraActive) { | |
| // Kamerayı başlat | |
| const flip = true; | |
| webcam = new tmImage.Webcam(400, 400, flip); | |
| await webcam.setup(); | |
| await webcam.play(); | |
| document.getElementById("webcam-container").innerHTML = ''; | |
| document.getElementById("webcam-container").appendChild(webcam.canvas); | |
| document.getElementById('cameraBtn').textContent = 'Kamerayı Kapat'; | |
| document.getElementById('predictBtn').disabled = false; | |
| cameraActive = true; | |
| // Optimize for smoother video | |
| let lastTime = 0; | |
| const fps = 30; | |
| const interval = 1000 / fps; | |
| const optimizedLoop = async (timestamp) => { | |
| if (!cameraActive) return; | |
| if (timestamp - lastTime >= interval) { | |
| webcam.update(); // web kamerası çerçevesini güncelle | |
| lastTime = timestamp; | |
| } | |
| requestAnimationFrame(optimizedLoop); | |
| }; | |
| requestAnimationFrame(optimizedLoop); | |
| // Optimized prediction loop for webcam | |
| const predictWebcam = async () => { | |
| if (!cameraActive) return; | |
| // Predict the current webcam frame | |
| const prediction = await model.predict(webcam.canvas); | |
| // Sort predictions by probability (highest first) | |
| prediction.sort((a, b) => b.probability - a.probability); | |
| // Update label container | |
| const labelContainer = document.getElementById('label-container'); | |
| labelContainer.innerHTML = ''; | |
| for (let i = 0; i < maxPredictions; i++) { | |
| const probabilityPercent = (prediction[i].probability * 100).toFixed(2); | |
| // Show notification if probability > 80% | |
| if (prediction[i].probability > 0.8) { | |
| showNotification(`Bu bir ${prediction[i].className}`); | |
| } | |
| const classPrediction = ` | |
| <div style="margin: 8px 0; padding: 8px; background-color: rgba(52, 152, 219, 0.1); border-radius: 4px;"> | |
| <strong>${prediction[i].className}:</strong> ${probabilityPercent}% | |
| <div style="height: 5px; background-color: #ecf0f1; margin-top: 5px; border-radius: 3px;"> | |
| <div style="width: ${probabilityPercent}%; height: 100%; background-color: #3498db; border-radius: 3px;"></div> | |
| </div> | |
| </div>`; | |
| labelContainer.innerHTML += classPrediction; | |
| } | |
| // Call this function again on the next animation frame | |
| requestAnimationFrame(predictWebcam); | |
| }; | |
| // Start the prediction loop | |
| predictWebcam(); | |
| } else { | |
| // Kamerayı kapat | |
| if (webcam) { | |
| webcam.stop(); | |
| } | |
| document.getElementById('cameraBtn').textContent = 'Kamerayı Aç'; | |
| document.getElementById("webcam-container").innerHTML = ''; | |
| cameraActive = false; | |
| } | |
| } | |
| // Modeli yükle (başlangıçta) | |
| async function init() { | |
| if (!model) { | |
| const modelURL = URL + "model.json"; | |
| const metadataURL = URL + "metadata.json"; | |
| model = await tmImage.load(modelURL, metadataURL); | |
| maxPredictions = model.getTotalClasses(); | |
| document.getElementById('predictBtn').disabled = false; | |
| } | |
| } | |
| // Sayfa yüklendiğinde modeli yükle | |
| window.onload = init; | |
| // Web kamerası görüntüsünü görüntü modelinden geçir | |
| // Maximum file size in bytes (2MB) | |
| const MAX_FILE_SIZE = 2 * 1024 * 1024; | |
| const ALLOWED_TYPES = ['image/jpeg', 'image/png', 'image/gif', 'image/webp']; | |
| // Handle file upload | |
| document.getElementById('fileUpload').addEventListener('change', function(e) { | |
| const file = e.target.files[0]; | |
| const errorElement = document.getElementById('fileError'); | |
| const previewElement = document.getElementById('imagePreview'); | |
| // Reset previous state | |
| errorElement.textContent = ''; | |
| previewElement.innerHTML = ''; | |
| if (!file) return; | |
| // Validate file type | |
| if (!ALLOWED_TYPES.includes(file.type)) { | |
| errorElement.textContent = 'Geçersiz dosya türü. Sadece JPG, PNG, GIF veya WEBP yükleyebilirsiniz.'; | |
| return; | |
| } | |
| // Validate file size | |
| if (file.size > MAX_FILE_SIZE) { | |
| errorElement.textContent = 'Dosya boyutu çok büyük. Maksimum 2MB boyutunda dosya yükleyebilirsiniz.'; | |
| return; | |
| } | |
| // Create preview | |
| const reader = new FileReader(); | |
| reader.onload = function(e) { | |
| const img = document.createElement('img'); | |
| img.src = e.target.result; | |
| img.style.maxWidth = '200px'; | |
| img.style.maxHeight = '200px'; | |
| previewElement.appendChild(img); | |
| // Predict on the uploaded image | |
| predictImage(img); | |
| }; | |
| reader.readAsDataURL(file); | |
| }); | |
| let lastDetectedClass = null; | |
| let lastNotificationTime = 0; | |
| // Predict on uploaded image (with live updates) | |
| async function predictImage(imageElement) { | |
| if (!model) { | |
| await init(); | |
| } | |
| // Clear previous results | |
| const labelContainer = document.getElementById('label-container'); | |
| labelContainer.innerHTML = ''; | |
| // Start continuous prediction | |
| const predictLoop = async () => { | |
| if (!imageElement.parentNode) return; // Stop if image was removed | |
| const prediction = await model.predict(imageElement); | |
| // Sort predictions by probability (highest first) | |
| prediction.sort((a, b) => b.probability - a.probability); | |
| labelContainer.innerHTML = ''; | |
| for (let i = 0; i < maxPredictions; i++) { | |
| const probabilityPercent = (prediction[i].probability * 100).toFixed(2); | |
| // Show notification if probability > 80% and class changed or 5 seconds passed | |
| const currentTime = Date.now(); | |
| if (prediction[i].probability > 0.8 && | |
| (prediction[i].className !== lastDetectedClass || currentTime - lastNotificationTime > 5000)) { | |
| showNotification(`Bu bir ${prediction[i].className}`); | |
| lastDetectedClass = prediction[i].className; | |
| lastNotificationTime = currentTime; | |
| } | |
| const classPrediction = ` | |
| <div style="margin: 8px 0; padding: 8px; background-color: rgba(52, 152, 219, 0.1); border-radius: 4px;"> | |
| <strong>${prediction[i].className}:</strong> ${probabilityPercent}% | |
| <div style="height: 5px; background-color: #ecf0f1; margin-top: 5px; border-radius: 3px;"> | |
| <div style="width: ${probabilityPercent}%; height: 100%; background-color: #3498db; border-radius: 3px;"></div> | |
| </div> | |
| </div>`; | |
| labelContainer.innerHTML += classPrediction; | |
| } | |
| requestAnimationFrame(predictLoop); | |
| }; | |
| predictLoop(); | |
| } | |
| async function predict() { | |
| if (!model) { | |
| alert('Lütfen önce modeli başlatın!'); | |
| return; | |
| } | |
| try { | |
| // Only predict if we have an active source | |
| if (!webcam?.canvas && !document.querySelector('#imagePreview img')) return; | |
| // predict fonksiyonu bir resim, video veya canvas html öğesi alabilir | |
| const prediction = await model.predict(webcam?.canvas || document.querySelector('#imagePreview img'), { flipHorizontal: false }); | |
| // Clear previous results | |
| labelContainer.innerHTML = ''; | |
| // Sort predictions by probability (highest first) | |
| prediction.sort((a, b) => b.probability - a.probability); | |
| for (let i = 0; i < maxPredictions; i++) { | |
| const probabilityPercent = (prediction[i].probability * 100).toFixed(2); | |
| const classPrediction = ` | |
| <div style="margin: 8px 0; padding: 8px; background-color: rgba(52, 152, 219, 0.1); border-radius: 4px;"> | |
| <strong>${prediction[i].className}:</strong> ${probabilityPercent}% | |
| <div style="height: 5px; background-color: #ecf0f1; margin-top: 5px; border-radius: 3px;"> | |
| <div style="width: ${probabilityPercent}%; height: 100%; background-color: #3498db; border-radius: 3px;"></div> | |
| </div> | |
| </div>`; | |
| labelContainer.innerHTML += classPrediction; | |
| } | |
| } catch (error) { | |
| console.error('Prediction error:', error); | |
| labelContainer.innerHTML = '<div style="color: #e74c3c;">Tahmin yapılırken bir hata oluştu. Lütfen kamera veya resim yüklediğinizden emin olun.</div>'; | |
| } | |
| } | |
| </script> | |
| <p style="border-radius: 8px; text-align: center; font-size: 12px; color: #fff; margin-top: 16px;position: fixed; left: 8px; bottom: 8px; z-index: 10; background: rgba(0, 0, 0, 0.8); padding: 4px 8px;">Made with <img src="https://enzostvs-deepsite.hf.space/logo.svg" alt="DeepSite Logo" style="width: 16px; height: 16px; vertical-align: middle;display:inline-block;margin-right:3px;filter:brightness(0) invert(1);"><a href="https://enzostvs-deepsite.hf.space" style="color: #fff;text-decoration: underline;" target="_blank" >DeepSite</a> - 🧬 <a href="https://enzostvs-deepsite.hf.space?remix=Denizfe/machinelearning" style="color: #fff;text-decoration: underline;" target="_blank" >Remix</a></p></body> | |
| </html> |