| import streamlit as st |
| import tensorflow as tf |
| from tensorflow.keras.preprocessing.image import ImageDataGenerator |
| from tensorflow.keras.models import Sequential, load_model |
| from tensorflow.keras.layers import Conv2D, MaxPooling2D, Flatten, Dense, Dropout |
| import numpy as np |
| from PIL import Image, UnidentifiedImageError |
| import os |
|
|
| |
| |
| |
| DATASET_DIR = "dataset-resized" |
| MODEL_PATH = "waste_classifier.h5" |
| CLASS_FILE = "classes.npy" |
|
|
| IMG_SIZE = (128, 128) |
| BATCH_SIZE = 16 |
| EPOCHS = 5 |
|
|
| |
| CLASSES = ['cardboard', 'glass', 'metal', 'paper', 'plastic', 'trash'] |
|
|
| |
| |
| |
| st.set_page_config( |
| page_title="AI Smart Waste Classification", |
| layout="centered" |
| ) |
|
|
| |
| |
| |
| def clean_dataset(dataset_path): |
| valid_extensions = (".jpg", ".jpeg", ".png") |
| removed = 0 |
|
|
| for root, dirs, files in os.walk(dataset_path): |
| for file in files: |
| file_path = os.path.join(root, file) |
|
|
| |
| if not file.lower().endswith(valid_extensions): |
| try: |
| os.remove(file_path) |
| removed += 1 |
| except: |
| pass |
| continue |
|
|
| |
| try: |
| with Image.open(file_path) as img: |
| img.verify() |
| except: |
| try: |
| os.remove(file_path) |
| removed += 1 |
| except: |
| pass |
|
|
| return removed |
|
|
| |
| |
| |
| def train_model(): |
| removed_files = clean_dataset(DATASET_DIR) |
| st.info(f"Removed {removed_files} corrupted/invalid files.") |
|
|
| datagen = ImageDataGenerator( |
| rescale=1./255, |
| validation_split=0.2, |
| rotation_range=20, |
| zoom_range=0.2, |
| horizontal_flip=True |
| ) |
|
|
| train_data = datagen.flow_from_directory( |
| DATASET_DIR, |
| target_size=IMG_SIZE, |
| batch_size=BATCH_SIZE, |
| class_mode='categorical', |
| subset='training', |
| shuffle=True, |
| classes=CLASSES |
| ) |
|
|
| val_data = datagen.flow_from_directory( |
| DATASET_DIR, |
| target_size=IMG_SIZE, |
| batch_size=BATCH_SIZE, |
| class_mode='categorical', |
| subset='validation', |
| shuffle=False, |
| classes=CLASSES |
| ) |
|
|
| model = Sequential([ |
| Conv2D(32, (3,3), activation='relu', input_shape=(128,128,3)), |
| MaxPooling2D(2,2), |
|
|
| Conv2D(64, (3,3), activation='relu'), |
| MaxPooling2D(2,2), |
|
|
| Conv2D(128, (3,3), activation='relu'), |
| MaxPooling2D(2,2), |
|
|
| Flatten(), |
|
|
| Dense(256, activation='relu'), |
| Dropout(0.5), |
|
|
| Dense(len(CLASSES), activation='softmax') |
| ]) |
|
|
| model.compile( |
| optimizer='adam', |
| loss='categorical_crossentropy', |
| metrics=['accuracy'] |
| ) |
|
|
| with st.spinner("Training AI model... Please wait..."): |
| model.fit( |
| train_data, |
| validation_data=val_data, |
| epochs=EPOCHS |
| ) |
|
|
| model.save(MODEL_PATH) |
| np.save(CLASS_FILE, CLASSES) |
|
|
| return model |
|
|
| |
| |
| |
| def load_or_train_model(): |
| if not os.path.exists(MODEL_PATH) or not os.path.exists(CLASS_FILE): |
| st.warning("Training model for first-time use...") |
| return train_model() |
|
|
| try: |
| model = load_model(MODEL_PATH) |
| saved_classes = np.load(CLASS_FILE, allow_pickle=True).tolist() |
|
|
| |
| if saved_classes != CLASSES or model.output_shape[-1] != len(CLASSES): |
| st.warning("Old model mismatch detected. Retraining...") |
| os.remove(MODEL_PATH) |
| os.remove(CLASS_FILE) |
| return train_model() |
|
|
| return model |
|
|
| except: |
| st.warning("Model corrupted. Retraining...") |
| return train_model() |
|
|
| |
| |
| |
| model = load_or_train_model() |
|
|
| |
| |
| |
| st.title("♻️ AI Smart Waste Classification") |
| st.write("Upload an image to classify waste and support sustainable recycling.") |
|
|
| uploaded_file = st.file_uploader( |
| "Upload Waste Image", |
| type=["jpg", "jpeg", "png"], |
| accept_multiple_files=False |
| ) |
|
|
| |
| |
| |
| if uploaded_file is not None: |
| try: |
| image = Image.open(uploaded_file).convert("RGB") |
|
|
| st.image( |
| image, |
| caption="Uploaded Image", |
| use_container_width=True |
| ) |
|
|
| |
| img = image.resize(IMG_SIZE) |
| img_array = np.array(img) / 255.0 |
| img_array = np.expand_dims(img_array, axis=0) |
|
|
| |
| with st.spinner("Analyzing waste type..."): |
| prediction = model.predict(img_array, verbose=0) |
|
|
| probabilities = prediction.flatten() |
|
|
| predicted_index = np.argmax(probabilities) |
| predicted_class = CLASSES[predicted_index] |
| confidence = probabilities[predicted_index] * 100 |
|
|
| |
| |
| |
| st.subheader("📊 Prediction Scores") |
|
|
| for i, class_name in enumerate(CLASSES): |
| st.write( |
| f"{class_name.upper()}: {probabilities[i]*100:.2f}%" |
| ) |
|
|
| |
| st.success( |
| f"Predicted Type: {predicted_class.upper()}" |
| ) |
|
|
| st.info( |
| f"Confidence: {confidence:.2f}%" |
| ) |
|
|
| |
| tips = { |
| 'plastic': 'Recycle plastic properly to reduce pollution.', |
| 'paper': 'Reuse or recycle paper to save trees.', |
| 'metal': 'Metal can be recycled efficiently.', |
| 'glass': 'Glass is reusable and recyclable.', |
| 'trash': 'Dispose responsibly to reduce environmental damage.', |
| 'cardboard': 'Recycle cardboard to reduce waste.' |
| } |
|
|
| st.subheader("🌱 Sustainability Suggestion") |
| st.write( |
| tips.get( |
| predicted_class, |
| "Dispose responsibly." |
| ) |
| ) |
|
|
| except UnidentifiedImageError: |
| st.error("Invalid image file. Please upload a valid JPG, JPEG, or PNG image.") |
|
|
| except Exception as e: |
| st.error(f"Error processing image: {str(e)}") |
|
|
| |
| |
| |
| st.markdown("---") |
| st.subheader("🖼️ Sample Images to Test") |
| st.write(""" |
| Use images like these: |
| - plastic_bottle.jpg |
| - newspaper.jpg |
| - soda_can.jpg |
| - glass_bottle.jpg |
| - cardboard_box.jpg |
| - trash_bag.jpg |
| """) |
|
|
| |
| |
| |
| st.markdown("---") |
| st.caption("Built using TensorFlow + Streamlit for Sustainable AI") |