24 |
25 | ### 🎯 Место: 4
26 | 📍 **Международный хакатон 2024 года**
27 | 🔗 [Ссылка на мероприятие](https://hacks-ai.ru/events/1077382)
28 |
29 | ### 📖 Описание кейса:
30 | Разработка ИИ-прототипа для автоматического анализа видеозаписей с видеорегистраторов автотранспорта ОАО «РЖД» с целью выявления нарушений ПДД.
31 |
32 | ### 👥 Участники команды:
33 | - [Константин Кожин](https://github.com/konstantinkozhin) — **Руководитель команды;**
34 | - [Павел Шерстнев](https://github.com/sherstpasha) — **Data Analyst;**
35 | - [Владислава Жуковская](https://github.com/vlada2025) — **Дизайнер;**
36 | - [Антон Михалев](https://github.com/asmikhalev) — **ML-инженер;**
37 | - [Алина Нуриманова](https://github.com/ALENKOZAVR) — **Data Scientist.**
38 |
39 |
40 |
41 |
42 |
43 |
44 |
45 |
46 |
47 |
48 | ## 📌 Описание решения
49 |
50 | ### 🔹 Общая концепция
51 | Наша система анализирует видеозаписи с видеорегистраторов для выявления нарушений правил дорожного движения. В основе работы — комбинация моделей компьютерного зрения, которые определяют ключевые элементы дорожной сцены и фиксируют возможные нарушения. Такой подход делает систему гибкой, масштабируемой и позволяет её дообучать для повышения точности.
52 |
53 | ### ⚙️ Схема работы системы
54 | 🔹 **Разделение видео** на фрагменты по 10 секунд для последовательной обработки.
55 | 🔹 **Детекция ключевых объектов** с помощью нейросетей:
56 | - YOLOv5 — поиск автомобилей и пешеходов.
57 | - YOLOv5 — обнаружение дорожных знаков и светофоров.
58 | - SegFormer — сегментация дорожной разметки.
59 | 🔹 **Фокусировка на значимых зонах**: фоновые элементы приглушаются, что повышает точность анализа.
60 | 🔹 **Определение нарушений** с помощью XCLIP: модель анализирует обработанный фрагмент и фиксирует возможные нарушения.
61 | 🔹 **Переход к следующему фрагменту** и повторение процесса.
62 |
63 | ### 🔄 Гибкость и масштабируемость
64 | Мы разработали **модульную систему**, которая не зависит от жёстких правил и эвристик. Вместо простых триггеров (например, пересечение разметки) используется **нейросеть**, которую можно дообучать и адаптировать под новые типы нарушений. Это делает систему более точной и расширяемой.
65 |
66 | ## 🎥 Screencast (Демонстрация решения)
67 | Посмотрите, как система выявляет нарушения на реальных видеозаписях.
68 |
69 | 📌 **[Смотреть видеоролик](Screencast.mp4)**
70 |
71 |
72 |
73 |
📹 Презентация работы системы
74 |
🎯 Обработанный фрагмент (вход для XCLIP)
75 |
76 |
77 |
78 |
79 |
80 |
81 |
82 | ## 📊 Дополнительный функционал: Распознавание скорости
83 |
84 | В дополнение к выявлению нарушений, наша система использует **OCR-модель** для **распознавания показателей скорости** с видеорегистраторов. Это позволяет фиксировать еще одно важное нарушение — **превышение скорости**.
85 |
86 | 🔹 **Извлечение скорости** из видео в режиме реального времени.
87 | 🔹 **Сравнение полученных данных** с допустимыми скоростными ограничениями.
88 | 🔹 **Фиксация нарушений** при превышении установленного лимита.
89 | 🔹 **Запись статистики** по каждому фрагменту видео для дальнейшего анализа.
90 |
91 | Этот модуль расширяет возможности системы, позволяя детектировать не только визуальные нарушения, но и **анализировать скорость транспортного средства**.
92 |
93 | ---
94 |
95 |
96 |
97 |
98 |
99 | ---
100 |
101 | ## 🛠 Технологический стек
102 | - **Python** — основной язык разработки;
103 | - **PyTorch** — для построения и обучения нейросетей;
104 | - **Hugging Face** — для использования предобученных моделей (X-CLIP, AST, BERT);
105 | - **EasyOCR** — для распознавания текста на видеозаписях (скорость);
106 | - **Gradio** — для удобного развертывания и тестирования модели через веб-интерфейс;
107 | - **Docker** — для контейнеризации и развертывания системы.
108 |
109 | ---
110 |
111 | ## 🏆 Лидерборд
112 |
113 |
114 |
115 |
116 | ## 📂 Описание файлов в репозитории
117 |
118 | ### 📌 Основные файлы проекта:
119 |
120 | 1. **`FULL_LAUNCH.ipynb`**
121 | - Jupyter Notebook для полного запуска решения на тестовом датасете.
122 | - Включает загрузку данных, обработку видео, предсказания и формирование `submission.csv`.
123 |
124 | 2. **`best_model_dataset_1_39.pth`** и **`best_model_dataset_1_47.pth`**
125 | - Файлы с весами предварительно обученных моделей.
126 | - Используются при запуске ноутбука для выполнения предсказаний.
127 |
128 | 3. **`dataset.py`**
129 | - Определяет класс датасета и логику загрузки данных.
130 | - Обеспечивает корректную обработку входных видеоданных.
131 |
132 | 4. **`model.py`**
133 | - Содержит архитектуру модели, используемой для анализа видео.
134 |
135 | 5. **`train_run.py`**
136 | - Скрипт для обучения модели, включая настройку, процесс обучения и сохранение контрольных точек.
137 |
138 | ### 🔄 Полный запуск решения
139 | Файл **`FULL_LAUNCH.ipynb`** позволяет запустить всю последовательность обработки данных, включая:
140 | ✅ Задание пути к папке с видео (`video_folder`).
141 | ✅ Формирование выходного файла (`output_csv`).
142 | ✅ Автоматическое создание `submission.csv` с результатами детекции нарушений.
143 |
--------------------------------------------------------------------------------
/README_.md:
--------------------------------------------------------------------------------
1 | # hack_101124
2 | Описание файлов в репозитории:
3 |
4 | 1. **`FULL_LAUNCH.ipynb`**:
5 | - Этот Jupyter Notebook предназначен для полного запуска решения на тестовом датасете, начиная с загрузки тестовых данных и заканчивая созданием файла `submission.csv`. Он расположен в корне проекта и соответствует всем требованиям для полного запуска.
6 |
7 | 2. **`best_model_dataset_1_39.pth`** и **`best_model_dataset_1_47.pth`**:
8 | - Файлы с весами предварительно обученных моделей, которые загружаются при выполнении ноутбука для выполнения предсказаний.
9 |
10 | 3. **`dataset.py`**:
11 | - Скрипт, определяющий класс датасета и логику загрузки данных, необходимые для обработки входных данных.
12 |
13 | 4. **`model.py`**:
14 | - Скрипт с архитектурой модели, используемой в решении.
15 |
16 | 5. **`train_run.py`**:
17 | - Скрипт для обучения модели, который включает настройку модели, цикл обучения и сохранение контрольных точек.
18 |
19 |
20 | Файл **`FULL_LAUNCH.ipynb`** предназначен для запуска всей последовательности обработки данных, включая указание путей к папке с видео (`video_folder`) и выходному файлу (`output_csv`), в результате чего создается файл `submission.csv`.
21 |
--------------------------------------------------------------------------------
/Screencast.gif:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/sherstpasha/hacks_ai_rzd_violation_detection/bd6345e27f6ecfbb4e3154cb71a3a616aa14f390/Screencast.gif
--------------------------------------------------------------------------------
/Screencast.mp4:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/sherstpasha/hacks_ai_rzd_violation_detection/bd6345e27f6ecfbb4e3154cb71a3a616aa14f390/Screencast.mp4
--------------------------------------------------------------------------------
/Screencast2.gif:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/sherstpasha/hacks_ai_rzd_violation_detection/bd6345e27f6ecfbb4e3154cb71a3a616aa14f390/Screencast2.gif
--------------------------------------------------------------------------------
/best_model_dataset_1_39.pth:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/sherstpasha/hacks_ai_rzd_violation_detection/bd6345e27f6ecfbb4e3154cb71a3a616aa14f390/best_model_dataset_1_39.pth
--------------------------------------------------------------------------------
/best_model_dataset_1_47.pth:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/sherstpasha/hacks_ai_rzd_violation_detection/bd6345e27f6ecfbb4e3154cb71a3a616aa14f390/best_model_dataset_1_47.pth
--------------------------------------------------------------------------------
/dataset.py:
--------------------------------------------------------------------------------
1 | from torch.utils.data import Dataset
2 | from torchvision import transforms
3 | import cv2
4 | import numpy as np
5 | from PIL import Image
6 | import os
7 | import torch
8 | import pandas as pd
9 |
10 | label_list = [
11 | "нарушений нет",
12 | "Статья 12.16. часть 1 Несоблюдение требований, предписанных дорожными знаками или разметкой проезжей части дороги",
13 | "Статья 12.16 часть 2 Поворот налево или разворот в нарушение требований, предписанных дорожными знаками или разметкой проезжей части дороги",
14 | "Статья 12.17 часть 1.1 и 1.2. движение транспортных средств по полосе для маршрутных транспортных средств или остановка на указанной полосе в нарушение Правил дорожного движения ",
15 | "Статья 12.12 часть 2 1. невыполнение требования ПДД об остановке перед стоп-линией, обозначенной дорожными знаками или разметкой проезжей части дороги, при запрещающем сигнале светофора или запрещающем жесте регулировщика",
16 | "Статья 12.15 часть 4 Выезд в нарушение правил дорожного движения на полосу, предназначенную для встречного движения, при объезде препятствия, либо на трамвайные пути встречного направления, за исключением случаев, предусмотренных частью 3 настоящей статьи",
17 | ]
18 |
19 |
20 | # Определение класса датасета с применением обработки кадров
21 | import torch
22 | import cv2
23 | import numpy as np
24 | from PIL import Image
25 | import os
26 | from torch.utils.data import Dataset
27 | from torchvision import transforms
28 | from transformers import SegformerForSemanticSegmentation, SegformerImageProcessor
29 |
30 | label_list = [
31 | "нарушений нет",
32 | "Статья 12.16. часть 1 Несоблюдение требований, предписанных дорожными знаками или разметкой проезжей части дороги",
33 | "Статья 12.16 часть 2 Поворот налево или разворот в нарушение требований, предписанных дорожными знаками или разметкой проезжей части дороги",
34 | "Статья 12.17 часть 1.1 и 1.2. движение транспортных средств по полосе для маршрутных транспортных средств или остановка на указанной полосе в нарушение Правил дорожного движения ",
35 | "Статья 12.12 часть 2 1. невыполнение требования ПДД об остановке перед стоп-линией, обозначенной дорожными знаками или разметкой проезжей части дороги, при запрещающем сигнале светофора или запрещающем жесте регулировщика",
36 | "Статья 12.15 часть 4 Выезд в нарушение правил дорожного движения на полосу, предназначенную для встречного движения, при объезде препятствия, либо на трамвайные пути встречного направления, за исключением случаев, предусмотренных частью 3 настоящей статьи",
37 | ]
38 |
39 |
40 | # Определение класса датасета с применением обработки кадров и сохранением в папку
41 | class XCLIPVideoDataset(Dataset):
42 | def __init__(
43 | self,
44 | dataframe,
45 | video_folder,
46 | processor,
47 | num_frames=8,
48 | apply_preprocessing=False,
49 | yolo_pretrained_path=None,
50 | yolo_custom_path=None,
51 | segformer_model_path=None,
52 | ):
53 | self.data_frame = dataframe.reset_index(drop=True)
54 | self.video_folder = video_folder
55 | self.processor = processor
56 | self.num_frames = num_frames
57 | self.apply_preprocessing = (
58 | apply_preprocessing # Тумблер для применения предварительной обработки
59 | )
60 |
61 | # Загрузка моделей
62 | self.device = "cuda" if torch.cuda.is_available() else "cpu"
63 |
64 | # Загрузка модели YOLOv5 (предобученной)
65 | if yolo_pretrained_path:
66 | self.pretrained_model = (
67 | torch.hub.load(
68 | "ultralytics/yolov5",
69 | "yolov5n",
70 | pretrained=True,
71 | )
72 | .to(self.device)
73 | .eval()
74 | )
75 |
76 | # Загрузка кастомной модели YOLOv5
77 | if yolo_custom_path:
78 | self.custom_model = (
79 | torch.hub.load(
80 | "ultralytics/yolov5",
81 | "custom",
82 | path=yolo_custom_path,
83 | force_reload=True,
84 | )
85 | .to(self.device)
86 | .eval()
87 | )
88 |
89 | # Загрузка модели SegFormer
90 | if segformer_model_path:
91 | self.extractor = SegformerImageProcessor()
92 | self.segformer_model = (
93 | SegformerForSemanticSegmentation.from_pretrained(segformer_model_path)
94 | .to(self.device)
95 | .eval()
96 | )
97 |
98 | self.video_transform = transforms.Compose(
99 | [
100 | transforms.Resize((224, 224)),
101 | transforms.ToTensor(),
102 | ]
103 | )
104 |
105 | # Папка для сохранения кадров
106 | # self.save_frames_folder = "./saved_frames"
107 | # os.makedirs(self.save_frames_folder, exist_ok=True)
108 |
109 | # Параметры для обработки
110 | self.traffic_related_classes = ["car", "bus", "truck", "motorcycle", "bicycle"]
111 | self.target_class_id = 2 # Идентификатор целевого класса для SegFormer
112 |
113 | def extract_video_frames(self, video_path, num_frames):
114 | video_capture = cv2.VideoCapture(video_path)
115 | frames = []
116 | total_frames = int(video_capture.get(cv2.CAP_PROP_FRAME_COUNT))
117 | frame_interval = max(total_frames // num_frames, 1)
118 |
119 | for frame_idx in range(0, total_frames, frame_interval):
120 | video_capture.set(cv2.CAP_PROP_POS_FRAMES, frame_idx)
121 | success, frame = video_capture.read()
122 | if not success:
123 | break
124 |
125 | if self.apply_preprocessing:
126 | frame = self.apply_models_processing(frame)
127 |
128 | frame_rgb = cv2.cvtColor(frame, cv2.COLOR_BGR2RGB)
129 | frame_pil = Image.fromarray(frame_rgb)
130 | frame_tensor = self.video_transform(frame_pil)
131 | frames.append(frame_tensor)
132 |
133 | # Сохранение кадра
134 | # frame_save_path = os.path.join(
135 | # self.save_frames_folder,
136 | # f"{os.path.basename(video_path).split('.')[0]}_frame_{frame_idx}.png",
137 | # )
138 | # frame_pil.save(frame_save_path)
139 |
140 | if len(frames) >= num_frames:
141 | break
142 |
143 | video_capture.release()
144 |
145 | if len(frames) == 0:
146 | print(f"Не удалось извлечь кадры для видео {video_path}")
147 | return torch.zeros((num_frames, 3, 224, 224))
148 |
149 | while len(frames) < num_frames:
150 | frames.append(
151 | frames[-1].clone() if len(frames) > 0 else torch.zeros(3, 224, 224)
152 | )
153 |
154 | return torch.stack(frames)
155 |
156 | def apply_models_processing(self, frame):
157 | height, width, _ = frame.shape
158 |
159 | # Преобразование кадра для SegFormer
160 | rgb_frame = cv2.cvtColor(frame, cv2.COLOR_BGR2RGB)
161 | img = Image.fromarray(rgb_frame)
162 |
163 | # Получение результатов от обеих моделей YOLOv5
164 | results_pretrained = self.pretrained_model(img)
165 | results_custom = self.custom_model(img)
166 |
167 | # Объединение результатов в один DataFrame
168 | results_combined = pd.concat(
169 | [results_pretrained.pandas().xyxy[0], results_custom.pandas().xyxy[0]],
170 | ignore_index=True,
171 | )
172 |
173 | # Обработка кадра моделью SegFormer
174 | seg_map = self.predict_segformer(
175 | self.segformer_model, self.extractor, rgb_frame
176 | )
177 |
178 | # Создание маски для затемнения
179 | mask = np.zeros((height, width), dtype=np.uint8)
180 |
181 | # Добавление результатов YOLOv5 в маску
182 | for _, row in results_combined.iterrows():
183 | if row["name"] in self.traffic_related_classes or row["confidence"] > 0.25:
184 | x1 = int(max(0, row["xmin"]))
185 | y1 = int(max(0, row["ymin"]))
186 | x2 = int(min(width - 1, row["xmax"]))
187 | y2 = int(min(height - 1, row["ymax"]))
188 | mask[y1:y2, x1:x2] = 255 # Область, которую не затемняем
189 |
190 | # Добавление результатов SegFormer в маску
191 | if seg_map.shape != (height, width):
192 | seg_map_resized = cv2.resize(
193 | seg_map, (width, height), interpolation=cv2.INTER_NEAREST
194 | )
195 | else:
196 | seg_map_resized = seg_map
197 | seg_mask = np.where(seg_map_resized == self.target_class_id, 255, 0).astype(
198 | np.uint8
199 | )
200 | mask = cv2.bitwise_or(mask, seg_mask)
201 |
202 | # Создание итогового кадра с затемнением
203 | alpha_mask = cv2.merge((mask, mask, mask))
204 | frame_darkened = (frame * 0.2).astype(np.uint8)
205 | frame_result = np.where(alpha_mask == 255, frame, frame_darkened)
206 |
207 | return frame_result
208 |
209 | def predict_segformer(self, model, extractor, image):
210 | inputs = extractor(images=image, return_tensors="pt").to(self.device)
211 | with torch.no_grad():
212 | outputs = model(**inputs)
213 | logits = outputs.logits # Shape [batch_size, num_classes, height, width]
214 | segmentation = torch.argmax(logits, dim=1).squeeze(0)
215 | return segmentation.cpu().numpy()
216 |
217 | def __getitem__(self, idx):
218 | row = self.data_frame.iloc[idx]
219 | segment_id = row["id"]
220 | segment_name = row["segment_name"]
221 | label = row["violation_name"]
222 | video_path = os.path.join(self.video_folder, segment_name)
223 |
224 | video_frames_tensor = self.extract_video_frames(video_path, self.num_frames)
225 | label_id = label_list.index(label)
226 |
227 | return {
228 | "pixel_values": video_frames_tensor, # [num_frames, 3, 224, 224]
229 | "label": torch.tensor(label_id, dtype=torch.long),
230 | "segment_id": segment_id, # Добавляем идентификатор сегмента
231 | "segment_name": segment_name, # Сохраняем имя сегмента для отладки, если нужно
232 | }
233 |
234 | def __len__(self):
235 | return len(self.data_frame)
236 |
237 |
238 | # ======== Создание датасета для эмбеддингов ======== #
239 | class EmbeddingsDataset(Dataset):
240 | def __init__(self, dataframe, embeddings_dir):
241 | self.data_frame = dataframe.reset_index(drop=True)
242 | self.embeddings_dir = embeddings_dir
243 |
244 | def __len__(self):
245 | return len(self.data_frame)
246 |
247 | def __getitem__(self, idx):
248 | row = self.data_frame.iloc[idx]
249 | segment_id = row["id"]
250 | label = row["violation_name"]
251 | label_id = label_list.index(label)
252 |
253 | embedding_filename = f"{segment_id}.pt"
254 | embedding_file = os.path.join(self.embeddings_dir, embedding_filename)
255 |
256 | data = torch.load(embedding_file)
257 | embedding = data["embedding"] # Tensor размерности [projection_dim]
258 | return {"embedding": embedding, "label": label_id}
259 |
--------------------------------------------------------------------------------
/leaderboard.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/sherstpasha/hacks_ai_rzd_violation_detection/bd6345e27f6ecfbb4e3154cb71a3a616aa14f390/leaderboard.jpg
--------------------------------------------------------------------------------
/model.py:
--------------------------------------------------------------------------------
1 | import torch.nn as nn
2 |
3 |
4 | # ======== Определение и обучение классификатора ======== #
5 | # Определение модели классификатора
6 | class EmbeddingClassifier(nn.Module):
7 | def __init__(self, input_dim, num_classes):
8 | super(EmbeddingClassifier, self).__init__()
9 | self.classifier = nn.Sequential(
10 | nn.Linear(input_dim, 1024),
11 | nn.ReLU(),
12 | nn.Dropout(0.1),
13 | nn.Linear(1024, num_classes)
14 | )
15 |
16 | def forward(self, embedding):
17 | logits = self.classifier(embedding)
18 | return logits
--------------------------------------------------------------------------------
/screenshot.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/sherstpasha/hacks_ai_rzd_violation_detection/bd6345e27f6ecfbb4e3154cb71a3a616aa14f390/screenshot.jpg
--------------------------------------------------------------------------------
/speed_detection.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/sherstpasha/hacks_ai_rzd_violation_detection/bd6345e27f6ecfbb4e3154cb71a3a616aa14f390/speed_detection.jpg
--------------------------------------------------------------------------------
/speed_test.ipynb:
--------------------------------------------------------------------------------
1 | {
2 | "cells": [
3 | {
4 | "cell_type": "code",
5 | "execution_count": 2,
6 | "id": "4c5e1150-a81d-45c1-b96f-d02211af69db",
7 | "metadata": {},
8 | "outputs": [
9 | {
10 | "name": "stdout",
11 | "output_type": "stream",
12 | "text": [
13 | "Найдена скорость: '72KM/y' с уверенностью 0.51\n",
14 | "Область скорости определена: (282, 1020, 362, 1044)\n",
15 | "\n",
16 | "Кадр 356:\n",
17 | "\n",
18 | "Кадр 445:\n",
19 | "\n",
20 | "Кадр 534:\n",
21 | "Найдена скорость: '69KM/4' с уверенностью 0.81\n",
22 | "\n",
23 | "Кадр 623:\n",
24 | "Найдена скорость: '68KM/y' с уверенностью 0.53\n",
25 | "\n",
26 | "Кадр 712:\n",
27 | "\n",
28 | "Кадр 801:\n",
29 | "Найдена скорость: '76KM/y' с уверенностью 0.44\n",
30 | "\n",
31 | "Кадр 890:\n",
32 | "Найдена скорость: '79KM/y' с уверенностью 0.55\n",
33 | "\n",
34 | "Кадр 979:\n",
35 | "\n",
36 | "Кадр 1068:\n",
37 | "\n",
38 | "Кадр 1157:\n",
39 | "\n",
40 | "Кадр 1246:\n",
41 | "\n",
42 | "Кадр 1335:\n",
43 | "\n",
44 | "Кадр 1424:\n",
45 | "\n",
46 | "Кадр 1513:\n",
47 | "Найдена скорость: '63KM/y' с уверенностью 0.68\n",
48 | "\n",
49 | "Кадр 1602:\n",
50 | "\n",
51 | "Кадр 1691:\n",
52 | "Найдена скорость: '72KM/y' с уверенностью 0.53\n",
53 | "\n",
54 | "Кадр 1780:\n",
55 | "Найдена скорость: '78KM/4' с уверенностью 0.83\n",
56 | "\n",
57 | "Кадр 1869:\n",
58 | "Найдена скорость: '84KM/y' с уверенностью 0.52\n",
59 | "\n",
60 | "Кадр 1958:\n",
61 | "Найдена скорость: '86KM/y' с уверенностью 0.47\n",
62 | "\n",
63 | "Кадр 2047:\n",
64 | "Найдена скорость: '87KM/y' с уверенностью 0.60\n",
65 | "\n",
66 | "Кадр 2136:\n",
67 | "\n",
68 | "Кадр 2225:\n",
69 | "Найдена скорость: '72Km/y' с уверенностью 0.49\n",
70 | "\n",
71 | "Кадр 2314:\n",
72 | "Найдена скорость: '59Km/y' с уверенностью 0.60\n",
73 | "\n",
74 | "Кадр 2403:\n",
75 | "Найдена скорость: '55Km/y' с уверенностью 0.42\n",
76 | "\n",
77 | "Кадр 2492:\n",
78 | "\n",
79 | "Кадр 2581:\n",
80 | "Найдена скорость: '39KM/y' с уверенностью 0.51\n",
81 | "\n",
82 | "Кадр 2670:\n",
83 | "\n",
84 | "Кадр 2759:\n",
85 | "Найдена скорость: '33KM/4' с уверенностью 0.58\n",
86 | "\n",
87 | "Кадр 2848:\n",
88 | "Найдена скорость: '64Km/y' с уверенностью 0.52\n",
89 | "\n",
90 | "Кадр 2937:\n",
91 | "Найдена скорость: '94KM/4' с уверенностью 0.68\n",
92 | "\n",
93 | "Кадр 3026:\n",
94 | "Найдена скорость: '69KM/ 4' с уверенностью 0.68\n",
95 | "\n",
96 | "Кадр 3115:\n",
97 | "Найдена скорость: '19Km/4' с уверенностью 0.48\n",
98 | "\n",
99 | "Кадр 3204:\n",
100 | "\n",
101 | "Кадр 3293:\n",
102 | "Найдена скорость: '74KM}y' с уверенностью 0.55\n",
103 | "\n",
104 | "Кадр 3382:\n",
105 | "Найдена скорость: '64Km/4' с уверенностью 0.62\n",
106 | "\n",
107 | "Кадр 3471:\n",
108 | "Найдена скорость: '53KM/y' с уверенностью 0.56\n",
109 | "\n",
110 | "Кадр 3560:\n",
111 | "Найдена скорость: '39KM/4' с уверенностью 0.91\n",
112 | "\n",
113 | "Кадр 3649:\n",
114 | "Найдена скорость: '44Km/4' с уверенностью 0.50\n",
115 | "\n",
116 | "Кадр 3738:\n",
117 | "Найдена скорость: '17KM/4' с уверенностью 0.49\n",
118 | "\n",
119 | "Кадр 3827:\n",
120 | "Найдена скорость: '13KM/4' с уверенностью 0.87\n",
121 | "\n",
122 | "Кадр 3916:\n",
123 | "Найдена скорость: '25Km/y' с уверенностью 0.55\n",
124 | "\n",
125 | "Кадр 4005:\n",
126 | "Найдена скорость: '26KM/y' с уверенностью 0.55\n",
127 | "\n",
128 | "Кадр 4094:\n",
129 | "Найдена скорость: '13Km/4' с уверенностью 0.74\n",
130 | "\n",
131 | "Кадр 4183:\n",
132 | "Найдена скорость: '24Km/y' с уверенностью 0.47\n",
133 | "\n",
134 | "Кадр 4272:\n",
135 | "\n",
136 | "Кадр 4361:\n",
137 | "Найдена скорость: '42KM/4' с уверенностью 0.51\n",
138 | "\n",
139 | "Кадр 4450:\n",
140 | "Найдена скорость: '63KM/ 4' с уверенностью 0.58\n",
141 | "\n",
142 | "Кадр 4539:\n",
143 | "Найдена скорость: '83KM/y' с уверенностью 0.67\n",
144 | "\n",
145 | "Кадр 4628:\n",
146 | "Найдена скорость: '89KM/y' с уверенностью 0.50\n",
147 | "\n",
148 | "Кадр 4717:\n",
149 | "Найдена скорость: '86KM/y' с уверенностью 0.44\n",
150 | "\n",
151 | "Кадр 4806:\n",
152 | "Найдена скорость: '78KM/4' с уверенностью 0.73\n",
153 | "\n",
154 | "Кадр 4895:\n",
155 | "Найдена скорость: '76Km/y' с уверенностью 0.47\n",
156 | "\n",
157 | "Кадр 4984:\n",
158 | "Найдена скорость: '75Km/y' с уверенностью 0.72\n",
159 | "\n",
160 | "Кадр 5073:\n",
161 | "\n",
162 | "Кадр 5162:\n",
163 | "Найдена скорость: '70Km/4' с уверенностью 0.42\n",
164 | "\n",
165 | "Кадр 5251:\n",
166 | "Найдена скорость: '73KM/y' с уверенностью 0.53\n",
167 | "\n",
168 | "Кадр 5340:\n",
169 | "Найдена скорость: '74Km/y' с уверенностью 0.83\n",
170 | "\n",
171 | "Кадр 5429:\n",
172 | "\n",
173 | "Кадр 5518:\n",
174 | "Найдена скорость: '76KM/y' с уверенностью 0.43\n",
175 | "\n",
176 | "Кадр 5607:\n",
177 | "Найдена скорость: '76Km/y' с уверенностью 0.44\n",
178 | "\n",
179 | "Кадр 5696:\n",
180 | "Найдена скорость: '77Km/y' с уверенностью 0.75\n",
181 | "\n",
182 | "Кадр 5785:\n",
183 | "Найдена скорость: '73KM/y' с уверенностью 0.53\n",
184 | "\n",
185 | "Кадр 5874:\n",
186 | "Найдена скорость: '74Km/4' с уверенностью 0.41\n",
187 | "\n",
188 | "Кадр 5963:\n",
189 | "Найдена скорость: '69KM/y' с уверенностью 0.62\n",
190 | "\n",
191 | "Кадр 6052:\n",
192 | "Найдена скорость: '70Km/y' с уверенностью 0.44\n",
193 | "\n",
194 | "Кадр 6141:\n",
195 | "\n",
196 | "Кадр 6230:\n",
197 | "Найдена скорость: '68KM/4' с уверенностью 0.76\n",
198 | "\n",
199 | "Кадр 6319:\n",
200 | "\n",
201 | "Кадр 6408:\n",
202 | "Найдена скорость: '74Km/4' с уверенностью 0.58\n",
203 | "\n",
204 | "Кадр 6497:\n",
205 | "Найдена скорость: '73KM/y' с уверенностью 0.59\n",
206 | "\n",
207 | "Кадр 6586:\n",
208 | "Найдена скорость: '72Km/y' с уверенностью 0.68\n",
209 | "\n",
210 | "Кадр 6675:\n",
211 | "Найдена скорость: '70KM/4' с уверенностью 0.49\n",
212 | "\n",
213 | "Кадр 6764:\n",
214 | "\n",
215 | "Кадр 6853:\n",
216 | "Найдена скорость: '63KM/ 4' с уверенностью 0.68\n",
217 | "\n",
218 | "Кадр 6942:\n",
219 | "Найдена скорость: '63KM/ 4' с уверенностью 0.69\n",
220 | "\n",
221 | "Кадр 7031:\n",
222 | "Найдена скорость: '63KM/ 4' с уверенностью 0.74\n",
223 | "\n",
224 | "Кадр 7120:\n",
225 | "Найдена скорость: '62KM/4' с уверенностью 0.88\n",
226 | "\n",
227 | "Кадр 7209:\n",
228 | "\n",
229 | "Кадр 7298:\n",
230 | "Найдена скорость: '59Km/y' с уверенностью 0.48\n",
231 | "\n",
232 | "Кадр 7387:\n",
233 | "\n",
234 | "Кадр 7476:\n",
235 | "\n",
236 | "Кадр 7565:\n",
237 | "Найдена скорость: '47KM/4' с уверенностью 0.58\n",
238 | "\n",
239 | "Кадр 7654:\n",
240 | "\n",
241 | "Кадр 7743:\n",
242 | "Найдена скорость: '37KM/y' с уверенностью 0.70\n",
243 | "\n",
244 | "Кадр 7832:\n",
245 | "Найдена скорость: '30KM/4' с уверенностью 0.68\n",
246 | "\n",
247 | "Кадр 7921:\n",
248 | "Найдена скорость: '22KM/y' с уверенностью 0.64\n",
249 | "\n",
250 | "Кадр 8010:\n",
251 | "Найдена скорость: '18KM/4' с уверенностью 0.62\n",
252 | "\n",
253 | "Кадр 8099:\n",
254 | "Найдена скорость: '12KM/4' с уверенностью 0.48\n",
255 | "\n",
256 | "Кадр 8188:\n",
257 | "\n",
258 | "Кадр 8277:\n",
259 | "Найдена скорость: '02Km/y' с уверенностью 0.69\n",
260 | "\n",
261 | "Кадр 8366:\n",
262 | "Найдена скорость: '0 1KM/y' с уверенностью 0.51\n",
263 | "\n",
264 | "Кадр 8455:\n",
265 | "\n",
266 | "Кадр 8544:\n",
267 | "Найдена скорость: '23KM/y' с уверенностью 0.58\n",
268 | "\n",
269 | "Кадр 8633:\n",
270 | "Найдена скорость: '18KM/y' с уверенностью 0.54\n",
271 | "\n",
272 | "Кадр 8722:\n",
273 | "Найдена скорость: '03KM/4' с уверенностью 0.47\n",
274 | "\n",
275 | "Кадр 8811:\n",
276 | "\n",
277 | "Кадр 8900:\n",
278 | "\n",
279 | "Кадр 8989:\n",
280 | "Обработка видео завершена.\n"
281 | ]
282 | },
283 | {
284 | "data": {
285 | "image/png": "iVBORw0KGgoAAAANSUhEUgAABH8AAAIjCAYAAACat7c0AAAAOXRFWHRTb2Z0d2FyZQBNYXRwbG90bGliIHZlcnNpb24zLjkuMiwgaHR0cHM6Ly9tYXRwbG90bGliLm9yZy8hTgPZAAAACXBIWXMAAA9hAAAPYQGoP6dpAAC9nElEQVR4nOzdeXgTZdcG8DtNS2mBllKgLbRQVATZRFkUsSyyo7IE9GVRBBT9VIQCoqIigiKCLEUQFF9lEUEFiq8LIigFyyIiioAoIIJAoeylbF1I5/vjcZq0WZplkplk7t91cc10Mp08SYdkcnLOeQySJEkgIiIiIiIiIqKgFKL2AIiIiIiIiIiIyHcY/CEiIiIiIiIiCmIM/hARERERERERBTEGf4iIiIiIiIiIghiDP0REREREREREQYzBHyIiIiIiIiKiIMbgDxERERERERFREGPwh4iIiIiIiIgoiDH4Q0REREREREQUxBj8ISIiIiIiIiIKYgz+EBER2bFo0SIYDAb8/PPPNrcNHjwYBoMBjRo1UmFkRERERETuYfCHiIjIDX/99ReWLl2q9jCIiIiIiFwWqvYAiIiIAsnkyZMRFhaGm266Se2hEBERERG5hJk/RERELjp06BCWLl2KJ554AvHx8SVuO3fuHLp164bExESEh4cjISEBAwcOxD///FO8z5EjR2AwGGAwGPD555+X+P28vDzExMTAYDBg+vTpJW7LysrC0KFDERcXh/DwcDRs2BAffvhhiX02btwIg8GAlStX2oy7YsWKGDx4cIltOTk5SE1NRVJSEsLDw3HTTTdh6tSpKCoqshlv6fEAQKNGjdCuXTub+9+4cWOJ/e69914YDAa8+uqrbj8mZ5YuXYqWLVsiMjISMTExaNOmDdatW1d8e3Jyss1jfvzxx1G+fHmbMc6bNw8NGzZEeHg4atSogaeffho5OTkl9mnXrh0aNWqEnTt34q677kJERATq1KmDd99912Zsp0+fxqOPPoq4uDiUL18et956KxYvXmyzX1FREWbPno3GjRujfPnyqFatGrp27VpcaiifK47+yc+/u899afLvO/pX+nn8+++/8cADD6BKlSqIjIzEnXfeia+//trpfcisj2s0GlGzZk08/vjjJZ5veTyffvopXnzxRcTHx6NChQro0aMHjh07ZnPM7du3o2vXroiOjkZkZCTatm2LLVu2lNjn1VdfhcFgQPXq1VFYWFjituXLlxeP6ezZsyVu++abb5CSkoIKFSqgUqVKuPfee/H777+X2Gfw4MGoWLGizbhWrlxp83dp165dif83ALBjx47i+yciIvIVZv4QERG56PXXX0doaCief/559O/fv8RtBQUFqFSpEkaOHInY2FgcOnQIc+bMwe7du7Fnz54S+5YvXx4LFy5Er169irelp6cjLy/P5j5PnTqFO++8EwaDAcOHD0e1atXwzTff4NFHH0Vubi5SU1PdfhxXr15F27ZtkZWVhSeeeAK1atXC1q1bMW7cOJw8eRJpaWluH9OeH374AWvWrLHZ7u1jmjhxIl599VXcddddmDRpEsqVK4ft27djw4YN6Ny5s93fmTBhAj744AN8+umnJT58v/rqq5g4cSI6duyIJ598Evv378f8+fOxY8cObNmyBWFhYcX7XrhwAd27d8eDDz6I/v3747PPPsOTTz6JcuXKYejQoQCAa9euoV27dvjrr78wfPhw1KlTBytWrMDgwYORk5ODkSNHFh/v0UcfxaJFi9CtWzc89thjuH79OjIzM/Hjjz+iefPm+Oijj4r3zczMxIIFCzBr1ixUrVoVABAXF+f2c+/MiBEj0KJFixLbHnvssRI/nzp1CnfddReuXr2KESNGIDY2FosXL0aPHj2wcuVK9O7du8z76d27N0wmE65fv45t27ZhwYIFuHbtWonHC4gsO4PBgOeffx6nT59GWloaOnbsiF27diEiIgIAsGHDBnTr1g3NmjXDhAkTEBISgoULF+Kee+5BZmYmWrZsWeKYly5dwldffVVinAsXLkT58uVt/v999NFHeOSRR9ClSxdMnToVV69exfz583H33Xfj119/RXJycpmP1RXPP/+8IschIiJySiIiIiIbCxculABIO3bskCRJkg4dOiSFhoZKI0aMkCRJktq2bSs1bNjQ6TGmTZsmAZDOnj0rSZIkHT58WAIg9e/fXwoNDZWys7OL9+3QoYM0YMAACYD01ltvFW9/9NFHpYSEhOJjyPr16ydFR0dLV69elSRJkjIyMiQA0ooVK2zGUaFCBemRRx4p/vm1116TKlSoIB04cKDEfi+88IJkNBqlo0ePlhiv9XhkDRs2lNq2bVv8s3z/GRkZxdvuuOMOqVu3bhIAacKECW4/JnsOHjwohYSESL1795bMZnOJ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Inbs2IERI0agXLlyeOSRR5CdnQ0AiIuLK/F7cXFxxbeVNmXKFEycONFm+7p16xAZGanIuNevX6/IcYh8heeo723ZkgCgBQoLDRg8WLyUxsZew2OP7UGrVifVHZwP/PRTPIA7EBKSgzVrflDkmNbnably96KgIBRffZWBuLhrihxf9scfrQBUx4kTv2HNGgdfibqodu27sHt3NSxevBcnThxVZoCkiu3bxTldsaLjc5qvpRQIeJ6S7IcfagJoXuZ+ixcfwJUrB7FtWwL++9/GOHfOknLri2sZnqMUCAL5PL169apL+6ka/CkqKkLz5s3xxhtvAABuu+027N27F++++y4eeeQRj445btw4jB49uvjn3NxcJCUloXPnzoiKivJqvIWFhVi/fj06deqEMOb7kwbxHPWP1asNmD7d9mux8+fLY9q0FvjkEzN69/ZB8xoVnT0r5pK94YZodO/e3atj2TtPo6ONOHMGaNGifXFJnVImThRvdR06NEG3bo29OtbmzSHYvRvIz2+C7t0VHij51dGjou3hLbfYntN8LaVAwPOUSqtQwYCZM8veb+nSBvjll/rYt89gc5uS1zI8RykQBMN5Klc7lUXV4E9CQgIaNGhQYtstt9yCVatWAQDi/y3CP3XqFBISEor3OXXqFJo6yLcPDw9HeHi4zfawsDDF/phKHovIF3iO+o7ZDIwZY78xsSQZYDAAzz4bij59gquePidHLKtWDUFYmDJzBVifpxUqiN48BQVhivfSkWf7io8P9frYd94plr/8YkRYWBD9gXXo5L9fateq5fic5mspBQKepyRr3140sM/KcjyBQmQkcO0asG+f/dc9X1zL8BylQBDI56mr43brCj4nJwcLFy7E0KFD0aFDB7Rq1Qo9evTAhAkTsHXrVrcH2bp1a+zfv7/EtgMHDqB27doARPPn+Ph4fP/998W35+bmYvv27WjVqpXb90dE5K3MTMv00PbI9fSZmf4bkz/IDZ+rVPHN8a1n/FKSJCnX8BmwNH3es0c0p6bAlZUllpzmnYiChdEIzJ4t1g2lknoMBvHvo4+ATz5xfpxgvZYh0juXgj8nTpzAY489hoSEBLz++uu4du0amjZtig4dOiAxMREZGRno1KkTGjRogE8//dTlOx81ahR+/PFHvPHGG/jrr7+wbNkyLFiwAE8//TQAwGAwIDU1Fa+//jq+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",
286 | "text/plain": [
287 | ""
288 | ]
289 | },
290 | "metadata": {},
291 | "output_type": "display_data"
292 | },
293 | {
294 | "name": "stdout",
295 | "output_type": "stream",
296 | "text": [
297 | "Кадр 267: скорость 72 км/ч\n",
298 | "Кадр 356: скорость 71 км/ч\n",
299 | "Кадр 445: скорость 70 км/ч\n",
300 | "Кадр 534: скорость 69 км/ч\n",
301 | "Кадр 623: скорость 68 км/ч\n",
302 | "Кадр 712: скорость 72 км/ч\n",
303 | "Кадр 801: скорость 76 км/ч\n",
304 | "Кадр 890: скорость 79 км/ч\n",
305 | "Кадр 979: скорость 76 км/ч\n",
306 | "Кадр 1068: скорость 74 км/ч\n",
307 | "Кадр 1157: скорость 72 км/ч\n",
308 | "Кадр 1246: скорость 69 км/ч\n",
309 | "Кадр 1335: скорость 67 км/ч\n",
310 | "Кадр 1424: скорость 65 км/ч\n",
311 | "Кадр 1513: скорость 63 км/ч\n",
312 | "Кадр 1602: скорость 67 км/ч\n",
313 | "Кадр 1691: скорость 72 км/ч\n",
314 | "Кадр 1780: скорость 78 км/ч\n",
315 | "Кадр 1869: скорость 84 км/ч\n",
316 | "Кадр 1958: скорость 86 км/ч\n",
317 | "Кадр 2047: скорость 87 км/ч\n",
318 | "Кадр 2136: скорость 79 км/ч\n",
319 | "Кадр 2225: скорость 72 км/ч\n",
320 | "Кадр 2314: скорость 59 км/ч\n",
321 | "Кадр 2403: скорость 55 км/ч\n",
322 | "Кадр 2492: скорость 47 км/ч\n",
323 | "Кадр 2581: скорость 39 км/ч\n",
324 | "Кадр 2670: скорость 36 км/ч\n",
325 | "Кадр 2759: скорость 33 км/ч\n",
326 | "Кадр 2848: скорость 64 км/ч\n",
327 | "Кадр 2937: скорость 94 км/ч\n",
328 | "Кадр 3026: скорость 69 км/ч\n",
329 | "Кадр 3115: скорость 19 км/ч\n",
330 | "Кадр 3204: скорость 46 км/ч\n",
331 | "Кадр 3293: скорость 74 км/ч\n",
332 | "Кадр 3382: скорость 64 км/ч\n",
333 | "Кадр 3471: скорость 53 км/ч\n",
334 | "Кадр 3560: скорость 39 км/ч\n",
335 | "Кадр 3649: скорость 44 км/ч\n",
336 | "Кадр 3738: скорость 17 км/ч\n",
337 | "Кадр 3827: скорость 13 км/ч\n",
338 | "Кадр 3916: скорость 25 км/ч\n",
339 | "Кадр 4005: скорость 26 км/ч\n",
340 | "Кадр 4094: скорость 13 км/ч\n",
341 | "Кадр 4183: скорость 24 км/ч\n",
342 | "Кадр 4272: скорость 33 км/ч\n",
343 | "Кадр 4361: скорость 42 км/ч\n",
344 | "Кадр 4450: скорость 63 км/ч\n",
345 | "Кадр 4539: скорость 83 км/ч\n",
346 | "Кадр 4628: скорость 89 км/ч\n",
347 | "Кадр 4717: скорость 86 км/ч\n",
348 | "Кадр 4806: скорость 78 км/ч\n",
349 | "Кадр 4895: скорость 76 км/ч\n",
350 | "Кадр 4984: скорость 75 км/ч\n",
351 | "Кадр 5073: скорость 72 км/ч\n",
352 | "Кадр 5162: скорость 70 км/ч\n",
353 | "Кадр 5251: скорость 73 км/ч\n",
354 | "Кадр 5340: скорость 74 км/ч\n",
355 | "Кадр 5429: скорость 75 км/ч\n",
356 | "Кадр 5518: скорость 76 км/ч\n",
357 | "Кадр 5607: скорость 76 км/ч\n",
358 | "Кадр 5696: скорость 77 км/ч\n",
359 | "Кадр 5785: скорость 73 км/ч\n",
360 | "Кадр 5874: скорость 74 км/ч\n",
361 | "Кадр 5963: скорость 69 км/ч\n",
362 | "Кадр 6052: скорость 70 км/ч\n",
363 | "Кадр 6141: скорость 69 км/ч\n",
364 | "Кадр 6230: скорость 68 км/ч\n",
365 | "Кадр 6319: скорость 71 км/ч\n",
366 | "Кадр 6408: скорость 74 км/ч\n",
367 | "Кадр 6497: скорость 73 км/ч\n",
368 | "Кадр 6586: скорость 72 км/ч\n",
369 | "Кадр 6675: скорость 70 км/ч\n",
370 | "Кадр 6764: скорость 66 км/ч\n",
371 | "Кадр 6853: скорость 63 км/ч\n",
372 | "Кадр 6942: скорость 63 км/ч\n",
373 | "Кадр 7031: скорость 63 км/ч\n",
374 | "Кадр 7120: скорость 62 км/ч\n",
375 | "Кадр 7209: скорость 60 км/ч\n",
376 | "Кадр 7298: скорость 59 км/ч\n",
377 | "Кадр 7387: скорость 55 км/ч\n",
378 | "Кадр 7476: скорость 51 км/ч\n",
379 | "Кадр 7565: скорость 47 км/ч\n",
380 | "Кадр 7654: скорость 42 км/ч\n",
381 | "Кадр 7743: скорость 37 км/ч\n",
382 | "Кадр 7832: скорость 30 км/ч\n",
383 | "Кадр 7921: скорость 22 км/ч\n",
384 | "Кадр 8010: скорость 18 км/ч\n",
385 | "Кадр 8099: скорость 12 км/ч\n",
386 | "Кадр 8188: скорость 7 км/ч\n",
387 | "Кадр 8277: скорость 2 км/ч\n",
388 | "Кадр 8366: скорость 0 км/ч\n",
389 | "Кадр 8455: скорость 11 км/ч\n",
390 | "Кадр 8544: скорость 23 км/ч\n",
391 | "Кадр 8633: скорость 18 км/ч\n",
392 | "Кадр 8722: скорость 3 км/ч\n",
393 | "Кадр 8811: скорость 0 км/ч\n",
394 | "Кадр 8900: скорость 0 км/ч\n",
395 | "Кадр 8989: скорость 0 км/ч\n"
396 | ]
397 | }
398 | ],
399 | "source": [
400 | "import cv2\n",
401 | "import easyocr\n",
402 | "import re\n",
403 | "import time\n",
404 | "import torch\n",
405 | "import numpy as np\n",
406 | "import matplotlib.pyplot as plt\n",
407 | "from scipy.interpolate import interp1d\n",
408 | "\n",
409 | "# Проверка наличия GPU\n",
410 | "use_gpu = torch.cuda.is_available()\n",
411 | "\n",
412 | "# Инициализация EasyOCR reader с использованием GPU, если доступен\n",
413 | "reader = easyocr.Reader(['en'], gpu=use_gpu)\n",
414 | "\n",
415 | "# Расширенное регулярное выражение для поиска значений скорости\n",
416 | "km_pattern = re.compile(r'\\b\\d+\\s?[kK][mM](?:/y|/ч|/h)?\\b')\n",
417 | "\n",
418 | "# Координаты области, где была обнаружена скорость\n",
419 | "speed_region = None\n",
420 | "\n",
421 | "def process_frame(frame, region=None):\n",
422 | " \"\"\"\n",
423 | " Обрабатывает один кадр, считывает текст и возвращает данные о тексте.\n",
424 | " \"\"\"\n",
425 | " if region:\n",
426 | " # Обрезаем кадр до области, где была найдена скорость\n",
427 | " frame = frame[region[1]:region[3], region[0]:region[2]]\n",
428 | " \n",
429 | " results = reader.readtext(frame, detail=1)\n",
430 | " return results\n",
431 | "\n",
432 | "def find_speed_region(results):\n",
433 | " \"\"\"\n",
434 | " Ищет область, содержащую текст скорости, используя регулярное выражение.\n",
435 | " \"\"\"\n",
436 | " for (bbox, text, prob) in results:\n",
437 | " if km_pattern.search(text) and prob > 0.4:\n",
438 | " print(f\"Найдена скорость: '{text}' с уверенностью {prob:.2f}\")\n",
439 | " return bbox # Возвращаем координаты области (top_left, bottom_right)\n",
440 | " return None\n",
441 | "\n",
442 | "def normalize_speed(text):\n",
443 | " \"\"\"\n",
444 | " Приводит найденное значение скорости к числовому формату.\n",
445 | " \"\"\"\n",
446 | " match = re.search(r'\\d+', text)\n",
447 | " if match:\n",
448 | " return int(match.group())\n",
449 | " return None\n",
450 | "\n",
451 | "def interpolate_speeds(frames, speeds):\n",
452 | " \"\"\"\n",
453 | " Интерполирует пропущенные значения скорости и возвращает только положительные значения.\n",
454 | " \"\"\"\n",
455 | " valid_indices = [i for i, s in enumerate(speeds) if s is not None]\n",
456 | " valid_frames = [frames[i] for i in valid_indices]\n",
457 | " valid_speeds = [s for s in speeds if s is not None]\n",
458 | "\n",
459 | " if len(valid_frames) < 2:\n",
460 | " # Недостаточно данных для интерполяции\n",
461 | " return speeds\n",
462 | "\n",
463 | " interpolation_function = interp1d(valid_frames, valid_speeds, kind='linear', fill_value='extrapolate')\n",
464 | " interpolated_speeds = interpolation_function(frames)\n",
465 | "\n",
466 | " # Убедимся, что все значения положительные\n",
467 | " return [max(0, int(s)) for s in interpolated_speeds]\n",
468 | "\n",
469 | "def process_video(video_path):\n",
470 | " \"\"\"\n",
471 | " Обрабатывает видеофайл, извлекает текст с каждого кадра и выводит результаты в консоль.\n",
472 | " \"\"\"\n",
473 | " global speed_region\n",
474 | "\n",
475 | " cap = cv2.VideoCapture(video_path)\n",
476 | " fps = cap.get(cv2.CAP_PROP_FPS)\n",
477 | " frame_interval = int(fps * 3) # Обрабатываем кадры каждые 3 секунды\n",
478 | " frame_count = 0\n",
479 | " speeds = []\n",
480 | " frames = []\n",
481 | "\n",
482 | " while cap.isOpened():\n",
483 | " ret, frame = cap.read()\n",
484 | " if not ret:\n",
485 | " break\n",
486 | "\n",
487 | " frame_count += 1\n",
488 | "\n",
489 | " if frame_count % frame_interval == 0:\n",
490 | " if speed_region is None:\n",
491 | " # Ищем скорость на всем кадре, если область еще не найдена\n",
492 | " results = process_frame(frame)\n",
493 | " region = find_speed_region(results)\n",
494 | " if region:\n",
495 | " x1, y1 = int(region[0][0]), int(region[0][1])\n",
496 | " x2, y2 = int(region[2][0]), int(region[2][1])\n",
497 | " speed_region = (x1, y1, x2, y2)\n",
498 | " print(f\"Область скорости определена: {speed_region}\")\n",
499 | " # Нормализуем значение скорости\n",
500 | " for (_, text, _) in results:\n",
501 | " if km_pattern.search(text):\n",
502 | " speed = normalize_speed(text)\n",
503 | " speeds.append(speed)\n",
504 | " frames.append(frame_count)\n",
505 | " break\n",
506 | " else:\n",
507 | " # Ищем только в заданной области\n",
508 | " results = process_frame(frame, region=speed_region)\n",
509 | " print(f\"\\nКадр {frame_count}:\")\n",
510 | " found_speed = None\n",
511 | " for (_, text, prob) in results:\n",
512 | " if km_pattern.search(text) and prob > 0.4:\n",
513 | " found_speed = normalize_speed(text)\n",
514 | " print(f\"Найдена скорость: '{text}' с уверенностью {prob:.2f}\")\n",
515 | " break\n",
516 | " \n",
517 | " speeds.append(found_speed)\n",
518 | " frames.append(frame_count)\n",
519 | "\n",
520 | " cap.release()\n",
521 | " print('Обработка видео завершена.')\n",
522 | "\n",
523 | " # Интерполяция пропущенных значений\n",
524 | " speeds = interpolate_speeds(frames, speeds)\n",
525 | "\n",
526 | " # Построение графика скорости\n",
527 | " plt.figure(figsize=(14, 6))\n",
528 | " plt.plot([f / fps for f in frames], speeds, marker='o', linestyle='-', color='b')\n",
529 | " plt.xlabel('Время (секунды)')\n",
530 | " plt.ylabel('Скорость (км/ч)')\n",
531 | " plt.title('Изменение скорости по времени')\n",
532 | " plt.grid(True)\n",
533 | " plt.show()\n",
534 | "\n",
535 | " # Вывод интерполированных значений\n",
536 | " for f, s in zip(frames, speeds):\n",
537 | " print(f\"Кадр {f}: скорость {s} км/ч\")\n",
538 | "\n",
539 | "# Путь к видеофайлу\n",
540 | "video_path = 'AKN00048.mp4'\n",
541 | "process_video(video_path)\n"
542 | ]
543 | },
544 | {
545 | "cell_type": "code",
546 | "execution_count": null,
547 | "id": "d38914b5-0ba3-4327-b43b-4fccc3014e18",
548 | "metadata": {},
549 | "outputs": [],
550 | "source": []
551 | }
552 | ],
553 | "metadata": {
554 | "kernelspec": {
555 | "display_name": "Python 3 (ipykernel)",
556 | "language": "python",
557 | "name": "python3"
558 | },
559 | "language_info": {
560 | "codemirror_mode": {
561 | "name": "ipython",
562 | "version": 3
563 | },
564 | "file_extension": ".py",
565 | "mimetype": "text/x-python",
566 | "name": "python",
567 | "nbconvert_exporter": "python",
568 | "pygments_lexer": "ipython3",
569 | "version": "3.10.0"
570 | }
571 | },
572 | "nbformat": 4,
573 | "nbformat_minor": 5
574 | }
575 |
--------------------------------------------------------------------------------
/train_run.py:
--------------------------------------------------------------------------------
1 | import os
2 | import torch
3 | import torch.nn as nn
4 | from torch.utils.data import DataLoader
5 | import pandas as pd
6 | from transformers import XCLIPModel, XCLIPProcessor
7 | from sklearn.model_selection import train_test_split
8 | from tqdm import tqdm
9 | import numpy as np
10 | import matplotlib.pyplot as plt
11 | import seaborn as sns
12 | from sklearn.metrics import confusion_matrix, classification_report
13 | import torch.optim as optim
14 |
15 | from dataset import XCLIPVideoDataset, EmbeddingsDataset
16 | from model import EmbeddingClassifier
17 |
18 | import warnings
19 |
20 | # Игнорировать все предупреждения
21 | warnings.filterwarnings("ignore")
22 |
23 |
24 | # ======== Подготовка данных ======== #
25 | # Пути к данным
26 | csv_path = r"C:\Users\pasha\OneDrive\Рабочий стол\dataset1011_1\videos_cut\violations_summary_p.csv"
27 | video_folder = r"C:\Users\pasha\OneDrive\Рабочий стол\dataset1011_1\videos_cut"
28 | processor = XCLIPProcessor.from_pretrained("microsoft/xclip-base-patch16")
29 |
30 | # Загрузка данных
31 | data = pd.read_csv(csv_path)
32 |
33 | # Добавляем уникальный идентификатор для каждой строки
34 | data.reset_index(drop=True, inplace=True)
35 | data["id"] = data.index # Используем индекс как уникальный идентификатор
36 |
37 | label_list = data["violation_name"].unique().tolist()
38 | print(label_list)
39 |
40 |
41 | # Создание полного датасета
42 | full_dataset = XCLIPVideoDataset(
43 | data,
44 | video_folder,
45 | processor,
46 | num_frames=8,
47 | apply_preprocessing=True,
48 | yolo_custom_path=r"C:\Users\pasha\OneDrive\Рабочий стол\best_93.pt",
49 | yolo_pretrained_path=r"C:\Users\pasha\OneDrive\Рабочий стол\best_93.pt",
50 | segformer_model_path=r"C:\Users\pasha\OneDrive\Рабочий стол\model",
51 | )
52 |
53 | # Создание DataLoader для вычисления эмбеддингов
54 | dataloader = DataLoader(full_dataset, batch_size=1, shuffle=False)
55 |
56 | # ======== Загрузка модели XCLIP и установка устройства ======== #
57 | model_name = "microsoft/xclip-base-patch16"
58 | model = XCLIPModel.from_pretrained(model_name)
59 |
60 | # Устройство (CPU или GPU)
61 | device = torch.device("cuda" if torch.cuda.is_available() else "cpu")
62 | print(f"Using device: {device}")
63 |
64 | model.to(device)
65 | model.eval() # Переводим модель в режим оценки
66 |
67 | # Папка для сохранения эмбеддингов
68 | embeddings_dir = "./embeddings"
69 | os.makedirs(embeddings_dir, exist_ok=True)
70 |
71 | # Вычисление и сохранение эмбеддингов
72 | with torch.no_grad():
73 | for idx, sample in enumerate(tqdm(dataloader, desc="Processing")):
74 | pixel_values = sample["pixel_values"] # [batch_size, num_frames, 3, 224, 224]
75 | label = sample["label"] # [batch_size]
76 | segment_id = sample["segment_id"].item() # Идентификатор сегмента
77 | segment_name = sample["segment_name"][0] # Имя видеофайла (для отладки)
78 |
79 | # Создаем имя файла для эмбеддинга, используя идентификатор сегмента
80 | embedding_filename = f"{segment_id}.pt"
81 | embedding_file = os.path.join(embeddings_dir, embedding_filename)
82 |
83 | # Проверяем, существует ли файл эмбеддинга
84 | if os.path.exists(embedding_file):
85 | # Эмбеддинг уже существует, пропускаем вычисление
86 | continue
87 |
88 | # Перемещаем данные на устройство
89 | pixel_values = pixel_values.to(device)
90 |
91 | # Генерируем фиктивный текстовый ввод и перемещаем на устройство
92 | text_inputs = processor(
93 | text=[""] * pixel_values.size(0),
94 | return_tensors="pt",
95 | padding=True,
96 | truncation=True,
97 | max_length=77,
98 | )
99 | input_ids = text_inputs["input_ids"].to(device)
100 | attention_mask = text_inputs["attention_mask"].to(device)
101 |
102 | # Передаем pixel_values в модель
103 | outputs = model(
104 | pixel_values=pixel_values,
105 | input_ids=input_ids,
106 | attention_mask=attention_mask,
107 | )
108 | video_embeds = outputs.video_embeds # [batch_size, projection_dim]
109 |
110 | # Переносим эмбеддинги на CPU перед сохранением
111 | video_embeds_cpu = video_embeds.squeeze(0).cpu()
112 |
113 | # Сохраняем эмбеддинги и метку
114 | torch.save(
115 | {
116 | "embedding": video_embeds_cpu, # [projection_dim]
117 | "label": label.item(),
118 | "segment_id": segment_id,
119 | "segment_name": segment_name, # Сохраняем имя сегмента для отладки
120 | },
121 | embedding_file,
122 | )
123 |
124 |
125 | # Разделяем исходный датафрейм на обучающую и валидационную выборки
126 | train_data, val_data = train_test_split(
127 | data, test_size=0.3, stratify=data["violation_name"], random_state=42
128 | )
129 |
130 | # Создаем датасеты
131 | train_dataset = EmbeddingsDataset(train_data, embeddings_dir)
132 | val_dataset = EmbeddingsDataset(val_data, embeddings_dir)
133 |
134 |
135 | # Инициализация модели
136 | input_dim = model.config.projection_dim # Размерность эмбеддингов
137 | num_classes = len(label_list)
138 | classifier_model = EmbeddingClassifier(input_dim, num_classes)
139 |
140 | # Параметры обучения
141 | batch_size = 8
142 | num_epochs = 5000
143 | initial_learning_rate = 1e-4 # Начальный learning rate
144 |
145 | # DataLoaders
146 | train_loader = DataLoader(train_dataset, batch_size=batch_size, shuffle=True)
147 | val_loader = DataLoader(val_dataset, batch_size=batch_size)
148 |
149 | # Определение функции потерь и оптимизатора
150 | criterion = nn.CrossEntropyLoss()
151 | optimizer = optim.Adam(classifier_model.parameters(), lr=initial_learning_rate)
152 |
153 | # Добавляем scheduler для изменения learning rate
154 | scheduler = optim.lr_scheduler.ReduceLROnPlateau(
155 | optimizer, mode="min", factor=0.1, patience=5, verbose=True
156 | )
157 |
158 | # Перемещение классификатора на устройство
159 | classifier_model.to(device)
160 |
161 | # Папка для сохранения моделей
162 | models_dir = "./models"
163 | os.makedirs(models_dir, exist_ok=True)
164 |
165 | best_val_loss = float("inf")
166 | best_model_path = os.path.join(models_dir, "best_model.pth")
167 |
168 | # Добавляем параметры для ранней остановки
169 | early_stopping_patience = 100 # Количество эпох без улучшения для остановки
170 | epochs_without_improvement = 0 # Счетчик эпох без улучшения
171 |
172 | # Цикл обучения
173 | for epoch in range(num_epochs):
174 | classifier_model.train()
175 | running_loss = 0.0
176 | for batch in train_loader:
177 | embeddings = batch["embedding"].to(device)
178 | labels = batch["label"].to(device)
179 |
180 | optimizer.zero_grad()
181 | outputs = classifier_model(embeddings)
182 | loss = criterion(outputs, labels)
183 | loss.backward()
184 | optimizer.step()
185 |
186 | running_loss += loss.item() * embeddings.size(0)
187 |
188 | epoch_loss = running_loss / len(train_dataset)
189 |
190 | # Валидация
191 | classifier_model.eval()
192 | val_running_loss = 0.0 # Добавлено для валидационного лосса
193 | correct = 0
194 | total = 0
195 | with torch.no_grad():
196 | for batch in val_loader:
197 | embeddings = batch["embedding"].to(device)
198 | labels = batch["label"].to(device)
199 | outputs = classifier_model(embeddings)
200 | val_loss = criterion(outputs, labels) # Вычисляем лосс на валидации
201 | val_running_loss += val_loss.item() * embeddings.size(0)
202 | _, predicted = torch.max(outputs.data, 1)
203 | total += labels.size(0)
204 | correct += (predicted == labels).sum().item()
205 | val_loss_epoch = val_running_loss / len(val_dataset) # Средний лосс на валидации
206 | val_accuracy = correct / total
207 |
208 | # Сохранение модели при улучшении валидационной ошибки
209 | if val_loss_epoch < best_val_loss:
210 | best_val_loss = val_loss_epoch
211 | torch.save(classifier_model.state_dict(), best_model_path)
212 | print(
213 | f"Модель сохранена на {epoch + 1} эпохе с валидационной ошибкой: {val_loss_epoch:.4f}"
214 | )
215 | epochs_without_improvement = 0 # Сброс счетчика
216 | else:
217 | epochs_without_improvement += 1
218 |
219 | # Шаг scheduler
220 | scheduler.step(val_loss_epoch)
221 |
222 | # Проверка на раннюю остановку
223 | if epochs_without_improvement >= early_stopping_patience:
224 | print(
225 | f"Ранняя остановка на {epoch + 1} эпохе. Валидационная ошибка не улучшалась {early_stopping_patience} эпох."
226 | )
227 | break
228 |
229 | # Вывод информации
230 | print(
231 | f"Epoch {epoch+1}/{num_epochs}, "
232 | f"Training Loss: {epoch_loss:.4f}, "
233 | f"Validation Loss: {val_loss_epoch:.4f}, "
234 | f"Validation Accuracy: {val_accuracy * 100:.2f}%"
235 | )
236 |
237 | # Загрузка лучшей модели для оценки
238 | best_model_path = os.path.join(models_dir, "best_model.pth")
239 | classifier_model.load_state_dict(torch.load(best_model_path))
240 | classifier_model.eval()
241 |
242 | print("Лучшая модель загружена для оценки.")
243 |
244 | # ======== Оценка модели на валидационном наборе ======== #
245 | # Сбор всех предсказаний и истинных меток
246 | val_all_preds = []
247 | val_all_labels = []
248 | with torch.no_grad():
249 | for batch in val_loader:
250 | embeddings = batch["embedding"].to(device)
251 | labels = batch["label"].to(device)
252 | outputs = classifier_model(embeddings)
253 | _, predicted = torch.max(outputs.data, 1)
254 | val_all_preds.extend(predicted.cpu().numpy())
255 | val_all_labels.extend(labels.cpu().numpy())
256 |
257 | # Получаем уникальные метки, присутствующие в данных
258 | present_labels = np.unique(val_all_labels)
259 | present_label_names = [label_list[i] for i in present_labels]
260 |
261 | # Вычисление матрицы ошибок для валидационного набора
262 | cm = confusion_matrix(val_all_labels, val_all_preds, labels=present_labels)
263 | print("Classification report for validation data:")
264 | print(
265 | classification_report(
266 | val_all_labels,
267 | val_all_preds,
268 | labels=present_labels,
269 | target_names=present_label_names,
270 | )
271 | )
272 |
273 | # Отображение матрицы ошибок для валидационного набора
274 | plt.figure(figsize=(12, 10))
275 | sns.heatmap(
276 | cm,
277 | annot=True,
278 | fmt="d",
279 | cmap="Blues",
280 | xticklabels=present_label_names,
281 | yticklabels=present_label_names,
282 | )
283 | plt.ylabel("Истинные метки")
284 | plt.xlabel("Предсказанные метки")
285 | plt.title("Матрица ошибок (валидация)")
286 | plt.show()
287 |
288 | # ======== Оценка модели на обучающем наборе ======== #
289 | # Сбор всех предсказаний и истинных меток для обучающего набора
290 | train_all_preds = []
291 | train_all_labels = []
292 | with torch.no_grad():
293 | for batch in train_loader:
294 | embeddings = batch["embedding"].to(device)
295 | labels = batch["label"].to(device)
296 | outputs = classifier_model(embeddings)
297 | _, predicted = torch.max(outputs.data, 1)
298 | train_all_preds.extend(predicted.cpu().numpy())
299 | train_all_labels.extend(labels.cpu().numpy())
300 |
301 | # Получаем уникальные метки, присутствующие в данных
302 | train_present_labels = np.unique(train_all_labels)
303 | train_present_label_names = [label_list[i] for i in train_present_labels]
304 | print(train_present_label_names)
305 |
306 | # Вычисление матрицы ошибок для обучающего набора
307 | train_cm = confusion_matrix(
308 | train_all_labels, train_all_preds, labels=train_present_labels
309 | )
310 | print("Classification report for training data:")
311 | print(
312 | classification_report(
313 | train_all_labels,
314 | train_all_preds,
315 | labels=train_present_labels,
316 | target_names=train_present_label_names,
317 | )
318 | )
319 |
320 | # Отображение матрицы ошибок для обучающего набора
321 | plt.figure(figsize=(12, 10))
322 | sns.heatmap(
323 | train_cm,
324 | annot=True,
325 | fmt="d",
326 | cmap="Blues",
327 | xticklabels=train_present_label_names,
328 | yticklabels=train_present_label_names,
329 | )
330 | plt.ylabel("Истинные метки")
331 | plt.xlabel("Предсказанные метки")
332 | plt.title("Матрица ошибок (обучение)")
333 | plt.show()
334 |
--------------------------------------------------------------------------------
/whisper.ipynb:
--------------------------------------------------------------------------------
1 | {
2 | "nbformat": 4,
3 | "nbformat_minor": 0,
4 | "metadata": {
5 | "colab": {
6 | "provenance": []
7 | },
8 | "kernelspec": {
9 | "name": "python3",
10 | "display_name": "Python 3"
11 | },
12 | "language_info": {
13 | "name": "python"
14 | }
15 | },
16 | "cells": [
17 | {
18 | "cell_type": "code",
19 | "source": [
20 | "# Установка библиотек\n",
21 | "!pip install openai-whisper ffmpeg-python pydub\n"
22 | ],
23 | "metadata": {
24 | "colab": {
25 | "base_uri": "https://localhost:8080/"
26 | },
27 | "id": "RnNNBBd0oPwN",
28 | "outputId": "957102ca-b752-4620-b936-478b67b3fd77"
29 | },
30 | "execution_count": null,
31 | "outputs": [
32 | {
33 | "output_type": "stream",
34 | "name": "stdout",
35 | "text": [
36 | "Requirement already satisfied: openai-whisper in /usr/local/lib/python3.10/dist-packages (20240930)\n",
37 | "Requirement already satisfied: ffmpeg-python in /usr/local/lib/python3.10/dist-packages (0.2.0)\n",
38 | "Requirement already satisfied: pydub in /usr/local/lib/python3.10/dist-packages (0.25.1)\n",
39 | "Requirement already satisfied: numba in /usr/local/lib/python3.10/dist-packages (from openai-whisper) (0.60.0)\n",
40 | "Requirement already satisfied: numpy in /usr/local/lib/python3.10/dist-packages (from openai-whisper) (1.26.4)\n",
41 | "Requirement already satisfied: torch in /usr/local/lib/python3.10/dist-packages (from openai-whisper) (2.5.0+cu121)\n",
42 | "Requirement already satisfied: tqdm in /usr/local/lib/python3.10/dist-packages (from openai-whisper) (4.66.6)\n",
43 | "Requirement already satisfied: more-itertools in /usr/local/lib/python3.10/dist-packages (from openai-whisper) (10.5.0)\n",
44 | "Requirement already satisfied: tiktoken in /usr/local/lib/python3.10/dist-packages (from openai-whisper) (0.8.0)\n",
45 | "Requirement already satisfied: triton>=2.0.0 in /usr/local/lib/python3.10/dist-packages (from openai-whisper) (3.1.0)\n",
46 | "Requirement already satisfied: future in /usr/local/lib/python3.10/dist-packages (from ffmpeg-python) (1.0.0)\n",
47 | "Requirement already satisfied: filelock in /usr/local/lib/python3.10/dist-packages (from triton>=2.0.0->openai-whisper) (3.16.1)\n",
48 | "Requirement already satisfied: llvmlite<0.44,>=0.43.0dev0 in /usr/local/lib/python3.10/dist-packages (from numba->openai-whisper) (0.43.0)\n",
49 | "Requirement already satisfied: regex>=2022.1.18 in /usr/local/lib/python3.10/dist-packages (from tiktoken->openai-whisper) (2024.9.11)\n",
50 | "Requirement already satisfied: requests>=2.26.0 in /usr/local/lib/python3.10/dist-packages (from tiktoken->openai-whisper) (2.32.3)\n",
51 | "Requirement already satisfied: typing-extensions>=4.8.0 in /usr/local/lib/python3.10/dist-packages (from torch->openai-whisper) (4.12.2)\n",
52 | "Requirement already satisfied: networkx in /usr/local/lib/python3.10/dist-packages (from torch->openai-whisper) (3.4.2)\n",
53 | "Requirement already satisfied: jinja2 in /usr/local/lib/python3.10/dist-packages (from torch->openai-whisper) (3.1.4)\n",
54 | "Requirement already satisfied: fsspec in /usr/local/lib/python3.10/dist-packages (from torch->openai-whisper) (2024.10.0)\n",
55 | "Requirement already satisfied: sympy==1.13.1 in /usr/local/lib/python3.10/dist-packages (from torch->openai-whisper) (1.13.1)\n",
56 | "Requirement already satisfied: mpmath<1.4,>=1.1.0 in /usr/local/lib/python3.10/dist-packages (from sympy==1.13.1->torch->openai-whisper) (1.3.0)\n",
57 | "Requirement already satisfied: charset-normalizer<4,>=2 in /usr/local/lib/python3.10/dist-packages (from requests>=2.26.0->tiktoken->openai-whisper) (3.4.0)\n",
58 | "Requirement already satisfied: idna<4,>=2.5 in /usr/local/lib/python3.10/dist-packages (from requests>=2.26.0->tiktoken->openai-whisper) (3.10)\n",
59 | "Requirement already satisfied: urllib3<3,>=1.21.1 in /usr/local/lib/python3.10/dist-packages (from requests>=2.26.0->tiktoken->openai-whisper) (2.2.3)\n",
60 | "Requirement already satisfied: certifi>=2017.4.17 in /usr/local/lib/python3.10/dist-packages (from requests>=2.26.0->tiktoken->openai-whisper) (2024.8.30)\n",
61 | "Requirement already satisfied: MarkupSafe>=2.0 in /usr/local/lib/python3.10/dist-packages (from jinja2->torch->openai-whisper) (3.0.2)\n"
62 | ]
63 | }
64 | ]
65 | },
66 | {
67 | "cell_type": "code",
68 | "execution_count": null,
69 | "metadata": {
70 | "colab": {
71 | "base_uri": "https://localhost:8080/",
72 | "height": 158
73 | },
74 | "id": "bHXubBYTlcvD",
75 | "outputId": "637a9cdf-0078-488a-808f-e9214c61fd31"
76 | },
77 | "outputs": [
78 | {
79 | "output_type": "stream",
80 | "name": "stderr",
81 | "text": [
82 | "100%|████████████████████████████████████████| 139M/139M [00:01<00:00, 101MiB/s]\n",
83 | "/usr/local/lib/python3.10/dist-packages/whisper/__init__.py:150: FutureWarning: You are using `torch.load` with `weights_only=False` (the current default value), which uses the default pickle module implicitly. It is possible to construct malicious pickle data which will execute arbitrary code during unpickling (See https://github.com/pytorch/pytorch/blob/main/SECURITY.md#untrusted-models for more details). In a future release, the default value for `weights_only` will be flipped to `True`. This limits the functions that could be executed during unpickling. Arbitrary objects will no longer be allowed to be loaded via this mode unless they are explicitly allowlisted by the user via `torch.serialization.add_safe_globals`. We recommend you start setting `weights_only=True` for any use case where you don't have full control of the loaded file. Please open an issue on GitHub for any issues related to this experimental feature.\n",
84 | " checkpoint = torch.load(fp, map_location=device)\n",
85 | "/usr/local/lib/python3.10/dist-packages/whisper/transcribe.py:126: UserWarning: FP16 is not supported on CPU; using FP32 instead\n",
86 | " warnings.warn(\"FP16 is not supported on CPU; using FP32 instead\")\n"
87 | ]
88 | },
89 | {
90 | "output_type": "stream",
91 | "name": "stdout",
92 | "text": [
93 | "Транскрибация речи:\n",
94 | " Проверяю по ребрику. Показываю поворотник. Ставай на тормоз. Садок тормозим! Тормозимать твою! Сура! Блин, тормозим! Взравляю!\n"
95 | ]
96 | },
97 | {
98 | "output_type": "display_data",
99 | "data": {
100 | "text/plain": [
101 | ""
102 | ],
103 | "application/javascript": [
104 | "\n",
105 | " async function download(id, filename, size) {\n",
106 | " if (!google.colab.kernel.accessAllowed) {\n",
107 | " return;\n",
108 | " }\n",
109 | " const div = document.createElement('div');\n",
110 | " const label = document.createElement('label');\n",
111 | " label.textContent = `Downloading \"${filename}\": `;\n",
112 | " div.appendChild(label);\n",
113 | " const progress = document.createElement('progress');\n",
114 | " progress.max = size;\n",
115 | " div.appendChild(progress);\n",
116 | " document.body.appendChild(div);\n",
117 | "\n",
118 | " const buffers = [];\n",
119 | " let downloaded = 0;\n",
120 | "\n",
121 | " const channel = await google.colab.kernel.comms.open(id);\n",
122 | " // Send a message to notify the kernel that we're ready.\n",
123 | " channel.send({})\n",
124 | "\n",
125 | " for await (const message of channel.messages) {\n",
126 | " // Send a message to notify the kernel that we're ready.\n",
127 | " channel.send({})\n",
128 | " if (message.buffers) {\n",
129 | " for (const buffer of message.buffers) {\n",
130 | " buffers.push(buffer);\n",
131 | " downloaded += buffer.byteLength;\n",
132 | " progress.value = downloaded;\n",
133 | " }\n",
134 | " }\n",
135 | " }\n",
136 | " const blob = new Blob(buffers, {type: 'application/binary'});\n",
137 | " const a = document.createElement('a');\n",
138 | " a.href = window.URL.createObjectURL(blob);\n",
139 | " a.download = filename;\n",
140 | " div.appendChild(a);\n",
141 | " a.click();\n",
142 | " div.remove();\n",
143 | " }\n",
144 | " "
145 | ]
146 | },
147 | "metadata": {}
148 | },
149 | {
150 | "output_type": "display_data",
151 | "data": {
152 | "text/plain": [
153 | ""
154 | ],
155 | "application/javascript": [
156 | "download(\"download_ae0475a8-6963-478b-9cde-b1b43df155cc\", \"transcription.txt\", 229)"
157 | ]
158 | },
159 | "metadata": {}
160 | }
161 | ],
162 | "source": [
163 | "import whisper\n",
164 | "import ffmpeg\n",
165 | "import os\n",
166 | "\n",
167 | "# Укажите путь к вашему видеофайлу (например, '/content/ваше_видео.mp4')\n",
168 | "video_path = '1.mp4'\n",
169 | "\n",
170 | "# Путь для сохранения извлеченного аудио\n",
171 | "audio_file = 'extracted_audio.mp3'\n",
172 | "\n",
173 | "# Преобразование видео в аудиоформат с использованием ffmpeg\n",
174 | "ffmpeg.input(video_path).output(audio_file, format='mp3').run(overwrite_output=True)\n",
175 | "\n",
176 | "# Загрузка модели Whisper для транскрибации\n",
177 | "model = whisper.load_model('base') # Можно заменить на другие размеры модели: 'small', 'medium', 'large'\n",
178 | "\n",
179 | "# Транскрибация аудио\n",
180 | "result = model.transcribe(audio_file)\n",
181 | "\n",
182 | "# Печать результата транскрибации\n",
183 | "print(\"Транскрибация речи:\")\n",
184 | "print(result['text'])\n",
185 | "\n",
186 | "# Сохранение результата в текстовый файл\n",
187 | "with open('transcription.txt', 'w') as f:\n",
188 | " f.write(result['text'])\n",
189 | "\n",
190 | "# Автоматическое скачивание файла с транскрибацией (если вы используете Colab)\n",
191 | "from google.colab import files\n",
192 | "files.download('transcription.txt')\n",
193 | "\n",
194 | "# Удаление временного аудиофайла\n",
195 | "os.remove(audio_file)"
196 | ]
197 | },
198 | {
199 | "cell_type": "code",
200 | "source": [],
201 | "metadata": {
202 | "id": "vDw1aOcSq1Yn"
203 | },
204 | "execution_count": null,
205 | "outputs": []
206 | }
207 | ]
208 | }
--------------------------------------------------------------------------------