UdaraChamidu commited on
Commit
360d47f
·
verified ·
1 Parent(s): 53a7c1d

Update app.py

Browse files
Files changed (1) hide show
  1. app.py +143 -109
app.py CHANGED
@@ -1,65 +1,52 @@
1
- import os, tempfile
 
 
 
2
  import cv2
3
  import numpy as np
4
- import pandas as pd
5
- from datetime import datetime
6
- from flask import Flask, render_template, request, redirect, send_file
7
  from tensorflow.keras.applications.mobilenet_v2 import preprocess_input
8
  from tensorflow.keras.models import load_model
9
  from fpdf import FPDF
10
  import matplotlib.pyplot as plt
 
11
 
12
  # -----------------------------
13
- # Environment fixes for Hugging Face
14
- # -----------------------------
15
- # Fix matplotlib cache dir
16
- os.environ["MPLCONFIGDIR"] = "/tmp/matplotlib"
17
- os.makedirs("/tmp/matplotlib", exist_ok=True)
18
-
19
- # Uploads go into /tmp
20
- UPLOAD_DIR = os.path.join(tempfile.gettempdir(), "uploads")
21
- os.makedirs(UPLOAD_DIR, exist_ok=True)
22
-
23
- # Logs and reports also in /tmp
24
- LOG_PATH = os.path.join(tempfile.gettempdir(), "waste_log.csv")
25
- REPORT_PDF = os.path.join(tempfile.gettempdir(), "waste_report.pdf")
26
- CHART_PATH = os.path.join(tempfile.gettempdir(), "stats_chart.png")
27
-
28
- # -----------------------------
29
- # Flask App Config
30
  # -----------------------------
31
  app = Flask(__name__)
32
- app.config["UPLOAD_FOLDER"] = UPLOAD_DIR
 
33
 
34
  # -----------------------------
35
- # Model path
36
- # -----------------------------
37
- MODEL_DIR = "model"
38
- os.makedirs(MODEL_DIR, exist_ok=True)
39
- MODEL_PATH = os.path.join(MODEL_DIR, "/efficientnet_b0_best.keras")
40
-
41
- if not os.path.exists(MODEL_PATH):
42
- raise FileNotFoundError(f"Model file not found at {MODEL_PATH}. Place your .keras model in /model.")
43
-
44
- best_model = load_model(MODEL_PATH)
45
 
46
  IMG_SIZE = 128
47
 
48
- # Class labels
49
  CLASS_LABELS = ['biological', 'brown-glass', 'cardboard', 'green-glass',
50
  'metal', 'paper', 'plastic', 'shoes', 'trash', 'white-glass']
51
 
52
- # Waste categories
53
  RECYCLABLE = ["brown-glass", "green-glass", "white-glass", "metal", "plastic", "paper", "cardboard"]
54
  NON_RECYCLABLE = ["trash", "biological", "shoes"]
55
 
56
- # Initialize statistics
57
  stats = {}
58
 
59
  # -----------------------------
60
- # Preprocess image
 
 
 
 
61
  def preprocess_image(file_path):
62
  img = cv2.imread(file_path)
 
 
63
  img_rgb = cv2.cvtColor(cv2.resize(img, (IMG_SIZE, IMG_SIZE)), cv2.COLOR_BGR2RGB)
64
  img_input = preprocess_input(img_rgb.astype("float32"))
65
  img_input = np.expand_dims(img_input, axis=0)
@@ -68,103 +55,150 @@ def preprocess_image(file_path):
68
  # -----------------------------
69
  # Log predictions
70
  def log_prediction(class_label):
71
- log_df = pd.DataFrame([[datetime.now(), class_label]], columns=["Timestamp", "Class"])
 
72
  try:
73
- old_df = pd.read_csv(LOG_PATH)
74
- new_df = pd.concat([old_df, log_df], ignore_index=True)
75
- except FileNotFoundError:
76
- new_df = log_df
77
- new_df.to_csv(LOG_PATH, index=False)
 
 
78
 
79
  # -----------------------------
80
- # PDF Report
81
  def generate_pdf_report():
82
- pdf = FPDF()
83
- pdf.add_page()
84
- pdf.set_font("Arial", size=14)
85
- pdf.cell(200, 10, txt="Waste Classification Report", ln=True, align="C")
86
- pdf.ln(10)
87
-
88
- total_items = sum(stats.values()) if stats else 0
89
- pdf.set_font("Arial", size=12)
90
- pdf.cell(0, 10, txt=f"Total Items Processed: {total_items}", ln=True)
91
-
92
- for category, count in stats.items():
93
- pdf.cell(0, 10, txt=f"{category}: {count}", ln=True)
94
-
95
- pdf.output(REPORT_PDF)
96
- return REPORT_PDF
 
 
 
 
 
97
 
98
  # -----------------------------
99
- # Routes
100
  @app.route("/", methods=["GET", "POST"])
101
  def index():
102
  if request.method == "POST":
103
  file = request.files.get("file")
104
- if file is None or file.filename == "":
105
  return redirect(request.url)
106
 
107
- file_path = os.path.join(app.config["UPLOAD_FOLDER"], file.filename)
108
- file.save(file_path)
109
-
110
- # Preprocess & predict
111
- img_rgb, img_input = preprocess_image(file_path)
112
- preds = best_model.predict(img_input)
113
- class_idx = np.argmax(preds, axis=1)[0]
114
- class_label = CLASS_LABELS[class_idx]
115
- confidence = preds[0][class_idx]
116
-
117
- # Update stats & log
118
- stats[class_label] = stats.get(class_label, 0) + 1
119
- log_prediction(class_label)
120
-
121
- # Determine bin type
122
- if class_label in RECYCLABLE:
123
- bin_type = "Recyclable ♻️"
124
- elif class_label in NON_RECYCLABLE:
125
- bin_type = "Non-Recyclable 🗑️"
126
- else:
127
- bin_type = "Unknown ⚠️"
128
-
129
- return render_template(
130
- "result.html",
131
- image=file.filename,
132
- label=class_label,
133
- confidence=f"{confidence*100:.2f}%",
134
- bin_type=bin_type
135
- )
 
 
 
 
 
 
 
 
136
 
137
  return render_template("index.html")
138
 
139
-
 
140
  @app.route("/stats")
141
  def show_stats():
142
  if stats:
143
- categories = list(stats.keys())
144
- counts = list(stats.values())
145
- plt.figure(figsize=(6, 4))
146
- plt.bar(categories, counts)
147
- plt.xlabel("Category")
148
- plt.ylabel("Count")
149
- plt.title("Waste Classification Statistics")
150
- plt.tight_layout()
151
- plt.savefig(CHART_PATH)
152
- plt.close()
153
- return render_template("report.html", stats=stats, chart_path=CHART_PATH)
154
-
 
 
 
 
 
 
 
155
 
 
 
156
  @app.route("/download_pdf")
157
  def download_pdf():
158
- pdf_path = generate_pdf_report()
159
- return send_file(pdf_path, as_attachment=True)
160
-
 
 
 
 
 
161
 
162
  @app.route("/download_csv")
163
  def download_csv():
164
- return send_file(LOG_PATH, as_attachment=True)
 
 
 
 
 
 
165
 
166
  # -----------------------------
167
- # Run Flask (useful locally)
 
 
 
 
 
 
 
 
 
 
168
  if __name__ == "__main__":
169
- port = int(os.environ.get("PORT", 8080))
170
- app.run(host="0.0.0.0", port=port, debug=True)
 
1
+ import matplotlib
2
+ matplotlib.use('Agg') # Non-GUI backend for plotting
3
+
4
+ import os
5
  import cv2
6
  import numpy as np
7
+ from flask import Flask, render_template, request, redirect, send_file, Response
 
 
8
  from tensorflow.keras.applications.mobilenet_v2 import preprocess_input
9
  from tensorflow.keras.models import load_model
10
  from fpdf import FPDF
11
  import matplotlib.pyplot as plt
12
+ # from ultralytics import YOLO # Commented out for now
13
 
14
  # -----------------------------
15
+ # Flask Config
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
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
+ # Load Keras classification model
23
+ try:
24
+ best_model = load_model("efficientnet_b0_best.keras")
25
+ print("✅ Model loaded successfully!")
26
+ except Exception as e:
27
+ print(f"❌ Error loading model: {e}")
28
+ best_model = None
 
 
 
29
 
30
  IMG_SIZE = 128
31
 
 
32
  CLASS_LABELS = ['biological', 'brown-glass', 'cardboard', 'green-glass',
33
  'metal', 'paper', 'plastic', 'shoes', 'trash', 'white-glass']
34
 
 
35
  RECYCLABLE = ["brown-glass", "green-glass", "white-glass", "metal", "plastic", "paper", "cardboard"]
36
  NON_RECYCLABLE = ["trash", "biological", "shoes"]
37
 
 
38
  stats = {}
39
 
40
  # -----------------------------
41
+ # Load YOLOv8 model (disabled for Hugging Face Spaces)
42
+ # yolo_model = YOLO("best.pt") # Uncomment and add your YOLO model file if needed
43
+
44
+ # -----------------------------
45
+ # Preprocess image for classification
46
  def preprocess_image(file_path):
47
  img = cv2.imread(file_path)
48
+ if img is None:
49
+ raise ValueError("Could not load image")
50
  img_rgb = cv2.cvtColor(cv2.resize(img, (IMG_SIZE, IMG_SIZE)), cv2.COLOR_BGR2RGB)
51
  img_input = preprocess_input(img_rgb.astype("float32"))
52
  img_input = np.expand_dims(img_input, axis=0)
 
55
  # -----------------------------
56
  # Log predictions
57
  def log_prediction(class_label):
58
+ stats[class_label] = stats.get(class_label, 0) + 1
59
+ total_items = sum(stats.values())
60
  try:
61
+ with open("waste_log.csv", "w") as f:
62
+ f.write("Waste Classification Report\n")
63
+ f.write(f"Total Items Processed: {total_items}\n")
64
+ for category, count in stats.items():
65
+ f.write(f"{category}: {count}\n")
66
+ except Exception as e:
67
+ print(f"Error writing log: {e}")
68
 
69
  # -----------------------------
70
+ # Generate PDF report
71
  def generate_pdf_report():
72
+ try:
73
+ pdf = FPDF()
74
+ pdf.add_page()
75
+ pdf.set_font("Arial", size=14)
76
+ pdf.cell(200, 10, txt="Waste Classification Report", ln=True, align="C")
77
+ pdf.ln(10)
78
+
79
+ total_items = sum(stats.values())
80
+ pdf.set_font("Arial", size=12)
81
+ pdf.cell(0, 10, txt=f"Total Items Processed: {total_items}", ln=True)
82
+
83
+ for category, count in stats.items():
84
+ pdf.cell(0, 10, txt=f"{category}: {count}", ln=True)
85
+
86
+ pdf_file = "waste_report.pdf"
87
+ pdf.output(pdf_file)
88
+ return pdf_file
89
+ except Exception as e:
90
+ print(f"Error generating PDF: {e}")
91
+ return None
92
 
93
  # -----------------------------
94
+ # Image classification route
95
  @app.route("/", methods=["GET", "POST"])
96
  def index():
97
  if request.method == "POST":
98
  file = request.files.get("file")
99
+ if not file or file.filename == "":
100
  return redirect(request.url)
101
 
102
+ # Validate file type
103
+ allowed_extensions = {'png', 'jpg', 'jpeg', 'gif', 'bmp'}
104
+ file_extension = file.filename.rsplit('.', 1)[1].lower() if '.' in file.filename else ''
105
+ if file_extension not in allowed_extensions:
106
+ return render_template("index.html", error="Please upload a valid image file (PNG, JPG, JPEG, GIF, BMP)")
107
+
108
+ try:
109
+ file_path = os.path.join(app.config["UPLOAD_FOLDER"], file.filename)
110
+ file.save(file_path)
111
+
112
+ if best_model is None:
113
+ return render_template("index.html", error="Model not loaded. Please check if the model file exists.")
114
+
115
+ img_rgb, img_input = preprocess_image(file_path)
116
+ preds = best_model.predict(img_input)
117
+ class_idx = np.argmax(preds, axis=1)[0]
118
+ class_label = CLASS_LABELS[class_idx]
119
+ confidence = preds[0][class_idx]
120
+
121
+ log_prediction(class_label)
122
+
123
+ if class_label in RECYCLABLE:
124
+ bin_type = "Recyclable ♻️"
125
+ elif class_label in NON_RECYCLABLE:
126
+ bin_type = "Non-Recyclable 🗑️"
127
+ else:
128
+ bin_type = "Unknown ⚠️"
129
+
130
+ return render_template(
131
+ "result.html",
132
+ image=file.filename,
133
+ label=class_label,
134
+ confidence=f"{confidence*100:.2f}%",
135
+ bin_type=bin_type
136
+ )
137
+ except Exception as e:
138
+ return render_template("index.html", error=f"Error processing image: {str(e)}")
139
 
140
  return render_template("index.html")
141
 
142
+ # -----------------------------
143
+ # Show statistics
144
  @app.route("/stats")
145
  def show_stats():
146
  if stats:
147
+ try:
148
+ categories = list(stats.keys())
149
+ counts = list(stats.values())
150
+ plt.figure(figsize=(10, 6))
151
+ plt.bar(categories, counts, color="green", alpha=0.7)
152
+ plt.xlabel("Category")
153
+ plt.ylabel("Count")
154
+ plt.title("Waste Classification Statistics")
155
+ plt.xticks(rotation=45)
156
+ plt.tight_layout()
157
+
158
+ # Ensure static directory exists
159
+ os.makedirs("static", exist_ok=True)
160
+ plt.savefig("static/stats_chart.png", dpi=150, bbox_inches='tight')
161
+ plt.close()
162
+ except Exception as e:
163
+ print(f"Error generating chart: {e}")
164
+
165
+ return render_template("report.html", stats=stats)
166
 
167
+ # -----------------------------
168
+ # Download reports
169
  @app.route("/download_pdf")
170
  def download_pdf():
171
+ try:
172
+ pdf_path = generate_pdf_report()
173
+ if pdf_path and os.path.exists(pdf_path):
174
+ return send_file(pdf_path, as_attachment=True)
175
+ else:
176
+ return "Error generating PDF report", 500
177
+ except Exception as e:
178
+ return f"Error: {str(e)}", 500
179
 
180
  @app.route("/download_csv")
181
  def download_csv():
182
+ try:
183
+ if os.path.exists("waste_log.csv"):
184
+ return send_file("waste_log.csv", as_attachment=True)
185
+ else:
186
+ return "No data to download", 404
187
+ except Exception as e:
188
+ return f"Error: {str(e)}", 500
189
 
190
  # -----------------------------
191
+ # Real-time camera detection (disabled for Hugging Face Spaces)
192
+ @app.route("/camera")
193
+ def camera():
194
+ return render_template("camera_disabled.html")
195
+
196
+ # Health check endpoint
197
+ @app.route("/health")
198
+ def health():
199
+ return {"status": "healthy", "model_loaded": best_model is not None}
200
+
201
+ # Run Flask
202
  if __name__ == "__main__":
203
+ port = int(os.environ.get("PORT", 7860)) # Hugging Face Spaces uses port 7860
204
+ app.run(host="0.0.0.0", port=port, debug=False)