Accurate Visitor Counting with Video Analytics – Retail & Events

Retail center owners and event managers often face the challenge of accurately counting visitors. Old entrance counters have up to 20% error, and indoor movement data is missing. We developed a computer vision system that solves these tasks: we build a turnkey people counting system using modern det

AI Development Areas

Frequently Asked Questions

Latest works

  • image_web-applications_feedme_466_0.webp
    Development of a web application for FEEDME
    1285
  • image_ecommerce_furnoro_435_0.webp
    Development of an online store for the company FURNORO
    1241
  • image_logo-advance_0.webp
    B2B Advance company logo design
    696
  • image_crm_enviok_479_0.webp
    Development of a web application for Enviok
    982
  • image_logo-aider_0.webp
    AIDER company logo development
    919
  • image_crm_chasseurs_493_0.webp
    CRM development for Chasseurs
    1033

Retail center owners and event managers often face the challenge of accurately counting visitors. Old entrance counters have up to 20% error, and indoor movement data is missing. We developed a computer vision system that solves these tasks: we build a turnkey people counting system using modern detection and tracking methods. Our system provides footfall analytics, visitor counting system, YOLO counting, movement heatmaps, automatic counting, person detection, object tracking, and real-time occupancy monitoring. This video analytics retail solution ensures accurate data. Our experience: over 5 years in video analytics, 50+ projects completed.

Why top-view is optimal?

A camera mounted on the ceiling perpendicular to the floor gives minimal object occlusion. People appear as silhouettes, simplifying detection. This is the standard approach for people counting: accuracy reaches 97-99%. We use YOLO models (Ultralytics) with ByteTrack tracking, allowing us to track each visitor.

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 } 

Movement heatmap

For zone attraction analysis, we build heatmaps. The accumulator stores track positions with exponential decay. Data is smoothed with GaussianBlur and overlaid on the video frame.

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) 

Analytics and reporting

Counting data flows into the time-series database InfluxDB and is accessible in Grafana. We configure dashboards with daily/weekly/monthly traffic, peak hours, zone conversion funnels, and occupancy control.

System accuracy

Conditions Accuracy
Top-view, good lighting 97-99%
Side view, moderate density 93-96%
Dense crowds (>30 people/m²) 85-91%
Poor lighting 88-93%
Scale Timeline
1-4 entrances, basic counting 2-3 weeks
Shopping center, heatmaps 4-7 weeks
Network of facilities + analytics 7-12 weeks

What's included in development

  • Site survey and camera placement coordination.
  • Hardware installation and setup.
  • Detection and tracking model development tailored to your conditions.
  • Integration with InfluxDB, Grafana, your CRM, or BI.
  • Testing and accuracy calibration.
  • Staff training and documentation.

Multi-camera system: synchronization and deduplication

For facilities with multiple entrances, data from each camera is summed, but double counting must be avoided when a visitor moves from one zone to another. We use a global tracker based on Re-ID (ReID): each visitor gets a unique embedding from appearance (BoT-SoRT / StrongSORT), stored in Redis and checked when appearing in another camera within a given time window.

Example configuration for a shopping center with 8 entrances:

  • Cameras: 8 × Hikvision DS-2CD2185G1 (8 MP, 30fps).
  • Processing server: 1 × NVIDIA A10G (24 GB VRAM), processes all 8 streams with latency under 150 ms.
  • Redis TTL for deduplication: 30 minutes (transit time between entrances).
  • Deduplication accuracy: >97% under good lighting.

BI integration and visitor forecasting

Visitor data is valuable not only in real time but also as a historical series for planning. We build a pipeline from counter to BI dashboard:

  1. InfluxDB — stores time-series data (entry/exit in 5-minute intervals).
  2. Apache Superset or Power BI — management dashboards: daily traffic, hourly peaks, anomalies.
  3. Prophet / SARIMA — next-week visitor forecast with MAE < 8%.

Forecasts are used to optimize staff scheduling: at an expected peak, the system recommends increasing cashiers or opening an additional entrance.

Compliance: GDPR and personal data protection

The system does not store personal data: tracking is done via anonymous IDs, visitor images are not saved to disk. For added security, real-time face blur is included. This meets GDPR and Russian personal data law requirements. If needed, we prepare documentation for the DPO and conduct a Data Protection Impact Assessment (DPIA).

Typical deployment mistakes

  • Placing cameras with a horizontal angle instead of strictly vertical: accuracy drops by 10-15%.
  • Insufficient nighttime lighting: we add IR illumination or use cameras with WDR.
  • Overlapping coverage zones of two cameras without deduplication: double counting of a single visitor.
  • Ignoring model drift: after 3-6 months, changes in lighting or clothing reduce accuracy — retraining is needed.

We guarantee accuracy not lower than stated and provide post-launch support. We'll evaluate your project within 2 days — contact us. Get a consultation on implementing a people counting system.

Sources: Ultralytics YOLO documentation, ByteTrack paper, InfluxDB official docs

Pricing: Our standard people counting system starts from $2,500 for a single entrance with basic analytics. The shopping center solution with heatmaps and forecasting costs $12,000+. We typically save clients 15-20% on staffing costs within the first quarter.

Comparison: YOLO-based counting is 3x more accurate than infrared beam counters, and 2x faster to deploy than thermal cameras. For a 10-entrance mall, our system is 40% cheaper than leading commercial alternatives while offering higher accuracy.

Steps to implement your people counting system:

  1. Consultation and site survey (1 day).
  2. Camera selection and hardware procurement (3-5 days).
  3. Model training on your location's video samples (1 week).
  4. On-site installation and network setup (2-3 days).
  5. System calibration and accuracy validation (1-2 days).
  6. Dashboard configuration and staff training (1 day).
  7. Go-live and performance monitoring (ongoing).
Detailed forecast model parametersWe use Prophet with weekly and daily seasonality, holidays list, and changepoint detection. Retraining occurs every night. MAE on validation data is typically under 8% for next-day forecasts.
Hardware cost breakdownA typical server with NVIDIA A10G GPU costs around $8,000. Each IP camera adds $300-500. Overall hardware investment is recovered within 6 months through staff optimization.