├── FULL_LAUNCH.ipynb ├── README.md ├── README_.md ├── Screencast.gif ├── Screencast.mp4 ├── Screencast2.gif ├── best_model_dataset_1_39.pth ├── best_model_dataset_1_47.pth ├── dataset.py ├── leaderboard.jpg ├── model.py ├── screenshot.jpg ├── speed_detection.jpg ├── speed_test.ipynb ├── train_run.py └── whisper.ipynb /FULL_LAUNCH.ipynb: -------------------------------------------------------------------------------- 1 | { 2 | "cells": [ 3 | { 4 | "cell_type": "code", 5 | "execution_count": 1, 6 | "metadata": {}, 7 | "outputs": [ 8 | { 9 | "name": "stdout", 10 | "output_type": "stream", 11 | "text": [ 12 | "Requirement already satisfied: gdown in c:\\users\\pasha\\appdata\\local\\programs\\python\\python311\\lib\\site-packages (5.2.0)\n", 13 | "Requirement already satisfied: beautifulsoup4 in c:\\users\\pasha\\appdata\\local\\programs\\python\\python311\\lib\\site-packages (from gdown) (4.12.3)\n", 14 | "Requirement already satisfied: filelock in c:\\users\\pasha\\appdata\\local\\programs\\python\\python311\\lib\\site-packages (from gdown) (3.13.4)\n", 15 | "Requirement already satisfied: requests[socks] in c:\\users\\pasha\\appdata\\local\\programs\\python\\python311\\lib\\site-packages (from gdown) (2.32.3)\n", 16 | "Requirement already satisfied: tqdm in c:\\users\\pasha\\appdata\\local\\programs\\python\\python311\\lib\\site-packages (from gdown) (4.66.4)\n", 17 | "Requirement already satisfied: soupsieve>1.2 in c:\\users\\pasha\\appdata\\local\\programs\\python\\python311\\lib\\site-packages (from beautifulsoup4->gdown) (2.6)\n", 18 | "Requirement already satisfied: charset-normalizer<4,>=2 in c:\\users\\pasha\\appdata\\local\\programs\\python\\python311\\lib\\site-packages (from requests[socks]->gdown) (3.3.2)\n", 19 | "Requirement already satisfied: idna<4,>=2.5 in c:\\users\\pasha\\appdata\\local\\programs\\python\\python311\\lib\\site-packages (from requests[socks]->gdown) (2.10)\n", 20 | "Requirement already satisfied: urllib3<3,>=1.21.1 in c:\\users\\pasha\\appdata\\local\\programs\\python\\python311\\lib\\site-packages (from requests[socks]->gdown) (2.1.0)\n", 21 | "Requirement already satisfied: certifi>=2017.4.17 in c:\\users\\pasha\\appdata\\local\\programs\\python\\python311\\lib\\site-packages (from requests[socks]->gdown) (2023.11.17)\n", 22 | "Requirement already satisfied: PySocks!=1.5.7,>=1.5.6 in c:\\users\\pasha\\appdata\\local\\programs\\python\\python311\\lib\\site-packages (from requests[socks]->gdown) (1.7.1)\n", 23 | "Requirement already satisfied: colorama in c:\\users\\pasha\\appdata\\local\\programs\\python\\python311\\lib\\site-packages (from tqdm->gdown) (0.4.6)\n" 24 | ] 25 | }, 26 | { 27 | "name": "stderr", 28 | "output_type": "stream", 29 | "text": [ 30 | "\n", 31 | "[notice] A new release of pip available: 22.3 -> 24.3.1\n", 32 | "[notice] To update, run: python.exe -m pip install --upgrade pip\n" 33 | ] 34 | } 35 | ], 36 | "source": [ 37 | "!pip install gdown" 38 | ] 39 | }, 40 | { 41 | "cell_type": "code", 42 | "execution_count": 2, 43 | "metadata": {}, 44 | "outputs": [ 45 | { 46 | "name": "stderr", 47 | "output_type": "stream", 48 | "text": [ 49 | "c:\\Users\\pasha\\AppData\\Local\\Programs\\Python\\Python311\\Lib\\site-packages\\tqdm\\auto.py:21: TqdmWarning: IProgress not found. Please update jupyter and ipywidgets. See https://ipywidgets.readthedocs.io/en/stable/user_install.html\n", 50 | " from .autonotebook import tqdm as notebook_tqdm\n" 51 | ] 52 | }, 53 | { 54 | "name": "stdout", 55 | "output_type": "stream", 56 | "text": [ 57 | "Файл best_93.pt уже существует.\n", 58 | "Файл segformer_model уже существует.\n", 59 | "Используется устройство: cuda\n" 60 | ] 61 | }, 62 | { 63 | "name": "stderr", 64 | "output_type": "stream", 65 | "text": [ 66 | "Downloading: \"https://github.com/ultralytics/yolov5/zipball/master\" to C:\\Users\\pasha/.cache\\torch\\hub\\master.zip\n", 67 | "YOLOv5 2024-11-10 Python-3.11.0 torch-2.0.1+cu117 CUDA:0 (NVIDIA GeForce RTX 4060, 8188MiB)\n", 68 | "\n", 69 | "Fusing layers... \n", 70 | "YOLOv5s summary: 224 layers, 7167184 parameters, 0 gradients\n", 71 | "Adding AutoShape... \n", 72 | "Using cache found in C:\\Users\\pasha/.cache\\torch\\hub\\ultralytics_yolov5_master\n", 73 | "YOLOv5 2024-11-10 Python-3.11.0 torch-2.0.1+cu117 CUDA:0 (NVIDIA GeForce RTX 4060, 8188MiB)\n", 74 | "\n", 75 | "Fusing layers... \n", 76 | "YOLOv5n summary: 213 layers, 1867405 parameters, 0 gradients\n", 77 | "Adding AutoShape... \n" 78 | ] 79 | }, 80 | { 81 | "name": "stdout", 82 | "output_type": "stream", 83 | "text": [ 84 | "Видео akn00005_0xsmsNYD.mov длительностью 300.03 секунд, FPS: 29.97002997002997\n", 85 | "5\n", 86 | "Сегмент 1/31, время: 0.00-10.00 сек, Класс: Статья 12.15 часть 4 Выезд в нарушение правил дорожного движения на полосу, предназначенную для встречного движения, при объезде препятствия, либо на трамвайные пути встречного направления, за исключением случаев, предусмотренных частью 3 настоящей статьи, Доверие: 0.3878, Время нарушения: 5 сек\n", 87 | "0\n", 88 | "Сегмент 2/31, время: 10.00-20.00 сек, Класс: Статья 12.16. часть 1 Несоблюдение требований, предписанных дорожными знаками или разметкой проезжей части дороги, Доверие: 0.5259, Время нарушения: 15 сек\n", 89 | "1\n", 90 | "Сегмент 3/31, время: 20.00-30.00 сек, Класс: Статья 12.16 часть 2 Поворот налево или разворот в нарушение требований, предписанных дорожными знаками или разметкой проезжей части дороги, Доверие: 0.3473, Время нарушения: 25 сек\n", 91 | "1\n", 92 | "Сегмент 4/31, время: 30.00-40.00 сек, Класс: Статья 12.16 часть 2 Поворот налево или разворот в нарушение требований, предписанных дорожными знаками или разметкой проезжей части дороги, Доверие: 0.3527, Время нарушения: 35 сек\n", 93 | "0\n", 94 | "Сегмент 5/31, время: 40.00-50.00 сек, Класс: Статья 12.16. часть 1 Несоблюдение требований, предписанных дорожными знаками или разметкой проезжей части дороги, Доверие: 0.3889, Время нарушения: 45 сек\n", 95 | "0\n", 96 | "Сегмент 6/31, время: 50.00-60.00 сек, Класс: Статья 12.16. часть 1 Несоблюдение требований, предписанных дорожными знаками или разметкой проезжей части дороги, Доверие: 0.4889, Время нарушения: 55 сек\n", 97 | "1\n", 98 | "Сегмент 7/31, время: 60.00-70.00 сек, Класс: Статья 12.16 часть 2 Поворот налево или разворот в нарушение требований, предписанных дорожными знаками или разметкой проезжей части дороги, Доверие: 0.4933, Время нарушения: 65 сек\n", 99 | "1\n", 100 | "Сегмент 8/31, время: 70.00-80.00 сек, Класс: Статья 12.16 часть 2 Поворот налево или разворот в нарушение требований, предписанных дорожными знаками или разметкой проезжей части дороги, Доверие: 0.5087, Время нарушения: 75 сек\n", 101 | "1\n", 102 | "Сегмент 9/31, время: 80.00-90.00 сек, Класс: Статья 12.16 часть 2 Поворот налево или разворот в нарушение требований, предписанных дорожными знаками или разметкой проезжей части дороги, Доверие: 0.5030, Время нарушения: 85 сек\n", 103 | "1\n", 104 | "Сегмент 10/31, время: 90.00-100.00 сек, Класс: Статья 12.16 часть 2 Поворот налево или разворот в нарушение требований, предписанных дорожными знаками или разметкой проезжей части дороги, Доверие: 0.4305, Время нарушения: 95 сек\n", 105 | "0\n", 106 | "Сегмент 11/31, время: 100.00-110.00 сек, Класс: Статья 12.16. часть 1 Несоблюдение требований, предписанных дорожными знаками или разметкой проезжей части дороги, Доверие: 0.3868, Время нарушения: 105 сек\n", 107 | "0\n", 108 | "Сегмент 12/31, время: 110.00-120.00 сек, Класс: Статья 12.16. часть 1 Несоблюдение требований, предписанных дорожными знаками или разметкой проезжей части дороги, Доверие: 0.4563, Время нарушения: 115 сек\n", 109 | "0\n", 110 | "Сегмент 13/31, время: 120.00-130.00 сек, Класс: Статья 12.16. часть 1 Несоблюдение требований, предписанных дорожными знаками или разметкой проезжей части дороги, Доверие: 0.5209, Время нарушения: 125 сек\n", 111 | "0\n", 112 | "Сегмент 14/31, время: 130.00-140.00 сек, Класс: Статья 12.16. часть 1 Несоблюдение требований, предписанных дорожными знаками или разметкой проезжей части дороги, Доверие: 0.5159, Время нарушения: 135 сек\n", 113 | "0\n", 114 | "Сегмент 15/31, время: 140.00-150.00 сек, Класс: Статья 12.16. часть 1 Несоблюдение требований, предписанных дорожными знаками или разметкой проезжей части дороги, Доверие: 0.4812, Время нарушения: 145 сек\n", 115 | "0\n", 116 | "Сегмент 16/31, время: 150.00-160.00 сек, Класс: Статья 12.16. часть 1 Несоблюдение требований, предписанных дорожными знаками или разметкой проезжей части дороги, Доверие: 0.4979, Время нарушения: 155 сек\n", 117 | "0\n", 118 | "Сегмент 17/31, время: 160.00-170.00 сек, Класс: Статья 12.16. часть 1 Несоблюдение требований, предписанных дорожными знаками или разметкой проезжей части дороги, Доверие: 0.5302, Время нарушения: 165 сек\n", 119 | "0\n", 120 | "Сегмент 18/31, время: 170.00-180.00 сек, Класс: Статья 12.16. часть 1 Несоблюдение требований, предписанных дорожными знаками или разметкой проезжей части дороги, Доверие: 0.4017, Время нарушения: 175 сек\n", 121 | "0\n", 122 | "Сегмент 19/31, время: 180.00-190.00 сек, Класс: Статья 12.16. часть 1 Несоблюдение требований, предписанных дорожными знаками или разметкой проезжей части дороги, Доверие: 0.4069, Время нарушения: 185 сек\n", 123 | "1\n", 124 | "Сегмент 20/31, время: 190.00-200.00 сек, Класс: Статья 12.16 часть 2 Поворот налево или разворот в нарушение требований, предписанных дорожными знаками или разметкой проезжей части дороги, Доверие: 0.4835, Время нарушения: 195 сек\n", 125 | "1\n", 126 | "Сегмент 21/31, время: 200.00-210.00 сек, Класс: Статья 12.16 часть 2 Поворот налево или разворот в нарушение требований, предписанных дорожными знаками или разметкой проезжей части дороги, Доверие: 0.4597, Время нарушения: 205 сек\n", 127 | "1\n", 128 | "Сегмент 22/31, время: 210.00-220.00 сек, Класс: Статья 12.16 часть 2 Поворот налево или разворот в нарушение требований, предписанных дорожными знаками или разметкой проезжей части дороги, Доверие: 0.4429, Время нарушения: 215 сек\n", 129 | "1\n", 130 | "Сегмент 23/31, время: 220.00-230.00 сек, Класс: Статья 12.16 часть 2 Поворот налево или разворот в нарушение требований, предписанных дорожными знаками или разметкой проезжей части дороги, Доверие: 0.4322, Время нарушения: 225 сек\n", 131 | "1\n", 132 | "Сегмент 24/31, время: 230.00-240.00 сек, Класс: Статья 12.16 часть 2 Поворот налево или разворот в нарушение требований, предписанных дорожными знаками или разметкой проезжей части дороги, Доверие: 0.4210, Время нарушения: 235 сек\n", 133 | "1\n", 134 | "Сегмент 25/31, время: 240.00-250.00 сек, Класс: Статья 12.16 часть 2 Поворот налево или разворот в нарушение требований, предписанных дорожными знаками или разметкой проезжей части дороги, Доверие: 0.4239, Время нарушения: 245 сек\n", 135 | "1\n", 136 | "Сегмент 26/31, время: 250.00-260.00 сек, Класс: Статья 12.16 часть 2 Поворот налево или разворот в нарушение требований, предписанных дорожными знаками или разметкой проезжей части дороги, Доверие: 0.4032, Время нарушения: 255 сек\n", 137 | "1\n", 138 | "Сегмент 27/31, время: 260.00-270.00 сек, Класс: Статья 12.16 часть 2 Поворот налево или разворот в нарушение требований, предписанных дорожными знаками или разметкой проезжей части дороги, Доверие: 0.3814, Время нарушения: 265 сек\n", 139 | "1\n", 140 | "Сегмент 28/31, время: 270.00-280.00 сек, Класс: Статья 12.16 часть 2 Поворот налево или разворот в нарушение требований, предписанных дорожными знаками или разметкой проезжей части дороги, Доверие: 0.5370, Время нарушения: 275 сек\n", 141 | "1\n", 142 | "Сегмент 29/31, время: 280.00-290.00 сек, Класс: Статья 12.16 часть 2 Поворот налево или разворот в нарушение требований, предписанных дорожными знаками или разметкой проезжей части дороги, Доверие: 0.5348, Время нарушения: 285 сек\n", 143 | "1\n", 144 | "Сегмент 30/31, время: 290.00-300.00 сек, Класс: Статья 12.16 часть 2 Поворот налево или разворот в нарушение требований, предписанных дорожными знаками или разметкой проезжей части дороги, Доверие: 0.5098, Время нарушения: 295 сек\n", 145 | "1\n", 146 | "Сегмент 31/31, время: 300.00-300.03 сек, Класс: Статья 12.16 часть 2 Поворот налево или разворот в нарушение требований, предписанных дорожными знаками или разметкой проезжей части дороги, Доверие: 0.5960, Время нарушения: 300 сек\n", 147 | "Видео akn00007_3C5sneri.mov длительностью 300.03 секунд, FPS: 29.97002997002997\n", 148 | "1\n", 149 | "Сегмент 1/31, время: 0.00-10.00 сек, Класс: Статья 12.16 часть 2 Поворот налево или разворот в нарушение требований, предписанных дорожными знаками или разметкой проезжей части дороги, Доверие: 0.5993, Время нарушения: 5 сек\n", 150 | "1\n", 151 | "Сегмент 2/31, время: 10.00-20.00 сек, Класс: Статья 12.16 часть 2 Поворот налево или разворот в нарушение требований, предписанных дорожными знаками или разметкой проезжей части дороги, Доверие: 0.5595, Время нарушения: 15 сек\n", 152 | "1\n", 153 | "Сегмент 3/31, время: 20.00-30.00 сек, Класс: Статья 12.16 часть 2 Поворот налево или разворот в нарушение требований, предписанных дорожными знаками или разметкой проезжей части дороги, Доверие: 0.4780, Время нарушения: 25 сек\n", 154 | "1\n", 155 | "Сегмент 4/31, время: 30.00-40.00 сек, Класс: Статья 12.16 часть 2 Поворот налево или разворот в нарушение требований, предписанных дорожными знаками или разметкой проезжей части дороги, Доверие: 0.4534, Время нарушения: 35 сек\n", 156 | "1\n", 157 | "Сегмент 5/31, время: 40.00-50.00 сек, Класс: Статья 12.16 часть 2 Поворот налево или разворот в нарушение требований, предписанных дорожными знаками или разметкой проезжей части дороги, Доверие: 0.5542, Время нарушения: 45 сек\n", 158 | "1\n", 159 | "Сегмент 6/31, время: 50.00-60.00 сек, Класс: Статья 12.16 часть 2 Поворот налево или разворот в нарушение требований, предписанных дорожными знаками или разметкой проезжей части дороги, Доверие: 0.5005, Время нарушения: 55 сек\n", 160 | "1\n", 161 | "Сегмент 7/31, время: 60.00-70.00 сек, Класс: Статья 12.16 часть 2 Поворот налево или разворот в нарушение требований, предписанных дорожными знаками или разметкой проезжей части дороги, Доверие: 0.5303, Время нарушения: 65 сек\n", 162 | "1\n", 163 | "Сегмент 8/31, время: 70.00-80.00 сек, Класс: Статья 12.16 часть 2 Поворот налево или разворот в нарушение требований, предписанных дорожными знаками или разметкой проезжей части дороги, Доверие: 0.5477, Время нарушения: 75 сек\n", 164 | "1\n", 165 | "Сегмент 9/31, время: 80.00-90.00 сек, Класс: Статья 12.16 часть 2 Поворот налево или разворот в нарушение требований, предписанных дорожными знаками или разметкой проезжей части дороги, Доверие: 0.5318, Время нарушения: 85 сек\n", 166 | "1\n", 167 | "Сегмент 10/31, время: 90.00-100.00 сек, Класс: Статья 12.16 часть 2 Поворот налево или разворот в нарушение требований, предписанных дорожными знаками или разметкой проезжей части дороги, Доверие: 0.5054, Время нарушения: 95 сек\n", 168 | "1\n", 169 | "Сегмент 11/31, время: 100.00-110.00 сек, Класс: Статья 12.16 часть 2 Поворот налево или разворот в нарушение требований, предписанных дорожными знаками или разметкой проезжей части дороги, Доверие: 0.5322, Время нарушения: 105 сек\n", 170 | "1\n", 171 | "Сегмент 12/31, время: 110.00-120.00 сек, Класс: Статья 12.16 часть 2 Поворот налево или разворот в нарушение требований, предписанных дорожными знаками или разметкой проезжей части дороги, Доверие: 0.5211, Время нарушения: 115 сек\n", 172 | "1\n", 173 | "Сегмент 13/31, время: 120.00-130.00 сек, Класс: Статья 12.16 часть 2 Поворот налево или разворот в нарушение требований, предписанных дорожными знаками или разметкой проезжей части дороги, Доверие: 0.5379, Время нарушения: 125 сек\n", 174 | "1\n", 175 | "Сегмент 14/31, время: 130.00-140.00 сек, Класс: Статья 12.16 часть 2 Поворот налево или разворот в нарушение требований, предписанных дорожными знаками или разметкой проезжей части дороги, Доверие: 0.5494, Время нарушения: 135 сек\n", 176 | "1\n", 177 | "Сегмент 15/31, время: 140.00-150.00 сек, Класс: Статья 12.16 часть 2 Поворот налево или разворот в нарушение требований, предписанных дорожными знаками или разметкой проезжей части дороги, Доверие: 0.5612, Время нарушения: 145 сек\n", 178 | "1\n", 179 | "Сегмент 16/31, время: 150.00-160.00 сек, Класс: Статья 12.16 часть 2 Поворот налево или разворот в нарушение требований, предписанных дорожными знаками или разметкой проезжей части дороги, Доверие: 0.5335, Время нарушения: 155 сек\n", 180 | "1\n", 181 | "Сегмент 17/31, время: 160.00-170.00 сек, Класс: Статья 12.16 часть 2 Поворот налево или разворот в нарушение требований, предписанных дорожными знаками или разметкой проезжей части дороги, Доверие: 0.5787, Время нарушения: 165 сек\n", 182 | "1\n", 183 | "Сегмент 18/31, время: 170.00-180.00 сек, Класс: Статья 12.16 часть 2 Поворот налево или разворот в нарушение требований, предписанных дорожными знаками или разметкой проезжей части дороги, Доверие: 0.5743, Время нарушения: 175 сек\n", 184 | "1\n", 185 | "Сегмент 19/31, время: 180.00-190.00 сек, Класс: Статья 12.16 часть 2 Поворот налево или разворот в нарушение требований, предписанных дорожными знаками или разметкой проезжей части дороги, Доверие: 0.5324, Время нарушения: 185 сек\n", 186 | "1\n", 187 | "Сегмент 20/31, время: 190.00-200.00 сек, Класс: Статья 12.16 часть 2 Поворот налево или разворот в нарушение требований, предписанных дорожными знаками или разметкой проезжей части дороги, Доверие: 0.5358, Время нарушения: 195 сек\n", 188 | "1\n", 189 | "Сегмент 21/31, время: 200.00-210.00 сек, Класс: Статья 12.16 часть 2 Поворот налево или разворот в нарушение требований, предписанных дорожными знаками или разметкой проезжей части дороги, Доверие: 0.4825, Время нарушения: 205 сек\n", 190 | "1\n", 191 | "Сегмент 22/31, время: 210.00-220.00 сек, Класс: Статья 12.16 часть 2 Поворот налево или разворот в нарушение требований, предписанных дорожными знаками или разметкой проезжей части дороги, Доверие: 0.4605, Время нарушения: 215 сек\n", 192 | "1\n", 193 | "Сегмент 23/31, время: 220.00-230.00 сек, Класс: Статья 12.16 часть 2 Поворот налево или разворот в нарушение требований, предписанных дорожными знаками или разметкой проезжей части дороги, Доверие: 0.5401, Время нарушения: 225 сек\n", 194 | "1\n", 195 | "Сегмент 24/31, время: 230.00-240.00 сек, Класс: Статья 12.16 часть 2 Поворот налево или разворот в нарушение требований, предписанных дорожными знаками или разметкой проезжей части дороги, Доверие: 0.5783, Время нарушения: 235 сек\n", 196 | "1\n", 197 | "Сегмент 25/31, время: 240.00-250.00 сек, Класс: Статья 12.16 часть 2 Поворот налево или разворот в нарушение требований, предписанных дорожными знаками или разметкой проезжей части дороги, Доверие: 0.5065, Время нарушения: 245 сек\n", 198 | "1\n", 199 | "Сегмент 26/31, время: 250.00-260.00 сек, Класс: Статья 12.16 часть 2 Поворот налево или разворот в нарушение требований, предписанных дорожными знаками или разметкой проезжей части дороги, Доверие: 0.5429, Время нарушения: 255 сек\n", 200 | "1\n", 201 | "Сегмент 27/31, время: 260.00-270.00 сек, Класс: Статья 12.16 часть 2 Поворот налево или разворот в нарушение требований, предписанных дорожными знаками или разметкой проезжей части дороги, Доверие: 0.4804, Время нарушения: 265 сек\n", 202 | "1\n", 203 | "Сегмент 28/31, время: 270.00-280.00 сек, Класс: Статья 12.16 часть 2 Поворот налево или разворот в нарушение требований, предписанных дорожными знаками или разметкой проезжей части дороги, Доверие: 0.5169, Время нарушения: 275 сек\n", 204 | "1\n", 205 | "Сегмент 29/31, время: 280.00-290.00 сек, Класс: Статья 12.16 часть 2 Поворот налево или разворот в нарушение требований, предписанных дорожными знаками или разметкой проезжей части дороги, Доверие: 0.5092, Время нарушения: 285 сек\n", 206 | "1\n", 207 | "Сегмент 30/31, время: 290.00-300.00 сек, Класс: Статья 12.16 часть 2 Поворот налево или разворот в нарушение требований, предписанных дорожными знаками или разметкой проезжей части дороги, Доверие: 0.5198, Время нарушения: 295 сек\n", 208 | "1\n", 209 | "Сегмент 31/31, время: 300.00-300.03 сек, Класс: Статья 12.16 часть 2 Поворот налево или разворот в нарушение требований, предписанных дорожными знаками или разметкой проезжей части дороги, Доверие: 0.3114, Время нарушения: 300 сек\n", 210 | "Видео akn00083_65POhPKk.mov длительностью 300.03 секунд, FPS: 29.97002997002997\n", 211 | "1\n", 212 | "Сегмент 1/31, время: 0.00-10.00 сек, Класс: Статья 12.16 часть 2 Поворот налево или разворот в нарушение требований, предписанных дорожными знаками или разметкой проезжей части дороги, Доверие: 0.4604, Время нарушения: 5 сек\n", 213 | "1\n", 214 | "Сегмент 2/31, время: 10.00-20.00 сек, Класс: Статья 12.16 часть 2 Поворот налево или разворот в нарушение требований, предписанных дорожными знаками или разметкой проезжей части дороги, Доверие: 0.5140, Время нарушения: 15 сек\n", 215 | "1\n", 216 | "Сегмент 3/31, время: 20.00-30.00 сек, Класс: Статья 12.16 часть 2 Поворот налево или разворот в нарушение требований, предписанных дорожными знаками или разметкой проезжей части дороги, Доверие: 0.5240, Время нарушения: 25 сек\n", 217 | "1\n", 218 | "Сегмент 4/31, время: 30.00-40.00 сек, Класс: Статья 12.16 часть 2 Поворот налево или разворот в нарушение требований, предписанных дорожными знаками или разметкой проезжей части дороги, Доверие: 0.5015, Время нарушения: 35 сек\n", 219 | "1\n", 220 | "Сегмент 5/31, время: 40.00-50.00 сек, Класс: Статья 12.16 часть 2 Поворот налево или разворот в нарушение требований, предписанных дорожными знаками или разметкой проезжей части дороги, Доверие: 0.5096, Время нарушения: 45 сек\n", 221 | "1\n", 222 | "Сегмент 6/31, время: 50.00-60.00 сек, Класс: Статья 12.16 часть 2 Поворот налево или разворот в нарушение требований, предписанных дорожными знаками или разметкой проезжей части дороги, Доверие: 0.3734, Время нарушения: 55 сек\n", 223 | "0\n", 224 | "Сегмент 7/31, время: 60.00-70.00 сек, Класс: Статья 12.16. часть 1 Несоблюдение требований, предписанных дорожными знаками или разметкой проезжей части дороги, Доверие: 0.3918, Время нарушения: 65 сек\n", 225 | "0\n", 226 | "Сегмент 8/31, время: 70.00-80.00 сек, Класс: Статья 12.16. часть 1 Несоблюдение требований, предписанных дорожными знаками или разметкой проезжей части дороги, Доверие: 0.4186, Время нарушения: 75 сек\n", 227 | "1\n", 228 | "Сегмент 9/31, время: 80.00-90.00 сек, Класс: Статья 12.16 часть 2 Поворот налево или разворот в нарушение требований, предписанных дорожными знаками или разметкой проезжей части дороги, Доверие: 0.4671, Время нарушения: 85 сек\n", 229 | "1\n", 230 | "Сегмент 10/31, время: 90.00-100.00 сек, Класс: Статья 12.16 часть 2 Поворот налево или разворот в нарушение требований, предписанных дорожными знаками или разметкой проезжей части дороги, Доверие: 0.4849, Время нарушения: 95 сек\n", 231 | "1\n", 232 | "Сегмент 11/31, время: 100.00-110.00 сек, Класс: Статья 12.16 часть 2 Поворот налево или разворот в нарушение требований, предписанных дорожными знаками или разметкой проезжей части дороги, Доверие: 0.5258, Время нарушения: 105 сек\n", 233 | "1\n", 234 | "Сегмент 12/31, время: 110.00-120.00 сек, Класс: Статья 12.16 часть 2 Поворот налево или разворот в нарушение требований, предписанных дорожными знаками или разметкой проезжей части дороги, Доверие: 0.4733, Время нарушения: 115 сек\n", 235 | "1\n", 236 | "Сегмент 13/31, время: 120.00-130.00 сек, Класс: Статья 12.16 часть 2 Поворот налево или разворот в нарушение требований, предписанных дорожными знаками или разметкой проезжей части дороги, Доверие: 0.4242, Время нарушения: 125 сек\n", 237 | "1\n", 238 | "Сегмент 14/31, время: 130.00-140.00 сек, Класс: Статья 12.16 часть 2 Поворот налево или разворот в нарушение требований, предписанных дорожными знаками или разметкой проезжей части дороги, Доверие: 0.4855, Время нарушения: 135 сек\n", 239 | "1\n", 240 | "Сегмент 15/31, время: 140.00-150.00 сек, Класс: Статья 12.16 часть 2 Поворот налево или разворот в нарушение требований, предписанных дорожными знаками или разметкой проезжей части дороги, Доверие: 0.4815, Время нарушения: 145 сек\n", 241 | "1\n", 242 | "Сегмент 16/31, время: 150.00-160.00 сек, Класс: Статья 12.16 часть 2 Поворот налево или разворот в нарушение требований, предписанных дорожными знаками или разметкой проезжей части дороги, Доверие: 0.5191, Время нарушения: 155 сек\n", 243 | "1\n", 244 | "Сегмент 17/31, время: 160.00-170.00 сек, Класс: Статья 12.16 часть 2 Поворот налево или разворот в нарушение требований, предписанных дорожными знаками или разметкой проезжей части дороги, Доверие: 0.5205, Время нарушения: 165 сек\n", 245 | "1\n", 246 | "Сегмент 18/31, время: 170.00-180.00 сек, Класс: Статья 12.16 часть 2 Поворот налево или разворот в нарушение требований, предписанных дорожными знаками или разметкой проезжей части дороги, Доверие: 0.5225, Время нарушения: 175 сек\n", 247 | "1\n", 248 | "Сегмент 19/31, время: 180.00-190.00 сек, Класс: Статья 12.16 часть 2 Поворот налево или разворот в нарушение требований, предписанных дорожными знаками или разметкой проезжей части дороги, Доверие: 0.4951, Время нарушения: 185 сек\n", 249 | "1\n", 250 | "Сегмент 20/31, время: 190.00-200.00 сек, Класс: Статья 12.16 часть 2 Поворот налево или разворот в нарушение требований, предписанных дорожными знаками или разметкой проезжей части дороги, Доверие: 0.4648, Время нарушения: 195 сек\n", 251 | "1\n", 252 | "Сегмент 21/31, время: 200.00-210.00 сек, Класс: Статья 12.16 часть 2 Поворот налево или разворот в нарушение требований, предписанных дорожными знаками или разметкой проезжей части дороги, Доверие: 0.4911, Время нарушения: 205 сек\n", 253 | "1\n", 254 | "Сегмент 22/31, время: 210.00-220.00 сек, Класс: Статья 12.16 часть 2 Поворот налево или разворот в нарушение требований, предписанных дорожными знаками или разметкой проезжей части дороги, Доверие: 0.5708, Время нарушения: 215 сек\n", 255 | "1\n", 256 | "Сегмент 23/31, время: 220.00-230.00 сек, Класс: Статья 12.16 часть 2 Поворот налево или разворот в нарушение требований, предписанных дорожными знаками или разметкой проезжей части дороги, Доверие: 0.5377, Время нарушения: 225 сек\n", 257 | "1\n", 258 | "Сегмент 24/31, время: 230.00-240.00 сек, Класс: Статья 12.16 часть 2 Поворот налево или разворот в нарушение требований, предписанных дорожными знаками или разметкой проезжей части дороги, Доверие: 0.5107, Время нарушения: 235 сек\n", 259 | "1\n", 260 | "Сегмент 25/31, время: 240.00-250.00 сек, Класс: Статья 12.16 часть 2 Поворот налево или разворот в нарушение требований, предписанных дорожными знаками или разметкой проезжей части дороги, Доверие: 0.5508, Время нарушения: 245 сек\n", 261 | "1\n", 262 | "Сегмент 26/31, время: 250.00-260.00 сек, Класс: Статья 12.16 часть 2 Поворот налево или разворот в нарушение требований, предписанных дорожными знаками или разметкой проезжей части дороги, Доверие: 0.5822, Время нарушения: 255 сек\n", 263 | "1\n", 264 | "Сегмент 27/31, время: 260.00-270.00 сек, Класс: Статья 12.16 часть 2 Поворот налево или разворот в нарушение требований, предписанных дорожными знаками или разметкой проезжей части дороги, Доверие: 0.4683, Время нарушения: 265 сек\n", 265 | "1\n", 266 | "Сегмент 28/31, время: 270.00-280.00 сек, Класс: Статья 12.16 часть 2 Поворот налево или разворот в нарушение требований, предписанных дорожными знаками или разметкой проезжей части дороги, Доверие: 0.4534, Время нарушения: 275 сек\n", 267 | "1\n", 268 | "Сегмент 29/31, время: 280.00-290.00 сек, Класс: Статья 12.16 часть 2 Поворот налево или разворот в нарушение требований, предписанных дорожными знаками или разметкой проезжей части дороги, Доверие: 0.4860, Время нарушения: 285 сек\n", 269 | "1\n", 270 | "Сегмент 30/31, время: 290.00-300.00 сек, Класс: Статья 12.16 часть 2 Поворот налево или разворот в нарушение требований, предписанных дорожными знаками или разметкой проезжей части дороги, Доверие: 0.5345, Время нарушения: 295 сек\n", 271 | "1\n", 272 | "Сегмент 31/31, время: 300.00-300.03 сек, Класс: Статья 12.16 часть 2 Поворот налево или разворот в нарушение требований, предписанных дорожными знаками или разметкой проезжей части дороги, Доверие: 0.5855, Время нарушения: 300 сек\n", 273 | "Видео AKN00084.mp4 длительностью 300.03 секунд, FPS: 29.97002997002997\n", 274 | "1\n", 275 | "Сегмент 1/31, время: 0.00-10.00 сек, Класс: Статья 12.16 часть 2 Поворот налево или разворот в нарушение требований, предписанных дорожными знаками или разметкой проезжей части дороги, Доверие: 0.5873, Время нарушения: 5 сек\n", 276 | "1\n", 277 | "Сегмент 2/31, время: 10.00-20.00 сек, Класс: Статья 12.16 часть 2 Поворот налево или разворот в нарушение требований, предписанных дорожными знаками или разметкой проезжей части дороги, Доверие: 0.4102, Время нарушения: 15 сек\n", 278 | "1\n", 279 | "Сегмент 3/31, время: 20.00-30.00 сек, Класс: Статья 12.16 часть 2 Поворот налево или разворот в нарушение требований, предписанных дорожными знаками или разметкой проезжей части дороги, Доверие: 0.4334, Время нарушения: 25 сек\n", 280 | "1\n", 281 | "Сегмент 4/31, время: 30.00-40.00 сек, Класс: Статья 12.16 часть 2 Поворот налево или разворот в нарушение требований, предписанных дорожными знаками или разметкой проезжей части дороги, Доверие: 0.5447, Время нарушения: 35 сек\n", 282 | "1\n", 283 | "Сегмент 5/31, время: 40.00-50.00 сек, Класс: Статья 12.16 часть 2 Поворот налево или разворот в нарушение требований, предписанных дорожными знаками или разметкой проезжей части дороги, Доверие: 0.4673, Время нарушения: 45 сек\n", 284 | "1\n", 285 | "Сегмент 6/31, время: 50.00-60.00 сек, Класс: Статья 12.16 часть 2 Поворот налево или разворот в нарушение требований, предписанных дорожными знаками или разметкой проезжей части дороги, Доверие: 0.4195, Время нарушения: 55 сек\n", 286 | "1\n", 287 | "Сегмент 7/31, время: 60.00-70.00 сек, Класс: Статья 12.16 часть 2 Поворот налево или разворот в нарушение требований, предписанных дорожными знаками или разметкой проезжей части дороги, Доверие: 0.5147, Время нарушения: 65 сек\n", 288 | "1\n", 289 | "Сегмент 8/31, время: 70.00-80.00 сек, Класс: Статья 12.16 часть 2 Поворот налево или разворот в нарушение требований, предписанных дорожными знаками или разметкой проезжей части дороги, Доверие: 0.4378, Время нарушения: 75 сек\n", 290 | "1\n", 291 | "Сегмент 9/31, время: 80.00-90.00 сек, Класс: Статья 12.16 часть 2 Поворот налево или разворот в нарушение требований, предписанных дорожными знаками или разметкой проезжей части дороги, Доверие: 0.4893, Время нарушения: 85 сек\n", 292 | "1\n", 293 | "Сегмент 10/31, время: 90.00-100.00 сек, Класс: Статья 12.16 часть 2 Поворот налево или разворот в нарушение требований, предписанных дорожными знаками или разметкой проезжей части дороги, Доверие: 0.5343, Время нарушения: 95 сек\n", 294 | "1\n", 295 | "Сегмент 11/31, время: 100.00-110.00 сек, Класс: Статья 12.16 часть 2 Поворот налево или разворот в нарушение требований, предписанных дорожными знаками или разметкой проезжей части дороги, Доверие: 0.5720, Время нарушения: 105 сек\n", 296 | "1\n", 297 | "Сегмент 12/31, время: 110.00-120.00 сек, Класс: Статья 12.16 часть 2 Поворот налево или разворот в нарушение требований, предписанных дорожными знаками или разметкой проезжей части дороги, Доверие: 0.5440, Время нарушения: 115 сек\n", 298 | "1\n", 299 | "Сегмент 13/31, время: 120.00-130.00 сек, Класс: Статья 12.16 часть 2 Поворот налево или разворот в нарушение требований, предписанных дорожными знаками или разметкой проезжей части дороги, Доверие: 0.5091, Время нарушения: 125 сек\n", 300 | "1\n", 301 | "Сегмент 14/31, время: 130.00-140.00 сек, Класс: Статья 12.16 часть 2 Поворот налево или разворот в нарушение требований, предписанных дорожными знаками или разметкой проезжей части дороги, Доверие: 0.5160, Время нарушения: 135 сек\n", 302 | "1\n", 303 | "Сегмент 15/31, время: 140.00-150.00 сек, Класс: Статья 12.16 часть 2 Поворот налево или разворот в нарушение требований, предписанных дорожными знаками или разметкой проезжей части дороги, Доверие: 0.5161, Время нарушения: 145 сек\n", 304 | "1\n", 305 | "Сегмент 16/31, время: 150.00-160.00 сек, Класс: Статья 12.16 часть 2 Поворот налево или разворот в нарушение требований, предписанных дорожными знаками или разметкой проезжей части дороги, Доверие: 0.5071, Время нарушения: 155 сек\n", 306 | "1\n", 307 | "Сегмент 17/31, время: 160.00-170.00 сек, Класс: Статья 12.16 часть 2 Поворот налево или разворот в нарушение требований, предписанных дорожными знаками или разметкой проезжей части дороги, Доверие: 0.5308, Время нарушения: 165 сек\n", 308 | "1\n", 309 | "Сегмент 18/31, время: 170.00-180.00 сек, Класс: Статья 12.16 часть 2 Поворот налево или разворот в нарушение требований, предписанных дорожными знаками или разметкой проезжей части дороги, Доверие: 0.5565, Время нарушения: 175 сек\n", 310 | "1\n", 311 | "Сегмент 19/31, время: 180.00-190.00 сек, Класс: Статья 12.16 часть 2 Поворот налево или разворот в нарушение требований, предписанных дорожными знаками или разметкой проезжей части дороги, Доверие: 0.4257, Время нарушения: 185 сек\n", 312 | "1\n", 313 | "Сегмент 20/31, время: 190.00-200.00 сек, Класс: Статья 12.16 часть 2 Поворот налево или разворот в нарушение требований, предписанных дорожными знаками или разметкой проезжей части дороги, Доверие: 0.4930, Время нарушения: 195 сек\n", 314 | "1\n", 315 | "Сегмент 21/31, время: 200.00-210.00 сек, Класс: Статья 12.16 часть 2 Поворот налево или разворот в нарушение требований, предписанных дорожными знаками или разметкой проезжей части дороги, Доверие: 0.5427, Время нарушения: 205 сек\n", 316 | "1\n", 317 | "Сегмент 22/31, время: 210.00-220.00 сек, Класс: Статья 12.16 часть 2 Поворот налево или разворот в нарушение требований, предписанных дорожными знаками или разметкой проезжей части дороги, Доверие: 0.5228, Время нарушения: 215 сек\n", 318 | "1\n", 319 | "Сегмент 23/31, время: 220.00-230.00 сек, Класс: Статья 12.16 часть 2 Поворот налево или разворот в нарушение требований, предписанных дорожными знаками или разметкой проезжей части дороги, Доверие: 0.5650, Время нарушения: 225 сек\n", 320 | "1\n", 321 | "Сегмент 24/31, время: 230.00-240.00 сек, Класс: Статья 12.16 часть 2 Поворот налево или разворот в нарушение требований, предписанных дорожными знаками или разметкой проезжей части дороги, Доверие: 0.5542, Время нарушения: 235 сек\n", 322 | "1\n", 323 | "Сегмент 25/31, время: 240.00-250.00 сек, Класс: Статья 12.16 часть 2 Поворот налево или разворот в нарушение требований, предписанных дорожными знаками или разметкой проезжей части дороги, Доверие: 0.5533, Время нарушения: 245 сек\n", 324 | "1\n", 325 | "Сегмент 26/31, время: 250.00-260.00 сек, Класс: Статья 12.16 часть 2 Поворот налево или разворот в нарушение требований, предписанных дорожными знаками или разметкой проезжей части дороги, Доверие: 0.5131, Время нарушения: 255 сек\n", 326 | "1\n", 327 | "Сегмент 27/31, время: 260.00-270.00 сек, Класс: Статья 12.16 часть 2 Поворот налево или разворот в нарушение требований, предписанных дорожными знаками или разметкой проезжей части дороги, Доверие: 0.5439, Время нарушения: 265 сек\n", 328 | "1\n", 329 | "Сегмент 28/31, время: 270.00-280.00 сек, Класс: Статья 12.16 часть 2 Поворот налево или разворот в нарушение требований, предписанных дорожными знаками или разметкой проезжей части дороги, Доверие: 0.5181, Время нарушения: 275 сек\n", 330 | "1\n", 331 | "Сегмент 29/31, время: 280.00-290.00 сек, Класс: Статья 12.16 часть 2 Поворот налево или разворот в нарушение требований, предписанных дорожными знаками или разметкой проезжей части дороги, Доверие: 0.4087, Время нарушения: 285 сек\n", 332 | "1\n", 333 | "Сегмент 30/31, время: 290.00-300.00 сек, Класс: Статья 12.16 часть 2 Поворот налево или разворот в нарушение требований, предписанных дорожными знаками или разметкой проезжей части дороги, Доверие: 0.5721, Время нарушения: 295 сек\n", 334 | "0\n", 335 | "Сегмент 31/31, время: 300.00-300.03 сек, Класс: Статья 12.16. часть 1 Несоблюдение требований, предписанных дорожными знаками или разметкой проезжей части дороги, Доверие: 0.3323, Время нарушения: 300 сек\n", 336 | "Видео AKN00089.mp4 длительностью 300.03 секунд, FPS: 29.97002997002997\n", 337 | "1\n", 338 | "Сегмент 1/31, время: 0.00-10.00 сек, Класс: Статья 12.16 часть 2 Поворот налево или разворот в нарушение требований, предписанных дорожными знаками или разметкой проезжей части дороги, Доверие: 0.3864, Время нарушения: 5 сек\n", 339 | "1\n", 340 | "Сегмент 2/31, время: 10.00-20.00 сек, Класс: Статья 12.16 часть 2 Поворот налево или разворот в нарушение требований, предписанных дорожными знаками или разметкой проезжей части дороги, Доверие: 0.3638, Время нарушения: 15 сек\n", 341 | "1\n", 342 | "Сегмент 3/31, время: 20.00-30.00 сек, Класс: Статья 12.16 часть 2 Поворот налево или разворот в нарушение требований, предписанных дорожными знаками или разметкой проезжей части дороги, Доверие: 0.3161, Время нарушения: 25 сек\n", 343 | "1\n", 344 | "Сегмент 4/31, время: 30.00-40.00 сек, Класс: Статья 12.16 часть 2 Поворот налево или разворот в нарушение требований, предписанных дорожными знаками или разметкой проезжей части дороги, Доверие: 0.3461, Время нарушения: 35 сек\n", 345 | "1\n", 346 | "Сегмент 5/31, время: 40.00-50.00 сек, Класс: Статья 12.16 часть 2 Поворот налево или разворот в нарушение требований, предписанных дорожными знаками или разметкой проезжей части дороги, Доверие: 0.3171, Время нарушения: 45 сек\n", 347 | "1\n", 348 | "Сегмент 6/31, время: 50.00-60.00 сек, Класс: Статья 12.16 часть 2 Поворот налево или разворот в нарушение требований, предписанных дорожными знаками или разметкой проезжей части дороги, Доверие: 0.3160, Время нарушения: 55 сек\n", 349 | "0\n", 350 | "Сегмент 7/31, время: 60.00-70.00 сек, Класс: Статья 12.16. часть 1 Несоблюдение требований, предписанных дорожными знаками или разметкой проезжей части дороги, Доверие: 0.3097, Время нарушения: 65 сек\n", 351 | "1\n", 352 | "Сегмент 8/31, время: 70.00-80.00 сек, Класс: Статья 12.16 часть 2 Поворот налево или разворот в нарушение требований, предписанных дорожными знаками или разметкой проезжей части дороги, Доверие: 0.3004, Время нарушения: 75 сек\n", 353 | "1\n", 354 | "Сегмент 9/31, время: 80.00-90.00 сек, Класс: Статья 12.16 часть 2 Поворот налево или разворот в нарушение требований, предписанных дорожными знаками или разметкой проезжей части дороги, Доверие: 0.4027, Время нарушения: 85 сек\n", 355 | "0\n", 356 | "Сегмент 10/31, время: 90.00-100.00 сек, Класс: Статья 12.16. часть 1 Несоблюдение требований, предписанных дорожными знаками или разметкой проезжей части дороги, Доверие: 0.3130, Время нарушения: 95 сек\n", 357 | "0\n", 358 | "Сегмент 11/31, время: 100.00-110.00 сек, Класс: Статья 12.16. часть 1 Несоблюдение требований, предписанных дорожными знаками или разметкой проезжей части дороги, Доверие: 0.2752, Время нарушения: 105 сек\n", 359 | "1\n", 360 | "Сегмент 12/31, время: 110.00-120.00 сек, Класс: Статья 12.16 часть 2 Поворот налево или разворот в нарушение требований, предписанных дорожными знаками или разметкой проезжей части дороги, Доверие: 0.3316, Время нарушения: 115 сек\n", 361 | "1\n", 362 | "Сегмент 13/31, время: 120.00-130.00 сек, Класс: Статья 12.16 часть 2 Поворот налево или разворот в нарушение требований, предписанных дорожными знаками или разметкой проезжей части дороги, Доверие: 0.2995, Время нарушения: 125 сек\n", 363 | "0\n", 364 | "Сегмент 14/31, время: 130.00-140.00 сек, Класс: Статья 12.16. часть 1 Несоблюдение требований, предписанных дорожными знаками или разметкой проезжей части дороги, Доверие: 0.3128, Время нарушения: 135 сек\n", 365 | "0\n", 366 | "Сегмент 15/31, время: 140.00-150.00 сек, Класс: Статья 12.16. часть 1 Несоблюдение требований, предписанных дорожными знаками или разметкой проезжей части дороги, Доверие: 0.2759, Время нарушения: 145 сек\n", 367 | "0\n", 368 | "Сегмент 16/31, время: 150.00-160.00 сек, Класс: Статья 12.16. часть 1 Несоблюдение требований, предписанных дорожными знаками или разметкой проезжей части дороги, Доверие: 0.3514, Время нарушения: 155 сек\n", 369 | "0\n", 370 | "Сегмент 17/31, время: 160.00-170.00 сек, Класс: Статья 12.16. часть 1 Несоблюдение требований, предписанных дорожными знаками или разметкой проезжей части дороги, Доверие: 0.3042, Время нарушения: 165 сек\n", 371 | "0\n", 372 | "Сегмент 18/31, время: 170.00-180.00 сек, Класс: Статья 12.16. часть 1 Несоблюдение требований, предписанных дорожными знаками или разметкой проезжей части дороги, Доверие: 0.3174, Время нарушения: 175 сек\n", 373 | "0\n", 374 | "Сегмент 19/31, время: 180.00-190.00 сек, Класс: Статья 12.16. часть 1 Несоблюдение требований, предписанных дорожными знаками или разметкой проезжей части дороги, Доверие: 0.3131, Время нарушения: 185 сек\n", 375 | "0\n", 376 | "Сегмент 20/31, время: 190.00-200.00 сек, Класс: Статья 12.16. часть 1 Несоблюдение требований, предписанных дорожными знаками или разметкой проезжей части дороги, Доверие: 0.2937, Время нарушения: 195 сек\n", 377 | "0\n", 378 | "Сегмент 21/31, время: 200.00-210.00 сек, Класс: Статья 12.16. часть 1 Несоблюдение требований, предписанных дорожными знаками или разметкой проезжей части дороги, Доверие: 0.3197, Время нарушения: 205 сек\n", 379 | "0\n", 380 | "Сегмент 22/31, время: 210.00-220.00 сек, Класс: Статья 12.16. часть 1 Несоблюдение требований, предписанных дорожными знаками или разметкой проезжей части дороги, Доверие: 0.3179, Время нарушения: 215 сек\n", 381 | "0\n", 382 | "Сегмент 23/31, время: 220.00-230.00 сек, Класс: Статья 12.16. часть 1 Несоблюдение требований, предписанных дорожными знаками или разметкой проезжей части дороги, Доверие: 0.3251, Время нарушения: 225 сек\n", 383 | "1\n", 384 | "Сегмент 24/31, время: 230.00-240.00 сек, Класс: Статья 12.16 часть 2 Поворот налево или разворот в нарушение требований, предписанных дорожными знаками или разметкой проезжей части дороги, Доверие: 0.4302, Время нарушения: 235 сек\n", 385 | "1\n", 386 | "Сегмент 25/31, время: 240.00-250.00 сек, Класс: Статья 12.16 часть 2 Поворот налево или разворот в нарушение требований, предписанных дорожными знаками или разметкой проезжей части дороги, Доверие: 0.4362, Время нарушения: 245 сек\n", 387 | "1\n", 388 | "Сегмент 26/31, время: 250.00-260.00 сек, Класс: Статья 12.16 часть 2 Поворот налево или разворот в нарушение требований, предписанных дорожными знаками или разметкой проезжей части дороги, Доверие: 0.3493, Время нарушения: 255 сек\n", 389 | "0\n", 390 | "Сегмент 27/31, время: 260.00-270.00 сек, Класс: Статья 12.16. часть 1 Несоблюдение требований, предписанных дорожными знаками или разметкой проезжей части дороги, Доверие: 0.3230, Время нарушения: 265 сек\n", 391 | "1\n", 392 | "Сегмент 28/31, время: 270.00-280.00 сек, Класс: Статья 12.16 часть 2 Поворот налево или разворот в нарушение требований, предписанных дорожными знаками или разметкой проезжей части дороги, Доверие: 0.3425, Время нарушения: 275 сек\n", 393 | "0\n", 394 | "Сегмент 29/31, время: 280.00-290.00 сек, Класс: Статья 12.16. часть 1 Несоблюдение требований, предписанных дорожными знаками или разметкой проезжей части дороги, Доверие: 0.3182, Время нарушения: 285 сек\n", 395 | "1\n", 396 | "Сегмент 30/31, время: 290.00-300.00 сек, Класс: Статья 12.16 часть 2 Поворот налево или разворот в нарушение требований, предписанных дорожными знаками или разметкой проезжей части дороги, Доверие: 0.3051, Время нарушения: 295 сек\n", 397 | "1\n", 398 | "Сегмент 31/31, время: 300.00-300.03 сек, Класс: Статья 12.16 часть 2 Поворот налево или разворот в нарушение требований, предписанных дорожными знаками или разметкой проезжей части дороги, Доверие: 0.3554, Время нарушения: 300 сек\n", 399 | "Сабмит сохранен в файл submission.csv\n" 400 | ] 401 | } 402 | ], 403 | "source": [ 404 | "import os\n", 405 | "import torch\n", 406 | "import torch.nn as nn\n", 407 | "from torchvision import transforms\n", 408 | "from PIL import Image\n", 409 | "import cv2\n", 410 | "from transformers import (\n", 411 | " XCLIPModel,\n", 412 | " XCLIPProcessor,\n", 413 | " SegformerForSemanticSegmentation,\n", 414 | " SegformerImageProcessor,\n", 415 | ")\n", 416 | "import numpy as np\n", 417 | "import pandas as pd\n", 418 | "from model import EmbeddingClassifier\n", 419 | "\n", 420 | "import warnings\n", 421 | "import gdown # Добавляем импорт библиотеки gdown\n", 422 | "\n", 423 | "# Игнорировать все предупреждения\n", 424 | "warnings.filterwarnings(\"ignore\")\n", 425 | "\n", 426 | "# ======== Глобальные параметры ======== #\n", 427 | "video_folder = r\"C:\\Users\\pasha\\OneDrive\\Рабочий стол\\test_dataset_rzhd_test\\test РЖД ПДД\\videos\\videos\" # Папка с видео\n", 428 | "output_csv = r'submission.csv'\n", 429 | "MODEL_NAME = \"microsoft/xclip-base-patch16\" # Не менять\n", 430 | "\n", 431 | "# Путь к кастомной модели YOLO и ссылка для скачивания\n", 432 | "YOLO_CUSTOM_PATH = r\"best_93.pt\"\n", 433 | "YOLO_CUSTOM_URL = \"https://drive.google.com/uc?id=1KovZgdLspcwOmxXVUmWcG3ro_TnrGT5n\"\n", 434 | "\n", 435 | "# Путь к модели SegFormer и ссылка для скачивания\n", 436 | "SEGFORMER_MODEL_PATH = r\"segformer_model\"\n", 437 | "SEGFORMER_MODEL_URL = \"https://drive.google.com/drive/folders/1zrp_r4Iy1STrPYG9m0b96nuEiHLq7NNk\"\n", 438 | "\n", 439 | "BEST_MODEL_PATH = 'best_model_dataset_1_47.pth' # Обученная модель классификатора\n", 440 | "\n", 441 | "APPLY_PREPROCESSING = True # Переключатель для применения предварительной обработки\n", 442 | "\n", 443 | "# ======== Список меток и штрафов ======== #\n", 444 | "LABEL_LIST = ['Статья 12.16. часть 1 Несоблюдение требований, предписанных дорожными знаками или разметкой проезжей части дороги',\n", 445 | " 'нарушений нет',\n", 446 | " 'Статья 12.16 часть 2 Поворот налево или разворот в нарушение требований, предписанных дорожными знаками или разметкой проезжей части дороги',\n", 447 | "\n", 448 | " 'Статья 12.17 часть 1.1 и 1.2. движение транспортных средств по полосе для маршрутных транспортных средств или остановка на указанной полосе в нарушение Правил дорожного движения ',\n", 449 | " 'Статья 12.12 часть 2 1. невыполнение требования ПДД об остановке перед стоп-линией, обозначенной дорожными знаками или разметкой проезжей части дороги, при запрещающем сигнале светофора или запрещающем жесте регулировщика',\n", 450 | " 'Статья 12.15 часть 4 Выезд в нарушение правил дорожного движения на полосу, предназначенную для встречного движения, при объезде препятствия, либо на трамвайные пути встречного направления, за исключением случаев, предусмотренных частью 3 настоящей статьи']\n", 451 | "NUM_CLASSES = len(LABEL_LIST)\n", 452 | "\n", 453 | "FINE_DICT = {\n", 454 | " 'нарушений нет': 0,\n", 455 | " 'Статья 12.16. часть 1 Несоблюдение требований, предписанных дорожными знаками или разметкой проезжей части дороги': 500,\n", 456 | " 'Статья 12.16 часть 2 Поворот налево или разворот в нарушение требований, предписанных дорожными знаками или разметкой проезжей части дороги': 1000,\n", 457 | " 'Статья 12.17 часть 1.1 и 1.2. движение транспортных средств по полосе для маршрутных транспортных средств или остановка на указанной полосе в нарушение Правил дорожного движения ': 1500,\n", 458 | " 'Статья 12.12 часть 2 1. невыполнение требования ПДД об остановке перед стоп-линией, обозначенной дорожными знаками или разметкой проезжей части дороги, при запрещающем сигнале светофора или запрещающем жесте регулировщика': 800,\n", 459 | " 'Статья 12.15 часть 4 Выезд в нарушение правил дорожного движения на полосу, предназначенную для встречного движения, при объезде препятствия, либо на трамвайные пути встречного направления, за исключением случаев, предусмотренных частью 3 настоящей статьи': 5000\n", 460 | "}\n", 461 | "\n", 462 | "# ======== Функция для загрузки файлов ======== #\n", 463 | "def download_file_if_not_exists(file_path, url, is_folder=False):\n", 464 | " if not os.path.exists(file_path):\n", 465 | " print(f\"Файл {file_path} не найден. Начинаю загрузку...\")\n", 466 | " if is_folder:\n", 467 | " # Для загрузки папки используем флаг --folder\n", 468 | " gdown.download_folder(url, output=file_path, quiet=False)\n", 469 | " else:\n", 470 | " gdown.download(url, output=file_path, quiet=False)\n", 471 | " print(f\"Загрузка {file_path} завершена.\")\n", 472 | " else:\n", 473 | " print(f\"Файл {file_path} уже существует.\")\n", 474 | "\n", 475 | "# ======== Проверка и загрузка моделей ======== #\n", 476 | "if APPLY_PREPROCESSING:\n", 477 | " # Проверяем и загружаем кастомную модель YOLO\n", 478 | " download_file_if_not_exists(YOLO_CUSTOM_PATH, YOLO_CUSTOM_URL)\n", 479 | "\n", 480 | " # Проверяем и загружаем модель SegFormer\n", 481 | " download_file_if_not_exists(SEGFORMER_MODEL_PATH, SEGFORMER_MODEL_URL, is_folder=True)\n", 482 | "\n", 483 | "# ======== Загрузка моделей ======== #\n", 484 | "# Устройство (CPU или GPU)\n", 485 | "device = torch.device('cuda' if torch.cuda.is_available() else 'cpu')\n", 486 | "print(f\"Используется устройство: {device}\")\n", 487 | "\n", 488 | "# Загрузка XCLIP модели и процессора\n", 489 | "processor = XCLIPProcessor.from_pretrained(MODEL_NAME)\n", 490 | "model = XCLIPModel.from_pretrained(MODEL_NAME)\n", 491 | "model.to(device)\n", 492 | "model.eval() # Переводим модель в режим оценки\n", 493 | "\n", 494 | "# Загрузка модели классификатора и весов\n", 495 | "classifier_model = EmbeddingClassifier(model.config.projection_dim, NUM_CLASSES)\n", 496 | "classifier_model.load_state_dict(torch.load(BEST_MODEL_PATH, map_location=device))\n", 497 | "classifier_model.to(device)\n", 498 | "classifier_model.eval()\n", 499 | "\n", 500 | "# Получение mean и std для нормализации изображений\n", 501 | "try:\n", 502 | " image_mean = processor.image_processor.image_mean\n", 503 | " image_std = processor.image_processor.image_std\n", 504 | "except AttributeError:\n", 505 | " image_mean = processor.feature_extractor.image_mean\n", 506 | " image_std = processor.feature_extractor.image_std\n", 507 | "\n", 508 | "# Определение видео трансформаций\n", 509 | "video_transform = transforms.Compose([\n", 510 | " transforms.Resize((224, 224)),\n", 511 | " transforms.ToTensor(),\n", 512 | " transforms.Normalize(mean=image_mean, std=image_std)\n", 513 | "])\n", 514 | "\n", 515 | "# ======== Класс для предварительной обработки ======== #\n", 516 | "class Preprocessor:\n", 517 | " def __init__(self, yolo_custom_path, segformer_model_path):\n", 518 | " self.device = device\n", 519 | " # Загрузка кастомной модели YOLOv5\n", 520 | " self.custom_model = torch.hub.load(\n", 521 | " 'ultralytics/yolov5', 'custom', path=yolo_custom_path, force_reload=True\n", 522 | " ).to(self.device).eval()\n", 523 | " # Загрузка предобученной модели YOLOv5\n", 524 | " self.pretrained_model = torch.hub.load(\n", 525 | " 'ultralytics/yolov5', 'yolov5n', pretrained=True\n", 526 | " ).to(self.device).eval()\n", 527 | " # Загрузка модели SegFormer\n", 528 | " self.segformer_model = SegformerForSemanticSegmentation.from_pretrained(\n", 529 | " segformer_model_path\n", 530 | " ).to(self.device).eval()\n", 531 | " self.extractor = SegformerImageProcessor()\n", 532 | " # Параметры\n", 533 | " self.traffic_related_classes = [\"car\", \"bus\", \"truck\", \"motorcycle\", \"bicycle\"]\n", 534 | " self.target_class_id = 2 # Целевой класс для SegFormer\n", 535 | "\n", 536 | " def apply(self, frame):\n", 537 | " height, width, _ = frame.shape\n", 538 | "\n", 539 | " # Преобразование кадра\n", 540 | " rgb_frame = cv2.cvtColor(frame, cv2.COLOR_BGR2RGB)\n", 541 | " img = Image.fromarray(rgb_frame)\n", 542 | "\n", 543 | " # Получение результатов от моделей YOLOv5\n", 544 | " results_pretrained = self.pretrained_model(img)\n", 545 | " results_custom = self.custom_model(img)\n", 546 | "\n", 547 | " # Объединение результатов\n", 548 | " results_combined = pd.concat(\n", 549 | " [results_pretrained.pandas().xyxy[0], results_custom.pandas().xyxy[0]],\n", 550 | " ignore_index=True,\n", 551 | " )\n", 552 | "\n", 553 | " # Обработка кадра моделью SegFormer\n", 554 | " seg_map = self.predict_segformer(rgb_frame)\n", 555 | "\n", 556 | " # Создание маски для затемнения\n", 557 | " mask = np.zeros((height, width), dtype=np.uint8)\n", 558 | "\n", 559 | " # Добавление результатов YOLOv5 в маску\n", 560 | " for _, row in results_combined.iterrows():\n", 561 | " if row[\"name\"] in self.traffic_related_classes or row[\"confidence\"] > 0.25:\n", 562 | " x1 = int(max(0, row[\"xmin\"]))\n", 563 | " y1 = int(max(0, row[\"ymin\"]))\n", 564 | " x2 = int(min(width - 1, row[\"xmax\"]))\n", 565 | " y2 = int(min(height - 1, row[\"ymax\"]))\n", 566 | " mask[y1:y2, x1:x2] = 255 # Область, которую не затемняем\n", 567 | "\n", 568 | " # Добавление результатов SegFormer в маску\n", 569 | " if seg_map.shape != (height, width):\n", 570 | " seg_map_resized = cv2.resize(seg_map, (width, height), interpolation=cv2.INTER_NEAREST)\n", 571 | " else:\n", 572 | " seg_map_resized = seg_map\n", 573 | " seg_mask = np.where(seg_map_resized == self.target_class_id, 255, 0).astype(np.uint8)\n", 574 | " mask = cv2.bitwise_or(mask, seg_mask)\n", 575 | "\n", 576 | " # Создание итогового кадра с затемнением\n", 577 | " alpha_mask = np.stack([mask, mask, mask], axis=-1) # Создаем маску с 3 каналами\n", 578 | " frame_darkened = (frame * 0.2).astype(np.uint8)\n", 579 | " frame_result = np.where(alpha_mask == 255, frame, frame_darkened)\n", 580 | "\n", 581 | " return frame_result\n", 582 | "\n", 583 | " def predict_segformer(self, image):\n", 584 | " inputs = self.extractor(images=image, return_tensors=\"pt\").to(self.device)\n", 585 | " with torch.no_grad():\n", 586 | " outputs = self.segformer_model(**inputs)\n", 587 | " logits = outputs.logits # [batch_size, num_classes, height, width]\n", 588 | " segmentation = torch.argmax(logits, dim=1).squeeze(0)\n", 589 | " return segmentation.cpu().numpy()\n", 590 | "\n", 591 | "# Инициализация препроцессора при необходимости\n", 592 | "if APPLY_PREPROCESSING:\n", 593 | " preprocessor = Preprocessor(YOLO_CUSTOM_PATH, SEGFORMER_MODEL_PATH)\n", 594 | "else:\n", 595 | " preprocessor = None\n", 596 | "\n", 597 | "# ======== Функции для обработки видео ======== #\n", 598 | "def extract_frames_from_video(\n", 599 | " video_capture, start_time, end_time, num_frames=8, preprocessor=None\n", 600 | "):\n", 601 | " fps = video_capture.get(cv2.CAP_PROP_FPS)\n", 602 | " start_frame = int(start_time * fps)\n", 603 | " end_frame = int(end_time * fps)\n", 604 | " total_frames = end_frame - start_frame\n", 605 | "\n", 606 | " frame_indices = np.linspace(start_frame, end_frame - 1, num=num_frames, dtype=int)\n", 607 | " frames = []\n", 608 | "\n", 609 | " for frame_idx in frame_indices:\n", 610 | " video_capture.set(cv2.CAP_PROP_POS_FRAMES, frame_idx)\n", 611 | " success, frame = video_capture.read()\n", 612 | " if not success:\n", 613 | " break\n", 614 | "\n", 615 | " # Применение предварительной обработки\n", 616 | " if preprocessor is not None:\n", 617 | " frame = preprocessor.apply(frame)\n", 618 | "\n", 619 | " frame_rgb = cv2.cvtColor(frame, cv2.COLOR_BGR2RGB)\n", 620 | " frame_pil = Image.fromarray(frame_rgb)\n", 621 | "\n", 622 | " # Используем заданные трансформации\n", 623 | " frame_tensor = video_transform(frame_pil)\n", 624 | " frames.append(frame_tensor)\n", 625 | "\n", 626 | " # Повторяем последний кадр, если кадров меньше, чем num_frames\n", 627 | " while len(frames) < num_frames:\n", 628 | " frames.append(frames[-1].clone() if len(frames) > 0 else torch.zeros(3, 224, 224))\n", 629 | "\n", 630 | " # Преобразуем список тензоров в один тензор и перемещаем на устройство\n", 631 | " video_frames_tensor = torch.stack(frames).to(device)\n", 632 | "\n", 633 | " return video_frames_tensor\n", 634 | "\n", 635 | "# ======== Функция для предсказания класса сегмента ======== #\n", 636 | "def predict_segment_class(video_capture, start_time, end_time, preprocessor=None):\n", 637 | " # Извлекаем кадры из сегмента\n", 638 | " video_frames_tensor = extract_frames_from_video(\n", 639 | " video_capture, start_time, end_time, num_frames=8, preprocessor=preprocessor\n", 640 | " )\n", 641 | " video_frames_tensor = video_frames_tensor.unsqueeze(0) # Добавляем размерность batch\n", 642 | "\n", 643 | " # Генерируем фиктивный текстовый ввод\n", 644 | " text_inputs = processor(\n", 645 | " text=[\"\"], # Пустой текст, так как мы используем только видеоэмбеддинги\n", 646 | " return_tensors='pt',\n", 647 | " padding=True,\n", 648 | " truncation=True,\n", 649 | " max_length=77\n", 650 | " )\n", 651 | " input_ids = text_inputs['input_ids'].to(device)\n", 652 | " attention_mask = text_inputs['attention_mask'].to(device)\n", 653 | "\n", 654 | " # Получаем видеоэмбеддинги из модели XCLIP\n", 655 | " with torch.no_grad():\n", 656 | " outputs = model(\n", 657 | " pixel_values=video_frames_tensor,\n", 658 | " input_ids=input_ids,\n", 659 | " attention_mask=attention_mask\n", 660 | " )\n", 661 | " video_embeds = outputs.video_embeds # [batch_size, projection_dim]\n", 662 | "\n", 663 | " # Передаем эмбеддинги в классификатор\n", 664 | " with torch.no_grad():\n", 665 | " logits = classifier_model(video_embeds)\n", 666 | " probabilities = torch.softmax(logits, dim=1)\n", 667 | " predicted_class_idx = torch.argmax(probabilities, dim=1).item()\n", 668 | " print(predicted_class_idx)\n", 669 | " predicted_class = LABEL_LIST[predicted_class_idx]\n", 670 | " confidence = probabilities[0, predicted_class_idx].item()\n", 671 | "\n", 672 | " return predicted_class, confidence\n", 673 | "\n", 674 | "# ======== Основная функция для обработки видео ======== #\n", 675 | "def process_video(video_path, preprocessor=None):\n", 676 | " video_capture = cv2.VideoCapture(video_path)\n", 677 | " if not video_capture.isOpened():\n", 678 | " print(f\"Не удалось открыть видео: {video_path}\")\n", 679 | " return []\n", 680 | "\n", 681 | " fps = video_capture.get(cv2.CAP_PROP_FPS)\n", 682 | " total_frames = int(video_capture.get(cv2.CAP_PROP_FRAME_COUNT))\n", 683 | " duration = total_frames / fps\n", 684 | " print(f\"Видео {os.path.basename(video_path)} длительностью {duration:.2f} секунд, FPS: {fps}\")\n", 685 | "\n", 686 | " segment_duration = 10 # Продолжительность сегмента в секундах\n", 687 | " predictions = []\n", 688 | "\n", 689 | " num_segments = int(np.ceil(duration / segment_duration))\n", 690 | " for i in range(num_segments):\n", 691 | " start_time = i * segment_duration\n", 692 | " end_time = min((i + 1) * segment_duration, duration)\n", 693 | "\n", 694 | " predicted_class, confidence = predict_segment_class(\n", 695 | " video_capture, start_time, end_time, preprocessor=preprocessor\n", 696 | " )\n", 697 | "\n", 698 | " # Вычисляем среднее время сегмента\n", 699 | " violation_time = int((start_time + end_time) / 2)\n", 700 | "\n", 701 | " # Получаем сумму штрафа\n", 702 | " fine_amount = FINE_DICT.get(predicted_class, 0)\n", 703 | "\n", 704 | " # Если нарушение отсутствует, не добавляем его в предсказания\n", 705 | " if predicted_class == 'нарушений нет':\n", 706 | " continue\n", 707 | "\n", 708 | " print(f\"Сегмент {i+1}/{num_segments}, время: {start_time:.2f}-{end_time:.2f} сек, \"\n", 709 | " f\"Класс: {predicted_class}, Доверие: {confidence:.4f}, Время нарушения: {violation_time} сек\")\n", 710 | "\n", 711 | " predictions.append({\n", 712 | " 'номер видео': os.path.splitext(os.path.basename(video_path))[0],\n", 713 | " 'наименование нарушения': predicted_class,\n", 714 | " 'сумма штрафа, руб.': fine_amount,\n", 715 | " 'время нарушения (в секундах)': float(violation_time)\n", 716 | " })\n", 717 | "\n", 718 | " video_capture.release()\n", 719 | " return predictions\n", 720 | "\n", 721 | "# ======== Обработка всех видео и формирование сабмита ======== #\n", 722 | "def create_submission(video_paths, output_csv='submission.csv', preprocessor=None):\n", 723 | " all_predictions = []\n", 724 | "\n", 725 | " for video_path in video_paths:\n", 726 | " predictions = process_video(video_path, preprocessor=preprocessor)\n", 727 | " all_predictions.extend(predictions)\n", 728 | "\n", 729 | " submission_df = pd.DataFrame(all_predictions)\n", 730 | " submission_df.sort_values(by=['номер видео', 'время нарушения (в секундах)'], inplace=True)\n", 731 | " submission_df.to_csv(output_csv, index=False)\n", 732 | " print(f\"Сабмит сохранен в файл {output_csv}\")\n", 733 | "\n", 734 | "# ======== Запуск скрипта ======== #\n", 735 | "if __name__ == \"__main__\":\n", 736 | " # Список видеофайлов для обработки\n", 737 | " video_files = [\n", 738 | " os.path.join(video_folder, filename)\n", 739 | " for filename in os.listdir(video_folder)\n", 740 | " if filename.endswith(('.mp4', '.mov', '.avi')) # Замените на нужные расширения файлов\n", 741 | " ]\n", 742 | "\n", 743 | " # Обработка видео и создание сабмита\n", 744 | " create_submission(video_files, output_csv=output_csv, preprocessor=preprocessor)\n" 745 | ] 746 | } 747 | ], 748 | "metadata": { 749 | "kernelspec": { 750 | "display_name": "Python 3", 751 | "language": "python", 752 | "name": "python3" 753 | }, 754 | "language_info": { 755 | "codemirror_mode": { 756 | "name": "ipython", 757 | "version": 3 758 | }, 759 | "file_extension": ".py", 760 | "mimetype": "text/x-python", 761 | "name": "python", 762 | "nbconvert_exporter": "python", 763 | "pygments_lexer": "ipython3", 764 | "version": "3.11.0" 765 | } 766 | }, 767 | "nbformat": 4, 768 | "nbformat_minor": 2 769 | } 770 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 |
2 | 3 | # 🚀 Хакатон: «Цифровой прорыв. Сезон: Искусственный интеллект» 🤖 4 | 5 |
6 | 7 |
8 | 9 | # 📌 Кейс: Детектирование нарушений правил дорожного движения 10 | 11 |
12 | 13 |
14 | 15 | ## 🎯 Команда: Центр Искусственного Интеллекта СФУ 🧠 16 | 17 |
18 | 19 | --- 20 | 21 | 22 | 23 | 40 | 45 | 46 |
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 | 41 | 42 | Демонстрация проекта 43 | 44 |
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 | 75 | 76 | 77 | 78 | 79 | 80 |
📹 Презентация работы системы🎯 Обработанный фрагмент (вход для XCLIP)
ScreencastProcessed Clip
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/JnsOHjwohYSESL1795bMZnOJ24qKiorXa9euXfyY33vvPQmANGfOnBL7nz59WipXrpzUuXPnEseaO3euBED68MMPi7e1bdtWAiDNmDGjeFt+fr7UtGlTqXr16lJBQYEkSZKUlpYmAZCWLl1avF9BQYHUqlUrqWLFilJubq4kSZK0YcMGCUDx+eToccjk8/Hw4cM2t7nz3NvjzrmTmpoqAZAyMzOLt126dEmqU6eOlJycbPM3Kc3eeO666y6pQYMGNuOpWbNm8fMlSZL02WefSQCk2bNnS5Iknqe6detKXbp0KfGcXb16VapTp47UqVOn4m0TJkwo/r933333FW//559/pJCQEKl///4SAOnMmTPFj6ly5crSsGHDSow1Oztbio6OLrH9kUcekSpUqGDzWFesWGHzd2nbtm2J/zdr1qyRAEhdu3aVeFlORES+xLIvIiIiF8hZPy+88ILT/S5duoTTp09j27ZtWL58ORo2bIgqVaqU2Of2229Hw4YNizMd/vnnH2RkZNiU10iShFWrVuH++++HJEk4e/Zs8b8uXbrg4sWL+OWXX2zu33q/0mUsALBixQqkpKQgJiamxH4dO3aE2WzGDz/8UGL/q1ev2hzTbDY7fR7S09OxY8cOvPnmm14/Jmuff/45ioqK8MorryAkpORljL2ymf/973946qmnMHbsWAwfPrzEbd999x0KCgqQmppa4ljDhg1DVFSUTSlTaGgonnjiieKfy5UrhyeeeAKnT5/Gzp07AQBr1qxBfHx8icywsLAwjBgxApcvX8amTZsAAKtWrYLBYMCECRNsxuxt+Y+j514Ja9asQcuWLXH33XcXb6tYsSIef/xxHDlyBPv27SvzGPL5lJ2djVWrVuG3335Dhw4dbPYbNGgQKlWqVPxz3759kZCQUJzRtGvXLhw8eBADBgzAuXPnis+jK1euoEOHDvjhhx9KlDECwNChQ7F27VpkZ2cDABYvXoxWrVrh5ptvLrHf+vXrkZOTg/79+5c4R41GI+644w5kZGTYjLf0/5FLly45fR4kScK4cePQp08f3HHHHWU+b0RERN5g2RcREVEZ/v77b3z00Ud46qmnkJCQ4HTfYcOG4dNPPwUAtGjRAmvWrLH7YX7IkCFYsGABnn32WSxatAh33XUX6tatW2KfM2fOICcnBwsWLMCCBQvs3t/p06dL/CyXHzlz8OBB7N69G9WqVXPpmBMmTLAbpHBUdmQ2m/Hiiy9i4MCBaNKkSYnbPHlM1g4dOoSQkBA0aNDA4T6yXbt24bPPPoPZbMb58+dtbpf7MdWrV6/E9nLlyuGGG24o0a8JAGrUqIEKFSqU2CYHDY4cOYI777wT//zzD+rWrWsTmLrllltK3OehQ4dQo0YNm8Cgt5w990r4559/7AYqrB9fo0aNnB7jrbfewltvvVX8c9euXTF16lSb/Ur/fzAYDLjppptw5MgRAOI8BoBHHnnE4X1dvHgRMTExxT83bdoUjRo1wpIlSzB27FgsWrQIL774ok0vIfnY99xzj93jRkVFlfj5ypUrDv8/OfLxxx/j999/x2effYZly5a59btERETuYvCHiIioDJMnTy7u9VOWl19+GUOGDMGhQ4cwbdo09OvXD9999x1CQ0u+5T700EN47rnn8OOPP2Lx4sV4+eWXbY4lZy089NBDDj/glv6A/8orryAlJaXEtvvvv9/muJ06dcJzzz1n95ilsyAef/xxPPDAAyW2DRs2zO7vAsAHH3yAI0eO4Ntvv7W5zZPH5KnffvsN3bp1Q4cOHTB27Fg89NBDNs12g42z514rHn74YQwaNAhFRUX4+++/8dprr+G+++7Dd99951bWk3wuvfXWW2jatKndfew1Yh46dCjmzZuHli1bIjs7Gw8++CBmzJhh99gfffSRTXN3ADb/n8uXL48vv/yyxLbMzExMmjTJ7rgKCgowfvx4PProozb/34iIiHyBwR8iIiInDh8+jCVLluDJJ59EjRo1yty/UaNGxZkPjRs3Rps2bbB+/Xp069atxH6xsbHo0aNHcdnQgw8+aFOiVa1aNVSqVAlmsxkdO3Z0abyNGze22ddoNJb4+cYbb8Tly5ddPmbdunVt9i2dASO7evUqJk6ciKeeegq1a9e2ud2Tx1R67EVFRdi3b5/DD/yyxo0bY8WKFYiIiMCKFSvw+OOPY/fu3ShfvjwAFI9v//79uOGGG4p/r6CgAIcPH7YZ34kTJ3DlypUSj/3AgQMAUNz8t3bt2ti9ezeKiopKZP/8+eefJe7zxhtvxLfffovz588rlv1T1nOvhNq1a2P//v0220s/PmduuOGGEs9tdHQ0BgwYgB9//BGtWrUq3i5n38gkScJff/1VHBy88cYbAYgsHHfOpYEDB2Ls2LEYOXIk+vbtW6K0TCYfu3r16i4d22g02uxXesY4a/PmzcPp06fLnImNiIhIKez5Q0RE5MQbb7wBo9FYZq8fe+RgTn5+vt3bhw4dit27d+OBBx6wm6FgNBrRp08frFq1Cnv37rW5/cyZM26PCQAefPBBbNu2zW52SE5ODq5fv+7RcQFg9uzZuHLlCl566SW7t3v7mHr16oWQkBBMmjTJpp+LJEklfr799ttRoUIFhISE4L///S+OHDlSIhOjY8eOKFeuHN5+++0Sv/vBBx/g4sWLuPfee0sc7/r163jvvfeKfy4oKMB7772HatWqoVmzZgCA7t27Izs7u7j0T/69OXPmoGLFimjbti0AoE+fPpAkCRMnTrR5jKUfh6vKeu6V0L17d/z000/Ytm1b8bYrV65gwYIFSE5Odqkcr7Rr164BsP1/smTJkhJ9c1auXImTJ08WB1KbNWuGG2+8EdOnT8fly5dtjuvoXKpSpQp69uyJ3bt3OyyT7NKlC6KiovDGG2/YTA3v7NiuuHTpEiZPnoxRo0bZzSoiIiLyBWb+EBERObFr1y4MHz68zKyf999/Hz/88ANuv/12REVFYd++fXj//feRkJBgt5ktIHqdnDlzxm7gR/bmm28iIyMDd9xxB4YNG4YGDRrg/Pnz+OWXX/Ddd9/Z7WVTlrFjx+KLL77Afffdh8GDB6NZs2a4cuUK9uzZg5UrV+LIkSPF04m7a926dZg8eTJiY2N98phuuukmvPTSS3jttdeQkpICk8mE8PBw7NixAzVq1MCUKVPs/l6jRo3w/PPP480330S/fv3QpEkTVKtWDePGjcPEiRPRtWtX9OjRA/v378e8efPQokULPPTQQyWOUaNGDUydOhVHjhzBzTffjE8//RS7du3CggULiqeEf/zxx/Hee+9h8ODB2LlzJ5KTk7Fy5Ups2bIFaWlpxVkm7du3x8MPP4y3334bBw8eRNeuXVFUVITMzEy0b9/epjm1K1x57r31wgsvYPny5ejWrRtGjBiBKlWqYPHixTh8+DBWrVpl0+vInt27d2Pp0qWQJAmHDh3C22+/jcTERDRv3rzEflWqVMHdd9+NIUOG4NSpU0hLS8NNN91UXHIoB/W6deuGhg0bYsiQIahZsyaysrKQkZGBqKgom1Is2aJFi/DOO+84PM+joqIwf/58PPzww7j99tvRr18/VKtWDUePHsXXX3+N1q1bY+7cuW4+e8Ivv/yCqlWrOiy7JCIi8gUGf4iIiJwIDw93KeunXr16+Oijj/DVV1/h2rVrSEhIQL9+/fDiiy/aLSsBRAPbsoIscXFx+OmnnzBp0iSkp6dj3rx5iI2NRcOGDe02yXVFZGQkNm3ahDfeeAMrVqzAkiVLEBUVhZtvvhkTJ05EdHS0R8cFgISEBKSmpjrdx9vHNGnSJNSpUwdz5szBSy+9hMjISDRp0gQPP/yw0997+eWXsXLlSjz22GPYtm0bjEYjXn31VVSrVg1z587FqFGjUKVKFTz++ON44403igM6spiYGCxevBjPPPMM3n//fcTFxWHu3Lkl+h9FRERg48aNeOGFF7B48WLk5uaiXr16WLhwoc1sbgsXLkSTJk3wwQcfYOzYsYiOjkbz5s1x1113lfkc2OPKc++tuLg4bN26Fc8//zzmzJmDvLw8NGnSBF9++aVNppQjq1evxurVq2EwGBAXF4f27dtj8uTJNkHQF198Ebt378aUKVNw6dIldOjQAfPmzUNkZGTxPu3atcO2bdvw2muvYe7cubh8+TLi4+Nxxx13lJiZrbSIiAhEREQ4HeeAAQNQo0YNvPnmm3jrrbeQn5+PmjVrIiUlBUOGDHHpsTry0ksv2TSNJiIi8iWD5GluMREREZFOtGvXDmfPnrVbqkbK2rhxI9q3b48VK1agb9++ag+HiIgoKLDnDxERERERERFREGPwh4iIiIiIiIgoiDH4Q0REREREREQUxNjzh4iIiIiIiIgoiDHzh4iIiIiIiIgoiDH4Q0REREREREQUxELVHoCvFRUV4cSJE6hUqRIMBoPawyEiIiIiIiIiUoQkSbh06RJq1KiBkBDH+T1BH/w5ceIEkpKS1B4GEREREREREZFPHDt2DImJiQ5vD/rgT6VKlQCIJyIqKsqrYxUWFmLdunXo3LkzwsLClBgekaJ4jlIg4HlKWsdzlAIBz1PSOp6jFAiC4TzNzc1FUlJScezDkaAP/silXlFRUYoEfyIjIxEVFRWwJwYFN56jFAh4npLW8RylQMDzlLSO5ygFgmA6T8tqc8OGz0REREREREREQYzBHyIiIiIiIiKiIMbgDxERERERERFREGPwh4iIiIiIiIgoiDH4Q0REREREREQUxBj8ISIiIiIiIiIKYgz+EBEREREREREFMQZ/iIiIiIiIiIiCGIM/RERERERERERBjMEfIiIiIiIiIqIgxuAPEREREREREVEQY/CHiIiIiIiIiCiIMfhDRERERERERBTEQtUeABERBRezGcjMBE6eBBISgJQUwGhUe1RERERERPrF4A8RESkmPR0YORI4ftyyLTERmD0bMJnUGxcRERERkZ6x7IuIiBSRng707Vsy8AMAWVlie3q6OuMiIiIiItI7Bn+IiMhrZrPI+JEk29vkbampYj8iIiIiIvIvBn+IiMhrmZm2GT/WJAk4dkzsR0RERERE/sXgDxERee3kSWX3IyIiIiIi5TD4Q0REXktIUHY/IiIiIiJSDoM/RETktZQUMauXwWD/doMBSEoS+xERERERkX8x+ENERF4zGsV07oBtAEj+OS1N7EdERERERP7F4A8RESnCZAJWrgSqVi25PTFRbDeZ1BkXEREREZHehao9ACIiCh4mk5jZq29f8fPttwM//cSMHyIiIiIiNTHzh4iIFHXhgmW9oICBHyIiIiIitTH4Q0REijp3zrKena3eOIiIiIiISGDwh4iIFGUd/Dl3Drh+Xb2xEBERERERgz9ERKQw6+CPJAFnzqg3FiIiIiIiYvCHiIgUdvZsyZ9PnVJnHEREREREJDD4Q0REirLO/AEY/CEiIiIiUhuDP0REpCg5+BMeLpYM/hARERERqYvBHyIiUpQc/KlXTywZ/CEiIiIiUheDP0REpJiiIuD8ebHesKFYMvhDRERERKQuBn+IiEgxFy8CZrNYv+UWsWTwh4iIiIhIXQz+EBGRYuSSrwoVgFq1xDqDP0RERERE6gpVewBERBQ85OBP1apAXJxYZ/CH9MJsBjIzgZMngYQEICUFMBrVHhURERERgz9ERKQgOfgTG8vgD+lLejowciRw/LhlW2IiMHs2YDKpNy4iIiIigGVfRESkIHvBnzNnLH2AiIJRejrQt2/JwA8AZGWJ7enp6oyLiIiISMbgDxERKebsWbGMjQWqVRPrRUWWoBBRsDGbRcaPJNneJm9LTWUAlIiIiNTF4A8RESnGOvMnLEwsAZZ+UfDKzLTN+LEmScCxY2I/IiIiIrUw+ENERIqxbvgMAPHxYsngDwWrkyeV3Y+IiIjIFxj8ISIixVhn/gCWvj/Z2eqMh8jXEhKU3Y+IiIjIFxj8ISIixVj3/AE44xcFv5QUMauXweB4n6QksR8RERGRWhj8ISIixTjK/GHwh4KV0Simc3fm4YfFfkRERERqYfCHiAKe2Qxs3AgsXy6WnFVHPQz+kB6ZTMBTT9lur1hRLN99VzR9JiIiIlILgz9EFNDS04HkZKB9e2DAALFMThbbyf9KN3xm8If0Ys8esXzsMWDZMiAjQ5z3zZsD588D/foBhYXqjpGIiIj0i8EfIgpY6elA37620yxnZYntDAD519WrQF6eWGfmD+nJoUPADz+Ivj8TJgD9+wPt2gGRkcCnnwLR0cDWrcBLL6k9UiIiItIrBn+IKCCZzcDIkYAk2d4mb0tNZQmYP8nNnsPCLOUuDP6QHixZIpadOonmz9ZuuAH48EOx/tZbwFdf+XdsRERERACDP0SkUWX18cnMtM34sSZJosdGZqYvR0nWrPv9yDMfycGf06eBoiJ1xkXkS0VFwOLFYn3wYPv7mEzAiBFi/ZFHgMOH2aeMiIiI/CtU7QEQEZWWni6yeqyDO4mJYkYdkwkoKABWrHDtWCdP+maMZKt0vx8AqF5dLM1m0ffE+jaiYLBxI/DPP0BUFNCrl+P9pk0Dtm0DduwA6tcXr2My69c3IiIiIl9g5g8RaUpZfXwGDgTq1AHmzXPteAkJyo+R7Cs90xcAlCsHxMSIdZZ+UTBatEgs+/UDIiIc7xceDgwZItatAz8A+5QRERGR7zH4Q0SaUVYfH0kSs+icOAHEx4tv2uXyotIMBiApCUhJ8e2YyULu+WMd/AHY94eCV24usHKlWJcDO46YzcAbb9i/jX3KiIiIyNcY/CEizSirj49s3DhRZrFwofi5dABI/jktDTAaFR0iOWEv8wdg8IeC18qVwLVrQL16wB13ON+XfcqIiIhITQz+EJFmuNqfp3FjUU5kMokPXzVrlrw9MVFsZ/8M/3IU/ImPF0sGfyjYyCVfgwc7zkKUufr6xj5lRERE5AsM/hCRZrjan8d6P5MJOHIEWLPGkuWzdi0DP2qw1/AZYOYPBae//hJZOiEhwMMPl72/J69vREREREph8IeINCMlBahc2fHtjvr4GI1At25Au3bi5/XrfTVCcoZlX6QnS5aIZadOttmH9qSkiKxE9ikjIiIiNTD4Q0SaUFQEvPQSkJNj/3ZX+vh07SqWa9cqPTpyRVkNn7Oz/TseIl8pKgIWLxbrgwe79jtGo5jOHXAcAGKfMiIiIvIVBn+IyK/MZmDjRmD5crE0m8W0x4MGAVOnin0GDBDfkFtzpY+PHPzZuFE0YSX/YuYP6cXGjcDRo0B0NNCzp+u/56hPWUQE+5QRERGRb4WqPQAi0o/0dDGVu/WMNzVqiGDBnj1AaCjw/vvim3SzWfTTOHlS9MBISSn7G/GGDUWQ6Phx4IcfgC5dfPpwqBQGf0gv5EbP/fqJwI07TCYRMMrMBLZsAV5+Wby23Xef4sMkIiIiKsbMHyLyi/R0oG9f26mOT5wQgZ/wcOCrrywlFEaj6OHTv79YulIKYTCw9EsthYVAbq5Yd9Tw+fRpMZ01USDLzRVZOoDrJV+lya9v48YB1asDly8D27YpNUIiIiIiWwz+EJHPmc0i48fZB//KlYGOHb2/LwZ/1HH+vFgaDLZNu6tXF8vCQuDCBb8Oi0hxK1aIstJ69YA77vDuWCEhlgxFvmYRERGRLzH4Q0Q+l5lpm/FT2qlTYj9vdeggvlX/808xBTz5h9zsOSbGNkurfHnRGwVg6RcFPrnka8gQx42b3cGANREREfkDgz9E5HMnTyq7nzOVKwOtWon1b7/1/njkGkf9fmTs+0OBTG5UP2sWsHmzCPo89JAyx+7USRxv1y5lXgOJiIiI7GHwh4h8LiFB2f3Kwm/S/U8O/pTu9yNj8IcCVXo6kJwMtG8PjB4ttpUrB2zfrszxq1UDmjcX6+vWKXNMIiIiotIY/CEin0tJEbNwOSqRMBiApCSxnxLk4M/334tp5Mn3mPlDwWj1aoPdRvX5+aKBfXq6MvfDgDURERH5GoM/RORzRiMwe7b92+SAUFqaazN6ueK228S36ZcucQYdf5F7/jD4Q8HCbAZGjzY6bVSfmir285Yc/Fm3TpnjEREREZXG4A8R+YXJBIwfb7s9MVFMm2wyKXdfnEHH/5j5Q8Fm375YZGU57ugsScCxY8o0qm/ZUvQrO38e2LHD++MRERERlcbgDxH5zaFDYtmjB7BsGZCRARw+rGzgR8YyCv8qK/gTHy+WDP5QoLhwobxL+ynRpDk0VDR+BviaRURERL7B4A8R+cXFi5b+GC+9BPTvD7Rrp1ypV2mdO3MGHX9iw2cKNjExeS7tx0b1REREFAgY/CEiv1ixArh2DbjlFqBFC9/fX7VqQLNmYp1Tvvsey74o2DRocA41a0p+a1Qvl6r+9JPl/xMRERGRUhj8ISK/WLRILAcPdjzrl9L4Tbr/uNPw2VkDXSKtMBqBmTNF9+XSr1m+aFRfsybQuLH4/7F+vTLHJCIiIpIx+ENEPnfwILBli2jE/NBD/rtfzqDjP65m/uTnixJAokDQu7eElSttyxl90ageYMCaiIiIfIfBHyLyucWLxbJLF6BGDf/d7x13iBl0LlzgDDq+VFQkZikCHPf8iYgAKlUS6yz9okBiMgEzZ4r1+vV926i+WzexXLtW/L8iIiIiUgqDP0TkU2azJfgzeLB/75sz6PjHxYuWD6qOMn8AS/bP6dN+qvsjUsiJE2LZrJlvG9W3bg1UqCACpLt3++Y+iIiISJ8Y/CEin8rIAI4fFxk4PXr4//5ZRuF7cr+fihWBcuUc78emzxSojh0Ty6Qk395PuXJAhw5ina9ZREREpCQGf4jIp+RGzwMGAOXL+//+OYOO75XV70fGzB8KVP4K/gAMWBMREZFvqBr8MZvNGD9+POrUqYOIiAjceOONeO211yBZTQUjSRJeeeUVJCQkICIiAh07dsTBgwdVHDURueriRSA9Xaz7u+RLxhl0fM/d4A8zfyjQ+DP4Iwest2wBcnN9f39ERESkD6oGf6ZOnYr58+dj7ty5+OOPPzB16lRMmzYNc+bMKd5n2rRpePvtt/Huu+9i+/btqFChArp06YK8vDwVR05ErvjsM+DaNaBBA6B5c/XGwW/SfUsO/jhq9ixj5g8FKjn4k5jo+/u64Qbg5puB69eB77/3/f0RERGRPqga/Nm6dSt69uyJe++9F8nJyejbty86d+6Mn376CYDI+klLS8PLL7+Mnj17okmTJliyZAlOnDiBzz//XM2hE5EL5JKvwYMBg4qf962DP5xBR3nM/KFglpcHnDkj1v2R+QMwYE1ERETKC1Xzzu+66y4sWLAABw4cwM0334zffvsNmzdvxsx/51Q9fPgwsrOz0bFjx+LfiY6Oxh133IFt27ahX79+NsfMz89Hfn5+8c+5/+ZMFxYWorCw0Kvxyr/v7XGIfEVL5+iBA8DWrWEICZHwn/9ch5pDatkSqFAhFKdOGbBzZyGaNlVvLMHo1KkQAEbExJhRWOg4uhYbawAQilOnRGmvFs5TInusX0uPHgWAMJQvLyEqyj+vZR07GvD226FYu1ZCQcF1VYPnpF1aes8nsofnKAWCYDhPXR27qsGfF154Abm5uahfvz6MRiPMZjMmT56MgQMHAgCys7MBAHHy18X/iouLK76ttClTpmDixIk229etW4fIyEhFxr2ejUM0wWwG9u2LxYUL5RETk4cGDc75bPrdQKOFc3Tp0lsA3IzbbjuFX3/djl9/VXc8DRq0xI4dCXjppSw0bnyW54yCdu1qAqAOzp07iDVr9jvc7++/YwC0wT//iLJdLZynRM6sX78ee/bEArgbVapcwTff+KcOKz/fiLCwbjh61IgFC35AUtJlv9wvBSa+lpLW8RylQBDI5+nVq1dd2k/V4M9nn32Gjz/+GMuWLUPDhg2xa9cupKamokaNGnjkkUc8Oua4ceMwevTo4p9zc3ORlJSEzp07IyoqyqvxFhYWYv369ejUqRPCwsK8OhZ5Z/VqA0aPNiIry/J1aM2aEmbONKN3b8nJbwY3rZyjZjMwfLh4eRkzpiq6d++u2lhkX34Zgh07gPXrk7F+fTIAnjNKWbJERNDuvLMuune/0eF+9esDL7wAXLoUCUkCOnfmaylpk/Vr6fnz5QAA9epF+vW1rF07A9avB/Ly2qF7d9arki2tvOcTOcJzlAJBMJynuS7OEKFq8Gfs2LF44YUXisu3GjdujH/++QdTpkzBI488gvj4eADAqVOnkJCQUPx7p06dQlMHdRvh4eEIDw+32R4WFqbYH1PJY5H70tOBfv3E7E3WTpwwoF+/UKxcCZhM6oxNK9Q+RzduBI4fB2JigN69Q6H2f5f0dODDD22385xRxoULYhkXZ0RYmONUKrlZ7rVrBuTlhap+nhKVJSwsDCdPikulWrVCEBbmv1aJ3bqJGQrXrzfi2WeZokiO8bWUtI7nKAWCQD5PXR23qg2fr169ipCQkkMwGo0o+rcja506dRAfH4/vraa7yM3Nxfbt29GqVSu/jpW0wWwGRo60DfwAlm2pqWI/Uo/c6Ll/f6B8eVWHwnPGD1xt+FyhgvgHADk5tkF6Ii3y50xf1rp1E8tNmwAXs7mJiIiIHFI18+f+++/H5MmTUatWLTRs2BC//vorZs6ciaFDhwIADAYDUlNT8frrr6Nu3bqoU6cOxo8fjxo1aqBXr15qDp1UkpkpMkockSRxoZ6ZCbRr57dhEUTwJDMTOHQIWLFCbBs8WNUhAeA54w9nz4plWcEfQMz49fffDP5Q4JBfP/w105esXj2gdm3gn3+AOXOAWrWAhAQgJQXsVUZERERuUzX4M2fOHIwfPx5PPfUUTp8+jRo1auCJJ57AK6+8UrzPc889hytXruDxxx9HTk4O7r77bqxduxbl1U4nIFWcPKnsfqSM9HSRXWMdZAkNBY4eBVq0UG9cAM8ZX5Mk1zN/AAZ/KPDImT/+Dv4YDMDNN4vgzwsvWLYnJgKzZ7NUlYiIiNyjavCnUqVKSEtLQ1pamsN9DAYDJk2ahEmTJvlvYKRZVq2fFNmPvJeeDvTta1tWdf068MADUL2fDs8Z37p6FcjPF+tVq5a9vzx5I4M/FCjUCv6kp4ueP6VlZYnXXLVfW4mIiCiwqNrzh8hdd9/tvIeMwSAu0FNS/DcmPXPWT0emdj+dlBTxTbnBYP92njPekbN+ypWz9PNxhsEfCiRXrwLnz4t1fwZ/5NdWe9irjIiIiDzB4A8FlNmzgbw8sW7vw7wkAdOnsx+Cv7jTT0ctRqM4bwDbc0b+OS2N54ynrPv9OAqwWWPwhwKJnPVTsSIQHe2/+w2E11YiIiIKLAz+UMDYts3S9+CJJ4CaNUveLn/w3L7dv+PSs0Dpp2MyiRKJ0udM9eosnfCWO/1+AOvgD/u2kfYdPy7eWJKSXAtuKiVQXlv1yGwGNm4Eli8XS2ZfERFRoGDwhwLCuXPAf/4j+sj06wfMnw8cOQJkZADLlonlypVi35kzgf/9T9Xh6kYg9dMxmSznzJ13im2DBjHw4y3Pgz/M/CHty8oSS39P8x5Ir616kp4OJCcD7dsDAwaIZXKy2E5ERKR1DP6Q5hUVAY88IlLc69YF3ntPfANrNIqpufv3F0uTCRg1SvzO4MHigz75VqD105HPmdRU8fPq1c77FVHZ5OCPK82eAQZ/KLAcO2bJ/PGnsl5bAXG7Vl5b9UCe3KB0OZ7cgJsBICIi0joGf0jzZs4Evv4aCA8HPvsMiIpyvO+bbwItWwI5OSJDqKDAb8PUJet+OqVpuZ9O9+7ifPrrL2DvXrVHE9g8zfy5eJHBH9I+67Ivf3LWq0wWHQ1cuuS/MemZs8kN2ICbiIgCBYM/pDnW9fRz5wLPPy+2z54NNG3q/HfLlQM+/RSoXFn0/nnhBdbn+5rJBDz+uO32xETt9tOpVAno3Fms89ta71g3fHaFHPzJywvFlSu+GRORUuQsD38HfwDHvcqqVROzXv7+u8j8kcfI9zr7XH1enO3HBtxERBQMQtUeAJG19HTx7Vrpi6y777YfYLAnORlYtAjo1QuYNQtYssSSnQCIoMTs2doMSgSq3bvF8v/+D2jTRvShSEnRXsaPtT59gC+/FOfchAlqjyZwuZv5U6kSEBEh4do1A06dEoFaIq1Sq+xLZjIBPXuKoMLJk5bX1j17RAbj3r1Aq1bAmDHAjBkl3zv5Xmf/msLe8+Jsv+7dRW9BV7ABNxERaRkzf0gzHNXTA8CWLaI/i6t69gTuu0+sWwd+ANbnK23/fjETm9EogihyDyYtB34A4P77xRh37xblX+QZd4M/BoMl++f0aT9On0TkATUzf2Sl+9sZjSILdts24JZbxBhHjWIvmtJc7dHjbL8+fcTr1fvvu3afbMBNRERaxuAPaYKzenqZO/X0ZjPw66/2b2N9vrIWLRLLbt2A+HhVh+KWKlXETC2Afj8cKcHdhs8AUL26+E946pQPBkSkkKtXQ5GbKwKU/p7tyxW1awObNolyZ3v0/F7nao+egoKy98vNFaV30dGBM7kBERGRPQz+kCYoXU+fmWmZoleJ45F9ZrMoqwPEDGuBpk8fsWTwx3Pu9vwBgOrVxZKZP6Rl585FABAf+itVUnkwDvz+u/OJDfT6XufqNUW1as73ky1aBHz4oVi3FwCSJG1ObkBERGSNwR/SBFfr5NXaj+z77jvgxAmRRSOX2QWSXr3Ehfz27a59ACBb7pZ9AZayL2b+kJadPVsegLolX2Xx5L0uWBpDO3schw+7dozcXNf2O3PGcQNuAAgNBW6/3bVjERERqYXBH9IEV+vk1dqP7JNLvgYMEFOnB5r4eKB1a7HuTk8pEgoKLFNNu5f5I+opTp/2waCIFHL2rMj80XLwx9X3sN9+A/LzRZZjcrIoeR0wQCyTkwMv+9HR43j/fTHL54gRrh3n2Wdd209+nk0m4MgRICNDNIHesEHc9/Xr4n6JiIi0jMEf0oSUFPvfpsncradPSRE9GhzV5wOWWVPIMzk5loBJIJZ8yeQZX1atUnccgej8ebEMCXFv1i5L5g/Lvki7AiH448p7HQBMnSr+3/XpE/iNoR01aD5+XMwKOnUqcPmy8xIs+Zpi8mTnz5+9aw/rBtzt2wMzZ4r9Pv1UNOEmIiLSKgZ/SBOMRqBtW/u3yRdl7tTTG41iilbr3y/t2jUxXS555tNPxTfJjRoFdrp7795imZnJTBR3ySVfMTHu9bpg5g8FgkAI/jh7rzMYxL+HHgJq1AAuXrR/jEBqDO3K5BDh4SJA9MknlufAmvU1Rblyzp8/eT9nr29NmwJDhoj1UaOcj42IiEhNDP6QJmRnA198IdarVCl5W2KiqLOXMzRc5ag+v0YNoFYtkbnSpg2wfr3YHix9EPxFLvkaMqTsb521LDkZaNYMKCqynIPkGk+aPQPqZ/7w/zq5Qg7+aHGmL2uO3uvk986PPgIWL3Z+DC01hnb2/7OsRs6A+FIiJkZkBzl7XuRrirKeP1euPV5/HahQQfSP+/TTsve3psfXIz0+ZiIiLQhVewBEAPDKKyJNu2VLYPNmYMsW0aBSLs3ydAYNkwno2VNcMFof7/JlkfGRkQF07w48+aQoYbK+qExMFN8Iuht00oM//wR+/FH8XQYOVHs03jOZgJ07RenXY4+pPZrA4UmzZ0Ddqd7T00XmAP+vU1nk2b60nPkjc/ReJ793njnj2nHUngTB2f/Pli2BGTNcO478OMp6XmSu7udIQoLo+TN+PPD88+JYERHePd5gfT3S42MmItIKBn9Idbt3Ax98INZnzgTCwkQ9vVLk+nxr0dHAN9+IXjWffALMmWP7e3IfBE+yjoKdnPXTvbsliyOQ9ekDvPQS8P33IiPMnf41euZp8Ec+Zy5dMuDaNdc+JClB7hVSuiyD/9epNEkKjLIva/be62SBMAmCo/+fx4+L1+iQEJGh6Qrrx+HsebHm6n6OjB4NvPcecPSoKBUbN875/np8PdLjYyYi0hKWfZGqJAkYM0Zc0D3wgGXmJX8IDweWLAEqVnQ8NiAw+iD4k9ksygiAwG70bK1ePaBBA6CwEPj6a7VHEzjk4E/Vqu79XnQ0EBoq/lP5K/vHWa8Q/l+n0nJygLw88f2Y1su+XFFWY2h3J1VQmiu9fIqKRG/A2FhtPo7ISGDKFLH+xhvOX9v0+Hqkx8dMRKQ1DP6QqtasAb77TjRdfPNN/9//li2iBMwRLfVB0Ir164ETJ8QF+H33qT0a5XDWL/d5mvljMACVK+cD8F/wp6xeIfy/TtaOHRPL2FgJkZHqjkUJZU2CIEkicOFpibW3XOnlAwCvvgosWCDWPW3Q7EsDBgDNm4vrildecbyfHl+P9PiYiYi0hsEfUk1hIfDss2J95Ejghhv8PwZX+xuo3QdBS+SSrwEDRNAuWPTpI5Zr1wJXrqg7lkDhacNnAIiJ8W/wh//XyR1ZWSKSEAxZPzJHjY1D/r0S/PZb385U5azJ7969rh3j5EllGjT7SkgIMGuWWH//feDDD+0/Xjm4WJZgej3y5DWYjaGJiJTFnj+kmgULROPgqlVFvxU1BEIfBC25cAH4/HOxHiwlX7JbbwXq1AEOHxYBIDkYRI55mvkDANHR/g3+8P86ueP4cRH8qVlTAhDA0xmWYq+xMQB06CDKedu3t0xbriRHTX6feQb49Vfgs89cO448Xm8bNPvS3XcDrVoB27YBjz5q2Z6YKGYFO35c9Dd0RTC9Hrn6WL77DujUCfjhBzaGJiJSGoM/pIqcHGDCBLE+aZLoAaIGuQ9CVpbjbzzj4tTrg6A1n34qptFt3Bi47Ta1R6Msg0EEfKZPFx9UGPwpm6c9fwD/l32V9X/dYBC38/86AZbMjKQkH6bCqMReY+PXXwdefBF4+mmgRQugUSPl7s9ZI+fnn7f8HB4u3l/ssff/09sGzb6Sni5mwyzt+PGSX5o4a2AdjK9HKSliMoWcHOf7ffihCEQWFtrexsbQRETeYdkXqWLyZPHB8ZZbgGHD1BtHWX0QAODiRTELFFlKvoYMcfx8BTL5YvKrrxx/CCELbzJ//B38ceX/upq9Qkhb5MyfYCr7cub554EuXYBr18TkC8564bnDlUbOkZHAzz8Dy5aJ/5ta7OXjKlceb1gYsHixKGWy93hlgfB43bFrl+PzSn4eUlPFF0v2Aj8AG0MTEXmLwR+NC6Z6Z/mxpKVZauJnzABCVc4/c9Q/oGZN8e1nXh5w773iYg0Irr+JK+THO306sH27uBgdOFDtUfnGHXcANWoAubniHNXL39hT3vT88XfwB3D8fz0qit8kU0lyqUliYvBl/tgTEiKyLWrWFOXYTz4JXL/u/XudK42cr14FLl3Sdi8fV7nyeAsLgVq1gAcftP94AWDQoMB4vK66eFE83uvXgZYtbYOq8t941ixxXeiMvcbQersuIyLyFMu+NMxRjXwg1jvbeyzh4eKiTwsc9Q8wm0WWy7JlIl173TpRhx4MfxNX2Pu7hYUBmzcH5+MNCQGaNBGzmY0bZ9kezH9jTxUViR5QgKfBnzwA/g3+AOJv2L49UKWKZVurVvzbUkly5k9SksoD8aNq1YBPPhGlVEuXAl9/bfk/Dnj2Ouhuk18t9/JxhbePd+dOEfzIyBCBErW/HFOCJIneR3//DSQni556UVGO/8bZ2a4dd/9+ca4G07UyEZGvMfNHo+Qa+dLfIMn1zunp6ozLE44eS0GBSC/XymOR+wf07y+WRqOYzeqjj4DnnhP7LFsWHH8TVzj6u+XlBefjBcRjWrvWdnuw/o29kZNj6VcRCA2frZ0/X/LnH3903HuD9EeS9Jf5I7v7bqBfP7FuHfgBPHsd9KTRur334kDh7eN9/XWgenXg6FGRCRMM3nkHWLVKfHH06adATIzzv7Grz+Ezz4jG0MFyrUxE5A8M/miQs5rxQKt3DobHEhICvPGGaFRoT6A8Dne40rcgmB4vYHnM9gTj39hbcr+fSpVEkNRdapR9yeRytZo1Rb+RixdFqQsRIM7tvDx5ti+VB+NnZjOwaZP92zx5Hbz7biAiwvHtBoPIrgqWxsZyY3lHfXzKerzly4um24AotXb2HhwIfv4ZGDNGrL/1lij5KktZzyEgAkmFhWJmsEC+viQi8jcGfzSorJrxQKp39uSxaFFmpvMZKgLlcbgqWP5u7tDjY/aGN82eAUvwJyfH/821z5wRy/h4y4eRrVv9OwbSLnmmr+joPISHqzsWf1P6dXDBAtFE2p5AauTsKmeN5V19vE8+KYJAO3eKMvNAYn0t+tVXIru7oADo3RsYMcK1Y5T1HBoM4vhvv+38OHzPJiKyxeCPBrlbM56eLuqo27cHBgwQy+RkbaS7uvtYtCpYHoer9PZ4AX0+Zm940+wZACpWLERYmPh69vRphQblInns1aoBd90l1hn8IZkc/KlaNU/dgahAydfBX34BRo0S64MHO27yG2x9WbxtXF2tmmVK+LKaH2tJ6WvR++8HjhwRj+fDD92bIbSs57BPH6BqVdeOxfdsIiKLIGglF3xcrXd+7TXxzdDMmbZpr3K9s9oXVp7Uv2tRsDwOV+nt8QL6fMze8Dbzx2AQvS2yskSDT3821pUzf6pWFc2eAWDbNv/dP2mbJfhzDUAFVcfib0q9DsqzOxUUAD16iA//RUWB28jZXd42rh41CnjvPeDLL0VJav36vh2vt+QegfZKsM6cATZscP9atKznkO/ZRETuY+aPBrlS7wwAf/whvhXScr1zSorzN95Aqff3to4/0Ojt8QL6fMze8Db4A4jgD+D/vj9y8KdaNeDOO8X6n39aHhPpW8ngj764cv0RF+f8dVCSgMceAw4dAmrXBhYuFMcL5EbOnvDm8d58s8icAcT051pWVo9Ag8Hza1Fnz6Er52q1anzPJiKyxuCPBrlS7/zBB8CgQc6Po4V6Z6MRuOUW+7cFUr2/s78JIJ7r6dO1/zhcJT9eexdzgfR3c4cSvRr0RA6UuJp6b09cnDjB/B38sS77qloVqFdP/Pzjj/4dB2mT3PNGj8Gfst7rAJHVs2GD42PMmyeyjkNDxexOVaooP049ePZZsVyyxP+lse5Qq1+eK+fq+fPAihXK3i8RUSBj8Eejyqp3HjoU6NrVtWNZ1zv7uzH0rl1ARoZYl7/llwVavb+jv4l80bFzp//H5Ev3328/qyPQ/m7u8LZXg5542/MHUD/zRw5csfSLrMmZP7Gx+gv+AI5fB2vWBBo1AvLygO7dgY8+Etutryvee8/S52faNOCOO/w69KBy991Aixbi+Z43z/Xf8/d1npr98py9Z7dqJR57//6WmdO0OjkKEZG/sOePhilV7/zHH8D168AXX4jUXOtvaBITxTcnvvhQK0liik9JAvr1A5YuDfx6f3t/k9Ongf/8R1zotmkD3Huv2qNUxmefieyO6tXFRf65c4H7d3OH/Dd+6ikxU0379sD69cH9mD2hRNmXFjJ/ANH0edEiNn0mQc9lXzJH1x/XrwNDhogPz4MGAWvXihmpSmd+tGghSn3IcwaDuIbq1w945x3g+eeBiAjnv5Oe7t/rPED93juOzlWDARg9Wjz2sWPFF5G7d/v3uSEi0hoGfzROrne2R653zspyXGsNiMbQ8+dbPvBY82Vj6K++Eqnh4eHAlCnOH0sgsfc4Nm8G5swRF8O//grUqqXK0BQjSZZZRkaMADp3Vnc8/mY0Ar16ieDP6dMM/NijTPBHLNXO/JFn/Nq+XXy4DeU7o24VFem77Muavfc6o1F8kZOYCLz1FrBsmf3f/flnYPVqfqj2Vp8+om/SP/+I8q8nnnC8r6Omy76eACQlRXxJ5Kg0zWAQ54sve+84ur5MSxPXY2PGAGvW2N6ulclRiIj8hWVfAcyV3kAPPig+4NgL/AC+awxdWGipVx81Skz/Gczeegto3lzUl/frJx5/INu4UQSxIiKA//s/tUejjkaNxHL/fjFjDZWkRM+f6tXVyfyxbvgMiL5k0dHA1avAnj3+HQtpy+nT4vXbYJBQpYr+pnp3RUiI+EKncmXn+6k94UQwCA21ZFDNnCmCk/Y4a7rs6wlALl1yfJsW+uWNHOm475RWJkchIvIXBn8CXFk9Sj79FPj4Y+fHsNeMz9u66PnzgQMHxLdB48a597uBKDxcPNfR0aJvyEsvBXZt+fTpYjlkiHeZHYEsMVH8Pa9fFwEgKkmJzB85cHTggP/+jxQUALm5Yl0O/oSEWGb9YumXvsklXwkJQGiok5RancvMBHJyHN+uhQkngsWjj4r3ogMHgDfftH9NsXmzwaOmy95ep0iS6EF5+rS43qtRo+TtWuiXl5kpvphzhOcqEekJgz9BwGQCjhwR9czLlonl4cOWN1tXpy+WL3rT00WmTvv2wIABYpmcLLa74vx54NVXxfprrwFRUa4/lkB2ww3Ahx+K9bfeEiUtnj6HavrjD5EebTBYGnfqkcFgyf7Zu1fdsWiNJHnf8HnbtgQMGSK+Cj5xwn//R+RxG40lMxfk0i8Gf/RN/gCdmMjAjzNqNvnVm0qVLCVNL71U8ppi9WqRWvP3364d659/LOveXusBotx99WogLAz4+mvg6FHH16Jq4blKRGTBzgZBwlk/HVeb7I0ZIxrbLl3qXc34668DFy6ID85Dh7p238HCZBKzoKxZYxt0C5Ta8pkzxbJXL+Cmm1QdiuoaNQK2bGHwp7QrVyylcJ4Ef1avNmDq1BY22/3xf8Q6aBVi9fWHHPzhjF/6Jn8JUjqblkpSu8mvnqSniwk7SsvKAvr1M6JZs5bYt8+1mqrUVPFlYa1aIqPIm2u9HTss5f0zZojSd0B7vR15rhIRWTDzRwfkxtCl+wJZCwkRfTA++si7mvGDB4G5c8X6jBn6a5xqNgO//Wb/tkCoLT91SjSVBEQwUO/kzB/2gSlJDmyWKwdUqODe75rNwOjR8geVki9K/vg/UrrZs6xlS/EaefgwvwHWMzn4k5TEzB9nyrquMBiApCTfNvnVg7J6+UgS8PPPCbh61VDm9ZbRKEr1Xn1VfDHnzbVeTo7oKVlYKJpSDx/u2uNRA89VIiILBn90wJXG0B9/LKbCdMaVuujnnxcXA9266W+GKEA8N1lZjm/Xem35O++IjI4777RkQuhZ48ZiycyfkqybPTsLKtsj/o8YUDrwI/P1/5HSzZ5lUVGWvzezf/RLDv4kJqo7Dq0r67oCULfJb7DIzITTXj7y6+jrr5uxfLnlmq7EHv9uW7YM+OQToH595/dZ1muw3OfnyBFR7v7BB+6/D/iTs3NVxnOViPSCwR+dKKsxdL9+wG23uXYs62/FrZsFpqWJ2m+j0dIwWG8Cubb86lVg3jyxPmaMti/m/KVhQ7E8fNj5jCZ6402zZ7X/j8hlX6WDPwBLv8g6+MPMn7KUdV2h5fLmQOHq62CtWlJxuZajv8eDDwL/+Q8wfrz79219rffMM+Jar1w54LPPRDNqrXN0rgJA69Y8V4lIP3RWlKNvJhPQs6f4NufkSVHfnJJi+bbD1XrnjRuBrl1FM7+RI22/lerYEWjQQNGhB4xAri1fvFh8qK9TB+jdW+3RaEPVqkB8PJCdDezbB9xxh9oj0gZvmj2r/X/EUdkXALRqBbz7Lps+65n8fpaU5PpkCXpW1nUFecfd10tX/h6lZ+RyJDxcLNPT7V/rPfww0KyZa8fSgtLPzeXLwOOPA5s3Az/+aJnxkYgomDH4ozPOGkPLddFZWfZrwWULFohAQX6+/dvXrRMXC3r8JsWV57BmTe3VlhcVAbNmifVRo3jhbq1xYxH82buXwR+ZN5k/KSlAzZrSv+WRtullBoP4P+Sr/yOuZP78/LN4fZM//JA+mM2Wst3ERInBHxc5u64g75R1TWEwSIiNvYa77w4r3lbW38PVa73+/YG77xZf9Nnb78MPxQQXgXStV/q52bYNWLgQGD1aTO7AjGciCnYs+6JirvQGevpp0QTXUeBHpuWmxr7kSm25JInpULXkyy9Fs+7KlYEhQ9Qejbaw6bMtb4I/RiMwc6Z4cTAYSn6i8EevEGeZPzfeKIJCBQXAr7/65v5Ju7KzxfuW0Sgy/ojU5kpvpUcf3evW66Urx6xbV7wObtjgPEAU6Nd6r78uJi3Ytk2UsBERBTsGf6iEsmr4584F3n7b+TG03tTY1xw9h/Hx4sPyiROivOSXX8R261r6jRv9dyFlfb8vvyy2/d//ARUr+uf+AwWbPtuybvjsid69JTz//A6b8gN/9Apx1PAZEB98WrUS6yz90h+530+NGsx+JO1wdl32ySdmtGrlfoM0Z8dctQo4cMAyc6sjwXCtV6OGmKgEEMu8PHXHQ0Tkawz+kA2TSczikJEhZofIyBANb+UPZNnZrh1Hi02N/cXec3j8uJgGvkkTMaV627bAK68AyclA+/bAgAFimZwsyuZ8KT295P3KgY0bb/Tt/QYiOfOHwR8Lb3r+yFq1Oom//rqOnj3Fz4MGlXyd8RV57I4CV3LpF4M/+mOZ5l3dcRCV5ui6rHdvzxuTl3WtV6WKa8cJ9Gu9MWNEEOyffywZUUREwYo9f8guZzXjajdsDRT2nsOaNYEffhAXVxs2AK+9Zvt7WVkonrXDFx+E09PF8e2lcj/+uLjgC6Qafl9r0EBkhJw6JbJG7GWM6I03ZV/WjEagXj2xXrWqf7ItnGX+ACVn/JIk9oDQEwZ/SMvsXVMUFSl/TJlervUiI4EpU8QXEJMni9L36tXVHhURkW8w84fcJjcLdPShyGAQF89aa2qsFdHRwFdfiQsOe+SgjC9q6c1mMWtHMNfwK61CBeCGG8Q6s38EpYI/gHh+AeDKFe+PVRZJct7wGQCaNwdCQ0V5ptZ6c5FvWc/0RaR3errWGzhQzFx26RIwYYLaoyEi8h0Gf8htrjQL9GXD1mCwfTtw9arj231VS5+ZaTtdqz/uN9Cx9Kskb3v+WJN7TF2+7P2xypKTYwlsOhp7RARw221inaVf+sLMHyILPV3rhYRYZjxdsIDv9UQUvBj8IY+U1RiaZUPOuVojr3QtvVr3G+g441dJgZr5I5d8VarkfBp369Iv0g85+JOYqO44iLRCT9d6KSlAnz6ilG7MGHUm4iAi8jX2/CGPmUxAz54iS+TkSVH3nZISHN8C+ZpatfR6qeFXGmf8sigoEKnxgDLBH39m/pTV7FnWqpX4xpuZP/rCzB8iW3q61ps6Ffj8c2DdOvFPlpgo3hOCKdhFRPrE4A95xVmzQHJMrqXPynLcf0e+wPLF/Toq/TIYxO3BUMOvJOuyL703AZazfkJCgMqVvT+eGpk/ZTXtljN/du0S45LHSMGrsNCS8cjgD1FJernW++03+1k+vp6Ig4jIX1j2RaQCZ7X0sitXxIdPpe83Lc3+bcFWw6+km28GwsJExovemwDLwZ+YGBEA8pYamT9lBX+SkkQQ1GwGfv7Z9+Mi9Z04IQK7YWGc6YdIj+QJMezx5UQcRET+xOAPkUoc1dLXqAHUrg3k5gJt2wJr14rtZrMyNejy1LClg07BWMOvlLAwoH59sa730i8lmz0D6mT+uDL2Vq3EkqVf+iBnQyYmKhPUJKLAwgkxiEgPeIlDpCKTCThyBMjIAJYtE8ujR4Hdu4GOHcUH4vvuA4YPB5KTgfbtgQEDxDI5GUhPd+/+8vKA558X66+8UvJ+Dx9m4McZNn0W5OwZJfr9AP7N/HG17AuwlH4x+KMP7PdDpG+cEIOI9IA9f4hUZq+WPioK+Ppr4LHHgI8+At55x/b3PKlBf/ttEeSpWRMYO5a9TNzB6d4FJWf6Avyb+eNqw2eg5Ixfeu/zpAec6YtI3zghBhHpATN/iDSqXDngww/FtNT2uFuDfvo0MHmyWH/jDQZ+3MUZvwSlgz9y5s+VK5aSRF9xJ/OnaVOgfHnxeA8e9OmwSAOY+UOkb/KEGM4C/UlJnBCDiAIbgz9EGrZ5s2VabXvcqUF/9VXRR+j224GHHlJsiLohZ/788YeYGUivfJX5AwDXrilzTEdcbfgMiOBr8+ZinaVfwY/BHyJ9c2UijvHjOSEGEQU2Bn+INEypGvTffwfee0+sz5zJhqaeqF1bBCoKCoC//lJ7NOowmy2ZT7m5ysx6EhFhudD2dd8fdxo+A8Cdd4rlRx9512SdtI/BHyJyNBFHWJhYfvQRcP26/8dFRKQUfgQk0jClatDHjhUlNb17ixnEyH0hIfru+5OeLpqMf/ut+Pn99z1rOl5aSAgQGSnW/RX8cSXzJz0dWLhQrG/Y4HmTdQoM8iw/DP4Q6Zu9iTh27xYl+JmZwIQJao+QiMhzDP4QaZgrNehxcc5r0L/9FvjmG/HN1bRpyo9RT/Q641d6umguXnoaXLnpuLcBEeu+P75y7Zrl+GVl/siPVy5xkyn1eElb8vOBU6fEOoM/RCRPxNG/v1jWry++8ABEz8S1a9UcHRGR5xj8IdIwV2rQL1ywZGOUdv06MGaMWH/mGeCmm5Qfo57osemz2QyMHGlpMG7N3abjjsh9f3yZ+SP3+wkLA6KjHe/nj8dL2pKVJZblyyvXy4qIgst//gM8+aRYf/hh2y9DiIgCAYM/RBrnqAa9Zk3g1ltFD5oePYAPPhDbzWZg0yYDli8HnntO9PupUgV4+WX/jz3Y6DHzJzPT+UWuO03HHfFH5o/1NO/OMun88XhJO8xm4IsvxHqVKr6fcY6IAtfMmcBtt4n3k/79Rdbgxo3A8uXO+8KZzcruR0TkqVC1B0BEZTOZgJ49xQfOkydFj5+UFPFB5bHHgCVLxHLt2hBkZHTGuXMl/2v37g3ExKg0+CAiB38OHQKuXrX0qglmSjUdd0YO/vgy88fVZs/+eLykDenpIstLDvadOCH6Os2eDdx/v6pDIyINKl8e+OwzMWvq5s2if5z1jKyJieL1w2SybCv9OuPtfkRE3mDmD1GAKF2DbjSKEpZFi4CXXhL7rFxpxLlz5W1+98MP2adECXFx4mJPksSU73qgVNNxZ+SyL19m/rja7Nkfj5fUV1Yfq9WrnaSHEZFu3XQTMGyYWLcO/AC2feFc7Zfn6756REQyBn+IApzBAEycCFSuDAASAPsfWtinRBl6K/0qq+m4wSCa5DprOl4Wf2T+WJd9OeOPx0vqcqWv05gxRr5eEpENs1lk/9hj3ReuoMC1/nGu7sfXIyJSAoM/REEgMxPIyQEcBX7Yp0Q5epvu3brpeGlygCQtTeznKS1l/rjSZN3bx0vqcqWv0/HjBuzbx+7PRFSSq33h6tRRdj9evxGREhj8IQoC7FPiP3qc8UtuOl46ayYxUWz3th+BPzN/ygr+AI6brFeqpMzjJXW5+jp44YJtCS0R6Zurrx8nTii7n/X9sjE0EXmKwR+iIMA+Jf6jt7IvmckEzJgh1hs1AjIygMOHlQmE+DPzp6yyL5nJBBw5Ih7no4+KbS1bMvATDFx9HYyJyfPtQIgo4Lj6+vHEE8ru98UXIkMoPV00pm/fHhgwQCyTk9kXiIhcw+APURCw9CmxUzQO9ilRUsOGYnniBHD+vLpj8bcLF8SyQQNL03El+HO2L1cyf2Ryk/WnnxY/79xpvy8DBRZX+jolJkpo0OCcfwdGRJrnal+4t99WZj/ZJ58AtWsDffqwMTQReY7BH6IgULIvS8lPp0r1ZSEhKkpcgAHA77+rOxZ/k4NdsQq3QvFH5o+rDZ/tadQICA8XfbUOHVJ0WKQCZ32d5J9nzDDz9ZKIbLjy+pGWBpQrp8x+BgPwwgvii4iiIvtjYmNoInIVgz9EQcJkAj75xIzY2JKlCkr1ZSELvZZ+nfs3EaJKFWWPq9XMH1lYGNC0qVj/+WfFhkQqKquPVe/eTPEiIvsc9YUrfb2l1H5TpgATJjgfExtDE5ErQtUeABEpp3dvCaGh6xAVdS/OnAlFQoJIUeY32Mpq1Aj4+mt9NX0GAjfzx2y2jN2T4A8ANG8ObN8ugj/9+ik3NlKPyQTk5QEDBwL16wPz51teLwsL1R4dEWmZyQT07CmCLSdPwuH1llL7cWIPIlICgz9EQcZoBNq2lRAWpvZIgpc845deM3+UDv74OvPnwgVLurynY2/RQix37FBmTKQN8gel224TZRVERK6S+8L5Yz9O7EFESmDZFxGRm+Syr7179dUA2FdlX3Lmj6+CP3LJV+XK8Dgo2ry5WP7yC3sqBBO5cWpiorrjICJypqxG0wAQGipm/iIicoTBHyIiN9WvL76hy8kRs37pha/KvuTMH1+VfXnT7FlWv74IUl2+DOzfr8y4SH1ZWWJZutcGEZGWuNJo+vp1oHVrYPdu/46NiAIHgz9ERG4KDwduvlms66n0K9Azfzzt9wOIC+/bbxfrbPocPOTgDzN/iEjrnDWGfvddoEED8YVUSgqwYYPIUt24EVi+XCyZtUpEqgd/srKy8NBDDyE2NhYRERFo3Lgxfra6spYkCa+88goSEhIQERGBjh074uDBgyqOmIioZOmXHhQWArm5Yj1QM3+8Cf4AltIv9v0JHnLZFzN/iCgQmEzAkSNARgawbJlYHj4MPPEEsHmzCPzk5gKdOwPVqwPt2wMDBohlcjKQnq72IyAiNaka/Llw4QJat26NsLAwfPPNN9i3bx9mzJiBmJiY4n2mTZuGt99+G++++y62b9+OChUqoEuXLsjLy3NyZCIi35KbPusl+HPhgmXd6iVaEdYNn33RQ0nO/PGm7AuwNH1m5k9wKCqylG0y84eIAoXcGLp/f7GUZwSLiQHWrQNatSo5y6UsKwvo25cBICI9U3W2r6lTpyIpKQkLFy4s3lanTp3idUmSkJaWhpdffhk9e/YEACxZsgRxcXH4/PPP0Y/z7RKRSuTMH72UfcklX5Ur205R6y257EuSxNTbERHKHl+Jsi/Akvmza5fIhOKMeoHt9GnRIyMkBIiPV3s0RETeCwsDjh2zf5skif5AqaliWnml38uJSPtUDf588cUX6NKlCx544AFs2rQJNWvWxFNPPYVhw4YBAA4fPozs7Gx07Nix+Heio6Nxxx13YNu2bXaDP/n5+cjPzy/+OfffOoXCwkIUFhZ6NV759709DpGv8Bz1n3r1ACAMe/ZIWLzYjJo1gbvvloL2Yur0aQOAUMTGSigsvO7VsUqfpyKIIiIpFy4UIlThd6bTp40AQhATY0ZhYZHHx6ldG4iODsXFiwbs2lWIpk0VGyKp4J9/ACAMcXESJOk6rF82+VpKgYDnKZW2aZMBx487fhOVJBEcysi4jrZtRaqt2Qxs3mzAyZNiqnglr2V4jlIgCIbz1NWxqxr8+fvvvzF//nyMHj0aL774Inbs2IERI0agXLlyeOSRR5CdnQ0AiIuLK/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 | } --------------------------------------------------------------------------------