sirpaused commited on
Commit
e094fd4
·
verified ·
1 Parent(s): 70357a6

Upload 4 files

Browse files
.gitattributes CHANGED
@@ -33,3 +33,5 @@ saved_model/**/* filter=lfs diff=lfs merge=lfs -text
33
  *.zip filter=lfs diff=lfs merge=lfs -text
34
  *.zst filter=lfs diff=lfs merge=lfs -text
35
  *tfevents* filter=lfs diff=lfs merge=lfs -text
 
 
 
33
  *.zip filter=lfs diff=lfs merge=lfs -text
34
  *.zst filter=lfs diff=lfs merge=lfs -text
35
  *tfevents* filter=lfs diff=lfs merge=lfs -text
36
+ final_ensemble_model.keras filter=lfs diff=lfs merge=lfs -text
37
+ flower_recognition_model.keras filter=lfs diff=lfs merge=lfs -text
app.py ADDED
@@ -0,0 +1,329 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ # app.py - версия с двумя моделями
2
+ import gradio as gr
3
+ import numpy as np
4
+ from PIL import Image
5
+ import tensorflow as tf
6
+ from tensorflow.keras.models import load_model
7
+ import os
8
+
9
+ # Конфигурация
10
+ IMG_SIZE_150 = 150
11
+ IMG_SIZE_224 = 224
12
+
13
+ # Порядок классов
14
+ CLASS_NAMES = ['Daisy', 'Dandelion', 'Rose', 'Sunflower', 'Tulip']
15
+
16
+ # Пути к моделям
17
+ MODEL_150_PATH = 'flower_recognition_model.keras' # модель на 150x150
18
+ MODEL_224_PATH = 'final_ensemble_model.keras' # модель на 224x224
19
+
20
+ # Глобальные переменные
21
+ model_150 = None
22
+ model_224 = None
23
+
24
+ def load_models():
25
+ """Загрузка обеих моделей"""
26
+ global model_150, model_224
27
+
28
+ # Загрузка модели 150x150
29
+ try:
30
+ if os.path.exists(MODEL_150_PATH):
31
+ model_150 = load_model(MODEL_150_PATH)
32
+ print(f"✅ Model 150x150 loaded from {MODEL_150_PATH}")
33
+ else:
34
+ print(f"⚠️ Model 150x150 not found at {MODEL_150_PATH}")
35
+ except Exception as e:
36
+ print(f"❌ Error loading model 150x150: {e}")
37
+
38
+ # Загрузка модели 224x224
39
+ try:
40
+ if os.path.exists(MODEL_224_PATH):
41
+ model_224 = load_model(MODEL_224_PATH)
42
+ print(f"✅ Model 224x224 loaded from {MODEL_224_PATH}")
43
+ else:
44
+ print(f"⚠️ Model 224x224 not found at {MODEL_224_PATH}")
45
+ except Exception as e:
46
+ print(f"❌ Error loading model 224x224: {e}")
47
+
48
+ # Функции для обработки изображений
49
+ try:
50
+ import cv2
51
+ USE_CV2 = True
52
+ print("✅ Using OpenCV for image processing")
53
+ except ImportError:
54
+ USE_CV2 = False
55
+ print("⚠️ Using PIL fallback for image processing")
56
+
57
+ def resize_image(img_array, target_size):
58
+ """Ресайз изображения"""
59
+ if USE_CV2:
60
+ return cv2.resize(img_array, target_size)
61
+ else:
62
+ from PIL import Image
63
+ img_pil = Image.fromarray(img_array.astype('uint8'))
64
+ img_resized = img_pil.resize(target_size, Image.Resampling.LANCZOS)
65
+ return np.array(img_resized)
66
+
67
+ def convert_to_bgr(img_array):
68
+ """Конвертация RGB -> BGR"""
69
+ if USE_CV2:
70
+ return cv2.cvtColor(img_array, cv2.COLOR_RGB2BGR)
71
+ else:
72
+ return img_array[:, :, ::-1]
73
+
74
+ def preprocess_image(image, img_size):
75
+ """
76
+ Предобработка изображения для конкретной модели
77
+ img_size: tuple (height, width)
78
+ """
79
+ # Конвертируем PIL в numpy
80
+ if isinstance(image, Image.Image):
81
+ img_array = np.array(image)
82
+ else:
83
+ img_array = np.array(image)
84
+
85
+ # Конвертируем RGB в BGR (как в Colab)
86
+ img_bgr = convert_to_bgr(img_array)
87
+
88
+ # Ресайз
89
+ img_resized = resize_image(img_bgr, img_size)
90
+
91
+ # Нормализация и добавление batch dimension
92
+ img_normalized = img_resized / 255.0
93
+ img_batch = np.expand_dims(img_normalized, axis=0)
94
+
95
+ return img_batch
96
+
97
+ def predict_with_model(model, image, img_size, model_name):
98
+ """Предсказание одной моделью"""
99
+ if model is None:
100
+ return None, f"❌ Модель {model_name} не загружена"
101
+
102
+ try:
103
+ processed_img = preprocess_image(image, img_size)
104
+ predictions = model.predict(processed_img, verbose=0)
105
+ predicted_index = np.argmax(predictions[0])
106
+ confidence = float(predictions[0][predicted_index])
107
+ predicted_flower = CLASS_NAMES[predicted_index]
108
+
109
+ probabilities = {
110
+ class_name: float(predictions[0][i])
111
+ for i, class_name in enumerate(CLASS_NAMES)
112
+ }
113
+
114
+ return {
115
+ 'flower': predicted_flower,
116
+ 'confidence': confidence,
117
+ 'probabilities': probabilities,
118
+ 'model': model_name
119
+ }, None
120
+
121
+ except Exception as e:
122
+ return None, f"❌ Ошибка в модели {model_name}: {str(e)}"
123
+
124
+ def predict_ensemble(image):
125
+ """
126
+ Предсказание с использованием двух моделей
127
+ Результат: усреднение предсказаний или выбор лучшего
128
+ """
129
+ results = []
130
+ errors = []
131
+
132
+ # Предсказание моделью 150x150
133
+ if model_150 is not None:
134
+ result_150, error_150 = predict_with_model(
135
+ model_150, image, (IMG_SIZE_150, IMG_SIZE_150), "150x150"
136
+ )
137
+ if result_150:
138
+ results.append(result_150)
139
+ elif error_150:
140
+ errors.append(error_150)
141
+
142
+ # Предсказание моделью 224x224
143
+ if model_224 is not None:
144
+ result_224, error_224 = predict_with_model(
145
+ model_224, image, (IMG_SIZE_224, IMG_SIZE_224), "224x224"
146
+ )
147
+ if result_224:
148
+ results.append(result_224)
149
+ elif error_224:
150
+ errors.append(error_224)
151
+
152
+ if not results:
153
+ error_msg = "\n".join(errors) if errors else "❌ Нет доступных моделей"
154
+ return error_msg, None
155
+
156
+ # Усредняем вероятности
157
+ avg_probabilities = {}
158
+ for class_name in CLASS_NAMES:
159
+ probs = [r['probabilities'][class_name] for r in results]
160
+ avg_probabilities[class_name] = np.mean(probs)
161
+
162
+ # Выбираем класс с максимальной средней вероятностью
163
+ predicted_index = np.argmax(list(avg_probabilities.values()))
164
+ predicted_flower = CLASS_NAMES[predicted_index]
165
+ avg_confidence = avg_probabilities[predicted_flower]
166
+
167
+ # Определяем, какая модель была увереннее
168
+ model_confidences = []
169
+ for r in results:
170
+ model_confidences.append(f"{r['model']}: {r['confidence']:.1%}")
171
+ model_info = " | ".join(model_confidences)
172
+
173
+ # Формируем результат
174
+ if avg_confidence > 0.7:
175
+ confidence_emoji = "🎯"
176
+ elif avg_confidence > 0.4:
177
+ confidence_emoji = "👍"
178
+ else:
179
+ confidence_emoji = "🤔"
180
+
181
+ # Сортируем вероятности
182
+ sorted_probs = sorted(avg_probabilities.items(), key=lambda x: x[1], reverse=True)
183
+
184
+ result_text = f"""
185
+ ## 🌸 **{predicted_flower}** {confidence_emoji}
186
+
187
+ ### Уверенность (ансамбль): **{avg_confidence:.1%}**
188
+
189
+ ---
190
+ **📊 Детали по моделям:**
191
+ {model_info}
192
+
193
+ **🎯 Вероятности по классам:**
194
+ {chr(10).join([f"- {name}: {prob:.1%}" for name, prob in sorted_probs])}
195
+
196
+ ---
197
+ *Ансамбль из 2 нейросетей (150x150 и 224x224)*
198
+ """
199
+
200
+ return result_text, avg_probabilities
201
+
202
+ def predict_single_150(image):
203
+ """Предсказание только моделью 150x150"""
204
+ if model_150 is None:
205
+ return "❌ Модель 150x150 не загружена", None
206
+
207
+ result, error = predict_with_model(
208
+ model_150, image, (IMG_SIZE_150, IMG_SIZE_150), "150x150"
209
+ )
210
+
211
+ if error:
212
+ return error, None
213
+
214
+ # Форматируем результат
215
+ sorted_probs = sorted(result['probabilities'].items(), key=lambda x: x[1], reverse=True)
216
+
217
+ result_text = f"""
218
+ ## 🌸 **{result['flower']}**
219
+
220
+ ### Уверенность: **{result['confidence']:.1%}**
221
+ *Модель: 150x150*
222
+
223
+ ---
224
+ **Вероятности:**
225
+ {chr(10).join([f"- {name}: {prob:.1%}" for name, prob in sorted_probs])}
226
+ """
227
+
228
+ return result_text, result['probabilities']
229
+
230
+ def predict_single_224(image):
231
+ """Предсказание только моделью 224x224"""
232
+ if model_224 is None:
233
+ return "❌ Модель 224x224 не загружена", None
234
+
235
+ result, error = predict_with_model(
236
+ model_224, image, (IMG_SIZE_224, IMG_SIZE_224), "224x224"
237
+ )
238
+
239
+ if error:
240
+ return error, None
241
+
242
+ # Форматируем результат
243
+ sorted_probs = sorted(result['probabilities'].items(), key=lambda x: x[1], reverse=True)
244
+
245
+ result_text = f"""
246
+ ## 🌸 **{result['flower']}**
247
+
248
+ ### Уверенность: **{result['confidence']:.1%}**
249
+ *Модель: 224x224*
250
+
251
+ ---
252
+ **Вероятности:**
253
+ {chr(10).join([f"- {name}: {prob:.1%}" for name, prob in sorted_probs])}
254
+ """
255
+
256
+ return result_text, result['probabilities']
257
+
258
+ # Загружаем модели при старте
259
+ load_models()
260
+
261
+ # Создаем интерфейс Gradio
262
+ with gr.Blocks(title="Flower Recognition - Ensemble of 2 CNNs", theme="soft") as demo:
263
+ gr.Markdown("""
264
+ # 🌼 Flower Recognition - Ансамбль из 2 нейросетей 🌻
265
+
266
+ ### Определяет 5 видов цветов: Daisy, Dandelion, Rose, Sunflower, Tulip
267
+
268
+ **🎯 Доступные модели:**
269
+ - Модель 1: CNN 150x150 пикселей
270
+ - Модель 2: CNN 224x224 пикселей (final_ensemble_model)
271
+ - Ансамбль: усреднение предсказаний обеих моделей
272
+ """)
273
+
274
+ with gr.Row():
275
+ with gr.Column():
276
+ input_image = gr.Image(label="📸 Загрузите фото цветка", type="pil", height=350)
277
+
278
+ with gr.Row():
279
+ ensemble_btn = gr.Button("🎯 Ансамбль (2 модели)", variant="primary", size="lg")
280
+
281
+ with gr.Row():
282
+ model150_btn = gr.Button("📱 Модель Оленбергер Данила", variant="secondary")
283
+ model224_btn = gr.Button("💻 Модель Виговской Марии", variant="secondary")
284
+
285
+ clear_btn = gr.Button("🗑️ Очистить", size="sm")
286
+
287
+ with gr.Column():
288
+ output_text = gr.Markdown(label="📊 Результат", value="### ⏳ Выберите модель и загрузите фото")
289
+ output_probs = gr.Label(label="📈 Вероятности по классам", num_top_classes=5)
290
+
291
+ with gr.Row():
292
+ gr.Markdown("""
293
+ ---
294
+ **💡 Как это работает:**
295
+ - **Ансамбль** - использует обе модели и усредняет их предсказания (рекомендуется)
296
+ - **150x150** - быстрая модель, хороша для простых случаев
297
+ - **224x224** - более точная модель, требует больше ресурсов
298
+
299
+ **🎨 Порядок цветов:**
300
+ Daisy (Маргаритка) → Dandelion (Одуванчик) → Rose (Роза) → Sunflower (Подсолнух) → Tulip (Тюльпан)
301
+ """)
302
+
303
+ # Обработчики
304
+ ensemble_btn.click(
305
+ fn=predict_ensemble,
306
+ inputs=input_image,
307
+ outputs=[output_text, output_probs]
308
+ )
309
+
310
+ model150_btn.click(
311
+ fn=predict_single_150,
312
+ inputs=input_image,
313
+ outputs=[output_text, output_probs]
314
+ )
315
+
316
+ model224_btn.click(
317
+ fn=predict_single_224,
318
+ inputs=input_image,
319
+ outputs=[output_text, output_probs]
320
+ )
321
+
322
+ clear_btn.click(
323
+ fn=lambda: [None, "### ⏳ Выберите модель и загрузите фото", None],
324
+ inputs=None,
325
+ outputs=[input_image, output_text, output_probs]
326
+ )
327
+
328
+ if __name__ == "__main__":
329
+ demo.launch()
final_ensemble_model.keras ADDED
@@ -0,0 +1,3 @@
 
 
 
 
1
+ version https://git-lfs.github.com/spec/v1
2
+ oid sha256:ca179b73a9499662d4466c66799455ff5290ef0bd981ccfafd88e3427a0dafcd
3
+ size 10026851
flower_recognition_model.keras ADDED
@@ -0,0 +1,3 @@
 
 
 
 
1
+ version https://git-lfs.github.com/spec/v1
2
+ oid sha256:c724de8007f1588c0560f11d5bc35f0b27b8bc26bfe8cd109d4bc1103f17372d
3
+ size 49779968
requirements.txt ADDED
@@ -0,0 +1,5 @@
 
 
 
 
 
 
1
+ gradio
2
+ tensorflow
3
+ pillow
4
+ numpy
5
+ opencv-python-headless>=4.8.0