| <!DOCTYPE html>
|
| <html lang="id">
|
| <head>
|
| <meta charset="UTF-8">
|
| <meta name="viewport" content="width=device-width, initial-scale=1.0">
|
| <title>ResNet50 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 ResNet50-MLP dan ResNet50-KAN pada CIFAR10</h1>
|
| <div class="hero-subtitle">Rakyan Adhitya Mahendra (2206824331)</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 RGB 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 ResNet50
|
| </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 ResNet50...</div>
|
| </div>
|
|
|
| <div id="results" style="display: none; height: 100%; flex-direction: column;">
|
| <div class="cards-row">
|
| <div class="card">
|
| <p class="model-label">ResNet50-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">ResNet50-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 mengkategorikan 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 ResNet50-MLP mengkategorikan ini sebagai <strong>${mlpClass}</strong>, sedangkan ResNet50-KAN mengkategorikan ini sebagai <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> |