berkeruveyik commited on
Commit
f2ae75a
·
verified ·
1 Parent(s): f2e87f9

Update app.py

Browse files
Files changed (1) hide show
  1. app.py +139 -156
app.py CHANGED
@@ -1,169 +1,152 @@
1
- # app.py
2
- import os
3
  import numpy as np
4
  from PIL import Image
5
- import gradio as gr
6
-
7
- # If you prefer to download the model from a HF model repo, set these:
8
- USE_HF_MODEL_REPO = False
9
- HF_MODEL_REPO = "berkeruveyik/food270-model" # not used if USE_HF_MODEL_REPO=False
10
- HF_MODEL_FILENAME = "finetuned_food_270.keras" # name on HF model repo (if used)
11
-
12
- # Local model filename (should be in the Space repo root)
13
- LOCAL_MODEL_PATH = "finetuned_food_270.keras"
14
 
15
- TOP_K = 5
16
- input_size = (224, 224) # fallback - will try to infer from model
 
 
17
 
18
- # lazy imports / model loader
19
- model = None
20
- class_names = None
 
21
 
22
- def download_model_from_hub():
23
- from huggingface_hub import hf_hub_download
24
- print("Downloading model from HF repo...")
25
- path = hf_hub_download(repo_id=HF_MODEL_REPO, filename=HF_MODEL_FILENAME, repo_type="model")
26
- print("Downloaded to:", path)
27
- return path
28
 
29
- def load_labels_from_file(path="labels.txt"):
30
- if os.path.exists(path):
31
- with open(path, "r", encoding="utf-8") as f:
32
- labels = [l.strip() for l in f if l.strip()]
33
- return labels
34
- return None
35
-
36
- def load_model():
37
- global model, class_names, input_size
38
- if model is not None:
39
- return model
40
-
41
- # determine model path
42
- model_path = None
43
- if USE_HF_MODEL_REPO:
44
- model_path = download_model_from_hub()
45
  else:
46
- if os.path.exists(LOCAL_MODEL_PATH):
47
- model_path = LOCAL_MODEL_PATH
48
- else:
49
- raise FileNotFoundError(
50
- f"Model file not found at {LOCAL_MODEL_PATH}. Upload '{LOCAL_MODEL_PATH}' to the Space root or enable USE_HF_MODEL_REPO and set HF_MODEL_REPO."
51
- )
 
 
 
 
 
 
 
 
 
 
 
 
52
 
53
- # import tensorflow lazily (reduce startup errors)
54
- import tensorflow as tf
55
- # If the model is a weights-only file, user might have saved model.save_weights(...)
56
- # We'll try to load it as a full model first, then as weights into a simple architecture if necessary.
57
  try:
58
- loaded = tf.keras.models.load_model(model_path, compile=False)
59
- model = loaded
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
60
  except Exception as e:
61
- # Try to load weights into a minimal model architecture as fallback:
62
- # WARNING: This fallback requires you to have the same architecture defined here.
63
- # If that is not the case, prefer uploading a full model (model.save()) instead of only weights.
64
- print("load_model failed, attempting to load weights into a small default model. Error:", e)
65
- # Example fallback architecture (replace with your actual architecture if needed)
66
- from tensorflow.keras import layers, models
67
- try:
68
- n_classes = 270
69
- inp = layers.Input(shape=(224,224,3))
70
- x = layers.Rescaling(1./255)(inp)
71
- x = layers.Conv2D(32,3,activation="relu")(x)
72
- x = layers.MaxPool2D()(x)
73
- x = layers.GlobalAveragePooling2D()(x)
74
- x = layers.Dense(256, activation="relu")(x)
75
- out = layers.Dense(n_classes, activation=None)(x)
76
- fallback = models.Model(inputs=inp, outputs=out)
77
- fallback.load_weights(model_path)
78
- model = fallback
79
- print("Loaded weights into fallback model (verify architecture).")
80
- except Exception as e2:
81
- raise RuntimeError("Could not load model or weights. Please upload a full Keras model (model.save) instead of only weights, or adjust the fallback architecture.") from e2
82
-
83
- # try infer input size from model
84
- try:
85
- shp = model.input_shape
86
- if isinstance(shp, (list, tuple)):
87
- if len(shp) == 4:
88
- _, h, w, c = shp
89
- if h and w:
90
- input_size = (int(h), int(w))
91
- elif len(shp) == 3:
92
- h, w, c = shp
93
- input_size = (int(h), int(w))
94
- except Exception:
95
- pass
96
-
97
- # load labels if available
98
- lbls = load_labels_from_file("labels.txt")
99
- if lbls:
100
- class_names = lbls
101
- else:
102
- # fallback: create class_0...class_269 if model output size unknown
103
- try:
104
- out_shape = model.output_shape
105
- if isinstance(out_shape, list):
106
- out_shape = out_shape[0]
107
- n_classes = int(out_shape[-1]) if out_shape is not None else 270
108
- except Exception:
109
- n_classes = 270
110
- class_names = [f"class_{i}" for i in range(n_classes)]
111
-
112
- print(f"Model loaded. input_size={input_size}, num_classes={len(class_names)}")
113
- return model
114
-
115
- def preprocess_image(image: Image.Image):
116
- # ensure RGB
117
- if image.mode != "RGB":
118
- image = image.convert("RGB")
119
- # resize
120
- image = image.resize(input_size, resample=Image.BILINEAR)
121
- arr = np.asarray(image).astype(np.float32) / 255.0
122
- if arr.ndim == 3:
123
- arr = np.expand_dims(arr, axis=0)
124
- return arr
125
-
126
- def predict(image):
127
- model = load_model()
128
- x = preprocess_image(image)
129
- preds = model.predict(x)
130
- if preds.ndim == 2:
131
- probs = preds[0]
132
- else:
133
- probs = np.asarray(preds).reshape(-1)
134
- # try softmax if not probability
135
- s = float(np.sum(probs))
136
- if not (0.9 < s < 1.1):
137
- from scipy.special import softmax
138
- probs = softmax(probs)
139
- top_idx = probs.argsort()[-TOP_K:][::-1]
140
- results = []
141
- for i in top_idx:
142
- name = class_names[i] if i < len(class_names) else str(i)
143
- results.append((name, float(probs[i])))
144
- md = "\n".join([f"{name}: {prob*100:.2f}%" for name, prob in results])
145
- # return markdown + dict for chart
146
- return md, {r[0]: r[1] for r in results}
147
-
148
- # Gradio UI
149
- title = "Food-270 Classifier"
150
- description = "Upload a food image and get top predictions (model: finetuned_cp.weights.h5)."
151
-
152
- with gr.Blocks() as demo:
153
- gr.Markdown(f"# {title}\n\n{description}")
154
  with gr.Row():
155
- img_in = gr.Image(type="pil", label="Upload an image")
156
- out_md = gr.Markdown()
157
- with gr.Row():
158
- btn = gr.Button("Predict")
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
159
  with gr.Row():
160
- chart = gr.Label(num_top_classes=TOP_K, label="Top predictions")
161
- btn.click(fn=lambda img: predict(img), inputs=img_in, outputs=[out_md, chart])
162
-
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
163
  if __name__ == "__main__":
164
- # optional warm-up model load
165
- try:
166
- load_model()
167
- except Exception as e:
168
- print("Model loading at start failed:", e)
169
- demo.launch(server_name="0.0.0.0", share=False)
 
1
+ import gradio as gr
2
+ import tensorflow as tf
3
  import numpy as np
4
  from PIL import Image
 
 
 
 
 
 
 
 
 
5
 
6
+ # Model'i yükle
7
+ print("Model yükleniyor...")
8
+ model = tf.keras.models.load_model('finetuned_food_270.keras')
9
+ print("Model başarıyla yüklendi!")
10
 
11
+ # Sınıf isimlerini yükle
12
+ with open('labels.txt', 'r', encoding='utf-8') as f:
13
+ class_names = [line.strip() for line in f.readlines()]
14
+ print(f"{len(class_names)} sınıf yüklendi")
15
 
16
+ # Görüntü boyutu (EfficientNet için)
17
+ IMG_SIZE = (224, 224) # EfficientNetB0 için, B2 kullanıyorsanız (260, 260) yapın
 
 
 
 
18
 
19
+ def preprocess_image(image):
20
+ """Resmi model için hazırla"""
21
+ # PIL Image'a çevir ve RGB yap
22
+ if isinstance(image, np.ndarray):
23
+ img = Image.fromarray(image)
 
 
 
 
 
 
 
 
 
 
 
24
  else:
25
+ img = image
26
+
27
+ # RGB'ye çevir (grayscale olabilir)
28
+ img = img.convert('RGB')
29
+
30
+ # Resize
31
+ img = img.resize(IMG_SIZE)
32
+
33
+ # Numpy array'e çevir
34
+ img_array = np.array(img, dtype=np.float32)
35
+
36
+ # Batch dimension ekle
37
+ img_array = np.expand_dims(img_array, axis=0)
38
+
39
+ # EfficientNet preprocessing (imagenet normalization)
40
+ img_array = tf.keras.applications.efficientnet.preprocess_input(img_array)
41
+
42
+ return img_array
43
 
44
+ def predict(image):
45
+ """Yemek tahmin fonksiyonu"""
 
 
46
  try:
47
+ # Görüntüyü işle
48
+ processed_image = preprocess_image(image)
49
+
50
+ # Tahmin yap
51
+ predictions = model.predict(processed_image, verbose=0)
52
+
53
+ # Softmax çıktısını al (ilk batch)
54
+ predictions = predictions[0]
55
+
56
+ # Top 5 tahmini bul
57
+ top_indices = np.argsort(predictions)[-5:][::-1]
58
+
59
+ # Sonuçları hazırla
60
+ results = {}
61
+ for idx in top_indices:
62
+ label = class_names[idx]
63
+ confidence = float(predictions[idx])
64
+ results[label] = confidence
65
+
66
+ return results
67
+
68
  except Exception as e:
69
+ print(f"Hata: {e}")
70
+ return {"Hata": str(e)}
71
+
72
+ # Gradio arayüzü
73
+ with gr.Blocks(title="Food 270 Classifier") as demo:
74
+ # Başlık ve açıklama
75
+ gr.Markdown("""
76
+ # 🍽️ Food 270 Yemek Sınıflandırıcı
77
+
78
+ **270 farklı yemek türünü tanıyabilen yapay zeka modeli**
79
+
80
+ 📸 Bir yemek fotoğrafı yükleyin ve hangi yemek olduğunu tahmin ettirin!
81
+ """)
82
+
83
+ # Ana satır
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
84
  with gr.Row():
85
+ # Sol kolon - Input
86
+ with gr.Column(scale=1):
87
+ input_image = gr.Image(
88
+ label="Yemek Fotoğrafı",
89
+ type="numpy"
90
+ )
91
+
92
+ # Butonlar
93
+ with gr.Row():
94
+ clear_btn = gr.Button("🗑️ Temizle", variant="secondary")
95
+ submit_btn = gr.Button("🔍 Tahmin Et", variant="primary")
96
+
97
+ # Sağ kolon - Output
98
+ with gr.Column(scale=1):
99
+ output = gr.Label(
100
+ label="Tahmin Sonuçları",
101
+ num_top_classes=5,
102
+ show_label=True
103
+ )
104
+
105
+ # Ek bilgi
106
+ gr.Markdown("""
107
+ ### 📊 Sonuç Açıklaması
108
+ - **En yüksek skor** en olası yemek türüdür
109
+ - Skorlar **0-1 arası** güven seviyesini gösterir
110
+ - **Top 5** en olası tahmin gösterilir
111
+ """)
112
+
113
+ # Alt kısım - Örnekler ve bilgi
114
  with gr.Row():
115
+ gr.Markdown("""
116
+ ### 💡 İpuçları
117
+ - Net ve iyi ışıklandırılmış fotoğraflar daha iyi sonuç verir
118
+ - Yemeğin tam görünmesi tahmini iyileştirir
119
+ - Tek bir yemek türü içeren fotoğraflar idealdir
120
+ """)
121
+
122
+ # Model bilgisi
123
+ gr.Markdown("""
124
+ ---
125
+ ### 🤖 Model Bilgileri
126
+ - **Model**: EfficientNet (Fine-tuned)
127
+ - **Dataset**: Food 270
128
+ - **Sınıf Sayısı**: 270 farklı yemek
129
+ - **Doğruluk**: Train sonrası elde edilen accuracy değerinizi buraya yazın
130
+
131
+ *Geliştirici: Berker Üveyik*
132
+ """)
133
+
134
+ # Event handlers
135
+ submit_btn.click(
136
+ fn=predict,
137
+ inputs=input_image,
138
+ outputs=output
139
+ )
140
+
141
+ clear_btn.click(
142
+ lambda: (None, None),
143
+ inputs=None,
144
+ outputs=[input_image, output]
145
+ )
146
+
147
+ # Uygulamayı başlat
148
  if __name__ == "__main__":
149
+ demo.launch(
150
+ share=False, # Space'de False olmalı
151
+ debug=False # Production'da False olmalı
152
+ )