UdaraChamidu commited on
Commit
d3a671e
·
verified ·
1 Parent(s): c94086d

Update app.py

Browse files
Files changed (1) hide show
  1. app.py +117 -240
app.py CHANGED
@@ -1,7 +1,5 @@
1
-
2
-
3
  import matplotlib
4
- matplotlib.use('Agg') # Non-GUI backend for plotting
5
 
6
  import os
7
  import cv2
@@ -18,31 +16,14 @@ from ultralytics import YOLO
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,278 +32,174 @@ NON_RECYCLABLE = ["trash", "biological", "shoes"]
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)
 
 
 
1
  import matplotlib
2
+ matplotlib.use('Agg')
3
 
4
  import os
5
  import cv2
 
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("model_files/efficientnet_b0_best.keras")
25
  IMG_SIZE = 128
26
 
 
27
  CLASS_LABELS = ['biological', 'brown-glass', 'cardboard', 'green-glass',
28
  'metal', 'paper', 'plastic', 'shoes', 'trash', 'white-glass']
29
 
 
32
 
33
  stats = {}
34
 
 
 
 
35
  # -----------------------------
36
+
37
+ # Load YOLOv8 model
38
+ yolo_model = YOLO("model_files/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)