Spaces:
Sleeping
Sleeping
| <template> | |
| <div class="container"> | |
| <h1>CIFAR-10 Image Checker</h1> | |
| <label class="upload-el"> | |
| <input type="file" accept="image/*" @change="fileChanged" /> | |
| <span>Select Image</span> | |
| </label> | |
| <div v-if="prev" class="preview"> | |
| <img :src="prev" alt="Preview" /> | |
| </div> | |
| <button v-if="prev" class="submit" :disabled="!imageFile || loading" @click="submitImage"> | |
| {{ loading ? 'Loading...' : 'Check Class' }} | |
| </button> | |
| <div v-if="loading" class="loader"></div> | |
| <div v-if="prediction" class="result"> | |
| <h3>Hasil Prediksi:</h3> | |
| <p>{{ prediction }}</p> | |
| </div> | |
| </div> | |
| </template> | |
| <script> | |
| export default { | |
| data() { | |
| return { | |
| imageFile: null, | |
| prev: null, | |
| prediction: null, | |
| loading: false, | |
| } | |
| }, | |
| methods: { | |
| fileChanged(event) { | |
| const file = event.target.files[0] | |
| if (file) { | |
| this.imageFile = file | |
| this.prev = URL.createObjectURL(file) | |
| this.prediction = null | |
| } | |
| }, | |
| async submitImage() { | |
| if(this.loading == true) return; | |
| this.loading = true | |
| this.prediction = null | |
| const formData = new FormData() | |
| formData.append('file', this.imageFile) | |
| try { | |
| const response = await fetch('/predict', { | |
| method: 'POST', | |
| body: formData, | |
| }) | |
| const data = await response.json(); | |
| if(data.error){ | |
| this.prediction = data.error; | |
| return; | |
| } | |
| const classNames = [ | |
| 'Pesawat', | |
| 'Mobil', | |
| 'Burung', | |
| 'Kucing', | |
| 'Rusa', | |
| 'Anjing', | |
| 'Katak', | |
| 'Kuda', | |
| 'Kambing', | |
| 'Truk', | |
| ] | |
| const index = data.class | |
| this.prediction = `${classNames[index]} (Confident Level: ${(data.confidence * 100).toFixed(2)}%)` | |
| } catch (err) { | |
| console.error(err) | |
| this.prediction = 'Maaf, terjadi kesalahan ketika proses prediksi'; | |
| } finally { | |
| this.loading = false | |
| } | |
| }, | |
| }, | |
| } | |
| </script> | |
| <style scoped> | |
| .container { | |
| max-width: 480px; | |
| margin: 40px auto; | |
| padding: 24px; | |
| border-radius: 12px; | |
| box-shadow: 0 0 10px rgba(0, 0, 0, 0.08); | |
| background-color: #fff; | |
| font-family: 'Segoe UI', sans-serif; | |
| text-align: center; | |
| } | |
| h1 { | |
| margin-bottom: 24px; | |
| color: #7a7a7a; | |
| font-size: 24px; | |
| } | |
| .upload-el { | |
| display: block; | |
| background-color: #e0e7ff; | |
| color: #1e40af; | |
| padding: 10px 20px; | |
| border-radius: 6px; | |
| font-weight: 600; | |
| cursor: pointer; | |
| margin-bottom: 16px; | |
| transition: background 0.3s ease; | |
| } | |
| .upload-el:hover { | |
| background-color: #c7d2fe; | |
| } | |
| .upload-el input { | |
| display: none; | |
| } | |
| .preview img { | |
| margin-top: 16px; | |
| max-width: 100%; | |
| min-width: 150px; | |
| border-radius: 6px; | |
| border: 1px solid #ddd; | |
| } | |
| .submit { | |
| margin-top: 16px; | |
| background-color: #2563eb; | |
| color: white; | |
| padding: 10px 20px; | |
| border: none; | |
| border-radius: 6px; | |
| font-weight: bold; | |
| cursor: pointer; | |
| display: block; | |
| width: 100%; | |
| transition: background 0.3s ease; | |
| } | |
| .submit:disabled { | |
| background-color: #9ca3af; | |
| cursor: not-allowed; | |
| } | |
| .submit:hover:enabled { | |
| background-color: #1d4ed8; | |
| } | |
| .result { | |
| margin-top: 24px; | |
| padding: 16px; | |
| background-color: #f1f5f9; | |
| border-left: 5px solid #2563eb; | |
| border-radius: 6px; | |
| text-align: left; | |
| } | |
| .loader { | |
| margin: 20px auto; | |
| border: 4px solid #e0e0e0; | |
| border-top: 4px solid #2563eb; | |
| border-radius: 50%; | |
| width: 30px; | |
| height: 30px; | |
| animation: spin 1s linear infinite; | |
| } | |
| @keyframes spin { | |
| 0% { | |
| transform: rotate(0); | |
| } | |
| 100% { | |
| transform: rotate(360deg); | |
| } | |
| } | |
| </style> | |