一. 课题简述
客流检测:在部分关键通道场景如地铁闸机,使用3D视觉可实现流量统计、尾随报警、异常行为报警等功能。本课题需要使用海康机器人3D相机产品,通过对深度和RGB图像数据的分析,实现一个区域内的客流检测相关功能。
二. 开发环境与目标
本课题项目是在windows系统下进行开发,使用 RGB-D感知相机MV-EB435i,结合海康SDK对读取到的图像帧进行解析,并将其转换为对深度和RGB图像数据的分析,从而通过人脸的信息检测和识别操作实现流量统计,以及对部分异常行为进行报警的功能。
使用到的开发环境主要有:
- opencv 4.3.0
- visual studio 2019
- RGB-D感知相机MV-EB435i
- SDK_Mv3dRgbd_V1.0.0_Win
三. 图像数据预处理
数据预处理:获取数据并解析
- 存储RGB_D相机每一帧的图像数据,即数据帧结构为: MV3D_RGBD_FRAME_DATA,其具体结构如下:
typedef struct _MV3D_RGBD_FRAME_DATA_
{
uint32_t nImageCount;
MV3D_RGBD_IMAGE_DATA tImageData[MV3D_RGBD_MAX_IMAGE_COUNT];
uint8_t nReserved[16];
} MV3D_RGBD_FRAME_DATA;
- 其中在ImageData数组中,储存了每帧图像数据所包含的nImageCount个图像,
这些图像都是_MV3D_RGBD_IMAGE_DATA_结构体类型,但也根据Mv3dRgbdImageType分为多种类型,该结构体如下所示:
typedef struct _MV3D_RGBD_IMAGE_DATA_
{
Mv3dRgbdImageType enImageType;
uint32_t nWidth;
uint32_t nHeight;
uint8_t* pData;
uint32_t nDataLen;
uint32_t nFrameNum;
int64_t nTimeStamp;
uint8_t nReserved[16];
} MV3D_RGBD_IMAGE_DATA;
- 主要包含图像格式、图像参数(图像宽、高)、图像数据(pData)、图像数据的长度等。其中,常见图像格式(Mv3dRgbdImageType)存储的图像数据(pData)如下:
ImageType_YUV422
ImageType_RGB8_Planar
ImageType_PointCloud
ImageType_Depth
根据上述可知,RGB_D相机直接输出数据格式ImageType_Depth(深度图)和ImageType_YUV422(YUV格式图像)比较常用,而在课题中,需要提取RGB图像,标记即上述代码中 ImageType_RGB8_Planar 类型,这就需要我们对采集的帧数据进行格式分析转换,输出成我们需要的图像格式。
在SDK中 parseframe(MV3D_RGBD_FRAME_DATA* , RIFrameInfo* ,RIFrameInfo* ) 的静态函数中可将帧数据分别转换成 RIFrameInfo类型的深度图格式和RGB格式,获取RGB格式的图像数据。
parseFrame(&stFrameData, &depth, &rgb);
Mat rgb_frame(rgb.nHeight, rgb.nWidth, CV_8UC3, rgb.pData);
用 cv::cvtColor 的函数中 COLOR_BGR2RGB 的图像转换类型进行转换。
Mat frame, gray_frame;
cvtColor(rgb_frame, frame, COLOR_BGR2RGB);
cvtColor(rgb_frame, gray_frame, COLOR_BGR2GRAY);
四. 算法设计与实现
1. 人脸框选与计数算法
方案设计:
- 调用opencv训练好的Haar 级联分类器和自带的检测函数检测人脸(也可以自己训练)。
由于它检测速度非常快,满足实时运行的要求,可以轻松地在嵌入式、资源受限的设备上运行;但容易出现误报检测,通常需要手动调整 detectMultiScale 函数。
- 调用detectMultiScale()函数检测,调整函数的参数可以使检测结果更加精确。
detectMultiScale函数可以检测出图片中所有的人脸,并将人脸用vector保存各个人脸的坐标、大小(用矩形表示)。
代码实现:
faceCascade.load("haarcascade_frontalface_default.xml");
if (faceCascade.empty())
{
LOGD("xml not found!")
}
vector<Rect> faces;
faceCascade.detectMultiScale(gray_frame, faces, 1.1, 7, 0, Size(24, 24));
//框选人脸
rectangle(frame, faces[i].tl(), faces[i].br(), Scalar(255, 255, 255), 4);
char text[10];
sprintf_s(text, "%s%d", "face:", i);
putText(frame, text, Point(faces[i].x, faces[i].y), FONT_HERSHEY_COMPLEX, 0.5, Scalar(0, 0, 255));
2. 人脸重叠的异常识别算法
方案设计:
上一步中调用detectMultiScale()函数将各个人脸的坐标、大小输出一个 Rect型的容器,一个Rect存放的是四个整型值(x,y,w,h),其中x、y为矩形框左上角的像素坐标,w、h分别代表着矩形框的宽和高。Rect成员函数area()返回Rect的面积; rect = rect1 & rect2 ,rect = rect1 | rect2用于求矩形交集和并集。
因此判断人脸重叠可以转换为求两个矩形是否重叠,以及他们的重叠度。
Rect t1 = faces[i] & faces[j];
if (t1.area() > 0)
{
float AJoin = t1.area();
float AUnion = faces[i].area() + faces[j].area() - t1.area();
if (f < 0.8)
{
Float rate = AJoin / AUnion;
if (rate < face_rate)
{
LOGD("pass");
}
else
{
LOGD("A--L--A--R--M!!!");
}
}
}
但是考虑到计算能力,为了达到实时的效果所以要把数值计算尽量变成逻辑运算。
从最简单的情况推理,假设有两个框(x1,y1,w1,h1), (x2,y2,w2,h2),如果要保证完全不重叠,则有以下关系:
x1 + w1 < x2
y1 + h1 < y2
但是当允许部分重叠时候,就可以添加一个参数:重叠度 FACE_OVERLAP_RATE,取值为0~1,则关系式变为:
x1 + (w1 x FACE_OVERLAP_RATE) < x2
y1 + (h1 x FACE_OVERLAP_RATE) < y2
代码实现:
void isOverLap(vector<Rect> faces)
{
for (int i = 0; i < faces.size(); i++)
{
for (int j = i + 1; j < faces.size(); j++)
{
if ((faces[i].x + faces[i].width * FACE_OVERLAP_RATE > faces[j].x) &&
(faces[j].x + faces[j].width * FACE_OVERLAP_RATE > faces[i].x) &&
(faces[i].y + faces[i].height * FACE_OVERLAP_RATE > faces[j].y) &&
(faces[j].y + faces[j].height * FACE_OVERLAP_RATE > faces[i].y)
)
{
LOGD("A--L--A--R--M!!!");
return;
}
}
}
LOGD("PASS");
}
五. 结果展示
1. 简单识别:

在图像中没有可识别对象的情况下,会输出识别错误提示:

2. 异常行为
为了模拟真实的地铁闸机客流检测时存在的大流量人群图像,使用网上获取到相关图片进行测试,可以看到识别出重叠情况并进行异常报警。
可以通过调整阈值来改变对重叠异常情况的判别。


六. 总结
因为没有太多数据用于训练,使用了库中自带的分类器,所以有些情况下未能完整识别出所有对象,在检测准确度上还有待提升。不过免去了复杂的计算,使得速度满足实时性的要求。