AudionAudion

Audion Traffic Counter — руководство для ИИ-агента

AI_AGENT_GUIDE_RU.md

Audion Traffic Counter — руководство для ИИ-агента

Содержание

Этот файл — для ИИ-агента, который работает в папке программы вместе с её пользователем: Claude Code, Codex или любого другого, кто умеет запускать команды и смотреть картинки. Пользователь запускает агента в папке программы и говорит: «Прочитай Docs\AI_AGENT_GUIDE_RU.md». Всё, что делает окно программы, идёт через команды движка, описанные здесь, поэтому агент может сделать ту же работу из командной строки.

Что делает программа: читает запись дорожной камеры, находит машины в каждом кадре, ведёт каждую от кадра к кадру и считает её один раз, когда она пересекает линию, проведённую по кадру, по стрелке линии. Итог — сколько легковых, грузовых, автобусов и мотоциклов сделали каждый манёвр. Линии записи хранятся в файле камеры; у каждой записи свой.

Люди работают с программой через окно, Start.exe, и руководство пользователя (Docs\USER_GUIDE_RU.md). Пути ниже — от папки программы.

Правила, которые действуют всегда

папку программы и никуда не отправлять.

линий, лист вырезок для разметки, — уходит в сервис, на котором агент работает. До первой картинки спросите пользователя, можно ли это для его записей. Если нельзя — картинки размечает и проверяет пользователь, а агент запускает команды.

их туда нельзя.

часть из командной строки — можно; прежде чем запускать то, что нагружает видеокарту, скажите, зачем и на сколько минут.

получает новое имя. Прогоны в output\ и данные обучения — пользователя.

значит. Одна машина из двадцати — пять процентов.

Папка

ПутьЧто в нём
Start.exe, App\окно
runtime\свой Python программы с библиотеками (ставит окно)
models\веса детектора и то, что из них сделано; models\classes\ — модели классов машин, обученные здесь
system_core\kinds\модель классов машин, которая идёт с программой (модель по умолчанию)
Tools\ffmpeg\binFFmpeg
system_core\main.pyдвижок: все команды ниже
config\cameras\<id>.jsonлинии каждой записи (файлы камер)
config\recordings.jsonсписок записей окна
output\<запись>\<дата-время>_<модель>\прогон: events.csv, summary.json, camera.json; с вырезками — crops\, crops.csv; отметки — review.csv
classes\ рядом с записямиданные обучения: вырезки, листы, метки, наборы (НАСТРОЙКИ, ДАННЫЕ ОБУЧЕНИЯ могут перенести их в выбранную папку)
reference\<камера>\<запись>.jsonручные счёты для сверки
logs\журнал окна

Рабочая папка. config\cameras, config\recordings.json, output, reference и logs — собственный материал пользователя. По умолчанию они лежат в папке программы, а если в НАСТРОЙКАХ, РАБОЧАЯ ПАПКА выбрано В ПАПКЕ ПОЛЬЗОВАТЕЛЯ — в папке, которую он назвал ("WorkFolder" в config\settings.json; сами настройки всегда остаются у программы). Раскладка в обоих местах одна, и движок следует выбору сам: recordings list печатает полный путь файла линий каждой записи, count без --out пишет в output рабочей папки, report читает прогоны оттуда. Поэтому, прежде чем писать файл линий или искать прогон, прочитайте, где они лежат, из recordings list — не считайте, что в папке программы. Рабочую папку пользователя не переносить и эту настройку не менять, пока он не попросил.

Как привести папку в рабочее состояние

Программа может быть голым скелетом: окно, код, настройки — и ничего не установлено. Start.exe, раздел УСТАНОВКА, УСТАНОВИТЬ НУЖНОЕ ставит то, чего не хватает машине (Python, PyTorch под видеокарту, TensorRT на видеокарте NVIDIA RTX, веса детектора, FFmpeg), около шести минут и 6 ГБ скачивания. Кнопка сначала спрашивает и говорит, что придёт под эту машину. Первый шаг проверяет, есть ли в Windows Microsoft Visual C++ Redistributable, который нужен PyTorch, и ставит его через winget, если нет (install\Ensure-VCRedist.ps1; единственное, что ставится в Windows, с разрешения администратора). Из командной строки: builder_main.cmd install_all. Потом:

builder_main.cmd verify
runtime\python.exe system_core\main.py doctor

verify должен закончиться строкой «Everything is in place»; backend в ответе doctor — то, на чём машина считает: tensorrt (NVIDIA RTX, быстрый путь), cuda (другие видеокарты NVIDIA), directml (любая видеокарта с DirectX 12 — Intel, AMD — в несколько раз медленнее), cpu.

Команды

Все — runtime\python.exe system_core\main.py <команда>; каждая печатаемая строка — один объект JSON.

doctor                                  что установлено и на чём идёт счёт
recordings add ПАПКА_ИЛИ_ФАЙЛ [...]     добавить видео в список окна (окно закрыто)
recordings list                         все записи: id, размер, файл линий, расставлены ли, что не так
frames [--id ID ...] [--at 0.5]         кадр из середины каждой записи в ._runtime\frames\<id>.jpg с сеткой
                                        в пикселях кадра; <id>.lines.jpg — с нарисованными линиями
count --video ФАЙЛ --camera config\cameras\ID.json --out ПАПКА
      [--start С] [--end С] [--model yolo26s.pt] [--device auto]
      [--crops 3] [--classes ИМЯ] [--snapshot-every 10]
                                        счёт одной записи (или части); --crops сохраняет вырезки каждой
                                        посчитанной машины, --classes называет классы моделью
compare --summary ПРОГОН\summary.json [...] --reference reference\КАМЕРА\ИМЯ.json
                                        прогон (или несколько) против ручного счёта
classify --run ПАПКА [--classes ИМЯ]    переназвать классы посчитанного прогона без пересчёта видео
harvest --video ФАЙЛ [--out ПАПКА] [--start С] [--end С] [--grid 2x2]
                                        вырезки всех машин, которые двигались; линии не нужны
sheets --dir ПАПКА --name ИМЯ [--classes ...] [--sample N] [--lines ...]
                                        листы по 48 пронумерованных вырезок для разметки глазами
labels --dir ПАПКА [...]                буквы, записанные рядом с листами -> labels.csv
teach --name ИМЯ --train ПАПКА ... --val ПАПКА ... [--epochs 25] [--boost ПРЕФИКС N]
      [--minibus bus|car] [--light truck|car]
                                        модель классов -> models\classes\ИМЯ.pt
check-classes --classes ИМЯ --val ПАПКА ... [--car-to-truck]
                                        детектор, модель и правило «оба вместе» против меток
report --out ПАПКА [--id ID ...] [--run ПРОГОН ...] [--formats xlsx docx md] [--lang ru|en]
                                        посчитанные записи файлами для людей: документ Word и ведомость
                                        Excel; без --id и --run — последний прогон каждой записи списка

--classes auto берёт модель классов по умолчанию — ту, что названа в config\classes_default.json. Окно делает то же, когда в разделе ОБУЧЕНИЕ не выбрано иное. Её файл идёт вместе с программой (system_core\kinds\), поэтому только что установленная папка считает с ней; модель с тем же именем в models\classes — своя, пользователя, и берётся первой. Какая модель стоит по умолчанию, говорит doctor ("kinds"). Ключ --car-to-truck у count, classify и check-classes — правило классов, каким оно было до 1.5.0: чтобы повторить старые числа.

Отчёт. Когда пользователь просит результат, запишите его командой report в папку, которую он назовёт, и скажите, что в нём: сколько записей, сколько помечено «проверить» и почему. Запись помечается, когда она прочитана не до конца или когда её классы сомнительны — модель классов переназвала не меньше 20 машин и не меньше 5 % из тех, что детектор назвал легковыми (cars, renamed, doubt в блоке classes файла summary.json). Это признак камеры, на которой модель не училась: число машин у такого прогона верно, а деление на легковые, автобусы и грузовые — нет. Что с этим делать — раздел «Обучение классов под камеры пользователя»; выдавать такое деление за результат нельзя.

Расстановка линий по кадрам

Удобный способ подготовить много записей: агент расставляет линии по одному кадру каждой, пользователь проходит записи в окне (КАМЕРА, кнопки «предыдущая / следующая»), поправляет мышью и нажимает СЧИТАТЬ.

  1. Закройте окно. main.py recordings add <папка>. Каждая запись получает id (имя файла

без расширения, с номером, когда имена повторяются).

  1. main.py frames. Посмотрите каждый <id>.jpg: кадр из середины записи (начало часто

тёмное или трясётся), узкий кадр показан вдвое крупнее, на сетке — пиксели кадра: координаты читаются прямо с неё.

  1. Для каждой записи напишите config\cameras\<id>.json:
   {
     "name": "название точки съёмки, для людей",
     "frame_size": [640, 480],
     "zones": [
       { "id": "near", "rect": [0, 0, 640, 480], "scale": 1 },
       { "id": "far",  "rect": [0, 196, 640, 292], "scale": 2 }
     ],
     "lines": [
       { "id": "south", "label": "что считает", "zone": "near",
         "p1": [60, 300], "p2": [280, 300], "pos": "N1", "neg": "" },
       { "id": "gate-A", "label": "", "zone": "near", "p1": [360, 430], "p2": [600, 455],
         "pos": "A:in", "neg": "A:out", "kind": "gate", "branch": "A" }
     ]
   }

x от левого края, y от верхнего.

прямым углом к движению, там, где машины крупные и не заслоняют друг друга, — не на стоп-линии, не за столбом, не у самого края кадра (след должен устояться до линии).

p1 -> p2. Проведённая снизу вверх — считает едущих вправо; сверху вниз — влево; справа налево — вверх по кадру; слева направо — вниз. neg оставьте пустым, если встречный поток на этой линии не считается; если считается — pos у большего потока.

"check": true, иначе её машины войдут в итог дважды.

N2…

("kind": "gate", "branch": "A", pos / neg = A:in / A:out); движок сводит въезды и выезды в манёвры «A → B».

линии в этой зоне. Без нужды зон не заводить.

  1. Снова main.py frames. У каждой записи problem должен быть null, placed — true.

Посмотрите каждый <id>.lines.jpg: линия лежит поперёк своей дороги, стрелка смотрит туда, куда едут считаемые машины. Поправьте и повторите.

  1. Записи с одной точки съёмки делят расстановку: скопируйте файл под другим id и проверьте

копию на её кадре — штатив двигают.

  1. Скажите пользователю, что расставлено и что нет, с путями к картинкам .lines.jpg. Чего

по кадру не понять — куда идёт движение по дороге без единой машины, какие манёвры в его ведомости, — спросите, а не угадывайте: линия со стрелкой не туда за ночь не посчитает ничего.

Счёт и чтение прогона

Одна запись для проверки:

main.py count --video <файл> --camera config\cameras\<id>.json --out output\<id>\<дата-время>_yolo26s

Прогон пишет events.csv — по строке на каждый посчитанный проезд: frame, time, movement, class, line, zone, track (с моделью классов ещё detector_class, classifier_class, classifier_share, mark), — и summary.json: summary (манёвр -> класс -> машин), total (вся запись без контрольных линий и выездов из ворот), manoeuvres, backend, complete (false — запись прочитана не до конца). Прогон повторяется байт в байт на том же пути счёта; разные пути различаются на единицы машин.

Ручной счёт для сверки — reference\<камера>\<запись>.json:

{
  "classes": ["Легковой/Микроавтобус", "Автобус", "Малый грузовой", "Средний грузовой", "Большой грузовой", "Спец"],
  "engine_classes": { "car": [0, 5], "bus": [1], "truck": [2, 3, 4] },
  "counts": { "N1": [1073, 12, 28, 16, 20, 0] },
  "derived": { "P1": ["N2", "N3"] }
}

В counts — по числу на столбец ведомости пользователя, engine_classes складывает столбцы в классы программы, derived задаёт контрольные манёвры как суммы. main.py compare печатает по каждому манёвру и классу: вручную, программа, разница, проценты.

Ручной счёт — точка отсчёта, а не истина. Люди точно считают хорошо видные машины, а остальное прикидывают. Где прогон и ручной счёт расходятся по классу, разложите машины этой линии на листы (счёт с --crops 3, потом sheets --dir <прогон> --lines <id линии> --classes bus truck и выборка легковых), разметьте и посчитайте по картинкам — получится третий счёт, который может проверить кто угодно. На записи, на которой делалась программа, такой счёт сошёлся с ручным до одной-двух машин на ближних линиях — когда маршрутки посчитали автобусами, как их считали люди.

Обучение классов под камеры пользователя

Детектор (YOLO, обученный на фотографиях со всего мира) хорошо называет легковые и плохо — остальное: маршрутка у него то автобус, то грузовая, длинный грузовик — автобус. Модель классов — MobileNetV3-small, 6 МБ — смотрит на три вырезки каждой посчитанной машины и называет её класс. С детектором её соединяет правило «оба вместе»: что детектор назвал легковой, остаётся легковой, пока модель не уверена на 90 %, что это автобус, — а грузовой легковая детектора не становится никогда; то, что детектор назвал грузовой или автобусом, называет модель; мотоцикл остаётся за детектором. Так модель исправляет автобусы и грузовые и не портит легковые. (Почему грузовой — никогда: с высокой камеры тёмные легковые кажутся модели грузовиками, и уверена она в неверных так же, как в верных; легковых девять машин из десяти, и нескольких неверных переназваний хватает, чтобы испортить счёт грузовых.)

Модель идёт вместе с программой и включена с самого начала. Она обучена примерно на 4 300 размеченных машинах городских камер — со столба, с моста, с обочины, с крыши, летом и зимой. Свою модель учат, когда модель по умолчанию плохо называет машины на камерах пользователя: сначала проверьте её (шаг 5 ниже) на нескольких сотнях его размеченных машин.

Замер на записи, на которой делалась программа, на одной линии: у детектора автобусы −53 %, грузовые +39 %; с моделью, обученной на первой половине той же камеры, на второй половине — автобусы +4 %, грузовые +6 %, легковые 0 %.

Решают две вещи, а не число эпох и не модель крупнее:

сбоку и сверху, называла грузовики камеры, которая видит их спереди, легковыми (−56 %). Около двухсот машин этой камеры это исправили. Учите на камерах пользователя и проверяйте на их машинах, которых модель не видела;

относят маршрутку (окна по всему борту) к автобусам; размеченная легковой, она дала автобусов −75 %. Прежде чем размечать, спросите пользователя, как его ведомости считают маршрутки и малые грузовые.

Время: дольше всего нарезка (20 минут записи 1080p с четырьмя камерами — 80 минут на графике Intel, минуты на RTX); обучение 25 эпох на 3500 вырезках — полчаса на процессоре ноутбука и несколько минут на видеокарте NVIDIA. Первое обучение скачивает веса ImageNet (10 МБ).

Порядок

  1. Вырезки. Нарежьте все машины части записи — линии не нужны:
   main.py harvest --video <файл> --start 0 --end 1200

(в classes\cut\<имя записи>\ рядом с записью; запись экрана с несколькими камерами — --grid 2x2, каждая камера в свою папку 11 12 21 22). Или посчитайте запись с --crops 3: в папке прогона будут вырезки посчитанных машин. Возьмите две разнесённые по времени части — две записи, два дня, две половины: на одной учить, на другой проверять.

  1. Листы. Для каждой папки вырезок — сначала всё, что детектор назвал не легковой, потом

выборку легковых:

   main.py sheets --dir <папка> --name big --classes bus truck motorcycle
   main.py sheets --dir <папка> --name car --classes car --sample 48

Лист — <папка>\sheets\<имя>_<nn>.jpg: до 48 вырезок рядами по 8, ячейки пронумерованы с

  1. Автобусы и грузовые — примерно каждая десятая машина; почти все они в первой пачке.
  1. Метки. Посмотрите лист и запишите рядом <имя>_<nn>.txt: по букве на ячейку, в

порядке ячеек, по восемь букв в строке (пробелы и переводы строк ничего не значат; букв должно быть столько же, сколько ячеек).

БукваМеткаЧто это
ccarлегковая, внедорожник, пикап, минивэн, скорая, полиция
vminibusмаршрутка: окна по всей верхней половине борта
llightмалый грузовой: фургон без окон по борту, бортовая, будка
ttruckгрузовик крупнее: грузовой, самосвал, тягач с прицепом
gcargoточно грузовой, малый или большой — не разобрать
bbusавтобус
otrolleyтроллейбус
r, mtram, motoтрамвай, мотоцикл
xjunkне разобрать, две машины в одной рамке, не машина

Сомневаетесь — x: неверная метка учит хуже, чем отсутствующая. Потом main.py labels --dir <папка> собирает буквы в <папка>\labels.csv и говорит, сколько каких меток и сколько ячеек ещё ждут букв. Прежде чем идти дальше, покажите пользователю лист-другой со своими буквами. Отметки, которые пользователь ставит в разделе ОБУЧЕНИЕ окна (review.csv прогона), — тоже метки.

  1. Обучение.
   main.py teach --name <камеры>-01 --train <папки для обучения> --val <папки для проверки>

Маршрутка идёт к автобусам, малый грузовой — к грузовым; --minibus car и --light car переносят их — спросите пользователя. Когда своих вырезок пользователя в --train мало рядом с другими, берите их чаще: --boost <начало имени их папки> 4 (вырезки набора называются по папке, из которой они нарезаны; ключ, который не называет ни одной папки из --train, отклоняется). Лучшая эпоха выбирается по папкам --val; набор и копия модели хранятся в classes\taught\<имя>\, модель — models\classes\<имя>.pt.

  1. Проверка на размеченных машинах, на которых модель не училась:
   main.py check-classes --classes <имя> --val <папки>

Три таблицы — детектор, модель одна, правило «оба вместе» — и сколько машин каждого класса дал бы счёт этих папок против того, сколько их на самом деле. Важно последнее, столбец «both». В пределах 10 % по каждому классу — хороший результат; скажите, на скольких машинах стоит каждый процент.

  1. Работа. В окне: ОБУЧЕНИЕ, МОДЕЛЬ КЛАССОВ, имя модели — следующий счёт называет классы

ею. Прогон, посчитанный раньше с --crops, переназывается без пересчёта видео: main.py classify --run <папка прогона> --classes <имя>. Спорные машины прогона (детектор и модель разошлись или модель не уверена) — в разделе ОБУЧЕНИЕ, пользователь отмечает их кнопкой.

  1. Лучше. Посмотрите, на чём модель ещё ошибается в проверочных папках, разметьте больше

таких машин с камер пользователя, обучите новую модель под новым именем и проверьте на тех же папках. Остановитесь, когда проверка в пределах цели: больше эпох не помогает, больше вырезок нужной камеры — помогает.

Если что-то не так

ПризнакЧто делать
camera lines are drawn for (640, 480), the video is (...)у записи другой размер: ей нужен свой файл камеры
cannot open videoневерный путь или файл, который не читает OpenCV: проверьте Tools\ffmpeg\bin\ffprobe.exe
дальняя линия почти ничего не считаетмашины там слишком мелкие: зона со scale 2 или модель крупнее (--model yolo26m.pt)
на разных машинах числа отличаются на несколько машинразные пути счёта; сравнивайте прогоны одного пути
DirectML is not installedУСТАНОВКА, кнопка DIRECTML, или builder_main.cmd directml
teach отказывает: no labelled bus, truckразметьте их больше: sheets --classes bus truck на других вырезках
labels отказывает: letters for cellsчисло букв в .txt не равно числу ячеек его листа
модель портит легковыеона учена на чужих камерах: разметьте около двухсот машин этой и обучите заново
компьютер засыпает во время долгой нарезкиharvest и teach сами не дают машине уснуть; закрытая крышка усыпит всё равно
Все проекты