|
|
|
|
| import matplotlib |
| matplotlib.use('Agg') |
| |
| import os |
| import cv2 |
| import numpy as np |
| from flask import Flask, render_template, request, redirect, send_file, Response |
| from tensorflow.keras.applications.mobilenet_v2 import preprocess_input |
| from tensorflow.keras.models import load_model |
| from fpdf import FPDF |
| import matplotlib.pyplot as plt |
| from ultralytics import YOLO |
|
|
| |
| |
| |
| app = Flask(__name__) |
| app.config["UPLOAD_FOLDER"] = "static/uploads" |
| app.config["MAX_CONTENT_LENGTH"] = 16 * 1024 * 1024 |
| os.makedirs(app.config["UPLOAD_FOLDER"], exist_ok=True) |
|
|
| |
| |
| try: |
| best_model = load_model("model.weights.h5") |
| print("✅ EfficientNet model loaded successfully!") |
| except Exception as e: |
| print(f"❌ Error loading EfficientNet model: {e}") |
| best_model = None |
|
|
| |
| |
| try: |
| yolo_model = YOLO("best.pt") |
| print("✅ YOLO model loaded successfully!") |
| print(f"YOLO model classes: {yolo_model.names}") |
| except Exception as e: |
| print(f"❌ Error loading YOLO model: {e}") |
| yolo_model = None |
|
|
| IMG_SIZE = 128 |
|
|
| |
| CLASS_LABELS = ['biological', 'brown-glass', 'cardboard', 'green-glass', |
| 'metal', 'paper', 'plastic', 'shoes', 'trash', 'white-glass'] |
|
|
| RECYCLABLE = ["brown-glass", "green-glass", "white-glass", "metal", "plastic", "paper", "cardboard"] |
| NON_RECYCLABLE = ["trash", "biological", "shoes"] |
|
|
| stats = {} |
|
|
| |
| YOLO_CONFIDENCE_THRESHOLD = 0.5 |
|
|
| |
| |
| def detect_objects_yolo(file_path): |
| """Detect objects in image using YOLO model""" |
| if yolo_model is None: |
| return None, "YOLO model not loaded" |
| |
| try: |
| |
| img = cv2.imread(file_path) |
| if img is None: |
| return None, "Could not read image" |
| |
| |
| results = yolo_model(img)[0] |
| |
| detections = [] |
| if results.boxes is not None and len(results.boxes) > 0: |
| boxes = results.boxes.xyxy.cpu().numpy() |
| confidences = results.boxes.conf.cpu().numpy() |
| class_ids = results.boxes.cls.cpu().numpy().astype(int) |
| |
| for i, box in enumerate(boxes): |
| if confidences[i] >= YOLO_CONFIDENCE_THRESHOLD: |
| x1, y1, x2, y2 = map(int, box) |
| class_name = yolo_model.names[class_ids[i]] |
| confidence = confidences[i] |
| |
| detections.append({ |
| 'class': class_name, |
| 'confidence': confidence, |
| 'bbox': [x1, y1, x2, y2] |
| }) |
| |
| return detections, None |
| except Exception as e: |
| return None, str(e) |
|
|
| |
| |
| def draw_detections(img_path, detections, output_path): |
| """Draw YOLO detections on image""" |
| try: |
| img = cv2.imread(img_path) |
| |
| for detection in detections: |
| x1, y1, x2, y2 = detection['bbox'] |
| class_name = detection['class'] |
| confidence = detection['confidence'] |
| |
| |
| if confidence >= 0.80: |
| color = (0, 255, 0) |
| elif confidence >= 0.60: |
| color = (0, 255, 255) |
| else: |
| color = (0, 165, 255) |
| |
| |
| cv2.rectangle(img, (x1, y1), (x2, y2), color, 2) |
| |
| |
| label = f"{class_name} {confidence*100:.1f}%" |
| (text_width, text_height), _ = cv2.getTextSize(label, cv2.FONT_HERSHEY_SIMPLEX, 0.6, 2) |
| cv2.rectangle(img, (x1, y1 - text_height - 10), (x1 + text_width, y1), color, -1) |
| |
| |
| cv2.putText(img, label, (x1, y1 - 5), cv2.FONT_HERSHEY_SIMPLEX, 0.6, (0, 0, 0), 2) |
| |
| cv2.imwrite(output_path, img) |
| return True |
| except Exception as e: |
| print(f"Error drawing detections: {e}") |
| return False |
| def preprocess_image(file_path): |
| img = cv2.imread(file_path) |
| if img is None: |
| raise ValueError("Could not load image") |
| img_rgb = cv2.cvtColor(cv2.resize(img, (IMG_SIZE, IMG_SIZE)), cv2.COLOR_BGR2RGB) |
| img_input = preprocess_input(img_rgb.astype("float32")) |
| img_input = np.expand_dims(img_input, axis=0) |
| return img_rgb, img_input |
|
|
| |
| |
| def log_prediction(class_label): |
| stats[class_label] = stats.get(class_label, 0) + 1 |
| total_items = sum(stats.values()) |
| try: |
| with open("waste_log.csv", "w") as f: |
| f.write("Waste Classification Report\n") |
| f.write(f"Total Items Processed: {total_items}\n") |
| for category, count in stats.items(): |
| f.write(f"{category}: {count}\n") |
| except Exception as e: |
| print(f"Error writing log: {e}") |
|
|
| |
| |
| def generate_pdf_report(): |
| try: |
| pdf = FPDF() |
| pdf.add_page() |
| pdf.set_font("Arial", size=14) |
| pdf.cell(200, 10, txt="Waste Classification Report", ln=True, align="C") |
| pdf.ln(10) |
|
|
| total_items = sum(stats.values()) |
| pdf.set_font("Arial", size=12) |
| pdf.cell(0, 10, txt=f"Total Items Processed: {total_items}", ln=True) |
|
|
| for category, count in stats.items(): |
| pdf.cell(0, 10, txt=f"{category}: {count}", ln=True) |
|
|
| pdf_file = "waste_report.pdf" |
| pdf.output(pdf_file) |
| return pdf_file |
| except Exception as e: |
| print(f"Error generating PDF: {e}") |
| return None |
|
|
| |
| |
| @app.route("/", methods=["GET", "POST"]) |
| def index(): |
| if request.method == "POST": |
| file = request.files.get("file") |
| detection_mode = request.form.get("detection_mode", "classification") |
| |
| if not file or file.filename == "": |
| return redirect(request.url) |
|
|
| |
| allowed_extensions = {'png', 'jpg', 'jpeg', 'gif', 'bmp'} |
| file_extension = file.filename.rsplit('.', 1)[1].lower() if '.' in file.filename else '' |
| if file_extension not in allowed_extensions: |
| return render_template("index.html", error="Please upload a valid image file (PNG, JPG, JPEG, GIF, BMP)") |
|
|
| try: |
| file_path = os.path.join(app.config["UPLOAD_FOLDER"], file.filename) |
| file.save(file_path) |
|
|
| if detection_mode == "yolo" and yolo_model is not None: |
| |
| detections, error = detect_objects_yolo(file_path) |
| |
| if error: |
| return render_template("index.html", error=f"YOLO detection error: {error}") |
| |
| if detections: |
| |
| output_filename = f"detected_{file.filename}" |
| output_path = os.path.join(app.config["UPLOAD_FOLDER"], output_filename) |
| draw_detections(file_path, detections, output_path) |
| |
| |
| for detection in detections: |
| log_prediction(detection['class']) |
| |
| return render_template( |
| "yolo_result.html", |
| original_image=file.filename, |
| detected_image=output_filename, |
| detections=detections, |
| detection_count=len(detections) |
| ) |
| else: |
| return render_template( |
| "yolo_result.html", |
| original_image=file.filename, |
| detected_image=file.filename, |
| detections=[], |
| detection_count=0, |
| message="No objects detected with sufficient confidence." |
| ) |
| |
| else: |
| |
| if best_model is None: |
| return render_template("index.html", error="Classification model not loaded. Please check if the model file exists.") |
|
|
| img_rgb, img_input = preprocess_image(file_path) |
| preds = best_model.predict(img_input) |
| class_idx = np.argmax(preds, axis=1)[0] |
| class_label = CLASS_LABELS[class_idx] |
| confidence = preds[0][class_idx] |
|
|
| log_prediction(class_label) |
|
|
| if class_label in RECYCLABLE: |
| bin_type = "Recyclable ♻️" |
| elif class_label in NON_RECYCLABLE: |
| bin_type = "Non-Recyclable 🗑️" |
| else: |
| bin_type = "Unknown ⚠️" |
|
|
| return render_template( |
| "result.html", |
| image=file.filename, |
| label=class_label, |
| confidence=f"{confidence*100:.2f}%", |
| bin_type=bin_type |
| ) |
| |
| except Exception as e: |
| return render_template("index.html", error=f"Error processing image: {str(e)}") |
|
|
| return render_template("index.html") |
|
|
| |
| |
| @app.route("/stats") |
| def show_stats(): |
| if stats: |
| try: |
| categories = list(stats.keys()) |
| counts = list(stats.values()) |
| plt.figure(figsize=(10, 6)) |
| plt.bar(categories, counts, color="green", alpha=0.7) |
| plt.xlabel("Category") |
| plt.ylabel("Count") |
| plt.title("Waste Classification Statistics") |
| plt.xticks(rotation=45) |
| plt.tight_layout() |
| |
| |
| os.makedirs("static", exist_ok=True) |
| plt.savefig("static/stats_chart.png", dpi=150, bbox_inches='tight') |
| plt.close() |
| except Exception as e: |
| print(f"Error generating chart: {e}") |
| |
| return render_template("report.html", stats=stats) |
|
|
| |
| |
| @app.route("/download_pdf") |
| def download_pdf(): |
| try: |
| pdf_path = generate_pdf_report() |
| if pdf_path and os.path.exists(pdf_path): |
| return send_file(pdf_path, as_attachment=True) |
| else: |
| return "Error generating PDF report", 500 |
| except Exception as e: |
| return f"Error: {str(e)}", 500 |
|
|
| @app.route("/download_csv") |
| def download_csv(): |
| try: |
| if os.path.exists("waste_log.csv"): |
| return send_file("waste_log.csv", as_attachment=True) |
| else: |
| return "No data to download", 404 |
| except Exception as e: |
| return f"Error: {str(e)}", 500 |
|
|
| |
| |
| @app.route("/camera") |
| def camera(): |
| return render_template("camera_disabled.html") |
|
|
| |
| @app.route("/health") |
| def health(): |
| return {"status": "healthy", "model_loaded": best_model is not None} |
|
|
| |
| if __name__ == "__main__": |
| port = int(os.environ.get("PORT", 7860)) |
| app.run(host="0.0.0.0", port=port, debug=False) |