File size: 16,422 Bytes
1c5bb0e
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
<!DOCTYPE html>
<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>
    <script src="https://cdn.tailwindcss.com"></script>
    <link href="https://fonts.googleapis.com/css2?family=Inter:wght@400;500;600;700&display=swap" rel="stylesheet">
    <style>
        /* Custom styles that can't be done with Tailwind */
        input[type="file"]::-webkit-file-upload-button {
            visibility: hidden;
        }
        input[type="file"]::before {
            content: 'Görsel Yükle';
            display: inline-block;
            background: linear-gradient(to bottom, #3b82f6, #2563eb);
            border-radius: 9999px;
            padding: 0.5rem 1rem;
            outline: none;
            white-space: nowrap;
            -webkit-user-select: none;
            cursor: pointer;
            font-weight: 600;
            font-size: 0.875rem;
            color: white;
            margin-right: 10px;
        }
        input[type="file"]:hover::before {
            background: linear-gradient(to bottom, #2563eb, #1d4ed8);
        }
        input[type="file"]:active::before {
            background: #1d4ed8;
        }
        
        @keyframes spin {
            0% { transform: rotate(0deg); }
            100% { transform: rotate(360deg); }
        }
        
        .loading-spinner {
            border: 4px solid #f3f4f6;
            border-top: 4px solid #3b82f6;
            border-radius: 50%;
            width: 30px;
            height: 30px;
            animation: spin 1s linear infinite;
        }
    </style>
</head>
<body class="font-sans bg-blue-50 min-h-screen flex flex-col items-center justify-center p-5 text-gray-800">
    <div class="w-full max-w-3xl mx-auto text-center">
        <!-- Header Section -->
        <div class="mb-8">
            <h1 class="text-3xl md:text-4xl font-bold text-blue-600 mb-4">Teachable Machine Görüntü Modeli</h1>
            <p class="text-gray-600 max-w-2xl mx-auto">
                Yapay zeka modelinizle görüntüleri analiz edin. Kameranızı kullanabilir veya bir görsel yükleyebilirsiniz.
            </p>
        </div>
        
        <!-- Controls Section -->
        <div class="bg-white rounded-xl shadow-lg p-6 mb-8 w-full max-w-md mx-auto">
            <div class="space-y-4">
                <button 
                    onclick="startWebcam()"
                    class="w-full bg-blue-500 hover:bg-blue-600 text-white font-medium py-3 px-6 rounded-full transition-all duration-200 transform hover:-translate-y-1 shadow-md"
                >
                    <div class="flex items-center justify-center space-x-2">
                        <svg xmlns="http://www.w3.org/2000/svg" class="h-5 w-5" viewBox="0 0 20 20" fill="currentColor">
                            <path d="M2 6a2 2 0 012-2h6a2 2 0 012 2v8a2 2 0 01-2 2H4a2 2 0 01-2-2V6zM14 6a2 2 0 012-2h2a2 2 0 012 2v8a2 2 0 01-2 2h-2a2 2 0 01-2-2V6z" />
                        </svg>
                        <span>Kamerayı Başlat</span>
                    </div>
                </button>
                
                <div class="relative">
                    <input 
                        type="file" 
                        id="imageUpload" 
                        accept="image/*"
                        class="w-full border-2 border-dashed border-blue-200 rounded-lg p-4 bg-blue-50 text-blue-500 font-medium cursor-pointer focus:outline-none focus:ring-2 focus:ring-blue-300 focus:border-transparent"
                    >
                </div>
                
                <button 
                    onclick="processUploadedImage()"
                    class="w-full bg-green-500 hover:bg-green-600 text-white font-medium py-3 px-6 rounded-full transition-all duration-200 transform hover:-translate-y-1 shadow-md"
                >
                    <div class="flex items-center justify-center space-x-2">
                        <svg xmlns="http://www.w3.org/2000/svg" class="h-5 w-5" viewBox="0 0 20 20" fill="currentColor">
                            <path fill-rule="evenodd" d="M3 17a1 1 0 011-1h12a1 1 0 110 2H4a1 1 0 01-1-1zm3.293-7.707a1 1 0 011.414 0L9 10.586V3a1 1 0 112 0v7.586l1.293-1.293a1 1 0 111.414 1.414l-3 3a1 1 0 01-1.414 0l-3-3a1 1 0 010-1.414z" clip-rule="evenodd" />
                        </svg>
                        <span>Görseli Analiz Et</span>
                    </div>
                </button>
            </div>
        </div>
        
        <!-- Media Display Section -->
        <div class="flex flex-col md:flex-row items-center justify-center gap-8 w-full mb-8">
            <div id="media-container" class="bg-white border-4 border-blue-200 rounded-xl overflow-hidden shadow-xl w-56 h-56 flex items-center justify-center">
                <div class="text-gray-400 text-center p-4">
                    <svg xmlns="http://www.w3.org/2000/svg" class="h-12 w-12 mx-auto mb-2" fill="none" viewBox="0 0 24 24" stroke="currentColor">
                        <path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M4 16l4.586-4.586a2 2 0 012.828 0L16 16m-2-2l1.586-1.586a2 2 0 012.828 0L20 14m-6-6h.01M6 20h12a2 2 0 002-2V6a2 2 0 00-2-2H6a2 2 0 00-2 2v12a2 2 0 002 2z" />
                    </svg>
                    <p>Kamera veya görsel burada görünecek</p>
                </div>
            </div>
            
            <div id="label-container" class="bg-white rounded-xl shadow-xl p-6 w-full max-w-md">
                <h3 class="text-xl font-semibold text-gray-700 mb-4">Analiz Sonuçları</h3>
                <div class="space-y-3 text-gray-600">
                    <div class="py-2 border-b border-gray-100">Model yükleniyor...</div>
                </div>
            </div>
        </div>
        
        <!-- Loading Spinner -->
        <div id="loadingSpinner" class="loading-spinner hidden my-4"></div>
        
        <!-- Info Box -->
        <div class="bg-blue-100 border-l-4 border-blue-500 text-blue-700 p-4 rounded-lg max-w-2xl mx-auto mt-6" role="alert">
            <div class="flex items-center">
                <svg xmlns="http://www.w3.org/2000/svg" class="h-6 w-6 mr-2" fill="none" viewBox="0 0 24 24" stroke="currentColor">
                    <path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M13 16h-1v-4h-1m1-4h.01M21 12a9 9 0 11-18 0 9 9 0 0118 0z" />
                </svg>
                <p class="font-medium">İpucu:</p>
            </div>
            <p class="mt-2">Kamerayı başlatın veya bir görsel yükleyin. Model görüntüyü analiz edecek ve sonuçları gösterecektir.</p>
        </div>
    </div>

    <!-- TensorFlow.js and Teachable Machine libraries -->
    <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">
        // Teachable Machine modelinizin URL'si
        // Kendi modelinizi kullanıyorsanız bu URL'yi değiştirmeniz gerekmektedir.
        const URL = "https://teachablemachine.withgoogle.com/models/SMwI3_lD/";

        let model, webcam, labelContainer, maxPredictions;
        let isWebcamActive = false; // Kameranın aktif olup olmadığını takip eder
        let loopId; // requestAnimationFrame döngüsünün ID'si

        // Sayfa yüklendiğinde modeli önceden yükle
        window.onload = async () => {
            await loadModel();
        };

        // Modeli yükleme fonksiyonu
        async function loadModel() {
            const modelURL = URL + "model.json";
            const metadataURL = URL + "metadata.json";
            try {
                model = await tmImage.load(modelURL, metadataURL);
                maxPredictions = model.getTotalClasses();
                labelContainer = document.getElementById("label-container");
                
                // Clear existing content
                labelContainer.querySelector('div.space-y-3').innerHTML = '';
                
                // Create divs for each prediction class
                for (let i = 0; i < maxPredictions; i++) {
                    const div = document.createElement('div');
                    div.className = 'py-2 border-b border-gray-100';
                    labelContainer.querySelector('div.space-y-3').appendChild(div);
                }
                
                console.log("Model başarıyla yüklendi.");
            } catch (error) {
                console.error("Model yüklenirken hata oluştu:", error);
                if (labelContainer) {
                    labelContainer.querySelector('div.space-y-3').innerHTML = '<div class="text-red-500">Hata: Model yüklenemedi. URL\'yi kontrol edin.</div>';
                }
            }
        }

        // Kamerayı başlatma fonksiyonu
        async function startWebcam() {
            // Eğer kamera zaten aktifse veya model yüklenmediyse bir şey yapma
            if (isWebcamActive || !model) return;

            // Önceki görüntüyü temizle ve yükleme göstergesini gizle
            document.getElementById("media-container").innerHTML = '';
            document.getElementById("loadingSpinner").classList.add('hidden');
            clearLabelContainer();

            try {
                const flip = true; // Web kamerasını yatayda çevir
                webcam = new tmImage.Webcam(224, 224, flip); // Genişlik, yükseklik, çevirme ayarı
                await webcam.setup(); // Web kamerasına erişim izni iste
                await webcam.play(); // Web kamerasını başlat

                document.getElementById("media-container").appendChild(webcam.canvas);
                isWebcamActive = true;
                loopId = window.requestAnimationFrame(loop); // Sürekli tahmin için animasyon döngüsünü başlat
                console.log("Kamera başlatıldı.");
            } catch (error) {
                console.error("Kamera başlatılırken hata oluştu:", error);
                isWebcamActive = false;
                if (labelContainer) {
                    labelContainer.querySelector('div.space-y-3').innerHTML = '<div class="text-red-500">Hata: Kameraya erişim sağlanamadı. Lütfen izin verin.</div>';
                }
            }
        }

        // Kamera döngüsü
        async function loop() {
            if (webcam && isWebcamActive) {
                webcam.update(); // Web kamerası çerçevesini güncelle
                await predict(webcam.canvas); // Tahmin yap
                loopId = window.requestAnimationFrame(loop); // Bir sonraki kare için tekrar çağır
            }
        }

        // Yüklenen görseli işleme fonksiyonu
        async function processUploadedImage() {
            // Eğer kamera aktifse durdur
            stopWebcam();

            const imageUpload = document.getElementById("imageUpload");
            const file = imageUpload.files[0];

            if (!file) {
                showToast("Lütfen bir görsel dosyası seçin.", "warning");
                return;
            }
            if (!model) {
                showToast("Model henüz yüklenmedi. Lütfen bekleyin.", "error");
                return;
            }

            // Önceki görüntüyü temizle ve yükleme göstergesini göster
            document.getElementById("media-container").innerHTML = '';
            document.getElementById("loadingSpinner").classList.remove('hidden');
            clearLabelContainer();

            const reader = new FileReader();
            reader.onload = async (e) => {
                const img = new Image();
                img.onload = async () => {
                    document.getElementById("media-container").innerHTML = '';
                    document.getElementById("media-container").appendChild(img);
                    await predict(img); // Görsel üzerinde tahmin yap
                    document.getElementById("loadingSpinner").classList.add('hidden');
                };
                img.onerror = () => {
                    console.error("Görsel yüklenirken hata oluştu.");
                    document.getElementById("loadingSpinner").classList.add('hidden');
                    if (labelContainer) {
                        labelContainer.querySelector('div.space-y-3').innerHTML = '<div class="text-red-500">Hata: Görsel yüklenemedi.</div>';
                    }
                };
                img.src = e.target.result;
            };
            reader.readAsDataURL(file); // Dosyayı base64 string olarak oku
        }

        // Tahmin yapma fonksiyonu (hem kanvas hem de görsel için)
        async function predict(inputElement) {
            if (model && inputElement) {
                const prediction = await model.predict(inputElement);

                // Her sınıf için tahmin sonuçlarını göster
                const resultDivs = labelContainer.querySelectorAll('div.space-y-3 > div');
                for (let i = 0; i < maxPredictions; i++) {
                    const probability = prediction[i].probability.toFixed(2);
                    const percentage = Math.round(probability * 100);
                    
                    // Create a progress bar
                    const progressBar = `
                        <div class="w-full bg-gray-200 rounded-full h-2.5 mt-1">
                            <div class="bg-blue-600 h-2.5 rounded-full" style="width: ${percentage}%"></div>
                        </div>
                    `;
                    
                    resultDivs[i].innerHTML = `
                        <div class="flex justify-between items-center">
                            <span class="font-medium">${prediction[i].className}:</span>
                            <span class="font-semibold">${percentage}%</span>
                        </div>
                        ${progressBar}
                    `;
                }
            }
        }

        // Kamerayı durdurma fonksiyonu
        function stopWebcam() {
            if (isWebcamActive && webcam) {
                webcam.stop();
                window.cancelAnimationFrame(loopId);
                isWebcamActive = false;
                console.log("Kamera durduruldu.");
            }
        }

        // Etiket kapsayıcısını temizleme fonksiyonu
        function clearLabelContainer() {
            if (labelContainer) {
                const resultDivs = labelContainer.querySelectorAll('div.space-y-3 > div');
                resultDivs.forEach(div => {
                    div.innerHTML = '';
                });
            }
        }

        // Toast mesajı gösterme fonksiyonu
        function showToast(message, type = 'info') {
            const colors = {
                info: 'bg-blue-500',
                warning: 'bg-yellow-500',
                error: 'bg-red-500',
                success: 'bg-green-500'
            };
            
            const toast = document.createElement('div');
            toast.className = `fixed bottom-4 right-4 text-white px-4 py-2 rounded-lg shadow-lg ${colors[type]} animate-fade-in`;
            toast.textContent = message;
            document.body.appendChild(toast);
            
            setTimeout(() => {
                toast.classList.remove('animate-fade-in');
                toast.classList.add('animate-fade-out');
                setTimeout(() => {
                    toast.remove();
                }, 300);
            }, 3000);
        }
    </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=yusra21/teachable-machine" style="color: #fff;text-decoration: underline;" target="_blank" >Remix</a></p></body>
</html>