Владельцы торговых центров и управляющие мероприятиями часто сталкиваются с проблемой точного подсчёта посетителей. Старые счётчики на входе дают погрешность до 20%, а данные о перемещении внутри помещений отсутствуют. Мы разработали систему на основе компьютерного зрения, которая решает эти задачи: мы строим систему подсчёта посетителей под ключ, используя современные методы детекции и трекинга. Наш опыт — более 5 лет в видеоаналитике, 50+ реализованных проектов.
Почему верхний обзор (top-view) оптимален?
Камера, установленная на потолке перпендикулярно полу, даёт минимальные перекрытия объектов. Люди видны как силуэты, что упрощает детекцию. Это стандартный подход для people counting: точность достигает 97–99%. Мы используем модели YOLO (Ultralytics) с трекингом ByteTrack, что позволяет отслеживать каждого посетителя.
from ultralytics import YOLO import numpy as np import cv2 class PeopleCounter: def __init__(self, model_path: str, count_line: tuple, # ((x1,y1), (x2,y2)) direction: str = 'both'): # 'in', 'out', 'both' self.model = YOLO(model_path) self.count_line = count_line self.direction = direction # ByteTrack встроен в Ultralytics self.tracker_config = 'bytetrack.yaml' self.track_history = {} self.count_in = 0 self.count_out = 0 self.counted_ids = set() def process(self, frame: np.ndarray) -> dict: # Детекция людей с трекингом results = self.model.track( frame, persist=True, conf=0.4, classes=[0], # только люди tracker=self.tracker_config ) if results[0].boxes.id is None: return self._get_counts() for box, track_id in zip(results[0].boxes.xyxy, results[0].boxes.id): tid = int(track_id) x1, y1, x2, y2 = map(int, box) cx, cy = (x1 + x2) // 2, (y1 + y2) // 2 if tid not in self.track_history: self.track_history[tid] = [] self.track_history[tid].append((cx, cy)) # Проверяем пересечение линии if len(self.track_history[tid]) >= 2 and tid not in self.counted_ids: prev_pos = self.track_history[tid][-2] curr_pos = self.track_history[tid][-1] crossing = self._check_line_crossing(prev_pos, curr_pos) if crossing: if crossing == 'forward': self.count_in += 1 else: self.count_out += 1 self.counted_ids.add(tid) return self._get_counts() def _check_line_crossing(self, prev: tuple, curr: tuple) -> str | None: """Определение факта и направления пересечения линии""" lx1, ly1 = self.count_line[0] lx2, ly2 = self.count_line[1] # Векторное произведение для определения стороны d1 = self._cross_product(prev, (lx1, ly1), (lx2, ly2)) d2 = self._cross_product(curr, (lx1, ly1), (lx2, ly2)) if d1 * d2 < 0: # пересечение return 'forward' if d1 < 0 else 'backward' return None def _cross_product(self, point, line_start, line_end): return ((line_end[0] - line_start[0]) * (point[1] - line_start[1]) - (line_end[1] - line_start[1]) * (point[0] - line_start[0])) def _get_counts(self) -> dict: return { 'count_in': self.count_in, 'count_out': self.count_out, 'current_occupancy': self.count_in - self.count_out } Тепловая карта передвижений
Для анализа зон притяжения мы строим тепловые карты. Аккумулятор накапливает позиции треков с экспоненциальным забыванием. Данные сглаживаются GaussianBlur и накладываются на видеокадр.
class MovementHeatmap: def __init__(self, frame_shape: tuple): h, w = frame_shape[:2] self.accumulator = np.zeros((h, w), dtype=np.float32) self.decay = 0.995 # забываем старые данные def update(self, track_positions: list[tuple]): self.accumulator *= self.decay for x, y in track_positions: if 0 <= x < self.accumulator.shape[1] and \ 0 <= y < self.accumulator.shape[0]: self.accumulator[y, x] += 1.0 # Gaussian blur для сглаживания self.accumulator = cv2.GaussianBlur( self.accumulator, (21, 21), 0 ) def get_heatmap(self, frame: np.ndarray) -> np.ndarray: normalized = cv2.normalize( self.accumulator, None, 0, 255, cv2.NORM_MINMAX ).astype(np.uint8) colormap = cv2.applyColorMap(normalized, cv2.COLORMAP_JET) return cv2.addWeighted(frame, 0.6, colormap, 0.4, 0) Аналитика и отчётность
Данные подсчёта идут в time-series базу InfluxDB и доступны в Grafana. Мы настраиваем дашборды с трафиком за день/неделю/месяц, пиковыми часами, конверсионной воронкой по зонам и контролем наполняемости.
Точность систем подсчёта
| Условия | Accuracy |
|---|---|
| Top-view, хорошее освещение | 97–99% |
| Боковой обзор, умеренная плотность | 93–96% |
| Плотные толпы (>30 чел/м²) | 85–91% |
| Плохое освещение | 88–93% |
| Масштаб | Срок |
|---|---|
| 1–4 входа, базовый подсчёт | 2–3 недели |
| Торговый центр, тепловые карты | 4–7 недель |
| Сеть объектов + аналитика | 7–12 недель |
Что входит в разработку
- Обследование помещения и согласование расположения камер.
- Установка и настройка оборудования.
- Разработка модели детекции и трекинга под ваши условия.
- Интеграция с InfluxDB, Grafana, вашей CRM или BI.
- Тестирование и калибровка точности.
- Обучение персонала и документация.
Многокамерная система: синхронизация и дедупликация
Для объектов с несколькими входами данные с каждой камеры суммируются, но важно исключить двойной счёт при переходе посетителя из зоны в зону. Мы используем глобальный трекер на основе Re-ID (ReID): каждый посетитель получает уникальный эмбеддинг по внешнему виду (BoT-SoRT / StrongSORT), сохраняется в Redis и проверяется при появлении в другой камере в течение заданного временного окна.
Пример конфигурации для торгового центра с 8 входами:
- Камеры: 8 × Hikvision DS-2CD2185G1 (8 МП, 30fps).
- Сервер обработки: 1 × NVIDIA A10G (24 ГБ VRAM), обрабатывает все 8 потоков с задержкой менее 150 мс.
- Redis TTL для дедупликации: 30 минут (время перехода между входами).
- Точность дедупликации: >97% при хорошем освещении.
Интеграция с BI и прогнозирование посещаемости
Данные о посещаемости ценны не только в реальном времени, но и как исторический ряд для планирования. Мы строим pipeline от счётчика до BI-дашборда:
- InfluxDB — хранение time-series данных (вход/выход по 5-минутным интервалам).
- Apache Superset или Power BI — дашборды для менеджмента: трафик по дням, часовые пики, аномалии.
- Prophet / SARIMA — прогноз посещаемости на следующую неделю с точностью MAE < 8%.
Прогнозы используются для оптимизации расписания персонала: при ожидаемом пике система рекомендует увеличить число кассиров или открыть дополнительный вход.
Соответствие нормам: GDPR и защита персональных данных
Система не хранит персональных данных: трекинг ведётся по анонимным ID, изображения посетителей не сохраняются на диск. Для дополнительной защиты предусмотрено размытие лиц в режиме реального времени (face blur). Это соответствует требованиям GDPR и российского законодательства о персональных данных. При необходимости — готовим документацию для DPO и проводим оценку воздействия на защиту данных (DPIA).
Типичные ошибки при развёртывании
- Установка камер с горизонтальным углом вместо строго вертикального: точность падает на 10–15%.
- Недостаточное освещение в ночное время: ставим IR-подсветку или используем камеры с WDR.
- Пересечение зон захвата двух камер без дедупликации: двойной счёт одного посетителя.
- Игнорирование дрейфа модели: через 3–6 месяцев при изменении условий освещения или одежды точность снижается — нужно дообучение.
Мы гарантируем точность не ниже заявленной, предоставляем поддержку после запуска. Оценим ваш проект за 2 дня — свяжитесь с нами. Получите консультацию по внедрению системы подсчёта посетителей.







