Maftuuh1922 commited on
Commit
e68f9ee
Β·
1 Parent(s): aa5c395

Update app for HF Spaces

Browse files
Files changed (1) hide show
  1. app.py +155 -0
app.py ADDED
@@ -0,0 +1,155 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import os
2
+ import json
3
+ import numpy as np
4
+ # Using ai-edge-litert (Google's new TFLite runtime)
5
+ from ai_edge_litert.interpreter import Interpreter
6
+
7
+ from flask import Flask, request, jsonify
8
+ from flask_cors import CORS
9
+ from PIL import Image
10
+ import io
11
+
12
+ app = Flask(__name__)
13
+ CORS(app)
14
+
15
+ # --- KONFIGURASI ---
16
+ BASE_DIR = os.path.dirname(os.path.abspath(__file__))
17
+ # Pastikan path ini sesuai struktur folder kamu
18
+ MODEL_PATH = os.path.join(BASE_DIR, 'models', 'batik_model.tflite')
19
+ CLASSES_PATH = os.path.join(BASE_DIR, 'models', 'batik_classes_mobilenet_ultimate.json')
20
+
21
+ print("==================================================")
22
+ print("πŸš€ MEMULAI BATIK CLASSIFIER (TFLITE ENGINE V2)")
23
+ print("⚑ Mode: ai-edge-litert Runtime (38 Batik Classes)")
24
+ print("==================================================")
25
+
26
+ # --- 1. LOAD MODEL TFLITE ---
27
+ if not os.path.exists(MODEL_PATH):
28
+ print(f"❌ ERROR: File model TFLite tidak ditemukan: {MODEL_PATH}")
29
+ exit()
30
+
31
+ try:
32
+ # Using ai-edge-litert Interpreter
33
+ interpreter = Interpreter(model_path=MODEL_PATH)
34
+ interpreter.allocate_tensors()
35
+
36
+ # Dapat info input/output
37
+ input_details = interpreter.get_input_details()
38
+ output_details = interpreter.get_output_details()
39
+
40
+ input_shape = input_details[0]['shape']
41
+ output_shape = output_details[0]['shape']
42
+ print(f"βœ… Model TFLite V2 berhasil dimuat!")
43
+ print(f"πŸ“Š Input Shape: {input_shape}, Output Classes: {output_shape[1]}")
44
+ except Exception as e:
45
+ print(f"❌ Gagal load TFLite: {e}")
46
+ exit()
47
+
48
+ # --- 2. LOAD CLASSES ---
49
+ if not os.path.exists(CLASSES_PATH):
50
+ print(f"❌ ERROR: File label tidak ditemukan di {CLASSES_PATH}")
51
+ exit()
52
+
53
+ try:
54
+ with open(CLASSES_PATH, 'r') as f:
55
+ class_data = json.load(f)
56
+
57
+ # Logika parsing JSON kamu sudah bagus!
58
+ if isinstance(class_data, dict) and "classes" in class_data:
59
+ class_names = class_data["classes"]
60
+ elif isinstance(class_data, dict):
61
+ try:
62
+ sorted_keys = sorted(class_data.keys(), key=lambda x: int(x))
63
+ class_names = [class_data[k] for k in sorted_keys]
64
+ except ValueError:
65
+ class_names = list(class_data.values())
66
+ elif isinstance(class_data, list):
67
+ class_names = class_data
68
+ else:
69
+ raise ValueError("Format JSON tidak dikenali.")
70
+
71
+ print(f"βœ… Berhasil memuat {len(class_names)} nama motif batik.")
72
+ # print(f"πŸ“ Contoh: {class_names[:3]}...")
73
+
74
+ except Exception as e:
75
+ print(f"❌ Gagal membaca file JSON Classes: {e}")
76
+ exit()
77
+
78
+ # --- PREPROCESSING ---
79
+ def prepare_image(image, target_size=(224, 224)):
80
+ if image.mode != "RGB":
81
+ image = image.convert("RGB")
82
+ image = image.resize(target_size)
83
+ img_array = np.array(image, dtype=np.float32)
84
+
85
+ # Normalisasi (Pastikan saat training kamu juga dibagi 255.0)
86
+ img_array = img_array / 255.0
87
+
88
+ # Tambah dimensi batch
89
+ img_array = np.expand_dims(img_array, axis=0)
90
+ return img_array
91
+
92
+ @app.route('/', methods=['GET'])
93
+ def home():
94
+ return jsonify({
95
+ "status": "Online",
96
+ "mode": "ai-edge-litert Runtime (BatikLens V2)",
97
+ "model_version": "v2",
98
+ "classes_loaded": len(class_names)
99
+ })
100
+
101
+ @app.route('/predict', methods=['POST'])
102
+ def predict():
103
+ # Support both 'file' and 'image' field names
104
+ file = request.files.get('file') or request.files.get('image')
105
+
106
+ if not file:
107
+ return jsonify({"error": "No file part"}), 400
108
+
109
+ if file.filename == '':
110
+ return jsonify({"error": "No selected file"}), 400
111
+
112
+ try:
113
+ # 1. Baca & Proses Gambar
114
+ image = Image.open(io.BytesIO(file.read()))
115
+ input_data = prepare_image(image)
116
+
117
+ # 2. Masukkan data ke Interpreter
118
+ interpreter.set_tensor(input_details[0]['index'], input_data)
119
+
120
+ # 3. Jalankan Prediksi
121
+ interpreter.invoke()
122
+
123
+ # 4. Ambil Hasil
124
+ output_data = interpreter.get_tensor(output_details[0]['index'])
125
+ predictions = output_data[0]
126
+
127
+ # 5. Cari skor tertinggi
128
+ predicted_index = np.argmax(predictions)
129
+ predicted_label = class_names[predicted_index]
130
+ confidence = float(predictions[predicted_index])
131
+
132
+ # Get top 5 predictions
133
+ top_5_indices = np.argsort(predictions)[-5:][::-1]
134
+ top_5_predictions = [
135
+ {
136
+ "class": class_names[idx],
137
+ "confidence": float(predictions[idx]),
138
+ "percentage": f"{float(predictions[idx]):.2%}"
139
+ }
140
+ for idx in top_5_indices
141
+ ]
142
+
143
+ return jsonify({
144
+ "success": True,
145
+ "prediction": predicted_label,
146
+ "confidence": confidence,
147
+ "percentage": f"{confidence:.2%}",
148
+ "top_5_predictions": top_5_predictions
149
+ })
150
+
151
+ except Exception as e:
152
+ return jsonify({"success": False, "error": str(e)}), 500
153
+
154
+ if __name__ == '__main__':
155
+ app.run(host='0.0.0.0', port=5000)