UdaraChamidu commited on
Commit
9d02e3c
·
verified ·
1 Parent(s): 9acfd81

Update app.py

Browse files
Files changed (1) hide show
  1. app.py +240 -117
app.py CHANGED
@@ -1,5 +1,7 @@
 
 
1
  import matplotlib
2
- matplotlib.use('Agg')
3
 
4
  import os
5
  import cv2
@@ -16,14 +18,31 @@ from ultralytics import YOLO
16
  # -----------------------------
17
  app = Flask(__name__)
18
  app.config["UPLOAD_FOLDER"] = "static/uploads"
 
19
  os.makedirs(app.config["UPLOAD_FOLDER"], exist_ok=True)
20
 
21
  # -----------------------------
22
-
23
  # Load Keras classification model
24
- best_model = load_model("efficientnet_b0_best.h5")
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
25
  IMG_SIZE = 128
26
 
 
27
  CLASS_LABELS = ['biological', 'brown-glass', 'cardboard', 'green-glass',
28
  'metal', 'paper', 'plastic', 'shoes', 'trash', 'white-glass']
29
 
@@ -32,174 +51,278 @@ NON_RECYCLABLE = ["trash", "biological", "shoes"]
32
 
33
  stats = {}
34
 
35
- # -----------------------------
36
-
37
- # Load YOLOv8 model
38
- yolo_model = YOLO("best.pt")
39
 
40
  # -----------------------------
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
41
 
42
- # Preprocess image for classification
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
43
  def preprocess_image(file_path):
44
  img = cv2.imread(file_path)
 
 
45
  img_rgb = cv2.cvtColor(cv2.resize(img, (IMG_SIZE, IMG_SIZE)), cv2.COLOR_BGR2RGB)
46
  img_input = preprocess_input(img_rgb.astype("float32"))
47
  img_input = np.expand_dims(img_input, axis=0)
48
  return img_rgb, img_input
49
 
50
  # -----------------------------
51
-
52
  # Log predictions
53
  def log_prediction(class_label):
54
  stats[class_label] = stats.get(class_label, 0) + 1
55
  total_items = sum(stats.values())
56
- with open("waste_log.csv", "w") as f:
57
- f.write("Waste Classification Report\n")
58
- f.write(f"Total Items Processed: {total_items}\n")
59
- for category, count in stats.items():
60
- f.write(f"{category}: {count}\n")
 
 
 
61
 
62
  # -----------------------------
63
-
64
  # Generate PDF report
65
  def generate_pdf_report():
66
- pdf = FPDF()
67
- pdf.add_page()
68
- pdf.set_font("Arial", size=14)
69
- pdf.cell(200, 10, txt="Waste Classification Report", ln=True, align="C")
70
- pdf.ln(10)
 
71
 
72
- total_items = sum(stats.values())
73
- pdf.set_font("Arial", size=12)
74
- pdf.cell(0, 10, txt=f"Total Items Processed: {total_items}", ln=True)
75
 
76
- for category, count in stats.items():
77
- pdf.cell(0, 10, txt=f"{category}: {count}", ln=True)
78
 
79
- pdf_file = "waste_report.pdf"
80
- pdf.output(pdf_file)
81
- return pdf_file
 
 
 
82
 
83
  # -----------------------------
84
-
85
  # Image classification route
86
  @app.route("/", methods=["GET", "POST"])
87
  def index():
88
  if request.method == "POST":
89
  file = request.files.get("file")
 
 
90
  if not file or file.filename == "":
91
  return redirect(request.url)
92
 
93
- file_path = os.path.join(app.config["UPLOAD_FOLDER"], file.filename)
94
- file.save(file_path)
95
-
96
- img_rgb, img_input = preprocess_image(file_path)
97
- preds = best_model.predict(img_input)
98
- class_idx = np.argmax(preds, axis=1)[0]
99
- class_label = CLASS_LABELS[class_idx]
100
- confidence = preds[0][class_idx]
101
-
102
- log_prediction(class_label)
103
-
104
- if class_label in RECYCLABLE:
105
- bin_type = "Recyclable ♻️"
106
- elif class_label in NON_RECYCLABLE:
107
- bin_type = "Non-Recyclable 🗑️"
108
- else:
109
- bin_type = "Unknown ⚠️"
110
-
111
- return render_template(
112
- "result.html",
113
- image=file.filename,
114
- label=class_label,
115
- confidence=f"{confidence*100:.2f}%",
116
- bin_type=bin_type
117
- )
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
118
 
119
  return render_template("index.html")
120
 
121
  # -----------------------------
122
-
123
  # Show statistics
124
  @app.route("/stats")
125
  def show_stats():
126
  if stats:
127
- categories = list(stats.keys())
128
- counts = list(stats.values())
129
- plt.figure(figsize=(6, 4))
130
- plt.bar(categories, counts, color="green")
131
- plt.xlabel("Category")
132
- plt.ylabel("Count")
133
- plt.title("Waste Classification Statistics")
134
- plt.tight_layout()
135
- plt.savefig("static/stats_chart.png")
136
- plt.close()
 
 
 
 
 
 
 
 
137
  return render_template("report.html", stats=stats)
138
 
139
  # -----------------------------
140
-
141
  # Download reports
142
  @app.route("/download_pdf")
143
  def download_pdf():
144
- pdf_path = generate_pdf_report()
145
- return send_file(pdf_path, as_attachment=True)
 
 
 
 
 
 
146
 
147
  @app.route("/download_csv")
148
  def download_csv():
149
- return send_file("waste_log.csv", as_attachment=True)
 
 
 
 
 
 
150
 
151
  # -----------------------------
152
-
153
- # Real-time camera detection
154
  @app.route("/camera")
155
  def camera():
156
- return render_template("camera.html")
157
-
158
- def generate_frames():
159
- cap = cv2.VideoCapture(0)
160
- while True:
161
- success, frame = cap.read()
162
- if not success:
163
- break
164
-
165
- results = yolo_model(frame)[0]
166
- boxes = results.boxes.xyxy.cpu().numpy()
167
- confidences = results.boxes.conf.cpu().numpy()
168
- class_ids = results.boxes.cls.cpu().numpy().astype(int)
169
-
170
- for i, box in enumerate(boxes):
171
- x1, y1, x2, y2 = map(int, box)
172
- label = yolo_model.names[class_ids[i]]
173
- confidence = confidences[i]
174
-
175
- # Color based on confidence
176
- if confidence >= 0.80:
177
- color = (0, 255, 0) # Green
178
- elif confidence >= 0.50:
179
- color = (0, 255, 255) # Yellow
180
- else:
181
- color = (0, 0, 255) # Red
182
-
183
- # Draw box & label
184
- cv2.rectangle(frame, (x1, y1), (x2, y2), color, 2)
185
- cv2.putText(frame, f"{label} {confidence*100:.1f}%", (x1, y1 - 10),
186
- cv2.FONT_HERSHEY_SIMPLEX, 0.6, color, 2)
187
-
188
- # Log only high-confidence detections
189
- if confidence >= 0.80:
190
- log_prediction(label)
191
-
192
- # Encode frame for streaming
193
- ret, buffer = cv2.imencode('.jpg', frame)
194
- frame_bytes = buffer.tobytes()
195
- yield (b'--frame\r\n'
196
- b'Content-Type: image/jpeg\r\n\r\n' + frame_bytes + b'\r\n')
197
 
198
- @app.route('/video_feed')
199
- def video_feed():
200
- return Response(generate_frames(),
201
- mimetype='multipart/x-mixed-replace; boundary=frame')
202
 
203
  # Run Flask
204
  if __name__ == "__main__":
205
- app.run(debug=True)
 
 
1
+
2
+
3
  import matplotlib
4
+ matplotlib.use('Agg') # Non-GUI backend for plotting
5
 
6
  import os
7
  import cv2
 
18
  # -----------------------------
19
  app = Flask(__name__)
20
  app.config["UPLOAD_FOLDER"] = "static/uploads"
21
+ app.config["MAX_CONTENT_LENGTH"] = 16 * 1024 * 1024 # 16MB max file size
22
  os.makedirs(app.config["UPLOAD_FOLDER"], exist_ok=True)
23
 
24
  # -----------------------------
 
25
  # Load Keras classification model
26
+ try:
27
+ best_model = load_model("efficientnet_b0_best.keras")
28
+ print("✅ EfficientNet model loaded successfully!")
29
+ except Exception as e:
30
+ print(f"❌ Error loading EfficientNet model: {e}")
31
+ best_model = None
32
+
33
+ # -----------------------------
34
+ # Load YOLO model
35
+ try:
36
+ yolo_model = YOLO("best.pt")
37
+ print("✅ YOLO model loaded successfully!")
38
+ print(f"YOLO model classes: {yolo_model.names}")
39
+ except Exception as e:
40
+ print(f"❌ Error loading YOLO model: {e}")
41
+ yolo_model = None
42
+
43
  IMG_SIZE = 128
44
 
45
+ # EfficientNet classes
46
  CLASS_LABELS = ['biological', 'brown-glass', 'cardboard', 'green-glass',
47
  'metal', 'paper', 'plastic', 'shoes', 'trash', 'white-glass']
48
 
 
51
 
52
  stats = {}
53
 
54
+ # YOLO detection confidence threshold
55
+ YOLO_CONFIDENCE_THRESHOLD = 0.5
 
 
56
 
57
  # -----------------------------
58
+ # YOLO object detection function
59
+ def detect_objects_yolo(file_path):
60
+ """Detect objects in image using YOLO model"""
61
+ if yolo_model is None:
62
+ return None, "YOLO model not loaded"
63
+
64
+ try:
65
+ # Read image
66
+ img = cv2.imread(file_path)
67
+ if img is None:
68
+ return None, "Could not read image"
69
+
70
+ # Run YOLO detection
71
+ results = yolo_model(img)[0]
72
+
73
+ detections = []
74
+ if results.boxes is not None and len(results.boxes) > 0:
75
+ boxes = results.boxes.xyxy.cpu().numpy()
76
+ confidences = results.boxes.conf.cpu().numpy()
77
+ class_ids = results.boxes.cls.cpu().numpy().astype(int)
78
+
79
+ for i, box in enumerate(boxes):
80
+ if confidences[i] >= YOLO_CONFIDENCE_THRESHOLD:
81
+ x1, y1, x2, y2 = map(int, box)
82
+ class_name = yolo_model.names[class_ids[i]]
83
+ confidence = confidences[i]
84
+
85
+ detections.append({
86
+ 'class': class_name,
87
+ 'confidence': confidence,
88
+ 'bbox': [x1, y1, x2, y2]
89
+ })
90
+
91
+ return detections, None
92
+ except Exception as e:
93
+ return None, str(e)
94
 
95
+ # -----------------------------
96
+ # Draw bounding boxes on image
97
+ def draw_detections(img_path, detections, output_path):
98
+ """Draw YOLO detections on image"""
99
+ try:
100
+ img = cv2.imread(img_path)
101
+
102
+ for detection in detections:
103
+ x1, y1, x2, y2 = detection['bbox']
104
+ class_name = detection['class']
105
+ confidence = detection['confidence']
106
+
107
+ # Choose color based on confidence
108
+ if confidence >= 0.80:
109
+ color = (0, 255, 0) # Green - high confidence
110
+ elif confidence >= 0.60:
111
+ color = (0, 255, 255) # Yellow - medium confidence
112
+ else:
113
+ color = (0, 165, 255) # Orange - low confidence
114
+
115
+ # Draw bounding box
116
+ cv2.rectangle(img, (x1, y1), (x2, y2), color, 2)
117
+
118
+ # Draw label background
119
+ label = f"{class_name} {confidence*100:.1f}%"
120
+ (text_width, text_height), _ = cv2.getTextSize(label, cv2.FONT_HERSHEY_SIMPLEX, 0.6, 2)
121
+ cv2.rectangle(img, (x1, y1 - text_height - 10), (x1 + text_width, y1), color, -1)
122
+
123
+ # Draw label text
124
+ cv2.putText(img, label, (x1, y1 - 5), cv2.FONT_HERSHEY_SIMPLEX, 0.6, (0, 0, 0), 2)
125
+
126
+ cv2.imwrite(output_path, img)
127
+ return True
128
+ except Exception as e:
129
+ print(f"Error drawing detections: {e}")
130
+ return False
131
  def preprocess_image(file_path):
132
  img = cv2.imread(file_path)
133
+ if img is None:
134
+ raise ValueError("Could not load image")
135
  img_rgb = cv2.cvtColor(cv2.resize(img, (IMG_SIZE, IMG_SIZE)), cv2.COLOR_BGR2RGB)
136
  img_input = preprocess_input(img_rgb.astype("float32"))
137
  img_input = np.expand_dims(img_input, axis=0)
138
  return img_rgb, img_input
139
 
140
  # -----------------------------
 
141
  # Log predictions
142
  def log_prediction(class_label):
143
  stats[class_label] = stats.get(class_label, 0) + 1
144
  total_items = sum(stats.values())
145
+ try:
146
+ with open("waste_log.csv", "w") as f:
147
+ f.write("Waste Classification Report\n")
148
+ f.write(f"Total Items Processed: {total_items}\n")
149
+ for category, count in stats.items():
150
+ f.write(f"{category}: {count}\n")
151
+ except Exception as e:
152
+ print(f"Error writing log: {e}")
153
 
154
  # -----------------------------
 
155
  # Generate PDF report
156
  def generate_pdf_report():
157
+ try:
158
+ pdf = FPDF()
159
+ pdf.add_page()
160
+ pdf.set_font("Arial", size=14)
161
+ pdf.cell(200, 10, txt="Waste Classification Report", ln=True, align="C")
162
+ pdf.ln(10)
163
 
164
+ total_items = sum(stats.values())
165
+ pdf.set_font("Arial", size=12)
166
+ pdf.cell(0, 10, txt=f"Total Items Processed: {total_items}", ln=True)
167
 
168
+ for category, count in stats.items():
169
+ pdf.cell(0, 10, txt=f"{category}: {count}", ln=True)
170
 
171
+ pdf_file = "waste_report.pdf"
172
+ pdf.output(pdf_file)
173
+ return pdf_file
174
+ except Exception as e:
175
+ print(f"Error generating PDF: {e}")
176
+ return None
177
 
178
  # -----------------------------
 
179
  # Image classification route
180
  @app.route("/", methods=["GET", "POST"])
181
  def index():
182
  if request.method == "POST":
183
  file = request.files.get("file")
184
+ detection_mode = request.form.get("detection_mode", "classification")
185
+
186
  if not file or file.filename == "":
187
  return redirect(request.url)
188
 
189
+ # Validate file type
190
+ allowed_extensions = {'png', 'jpg', 'jpeg', 'gif', 'bmp'}
191
+ file_extension = file.filename.rsplit('.', 1)[1].lower() if '.' in file.filename else ''
192
+ if file_extension not in allowed_extensions:
193
+ return render_template("index.html", error="Please upload a valid image file (PNG, JPG, JPEG, GIF, BMP)")
194
+
195
+ try:
196
+ file_path = os.path.join(app.config["UPLOAD_FOLDER"], file.filename)
197
+ file.save(file_path)
198
+
199
+ if detection_mode == "yolo" and yolo_model is not None:
200
+ # YOLO Object Detection Mode
201
+ detections, error = detect_objects_yolo(file_path)
202
+
203
+ if error:
204
+ return render_template("index.html", error=f"YOLO detection error: {error}")
205
+
206
+ if detections:
207
+ # Draw detections on image
208
+ output_filename = f"detected_{file.filename}"
209
+ output_path = os.path.join(app.config["UPLOAD_FOLDER"], output_filename)
210
+ draw_detections(file_path, detections, output_path)
211
+
212
+ # Log detections
213
+ for detection in detections:
214
+ log_prediction(detection['class'])
215
+
216
+ return render_template(
217
+ "yolo_result.html",
218
+ original_image=file.filename,
219
+ detected_image=output_filename,
220
+ detections=detections,
221
+ detection_count=len(detections)
222
+ )
223
+ else:
224
+ return render_template(
225
+ "yolo_result.html",
226
+ original_image=file.filename,
227
+ detected_image=file.filename,
228
+ detections=[],
229
+ detection_count=0,
230
+ message="No objects detected with sufficient confidence."
231
+ )
232
+
233
+ else:
234
+ # EfficientNet Classification Mode
235
+ if best_model is None:
236
+ return render_template("index.html", error="Classification model not loaded. Please check if the model file exists.")
237
+
238
+ img_rgb, img_input = preprocess_image(file_path)
239
+ preds = best_model.predict(img_input)
240
+ class_idx = np.argmax(preds, axis=1)[0]
241
+ class_label = CLASS_LABELS[class_idx]
242
+ confidence = preds[0][class_idx]
243
+
244
+ log_prediction(class_label)
245
+
246
+ if class_label in RECYCLABLE:
247
+ bin_type = "Recyclable ♻️"
248
+ elif class_label in NON_RECYCLABLE:
249
+ bin_type = "Non-Recyclable 🗑️"
250
+ else:
251
+ bin_type = "Unknown ⚠️"
252
+
253
+ return render_template(
254
+ "result.html",
255
+ image=file.filename,
256
+ label=class_label,
257
+ confidence=f"{confidence*100:.2f}%",
258
+ bin_type=bin_type
259
+ )
260
+
261
+ except Exception as e:
262
+ return render_template("index.html", error=f"Error processing image: {str(e)}")
263
 
264
  return render_template("index.html")
265
 
266
  # -----------------------------
 
267
  # Show statistics
268
  @app.route("/stats")
269
  def show_stats():
270
  if stats:
271
+ try:
272
+ categories = list(stats.keys())
273
+ counts = list(stats.values())
274
+ plt.figure(figsize=(10, 6))
275
+ plt.bar(categories, counts, color="green", alpha=0.7)
276
+ plt.xlabel("Category")
277
+ plt.ylabel("Count")
278
+ plt.title("Waste Classification Statistics")
279
+ plt.xticks(rotation=45)
280
+ plt.tight_layout()
281
+
282
+ # Ensure static directory exists
283
+ os.makedirs("static", exist_ok=True)
284
+ plt.savefig("static/stats_chart.png", dpi=150, bbox_inches='tight')
285
+ plt.close()
286
+ except Exception as e:
287
+ print(f"Error generating chart: {e}")
288
+
289
  return render_template("report.html", stats=stats)
290
 
291
  # -----------------------------
 
292
  # Download reports
293
  @app.route("/download_pdf")
294
  def download_pdf():
295
+ try:
296
+ pdf_path = generate_pdf_report()
297
+ if pdf_path and os.path.exists(pdf_path):
298
+ return send_file(pdf_path, as_attachment=True)
299
+ else:
300
+ return "Error generating PDF report", 500
301
+ except Exception as e:
302
+ return f"Error: {str(e)}", 500
303
 
304
  @app.route("/download_csv")
305
  def download_csv():
306
+ try:
307
+ if os.path.exists("waste_log.csv"):
308
+ return send_file("waste_log.csv", as_attachment=True)
309
+ else:
310
+ return "No data to download", 404
311
+ except Exception as e:
312
+ return f"Error: {str(e)}", 500
313
 
314
  # -----------------------------
315
+ # Real-time camera detection (disabled for Hugging Face Spaces)
 
316
  @app.route("/camera")
317
  def camera():
318
+ return render_template("camera_disabled.html")
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
319
 
320
+ # Health check endpoint
321
+ @app.route("/health")
322
+ def health():
323
+ return {"status": "healthy", "model_loaded": best_model is not None}
324
 
325
  # Run Flask
326
  if __name__ == "__main__":
327
+ port = int(os.environ.get("PORT", 7860)) # Hugging Face Spaces uses port 7860
328
+ app.run(host="0.0.0.0", port=port, debug=False)