Разработка системы подсчёта объектов в кадре (Object Counting)
Подсчёт объектов на изображении или видео — задача с нюансами. Простой подход «детектируй и посчитай боксы» работает только при малом количестве объектов и хорошей видимости каждого. При плотных скоплениях (толпа, урожай на поле, клетки под микроскопом, автомобили на парковке) детекторы теряют производительность. Для таких случаев применяют специализированные подходы: density maps и crowd counting модели.
Подход 1: Детекция + подсчёт
Для разреженных объектов (< 50 в кадре, объекты не перекрываются сильно) — YOLOv8/YOLO11 + подсчёт боксов:
from ultralytics import YOLO
model = YOLO('yolov8m.pt')
def count_objects(image_path: str, target_class: str) -> int:
results = model(image_path, conf=0.4, iou=0.5)
class_names = model.names
target_id = [k for k, v in class_names.items() if v == target_class][0]
count = 0
for result in results:
for cls in result.boxes.cls:
if cls.item() == target_id:
count += 1
return count
Подход 2: Density Map для плотных скоплений
Для задач с сотнями и тысячами объектов в кадре: подсчёт людей в толпе, зёрен на поле, клеток под микроскопом.
Density map — изображение, где каждый пиксель содержит «плотность» объектов в окрестности. Интеграл по density map = количество объектов.
import torch
import torch.nn as nn
from torchvision.models import vgg16
class CSRNet(nn.Module):
"""Crowd Scene Recognition Network для подсчёта людей"""
def __init__(self):
super().__init__()
# Frontend: VGG16 без FC слоёв
vgg = vgg16(pretrained=True)
self.frontend = nn.Sequential(*list(vgg.features.children())[:23])
# Backend: dilated convolutions для multi-scale context
self.backend = nn.Sequential(
nn.Conv2d(512, 512, 3, padding=2, dilation=2),
nn.ReLU(inplace=True),
nn.Conv2d(512, 256, 3, padding=2, dilation=2),
nn.ReLU(inplace=True),
nn.Conv2d(256, 128, 3, padding=2, dilation=2),
nn.ReLU(inplace=True),
nn.Conv2d(128, 64, 3, padding=2, dilation=2),
nn.ReLU(inplace=True),
nn.Conv2d(64, 1, 1)
)
def forward(self, x):
x = self.frontend(x)
density_map = self.backend(x)
count = density_map.sum()
return density_map, count
Разметка для обучения: точечные аннотации (dot annotations) — по одной точке на каждый объект. Из точек генерируем density map через Gaussian kernel.
Подход 3: Counting через линию (Line Crossing)
Для видео-подсчёта транспорта, людей в дверях: трекинг + виртуальная линия.
class LineCrossingCounter:
def __init__(self, line_start, line_end):
self.line = (line_start, line_end)
self.counted_ids = set()
self.count = 0
self.prev_positions = {}
def update(self, track_id, center_x, center_y):
if track_id in self.prev_positions:
prev_pos = self.prev_positions[track_id]
if self._crosses_line(prev_pos, (center_x, center_y)):
if track_id not in self.counted_ids:
self.count += 1
self.counted_ids.add(track_id)
self.prev_positions[track_id] = (center_x, center_y)
Применения и метрики
| Применение | Подход | Метрика |
|---|---|---|
| Подсчёт транспорта на дороге | Трекинг + линия | Accuracy, false count rate |
| Подсчёт людей в толпе | Density map (CSRNet) | MAE, RMSE |
| Подсчёт клеток под микроскопом | Density map | MAE |
| Подсчёт фруктов на плантации | YOLO + counting | mAP, MAE |
| Инвентаризация товаров на полке | YOLO + counting | Accuracy |
Типичные метрики CSRNet на Shanghai Tech dataset:
- Part A (плотные толпы): MAE 68.2, RMSE 115.0
- Part B (разреженные): MAE 10.6, RMSE 16.0
| Задача | Срок |
|---|---|
| Подсчёт через детекцию, готовая модель | 1–2 недели |
| Density map, кастомный домен | 3–5 недель |
| Комплексная система (видео + аналитика) | 4–7 недель |







