File size: 6,638 Bytes
21c551e
 
 
 
 
 
 
da622da
 
21c551e
 
 
 
 
da622da
 
 
 
 
 
 
 
21c551e
da622da
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
21c551e
da622da
 
 
21c551e
da622da
 
 
 
 
 
21c551e
da622da
21c551e
 
 
da622da
 
21c551e
 
da622da
 
 
21c551e
 
da622da
 
 
 
21c551e
da622da
21c551e
 
da622da
21c551e
 
 
 
da622da
21c551e
 
 
 
da622da
21c551e
 
 
 
da622da
21c551e
 
 
 
da622da
 
21c551e
 
 
da622da
21c551e
 
 
 
 
 
 
 
 
 
 
da622da
21c551e
 
 
 
da622da
21c551e
 
da622da
 
 
 
21c551e
 
 
 
 
da622da
 
21c551e
 
da622da
21c551e
 
 
 
 
 
 
da622da
21c551e
 
 
 
 
 
 
da622da
21c551e
 
da622da
21c551e
da622da
 
 
21c551e
da622da
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
import os
import numpy as np
import tensorflow as tf
from flask import Flask, request, render_template, jsonify
from tensorflow.keras.utils import load_img, img_to_array
from werkzeug.utils import secure_filename
from datetime import datetime
from huggingface_hub import hf_hub_download # Crucial for downloading model from HF Hub
import time # Used for potential retry logic, though not explicitly in hf_hub_download here

app = Flask(__name__)

# --- Model Loading Configuration ---
MODEL_FILE_NAME = "model.keras"
# IMPORTANT: Replace "YOUR_USERNAME/garbage-detection-model" with the actual REPO ID of YOUR MODEL on Hugging Face Hub.
# This is the repository where your 'model.keras' file is stored.
# Example: "your_huggingface_username/your_model_repo_name"
MODEL_REPO_ID = "nonamelife/garbage-detection-model" # <--- MAKE SURE THIS IS YOUR CORRECT MODEL REPO ID!

model = None # Initialize model as None

# --- Model Loading Logic ---
try:
    print(f"Attempting to download '{MODEL_FILE_NAME}' from Hugging Face Hub ({MODEL_REPO_ID})...")
    
    # hf_hub_download returns the FULL PATH to the downloaded file in the cache.
    # We specify local_dir within /app to ensure write permissions and consistency.
    # local_dir_use_symlinks=False is important for Docker environments to avoid symlink issues.
    downloaded_model_path = hf_hub_download(
        repo_id=MODEL_REPO_ID, 
        filename=MODEL_FILE_NAME,
        local_dir="/app/.cache/huggingface/models", # This directory will be created if it doesn't exist
        local_dir_use_symlinks=False 
    )
    
    print(f"'{MODEL_FILE_NAME}' downloaded successfully to: {downloaded_model_path}")

    # Now, load the model directly from the downloaded path.
    # This check is a safeguard, as hf_hub_download should ensure existence.
    if os.path.exists(downloaded_model_path): 
        model = tf.keras.models.load_model(downloaded_model_path)
        print("Model loaded successfully!")
    else:
        # This message indicates a very unusual state if download was reported successful.
        print(f"ERROR: Download reported success, but file not found at expected path: {downloaded_model_path}")

except Exception as e:
    # Catch any exceptions during download or loading and log them.
    print(f"FATAL: Could not download or load model from Hugging Face Hub: {e}")
    model = None # Ensure model remains None if there's an error

# --- End Model Loading Logic ---


# Configurations for Flask app
UPLOAD_FOLDER = os.path.join('static', 'uploads')
ALLOWED_EXTENSIONS = {'jpg', 'jpeg', 'png'}
app.config['UPLOAD_FOLDER'] = UPLOAD_FOLDER
# Ensure the uploads directory exists within the container
os.makedirs(UPLOAD_FOLDER, exist_ok=True) 

def allowed_file(filename):
    """Checks if the uploaded file has an allowed extension."""
    return '.' in filename and \
           filename.rsplit('.', 1)[1].lower() in ALLOWED_EXTENSIONS

def preprocess_image(image_path):
    """Loads and preprocesses an image for model prediction."""
    img = load_img(image_path, target_size=(224, 224)) # Load image, resize to 224x224
    img_array = img_to_array(img) / 255.0 # Convert to array and normalize pixel values
    return np.expand_dims(img_array, axis=0) # Add batch dimension for model input

# --- Flask Routes ---
@app.route('/')
def index():
    """Renders the home page."""
    return render_template('home.html')

@app.route('/tool')
def tool():
    """Renders the image upload tool page."""
    return render_template('tool.html')

@app.route('/about')
def about():
    """Renders the about page."""
    return render_template('about.html')

@app.route('/contact')
def contact():
    """Renders the contact page."""
    return render_template('contact.html')

@app.route('/predict', methods=['POST'])
def predict():
    """Handles image uploads and returns predictions."""
    # Check if the model was loaded successfully at startup
    if model is None:
        return jsonify({'error': 'Model not loaded. Please check server logs.'}), 500

    # Check if a file was part of the request
    if 'file' not in request.files:
        return jsonify({'error': 'No files uploaded'}), 400

    files = request.files.getlist('file')
    if not files or all(f.filename == '' for f in files):
        return jsonify({'error': 'No files selected'}), 400

    results = []
    for file in files:
        file_path = None
        if file and allowed_file(file.filename):
            # Secure filename and create a unique name to prevent collisions
            filename = secure_filename(file.filename)
            timestamp = datetime.now().strftime("%Y%m%d%H%M%S%f")
            unique_filename = f"{timestamp}_{filename}"
            file_path = os.path.join(app.config['UPLOAD_FOLDER'], unique_filename)
            file.save(file_path) # Save the uploaded file temporarily

            try:
                img_array = preprocess_image(file_path) # Preprocess the image
                prediction = model.predict(img_array)[0][0] # Get prediction from the model
                
                # Determine label and confidence based on sigmoid output
                label = "Dirty" if prediction > 0.5 else "Clean"
                confidence = prediction if label == "Dirty" else 1 - prediction

                results.append({
                    'label': label,
                    'confidence': f"{confidence:.2%}", # Format confidence as percentage
                    'image_url': f"/static/uploads/{unique_filename}" # URL for displaying the image
                })
            except Exception as e:
                # Catch any errors during prediction or processing
                results.append({
                    'label': 'Error',
                    'confidence': 'N/A',
                    'image_url': None,
                    'error': str(e)
                })
        else:
            # Handle invalid file types
            results.append({
                'label': 'Error',
                'confidence': 'N/A',
                'image_url': None,
                'error': f"Invalid file type: {file.filename}"
            })

    # Render the results page with predictions
    return render_template('results.html', results=results)

# --- Main execution block ---
if __name__ == '__main__':
    # Hugging Face Spaces sets the PORT environment variable for the app to listen on.
    # We default to 7860 as it's common for HF Spaces apps.
    # Debug mode should be OFF for production deployments (like Hugging Face Spaces) for security.
    port = int(os.environ.get('PORT', 7860))
    app.run(host='0.0.0.0', port=port, debug=False)