Spaces:
Running
Running
Upload 6 files
Browse files- README.md +78 -6
- app.py +260 -0
- config/data.yaml +105 -0
- models/best.pt +3 -0
- models/read_char.pt +3 -0
- requirements.txt +10 -0
README.md
CHANGED
@@ -1,13 +1,85 @@
|
|
|
|
1 |
---
|
2 |
-
title: License Plate
|
3 |
-
emoji:
|
4 |
-
colorFrom:
|
5 |
-
colorTo:
|
6 |
sdk: streamlit
|
7 |
-
sdk_version: 1.
|
8 |
app_file: app.py
|
9 |
pinned: false
|
10 |
-
short_description: License plate detection
|
11 |
---
|
12 |
|
13 |
Check out the configuration reference at https://huggingface.co/docs/hub/spaces-config-reference
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
|
2 |
---
|
3 |
+
title: Thai License Plate Detection V1.2
|
4 |
+
emoji: 🚗
|
5 |
+
colorFrom: blue
|
6 |
+
colorTo: green
|
7 |
sdk: streamlit
|
8 |
+
sdk_version: "1.29.0"
|
9 |
app_file: app.py
|
10 |
pinned: false
|
|
|
11 |
---
|
12 |
|
13 |
Check out the configuration reference at https://huggingface.co/docs/hub/spaces-config-reference
|
14 |
+
|
15 |
+
# Thai License Plate Recognition App
|
16 |
+
|
17 |
+
This Streamlit application performs Thai license plate detection and recognition using deep learning models.
|
18 |
+
|
19 |
+
## Features
|
20 |
+
|
21 |
+
- License plate detection using YOLO
|
22 |
+
- Character recognition for Thai license plates
|
23 |
+
- Province name detection and recognition
|
24 |
+
- Real-time processing with visual feedback
|
25 |
+
- User-friendly web interface
|
26 |
+
|
27 |
+
## Setup
|
28 |
+
|
29 |
+
1. Install the required dependencies:
|
30 |
+
```bash
|
31 |
+
pip install -r requirements.txt
|
32 |
+
```
|
33 |
+
|
34 |
+
2. Download the required model files and place them in the `models` directory:
|
35 |
+
- `best.pt` - YOLO model for license plate detection
|
36 |
+
- `read_char.pt` - YOLO model for character recognition
|
37 |
+
|
38 |
+
3. Ensure the configuration file `config/data.yaml` is present with character mappings.
|
39 |
+
|
40 |
+
## Running the Application
|
41 |
+
|
42 |
+
To run the application:
|
43 |
+
|
44 |
+
```bash
|
45 |
+
streamlit run app.py
|
46 |
+
```
|
47 |
+
|
48 |
+
The application will be available at `http://localhost:8501` by default.
|
49 |
+
|
50 |
+
## Usage
|
51 |
+
|
52 |
+
1. Open the application in your web browser
|
53 |
+
2. Click the "Choose an image..." button to upload an image containing a Thai license plate
|
54 |
+
3. Wait for the processing to complete
|
55 |
+
4. View the results:
|
56 |
+
- Detected license plate number
|
57 |
+
- Detected province name
|
58 |
+
- Visual detection results
|
59 |
+
|
60 |
+
## Models
|
61 |
+
|
62 |
+
The application uses three main models:
|
63 |
+
1. YOLO model for license plate detection
|
64 |
+
2. YOLO model for character recognition
|
65 |
+
3. TrOCR model for province text recognition (automatically downloaded)
|
66 |
+
|
67 |
+
## Directory Structure
|
68 |
+
|
69 |
+
```
|
70 |
+
streamlitapp/
|
71 |
+
├── app.py
|
72 |
+
├── requirements.txt
|
73 |
+
├── README.md
|
74 |
+
├── models/
|
75 |
+
│ ├── best.pt
|
76 |
+
│ └── read_char.pt
|
77 |
+
└── config/
|
78 |
+
└── data.yaml
|
79 |
+
```
|
80 |
+
|
81 |
+
## Notes
|
82 |
+
|
83 |
+
- The application requires an internet connection for the first run to download the TrOCR model
|
84 |
+
- Supported image formats: JPG, JPEG, PNG
|
85 |
+
- For optimal results, ensure the license plate is clearly visible in the image
|
app.py
ADDED
@@ -0,0 +1,260 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
import streamlit as st
|
2 |
+
from PIL import Image
|
3 |
+
import cv2
|
4 |
+
import numpy as np
|
5 |
+
from transformers import TrOCRProcessor, VisionEncoderDecoderModel
|
6 |
+
from ultralytics import YOLO
|
7 |
+
import Levenshtein
|
8 |
+
import yaml
|
9 |
+
import os
|
10 |
+
import io
|
11 |
+
import tempfile
|
12 |
+
import torchvision
|
13 |
+
|
14 |
+
|
15 |
+
class LicensePlateProcessor:
|
16 |
+
def __init__(self):
|
17 |
+
# Load models for plate detection
|
18 |
+
self.yolo_detector = YOLO('models/best.pt') # For plate detection
|
19 |
+
self.char_reader = YOLO('models/read_char.pt') # For character reading
|
20 |
+
|
21 |
+
# Load TrOCR for province detection
|
22 |
+
self.processor_plate = TrOCRProcessor.from_pretrained('openthaigpt/thai-trocr')
|
23 |
+
self.model_plate = VisionEncoderDecoderModel.from_pretrained('openthaigpt/thai-trocr')
|
24 |
+
|
25 |
+
# Load character mapping from yaml
|
26 |
+
with open('config/data.yaml', 'r', encoding='utf-8') as f:
|
27 |
+
data_config = yaml.safe_load(f)
|
28 |
+
self.char_mapping = data_config.get('char_mapping', {})
|
29 |
+
self.names = data_config['names']
|
30 |
+
|
31 |
+
# Load province list
|
32 |
+
self.thai_provinces = [
|
33 |
+
"กรุงเทพมหานคร", "กระบี่", "กาญจนบุรี", "กาฬสินธุ์", "กำแพงเพชร", "ขอนแก่น",
|
34 |
+
"จันทบุรี", "ฉะเชิงเทรา", "ชลบุรี", "ชัยนาท", "ชัยภูมิ", "ชุมพร", "เชียงราย",
|
35 |
+
"เชียงใหม่", "ตรัง", "ตราด", "ตาก", "นครนายก", "นครปฐม", "นครพนม", "นครราชสีมา",
|
36 |
+
"นครศรีธรรมราช", "นครสวรรค์", "นราธิวาส", "น่าน", "บึงกาฬ", "บุรีรัมย์", "ปทุมธานี",
|
37 |
+
"ประจวบคีรีขันธ์", "ปราจีนบุรี", "ปัตตานี", "พะเยา", "พังงา", "พัทลุง", "พิจิตร",
|
38 |
+
"พิษณุโลก", "เพชรบูรณ์", "เพชรบุรี", "แพร่", "ภูเก็ต", "มหาสารคาม", "มุกดาหาร",
|
39 |
+
"แม่ฮ่องสอน", "ยโสธร", "ยะลา", "ร้อยเอ็ด", "ระนอง", "ระยอง", "ราชบุรี", "ลพบุรี",
|
40 |
+
"ลำปาง", "ลำพูน", "เลย", "ศรีสะเกษ", "สกลนคร", "สงขลา", "สมุทรปราการ", "สมุทรสงคราม",
|
41 |
+
"สมุทรสาคร", "สระแก้ว", "สระบุรี", "สิงห์บุรี", "สุโขทัย", "สุพรรณบุรี", "สุราษฎร์ธานี",
|
42 |
+
"สุรินทร์", "หนองคาย", "หนองบัวลำภู", "อำนาจเจริญ", "อุดรธานี", "อุทัยธานี",
|
43 |
+
"อุบลราชธานี", "อ่างทอง"
|
44 |
+
]
|
45 |
+
|
46 |
+
self.CONF_THRESHOLD = 0.3
|
47 |
+
|
48 |
+
def _map_class_to_char(self, class_name):
|
49 |
+
"""Map class to character using yaml mapping"""
|
50 |
+
if str(class_name) in self.char_mapping:
|
51 |
+
return self.char_mapping[str(class_name)]
|
52 |
+
return str(class_name)
|
53 |
+
|
54 |
+
def get_closest_province(self, input_text):
|
55 |
+
"""Find closest matching province"""
|
56 |
+
min_distance = float('inf')
|
57 |
+
closest_province = None
|
58 |
+
|
59 |
+
for province in self.thai_provinces:
|
60 |
+
distance = Levenshtein.distance(input_text, province)
|
61 |
+
if distance < min_distance:
|
62 |
+
min_distance = distance
|
63 |
+
closest_province = province
|
64 |
+
|
65 |
+
return closest_province, min_distance
|
66 |
+
|
67 |
+
def read_plate_characters(self, plate_image):
|
68 |
+
"""Read characters from plate image"""
|
69 |
+
results = self.char_reader.predict(plate_image, conf=0.3)
|
70 |
+
|
71 |
+
detections = []
|
72 |
+
for r in results:
|
73 |
+
boxes = r.boxes
|
74 |
+
for box in boxes:
|
75 |
+
x1, y1, x2, y2 = map(int, box.xyxy[0])
|
76 |
+
confidence = float(box.conf[0])
|
77 |
+
class_id = int(box.cls[0])
|
78 |
+
|
79 |
+
mapped_char = self._map_class_to_char(self.names[class_id])
|
80 |
+
|
81 |
+
detections.append({
|
82 |
+
'char': mapped_char,
|
83 |
+
'confidence': confidence,
|
84 |
+
'bbox': (x1, y1, x2, y2)
|
85 |
+
})
|
86 |
+
|
87 |
+
# Sort detections left to right
|
88 |
+
detections.sort(key=lambda x: x['bbox'][0])
|
89 |
+
|
90 |
+
# Combine characters
|
91 |
+
plate_text = ''.join(det['char'] for det in detections)
|
92 |
+
return plate_text
|
93 |
+
|
94 |
+
def process_image(self, image_path: str):
|
95 |
+
try:
|
96 |
+
# Read image
|
97 |
+
image = cv2.imread(image_path)
|
98 |
+
if image is None:
|
99 |
+
print(f"Error: Could not read image from {image_path}")
|
100 |
+
return None
|
101 |
+
|
102 |
+
# Detect license plate location
|
103 |
+
results = self.yolo_detector(image)
|
104 |
+
|
105 |
+
data = {"plate_number": "", "province": "", "raw_province": ""}
|
106 |
+
|
107 |
+
# Save visualization
|
108 |
+
output_image = image.copy()
|
109 |
+
|
110 |
+
for result in results:
|
111 |
+
for box in result.boxes:
|
112 |
+
confidence = float(box.conf)
|
113 |
+
if confidence < self.CONF_THRESHOLD:
|
114 |
+
continue
|
115 |
+
|
116 |
+
x1, y1, x2, y2 = map(int, box.xyxy.flatten())
|
117 |
+
cropped_image = image[y1:y2, x1:x2]
|
118 |
+
|
119 |
+
# Draw rectangle on output image
|
120 |
+
color = (0, 255, 0) if int(box.cls.item()) == 0 else (255, 0, 0)
|
121 |
+
cv2.rectangle(output_image, (x1, y1), (x2, y2), color, 2)
|
122 |
+
|
123 |
+
if int(box.cls.item()) == 0: # License plate number
|
124 |
+
# Read characters using YOLO character reader
|
125 |
+
data["plate_number"] = self.read_plate_characters(cropped_image)
|
126 |
+
|
127 |
+
elif int(box.cls.item()) == 1: # Province
|
128 |
+
# Process province using TrOCR
|
129 |
+
cropped_image_gray = cv2.cvtColor(cropped_image, cv2.COLOR_BGR2GRAY)
|
130 |
+
equalized_image = cv2.equalizeHist(cropped_image_gray)
|
131 |
+
_, thresh_image = cv2.threshold(equalized_image, 65, 255, cv2.THRESH_BINARY_INV)
|
132 |
+
cropped_image_3d = cv2.cvtColor(thresh_image, cv2.COLOR_GRAY2RGB)
|
133 |
+
resized_image = cv2.resize(cropped_image_3d, (128, 32))
|
134 |
+
|
135 |
+
pixel_values = self.processor_plate(resized_image, return_tensors="pt").pixel_values
|
136 |
+
generated_ids = self.model_plate.generate(pixel_values)
|
137 |
+
generated_text = self.processor_plate.batch_decode(generated_ids, skip_special_tokens=True)[0]
|
138 |
+
|
139 |
+
generated_province, _ = self.get_closest_province(generated_text)
|
140 |
+
data["raw_province"] = generated_text
|
141 |
+
data["province"] = generated_province
|
142 |
+
|
143 |
+
# Save the output image
|
144 |
+
cv2.imwrite('output_detection.jpg', output_image)
|
145 |
+
return data
|
146 |
+
|
147 |
+
except Exception as e:
|
148 |
+
print(f"Error processing image: {str(e)}")
|
149 |
+
return None
|
150 |
+
|
151 |
+
def main():
|
152 |
+
st.set_page_config(
|
153 |
+
page_title="Thai License Plate Recognition",
|
154 |
+
layout="wide"
|
155 |
+
)
|
156 |
+
|
157 |
+
st.title("Thai License Plate Recognition")
|
158 |
+
st.write("Upload an image to detect and read Thai license plates")
|
159 |
+
|
160 |
+
# Initialize processor
|
161 |
+
@st.cache_resource
|
162 |
+
def load_processor():
|
163 |
+
return LicensePlateProcessor()
|
164 |
+
|
165 |
+
processor = load_processor()
|
166 |
+
|
167 |
+
# File uploader
|
168 |
+
uploaded_file = st.file_uploader("Choose an image...", type=["jpg", "jpeg", "png"])
|
169 |
+
|
170 |
+
if uploaded_file is not None:
|
171 |
+
# Create columns for side-by-side display
|
172 |
+
col1, col2 = st.columns(2)
|
173 |
+
|
174 |
+
# Display original image
|
175 |
+
with col1:
|
176 |
+
st.subheader("Original Image")
|
177 |
+
image = Image.open(uploaded_file)
|
178 |
+
st.image(image, use_column_width=True)
|
179 |
+
|
180 |
+
# Convert PIL Image to OpenCV format for processing
|
181 |
+
image_array = np.array(image)
|
182 |
+
if len(image_array.shape) == 3 and image_array.shape[2] == 4:
|
183 |
+
# Convert RGBA to RGB if needed
|
184 |
+
image_array = cv2.cvtColor(image_array, cv2.COLOR_RGBA2RGB)
|
185 |
+
image_cv = cv2.cvtColor(image_array, cv2.COLOR_RGB2BGR)
|
186 |
+
|
187 |
+
# Process image
|
188 |
+
with st.spinner("Processing image..."):
|
189 |
+
try:
|
190 |
+
# Save the OpenCV image for processing
|
191 |
+
temp_path = 'temp_input.jpg'
|
192 |
+
cv2.imwrite(temp_path, image_cv)
|
193 |
+
|
194 |
+
# Process the image using the processor
|
195 |
+
results = processor.process_image(temp_path)
|
196 |
+
|
197 |
+
# Clean up temporary input file
|
198 |
+
os.remove(temp_path)
|
199 |
+
|
200 |
+
if results:
|
201 |
+
# Display results
|
202 |
+
st.subheader("Detection Results")
|
203 |
+
|
204 |
+
# Create a styled container for results
|
205 |
+
results_container = st.container()
|
206 |
+
with results_container:
|
207 |
+
st.markdown(f"""
|
208 |
+
<div style='background-color: #f0f2f6; padding: 20px; border-radius: 10px;'>
|
209 |
+
<h3>License Plate: {results['plate_number']}</h3>
|
210 |
+
<h3>Province: {results['province']}</h3>
|
211 |
+
<p>Raw Province Text: {results['raw_province']}</p>
|
212 |
+
</div>
|
213 |
+
""", unsafe_allow_html=True)
|
214 |
+
|
215 |
+
# Display detection visualization
|
216 |
+
with col2:
|
217 |
+
st.subheader("Detection Visualization")
|
218 |
+
if os.path.exists('output_detection.jpg'):
|
219 |
+
# Read and convert the output image from BGR to RGB
|
220 |
+
output_image = cv2.imread('output_detection.jpg')
|
221 |
+
output_image_rgb = cv2.cvtColor(output_image, cv2.COLOR_BGR2RGB)
|
222 |
+
st.image(output_image_rgb, use_column_width=True)
|
223 |
+
# Clean up output image
|
224 |
+
os.remove('output_detection.jpg')
|
225 |
+
else:
|
226 |
+
st.error("No license plate detected in the image.")
|
227 |
+
|
228 |
+
except Exception as e:
|
229 |
+
st.error(f"Error processing image: {str(e)}")
|
230 |
+
# Clean up any temporary files in case of error
|
231 |
+
if os.path.exists('temp_input.jpg'):
|
232 |
+
os.remove('temp_input.jpg')
|
233 |
+
if os.path.exists('output_detection.jpg'):
|
234 |
+
os.remove('output_detection.jpg')
|
235 |
+
|
236 |
+
# Add information about the application
|
237 |
+
with st.expander("About This Application"):
|
238 |
+
st.markdown("""
|
239 |
+
### Thai License Plate Recognition System
|
240 |
+
|
241 |
+
This application uses advanced computer vision and deep learning to:
|
242 |
+
- Detect license plates in images using YOLO
|
243 |
+
- Read Thai license plate numbers using character recognition
|
244 |
+
- Identify province names using TrOCR
|
245 |
+
- Provide visual detection results
|
246 |
+
|
247 |
+
#### How to Use:
|
248 |
+
1. Click the 'Browse files' button above
|
249 |
+
2. Select an image containing a Thai license plate
|
250 |
+
3. Wait for the processing to complete
|
251 |
+
4. View the results and detection visualization
|
252 |
+
|
253 |
+
#### Technologies Used:
|
254 |
+
- YOLO for license plate detection
|
255 |
+
- Custom YOLO model for character recognition
|
256 |
+
- TrOCR for province text recognition
|
257 |
+
""")
|
258 |
+
|
259 |
+
if __name__ == "__main__":
|
260 |
+
main()
|
config/data.yaml
ADDED
@@ -0,0 +1,105 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
names:
|
2 |
+
0: '0'
|
3 |
+
1: '1'
|
4 |
+
2: '2'
|
5 |
+
3: '3'
|
6 |
+
4: '4'
|
7 |
+
5: '5'
|
8 |
+
6: '6'
|
9 |
+
7: '7'
|
10 |
+
8: '8'
|
11 |
+
9: '9'
|
12 |
+
10: 'ก'
|
13 |
+
11: 'ข'
|
14 |
+
12: 'ค'
|
15 |
+
13: 'ง'
|
16 |
+
14: 'จ'
|
17 |
+
15: 'ฉ'
|
18 |
+
16: 'ช'
|
19 |
+
17: 'ซ'
|
20 |
+
18: 'ฌ'
|
21 |
+
19: 'ญ'
|
22 |
+
20: 'ฎ'
|
23 |
+
21: 'ฏ'
|
24 |
+
22: 'ฐ'
|
25 |
+
23: 'ฑ'
|
26 |
+
24: 'ฒ'
|
27 |
+
25: 'ณ'
|
28 |
+
26: 'ด'
|
29 |
+
27: 'ต'
|
30 |
+
28: 'ถ'
|
31 |
+
29: 'ท'
|
32 |
+
30: 'ธ'
|
33 |
+
31: 'น'
|
34 |
+
32: 'บ'
|
35 |
+
33: 'ป'
|
36 |
+
34: 'ผ'
|
37 |
+
35: 'ฝ'
|
38 |
+
36: 'พ'
|
39 |
+
37: 'ฟ'
|
40 |
+
38: 'ภ'
|
41 |
+
39: 'ม'
|
42 |
+
40: 'ย'
|
43 |
+
41: 'ร'
|
44 |
+
42: 'ล'
|
45 |
+
43: 'ว'
|
46 |
+
44: 'ศ'
|
47 |
+
45: 'ษ'
|
48 |
+
46: 'ส'
|
49 |
+
47: 'ห'
|
50 |
+
48: 'ฬ'
|
51 |
+
49: 'อ'
|
52 |
+
50: 'ฮ'
|
53 |
+
|
54 |
+
char_mapping:
|
55 |
+
'0': '0'
|
56 |
+
'1': '1'
|
57 |
+
'2': '2'
|
58 |
+
'3': '3'
|
59 |
+
'4': '4'
|
60 |
+
'5': '5'
|
61 |
+
'6': '6'
|
62 |
+
'7': '7'
|
63 |
+
'8': '8'
|
64 |
+
'9': '9'
|
65 |
+
'10': 'ก'
|
66 |
+
'11': 'ข'
|
67 |
+
'12': 'ค'
|
68 |
+
'13': 'ง'
|
69 |
+
'14': 'จ'
|
70 |
+
'15': 'ฉ'
|
71 |
+
'16': 'ช'
|
72 |
+
'17': 'ซ'
|
73 |
+
'18': 'ฌ'
|
74 |
+
'19': 'ญ'
|
75 |
+
'20': 'ฎ'
|
76 |
+
'21': 'ฏ'
|
77 |
+
'22': 'ฐ'
|
78 |
+
'23': 'ฑ'
|
79 |
+
'24': 'ฒ'
|
80 |
+
'25': 'ณ'
|
81 |
+
'26': 'ด'
|
82 |
+
'27': 'ต'
|
83 |
+
'28': 'ถ'
|
84 |
+
'29': 'ท'
|
85 |
+
'30': 'ธ'
|
86 |
+
'31': 'น'
|
87 |
+
'32': 'บ'
|
88 |
+
'33': 'ป'
|
89 |
+
'34': 'ผ'
|
90 |
+
'35': 'ฝ'
|
91 |
+
'36': 'พ'
|
92 |
+
'37': 'ฟ'
|
93 |
+
'38': 'ภ'
|
94 |
+
'39': 'ม'
|
95 |
+
'40': 'ย'
|
96 |
+
'41': 'ร'
|
97 |
+
'42': 'ล'
|
98 |
+
'43': 'ว'
|
99 |
+
'44': 'ศ'
|
100 |
+
'45': 'ษ'
|
101 |
+
'46': 'ส'
|
102 |
+
'47': 'ห'
|
103 |
+
'48': 'ฬ'
|
104 |
+
'49': 'อ'
|
105 |
+
'50': 'ฮ'
|
models/best.pt
ADDED
@@ -0,0 +1,3 @@
|
|
|
|
|
|
|
|
|
1 |
+
version https://git-lfs.github.com/spec/v1
|
2 |
+
oid sha256:3b1da8d9362a1005aa5b060b0ac53b4622677e753eded2893da10b6a69bc9fb7
|
3 |
+
size 5468691
|
models/read_char.pt
ADDED
@@ -0,0 +1,3 @@
|
|
|
|
|
|
|
|
|
1 |
+
version https://git-lfs.github.com/spec/v1
|
2 |
+
oid sha256:871501b08ee035447680cfef41a764da1dc9ed67fabdac1bcb3b67543a2678e1
|
3 |
+
size 19224275
|
requirements.txt
ADDED
@@ -0,0 +1,10 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
streamlit==1.31.1
|
2 |
+
Pillow==10.0.0
|
3 |
+
opencv-python-headless==4.8.0.74
|
4 |
+
numpy==1.24.3
|
5 |
+
transformers==4.36.2
|
6 |
+
ultralytics==8.0.196
|
7 |
+
python-Levenshtein==0.23.0
|
8 |
+
PyYAML==6.0.1
|
9 |
+
torch==2.1.0
|
10 |
+
torchvision==0.16.0
|