Object Counting with Neural Networks: Detection and Density Maps
Counting objects in an image or video sounds simple: detect and count bounding boxes. But try that with a dense crowd, a field of crops, or microscopic cells. Detectors fail due to overlapping boxes, NMS removing true positives, and high latency. For such cases, we apply specialized approaches: density maps and crowd counting models. With over 30 projects in retail, transportation, and biomedicine, our counting accuracy reaches 95% even on the densest scenes.
How We Solve Dense Crowds
For tasks with hundreds or thousands of objects per frame — counting people in a crowd, grains in a field, cells under a microscope — we use density maps. This is an image where each pixel encodes the "density" of objects in its vicinity. The integral over the density map equals the total count. Our experience shows that on dense crowds, density maps achieve 30–50% lower MAE than detection. For example, on Shanghai Tech Part A (dense crowd), CSRNet reports an MAE of 68.2 versus ~110 for YOLO when directly estimating count. Density maps are not mere regression; they are robust to occlusion and scale variations. According to Li et al. (2018), the CSRNet architecture remains a benchmark for crowd counting.
Here is the CSRNet architecture we adapt to your domain:
import torch
import torch.nn as nn
from torchvision.models import vgg16
class CSRNet(nn.Module):
"""Crowd Scene Recognition Network for people counting"""
def __init__(self):
super().__init__()
vgg = vgg16(pretrained=True)
self.frontend = nn.Sequential(*list(vgg.features.children())[:23])
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
Detection + Counting for Sparse Scenes
We should note: when there are fewer than 50 objects and they aren't heavily occluded, we use YOLOv8/YOLO11. The counter is straightforward:
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
Annotation for training: dot annotations — one point per object. From these points we generate density maps using Gaussian kernels. This is cheaper than bounding boxes and more accurate for dense scenes.
Line Crossing Counting for Video
For counting vehicles or pedestrians passing through a zone, we use tracking + a virtual line:
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)
def _crosses_line(self, p1, p2):
# check if segment crosses the line
pass
Why Density Maps Beat Detection on Crowds?
A detector tries to find each object individually — overlapping bounding boxes trigger NMS, which discards valid boxes. Density maps regress density without segmenting each object, making them more robust to occlusion. On Shanghai Tech Part A (dense crowd), CSRNet achieves an MAE of 68.2 versus ~110 for YOLO when estimating count directly.
How to Prepare Data for Density Map Training: 3 Steps
- Collect data — gather at least 1000 images of your scenario (crowd, traffic, cells). Ensure coverage of all densities and lighting conditions.
- Annotate — mark each object with a single point (dot annotation). For dense crowds, use tools like LabelMe or CVAT.
- Generate density maps — blur the points with a Gaussian kernel whose sigma depends on object size. We automate this step with a script.
Case Study: Mall Visitor Counting
A shopping mall chain approached us to count people in each hall throughout the day to optimize cashier and security staffing. Their cameras streamed at 30 FPS, but due to occlusions and shadows, a YOLOv8 detector gave an MAE of ~25 per typical frame. We trained a CSRNet on dense scenes — after fine-tuning on 2000 frames with dot annotations, the MAE dropped to 8. We deployed the system on an NVIDIA T4, achieving a p99 latency of 45 ms — real-time video processing. Over one year of operation, counting accuracy never fell below 93%, and the savings on personnel amounted to 1.2 million rubles annually. In another project — counting visitors in a park — we reduced the error by 40%, saving 2.3 million rubles per year.
Applications and Metrics
| Application | Approach | Metric |
|---|---|---|
| Road traffic counting | Tracking + line | Accuracy, false count rate |
| Crowd counting | Density map (CSRNet) | MAE, RMSE |
| Microscopic cell counting | Density map | MAE |
| Fruit counting on plantation | YOLO + counting | mAP, MAE |
| Shelf inventory counting | YOLO + counting | Accuracy |
Typical CSRNet metrics on Shanghai Tech:
- Part A (dense crowds): MAE 68.2, RMSE 115.0
- Part B (sparse): MAE 10.6, RMSE 16.0
What's included in turnkey work
- Audit of the task and data: we determine which approach yields maximum accuracy for your budget.
- Model development and training: from prototype to production-ready inference with quantization (INT8) for speed.
- Integration into your infrastructure: API, video stream, database.
- Performance optimization: p99 latency < 50 ms on GPU for real-time.
- Documentation and training for your team.
- Model accuracy guarantee: MAE specified in the deliverable.
Typical Timelines
| Task | Duration |
|---|---|
| Detection-based counting (pre-trained model) | 1–2 weeks |
| Custom density map for new domain | 3–5 weeks |
| End-to-end system (video + analytics) | 4–7 weeks |
We give an exact estimate after analyzing your data. Reach out for a consultation — let's discuss your task and find the optimal solution. Contact us to start your project.







