File size: 7,563 Bytes
11f85a8
 
 
 
 
 
 
 
 
42de794
11f85a8
 
42de794
 
11f85a8
42de794
11f85a8
 
 
 
 
 
42de794
 
 
 
 
 
 
 
11f85a8
42de794
 
 
 
 
 
 
 
 
 
 
 
 
 
 
11f85a8
42de794
 
 
11f85a8
 
42de794
 
11f85a8
 
 
 
 
 
 
42de794
 
11f85a8
 
 
 
 
42de794
 
11f85a8
42de794
11f85a8
42de794
11f85a8
42de794
11f85a8
42de794
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
11f85a8
42de794
11f85a8
42de794
 
11f85a8
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
42de794
11f85a8
 
 
 
 
 
 
 
42de794
11f85a8
42de794
 
11f85a8
 
 
 
 
 
 
42de794
11f85a8
42de794
11f85a8
42de794
 
11f85a8
 
42de794
11f85a8
 
42de794
11f85a8
42de794
 
 
11f85a8
42de794
11f85a8
 
 
 
 
 
42de794
 
 
11f85a8
 
 
42de794
11f85a8
 
42de794
 
11f85a8
 
 
 
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
import os
# این خط باید قبل از هرگونه import از torch یا ultralytics باشد
os.environ['PYTORCH_WEIGHTS_ONLY'] = 'false'

import gradio as gr
from ultralytics import YOLO
import cv2
import numpy as np
from PIL import Image
import random

# --- 1. Load the Model ---
# توجه: نام مدل به 'best.pt' برگردانده شد.
# مطمئن شوید که فایل 'best.pt' در کنار این اسکریپت وجود دارد.
try:
    model = YOLO("best.pt")
except Exception as e:
    print(f"Error loading model: {e}")
    # در صورت عدم وجود مدل، یک مدل پیش‌فرض یولو بارگذاری می‌شود تا برنامه اجرا شود
    model = YOLO("yolov8n.pt")


# --- 2. Define Class Names & Character Map ---
# برای این پروژه، لیست کلاس‌ها تمام کاراکترهای ممکن را پوشش می‌دهد.
class_names = [
    '0', '1', '2', '3', '4', '5', '6', '7', '8', '9',
    'Alef', 'Be', 'Te', 'Se', 'Jim', 'Dal', 'Sin', 'Shin',
    'Sad', 'Ta', 'Za', 'Eyn', 'Ghaf', 'Lam', 'Mim', 'Nun',
    'He', 'Vav', 'Pe', 'Zhe', 'plate', 'Ye', 'Ze'
]

# نقشه برای تبدیل ID کلاس به کاراکتر فارسی برای نمایش در خروجی متنی
char_map = {
    0: '۰', 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: 'ژ', 31: 'ی', 32: 'ز'
    # کلاس 30 (plate) از این نقشه حذف شده زیرا یک کاراکتر نیست.
}

# برای هر کلاس یک رنگ رندوم تعریف می‌کنیم.
class_colors = [[random.randint(0, 255) for _ in range(3)] for _ in range(len(class_names))]


# --- 3. Main Image Processing Function ---
def detect_and_read_plate(input_image: Image.Image):
    """
    This function takes an input image, detects all objects (plate and characters),
    draws bounding boxes, sorts the characters, and returns the processed image
    along with the recognized license plate text.
    """
    if input_image is None:
        return None, ""

    # اجرای مدل روی تصویر ورودی
    results = model(input_image, verbose=False)[0]
    
    # تبدیل تصویر ورودی به فرمت مناسب برای پردازش با OpenCV
    img_np = np.array(input_image.convert("RGB"))
    img_copy = img_np.copy()

    recognized_characters = []

    # بررسی اینکه آیا باکسی تشخیص داده شده است یا خیر
    if results.boxes is not None:
        boxes = results.boxes.data.cpu().numpy()

        for box in boxes:
            x1, y1, x2, y2 = map(int, box[:4])
            confidence = box[4]
            cls_id = int(box[5])

            if cls_id < len(class_names):
                # گرفتن رنگ و لیبل برای رسم کادر
                color = class_colors[cls_id]
                label = f"{class_names[cls_id]} {confidence:.2f}"
                
                # رسم کادر دور هر شیء تشخیص داده شده
                cv2.rectangle(img_copy, (x1, y1), (x2, y2), color, 2)
                cv2.putText(img_copy, label, (x1, y1 - 10), cv2.FONT_HERSHEY_SIMPLEX, 0.6, color, 2)

                # اگر شیء تشخیص داده شده یک کاراکتر بود، آن را برای مرتب‌سازی ذخیره کن
                if cls_id in char_map:
                    recognized_characters.append({'box': box, 'char': char_map[cls_id]})
    
    # مرتب‌سازی کاراکترهای تشخیص داده شده بر اساس موقعیت افقی (x1)
    plate_text = ""
    if recognized_characters:
        sorted_chars = sorted(recognized_characters, key=lambda item: item['box'][0])
        # ساختن رشته نهایی پلاک
        plate_text = " ".join([item['char'] for item in sorted_chars])
    else:
        plate_text = "No characters were detected."

    return Image.fromarray(img_copy), plate_text


# --- 4. Dynamically Read Examples from a Folder (unchanged) ---
# نام فولدر به نامی مرتبط با پروژه تغییر کرده است.
examples_folder = "Persian_License_Plate_Images"
examples_list = []
try:
    all_files_in_folder = os.listdir(examples_folder)
    # فیلتر کردن فایل‌ها برای اطمینان از اینکه فقط تصاویر خوانده می‌شوند
    image_extensions = ['.jpg', '.jpeg', '.png', '.bmp']
    image_files = [f for f in all_files_in_folder if os.path.splitext(f)[1].lower() in image_extensions]
    for filename in image_files:
        full_path = os.path.join(examples_folder, filename)
        examples_list.append(full_path)
except FileNotFoundError:
    print(f"Warning: Folder '{examples_folder}' not found. No examples will be shown.")
except Exception as e:
    print(f"An error occurred while reading examples: {e}")


# ===================================================================
# --- Key Section: Building the Custom Layout with gr.Blocks ---
# ===================================================================

# راهنمای متنی که برای پروژه تشخیص پلاک بازنویسی شده است.
guide_html = """
<hr>
<div style="text-align: center;">
  <h3>🚗 Iranian License Plate Detection & Reading Guide 🇮🇷</h3>
  <p>
    This model detects <strong>Iranian vehicle license plates</strong> and reads the characters on them.
    Upload an image, and the model will draw boxes around the plate and each character, then display the read text below.
  </p>
  <br>
  <p><strong>Note:</strong> For best results, use clear images where the license plate is easily readable.</p>
</div>
"""

# شروع تعریف بلوک‌های رابط کاربری
with gr.Blocks(theme=gr.themes.Soft(primary_hue="blue")) as demo:
    # ۱. عنوان اصلی و توضیحات اولیه
    gr.Markdown("<h1 style='text-align: center;'>Persian License Plate Detection and OCR</h1>")
    gr.Markdown(
        "**Welcome!** Upload a vehicle image or click an example. "
        "The model will draw bounding boxes and output the recognized plate text."
    )
    
    # ۲. چیدمان ورودی و خروجی
    with gr.Row():
        input_image = gr.Image(type="pil", label="Upload Vehicle Image")
        output_image = gr.Image(type="pil", label="Processed Image with Detections")
    
    # کادر متنی برای نمایش پلاک خوانده شده
    recognized_text = gr.Textbox(label="Recognized Plate Text", interactive=False)

    # ۳. دکمه برای ارسال تصویر
    submit_btn = gr.Button("🔍 Detect and Read Plate")

    # ۴. بخش نمونه‌ها
    if examples_list:
        gr.Examples(
            examples=examples_list,
            inputs=input_image,
            # خروجی‌ها شامل تصویر و کادر متنی هستند
            outputs=[output_image, recognized_text],
            fn=detect_and_read_plate,
            cache_examples=True,
        )

    # ۵. متن راهنما
    gr.Markdown(guide_html)
    
    # تعریف عملکرد دکمه: خروجی‌ها به تصویر و کادر متنی ارسال می‌شوند
    submit_btn.click(fn=detect_and_read_plate, inputs=input_image, outputs=[output_image, recognized_text])

# اجرای برنامه
if __name__ == "__main__":
    demo.launch(share=True)