Spaces:
Build error
Build error
File size: 12,363 Bytes
f02eb0f | 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 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 | import os
import time
import cv2 # OpenCV for image handling
import easyocr # For OCR
import torch # PyTorch (dependency for YOLO/EasyOCR)
import numpy as np # For numerical operations
from ultralytics import YOLO # YOLO model from ultralytics
from flask import Flask, request, render_template, redirect, url_for, flash
from werkzeug.utils import secure_filename
import traceback # To print full error tracebacks
import re # Import regular expressions for post-processing
# --- Configuration ---
UPLOAD_FOLDER = os.path.join('static', 'uploads')
RESULT_FOLDER = os.path.join('static', 'results')
ALLOWED_EXTENSIONS = {'png', 'jpg', 'jpeg'}
weights_path = 'best.pt' # Assumes best.pt is in the same folder as app.py
PLATE_CLASS_NAME = 'license_plate'
app = Flask(__name__)
app.config['UPLOAD_FOLDER'] = UPLOAD_FOLDER
app.config['RESULT_FOLDER'] = RESULT_FOLDER
app.secret_key = 'super secret key' # Needed for flashing messages
# --- Create Folders if they don't exist ---
os.makedirs(UPLOAD_FOLDER, exist_ok=True)
os.makedirs(RESULT_FOLDER, exist_ok=True)
# --- Load Models (Load only once when the app starts) ---
reader = None
model = None
model_class_names = {} # Initialize empty
print("Loading EasyOCR Reader...")
try:
# Use gpu=True if you have a CUDA-enabled GPU and compatible PyTorch/CUDA
model_dir = os.path.join('.', 'easyocr_models')
os.makedirs(model_dir, exist_ok=True)
reader = easyocr.Reader(['en'], gpu=torch.cuda.is_available(), model_storage_directory=model_dir)
print("EasyOCR Reader loaded successfully.")
except Exception as e:
print(f"Error loading EasyOCR Reader: {e}. OCR will not work.")
print(traceback.format_exc()) # Print full traceback
print("Loading YOLO Model...")
try:
if not os.path.exists(weights_path):
raise FileNotFoundError(f"YOLO weights file not found at {weights_path}. Please make sure 'best.pt' is in the same directory as app.py or update the path.")
model = YOLO(weights_path)
print("YOLO Model loaded successfully.")
# Store class names if model loaded
if model:
model_class_names = model.names
print("Class names:", model_class_names) # Print class names {index: 'name'}
except Exception as e:
print(f"Error loading YOLO model from {weights_path}: {e}. Detection will not work.")
print(traceback.format_exc()) # Print full traceback
# --- Helper Functions ---
def allowed_file(filename):
"""Checks if the filename has an allowed extension."""
return '.' in filename and \
filename.rsplit('.', 1)[1].lower() in ALLOWED_EXTENSIONS
def perform_ocr_on_image(img_crop, allowlist='0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ'):
"""Performs OCR on a given image crop (NumPy array)."""
if reader is None:
print("EasyOCR Reader not loaded. Cannot perform OCR.")
return "OCR Error"
try:
# Convert to grayscale
gray_img = cv2.cvtColor(img_crop, cv2.COLOR_BGR2GRAY)
# Resize for consistent input size
target_height = 64 # You can adjust this value
scale_ratio = target_height / gray_img.shape[0]
target_width = int(gray_img.shape[1] * scale_ratio)
resized_img = cv2.resize(gray_img, (target_width, target_height))
# Basic preprocessing
resized_img = cv2.equalizeHist(resized_img) # Enhance contrast
# Perform OCR
results = reader.readtext(resized_img, allowlist=allowlist)
# Process results
if not results:
return "No text found"
# Take the result with highest confidence
text = results[0][1] # Get the text part
# Convert to uppercase and remove any non-alphanumeric characters
text = text.upper()
text = re.sub(r'[^A-Z0-9]', '', text)
return text if text else "No text found"
print(f"Final combined text after post-processing: '{plate_text}'")
return plate_text if plate_text else "No text found"
except Exception as e:
print(f"Error during OCR processing in perform_ocr_on_image: {e}")
print(traceback.format_exc())
return "OCR Processing Error"
# ... (rest of run_detection_ocr, Flask routes, and __main__ remain the same) ...
def run_detection_ocr(image_path, result_filename):
"""Runs YOLO detection and EasyOCR on the image."""
if model is None or reader is None:
print("Models not loaded. Cannot process image.")
return None, ["Model Loading Error"]
detected_texts = []
result_image_path = None
try:
# Read the image
img = cv2.imread(image_path)
if img is None:
print(f"Error: Could not read image at {image_path}")
return None, ["Image Reading Error"]
img_copy = img.copy() # Make a copy for drawing
# Run YOLO detection with original confidence threshold
print(f"Running YOLO prediction on {image_path}...")
results = model.predict(img, conf=0.25) # Restore original confidence for detection
print("YOLO prediction complete.")
# Process detections
for result in results:
boxes = result.boxes
if boxes is None or len(boxes) == 0:
print("No boxes detected in this result.")
continue
for box in boxes:
try:
# Get class index and name using the loaded names
class_id = int(box.cls[0])
class_name = model_class_names.get(class_id, 'Unknown')
confidence = float(box.conf[0])
x1_temp, y1_temp, x2_temp, y2_temp = map(int, box.xyxy[0])
print(f"Detected '{class_name}' (Conf: {confidence:.2f}) at [{x1_temp}, {y1_temp}, {x2_temp}, {y2_temp}]")
# Check with the CORRECTED class name
if class_name.lower() == PLATE_CLASS_NAME.lower(): # Comparison should now work
x1, y1, x2, y2 = map(int, box.xyxy[0])
padding = 10
crop_y1 = max(0, y1 - padding)
crop_y2 = min(img.shape[0], y2 + padding)
crop_x1 = max(0, x1 - padding)
crop_x2 = min(img.shape[1], x2 + padding)
if crop_y2 > crop_y1 and crop_x2 > crop_x1:
plate_crop = img[crop_y1:crop_y2, crop_x1:crop_x2]
# Added print before calling OCR
print(f"--- Cropped {PLATE_CLASS_NAME} at [{crop_x1}, {crop_y1}, {crop_x2}, {crop_y2}], attempting OCR ---")
plate_text = perform_ocr_on_image(plate_crop) # Function updated with preprocessing/postprocessing
print(f"-> OCR Result returned for {PLATE_CLASS_NAME}: {plate_text}")
detected_texts.append(plate_text)
# Drawing logic
label = f"{class_name}: {plate_text}" # Label uses result from OCR call
cv2.rectangle(img_copy, (x1, y1), (x2, y2), (0, 255, 0), 2)
text_pos = (x1, y1 - 10) if y1 > 20 else (x1, y2 + 20)
cv2.putText(img_copy, label, text_pos,
cv2.FONT_HERSHEY_SIMPLEX, 0.7, (0, 0, 255), 2)
else:
print(f"Skipping invalid crop dimensions for {PLATE_CLASS_NAME}: y1={crop_y1}, y2={crop_y2}, x1={crop_x1}, x2={crop_x2}")
else:
# Draw blue boxes for other detected objects (optional)
cv2.rectangle(img_copy, (x1_temp, y1_temp), (x2_temp, y2_temp), (255, 0, 0), 1) # Blue box, thinner
cv2.putText(img_copy, f"{class_name} {confidence:.2f}", (x1_temp, y1_temp - 5),
cv2.FONT_HERSHEY_SIMPLEX, 0.5, (255, 0, 0), 1)
except Exception as e_inner:
print(f"Error processing a specific detection: {e_inner}")
print(traceback.format_exc())
detected_texts.append("Detection Proc. Error")
# Save the result image
result_image_path = os.path.join(app.config['RESULT_FOLDER'], result_filename)
cv2.imwrite(result_image_path, img_copy)
print(f"Result image saved to: {result_image_path}")
return result_image_path, detected_texts
except Exception as e:
print(f"Error during detection/OCR: {e}")
print(traceback.format_exc())
return None, ["Overall Processing Error"]
# --- Flask Routes ---
@app.route('/', methods=['GET', 'POST'])
def upload_file():
error_message = None
if request.method == 'POST':
if 'file' not in request.files:
flash('No file part')
return redirect(request.url)
file = request.files['file']
if file.filename == '':
flash('No selected file')
return redirect(request.url)
if file and allowed_file(file.filename):
try:
original_filename = secure_filename(file.filename)
timestamp = time.strftime("%Y%m%d-%H%M%S")
unique_filename = f"{timestamp}_{original_filename}"
upload_path = os.path.join(app.config['UPLOAD_FOLDER'], unique_filename)
file.save(upload_path)
print(f"File saved to: {upload_path}")
# --- Call Processing Logic ---
if model is None or reader is None:
return render_template('result.html',
original_image_url=url_for('static', filename=f'uploads/{unique_filename}'),
result_image_url=None,
detected_texts=None,
error_message="AI models failed to load. Cannot process image.")
result_filename = f"result_{unique_filename}"
result_image_path, detected_texts = run_detection_ocr(upload_path, result_filename)
# Generate URLs for the templates
original_url = url_for('static', filename=f'uploads/{unique_filename}')
result_url = url_for('static', filename=f'results/{result_filename}') if result_image_path else None
# *** Filter out error messages before displaying ***
display_texts = [text for text in detected_texts if "Error" not in text and text != "No text found" and text != "Crop too small" and text != "Invalid Crop"]
if not display_texts and detected_texts: # If only errors or 'no text', show generic message
display_texts = ["No text recognized"]
elif not detected_texts and not display_texts: # If detection worked but OCR returned nothing valid
display_texts = ["No text recognized"]
elif not detected_texts: # Fallback if processing completely failed
display_texts = ["Processing Error"]
return render_template('result.html',
original_image_url=original_url,
result_image_url=result_url,
detected_texts=display_texts, # Use filtered list
error_message=None if result_image_path else "An error occurred during image processing.")
except Exception as e:
error_message = f"An error occurred during file upload or processing: {e}"
print(f"Error in upload_file route: {e}")
print(traceback.format_exc())
# Render index again, show the error
return render_template('index.html', error_message=error_message)
else:
error_message = "Invalid file type. Please upload a PNG, JPG, or JPEG image."
return render_template('index.html', error_message=error_message)
# For GET requests
return render_template('index.html', error_message=None)
# --- Run the App ---
if __name__ == "__main__":
app.run(debug=True)
|