Spaces:
Sleeping
Sleeping
sergey21000
commited on
Commit
•
f0d0031
1
Parent(s):
946fe5f
Upload 2 files
Browse files
app.py
CHANGED
@@ -1,183 +1,183 @@
|
|
1 |
-
import shutil
|
2 |
-
from pathlib import Path
|
3 |
-
from typing import List, Dict, Union, Tuple, Literal, Optional
|
4 |
-
|
5 |
-
import numpy as np
|
6 |
-
import gradio as gr
|
7 |
-
from gradio.components.base import Component
|
8 |
-
from ultralytics import YOLO
|
9 |
-
|
10 |
-
from utils import download_model, detect_image, detect_video, get_csv_annotate
|
11 |
-
|
12 |
-
|
13 |
-
# ======================= MODEL ===================================
|
14 |
-
|
15 |
-
MODELS_DIR = Path('models')
|
16 |
-
MODELS_DIR.mkdir(exist_ok=True)
|
17 |
-
|
18 |
-
MODELS = {
|
19 |
-
'yolov11n.pt': 'https://github.com/ultralytics/assets/releases/download/v8.3.0/yolo11n.pt',
|
20 |
-
'yolov11s.pt': 'https://github.com/ultralytics/assets/releases/download/v8.3.0/yolo11s.pt',
|
21 |
-
'yolov11m.pt': 'https://github.com/ultralytics/assets/releases/download/v8.3.0/yolo11m.pt',
|
22 |
-
'yolov11l.pt': 'https://github.com/ultralytics/assets/releases/download/v8.3.0/yolo11l.pt',
|
23 |
-
'yolov11x.pt': 'https://github.com/ultralytics/assets/releases/download/v8.3.0/yolo11x.pt',
|
24 |
-
}
|
25 |
-
MODEL_NAMES = list(MODELS.keys())
|
26 |
-
|
27 |
-
model_path = download_model(MODEL_NAMES[0], MODELS_DIR, MODELS)
|
28 |
-
default_model = YOLO(model_path)
|
29 |
-
|
30 |
-
IMAGE_EXTENSIONS = ['.jpg', '.jpeg', '.png']
|
31 |
-
VIDEO_EXTENSIONS = ['.mp4', '.avi']
|
32 |
-
|
33 |
-
|
34 |
-
# =================== ADDITIONAL INTERFACE FUNCTIONS ========================
|
35 |
-
|
36 |
-
def change_model(model_state: Dict[str, YOLO], model_name: str):
|
37 |
-
progress = gr.Progress()
|
38 |
-
progress(0.3, desc='Downloading the model')
|
39 |
-
model_path = download_model(model_name)
|
40 |
-
progress(0.7, desc='Model initialization')
|
41 |
-
model_state['model'] = YOLO(model_path)
|
42 |
-
return f'Model {model_name} initialized'
|
43 |
-
|
44 |
-
|
45 |
-
def detect(file_path: str, file_link: str, model_state: Dict[str, YOLO], conf: float, iou: float):
|
46 |
-
model = model_state['model']
|
47 |
-
if file_link:
|
48 |
-
file_path = file_link
|
49 |
-
|
50 |
-
file_ext = f'.{file_path.rsplit(".")[-1]}'
|
51 |
-
if file_ext in IMAGE_EXTENSIONS:
|
52 |
-
np_image = detect_image(file_path, model, conf, iou)
|
53 |
-
return np_image, "Detection complete, opening image..."
|
54 |
-
elif file_ext in VIDEO_EXTENSIONS or 'youtube.com' in file_link:
|
55 |
-
video_path = detect_video(file_path, model, conf, iou)
|
56 |
-
return video_path, "Detection complete, converting and opening video..."
|
57 |
-
else:
|
58 |
-
gr.Info('Invalid image or video format...')
|
59 |
-
return None, None
|
60 |
-
|
61 |
-
# =================== INTERFACE COMPONENTS ============================
|
62 |
-
|
63 |
-
def get_output_media_components(detect_result: Optional[Union[np.ndarray, str, Path]] = None):
|
64 |
-
visible = isinstance(detect_result, np.ndarray)
|
65 |
-
image_output = gr.Image(
|
66 |
-
value=detect_result if visible else None,
|
67 |
-
type="numpy",
|
68 |
-
width=640,
|
69 |
-
height=480,
|
70 |
-
visible=visible,
|
71 |
-
label='Output',
|
72 |
-
)
|
73 |
-
visible = isinstance(detect_result, (str, Path))
|
74 |
-
video_output = gr.Video(
|
75 |
-
value=detect_result if visible else None,
|
76 |
-
width=640,
|
77 |
-
height=480,
|
78 |
-
visible=visible,
|
79 |
-
label='Output',
|
80 |
-
)
|
81 |
-
clear_btn = gr.Button(
|
82 |
-
value='Clear',
|
83 |
-
scale=0,
|
84 |
-
visible=detect_result is not None,
|
85 |
-
)
|
86 |
-
return image_output, video_output, clear_btn
|
87 |
-
|
88 |
-
|
89 |
-
def get_download_csv_btn(csv_annotations_path: Optional[Path] = None):
|
90 |
-
download_csv_btn = gr.DownloadButton(
|
91 |
-
label='Download csv annotations for video',
|
92 |
-
value=csv_annotations_path,
|
93 |
-
scale=0,
|
94 |
-
visible=csv_annotations_path is not None,
|
95 |
-
)
|
96 |
-
return download_csv_btn
|
97 |
-
|
98 |
-
# =================== APPINTERFACE ==========================
|
99 |
-
|
100 |
-
css = '''
|
101 |
-
.gradio-container { width: 70% !important }
|
102 |
-
'''
|
103 |
-
with gr.Blocks(css=css) as demo:
|
104 |
-
gr.HTML("""<h3 style='text-align: center'>YOLOv11 Detector</h3>""")
|
105 |
-
|
106 |
-
model_state = gr.State({'model': default_model})
|
107 |
-
detect_result = gr.State(None)
|
108 |
-
csv_annotations_path = gr.State(None)
|
109 |
-
|
110 |
-
with gr.Row():
|
111 |
-
with gr.Column():
|
112 |
-
file_path = gr.File(file_types=['image', 'video'], file_count='single', label='Select an image or video')
|
113 |
-
file_link = gr.Textbox(label='Direct link to image or YouTube link')
|
114 |
-
model_name = gr.Radio(choices=MODEL_NAMES, value=MODEL_NAMES[0], label='Select YOLO model')
|
115 |
-
conf = gr.Slider(0, 1, value=0.5, step=0.05, label='Confidence')
|
116 |
-
iou = gr.Slider(0, 1, value=0.7, step=0.1, label='IOU')
|
117 |
-
status_message = gr.Textbox(value='Ready to go', label='Status')
|
118 |
-
detect_btn = gr.Button('Detect', interactive=True)
|
119 |
-
|
120 |
-
with gr.Column():
|
121 |
-
image_output, video_output, clear_btn = get_output_media_components()
|
122 |
-
download_csv_btn = get_download_csv_btn()
|
123 |
-
|
124 |
-
model_name.change(
|
125 |
-
fn=lambda: gr.update(interactive=False),
|
126 |
-
inputs=None,
|
127 |
-
outputs=[detect_btn],
|
128 |
-
).then(
|
129 |
-
fn=change_model,
|
130 |
-
inputs=[model_state, model_name],
|
131 |
-
outputs=[status_message],
|
132 |
-
).success(
|
133 |
-
fn=lambda: gr.update(interactive=True),
|
134 |
-
inputs=None,
|
135 |
-
outputs=[detect_btn],
|
136 |
-
)
|
137 |
-
|
138 |
-
detect_btn.click(
|
139 |
-
fn=detect,
|
140 |
-
inputs=[file_path, file_link, model_state, conf, iou],
|
141 |
-
outputs=[detect_result, status_message],
|
142 |
-
).success(
|
143 |
-
fn=get_output_media_components,
|
144 |
-
inputs=[detect_result],
|
145 |
-
outputs=[image_output, video_output, clear_btn],
|
146 |
-
).then(
|
147 |
-
fn=lambda: 'Ready to go',
|
148 |
-
inputs=None,
|
149 |
-
outputs=[status_message],
|
150 |
-
).then(
|
151 |
-
fn=get_csv_annotate,
|
152 |
-
inputs=[detect_result],
|
153 |
-
outputs=[csv_annotations_path],
|
154 |
-
).success(
|
155 |
-
fn=get_download_csv_btn,
|
156 |
-
inputs=[csv_annotations_path],
|
157 |
-
outputs=[download_csv_btn],
|
158 |
-
)
|
159 |
-
|
160 |
-
def clear_results_dir(detect_result):
|
161 |
-
if isinstance(detect_result, Path):
|
162 |
-
shutil.rmtree(detect_result.parent, ignore_errors=True)
|
163 |
-
|
164 |
-
clear_components = [image_output, video_output, clear_btn, download_csv_btn]
|
165 |
-
clear_btn.click(
|
166 |
-
fn=lambda: [gr.update(visible=False) for _ in range(len(clear_components))],
|
167 |
-
inputs=None,
|
168 |
-
outputs=clear_components,
|
169 |
-
).then(
|
170 |
-
fn=clear_results_dir,
|
171 |
-
inputs=[detect_result],
|
172 |
-
outputs=None,
|
173 |
-
).then(
|
174 |
-
fn=lambda: (None, None),
|
175 |
-
inputs=None,
|
176 |
-
outputs=[detect_result, csv_annotations_path]
|
177 |
-
)
|
178 |
-
|
179 |
-
gr.HTML("""<h3 style='text-align: center'>
|
180 |
-
<a href="https://github.com/sergey21000/yolo-detector" target='_blank'>GitHub Page</a></h3>
|
181 |
-
""")
|
182 |
-
|
183 |
demo.launch(server_name='0.0.0.0') # debug=True
|
|
|
1 |
+
import shutil
|
2 |
+
from pathlib import Path
|
3 |
+
from typing import List, Dict, Union, Tuple, Literal, Optional
|
4 |
+
|
5 |
+
import numpy as np
|
6 |
+
import gradio as gr
|
7 |
+
from gradio.components.base import Component
|
8 |
+
from ultralytics import YOLO
|
9 |
+
|
10 |
+
from utils import download_model, detect_image, detect_video, get_csv_annotate
|
11 |
+
|
12 |
+
|
13 |
+
# ======================= MODEL ===================================
|
14 |
+
|
15 |
+
MODELS_DIR = Path('models')
|
16 |
+
MODELS_DIR.mkdir(exist_ok=True)
|
17 |
+
|
18 |
+
MODELS = {
|
19 |
+
'yolov11n.pt': 'https://github.com/ultralytics/assets/releases/download/v8.3.0/yolo11n.pt',
|
20 |
+
'yolov11s.pt': 'https://github.com/ultralytics/assets/releases/download/v8.3.0/yolo11s.pt',
|
21 |
+
'yolov11m.pt': 'https://github.com/ultralytics/assets/releases/download/v8.3.0/yolo11m.pt',
|
22 |
+
'yolov11l.pt': 'https://github.com/ultralytics/assets/releases/download/v8.3.0/yolo11l.pt',
|
23 |
+
'yolov11x.pt': 'https://github.com/ultralytics/assets/releases/download/v8.3.0/yolo11x.pt',
|
24 |
+
}
|
25 |
+
MODEL_NAMES = list(MODELS.keys())
|
26 |
+
|
27 |
+
model_path = download_model(MODEL_NAMES[0], MODELS_DIR, MODELS)
|
28 |
+
default_model = YOLO(model_path)
|
29 |
+
|
30 |
+
IMAGE_EXTENSIONS = ['.jpg', '.jpeg', '.png']
|
31 |
+
VIDEO_EXTENSIONS = ['.mp4', '.avi']
|
32 |
+
|
33 |
+
|
34 |
+
# =================== ADDITIONAL INTERFACE FUNCTIONS ========================
|
35 |
+
|
36 |
+
def change_model(model_state: Dict[str, YOLO], model_name: str):
|
37 |
+
progress = gr.Progress()
|
38 |
+
progress(0.3, desc='Downloading the model')
|
39 |
+
model_path = download_model(model_name)
|
40 |
+
progress(0.7, desc='Model initialization')
|
41 |
+
model_state['model'] = YOLO(model_path)
|
42 |
+
return f'Model {model_name} initialized'
|
43 |
+
|
44 |
+
|
45 |
+
def detect(file_path: str, file_link: str, model_state: Dict[str, YOLO], conf: float, iou: float):
|
46 |
+
model = model_state['model']
|
47 |
+
if file_link:
|
48 |
+
file_path = file_link
|
49 |
+
|
50 |
+
file_ext = f'.{file_path.rsplit(".")[-1]}'
|
51 |
+
if file_ext in IMAGE_EXTENSIONS:
|
52 |
+
np_image = detect_image(file_path, model, conf, iou)
|
53 |
+
return np_image, "Detection complete, opening image..."
|
54 |
+
elif file_ext in VIDEO_EXTENSIONS or 'youtube.com' in file_link:
|
55 |
+
video_path = detect_video(file_path, model, conf, iou)
|
56 |
+
return video_path, "Detection complete, converting and opening video..."
|
57 |
+
else:
|
58 |
+
gr.Info('Invalid image or video format...')
|
59 |
+
return None, None
|
60 |
+
|
61 |
+
# =================== INTERFACE COMPONENTS ============================
|
62 |
+
|
63 |
+
def get_output_media_components(detect_result: Optional[Union[np.ndarray, str, Path]] = None):
|
64 |
+
visible = isinstance(detect_result, np.ndarray)
|
65 |
+
image_output = gr.Image(
|
66 |
+
value=detect_result if visible else None,
|
67 |
+
type="numpy",
|
68 |
+
width=640,
|
69 |
+
height=480,
|
70 |
+
visible=visible,
|
71 |
+
label='Output',
|
72 |
+
)
|
73 |
+
visible = isinstance(detect_result, (str, Path))
|
74 |
+
video_output = gr.Video(
|
75 |
+
value=detect_result if visible else None,
|
76 |
+
width=640,
|
77 |
+
height=480,
|
78 |
+
visible=visible,
|
79 |
+
label='Output',
|
80 |
+
)
|
81 |
+
clear_btn = gr.Button(
|
82 |
+
value='Clear',
|
83 |
+
scale=0,
|
84 |
+
visible=detect_result is not None,
|
85 |
+
)
|
86 |
+
return image_output, video_output, clear_btn
|
87 |
+
|
88 |
+
|
89 |
+
def get_download_csv_btn(csv_annotations_path: Optional[Path] = None):
|
90 |
+
download_csv_btn = gr.DownloadButton(
|
91 |
+
label='Download csv annotations for video',
|
92 |
+
value=csv_annotations_path,
|
93 |
+
scale=0,
|
94 |
+
visible=csv_annotations_path is not None,
|
95 |
+
)
|
96 |
+
return download_csv_btn
|
97 |
+
|
98 |
+
# =================== APPINTERFACE ==========================
|
99 |
+
|
100 |
+
css = '''
|
101 |
+
.gradio-container { width: 70% !important }
|
102 |
+
'''
|
103 |
+
with gr.Blocks(css=css) as demo:
|
104 |
+
gr.HTML("""<h3 style='text-align: center'>YOLOv11 Detector</h3>""")
|
105 |
+
|
106 |
+
model_state = gr.State({'model': default_model})
|
107 |
+
detect_result = gr.State(None)
|
108 |
+
csv_annotations_path = gr.State(None)
|
109 |
+
|
110 |
+
with gr.Row():
|
111 |
+
with gr.Column():
|
112 |
+
file_path = gr.File(file_types=['image', 'video'], file_count='single', label='Select an image or video')
|
113 |
+
file_link = gr.Textbox(label='Direct link to image or YouTube link')
|
114 |
+
model_name = gr.Radio(choices=MODEL_NAMES, value=MODEL_NAMES[0], label='Select YOLO model')
|
115 |
+
conf = gr.Slider(0, 1, value=0.5, step=0.05, label='Confidence')
|
116 |
+
iou = gr.Slider(0, 1, value=0.7, step=0.1, label='IOU')
|
117 |
+
status_message = gr.Textbox(value='Ready to go', label='Status')
|
118 |
+
detect_btn = gr.Button('Detect', interactive=True)
|
119 |
+
|
120 |
+
with gr.Column():
|
121 |
+
image_output, video_output, clear_btn = get_output_media_components()
|
122 |
+
download_csv_btn = get_download_csv_btn()
|
123 |
+
|
124 |
+
model_name.change(
|
125 |
+
fn=lambda: gr.update(interactive=False),
|
126 |
+
inputs=None,
|
127 |
+
outputs=[detect_btn],
|
128 |
+
).then(
|
129 |
+
fn=change_model,
|
130 |
+
inputs=[model_state, model_name],
|
131 |
+
outputs=[status_message],
|
132 |
+
).success(
|
133 |
+
fn=lambda: gr.update(interactive=True),
|
134 |
+
inputs=None,
|
135 |
+
outputs=[detect_btn],
|
136 |
+
)
|
137 |
+
|
138 |
+
detect_btn.click(
|
139 |
+
fn=detect,
|
140 |
+
inputs=[file_path, file_link, model_state, conf, iou],
|
141 |
+
outputs=[detect_result, status_message],
|
142 |
+
).success(
|
143 |
+
fn=get_output_media_components,
|
144 |
+
inputs=[detect_result],
|
145 |
+
outputs=[image_output, video_output, clear_btn],
|
146 |
+
).then(
|
147 |
+
fn=lambda: 'Ready to go',
|
148 |
+
inputs=None,
|
149 |
+
outputs=[status_message],
|
150 |
+
).then(
|
151 |
+
fn=get_csv_annotate,
|
152 |
+
inputs=[detect_result],
|
153 |
+
outputs=[csv_annotations_path],
|
154 |
+
).success(
|
155 |
+
fn=get_download_csv_btn,
|
156 |
+
inputs=[csv_annotations_path],
|
157 |
+
outputs=[download_csv_btn],
|
158 |
+
)
|
159 |
+
|
160 |
+
def clear_results_dir(detect_result):
|
161 |
+
if isinstance(detect_result, Path):
|
162 |
+
shutil.rmtree(detect_result.parent, ignore_errors=True)
|
163 |
+
|
164 |
+
clear_components = [image_output, video_output, clear_btn, download_csv_btn]
|
165 |
+
clear_btn.click(
|
166 |
+
fn=lambda: [gr.update(visible=False) for _ in range(len(clear_components))],
|
167 |
+
inputs=None,
|
168 |
+
outputs=clear_components,
|
169 |
+
).then(
|
170 |
+
fn=clear_results_dir,
|
171 |
+
inputs=[detect_result],
|
172 |
+
outputs=None,
|
173 |
+
).then(
|
174 |
+
fn=lambda: (None, None),
|
175 |
+
inputs=None,
|
176 |
+
outputs=[detect_result, csv_annotations_path]
|
177 |
+
)
|
178 |
+
|
179 |
+
gr.HTML("""<h3 style='text-align: center'>
|
180 |
+
<a href="https://github.com/sergey21000/yolo-detector" target='_blank'>GitHub Page</a></h3>
|
181 |
+
""")
|
182 |
+
|
183 |
demo.launch(server_name='0.0.0.0') # debug=True
|
utils.py
CHANGED
@@ -25,7 +25,7 @@ def download_model(model_name: str, models_dir: Path, models: dict) -> str:
|
|
25 |
|
26 |
|
27 |
def detect_image(image_path: str, model: YOLO, conf: float, iou: float) -> np.ndarray:
|
28 |
-
gr.Progress()(0.5, desc='
|
29 |
detections = model.predict(source=image_path, conf=conf, iou=iou)
|
30 |
np_image = detections[0].plot()
|
31 |
np_image = cv2.cvtColor(np_image, cv2.COLOR_BGR2RGB)
|
@@ -36,7 +36,7 @@ def detect_video(video_path_or_url: str, model: YOLO, conf: float, iou: float) -
|
|
36 |
progress = gr.Progress()
|
37 |
video_path = video_path_or_url
|
38 |
if 'youtube.com' in video_path_or_url or 'youtu.be' in video_path_or_url:
|
39 |
-
progress(0.001, desc='
|
40 |
ydl_opts = {'format': 'bestvideo[height<=720]'}
|
41 |
with yt_dlp.YoutubeDL(ydl_opts) as ydl:
|
42 |
video_info_dict = ydl.extract_info(video_path_or_url, download=True)
|
@@ -60,7 +60,7 @@ def detect_video(video_path_or_url: str, model: YOLO, conf: float, iou: float) -
|
|
60 |
frames_count = 0
|
61 |
for result in generator:
|
62 |
frames_count += 1
|
63 |
-
progress((frames_count, num_frames), desc=f'
|
64 |
|
65 |
file_name = Path(result.path).with_suffix('.avi').name
|
66 |
result_video_path = Path(result.save_dir) / file_name
|
|
|
25 |
|
26 |
|
27 |
def detect_image(image_path: str, model: YOLO, conf: float, iou: float) -> np.ndarray:
|
28 |
+
gr.Progress()(0.5, desc='Image detection...')
|
29 |
detections = model.predict(source=image_path, conf=conf, iou=iou)
|
30 |
np_image = detections[0].plot()
|
31 |
np_image = cv2.cvtColor(np_image, cv2.COLOR_BGR2RGB)
|
|
|
36 |
progress = gr.Progress()
|
37 |
video_path = video_path_or_url
|
38 |
if 'youtube.com' in video_path_or_url or 'youtu.be' in video_path_or_url:
|
39 |
+
progress(0.001, desc='Downloading videos from YouTube...')
|
40 |
ydl_opts = {'format': 'bestvideo[height<=720]'}
|
41 |
with yt_dlp.YoutubeDL(ydl_opts) as ydl:
|
42 |
video_info_dict = ydl.extract_info(video_path_or_url, download=True)
|
|
|
60 |
frames_count = 0
|
61 |
for result in generator:
|
62 |
frames_count += 1
|
63 |
+
progress((frames_count, num_frames), desc=f'Video detection, step {frames_count}/{num_frames}')
|
64 |
|
65 |
file_name = Path(result.path).with_suffix('.avi').name
|
66 |
result_video_path = Path(result.save_dir) / file_name
|