Spaces:
Sleeping
Sleeping
Upload 10 files
Browse files- app.py +41 -0
- classes/classes.txt +52 -0
- classes/classes_vie.txt +52 -0
- core/__pycache__/helpers.cpython-311.pyc +0 -0
- core/__pycache__/model.cpython-311.pyc +0 -0
- core/__pycache__/ui.cpython-311.pyc +0 -0
- core/helpers.py +29 -0
- core/model.py +51 -0
- core/ui.py +61 -0
- requirements.txt +8 -0
app.py
ADDED
@@ -0,0 +1,41 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
import streamlit as st
|
2 |
+
import os
|
3 |
+
|
4 |
+
from core.ui import show_choosing_pic_bar, show_right_sidebar
|
5 |
+
from core.model import load_model, run_yolo_inference
|
6 |
+
from core.helpers import get_image_list, show_note_for_classes, show_hyperparameters
|
7 |
+
|
8 |
+
def main():
|
9 |
+
st.set_page_config(layout="wide")
|
10 |
+
st.title("Vietnam Traffic Sign Deployment")
|
11 |
+
|
12 |
+
model = load_model()
|
13 |
+
st.success("Model load successfully!")
|
14 |
+
|
15 |
+
# Left sidebar for image selection
|
16 |
+
images = get_image_list()
|
17 |
+
img_folder = "test_dataset/images"
|
18 |
+
show_choosing_pic_bar(images, img_folder)
|
19 |
+
|
20 |
+
if "selected_image" in st.session_state:
|
21 |
+
selected_image = os.path.join(img_folder, st.session_state.selected_image)
|
22 |
+
|
23 |
+
st.subheader("🖼️ Selected Image")
|
24 |
+
st.image(selected_image, caption=f"Original Image {st.session_state.selected_image}", use_container_width=True)
|
25 |
+
|
26 |
+
# Now create 2 columns below the Selected Image
|
27 |
+
middle_part, right_bar = st.columns([4, 1], gap="large")
|
28 |
+
|
29 |
+
# Right sidebar for thresholds
|
30 |
+
conf_threshold, iou_threshold, conf_show = show_right_sidebar(right_bar)
|
31 |
+
|
32 |
+
with middle_part:
|
33 |
+
st.subheader("🔍 Detection Result")
|
34 |
+
detected_img, classes_with_conf = run_yolo_inference(model, selected_image, conf_threshold, iou_threshold, conf_show)
|
35 |
+
st.image(detected_img, caption="Detected Objects", use_container_width=True)
|
36 |
+
|
37 |
+
show_hyperparameters(conf_threshold, iou_threshold)
|
38 |
+
show_note_for_classes(classes_with_conf)
|
39 |
+
|
40 |
+
if __name__ == "__main__":
|
41 |
+
main()
|
classes/classes.txt
ADDED
@@ -0,0 +1,52 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
W.224
|
2 |
+
W.205c
|
3 |
+
P.102
|
4 |
+
R.302a
|
5 |
+
W.205a
|
6 |
+
W.207
|
7 |
+
W.201a
|
8 |
+
P.123a
|
9 |
+
I.434a
|
10 |
+
R.303
|
11 |
+
P.130
|
12 |
+
I.409
|
13 |
+
R.415a
|
14 |
+
W.245a
|
15 |
+
P.106a*Xe tải
|
16 |
+
W.203c
|
17 |
+
P.117*
|
18 |
+
P.124a*
|
19 |
+
P.107
|
20 |
+
P.124d
|
21 |
+
P.103a
|
22 |
+
W.203b
|
23 |
+
W.221b
|
24 |
+
P.111
|
25 |
+
P.129
|
26 |
+
S.505a*Xe máy
|
27 |
+
W.246a
|
28 |
+
W.225
|
29 |
+
S.505a*Xe tải và công
|
30 |
+
P.104
|
31 |
+
S.505a*Xe tải
|
32 |
+
Camera
|
33 |
+
P.123b
|
34 |
+
W.202b
|
35 |
+
B.8a
|
36 |
+
P.137
|
37 |
+
P.139
|
38 |
+
W.205b
|
39 |
+
P.127*50
|
40 |
+
P.127*60
|
41 |
+
P.127*80
|
42 |
+
P.127*40
|
43 |
+
R.301e
|
44 |
+
W.239b*
|
45 |
+
W.233
|
46 |
+
I.407a
|
47 |
+
P.131a
|
48 |
+
P.124b1
|
49 |
+
W.210
|
50 |
+
P.124c
|
51 |
+
W.201b
|
52 |
+
W.246c
|
classes/classes_vie.txt
ADDED
@@ -0,0 +1,52 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
Đường người đi bộ cắt ngang
|
2 |
+
Đường giao nhau (ngã ba bên phải)
|
3 |
+
Cấm đi ngược chiều
|
4 |
+
Phải đi vòng sang bên phải
|
5 |
+
Giao nhau với đường đồng cấp
|
6 |
+
Giao nhau với đường không ưu tiên
|
7 |
+
Chỗ ngoặt nguy hiểm vòng bên trái
|
8 |
+
Cấm rẽ trái
|
9 |
+
Bến xe buýt
|
10 |
+
Nơi giao nhau chạy theo vòng xuyến
|
11 |
+
Cấm dừng và đỗ xe
|
12 |
+
Chỗ quay xe
|
13 |
+
Biển gộp làn đường theo phương tiện
|
14 |
+
Đi chậm
|
15 |
+
Cấm xe tải
|
16 |
+
Đường bị thu hẹp về phía phải
|
17 |
+
Giới hạn chiều cao
|
18 |
+
Cấm quay đầu
|
19 |
+
Cấm ô tô khách và ô tô tải
|
20 |
+
Cấm rẽ phải và quay đầu
|
21 |
+
Cấm ô tô
|
22 |
+
Đường bị thu hẹp về phía trái
|
23 |
+
Gồ giảm tốc phía trước
|
24 |
+
Cấm xe hai và ba bánh
|
25 |
+
Kiểm tra
|
26 |
+
Chỉ dành cho xe máy*
|
27 |
+
Chướng ngoại vật phía trước
|
28 |
+
Trẻ em
|
29 |
+
Xe tải và xe công*
|
30 |
+
Cấm mô tô và xe máy
|
31 |
+
Chỉ dành cho xe tải*
|
32 |
+
Đường có camera giám sát
|
33 |
+
Cấm rẽ phải
|
34 |
+
Nhiều chỗ ngoặt nguy hiểm liên tiếp, chỗ đầu tiên sang phải
|
35 |
+
Cấm xe sơ-mi rơ-moóc
|
36 |
+
Cấm rẽ trái và phải
|
37 |
+
Cấm đi thẳng và rẽ phải
|
38 |
+
Đường giao nhau (ngã ba bên trái)
|
39 |
+
Giới hạn tốc độ (50km/h)
|
40 |
+
Giới hạn tốc độ (60km/h)
|
41 |
+
Giới hạn tốc độ (80km/h)
|
42 |
+
Giới hạn tốc độ (40km/h)
|
43 |
+
Các xe chỉ được rẽ trái
|
44 |
+
Chiều cao tĩnh không thực tế
|
45 |
+
Nguy hiểm khác
|
46 |
+
Đường một chiều
|
47 |
+
Cấm đỗ xe
|
48 |
+
Cấm ô tô quay đầu xe (được rẽ trái)
|
49 |
+
Giao nhau với đường sắt có rào chắn
|
50 |
+
Cấm rẽ trái và quay đầu xe
|
51 |
+
Chỗ ngoặt nguy hiểm vòng bên phải
|
52 |
+
Chú ý chướng ngại vật – vòng tránh sang bên phải
|
core/__pycache__/helpers.cpython-311.pyc
ADDED
Binary file (3.01 kB). View file
|
|
core/__pycache__/model.cpython-311.pyc
ADDED
Binary file (2.72 kB). View file
|
|
core/__pycache__/ui.cpython-311.pyc
ADDED
Binary file (3.45 kB). View file
|
|
core/helpers.py
ADDED
@@ -0,0 +1,29 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
import os
|
2 |
+
import streamlit as st
|
3 |
+
|
4 |
+
def get_image_list():
|
5 |
+
folder_path = "test_dataset/images"
|
6 |
+
images = [f for f in os.listdir(folder_path) if f.lower().endswith('jpg')]
|
7 |
+
return images
|
8 |
+
|
9 |
+
def show_hyperparameters(conf_threshold, iou_threshold):
|
10 |
+
st.subheader("Respect to hyperparameters:")
|
11 |
+
st.markdown(f"- Confidence threshold: {conf_threshold}")
|
12 |
+
st.markdown(f"- IOU threshold for Non-Maximum Suppression (NMS): {iou_threshold}")
|
13 |
+
|
14 |
+
def show_note_for_classes(classes_with_conf):
|
15 |
+
with open("classes/classes.txt", "r", encoding="utf-8") as f:
|
16 |
+
classes = [line.strip() for line in f.readlines()]
|
17 |
+
|
18 |
+
with open("classes/classes_vie.txt", "r", encoding="utf-8") as f:
|
19 |
+
classes_vie = [line.strip() for line in f.readlines()]
|
20 |
+
|
21 |
+
if not classes_with_conf:
|
22 |
+
st.info("No detections found.")
|
23 |
+
return
|
24 |
+
|
25 |
+
st.subheader("📝 Detected Classes")
|
26 |
+
for cls, conf in classes_with_conf:
|
27 |
+
label = classes[cls]
|
28 |
+
label_vie = classes_vie[cls]
|
29 |
+
st.markdown(f"- **{label}** (`{label_vie}`) — Confidence: **{conf:.2f}**")
|
core/model.py
ADDED
@@ -0,0 +1,51 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
from ultralytics import YOLO
|
2 |
+
import streamlit as st
|
3 |
+
import cv2
|
4 |
+
from huggingface_hub import hf_hub_download
|
5 |
+
|
6 |
+
@st.cache_resource
|
7 |
+
def load_model():
|
8 |
+
model_path = hf_hub_download(
|
9 |
+
repo_id="thanhhiepvos/Fine_tuned_Vietnam_Traffic_Sign_Detection",
|
10 |
+
filename="YOLO12X.pt",
|
11 |
+
repo_type="model"
|
12 |
+
)
|
13 |
+
return YOLO(model_path)
|
14 |
+
|
15 |
+
def run_yolo_inference(model, image_path, conf_threshold=0.25, iou_threshold=0.7, conf_show=False):
|
16 |
+
# Read image with OpenCV (BGR)
|
17 |
+
img = cv2.imread(image_path)
|
18 |
+
results = model(img,
|
19 |
+
conf=conf_threshold,
|
20 |
+
iou=iou_threshold
|
21 |
+
)[0]
|
22 |
+
|
23 |
+
classes_with_conf = []
|
24 |
+
|
25 |
+
for i, box in enumerate(results.boxes):
|
26 |
+
x1, y1, x2, y2 = box.xyxy[0].int().tolist()
|
27 |
+
conf = float(box.conf[0])
|
28 |
+
cls = int(box.cls[0])
|
29 |
+
if conf_show:
|
30 |
+
label = f"{model.names[cls]} {conf:.2f}"
|
31 |
+
else:
|
32 |
+
label = f"{model.names[cls]}"
|
33 |
+
classes_with_conf.append([cls, conf])
|
34 |
+
|
35 |
+
# Draw bounding box
|
36 |
+
cv2.rectangle(img, (x1, y1), (x2, y2), (0, 0, 255), 2)
|
37 |
+
|
38 |
+
# Get text size
|
39 |
+
(text_w, text_h), baseline = cv2.getTextSize(label, cv2.FONT_HERSHEY_SIMPLEX, 0.6, 1)
|
40 |
+
|
41 |
+
# New Y position: just below the bounding box, plus spacing
|
42 |
+
label_y = y2 + (i * (text_h + 6)) + 10
|
43 |
+
|
44 |
+
# Background rectangle for the label
|
45 |
+
cv2.rectangle(img, (x1, label_y), (x1 + text_w, label_y + text_h + 4), (255, 255, 255), -1)
|
46 |
+
|
47 |
+
# Red text over white background
|
48 |
+
cv2.putText(img, label, (x1, label_y + text_h + 2), cv2.FONT_HERSHEY_SIMPLEX,
|
49 |
+
0.6, (0, 0, 255), 1, cv2.LINE_AA)
|
50 |
+
|
51 |
+
return cv2.cvtColor(img, cv2.COLOR_BGR2RGB), classes_with_conf
|
core/ui.py
ADDED
@@ -0,0 +1,61 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
import streamlit as st
|
2 |
+
from PIL import Image
|
3 |
+
import os
|
4 |
+
|
5 |
+
|
6 |
+
def show_choosing_pic_bar(images, img_folder):
|
7 |
+
st.sidebar.header("📂 Select an Image")
|
8 |
+
col1, col2 = st.sidebar.columns(2)
|
9 |
+
selected_img = None
|
10 |
+
|
11 |
+
for i, img_name in enumerate(images):
|
12 |
+
img_path = os.path.join(img_folder, img_name)
|
13 |
+
image = Image.open(img_path).resize((100, 100))
|
14 |
+
|
15 |
+
col = col1 if i % 2 == 0 else col2
|
16 |
+
if col.button("Select", key=img_name):
|
17 |
+
st.session_state.selected_image = img_name
|
18 |
+
|
19 |
+
col.image(image, use_container_width = True)
|
20 |
+
#st.sidebar.image(image)
|
21 |
+
col.caption(img_name)
|
22 |
+
|
23 |
+
def show_right_sidebar(col):
|
24 |
+
# Set defaults if not present
|
25 |
+
if "confidence" not in st.session_state:
|
26 |
+
st.session_state.confidence = 0.25
|
27 |
+
if "iou" not in st.session_state:
|
28 |
+
st.session_state.iou = 0.7
|
29 |
+
if "show_confidence" not in st.session_state:
|
30 |
+
st.session_state.show_confidence = False # Set default to False
|
31 |
+
|
32 |
+
with col:
|
33 |
+
st.markdown("### ⚙️ Hyperparameters", unsafe_allow_html=True)
|
34 |
+
|
35 |
+
# --- Confidence ---
|
36 |
+
st.session_state.confidence = st.number_input(
|
37 |
+
"Confidence Threshold", min_value=0.0, max_value=1.0,
|
38 |
+
step=0.01, value=st.session_state.confidence,
|
39 |
+
format="%.2f"
|
40 |
+
)
|
41 |
+
st.caption("Confidence score for label in predicted bounding box.")
|
42 |
+
|
43 |
+
st.divider()
|
44 |
+
|
45 |
+
# --- IOU ---
|
46 |
+
st.session_state.iou = st.number_input(
|
47 |
+
"IOU Threshold", min_value=0.0, max_value=1.0,
|
48 |
+
step=0.01, value=st.session_state.iou,
|
49 |
+
format="%.2f"
|
50 |
+
)
|
51 |
+
st.caption("Lower values result in fewer detections by eliminating overlapping boxes, useful for reducing duplicates.")
|
52 |
+
|
53 |
+
st.markdown("---")
|
54 |
+
st.header("📊 Display Options")
|
55 |
+
|
56 |
+
# --- Show Confidence Checkbox ---
|
57 |
+
st.session_state.show_confidence = st.checkbox(
|
58 |
+
"Show Confidence", value=st.session_state.show_confidence
|
59 |
+
)
|
60 |
+
|
61 |
+
return st.session_state.confidence, st.session_state.iou, st.session_state.show_confidence
|
requirements.txt
ADDED
@@ -0,0 +1,8 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
streamlit
|
2 |
+
opencv-python
|
3 |
+
numpy
|
4 |
+
Pillow
|
5 |
+
ultralytics
|
6 |
+
base64
|
7 |
+
io
|
8 |
+
huggingface
|