Skip to content
Binary file added __pycache__/classifier.cpython-313.pyc
Binary file not shown.
83 changes: 83 additions & 0 deletions app.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,83 @@
from pathlib import Path
import zipfile
import streamlit as st
from PIL import Image
from classifier import WasteClassifier
Comment on lines +3 to +5

# --- MODEL KONFİGÜRASYONLARI (Notebook Analiz Sonuçları) ---
APP_DIR = Path(__file__).resolve().parent
MODEL_CANDIDATES = [
APP_DIR / "model" / "akilli_kutu_model.keras",
APP_DIR / "akilli_kutu_model.keras",
]

def resolve_model_path() -> Path:
for candidate in MODEL_CANDIDATES:
if candidate.is_file() and zipfile.is_zipfile(candidate):
return candidate
for candidate in MODEL_CANDIDATES:
if candidate.is_file():
return candidate
return MODEL_CANDIDATES[0]

MODEL_PATH = resolve_model_path()

# Klasörlerin alfabetik dizilimine sadık kalınmış Türkçe karşılıklar:
# ['cardboard', 'glass', 'metal', 'paper', 'plastic', 'trash']
CLASSES = ["Karton", "Cam", "Metal", "Kağıt", "Plastik", "Genel Çöp"]
TARGET_SIZE = (224, 224)

# Her etkileşimde modelin sıfırdan yüklenip sistemi dondurmaması için cache yapısı kullanıyoruz
@st.cache_resource
def load_cached_classifier():
Comment on lines +31 to +32
return WasteClassifier(model_path=str(MODEL_PATH), class_names=CLASSES, target_size=TARGET_SIZE)


def main():
st.set_page_config(page_title="AI Atık Sınıflandırma", page_icon="♻️", layout="centered")

st.title("♻️ Akıllı Hazne Atık Sınıflandırma Sistemi")
st.markdown("Eğitilen **MobileNetV2** modelini kullanarak atık türünü gerçek zamanlı tespit edin.")
st.write("---")


try:
classifier = load_cached_classifier()
except Exception as e:
st.error(f"Model yüklenirken kritik hata: {e}")
st.info("Lütfen model dosyasının doğru dizinde ve doğru adda olduğundan emin olun.")
return

# Dosya Yükleme Alanı
uploaded_file = st.file_uploader("Siyah arka planda çekilmiş atık fotoğrafını yükleyin...", type=["jpg", "jpeg", "png"])

if uploaded_file is not None:
# Görseli RAM'e alıyoruz
image = Image.open(uploaded_file)


col1, col2 = st.columns(2)

with col1:
st.image(image, caption="Yüklenen Nesne", use_container_width=True)

with col2:
st.subheader("Model Analizi")

with st.spinner("Model tahmin yürütüyor..."):
try:
# Tahmin motorunu çalıştır
prediction, confidence = classifier.predict(image)

# Sonuç Ekranı
st.success(f"**Tespit Edilen Sınıf:** {prediction}")
st.metric(label="Güven Skoru", value=f"% {confidence:.2f}")

# Kullanıcı yönlendirme kalkanı
st.info(f"Yapay zeka bu nesnenin **{prediction}** kutusuna atılmasını öneriyor.")

except Exception as e:
st.error(f"Tahmin işlemi esnasında bir hata oluştu: {e}")

if __name__ == "__main__":
main()
78 changes: 78 additions & 0 deletions classifier.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,78 @@
import numpy as np
from abc import ABC, abstractmethod
from pathlib import Path
import zipfile
from PIL import Image
import tensorflow as tf



class BaseClassifier(ABC):
def __init__(self, class_names: list[str]) -> None:
self.class_names = class_names
self.model: tf.keras.Model | None = None

@abstractmethod
def load_model(self, model_path: str) -> None:
pass

@abstractmethod
def _preprocess_image(self, image: Image.Image) -> np.ndarray:
pass

def predict(self, image: Image.Image) -> tuple[str, float]:
if self.model is None:
raise ValueError("Model henüz yüklenmedi.")

processed_img = self._preprocess_image(image)
predictions = self.model.predict(processed_img)

best_class_idx = np.argmax(predictions[0])

predicted_class = self.class_names[best_class_idx]
confidence = float(predictions[0][best_class_idx]) * 100

return predicted_class, confidence


class WasteClassifier(BaseClassifier):
"""MobileNetV2 tabanlı çöp sınıflandırma modelinin yönetim sınıfı."""

def __init__(self, model_path: str, class_names: list[str], target_size: tuple[int, int] = (224, 224)):
super().__init__(class_names=class_names)
self.model_path = model_path
self.target_size = target_size


self.load_model(self.model_path)

def load_model(self, model_path: str) -> None:
model_file = Path(model_path)
if not model_file.is_file():
raise FileNotFoundError(f"Model dosyası bulunamadı: {model_file}")
if not zipfile.is_zipfile(model_file):
raise ValueError(f"Model dosyası geçersiz .keras arşivi: {model_file}")
Comment on lines +53 to +54

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

issue (bug_risk): Model format doğrulaması resolve_model_path ile tutarsız görünüyor.

Burada .keras dosyasının mutlaka zipfile.is_zipfile olması şartı, resolve_model_path içindeki ikinci döngüyle çelişiyor; orada zip kontrolü yapılmadan da model yolu dönebiliyor. Böylece resolve_model_path bir dosyayı seçerken, WasteClassifier aynı dosyayı “format geçersiz” diyerek reddedebilir.

Bu tutarsızlığı gidermek için ya resolve_model_path yalnızca zipfile.is_zipfile olan adayları döndürmeli, ya da burada zip kontrolünü uyarı/log seviyesine çekip zip olmayan .keras dosyalarının da yüklenmesine izin vermelisiniz. Aksi halde geçerli bir model dosyası için kullanıcı gereksiz yere "dosya geçersiz" hatası alabilir.


try:
self.model = tf.keras.models.load_model(str(model_file))
except Exception as e:
raise RuntimeError(f"Model yüklenirken hata oluştu: {e}")


# Görüntü ön işleme adımları
def _preprocess_image(self, image: Image.Image) -> np.ndarray:
"""Görüntüyü MobileNetV2 girdisine ve notebook'taki ön işleme adımlarına hazırlar."""
# Eğer görsel PNG formatında ve saydam arka planlıysa, RGB'ye dönüştürerek alpha kanalını eliyoruz
if image.mode != "RGB":
image = image.convert("RGB")

# Görseli modelin eğitim boyutu olan 224x224'e ayarladık
image = image.resize(self.target_size)
img_array = tf.keras.preprocessing.image.img_to_array(image)

# Notebook'taki Veri Önişleme (rescale=1./255) adımı ile eşleme
img_array = img_array / 255.0

# Batch boyutu ekleme: (224, 224, 3) -> (1, 224, 224, 3)
img_array = np.expand_dims(img_array, axis=0)
return img_array
Binary file not shown.
Loading