diff --git a/backend-ai/app.py b/backend-ai/app.py new file mode 100644 index 0000000..9a7937b --- /dev/null +++ b/backend-ai/app.py @@ -0,0 +1,74 @@ +import os +import io +import numpy as np +import tensorflow as tf +from flask import Flask, request, jsonify +from flask_cors import CORS +from PIL import Image + +app = Flask(__name__) +CORS(app) + +# 1. SETUP PATH MODEL +BASE_DIR = os.path.dirname(os.path.abspath(__file__)) +MODEL_PATH = os.path.join(BASE_DIR, 'model_efficientNet.keras') + +# 2. DUMMY PREPROCESS (Agar tidak error saat load Lambda layer) +def preprocess_input(x): + return x + +print("⏳ Sedang memuat 'Otak AI'...") + +try: + # Menggunakan parameter Keras 3 untuk memuat model lama + model = tf.keras.models.load_model( + MODEL_PATH, + custom_objects={'preprocess_input': preprocess_input}, + compile=False, + safe_mode=False # Kunci agar Keras 3 mau menerima config Keras lama + ) + print("✅ BERHASIL: Model AI siap digunakan!") +except Exception as e: + print(f"❌ GAGAL: {str(e)}") + print("--- SOLUSI ---") + print("Jika error 'Unrecognized keyword arguments', silakan info ke saya.") + +# Label klasifikasi kopi kamu +labels = ['Honey', 'Natural', 'Washed'] + +@app.route('/predict', methods=['POST']) +def predict(): + try: + file = request.files['image'] + + # 1. Load Gambar & Resize ke 224x224 + img = Image.open(file.stream).convert('RGB') + img = img.resize((224, 224)) + + # 2. Konversi ke Array + img_array = np.array(img).astype('float32') + + # 3. JURUS SAKTI: Gunakan preprocessing asli EfficientNet + # Ini akan menangani scaling warna agar sama persis dengan saat training + img_array = tf.keras.applications.efficientnet.preprocess_input(img_array) + img_array = np.expand_dims(img_array, axis=0) + + # 4. Prediksi + preds = model.predict(img_array, verbose=0) + class_idx = np.argmax(preds[0]) + confidence = float(np.max(preds[0])) + + print(f"📥 Prediksi: {labels[class_idx]} ({confidence*100:.2f}%)") + + return jsonify({ + 'label': labels[class_idx], + 'confidence': f"{confidence * 100:.2f}%", + 'status': 'success' + }) + except Exception as e: + print(f"❌ ERROR PREDIKSI: {str(e)}") + return jsonify({'error': str(e)}), 500 + +if __name__ == '__main__': + # Jalankan di port 5001 agar tidak diblokir Windows AirPlay + app.run(host='127.0.0.1', port=5001, debug=True) \ No newline at end of file diff --git a/backend-ai/model_efficientNet.h5 b/backend-ai/model_efficientNet.h5 new file mode 100644 index 0000000..e6770bf Binary files /dev/null and b/backend-ai/model_efficientNet.h5 differ diff --git a/backend-ai/model_efficientNet.keras b/backend-ai/model_efficientNet.keras new file mode 100644 index 0000000..1152ac6 Binary files /dev/null and b/backend-ai/model_efficientNet.keras differ diff --git a/backend-ai/requirements.txt b/backend-ai/requirements.txt new file mode 100644 index 0000000..de91053 --- /dev/null +++ b/backend-ai/requirements.txt @@ -0,0 +1,5 @@ +flask==3.0.2 +flask-cors==4.0.0 +tensorflow==2.15.0 +numpy==1.26.4 +Pillow==10.2.0 \ No newline at end of file diff --git a/resources/js/components/public/Keunggulan.vue b/resources/js/components/public/Keunggulan.vue new file mode 100644 index 0000000..bf7cf04 --- /dev/null +++ b/resources/js/components/public/Keunggulan.vue @@ -0,0 +1,141 @@ + + + diff --git a/resources/js/components/public/LogoMarquee.vue b/resources/js/components/public/Logo.vue similarity index 100% rename from resources/js/components/public/LogoMarquee.vue rename to resources/js/components/public/Logo.vue diff --git a/resources/js/components/public/BentoGrid.vue b/resources/js/components/public/Scanner.vue similarity index 51% rename from resources/js/components/public/BentoGrid.vue rename to resources/js/components/public/Scanner.vue index d36d820..4428403 100644 --- a/resources/js/components/public/BentoGrid.vue +++ b/resources/js/components/public/Scanner.vue @@ -1,20 +1,17 @@