Audion Traffic Counter — руководство для ИИ-агента
Audion Traffic Counter — руководство для ИИ-агента
Содержание
- Правила, которые действуют всегда
- Папка
- Как привести папку в рабочее состояние
- Команды
- Расстановка линий по кадрам
- Счёт и чтение прогона
- Обучение классов под камеры пользователя
- Если что-то не так
Этот файл — для ИИ-агента, который работает в папке программы вместе с её пользователем: Claude Code, Codex или любого другого, кто умеет запускать команды и смотреть картинки. Пользователь запускает агента в папке программы и говорит: «Прочитай Docs\AI_AGENT_GUIDE_RU.md». Всё, что делает окно программы, идёт через команды движка, описанные здесь, поэтому агент может сделать ту же работу из командной строки.
Что делает программа: читает запись дорожной камеры, находит машины в каждом кадре, ведёт каждую от кадра к кадру и считает её один раз, когда она пересекает линию, проведённую по кадру, по стрелке линии. Итог — сколько легковых, грузовых, автобусов и мотоциклов сделали каждый манёвр. Линии записи хранятся в файле камеры; у каждой записи свой.
Люди работают с программой через окно, Start.exe, и руководство пользователя (Docs\USER_GUIDE_RU.md). Пути ниже — от папки программы.
Правила, которые действуют всегда
- Записи — пользователя. Они лежат там, где их держит пользователь: не копировать их в
папку программы и никуда не отправлять.
- Спросить, прежде чем смотреть. Всё, что рассматривает агент — кадр для расстановки
линий, лист вырезок для разметки, — уходит в сервис, на котором агент работает. До первой картинки спросите пользователя, можно ли это для его записей. Если нельзя — картинки размечает и проверяет пользователь, а агент запускает команды.
- Движок — только
runtime\python.exe. В системном Python нужных пакетов нет, и ставить
их туда нельзя.
- Не запускать счёт очереди записей. Это кнопка СЧИТАТЬ пользователя. Одну запись или её
часть из командной строки — можно; прежде чем запускать то, что нагружает видеокарту, скажите, зачем и на сколько минут.
- Ничего не писать поверх. Модель, набор, пачка листов сохраняют свои имена; новое
получает новое имя. Прогоны в output\ и данные обучения — пользователя.
- Закрыть окно перед
recordings add: окно тоже держит список. - Число — вместе с тем, на чём оно стоит: «автобусов −6 %» без «из 51 автобуса» ничего не
значит. Одна машина из двадцати — пять процентов.
Папка
| Путь | Что в нём |
|---|---|
Start.exe, App\ | окно |
runtime\ | свой Python программы с библиотеками (ставит окно) |
models\ | веса детектора и то, что из них сделано; models\classes\ — модели классов машин, обученные здесь |
system_core\kinds\ | модель классов машин, которая идёт с программой (модель по умолчанию) |
Tools\ffmpeg\bin | FFmpeg |
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). Это признак камеры, на которой модель не училась: число машин у такого прогона верно, а деление на легковые, автобусы и грузовые — нет. Что с этим делать — раздел «Обучение классов под камеры пользователя»; выдавать такое деление за результат нельзя.
Расстановка линий по кадрам
Удобный способ подготовить много записей: агент расставляет линии по одному кадру каждой, пользователь проходит записи в окне (КАМЕРА, кнопки «предыдущая / следующая»), поправляет мышью и нажимает СЧИТАТЬ.
- Закройте окно.
main.py recordings add <папка>. Каждая запись получаетid(имя файла
без расширения, с номером, когда имена повторяются).
main.py frames. Посмотрите каждый<id>.jpg: кадр из середины записи (начало часто
тёмное или трясётся), узкий кадр показан вдвое крупнее, на сетке — пиксели кадра: координаты читаются прямо с неё.
- Для каждой записи напишите
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" }
]
}
frame_size— размер, который напечаталframes; координаты — пиксели исходного кадра,
x от левого края, y от верхнего.
- По линии на каждый поток, который считает обследование: поперёк проезжей части под
прямым углом к движению, там, где машины крупные и не заслоняют друг друга, — не на стоп-линии, не за столбом, не у самого края кадра (след должен устояться до линии).
- Направление: под именем
posлиния считает машины, которые оказываются слева от
p1 -> p2. Проведённая снизу вверх — считает едущих вправо; сверху вниз — влево; справа налево — вверх по кадру; слева направо — вниз. neg оставьте пустым, если встречный поток на этой линии не считается; если считается — pos у большего потока.
- Линия перед развилкой, у ветвей которой есть свои линии, — контрольная:
"check": true, иначе её машины войдут в итог дважды.
- Имена — названия манёвров из ведомости пользователя, если они известны; иначе
N1,
N2…
- Перекрёсток или кольцо: ворота поперёк каждой ветки, через въезд и выезд разом
("kind": "gate", "branch": "A", pos / neg = A:in / A:out); движок сводит въезды и выезды в манёвры «A → B».
- Дальняя часть, где машины в несколько пикселей: зона вокруг неё со
scale2 и дальние
линии в этой зоне. Без нужды зон не заводить.
- Снова
main.py frames. У каждой записиproblemдолжен бытьnull,placed— true.
Посмотрите каждый <id>.lines.jpg: линия лежит поперёк своей дороги, стрелка смотрит туда, куда едут считаемые машины. Поправьте и повторите.
- Записи с одной точки съёмки делят расстановку: скопируйте файл под другим id и проверьте
копию на её кадре — штатив двигают.
- Скажите пользователю, что расставлено и что нет, с путями к картинкам
.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 %.
Решают две вещи, а не число эпох и не модель крупнее:
- вырезки с камер того же вида. Модель, обученная на 1400 машинах городских камер, снятых
сбоку и сверху, называла грузовики камеры, которая видит их спереди, легковыми (−56 %). Около двухсот машин этой камеры это исправили. Учите на камерах пользователя и проверяйте на их машинах, которых модель не видела;
- граница классов, как её проводит ведомость пользователя. Люди, которые считают вручную,
относят маршрутку (окна по всему борту) к автобусам; размеченная легковой, она дала автобусов −75 %. Прежде чем размечать, спросите пользователя, как его ведомости считают маршрутки и малые грузовые.
Время: дольше всего нарезка (20 минут записи 1080p с четырьмя камерами — 80 минут на графике Intel, минуты на RTX); обучение 25 эпох на 3500 вырезках — полчаса на процессоре ноутбука и несколько минут на видеокарте NVIDIA. Первое обучение скачивает веса ImageNet (10 МБ).
Порядок
- Вырезки. Нарежьте все машины части записи — линии не нужны:
main.py harvest --video <файл> --start 0 --end 1200
(в classes\cut\<имя записи>\ рядом с записью; запись экрана с несколькими камерами — --grid 2x2, каждая камера в свою папку 11 12 21 22). Или посчитайте запись с --crops 3: в папке прогона будут вырезки посчитанных машин. Возьмите две разнесённые по времени части — две записи, два дня, две половины: на одной учить, на другой проверять.
- Листы. Для каждой папки вырезок — сначала всё, что детектор назвал не легковой, потом
выборку легковых:
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, ячейки пронумерованы с
- Автобусы и грузовые — примерно каждая десятая машина; почти все они в первой пачке.
- Метки. Посмотрите лист и запишите рядом
<имя>_<nn>.txt: по букве на ячейку, в
порядке ячеек, по восемь букв в строке (пробелы и переводы строк ничего не значат; букв должно быть столько же, сколько ячеек).
| Буква | Метка | Что это |
|---|---|---|
c | car | легковая, внедорожник, пикап, минивэн, скорая, полиция |
v | minibus | маршрутка: окна по всей верхней половине борта |
l | light | малый грузовой: фургон без окон по борту, бортовая, будка |
t | truck | грузовик крупнее: грузовой, самосвал, тягач с прицепом |
g | cargo | точно грузовой, малый или большой — не разобрать |
b | bus | автобус |
o | trolley | троллейбус |
r, m | tram, moto | трамвай, мотоцикл |
x | junk | не разобрать, две машины в одной рамке, не машина |
Сомневаетесь — x: неверная метка учит хуже, чем отсутствующая. Потом main.py labels --dir <папка> собирает буквы в <папка>\labels.csv и говорит, сколько каких меток и сколько ячеек ещё ждут букв. Прежде чем идти дальше, покажите пользователю лист-другой со своими буквами. Отметки, которые пользователь ставит в разделе ОБУЧЕНИЕ окна (review.csv прогона), — тоже метки.
- Обучение.
main.py teach --name <камеры>-01 --train <папки для обучения> --val <папки для проверки>
Маршрутка идёт к автобусам, малый грузовой — к грузовым; --minibus car и --light car переносят их — спросите пользователя. Когда своих вырезок пользователя в --train мало рядом с другими, берите их чаще: --boost <начало имени их папки> 4 (вырезки набора называются по папке, из которой они нарезаны; ключ, который не называет ни одной папки из --train, отклоняется). Лучшая эпоха выбирается по папкам --val; набор и копия модели хранятся в classes\taught\<имя>\, модель — models\classes\<имя>.pt.
- Проверка на размеченных машинах, на которых модель не училась:
main.py check-classes --classes <имя> --val <папки>
Три таблицы — детектор, модель одна, правило «оба вместе» — и сколько машин каждого класса дал бы счёт этих папок против того, сколько их на самом деле. Важно последнее, столбец «both». В пределах 10 % по каждому классу — хороший результат; скажите, на скольких машинах стоит каждый процент.
- Работа. В окне: ОБУЧЕНИЕ, МОДЕЛЬ КЛАССОВ, имя модели — следующий счёт называет классы
ею. Прогон, посчитанный раньше с --crops, переназывается без пересчёта видео: main.py classify --run <папка прогона> --classes <имя>. Спорные машины прогона (детектор и модель разошлись или модель не уверена) — в разделе ОБУЧЕНИЕ, пользователь отмечает их кнопкой.
- Лучше. Посмотрите, на чём модель ещё ошибается в проверочных папках, разметьте больше
таких машин с камер пользователя, обучите новую модель под новым именем и проверьте на тех же папках. Остановитесь, когда проверка в пределах цели: больше эпох не помогает, больше вырезок нужной камеры — помогает.
Если что-то не так
| Признак | Что делать |
|---|---|
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 сами не дают машине уснуть; закрытая крышка усыпит всё равно |