Spaces:
Sleeping
Sleeping
| <html lang="id"> | |
| <head> | |
| <meta charset="UTF-8"> | |
| <meta name="viewport" content="width=device-width, initial-scale=1.0"> | |
| <title>ResNet CIFAR10 - Research Deployment</title> | |
| <style> | |
| @import url('https://fonts.googleapis.com/css2?family=Poppins:wght@300;400;500;600;700&display=swap'); | |
| * { | |
| margin: 0; | |
| padding: 0; | |
| box-sizing: border-box; | |
| font-family: 'Poppins', sans-serif; | |
| } | |
| body { | |
| background-color: #f8fafc; | |
| color: #334155; | |
| } | |
| .hero-section { | |
| background: linear-gradient(135deg, #1e293b 0%, #334155 100%); | |
| color: white; | |
| padding: 70px 8%; | |
| text-align: center; | |
| } | |
| .hero-title { | |
| font-size: 32px; | |
| font-weight: 700; | |
| margin-bottom: 12px; | |
| line-height: 1.4; | |
| } | |
| .hero-subtitle { | |
| font-size: 18px; | |
| color: #cbd5e0; | |
| font-weight: 500; | |
| letter-spacing: 0.5px; | |
| } | |
| .support-section { | |
| background: white; | |
| padding: 40px 8%; | |
| text-align: center; | |
| border-bottom: 1px solid #e2e8f0; | |
| } | |
| .support-title { | |
| font-size: 14px; | |
| font-weight: 700; | |
| color: #64748b; | |
| letter-spacing: 2px; | |
| text-transform: uppercase; | |
| margin-bottom: 30px; | |
| } | |
| .logos-container { | |
| display: flex; | |
| justify-content: center; | |
| align-items: center; | |
| gap: 60px; | |
| flex-wrap: wrap; | |
| } | |
| .logo-item { | |
| display: flex; | |
| flex-direction: column; | |
| align-items: center; | |
| gap: 15px; | |
| } | |
| .logo-img { | |
| height: 80px; | |
| } | |
| .logo-text { | |
| font-size: 12px; | |
| font-weight: 600; | |
| color: #475569; | |
| max-width: 250px; | |
| } | |
| .about-section { | |
| padding: 60px 8%; | |
| background: #f1f5f9; | |
| display: flex; | |
| flex-direction: column; | |
| align-items: center; | |
| } | |
| .section-header { | |
| text-align: center; | |
| margin-bottom: 40px; | |
| } | |
| .section-header h2 { | |
| font-size: 28px; | |
| color: #1e293b; | |
| margin-bottom: 10px; | |
| font-weight: 700; | |
| } | |
| .section-header p { | |
| color: #64748b; | |
| font-size: 15px; | |
| } | |
| .class-grid { | |
| display: flex; | |
| flex-wrap: wrap; | |
| gap: 15px; | |
| justify-content: center; | |
| max-width: 1000px; | |
| } | |
| .class-card { | |
| background: white; | |
| padding: 20px 15px; | |
| border-radius: 16px; | |
| text-align: center; | |
| box-shadow: 0 4px 6px rgba(0,0,0,0.02); | |
| border: 1px solid #e2e8f0; | |
| width: 140px; | |
| cursor: pointer; | |
| transition: transform 0.2s, border-color 0.2s; | |
| display: flex; | |
| flex-direction: column; | |
| align-items: center; | |
| gap: 15px; | |
| } | |
| .class-card:hover { | |
| transform: translateY(-5px); | |
| border-color: #3b82f6; | |
| box-shadow: 0 10px 15px rgba(59, 130, 246, 0.1); | |
| } | |
| .class-emoji { | |
| font-size: 40px; | |
| line-height: 1; | |
| } | |
| .class-card span { | |
| display: block; | |
| font-weight: 700; | |
| color: #334155; | |
| font-size: 15px; | |
| } | |
| .detection-section { | |
| padding: 60px 8%; | |
| display: flex; | |
| gap: 30px; | |
| align-items: stretch; | |
| } | |
| .panel { | |
| background: white; | |
| border-radius: 20px; | |
| padding: 30px; | |
| border: 1px solid #e2e8f0; | |
| box-shadow: 0 10px 25px rgba(0,0,0,0.02); | |
| flex: 1; | |
| display: flex; | |
| flex-direction: column; | |
| } | |
| .panel-header { | |
| display: flex; | |
| align-items: center; | |
| gap: 12px; | |
| margin-bottom: 25px; | |
| color: #1a202c; | |
| font-size: 1.1rem; | |
| font-weight: 600; | |
| } | |
| .panel-svg { | |
| color: #3b82f6; | |
| display: flex; | |
| } | |
| .upload-area { | |
| border: 2px dashed #cbd5e0; | |
| border-radius: 15px; | |
| height: 400px; | |
| display: flex; | |
| flex-direction: column; | |
| align-items: center; | |
| justify-content: center; | |
| cursor: pointer; | |
| background: #ffffff; | |
| transition: 0.3s; | |
| position: relative; | |
| } | |
| .upload-area:hover { | |
| border-color: #3b82f6; | |
| background: #f8fafc; | |
| } | |
| .plus-icon { | |
| font-size: 50px; | |
| color: #94a3b8; | |
| font-weight: 300; | |
| line-height: 1; | |
| margin-bottom: 15px; | |
| } | |
| .upload-text-main { | |
| font-weight: 700; | |
| color: #334155; | |
| font-size: 16px; | |
| margin-bottom: 5px; | |
| } | |
| .upload-text-sub { | |
| color: #94a3b8; | |
| font-size: 12px; | |
| } | |
| #image-preview-container { | |
| display: none; | |
| flex-direction: column; | |
| align-items: center; | |
| justify-content: center; | |
| width: 100%; | |
| height: 100%; | |
| padding: 20px; | |
| } | |
| #image-preview { | |
| max-width: 100%; | |
| max-height: 280px; | |
| border-radius: 10px; | |
| margin-bottom: 20px; | |
| box-shadow: 0 4px 10px rgba(0,0,0,0.1); | |
| } | |
| .btn-change { | |
| background: white; | |
| border: 1px solid #cbd5e0; | |
| padding: 8px 20px; | |
| border-radius: 8px; | |
| font-weight: 500; | |
| color: #475569; | |
| cursor: pointer; | |
| } | |
| .btn-change:hover { | |
| border-color: #3b82f6; | |
| color: #3b82f6; | |
| } | |
| .result-panel { | |
| flex: 1.2; | |
| } | |
| .empty-state { | |
| display: flex; | |
| flex-direction: column; | |
| align-items: center; | |
| justify-content: center; | |
| height: 100%; | |
| text-align: center; | |
| } | |
| .empty-icon { | |
| width: 50px; | |
| height: 50px; | |
| stroke: #cbd5e0; | |
| stroke-width: 1.5; | |
| fill: none; | |
| margin-bottom: 15px; | |
| } | |
| .empty-title { | |
| font-weight: 700; | |
| color: #334155; | |
| margin-bottom: 8px; | |
| } | |
| .empty-desc { | |
| color: #94a3b8; | |
| font-size: 13px; | |
| max-width: 250px; | |
| line-height: 1.6; | |
| } | |
| .cards-row { | |
| display: flex; | |
| gap: 15px; | |
| margin-bottom: 20px; | |
| } | |
| .card { | |
| flex: 1; | |
| background: #f8fafc; | |
| border: 1px solid #e2e8f0; | |
| border-radius: 15px; | |
| padding: 20px; | |
| } | |
| .model-label { | |
| font-size: 11px; | |
| font-weight: 700; | |
| color: #94a3b8; | |
| text-transform: uppercase; | |
| margin-bottom: 5px; | |
| } | |
| .class-name { | |
| font-size: 22px; | |
| font-weight: 700; | |
| color: #1e293b; | |
| text-transform: capitalize; | |
| } | |
| .conf-badge { | |
| font-size: 13px; | |
| color: #10b981; | |
| font-weight: 600; | |
| } | |
| .prob-list { | |
| margin-top: 15px; | |
| border-top: 1px solid #e2e8f0; | |
| padding-top: 15px; | |
| display: flex; | |
| flex-direction: column; | |
| gap: 6px; | |
| max-height: 180px; | |
| overflow-y: auto; | |
| } | |
| .prob-item { | |
| display: flex; | |
| align-items: center; | |
| font-size: 11px; | |
| } | |
| .prob-label { | |
| width: 70px; | |
| text-transform: capitalize; | |
| font-weight: 500; | |
| } | |
| .prob-bar-bg { | |
| flex: 1; | |
| height: 6px; | |
| background: #e2e8f0; | |
| border-radius: 3px; | |
| margin: 0 10px; | |
| overflow: hidden; | |
| } | |
| .prob-bar-fill { | |
| height: 100%; | |
| background: #3b82f6; | |
| border-radius: 3px; | |
| transition: width 0.5s; | |
| } | |
| .prob-val { | |
| width: 35px; | |
| text-align: right; | |
| font-weight: 600; | |
| } | |
| .interpretation { | |
| background: #eff6ff; | |
| padding: 20px; | |
| border-radius: 15px; | |
| border-left: 5px solid #3b82f6; | |
| margin-top: auto; | |
| } | |
| .inter-title { | |
| font-weight: 700; | |
| color: #1e40af; | |
| margin-bottom: 5px; | |
| display: flex; | |
| align-items: center; | |
| gap: 8px; | |
| } | |
| .inter-text { | |
| font-size: 13px; | |
| line-height: 1.5; | |
| color: #1e3a8a; | |
| } | |
| .loader { | |
| display: none; | |
| flex-direction: column; | |
| align-items: center; | |
| justify-content: center; | |
| height: 100%; | |
| } | |
| .spinner { | |
| width: 40px; | |
| height: 40px; | |
| border: 4px solid #f3f3f3; | |
| border-top: 4px solid #3b82f6; | |
| border-radius: 50%; | |
| animation: spin 1s linear infinite; | |
| margin-bottom: 15px; | |
| } | |
| @keyframes spin { | |
| 0% { transform: rotate(0deg); } | |
| 100% { transform: rotate(360deg); } | |
| } | |
| .modal { | |
| display: none; | |
| position: fixed; | |
| top: 0; | |
| left: 0; | |
| width: 100%; | |
| height: 100%; | |
| background: rgba(0,0,0,0.5); | |
| backdrop-filter: blur(4px); | |
| z-index: 2000; | |
| align-items: center; | |
| justify-content: center; | |
| opacity: 0; | |
| transition: opacity 0.3s; | |
| } | |
| .modal.show { | |
| opacity: 1; | |
| } | |
| .modal-box { | |
| background: white; | |
| padding: 40px; | |
| border-radius: 20px; | |
| text-align: center; | |
| max-width: 450px; | |
| width: 90%; | |
| position: relative; | |
| transform: translateY(20px); | |
| transition: transform 0.3s; | |
| } | |
| .modal.show .modal-box { | |
| transform: translateY(0); | |
| } | |
| .close-btn { | |
| position: absolute; | |
| top: 15px; | |
| right: 20px; | |
| font-size: 28px; | |
| cursor: pointer; | |
| color: #94a3b8; | |
| line-height: 1; | |
| } | |
| .close-btn:hover { | |
| color: #ef4444; | |
| } | |
| .class-modal-emoji { | |
| font-size: 60px; | |
| margin-bottom: 15px; | |
| } | |
| .class-modal-title { | |
| font-size: 24px; | |
| font-weight: 700; | |
| color: #1e293b; | |
| margin-bottom: 10px; | |
| } | |
| .class-modal-desc { | |
| color: #475569; | |
| font-size: 14px; | |
| line-height: 1.6; | |
| } | |
| ::-webkit-scrollbar { width: 6px; } | |
| ::-webkit-scrollbar-track { background: #f1f5f9; border-radius: 4px; } | |
| ::-webkit-scrollbar-thumb { background: #cbd5e0; border-radius: 4px; } | |
| ::-webkit-scrollbar-thumb:hover { background: #94a3b8; } | |
| </style> | |
| </head> | |
| <body> | |
| <section class="hero-section"> | |
| <h1 class="hero-title">Deployment ResNet-MLP dan ResNet-KAN pada CIFAR10</h1> | |
| <div class="hero-subtitle">Rakyan (NPM)</div> | |
| </section> | |
| <section class="support-section"> | |
| <h3 class="support-title">Penelitian Ini Didukung Oleh</h3> | |
| <div class="logos-container"> | |
| <div class="logo-item"> | |
| <img src="/static/DSC UI.png" class="logo-img" alt="DSC Logo"> | |
| <p class="logo-text">Data Science Center, FMIPA UI</p> | |
| </div> | |
| <div class="logo-item" style="border-left: 2px solid #e2e8f0; padding-left: 40px;"> | |
| <img src="/static/BrainAI.png" class="logo-img" alt="BrainAI Logo"> | |
| <p class="logo-text">BrainAI Lab, Departemen Matematika FMIPA UI</p> | |
| </div> | |
| </div> | |
| </section> | |
| <section class="about-section"> | |
| <div class="section-header"> | |
| <h2>About CIFAR-10 Dataset</h2> | |
| <p>Dataset ini terdiri dari 60.000 citra berwarna 32x32 dalam 10 kelas berbeda.</p> | |
| </div> | |
| <div class="class-grid"> | |
| <div class="class-card" onclick="openClassModal('Pesawat', '✈️', 'Citra yang merepresentasikan pesawat terbang, termasuk jet komersial, pesawat tempur, hingga pesawat baling-baling ringan.')"> | |
| <div class="class-emoji">✈️</div> | |
| <span>Pesawat</span> | |
| </div> | |
| <div class="class-card" onclick="openClassModal('Mobil', '🚗', 'Kendaraan roda empat untuk penumpang seperti sedan, hatchback, dan SUV. Kelas ini tidak mencakup truk atau kendaraan alat berat.')"> | |
| <div class="class-emoji">🚗</div> | |
| <span>Mobil</span> | |
| </div> | |
| <div class="class-card" onclick="openClassModal('Burung', '🐦', 'Berbagai spesies burung dari yang berukuran kecil seperti pipit hingga yang besar seperti elang atau unta.')"> | |
| <div class="class-emoji">🐦</div> | |
| <span>Burung</span> | |
| </div> | |
| <div class="class-card" onclick="openClassModal('Kucing', '🐱', 'Mamalia karnivora berukuran kecil dari keluarga Felidae, mencakup berbagai ras kucing domestik.')"> | |
| <div class="class-emoji">🐱</div> | |
| <span>Kucing</span> | |
| </div> | |
| <div class="class-card" onclick="openClassModal('Rusa', '🦌', 'Hewan mamalia pemamah biak yang termasuk dalam famili Cervidae, mencakup berbagai jenis rusa dan kijang.')"> | |
| <div class="class-emoji">🦌</div> | |
| <span>Rusa</span> | |
| </div> | |
| <div class="class-card" onclick="openClassModal('Anjing', '🐶', 'Mamalia karnivora yang telah didomestikasi dari serigala, mencakup berbagai ras dan ukuran.')"> | |
| <div class="class-emoji">🐶</div> | |
| <span>Anjing</span> | |
| </div> | |
| <div class="class-card" onclick="openClassModal('Katak', '🐸', 'Amfibi tak berekor yang pandai melompat, mencakup berbagai spesies katak dan kodok di alam liar.')"> | |
| <div class="class-emoji">🐸</div> | |
| <span>Katak</span> | |
| </div> | |
| <div class="class-card" onclick="openClassModal('Kuda', '🐴', 'Mamalia berkuku satu yang sering digunakan untuk berkuda, pacuan, maupun hewan pekerja beban.')"> | |
| <div class="class-emoji">🐴</div> | |
| <span>Kuda</span> | |
| </div> | |
| <div class="class-card" onclick="openClassModal('Kapal', '🚢', 'Kendaraan air berukuran besar maupun kecil, termasuk kapal pesiar, perahu nelayan, kargo, dan feri.')"> | |
| <div class="class-emoji">🚢</div> | |
| <span>Kapal</span> | |
| </div> | |
| <div class="class-card" onclick="openClassModal('Truk', '🚚', 'Kendaraan bermotor berukuran besar yang dirancang khusus untuk mengangkut barang, muatan, atau kargo berat.')"> | |
| <div class="class-emoji">🚚</div> | |
| <span>Truk</span> | |
| </div> | |
| </div> | |
| </section> | |
| <section class="detection-section"> | |
| <div class="panel"> | |
| <div class="panel-header"> | |
| <div class="panel-svg"> | |
| <svg viewBox="0 0 24 24" width="22" height="22" stroke="currentColor" stroke-width="2.5" fill="none" stroke-linecap="round" stroke-linejoin="round"> | |
| <path d="M21 15v4a2 2 0 0 1-2 2H5a2 2 0 0 1-2-2v-4"></path> | |
| <polyline points="17 8 12 3 7 8"></polyline> | |
| <line x1="12" y1="3" x2="12" y2="15"></line> | |
| </svg> | |
| </div> | |
| Input Citra | |
| </div> | |
| <div class="upload-area" id="drop-zone"> | |
| <div id="prompt" style="text-align: center;"> | |
| <div class="plus-icon">+</div> | |
| <div class="upload-text-main">Pilih Gambar Objek</div> | |
| <div class="upload-text-sub">Format: JPG, PNG (Max 5MB)</div> | |
| </div> | |
| <div id="image-preview-container"> | |
| <img id="image-preview" src="#" alt="Preview"> | |
| <button class="btn-change" id="btn-change">Ganti Gambar</button> | |
| </div> | |
| <input type="file" id="file-input" accept="image/jpeg, image/png" hidden> | |
| </div> | |
| </div> | |
| <div class="panel result-panel"> | |
| <div class="panel-header"> | |
| <div class="panel-svg"> | |
| <svg viewBox="0 0 24 24" width="22" height="22" stroke="currentColor" stroke-width="2.5" fill="none" stroke-linecap="round" stroke-linejoin="round"> | |
| <polygon points="12 2 2 7 12 12 22 7 12 2"></polygon> | |
| <polyline points="2 17 12 22 22 17"></polyline> | |
| <polyline points="2 12 12 17 22 12"></polyline> | |
| </svg> | |
| </div> | |
| Hasil ResNet | |
| </div> | |
| <div class="empty-state" id="empty-state"> | |
| <svg class="empty-icon" viewBox="0 0 24 24" stroke-linejoin="round" stroke-linecap="round"> | |
| <path d="M21 15a2 2 0 0 1-2 2H7l-4 4V5a2 2 0 0 1 2-2h14a2 2 0 0 1 2 2z"></path> | |
| </svg> | |
| <div class="empty-title">Belum Ada Data</div> | |
| <div class="empty-desc">Silakan unggah gambar objek di panel sebelah kiri untuk memulai proses deteksi.</div> | |
| </div> | |
| <div class="loader" id="loader"> | |
| <div class="spinner"></div> | |
| <div style="color: #64748b; font-size: 14px;">Memproses prediksi ResNet...</div> | |
| </div> | |
| <div id="results" style="display: none; height: 100%; flex-direction: column;"> | |
| <div class="cards-row"> | |
| <div class="card"> | |
| <p class="model-label">ResNet-MLP</p> | |
| <p class="class-name" id="mlp-name">-</p> | |
| <p class="conf-badge">✓ <span id="mlp-conf">-</span>%</p> | |
| <div class="prob-list" id="mlp-probs"></div> | |
| </div> | |
| <div class="card"> | |
| <p class="model-label">ResNet-KAN</p> | |
| <p class="class-name" id="kan-name">-</p> | |
| <p class="conf-badge">✓ <span id="kan-conf">-</span>%</p> | |
| <div class="prob-list" id="kan-probs"></div> | |
| </div> | |
| </div> | |
| <div class="interpretation" id="inter-box"> | |
| <div class="inter-title" id="inter-title"></div> | |
| <div class="inter-text" id="inter-text"></div> | |
| </div> | |
| </div> | |
| </div> | |
| </section> | |
| <div class="modal" id="classModal"> | |
| <div class="modal-box"> | |
| <span class="close-btn" onclick="closeModal('classModal')">×</span> | |
| <div class="class-modal-emoji" id="c-emoji"></div> | |
| <div class="class-modal-title" id="c-title"></div> | |
| <div class="class-modal-desc" id="c-desc"></div> | |
| </div> | |
| </div> | |
| <div class="modal" id="warningModal"> | |
| <div class="modal-box"> | |
| <span class="close-btn" onclick="closeModal('warningModal')">×</span> | |
| <h2 style="color: #f59e0b; margin-bottom: 10px; font-weight: 700;">⚠️ Kemungkinan Objek Asing</h2> | |
| <p style="color: #475569; font-size: 15px; line-height: 1.6;">Citra ini kemungkinan besar berada di luar 10 kelas CIFAR-10 karena tingkat konfidensi yang rendah. Hasil probabilitas di bawah tetap ditampilkan sebagai referensi.</p> | |
| </div> | |
| </div> | |
| <script> | |
| const dropZone = document.getElementById('drop-zone'); | |
| const fileInput = document.getElementById('file-input'); | |
| const prompt = document.getElementById('prompt'); | |
| const previewContainer = document.getElementById('image-preview-container'); | |
| const preview = document.getElementById('image-preview'); | |
| const btnChange = document.getElementById('btn-change'); | |
| const emptyState = document.getElementById('empty-state'); | |
| const loader = document.getElementById('loader'); | |
| const results = document.getElementById('results'); | |
| dropZone.addEventListener('click', (e) => { | |
| if (e.target !== btnChange) { | |
| fileInput.click(); | |
| } | |
| }); | |
| btnChange.addEventListener('click', (e) => { | |
| e.stopPropagation(); | |
| fileInput.click(); | |
| }); | |
| fileInput.addEventListener('change', (e) => { | |
| const file = e.target.files[0]; | |
| if (file) handleFile(file); | |
| }); | |
| dropZone.addEventListener('dragover', (e) => { | |
| e.preventDefault(); | |
| dropZone.style.borderColor = '#3b82f6'; | |
| dropZone.style.backgroundColor = '#f8fafc'; | |
| }); | |
| dropZone.addEventListener('dragleave', () => { | |
| dropZone.style.borderColor = '#cbd5e0'; | |
| dropZone.style.backgroundColor = '#ffffff'; | |
| }); | |
| dropZone.addEventListener('drop', (e) => { | |
| e.preventDefault(); | |
| dropZone.style.borderColor = '#cbd5e0'; | |
| dropZone.style.backgroundColor = '#ffffff'; | |
| if (e.dataTransfer.files.length) { | |
| fileInput.files = e.dataTransfer.files; | |
| handleFile(e.dataTransfer.files[0]); | |
| } | |
| }); | |
| function handleFile(file) { | |
| if (file.type === 'image/jpeg' || file.type === 'image/png') { | |
| const reader = new FileReader(); | |
| reader.onload = (e) => { | |
| preview.src = e.target.result; | |
| prompt.style.display = 'none'; | |
| previewContainer.style.display = 'flex'; | |
| dropZone.style.borderStyle = 'solid'; | |
| upload(file); | |
| }; | |
| reader.readAsDataURL(file); | |
| } | |
| } | |
| function renderProbs(containerId, probs) { | |
| const container = document.getElementById(containerId); | |
| container.innerHTML = ''; | |
| probs.forEach(p => { | |
| let barColor = '#3b82f6'; | |
| if(p.confidence > 70) barColor = '#10b981'; | |
| else if(p.confidence < 15) barColor = '#94a3b8'; | |
| let indonesianClass = p.class; | |
| const mapId = { | |
| 'airplane': 'Pesawat', 'automobile': 'Mobil', 'bird': 'Burung', 'cat': 'Kucing', | |
| 'deer': 'Rusa', 'dog': 'Anjing', 'frog': 'Katak', 'horse': 'Kuda', | |
| 'ship': 'Kapal', 'truck': 'Truk' | |
| }; | |
| if(mapId[p.class]) indonesianClass = mapId[p.class]; | |
| container.innerHTML += ` | |
| <div class="prob-item"> | |
| <span class="prob-label">${indonesianClass}</span> | |
| <div class="prob-bar-bg"> | |
| <div class="prob-bar-fill" style="width: ${p.confidence}%; background-color: ${barColor};"></div> | |
| </div> | |
| <span class="prob-val">${p.confidence}%</span> | |
| </div> | |
| `; | |
| }); | |
| } | |
| function translateClass(enClass) { | |
| const mapId = { | |
| 'airplane': 'Pesawat', 'automobile': 'Mobil', 'bird': 'Burung', 'cat': 'Kucing', | |
| 'deer': 'Rusa', 'dog': 'Anjing', 'frog': 'Katak', 'horse': 'Kuda', | |
| 'ship': 'Kapal', 'truck': 'Truk' | |
| }; | |
| return mapId[enClass] || enClass; | |
| } | |
| function upload(file) { | |
| emptyState.style.display = 'none'; | |
| results.style.display = 'none'; | |
| loader.style.display = 'flex'; | |
| const formData = new FormData(); | |
| formData.append('file', file); | |
| fetch('/api/predict', { | |
| method: 'POST', | |
| body: formData | |
| }) | |
| .then(res => res.json()) | |
| .then(data => { | |
| loader.style.display = 'none'; | |
| if (data.status === 'error') { | |
| alert("Terjadi kesalahan dari server."); | |
| emptyState.style.display = 'flex'; | |
| return; | |
| } | |
| results.style.display = 'flex'; | |
| const mlpClass = translateClass(data.model_1.class); | |
| const kanClass = translateClass(data.model_2.class); | |
| document.getElementById('mlp-name').innerText = mlpClass; | |
| document.getElementById('mlp-conf').innerText = data.model_1.confidence; | |
| document.getElementById('kan-name').innerText = kanClass; | |
| document.getElementById('kan-conf').innerText = data.model_2.confidence; | |
| renderProbs('mlp-probs', data.model_1.all_probs); | |
| renderProbs('kan-probs', data.model_2.all_probs); | |
| const interBox = document.getElementById('inter-box'); | |
| const it = document.getElementById('inter-title'); | |
| const ix = document.getElementById('inter-text'); | |
| if(mlpClass === kanClass) { | |
| interBox.style.backgroundColor = '#f0fdf4'; | |
| interBox.style.borderLeftColor = '#22c55e'; | |
| it.style.color = '#15803d'; | |
| ix.style.color = '#166534'; | |
| it.innerHTML = 'Konsisten'; | |
| ix.innerHTML = `Kedua model bilang bahwa objek ini adalah <strong>${mlpClass}</strong>.`; | |
| } else { | |
| interBox.style.backgroundColor = '#fff7ed'; | |
| interBox.style.borderLeftColor = '#f97316'; | |
| it.style.color = '#c2410c'; | |
| ix.style.color = '#9a3412'; | |
| it.innerHTML = 'Perbedaan Prediksi'; | |
| ix.innerHTML = `Model ResNet-MLP bilang ini <strong>${mlpClass}</strong>, sedangkan ResNet-KAN bilang ini <strong>${kanClass}</strong>.`; | |
| } | |
| if (data.is_outlier) { | |
| showModal('warningModal'); | |
| } | |
| }) | |
| .catch(err => { | |
| loader.style.display = 'none'; | |
| emptyState.style.display = 'flex'; | |
| alert("Gagal terhubung ke server."); | |
| }); | |
| } | |
| function openClassModal(title, emoji, desc) { | |
| document.getElementById('c-title').innerText = title; | |
| document.getElementById('c-emoji').innerText = emoji; | |
| document.getElementById('c-desc').innerText = desc; | |
| showModal('classModal'); | |
| } | |
| function showModal(id) { | |
| const modal = document.getElementById(id); | |
| modal.style.display = 'flex'; | |
| setTimeout(() => modal.classList.add('show'), 10); | |
| } | |
| function closeModal(id) { | |
| const modal = document.getElementById(id); | |
| modal.classList.remove('show'); | |
| setTimeout(() => modal.style.display = 'none', 300); | |
| } | |
| window.onclick = function(event) { | |
| if (event.target.classList.contains('modal')) { | |
| closeModal(event.target.id); | |
| } | |
| } | |
| </script> | |
| </body> | |
| </html> |