共计 4307 个字符,预计需要花费 11 分钟才能阅读完成。
CARPK 数据集技术解析与实战指南
背景痛点:高空俯视车辆检测的挑战
高空俯视视角下的车辆检测是智慧交通、停车场管理等场景的核心需求,但存在三大技术难点:

- 小目标密集:车辆在航拍图像中仅占 50×50 像素左右(约占图像的 0.1%),YOLOv5 等检测器的默认 anchor 尺寸难以匹配
- 透视畸变严重:镜头边缘的车辆会发生几何形变,导致标注框与实际轮廓偏移
- 遮挡频繁:停车场场景中车辆间距小,平均每图有 30+ 辆车的重叠遮挡
CARPK 数据集的价值在于:
- 包含 1,500 张 4K 分辨率航拍图像(是 UAVDT 数据集的 3 倍)
- 精确标注了 89,777 辆车的旋转边界框(Rotated BBox)
- 覆盖昼夜、晴雨等多种光照条件
数据特性对比分析
| 特性 | CARPK | VisDrone | UA-DETRAC |
|---|---|---|---|
| 视角 | 俯视 90° | 俯视 45° | 平视 |
| 标注格式 | PASCAL VOC | COCO | XML |
| 车辆尺寸 | 8-120 像素 | 20-300 像素 | 50-500 像素 |
| 遮挡样本占比 | 68% | 42% | 15% |
标注文件结构解析
CARPK 采用 PASCAL VOC 标准的 XML 标注,关键字段如下:
<annotation>
<size>
<width>4000</width>
<height>3000</height>
</size>
<object>
<name>car</name>
<bndbox>
<xmin>1254</xmin>
<ymin>876</ymin>
<xmax>1312</xmax>
<ymax>932</ymax>
</bndbox>
<difficult>0</difficult>
</object>
<!-- 更多 object 节点 -->
</annotation>
PyTorch 实战代码
1. 数据加载器实现
from lxml import etree
from typing import List, Dict, Tuple
import cv2
import albumentations as A
class CARPKDataset(torch.utils.data.Dataset):
"""CARPK 数据集加载器,支持 Mosaic 增强"""
def __init__(self,
img_dir: str,
annot_dir: str,
transforms=None,
use_mosaic: bool = False):
self.img_dir = img_dir
self.annot_dir = annot_dir
self.transforms = transforms
self.use_mosaic = use_mosaic
self.img_ids = [f.stem for f in Path(img_dir).glob('*.jpg')]
# 小目标专用增强
self.small_obj_aug = A.Compose([A.RandomResizedCrop(1024, 1024, scale=(0.3, 1.0)),
A.HueSaturationValue(hue_shift_limit=20),
A.RandomBrightnessContrast(p=0.5),
], bbox_params=A.BboxParams(format='pascal_voc'))
def __getitem__(self, idx):
if self.use_mosaic and idx > 3: # 跳过前 4 张用于 Mosaic
return self._load_mosaic(idx)
img, target = self._load_single(idx)
if self.transforms:
transformed = self.transforms(
image=img,
bboxes=target['boxes'],
labels=target['labels'])
img = transformed['image']
target['boxes'] = torch.as_tensor(transformed['bboxes'])
return img, target
def _load_single(self, idx) -> Tuple[np.ndarray, Dict]:
"""加载单张图像及其标注"""
img_id = self.img_ids[idx]
img_path = f"{self.img_dir}/{img_id}.jpg"
annot_path = f"{self.annot_dir}/{img_id}.xml"
# 解析 XML
tree = etree.parse(annot_path)
size = tree.find('size')
boxes, labels = [], []
for obj in tree.xpath('//object'):
bndbox = obj.find('bndbox')
boxes.append([float(bndbox.find('xmin').text),
float(bndbox.find('ymin').text),
float(bndbox.find('xmax').text),
float(bndbox.find('ymax').text)
])
labels.append(1) # CARPK 只有 car 类别
img = cv2.imread(img_path)
target = {
'boxes': boxes,
'labels': torch.as_tensor(labels, dtype=torch.int64),
'image_id': torch.tensor([idx])
}
# 小目标增强
if len(boxes) > 30: # 只在密集场景应用
augmented = self.small_obj_aug(image=img, bboxes=boxes, labels=labels)
img = augmented['image']
target['boxes'] = augmented['bboxes']
return img, target
2. Mosaic 增强实现
def _load_mosaic(self, idx):
"""加载 4 张图像拼接成 Mosaic"""
indices = [idx] + [random.randint(0, len(self)-1) for _ in range(3)]
imgs, targets = [], []
# 加载四角图像
for i, index in enumerate(indices):
img, target = self._load_single(index)
imgs.append(img)
targets.append(target)
# 拼接逻辑
mosaic_img = np.zeros((2048, 2048, 3), dtype=np.uint8)
mosaic_boxes = []
# 左上角
x1a, y1a, x2a, y2a = 0, 0, 1024, 1024
mosaic_img[y1a:y2a, x1a:x2a] = cv2.resize(imgs[0], (1024, 1024))
for box in targets[0]['boxes']:
mosaic_boxes.append([box[0]/4 + x1a, box[1]/4 + y1a,
box[2]/4 + x1a, box[3]/4 + y1a
])
# 其他三个区域类似处理...
# 随机裁剪最终输出
crop = A.RandomCrop(1024, 1024)(image=mosaic_img, bboxes=mosaic_boxes)
return crop['image'], {'boxes': crop['bboxes'], 'labels': torch.cat([t['labels'] for t in targets])}
避坑指南
1. 畸变矫正问题
现象:模型在图像边缘的检测 AP 显著低于中心区域
解决方案:
- 在数据加载时添加镜头畸变校正
- 使用 OpenCV 的
cv2.undistort函数:camera_matrix = np.array([[fx, 0, cx], [0, fy, cy], [0, 0, 1]]) dist_coeffs = np.array([k1, k2, p1, p2, k3]) img = cv2.undistort(img, camera_matrix, dist_coeffs)
2. 空标注处理
现象:部分图像因天气原因无车辆,直接加载会报错
解决方案:
# 在__getitem__中添加检查
if len(target['boxes']) == 0:
# 返回虚拟标注
target['boxes'] = torch.zeros((0, 4), dtype=torch.float32)
target['labels'] = torch.zeros((0,), dtype=torch.int64)
3. 内存泄漏问题
现象:训练时内存持续增长直至 OOM
排查方法:
- 检查数据加载器是否在
__getitem__中意外保留引用 - 使用
tracemalloc定位泄漏点:import tracemalloc tracemalloc.start() # ... 运行训练代码... snapshot = tracemalloc.take_snapshot() for stat in snapshot.statistics('lineno')[:10]: print(stat)
性能优化建议
8GB 显存配置下的训练技巧:
- Batch Size 选择:
- YOLOv5s 模型:batch_size=8
-
Faster R-CNN:batch_size=2 + 梯度累积 4 次
-
混合精度训练:
scaler = torch.cuda.amp.GradScaler() with torch.cuda.amp.autocast(): outputs = model(inputs) loss = criterion(outputs, targets) scaler.scale(loss).backward() scaler.step(optimizer) scaler.update() -
数据加载优化:
- 使用
torchvision.ops.drop_duplicate_boxes过滤重复标注 - 启用
pin_memory=True和num_workers=4
延伸思考
- 标注质量评估:当前 mAP 指标是否足以反映小目标检测质量?是否需要引入:
- 像素级 IoU(对 50px 以下目标更敏感)
-
边缘重合度指标
-
跨摄像头适应:当部署到新停车场时,如何解决:
- 摄像头参数差异导致的域偏移
- 不同光照条件下的颜色分布变化
结语
CARPK 数据集为高空车辆检测研究提供了宝贵的基准数据,但其小目标、高密度的特性仍需特殊处理。通过本文介绍的数据加载、增强和优化技巧,开发者可以快速构建 baseline 模型。建议在实际项目中结合具体业务场景(如停车场车位统计、交通流量监控等)进一步优化模型架构。
正文完
