Update app.py
Browse files
app.py
CHANGED
@@ -7,33 +7,51 @@ from ultralytics import YOLO
|
|
7 |
import cv2
|
8 |
import numpy as np
|
9 |
from PIL import Image
|
|
|
10 |
|
11 |
# --- 1. Load the Model ---
|
12 |
-
# توجه: نام مدل به
|
13 |
-
# مطمئن شوید که فایل '
|
14 |
try:
|
15 |
-
model = YOLO("
|
16 |
except Exception as e:
|
17 |
print(f"Error loading model: {e}")
|
18 |
# در صورت عدم وجود مدل، یک مدل پیشفرض یولو بارگذاری میشود تا برنامه اجرا شود
|
19 |
model = YOLO("yolov8n.pt")
|
20 |
|
21 |
|
22 |
-
# --- 2. Define Class Names ---
|
23 |
-
# برای این پروژه،
|
24 |
-
class_names = [
|
25 |
-
|
26 |
-
|
|
|
|
|
|
|
27 |
|
28 |
-
#
|
29 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
30 |
"""
|
31 |
-
This function takes an input image, detects
|
32 |
-
draws
|
|
|
33 |
"""
|
34 |
if input_image is None:
|
35 |
-
return None
|
36 |
-
|
37 |
# اجرای مدل روی تصویر ورودی
|
38 |
results = model(input_image, verbose=False)[0]
|
39 |
|
@@ -41,30 +59,43 @@ def detect_license_plate(input_image: Image.Image):
|
|
41 |
img_np = np.array(input_image.convert("RGB"))
|
42 |
img_copy = img_np.copy()
|
43 |
|
|
|
|
|
44 |
# بررسی اینکه آیا باکسی تشخیص داده شده است یا خیر
|
45 |
if results.boxes is not None:
|
46 |
boxes = results.boxes.data.cpu().numpy()
|
47 |
|
48 |
for box in boxes:
|
|
|
|
|
49 |
cls_id = int(box[5])
|
50 |
-
|
51 |
if cls_id < len(class_names):
|
52 |
-
|
53 |
color = class_colors[cls_id]
|
|
|
54 |
|
55 |
-
#
|
56 |
-
x1, y1, x2, y2
|
57 |
-
|
58 |
-
|
59 |
-
|
60 |
-
|
61 |
-
|
62 |
-
|
63 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
64 |
|
65 |
-
return Image.fromarray(img_copy)
|
66 |
|
67 |
-
|
|
|
68 |
# نام فولدر به نامی مرتبط با پروژه تغییر کرده است.
|
69 |
examples_folder = "Persian_License_Plate_Images"
|
70 |
examples_list = []
|
@@ -81,6 +112,7 @@ except FileNotFoundError:
|
|
81 |
except Exception as e:
|
82 |
print(f"An error occurred while reading examples: {e}")
|
83 |
|
|
|
84 |
# ===================================================================
|
85 |
# --- Key Section: Building the Custom Layout with gr.Blocks ---
|
86 |
# ===================================================================
|
@@ -89,56 +121,52 @@ except Exception as e:
|
|
89 |
guide_html = """
|
90 |
<hr>
|
91 |
<div style="text-align: center;">
|
92 |
-
<h3>🚗 Iranian License Plate Detection Guide 🇮🇷</h3>
|
93 |
<p>
|
94 |
-
This model
|
95 |
-
Upload
|
96 |
</p>
|
97 |
-
<div style="display: flex; justify-content: center; align-items: center; gap: 20px; margin-top: 15px;">
|
98 |
-
<div style="padding: 10px; border: 1px solid #4CAF50; border-radius: 8px; background-color: #f2fff2;">
|
99 |
-
<strong style="color: #005704;">🟩 License Plate</strong><br>
|
100 |
-
<em>
|
101 |
-
The green bounding box indicates the detected location of the license plate, along with the model's confidence score.
|
102 |
-
</em>
|
103 |
-
</div>
|
104 |
-
</div>
|
105 |
<br>
|
106 |
<p><strong>Note:</strong> For best results, use clear images where the license plate is easily readable.</p>
|
107 |
</div>
|
108 |
"""
|
109 |
|
110 |
# شروع تعریف بلوکهای رابط کاربری
|
111 |
-
with gr.Blocks(theme=gr.themes.Soft(primary_hue="
|
112 |
# ۱. عنوان اصلی و توضیحات اولیه
|
113 |
-
gr.Markdown("<h1 style='text-align: center;'>Persian License Plate Detection</h1>")
|
114 |
gr.Markdown(
|
115 |
-
"**Welcome!** Upload
|
116 |
-
"The model will draw
|
117 |
)
|
118 |
|
119 |
-
# ۲. چیدمان ورودی و خروجی
|
120 |
with gr.Row():
|
121 |
input_image = gr.Image(type="pil", label="Upload Vehicle Image")
|
122 |
-
output_image = gr.Image(type="pil", label="Processed Image with
|
123 |
|
|
|
|
|
|
|
124 |
# ۳. دکمه برای ارسال تصویر
|
125 |
-
submit_btn = gr.Button("🔍 Detect
|
126 |
|
127 |
# ۴. بخش نمونهها
|
128 |
if examples_list:
|
129 |
gr.Examples(
|
130 |
examples=examples_list,
|
131 |
inputs=input_image,
|
132 |
-
|
133 |
-
|
|
|
134 |
cache_examples=True,
|
135 |
)
|
136 |
|
137 |
-
# ۵. متن
|
138 |
gr.Markdown(guide_html)
|
139 |
|
140 |
-
# تعریف عملکرد دکمه:
|
141 |
-
submit_btn.click(fn=
|
142 |
|
143 |
# اجرای برنامه
|
144 |
if __name__ == "__main__":
|
|
|
7 |
import cv2
|
8 |
import numpy as np
|
9 |
from PIL import Image
|
10 |
+
import random
|
11 |
|
12 |
# --- 1. Load the Model ---
|
13 |
+
# توجه: نام مدل به 'best.pt' برگردانده شد.
|
14 |
+
# مطمئن شوید که فایل 'best.pt' در کنار این اسکریپت وجود دارد.
|
15 |
try:
|
16 |
+
model = YOLO("best.pt")
|
17 |
except Exception as e:
|
18 |
print(f"Error loading model: {e}")
|
19 |
# در صورت عدم وجود مدل، یک مدل پیشفرض یولو بارگذاری میشود تا برنامه اجرا شود
|
20 |
model = YOLO("yolov8n.pt")
|
21 |
|
22 |
|
23 |
+
# --- 2. Define Class Names & Character Map ---
|
24 |
+
# برای این پروژه، لیست کلاسها تمام کاراکترهای ممکن را پوشش میدهد.
|
25 |
+
class_names = [
|
26 |
+
'0', '1', '2', '3', '4', '5', '6', '7', '8', '9',
|
27 |
+
'Alef', 'Be', 'Te', 'Se', 'Jim', 'Dal', 'Sin', 'Shin',
|
28 |
+
'Sad', 'Ta', 'Za', 'Eyn', 'Ghaf', 'Lam', 'Mim', 'Nun',
|
29 |
+
'He', 'Vav', 'Pe', 'Zhe', 'plate', 'Ye', 'Ze'
|
30 |
+
]
|
31 |
|
32 |
+
# نقشه برای تبدیل ID کلاس به کاراکتر فارسی برای نمایش در خروجی متنی
|
33 |
+
char_map = {
|
34 |
+
0: '۰', 1: '۱', 2: '۲', 3: '۳', 4: '۴', 5: '۵', 6: '۶', 7: '۷', 8: '۸', 9: '۹',
|
35 |
+
10: 'الف', 11: 'ب', 12: 'ت', 13: 'ث', 14: 'ج', 15: 'د', 16: 'س', 17: 'ش',
|
36 |
+
18: 'ص', 19: 'ط', 20: 'ظ', 21: 'ع', 22: 'ق', 23: 'ل', 24: 'م', 25: 'ن',
|
37 |
+
26: 'ه', 27: 'و', 28: 'پ', 29: 'ژ', 31: 'ی', 32: 'ز'
|
38 |
+
# کلاس 30 (plate) از این نقشه حذف شده زیرا یک کاراکتر نیست.
|
39 |
+
}
|
40 |
+
|
41 |
+
# برای هر کلاس یک رنگ رندوم تعریف میکنیم.
|
42 |
+
class_colors = [[random.randint(0, 255) for _ in range(3)] for _ in range(len(class_names))]
|
43 |
+
|
44 |
+
|
45 |
+
# --- 3. Main Image Processing Function ---
|
46 |
+
def detect_and_read_plate(input_image: Image.Image):
|
47 |
"""
|
48 |
+
This function takes an input image, detects all objects (plate and characters),
|
49 |
+
draws bounding boxes, sorts the characters, and returns the processed image
|
50 |
+
along with the recognized license plate text.
|
51 |
"""
|
52 |
if input_image is None:
|
53 |
+
return None, ""
|
54 |
+
|
55 |
# اجرای مدل روی تصویر ورودی
|
56 |
results = model(input_image, verbose=False)[0]
|
57 |
|
|
|
59 |
img_np = np.array(input_image.convert("RGB"))
|
60 |
img_copy = img_np.copy()
|
61 |
|
62 |
+
recognized_characters = []
|
63 |
+
|
64 |
# بررسی اینکه آیا باکسی تشخیص داده شده است یا خیر
|
65 |
if results.boxes is not None:
|
66 |
boxes = results.boxes.data.cpu().numpy()
|
67 |
|
68 |
for box in boxes:
|
69 |
+
x1, y1, x2, y2 = map(int, box[:4])
|
70 |
+
confidence = box[4]
|
71 |
cls_id = int(box[5])
|
72 |
+
|
73 |
if cls_id < len(class_names):
|
74 |
+
# گرفتن رنگ و لیبل برای رسم کادر
|
75 |
color = class_colors[cls_id]
|
76 |
+
label = f"{class_names[cls_id]} {confidence:.2f}"
|
77 |
|
78 |
+
# رسم کادر دور هر شیء تشخیص داده شده
|
79 |
+
cv2.rectangle(img_copy, (x1, y1), (x2, y2), color, 2)
|
80 |
+
cv2.putText(img_copy, label, (x1, y1 - 10), cv2.FONT_HERSHEY_SIMPLEX, 0.6, color, 2)
|
81 |
+
|
82 |
+
# اگر شیء تشخیص داده شده یک کاراکتر بود، آن را برای مرتبسازی ذخیره کن
|
83 |
+
if cls_id in char_map:
|
84 |
+
recognized_characters.append({'box': box, 'char': char_map[cls_id]})
|
85 |
+
|
86 |
+
# مرتبسازی کاراکترهای تشخیص داده شده بر اساس موقعیت افقی (x1)
|
87 |
+
plate_text = ""
|
88 |
+
if recognized_characters:
|
89 |
+
sorted_chars = sorted(recognized_characters, key=lambda item: item['box'][0])
|
90 |
+
# ساختن رشته نهایی پلاک
|
91 |
+
plate_text = " ".join([item['char'] for item in sorted_chars])
|
92 |
+
else:
|
93 |
+
plate_text = "No characters were detected."
|
94 |
|
95 |
+
return Image.fromarray(img_copy), plate_text
|
96 |
|
97 |
+
|
98 |
+
# --- 4. Dynamically Read Examples from a Folder (unchanged) ---
|
99 |
# نام فولدر به نامی مرتبط با پروژه تغییر کرده است.
|
100 |
examples_folder = "Persian_License_Plate_Images"
|
101 |
examples_list = []
|
|
|
112 |
except Exception as e:
|
113 |
print(f"An error occurred while reading examples: {e}")
|
114 |
|
115 |
+
|
116 |
# ===================================================================
|
117 |
# --- Key Section: Building the Custom Layout with gr.Blocks ---
|
118 |
# ===================================================================
|
|
|
121 |
guide_html = """
|
122 |
<hr>
|
123 |
<div style="text-align: center;">
|
124 |
+
<h3>🚗 Iranian License Plate Detection & Reading Guide 🇮🇷</h3>
|
125 |
<p>
|
126 |
+
This model detects <strong>Iranian vehicle license plates</strong> and reads the characters on them.
|
127 |
+
Upload an image, and the model will draw boxes around the plate and each character, then display the read text below.
|
128 |
</p>
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
129 |
<br>
|
130 |
<p><strong>Note:</strong> For best results, use clear images where the license plate is easily readable.</p>
|
131 |
</div>
|
132 |
"""
|
133 |
|
134 |
# شروع تعریف بلوکهای رابط کاربری
|
135 |
+
with gr.Blocks(theme=gr.themes.Soft(primary_hue="blue")) as demo:
|
136 |
# ۱. عنوان اصلی و توضیحات اولیه
|
137 |
+
gr.Markdown("<h1 style='text-align: center;'>Persian License Plate Detection and OCR</h1>")
|
138 |
gr.Markdown(
|
139 |
+
"**Welcome!** Upload a vehicle image or click an example. "
|
140 |
+
"The model will draw bounding boxes and output the recognized plate text."
|
141 |
)
|
142 |
|
143 |
+
# ۲. چیدمان ورودی و خروجی
|
144 |
with gr.Row():
|
145 |
input_image = gr.Image(type="pil", label="Upload Vehicle Image")
|
146 |
+
output_image = gr.Image(type="pil", label="Processed Image with Detections")
|
147 |
|
148 |
+
# کادر متنی برای نمایش پلاک خوانده شده
|
149 |
+
recognized_text = gr.Textbox(label="Recognized Plate Text", interactive=False)
|
150 |
+
|
151 |
# ۳. دکمه برای ارسال تصویر
|
152 |
+
submit_btn = gr.Button("🔍 Detect and Read Plate")
|
153 |
|
154 |
# ۴. بخش نمونهها
|
155 |
if examples_list:
|
156 |
gr.Examples(
|
157 |
examples=examples_list,
|
158 |
inputs=input_image,
|
159 |
+
# خروجیها شامل تصویر و کادر متنی هستند
|
160 |
+
outputs=[output_image, recognized_text],
|
161 |
+
fn=detect_and_read_plate,
|
162 |
cache_examples=True,
|
163 |
)
|
164 |
|
165 |
+
# ۵. متن راهنما
|
166 |
gr.Markdown(guide_html)
|
167 |
|
168 |
+
# تعریف عملکرد دکمه: خروجیها به تصویر و کادر متنی ارسال میشوند
|
169 |
+
submit_btn.click(fn=detect_and_read_plate, inputs=input_image, outputs=[output_image, recognized_text])
|
170 |
|
171 |
# اجرای برنامه
|
172 |
if __name__ == "__main__":
|