Ayush commited on
Commit
f02eb0f
·
1 Parent(s): 85c89a4

Added files

Browse files
Dockerfile ADDED
@@ -0,0 +1,31 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ # Use a base Python image
2
+ FROM python:3.9-slim
3
+
4
+ # Set the working directory
5
+ WORKDIR /app
6
+
7
+ # Copy requirements first to leverage Docker caching
8
+ COPY requirements.txt requirements.txt
9
+
10
+ # Install system dependencies needed by OpenCV and potentially others
11
+ RUN apt-get update && apt-get install -y --no-install-recommends \
12
+ libgl1-mesa-glx \
13
+ libglib2.0-0 \
14
+ # Add any other system libs if needed during build
15
+ && rm -rf /var/lib/apt/lists/*
16
+
17
+ # Install Python dependencies
18
+ # Use --no-cache-dir to reduce image size
19
+ RUN pip install --no-cache-dir -r requirements.txt
20
+
21
+ # Copy the rest of the application code
22
+ COPY . .
23
+
24
+ # Expose the port Flask runs on (default is 5000)
25
+ EXPOSE 5000
26
+
27
+ # Command to run the application
28
+ # Use gunicorn for a more robust server than Flask's default debug server
29
+ # Install gunicorn first
30
+ RUN pip install --no-cache-dir gunicorn
31
+ CMD ["gunicorn", "--bind", "0.0.0.0:5000", "app:app"]
app.py ADDED
@@ -0,0 +1,274 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import os
2
+ import time
3
+ import cv2 # OpenCV for image handling
4
+ import easyocr # For OCR
5
+ import torch # PyTorch (dependency for YOLO/EasyOCR)
6
+ import numpy as np # For numerical operations
7
+ from ultralytics import YOLO # YOLO model from ultralytics
8
+ from flask import Flask, request, render_template, redirect, url_for, flash
9
+ from werkzeug.utils import secure_filename
10
+ import traceback # To print full error tracebacks
11
+ import re # Import regular expressions for post-processing
12
+
13
+ # --- Configuration ---
14
+ UPLOAD_FOLDER = os.path.join('static', 'uploads')
15
+ RESULT_FOLDER = os.path.join('static', 'results')
16
+ ALLOWED_EXTENSIONS = {'png', 'jpg', 'jpeg'}
17
+
18
+ weights_path = 'best.pt' # Assumes best.pt is in the same folder as app.py
19
+ PLATE_CLASS_NAME = 'license_plate'
20
+
21
+
22
+ app = Flask(__name__)
23
+ app.config['UPLOAD_FOLDER'] = UPLOAD_FOLDER
24
+ app.config['RESULT_FOLDER'] = RESULT_FOLDER
25
+ app.secret_key = 'super secret key' # Needed for flashing messages
26
+
27
+ # --- Create Folders if they don't exist ---
28
+ os.makedirs(UPLOAD_FOLDER, exist_ok=True)
29
+ os.makedirs(RESULT_FOLDER, exist_ok=True)
30
+
31
+ # --- Load Models (Load only once when the app starts) ---
32
+ reader = None
33
+ model = None
34
+ model_class_names = {} # Initialize empty
35
+
36
+ print("Loading EasyOCR Reader...")
37
+ try:
38
+ # Use gpu=True if you have a CUDA-enabled GPU and compatible PyTorch/CUDA
39
+ model_dir = os.path.join('.', 'easyocr_models')
40
+ os.makedirs(model_dir, exist_ok=True)
41
+ reader = easyocr.Reader(['en'], gpu=torch.cuda.is_available(), model_storage_directory=model_dir)
42
+ print("EasyOCR Reader loaded successfully.")
43
+ except Exception as e:
44
+ print(f"Error loading EasyOCR Reader: {e}. OCR will not work.")
45
+ print(traceback.format_exc()) # Print full traceback
46
+
47
+ print("Loading YOLO Model...")
48
+ try:
49
+ if not os.path.exists(weights_path):
50
+ 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.")
51
+ model = YOLO(weights_path)
52
+ print("YOLO Model loaded successfully.")
53
+ # Store class names if model loaded
54
+ if model:
55
+ model_class_names = model.names
56
+ print("Class names:", model_class_names) # Print class names {index: 'name'}
57
+ except Exception as e:
58
+ print(f"Error loading YOLO model from {weights_path}: {e}. Detection will not work.")
59
+ print(traceback.format_exc()) # Print full traceback
60
+
61
+ # --- Helper Functions ---
62
+ def allowed_file(filename):
63
+ """Checks if the filename has an allowed extension."""
64
+ return '.' in filename and \
65
+ filename.rsplit('.', 1)[1].lower() in ALLOWED_EXTENSIONS
66
+
67
+ def perform_ocr_on_image(img_crop, allowlist='0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ'):
68
+ """Performs OCR on a given image crop (NumPy array)."""
69
+ if reader is None:
70
+ print("EasyOCR Reader not loaded. Cannot perform OCR.")
71
+ return "OCR Error"
72
+
73
+ try:
74
+ # Convert to grayscale
75
+ gray_img = cv2.cvtColor(img_crop, cv2.COLOR_BGR2GRAY)
76
+
77
+ # Resize for consistent input size
78
+ target_height = 64 # You can adjust this value
79
+ scale_ratio = target_height / gray_img.shape[0]
80
+ target_width = int(gray_img.shape[1] * scale_ratio)
81
+ resized_img = cv2.resize(gray_img, (target_width, target_height))
82
+
83
+ # Basic preprocessing
84
+ resized_img = cv2.equalizeHist(resized_img) # Enhance contrast
85
+
86
+ # Perform OCR
87
+ results = reader.readtext(resized_img, allowlist=allowlist)
88
+
89
+ # Process results
90
+ if not results:
91
+ return "No text found"
92
+
93
+ # Take the result with highest confidence
94
+ text = results[0][1] # Get the text part
95
+
96
+ # Convert to uppercase and remove any non-alphanumeric characters
97
+ text = text.upper()
98
+ text = re.sub(r'[^A-Z0-9]', '', text)
99
+
100
+ return text if text else "No text found"
101
+
102
+
103
+ print(f"Final combined text after post-processing: '{plate_text}'")
104
+
105
+ return plate_text if plate_text else "No text found"
106
+
107
+ except Exception as e:
108
+ print(f"Error during OCR processing in perform_ocr_on_image: {e}")
109
+ print(traceback.format_exc())
110
+ return "OCR Processing Error"
111
+
112
+ # ... (rest of run_detection_ocr, Flask routes, and __main__ remain the same) ...
113
+
114
+ def run_detection_ocr(image_path, result_filename):
115
+ """Runs YOLO detection and EasyOCR on the image."""
116
+ if model is None or reader is None:
117
+ print("Models not loaded. Cannot process image.")
118
+ return None, ["Model Loading Error"]
119
+
120
+ detected_texts = []
121
+ result_image_path = None
122
+
123
+ try:
124
+ # Read the image
125
+ img = cv2.imread(image_path)
126
+ if img is None:
127
+ print(f"Error: Could not read image at {image_path}")
128
+ return None, ["Image Reading Error"]
129
+
130
+ img_copy = img.copy() # Make a copy for drawing
131
+
132
+ # Run YOLO detection with original confidence threshold
133
+ print(f"Running YOLO prediction on {image_path}...")
134
+ results = model.predict(img, conf=0.25) # Restore original confidence for detection
135
+ print("YOLO prediction complete.")
136
+
137
+ # Process detections
138
+ for result in results:
139
+ boxes = result.boxes
140
+ if boxes is None or len(boxes) == 0:
141
+ print("No boxes detected in this result.")
142
+ continue
143
+
144
+ for box in boxes:
145
+ try:
146
+ # Get class index and name using the loaded names
147
+ class_id = int(box.cls[0])
148
+ class_name = model_class_names.get(class_id, 'Unknown')
149
+ confidence = float(box.conf[0])
150
+ x1_temp, y1_temp, x2_temp, y2_temp = map(int, box.xyxy[0])
151
+ print(f"Detected '{class_name}' (Conf: {confidence:.2f}) at [{x1_temp}, {y1_temp}, {x2_temp}, {y2_temp}]")
152
+
153
+ # Check with the CORRECTED class name
154
+ if class_name.lower() == PLATE_CLASS_NAME.lower(): # Comparison should now work
155
+ x1, y1, x2, y2 = map(int, box.xyxy[0])
156
+ padding = 10
157
+ crop_y1 = max(0, y1 - padding)
158
+ crop_y2 = min(img.shape[0], y2 + padding)
159
+ crop_x1 = max(0, x1 - padding)
160
+ crop_x2 = min(img.shape[1], x2 + padding)
161
+
162
+ if crop_y2 > crop_y1 and crop_x2 > crop_x1:
163
+ plate_crop = img[crop_y1:crop_y2, crop_x1:crop_x2]
164
+
165
+ # Added print before calling OCR
166
+ print(f"--- Cropped {PLATE_CLASS_NAME} at [{crop_x1}, {crop_y1}, {crop_x2}, {crop_y2}], attempting OCR ---")
167
+ plate_text = perform_ocr_on_image(plate_crop) # Function updated with preprocessing/postprocessing
168
+ print(f"-> OCR Result returned for {PLATE_CLASS_NAME}: {plate_text}")
169
+ detected_texts.append(plate_text)
170
+
171
+ # Drawing logic
172
+ label = f"{class_name}: {plate_text}" # Label uses result from OCR call
173
+ cv2.rectangle(img_copy, (x1, y1), (x2, y2), (0, 255, 0), 2)
174
+ text_pos = (x1, y1 - 10) if y1 > 20 else (x1, y2 + 20)
175
+ cv2.putText(img_copy, label, text_pos,
176
+ cv2.FONT_HERSHEY_SIMPLEX, 0.7, (0, 0, 255), 2)
177
+ else:
178
+ print(f"Skipping invalid crop dimensions for {PLATE_CLASS_NAME}: y1={crop_y1}, y2={crop_y2}, x1={crop_x1}, x2={crop_x2}")
179
+ else:
180
+ # Draw blue boxes for other detected objects (optional)
181
+ cv2.rectangle(img_copy, (x1_temp, y1_temp), (x2_temp, y2_temp), (255, 0, 0), 1) # Blue box, thinner
182
+ cv2.putText(img_copy, f"{class_name} {confidence:.2f}", (x1_temp, y1_temp - 5),
183
+ cv2.FONT_HERSHEY_SIMPLEX, 0.5, (255, 0, 0), 1)
184
+
185
+
186
+ except Exception as e_inner:
187
+ print(f"Error processing a specific detection: {e_inner}")
188
+ print(traceback.format_exc())
189
+ detected_texts.append("Detection Proc. Error")
190
+
191
+ # Save the result image
192
+ result_image_path = os.path.join(app.config['RESULT_FOLDER'], result_filename)
193
+ cv2.imwrite(result_image_path, img_copy)
194
+ print(f"Result image saved to: {result_image_path}")
195
+
196
+ return result_image_path, detected_texts
197
+
198
+ except Exception as e:
199
+ print(f"Error during detection/OCR: {e}")
200
+ print(traceback.format_exc())
201
+ return None, ["Overall Processing Error"]
202
+
203
+ # --- Flask Routes ---
204
+ @app.route('/', methods=['GET', 'POST'])
205
+ def upload_file():
206
+ error_message = None
207
+ if request.method == 'POST':
208
+ if 'file' not in request.files:
209
+ flash('No file part')
210
+ return redirect(request.url)
211
+ file = request.files['file']
212
+ if file.filename == '':
213
+ flash('No selected file')
214
+ return redirect(request.url)
215
+
216
+ if file and allowed_file(file.filename):
217
+ try:
218
+ original_filename = secure_filename(file.filename)
219
+ timestamp = time.strftime("%Y%m%d-%H%M%S")
220
+ unique_filename = f"{timestamp}_{original_filename}"
221
+ upload_path = os.path.join(app.config['UPLOAD_FOLDER'], unique_filename)
222
+ file.save(upload_path)
223
+ print(f"File saved to: {upload_path}")
224
+
225
+ # --- Call Processing Logic ---
226
+ if model is None or reader is None:
227
+ return render_template('result.html',
228
+ original_image_url=url_for('static', filename=f'uploads/{unique_filename}'),
229
+ result_image_url=None,
230
+ detected_texts=None,
231
+ error_message="AI models failed to load. Cannot process image.")
232
+
233
+ result_filename = f"result_{unique_filename}"
234
+ result_image_path, detected_texts = run_detection_ocr(upload_path, result_filename)
235
+
236
+ # Generate URLs for the templates
237
+ original_url = url_for('static', filename=f'uploads/{unique_filename}')
238
+ result_url = url_for('static', filename=f'results/{result_filename}') if result_image_path else None
239
+
240
+ # *** Filter out error messages before displaying ***
241
+ 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"]
242
+ if not display_texts and detected_texts: # If only errors or 'no text', show generic message
243
+ display_texts = ["No text recognized"]
244
+ elif not detected_texts and not display_texts: # If detection worked but OCR returned nothing valid
245
+ display_texts = ["No text recognized"]
246
+ elif not detected_texts: # Fallback if processing completely failed
247
+ display_texts = ["Processing Error"]
248
+
249
+
250
+ return render_template('result.html',
251
+ original_image_url=original_url,
252
+ result_image_url=result_url,
253
+ detected_texts=display_texts, # Use filtered list
254
+ error_message=None if result_image_path else "An error occurred during image processing.")
255
+
256
+ except Exception as e:
257
+ error_message = f"An error occurred during file upload or processing: {e}"
258
+ print(f"Error in upload_file route: {e}")
259
+ print(traceback.format_exc())
260
+ # Render index again, show the error
261
+ return render_template('index.html', error_message=error_message)
262
+
263
+ else:
264
+ error_message = "Invalid file type. Please upload a PNG, JPG, or JPEG image."
265
+ return render_template('index.html', error_message=error_message)
266
+
267
+ # For GET requests
268
+ return render_template('index.html', error_message=None)
269
+
270
+
271
+ # --- Run the App ---
272
+ if __name__ == "__main__":
273
+ app.run(debug=True)
274
+
best.pt ADDED
@@ -0,0 +1,3 @@
 
 
 
 
1
+ version https://git-lfs.github.com/spec/v1
2
+ oid sha256:2d95861825bb4184404344c9cf809f40fd31dba785fe54e8ba5b9a3583789822
3
+ size 6248291
easyocr_models/craft_mlt_25k.pth ADDED
@@ -0,0 +1,3 @@
 
 
 
 
1
+ version https://git-lfs.github.com/spec/v1
2
+ oid sha256:4a5efbfb48b4081100544e75e1e2b57f8de3d84f213004b14b85fd4b3748db17
3
+ size 83152330
easyocr_models/english_g2.pth ADDED
@@ -0,0 +1,3 @@
 
 
 
 
1
+ version https://git-lfs.github.com/spec/v1
2
+ oid sha256:e2272681d9d67a04e2dff396b6e95077bc19001f8f6d3593c307b9852e1c29e8
3
+ size 15143997
readme copy.md ADDED
@@ -0,0 +1,60 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ ---
2
+ title: Automatic Number Plate Recognition
3
+ emoji: 🚗
4
+ colorFrom: blue
5
+ colorTo: green
6
+ sdk: docker # Using Docker for more control with OpenCV/PyTorch
7
+ pinned: false
8
+ app_file: app.py # Tells Spaces which file to run
9
+ ---
10
+
11
+ # 🚗 Automatic Number Plate Recognition (ANPR)
12
+
13
+ This **Hugging Face Space** demonstrates an **Automatic Number Plate Recognition (ANPR)** system built using **Flask**, **YOLOv9**, and **EasyOCR**.
14
+
15
+ ---
16
+
17
+ ## 🔍 Features
18
+
19
+ - **Detection:** [YOLOv9](https://github.com/ultralytics/ultralytics) via the Ultralytics library, trained specifically on license plates.
20
+ - **OCR:** [EasyOCR](https://github.com/JaidedAI/EasyOCR) to extract text from detected plates.
21
+ - **Framework:** Flask-based web application for easy upload and visualization.
22
+
23
+ ---
24
+
25
+ ## 🧠 How It Works
26
+
27
+ 1. **Upload an Image:**
28
+ Click **“Choose File”** to select a `.png`, `.jpg`, or `.jpeg` image containing a vehicle.
29
+
30
+ 2. **Process the Image:**
31
+ Click **“Upload and Process”**.
32
+
33
+ 3. **View Results:**
34
+ - Original image
35
+ - Processed image with the detected plate **boxed and labeled**
36
+ - Extracted **plate text** displayed below
37
+
38
+ ---
39
+
40
+ ## ⚙️ Architecture Overview
41
+
42
+ | Component | Technology | Purpose |
43
+ |------------|-------------|----------|
44
+ | **Detection Model** | YOLOv9 | Detects license plate region |
45
+ | **OCR Engine** | EasyOCR | Extracts alphanumeric text |
46
+ | **Framework** | Flask | Provides web interface |
47
+ | **Containerization** | Docker | Ensures reproducible environment |
48
+
49
+ ---
50
+
51
+ ## 📦 Installation & Setup
52
+
53
+ ```bash
54
+ # Clone this repository
55
+ git clone https://huggingface.co/spaces/your-username/anpr
56
+ cd anpr
57
+
58
+ # Build and run using Docker
59
+ docker build -t anpr .
60
+ docker run -p 7860:7860 anpr
requirements.txt ADDED
@@ -0,0 +1,6 @@
 
 
 
 
 
 
 
1
+ Flask>=2.0
2
+ opencv-python-headless
3
+ easyocr
4
+ ultralytics
5
+ torch
6
+ torchvision
templates/index.html ADDED
@@ -0,0 +1,53 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <!DOCTYPE html>
2
+ <html lang="en">
3
+
4
+ <head>
5
+ <meta charset="UTF-8">
6
+ <meta name="viewport" content="width=device-width, initial-scale=1.0">
7
+ <title>ANPR Image Upload</title>
8
+ <!-- Load Tailwind CSS from CDN -->
9
+ <script src="https://www.google.com/search?q=https://cdn.tailwindcss.com"></script>
10
+ <style>
11
+ /* Optional: Add custom styles or use a font like Inter */
12
+ body {
13
+ font-family: 'Inter', sans-serif;
14
+ }
15
+ </style>
16
+ </head>
17
+
18
+ <body class="bg-gray-100 flex items-center justify-center min-h-screen">
19
+ <div class="bg-white p-8 rounded-lg shadow-xl w-full max-w-lg">
20
+ <h1 class="text-3xl font-bold mb-6 text-center text-gray-800">Number Plate Detector</h1>
21
+ <p class="text-center text-gray-600 mb-6">Upload an image containing a vehicle's number plate.</p>
22
+
23
+ <!-- The form sends data to our '/' route using the POST method -->
24
+ <!-- enctype="multipart/form-data" is essential for file uploads -->
25
+ <form method="post" enctype="multipart/form-data" class="space-y-6">
26
+ <div>
27
+ <label for="file" class="block text-sm font-medium text-gray-700 mb-2">Choose an image file:</label>
28
+ <input type="file" name="file" id="file" required accept="image/png, image/jpeg, image/jpg"
29
+ class="block w-full text-sm text-gray-500 rounded-md border border-gray-300 cursor-pointer
30
+ file:mr-4 file:py-2 file:px-4
31
+ file:rounded-md file:border-0
32
+ file:text-sm file:font-semibold
33
+ file:bg-indigo-50 file:text-indigo-700
34
+ hover:file:bg-indigo-100 focus:outline-none focus:ring-2 focus:ring-indigo-500 focus:border-indigo-500">
35
+ <p class="text-xs text-gray-500 mt-1">Accepted formats: PNG, JPG, JPEG.</p>
36
+ </div>
37
+
38
+ <div>
39
+ <button type="submit"
40
+ class="w-full flex justify-center py-2 px-4 border border-transparent rounded-md shadow-sm text-sm font-medium text-white bg-indigo-600 hover:bg-indigo-700 focus:outline-none focus:ring-2 focus:ring-offset-2 focus:ring-indigo-500 transition duration-150 ease-in-out">
41
+ Upload and Detect
42
+ </button>
43
+ </div>
44
+ </form>
45
+
46
+ <!-- We can add messages here later (e.g., upload errors) -->
47
+ {% if error_message %}
48
+ <p class="mt-4 text-center text-red-600">{{ error_message }}</p>
49
+ {% endif %}
50
+ </div>
51
+ </body>
52
+
53
+ </html>
templates/result.html ADDED
@@ -0,0 +1,74 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <!doctype html>
2
+ <html lang="en">
3
+
4
+ <head>
5
+ <meta charset="UTF-8">
6
+ <meta name="viewport" content="width=device-width, initial-scale=1.0">
7
+ <title>ANPR Result</title>
8
+ <script src="https://www.google.com/search?q=https://cdn.tailwindcss.com"></script>
9
+ <link href="https://www.google.com/search?q=https://fonts.googleapis.com/css2%3Ffamily%3DInter:wght%40400%3B500%3B600%3B700%26display%3Dswap" rel="stylesheet">
10
+ <style>
11
+ body {
12
+ font-family: 'Inter', sans-serif;
13
+ }
14
+
15
+ .result-image {
16
+ max-width: 100%;
17
+ height: auto;
18
+ max-height: 70vh;
19
+ margin: 10px auto;
20
+ display: block;
21
+ }
22
+ </style>
23
+ </head>
24
+
25
+ <body class="bg-gray-100 p-8">
26
+ <div class="max-w-4xl mx-auto bg-white p-6 rounded-lg shadow-md">
27
+ <h1 class="text-2xl font-semibold mb-6 text-center text-gray-700">ANPR Processing Result</h1>
28
+
29
+ {% if error_message %}
30
+ <div class="bg-red-100 border border-red-400 text-red-700 px-4 py-3 rounded relative mb-4" role="alert">
31
+ <strong class="font-bold">Error:</strong>
32
+ <span class="block sm:inline">{{ error_message }}</span>
33
+ </div>
34
+ {% else %}
35
+ <div class="grid grid-cols-1 md:grid-cols-2 gap-6 mb-6">
36
+ <div>
37
+ <h2 class="text-xl font-medium mb-2 text-gray-600">Original Image</h2>
38
+ {% if original_image_url %}
39
+ <img src="{{ original_image_url }}" alt="Original Uploaded Image" class="result-image rounded-md border border-gray-300">
40
+ {% else %}
41
+ <p class="text-gray-500">Original image not available.</p>
42
+ {% endif %}
43
+ </div>
44
+ <div>
45
+ <h2 class="text-xl font-medium mb-2 text-gray-600">Processed Image</h2>
46
+ {% if result_image_url %}
47
+ <img src="{{ result_image_url }}" alt="Processed Image with Detections" class="result-image rounded-md border border-gray-300">
48
+ {% else %}
49
+ <p class="text-gray-500">Processed image not available.</p>
50
+ {% endif %}
51
+ </div>
52
+ </div>
53
+
54
+ <div class="mb-6">
55
+ <h2 class="text-xl font-medium mb-2 text-gray-600">Detected Plate Texts:</h2>
56
+ {% if detected_texts %}
57
+ <ul class="list-disc list-inside bg-gray-50 p-4 rounded-md border border-gray-200">
58
+ {% for text in detected_texts %}
59
+ <li class="text-lg text-gray-800 mb-1 font-mono">{{ text }}</li>
60
+ {% endfor %}
61
+ </ul>
62
+ {% else %}
63
+ <p class="text-gray-500 bg-gray-50 p-4 rounded-md border border-gray-200">No license plates detected or text recognized.</p>
64
+ {% endif %}
65
+ </div>
66
+ {% endif %}
67
+
68
+ <div class="text-center mt-6">
69
+ <a href="{{ url_for('upload_file') }}" class="inline-block bg-blue-500 hover:bg-blue-600 text-white font-semibold py-2 px-4 rounded-md transition duration-300">Upload Another Image</a>
70
+ </div>
71
+ </div>
72
+ </body>
73
+
74
+ </html>
test.jpeg ADDED
test2.jpeg ADDED
yolov8n.pt ADDED
@@ -0,0 +1,3 @@
 
 
 
 
1
+ version https://git-lfs.github.com/spec/v1
2
+ oid sha256:31e20dde3def09e2cf938c7be6fe23d9150bbbe503982af13345706515f2ef95
3
+ size 6534387