Sompote commited on
Commit
49d297d
·
verified ·
1 Parent(s): e576fa4

Upload 4 files

Browse files
Files changed (4) hide show
  1. README.md +59 -14
  2. app.py +201 -0
  3. best.pt +3 -0
  4. requirements.txt +8 -0
README.md CHANGED
@@ -1,14 +1,59 @@
1
- ---
2
- title: License Plate
3
- emoji: 😻
4
- colorFrom: pink
5
- colorTo: green
6
- sdk: streamlit
7
- sdk_version: 1.41.1
8
- app_file: app.py
9
- pinned: false
10
- license: mit
11
- short_description: license-late
12
- ---
13
-
14
- Check out the configuration reference at https://huggingface.co/docs/hub/spaces-config-reference
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ # Thai License Plate Detection App 🚗
2
+
3
+ This Streamlit application detects and recognizes Thai license plates and provinces from images. It uses YOLOv8 for object detection and TrOCR for text recognition.
4
+
5
+ ## Features
6
+
7
+ - 📷 Upload images containing Thai license plates
8
+ - 🔍 Detect and extract license plate numbers
9
+ - 🏠 Recognize and match province names
10
+ - 🖼️ Display cropped regions of detected plates and provinces
11
+ - 🎯 High accuracy text recognition using TrOCR
12
+
13
+ ## How to Use
14
+
15
+ 1. Upload an image containing a Thai license plate using the file uploader
16
+ 2. Wait for the processing to complete
17
+ 3. View the results:
18
+ - Detected license plate number
19
+ - Cropped license plate image
20
+ - Detected province name
21
+ - Cropped province image
22
+
23
+ ## Technical Details
24
+
25
+ The application uses:
26
+ - YOLOv8 for license plate and province detection
27
+ - TrOCR (Thai) for text recognition
28
+ - OpenCV for image preprocessing
29
+ - Levenshtein distance for province name matching
30
+
31
+ ## Models
32
+
33
+ - Object Detection: YOLOv8 (custom trained for Thai license plates)
34
+ - Text Recognition: openthaigpt/thai-trocr
35
+
36
+ ## Deployment
37
+
38
+ This app is deployed on Hugging Face Spaces. The deployment includes:
39
+ - Streamlit web interface
40
+ - Pre-trained YOLO model weights
41
+ - Required Python dependencies
42
+
43
+ ## Requirements
44
+
45
+ All required packages are listed in `requirements.txt`. The main dependencies are:
46
+ - streamlit
47
+ - opencv-python-headless
48
+ - transformers
49
+ - ultralytics
50
+ - torch
51
+ - python-Levenshtein
52
+
53
+ ## License
54
+
55
+ [Your chosen license]
56
+
57
+ ## Credits
58
+
59
+ Created by [Your Name/Organization]
app.py ADDED
@@ -0,0 +1,201 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import streamlit as st
2
+ import os
3
+ import numpy as np
4
+ import cv2
5
+ from PIL import Image
6
+ from transformers import TrOCRProcessor, VisionEncoderDecoderModel
7
+ from ultralytics import YOLO
8
+ import Levenshtein
9
+
10
+ # Page config
11
+ st.set_page_config(
12
+ page_title="Thai License Plate Detection",
13
+ page_icon="🚗",
14
+ layout="centered"
15
+ )
16
+
17
+ # Initialize session state for models
18
+ if 'models_loaded' not in st.session_state:
19
+ st.session_state['models_loaded'] = False
20
+
21
+ # Load models
22
+ @st.cache_resource
23
+ def load_models():
24
+ processor = TrOCRProcessor.from_pretrained('openthaigpt/thai-trocr')
25
+ ocr_model = VisionEncoderDecoderModel.from_pretrained('openthaigpt/thai-trocr')
26
+ yolo_model = YOLO('best.pt') # Make sure to include this in the repository
27
+ return processor, ocr_model, yolo_model
28
+
29
+ # Thai provinces list
30
+ thai_provinces = [
31
+ "กรุงเทพมหานคร", "กระบี่", "กาญจนบุรี", "กาฬสินธุ์", "กำแพงเพชร", "ขอนแก่น", "จันทบุรี", "ฉะเชิงเทรา",
32
+ "ชลบุรี", "ชัยนาท", "ชัยภูมิ", "ชุมพร", "เชียงราย", "เชียงใหม่", "ตรัง", "ตราด", "ตาก", "นครนายก",
33
+ "นครปฐม", "นครพนม", "นครราชสีมา", "นครศรีธรรมราช", "นครสวรรค์", "นราธิวาส", "น่าน", "บึงกาฬ",
34
+ "บุรีรัมย์", "ปทุมธานี", "ประจวบคีรีขันธ์", "ปราจีนบุรี", "ปัตตานี", "พะเยา", "พังงา", "พัทลุง",
35
+ "พิจิตร", "พิษณุโลก", "เพชรบูรณ์", "เพชรบุรี", "แพร่", "ภูเก็ต", "มหาสารคาม", "มุกดาหาร", "แม่ฮ่องสอน",
36
+ "ยโสธร", "ยะลา", "ร้อยเอ็ด", "ระนอง", "ระยอง", "ราชบุรี", "ลพบุรี", "ลำปาง", "ลำพูน", "เลย",
37
+ "ศรีสะเกษ", "สกลนคร", "สงขลา", "สมุทรปราการ", "สมุทรสงคราม", "สมุทรสาคร", "สระแก้ว", "สระบุรี",
38
+ "สิงห์บุรี", "สุโขทัย", "สุพรรณบุรี", "สุราษฎร์ธานี", "สุรินทร์", "หนองคาย", "หนองบัวลำภู", "อำนาจเจริญ",
39
+ "อุดรธานี", "อุทัยธานี", "อุบลราชธานี", "อ่างทอง"
40
+ ]
41
+
42
+ def get_closest_province(input_text, provinces):
43
+ min_distance = float('inf')
44
+ closest_province = None
45
+ for province in provinces:
46
+ distance = Levenshtein.distance(input_text, province)
47
+ if distance < min_distance:
48
+ min_distance = distance
49
+ closest_province = province
50
+ return closest_province, min_distance
51
+
52
+ def process_image(image, processor, ocr_model, yolo_model):
53
+ CONF_THRESHOLD = 0.2
54
+ data = {"plate_number": "", "province": "", "raw_province": "", "plate_crop": None, "province_crop": None}
55
+
56
+ # Convert PIL Image to cv2 format
57
+ image = np.array(image)
58
+ image = cv2.cvtColor(image, cv2.COLOR_RGB2BGR)
59
+
60
+ # Image enhancement
61
+ lab = cv2.cvtColor(image, cv2.COLOR_BGR2LAB)
62
+ l, a, b = cv2.split(lab)
63
+ clahe = cv2.createCLAHE(clipLimit=3.0, tileGridSize=(8,8))
64
+ cl = clahe.apply(l)
65
+ enhanced = cv2.merge((cl,a,b))
66
+ image = cv2.cvtColor(enhanced, cv2.COLOR_LAB2BGR)
67
+
68
+ # YOLO detection
69
+ results = yolo_model(image)
70
+
71
+ # Process detections
72
+ detections = []
73
+ for result in results:
74
+ for box in result.boxes:
75
+ confidence = float(box.conf)
76
+ class_id = int(box.cls.item())
77
+ if confidence < CONF_THRESHOLD:
78
+ continue
79
+ x1, y1, x2, y2 = map(int, box.xyxy.flatten())
80
+ detections.append((class_id, confidence, (x1, y1, x2, y2)))
81
+
82
+ # Sort by class_id
83
+ detections.sort(key=lambda x: x[0])
84
+
85
+ for class_id, confidence, (x1, y1, x2, y2) in detections:
86
+ cropped_image = image[y1:y2, x1:x2]
87
+ if cropped_image.size == 0:
88
+ continue
89
+
90
+ # Preprocess for OCR
91
+ cropped_image_gray = cv2.cvtColor(cropped_image, cv2.COLOR_BGR2GRAY)
92
+ thresh_image = cv2.adaptiveThreshold(
93
+ cropped_image_gray,
94
+ 255,
95
+ cv2.ADAPTIVE_THRESH_GAUSSIAN_C,
96
+ cv2.THRESH_BINARY_INV,
97
+ 11,
98
+ 2
99
+ )
100
+
101
+ kernel = cv2.getStructuringElement(cv2.MORPH_RECT, (2,2))
102
+ thresh_image = cv2.morphologyEx(thresh_image, cv2.MORPH_CLOSE, kernel)
103
+ cropped_image_3d = cv2.cvtColor(thresh_image, cv2.COLOR_GRAY2RGB)
104
+ resized_image = cv2.resize(cropped_image_3d, (128, 32))
105
+
106
+ # OCR processing
107
+ pixel_values = processor(resized_image, return_tensors="pt").pixel_values
108
+ generated_ids = ocr_model.generate(pixel_values)
109
+ generated_text = processor.batch_decode(generated_ids, skip_special_tokens=True)[0]
110
+
111
+ # Convert crop to PIL for display
112
+ cropped_pil = Image.fromarray(cv2.cvtColor(cropped_image, cv2.COLOR_BGR2RGB))
113
+
114
+ if class_id == 0: # License plate
115
+ data["plate_number"] = generated_text
116
+ data["plate_crop"] = cropped_pil
117
+ elif class_id == 1: # Province
118
+ generated_province, distance = get_closest_province(generated_text, thai_provinces)
119
+ data["raw_province"] = generated_text
120
+ data["province"] = generated_province
121
+ data["province_crop"] = cropped_pil
122
+
123
+ return data
124
+
125
+ # Main app
126
+ st.title("Thai License Plate Detection 🚗")
127
+
128
+ # Load models
129
+ try:
130
+ if not st.session_state['models_loaded']:
131
+ with st.spinner("Loading models... (this may take a minute)"):
132
+ processor, ocr_model, yolo_model = load_models()
133
+ st.session_state['models_loaded'] = True
134
+ st.session_state['processor'] = processor
135
+ st.session_state['ocr_model'] = ocr_model
136
+ st.session_state['yolo_model'] = yolo_model
137
+ except Exception as e:
138
+ st.error(f"Error loading models: {str(e)}")
139
+ st.stop()
140
+
141
+ # File uploader
142
+ uploaded_file = st.file_uploader("Upload an image of a Thai license plate", type=["jpg", "jpeg", "png"])
143
+
144
+ if uploaded_file is not None:
145
+ try:
146
+ # Display the uploaded image
147
+ col1, col2 = st.columns(2)
148
+ with col1:
149
+ st.subheader("Uploaded Image")
150
+ image = Image.open(uploaded_file)
151
+ st.image(image, use_column_width=True)
152
+
153
+ # Process the image
154
+ with col2:
155
+ st.subheader("Detection Results")
156
+ with st.spinner("Processing image..."):
157
+ results = process_image(
158
+ image,
159
+ st.session_state['processor'],
160
+ st.session_state['ocr_model'],
161
+ st.session_state['yolo_model']
162
+ )
163
+
164
+ if results["plate_number"]:
165
+ st.success("Detection successful!")
166
+ st.write("📝 License Plate:", results['plate_number'])
167
+
168
+ if results['plate_crop'] is not None:
169
+ st.subheader("Cropped License Plate")
170
+ st.image(results['plate_crop'], caption="Detected License Plate Region")
171
+
172
+ if results['raw_province']:
173
+ st.write("🔍 Detected Province Text:", results['raw_province'])
174
+ if results['province']:
175
+ st.write("🏠 Matched Province:", results['province'])
176
+ else:
177
+ st.write("⚠️ No close province match found")
178
+
179
+ if results['province_crop'] is not None:
180
+ st.subheader("Cropped Province")
181
+ st.image(results['province_crop'], caption="Detected Province Region")
182
+ else:
183
+ st.write("⚠️ No province text detected")
184
+ else:
185
+ st.error("No license plate detected in the image.")
186
+
187
+ except Exception as e:
188
+ st.error(f"An error occurred: {str(e)}")
189
+
190
+ st.markdown("---")
191
+ st.markdown("### Instructions")
192
+ st.markdown("""
193
+ 1. Upload an image containing a Thai license plate
194
+ 2. Wait for the processing to complete
195
+ 3. View the detected license plate number and province
196
+ """)
197
+
198
+ # Add footer with GitHub link
199
+ st.markdown("---")
200
+ st.markdown("Made with ❤️ by [Your Name/Organization]")
201
+ st.markdown("Check out the [GitHub Repository](https://github.com/yourusername/your-repo) for more information")
best.pt ADDED
@@ -0,0 +1,3 @@
 
 
 
 
1
+ version https://git-lfs.github.com/spec/v1
2
+ oid sha256:3b1da8d9362a1005aa5b060b0ac53b4622677e753eded2893da10b6a69bc9fb7
3
+ size 5468691
requirements.txt ADDED
@@ -0,0 +1,8 @@
 
 
 
 
 
 
 
 
 
1
+ streamlit==1.29.0
2
+ opencv-python-headless==4.8.1.78
3
+ numpy==1.26.2
4
+ Pillow==10.1.0
5
+ transformers==4.36.2
6
+ torch==2.1.2
7
+ ultralytics==8.0.227
8
+ python-Levenshtein==0.23.0